Skip to content

렌더링 최적화 2탄: 잘못된 custom hook 사용,, 전체 리렌더링을 부르다,,

Soobeen Yoon edited this page Dec 14, 2022 · 2 revisions

문제 인식

스크린샷 2022-12-14 오후 12 21 44

문제 원인 파악

🤔 기존에 설계한 구조의 문제

  • useTodoList.tsx에서 모든 상태(todoList, displayTime 등)을 가지고 있었고, 이를 Main에서 받아서 자식 컴포넌트에 내려주는 방식이었다.
  • custom Hook은 사용하는 컴포넌트마다 custom hook이 가지는 state, effect는 완전히 독립적이기 때문에 Main 하위의 모든 컴포넌트가 같은 상태를 가질 수 없을 수도 있겠다는 생각이 들어서 props로 내려주게 되었다.

Do two components using the same Hook share state?

    No. Custom Hooks are a mechanism to reuse stateful logic (such as setting up a subscription and remembering the current value), but every time you use a custom Hook, all state and effects inside of it are fully isolated.


  • 따라서, 재생버튼을 눌러서 소요시간을 보여주는 displayTime이 1초에 한 번 변경되면 페이지 전체가 리렌더링되는 문제가 발생했다.
스크린샷 2022-12-14 오후 3 34 11

해결 방법

  • Jotai, useMemo, React.memo를 활용하여 소요시간에 영향을 받는 부분만 렌더링되도록 설정

해결 과정

  • 리렌더링 문제를 해결하기 위해 설계된 Jotai 라이브러리를 활용해보기로 했다.
    • setTimerAtom를 활용하여 timer를 제어하는 부분을 props로 내릴 필요 없이, 컴포넌트 단에서 호출하여 사용할 수 있도록 수정.
  • 이에 따라, Main에서 props로 내려주는 각종 상태를 가지고 있는 불필요한 useTodoList hook을 지울 수 있었음.
export const globalTimerAtom = atom(-1);

export const setTimerAtom = atom(
  (get) => get(globalTimerAtom),
  (get, set) => {
    const timer = get(globalTimerAtom);
    if (timer === -1) {
      set(
        globalTimerAtom,
        window.setInterval(() => {
          set(elapsedTimeAtom, get(elapsedTimeAtom) + 1);
        }, 1000),
      );
      set(isOnProgress, 'working'); // start
      return;
    }
    clearInterval(timer);
    set(globalTimerAtom, -1);
    set(isOnProgress, 'relaxing'); // stop
  },
);

export const stopTimerAtom = atom(null, (get, set) => {
  const timer = get(globalTimerAtom);
  if (timer !== -1) {
    clearInterval(timer);
    set(globalTimerAtom, -1);
    set(isOnProgress, 'relaxing'); // stop
  }
});
스크린샷 2022-12-14 오후 3 34 28

after

스크린샷 2022-12-14 오후 12 23 48

성능 비교

  • Rendering Duration 35% 개선 (timer 움직일 때 displayTime 변경되는 동일 시점 비교)

before

after

스크린샷 2022-12-14 오후 3 45 21

느낀점

  • Timer를 시작하는 atom과 Timer를 멈추는 Atom으로 어플리케이션 전역에서 타이머를 제어할 수 있게 되어서, 추후 다른 페이지에서 Timer를 제어할 수 있도록 기능을 확장하기 쉬워져서 좋았던 것 같다.

참고 자료

💊 비타500

📌 프로젝트

🐾 개발 일지

🥑 그룹활동

🌴 멘토링
🥕 데일리 스크럼
🍒 데일리 개인 회고
🐥 주간 회고
👯 발표 자료

Clone this wiki locally