Tan Kim

Prisma

TypeScript/Node.js용 ORM. 스키마 파일로 DB 구조를 정의하고, 타입 안전한 쿼리 클라이언트를 자동 생성한다.

구성 요소

구성 역할
prisma/schema.prisma 데이터 모델 정의
prisma/migrations/ 마이그레이션 파일
@prisma/client 자동 생성되는 쿼리 클라이언트

스키마 작성

// prisma/schema.prisma
 
datasource db {
  provider = "mysql"       // mysql | postgresql | sqlite
  url      = env("DATABASE_URL")
}
 
generator client {
  provider = "prisma-client-js"
}
 
model User {
  id        Int      @id @default(autoincrement())
  email     String   @unique
  name      String
  createdAt DateTime @default(now())
  orders    Order[]
}
 
model Order {
  id        Int      @id @default(autoincrement())
  total     Int
  status    String   @default("pending")
  userId    Int
  user      User     @relation(fields: [userId], references: [id])
  createdAt DateTime @default(now())
}

CLI 명령어

npx prisma init                   # 초기화 (schema.prisma 생성)
npx prisma generate               # Client 재생성 (스키마 변경 후)
npx prisma migrate dev            # 마이그레이션 생성 + 적용 (개발)
npx prisma migrate deploy         # 마이그레이션 적용 (프로덕션)
npx prisma migrate reset          # DB 초기화 + 마이그레이션 재적용
npx prisma db push                # 마이그레이션 없이 스키마 동기화 (프로토타이핑)
npx prisma db seed                # 시드 데이터 삽입
npx prisma studio                 # GUI 데이터 브라우저 (localhost:5555)

쿼리 예시

import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient()
 
// 단건 조회
const user = await prisma.user.findUnique({ where: { id: 1 } })
 
// 목록 조회
const users = await prisma.user.findMany({
  where: { name: { contains: '김' } },
  orderBy: { createdAt: 'desc' },
  take: 10,
  skip: 0,
})
 
// 관계 포함 조회
const userWithOrders = await prisma.user.findUnique({
  where: { id: 1 },
  include: { orders: true },
})
 
// 생성
const newUser = await prisma.user.create({
  data: { email: 'test@example.com', name: '김탄' },
})
 
// 수정
await prisma.user.update({
  where: { id: 1 },
  data: { name: '이름변경' },
})
 
// 삭제
await prisma.user.delete({ where: { id: 1 } })
 
// 집계
const count = await prisma.user.count()
const stats = await prisma.order.aggregate({
  _sum: { total: true },
  _avg: { total: true },
  where: { status: 'paid' },
})

트랜잭션

// 순차 실행 (각 쿼리가 독립적)
await prisma.$transaction([
  prisma.user.update({ where: { id: 1 }, data: { name: 'A' } }),
  prisma.order.create({ data: { total: 1000, userId: 1 } }),
])
 
// 인터랙티브 트랜잭션 (이전 결과 참조 가능)
await prisma.$transaction(async (tx) => {
  const user = await tx.user.findUnique({ where: { id: 1 } })
  await tx.order.create({ data: { total: 1000, userId: user.id } })
})

.env 설정

# 기본
DATABASE_URL="mysql://user:password@localhost:3306/dbname"
 
# TCP 연결 강제 (Unix socket 우회) — 아래 참고
DATABASE_URL="mysql://user:password@127.0.0.1:3306/dbname?prefer_socket=false"

localhost vs 127.0.0.1 (Unix Socket 이슈)

mysql2는 host=localhost 지정 시 TCP 대신 Unix Domain Socket(/var/run/mysqld/mysqld.sock)으로 연결을 시도한다. IP 서버에 연결해야 하는데 로컬로 연결되거나 소켓 파일을 찾지 못해 실패하는 경우 두 가지 방법 중 하나로 해결한다.

방법 예시
prefer_socket=false 추가 mysql://user:pass@localhost:3306/db?prefer_socket=false
host를 127.0.0.1로 변경 mysql://user:pass@127.0.0.1:3306/db

Unix socket을 의도적으로 사용하는 경우, ?socket=에 소켓 파일 전체 경로를 지정한다 (지정 시 host/port는 무시됨).

DATABASE_URL="mysql://user:pass@localhost/dbname?socket=/var/run/mysqld/mysqld.sock"

메모