q1Lim 님의 상세페이지[9팀 임규원] Chapter 1-3. React, Beyond the Basics

과제 체크포인트

배포 링크

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

과제 셀프회고

  1. 재밌었다! 무의식적으로 사용하는 hook에 대해 깊게 생각하지 않았는데 React 내부의 hook을 구현해보면서 작동 원리를 이해할 수 있는 기회가 되었습니다. 개인적으로 세번째 과제는 이 쇼핑몰 시리즈(?) 중에서 제일 흥미로웠습니다!

  2. 설계 친구 AI 이번 과제의 목표는 AI에 대한 의존도를 낮추는 것이었습니다. (하지만 안 쓸 수 없는 😔 ) AI는 함수 내부에 구현해야 하는 요구사항을 정리하고, 설계에 도움을 주는 가이드 역할로만 설정했습니다. 처음부터 구현한 코드를 보여주지 말라는 요청과 함께 과제를 진행하다 보니 중간중간 답답한 부분도 있었고, 특정 함수를 직접 구현해 달라고 한 적도 있었지만 이러한 방식이 결국 스스로 사고하고 구현하는 힘을 기르는 데 더 효과적이었다고 느꼈습니다.

  3. 친해지고 싶은 TS 실무에서 React만 썼기 때문에 아직 TS 타입 사용이 능숙하지 않아서 시간을 많이 소요했습니다. 아마 AI에게 추가 질문을 제일 많이 한 부분이 타입 관련 질문이었던 것 같습니다.

기술적 성장

진행하면서 고민했던 부분을 노션으로 나열(?)했습니다. 의식의 흐름을 나열해보자

자랑하고 싶은 코드

자랑하고 싶은 코드가 있었으면 좋았겠지만 이번 과제에서는 없습니다.

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

  • 심화과제 ToastProvider의 useMemo()
export const ToastProvider = memo(({ children }: PropsWithChildren) => {
  const [state, dispatch] = useReducer(toastReducer, initialState);
  const { show, hide } = useMemo(() => createActions(dispatch), [dispatch]); // useMemo 1#
  const visible = state.message !== "";

  const hideAfter = useMemo(() => debounce(hide, DEFAULT_DELAY), [hide]); // useMemo 2#

  const showWithHide: ShowToast = useAutoCallback((...args) => {
    show(...args);
    hideAfter();
  });

  const toastActionValue = useMemo(() => ({ show: showWithHide, hide }), [showWithHide, hide]); // useMemo 3#
  const toastStateValue = useMemo(() => ({ message: state.message, type: state.type }), [state.message, state.type]); // useMemo 4#

  return (
    <ToastActionContext.Provider value={toastActionValue}>
      <ToastStateContext.Provider value={toastStateValue}>
        {children}
        {visible && createPortal(<Toast />, document.body)}
      </ToastStateContext.Provider>
    </ToastActionContext.Provider>
  );
});

useMemo와 useAutoCallback을 활용해 리렌더링을 방지했고, 이 과정이 심화 과제의 목적이라는 것도 이해하고 있습니다. 다만, 생각보다 많은 곳에 useMemo를 사용하다 보니 정말 이 정도까지 필요한지, 혹은 과한 최적화는 아닌지에 대한 고민이 생겼습니다. 학습 목적으로 useMemo를 적극 활용하는 건 이해되지만, 실제 이렇게 많은 useMemo 사용이 일반적일까? 라는 생각도 들었습니다.

학습 효과 분석

  • 추가 학습이 필요한 영역까지는 아니지만, useRef가 useState를 이용해서 구현할 수 있다면 이번엔 useState를 구현해보고 싶다는 생각이 들었습니다. (가까운 미래의 나에게 전달 🤾 )
  • HOC에 대해 개념을 익혔고, 유용하게 활용하고 싶어서 HOC 구조를 연습하고자 합니다.
  • 심화과정에서 Context를 분리에서 구현하는 방법은 학습메이트님에게 힌트를 얻어서 진행했는데, 이 부분에 대해 실무에 적용하면 효과적이라고 생각했습니다. 특히 이번 심화과제에서의 type, message와 show, hide처럼 역할이 다른 데이터를 구분하여 분리하는 구조는 렌더링 최적화에 도움이 된다고 생각했습니다. 다만 Context 분리 시에 어떤 데이터가 어떤 컴포넌트에 영향을 미치는지에 대한 범위를 충분히 고려해서 설계해야할 것 같다는 생각 또한 들었습니다. (무분별한 Provider 감싸기는 코드의 가독성을 낮춰줄 것 같아요ㅠ)

과제 피드백

