
Prisma의 진정한 강점은 관계형 데이터를 다루는 강력하고 직관적인 방식에 있습니다.
이 섹션에서는 일대다(One-to-Many), 다대다(Many-to-Many) 관계 설정부터, 데이터 로딩 최적화 및 복합적인 생성/수정(Nested Write) 기법까지 다룹니다.
4.1. 일대다 관계 (One-to-Many)
일대다 관계는 가장 흔한 관계이며, 한 모델이 다른 모델의 여러 인스턴스를 소유할 때 사용됩니다 (예: 한 User는 여러 Post를 작성).
🛠️ 스키마 정의
일대다 관계는 두 가지 필드로 정의됩니다.
- 외래 키 필드 (Foreign Key Field): N 쪽에 위치하며, 1 쪽 모델의 ID를 저장합니다. (예: Post 모델의 authorId)
- 관계 필드 (Relation Field):
- N 쪽: 외래 키를 참조하는 @relation 데코레이터와 함께 정의됩니다.
- 1 쪽: 관계된 모델의 배열 타입으로 정의됩니다.
// 1쪽 (User)
model User {
id Int @id @default(autoincrement())
email String @unique
posts Post[] // ⭐️ N쪽 모델의 배열 타입으로 정의 (관계 필드)
}
// N쪽 (Post)
model Post {
id Int @id @default(autoincrement())
title String
authorId Int // ⭐️ 외래 키 필드
author User @relation(fields: [authorId], references: [id]) // ⭐️ 관계 필드
}
💾 연결된 데이터 생성/조회
1. 관계를 이용한 데이터 생성
connect를 사용하여 기존 User와 Post를 연결하거나, create를 사용하여 연결과 동시에 새 User를 만들 수 있습니다.
// 🔗 기존 User에 Post 연결 생성
async createPost(title: string, authorId: number) {
return this.prisma.post.create({
data: {
title: title,
author: {
connect: { id: authorId }, // ⭐️ 기존 User ID를 이용해 연결
},
},
});
}
2. 관계 데이터 조회 (Eager Loading)
조회 시 include를 사용하여 연결된 데이터를 함께 가져옵니다. (4.3절에서 자세히 설명)
async getUserWithPosts(id: number) {
return this.prisma.user.findUnique({
where: { id },
include: { posts: true }, // ⭐️ 연결된 모든 posts를 한 쿼리로 가져옴
});
}
4.2. 다대다 관계 (Many-to-Many)
다대다 관계는 하나의 레코드가 다른 모델의 여러 레코드와 연결될 수 있고, 그 반대도 가능한 관계입니다 (예: 하나의 Post에는 여러 Tag가, 하나의 Tag는 여러 Post에 연결됨).
🛠️ 스키마 정의 (중간 테이블 자동 관리)
Prisma는 명시적인 중간 테이블(조인 테이블) 모델 없이도 다대다 관계를 자동으로 관리합니다.
// Post와 Tag는 서로의 배열 타입 필드를 가집니다.
model Post {
id Int @id @default(autoincrement())
title String
tags Tag[] // ⭐️ N쪽 모델의 배열 타입 (관계 필드)
}
model Tag {
id Int @id @default(autoincrement())
name String @unique
posts Post[] // ⭐️ N쪽 모델의 배열 타입 (관계 필드)
// Prisma가 자동으로 _PostToTag라는 중간 테이블을 생성/관리합니다.
}
💾 다대다 관계 쿼리
1. 연결 및 연결 해제
connect, disconnect, set을 사용하여 다대다 관계를 설정합니다.
async addTagsToPost(postId: number, tagIds: number[]) {
return this.prisma.post.update({
where: { id: postId },
data: {
tags: {
// ⭐️ 기존 태그는 그대로 두고, 새 태그 ID들만 연결
connect: tagIds.map(id => ({ id })),
// set: 모든 기존 연결을 끊고, 새 ID들로 대체
// disconnect: 특정 ID들의 연결을 끊음
},
},
include: { tags: true },
});
}
4.3. 관계형 데이터 로딩: include와 select
관계형 데이터를 가져오는 방식은 성능에 큰 영향을 미칩니다. Prisma는 Eager Loading을 사용하여 N+1 문제를 방지합니다.
🚀 Eager Loading: include
include는 주 모델을 조회할 때 연결된 관계 데이터를 함께 가져오도록 지시합니다. Prisma는 내부적으로 조인(Join) 또는 배치(Batch) 쿼리를 사용하여 단 한 번의 쿼리로 모든 데이터를 가져옵니다.
async getPostsWithAuthorAndTags() {
return this.prisma.post.findMany({
include: {
author: true, // ⭐️ User 객체 전체 포함
tags: { // ⭐️ Tag 목록 포함 (선택적으로 포함 필터링 가능)
select: {
name: true,
},
},
},
});
}
// N+1 문제: 목록을 가져온 후, 목록의 각 항목에 대해 별도의 쿼리를 날려 관계 데이터를 가져오는 비효율적인 상황
// include를 사용하면 이를 방지할 수 있습니다.
✂️ 필드 선택: select
데이터 전송량(Payload)을 줄이고 성능을 최적화하기 위해, 필요한 필드만 선택적으로 가져와야 합니다.
- select는 포함할 필드만 명시적으로 지정합니다.
- select와 include는 같은 레벨에서 함께 사용할 수 없습니다. select를 사용하는 경우, 관계 데이터도 select 객체 내부에 정의해야 합니다.
async getPostTitlesWithAuthorName() {
return this.prisma.post.findMany({
select: {
id: true,
title: true,
author: { // ⭐️ 관계 필드를 select 내에 정의
select: {
name: true, // User 모델에서 이름만 가져옴
},
},
},
});
}
4.4. 관계 데이터를 통한 생성/수정 (Nested Write)
중첩 쓰기(Nested Write)는 하나의 쿼리 요청으로 주 모델과 관계된 모델의 데이터를 동시에 생성, 수정, 또는 연결/연결 해제하는 강력한 기능입니다.
📝 중첩 생성 (create)
하나의 트랜잭션으로 User를 생성하면서 연결된 Post도 함께 생성할 수 있습니다.
async createCompleteUser(email: string, postTitle: string) {
return this.prisma.user.create({
data: {
email: email,
// ⭐️ posts 관계 필드에 중첩 쓰기 (Post 생성)
posts: {
create: {
title: postTitle,
content: 'Nested write example content',
},
},
},
include: { posts: true },
});
}
📝 중첩 수정 (update)
update 쿼리 내에서 관계된 모델을 create, update, delete, connect, disconnect 등으로 조작할 수 있습니다.
async updateUserAndPosts(userId: number, newPostTitle: string, postIdToUpdate: number) {
return this.prisma.user.update({
where: { id: userId },
data: {
// 1. 새로운 Post 생성
posts: {
create: { title: newPostTitle },
// 2. 기존 Post 수정
update: {
where: { id: postIdToUpdate },
data: { content: 'Updated content via nested write' },
},
// 3. 특정 Post 삭제
// delete: [{ id: postIdToDelete }],
},
},
include: { posts: true },
});
}
'Nest.js를 배워보자 > 15. NestJS & Prisma 완벽 가이드' 카테고리의 다른 글
| 🚢 6부. 실전 배포 환경 구성 (0) | 2025.12.03 |
|---|---|
| 🛡️ 5부. 고급 활용 및 데이터 무결성 (0) | 2025.12.03 |
| 🔍 3부. CRUD 기본 및 복합 쿼리 (0) | 2025.12.03 |
| 🛠️ 2부. NestJS 모듈 시스템에 Prisma 통합하기 (0) | 2025.12.03 |
| 🚀 1부.NestJS와 Prisma로 ORM 끝판왕 되기: 소개 및 기본 설정 (3) | 2025.12.03 |