yuhyeon99 님의 상세페이지[5팀 김유현] Chapter 1-3. React, Beyond the Basics

과제 체크포인트

배포 링크

https://yuhyeon99.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 개선

과제 셀프회고

기술적 성장

자랑하고 싶은 코드

  • 얕은 복사와, 깊은 복사 구현
    • shallowEquals함수를 구현한 코드
      • export const shallowEquals = (a: unknown, b: unknown) => {
          if (Object.is(a, b)) {
            return true;
          }
        
          if (typeof a !== "object" || a === null || typeof b !== "object" || b === null) {
            return false;
          }
        
          const keysA = Object.keys(a);
          const keysB = Object.keys(b);
        
          if (keysA.length !== keysB.length) {
            return false;
          }
        
          for (let i = 0; i < keysA.length; i++) {
            const currentKey = keysA[i];
            if (
              !Object.prototype.hasOwnProperty.call(b, currentKey) ||
              !Object.is((a as Record<string, unknown>)[currentKey], (b as Record<string, unknown>)[currentKey])
            ) {
              return false;
            }
          }
        
          return true;
        };
        
        • React repository에서 shallowEquals.ts 파일을 참고해서 작성했음.
        • 해당 코드는 Flow로 작업되어있었는데, TypeScript 버전으로 수정해서 적용.
        • Object.is를 통해 +0과 -0, NaN === NaN을 true로 처리하는 부분을 심플하게 구현
        • hasOwnProperty가 객체의 메서드로 덮어져서 들어오는 경우를 대비해서 prototype과 call 메서드를 활용한 것

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

학습 효과 분석

  • useCallback, useMemo, memo 등 메모이제이션 관련 훅과 HOC를 직접 구현하며 리액트 렌더링 최적화 개념을 체득
  • 컨텍스트와 상태 관리를 다루는 구조적 설계 경험이 생김.

과제 피드백

  • 단계별로 구현 난이도를 높이며 개념을 학습할 수 있도록 구성된 점이 좋았습니다.
  • 실제 React 내부 구현 패턴을 따라가는 문제 구성이라 실무에 대한 감각도 높아졌습니다.
  • useRef, useMemo 같은 기본 훅 부터 useStore, useRouter까지 이어지는 심화 과제가 자연스럽게 연결되어 있어 성장하는 느낌이 들었습니다.

학습 갈무리

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

  • 리액트의 렌더링 정리:
    • 상태/props 변경 -> 리렌더 -> Virtual DOM -> diff -> 실제 DOM 업데이트 (Reconciliation)
    • memo, useMemo, useCallback 등으로 불필요한 리렌더링 최소화
    • 렌더링 최적화를 위해 참조 안정성 관리, 의존성 명시, selector 분리 등이 중요

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

  • 사용 시점: 고비용 계산 로직, 자주 리렌더링되는 컴포넌트, 참조 안정성이 필요한 콜백
  • 장점: 성능 개선, 불필요한 리렌더 방지
  • 단점: 남용 시 코드 복잡도 증가, 메모리 사용 증가
  • 대안: context 분리, state 구조 개선, selector 최소화

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

  • 전역적 상태 공유가 필요한 UI에 필수
  • 불필요한 리렌더를 막기 위해 구조 분리 또는 selector 사용이 중요함

리뷰 받고 싶은 내용

1. 옵저버 패턴 기반의 상태 관리 로직 (useStore, createStore)

useSyncExternalStore와 옵저버 패턴 기반의 createStore를 사용해 전역 상태 관리 시스템을 구성했습니다.

  • createStore는 내부적으로 subscribe/notify 구조를 따르고 있어, 상태 변경 시 수동으로 구독자에게 알리는 구조입니다.
  • useStoreuseSyncExternalStore를 통해 외부 상태의 변경을 감지하며 selectoruseShallowSelector를 함께 사용해 리렌더링 최소화를 유도했습니다.
    • 질문: 이 상태 관리 구조가 다음과 같은 기능 확장 상황에서도 유연하게 대응할 수 있을지 피드백 받고 싶습니다
      • 비동기 액션 처리 (ex. Redux middleware처럼 side-effect 대응)
        • 상황 예시:
          • // 제품 목록을 API로 불러와서 상태에 저장
            function fetchAndSetProducts() {
              const res = await fetch("/products");
              const data = await res.json();
              productStore.setState({ items: data });
            }
            
            • 이 함수는 store 외부에서 직접 호출해야 함 → store가 액션을 모르는 상태
            • 로딩 중/에러 처리 상태를 분리해서 관리하기가 어려움

과제 피드백

고생하셨습니다 유현님! 이번 주 과제도 잘 해주셨네요 ㅎㅎ 특히 블로그에 작성해주신 내용도 깔끔하게 필요한 내용 정리해주셔서 저도 잘 정리 할 수 있었습니다 :+1 실제 구현되어 있는 코드들을 참고하고 내 구현으로 만들어보려고 하셨던 부분도 너무 좋은 것 같아요.

질문 주신 부분도 이어서 살펴보면요. 말씀해주신것을 보니 이미 정답을 알고 계신것 같은데요. 지금의 구현처럼(또는, 리덕스와 비슷한 구조처럼 액션과 상태를 명확하게 분리된 상황이라면) 미들웨어 시스템 개념을 차용해서 횡단 관심사를 처리할 수 있도록 구현하는 방식이나, 액션과 상태를 한 곳에서 관리하는 형태로 구현하는 방식 이렇게 있을 것 같아요. 근데 후자는 관심사 분리 측면에서 적절하지 않은 것 같고요! 비동기 로직을 다루는데 있어서 참고할만한 부분은 리덕스의 createAsyncThunk나 RTK Query내용을 살펴보는 것 같은데 이 부분에 대해서도 구현을 살펴보면 도움이 되지 않을까..! 싶습니다. (제가 질문을 잘 이해한거겠죠?)

다음주도 지금처럼 꾸준히 학습 이어나가시길 바랍니다! 화이팅입니다~~