Skip to content

refactor: web 패키지 빌드 도구를 webpack에서 vite로 전환#211

Draft
jaeyoung-kwon wants to merge 6 commits into
devfrom
refactor/web-webpack-to-vite
Draft

refactor: web 패키지 빌드 도구를 webpack에서 vite로 전환#211
jaeyoung-kwon wants to merge 6 commits into
devfrom
refactor/web-webpack-to-vite

Conversation

@jaeyoung-kwon

@jaeyoung-kwon jaeyoung-kwon commented Apr 28, 2026

Copy link
Copy Markdown
Contributor

📌 What

  • web 패키지의 앱 빌드와 Storybook을 webpack에서 vite로 전환

❓ Why

  • admin·maeil-mail은 이미 vite를 사용 중이어서 모노레포 내 빌드 도구를 일치시키고 webpack 전용 의존성을 제거하기 위함
  • webpack dev 서버/빌드 속도 대비 vite의 HMR·콜드 스타트 개선을 확보

🔧 How

설정 매핑 — 무엇이 어디로 갔는가

동등하게 옮겨진 것

영역 webpack vite
TS/TSX 트랜스파일 swc-loader @vitejs/plugin-react-swc
SVG → React component @svgr/webpack vite-plugin-svgr
path alias (@, #, @bombom/shared/env) resolve.alias resolve.alias
TanStack Router 라우트 분할 tanstackRouter/webpack tanstackRouter/vite (autoCodeSplitting: true)

vite 빌트인이라 명시 제거된 것

제거된 webpack 설정 vite 빌트인 동작
HtmlWebpackPlugin index.html이 entry로 자동 처리
CopyWebpackPlugin (assets, favicon, prerendered) public/ 자동 복사
css/image/font loader rule 모두 빌트인
devServer.hot / historyApiFallback / client.overlay 모두 빌트인 default

변경된 것

webpack vite 비고
DefinePlugin('process.env': JSON.stringify(process.env)) defineENV_KEYS 화이트리스트만 치환 모든 env가 클라이언트 번들로 들어가던 보안 이슈 개선
SwcMinifyWebpackPlugin (compress/mangle/format) esbuild 빌트인 minify + esbuild.drop/pure mangle.toplevel은 esbuild default로 위임
usedExports / sideEffects 명시 rollup default tree-shaking + package.json sideEffects 명시 불필요
public sitemap/robots 커스텀 복사 postBuildAssetsPlugin (closeBundle hook) dev 빌드에서 sitemap 제거 + robots.{SERVER_TYPE}.txt → robots.txt 리네임 보존

청크 분할 변화 (의도 차이) ⚠️

webpack 시절의 splitChunks cacheGroups에서 react/vendor/common 3-tier 분리 전략을 직접 정의했지만, vite로 옮기면서 manualChunksreact만 명시되어 있고 나머지는 TanStack Router autoCodeSplitting + dynamic import 기반 라우트 분할에 의존하도록 단순화됨.

의도 webpack vite (현재)
React 청크 분리 ✅ cacheGroup react manualChunks: { react: ['react', 'react-dom'] }
라우트 단위 코드 스플리팅 TanStack Router TanStack Router (autoCodeSplitting: true)
Vendor(나머지 node_modules) 단일 청크 캐싱 ✅ cacheGroup vendor ❌ 라우트 청크에 흩어짐
Common(3+회 사용 모듈) 분리 ✅ cacheGroup common (minChunks: 3)
minSize/maxSize/maxAsyncRequests/maxInitialRequests 튜닝 ❌ rollup default

autoCodeSplitting 보충: TanStack Router 플러그인 옵션. 빌드 시 라우트 파일의 component/loader/pendingComponent/errorComponent를 동적 import로 lazy 변환해 라우트별 별도 청크를 만들어줌. 첫 페이지 로딩 번들이 가벼워지는 대신, vendor 라이브러리가 각 라우트 청크에 함께 분산되어 재방문 시 vendor 캐시 효율은 webpack 시절보다 약화됨.

후속 검토 후보: manualChunks를 함수형으로 바꿔 vendor 단일 청크 캐싱 전략을 회복하거나, 라이브러리 그룹별(tanstack, emotion, sentry 등)로 더 세밀하게 분할.

성능 측정 (M-series Mac, pnpm 캐시 warm, 각 3회 측정)

지표 webpack vite 변화
prod build (3회 평균) 3235 ms
(3371 / 3251 / 3082)
2700 ms
(2790 / 2620 / 2690)
−16.5 %
dev cold start (3회 평균)
webpack: 첫 compile 완료 시점
vite: ready in 시점
3113 ms
(3458 / 2897 / 2983)
488 ms
(439 / 600 / 424)
−84.3 %
dist 총 크기 29 MB 33 MB +14 %
JS 청크 개수 101 75 −26 %
JS 단일 최대 청크 358 KB (vendor~bfd775f7) 621 KB (index) ⚠️ +73 %

해석

  • prod build 시간은 약 16% 개선. 더 큰 효과는 dev cold start 84% 단축 (3.1s → 0.5s). vite는 라우트를 lazy compile해서 첫 진입 시점만 즉시 띄우는 구조라 페이지 진입 대기시간이 크게 줄어듦.
  • dist 총량은 +14 % — vite 청크 분할이 단순해지면서 vendor가 entry index 청크에 통합되어 전체 크기가 약간 늘었음. 라우트 단위 분할은 잘 동작하지만 entry index 청크가 621 KB로 비대화된 점은 위 청크 분할 변화 섹션의 vendor 캐시 약화와 같은 현상.
  • HMR 응답 시간은 자동 측정에서 제외. 일반적으로 vite HMR은 변경 모듈만 transform해 1초 미만, webpack-dev-server는 1~3초대.
  • 측정 환경: pnpm 캐시 / node_modules 이미 존재 / dist 매번 삭제 후 cold build / node_modules/.cache 또는 node_modules/.vite 매번 삭제 후 dev cold start.

👀 Review Point

  • 로컬 pnpm run start / pnpm run start:msw 동작 확인 (3000번 포트, MSW 워커 등록)
  • pnpm run build 결과물 점검 — dist/robots.txt가 SERVER_TYPE에 맞는 내용인지, sitemap.xml이 prod 빌드에서만 남는지
  • SVG/AVIF/GIF 임포트하는 컴포넌트 렌더 회귀 (FloatingActionButton, Toast 등)
  • pnpm run storybook / pnpm run build-storybook 정상 동작
  • 번들 분할 결과 비교 — Rollup 기본 분할 + react manualChunks 사용으로 webpack 대비 chunk 분포가 달라질 수 있음 (vendor 캐시 약화는 위 How에 명시)
  • buildspec.yml은 pnpm run build 그대로 사용 — CI 변경 없음

JeLee-river and others added 3 commits May 5, 2026 14:43
- webpack.config.ts 삭제하고 vite.config.ts 추가
  - swc-loader/copy-webpack-plugin/html-webpack-plugin/dev-server 등 webpack 전용 의존성 제거
  - tanstackRouter, vite-plugin-svgr, @vitejs/plugin-react-swc 도입
- 환경 변수는 loadEnv + define으로 process.env.X를 빌드 상수로 치환해 src 코드 변경 없이 동작
- public/robots.{SERVER_TYPE}.txt → dist/robots.txt 리네임과 dev 빌드의 sitemap 제외 동작은 closeBundle 인라인 플러그인으로 대체
- "type": "module" 도입에 따라 babel.config.js → babel.config.cjs 로 리네임 (jest 호환)
- tsconfig moduleResolution을 nodenext에서 bundler로 정렬 (admin/maeil-mail와 일치)
- index.html에 vite 모듈 엔트리 스크립트 추가

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- @storybook/react-webpack5 → @storybook/react-vite (9.0.17 → 9.1.20 업그레이드 동반)
- @storybook/addon-webpack5-compiler-swc, @svgr/webpack, tsconfig-paths-webpack-plugin, webpack, @types/webpack 제거
- .storybook/main.ts 재작성: viteFinal에서 mergeConfig로 환경 변수 define만 주입, SVG/path-alias는 메인 vite.config.ts와 vite-plugin-svgr에 위임
- .storybook/preview.tsx와 모든 *.stories 파일의 @storybook/react-webpack5 타입 import를 @storybook/react-vite로 일괄 교체

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
7746eee (refactor: web 앱 빌드를 webpack에서 vite로 전환)에 빌드 변환과
직접 관련 없는 4건이 함께 변경되어 있어 그 직전 상태로 복원:

- public/sitemap.xml: 로그인 페이지(/login) 항목 복원, landing 페이지
  priority 0.8 → 0.3 원상복귀
- src/contexts/AuthContext.tsx: /signup 경로에서 userProfile query를
  비활성화하던 로직 제거 (vite 작업과 무관)
- src/pages/my-page/components/NotificationSettingsSection/{
    NotificationSettingsSection.tsx, SettingList.tsx
  }: 알림 카테고리 매칭이 7746eee에서 인덱스 기반으로 회귀했던 것을
  77ab3f5 (refactor: 알림 카테고리 조회를 인덱스 매칭에서 category 키
  매칭으로 변경)의 category 키 매칭 방식으로 다시 복원

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Comment thread web/.storybook/main.ts
Comment on lines +11 to 21
viteFinal: async (config) => {
return mergeConfig(config, {
define: {
'process.env.API_BASE_URL': JSON.stringify(
process.env.API_BASE_URL ?? 'https://api-dev.bombom.news/api/v1',
),
'process.env.API_TOKEN': JSON.stringify(process.env.API_TOKEN ?? ''),
'process.env.ENABLE_MSW': JSON.stringify(process.env.ENABLE_MSW ?? 'false'),
},
});

return {
...config,
resolve: {
...config.resolve,
plugins: [
...(config.resolve?.plugins || []),
new TsconfigPathsPlugin({}),
],
},
};
},

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

부연 설명: webpack 시절 main.ts가 60줄에서 24줄로 줄어든 이유

webpack 버전에서 webpackFinal이 직접 처리하던 세 가지가 vite 버전에서는 다음과 같이 분배됨:

  1. 환경 변수 주입 (process.env.X → 빌드 상수)

    • webpack: webpack.EnvironmentPlugin + webpack.DefinePlugin(JSON.stringify(process.env)) 두 단계
    • vite: viteFinal에서 mergeConfigdefine만 주입. vite의 define은 단순 raw 치환이라 문자열 값에는 JSON.stringify로 따옴표를 직접 감싸야 함 (그래서 JSON.stringify(... ?? '...') 형태). ?? fallback은 storybook 단독 실행(storybook dev) 시 .env가 없는 케이스 대비.
  2. SVG 처리 (*.svg → React component)

    • webpack: imageRule.exclude = /\.svg$/ 로 기존 image rule을 깎고 @svgr/webpack을 새 rule로 추가하는 수동 surgery
    • vite: 메인 web/vite.config.tsvite-plugin-svgr이 처리. @storybook/react-vite가 메인 vite config을 자동 상속하므로 storybook 쪽에서 다시 선언할 필요 없음.
  3. 이미지 자산 처리 (avif/png/jpe?g/gif)

    • webpack: asset/resource rule을 직접 push
    • vite: 빌트인. 별도 설정 불필요.
  4. path alias (@/, #/)

    • webpack: TsconfigPathsPluginresolve.plugins에 주입
    • vite: 메인 vite.config.ts의 alias 설정 + tsconfig를 storybook이 자동 상속.

요약: vite가 빌트인으로 처리하거나(이미지) 메인 config을 상속(SVG/alias)하는 부분이 많아 storybook main.ts는 "환경 변수 주입"만 남음.

@jaeyoung-kwon jaeyoung-kwon changed the title [FE] refactor: web 패키지 빌드 도구를 webpack에서 vite로 전환 refactor: web 패키지 빌드 도구를 webpack에서 vite로 전환 May 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants