개요
스프링의 @Transactional은 선언형 트랜잭션 처리를 가능하게 해주는 강력한 도구입니다. 하지만 실무에서는 @Transactional을 붙였음에도 트랜잭션이 전혀 동작하지 않는 이상한 상황을 겪곤 합니다. 그 대표적인 원인이 바로 Self-Invocation(자기 호출)입니다.
이 문서에서는 Self-Invocation이 발생하는 구조적 원인과 해결 방법을 명확하게 정리합니다.
핵심 개념 요약
항목 | 설명 |
@Transactional 처리 방식 | 프록시(proxy) 객체를 통해 트랜잭션을 적용 (JDK 동적 프록시 또는 CGLIB) |
Self-Invocation 정의 | 하나의 클래스 내부에서 자신의 메서드를 직접 호출하는 행위 |
문제의 본질 | 프록시가 아닌 실제 인스턴스(this)를 통해 메서드가 호출되어 AOP 미적용 |
결과 | 트랜잭션 미작동 → 롤백 안 됨 / 커밋 안 됨 / 트랜잭션 존재하지 않음 |
해결 전략 | 트랜잭션 메서드를 외부에서 프록시를 통해 호출되도록 리팩토링 |
문제가 되는 코드 예시 (Self-Invocation)
@Service
public class OrderService {
@Transactional
public void outer() {
// 비즈니스 로직
inner(); // Self-Invocation → 트랜잭션 적용 안 됨
}
@Transactional
public void inner() {
// DB 작업 (트랜잭션 없음)
}
}
- outer()는 정상적으로 트랜잭션을 시작하지 않으며, inner() 역시 프록시를 통하지 않으므로 트랜잭션 기능이 적용되지 않습니다.
해결 방법
1. 자신을 의존성으로 주입받아 호출
@Service
public class OrderService {
@Autowired
private OrderService self; // 주의: 프록시가 주입되어야 함
@Transactional
public void outer() {
self.inner(); // 프록시를 통해 트랜잭션 적용됨
}
@Transactional
public void inner() {
// 트랜잭션 적용 가능
}
}
2. 트랜잭션 대상 메서드를 분리된 Bean으로 이동
@Service
public class OrderProcessor {
@Transactional
public void process() {
// 트랜잭션 정상 적용
}
}
@Service
public class OrderService {
@Autowired
private OrderProcessor processor;
public void outer() {
processor.process(); // proxy 적용
}
}
실무에서의 주의사항 / Best Practices
- @Transactional이 붙은 메서드는 외부 Bean 또는 프록시 객체를 통해 호출되어야 한다.
- 자기 자신을 주입하는 방식은 순환 참조 문제가 발생할 수 있으므로 상황에 따라 @Lazy 또는 ObjectProvider 사용 고려
- AOP가 적용되었는지 확인하려면, 트랜잭션 로그 또는 TransactionSynchronizationManager를 통해 직접 확인 가능
- 테스트 시 @Transactional을 사용하면 테스트 클래스 자체가 트랜잭션 안에 있어 오히려 문제가 감춰질 수 있음 → 단위 테스트와 통합 테스트를 구분
참고자료
반응형
'Framework > Spring' 카테고리의 다른 글
[Batch] 배치 애플리케이션의 이해와 Spring Batch 기본 활용법 (0) | 2025.01.19 |
---|---|
[Batch] Spring Batch Scope의 개념과 Job Parameter 사용 방법 (0) | 2024.11.27 |
[Spring] Spring Boot 환경설정 가이드 (2) | 2024.09.23 |
[Spring] Spring 프레임워크란 무엇인가? (1) | 2024.09.20 |
[Spring] IoC와 DI란 무엇인가? (0) | 2024.09.20 |
댓글