본문으로 건너뛰기

📦 번들러란?

📖 정의

**번들러(Bundler)**는 여러 개의 파일과 모듈을 하나 또는 몇 개의 파일로 합치는 도구입니다. JavaScript, CSS, 이미지 등 다양한 리소스를 최적화하고, 브라우저에서 효율적으로 로드할 수 있도록 변환합니다. 대표적으로 Webpack, Vite, Rollup, Parcel 등이 있습니다.

🎯 비유로 이해하기

이사짐 센터 비유

번들러를 이사짐 센터에 비유하면:

이사 전 (번들링 전)
집안 물건들:
├─ 거실: 소파, TV, 테이블 (JavaScript 파일들)
├─ 방: 침대, 책상, 옷장 (CSS 파일들)
├─ 주방: 냉장고, 식기 (이미지 파일들)
└─ 화장실: 세면대, 수건 (폰트 파일들)

문제:
- 물건 하나씩 옮기기 = 비효율
- 정리 안 됨 = 찾기 어려움
- 공간 낭비 = 느림

이사짐 센터 (번들러):
1. 물건 분류
2. 박스에 담기
3. 라벨링
4. 효율적 배치
5. 트럭에 실기

이사 후 (번들링 후):
📦 박스1: 가구 (main.js)
📦 박스2: 주방용품 (styles.css)
📦 박스3: 작은 물건들 (assets)

장점:
- 한 번에 운반 = 빠름
- 정리됨 = 찾기 쉬움
- 공간 효율 = 최적화

도서관 비유

산만한 책들 (번들링 전)
- 책 1000권이 바닥에 흩어져 있음
- 찾기 어려움
- 공간 낭비
- 먼지 쌓임

도서관 시스템 (번들러)
1. 주제별 분류
2. 책장에 정리
3. 색인 만들기
4. 라벨 붙이기

정리된 도서관 (번들링 후)
- 분야별 서가
- 빠른 검색
- 공간 효율
- 깔끔한 관리

요리 준비 비유

재료 준비 전 (번들링 전)
냉장고 전체:
- 야채 20종
- 고기 5종
- 양념 30종
- 모두 제각각 위치

요리사 (번들러):
1. 오늘 메뉴 확인
2. 필요한 재료만 선택
3. 손질하기
4. 조리 순서대로 배치

미장플라스 (번들링 후)
- 필요한 재료만 정리
- 바로 요리 가능
- 효율적
- 시간 절약

⚙️ 작동 원리

1. 모듈 시스템의 필요성

// 문제: 전역 오염
// HTML에서 여러 스크립트 로드
<!DOCTYPE html>
<html>
<head>
<script src="utils.js"></script>
<script src="math.js"></script>
<script src="app.js"></script>
</head>
</html>

// utils.js
var name = 'Utils';
function log(msg) {
console.log(msg);
}

// math.js
var name = 'Math'; // ❌ 충돌! utils.js의 name 덮어씀
function add(a, b) {
return a + b;
}

// app.js
console.log(name); // 'Math'가 출력됨 (예상: 'Utils')
log('Hello'); // 작동은 하지만 어디서 온 함수인지 불명확

// 문제점:
// 1. 변수명 충돌
// 2. 의존성 순서 중요
// 3. 전역 네임스페이스 오염
// 4. 파일 많으면 HTTP 요청 많음
// 5. 사용하지 않는 코드도 로드

2. 모듈 시스템

// ✅ ES6 Modules (ESM)
// utils.js
export const name = 'Utils';
export function log(msg) {
console.log(msg);
}

// math.js
export const name = 'Math';
export function add(a, b) {
return a + b;
}
export function subtract(a, b) {
return a - b;
}

// app.js
import { log } from './utils.js';
import { add, subtract } from './math.js';

log('Hello'); // 명확!
console.log(add(1, 2)); // 3

