빌드 최적화
Elftia의 메인 번들은 1,812 KB에서 759 KB로 최적화되었습니다(-58%). 이 문서는 회귀를 방지하기 위한 최적화 전략과 기준을 기록합니다.
번들 크기 제한
절대 제한 (반드시 준수)
| 지표 | 제한 | 현재 | 확인 방법 |
|---|---|---|---|
| 단일 청크 최대 크기 | < 1 MB | 759 KB | npm run verify:build |
| 메인 번들 (gzip 압축) | < 300 KB | 221 KB | npm run verify:build |
| Vite 경고 수 | 0 | 0 | 빌드 출력 |
권장 목표
| 지표 | 목표 | 현재 | 우선순위 |
|---|---|---|---|
| 메인 번들 (비압축) | < 500 KB | 759 KB | P1 |
| 전체 번들 | < 3 MB | 6.24 MB | P2 |
| 첫 화면 로딩 | < 2s | ~2s | P0 |
확인 명령어
# 빠른 검증 (4가지 핵심 지표)
npm run verify:build
# 상세 분석 (청크 분류 통계)
npm run analyze:build
# 시각적 분석 (stats.html 생성)
npm run build:renderer
# dist/stats.html 열기
코드 분할 전략
라우트 수준 코드 분할 (필수)
모든 페이지 컴포넌트는 지연 로딩을 위해 React.lazy를 사용해야 합니다. lazy/는 용도별로 구성된 디렉터리입니다(pages.tsx / workspaces.tsx / inline.tsx / shell.tsx / skeletons.tsx / with-suspense.tsx / index.ts 배럴). 새 페이지를 추가할 때는 lazy/pages.tsx를 수정하세요:
// packages/renderer/src/app/lazy/pages.tsx
export const LazySettingsPage = lazy(() => import('../Settings'));
export const SuspenseSettings = withSuspense(LazySettingsPage, PageSkeleton);
// packages/renderer/src/app/App.tsx — 기존 import 경로도 유효 (배럴 재내보내기)
import { SuspenseSettings as Settings } from './components/lazy';
<Route path="/settings" element={<Settings />} />
// 이렇게 하지 마세요: 페이지 컴포넌트를 직접 import
import Settings from './components/Settings';
<Route path="/settings" element={<Settings />} />
대형 컴포넌트 지연 로딩 (권장)
단일 컴포넌트가 100 KB를 초과하거나 대형 서드파티 라이브러리에 의존하는 경우 지연 로딩을 사용하세요.
이미 지연 로딩이 적용된 컴포넌트:
| 컴포넌트 | 크기 | 의존성 |
|---|---|---|
MermaidDiagram | 451 KB | mermaid |
HtmlPreviewPanel | 198 KB | iframe sandbox |
CodeEditor | 34 KB | CodeMirror |
Shell / StandaloneShell | 9 KB | xterm |
적용 기준:
| 조건 | 지연 로딩 적용? |
|---|---|
| 컴포넌트 번들 > 100 KB | 예 |
| 대형 서드파티 라이브러리 의존 | 예 |
| 첫 화면에 불필요 | 예 |
| 사용 빈도가 낮음 | 예 |
| 소형 공통 컴포넌트 (< 10 KB) | 아니요 |
| 첫 화면에 필요한 핵심 컴포넌트 | 아니요 |
| 자주 토글되는 UI 컴포넌트 | 아니요 |
Context 최적화
전역적으로 필요하지 않은 Context는 페이지 수준 컴포넌트로 이동하세요:
// 좋은 예: 페이지 수준 Context
export function Settings() {
return (
<SettingsProvider>
<SettingsContent />
</SettingsProvider>
);
}
// 나쁜 예: 불필요한 Context를 전역으로 로드
<GlobalContext>
<Routes />
</GlobalContext>
경고
Context를 이동하기 전에 반드시 의존성을 분석하여 페이지 간 상태 공유가 깨지지 않는지 확인하세요.
Vendor 청크 구성
현재 전략은 업데이트 빈도와 사용 빈도를 기준으로 그룹화합니다:
// vite.config.js — manualChunks
{
'vendor-react': ['react', 'react-dom', 'react-router-dom'],
'vendor-ui': ['@radix-ui/react-context-menu', '@radix-ui/react-dialog', ...],
'vendor-icons': ['lucide-react'],
'vendor-utils': ['clsx', 'tailwind-merge', 'class-variance-authority', 'zustand'],
'vendor-markdown': ['react-markdown', 'remark-gfm', 'rehype-highlight'],
'vendor-codemirror': ['@codemirror/state', '@codemirror/view', ...],
'vendor-xterm': ['@xterm/xterm', '@xterm/addon-fit', '@xterm/addon-web-links'],
}
그룹화 원칙
| 유형 | 업데이트 빈도 | 캐시 우선순위 | 예시 |
|---|---|---|---|
| 핵심 프레임워크 | 낮음 | 최고 | React, React DOM |
| 대형 서드파티 라이브러리 | 낮음 | 높음 | CodeMirror, xterm |
| UI 컴포넌트 라이브러리 | 중간 | 중간 | Radix UI, lucide |
| 유틸리티 라이브러리 | 중간 | 중간 | clsx, zustand |
새 의존성 추가 시 확인
새 서드파티 의존성을 추가할 때:
# 1. 크기 확인
npm info <package> dist.unpackedSize
# 2. 100 KB 초과 시 vendor 청크에 추가
# 3. 업데이트 빈도가 높으면 별도 vendor 청크 생성
# 4. tree shaking 지원 여부 확인
# 5. npm run analyze:build로 영향도 확인
Tree Shaking
올바른 import 방법
// 좋은 예: named import (tree shaking 지원)
import { Button, Input, Select } from '@/components/ui';
import { Home, Settings, User } from 'lucide-react';
// 좋은 예: Radix UI 네임스페이스 import (공식 권장)
import * as Dialog from '@radix-ui/react-dialog';
// 나쁜 예: 라이브러리 전체 import
import * as UI from '@/components/ui';
import * as Icons from 'lucide-react';
미사용 import 확인
npm run typecheck # TypeScript로 미사용 import 감지
npm run lint:eslint # ESLint로 미사용 변수 경고
Vite 프로덕션 빌드 구성
// vite.config.js — 핵심 구성
build: {
minify: 'terser', // terser가 esbuild보다 압축률 우수
sourcemap: false, // 프로덕션에서 sourcemap 비활성화
chunkSizeWarningLimit: 500, // 청크 크기 경고 임계값 (KB)
terserOptions: {
compress: {
drop_console: true, // console.log 제거 (warn/error는 유지)
drop_debugger: true, // debugger 제거
},
},
}
일반적인 문제와 해결 방법
메인 번들이 너무 큼 (> 500 KB)
조사 절차:
npm run build:renderer실행 후dist/stats.html열기- 메인 번들 구성 확인, 가장 큰 모듈 찾기
- 대형 컴포넌트에 지연 로딩이 빠져 있는지 확인
- 불필요한 전역 import 확인
해결 방법:
- 대형 컴포넌트를 지연 로딩으로 전환
- 미사용 import 제거
- 페이지 컴포넌트를 라우트 수준 지연 로딩으로 전환
Vendor 청크가 너무 큼 (> 500 KB)
해결 방법:
- 대형 vendor를 더 작은 청크로 분할
- 업데이트 빈도에 따라 재그룹화
- 중복 의존성 확인
전체 번들이 너무 큼 (> 5 MB)
해결 방법:
- 차트 라이브러리 지연 로딩 (Mermaid, Cytoscape 등)
- 사용 빈도가 낮은 기능 또는 의존성 제거
- 더 가벼운 대체 라이브러리 고려
코드 분할 실패
조사 절차:
lazy/디렉터리에서 관련 분할 파일의 지연 로딩 구성 확인 (페이지 수준은lazy/pages.tsx, 워크스페이스 본문은lazy/workspaces.tsx, 인라인 채팅 컴포넌트는lazy/inline.tsx)- 지연 로딩된 컴포넌트가
App.tsx에서 사용되고 있는지 확인 - Vite 구성의
manualChunks확인
커밋 전 체크리스트
새 페이지 추가 시
- 페이지 컴포넌트를
lazy/pages.tsx(또는 해당 분할 파일)에 추가 -
withSuspense로 래핑하고 fallback 제공 -
App.tsx에서 지연 로딩 버전 사용 -
npm run build:renderer실행하여 별도 청크 생성 확인
대형 컴포넌트 추가 시
- 컴포넌트 크기 > 100 KB → 지연 로딩 사용
- 대형 서드파티 라이브러리 의존 → 지연 로딩 사용
- 적절한 로딩 상태 제공
서드파티 의존성 추가 시
- 의존성 크기 확인
- 의존성 > 100 KB → vendor 청크에 추가
- 업데이트 빈도가 높음 → 별도 vendor 청크
- tree shaking 지원 → named import 사용
-
npm run analyze:build실행하여 영향도 확인
지속적 최적화 제안
매 릴리스 전
npm run verify:build
npm run build:renderer | grep -i "warning"
# 메인 번들이 50 KB 이상 증가했다면 원인 조사
매월
npm run build:renderer
# dist/stats.html을 열어 최적화 가능한 모듈 확인
npm outdated
npm update
분기별
- 모든 vendor 청크 구성 검토
- 새로운 최적화 전략 평가
- 사용 빈도가 낮은 기능 제거 여부 평가
- 이 기준 문서 업데이트
CI/CD 연동
CI에 번들 크기 검사를 추가할 것을 권장합니다:
- name: Build and verify
run: |
npm run build:renderer
npm run verify:build || echo "Warning: Bundle size check failed"
성능 지표 기준선
| 지표 | 기준선 | 목표 | 모니터링 |
|---|---|---|---|
| 메인 번들 | 759 KB | < 500 KB | verify:build |
| 첫 화면 로딩 | ~2s | < 2s | Lighthouse |
| 전체 번들 | 6.24 MB | < 3 MB | analyze:build |
| 청크 수 | 76 | - | analyze:build |