성능 개선을 위한 "memoization", 그 중 "useCallback"에 대해 알아보자.
React Hook.const cachedFn = useCallback(fn, dependencies)
fn(함수): 캐시하려는 함수로, 어떤 인자든 받을 수 있으며, 초기 렌더링에서 반환된다. 이후 렌더링 시, 의존성이 변경되지 않았다면 React는 동일한 함수를 반환하고, 변경되었다면 새로운 함수를 반환하여 캐싱한다. React는 이 함수를 직접 호출하지 않으며, 언제 호출할지는 사용자가 결정한다.dependencies(의존성 배열): 함수에서 참조하는 모든 반응형 값(예: props, state)을 포함해야 한다. 배열은 항상 같은 크기로 작성되어야 하고, 의존성 값들이 이전 값과 다를 경우 새로운 함수가 생성된다.useCallback은 전달된 함수를 반환한다. useCallback은 컴포넌트나 커스텀 훅의 최상위에서만 호출할 수 있으며, 반복문이나 조건문 안에서는 호출할 수 없다. 또한, React는 특별한 이유가 있을 때만 캐시된 함수를 폐기한다. 예를 들어, 개발 환경에서 파일을 수정하거나, 컴포넌트가 초기 마운트 중에 중단되면 캐시를 폐기할 수 있다.useCallback은 주로 성능 최적화를 위해 사용되며, 그렇지 않다면 state나 ref가 더 적절할 수 있다.function TodoList() {
const [todos, setTodos] = useState([]);
const handleAddTodo = useCallback((text) => {
const newTodo = { id: nextId++, text };
setTodos([...todos, newTodo]);
}, [todos]);
// ...
todos 배열을 의존성으로 사용하여 새 할 일을 추가하는 함수 handleAddTodo를 정의한다. 하지만 이렇게 하면 todos 배열이 변경될 때마다 함수가 재생성된다.
function TodoList() {
const [todos, setTodos] = useState([]);
const handleAddTodo = useCallback((text) => {
const newTodo = { id: nextId++, text };
setTodos(todos => [...todos, newTodo]);
}, []);
// ...
이 예시에서는 상태를 업데이트할 때 이전 상태를 읽기 위해 업데이터 함수를 사용한다.
이 방법은 todos를 의존성 배열에서 제거할 수 있어 의존성을 줄인다. 상태 업데이트는 React에게 "이전 상태를 받아서 새 상태를 계산하라"는 방식으로 전달된다.
useCallback 훅에 빈 의존성 배열 []을 사용했기 때문에 함수는 재생성되지 않는다.
컴포넌트가 처음 렌더링될 때 한 번 생성되고, 이후 렌더링 시에도 재생성되지 않고 동일한 함수가 계속해서 사용된다.