React 16.6의 React.memo로 불필요한 리렌더링 줄이기

문제 상황

대시보드 프로젝트에서 리스트 아이템이 300개 이상일 때 스크롤이 버벅이는 이슈가 있었다. Chrome DevTools Profiler로 확인해보니 부모 컴포넌트의 상태 변경 시 모든 자식 컴포넌트가 리렌더링되고 있었다.

기존에는 클래스 컴포넌트에서 PureComponent를 사용하거나 shouldComponentUpdate를 직접 구현했는데, 함수형 컴포넌트에서는 방법이 마땅치 않았다.

React.memo 도입

React 16.6에서 추가된 React.memo를 사용하면 함수형 컴포넌트도 메모이제이션할 수 있다.

import React from 'react';

const ListItem = ({ id, title, onClick }) => {
  console.log('ListItem render:', id);
  return (
    <li onClick={onClick}>
      {title}
    </li>
  );
};

export default React.memo(ListItem);

기본적으로 shallow comparison을 수행하며, 커스텀 비교 함수도 전달할 수 있다.

export default React.memo(ListItem, (prevProps, nextProps) => {
  return prevProps.id === nextProps.id && 
         prevProps.title === nextProps.title;
});

적용 결과

부모 컴포넌트에서 필터 상태가 변경될 때, 실제로 props가 변경된 아이템만 리렌더링되도록 개선했다. Profiler 결과 렌더링 시간이 평균 450ms에서 80ms로 감소했다.

주의할 점은 props로 함수를 전달할 경우 매번 새로운 참조가 생성되어 메모이제이션이 무효화된다는 것이다. 이 경우 부모에서 useCallback을 사용해야 하는데, 아직 Hooks가 정식 릴리즈되지 않아서 클래스 컴포넌트 메서드를 활용했다.

결론

React.memo는 함수형 컴포넌트의 성능 최적화에 유용한 도구다. 특히 리스트 렌더링이나 props 변경이 적은 컴포넌트에 효과적이다. 다만 모든 컴포넌트에 적용하기보다는 실제 성능 이슈가 있는 곳에 선택적으로 사용하는 것이 좋다.

React 16.6의 React.memo로 불필요한 리렌더링 줄이기