일상이 개발

React 앱에서 성능 병목 찾기: DevTools, why-did-you-render, Profiler 실전 분석

디어노미 2025. 4. 11. 17:32
반응형

⚙️ React 앱에서 성능 병목 찾기

DevTools, why-did-you-render, Profiler 실전 분석 가이드

React로 앱을 개발하다 보면, 어느 순간 앱이 “느려졌다”고 느끼는 순간이 옵니다. 특히 페이지 전환, 리스트 렌더링, Form 조작 등 사용자 인터랙션이 많아질수록 성능 병목은 명확해집니다.

하지만 그 "느려짐"이 정확히 어디서 오는지 모르면 최적화는 막막하기만 하죠.
이번 글에서는 React 앱의 병목을 실전에서 어떻게 탐지하고 분석하며 개선할 수 있는지를 다음 도구들을 중심으로 소개합니다:

  • React DevTools Profiler
  • why-did-you-render 라이브러리
  • Chrome DevTools + 실제 UX 테스트 전략

🚨 성능 병목이란? 왜 생길까?

React 앱의 성능 병목은 주로 다음 3가지에서 발생합니다:

  1. 불필요한 리렌더링
    상태 변화가 직접 연관 없는 컴포넌트까지 영향을 주는 경우
  2. 복잡한 연산을 매 렌더마다 반복
    예: 무거운 계산, 정렬, 필터링이 컴포넌트 내에서 실행됨
  3. 비효율적인 컴포넌트 구조
    모든 UI가 하나의 상위 컴포넌트에 묶여있거나, props drilling이 과한 경우

🛠 React DevTools Profiler로 분석하기

React DevTools의 Profiler 탭은 브라우저 확장으로 설치할 수 있습니다.
크롬에서 설치 후, React 앱을 열면 "Profiler" 탭이 보입니다.

🔍 사용법

  1. Profiler 탭 클릭 → ‘Record’ 버튼 클릭
  2. 앱에서 느려지는 액션 수행 (예: 버튼 클릭, 리스트 필터링 등)
  3. 다시 Profiler 탭 → ‘Stop’ 클릭 → 분석 결과 확인

📈 분석 포인트

  • 렌더링 시간: 어떤 컴포넌트가 몇 ms 걸렸는가?
  • 리렌더 횟수: 같은 컴포넌트가 너무 자주 리렌더되는가?
  • 렌더 트리 흐름: 부모-자식 간 렌더링 연쇄가 발생했는가?

✅ 활용 팁

  • 너무 자주 리렌더되는 컴포넌트는 React.memo, useMemo, useCallback으로 묶을 후보
  • 무거운 리스트 컴포넌트는 virtual scroll 도입 검토
  • 상태 변경이 예상보다 많은 컴포넌트까지 퍼지지 않는지 확인

🧠 why-did-you-render로 리렌더링 감지하기

불필요한 리렌더링은 병목의 대표 원인입니다.
why-did-you-render는 이런 리렌더를 콘솔에서 자동으로 알려주는 강력한 도구입니다.

🔧 설치 및 세팅

npm install @welldone-software/why-did-you-render

_app.tsx 또는 _app.js에서 설정:

{`if (process.env.NODE_ENV === 'development') {
  const whyDidYouRender = require('@welldone-software/why-did-you-render');
  whyDidYouRender(React, {
    trackAllPureComponents: true,
  });
}`}

컴포넌트에서 리렌더 감지 활성화:

{`const MyComponent = React.memo(({ title }) => {
  return
  {title};
});

MyComponent.whyDidYouRender = true;`}

 

📝 콘솔 로그 예시

[why-did-you-render] MyComponent re-rendered because props.title changed shallowly.
  • props나 context 변경으로 인한 리렌더
  • useCallback, useMemo를 사용하지 않아 함수나 객체 참조가 매번 변경됨

🧪 DevTools + 실측 벤치마크 테스트

UI는 “수치”보다 실제 “느낌”이 중요할 때도 있습니다.
브라우저 DevTools → Performance 탭에서 아래 항목을 체크하세요:

🔍 확인 포인트

  • Layout Shift: 요소 이동으로 화면 흔들림 발생 여부
  • Long Tasks: 메인 스레드가 50ms 이상 점유된 작업
  • Scripting Time: JavaScript 실행 시간이 과도한 구간
  • Paint 시간: 브라우저가 화면을 다시 그리는 시간

이 도구는 React 외에도 전체 렌더링 체인을 포함해 분석하므로, 외부 라이브러리 병목도 감지 가능합니다.


🛠 실전 최적화 전략 정리

1️⃣ React.memo로 리렌더 방지

{`const Item = React.memo(({ name }) => {
  return
  {name};
});`}
 

props가 바뀌지 않으면 렌더 스킵
함수/객체는 참조가 바뀌면 리렌더 → useCallback 필수

2️⃣ useCallback, useMemo로 참조 유지

{`const handleClick = useCallback(() => {
  doSomething();
}, []);

const value = useMemo(() => computeValue(), [dependency]);`}

3️⃣ key 속성은 안정적으로 관리

{`// ❌ Bad
{items.map(item => )}

// ✅ Good
{items.map(item => )}`}

4️⃣ 조건부 렌더링 최소화

{`// ❌
{condition && }

// ✅
const MemoizedBigComponent = useMemo(() => , []);
return condition ? MemoizedBigComponent : null;`}

5️⃣ Lazy Load + Dynamic Import

{`import dynamic from 'next/dynamic';
const HeavyChart = dynamic(() => import('./Chart'), { ssr: false });`}

초기 로딩 속도 향상에 효과적입니다.


✅ 마무리 요약

React 앱이 “느려진다”는 느낌은 거의 대부분 렌더링 구조의 문제입니다.
DevTools, why-did-you-render, Profiler 등의 도구를 적극 활용하면 병목 지점을 빠르게 진단할 수 있습니다.

🔑 핵심 요약

항목 전략
리렌더 추적 why-did-you-render 도입
병목 시간 분석 React Profiler, Performance 탭 사용
최적화 기법 React.memo, useMemo, useCallback
큰 컴포넌트 로딩 최적화 dynamic import, lazy loading
상태 최적화 불필요한 props 전달 줄이기, 상태 최소화

성능 최적화는 한 번에 끝나지 않습니다.
작은 병목을 발견하고 고쳐가는 반복적인 흐름 속에서 더 좋은 React 앱을 만들어갈 수 있어요. 🚀

반응형