본문 바로가기

Spring

@Transactional Rollback이 안되는 이유, @Transactional 사용시 주의 사항 - 삽질중인 개발자

반응형

개발을 하다 보니 예외(Exception)에 대해서 아무 생각 없이 사용하다가 심각한 버그를 발견하게 되어서 글을 정리한다.

아마 초보 개발자들이 가장 많이 하는 실수 중 하나 아닐까 싶다.

 

아래의 코드는 문자열을 넘겨주면 해당 문자열의 길이를 반환해주는 함수이다.

public int getLength(String str) throws Exception{
	if(str == null){
		throw new Exception();
	}
	return str.length();
}

그냥 봤을 때는 null일 때 유효성 검사도 되어 있고 유효성 검사에서 걸리면 Exception까지 던져주는 문제없어 보이는 코드처럼 보인다. 

 

만약 위에 있는 함수를 사용해서 스프링에서 제공하는 @Transactional 어노테이션을 사용하여 아래와 같은 코드를 짠다고 가정하면 과연 DB에는 값이 저장되어 있을까? 아니면 롤백 처리가 되어 있을까?? 

 

@Transactional
public void something() throws Exception {
	String str = "문자 1" ;
	saveStr(str);	// 문자를  DB에 저장하는 코드 
	
	String str2 = null;
	int length = getLength(str2);  // null 이기에 여기서 Exception이 발생한다.
	saveStr(str2);	// 문자를  DB에 저장하는 코드 
}

정답은 saveStr까지는 저장이 된다.

 

분명 @Transactional을 사용해서 로직에 문제가 있으면 Rollback이 되도록 의도하여 코드를 작성했을 텐데 실제로 DB에는 "문자 1" 이라는 값이 저장되어 있다. 왜 "문자 1"이 저장이 되었을까?

 

위에 의문을 풀기 위해서는 우선 자바에서의 예외의 정의에 대해서 알아야 한다.

 

 

java Error, Checked Exception, Unchecked Exception - 삽질중인 개발자

개발을 하다 보면 예외를 처리해야 하는 상황이 아주 많이 발생한다. 이때 예외에 대해서 정확히 알고 있어야 좋은 코드를 짤 수 있기에 예외에 대한 포스팅을 한다. 에러(Error) vs 예외(Exception)

programmer93.tistory.com

 

위에 포스팅을 읽고 왔다면 Checked Exception과 Unchecked Exception이 무엇인지 알 것이다.

 

그럼 이제 왜 DB가 롤백되지 않는지에 대해서 알아보자.

 

스프링 프레임워크에서 @Transactional 어노테이션은 기본적으로 Checked Exception에 대해서는 롤백시키지 않도록 설계되어 있다. 그 이유는 스프링 프레임워크가 EJB에서의 관습을 따르기 때문이라고 한다.

 

그렇기 때문에 위에 있는 코드가 throw new Exception(); 로 강제로 Checked Exception을 던져버리면서 롤백이 발생되지 않았던 것이다.

 

다행이게도 @Transactional  어노테이션의 rollbackFor 이라는 옵션을 사용하여 원하는 Exception에 대해서 롤백시켜버릴 수 있는 방법이 존재한다.

@Transactional(rollbackFor = {Exception.class})

 

하지만 이런 식으로 코딩을 하는 것보다 예외 전환을 통해서 롤백이 되도록 구현하는 게 더 좋아 보인다.

 

반응형