프로젝트 특성상 DB 작업이 복잡하게 일어나지 않습니다. ORM에서 제공하는 트랜잭션, 쿼리문으로 충분한 수준입니다.

하지만 만약 DB 작업이 복잡해진다면 어떻게 해야할까요? 쿼리나 트랜잭션을 직접 관리해주는 것이 좋을 것입니다.

그래서 이번 프로젝트에서도 트랜잭션을 적용해보기로 했습니다.

문제점

가장 기본적으로 트랜잭션은 이렇게 구현할 수 있습니다.

  const qr = this.getQueryRunner();
  try {
    await qr.connect();
    await qr.startTransaction();
    await qr.manager.save<UserEntity>(user); // 실제 쓰기가 일어나는 곳
    await qr.commitTransaction();
  } catch (e) {
    console.log(e);
    await qr.rollbackTransaction();
    throw new TransactionRollback();
  } finally {
    await qr.release();
  }

실제 쓰기가 일어나는 곳 하나를 작성하기위해 무수히 많은 코드가 필요합니다.

프로젝트에는 쿼리가 하나만 있는 것이 아니기 때문에 매번 데이터베이스 쓰기작업을 하는 함수마다 이 모든 걸 작성해주는 것은 귀찮고, 비효율적이고, 휴먼에러 발생확률이 매우 높습니다.

그래서 관심사 분리를 통해 공통 로직들을 분리합니다.

AOP

Aspect Oriented Programming, 관점 지향 프로그래밍

https://tecoble.techcourse.co.kr/post/2021-06-25-aop-transaction/

https://tecoble.techcourse.co.kr/post/2021-06-25-aop-transaction/

위의 그림으로 가장 잘 AOP가 설명된다고 생각합니다.

Class A가 회원가입을 담당하고 있으면 A에서는 회원가입만하고 로깅, 보안, 트랜잭션은 다른 곳에서 하게 하겠다는 것이죠.

핵심 비즈니스 로직과 공통적으로 필요한 코드를 분리해서 단일책임원칙을 지킬 수 있게 됩니다.

Intercepter?

NestJs에는 인터셉터라는 개념이 존재합니다.

라우트핸들러가 있을때 인터셉터를 적용하면 라우트핸들러 실행 전, 후로 실행되어 생명주기를 관리할 수 있습니다.