Vite 설정 최적화 — 빌드 속도와 개발 경험 동시에 잡기
CRA에서 Vite로 넘어오면서 배운 것들
3년 전만 해도 Create React App은 정말 고마운 도구였다. 그런데 프로젝트가 커지면서 빌드 시간이 40초, 50초가 되니까 개발 경험이 정말 나빠졌다. HMR도 매번 10초 이상 걸렸고, 핫 리로드가 제대로 안 될 때도 많았다. 나는 번들러를 바꿔야겠다고 생각했고, 그게 Vite였다.
처음 Vite로 마이그레이션했을 때 기대는 했지만, 실제 결과는 기대를 훨씬 넘었다. 같은 프로젝트를 Vite로 설정하니 개발 서버 시작 시간이 15초에서 2초로 줄었다. HMR도 평균 0.3초면 충분했다. 그런데 Vite 설정을 제대로 안 하면 이런 이점을 제대로 살릴 수 없다는 걸 깨달았다.
vite.config.ts의 핵심 설정들
먼저 기본 Vite 설정부터 시작하자. 뼈대는 간단하지만, 프로덕션 빌드를 최적화하려면 신경 써야 할 게 많다.
<?xml version="1.0"?>
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { visualizer } from 'rollup-plugin-visualizer'
export default defineConfig({
plugins: [
react({
babel: {
plugins: [['@babel/plugin-proposal-decorators', { legacy: true }]],
},
}),
visualizer({
open: true,
gzipSize: true,
brotliSize: true,
}),
],
build: {
target: 'ES2020',
minify: 'terser',
terserOptions: {
compress: {
drop_console: true,
drop_debugger: true,
},
},
rollupOptions: {
output: {
manualChunks: {
'vendor': ['react', 'react-dom'],
'ui-lib': ['@mui/material', '@emotion/react'],
},
},
},
},
server: {
port: 3000,
open: true,
cors: true,
},
})
이 설정의 핵심은 뭘까? 먼저 build.target을 ES2020으로 설정했다. 옛날엔 ES5를 타겟으로 해야 했지만, 2025년의 브라우저들은 대부분 ES2020을 지원한다. 이렇게 하면 번들 크기가 20~30% 줄어든다.
minify: 'terser'는 기본값이지만 명시적으로 설정했다. esbuild도 괜찮지만, 복잡한 코드 최적화엔 Terser가 낫다. drop_console을 활성화하면 프로덕션 빌드에서 모든 console.log가 제거된다.
가장 중요한 건 manualChunks다. 의존성을 적절히 분리하면 캐싱이 훨씬 효율적이다. React와 React DOM은 거의 안 바뀌니까 별도 청크로 분리하고, UI 라이브러리도 마찬가지다. 이렇게 하면 코드만 변할 때 유저 캐시를 활용할 수 있다.
플러그인 에코시스템 활용
Vite의 진정한 힘은 플러그인 시스템에 있다. 내가 자주 쓰는 플러그인들을 소개하겠다.
<?xml version="1.0"?>
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { compression } from 'vite-plugin-compression'
import legacy from '@vitejs/plugin-legacy'
import inspect from 'vite-plugin-inspect'
export default defineConfig({
plugins: [
react(),
compression({
verbose: true,
disable: false,
threshold: 10240,
algorithm: 'brotli',
ext: '.br',
}),
legacy({
targets: ['defaults', 'not IE 11'],
}),
inspect(),
],
})
vite-plugin-compression은 빌드 후 자동으로 Brotli 압축을 해준다. 대부분의 모던 브라우저는 Brotli를 지원하고, gzip보다 20~30% 더 잘 압축된다. threshold를 설정해서 10KB 이상인 파일만 압축하도록 했다.
@vitejs/plugin-legacy는 구형 브라우저 지원이 필요할 때 쓴다. 회사에서 IE11을 지원해야 한다면 이 플러그인이 필수다. 다만 번들 크기가 30~40% 증가하니까 정말 필요할 때만 활성화하자.
vite-plugin-inspect는 개발 중에 Vite가 어떻게 코드를 변환하는지 보여준다. 가끔 이상한 동작을 하면 이걸로 디버깅한다.
HMR 최적화 — 개발 경험의 핵심
HMR(Hot Module Replacement)이 빠를수록 개발이 쾌적하다. Vite는 기본적으로 빠르지만, 추가로 튜닝할 게 있다.
<?xml version="1.0"?>
export default defineConfig({
server: {
hmr: {
protocol: 'ws',
host: 'localhost',
port: 5173,
timeout: 60000,
},
middlewareMode: false,
watch: {
usePolling: false,
ignored: ['**/node_modules/**', '**/.git/**'],
},
},
})
hmr.protocol을 명시적으로 설정했다. localhost에선 상관없지만, Docker 환경이나 네트워크를 거칠 땐 중요하다. 프로덕션 도메인으로 HMR 요청을 보내야 하는 경우도 있으니까.
watch.usePolling: false는 파일 변경을 감지하는 방식이다. 기본값이 false지만, NFS 드라이브나 WSL2 환경에선 true로 해야 할 수도 있다. 대신 CPU 사용률이 올라가니까 필요할 때만 켜자.
개발과 프로덕션의 차이를 활용하기
Vite는 개발과 프로덕션 환경에서 완전히 다르게 동작한다. 이걸 제대로 이해하면 더 최적화할 수 있다.
<?xml version="1.0"?>
export default defineConfig(({ command, mode }) => {
const isDev = command === 'serve'
const isProd = command === 'build'
return {
define: {
'process.env.APP_ENV': JSON.stringify(mode),
'process.env.IS_DEV': isDev,
'__DEV__': isDev,
},
build: {
sourcemap: !isProd,
chunkSizeWarningLimit: 1000,
cssCodeSplit: true,
},
server: {
fs: {
strict: false,
},
},
}
})
개발 환경에서만 sourcemap을 활성화하면 빌드가 훨씬 빨라진다. 프로덕션에선 sourcemap이 필요 없으니까. chunkSizeWarningLimit은 청크가 너무 크면 경고를 띄운다. 기본값 500KB는 너무 엄격하다고 생각해서 1000KB로 설정했다.
환경 변수 관리
Vite에서 환경 변수는 빌드 타임에 주입된다. 이걸 잘못 이해하면 곤란하다.
<?xml version="1.0"?>
// .env.development
VITE_API_URL=http://localhost:3001
VITE_LOG_LEVEL=debug
// .env.production
VITE_API_URL=https://api.example.com
VITE_LOG_LEVEL=error
// vite.config.ts
import { defineConfig, loadEnv } from 'vite'
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd(), '')
return {
define: {
'process.env': JSON.stringify(env),
},
}
})
중요한 건 VITE_ 접두사다. Vite는 VITE_로 시작하는 변수만 번들에 포함한다. 다른 환경 변수는 보안상 노출되지 않는다. 이건 민감한 정보(API 키 같은)를 실수로 번들에 포함하는 걸 방지한다.
빌드 성능 측정과 모니터링
빌드를 최적화하려면 먼저 병목을 찾아야 한다. Vite는 좋은 도구들을 제공한다.
<?xml version="1.0"?>
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { visualizer } from 'rollup-plugin-visualizer'
export default defineConfig({
plugins: [
react(),
visualizer({
open: true,
gzipSize: true,
brotliSize: true,
title: 'Bundle Analysis',
}),
],
build: {
rollupOptions: {
output: {
experimentalMinChunkSize: 20000,
},
},
},
})
Visualizer는 번들 구조를 시각화해준다. 어떤 라이브러리가 얼마나 자리를 차지하는지 한눈에 볼 수 있다. 불필요한 의존성이 있으면 바로 보인다.
실제 프로젝트에서의 성과
내가 일하는 프로젝트에서 Vite 최적화로 얻은 결과를 정리하면:
- 개발 서버 시작: 40초 → 2초 (95% 감소)
- HMR 응답시간: 10초 → 0.3초 (97% 감소)
- 프로덕션 번들: 2.5MB → 680KB (73% 감소)
- 초기 로딩 시간: 8초 → 1.2초
이정도면 개발 생산성이 정말 달라진다. PR 리뷰할 때마다 코드 저장하고 HMR로 바로 변화를 본다. 버그 수정도 빠르고, 피드백 루프가 짧아진다.
주의할 점들
Vite를 쓰면서 주의해야 할 게 몇 가지 있다.
첫째, 번들 분석을 자주 하자. 의존성이 하나 추가될 때마다 번들 크기가 얼마나 변하는지 확인해야 한다. 불필요한 라이브러리가 슬금슬금 쌓인다.
둘째, SSR을 하려면 설정이 복잡해진다. 클라이언트 빌드와 서버 빌드를 따로 해야 하고, 플러그인도 신경써야 한다. 난 SSR이 필요한 프로젝트에선 Next.js를 권한다.
셋째, 구형 브라우저 지원이 필요하면 @vitejs/plugin-legacy를 쓰되, 성능 비용을 감수해야 한다. IE11 지원에 30~40% 번들 증가는 현대적인 선택지다.
마무리
Vite는 단순한 번들러가 아니라, 개발 경험을 근본적으로 바꾼 도구다. 처음부턴 충분히 빠르지만, 설정을 제대로 하면 훨씬 더 최적화할 수 있다. 특히 청킹 전략과 플러그인 선택이 중요하다.
만약 아직도 Create React App이나 Webpack을 쓰고 있다면, 정말 진심으로 Vite로 옮겨보길 권한다. 처음엔 마이그레이션 비용이 있지만, 얻는 생산성 향상이 그걸 충분히 상쇄한다.