본문 바로가기
프로젝트/자리 있어요?

[Refactoring] 22.09.01. Transaction과 Exception

by 규글 2022. 9. 1.

 트랜잭션에 대해 검색해보던 중 다른 이들은 exception과 함께 포스팅한 내용들을 보게 되었다. 따라서 해당 내용을 기반으로 알아보고 정리한 뒤 트랜잭션 처리를 마무리 지으려고 한다.

 

Transaction과 Exception

Error와 Exception

 Java docs에 exception을 검색해보면 Throwable을 부모 class로 두고 있고, 그 Throwable은 Exception 외에도 Error에서도 상속받고 있다. [각주:1] 그리고 Exception은 당연히 예외 처리를 위한 모든 class에서 상속받고 있을테지만 여기에서 중요한 것은 RuntimeException이다.

 

 Throwable은 Error와 Exception class에서 상속받고 있고, 각각은 또 여러 Error와 Exception 객체에서 상속받고 있다.

 

 먼저 'Error'는 메모리 부족이나 스택 오버 플로우 (Stack Overflow) 와 같이 발생한 경우 복구할 수 없는 치명적인 문제를 말한다. Compile 시에 존재하는 문법적인 오류나 기타의 이유로 process에 심각한 문제를 발생시켜서 그 process 자체를 종료시킬 수 있다. 시스템의 레벨에서 발생하는 문제라고도 할 수 있어서 예측과 처리가 쉽지 않다.

 반면 'Exception'은 Error 보다는 조금 나아서 문제가 발생하더라도 수습할 수 있다. 이미지에서 볼 수 있는 Null 값을 참조해서 value를 얻으려 하거나 method를 사용해서 발생하는 NullPointerException과 List나 array에 그 길이를 벗어나는 index를 요구해서 발생하는 IndexOutOfBoundException이 대표적이다. 그리고 Error가 발생할 수 있는 상황을 사전에 방지하기 위해서 Exception 상황을 따로 만들 수도 있다고 한다.

 

Checked Exception과 Unchecked Exception


Checked Exception Unchecked Exception
확인 시점 Compile Runtime
발생하는 경우 존재하지 않는 file에 대한 처리
Class 이름 작성 오류
data format의 오류
SQL 문의 오류
값이 Nullvariable을 참조
배열의 범위를 벗어남
형 변환을 잘못함
값을 0으로 나누려는 시도
예외 처리 여부 명시적으로 처리해주어야 함 명시적인 처리를 강제하지 않음
트랜잭션 처리 roll back 하지 않음 roll back

 여기에서 Exception은 다시 Checked Exception과 Unchecked Exception의 두 부류로 나뉜다.

 우선 'Checked Exception' 은 RuntimeException을 상속받지 않는 것들이다. Compile 시 이미 확인이 되며, 오류를 표기해주어서 그에 대한 명시적인 처리를 하도록 하는 Exception이다. 모두에 해당되는 것은 아니지만, 주로 동작하기 전에 IDE (Integrated Development Environment : 통합 개발 환경)에서 예외 처리를 해야한다고 표기해주는 친구들이라고 생각하면 될 것 같다. 때문에 명시적인 예외 처리를 해야 하며, 트랜잭션에서 execption이 발생했을 때 roll back을 하지 않는다.

 반대로 'Unchecked Exception' 은 RuntimeException을 상속받는 것들이다. 이들은 compile은 정상적으로 되지만, 실제로 동작하는 runtime 시에 발생하는 Exception이다. Variable을 참조해 method를 사용하거나 그 value 값을 얻으려고 했지만, 해당 variable에 동작 중에 null이 들어가는 경우를 예로 들 수 있겠다. 명시적인 예외 처리를 강제하지 않고, 트랜잭션에서 exception이 발생했을 때 roll back을 한다. Ehgks 이 Unchecked Exception에는 RuntimeException class를 상속받은 친구들뿐만 아니라 Error를 상속받은 친구들까지 포함한다.[각주:2]

 

