
백엔드
Spring Transactional Rollback Deep Dive
두줄요약
Spring @Transactional 의 롤백이 언제 마킹되는지 예외 타입과 프록시 동작을 기준으로 정리했습니다. Kotlin 과 REQUIRES_NEW 까지 포함해 실제 동작 차이와 주의점을 설명했습니다.
문제 상황
- Spring @Transactional 에서 롤백이 언제 발생하는지, 예외를 잡아도 롤백되는지에 대한 혼동
- 같은 클래스 내부 호출, 다른 서비스 호출, Checked/Unchecked Exception, Kotlin 예외 처리 차이에 따른 동작 차이
- REQUIRES_NEW 와 스레드/트랜잭션 경계가 실제 롤백 결과에 미치는 영향
원인 분석
- @Transactional 은 AOP 프록시 기반이라 같은 클래스 내부 호출에서는 TransactionInterceptor 가 동작하지 않음
- 롤백은 예외 발생 자체보다 TransactionInterceptor 가 예외를 감지해 rollback-only 로 마킹하는지에 좌우됨
- Kotlin 에서는 Checked Exception 이 UndeclaredThrowableException 으로 바뀌며 Unchecked 로 취급될 수 있음
해결 방법
- 트랜잭션이 필요한 메서드는 별도 서비스로 분리하거나 TransactionTemplate 로 명시적 트랜잭션 제어
- @Throws 를 사용해 Kotlin 예외를 Java throws 와 유사하게 노출하여 의도치 않은 래핑 방지
- REQUIRES_NEW 로 트랜잭션 경계를 분리해 긴 처리와 재시도 범위를 조정
적용해볼 점
- 예외 처리 위치와 트랜잭션 프록시 통과 여부를 먼저 점검
- 롤백 필요 여부에 맞게 propagation 과 예외 타입을 명시적으로 설계
- 긴 이벤트 처리에서는 내부 트랜잭션 분리로 재시도 비용과 일관성 균형 조정
