@kood/claude-code 0.1.6 → 0.1.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 (75) hide show
  1. package/dist/index.js +21 -243
  2. package/package.json +1 -1
  3. package/templates/hono/CLAUDE.md +10 -6
  4. package/templates/hono/docs/deployment/index.md +5 -0
  5. package/templates/hono/docs/library/hono/index.md +6 -0
  6. package/templates/hono/docs/library/prisma/index.md +3 -0
  7. package/templates/npx/CLAUDE.md +8 -2
  8. package/templates/tanstack-start/CLAUDE.md +103 -255
  9. package/templates/tanstack-start/docs/deployment/cloudflare.md +37 -424
  10. package/templates/tanstack-start/docs/deployment/index.md +57 -286
  11. package/templates/tanstack-start/docs/deployment/nitro.md +36 -318
  12. package/templates/tanstack-start/docs/deployment/railway.md +40 -409
  13. package/templates/tanstack-start/docs/deployment/vercel.md +43 -465
  14. package/templates/tanstack-start/docs/design/accessibility.md +56 -326
  15. package/templates/tanstack-start/docs/design/color.md +37 -179
  16. package/templates/tanstack-start/docs/design/components.md +77 -311
  17. package/templates/tanstack-start/docs/design/index.md +24 -87
  18. package/templates/tanstack-start/docs/design/safe-area.md +51 -250
  19. package/templates/tanstack-start/docs/design/spacing.md +57 -276
  20. package/templates/tanstack-start/docs/design/tailwind-setup.md +45 -359
  21. package/templates/tanstack-start/docs/design/typography.md +40 -284
  22. package/templates/tanstack-start/docs/library/better-auth/2fa.md +27 -115
  23. package/templates/tanstack-start/docs/library/better-auth/advanced.md +22 -105
  24. package/templates/tanstack-start/docs/library/better-auth/index.md +17 -66
  25. package/templates/tanstack-start/docs/library/better-auth/plugins.md +11 -88
  26. package/templates/tanstack-start/docs/library/better-auth/session.md +12 -92
  27. package/templates/tanstack-start/docs/library/better-auth/setup.md +9 -91
  28. package/templates/tanstack-start/docs/library/prisma/cloudflare-d1.md +30 -358
  29. package/templates/tanstack-start/docs/library/prisma/config.md +27 -327
  30. package/templates/tanstack-start/docs/library/prisma/crud.md +46 -174
  31. package/templates/tanstack-start/docs/library/prisma/index.md +23 -113
  32. package/templates/tanstack-start/docs/library/prisma/relations.md +31 -153
  33. package/templates/tanstack-start/docs/library/prisma/schema.md +40 -217
  34. package/templates/tanstack-start/docs/library/prisma/setup.md +12 -112
  35. package/templates/tanstack-start/docs/library/prisma/transactions.md +20 -110
  36. package/templates/tanstack-start/docs/library/tanstack-query/index.md +12 -99
  37. package/templates/tanstack-start/docs/library/tanstack-query/invalidation.md +28 -107
  38. package/templates/tanstack-start/docs/library/tanstack-query/optimistic-updates.md +44 -146
  39. package/templates/tanstack-start/docs/library/tanstack-query/setup.md +11 -70
  40. package/templates/tanstack-start/docs/library/tanstack-query/use-mutation.md +33 -127
  41. package/templates/tanstack-start/docs/library/tanstack-query/use-query.md +49 -149
  42. package/templates/tanstack-start/docs/library/tanstack-start/auth-patterns.md +19 -112
  43. package/templates/tanstack-start/docs/library/tanstack-start/index.md +33 -80
  44. package/templates/tanstack-start/docs/library/tanstack-start/middleware.md +28 -106
  45. package/templates/tanstack-start/docs/library/tanstack-start/routing.md +21 -118
  46. package/templates/tanstack-start/docs/library/tanstack-start/server-functions.md +34 -246
  47. package/templates/tanstack-start/docs/library/tanstack-start/setup.md +6 -39
  48. package/templates/tanstack-start/docs/library/zod/basic-types.md +33 -145
  49. package/templates/tanstack-start/docs/library/zod/complex-types.md +32 -156
  50. package/templates/tanstack-start/docs/library/zod/index.md +22 -150
  51. package/templates/tanstack-start/docs/library/zod/transforms.md +20 -129
  52. package/templates/tanstack-start/docs/library/zod/validation.md +39 -155
  53. package/templates/hono/docs/commands/git.md +0 -145
  54. package/templates/hono/docs/mcp/context7.md +0 -106
  55. package/templates/hono/docs/mcp/index.md +0 -176
  56. package/templates/hono/docs/mcp/sequential-thinking.md +0 -101
  57. package/templates/hono/docs/mcp/serena.md +0 -269
  58. package/templates/hono/docs/mcp/sgrep.md +0 -105
  59. package/templates/hono/docs/skills/gemini-review/SKILL.md +0 -220
  60. package/templates/hono/docs/skills/gemini-review/references/checklists.md +0 -136
  61. package/templates/hono/docs/skills/gemini-review/references/prompt-templates.md +0 -303
  62. package/templates/npx/docs/commands/git.md +0 -145
  63. package/templates/npx/docs/mcp/index.md +0 -60
  64. package/templates/npx/docs/skills/gemini-review/SKILL.md +0 -220
  65. package/templates/npx/docs/skills/gemini-review/references/checklists.md +0 -134
  66. package/templates/npx/docs/skills/gemini-review/references/prompt-templates.md +0 -301
  67. package/templates/tanstack-start/docs/commands/git.md +0 -145
  68. package/templates/tanstack-start/docs/mcp/context7.md +0 -204
  69. package/templates/tanstack-start/docs/mcp/index.md +0 -177
  70. package/templates/tanstack-start/docs/mcp/sequential-thinking.md +0 -180
  71. package/templates/tanstack-start/docs/mcp/serena.md +0 -269
  72. package/templates/tanstack-start/docs/mcp/sgrep.md +0 -174
  73. package/templates/tanstack-start/docs/skills/gemini-review/SKILL.md +0 -220
  74. package/templates/tanstack-start/docs/skills/gemini-review/references/checklists.md +0 -144
  75. package/templates/tanstack-start/docs/skills/gemini-review/references/prompt-templates.md +0 -292
