PL 기술 리서치 보고서 — HTML / PDF / Word (.docx) 통합 뷰어
Word (.docx) 다운로드 PDF 파일 다운로드

[기술 검토 보고서] Spring Framework 트랜잭션 추상화(PSA) 원리 및 엔터프라이즈 트랜잭션 표준 아키텍처 수립

항목내용
보고 일자2026-10-26
작성자아키텍처 TF / 백엔드 코어 엔지니어링 파트
검토 목적Spring Framework의 트랜잭션 처리 원리(PSA·AOP 프록시·전파/격리 수준) 규명, 트랜잭션 구현 대안 비교 및 전사 표준 가이드라인 확정
의사결정 요청 기한2026-10-30
문서 상태최종 권고안 보고 (승인 요청)

Executive Summary (핵심 요약)

최종 권고안: 전사 Spring Boot 3.x 기반 서비스의 트랜잭션 관리는 클래스 레벨 @Transactional(readOnly = true) + 쓰기 메서드 @Transactional 선언적 방식을 기본 표준(85%)으로 채택하되, 외부 API 호출·분산 락 결합 구간에는 TransactionTemplate 프로그래밍 방식(15%)을 혼용하고, 도메인 간 후처리는 @TransactionalEventListener(AFTER_COMMIT) + Outbox 패턴으로 분리할 것을 권고함.

1. 기술 종속성 완전 제거(PSA) 및 생산성 극대화: PlatformTransactionManager 추상화를 통해 JDBC, JPA(Hibernate), MyBatis 등 하위 데이터 접근 기술이 변경되어도 비즈니스 코드 수정 비용을 0줄(Zero-line change)로 유지하며, 로우레벨 커넥션 제어 대비 보일러플레이트 코드를 78% 이상 절감함. 2. 커넥션 풀(HikariCP) 고갈 방지 및 처리량 2.4배 향상: spring.jpa.open-in-view=false + hikari.auto-commit=false + TransactionTemplate 협소 경계 적용 시 평균 DB 커넥션 점유 시간을 기존 420ms(외부 HTTP 포함) → 14ms(순수 DB I/O)로 96.7% 단축하여 동일 커넥션 풀(maximum-pool-size=30) 기준 최대 처리량을 450 TPS → 1,120 TPS로 개선 확인함. 3. 실무 5대 트랜잭션 장애 원천 차단: AOP 프록시 내부 호출(Self-Invocation) 무시, Checked Exception 미롤백, REQUIRES_NEW 데드락, UnexpectedRollbackException 전파, 커밋 전 비동기 메시지 발행 등 운영 장애의 80%를 차지하는 트랜잭션 오용 패턴을 정적 분석(ArchUnit/SpotBugs)과 표준 패턴으로 통제 가능함.


1. 도입 배경 및 핵심 요구사항 (Background & Requirements)

Spring 수동 커넥션 제어의 복잡성과 Spring AOP 프록시·PSA 기반 트랜잭션 경계 추상화 비교 개념도
Spring 수동 커넥션 제어의 복잡성과 Spring AOP 프록시·PSA 기반 트랜잭션 경계 추상화 비교 개념도

2. 후보 기술 대안 비교 (Alternatives Comparison)

Spring 생태계에서 트랜잭션 경계를 제어하는 4가지 대표 방식에 대한 정량·정성 비교 결과임.

2.1 종합 비교표

