yangchanghun 님의 상세페이지[7팀 양창훈] Chapter 1-3. React, Beyond the Basics

과제 체크포인트

배포 링크

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

 ERR_PNPM_RECURSIVE_RUN_FIRST_FAIL  @hanghae-plus/shopping@0.0.1 build: vite build && cp ./dist/index.html ./dist/404.html 빌드과정중 위 오류가 나와서 지피티한테 물어보니까 window에서는 호환이안되서 "build": "vite build && copy dist\index.html dist\404.html", 이걸로 바꾸래서 바꾸니까 되네요 윈도우에선 이 방식으로 해야하는건가용?

기본과제

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 개선

과제 셀프회고

useRef를 제외하곤 과제에있는 훅들은 사용해본적이 없는데 실제로 구현을하면서 다양한 훅들이 왜 생겨났고 어떻게 생겨났는지 알게 된거같습니다. 그리고 과제를 진행하면서 이번과제는 기본기(?)가 있으면 쉬웠을거 같다는 생각이 드는데., 저는 고차함수나 콜백함수에 관한 개념이 부족해서 그런지 모르겠지만 개념을 이해하는것도 어려웠고 과제도 어려웠습니다. ㅠ

기술적 성장

자랑하고 싶은 코드

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

추가 학습이 필요한 영역

고차함수와 콜백함수에 대해서 정확히 이해가 안되어 추가 학습이 필요할거같당,,,

학습 효과 분석??

useRef를 직접 구현하려고 할 때, 처음에는 아래와 같이 작성했다:

const [ref] = useState<{ current: T }>({ current: initialValue });

하지만 이렇게 하면 initialValue가 렌더링 시마다 실행되어 ref의 안정성이 깨질 수 있다는 문제가 있다고 한다.

찾아보니, useRef는 컴포넌트가 재렌더링되더라도 값이 재생성되면 안 된다는 특징이 있었고, 이를 위해 React에서는 useState(() => ...)처럼 함수를 통해 lazy initialization 방식으로 한 번만 실행되도록 처리한다.

그래서 아래처럼 했따.

const [ref] = useState<{ current: T }>(() => ({ current: initialValue }));

이 방식은 컴포넌트가 마운트될 때 단 한 번만 실행되며, 이후 재렌더링돼도 ref는 그대로 유지되므로 useRef와 동일한 효과를 갖는다.

그다음에 useMemo는 값이 변경되면 함수가 실행된다 라고 이해하면 얼추 맞는 것 같다?

근데 정확히는 의존성 배열(deps)이 바뀔 때만 factory 함수가 실행되고, 그 외엔 이전 값을 그대로 캐싱해서 반환한다. useeffect와 차이점이 뭐가 있나 생각해봤는데 useMemo는 리턴값이 있고, useEffect는 리턴값이 없고 cleanup 함수만 리턴할 수 있다. 그리고 useMemo는 값을 캐싱하고, useEffect는 그런 캐싱 기능은 없다.

useCallback은 값이 변경되면 새로운 함수를 반환해주고, 그 함수를 버튼 같은 데 달아놓으면 → 나중에 이벤트 발생 시 실행되는 구조다. 함수 자체를 기억하고 있는 느낌? 그 덕에 렌더링될 때마다 불필요하게 새 함수 안 만들어도 된다.

useMemo와 useCallback을 구현하면서 메모이제이션에대해 좀 더 알게 된거같습니다. 아직까진 내부적으로 어떻게 캐싱되고 재렌더링시에 불필요한연산을 어떻게 최소화하는지는 이해는 못했지만 그래도 이전 값을 저장해놨다가, 의존성이 바뀌지 않으면 → 기존 값 그대로 꺼내 쓰고 바뀌면 → 다시 계산해서 저장하는 방식까지는 이해한 것 같습니다

과제 피드백

학습 갈무리

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

우선 저번 과제를 하면서 진행한 리액트 렌더링과정은 아래와 같습니다 리액트 jsx문법 -> createVNode 평탄화 -> normalizedNode 정규화 -> createElement 돔생성 -> renderElement 생성한 돔 렌더링

그리고 구현엔 실패했지만 diff알고리즘을 통해 재렌더링될때 이전 돔과 비교하여 최소한의 연산을 통해 dom을 재생성하는 것이 리액트식 렌더링이라고 생각합니다

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

위 코드들을 구현하면서 느낀 메모이제이션의 대한 생각은 우선 이전상태와 현재상태를 비교하여 재랜더링시에 불필요한 연산을 최소화 한다. 라고 이해했다 근데 이전상태가 매우매우 크면 어떻게 됄까 라는 생각이 지금 이글을 쓸때 든다. 후에 찾아봐야겟따.

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

궁금한 내용.(?)

고차함수란게 특정 매개변수를 받아서 함수를 다시 만들고 그 함수를 호출하는게 고차함수일까요?? ㅠㅠ 어떻게 이해하면 될지 잘모르겠습니다. 제가 고차함수를 많이 안써봐서 이해가 안되는걸까요.. 흡 ㅠ

export function memo<P extends object>(Component: FunctionComponent<P>, equals = shallowEquals): FunctionComponent<P> {
  const memoComponent = (props: P) => {
    const prevProps = useRef<P | null>(null);
    const prevComponent = useRef<ReactNode | null>(null);
    if (!equals(prevProps.current, props)) {
      prevProps.current = props;

      prevComponent.current = Component(props);
    }
    return prevComponent.current;
  };
  return memoComponent;
}
const MemoizedComponent =memo(TestComponent) 

에서 MemoizedComponent는 결국 memoComponent라는 함수이고 이 함수에 prop을 전달해야지 컴포넌트가 되는 그런 구조일까요?

어찌저찌 구현했지만 이해안되는 부분이 많네요 ㅠ.

과제 피드백

Q. 고차함수란 무엇인가?

A. 고차함수 어렵게 생각하지 마세요. 정말 아무것도 아닙니다. 너무 아무것도 아니에요 ㅎㅎㅎ

고차함수른 함수를 인자로 전달받아서 새로운 함수를 리턴하는 함수에요. ㅎㅎ 별거 아니죵?

function 고차함수란놈(인자로전달되는함수) { return 새로리턴되는함수() { return 인자로전달되는함수() + 1; } }

이런 함수가 고차함수입니다. 그래서 고차함수는 보통 클로저를 만들죵

memo라는 고차함수는 Component라는 함수를 인자로 받아서 memoComponent라는 새로운 함수를 리턴하죵? 그래서 memo가 고차함수입니다 :)

ㅎㅎ 이해가 되셨으면 좋겠어요 정말 아무것도 아닙니다. 고차함수는~ 혹시 이해안되시면 디스코드로 다시 알려주세욥