@kood/claude-code 0.3.6 → 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 (34) 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/guides/hooks.md +28 -0
  34. package/templates/tanstack-start/docs/guides/routes.md +29 -10
@@ -0,0 +1,382 @@
1
+ # Route Handlers
2
+
3
+ > REST API 엔드포인트 (`app/api/`)
4
+
5
+ ---
6
+
7
+ ## 기본 사용법
8
+
9
+ ```typescript
10
+ // app/api/posts/route.ts
11
+ import { NextRequest, NextResponse } from "next/server"
12
+
13
+ export async function GET(request: NextRequest) {
14
+ const posts = await prisma.post.findMany()
15
+ return NextResponse.json(posts)
16
+ }
17
+
18
+ export async function POST(request: NextRequest) {
19
+ const body = await request.json()
20
+ const post = await prisma.post.create({ data: body })
21
+ return NextResponse.json(post, { status: 201 })
22
+ }
23
+ ```
24
+
25
+ ---
26
+
27
+ ## HTTP 메서드
28
+
29
+ ```typescript
30
+ // app/api/posts/route.ts
31
+ export async function GET(request: NextRequest) {}
32
+ export async function POST(request: NextRequest) {}
33
+ export async function PUT(request: NextRequest) {}
34
+ export async function PATCH(request: NextRequest) {}
35
+ export async function DELETE(request: NextRequest) {}
36
+ export async function HEAD(request: NextRequest) {}
37
+ export async function OPTIONS(request: NextRequest) {}
38
+ ```
39
+
40
+ ---
41
+
42
+ ## 동적 라우트
43
+
44
+ ```typescript
45
+ // app/api/posts/[id]/route.ts
46
+ export async function GET(
47
+ request: NextRequest,
48
+ { params }: { params: { id: string } }
49
+ ) {
50
+ const post = await prisma.post.findUnique({ where: { id: params.id } })
51
+
52
+ if (!post) {
53
+ return NextResponse.json({ error: "Not found" }, { status: 404 })
54
+ }
55
+
56
+ return NextResponse.json(post)
57
+ }
58
+
59
+ export async function DELETE(
60
+ request: NextRequest,
61
+ { params }: { params: { id: string } }
62
+ ) {
63
+ await prisma.post.delete({ where: { id: params.id } })
64
+ return NextResponse.json({ success: true })
65
+ }
66
+ ```
67
+
68
+ ---
69
+
70
+ ## Request 처리
71
+
72
+ ### Query Parameters
73
+
74
+ ```typescript
75
+ // GET /api/posts?page=1&limit=10
76
+ export async function GET(request: NextRequest) {
77
+ const searchParams = request.nextUrl.searchParams
78
+ const page = Number(searchParams.get("page") || 1)
79
+ const limit = Number(searchParams.get("limit") || 10)
80
+
81
+ const posts = await prisma.post.findMany({
82
+ skip: (page - 1) * limit,
83
+ take: limit,
84
+ })
85
+
86
+ return NextResponse.json(posts)
87
+ }
88
+ ```
89
+
90
+ ### JSON Body
91
+
92
+ ```typescript
93
+ export async function POST(request: NextRequest) {
94
+ const body = await request.json()
95
+
96
+ const post = await prisma.post.create({
97
+ data: {
98
+ title: body.title,
99
+ content: body.content,
100
+ },
101
+ })
102
+
103
+ return NextResponse.json(post, { status: 201 })
104
+ }
105
+ ```
106
+
107
+ ### FormData
108
+
109
+ ```typescript
110
+ export async function POST(request: NextRequest) {
111
+ const formData = await request.formData()
112
+ const title = formData.get("title") as string
113
+ const file = formData.get("file") as File
114
+
115
+ // 파일 처리...
116
+
117
+ return NextResponse.json({ success: true })
118
+ }
119
+ ```
120
+
121
+ ### Headers
122
+
123
+ ```typescript
124
+ export async function GET(request: NextRequest) {
125
+ const token = request.headers.get("authorization")
126
+ const userAgent = request.headers.get("user-agent")
127
+
128
+ // 헤더 사용...
129
+
130
+ return NextResponse.json({ data: "..." })
131
+ }
132
+ ```
133
+
134
+ ---
135
+
136
+ ## Response 처리
137
+
138
+ ### JSON Response
139
+
140
+ ```typescript
141
+ export async function GET() {
142
+ return NextResponse.json({ message: "Hello" }, { status: 200 })
143
+ }
144
+ ```
145
+
146
+ ### Response Headers
147
+
148
+ ```typescript
149
+ export async function GET() {
150
+ return NextResponse.json(
151
+ { data: "..." },
152
+ {
153
+ status: 200,
154
+ headers: {
155
+ "Content-Type": "application/json",
156
+ "Cache-Control": "max-age=3600",
157
+ },
158
+ }
159
+ )
160
+ }
161
+ ```
162
+
163
+ ### Redirect
164
+
165
+ ```typescript
166
+ import { redirect } from "next/navigation"
167
+
168
+ export async function GET() {
169
+ redirect("https://example.com")
170
+ }
171
+ ```
172
+
173
+ ### Cookies
174
+
175
+ ```typescript
176
+ import { cookies } from "next/headers"
177
+
178
+ export async function GET() {
179
+ const cookieStore = cookies()
180
+
181
+ // 쿠키 읽기
182
+ const token = cookieStore.get("token")
183
+
184
+ // 쿠키 설정
185
+ cookieStore.set("token", "value", {
186
+ httpOnly: true,
187
+ secure: true,
188
+ maxAge: 60 * 60 * 24 * 7, // 7일
189
+ })
190
+
191
+ return NextResponse.json({ success: true })
192
+ }
193
+ ```
194
+
195
+ ---
196
+
197
+ ## Zod 검증
198
+
199
+ ```typescript
200
+ import { z } from "zod"
201
+ import { NextRequest, NextResponse } from "next/server"
202
+
203
+ const createPostSchema = z.object({
204
+ title: z.string().min(1).max(100),
205
+ content: z.string().min(1),
206
+ })
207
+
208
+ export async function POST(request: NextRequest) {
209
+ try {
210
+ const body = await request.json()
211
+ const parsed = createPostSchema.parse(body)
212
+
213
+ const post = await prisma.post.create({ data: parsed })
214
+ return NextResponse.json(post, { status: 201 })
215
+ } catch (error) {
216
+ if (error instanceof z.ZodError) {
217
+ return NextResponse.json(
218
+ { errors: error.errors },
219
+ { status: 400 }
220
+ )
221
+ }
222
+ return NextResponse.json(
223
+ { error: "Internal Server Error" },
224
+ { status: 500 }
225
+ )
226
+ }
227
+ }
228
+ ```
229
+
230
+ ---
231
+
232
+ ## 인증
233
+
234
+ ```typescript
235
+ import { auth } from "@/lib/auth"
236
+
237
+ export async function DELETE(
238
+ request: NextRequest,
239
+ { params }: { params: { id: string } }
240
+ ) {
241
+ const session = await auth()
242
+
243
+ if (!session?.user) {
244
+ return NextResponse.json({ error: "Unauthorized" }, { status: 401 })
245
+ }
246
+
247
+ await prisma.post.delete({
248
+ where: {
249
+ id: params.id,
250
+ userId: session.user.id,
251
+ },
252
+ })
253
+
254
+ return NextResponse.json({ success: true })
255
+ }
256
+ ```
257
+
258
+ ---
259
+
260
+ ## CORS
261
+
262
+ ```typescript
263
+ export async function GET(request: NextRequest) {
264
+ const data = await fetchData()
265
+
266
+ return NextResponse.json(data, {
267
+ headers: {
268
+ "Access-Control-Allow-Origin": "*",
269
+ "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
270
+ "Access-Control-Allow-Headers": "Content-Type, Authorization",
271
+ },
272
+ })
273
+ }
274
+
275
+ export async function OPTIONS() {
276
+ return new NextResponse(null, {
277
+ status: 204,
278
+ headers: {
279
+ "Access-Control-Allow-Origin": "*",
280
+ "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
281
+ "Access-Control-Allow-Headers": "Content-Type, Authorization",
282
+ },
283
+ })
284
+ }
285
+ ```
286
+
287
+ ---
288
+
289
+ ## 스트리밍
290
+
291
+ ```typescript
292
+ export async function GET() {
293
+ const encoder = new TextEncoder()
294
+
295
+ const stream = new ReadableStream({
296
+ async start(controller) {
297
+ for (let i = 0; i < 10; i++) {
298
+ await new Promise(resolve => setTimeout(resolve, 1000))
299
+ controller.enqueue(encoder.encode(`data: ${i}\n\n`))
300
+ }
301
+ controller.close()
302
+ },
303
+ })
304
+
305
+ return new Response(stream, {
306
+ headers: {
307
+ "Content-Type": "text/event-stream",
308
+ "Cache-Control": "no-cache",
309
+ "Connection": "keep-alive",
310
+ },
311
+ })
312
+ }
313
+ ```
314
+
315
+ ---
316
+
317
+ ## 에러 처리
318
+
319
+ ```typescript
320
+ export async function GET(
321
+ request: NextRequest,
322
+ { params }: { params: { id: string } }
323
+ ) {
324
+ try {
325
+ const post = await prisma.post.findUnique({ where: { id: params.id } })
326
+
327
+ if (!post) {
328
+ return NextResponse.json({ error: "Not found" }, { status: 404 })
329
+ }
330
+
331
+ return NextResponse.json(post)
332
+ } catch (error) {
333
+ console.error("Error fetching post:", error)
334
+ return NextResponse.json(
335
+ { error: "Internal Server Error" },
336
+ { status: 500 }
337
+ )
338
+ }
339
+ }
340
+ ```
341
+
342
+ ---
343
+
344
+ ## 캐싱
345
+
346
+ ```typescript
347
+ // 정적 응답 (빌드 시 생성)
348
+ export const dynamic = "force-static"
349
+
350
+ // 동적 응답 (매 요청마다)
351
+ export const dynamic = "force-dynamic"
352
+
353
+ // Revalidate (n초마다 재생성)
354
+ export const revalidate = 60 // 60초
355
+
356
+ export async function GET() {
357
+ const posts = await prisma.post.findMany()
358
+ return NextResponse.json(posts)
359
+ }
360
+ ```
361
+
362
+ ---
363
+
364
+ ## Server Actions vs Route Handlers
365
+
366
+ | 기준 | Server Actions | Route Handlers |
367
+ |------|----------------|----------------|
368
+ | **용도** | 폼 제출, 뮤테이션 | REST API, 외부 연동 |
369
+ | **타입** | ✅ 자동 타입 추론 | ❌ 수동 타입 정의 |
370
+ | **캐싱** | revalidatePath | export const revalidate |
371
+ | **인증** | await auth() | await auth() |
372
+ | **권장** | 내부 API | 외부 API, 웹훅 |
373
+
374
+ **권장 사항:**
375
+ - 내부 API → Server Actions
376
+ - 외부 API, 웹훅, 써드파티 연동 → Route Handlers
377
+
378
+ ---
379
+
380
+ ## 참조
381
+
382
+ - [Next.js Route Handlers](https://nextjs.org/docs/app/building-your-application/routing/route-handlers)