트랜잭션(Transaction)이란?
트랜잭션은 하나의 논리적 작업을 완료하기 위해 데이터베이스를 변화시키는 일련의 행동들을 하나의 단위로 바라보는 것이다. 예를 들어 '게시판에서 유저가 탈퇴한다' 라는 하나의 논리적 작업을 완료하기 위해, 먼저 유저를 조회하는 작업과 해당 유저가 작성한 글, 댓글을 모두 삭제하는 작업, 그리고 그 유저데이터를 삭제하는 작업 이 세 일련의 행동을 하나의 유저탈퇴 트랜잭션으로 구성할 수 있는 것이다.
트랜잭션의 중요한 특징 중 하나는 이러한 작업들이 All or Nothing 즉 전부 완료되거나, 하나도 완료되지 않아야 한다는 점이다. 유저가 탈퇴하는 작업중 어느 하나에서 에러가 발생하여 유저탈퇴가 취소된다면, 작성한 글들이 전부 삭제되는 일은 일어나지 않아야 한다는 것이다.
트랜잭션이 지켜야 하는 ACID 원칙이나, 트랜잭션 격리 수준 등 다양한 내용이 더 존재하지만, 이번 글에서는 TypeORM에서 트랜잭션을 사용하는 몇가지 방법에 대해 다루어보려고 한다.
엔티티는 아주 간단하게 User와 Post로만 구성했다. 유저와 글을 삭제하는 과정에서 CASCADE를 사용할 수도 있겠지만 트랜잭션의 설명을 위해 여기서는 떼어 놓았다.
Connection 혹은 EntityManager 사용하기
가장 먼저 소개된 방법은 Connection이나 EntityManager의 transaction 함수를 사용하는 것이다.
이런 식으로 함수를 작성하고 실행하면 다음과 같은 쿼리가 날아간다.
Connection 대신 getManager를 통해 엔티티 매니저를 얻은 후 실행해도 같은 결과를 얻을 수 있다.
고의로 트랜잭션 중간에 에러를 발생시켜 보면 정상적으로 롤백시키는것도 확인할 수 있다.
이때 주의할 점은 무조건 제공받은 엔티티 매니저를 사용해야 한다는 점이다. 실제로 getManager 를 통해 다른 매니저를 사용하여 데이터베이스를 조작하면 롤백시에 롤백이 반영이 되지 않는다. 이에 대하여 따로 오류나 경고 메시지조차 뱉지 않기 때문에 데이터 무결성이 깨질 수 있어 주의해야한다.
트랜잭션 격리 수준 또한 첫 번째 인자로 전달해 줄 수 있다.
데코레이터 사용하기
콜백 말고 조금더 JPA스럽게(?) 작성할 수 있는 방법이 있다. 바로 @Transaction() 데코레이터를 이용하는 것이다.
(isolationLevel은 @Transaction() 의 첫 번째 인자로 넘길 수 있다)
Transaction 데코레이터가 있는 함수는 인자로 TransactionManager 데코레이터를 사용할 수 있고, 이를 통해 매니저를 주입받을 수 있다. 그런데 이렇게만 작성하면 사용할 때 에러가 발생한다.
manager 인자는 TypeORM에서 주입하지만 이를 인식하지 못하여 에러가 발생한다. 이를 해결하기 위해 인자의 manager에 Optional Chaining (기호 ?)을 사용하여 인자를 받지 않을 수 있게 하고, 이후에 어떠한 문제가 발생하여 manager가 주입되지 않으면 에러를 던지도록 설정해 둔다.
더 좋은 해결법이 있다면 댓글로 작성해주시면 감사하겠습니다!
이때도 주의할 점은, TransactionEntityManager 데코레이터를 사용하여 얻은 매니저로만 데이터를 조작해야 한다는 점이다.
QueryRunner 사용하기
TypeORM의 Connection은 기본적으로 Connection Pool 을 만들어 사용한다. 이때 실제 단일 데이터베이스 커넥션을 따로 만들어서 좀더 세부적으로 트랜잭션을 관리하고 싶다면 QueryRunner를 사용할 수 있다.
connection으로부터 createQueryRunner 함수를 통해 QueryRunner 인스턴스를 생성할 수 있다. 해당 인스턴스를 connect 함수를 통해 데이터베이스에 연결한 뒤 쿼리를 날리거나 트랜잭션을 수행할 수 있다.
- startTransaction(isolationLevel) 트랜잭션 시작
- commitTransaction() 트랜잭션 커밋
- rollbackTransaction() 트랜잭션 롤백
queryRunner를 다 사용하고 나면 release 를 통해 인스턴스를 해제해 주어야 한다.
참고자료
'Study > DATABASE & ORM' 카테고리의 다른 글
[TypeORM] N+1 문제와 Eager and Lazy Relations (0) | 2021.12.19 |
---|---|
[MySQL]페이징 기능 구현하기+Console UI에 적용 (0) | 2020.11.28 |
[MySQL]MySQL 기본적인 명령어 모음 (0) | 2020.11.26 |
[SQL developer]SQL developer 설치와 MySQL 연동 (0) | 2020.11.25 |
댓글