Nest.js를 배워보자/4. NestJS에서 데이터베이스 연결하기 — Prisma 기반 실습

NestJS + Prisma: 트랜잭션과 관계(Relation)

_Blue_Sky_ 2025. 12. 2. 17:36
728x90

 


1. 🤝 트랜잭션 (Transactions)

트랜잭션은 여러 데이터베이스 작업(쿼리)을 하나의 논리적인 작업 단위로 묶어 처리하는 기능입니다. 트랜잭션 내의 모든 작업은 성공하거나(Commit), 하나라도 실패하면 전체 작업이 취소(Rollback)되어 데이터의 일관성을 보장합니다.

Prisma는 두 가지 방법으로 트랜잭션을 지원합니다.

1.1. 상호작용적 트랜잭션 (Interactive Transactions)

복잡하고 상호 의존적인 작업 흐름을 처리할 때 사용합니다. 이는 트랜잭션 블록 내에서 비동기 작업을 포함한 복잡한 로직을 순서대로 실행할 수 있게 해줍니다.

// posts/posts.service.ts
async createPostAndLog(createPostDto: CreatePostDto, userId: number) {
  // $transaction 블록 시작
  return this.prisma.$transaction(async (tx) => {
    // 1. 게시물 생성
    const newPost = await tx.post.create({ 
      data: { ...createPostDto, authorId: userId } 
    });

    // 2. 사용자의 게시물 개수 업데이트
    await tx.user.update({
      where: { id: userId },
      data: { postCount: { increment: 1 } },
    });

    // 💡 1, 2번 작업 중 하나라도 실패하면 모두 롤백됩니다.
    return newPost;
  });
}

1.2. 배치 트랜잭션 (Batch Transactions)

단순히 독립적인 여러 개의 쓰기(Create, Update, Delete) 쿼리를 순서대로 실행하고 싶을 때 사용합니다.

// posts/posts.service.ts
async deleteMultiplePosts(ids: number[]) {
  // 💡 독립적인 쿼리들을 배열로 묶어 전달합니다.
  const deleteQueries = ids.map(id => 
    this.prisma.post.delete({ where: { id } })
  );

  // 모든 쿼리가 성공해야 커밋됩니다.
  return this.prisma.$transaction(deleteQueries);
}

728x90

2. 🔗 관계(Relation) 데이터 처리

Prisma는 모델 간의 관계(1:N, 1:1, N:M)를 처리할 때 중첩된 쓰기(Nested Writes) 기능을 제공하여, 한 번의 쿼리로 관련된 데이터까지 함께 생성, 연결 또는 업데이트할 수 있게 합니다.

가정: User와 Post는 1:N 관계이며, Post 모델에 author 필드가 정의되어 있습니다.

2.1. 관계를 이용한 데이터 생성 (연결)

새로운 Post를 생성하면서 기존에 존재하는 User와 연결할 수 있습니다.

// 새로운 게시물 생성 시 기존 사용자(ID: 5)와 연결
async createPostWithExistingUser(data: CreatePostDto) {
  return this.prisma.post.create({
    data: {
      title: data.title,
      content: data.content,
      // 💡 connect: 기존 레코드와 연결 (외래 키에 값 자동 입력)
      author: {
        connect: { id: 5 }, 
      },
    },
  });
}

2.2. 관계를 이용한 데이터 생성 (중첩 쓰기)

새로운 User를 생성하면서 동시에 해당 User가 작성한 새로운 Post를 함께 생성할 수 있습니다.

// 새로운 사용자와 게시물을 동시에 생성
async createUserAndPost(userData: any, postData: any) {
  return this.prisma.user.create({
    data: {
      email: userData.email,
      name: userData.name,
      // 💡 create: User 생성 시 Post 레코드도 함께 생성
      posts: {
        create: [
          { title: postData.title1, content: postData.content1 },
          { title: postData.title2, content: postData.content2 },
        ],
      },
    },
    // 💡 select/include: 생성 후 Post 데이터까지 함께 반환
    include: {
      posts: true,
    },
  });
}
728x90

2.3. 관계 데이터 조회 (Eager Loading)

Post를 조회할 때, 관련된 User 데이터도 함께 조회(Join)할 수 있습니다.

// 게시물 목록과 함께 작성자 정보(User)를 포함하여 조회
async findPostsWithAuthors() {
  return this.prisma.post.findMany({
    // 💡 include: 관계된 데이터(author)를 함께 불러오기
    include: {
      author: true, 
    },
  });
}

Prisma의 트랜잭션과 관계 처리는 복잡한 비즈니스 로직을 타입 안전하고 명료하게 구현할 수 있도록 지원하며, NestJS 서비스 계층의 기능을 극대화합니다.

728x90