useCallback vs useMemo(feat. React.memo)

Ayaan·2022년 4월 6일
0

프로젝트를 보다가 useCallback, useMemo, React.memo를 봤는데 정확한 개념을 이해하지 못해서 찾아보기로 했는데 먼저 메모이제이션(memoization)에 대해 알아야 한다

memoization이란 기존에 수행한 연산의 결과 값을 어딘가에 저장해두고 동일한 입력이 들어오면 재활용하는 기법으로 중복 연산을 피할 수 있다는 장점이 있다
useMemo의 memo도 이 memoization의 앞글자를 따왔다

1. useCallback


Returns a memoized callback.

메모이제이션된 함수를 반환하는 훅이다
죽, 실행할지 말지는 본인에게 달렸다는 말이다
기본적인 구조는 다음과 같다

useCallback(fn, deps)

useCallbackdeps가 변하면 fn이라는 새로운함수를 반환한다

import React, { useState, useCallback, useMemo } from "react";

export default function App() {
  const [ex, setEx] = useState(0);
  const [why, setWhy] = useState(0);

  // useCallback 이 () => {console.log(why)} 라는 함수를 반환한다.
  const useCallbackReturn = useCallback(() => {console.log(why)}, [ex]);

  // useCallback 이 담겨있는 함수를 실행
  useCallbackReturn();

  return (
    <>
      <button onClick={() => setEx((curr) => (curr + 1))}>X</button>
      <button onClick={() => setWhy((curr2) => (curr2 + 1))}>Y</button>
    </>
  );
}

위의 코드에서 ex가 변하면 console.log(why)가 반환되고 이를 useCallbackReturn에 담아 실행했기 때문에 console.log(why)가 실행된다

여기서 중요한 것은 '새로운 함수를 반환'한다는 점인데 따라서 deps가 변하면 함수의 형태가 동일하더라도 새로운 함수를 반환한다

const func1 = () => {};
const func2 = () => {};

console.log(func1 === func2); // false

2. useMemo()


Returns a memoized value.

메모이제이션된 을 반환한다
기본구조는 다음과 같다

useMemo(() => fn, deps)

마찬가지로 deps가 변하면 () => fn이 실행되어 값을 반환한다

import React, { useState, useCallback, useMemo } from "react";

export default function App() {
  const [ex, setEx] = useState(0);
  const [why, setWhy] = useState(0);

  // useMemo 사용하기
  useMemo(() => {console.log(ex)}, [ex]);

  // 두 개의 버튼을 설정했다. X버튼만이 ex를 변화시킨다.
  return (
    <>
      <button onClick={() => setEx((curr) => (curr + 1))}>X</button>
      <button onClick={() => setWhy((curr2) => (curr2 + 1))}>Y</button>
    </>
  );
}

X버튼을 누르면 () => console.log(ex)가 실행되어 콘솔에 ex가 출력된다

useMemo는 렌더링 중에 실행되므로 렌더링 중에는 하지 않는 것을 여기에 넣어서는 안된다

3. 그래서 그 둘의 차이점이 뭔데?


useCallback(fn, deps) is equivalent to useMemo(() => fn, deps).

리엑트 공식문서에서도 useCallback(fn, deps)useMemo(() => fn, deps)이 같다고 말한다

그렇다면 왜 두 개의 훅으로 구분해서 만들어 놓은 걸까?

useMemo은 함수의 연산량이 많을 때 이전과 값이 같으면 이 연산을 건너뛰기 위한 목적이다

예를 들어 useMemo(() => veryHeavyFunc(a, b), [a, b])같이 매우 무겁고 느린 함수에서 a, b의 값이 바뀌지 않았다면 이 연산을 건너뛸 수 있다

useCallback은 함수가 재생성 되는 것을 방지하기 위한 목적이다
이 훅은 useMemo를 기반으로 만들어졌기 때문에 아무래도 비슷해보일 수 밖에 없는 것 뿐이다

4. React.memo


React.memouseMemo의 차이점은 뭘까?
찾아보기 전에 이 둘은 같은 것이라고 나는 생각했다

하지만 근본적으로 다른데 useMemo는 훅이기 때문에 함수형 컴포넌트에서만 사용할 수 있지만 React.memoHOC이기 때문에 클래스형, 함수형 두 곳에서 다 사용할 수 있다

컴포넌트가 동일한 props로 동일한 결과를 렌더링한다면 React.memo(component)형태로 메모이징(memoizing)하여 성능향상을 할 수 있다

React.memo는 이전 컴포넌트와 비교하지만 비교방식이 shallow equal이기 때문에 이를 수정하고 싶다면 두번째 arg로 비교함수를 직접 제공할 수도 있다
하지만 이는 예측불가능한 오류를 발생할 수 있으므로 권장되지는 않는다

5. TL; DR


React.memo는 HOC이기 때문에 함수형, 클래스형 컴포넌트에 모두 사용될 수 있다
useMemouseCallback의 가장 큰 차이점을 참조 동일성을 갖는 '값'을 계산하느냐 '함수'를 계산하느냐의 차이이다







*References

profile
2022.04.01. 첫직장에 입사한 신입 FE개발자입니다🔥

0개의 댓글