Conversation
|
지금 Files Changed에 들어가면 node_modules 폴더만 뜨는거 같습니다..ㅜ 포크하신 레포지토리 가보니까 src 폴더 있던데 다시 PR 올려주실 수 있으신가요?? |
only1Ksy
left a comment
There was a problem hiding this comment.
안녕하세요, 윤성 님! 2주차 과제 하시느라 수고 많으셨습니다👍 몇 가지 의견을 남겨 보았으니 참고해 보시면 좋을 것 같습니다!
+주완 님이 남겨주신 것처럼 지금 node_modules가 전부 업로드 되어 있는 상황이라 추후 진행될 과제에서는 이 부분도 꼭 확인해 보셔야 할 것 같습니당
There was a problem hiding this comment.
favicon이 기본 상태로 남아 있는 것 같은데 이것도 바꿔 보면 좋을 것 같습니당
| import styled from 'styled-components' | ||
| import { formatKoreanLabel } from '../utils/date' | ||
|
|
||
| const Bar = styled.div` |
There was a problem hiding this comment.
component 안에서도 style은 따로 파일을 분리해서 나눠보면 가독성이 좋아질 것 같아요! 주완 님께서 그런 방식으로 코드를 작성하셔서 참고해 보셔도 좋을 것 같습니당
| }, [todos]) | ||
|
|
||
| // 현재 날짜의 목록 | ||
| const todays = useMemo(() => todos.filter((t) => t.due === currentDate), [todos, currentDate]) |
| setTodos((prev) => [ | ||
| ...prev, | ||
| { | ||
| id: `${Date.now()}${Math.random().toString(16).slice(2)}`, |
There was a problem hiding this comment.
이런 방법도 좋지만, 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.
지금은 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.
지금 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.
지금 전체적으로 styled-component와 classname 기반 스타일링이 혼용되어 있는 것 같아요! component 기반으로 쪼개서 styled component 방식으로 일관되게 통일하는 쪽이 더 좋지 않을까 싶습니당
|
2주차도 고생하셨습니다! 아직 node_modules가 있는 상태라, files changed 탭에서 리뷰를 작성하기엔 어려움이 있어, 여기에 하나하나 적도록 하겠습니다! 리뷰 읽어보시고 궁금하신 점 있으시면 댓글 달아주세요!
그리고 밑에 사진처럼 할 일 input이 두 줄 이상 넘어가면 완료 버튼이 밀려나는데,
|
Jy000n
left a comment
There was a problem hiding this comment.
작성해주신 코드와 사이트 잘 봤습니다! 기능별로 컴포넌트를 분리하여 작성하셔서 더 가독성이 좋았던 것 같습니다😄
There was a problem hiding this comment.
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.
아래와 같이 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.
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.
개인적으로 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, 타입스크립트, 빌드 도구 등을 접하며, 바닐라보다 체계적인 환경에서 개발이 진행된다는 점을 알게 됨.