트랜잭션 데코레이터화 하기

JSM·2023년 12월 11일
0

프로젝트

목록 보기
8/10
post-thumbnail

NEST.JS의 문제..?

  • Nest.js의 문제점으로 느낀 것이 바로 불필요한 트랜잭션 코드입니다.
  • 스프링 AOP 기술을 통해 트랜잭션 어노테이션을 사용해오던 저에게는 매우 불필요한 코드였습니다.
  • 따라서 이를 분리하기 위해 인터셉터와 데코레이터를 사용하였습니다.

트랜잭션 인터셉터

  • Nest.js의 인터셉터는 크게 3가지 기능을 할 수 있습니다
  • 요청 전 로직, 요청 성공 시 로직, 예외 터질 시 로직
  • 따라서 요청이 컨트롤러로 들어가기전에 쿼리 러너를 만든 후 트랜잭션을 시작합니다.
  • 이떄 쿼리 러너를 http request 객체에 넣어줍니다. 이는 나중에 재사용하기 위함입니다.
  • 예외가 터지면 catchError 기능을 통해 트랜잭션을 롤백합니다.
  • 요청이 성공적으로 마무리되면 트랜잭션을 커밋합니다.
@Injectable()
export class TransactionInterceptor implements NestInterceptor {
  constructor(private readonly dataSource: DataSource) {}

  async intercept(
    context: ExecutionContext,
    next: CallHandler,
  ): Promise<Observable<any>> {
    const request = context.switchToHttp().getRequest();

    const qr = this.dataSource.createQueryRunner();

    await qr.connect();

    await qr.startTransaction();

    request.queryRunner = qr;

    return next.handle().pipe(
      catchError(async (e) => {
        await qr.rollbackTransaction();
        await qr.release();
        throw new TransactionRollback();
      }),
      tap(async () => {
        await qr.commitTransaction();
        await qr.release();
      }),
    );
  }
}

쿼러 러너 데코레이터

  • 쿼리 러너 데코레이터를 트랜잭션을 적용할 각 컨트롤러에 붙여 줍니다.
  • 이를 통해 HTTP 요청 안에 쿼리 러너가 있는지 파악하고 만약 쿼리 러너가 없다면 예외를 터뜨립니다.
export const QueryRunner = createParamDecorator(
  (data, context: ExecutionContext) => {
    const request = context.switchToHttp().getRequest();

    if (!request.queryRunner) {
      new TransactionInterceptorRequired();
    }

    return request.queryRunner;
  },
);

마무리

  • Nest.js에 있을게 다있는데 아쉽게도 트랜잭션에 대해 매번 불필요한 코드가 너무 많았습니다.
  • 이를 계선해볼 작업을 고민했고 쿼리 러너 데코레이터와 트랜잭션 인터셉터를 구현해보는 방법을 고민했습니다.
profile
내 기술적 고민들을 모은 곳...

0개의 댓글