[JAVA] Spring에서 트랜잭션 관리하기 – @Transactional 완벽 이해

Spring Framework는 데이터 무결성을 보장하고 롤백 처리까지 자동화하는 선언적 트랜잭션 관리를 제공합니다. 그 중심에 있는 것이 바로 @Transactional 어노테이션입니다. 실무에서 자주 마주치는 트랜잭션 관련 문제와 함께, @Transactional의 핵심 개념과 활용법을 정리해 보았습니다.

 

1. 트랜잭션이란 무엇인가?

트랜잭션(Transaction)은 하나의 논리적 작업 단위를 의미합니다. 데이터베이스에서 여러 작업이 함께 수행되어야 할 때, 일부 작업만 완료되고 나머지가 실패하면 데이터의 일관성이 깨집니다. 그래서 다음 네 가지 특징을 만족해야 합니다 (ACID):

  • Atomicity (원자성): 모두 성공하거나, 전부 실패해야 함
  • Consistency (일관성): 일관된 상태로 유지되어야 함
  • Isolation (고립성): 동시에 실행 중인 트랜잭션은 서로 간섭하지 않아야 함
  • Durability (지속성): 커밋된 트랜잭션은 영구 반영되어야 함

 

2. @Transactional의 기본 개념

Spring에서는 트랜잭션을 선언적으로 처리할 수 있도록 @Transactional 어노테이션을 제공합니다. 이 어노테이션을 메서드 또는 클래스에 붙이면, 트랜잭션 시작 → 메서드 수행 → 커밋 또는 롤백 흐름이 자동으로 적용됩니다.

@Transactional
public void registerUser(User user) {
    userRepository.save(user);
    emailService.sendWelcomeMail(user); // 예외 발생 시 모두 롤백
}

클래스 vs 메서드 단위 적용

  • 클래스에 적용: 모든 public 메서드에 적용
  • 메서드에 적용: 해당 메서드에만 적용

 

3. 주요 속성 정리

속성 설명
propagation 기존 트랜잭션 존재 시 어떻게 처리할지 (기본: REQUIRED)
isolation 격리 수준 설정 (기본: DEFAULT → DB 기본값 사용)
rollbackFor 어떤 예외가 발생할 때 롤백할지 지정 (기본은 RuntimeException만)
readOnly 읽기 전용 여부 (쓰기 불가 트랜잭션)
timeout 제한 시간 초과 시 롤백 (초 단위)

 

예시: 커스텀 롤백 조건

@Transactional(rollbackFor = {IOException.class})
public void saveWithIO() throws IOException {
    ...
}

 

4. 실무에서 자주 겪는 이슈

1) 내부 메서드 호출 시 트랜잭션 무시됨

동일 클래스 내에서 this.method() 방식으로 호출하면 프록시를 통하지 않아 @Transactional이 적용되지 않습니다.Service를 분리하거나 self-injection 기법이 필요합니다.

2) 예외가 발생했는데 롤백되지 않음

기본적으로 RuntimeException만 롤백 대상입니다. 예외가 checked exception인 경우 rollbackFor를 명시해야 합니다.

3) readOnly = true인데 INSERT 수행

MySQL 등에서는 readOnly 설정이 강제 제한이 되지 않습니다. 하지만 Hibernate는 내부적으로 flush 생략 등의 최적화를 수행하므로 읽기 성능 개선에 도움을 줍니다.

 

5. Propagation 옵션 정리

옵션 설명
REQUIRED 기존 트랜잭션이 있으면 참여, 없으면 새로 생성 (기본값)
REQUIRES_NEW 기존 트랜잭션을 일시 중단하고 새 트랜잭션 생성
NESTED Savepoint를 만들어 부분 롤백 가능 (JDBC 지원 필요)
NOT_SUPPORTED 트랜잭션 없이 실행
NEVER 트랜잭션 존재 시 예외 발생

 

6. 실전 적용 팁

  • 외부 API 호출은 가급적 트랜잭션 외부에서 실행 (rollback 영향 없음)
  • 읽기 전용 쿼리에는 readOnly=true로 최적화
  • 복잡한 처리 로직은 트랜잭션 분리하여 재시도 전략 적용 (e.g. 결제 처리)
  • 테스트 코드에도 @Transactional 사용하면 테스트 후 자동 롤백 가능

 

마무리

Spring에서의 트랜잭션 관리는 복잡한 로직을 안정적으로 처리할 수 있게 해주는 중요한 기초입니다. 하지만 @Transactional의 기본 동작 원리를 정확히 이해하지 못하면, 트랜잭션이 적용되지 않는 버그로 이어질 수 있습니다. 트랜잭션 동작 방식과 다양한 실전 이슈를 확실히 정리하고 넘어가시기 바랍니다.