[나만무/개발단계]useMemo & useCallback

CHO WanGi·2025년 6월 28일

KRAFTON JUNGLE 8th

목록 보기
78/89

useMemo

https://ko.react.dev/reference/react/useMemo#skipping-re-rendering-of-components

재렌더링 사이 계산 결과를 캐싱할 수 있도록 React Hook
같은 값을 반환하는 함수를 반복적으로 호출한다면 이전에 계산한 값을 메모리에 저장.
동일한 계산의 반복수행을 제거해서 실행 속도를 빠르게 할 수 있는 목적으로 사용.

DP의 Memoization 을 생각하면 된다.

export default function TodoList({ todos, tab, theme }) {
  // ...
  return (
    <div className={theme}>
      <List items={visibleTodos} />
    </div>
  );
}


import { memo } from 'react';

const List = memo(function List({ items }) {
  // ...
});

visibleTodos라는 Props가 바뀌지 않는다면, List 컴포넌트는 useMemo 훅을 통해
메모리에 캐싱된 값을 사용하여 리렌더링하지 않는다.

주의 할 점

useMemo는 두번째 인수로 의존성 배열을 전달해야 렌더링 될때마다 계산이 다시 실행되지 않는다

  const visibleTodos = useMemo(() => filterTodos(todos, tab), [todos, tab]);

useCallback

계산 결과를 메모이제이션 하는 useMemo와 다르게 useCallback은 이벤트 핸들러 등 함수를 메모이제이션
할 때 사용되는 훅.
컴포넌트가 리렌더링할때 함수를 새로 생성하는 것을 막고 이전에 생성한 함수를 재사용하여 불필요한 렌더링을
줄이려는 목적으로 사용.

const memoizedCallback = useCallback(
  () => {
    doSomething(a, b);
  },
  [a, b],
);

함수 동등성?

JS에서 함수는 객체로 취급, 함수를 동일하게 만들어도 메모리 주소가 다르면 다른 함수로 간주하게 된다.

const add1 = () => x + y;

const add2 = () => x + y;

add1 === add2 // false

동일한 코드지만 일치연산자로 비교하면 false를 반환한다.

  const [data, setData] = useState(null);

  const fetchData = () =>
    fetch(`https://test-api.com/data/${id}`)
      .then((response) => response.json())
      .then(({ data }) => data);

  useEffect(() => {
    fetchData().then((data) => setData(data));
  }, [fetchData]);

따라서 fetchData 함수를 호출해서 잘 돌아가는 것 처럼 보이나,
함수의 동등성 이슈로 인해 컴포넌트가 렌더링 될때마다 새로운 참조 값을 갖게 된다.
따라서 함수의 변경이 일어났기 때문에 useEffect가 실행되어 무한 루프에 빠지게 된다.

  const fetchData = useCallback(
    () =>
      fetch(`https://test-api.com/data/${id}`)
        .then((response) => response.json())
        .then(({ data }) => data),
    [id]
  );

  useEffect(() => {
    fetchData().then((data) => setData(data));
  }, [fetchData]);

이럴때 useCallback을 활용하여 함수의 동등성을 유지하면 된다.

주의할 점.

모든 함수, 결과값 마다 useCallback/ useMemo을 사용하는 것은 지양하자.
SW 성능 최적화시 꼭 트레이드 오프를 고려해야하는데,
메모리 사용 증가 혹은 어려운 유지보수 라는 대가를 꼭 기억하고 사용할 것

그럼 언제 쓸까요?

  • 안써도 되는 경우
  1. 단순한 연산 함수와 setState, Dispatch를 호출하는 경우
  2. 의존성 배열안에 완전히 새로운 객체나 배열을 전달하는 경우 => 다시 사용하지 않을 값들을 전달하는 것은 의미가 없다.
  3. 의도적으로 매번 새로운 함수나 값을 계산하는 경우
  • 써야하는 경우
  1. 연산 혹은 처리량이 매우 많은 경우, 리렌더링시 비용절감 목적
  2. 자식 컴포넌트에서 useEffect가 반복적으로 호출되는 경우
  3. 함수 자체가 매우 복합하거나 다시 계산하는 경우
  4. 부모가 리렌더링 될 때 자식 컴포넌트까지 렌더링 전파를 막고 싶은 경우.
profile
제 Velog에 오신 모든 분들이 작더라도 인사이트를 얻어가셨으면 좋겠습니다 :)

0개의 댓글