@kood/claude-code 0.4.1 → 0.5.1
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 +69 -12
- package/package.json +2 -1
- package/templates/.claude/PARALLEL_AGENTS.md +737 -0
- package/templates/.claude/agents/analyst.md +416 -0
- package/templates/.claude/agents/architect.md +569 -0
- package/templates/.claude/agents/code-reviewer.md +132 -133
- package/templates/.claude/agents/dependency-manager.md +93 -94
- package/templates/.claude/agents/deployment-validator.md +64 -65
- package/templates/.claude/agents/designer.md +655 -0
- package/templates/.claude/agents/document-writer.md +500 -0
- package/templates/.claude/agents/explore.md +499 -0
- package/templates/.claude/agents/git-operator.md +74 -75
- package/templates/.claude/agents/implementation-executor.md +138 -109
- package/templates/.claude/agents/ko-to-en-translator.md +18 -22
- package/templates/.claude/agents/lint-fixer.md +250 -93
- package/templates/.claude/agents/planner.md +356 -0
- package/templates/.claude/agents/refactor-advisor.md +135 -136
- package/templates/.claude/commands/bug-fix.md +296 -207
- package/templates/.claude/commands/git-all.md +199 -46
- package/templates/.claude/commands/git-session.md +113 -57
- package/templates/.claude/commands/lint-fix.md +219 -153
- package/templates/.claude/commands/lint-init.md +113 -76
- package/templates/.claude/commands/pre-deploy.md +190 -124
- package/templates/.claude/commands/refactor.md +407 -162
- package/templates/.claude/commands/version-update.md +138 -37
- package/templates/.claude/instructions/context-engineering/ANTHROPIC_CONTEXT_ENGINEERING.md +178 -0
- package/templates/.claude/instructions/context-engineering/references/claude-4x.md +215 -0
- package/templates/.claude/instructions/context-engineering/references/core-principles.md +137 -0
- package/templates/.claude/instructions/context-engineering/references/examples.md +351 -0
- package/templates/.claude/instructions/context-engineering/references/techniques.md +162 -0
- package/templates/.claude/instructions/parallel-agent-execution.md +874 -0
- package/templates/.claude/skills/docs-creator/AGENTS.md +238 -0
- package/templates/.claude/{commands/docs-creator.md → skills/docs-creator/SKILL.md} +61 -75
- package/templates/.claude/skills/docs-refactor/AGENTS.md +270 -0
- package/templates/.claude/{commands/docs-refactor.md → skills/docs-refactor/SKILL.md} +30 -44
- package/templates/.claude/skills/execute/SKILL.md +451 -0
- package/templates/.claude/skills/figma-to-code/AGENTS.md +287 -0
- package/templates/.claude/skills/figma-to-code/SKILL.md +225 -225
- package/templates/.claude/skills/figma-to-code/references/design-tokens.md +75 -73
- package/templates/.claude/skills/figma-to-code/references/figma-mcp-tools.md +73 -73
- package/templates/.claude/skills/figma-to-code/references/layout-mapping.md +104 -104
- package/templates/.claude/skills/figma-to-code/references/responsive-design.md +99 -99
- package/templates/.claude/skills/figma-to-code/references/verification.md +91 -91
- package/templates/.claude/skills/global-uiux-design/AGENTS.md +317 -0
- package/templates/.claude/skills/global-uiux-design/SKILL.md +738 -0
- package/templates/.claude/skills/global-uiux-design/references/accessibility.md +401 -0
- package/templates/.claude/skills/global-uiux-design/references/color-system.md +275 -0
- package/templates/.claude/skills/global-uiux-design/references/design-philosophy.md +206 -0
- package/templates/.claude/skills/global-uiux-design/references/design-systems.md +446 -0
- package/templates/.claude/skills/korea-uiux-design/AGENTS.md +307 -0
- package/templates/.claude/skills/korea-uiux-design/SKILL.md +170 -0
- package/templates/.claude/skills/nextjs-react-best-practices/AGENTS.md +95 -116
- package/templates/.claude/skills/nextjs-react-best-practices/SKILL.md +134 -152
- package/templates/.claude/skills/nextjs-react-best-practices/rules/advanced-event-handler-refs.md +6 -6
- package/templates/.claude/skills/nextjs-react-best-practices/rules/advanced-use-latest.md +5 -5
- package/templates/.claude/skills/nextjs-react-best-practices/rules/async-api-routes.md +5 -5
- package/templates/.claude/skills/nextjs-react-best-practices/rules/async-defer-await.md +22 -22
- package/templates/.claude/skills/nextjs-react-best-practices/rules/async-dependencies.md +5 -5
- package/templates/.claude/skills/nextjs-react-best-practices/rules/async-parallel.md +4 -4
- package/templates/.claude/skills/nextjs-react-best-practices/rules/async-suspense-boundaries.md +21 -21
- package/templates/.claude/skills/nextjs-react-best-practices/rules/bundle-barrel-imports.md +18 -18
- package/templates/.claude/skills/nextjs-react-best-practices/rules/bundle-conditional.md +4 -4
- package/templates/.claude/skills/nextjs-react-best-practices/rules/bundle-defer-third-party.md +4 -4
- package/templates/.claude/skills/nextjs-react-best-practices/rules/bundle-dynamic-imports.md +4 -4
- package/templates/.claude/skills/nextjs-react-best-practices/rules/bundle-preload.md +5 -5
- package/templates/.claude/skills/nextjs-react-best-practices/rules/client-event-listeners.md +9 -9
- package/templates/.claude/skills/nextjs-react-best-practices/rules/client-swr-dedup.md +7 -7
- package/templates/.claude/skills/nextjs-react-best-practices/rules/js-batch-dom-css.md +13 -13
- package/templates/.claude/skills/nextjs-react-best-practices/rules/js-cache-function-results.md +14 -14
- package/templates/.claude/skills/nextjs-react-best-practices/rules/js-cache-property-access.md +4 -4
- package/templates/.claude/skills/nextjs-react-best-practices/rules/js-cache-storage.md +10 -10
- package/templates/.claude/skills/nextjs-react-best-practices/rules/js-combine-iterations.md +4 -4
- package/templates/.claude/skills/nextjs-react-best-practices/rules/js-early-exit.md +7 -7
- package/templates/.claude/skills/nextjs-react-best-practices/rules/js-hoist-regexp.md +6 -6
- package/templates/.claude/skills/nextjs-react-best-practices/rules/js-index-maps.md +6 -6
- package/templates/.claude/skills/nextjs-react-best-practices/rules/js-length-check-first.md +14 -14
- package/templates/.claude/skills/nextjs-react-best-practices/rules/js-min-max-loop.md +16 -16
- package/templates/.claude/skills/nextjs-react-best-practices/rules/js-set-map-lookups.md +4 -4
- package/templates/.claude/skills/nextjs-react-best-practices/rules/js-tosorted-immutable.md +17 -17
- package/templates/.claude/skills/nextjs-react-best-practices/rules/rendering-activity.md +4 -4
- package/templates/.claude/skills/nextjs-react-best-practices/rules/rendering-animate-svg-wrapper.md +11 -11
- package/templates/.claude/skills/nextjs-react-best-practices/rules/rendering-conditional-render.md +8 -8
- package/templates/.claude/skills/nextjs-react-best-practices/rules/rendering-content-visibility.md +4 -4
- package/templates/.claude/skills/nextjs-react-best-practices/rules/rendering-hoist-jsx.md +6 -6
- package/templates/.claude/skills/nextjs-react-best-practices/rules/rendering-hydration-no-flicker.md +14 -14
- package/templates/.claude/skills/nextjs-react-best-practices/rules/rendering-svg-precision.md +5 -5
- package/templates/.claude/skills/nextjs-react-best-practices/rules/rerender-defer-reads.md +4 -4
- package/templates/.claude/skills/nextjs-react-best-practices/rules/rerender-dependencies.md +7 -7
- package/templates/.claude/skills/nextjs-react-best-practices/rules/rerender-derived-state.md +5 -5
- package/templates/.claude/skills/nextjs-react-best-practices/rules/rerender-functional-setstate.md +34 -34
- package/templates/.claude/skills/nextjs-react-best-practices/rules/rerender-lazy-state-init.md +15 -15
- package/templates/.claude/skills/nextjs-react-best-practices/rules/rerender-memo.md +5 -5
- package/templates/.claude/skills/nextjs-react-best-practices/rules/rerender-transitions.md +4 -4
- package/templates/.claude/skills/nextjs-react-best-practices/rules/server-after-nonblocking.md +24 -24
- package/templates/.claude/skills/nextjs-react-best-practices/rules/server-cache-lru.md +10 -10
- package/templates/.claude/skills/nextjs-react-best-practices/rules/server-cache-react.md +4 -4
- package/templates/.claude/skills/nextjs-react-best-practices/rules/server-parallel-fetching.md +5 -5
- package/templates/.claude/skills/nextjs-react-best-practices/rules/server-serialization.md +6 -6
- package/templates/.claude/skills/plan/SKILL.md +594 -0
- package/templates/.claude/skills/prd/SKILL.md +496 -0
- package/templates/.claude/skills/ralph/AGENTS.md +393 -0
- package/templates/.claude/skills/ralph/SKILL.md +1035 -0
- package/templates/.claude/skills/tanstack-start-react-best-practices/AGENTS.md +100 -121
- package/templates/.claude/skills/tanstack-start-react-best-practices/SKILL.md +139 -157
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/async-defer-await.md +22 -22
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/async-dependencies.md +5 -5
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/async-loader.md +7 -7
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/async-parallel.md +4 -4
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/bundle-barrel-imports.md +18 -18
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/bundle-conditional.md +4 -4
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/bundle-defer-third-party.md +4 -4
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/bundle-lazy-routes.md +12 -12
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/bundle-preload.md +5 -5
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/client-event-listeners.md +9 -9
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/client-tanstack-query.md +12 -12
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/js-batch-dom-css.md +13 -13
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/js-cache-function-results.md +14 -14
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/js-cache-property-access.md +4 -4
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/js-cache-storage.md +10 -10
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/js-combine-iterations.md +4 -4
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/js-early-exit.md +7 -7
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/js-hoist-regexp.md +6 -6
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/js-index-maps.md +6 -6
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/js-length-check-first.md +14 -14
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/js-min-max-loop.md +16 -16
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/js-set-map-lookups.md +4 -4
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/js-tosorted-immutable.md +17 -17
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/rendering-animate-svg-wrapper.md +11 -11
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/rendering-conditional-render.md +8 -8
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/rendering-content-visibility.md +4 -4
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/rendering-hoist-jsx.md +6 -6
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/rendering-svg-precision.md +5 -5
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/rerender-defer-reads.md +4 -4
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/rerender-dependencies.md +7 -7
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/rerender-derived-state.md +5 -5
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/rerender-functional-setstate.md +34 -34
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/rerender-lazy-state-init.md +15 -15
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/rerender-memo.md +5 -5
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/rerender-transitions.md +4 -4
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/server-cache-lru.md +12 -12
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/server-deferred-data.md +14 -14
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/server-parallel-fetching.md +9 -9
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/server-serialization.md +6 -6
- package/templates/.claude/commands/agent-creator.md +0 -370
- package/templates/.claude/commands/command-creator.md +0 -524
- package/templates/.claude/commands/execute.md +0 -469
- package/templates/.claude/commands/git.md +0 -98
- package/templates/.claude/commands/plan.md +0 -526
- package/templates/.claude/commands/prd.md +0 -629
|
@@ -1,44 +1,23 @@
|
|
|
1
|
-
# TanStack Start React
|
|
1
|
+
# TanStack Start React 베스트 프랙티스
|
|
2
2
|
|
|
3
3
|
**Version 1.0.0**
|
|
4
|
-
TanStack Start
|
|
4
|
+
TanStack Start 최적화 가이드
|
|
5
5
|
January 2026
|
|
6
6
|
|
|
7
|
-
>
|
|
8
|
-
>
|
|
9
|
-
> generating, or refactoring React and TanStack Start codebases. Humans
|
|
10
|
-
> may also find it useful, but guidance here is optimized for automation
|
|
11
|
-
> and consistency by AI-assisted workflows.
|
|
7
|
+
> **참고:**
|
|
8
|
+
> 이 문서는 주로 에이전트와 LLM이 React 및 TanStack Start 코드베이스를 유지보수, 생성, 리팩토링할 때 따르기 위한 것입니다. 사람도 유용하게 사용할 수 있지만, AI 지원 워크플로의 자동화 및 일관성을 위해 최적화되어 있습니다.
|
|
12
9
|
|
|
13
10
|
---
|
|
14
11
|
|
|
15
|
-
|
|
12
|
+
## 요약
|
|
16
13
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
**CRITICAL: Always communicate with the user in Korean (한국어).**
|
|
20
|
-
|
|
21
|
-
This applies to:
|
|
22
|
-
- Questions and clarifications
|
|
23
|
-
- Progress updates and summaries
|
|
24
|
-
- Explaining decisions and trade-offs
|
|
25
|
-
- Reporting errors or blockers
|
|
26
|
-
|
|
27
|
-
Internal processing and code comments remain in English. Only user-facing messages must be in Korean.
|
|
28
|
-
|
|
29
|
-
</communication>
|
|
30
|
-
|
|
31
|
-
---
|
|
32
|
-
|
|
33
|
-
## Abstract
|
|
34
|
-
|
|
35
|
-
Comprehensive performance optimization guide for React and TanStack Start applications, designed for AI agents and LLMs. Contains 38 rules across 7 categories, prioritized by impact from critical (eliminating waterfalls, reducing bundle size) to incremental (JavaScript performance). Each rule includes detailed explanations, real-world examples comparing incorrect vs. correct implementations, and specific impact metrics to guide automated refactoring and code generation.
|
|
14
|
+
AI 에이전트와 LLM을 위한 React 및 TanStack Start 애플리케이션 종합 성능 최적화 가이드. 7개 카테고리에 걸쳐 38개 규칙을 포함하며, 영향도별로 우선순위를 매겼습니다 (critical: waterfall 제거, 번들 크기 감소 → incremental: JavaScript 성능). 각 규칙은 자동 리팩토링 및 코드 생성을 위한 상세한 설명, 올바른 구현 대 잘못된 구현을 비교하는 실제 예시, 구체적인 영향 지표를 포함합니다.
|
|
36
15
|
|
|
37
16
|
---
|
|
38
17
|
|
|
39
18
|
<instructions>
|
|
40
19
|
|
|
41
|
-
##
|
|
20
|
+
## 문서 사용 지침
|
|
42
21
|
|
|
43
22
|
@rules/async-defer-await.md
|
|
44
23
|
@rules/async-parallel.md
|
|
@@ -86,17 +65,17 @@ Comprehensive performance optimization guide for React and TanStack Start applic
|
|
|
86
65
|
|
|
87
66
|
<categories>
|
|
88
67
|
|
|
89
|
-
##
|
|
68
|
+
## 카테고리별 우선순위
|
|
90
69
|
|
|
91
|
-
|
|
|
92
|
-
|
|
93
|
-
| 1 |
|
|
94
|
-
| 2 |
|
|
95
|
-
| 3 |
|
|
96
|
-
| 4 |
|
|
97
|
-
| 5 | Re-render
|
|
98
|
-
| 6 |
|
|
99
|
-
| 7 | JavaScript
|
|
70
|
+
| 우선순위 | 카테고리 | 영향도 | 설명 |
|
|
71
|
+
|---------|---------|--------|------|
|
|
72
|
+
| 1 | Waterfall 제거 | **CRITICAL** | 순차 await를 병렬로 전환. 가장 큰 성능 향상 제공 |
|
|
73
|
+
| 2 | 번들 크기 최적화 | **CRITICAL** | TTI와 LCP 개선. 초기 로딩 속도 향상 |
|
|
74
|
+
| 3 | 서버 사이드 성능 | HIGH | 서버 사이드 waterfall 제거, 응답 시간 단축 |
|
|
75
|
+
| 4 | 클라이언트 데이터 페칭 | MEDIUM-HIGH | 자동 중복 제거, 효율적 데이터 페칭 |
|
|
76
|
+
| 5 | Re-render 최적화 | MEDIUM | 불필요한 re-render 최소화, UI 반응성 향상 |
|
|
77
|
+
| 6 | 렌더링 성능 | MEDIUM | 브라우저 렌더링 작업 최적화 |
|
|
78
|
+
| 7 | JavaScript 성능 | LOW-MEDIUM | Hot path 마이크로 최적화 |
|
|
100
79
|
|
|
101
80
|
</categories>
|
|
102
81
|
|
|
@@ -104,19 +83,19 @@ Comprehensive performance optimization guide for React and TanStack Start applic
|
|
|
104
83
|
|
|
105
84
|
<critical_patterns>
|
|
106
85
|
|
|
107
|
-
## 1.
|
|
86
|
+
## 1. Waterfall 제거 (CRITICAL)
|
|
108
87
|
|
|
109
|
-
|
|
88
|
+
Waterfall은 가장 큰 성능 저해 요소입니다. 순차 await는 전체 네트워크 지연을 추가합니다.
|
|
110
89
|
|
|
111
|
-
###
|
|
90
|
+
### 병렬 실행
|
|
112
91
|
|
|
113
92
|
```typescript
|
|
114
|
-
// ❌
|
|
93
|
+
// ❌ 순차 실행 (3번 왕복)
|
|
115
94
|
const user = await fetchUser()
|
|
116
95
|
const posts = await fetchPosts()
|
|
117
96
|
const comments = await fetchComments()
|
|
118
97
|
|
|
119
|
-
// ✅
|
|
98
|
+
// ✅ 병렬 실행 (1번 왕복)
|
|
120
99
|
const [user, posts, comments] = await Promise.all([
|
|
121
100
|
fetchUser(),
|
|
122
101
|
fetchPosts(),
|
|
@@ -124,13 +103,13 @@ const [user, posts, comments] = await Promise.all([
|
|
|
124
103
|
])
|
|
125
104
|
```
|
|
126
105
|
|
|
127
|
-
### createServerFn + Loader
|
|
106
|
+
### createServerFn + Loader에서 병렬 페칭
|
|
128
107
|
|
|
129
108
|
```typescript
|
|
130
109
|
import { createServerFn } from '@tanstack/react-start'
|
|
131
110
|
import { createFileRoute } from '@tanstack/react-router'
|
|
132
111
|
|
|
133
|
-
//
|
|
112
|
+
// Server Functions 정의
|
|
134
113
|
const getPost = createServerFn().handler(async (postId: string) => {
|
|
135
114
|
return await db.post.findUnique({ where: { id: postId } })
|
|
136
115
|
})
|
|
@@ -143,7 +122,7 @@ const getComments = createServerFn().handler(async (postId: string) => {
|
|
|
143
122
|
return await db.comment.findMany({ where: { postId } })
|
|
144
123
|
})
|
|
145
124
|
|
|
146
|
-
// ❌
|
|
125
|
+
// ❌ 순차 로딩
|
|
147
126
|
export const Route = createFileRoute('/posts/$postId')({
|
|
148
127
|
loader: async ({ params }) => {
|
|
149
128
|
const post = await getPost(params.postId)
|
|
@@ -153,7 +132,7 @@ export const Route = createFileRoute('/posts/$postId')({
|
|
|
153
132
|
}
|
|
154
133
|
})
|
|
155
134
|
|
|
156
|
-
// ✅
|
|
135
|
+
// ✅ 병렬 로딩 (독립 데이터)
|
|
157
136
|
export const Route = createFileRoute('/posts/$postId')({
|
|
158
137
|
loader: async ({ params }) => {
|
|
159
138
|
const [post, comments] = await Promise.all([
|
|
@@ -165,12 +144,12 @@ export const Route = createFileRoute('/posts/$postId')({
|
|
|
165
144
|
})
|
|
166
145
|
```
|
|
167
146
|
|
|
168
|
-
###
|
|
147
|
+
### 의존성 기반 병렬화
|
|
169
148
|
|
|
170
149
|
```typescript
|
|
171
150
|
import { all } from 'better-all'
|
|
172
151
|
|
|
173
|
-
// ✅
|
|
152
|
+
// ✅ better-all로 최대 병렬화
|
|
174
153
|
export const Route = createFileRoute('/posts/$postId')({
|
|
175
154
|
loader: async ({ params }) => {
|
|
176
155
|
const { post, author, comments } = await all({
|
|
@@ -190,31 +169,31 @@ export const Route = createFileRoute('/posts/$postId')({
|
|
|
190
169
|
})
|
|
191
170
|
```
|
|
192
171
|
|
|
193
|
-
### Deferred Data
|
|
172
|
+
### Deferred Data로 비차단 로딩 (자동 처리)
|
|
194
173
|
|
|
195
174
|
```typescript
|
|
196
175
|
import { createServerFn } from '@tanstack/react-start'
|
|
197
176
|
import { createFileRoute, Await } from '@tanstack/react-router'
|
|
198
177
|
import { Suspense } from 'react'
|
|
199
178
|
|
|
200
|
-
//
|
|
179
|
+
// 빠른 Server Function
|
|
201
180
|
const getPost = createServerFn().handler(async (postId: string) => {
|
|
202
181
|
return await db.post.findUnique({ where: { id: postId } })
|
|
203
182
|
})
|
|
204
183
|
|
|
205
|
-
//
|
|
184
|
+
// 느린 Server Function
|
|
206
185
|
const getComments = createServerFn().handler(async (postId: string) => {
|
|
207
|
-
await new Promise(r => setTimeout(r, 3000)) //
|
|
186
|
+
await new Promise(r => setTimeout(r, 3000)) // 느린 쿼리
|
|
208
187
|
return await db.comment.findMany({ where: { postId } })
|
|
209
188
|
})
|
|
210
189
|
|
|
211
|
-
// ✅
|
|
190
|
+
// ✅ 중요 데이터는 await, 비중요 데이터는 Promise 반환
|
|
212
191
|
export const Route = createFileRoute('/posts/$postId')({
|
|
213
192
|
loader: async ({ params }) => {
|
|
214
|
-
//
|
|
193
|
+
// 중요: post는 즉시 로드 (await)
|
|
215
194
|
const post = await getPost(params.postId)
|
|
216
195
|
|
|
217
|
-
//
|
|
196
|
+
// 비중요: comments는 Promise 그대로 반환 (자동 deferred)
|
|
218
197
|
const deferredComments = getComments(params.postId)
|
|
219
198
|
|
|
220
199
|
return {
|
|
@@ -229,10 +208,10 @@ function PostPage() {
|
|
|
229
208
|
|
|
230
209
|
return (
|
|
231
210
|
<div>
|
|
232
|
-
{/* post
|
|
211
|
+
{/* post는 즉시 렌더링 */}
|
|
233
212
|
<PostContent post={post} />
|
|
234
213
|
|
|
235
|
-
{/* comments
|
|
214
|
+
{/* comments는 스트리밍 */}
|
|
236
215
|
<Suspense fallback={<CommentsSkeleton />}>
|
|
237
216
|
<Await promise={deferredComments}>
|
|
238
217
|
{(comments) => <Comments comments={comments} />}
|
|
@@ -243,7 +222,7 @@ function PostPage() {
|
|
|
243
222
|
}
|
|
244
223
|
```
|
|
245
224
|
|
|
246
|
-
|
|
225
|
+
**중요:** TanStack Start에서는 `defer()` 함수를 명시적으로 호출할 필요가 없습니다. Promise를 그대로 반환하면 자동으로 deferred 처리됩니다.
|
|
247
226
|
|
|
248
227
|
</critical_patterns>
|
|
249
228
|
|
|
@@ -251,29 +230,29 @@ function PostPage() {
|
|
|
251
230
|
|
|
252
231
|
<bundle_optimization>
|
|
253
232
|
|
|
254
|
-
## 2.
|
|
233
|
+
## 2. 번들 크기 최적화 (CRITICAL)
|
|
255
234
|
|
|
256
|
-
###
|
|
235
|
+
### Barrel Import 회피
|
|
257
236
|
|
|
258
237
|
```tsx
|
|
259
|
-
// ❌
|
|
238
|
+
// ❌ 전체 라이브러리 import (1583개 모듈, ~2.8초)
|
|
260
239
|
import { Check, X, Menu } from 'lucide-react'
|
|
261
240
|
|
|
262
|
-
// ✅
|
|
241
|
+
// ✅ 직접 import (3개 모듈만)
|
|
263
242
|
import Check from 'lucide-react/dist/esm/icons/check'
|
|
264
243
|
import X from 'lucide-react/dist/esm/icons/x'
|
|
265
244
|
import Menu from 'lucide-react/dist/esm/icons/menu'
|
|
266
245
|
```
|
|
267
246
|
|
|
268
|
-
|
|
247
|
+
영향 받는 라이브러리: `lucide-react`, `@mui/material`, `@mui/icons-material`, `@tabler/icons-react`, `react-icons`, `@headlessui/react`, `@radix-ui/react-*`, `lodash`, `ramda`, `date-fns`, `rxjs`, `react-use`
|
|
269
248
|
|
|
270
|
-
###
|
|
249
|
+
### 라우트 기반 코드 스플리팅
|
|
271
250
|
|
|
272
251
|
```typescript
|
|
273
252
|
import { lazy } from 'react'
|
|
274
253
|
import { createFileRoute } from '@tanstack/react-router'
|
|
275
254
|
|
|
276
|
-
// ❌
|
|
255
|
+
// ❌ 모든 컴포넌트가 메인 번들에 포함
|
|
277
256
|
import { HeavyEditor } from './components/HeavyEditor'
|
|
278
257
|
import { ComplexChart } from './components/ComplexChart'
|
|
279
258
|
|
|
@@ -290,7 +269,7 @@ function DashboardPage() {
|
|
|
290
269
|
)
|
|
291
270
|
}
|
|
292
271
|
|
|
293
|
-
// ✅
|
|
272
|
+
// ✅ 무거운 컴포넌트는 lazy load
|
|
294
273
|
const HeavyEditor = lazy(() => import('./components/HeavyEditor'))
|
|
295
274
|
const ComplexChart = lazy(() => import('./components/ComplexChart'))
|
|
296
275
|
|
|
@@ -312,10 +291,10 @@ function DashboardPage() {
|
|
|
312
291
|
}
|
|
313
292
|
```
|
|
314
293
|
|
|
315
|
-
###
|
|
294
|
+
### 조건부 모듈 로딩
|
|
316
295
|
|
|
317
296
|
```tsx
|
|
318
|
-
// ❌
|
|
297
|
+
// ❌ 항상 로드
|
|
319
298
|
import { AnimationFrames } from './animation-frames'
|
|
320
299
|
|
|
321
300
|
function AnimationPlayer({ enabled }: { enabled: boolean }) {
|
|
@@ -323,7 +302,7 @@ function AnimationPlayer({ enabled }: { enabled: boolean }) {
|
|
|
323
302
|
return <Canvas frames={AnimationFrames} />
|
|
324
303
|
}
|
|
325
304
|
|
|
326
|
-
// ✅
|
|
305
|
+
// ✅ 활성화 시에만 로드
|
|
327
306
|
function AnimationPlayer({ enabled }: { enabled: boolean }) {
|
|
328
307
|
const [frames, setFrames] = useState<Frame[] | null>(null)
|
|
329
308
|
|
|
@@ -346,16 +325,16 @@ function AnimationPlayer({ enabled }: { enabled: boolean }) {
|
|
|
346
325
|
|
|
347
326
|
<server_performance>
|
|
348
327
|
|
|
349
|
-
## 3.
|
|
328
|
+
## 3. 서버 사이드 성능 (HIGH)
|
|
350
329
|
|
|
351
|
-
### LRU
|
|
330
|
+
### LRU 캐시 - 요청 간 캐싱
|
|
352
331
|
|
|
353
332
|
```typescript
|
|
354
333
|
import { LRUCache } from 'lru-cache'
|
|
355
334
|
|
|
356
335
|
const cache = new LRUCache<string, any>({
|
|
357
336
|
max: 1000,
|
|
358
|
-
ttl: 5 * 60 * 1000 // 5
|
|
337
|
+
ttl: 5 * 60 * 1000 // 5분
|
|
359
338
|
})
|
|
360
339
|
|
|
361
340
|
export async function getUser(id: string) {
|
|
@@ -367,27 +346,27 @@ export async function getUser(id: string) {
|
|
|
367
346
|
return user
|
|
368
347
|
}
|
|
369
348
|
|
|
370
|
-
//
|
|
371
|
-
//
|
|
349
|
+
// 요청 1: DB 쿼리, 결과 캐싱
|
|
350
|
+
// 요청 2: 캐시 히트, DB 쿼리 없음
|
|
372
351
|
```
|
|
373
352
|
|
|
374
|
-
###
|
|
353
|
+
### 직렬화 최소화
|
|
375
354
|
|
|
376
355
|
```typescript
|
|
377
|
-
// ❌
|
|
356
|
+
// ❌ 50개 필드 모두 직렬화
|
|
378
357
|
export const Route = createFileRoute('/profile')({
|
|
379
358
|
loader: async () => {
|
|
380
|
-
const user = await fetchUser() // 50
|
|
359
|
+
const user = await fetchUser() // 50개 필드
|
|
381
360
|
return { user }
|
|
382
361
|
}
|
|
383
362
|
})
|
|
384
363
|
|
|
385
364
|
function ProfilePage() {
|
|
386
365
|
const { user } = Route.useLoaderData()
|
|
387
|
-
return <div>{user.name}</div> //
|
|
366
|
+
return <div>{user.name}</div> // 1개 필드만 사용
|
|
388
367
|
}
|
|
389
368
|
|
|
390
|
-
// ✅
|
|
369
|
+
// ✅ 필요한 필드만 직렬화
|
|
391
370
|
export const Route = createFileRoute('/profile')({
|
|
392
371
|
loader: async () => {
|
|
393
372
|
const user = await fetchUser()
|
|
@@ -409,13 +388,13 @@ function ProfilePage() {
|
|
|
409
388
|
}
|
|
410
389
|
```
|
|
411
390
|
|
|
412
|
-
###
|
|
391
|
+
### Loader에서 병렬 페칭 + createServerFn
|
|
413
392
|
|
|
414
393
|
```typescript
|
|
415
394
|
import { createServerFn } from '@tanstack/react-start'
|
|
416
395
|
import { createFileRoute } from '@tanstack/react-router'
|
|
417
396
|
|
|
418
|
-
//
|
|
397
|
+
// Server Functions 정의
|
|
419
398
|
const getUser = createServerFn().handler(async () => {
|
|
420
399
|
return await db.user.findMany()
|
|
421
400
|
})
|
|
@@ -428,7 +407,7 @@ const getNotifications = createServerFn().handler(async () => {
|
|
|
428
407
|
return await db.notification.findMany()
|
|
429
408
|
})
|
|
430
409
|
|
|
431
|
-
// ❌
|
|
410
|
+
// ❌ 순차 페칭
|
|
432
411
|
export const Route = createFileRoute('/dashboard')({
|
|
433
412
|
loader: async () => {
|
|
434
413
|
const user = await getUser()
|
|
@@ -438,7 +417,7 @@ export const Route = createFileRoute('/dashboard')({
|
|
|
438
417
|
}
|
|
439
418
|
})
|
|
440
419
|
|
|
441
|
-
// ✅
|
|
420
|
+
// ✅ 병렬 페칭
|
|
442
421
|
export const Route = createFileRoute('/dashboard')({
|
|
443
422
|
loader: async () => {
|
|
444
423
|
const [user, stats, notifications] = await Promise.all([
|
|
@@ -451,7 +430,7 @@ export const Route = createFileRoute('/dashboard')({
|
|
|
451
430
|
})
|
|
452
431
|
```
|
|
453
432
|
|
|
454
|
-
|
|
433
|
+
**중요:** TanStack Start의 loader는 isomorphic입니다. 서버와 클라이언트 둘 다에서 실행될 수 있으므로, 서버 전용 코드는 `createServerFn()`으로 분리하세요.
|
|
455
434
|
|
|
456
435
|
</server_performance>
|
|
457
436
|
|
|
@@ -459,14 +438,14 @@ export const Route = createFileRoute('/dashboard')({
|
|
|
459
438
|
|
|
460
439
|
<client_data_fetching>
|
|
461
440
|
|
|
462
|
-
## 4.
|
|
441
|
+
## 4. 클라이언트 데이터 페칭 (MEDIUM-HIGH)
|
|
463
442
|
|
|
464
|
-
### TanStack Query
|
|
443
|
+
### TanStack Query로 자동 캐싱
|
|
465
444
|
|
|
466
445
|
```tsx
|
|
467
446
|
import { useQuery } from '@tanstack/react-query'
|
|
468
447
|
|
|
469
|
-
// ❌
|
|
448
|
+
// ❌ 중복 제거 없음, 각 인스턴스가 fetch
|
|
470
449
|
function UserList() {
|
|
471
450
|
const [users, setUsers] = useState([])
|
|
472
451
|
useEffect(() => {
|
|
@@ -476,7 +455,7 @@ function UserList() {
|
|
|
476
455
|
}, [])
|
|
477
456
|
}
|
|
478
457
|
|
|
479
|
-
// ✅
|
|
458
|
+
// ✅ 여러 인스턴스가 하나의 요청 공유
|
|
480
459
|
function UserList() {
|
|
481
460
|
const { data: users } = useQuery({
|
|
482
461
|
queryKey: ['users'],
|
|
@@ -496,7 +475,7 @@ function UpdateButton() {
|
|
|
496
475
|
const mutation = useMutation({
|
|
497
476
|
mutationFn: updateUser,
|
|
498
477
|
onSuccess: () => {
|
|
499
|
-
//
|
|
478
|
+
// 캐시 무효화로 자동 재페칭
|
|
500
479
|
queryClient.invalidateQueries({ queryKey: ['users'] })
|
|
501
480
|
}
|
|
502
481
|
})
|
|
@@ -515,55 +494,55 @@ function UpdateButton() {
|
|
|
515
494
|
|
|
516
495
|
<rerender_optimization>
|
|
517
496
|
|
|
518
|
-
## 5. Re-render
|
|
497
|
+
## 5. Re-render 최적화 (MEDIUM)
|
|
519
498
|
|
|
520
|
-
###
|
|
499
|
+
### 함수형 setState
|
|
521
500
|
|
|
522
501
|
```tsx
|
|
523
|
-
// ❌ items
|
|
502
|
+
// ❌ items가 의존성, 매번 재생성
|
|
524
503
|
function TodoList() {
|
|
525
504
|
const [items, setItems] = useState(initialItems)
|
|
526
505
|
|
|
527
506
|
const addItems = useCallback((newItems: Item[]) => {
|
|
528
507
|
setItems([...items, ...newItems])
|
|
529
|
-
}, [items]) //
|
|
508
|
+
}, [items]) // items 변경마다 재생성
|
|
530
509
|
}
|
|
531
510
|
|
|
532
|
-
// ✅
|
|
511
|
+
// ✅ 안정적 콜백, 재생성 없음
|
|
533
512
|
function TodoList() {
|
|
534
513
|
const [items, setItems] = useState(initialItems)
|
|
535
514
|
|
|
536
515
|
const addItems = useCallback((newItems: Item[]) => {
|
|
537
516
|
setItems(curr => [...curr, ...newItems])
|
|
538
|
-
}, []) //
|
|
517
|
+
}, []) // 의존성 없음, 항상 최신 상태 사용
|
|
539
518
|
}
|
|
540
519
|
```
|
|
541
520
|
|
|
542
|
-
### Lazy
|
|
521
|
+
### Lazy 상태 초기화
|
|
543
522
|
|
|
544
523
|
```tsx
|
|
545
|
-
// ❌
|
|
524
|
+
// ❌ 매 렌더마다 실행
|
|
546
525
|
function FilteredList({ items }: { items: Item[] }) {
|
|
547
526
|
const [searchIndex, setSearchIndex] = useState(buildSearchIndex(items))
|
|
548
527
|
}
|
|
549
528
|
|
|
550
|
-
// ✅
|
|
529
|
+
// ✅ 초기 렌더 시에만 실행
|
|
551
530
|
function FilteredList({ items }: { items: Item[] }) {
|
|
552
531
|
const [searchIndex, setSearchIndex] = useState(() => buildSearchIndex(items))
|
|
553
532
|
}
|
|
554
533
|
```
|
|
555
534
|
|
|
556
|
-
###
|
|
535
|
+
### 파생 상태 구독
|
|
557
536
|
|
|
558
537
|
```tsx
|
|
559
|
-
// ❌
|
|
538
|
+
// ❌ 픽셀 변경마다 re-render
|
|
560
539
|
function Sidebar() {
|
|
561
|
-
const width = useWindowWidth() //
|
|
540
|
+
const width = useWindowWidth() // 연속 업데이트
|
|
562
541
|
const isMobile = width < 768
|
|
563
542
|
return <nav className={isMobile ? 'mobile' : 'desktop'}>
|
|
564
543
|
}
|
|
565
544
|
|
|
566
|
-
// ✅
|
|
545
|
+
// ✅ boolean 변경 시에만 re-render
|
|
567
546
|
function Sidebar() {
|
|
568
547
|
const isMobile = useMediaQuery('(max-width: 767px)')
|
|
569
548
|
return <nav className={isMobile ? 'mobile' : 'desktop'}>
|
|
@@ -576,12 +555,12 @@ function Sidebar() {
|
|
|
576
555
|
|
|
577
556
|
<rendering_performance>
|
|
578
557
|
|
|
579
|
-
## 6.
|
|
558
|
+
## 6. 렌더링 성능 (MEDIUM)
|
|
580
559
|
|
|
581
|
-
###
|
|
560
|
+
### SVG 래퍼 애니메이션
|
|
582
561
|
|
|
583
562
|
```tsx
|
|
584
|
-
// ❌
|
|
563
|
+
// ❌ SVG 직접 애니메이션 - 하드웨어 가속 없음
|
|
585
564
|
function LoadingSpinner() {
|
|
586
565
|
return (
|
|
587
566
|
<svg className="animate-spin" width="24" height="24">
|
|
@@ -590,7 +569,7 @@ function LoadingSpinner() {
|
|
|
590
569
|
)
|
|
591
570
|
}
|
|
592
571
|
|
|
593
|
-
// ✅
|
|
572
|
+
// ✅ 래퍼 div 애니메이션 - 하드웨어 가속
|
|
594
573
|
function LoadingSpinner() {
|
|
595
574
|
return (
|
|
596
575
|
<div className="animate-spin">
|
|
@@ -626,12 +605,12 @@ function MessageList({ messages }: { messages: Message[] }) {
|
|
|
626
605
|
}
|
|
627
606
|
```
|
|
628
607
|
|
|
629
|
-
|
|
608
|
+
1000개 메시지 기준, 브라우저가 ~990개 오프스크린 항목의 레이아웃/페인트 스킵 (초기 렌더 10배 빠름)
|
|
630
609
|
|
|
631
610
|
### JSX Hoisting
|
|
632
611
|
|
|
633
612
|
```tsx
|
|
634
|
-
// ❌
|
|
613
|
+
// ❌ 매 렌더마다 재생성
|
|
635
614
|
function Container() {
|
|
636
615
|
return (
|
|
637
616
|
<div>
|
|
@@ -640,7 +619,7 @@ function Container() {
|
|
|
640
619
|
)
|
|
641
620
|
}
|
|
642
621
|
|
|
643
|
-
// ✅
|
|
622
|
+
// ✅ 한 번만 생성
|
|
644
623
|
const loadingSkeleton = (
|
|
645
624
|
<div className="animate-pulse h-20 bg-gray-200" />
|
|
646
625
|
)
|
|
@@ -660,9 +639,9 @@ function Container() {
|
|
|
660
639
|
|
|
661
640
|
<javascript_performance>
|
|
662
641
|
|
|
663
|
-
## 7. JavaScript
|
|
642
|
+
## 7. JavaScript 성능 (LOW-MEDIUM)
|
|
664
643
|
|
|
665
|
-
###
|
|
644
|
+
### 반복 조회용 Map
|
|
666
645
|
|
|
667
646
|
```typescript
|
|
668
647
|
// ❌ O(n) per lookup
|
|
@@ -685,15 +664,15 @@ function processOrders(orders: Order[], users: User[]) {
|
|
|
685
664
|
|
|
686
665
|
1000 orders × 1000 users: 1M ops → 2K ops
|
|
687
666
|
|
|
688
|
-
###
|
|
667
|
+
### 배열 비교 시 길이 체크
|
|
689
668
|
|
|
690
669
|
```typescript
|
|
691
|
-
// ❌
|
|
670
|
+
// ❌ 항상 비싼 비교 실행
|
|
692
671
|
function hasChanges(current: string[], original: string[]) {
|
|
693
672
|
return current.sort().join() !== original.sort().join()
|
|
694
673
|
}
|
|
695
674
|
|
|
696
|
-
// ✅ O(1)
|
|
675
|
+
// ✅ O(1) 길이 체크 먼저
|
|
697
676
|
function hasChanges(current: string[], original: string[]) {
|
|
698
677
|
if (current.length !== original.length) return true
|
|
699
678
|
|
|
@@ -706,10 +685,10 @@ function hasChanges(current: string[], original: string[]) {
|
|
|
706
685
|
}
|
|
707
686
|
```
|
|
708
687
|
|
|
709
|
-
###
|
|
688
|
+
### toSorted()로 불변성 유지
|
|
710
689
|
|
|
711
690
|
```typescript
|
|
712
|
-
// ❌
|
|
691
|
+
// ❌ 원본 배열 변경
|
|
713
692
|
function UserList({ users }: { users: User[] }) {
|
|
714
693
|
const sorted = useMemo(
|
|
715
694
|
() => users.sort((a, b) => a.name.localeCompare(b.name)),
|
|
@@ -717,7 +696,7 @@ function UserList({ users }: { users: User[] }) {
|
|
|
717
696
|
)
|
|
718
697
|
}
|
|
719
698
|
|
|
720
|
-
// ✅
|
|
699
|
+
// ✅ 새 배열 생성
|
|
721
700
|
function UserList({ users }: { users: User[] }) {
|
|
722
701
|
const sorted = useMemo(
|
|
723
702
|
() => users.toSorted((a, b) => a.name.localeCompare(b.name)),
|
|
@@ -732,9 +711,9 @@ function UserList({ users }: { users: User[] }) {
|
|
|
732
711
|
|
|
733
712
|
<references>
|
|
734
713
|
|
|
735
|
-
##
|
|
714
|
+
## 참고 자료
|
|
736
715
|
|
|
737
|
-
### TanStack
|
|
716
|
+
### TanStack 공식 문서
|
|
738
717
|
1. [React](https://react.dev)
|
|
739
718
|
2. [TanStack Start Overview](https://tanstack.com/start/latest/docs/framework/react/overview)
|
|
740
719
|
3. [TanStack Start Quick Start](https://tanstack.com/start/latest/docs/framework/react/quick-start)
|
|
@@ -743,7 +722,7 @@ function UserList({ users }: { users: User[] }) {
|
|
|
743
722
|
6. [TanStack Query](https://tanstack.com/query)
|
|
744
723
|
7. [Server Functions Guide](https://tanstack.com/start/latest/docs/framework/react/guide/server-functions)
|
|
745
724
|
|
|
746
|
-
###
|
|
725
|
+
### 외부 자료
|
|
747
726
|
8. [better-all](https://github.com/shuding/better-all)
|
|
748
727
|
9. [node-lru-cache](https://github.com/isaacs/node-lru-cache)
|
|
749
728
|
10. [Using Server Functions and TanStack Query](https://www.brenelz.com/posts/using-server-functions-and-tanstack-query/)
|