React 성능 최적화 고도화 – 리렌더링 감지부터 구조적 개선 전략까지
React 성능 최적화 고도화 – 불필요한 리렌더링 감지 및 개선 전략 총정리
React는 Virtual DOM을 기반으로 빠른 UI 업데이트를 제공하지만, 컴포넌트가 불필요하게 자주 리렌더링되면 성능 저하와 사용자 경험 악화로 이어질 수 있습니다.
이번 포스팅에서는 리렌더링의 원인부터 감지, 최적화 전략까지 실제 실무에서 적용할 수 있는 내용을 총정리합니다.
1. 왜 리렌더링을 줄여야 할까?
React는 상태(state), props, context, key 변경 등으로 인해 컴포넌트가 리렌더링됩니다. 하지만 다음과 같은 경우 불필요한 리렌더링이 발생할 수 있습니다.
- 부모 컴포넌트가 자주 리렌더링되며 자식까지 연쇄적으로 발생
- 불변성을 지키지 않아 reference가 매번 바뀜
- useCallback, useMemo를 제대로 사용하지 않아 캐싱 실패
- React.memo, PureComponent 미사용
리렌더링을 줄이는 것은 앱의 전체 성능, 반응속도, UX에 큰 영향을 미칩니다.
2. 리렌더링 감지 방법
🔍 2-1. React DevTools로 감지
크롬 확장 프로그램인 React DevTools에서 'Highlight Updates' 옵션을 켜면 리렌더링되는 컴포넌트를 시각적으로 볼 수 있습니다.
🔍 2-2. console.log 활용
useEffect(() => {
console.log('MyComponent 렌더링');
});
🔍 2-3. why-did-you-render 라이브러리
import React from 'react';
import whyDidYouRender from '@welldone-software/why-did-you-render';
if (process.env.NODE_ENV === 'development') {
whyDidYouRender(React, {
trackAllPureComponents: true,
});
}
컴포넌트가 어떤 props/state 변화로 인해 리렌더링됐는지 상세히 확인 가능합니다.
3. 실전 리렌더링 최적화 전략
✅ 3-1. React.memo 사용
props가 변경되지 않으면 리렌더링을 방지하는 고차 컴포넌트입니다.
const MyComponent = React.memo(({ value }) => {
return <div>{value}</div>;
});
✅ 3-2. useCallback으로 함수 캐싱
props로 함수를 전달할 때 매번 새로운 함수 reference를 넘기면 자식 컴포넌트가 리렌더링됩니다.
const handleClick = useCallback(() => {
doSomething();
}, [dependency]);
✅ 3-3. useMemo로 계산 값 캐싱
const expensiveValue = useMemo(() => {
return heavyCalculation(input);
}, [input]);
✅ 3-4. 불변성 유지
객체/배열 상태 업데이트 시 새로운 객체로 대체해야 합니다.
// ❌ 안티패턴
state.arr.push(1);
// ✅ 올바른 패턴
setArr([...arr, 1]);
4. 컴포넌트 분리 전략
리렌더링 영향을 줄이려면, 상태에 따라 변경되는 영역만 분리하여 리렌더링 범위를 최소화해야 합니다.
📦 상태 단위로 컴포넌트 분할
- 입력 폼 ↔ 미리보기 영역 분리
- 리스트 ↔ 리스트 항목 분리
📦 불필요한 context 사용 최소화
Context Provider 내부의 모든 컴포넌트는 value가 변경되면 전부 리렌더링됩니다.
해결: Provider를 세분화하거나, context value를 useMemo로 캐싱
5. 상태 관리 전략 개선
전역 상태로 인해 과도한 리렌더링이 발생할 수 있습니다. 아래는 각 상태 범위에 따른 권장 전략입니다.
상태 범위 | 권장 위치 |
---|---|
지역 UI 상태 | useState (로컬) |
페이지 간 공유 | Context, Zustand |
전역 앱 설정 | Redux / 상태 모듈 |
임시 상태 | useRef / useReducer |
6. Next.js 환경에서의 최적화 포인트
- SSR 렌더링 분리: 서버 컴포넌트 ↔ 클라이언트 컴포넌트 나누기
- Dynamic import를 통한 코드 스플리팅
- 렌더링 트리 최적화를 위한 Headless UI 전략 활용
7. 마무리 – '최적화는 정확한 측정에서 시작된다'
리렌더링을 무조건 줄이는 것이 능사는 아닙니다. 어떤 컴포넌트가 병목인지, 왜 리렌더링되는지 정확히 파악하는 것이 우선입니다.
요약:
- DevTools, 라이브러리로 리렌더링 원인 분석
- memoization 패턴 적극 활용
- 상태 범위에 따른 구조화된 설계
React 최적화는 '경험'과 '측정'의 반복입니다. 이 글을 통해 성능 병목 구간을 효율적으로 다루실 수 있기를 바랍니다.