본문으로 건너뛰기

Cursor에서 Prisma ORM 개발: 완전한 가이드

Prisma development in Cursor

Prisma는 타입 안전 데이터베이스 클라이언트, 선언적 스키마 마이그레이션 및 Prisma Studio라는 시각적 데이터베이스 브라우저를 제공하는 차세대 TypeScript ORM입니다. Prisma는 상용구 데이터베이스 액세스 코드를 제거하고 컴파일 타임에 쿼리 오류를 잡습니다. Cursor는 통합 AI 어시스턴트와 VS Code 호환 기반을 통해 Prisma 개발에 탁월한 편집기입니다 -- Prisma 스키마 언어에 대한 IntelliSense를 제공하고, Prisma Client 쿼리를 자동 완성하며, 자연어 설명에서 완전한 스키마를 생성할 수 있습니다.

이 가이드는 Cursor에서의 완전한 Prisma 개발 워크플로우를 다룹니다: 설치 및 설정, 스키마 설계, 마이그레이션, Prisma Client 쿼리, 관계, Prisma Studio 및 AI 지원 스키마 생성.

Cursor에서 Prisma 설치 및 설정​

사전 요구 사항​

Prisma는 Node.js 18+에서 작동하며 PostgreSQL, MySQL, SQLite, MongoDB 및 SQL Server를 지원합니다. Node.js 버전을 확인하세요:

node --version
팁

