@kood/claude-code 0.3.8 → 0.3.10
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 +1 -1
- package/package.json +1 -1
- package/templates/.claude/agents/code-reviewer.md +16 -1
- package/templates/.claude/agents/dependency-manager.md +16 -1
- package/templates/.claude/agents/deployment-validator.md +16 -1
- package/templates/.claude/agents/git-operator.md +16 -1
- package/templates/.claude/agents/implementation-executor.md +16 -1
- package/templates/.claude/agents/lint-fixer.md +16 -1
- package/templates/.claude/agents/refactor-advisor.md +16 -1
- package/templates/.claude/commands/agent-creator.md +16 -1
- package/templates/.claude/commands/bug-fix.md +16 -1
- package/templates/.claude/commands/command-creator.md +17 -1
- package/templates/.claude/commands/docs-creator.md +17 -1
- package/templates/.claude/commands/docs-refactor.md +17 -1
- package/templates/.claude/commands/execute.md +17 -1
- package/templates/.claude/commands/git-all.md +16 -1
- package/templates/.claude/commands/git-session.md +17 -1
- package/templates/.claude/commands/git.md +17 -1
- package/templates/.claude/commands/lint-fix.md +17 -1
- package/templates/.claude/commands/lint-init.md +17 -1
- package/templates/.claude/commands/plan.md +17 -1
- package/templates/.claude/commands/prd.md +17 -1
- package/templates/.claude/commands/pre-deploy.md +17 -1
- package/templates/.claude/commands/refactor.md +17 -1
- package/templates/.claude/commands/version-update.md +17 -1
- package/templates/hono/CLAUDE.md +1 -0
- package/templates/nextjs/CLAUDE.md +12 -9
- package/templates/nextjs/docs/architecture.md +812 -0
- package/templates/npx/CLAUDE.md +1 -0
- package/templates/tanstack-start/CLAUDE.md +1 -0
- package/templates/tanstack-start/docs/library/better-auth/index.md +225 -185
- package/templates/tanstack-start/docs/library/prisma/index.md +1025 -41
- package/templates/tanstack-start/docs/library/t3-env/index.md +207 -40
- package/templates/tanstack-start/docs/library/tanstack-query/index.md +878 -42
- package/templates/tanstack-start/docs/library/tanstack-router/index.md +602 -54
- package/templates/tanstack-start/docs/library/tanstack-start/index.md +1334 -33
- package/templates/tanstack-start/docs/library/zod/index.md +674 -31
|
@@ -1,73 +1,1057 @@
|
|
|
1
1
|
# Prisma
|
|
2
2
|
|
|
3
|
-
> **Version
|
|
3
|
+
> **Version 7.x** | Node.js/TypeScript ORM
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
<context>
|
|
8
|
+
|
|
9
|
+
**Purpose:** Node.js/TypeScript용 타입 안전 ORM
|
|
10
|
+
|
|
11
|
+
**Key Features:**
|
|
12
|
+
- 타입 안전 데이터베이스 클라이언트
|
|
13
|
+
- Multi-File 스키마 지원 (v7)
|
|
14
|
+
- 마이그레이션 관리
|
|
15
|
+
- 관계형/NoSQL 데이터베이스 지원
|
|
16
|
+
|
|
17
|
+
**Breaking Changes (v7):**
|
|
18
|
+
- `generator client { provider = "prisma-client" }` (~~prisma-client-js~~)
|
|
19
|
+
- `output` 필드 필수
|
|
20
|
+
- Multi-File 스키마 구조 (`prisma/schema/` 폴더)
|
|
21
|
+
|
|
22
|
+
</context>
|
|
12
23
|
|
|
13
24
|
---
|
|
14
25
|
|
|
15
|
-
|
|
26
|
+
<forbidden>
|
|
27
|
+
|
|
28
|
+
| 분류 | 금지 |
|
|
29
|
+
|------|------|
|
|
30
|
+
| **자동 실행** | ❌ `prisma db push` 자동 실행 |
|
|
31
|
+
| | ❌ `prisma migrate` 자동 실행 |
|
|
32
|
+
| | ❌ `prisma generate` 자동 실행 |
|
|
33
|
+
| **스키마 변경** | ❌ `schema.prisma` 임의 변경 (사용자 확인 없이) |
|
|
34
|
+
| **구조** | ❌ Single-File 스키마 (`schema.prisma`) |
|
|
35
|
+
|
|
36
|
+
</forbidden>
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
<required>
|
|
41
|
+
|
|
42
|
+
| 분류 | 필수 |
|
|
43
|
+
|------|------|
|
|
44
|
+
| **구조** | ✅ Multi-File 스키마 (`prisma/schema/` 폴더) |
|
|
45
|
+
| **주석** | ✅ 모든 모델/필드/enum에 한글 주석 |
|
|
46
|
+
| **v7 설정** | ✅ `generator client { provider = "prisma-client" }` |
|
|
47
|
+
| | ✅ `output` 필드 명시 (`../generated/prisma`) |
|
|
48
|
+
| **Import** | ✅ `import { PrismaClient } from './generated/prisma'` |
|
|
49
|
+
|
|
50
|
+
</required>
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
<version_v7>
|
|
55
|
+
|
|
56
|
+
## v7 Breaking Changes
|
|
57
|
+
|
|
58
|
+
### 1. Generator Provider
|
|
59
|
+
|
|
60
|
+
```prisma
|
|
61
|
+
// ❌ v6 (이전)
|
|
62
|
+
generator client {
|
|
63
|
+
provider = "prisma-client-js"
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// ✅ v7 (필수)
|
|
67
|
+
generator client {
|
|
68
|
+
provider = "prisma-client"
|
|
69
|
+
output = "../generated/prisma" // output 필수!
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### 2. Multi-File Schema
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
// ❌ v6 구조
|
|
77
|
+
prisma/
|
|
78
|
+
└── schema.prisma
|
|
79
|
+
|
|
80
|
+
// ✅ v7 구조
|
|
81
|
+
prisma/
|
|
82
|
+
├── schema/
|
|
83
|
+
│ ├── +base.prisma # datasource, generator
|
|
84
|
+
│ ├── +enum.prisma # 모든 enum
|
|
85
|
+
│ ├── user.prisma # User 모델
|
|
86
|
+
│ └── post.prisma # Post 모델
|
|
87
|
+
└── migrations/
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### 3. Import Path
|
|
16
91
|
|
|
17
92
|
```typescript
|
|
18
|
-
|
|
19
|
-
|
|
93
|
+
// ❌ v6
|
|
94
|
+
import { PrismaClient } from '@prisma/client'
|
|
20
95
|
|
|
21
|
-
//
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const updated = await prisma.user.update({ where: { id }, data: { name } })
|
|
25
|
-
const deleted = await prisma.user.delete({ where: { id } })
|
|
96
|
+
// ✅ v7
|
|
97
|
+
import { PrismaClient } from './generated/prisma'
|
|
98
|
+
```
|
|
26
99
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
100
|
+
### 4. Config File
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
// prisma.config.ts (v7 필수)
|
|
104
|
+
import 'dotenv/config'
|
|
105
|
+
import path from 'node:path'
|
|
106
|
+
import { defineConfig, env } from 'prisma/config'
|
|
107
|
+
|
|
108
|
+
export default defineConfig({
|
|
109
|
+
schema: path.join('prisma', 'schema'), // 폴더!
|
|
110
|
+
migrations: {
|
|
111
|
+
path: 'prisma/migrations',
|
|
112
|
+
seed: 'tsx prisma/seed.ts',
|
|
113
|
+
},
|
|
114
|
+
datasource: {
|
|
115
|
+
url: env('DATABASE_URL'),
|
|
116
|
+
},
|
|
31
117
|
})
|
|
32
118
|
```
|
|
33
119
|
|
|
34
|
-
|
|
120
|
+
</version_v7>
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
<setup>
|
|
125
|
+
|
|
126
|
+
## 설치 및 설정
|
|
127
|
+
|
|
128
|
+
### 설치
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
yarn add @prisma/client@7
|
|
132
|
+
yarn add -D prisma@7
|
|
133
|
+
npx prisma init
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Multi-File Schema 구조 생성
|
|
137
|
+
|
|
138
|
+
```
|
|
139
|
+
프로젝트/
|
|
140
|
+
├── prisma.config.ts
|
|
141
|
+
├── prisma/
|
|
142
|
+
│ ├── schema/
|
|
143
|
+
│ │ ├── +base.prisma
|
|
144
|
+
│ │ ├── +enum.prisma
|
|
145
|
+
│ │ └── user.prisma
|
|
146
|
+
│ ├── migrations/
|
|
147
|
+
│ └── seed.ts
|
|
148
|
+
└── generated/
|
|
149
|
+
└── prisma/
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Prisma Client 초기화
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
// lib/prisma.ts
|
|
156
|
+
import { PrismaClient } from './generated/prisma'
|
|
157
|
+
|
|
158
|
+
const globalForPrisma = globalThis as unknown as {
|
|
159
|
+
prisma: PrismaClient | undefined
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export const prisma = globalForPrisma.prisma ?? new PrismaClient({
|
|
163
|
+
log: ['query', 'error', 'warn']
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
167
|
+
globalForPrisma.prisma = prisma
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### TanStack Start 연동
|
|
172
|
+
|
|
173
|
+
```typescript
|
|
174
|
+
import { createServerFn } from '@tanstack/react-start'
|
|
175
|
+
import { prisma } from '@/lib/prisma'
|
|
176
|
+
|
|
177
|
+
// GET
|
|
178
|
+
export const getUsers = createServerFn({ method: 'GET' })
|
|
179
|
+
.handler(async () => prisma.user.findMany({ include: { posts: true } }))
|
|
180
|
+
|
|
181
|
+
// POST with validation
|
|
182
|
+
export const createUser = createServerFn({ method: 'POST' })
|
|
183
|
+
.inputValidator(createUserSchema)
|
|
184
|
+
.handler(async ({ data }) => prisma.user.create({ data }))
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
</setup>
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
<configuration>
|
|
192
|
+
|
|
193
|
+
## Configuration (prisma.config.ts)
|
|
194
|
+
|
|
195
|
+
### 기본 설정
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
// prisma.config.ts
|
|
199
|
+
import 'dotenv/config'
|
|
200
|
+
import path from 'node:path'
|
|
201
|
+
import { defineConfig, env } from 'prisma/config'
|
|
202
|
+
|
|
203
|
+
export default defineConfig({
|
|
204
|
+
schema: path.join('prisma', 'schema'),
|
|
205
|
+
migrations: {
|
|
206
|
+
path: 'prisma/migrations',
|
|
207
|
+
seed: 'tsx prisma/seed.ts',
|
|
208
|
+
},
|
|
209
|
+
datasource: {
|
|
210
|
+
url: env('DATABASE_URL'),
|
|
211
|
+
},
|
|
212
|
+
})
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### 옵션
|
|
216
|
+
|
|
217
|
+
| 옵션 | 설명 | 필수 |
|
|
218
|
+
|------|------|------|
|
|
219
|
+
| `schema` | 스키마 폴더 경로 | ✅ |
|
|
220
|
+
| `datasource.url` | 데이터베이스 URL | ✅ |
|
|
221
|
+
| `datasource.shadowDatabaseUrl` | Shadow DB URL (개발용) | |
|
|
222
|
+
| `migrations.path` | 마이그레이션 폴더 | |
|
|
223
|
+
| `migrations.seed` | 시드 명령어 | |
|
|
224
|
+
|
|
225
|
+
### +base.prisma
|
|
35
226
|
|
|
36
227
|
```prisma
|
|
228
|
+
datasource db {
|
|
229
|
+
provider = "postgresql" // postgresql | mysql | sqlite | mongodb
|
|
230
|
+
url = env("DATABASE_URL")
|
|
231
|
+
}
|
|
232
|
+
|
|
37
233
|
generator client {
|
|
38
|
-
provider = "prisma-client"
|
|
39
|
-
output = "
|
|
234
|
+
provider = "prisma-client"
|
|
235
|
+
output = "../../generated/prisma"
|
|
236
|
+
}
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### Seed 파일
|
|
240
|
+
|
|
241
|
+
```typescript
|
|
242
|
+
// prisma/seed.ts
|
|
243
|
+
import { PrismaClient } from '../generated/prisma'
|
|
244
|
+
|
|
245
|
+
const prisma = new PrismaClient()
|
|
246
|
+
|
|
247
|
+
async function main() {
|
|
248
|
+
// 관리자 계정 생성
|
|
249
|
+
await prisma.user.create({
|
|
250
|
+
data: {
|
|
251
|
+
email: 'admin@example.com',
|
|
252
|
+
name: '관리자',
|
|
253
|
+
role: 'ADMIN',
|
|
254
|
+
},
|
|
255
|
+
})
|
|
256
|
+
|
|
257
|
+
console.log('Seed completed')
|
|
40
258
|
}
|
|
259
|
+
|
|
260
|
+
main()
|
|
261
|
+
.catch(console.error)
|
|
262
|
+
.finally(() => prisma.$disconnect())
|
|
41
263
|
```
|
|
42
264
|
|
|
43
|
-
|
|
265
|
+
</configuration>
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
269
|
+
<schema>
|
|
270
|
+
|
|
271
|
+
## Schema Definition (Multi-File)
|
|
272
|
+
|
|
273
|
+
### 구조
|
|
274
|
+
|
|
275
|
+
```
|
|
276
|
+
prisma/schema/
|
|
277
|
+
├── +base.prisma # datasource, generator
|
|
278
|
+
├── +enum.prisma # 모든 enum 정의
|
|
279
|
+
├── user.prisma # User 모델
|
|
280
|
+
├── post.prisma # Post 모델
|
|
281
|
+
└── category.prisma # Category 모델
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
### +enum.prisma
|
|
285
|
+
|
|
286
|
+
```prisma
|
|
287
|
+
// +enum.prisma
|
|
288
|
+
// 사용자 역할
|
|
289
|
+
enum Role {
|
|
290
|
+
USER // 일반 사용자
|
|
291
|
+
ADMIN // 관리자
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// 게시글 상태
|
|
295
|
+
enum PostStatus {
|
|
296
|
+
DRAFT // 임시 저장
|
|
297
|
+
PUBLISHED // 발행됨
|
|
298
|
+
ARCHIVED // 보관됨
|
|
299
|
+
}
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### 모델 정의
|
|
303
|
+
|
|
304
|
+
```prisma
|
|
305
|
+
// user.prisma
|
|
306
|
+
// 사용자 모델
|
|
307
|
+
model User {
|
|
308
|
+
id Int @id @default(autoincrement())
|
|
309
|
+
email String @unique // 로그인 이메일
|
|
310
|
+
name String? // 표시 이름
|
|
311
|
+
role Role @default(USER) // 권한
|
|
312
|
+
posts Post[] // 작성 게시글 (1:N)
|
|
313
|
+
profile Profile? // 프로필 (1:1)
|
|
314
|
+
createdAt DateTime @default(now())
|
|
315
|
+
updatedAt DateTime @updatedAt
|
|
316
|
+
|
|
317
|
+
@@index([email])
|
|
318
|
+
@@map("users")
|
|
319
|
+
}
|
|
44
320
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
321
|
+
// post.prisma
|
|
322
|
+
// 게시글 모델
|
|
323
|
+
model Post {
|
|
324
|
+
id Int @id @default(autoincrement())
|
|
325
|
+
title String // 제목
|
|
326
|
+
content String? // 본문
|
|
327
|
+
status PostStatus @default(DRAFT)
|
|
328
|
+
author User @relation(fields: [authorId], references: [id])
|
|
329
|
+
authorId Int // 작성자 ID
|
|
330
|
+
categories Category[] // 카테고리 (M:N)
|
|
331
|
+
createdAt DateTime @default(now())
|
|
332
|
+
updatedAt DateTime @updatedAt
|
|
333
|
+
|
|
334
|
+
@@index([authorId])
|
|
335
|
+
@@index([status])
|
|
336
|
+
@@map("posts")
|
|
337
|
+
}
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
### 관계 패턴
|
|
341
|
+
|
|
342
|
+
| 관계 | 패턴 |
|
|
343
|
+
|------|------|
|
|
344
|
+
| **1:1** | `Profile? @relation(fields: [userId], references: [id])` + `userId Int @unique` |
|
|
345
|
+
| **1:N** | `posts Post[]` (부모) / `author User @relation(...)` (자식) |
|
|
346
|
+
| **M:N (암묵적)** | `categories Category[]` (양쪽) |
|
|
347
|
+
| **M:N (명시적)** | 중간 테이블 + `@@id([id1, id2])` |
|
|
348
|
+
|
|
349
|
+
### 1:1 관계
|
|
350
|
+
|
|
351
|
+
```prisma
|
|
352
|
+
// profile.prisma
|
|
353
|
+
model Profile {
|
|
354
|
+
id Int @id @default(autoincrement())
|
|
355
|
+
bio String? // 소개
|
|
356
|
+
user User @relation(fields: [userId], references: [id])
|
|
357
|
+
userId Int @unique // unique 필수!
|
|
358
|
+
}
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
### 1:N 관계
|
|
362
|
+
|
|
363
|
+
```prisma
|
|
364
|
+
// user.prisma
|
|
365
|
+
model User {
|
|
366
|
+
posts Post[] // 관계 배열
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// post.prisma
|
|
370
|
+
model Post {
|
|
371
|
+
author User @relation(fields: [authorId], references: [id])
|
|
372
|
+
authorId Int // Foreign Key
|
|
373
|
+
}
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
### M:N 관계 (암묵적)
|
|
377
|
+
|
|
378
|
+
```prisma
|
|
379
|
+
// post.prisma
|
|
380
|
+
model Post {
|
|
381
|
+
categories Category[] // 중간 테이블 자동 생성
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// category.prisma
|
|
385
|
+
model Category {
|
|
386
|
+
posts Post[]
|
|
387
|
+
}
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
### M:N 관계 (명시적)
|
|
391
|
+
|
|
392
|
+
```prisma
|
|
393
|
+
// category-on-post.prisma
|
|
394
|
+
model CategoriesOnPosts {
|
|
395
|
+
post Post @relation(fields: [postId], references: [id])
|
|
396
|
+
postId Int
|
|
397
|
+
category Category @relation(fields: [categoryId], references: [id])
|
|
398
|
+
categoryId Int
|
|
399
|
+
assignedAt DateTime @default(now()) // 추가 필드 가능
|
|
400
|
+
|
|
401
|
+
@@id([postId, categoryId])
|
|
402
|
+
@@index([postId])
|
|
403
|
+
@@index([categoryId])
|
|
404
|
+
}
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
### 선택적 관계
|
|
408
|
+
|
|
409
|
+
```prisma
|
|
410
|
+
author User? @relation(fields: [authorId], references: [id])
|
|
411
|
+
authorId Int? // nullable
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
### 인덱스 & 매핑
|
|
415
|
+
|
|
416
|
+
```prisma
|
|
417
|
+
// 단일 인덱스
|
|
418
|
+
@@index([email])
|
|
419
|
+
@@index([createdAt])
|
|
420
|
+
|
|
421
|
+
// 복합 인덱스
|
|
422
|
+
@@index([authorId, status])
|
|
423
|
+
|
|
424
|
+
// 복합 키
|
|
425
|
+
@@id([postId, categoryId])
|
|
426
|
+
|
|
427
|
+
// 필드 매핑
|
|
428
|
+
userId Int @map("user_id")
|
|
429
|
+
|
|
430
|
+
// 테이블 매핑
|
|
431
|
+
@@map("users")
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
</schema>
|
|
51
435
|
|
|
52
436
|
---
|
|
53
437
|
|
|
54
|
-
|
|
438
|
+
<crud>
|
|
439
|
+
|
|
440
|
+
## CRUD Operations
|
|
441
|
+
|
|
442
|
+
### Create
|
|
443
|
+
|
|
444
|
+
```typescript
|
|
445
|
+
// 단일 생성
|
|
446
|
+
const user = await prisma.user.create({
|
|
447
|
+
data: {
|
|
448
|
+
email: 'alice@prisma.io',
|
|
449
|
+
name: 'Alice',
|
|
450
|
+
role: 'USER',
|
|
451
|
+
},
|
|
452
|
+
})
|
|
453
|
+
|
|
454
|
+
// 관계 포함 생성
|
|
455
|
+
const user = await prisma.user.create({
|
|
456
|
+
data: {
|
|
457
|
+
email: 'bob@prisma.io',
|
|
458
|
+
name: 'Bob',
|
|
459
|
+
posts: {
|
|
460
|
+
create: [
|
|
461
|
+
{ title: 'Hello World', status: 'PUBLISHED' },
|
|
462
|
+
{ title: 'Second Post', status: 'DRAFT' },
|
|
463
|
+
],
|
|
464
|
+
},
|
|
465
|
+
},
|
|
466
|
+
include: { posts: true },
|
|
467
|
+
})
|
|
468
|
+
|
|
469
|
+
// connectOrCreate (있으면 연결, 없으면 생성)
|
|
470
|
+
const post = await prisma.post.create({
|
|
471
|
+
data: {
|
|
472
|
+
title: 'Post with Tags',
|
|
473
|
+
categories: {
|
|
474
|
+
connectOrCreate: [
|
|
475
|
+
{ where: { name: 'Tech' }, create: { name: 'Tech' } },
|
|
476
|
+
{ where: { name: 'Tutorial' }, create: { name: 'Tutorial' } },
|
|
477
|
+
],
|
|
478
|
+
},
|
|
479
|
+
},
|
|
480
|
+
})
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
### Read
|
|
484
|
+
|
|
485
|
+
```typescript
|
|
486
|
+
// 단일 조회 (findUnique)
|
|
487
|
+
const user = await prisma.user.findUnique({
|
|
488
|
+
where: { email: 'alice@prisma.io' }
|
|
489
|
+
})
|
|
490
|
+
|
|
491
|
+
// 다중 조회 (findMany)
|
|
492
|
+
const users = await prisma.user.findMany({
|
|
493
|
+
where: { role: 'ADMIN' },
|
|
494
|
+
orderBy: { createdAt: 'desc' },
|
|
495
|
+
take: 10,
|
|
496
|
+
})
|
|
497
|
+
|
|
498
|
+
// 관계 포함
|
|
499
|
+
const users = await prisma.user.findMany({
|
|
500
|
+
include: {
|
|
501
|
+
posts: true,
|
|
502
|
+
profile: true,
|
|
503
|
+
},
|
|
504
|
+
})
|
|
505
|
+
|
|
506
|
+
// 필드 선택 (select)
|
|
507
|
+
const user = await prisma.user.findUnique({
|
|
508
|
+
where: { email: 'alice@prisma.io' },
|
|
509
|
+
select: {
|
|
510
|
+
email: true,
|
|
511
|
+
posts: {
|
|
512
|
+
select: { title: true, createdAt: true },
|
|
513
|
+
where: { status: 'PUBLISHED' },
|
|
514
|
+
},
|
|
515
|
+
},
|
|
516
|
+
})
|
|
517
|
+
|
|
518
|
+
// 관계로 필터
|
|
519
|
+
const users = await prisma.user.findMany({
|
|
520
|
+
where: {
|
|
521
|
+
posts: {
|
|
522
|
+
some: { status: 'PUBLISHED' }, // 게시글이 하나라도 있는 사용자
|
|
523
|
+
},
|
|
524
|
+
},
|
|
525
|
+
})
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
### Update
|
|
529
|
+
|
|
530
|
+
```typescript
|
|
531
|
+
// 단일 수정
|
|
532
|
+
const user = await prisma.user.update({
|
|
533
|
+
where: { id: 1 },
|
|
534
|
+
data: { name: 'Updated Name' },
|
|
535
|
+
})
|
|
536
|
+
|
|
537
|
+
// 다중 수정
|
|
538
|
+
await prisma.user.updateMany({
|
|
539
|
+
where: { role: 'USER' },
|
|
540
|
+
data: { role: 'ADMIN' },
|
|
541
|
+
})
|
|
542
|
+
|
|
543
|
+
// Upsert (있으면 수정, 없으면 생성)
|
|
544
|
+
const user = await prisma.user.upsert({
|
|
545
|
+
where: { email: 'alice@prisma.io' },
|
|
546
|
+
update: { name: 'Updated Alice' },
|
|
547
|
+
create: { email: 'alice@prisma.io', name: 'Alice' },
|
|
548
|
+
})
|
|
549
|
+
|
|
550
|
+
// 증감 연산
|
|
551
|
+
await prisma.post.update({
|
|
552
|
+
where: { id: 1 },
|
|
553
|
+
data: {
|
|
554
|
+
views: { increment: 1 },
|
|
555
|
+
likes: { decrement: 1 },
|
|
556
|
+
},
|
|
557
|
+
})
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
### Delete
|
|
561
|
+
|
|
562
|
+
```typescript
|
|
563
|
+
// 단일 삭제
|
|
564
|
+
await prisma.user.delete({ where: { id: 1 } })
|
|
565
|
+
|
|
566
|
+
// 다중 삭제
|
|
567
|
+
await prisma.post.deleteMany({ where: { status: 'DRAFT' } })
|
|
568
|
+
|
|
569
|
+
// 전체 삭제
|
|
570
|
+
await prisma.user.deleteMany({})
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
### 필터 연산자
|
|
574
|
+
|
|
575
|
+
| 타입 | 연산자 |
|
|
576
|
+
|------|--------|
|
|
577
|
+
| **문자열** | `contains`, `startsWith`, `endsWith`, `mode: 'insensitive'` |
|
|
578
|
+
| **숫자** | `gt`, `gte`, `lt`, `lte` |
|
|
579
|
+
| **배열** | `in`, `notIn` |
|
|
580
|
+
| **논리** | `OR`, `AND`, `NOT` |
|
|
581
|
+
| **관계** | `some`, `every`, `none` |
|
|
582
|
+
|
|
583
|
+
```typescript
|
|
584
|
+
// 문자열 검색
|
|
585
|
+
where: { email: { contains: 'prisma', mode: 'insensitive' } }
|
|
586
|
+
|
|
587
|
+
// 숫자 범위
|
|
588
|
+
where: { age: { gte: 18, lte: 65 } }
|
|
589
|
+
|
|
590
|
+
// 배열
|
|
591
|
+
where: { id: { in: [1, 2, 3] } }
|
|
592
|
+
|
|
593
|
+
// 논리 연산
|
|
594
|
+
where: {
|
|
595
|
+
OR: [
|
|
596
|
+
{ role: 'ADMIN' },
|
|
597
|
+
{ posts: { some: { status: 'PUBLISHED' } } },
|
|
598
|
+
],
|
|
599
|
+
}
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
</crud>
|
|
603
|
+
|
|
604
|
+
---
|
|
605
|
+
|
|
606
|
+
<relations>
|
|
607
|
+
|
|
608
|
+
## Relation Queries
|
|
609
|
+
|
|
610
|
+
### 중첩 생성
|
|
611
|
+
|
|
612
|
+
```typescript
|
|
613
|
+
// 1:N 관계 생성
|
|
614
|
+
const user = await prisma.user.create({
|
|
615
|
+
data: {
|
|
616
|
+
email: 'user@prisma.io',
|
|
617
|
+
posts: {
|
|
618
|
+
create: [
|
|
619
|
+
{ title: 'Post 1', status: 'PUBLISHED' },
|
|
620
|
+
{ title: 'Post 2', status: 'DRAFT' },
|
|
621
|
+
],
|
|
622
|
+
},
|
|
623
|
+
},
|
|
624
|
+
include: { posts: true },
|
|
625
|
+
})
|
|
626
|
+
```
|
|
627
|
+
|
|
628
|
+
### 관계 연결
|
|
629
|
+
|
|
630
|
+
```typescript
|
|
631
|
+
// connect - 기존 레코드 연결
|
|
632
|
+
await prisma.post.create({
|
|
633
|
+
data: {
|
|
634
|
+
title: 'New Post',
|
|
635
|
+
author: { connect: { id: 1 } },
|
|
636
|
+
},
|
|
637
|
+
})
|
|
638
|
+
|
|
639
|
+
// connectOrCreate
|
|
640
|
+
await prisma.post.create({
|
|
641
|
+
data: {
|
|
642
|
+
title: 'Post with Category',
|
|
643
|
+
categories: {
|
|
644
|
+
connectOrCreate: {
|
|
645
|
+
where: { name: 'Tech' },
|
|
646
|
+
create: { name: 'Tech' },
|
|
647
|
+
},
|
|
648
|
+
},
|
|
649
|
+
},
|
|
650
|
+
})
|
|
651
|
+
|
|
652
|
+
// disconnect
|
|
653
|
+
await prisma.post.update({
|
|
654
|
+
where: { id: 1 },
|
|
655
|
+
data: {
|
|
656
|
+
author: { disconnect: true },
|
|
657
|
+
},
|
|
658
|
+
})
|
|
659
|
+
```
|
|
660
|
+
|
|
661
|
+
### 관계 포함 조회
|
|
662
|
+
|
|
663
|
+
```typescript
|
|
664
|
+
// include
|
|
665
|
+
const users = await prisma.user.findMany({
|
|
666
|
+
include: {
|
|
667
|
+
posts: true,
|
|
668
|
+
profile: true,
|
|
669
|
+
},
|
|
670
|
+
})
|
|
671
|
+
|
|
672
|
+
// 중첩 include
|
|
673
|
+
const users = await prisma.user.findMany({
|
|
674
|
+
include: {
|
|
675
|
+
posts: {
|
|
676
|
+
include: { categories: true },
|
|
677
|
+
},
|
|
678
|
+
},
|
|
679
|
+
})
|
|
680
|
+
|
|
681
|
+
// 필터 + 정렬
|
|
682
|
+
const users = await prisma.user.findMany({
|
|
683
|
+
include: {
|
|
684
|
+
posts: {
|
|
685
|
+
where: { status: 'PUBLISHED' },
|
|
686
|
+
orderBy: { createdAt: 'desc' },
|
|
687
|
+
take: 5,
|
|
688
|
+
},
|
|
689
|
+
},
|
|
690
|
+
})
|
|
691
|
+
```
|
|
692
|
+
|
|
693
|
+
### 관계로 필터링
|
|
694
|
+
|
|
695
|
+
```typescript
|
|
696
|
+
// some - 하나라도 만족
|
|
697
|
+
where: {
|
|
698
|
+
posts: { some: { status: 'PUBLISHED' } },
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
// every - 모두 만족
|
|
702
|
+
where: {
|
|
703
|
+
posts: { every: { status: 'PUBLISHED' } },
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
// none - 만족하는 것 없음
|
|
707
|
+
where: {
|
|
708
|
+
posts: { none: { status: 'DRAFT' } },
|
|
709
|
+
}
|
|
710
|
+
```
|
|
711
|
+
|
|
712
|
+
### 관계 카운트
|
|
713
|
+
|
|
714
|
+
```typescript
|
|
715
|
+
const users = await prisma.user.findMany({
|
|
716
|
+
include: {
|
|
717
|
+
_count: {
|
|
718
|
+
select: { posts: true },
|
|
719
|
+
},
|
|
720
|
+
},
|
|
721
|
+
})
|
|
722
|
+
// 결과: { ..., _count: { posts: 5 } }
|
|
723
|
+
```
|
|
724
|
+
|
|
725
|
+
### 중첩 수정/삭제
|
|
726
|
+
|
|
727
|
+
```typescript
|
|
728
|
+
// 중첩 수정
|
|
729
|
+
await prisma.user.update({
|
|
730
|
+
where: { id: 1 },
|
|
731
|
+
data: {
|
|
732
|
+
posts: {
|
|
733
|
+
updateMany: {
|
|
734
|
+
where: { status: 'DRAFT' },
|
|
735
|
+
data: { status: 'PUBLISHED' },
|
|
736
|
+
},
|
|
737
|
+
},
|
|
738
|
+
},
|
|
739
|
+
})
|
|
740
|
+
|
|
741
|
+
// 중첩 삭제
|
|
742
|
+
await prisma.user.update({
|
|
743
|
+
where: { id: 1 },
|
|
744
|
+
data: {
|
|
745
|
+
posts: {
|
|
746
|
+
deleteMany: { where: { status: 'DRAFT' } },
|
|
747
|
+
},
|
|
748
|
+
},
|
|
749
|
+
})
|
|
750
|
+
```
|
|
751
|
+
|
|
752
|
+
</relations>
|
|
753
|
+
|
|
754
|
+
---
|
|
755
|
+
|
|
756
|
+
<transactions>
|
|
757
|
+
|
|
758
|
+
## Transactions
|
|
759
|
+
|
|
760
|
+
### 배열 기반 트랜잭션
|
|
761
|
+
|
|
762
|
+
하나라도 실패하면 전체 롤백.
|
|
763
|
+
|
|
764
|
+
```typescript
|
|
765
|
+
const deletePosts = prisma.post.deleteMany({ where: { authorId: 7 } })
|
|
766
|
+
const deleteUser = prisma.user.delete({ where: { id: 7 } })
|
|
767
|
+
|
|
768
|
+
await prisma.$transaction([deletePosts, deleteUser])
|
|
769
|
+
```
|
|
770
|
+
|
|
771
|
+
### 인터랙티브 트랜잭션
|
|
772
|
+
|
|
773
|
+
복잡한 로직, 조건부 처리.
|
|
774
|
+
|
|
775
|
+
```typescript
|
|
776
|
+
const result = await prisma.$transaction(async (tx) => {
|
|
777
|
+
// 1. 잔액 확인
|
|
778
|
+
const sender = await tx.account.findUnique({ where: { id: senderId } })
|
|
779
|
+
if (!sender || sender.balance < amount) {
|
|
780
|
+
throw new Error('Insufficient balance')
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
// 2. 송금자 차감
|
|
784
|
+
await tx.account.update({
|
|
785
|
+
where: { id: senderId },
|
|
786
|
+
data: { balance: { decrement: amount } },
|
|
787
|
+
})
|
|
788
|
+
|
|
789
|
+
// 3. 수신자 증가
|
|
790
|
+
await tx.account.update({
|
|
791
|
+
where: { id: recipientId },
|
|
792
|
+
data: { balance: { increment: amount } },
|
|
793
|
+
})
|
|
794
|
+
|
|
795
|
+
return { success: true, amount }
|
|
796
|
+
})
|
|
797
|
+
```
|
|
798
|
+
|
|
799
|
+
### 트랜잭션 옵션
|
|
800
|
+
|
|
801
|
+
```typescript
|
|
802
|
+
await prisma.$transaction(async (tx) => {
|
|
803
|
+
// 트랜잭션 로직
|
|
804
|
+
}, {
|
|
805
|
+
maxWait: 5000, // 최대 대기 시간 (ms)
|
|
806
|
+
timeout: 10000, // 타임아웃 (ms)
|
|
807
|
+
isolationLevel: 'Serializable', // 격리 수준
|
|
808
|
+
})
|
|
809
|
+
```
|
|
810
|
+
|
|
811
|
+
| Isolation Level | 설명 |
|
|
812
|
+
|----------------|------|
|
|
813
|
+
| `ReadUncommitted` | 가장 낮은 수준 (Dirty Read 가능) |
|
|
814
|
+
| `ReadCommitted` | 커밋된 데이터만 읽기 |
|
|
815
|
+
| `RepeatableRead` | 반복 읽기 보장 |
|
|
816
|
+
| `Serializable` | 가장 높은 수준 (직렬화) |
|
|
817
|
+
|
|
818
|
+
### 에러 처리
|
|
819
|
+
|
|
820
|
+
```typescript
|
|
821
|
+
try {
|
|
822
|
+
await prisma.$transaction(async (tx) => {
|
|
823
|
+
await tx.user.create({ data: { email: 'test@example.com' } })
|
|
824
|
+
|
|
825
|
+
if (someCondition) {
|
|
826
|
+
throw new Error('Rollback condition')
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
await tx.post.create({ data: { title: 'Post' } })
|
|
830
|
+
})
|
|
831
|
+
} catch (error) {
|
|
832
|
+
console.error('Transaction rolled back:', error)
|
|
833
|
+
// 전체 작업 롤백됨
|
|
834
|
+
}
|
|
835
|
+
```
|
|
836
|
+
|
|
837
|
+
</transactions>
|
|
838
|
+
|
|
839
|
+
---
|
|
840
|
+
|
|
841
|
+
<cloudflare_d1>
|
|
842
|
+
|
|
843
|
+
## Cloudflare D1
|
|
844
|
+
|
|
845
|
+
SQLite 기반 서버리스 데이터베이스.
|
|
846
|
+
|
|
847
|
+
**Limitations:**
|
|
848
|
+
- ❌ 트랜잭션 미지원
|
|
849
|
+
- ❌ `prisma migrate` 불가 (wrangler 사용)
|
|
850
|
+
- ⚠️ Preview 상태
|
|
851
|
+
|
|
852
|
+
### 설정
|
|
853
|
+
|
|
854
|
+
```prisma
|
|
855
|
+
// +base.prisma
|
|
856
|
+
generator client {
|
|
857
|
+
provider = "prisma-client"
|
|
858
|
+
output = "../../src/generated/prisma"
|
|
859
|
+
runtime = "cloudflare" // 필수!
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
datasource db {
|
|
863
|
+
provider = "sqlite"
|
|
864
|
+
}
|
|
865
|
+
```
|
|
866
|
+
|
|
867
|
+
```jsonc
|
|
868
|
+
// wrangler.jsonc
|
|
869
|
+
{
|
|
870
|
+
"d1_databases": [
|
|
871
|
+
{
|
|
872
|
+
"binding": "DB",
|
|
873
|
+
"database_name": "my-db",
|
|
874
|
+
"database_id": "your-database-id"
|
|
875
|
+
}
|
|
876
|
+
]
|
|
877
|
+
}
|
|
878
|
+
```
|
|
879
|
+
|
|
880
|
+
### 사용법
|
|
55
881
|
|
|
56
882
|
```typescript
|
|
57
|
-
// lib/prisma.ts
|
|
58
883
|
import { PrismaClient } from './generated/prisma'
|
|
884
|
+
import { PrismaD1 } from '@prisma/adapter-d1'
|
|
885
|
+
|
|
886
|
+
export interface Env {
|
|
887
|
+
DB: D1Database
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
export default {
|
|
891
|
+
async fetch(request: Request, env: Env): Promise<Response> {
|
|
892
|
+
const adapter = new PrismaD1(env.DB)
|
|
893
|
+
const prisma = new PrismaClient({ adapter })
|
|
894
|
+
|
|
895
|
+
const users = await prisma.user.findMany()
|
|
896
|
+
|
|
897
|
+
return new Response(JSON.stringify(users), {
|
|
898
|
+
headers: { 'Content-Type': 'application/json' },
|
|
899
|
+
})
|
|
900
|
+
},
|
|
901
|
+
}
|
|
902
|
+
```
|
|
903
|
+
|
|
904
|
+
### 마이그레이션
|
|
905
|
+
|
|
906
|
+
```bash
|
|
907
|
+
# 1. D1 데이터베이스 생성
|
|
908
|
+
npx wrangler d1 create my-database
|
|
909
|
+
|
|
910
|
+
# 2. 마이그레이션 생성
|
|
911
|
+
npx wrangler d1 migrations create my-database init
|
|
912
|
+
|
|
913
|
+
# 3. SQL 생성 (초기)
|
|
914
|
+
npx prisma migrate diff \
|
|
915
|
+
--from-empty \
|
|
916
|
+
--to-schema-datamodel prisma/schema.prisma \
|
|
917
|
+
--script \
|
|
918
|
+
--output prisma/migrations/0001.sql
|
|
59
919
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
920
|
+
# 4. SQL 생성 (후속)
|
|
921
|
+
npx prisma migrate diff \
|
|
922
|
+
--from-local-d1 \
|
|
923
|
+
--to-schema-datamodel prisma/schema.prisma \
|
|
924
|
+
--script
|
|
925
|
+
|
|
926
|
+
# 5. 마이그레이션 적용
|
|
927
|
+
npx wrangler d1 migrations apply my-database --local # 로컬
|
|
928
|
+
npx wrangler d1 migrations apply my-database --remote # 프로덕션
|
|
929
|
+
|
|
930
|
+
# 6. Client 생성
|
|
931
|
+
npx prisma generate
|
|
63
932
|
```
|
|
64
933
|
|
|
65
|
-
|
|
934
|
+
### 비교
|
|
935
|
+
|
|
936
|
+
| 항목 | 일반 SQLite | Cloudflare D1 |
|
|
937
|
+
|------|-------------|---------------|
|
|
938
|
+
| **마이그레이션** | ✅ `prisma migrate` | ❌ `wrangler d1` |
|
|
939
|
+
| **트랜잭션** | ✅ 지원 | ❌ 미지원 |
|
|
940
|
+
| **접속** | ✅ 직접 연결 | HTTP 어댑터 |
|
|
941
|
+
| **환경** | 로컬/서버 | Cloudflare Workers |
|
|
942
|
+
|
|
943
|
+
</cloudflare_d1>
|
|
944
|
+
|
|
945
|
+
---
|
|
946
|
+
|
|
947
|
+
<commands>
|
|
948
|
+
|
|
949
|
+
## CLI Commands
|
|
66
950
|
|
|
67
951
|
```bash
|
|
68
|
-
|
|
69
|
-
npx prisma
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
npx prisma
|
|
952
|
+
# 초기화
|
|
953
|
+
npx prisma init
|
|
954
|
+
|
|
955
|
+
# Client 생성
|
|
956
|
+
npx prisma generate
|
|
957
|
+
|
|
958
|
+
# 마이그레이션
|
|
959
|
+
npx prisma migrate dev --name init # 개발 (스키마 변경 + 마이그레이션)
|
|
960
|
+
npx prisma migrate deploy # 프로덕션 (마이그레이션 적용)
|
|
961
|
+
npx prisma migrate reset # DB 초기화
|
|
962
|
+
|
|
963
|
+
# 스키마 동기화 (개발용)
|
|
964
|
+
npx prisma db push
|
|
965
|
+
|
|
966
|
+
# 시드
|
|
967
|
+
npx prisma db seed
|
|
968
|
+
|
|
969
|
+
# GUI
|
|
970
|
+
npx prisma studio
|
|
971
|
+
|
|
972
|
+
# 포맷팅
|
|
973
|
+
npx prisma format
|
|
974
|
+
|
|
975
|
+
# 유효성 검사
|
|
976
|
+
npx prisma validate
|
|
977
|
+
```
|
|
978
|
+
|
|
979
|
+
</commands>
|
|
980
|
+
|
|
981
|
+
---
|
|
982
|
+
|
|
983
|
+
<dos_and_donts>
|
|
984
|
+
|
|
985
|
+
## Do's & Don'ts
|
|
986
|
+
|
|
987
|
+
### ✅ Do
|
|
988
|
+
|
|
989
|
+
| 상황 | 방법 |
|
|
990
|
+
|------|------|
|
|
991
|
+
| **구조** | Multi-File 스키마 사용 (`prisma/schema/`) |
|
|
992
|
+
| **주석** | 모든 모델/필드/enum에 한글 주석 필수 |
|
|
993
|
+
| **Provider** | v7: `generator client { provider = "prisma-client" }` |
|
|
994
|
+
| **Import** | `import { PrismaClient } from './generated/prisma'` |
|
|
995
|
+
| **트랜잭션** | 복잡한 로직 → 인터랙티브 트랜잭션 사용 |
|
|
996
|
+
| **관계** | `include`로 관계 데이터 로드 |
|
|
997
|
+
| **인덱스** | 자주 조회하는 필드에 `@@index` 추가 |
|
|
998
|
+
|
|
999
|
+
### ❌ Don't
|
|
1000
|
+
|
|
1001
|
+
| 상황 | 이유 |
|
|
1002
|
+
|------|------|
|
|
1003
|
+
| **Single File** | ❌ `schema.prisma` 단일 파일 사용 금지 |
|
|
1004
|
+
| **v6 Provider** | ❌ `prisma-client-js` 사용 금지 (v7) |
|
|
1005
|
+
| **Import** | ❌ `@prisma/client` import 금지 (v7) |
|
|
1006
|
+
| **자동 실행** | ❌ `prisma migrate/db push` 자동 실행 금지 |
|
|
1007
|
+
| **N+1** | ❌ 루프에서 개별 쿼리 실행 → `include`/`findMany` 사용 |
|
|
1008
|
+
| **Raw Query** | ❌ 타입 안전성 없음 → Prisma Query API 우선 |
|
|
1009
|
+
|
|
1010
|
+
### N+1 Problem 예방
|
|
1011
|
+
|
|
1012
|
+
```typescript
|
|
1013
|
+
// ❌ N+1 Problem
|
|
1014
|
+
const users = await prisma.user.findMany()
|
|
1015
|
+
for (const user of users) {
|
|
1016
|
+
const posts = await prisma.post.findMany({ where: { authorId: user.id } })
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
// ✅ include 사용
|
|
1020
|
+
const users = await prisma.user.findMany({
|
|
1021
|
+
include: { posts: true },
|
|
1022
|
+
})
|
|
73
1023
|
```
|
|
1024
|
+
|
|
1025
|
+
</dos_and_donts>
|
|
1026
|
+
|
|
1027
|
+
---
|
|
1028
|
+
|
|
1029
|
+
<quick_reference>
|
|
1030
|
+
|
|
1031
|
+
## Quick Reference
|
|
1032
|
+
|
|
1033
|
+
```typescript
|
|
1034
|
+
// Client 초기화
|
|
1035
|
+
import { PrismaClient } from './generated/prisma'
|
|
1036
|
+
export const prisma = new PrismaClient()
|
|
1037
|
+
|
|
1038
|
+
// CRUD
|
|
1039
|
+
const users = await prisma.user.findMany()
|
|
1040
|
+
const user = await prisma.user.create({ data: { email, name } })
|
|
1041
|
+
const updated = await prisma.user.update({ where: { id }, data: { name } })
|
|
1042
|
+
const deleted = await prisma.user.delete({ where: { id } })
|
|
1043
|
+
|
|
1044
|
+
// 관계 포함
|
|
1045
|
+
const userWithPosts = await prisma.user.findUnique({
|
|
1046
|
+
where: { id },
|
|
1047
|
+
include: { posts: true },
|
|
1048
|
+
})
|
|
1049
|
+
|
|
1050
|
+
// 트랜잭션
|
|
1051
|
+
await prisma.$transaction([
|
|
1052
|
+
prisma.user.create({ data: { email: 'a@example.com' } }),
|
|
1053
|
+
prisma.post.create({ data: { title: 'Post' } }),
|
|
1054
|
+
])
|
|
1055
|
+
```
|
|
1056
|
+
|
|
1057
|
+
</quick_reference>
|