// 장점:
// - 네임스페이스 격리
// - 명시적 의존성
// - Tree Shaking 가능
// - 재사용 쉬움

// 하지만 문제:
// 1. 구형 브라우저 지원 안 됨
// 2. HTTP 요청 여전히 많음
// 3. 모듈 해석 느림

// → 번들러 필요!

3. 번들러의 동작 과정

// 1단계: Entry Point (진입점)
// webpack.config.js
module.exports = {
entry: './src/index.js' // 시작점
};

// 2단계: Dependency Graph (의존성 그래프)
// index.js
import { render } from './render.js';
import './styles.css';
import logo from './logo.png';

// render.js
import { createElement } from './dom.js';
import { formatDate } from './utils.js';

// dom.js
import { debounce } from './helpers.js';

// 의존성 트리:
index.js
├─ render.js
│ ├─ dom.js
│ │ └─ helpers.js
│ └─ utils.js
├─ styles.css
└─ logo.png

// 3단계: Loaders (변환)
// - CSS → JavaScript
// - SCSS → CSS → JavaScript
// - 이미지 → Base64 or 파일
// - TypeScript → JavaScript
// - JSX → JavaScript

// 4단계: Plugins (추가 작업)
// - HTML 생성
// - 코드 압축
// - 환경 변수 주입
// - 소스맵 생성

// 5단계: Output (출력)
// dist/
// ├─ bundle.js (모든 JS 합쳐짐)
// ├─ styles.css (추출된 CSS)
// └─ logo.abc123.png (해시 추가)

// 결과:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="dist/styles.css">
</head>
<body>
<script src="dist/bundle.js"></script>
<!-- 하나의 파일! -->
</body>
</html>

4. Tree Shaking

// math.js
export function add(a, b) {
return a + b;
}

export function subtract(a, b) {
return a - b;
}

export function multiply(a, b) {
return a * b;
}

export function divide(a, b) {
return a / b;
}

// app.js
import { add, subtract } from './math.js';

console.log(add(1, 2)); // 사용함
console.log(subtract(5, 3)); // 사용함

// multiply와 divide는 사용 안 함!

// 번들러 (Tree Shaking 후)
// bundle.js
function add(a, b) {
return a + b;
}
function subtract(a, b) {
return a - b;
}
console.log(add(1, 2));
console.log(subtract(5, 3));

// multiply와 divide는 제거됨!
// 번들 크기 감소!

// Tree Shaking 조건:
// 1. ES6 Modules 사용
// 2. 사이드 이펙트 없는 코드
// 3. Production 모드

5. Code Splitting

// 코드 분할 (Code Splitting)

// ❌ 모든 코드를 하나의 번들에
import Home from './pages/Home';
import About from './pages/About';
import Dashboard from './pages/Dashboard';
import Admin from './pages/Admin';

// bundle.js (2MB) - 너무 큼!

// ✅ 동적 import로 분할
// app.js
const routes = {
'/': () => import('./pages/Home'),
'/about': () => import('./pages/About'),
'/dashboard': () => import('./pages/Dashboard'),
'/admin': () => import('./pages/Admin')
};

// 라우터
async function navigate(path) {
const loadPage = routes[path];
if (loadPage) {
const module = await loadPage();
module.default.render();
}
}

// 결과:
// bundle.js (100KB) - 기본 코드
// home.chunk.js (50KB) - 홈 페이지만
// about.chunk.js (30KB) - 어바웃 페이지만
// dashboard.chunk.js (200KB) - 대시보드만
// admin.chunk.js (500KB) - 관리자만

// 장점:
// - 초기 로딩 빠름
// - 필요할 때만 로드
// - 사용자는 빠르게 느낌

// React에서 Code Splitting
import { lazy, Suspense } from 'react';

const Dashboard = lazy(() => import('./pages/Dashboard'));

function App() {
return (
<Suspense fallback={<div>로딩 중...</div>}>
<Dashboard />
</Suspense>
);
}

💡 실제 예시

