You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
기본 정책과 무관하게 특정 예외를 강제로 롤백하고 싶으면 rollbackFor 를 사용하면 된다. (해당 예외의 자식도 포함된다.)
rollbackFor = MyException.class 를 지정했기 때문에 MyException 이 발생하면 체크 예외이지만 트랜잭션이 롤백된다.
실행결과
Getting transaction for [...RollbackService.rollbackFor]
call rollbackFor
Completing transaction for [...RollbackService.rollbackFor] after exception:
MyException
Initiating transaction rollback
Rolling back JPA transaction on EntityManager
2️⃣ 예외와 트랜잭션 커밋, 롤백 - 활용
스프링은 왜 체크 예외는 커밋하고, 언체크 예외는 롤백할까 ?
스프링은 기본적으로 체크 예외늰 비즈니스 의미가 있을 때 사용하고, 런타임 예외는 복구 불가능한 예외로 가정한다.
체크 예외 : 비즈니스 의미가 있을 때 사용
언체크 예외 : 복구 불가능한 예외
이런 정책을 꼭 따를 필요는 없고, rollbackFor 라는 옵션을 사용해서 체크 예외도 롤백하면 된다.
비즈니스 의미가 있는 비즈니스 예외 라는 것이 무슨 뜻일까 ?
비즈니스 요구사항
주문을 하는데 상황에 따라 다음과 같이 조치한다.
정상 : 주문 시 결제를 성공하면 주문 데이터를 저장하고 결제 상태를 완료로 처리한다.
시스템 예외 : 주문 시 내부에 복구 불가능한 예외가 발생하면 전체 데이터를 롤백한다.
비즈니스 예외 : 주문 시 결제 잔고가 부족하면 주문 데이터를 저장하고, 결제 상태를 대기로 처리한다.
결제 잔고가 부족하면 NotEnoughMoneyException 이라는 체크 예외가 발생한다. 이 예외는 시스템에 문제가 있어서 발생하는 시스템 예외는 아니고, 시스템은 정상 동작했지만, 비즈니스 상황에서 문제가 되기 때문에 발생한 예외이다. 더 자세히 설명하자면 고객의 잔고가 부족한 것은 시스템에 문제가 있는 것이 아니다. 오히려 시스템은 문제 없이 동작한 것이고 비즈니스 상황이 예외인 것이다. 이런 예외를 비즈니스 예외라고 한다. 비즈니스 예외는 매우 중요하고, 반드시 처리해야 하는 경우가 많으므로 체크 예외를 고려할 수 있다.
예제를 단순하게 하기 위해 @Getter , @Setter 를 사용했다. 실무에서는 @Setter 를 남발해서 불필요한 변경 포인트를 노출하는 것은 좋지 않다.
@Table(name = "orders") 라고 했는데, 테이블 이름을 지정하지 않으면 테이블 이름이 클래스 이름인 order 가 된다. order 는 데이터베이스 예약어 (order by) 여서 사용할 수 없다. 그래서 orders 라는 테이블 이름을 따로 지정해주었다.
OrderRepository
packagehello.springtx.order;
importorg.springframework.data.jpa.repository.JpaRepository;
publicinterfaceOrderRepositoryextendsJpaRepository<Order, Long > {
}
OrderService
packagehello.springtx.order;
importlombok.RequiredArgsConstructor;
importlombok.extern.slf4j.Slf4j;
importorg.springframework.stereotype.Service;
importorg.springframework.transaction.annotation.Transactional;
@Slf4j@Service@RequiredArgsConstructorpublicclassOrderService {
privatefinalOrderRepositoryorderRepository;
//jpa 는 트랜잭션 커밋 시점에 Order 데이터를 DB에 반영한다.@Transactionalpublicvoidorder(Orderorder) throwsNotEnoughMoneyException {
log.info("order 호출");
orderRepository.save(order);
log.info("결제 프로세스 진입");
if (order.getUsername().equals("예외")) {
log.info("시스템 예외 발생");
thrownewRuntimeException("시스템 예외");
} elseif (order.getUsername().equals("잔고부족")) {
log.info("잔고 부족 비즈니스 예외 발생");
order.setPayStatus("대기");
thrownewNotEnoughMoneyException("잔고가 부족합니다");
} else {
//정상 승인log.info("정상 승인");
order.setPayStatus("완료");
}
log.info("결제 프로세스 완료");
}
}
여러 상황을 만들기 위해서 사용자 이름(username) 에 따라서 처리 프로세스를 다르게 했다.
기본 : PayStatus 를 완료상태로 처리하고 정상 처리된다.
예외 : RuntimeException("시스템 예외") 런타임 예외가 발생한다.
잔고부족
payStatus 를 대기 상태로 처리한다.
NotEnoughMoneyException("잔고가 부족합니다") 체크 예외가 발생한다.
잔고 부족은 payStatus 를 대기 상태로 두고, 체크 예외가 발생하지만, order 데이터는 커밋되기를 기대한다.
NotEnoughMoneyException 은 시스템에 문제가 발생한 것이 아니라, 비즈니스 문제 상황을 예외를 통해 알려준다. 마치 예외가 리턴 값처럼 사용된다. 이 경우에는 트랜잭션을 커밋하는 것이 맞다. 이 경우 롤백하면 생성한 Order 자체가 사라진다. 그러면 고객에게 잔고 부족을 알리고 별도의 게좌로 입금하도록 안내해도 주문(Order) 자체가 사라지기 때문에 문제가 된다.
비즈니스 상황에 따라 체크 예외의 경우에도 트랜잭션을 커밋하지 않고, 롤백하고 싶을 수 있다. 이 때는 rollbackFor 옵션을 사용하면 된다.
런타임 예외는 항상 롤백된다. 체크 예외의 경우 rollbackFor 옵션을 사용해서 비즈니스 상황에 따라서 커밋과 롤백을 선택하면 된다.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
1️⃣ 예외와 트랜잭션 커밋, 롤백 - 기본
@Transactional가 적용된 AOP) 밖으로 예외를 던지면 ?예외 발생 시 스프링 트랜잭션 AOP는 예외의 종류에 따라 트랜잭션을 커밋하거나 롤백한다.
언체크 예외인
RuntimeException,Error와 그 하위 예외가 발생하면 트랜잭션을 롤백한다.체크 예외인
Exception과 그 하위 예외가 발생하면 트랜잭션을 커밋한다.정상 응답하면 트랜잭션을 커밋한다.
RollbackTest
RuntimeException이 발생하므로 트랜잭션이 롤백된다.실행 결과

