Advanced (업데이트: 2026. 6. 2.)

Claude Code로 JavaScript 번들 분석 자동화하기

Vite/Astro/Next 번들 분석, 중복 의존성, dynamic import, CI 예산을 Claude Code로 개선합니다.

Claude Code로 JavaScript 번들 분석 자동화하기

JavaScript 번들은 한 번의 큰 실수로만 무거워지지 않습니다. 차트, 리치 에디터, 날짜 라이브러리, 지도, 동영상 플레이어, 인증 SDK, A/B 테스트 코드가 조금씩 추가되면서 첫 화면에 필요하지 않은 코드까지 사용자에게 전달됩니다. 기능 자체는 필요할 수 있지만, 모든 사용자가 첫 로드에서 그 기능을 받아야 하는 것은 아닙니다.

번들 분석은 프로덕션 빌드 결과물 안에 무엇이 들어 있는지 확인하는 작업입니다. 초보자에게는 “배송 상자의 무게를 재고, 어떤 물건이 무거운지 꺼내 보는 일”이라고 설명할 수 있습니다. Claude Code는 이 과정을 도와줄 수 있지만, “빠르게 해줘”가 아니라 측정, 원인 파악, 수정, 검증, CI 예산까지 명확히 맡겨야 합니다.

이 글은 Vite, Astro, Next.js 계열 프로젝트를 기준으로 rollup-plugin-visualizer, source-map-explorer, 중복 패키지 확인, dynamic import, CI bundle budget, Claude Code review 흐름을 설명합니다. 함께 보면 좋은 글은 코드 스플리팅, tree shaking, 성능 최적화입니다.

기본 흐름

분석에서 자주 보는 값은 raw, gzip, brotli입니다. raw는 압축 전 크기라 treemap을 읽을 때 좋고, gzip과 brotli는 네트워크 전송에 더 가깝습니다. 다만 압축 크기가 작아도 파싱과 실행 비용이 클 수 있으므로 하나의 숫자만 보고 결정하면 위험합니다.

flowchart LR
  A["production build"] --> B["visual report"]
  B --> C["duplicate packages"]
  C --> D["replace or dedupe"]
  B --> E["route-level split"]
  D --> F["bundle budget in CI"]
  E --> F
  F --> G["Claude Code review"]

작업할 때는 공식 문서를 근거로 삼습니다. Vite의 Building for Production, Astro의 Analyze bundle size, Next.js의 Package Bundling, web.dev의 Performance budgets 101, 그리고 Claude Code 공식 문서를 확인합니다.

Vite와 Astro 시각화

Vite에서는 rollup-plugin-visualizer로 시작하는 것이 빠릅니다. HTML treemap을 만들어 어떤 패키지가 큰지 눈으로 확인할 수 있습니다.

npm install -D rollup-plugin-visualizer
// vite.config.ts
import { defineConfig } from "vite";
import { visualizer } from "rollup-plugin-visualizer";

export default defineConfig({
  plugins: [
    visualizer({
      filename: "dist/bundle-stats.html",
      template: "treemap",
      gzipSize: true,
      brotliSize: true,
      open: false
    })
  ],
  build: {
    sourcemap: true,
    rollupOptions: {
      output: {
        manualChunks: {
          react: ["react", "react-dom"],
          charts: ["recharts"],
          editor: ["@tiptap/react", "@tiptap/starter-kit"]
        }
      }
    }
  }
});

Astro에서는 Vite 설정 아래에 둡니다.

// astro.config.mjs
import { defineConfig } from "astro/config";
import { visualizer } from "rollup-plugin-visualizer";

export default defineConfig({
  vite: {
    plugins: [
      visualizer({
        filename: "dist/bundle-stats.html",
        template: "treemap",
        gzipSize: true,
        brotliSize: true
      })
    ],
    build: { sourcemap: true }
  }
});
npm run build
open dist/bundle-stats.html

Windows PowerShell에서는 다음을 씁니다.

start dist/bundle-stats.html

보고서에서 먼저 볼 것은 첫 화면에 필요 없는 큰 덩어리입니다. 관리자용 차트, 리치 에디터, 지도, 동영상 플레이어, Markdown 변환기, 무거운 날짜 처리, 백오피스 전용 UI가 대표적입니다.

source map과 Next.js 분석

source-map-explorer는 생성된 JS와 source map을 사용해 bundle의 출처를 보여줍니다. Vite에서는 sourcemap: true를 켠 뒤 실행합니다.

npm install -D source-map-explorer
npm run build
npx source-map-explorer "dist/assets/*.js" --html dist/source-map-report.html

Next.js에서는 현재 버전과 빌더에 맞는 공식 분석 방법을 먼저 확인합니다. 필요한 경우 @next/bundle-analyzer를 설정합니다.

// next.config.mjs
import bundleAnalyzer from "@next/bundle-analyzer";

const withBundleAnalyzer = bundleAnalyzer({
  enabled: process.env.ANALYZE === "true"
});

