Spring Transaction Strategy

1 분 소요

Overview


Spring의 @Transactional 어노테이션은 매우 유용하지만, 기본 동작 방식에 대한 오해는 종종 데이터 정합성 문제로 이어질 수 있음. 해당 포스트에서는 핵심적인 두 가지 질문에 대해 명확히 짚으려 함

롤백(Rollback)의 기본 규칙: Unchecked Exception vs Checked Exception

가장 흔한 오해는 “예외가 발생하면(모든) 롤백을 일으킨다.” 이는 오해

예외 종류 기본 동작 예시 Spring의 의도
Unchecked Exception ⭕️ 롤백 (Rollback) RuntimeException 및 그 하위 클래스들 (NullPointerException, IllegalArgumentException, FeignException 등) “복구 불가능한 시스템 오류/버그”. 데이터 정합성이 깨졌을 가능성이 높으므로 즉시 롤백하여 작업을 되돌린다.
Checked Exception ❌ 커밋 (Commit) IOException, SQLException, JsonProcessingExceptionException을 상속하지만 RuntimeException이 아닌 클래스들 “복구 가능한 예외”. 개발자가 try-catch로 처리하여 비즈니스 로직을 이어갈 수 있다고 간주한다. 따라서 예외가 처리될 것을 기대하고 트랜잭션을 커밋한다.

결론: 질문하신 내용과 달리, Spring은 기본적으로 Checked Exception에 대해서는 롤백을 수행하지 않습니다.

롤백 정책 제어하기: @Transactional 속성

Spring은 개발자가 이 기본 동작을 제어할 수 있도록 강력한 옵션을 제공함

  • rollbackFor: 특정 Checked Exception이 발생했을 때도 롤백을 강제하고 싶을 때 사용함
    // JsonProcessingException (Checked Exception)이 발생해도 롤백하도록 설정
    @Transactional(rollbackFor = JsonProcessingException.class)
    public void processJson() throws JsonProcessingException {
        // ...
    }
    
  • noRollbackFor: 특정 Unchecked Exception이 발생했을 때 예외적으로 롤백을 막을 수 도 있음
    // MyBusinessAlertException (RuntimeException)이 발생해도 롤백하지 않도록 설정
    @Transactional(noRollbackFor = MyBusinessAlertException.class)
    public void processWithAlert() {
        // ...
    }
    

아키텍처 시나리오별 트랜잭션 전략


시나리오 A: 단일 데이터베이스 트랜잭션

가장 일반적인 경우로, 하나의 비즈니스 유스케이스가 단일 데이터베이스 내의 여러 테이블을 변경하는 상황

  • 전략: 유스케이스가 시작되는 가장 바깥쪽의 애플리케이션 서비스 메소드에 @Transactional을 선언함
  • 예시: TicketEventHandleSampleService.issue()
  • 동작: 이 메소드에서 시작된 트랜잭션은 내부적으로 호출되는 모든 Repository 메소드에 전파(propagation)되어, 모든 DB 작업이 하나의 원자적인 단위로 묶이며, 중간에 RuntimeException이 발생하면 모든 작업이 롤백됨

–>