useCallback
✒️ 2025-05-28 10:43 내용 수정
리렌더 사이의 함수 정의를 캐시할 수 있도록 하는 Hook
- 참고 자료 : React useCallback
- cache : 데이터나 값을 미리 복사해두는 임시 저장소로, 캐시의 접근 시간에 비해 원래 데이터를 접근하는 시간이 오래 걸리거나 값을 다시 계산하는 시간을 절약하고 싶을 때 사용한다.
- 참고 자료 : 위키백과 캐시
useCallback()는 Component 최상위 레벨에서 호출해야 하며, dependencies가 변경되기 전까지 리렌더 사이의 함수 정의를 캐시할 수 있다.- dependencies가 바뀌기 전까지 함수 정의를 저장해둔다.
- 이러한 특성을 이용하여 성능 최적화에 사용할 수 있다.
- React는 특별한 이유가 있지 않는 한 캐시된 값을 유지한다.
const cachedFn = useCallback(fn, dependencies);
useCallback()에 매개변수로 함수를 전달하면 React는 초기 렌더링을 하는 동안 함수를 호출하지 않고 반환한다.- 다음 렌더링에서 마지막 렌더링 이후 dependencies의 값이 변경되지 않았다면 매개변수로 전달했던 동일한 함수를 다시 제공한다.
- dependencies의 값이 변경되었다면 현재 렌더링 중에 전달한 함수를 제공하고, 나중에 재사용할 수 있도록 저장한다.
- Singleton Pattern과 유사하다.
- dependencies는 매개변수로 전달한 함수 내에서 참조된 모든 반응형 값의 배열로, props, state, Component 본문 내에 선언한 모든 변수 및 함수가 포함된다.
- 배열에는 일정 수의 항목이 존재해야 하며,
[dep1, dep2, dep3, ..]같이 인라인으로 작성해야 한다.
- 배열에는 일정 수의 항목이 존재해야 하며,
import { useCallback } from 'react';
function TestPage({id, name, theme='dark'}) {
// Javascript는 특성 상 항상 다른 함수를 생성한다
// useCallback() 사용시 리렌더링 사이에 handleClick()을 캐싱하도록 지시
const handleClick = useCallback(()=>{
console.log(id, name);
}, [id, name]); // id와 name이 변경되지 않는 한 useCallback()으로 반환되는 함수는 동일한 함수다
return(
//...
)
}
export default TestPage;
- React에서는 Component가 리렌더링되면 모든 자식들을 재귀적으로 리렌더한다.
- Component를 리렌더링할 때 많은 계산이 필요없다면 렌더링이 느려지지 않지만, dependecies가 변하지 않았음에도 리렌더링하는데 많은 시간이 소요된다면 특정 Component를
memo로 리렌더링을 건너뛰도록 설정할 수 있다.- memo 참고.
- Component를 리렌더링할 때 많은 계산이 필요없다면 렌더링이 느려지지 않지만, dependecies가 변하지 않았음에도 리렌더링하는데 많은 시간이 소요된다면 특정 Component를
- Javascript에서는 함수 선언(
function() {})과 함수 표현식(()=>{})은 항상 다른 함수를 생성한다.- 일반적으론 문제되지 않으나,
memo를 사용하여 리렌더를 건너뛰도록 지시하는 경우 함수를 매번 재생성하는 것은 최적화에 문제가 된다. - 함수를 매 리렌더링때마다 새로 생성하지 않고 dependencies가 변경되기 전까지 동일한 함수를 사용하도록 하려면
useCallback()을 사용해야 한다.
- 일반적으론 문제되지 않으나,
function App() {
return(
{/* TestPage component에 id와 name을 전달 */}
<TestPage id={1} name={'test'}/>
)
}
export default App;
import { useCallback, memo } from 'react';
export default function TestPage({id, name, theme='dark'}) {
// useCallback()을 사용하지 않았다면 handleClick()은 theme이 변경될 때마다 매번 변경됨
// useCallback() 사용시 리렌더링 사이에 handleClick()을 캐싱하도록 지시
const handleClick = useCallback(()=>{
console.log(id, name);
}, [id, name]); // id와 name이 변경되지 않는 한 Test Component에 전달되는 함수는 동일한 함수
return(
<div className={theme}>
<Test handleClick={handleClick}/>
</div>
)
}
// 1. theme을 받지 않는 Test Component는 theme의 변경에 무관하므로 memo()로 감싸게 되면
// Test Component의 모든 props가 마지막 렌더링과 동일할 때 리렌더링을 건너뛰도록 지시할 수 있다
// 2. handleClick()이 useCallback()으로 저장되지 않았다면 Test Component가 전달받은 handleClick()는
// theme가 변경될 때마다 매번 새로 생성된 함수이므로 이전 렌더링과 같지 않아 매번 리렌더링 됬을 것
/*
function handleClick() { // 이런 경우 theme이 변경될 때 매번 달라지는 함수가 된다
console.log(id, name);
}
*/
// 3. handleClick()이 useCallback()으로 캐싱되었다면 id와 name이 변경되지 않는 한 이전 렌더링과 동일한 함수를 저장하므로
// Test Component는 동일한 props를 가지게 되어 리렌더링을 건너 뛸 수 있음
const Test = memo(function Test({ handleClick }) {
return (
<button onClick={handleClick}>클릭</button>
)
})
-
메모화를 할 때 항상 모든 곳에 적용할 필요는 없으며, state나 Effect Hook의 처리만으로 불필요한 메모화를 줄일 수 있다.
-
useMemo()와 함께 자식 Component를 최적화할 때 많이 사용된다.- useMemo 참고.
useMemo()는 호출한 함수의 결과를 캐시하고,useCallback()은 함수를 호출하지 않고 함수 자체를 캐시하여 반환한다.
// 간소화된 구현체
function useCallback(fn, dependencies) {
return useMemo(()=> fn, dependencies);
}