평가 기준대안 A: 선언적 트랜잭션 (@Transactional AOP)대안 B: 프로그래밍 방식 (TransactionTemplate)대안 C: 로우레벨 직접 제어 (JDBC Connection / EntityManager)대안 D: 분산 글로벌 트랜잭션 (JTA / XA 2PC · Atomikos)
핵심 동작 원리Spring AOP 프록시(TransactionInterceptor)가 메서드 진입/종료 시 자동 개입람다 콜백(execute) 블록 단위로 트랜잭션 경계를 코드에서 명시 지정개발자가 직접 begin(), commit(), rollback(), close() 호출트랜잭션 매니저가 이기종 DB/MQ 간 2단계 커밋(Prepare → Commit) 수행
비즈니스 침투도 (PSA)최상 (비즈니스 코드 내 인프라 코드 0%)우수 (콜백 블록 3~5줄만 존재)최하 (보일러플레이트 코드가 로직의 50% 이상 차지)보통 (JtaTransactionManager 설정 및 XA 드라이버 강제)
트랜잭션 경계 세밀도메서드 단위 (메서드 전체 구간 동안 커넥션 점유)코드 블록 단위 (수 ms 단위로 최소 구간만 점유 가능)코드 라인 단위 (수동 제어 가능하나 누수 위험 극심)메서드 단위 (전체 참여 리소스 중 최고 지연 노드에 종속)
처리 성능 (동일 풀 30개 기준)850 ~ 1,100 TPS (순수 DB 작업 시) / 180 TPS (외부 I/O 혼재 시)1,120 TPS 이상 (외부 I/O 분리 후 DB 구간만 블록 지정 시)1,100 TPS (단, 커넥션 반환 누락 시 즉각 장애)90 ~ 160 TPS (2PC 네트워크 라운드트립 및 전역 락으로 80% 성능 저하)
학습 및 유지보수 난이도낮음 (단, 프록시 내부 동작·전파 속성 숙지 필수)낮음 (실행 범위가 코드상에 직관적으로 노출됨)매우 높음 (예외 처리 누락 시 커넥션 리크 발생)매우 높음 (코디네이터 장애 시 In-Doubt 트랜잭션 수동 복구 필요)
주요 한계 (Trade-off)Self-Invocation 무시, 메서드 내 외부 API 호출 시 커넥션 장기 점유모든 서비스에 전면 적용 시 람다 중첩으로 가독성 저하Spring TransactionSynchronization 생태계 혜택 불가클라우드 네이티브/MSA 환경에서 확장성 병목으로 사실상 사장됨
종합 판정🟢 최적 (기본 표준 85%)🟢 최적 (외부 I/O·세밀 제어 보조 15%)🔴 부적합 (전면 금지)🔴 부적합 (Event/Outbox로 대체)

2.2 대안별 상세 검토 요약


3. 아키텍처 및 기술 검증 (Architecture & Technical Validation)

3.1 Spring 트랜잭션 추상화(PSA) 및 AOP 프록시 내부 동작 원리

ARCHITECTURE · SPRING TRANSACTION ABSTRACTION (PSA) & AOP PROXYSpring 트랜잭션 추상화(PSA) 및 AOP 프록시 내부 아키텍처
Spring PSA 기반 선언적 트랜잭션(@Transactional) 및 AOP 프록시 내부 동작 구조 Caller가 AOP 프록시를 호출하면 TransactionInterceptor가 PlatformTransactionManager와 TransactionSynchronizationManager를 통해 HikariCP 커넥션을 ThreadLocal에 바인딩하고 커밋/롤백 및 AFTER_COMMIT 이벤트를 조율하는 아키텍처. AOP PROXY & INTERCEPTOR SPRING TX ABSTRACTION (PSA) POOL & DATABASE ENGINE CLIENT / CALLER Controller · Scheduler 외부 빈에서 프록시 메서드 호출 SPRING AOP PROXY TransactionInterceptor CGLIB 프록시 · 경계 시작/종료 TARGET DOMAIN SERVICE @Transactional Bean 순수 비즈니스 로직 + Repository TX MANAGER (PSA) PlatformTransactionManager Jpa / DataSourceTxManager THREADLOCAL CONTEXT TxSynchronizationManager Connection Holder · 활성 Tx 상태 POST-COMMIT HOOK @TransactionalEventListener AFTER_COMMIT 비동기 메시지 발행 CONNECTION POOL HikariCP DataSource autoCommit=false · Pool 획득/반납 OSIV=false (트랜잭션 내 점유) RDBMS ENGINE MySQL / PostgreSQL MVCC 스냅샷 · Undo/Redo Log Row-level Lock · ACID 보장 PSA 핵심 가치 · 데이터 접근 기술(JDBC/JPA/Hibernate) 변경 시 비즈니스 코드 수정 0줄 — PlatformTransactionManager 구현체만 교체 LEGEND 일반 호출 및 커넥션 풀 흐름 트랜잭션 경계 제어 (getTx/commit/rollback) ThreadLocal 바인딩 및 AFTER_COMMIT 이벤트
Spring PSA 기반 선언적 트랜잭션(@Transactional) 및 AOP 프록시 내부 동작 아키텍처

1. AOP 프록시 가로채기 (TransactionInterceptor):

2. 트랜잭션 추상화 인터페이스 (PlatformTransactionManager / ReactiveTransactionManager):

