본문 바로가기

React.js

[React] 렌더링 최적화하기 - React.memo 현명하게 사용하기

들어가며

React 애플리케이션에서 불필요한 리렌더링은 성능 저하의 주요 원인입니다. React.memo는 이러한 문제를 해결할 수 있는 강력한 도구지만, 무분별한 사용은 오히려 성능을 저하시킬 수 있습니다. 언제, 어떻게 React.memo를 사용해야 할지 알아보겠습니다.

React.memo가 필요한 이유

// 부모 컴포넌트가 리렌더링될 때마다 자식 컴포넌트도 불필요하게 리렌더링됩니다
const ParentComponent = () => {
  const [count, setCount] = useState(0);

  return (
    <div>
      <button onClick={() => setCount(count + 1)}>
        Count: {count}
      </button>
      <ExpensiveChild data="이 데이터는 변하지 않습니다" />
    </div>
  );
};

React.memo 기본 사용법

1. 간단한 사용 예시

const ExpensiveChild = React.memo(function ExpensiveChild({ data }) {
  return <div>{data}</div>;
});

2. 커스텀 비교 함수 사용

const MyComponent = React.memo(function MyComponent({ data }) {
  return <div>{data.value}</div>;
}, (prevProps, nextProps) => {
  return prevProps.data.value === nextProps.data.value;
});

React.memo를 사용해야 하는 경우

1. 순수 표현 컴포넌트

// Good: props가 자주 변경되지 않는 경우
const UserProfile = React.memo(function UserProfile({ name, avatar }) {
  return (
    <div>
      <img src={avatar} alt={name} />
      <h2>{name}</h2>
    </div>
  );
});

2. 비용이 큰 렌더링

// Good: 복잡한 계산이나 큰 리스트를 렌더링하는 경우
const ExpensiveList = React.memo(function ExpensiveList({ items }) {
  return (
    <div>
      {items.map(item => (
        <ComplexItem key={item.id} {...item} />
      ))}
    </div>
  );
});

React.memo를 피해야 하는 경우

1. 자주 변경되는 props

// Bad: props가 매번 변경되는 경우
const Counter = React.memo(function Counter({ count, onClick }) {
  return <button onClick={onClick}>{count}</button>;
});

// Parent
function Parent() {
  const [count, setCount] = useState(0);

  // 이 함수는 매 렌더링마다 새로 생성됩니다
  const handleClick = () => setCount(count + 1);

  return <Counter count={count} onClick={handleClick} />;
}

2. 단순한 컴포넌트

// Bad: 렌더링 비용이 메모이제이션 비용보다 적은 경우
const TooSimple = React.memo(function TooSimple({ text }) {
  return <span>{text}</span>;
});

최적화 팁

1. useCallback과 함께 사용하기

function Parent() {
  const [count, setCount] = useState(0);

  const handleClick = useCallback(() => {
    setCount(c => c + 1);
  }, []); // 의존성 배열이 비어있으므로 함수가 재생성되지 않습니다

  return <Counter count={count} onClick={handleClick} />;
}

2. useMemo로 객체 props 최적화

function Parent() {
  const data = useMemo(() => ({
    title: "제목",
    description: "설명"
  }), []); // 객체가 재생성되지 않습니다

  return <ComplexComponent data={data} />;
}

성능 측정하기

React DevTools 사용

// 개발 모드에서만 실행
if (process.env.NODE_ENV === 'development') {
  const whyDidYouRender = require('@welldone-software/why-did-you-render');
  whyDidYouRender(React);
}

실제 측정 예시

const MeasuredComponent = React.memo(function MeasuredComponent({ data }) {
  console.time('render');
  // 렌더링 로직
  console.timeEnd('render');
  return <div>{/* 컴포넌트 내용 */}</div>;
});

체크리스트

React.memo 사용 전 확인사항:

  1. 컴포넌트가 같은 props로 자주 렌더링되는가?
  2. 컴포넌트의 렌더링 비용이 큰가?
  3. 컴포넌트의 props가 자주 변경되지 않는가?
  4. 부모 컴포넌트가 자주 리렌더링되는가?

마무리

React.memo는 강력한 도구지만, 모든 상황에서 사용하는 것은 오히려 역효과를 낼 수 있습니다.
컴포넌트의 특성과 실제 성능 측정을 통해 필요한 곳에만 적절히 사용하는 것이 중요합니다.