Skip to content

Conversation

Jy000n
Copy link

@Jy000n Jy000n commented Sep 19, 2025

결과 화면

image

느낀점 및 배운점

  • 처음에 기존 VanillaJS로 작성한 코드를 참고하며 React 코드를 작성하다가, 컴포넌트를 나누려던 것을 깜빡하고 한 파일 안에 모두 작성을 해버렸다. 다시 컴포넌트별로 나눠서 수정하기에는 시간이 부족할 것 같아서 적용하지 못했다. 다음에는 처음부터 컴포넌트를 나눠서 작업할 것이다.
  • merge 충돌이 일어나서 분명 해결 후에 저장, 커밋까지 했는데 왜 계속 충돌이 해결이 안 됐다고 뜨는지 모르겠다. 여기서 시간을 좀 많이 잡아먹었다.

Review Questions

Virtual DOM (가상 DOM)

  • 실제 브라우저의 DOM 트리를 메모리 상에서 복제한 자바스크립트 객체
  • UI 업데이트 시 실제 DOM을 직접 조작하는 대신 가상 DOM 간의 변화를 비교하여 효율적인 업데이트 수행

가상 DOM 원리

  • 가상 DOM 생성 : 변경이 발생할 때마다 기존의 가상 DOM과 업데이트된 내용으로 새로운 가상 DOM 객체 트리 생성
  • 비교 : 이전의 가상 DOM과 새롭게 생성된 가상 DOM을 비교하여 어떤 부분이 변경되었는지 찾아냄
  • 일괄 업데이트 : 변경된 부분만 실제 DOM에 효율적으로 적용

가상 DOM 이점

  • 성능 향상 : 실제 DOM은 변경 발생할 때마다 복잡한 렌더링, 리플로우, 리페인트 과정을 거치지만, 가상 DOM은 이러한 과정을 피하고 변경 사항만 모아 한 번에 적용
  • 효율적인 업데이트 : 수많은 요소가 변경될 경우, 각 요소를 개별적으로 업데이트하는 것이 아니라 전체 변경 사항을 하나의 묶음으로 만들어 한 번에 처리 -> 연산 횟수 줄어듦
  • SPA의 효율적 구현 : React, Vue.js와 같은 SPA(Single Page Application) 라이브러리는 가상 DOM을 사용하여 빈번한 DOM 조작 처리 + SPA 성능 높임

리액트 렌더링 최적화

React.memo()

  • 목적 : 컴포넌트의 렌더리 최적화
  • 동작 방식 : props가 변경되지 않았을 때, 이전 렌더링된 결과를 재사용하여 컴포넌트의 불필요한 렌더링 방지
    • 컴포넌트를 memoization하여 props가 변경되지 않으면 리렌더링 방지
    • 함수형 컴포넌트를 React.memo(Component)로 감싸면, 부모가 리렌더링되어도 props가 같다면 해당 컴포넌트는 리렌더링 X
  • 사용 시기 : 동일한 props로 컴포넌트가 자주 렌더링되거나, 렌더링 시 복잡한 로직 처리 시 유용
const Child = React.memo(({value}) => {
  console.log('렌더링');
  return <div>{value}</div>;
});

-> 컴포넌트 레벨에서, props가 같으면 리렌더링 건너뜀

useMemo()

  • 목적 : 비용이 큰 연산의 결과를 캐싱(memoization)
  • 동작 방식 : 함수를 호출하여 얻은 결과값을 메모리에 저장하고, 의존성 배열의 값이 변경되지 않았다면 이전 계산 결과 반환
    • 무거운 연산이 매번 실행되지 않도록 특정 값이 변할 때만 다시 계산
  • 사용 시기 : 복잡한 계산이나 객체/배열 생성이 잦은 경우, 해당 연산의 결과가 이전과 동일하다면 다시 계산하는 대신 캐싱된 값을 재사용하여 성능 향상 가능