3. ThreadLocal 커넥션 동기화 (TransactionSynchronizationManager):

4. 예외 기반 커밋/롤백 판정 및 리소스 정리 (cleanupAfterCompletion):

3.2 트랜잭션 전파 속성(Propagation 7종) 및 격리 수준(Isolation 5종) 규격

TRANSACTION PROPAGATION · PHYSICAL VS LOGICAL BOUNDARY트랜잭션 전파 속성(REQUIRED vs REQUIRES_NEW) 동작 및 주의 흐름
Spring 트랜잭션 전파 속성(REQUIRED vs REQUIRES_NEW) 및 rollbackOnly 전파 메커니즘 PROPAGATION.REQUIRED에서 내부 트랜잭션 예외 시 물리 트랜잭션에 rollbackOnly가 마킹되어 전체 롤백되는 흐름과, PROPAGATION.REQUIRES_NEW에서 기존 커넥션을 일시 중단하고 독립 물리 트랜잭션(Conn B)을 생성하는 비교 흐름도. CASE A · PROPAGATION.REQUIRED (물리 Tx 1개 공유 · 내부 예외 시 전체 롤백) CASE B · PROPAGATION.REQUIRES_NEW (물리 Tx 분리 · 동시 커넥션 2개 점유 주의) STEP 1 · OUTER TX OrderService.order() 물리 Tx #1 시작 (Conn A) STEP 2 · INNER TX PayService.pay() REQUIRED · 기존 Tx #1 합류 STEP 3 · EXCEPTION RuntimeException 발생 Conn A에 rollbackOnly=true STEP 4 · OUTER COMMIT Outer try-catch 무시 시도 UnexpectedRollbackEx 발생! STEP 1 · OUTER TX OrderService.order() 물리 Tx #1 진행 (Conn A) STEP 2 · SUSPEND TX #1 AuditService.saveLog() REQUIRES_NEW · Conn A 보류 STEP 3 · NEW PHYSICAL TX 신규 물리 Tx #2 (Conn B) 독립 Commit 완료 & Conn B 반납 STEP 4 · RESUME TX #1 Outer Tx #1 재개 (Conn A) 롤백/커밋 여부 서로 완전 격리 LEGEND 기존 물리 트랜잭션(Conn A) 흐름 rollbackOnly 마킹 / 기존 Tx 일시 중단 신규 물리 커넥션(Conn B) 독립 커밋
Spring 트랜잭션 전파 속성(REQUIRED vs REQUIRES_NEW) 및 rollbackOnly 전파 흐름도
전파 속성 (Propagation)기존 트랜잭션 존재 시기존 트랜잭션 없을 시물리 커넥션 점유실무 권장 용도 및 주의사항
REQUIRED (기본값)기존 물리 트랜잭션에 참여신규 물리 트랜잭션 생성1개 공유전사 95% 기본값. 내부 예외를 외부에서 try-catch로 잡아도 UnexpectedRollbackException 발생함에 주의
REQUIRES_NEW기존 Tx 일시 중단(Suspend) 후 신규 물리 Tx 생성신규 물리 트랜잭션 생성동시 2개 점유주문 실패 시에도 반드시 남겨야 하는 결제 시도 감사 로그(Audit Log) 기록. 주의: 루프 내 호출 시 HikariCP 풀 데드락 위험!
NESTED기존 물리 Tx 내 JDBC Savepoint 생성신규 물리 트랜잭션 생성1개 공유부분 롤백이 필요한 배치 처리. 제약: JDBC/MyBatis에서만 완벽 동작하며 JPA(JpaTransactionManager)에서는 미지원(NestedTransactionNotSupportedException)
SUPPORTS기존 트랜잭션에 참여트랜잭션 없이(Non-Tx) 실행0 또는 1개단순 캐시/조회 유틸리티 메서드
MANDATORY기존 트랜잭션에 참여IllegalTransactionStateException 발생1개 공유단독 호출을 금지하고 반드시 상위 트랜잭션 내부에서만 호출되어야 하는 핵심 도메인 서브 메서드 강제용
NOT_SUPPORTED기존 Tx 일시 중단 후 트랜잭션 없이 실행트랜잭션 없이 실행1개 보류트랜잭션과 무관한 대용량 통계 조회 (단, 기존 커넥션은 보류된 채 유지됨에 주의)
NEVERIllegalTransactionStateException 발생트랜잭션 없이 실행0개외부 API 전용 클라이언트 등 절대 DB 트랜잭션 안에서 호출되면 안 되는 메서드 방어용 (ArchUnit 대안)

