Last edited: March 16, 2026
32만줄 규모의 레거시 React 프로젝트에서 로컬 개발 서버 시작에 4~5분, HMR이 사실상 작동하지 않는 문제를 겪었다. Vite로 번들러를 교체하니 체감 속도가 극적으로 개선됐는데, "왜 빨라지는가"를 감이 아닌 구조적 근거로 설명할 수 있어야 했다. 이 글은 그 분석 과정을 정리한 것이다.
대상 프로젝트의 스펙은 다음과 같았다.
2020년 말에 시작된 프로젝트인데, 당시 이미 Webpack 5가 정식 릴리즈(2020년 10월)되었고 Vite 1.0도 나온 상태였다. 프로젝트 시작 시점에 이미 더 나은 선택지가 있었지만, CRA 4 + Webpack 4로 시작된 것이다.
Webpack 4는 Bundle-based Dev Server다. 개발 서버를 띄우려면 전체 소스코드를 파싱하고, 의존성 그래프를 구축하고, 번들을 생성한 뒤에야 브라우저에서 접근할 수 있다. 32만줄 + 976MB node_modules를 매번 처리하니 느릴 수밖에 없다.
추가로 발견한 번들 비효율:
| 문제 | 영향 |
|---|---|
| moment.js 137개 locale 전부 포함 | IgnorePlugin 미설정, 불필요한 5.2MB |
| lodash 전체 import | tree-shaking 플러그인 없음, 4.9MB |
| core-js 풀 폴리필 | 현대 브라우저 타겟인데 14MB 폴리필 |
| 4중 폴리필 중복 | core-js + regenerator + react-app-polyfill + event-source-polyfill |
react-refresh-webpack-plugin v0.4.3은 Webpack 4에서 알려진 호환성 이슈가 있고, react-app-rewired로 CRA 내부 설정을 monkey-patch하면서 HMR 클라이언트 주입 타이밍이 꼬질 수 있다. 게다가 단일 컴포넌트에 useState가 29개인 파일에서는 React Fast Refresh가 state 보존을 포기하고 full remount를 수행한다 — 새로고침과 다를 바 없다.
번들러만의 문제가 아니었다. 코드 자체가 번들러에게 최악의 입력을 제공하고 있었다.
"Vite 넣으니 빨라졌다"가 아니라, 왜 이 프로젝트에서 특히 효과적인지 설명할 수 있어야 했다.
[Webpack 4 — Bundle-based]
시작 → 전체 파일 파싱 → 의존성 그래프 구축 → 번들 생성 → Dev Server 시작
⏱️ 32만줄 전부 처리 후에야 브라우저 접근 가능
[Vite — Native ESM]
시작 → esbuild로 deps만 pre-bundle → Dev Server 즉시 시작
→ 브라우저가 요청하는 모듈만 on-demand 변환
⏱️ 요청된 파일만 처리이 프로젝트에서 차이가 극적인 이유:
[Webpack 4] 파일 변경 → 관련 청크 전체 리빌드 → 번들 교체
[Vite] 파일 변경 → 해당 모듈 1개만 ESM 교체 → 브라우저가 해당 모듈만 re-fetchVite의 HMR은 프로젝트 크기와 무관하게 일정한 속도를 유지한다. 5천줄짜리 파일을 수정해도 해당 모듈만 교체하면 되기 때문이다.
react-app-rewired + customize-cra의 monkey-patch가 vite.config.js 하나로 대체된다config-overrides.js에서 빠져 있던 moment locale 제거, lodash tree-shaking 같은 최적화가 Vite 생태계에선 더 자연스럽게 적용된다