Webpack 설정

// webpack.config.js (기본)
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {
// 1. 진입점
entry: './src/index.js',

// 2. 출력
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.[contenthash].js', // 캐시 무효화를 위한 해시
clean: true // 빌드 전 dist 폴더 정리
},

// 3. 모드
mode: 'production', // or 'development'

// 4. Loaders (파일 변환)
module: {
rules: [
// JavaScript (Babel)
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env', '@babel/preset-react']
}
}
},

// CSS
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader, // CSS 파일 추출
'css-loader' // CSS → JS
]
},

// SCSS
{
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'sass-loader' // SCSS → CSS
]
},

// 이미지
{
test: /\.(png|jpg|gif|svg)$/,
type: 'asset',
parser: {
dataUrlCondition: {
maxSize: 8 * 1024 // 8KB 이하면 Base64 인라인
}
}
},

// 폰트
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
type: 'asset/resource'
}
]
},

// 5. Plugins (추가 기능)
plugins: [
// HTML 생성
new HtmlWebpackPlugin({
template: './src/index.html',
minify: true
}),

// CSS 파일 추출
new MiniCssExtractPlugin({
filename: 'styles.[contenthash].css'
})
],

// 6. 개발 서버
devServer: {
static: './dist',
port: 3000,
hot: true, // Hot Module Replacement
open: true
},

// 7. 소스맵 (디버깅)
devtool: 'source-map',

// 8. 최적화
optimization: {
splitChunks: {
chunks: 'all', // 코드 분할
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
priority: 10
}
}
}
}
};

// package.json
{
"scripts": {
"dev": "webpack serve --mode development",
"build": "webpack --mode production"
}
}

// 사용:
// npm run dev - 개발 서버 실행
// npm run build - 프로덕션 빌드

Vite 설정

// vite.config.js
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';

export default defineConfig({
// 1. 플러그인
plugins: [react()],

// 2. 별칭
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
'@components': path.resolve(__dirname, './src/components'),
'@utils': path.resolve(__dirname, './src/utils')
}
},

// 3. 개발 서버
server: {
port: 3000,
open: true,
cors: true,
proxy: {
// API 프록시
'/api': {
target: 'http://localhost:5000',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
}
}
},

// 4. 빌드
build: {
outDir: 'dist',
assetsDir: 'assets',
sourcemap: true,

// 청크 크기 제한
chunkSizeWarningLimit: 1000,

// Rollup 옵션
rollupOptions: {
output: {
// 수동 청크 분할
manualChunks: {
'react-vendor': ['react', 'react-dom'],
'router': ['react-router-dom'],
'ui': ['@mui/material']
}
}
},

// 압축
minify: 'terser',
terserOptions: {
compress: {
drop_console: true // console.log 제거
}
}
},

// 5. CSS
css: {
preprocessorOptions: {
scss: {
additionalData: `@import "@/styles/variables.scss";`
}
},
modules: {
localsConvention: 'camelCase'
}
},

// 6. 환경 변수
define: {
__APP_VERSION__: JSON.stringify('1.0.0')
}
});

// package.json
{
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
}
}

// Vite의 장점:
// 1. 매우 빠른 개발 서버 (ESM 사용)
// 2. 즉시 시작 (번들링 안 함)
// 3. HMR 초고속
// 4. 간단한 설정
// 5. 현대적 기본값

Rollup 설정 (라이브러리용)

// rollup.config.js
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import babel from '@rollup/plugin-babel';
import { terser } from 'rollup-plugin-terser';
import pkg from './package.json';

