@kood/claude-code 0.1.6 → 0.1.9

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 (107) hide show
  1. package/dist/index.js +109 -216
  2. package/package.json +8 -2
  3. package/templates/hono/CLAUDE.md +59 -328
  4. package/templates/hono/docs/architecture/architecture.md +93 -747
  5. package/templates/hono/docs/deployment/cloudflare.md +59 -513
  6. package/templates/hono/docs/deployment/docker.md +41 -356
  7. package/templates/hono/docs/deployment/index.md +54 -190
  8. package/templates/hono/docs/deployment/railway.md +36 -306
  9. package/templates/hono/docs/deployment/vercel.md +49 -434
  10. package/templates/hono/docs/library/ai-sdk/index.md +53 -290
  11. package/templates/hono/docs/library/ai-sdk/openrouter.md +19 -387
  12. package/templates/hono/docs/library/ai-sdk/providers.md +28 -394
  13. package/templates/hono/docs/library/ai-sdk/streaming.md +52 -353
  14. package/templates/hono/docs/library/ai-sdk/structured-output.md +63 -395
  15. package/templates/hono/docs/library/ai-sdk/tools.md +62 -431
  16. package/templates/hono/docs/library/hono/env-setup.md +24 -313
  17. package/templates/hono/docs/library/hono/error-handling.md +34 -295
  18. package/templates/hono/docs/library/hono/index.md +29 -121
  19. package/templates/hono/docs/library/hono/middleware.md +21 -188
  20. package/templates/hono/docs/library/hono/rpc.md +40 -341
  21. package/templates/hono/docs/library/hono/validation.md +35 -195
  22. package/templates/hono/docs/library/pino/index.md +42 -333
  23. package/templates/hono/docs/library/prisma/cloudflare-d1.md +64 -367
  24. package/templates/hono/docs/library/prisma/config.md +19 -260
  25. package/templates/hono/docs/library/prisma/index.md +67 -320
  26. package/templates/hono/docs/library/zod/index.md +53 -257
  27. package/templates/npx/CLAUDE.md +62 -274
  28. package/templates/npx/docs/references/patterns.md +160 -0
  29. package/templates/tanstack-start/CLAUDE.md +100 -256
  30. package/templates/tanstack-start/docs/architecture/architecture.md +44 -589
  31. package/templates/tanstack-start/docs/deployment/cloudflare.md +37 -424
  32. package/templates/tanstack-start/docs/deployment/index.md +57 -286
  33. package/templates/tanstack-start/docs/deployment/nitro.md +36 -318
  34. package/templates/tanstack-start/docs/deployment/railway.md +40 -409
  35. package/templates/tanstack-start/docs/deployment/vercel.md +43 -465
  36. package/templates/tanstack-start/docs/design/components.md +77 -311
  37. package/templates/tanstack-start/docs/design/index.md +113 -69
  38. package/templates/tanstack-start/docs/design/safe-area.md +51 -250
  39. package/templates/tanstack-start/docs/design/tailwind-setup.md +45 -359
  40. package/templates/tanstack-start/docs/guides/conventions.md +103 -0
  41. package/templates/tanstack-start/docs/guides/env-setup.md +34 -340
  42. package/templates/tanstack-start/docs/guides/getting-started.md +22 -209
  43. package/templates/tanstack-start/docs/guides/hooks.md +166 -0
  44. package/templates/tanstack-start/docs/guides/routes.md +166 -0
  45. package/templates/tanstack-start/docs/guides/services.md +143 -0
  46. package/templates/tanstack-start/docs/library/better-auth/2fa.md +27 -115
  47. package/templates/tanstack-start/docs/library/better-auth/advanced.md +22 -105
  48. package/templates/tanstack-start/docs/library/better-auth/index.md +17 -66
  49. package/templates/tanstack-start/docs/library/better-auth/plugins.md +11 -88
  50. package/templates/tanstack-start/docs/library/better-auth/session.md +12 -92
  51. package/templates/tanstack-start/docs/library/better-auth/setup.md +9 -91
  52. package/templates/tanstack-start/docs/library/prisma/cloudflare-d1.md +30 -358
  53. package/templates/tanstack-start/docs/library/prisma/config.md +27 -327
  54. package/templates/tanstack-start/docs/library/prisma/crud.md +46 -174
  55. package/templates/tanstack-start/docs/library/prisma/index.md +23 -113
  56. package/templates/tanstack-start/docs/library/prisma/relations.md +31 -153
  57. package/templates/tanstack-start/docs/library/prisma/schema.md +40 -217
  58. package/templates/tanstack-start/docs/library/prisma/setup.md +12 -112
  59. package/templates/tanstack-start/docs/library/prisma/transactions.md +20 -110
  60. package/templates/tanstack-start/docs/library/tanstack-query/index.md +26 -97
  61. package/templates/tanstack-start/docs/library/tanstack-query/invalidation.md +28 -107
  62. package/templates/tanstack-start/docs/library/tanstack-query/optimistic-updates.md +44 -146
  63. package/templates/tanstack-start/docs/library/tanstack-query/use-mutation.md +33 -127
  64. package/templates/tanstack-start/docs/library/tanstack-query/use-query.md +49 -149
  65. package/templates/tanstack-start/docs/library/tanstack-start/auth-patterns.md +19 -112
  66. package/templates/tanstack-start/docs/library/tanstack-start/index.md +33 -80
  67. package/templates/tanstack-start/docs/library/tanstack-start/middleware.md +28 -106
  68. package/templates/tanstack-start/docs/library/tanstack-start/routing.md +21 -118
  69. package/templates/tanstack-start/docs/library/tanstack-start/server-functions.md +34 -246
  70. package/templates/tanstack-start/docs/library/tanstack-start/setup.md +6 -39
  71. package/templates/tanstack-start/docs/library/zod/complex-types.md +32 -156
  72. package/templates/tanstack-start/docs/library/zod/index.md +31 -144
  73. package/templates/tanstack-start/docs/library/zod/transforms.md +20 -129
  74. package/templates/tanstack-start/docs/library/zod/validation.md +39 -155
  75. package/templates/hono/docs/commands/git.md +0 -145
  76. package/templates/hono/docs/mcp/context7.md +0 -106
  77. package/templates/hono/docs/mcp/index.md +0 -176
  78. package/templates/hono/docs/mcp/sequential-thinking.md +0 -101
  79. package/templates/hono/docs/mcp/serena.md +0 -269
  80. package/templates/hono/docs/mcp/sgrep.md +0 -105
  81. package/templates/hono/docs/skills/gemini-review/SKILL.md +0 -220
  82. package/templates/hono/docs/skills/gemini-review/references/checklists.md +0 -136
  83. package/templates/hono/docs/skills/gemini-review/references/prompt-templates.md +0 -303
  84. package/templates/npx/docs/commands/git.md +0 -145
  85. package/templates/npx/docs/mcp/index.md +0 -60
  86. package/templates/npx/docs/skills/gemini-review/SKILL.md +0 -220
  87. package/templates/npx/docs/skills/gemini-review/references/checklists.md +0 -134
  88. package/templates/npx/docs/skills/gemini-review/references/prompt-templates.md +0 -301
  89. package/templates/tanstack-start/docs/commands/git.md +0 -145
  90. package/templates/tanstack-start/docs/design/accessibility.md +0 -433
  91. package/templates/tanstack-start/docs/design/color.md +0 -235
  92. package/templates/tanstack-start/docs/design/spacing.md +0 -341
  93. package/templates/tanstack-start/docs/design/typography.md +0 -324
  94. package/templates/tanstack-start/docs/guides/best-practices.md +0 -950
  95. package/templates/tanstack-start/docs/guides/husky-lint-staged.md +0 -303
  96. package/templates/tanstack-start/docs/guides/prettier.md +0 -189
  97. package/templates/tanstack-start/docs/guides/project-templates.md +0 -710
  98. package/templates/tanstack-start/docs/library/tanstack-query/setup.md +0 -107
  99. package/templates/tanstack-start/docs/library/zod/basic-types.md +0 -186
  100. package/templates/tanstack-start/docs/mcp/context7.md +0 -204
  101. package/templates/tanstack-start/docs/mcp/index.md +0 -177
  102. package/templates/tanstack-start/docs/mcp/sequential-thinking.md +0 -180
  103. package/templates/tanstack-start/docs/mcp/serena.md +0 -269
  104. package/templates/tanstack-start/docs/mcp/sgrep.md +0 -174
  105. package/templates/tanstack-start/docs/skills/gemini-review/SKILL.md +0 -220
  106. package/templates/tanstack-start/docs/skills/gemini-review/references/checklists.md +0 -144
  107. package/templates/tanstack-start/docs/skills/gemini-review/references/prompt-templates.md +0 -292
