Hello

Spring에서 트랜잭션을 관리하는 세 가지 방법 본문

spring

Spring에서 트랜잭션을 관리하는 세 가지 방법

nari0_0 2024. 11. 18. 18:33
728x90

비즈니스 코드를 작성하다 보면 다른 트랜잭션의 메소드를 호출해야 할 때가 있습니다. 이 글에서는 Spring에서 트랜잭션을 관리하는 세 가지 방법을 비교하고, 각 방법의 장단점을 설명합니다. 상황에 맞는 최적의 트랜잭션 관리 방법을 선택할 수 있습니다.

 

1. Self-Injection

Spring Bean이 자신을 의존성으로 주입받아 사용하는 방식입니다. 사용 방법이 간단하며, 메소드 간의 트랜잭션 경계를 명확하게 나눌 수 있습니다.

장점

  • 단순성: 코드가 비교적 단순하고 이해하기 쉽습니다.
  • 명확한 트랜잭션 경계: 메소드 간의 트랜잭션 경계를 명확하게 정의할 수 있습니다.
  • 간단한 설정: 설정이 비교적 간단하여 빠르게 적용할 수 있습니다.
  • 유연성: 특정 메소드에만 트랜잭션을 적용할 수 있어 유연하게 트랜잭션을 관리할 수 있습니다.

단점

  • 순환 의존성: 잘못 사용하면 순환 의존성 문제가 발생할 수 있습니다.
  • 테스트 어려움: self-injection을 사용하는 코드는 테스트하기 어려울 수 있습니다.
  • 유지보수: 코드가 복잡해지면 유지보수가 어려울 수 있습니다.
  • 의존성 주입: 자기 참조를 주입받아야 하므로, 의존성 주입 설정이 필요합니다.
@Service
public class SomeService {

    private SomeService self;

    @Autowired
    public void setSelf(SomeService self) {
        this.self = self;
    }

	// 트랜잭션이 없는 비즈니스 로직
	public void someFacadeBusiness() {
        self.txMethodA(); // 트랜잭션A 메소드 호출
    }
    
	// 트랜잭션B가 적용된 비즈니스 로직 에서 트랜잭션A가 메소드 호출
	@Transactional("transactionManagerB")
    public void txMethodB() {
    	//트랜잭션 B를 사용하는 로직
        //...
        self.txMethodA(); // 트랜잭션A 메소드 호출
    }

    @Transactional("transactionManagerA")
    public void txMethodA() {
        // 트랜잭션A가 적용된 비즈니스 로직
    }
}

2. 서비스 클래스를 두 개로 분리

말 그대로 서비스를 분리해 트랜잭션을 각각 관리할 수 있도록 합니다. 각 서비스의 역활이 명확해지며 코드가 모듈화 되어 유지보수 및 테스트 편의성이 용이합니다.

장점

  • 역할 분리: 각 서비스 클래스가 명확한 역할을 가지며, 서로 다른 트랜잭션을 관리할 수 있습니다.
  • 모듈화: 코드가 모듈화되어 유지보수와 테스트가 용이합니다.
  • 가독성: 코드의 가독성이 향상되어 이해하기 쉽습니다.
  • 테스트 용이성: 각 클래스가 독립적으로 동작하므로 단위 테스트가 용이합니다.
  • 순환 의존성 해결: 순환 의존성 문제를 피할 수 있습니다.

단점

  • 복잡성 증가: 클래스가 많아지면 관리가 복잡해질 수 있습니다.
  • 의존성 관리: 클래스 간의 의존성을 잘 관리해야 합니다.
  • 코드 분리: 코드가 여러 클래스에 분리되어 있어, 전체 흐름을 파악하기 어려울 수 있습니다.
  • 설정 필요: 각 클래스에 대한 별도의 트랜잭션 설정이 필요할 수 있습니다.
@Service
public class ServiceA {
	@Autowired
    public ServiceB serviceB;

    @Transactional("transactionManagerA")
    public void someFacadeBusiness() {
        // 트랜잭션 A가 적용된 비즈니스 로직
        serviceB.txMethod();// ServiceB의 메소드 호출
    }
    
    @Transactional("transactionManagerA")
    public void txMethod() {
        // 트랜잭션 A가 적용된 비즈니스 로직
    }
}

@Service
public class ServiceB {

