본문 바로가기
일상이 개발

React 성능 최적화 고도화 – 리렌더링 감지부터 구조적 개선 전략까지

by 디어노미 2025. 4. 15.
반응형

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 최적화는 '경험'과 '측정'의 반복입니다. 이 글을 통해 성능 병목 구간을 효율적으로 다루실 수 있기를 바랍니다.


 

반응형