@@ -1,287 +1,75 @@
1
1
  # TanStack Start - Server Functions
2
2
 
3
- > **상위 문서**: [TanStack Start](./index.md)
4
-
5
- Server Functions는 서버에서만 실행되는 타입 안전한 함수입니다.
3
+ 서버에서만 실행되는 타입 안전한 함수.
6
4
 
7
5
  ## ⚠️ 필수: TanStack Query 사용
8
6
 
9
- **Server Function을 클라이언트에서 호출할 때는 반드시 TanStack Query를 사용해야 합니다.**
10
-
11
- ```
12
- ❌ 금지: Server Function 직접 호출
13
- ✅ 필수: useQuery/useMutation과 함께 사용
14
- ```
15
-
16
- **이유**:
17
- - 자동 캐싱 및 중복 요청 제거
18
- - 로딩/에러 상태 관리
19
- - 자동 재시도 및 백그라운드 갱신
20
- - invalidateQueries로 일관된 데이터 동기화
7
+ 클라이언트 호출 반드시 useQuery/useMutation 사용.
8
+ - 자동 캐싱, 중복 요청 제거, 로딩/에러 상태 관리, invalidateQueries 동기화
21
9
 
22
- ## 기본 Server Function
10
+ ## 기본 패턴
23
11
 