    @Transactional("transactionManagerB")
    public void txMethod() {
        // 트랜잭션 B가 적용된 비즈니스 로직
    }
}

@Service
public class BusinessService {
	@Autowired
    private ServiceA serviceA;
    @Autowired
    private final ServiceB serviceB;

    public void executeBusinessLogic1() {
        serviceA.someFacadeBusiness();
    }
    public void executeBusinessLogic2() {
        serviceA.txMethod();
        serviceB.txMethod();
    }
}

 

3. ChainedTransactionManager [ deprecated ]

여러 트랜잭션 매니저를 체인으로 연결하여 복잡한 트랜잭션 시나리오를 처리할 수 있게 합니다. 아래 예시는 2개의 트랜잭션만 연결했지만 n개의 트랜잭션을 연결할 수 있습니다.

 

ChainedTransactionManager는 Spring Data Core 2.5 버전부터 deprecated되었습니다. 이 클래스는 여러 트랜잭션 매니저를 체인으로 연결하여 트랜잭션을 관리하는 기능을 제공했지만, 일관된 트랜잭션 처리를 보장하기 어렵다는 이유로 더 이상 권장되지 않습니다. 

 

장점

  • 다중 트랜잭션 매니저: 여러 트랜잭션 매니저를 체인으로 연결하여 사용합니다.
  • 중앙 집중식 관리: 트랜잭션 매니저를 중앙에서 관리하여 트랜잭션 경계를 명확하게 정의할 수 있습니다.
  • 복잡한 트랜잭션 시나리오: 복잡한 트랜잭션 시나리오를 처리할 때 유용합니다.
  • 일관성: 여러 데이터 소스에 걸쳐 일관된 트랜잭션 처리가 가능합니다.
  • 확장성: 새로운 트랜잭션 매니저를 쉽게 추가할 수 있습니다.

단점

  • 설정 복잡성: 설정이 복잡할 수 있으며, 잘못된 설정으로 인해 예상치 못한 동작이 발생할 수 있습니다.
  • 유지보수 어려움: 여러 트랜잭션 매니저를 관리해야 하므로 유지보수가 어려울 수 있습니다.
  • 성능: 여러 트랜잭션 매니저를 사용하면 성능에 영향을 미칠 수 있습니다.
  • 의존성: 트랜잭션 매니저 간의 의존성을 잘 관리해야 합니다.
@Configuration
public class TransactionConfig {
    @Bean
    public PlatformTransactionManager transactionManagerA(EntityManagerFactory entityManagerFactoryA) {
        return new JpaTransactionManager(entityManagerFactoryA);
    }

    @Bean
    public PlatformTransactionManager transactionManagerB(EntityManagerFactory entityManagerFactoryB) {
        return new JpaTransactionManager(entityManagerFactoryB);
    }

    @Bean
    public ChainedTransactionManager chainedTransactionManager(
            PlatformTransactionManager transactionManagerA,
            PlatformTransactionManager transactionManagerB) {
        return new ChainedTransactionManager(transactionManagerA, transactionManagerB);
    }
}

@Service
public class SomeService {

    @Transactional("chainedTransactionManager")
    public void someFacadeBusiness() {
        // 트랜잭션 A와 B가 모두 적용된 비즈니스 로직 작성 또는 호출
        txMethodA();
        txMethodB();
    }
 
    @Transactional("transactionManagerA")
    public void txMethodA() {
        // 트랜잭션 A가 적용된 비즈니스 로직
    }
 
    @Transactional("transactionManagerB")
    public void txMethodB() {
        // 트랜잭션 B가 적용된 비즈니스 로직
    }
}

 

Spring에서 트랜잭션을 관리하는 방법은 다양하며, 각 방법은 특정 상황에 맞는 장단점을 가지고 있습니다. Self-Injection은 코드가 단순하고 이해하기 쉬우며, 메소드 간의 트랜잭션 경계를 명확하게 정의할 수 있는 장점이 있습니다. 서비스 클래스를 분리하는 방법은 코드의 가독성과 유지보수성을 높이고, 테스트 용이성을 향상시키는 데 매우 유용합니다.

 

참고 : https://docs.spring.io/spring-framework/reference/data-access/transaction/motivation.html

Dependency Injection :: Spring Framework

Self-Injection With Spring | Baeldung

ChainedTransactionManager (스프링 데이터 코어 3.4.0 API)

728x90