다 되거나 안 되게 하는 그것.
Spring Boot에서 @Transaction 띡 붙여서 해결하던 그거에 대해서 알아보려고 한다.
트랜잭션 구현은 Spring Boot의 압도적인 승리다.
대충 아래와 같은 Spring Boot 코드와 똑같이 동작하는 코드를 NestJS에서 작성하려면 얼마나 귀찮아지는지 알아보자.
@Transactional
public void transactionTest() {
entityARepository.save(entityA);
entityBRepository.save(entityB);
}
@Injectable()
export class TransactionTestService {
constructor(
private dataSource: DataSource // ⭐️ 1
) { }
// 트랜잭션 테스트
async saveBoard(): void {
const queryRunner = this.dataSource.createQueryRunner();
await queryRunner.connect();
await queryRunner.startTransaction();
try {
// 로직
const savedEntityA = await queryRunner.manager.save(entityA);
const savedEntityB = await queryRunner.manager.save(entityB);
await queryRunner.commitTransaction();
} catch (error) {
await queryRunner.rollbackTransaction();
} finally {
await queryRunner.release();
}
}
}
DataSource 객체를 주입하고, try-catch-finally 블록을 통해 성공 시 커밋 / 실패 시 롤백, queryRunner 리소스 해제를 해줘야 한다.
Model1으로 만들었던 쇼핑몰의 악몽이 떠오른다...😱
개발 공부 초기에 Model1 방식으로 쇼핑몰을 만들었던 적이 있다.
그때 정확히 트랜잭션을 저렇게 처리했었다.
public boolean addEmployee(Employee paramEmployee) {
boolean result = true; // 메소드 실행 결과값을 담을 변수
Connection conn = null;
try {
this.dbUtil = new DBUtil();
conn = dbUtil.getConnection();
conn.setAutoCommit(false);
this.employeeDao = new EmployeeDao();
if(employeeDao.insertEmployee(conn, paramEmployee) != 1) {
// 오류는 나지 않았지만 정상적 삽입도 아닌 경우 의도적으로 오류를 발생시킴
throw new Exception();
}
conn.commit();
System.out.println("addEmployee() 성공");
} catch(Exception e) {
result = false;
e.printStackTrace();
try {
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
} finally {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
return result;
}
사실 처음 이 코드를 배울 땐 Transaction이라는 개념 자체가 너무 신기해서 귀찮은지 몰랐다.
하지만 그것마저도 메소드 몇 개 만들고 나니 황당할정도로 귀찮아 졌고, try 블록 안의 코드만 바꿔끼울 수 있는 방법이 없는지 궁금했던 기억이 난다.
끝내 이 방법으로 쇼핑몰을 완성하고 Spring Boot를 처음 배웠을 때 얼마나 혁신ㅋㅋㅋ적이었는지도!
QueryRunner를 사용한 트랜잭션은 NestJS 공식문서를 통해 학습한 것이다. 뭔가 더 편한 방법이 있을수도 있다. 인터셉터를 직접 구현하거나?
어쨌거나 공식문서 기준 NestJS의 트랜잭션은 당황스러운걸로...