React 컴포넌트 성능 최적화: shouldComponentUpdate vs PureComponent
문제 상황
실시간 데이터를 보여주는 대시보드 화면에서 성능 문제가 발생했다. Chrome DevTools의 React Profiler로 확인해보니, 상위 컴포넌트가 업데이트될 때마다 변경되지 않은 하위 컴포넌트들까지 모두 리렌더링되고 있었다.
shouldComponentUpdate 적용
가장 먼저 시도한 방법은 shouldComponentUpdate를 직접 구현하는 것이었다.
class DataCard extends React.Component {
shouldComponentUpdate(nextProps) {
return this.props.data.id !== nextProps.data.id ||
this.props.data.value !== nextProps.data.value;
}
render() {
const { data } = this.props;
return (
<div className="card">
<h3>{data.title}</h3>
<p>{data.value}</p>
</div>
);
}
}
이 방법은 효과가 있었지만, 비교해야 할 props가 많아지면 코드가 길어지고 실수하기 쉬웠다.
PureComponent로 개선
React.PureComponent를 사용하면 shallow comparison을 자동으로 수행한다.
class DataCard extends React.PureComponent {
render() {
const { data } = this.props;
return (
<div className="card">
<h3>{data.title}</h3>
<p>{data.value}</p>
</div>
);
}
}
훨씬 간결해졌고, 리렌더링 횟수도 약 70% 감소했다.
주의사항
PureComponent 사용 시 주의할 점이 있다. props로 객체나 배열을 전달할 때 매번 새로 생성하면 shallow comparison에서 항상 다르다고 판단한다.
// 잘못된 예
<DataCard data={{ value: this.state.value }} />
// 올바른 예
<DataCard value={this.state.value} />
리스트를 다룰 때는 filter, map 같은 메서드가 새 배열을 반환한다는 점도 고려해야 한다. render 메서드 내부에서 데이터를 가공하지 말고, 필요하다면 memoization을 활용하는 것이 좋다.
결과
최종적으로 주요 컴포넌트 10개에 PureComponent를 적용했고, 대시보드 전체 렌더링 시간이 약 400ms에서 150ms로 단축되었다. 사용자 인터랙션도 훨씬 부드러워졌다.