@kood/claude-code 0.5.4 → 0.5.6
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.js +6 -2
- package/package.json +1 -1
- package/templates/.claude/agents/document-writer.md +2 -2
- package/templates/.claude/skills/docs-creator/SKILL.md +2 -2
- package/templates/.claude/skills/docs-refactor/SKILL.md +1 -1
- package/templates/.claude/skills/plan/SKILL.md +15 -15
- package/templates/.claude/skills/ralph/SKILL.md +425 -72
- package/templates/hono/CLAUDE.md +28 -28
- package/templates/hono/docs/architecture.md +24 -24
- package/templates/hono/docs/deployment/cloudflare.md +18 -18
- package/templates/hono/docs/deployment/docker.md +13 -13
- package/templates/hono/docs/deployment/index.md +19 -19
- package/templates/hono/docs/deployment/railway.md +32 -32
- package/templates/hono/docs/deployment/vercel.md +29 -29
- package/templates/hono/docs/guides/conventions.md +57 -57
- package/templates/hono/docs/guides/env-setup.md +47 -47
- package/templates/hono/docs/guides/getting-started.md +27 -27
- package/templates/hono/docs/library/hono/error-handling.md +11 -11
- package/templates/hono/docs/library/hono/index.md +4 -4
- package/templates/hono/docs/library/hono/middleware.md +18 -18
- package/templates/hono/docs/library/hono/rpc.md +7 -7
- package/templates/hono/docs/library/hono/validation.md +6 -6
- package/templates/hono/docs/library/prisma/cloudflare-d1.md +29 -29
- package/templates/hono/docs/library/prisma/config.md +16 -16
- package/templates/hono/docs/library/prisma/index.md +32 -32
- package/templates/hono/docs/library/t3-env/index.md +22 -22
- package/templates/hono/docs/library/zod/index.md +31 -31
- package/templates/nextjs/CLAUDE.md +54 -54
- package/templates/nextjs/docs/architecture.md +146 -146
- package/templates/nextjs/docs/design.md +183 -183
- package/templates/nextjs/docs/guides/conventions.md +86 -86
- package/templates/nextjs/docs/guides/getting-started.md +28 -28
- package/templates/nextjs/docs/guides/routes.md +32 -32
- package/templates/nextjs/docs/library/better-auth/index.md +70 -70
- package/templates/nextjs/docs/library/nextjs/app-router.md +43 -43
- package/templates/nextjs/docs/library/nextjs/caching.md +73 -73
- package/templates/nextjs/docs/library/nextjs/index.md +51 -51
- package/templates/nextjs/docs/library/nextjs/middleware.md +41 -41
- package/templates/nextjs/docs/library/nextjs/route-handlers.md +31 -31
- package/templates/nextjs/docs/library/nextjs/server-actions.md +34 -34
- package/templates/nextjs/docs/library/prisma/cloudflare-d1.md +20 -20
- package/templates/nextjs/docs/library/prisma/config.md +18 -18
- package/templates/nextjs/docs/library/prisma/crud.md +17 -17
- package/templates/nextjs/docs/library/prisma/index.md +18 -18
- package/templates/nextjs/docs/library/prisma/relations.md +16 -16
- package/templates/nextjs/docs/library/prisma/schema.md +23 -23
- package/templates/nextjs/docs/library/prisma/setup.md +6 -6
- package/templates/nextjs/docs/library/prisma/transactions.md +10 -10
- package/templates/nextjs/docs/library/tanstack-query/index.md +6 -6
- package/templates/nextjs/docs/library/tanstack-query/invalidation.md +20 -20
- package/templates/nextjs/docs/library/tanstack-query/optimistic-updates.md +4 -4
- package/templates/nextjs/docs/library/tanstack-query/use-mutation.md +15 -15
- package/templates/nextjs/docs/library/tanstack-query/use-query.md +22 -22
- package/templates/nextjs/docs/library/zod/complex-types.md +11 -11
- package/templates/nextjs/docs/library/zod/index.md +8 -8
- package/templates/nextjs/docs/library/zod/transforms.md +11 -11
- package/templates/nextjs/docs/library/zod/validation.md +9 -9
- package/templates/npx/CLAUDE.md +38 -38
- package/templates/npx/docs/library/commander/index.md +12 -12
- package/templates/npx/docs/library/fs-extra/index.md +9 -9
- package/templates/npx/docs/library/prompts/index.md +3 -3
- package/templates/npx/docs/references/patterns.md +12 -12
- package/templates/tanstack-start/CLAUDE.md +54 -54
- package/templates/tanstack-start/docs/architecture.md +128 -128
- package/templates/tanstack-start/docs/design.md +169 -169
- package/templates/tanstack-start/docs/guides/conventions.md +43 -43
- package/templates/tanstack-start/docs/guides/env-setup.md +35 -35
- package/templates/tanstack-start/docs/guides/getting-started.md +19 -19
- package/templates/tanstack-start/docs/guides/hooks.md +45 -45
- package/templates/tanstack-start/docs/guides/routes.md +54 -54
- package/templates/tanstack-start/docs/guides/services.md +45 -45
- package/templates/tanstack-start/docs/library/prisma/cloudflare-d1.md +19 -19
- package/templates/tanstack-start/docs/library/prisma/config.md +16 -16
- package/templates/tanstack-start/docs/library/prisma/crud.md +17 -17
- package/templates/tanstack-start/docs/library/prisma/relations.md +16 -16
- package/templates/tanstack-start/docs/library/prisma/schema.md +23 -23
- package/templates/tanstack-start/docs/library/prisma/setup.md +6 -6
- package/templates/tanstack-start/docs/library/prisma/transactions.md +10 -10
- package/templates/tanstack-start/docs/library/tanstack-query/invalidation.md +19 -19
- package/templates/tanstack-start/docs/library/tanstack-query/optimistic-updates.md +4 -4
- package/templates/tanstack-start/docs/library/tanstack-query/use-mutation.md +14 -14
- package/templates/tanstack-start/docs/library/tanstack-query/use-query.md +21 -21
- package/templates/tanstack-start/docs/library/tanstack-router/error-handling.md +9 -9
- package/templates/tanstack-start/docs/library/tanstack-router/hooks.md +11 -11
- package/templates/tanstack-start/docs/library/tanstack-router/navigation.md +17 -17
- package/templates/tanstack-start/docs/library/tanstack-router/route-context.md +5 -5
- package/templates/tanstack-start/docs/library/tanstack-router/search-params.md +10 -10
- package/templates/tanstack-start/docs/library/tanstack-start/auth-patterns.md +8 -8
- package/templates/tanstack-start/docs/library/tanstack-start/middleware.md +9 -9
- package/templates/tanstack-start/docs/library/tanstack-start/routing.md +6 -6
- package/templates/tanstack-start/docs/library/tanstack-start/server-functions.md +18 -18
- package/templates/tanstack-start/docs/library/tanstack-start/setup.md +4 -4
- package/templates/tanstack-start/docs/library/zod/complex-types.md +11 -11
- package/templates/tanstack-start/docs/library/zod/transforms.md +11 -11
- package/templates/tanstack-start/docs/library/zod/validation.md +9 -9
|
@@ -1,71 +1,71 @@
|
|
|
1
1
|
# Conventions
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> 코드 작성 규칙
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## 파일명
|
|
8
8
|
|
|
9
|
-
|
|
|
10
|
-
|
|
11
|
-
|
|
|
12
|
-
|
|
|
9
|
+
| 유형 | 규칙 | 예시 |
|
|
10
|
+
|------|------|------|
|
|
11
|
+
| 컴포넌트 | kebab-case | `user-profile.tsx` |
|
|
12
|
+
| 라우트 | Next.js 규칙 | `page.tsx`, `layout.tsx`, `[id]/page.tsx` |
|
|
13
13
|
| Server Actions | kebab-case | `create-post.ts`, `posts.ts` |
|
|
14
|
-
|
|
|
14
|
+
| 유틸리티 | kebab-case | `format-date.ts` |
|
|
15
15
|
|
|
16
16
|
---
|
|
17
17
|
|
|
18
18
|
## TypeScript
|
|
19
19
|
|
|
20
|
-
###
|
|
20
|
+
### 변수 선언
|
|
21
21
|
|
|
22
22
|
```typescript
|
|
23
|
-
// ✅
|
|
23
|
+
// ✅ const 우선
|
|
24
24
|
const user = { name: "Alice" }
|
|
25
25
|
const posts = await prisma.post.findMany()
|
|
26
26
|
|
|
27
|
-
// ❌
|
|
28
|
-
let count = 0 //
|
|
27
|
+
// ❌ let 최소화
|
|
28
|
+
let count = 0 // 변경 필요시만
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
-
###
|
|
31
|
+
### 함수 선언
|
|
32
32
|
|
|
33
33
|
```typescript
|
|
34
|
-
// ✅ const
|
|
34
|
+
// ✅ const 화살표 함수 + 명시적 return type
|
|
35
35
|
const getUser = async (id: string): Promise<User> => {
|
|
36
36
|
return prisma.user.findUnique({ where: { id } })
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
// ❌ function
|
|
39
|
+
// ❌ function 키워드 (export default 제외)
|
|
40
40
|
function getUser(id: string) {
|
|
41
41
|
return prisma.user.findUnique({ where: { id } })
|
|
42
42
|
}
|
|
43
43
|
```
|
|
44
44
|
|
|
45
|
-
###
|
|
45
|
+
### 타입 정의
|
|
46
46
|
|
|
47
47
|
```typescript
|
|
48
|
-
// ✅ interface (
|
|
48
|
+
// ✅ interface (객체)
|
|
49
49
|
interface User {
|
|
50
50
|
id: string
|
|
51
51
|
name: string
|
|
52
52
|
email: string
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
// ✅ type (
|
|
55
|
+
// ✅ type (유니온, 기타)
|
|
56
56
|
type Status = "active" | "inactive"
|
|
57
57
|
type UserOrNull = User | null
|
|
58
58
|
|
|
59
|
-
// ❌
|
|
59
|
+
// ❌ any 금지 → unknown 사용
|
|
60
60
|
const data: unknown = JSON.parse(jsonString)
|
|
61
61
|
```
|
|
62
62
|
|
|
63
63
|
---
|
|
64
64
|
|
|
65
|
-
## Import
|
|
65
|
+
## Import 순서
|
|
66
66
|
|
|
67
67
|
```typescript
|
|
68
|
-
// 1.
|
|
68
|
+
// 1. 외부 라이브러리
|
|
69
69
|
import { useState } from "react"
|
|
70
70
|
import { useQuery } from "@tanstack/react-query"
|
|
71
71
|
|
|
@@ -73,21 +73,21 @@ import { useQuery } from "@tanstack/react-query"
|
|
|
73
73
|
import { Button } from "@/components/ui/button"
|
|
74
74
|
import { prisma } from "@/database/prisma"
|
|
75
75
|
|
|
76
|
-
// 3.
|
|
76
|
+
// 3. 상대경로
|
|
77
77
|
import { UserProfile } from "./-components/user-profile"
|
|
78
78
|
|
|
79
|
-
// 4.
|
|
79
|
+
// 4. 타입 (분리)
|
|
80
80
|
import type { User } from "@/types"
|
|
81
81
|
```
|
|
82
82
|
|
|
83
83
|
---
|
|
84
84
|
|
|
85
|
-
##
|
|
85
|
+
## 컴포넌트
|
|
86
86
|
|
|
87
|
-
### Server Component (
|
|
87
|
+
### Server Component (기본)
|
|
88
88
|
|
|
89
89
|
```typescript
|
|
90
|
-
// ✅ async
|
|
90
|
+
// ✅ async 함수 + 직접 데이터 페칭
|
|
91
91
|
export default async function PostsPage() {
|
|
92
92
|
const posts = await prisma.post.findMany()
|
|
93
93
|
return <PostsList posts={posts} />
|
|
@@ -97,7 +97,7 @@ export default async function PostsPage() {
|
|
|
97
97
|
### Client Component
|
|
98
98
|
|
|
99
99
|
```typescript
|
|
100
|
-
// ✅ "use client" +
|
|
100
|
+
// ✅ "use client" + 상호작용
|
|
101
101
|
"use client"
|
|
102
102
|
|
|
103
103
|
import { useState } from "react"
|
|
@@ -112,10 +112,10 @@ export function Counter() {
|
|
|
112
112
|
|
|
113
113
|
## Server Actions
|
|
114
114
|
|
|
115
|
-
###
|
|
115
|
+
### 파일 상단
|
|
116
116
|
|
|
117
117
|
```typescript
|
|
118
|
-
// ✅ "use server" +
|
|
118
|
+
// ✅ "use server" + 여러 함수
|
|
119
119
|
"use server"
|
|
120
120
|
|
|
121
121
|
export async function createPost(formData: FormData) {
|
|
@@ -127,7 +127,7 @@ export async function deletePost(id: string) {
|
|
|
127
127
|
}
|
|
128
128
|
```
|
|
129
129
|
|
|
130
|
-
### Zod
|
|
130
|
+
### Zod 검증
|
|
131
131
|
|
|
132
132
|
```typescript
|
|
133
133
|
"use server"
|
|
@@ -151,61 +151,61 @@ export async function createPost(formData: FormData) {
|
|
|
151
151
|
|
|
152
152
|
---
|
|
153
153
|
|
|
154
|
-
##
|
|
154
|
+
## 주석
|
|
155
155
|
|
|
156
156
|
```typescript
|
|
157
|
-
// ✅
|
|
158
|
-
//
|
|
157
|
+
// ✅ 코드 묶음 단위로 한글 주석
|
|
158
|
+
// 사용자 인증 체크
|
|
159
159
|
const session = await auth.api.getSession({ headers: headers() })
|
|
160
160
|
if (!session?.user) redirect("/login")
|
|
161
161
|
|
|
162
|
-
//
|
|
162
|
+
// 게시글 조회
|
|
163
163
|
const posts = await prisma.post.findMany({
|
|
164
164
|
where: { userId: session.user.id },
|
|
165
165
|
orderBy: { createdAt: "desc" },
|
|
166
166
|
})
|
|
167
167
|
|
|
168
|
-
// ❌
|
|
169
|
-
const session = await auth.api.getSession({ headers: headers() }) //
|
|
170
|
-
if (!session?.user) redirect("/login") //
|
|
168
|
+
// ❌ 모든 줄마다 주석
|
|
169
|
+
const session = await auth.api.getSession({ headers: headers() }) // 세션 조회
|
|
170
|
+
if (!session?.user) redirect("/login") // 로그인 리다이렉트
|
|
171
171
|
```
|
|
172
172
|
|
|
173
173
|
---
|
|
174
174
|
|
|
175
175
|
## Prisma
|
|
176
176
|
|
|
177
|
-
### Multi-File
|
|
177
|
+
### Multi-File 구조
|
|
178
178
|
|
|
179
179
|
```
|
|
180
180
|
prisma/schema/
|
|
181
181
|
├── +base.prisma # datasource, generator
|
|
182
|
-
├── +enum.prisma #
|
|
183
|
-
└── user.prisma # User
|
|
182
|
+
├── +enum.prisma # 모든 enum
|
|
183
|
+
└── user.prisma # User 모델
|
|
184
184
|
```
|
|
185
185
|
|
|
186
|
-
###
|
|
186
|
+
### 한글 주석 필수
|
|
187
187
|
|
|
188
188
|
```prisma
|
|
189
|
-
///
|
|
189
|
+
/// 사용자
|
|
190
190
|
model User {
|
|
191
|
-
id String @id @default(cuid()) ///
|
|
192
|
-
email String @unique ///
|
|
193
|
-
name String? ///
|
|
194
|
-
role Role @default(USER) ///
|
|
195
|
-
createdAt DateTime @default(now()) ///
|
|
196
|
-
updatedAt DateTime @updatedAt ///
|
|
191
|
+
id String @id @default(cuid()) /// 고유 ID
|
|
192
|
+
email String @unique /// 이메일 (고유)
|
|
193
|
+
name String? /// 이름 (옵션)
|
|
194
|
+
role Role @default(USER) /// 역할
|
|
195
|
+
createdAt DateTime @default(now()) /// 생성일
|
|
196
|
+
updatedAt DateTime @updatedAt /// 수정일
|
|
197
197
|
}
|
|
198
198
|
|
|
199
|
-
///
|
|
199
|
+
/// 역할
|
|
200
200
|
enum Role {
|
|
201
|
-
USER ///
|
|
202
|
-
ADMIN ///
|
|
201
|
+
USER /// 일반 사용자
|
|
202
|
+
ADMIN /// 관리자
|
|
203
203
|
}
|
|
204
204
|
```
|
|
205
205
|
|
|
206
206
|
---
|
|
207
207
|
|
|
208
|
-
##
|
|
208
|
+
## 폴더 구조
|
|
209
209
|
|
|
210
210
|
```
|
|
211
211
|
app/
|
|
@@ -216,21 +216,21 @@ app/
|
|
|
216
216
|
│ └── page.tsx
|
|
217
217
|
├── dashboard/
|
|
218
218
|
│ ├── page.tsx
|
|
219
|
-
│ ├── -components/ #
|
|
219
|
+
│ ├── -components/ # 페이지 전용 (필수)
|
|
220
220
|
│ │ ├── stats.tsx
|
|
221
221
|
│ │ └── chart.tsx
|
|
222
222
|
│ └── settings/
|
|
223
223
|
│ └── page.tsx
|
|
224
|
-
└── _components/ #
|
|
224
|
+
└── _components/ # 공통 Client Components
|
|
225
225
|
```
|
|
226
226
|
|
|
227
|
-
|
|
228
|
-
- `-components/` -
|
|
229
|
-
- `_components/` -
|
|
227
|
+
**규칙:**
|
|
228
|
+
- `-components/` - 페이지 전용 (밖에서 import 불가)
|
|
229
|
+
- `_components/` - 공통 (라우트에 포함 안됨)
|
|
230
230
|
|
|
231
231
|
---
|
|
232
232
|
|
|
233
|
-
## Custom Hook
|
|
233
|
+
## Custom Hook 순서
|
|
234
234
|
|
|
235
235
|
```typescript
|
|
236
236
|
"use client"
|
|
@@ -267,19 +267,19 @@ export function useExample() {
|
|
|
267
267
|
|
|
268
268
|
---
|
|
269
269
|
|
|
270
|
-
##
|
|
270
|
+
## 베스트 프랙티스
|
|
271
271
|
|
|
272
272
|
### ✅ DO
|
|
273
273
|
|
|
274
274
|
```typescript
|
|
275
|
-
// 1.
|
|
275
|
+
// 1. 명시적 타입
|
|
276
276
|
const getUser = async (id: string): Promise<User> => { /* ... */ }
|
|
277
277
|
|
|
278
|
-
// 2. Zod
|
|
278
|
+
// 2. Zod 검증
|
|
279
279
|
const schema = z.object({ email: z.email() })
|
|
280
280
|
const parsed = schema.parse(data)
|
|
281
281
|
|
|
282
|
-
// 3.
|
|
282
|
+
// 3. 에러 처리
|
|
283
283
|
try {
|
|
284
284
|
await prisma.post.create({ data })
|
|
285
285
|
} catch (error) {
|
|
@@ -295,49 +295,49 @@ revalidatePath("/posts")
|
|
|
295
295
|
### ❌ DON'T
|
|
296
296
|
|
|
297
297
|
```typescript
|
|
298
|
-
// 1.
|
|
298
|
+
// 1. any 사용
|
|
299
299
|
const data: any = await fetchData() // ❌
|
|
300
300
|
|
|
301
|
-
// 2.
|
|
302
|
-
const email = formData.get("email") // ❌
|
|
301
|
+
// 2. 검증 누락
|
|
302
|
+
const email = formData.get("email") // ❌ Zod 검증 필요
|
|
303
303
|
await createUser({ email })
|
|
304
304
|
|
|
305
|
-
// 3.
|
|
306
|
-
await prisma.post.create({ data }) // ❌
|
|
305
|
+
// 3. 에러 무시
|
|
306
|
+
await prisma.post.create({ data }) // ❌ try-catch 필요
|
|
307
307
|
|
|
308
|
-
// 4.
|
|
309
|
-
await prisma.post.create({ data }) // ❌
|
|
308
|
+
// 4. 캐시 무효화 누락
|
|
309
|
+
await prisma.post.create({ data }) // ❌ revalidatePath 필요
|
|
310
310
|
```
|
|
311
311
|
|
|
312
312
|
---
|
|
313
313
|
|
|
314
|
-
## Git
|
|
314
|
+
## Git 커밋
|
|
315
315
|
|
|
316
316
|
```bash
|
|
317
|
-
# ✅
|
|
318
|
-
git commit -m "feat:
|
|
319
|
-
git commit -m "fix:
|
|
317
|
+
# ✅ 한 줄, prefix 사용
|
|
318
|
+
git commit -m "feat: 게시글 생성 기능 추가"
|
|
319
|
+
git commit -m "fix: 로그인 버그 수정"
|
|
320
320
|
|
|
321
|
-
# ❌
|
|
322
|
-
git commit -m "feat:
|
|
321
|
+
# ❌ 여러 줄, 이모지, AI 표시
|
|
322
|
+
git commit -m "feat: 게시글 생성 기능 추가
|
|
323
323
|
|
|
324
|
-
|
|
324
|
+
상세 설명...
|
|
325
325
|
|
|
326
326
|
Co-Authored-By: Claude Code <noreply@anthropic.com>" # ❌
|
|
327
327
|
```
|
|
328
328
|
|
|
329
|
-
**
|
|
330
|
-
- `feat` -
|
|
331
|
-
- `fix` -
|
|
332
|
-
- `refactor` -
|
|
333
|
-
- `style` -
|
|
334
|
-
- `docs` -
|
|
335
|
-
- `test` -
|
|
336
|
-
- `chore` -
|
|
329
|
+
**Prefix:**
|
|
330
|
+
- `feat` - 새 기능
|
|
331
|
+
- `fix` - 버그 수정
|
|
332
|
+
- `refactor` - 리팩토링
|
|
333
|
+
- `style` - 코드 스타일
|
|
334
|
+
- `docs` - 문서
|
|
335
|
+
- `test` - 테스트
|
|
336
|
+
- `chore` - 기타
|
|
337
337
|
|
|
338
338
|
---
|
|
339
339
|
|
|
340
|
-
##
|
|
340
|
+
## 참조
|
|
341
341
|
|
|
342
|
-
- [Next.js
|
|
343
|
-
- [TypeScript
|
|
342
|
+
- [Next.js 공식 문서](https://nextjs.org/docs)
|
|
343
|
+
- [TypeScript 공식 문서](https://www.typescriptlang.org/)
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# Getting Started
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> Next.js 15 프로젝트 시작하기
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## 프로젝트 생성
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
10
|
npx create-next-app@latest my-app \
|
|
@@ -20,7 +20,7 @@ npm run dev
|
|
|
20
20
|
|
|
21
21
|
---
|
|
22
22
|
|
|
23
|
-
##
|
|
23
|
+
## 필수 의존성
|
|
24
24
|
|
|
25
25
|
```bash
|
|
26
26
|
# Database
|
|
@@ -39,7 +39,7 @@ npm install @tanstack/react-query
|
|
|
39
39
|
|
|
40
40
|
---
|
|
41
41
|
|
|
42
|
-
##
|
|
42
|
+
## 폴더 구조
|
|
43
43
|
|
|
44
44
|
```
|
|
45
45
|
src/
|
|
@@ -53,18 +53,18 @@ src/
|
|
|
53
53
|
│ │ └── page.tsx
|
|
54
54
|
│ ├── dashboard/
|
|
55
55
|
│ │ ├── page.tsx
|
|
56
|
-
│ │ └── -components/ #
|
|
56
|
+
│ │ └── -components/ # 페이지 전용
|
|
57
57
|
│ └── api/
|
|
58
58
|
│ └── auth/
|
|
59
59
|
│ └── [...all]/
|
|
60
60
|
│ └── route.ts
|
|
61
|
-
├── actions/ # Server Actions (
|
|
61
|
+
├── actions/ # Server Actions (공통)
|
|
62
62
|
│ ├── posts.ts
|
|
63
63
|
│ └── users.ts
|
|
64
64
|
├── components/
|
|
65
|
-
│ └── ui/ # UI
|
|
65
|
+
│ └── ui/ # UI 컴포넌트
|
|
66
66
|
├── lib/
|
|
67
|
-
│ ├── auth.ts # Better Auth
|
|
67
|
+
│ ├── auth.ts # Better Auth 설정
|
|
68
68
|
│ ├── auth-client.ts # Auth Client
|
|
69
69
|
│ ├── prisma.ts # Prisma Client
|
|
70
70
|
│ └── query-client.ts # React Query Client
|
|
@@ -75,7 +75,7 @@ src/
|
|
|
75
75
|
|
|
76
76
|
---
|
|
77
77
|
|
|
78
|
-
##
|
|
78
|
+
## 환경 변수
|
|
79
79
|
|
|
80
80
|
```bash
|
|
81
81
|
# .env.local
|
|
@@ -83,25 +83,25 @@ DATABASE_URL="postgresql://user:password@localhost:5432/mydb"
|
|
|
83
83
|
BETTER_AUTH_SECRET="your-secret-key"
|
|
84
84
|
BETTER_AUTH_URL="http://localhost:3000"
|
|
85
85
|
|
|
86
|
-
#
|
|
86
|
+
# 소셜 로그인 (옵션)
|
|
87
87
|
GOOGLE_CLIENT_ID="..."
|
|
88
88
|
GOOGLE_CLIENT_SECRET="..."
|
|
89
89
|
GITHUB_CLIENT_ID="..."
|
|
90
90
|
GITHUB_CLIENT_SECRET="..."
|
|
91
91
|
|
|
92
|
-
#
|
|
92
|
+
# 클라이언트 공개 변수
|
|
93
93
|
NEXT_PUBLIC_APP_URL="http://localhost:3000"
|
|
94
94
|
```
|
|
95
95
|
|
|
96
96
|
---
|
|
97
97
|
|
|
98
|
-
## Prisma
|
|
98
|
+
## Prisma 설정
|
|
99
99
|
|
|
100
100
|
```bash
|
|
101
|
-
#
|
|
101
|
+
# 초기화
|
|
102
102
|
npx prisma init
|
|
103
103
|
|
|
104
|
-
#
|
|
104
|
+
# 스키마 생성
|
|
105
105
|
mkdir -p prisma/schema
|
|
106
106
|
```
|
|
107
107
|
|
|
@@ -139,10 +139,10 @@ model User {
|
|
|
139
139
|
```
|
|
140
140
|
|
|
141
141
|
```bash
|
|
142
|
-
#
|
|
142
|
+
# DB 동기화
|
|
143
143
|
npx prisma db push
|
|
144
144
|
|
|
145
|
-
#
|
|
145
|
+
# Client 생성
|
|
146
146
|
npx prisma generate
|
|
147
147
|
```
|
|
148
148
|
|
|
@@ -165,7 +165,7 @@ if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = prisma
|
|
|
165
165
|
|
|
166
166
|
---
|
|
167
167
|
|
|
168
|
-
## Better Auth
|
|
168
|
+
## Better Auth 설정
|
|
169
169
|
|
|
170
170
|
```typescript
|
|
171
171
|
// src/lib/auth.ts
|
|
@@ -204,7 +204,7 @@ export const POST = (request: Request) => auth.handler(request)
|
|
|
204
204
|
|
|
205
205
|
---
|
|
206
206
|
|
|
207
|
-
## React Query
|
|
207
|
+
## React Query 설정
|
|
208
208
|
|
|
209
209
|
```typescript
|
|
210
210
|
// src/lib/query-client.ts
|
|
@@ -214,7 +214,7 @@ export function makeQueryClient() {
|
|
|
214
214
|
return new QueryClient({
|
|
215
215
|
defaultOptions: {
|
|
216
216
|
queries: {
|
|
217
|
-
staleTime: 60 * 1000, // 1
|
|
217
|
+
staleTime: 60 * 1000, // 1분
|
|
218
218
|
},
|
|
219
219
|
},
|
|
220
220
|
})
|
|
@@ -256,7 +256,7 @@ import { Providers } from "./providers"
|
|
|
256
256
|
|
|
257
257
|
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
258
258
|
return (
|
|
259
|
-
<html lang="
|
|
259
|
+
<html lang="ko">
|
|
260
260
|
<body>
|
|
261
261
|
<Providers>{children}</Providers>
|
|
262
262
|
</body>
|
|
@@ -267,7 +267,7 @@ export default function RootLayout({ children }: { children: React.ReactNode })
|
|
|
267
267
|
|
|
268
268
|
---
|
|
269
269
|
|
|
270
|
-
##
|
|
270
|
+
## 첫 Server Action
|
|
271
271
|
|
|
272
272
|
```typescript
|
|
273
273
|
// actions/posts.ts
|
|
@@ -296,7 +296,7 @@ export async function createPost(formData: FormData) {
|
|
|
296
296
|
|
|
297
297
|
---
|
|
298
298
|
|
|
299
|
-
##
|
|
299
|
+
## 첫 페이지 (Server Component)
|
|
300
300
|
|
|
301
301
|
```typescript
|
|
302
302
|
// app/posts/page.tsx
|
|
@@ -320,7 +320,7 @@ export default async function PostsPage() {
|
|
|
320
320
|
|
|
321
321
|
---
|
|
322
322
|
|
|
323
|
-
##
|
|
323
|
+
## 첫 폼 (Client Component)
|
|
324
324
|
|
|
325
325
|
```typescript
|
|
326
326
|
// app/posts/_components/create-post-form.tsx
|
|
@@ -359,9 +359,9 @@ export function CreatePostForm() {
|
|
|
359
359
|
|
|
360
360
|
---
|
|
361
361
|
|
|
362
|
-
##
|
|
362
|
+
## 다음 단계
|
|
363
363
|
|
|
364
|
-
- [Conventions](conventions.md) -
|
|
365
|
-
- [Routes](routes.md) -
|
|
366
|
-
- [Server Actions](server-actions.md) - Server Actions
|
|
367
|
-
- [Client Components](client-components.md) - Client Components
|
|
364
|
+
- [Conventions](conventions.md) - 코드 컨벤션
|
|
365
|
+
- [Routes](routes.md) - 라우팅 패턴
|
|
366
|
+
- [Server Actions](server-actions.md) - Server Actions 패턴
|
|
367
|
+
- [Client Components](client-components.md) - Client Components 패턴
|