티스토리 뷰

Spring

어떤 @Transactional을 사용해야 할까?

RyanGomdoriPooh 2020. 6. 14. 23:59

애플리케이션을 개발하다 보면, 보통 @Transactional을 사용해서 Transaction을 사용합니다.

 

Transaction의 동작원리

 

관습적으로 사용하다 보니, 내부적으로 어떻게 돌아가는지 원리에 대해서만 관심을 가졌습니다.

 

하지만 여기에서는 조금 더 들어가서 살펴보려고 합니다.

 

Transaction은 두 가지 형태로 구현할 수 있습니다.

  1. Programmatic Transaction 방식 : 직접 코드에서 Transaction을 구현하는 방식
  2. Declarative Transaction 방식 : 원하는 Scope에서 Annotation을 선언하는 방식

여기서 이야기하려고 하는 주제는 2.Declarative Transaction방식입니다.

 

여기서 다시 "2.Declarative Transaction"의 종류는 다음 두 가지로 나뉩니다.

  • Annotation으로 Transaction 선언
  • AOP Configuration으로 Transaction 선언

"어노테이션(Annotation)으로 트랜잭션 선언"으로 사용하다 보면, 서로 다른 패키지에 담긴 @Transactional을 보게 됩니다. 여기서 궁금증이 생겼습니다.

 

아주 간혹 레거시에서 구현된 코드를 보면 javax.transaction가 사용된 클래스를 발견할 수 있었기 때문입니다.

 

우리가 보통 볼 수 있는 @Transactional의 종류는 다음과 같습니다.

  1. org.springframework.transaction.annotation의 @Transactional
  2. javax.transaction의 @Transactional

1번과 2번의 패키지의 선언적인 Annotation @Transactional을 어떻게 동작하고, 어떤 걸 사용해야 할까요?

 

일단, 테스트 환경은 Spring v5.2.6.RELEASE에서 진행하였습니다.

 

우리가 보통 사용하는 Service Layer안에 있는 method 위에 @Transactional을 적용했습니다.

@javax.transaction.Transactional
void createEmailUser1() {
	User user = new User("test@gmail.com", "1234");
	userRepository.save(user);
}

@org.springframework.transaction.annotation.Transactional
void createEmailUser2() {
	User user = new User("test@gmail.com", "1234");
	userRepository.save(user);
}

위 코드를 테스트 실행 했을 때, 정상적으로 user가 저장이 되는 것을 확인할 수 있습니다.

 

이번에는 Transaction 안에는 Unchecked Exception이 일어났을 때, Rollback이 진행되는지 살펴보겠습니다.

@javax.transaction.Transactional
void createEmailUser1() {
	User user = new User("test@gmail.com", "1234");
	userRepository.save(user);
	throw new RuntimeException();
}

@org.springframework.transaction.annotation.Transactional
void createEmailUser2() {
	User user = new User("test@gmail.com", "1234");
	userRepository.save(user);
	throw new RuntimeException();
}

Rollback과 마찬가지로 의도한 대로 정상적으로 user의 저장은 Unchecked Exception이 발생하면서 Rollback 되었습니다.

 

여기까지 결론은 Declarative Transaction 방식으로 Transaction을 관리하였을 때,

@Transactional의 종류와 상관없이 Transaction을 관리가 가능하다는 것을 알 수 있었습니다.

 

그러면 왜 다른 @Transactional을 사용할 수 있게 제공되어있을까?

 

조금 더 패키지를 면밀하게 보겠습니다.

 

  1. 일단 패키지 이름을 보면, 힌트가 있습니다.
    • @javax.transaction.Transactional의 javax는 자바 표준 확장 패키지를 뜻합니다. 자바에서 기본 제공
    • @org.springframework.transaction.annotation.Transactional는 org.springframework에서 제공, Container가 관리해준다는 것이겠죠!?
  2. 해당 패키지를 들어가 보면 패키지 구성이 다르다는 것을 알 수 있습니다.
    • @javax.transaction.Transactional은 TransactionType, rollback 정도로 구현되어있습니다.
    • @org.springframework.transaction.annotation.Transactional은propagation, isolation, timeout, readOnly, rollback으로 구성되어있습니다.
    • Java에서 제공하는 Transaction 기능에서 Spring에서 Framework를 위한 기능을 보충하였습니다.

여기까지 보면 @org.springframework.transaction.annotation.Transactional를 Spring에서 사용한다면 많은 옵션으로 구현에 유리할 거라 생각을 할 수 있습니다.

 

저는 여기서 한 단계 더 들어가 보겠습니다.

 

저희가 선언해놓은 @Transactional을 어디에서 선언했는지 알고, 해당 Scope의 소스에 Transaction을 적용할지 찾아보았습니다.

 

@javax.transaction.Transactional를 Parsing 하는 class가 있었습니다.

package org.springframework.transaction.annotation;

import java.io.Serializable;
import java.lang.reflect.AnnotatedElement;
import java.util.ArrayList;
import java.util.List;

import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.lang.Nullable;
import org.springframework.transaction.interceptor.NoRollbackRuleAttribute;
import org.springframework.transaction.interceptor.RollbackRuleAttribute;
import org.springframework.transaction.interceptor.RuleBasedTransactionAttribute;
import org.springframework.transaction.interceptor.TransactionAttribute;

/**
 * Strategy implementation for parsing JTA 1.2's {@link javax.transaction.Transactional} annotation.
 *
 * @author Juergen Hoeller
 * @since 4.0
 */
@SuppressWarnings("serial")
public class JtaTransactionAnnotationParser implements TransactionAnnotationParser, Serializable {
	...
}

일단 package를 보면, springframework에서 @javax.transaction.Transactional를 인식해서 Transaction을 Parsing 합니다.

 

그리고 @since를 보시면 4.0부터라고 쓰여있으니, Spring 4.0 이상부터@javax.transaction.Transactional를 지원합니다.

 

그리고 @org.springframework.transaction.annotation.Transactional를 Parsing 하는 class도 있었습니다.

package org.springframework.transaction.annotation;

import java.io.Serializable;
import java.lang.reflect.AnnotatedElement;
import java.util.ArrayList;
import java.util.List;

import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.lang.Nullable;
import org.springframework.transaction.interceptor.NoRollbackRuleAttribute;
import org.springframework.transaction.interceptor.RollbackRuleAttribute;
import org.springframework.transaction.interceptor.RuleBasedTransactionAttribute;
import org.springframework.transaction.interceptor.TransactionAttribute;

/**
 * Strategy implementation for parsing Spring's {@link Transactional} annotation.
 *
 * @author Juergen Hoeller
 * @since 2.5
 */
@SuppressWarnings("serial")
public class SpringTransactionAnnotationParser implements TransactionAnnotationParser, Serializable {
	...
}

 

@org.springframework.transaction.annotation.Transactional은 @since 2.5라고 쓰여있는 것을 보니 Spring 2.5 이상부터 지원합니다.

 

"어떤 @Transactional을 사용해야 할까?"의 결론

  • Spring에서 @Transactional을 사용할 때, 더 많은 옵션과 더 많은 버전을 지원하는 @org.springframework.transaction.annotation.Transactional를 사용하는 것이 개발에 유리해 보입니다.
  • 옵션 사용을 하지 않고, Spring Version도 높다면, 어느 @Transactional을 사용하던 상관없다는 게 여기까지 결론입니다.
댓글
댓글쓰기 폼
공지사항
Total
494,172
Today
228
Yesterday
138
링크
TAG
more
«   2022/01   »
            1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31          
글 보관함