Skip to content

Prisma 입문 가이드

Prisma는 Next-generation Node.js & TypeScript ORM이야. 기존의 ORM들과 달리 Type-safe하고 Auto-completion이 완벽하게 지원되는 게 특징이지.

Prisma

Prisma Client

Prisma Migrate

Prisma Studio

Prisma Schema

Type-safe Database Client

Database Migration Tool

Visual Database Browser

Database Schema Definition

전통적인 방식과 비교해보면 차이가 확실해:

// SQL 직접 작성 - Error-prone!
const users = await db.query(
'SELECT * FROM users WHERE age > $1 AND active = $2',
[18, true]
);
Terminal window
# 새 프로젝트 생성
mkdir prisma-tutorial && cd prisma-tutorial
npm init -y
# TypeScript 설정 (선택사항이지만 강력 추천!)
npm install -D typescript ts-node @types/node
npx tsc --init
# Prisma 설치 (prisma CLI는 dev dependency, client는 production dependency)
npm install -D prisma
npm install @prisma/client
Terminal window
npx prisma init

이 명령어가 생성하는 것들:

  • prisma/schema.prisma - 데이터베이스 스키마 정의 파일
  • .env - 환경 변수 파일 (DATABASE_URL 포함)
  • .gitignore - Git에서 .env 파일 제외

prisma/schema.prisma 파일에서 데이터베이스 구조를 정의해보자:

// 데이터베이스 연결 설정
datasource db {
provider = "postgresql" // mysql, sqlite, mongodb도 가능!
url = env("DATABASE_URL")
}
// Prisma Client 생성 설정
generator client {
provider = "prisma-client-js"
}
// User 모델 정의
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
role Role @default(USER)
posts Post[] // 1:N relation
profile Profile? // 1:1 relation
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
// Post 모델
model Post {
id Int @id @default(autoincrement())
title String
content String?
published Boolean @default(false)
author User @relation(fields: [authorId], references: [id])
authorId Int
categories Category[] // M:N relation
createdAt DateTime @default(now())
}
// Category 모델
model Category {
id Int @id @default(autoincrement())
name String @unique
posts Post[]
}
// Profile 모델
model Profile {
id Int @id @default(autoincrement())
bio String
user User @relation(fields: [userId], references: [id])
userId Int @unique
}
// Enum 정의
enum Role {
USER
ADMIN
}

writes

has

belongs_to

USER

int

id

PK

string

email

UK

string

name

Role

role

datetime

createdAt

datetime

updatedAt

POST

int

id

PK

string

title

string

content

boolean

published

int

authorId

FK

datetime

createdAt

PROFILE

int

id

PK

string

bio

int

userId

FK

CATEGORY

int

id

PK

string

name

UK

Schema를 정의했으니 이제 실제 데이터베이스에 반영해보자:

Terminal window
# 개발 환경에서 마이그레이션 생성 및 적용
npx prisma migrate dev --name init
# Production 환경에서는
npx prisma migrate deploy
import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient()
async function main() {
// CREATE - 새 사용자 생성
const user = await prisma.user.create({
data: {
email: 'alice@prisma.io',
name: 'Alice',
posts: {
create: {
title: 'Hello World',
content: 'My first post!'
}
}
},
include: {
posts: true
}
})
console.log(user)
// READ - 모든 사용자 조회
const users = await prisma.user.findMany({
where: {
email: {
contains: 'prisma.io'
}
},
include: {
posts: {
where: {
published: true
}
}
}
})
// UPDATE - 사용자 정보 수정
const updatedUser = await prisma.user.update({
where: {
email: 'alice@prisma.io'
},
data: {
name: 'Alice Kim'
}
})
// DELETE - 사용자 삭제
const deletedUser = await prisma.user.delete({
where: {
email: 'alice@prisma.io'
}
})
}
main()
.catch((e) => {
throw e
})
.finally(async () => {
await prisma.$disconnect()
})
// Aggregation - 집계 함수
const userCount = await prisma.user.count({
where: {
posts: {
some: {
published: true
}
}
}
})
// Grouping - 그룹화
const groupedPosts = await prisma.post.groupBy({
by: ['published'],
_count: {
_all: true
}
})
// Transaction - 트랜잭션
const [user, post] = await prisma.$transaction([
prisma.user.create({
data: { email: 'bob@prisma.io', name: 'Bob' }
}),
prisma.post.create({
data: {
title: 'Join the Prisma Community',
authorId: 1
}
})
])
// Raw SQL - 필요할 때 직접 SQL 실행
const result = await prisma.$queryRaw`
SELECT * FROM "User"
WHERE email = ${email}
`

Prisma Studio는 데이터베이스를 시각적으로 탐색하고 편집할 수 있는 GUI 도구야:

Terminal window
npx prisma studio

브라우저에서 http://localhost:5555로 접속하면 다음과 같은 기능을 사용할 수 있어:

  • 데이터 조회 및 필터링
  • 데이터 추가, 수정, 삭제
  • 관계 탐색
  • 데이터 내보내기

Prisma는 Schema를 기반으로 TypeScript 타입을 자동 생성해줘:

import { User, Post, Prisma } from '@prisma/client'
// Prisma가 생성한 타입 활용
type UserWithPosts = Prisma.UserGetPayload<{
include: { posts: true }
}>
function displayUser(user: UserWithPosts) {
console.log(`${user.name} has ${user.posts.length} posts`)
}

참고: Prisma 4.16.0 이전에 사용되던 prisma.$use() 미들웨어 API는 deprecated 되었다. 현재 권장되는 방식은 $extends()를 사용한 Client Extensions이다.

// Client Extensions를 이용한 쿼리 로깅
const prisma = new PrismaClient().$extends({
query: {
$allModels: {
async $allOperations({ model, operation, args, query }) {
const before = Date.now()
const result = await query(args)
const after = Date.now()
console.log(`Query ${model}.${operation} took ${after - before}ms`)
return result
},
},
},
})
// Soft Delete 구현
const xprisma = prisma.$extends({
query: {
user: {
async delete({ args, query }) {
return prisma.user.update({
where: args.where,
data: { deletedAt: new Date() },
})
},
},
},
})

Production 환경에서는 connection pool 설정이 중요해:

DATABASE_URL="postgresql://user:password@localhost:5432/mydb?connection_limit=5"

개발 환경에서 테스트 데이터를 자동으로 생성하려면:

prisma/seed.ts
import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient()
async function main() {
const alice = await prisma.user.upsert({
where: { email: 'alice@prisma.io' },
update: {},
create: {
email: 'alice@prisma.io',
name: 'Alice',
posts: {
create: [
{
title: 'Check out Prisma with Next.js',
content: 'https://www.prisma.io/nextjs',
published: true,
},
{
title: 'Follow Prisma on Twitter',
content: 'https://twitter.com/prisma',
published: true,
},
],
},
},
})
console.log({ alice })
}
main()
.catch((e) => {
console.error(e)
process.exit(1)
})
.finally(async () => {
await prisma.$disconnect()
})

package.json에 seed 스크립트 추가:

{
"prisma": {
"seed": "ts-node prisma/seed.ts"
}
}

이제 Prisma의 기본을 마스터했어! 다음 단계로는:

  1. Prisma with GraphQL - Nexus나 TypeGraphQL과 함께 사용하기
  2. Prisma with REST API - Express나 Fastify와 통합하기
  3. Advanced Patterns - Repository 패턴, Unit of Work 구현하기
  4. Performance Optimization - Query 최적화, N+1 문제 해결하기