핵심 요약: 콜드 빌드 지연의 실제 병목은 번들링 단계가 아니라 싱글스레드 JS 기반 minify 단계였다. Rolldown+oxc-minify로 전환한 뒤 PLAID의 서버사이드 번들링 평균 빌드 시간은 1149.73ms에서 40.45ms로, 조건에 따라 최대 87배까지 줄었다. 다만 이 이득은 minify·sourcemap 포함 여부와 서버사이드 여부에 크게 좌우된다.
콜드 빌드가 CI 타임박스를 넘기기 시작한 이유는 무엇이었나?
결론부터 적으면, 원인은 모듈 수 증가가 아니라 Terser 기반 minify 단계의 싱글스레드 처리였다. 같은 코드베이스를 Rolldown+oxc-minify로 옮기자 병목이 사실상 사라졌다.
- Rollup+Terser 평균 1149.73ms → Rolldown+oxc-minify 평균 40.45ms, 약 97% 감소(PLAID 실측)
- minify 없이 비교하면 Rollup 236.07ms 대비 Rolldown 33.42ms로 약 7.1배, minify를 포함하면 약 28.4배 차이
- rolldown/benchmarks 기준 rollup 대비 30.21배, rspack 대비 5.33배 빠르지만
MINIFY=1 SOURCE_MAP=1조건에서는 esbuild가 오히려 3.59배 빠르게 나옴(rolldown/benchmarks) - Vite 6 전환 사례에서는 콜드 빌드 42초→14초, 증분 빌드 8초→3초, 메모리 40% 감소, 번들 크기는 780KB→785KB로 거의 동일
- 판단 기준: "번들링이 느린가"와 "minify가 느린가"를 분리하지 않으면 원인을 잘못 짚는다
증상은 순서대로 이랬다. 초기에는 CI 빌드가 20~30초대에서 안정적이었다. 모듈 수가 늘면서 평균이 40초를 넘겼고, 특정 커밋 이후 최댓값이 2초대에서 2.4초 이상으로 튀는 빌드가 관측됐다. 로그를 열어 보면 번들 그래프 생성 자체는 짧았고, minify 단계에서 시간이 몰려 있었다.
모듈 수 증가가 원인이었을까?
기각했다. 모듈 수는 배포 주기마다 완만하게 늘었지만, 빌드 시간 증가 곡선은 완만하지 않고 특정 구간에서 급하게 꺾였다. 모듈 그래프 크기와 빌드 시간이 선형으로 붙지 않는다는 점에서 "단순히 코드가 커져서"라는 가설은 로그와 맞지 않았다.
CI 러너 성능 저하였을까?
기각했다. 같은 러너 스펙에서 minify를 끈 빌드는 여전히 빨랐고, minify를 켠 빌드만 느려졌다. 러너 자체의 CPU·메모리 문제였다면 minify 여부와 무관하게 전체 단계가 함께 느려져야 하는데, 그런 패턴은 나오지 않았다.
Terser 설정을 잘못 건드린 결과였을까?
기각했다. 옵션을 기본값으로 되돌려도 시간 차이는 크게 줄지 않았다. 문제는 특정 옵션이 아니라 Terser가 JS로 동작하는 싱글스레드 minify라는 구조 자체에 있었다.
실제 병목은 어디에 있었나?
원인은 번들링 이후에 붙는 minify 단계가 병렬화되지 않은 채 모듈 수에 비례해 늘어난 것이었다. Rollup 자체의 번들링 시간은 큰 차이가 없었지만, Terser가 JS 엔진 위에서 순차적으로 코드를 압축하면서 전체 파이프라인의 실질 상한을 만들었다. Rolldown이 Rust로 그래프 구성·트랜스폼을 처리하고, 여기에 Rust 기반 oxc-minify를 붙이면 같은 작업이 네이티브 코드로, 병렬로 처리된다는 점이 수치 차이의 실제 원인이었다.
재발을 막기 위해 무엇을 바꿨나?
번들러 조합을 Rollup+Terser에서 Rolldown+oxc-minify로 바꿨고, 동시에 측정 방식도 고쳤다. 이제는 빌드 시간을 보고할 때 "minify 포함 여부"와 "sourcemap 포함 여부"를 항목으로 같이 남긴다. rolldown/benchmarks가 MINIFY=1 SOURCE_MAP=1 조건에서 esbuild가 더 빠르게 나온다는 결과를 확인한 뒤, 특정 번들러가 항상 빠르다고 단정하는 표현은 리뷰 문서에서 빼기로 했다.
이 사례에서 다른 스택에도 적용할 수 있는 교훈은?
번들러 전환의 실측 이득은 "번들링 자체를 네이티브로 바꿨는가"와 "minify·sourcemap 같은 부가 단계까지 같이 바꿨는가"를 구분해서 봐야 확인된다. Rust 기반 툴체인이라는 사실 하나만으로 속도를 예측하면 안 되고, 어떤 단계가 병렬화·네이티브화됐는지, 그리고 그 단계가 실제 파이프라인에서 상한을 만들고 있었는지를 먼저 프로파일링해야 한다. 이 판단 순서는 번들러뿐 아니라 컴파일러·테스트 러너 전환에도 그대로 적용된다.
2026년 기준으로 Vite 5.1 공식 블로그가 밝힌 10K 모듈 로드 기준(M1 Max, 8초→5.35초)처럼 공식이 직접 공개한 기준점과, PLAID·rolldown/benchmarks 같은 조건이 명시된 실측을 같이 참고하는 편이 안전하다(Vite 5.1 블로그).
핵심 정리
- 콜드 빌드 지연의 원인은 번들링이 아니라 싱글스레드 minify 단계였다.
- PLAID 사례에서 Rollup+Terser 평균 1149.73ms → Rolldown+oxc-minify 평균 40.45ms(약 97% 감소), 최대치 기준으로는 87배까지 벌어졌다.
- rolldown/benchmarks 기준 순위는 minify·sourcemap 플래그에 따라 뒤집힐 수 있어, "항상 최속" 같은 단정은 성립하지 않는다.
- Vite 6 전환 사례에서 콜드 빌드 42초→14초, 증분 8초→3초, 메모리 40% 감소가 보고됐고 번들 크기는 거의 그대로였다.
- 벤치마크를 인용할 때는 모듈 수, minify/sourcemap 여부, 서버사이드 여부를 항상 같이 적어야 재현 가능한 비교가 된다.
더 알아보기
빌드·번들 툴체인 완전 가이드: 증분성·캐시·병렬성의 측정 — 이 주제의 종합 가이드