들어가기 전

이번 포스팅에서는 체크드 익셉션(Checked Exception), 언체크드 익셉션(Unchecked Exception)에서의 트랜잭션을 어떻게 처리하는지에 대해 알아보겠습니다. 그리고 @Transactional의 속성인 noRollbackFor, rollbackFor 속성에 대해서도 알아보겠습니다.

 

 

체크드 익셉션(Checked Exception)

RuntimeException을 제외한 Exception과 하위 예외를 모두 컴파일러가 체크하고 애플리케이션 로직에서 사용할 수 있는 최상위 예외입니다.

try-catch 또는 throws를 이용하여 예외를 처리해야 합니다.

그리고 체크드 익셉션 같은 경우는 언체크드 익셉션과 달리 예외가 발생하면 롤백이 되지 않고 커밋이 된다는 특성이 있습니다.

커밋이 되는 이유는 스프링에서 제공하는 @Transactional의 기본 정책은 언체크드 익셉션과 Error입니다. 그래서 체크드 익셉션에서는 

스프링이 EJB 관습을 따르기 때문에 @Transactional이 작동하지 않아 롤백이 되지 않고 커밋이 됩니다.

 

체크드 익셉션이 무엇이고 어떻게 처리하는지에 대해 알아보았습니다. 그럼 이제 예제를 통해서 알아보겠습니다.

 

로그 확인을 위한 application.properties설정

 

logging.level.org.springframework.transaction.interceptor=TRACE
logging.level.org.springframework.jdbc.datasource.DataSourceTransactionManager=DEBUG

#JPA log
logging.level.org.springframework.orm.jpa.JpaTransactionManager=DEBUG
logging.level.org.hibernate.resource.transaction=DEBUG
logging.level.org.hibernate.SQL=DEBUG

 

예제를 위한 커스텀 체크드 익셉션 생성

 

class CheckedException extends Exception {

	public CheckedException(String message) {
		super(message);
	}
}

@TestConfiguration
class Config {
	@Bean
	PlatformTransactionManager platformTransactionManager(DataSource dataSource) {
	    return new DataSourceTransactionManager(dataSource);
	}
}

@Service
@Slf4j
class CheckedService {

	@Transactional
	public void checkedExceptionTryCatch() throws CheckedException {
	    try {
		    log.info("try-catch, throws를 이용한 체크드 익셉션");
        throw new CheckedException("체크드 익셉션");
	    } catch (CheckedException e) {
		    throw new CheckedException(e.getMessage());
		}
	}
}

 

 

try-catch 또는 throws를 사용하지 않을 경우

 

@Slf4j
@SpringBootTest
public class CheckExceptionTest {
	@Autowired
	PlatformTransactionManager transactionManager;
    
	@DisplayName("체크드 예외를 try-catch 또는 throws로 처리하지 않았을때")
	@Test
	void checkedExceptionNonTryCatchAndThrows() {
	    log.info("트랜잭션 시작");
	    TransactionStatus transaction = transactionManager.getTransaction(
		    new DefaultTransactionDefinition());
	    transactionManager.rollback(transaction);
	    throw new CheckedException("체크드 익셉션");
	}
}

 

위와 같이 작성을 하면 아래와 같이 이미지처럼 컴파일 에러가 발생하면서 throws 또는 try-catch를 이용하라는 메시지를 확인할 수 있습니다.

 

 

 

try-catch, throws를 사용하는 경우

 

@Slf4j
@SpringBootTest
public class CheckExceptionTest {
	@Autowired
	CheckedService checkedService;
    
	@DisplayName("체크드 예외 - try-catch + throws")
	@Test
	void checkedExceptionTryCatch() throws CheckedException {
		checkedService.checkedExceptionTryCatch();
	}
}

 

 

아래 이미지를 보면 첫 번째 예제와는 달리 컴파일 에러가 발생 안 하는 것을 확인할 수 있습니다.

 

 

 

그럼 이제 해당 코드에서 발생하는 로그를 파악해 보겠습니다.

 

 

  • Creating new Transaction : 새로운 트랜잭션 생성
  • Opened new EntityManager : 영속성 컨텍스트 생성
  • Committing Jpa Transaction : 트랜잭션 커밋
  • Closing Jpa EntityManager : 영속성 컨텍스트 닫힘

로그를 자세히 보면 에러가 발생하여 에러 메시지가 찍힌 것을 확인할 수 있습니다. 예외가 발생하였는데 로그에 "Committing Jpa Transaction" 내용도 확인할 수 있습니다. 즉 예외가 발생하였음에도 불구하고 롤백이 되지 않고 커밋이 되었다는 것을 의미합니다. 위에서 설명한 거처럼 체크드 익셉션이 발생하면 롤백이 되지 않고 커밋이 된다는 것을 해당 예제에서 확인할 수 있습니다.

 

언체크드 익셉션(Unchecked Exception)

체크드 익셉션은 Exception의 하위 예외를 처리하고 컴파일 시점에서 확인할 수 있습니다.

반대로 언체크드 익셉션은 RuntimeException의 하위 예외들을 처리하고 런타임 시점에서 확인할 수 있습니다.

