@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
package/templates/.claude/skills/tanstack-start-react-best-practices/rules/server-serialization.md
CHANGED
|
@@ -1,38 +1,73 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: Minimize Serialization
|
|
2
|
+
title: Minimize Serialization in Loader Data
|
|
3
3
|
impact: HIGH
|
|
4
4
|
impactDescription: reduces data transfer size
|
|
5
|
-
tags: server,
|
|
5
|
+
tags: server, loader, serialization, tanstack-start
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## Loader 데이터 직렬화 최소화
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
TanStack Start의 loader에서 반환하는 데이터는 서버 → 클라이언트로 직렬화되어 전달됩니다. 이 직렬화된 데이터는 HTML 응답 크기와 하이드레이션 시간에 직접 영향을 미치므로 **클라이언트가 실제로 사용하는 필드만 반환하세요**.
|
|
11
11
|
|
|
12
12
|
**❌ 잘못된 예시 (50개 필드 모두 직렬화):**
|
|
13
13
|
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
```typescript
|
|
15
|
+
import { createServerFn } from '@tanstack/react-start'
|
|
16
|
+
import { createFileRoute } from '@tanstack/react-router'
|
|
17
|
+
|
|
18
|
+
const getUser = createServerFn().handler(async () => {
|
|
19
|
+
return await db.user.findUnique({ where: { id: '1' } }) // 50개 필드
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
export const Route = createFileRoute('/profile')({
|
|
23
|
+
loader: async () => {
|
|
24
|
+
const user = await getUser()
|
|
25
|
+
return { user } // 50개 필드 모두 직렬화
|
|
26
|
+
}
|
|
27
|
+
})
|
|
19
28
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
return <div>{user.name}</div>
|
|
29
|
+
function ProfilePage() {
|
|
30
|
+
const { user } = Route.useLoaderData()
|
|
31
|
+
return <div>{user.name}</div> // 1개 필드만 사용
|
|
23
32
|
}
|
|
24
33
|
```
|
|
25
34
|
|
|
26
|
-
**✅ 올바른 예시 (
|
|
35
|
+
**✅ 올바른 예시 (필요한 필드만 직렬화):**
|
|
27
36
|
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
const user = await
|
|
31
|
-
return
|
|
32
|
-
}
|
|
37
|
+
```typescript
|
|
38
|
+
const getUser = createServerFn().handler(async () => {
|
|
39
|
+
const user = await db.user.findUnique({ where: { id: '1' } })
|
|
40
|
+
return { name: user.name, email: user.email } // 필요한 필드만
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
export const Route = createFileRoute('/profile')({
|
|
44
|
+
loader: async () => {
|
|
45
|
+
const user = await getUser()
|
|
46
|
+
return { userName: user.name, userEmail: user.email }
|
|
47
|
+
}
|
|
48
|
+
})
|
|
33
49
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
return
|
|
50
|
+
function ProfilePage() {
|
|
51
|
+
const { userName, userEmail } = Route.useLoaderData()
|
|
52
|
+
return (
|
|
53
|
+
<div>
|
|
54
|
+
<div>{userName}</div>
|
|
55
|
+
<div>{userEmail}</div>
|
|
56
|
+
</div>
|
|
57
|
+
)
|
|
37
58
|
}
|
|
38
59
|
```
|
|
60
|
+
|
|
61
|
+
**✅ Server Function에서 select로 필드 제한:**
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
const getUser = createServerFn().handler(async () => {
|
|
65
|
+
// DB 쿼리 자체에서 필드 제한 (가장 효율적)
|
|
66
|
+
return await db.user.findUnique({
|
|
67
|
+
where: { id: '1' },
|
|
68
|
+
select: { name: true, email: true, avatar: true }
|
|
69
|
+
})
|
|
70
|
+
})
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
loader 데이터가 클수록 SSR HTML 크기, 하이드레이션 시간, 클라이언트 네비게이션 데이터 전송량이 모두 증가합니다.
|
package/templates/.claude/skills/tanstack-start-react-best-practices/rules/server-streaming.md
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Stream Data from Server Functions
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: progressive data delivery
|
|
5
|
+
tags: server, streaming, async-generator, ReadableStream, tanstack-start
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Server Functions에서 데이터 스트리밍
|
|
9
|
+
|
|
10
|
+
대량 데이터나 실시간 업데이트가 필요한 경우, Server Function에서 스트리밍으로 데이터를 점진적 전달합니다.
|
|
11
|
+
|
|
12
|
+
**❌ 잘못된 예시 (전체 데이터 대기):**
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
const generateReport = createServerFn({ method: 'POST' })
|
|
16
|
+
.handler(async () => {
|
|
17
|
+
const items = []
|
|
18
|
+
for (const chunk of await processLargeDataset()) {
|
|
19
|
+
items.push(chunk)
|
|
20
|
+
}
|
|
21
|
+
return items // 전체 처리 완료까지 대기
|
|
22
|
+
})
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
**✅ 올바른 예시 (async generator로 스트리밍):**
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
import { createServerFn } from '@tanstack/react-start'
|
|
29
|
+
|
|
30
|
+
const generateReport = createServerFn({ method: 'POST' })
|
|
31
|
+
.handler(async function* () {
|
|
32
|
+
yield { status: 'processing', progress: 0 }
|
|
33
|
+
|
|
34
|
+
for await (const chunk of processLargeDataset()) {
|
|
35
|
+
yield { status: 'processing', data: chunk, progress: chunk.index }
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
yield { status: 'complete', progress: 100 }
|
|
39
|
+
})
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
**✅ ReadableStream 방식 (세밀한 제어):**
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
const streamResponse = createServerFn({ method: 'POST' })
|
|
46
|
+
.handler(async () => {
|
|
47
|
+
return new ReadableStream({
|
|
48
|
+
start(controller) {
|
|
49
|
+
const encoder = new TextEncoder()
|
|
50
|
+
controller.enqueue(encoder.encode(JSON.stringify({ msg: 'chunk 1' })))
|
|
51
|
+
controller.enqueue(encoder.encode(JSON.stringify({ msg: 'chunk 2' })))
|
|
52
|
+
controller.close()
|
|
53
|
+
}
|
|
54
|
+
})
|
|
55
|
+
})
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
**클라이언트에서 스트림 소비:**
|
|
59
|
+
|
|
60
|
+
```tsx
|
|
61
|
+
function ReportGenerator() {
|
|
62
|
+
const [chunks, setChunks] = useState<ReportChunk[]>([])
|
|
63
|
+
|
|
64
|
+
const handleGenerate = async () => {
|
|
65
|
+
const stream = await generateReport()
|
|
66
|
+
for await (const chunk of stream) {
|
|
67
|
+
setChunks(prev => [...prev, chunk])
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<div>
|
|
73
|
+
<button onClick={handleGenerate}>Generate</button>
|
|
74
|
+
{chunks.map((chunk, i) => <ChunkDisplay key={i} chunk={chunk} />)}
|
|
75
|
+
</div>
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**사용 시점:** AI 응답 스트리밍, 대용량 CSV/JSON 내보내기, 실시간 진행률 표시, 로그 스트리밍.
|
|
81
|
+
|
|
82
|
+
**주의:** ReadableStream 사용 시 UTF-8 인코딩을 위해 `TextEncoder.encode()` 필수. async generator가 더 간결하고 타입 안전합니다.
|
|
83
|
+
|
|
84
|
+
참고: [Streaming Data from Server Functions](https://tanstack.com/start/latest/docs/framework/react/guide/streaming-data-from-server-functions)
|
package/templates/.claude/skills/tanstack-start-react-best-practices/rules/server-validator.md
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Use inputValidator for Type-Safe Server Functions
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: runtime type safety with Zod
|
|
5
|
+
tags: server, validation, inputValidator, zod, tanstack-start
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## inputValidator로 타입 안전한 Server Functions
|
|
9
|
+
|
|
10
|
+
`createServerFn`의 `.inputValidator()`로 런타임 입력 검증과 TypeScript 타입 추론을 동시에 확보합니다.
|
|
11
|
+
|
|
12
|
+
**❌ 잘못된 예시 (검증 없음, 타입 안전하지 않음):**
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
const createUser = createServerFn({ method: 'POST' })
|
|
16
|
+
.handler(async (data: any) => {
|
|
17
|
+
// data가 검증되지 않음 - 런타임 에러 위험
|
|
18
|
+
return await db.user.create({ data })
|
|
19
|
+
})
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
**✅ 올바른 예시 (Zod로 검증 + 타입 추론):**
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
import { z } from 'zod'
|
|
26
|
+
import { createServerFn } from '@tanstack/react-start'
|
|
27
|
+
|
|
28
|
+
const UserSchema = z.object({
|
|
29
|
+
name: z.string().min(1),
|
|
30
|
+
email: z.string().email()
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
const createUser = createServerFn({ method: 'POST' })
|
|
34
|
+
.inputValidator((d: unknown) => UserSchema.parse(d))
|
|
35
|
+
.handler(async ({ data }) => {
|
|
36
|
+
// data는 자동으로 { name: string; email: string } 타입
|
|
37
|
+
return await db.user.create({ data })
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
// 클라이언트에서 호출
|
|
41
|
+
await createUser({ data: { name: 'Alice', email: 'alice@example.com' } })
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
**중요:** `.inputValidator()`는 반드시 **함수** 형태로 전달해야 합니다. Zod schema를 직접 전달하면 안 됩니다.
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
// ❌ 잘못됨: schema 직접 전달
|
|
48
|
+
.inputValidator(UserSchema)
|
|
49
|
+
|
|
50
|
+
// ✅ 올바름: 함수로 감싸서 전달
|
|
51
|
+
.inputValidator((d: unknown) => UserSchema.parse(d))
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**FormData 검증:**
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
const uploadFile = createServerFn({ method: 'POST' })
|
|
58
|
+
.inputValidator((d: unknown) => {
|
|
59
|
+
if (!(d instanceof FormData)) throw new Error('Expected FormData')
|
|
60
|
+
return {
|
|
61
|
+
name: d.get('name')?.toString() || '',
|
|
62
|
+
file: d.get('file') as File
|
|
63
|
+
}
|
|
64
|
+
})
|
|
65
|
+
.handler(async ({ data }) => {
|
|
66
|
+
// data.name, data.file 모두 타입 안전
|
|
67
|
+
return await saveFile(data.file)
|
|
68
|
+
})
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
참고: [Server Functions Guide](https://tanstack.com/start/latest/docs/framework/react/guide/server-functions)
|