06. 빌드 도구: Vite vs esbuild
06. 빌드 도구: Vite vs esbuild
이 문서에서 배우는 것
- esbuild: Go로 작성된 초고속 번들러, 모든 것을 직접 설정
- Vite: esbuild + Rollup을 감싼 고수준 도구, 설정 최소화
- "esbuild = 엔진, Vite = 자동차" 비유
- 같은 작업 비교: esbuild는 68줄, Vite는 12줄
- Vite 내부 구조: 개발 모드(esbuild) vs 프로덕션(Rollup)
- vite.config.js 각 옵션의 역할
- 언제 뭘 써야 하는지
PM 요청 (김도연)
김도연 PM: 준혁님, A003 프로젝트에 build.js(esbuild)랑 vite.config.js(Vite) 두 개가 있잖아요. 둘 다 빌드 도구인 것 같은데, 뭐가 다른 거예요? 왜 두 개나 있는 건가요?
이준혁 시니어: 좋은 질문이야. A003 프로젝트는 실험 프로젝트라서 두 가지 방식을 모두 시도해봤거든. esbuild가 "엔진"이라면 Vite는 "자동차"야. 하나씩 비교해볼게.
시니어 멘토링 (이준혁)
esbuild: Go로 만든 초고속 엔진
이준혁: esbuild는 Go 언어로 작성된 JavaScript 번들러야. 핵심 특징은 속도.
esbuild의 특징:
언어: Go (컴파일 언어 → 네이티브 바이너리)
속도: 기존 번들러(Webpack) 대비 10~100배 빠름
레벨: 저수준 도구 (Low-level)
철학: "빠르게 번들링. 나머지는 니가 알아서"
설정: JavaScript API로 직접 호출김도연: "저수준"이라는 게 무슨 뜻이에요?
이준혁: 기본 기능만 제공하고, 나머지는 전부 직접 설정해야 한다는 뜻이야. 예를 들어:
esbuild가 해주는 것:
✅ JavaScript/TypeScript 번들링
✅ JSX 변환
✅ 코드 최소화 (minify)
✅ 트리 셰이킹
esbuild가 해주지 않는 것:
❌ HMR (Hot Module Replacement)
❌ 개발 서버
❌ manifest.json 자동 생성
❌ CSS 모듈
❌ 환경 변수 자동 주입
❌ HTML 생성Vite: esbuild + Rollup을 감싼 자동차
이준혁: Vite는 esbuild와 Rollup을 내부적으로 사용하는 고수준 빌드 도구야.
Vite의 특징:
내부 엔진: esbuild (개발) + Rollup (프로덕션)
레벨: 고수준 도구 (High-level)
철학: "개발 경험을 최적화. 설정은 최소화"
설정: vite.config.js 하나로 끝Vite가 해주는 것:
✅ JavaScript/TypeScript 번들링
✅ JSX 변환
✅ 코드 최소화 (minify)
✅ 트리 셰이킹
✅ HMR (Hot Module Replacement) ← esbuild에는 없음
✅ 개발 서버 ← esbuild에는 없음
✅ manifest.json 자동 생성 ← esbuild에는 없음
✅ CSS 모듈 / PostCSS ← esbuild에는 없음
✅ 환경 변수 (.env) ← esbuild에는 없음
✅ 코드 스플리팅 ← esbuild는 제한적비유: esbuild = 엔진, Vite = 자동차
이준혁: 이 비유가 가장 정확해.
esbuild = 자동차 엔진
┌────────────────────────────────┐
│ esbuild │
│ │
│ 초고속 번들링 엔진 │
│ Go로 만들어서 매우 빠름 │
│ │
│ 하지만 엔진만으로는 │
│ 자동차가 되지 않음 │
│ │
│ 핸들, 바퀴, 시트, 계기판... │
│ 전부 직접 조립해야 함 │
└────────────────────────────────┘
Vite = 완성된 자동차
┌────────────────────────────────┐
│ Vite │
│ ┌─────────────────────────┐ │
│ │ esbuild (개발 엔진) │ │
│ │ Rollup (프로덕션 엔진)│ │
│ └─────────────────────────┘ │
│ │
│ + HMR (자동 변속기) │
│ + 개발 서버 (에어컨) │
│ + manifest (계기판) │
│ + 플러그인 (옵션 장착) │
│ │
│ 시동만 걸면 바로 출발! │
└────────────────────────────────┘김도연: 아, 그러니까 Vite 안에 esbuild가 들어있는 거군요!
이준혁: 맞아. Vite는 esbuild를 개발 모드에서 번들링 엔진으로 사용하고 있어.
같은 작업 비교: 68줄 vs 12줄
이준혁: A003 프로젝트에서 같은 작업(SSR 빌드)을 하는 코드를 비교해보자.
esbuild 방식 — build.js (68줄):
// build.js — 68줄
import esbuild from 'esbuild';
import sveltePlugin from 'esbuild-svelte';
import { mkdirSync } from 'fs';
mkdirSync('./svelte-pages/build', { recursive: true });
mkdirSync('./public', { recursive: true });
mkdirSync('./dist', { recursive: true });
console.log('Building...\n');
// 1/4. Svelte SSR 컴포넌트 빌드
console.log('1/4 Svelte SSR component');
await esbuild.build({
entryPoints: ['./svelte-pages/About.svelte'],
outfile: './svelte-pages/build/About.js',
bundle: true,
format: 'esm',
platform: 'node',
plugins: [
sveltePlugin({
compilerOptions: { generate: 'ssr', hydratable: true },
}),
],
});
// 2/4. Express 서버 번들
console.log('2/4 Server bundle');
await esbuild.build({
entryPoints: ['./server.js'],
outfile: './dist/server.js',
bundle: true,
format: 'esm',
platform: 'node',
packages: 'external',
jsx: 'automatic',
jsxImportSource: 'react',
});
// 3/4. React 클라이언트 번들
console.log('3/4 React client bundle');
await esbuild.build({
entryPoints: ['./client/react-entry.jsx'],
outfile: './public/react-bundle.js',
bundle: true,
format: 'iife',
platform: 'browser',
jsx: 'automatic',
jsxImportSource: 'react',
minify: true,
});
// 4/4. Svelte 클라이언트 번들
console.log('4/4 Svelte client bundle');
await esbuild.build({
entryPoints: ['./client/svelte-entry.js'],
outfile: './public/svelte-bundle.js',
bundle: true,
format: 'iife',
platform: 'browser',
plugins: [
sveltePlugin({
compilerOptions: { generate: 'dom', hydratable: true },
}),
],
minify: true,
});
console.log('\nBuild complete! Run: npm start');Vite 방식 — vite.config.js (12줄):
// vite.config.js — 12줄
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
build: {
manifest: true,
rollupOptions: {
input: 'entry-client.jsx',
}
}
})코드 양 비교:
esbuild: build.js 68줄 + 디렉토리 생성 + 4단계 수동 빌드 + 로그 출력
Vite: vite.config.js 12줄 + package.json scripts 2줄
Vite가 자동으로 해주는 것:
✅ 디렉토리 생성 (자동)
✅ manifest.json 생성 (manifest: true 한 줄)
✅ 파일명 해시 (기본 동작)
✅ 코드 스플리팅 (자동)
✅ 서버/클라이언트 분리 빌드 (--ssr 플래그)
✅ 진행 상황 표시 (자동)김도연: 12줄로 68줄의 일을 다 하는 거예요?
이준혁: 그게 "엔진 vs 자동차"의 차이야. 엔진을 직접 조립하면 많은 걸 직접 해야 하지만, 자동차는 시동만 걸면 돼.
Vite 내부 구조: 개발 모드 vs 프로덕션 모드
이준혁: Vite가 영리한 점은 모드에 따라 다른 도구를 사용한다는 거야.
Vite의 이중 구조:
┌─── 개발 모드 (vite dev) ───────────────────┐
│ │
│ esbuild로 JSX/TS 변환 │
│ 네이티브 ESM (번들링 안 함!) │
│ HMR (수정 즉시 반영) │
│ 개발 서버 (localhost:5173) │
│ │
│ 핵심: 빠른 시작, 빠른 반영 │
└──────────────────────────────────────────────┘
┌─── 프로덕션 모드 (vite build) ─────────────┐
│ │
│ Rollup으로 번들링 │
│ 트리 셰이킹 + 코드 스플리팅 │
│ 해시 파일명 + manifest.json │
│ 코드 최소화 (minify) │
│ │
│ 핵심: 최적화된 프로덕션 번들 │
└──────────────────────────────────────────────┘김도연: 왜 개발이랑 프로덕션에서 다른 도구를 써요?
이준혁: 개발 때는 "속도"가 가장 중요하고, 프로덕션에서는 "최적화"가 가장 중요해.
개발 모드에서 esbuild를 쓰는 이유:
esbuild는 번들링 속도가 극단적으로 빠름
+ 개발 모드에서는 네이티브 ESM으로 번들링 자체를 건너뜀
→ 코드 수정 → 반영까지 밀리초 단위
프로덕션에서 Rollup을 쓰는 이유:
Rollup은 트리 셰이킹과 코드 스플리팅이 더 정교함
+ 다양한 출력 형식 지원 (ESM, CJS, IIFE)
+ 풍부한 플러그인 생태계
→ 최적화된 프로덕션 번들 생성vite.config.js 각 옵션 분석
이준혁: A003 프로젝트의 vite.config.js를 한 줄씩 뜯어볼게.
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
// ↑ React 지원 플러그인
// - JSX 변환 (esbuild 또는 Babel 사용)
// - React Fast Refresh (개발 모드 HMR)
// - react-dom/server 자동 처리
build: {
manifest: true,
// ↑ manifest.json 생성 활성화
// → dist/client/.vite/manifest.json
// → "entry-client.jsx" → "assets/entry-client-D6usOwnO.js" 매핑
rollupOptions: {
input: 'entry-client.jsx',
// ↑ 클라이언트 빌드의 진입점 지정
// 기본값은 index.html이지만
// SSR에서는 HTML 없이 JSX 파일을 직접 진입점으로 사용
}
}
})plugins: [react()]가 하는 일:
1. JSX 변환
<h1>안녕</h1> → React.createElement('h1', null, '안녕')
2. React Fast Refresh (개발 모드)
컴포넌트 수정 시 상태를 유지하면서 화면만 업데이트
3. 서버/클라이언트 자동 처리
--ssr 플래그에 따라 적절한 React DOM 패키지 사용
build.manifest: true가 하는 일:
빌드 시 .vite/manifest.json 파일 생성
서버가 "빌드된 파일명을 알아내는 데" 사용 (03-manifest-and-hash.md 참조)
build.rollupOptions.input이 하는 일:
Rollup에게 "이 파일부터 의존성을 따라가면서 번들링해"라고 지시
일반 웹앱은 index.html이 진입점이지만
SSR에서는 entry-client.jsx가 진입점빌드 명령어 비교
이준혁: package.json의 scripts를 비교해보면 차이가 명확해.
{
"scripts": {
// esbuild 방식 — 직접 빌드 스크립트 실행
"dev": "node build.js && node dist/server.js",
// Vite 방식 — Vite CLI 사용
"build:client": "vite build --outDir dist/client",
"build:server": "vite build --outDir dist/server --ssr entry-server.jsx"
}
}esbuild 방식:
1. node build.js 실행
2. build.js 안의 4개 esbuild.build() 호출이 순서대로 실행
3. dist/server.js, public/react-bundle.js 등 생성
4. node dist/server.js로 서버 실행
Vite 방식:
1. vite build --outDir dist/client
→ entry-client.jsx 기반 클라이언트 번들 + manifest.json 생성
2. vite build --outDir dist/server --ssr entry-server.jsx
→ entry-server.jsx 기반 서버 번들 생성
3. node test.js로 서버 실행 (manifest.json 읽어서 사용)언제 뭘 쓰나
김도연: 결국 Vite가 더 좋은 거 아닌가요? esbuild를 직접 쓸 이유가 있나요?
이준혁: 상황에 따라 달라.
| 상황 | 추천 도구 | 이유 |
|---|---|---|
| 웹 앱 (SPA/SSR) | Vite | HMR, 코드 스플리팅, manifest 등 자동 |
| 라이브러리 빌드 | esbuild | 단순 번들링만 필요, 설정 최소 |
| CLI 도구 빌드 | esbuild | 브라우저 기능 불필요, 빠른 빌드 |
| 학습/실험 | esbuild | 빌드 과정의 각 단계를 이해하기 좋음 |
| 프로덕션 웹 서비스 | Vite | 최적화, 캐시 무효화 등 프로덕션 기능 |
| Svelte + React 혼합 | esbuild | 직접 플러그인 조합 가능 |
정리:
esbuild를 직접 쓰는 경우:
- 빌드 과정을 완전히 통제하고 싶을 때
- 브라우저 기능(HMR, 개발 서버)이 필요 없을 때
- 라이브러리나 CLI 도구를 빌드할 때
- 빌드 도구의 동작 원리를 학습할 때
Vite를 쓰는 경우:
- 웹 앱을 개발할 때 (대부분의 경우)
- 빠른 개발 경험(HMR)이 중요할 때
- manifest, 해시, 코드 스플리팅이 필요할 때
- "빌드 설정은 최소화하고 개발에 집중"하고 싶을 때이준혁: A003 프로젝트에서 esbuild(시스템 A)를 먼저 만든 이유가 있어. 빌드 과정의 각 단계를 직접 보면서 이해하기 위해서야. Vite는 너무 많은 걸 자동으로 해주니까, 처음부터 Vite를 쓰면 "뭘 하고 있는지" 모르게 되거든.
김도연: 아, 그래서 이 튜토리얼에서도 esbuild를 먼저 다룬 거군요!
이준혁: 맞아. 원리를 이해한 다음에 Vite를 쓰면, Vite가 뭘 해주는지 정확히 알 수 있어.
핵심 정리
-
esbuild는 Go로 작성된 초고속 번들러. 저수준 도구로, 빌드의 모든 것을 직접 설정해야 한다.
-
Vite는 esbuild(개발) + Rollup(프로덕션)을 내부에 가진 고수준 빌드 도구. 설정 최소화, 개발 경험 최적화가 목표다.
-
"esbuild = 엔진, Vite = 자동차". Vite 내부에 esbuild가 들어있고, HMR, manifest, 코드 스플리팅 등을 추가로 제공한다.
-
같은 작업: esbuild는 build.js 68줄, Vite는 vite.config.js 12줄. Vite가 대부분을 자동 처리한다.
-
Vite의 이중 구조: 개발 모드에서는 esbuild(속도), 프로덕션에서는 Rollup(최적화)을 사용한다.
-
사용 기준: 웹 앱이면 Vite, 라이브러리/CLI이면 esbuild, 학습 목적이면 esbuild부터 시작.