Cursor의 통합 터미널(Ctrl+`)은 Explorer 패널과 동기화 상태를 유지합니다. Prisma CLI 명령을 실행하고 생성된 파일이 파일 트리에 즉시 나타나는 것을 볼 수 있습니다.

Prisma 확장 설치​

Cursor는 VS Code 확장 호환성을 상속합니다. 확장 보기(Ctrl+Shift+X)에서 공식 Prisma 확장을 설치하세요:

  1. "Prisma"를 검색합니다.
  2. Prisma가 게시한 확장을 설치합니다.
  3. 메시지가 표시되면 Cursor를 다시 로드합니다.

이 확장은 다음을 제공합니다:

  • .prisma 스키마 파일에 대한 구문 강조.
  • 저장 시 자동 포맷팅.
  • 모델 필드, 속성 및 관계에 대한 IntelliSense.
  • 스키마 오류에 대한 인라인 검증.
  • 빠른 수정을 위한 코드 액션.
경고

Prisma 확장이 설치되어 있지 않으면 Cursor는 .prisma 파일을 일반 텍스트로 처리합니다. 구문 강조, 자동 완성 및 오류 감지를 잃게 됩니다. 스키마 파일을 편집하기 전에 항상 공식 확장을 설치하세요.

프로젝트에서 Prisma 초기화​

Prisma CLI를 설치하고 새 Prisma 설정을 초기화합니다:

npm install prisma --save-dev
npm install @prisma/client

npx prisma init

이것은 다음을 생성합니다:

  • prisma/schema.prisma -- 메인 스키마 파일.
  • .env -- DATABASE_URL 자리표시자가 있는 환경 파일.

.env에 데이터베이스 연결 문자열을 설정합니다:

# .env
DATABASE_URL="postgresql://user:password@localhost:5432/mydb?schema=public"
팁

Cursor에게 요청하세요: "내 프로젝트에 PostgreSQL로 Prisma를 설정해줘. User 모델이 있는 스키마 파일을 만들고 데이터베이스 연결을 구성해줘." Cursor가 init 명령을 실행하고, 스키마를 생성하며, .env 파일을 설정합니다.

스키마 설계​

Prisma 스키마는 데이터베이스 구조의 유일한 진실 공급원입니다. 모델, 관계, enum 및 데이터 소스 구성을 정의합니다.

스키마 파일 이해​

// prisma/schema.prisma

// Generator: Prisma에 클라이언트 생성 방법을 알림
generator client {
provider = "prisma-client-js"
previewFeatures = ["fullTextSearch", "postgresqlExtensions"]
}

// Datasource: 데이터베이스 연결
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
extensions = [pgcrypto, postgis]
}

// Model: 데이터베이스 테이블에 매핑
model User {
id String @id @default(uuid())
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(uuid())
title String
content String?
published Boolean @default(false)
viewCount Int @default(0)
author User @relation(fields: [authorId], references: [id])
authorId String
categories Category[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt

@@index([authorId])
@@index([published])
}

model Category {
id String @id @default(uuid())
name String @unique
posts Post[]
}

model Profile {
id String @id @default(uuid())
bio String?
user User @relation(fields: [userId], references: [id])
userId String @unique
}

enum Role {
USER
ADMIN
EDITOR
}

일반적인 필드 속성​

속성설명
@id필드를 기본 키로 표시
@default(uuid())삽입 시 UUID 자동 생성
@default(autoincrement())자동 증가 정수 (MySQL/PostgreSQL)
@unique필드에 고유성 적용
@updatedAt레코드 업데이트 시 타임스탬프 자동 업데이트
@relation모델 간의 관계 정의
@map필드를 다른 이름의 열에 매핑
@@index필드(들)에 데이터베이스 인덱스 생성
@@unique복합 고유 제약 조건
@@map모델을 다른 이름의 테이블에 매핑
팁

Cursor에게 요청하세요: "User, Product, Order, OrderItem 및 Category 모델이 있는 이커머스 플랫폼용 Prisma 스키마를 생성해줘. 주문 상태 및 제품 가용성에 대한 적절한 관계, 인덱스 및 enum을 포함해줘." Cursor가 올바른 관계 구문으로 완전하고 검증된 스키마를 생성합니다.

관계​

Prisma는 1:1, 1:n 및 m:n 관계를 지원합니다.

1:1 관계:

model User {
id String @id @default(uuid())
profile Profile?
}

model Profile {
id String @id @default(uuid())
user User @relation(fields: [userId], references: [id])
userId String @unique
}

1:n 관계:

model User {
id String @id @default(uuid())
posts Post[]
}

model Post {
id String @id @default(uuid())
author User @relation(fields: [authorId], references: [id])
authorId String
}

m:n 관계 (암시적):

model Post {
id String @id @default(uuid())
categories Category[]
}

model Category {
id String @id @default(uuid())
posts Post[]
}

m:n 관계 (명시적, 조인 테이블에 추가 필드 포함):

model Post {
id String @id @default(uuid())
tags PostTag[]
}

model Tag {
id String @id @default(uuid())
posts PostTag[]
}

model PostTag {
post Post @relation(fields: [postId], references: [id])
postId String
tag Tag @relation(fields: [tagId], references: [id])
tagId String
assignedAt DateTime @default(now())

@@id([postId, tagId])
}
경고

암시적 m:n 관계를 사용할 때 Prisma는 숨겨진 조인 테이블을 자동으로 생성합니다. 이 숨겨진 테이블에 추가 필드(예: assignedAt 또는 createdBy)를 추가할 수 없습니다. 관계에 메타데이터가 필요한 경우 위와 같이 명시적 조인 테이블을 사용하세요.

마이그레이션​

Prisma Migrate는 버전화되고 되돌릴 수 있는 방식으로 데이터베이스 스키마 변경을 생성하고 적용합니다.

첫 번째 마이그레이션 생성​

스키마를 정의한 후, 초기 마이그레이션을 생성합니다:

npx prisma migrate dev --name init

이 명령은:

  1. prisma/migrations/에 SQL 마이그레이션 파일을 생성합니다.
  2. 마이그레이션을 데이터베이스에 적용합니다.
  3. Prisma Client를 재생성합니다.
팁

설명적인 마이그레이션 이름을 사용하세요: npx prisma migrate dev --name add_order_status_enum. 이 이름은 마이그레이션 기록에 나타나며 스키마 진화를 추적하는 데 도움이 됩니다.

스키마 수정​

스키마를 변경할 때 새 마이그레이션을 생성합니다:

# schema.prisma 편집 후
npx prisma migrate dev --name add_post_view_count

Prisma는 스키마와 데이터베이스 간의 차이를 감지하고, 마이그레이션을 생성하고, 적용합니다.

프로덕션을 위한 마이그레이션 워크플로우​

프로덕션 데이터베이스의 경우, 데이터를 재설정하지 않고 보류 중인 마이그레이션을 적용하는 prisma migrate deploy를 사용합니다:

# 프로덕션에서 마이그레이션 적용
npx prisma migrate deploy
경고

프로덕션 데이터베이스에 prisma migrate dev를 절대 실행하지 마세요. dev 명령은 스키마 드리프트를 감지하면 데이터베이스를 재설정할 수 있습니다. 프로덕션 환경에서는 항상 migrate deploy를 사용하세요.

데이터베이스 재설정​

개발 중에 데이터베이스를 재설정하고 모든 마이그레이션을 다시 적용해야 할 수 있습니다:

npx prisma migrate reset

이것은 모든 데이터와 테이블을 삭제한 다음 모든 마이그레이션을 처음부터 다시 적용합니다.

데이터베이스 시드​

초기 데이터로 데이터베이스를 채우기 위해 시드 스크립트를 생성합니다:

// prisma/seed.ts
import { PrismaClient } from '@prisma/client';

const prisma = new PrismaClient();

async function main() {
// 사용자 생성
const alice = await prisma.user.create({
data: {
name: 'Alice Johnson',
role: 'ADMIN',
profile: {
create: {
bio: 'Software engineer and blogger',
},
},
},
});

const bob = await prisma.user.create({
data: {
name: 'Bob Smith',
role: 'USER',
},
});

// 게시물 생성
const post1 = await prisma.post.create({
data: {
title: 'Getting Started with Prisma',
content: 'Prisma is a type-safe ORM...',
published: true,
author: { connect: { id: alice.id } },
},
});

const post2 = await prisma.post.create({
data: {
title: 'Advanced Prisma Relations',
content: 'Relations in Prisma...',
published: true,
author: { connect: { id: alice.id } },
},
});

console.log('Seed data created successfully');
}

main()
.catch((e) => {
console.error(e);
process.exit(1);
})
.finally(async () => {
await prisma.$disconnect();
});

package.json에 시드 명령을 구성합니다:

{
"prisma": {
"seed": "tsx prisma/seed.ts"
}
}

시드를 실행합니다:

npx prisma db seed
팁

Cursor에게 요청하세요: "5명의 사용자, 20개의 게시물, 10개의 카테고리가 있는 내 Prisma 스키마용 시드 스크립트를 생성해줘. 일부 게시물은 미게시 상태이고 일부 사용자는 프로필이 있고 일부는 없도록 만들어줘." Cursor가 모든 관계 유형을 테스트하는 현실적인 시드 스크립트를 생성합니다.

Prisma Client 쿼리​

Prisma Client는 전통적인 ORM 모델과 원시 SQL을 대체하는 자동 생성된 타입 안전 쿼리 빌더입니다.

클라이언트 인스턴스화​

개발 중 여러 연결을 피하기 위해 싱글톤 인스턴스를 생성합니다:

// lib/prisma.ts
import { PrismaClient } from '@prisma/client';

const globalForPrisma = globalThis as unknown as {
prisma: PrismaClient | undefined;
};

export const prisma =
globalForPrisma.prisma ??
new PrismaClient({
log: ['query', 'info', 'warn', 'error'],
});

if (process.env.NODE_ENV !== 'production') {
globalForPrisma.prisma = prisma;
}
경고

Hot Module Reloading이 있는 개발에서는 파일이 변경될 때마다 새 PrismaClient를 생성하면 연결 풀이 고갈됩니다. 위의 싱글톤 패턴은 globalThis에 인스턴스를 캐시하여 이를 방지합니다.

CRUD 작업​

생성:

// 단일 레코드 생성
const user = await prisma.user.create({
data: {
name: 'Charlie Brown',
profile: {
create: {
bio: 'New user bio',
},
},
},
});

// 여러 레코드 생성
const users = await prisma.user.createMany({
data: [
{ email: '[email protected]', name: 'User One' },
{ email: '[email protected]', name: 'User Two' },
{ email: '[email protected]', name: 'User Three' },
],
});

읽기:

// 단일 레코드 찾기
const user = await prisma.user.findUnique({
where: { email: '[email protected]' },
include: { profile: true, posts: true },
});

// 필터링 및 정렬로 여러 레코드 찾기
const publishedPosts = await prisma.post.findMany({
where: { published: true },
orderBy: { createdAt: 'desc' },
take: 10,
skip: 0,
include: {
author: {
select: { id: true, name: true, email: true },
},
},
});

// 레코드 수 세기
const totalPosts = await prisma.post.count({
where: { published: true },
});

업데이트:

// 단일 레코드 업데이트
const updatedPost = await prisma.post.update({
where: { id: postId },
data: {
title: 'Updated Title',
viewCount: { increment: 1 },
},
});

// 여러 레코드 업데이트
const result = await prisma.post.updateMany({
where: { published: false, authorId: userId },
data: { published: true },
});

삭제:

// 단일 레코드 삭제
const deletedUser = await prisma.user.delete({
where: { id: userId },
});

// 여러 레코드 삭제
const result = await prisma.post.deleteMany({
where: { authorId: userId, published: false },
});

고급 필터링​

const posts = await prisma.post.findMany({
where: {
AND: [
{ published: true },
{
OR: [
{ title: { contains: 'prisma', mode: 'insensitive' } },
{ content: { contains: 'orm', mode: 'insensitive' } },
],
},
],
createdAt: {
gte: new Date('2024-01-01'),
lte: new Date('2024-12-31'),
},
author: {
role: { in: ['ADMIN', 'EDITOR'] },
},
},
orderBy: [{ viewCount: 'desc' }, { createdAt: 'desc' }],
});

트랜잭션​

// 대화형 트랜잭션
const result = await prisma.$transaction(async (tx) => {
const author = await tx.user.create({
data: { email: '[email protected]', name: 'New Author' },
});

const post = await tx.post.create({
data: {
title: 'My First Post',
content: 'Hello world',
authorId: author.id,
},
});

await tx.profile.create({
data: {
bio: 'New author profile',
userId: author.id,
},
});

return { author, post };
});
팁

Cursor에게 요청하세요: "두 사용자 간에 크레딧을 이체하는 Prisma 트랜잭션을 작성해줘. 송신자에서 차감하고, 수신자에게 추가하고, 이체 기록을 생성해줘. 송신자의 잔액이 부족하면 롤백해줘." Cursor가 적절한 오류 처리로 대화형 트랜잭션을 생성합니다.

페이지네이션​

// 오프셋 기반 페이지네이션
const page = 1;
const pageSize = 10;

const [posts, totalCount] = await prisma.$transaction([
prisma.post.findMany({
where: { published: true },
orderBy: { createdAt: 'desc' },
skip: (page - 1) * pageSize,
take: pageSize,
include: { author: true },
}),
prisma.post.count({ where: { published: true } }),
]);

const totalPages = Math.ceil(totalCount / pageSize);

Prisma Studio​

Prisma Studio는 브라우저에서 실행되는 시각적 데이터베이스 브라우저입니다. 쿼리를 작성하지 않고도 레코드를 보고, 생성, 업데이트, 삭제할 수 있습니다.

Prisma Studio 실행​

npx prisma studio

이것은 http://localhost:5555에서 브라우저 인터페이스를 엽니다. 여기서 다음을 할 수 있습니다:

  • 스키마에 정의된 모든 모델을 탐색.
  • 정렬 및 필터링 가능한 테이블에서 레코드 보기.
  • 폼 인터페이스로 새 레코드 생성.
  • 셀을 클릭하여 기존 레코드 편집.
  • 확인 대화상자로 레코드 삭제.
  • 필드 값으로 레코드 필터링 및 검색.
팁

Prisma Studio는 개발 중 디버깅에 필수적입니다. 일회용 쿼리를 작성하는 대신 Studio를 실행하고, 데이터를 검사하고, 빠른 편집을 하세요. 변경 사항이 실시간으로 반영됩니다.

경고

Prisma Studio는 데이터베이스에 직접 씁니다. 프로덕션 데이터베이스에서 사용할 때 주의하세요 -- 삭제나 편집에 대한 실행 취소가 없습니다. Studio에서 수동 변경을 하기 전에 항상 데이터를 백업하세요.

Studio에서 필터링​

Prisma Studio는 다음으로 필터링을 지원합니다:

  • 필드 이름 (아무 열이나 선택).
  • 필터 연산자 (같음, 같지 않음, 포함, 시작, 끝 등).
  • AND/OR 논리로 결합된 여러 필터.

Cursor에서 AI 지원 스키마 생성​

Cursor의 AI 어시스턴트는 자연어 설명에서 Prisma 스키마, 쿼리 및 마이그레이션 전략을 생성하는 데 탁월합니다.

설명에서 스키마 생성​

Cursor Chat(Ctrl+L)을 열고 데이터 모델을 설명합니다:

Generate a Prisma schema for a project management application with these models:
- User (id, email, name, avatar, role enum: ADMIN/MEMBER/VIEWER)
- Workspace (id, name, slug, ownerId)
- Project (id, name, description, workspaceId, status enum)
- Task (id, title, description, status, priority, assigneeId, projectId, dueDate)
- Comment (id, content, authorId, taskId)
- Label (id, name, color, projectId)
Include all necessary relations, indexes on foreign keys, and timestamps.
Use PostgreSQL with UUID primary keys.

Cursor가 모든 모델, 관계, enum 및 인덱스가 포함된 완전한 스키마를 생성합니다. 생성된 스키마는 적절한 외래 키 관계, 복합 고유 제약 조건 및 모든 외래 키 열에 대한 데이터베이스 인덱스가 포함된 User, Workspace, Project, Task, Comment 및 Label 모델을 포함합니다.

팁

Cursor에게 스키마 생성을 요청할 때, 데이터베이스 제공자(PostgreSQL, MySQL 등), 기본 키 유형(UUID vs. autoincrement) 및 인덱스 필요 여부를 지정하세요. 이렇게 하면 생성된 스키마가 데이터베이스별 기능을 올바르게 사용합니다.

AI 지원 쿼리 생성​

Cursor에게 복잡한 쿼리 작성을 요청합니다:

Write a Prisma query to fetch the top 5 most active users in a workspace,
sorted by the number of completed tasks. Include each user's name, email,
avatar, and task count. Only count tasks created in the last 30 days.
const thirtyDaysAgo = new Date();
thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);

const topUsers = await prisma.user.findMany({
where: {
assignedTasks: {
some: {
status: 'DONE',
createdAt: { gte: thirtyDaysAgo },
project: { workspaceId },
},
},
},
select: {
id: true,
name: true,
email: true,
avatar: true,
_count: {
select: {
assignedTasks: {
where: {
status: 'DONE',
createdAt: { gte: thirtyDaysAgo },
},
},
},
},
},
orderBy: {
assignedTasks: {
_count: 'desc',
},
},
take: 5,
});
경고

중첩 필터링이 있는 Prisma의 _count는 비용이 많이 드는 SQL 쿼리를 생성할 수 있습니다. 항상 생성된 SQL을 확인하고(new PrismaClient({ log: ['query'] })로 쿼리 로깅 활성화), 전체 테이블 스캔을 피하기 위해 적절한 데이터베이스 인덱스를 추가하세요.

결론​

Prisma와 Cursor의 조합은 간소화된 데이터베이스 개발 경험을 만듭니다. 선언적 스키마 파일은 유일한 진실 공급원으로 작용하고, 마이그레이션은 스키마 진화를 위한 안전한 경로를 제공하며, 타입 안전 Prisma Client는 컴파일 타임에 쿼리 오류를 잡고, Prisma Studio는 데이터 검사를 위한 시각적 인터페이스를 제공합니다. Cursor의 AI 어시스턴트는 복잡한 도메인 모델에 대한 완전한 스키마 생성부터 중첩 include 및 집계가 있는 최적화된 쿼리 작성까지 모든 단계를 가속화합니다. Prisma 확장을 설치하고, 적절한 관계와 인덱스로 모델을 정의하고, 개발 중에는 migrate dev를, 프로덕션에서는 migrate deploy를 사용하고, Cursor가 반복적인 상용구를 생성하도록 하여 애플리케이션의 데이터 계층에 집중하세요.