@kood/claude-code 0.3.5 → 0.3.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 (35) hide show
  1. package/dist/index.js +1 -1
  2. package/package.json +1 -1
  3. package/templates/nextjs/CLAUDE.md +228 -0
  4. package/templates/nextjs/docs/design.md +558 -0
  5. package/templates/nextjs/docs/guides/conventions.md +343 -0
  6. package/templates/nextjs/docs/guides/getting-started.md +367 -0
  7. package/templates/nextjs/docs/guides/routes.md +342 -0
  8. package/templates/nextjs/docs/library/better-auth/index.md +541 -0
  9. package/templates/nextjs/docs/library/nextjs/app-router.md +269 -0
  10. package/templates/nextjs/docs/library/nextjs/caching.md +351 -0
  11. package/templates/nextjs/docs/library/nextjs/index.md +291 -0
  12. package/templates/nextjs/docs/library/nextjs/middleware.md +391 -0
  13. package/templates/nextjs/docs/library/nextjs/route-handlers.md +382 -0
  14. package/templates/nextjs/docs/library/nextjs/server-actions.md +366 -0
  15. package/templates/nextjs/docs/library/prisma/cloudflare-d1.md +76 -0
  16. package/templates/nextjs/docs/library/prisma/config.md +77 -0
  17. package/templates/nextjs/docs/library/prisma/crud.md +90 -0
  18. package/templates/nextjs/docs/library/prisma/index.md +73 -0
  19. package/templates/nextjs/docs/library/prisma/relations.md +69 -0
  20. package/templates/nextjs/docs/library/prisma/schema.md +98 -0
  21. package/templates/nextjs/docs/library/prisma/setup.md +49 -0
  22. package/templates/nextjs/docs/library/prisma/transactions.md +50 -0
  23. package/templates/nextjs/docs/library/tanstack-query/index.md +66 -0
  24. package/templates/nextjs/docs/library/tanstack-query/invalidation.md +54 -0
  25. package/templates/nextjs/docs/library/tanstack-query/optimistic-updates.md +77 -0
  26. package/templates/nextjs/docs/library/tanstack-query/use-mutation.md +63 -0
  27. package/templates/nextjs/docs/library/tanstack-query/use-query.md +70 -0
  28. package/templates/nextjs/docs/library/zod/complex-types.md +61 -0
  29. package/templates/nextjs/docs/library/zod/index.md +56 -0
  30. package/templates/nextjs/docs/library/zod/transforms.md +51 -0
  31. package/templates/nextjs/docs/library/zod/validation.md +70 -0
  32. package/templates/tanstack-start/CLAUDE.md +7 -3
  33. package/templates/tanstack-start/docs/architecture.md +38 -7
  34. package/templates/tanstack-start/docs/guides/hooks.md +28 -0
  35. package/templates/tanstack-start/docs/guides/routes.md +29 -10