@@ -1,21 +1,25 @@
1
1
  # Zod
2
2
 
3
- > **Version**: 4.x | TypeScript Schema Validation
3
+ > **v4** | TypeScript Schema Validation
4
+
5
+ @basic-types.md
6
+ @complex-types.md
7
+ @transforms.md
8
+ @validation.md
4
9
 
5
10
  ---
6
11
 
7
- ## 🚀 Quick Reference (복사용)
12
+ ## Quick Reference
8
13
 
9
14
  ```typescript
10
15
  // 기본 스키마
11
16
  const schema = z.object({
12
- email: z.email(), // v4 (string().email() 아님!)
17
+ email: z.email(), // v4! (string().email() 아님)
13
18
  name: z.string().min(1).trim(),
14
- website: z.url().optional(), // v4 (string().url() 아님!)
19
+ website: z.url().optional(), // v4! (string().url() 아님)
15
20
  age: z.number().int().positive(),
16
21
  })
17
22
 
18
- // 타입 추출
19
23
  type Input = z.infer<typeof schema>
20
24
 
21
25
  // 파싱
@@ -28,159 +32,27 @@ export const createUser = createServerFn({ method: 'POST' })
28
32
  .handler(async ({ data }) => prisma.user.create({ data }))
29
33
  ```
30
34
 
31
- ### v4 새 API (⚠️ 중요)
35
+ ### ⚠️ v4 새 API
32
36
 
33
37
  ```typescript
34
- // ✅ v4 사용
35
- z.email()
36
- z.url()
37
- z.uuid()
38
- z.iso.date()
39
- z.iso.datetime()
38
+ // ✅ v4
39
+ z.email() z.url() z.uuid()
40
+ z.iso.date() z.iso.datetime()
40
41
 
41
- // ❌ v3 (deprecated)
42
- z.string().email()
43
- z.string().url()
42
+ // ❌ v3 deprecated
43
+ z.string().email() z.string().url()
44
44
  ```
45
45
 
