Vite 8을 뜯어보니 빌드 번들러가 통째로 Rust로 바뀌어 있었다
Vite 8은 개발용 esbuild와 빌드용 Rollup을 Rust로 짠 Rolldown 하나로 합쳤다. 번들러가 둘이라 생기던 문제와 빌드가 몇 배씩 빨라지는 이유, 내 프로젝트들이 아직 올리지 못한 사정을 정리했다.
#Vite #Rolldown #빌드 #프론트엔드 #Rust
블로그 프론트가 한참 Vite 4에 멈춰 있었다. 잘 돌아가니까 굳이 안 건드렸는데, 어느새 Vite 8까지 나와 있었다. 메이저를 네 칸이나 건너뛰는 거라 올리기 전에 뭐가 바뀌었는지부터 읽어봤는데, 빌드 도구 속이 통째로 Rust 로 갈려 있었다. 이 글에서는 Vite 8이 뭘 바꿨는지, 그리고 빌드가 왜 갑자기 몇 배씩 빨라졌는지를 정리해본다. 마지막에는 그래서 내 프로젝트들은 지금 몇 버전인지도 솔직하게 적어둔다. 원래 Vite는 번들러를 두 개 썼다 이게 제일 중요한 배경이라 먼저 짚고 가야 한다. Vite는 처음부터 번들러를 두 개 썼다. 개발할 땐 esbuild , 프로덕션 빌드할 땐 Rollup 이다. 왜 굳이 둘로 나눴냐면, 둘이 잘하는 게 달랐기 때문이다. esbuild는 Go로 짜여서 아주 빠르다. 그래서 개발 서버를 띄울 때 의존성을 미리 변환하는 데 썼다. 근데 청크를 어떻게 쪼갤지, 출력물을 어떻게 다듬을지 같은 세밀한 제어가 약했다. 반대로 Rollup은 그 출력 제어가 훌륭한데 느렸다. 그래서 개발은 esbuild로 속도를 챙기고, 빌드는 Rollup으로 제어를 챙기는 식으로 나눠 쓴 거다. 지금도 Vite 4 프로젝트의 node_modules를 열어보면 esbuild와 rollup이 나란히 들어 있다. 이 둘이 Vite 안에서 각자 다른 시점에 일하고 있었던 셈이다. 근데 번들러가 둘이면 문제가 생긴다 번들러가 두 개라는 건 플러그인 시스템도 두 개, 동작 방식도 미묘하게 둘이라는 뜻이다. 개발 서버에선 멀쩡하던 게 막상 build 하면 깨지는 일이 여기서 나온다. 둘 사이의 미세한 차이를 메우려고 글루 코드가 계속 붙고, 엣지 케이스가 쌓인다. "dev에선 되는데 빌드하면 안 돼"가 왜 그렇게 잡기 힘들었냐면, 애초에 두 개의 다른 도구가 같은 코드를 다르게 처리하고 있었기 때문이다. 코드를 아무리 들여다봐도 코드에는 문제가 없고, 문제는 그 코드를 읽는 도구 쪽에 있었던 거다. Vite 8은 Rolldown 하나로 합쳤다 Vite 8은 이 둘을 Rolldown 하나로 통합했다. Rolldown은 Rust로 새로 짠 번들러인데, Rollup의 플러그인 API를 그대로 호환한다. 그래서 개발이든 빌드든 같은 번들러가 처리한다. dev와 prod가 다르게 동작할 여지 자체가 사라진 거다. Vite 2 이후 제일 큰 구조 변경이라고들 하는데, 마이그레이션 문서를 보면 과장이 아니다. 그런데 설정 파일 쪽은 의외로 조용하다. build.rollupOptions 키가 그대로 먹는다. 이름은 rollup인데 실제로 도는 건 Rolldown이라, 옮길 때 설정을 새로 배울 필요가 거의 없어 보인다. // vite.config.ts // Vite 8에서도 이 키 이름을 그대로 쓴다. 안에서 도는 건 Rollup이 아니라 Rolldown이다. export default defineConfig({ build: { rollupOptions: { output: { manualChunks(id) { if (id.includes('node_modules/react')) return 'react-core'; }, }, }, }, }); 그래서 왜 빨라졌나, 퍼센트가 아니라 배수다 제일 궁금했던 게 이거다. 빨라진 이유는 한두 개가 아니라 겹쳐 있다. 첫째, Rust 네이티브로 돈다 . 자바스크립트는 단일 스레드에 GC(가비지 컬렉션) 멈춤이 있다. Rust는 GC가 없고, 진짜 멀티 스레드 병렬이 된다. 번들링은 파일 수천 개를 동시에 주무르는 작업이라, 병렬이 되느냐 마느냐가 결정적이다. 둘째, 파이프라인 전체가 한 언어로 돈다 . 파싱, 의존성 그래프 만들기, 최적화, 코드 생성까지 전 단계가 Rust 안에서 끝난다. 중간에 자바스크립트랑 데이터를 주고받느라 멈추는 구간이 없다. 예전에는 esbuild가 변환한 결과를 자바스크립트로 받아서 다시 Rollup에 넘기는 식이라 그 경계마다 비용이 있었다. 셋째, 밑바닥에 Oxc가 깔린다 . Rolldown 아래엔 Oxc라는 Rust 파서가 있다. 코드 포매팅 기준으로 Prettier보다 수십 배 빠르다고 알려져 있는데, 파싱부터 속도가 다르니 그 위에 올라가는 번들링도 같이 빨라진다. 그래서 개선 폭이 10에서 30퍼센트가 아니라 10배에서 30배 수준으로 나온다. Linear 같은 회사는 빌드가 46초에서 6초로 줄었다고 발표했다는데, 내가 직접 확인한 수치는 아니라 그렇다고 한다 정도로만 적어둔다. 언어를 바꾸면 개선이 퍼센트가 아니라 배수로 찍힌다는 건 분명해 보인다. 올릴 때 걸리는 점 메이저 네 칸 점프라 공짜는 아니다. 마이그레이션 문서를 따라가 보면 바뀐 설정 옵션 몇 개를 손봐야 하고, Node 버전도 최신으로 올려야 한다. 이 서버는 아직 Node 18이라 이것부터가 걸린다. 다만 Rolldown이 Rollup 플러그인 API를 호환해줘서, 쓰던 플러그인 대부분은 그대로 붙는다고 한다. 내가 쓰는 건 react 플러그인 하나뿐이라 여기서 막힐 일은 없어 보인다. 한 가지 좋았던 건, Vite와 Rolldown, Oxc가 이제 한 팀처럼 같이 움직인다는 점이다. 파싱은 Oxc가, 번들은 Rolldown이, 빌드 전체는 Vite가 맡는데 셋이 같은 진영 도구라 단계 사이에서 동작이 어긋날 일이 줄었다. 그래놓고 안 올렸다 여기까지 읽고 나면 바로 올리고 싶어지는데, 실제로는 안 올렸다. 내 프로젝트들 버전을 확인해봤다. 아래는 이 글을 다시 손보는 2026년 9월 기준으로 각 프로젝트의 package.json에서 직접 읽은 값이다. coin-server/coinSite vite ^6.4.2 ieum-server/ieumSite vite ^5.4.11 travel-server/travel-frontend vite ^5.4.9 monitor-server/monitor-client vite ^5.0.0 blog-server/blog vite ^4.1.0 - 이 글이 올라가 있는 블로그 portfolio-server/final-portfolio vite ^4.1.0 couple-server/... vite ^4.1.0 game-server/... vite ^4.1.0 taro-server/... vite ^4.1.0 아홉 개 중에 8은 하나도 없다. 제일 높은 게 코인 사이트의 6이고, 정작 이 글이 올라가 있는 블로그는 4에 멈춰 있다. 8로 올리라고 써놓고 본인은 안 올린 거다. 블로그는 잘 돌고 있어서 우선순위에서 밀렸다. 빌드가 몇 초 느린 건 하루에 한두 번 겪는 일이고, 메이저를 네 단계 건너뛰면 뭐가 깨질지 모른다. 게다가 이 블로그는 manualChunks 규칙이나 CJS 헬퍼 청크 분리 같은 걸 손으로 맞춰둔 상태라, 번들러가 통째로 바뀌면 그걸 다시 재봐야 한다. 다만 기준은 하나 정해뒀다. 블로그를 다음에 크게 손볼 일이 생기면 그때 같이 올린다. 도구 버전만 따로 올리는 작업은 얻는 것에 비해 확인할 게 너무 많다. 새로 시작하는 프로젝트라면 고민 없이 8로 시작하는 게 맞고, 굴러가는 프로젝트라면 손볼 김에 같이 올리는 쪽을 추천한다.