Java Framework/Spring

[Spring] @Transactional

annovation 2025. 1. 30. 08:50

@Transactional

@Transactional은 Spring에서 트랜잭션의 시작과 종료를 자동으로 관리해주는 애노테이션입니다. 이 애노테이션은 데이터의 일관성을 보장하기 위해 트랜잭션 경계를 설정하며, 작업이 실패할 경우 자동으로 롤백합니다.

  • 트랜잭션 관리를 자동화 : 개발자가 직접 트랜잭션을 열고 닫는 코드를 작성할 필요 없음
  • 데이터 일관성 보장 : 작업 중 하나라도 실패하면 데이터베이스에 반영되지 않도록 롤백 처리
  • 동시성 문제 방지 : 여러 사용자가 동시에 데이터를 처리할 때 일관성을 유지

DB 세션

데이터베이스(DB) 세션(Session)은 애플리케이션과 데이터베이스 간의 연결 상태를 말합니다. 이 세션이 끊어지는 경우 @Transactional를 통해 해결할 수 있습니다.

 

1. Lazy Loading과 세션 종료

Lazy Loading은 연관 데이터를 처음부터 가져오지 않고, 실제로 필요할 때 데이터베이스에서 조회하는 방식입니다.

  • 문제 상황
    • 트랜잭션이 종료되면 세션도 닫힙니다.
    • 세션이 닫힌 상태에서 Lazy Loading을 호출하면 데이터베이스에 접근할 수 없으므로 LazyInitializationException이 발생합니다.
@Transactional
public Order getOrder(Long orderId) {
    Order order = orderRepository.findById(orderId).orElseThrow();
    return order; // Lazy Loading으로 order.items를 접근하려 하면 문제가 발생
}
  • order.items가 Lazy Loading으로 설정된 경우, 트랜잭션이 종료된 후 접근하면 예외가 발생합니다.

2. 예외 발생으로 인한 트랜잭션 종료

트랜잭션 내에서 예외가 발생하면 트랜잭션이 롤백되고, 데이터베이스 세션도 종료됩니다.

  • 문제 상황 
    • 트랜잭션 종료 후 데이터를 수정하거나 조회하려 하면 세션이 없으므로 오류가 발생합니다.

@Transactional로 문제 해결 

1. Lazy Loading 문제 해결

Lazy Loading 문제를 방지하려면 트랜잭션 범위 내에서 모든 데이터를 초기화해야 합니다.

  • 해결 방법
    1. 트랜잭션을 유지한 상태에서 Lazy Loading 데이터를 로드
    2. 트랜잭션이 종료되기 전에 데이터를 미리 가져오기
@Transactional
public Order getOrderWithItems(Long orderId) {
    Order order = orderRepository.findById(orderId).orElseThrow();
    order.getItems().size(); // Lazy Loading 데이터 강제 초기화
    return order;
}

 

 

2. 작업 중 예외 발생 시 rollback

트랜잭션 내에서 예외가 발생하면 @Transactional이 자동으로 트랜잭션을 롤백하여 데이터의 일관성을 보장합니다.

@Transactional
public void transferMoney(Long fromAccountId, Long toAccountId, double amount) {
    Account fromAccount = accountRepository.findById(fromAccountId).orElseThrow();
    Account toAccount = accountRepository.findById(toAccountId).orElseThrow();

    fromAccount.debit(amount);
    toAccount.credit(amount);

    if (amount > 1000) {
        throw new RuntimeException("Transfer limit exceeded");
    }

    accountRepository.save(fromAccount);
    accountRepository.save(toAccount);
}
  • 트랜잭션 내에서 예외가 발생하면 데이터는 자동으로 롤백됩니다.

@Transactional의 주요 옵션 

1. readOnly

데이터 조회 작업에서 @Transactional(readOnly = true)를 사용하면 데이터베이스 성능을 최적화할 수 있습니다.

@Transactional(readOnly = true)
public List<Order> getAllOrders() {
    return orderRepository.findAll();
}

 

2. rollbackFor

기본적으로 @TransactionalRuntimeException에서만 롤백합니다. 특정 예외에서 롤백하려면 rollbackFor옵션을 사용합니다.

@Transactional(rollbackFor = CustomException.class)
public void processOrder(Order order) throws CustomException {
    // CustomException 발생 시 롤백
}

 

3. propagation

트랜잭션의 전파 방식을 설정합니다. 기본값은 REQUIRED이며, 기존 트랜잭션이 있으면 이를 사용하고 없으면 새로 생성합니다.

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveLog(String message) {
    // 새로운 트랜잭션으로 로그 저장
}

주의

  1. Spring 관리 빈에서만 작동
    • @Transactional은 Spring이 관리하는 클래스에서만 동작합니다. 내부 메서드 호출에는 적용되지 않습니다.
  2. 트랜잭션 경계
    • 트랜잭션 범위 내에서 모든 작업을 완료해야 합니다. 트랜잭션 종료 후 데이터를 수정하거나 조회하려 하면 오류가 발생할 수 있습니다.
  3. 성능 최적화
    • 읽기 전용 작업에는 readOnly = true를 설정하여 성능을 향상시킬 수 있습니다.

출처

OpenAI ChatGPT (https://openai.com)

'Java Framework > Spring' 카테고리의 다른 글

[Spring] @Autowired  (1) 2025.02.02
[Spring] @GetMapping  (0) 2025.01.31
[Spring] HTTP 요청 어노테이션 (Annotation)  (0) 2024.12.18
[Spring] URL 프리픽스(prefix)  (1) 2024.12.17
[Spring Boot] MVC (Model View Controller) 패턴  (0) 2024.12.12