/**
 * Transaction attribute that takes the EJB approach to rolling
 * back on runtime, but not checked, exceptions.
 *
 * @author Rod Johnson
 * @since 16.03.2003
 */

 현재 사용하고 있는 @Transactional annotation class에서 @see 로 소개하고 있는 org.springframework.transaction.interceptor.DefaultTransactionAttribute 로 가보면 볼 수 있는 주석이다. Runtime 시 roll back을 위해 EJB 접근 방식을 채택한다고 되어 있으며, checked exception이 아니라고 되어 있다. 여기에서 등장하는 EJB는 Enterprise JavaBeans의 약자로, 기업 환경의 시스템을 구현하기 위한 server 측 component model이라고 한다.[각주:3] EJB는 Java EE의 java spi 중 하나인데, JSP가 화면 logic을 처리한다면 EJB는 업무 logic을 처리하는 역할을 한다고 적혀 있다.

 많은 게시글에서 EJB의 관습을 따르기 때문에 runtime에 발생하는 Uncheched Exception 일 경우에만 roll back 하는 것이라고 적어두었지만, 정작 EJB의 관습이 무엇인지에 대해서는 설명해놓은 곳이 없었다. 궁금한데 대체 그 관습이란 것이 무엇일까?

 

EJB(Enterprise JavaBeans) 컨테이너의 기본 동작은 시스템 예외 (보통 런타임 예외)에 자동으로 트랜잭션을 롤백하는 것이더라도 EJB CMT (Container Managed Transaction)는 어플리케이션 예외에는 자동으로 트랜잭션을 롤백하지 않는다.(즉, java.rmi.RemoteException를 제외한 Checked Exception) 선언적인 트랜잭션 관리의 스프링 기본동작이 EJB 관례(Unchecked Exception에만 자동으로 롤백한다.)를 따르지만 스프링의 기본동작을 커스터마이징 하는 것은 종종 유용하다.

 그 내용은 이곳 [각주:4] 에서 찾을 수 있었다. 이곳에서는 위와 같이 적혀있었다. 오타는 임의로 수정하였다. 즉, Unchecked Exception에만 roll back 하는 것이 EJB의 관습인 것 같다.

 

 

Transaction을 사용할 곳

 

 그렇다면 transaction을 사용할 곳이라고 한다면 기본적으로는 Unchecked ExceptionError가 발생하여 query 의 roll back이 필요한 곳이라고 할 수 있겠다. 하지만 SQL exception은 Checked Exception이다. 계좌의 잔고가 0 아래로 떨어지게 되어 column 조건에 맞지 않았을 때 SQL exception을 발생시키는 경우에도 roll back이 일어나야 하지 않을까? 이에 대해서는 걱정하지 않아도 된다. 이전 게시글에도 작성했듯 Spring에서는 이 SQL exception이 발생하면 DataAccessException을 발생시키도록 되어있다.[각주:5]

 

모든 Exception에 대해 roll back을 원한다면?

	/**
	 * Defines zero (0) or more exception {@link Class classes}, which must be a
	 * subclass of {@link Throwable}, indicating which exception types must cause
	 * a transaction rollback.
	 * <p>This is the preferred way to construct a rollback rule, matching the
	 * exception class and subclasses.
	 * <p>Similar to {@link org.springframework.transaction.interceptor.RollbackRuleAttribute#RollbackRuleAttribute(Class clazz)}
	 */
	Class<? extends Throwable>[] rollbackFor() default {};

 @Transaction annotation에는 rollbackFor 라는 속성을 작성할 수 있다. 기본적으로는 Unchecked Exception과 Error에 대해서 roll back을 하도록 되어있지만, 이를 원하는 Exception에 대해서 roll back을 할 수 있도록 할 수 있다는 것이다. 해당 내용에 Exception type을 작성해주면 모든 Exception에 대해 roll back이 일어날 것이다.

 

 

코드 뜯어보기

 이를 토대로 Project의 Service class의 method에서 data를 추가, 수정, 삭제하는 경우 @Transactional annotation을 붙여주기로 했다.

댓글