본문 바로가기
Server/Spring

Spring AOP 내부 호출 작동 문제

by min_gui 2024. 12. 11.

AOP를 사용하다 보면 같은 클래스에서 AOP 어노테이션 설정된 메소드를 호출하면, 해당하는 AOP 가 작동하지 않는다.

@Autowired로 자기 자신 객체를 생성해 프록시 객체를 주입받으면 해결 되는 문제이다.

설계상의 응집도 문제로 좋은 설계 방법은 아니다.

이러한 문제가 왜 발생하는지 알아보자.

 

Spring AOP(Aspect Oriented Programming) 란?

  • 관점 지향 프록그래밍, 프록시 기반(Proxy-based) 구조를 사용합니다.
  • 로깅, 트랜잭션 관리 (@Transactional), 인증, 인가 등 해당 지점에서 실행됐을때, 자동으로 다른 로직도 실행 하기 위해 사용됩니다.
@Service
public class UserService {

    @Transactional
    public void createUser(User user) {
        // DB 작업
    }
}

 

Spring AOP 동작 방식

  • 작동 조건
    • 대상 객체가 Spring 컨테이너 빈(Bean)에 등록되어 있어야 합니다.
  • 실행 
    • 프록시 객체는 런타임 시점에 생성됩니다.
    • 클라이언트가 AOP가 적용된 메서드를 호출하면, 프록시 객체가 가로채어(AOP Advice) 필요한 부가 작업(로깅, 트랜잭션) 을 수행한뒤, 실제 메서드를 호출합니다.

 

내부호출 문제점

  • 내부 호출은 프록시 객체를 거치지 않고, 실제 객체의 메서드를 직접 호출해 프록시 객체가 감지하지 못해 AOP는 작동하지 않습니다.

 

해결방법

  • 클래스 내부에 자기 자신을 주입받아 프록시 객체를 통해 매서드를 호출하면 정상 작동 합니다.
  • @Autowired 이용(생성자 주입과 달리 빈 생성후 주입이 이루어져 순환참조 문제를 회피 할 수 있다), 생성자 주입은 Spring Bean 생성시 순환참조를 허용하지 않기 때문에 런타임 시점 에서 예외를 발생시킨다.
@Service 
public class MyServiceB { 
    @Autowired 
    private MyServiceB myServiceB; // 자기 자신을 주입받음
    
    public void methodB() { 
        myServiceB.methodA(); // AOP 적용됨 - 프록시를 통한 호출
    } 
    
    @Transactional
    public void methodA() {
        // 트랜잭션이 적용되는 로직
    }
}

 

 

정리

  • 생성자 주입을 사용해 빈을 주입받는 이유는 순환 참조를 미리 대비하기 위한 것인데, 회피하는 방법으로 코드를 구성하면 추후에 코드적인 문제가 생길 수 있다.
  • 자기자신을 주입받아서 문제를 해결하는 방법은 되도록 지양하자.
  • 최대한 외부에서 호출할 수 있도록 설계를 하자 .