24
12
  ```typescript
25
- import { createServerFn } from '@tanstack/react-start'
26
-
27
- // GET 요청 (데이터 조회)
13
+ // GET
28
14
  export const getUsers = createServerFn({ method: 'GET' })
29
- .handler(async () => {
30
- return prisma.user.findMany()
31
- })
32
-
33
- // POST 요청 (데이터 생성/수정)
34
- export const createUser = createServerFn({ method: 'POST' })
35
- .handler(async () => {
36
- return { success: true }
37
- })
38
- ```
39
-
40
- ## Input Validation
41
-
42
- ### 기본 Validator
43
-
44
- ```typescript
45
- import { createServerFn } from '@tanstack/react-start'
46
-
47
- export const createUser = createServerFn({ method: 'POST' })
48
- .inputValidator((data: { email: string; name: string }) => data)
49
- .handler(async ({ data }) => {
50
- // data는 타입 안전함
51
- return prisma.user.create({ data })
52
- })
53
- ```
54
-
55
- ### Zod Validation 사용
56
-
57
- ```typescript
58
- import { createServerFn } from '@tanstack/react-start'
59
- import { zodValidator } from '@tanstack/react-start/validators'
60
- import { z } from 'zod'
15
+ .handler(async () => prisma.user.findMany())
61
16
 
17
+ // POST + Zod Validation
62
18
  const createUserSchema = z.object({
63
- email: z.string().email(),
19
+ email: z.email(),
64
20
  name: z.string().min(1).max(100),
65
- bio: z.string().max(500).optional(),
66
21
  })
67
22
 
68
23
  export const createUser = createServerFn({ method: 'POST' })
69
24
  .inputValidator(zodValidator(createUserSchema))
70
- .handler(async ({ data }) => {
71
- return prisma.user.create({ data })
72
- })
73
- ```
74
-
75
- ## FormData 처리
76
-
77
- ```typescript
78
- import { createServerFn } from '@tanstack/react-start'
79
-
80
- export const submitForm = createServerFn({ method: 'POST' })
81
- .inputValidator((formData: FormData) => {
82
- const name = formData.get('name') as string
83
- const email = formData.get('email') as string
84
- return { name, email }
85
- })
86
- .handler(async ({ data }) => {
87
- return prisma.user.create({ data })
88
- })
25
+ .handler(async ({ data }) => prisma.user.create({ data }))
89
26
  ```
