Back-End/Java

Exception과 Transaction

김검정 2024. 2. 5. 11:25

Exception 과 Error

Exception 과 Error 는 한 마디로 개발자가 대응할 수 있냐 없냐로 구분할 수 있다.

 

- Exception : 프로그램을 실행 중 발생하는 예외 상황을 나타낸다. 예외는 프로그램이 실행되는 동안 발생할 수 있는 일반적인 에러를 포함한다. 예를 들어, 파일을 찾을 수 없는 경우, 네트워크 연결이 끊어진 경우, 잘못된 사용자 입력 등이 있다.

 

- Error : 프로그램이 더 이상 수행될 수 없는 심각한 예외 상황을 나타낸다. 일반적으로 Error는 시스템 수준의 문제를 나타내며, 프로그래머가 직접적으로 처리하기 어렵거나 불가능한 상황이다. 프로그램이 더 이상 복구할 수 없는 상황에서 발생하는데, 가장 흔한 예로는 OutOfMemoryError 가 있다.

 

자바에서는 이러한 예외를 처리하기 위해 'try', 'catch', 'finally', 'thorw', 'thorws' 등의 예외 처리 구문을 제공한다.

 

try {
	// 예외가 발생할 수 있는 코드 작성 
} catch {
	// 예외를 처리하는 코드 작성
} finally {
	// 항상 실행되는 코드 작성 (예외 발생 여부에 상관없이)
}

 

 

 

추가로 이러한 오류가 발생하는 시점에 따라 분류를 할 수도 있는데

   1. 컴파일 에러 (compile-time error): 컴파일시에 발생하는 에러

   2. 런타임 에러 (runtime error): 프로그램 실행시에 발생하는 에러

   3. 논리적 에러 (logical error): 실행은 되지만 의도와 다르게 동작하는 것

로 나뉜다. 

 

* 논리 에러 (logical error) 는 버그라고 생각하면 된다. 프로그램이 실행되고 작동하는데는 아무런 문제가 없는 오류지만, 결과가 예상과 달라 개발자가 의도한 작업을 수행하지 못하게 되어 서비스 이용에 지장이 생길 수 있다. 

이러한 논리 에러는 프로그램이 멀쩡하게 돌아가기 떄문에 따로 에러 메시지는 발생하지 않는다. 따라서 논리 에러를 피하기 위해서는 프로그램의 전반적인 코드와 알고리즘을 체크하고 테스트를 진행해야 한다.

 

* 컴파일 에러 (compile-time error) : 컴파일 에러는 컴파일 단계에서 오류를 발견하면 컴파일러가 에러 메시지를 출력해주는 것을 말한다. 대표적인 컴파일 에러 발생의 원인은 '문법 구문 오류 (syntax error)'를 들 수 있다.

 

* 런타임 에러 (runtime error) : 프로그램 실행 중에 에러가 발생하여 잘못된 결과를 얻는 경우 런타임 에러가 발생할 수 있다.

 

 

자바의 오류 클래스 계층 구조를 확인해보자

Exception 계층 구조

 

 

자바에서 다루는 모든 예외 처리는 Exception 클래스에서 처리한다. Exception 클래스는 RunTimeException 과 CompileException 으로 구분 할 수 있다.

 

그렇다면 RuntimeException 클래스의 종류에는 어떠한 것들이 있을까?

 

런타임 예외의 종류

 

 

@Transaction 어노테이션

@Transaction 어노테이션은 saveDataInTransaction 메서드에 적용되어있다. 이 메서드가 실행될때 스프링은 트랜잭션을 시작하고, 메서드가 완료되면 트랜잭션을 커밋한다. 만약 메서드 수행 중 예외가 발생하면 트랜잭션은 롤백된다. 

 

Spring @Transactional 선언 시 기본 속성은 아래와 같다.

@Transactional
//@Transactional(rollbackFro = {RuntimeException.class, Error.class}) 기본 속성
public void doSave(User userInfo) {
	 userInfoSaveDao.save(user);
}

 

RuntimeException 과 Error 가 발생했을 경우 기본적으로 rollback이 된다. 해당 2가지 경우가 아니면 rollback 되지 않는다. (컴파일 예외는 rollback되지 않는다)

 

하지만 트랜잭션을 rollback 하는 다른 방법들이 존재한다.

 

1. @Transactional 의 옵션 값을 변경

@Transactional(rollbackFor = {Exception.class})
public void doSave(User userInfo) {
	 userInfoSaveDao.save(user);
}

위와 같이 변경하면 모든 예외에 대해서 rollback을 진행하게 된다.

 

2. RuntimeException

@Transactional(rollbackFor = {RuntimeException.class, Error.class})
public void doSave(User userInfo) {
	 userInfoSaveDao.save(user);
}

 

3. 수동으로 하는 rollback

@Autowired
private DataSourceTransactionManager txm;


@Transactional(rollbackFor = {Exception.class})
public void doSave(User userInfo) {
	
    TransactionStatus tx = CommonUtils.getTransactionStatus(txm);

	try {
		 userInfoSaveDao.save(user);    
    } catch(Exception e) {
    	tx.rollback();
    }

}