export default withBundleAnalyzer({
  reactStrictMode: true
});
npm install -D @next/bundle-analyzer
ANALYZE=true npm run build

PowerShell에서는 환경 변수 문법이 다릅니다.

$env:ANALYZE="true"; npm run build

중복 의존성 찾기

큰 라이브러리는 treemap에서 눈에 띄지만, 중복 패키지는 놓치기 쉽습니다. date-fns가 두 버전으로 들어오거나, lodashlodash-es가 함께 들어오면 실제 사용량보다 큰 코드가 배포됩니다.

npm ls date-fns lodash lodash-es
npm dedupe

pnpm이라면:

pnpm why date-fns
pnpm dedupe

Claude Code에는 이렇게 요청합니다.

이 저장소의 production bundle을 분석해 주세요.
1. dist/bundle-stats.html 또는 source-map-explorer 결과에서 무거운 의존성을 표로 정리
2. npm ls 또는 pnpm why로 중복 패키지 확인
3. replace, dedupe, dynamic import, remove 후보로 분류
4. 기존 UI, SEO 본문, CTA, analytics 이벤트를 유지
5. 최소 변경 후 npm run build와 bundle budget check를 실행
원인대응머지 전 확인
moment가 전역 로드Intl.DateTimeFormat 또는 작은 도구 사용시간대와 언어
lodash 전체 import함수 단위 import 또는 네이티브 APIESM/CommonJS 혼합
관리자 전용 에디터클릭 후 로드loading과 오류 상태
차트가 홈에 포함리포트 화면으로 분리반응형 레이아웃
여러 버전의 의존성dedupe 또는 버전 정렬peer dependency

dynamic import로 첫 로드에서 분리

dynamic import는 코드를 없애는 기능이 아니라 필요한 시점으로 미루는 기능입니다. 관리자 리포트, 에디터, 지도, 가끔 쓰는 모달에 적합합니다.

// src/features/reports/ReportsButton.tsx
import { useState } from "react";

export function ReportsButton() {
  const [html, setHtml] = useState<string>("");
  const [loading, setLoading] = useState(false);

  async function handleClick() {
    setLoading(true);
    const { renderRevenueReport } = await import("./renderRevenueReport");
    setHtml(renderRevenueReport([12000, 18400, 9300]));
    setLoading(false);
  }

  return (
    <section>
      <button type="button" onClick={handleClick} disabled={loading}>
        {loading ? "리포트 생성 중" : "매출 리포트 보기"}
      </button>
      <output aria-live="polite">{html}</output>
    </section>
  );
}
// src/features/reports/renderRevenueReport.ts
export function renderRevenueReport(values: number[]): string {
  const total = values.reduce((sum, value) => sum + value, 0);
  return `이번 달 합계: ${new Intl.NumberFormat("ko-KR").format(total)}원`;
}

Next.js에서는 SEO 본문, 가격, 구매 버튼, 상담 CTA를 서버 렌더링에 남기고 무거운 편집 UI만 지연합니다.

// app/admin/EditorSlot.tsx
"use client";

import dynamic from "next/dynamic";

const RichEditor = dynamic(() => import("./RichEditor"), {
  ssr: false,
  loading: () => <p aria-live="polite">에디터를 불러오는 중...</p>
});

export function EditorSlot() {
  return <RichEditor initialMarkdown="# Draft" />;
}

CI 예산 넣기

한 번 줄인 bundle은 다시 커집니다. 예산을 CI에 넣으면 새 PR이 큰 의존성을 추가할 때 이유를 남기게 됩니다.

// scripts/check-bundle-budget.mjs
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
import path from "node:path";
import { brotliCompressSync, gzipSync } from "node:zlib";

const targetDir = "dist/assets";
const maxTotalGzip = 220 * 1024;
const maxSingleGzip = 140 * 1024;

function walk(dir) {
  return readdirSync(dir, { withFileTypes: true }).flatMap((entry) => {
    const fullPath = path.join(dir, entry.name);
    if (entry.isDirectory()) return walk(fullPath);
    return /\.(js|css)$/.test(entry.name) ? [fullPath] : [];
  });
}

if (!existsSync(targetDir)) {
  console.error(`Missing ${targetDir}. Run npm run build first.`);
  process.exit(1);
}

const rows = walk(targetDir).map((file) => {
  const content = readFileSync(file);
  return {
    file,
    raw: statSync(file).size,
    gzip: gzipSync(content).byteLength,
    brotli: brotliCompressSync(content).byteLength
  };
});

const totalGzip = rows.reduce((sum, row) => sum + row.gzip, 0);
const tooLarge = rows.filter((row) => row.gzip > maxSingleGzip);

if (totalGzip > maxTotalGzip || tooLarge.length > 0) {
  console.error(`Bundle budget failed. total gzip=${totalGzip} bytes`);
  process.exit(1);
}