90
27
 
91
- ## 컴포넌트에서 호출 (TanStack Query 필수)
92
-
93
- ### ✅ 올바른 패턴: useQuery 사용 (데이터 조회)
28
+ ## 컴포넌트에서 호출
94
29
 
95
30
  ```tsx
96
- import { useQuery } from '@tanstack/react-query'
97
- import { getServerPosts } from '@/lib/server-functions'
98
-
99
- function PostList() {
100
- const { data, isLoading, error } = useQuery({
101
- queryKey: ['posts'],
102
- queryFn: () => getServerPosts(),
103
- })
104
-
105
- if (isLoading) return <div>Loading...</div>
106
- if (error) return <div>Error: {error.message}</div>
107
-
108
- return (
109
- <ul>
110
- {data?.map((post) => (
111
- <li key={post.id}>{post.title}</li>
112
- ))}
113
- </ul>
114
- )
115
- }
116
- ```
117
-
118
- ### ✅ 올바른 패턴: useMutation 사용 (데이터 변경)
119
-
120
- ```tsx
121
- import { useMutation, useQueryClient } from '@tanstack/react-query'
122
- import { createPost, deletePost } from '@/lib/server-functions'
123
-
124
- function PostForm() {
125
- const queryClient = useQueryClient()
126
-
127
- const mutation = useMutation({
128
- mutationFn: (data: { title: string; content: string }) => createPost({ data }),
129
- onSuccess: () => {
130
- // 관련 쿼리 무효화로 데이터 동기화
131
- queryClient.invalidateQueries({ queryKey: ['posts'] })
132
- },
133
- })
134
-
135
- const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
136
- e.preventDefault()
137
- const formData = new FormData(e.currentTarget)
138
- mutation.mutate({
139
- title: formData.get('title') as string,
140
- content: formData.get('content') as string,
141
- })
142
- }
143
-
144
- return (
145
- <form onSubmit={handleSubmit}>
146
- <input name="title" required />
147
- <textarea name="content" required />
148
- <button type="submit" disabled={mutation.isPending}>
149
- {mutation.isPending ? '저장 중...' : '저장'}
150
- </button>
151
- </form>
152
- )
153
- }
154
- ```
155
-
156
- ### ❌ 금지: Server Function 직접 호출
157
-
158
- ```tsx
159
- // ❌ 이렇게 하지 마세요!
160
- function BadExample() {
161
- const [posts, setPosts] = useState([])
162
- const [loading, setLoading] = useState(false)
163
-
164
- useEffect(() => {
165
- setLoading(true)
166
- getPosts()
167
- .then(setPosts)
168
- .finally(() => setLoading(false))
169
- }, [])
170
-
171
- // 문제점:
172
- // - 중복 요청 발생 가능
173
- // - 캐싱 없음
174
- // - 에러 처리 수동
175
- // - 다른 컴포넌트와 데이터 동기화 안됨
176
- }
177
- ```
178
-
179
- ## ⚠️ 함수 분리 시 유의사항
180
-
181
- Server Function 내부 로직을 별도 함수로 분리할 때 반드시 아래 규칙을 따르세요.
31
+ // useQuery (조회)
32
+ const { data, isLoading } = useQuery({
33
+ queryKey: ['posts'],
34
+ queryFn: () => getServerPosts(),
35
+ })
182
36
 
183
- ### 규칙
37
+ // ✅ useMutation (변경)
38
+ const mutation = useMutation({
39
+ mutationFn: createPost,
40
+ onSuccess: () => queryClient.invalidateQueries({ queryKey: ['posts'] }),
41
+ })
184
42
 
