@kood/claude-code 0.1.2 → 0.1.4
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 +129 -5
- package/package.json +2 -2
- package/templates/hono/CLAUDE.md +20 -2
- package/templates/hono/docs/architecture/architecture.md +909 -0
- package/templates/hono/docs/commands/git.md +275 -0
- package/templates/hono/docs/deployment/cloudflare.md +527 -190
- package/templates/hono/docs/deployment/docker.md +514 -0
- package/templates/hono/docs/deployment/index.md +179 -214
- package/templates/hono/docs/deployment/railway.md +416 -0
- package/templates/hono/docs/deployment/vercel.md +567 -0
- package/templates/hono/docs/library/ai-sdk/index.md +427 -0
- package/templates/hono/docs/library/ai-sdk/openrouter.md +479 -0
- package/templates/hono/docs/library/ai-sdk/providers.md +468 -0
- package/templates/hono/docs/library/ai-sdk/streaming.md +447 -0
- package/templates/hono/docs/library/ai-sdk/structured-output.md +493 -0
- package/templates/hono/docs/library/ai-sdk/tools.md +513 -0
- package/templates/hono/docs/library/hono/env-setup.md +458 -0
- package/templates/hono/docs/library/hono/index.md +1 -3
- package/templates/hono/docs/library/pino/index.md +437 -0
- package/templates/hono/docs/library/prisma/cloudflare-d1.md +503 -0
- package/templates/hono/docs/library/prisma/config.md +362 -0
- package/templates/hono/docs/library/prisma/index.md +86 -13
- package/templates/hono/docs/skills/gemini-review/SKILL.md +116 -116
- package/templates/hono/docs/skills/gemini-review/references/checklists.md +125 -125
- package/templates/hono/docs/skills/gemini-review/references/prompt-templates.md +191 -191
- package/templates/npx/CLAUDE.md +309 -0
- package/templates/npx/docs/commands/git.md +275 -0
- package/templates/npx/docs/library/commander/index.md +164 -0
- package/templates/npx/docs/library/fs-extra/index.md +171 -0
- package/templates/npx/docs/library/prompts/index.md +253 -0
- package/templates/npx/docs/mcp/index.md +60 -0
- package/templates/npx/docs/skills/gemini-review/SKILL.md +220 -0
- package/templates/npx/docs/skills/gemini-review/references/checklists.md +134 -0
- package/templates/npx/docs/skills/gemini-review/references/prompt-templates.md +301 -0
- package/templates/tanstack-start/CLAUDE.md +43 -5
- package/templates/tanstack-start/docs/architecture/architecture.md +134 -4
- package/templates/tanstack-start/docs/commands/git.md +275 -0
- package/templates/tanstack-start/docs/deployment/cloudflare.md +223 -50
- package/templates/tanstack-start/docs/deployment/index.md +320 -30
- package/templates/tanstack-start/docs/deployment/nitro.md +195 -14
- package/templates/tanstack-start/docs/deployment/railway.md +302 -150
- package/templates/tanstack-start/docs/deployment/vercel.md +345 -75
- package/templates/tanstack-start/docs/guides/best-practices.md +203 -1
- package/templates/tanstack-start/docs/guides/env-setup.md +450 -0
- package/templates/tanstack-start/docs/library/ai-sdk/hooks.md +472 -0
- package/templates/tanstack-start/docs/library/ai-sdk/index.md +264 -0
- package/templates/tanstack-start/docs/library/ai-sdk/openrouter.md +371 -0
- package/templates/tanstack-start/docs/library/ai-sdk/providers.md +403 -0
- package/templates/tanstack-start/docs/library/ai-sdk/streaming.md +320 -0
- package/templates/tanstack-start/docs/library/ai-sdk/structured-output.md +454 -0
- package/templates/tanstack-start/docs/library/ai-sdk/tools.md +473 -0
- package/templates/tanstack-start/docs/library/pino/index.md +320 -0
- package/templates/tanstack-start/docs/library/prisma/cloudflare-d1.md +404 -0
- package/templates/tanstack-start/docs/library/prisma/config.md +377 -0
- package/templates/tanstack-start/docs/library/prisma/index.md +3 -5
- package/templates/tanstack-start/docs/library/prisma/schema.md +123 -25
- package/templates/tanstack-start/docs/library/prisma/setup.md +0 -7
- package/templates/tanstack-start/docs/library/tanstack-start/server-functions.md +80 -2
- package/templates/tanstack-start/docs/skills/gemini-review/SKILL.md +116 -116
- package/templates/tanstack-start/docs/skills/gemini-review/references/checklists.md +138 -144
- package/templates/tanstack-start/docs/skills/gemini-review/references/prompt-templates.md +186 -187
- package/templates/hono/docs/git/index.md +0 -180
- package/templates/tanstack-start/docs/git/index.md +0 -203
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
# Prisma - Config 파일 (prisma.config.ts)
|
|
2
|
+
|
|
3
|
+
> **상위 문서**: [Prisma](./index.md)
|
|
4
|
+
|
|
5
|
+
Prisma v7에서 도입된 `prisma.config.ts` 설정 파일입니다.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## ⚠️ 필수: Multi-File 스키마 설정
|
|
10
|
+
|
|
11
|
+
Multi-File 스키마를 사용하려면 **반드시** `prisma.config.ts`에서 스키마 폴더를 지정해야 합니다.
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
// prisma.config.ts
|
|
15
|
+
import path from 'node:path'
|
|
16
|
+
import { defineConfig, env } from 'prisma/config'
|
|
17
|
+
|
|
18
|
+
export default defineConfig({
|
|
19
|
+
// Multi-File 스키마 폴더 지정
|
|
20
|
+
schema: path.join('prisma', 'schema'),
|
|
21
|
+
datasource: {
|
|
22
|
+
url: env('DATABASE_URL'),
|
|
23
|
+
},
|
|
24
|
+
})
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## 파일 위치 및 이름
|
|
30
|
+
|
|
31
|
+
### 지원되는 파일명
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
prisma.config.ts # 권장 (소규모 프로젝트)
|
|
35
|
+
prisma.config.js
|
|
36
|
+
prisma.config.mjs
|
|
37
|
+
prisma.config.cjs
|
|
38
|
+
prisma.config.mts
|
|
39
|
+
prisma.config.cts
|
|
40
|
+
|
|
41
|
+
.config/prisma.ts # 권장 (대규모 프로젝트)
|
|
42
|
+
.config/prisma.js
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### 권장 위치
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
프로젝트/
|
|
49
|
+
├── prisma.config.ts # 프로젝트 루트
|
|
50
|
+
├── prisma/
|
|
51
|
+
│ ├── schema/ # Multi-File 스키마 폴더
|
|
52
|
+
│ │ ├── +base.prisma
|
|
53
|
+
│ │ ├── +enum.prisma
|
|
54
|
+
│ │ └── user.prisma
|
|
55
|
+
│ └── migrations/ # 마이그레이션 폴더
|
|
56
|
+
└── package.json
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## 설정 옵션
|
|
62
|
+
|
|
63
|
+
### 전체 옵션 목록
|
|
64
|
+
|
|
65
|
+
| 옵션 | 타입 | 필수 | 설명 |
|
|
66
|
+
|------|------|------|------|
|
|
67
|
+
| `schema` | string | ❌ | 스키마 파일 또는 폴더 경로 |
|
|
68
|
+
| `datasource.url` | string | ✅ | 데이터베이스 연결 URL |
|
|
69
|
+
| `datasource.shadowDatabaseUrl` | string | ❌ | Shadow DB URL |
|
|
70
|
+
| `migrations.path` | string | ❌ | 마이그레이션 폴더 경로 |
|
|
71
|
+
| `migrations.seed` | string | ❌ | 시드 스크립트 명령어 |
|
|
72
|
+
| `migrations.initShadowDb` | string | ❌ | Shadow DB 초기화 SQL |
|
|
73
|
+
| `views.path` | string | ❌ | SQL 뷰 정의 폴더 |
|
|
74
|
+
| `typedSql.path` | string | ❌ | TypedSQL 파일 폴더 |
|
|
75
|
+
| `experimental` | object | ❌ | 실험적 기능 활성화 |
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## 기본 설정 예시
|
|
80
|
+
|
|
81
|
+
### defineConfig 사용 (권장)
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
// prisma.config.ts
|
|
85
|
+
import 'dotenv/config'
|
|
86
|
+
import { defineConfig, env } from 'prisma/config'
|
|
87
|
+
|
|
88
|
+
export default defineConfig({
|
|
89
|
+
// Multi-File 스키마 설정
|
|
90
|
+
schema: 'prisma/schema',
|
|
91
|
+
|
|
92
|
+
// 마이그레이션 설정
|
|
93
|
+
migrations: {
|
|
94
|
+
path: 'prisma/migrations',
|
|
95
|
+
seed: 'tsx prisma/seed.ts',
|
|
96
|
+
},
|
|
97
|
+
|
|
98
|
+
// 데이터베이스 연결
|
|
99
|
+
datasource: {
|
|
100
|
+
url: env('DATABASE_URL'),
|
|
101
|
+
},
|
|
102
|
+
})
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### satisfies 사용
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
// prisma.config.ts
|
|
109
|
+
import 'dotenv/config'
|
|
110
|
+
import type { PrismaConfig } from 'prisma'
|
|
111
|
+
import { env } from 'prisma/config'
|
|
112
|
+
|
|
113
|
+
export default {
|
|
114
|
+
schema: 'prisma/schema',
|
|
115
|
+
migrations: {
|
|
116
|
+
path: 'prisma/migrations',
|
|
117
|
+
seed: 'tsx prisma/seed.ts',
|
|
118
|
+
},
|
|
119
|
+
datasource: {
|
|
120
|
+
url: env('DATABASE_URL'),
|
|
121
|
+
},
|
|
122
|
+
} satisfies PrismaConfig
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## Multi-File 스키마 설정
|
|
128
|
+
|
|
129
|
+
### 필수 설정
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
// prisma.config.ts
|
|
133
|
+
import path from 'node:path'
|
|
134
|
+
import { defineConfig, env } from 'prisma/config'
|
|
135
|
+
|
|
136
|
+
export default defineConfig({
|
|
137
|
+
// ⚠️ 폴더 경로 지정 (파일이 아님!)
|
|
138
|
+
schema: path.join('prisma', 'schema'),
|
|
139
|
+
datasource: {
|
|
140
|
+
url: env('DATABASE_URL'),
|
|
141
|
+
},
|
|
142
|
+
})
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### 폴더 구조
|
|
146
|
+
|
|
147
|
+
```
|
|
148
|
+
prisma/
|
|
149
|
+
├── schema/ # Multi-File 스키마 폴더
|
|
150
|
+
│ ├── +base.prisma # datasource, generator 설정
|
|
151
|
+
│ ├── +enum.prisma # 모든 enum 정의
|
|
152
|
+
│ ├── user.prisma # User 모델
|
|
153
|
+
│ ├── post.prisma # Post 모델
|
|
154
|
+
│ └── ...
|
|
155
|
+
└── migrations/ # ⚠️ datasource와 같은 레벨에 위치
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### +base.prisma 예시
|
|
159
|
+
|
|
160
|
+
```prisma
|
|
161
|
+
// prisma/schema/+base.prisma
|
|
162
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
163
|
+
// Prisma 기본 설정 파일
|
|
164
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
165
|
+
|
|
166
|
+
// 데이터베이스 연결 설정
|
|
167
|
+
datasource db {
|
|
168
|
+
provider = "postgresql"
|
|
169
|
+
url = env("DATABASE_URL")
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Prisma Client 생성 설정
|
|
173
|
+
generator client {
|
|
174
|
+
provider = "prisma-client" // Prisma v7 필수
|
|
175
|
+
output = "../../generated/prisma"
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## 환경 변수 설정
|
|
182
|
+
|
|
183
|
+
### dotenv 설치
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
npm install dotenv
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### 환경 변수 사용
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
// prisma.config.ts
|
|
193
|
+
import 'dotenv/config' // ⚠️ 반드시 최상단에 import
|
|
194
|
+
import { defineConfig, env } from 'prisma/config'
|
|
195
|
+
|
|
196
|
+
export default defineConfig({
|
|
197
|
+
schema: 'prisma/schema',
|
|
198
|
+
datasource: {
|
|
199
|
+
url: env('DATABASE_URL'), // 타입 안전한 환경 변수 접근
|
|
200
|
+
},
|
|
201
|
+
})
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### .env 파일
|
|
205
|
+
|
|
206
|
+
```env
|
|
207
|
+
DATABASE_URL="postgresql://user:password@localhost:5432/mydb"
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
## 마이그레이션 설정
|
|
213
|
+
|
|
214
|
+
### 시드 스크립트
|
|
215
|
+
|
|
216
|
+
```typescript
|
|
217
|
+
// prisma.config.ts
|
|
218
|
+
import { defineConfig, env } from 'prisma/config'
|
|
219
|
+
|
|
220
|
+
export default defineConfig({
|
|
221
|
+
schema: 'prisma/schema',
|
|
222
|
+
migrations: {
|
|
223
|
+
path: 'prisma/migrations',
|
|
224
|
+
seed: 'tsx prisma/seed.ts', // 시드 명령어
|
|
225
|
+
},
|
|
226
|
+
datasource: {
|
|
227
|
+
url: env('DATABASE_URL'),
|
|
228
|
+
},
|
|
229
|
+
})
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### 시드 파일 예시
|
|
233
|
+
|
|
234
|
+
```typescript
|
|
235
|
+
// prisma/seed.ts
|
|
236
|
+
import { PrismaClient } from '../generated/prisma'
|
|
237
|
+
|
|
238
|
+
const prisma = new PrismaClient()
|
|
239
|
+
|
|
240
|
+
async function main() {
|
|
241
|
+
// 초기 데이터 생성
|
|
242
|
+
await prisma.user.create({
|
|
243
|
+
data: {
|
|
244
|
+
email: 'admin@example.com',
|
|
245
|
+
name: '관리자',
|
|
246
|
+
role: 'ADMIN',
|
|
247
|
+
},
|
|
248
|
+
})
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
main()
|
|
252
|
+
.catch(console.error)
|
|
253
|
+
.finally(() => prisma.$disconnect())
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## Shadow Database 설정
|
|
259
|
+
|
|
260
|
+
마이그레이션 시 사용되는 임시 데이터베이스입니다.
|
|
261
|
+
|
|
262
|
+
```typescript
|
|
263
|
+
// prisma.config.ts
|
|
264
|
+
import { defineConfig, env } from 'prisma/config'
|
|
265
|
+
|
|
266
|
+
export default defineConfig({
|
|
267
|
+
schema: 'prisma/schema',
|
|
268
|
+
datasource: {
|
|
269
|
+
url: env('DATABASE_URL'),
|
|
270
|
+
shadowDatabaseUrl: env('SHADOW_DATABASE_URL'),
|
|
271
|
+
},
|
|
272
|
+
})
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
---
|
|
276
|
+
|
|
277
|
+
## 실험적 기능
|
|
278
|
+
|
|
279
|
+
### External Tables
|
|
280
|
+
|
|
281
|
+
외부에서 관리되는 테이블 설정:
|
|
282
|
+
|
|
283
|
+
```typescript
|
|
284
|
+
// prisma.config.ts
|
|
285
|
+
import { defineConfig, env } from 'prisma/config'
|
|
286
|
+
|
|
287
|
+
export default defineConfig({
|
|
288
|
+
schema: 'prisma/schema',
|
|
289
|
+
experimental: {
|
|
290
|
+
externalTables: true,
|
|
291
|
+
},
|
|
292
|
+
tables: {
|
|
293
|
+
external: ['public.users'],
|
|
294
|
+
},
|
|
295
|
+
enums: {
|
|
296
|
+
external: ['public.role'],
|
|
297
|
+
},
|
|
298
|
+
datasource: {
|
|
299
|
+
url: env('DATABASE_URL'),
|
|
300
|
+
},
|
|
301
|
+
})
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
---
|
|
305
|
+
|
|
306
|
+
## 경로 해석 규칙
|
|
307
|
+
|
|
308
|
+
### 중요 사항
|
|
309
|
+
|
|
310
|
+
- 모든 경로는 **config 파일 위치 기준**으로 해석됩니다
|
|
311
|
+
- CLI 명령어 실행 위치가 아닙니다!
|
|
312
|
+
|
|
313
|
+
### 예시
|
|
314
|
+
|
|
315
|
+
```
|
|
316
|
+
프로젝트/
|
|
317
|
+
├── prisma.config.ts # 여기가 기준!
|
|
318
|
+
├── prisma/
|
|
319
|
+
│ └── schema/
|
|
320
|
+
└── src/
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
```typescript
|
|
324
|
+
// prisma.config.ts
|
|
325
|
+
export default defineConfig({
|
|
326
|
+
// ✅ prisma.config.ts 기준 상대 경로
|
|
327
|
+
schema: 'prisma/schema',
|
|
328
|
+
|
|
329
|
+
// ❌ 이렇게 하지 마세요
|
|
330
|
+
schema: './prisma/schema', // ./ 불필요
|
|
331
|
+
})
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
### 커스텀 경로 지정
|
|
335
|
+
|
|
336
|
+
```bash
|
|
337
|
+
# --config 플래그로 config 파일 위치 지정
|
|
338
|
+
npx prisma validate --config ./path/to/myconfig.ts
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
---
|
|
342
|
+
|
|
343
|
+
## 전체 설정 예시
|
|
344
|
+
|
|
345
|
+
```typescript
|
|
346
|
+
// prisma.config.ts
|
|
347
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
348
|
+
// Prisma v7 설정 파일
|
|
349
|
+
// Multi-File 스키마 및 마이그레이션 설정
|
|
350
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
351
|
+
import 'dotenv/config'
|
|
352
|
+
import path from 'node:path'
|
|
353
|
+
import { defineConfig, env } from 'prisma/config'
|
|
354
|
+
|
|
355
|
+
export default defineConfig({
|
|
356
|
+
// Multi-File 스키마 폴더
|
|
357
|
+
schema: path.join('prisma', 'schema'),
|
|
358
|
+
|
|
359
|
+
// 마이그레이션 설정
|
|
360
|
+
migrations: {
|
|
361
|
+
path: 'prisma/migrations',
|
|
362
|
+
seed: 'tsx prisma/seed.ts',
|
|
363
|
+
},
|
|
364
|
+
|
|
365
|
+
// 데이터베이스 연결
|
|
366
|
+
datasource: {
|
|
367
|
+
url: env('DATABASE_URL'),
|
|
368
|
+
},
|
|
369
|
+
})
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
---
|
|
373
|
+
|
|
374
|
+
## 관련 문서
|
|
375
|
+
|
|
376
|
+
- [스키마 정의 (Multi-File)](./schema.md)
|
|
377
|
+
- [설치 및 설정](./setup.md)
|
|
@@ -47,11 +47,13 @@ generator client {
|
|
|
47
47
|
|
|
48
48
|
## 문서 구조
|
|
49
49
|
|
|
50
|
+
- [Config 파일](./config.md) - prisma.config.ts 설정 ⭐
|
|
50
51
|
- [설치 및 설정](./setup.md) - Prisma Client 설정
|
|
51
|
-
- [스키마 정의](./schema.md) - 모델, 관계, Enum 정의
|
|
52
|
+
- [스키마 정의 (Multi-File)](./schema.md) - 모델, 관계, Enum 정의
|
|
52
53
|
- [CRUD 작업](./crud.md) - Create, Read, Update, Delete
|
|
53
54
|
- [관계 쿼리](./relations.md) - 중첩 생성, 관계 포함 조회
|
|
54
55
|
- [트랜잭션](./transactions.md) - 배열 기반 트랜잭션, 인터랙티브 트랜잭션
|
|
56
|
+
- [Cloudflare D1](./cloudflare-d1.md) - D1 서버리스 데이터베이스 연동
|
|
55
57
|
|
|
56
58
|
## 빠른 시작
|
|
57
59
|
|
|
@@ -64,10 +66,6 @@ npm install -D prisma@7
|
|
|
64
66
|
yarn add @prisma/client@7
|
|
65
67
|
yarn add -D prisma@7
|
|
66
68
|
|
|
67
|
-
# pnpm
|
|
68
|
-
pnpm add @prisma/client@7
|
|
69
|
-
pnpm add -D prisma@7
|
|
70
|
-
|
|
71
69
|
# 초기화
|
|
72
70
|
npx prisma init
|
|
73
71
|
```
|
|
@@ -1,63 +1,161 @@
|
|
|
1
|
-
# Prisma - 스키마 정의
|
|
1
|
+
# Prisma - 스키마 정의 (Multi-File)
|
|
2
2
|
|
|
3
3
|
> **상위 문서**: [Prisma](./index.md)
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## ⚠️ 필수: Multi-File 구조 사용
|
|
6
|
+
|
|
7
|
+
Prisma 스키마는 **반드시 Multi-File 구조**로 작성합니다.
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
prisma/
|
|
11
|
+
├── schema/
|
|
12
|
+
│ ├── +base.prisma # datasource, generator 설정
|
|
13
|
+
│ ├── +enum.prisma # 모든 enum 정의
|
|
14
|
+
│ ├── user.prisma # User 모델
|
|
15
|
+
│ ├── post.prisma # Post 모델
|
|
16
|
+
│ ├── category.prisma # Category 모델
|
|
17
|
+
│ └── profile.prisma # Profile 모델
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## ⚠️ 필수: 한글 주석 작성
|
|
21
|
+
|
|
22
|
+
**Prisma Multi-File의 모든 요소에 한글 주석을 작성해야 합니다.**
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
✅ 파일별 목적 주석
|
|
26
|
+
✅ 모델별 설명 주석
|
|
27
|
+
✅ 필드별 설명 주석 (용도가 명확하지 않은 경우)
|
|
28
|
+
✅ 관계 설명 주석
|
|
29
|
+
✅ enum 값 설명 주석
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Multi-File 스키마 예시
|
|
35
|
+
|
|
36
|
+
### +base.prisma (기본 설정)
|
|
6
37
|
|
|
7
38
|
```prisma
|
|
8
|
-
// prisma/schema.prisma
|
|
39
|
+
// prisma/schema/+base.prisma
|
|
40
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
41
|
+
// Prisma 기본 설정 파일
|
|
42
|
+
// datasource 및 generator 설정
|
|
43
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
44
|
+
|
|
45
|
+
// 데이터베이스 연결 설정
|
|
9
46
|
datasource db {
|
|
10
47
|
provider = "postgresql"
|
|
11
48
|
url = env("DATABASE_URL")
|
|
12
49
|
}
|
|
13
50
|
|
|
51
|
+
// Prisma Client 생성 설정
|
|
14
52
|
generator client {
|
|
15
|
-
provider = "prisma-client"
|
|
53
|
+
provider = "prisma-client" // Prisma v7 필수
|
|
16
54
|
output = "../generated/prisma"
|
|
17
55
|
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### +enum.prisma (열거형 정의)
|
|
59
|
+
|
|
60
|
+
```prisma
|
|
61
|
+
// prisma/schema/+enum.prisma
|
|
62
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
63
|
+
// 모든 열거형(enum) 정의 파일
|
|
64
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
65
|
+
|
|
66
|
+
// 사용자 권한 열거형
|
|
67
|
+
enum Role {
|
|
68
|
+
USER // 일반 사용자
|
|
69
|
+
ADMIN // 관리자
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// 게시글 상태 열거형
|
|
73
|
+
enum PostStatus {
|
|
74
|
+
DRAFT // 임시저장
|
|
75
|
+
PUBLISHED // 공개됨
|
|
76
|
+
ARCHIVED // 보관됨
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### user.prisma (User 모델)
|
|
81
|
+
|
|
82
|
+
```prisma
|
|
83
|
+
// prisma/schema/user.prisma
|
|
84
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
85
|
+
// 사용자 모델
|
|
86
|
+
// 시스템의 모든 사용자 정보를 저장
|
|
87
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
18
88
|
|
|
19
89
|
model User {
|
|
20
90
|
id Int @id @default(autoincrement())
|
|
21
|
-
email String @unique
|
|
22
|
-
name String?
|
|
23
|
-
role Role @default(USER)
|
|
24
|
-
posts Post[]
|
|
25
|
-
profile ExtendedProfile?
|
|
91
|
+
email String @unique // 로그인 이메일 (중복 불가)
|
|
92
|
+
name String? // 표시 이름 (선택)
|
|
93
|
+
role Role @default(USER) // 사용자 권한
|
|
94
|
+
posts Post[] // 작성한 게시글 목록
|
|
95
|
+
profile ExtendedProfile? // 확장 프로필 (1:1)
|
|
26
96
|
createdAt DateTime @default(now())
|
|
27
97
|
updatedAt DateTime @updatedAt
|
|
28
98
|
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### post.prisma (Post 모델)
|
|
102
|
+
|
|
103
|
+
```prisma
|
|
104
|
+
// prisma/schema/post.prisma
|
|
105
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
106
|
+
// 게시글 모델
|
|
107
|
+
// 사용자가 작성한 게시글 정보
|
|
108
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
29
109
|
|
|
30
110
|
model Post {
|
|
31
111
|
id Int @id @default(autoincrement())
|
|
32
|
-
title String
|
|
33
|
-
content String?
|
|
34
|
-
published Boolean @default(false)
|
|
35
|
-
|
|
36
|
-
authorId
|
|
37
|
-
|
|
112
|
+
title String // 게시글 제목
|
|
113
|
+
content String? // 게시글 본문 (선택)
|
|
114
|
+
published Boolean @default(false) // 공개 여부
|
|
115
|
+
status PostStatus @default(DRAFT) // 게시글 상태
|
|
116
|
+
author User @relation(fields: [authorId], references: [id]) // 작성자 관계
|
|
117
|
+
authorId Int // 작성자 ID (외래키)
|
|
118
|
+
categories Category[] // 소속 카테고리 목록 (M:N)
|
|
38
119
|
createdAt DateTime @default(now())
|
|
39
120
|
updatedAt DateTime @updatedAt
|
|
40
121
|
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### category.prisma (Category 모델)
|
|
125
|
+
|
|
126
|
+
```prisma
|
|
127
|
+
// prisma/schema/category.prisma
|
|
128
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
129
|
+
// 카테고리 모델
|
|
130
|
+
// 게시글 분류를 위한 카테고리
|
|
131
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
41
132
|
|
|
42
133
|
model Category {
|
|
43
134
|
id Int @id @default(autoincrement())
|
|
44
|
-
name String @unique
|
|
45
|
-
posts Post[]
|
|
135
|
+
name String @unique // 카테고리명 (중복 불가)
|
|
136
|
+
posts Post[] // 소속 게시글 목록 (M:N)
|
|
46
137
|
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### profile.prisma (Profile 모델)
|
|
141
|
+
|
|
142
|
+
```prisma
|
|
143
|
+
// prisma/schema/profile.prisma
|
|
144
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
145
|
+
// 확장 프로필 모델
|
|
146
|
+
// 사용자의 추가 정보 (1:1 관계)
|
|
147
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
47
148
|
|
|
48
149
|
model ExtendedProfile {
|
|
49
150
|
id Int @id @default(autoincrement())
|
|
50
|
-
biography String
|
|
51
|
-
user User @relation(fields: [userId], references: [id])
|
|
52
|
-
userId Int @unique
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
enum Role {
|
|
56
|
-
USER
|
|
57
|
-
ADMIN
|
|
151
|
+
biography String // 자기소개
|
|
152
|
+
user User @relation(fields: [userId], references: [id]) // 사용자 관계
|
|
153
|
+
userId Int @unique // 사용자 ID (외래키, 유니크)
|
|
58
154
|
}
|
|
59
155
|
```
|
|
60
156
|
|
|
157
|
+
---
|
|
158
|
+
|
|
61
159
|
## 선택적 관계
|
|
62
160
|
|
|
63
161
|
```prisma
|
|
@@ -13,10 +13,6 @@ npm install -D prisma@7
|
|
|
13
13
|
yarn add @prisma/client@7
|
|
14
14
|
yarn add -D prisma@7
|
|
15
15
|
|
|
16
|
-
# pnpm
|
|
17
|
-
pnpm add @prisma/client@7
|
|
18
|
-
pnpm add -D prisma@7
|
|
19
|
-
|
|
20
16
|
# bun
|
|
21
17
|
bun add @prisma/client@7
|
|
22
18
|
bun add prisma@7 --dev
|
|
@@ -34,9 +30,6 @@ npm install -D prisma@7
|
|
|
34
30
|
|
|
35
31
|
# yarn
|
|
36
32
|
yarn up prisma@7 @prisma/client@7
|
|
37
|
-
|
|
38
|
-
# pnpm
|
|
39
|
-
pnpm upgrade prisma@7 @prisma/client@7
|
|
40
33
|
```
|
|
41
34
|
|
|
42
35
|
### Generator 변경 (필수)
|
|
@@ -4,6 +4,21 @@
|
|
|
4
4
|
|
|
5
5
|
Server Functions는 서버에서만 실행되는 타입 안전한 함수입니다.
|
|
6
6
|
|
|
7
|
+
## ⚠️ 필수: TanStack Query 사용
|
|
8
|
+
|
|
9
|
+
**Server Function을 클라이언트에서 호출할 때는 반드시 TanStack Query를 사용해야 합니다.**
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
❌ 금지: Server Function 직접 호출
|
|
13
|
+
✅ 필수: useQuery/useMutation과 함께 사용
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
**이유**:
|
|
17
|
+
- 자동 캐싱 및 중복 요청 제거
|
|
18
|
+
- 로딩/에러 상태 관리
|
|
19
|
+
- 자동 재시도 및 백그라운드 갱신
|
|
20
|
+
- invalidateQueries로 일관된 데이터 동기화
|
|
21
|
+
|
|
7
22
|
## 기본 Server Function
|
|
8
23
|
|
|
9
24
|
```typescript
|
|
@@ -73,9 +88,9 @@ export const submitForm = createServerFn({ method: 'POST' })
|
|
|
73
88
|
})
|
|
74
89
|
```
|
|
75
90
|
|
|
76
|
-
## 컴포넌트에서 호출
|
|
91
|
+
## 컴포넌트에서 호출 (TanStack Query 필수)
|
|
77
92
|
|
|
78
|
-
###
|
|
93
|
+
### ✅ 올바른 패턴: useQuery 사용 (데이터 조회)
|
|
79
94
|
|
|
80
95
|
```tsx
|
|
81
96
|
import { useServerFn } from '@tanstack/react-start'
|
|
@@ -103,6 +118,69 @@ function PostList() {
|
|
|
103
118
|
}
|
|
104
119
|
```
|
|
105
120
|
|
|
121
|
+
### ✅ 올바른 패턴: useMutation 사용 (데이터 변경)
|
|
122
|
+
|
|
123
|
+
```tsx
|
|
124
|
+
import { useServerFn } from '@tanstack/react-start'
|
|
125
|
+
import { useMutation, useQueryClient } from '@tanstack/react-query'
|
|
126
|
+
import { createPost, deletePost } from '~/lib/server-functions'
|
|
127
|
+
|
|
128
|
+
function PostForm() {
|
|
129
|
+
const queryClient = useQueryClient()
|
|
130
|
+
const createPostFn = useServerFn(createPost)
|
|
131
|
+
|
|
132
|
+
const mutation = useMutation({
|
|
133
|
+
mutationFn: (data: { title: string; content: string }) => createPostFn({ data }),
|
|
134
|
+
onSuccess: () => {
|
|
135
|
+
// 관련 쿼리 무효화로 데이터 동기화
|
|
136
|
+
queryClient.invalidateQueries({ queryKey: ['posts'] })
|
|
137
|
+
},
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
|
141
|
+
e.preventDefault()
|
|
142
|
+
const formData = new FormData(e.currentTarget)
|
|
143
|
+
mutation.mutate({
|
|
144
|
+
title: formData.get('title') as string,
|
|
145
|
+
content: formData.get('content') as string,
|
|
146
|
+
})
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return (
|
|
150
|
+
<form onSubmit={handleSubmit}>
|
|
151
|
+
<input name="title" required />
|
|
152
|
+
<textarea name="content" required />
|
|
153
|
+
<button type="submit" disabled={mutation.isPending}>
|
|
154
|
+
{mutation.isPending ? '저장 중...' : '저장'}
|
|
155
|
+
</button>
|
|
156
|
+
</form>
|
|
157
|
+
)
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### ❌ 금지: Server Function 직접 호출
|
|
162
|
+
|
|
163
|
+
```tsx
|
|
164
|
+
// ❌ 이렇게 하지 마세요!
|
|
165
|
+
function BadExample() {
|
|
166
|
+
const [posts, setPosts] = useState([])
|
|
167
|
+
const [loading, setLoading] = useState(false)
|
|
168
|
+
|
|
169
|
+
useEffect(() => {
|
|
170
|
+
setLoading(true)
|
|
171
|
+
getPosts()
|
|
172
|
+
.then(setPosts)
|
|
173
|
+
.finally(() => setLoading(false))
|
|
174
|
+
}, [])
|
|
175
|
+
|
|
176
|
+
// 문제점:
|
|
177
|
+
// - 중복 요청 발생 가능
|
|
178
|
+
// - 캐싱 없음
|
|
179
|
+
// - 에러 처리 수동
|
|
180
|
+
// - 다른 컴포넌트와 데이터 동기화 안됨
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
106
184
|
## 보안 패턴
|
|
107
185
|
|
|
108
186
|
### 서버 전용 데이터 보호
|