46
- ---
47
-
48
- ## 문서 구조
49
-
50
- - [기본 타입](./basic-types.md) - 문자열, 숫자, 불리언, 날짜
51
- - [복합 타입](./complex-types.md) - 객체, 배열, 튜플, 유니온, Enum
52
- - [변환](./transforms.md) - Transform, Coerce, Preprocess
53
- - [검증](./validation.md) - Refinement, Superrefine, 커스텀 검증
54
-
55
- ## 빠른 시작
56
-
57
- ```bash
58
- yarn add zod
59
- ```
60
-
61
- ### 기본 사용법
46
+ ### v4 주요 변경
62
47
 
63
48
  ```typescript
64
- import { z } from 'zod'
65
-
66
- // 스키마 생성
67
- const mySchema = z.string()
68
-
69
- // parsing (실패 시 에러 throw)
70
- mySchema.parse('tuna') // => "tuna"
71
- mySchema.parse(12) // => throws ZodError
72
-
73
- // safe parsing (에러를 던지지 않음)
74
- mySchema.safeParse('tuna') // => { success: true; data: "tuna" }
75
- mySchema.safeParse(12) // => { success: false; error: ZodError }
76
- ```
77
-
78
- ### 타입 추론
79
-
80
- ```typescript
81
- import { z } from 'zod'
82
-
83
- const Player = z.object({
84
- username: z.string(),
85
- xp: z.number(),
86
- })
87
-
88
- // 스키마에서 타입 추출
89
- type Player = z.infer<typeof Player>
90
-
91
- // 타입 안전하게 사용
92
- const player: Player = { username: 'billie', xp: 100 }
93
- ```
94
-
95
- ## v4 주요 변경사항
96
-
97
- ### 에러 커스터마이징 통합
98
-
99
- ```typescript
100
- // v3 (deprecated)
101
- z.string().min(5, { message: "Too short." })
102
- z.string({ invalid_type_error: "Not a string", required_error: "Required" })
103
-
104
- // v4 - error 파라미터로 통합
49
+ // 에러 커스텀: message error
105
50
  z.string().min(5, { error: "Too short." })
106
- z.string({
107
- error: (issue) => issue.input === undefined
108
- ? "This field is required"
109
- : "Not a string"
110
- })
111
- ```
112
-
113
- ### 새로운 최상위 문자열 포맷 API
114
-
115
- ```typescript
116
- // v3 (deprecated)
117
- z.string().email()
118
- z.string().uuid()
119
-
120
- // v4 - 최상위 API (더 나은 tree-shaking)
121
- z.email()
122
- z.uuid()
123
- z.url()
124
- z.base64()
125
- z.nanoid()
126
- z.cuid()
127
- z.cuid2()
128
- z.ulid()
129
- z.ipv4()
130
- z.ipv6()
131
- z.iso.date()
132
- z.iso.time()
133
- z.iso.datetime()
134
- ```
135
-
136
- ### Strict/Loose 객체
137
-
138
- ```typescript
139
- // v3
140
- z.object({ name: z.string() }).strict()
141
- z.object({ name: z.string() }).passthrough()
142
-
143
- // v4
144
- z.strictObject({ name: z.string() })
145
- z.looseObject({ name: z.string() })
146
- ```
147
51
 
148
- ### Refinements가 스키마 내부에 저장
52
+ // Strict/Loose 객체
53
+ z.strictObject({ name: z.string() }) // 추가 키 에러
54
+ z.looseObject({ name: z.string() }) // 추가 키 통과
149
55
 
150
- ```typescript
151
- // v3 - ZodEffects로 래핑되어 .min() 호출 불가
152
- z.string()
153
- .refine(val => val.includes("@"))
154
- .min(5) // ❌ Property 'min' does not exist on type ZodEffects
155
-
156
- // v4 - 스키마 내부에 저장되어 체이닝 가능
157
- z.string()
158
- .refine(val => val.includes("@"))
159
- .min(5) // ✅
160
- ```
161
-
162
- ## TanStack Start와 함께 사용
163
-
164
- ```typescript
165
- import { createServerFn } from '@tanstack/react-start'
166
- import { zodValidator } from '@tanstack/react-start/validators'
167
- import { z } from 'zod'
168
-
169
- const createUserSchema = z.object({
170
- email: z.email(), // v4 새 API
171
- name: z.string().min(1).max(100).trim(),
172
- bio: z.string().max(500).optional(),
173
- })
174
-
175
- export const createUser = createServerFn({ method: 'POST' })
176
- .inputValidator(zodValidator(createUserSchema))
177
- .handler(async ({ data }) => {
178
- // data는 타입 안전함
179
- return prisma.user.create({ data })
180
- })
56
+ // Refinement 체이닝 가능
57
+ z.string().refine(val => val.includes("@")).min(5) // v4
181
58
  ```