checkedException() 실행 - 체크 예외
MyException은Exception을 상속받은 체크 예외이다. 예외가 발생해도 트랜잭션이 커밋된다.실행 결과
이 옵션을 사용하면 기본 정책에 추가로 어떤 예외가 발생할 때 롤백할 지 지정할 수 있다.
예를 들어 이렇게 지정하면 체크 예외인
Exception이 발생해도 커밋 대신 롤백된다.기본 정책과 무관하게 특정 예외를 강제로 롤백하고 싶으면
rollbackFor를 사용하면 된다. (해당 예외의 자식도 포함된다.)rollbackFor = MyException.class를 지정했기 때문에MyException이 발생하면 체크 예외이지만 트랜잭션이 롤백된다.실행결과
2️⃣ 예외와 트랜잭션 커밋, 롤백 - 활용
스프링은 왜 체크 예외는 커밋하고, 언체크 예외는 롤백할까 ?
스프링은 기본적으로 체크 예외늰 비즈니스 의미가 있을 때 사용하고, 런타임 예외는 복구 불가능한 예외로 가정한다.
이런 정책을 꼭 따를 필요는 없고,
rollbackFor라는 옵션을 사용해서 체크 예외도 롤백하면 된다.비즈니스 의미가 있는 비즈니스 예외 라는 것이 무슨 뜻일까 ?
비즈니스 요구사항
주문을 하는데 상황에 따라 다음과 같이 조치한다.
결제 잔고가 부족하면
NotEnoughMoneyException이라는 체크 예외가 발생한다. 이 예외는 시스템에 문제가 있어서 발생하는 시스템 예외는 아니고, 시스템은 정상 동작했지만, 비즈니스 상황에서 문제가 되기 때문에 발생한 예외이다. 더 자세히 설명하자면 고객의 잔고가 부족한 것은 시스템에 문제가 있는 것이 아니다. 오히려 시스템은 문제 없이 동작한 것이고 비즈니스 상황이 예외인 것이다. 이런 예외를 비즈니스 예외라고 한다. 비즈니스 예외는 매우 중요하고, 반드시 처리해야 하는 경우가 많으므로 체크 예외를 고려할 수 있다.결제 잔고가 부족하면 발생하는 비즈니스 예외이다.
Exception을 상속 받아서 체크 예외가 된다.Order
JPA 를 사용하는
Order엔티티이다.예제를 단순하게 하기 위해
@Getter,@Setter를 사용했다. 실무에서는@Setter를 남발해서 불필요한 변경 포인트를 노출하는 것은 좋지 않다.@Table(name = "orders")라고 했는데, 테이블 이름을 지정하지 않으면 테이블 이름이 클래스 이름인order가 된다.order는 데이터베이스 예약어 (order by) 여서 사용할 수 없다. 그래서orders라는 테이블 이름을 따로 지정해주었다.OrderRepository
여러 상황을 만들기 위해서 사용자 이름(
username) 에 따라서 처리 프로세스를 다르게 했다.PayStatus를 완료상태로 처리하고 정상 처리된다.RuntimeException("시스템 예외")런타임 예외가 발생한다.payStatus를 대기 상태로 처리한다.NotEnoughMoneyException("잔고가 부족합니다")체크 예외가 발생한다.payStatus를 대기 상태로 두고, 체크 예외가 발생하지만,order데이터는 커밋되기를 기대한다.OrderServiceTest
실행하기 전에 아래와 같은 걸 추가하자. JPA 가 실행하는 SQL 을 로그로 확인할 수 있다.
테이블 생성하지 않았는데, 메모리 DB 를 통해 테스트를 수행하면 테이블 자동 생성 옵션이 활성화 된다. JPA 는 엔티티 정보를 참고해서 테이블을 자동으로 생성해준다.
complete()
사용자 이름을 정상으로 설정했다. 모든 프로세스가 정상 수행된다.
다음을 통해 데이터가 완료 상태로 저장되었는지 검증한다.
runtimeException()
사용자 이름을 예외로 설정했다.
RuntimeException("시스템 예외")가 발생한다.런타임 예외로 롤백이 수행되었기 때문에
Order데이터가 비어있는 것을 확인할 수 있다.bizException()
사용자 이름을 잔고부족 으로 설정했다.
NotEnoughMoneyException("잔고가 부족합니다")가 발생한다.체크 예외로 커밋이 수행되었기 때문에
Order데이터가 저장된다.다음을 통해 데이터가 대기 상태로 잘 저장 되었는지 검증한다.
정리
NotEnoughMoneyException은 시스템에 문제가 발생한 것이 아니라, 비즈니스 문제 상황을 예외를 통해 알려준다. 마치 예외가 리턴 값처럼 사용된다. 이 경우에는 트랜잭션을 커밋하는 것이 맞다. 이 경우 롤백하면 생성한Order자체가 사라진다. 그러면 고객에게 잔고 부족을 알리고 별도의 게좌로 입금하도록 안내해도 주문(Order) 자체가 사라지기 때문에 문제가 된다.rollbackFor옵션을 사용하면 된다.rollbackFor옵션을 사용해서 비즈니스 상황에 따라서 커밋과 롤백을 선택하면 된다.All reactions