DevOn Boot 3.0의 멀티 데이터소스 트랜잭션 관리
DevOn Boot 3.0의 신규 CombinedTransactionManager가 JDBC와 JPA를 동일한 트랜잭션 경계로 묶는 방식을 설명합니다. 기존 CompositeDataSourceTransactionManager의 한계와 체인 방식의 Best-Effort 1PC 메커니즘, 적용 시 유의사항을 함께 다룹니다.

서론: 한 트랜잭션, 여러 데이터소스
금융권을 비롯한 엔터프라이즈 환경에서는 단일 비즈니스 처리가 여러 데이터베이스에 걸쳐 구현되는 경우가 흔하다. 예를 들어, 주계정 DB, 이력 DB, 통계 DB 등 물리적으로 분리된 여러 데이터소스에 동시에 쓰기 작업이 발생하지만, 비즈니스 관점에서는 이를 하나의 트랜잭션으로 다뤄야 한다.
표준 해법으로 JTA(XA) 기반 분산 트랜잭션이 존재하지만, 2PC 오버헤드, XA 드라이버 종속성, 운영 복잡도라는 부담이 따른다. 그렇다고 단일 DataSourceTransactionManager만으로는 여러 데이터소스를 하나의 트랜잭션으로 묶을 수 없어 한계가 있다.
DevOn Boot는 이 사이에서 체인 방식의 Best-Effort 1PC를 오랫동안 제공해 왔으며, 3.0 릴리즈에서 그 구현체를 한 단계 더 진화시켰다.
기존 구현: 체인 방식의 Best-Effort 1PC
DevOn Boot 1.0부터 제공된 CompositeDataSourceTransactionManager는 2PC 오버헤드와 XA 드라이버 종속성 없이 멀티 DB 트랜잭션을 묶기 위해 체인 방식의 Best-Effort 1PC를 구현했다. 사용자가 데이터소스 목록만 지정하면, 내부적으로 각 데이터소스에 대한 JDBC 트랜잭션 매니저를 생성해 하나의 체인으로 묶고 commit/rollback을 일괄 처리한다. 설정은 다음과 같이 간결하다.
devon:
transaction:
composite:
data-source-list:
- primary
- secondary
이 구조 덕분에 MyBatis 기반 JDBC 멀티 DB 시나리오는 비교적 단순한 설정으로 다룰 수 있었다. 다만 데이터소스 이름만 나열할 수 있을 뿐 매니저의 종류를 지정할 수는 없었기에, 모든 데이터소스가 JDBC 트랜잭션 매니저로만 묶였다. 결과적으로 JPA처럼 자체 PlatformTransactionManager가 필요한 기술은 이 체인에 포함시킬 수 없어, 같은 비즈니스 단위라도 JDBC와 JPA의 commit/rollback이 하나의 트랜잭션 경계로 묶이지 못했다.
대응 전략: JPA 통합을 위한 두 가지 선택지
JPA처럼 자체 트랜잭션 매니저가 필요한 기술을 동일 트랜잭션 경계로 묶는 선택지는 크게 두 가지다.
| 전략 | 방식 | 한계 |
|---|---|---|
| JTA 전환 | 표준 분산 트랜잭션(2PC) 사용 | XA 드라이버 종속, 운영 부담, 성능 저하 |
| 체인 매니저 확장 | 기존 체인 방식을 JPA까지 수용하도록 재설계 | 기존 호환성 유지가 관건 |
JTA 전환은 검증된 표준이지만, XA 드라이버 종속과 운영 복잡도라는 부담이 다시 따라붙는다. 반대로 체인 방식을 유지한 채 매니저 종류를 명시적으로 선언할 수 있도록 구조를 확장하면, 기존 동작 모델을 그대로 유지한 채 JPA를 같은 경계로 묶을 수 있다.
DevOn Boot의 선택은 후자였다. 체인 commit/rollback이라는 검증된 메커니즘은 그대로 유지하되, 매니저 타입을 명시적으로 선언할 수 있도록 구조를 재설계한 것이다.
해결: CombinedTransactionManager 도입 (v3.0)
DevOn Boot 3.0은 신규 구현체 CombinedTransactionManager를 도입하고, 기존 CompositeDataSourceTransactionManager는 deprecated 처리되어 향후 릴리즈에서 제거될 예정이다.
핵심 변경점은 두 가지다.
1. 매니저 타입 명시 가능 (JDBC / JPA)
YAML 설정에서 데이터소스마다 매니저 종류를 직접 선언할 수 있다. JPA가 classpath에 있을 때만 JPA 트랜잭션 매니저가 자동 활성화된다. 데이터소스 이름만 지정하면, Spring 컨텍스트에 이미 등록된 EntityManagerFactory 중 해당 데이터소스로 만들어진 것을 자동으로 찾아 연결한다.
2. 외부에서 정의한 트랜잭션 매니저 주입 가능
사용자가 직접 정의한 PlatformTransactionManager도 체인에 끼워넣을 수 있어, JDBC/JPA를 넘어선 확장 시나리오에도 대응할 수 있다.
설정 예시 (application.yml)
devon:
transaction:
combined:
transaction-manager-list:
- data-source: primary
transaction-manager-type: jdbc
- data-source: secondary
transaction-manager-type: jpa # JDBC와 동일 체인에 등록 가능
동작 메커니즘
등록 순서대로 체인이 구성되고, commit/rollback은 역순(LIFO) 으로 진행된다. 즉 가장 마지막에 등록된 매니저가 가장 먼저 commit된다.
비즈니스 로직 중 예외가 발생하면 묶여 있는 모든 매니저가 함께 rollback되어, 모든 DB가 원상 복구된다. 이것이 체인으로 묶는 핵심 효과다.
Spring @Transactional이 예외를 감지해 CombinedTransactionManager.rollback()을 호출하면, 이 메서드가 체인의 모든 매니저에 rollback을 전파한다.