export default {
// 진입점
input: 'src/index.js',

// 다중 출력 (여러 포맷)
output: [
// CommonJS (Node.js)
{
file: pkg.main,
format: 'cjs',
sourcemap: true
},

// ES Module (번들러용)
{
file: pkg.module,
format: 'esm',
sourcemap: true
},

// UMD (브라우저용)
{
file: pkg.browser,
format: 'umd',
name: 'MyLibrary',
sourcemap: true,
globals: {
react: 'React'
}
}
],

// 외부 의존성 (번들에 포함 안 함)
external: ['react', 'react-dom'],

// 플러그인
plugins: [
// Node 모듈 해석
resolve({
extensions: ['.js', '.jsx']
}),

// CommonJS → ESM
commonjs(),

// Babel 변환
babel({
exclude: 'node_modules/**',
babelHelpers: 'bundled',
presets: ['@babel/preset-env', '@babel/preset-react']
}),

// 압축
terser()
]
};

// package.json
{
"name": "my-library",
"version": "1.0.0",
"main": "dist/index.cjs.js", // CommonJS
"module": "dist/index.esm.js", // ES Module
"browser": "dist/index.umd.js", // UMD
"scripts": {
"build": "rollup -c"
}
}

// Rollup의 장점:
// 1. Tree Shaking 우수
// 2. 작은 번들 크기
// 3. 라이브러리 개발에 최적
// 4. 여러 포맷 출력

Parcel (설정 없음)

// package.json (설정 파일 불필요!)
{
"name": "my-app",
"scripts": {
"dev": "parcel src/index.html",
"build": "parcel build src/index.html"
}
}

// src/index.html
<!DOCTYPE html>
<html>
<head>
<title>Parcel App</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="./index.js"></script>
</body>
</html>

// src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './styles.css';
import App from './App';

ReactDOM.render(<App />, document.getElementById('root'));

// src/styles.css
body {
margin: 0;
font-family: sans-serif;
}

// src/App.jsx
import React from 'react';

function App() {
return <h1>Hello Parcel!</h1>;
}

export default App;

// 그냥 실행!
// npm run dev

// Parcel이 자동으로:
// - Babel 변환
// - CSS 처리
// - 이미지 최적화
// - HMR 설정
// - 코드 분할

// Parcel의 장점:
// 1. 제로 설정
// 2. 빠른 시작
// 3. 자동 최적화
// 4. 초보자 친화적

// 단점:
// 1. 세밀한 제어 어려움
// 2. 플러그인 생태계 작음

실제 프로젝트 구조

# Create React App (Webpack)
my-app/
├── node_modules/
├── public/
│ ├── index.html
│ └── favicon.ico
├── src/
│ ├── components/
│ │ ├── Header.js
│ │ └── Footer.js
│ ├── App.js
│ ├── App.css
│ └── index.js
├── package.json
└── README.md

# Vite
my-vite-app/
├── node_modules/
├── public/
│ └── vite.svg
├── src/
│ ├── assets/
│ ├── components/
│ ├── App.jsx
│ ├── App.css
│ └── main.jsx
├── index.html # 루트에 있음!
├── vite.config.js
└── package.json

# Next.js (Webpack + SWC)
my-next-app/
├── node_modules/
├── pages/
│ ├── api/
│ ├── _app.js
│ ├── _document.js
│ └── index.js
├── public/
├── styles/
├── next.config.js
└── package.json

성능 최적화 예시

// webpack.config.js (프로덕션 최적화)

const webpack = require('webpack');
const TerserPlugin = require('terser-webpack-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const CompressionPlugin = require('compression-webpack-plugin');
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');

module.exports = {
mode: 'production',

optimization: {
// 1. 코드 압축
minimize: true,
minimizer: [
new TerserPlugin({
terserOptions: {
compress: {
drop_console: true, // console.log 제거
drop_debugger: true, // debugger 제거
pure_funcs: ['console.log']
}
}
}),
new CssMinimizerPlugin()
],

// 2. 코드 분할
splitChunks: {
chunks: 'all',
minSize: 20000,
maxSize: 244000,
cacheGroups: {
// React 라이브러리
react: {
test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/,
name: 'react',
priority: 20
},
// 기타 라이브러리
vendors: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
priority: 10
},
// 공통 코드
common: {
minChunks: 2,
name: 'common',
priority: 5,
reuseExistingChunk: true
}
}
},

// 3. 런타임 청크 분리
runtimeChunk: 'single',

// 4. Module ID 안정화
moduleIds: 'deterministic'
},

plugins: [
// 5. Gzip 압축
new CompressionPlugin({
filename: '[path][base].gz',
algorithm: 'gzip',
test: /\.(js|css|html|svg)$/,
threshold: 10240,
minRatio: 0.8
}),

// 6. 번들 분석
new BundleAnalyzerPlugin({
analyzerMode: 'static',
openAnalyzer: false,
reportFilename: 'bundle-report.html'
}),

// 7. 환경 변수
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production')
})
],