185
- ```
186
- 1. 분리한 함수는 createServerFn 내부에서만 호출
187
- 2. 분리한 함수는 createServerFn으로 감싸지 않음
188
- 3. 분리한 함수는 index.ts에서 export 금지 (프론트엔드 import 방지)
43
+ // ❌ 직접 호출 금지 (캐싱 없음, 동기화 안됨)
189
44
  ```
190
45
 
191
- ### 올바른 패턴
46
+ ## 함수 분리 규칙
192
47
 
193
48
  ```typescript
194
- // services/user/mutations.ts
195
-
196
- // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
197
- // 내부 헬퍼 함수 (export 금지!)
198
- // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
199
- const validateUserData = async (email: string) => {
200
- const existing = await prisma.user.findUnique({ where: { email } })
201
- if (existing) throw new Error('Email already exists')
202
- return true
203
- }
49
+ // 내부 헬퍼 (export 금지!)
50
+ const validateUserData = async (email: string) => { ... }
204
51
 
205
- const sendWelcomeEmail = async (userId: string, email: string) => {
206
- await emailService.send({
207
- to: email,
208
- template: 'welcome',
209
- data: { userId },
210
- })
211
- }
212
-
213
- // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
214
52
  // Server Function (export 가능)
215
- // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
216
53
  export const createUser = createServerFn({ method: 'POST' })
217
54
  .inputValidator(createUserSchema)
218
55
  .handler(async ({ data }) => {
219
- // 내부 헬퍼 함수 호출
220
56
  await validateUserData(data.email)
221
-
222
- const user = await prisma.user.create({ data })
223
-
224
- // 내부 헬퍼 함수 호출
225
- await sendWelcomeEmail(user.id, user.email)
226
-
227
- return user
228
- })
229
- ```
230
-
231
- ```typescript
232
- // services/user/index.ts
233
-
234
- // ✅ Server Function만 export
235
- export { createUser, updateUser, deleteUser } from './mutations'
236
- export { getUsers, getUserById } from './queries'
237
-
238
- // ❌ 내부 헬퍼 함수는 export 금지!
239
- // export { validateUserData, sendWelcomeEmail } from './mutations'
240
- ```
241
-
242
- ### ❌ 잘못된 패턴
243
-
244
- ```typescript
245
- // ❌ 분리한 함수를 createServerFn으로 감싸지 마세요
246
- export const validateUserData = createServerFn({ method: 'POST' })
247
- .handler(async ({ data }) => {
248
- // ...
57
+ return prisma.user.create({ data })
249
58
  })
250
59
 
251
- // 내부 헬퍼 함수를 export 하지 마세요
252
- export const sendWelcomeEmail = async (userId: string) => {
253
- // 프론트엔드에서 import 가능해져 보안 위험!
254
- }
60
+ // index.ts: Server Function만 export
61
+ export { createUser } from './mutations'
62
+ // export { validateUserData } 금지
255
63
  ```
256
64
 
257
- ### 이유
258
-
259
- - **보안**: 내부 로직이 프론트엔드 번들에 포함되지 않음
260
- - **명확한 API**: Server Function만 외부에 노출되어 API 경계가 명확함
261
- - **트리 쉐이킹**: export하지 않은 함수는 번들에서 제거됨
262
-
263
- ---
264
-
265
- ## 보안 패턴
266
-
267
- ### 서버 전용 데이터 보호
65
+ ## 보안
268
66
 
269
67
  ```tsx
270
- // ❌ 잘못된 방법 - 클라이언트에 노출됨
271
- export const Route = createFileRoute('/users')({
272
- loader: () => {
273
- const secret = process.env.SECRET // 클라이언트에 노출!
274
- return fetch(`/api/users?key=${secret}`)
275
- },
276
- })
277
-
278
- // ✅ 올바른 방법 - 서버 함수 사용
279
- const getUsersSecurely = createServerFn().handler(() => {
280
- const secret = process.env.SECRET // 서버에서만 접근
281
- return fetch(`/api/users?key=${secret}`)
282
- })
68
+ // ❌ loader에서 환경변수 직접 사용 (노출됨)
69
+ loader: () => { const secret = process.env.SECRET }
283
70
 
284
- export const Route = createFileRoute('/users')({
285
- loader: () => getUsersSecurely(),
71
+ // Server Function 사용
72
+ const fn = createServerFn().handler(() => {
73
+ const secret = process.env.SECRET // 서버에서만
286
74
  })
287
75
  ```
