Prisma ORM 완벽 가이드 2025: 스키마부터 고급 쿼리까지
Prisma ORM의 기초부터 고급 기능까지 완벽하게 알아봅니다. 스키마 설계, CRUD 작업, 관계 처리, 트랜잭션, 마이그레이션, 성능 최적화를 다룹니다.
Spacebar AI
2025년 12월 7일
13분

Prisma ORM 완벽 가이드 2025
Prisma란?
Prisma는 Node.js와 TypeScript를 위한 차세대 ORM입니다. 타입 안전성, 자동 완성, 직관적인 API를 제공합니다.
시작하기
설치
npm install prisma @prisma/client
npx prisma init
기본 스키마
// prisma/schema.prisma
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql" // mysql, sqlite, sqlserver, mongodb
url = env("DATABASE_URL")
}
model User {
id String @id @default(cuid())
email String @unique
name String?
role Role @default(USER)
posts Post[]
profile Profile?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model Post {
id String @id @default(cuid())
title String
content String? @db.Text
published Boolean @default(false)
author User @relation(fields: [authorId], references: [id])
authorId String
tags Tag[]
createdAt DateTime @default(now())
}
model Profile {
id String @id @default(cuid())
bio String?
user User @relation(fields: [userId], references: [id])
userId String @unique
}
model Tag {
id String @id @default(cuid())
name String @unique
posts Post[]
}
enum Role {
USER
ADMIN
MODERATOR
}
마이그레이션
# 개발 환경
npx prisma migrate dev --name init
# 프로덕션 환경
npx prisma migrate deploy
# 스키마만 동기화 (마이그레이션 없이)
npx prisma db push
CRUD 작업
Create
import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient()
// 단일 생성
const user = await prisma.user.create({
data: {
email: 'alice@example.com',
name: 'Alice',
profile: {
create: { bio: 'Hello!' } // 관계 함께 생성
}
}
})
// 여러 개 생성
const users = await prisma.user.createMany({
data: [
{ email: 'bob@example.com', name: 'Bob' },
{ email: 'charlie@example.com', name: 'Charlie' }
],
skipDuplicates: true
})
Read
// 단일 조회
const user = await prisma.user.findUnique({
where: { email: 'alice@example.com' }
})
// 조건부 조회
const user = await prisma.user.findFirst({
where: { name: { contains: 'Ali' } }
})
// 목록 조회
const users = await prisma.user.findMany({
where: {
role: 'ADMIN',
createdAt: {
gte: new Date('2024-01-01')
}
},
orderBy: { createdAt: 'desc' },
skip: 0,
take: 10,
include: {
posts: true,
profile: true
}
})
Update
// 단일 업데이트
const user = await prisma.user.update({
where: { id: 'user-id' },
data: { name: 'New Name' }
})
// 여러 개 업데이트
const users = await prisma.user.updateMany({
where: { role: 'USER' },
data: { role: 'MODERATOR' }
})
// Upsert (있으면 업데이트, 없으면 생성)
const user = await prisma.user.upsert({
where: { email: 'alice@example.com' },
update: { name: 'Updated Alice' },
create: { email: 'alice@example.com', name: 'Alice' }
})
Delete
// 단일 삭제
const user = await prisma.user.delete({
where: { id: 'user-id' }
})
// 여러 개 삭제
const users = await prisma.user.deleteMany({
where: { role: 'USER' }
})
고급 쿼리
필터링
const posts = await prisma.post.findMany({
where: {
AND: [
{ published: true },
{ title: { contains: 'Prisma', mode: 'insensitive' } }
],
OR: [
{ authorId: 'user-1' },
{ authorId: 'user-2' }
],
NOT: {
title: { startsWith: '[Draft]' }
}
}
})
관계 필터링
// 특정 조건의 포스트를 가진 사용자
const users = await prisma.user.findMany({
where: {
posts: {
some: { published: true } // 하나라도 있으면
// every: { published: true } // 모두 만족
// none: { published: true } // 하나도 없으면
}
}
})
Select와 Include
// 특정 필드만 선택
const user = await prisma.user.findUnique({
where: { id: 'user-id' },
select: {
id: true,
email: true,
posts: {
select: { title: true }
}
}
})
// 관계 포함
const user = await prisma.user.findUnique({
where: { id: 'user-id' },
include: {
posts: {
where: { published: true },
orderBy: { createdAt: 'desc' },
take: 5
},
profile: true
}
})
집계 함수
// Count
const count = await prisma.user.count({
where: { role: 'ADMIN' }
})
// Aggregate
const stats = await prisma.post.aggregate({
_count: true,
_avg: { views: true },
_max: { views: true },
_min: { views: true }
})
// Group By
const postsByAuthor = await prisma.post.groupBy({
by: ['authorId'],
_count: { id: true },
_sum: { views: true },
having: {
views: { _sum: { gt: 100 } }
}
})
트랜잭션
Sequential 트랜잭션
const [user, post] = await prisma.$transaction([
prisma.user.create({ data: { email: 'new@example.com' } }),
prisma.post.create({ data: { title: 'New Post', authorId: 'user-id' } })
])
Interactive 트랜잭션
await prisma.$transaction(async (tx) => {
const user = await tx.user.findUnique({
where: { id: 'user-id' }
})
if (!user) {
throw new Error('User not found')
}
await tx.post.create({
data: {
title: 'New Post',
authorId: user.id
}
})
}, {
maxWait: 5000, // 최대 대기 시간
timeout: 10000 // 최대 실행 시간
})
성능 최적화
Connection Pool
const prisma = new PrismaClient({
datasources: {
db: {
url: process.env.DATABASE_URL
}
},
log: ['query', 'info', 'warn', 'error']
})
N+1 문제 해결
// 문제: N+1 쿼리 발생
const users = await prisma.user.findMany()
for (const user of users) {
const posts = await prisma.post.findMany({
where: { authorId: user.id }
})
}
// 해결: include 사용
const users = await prisma.user.findMany({
include: { posts: true }
})
Raw Query
// 복잡한 쿼리는 Raw SQL 사용
const result = await prisma.$queryRaw`
SELECT u.*, COUNT(p.id) as post_count
FROM "User" u
LEFT JOIN "Post" p ON p."authorId" = u.id
GROUP BY u.id
HAVING COUNT(p.id) > 5
`
Next.js에서 사용
싱글톤 패턴
// lib/prisma.ts
import { PrismaClient } from '@prisma/client'
const globalForPrisma = globalThis as unknown as {
prisma: PrismaClient | undefined
}
export const prisma = globalForPrisma.prisma ?? new PrismaClient()
if (process.env.NODE_ENV !== 'production') {
globalForPrisma.prisma = prisma
}
결론
Prisma는 타입 안전성과 개발자 경험을 모두 만족시키는 현대적인 ORM입니다. 직관적인 API와 강력한 마이그레이션 도구로 데이터베이스 작업을 크게 단순화할 수 있습니다.