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() {
// 트랜잭션이 적용되는 로직
}
}
정리
- 생성자 주입을 사용해 빈을 주입받는 이유는 순환 참조를 미리 대비하기 위한 것인데, 회피하는 방법으로 코드를 구성하면 추후에 코드적인 문제가 생길 수 있다.
- 자기자신을 주입받아서 문제를 해결하는 방법은 되도록 지양하자.
- 최대한 외부에서 호출할 수 있도록 설계를 하자 .
'Server > Spring' 카테고리의 다른 글
Webclient io.netty.resolver.dns.DnsNameResolverTimeoutException (0) | 2025.06.24 |
---|---|
Spring WebFlux 와 Netty (0) | 2025.05.12 |
Spring Boot Servlet Requset euc-kr 인코딩 문제 (0) | 2024.01.10 |