-
Notifications
You must be signed in to change notification settings - Fork 10
[2주차] 송아영 미션 제출합니다. #2
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
base: main
Are you sure you want to change the base?
Conversation
deploy Protection 풀어주세용 |
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.
컴포넌트별로 폴더를 나누고 스타일드 컴포넌트는 style에 모아두셨네요! 디렉토리 구조 잘 보고 갑니다. 과제 수고하셨어요😊!!
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.
현재 한 달 전 객체를 만든 후 setDate(0)으로 전 달의 마지막 날을 구해 한 달 전씩 뒤로 미는 로직인 거 같아요.
createMonthlyCalendar를 시작일과 끝일 두 가지를 인수로 받아 캘린더를 반환하는 방식으로 구현해도 좋을 거 같아요. getTime()을 사용하면 두 date 사이의 일 수를 알 수 있거든요.
export const createGrassCalendar = () => {
const date = new Date() // 오늘
const twoMonthAgo = new Date(date.getFullYear(), date.getMonth() - 2, 1) // 두 달 전 1일
...
const getDaysBetween = (startDate, endDate) => {
const diffTime = Math.abs(endDate.getTime() - startDate.getTime()); // 밀리초 차이
return Math.ceil(diffTime / (1000 * 60 * 60 * 24)); // 밀리초를 일(day)로 변환
}
const createMonthlyCalendar = (startDate, endDate /* or numberOfDays */) => {
const year = startDate.getFullYear();
const month = startDate.getMonth();
const numberOfDays = getDaysBetween(startDate, endDate);
...
}
그러면 한 달 전 객체와 이번 달인지 아닌지에 대한 검증 과정이 필요하지 않아 코드가 더 단순해질 거 같아요!
const aMonthAgo = new Date();
aMonthAgo.setDate(0);
const numberOfDays = date.getMonth() === new Date().getMonth() ? new Date().getDate() : date.getDate();
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일을 알 때, getDaysBetween을 통해 그 사이의 일수를 구하면 setDate(0)으로 마지막 날짜를 구하지 않더라도 그 달의 일수를 구할 수 있다는 뜻인 걸까요?
const data = localStorage.getItem(date); | ||
const todos: TodoDto[] = JSON.parse(data || '[]'); | ||
const hasDoneTodo = | ||
date === formatDate(new Date()) ? false : !!todos.filter((todo) => todo.isDone === true).length; |
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.
잔디가 심어졌다가 삭제되는 경우를 만들고 싶지 않았습니다! 오늘의 투두는 체크했다가 취소하는 등 계속 변화할 수 있으니까요. 깃허브에서도 잔디를 그런 식으로 관리한다고 생각했었고요!
그런데 사실상 완료한 투두를 취소하거나 삭제하는 일이 잘 없겠다는 점, 깃허브에서도 당일 컨트리뷰트를 반영한다는 점, 동일 피드백이 여러 번 들어왔다는 점에서 오히려 사용자 경험을 저해한 것 같다는 생각이 드네요 ㅠㅠ
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.
헉스. 좋은 피드백 감사합니다. 스크롤 테스트를 깜빡해버렸네요.
<div> | ||
<TodoInputBox> | ||
<TodoInput | ||
value={todo} | ||
onChange={handleTodoChange} | ||
onKeyDown={handleEnterKeyDown} | ||
placeholder="할 일을 입력하세요..." | ||
/> | ||
<Button disabled={!todo.trim()} onClick={handleAppendButtonClick}> | ||
추가 | ||
</Button> | ||
</TodoInputBox> | ||
|
||
<ul> | ||
{allTodos.map((todo) => ( | ||
<TodoItem | ||
key={todo.id} | ||
onChange={handleCheckboxChange} | ||
onClickDeleteButton={handleDeleteButtonClick} | ||
{...todo} | ||
/> | ||
))} | ||
</ul> |
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.
2주차 과제도 수고하셨습니다! 너무 잘 해주셔서, memo 최적화 사용 등 코드리뷰 하면서 제가 배운점이 엄청 많은 것 같아요!! 많이 배워 갑니다ㅎㅎ
|
||
const handleAppendButtonClick = () => { | ||
if (todo.trim()) { | ||
const id = allTodos.length ? allTodos[allTodos.length - 1].id + 1 : 0; |
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부터 시작하는 아이디를 지정하는 방식 말고, 밀리초를 사용한 숫자나, randomUUID같이 고유 id를 사용하는 방법이 더 좋을 것 같습니다! 4번 지우고, 다른 투두를 추가하면 다시 4번이 된다는것보다 고유 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.
좋은 제안 감사합니다!
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.
오늘 투두는 반영되지 않습니다!
} | ||
`; | ||
|
||
export const TodoContent = memo(styled.label<{ $isDone: boolean; $isHovered: boolean }>` |
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.
여기에 memo 사용하는건 생각도 못했는데 좋은 것 같습니다!
setIsExpended(true); | ||
}} | ||
onMouseLeave={() => { | ||
setTimeout(() => setIsExpended(false), 700); |
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.
여기세어 set time out을 사용하신 이유가 무엇인가요?
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.
hover 시 white-space와 max-height를 조절해 transition을 주었는데요, mouseleave 시 white-space가 먼저 변경되면서 max-height에 대한 transition이 적용되지 않는 문제가 있었습니다. mouseleave 시에 white-space보다 max-height가 먼저 줄어들게 함으로써 transition을 적용시키려고 해 봤습니다!
|
||
const totalTodoCount = allTodos.length; | ||
const doneTodoCount = allTodos.filter((todo) => todo.isDone).length; | ||
const doneTodoRatio = `(${doneTodoCount}/${totalTodoCount})`; |
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.
이 코드는 그냥 리턴문 내에서 작성해도 될 것 같아요..!
저는 주로 복잡한 조건문 등 분기가 복잡하거나 긴 함수가 필요한 랜더링용 코드만 return 밖으로 꺼내놓는데, 아영님은 어떤 기준으로 분리하시나요?
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.
말씀하신 대로라면 return문 내에 아래와 같은 코드를 작성하게 되는데요, 가독성을 많이 저하시키는 것 같아 분리하게 되었습니다.
<Title>To Do List ({allTodos.filter((todo) => todo.isDone).length}/{allTodos.length})</Title>
저는 return문 내에 어떤 것들을 넣고 빼는 기준이 있다기보다는 항상 그때 그때의 가독성을 높일 수 있는 방식으로 변수나 함수를 사용하는 것 같습니다! 또한 가독성을 높일 수 있는 가장 좋은 방법이 관심사의 분리와 적절한 변수 및 함수명을 설정하는 것이라고 생각하고요!
배포 링크
https://react-todo-21th-git-gustn99-gustns-projects.vercel.app?_vercel_share=RvvoG4gHVQaEFeMoMKzee2mvcEXFBxmz
과제를 하며. . .
지난 과제에서 잘 모듈화된 코드를 보고 오히려 리액트를 사용할 때 state 관리 이슈로 코드가 더 복잡해질 수 있겠다 싶었는데요, 실제로 그 일이 일어난 것 같습니다. . . state를 공유하는 다수의 이벤트 핸들러와 또 jsx 반환문까지 함께 있다 보니 바닐라보다 코드가 지저분해 보이기도 하네요 ㅠㅠ 여러분들은 리액트 프로젝트 구조를 어떻게 잡고 사용하실지!! 이번에도 코드를 열심히 읽어보겠습니다.
그런 한편 매번 document.createElemet를 사용하지 않아도 쉽게 원하는 요소를 반환할 수 있다는 것은 큰 장점이겠죠! 사실은 react도 내부적으로 document.createElement를 수행하고 있다는 점이 재미있는 것 같습니다.
첫 번째 미션을 계속해서 업데이트하는 멋진 분들이 계신데요, 저는 받았던 피드백들을 이번 미션에 적용해서 trim을 사용한 유효성 검사, isComposing 검사 등의 로직을 추가했습니다. 또 모달 UI 가 불필요하다고 느껴 제거하고, 깃허브의 잔디처럼 투두를 완료한 날들을 심을 수 있는 Grass 컴포넌트를 추가해 봤습니다!
Key Questions
1. Virtual-DOM은 무엇이고, 이를 사용함으로서 얻는 이점은 무엇인가요?
라고 리액트 공식 문서에는 나와 있습니다. 간략하게 말하면 리액트는 리렌더링이 일어날 때 가상 돔을 재구성합니다. 그리고 브라우저 렌더링 시 가상 돔과 실제 돔을 비교하여 변경된 요소들만 수정합니다.
최근에 리액트 공식 문서를 보며 벨로그에 기록하기 시작했는데요, 부끄럽지만 혹시 참고가 될까 하여. . . 첨부해 봅니다 ㅎㅎ. 정확히 가상 돔에 대해 다루는 내용은 아니지만, 리액트의 렌더링 전반에 대한 이해를 도울 수 있으리라 예상합니닷. 사실은 제 글보다도 리액트 공식 문서 자체적으로 한국어 번역을 지원하고 있으니 읽어보시면 많은 도움이 될 것 같습니다!
2. React.memo(), useMemo(), useCallback() 함수로 진행할 수 있는 리액트 렌더링 최적화에 대해 설명해주세요.
React.memo()
useMemo()
useCallback()
저도 최적화에 굉장히 취약한 편인데요, 어쩌다 보니 이번 미션을 진행하면서 세 가지 함수를 모두 사용했습니다! 오용 또는 남용된 곳이 있을 수 있으니 발견하시면 코드리뷰 남겨주세요!
3. React 컴포넌트 생명주기에 대해서 설명해주세요.
마운트
업데이트
언마운트