위클리 페이퍼

리액트 `useCallback`에 대한 설명 + `useMemo`와의 차이점

rexondex 2025. 3. 10. 09:44

useCallback에 대해 스스로 내용을 정리해 보았습니다.


useCallback ?

 

useCallback의 간단한 정의는

`리렌더링 간에 함수 정의를 캐싱해 주는 React Hook` 입니다.

const cachedFn = useCallback(fn, dependencies)

[ 레퍼런스 ]

 

리렌더링 간에 함수 정의를 캐싱하려면 컴포넌트의 최상단에서 useCallback을 호출합니다

import { useCallback } from 'react';

export default function ProductPage({ productId, referrer, theme }) {
  const handleSubmit = useCallback((orderDetails) => {
    post('/product/' + productId + '/buy', {
      referrer,
      orderDetails,
    });
  }, [productId, referrer]);

 

- 매개변수

  • fn : 캐싱할 함숫값입니다. 이 함수는 어떤 인자나 반환값도 가질 수 있습니다. 다음 렌더링에서 dependencies 값이 이전과 같다면 React는 같은 함수를 다시 반환합니다. 반대로 dependencies 값이 변경되었다면 이번 렌더링에서 전달한 함수를 반환하고 나중에 재사용할 수 있도록 이를 저장합니다.
  • dependencies : fn 내에서 참조되는 모든 반응형 값의 목록입니다. 반응형 값은 props와 state, 그리고 컴포넌트 안에서 직접 선언된 모든 변수와 함수를 포함합니다. 의존성 목록은 항목 수가 일정해야 하며 `[dep1, dep2, dep3]` 처럼 인라인으로 작성해야 합니다.

 

- 반환값

  • 최초 렌더링에서는 useCallback은 전달한 fn 함수를 그대로 반환합니다.
  • 후속 렌더링에서는 이전 렌더링에서 이미 저장해 두었던 fn 함수를 반환하거나, 현재 렌더링 중에 전달한 fn 함수를 그대로 반환합니다.

 

- 주의사항

  • useCallback은 Hook이므로, 컴포넌트의 최상위 레벨 또는 커스텀 Hook 에서만 호출할 수 있습니다.
  • React는 특별한 이유가 없는 한 캐시 된 함수를 삭제하지 않습니다. `useCallback`을 성능 최적화 방법으로 의존하는 경우에 개발자의 예상과 일치해야 합니다. 그렇지 않다면 state 변수나 ref가 더 적절할 수 있습니다.

[ 용법 ]

 

- 컴포넌트의 리렌더링 건너뛰기

 

렌더링 성능을 최적화할 때 자식 컴포넌트에 넘기는 함수를 캐싱할 필요가 있습니다.

먼저 이 작업을 수행하는 방법에 대한 구문을 살펴본 다음 어떤 경우에 유용한지 알아보겠습니다.

 

컴포넌트의 리렌더링 간에 함수를 캐싱하려면 함수 정의를 `useCallback` Hook으로 감싸세요.

import { useCallback } from 'react';

function ProductPage({ productId, referrer, theme }) {
  const handleSubmit = useCallback((orderDetails) => {
    post('/product/' + productId + '/buy', {
      referrer,
      orderDetails,
    });
  }, [productId, referrer]);
  // ...

 

`useCallback`에게 두 가지를 전달해야 합니다.

 

  1. 리렌더링 간에 캐싱할 함수 정의
  2. 함수에서 사용되는 컴포넌트 내부의 모든 값을 포함하고 있는 의존성 목록

최초 렌더링에서 `useCallback`으로부터 반환되는 함수는 호출시에 전달할 함수입니다.

 

이어지는 렌더링에서 React는 의존성을 이전 렌더링에서 전달한 의존성과 비교합니다.

의존성 중 하나라도 변한 값이 없다면(Object.is로 비교), `useCallback`은 전과 똑같은 함수를 반환합니다.

 

그렇지 않으면 `useCallback`은 이번 렌더링에서 전달한 함수를 반환합니다.

 

다시 말하면, `useCallback`은 의존성이 변하기 전까지 리렌더링 간에 함수를 캐싱합니다.


useCallback은 성능 최적화를 위한 용도로만 사용해야 합니다.

 

만약 코드가 useCallback 없이 작동하지 않는다면 먼저 근본적인 문제를 찾아 해결해야 합니다. 그 다음에 useCallback을 다시 추가할 수 있습니다.


useCallback과 useMemo는 어떤 연관이 있나요?

 

useMemo가 useCallback과 함께 쓰이는 것을 자주 봤을 것입니다. 두 hook은 모두 자식 컴포넌트를 최적화할 때 유용합니다.

 

무언가를 전달할 때 memoization(다른 말로는 캐싱)을 할 수 있도록 해줍니다.

import { useMemo, useCallback } from 'react';

function ProductPage({ productId, referrer }) {
  const product = useData('/product/' + productId);

  const requirements = useMemo(() => { // 함수를 호출하고 그 결과를 캐싱합니다.
    return computeRequirements(product);
  }, [product]);

  const handleSubmit = useCallback((orderDetails) => { // 함수 자체를 캐싱합니다.
    post('/product/' + productId + '/buy', {
      referrer,
      orderDetails,
    });
  }, [productId, referrer]);

  return (
    <div className={theme}>
      <ShippingForm requirements={requirements} onSubmit={handleSubmit} />
    </div>
  );
}

 

차이점은 무엇을 캐싱하는지 입니다.

  • useMemo는 호출한 함수의 결과값을 캐싱합니다.
  • useCallback은 함수 자체를 캐싱합니다. useMemo와 달리, 전달한 함수를 호출하지 않습니다. 그 대신, 전달한 함수를 캐싱해서 productId나 referrer이 변하지 않으면 handleSubmit 자체가 변하지 않도록 합니다. 이것은 불필요하게 ShippingForm을 리렌더링하지 않고 handleSubmit 함수를 전달할 수 있도록 해줍니다. 함수의 코드는 사용자가 폼을 제출하기 전까지 실행되지 않을 것입니다.

이미 useMemo에 익숙하다면 useCallback을 다음과 같이 생각하는 것이 도움이 될 수 있습니다.

// (React 내부의) 간단한 구현
function useCallback(fn, dependencies) {
  return useMemo(() => fn, dependencies);
}

useCallback – React

 

useCallback – React

The library for web and native user interfaces

ko.react.dev