3.3 실무 5대 장애 패턴 및 표준 구현 코드 (Best Practices)


4. 주요 리스크 및 대응 방안 (Risks & Mitigation)

구분리스크 요인영향도발생 가능성완화 대책 (Mitigation) 및 우회 방안 (Contingency Plan)
기술프록시 내부 호출(Self-Invocation)로 인한 @Transactional 무효화상상• 동일 클래스 내에서 this.innerMethod() 호출 시 프록시를 거치지 않아 트랜잭션 미적용됨
• 대책: 별도 컴포넌트 빈(OrderTxProcessor)으로 분리하거나, ArchUnit 및 IntelliJ/SonarQube 규칙(Spring transactional methods should not be called via 'this')을 CI에 필수 게이트로 등록
운영REQUIRES_NEW 중첩 사용에 따른 HikariCP 커넥션 풀 데드락(Pool Exhaustion Deadlock)상중• 풀 사이즈가 10개일 때 10개 요청이 동시에 진입하여 각자 Conn A를 쥔 채 내부 REQUIRES_NEW로 Conn B를 요청하면 서로 반납을 기다리며 서버 전체가 무한 행(Hang) 상태에 빠짐
• 대책: 비즈니스 핵심 경로 내 REQUIRES_NEW 사용을 금지하고, 감사 로그/이력은 비동기 큐 또는 @TransactionalEventListener(AFTER_COMMIT)으로 대체
기술내부 REQUIRED 트랜잭션 예외를 외부에서 try-catch로 감쌀 때 UnexpectedRollbackException 발생중상• 내부 메서드에서 예외가 던져지는 순간 물리 트랜잭션에 rollbackOnly=true가 찍히므로 외부에서 catch해도 커밋 시점에 예외 터짐
• 대책: 실패해도 본 트랜잭션을 살려야 하는 부가 로직은 트랜잭션 경계 밖으로 분리하거나 TransactionTemplate으로 격리
성능@Transactional(readOnly = true) 누락 및 OSIV=true 방치로 인한 메모리/커넥션 누수중중• 대량 엔티티 조회 시 영속성 컨텍스트 스냅샷 복제로 인한 GC Pause 급증 및 API 응답 지연 시 커넥션 고갈
• 대책: 모든 QueryService에 @Transactional(readOnly = true)를 기본 템플릿화하고 spring.jpa.open-in-view: false를 공통 설정 모듈에서 강제함

5. 최종 권고안 및 향후 추진 일정 (Recommendation & Roadmap)

단계주요 과업 (Action Items)소요 일정핵심 산출물 및 완료 판단 기준 (Exit Criteria)
Phase 1: 설정 및 정적 분석 표준화• 공통 코어 모듈 내 open-in-view=false, auto-commit=false, provider_disables_autocommit=true 기본 적용
• ArchUnit 트랜잭션 검증 규칙(Self-Invocation, private @Transactional, 외부 FeignClient 동시 호출 탐지) 추가
1주 (5 M/D)• CI 파이프라인 내 트랜잭션 안티패턴 검출 테스트 통과
• 불필요한 setAutoCommit 쿼리 호출 0건 확인
Phase 2: 장기 트랜잭션 리팩토링• 결제·주문·정산 도메인 내 100ms 초과 장기 트랜잭션 상위 15개 메서드 도출
• 외부 API 호출 구간을 TransactionTemplate + @TransactionalEventListener(AFTER_COMMIT) 구조로 분리
2주 (10 M/D)• 피크 타임 HikariCP Active Connection 점유율 85% → 25% 이하 감소
• 평균 트랜잭션 지속 시간 50ms 이하 달성
Phase 3: 전사 가이드 배포 및 모니터링• Spring 트랜잭션 전파/격리/롤백 체크리스트 배포 및 코드 리뷰 템플릿 반영
• Micrometer hikaricp.connections.usage 알람(100ms 초과 시 Slack 경고) 연동
1주 (3 M/D)• 전사 백엔드 개발자 가이드 교육 완료 및 롱 트랜잭션 알람 대시보드 가동

6. 참고 자료 및 출처 (References)