React.memo – 컴포넌트 리렌더링 방지💡 한 줄 요약:
props가 변경되지 않았다면 컴포넌트의 리렌더링을 건너뜁니다.
부모 컴포넌트가 자주 리렌더링되는 구조에서, 자식 컴포넌트의 props가 변하지 않는다면 React.memo를 통해 리렌더를 막을 수 있습니다.
const ListItem = React.memo(({ value }) => {
console.log("렌더링됨:", value);
return <li>{value}</li>;
});
export default function App() {
const [count, setCount] = useState(0);
const items = ["A", "B", "C"];
return (
<div>
<button onClick={() => setCount(count + 1)}>+1</button>
<ul>
{items.map((item) => (
<ListItem key={item} value={item} />
))}
</ul>
</div>
);
}
+1버튼을 눌러도ListItem은 리렌더링되지 않습니다. (props가 변하지 않았기 때문)
React.memo는 아무 효과가 없습니다.useCallback, useMemo와 함께 써야 합니다.useCallback – 함수 참조 고정하기💡 한 줄 요약:
함수형 컴포넌트에서 핸들러 함수가 불필요하게 새로 생성되는 것을 막아줍니다.
부모 컴포넌트에서 자식에게 콜백 함수를 props로 넘길 때, 리렌더링을 방지하고 싶다면 useCallback이 필수입니다.
const Child = React.memo(({ onClick }) => {
console.log("자식 렌더링");
return <button onClick={onClick}>Click me</button>;
});
export default function Parent() {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
console.log("clicked");
}, []);
return (
<div>
<Child onClick={handleClick} />
<button onClick={() => setCount(count + 1)}>+1</button>
</div>
);
}
+1을 눌러도Child컴포넌트는 리렌더링되지 않습니다.handleClick이useCallback으로 고정됐기 때문입니다.
useMemo – 계산 결과 캐싱💡 한 줄 요약:
비싼 연산 결과를 캐싱해서 불필요한 계산을 줄입니다.
useMemo로 메모이제이션할 수 있습니다export default function App() {
const [count, setCount] = useState(0);
const numbers = [1, 3, 2, 4, 5];
const sorted = useMemo(() => {
console.log("정렬 실행됨");
return [...numbers].sort((a, b) => a - b);
}, [numbers]);
return (
<div>
<p>Sorted: {sorted.join(", ")}</p>
<button onClick={() => setCount(count + 1)}>+1</button>
</div>
);
}
+1을 눌러도sorted는 재계산되지 않습니다.numbers가 변하지 않았기 때문입니다.
useMemo도 한계가 있음useMemo 자체가 오히려 오버헤드가 될 수 있습니다| 도구 | 목적 | 효과적인 사용 시점 |
|---|---|---|
React.memo | props가 바뀌지 않으면 리렌더링 방지 | 자식 컴포넌트가 동일한 props를 받을 때 |
useCallback | 함수 참조 고정 | 함수 props로 자식 컴포넌트를 제어할 때 |
useMemo | 계산 결과 캐싱 | 반복적인 무거운 계산이 있는 경우 |
불필요한 렌더링을 줄이는 것은 성능 최적화의 출발점입니다.
하지만 무조건memo,useCallback,useMemo를 쓰기보다는 정말 필요한 곳에만 적용하는 게 중요합니다.