TIL: useCallback, useMemo는 언제 진짜 써야 할까?
React에서 useCallback과 useMemo는 무조건 성능 최적화에 도움을 줄까? 아니다. 잘못 쓰면 오히려 독이 된다.
useCallback, useMemo는 언제 필요할까?
React에서 useCallback, useMemo는 컴포넌트가 리렌더링될 때 같은 값을 재계산하지 않도록 막아주는 훅이다. 하지만, 다음과 같은 오해가 있다:
props로 넘기는 값은 어차피 리렌더링에 영향을 안 주니까 memoization할 필요 없다?
사실 props 자체는 리렌더링의 트리거가 아니지만, 자식 컴포넌트에서 useEffect의 dependency나 React.memo와 함께 쓸 경우 상황이 달라진다.
꼭 써야 하는 상황
1.
자식 컴포넌트에 props로 넘길 때, 그 값이 useEffect 같은 훅의 의존성으로 쓰이는 경우
// 부모 컴포넌트
const Parent = () => {
const handleClick = () => console.log('clicked');
return <Child onClick={handleClick} />;
};
// 자식 컴포넌트
const Child = ({ onClick }) => {
useEffect(() => {
console.log('onClick이 변경됨');
}, [onClick]);
return <button onClick={onClick}>click</button>;
};
TypeScript
복사
위 코드에서 handleClick이 매 렌더링마다 새로 생성되기 때문에 Child의 useEffect가 매번 실행된다. 이럴 때는 useCallback으로 감싸줘야 한다.
2.
React.memo로 감싼 컴포넌트에 non-primitive 값을 넘기는 경우
const Child = React.memo(({ config }) => {
return <div>{config.label}</div>;
});
const Parent = () => {
const config = { label: 'hello' }; // 매번 새 객체 생성됨
return <Child config={config} />;
};
TypeScript
복사
config는 객체이기 때문에 매 렌더링마다 참조값이 달라져 React.memo가 무용지물이 된다. 이럴 땐 useMemo로 감싸준다:
const config = useMemo(() => ({ label: 'hello' }), []);
TypeScript
복사
3.
children을 넘길 때도 동일하게 적용된다.
React에서는 children도 결국 props이고, 함수형/JSX라면 non-primitive이므로 React.memo 자식에게 넘길 경우 주의해야 한다.