다만 예외 없이 commit 단계까지 진입한 뒤 도중에 commit이 실패하면, 아직 처리되지 않은 매니저는 rollback되지만 이미 commit된 매니저는 되돌릴 수 없는 부분 commit이 남는 경우가 생길 수 있다.
이때는 HeuristicCompletionException(STATE_MIXED)로 통지되며, 첫 commit 시도부터 실패한 경우 STATE_ROLLED_BACK으로 통지된다.

적용 시 유의사항
2PC가 아닌 Best-Effort 1PC
이 체인은 2PC 기반 분산 트랜잭션이 아니다. 위 동작 메커니즘에서 본 것처럼 commit 단계에서 일부 매니저만 깨지면 부분 commit이 남을 수 있다. 거래 정합성이 결정적으로 중요한 경우는 별도 보정 로직이나 JTA를 검토해야 한다.
분산 서비스 트랜잭션은 별도 영역
본 도구는 한 서비스 안에서 여러 DB를 묶는 용도이다. MSA 환경에서 여러 마이크로서비스에 걸친 분산 트랜잭션은 SAGA 패턴이나 전용 분산 트랜잭션 솔루션(Oracle MicroTx, Apache Seata 등)을 검토해야 한다.
점진적 전환 가능
CompositeDataSourceTransactionManager는 하위 호환성 유지를 위해 남아 있다. 운영 중인 시스템은 차기 메이저 릴리즈 전까지 단계적으로 전환할 수 있다.
마치며
멀티 DB 트랜잭션은 엔터프라이즈 어플리케이션에서 흔히 마주치는 과제다. DevOn Boot 3.0의 CombinedTransactionManager는 JDBC 전용이라는 가정을 걷어내고, 매니저 타입을 명시적으로 선언하는 구조로 발전했다.
핵심은 두 가지다. 첫째, JPA를 포함한 어떤 PlatformTransactionManager도 동일한 체인에 등록할 수 있다. 둘째, 기존 commit/rollback 동작이 그대로 보존되어 운영 중인 시스템도 점진적으로 전환할 수 있다.
DevOn Boot는 이렇게 트랜잭션 관리의 복잡함을 프레임워크 안으로 흡수해, 개발자가 비즈니스 로직에 더 집중할 수 있도록 돕는다.
※ 본 게시글의 이미지는 AI를 활용해 제작되었습니다.