
핵심요약
이 글은 Spring @Transactional의 동작 방식과 롤백 처리 메커니즘을 상세히 다룹니다. 특히 Unchecked Exception과 Checked Exception의 차이, TransactionInterceptor의 역할, 그리고 Kotlin 환경에서의 예외 처리 및 다양한 전파 옵션의 영향을 실제 사례를 통해 분석하며 효율적인 트랜잭션 관리 방안을 제시합니다.
Spring @Transactional 롤백 동작 심층 분석
@Transactional 기본 동작 및 롤백 마킹
- Spring
@Transactional은 Spring AOP 기반으로 동작하며, 트랜잭션 진행 중 예외 발생 시 롤백 마킹 여부를 결정한다. - 기본적으로
RuntimeException및Error같은 Unchecked Exception 발생 시 트랜잭션은 rollback-only로 마킹된다. - Checked Exception은 예상된 예외로 간주되어 기본적으로 롤백을 유발하지 않으며, 개발자의 명시적 처리가 필요하다.
@Transactional의rollbackFor속성을 통해 롤백 대상 예외를 커스터마이징할 수 있다.
@Transactional 호출 패턴과 롤백 메커니즘
- 동일 클래스 내 메서드 호출:
@Transactional메서드가 내부 메서드를 호출할 경우 **프록시(TransactionInterceptor)**가 동작하지 않아 새 트랜잭션이 열리지 않으며 롤백 마킹도 수행되지 않는다. - 다른 서비스 호출: 다른 서비스의
@Transactional메서드를 호출하면 프록시를 거쳐TransactionInterceptor가 동작하며, Unchecked Exception 발생 시 트랜잭션에 롤백 마킹이 된다. - 예외 처리와 롤백:
try-catch블록으로 예외를 처리하더라도, 이미TransactionInterceptor에 의해 트랜잭션이 rollback-only로 마킹되어 있다면 최종 커밋 시점에서 롤백이 발생한다.
Kotlin 예외 처리 및 트랜잭션 전파 옵션 심화
- Kotlin의 Checked Exception: Kotlin은 Checked Exception 제약을 제거하여, Java 코드와 상호 작용 시
IOException같은 Checked Exception이 UndeclaredThrowableException으로 변환될 수 있다. - UndeclaredThrowableException:
RuntimeException을 상속하므로 Spring 트랜잭션에서 기본적으로 롤백을 유발한다. 이를 방지하려면@Throws어노테이션을 사용하여 의도한 Checked Exception을 명시해야 한다. REQUIRES_NEW전파:Propagation.REQUIRES_NEW는 항상 새 트랜잭션을 생성하며, 부모 트랜잭션과 독립적으로 동작하므로 자식 트랜잭션의 롤백은 부모 트랜잭션에 영향을 주지 않는다.- 스레드와 트랜잭션: Spring 트랜잭션은 ThreadLocal로 상태를 관리하므로,
@Async와 같은 비동기 처리는 기존 트랜잭션과 별도로 동작한다.
실제 사례를 통한 효율적인 트랜잭션 관리
- 긴 트랜잭션 문제 해결: 대량 데이터 처리 시 발생하는 긴 트랜잭션으로 인한 타임아웃 및 전체 롤백 문제를 해결하기 위해 트랜잭션 분리 전략을 사용한다.
REQUIRES_NEW활용: 사용자 데이터 삭제와 같이 독립적으로 성공/실패해야 하는 로직을Propagation.REQUIRES_NEW로 분리하여 무손실 처리를 보장하면서 전체 재시도 시 트랜잭션 길이를 단축한다.- 커서 기반 처리: 매우 큰 데이터 셋은 커서 기반으로 분할하고, 각 커서 처리마다 새로운 트랜잭션을 생성하여 짧은 단위로 트랜잭션을 완료함으로써 효율성을 높인다.