Memoization :: memo, useMemo, useCallback

이주희·2022년 4월 27일
0

React ♥️ Next.js

목록 보기
38/48

자꾸 새로 만들지 말고, 메모해 놓는 거 어때? >> memoization

state가 바뀌면 바뀐 state로 컴포넌트가 다시 만들어지는데, 다시 만들어지면서 성능에 악영향을 미친다.
컴포넌트가 어떻게 재생성되고, 다시 만들지 않게 하는 방법은 무엇인지 알아보자!

memoization

  • 새로 만들 컴포넌트를 메모해놓고, 다음에 다시 만들어야 할 때 새로 만들지 않고 메모해 놓은 것을 가져다 쓴다고 생각하면 된다.

새로 만들어지는 것들

  • 컴포넌트 안의 hook을 제외한 나머지는 전부 새롭게 다시 만들어진다.
  • 부모가 새로 만들어지면 자식들도 새로 만들어진다.

<뜬금꿀팁>
(index.tsx일 경우 폴더명까지만 적으면 되지만(index 생략 가능) 파일명이 index.tsx가 아닐 경우 폴더명/파일명으로 접속하면 된다.)

http://localhost:3000/32-01-memoization.container


렌더링이 일어나면 hook을 제외한 모든것이 새로 만들어진다.

1. let과 state

  • let으로 선언한 변수는 값이 바뀌어도 화면에 반영되지 않는다.
  • state가 변경되면 렌더링이 일어나고 화면에 반영된다.
  • state가 변경되면(렌더링이 일어나면) let으로 선언한 값이 초기화된다.
    (state는 useState로 만든 hook이니까 새로 만들어지지 않는다.)

렌더링이 일어나면 hook을 제외한 모든것이 새로 만들어진다.
(state는 useState로 만든 hook이니까 새로 만들어지지 않음)

  /* let으로 선언한 변수 */
  let countLet = 0;

  /* let으로 선언한 변수를 증가시키는 함수 */
  const onClickCountLet = () => {
    console.log("let : " + Number(countLet + 1)); // 실행될 결과
    countLet += 1;
  };

  /* state로 선언한 변수 */
  const [countState, setCountState] = useState(0);

  /* state로 선언한 변수를 증가시키는 함수 */
  const onClickCountState = () => {
    console.log("state : " + Number(countState + 1)); // 실행될 결과
    setCountState((prev) => prev + 1);
  };

2. 부모 컴포넌트와 자식 컴포넌트

  • 렌더링이 일어나면 부모의 하위 자식 컴포넌트도 렌더링이 일어난다.
    새로 만들어지는 컴포넌트 안의 함수, 변수 등이 전부 새로 만들어지는 것이다.

3. Profiler로 확인해보기

[React Developer Tools]
React Developer Tools의 Profiler: 성능 측정 도구

  1. React Developer Tools를 설치한다.

  2. 리액트 앱으로 만들어진 브라우저를 열고 개발자 도구에서 Profiler 탭으로 들어간다.

  3. 설정 아이콘 > Highlight updates when components render 체크

  1. 렌더링이 일어나는 부분이 화면에 표시된다.

  2. Start profiling을 누르면 녹화를 할 수 있다.

5-1. 렌더링된 부분이 노란색으로 표시되어 나온다.
부모가 렌더링이 되면 자식도 렌더링이 된다는 것을 확인할 수 있다.


렌더링이 일어나지 않게 만들어보자!

Memoization

1. memo :: 자식 컴포넌트의 리렌더링 막기

  • 부모가 렌더링되어도 자식은 렌더링 되지 않게 만들어준다.

  • memo도 HOC이다.

  • 자식까지 리렌더링 되는 것은 피할 수 있으면 memo를 이용해서 방지해야 한다.

  • memo를 쓰고 있어도 전달하고 있는 props가 변경되면 자식 컴포넌트가 리렌더링 된다.
    (자식 컴포넌트에서 전달 받은 props를 사용하지 않고 있더라도 변경되면 자식 컴포넌트가 리렌더링된다.
    따라서, props를 전달할 때 정말 필요한 것만 전달해야 한다.)

  • props가 바뀌면 바뀐다고 해서 memo를 남용하면 안된다.
    메모를 한다는 것은 결국엔 어딘가에 저장된다는 것이고, 변경될 때 계속해서 체크해야 하기 때문에 남용을 하면 오히려 퍼포먼스가 더 떨어질 수 있다.
