개발기록

@Transactional: REQUIRES_NEW vs NESTED 전파 속성 비교 본문

Spring

@Transactional: REQUIRES_NEW vs NESTED 전파 속성 비교

Danuvibe 2024. 9. 13. 15:31

Spring의 @Transactional 어노테이션에서 REQUIRES_NEW와 NESTED는 둘 다 새로운 트랜잭션을 시작하는 전파 속성이지만, 그 동작 방식에는 중요한 차이가 있습니다. 이 글에서는 두 속성의 차이점을 자세히 살펴보겠습니다.

REQUIRES_NEW

특징:

  • 항상 새로운 트랜잭션을 시작합니다.
  • 기존 트랜잭션이 있다면 그것을 일시 중단하고 새 트랜잭션을 시작합니다.
  • 새 트랜잭션이 완료된 후 기존 트랜잭션을 재개합니다.

동작 방식:

  1. 기존 트랜잭션 존재 여부와 관계없이 새 트랜잭션을 시작합니다.
  2. 새 트랜잭션은 기존 트랜잭션과 완전히 독립적입니다.
  3. 새 트랜잭션이 롤백되어도 기존 트랜잭션에 영향을 주지 않습니다.

사용 예:

@Service
class UserService(private val paymentService: PaymentService) {

    @Transactional
    fun registerUser(user: User) {
        // 사용자 등록 로직
        userRepository.save(user)

        try {
            paymentService.processPayment(user)
        } catch (e: Exception) {
            // 결제 실패 로그 기록
            logger.error("Payment failed for user: ${user.id}")
        }

        // 사용자 등록은 결제 성공 여부와 관계없이 완료됨
    }
}

@Service
class PaymentService {

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    fun processPayment(user: User) {
        // 결제 처리 로직
        // 이 메소드의 트랜잭션은 registerUser의 트랜잭션과 독립적
    }
}

NESTED

특징:

  • 기존 트랜잭션이 있는 경우, 중첩 트랜잭션(nested transaction)을 시작합니다.
  • 기존 트랜잭션이 없는 경우, REQUIRED와 동일하게 새 트랜잭션을 시작합니다.
  • 데이터베이스가 savepoint를 지원해야 합니다.

동작 방식:

  1. 기존 트랜잭션 내에서 savepoint를 생성합니다.
  2. 중첩 트랜잭션이 롤백되면 해당 savepoint로 롤백됩니다.
  3. 중첩 트랜잭션의 커밋/롤백은 최종적으로 외부 트랜잭션의 커밋/롤백에 종속됩니다.

사용 예:

@Service
class OrderService(private val inventoryService: InventoryService) {

    @Transactional
    fun placeOrder(order: Order) {
        // 주문 저장
        orderRepository.save(order)

        try {
            inventoryService.updateInventory(order)
        } catch (e: Exception) {
            // 재고 업데이트 실패 시 해당 부분만 롤백
            logger.error("Inventory update failed for order: ${order.id}")
        }

        // 주문 처리 계속 진행
    }
}

@Service
class InventoryService {

    @Transactional(propagation = Propagation.NESTED)
    fun updateInventory(order: Order) {
        // 재고 업데이트 로직
        // 이 메소드의 트랜잭션은 placeOrder의 트랜잭션 내에 중첩됨
    }
}

주요 차이점

  1. 독립성:

    • REQUIRES_NEW: 완전히 독립적인 새 트랜잭션을 시작합니다.
    • NESTED: 외부 트랜잭션에 종속적인 중첩 트랜잭션을 시작합니다.
  2. 롤백 영향:

    • REQUIRES_NEW: 내부 트랜잭션의 롤백이 외부 트랜잭션에 영향을 주지 않습니다.
    • NESTED: 내부 트랜잭션의 롤백은 해당 부분만 롤백하고, 외부 트랜잭션은 계속 진행할 수 있습니다.
  3. 리소스 사용:

    • REQUIRES_NEW: 새로운 데이터베이스 연결을 사용하므로 리소스 사용량이 증가할 수 있습니다.
    • NESTED: 같은 데이터베이스 연결을 공유하므로 상대적으로 리소스 사용량이 적습니다.
  4. 데이터베이스 지원:

    • REQUIRES_NEW: 모든 데이터베이스에서 지원됩니다.
    • NESTED: Savepoint를 지원하는 데이터베이스에서만 사용 가능합니다 (예: Oracle, PostgreSQL).

결론

  • REQUIRES_NEW는 완전히 독립적인 트랜잭션이 필요할 때 사용합니다. 예를 들어, 주 트랜잭션의 성공 여부와 관계없이 반드시 수행해야 하는 작업(로깅, 알림 등)에 적합합니다.

  • NESTED는 주 트랜잭션의 일부로 실행되지만, 부분적인 롤백이 가능해야 할 때 사용합니다. 예를 들어, 배치 작업에서 일부 항목의 실패가 전체 배치를 롤백하지 않아야 할 때 유용합니다.

트랜잭션 전파 속성을 선택할 때는 비즈니스 요구사항, 성능 고려사항, 그리고 데이터 일관성 요구사항을 종합적으로 고려해야 합니다.

Comments