@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.
Files changed (137) hide show
  1. package/dist/index.js +255 -149
  2. package/package.json +1 -1
  3. package/templates/.claude/agents/researcher.md +8 -1
  4. package/templates/.claude/instructions/sourcing/reliable-search.md +49 -2
  5. package/templates/.claude/scripts/deploy/build-run.sh +36 -0
  6. package/templates/.claude/scripts/deploy/deploy-check.sh +38 -0
  7. package/templates/.claude/scripts/git/git-all.sh +57 -0
  8. package/templates/.claude/scripts/git/git-clean-check.sh +31 -0
  9. package/templates/.claude/scripts/git/git-commit.sh +51 -0
  10. package/templates/.claude/scripts/git/git-info.sh +34 -0
  11. package/templates/.claude/scripts/git/git-push.sh +50 -0
  12. package/templates/.claude/scripts/lint/lint-check.sh +56 -0
  13. package/templates/.claude/scripts/lint/lint-file.sh +41 -0
  14. package/templates/.claude/scripts/pm/pm-detect.sh +25 -0
  15. package/templates/.claude/scripts/pm/pm-run.sh +41 -0
  16. package/templates/.claude/scripts/version/version-bump.sh +54 -0
  17. package/templates/.claude/scripts/version/version-find.sh +49 -0
  18. package/templates/.claude/skills/docs-fetch/SKILL.md +5 -4
  19. package/templates/.claude/skills/project-optimizer/AGENTS.md +275 -0
  20. package/templates/.claude/skills/project-optimizer/SKILL.md +374 -0
  21. package/templates/.claude/skills/project-optimizer/rules/arch-config-centralize.md +66 -0
  22. package/templates/.claude/skills/project-optimizer/rules/arch-hot-path.md +35 -0
  23. package/templates/.claude/skills/project-optimizer/rules/arch-interface-segregation.md +51 -0
  24. package/templates/.claude/skills/project-optimizer/rules/arch-module-boundary.md +42 -0
  25. package/templates/.claude/skills/project-optimizer/rules/build-cache.md +57 -0
  26. package/templates/.claude/skills/project-optimizer/rules/build-code-split.md +56 -0
  27. package/templates/.claude/skills/project-optimizer/rules/build-incremental.md +65 -0
  28. package/templates/.claude/skills/project-optimizer/rules/build-minify.md +61 -0
  29. package/templates/.claude/skills/project-optimizer/rules/build-tree-shake.md +60 -0
  30. package/templates/.claude/skills/project-optimizer/rules/code-complexity.md +65 -0
  31. package/templates/.claude/skills/project-optimizer/rules/code-dead-elimination.md +32 -0
  32. package/templates/.claude/skills/project-optimizer/rules/code-duplication.md +54 -0
  33. package/templates/.claude/skills/project-optimizer/rules/code-error-handling.md +75 -0
  34. package/templates/.claude/skills/project-optimizer/rules/code-naming.md +52 -0
  35. package/templates/.claude/skills/project-optimizer/rules/concurrency-defer-await.md +54 -0
  36. package/templates/.claude/skills/project-optimizer/rules/concurrency-parallel.md +90 -0
  37. package/templates/.claude/skills/project-optimizer/rules/concurrency-pipeline.md +68 -0
  38. package/templates/.claude/skills/project-optimizer/rules/concurrency-pool.md +68 -0
  39. package/templates/.claude/skills/project-optimizer/rules/deps-lightweight-alt.md +37 -0
  40. package/templates/.claude/skills/project-optimizer/rules/deps-peer-align.md +44 -0
  41. package/templates/.claude/skills/project-optimizer/rules/deps-security-audit.md +45 -0
  42. package/templates/.claude/skills/project-optimizer/rules/deps-unused-removal.md +25 -0
  43. package/templates/.claude/skills/project-optimizer/rules/deps-version-pin.md +40 -0
  44. package/templates/.claude/skills/project-optimizer/rules/dx-ci-speed.md +47 -0
  45. package/templates/.claude/skills/project-optimizer/rules/dx-dev-server.md +35 -0
  46. package/templates/.claude/skills/project-optimizer/rules/dx-lint-config.md +36 -0
  47. package/templates/.claude/skills/project-optimizer/rules/dx-test-coverage.md +34 -0
  48. package/templates/.claude/skills/project-optimizer/rules/dx-type-safety.md +49 -0
  49. package/templates/.claude/skills/project-optimizer/rules/io-batch-queries.md +67 -0
  50. package/templates/.claude/skills/project-optimizer/rules/io-cache-layer.md +67 -0
  51. package/templates/.claude/skills/project-optimizer/rules/io-connection-reuse.md +67 -0
  52. package/templates/.claude/skills/project-optimizer/rules/io-serialize-minimal.md +61 -0
  53. package/templates/.claude/skills/project-optimizer/rules/io-stream.md +75 -0
  54. package/templates/.claude/skills/project-optimizer/rules/memory-bounded-cache.md +65 -0
  55. package/templates/.claude/skills/project-optimizer/rules/memory-large-data.md +64 -0
  56. package/templates/.claude/skills/project-optimizer/rules/memory-lazy-init.md +78 -0
  57. package/templates/.claude/skills/project-optimizer/rules/memory-leak-prevention.md +79 -0
  58. package/templates/.claude/skills/project-optimizer/rules/memory-pool-reuse.md +70 -0
  59. package/templates/.claude/skills/sql-optimizer/SKILL.md +437 -0
  60. package/templates/.claude/skills/sql-optimizer/orm-patterns.md +218 -0
  61. package/templates/.claude/skills/tanstack-start-react-best-practices/AGENTS.md +53 -14
  62. package/templates/.claude/skills/tanstack-start-react-best-practices/SKILL.md +93 -27
  63. package/templates/.claude/skills/tanstack-start-react-best-practices/rules/bundle-defer-third-party.md +42 -19
  64. package/templates/.claude/skills/tanstack-start-react-best-practices/rules/client-optimistic-updates.md +109 -0
  65. package/templates/.claude/skills/tanstack-start-react-best-practices/rules/client-suspense-query.md +74 -0
  66. package/templates/.claude/skills/tanstack-start-react-best-practices/rules/client-use-hook.md +81 -0
  67. package/templates/.claude/skills/tanstack-start-react-best-practices/rules/rerender-react-compiler.md +81 -0
  68. package/templates/.claude/skills/tanstack-start-react-best-practices/rules/routing-beforeload-auth.md +121 -0
  69. package/templates/.claude/skills/tanstack-start-react-best-practices/rules/routing-file-conventions.md +104 -0
  70. package/templates/.claude/skills/tanstack-start-react-best-practices/rules/routing-link-navigation.md +119 -0
  71. package/templates/.claude/skills/tanstack-start-react-best-practices/rules/routing-nested-layouts.md +155 -0
  72. package/templates/.claude/skills/tanstack-start-react-best-practices/rules/routing-path-params.md +89 -0
  73. package/templates/.claude/skills/tanstack-start-react-best-practices/rules/routing-pending-component.md +110 -0
  74. package/templates/.claude/skills/tanstack-start-react-best-practices/rules/routing-preload-strategy.md +91 -0
  75. package/templates/.claude/skills/tanstack-start-react-best-practices/rules/routing-router-context.md +120 -0
  76. package/templates/.claude/skills/tanstack-start-react-best-practices/rules/routing-search-params.md +114 -0
  77. package/templates/.claude/skills/tanstack-start-react-best-practices/rules/server-deferred-data.md +1 -1
  78. package/templates/.claude/skills/tanstack-start-react-best-practices/rules/server-error-boundaries.md +79 -0
  79. package/templates/.claude/skills/tanstack-start-react-best-practices/rules/server-middleware.md +85 -0
  80. package/templates/.claude/skills/tanstack-start-react-best-practices/rules/server-serialization.md +56 -21
  81. package/templates/.claude/skills/tanstack-start-react-best-practices/rules/server-streaming.md +84 -0
  82. package/templates/.claude/skills/tanstack-start-react-best-practices/rules/server-validator.md +71 -0
  83. package/templates/.claude/skills/tauri-react-best-practices/AGENTS.md +527 -0
  84. package/templates/.claude/skills/tauri-react-best-practices/SKILL.md +570 -0
  85. package/templates/.claude/skills/tauri-react-best-practices/rules/bundle-barrel-imports.md +140 -0
  86. package/templates/.claude/skills/tauri-react-best-practices/rules/bundle-cargo-profile.md +96 -0
  87. package/templates/.claude/skills/tauri-react-best-practices/rules/bundle-frontend-treeshake.md +242 -0
  88. package/templates/.claude/skills/tauri-react-best-practices/rules/bundle-lazy-components.md +255 -0
  89. package/templates/.claude/skills/tauri-react-best-practices/rules/bundle-remove-unused-commands.md +160 -0
  90. package/templates/.claude/skills/tauri-react-best-practices/rules/deploy-ci-pipeline.md +269 -0
  91. package/templates/.claude/skills/tauri-react-best-practices/rules/deploy-signing.md +207 -0
  92. package/templates/.claude/skills/tauri-react-best-practices/rules/deploy-updater.md +226 -0
  93. package/templates/.claude/skills/tauri-react-best-practices/rules/ipc-async-commands.md +172 -0
  94. package/templates/.claude/skills/tauri-react-best-practices/rules/ipc-batch-commands.md +133 -0
  95. package/templates/.claude/skills/tauri-react-best-practices/rules/ipc-binary-response.md +198 -0
  96. package/templates/.claude/skills/tauri-react-best-practices/rules/ipc-channel-streaming.md +186 -0
  97. package/templates/.claude/skills/tauri-react-best-practices/rules/ipc-error-handling.md +250 -0
  98. package/templates/.claude/skills/tauri-react-best-practices/rules/ipc-type-safe.md +227 -0
  99. package/templates/.claude/skills/tauri-react-best-practices/rules/perf-derived-state.md +231 -0
  100. package/templates/.claude/skills/tauri-react-best-practices/rules/perf-functional-setstate.md +191 -0
  101. package/templates/.claude/skills/tauri-react-best-practices/rules/perf-index-maps.md +276 -0
  102. package/templates/.claude/skills/tauri-react-best-practices/rules/perf-lazy-state-init.md +196 -0
  103. package/templates/.claude/skills/tauri-react-best-practices/rules/plugin-lifecycle.md +265 -0
  104. package/templates/.claude/skills/tauri-react-best-practices/rules/plugin-mobile-compat.md +199 -0
  105. package/templates/.claude/skills/tauri-react-best-practices/rules/plugin-permission-scope.md +193 -0
  106. package/templates/.claude/skills/tauri-react-best-practices/rules/react-error-boundary.md +239 -0
  107. package/templates/.claude/skills/tauri-react-best-practices/rules/react-event-listener.md +151 -0
  108. package/templates/.claude/skills/tauri-react-best-practices/rules/react-file-src.md +155 -0
  109. package/templates/.claude/skills/tauri-react-best-practices/rules/react-invoke-hook.md +139 -0
  110. package/templates/.claude/skills/tauri-react-best-practices/rules/react-optimistic-update.md +211 -0
  111. package/templates/.claude/skills/tauri-react-best-practices/rules/security-capability-split.md +205 -0
  112. package/templates/.claude/skills/tauri-react-best-practices/rules/security-csp.md +207 -0
  113. package/templates/.claude/skills/tauri-react-best-practices/rules/security-least-privilege.md +106 -0
  114. package/templates/.claude/skills/tauri-react-best-practices/rules/security-no-wildcard.md +253 -0
  115. package/templates/.claude/skills/tauri-react-best-practices/rules/security-scope-paths.md +160 -0
  116. package/templates/.claude/skills/tauri-react-best-practices/rules/state-async-mutex.md +270 -0
  117. package/templates/.claude/skills/tauri-react-best-practices/rules/state-mutex-pattern.md +265 -0
  118. package/templates/.claude/skills/tauri-react-best-practices/rules/state-react-sync.md +375 -0
  119. package/templates/.claude/skills/tauri-react-best-practices/rules/state-single-container.md +275 -0
  120. package/templates/tanstack-start/docs/architecture.md +238 -167
  121. package/templates/tanstack-start/docs/library/tanstack-router/error-handling.md +777 -38
  122. package/templates/tanstack-start/docs/library/tanstack-router/hooks.md +549 -37
  123. package/templates/tanstack-start/docs/library/tanstack-router/index.md +895 -111
  124. package/templates/tanstack-start/docs/library/tanstack-router/navigation.md +641 -43
  125. package/templates/tanstack-start/docs/library/tanstack-router/route-context.md +889 -38
  126. package/templates/tanstack-start/docs/library/tanstack-router/search-params.md +891 -29
  127. package/templates/tanstack-start/docs/library/tanstack-start/auth-patterns.md +972 -36
  128. package/templates/tanstack-start/docs/library/tanstack-start/index.md +1525 -881
  129. package/templates/tanstack-start/docs/library/tanstack-start/middleware.md +1099 -20
  130. package/templates/tanstack-start/docs/library/tanstack-start/routing.md +796 -30
  131. package/templates/tanstack-start/docs/library/tanstack-start/server-functions.md +953 -35
  132. package/templates/tanstack-start/docs/library/tanstack-start/setup.md +371 -15
  133. package/templates/tauri/CLAUDE.md +189 -0
  134. package/templates/tauri/docs/guides/distribution.md +261 -0
  135. package/templates/tauri/docs/guides/getting-started.md +302 -0
  136. package/templates/tauri/docs/guides/mobile.md +288 -0
  137. package/templates/tauri/docs/library/tauri/index.md +510 -0