// 8. 캐싱
cache: {
type: 'filesystem',
buildDependencies: {
config: [__filename]
}
},

// 9. 성능 힌트
performance: {
hints: 'warning',
maxEntrypointSize: 512000,
maxAssetSize: 512000
}
};

// 결과:
// Before: bundle.js (2.5MB)
// After:
// react.js (140KB)
// vendors.js (300KB)
// common.js (50KB)
// main.js (100KB)
// Total: 590KB (76% 감소!)

환경별 설정

// webpack.common.js (공통)
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].[contenthash].js'
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: 'babel-loader'
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
})
]
};

// webpack.dev.js (개발)
const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');

module.exports = merge(common, {
mode: 'development',
devtool: 'eval-source-map', // 빠른 소스맵
devServer: {
hot: true,
port: 3000
},
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader'] // CSS를 JS에 인라인
}
]
}
});

// webpack.prod.js (프로덕션)
const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = merge(common, {
mode: 'production',
devtool: 'source-map', // 정확한 소스맵
module: {
rules: [
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader, // CSS 파일 추출
'css-loader'
]
}
]
},
plugins: [
new MiniCssExtractPlugin({
filename: '[name].[contenthash].css'
})
]
});

// package.json
{
"scripts": {
"dev": "webpack serve --config webpack.dev.js",
"build": "webpack --config webpack.prod.js"
}
}

🤔 자주 묻는 질문

Q1. Webpack vs Vite, 어떤 걸 선택해야 하나요?

A: 프로젝트 특성에 따라 선택하세요:

// Webpack 선택 시
const webpackUseCases = {
적합한 경우: [
'복잡한 빌드 요구사항',
'세밀한 제어 필요',
'많은 플러그인 필요',
'레거시 프로젝트',
'구형 브라우저 지원'
],
장점: [
'성숙한 생태계',
'풍부한 플러그인',
'완벽한 제어',
'대규모 프로젝트 검증됨'
],
단점: [
'느린 개발 서버',
'복잡한 설정',
'학습 곡선 높음'
]
};

// Vite 선택 시
const viteUseCases = {
적합한 경우: [
'새 프로젝트',
'빠른 개발 경험 원함',
'현대 브라우저만 지원',
'간단한 설정 선호'
],
장점: [
'초고속 개발 서버',
'즉시 시작',
'간단한 설정',
'현대적 기본값',
'HMR 매우 빠름'
],
단점: [
'생태계 상대적으로 작음',
'일부 레거시 라이브러리 문제',
'프로덕션 빌드는 Rollup 사용'
]
};

// 속도 비교:
// 개발 서버 시작:
// Webpack: 10-60초
// Vite: 0.5-2초 (20-100배 빠름!)

// HMR (Hot Module Replacement):
// Webpack: 0.5-2초
// Vite: 50-200ms (10배 빠름!)

// 프로덕션 빌드:
// Webpack: 비슷
// Vite: 비슷 (둘 다 충분히 빠름)

// 추천:
// - 새 프로젝트: Vite
// - 기존 프로젝트: Webpack 유지
// - 마이그레이션: 신중히 고려

Q2. 번들 크기를 줄이는 방법은?