const expensiveValue = useMemo(()=>{
  return heavyCalculation(input);
}, [input]);

-> 값(계산 결과) 캐싱

useCallback()

  • 목적 : 함수 자체를 memoization
  • 동작 방식 : 컴포넌트가 렌더링될 때마다 새로운 함수가 생성되는 대신, 의존성 배열의 값이 변경되지 않으면 이전의 함수 참조 재사용
  • 사용 시기 : 자식 컴포넌트에게 props 전달 시, 해당 함수를 useCallback으로 감싸주면 부모 컴포넌트가 리렌더링되어도 함수 참조가 유지되어 자식 컴포넌트의 불필요한 렌더링 막음
const handleClick = useCallback(()=>{
  console.log('클릭');
}, []);

-> 함수 자체 캐싱

React 컴포넌트 생명주기

클래스 컴포넌트

  • Mounting(생성) : constructor -> render -> componentDidMount
  • Updating(업데이트) : shouldComponentUpdate -> render -> componentDidUpdate
  • Unmounting(제거) : componentWillUnmount

함수형 컴포넌트 + Hook
useEffect Hook이 클래스 생명주기 메서드 대체

  • useEffect(()=>{...}, []) : componentDidMount (처음 렌더링 시 1회 실행)
  • useEffect(()=>{...}, [deps]) : componentDidUpdate (deps 변경 시 실행)
  • useEffect(()=>{return()=>{...}}, []) : componentWillUnmount (cleanup)

배포 링크 : react-todo-22nd.vercel.app

+ 커밋 메시지 'fix: merge 충돌 해결' 부분에서 VanillaJS->React로 변경하였는데 merge 충돌 문제도 함께 해결하면서 커밋메시지를 저렇게 남겼네요,, 참고해주시면 감사하겠습니다!!

Copy link

@only1Ksy only1Ksy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

안녕하세요, 자윤 님! 다크모드/라이트모드까지 구현해 주셨네용 ㅎㅎ 몇 가지 의견을 남겨 보았으니 참고해 보시면 좋을 것 같습니다 👍 2주차 과제도 고생 많으셨습니다!!

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

지금 app 파일 하나에서 모든 theme 제외 모든 컴포넌트에 대한 styled component가 정의되고 있는 것으로 보이는데, 가독성과 유지보수를 위해 파일을 분리해 보시는 것이 좋을 것 같습니다!

const dd = String(selectedDate.getDate()).padStart(2, '0');

// selectedDate = Date 객체 -> 문자열로 변경
const stringDate = `${yyyy}-${mm}-${dd}`;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

지금 가장 상단의 오늘 날짜를 받는 부분에서도 같은 형식으로 날짜를 반환하는 로직이 있는 것 같습니다! 이렇게 여러 번 사용되는 로직은 util 함수로 따로 빼서 관리해 보는 것이 어떨까요?

margin-bottom: 0.3rem;
`;

const PrevWeekButton = styled.button`

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PrevWeekButton, PrevButton, NextButton, NextWeekButton 네 가지는 대부분의 속성이 같고 세부 사항만 조금 다른 것 같습니다
자윤 님께서 저번 주차에 발표해 주셨던 것 같은데, 공통 스타일을 NavButton 같이 묶고, styled(NavButton) 과 같은 형태로 사용하면 중복을 줄일 수 있을 것 같아요!

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

공통 스타일을 NavButton 컴포넌트로 만들고 차이점만 props로 조정하면 더 효율적일 것 같습니다.

border: 1px solid grey;
border-radius: 5px;

