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`에게 두 가지를 전달해야 합니다.
- 리렌더링 간에 캐싱할 함수 정의
- 함수에서 사용되는 컴포넌트 내부의 모든 값을 포함하고 있는 의존성 목록
최초 렌더링에서 `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
The library for web and native user interfaces
ko.react.dev
'위클리 페이퍼' 카테고리의 다른 글
React에서 배열 렌더링할 때 Key 설정하는 이유 (0) | 2025.03.14 |
---|---|
리액트 생명주기(Lifecycle)에 대해서 (0) | 2025.03.14 |
리액트 `useMemo`에 대한 설명 (1) | 2025.03.10 |
리액트에서 `Virtual DOM` 사용하는 이유 (0) | 2025.02.19 |
리액트 렌더링 과정 요약 (1) | 2025.02.19 |