진행한 과제중에 재밌다고 느꼈던 부분 중 하나가 과제에 대한 소소한 힌트들도 한몫했습니다. 👍 그리고 참고할 수 있는 학습자료도 많고, 아카이브가 잘 되어있어서 공부하는데 많은 도움이 되었습니다.

학습 갈무리

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

<리액트의 렌더링 과정>

  • 렌더 트리거 : 상태 변경, props 변경, context 값 변경 등으로 컴포넌트가 다시 렌더링 됩니다.
  • 렌더 단계 : React는 변경된 컴포넌트를 함수처럼 다시 호출해서 jsx를 반환합니다. 이 과정에서 Virtual DOM이 새롭게 생성됩니다. 실제 브라우저에서는 반영되지 않지만 어떤 변화가 이루어졌는지 diff 계산을 진행합니다.
  • 커밋 단계 : Reconciliation (이전 Virtual DOM과 비교해 변경된 부분만 찾기)을 진행하고 실제로 변경된 DOM 조각만 실제 DOM에 반영합니다.

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

메모이제이션의 핵심은 '비용'과 '빈도'라고 생각합니다. 연산량이 많고, 비용이 많이 드는 반복되는 계산이 많은 상황에서 불필요한 리렌더링을 방지하는 부분에서 활용됩니다.

  • 장점 : 불필요한 계산을 피하고 렌더링 성능 개선 가능 하지만 무조건적인 메모이제이션의 사용은 메모이제이션 관리에 리소스를 더 소요할 수 있습니다.
  • 단점 : 코드 관리 복잡도 증가, 의존성 관리 부담 메모이제이션은 성능 최적화할 수 있는 여러 방법 중 선택할 수 있는 옵션 중 하나라고 생각합니다.

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

  • 컨텍스트와 상태관리가 필요한 이유는 무엇일까? 저는 준일코치님이 세션때 말씀주셨던 것처럼 관심사 분리와 참조 동일성이 먼저 떠오른 것 같습니다.
  • 관심사 분리 UI와 비즈니스 로직 등 관심사 분리할 수 있어서 한 파일에 하나의 역할만 집중해서 협업 시 코드를 빠르게 이해하는데 도움이 될 것 같습니다.
  • 참조 동일성 컨텍스트를 사용하는 것은 결국 동일한 데이터 혹은 상태를 공유해서 데이터의 일관성을 유지하기 위해 사용할 수 있는 방법 중 하나라고 생각합니다. 실무에서 컨텍스트를 사용했을 때는 항상 Provider를 감싸지 않은 영역에서 데이터를 참조하고 싶어서 어떻게 해야하지를 많이 고민했던 것 같습니다ㅎㅎ

리뷰 받고 싶은 내용

과제 피드백

안녕하세요 규원! 수고하셨습니다. 이번 과제는 React의 내장 훅들을 직접 구현해보면서 프레임워크가 어떻게 상태를 관리하고 최적화하는지 이론을 넘어 몸으로 깊이 이해하는 것이 목표였습니다.

AI 의존도를 줄이고 스스로 고민하며 구현하려는 태도 칭찬합니다. 사실 한번 편리를 경험하면 스스로 안하겠다 생각하기가 쉽지 않았을텐데 수고했어요. "설계 친구 AI"라고 표현하신 것처럼, AI를 설계 가이드로만 활용하고 직접 구현해보신 과정이 정말 값진 학습 경험이었을 것입니다.

답답하겠지만 그 만큼 스스로 생각하는 힘이 길러졌을거라고 생각해요. 노션의 정리 과정도 좋았습니다.

이번 과제를 통해 React가 제공하는 편리한 API들이 내부적으로 어떤 문제를 해결하고 있는지 몸소 체험하셨을 거예요. 특히 렌더링 최적화와 상태 관리에 대한 깊이 있는 이해를 얻으셨기를 바랍니다.


Q) 실제로 이렇게 많은 useMemo 사용이 일반적일까요?

=> 좋은 질문이에요! 실무에서는 이렇게 많은 useMemo를 한 컴포넌트에 사용하는 경우는 드뭅니다. useMemo를 쓴다는 건 리렌더링이 되는 그룹이 2개이상으로 나눠진다는 건데 그렇다는 건 컴포넌트가 단일책임하지 않을 확률이 높기 때문입니다. 대부분의 경우는 컴포넌트를 단일책임하게 만들면 useMemo를 써야할 이유가 많이 없습니다.

수고하셨습니다. 재밌었다! 라는 말로 시작한 것 처럼 계속해서 클린코드 과제도 재밌기를 바래요. 화이팅입니다! :)