React 컴포넌트 렌더링 최적화 - PureComponent vs shouldComponentUpdate

문제 상황

관리자 대시보드에서 실시간 데이터를 polling으로 받아오는데, 부모 컴포넌트가 업데이트될 때마다 하위 컴포넌트들이 모두 리렌더링되면서 화면이 버벅이는 현상이 발생했다. Chrome DevTools Profiler로 확인해보니 props가 변하지 않는 컴포넌트들도 계속 렌더링되고 있었다.

해결 방법

1. PureComponent 적용

가장 간단한 방법은 React.PureComponent를 사용하는 것이다. shallow comparison으로 props와 state를 비교해 변경이 없으면 렌더링을 스킵한다.

class UserCard extends React.PureComponent {
  render() {
    const { user } = this.props;
    return (
      <div className="user-card">
        <h3>{user.name}</h3>
        <p>{user.email}</p>
      </div>
    );
  }
}

2. shouldComponentUpdate 커스텀

PureComponent의 shallow comparison으로 부족한 경우, shouldComponentUpdate를 직접 구현했다.

class DataTable extends React.Component {
  shouldComponentUpdate(nextProps, nextState) {
    // 특정 props만 비교
    return (
      nextProps.data.length !== this.props.data.length ||
      nextProps.sortOrder !== this.props.sortOrder
    );
  }
  
  render() {
    // 무거운 렌더링 로직
  }
}

3. 불변성 유지

PureComponent가 제대로 동작하려면 props를 불변하게 관리해야 한다. 배열이나 객체를 직접 수정하지 않고 새로 생성했다.

// Bad
this.state.items.push(newItem);
this.setState({ items: this.state.items });

// Good
this.setState({ 
  items: [...this.state.items, newItem] 
});

결과

  • 초당 렌더링 횟수: 45회 → 13회
  • FPS: 3040 → 5560
  • 사용자 인터랙션 반응 속도 체감 개선

주의사항

PureComponent는 shallow comparison만 하기 때문에 nested object가 변경되는 경우 감지하지 못한다. 이런 경우는 shouldComponentUpdate를 직접 구현하거나, immutable.js 같은 라이브러리 사용을 고려해야 한다.

상태 관리가 복잡해지면서 Redux를 도입할지 고민 중이다. reselect 같은 memoization 라이브러리도 함께 검토해볼 예정이다.

React 컴포넌트 렌더링 최적화 - PureComponent vs shouldComponentUpdate