1. Vite로 개발하는 이유
기존 번들러(Webpack 등)는 프로젝트 코드 전체를 미리 번들링하고(JS 하나로), 개발서버가 이 결과물을 서빙하기 때문에 서버 시작이 느렸음.
"webpack은 모던 JavaScript 애플리케이션을 위한 정적 모듈 번들러 입니다. webpack이 애플리케이션을 처리할 때, 내부적으로는 프로젝트에 필요한 모든 모듈을 매핑하고 하나 이상의 번들을 생성하는 디펜던시 그래프를 만듭니다."

- Vite를 사용해야 하는 이유
- Vite는 자체적으로 개발서버와 빌드시스템을 제공하는 빌드 도구
1.1. 개발 환경에서는 Native ESM과 esbuild를 사용함
vite는 애플리케이션의 모듈을 디펜던시와 소스 코드 두 가지 카테고리로 나누어 개발 서버의 시작 시간을 개선합니다.
(공식문서)
1.1.1 Vite는 개발 서버 시작 시 esbuild로 의존성을 “사전 번들링(pre-bundling)”함.
- esbuild는 Wepack, Parcel과 같은 기존 번들러 대비 10-100배 빠른 속도를 제공함
- esbuild는 CommonJS(CJS), UMD 등 브라우저가 이해 못 하는 형식을 ESM으로 변환할 수 있음
- 또한 수많은 작은 모듈을 소수의 큰 ESM 덩어리로 합쳐 둠.
- 따라서 개발 서버에서 esbuild를 사용하면 개발 중 발생하는 네트워크 요청 수를 줄이고, 해석/변환 비용을 크게 낮출 수 있음.
1.1.2 브라우저는 ESM을 네이티브로 지원하기 때문에, 번들없이도 모듈을 직접 해석하고 네트워크 요청 보낼 수 있음.
- (ESM 덕분에 브라우저가 "파일을 모듈 단위로 요청"할 수 있기 때문에 번들이 필요없는 것)
- 수정이 잦은 개발 환경에서 적절함. 번들링을 하지 않고도 빠르게 동작할 수 있기 때문.
- 모듈을 개별 요청으로 로드하여 개발 경험이 빠르고 유연해짐
- import A from "@/components/A" 같은 부분만 브라우저가 해석할 수 있는 JS로 변환해서 보내줌. 나머지는 번들링없이 바로 실행.
- 정리하면:
”개발 모드에서 Vite는 브라우저의 Native ESM을 사용해import된 모듈만 요청 시점에 변환하여 제공한다. 따라서 개발 단계에서는 별도의 번들링 과정이 필요 없다.
다만, npm dependencies는 구조가 복잡하거나 CommonJS 기반일 수 있어 브라우저가 직접 처리하기 어렵고, 또한 너무 많은 모듈 요청이 발생할 수 있다.
그래서 Vite는 esbuild를 이용해 dependencies를 ESM 형태로 빠르게 변환하고, 동시에 하나의 큰 모듈로 합쳐서 사전 번들링(pre-bundling) 한다.
즉, 프로젝트 소스는 ESM으로 개별 변환하여 즉시 제공하고, 외부 라이브러리는 esbuild로 미리 묶어서 요청 수를 최소화하여 빠르게 전달하는 구조다.”
1.2. 프로덕션 빌드단계에서는 내부적으로 Rollup을 사용하여 최종 번들링, 트리쉐이킹, 코드 스플리팅, 최적화를 수행함.
- esbuild는 아직 코드 스플리팅과 CSS 처리 등 프로덕션 최적화에 제약이 있어서, Vite는 안정적이고 성숙한 Rollup을 프로덕션 빌드에 사용한다고 함.
- Rollup은 ESM 기반이기 때문에 import/export를 정적으로(런타임 전 = 빌드시점에) 분석할 수 있어 트리셰이킹(불필요한 코드 제거) 정확도가 높고, 결과물도 더 작고, 코드 스플리팅도 더 똑똑하게 수행함
- ESM의 강력한 장점: 정적 분석이 가능하여 트리쉐이킹과 같은 최적화 작업에 유리하다.
정리:
- 개발 모드: ESM 기반 모듈 시스템 → 번들링 없음(단, 의존성만 esbuild로 사전 번들링)
- 운영 모드: Rollup 기반 번들링 → 요청 수 최소화 + 최적화된 배포 번들
2. Vite public 디렉토리
- 운영모드에서 Vite는 /public 경로를 제외한 모든 파일들을 Rollup으로 번들링하고, 번들링된 파일에 자동으로 해시를 붙인다.
- (단, 내가 참여했던 Nuxt.js 프로젝트에서 Vite를 사용했는데 그 상황에서는 빌드 시 해시 처리 옵션을 추가해야 했음. Nuxt는 Vite의 Rollup output을 그대로 쓰지 않기 때문에 빌드 후 결과물이 저장되는 실제 디렉터리 경로가 dist가 아니라 _nuxt 였음. 그래서 Vite의 기본 해시 규칙이 적용되지 못했음.)
- public 경로 파일들은 빌드 시 dist 경로에 그대로 복사됨.
- 번들링x, 해시 처리x, 파일명이 유지됨.
- /src 내부는 번들러가 관리하는 영역
- public 디렉토리는 번들러가 관여하지 않음
- 공식문서에 따르면 다음 3가지 조건을 가진 에셋은 public 경로에 두는 것이 권장된다.
robots.txt와 같이 소스 코드에서 참조되지 않는 에셋- 해싱을 거치지 않고 항상 같은 이름을 가져야 하는 에셋
- ...또는 URL을 얻기 위해
import할 필요 없는 에셋
- 추가로 정리하면:
- 기준: 에셋을 가져오는 방식이 import 가능한가?
- 다음 상황에서는 public 디렉토리에 저장해야 한다.
- CSS url()에 문자열로 넣고 싶을 때
- CSS url()에서 문자열로 경로를 쓸 경우에는 import가 일어나지 않기 때문에, Vite가 자산으로 처리할 수 없다. 따라서 public에 둬야 한다.
- HTML에서 직접 경로 쓰는 경우
- favicon, robots.txt, manifest.json
- 파일 다운로드용 정적 파일
- CSS url()에 문자열로 넣고 싶을 때
- 다음 상황에서는 public 디렉토리에 둘 필요가 없다(/src/에 둬도 된다)
- import 가능한 경우
- css-in-js에서 import한 걸 적용하는 경우
3. 각 용어 간단 설명:
- Bundling: 여러 JS/CSS/image… 파일들을 소수의 파일로 묶는 과정
- Build: 번들링+트랜스파일링+압축+해시처리 등 전체적인 출력 과정
- ESM(ES Module): 브라우저가 모듈을 직접 가져올 수 있는 표준 모듈 시스템 (import/export 지원)
- CJS(Common JS): 주로 Node.js 환경에서 사용되는 모듈 시스템, ESM과 달리 동기적으로 로딩함
- 온디맨드(On-Demand) 로딩: 실제로 import된 모듈만 요청 (전체 번들링 X)
- esbuild: Go 언어로 작성된 초고속 번들러, CommonJS → ESM 변환 및 사전 번들링 담당
- Rollup: Vite의 프로덕션 번들러. 트리 셰이킹, 코드 스플리팅, 번들 최적화 수행
- Tree-shaking: 빌드 시, 최종 번들 크기를 줄이기 위해 사용되지 않는 코드를 제거하는 최적화 방법. ESM의 정적 분석 특성을 활용하여 최종 번들에서 사용되지 않는 코드를 제거함
- Code Splitting: 하나의 큰 번들을 여러 작은 청크로 나누는 기법. 필요한 코드만 로드할 수 있도록. 초기 로딩 최소화
'TIL*' 카테고리의 다른 글
| AI의 버그 찾기(서버 로그 vs 브라우저 로그) (0) | 2025.12.03 |
|---|---|
| TypeScript Discriminated Union(구별된 유니온) 패턴 적용하기 (0) | 2025.11.19 |
| VSCode, Cursor - TypeScript 경로 별칭(@) 자동 import가 안 될 때 (0) | 2025.10.30 |
| form submit 동작 방식 정리(HTML, React18, React19) (0) | 2025.10.28 |
| 브라우저 엔진, 자바스크립트 엔진 (0) | 2025.10.22 |