[2주차] 원채영 미션 제출합니다.#5
Conversation
seoyeon5117
left a comment
There was a problem hiding this comment.
다크모드 구경하려고 왔는데 잘 보고 갑니다👀 2주차 과제도 수고 많으셨습니다!!
| <S.ItemContainer> | ||
| {todos.map((todo, index) => ( | ||
| <TodoItem | ||
| key={index} |
There was a problem hiding this comment.
key에 index값을 넣는 것은 권장되지 않습니다. 아래 문서 참고하시면 좋을 것 같아요!
|
|
||
| return ( | ||
| <BrowserRouter> | ||
| <ThemeProvider theme={{ ...theme }}> |
There was a problem hiding this comment.
저도 이번에 다크모드 구현하면서 이 부분 어떻게 구현하셨는지 궁금해서 놀러왔습니다. 저도 훅으로 만들었으면 더 좋을 것 같네요!
| margin: 1.5rem 0; | ||
| `; | ||
|
|
||
| export const CloseButton = styled.span` |
There was a problem hiding this comment.
button과 같은 시맨틱 태그를 활용하면 웹사이트의 구조를 파악하기 쉽고 스크린 리더를 사용하는 사용자에게도 더 좋을 것 같습니다!
| gap: 10px; | ||
| height: 100%; | ||
| max-height: 500px; | ||
| overflow-y: scroll; |
There was a problem hiding this comment.
아 그리고 overflow-w도 숨겨주시면 더 예쁠 것 같아요!
| export const WeekButton = styled.button` | ||
| font-size: 1rem; | ||
| cursor: pointer; | ||
| background: none; | ||
| border: none; | ||
| color: ${({ theme }) => theme.colors.text}; | ||
| padding: 5px 10px; | ||
| `; |
There was a problem hiding this comment.
rem과 px을 번갈아 쓰시는 이유가 있나요? 어떤 부분은 padding에 rem이 들어가있고 이 부분에는 px이 들어가 있어서 어떤 때 px을 사용하시고 어떤 경우 rem을 사용하시는지 궁금합니다!
There was a problem hiding this comment.
디자인을 만들면서 개발하다 보니 스타일을 고정하고 싶을 경우에는 px로 사용하고, 그 경우가 아니라면 rem을 썼습니다 ,,, ㅎㅎㅎ
| :root { | ||
| --vh: 100%; | ||
| } |
| @@ -0,0 +1,53 @@ | |||
| import { createGlobalStyle } from 'styled-components'; | |||
| import reset from 'styled-reset'; | |||
| }, | ||
| }; | ||
|
|
||
| export { lightTheme, darkTheme }; |
| return date.toLocaleDateString('ko-KR', { | ||
| year: 'numeric', | ||
| month: 'long', | ||
| day: 'numeric', | ||
| }); |
There was a problem hiding this comment.
저도 바닐라 js로 투두리스트 만들때 코드리뷰 받았던 부분인데 OPTION을 따로 상수로 빼면 코드를 자세히 보지 않아도 추후 쉽게 유지보수할 수 있어 상수로 빼는 방식도 고려해보시면 좋을 것 같네요!
| const newDate = new Date(currentDate); | ||
| newDate.setDate(currentDate.getDate() + days); | ||
| setCurrentDate(newDate); | ||
| }; |
There was a problem hiding this comment.
이렇게 todo 핸들링 관련 함수들 아예 utils로 따로 빼 놓으신 게 너무 좋은 것 같스빈다! 한 눈에 보기 편리하네요
| month: 'long', | ||
| day: 'numeric', | ||
| }); | ||
| }; |
There was a problem hiding this comment.
확실히 이렇게 parameter로 format 전달하게 되면 너무 좋을 것 같습니다! 다만 한 가지 의견을 드리자면, date.toLocaleDateString 에서 'ko-KR' 대신 format으로 설정하시는 건 어떠신가요? 이렇게 하면 추후 유지보수에 더 편리할 것 같습니다!
| }, | ||
| }; | ||
|
|
||
| export { lightTheme, darkTheme }; |
There was a problem hiding this comment.
colors 파일 따로 분리하신 것도 그렇고, light/dark 모드 1주차 때 리뷰하실 때도 너무 인상깊게 봤습니다. 저도 기회가 되면 이렇게 모드 분리해서 만들어보고 싶네요!
| } | ||
| `; | ||
|
|
||
| export default GlobalStyles; |
There was a problem hiding this comment.
저는 meyer's reset을 따로 복붙해서 사용하였는데, 아예 이렇게 패키지로 사용할 수도 있군요?? 너무 좋은 것 같습니다!
| const theme: DefaultTheme = themeMode === 'light' ? lightTheme : darkTheme; | ||
|
|
||
| return { theme, themeMode, toggleTheme }; | ||
| }; |
There was a problem hiding this comment.
저도 추후 light/dark 모드 적용해볼 때 이렇게 custom hook 만들어서 사용하면 정말 좋을 거 같네요, 좋은 코드 감사합니다!
| ); | ||
| }; | ||
|
|
||
| export default ToggleButton; |
There was a problem hiding this comment.
이건 그냥 제 개인적인 바램인데, 혹시 기회가 되신다면 토글 버튼 옆에 낮/밤 이모지도 간단하게 추가해주실 수 있으실까요? 1주차 때 있으셨던 거로 기억하는데 너무 좋게 봤어서 그렇습니다!!
| ); | ||
| }; | ||
|
|
||
| export default Header; |
There was a problem hiding this comment.
혹시 헤더를 위쪽 상단에 고정하시는 방안에 대해서는 어떻게 생각하시나요? 지금은 스크롤 내리면 헤더가 가려지더라고요 ㅜㅜ
| return () => window.removeEventListener('storage', updateCompletedTodos); | ||
| }, [updateCompletedTodos]); | ||
|
|
||
| return { completedTodos, maxDays }; |
There was a problem hiding this comment.
위에 커밋에서도 언급하였는데, 지금 방법도 너무 좋지만 혹시 max day를 단순 count가 아닌 비율로 설정하시는 방법은 어떠신가요??
| @@ -0,0 +1,79 @@ | |||
| import { useState, useRef } from 'react'; | |||
| import * as S from './TodoInput.Styled'; | |||
| import { FaCalendarAlt } from 'react-icons/fa'; | |||
There was a problem hiding this comment.
react에서 자체적으로 아이콘 제공을 해주는군요?? 대박이네요
| const [isComposing, setIsComposing] = useState(false); | ||
| const [selectedDate, setSelectedDate] = useState(''); | ||
| const [isDatePickerOpen, setIsDatePickerOpen] = useState(false); | ||
| const dateInputRef = useRef<HTMLInputElement>(null); |
There was a problem hiding this comment.
제가 구현하지는 않았지만, 추후에 input type=date 사용할 때 useRef 사용하면 많은 도움이 될 것 같네요 감사합니다!
lemoncurdyogurt
left a comment
There was a problem hiding this comment.
다크모드랑 화이트모드로 모드 전환하는거에서 굉장히 신선함을 느꼈는데, 토글로 전환해서 디벨롭 한게 너무 인상깊었어요! 화면 스타일면에서도 많은 고민을 한게 느껴졌고, 코드에서도 많은걸 배울 수 있었어요!
| &::-webkit-scrollbar { | ||
| display: none; /* 웹 브라우저에서 스크롤 바 숨기기 */ | ||
| } |
There was a problem hiding this comment.
웹 브라우저에 스크롤바 없애는거에 대해서 생각 해본적 없는데 되게 좋은 기술인거 같아요!
| <S.WeekText>Weekly Progress 📅</S.WeekText> | ||
| {Object.entries(completedTodos).map(([day, count]) => ( | ||
| <S.DayStat key={day} $isMax={maxDays.includes(day)}> | ||
| {day === todayDay ? `Today (${day})` : day} | {count}개 완료 | ||
| </S.DayStat> |
There was a problem hiding this comment.
사소한거긴 한데, 요일 별 할일을 보고 -> 해당 날짜로 이동하는 과정이 조금 불편한거 같아서 요일을 클릭했을 때 해당 날짜로 이동시킬 수 있는 navigate가 있으면 좋을 거 같아요!
| if (e.key === 'Enter' && !isComposing) { | ||
| e.preventDefault(); | ||
| handleAddTodo(); | ||
| } |
| <StrictMode> | ||
| <GlobalStyles /> | ||
| <App /> | ||
| </StrictMode>, |
There was a problem hiding this comment.
StrictMode를 사용한 이유가 있을까요?


배포 링크
🔗
미션을 진행하면서
HTML, CSS, vanillaJS로만 만들던 걸 리액트로 옮겨보는 건 이번이 처음이었던 것 같습니다.
미션하면서 styled-components로 컴포넌트를 나누는 과정이 되게 반가웠고, 리액트의 편리함도 새삼 느꼈습니다..!
지난번엔 사이드바를 만들면서 "굳이 이 위치에 있어야 하나?" 싶은 버튼들까지 마구 넣었었는데,
이번엔 실제 투두리스트에 필요한 요소들만 골라서, 날짜 변경 같은 건 밖으로 빼고,
사이드바에는 주간 완료한 할 일만 딱 보여주도록 구성해봤습니다. 열리고 닫히는 애니메이션, 외부 overlay도 추가했습니다!
다크모드/라이트모드 전환 버튼도 많이들 사용하는 토글 스위치 느낌으로 바꿔봤는데, 디자인적으로 더 깔끔해진 것 같아서 뿌듯했습니다☺️
그리고 저번엔 key로 index를 쓰다가 문제가 생겼었는데, 이번엔 다시 코드를 짜다 보니 구조상 크게 문제 없어서
uuid 대신 index로 간단하게 사용해봤습니다 😥
프로젝트를 할 때 리액트를 주로 사용하긴 했지만, 최적화나 깔끔한 코드 작성은 여전히 어렵게 느껴지는 부분인 것 같습니다... 혹시 코드 보시면서 조언해주실 부분이 있다면 언제든지 알려주시면 감사하겠습니다!
그리고 url이 같은 상황에서 처음 상태로 돌아가고 싶을때,, window reload말고 다른 방식이 있는지 알고싶습니다,,,
Key Questions
1. Virtual-DOM은 무엇이고, 이를 사용함으로서 얻는 이점은 무엇인가요?
=> 사용하는 이유: 실제 DOM에는 브라우저가 화면을 그리는 데 필요한 모든 정보가 들어있어 무겁다.

따라서 부드러운 UX를 제공하고자 변경사항만 빠르게 파악하고 리렌더링하는 방식을 채택한다.
diffing 알고리즘 동작 방식이 이루어짐 (트리, 노드 비교)
가상 DOM의 재조정 과정 3단계
1: UI가 변경 감지 -> UI를 가상돔으로 렌더링 (여기서 렌더링은 가상렌더링을 의미)
2: 현재 가상돔과 이전 가상돔을 비교해 차이 계산
3: 변경 부분을 실제 DOM에 반영
가상 DOM의 장단점
장점: 성능/ 일관성/ 복잡한 UI 관리/ cross-platform 지원
단점: 추가 메모리 사용/ 복잡성 증가/ 특정상황에서의 불필요함
2. React.memo(), useMemo(), useCallback() 함수로 진행할 수 있는 리액트 렌더링 최적화에 대해 설명해주세요. 다른 방식이 있다면 이에 대한 소개도 좋습니다.
(1) React.memo():
const MyComponent = React.memo((props) => { return (/*컴포넌트 렌더링 코드*/)} );=> 똑같은 props를 넘겨받아 같은 결과 렌더링 x => 렌더링 과정 skip, 최근 결과 재사용
(2) useMemo(): 값을 캐싱 => hook
(3) useCallback(): 함수를 캐싱 => hook
이 경우에서는, profile 컴포넌트가 렌더링 될때마다 userEdit의 onsave로 새로운 함수가 전달된다.
그러나 onsave의 속성값은 name, age의 변경이 없으면 같아야 함 ! => 따라서 useCallback 필요
const onSave = useCallback(() => saveToServer(name, age), [name, age]);3. React 컴포넌트 생명주기에 대해서 설명해주세요. (3단계)
1단계: 마운팅 단계 -> 2단계: 업데이트 단계 -> 3단계: 언마운팅 단계