2020년 이후 5년간 스택을 유지한 컬리 CMS가 도메인·의존성 폭증으로 빌드 OOM에 이르자 Webpack4를 Vite로 전환한 기록이다. 순환 참조 제거와 번들러 교체로 8GB 메모리 요구를 없애고 개발 서버 시작을 460배 단축했다.
핵심 포인트- 도메인 10→40개+·의존성 100→300개+로 커지며 2025년 2월 CI 빌드가 JavaScript heap out of memory로 급증했고 로컬도 8GB가 필수였다.
- 근본 원인은 barrel 파일 절대경로가 만든 순환 참조(Scope Hoisting 실패·청크 계산 복잡·타입 검사 메모리 증가)와 전체 의존성 그래프를 메모리에 유지하는 Webpack4 구조다.
- Vite/Parcel/Rsbuild 중 ESM 기반·생태계·기존 스택(React 16·MobX 5 decorator·MUI4) 호환으로 Vite를 선택했다.
- 설정 538줄→121줄(77%), Node 16→22, 환경변수 process.env→import.meta.env로 전환했다.
- 결과는 빌드 54.28→28.21초(48%), 개발 서버 시작 47초→102ms(460배), 번들 57MB→11MB(81%), 21만 줄 삭제다.
상세 정리- 스택: React 16.13.1·TS 4.4.0·MobX 5.15.6·MUI 4.11.0·Webpack 4.44.2, 설정이 200→538줄로 늘었다.
- OOM: 2025-02-27 CI 빌드 실패가 급증했고, 메모리 임계 테스트에서 1~5.5GB는 OOM·6GB 45.07초·8GB 48.15초로 최소 6GB가 필요했다.
- 원인 1(순환 참조): barrel index의 절대경로 재내보내기가 순환 구조를 유발해 상대경로로 수정했고, ModuleConcatenationPlugin scope hoisting 실패·SplitChunks 계산 복잡·순환 타입 추론으로 메모리가 늘었다.
- 원인 2(Webpack4): 전체 의존성 그래프를 메모리에 유지하고 GC 오버헤드·전체 AST 보관이 컸다.
- Vite 선택: ESM 개발 서버로 요청 시점 모듈만 제공해 OOM을 근본 해결하고, esbuild 사전 번들링, plugin-react·vite-plugin-babel·visualizer 생태계, 기존 스택 호환이 이유였다.
- 브랜치 전략: qa-vite-conversion 브랜치에서 진행해 실패 시 회귀 가능하게 했다.
- 설정: MobX decorator용 babel(plugin-proposal-decorators legacy·class-properties loose), 경로 alias, manualChunks로 handsontable·lottie·toast-ui·vendor를 분할했다.
- 마이그레이션 이슈: aws-sdk의 querystring을 qs로 alias, tui-color-picker IE6 해킹 CSS 경고를 logOverride로 무시, react-csv는 esbuild 비호환이라 babel로만 트랜스파일, dayjs 초기화 순서는 optimizeDeps.include로 해결, MUI4 inputProps→InputProps, HMR 순환참조는 lazy import로 처리했다.
- 성능(M2 Pro 32GB 12코어): production 빌드 Webpack 54.28초 vs Vite 28.21초(48%), 개발 서버 47초 vs 102ms(99.8%·460배), 번들 57→11MB(81%), 코드 21만 줄 삭제, 메모리 8GB→4GB 이하.
- 후속 변경: Node 16.18→22.14, 환경변수 process.env.REACT_APP_→import.meta.env.VITE_, .js에서 JSX 불가라 .jsx/.tsx가 필수가 됐다.
- 교훈: 기술 부채는 이자가 붙어 OOM으로 폭발하고, 체감이 아닌 측정(메모리 디버깅·임계 테스트)이 의사결정 근거이며, 별도 브랜치로 점진 전환이 과감한 시도를 가능하게 했다.
왜 읽나오래된 Webpack 프로젝트의 빌드 OOM·느린 개발 서버로 고통받는 프론트엔드 개발자에게 Vite 전환의 원인 분석·설정·이슈 해결 레퍼런스.