@kood/claude-code 0.1.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.d.ts +2 -0
- package/dist/index.js +297 -0
- package/package.json +47 -0
- package/templates/hono/CLAUDE.md +376 -0
- package/templates/hono/docs/deployment/cloudflare.md +328 -0
- package/templates/hono/docs/deployment/index.md +291 -0
- package/templates/hono/docs/git/index.md +180 -0
- package/templates/hono/docs/library/hono/error-handling.md +400 -0
- package/templates/hono/docs/library/hono/index.md +241 -0
- package/templates/hono/docs/library/hono/middleware.md +334 -0
- package/templates/hono/docs/library/hono/rpc.md +454 -0
- package/templates/hono/docs/library/hono/validation.md +328 -0
- package/templates/hono/docs/library/prisma/index.md +427 -0
- package/templates/hono/docs/library/zod/index.md +413 -0
- package/templates/hono/docs/mcp/context7.md +106 -0
- package/templates/hono/docs/mcp/index.md +94 -0
- package/templates/hono/docs/mcp/sequential-thinking.md +101 -0
- package/templates/hono/docs/mcp/sgrep.md +105 -0
- package/templates/hono/docs/skills/gemini-review/SKILL.md +220 -0
- package/templates/hono/docs/skills/gemini-review/references/checklists.md +136 -0
- package/templates/hono/docs/skills/gemini-review/references/prompt-templates.md +303 -0
- package/templates/tanstack-start/CLAUDE.md +279 -0
- package/templates/tanstack-start/docs/architecture/architecture.md +547 -0
- package/templates/tanstack-start/docs/deployment/cloudflare.md +346 -0
- package/templates/tanstack-start/docs/deployment/index.md +102 -0
- package/templates/tanstack-start/docs/deployment/nitro.md +211 -0
- package/templates/tanstack-start/docs/deployment/railway.md +364 -0
- package/templates/tanstack-start/docs/deployment/vercel.md +287 -0
- package/templates/tanstack-start/docs/design/accessibility.md +433 -0
- package/templates/tanstack-start/docs/design/color.md +235 -0
- package/templates/tanstack-start/docs/design/components.md +409 -0
- package/templates/tanstack-start/docs/design/index.md +107 -0
- package/templates/tanstack-start/docs/design/safe-area.md +317 -0
- package/templates/tanstack-start/docs/design/spacing.md +341 -0
- package/templates/tanstack-start/docs/design/tailwind-setup.md +470 -0
- package/templates/tanstack-start/docs/design/typography.md +324 -0
- package/templates/tanstack-start/docs/git/index.md +203 -0
- package/templates/tanstack-start/docs/guides/best-practices.md +753 -0
- package/templates/tanstack-start/docs/guides/getting-started.md +304 -0
- package/templates/tanstack-start/docs/guides/husky-lint-staged.md +303 -0
- package/templates/tanstack-start/docs/guides/prettier.md +189 -0
- package/templates/tanstack-start/docs/guides/project-templates.md +710 -0
- package/templates/tanstack-start/docs/library/better-auth/2fa.md +136 -0
- package/templates/tanstack-start/docs/library/better-auth/advanced.md +138 -0
- package/templates/tanstack-start/docs/library/better-auth/index.md +83 -0
- package/templates/tanstack-start/docs/library/better-auth/plugins.md +111 -0
- package/templates/tanstack-start/docs/library/better-auth/session.md +127 -0
- package/templates/tanstack-start/docs/library/better-auth/setup.md +123 -0
- package/templates/tanstack-start/docs/library/prisma/crud.md +218 -0
- package/templates/tanstack-start/docs/library/prisma/index.md +165 -0
- package/templates/tanstack-start/docs/library/prisma/relations.md +191 -0
- package/templates/tanstack-start/docs/library/prisma/schema.md +177 -0
- package/templates/tanstack-start/docs/library/prisma/setup.md +156 -0
- package/templates/tanstack-start/docs/library/prisma/transactions.md +140 -0
- package/templates/tanstack-start/docs/library/tanstack-query/index.md +146 -0
- package/templates/tanstack-start/docs/library/tanstack-query/invalidation.md +146 -0
- package/templates/tanstack-start/docs/library/tanstack-query/optimistic-updates.md +196 -0
- package/templates/tanstack-start/docs/library/tanstack-query/setup.md +110 -0
- package/templates/tanstack-start/docs/library/tanstack-query/use-mutation.md +170 -0
- package/templates/tanstack-start/docs/library/tanstack-query/use-query.md +173 -0
- package/templates/tanstack-start/docs/library/tanstack-start/auth-patterns.md +171 -0
- package/templates/tanstack-start/docs/library/tanstack-start/index.md +114 -0
- package/templates/tanstack-start/docs/library/tanstack-start/middleware.md +142 -0
- package/templates/tanstack-start/docs/library/tanstack-start/routing.md +163 -0
- package/templates/tanstack-start/docs/library/tanstack-start/server-functions.md +128 -0
- package/templates/tanstack-start/docs/library/tanstack-start/setup.md +85 -0
- package/templates/tanstack-start/docs/library/zod/basic-types.md +186 -0
- package/templates/tanstack-start/docs/library/zod/complex-types.md +204 -0
- package/templates/tanstack-start/docs/library/zod/index.md +186 -0
- package/templates/tanstack-start/docs/library/zod/transforms.md +174 -0
- package/templates/tanstack-start/docs/library/zod/validation.md +208 -0
- package/templates/tanstack-start/docs/mcp/context7.md +204 -0
- package/templates/tanstack-start/docs/mcp/index.md +116 -0
- package/templates/tanstack-start/docs/mcp/sequential-thinking.md +180 -0
- package/templates/tanstack-start/docs/mcp/sgrep.md +174 -0
- package/templates/tanstack-start/docs/skills/gemini-review/SKILL.md +220 -0
- package/templates/tanstack-start/docs/skills/gemini-review/references/checklists.md +150 -0
- package/templates/tanstack-start/docs/skills/gemini-review/references/prompt-templates.md +293 -0
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# TanStack Start - Routing
|
|
2
|
+
|
|
3
|
+
> **상위 문서**: [TanStack Start](./index.md)
|
|
4
|
+
|
|
5
|
+
TanStack Start는 파일 기반 라우팅을 지원합니다.
|
|
6
|
+
|
|
7
|
+
## 기본 라우트
|
|
8
|
+
|
|
9
|
+
```tsx
|
|
10
|
+
// routes/about.tsx
|
|
11
|
+
import { createFileRoute } from '@tanstack/react-router'
|
|
12
|
+
|
|
13
|
+
export const Route = createFileRoute('/about')({
|
|
14
|
+
component: AboutPage,
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
function AboutPage() {
|
|
18
|
+
return <h1>About</h1>
|
|
19
|
+
}
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Loader를 사용한 데이터 로딩
|
|
23
|
+
|
|
24
|
+
```tsx
|
|
25
|
+
// routes/index.tsx
|
|
26
|
+
import { createFileRoute } from '@tanstack/react-router'
|
|
27
|
+
|
|
28
|
+
export const Route = createFileRoute('/')({
|
|
29
|
+
component: Page,
|
|
30
|
+
loader: async () => {
|
|
31
|
+
const res = await fetch('https://api.example.com/posts')
|
|
32
|
+
return res.json()
|
|
33
|
+
},
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
function Page() {
|
|
37
|
+
const posts = Route.useLoaderData()
|
|
38
|
+
return (
|
|
39
|
+
<ul>
|
|
40
|
+
{posts.map((post) => (
|
|
41
|
+
<li key={post.id}>{post.title}</li>
|
|
42
|
+
))}
|
|
43
|
+
</ul>
|
|
44
|
+
)
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## 동적 라우트
|
|
49
|
+
|
|
50
|
+
```tsx
|
|
51
|
+
// routes/users/$id.tsx
|
|
52
|
+
import { createFileRoute } from '@tanstack/react-router'
|
|
53
|
+
|
|
54
|
+
export const Route = createFileRoute('/users/$id')({
|
|
55
|
+
loader: async ({ params }) => {
|
|
56
|
+
const user = await getUserById(params.id)
|
|
57
|
+
return { user }
|
|
58
|
+
},
|
|
59
|
+
component: UserDetailPage,
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
function UserDetailPage() {
|
|
63
|
+
const { user } = Route.useLoaderData()
|
|
64
|
+
return <h1>{user.name}</h1>
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## SSR 설정
|
|
69
|
+
|
|
70
|
+
```tsx
|
|
71
|
+
// routes/posts/$postId.tsx
|
|
72
|
+
export const Route = createFileRoute('/posts/$postId')({
|
|
73
|
+
ssr: true, // SSR 활성화
|
|
74
|
+
beforeLoad: () => {
|
|
75
|
+
console.log('서버에서 초기 요청 시 실행')
|
|
76
|
+
},
|
|
77
|
+
loader: () => {
|
|
78
|
+
console.log('서버에서 초기 요청 시 실행')
|
|
79
|
+
},
|
|
80
|
+
component: () => <div>서버에서 렌더링됨</div>,
|
|
81
|
+
})
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### SSR 옵션
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
// ssr: true - 전체 SSR (기본값)
|
|
88
|
+
// ssr: false - 클라이언트 사이드만
|
|
89
|
+
// ssr: 'data-only' - 데이터만 서버에서 로드, 렌더링은 클라이언트
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Server Routes (API)
|
|
93
|
+
|
|
94
|
+
### 기본 API 라우트
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
// routes/api/hello.ts
|
|
98
|
+
import { createFileRoute } from '@tanstack/react-router'
|
|
99
|
+
|
|
100
|
+
export const Route = createFileRoute('/api/hello')({
|
|
101
|
+
server: {
|
|
102
|
+
handlers: {
|
|
103
|
+
GET: async ({ request }) => {
|
|
104
|
+
return new Response('Hello, World!')
|
|
105
|
+
},
|
|
106
|
+
POST: async ({ request }) => {
|
|
107
|
+
const body = await request.json()
|
|
108
|
+
return new Response(`Hello, ${body.name}!`)
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
})
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### JSON 응답
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
import { createFileRoute } from '@tanstack/react-router'
|
|
119
|
+
import { json } from '@tanstack/react-start'
|
|
120
|
+
|
|
121
|
+
export const Route = createFileRoute('/api/users')({
|
|
122
|
+
server: {
|
|
123
|
+
handlers: {
|
|
124
|
+
GET: async ({ request }) => {
|
|
125
|
+
const users = await getUsers()
|
|
126
|
+
return json({ users })
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
})
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## 라우트 파일 구조
|
|
134
|
+
|
|
135
|
+
```
|
|
136
|
+
routes/
|
|
137
|
+
├── __root.tsx → Root layout
|
|
138
|
+
├── index.tsx → /
|
|
139
|
+
├── about.tsx → /about
|
|
140
|
+
├── users/
|
|
141
|
+
│ ├── index.tsx → /users
|
|
142
|
+
│ └── $id.tsx → /users/:id
|
|
143
|
+
├── posts/
|
|
144
|
+
│ ├── index.tsx → /posts
|
|
145
|
+
│ ├── $postId.tsx → /posts/:postId
|
|
146
|
+
│ └── new.tsx → /posts/new
|
|
147
|
+
└── api/
|
|
148
|
+
├── hello.ts → /api/hello
|
|
149
|
+
└── users.ts → /api/users
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Catch-All 라우트
|
|
153
|
+
|
|
154
|
+
```tsx
|
|
155
|
+
// routes/$.tsx - 모든 매칭되지 않는 경로 처리
|
|
156
|
+
export const Route = createFileRoute('/$')({
|
|
157
|
+
component: NotFoundPage,
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
function NotFoundPage() {
|
|
161
|
+
return <h1>404 - Page Not Found</h1>
|
|
162
|
+
}
|
|
163
|
+
```
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# TanStack Start - Server Functions
|
|
2
|
+
|
|
3
|
+
> **상위 문서**: [TanStack Start](./index.md)
|
|
4
|
+
|
|
5
|
+
Server Functions는 서버에서만 실행되는 타입 안전한 함수입니다.
|
|
6
|
+
|
|
7
|
+
## 기본 Server Function
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
import { createServerFn } from '@tanstack/react-start'
|
|
11
|
+
|
|
12
|
+
// GET 요청 (데이터 조회)
|
|
13
|
+
export const getUsers = createServerFn({ method: 'GET' })
|
|
14
|
+
.handler(async () => {
|
|
15
|
+
return prisma.user.findMany()
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
// POST 요청 (데이터 생성/수정)
|
|
19
|
+
export const createUser = createServerFn({ method: 'POST' })
|
|
20
|
+
.handler(async () => {
|
|
21
|
+
return { success: true }
|
|
22
|
+
})
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Input Validation
|
|
26
|
+
|
|
27
|
+
### 기본 Validator
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
import { createServerFn } from '@tanstack/react-start'
|
|
31
|
+
|
|
32
|
+
export const createUser = createServerFn({ method: 'POST' })
|
|
33
|
+
.inputValidator((data: { email: string; name: string }) => data)
|
|
34
|
+
.handler(async ({ data }) => {
|
|
35
|
+
// data는 타입 안전함
|
|
36
|
+
return prisma.user.create({ data })
|
|
37
|
+
})
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Zod Validation 사용
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
import { createServerFn } from '@tanstack/react-start'
|
|
44
|
+
import { zodValidator } from '@tanstack/react-start/validators'
|
|
45
|
+
import { z } from 'zod'
|
|
46
|
+
|
|
47
|
+
const createUserSchema = z.object({
|
|
48
|
+
email: z.string().email(),
|
|
49
|
+
name: z.string().min(1).max(100),
|
|
50
|
+
bio: z.string().max(500).optional(),
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
export const createUser = createServerFn({ method: 'POST' })
|
|
54
|
+
.inputValidator(zodValidator(createUserSchema))
|
|
55
|
+
.handler(async ({ data }) => {
|
|
56
|
+
return prisma.user.create({ data })
|
|
57
|
+
})
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## FormData 처리
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
import { createServerFn } from '@tanstack/react-start'
|
|
64
|
+
|
|
65
|
+
export const submitForm = createServerFn({ method: 'POST' })
|
|
66
|
+
.inputValidator((formData: FormData) => {
|
|
67
|
+
const name = formData.get('name') as string
|
|
68
|
+
const email = formData.get('email') as string
|
|
69
|
+
return { name, email }
|
|
70
|
+
})
|
|
71
|
+
.handler(async ({ data }) => {
|
|
72
|
+
return prisma.user.create({ data })
|
|
73
|
+
})
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## 컴포넌트에서 호출
|
|
77
|
+
|
|
78
|
+
### useServerFn 사용
|
|
79
|
+
|
|
80
|
+
```tsx
|
|
81
|
+
import { useServerFn } from '@tanstack/react-start'
|
|
82
|
+
import { useQuery } from '@tanstack/react-query'
|
|
83
|
+
import { getServerPosts } from '~/lib/server-functions'
|
|
84
|
+
|
|
85
|
+
function PostList() {
|
|
86
|
+
const getPosts = useServerFn(getServerPosts)
|
|
87
|
+
|
|
88
|
+
const { data, isLoading, error } = useQuery({
|
|
89
|
+
queryKey: ['posts'],
|
|
90
|
+
queryFn: () => getPosts(),
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
if (isLoading) return <div>Loading...</div>
|
|
94
|
+
if (error) return <div>Error: {error.message}</div>
|
|
95
|
+
|
|
96
|
+
return (
|
|
97
|
+
<ul>
|
|
98
|
+
{data?.map((post) => (
|
|
99
|
+
<li key={post.id}>{post.title}</li>
|
|
100
|
+
))}
|
|
101
|
+
</ul>
|
|
102
|
+
)
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## 보안 패턴
|
|
107
|
+
|
|
108
|
+
### 서버 전용 데이터 보호
|
|
109
|
+
|
|
110
|
+
```tsx
|
|
111
|
+
// ❌ 잘못된 방법 - 클라이언트에 노출됨
|
|
112
|
+
export const Route = createFileRoute('/users')({
|
|
113
|
+
loader: () => {
|
|
114
|
+
const secret = process.env.SECRET // 클라이언트에 노출!
|
|
115
|
+
return fetch(`/api/users?key=${secret}`)
|
|
116
|
+
},
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
// ✅ 올바른 방법 - 서버 함수 사용
|
|
120
|
+
const getUsersSecurely = createServerFn().handler(() => {
|
|
121
|
+
const secret = process.env.SECRET // 서버에서만 접근
|
|
122
|
+
return fetch(`/api/users?key=${secret}`)
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
export const Route = createFileRoute('/users')({
|
|
126
|
+
loader: () => getUsersSecurely(),
|
|
127
|
+
})
|
|
128
|
+
```
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# TanStack Start - 설치 및 설정
|
|
2
|
+
|
|
3
|
+
> **상위 문서**: [TanStack Start](./index.md)
|
|
4
|
+
|
|
5
|
+
## 패키지 설치
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
yarn add @tanstack/react-start @tanstack/react-router vinxi
|
|
9
|
+
yarn add -D vite @vitejs/plugin-react vite-tsconfig-paths
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Vite 설정
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
// vite.config.ts
|
|
16
|
+
import { defineConfig } from 'vite'
|
|
17
|
+
import tsConfigPaths from 'vite-tsconfig-paths'
|
|
18
|
+
import { tanstackStart } from '@tanstack/react-start/plugin/vite'
|
|
19
|
+
import viteReact from '@vitejs/plugin-react'
|
|
20
|
+
|
|
21
|
+
export default defineConfig({
|
|
22
|
+
server: {
|
|
23
|
+
port: 3000,
|
|
24
|
+
},
|
|
25
|
+
plugins: [
|
|
26
|
+
tsConfigPaths(),
|
|
27
|
+
tanstackStart(),
|
|
28
|
+
viteReact(),
|
|
29
|
+
],
|
|
30
|
+
})
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## TypeScript 설정
|
|
34
|
+
|
|
35
|
+
```json
|
|
36
|
+
// tsconfig.json
|
|
37
|
+
{
|
|
38
|
+
"compilerOptions": {
|
|
39
|
+
"target": "ES2022",
|
|
40
|
+
"module": "ESNext",
|
|
41
|
+
"moduleResolution": "bundler",
|
|
42
|
+
"strict": true,
|
|
43
|
+
"esModuleInterop": true,
|
|
44
|
+
"skipLibCheck": true,
|
|
45
|
+
"jsx": "react-jsx",
|
|
46
|
+
"baseUrl": ".",
|
|
47
|
+
"paths": {
|
|
48
|
+
"~/*": ["./src/*"]
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
"include": ["src/**/*"]
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
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
|
+
## 환경 변수 검증
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
// lib/env.ts
|
|
76
|
+
import { z } from 'zod'
|
|
77
|
+
|
|
78
|
+
const envSchema = z.object({
|
|
79
|
+
NODE_ENV: z.enum(['development', 'production', 'test']),
|
|
80
|
+
DATABASE_URL: z.string().url(),
|
|
81
|
+
API_SECRET: z.string().min(32),
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
export const env = envSchema.parse(process.env)
|
|
85
|
+
```
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
# Zod - 기본 타입
|
|
2
|
+
|
|
3
|
+
> **상위 문서**: [Zod](./index.md)
|
|
4
|
+
|
|
5
|
+
## 문자열
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
z.string()
|
|
9
|
+
z.string().min(1) // 최소 길이
|
|
10
|
+
z.string().max(100) // 최대 길이
|
|
11
|
+
z.string().length(5) // 정확한 길이
|
|
12
|
+
z.string().regex(/^\d+$/) // 정규식
|
|
13
|
+
z.string().trim() // 공백 제거
|
|
14
|
+
z.string().toLowerCase() // 소문자 변환
|
|
15
|
+
z.string().toUpperCase() // 대문자 변환
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
### v4 새로운 최상위 문자열 포맷 API
|
|
19
|
+
|
|
20
|
+
```typescript
|
|
21
|
+
// 더 나은 tree-shaking을 위해 최상위 API 사용 권장
|
|
22
|
+
z.email() // 이메일 형식
|
|
23
|
+
z.url() // URL 형식
|
|
24
|
+
z.uuid() // UUID 형식
|
|
25
|
+
z.base64() // Base64 형식
|
|
26
|
+
z.base64url() // Base64 URL 형식
|
|
27
|
+
z.nanoid() // NanoID 형식
|
|
28
|
+
z.cuid() // CUID 형식
|
|
29
|
+
z.cuid2() // CUID2 형식
|
|
30
|
+
z.ulid() // ULID 형식
|
|
31
|
+
z.emoji() // 이모지 (단일 문자)
|
|
32
|
+
z.ipv4() // IPv4 주소
|
|
33
|
+
z.ipv6() // IPv6 주소
|
|
34
|
+
z.cidrv4() // IPv4 범위
|
|
35
|
+
z.cidrv6() // IPv6 범위
|
|
36
|
+
|
|
37
|
+
// ISO 날짜/시간 형식
|
|
38
|
+
z.iso.date() // ISO 날짜 (2024-01-15)
|
|
39
|
+
z.iso.time() // ISO 시간 (14:30:00)
|
|
40
|
+
z.iso.datetime() // ISO 날짜시간
|
|
41
|
+
z.iso.duration() // ISO 기간 (P1D, PT1H)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### 템플릿 리터럴 타입 (v4 신규)
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
const hello = z.templateLiteral(["hello, ", z.string()])
|
|
48
|
+
// `hello, ${string}`
|
|
49
|
+
|
|
50
|
+
const cssUnits = z.enum(["px", "em", "rem", "%"])
|
|
51
|
+
const css = z.templateLiteral([z.number(), cssUnits])
|
|
52
|
+
// `${number}px` | `${number}em` | `${number}rem` | `${number}%`
|
|
53
|
+
|
|
54
|
+
const email = z.templateLiteral([
|
|
55
|
+
z.string().min(1),
|
|
56
|
+
"@",
|
|
57
|
+
z.string().max(64),
|
|
58
|
+
])
|
|
59
|
+
// `${string}@${string}` (min/max refinements도 적용됨!)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## 숫자
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
z.number()
|
|
66
|
+
z.number().min(0) // 최솟값
|
|
67
|
+
z.number().max(100) // 최댓값
|
|
68
|
+
z.number().int() // 정수만
|
|
69
|
+
z.number().positive() // 양수만
|
|
70
|
+
z.number().negative() // 음수만
|
|
71
|
+
z.number().nonnegative() // 0 이상
|
|
72
|
+
z.number().nonpositive() // 0 이하
|
|
73
|
+
z.number().finite() // 유한 숫자
|
|
74
|
+
z.number().safe() // 안전한 정수 범위
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## 불리언
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
z.boolean()
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### 문자열 불리언 (v4 신규)
|
|
84
|
+
|
|
85
|
+
환경변수 스타일 불리언 변환:
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
const strbool = z.stringbool()
|
|
89
|
+
|
|
90
|
+
strbool.parse("true") // => true
|
|
91
|
+
strbool.parse("1") // => true
|
|
92
|
+
strbool.parse("yes") // => true
|
|
93
|
+
strbool.parse("on") // => true
|
|
94
|
+
strbool.parse("enabled") // => true
|
|
95
|
+
|
|
96
|
+
strbool.parse("false") // => false
|
|
97
|
+
strbool.parse("0") // => false
|
|
98
|
+
strbool.parse("no") // => false
|
|
99
|
+
strbool.parse("off") // => false
|
|
100
|
+
strbool.parse("disabled") // => false
|
|
101
|
+
|
|
102
|
+
// 커스텀 truthy/falsy 값
|
|
103
|
+
z.stringbool({
|
|
104
|
+
truthy: ["yes", "true"],
|
|
105
|
+
falsy: ["no", "false"]
|
|
106
|
+
})
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## 날짜
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
z.date()
|
|
113
|
+
z.date().min(new Date('2020-01-01'))
|
|
114
|
+
z.date().max(new Date('2030-12-31'))
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## BigInt
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
z.bigint()
|
|
121
|
+
z.bigint().positive()
|
|
122
|
+
z.bigint().negative()
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## 리터럴
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
z.literal('hello') // 정확히 'hello'만
|
|
129
|
+
z.literal(42) // 정확히 42만
|
|
130
|
+
z.literal(true) // 정확히 true만
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Null, Undefined, Void
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
z.null() // null만 허용
|
|
137
|
+
z.undefined() // undefined만 허용
|
|
138
|
+
z.void() // undefined 허용 (반환 타입용)
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Any, Unknown, Never
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
z.any() // 모든 타입 허용
|
|
145
|
+
z.unknown() // unknown 타입
|
|
146
|
+
z.never() // 값 없음 (유니온에서 사용)
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
> **v4 변경**: `z.any()`와 `z.unknown()`은 더 이상 객체에서 선택적 키로 추론되지 않습니다.
|
|
150
|
+
> ```typescript
|
|
151
|
+
> const mySchema = z.object({ a: z.any(), b: z.unknown() })
|
|
152
|
+
> // v3: { a?: any; b?: unknown }
|
|
153
|
+
> // v4: { a: any; b: unknown }
|
|
154
|
+
> ```
|
|
155
|
+
|
|
156
|
+
## Optional & Nullable
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
z.string().optional() // string | undefined
|
|
160
|
+
z.string().nullable() // string | null
|
|
161
|
+
z.string().nullish() // string | null | undefined
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Default 값
|
|
165
|
+
|
|
166
|
+
```typescript
|
|
167
|
+
const Schema = z.object({
|
|
168
|
+
name: z.string().default('Anonymous'),
|
|
169
|
+
role: z.enum(['admin', 'user']).default('user'),
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
Schema.parse({}) // { name: 'Anonymous', role: 'user' }
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## 타입 추론
|
|
176
|
+
|
|
177
|
+
```typescript
|
|
178
|
+
const stringSchema = z.string()
|
|
179
|
+
type StringType = z.infer<typeof stringSchema> // string
|
|
180
|
+
|
|
181
|
+
const numberSchema = z.number().optional()
|
|
182
|
+
type NumberType = z.infer<typeof numberSchema> // number | undefined
|
|
183
|
+
|
|
184
|
+
const dateSchema = z.date().nullable()
|
|
185
|
+
type DateType = z.infer<typeof dateSchema> // Date | null
|
|
186
|
+
```
|