A: 여러 최적화 기법을 사용하세요:

// 1. Tree Shaking (사용 안 하는 코드 제거)

// ❌ 나쁜 예 (전체 import)
import _ from 'lodash'; // 전체 라이브러리 (70KB)
_.debounce(fn, 100);

// ✅ 좋은 예 (필요한 것만)
import debounce from 'lodash/debounce'; // 2KB만
debounce(fn, 100);

// 더 좋은 예 (lodash-es)
import { debounce } from 'lodash-es'; // Tree Shaking 가능

// 2. Code Splitting (코드 분할)
// 라우트별 분할
const Home = lazy(() => import('./pages/Home'));
const About = lazy(() => import('./pages/About'));

// 조건부 로드
if (user.isAdmin) {
const AdminPanel = await import('./AdminPanel');
AdminPanel.render();
}

// 3. Dynamic Import (동적 import)
button.addEventListener('click', async () => {
const module = await import('./heavyFeature.js');
module.doSomething();
});

// 4. 외부 CDN 사용
// webpack.config.js
module.exports = {
externals: {
react: 'React',
'react-dom': 'ReactDOM'
}
};

// index.html
<script src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>

// 5. 이미지 최적화
// - WebP 포맷 사용
// - 적절한 크기로 리사이즈
// - Lazy Loading

// webpack.config.js
{
test: /\.(png|jpg|jpeg)$/,
use: [
{
loader: 'image-webpack-loader',
options: {
mozjpeg: { quality: 75 },
pngquant: { quality: [0.65, 0.9] }
}
}
]
}

// 6. Compression (압축)
// Gzip, Brotli
new CompressionPlugin({
algorithm: 'brotliCompress',
test: /\.(js|css|html|svg)$/
});

// 7. Bundle Analysis (분석)
npm install --save-dev webpack-bundle-analyzer

// webpack.config.js
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

plugins: [
new BundleAnalyzerPlugin()
]

// 8. Moment.js 대체
// ❌ Moment.js (70KB)
import moment from 'moment';

// ✅ date-fns (2-5KB)
import { format } from 'date-fns';

// ✅ Day.js (2KB)
import dayjs from 'dayjs';

// 결과:
// Before: 2.5MB
// After: 500KB (80% 감소!)

Q3. 개발 서버가 느린데 어떻게 해야 하나요?

A: 여러 최적화 방법이 있습니다:

// webpack.config.js (개발 서버 최적화)

module.exports = {
// 1. 캐싱 활성화
cache: {
type: 'filesystem',
buildDependencies: {
config: [__filename]
}
},

// 2. 소스맵 최적화
devtool: 'eval-cheap-module-source-map', // 빠름!
// 'eval-source-map' - 중간
// 'source-map' - 느림 (프로덕션용)

// 3. Babel 캐싱
module: {
rules: [
{
test: /\.js$/,
use: {
loader: 'babel-loader',
options: {
cacheDirectory: true // 캐시 활성화
}
}
}
]
},

// 4. 해석 최적화
resolve: {
// 확장자 순서
extensions: ['.js', '.jsx'], // 필요한 것만

// 별칭
alias: {
'@': path.resolve(__dirname, 'src')
},

// 모듈 경로
modules: ['node_modules'] // 명시적으로
},

// 5. 개발 서버 설정
devServer: {
hot: true, // HMR
liveReload: false, // HMR 사용하므로 불필요
client: {
overlay: {
errors: true,
warnings: false // 경고는 콘솔에만
}
}
},

// 6. 성능 힌트 끄기 (개발 중)
performance: {
hints: false
},

// 7. 통계 최소화
stats: 'errors-warnings'
};

// 또는 Vite로 마이그레이션!
// Webpack: 30초 시작
// Vite: 1초 시작 (30배 빠름!)

Q4. 번들러 없이 개발할 수 있나요?

