LabHub

블로그

프론트엔드 번들러 전쟁 완전 정복 — Webpack, Vite, esbuild, Turbopack, Rspack, Rolldown, Bun (2025)

"We spent 10 years teaching browsers to understand JavaScript. Then we spent another 10 years teaching build tools to undo what browsers learned." — Sebastian McKenzie (Babel, Yarn, Rome 창시자)

2015년 웹팩 설정 파일을 처음 열어본 개발자들의 공통된 반응은 "이게 뭐야?"였다. 500줄짜리 webpack.config.js, loader와 plugin의 무한 조합, 빌드 한 번에 10분. 10년이 지난 2025년, 우리는 Vite로 프로젝트를 시작하고 3초 만에 개발 서버를 띄운다. 이 극적인 변화 뒤에는 무엇이 있었나?

이 글은 번들러의 탄생부터 Rust 혁명, 그리고 2025년 RolldownBun 시대까지의 프론트엔드 빌드 진화사를 파헤친다.


1. 태초에 script 태그가 있었다

2009년 이전 — 파일 나열의 시대

<script src="jquery.js"></script>
<script src="jquery-ui.js"></script>
<script src="myapp-utils.js"></script>
<script src="myapp.js"></script>

CommonJS — Node.js의 선물 (2009)

const _ = require('lodash');
module.exports = { ... };

Node가 서버에서 잘 작동했지만, 브라우저는 require를 모른다. 누군가 이를 브라우저에서 돌리는 법을 만들어야 했다.

Browserify — 번들링의 탄생 (2011)

James Halliday(substack)가 만든 도구. require가 있는 Node 코드를 하나의 큰 JS 파일로 묶어 브라우저에 넘긴다.

browserify main.js -o bundle.js

"모듈을 합친다"는 개념의 시작. 모든 이후 번들러의 조상.


2. Webpack의 왕좌 (2014-2020)

등장

Tobias Koppers가 2012년에 시작. 2014년 v1.0. 2016-2020년 사이 사실상 독점.

왜 승자가 되었나

  1. 모든 것이 모듈 — JS뿐 아니라 CSS, 이미지, 폰트까지 import 가능
  2. Code Splitting — 동적 import()로 청크 분할
  3. Loader 생태계 — babel-loader, css-loader, file-loader, 수천 개
  4. Plugin 시스템 — HtmlWebpackPlugin, MiniCssExtract 등
  5. Dev Server + HMR — 수정하면 페이지 새로고침 없이 반영

Webpack의 철학 — Dependency Graph

entry 지점에서 시작해 모든 import를 따라가며 의존성 그래프를 만든다. 그래프의 각 노드가 모듈이고, 각 모듈은 loader 체인을 거쳐 JS로 변환된다.

악명 — 복잡한 설정

