개발공부/SPRING

[Spring] @Transactional이 2개라고? 일단 그게 뭔데?

키크니개발자 2022. 7. 20. 19:36

💡 코드가 보이지 않으시다면 드래그 혹은 오른쪽 아래 🌜 아이콘을 눌러 테마 색을 변경해주세요.

 

 

안녕하세요!

키크니 개발자 입니다. 🦒

 

레거시 프로젝트를 버전 업을 하는 도중 @Transactional이 import 방법이 2가지가 있다는 것을 알았습니다.

당연하게 써오던 것이었는데 두 가지의 차이가 무엇인지 궁금하여 알아보았습니다. 😅

 

Transactional이 무엇인데?

우선 여기서 말하는 트랜잭션은 데이터베이스 관리 시스템이나 유사한 시스템에서 상호작용 단위라고 할 수 있습니다.

그리고 이 상호작용 단위는 더 이상 쪼개질 수 없는 최소의 연산을 의미합니다.

 

트랜잭션은 ACID 원칙을 갖고 있습니다.

ACID원칙이란?

더보기

원자성(Atomicity) : 트랜잭션은 분리할 수 없는 하나의 단위입니다.

작업이 모두 수행되거나(commit) 하나도 수행되지 않아야(rollback) 합니다. 

 

일관성(Consistency) : 트랜잭션에서 사용되는 모든 데이터는 일관되어야 합니다.

 

독립성(Isolation) : 현재 트랜잭션이 접근하고 있는 데이터는 다른 트랜잭션으로부터 격리되어야 하는 것을 의미합니다.

트랜잭션이 발생하기 이전 상태나 완료된 이후 상태를 볼 수는 있지만, 트랜잭션이 진행중인 중간 데이터는 볼 수 없습니다.

 

지속성(Durability) : 트랜잭션이 정상적으로 종료되면, 그 결과는 시스템 오류가 발생하더라도 시스템에 영구적으로 적용되어야 합니다.

@Transactional 어노테이션

@Transactional 은 클래스나 메서드에 붙여줄 경우, 해당 범위 내 메서드가 트랜잭션이 되도록 보장해줍니다.

이는 선언적 트랜잭션(Declarative Transaction)이라고 하는데,

직접 객체를 만들 필요 없이 선언만으로도 관리를 용이하게 해줍니다.

@RequiredArgsConstructor
@Service
public class OrderService {
    private final OrderRepository orderRepository;

    @Transactional(readOnly = true)	// (1)
    public List<OrderResponseServiceDto> getOrders() {
        return orderRepository.findAll()
                .stream()
                .map(OrderResponseServiceDto::new)
                .collect(Collectors.toList());
    }

 

(1) 위의 예시에 있는 @Transactional(readOnly = true)는 읽기 전용으로 엔티티를 조회하는 방법입니다.

💡 readOnly 옵션을 주면 스프링 프레임워크가 하이버네이트 세션 플러시 모드를 MANUAL로 설정하기 때문에 
강제로 flush를 호출하지 않는 한 flush가 일어나지 않습니다. 
따라서 트랜잭션을 커밋하더라도 영속성 컨텍스트가 flush가 되지 않아 엔티티의 등록, 수정, 삭제가 동작하지 않습니다.
또한 영속성 컨텍스트는 변경 감지를 위한 스냅샷을 보관하지 않으므로 성능이 향상됩니다. 
(즉, 엔티티를 읽기 전용으로 조회하면, 변경 감지를 위한 스냅샷을 유지하지 않아도 되고, 영속성 컨텍스트를 flush 하지 않아도 되므로 성능을 최적할 수 있습니다.)

@Transactional의 작동 원리와 흐름은?

@Transactional이 붙은 메서드를 호출할 경우에는 Spring은 해당 메서드에 대한 프록시를 만듭니다.

프록시 패턴은 디자인 패턴 중 하나로, 어떤 코드를 감싸면서 추가적인 연산을 수행하도록 강제하는 방법입니다.

 

트랜잭션의 경우 트랜잭션의 시작과 연산 종료시 커밋 과정이 필요하므로,
프록시를 생성해 해당 메서드의 앞 뒤에 트랜잭션의 시작과 끝을 추가합니다.

이를 트랜잭션 AOP로 명칭하겠습니다.

 

스프링 컨테이너는 트랜잭션 범위의 영속성 컨텍스트 전략을 기본으로 사용합니다.

서비스 클래스에서 @Transactional을 사용할 경우,

해당 코드 내의 메서드를 호출할 때 영속성 컨텍스트가 생깁니다.

영속성 컨텍스트는 트랜잭션 AOP가 트랜잭션을 시작할 때 생겨나고, 

메서드가 종료되어 트랜잭션 AOP가 트랜잭션을 커밋할 경우 영속성 컨텍스트가 flush되면서

해당 내용이 반영됩니다. 이후 영속성 컨텍스트가 종료됩니다.

 

이러한 방식으로 영속성 컨텍스트를 관리해 주기 때문에 @Transactional을 쓸 경우 트랜잭션의 원칙을 정확히 지킬 수 있습니다.

 

 

그래서 @Transactional이 왜 2개인데?

spring에서 사용할 수 있는 @Transactional의 종류는 두 가지 입니다.

 

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

: javax 자바 표준 확장 패키지를 의미합니다.(자바에서 기본 제공)

 

2. javax.transaction의 @Transactional

: sping에서 제공하므로 container가 관리해줍니다.

 

이 두 가지의 공통점은 Unckecked Exception이 일어났을 때 Rollback이 진행됩니다.

그럼 차이점이 무엇일까요?

해당 패키지를 들어가 보면 패키지 구성의 차이가 있다는 것을 알 수 있습니다.

 

javax @Transactional

TransactionalType, rollback 정도로 구현되어있습니다.

package javax.transaction;

@Inherited
@InterceptorBinding
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(value = RetentionPolicy.RUNTIME)
public @interface Transactional {

	...
    
	 public enum TxType {
     	REQUIRED, REQUIRES_NEW, MANDATORY, SUPPORTS, NOT_SUPPORTED, NEVER
     }
     
 	@Nonbinding
    public Class[] rollbackOn() default {};
    
    @Nonbinding
    public Class[] dontRollbackOn() default {};
    
}

spirng @Transactional

propagation, isolation, timeout, readOnly, rollback 등으로 구현되어있습니다.

package org.springframework.transaction.annotation;

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {

	...
    
	int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;
	String timeoutString() default "";
	boolean readOnly() default false;
    Class<? extends Throwable>[] rollbackFor() default {};
    Class<? extends Throwable>[] noRollbackFor() default {};
    
    ...

}

 

결론이 뭐야?

결론적으로는 spring에서 지원하는 @Transactional을 사용할 때

더 많은 옵션과 더 많은 버전을 지원하는 것을 원하시는 분은

@org.springframework.transaction.annotation.Transactional 을 사용하는 것이 유리합니다.

(위에 readOnly에 대한 부분을 다시 참고해주세요 🫣 )

 

 

 

⭐️  참고한 곳  


자바 ORM 표준 JPA 프로그래밍 - 김영한님

https://kafcamus.tistory.com/30

https://willseungh0.tistory.com/75

http://egloos.zum.com/sweeper/v/3003805

 

배워야 할 것이 더 많은 주니어 개발자입니다. 🐣
내용 전달보다는 정리를 목적으로 포스팅을 하고 있습니다.
잘못 된 내용이나 부족한 부분은 댓글로 주시면 감사드리겠습니다. 
반응형