Skip to content

[8팀 김민지] Chapter 1-3. React, Beyond the Basics #18

Open
annkimm wants to merge 9 commits intohanghae-plus:mainfrom
annkimm:main
Open

[8팀 김민지] Chapter 1-3. React, Beyond the Basics #18
annkimm wants to merge 9 commits intohanghae-plus:mainfrom
annkimm:main

Conversation

@annkimm
Copy link

@annkimm annkimm commented Jul 20, 2025

과제 체크포인트

배포 링크

https://annkimm.github.io/front_6th_chapter1-3/

기본과제

equalities

  • shallowEquals 구현 완료
  • deepEquals 구현 완료

hooks

  • useRef 구현 완료
  • useMemo 구현 완료
  • useCallback 구현 완료
  • useDeepMemo 구현 완료
  • useShallowState 구현 완료
  • useAutoCallback 구현 완료

High Order Components

  • memo 구현 완료
  • deepMemo 구현 완료

심화 과제

hooks

  • createObserver를 useSyncExternalStore에 사용하기 적합한 코드로 개선
  • useShallowSelector 구현
  • useStore 구현
  • useRouter 구현
  • useStorage 구현

context

  • ToastContext, ModalContext 개선

과제 셀프회고

기존에 너무 AI에 의존한 것에 대해서 어떻게 하면 개선할 수 있을까 고민하다가
대신에 AI에게 코드 없이 구현해야하는 함수를 어떻게 하면
짤 수 있을지 순서를 정하고 흐름을 알려주는 방식으로 바꿨다.
이른바 AI를 튜터처럼 쓰기 개시...!

가끔 너무 많은 흐름을 알려줄 때도 있었지만,
두번째 과제보다는 이번 과제를 해보면서
과제를 스스로 구현할 수 있는 힘을 좀 더 기르고 있다는 생각이 들었다.

기술적 성장

  • 다른 훅들도 의외로 useState와 useRef를 기반으로 구현
    이를 통해 useState와 useRef가 훅의 근간을 이루는 필수 개념임을 깨달았다.

  • useAutoCallback과 useCallback의 차이점에 대한 이해
    그동안 useCallback을 너무 무지성으로 썼다는 것을 알 수 있었던 지점 중에 하나였다.
    useCallback은 의존성 배열에 따라 값이 바뀌면 새로 참조된다는 것을 새롭게 알 수 있었고,
    useAutoCallback은 한번만 참조하기 때문에 특히나 성능 최적화에 유리하다는 것까지 이번에 알 수 있었다.

  • useShallowSelector와 useShallowState 차이
    useShallowSelector -> 얕은 비교를 하는 함수의 역할, (useSyncExternalStore 같은 store가 없기 때문에 구독을 할 수 없다)
    useShallowState -> context api, useState 같은 데에서 불필요한 리렌더링을 줄일 때 사용하는 역할

  • useStorage, useStore 차이
    useStorage -> localStorage 같은 Storage에서 값이 변경되면 컴포넌트를 재리렌더링 역할
    useStore -> 구독해서 바뀌는 것, 즉 zustand같은 라이브러리 역할

  • 생각보다 deepEqual은 사용하지 않는다는 점을 봤을 때, 연산에 대한 성능을 위해 쓰지 않는걸까라는 생각이 들었다.

자랑하고 싶은 코드

없다...

개선이 필요하다고 생각하는 코드

shallowEquals에서 객체로 비교한 구문과 배열로 비교한 구문을 따로 함수로 뺐으면 좋았을 것 같다.

학습 효과 분석

기술적 성장으로 대체

과제 피드백

저는 2주차 과제보다
이번주차 과제가 더 재밌었어요...
회사에서 아무 생각없이 훅을 쓰게 되는데
여기서 구현해봄으로써 이건 굳이 필요할까?
라는 생각을 더 해보게 되었습니다.

학습 갈무리

리액트의 렌더링이 어떻게 이루어지는지 정리해주세요.

  1. 업데이트 트리거
  • 사용자 상호작용 (클릭, 입력 등)
  • setState() 또는 useState setter 호출
  • useReducer의 dispatch() 실행
  • 부모로부터 받은 props 변경
  • 네트워크 응답 또는 타이머 콜백
  1. 스케줄링
  • 들어온 업데이트를 배치 처리
  • 동기 모드(이벤트 루프 후 즉시)
  • Concurrent 모드(우선순위에 따라 분할 실행)
  • React 스케줄러로 작업 정리
  1. 렌더(Render) 단계
  • 함수형 컴포넌트 실행 또는 클래스 render() 호출
  • 새로운 가상 DOM 트리 생성
  • 순수 연산만 수행 (부작용 없음)
  1. 리콘실리에이션(Reconciliation)
  • 이전 가상 DOM과 새 가상 DOM 비교(diff)
  • 변경될 노드 삽입·삭제·속성 업데이트 목록 계산
  • key 기반으로 리스트 항목 효율적 매핑
  1. 커밋(Commit) 단계
  • Before Mutation: getSnapshotBeforeUpdate 호출
  • Mutation: 실제 DOM에 노드 추가·삭제·속성 변경
  • Layout: useLayoutEffect 또는 componentDidUpdate 동기 실행
  1. 사이드 이펙트 처리
  • 브라우저 레이아웃 완료 후 실행
  • useEffect 또는 componentDidMount/componentDidUpdate
  • 네트워크 요청, 구독 설정, 이벤트 등록 등 수행