@@ -1,15 +1,13 @@
1
1
  # TanStack Start - 설치 및 설정
2
2
 
3
- > **상위 문서**: [TanStack Start](./index.md)
4
-
5
- ## 패키지 설치
3
+ ## 설치
6
4
 
7
5
  ```bash
8
6
  yarn add @tanstack/react-start @tanstack/react-router vinxi
9
7
  yarn add -D vite @vitejs/plugin-react vite-tsconfig-paths
10
8
  ```
11
9
 
12
- ## Vite 설정
10
+ ## 설정
13
11
 
14
12
  ```typescript
15
13
  // vite.config.ts
@@ -19,19 +17,11 @@ import { tanstackStart } from '@tanstack/react-start/plugin/vite'
19
17
  import viteReact from '@vitejs/plugin-react'
20
18
 
21
19
  export default defineConfig({
22
- server: {
23
- port: 3000,
24
- },
25
- plugins: [
26
- tsConfigPaths(),
27
- tanstackStart(),
28
- viteReact(),
29
- ],
20
+ server: { port: 3000 },
21
+ plugins: [tsConfigPaths(), tanstackStart(), viteReact()],
30
22
  })
31
23
  ```
32
24
 
33
- ## TypeScript 설정
34
-
35
25
  ```json
36
26
  // tsconfig.json
37
27
  {
@@ -40,35 +30,12 @@ export default defineConfig({
40
30
  "module": "ESNext",
41
31
  "moduleResolution": "bundler",
42
32
  "strict": true,
43
- "esModuleInterop": true,
44
- "skipLibCheck": true,
45
33
  "jsx": "react-jsx",
46
- "baseUrl": ".",
47
- "paths": {
48
- "@/*": ["./src/*"]
49
- }
50
- },
51
- "include": ["src/**/*"]
34
+ "paths": { "@/*": ["./src/*"] }
35
+ }
52
36
  }
53
37
  ```
54
38
 
55
- ## 프로젝트 구조
56
-
57
- ```
58
- project/
59
- ├── src/
60
- │ ├── routes/
61
- │ │ ├── __root.tsx
62
- │ │ ├── index.tsx
63
- │ │ └── about.tsx
64
- │ ├── lib/
65
- │ │ └── server-functions.ts
66
- │ └── start.ts
67
- ├── vite.config.ts
68
- ├── tsconfig.json
69
- └── package.json
70
- ```
71
-
72
39
  ## 환경 변수 검증
73
40
 
74
41
  ```typescript
@@ -1,204 +1,80 @@
1
1
  # Zod - 복합 타입
2
2
 
3
- > **상위 문서**: [Zod](./index.md)
4
-
5
3
  ## 객체
6
4
 
7
5
  ```typescript
8
6
  const UserSchema = z.object({
9
7
  name: z.string(),
10
- email: z.email(), // v4 새 API
11
- age: z.number().optional(), // 선택적 필드
12
- })
13
-
14
- // 검증
15
- const result = UserSchema.parse({
16
- name: 'john',
17
- email: 'john@example.com',
18
- age: 25,
19
- })
20
-
21
- // 안전한 검증
22
- const safeResult = UserSchema.safeParse(data)
23
- if (safeResult.success) {
24
- console.log(safeResult.data)
25
- } else {
26
- console.log(safeResult.error)
27
- }
28
- ```
29
-
30
- ### 객체 메서드
31
-
32
- ```typescript
33
- // 부분 객체 (모든 필드가 optional)
34
- const PartialUser = UserSchema.partial()
35
-
36
- // 필수 객체 (모든 필드가 required)
37
- const RequiredUser = UserSchema.required()
38
-
39
- // 필드 선택
40
- const UserName = UserSchema.pick({ name: true })
41
-
42
- // 필드 제외
43
- const UserWithoutEmail = UserSchema.omit({ email: true })
44
-
45
- // 확장
46
- const ExtendedUser = UserSchema.extend({
47
- role: z.enum(['admin', 'user']),
8
+ email: z.email(),
9
+ age: z.number().optional(),
48
10
  })
