Java 아키텍처·Spring 용어 사전
트랜잭션선언적 트랜잭션 · rollbackFor · 롤백 규칙

@Transactional

메서드에 트랜잭션 경계를 선언하는 애노테이션. 프록시로 동작하며 기본은 언체크 예외만 롤백한다.

메서드에 트랜잭션 경계를 선언하는 애노테이션. 내부적으로는 AOP 프록시가 setAutoCommit(false)commit/rollback을 대신 호출한다.

무엇을 대신해 주나

// 이 한 줄이
@Transactional
void transfer(...) { ... }

// 이걸 대신한다
try (Connection con = ds.getConnection()) {
    con.setAutoCommit(false);
    try { /* 본문 */ con.commit(); }
    catch (Exception e) { con.rollback(); throw e; }
}

가장 많이 데는 함정 — 검사 예외는 롤백되지 않는다

@Transactional
void order() throws IOException {
    repo.save(o);
    throw new IOException("실패");    // 검사 예외 → 커밋된다!
}
기본 롤백 규칙
  RuntimeException · Error       → 롤백
  Exception (검사 예외)          → 커밋   ← 직관과 반대다
@Transactional(rollbackFor = Exception.class)   // 모든 예외에 롤백

이 기본값은 EJB 시절의 관례를 물려받은 것이라 논리적 필연이 아니다. 그래서 더 헷갈린다.

프록시라서 생기는 제약

  • private · final 메서드 — → 프록시가 오버라이드 못 한다 → 무시된다
  • 자기 호출 — → 프록시를 안 거친다 → 무시된다
  • 빈이 아닌 객체 — → 프록시 자체가 없다

아무 예외 없이 조용히 무시된다는 점이 위험하다.

예외를 잡아먹으면 롤백되지 않는다

@Transactional
void outer() {
    try { inner(); }
    catch (Exception e) { log.error("무시", e); }   // 삼켰다 → 커밋된다
}

다만 내부 트랜잭션이 rollback-only로 표시한 경우는 다르다.

@Transactional(REQUIRED) 인 inner 가 예외로 실패

  • 같은 트랜잭션에 rollback-only 표시가 붙는다
  • outer 가 예외를 삼켜도 커밋 시점에
    • UnexpectedRollbackException: Transaction silently rolled back

readOnly의 실제 효과

@Transactional(readOnly = true)
  • Hibernate 가 스냅샷을 만들지 않는다 → 메모리·변경 감지 비용 절약
  • flush 모드가 MANUAL 이 되어 불필요한 flush 가 사라진다
  • 일부 환경에서 읽기 전용 복제본으로 라우팅되는 근거가 된다

조회 전용 메서드에 붙이는 습관이 실무 기본값이다.

면접 함정

  • "예외가 나면 무조건 롤백" → 검사 예외는 커밋된다.
  • "트랜잭션 안에서 외부 API를 불러도 된다" → 그동안 커넥션이 묶인다. 밖으로 빼거나 커밋 후로 미룬다.

클래스에 붙일 때의 범위

@Service
@Transactional(readOnly = true)          // 클래스 기본값: 읽기 전용
class OrderService {
    public Order find(Long id) { ... }   // readOnly = true 적용

    @Transactional                        // 메서드에서 재정의 (쓰기)
    public void create(...) { ... }
}

"클래스에 readOnly, 쓰기 메서드만 재정의" 가 널리 쓰이는 관용구다. 조회에 실수로 쓰기가 섞이는 것도 막아 준다.

타임아웃을 건다

@Transactional(timeout = 5)     // 5초를 넘기면 롤백시킨다

느린 쿼리 하나가 커넥션과 락을 오래 쥐는 것을 막는 안전장치다. 긴 배치에는 반드시 명시한다.

트랜잭션 밖으로 빼야 할 것들

  • 외부 API 호출 — 느린 I/O 가 커넥션과 락을 인질로 잡는다

  • 메일 · 알림 발송 — 롤백돼도 되돌릴 수 없다 → AFTER_COMMIT 으로

  • 파일 업로드 — 시간이 오래 걸린다

  • 대용량 계산 — DB 와 무관한 CPU 작업

  • "트랜잭션은 DB 작업만, 짧게" 가 원칙이다

함께 보면 좋은 용어

노트에서 맥락과 함께 보기 — 트랜잭션 — 전파·격리·롤백 함정