개발기록
@Transactional: REQUIRES_NEW vs NESTED 전파 속성 비교 본문
Spring의 @Transactional 어노테이션에서 REQUIRES_NEW와 NESTED는 둘 다 새로운 트랜잭션을 시작하는 전파 속성이지만, 그 동작 방식에는 중요한 차이가 있습니다. 이 글에서는 두 속성의 차이점을 자세히 살펴보겠습니다.
REQUIRES_NEW
특징:
- 항상 새로운 트랜잭션을 시작합니다.
- 기존 트랜잭션이 있다면 그것을 일시 중단하고 새 트랜잭션을 시작합니다.
- 새 트랜잭션이 완료된 후 기존 트랜잭션을 재개합니다.
동작 방식:
- 기존 트랜잭션 존재 여부와 관계없이 새 트랜잭션을 시작합니다.
- 새 트랜잭션은 기존 트랜잭션과 완전히 독립적입니다.
- 새 트랜잭션이 롤백되어도 기존 트랜잭션에 영향을 주지 않습니다.
사용 예:
@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를 지원해야 합니다.
동작 방식:
- 기존 트랜잭션 내에서 savepoint를 생성합니다.
- 중첩 트랜잭션이 롤백되면 해당 savepoint로 롤백됩니다.
- 중첩 트랜잭션의 커밋/롤백은 최종적으로 외부 트랜잭션의 커밋/롤백에 종속됩니다.
사용 예:
@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의 트랜잭션 내에 중첩됨
}
}
주요 차이점
독립성:
- REQUIRES_NEW: 완전히 독립적인 새 트랜잭션을 시작합니다.
- NESTED: 외부 트랜잭션에 종속적인 중첩 트랜잭션을 시작합니다.
롤백 영향:
- REQUIRES_NEW: 내부 트랜잭션의 롤백이 외부 트랜잭션에 영향을 주지 않습니다.
- NESTED: 내부 트랜잭션의 롤백은 해당 부분만 롤백하고, 외부 트랜잭션은 계속 진행할 수 있습니다.
리소스 사용:
- REQUIRES_NEW: 새로운 데이터베이스 연결을 사용하므로 리소스 사용량이 증가할 수 있습니다.
- NESTED: 같은 데이터베이스 연결을 공유하므로 상대적으로 리소스 사용량이 적습니다.
데이터베이스 지원:
- REQUIRES_NEW: 모든 데이터베이스에서 지원됩니다.
- NESTED: Savepoint를 지원하는 데이터베이스에서만 사용 가능합니다 (예: Oracle, PostgreSQL).
결론
REQUIRES_NEW는 완전히 독립적인 트랜잭션이 필요할 때 사용합니다. 예를 들어, 주 트랜잭션의 성공 여부와 관계없이 반드시 수행해야 하는 작업(로깅, 알림 등)에 적합합니다.
NESTED는 주 트랜잭션의 일부로 실행되지만, 부분적인 롤백이 가능해야 할 때 사용합니다. 예를 들어, 배치 작업에서 일부 항목의 실패가 전체 배치를 롤백하지 않아야 할 때 유용합니다.
트랜잭션 전파 속성을 선택할 때는 비즈니스 요구사항, 성능 고려사항, 그리고 데이터 일관성 요구사항을 종합적으로 고려해야 합니다.
'Spring' 카테고리의 다른 글
| JPA 영속성 컨텍스트의 특징 (0) | 2024.09.15 |
|---|---|
| @Transactional(readOnly = true)의 모든 것 (1) | 2024.09.15 |
| N+1 문제와 해결 방법: Kotlin과 Spring Data JPA (0) | 2024.09.13 |
| @Configuration은 어떻게 싱글톤 빈을 보장하는가? (0) | 2024.09.10 |
| Spring AOP 내부 호출 문제 해결 방법 (0) | 2024.09.10 |
Comments