49
11
 
50
- // 병합
51
- const MergedSchema = UserSchema.merge(AnotherSchema)
52
- ```
53
-
54
- ### Strict/Loose 객체 (v4)
12
+ // 메서드
13
+ UserSchema.partial() // 모든 필드 optional
14
+ UserSchema.required() // 모든 필드 required
15
+ UserSchema.pick({ name: true }) // 특정 필드만
16
+ UserSchema.omit({ email: true }) // 특정 필드 제외
17
+ UserSchema.extend({ role: z.enum(['admin', 'user']) })
18
+ UserSchema.merge(AnotherSchema)
55
19
 
56
- ```typescript
57
- // v3
58
- z.object({ name: z.string() }).strict() // 추가 키 에러
59
- z.object({ name: z.string() }).passthrough() // 추가 키 통과
60
-
61
- // v4 - 새로운 API
20
+ // v4 Strict/Loose
62
21
  z.strictObject({ name: z.string() }) // 추가 키 에러
63
22
  z.looseObject({ name: z.string() }) // 추가 키 통과
64
23
  ```
65
24
 
66
- ## 배열
25
+ ## 배열/튜플
67
26
 
68
27
  ```typescript
69
28
  z.array(z.string())
70
- z.array(z.number()).min(1) // 최소 1개
71
- z.array(z.number()).max(10) // 최대 10개
72
- z.array(z.number()).length(5) // 정확히 5개
73
- z.array(z.number()).nonempty() // 비어있지 않음
74
- ```
29
+ z.array(z.number()).min(1).max(10).length(5).nonempty()
75
30
 
76
- ## 튜플
77
-
78
- ```typescript
79
- const tuple = z.tuple([
80
- z.string(), // 첫 번째 요소
81
- z.number(), // 두 번째 요소
82
- ])
83
-
84
- type Tuple = z.infer<typeof tuple>
85
- // [string, number]
31
+ z.tuple([z.string(), z.number()]) // [string, number]
86
32
  ```
87
33
 
88
34
  ## 유니온
89
35
 
90
36
  ```typescript
