본문 바로가기
Programming & Platform/NestJS

NestJS TypeORM 트랜잭션 사용방법, 예시코드

by 코드스니펫 2024. 1. 6.
반응형

NestJS TypeORM 트랜잭션 사용방법, 예시코드

 

NestJS logo

 

트랜잭션은 데이터베이스에서 수행되는 작업의 단위를 나타냅니다. 이는 다른 트랜잭션과 독립적으로 일관되고 안정적인 방식으로 처리되어야 합니다. NestJS에서 TypeORM을 사용하여 트랜잭션을 다루는 방법에 대해 알아보겠습니다.

 

 

NestJS 트랜잭션 사용방법

 

DataSource 주입

우선, 일반적인 방법으로 DataSource 객체를 클래스에 주입해야 합니다.

@Injectable()
export class UsersService {
  constructor(private dataSource: DataSource) {}
}

 

 

QueryRunner를 사용한 트랜잭션 생성

이제 이 객체를 사용하여 트랜잭션을 생성할 수 있습니다.

async updateUserPoint(userId: number, seat_price: number) {
  const queryRunner = this.dataSource.createQueryRunner();

  await queryRunner.connect();
  await queryRunner.startTransaction();

  try {
    // 트랜잭션 범위 내에서 사용자 정보 가져오기
    const user = await queryRunner.manager.findOne(User, userId, { lock: { mode: 'pessimistic_write' } });

    if (!user) {
      throw new NotFoundException('사용자를 찾을 수 없습니다.');
    }

    if (user.point - seat_price < 0) {
      throw new BadRequestException('사용자의 포인트가 부족합니다.');
    }

    // 포인트 업데이트는 트랜잭션 범위 내에서 처리
    await queryRunner.manager.update(User, userId, { point: user.point - seat_price });

    await queryRunner.commitTransaction();
  } catch (err) {
    await queryRunner.rollbackTransaction();
    throw err; // 예외를 다시 throw하여 호출자에게 전파
  } finally {
    await queryRunner.release();
  }
}

 

이 코드는 NestJS에서 TypeORM을 사용하여 트랜잭션을 안전하게 처리하는 방법을 나타냅니다. 아래는 코드의 동작 및 주요 수정 사항에 대한 설명입니다:

 

 

예시코드 설명

 

트랜잭션 시작 및 QueryRunner 생성

queryRunner를 생성하고, 데이터베이스 연결 및 트랜잭션을 시작합니다.

 

 

const queryRunner = this.dataSource.createQueryRunner();
await queryRunner.connect();
await queryRunner.startTransaction();

 

 

사용자 정보 가져오기

  • queryRunner.manager.findOne 메서드를 사용하여 사용자 정보를 트랜잭션 범위 내에서 안전하게 가져옵니다.
  • lock: { mode: 'pessimistic_write' }를 사용하여 쓰기 잠금을 설정합니다.
const user = await queryRunner.manager.findOne(User, userId, { lock: { mode: 'pessimistic_write' } });

 

 

사용자 및 포인트 유효성 검사

  • 사용자가 없는 경우 NotFoundException을 throw하여 적절히 처리합니다.
  • 사용자의 포인트가 부족한 경우 BadRequestException을 throw하여 트랜잭션 롤백 및 예외 전파를 수행합니다.
if (!user) {
  throw new NotFoundException('사용자를 찾을 수 없습니다.');
}

if (user.point - seat_price < 0) {
  throw new BadRequestException('사용자의 포인트가 부족합니다.');
}

 

 

포인트 업데이트 및 트랜잭션 커밋

사용자의 포인트를 업데이트하고, 모든 작업이 성공한 경우 트랜잭션을 커밋합니다.

await queryRunner.manager.update(User, userId, { point: user.point - seat_price });
await queryRunner.commitTransaction();

 

 

트랜잭션 롤백 및 QueryRunner 해제

예외가 발생한 경우 트랜잭션을 롤백하고, 마지막에는 queryRunner를 해제합니다.

} catch (err) {
  await queryRunner.rollbackTransaction();
  throw err; // 예외를 다시 throw하여 호출자에게 전파
} finally {
  await queryRunner.release();
}

 

 

코드 동작 설명

 

트랜잭션 범위

코드 내의 모든 데이터베이스 작업은 queryRunner 객체 내에서 트랜잭션 범위에 있습니다. 이는 여러 쿼리가 모두 성공하거나 실패할 때 일관성을 유지하는 데 도움이 됩니다.

 

Pessimistic Write Lock

사용자 정보를 가져올 때 pessimistic_write 모드로 쓰기 잠금을 설정하여 다른 트랜잭션과의 충돌을 방지합니다.

 

 

예외 처리와 롤백

어떠한 예외가 발생하면 트랜잭션을 롤백하고, 예외를 다시 throw하여 호출자에게 전파합니다.

 

QueryRunner 해제

모든 작업이 완료되면 queryRunner를 해제하여 데이터베이스 리소스를 정리합니다.

 

요약하자면 이 코드는 트랜잭션을 안전하게 다루면서 사용자 포인트 업데이트 시 발생할 수 있는 문제를 방지합니다.

 

 

주의사항

 

dataSource는 단순히 QueryRunner를 생성하는 데만 사용됩니다. 그러나 이 클래스를 테스트하려면 전체 DataSource 객체를 mocking해야 합니다. 이를 해결하기 위해 QueryRunnerFactory와 필요한 트랜잭션 유지에 필요한 일부 메서드를 정의한 인터페이스를 사용하는 것이 좋습니다. 이 기술을 사용하면 이러한 메서드를 mocking하는 것이 간단해집니다.

 

끝으로

NestJS에서 TypeORM을 사용하여 데이터베이스 트랜잭션을 다루는 것은 QueryRunner 클래스를 이용하는 것이 권장됩니다. 이를 통해 트랜잭션의 전체적인 제어를 얻을 수 있으며, 테스트에서는 QueryRunnerFactory 및 관련 인터페이스를 사용하여 효과적으로 mocking할 수 있습니다. 이러한 접근 방식은 안정적이고 일관된 데이터베이스 작업을 보장하는 데 도움이 됩니다.

 

▼ NestJS 카테고리 인기글 ▼

 

 

NestJS JWT 토큰 유효 시간 설정하는 방법

NestJS JWT 토큰 유효 시간 설정하는 방법 NestJS에서 JWT(Json Web Token)을 사용하여 인증을 구현할 때, 토큰의 유효 시간을 설정하는 것은 중요한 보안 고려 사항 중 하나입니다. 이 글에서는 Nest.js의 Jwt

lemonlog.tistory.com

 

 

NestJS 앱의 시작, main.ts 기본 구조

NestJS 앱의 시작, main.ts 기본 구조 우리의 NestJS 앱이 여기서 시작됩니다. main.ts 파일은 우리 앱의 진입점이자, 새로운 모험의 문을 열어주는 열쇠입니다. 이 코드를 통해 앱이 어떻게 생성되고 구

lemonlog.tistory.com