-
Notifications
You must be signed in to change notification settings - Fork 10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[2주차] 김동혁 미션 제출합니다. #1
base: master
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
아예 스타일드 컴포넌트를 파일 하나에서 전체적으로 설정하고, 나중에 필요한 곳에서 import
하여 쓰시는 것이 굉장히 인상 깊었습니다.
또한 로컬 스토리지에 저장하는 자료 구조도 잘 설계하시고 깔끔하고 직관적으로 렌더링 하시는 모습에서 많이 배울 수 있었습니다.
좋은 코드 잘 봤습니다 :)
"@babel/plugin-proposal-private-property-in-object": "^7.21.11", | ||
"eslint": "^8.0.1", | ||
"eslint-config-prettier": "^9.1.0", | ||
"eslint-config-standard": "^17.1.0", | ||
"eslint-plugin-import": "^2.25.2", | ||
"eslint-plugin-n": "^15.0.0 || ^16.0.0 ", | ||
"eslint-plugin-promise": "^6.0.0", | ||
"eslint-plugin-react": "^7.34.1", | ||
"prettier": "3.2.5" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
eslint로 문법 정적 검사기를 적용하고, prettier을 통해 코드 포맷팅을 관리해주신 것 좋습니다! 이를 devDependencies
에 추가하여 배포 환경에서의 불필요한 설치를 막고 다른 동료 개발자들에게도 동일하게 적용하여 추후의 git 충돌을 막을 수 있을 것 같아요 :)
}, | ||
plugins: ["react"], | ||
rules: { | ||
"react/react-in-jsx-scope": "off", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@@ -1,7 +1,11 @@ | |||
import TodoListPage from "./pages/todoList/todoList"; | |||
import { GlobalStyle } from "./styles/GlobalStyles"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
매번 다른 컴포넌트들을 import 해올 때, 상대 경로로 적어주는 것이 약간 가독성을 해친다는 생각이 들었어요(근데 저도 이렇게 과제 했습니다....🤣). 유담님 코드 구경하는데 jsconfig.json
파일에서 절대 경로를 설정해주는 것을 보고 리팩터링 해보시면 좋을 것 같습니다.
유담님 코드 레퍼런스
<GlobalStyle /> | ||
<TodoListPage /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
가장 최외곽 컴포넌트에서 프로젝트의 구조를 알 수 있도록 두 개의 컴포넌트만으로 깔끔하게 구성하신 것이 좋습니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
하나의 페이지이므로 TodoListPage
로 접근해서 명확하게 엔트리포인트가 보이지만 내부에 구성하는 컴포넌트가 많아서 나중에 유지보수를 위해선 분리하는 것도 좋을 것 같아요
export const CheckBoxImg = styled.img` | ||
width: 20px; | ||
height: 20px; | ||
margin: 0 15px 0 0px; | ||
fill: #788bff; | ||
`; | ||
|
||
export const TodoInput = styled.input` | ||
width: 100%; | ||
height: 50px; | ||
background-color: #252423; | ||
outline: none; | ||
border: none; | ||
color: #e9ecef; | ||
padding: 10px 28px; | ||
font-size: 0.73rem; | ||
::placeholder { | ||
height: 45px; | ||
padding: 8px; | ||
font-size: 0.73rem; | ||
} | ||
|
||
@media (max-width: 768px) { | ||
height: 45px; | ||
} | ||
`; | ||
|
||
export const SubmitBtn = styled.button` | ||
cursor: pointer; | ||
background-color: #252423; | ||
border: 1px solid #788bff; | ||
width: 40px; | ||
height: 26px; | ||
border-radius: 10px; | ||
font-size: 0.82rem; | ||
color: #788bff; | ||
:hover { | ||
background-color: #3c3a39; | ||
} | ||
|
||
@media (max-width: 768px) { | ||
width: 40px; | ||
height: 25px; | ||
font-size: 10px; | ||
} | ||
`; | ||
|
||
export const TodoList = styled.div` | ||
width: 100%; | ||
list-style: none; | ||
`; | ||
|
||
export const TodoListLi = styled.li` | ||
width: 100%; | ||
height: 52px; | ||
border-radius: 10px; | ||
font-size: 1rem; | ||
|
||
display: flex; | ||
justify-content: space-between; | ||
|
||
align-items: center; | ||
background-color: #252423; | ||
padding-right: 5px; | ||
margin: 5px 0px; | ||
padding: 0 15px 0 15px; | ||
color: #cdcdcd; | ||
|
||
${animationStyles} | ||
|
||
@media (max-width: 768px) { | ||
height: 45px; | ||
} | ||
`; | ||
export const TodoListSpan = styled.span` | ||
width: 100%; | ||
padding: 0px 26.1px; | ||
font-size: 0.73rem; | ||
`; | ||
|
||
export const TodoListBtn = styled.button` | ||
cursor: pointer; | ||
background-color: #252423; | ||
border: 1px solid #788bff; | ||
width: 40px; | ||
height: 26px; | ||
border-radius: 10px; | ||
font-size: 0.8rem; | ||
color: #788bff; | ||
|
||
:hover { | ||
background-color: #3c3a39; | ||
} | ||
|
||
@media (max-width: 768px) { | ||
width: 40px; | ||
height: 25px; | ||
font-size: 10px; | ||
} | ||
`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
아예 스타일 관련 css-in-js 코드를 다른 파일에 분리하여 프로젝트 구성을 한 것도 좋은 것 같습니다! 실제 컴포넌트 UI를 작성하는 파일에서는 이를 import
하여 쓰면 되니까요!
다만 개인적으로 styled-components
라이브러리의 큰 매력은 css 코드와 Javascript 코드가 동일한 파일에 위치해서 관련 스타일링을 바꿔줄 때마다 여러 파일을 돌아다니는 것이 아닌, 한 파일에서 적용할 수 있는 점이라고 생각해요. 당연히 그냥 제 생각입니다.
유명한 Javascript 개발자인 kent c dodds 님의 colocation 관련 포스팅 레퍼런스를 첨부합니다 :) 블로그 링크
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
한 파일내에 쓰면 코드가 길어져서 불편할 거라 생각했는데, 한번 직접 시도해보고 경험해봐야겠습니다. 감사합니다!
@@ -0,0 +1,134 @@ | |||
import { useEffect, useState } from "react"; | |||
import * as T from "./todoList.styles"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
스타일들을 객체 느낌으로 받아서 속성으로서 활용하는 것 너무 좋습니다 :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
저도 놀랐어요! 저는 한 js 파일 내에서 styled-components
로 스타일들을 적용하고 관리하는 편인데 이렇게 객체로 받아서 활용하면 중복된 css 작성을 방지할 수 있을 것 같아요.
(제 코드의 버튼 컴포넌트들에 활용해봐야겠어요 감사합니다)
다만 todoList.js
한 파일에서 모든 기능을 구현하셨을 때보다는 컴포넌트로 쪼개서 구현하셨을 때 이 방법이 더 잘 쓰일 것 같아요!!
const indexToDelete = parseInt(event.currentTarget.value, 10); | ||
|
||
setDeleteIndexAnimation(indexToDelete); // 삭제 애니메이션 시작 | ||
setTimeout(() => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
삭제 애니메이션 구현을 저도 구현해보고 싶었는데, setTimeout()
함수를 이용해야겠다는 생각은 미처 못했습니다! 좋은 코드에서 많이 배우고 갑니다.
<T.InputCheckImg alt="addTodo Icon" /> | ||
<T.TodoInput | ||
type="text" | ||
onChange={onChangeTodo} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
해당 핸들러 함수는 사용자가 글자 하나를 입력할 때마다 트리거 되어서 매번 상태를 업데이트 하는 것으로 보입니다! 이 부분도 개인적인 취향이지만 저 같은 경우는 실제로 사용자가 form을 제출할 때에만 딱 한번 상태를 변경하는 것으로 퍼포먼스 향상을 구현하려고 했습니다.
위 개념은 controlled component
vs uncontrolled component
의 개념인데 관련해서 알아보셔두 좋을 것 같아요. 관련 레퍼런스 남깁니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
상태가 업데이트됨과 동시에 todoList.js 전체가 렌더링되고있어서 uncontrolled component로 관리하면서 추가로 컴포넌트를 분리해도 좋을 것 같습니다!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
onChange 함수가 이런 문제가 있군요.. 첨 알아갑니다!. uncontrolled component 좋은 개념 감사합니다.
<T.TodoList> | ||
{todoList?.map((item, index) => ( | ||
<T.TodoListLi | ||
key={item.id} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
key
속성을 통해서 렌더링 최적화를 구현해주신 것 좋습니다!
value={index} | ||
className={`${item.id === addedItemId ? "animate-slide-down" : ""} ${deleteIndexAnimation === index ? "animate-fade-out" : ""}`} | ||
> | ||
{item.checked ? ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
관련 체크 속성을 사용하기 이해 로컬 스토리지에 저장할 자료에 checked
속성을 넣어주시고 삼항 연산자를 이용해 조건부 렌더링 해주신 것 좋습니다 :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
2주차 과제도 1등으로 제출하셨네요 과제하시느라 수고하셨어요!
애니메이션 효과가 인상적이네요 구경하는 동안 재밌었습니다 ㅎㅎ
코드도 전체적으로 깔끔해서 리뷰하면서 편안했어요~
새로운 스타일링 방법 잘 배워갑니다! 새로운 속성도 알게되어서 기뻤어요 🤩
@@ -0,0 +1,134 @@ | |||
import { useEffect, useState } from "react"; | |||
import * as T from "./todoList.styles"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
저도 놀랐어요! 저는 한 js 파일 내에서 styled-components
로 스타일들을 적용하고 관리하는 편인데 이렇게 객체로 받아서 활용하면 중복된 css 작성을 방지할 수 있을 것 같아요.
(제 코드의 버튼 컴포넌트들에 활용해봐야겠어요 감사합니다)
다만 todoList.js
한 파일에서 모든 기능을 구현하셨을 때보다는 컴포넌트로 쪼개서 구현하셨을 때 이 방법이 더 잘 쓰일 것 같아요!!
// 로컬스토리지에서 할일 불러오기 | ||
const savedTodo = localStorage.getItem("todoItem"); | ||
return savedTodo ? JSON.parse(savedTodo) : []; | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
useState
의 초기값을 함수형 업데이트로 구현하신 부분 정말 좋은 것 같아요!
저도 이번에 알게 되었는데,
초기값 설정에 함수를 사용하면 해당 로직이 컴포넌트가 랜더링 될 때마다 실행되는 것이 아니라
초기 랜더링 때만 실행되어서 성능 최적화에 좋다고 하네요!!
}; | ||
|
||
const onClickDelete = (event) => { | ||
const indexToDelete = parseInt(event.currentTarget.value, 10); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
할일의 value 값을 얻어서 인덱스에 바로 접근하는 방식이 직관적이라서 좋았어요!
저는각 항목별로 현재 날짜를 사용해서 임의의 ID를 부여하고 이에 접근해서 삭제하는 방법으로 삭제 핸들러를 구현했었는데, 이렇게 인덱스에 직접 접근하는게 더 간결하고 직관적인 방법인 것 같아요
index === currentIndex ? { ...item, checked: !item.checked } : item, | ||
); | ||
setTodoList(updatedTodo); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
checked 상태 반전 로직이 간단해서 이해하기 편하네요!
setTimeout(() => { | ||
setTodoList(todoList.filter((_, index) => index !== indexToDelete)); | ||
setDeleteIndexAnimation(null); // 애니메이션 후 상태 업데이트 | ||
}, 300); // setTimeout의 시간은 애니메이션의 지속 시간과 일치해야 합니다. | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
삭제 애니메이션 멋지네요 ㅎㅎ
다만 setTimeout
을 사용할 때는 컴포넌트가 언마운트(삭제)되기 전에 타이머를 명확하게 정리해야 메모리 누수를 방지할 수 있대요!!
그래서 clearTimeout
을 호출해야 하는데,
useEffect 내부에서 setTimeout
을 호출하고,
return문에 clearTimeout
을 호출해서
타이머를 정리해주면
컴포넌트가 사라질 때 메모리 누수를 방지할 수 있다고 합니다!!
setTimeout(() => { | |
setTodoList(todoList.filter((_, index) => index !== indexToDelete)); | |
setDeleteIndexAnimation(null); // 애니메이션 후 상태 업데이트 | |
}, 300); // setTimeout의 시간은 애니메이션의 지속 시간과 일치해야 합니다. | |
}; | |
useEffect(() => { | |
const timerId = setTimeout(() => { | |
setTodoList(todoList.filter((_, index) => index !== indexToDelete)); | |
setDeleteIndexAnimation(null); | |
}, 300); | |
// 컴포넌트 언마운트되기 전에 clearTimeout 호출하기 | |
return () => clearTimeout(timerId); | |
}, [todoList, indexToDelete]); | |
// 의존성 배열에 todoList와 indexToDelete추가!! | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
좋은 정보 감사합니다! 👍
onChange={onChangeTodo} | ||
value={todo} | ||
placeholder="할 일을 입력해주세요." | ||
maxLength="45" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
글자수 제한을 maxLength로 할 수 있다니!!
새로운 속성 배워갑니다!!!
나중에 잘 활용하겠습니다 감사합니다~~
<T.TodoInput | ||
type="text" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
여기에 autofocus
만 추가하면 인풋칸에 자동으로 포커싱 되어서 편하더라구요. UX 측면에서 좋은 수정이 될 것 같아요~!
<T.TodoInput | |
type="text" | |
<T.TodoInput | |
type="text" | |
autoFocus |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
오 autoFocus 너무 좋은거같아요.. 좋은 정보 감사합니다
@media (max-width: 768px) { | ||
width: 40px; | ||
height: 25px; | ||
font-size: 10px; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
반응형까지 고려하시는 부분 멋져요! 세심하시네요
} | ||
`; | ||
|
||
const animationStyles = css` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
저도 배워갑니다..!!
} | ||
`; | ||
|
||
export const NotCheck = styled(NotCheckImg)` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
저는 이미지 스타일링을 할 때 <img>
태그에 styled-components
를 적용하는 방식을 사용하는데,
동혁님처럼 직접 이미지를 컴포넌트로 가져와서 직접 스타일링하는 방식이 더 직관적이라 이해도 잘 되고 재사용하기도 편해서 협업할 때 되게 유용할 것 같아요!! 많이 배워갑니다
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
안녕하세요 프론트운영진 배성준입니다~~~~~
애니메이션 디테일이랑 반응형까지 너무 좋았어요!!!!
애니메이션 구현에서 느낄 수 있듯이 코드에도 되게 신경을 많이 쓴 것 같아서 재밌게 봤습니다 ㅎㅎ
Todo는 비교적 작은 기능구현들이라 괜찮을 지 몰라도 프로젝트 규모가 커지면
컴포넌트 분리를 하는게 유지보수 측면에서 좋아서 미리 연습을 해도 좋을 것 같습니다!!
다음 과제도 기대하겠습니다 ㅎㅎㅎ
<GlobalStyle /> | ||
<TodoListPage /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
하나의 페이지이므로 TodoListPage
로 접근해서 명확하게 엔트리포인트가 보이지만 내부에 구성하는 컴포넌트가 많아서 나중에 유지보수를 위해선 분리하는 것도 좋을 것 같아요
<T.InputCheckImg alt="addTodo Icon" /> | ||
<T.TodoInput | ||
type="text" | ||
onChange={onChangeTodo} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
상태가 업데이트됨과 동시에 todoList.js 전체가 렌더링되고있어서 uncontrolled component로 관리하면서 추가로 컴포넌트를 분리해도 좋을 것 같습니다!
<T.TodoListSpan | ||
style={{ textDecoration: "line-through", color: "#808080" }} | ||
> | ||
{item.text} | ||
</T.TodoListSpan> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
props
를 이용하여 구분해도 좋을 것 같아요! 혹은 간단하게 className을 주고 style에서 class 접근자로 설정해도 좋을 것 같습니다!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
오 좋은 아이디어 감사합니다~!
const slideDownFadeIn = keyframes` | ||
from { | ||
opacity: 0; | ||
transform: translateY(-20px); | ||
} | ||
to { | ||
opacity: 1; | ||
transform: translateY(0); | ||
} | ||
`; | ||
|
||
// 할 일 삭제 시 애니메이션 | ||
const fadeOutScaleDown = keyframes` | ||
from { | ||
opacity: 1; | ||
transform: scale(1); | ||
} | ||
to { | ||
opacity: 0; | ||
transform: scale(0.9); | ||
} | ||
`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
애니메이션 너무 이뻐요!!
import { ReactComponent as NotCheckImg } from "../../assets/NotCheck.svg"; | ||
import { ReactComponent as CheckImg } from "../../assets/checkComplete.svg"; | ||
import { ReactComponent as InputCheck } from "../../assets/check.svg"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
svg를 React 컴포넌트로 사용하는 방식도 좋은 것 같아요 ㅎㅎ
🔗 링크
https://react-todo-19th-dh-1.vercel.app/
📌 기능구현
🤔 후기
전 과제와 비교해보기 위해 똑같은 인터페이스와 기능 React로 구현하는 것을 목표로 이번 2주차 과제를 해보았습니다.
html, css, javascript로 투두리스트를 구현하는 것보다 react 라이브러리를 이용해서 구현하는 것이 확실히 편리하다는 것을 느꼈습니다.
특히 useState나 useEffect같은 React Hook으로 상태관리 하는 것이 DOM을 조작하는 것보다 직관적이고 관리하기 용이하고,
StyledComponent로 스타일 관리하는 것이 적용이 휠씬 빠르고 직관적인 것 같습니다.
📄 Key Questions
1. Virtual-DOM은 무엇이고, 이를 사용함으로서 얻는 이점은 무엇인가요?
2. 미션을 진행하면서 느낀, React를 사용함으로서 얻을수 있는 장점은 무엇이었나요?
3. React에서 상태란 무엇이고 어떻게 관리할 수 있을까요?
4. Styled-Components 사용 후기 (CSS와 비교)