"내 리액트 앱이 느린 것 같은데... 어디서부터 시작해야 하지?"
리액트 앱을 만들다 보면 어느 순간 이런 고민이 생깁니다. 이 글에서는 리액트 성능 문제를 진단하는 도구와 첫 페이지 로딩을 빠르게 만드는 방법을 초보자도 이해할 수 있게 정리합니다.
🔍 React Developer Tools — 컴포넌트를 직접 들여다보는 도구
브라우저 기본 개발자도구(F12)의 Elements 탭을 열면 우리가 짠 JSX 코드가 실제 HTML로 변환된 모습을 볼 수 있습니다. 하지만 이건 리액트 컴포넌트 구조와는 다르게 보여서 디버깅이 불편합니다.
이를 해결해주는 것이 React Developer Tools 크롬 확장프로그램입니다.
설치 방법
크롬 웹스토어에서 "React Developer Tools" 를 검색해 설치하면 됩니다. 설치 후 리액트로 만든 사이트에서 개발자도구(F12)를 열면 Components 탭과 Profiler 탭이 새로 생깁니다.
Components 탭 — 컴포넌트 구조 파악
Components 탭에서는 현재 화면을 구성하는 컴포넌트 트리를 한눈에 볼 수 있습니다.
- 왼쪽 패널: 컴포넌트 계층 구조를 트리 형태로 표시
- 왼쪽 상단 아이콘: 화면에서 특정 요소를 클릭하면 해당 컴포넌트를 바로 찾아줌
- 오른쪽 패널: 선택한 컴포넌트의 state와 props 값을 실시간으로 확인하고 직접 수정도 가능
💡 활용 팁: props를 보냈는데 화면에 출력이 안 된다면? Components 탭에서 해당 컴포넌트를 찾아 props가 제대로 전달됐는지 바로 확인해보세요.
📊 Profiler 탭 — 성능 병목 찾기
Profiler 탭은 어떤 컴포넌트가 얼마나 오래 렌더링되는지 측정해주는 성능 분석 도구입니다.
사용 방법
- Profiler 탭으로 이동
- 녹화 버튼(●) 클릭
- 페이지 이동이나 버튼 클릭 등 테스트할 동작 수행
- 녹화 중지
- 방금 렌더링된 모든 컴포넌트의 렌더링 시간 확인
느린 컴포넌트가 있다면 여기서 범인을 특정할 수 있습니다.
⚠️ 주의할 점: 성능 저하의 대부분은 <div>를 너무 많이 만든 것보다 서버에서 ajax 응답이 늦게 오는 경우가 훨씬 많습니다. 서버 응답이 느린 건 프론트에서 고치기 어렵습니다. Profiler에서 특이하게 느린 컴포넌트가 없다면 서버 쪽을 확인해보세요.
🛠️ Redux Developer Tools — Redux 상태 추적
Redux를 사용하고 있다면 Redux Developer Tools도 크롬 웹스토어에서 설치할 수 있습니다.
- Redux store에 보관된 모든 state를 실시간으로 확인
- dispatch를 날릴 때마다 어떤 값이 어떻게 바뀌었는지 로그로 기록
- store가 복잡해질수록 가치가 올라가는 도구
디버깅이 어려웠던 Redux 상태 관리 문제를 이 도구 하나로 대부분 해결할 수 있습니다.
🚀 lazy import — 첫 페이지 로딩 속도 개선
왜 리액트 앱은 첫 로딩이 느릴까?
npm run build로 리액트 프로젝트를 빌드하면 작성했던 모든 코드가 HTML, CSS, JS 파일로 변환됩니다. 그런데 리액트로 만드는 SPA(Single Page Application)의 특성상 JS 파일이 단 하나로 합쳐집니다. App, Detail, Cart 등 모든 컴포넌트가 이 하나의 파일 안에 들어가기 때문에 파일 크기가 커지고, 첫 페이지 로딩이 느려집니다.
예를 들어 사용자가 메인 페이지만 보러 왔는데도 Detail 페이지, Cart 페이지의 코드까지 모두 다운로드 받아야 하는 상황이 되는 것입니다.
해결책 — lazy import로 JS 파일 쪼개기
기존의 일반 import를 lazy import로 바꾸면 컴포넌트를 별도의 JS 파일로 분리할 수 있습니다.
// ❌ 기존 방식: 앱 시작 시 모두 한꺼번에 로드
import Detail from './routes/Detail.js';
import Cart from './routes/Cart.js';
// ✅ lazy import 방식: 필요할 때만 로드
import { lazy, Suspense } from 'react';
const Detail = lazy(() => import('./routes/Detail.js'));
const Cart = lazy(() => import('./routes/Cart.js'));
lazy로 감싼 컴포넌트는 "이 컴포넌트가 실제로 화면에 필요해지는 순간에 그때서야 파일을 불러오세요" 라는 의미가 됩니다. 덕분에 첫 페이지 진입 시 불필요한 JS 파일을 다운받지 않아 초기 로딩 속도가 빨라집니다.
Suspense — 로딩 중 화면 처리
lazy로 컴포넌트를 불러오는 동안 잠깐의 지연이 생길 수 있습니다. 이때 빈 화면이 보이면 사용자 경험이 좋지 않으니 Suspense 를 사용해 로딩 중 대체 화면을 보여줍니다.
<Suspense fallback={<div>로딩중입니다...</div>}>
<Detail shoes={shoes} />
</Suspense>
- fallback 속성에 로딩 중 보여줄 JSX를 작성합니다.
- 귀찮다면 <Suspense>로 <Routes> 전체를 한꺼번에 감싸도 됩니다.
lazy import를 쓰면 좋은 경우
모든 컴포넌트에 lazy를 적용할 필요는 없습니다. 아래 조건에 해당하는 컴포넌트에 우선 적용해보세요.
- 메인 페이지에서 보이지 않는 하위 페이지 컴포넌트 (Detail, Cart, MyPage 등)
- 용량이 크거나 무거운 라이브러리를 사용하는 컴포넌트
- 사용자가 자주 방문하지 않는 페이지
반대로 메인 페이지 진입 시 즉시 보여야 하는 컴포넌트는 일반 import를 유지하는 게 좋습니다.
🔖 정리
| React Developer Tools | 컴포넌트 구조, state/props 실시간 확인 및 수정 |
| Profiler 탭 | 컴포넌트별 렌더링 시간 측정, 성능 병목 탐지 |
| Redux Developer Tools | Redux store 상태 확인, dispatch 히스토리 추적 |
| lazy import | 컴포넌트를 별도 JS 파일로 분리해 초기 로딩 속도 개선 |
| Suspense | lazy 컴포넌트 로딩 중 대체 UI 표시 |
성능 개선은 무조건 처음부터 최적화하려는 것보다, 먼저 개발자도구로 문제를 파악하고 실제로 느린 부분을 찾아서 개선하는 순서로 접근하는 것이 효율적입니다. lazy import는 별다른 로직 변경 없이 import 한 줄만 바꾸면 되니 부담 없이 시도해볼 수 있는 첫 번째 성능 개선 방법입니다!
Suspense의 동작 흐름
Suspense의 동작 흐름을 단계별로 설명하면 이렇습니다.
📺 화면에 보이는 순서
1단계 — Detail 컴포넌트 파일을 아직 다운로드 중일 때
사용자 화면에는 fallback에 넣은 내용이 보입니다.
┌──────────────────────────┐
│ │
│ 로딩중입니다... │
│ │
└──────────────────────────┘
2단계 — 파일 다운로드가 완료되면
<div>로딩중입니다...</div> 가 사라지고 실제 Detail 컴포넌트가 그 자리에 렌더링됩니다.
┌──────────────────────────┐
│ │
│ [Detail 컴포넌트 내용] │
│ 신발 이름: Nike Air │
│ 가격: 129,000원 │
│ ... │
│ │
└──────────────────────────┘
💡 핵심 포인트
fallback에는 어떤 JSX든 넣을 수 있습니다. 단순한 텍스트 대신 스피너(로딩 애니메이션)나 스켈레톤 UI를 넣는 것이 실무에서 더 흔한 방식입니다.
// 텍스트
<Suspense fallback={<div>로딩중입니다...</div>}>
// 스피너 컴포넌트
<Suspense fallback={<Spinner />}>
// 스켈레톤 UI
<Suspense fallback={<SkeletonCard />}>
그리고 lazy import로 쪼개진 파일은 최초 1회만 다운로드됩니다. 사용자가 Detail 페이지를 한 번 방문한 뒤 다시 돌아오면 이미 브라우저에 캐싱되어 있어서 로딩 화면이 다시 뜨지 않습니다.
'공부 > frontend' 카테고리의 다른 글
| setState를 썼는데 값이 왜 이상하지? — async 완전 이해하기 (0) | 2026.07.01 |
|---|---|
| React 성능 개선2 — memo와 useMemo 완전 정리 (0) | 2026.07.01 |
| Tanstack Query(React Query) 완전 정복 — 실시간 데이터를 쉽게 다루는 법 (0) | 2026.07.01 |
| 리액트에서 자주 쓰는 if문 작성 패턴 5가지 (0) | 2026.06.17 |
| [javascript]자바스크립트 이벤트리스너 (0) | 2026.04.18 |