@@ -0,0 +1,366 @@
1
+ # Server Actions
2
+
3
+ > 타입 안전한 서버 함수 (React 19 Server Actions)
4
+
5
+ ---
6
+
7
+ ## 기본 사용법
8
+
9
+ ### 파일 상단 선언
10
+
11
+ ```typescript
12
+ // app/actions.ts
13
+ "use server"
14
+
15
+ import { z } from "zod"
16
+ import { revalidatePath } from "next/cache"
17
+
18
+ export async function createPost(formData: FormData) {
19
+ const title = formData.get("title") as string
20
+ const content = formData.get("content") as string
21
+
22
+ const post = await prisma.post.create({
23
+ data: { title, content },
24
+ })
25
+
26
+ revalidatePath("/posts")
27
+ return post
28
+ }
29
+ ```
30
+
31
+ ### 인라인 선언
32
+
33
+ ```typescript
34
+ // app/posts/page.tsx
35
+ export default function PostsPage() {
36
+ async function createPost(formData: FormData) {
37
+ "use server"
38
+
39
+ const title = formData.get("title") as string
40
+ await prisma.post.create({ data: { title } })
41
+ }
42
+
43
+ return <form action={createPost}>...</form>
44
+ }
45
+ ```
46
+
47
+ ---
48
+
49
+ ## Zod 검증
50
+
51
+ ```typescript
52
+ "use server"
53
+
54
+ import { z } from "zod"
55
+
56
+ const createPostSchema = z.object({
57
+ title: z.string().min(1).max(100),
58
+ content: z.string().min(1),
59
+ published: z.boolean().default(false),
60
+ })
61
+
62
+ export async function createPost(formData: FormData) {
63
+ const parsed = createPostSchema.parse({
64
+ title: formData.get("title"),
65
+ content: formData.get("content"),
66
+ published: formData.get("published") === "on",
67
+ })
68
+
69
+ const post = await prisma.post.create({ data: parsed })
70
+ revalidatePath("/posts")
71
+ return post
72
+ }
73
+ ```
74
+
75
+ ---
76
+
77
+ ## 인증
78
+
79
+ ```typescript
80
+ "use server"
81
+
82
+ import { auth } from "@/lib/auth"
83
+ import { redirect } from "next/navigation"
84
+
85
+ export async function deletePost(id: string) {
86
+ const session = await auth()
87
+
88
+ if (!session?.user) {
89
+ redirect("/login")
90
+ }
91
+
92
+ await prisma.post.delete({
93
+ where: {
94
+ id,
95
+ userId: session.user.id, // 본인 게시물만 삭제
96
+ },
97
+ })
98
+
99
+ revalidatePath("/posts")
100
+ }
101
+ ```
102
+
103
+ ---
104
+
105
+ ## 에러 처리
106
+
107
+ ```typescript
108
+ "use server"
109
+
110
+ export async function updatePost(id: string, data: PostInput) {
111
+ try {
112
+ const post = await prisma.post.update({ where: { id }, data })
113
+ revalidatePath(`/posts/${id}`)
114
+ return { success: true, post }
115
+ } catch (error) {
116
+ if (error instanceof z.ZodError) {
117
+ return { success: false, errors: error.errors }
118
+ }
119
+ return { success: false, message: "업데이트 실패" }
120
+ }
121
+ }
122
+ ```
123
+
124
+ ---
125
+
126
+ ## 클라이언트에서 사용
127
+
128
+ ### Form Action
129
+
130
+ ```typescript
131
+ "use client"
132
+
133
+ import { createPost } from "@/actions/posts"
134
+
135
+ export function CreatePostForm() {
136
+ return (
137
+ <form action={createPost}>
138
+ <input name="title" required />
139
+ <textarea name="content" required />
140
+ <button type="submit">등록</button>
141
+ </form>
142
+ )
143
+ }
144
+ ```
145
+
146
+ ### TanStack Query
147
+
148
+ ```typescript
149
+ "use client"
150
+
151
+ import { useMutation, useQueryClient } from "@tanstack/react-query"
152
+ import { createPost } from "@/actions/posts"
153
+
154
+ export function CreatePostForm() {
155
+ const queryClient = useQueryClient()
156
+
157
+ const mutation = useMutation({
158
+ mutationFn: async (formData: FormData) => {
159
+ return createPost(formData)
160
+ },
161
+ onSuccess: () => {
162
+ queryClient.invalidateQueries({ queryKey: ["posts"] })
163
+ },
164
+ })
165
+
166
+ return (
167
+ <form
168
+ onSubmit={(e) => {
169
+ e.preventDefault()
170
+ const formData = new FormData(e.currentTarget)
171
+ mutation.mutate(formData)
172
+ }}
173
+ >
174
+ <input name="title" required />
175
+ <button type="submit" disabled={mutation.isPending}>
176
+ {mutation.isPending ? "등록 중..." : "등록"}
177
+ </button>
178
+ </form>
179
+ )
180
+ }
181
+ ```
182
+
183
+ ---
184
+
185
+ ## useFormState (React 19)
186
+
187
+ ```typescript
188
+ "use server"
189
+
190
+ export async function createPost(prevState: any, formData: FormData) {
191
+ const title = formData.get("title") as string
192
+
193
+ if (!title) {
194
+ return { error: "제목을 입력하세요" }
195
+ }
196
+
197
+ const post = await prisma.post.create({ data: { title } })
198
+ return { success: true, post }
199
+ }
200
+ ```
201
+
202
+ ```typescript
203
+ "use client"
204
+
205
+ import { useFormState } from "react-dom"
206
+ import { createPost } from "@/actions/posts"
207
+
208
+ export function CreatePostForm() {
209
+ const [state, formAction] = useFormState(createPost, null)
210
+
211
+ return (
212
+ <form action={formAction}>
213
+ <input name="title" />
214
+ {state?.error && <p>{state.error}</p>}
215
+ <button type="submit">등록</button>
216
+ </form>
217
+ )
218
+ }
219
+ ```
220
+
221
+ ---
222
+
223
+ ## 캐시 무효화
224
+
225
+ ### revalidatePath
226
+
227
+ ```typescript
228
+ "use server"
229
+
230
+ import { revalidatePath } from "next/cache"
231
+
232
+ export async function createPost(data: PostInput) {
233
+ const post = await prisma.post.create({ data })
234
+
235
+ // 특정 경로 캐시 무효화
236
+ revalidatePath("/posts")
237
+ revalidatePath(`/posts/${post.id}`)
238
+
239
+ // 레이아웃 포함 모든 캐시 무효화
240
+ revalidatePath("/posts", "layout")
241
+
242
+ return post
243
+ }
244
+ ```
245
+
246
+ ### revalidateTag
247
+
248
+ ```typescript
249
+ // 데이터 페칭 시 태그 설정
250
+ const posts = await fetch("https://api.example.com/posts", {
251
+ next: { tags: ["posts"] },
252
+ })
253
+
254
+ // Server Action에서 태그 무효화
255
+ "use server"
256
+
257
+ import { revalidateTag } from "next/cache"
258
+
259
+ export async function createPost(data: PostInput) {
260
+ const post = await prisma.post.create({ data })
261
+ revalidateTag("posts") // "posts" 태그 캐시 무효화
262
+ return post
263
+ }
264
+ ```
265
+
266
+ ---
267
+
268
+ ## redirect
269
+
270
+ ```typescript
271
+ "use server"
272
+
273
+ import { redirect } from "next/navigation"
274
+
275
+ export async function createPost(formData: FormData) {
276
+ const post = await prisma.post.create({
277
+ data: {
278
+ title: formData.get("title") as string,
279
+ },
280
+ })
281
+
282
+ redirect(`/posts/${post.id}`) // 페이지 이동
283
+ }
284
+ ```
285
+
286
+ ---
287
+
288
+ ## 파일 업로드
289
+
290
+ ```typescript
291
+ "use server"
292
+
293
+ import { writeFile } from "fs/promises"
294
+ import { join } from "path"
295
+
296
+ export async function uploadFile(formData: FormData) {
297
+ const file = formData.get("file") as File
298
+
299
+ if (!file) {
300
+ throw new Error("파일이 없습니다")
301
+ }
302
+
303
+ const bytes = await file.arrayBuffer()
304
+ const buffer = Buffer.from(bytes)
305
+
306
+ const path = join(process.cwd(), "public", "uploads", file.name)
307
+ await writeFile(path, buffer)
308
+
309
+ return { url: `/uploads/${file.name}` }
310
+ }
311
+ ```
312
+
313
+ ---
314
+
315
+ ## 베스트 프랙티스
316
+
317
+ ### ✅ DO
318
+
319
+ ```typescript
320
+ "use server"
321
+
322
+ // 1. Zod 검증
323
+ const schema = z.object({ title: z.string().min(1) })
324
+
325
+ // 2. 인증 체크
326
+ const session = await auth()
327
+ if (!session) throw new Error("Unauthorized")
328
+
329
+ // 3. try-catch
330
+ try {
331
+ const post = await prisma.post.create({ data })
332
+ revalidatePath("/posts")
333
+ return { success: true, post }
334
+ } catch (error) {
335
+ return { success: false, message: "생성 실패" }
336
+ }
337
+ ```
338
+
339
+ ### ❌ DON'T
340
+
341
+ ```typescript
342
+ // 1. 클라이언트 컴포넌트에서 Server Action 정의
343
+ "use client"
344
+
345
+ async function createPost() {
346
+ "use server" // ❌ 에러
347
+ }
348
+
349
+ // 2. 검증 없이 사용
350
+ export async function createPost(formData: FormData) {
351
+ const title = formData.get("title") // ❌ 검증 누락
352
+ await prisma.post.create({ data: { title } })
353
+ }
354
+
355
+ // 3. try-catch 없이 사용
356
+ export async function createPost(data: PostInput) {
357
+ const post = await prisma.post.create({ data }) // ❌ 에러 처리 누락
358
+ return post
359
+ }
360
+ ```
361
+
362
+ ---
363
+
364
+ ## 참조
365
+
366
+ - [Next.js Server Actions](https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations)
@@ -0,0 +1,76 @@
1
+ # Prisma - Cloudflare D1
2
+
3
+ SQLite 기반 서버리스 DB. 일반 Prisma 마이그레이션과 다른 워크플로우.
4
+
5
+ ⚠️ 트랜잭션 미지원 | prisma migrate 불가 - wrangler 사용 | Preview 상태
6
+
7
+ ## 설정
8
+
9
+ ```prisma
10
+ // schema.prisma
11
+ generator client {
12
+ provider = "prisma-client"
13
+ output = "../src/generated/prisma"
14
+ runtime = "cloudflare" // 필수
15
+ }
16
+
17
+ datasource db {
18
+ provider = "sqlite"
19
+ }
20
+ ```
21
+
22
+ ```jsonc
23
+ // wrangler.jsonc
24
+ {
25
+ "d1_databases": [{ "binding": "DB", "database_name": "my-db", "database_id": "ID" }]
26
+ }
27
+ ```
28
+
29
+ ## 사용법
30
+
31
+ ```typescript
32
+ import { PrismaClient } from './generated/prisma'
33
+ import { PrismaD1 } from '@prisma/adapter-d1'
34
+
35
+ export interface Env { DB: D1Database }
36
+
37
+ export default {
38
+ async fetch(request: Request, env: Env): Promise<Response> {
39
+ const adapter = new PrismaD1(env.DB)
40
+ const prisma = new PrismaClient({ adapter })
41
+ const users = await prisma.user.findMany()
42
+ return new Response(JSON.stringify(users))
43
+ },
44
+ }
45
+ ```
46
+
47
+ ## 마이그레이션 워크플로우
48
+
49
+ ```bash
50
+ # 1. D1 생성
51
+ npx wrangler d1 create my-database
52
+
53
+ # 2. 마이그레이션 생성
54
+ npx wrangler d1 migrations create my-database init
55
+
56
+ # 3. SQL 생성 (초기)
57
+ npx prisma migrate diff --from-empty --to-schema-datamodel prisma/schema.prisma --script --output prisma/migrations/0001.sql
58
+
59
+ # 4. SQL 생성 (후속)
60
+ npx prisma migrate diff --from-local-d1 --to-schema-datamodel prisma/schema.prisma --script
61
+
62
+ # 5. 적용
63
+ npx wrangler d1 migrations apply my-database --local # 로컬
64
+ npx wrangler d1 migrations apply my-database --remote # 프로덕션
65
+
66
+ # 6. Client 생성
67
+ npx prisma generate
68
+ ```
69
+
70
+ ## 제한사항
71
+
72
+ | 항목 | 일반 SQLite | D1 |
73
+ |------|-------------|-----|
74
+ | 마이그레이션 | prisma migrate | wrangler d1 |
75
+ | 트랜잭션 | ✅ | ❌ |
76
+ | 접속 | 직접 | HTTP 어댑터 |
@@ -0,0 +1,77 @@
1
+ # Prisma - Config 파일
2
+
3
+ Prisma v7 `prisma.config.ts` 설정.
4
+
5
+ ## Multi-File 스키마 (필수)
6
+
7
+ ```typescript
8
+ // prisma.config.ts
9
+ import 'dotenv/config'
10
+ import path from 'node:path'
11
+ import { defineConfig, env } from 'prisma/config'
12
+
13
+ export default defineConfig({
14
+ schema: path.join('prisma', 'schema'), // 폴더 경로!
15
+ migrations: {
16
+ path: 'prisma/migrations',
17
+ seed: 'tsx prisma/seed.ts',
18
+ },
19
+ datasource: {
20
+ url: env('DATABASE_URL'),
21
+ },
22
+ })
23
+ ```
24
+
25
+ ## 폴더 구조
26
+
27
+ ```
28
+ 프로젝트/
29
+ ├── prisma.config.ts
30
+ ├── prisma/
31
+ │ ├── schema/
32
+ │ │ ├── +base.prisma # datasource, generator
33
+ │ │ ├── +enum.prisma # enum 정의
34
+ │ │ └── user.prisma # 모델
35
+ │ └── migrations/
36
+ ```
37
+
38
+ ## +base.prisma
39
+
40
+ ```prisma
41
+ datasource db {
42
+ provider = "postgresql"
43
+ url = env("DATABASE_URL")
44
+ }
45
+
46
+ generator client {
47
+ provider = "prisma-client"
48
+ output = "../../generated/prisma"
49
+ }
50
+ ```
51
+
52
+ ## 설정 옵션
53
+
54
+ | 옵션 | 설명 |
55
+ |------|------|
56
+ | `schema` | 스키마 폴더 경로 |
57
+ | `datasource.url` | DB URL (필수) |
58
+ | `datasource.shadowDatabaseUrl` | Shadow DB URL |
59
+ | `migrations.path` | 마이그레이션 폴더 |
60
+ | `migrations.seed` | 시드 명령어 |
61
+
62
+ ## 시드 파일
63
+
64
+ ```typescript
65
+ // prisma/seed.ts
66
+ import { PrismaClient } from '../generated/prisma'
67
+
68
+ const prisma = new PrismaClient()
69
+
70
+ async function main() {
71
+ await prisma.user.create({
72
+ data: { email: 'admin@example.com', name: '관리자', role: 'ADMIN' },
73
+ })
74
+ }
75
+
76
+ main().catch(console.error).finally(() => prisma.$disconnect())
77
+ ```
@@ -0,0 +1,90 @@
1
+ # Prisma - CRUD 작업
2
+
3
+ ## Create
4
+
5
+ ```typescript
6
+ // 단일
7
+ const user = await prisma.user.create({
8
+ data: { email: 'alice@prisma.io', name: 'Alice' },
9
+ })
10
+
11
+ // 관계 포함
12
+ const user = await prisma.user.create({
13
+ data: {
14
+ email: 'bob@prisma.io',
15
+ posts: { create: [{ title: 'Hello' }, { title: 'World' }] },
16
+ },
17
+ include: { posts: true },
18
+ })
19
+
20
+ // connectOrCreate
21
+ posts: { create: [{
22
+ title: 'Post',
23
+ categories: { connectOrCreate: [{ create: { name: 'Tech' }, where: { name: 'Tech' } }] },
24
+ }]}
25
+ ```
26
+
27
+ ## Read
28
+
29
+ ```typescript
30
+ // 단일
31
+ const user = await prisma.user.findUnique({ where: { email } })
32
+
33
+ // 다중
34
+ const users = await prisma.user.findMany({ where: { name: 'Alice' } })
35
+
36
+ // 관계 포함
37
+ const users = await prisma.user.findMany({ where: { role: 'ADMIN' }, include: { posts: true } })
38
+
39
+ // 필드 선택
40
+ const user = await prisma.user.findUnique({
41
+ where: { email },
42
+ select: { email: true, posts: { select: { title: true } } },
43
+ })
44
+
45
+ // 관계로 필터
46
+ const users = await prisma.user.findMany({
47
+ where: { posts: { some: { published: false } } },
48
+ })
49
+ ```
50
+
51
+ ## Update
52
+
53
+ ```typescript
54
+ // 단일
55
+ const user = await prisma.user.update({ where: { id }, data: { name: 'Updated' } })
56
+
57
+ // 다중
58
+ await prisma.user.updateMany({ where: { role: 'USER' }, data: { role: 'ADMIN' } })
59
+
60
+ // Upsert
61
+ const user = await prisma.user.upsert({
62
+ where: { email },
63
+ update: { name: 'Updated' },
64
+ create: { email, name: 'New' },
65
+ })
66
+ ```
67
+
68
+ ## Delete
69
+
70
+ ```typescript
71
+ await prisma.user.delete({ where: { id } })
72
+ await prisma.user.deleteMany({}) // 전체
73
+ await prisma.post.deleteMany({ where: { published: false } }) // 조건부
74
+ ```
75
+
76
+ ## 필터 연산자
77
+
78
+ ```typescript
79
+ // 문자열
80
+ { contains: 'prisma', startsWith: 'A', endsWith: 'io' }
81
+
82
+ // 숫자
83
+ { gt: 18, gte: 18, lt: 65, lte: 65 }
84
+
85
+ // 배열
86
+ { in: [1, 2, 3], notIn: [4, 5] }
87
+
88
+ // 논리
89
+ { OR: [...], AND: [...], NOT: {...} }
90
+ ```
@@ -0,0 +1,73 @@
1
+ # Prisma
2
+
3
+ > **Version**: 7.x | Node.js/TypeScript ORM
4
+
5
+ @setup.md
6
+ @config.md
7
+ @schema.md
8
+ @crud.md
9
+ @relations.md
10
+ @transactions.md
11
+ @cloudflare-d1.md
12
+
13
+ ---
14
+
15
+ ## Quick Reference
16
+
17
+ ```typescript
18
+ import { PrismaClient } from './generated/prisma' // v7 경로!
19
+ export const prisma = new PrismaClient()
20
+
21
+ // CRUD
22
+ const users = await prisma.user.findMany()
23
+ const user = await prisma.user.create({ data: { email, name } })
24
+ const updated = await prisma.user.update({ where: { id }, data: { name } })
25
+ const deleted = await prisma.user.delete({ where: { id } })
26
+
27
+ // 관계 포함
28
+ const userWithPosts = await prisma.user.findUnique({
29
+ where: { id },
30
+ include: { posts: true },
31
+ })
32
+ ```
33
+
34
+ ### v7 schema.prisma (⚠️ 중요)
35
+
36
+ ```prisma
37
+ generator client {
38
+ provider = "prisma-client" // v7! (prisma-client-js 아님)
39
+ output = "../generated/prisma" // output 필수!
40
+ }
41
+ ```
42
+
43
+ ### ⛔ Claude Code 금지
44
+
45
+ | 금지 사항 |
46
+ |----------|
47
+ | prisma db push 자동 실행 |
48
+ | prisma migrate 자동 실행 |
49
+ | prisma generate 자동 실행 |
50
+ | schema.prisma 임의 변경 |
51
+
52
+ ---
53
+
54
+ ## Prisma Client 설정
55
+
56
+ ```typescript
57
+ // lib/prisma.ts
58
+ import { PrismaClient } from './generated/prisma'
59
+
60
+ const globalForPrisma = globalThis as unknown as { prisma: PrismaClient | undefined }
61
+ export const prisma = globalForPrisma.prisma ?? new PrismaClient({ log: ['query'] })
62
+ if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma
63
+ ```
64
+
65
+ ## 마이그레이션 명령어
66
+
67
+ ```bash
68
+ npx prisma migrate dev --name init # 개발 마이그레이션
69
+ npx prisma migrate deploy # 프로덕션 마이그레이션
70
+ npx prisma db push # 스키마 동기화 (개발용)
71
+ npx prisma generate # Client 생성
72
+ npx prisma studio # GUI
73
+ ```