번들 크기를 줄이자: Tree Shaking/Code Splitting
번들이 무엇이길래 줄여야 최적화가 될까요? 모듈과 번들링모듈 : 독립적인 기능을 가진 코드 블록 // math.js — 모듈 정의export const add = (a, b) => a + b;export const subtract = (a, b) => a - b;export const multiply
hyeche.tistory.com
저희 여기서 번들 크기 줄이기로 code splitting을 살펴봤었는데요,
오늘은 그 번들 사이즈를 시각적으로 확인할 수 있는 Webpack Bundler Analyze를 알아봅시다!
webpack-bundle-analyzer는 언제 사용할까?
- 번들 크기 개선: 번들에 포함된 불필요하거나 중복된 모듈을 식별하여 번들 크기 줄이기 가능
- 로딩 성능 개선: 번들의 크기가 클수록 로딩 시간이 증가하므로 이를 최적화하여 사용자 경험 개선
- 디펜던시 관리: 예상보다 큰 외부 라이브러리나 불필요한 라이브러리를 발견하여 효율적으로 관리 가능
- 코드 분리 검토: 코드 스플리팅 상태를 확인하고 적절히 분리되지 않은 모듈을 찾아 수정 가능
- 의존성 추가 후 점검: 새로운 라이브러리를 추가한 후 번들 크기와 성능에 미치는 영향 분석
- 불필요한 모듈 제거: 중복되거나 사용하지 않는 모듈 제거할 필요성 파악 가능
이제 제가 진행했던 프로젝트에 적용해볼게요!
CRA로 작업하면 webpack-bundle-analyzer를 사용하지만 저는 Vite로 작업했기 때문에 rollup-plugin-visualizer를 사용하겠습니다.

pnpm add -D rollup-plugin-visualizer
설치한 후 vite.config.ts에 다음과 같이 추가합니다.
import { visualizer } from 'rollup-plugin-visualizer'
export default defineConfig({
plugins: [
visualizer({
open: true,
gzipSize: true,
brotliSize: true,
})
]
})
그 다음 pnpm build로 실행하면 stats.html 파일이 생성되는데요
브라우저에서 확인하면 아래와 같이 번들의 크기를 시각적으로 확인할 수 있습니다.


각 블록에 마우스를 올리면 해당 모듈의 파일명, Rendered, Gzip, Brotli 크기를 확인할 수 있습니다.
Rendered - 트리쉐이킹 등 번들러 최적화가 적용된 후 크기 (실제 번들에 포함되는 크기라 기준이 되는 값)
Gzip - Gzip 압축 후 크기 (실제 네트워크 전송 크기)
Brotli - Brotli 압축 후 크기 (Gzip보다 압축률이 좋음)
여기서 얻을 수 있는 인사이트는 크게 세 가지입니다.
1. 크기가 큰 라이브러리 발견
생각보다 무거운 라이브러리가 있으면 더 가벼운 대안으로 교체할지 고민할 수 있습니다
2. 중복 번들 감지
같은 라이브러리가 여러 청크에 중복으로 들어가 있는 경우를 발견할 수 있습니다.
3. Code Splitting 효과 확인
청크가 잘 나뉘어져 있는지, 특정 청크에 너무 많은 코드가 몰려있지 않은지 확인할 수 있습니다.
봐야할 포인트를 정리해봅시다.
1. node_modules 블록이 src 블록보다 큰지 확인
node_modules 블록이 src 블록보다 압도적으로 크면 라이브러리 의존성을 점검해야 합니다.
근데 지금 react-dom 너무 크잖아요? 이건 뭐 필수니까 줄일 수가 없습니다..
필수인 라이브러리들은 어쩔 수 없고,,
실제로 문제가 되는 경우는
쓰지도 않는 라이브러리가 들어있거나,
비슷한 기능인데 라이브러리를 여러 개 쓸 때!(axios + fetch)
2. 같은 라이브러리에 여러 청크가 반복되는지
3. src 영역에서 페이지 코드가 차지하는 크기
특정 페이지 코드가 비정상적으로 크면 code splitting 후보입니다.
manualChunks 옵션
하나의 번들을 수동으로 분할시키는 옵션입니다.
기본적으로 Vite가 자동으로 청크를 나누는데 manualChunks 옵션을 사용하면 개발자가 수동으로 제어할 수 있습니다.
// vite.config.ts
export default defineConfig({
build: {
rollupOptions: {
output: {
manualChunks: {
'react-vendor': ['react', 'react-dom'],
'query-vendor': ['@tanstack/react-query'],
'axios-vendor': ['axios'],
}
}
}
}
})
vite.config.ts를 수정하면 index.js를 라이브러리별로 분리할 수 있습니다.
index.js를 나누는 게 왜 좋냐!
지금은 [react-dom + 내 코드]가 index.js하나에 들어있습니다.
그러면 제가 버튼 색깔 하나만 바꿔도 index.js 전체가 새로 해시값이 바뀌어서 전체를 재다운로드 해야합니다.
근데 분리해두면 react-dom은 거의 안 바뀌니까 브라우저가 캐시해둔 것을 그냥 쓰게 되고
내 코드에서의 변경분만 새로 받으면 됩니다.
자주 안 바뀌는 것들은 따로 빼서 캐시를 오래 사용하자!
manualChunks(id) {
if (id.includes('react-dom')) return 'react-vendor'
if (id.includes('@tanstack')) return 'query-vendor'
if (id.includes('axios')) return 'axios-vendor'
}
저는 타입 에러가 나서 부분 수정했습니다..

시각화만 바뀐 게 아니라 실제 dist 폴더에도 파일이 나뉘어져 있는 걸 확인할 수 있습니다.


이전에는 index.js 하나에 다 들어있었는데 지금은
react-vendor에는 react-dom만,
query-vendor에는 @tanstack/query
axios-cendor에는 axios,
그리고 index.js로 분리되었습니다.
분석 끝..
'CS' 카테고리의 다른 글
| [JavaScript] 브라우저 렌더링과 DOM (2) | 2026.05.31 |
|---|---|
| 렌더링 최적화: React.memo/useMemo/useCallback (0) | 2026.05.25 |
| [JavaScript] 객체/프로퍼티/프로토타입 (1) | 2026.05.23 |
| [JavaScript] this/클로저 : 코드로 이해하기 (2) | 2026.05.22 |
| Caching과 Prefetching (0) | 2026.05.11 |