&: placeholder {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
&: placeholder {
&::placeholder {

<AppendButton onClick={addTodo}>+</AppendButton>
</InputContainer>
{list.map((todo, index) => (
<ListContainer key={index}>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

react에서 key 값에 index를 사용하지 않는 것을 권장하고 있습니다! id 같은 고유한 값으로 설정하신다면 더 좋을 것 같습니당
(리액트 공식 문서 참고)
https://react.dev/learn/rendering-lists

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

key 값에 index를 사용하면 안되는 이유

저도 왜 안되는지 잘 이해하지 못했는데, 위 글을 읽고 이번 과제에 고쳤었습니다! 한번 꼭 읽어보세요!

// Todo 추가
const addTodo = () => {
const todoInputText = todoInput.trim();
if (!todoInputText) return;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UX 향상을 위해 alert나 error UI 같이 왜 추가가 안 되는지 사용자가 알 수 있는 장치가 있으면 좋을 것 같아요!

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

동감합니다! 사용자가 입력했던 공백이 안 사라지고 그대로 남아있더라구요! 이런식으로 input 창 밑에 빨간 글씨로 나타나게 하는 방식이 괜찮을 거 같습니다!
image

};
// 한글 입력 enter 처리
const handleKeyDown = (e) => {
if (e.key === 'Enter' && !e.nativeEvent.isComposing) addTodo();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

한글 중복 입력 방지해 주신 거 좋습니당👍

Copy link

@Wannys26 Wannys26 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

안녕하세요! 2주차 과제 리뷰를 맡게 된 손주완입니다!
react 폴더 구조
이 글과 21기 react-todo-21nd pr을 보시면 예쁘게 폴더구조가 형성된 react 프로젝트를 볼 수 있으니 다음번에 한 번 적용해보셨으면 좋겠습니다!
제 코드리뷰 읽어보시고 질문이 있으시면 댓글 달아주세요! 이번주도 고생많으셨습니다!

const toggleTheme = () =>
setIsDark((prev) => {
const themeMode = !prev;
sessionStorage.setItem('themeMode', themeMode ? 'dark' : 'light');

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

현재 테마 설정이 sessionStorage에 저장되고 있는 것 같습니다.

UX 개선을 위해 localStorage로 변경하는 걸 추천드립니다! 사용자가 브라우저를 재방문해도 다크/라이트 테마가 유지될 수 있으니까요!

});
};

// 오늘 날짜로 (로고클릭)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

로고를 클릭하면 오늘 날짜로 되돌아 올 수 있다는 사실을 처음 방문한 사용자는 알아차리기 힘들거 같습니다!
그리고 통상적으로 로고를 클릭하면 메인 페이지(초기 페이지)로 간다고 생각하기에, 페이지를 분할했을 때 더 아쉬울 거 같기도 하구요!

<AppendButton onClick={addTodo}>+</AppendButton>
</InputContainer>
{list.map((todo, index) => (
<ListContainer key={index}>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

key 값에 index를 사용하면 안되는 이유

저도 왜 안되는지 잘 이해하지 못했는데, 위 글을 읽고 이번 과제에 고쳤었습니다! 한번 꼭 읽어보세요!

background-color: ${(props) => props.theme.background};
color: ${(props) => props.theme.color};
font-family: Pretendard, sans-serif;
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

transition: background-color 0.3s ease, color 0.3s ease;
여기에 트랜지션 코드 한 줄만 넣으시면 다크/라이트 모드 전환이 더 예뻐질 거 같습니다!!

const savedTodos = localStorage.getItem('todos');
return savedTodos ? JSON.parse(savedTodos) : {};
});
const list = todos[currentDate] || [];

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

변수명을 list 대신 currentTodos로 하시면 더 좋을거 같습니다!

// Todo 추가
const addTodo = () => {
const todoInputText = todoInput.trim();
if (!todoInputText) return;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

동감합니다! 사용자가 입력했던 공백이 안 사라지고 그대로 남아있더라구요! 이런식으로 input 창 밑에 빨간 글씨로 나타나게 하는 방식이 괜찮을 거 같습니다!
image

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

eslint와 prettier을 같이 사용할 때 참고해 볼 만한 글 남겨드립니다. (충돌방지 셋팅도 읽어 보시면 좋을거 같아요)
eslint와prettier설정방법

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants