⚡
EPISODE 06
React.memo · useMemo · useCallback · useRef
성능 최적화
불필요한 리렌더링을 방지하는 React.memo, 비싼 계산을 메모하는 useMemo, 함수 참조를 유지하는 useCallback, 그리고 렌더링 없이 값을 저장하는 useRef를 익힙니다.
ReactmemouseMemouseCallback
소요 시간
⏱ 45~60분
난이도
📊 중급~고급
선수 조건
🎯 react-05
결과물
Profiler로 측정한 병목을 정확히 최적화
이 강의에서 배우는 것
- 1불필요한 리렌더링이 발생하는 3가지 원인을 안다
- 2React.memo로 컴포넌트 메모이제이션을 한다
- 3useMemo로 비싼 계산 결과를 메모한다
- 4useCallback과 React.memo를 함께 써서 자식 리렌더를 막는다
- 5useRef로 DOM 접근/이전 값 저장을 한다
1. 리렌더링 원인
- state가 바뀔 때
- props가 바뀔 때
- 부모 컴포넌트가 리렌더링될 때 — 자식도 함께
jsx
function Parent() {
const [count, setCount] = useState(0);
return (
<>
<button onClick={() => setCount(c => c + 1)}>+{count}</button>
<Child /> {/* count와 무관하지만 함께 리렌더링 */}
</>
);
}2. React.memo
jsx
// props가 바뀌지 않으면 리렌더링 건너뜀
const ExpensiveChild = React.memo(function ExpensiveChild({ data }) {
console.log('렌더링');
return <div>{data}</div>;
});⚠️
모든 컴포넌트에 무조건 적용 X — memo 자체도 비교 비용. 렌더링 비용이 큰 컴포넌트에만.
3. useMemo
jsx
// ❌ 매 렌더링마다 필터링
const filteredProducts = products.filter(p => p.name.includes(filterKeyword));
// ✅ 의존성이 바뀔 때만 재계산
const filteredProducts = useMemo(
() => products.filter(p => p.name.includes(filterKeyword)),
[products, filterKeyword]
);4. useCallback
jsx
function Parent() {
const [count, setCount] = useState(0);
const [text, setText] = useState('');
// ❌ 매 렌더링마다 새 함수 → React.memo 무력화
const handleClick = () => setCount(c => c + 1);
// ✅ 항상 같은 함수 참조
const handleClick = useCallback(() => {
setCount(c => c + 1);
}, []);
return (
<>
<input value={text} onChange={e => setText(e.target.value)} />
<MemoizedButton onClick={handleClick}>{count}</MemoizedButton>
</>
);
}
const MemoizedButton = React.memo(function Button({ onClick, children }) {
return <button onClick={onClick}>{children}</button>;
});5. useRef
DOM 접근
jsx
function InputFocus() {
const inputRef = useRef(null);
const focusInput = () => inputRef.current.focus();
return (
<>
<input ref={inputRef} />
<button onClick={focusInput}>포커스</button>
</>
);
}렌더링 없이 값 저장
jsx
function Counter() {
const [count, setCount] = useState(0);
const prevCountRef = useRef(0);
useEffect(() => {
prevCountRef.current = count; // ref 변경은 리렌더링 X
});
return <p>현재: {count}, 이전: {prevCountRef.current}</p>;
}6. React DevTools Profiler
- React Developer Tools 확장 설치 (Chrome/Firefox)
- DevTools → Profiler 탭
- 녹화 시작 → 앱 조작 → 녹화 중지
- 어떤 컴포넌트가 얼마나 자주, 오래 렌더링되는지 확인
- 병목을 React.memo / useMemo / useCallback으로 최적화
| 도구 | 대상 | 사용 시점 |
|---|---|---|
| React.memo | 컴포넌트 | 렌더링 비용 크고 props가 자주 안 바뀔 때 |
| useMemo | 계산 결과 | 비싼 계산 반복 |
| useCallback | 함수 | memo 컴포넌트에 함수 prop 전달 시 |
| useRef | DOM/값 | 렌더링 없이 값 유지 |
💡
성능 최적화는 문제가 생겼을 때 하세요. 미리 하면 코드만 복잡해집니다.