Spring의 Transaction 처리 방법
Spring에서 Transaction을 처리하는 방식에는 세 가지 방법이 있는데요. 첫 번째는 TransactionSynchronizationManager를 통한 동기화 방식, 두 번째로 PlatformTransactionManager를 통한 추상화 방식, 마지막으로 AOP를 통한 @Transactional 애노테이션을 활용한 선언적 트랜잭션 방식이 있습니다.
각 방식에 대해 간단하게 정리해보면, 동기화 방식은 Connection 객체를 보관해두고 필요할 때마다 호출하는 방식인데 이 방식의 경우 DB를 여러 개를 사용하게 되는 경우 로컬 트랜잭션이 아닌 글로벌 트랜잭션을 사용한다는 점에서 문제가 발생하게 됩니다. 이런 문제를 해결하기 위해 PlatformTransactionManager로 추상화하여 각 기술마다 종속적이지 않은 Transaction 처리를 구현할 수 있습니다.
하지만 가장 많이 사용하는 방식은 AOP 기반의 @Transactional 애노테이션을 활용하는 방법인데요. 이 방법의 장점은 비즈니스 로직과 트랜잭션 처리 코드를 완전히 분리하고, 부분적이고 세밀하게 트랜잭션의 처리를 구현할 수 있다는 점입니다.
@Transactional
Transactional의 동작원리보다는 @Transactional의 트랜잭션 처리를 위한 속성에 대해서 정리하도록 하겠습니다. @Transactional 속성에는 propagation, isolation, rollbackFor, noRollbackFor, readOnly, timeout 등이 있는데 Propagation에 대해 중점적으로 정리하고 나머지는 간단하게 정리하도록 하겠습니다.
Propagation
propagation은 트랜잭션의 시작에서 적용범위를 어디까지 묶어서 어떤 식으로 진행시킬지를 결정하기 위한 속성입니다.
| propagation | description |
| REQUIRED | @Transactional propagation의 default설정으로 기존 트랜잭션이 있다면 참여하고, 없으면 새로운 트랜잭션을 생성해서 진행하게 되는 속성입니다. |
| SUPPORTS | 기존 트랜잭션이 있으면 참여하고, 없으면 트랜잭션과 상관없이 진행하게 됩니다. 트랜잭션이 없지만 Connection이나 Hibernate Session을 공유할 수 있습니다. |
| MANDATORY | 기존 트랜잭션이 있으면 참여하고, 없으면 예외를 발생시킵니다. 독립적으로 트랜잭션을 진행할 수 없는 상황에서 사용합니다. |
| REQUIRES_NEW | 기존 트랜잭션이 있으면 해당 트랜잭션을 보류하고, 새로운 트랜잭션을 생성합니다. 항상 새로운 트랜잭션을 시작하는 속성입니다. |
| NOT_SUPPORTED | 트랜잭션을 사용하지 않도록 합니다. 이미 진행중인 트랜잭션이 있으면 보류합니다. |
| NEVER | 트랜잭션을 사용하지 않도록 강제 합니다. 기존 트랜잭션이 있어도 예외를 발생시킵니다. |
| NESTED | 중첩 트랜잭션을 실행하게 됩니다. REQUIRES_NEW와 다른 점은 트랜잭션 안에 또 다른 트랜잭션을 만든다는 점인데요. 중첩 트랜잭션은 부모 트랜잭션의 커밋과 롤백이 자식 트랜잭션까지 영향을 주지만 반대로 자식 트랜잭션은 부모에게 영향을 주지 않는다는 점입니다. 그래서 중요한 작업과 별개로 작업 로그를 커밋해야 한다면 작업 로그를 남기는 작업은 부모 트랜잭션에 영향을 받아서는 안되기 때문에 해당 옵션을 사용합니다. |
Isolation
isolation은 별도로 정리해두었으니 Transaction Isolation Level 포스팅을 참고하면 좋을 것 같습니다.
readOnly
이제 나머지 옵션에 대해서는 대략적으로 프로퍼티만으로 어떤 역할인지 이해가 가실 텐데요. readOnly는 읽기 전용 속성을 설정하는 것입니다. 저는 @Transactional(readOnly=true)를 클래스 레벨에 설정하고 추가, 수정, 삭제 작업에서 @Transactional 어노테이션을 설정합니다.
JPA와 함께 사용할 때 readOnly 설정은 하이버네이트의 session flush 모드를 MANUAL로 바꿔버립니다. 그 결과 강제로 flush() 하지 않으면 쿼리가 나가지 않아 추가, 수정, 삭제 작업을 할 수 없게 되고, Dirty Checking과 같은 기능을 막아 성능이 향상된다는 장점이 있습니다.
Wrap-up
Spring Transaction은 너무나 중요한 내용이라고 생각하고, 실제로 면접 단골 질문입니다. 특히 propagation, isolation은 정말 필수적으로 학습해야 한다고 생각해요. @Transactional의 경우 AOP의 동작원리와 함께 학습해서 추가적인 설명을 할 수 있다면 더 좋을 것 같다는 생각이 듭니다. 여유가 생긴다면 AOP의 동작원리를 자세하게 정리하면서 @Transactional과 함께 생각해볼 수 있는 포스팅을 작성하도록 하겠습니다.
reference
토비의 스프링 vol1, vol2
'Spring' 카테고리의 다른 글
| Annotation based Interceptor (0) | 2021.09.10 |
|---|---|
| Spring MVC - Filter, Interceptor (0) | 2021.08.11 |
| AOP를 직접 구현해보자 - Proxy적용 (0) | 2021.08.02 |
댓글