Framework/Spring

Spring @Transactional이 동작하지 않는 이유: Self-Invocation 정리

Joonfluence 2025. 5. 7.

개요

스프링의 @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을 사용하면 테스트 클래스 자체가 트랜잭션 안에 있어 오히려 문제가 감춰질 수 있음 → 단위 테스트와 통합 테스트를 구분

참고자료

반응형

댓글