← 홈페이지 5강 목록으로
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. 리렌더링 원인

  1. state가 바뀔 때
  2. props가 바뀔 때
  3. 부모 컴포넌트가 리렌더링될 때 — 자식도 함께
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

  1. React Developer Tools 확장 설치 (Chrome/Firefox)
  2. DevTools → Profiler 탭
  3. 녹화 시작 → 앱 조작 → 녹화 중지
  4. 어떤 컴포넌트가 얼마나 자주, 오래 렌더링되는지 확인
  5. 병목을 React.memo / useMemo / useCallback으로 최적화
도구대상사용 시점
React.memo컴포넌트렌더링 비용 크고 props가 자주 안 바뀔 때
useMemo계산 결과비싼 계산 반복
useCallback함수memo 컴포넌트에 함수 prop 전달 시
useRefDOM/값렌더링 없이 값 유지
💡

성능 최적화는 문제가 생겼을 때 하세요. 미리 하면 코드만 복잡해집니다.

예제 코드 / 강의 자료

전체 강의 자료와 예제 코드는 GitHub에서 자유롭게 받아볼 수 있습니다.

GitHub에서 보기 ↗