1. 트랜잭션이란?
트랜잭션은 데이터베이스의 상태를 변환시키는 하나의 논리적 작업 단위를 말합니다. 쇼핑몰에서 주문을 처리할 때, 주문 정보 저장, 재고 감소, 결제 처리가 모두 성공해야만 전체 주문이 완료됩니다. 이 세 가지 작업을 하나의 트랜잭션으로 묶어서 처리하면, 중간에 문제가 생겼을 때 모든 작업을 원래대로 되돌릴 수 있습니다.
트랜잭션은 다음 네 가지 특성(ACID)을 가집니다.
- 원자성(Atomicity): 트랜잭션 내의 모든 작업은 전부 성공하거나 전부 실패합니다.
- 일관성(Consistency): 트랜잭션이 완료된 후에도 데이터베이스는 일관된 상태를 유지해야 합니다.
- 격리성(Isolation): 동시에 실행되는 트랜잭션들은 서로 영향을 미치지 않습니다.
- 지속성(Durability): 트랜잭션이 성공적으로 완료되면, 그 결과는 영구적으로 반영됩니다.
2. @Transactional 기본 개념
@Transactional은 말 그대로 트랜잭션(거래)의 경계를 설정해주는 어노테이션입니다.
하나의 작업 단위에서 모든 처리가 성공해야만 커밋되고, 하나라도 실패하면 롤백되어야 하는 경우에 사용합니다.
스프링에서는 트랜잭션을 관리하는 두 가지 방법이 있습니다:
- 프로그래밍 방식: 직접 코드로 트랜잭션을 시작, 커밋, 롤백하는 방식
- 선언적 방식: @Transactional 어노테이션을 사용하는 방식
대부분의 경우 @Transactional 어노테이션을 사용하는 선언적 방식이 더 간편하고 코드도 깔끔해집니다.
기본 사용법
import org.springframework.transaction.annotation.Transactional;
@Service
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Transactional
public void createUser(User user) {
userRepository.save(user);
// 이 메서드 내의 모든 데이터베이스 작업은 하나의 트랜잭션으로 처리됩니다.
// 중간에 예외가 발생하면 모든 변경사항이 롤백됩니다.
}
}
- 클래스에 적용: 해당 클래스의 모든 public 메서드에 트랜잭션이 적용됩니다.
- 메서드에 적용: 해당 메서드만 트랜잭션으로 처리됩니다.
3. 주요 속성 알아보기
@Transactional에는 다양한 속성이 있어 트랜잭션 동작을 세밀하게 제어할 수 있습니다.
대표적인 속성들
@Transactional(
propagation = Propagation.REQUIRED, // 트랜잭션 전파 방식
isolation = Isolation.DEFAULT, // 격리 수준
timeout = 30, // 타임아웃(초)
readOnly = false, // 읽기 전용 여부
rollbackFor = Exception.class, // 롤백을 발생시킬 예외
noRollbackFor = NotFoundException.class // 롤백을 발생시키지 않을 예외
)
timeout
트랜잭션의 제한 시간을 초 단위로 설정합니다. 지정한 시간이 지나면 트랜잭션이 자동으로 롤백됩니다.
readOnly
트랜잭션을 읽기 전용으로 설정합니다. 이 설정은 다음과 같은 이점이 있습니다:
- 데이터베이스에 읽기 전용이라는 힌트를 제공해 최적화 가능
- JPA/Hibernate의 경우 더티 체킹(변경 감지)을 하지 않아 성능 향상
rollbackFor / noRollbackFor
기본적으로 스프링은 RuntimeException과 Error가 발생했을 때만 롤백합니다. 하지만 이 속성들을 사용하면 롤백 조건을 직접 지정할 수 있습니다.
4. @Transactional의 내부 동작 원리
1. 프록시 기반 AOP(관점 지향 프로그래밍)
스프링의 @Transactional은 프록시 기반 AOP를 통해 동작합니다.
- 스프링이 @Transactional이 붙은 클래스에 대해 프록시 객체를 생성
- 클라이언트가 메서드를 호출하면 실제 객체가 아닌 프록시 객체가 호출을 가로채서 트랜잭션을 처리함
AOP란?
AOP는 공통 관심 사항(cross-cutting concerns)을 핵심 로직과 분리해서 코드를 깔끔하게 유지할 수 있도록 도와주는 프로그래밍 패러다임입니다.
예: 로깅, 보안 체크, 트랜잭션 처리
2. 트랜잭션 처리 흐름
@Transactional이 적용된 메서드가 호출되면 다음과 같은 순서로 처리됩니다.
[호출] → [트랜잭션 시작] → [원본 메서드 실행] → [정상: 커밋 / 예외: 롤백] → [종료]
- 트랜잭션 경계 설정: 프록시는 PlatformTransactionManager를 통해 트랜잭션을 시작합니다.
- 트랜잭션 속성 적용: @Transactional에 지정된 전파 방식, 격리 수준 등의 속성을 적용합니다.
- 원본 메서드 호출: 실제 비즈니스 로직이 담긴 원본 메서드를 실행합니다.
- 예외 처리 및 커밋/롤백 결정:
- 메서드가 정상적으로 완료되면 트랜잭션을 커밋합니다.
- 런타임 예외(RuntimeException)가 발생하면 트랜잭션을 롤백합니다.
- checked 예외(Exception)는 기본적으로 롤백하지 않습니다(rollbackFor 속성으로 변경 가능).
1) 트랜잭션 선언 (@Transactional)
개발자가 @Transactional 어노테이션을 메서드나 클래스에 붙이면
스프링은 프록시(Proxy) 객체를 생성하여 원래 객체를 감싸게 됩니다.
@Service
public class UserService {
@Transactional
public void registerUser(User user) {
userRepository.save(user);
logRepository.save(new Log("User created"));
}
}
2) 프록시가 메서드 호출 감지
사용자가 registerUser()를 호출하면 실제 객체가 아니라 프록시 객체가 호출을 가로채 다음 작업을 합니다:
- 트랜잭션이 이미 존재하는지 확인
- 없다면 새 트랜잭션 시작
- 대상 메서드 실행
- 정상 종료 → 커밋 / 예외 발생 → 롤백
3) 트랜잭션 경계 설정
스프링은 PlatformTransactionManager를 통해 트랜잭션을 실제로 관리합니다.
JPA를 사용할 경우 → JpaTransactionManager가 자동으로 등록됨.
TransactionStatus status = transactionManager.getTransaction(...);
try {
// 비즈니스 로직 수행
transactionManager.commit(status);
} catch (Exception e) {
transactionManager.rollback(status);
}
'개발 | 프로젝트 > Java | Spring' 카테고리의 다른 글
[Spring Boot] GlobalExceptionHandler 예외 처리 (0) | 2025.04.06 |
---|---|
[Spring Security] UsernamePasswordAuthenticationFilter Authentication Flow (0) | 2025.03.21 |
[Spring] Spring Security CSS 적용 안되는 오류 (0) | 2025.02.27 |
[Spring] Spring Security (0) | 2025.02.26 |
[Spring] 개방-폐쇄 원칙 (OCP, Open Closed Principle) (0) | 2025.02.18 |