실무에서 @Transactional이 걸려 있었음에도 불구하고 rollback이 되지 않았던 이슈가 있었습니다.
예를들면 2가지 상품을 구매 했는데 1개는 구매 성공하고 다른 한가지 상품에 대해 구매 시도하다 오류가 발생하면
이전에 성공처리 된 상품도 취소 되어야하는데 rollback이 되지 않았습니다.
이것에 대해 알아보기 위해 우리는 java에서의 예외에 부터 먼저 알아봐야 합니다.
자바의 예외
자바에서 예외는 크게 Checked Exception과 Unchecked Exception으로 이루어져 있습니다.
전자인 Checked Exception은 개발자가 반드시 예외처리를 진행해야 하며, Unchecked Exception은 개발자가 예외처리를 하지 않아도 됩니다.
트랜잭션이란 무엇일까요?
쉽게말하면 작업의 단위, 묶음이라고 할 수 있겠습니다. 가장 많이 비유하는 계좌 이체을 예로들면 내 계좌에서 돈 인출, 다른 계좌에 돈 입금 이 2가지 작업을 묶을 수 있을 것입니다. 2가지 작업이 성공해서 DB에 정상적으로 반영하는 것을 커밋, 하나라도 실패하면 Rollback이 됩니다.
스프링에서의 @Transactional
스프링에서의 @Transacational은 AOP를 활용한 대표적인 어노테이션 중 하나입니다. AOP라고 하면 Runtime Weaving방식을 이용하는데, Proxy를 생성하여 내가 작성한 비즈니스 로직 사이에 스프링이 작성한 로직을 끼어넣는다고 생각하면 될것 같습니다.
@Transactional 어노테이션 하나만 선언하면 매우 편안하게 트랜잭션을 적용할 수 있고 이러한 방식을 선언적 트랜잭션 관리라고도 합니다.
@Transacation의 rollback 기본 정책
Unchecked Exception인 RuntimeException 그리고 상위 클래스인 Error와 그 하위 예외가 발생하면 롤백합니다.
반대로 체크 예외인 Exception과 그 하위 예외들은 커밋합니다. rollbackFor와 noRollbackFor옵션을 통해 기본 정책에 추가로 특정 예외를 롤백할지 말지 지정할 수도 있습니다.
예를들어 아래와 같이 옵션을 사용하면 체크 예외인 Exception이 발생해도 커밋이 아닌 롤백이 됩니다.
/**
* <p>By default, a transaction will be rolled back on {@link RuntimeException}
* and {@link Error} but not on checked exceptions (business exceptions). See
...
**/
@Transactional(rollbackFor = Exception.class)
왜 Unchecked Exception만 예외처리가 될까? 과연 Unchecked Exception만 무조건 rollback을 하는게 맞을까? 라는 생각이 들었습니다. 관련 해서 Spring 내부 DefaultTransactionAttribute에 설명되어 있습니다.
결국 unchecked exception은 비즈니스상 예상하지 못해 롤백 처리하고, checked exception은 의도한 걸 수 있어 커밋 한다는 것입니다.
결론적으로 특정 예외 클래스에 대해서도 롤백이 필요하면 Transactional 옵션인 rollbackFor에 적으면 됩니다.
Reference
'Spring' 카테고리의 다른 글
Resilience4j Circuit Breaker에 대해서 (0) | 2023.12.03 |
---|---|
[Spring] 스프링 어노테이션 정리 (0) | 2020.08.02 |
스프링과 스프링 부트 (0) | 2020.03.28 |