체크드 익셉션과 달리 언체크드 익셉션은 예외가 발생하면 롤백이 됩니다. 그리고 try-catch 또는 throws를 이용하여 강제로 예외처리를 하지 않아도 됩니다.

 

예제를 위한 커스텀 언체크드 익셉션 생성

 

class UncheckedException extends RuntimeException {

	public UncheckedException(String message) {
		super(message);
	}
}

@TestConfiguration
class UncheckedExceptionConfig {

	@Bean
	public PlatformTransactionManager platformTransactionManager(DataSource dataSource) {
		return new DataSourceTransactionManager(dataSource);
	}
}

@Service
@Slf4j
class UnCheckedService {

	@Transactional
	public void uncheckedExceptionTryCatch() {
			log.info("try-catch, throws를 이용한 체크드 익셉션");
			throw new UncheckedException("언체크드 익셉션");
	}
}

 

 

@Slf4j
@SpringBootTest
public class UncheckedExcetpionTest {
	@Autowired
	PlatformTransactionManager transactionManager;

	@Autowired
	UncheckedService uncheckedService;


	@DisplayName("언체크드 익셉션 발생시 롤백")
	@Test
	void uncheckedException() {
		uncheckedService.uncheckedExceptionTryCatch();
	}

}

 

 

  • Creating new Transaction : 새로운 트랜잭션 생성
  • Opened new EntityManager : 영속성 컨텍스트 생성
  • Rolling back Jpa Transaction : 트랜잭션 롤백
  • Closing Jpa EntityManager : 영속성 컨텍스트 닫힘

로그를 자세히 보면 체크드 익셉션과 달리 언체크드 익셉션은 예외가 발생하면 커밋이 되지 않고 롤백이 진행됩니다.

 

 

지금까지 체크드 익셉션, 언체크드 익셉션 발생 시 트랜잭션 연산에 대해서 알아보았습니다. 그럼 이제 체크드 익셉션 때 rollBack을 발생시키는 방법과 언체크드 익셉션 때 commit을 발생시킬 수 있는 @Transactional의 속성인 noRollbackFor, rollbackFor에 대해 알아보겠습니다.

 

 

스프링에서 제공하는 @Transactional 내부를 확인해 보면 rollbackFor, noRollbackFor이 있습니다. 먼저 rollbackFor에 대해서 알아보겠습니다.

 

rollbackFor

체크드 익셉션 같이 예외가 발생했음에도 불구하고 롤백을 원했지만 커밋이 되는 상황에서 사용하는 옵션입니다.

해당 옵션을 사용하면 커밋이 되지 않고 선언한 예외가 발생했을 때 롤백이 됩니다.

 

 

@Service
@Slf4j
class CheckedService {

	@Transactional(rollbackFor = CheckedException.class)
	public void checkedExceptionTransactionalOptionRollbackFor() throws CheckedException {
		try {
			log.info("try-catch, throws를 이용한 체크드 익셉션");
			throw new CheckedException("체크드 익셉션");
		} catch (CheckedException e) {
			throw new CheckedException(e.getMessage());
		}
	}

}

 

@Slf4j
@SpringBootTest
public class CheckExceptionTest {

	@Autowired
	CheckedService checkedService;
    	
        @DisplayName("@Transactional 옵션 중 rollbackFor을 사용하면 체크드 익셉션은 롤백이 된다.")
	@Test
	void checkedExceptionTransactionalOptionRollbackFor() throws CheckedException {
		checkedService.checkedExceptionTransactionalOptionRollbackFor();
	}
}

 

 

 

위에서 rollbackFor을 사용을 안 했을 때 체크드 익셉션 같은 경우는 커밋이 되었지만 rollbackFor을 사용하고 Exception을 상속받은 CheckedException을 선언하였더니 해당 예외가 발생하면서 롤백이 된 것을 확인할 수 있습니다.

 

 

noRollbackFor

언체크드 익셉션이 발생하면 커밋이 되지 않고 롤백이 됩니다. 그런데 언체크드 익셉션이 발생했을 때 커밋이 되어야 하는 상황이 존재할 때 사용하는 옵션입니다.

해당 옵션을 사용하면 롤백이 되지 않고 커밋이 진행됩니다.

 

@Service
@Slf4j
class UncheckedService{

	@Transactional(noRollbackFor = UncheckedException.class)
	public void uncheckedExceptionTransactionalOptionNoRollbackFor() {
		log.info("언체크드 익셉션 - noRollbackFor");
		throw new UncheckedException("언체크드 익셉션 - noRollbackFor");
	}
}

 

 

@Slf4j
@SpringBootTest
public class UncheckedExcetpionTest {

	@Autowired
	UncheckedService uncheckedService;


	@DisplayName("언체크드 익셉션 noRollbackFor - 롤백 X 커밋 O")
	@Test
	void uncheckedExceptionTransactionalOptionNoRollbackFor() {
		uncheckedService.uncheckedExceptionTransactionalOptionNoRollbackFor();
	}

}

 

 

위에서 noRollbackFor을 사용하지 않고 언체크드 익셉션이 발생했을 때 롤백이 일어났었습니다. 

noRollbackFor을 사용하고 언체크드 익셉션을 상속받은 예외를 선언을 하고 해당 예외가 발생하면 롤백이 되지 않고 커밋이 되는 것을 확인할 수 있습니다.