[Spring] @Transactional 롤백은 언제 되는 걸까? - 예외가 발생했는데도 DB 반영이 된다고?
@Transactional 애노테이션
스프링은 @Transactional 애노테이션이 붙은 클래스에 프록시를 생성합니다. 프록시는 트랜잭션 로직을 메서드 앞뒤에 넣어줍니다.
이 글은 @Transactional 의 롤백에 대해서 다룹니다. rollbackFor이라는 것을 써보면서 어디에 어떻게 적용되는지 알아보겠습니다.
책 정보를 저장하는 아주 간단한 코드입니다.
다음은 책을 저장하는 메서드입니다. @Transactional 애노테이션의 rollbackFor 속성에 대해서 RuntimeException.class를 등록했습니다.
이 애노테이션은 스프링에게 런타임 예외가 발생한다면 롤백을 하라고 말합니다.
그렇다면 rollbackFor 속성을 주지 않는다면 어떻게 될까요?
사실은 스프링은 디폴트로 UnCheckedException 과 Error에 대해서 롤백 정책을 설정합니다. 헷갈리나요?
예시를 다시 살펴볼게요.
다음과 같이 @Transactional에 아무 속성을 주지 않는다면
스프링이 다음과 같이 이해합니다.
스프링은 RuntimeException 과 Error를 기본적으로 롤백 정책으로 이해합니다.
(CheckedException, UnCheckedException, Error에 대해서 구분을 하지 못하시는 분들은 예외에 대해서 숙지를 하시고 보시면 될 것 같습니다.)
하지만 이런식으로 checked 예외를 던져서 catch로 처리했다고 가정합시다. (catch로 처리하든 안 하든 결과는 같음)
이런 경우는 롤백이 기본적으로 되지 않습니다. 결국 데이터베이스에는 값이 들어가게 된 상태로 끝나게 됩니다.
다시 말하면 모든 예외상황에서 롤백이 되는 것이 아니라는 것입니다.
앞서 설명드린 것처럼 catch 를 하지 않고 상위로 throw를 해도 마찬가지로 롤백이 되지 않습니다.
직접 postman으로 요청을 보내면서 확인해보죠.
예외가 발생했음에도 (Checked Exception) 데이터베이스에 값이 들어간 모습입니다.
참고로 NullPointerException 은 RuntimeException (UnCheckedException) 이기 때문에 디폴트로 롤백이 됩니다.
rollbackFor은 그럼 언제쓰나요?
먼저 스프링이 RuntimeException을 관리하는 것을 잊지 않는 게 중요합니다. 만약에 Checked Exception 이 발생했을 때 트랜잭션이 롤백이 되지 않고 디비에 변경이 되는 것을 모르고 있을 때가 문제가 됩니다. 모든 예외에 대해서 전부 트랜잭션을 롤백하고 싶다면
rollbackFor = {Exception.class}
으로 설정을 해야 롤백이 됩니다.
결론은 롤백이 언제 되는지 정확히 파악하고 애플리케이션에 맞게 설정을 하는 것이 좋겠죠.
참고:
https://netsurfingzone.com/spring/transactional-rollbackfor-example-using-spring-boot