91
- const StringOrNumber = z.union([z.string(), z.number()])
92
- // 또는
93
- const StringOrNumber = z.string().or(z.number())
94
-
95
- type StringOrNumber = z.infer<typeof StringOrNumber>
96
- // string | number
97
- ```
37
+ z.union([z.string(), z.number()])
38
+ z.string().or(z.number())
98
39
 
99
- ## Discriminated Union
100
-
101
- ```typescript
102
- const Shape = z.discriminatedUnion('type', [
40
+ // Discriminated Union
41
+ z.discriminatedUnion('type', [
103
42
  z.object({ type: z.literal('circle'), radius: z.number() }),
104
43
  z.object({ type: z.literal('rectangle'), width: z.number(), height: z.number() }),
105
44
  ])
106
-
107
- type Shape = z.infer<typeof Shape>
108
- // { type: 'circle'; radius: number } | { type: 'rectangle'; width: number; height: number }
109
- ```
110
-
111
- ### v4 향상된 Discriminated Union
112
-
113
- ```typescript
114
- // 유니온 및 파이프 discriminator 지원
115
- const MyResult = z.discriminatedUnion("status", [
116
- // 단순 리터럴
117
- z.object({ status: z.literal("aaa"), data: z.string() }),
118
- // 유니온 discriminator
119
- z.object({ status: z.union([z.literal("bbb"), z.literal("ccc")]) }),
120
- // 파이프 discriminator
121
- z.object({ status: z.literal("fail").transform(val => val.toUpperCase()) }),
122
- ])
123
-
124
- // 중첩 discriminated union
125
- const BaseError = z.object({ status: z.literal("failed"), message: z.string() })
126
-
127
- const MyResult2 = z.discriminatedUnion("status", [
128
- z.object({ status: z.literal("success"), data: z.string() }),
129
- z.discriminatedUnion("code", [
130
- BaseError.extend({ code: z.literal(400) }),
131
- BaseError.extend({ code: z.literal(401) }),
132
- BaseError.extend({ code: z.literal(500) })
133
- ])
134
- ])
135
45
  ```
136
46
 
137
47
  ## Enum
138
48
 
139
49
  ```typescript
140
- // 문자열 enum
141
- const FishEnum = z.enum(['Salmon', 'Tuna', 'Trout'])
142
-
143
- FishEnum.parse('Salmon') // => "Salmon"
144
- FishEnum.parse('Swordfish') // => ❌ throws
145
-
146
- type Fish = z.infer<typeof FishEnum>
147
- // 'Salmon' | 'Tuna' | 'Trout'
50
+ const Status = z.enum(['pending', 'done', 'cancelled'])
51
+ type Status = z.infer<typeof Status> // 'pending' | 'done' | 'cancelled'
148
52
 
149
53
  // Native enum
150
- enum Fruits {
151
- Apple,
152
- Banana,
153
- }
154
- const FruitSchema = z.nativeEnum(Fruits)
54
+ enum Fruits { Apple, Banana }
55
+ z.nativeEnum(Fruits)
155
56
  ```
156
57
 
157
- ## Record
58
+ ## Record/Map/Set
158
59
 
159
60
  ```typescript
160
- const UserStore = z.record(z.string(), z.object({
161
- name: z.string(),
162
- }))
163
-
164
- type UserStore = z.infer<typeof UserStore>
61
+ z.record(z.string(), z.object({ name: z.string() }))
165
62
  // { [key: string]: { name: string } }
166
63
 
167
- // 사용
168
- const userStore: UserStore = {}
169
- userStore['77d2586b-9e8e-4ecf-8b21-ea7e0530eadd'] = {
170
- name: 'Carlotta',
171
- } // passes
172
-
173
- userStore['77d2586b-9e8e-4ecf-8b21-ea7e0530eadd'] = {
174
- whatever: 'Ice cream sundae',
175
- } // TypeError
64
+ z.map(z.string(), z.number()) // Map<string, number>
65
+ z.set(z.number()) // Set<number>
176
66
  ```
177
67
 
178
- ## Map & Set
68
+ ## 재귀 스키마
179
69
 
180
70
  ```typescript
181
- // Map
182
- const stringNumberMap = z.map(z.string(), z.number())
183
- type StringNumberMap = z.infer<typeof stringNumberMap>
184
- // Map<string, number>
185
-
186
- // Set
187
- const numberSet = z.set(z.number())
188
- type NumberSet = z.infer<typeof numberSet>
189
- // Set<number>
190
- ```
191
-
192
- ## 재귀 스키마 (JSON)
193
-
194
- ```typescript
195
- const literalSchema = z.union([z.string(), z.number(), z.boolean(), z.null()])
196
- type Literal = z.infer<typeof literalSchema>
197
- type Json = Literal | { [key: string]: Json } | Json[]
71
+ type Json = string | number | boolean | null | { [key: string]: Json } | Json[]
198
72
 
199
73
  const jsonSchema: z.ZodType<Json> = z.lazy(() =>
200
- z.union([literalSchema, z.array(jsonSchema), z.record(jsonSchema)])
74
+ z.union([
75
+ z.string(), z.number(), z.boolean(), z.null(),
76
+ z.array(jsonSchema),
77
+ z.record(jsonSchema)
78
+ ])
201
79
  )
202
-
203
- jsonSchema.parse({ foo: [1, 2, { bar: 'baz' }] })
204
80
  ```