-
Notifications
You must be signed in to change notification settings - Fork 10
[2주차] 김윤성 과제 제출합니다. #8
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: master
Are you sure you want to change the base?
Conversation
지금 Files Changed에 들어가면 node_modules 폴더만 뜨는거 같습니다..ㅜ 포크하신 레포지토리 가보니까 src 폴더 있던데 다시 PR 올려주실 수 있으신가요?? |
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주차 과제 하시느라 수고 많으셨습니다👍 몇 가지 의견을 남겨 보았으니 참고해 보시면 좋을 것 같습니다!
+주완 님이 남겨주신 것처럼 지금 node_modules가 전부 업로드 되어 있는 상황이라 추후 진행될 과제에서는 이 부분도 꼭 확인해 보셔야 할 것 같습니당
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.
favicon이 기본 상태로 남아 있는 것 같은데 이것도 바꿔 보면 좋을 것 같습니당
import styled from 'styled-components' | ||
import { formatKoreanLabel } from '../utils/date' | ||
|
||
const Bar = styled.div` |
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.
component 안에서도 style은 따로 파일을 분리해서 나눠보면 가독성이 좋아질 것 같아요! 주완 님께서 그런 방식으로 코드를 작성하셔서 참고해 보셔도 좋을 것 같습니당
}, [todos]) | ||
|
||
// 현재 날짜의 목록 | ||
const todays = useMemo(() => todos.filter((t) => t.due === currentDate), [todos, currentDate]) |
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.
useMemo로 성능 최적화를 적용하셨네요!👍
setTodos((prev) => [ | ||
...prev, | ||
{ | ||
id: `${Date.now()}${Math.random().toString(16).slice(2)}`, |
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.
이런 방법도 좋지만, uuid를 활용하면 충돌 걱정 없이 더욱 안전한 로직이 될 것 같습니다!
https://it-timehacker.tistory.com/317
}, [todos]) | ||
|
||
// 현재 날짜의 목록 | ||
const todays = useMemo(() => todos.filter((t) => t.due === currentDate), [todos, currentDate]) |
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.
지금은 todos 객체를 하나의 배열 형태로 다루고, 날짜가 바뀔 때마다 매번 filter로 순회하고 있는데, localstorage에 저장할 때부터 { "2025-09-19": Todo[] } 와 같이 두면 필터링이 더 간단해질 수 있을 것 같네요!
// 날짜 이동/선택 | ||
const goPrev = () => { | ||
const d = new Date(currentDate + 'T00:00:00') | ||
d.setDate(d.getDate() - 1) | ||
setCurrentDate(toYYYYMMDD(d)) | ||
} | ||
const goNext = () => { | ||
const d = new Date(currentDate + 'T00:00:00') | ||
d.setDate(d.getDate() + 1) | ||
setCurrentDate(toYYYYMMDD(d)) | ||
} |
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.
지금 goPrev와 goNext가 -1, +1 숫자만 다르고 로직은 동일한 상황이니, 아래와 같이 중복을 줄이는 방향으로 바꾸는 방법도 있을 것 같네요!
// 날짜 이동/선택 | |
const goPrev = () => { | |
const d = new Date(currentDate + 'T00:00:00') | |
d.setDate(d.getDate() - 1) | |
setCurrentDate(toYYYYMMDD(d)) | |
} | |
const goNext = () => { | |
const d = new Date(currentDate + 'T00:00:00') | |
d.setDate(d.getDate() + 1) | |
setCurrentDate(toYYYYMMDD(d)) | |
} | |
// 날짜 이동/선택 | |
const changeDate = (offset: number) => { | |
const d = new Date(currentDate + 'T00:00:00') | |
d.setDate(d.getDate() + offset) | |
setCurrentDate(toYYYYMMDD(d)) | |
} | |
const goPrev = () => changeDate(-1) | |
const goNext = () => changeDate(1) |
import styled from 'styled-components' | ||
import type { Todo } from '../types' | ||
|
||
const Li = styled.li<{ done: 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.
지금 전체적으로 styled-component와 classname 기반 스타일링이 혼용되어 있는 것 같아요! component 기반으로 쪼개서 styled component 방식으로 일관되게 통일하는 쪽이 더 좋지 않을까 싶습니당
2주차도 고생하셨습니다! 아직 node_modules가 있는 상태라, files changed 탭에서 리뷰를 작성하기엔 어려움이 있어, 여기에 하나하나 적도록 하겠습니다! 리뷰 읽어보시고 궁금하신 점 있으시면 댓글 달아주세요!
그리고 밑에 사진처럼 할 일 input이 두 줄 이상 넘어가면 완료 버튼이 밀려나는데,
|
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.
styled-components를 활용하는 경우, 기존 Vanilla CSS가 남아있거나 외부 라이브러리에서 className으로만 꼭 참조해야하는 경우가 아닌 이상 유지보수와 일관성을 위해 제거해주시는 것이 가독성 면에서도 좋을 것 같습니다!
<button type='button' aria-label='달력 열기' onClick={openPicker}> | ||
📅 | ||
</button> | ||
<input ref={dateInputRef} type='date' className='visually-hidden' /> |
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 핸들러를 사용해서 인풋 태그에 바로 연결하는 방식을 사용하면 위 useEffect(57~65줄)에 이벤트들을 직접 바인딩할 필요 없이 더 간단한 코드가 될 것 같습니다 !
<input ref={dateInputRef} type='date' className='visually-hidden' onChange={(e) => onPick(e.target.value)} />
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.
styled-components의 DefaultTheme를 프로젝트에서 정의한 Theme 타입으로 확장한 부분이 인상적이네요😊 새로운 방법 알게 되었습니다!
<span className='title'>{todo.text}</span> | ||
<div className='actions'> | ||
<label style={{ marginRight: 8 }}> | ||
<input type='checkbox' checked={todo.done} onChange={onToggle} data-action='toggle' /> |
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.
개인적으로 checkbox의 사이즈가 조금 더 컸어도 괜찮을 것 같습니다!
그리고 다른 버튼들처럼 cursor:pointer;
도 적용해주시면 좋을 것 같아요
배포링크
배포화면

Review Question
1. Virtual DOM은 무엇이고, 이점은 무엇인가요?
-> 실제 DOM을 직접 건드리기 전에, 자바스크립트 객체로 된 “가상 트리”를 메모리에서 먼저 만들고 변화를 계산해 최소한의 실제 DOM 조작만 반영하는 기법이다.

-실제 DOM에는 브라우저가 화면을 그리는데 필요한 모든 정보가 들어있어 실제 DOM을 조작하는 작업이 무겁기 때문에 사용한다.
DOM의 상태를 메모리에 저장-> 변경 전과 변경 후의 상태를 비교 -> 최소한의 내용만 반영
이러한 과정을 통해 성능 향상을 이끌어낸다. DOM의 상태를 메모리 위에 계속 올려두고, DOM에 변경 있을 경우 해당 변경 사항만 반영하는 것이다.
2. React.memo(), useMemo(), useCallback() 함수로 진행할 수 있는 리액트 렌더링 최적화에 대해 설명해주세요.
(1) React.memo()
(2) useMemo()
(3) useCallback()
3.React 컴포넌트 생명주기에 대해서 설명해주세요.
-> 컴포넌트가 생성되고 사용되고 소멸될 때 까지 일련의 과정을 말한다.
-클래스형 컴포넌트에서는 메서드로, 함수형 컴포넌트에서는 훅(Hook)으로 다룬다.
-마운팅(mounting) 이벤트: 엘리먼트를 DOM 노드에 추가할 때 발생하며, 한 번만 실행된다.
-갱신(updating) 이벤트: 속성이나 상태가 변경되어 엘리먼트를 업데이트할 때 발생하며, 여러 번 실행된다.
-언마운팅(unmounting) 이벤트: 엘리먼트를 DOM에서 제거할 때 발생하며, 한 번만 실행된다.
-Error: 에러 발생 시 한 번만 실행한다. 최상위 컴포넌트에 한 번만 작성하며 에러 발생 시 행동을 정한다.
(1) 클래스형 컴포넌트 (메서드 기반)
각 단계에서 componentDidMount(), componentDidUpdate(), componentWillUnmount() 같은 메서드를 사용한다.
(2) 함수형 컴포넌트 (Hook 기반)
각 단계를 각각의 메서드로 구현하지 않고 useEffect 훅이 이 역할을 통합해서 수행한다.
어려웠던 점
(1) 프로젝트 초기 세팅
npm init, vite 실행, package.json 관리 등 바닐라 JS 때는 없던 설정 과정이 낯설었음.
npm run dev, npm run build 같은 명령어의 의미와 흐름을 이해하는 데 시간이 필요했음.
(2)파일 관리의 복잡함
바닐라 프로젝트는 index.html + style.css + script.js 정도로 단순했는데,
React는 App.tsx, TodoList.tsx, TodoItem.tsx 등 컴포넌트 단위로 나눠 관리하다 보니 구조를 잡는 게 어려웠음.
(3)에러 메시지 이해
리액트도 사용해보지 못한 상황에서 타입스크립트를 쓰니 에러 메시지를 이해하는 데 어려웠음
'React' is declared but its value is never read 같은 타입스크립트/ESLint 경고가 처음엔 당황스러웠음.
에러 해결 과정에서 린트/타입 검사 도구의 역할을 배울 수 있었지만 진입장벽이 느껴졌음.
배운 점
(1)컴포넌트 기반 개발
리액트를 사용하는 큰 특징 중 하나로 알고있던 컴포넌트 기반 개발이라는 것이 무엇인지 조금 알게됨.
UI를 작은 단위(TodoItem, TodoList)로 쪼개서 재사용할 수 있어, 규모가 커질수록 관리가 쉬워질 것 같음.
(2)Virtual DOM과 효율성
실제 DOM 조작을 일일이 신경 쓰지 않고, 상태만 선언적으로 바꿔주면 React가 알아서 최소한의 DOM 변경을 수행하는 점이 인상 깊었음.
(3)개발 환경/도구 이해
ESLint, 타입스크립트, 빌드 도구 등을 접하며, 바닐라보다 체계적인 환경에서 개발이 진행된다는 점을 알게 됨.