Webpack에서 Vite 마이그레이션 및 번들 크기 최소화를 위한 Tree Shaking 설정 II
1. 소개 및 개요
1. 소개 및 개요
현대 프론트엔드 개발 생태계에서 프로젝트의 규모가 커짐에 따라, 개발자가 직면하는 가장 큰 병목 지점 중 하나는 바로 '빌드 속도'와 '개발 경험(DX, Developer Experience)'입니다. 과거에는 프로젝트의 규모가 작았기에 Webpack과 같은 강력한 번들러가 제공하는 세밀한 설정이 큰 문제가 되지 않았습니다. 하지만 수천 개의 모듈이 얽혀 있는 대규모 엔터프라이즈 애플리케이션 환경에서는, 모든 모듈을 하나로 묶어(Bundling) 실행 가능한 파일을 만드는 과정 자체가 개발 흐름을 끊는 거대한 장애물이 되곤 합니다.
번들러의 패러다임 변화: Webpack에서 Vite로
전통적인 Webpack 기반의 워크플로우는 소스 코드를 수정할 때마다 전체 혹은 변경된 부분의 의존성 그래프를 다시 계산하고, 이를 하나의 번들 파일로 만드는 과정을 거칩니다. 프로젝트가 커질수록 이 '번들링' 과정에 소요되는 시간은 기하급급수적으로 늘어나며, 이는 곧 개발자의 대기 시간 증가와 생산성 저하로 직결됩니다.
이러한 문제를 해결하기 위해 등장한 Vite는 기존의 패러다임을 완전히 뒤집었습니다. Vite는 브라우저의 Native ESM(ES Modules)을 활용합니다. 즉, 개발 서버를 구동할 때 모든 코드를 미리 번들링하는 대신, 브라우저가 요청하는 시점에 해당 모듈을 즉시 전달하는 방식을 취합니다. 이를 통해 프로젝트 규모와 상관없이 거의 즉각적인 서버 구동(Cold Start)이 가능해졌으며, 수정된 모듈만 교체하는 효율적인 HMR(Hot Module Replacement)을 구현하여 개발자의 흐름이 끊기지 않도록 돕습니다.
왜 단순한 전환 그 이상이 필요한가?
많은 개발자가 단순히 "속도가 빠르니까 Vite로 바꾸자"라는 생각으로 마이그레이션을 시작합니다. 하지만 단순히 설정 파일(
webpack.config.js $\rightarrow$ vite.config.ts)을 옮기는 것만으로는 충분하지 않습니다. 진정한 의미의 마이그레이션은 다음 두 가지 핵심 과제를 해결해야 합니다.- 런타임 환경의 차이 극복: Webpack의
DefinePlugin이나ProvidePlugin등 Webpack 전용 기능에 의존하던 로직을 Vite의 플러인 생태계와 환경 변수 시스템(import.meta.env)에 맞게 재설계해야 합니다. - 프로덕션 빌드 최적화(Tree Shaking & Size Control): Vite는 개발 환경에서는 ESM을 사용하지만, 프로덕션 빌드 시에는 여전히 Rollup을 사용하여 코드를 번들링합니다. 이때, 우리가 작성한 코드와 외부 라이브러리가 얼마나 효율적으로 Tree Shaking(사용하지 않는 코드를 제거하는 과정)되는지가 서비스의 성능을 결정짓습니다.
본 가이드의 목적: 성능과 최적화의 결합
본 가이드는 단순히 Webpack에서 Vite로 전환하는 방법을 넘어, '어떻게 하면 프로덕션 환경에서 최적의 번들 사이즈를 유지할 것인가'에 초점을 맞춥니다.
우리는 다음과 같은 흐름으로 학습을 진행할 것입니다:
- Step 1: Webpack 의존성 분석 및 Vite로의 구조적 전환 전략
- Step 2: Rollup 기반의 프로덕션 빌드 최적화 및 Tree Shaking 극대화 기법
- Step 3:
esbuild를 활용한 사전 번들링(Pre-bundling) 최적화 및 의존성 관리
결과적으로, 개발 시에는 Webpack보다 압도적으로 빠른 피드백 루프를 경험하고, 배포 시에는 Rollup의 강력한 최적화 기능을 통해 최소한의 JavaScript 페이로드(Payload)만을 사용자에게 전달하는 '고성능 프론트엔드 아키텍처'를 구축하는 것이 이 가이드의 최종 목표입니다.
2. 핵심 구현 및 설정 방법
2. 핵심 구현 및 설정 가이드: Webpack에서 Vite로의 전환과 최적화
Webpack에서 Vite로의 전환은 단순히 빌드 도구를 교체하는 작업이 아닙니다. 이는 '모든 모듈을 번들링한 후 실행하는 방식(Bundle-based)'에서 '브라우저의 Native ESM을 활용하여 필요한 모듈만 요청하는 방식(Native ESM-based)'으로의 패러듈 전환을 의미합니다. 성공적인 전환과 번들 사이즈 최적화를 위해 반드시 거쳐야 할 단계별 가이드를 제시합니다.
2.1 의존성 재설계 및 환경 구축 (Dependency Re-engineering)
가장 먼저 수행해야 할 작업은 기존 Webpack 기반의 무거운 의존성들을 제거하고, Vite 생태계에 맞는 가벼운 플러그인으로 교체하는 것입니다.
- 기존 번들러 제거:
webpack,webpack-cli,webpack-dev-server,babel-loader등 Webpack 관련 패키지를 삭제합니다. - Vite 코어 설치:
vite와 React 환경이라면@vitejs/plugin-react를 설치합니다. - 로더 대체: Webpack의
css-loader,sass-loader등은 Vite에서 별도의 설치 없이 내장된 기능을 사용하거나, Vite 전용 플러그인으로 대체하여 설정의 복잡도를 낮춥니다.
2.2 Webpack 설정을 Vite로 이식하기 (Configuration Migration)
Webpack의
module.rules와 plugins 설정을 Vite의 vite.config.ts로 재구성해야 합니다. 이때 핵심은 '어떻게 하면 최대한 단순하게 유지하면서도 최적화 기능을 활용할 것인가'입니다.[예시] vite.config.ts 최적화 설정
import { defineConfig } eyes from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';
export default defineConfig({
plugins: [react()],
resolve: {
// Webpack의 alias 설정을 그대로 이식하여 경로 참조 오류 방지
alias: {
'@': path.resolve(__dirname, './src'),
},
},
build: {
// 1. Rollup 옵션을 통한 정밀한 트리 쉐이킹(Tree Shaking) 설정
rollupOptions: {
output: {
// 청크 분할(Chunk Splitting) 전략: 대형 라이브러리를 별도 파일로 분리
manualChunks: (id) => {
if (id.includes('node_modules')) {
if (id.includes('react') || id.includes('react-dom')) {
return 'vendor-react';
}
return 'vendor'; // 나머지 외부 라이브러리 분리
}
},
},
},
// 2. 번들 사이즈 최적화 관련 설정
target: 'esnext', // 최신 브라우저 기능을 활용하여 가벼운 코드 생성
minify: 'esbuild', // Webpack의 Terser보다 훨씬 빠른 esbuild 사용
cssCodeSplit: true, // CSS를 별도 파일로 분리하여 초기 로딩 속도 개선
},
server: {
// 개발 서버 환경 설정
port: 3000,
open: true,
},
});
2.3 트리 쉐이킹(Tree Shaking) 극대화 전략
Vite는 빌드 단계에서 Rollup을 사용합니다. Rollup의 강력한 정적 분석 능력을 활용하여 사용하지 않는 코드를 제거하는 것이 번들 사이즈 감소의 핵심입니다.
- Side Effects 명시:
package.json에"sideEffects": false를 설정하거나, CSS/Polyfill 등 실제 부작용이 있는 파일만 배열로 명시하십시오. 이는 Rollup이 "이 모듈은 안전하게 제거할 수 있다"라고 판단하는 근거가 됩니다. - ESM(ES Modules) 준수: 외부 라이브즘을 도입할 때, CommonJS(CJS) 버전이 아닌 ESM 버전을 지원하는 라이브러리를 우선적으로 선택하십시오. CJS는 정적 분석이 어려워 트리 쉐이킹이 제대로 작동하지 않을 확률이 높습니다.
- Manual Chunks 전략: 위 코드 예시의
manualChunks를 활용하여, 자주 변경되지 않는 대규모 라이브러리(예: lodash, three.js)를 별도의 청크로 분리하십시오. 이는 브라우저 캐싱 효율을 극대화합니다.
2.4 환경 변수 및 모듈 호환성 처리
Webpack의
DefinePlugin을 사용하던 방식에서 Vite의 import.meta.env 방식으로 전환해야 합니다.- Webpack:
process.env.NODE_ENV - Vite:
import.meta.env.MODE(또는import.meta.env.VITE_APP_TITLE처럼VITE_접두사가 붙은 변수)
이 과정에서 기존에
process.env를 직접 참조하던 레거시 코드들을 찾아 import.meta.env로 리팩토링하는 작업이 반드시 병행되어야 빌드 에러를 방지할 수 있습니다.3. 모니터링 및 트러블슈팅
3. 모니터링 및 트러블슈팅: 최적화의 완성
마이그레이션과 설정이 완료되었다고 해서 모든 작업이 끝난 것은 아닙니다. 실제 운영 환경과 유사한 환경에서 번들 크기를 모니터링하고, 예상치 못한 런타임 에러나 의존성 충돌을 해결하는 과정이 반드시 필요합니다. 본 섹션에서는 마이re그레이션 이후 발생할 수 있는 주요 문제들과 이를 해결하기 위한 전문적인 디버깅 전략을 다룹니다.
3.1 번들 크기 모니터링: rollup-plugin-visualizer 활용
트리 쉐이킹(Tree Shaking)이 제대로 작동하고 있는지, 어떤 라이브러리가 번들 크기를 비대하게 만들고 있는지 확인하기 위해서는 시각화 도구가 필수적입니다. 단순히 숫자로 된 파일 크기를 보는 것이 아니라, 의존성 그래프를 통해 '범인'을 찾아야 합니다.
설정 방법
rollup-plugin-visualizer를 설치하고 vite.config.ts에 통합합니다.npm install -D rollup-plugin-visualizer
// vite.config.ts
import { defineConfig } from 'vite';
import { visualizer } from 'rollup-plugin-visualizer';
export default defineConfig({
plugins: [
// ... 기존 플러그인 설정
visualizer({
open: true, // 빌드 완료 후 자동으로 브라우저에서 리포트 열기
filename: 'stats.html', // 생성될 리포트 파일명
gzipSize: true, // gzip 압축 크기 표시
template: 'treemap', // treemap 형태의 시각화
}),
ms ],
});
모니터링 포인트
- Unused Dependencies 확인:
stats.html을 열었을 때, 사용하지 않는데도 큰 면적을 차지하고 있는 라이브러리가 있다면package.json에서 제거하거나 더 가벼운 대체재(예:moment$\rightarrow$dayjs)로 교체해야 합니다. - Duplicate Modules 감지: 동일한 라이브러리가 서로 다른 버전으로 중복 포함되어 있는지 확인합니다. 이는
npm dedupe나yarn dedupe를 통해 해결할 수 있습니다. - Side Effects 확인: 특정 라이브러리가
sideEffects: true로 설정되어 있어 트리 쉐이킹이 방해받고 있는지 체크합니다.
3.2 주요 트러বি슈팅 가이드 (Common Issues & Solutions)
마이그레이션 과정에서 개발자를 가장 괴롭히는 세 가지 핵심 이슈와 해결책을 정리했습니다.
이슈 1: process is not defined (환경 변수 참조 에러)
Webpack 환경에서는 process.env를 통해 환경 변수에 접근할 수 있었지만, Vite는 브표준 ESM을 따르므로 process 객체가 존재하지 않습니다.- 증상: 브라우저 콘솔에
Uncaught ReferenceError: process is not defined발생. - 해결책:
import.meta.env로 교체합니다.
2. 기존 코드가 너무 방대하여 일괄 수정이 어렵다면, vite.config.ts의 define 옵션을 사용하여 폴리필을 제공합니다.// vite.config.ts
export default defineConfig({
define: {
'process.env': {}, // 빈 객체라도 할당하여 에러 방지
},
});
이슈 2: CommonJS 모듈 로드 실패 (require is not defined)
Vite는 ESM 기반이므로 require 문법을 지원하지 않습니다. 오래된 라이브러리가 내부적으로 require를 사용하고 있다면 에러가 발생합니다.- 증상:
Uncaught ReferenceError: require is not defined발생. - 해결책:
@rollup/plugin-commonjs와 유사한 역할을 하는 Vite의 내장 기능을 활용하거나, vite-plugin-commonjs 같은 플러그인을 도입합니다.
3. 가장 근본적인 해결책은 해당 라이브라이를 ESM을 지원하는 최신 버전으로 교체하는 것입니다.이슈로 3: 의존성 사전 번들링(Dependency Pre-bundling) 오류
특정 라이브러리가 브라우저에서 지원하지 않는 문법을 포함하고 있거나, 의존성 관계가 복잡할 때 발생합니다.- 해결책:
vite.config.ts의optimizeDeps설정을 통해 명시적으로 해당 패키지를 포함시킵입다.
// vite.config.ts
export default defineConfig({
optimizeDeps: {
include: ['my-problematic-package', 'other-package'],
},
});
3.3 디버깅 팁: vite-plugin-inspect 활용
어떤 플러그인이 코드를 어떻게 변형시키는지, 빌드 과정에서 어떤 일이 일어나는지 추적하기 어렵다면
vite-plugin-inspect를 사용하십시오. 이 플러그인은 빌드 파이프라인의 각 단계(Transform, Resolve 등)를 시각적으로 보여줍니다.npm install -D vite-plugin-inspect
// vite.config.ts
import Inspect from 'vite-plugin-inspect';
export default defineConfig({
plugins: [
Inspect(),
// ... 기타 플러그인
],
});
설치 후 서버를 실행하면
localhost:5173/__inspect/ 경로를 통해 각 파일이 플러그인을 거치며 어떻게 변했는지(Before/After)를 한눈에 확인할 수 있어, 복잡한 트랜스파일링 오류를 잡는 데 결정적인 역할을 합니다.결론
본 가이드에 기술된 단계별 설정을 참고하여 시스템 최적화를 완료하시기 바랍니다. 추가로 생기는 예외 상황이나 질문은 댓글로 남겨주시면 성심껏 조치해 드리겠습니다.
댓글 1
전문적인 지식 교류에 참여하시려면 HOWTODOIT 회원이 되어주세요.
로그인 후 참여하기