Webpack은 모듈 번들러(Module Bundler)
JS, CSS, 이미지 등 다양한 자원을 하나 또는 여러 개의 번들 파일로 묶어주는 도구. → 브라우저가 이해할 수 있는 단 하나의 JS 파일로 변환해주는 도구
번들링이란?
번들링(Bundle)이란, 여러 개의 파일(모듈)을 하나 또는 몇 개의 파일로 합치는 과정.
📌 이유 1. 브라우저는 수백 개의 JS 파일을 잘 못 처리함
React 프로젝트는 보통 수십~수백 개의 JS/TS, CSS, 이미지 파일로 구성
이걸 하나하나 다 로딩하면 HTTP 요청이 수백 개
그래서 “파일 수를 줄이기 위해” 번들링을 함
→ 왜냐하면, 요청이 많을수록 브라우저는 느려지고 복잡해짐
📌 이유 2. 의존성 관리 자동화
우리가 import / require를 사용해서 모듈을 나눠 쓰면
번들러는 이걸 자동으로 따라가서 하나로 묶어줌
🔷 의존성 분석이란?
파일 간에 import, require 같은 관계를 자동으로 찾아내는 작업
// Test.jsx
import a from './a';
import b from './b';
이걸 의존성 그래프(Dependency Graph)라고 함.
📌 이유 3. 최신 문법(ES6+, JSX, TS 등)은 브라우저가 이해 못 함
Babel + 번들링 도구가 이걸 변환 + 묶어줌
📌 이유 4. 성능 최적화
- Tree shaking으로 쓸모없는 코드는 제거
- Lazy loading, 코드 스플리팅으로 필요한 시점에만 로딩
- 번들 크기 줄이고, UX 개선
index.js)development / production 모드로 빌드 최적화 정도가 달라짐
- Entry 파일부터 시작해 모든 의존성을 분석
- 각 자원을 loader를 통해 변환
- plugins을 적용해 최종 output 생성
- 결과물을 하나 또는 여러 개의 JS 파일로 저장
index.js
├── A.js
│ ├── a.js
│ ├── b.js
│ └── c.js
└── B.js
├── a.js (공통)
├── d.
└── e.js
Webpack의 동작 순서
Entry Point 설정 (index.jsx)
//index.jsx
import A from './A';
import B from './B';
→ Webpack은 index.jsx를 entry로 시작함
의존성 그래프 생성
a.jsx는 A와 B 양쪽에서 쓰이니까 한 번만 포함됨 (중복 제거)최종 번들링 결과
a, b, c, d, e, A, B, index 등 필요한 코드를 하나의 파일(bundle.js)로 묶음a가 중복되지 않게 잘 정리되어 들어감✅ 중복 모듈은?
Webpack은 a가 두 군데서 import되더라도
→ 내부적으로는 딱 한 번만 포함시킴 (고유 ID 또는 경로 기반 키)
→ 필요한 곳에서 require("a") 형태로 공유하게 만들어.
필요한 모든 모듈을 모아서 하나의 번들 파일로 만든다.
(혹은 성능을 위해 여러 청크로 나눌 수도 있지만 기본은 하나)
✅ 그래프와 번들 결과물은 다르다
🔷 의존성 그래프 - 파일간 참조관계를 나타내는 구조 .
✅그래프에선 여러 번 등장해도, 번들 결과물엔 한 번만 포함
✅ 그래프 구조는 참조 관계를 나타내고,
✅ 실제 번들링 결과는 중복 없이 최적화된 코드로 구성됨.
✅ 같은 모듈은 한 번만 로딩되고, 어디서든 참조 가능.
index.js
/ \
A B
/ | \ / | \
a b c d e f
Webpack에 명시적으로 “이 부분은 따로 나눠줘” 라고 알려주면
→ A와 B를 각각 별도의 번들 파일로 나눌 수 있음.
[main.js] → index + 공통 모듈
[chunk-A.js] → A + a + b + c
[chunk-B.js] → B + d + e + f
// App.jsx
import React, { Suspense, lazy } from 'react';
const PageA = lazy(() => import('./PageA'));
const PageB = lazy(() => import('./PageB'));
function App() {
return (
<div>
<Suspense fallback={<div>Loading A...</div>}>
<PageA />
</Suspense>
</div>
);
}
이 경우:
즉, 번들 전체를 나무라고 보면:
AppPageA, PageB는 각각 비동기 청크| 이유 | 설명 |
|---|---|
| 🚀 초기 로딩 최적화 | 앱이 클수록 한 번에 다 불러오면 느려짐 |
| 🧠 사용자 흐름에 맞게 | 메인 화면만 빠르게, 하위 페이지는 나중에 |
| 💾 캐싱 및 청크 업데이트 용이 | A만 바뀌면 A만 새로 받으면 됨 |
⚒️ Entry-based splitting vs Dynamic import splitting
webpack.config.ts 파일에서 entry에 명시해서 나누는 것보다
Dynamic을 통해 분리하는게 일반적.
✔️ Webpack 파일 분리
코드 스플리팅을 해도 초기 빌드 or HMR이 느린 건 동일
왜냐하면 Webpack은 "빌드 중심" 구조라서 전체 트리를 항상 계산하고 캐싱함
React.lazy 등으로 import()한 청크가 미리 번들링되어 있음| 문제 | Webpack에서 |
|---|---|
| ❌ 개발 서버 시작 느림 | 모든 모듈을 미리 번들링 |
| ❌ HMR 느림 | 전체 의존성 그래프 재계산 |
| ❌ 설정 복잡 | 너무 많은 loader, plugin 의존 |
개발은 가볍고 빠르게,
배포는 완전히 최적화된 번들
src/
├── main.ts
├── App.tsx
├── components/
│ └── Header.tsx
main.ts 기준으로 전부 번들링 → main.bundle.jsindex.html을 entry로 삼고GET /src/main.ts
GET /src/App.tsx
GET /src/components/Header.tsx
✅ 즉, Vite는 브라우저가 import를 따라가는 구조
→ 이것이 가능한 이유? ESM (ES Modules) 기반이기 때문. - 사전 번들링
🔹사전 번들링(Pre-Bundling) 이란?
외부 라이브러리인 경우, esm을 지원안하거나 내부가 복잡할 수 있음
node_modules 내의 의존성을 .vite/deps/ 디렉토리에 따로 번들링을 함으로써, 브라우저 요청시 한번의 요청으로 불러올 수 있게 해줌
✅ Vite의 실행 흐름 (DEV 환경)
vite.config.ts 로드esbuild를 통해 빠르게 변환됨ws(웹소켓) 통해 반영Webpack:
Vite:
.ts, .vue, .css만 감지해서 해당 모듈만 브라우저에 푸시✅ ms 단위의 빠른 HMR
✅ 상태도 유지 가능 (React Fast Refresh)
⚠️ 이때는 “번들링 한다”
- 내부적으로 Rollup 사용
- 모든 모듈을 분석해서 Tree Shaking, Code Splitting, Lazy Loading, Minify 수행
- 파일 시스템 단위로 청크 나눔
⚙️ Rollup
import()는 실제로 브라우저가 네트워크를 통해 개별 JS 모듈 요청함Vite는 스플리팅 자체가 브라우저 주도 방식
→ 개발 중에도 네트워크로 개별 모듈을 요청하니
→ 번들링 자체가 필요 없음, 빠르고 효율적임
// App.jsx
import PageA from './PageA'
PageA를 import하면 → GET /src/PageA.jsx 요청을 브라우저가 보냄 → Vite dev server가 그 파일을 즉시 변환해서 전송| 항목 | 설명 |
|---|---|
| ⚡ 빠른 개발 서버 | esbuild 기반 실시간 변환 + ESM 요청 |
| 💥 빠른 HMR | 모듈 단위 감지 + 즉시 갱신 |
| 🎯 명확한 설정 | Rollup 기반 설정이라 구조가 단순 |
| 🌐 HTTP/2 최적화 | ESM + 파일 단위 요청에 최적화 |
| 🧩 강력한 플러그인 생태계 | Rollup/Vite 플러그인 모두 사용 가능 |
| 📦 완성도 높은 빌드 | Rollup으로 tree shaking + 청크 분리 자동 |
❗빠른 개발 서버란?
| 상황 | 설명 |
|---|---|
| 레거시 시스템 유지보수 | 이미 Webpack으로 복잡하게 구성된 시스템은 갈아엎기 어려움 |
| IE11 등 구형 브라우저 지원 필요 | Vite는 ESM 기반이라 IE 지원이 까다로움 (polyfill도 제한적) |
| 커스터마이징이 복잡함 | Microfrontend, Module Federation, Custom Plugin 등 |
| 코드 분할, 캐싱 최적화, 복잡한 의존성 그래프 | 고급 빌드 최적화 기능을 적극적으로 사용하는 경우 |
| 사내 개발 규칙이 Webpack 기반 | 전통적인 엔터프라이즈 환경에선 여전히 Webpack 많이 사용 |
✅ 대기업 프론트엔드 구조나 내부 플랫폼에서 Webpack 기반의 구조가 많음
| 상황 | 설명 |
|---|---|
| 새로운 프로젝트 시작 | 빠른 초기 개발과 설정 최소화가 가능 |
| SPA (Single Page Application) | 모듈 기반의 구조에 최적화 되어 있고, React/Vue/Svelte 다 잘 됨 |
| 빠른 개발 서버, HMR 필요 | 번들 없이 ESM 기반으로 dev server 속도는 진짜 압도적 |
| 최신 브라우저만 타겟 | 크롬, 사파리, 엣지 중심이면 ESM 기반으로 문제 없음 |
| TS + JSX + CSS 모듈 등 최신 기술 스택 사용 | 설정 거의 없이 잘 돌아감 |
| SSR을 포함한 모던 프레임워크 사용 시 | Nuxt, SvelteKit, Astro, SolidStart 등은 대부분 Vite 기반 |
⚠️ Vite는 모던 웹 생태계에서 기본값이 되어가는 중임 (React 공식 템플릿도 Vite 권장)
SPA는 다음 특성을 가짐:
Vite는 이런 SPA 특성과 아주 잘 맞음:
vite-plugin-react, vite-plugin-vue 등 생태계 풍부→ 그래서 요즘 신생 프로젝트는 SPA + Vite가 기본이 됨.
| 기준 | Webpack | Vite |
|---|---|---|
| 구형 브라우저 지원 | ✅ 좋음 (IE까지 가능) | ❌ ESM 중심 |
| 레거시 시스템 호환 | ✅ 적합 | ❌ 어려움 |
| 속도 (dev server, HMR) | ❌ 느림 | ✅ 빠름 |
| 설정 난이도 | ❌ 높음 | ✅ 낮음 |
| 신규 프로젝트 | 🔄 조건부 | ✅ 매우 추천 |
| 커스터마이징 (복잡한 plugin 등) | ✅ 강력함 | 🔄 일부 한계 |
| SSR + 최신 프레임워크 | 🔄 조건부 | ✅ 최적화됨 |
https://wonsss.github.io/webpack/webpack-all-in-one/
https://webpack.kr/guides/code-splitting/#dynamic-imports