A: 현대 브라우저는 ES Modules를 지원하지만, 한계가 있습니다:

<!-- 번들러 없이 (ESM 사용) -->
<!DOCTYPE html>
<html>
<head>
<title>No Bundler</title>
</head>
<body>
<div id="app"></div>

<!-- type="module" 필수 -->
<script type="module">
import { render } from './render.js';
import { utils } from './utils.js';

render();
</script>
</body>
</html>

<!-- render.js -->
<script type="module">
export function render() {
document.getElementById('app').innerHTML = '<h1>Hello!</h1>';
}
</script>

<!-- 장점: -->
- 설정 불필요
- 즉시 시작
- 간단함

<!-- 단점: -->
- HTTP/2 필요 (많은 요청)
- node_modules 지원 안 됨
- TypeScript 변환 안 됨
- JSX 사용 불가
- 이미지/CSS 처리 어려움
- 프로덕션 최적화 어려움
- 구형 브라우저 지원 안 됨

<!-- Import Maps (실험적) -->
<script type="importmap">
{
"imports": {
"react": "https://esm.sh/react@18",
"react-dom": "https://esm.sh/react-dom@18"
}
}
</script>

<script type="module">
import React from 'react';
import ReactDOM from 'react-dom';

// CDN에서 직접 가져옴!
</script>

<!-- 결론: -->
<!-- 작은 프로토타입: 번들러 없이 가능 -->
<!-- 실제 프로젝트: 번들러 필수! -->

Q5. TypeScript와 번들러를 함께 사용하는 방법은?

A: 대부분의 번들러가 TypeScript를 지원합니다:

// Webpack + TypeScript
// webpack.config.js
module.exports = {
entry: './src/index.tsx',
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/
}
]
},
resolve: {
extensions: ['.tsx', '.ts', '.js']
}
};

// tsconfig.json
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"jsx": "react",
"moduleResolution": "node",
"esModuleInterop": true,
"strict": true,
"skipLibCheck": true
},
"include": ["src"]
}

// Vite + TypeScript (자동 지원!)
// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

export default defineConfig({
plugins: [react()]
});

// 그냥 .tsx 파일 작성하면 됨!
// src/App.tsx
import React from 'react';

interface Props {
name: string;
age: number;
}

const App: React.FC<Props> = ({ name, age }) => {
return (
<div>
<h1>Hello, {name}!</h1>
<p>Age: {age}</p>
</div>
);
};

export default App;

// esbuild (초고속)
// build.js
const esbuild = require('esbuild');

esbuild.build({
entryPoints: ['src/index.tsx'],
bundle: true,
outfile: 'dist/bundle.js',
loader: { '.tsx': 'tsx' },
minify: true
});

// SWC (Next.js 기본)
// .swcrc
{
"jsc": {
"parser": {
"syntax": "typescript",
"tsx": true
},
"target": "es2020"
}
}

// 속도 비교:
// tsc (TypeScript): 느림
// Babel: 보통
// esbuild: 10-100배 빠름
// SWC: 20-70배 빠름

// 추천:
// - Vite: esbuild 내장
// - Next.js: SWC 기본
// - Webpack: ts-loader 또는 babel-loader

🎓 다음 단계

번들러를 이해했다면, 다음을 학습해보세요:

  1. Node.js란? (문서 작성 예정) - 번들러 실행 환경
  2. Git이란? (문서 작성 예정) - 버전 관리로 안전한 개발
  3. CI/CD란? - 자동 빌드와 배포

🎬 마무리

번들러는 현대 웹 개발의 필수 도구입니다:

  • Webpack: 강력하고 성숙, 복잡한 설정
  • Vite: 초고속 개발 경험, 현대적
  • Rollup: 라이브러리 개발, Tree Shaking 우수
  • Parcel: 제로 설정, 초보자 친화적

프로젝트에 맞는 번들러를 선택하고, 최적화하여 빠른 웹 애플리케이션을 만들어보세요!