@kood/claude-code 0.6.5 → 0.6.7
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 +255 -149
- package/package.json +1 -1
- package/templates/.claude/agents/researcher.md +8 -1
- package/templates/.claude/instructions/sourcing/reliable-search.md +49 -2
- package/templates/.claude/scripts/deploy/build-run.sh +36 -0
- package/templates/.claude/scripts/deploy/deploy-check.sh +38 -0
- package/templates/.claude/scripts/git/git-all.sh +57 -0
- package/templates/.claude/scripts/git/git-clean-check.sh +31 -0
- package/templates/.claude/scripts/git/git-commit.sh +51 -0
- package/templates/.claude/scripts/git/git-info.sh +34 -0
- package/templates/.claude/scripts/git/git-push.sh +50 -0
- package/templates/.claude/scripts/lint/lint-check.sh +56 -0
- package/templates/.claude/scripts/lint/lint-file.sh +41 -0
- package/templates/.claude/scripts/pm/pm-detect.sh +25 -0
- package/templates/.claude/scripts/pm/pm-run.sh +41 -0
- package/templates/.claude/scripts/version/version-bump.sh +54 -0
- package/templates/.claude/scripts/version/version-find.sh +49 -0
- package/templates/.claude/skills/docs-fetch/SKILL.md +5 -4
- package/templates/.claude/skills/project-optimizer/AGENTS.md +275 -0
- package/templates/.claude/skills/project-optimizer/SKILL.md +374 -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/sql-optimizer/SKILL.md +437 -0
- package/templates/.claude/skills/sql-optimizer/orm-patterns.md +218 -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 +93 -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 +570 -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,218 @@
|
|
|
1
|
+
# ORM별 최적화 패턴
|
|
2
|
+
|
|
3
|
+
> SQL Optimizer 스킬에서 참조하는 ORM별 안티패턴 + 수정 코드
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Prisma (v5+, select/omit 지원)
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
// ❌ C1: N+1 - include 없이 루프 접근
|
|
11
|
+
const users = await prisma.user.findMany()
|
|
12
|
+
for (const user of users) {
|
|
13
|
+
const posts = await prisma.post.findMany({ where: { authorId: user.id } })
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// ✅ 수정: select로 필요 필드만 (v5+)
|
|
17
|
+
const users = await prisma.user.findMany({
|
|
18
|
+
select: {
|
|
19
|
+
id: true,
|
|
20
|
+
name: true,
|
|
21
|
+
posts: {
|
|
22
|
+
select: { id: true, title: true },
|
|
23
|
+
take: 10,
|
|
24
|
+
orderBy: { createdAt: 'desc' }
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
// ❌ H4: include 깊은 중첩 (3단계+)
|
|
30
|
+
await prisma.user.findMany({
|
|
31
|
+
include: { posts: { include: { comments: { include: { author: true } } } } }
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
// ✅ 수정: select + 필요 깊이만 + _count
|
|
35
|
+
await prisma.user.findMany({
|
|
36
|
+
select: {
|
|
37
|
+
id: true,
|
|
38
|
+
posts: {
|
|
39
|
+
select: {
|
|
40
|
+
id: true,
|
|
41
|
+
title: true,
|
|
42
|
+
_count: { select: { comments: true } }
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
// ❌ C4: 배치 없이 대량 생성
|
|
49
|
+
for (const item of items) {
|
|
50
|
+
await prisma.item.create({ data: item })
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// ✅ 수정: createMany 배치
|
|
54
|
+
await prisma.item.createMany({ data: items })
|
|
55
|
+
|
|
56
|
+
// ❌ L2: Serverless 환경 매번 new PrismaClient
|
|
57
|
+
export async function handler() {
|
|
58
|
+
const prisma = new PrismaClient() // 연결 풀 소모
|
|
59
|
+
return prisma.user.findMany()
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// ✅ 수정: 전역 인스턴스 재사용
|
|
63
|
+
const prisma = new PrismaClient()
|
|
64
|
+
export async function handler() {
|
|
65
|
+
return prisma.user.findMany()
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Prisma 버전별 차이
|
|
70
|
+
|
|
71
|
+
| 기능 | v5 | v6 | v7 |
|
|
72
|
+
|------|-----|-----|-----|
|
|
73
|
+
| `select` | ✅ | ✅ | ✅ |
|
|
74
|
+
| `omit` | ✅ (Preview) | ✅ | ✅ |
|
|
75
|
+
| `relationLoadStrategy: 'join'` | ✅ (v5.9+) | ✅ | ✅ |
|
|
76
|
+
| Pure TS Client (Rust 제거) | ❌ | ❌ | ✅ |
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Drizzle
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
// ❌ M6: prepared statement 미사용 (반복 쿼리)
|
|
84
|
+
for (const id of userIds) {
|
|
85
|
+
await db.select().from(users).where(eq(users.id, id))
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// ✅ 수정: prepare + IN 조건 (PostgreSQL named prepared statement)
|
|
89
|
+
const getUsers = db
|
|
90
|
+
.select({ id: users.id, name: users.name })
|
|
91
|
+
.from(users)
|
|
92
|
+
.where(inArray(users.id, sql.placeholder('ids')))
|
|
93
|
+
.prepare('get_users_batch')
|
|
94
|
+
|
|
95
|
+
await getUsers.execute({ ids: userIds })
|
|
96
|
+
|
|
97
|
+
// ❌ C4: select() 전체 컬럼
|
|
98
|
+
const result = await db.select().from(users)
|
|
99
|
+
|
|
100
|
+
// ✅ 수정: partial select
|
|
101
|
+
const result = await db
|
|
102
|
+
.select({ id: users.id, email: users.email })
|
|
103
|
+
.from(users)
|
|
104
|
+
|
|
105
|
+
// ✅ JSON 집계 (N+1 방지, 단일 쿼리)
|
|
106
|
+
const usersWithPosts = await db
|
|
107
|
+
.select({
|
|
108
|
+
id: users.id,
|
|
109
|
+
name: users.name,
|
|
110
|
+
posts: sql`json_agg(${posts}) FILTER (WHERE ${posts.id} IS NOT NULL)`
|
|
111
|
+
})
|
|
112
|
+
.from(users)
|
|
113
|
+
.leftJoin(posts, eq(users.id, posts.userId))
|
|
114
|
+
.groupBy(users.id)
|
|
115
|
+
.limit(20)
|
|
116
|
+
.prepare('get_users_with_posts')
|
|
117
|
+
.execute()
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## TypeORM
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
// ❌ H5: lazy loading 루프
|
|
126
|
+
const users = await User.find()
|
|
127
|
+
for (const user of users) {
|
|
128
|
+
const posts = await user.posts // Promise<Post[]> - 매번 쿼리
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// ✅ 수정: leftJoinAndSelect + 필드 선택
|
|
132
|
+
const users = await createQueryBuilder('user')
|
|
133
|
+
.leftJoinAndSelect('user.posts', 'post')
|
|
134
|
+
.select(['user.id', 'user.name', 'post.id', 'post.title'])
|
|
135
|
+
.getMany()
|
|
136
|
+
|
|
137
|
+
// ✅ 성능 극대화: getRawMany (3.5x 빠름, 엔티티 변환 생략)
|
|
138
|
+
const users = await createQueryBuilder('user')
|
|
139
|
+
.leftJoin('user.posts', 'post')
|
|
140
|
+
.select(['user.id', 'user.name', 'post.id', 'post.title'])
|
|
141
|
+
.getRawMany()
|
|
142
|
+
|
|
143
|
+
// ✅ 배치 로드 (IN 쿼리, N+1 대안)
|
|
144
|
+
const users = await User.find()
|
|
145
|
+
const userIds = users.map(u => u.id)
|
|
146
|
+
const posts = await Post.find({ where: { userId: In(userIds) } })
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## Raw SQL
|
|
152
|
+
|
|
153
|
+
```sql
|
|
154
|
+
-- ❌ H2: WHERE IN 서브쿼리
|
|
155
|
+
SELECT * FROM orders
|
|
156
|
+
WHERE customer_id IN (SELECT id FROM customers WHERE country = 'KR');
|
|
157
|
+
|
|
158
|
+
-- ✅ 수정: JOIN + 필요 컬럼만
|
|
159
|
+
SELECT o.id, o.total, o.created_at
|
|
160
|
+
FROM orders o
|
|
161
|
+
INNER JOIN customers c ON o.customer_id = c.id
|
|
162
|
+
WHERE c.country = 'KR';
|
|
163
|
+
|
|
164
|
+
-- ❌ M3: 집계 서브쿼리 (Window Function 미사용)
|
|
165
|
+
SELECT *, (SELECT COUNT(*) FROM orders o WHERE o.user_id = u.id) as order_count
|
|
166
|
+
FROM users u;
|
|
167
|
+
|
|
168
|
+
-- ✅ 수정: LEFT JOIN + GROUP BY
|
|
169
|
+
SELECT u.id, u.name, COUNT(o.id) as order_count
|
|
170
|
+
FROM users u
|
|
171
|
+
LEFT JOIN orders o ON u.id = o.user_id
|
|
172
|
+
GROUP BY u.id, u.name;
|
|
173
|
+
|
|
174
|
+
-- ❌ M4: Partial Index 미적용
|
|
175
|
+
SELECT * FROM orders WHERE status = 'pending' AND created_at > NOW() - INTERVAL '7 days';
|
|
176
|
+
-- CREATE INDEX idx_orders_created ON orders(created_at); -- 전체 인덱스
|
|
177
|
+
|
|
178
|
+
-- ✅ 수정: Partial Index (최대 275x 성능 향상)
|
|
179
|
+
CREATE INDEX idx_orders_pending ON orders(created_at)
|
|
180
|
+
WHERE status = 'pending';
|
|
181
|
+
|
|
182
|
+
-- ❌ M1: DISTINCT 마스킹 (잘못된 JOIN 은폐)
|
|
183
|
+
SELECT DISTINCT u.* FROM users u
|
|
184
|
+
JOIN orders o ON u.id = o.user_id;
|
|
185
|
+
|
|
186
|
+
-- ✅ 수정: JOIN 로직 수정 또는 EXISTS
|
|
187
|
+
SELECT u.* FROM users u
|
|
188
|
+
WHERE EXISTS (SELECT 1 FROM orders o WHERE o.user_id = u.id);
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## 대량 데이터 로드 최적화
|
|
194
|
+
|
|
195
|
+
```sql
|
|
196
|
+
-- Step 1: 트랜잭션 시작
|
|
197
|
+
BEGIN;
|
|
198
|
+
|
|
199
|
+
-- Step 2: 인덱스 임시 제거 + 트리거 비활성화
|
|
200
|
+
DROP INDEX IF EXISTS idx_target;
|
|
201
|
+
ALTER TABLE target DISABLE TRIGGER ALL;
|
|
202
|
+
|
|
203
|
+
-- Step 3: 메모리 설정 증가
|
|
204
|
+
SET maintenance_work_mem = '1GB';
|
|
205
|
+
|
|
206
|
+
-- Step 4: COPY 사용 (INSERT 대비 10x 빠름)
|
|
207
|
+
COPY target FROM STDIN;
|
|
208
|
+
|
|
209
|
+
-- Step 5: 트리거 복구 + 인덱스 재생성
|
|
210
|
+
ALTER TABLE target ENABLE TRIGGER ALL;
|
|
211
|
+
CREATE INDEX idx_target ON target(col);
|
|
212
|
+
|
|
213
|
+
-- Step 6: 트랜잭션 완료
|
|
214
|
+
COMMIT;
|
|
215
|
+
|
|
216
|
+
-- Step 7: 통계 업데이트 (트랜잭션 외부에서 실행)
|
|
217
|
+
ANALYZE target;
|
|
218
|
+
```
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# TanStack Start React 베스트 프랙티스
|
|
2
2
|
|
|
3
|
-
**Version
|
|
4
|
-
TanStack Start 최적화 가이드
|
|
5
|
-
|
|
3
|
+
**Version 3.0.0**
|
|
4
|
+
TanStack Start v1 + TanStack Router + React 19 최적화 가이드
|
|
5
|
+
February 2026
|
|
6
6
|
|
|
7
7
|
> **참고:**
|
|
8
8
|
> 이 문서는 주로 에이전트와 LLM이 React 및 TanStack Start 코드베이스를 유지보수, 생성, 리팩토링할 때 따르기 위한 것입니다. 사람도 유용하게 사용할 수 있지만, AI 지원 워크플로의 자동화 및 일관성을 위해 최적화되어 있습니다.
|
|
@@ -11,7 +11,7 @@ January 2026
|
|
|
11
11
|
|
|
12
12
|
## 요약
|
|
13
13
|
|
|
14
|
-
AI 에이전트와 LLM을 위한 React 및 TanStack Start 애플리케이션 종합 성능 최적화 가이드.
|
|
14
|
+
AI 에이전트와 LLM을 위한 React 19 및 TanStack Start v1 애플리케이션 종합 성능 최적화 가이드. 8개 카테고리에 걸쳐 55개 규칙을 포함하며, 영향도별로 우선순위를 매겼습니다 (critical: waterfall 제거, 번들 크기 감소 → incremental: JavaScript 성능). TanStack Router 라우팅 패턴(Link, useNavigate, useSearch, useParams, beforeLoad, Outlet, pendingComponent, 프리로딩, 파일 컨벤션), React Compiler 자동 메모이제이션, use() hook, useOptimistic, createMiddleware, inputValidator, 데이터 스트리밍 등 최신 패턴 반영. 각 규칙은 자동 리팩토링 및 코드 생성을 위한 상세한 설명, 올바른 구현 대 잘못된 구현을 비교하는 실제 예시, 구체적인 영향 지표를 포함합니다.
|
|
15
15
|
|
|
16
16
|
---
|
|
17
17
|
|
|
@@ -32,10 +32,27 @@ AI 에이전트와 LLM을 위한 React 및 TanStack Start 애플리케이션 종
|
|
|
32
32
|
@rules/server-serialization.md
|
|
33
33
|
@rules/server-parallel-fetching.md
|
|
34
34
|
@rules/server-deferred-data.md
|
|
35
|
+
@rules/server-middleware.md
|
|
36
|
+
@rules/server-validator.md
|
|
37
|
+
@rules/server-streaming.md
|
|
38
|
+
@rules/server-error-boundaries.md
|
|
35
39
|
@rules/client-tanstack-query.md
|
|
40
|
+
@rules/client-suspense-query.md
|
|
41
|
+
@rules/client-optimistic-updates.md
|
|
42
|
+
@rules/client-use-hook.md
|
|
36
43
|
@rules/client-event-listeners.md
|
|
44
|
+
@rules/routing-file-conventions.md
|
|
45
|
+
@rules/routing-link-navigation.md
|
|
46
|
+
@rules/routing-search-params.md
|
|
47
|
+
@rules/routing-path-params.md
|
|
48
|
+
@rules/routing-beforeload-auth.md
|
|
49
|
+
@rules/routing-nested-layouts.md
|
|
50
|
+
@rules/routing-router-context.md
|
|
51
|
+
@rules/routing-preload-strategy.md
|
|
52
|
+
@rules/routing-pending-component.md
|
|
37
53
|
@rules/rerender-defer-reads.md
|
|
38
54
|
@rules/rerender-memo.md
|
|
55
|
+
@rules/rerender-react-compiler.md
|
|
39
56
|
@rules/rerender-dependencies.md
|
|
40
57
|
@rules/rerender-derived-state.md
|
|
41
58
|
@rules/rerender-functional-setstate.md
|
|
@@ -71,11 +88,12 @@ AI 에이전트와 LLM을 위한 React 및 TanStack Start 애플리케이션 종
|
|
|
71
88
|
|---------|---------|--------|------|
|
|
72
89
|
| 1 | Waterfall 제거 | **CRITICAL** | 순차 await를 병렬로 전환. 가장 큰 성능 향상 제공 |
|
|
73
90
|
| 2 | 번들 크기 최적화 | **CRITICAL** | TTI와 LCP 개선. 초기 로딩 속도 향상 |
|
|
74
|
-
| 3 |
|
|
75
|
-
| 4 |
|
|
76
|
-
| 5 |
|
|
77
|
-
| 6 |
|
|
78
|
-
| 7 |
|
|
91
|
+
| 3 | TanStack Router 라우팅 | **HIGH** | 타입 안전 네비게이션, 인증 가드, 레이아웃, 프리로딩 |
|
|
92
|
+
| 4 | 서버 사이드 성능 | HIGH | 서버 사이드 waterfall 제거, 응답 시간 단축 |
|
|
93
|
+
| 5 | 클라이언트 데이터 페칭 | MEDIUM-HIGH | 자동 중복 제거, 효율적 데이터 페칭 |
|
|
94
|
+
| 6 | Re-render 최적화 | MEDIUM | 불필요한 re-render 최소화, UI 반응성 향상 |
|
|
95
|
+
| 7 | 렌더링 성능 | MEDIUM | 브라우저 렌더링 작업 최적화 |
|
|
96
|
+
| 8 | JavaScript 성능 | LOW-MEDIUM | Hot path 마이크로 최적화 |
|
|
79
97
|
|
|
80
98
|
</categories>
|
|
81
99
|
|
|
@@ -718,13 +736,34 @@ function UserList({ users }: { users: User[] }) {
|
|
|
718
736
|
2. [TanStack Start Overview](https://tanstack.com/start/latest/docs/framework/react/overview)
|
|
719
737
|
3. [TanStack Start Quick Start](https://tanstack.com/start/latest/docs/framework/react/quick-start)
|
|
720
738
|
4. [TanStack Router](https://tanstack.com/router)
|
|
721
|
-
5. [TanStack Router Deferred Data Loading](https://tanstack.com/router/
|
|
722
|
-
6. [TanStack Query](https://tanstack.com/query)
|
|
739
|
+
5. [TanStack Router Deferred Data Loading](https://tanstack.com/router/latest/docs/framework/react/guide/deferred-data-loading)
|
|
740
|
+
6. [TanStack Query](https://tanstack.com/query/latest/docs/framework/react/overview)
|
|
723
741
|
7. [Server Functions Guide](https://tanstack.com/start/latest/docs/framework/react/guide/server-functions)
|
|
742
|
+
8. [Middleware Guide](https://tanstack.com/start/latest/docs/framework/react/guide/middleware)
|
|
743
|
+
9. [Streaming Data Guide](https://tanstack.com/start/latest/docs/framework/react/guide/streaming-data-from-server-functions)
|
|
744
|
+
|
|
745
|
+
### TanStack Router
|
|
746
|
+
10. [Navigation Guide](https://tanstack.com/router/latest/docs/framework/react/guide/navigation)
|
|
747
|
+
11. [Link Options](https://tanstack.com/router/latest/docs/framework/react/guide/link-options)
|
|
748
|
+
12. [Search Params](https://tanstack.com/router/latest/docs/framework/react/guide/search-params)
|
|
749
|
+
13. [Path Params](https://tanstack.com/router/latest/docs/framework/react/guide/path-params)
|
|
750
|
+
14. [Authenticated Routes](https://tanstack.com/router/latest/docs/framework/react/guide/authenticated-routes)
|
|
751
|
+
15. [Outlets](https://tanstack.com/router/latest/docs/framework/react/guide/outlets)
|
|
752
|
+
16. [Route Trees](https://tanstack.com/router/latest/docs/framework/react/guide/route-trees)
|
|
753
|
+
17. [File-Based Routing](https://tanstack.com/router/latest/docs/framework/react/routing/file-based-routing)
|
|
754
|
+
18. [Router Context](https://tanstack.com/router/latest/docs/framework/react/guide/router-context)
|
|
755
|
+
19. [Preloading](https://tanstack.com/router/latest/docs/framework/react/guide/preloading)
|
|
756
|
+
20. [Data Loading](https://tanstack.com/router/latest/docs/framework/react/guide/data-loading)
|
|
757
|
+
|
|
758
|
+
### React 19
|
|
759
|
+
21. [React 19 Blog](https://react.dev/blog/2024/12/05/react-19)
|
|
760
|
+
22. [React use() API](https://react.dev/reference/react/use)
|
|
761
|
+
23. [React useOptimistic](https://react.dev/reference/react/useOptimistic)
|
|
762
|
+
24. [React Compiler](https://react.dev/learn/react-compiler)
|
|
724
763
|
|
|
725
764
|
### 외부 자료
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
765
|
+
25. [better-all](https://github.com/shuding/better-all)
|
|
766
|
+
26. [node-lru-cache](https://github.com/isaacs/node-lru-cache)
|
|
767
|
+
27. [Using Server Functions and TanStack Query](https://www.brenelz.com/posts/using-server-functions-and-tanstack-query/)
|
|
729
768
|
|
|
730
769
|
</references>
|
|
@@ -5,7 +5,7 @@ license: MIT
|
|
|
5
5
|
framework: tanstack-start
|
|
6
6
|
metadata:
|
|
7
7
|
author: vercel
|
|
8
|
-
version: "
|
|
8
|
+
version: "3.0.0"
|
|
9
9
|
adapted_for: tanstack-start
|
|
10
10
|
---
|
|
11
11
|
|
|
@@ -17,7 +17,7 @@ metadata:
|
|
|
17
17
|
|
|
18
18
|
# TanStack Start React 베스트 프랙티스
|
|
19
19
|
|
|
20
|
-
React와 TanStack Start 애플리케이션 성능 최적화 가이드.
|
|
20
|
+
React 19와 TanStack Start v1 애플리케이션 성능 최적화 가이드. 8개 카테고리, 55개 규칙 포함. 영향도별 우선순위로 자동 리팩토링과 코드 생성 가이드 제공. TanStack Router 라우팅 패턴(Link, useNavigate, useSearch, useParams, beforeLoad, Outlet, pendingComponent, 프리로딩), React Compiler, use() hook, useOptimistic, Middleware, Streaming 등 최신 패턴 반영.
|
|
21
21
|
|
|
22
22
|
---
|
|
23
23
|
|
|
@@ -188,11 +188,12 @@ Task(subagent_type="implementation-executor", model="sonnet",
|
|
|
188
188
|
|---------|---------|--------|--------|
|
|
189
189
|
| 1 | Waterfall 제거 | **CRITICAL** | `async-` |
|
|
190
190
|
| 2 | 번들 크기 최적화 | **CRITICAL** | `bundle-` |
|
|
191
|
-
| 3 |
|
|
192
|
-
| 4 |
|
|
193
|
-
| 5 |
|
|
194
|
-
| 6 |
|
|
195
|
-
| 7 |
|
|
191
|
+
| 3 | TanStack Router 라우팅 | **HIGH** | `routing-` |
|
|
192
|
+
| 4 | 서버 사이드 성능 | HIGH | `server-` |
|
|
193
|
+
| 5 | 클라이언트 데이터 페칭 | MEDIUM-HIGH | `client-` |
|
|
194
|
+
| 6 | Re-render 최적화 | MEDIUM | `rerender-` |
|
|
195
|
+
| 7 | 렌더링 성능 | MEDIUM | `rendering-` |
|
|
196
|
+
| 8 | JavaScript 성능 | LOW-MEDIUM | `js-` |
|
|
196
197
|
|
|
197
198
|
</categories>
|
|
198
199
|
|
|
@@ -221,35 +222,57 @@ Task(subagent_type="implementation-executor", model="sonnet",
|
|
|
221
222
|
| `bundle-conditional` | 기능 활성화 시에만 모듈 로드 |
|
|
222
223
|
| `bundle-preload` | hover/focus 시 preload로 체감 속도 향상 |
|
|
223
224
|
|
|
224
|
-
### 3.
|
|
225
|
+
### 3. TanStack Router 라우팅 (HIGH)
|
|
226
|
+
|
|
227
|
+
| 규칙 | 설명 |
|
|
228
|
+
|------|------|
|
|
229
|
+
| `routing-file-conventions` | 파일 기반 라우팅 컨벤션 (__root, $param, _layout, lazy) |
|
|
230
|
+
| `routing-link-navigation` | Link 컴포넌트와 useNavigate로 타입 안전 네비게이션 |
|
|
231
|
+
| `routing-search-params` | useSearch + Zod 스키마로 search params 검증 |
|
|
232
|
+
| `routing-path-params` | useParams와 getRouteApi로 타입 안전 path params |
|
|
233
|
+
| `routing-beforeload-auth` | beforeLoad로 인증 가드, pathless layout 조합 |
|
|
234
|
+
| `routing-nested-layouts` | Outlet과 pathless layout으로 중첩 UI |
|
|
235
|
+
| `routing-router-context` | createRootRouteWithContext로 의존성 주입 |
|
|
236
|
+
| `routing-preload-strategy` | Link preload="intent"로 즉각적 네비게이션 |
|
|
237
|
+
| `routing-pending-component` | pendingComponent로 로딩 상태 (pendingMs/pendingMinMs) |
|
|
238
|
+
|
|
239
|
+
### 4. 서버 사이드 성능 (HIGH)
|
|
225
240
|
|
|
226
241
|
| 규칙 | 설명 |
|
|
227
242
|
|------|------|
|
|
228
243
|
| `server-cache-lru` | LRU 캐시로 요청 간 캐싱 |
|
|
229
|
-
| `server-serialization` |
|
|
244
|
+
| `server-serialization` | loader 데이터 직렬화 최소화 |
|
|
230
245
|
| `server-parallel-fetching` | loader에서 병렬 데이터 페칭 |
|
|
231
|
-
| `server-deferred-data` |
|
|
246
|
+
| `server-deferred-data` | Promise 반환으로 비차단 데이터 로딩 |
|
|
247
|
+
| `server-middleware` | createMiddleware()로 인증/로깅 중앙화 |
|
|
248
|
+
| `server-validator` | inputValidator()로 타입 안전한 Server Functions |
|
|
249
|
+
| `server-streaming` | async generator/ReadableStream으로 데이터 스트리밍 |
|
|
250
|
+
| `server-error-boundaries` | 라우트 레벨 errorComponent/notFoundComponent |
|
|
232
251
|
|
|
233
|
-
###
|
|
252
|
+
### 5. 클라이언트 데이터 페칭 (MEDIUM-HIGH)
|
|
234
253
|
|
|
235
254
|
| 규칙 | 설명 |
|
|
236
255
|
|------|------|
|
|
237
256
|
| `client-tanstack-query` | TanStack Query로 자동 캐싱/중복 제거 |
|
|
257
|
+
| `client-suspense-query` | useSuspenseQuery로 선언적 데이터 로딩 (v5) |
|
|
258
|
+
| `client-optimistic-updates` | useOptimistic으로 즉각적 UI 피드백 (React 19) |
|
|
259
|
+
| `client-use-hook` | use() hook으로 Promise 처리 (React 19) |
|
|
238
260
|
| `client-event-listeners` | 전역 이벤트 리스너 중복 제거 |
|
|
239
261
|
|
|
240
|
-
###
|
|
262
|
+
### 6. Re-render 최적화 (MEDIUM)
|
|
241
263
|
|
|
242
264
|
| 규칙 | 설명 |
|
|
243
265
|
|------|------|
|
|
244
266
|
| `rerender-defer-reads` | 콜백 전용 상태는 구독 안 함 |
|
|
245
267
|
| `rerender-memo` | 비싼 작업은 memoized 컴포넌트로 추출 |
|
|
268
|
+
| `rerender-react-compiler` | React Compiler로 자동 메모이제이션 (React 19) |
|
|
246
269
|
| `rerender-dependencies` | effect에 원시값 의존성 사용 |
|
|
247
270
|
| `rerender-derived-state` | 파생 boolean 구독, raw 값 구독 회피 |
|
|
248
271
|
| `rerender-functional-setstate` | 안정적 콜백용 함수형 setState |
|
|
249
272
|
| `rerender-lazy-state-init` | 비싼 초기값은 함수로 useState에 전달 |
|
|
250
273
|
| `rerender-transitions` | 비긴급 업데이트는 startTransition |
|
|
251
274
|
|
|
252
|
-
###
|
|
275
|
+
### 7. 렌더링 성능 (MEDIUM)
|
|
253
276
|
|
|
254
277
|
| 규칙 | 설명 |
|
|
255
278
|
|------|------|
|
|
@@ -259,7 +282,7 @@ Task(subagent_type="implementation-executor", model="sonnet",
|
|
|
259
282
|
| `rendering-svg-precision` | SVG 좌표 정밀도 감소 |
|
|
260
283
|
| `rendering-conditional-render` | 조건부 렌더링은 &&가 아닌 삼항 연산자 |
|
|
261
284
|
|
|
262
|
-
###
|
|
285
|
+
### 8. JavaScript 성능 (LOW-MEDIUM)
|
|
263
286
|
|
|
264
287
|
| 규칙 | 설명 |
|
|
265
288
|
|------|------|
|
|
@@ -400,26 +423,46 @@ const addItems = useCallback((newItems: Item[]) => {
|
|
|
400
423
|
import { createServerFn } from '@tanstack/react-start'
|
|
401
424
|
import { z } from 'zod'
|
|
402
425
|
|
|
403
|
-
// ✅ 기본 Server Function
|
|
426
|
+
// ✅ 기본 Server Function (GET)
|
|
404
427
|
const getUser = createServerFn().handler(async () => {
|
|
405
428
|
// 서버에서만 실행
|
|
406
429
|
return await db.user.findMany()
|
|
407
430
|
})
|
|
408
431
|
|
|
409
|
-
// ✅ POST + Validation
|
|
410
|
-
const
|
|
432
|
+
// ✅ POST + Validation (inputValidator는 함수 형태 필수)
|
|
433
|
+
const UserSchema = z.object({
|
|
411
434
|
name: z.string().min(1),
|
|
412
435
|
email: z.string().email()
|
|
413
436
|
})
|
|
414
437
|
|
|
415
438
|
const createUser = createServerFn({ method: 'POST' })
|
|
416
|
-
.inputValidator(
|
|
439
|
+
.inputValidator((d: unknown) => UserSchema.parse(d))
|
|
417
440
|
.handler(async ({ data }) => {
|
|
418
|
-
// data는
|
|
441
|
+
// data는 자동으로 { name: string; email: string } 타입
|
|
419
442
|
return await db.user.create({ data })
|
|
420
443
|
})
|
|
421
444
|
```
|
|
422
445
|
|
|
446
|
+
### Middleware로 인증 중앙화
|
|
447
|
+
|
|
448
|
+
```typescript
|
|
449
|
+
import { createMiddleware } from '@tanstack/react-start'
|
|
450
|
+
|
|
451
|
+
const authMiddleware = createMiddleware()
|
|
452
|
+
.server(async ({ next }) => {
|
|
453
|
+
const session = await getSession()
|
|
454
|
+
if (!session?.user) throw redirect({ to: '/login' })
|
|
455
|
+
return next({ context: { user: session.user } })
|
|
456
|
+
})
|
|
457
|
+
|
|
458
|
+
// 보호된 Server Function
|
|
459
|
+
const getMyTodos = createServerFn()
|
|
460
|
+
.middleware([authMiddleware])
|
|
461
|
+
.handler(async ({ context }) => {
|
|
462
|
+
return await db.todo.findMany({ where: { userId: context.user.id } })
|
|
463
|
+
})
|
|
464
|
+
```
|
|
465
|
+
|
|
423
466
|
### Loader 최적화
|
|
424
467
|
|
|
425
468
|
```typescript
|
|
@@ -546,18 +589,41 @@ rules/server-deferred-data.md
|
|
|
546
589
|
|
|
547
590
|
## 참고 자료
|
|
548
591
|
|
|
549
|
-
### TanStack 공식 문서
|
|
592
|
+
### TanStack Start 공식 문서
|
|
550
593
|
1. [React 공식 문서](https://react.dev)
|
|
551
594
|
2. [TanStack Start Overview](https://tanstack.com/start/latest/docs/framework/react/overview)
|
|
552
595
|
3. [TanStack Start Quick Start](https://tanstack.com/start/latest/docs/framework/react/quick-start)
|
|
553
|
-
4. [TanStack
|
|
554
|
-
5. [
|
|
555
|
-
6. [
|
|
556
|
-
7. [
|
|
596
|
+
4. [TanStack Query](https://tanstack.com/query/latest/docs/framework/react/overview)
|
|
597
|
+
5. [Server Functions Guide](https://tanstack.com/start/latest/docs/framework/react/guide/server-functions)
|
|
598
|
+
6. [Middleware Guide](https://tanstack.com/start/latest/docs/framework/react/guide/middleware)
|
|
599
|
+
7. [Streaming Data Guide](https://tanstack.com/start/latest/docs/framework/react/guide/streaming-data-from-server-functions)
|
|
600
|
+
8. [Authentication Guide](https://tanstack.com/start/latest/docs/framework/react/guide/authentication)
|
|
601
|
+
|
|
602
|
+
### TanStack Router
|
|
603
|
+
9. [TanStack Router](https://tanstack.com/router)
|
|
604
|
+
10. [Navigation Guide](https://tanstack.com/router/latest/docs/framework/react/guide/navigation)
|
|
605
|
+
11. [Link Options](https://tanstack.com/router/latest/docs/framework/react/guide/link-options)
|
|
606
|
+
12. [Search Params](https://tanstack.com/router/latest/docs/framework/react/guide/search-params)
|
|
607
|
+
13. [Path Params](https://tanstack.com/router/latest/docs/framework/react/guide/path-params)
|
|
608
|
+
14. [Authenticated Routes](https://tanstack.com/router/latest/docs/framework/react/guide/authenticated-routes)
|
|
609
|
+
15. [Outlets](https://tanstack.com/router/latest/docs/framework/react/guide/outlets)
|
|
610
|
+
16. [Route Trees](https://tanstack.com/router/latest/docs/framework/react/guide/route-trees)
|
|
611
|
+
17. [File-Based Routing](https://tanstack.com/router/latest/docs/framework/react/routing/file-based-routing)
|
|
612
|
+
18. [Router Context](https://tanstack.com/router/latest/docs/framework/react/guide/router-context)
|
|
613
|
+
19. [Preloading](https://tanstack.com/router/latest/docs/framework/react/guide/preloading)
|
|
614
|
+
20. [Data Loading](https://tanstack.com/router/latest/docs/framework/react/guide/data-loading)
|
|
615
|
+
21. [Deferred Data Loading](https://tanstack.com/router/latest/docs/framework/react/guide/deferred-data-loading)
|
|
616
|
+
|
|
617
|
+
### React 19
|
|
618
|
+
22. [React 19 Blog](https://react.dev/blog/2024/12/05/react-19)
|
|
619
|
+
23. [React use() API](https://react.dev/reference/react/use)
|
|
620
|
+
24. [React useOptimistic](https://react.dev/reference/react/useOptimistic)
|
|
621
|
+
25. [React useActionState](https://react.dev/reference/react/useActionState)
|
|
622
|
+
26. [React Compiler](https://react.dev/learn/react-compiler)
|
|
557
623
|
|
|
558
624
|
### 외부 자료
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
625
|
+
27. [better-all](https://github.com/shuding/better-all)
|
|
626
|
+
28. [node-lru-cache](https://github.com/isaacs/node-lru-cache)
|
|
627
|
+
29. [Using Server Functions and TanStack Query](https://www.brenelz.com/posts/using-server-functions-and-tanstack-query/)
|
|
562
628
|
|
|
563
629
|
</references>
|
|
@@ -13,37 +13,60 @@ tags: bundle, third-party, analytics, defer
|
|
|
13
13
|
|
|
14
14
|
```tsx
|
|
15
15
|
import { Analytics } from '@vercel/analytics/react'
|
|
16
|
+
import { ErrorTracker } from './error-tracker'
|
|
16
17
|
|
|
17
|
-
|
|
18
|
+
function RootLayout({ children }: { children: React.ReactNode }) {
|
|
18
19
|
return (
|
|
19
|
-
<
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
</html>
|
|
20
|
+
<div>
|
|
21
|
+
{children}
|
|
22
|
+
<Analytics />
|
|
23
|
+
<ErrorTracker />
|
|
24
|
+
</div>
|
|
25
25
|
)
|
|
26
26
|
}
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
-
**✅ 올바른 예시 (하이드레이션 후
|
|
29
|
+
**✅ 올바른 예시 (하이드레이션 후 lazy load):**
|
|
30
30
|
|
|
31
31
|
```tsx
|
|
32
|
-
import
|
|
32
|
+
import { lazy, Suspense, useEffect, useState } from 'react'
|
|
33
33
|
|
|
34
|
-
const Analytics =
|
|
35
|
-
|
|
36
|
-
{ ssr: false }
|
|
34
|
+
const Analytics = lazy(() =>
|
|
35
|
+
import('@vercel/analytics/react').then(m => ({ default: m.Analytics }))
|
|
37
36
|
)
|
|
38
37
|
|
|
39
|
-
|
|
38
|
+
function RootLayout({ children }: { children: React.ReactNode }) {
|
|
39
|
+
const [hydrated, setHydrated] = useState(false)
|
|
40
|
+
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
setHydrated(true)
|
|
43
|
+
}, [])
|
|
44
|
+
|
|
40
45
|
return (
|
|
41
|
-
<
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
<
|
|
45
|
-
|
|
46
|
-
|
|
46
|
+
<div>
|
|
47
|
+
{children}
|
|
48
|
+
{hydrated && (
|
|
49
|
+
<Suspense fallback={null}>
|
|
50
|
+
<Analytics />
|
|
51
|
+
</Suspense>
|
|
52
|
+
)}
|
|
53
|
+
</div>
|
|
47
54
|
)
|
|
48
55
|
}
|
|
49
56
|
```
|
|
57
|
+
|
|
58
|
+
**✅ 더 간단한 대안 (useEffect로 직접 초기화):**
|
|
59
|
+
|
|
60
|
+
```tsx
|
|
61
|
+
function RootLayout({ children }: { children: React.ReactNode }) {
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
// 하이드레이션 후 비동기 로드
|
|
64
|
+
import('./analytics').then(mod => mod.init())
|
|
65
|
+
import('./error-tracker').then(mod => mod.init())
|
|
66
|
+
}, [])
|
|
67
|
+
|
|
68
|
+
return <div>{children}</div>
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
TanStack Start에서는 `next/dynamic`을 사용할 수 없으므로, React의 `lazy()` + `Suspense` 또는 `useEffect` 내 동적 `import()`를 사용합니다.
|