AOP로 로깅을 하거나 공통 부가기능을 Aspect 클래스로 분리하여 적용하였는데, 특정 부분에서만 AOP가 동작하지 않는 문제점을 확인했습니다.
부가 기능이 동작하지 않는 이유는 Self - Invocation(내부 호출) 이슈때문인데요.
이게 어떤 문제인지와 어떻게 해결 할 수 있는지에 대해서 알아보겠습니다.
Self - Invocation 이란 간단히 표현해서 자기 자신을 호출하는 것을 뜻합니다.
한 클래스에서 메서드가 내부 메서드를 호출하는 것에서 대부분 이 문제가 발생하게 됩니다.
간단한 RunService라는 클래스를 만들고, go와 run 두개의 메서드를 만들어 주었습니다.
go 메서드 내부에서 run을 호출하게 됩니다. 이럴경우 Self - Invocation 문제가 발생합니다.
어떤 문제가 발생하는지 Aspect 클래스를 만들어서 먼저 부가기능을 적용해보겠습니다.
현재 어떤 메서드가 실행중인지 간단히 출력하는 부가기능을 적용하였습니다.
아직 AOP에 대해 잘 모르시거나 개념이 혼동되시는 분들은 여기를 참고해주세요.
간단한 테스트 코드를 작성하여 실행해보겠습니다.
run 메서드 실행중.. 로그를 출력해주는 우리가 예상한 부가기능이 동작하지 않습니다.
왜 그런걸까요?
이 문제에 대해 이해하려면 Spring에서 어떻게 AOP를 동작시키는지 알아야합니다.
Spring AOP는 Runtime에 동작하며, CGLIB를 이용해 프록시 객체를 만들어 참조합니다.
Proxy 객체는 내부에 target(대상 클래스)을 가지고 있고, AOP는 프록시 객체를 참조하여 내부 타깃의 메서드를 호출하게 되고 프록시에 의해 호출된 메서드와 PointCut을 비교하여 Advice를 수행하게 됩니다.
go 메서드 수행 후 run 메서드를 실행하는 것은 내부 호출이 일어나므로, 이미 프록시가 아닌 target을 직접 참조하게 되어 부가기능이 동작하지 않게 됩니다.
이 문제를 해결하기 위한 방법은 크게 세가지 방법이 있습니다.
한개씩 살펴보겠습니다.
현재 문제는 Proxy 객체를 참조하지 않고 내부 target을 직접 참조하여 생기는 문제였습니다.
이 문제 해결법은 run 메서드 호출을 Proxy 객체를 통해서 호출하도록 변경하는 방법입니다.
AopContext는 현재 AOP 프록시를 반환합니다. AopContext에서 반환받은 프록시 객체로 run 메서드를 호출하면 프록시를 통한 호출이기때문에 부가기능이 정상적으로 동작합니다.
이 기능이 제대로 동작하려면 exposeProxy 기능을 true로 설정해주어야 합니다.
부가 기능이 제대로 동작하는 것을 확인할 수 있습니다.
Spring Boot 2.6.x 버전부터 순환참조는 예외를 발생시키도록 되어 있습니다.
이 방법으로 문제를 해결하기 위해서는 application.properties에 설정 변경이 필요합니다.
순환 참조를 허용하는 옵션을 켜주시면 손쉽게 문제를 해결할 수 있습니다.
RunService 클래스 안에서 RunService를 주입받는 방식입니다.
의존 관계가 RunService -> RunServie가 되므로 순환참조되고 있는 것입니다.
이 방법으로도 문제는 해결할 수 있지만 순환참조의 문제가 있어 그다지 좋은 방법은 아니라고 생각합니다.
순환참조는 컴포넌트 간의 명확한 경계가 사라지고 연쇄적으로 변경에 의한 영향이 발생할 수 있기 때문에 장기적으로 DDD나 MSA와 같은 방법론을 적용할 수 없다는 문제가 있습니다.
AspectJ Weaving을 이용해 바이트 코드를 조작하는 방법으로 이 문제를 해결할 수도 있습니다.
또 기존 코드를 수정하지 않고도 이 문제를 손쉽게 해결할 수 있다는 장점이 있습니다.
하지만 바이트 코드를 조작하는 방식의 AspectJ Weaving을 사용하면 Lombok 처럼 바이트 코드를 조작하는 다른 라이브러리와 충돌 가능성이 매우 높아 둘은 사실상 공존이 불가능하다고 봐야 하는 문제가 있어서 따로 다루지 않고 이 부분을 잘 설명한 포스팅이 있어 대신 첨부합니다.
AOP를 이용해 여러 부가기능을 개발하고 프로젝트에 적용해보면서 한번도 AOP의 한계점이나 문제점에 대해서 생각해본적이 없었습니다.
모든 기술에는 장점도 있지만 단점도 있고 그로인한 한계점과 그 한계를 돌파하기 위한 노력들이 있다는 것을 알게 된 좋은 공부였던 것 같습니다.
오늘도 읽어주셔서 감사합니다.
크...