프로젝트 규모가 커지면서 가장 먼저 체감한 변화는 Hot Reloading 속도 저하였습니다.
작은 수정에도 화면 반영이 늦어지면서 개발 흐름이 끊기기 시작했고, 이 과정에서 전체 Webpack 빌드 시간 역시 함께 증가하고 있다는 문제도 확인했습니다.
개발 속도와 배포 템포 모두에 영향을 주는 신호였기 때문에, 빌드 파이프라인 전반을 점검하고 최적화가 필요하다고 판단했습니다.
👉 최적화 과정을 더 잘 이해하고 싶다면 아래 글을 먼저 읽어보세요!
Webpack 빌드 프로세스 처음부터 끝까지 한 번에 이해하기
이번 글에서는 Hot Reloading과 전체 빌드 시간이 느려진 원인을 분석한 방법과, 이를 어떻게 개선했는지 구체적인 과정과 결과를 공유합니다.
프로젝트가 커지면서 가장 먼저 체감된 변화는 Hot Reloading 속도 저하였습니다.
초기에는 저장 즉시 화면이 갱신됐지만, 코드가 늘어날수록 변경 반영에 0.5~1초까지 걸리며 개발 흐름을 끊어놓는 병목이 되었습니다.
이 문제를 이해하기 위해 Hot Reloading의 동작 방식을 살펴보면 다음과 같습니다.
여기서 자연스럽게 다음과 같은 의문이 생깁니다.
“변경된 모듈만 처리하는데, 왜 프로젝트가 커지면 Hot Reloading이 느려질까?”
“변경된 범위는 비슷한데 처리 시간은 왜 증가할까?”
핵심은, Webpack의 부분 빌드조차 “전체 프로젝트 규모를 기반으로” 이루어진다는 점입니다. 따라서 프로젝트가 커질수록 Hot Reloading이 수행해야 하는 기본적인 처리 비용도 함께 증가하게 됩니다.
결국 Hot Reloading 속도를 개선하려면, 변경된 파일을 가장 먼저 처리하는 로더 자체를 더 빠른 도구로 교체해야 한다는 결론에 도달했습니다.
Hot Reloading 문제를 분석하던 중, 전체 Webpack 빌드 시간 역시 꾸준히 증가하고 있다는 사실을 확인했습니다.
로컬 빌드는 7~8초, 배포 환경(CodeBuild)에서는 40초 이상 걸리며, 프로젝트 규모가 커질수록 병목이 더 분명하게 나타났습니다.
빌드는 여러 단계로 구성되기 때문에 원인이 다양할 수 있습니다.
그래서 speed-measure-webpack-v5-plugin(SMWP)을 사용해 로더와 플러그인별 시간을 계측해 보기로 했습니다. 이 도구는 Webpack 파이프라인에서 어떤 단계가 시간을 가장 많이 쓰는지 정확하게 보여줍니다.
SMWP 로그를 분석해보니 측정 결과는 다음과 같았습니다.
로그를 보면 전체 빌드 시간(약 7.16초) 중 TerserPlugin이 단독으로 3.95초를 사용하고 있었습니다.
즉, 전체 빌드 시간의 50% 이상이 minify 단계에서 소모되고 있었던 것입니다.
이를 통해 병목 지점이 매우 명확해졌습니다.
결국 전체 빌드가 느린 근본 원인은 코드 압축 과정, 즉 Terser 기반의 minify 단계가 지나치게 느리다는 점이었습니다.
빌드 병목이 Babel과 Terser에 있다는 것이 확인된 후, 대체 도구로 esbuild와 SWC를 비교했습니다. Webpack 기반 프로젝트에서 속도·호환성·마이그레이션 난이도를 고려했을 때 SWC가 더 적절한 선택이라고 판단했습니다.
1) React + TypeScript 호환성과 안정성
esbuild는 속도는 빠르지만 React/TS 트랜스파일링에서 Babel 수준의 세밀함은 부족합니다. SWC는 Babel 대체를 목표로 만들어져 호환성이 안정적입니다.
2) Babel 옵션과 구조가 유사해 교체 비용이 거의 없음
SWC는 Babel과 프리셋 옵션 구조가 비슷해, 기존 설정을 거의 그대로 옮길 수 있습니다.
3) Webpack 구조를 유지하기에 가장 자연스러운 선택
esbuild는 번들러 성격이 강해 Webpack 자체를 대체하는 방향이지만, SWC는 Babel/Terser만 대체하는 트랜스파일러·미니파이어입니다.
4) 실제 빌드 환경에서 성능 차이도 크게 벌어지지 않음
공식 비교에서는 esbuild가 더 빠르지만, Webpack 기반 빌드에서는 SWC도 충분히 빠르고 체감 속도 차이가 크지 않습니다.
Babel과 SWC의 결정적 차이는 구현 언어입니다.
| 도구 | 구현 언어 | 특징 |
|---|---|---|
| Babel | JavaScript | 유연하고 확장성 높지만 상대적으로 느림 |
| SWC | Rust | 컴파일러 수준 성능, 멀티스레드 가능 |
즉, 기능은 같지만 구현 방식 자체가 속도 차이를 만들어냅니다.
Terser와 SWC minifier 역시 핵심 차이는 언어 성능 + 알고리즘 최적화 정도입니다.
| 도구 | 구현 언어 | 장점 | 단점 |
|---|---|---|---|
| TerserPlugin | JavaScript | 안정성·호환성 높음 | 압축 단계가 매우 느림 |
| SwcMinifyWebpackPlugin | Rust | 10~20배 빠른 압축 속도 | (초기엔) 일부 edge-case 이슈 |
Webpack 빌드 분석에서도 확인했듯,
Rust 기반의 병렬 압축기이기 때문에 대규모 번들에서도 속도가 크게 개선됩니다.
Webpack의 병목을 SWC 기반 스택으로 교체한 뒤, Hot Reloading부터 전체 빌드, 배포 빌드까지 전 구간에서 즉각적인 성능 개선이 확인되었습니다.
먼저 Hot Reloading 시간을 측정했습니다.
[개선 전 – 약 575ms]
[개선 후 – 약 220ms]
기존에는 파일 크기나 의존성 규모에 따라 0.5~0.9초까지 지연되는 경우도 있었고, 페이지 단위 변경 시 UI 전체가 잠시 멈추는 느낌도 있었습니다.
SWC 기반의 swc-loader로 교체한 뒤에는 대부분의 수정이 0.2~0.3초 내에 반영되어, 체감 속도가 완전히 달라졌습니다.
가장 큰 병목이었던 TerserPlugin을 SWC 기반의 SwcMinifyWebpackPlugin으로 교체하면서, 압축 단계 → 전체 빌드 → 배포 빌드까지 전 구간에서 속도가 크게 개선되었습니다.
아래는 변경 후 빌드 로그입니다.
TerserPlugin이 빌드 시간의 절반 이상(3.95초)을 차지했지만, SWC minifier는 Rust 기반의 병렬 처리로 0.169초만에 압축을 완료했습니다.
Rust 기반 로더와 minifier의 전환만으로도 전체 빌드 속도가 7.16초에서 3.15초로 약 2배 빨라졌습니다.
로컬 빌드보다 더 중요한 부분은 실제 배포 환경에서의 효과입니다.
AWS CodeBuild는 매번 캐시 없이 완전한 cold build를 수행하기 때문에 압축기의 성능이 매우 크게 반영됩니다.
아래는 CodeBuild 빌드 로그 비교입니다.
[개선 전 – BUILD 단계 약 40초]
[개선 후 – BUILD 단계 약 16초]
빌드 단계 시간이 40초 → 16초로 줄어들며, 전체 배포 속도가 약 60% 개선되었습니다.
단순 수치로도 2.5배 이상 빠른 빌드 파이프라인을 얻게 되었습니다.
이번 Webpack 최적화 작업을 진행하면서 가장 크게 느낀 점은, “감으로 최적화하지 않는다”는 원칙이 얼마나 중요한지였습니다.
이전에 Webpack 전체 빌드 사이클을 처음부터 끝까지 정리해두었던 덕분에, 각 로더와 플러그인이 어떤 역할을 하는지 정확히 이해할 수 있었고, 그 이해를 기반으로 병목 구간을 명확하게 찾아낼 수 있었습니다.
Webpack 최적화를 시작한다면 다음 두 가지를 기억하세요.
1) HMR 개선은 로더에서 시작된다
변경된 파일을 가장 먼저 처리하는 로더의 속도가 Hot Reloading 체감 속도를 결정합니다.
2) 빌드 개선은 수치 기반 병목 분석이 핵심이다
SMWP 같은 도구로 실제 시간을 측정하면, 추측 없이 가장 효과적인 최적화 지점을 찾을 수 있습니다.
이 두 가지 원칙만으로도 시행착오 없이 의미 있는 성능 개선을 해낼 수 있습니다.