-
Spring의 @Transactional은 AOP 프록시 기반 프록시를 “통과하는” 메서드 호출만 트랜잭션 어드바이스가 적용된다.Spring 2025. 10. 13. 17:12728x90반응형
Spring의 @Transactional은 AOP 프록시 기반이라서, 프록시를 “통과하는” 메서드 호출만 트랜잭션 어드바이스가 적용된다는 점입니다. 같은 클래스 내부에서 this.someMethod()로 자기 자신을 호출하면 프록시를 거치지 않으므로, @Transactional이 동작하지 않습니다.
왜 적용 안 되나요? (프록시 구조 이해)
- 스프링은 @Transactional을 보고 빈의 프록시(Proxy)를 만듭니다. (JDK 동적 프록시나 CGLIB)
🧩 예시 코드 구조
@Service public class OrderService { @Transactional public void placeOrder(String itemId) { System.out.println(">>> 비즈니스 로직 시작"); saveOrder(itemId); // DB 작업 System.out.println(">>> 비즈니스 로직 종료"); } private void saveOrder(String itemId) { System.out.println("주문 저장: " + itemId); // 실제 DB insert 로직 (예: orderRepository.save(...)) } }🧠 스프링이 내부적으로 하는 일
- 스프링 컨테이너가 OrderService를 스캔할 때, @Transactional이 붙은 메서드가 있는 것을 감지합니다.
- 그 후 원본 객체 대신 프록시 객체를 생성합니다. ( 프록시는 JDK Dynamic Proxy(인터페이스 기반) 또는 CGLIB(클래스 상속 기반) 기술을 이용합니다. )
예를 들어 OrderService의 실제 빈은 다음과 같이 만들어집니다:
OrderService@Proxy -> 내부적으로 OrderServiceImpl을 감쌈즉, 컨테이너에는 OrderService 대신 다음과 같은 객체가 들어있어요:
class OrderService$$SpringCGLIB$$Proxy extends OrderService { @Override public void placeOrder(String itemId) { // 트랜잭션 시작 TransactionManager.begin(); try { super.placeOrder(itemId); // 실제 비즈니스 로직 호출 TransactionManager.commit(); } catch (Exception e) { TransactionManager.rollback(); throw e; } } }단, @Transactional이 하나도 없으면, 스프링은 프록시를 만들지 않습니다.
- 외부에서 OrderService 빈을 호출하면 실제로는 OrderService의 프록시가 먼저 호출됩니다.
- 프록시는 트랜잭션 어드바이스를 적용한 뒤 실제 타깃(원본 객체)의 메서드를 호출합니다.
- 그런데 같은 클래스 내부 메서드 호출은 this.someMethod()로 원본 객체 내부에서 바로 일어나므로 프록시를 우회합니다 → 어드바이스 미적용 → @Transactional 무시.
예시 코드 (이미 프록시를 타고 메소드에 진입하게됨.)
@Service public class OrderService { // 외부에서 호출되는 진입점 (프록시를 타고 들어옴) public void placeOrder(String itemId) { // 내부 메서드 호출 - this로 직접 호출 → 프록시 우회 pay(itemId); // @Transactional인데도 적용 안 됨 } @Transactional public void pay(String itemId) { System.out.println("TX active? " + TransactionSynchronizationManager.isActualTransactionActive()); // DB 업데이트... } }마지막으로..
aop가 우회 된다는 설명 => 만약에 orderservice가 존재하고 order객체,log객체를 저장한다고 치자. 이때 orderservice에 save라는 메소드가 있고 여기서 트랜잭션을 거고 1차적으로 order를 적재함. 그후 logsave라는 메소드에도 request_new라는 트랜잭션을 걸고 save라는 메소드에서 logsave를 호출하면 프록시가 우회되서 logsave에서 에러가 발생하면 , order 객체를 저장하는 save도 롤백됨.
728x90반응형'Spring' 카테고리의 다른 글
CompletableFuture + commonPool은 왜 위험한가 — timeout을 걸어도 서버가 망가지는 이유 (0) 2025.12.24 List<MultipartFile> 가 톰캣서버,스프링에서 읽어드리는 과정 (1) 2024.11.12 Spring framework에서 사용하는 디자인 패턴 (0) 2024.11.11 AOP vs OOP 차이점 (0) 2024.09.12 Spring에서 @Autowired를 사용하는 것보다 생성자 주입 방식이 나은 이유? (0) 2024.07.24