@@ -0,0 +1,79 @@
1
+ ---
2
+ title: Use Route-Level Error Boundaries
3
+ impact: HIGH
4
+ impactDescription: graceful error recovery per route
5
+ tags: server, error-handling, error-boundary, tanstack-router
6
+ ---
7
+
8
+ ## 라우트 레벨 에러 바운더리
9
+
10
+ TanStack Router는 라우트별 `errorComponent`를 지원합니다. 전역 에러 페이지 대신 라우트 단위 에러 처리로 사용자 경험을 개선하세요.
11
+
12
+ **❌ 잘못된 예시 (에러 처리 없음):**
13
+
14
+ ```typescript
15
+ export const Route = createFileRoute('/posts/$postId')({
16
+ loader: async ({ params }) => {
17
+ const post = await getPost(params.postId)
18
+ return { post } // post가 null이면 런타임 에러
19
+ },
20
+ component: PostPage
21
+ })
22
+
23
+ function PostPage() {
24
+ const { post } = Route.useLoaderData()
25
+ return <div>{post.title}</div> // 💥 Cannot read property 'title' of null
26
+ }
27
+ ```
28
+
29
+ **✅ 올바른 예시 (라우트별 에러 처리):**
30
+
31
+ ```typescript
32
+ export const Route = createFileRoute('/posts/$postId')({
33
+ loader: async ({ params }) => {
34
+ const post = await getPost(params.postId)
35
+ if (!post) throw new Error('Post not found')
36
+ return { post }
37
+ },
38
+ errorComponent: ({ error, reset }) => (
39
+ <div>
40
+ <h2>오류가 발생했습니다</h2>
41
+ <p>{error.message}</p>
42
+ <button onClick={reset}>다시 시도</button>
43
+ </div>
44
+ ),
45
+ component: PostPage
46
+ })
47
+ ```
48
+
49
+ **전역 기본 에러 컴포넌트 설정:**
50
+
51
+ ```typescript
52
+ import { createRouter } from '@tanstack/react-router'
53
+
54
+ const router = createRouter({
55
+ routeTree,
56
+ defaultErrorComponent: ({ error, reset }) => (
57
+ <div>
58
+ <h1>예기치 못한 오류</h1>
59
+ <p>{error.message}</p>
60
+ <button onClick={reset}>재시도</button>
61
+ </div>
62
+ )
63
+ })
64
+ ```
65
+
66
+ **notFoundComponent로 404 처리:**
67
+
68
+ ```typescript
69
+ export const Route = createFileRoute('/posts/$postId')({
70
+ loader: async ({ params }) => {
71
+ const post = await getPost(params.postId)
72
+ if (!post) throw notFound()
73
+ return { post }
74
+ },
75
+ notFoundComponent: () => <div>게시글을 찾을 수 없습니다</div>
76
+ })
77
+ ```
78
+
79
+ **원칙:** loader에서 예외를 명시적으로 throw하고, errorComponent/notFoundComponent로 처리합니다. TanStack Router가 자동으로 Suspense와 ErrorBoundary를 래핑합니다.
@@ -0,0 +1,85 @@
1
+ ---
2
+ title: Use Middleware for Cross-Cutting Concerns
3
+ impact: HIGH
4
+ impactDescription: centralizes auth, logging, validation
5
+ tags: server, middleware, createMiddleware, authentication, tanstack-start
6
+ ---
7
+
8
+ ## 크로스 커팅 관심사에 Middleware 사용
9
+
10
+ `createMiddleware()`로 인증, 로깅, 에러 처리 등 공통 로직을 중앙화합니다. 각 Server Function에 반복 코드를 작성하지 마세요.
11
+
12
+ **❌ 잘못된 예시 (모든 Server Function에 인증 로직 반복):**
13
+
14
+ ```typescript
15
+ import { createServerFn } from '@tanstack/react-start'
16
+
17
+ const getTodos = createServerFn().handler(async () => {
18
+ const session = await getSession()
19
+ if (!session?.user) throw new Error('Unauthorized')
20
+ return await db.todo.findMany({ where: { userId: session.user.id } })
21
+ })
22
+
23
+ const createTodo = createServerFn({ method: 'POST' })
24
+ .handler(async (data) => {
25
+ const session = await getSession()
26
+ if (!session?.user) throw new Error('Unauthorized')
27
+ return await db.todo.create({ data: { ...data, userId: session.user.id } })
28
+ })
29
+ ```
30
+
31
+ **✅ 올바른 예시 (Middleware로 인증 중앙화):**
32
+
33
+ ```typescript
34
+ import { createServerFn } from '@tanstack/react-start'
35
+ import { createMiddleware } from '@tanstack/react-start'
36
+
37
+ // 인증 미들웨어 정의 (재사용 가능)
38
+ const authMiddleware = createMiddleware()
39
+ .server(async ({ next }) => {
40
+ const session = await getSession()
41
+ if (!session?.user) {
42
+ throw redirect({ to: '/login' })
43
+ }
44
+ return next({ context: { user: session.user } })
45
+ })
46
+
47
+ // Server Functions에 미들웨어 적용
48
+ const getTodos = createServerFn()
49
+ .middleware([authMiddleware])
50
+ .handler(async ({ context }) => {
51
+ return await db.todo.findMany({ where: { userId: context.user.id } })
52
+ })
53
+
54
+ const createTodo = createServerFn({ method: 'POST' })
55
+ .middleware([authMiddleware])
56
+ .inputValidator((d: unknown) => TodoSchema.parse(d))
57
+ .handler(async ({ data, context }) => {
58
+ return await db.todo.create({ data: { ...data, userId: context.user.id } })
59
+ })
60
+ ```
61
+
62
+ **미들웨어 체이닝:**
63
+
64
+ ```typescript
65
+ const loggingMiddleware = createMiddleware()
66
+ .server(async ({ next }) => {
67
+ const start = Date.now()
68
+ const result = await next()
69
+ console.log(`Duration: ${Date.now() - start}ms`)
70
+ return result
71
+ })
72
+
73
+ // 여러 미들웨어 조합 (배열 순서대로 실행)
74
+ const protectedAction = createServerFn({ method: 'POST' })
75
+ .middleware([loggingMiddleware, authMiddleware])
76
+ .handler(async ({ context }) => { /* ... */ })
77
+ ```
78
+
79
+ **실행 순서:** inputValidator → middleware (배열 순서) → handler
80
+
81
+ **주의사항:**
82
+ - 미들웨어 코드가 클라이언트 번들에 포함될 수 있으므로, DB 연결/API 키 등 민감 정보는 handler 내에서만 사용
83
+ - `redirect()` 사용 시 직렬화 에러가 발생할 수 있으므로 `throw redirect()` 패턴 사용
84
+
85
+ 참고: [Middleware Guide](https://tanstack.com/start/latest/docs/framework/react/guide/middleware)
@@ -1,38 +1,73 @@
1
1
  ---
2
- title: Minimize Serialization at RSC Boundaries
2
+ title: Minimize Serialization in Loader Data
3
3
  impact: HIGH
4
4
  impactDescription: reduces data transfer size
5
- tags: server, rsc, serialization, props
5
+ tags: server, loader, serialization, tanstack-start
6
6
  ---
7
7
 
8
- ## RSC 경계에서 직렬화 최소화
8
+ ## Loader 데이터 직렬화 최소화
9
9
 
10
- React Server/Client 경계는 모든 객체 속성을 문자열로 직렬화하여 HTML 응답 및 후속 RSC 요청에 포함합니다. 이 직렬화된 데이터는 페이지 가중치와 로드 시간에 직접 영향을 미치므로 **크기가 매우 중요합니다**. 클라이언트가 실제로 사용하는 필드만 전달하세요.
10
+ TanStack Start의 loader에서 반환하는 데이터는 서버 클라이언트로 직렬화되어 전달됩니다. 이 직렬화된 데이터는 HTML 응답 크기와 하이드레이션 시간에 직접 영향을 미치므로 **클라이언트가 실제로 사용하는 필드만 반환하세요**.
11
11
 
12
12
  **❌ 잘못된 예시 (50개 필드 모두 직렬화):**
13
13
 
14
- ```tsx
15
- async function Page() {
16
- const user = await fetchUser() // 50개 필드
17
- return <Profile user={user} />
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
- 'use client'
21
- function Profile({ user }: { user: User }) {
22
- return <div>{user.name}</div> // 1개 필드만 사용
29
+ function ProfilePage() {
30
+ const { user } = Route.useLoaderData()
31
+ return <div>{user.name}</div> // 1개 필드만 사용
23
32
  }
24
33
  ```
25
34
 
26
- **✅ 올바른 예시 (1개 필드만 직렬화):**
35
+ **✅ 올바른 예시 (필요한 필드만 직렬화):**
27
36
 
28
- ```tsx
29
- async function Page() {
30
- const user = await fetchUser()
31
- return <Profile name={user.name} />
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
- 'use client'
35
- function Profile({ name }: { name: string }) {
36
- return <div>{name}</div>
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 크기, 하이드레이션 시간, 클라이언트 네비게이션 데이터 전송량이 모두 증가합니다.
@@ -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)
@@ -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)