import { memo } from "react";

function ChildrenPage(props: any) {

  ~~ 자식 컴포넌트 ~~
  
}
export default memo(ChildrenPage);

2. useMemo :: 리렌더링이 일어날 때 변수의 값 유지하기

const aaa = useMemo(() => Math.random(), [countState]);

  • useMemo 안에서 메모할 내용을 리턴해주면 된다.
  • useEffect와 마찬가지로 dependency array가 있다.
    다시 만들어줄 상황을 지정해주고 싶다면, dependency array를 활용하면 된다.

import { useMemo } from "react";

  const random = Math.random();
  console.log("그냥 랜덤 숫자 : " + random);

  const useMemoRandom = useMemo(() => Math.random(), []);
  console.log("useMemo 랜덤 숫자 : " + useMemoRandom);

👇🏻 사실, 변하지 않는 상수값이라면 useMemo를 쓰지 않고 컴포넌트 밖에 const로 선언하는 것이 편리하다..^^

그럼 언제 써?!

컴포넌트 안에서 써야 하고, 계산을 통해서 만들어지면서 리렌더 될 때 다시 계산될 필요가 없을 때 쓰면 된다.
쓸 일이 별로 없다~

useMemo로 함수도 저장할 수 있다.

(useMemo로 useCallback 만들기)
리턴하는 값을 함수로 넣어주면 된다.

  const onClickCountState = useMemo(() => {
    return () => setCountState((prev) => prev + 1);
  }, []);

/* 위아래 같음 */

  const onClickCountState = useMemo(
    () => () => setCountState((prev) => prev + 1),
    []
  );

뜬금 꿀팁 2,,
button 태그의 onClick 안에 함수를 바인딩시키지 않고 바로 작성해도 된다.
하지만 이렇게 하면 useCallback은 적용 못하니까 바인딩해서 사용하자~


3. useCallback :: 리렌더링이 일어날 때 함수를 다시 만들지 않게 하기

useCallback( () => { ~~함수~~ }, [] );

  • 다시 만들어질 필요가 없는 함수에 사용한다.
  • useCallback은 실무에서 굉장히 많이 쓴다.
  const onClickCountState = useCallback(() => {
    console.log(countState);
    setCountState(countState + 1);
  }, []);

useCallback의 문제점

  • state까지 같이 기억해버린다..
    state를 1씩 증가시키는 함수를 실행시켜도 값이 변하지 않는다.

  • useCallback 안에서 state를 직접 사용하는 것을 피해야 한다.
/* ❌ useCallback을 잘못 사용한 예 */
  const onClickCountState = useCallback(() => {
    console.log("state : " + Number(countState + 1)); // 증가된 결과
    setCountState(countState + 1);
  }, []);

👇🏻

/* 👍🏻 옳은 예 */
 const onClickCountState = useCallback(() => {
    setCountState((prev) => prev + 1);
  }, []);

useCallback을 사용하지 말아야 하는 경우

dependency array에 들어간 값이 변경되면 다시 리렌더링이 되는데,
그 값이 많아지면 언제 새로 만들어지고 만들어지지 않는지 확인이 어려워서 오히려 유지보수가 힘들어질 수 있다.
따라서, dependency array에 들어가는 값이 한 두개일 때 사용하는 것이 좋다.


[memoization을 활용했을 때의 성능 차이]

관리자 사이트처럼 데이터가 많은 경우, 체크박스가 체크될 때 memoization을 하지 않으면 굉장히 버벅이게 된다!
memo만 달아줘도 성능 향상 굿임

profile
🍓e-juhee.tistory.com 👈🏻 이사중

0개의 댓글