@kood/claude-code 0.6.6 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +7 -1
- package/package.json +1 -1
- package/templates/.claude/agents/analyst.md +5 -0
- package/templates/.claude/agents/architect.md +5 -0
- package/templates/.claude/agents/build-fixer.md +1 -0
- package/templates/.claude/agents/code-reviewer.md +1 -0
- package/templates/.claude/agents/critic.md +4 -0
- package/templates/.claude/agents/deep-executor.md +1 -0
- package/templates/.claude/agents/dependency-manager.md +2 -0
- package/templates/.claude/agents/deployment-validator.md +2 -0
- package/templates/.claude/agents/designer.md +2 -0
- package/templates/.claude/agents/document-writer.md +3 -0
- package/templates/.claude/agents/explore.md +1 -0
- package/templates/.claude/agents/git-operator.md +2 -0
- package/templates/.claude/agents/implementation-executor.md +2 -0
- package/templates/.claude/agents/ko-to-en-translator.md +3 -0
- package/templates/.claude/agents/lint-fixer.md +2 -0
- package/templates/.claude/agents/planner.md +3 -0
- package/templates/.claude/agents/pm.md +349 -0
- package/templates/.claude/agents/qa-tester.md +1 -0
- package/templates/.claude/agents/refactor-advisor.md +4 -0
- package/templates/.claude/agents/researcher.md +9 -1
- package/templates/.claude/agents/scientist.md +1 -0
- package/templates/.claude/agents/security-reviewer.md +1 -0
- package/templates/.claude/agents/tdd-guide.md +1 -0
- package/templates/.claude/agents/vision.md +1 -0
- package/templates/.claude/instructions/agent-patterns/agent-teams-usage.md +376 -0
- package/templates/.claude/instructions/sourcing/reliable-search.md +49 -2
- package/templates/.claude/scripts/agent-teams/check-availability.sh +238 -0
- package/templates/.claude/scripts/agent-teams/setup-tmux.sh +125 -0
- package/templates/.claude/skills/agent-teams-setup/SKILL.md +460 -0
- package/templates/.claude/skills/brainstorm/SKILL.md +1 -0
- package/templates/.claude/skills/bug-fix/SKILL.md +1 -0
- package/templates/.claude/skills/crawler/SKILL.md +2 -0
- package/templates/.claude/skills/docs-creator/SKILL.md +1 -0
- package/templates/.claude/skills/docs-fetch/SKILL.md +6 -4
- package/templates/.claude/skills/docs-refactor/SKILL.md +1 -0
- package/templates/.claude/skills/elon-musk/SKILL.md +1 -0
- package/templates/.claude/skills/execute/SKILL.md +1 -0
- package/templates/.claude/skills/feedback/SKILL.md +1 -0
- package/templates/.claude/skills/figma-to-code/SKILL.md +1 -0
- package/templates/.claude/skills/genius-thinking/SKILL.md +1 -0
- package/templates/.claude/skills/global-uiux-design/SKILL.md +1 -0
- package/templates/.claude/skills/korea-uiux-design/SKILL.md +1 -0
- package/templates/.claude/skills/nextjs-react-best-practices/SKILL.md +1 -0
- package/templates/.claude/skills/plan/SKILL.md +1 -0
- package/templates/.claude/skills/prd/SKILL.md +1 -0
- package/templates/.claude/skills/project-optimizer/AGENTS.md +275 -0
- package/templates/.claude/skills/project-optimizer/SKILL.md +375 -0
- package/templates/.claude/skills/project-optimizer/rules/arch-config-centralize.md +66 -0
- package/templates/.claude/skills/project-optimizer/rules/arch-hot-path.md +35 -0
- package/templates/.claude/skills/project-optimizer/rules/arch-interface-segregation.md +51 -0
- package/templates/.claude/skills/project-optimizer/rules/arch-module-boundary.md +42 -0
- package/templates/.claude/skills/project-optimizer/rules/build-cache.md +57 -0
- package/templates/.claude/skills/project-optimizer/rules/build-code-split.md +56 -0
- package/templates/.claude/skills/project-optimizer/rules/build-incremental.md +65 -0
- package/templates/.claude/skills/project-optimizer/rules/build-minify.md +61 -0
- package/templates/.claude/skills/project-optimizer/rules/build-tree-shake.md +60 -0
- package/templates/.claude/skills/project-optimizer/rules/code-complexity.md +65 -0
- package/templates/.claude/skills/project-optimizer/rules/code-dead-elimination.md +32 -0
- package/templates/.claude/skills/project-optimizer/rules/code-duplication.md +54 -0
- package/templates/.claude/skills/project-optimizer/rules/code-error-handling.md +75 -0
- package/templates/.claude/skills/project-optimizer/rules/code-naming.md +52 -0
- package/templates/.claude/skills/project-optimizer/rules/concurrency-defer-await.md +54 -0
- package/templates/.claude/skills/project-optimizer/rules/concurrency-parallel.md +90 -0
- package/templates/.claude/skills/project-optimizer/rules/concurrency-pipeline.md +68 -0
- package/templates/.claude/skills/project-optimizer/rules/concurrency-pool.md +68 -0
- package/templates/.claude/skills/project-optimizer/rules/deps-lightweight-alt.md +37 -0
- package/templates/.claude/skills/project-optimizer/rules/deps-peer-align.md +44 -0
- package/templates/.claude/skills/project-optimizer/rules/deps-security-audit.md +45 -0
- package/templates/.claude/skills/project-optimizer/rules/deps-unused-removal.md +25 -0
- package/templates/.claude/skills/project-optimizer/rules/deps-version-pin.md +40 -0
- package/templates/.claude/skills/project-optimizer/rules/dx-ci-speed.md +47 -0
- package/templates/.claude/skills/project-optimizer/rules/dx-dev-server.md +35 -0
- package/templates/.claude/skills/project-optimizer/rules/dx-lint-config.md +36 -0
- package/templates/.claude/skills/project-optimizer/rules/dx-test-coverage.md +34 -0
- package/templates/.claude/skills/project-optimizer/rules/dx-type-safety.md +49 -0
- package/templates/.claude/skills/project-optimizer/rules/io-batch-queries.md +67 -0
- package/templates/.claude/skills/project-optimizer/rules/io-cache-layer.md +67 -0
- package/templates/.claude/skills/project-optimizer/rules/io-connection-reuse.md +67 -0
- package/templates/.claude/skills/project-optimizer/rules/io-serialize-minimal.md +61 -0
- package/templates/.claude/skills/project-optimizer/rules/io-stream.md +75 -0
- package/templates/.claude/skills/project-optimizer/rules/memory-bounded-cache.md +65 -0
- package/templates/.claude/skills/project-optimizer/rules/memory-large-data.md +64 -0
- package/templates/.claude/skills/project-optimizer/rules/memory-lazy-init.md +78 -0
- package/templates/.claude/skills/project-optimizer/rules/memory-leak-prevention.md +79 -0
- package/templates/.claude/skills/project-optimizer/rules/memory-pool-reuse.md +70 -0
- package/templates/.claude/skills/ralph/SKILL.md +1 -0
- package/templates/.claude/skills/refactor/SKILL.md +1 -0
- package/templates/.claude/skills/research/SKILL.md +1 -0
- package/templates/.claude/skills/sql-optimizer/SKILL.md +438 -0
- package/templates/.claude/skills/sql-optimizer/orm-patterns.md +218 -0
- package/templates/.claude/skills/startup-validator/SKILL.md +1 -0
- package/templates/.claude/skills/tanstack-start-react-best-practices/AGENTS.md +53 -14
- package/templates/.claude/skills/tanstack-start-react-best-practices/SKILL.md +94 -27
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/bundle-defer-third-party.md +42 -19
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/client-optimistic-updates.md +109 -0
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/client-suspense-query.md +74 -0
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/client-use-hook.md +81 -0
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/rerender-react-compiler.md +81 -0
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/routing-beforeload-auth.md +121 -0
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/routing-file-conventions.md +104 -0
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/routing-link-navigation.md +119 -0
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/routing-nested-layouts.md +155 -0
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/routing-path-params.md +89 -0
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/routing-pending-component.md +110 -0
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/routing-preload-strategy.md +91 -0
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/routing-router-context.md +120 -0
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/routing-search-params.md +114 -0
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/server-deferred-data.md +1 -1
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/server-error-boundaries.md +79 -0
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/server-middleware.md +85 -0
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/server-serialization.md +56 -21
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/server-streaming.md +84 -0
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/server-validator.md +71 -0
- package/templates/.claude/skills/tauri-react-best-practices/AGENTS.md +527 -0
- package/templates/.claude/skills/tauri-react-best-practices/SKILL.md +571 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/bundle-barrel-imports.md +140 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/bundle-cargo-profile.md +96 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/bundle-frontend-treeshake.md +242 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/bundle-lazy-components.md +255 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/bundle-remove-unused-commands.md +160 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/deploy-ci-pipeline.md +269 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/deploy-signing.md +207 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/deploy-updater.md +226 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/ipc-async-commands.md +172 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/ipc-batch-commands.md +133 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/ipc-binary-response.md +198 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/ipc-channel-streaming.md +186 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/ipc-error-handling.md +250 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/ipc-type-safe.md +227 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/perf-derived-state.md +231 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/perf-functional-setstate.md +191 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/perf-index-maps.md +276 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/perf-lazy-state-init.md +196 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/plugin-lifecycle.md +265 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/plugin-mobile-compat.md +199 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/plugin-permission-scope.md +193 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/react-error-boundary.md +239 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/react-event-listener.md +151 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/react-file-src.md +155 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/react-invoke-hook.md +139 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/react-optimistic-update.md +211 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/security-capability-split.md +205 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/security-csp.md +207 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/security-least-privilege.md +106 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/security-no-wildcard.md +253 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/security-scope-paths.md +160 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/state-async-mutex.md +270 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/state-mutex-pattern.md +265 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/state-react-sync.md +375 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/state-single-container.md +275 -0
- package/templates/tanstack-start/docs/architecture.md +238 -167
- package/templates/tanstack-start/docs/library/tanstack-router/error-handling.md +777 -38
- package/templates/tanstack-start/docs/library/tanstack-router/hooks.md +549 -37
- package/templates/tanstack-start/docs/library/tanstack-router/index.md +895 -111
- package/templates/tanstack-start/docs/library/tanstack-router/navigation.md +641 -43
- package/templates/tanstack-start/docs/library/tanstack-router/route-context.md +889 -38
- package/templates/tanstack-start/docs/library/tanstack-router/search-params.md +891 -29
- package/templates/tanstack-start/docs/library/tanstack-start/auth-patterns.md +972 -36
- package/templates/tanstack-start/docs/library/tanstack-start/index.md +1525 -881
- package/templates/tanstack-start/docs/library/tanstack-start/middleware.md +1099 -20
- package/templates/tanstack-start/docs/library/tanstack-start/routing.md +796 -30
- package/templates/tanstack-start/docs/library/tanstack-start/server-functions.md +953 -35
- package/templates/tanstack-start/docs/library/tanstack-start/setup.md +371 -15
- package/templates/tauri/CLAUDE.md +189 -0
- package/templates/tauri/docs/guides/distribution.md +261 -0
- package/templates/tauri/docs/guides/getting-started.md +302 -0
- package/templates/tauri/docs/guides/mobile.md +288 -0
- package/templates/tauri/docs/library/tauri/index.md +510 -0
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Cargo Release Profile Optimization
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: reduces binary size by 30-50%
|
|
5
|
+
tags: bundle, cargo, optimization, release, size
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Cargo Release 프로필 최적화
|
|
9
|
+
|
|
10
|
+
## 왜 중요한가
|
|
11
|
+
|
|
12
|
+
Tauri 앱의 최종 바이너리 크기는 사용자의 다운로드 시간, 설치 크기, 메모리 사용량에 직접 영향을 미칩니다. Cargo의 기본 release 설정은 빠른 컴파일을 위해 최적화되지만, 더 공격적인 최적화 옵션을 적용하면 바이너리 크기를 **30-50%까지 줄일 수 있습니다**.
|
|
13
|
+
|
|
14
|
+
## ❌ 잘못된 패턴
|
|
15
|
+
|
|
16
|
+
**기본 release 프로필 사용 (Cargo.toml):**
|
|
17
|
+
|
|
18
|
+
```toml
|
|
19
|
+
# 기본 설정 (최적화 안함)
|
|
20
|
+
[package]
|
|
21
|
+
name = "app"
|
|
22
|
+
version = "0.1.0"
|
|
23
|
+
|
|
24
|
+
# release 프로필 설정 없음
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
**결과:**
|
|
28
|
+
- 바이너리 크기: ~15MB (최적화 전)
|
|
29
|
+
- 컴파일 시간: 빠름
|
|
30
|
+
- 디버그 심볼 포함
|
|
31
|
+
|
|
32
|
+
## ✅ 올바른 패턴
|
|
33
|
+
|
|
34
|
+
**최적화된 release 프로필 (Cargo.toml):**
|
|
35
|
+
|
|
36
|
+
```toml
|
|
37
|
+
[package]
|
|
38
|
+
name = "app"
|
|
39
|
+
version = "0.1.0"
|
|
40
|
+
|
|
41
|
+
[profile.release]
|
|
42
|
+
# 코드 생성 유닛을 1개로 통합 (LLVM 최적화 극대화)
|
|
43
|
+
codegen-units = 1
|
|
44
|
+
|
|
45
|
+
# Link-Time Optimization 활성화 (전체 프로그램 최적화)
|
|
46
|
+
lto = true
|
|
47
|
+
|
|
48
|
+
# 크기 최적화 모드 ("s" = size, "z" = aggressive size)
|
|
49
|
+
opt-level = "s"
|
|
50
|
+
|
|
51
|
+
# panic을 abort로 변경 (unwinding 코드 제거)
|
|
52
|
+
panic = "abort"
|
|
53
|
+
|
|
54
|
+
# 디버그 심볼 제거
|
|
55
|
+
strip = true
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
**각 옵션 설명:**
|
|
59
|
+
|
|
60
|
+
| 옵션 | 설명 | 크기 영향 |
|
|
61
|
+
|------|------|-----------|
|
|
62
|
+
| `codegen-units = 1` | 병렬 컴파일 비활성화, LLVM 최적화 극대화 | ~10-15% 감소 |
|
|
63
|
+
| `lto = true` | 전체 프로그램 최적화 (Link-Time Optimization) | ~15-20% 감소 |
|
|
64
|
+
| `opt-level = "s"` | 속도 대신 크기 최적화 | ~10-15% 감소 |
|
|
65
|
+
| `panic = "abort"` | panic unwinding 코드 제거 | ~5-10% 감소 |
|
|
66
|
+
| `strip = true` | 디버그 심볼 제거 | ~10-20% 감소 |
|
|
67
|
+
|
|
68
|
+
**예상 결과:**
|
|
69
|
+
- 바이너리 크기: ~7-10MB (최적화 후)
|
|
70
|
+
- 컴파일 시간: 느림 (2-3배)
|
|
71
|
+
- 프로덕션 배포용
|
|
72
|
+
|
|
73
|
+
## 추가 컨텍스트
|
|
74
|
+
|
|
75
|
+
**컴파일 시간 vs 크기:**
|
|
76
|
+
- 개발 중에는 기본 설정 사용 (빠른 반복)
|
|
77
|
+
- CI/CD 프로덕션 빌드에서만 최적화 프로필 사용
|
|
78
|
+
|
|
79
|
+
**opt-level 옵션:**
|
|
80
|
+
- `"s"` (size): 크기 최적화, 합리적인 성능 유지
|
|
81
|
+
- `"z"` (aggressive size): 극단적 크기 최적화, 일부 성능 희생
|
|
82
|
+
- 대부분의 앱에서는 `"s"` 권장
|
|
83
|
+
|
|
84
|
+
**LTO 옵션:**
|
|
85
|
+
- `lto = true`: 전체 최적화 (가장 느림, 가장 작음)
|
|
86
|
+
- `lto = "thin"`: 부분 최적화 (빠름, 적당한 크기)
|
|
87
|
+
- 프로덕션에서는 `lto = true` 권장
|
|
88
|
+
|
|
89
|
+
**참고:**
|
|
90
|
+
- Tauri 공식 가이드: [App Size Optimization](https://tauri.app/v2/guides/building/app-size/)
|
|
91
|
+
- Rust Cargo Book: [Profiles](https://doc.rust-lang.org/cargo/reference/profiles.html)
|
|
92
|
+
|
|
93
|
+
**영향도:**
|
|
94
|
+
- 크기: HIGH (30-50% 감소)
|
|
95
|
+
- 컴파일 시간: HIGH (2-3배 증가)
|
|
96
|
+
- 런타임 성능: NEUTRAL (opt-level="s"일 때)
|
package/templates/.claude/skills/tauri-react-best-practices/rules/bundle-frontend-treeshake.md
ADDED
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Frontend Tree-Shaking Verification
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: removes unused code from bundle
|
|
5
|
+
tags: bundle, tree-shaking, vite, optimization, frontend
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# 프론트엔드 Tree-Shaking 확인
|
|
9
|
+
|
|
10
|
+
## 왜 중요한가
|
|
11
|
+
|
|
12
|
+
Tree-shaking은 **사용하지 않는 코드를 번들에서 자동으로 제거**하는 최적화 기법입니다. 하지만 **side effect가 있는 import**나 **default export**를 사용하면 tree-shaking이 작동하지 않아 불필요한 코드가 번들에 포함됩니다.
|
|
13
|
+
|
|
14
|
+
## ❌ 잘못된 패턴
|
|
15
|
+
|
|
16
|
+
**Side effect가 있는 import (전체 모듈 번들링):**
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
// ❌ lodash 전체 (~500KB)가 번들에 포함
|
|
20
|
+
import _ from 'lodash'
|
|
21
|
+
|
|
22
|
+
function App() {
|
|
23
|
+
const result = _.chunk([1, 2, 3, 4], 2)
|
|
24
|
+
// chunk 함수만 사용하지만 전체 라이브러리 포함
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
**Default export 사용 (tree-shaking 불가):**
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
// ❌ utils.ts
|
|
32
|
+
export default {
|
|
33
|
+
add: (a: number, b: number) => a + b,
|
|
34
|
+
subtract: (a: number, b: number) => a - b,
|
|
35
|
+
multiply: (a: number, b: number) => a * b,
|
|
36
|
+
divide: (a: number, b: number) => a / b
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// App.tsx
|
|
40
|
+
import utils from './utils'
|
|
41
|
+
|
|
42
|
+
function App() {
|
|
43
|
+
const sum = utils.add(1, 2) // add만 사용해도 전체 객체 포함
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
**package.json에 sideEffects 설정 없음:**
|
|
48
|
+
|
|
49
|
+
```json
|
|
50
|
+
{
|
|
51
|
+
"name": "my-app",
|
|
52
|
+
"version": "1.0.0"
|
|
53
|
+
// sideEffects 설정 없음 - bundler가 안전하게 제거 못함
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## ✅ 올바른 패턴
|
|
58
|
+
|
|
59
|
+
**Named export + 직접 import로 tree-shaking 활성화:**
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
// ✅ 필요한 함수만 번들에 포함 (~5KB)
|
|
63
|
+
import { chunk } from 'lodash-es'
|
|
64
|
+
|
|
65
|
+
function App() {
|
|
66
|
+
const result = chunk([1, 2, 3, 4], 2)
|
|
67
|
+
// chunk 함수만 번들에 포함
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**Named export 사용 (tree-shaking 가능):**
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
// ✅ utils.ts
|
|
75
|
+
export const add = (a: number, b: number) => a + b
|
|
76
|
+
export const subtract = (a: number, b: number) => a - b
|
|
77
|
+
export const multiply = (a: number, b: number) => a * b
|
|
78
|
+
export const divide = (a: number, b: number) => a / b
|
|
79
|
+
|
|
80
|
+
// App.tsx
|
|
81
|
+
import { add } from './utils'
|
|
82
|
+
|
|
83
|
+
function App() {
|
|
84
|
+
const sum = add(1, 2) // add만 번들에 포함
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
**package.json에 sideEffects 명시:**
|
|
89
|
+
|
|
90
|
+
```json
|
|
91
|
+
{
|
|
92
|
+
"name": "my-app",
|
|
93
|
+
"version": "1.0.0",
|
|
94
|
+
"sideEffects": false
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**CSS import가 있는 경우:**
|
|
99
|
+
|
|
100
|
+
```json
|
|
101
|
+
{
|
|
102
|
+
"name": "my-app",
|
|
103
|
+
"version": "1.0.0",
|
|
104
|
+
"sideEffects": [
|
|
105
|
+
"*.css",
|
|
106
|
+
"*.scss"
|
|
107
|
+
]
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
**Vite 설정으로 tree-shaking 강화:**
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
// vite.config.ts
|
|
115
|
+
import { defineConfig } from 'vite'
|
|
116
|
+
|
|
117
|
+
export default defineConfig({
|
|
118
|
+
build: {
|
|
119
|
+
// minify를 활성화하여 dead code 제거
|
|
120
|
+
minify: 'terser',
|
|
121
|
+
terserOptions: {
|
|
122
|
+
compress: {
|
|
123
|
+
// 사용하지 않는 코드 제거
|
|
124
|
+
dead_code: true,
|
|
125
|
+
drop_console: true, // console.log 제거
|
|
126
|
+
drop_debugger: true, // debugger 제거
|
|
127
|
+
pure_funcs: ['console.log', 'console.info'] // 특정 함수 제거
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
rollupOptions: {
|
|
131
|
+
treeshake: {
|
|
132
|
+
// aggressive tree-shaking
|
|
133
|
+
moduleSideEffects: 'no-external',
|
|
134
|
+
preset: 'smallest'
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
})
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
**번들 분석으로 tree-shaking 확인:**
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
# Vite Bundle Visualizer 설치
|
|
145
|
+
npm install -D rollup-plugin-visualizer
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
// vite.config.ts
|
|
150
|
+
import { visualizer } from 'rollup-plugin-visualizer'
|
|
151
|
+
|
|
152
|
+
export default defineConfig({
|
|
153
|
+
plugins: [
|
|
154
|
+
visualizer({
|
|
155
|
+
open: true,
|
|
156
|
+
gzipSize: true,
|
|
157
|
+
brotliSize: true,
|
|
158
|
+
template: 'treemap' // or 'sunburst', 'network'
|
|
159
|
+
})
|
|
160
|
+
]
|
|
161
|
+
})
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
# 빌드 후 자동으로 stats.html 열림
|
|
166
|
+
npm run build
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## 추가 컨텍스트
|
|
170
|
+
|
|
171
|
+
**Tree-shaking 작동 조건:**
|
|
172
|
+
|
|
173
|
+
| 조건 | 설명 |
|
|
174
|
+
|------|------|
|
|
175
|
+
| **ESM** | ES Module (import/export) 사용 필수 |
|
|
176
|
+
| **Named Export** | default export 대신 named export |
|
|
177
|
+
| **No Side Effects** | package.json에 sideEffects: false |
|
|
178
|
+
| **Static Analysis** | 동적 import는 tree-shaking 불가 |
|
|
179
|
+
|
|
180
|
+
**라이브러리별 tree-shaking 지원:**
|
|
181
|
+
|
|
182
|
+
| 라이브러리 | CommonJS | ESM | Tree-shakable | 대안 |
|
|
183
|
+
|-----------|----------|-----|---------------|------|
|
|
184
|
+
| `lodash` | ✅ | ❌ | ❌ | `lodash-es` 사용 |
|
|
185
|
+
| `lodash-es` | ❌ | ✅ | ✅ | - |
|
|
186
|
+
| `moment` | ✅ | ❌ | ❌ | `date-fns` 또는 `dayjs` |
|
|
187
|
+
| `date-fns` | ❌ | ✅ | ✅ | - |
|
|
188
|
+
| `axios` | ✅ | ✅ | ⚠️ | 전체 번들 (~20KB) |
|
|
189
|
+
| `ky` | ❌ | ✅ | ✅ | axios 대안 (~5KB) |
|
|
190
|
+
|
|
191
|
+
**Side effect가 있는 코드 예시:**
|
|
192
|
+
|
|
193
|
+
```typescript
|
|
194
|
+
// ❌ Side effect 있음 - tree-shaking 불가
|
|
195
|
+
export const API_URL = 'https://api.example.com'
|
|
196
|
+
console.log('API initialized') // 부수 효과!
|
|
197
|
+
|
|
198
|
+
export function fetchData() {
|
|
199
|
+
return fetch(API_URL)
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
```typescript
|
|
204
|
+
// ✅ Side effect 없음 - tree-shaking 가능
|
|
205
|
+
export const API_URL = 'https://api.example.com'
|
|
206
|
+
|
|
207
|
+
export function fetchData() {
|
|
208
|
+
return fetch(API_URL)
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
**동적 import vs 정적 import:**
|
|
213
|
+
|
|
214
|
+
```typescript
|
|
215
|
+
// ✅ 정적 import - tree-shaking 가능
|
|
216
|
+
import { chunk } from 'lodash-es'
|
|
217
|
+
|
|
218
|
+
// ❌ 동적 import - tree-shaking 불가
|
|
219
|
+
const { chunk } = await import('lodash-es')
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
**Rollup 플러그인 확인 (Vite 내부 사용):**
|
|
223
|
+
|
|
224
|
+
```bash
|
|
225
|
+
# Vite는 내부적으로 Rollup 사용
|
|
226
|
+
# tree-shaking은 Rollup의 기본 기능
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
**효과:**
|
|
230
|
+
- lodash → lodash-es: 500KB → 5KB (100배 감소)
|
|
231
|
+
- moment → date-fns: 300KB → 20KB (15배 감소)
|
|
232
|
+
- 전체 프로젝트: 평균 10-30% 번들 크기 감소
|
|
233
|
+
|
|
234
|
+
**참고:**
|
|
235
|
+
- Vite Tree-shaking: [Build Optimizations](https://vitejs.dev/guide/features.html#build-optimizations)
|
|
236
|
+
- Webpack Tree-shaking: [Documentation](https://webpack.js.org/guides/tree-shaking/)
|
|
237
|
+
- Rollup Tree-shaking: [Guide](https://rollupjs.org/guide/en/#tree-shaking)
|
|
238
|
+
|
|
239
|
+
**영향도:**
|
|
240
|
+
- 크기: MEDIUM (10-30% 감소)
|
|
241
|
+
- 빌드 시간: NEUTRAL
|
|
242
|
+
- 런타임 성능: POSITIVE (작은 번들 = 빠른 로드)
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Lazy Load Heavy Components
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: splits bundle and loads on demand
|
|
5
|
+
tags: bundle, lazy, code-splitting, suspense, react
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# 무거운 컴포넌트 Lazy 로드
|
|
9
|
+
|
|
10
|
+
## 왜 중요한가
|
|
11
|
+
|
|
12
|
+
차트, 에디터, PDF 뷰어 등 무거운 컴포넌트를 메인 번들에 포함하면 **초기 로드 시간이 크게 증가**합니다. React의 `lazy()`와 `Suspense`를 사용하면 필요할 때만 로드하여 **초기 번들 크기를 크게 줄일 수 있습니다**.
|
|
13
|
+
|
|
14
|
+
## ❌ 잘못된 패턴
|
|
15
|
+
|
|
16
|
+
**무거운 컴포넌트 직접 import (메인 번들에 포함):**
|
|
17
|
+
|
|
18
|
+
```tsx
|
|
19
|
+
// ❌ 메인 번들: 2.5MB (react-chartjs-2 + chart.js 포함)
|
|
20
|
+
import { Line } from 'react-chartjs-2'
|
|
21
|
+
import { Chart as ChartJS, registerables } from 'chart.js'
|
|
22
|
+
|
|
23
|
+
ChartJS.register(...registerables)
|
|
24
|
+
|
|
25
|
+
function Dashboard() {
|
|
26
|
+
return (
|
|
27
|
+
<div>
|
|
28
|
+
<h1>Dashboard</h1>
|
|
29
|
+
<Line data={chartData} /> {/* 500KB 라이브러리 */}
|
|
30
|
+
</div>
|
|
31
|
+
)
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
**다른 무거운 라이브러리 예시:**
|
|
36
|
+
|
|
37
|
+
```tsx
|
|
38
|
+
// ❌ 메인 번들: 3.2MB
|
|
39
|
+
import { Editor } from '@monaco-editor/react' // ~1.8MB
|
|
40
|
+
import { PDFViewer } from '@react-pdf/renderer' // ~800KB
|
|
41
|
+
import { DataGrid } from '@mui/x-data-grid' // ~600KB
|
|
42
|
+
|
|
43
|
+
function App() {
|
|
44
|
+
return (
|
|
45
|
+
<div>
|
|
46
|
+
<Editor />
|
|
47
|
+
<PDFViewer />
|
|
48
|
+
<DataGrid />
|
|
49
|
+
</div>
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**번들 분석:**
|
|
55
|
+
```
|
|
56
|
+
main.js: 3.2 MB (gzipped: 1.1 MB)
|
|
57
|
+
- Monaco Editor: 1.8 MB
|
|
58
|
+
- React PDF: 800 KB
|
|
59
|
+
- MUI DataGrid: 600 KB
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## ✅ 올바른 패턴
|
|
63
|
+
|
|
64
|
+
**React.lazy()로 코드 스플리팅:**
|
|
65
|
+
|
|
66
|
+
```tsx
|
|
67
|
+
// ✅ 메인 번들: 200KB (차트 제외)
|
|
68
|
+
import { lazy, Suspense } from 'react'
|
|
69
|
+
|
|
70
|
+
// 동적 import로 별도 chunk 생성
|
|
71
|
+
const ChartComponent = lazy(() => import('./components/ChartComponent'))
|
|
72
|
+
|
|
73
|
+
function Dashboard() {
|
|
74
|
+
const [showChart, setShowChart] = useState(false)
|
|
75
|
+
|
|
76
|
+
return (
|
|
77
|
+
<div>
|
|
78
|
+
<h1>Dashboard</h1>
|
|
79
|
+
<button onClick={() => setShowChart(true)}>
|
|
80
|
+
Show Chart
|
|
81
|
+
</button>
|
|
82
|
+
|
|
83
|
+
{showChart && (
|
|
84
|
+
<Suspense fallback={<div>Loading chart...</div>}>
|
|
85
|
+
<ChartComponent data={chartData} />
|
|
86
|
+
</Suspense>
|
|
87
|
+
)}
|
|
88
|
+
</div>
|
|
89
|
+
)
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
**별도 파일로 분리 (components/ChartComponent.tsx):**
|
|
94
|
+
|
|
95
|
+
```tsx
|
|
96
|
+
// ChartComponent.tsx - 별도 chunk로 빌드됨
|
|
97
|
+
import { Line } from 'react-chartjs-2'
|
|
98
|
+
import { Chart as ChartJS, registerables } from 'chart.js'
|
|
99
|
+
|
|
100
|
+
ChartJS.register(...registerables)
|
|
101
|
+
|
|
102
|
+
export default function ChartComponent({ data }: { data: any }) {
|
|
103
|
+
return <Line data={data} />
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
**여러 무거운 컴포넌트 lazy 로드:**
|
|
108
|
+
|
|
109
|
+
```tsx
|
|
110
|
+
import { lazy, Suspense } from 'react'
|
|
111
|
+
|
|
112
|
+
const Editor = lazy(() => import('@monaco-editor/react'))
|
|
113
|
+
const PDFViewer = lazy(() => import('./components/PDFViewer'))
|
|
114
|
+
const DataGrid = lazy(() => import('./components/DataGridWrapper'))
|
|
115
|
+
|
|
116
|
+
function App() {
|
|
117
|
+
return (
|
|
118
|
+
<div>
|
|
119
|
+
<Suspense fallback={<div>Loading editor...</div>}>
|
|
120
|
+
<Editor />
|
|
121
|
+
</Suspense>
|
|
122
|
+
|
|
123
|
+
<Suspense fallback={<div>Loading PDF viewer...</div>}>
|
|
124
|
+
<PDFViewer />
|
|
125
|
+
</Suspense>
|
|
126
|
+
|
|
127
|
+
<Suspense fallback={<div>Loading data grid...</div>}>
|
|
128
|
+
<DataGrid />
|
|
129
|
+
</Suspense>
|
|
130
|
+
</div>
|
|
131
|
+
)
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
**번들 분석:**
|
|
136
|
+
```
|
|
137
|
+
main.js: 200 KB (gzipped: 70 KB)
|
|
138
|
+
ChartComponent-[hash].js: 500 KB (gzipped: 180 KB)
|
|
139
|
+
Editor-[hash].js: 1.8 MB (gzipped: 600 KB)
|
|
140
|
+
PDFViewer-[hash].js: 800 KB (gzipped: 250 KB)
|
|
141
|
+
DataGrid-[hash].js: 600 KB (gzipped: 200 KB)
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
**Route-based code splitting (추가 최적화):**
|
|
145
|
+
|
|
146
|
+
```tsx
|
|
147
|
+
// App.tsx
|
|
148
|
+
import { lazy, Suspense } from 'react'
|
|
149
|
+
import { BrowserRouter, Routes, Route } from 'react-router-dom'
|
|
150
|
+
|
|
151
|
+
// 라우트 레벨에서 lazy loading
|
|
152
|
+
const Dashboard = lazy(() => import('./pages/Dashboard'))
|
|
153
|
+
const Settings = lazy(() => import('./pages/Settings'))
|
|
154
|
+
const Analytics = lazy(() => import('./pages/Analytics'))
|
|
155
|
+
|
|
156
|
+
function App() {
|
|
157
|
+
return (
|
|
158
|
+
<BrowserRouter>
|
|
159
|
+
<Suspense fallback={<div>Loading page...</div>}>
|
|
160
|
+
<Routes>
|
|
161
|
+
<Route path="/" element={<Dashboard />} />
|
|
162
|
+
<Route path="/settings" element={<Settings />} />
|
|
163
|
+
<Route path="/analytics" element={<Analytics />} />
|
|
164
|
+
</Routes>
|
|
165
|
+
</Suspense>
|
|
166
|
+
</BrowserRouter>
|
|
167
|
+
)
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## 추가 컨텍스트
|
|
172
|
+
|
|
173
|
+
**Lazy Loading 대상 라이브러리:**
|
|
174
|
+
|
|
175
|
+
| 라이브러리 | 크기 | Lazy 로드 권장 |
|
|
176
|
+
|-----------|------|----------------|
|
|
177
|
+
| `@monaco-editor/react` | ~1.8MB | ✅ 필수 |
|
|
178
|
+
| `react-chartjs-2` + `chart.js` | ~500KB | ✅ 권장 |
|
|
179
|
+
| `@react-pdf/renderer` | ~800KB | ✅ 권장 |
|
|
180
|
+
| `@mui/x-data-grid` | ~600KB | ✅ 권장 |
|
|
181
|
+
| `react-quill` | ~400KB | ✅ 권장 |
|
|
182
|
+
| `katex` (수식 렌더링) | ~300KB | ✅ 권장 |
|
|
183
|
+
| `prismjs` (코드 하이라이팅) | ~200KB | ⚠️ 선택 |
|
|
184
|
+
|
|
185
|
+
**Suspense fallback 패턴:**
|
|
186
|
+
|
|
187
|
+
```tsx
|
|
188
|
+
// 스켈레톤 UI로 로딩 상태 표시
|
|
189
|
+
function ChartSkeleton() {
|
|
190
|
+
return (
|
|
191
|
+
<div className="animate-pulse">
|
|
192
|
+
<div className="h-64 bg-gray-200 rounded" />
|
|
193
|
+
</div>
|
|
194
|
+
)
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
<Suspense fallback={<ChartSkeleton />}>
|
|
198
|
+
<ChartComponent />
|
|
199
|
+
</Suspense>
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
**Preloading 패턴 (UX 개선):**
|
|
203
|
+
|
|
204
|
+
```tsx
|
|
205
|
+
import { lazy, Suspense } from 'react'
|
|
206
|
+
|
|
207
|
+
const ChartComponent = lazy(() => import('./ChartComponent'))
|
|
208
|
+
|
|
209
|
+
function Dashboard() {
|
|
210
|
+
const handleMouseEnter = () => {
|
|
211
|
+
// 마우스 호버 시 미리 로드
|
|
212
|
+
import('./ChartComponent')
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return (
|
|
216
|
+
<button onMouseEnter={handleMouseEnter}>
|
|
217
|
+
Show Chart
|
|
218
|
+
</button>
|
|
219
|
+
)
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
**Vite 설정 확인:**
|
|
224
|
+
|
|
225
|
+
```typescript
|
|
226
|
+
// vite.config.ts
|
|
227
|
+
export default defineConfig({
|
|
228
|
+
build: {
|
|
229
|
+
rollupOptions: {
|
|
230
|
+
output: {
|
|
231
|
+
manualChunks: {
|
|
232
|
+
// 특정 라이브러리를 별도 chunk로 분리
|
|
233
|
+
'chart-lib': ['react-chartjs-2', 'chart.js'],
|
|
234
|
+
'editor-lib': ['@monaco-editor/react'],
|
|
235
|
+
'pdf-lib': ['@react-pdf/renderer']
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
})
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
**효과:**
|
|
244
|
+
- 초기 번들: 3.2MB → 200KB (16배 감소)
|
|
245
|
+
- 초기 로드 시간: 3초 → 0.5초 (6배 빠름)
|
|
246
|
+
- 차트 표시 시 추가 로드: ~500KB (병렬 로드 가능)
|
|
247
|
+
|
|
248
|
+
**참고:**
|
|
249
|
+
- React Documentation: [Code-Splitting](https://react.dev/reference/react/lazy)
|
|
250
|
+
- Vite Guide: [Code Splitting](https://vitejs.dev/guide/features.html#code-splitting)
|
|
251
|
+
|
|
252
|
+
**영향도:**
|
|
253
|
+
- 크기: HIGH (초기 번들 1-3MB 감소)
|
|
254
|
+
- 초기 로드 시간: HIGH (2-6배 빠름)
|
|
255
|
+
- UX: POSITIVE (페이지 즉시 표시)
|