본문 바로가기
Frontend/ReactJS

[React] useCallback vs useMemo

by 모너아링 2023. 9. 21.

Introduction

useMemo와 useCallback의 개념에 대해서는 알고 있었지만 이 둘의 정확한 차이점은 알지 못했기에 각각 이 둘의 쓰임과 어떤 부분이 다른지 알아보려 한다.

 

useMemo 와 useCallback

useMemo와 useCallback은 모두 성능 최적화를 위해 사용되는 리액트 훅이다.

함수의 결과를 저장하여 동일한 계산을 여러번 하지 않도록 하여 더 빠르고 효율적인 로직을 구현할 수 있다.

 

useMemo의 기본 구조

useMemo(() => fn, [deps])

첫 번째 인자로는 함수를, 두 번째 인자로는 실행할 조건 변수를 담은 배열의 형태이다.

 

useMemo 사용 예시

- useMemo를 사용하지 않았을 경우

const Calculation = ({ a, b }) => {
  return a + b;
};

const MyComponentWithoutMemo = () => {
  const [numA, setNumA] = useState(0);
  const [numB, setNumB] = useState(0);

  const sum = Calculation({ a: numA, b: numB });

  return (
    <div>
      <div>
        Number A: <input value={numA} onChange={(e) => setNumA(parseInt(e.target.value))} />
        Number B: <input value={numB} onChange={(e) => setNumB(parseInt(e.target.value))} />
      </div>
      <div>
        Sum: {sum}
      </div>
    </div>
  );
};

export default MyComponentWithoutMemo;

먼저 이 경우에는 numA나 numB의 값이 변경된다면 리렌더링이 발생한다. 하지만 렌더링이 발생할 때마다 모든 변수와 함수가 재생성되고 실행되기 때문에 렌더링 시 Calculation 생성 & 실행이 반복된다.

 

만약 Calculation 함수가 소요 시간이 긴 함수라면 렌더링 할 때마다 이런 불필요한 함수의 실행은 코드의 효율을 떨어뜨릴 수 있기 때문에 useMemo를 사용한다.

 

- useMemo를 사용한 경우

const Calculation = ({ a, b }) => {
  return a + b;
};

const MyComponentWithMemo = () => {
  const [numA, setNumA] = useState(0);
  const [numB, setNumB] = useState(0);

  const sum = useMemo(() => {
    return Calculation({ a: numA, b: numB });
  }, [numA, numB]);

  return (
    <div>
      <div>
        Number A: <input value={numA} onChange={(e) => setNumA(parseInt(e.target.value))} />
        Number B: <input value={numB} onChange={(e) => setNumB(parseInt(e.target.value))} />
      </div>
      <div>
        Sum: {sum}
      </div>
    </div>
  );
};

export default MyComponentWithMemo;

이 경우에는 useMemo를 이용하여 Calculation 함수의 결과를 메모리에 저장하고 있기 때문에 불필요한 렌더링을 줄일 수 있다.

이때 useMemo의 두 번째 인자를 이용하여 첫 번째 인자의 함수가 호출될 조건을 제시한다. 이 코드에서는 numA, numB가 변경되면 Calculation 함수가 실행되는 조건을 가지고 있다.

조건으로 제시된 경우가 아니라면 렌더링이 발생하지 않기 때문에 성능 최적화에 적합하다.

 

 

useCallback의 기본 구조

useCallback(fn, [deps])

useCallback 또한 두 번째 인자의 조건 배열의 값이 변경된다면 첫 번째 인자의 함수가 실행된다.

 

useCallBack 사용 예시

- useCallBack을 사용하지 않은 경우

const ParentComponentWithoutCallback = () => {
  const [count, setCount] = useState(0);

  const handleClick = () => {
    setCount(count + 1);
  };

  return (
    <div>
      <p>Count: {count}</p>
      <ChildComponent onClick={handleClick} />
    </div>
  );
};

handleClick 함수는 매 렌더링 시에 새로 생성된다. 이 함수가 자식 컴포넌트에 전달되면, 자식 컴포넌트마다 새로운 함수를 생성한다.

 

함수는 같은 이름과 형태를 가지고 있더라도 값이 아닌 참조로 비교되기 때문에 handleClick 함수가 새로 생성된다면 이전과는 다른 handleClick 함수가 생성된 것이다. 따라서 렌더링 시 매번 다른 주소를 참조하는 handleClick 함수가 생성된다.

 

- useCallback을 사용한 경우

const ParentComponentWithCallback = () => {
  const [count, setCount] = useState(0);

  const handleClick = useCallback(() => {
    setCount(count + 1);
  }, [count]);

  return (
    <div>
      <p>Count: {count}</p>
      <ChildComponent onClick={handleClick} />
    </div>
  );
};

useCallback을 사용했을 경우, count의 값이 변경되었을 때만 새로운 함수를 생성하고, 그렇지 않으면 이전 함수를 재사용하여 처음 생성된 handleClick 함수의 참조가 유지된다. 이로 인해 자식 컴포넌트에 전달되는 함수도 count 값이 변경될 때만 생성된다.

 

useCallback을 이용한 함수 재사용으로 불필요한 렌더링을 방지할 수 있다.

 

useMemo vs useCallback

그럼 다시 돌아가서, useMemo와 useCallback의 차이는 무엇일까?

useMemo는 함수의 결과 값을 캐싱하여 불필요한 재계산을 방지하는 것이며,

useCallback은 함수를 메모리제이션하여 동일한 함수 참조를 반환하고, 불필요한 함수 재생성을 방지한다.

 

즉, useMemo는 계산 결과를 캐싱하여 성능을 최적화하고, useCallback은 함수를  메모리제이션하여 자식 컴포넌트에 전달되는 함수의 재생성을 방지한다.