prisma 사용기 3 - transaction

김지원·2023년 2월 16일
0

prisma

목록 보기
1/3
post-thumbnail

트랜잭션

프리즈마의 공식 홈페이지를 가면 트랜잭션 방법엔 2가지가 있습니다.

순차작업 (Sequential operations) - 공식적인 방법

const createOne = prisma.team.create({
  data: {
    name: 'Aurora Adventures',
    members: {
      create: {
        email: 'alice@prisma.io',
      },
    },
  },
})

// Nested write
const createTwo = prisma.team.create({
  data: {
    name: 'Cool Crew',
    members: {
      create: {
        email: 'elsa@prisma.io',
      },
    },
  },
})

// $transaction API
await prisma.$transaction([createTwo, createOne])

대화형 트랜잭션 (Interactive transactions) - priview 방법

generator client {
  provider = "prisma-client-js"
  previewFeatures = ["interactiveTransactions"] <- 추가 필요
}

schema.prisma에 위 설정을 등록해줍니다. (4.6버전 이상부터는 필요 X)

async function transfer(from: string, to: string, amount: number) {
  return await prisma.$transaction(async (prisma) => {
    // 1. Decrement amount from the sender.
    const sender = await prisma.account.update({
      data: {
        balance: {
          decrement: amount,
        },
      },
      where: {
        email: from,
      },
    })

    // 2. Verify that the sender's balance didn't go below zero.
    if (sender.balance < 0) {
      throw new Error(`${from} doesn't have enough to send ${amount}`)
    }

    // 3. Increment the recipient's balance by amount
    const recipient = await prisma.account.update({
      data: {
        balance: {
          increment: amount,
        },
      },
      where: {
        email: to,
      },
    })

    return recipient
  })
}

async function main() {
  // This transfer is successful
  await transfer('alice@prisma.io', 'bob@prisma.io', 100)
  // This transfer fails because Alice doesn't have enough funds in her account
  await transfer('alice@prisma.io', 'bob@prisma.io', 100)
}
  • 참조 (공식 홈페이지)

isolation level도 설정해줄 수 있지만 기본적으로 제가 사용하는 mysql은 repeatableRead기 때문에 따로 작업을 안해주겠습니다.

실사용기


@Injectable()
export class BoardService {
  constructor(
    private readonly boardsRepository: BoardRepository,
    private readonly categoriesRepository: CategoryRepository,
    private readonly prisma: PrismaClient,
  ) {}
  
  async create(userId: number, dto: CreateBoardDto): Promise<board> {
      const [boards] = await this.prisma.$transaction([
        this.boardsRepository.create(userId, dto),
        this.categoriesRepository.multiCreate(dto.categories),
      ]);
      return boards;
  }
}

저는 repository형식으로 했기 때문에 repository를 가져와서 실행하였고 insert였기 때문에 순차 방법으로 사용했습니다.
하지만 여기서 문제점이 발생했는데요

n-n 관계 테이블에도 데이터를 삽입해줘야하는데 트랜잭션이 끝난 후 넣어주면 데이터에 대해 보장을 해줄 수가 없는 문제점이 있었습니다.

그래서 2번째 방법으로 방향을 틀었는데요 이것도 문제가 있었습니다..😅

priview 방법에서는 무조건 transaction안에 있는 인자로 prisma에 접근해야 트랜잭션이 실행되는 문제였습니다.

return await this.prisma.$transaction(async (prisma) => {
      const board = await prisma.board.create({
        data: {
          title: dto.title,
          context: dto.context,
          thumbnail: dto.thumbnail !== null ? dto.thumbnail : 'thumbnail',
          user: {
            connect: {
              id: userId,
            },
          },
        },
      });

      dto.categories.map(async (name) => {
        const category = await prisma.category.upsert({
          where: {
            name: category,
          },
          update: {},
          create: {
            name: category,
          },
    	});
        await prisma.categories_on_boards.create({
          data: {
            categoryId: category.id,
            boardId: board.id,
          },
        });
      });

      return board;
    });

위 코드처럼 트랜잭션 안의 인자로 구성되어야하기 때문에 repository에서 구현하는 저로서는 사용하기 꺼려지는 방법이였습니다.
하지만 n-n 관계 테이블에도 데이터를 삽입해줘야한다면.. 어쩔 수 없이 사용해야되는 방법으로 볼 수도 있을 것 같습니다ㅠㅠㅠ

더 좋은 방법이 있다면 댓글로 남겨주세요 제발 🙏

주의할 점 (쓸데없긴함)

항상 사용하던데로 await을 쓰게 되면 아래 에러를 맛볼 수 있습니다..ㅎㅎ
아래 에러가 나는 이유는 트랜잭션의 결과값이 PrismaPromise로 나와야되기 때문입니다.
그래서 선언부나 호출부에서 async와 await을 안쓰는게 포인트라고 볼 수 있습니다

도움이 된 링크들

https://stackoverflow.com/questions/70893348/rollback-of-prisma-interactive-transaction-in-nestjs-not-working-when-throwing-a
https://www.prisma.io/docs/concepts/components/prisma-client/transactions#interactive-transactions
https://github.com/prisma/prisma/issues/1844
https://stackoverflow.com/questions/70305255/prisma-transaction-in-nodejs-and-typescript-not-working

profile
backend-developer

0개의 댓글