module.exports = {
  entry: './src/index.js',
  output: { ... },
  module: {
    rules: [
      { test: /\.js$/, use: 'babel-loader' },
      { test: /\.css$/, use: ['style-loader', 'css-loader'] },
      { test: /\.(png|jpg)$/, type: 'asset/resource' },
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({ template: './src/index.html' }),
    new MiniCssExtractPlugin(),
  ],
  optimization: { splitChunks: { chunks: 'all' } },
  resolve: { alias: { ... }, extensions: [...] },
};

10줄이 50줄이 되고 500줄이 된다. "Webpack Config Engineer"라는 농담이 생긴 이유.

한계 — 왜 내려가는가


3. Rollup — 라이브러리의 표준

등장

Rich Harris(Svelte 창시자)가 2015년에 시작. 목표는 라이브러리 번들링.

왜 Rollup인가

한계

현실


4. esbuild — Go가 연 속도 혁명 (2020)

등장

Evan Wallace(Figma CTO)가 2020년 공개. Go로 작성.

속도 — 100배

Webpack: 20esbuild: 0.2

같은 프로젝트를 빌드했을 때 이런 차이가 실제로 난다.

어떻게 이렇게 빠른가

  1. Go — 네이티브 컴파일, JS보다 10-100배 빠름
  2. 병렬화 — Go의 goroutine으로 파일을 병렬 파싱/링크
  3. 최소한의 abstraction — 순수 파싱 → 번들링 직선형
  4. 단일 패스 알고리즘 — 스캔/변환/링크를 한 번에
  5. 메모리 공유 — 파싱 결과를 스레드 간 공유

esbuild의 함정

용도


5. Vite — Evan You의 "개발은 ESM, 빌드는 Rollup" (2020)

아이디어의 단순함

Evan You가 Vue 3 작업 중 Webpack 느림에 질려 만든 도구:

Native ESM Dev Server

<script type="module" src="/src/main.js"></script>

브라우저가 import 구문을 직접 처리. Vite는:

  1. .vue, .tsx 등을 on-demand로 ESM JS로 변환 (esbuild 사용)
  2. 브라우저가 필요한 파일만 요청
  3. 수정 시 해당 모듈만 다시 보냄 (HMR)

초기 로드 수 초, HMR 수십 ms

왜 혁명이었나

Vite의 구성

현재 — 기본 선택

문제점

→ 이 문제가 Rolldown 등장의 원인.


6. Turbopack — Vercel의 Rust 베팅 (2022)

등장

Next.js 팀이 Webpack 후속작으로 Rust로 작성. 2022년 알파, 2024년 안정, 2025년 Next.js 15에서 기본 기본값 목표.

핵심 아이디어 — Incremental Computation

Tobias Koppers(Webpack 창시자)가 Vercel 합류 후 이끄는 프로젝트. 아이디어의 핵심:

"변경된 것만 다시 계산한다."

함수 수준에서 캐싱. f(x, y)를 호출했는데 x만 바뀌면, y 관련 계산은 재사용.

Turbo Engine

성능 주장

한계


7. Rspack — 바이트댄스의 Rust Webpack (2023)

등장

TikTok 모회사 바이트댄스가 "Webpack 설정 그대로, 속도는 10배"를 목표로 만듦.

핵심 — Webpack API 호환

어떻게 10배 빠른가

빠른 채택

Rsbuild / Rslib


8. Rolldown — Vite의 다음 진화 (2024-2025)

왜 필요한가

Vite는 개발용 esbuild + 빌드용 Rollup 이중 체인:

해결 — 하나의 Rust 번들러

성능

현재 상태 (2025)

의의


9. Bun — 런타임 + 번들러 + 패키지 매니저

등장

Jarred Sumner가 2022년 공개. Zig로 작성.

통합 전략

한 도구로:

bun install — 30배 빠른 npm

npm install:  12bun install:  0.4

병렬 다운로드 + 네이티브 symlink 조작 + 캐시 최적화.

번들러

현실


10. Tree-shaking의 과학

아이디어

// utils.js
export function a() { return 1 }
export function b() { return 2 }

// main.js
import { a } from './utils'

b는 사용되지 않음 → 번들에서 제거.

ESM이 열쇠

Side Effects

// package.json
"sideEffects": false   // 또는 ["*.css"]

번들러에게 "import만 해도 부수효과 없다"고 알림. false가 기본이 아님 → 라이브러리가 명시해야 함.

Tree-shaking이 실패하는 이유

  1. CommonJS 의존성 — require는 정적 분석 불가
  2. 사이드 이펙트 못 증명new Class()의 효과?
  3. Polyfill을 전역에 주입
  4. Barrel file (index.ts에서 re-export) — 느린 해결, Turbopack/Rspack이 최적화

실전 검증

npx webpack-bundle-analyzer stats.json
# 또는 rollup-plugin-visualizer

시각화로 진짜 불필요한 게 들어갔는지 확인. 사용도 안 하는 moment.js가 전체 번들의 30%인 경우가 흔함.


11. Code Splitting — 분할의 예술

Dynamic Import

const Chart = React.lazy(() => import('./Chart'))

→ Chart는 별도 청크로 분리, 필요할 때 네트워크 로드.

라우트 기반 분할

Vendor Splitting

너무 많은 분할의 함정


12. HMR (Hot Module Replacement) — 새로고침 없는 개발

기본 원리

  1. Dev server가 파일 변경 감지
  2. WebSocket으로 브라우저에 알림
  3. 브라우저가 변경된 모듈만 다시 요청
  4. 상태 유지하면서 교체

React Fast Refresh

왜 어려운가

초기 vs 점진 업데이트


13. Module Federation — 마이크로 프론트엔드

개념

Webpack 5 (2020)이 최초 네이티브 지원

new ModuleFederationPlugin({
  name: 'shell',
  remotes: {
    checkout: 'checkout@https://cdn.example.com/checkout/remoteEntry.js'
  }
})

현재 — Rspack, Vite도 지원

트레이드오프


14. Import Maps & ESM in Production

Import Map

<script type="importmap">
{
  "imports": {
    "react": "https://esm.sh/react@18",
    "lodash-es/": "https://esm.sh/lodash-es/"
  }
}
</script>
<script type="module">
  import React from 'react'
</script>

Deno / Bun과 통합

여전히 번들러가 필요한 이유

→ 2030년쯤이면 상당수가 번들 없이 배포될 가능성도.


15. 2025년 선택 가이드

신규 프로젝트

용도추천
React/Vue/Svelte 앱Vite (5% 확률로 Turbopack)
Next.jsTurbopack (기본값 전환 중)
대형 Webpack 레거시 마이그레이션Rspack
라이브러리Rolldown (또는 Rollup)
최소 의존성 CLI 도구esbuild
백엔드/CLI 런타임까지Bun
파이썬/Ruby 급 간편함Bun 또는 Deno

기존 Webpack 탈출


16. 안티패턴 TOP 10

  1. Create React App 고수 — 2025년 완전 deprecated, Vite로
  2. Webpack config 500줄+ — 리팩토링 또는 Rspack으로
  3. 번들 크기 측정 안 함webpack-bundle-analyzer 상시
  4. CommonJS 라이브러리 범람 — tree-shaking 실패 → ESM 라이브러리 선호
  5. Barrel file 남용index.ts에서 전체 re-export → 트리셰이킹 방해
  6. 모든 이미지 번들링 — 큰 이미지는 CDN, import는 최소
  7. process.env 런타임 사용 — 빌드 시 substitute 되므로 import.meta.env
  8. source map을 프로덕션에 포함 — 유출 + 파일 크기
  9. lodash 전체 importimport _ from 'lodash' → 개별 함수만
  10. Hydration mismatch 방치 — SSR/CSR 결과가 달라서 flash

17. 번들러 현명하게 쓰기 체크리스트


마치며 — 번들러의 종말?

"Bundless future"는 2015년부터 회자되었다. HTTP/2와 ESM이 등장하면 번들러가 필요 없어질 거라고. 10년 지난 지금, 번들러는 더 복잡해졌고 더 빨라졌다. 이유는 간단하다: "최적화"의 끝이 없기 때문.

하지만 변화의 방향은 분명하다:

  1. Rust/Go로의 이주 — JavaScript 런타임의 한계 극복
  2. 통합 — 번들러+런타임+테스트의 경계 붕괴 (Bun, Deno)
  3. Incremental everywhere — Turbopack의 철학 확산
  4. ESM 표준화 — CommonJS의 느린 퇴장
  5. GitHub Copilot/Cursor 생성 코드 — 설정 자체가 덜 중요해짐

Webpack 설정으로 3시간씩 보내던 시절은 저물었다. 2030년의 개발자는 번들러를 당연한 것으로 여길 것이다. 마치 우리가 C 컴파일러를 그렇게 여기듯이.


다음 글 예고 — React 서버 컴포넌트(RSC)와 Next.js App Router의 내부

번들러가 빌드 시간을 해결했다면, 서버 컴포넌트는 "무엇을 서버에서 렌더링할 것인가"의 질문을 재정의했다. 다음 글에서는:

2026년 React 개발자의 필수 지식을 한 번에 정리하는 여정.


"The best bundler is the one you don't have to configure." — Evan You (Vite 창시자, VueConf 2024)

댓글

아직 댓글이 없습니다.

로그인하면 댓글을 쓸 수 있습니다