console.log(`Bundle budget passed. total gzip=${totalGzip} bytes`);
# .github/workflows/bundle-budget.yml
name: Bundle Budget
on: [pull_request]
jobs:
  bundle-budget:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: npm
      - run: npm ci
      - run: npm run build
      - run: node scripts/check-bundle-budget.mjs

처음부터 이상적인 예산을 잡지 말고 현재 gzip 크기에 약 10% 여유를 둡니다. 초과하면 어떤 페이지에 영향이 있고 왜 필요한지 PR에서 설명하게 합니다.

실제 사용 사례

첫째, SaaS 관리자 대시보드입니다. 일반 사용자가 보는 목록, 검색, 주요 CTA는 빠르게 보여 주고, 관리자만 쓰는 차트, 감사 로그, CSV export, 결제 리포트는 별도 chunk로 둡니다.

둘째, 콘텐츠 사이트나 강의 사이트입니다. 본문, 제목, 구매 링크, 상담 CTA는 초기에 렌더링하고, Markdown preview, 이미지 자르기, 캠페인 관리 도구는 늦게 불러옵니다. 수익형 사이트에서는 속도가 광고 노출과 문의 전환에 연결됩니다. 측정은 analytics 구현과 함께 봅니다.

셋째, 지도, 동영상, 계산기, 진단 폼이 있는 랜딩 페이지입니다. 첫 화면에는 제안, 가격, 증거, 행동 버튼을 남기고 무거운 위젯은 스크롤이나 클릭 뒤에 로드합니다. 미디어 UI는 동영상 플레이어, 폼은 접근성 구현도 확인합니다.

넷째, 사내 UI 패키지입니다. @company/ui의 최상위 import로 Button만 썼다고 생각했는데 DatePicker, Modal, Chart, 아이콘 전체가 같이 평가될 수 있습니다. exports를 나누고 CSS나 테마 초기화 같은 side effect를 명확히 표시합니다.

실패 사례와 함정

가장 흔한 실수는 dev build로 판단하는 것입니다. 반드시 production build를 측정해야 합니다. 두 번째는 너무 잘게 쪼개는 것입니다. 작은 chunk가 너무 많으면 요청 수와 대기 시간이 늘어납니다. 세 번째는 source map 공개 정책을 정하지 않는 것입니다. 분석에는 좋지만 CDN에 공개하면 내부 구현이 드러날 수 있습니다. 네 번째는 manualChunks를 고정해 두고 잊어버리는 것입니다. 의존성이 바뀌면 분할 전략도 다시 봐야 합니다.

수정 후에는 Claude Code에 review를 맡깁니다.

이 PR을 bundle 분석 관점에서 review해 주세요.
- 첫 로드 의존성이 정말 필요한가
- 중복 패키지 버전이 있는가
- dynamic import에 loading과 error 상태가 있는가
- SEO 본문, 가격, CTA, analytics 이벤트가 지연 chunk로 들어가지 않았는가
- bundle budget 실패 로그로 원인을 추적할 수 있는가
- npm run build 결과와 report 경로를 요약해 달라

수익 흐름

Bundle 분석은 기술 정리만이 아닙니다. 첫 화면이 빨라지면 본문, 상품 링크, 무료 자료, 상담 CTA가 더 빨리 보입니다. ClaudeCodeLab 같은 사이트에서는 광고 수익, 템플릿 판매, 교육 상담으로 이어지는入口입니다.

개인 학습은 무료 치트시트에서 시작하고, 반복 가능한 프롬프트와 템플릿은 제품 목록에서 확인할 수 있습니다. 팀에서 Vite, Astro, Next.js, CLAUDE.md, CI 예산, review 규칙을 함께 정리하려면 Claude Code 교육 및 상담이 적합합니다.

직접 확인한 결과

이 글의 예시는 Vite/React 스타일 구성을 기준으로 확인했습니다. visualizer 설정은 HTML 보고서를 만들고, source-map-explorer는 source map이 필요하며, Node 예산 스크립트는 내장 fs, path, zlib만 사용합니다. 실제 작업에서 가장 중요했던 점은 모든 큰 패키지를 없애는 것이 아니라 첫 화면에 꼭 필요한 것과 나중에 읽어도 되는 것을 분리하는 것이었습니다.

정리

Claude Code로 bundle을 줄이려면 측정이 먼저입니다. production build, 시각화, 중복 의존성 확인, dynamic import, CI 예산, Claude Code review를 하나의 흐름으로 운영하세요. Bundle analysis, tree shaking, code splitting은 따로 떨어진 작업이 아니라 트래픽과 수익이 늘어도 앱을 빠르게 유지하기 위한 같은 운영 루프입니다.

#Claude Code #bundle analysis #Webpack #Vite #performance
무료

무료 PDF: Claude Code 치트시트

이메일을 입력하면 명령, 리뷰 습관, 안전한 워크플로를 정리한 PDF를 받을 수 있습니다.

개인정보를 안전하게 관리하며 스팸을 보내지 않습니다.

Masa

작성자 소개

Masa

Claude Code 실무 워크플로와 팀 도입을 검증하는 엔지니어입니다.