스페이스바AI
블로그문서강의가격
스페이스바AI

AI를 제대로 활용하는 실전 가이드

(주)스페이스바 | 대표: 김정우

서비스

  • 블로그
  • 문서
  • 강의
  • 가격

법적 고지

  • 이용약관
  • 개인정보처리방침

© 2025 (주)스페이스바. All rights reserved.

모든 글 보기
Web Development

Prisma ORM 완벽 가이드 2025: 스키마부터 고급 쿼리까지

Prisma ORM의 기초부터 고급 기능까지 완벽하게 알아봅니다. 스키마 설계, CRUD 작업, 관계 처리, 트랜잭션, 마이그레이션, 성능 최적화를 다룹니다.

Spacebar AI
2025년 12월 7일
13분
#Prisma
#ORM
#데이터베이스
#TypeScript
#Node.js
#PostgreSQL
Prisma ORM 완벽 가이드 2025: 스키마부터 고급 쿼리까지

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와 강력한 마이그레이션 도구로 데이터베이스 작업을 크게 단순화할 수 있습니다.