182
-
183
- ## 참고 자료
184
-
185
- - [Zod 공식 문서](https://zod.dev)
186
- - [Zod GitHub](https://github.com/colinhacks/zod)
@@ -1,148 +1,56 @@
1
1
  # Zod - 변환
2
2
 
3
- > **상위 문서**: [Zod](./index.md)
4
-
5
3
  ## Transform
6
4
 
7
- 입력값을 다른 형태로 변환합니다.
8
-
9
5
  ```typescript
10
6
  const stringToLength = z.string().transform((val) => val.length)
7
+ stringToLength.parse('hello') // => 5
11
8
 
12
- stringToLength.parse('string') // => 6
13
-
14
- type MySchemaIn = z.input<typeof stringToLength> // string
15
- type MySchemaOut = z.output<typeof stringToLength> // number
16
- ```
17
-
18
- ### 체인으로 변환
19
-
20
- ```typescript
21
- const emailToLower = z.string()
22
- .email()
23
- .transform((email) => email.toLowerCase())
24
-
25
- emailToLower.parse('User@Example.COM') // => "user@example.com"
26
- ```
27
-
28
- ## Pipe를 사용한 Transform
29
-
30
- ```typescript
31
- const stringToLength = z.string().pipe(z.transform(val => val.length))
32
-
33
- stringToLength.parse('hello') // => 5
9
+ type In = z.input<typeof stringToLength> // string
10
+ type Out = z.output<typeof stringToLength> // number
34
11
  ```
35
12
 
36
- ### 검증 후 변환
13
+ ## Pipe (검증 후 변환)
37
14
 
38
15
  ```typescript
39
16
  const stringToNumber = z.string()
40
17
  .transform((val) => parseInt(val, 10))
41
18
  .pipe(z.number().min(0).max(100))
42
19
 
43
- stringToNumber.parse('50') // => 50
44
- stringToNumber.parse('150') // => throws
45
- ```
46
-
47
- ## Transform 내에서 검증
48
-
49
- ```typescript
50
- const coercedInt = z.transform((val, ctx) => {
51
- try {
52
- const parsed = Number.parseInt(String(val))
53
- return parsed
54
- } catch (e) {
55
- ctx.issues.push({
56
- code: 'custom',
57
- message: 'Not a number',
58
- input: val,
59
- })
60
- return z.NEVER // 타입에 영향 없이 에러 반환
61
- }
62
- })
20
+ stringToNumber.parse('50') // => 50
21
+ stringToNumber.parse('150') // throws
63
22
  ```
64
23
 
65
24
  ## Coerce (강제 변환)
66
25
 
67
- 입력값을 자동으로 해당 타입으로 변환합니다.
68
-
69
- ```typescript
70
- z.coerce.string() // 모든 값을 문자열로
71
- z.coerce.number() // 모든 값을 숫자로
72
- z.coerce.boolean() // 모든 값을 불리언으로
73
- z.coerce.date() // 모든 값을 날짜로
74
- z.coerce.bigint() // 모든 값을 BigInt로
75
- ```
76
-
77
- ### v4 Coerce 입력 타입 변경
78
-
79
26
  ```typescript
80
- const schema = z.coerce.string()
81
- type schemaInput = z.input<typeof schema>
82
- // v3: string
83
- // v4: unknown // ⚠️ 주의: 입력 타입이 unknown으로 변경됨
84
- ```
27
+ z.coerce.string() // 모든 값 → 문자열
28
+ z.coerce.number() // 모든 숫자
29
+ z.coerce.boolean() // 모든 값 → 불리언
30
+ z.coerce.date() // 모든 날짜
31
+ z.coerce.bigint() // 모든 값 → BigInt
85
32
 
86
- ### Coerce 예시
33
+ z.coerce.number().parse("42") // => 42
34
+ z.coerce.date().parse("2021-01-01") // => Date
87
35
 
88
- ```typescript
89
- // string coercion
90
- z.coerce.string().parse(123) // => "123"
91
- z.coerce.string().parse(true) // => "true"
92
- z.coerce.string().parse(null) // => "null"
93
-
94
- // number coercion
95
- z.coerce.number().parse("42") // => 42
96
- z.coerce.number().parse(true) // => 1
97
- z.coerce.number().parse(false) // => 0
98
-
99
- // boolean coercion
100
- z.coerce.boolean().parse("true") // => true
101
- z.coerce.boolean().parse("") // => false
102
- z.coerce.boolean().parse(1) // => true
103
-
104
- // date coercion
105
- z.coerce.date().parse("2021-01-01") // => Date
106
- z.coerce.date().parse(1609459200000) // => Date
36
+ // ⚠️ v4: 입력 타입이 unknown으로 변경됨
37
+ type In = z.input<typeof z.coerce.string()> // unknown
107
38
  ```
108
39
 
109
40
  ### 환경변수 불리언 (v4 권장)
110
41
 
111
- `z.coerce.boolean()` 대신 더 정교한 `z.stringbool()` 사용:
112
-
113
42
  ```typescript
114
- const strbool = z.stringbool()
115
-
116
- strbool.parse("true") // => true
117
- strbool.parse("yes") // => true
118
- strbool.parse("1") // => true
119
- strbool.parse("false") // => false
120
- strbool.parse("no") // => false
121
- strbool.parse("0") // => false
43
+ z.stringbool() // "true"/"yes"/"1" → true
44
+ // "false"/"no"/"0" → false
122
45
  ```
123
46
 
124
47
  ## Preprocess
125
48
 
126
- 검증 전에 데이터를 전처리합니다.
127
-
128
- ```typescript
129
- const castToString = z.preprocess((val) => String(val), z.string())
130
-
131
- castToString.parse(123) // => "123"
132
- castToString.parse(null) // => "null"
133
- ```
134
-
135
- ### Preprocess vs Coerce
136
-
137
49
  ```typescript
138
- // preprocess - 커스텀 로직 가능
139
- const trimmedString = z.preprocess(
50
+ const trimmed = z.preprocess(
140
51
  (val) => typeof val === 'string' ? val.trim() : val,
141
52
  z.string()
142
53
  )
143
-
144
- // coerce - 내장 변환만
145
- const coercedString = z.coerce.string()
146
54
  ```
147
55
 
148
56
  ## 입력/출력 타입 분리
@@ -152,23 +60,6 @@ const Schema = z.object({
152
60
  createdAt: z.string().transform((str) => new Date(str)),
153
61
  })
154
62
 
155
- type SchemaInput = z.input<typeof Schema>
156
- // { createdAt: string }
157
-
158
- type SchemaOutput = z.output<typeof Schema>
159
- // { createdAt: Date }
160
- ```
161
-
162
- ## 조건부 변환
163
-
164
- ```typescript
165
- const conditionalTransform = z.string().transform((val) => {
166
- if (val === 'null') return null
167
- if (val === 'undefined') return undefined
168
- return val
169
- })
170
-
171
- conditionalTransform.parse('hello') // => "hello"
172
- conditionalTransform.parse('null') // => null
173
- conditionalTransform.parse('undefined') // => undefined
63
+ type SchemaInput = z.input<typeof Schema> // { createdAt: string }
64
+ type SchemaOutput = z.output<typeof Schema> // { createdAt: Date }
174
65
  ```
@@ -1,208 +1,92 @@
1
1
  # Zod - 검증
2
2
 
3
- > **상위 문서**: [Zod](./index.md)
4
-
5
- ## Refinement (커스텀 검증)
3
+ ## Refinement
6
4
 
7
5
  ```typescript
8
- // v4 - error 파라미터 사용 (message는 deprecated)
6
+ // v4: message error
9
7
  const PasswordSchema = z.string()
10
8
  .min(8)
11
- .refine((val) => /[A-Z]/.test(val), {
12
- error: '대문자가 포함되어야 합니다', // v4: message error
13
- })
14
- .refine((val) => /[0-9]/.test(val), {
15
- error: '숫자가 포함되어야 합니다',
16
- })
17
- ```
9
+ .refine((val) => /[A-Z]/.test(val), { error: '대문자 필수' })
10
+ .refine((val) => /[0-9]/.test(val), { error: '숫자 필수' })
18
11
 
19
- ### v4 Refinement 체이닝 개선
12
+ // v4: refinement 체이닝 가능
13
+ z.string().refine(val => val.includes("@")).min(5) // ✅
20
14
 
21
- ```typescript
22
- // v3 - ZodEffects로 래핑되어 .min() 호출 불가
23
- z.string()
24
- .refine(val => val.includes("@"))
25
- .min(5) // ❌ 에러
26
-
27
- // v4 - 스키마 내부에 저장되어 체이닝 가능
28
- z.string()
29
- .refine(val => val.includes("@"))
30
- .min(5) // ✅ 정상 작동
31
- ```
32
-
33
- ### Async Refinement
34
-
35
- ```typescript
15
+ // Async
36
16
  const schema = z.string().refine(async (val) => val.length <= 8)
37
-
38
- await schema.parseAsync('hello') // => "hello"
39
- ```
40
-
41
- ### 이메일 중복 검사 예시
42
-
43
- ```typescript
44
- const EmailSchema = z.email() // v4 새 API
45
- .refine(async (email) => {
46
- const exists = await checkEmailExists(email)
47
- return !exists
48
- }, {
49
- error: '이미 사용 중인 이메일입니다', // v4: message → error
50
- })
17
+ await schema.parseAsync('hello')
51
18
  ```
52
19
 
53
- ## Superrefine (고급 검증)
54
-
55
- 여러 이슈를 추가하거나 경로를 지정할 수 있습니다.
56
-
57
- > **v4 변경**: `ctx.path`는 더 이상 사용할 수 없습니다 (성능 향상을 위해 제거됨)
20
+ ## Superrefine (고급)
58
21
 
59
22
  ```typescript
60
- const Schema = z.object({
23
+ z.object({
61
24
  password: z.string(),
62
25
  confirmPassword: z.string(),
63
26
  }).superRefine((data, ctx) => {
64
27
  if (data.password !== data.confirmPassword) {
65
28
  ctx.addIssue({
66
29
  code: z.ZodIssueCode.custom,
67
- message: '비밀번호가 일치하지 않습니다',
30
+ message: '비밀번호 불일치',
68
31
  path: ['confirmPassword'],
69
32
  })
70
33
  }
71
- // ctx.path // ❌ v4에서 더 이상 사용 불가
72
34
  })
35
+ // ⚠️ v4: ctx.path 사용 불가
73
36
  ```
74
37
 
75
- ### 복잡한 검증 로직
38
+ ## 커스텀 스키마
76
39
 
77
40
  ```typescript
78
- const UserSchema = z.object({
79
- username: z.string(),
80
- email: z.string().email(),
81
- age: z.number(),
82
- }).superRefine((data, ctx) => {
83
- // 여러 조건 검증
84
- if (data.age < 13 && data.email.includes('work')) {
85
- ctx.addIssue({
86
- code: z.ZodIssueCode.custom,
87
- message: '13세 미만은 업무용 이메일을 사용할 수 없습니다',
88
- path: ['email'],
89
- })
90
- }
41
+ const px = z.custom<`${number}px`>((val) =>
42
+ typeof val === 'string' && /^\d+px$/.test(val)
43
+ )
91
44
 
92
- if (data.username.toLowerCase() === 'admin' && data.age < 18) {
93
- ctx.addIssue({
94
- code: z.ZodIssueCode.custom,
95
- message: 'admin 사용자명은 18세 이상만 사용 가능합니다',
96
- path: ['username'],
97
- })
98
- }
99
- })
45
+ px.parse('42px') //
46
+ px.parse('42vw') // throws
100
47
  ```
101
48
 
102
- ## 커스텀 스키마
49
+ ## 에러 처리
103
50
 
104
51
  ```typescript
105
- const px = z.custom<`${number}px`>((val) => {
106
- return typeof val === 'string' ? /^\d+px$/.test(val) : false
107
- })
52
+ const result = schema.safeParse(data)
108
53
 
109
- type Px = z.infer<typeof px> // `${number}px`
54
+ if (!result.success) {
55
+ result.error.errors.forEach((err) => {
56
+ console.log(err.path, err.message, err.code)
57
+ })
110
58
 
111
- px.parse('42px') // '42px'
112
- px.parse('42vw') // throws
59
+ // 평면화
60
+ const flat = result.error.flatten()
61
+ // { fieldErrors: { email: ['Invalid email'] } }
62
+ }
113
63
  ```
114
64
 
115
- ## TanStack Start 함께 사용
116
-
117
- ### Server Function 입력 검증
65
+ ## TanStack Start 연동
118
66
 
119
67
  ```typescript
120
- import { createServerFn } from '@tanstack/react-start'
121
- import { zodValidator } from '@tanstack/react-start/validators'
122
- import { z } from 'zod'
123
-
124
- const createUserSchema = z.object({
125
- email: z.email(), // v4 새 API
126
- name: z.string().min(1).max(100).trim(),
127
- bio: z.string().max(500).optional(),
128
- })
129
-
68
+ // Server Function
130
69
  export const createUser = createServerFn({ method: 'POST' })
131
70
  .inputValidator(zodValidator(createUserSchema))
132
- .handler(async ({ data }) => {
133
- // data는 타입 안전함
134
- return prisma.user.create({ data })
135
- })
136
- ```
137
-
138
- ### 환경 변수 검증
71
+ .handler(async ({ data }) => prisma.user.create({ data }))
139
72
 
140
- ```typescript
141
- const envSchema = z.object({
73
+ // 환경 변수
74
+ const env = z.object({
142
75
  NODE_ENV: z.enum(['development', 'production', 'test']),
143
76
  DATABASE_URL: z.string().url(),
144
77
  API_SECRET: z.string().min(32),
145
- })
146
-
147
- export const env = envSchema.parse(process.env)
148
- ```
149
-
150
- ### Middleware에서 사용
151
-
152
- ```typescript
153
- import { createMiddleware } from '@tanstack/react-start'
154
- import { zodValidator } from '@tanstack/react-start/validators'
155
- import { z } from 'zod'
156
-
157
- const mySchema = z.object({
158
- workspaceId: z.string(),
159
- })
78
+ }).parse(process.env)
160
79
 
80
+ // Middleware
161
81
  const workspaceMiddleware = createMiddleware({ type: 'function' })
162
- .inputValidator(zodValidator(mySchema))
163
- .server(({ next, data }) => {
164
- console.log('Workspace ID:', data.workspaceId)
165
- return next()
166
- })
167
- ```
168
-
169
- ## 에러 처리
170
-
171
- ```typescript
172
- const result = UserSchema.safeParse(data)
173
-
174
- if (!result.success) {
175
- // ZodError 객체
176
- const errors = result.error.errors
177
-
178
- errors.forEach((err) => {
179
- console.log('Path:', err.path)
180
- console.log('Message:', err.message)
181
- console.log('Code:', err.code)
182
- })
183
-
184
- // 평면화된 에러
185
- const flatErrors = result.error.flatten()
186
- console.log(flatErrors.fieldErrors)
187
- // { email: ['Invalid email'], name: ['Required'] }
188
- }
82
+ .inputValidator(zodValidator(z.object({ workspaceId: z.string() })))
83
+ .server(({ next, data }) => next())
189
84
  ```
190
85
 
191
- ## Zod Mini (경량 버전)
192
-
193
- v4에서 새롭게 추가된 경량 API:
86
+ ## Zod Mini (v4 경량)
194
87
 
195
88
  ```typescript
196
89
  import * as z from "zod/mini"
197
90
 
198
- // regular Zod
199
- z.string().min(5).max(10).trim()
200
-
201
- // Zod Mini - check 메서드로 통합
202
- z.string().check(
203
- z.minLength(5),
204
- z.maxLength(10),
205
- z.toLowerCase(),
206
- z.trim(),
207
- )
91
+ z.string().check(z.minLength(5), z.maxLength(10), z.trim())
208
92
  ```