⚛️ React 렌더링 최적화: memo, useCallback, useMemo

김덕진·2025년 6월 8일

React

목록 보기
11/13

✅ 1. 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는 얕은 비교(shallow equality)만 합니다.
  • props로 전달된 함수나 객체가 항상 새로운 참조라면 React.memo는 아무 효과가 없습니다.
    → 이럴 땐 useCallback, useMemo와 함께 써야 합니다.

✅ 2. 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 컴포넌트는 리렌더링되지 않습니다. handleClickuseCallback으로 고정됐기 때문입니다.

⚠️ 주의할 점

  • 의존성 배열이 올바르지 않으면 stale closure 문제 발생
  • 의존성이 많거나, 함수 자체가 가볍다면 굳이 쓰지 않아도 됩니다
  • 과도하게 쓰면 읽기 어려운 코드가 될 수 있음

✅ 3. 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.memoprops가 바뀌지 않으면 리렌더링 방지자식 컴포넌트가 동일한 props를 받을 때
useCallback함수 참조 고정함수 props로 자식 컴포넌트를 제어할 때
useMemo계산 결과 캐싱반복적인 무거운 계산이 있는 경우

불필요한 렌더링을 줄이는 것은 성능 최적화의 출발점입니다.
하지만 무조건 memo, useCallback, useMemo를 쓰기보다는 정말 필요한 곳에만 적용하는 게 중요합니다.

profile
FrontEnd Developer

0개의 댓글