메모이제이션에 대한 나의 생각을 적어주세요.

  • 기본적으로 리액트가 성능 최적화를 다해주기 때문에 굳이 메모리제이션을 쓸 필요는 없다고 생각합니다.
  • 다만 굳이 사용한다면 API 통신으로 값을 업데이트할 때나 같은 함수나 연산이 반복된다면 그 정도는 할만하다고 생각합니다.
  • 메모리제이션으로 캐시를 물고 있는 것 자체도 다 성능에 문제를 일으킬 수 있기 때문에 굳이 꼭 필요한 게 아니라면 저는 사용하는 것 보단 사용하지 않는 걸 더 추천합니다.

컨텍스트와 상태관리에 대한 나의 생각을 적어주세요.

  • Context API를 사용해보면서의 느낀 장점은 일단 이전에는 컴포넌트에 모든 값을 props로 전달해줘야 해서 props drilling이 너무 깊어져서 일일히 props 전달하는 것도 굉장히 귀찮은 일이고, 어디까지 이어져 있는지 알 수 없어서 힘든 일이엇지만, 이제는 Context API로 한 번 선언하면 어디서든 간단하게 가져다 쓸 수 있는게 굉장히 편리하고 간단해서 메리트라고 생각이 들었습니다.
  • 하지만 하나의 페이지 안에 데이터가 굉장히 많아지게 되면 Context API에 그 데이터를 다 관리하다보니 관리하기도 힘들고 너무 불필요한 렌더링이 여러번 일어난다는 걸 알 수 있었습니다. 이번에 토스트 과제에서 ToastContext 부분을 함수 부분과 변수부분으로 나눠서 관리하는 방법을 보고 아 저런 방법이 있겠구나 생각이 들었습니다. 오...너무 무거워지지 않게 분산 투자는 중요하구나..(유레카...?!)

리뷰 받고 싶은 내용

  • 이번에 useCallback와 useAutoCallback에 대해서 구현하면서 생각이 든건데 useCallback은 의존성 배열에서 따라서 새로 참조한다고 이번에 알게 되었는데, 그냥 드는 생각은 의존성 배열 대신에 매개변수로 받아서 useAutoCallback으로 하먄 될 꺼 같은데 왜 useCallback가 생겨났는지에 대한 이해가 잘 되지 않더라구요. 이렇게 함으로써 장점이 있나요? 성능 최적화의 부분으로써는 최악? 이라는 생각이 드는데.. 의존성 배열에 값이 바뀌면 다시 계속 새로 참조해야 하잖아요.

  • deepEquals에서 배열 부분을 짤 때 flat을 이용해서 평탄화시킨 다음에 JSON.stringify를 사용해서 짰는데요..
    역시 이렇게 짜면 안좋은걸까요? for문이나 이런 걸로 하나씩 다 비교하는 게 맞는 걸까요? (과제는 다 통과하긴 했지만...)
    뭔가 더 간단한 방법이 없을까 싶어서 해보긴 했지만 틀린건가라는 생각이 들더라구요.

  if (Array.isArray(a) && Array.isArray(b)) {
    if (a.length !== b.length) {
      return false;
    }

    const flattedA = a.flat(Infinity);
    const flattedB = b.flat(Infinity);
    return JSON.stringify(flattedA) === JSON.stringify(flattedB);
  }

Copy link

@unseoJang unseoJang left a comment

Choose a reason for hiding this comment

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

민지님 이번 1챕터 마무리 1-3 과제 너무 수고 많으셨습니다.
해당 과제도 무사히 해내셧네요 ㅎㅎㅎ
코드 전반적으로는 문제는 없지만 추후에 공부할때 리펙토링 하면서 다른분들 코드를 보면서 왜 저렇게 짯을까를 같이 고민해 보는 것도 좋을 것 같아요

2챕터 과제도 화이팅입니다👏

p.s : 아 자랑하고 싶은 코드가 왜없어요....열받네 진짜...그래도...이정도면 많이 발전했어요 PR...ㅜㅜ

import { Toast } from "./Toast";
import { createActions, initialState, toastReducer, type ToastType } from "./toastReducer";
import { debounce } from "../../utils";
import { useAutoCallback } from "@hanghae-plus/lib";

Choose a reason for hiding this comment

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

useAutoCallback은 구현체가 명확하지 않아서 안정성을 떨어뜨릴 수 있어요!
React 내장객체를 사용하기를 권장드립니다!

return result;
}

export const deepEquals = (a: unknown, b: unknown) => {

Choose a reason for hiding this comment

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

이런식으로 비교하게되면 성능은 확실히 빠르지만 배열 비교 정확도 낮아지는 단점이 있어요, React가 생각보다 성능이 좋은 프레임워크는 아니라서 재귀를 기반으로한 깊은 비교를 하도록 권유드리고 싶어요

다른분들의 코드를 보면서 복습해보면 이러한 방법도 있구나 라고 많은 공부가 될것 같아요

}

if (typeof a === "object" && typeof b === "object" && a && b) {
const aKeys = Object.keys(a).sort();

Choose a reason for hiding this comment

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

해당 방식은 얕은 비교를 추천드립니다.
sort()로 비교하게 되면 이후 인덱스마다 키를 비교하는 방식은 퍼포먼스 측면에서 손해를 볼수 있어요

const aKeys = Object.keys(a);
const bKeys = Object.keys(b);

if (aKeys.length !== bKeys.length) return false;

for (const key of aKeys) {
  if (!(key in b) || a[key] !== b[key]) return false;
}

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.

2 participants