ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Checked Exception은 커밋? Unchecked Excpetion은 롤백? 스프링에서 @Transactional 으로 rollBack 다뤄보기
    Java 2023. 10. 23. 04:09

    https://www.youtube.com/watch?v=_WkMhytqoCc

    백기선님 유튜브

    최근 트랜잭션에 대해 공부중이었는데 알고리즘이 나를 해당 영상으로 이끌었다.

     

    간략하게 설명을 하면 

     

    - 컴파일 타임에 꼭 처리해야하는 checked Exception은 커밋으로 처리하기

     

    - 런타임에 처리해야하는 unchecked Exception은 롤백 처리하기

     

    이 두가지는 스프링 프레임워크에서 트랜잭션 실행시 작동하는 기본 전략이다. (외에 여러 옵션이 있다.)

     

    그런데 자바의 Checked Exception 은 커밋으로 처리해야하고, Unchecked Exception은 롤백해야한다. 라고 잘못 퍼져나가는것에 대한 비판이 담긴 영상이다. 

     

    나도 사실  spring에서 transaction을  @Transactional 기본만 사용해봐서 정확히 어떤 상황에 어떻게 처리해야하는지 모른다.

    그저 @Transaction 걸어놓고 되기만을 바랄뿐...

     

     

    오늘 공부하는 내용은 @Transactional(어노테이션을 이용한 선언적 방식)을 사용한다.

    TransactionManager를 주입받아서 일일이 setAutoComit 하고 comit 적고 rollback 하는것을 프로그래밍 방식이라 하는데,

    실무에서는 훨씬 간편하고 실용적인 @Transactional을 대게 사용하기 때문이다. 


    우선 위에 나온 스프링의 기본전략 

     

    스프링에서 checked exception 을 커밋으로 처리하고 unchecked exception 을 롤백처리하는 이유는

    • IOException -> 파일 읽고 쓸때 발생할수 있는 예외
    • SQLException -> DB작업시 일어나는 예외
    • ClassNotFoundException ->클래스를 찾을 수 없을때 발생하는 예외

     

    애플리케이션이 작동하는데서 일어나기보다 외부에서 일어나는 문제다.

     

    그래서 스프링은 '이정도는 너네도 예상했을텐데.. 그냥 진행시켰네? 비즈니스 로직 중요한가보다? 그럼 그냥 진행시켜~ ' 라고 생각해서 그냥 커밋을 때려버리는 것이다. 

     

    반면 unchecked exception은 말그대로 RuntimeException으로  애플리케이션 작동하는 범위에서 일어나는 Exception이기에,

    스프링은 '너네 예상 못한거니까 롤백처리 해줄게' 라는 생각으로 기본 전략을 세운것이다. 

     


    1. rollbackFor

     

    @Transaction(rollbackFor = Exception.class ) 

    원하는 예외에 대해서 롤백을 시켜버릴수있다. (롤백하지 않는 checked exception의 경우)

     

    2. noRollbackFor

     

    @Transaction(norollbackFor = RuntimeException.class ) 
    원하는 예외에 대해서는 롤백을 하고싶지 않을때 사용한다. (롤백을 하는 unchecked exception의 경우)

     


    그렇지만 우리가 고려해야할 케이스들은 너무나 다양하다.

    예시) 한 쇼핑몰에서..

    옷 골랐고~

    결제해야지~~

    응??잔액 부족이네??

    이런 상황에 그냥 롤백 처리해야할까?

    롤백처리하면 결제하기까지의 과정을 전부 다시해야되는데...?

    만약 첫주문이라면, 주소지도 새로 기입하고 쿠폰도 입력했는데 아오ㅋㅋ

    결제가 필요한 사이트에서 해당 페이지에서 재결제시 주소지를 다시 안써도 되는곳을 여러번 봤었다.

     

    롤백 처리보다는 보류를 생각해볼수도 있지 않을까?

     

    즉 커밋은 하되 상태를 결제완료가 아닌 다르게 처리하면 되지 않을까? 라는 생각이 들었다. 

     


    코드로 알아보자

     

    NotEnoughMoneyException 은 UncheckedException을  Checked Exception으로 바꿔 처리하기 위한것이다.

    public class NotEnoughMoneyException extends Exception {
    	public NotEnoughMoneyException(String message) {
       		 super(message);
            } 
    }

    Order 

    @Entity
    @Table(name = "orders")
    @Getter
    @Setter
    public class Order {
        @Id
        @GeneratedValue
        private Long id;
        private String username; 
        private String payStatus;
    }

    OrderRepository

    public interface OrderRepository extends JpaRepository<Order, Long> {
      }

     

    OrderService

    @Slf4j
    @Service
    @RequiredArgsConstructor
    public class OrderService {
        private final OrderRepository orderRepository;
    
        //JPA는 트랜잭션 커밋 시점에 Order 데이터를 DB에 반영한다.
        @Transactional
        public void order(Order order) throws NotEnoughMoneyException {
            log.info("order 호출");
            orderRepository.save(order);
            log.info("결제 프로세스 진입");
            if (order.getUsername().equals("시스템 자체 문제 발생")) {
                throw new RuntimeException("시스템 문제");
            } else if (order.getUsername().equals("잔고부족")) {
                order.setPayStatus("대기");
                throw new NotEnoughMoneyException("잔고가 부족합니다");
            } else { //정상 승인
                order.setPayStatus("완료");
            }
        }
    }

     

     

    애플리케이션 시스템 문제 발생 -> RuntimeException으로 롤백처리

     

    잔고부족(시스템 문제가 아니다) -> Exception을 상속받은 커스텀 Exception으로 커밋 처리후, Order의 상태를 '대기'로 변경

     

    그외 -> 정상 커밋(완료)

     

     

     

    이를 통해 특정 예외에 따라 롤백을 해야하니,하지말아야하니는 의미가 없다고 배웠다.

    트랜잭션 Exception을 가지고 놀수있는 그날까지.. 화이팅~~

Designed by Tistory.