@Transactional은 Spring Framework에서 제공하는 어노테이션으로, 메소드나 클래스에 적용하여 트랜잭션 경계를 설정하는 데 사용됩니다. 트랜잭션은 데이터베이스에서 여러 작업을 하나의 작업 단위로 묶어서 처리하며, 모든 작업이 성공적으로 완료되거나 실패 시 모두 롤백되는 특성을 가지고 있습니다. @Transactional은 이 트랜잭션을 쉽게 처리할 수 있도록 도와줍니다.
주요기능
-
트랜잭션 시작: @Transactional이 적용된 메소드가 호출되면, 해당 메소드 실행 전에 트랜잭션이 시작됩니다. 트랜잭션은 데이터베이스와의 연결에서 사용되며, 여러 쿼리를 하나의 작업 단위로 묶습니다.
-
트랜잭션 커밋: 메소드가 성공적으로 끝나면 트랜잭션이 커밋됩니다. 커밋되면 트랜잭션 내에서 이루어진 모든 데이터베이스 변경 사항이 영구적으로 반영됩니다.
-
트랜잭션 롤백: 메소드 실행 중 예외가 발생하면 트랜잭션이 롤백됩니다. 롤백되면 트랜잭션 내에서 이루어진 모든 데이터베이스 변경 사항이 무효화되며, 데이터가 원래 상태로 돌아갑니다. 기본적으로 RuntimeException 및 그 하위 예외가 발생했을 때 트랜잭션이 롤백됩니다.
작동 원리
@Transactional을 사용하면 Spring AOP(Aspect-Oriented Programming) 기반으로 트랜잭션 관리를 처리합니다. 이를 더 구체적으로 설명하면, @Transactional이 붙은 메서드를 호출할 때 Spring이 동적으로 프록시 객체를 생성하여 트랜잭션 경계(transaction boundary)를 관리합니다.
- 프록시 생성
- Spring은 @Transactional이 적용된 메서드가 포함된 빈(Bean)에 대해 동적으로 프록시 객체를 생성합니다. 이 프록시는 원래 메서드를 호출하기 전에 트랜잭션을 시작하고, 메서드가 완료되면 트랜잭션을 커밋하거나 롤백하는 작업을 수행합니다.
- 트랙잭션 시작
- 프록시가 호출되면 Spring은 먼저 트랜잭션을 시작합니다. 구체적으로는 PlatformTransactionManager를 사용하여 트랜잭션을 시작하고, 데이터베이스 연결을 가져와 트랜잭션 경계를 설정합니다. 이때 트랜잭션 속성(예: 전파, 격리 수준 등)은 @Transactional에 설정된 대로 적용됩니다.
- 메서드 실행
- 트랜잭션이 시작된 후에 실제 메서드를 실행합니다. 이 메서드 내에서는 데이터베이스 작업이나 다른 트랜잭션 관련 작업이 수행됩니다
- 트랜잭션 커밋 또는 롤백
- 메서드가 성공적으로 실행되면 트랜잭션이 커밋됩니다. 만약 메서드 실행 중 예외가 발생하면 트랜잭션은 롤백됩니다. 기본적으로 RuntimeException이나 Error가 발생하면 트랜잭션이 롤백되고, Checked Exception은 롤백되지 않습니다. 하지만 @Transactional의 rollbackFor 속성을 사용하여 롤백할 예외를 직접 지정할 수도 있습니다.
속성
@Transactional 애너테이션에는 다양한 속성이 있어 트랜잭션의 동작을 세밀하게 조정할 수 있습니다.
- propagation: 트랜잭션의 전파 방식을 설정합니다.
- REQUIRED (기본값): 기존 트랜잭션이 있으면 그 트랜잭션에 참여하고, 없으면 새로운 트랜잭션을 생성합니다.
- REQUIRES_NEW: 항상 새로운 트랜잭션을 생성하며, 기존 트랜잭션이 있으면 일시 중지합니다.
- SUPPORTS: 트랜잭션이 있으면 참여하지만, 없으면 트랜잭션 없이 실행됩니다.
- NOT_SUPPORTED: 트랜잭션 없이 실행하며, 기존 트랜잭션이 있으면 일시 중지합니다.
- MANDATORY: 트랜잭션이 반드시 존재해야 하며, 없으면 예외가 발생합니다.
- NEVER: 트랜잭션 없이 실행되며, 트랜잭션이 있으면 예외가 발생합니다.
- NESTED: 중첩 트랜잭션을 생성합니다. 기존 트랜잭션 내에서 중첩된 트랜잭션을 독립적으로 커밋 또는 롤백할 수 있습니다.
- isolation: 트랜잭션의 격리 수준을 설정합니다. 이 설정은 여러 트랜잭션이 동시에 실행될 때 데이터의 일관성을 어떻게 보장할지를 정의합니다.
- DEFAULT (기본값): 데이터베이스의 기본 격리 수준을 사용합니다.
- READ_UNCOMMITTED: 트랜잭션에서 커밋되지 않은 변경사항을 다른 트랜잭션에서 읽을 수 있습니다.
- READ_COMMITTED: 커밋된 변경사항만 읽을 수 있습니다.
- REPEATABLE_READ: 트랜잭션 내에서 동일한 데이터를 여러 번 읽어도 같은 결과를 보장합니다.
- SERIALIZABLE: 가장 엄격한 격리 수준으로, 트랜잭션이 완전히 직렬화되어 실행됩니다.
- timeout: 트랜잭션이 지정된 시간 내에 완료되지 않으면 롤백됩니다. 기본값은 무제한입니다.
- readOnly: 읽기 전용 트랜잭션을 지정할 수 있습니다. 이는 데이터베이스에 쓰기 작업이 발생하지 않음을 보장하며, 성능 최적화에 도움이 될 수 있습니다.
- rollbackFor: 지정된 예외가 발생했을 때 트랜잭션을 롤백하도록 설정합니다. 기본적으로 RuntimeException과 그 하위 클래스에 대해서만 롤백하지만, 이 속성을 사용하여 CheckedException에도 롤백을 적용할 수 있습니다.
- noRollbackFor: 특정 예외가 발생했을 때는 롤백하지 않도록 설정합니다.
예시
@Service
public class MyService {
@Transactional
public void performTransaction() {
}
@Transactional(rollbackFor = Exception.class)
public void performTransactionWithExceptionHandling() throws Exception {
}
@Transactional(readOnly = true)
public void performReadOnlyTransaction() {
}
}