
이번주 백엔드 세미나를 준비하면서 어떤 주제를 선정할지 많은 고민이 됐다. 백엔드 개발을 시작한지 얼마 되지 않았기 때문에 기본 개념을 공부하고 싶어 AOP 주제를 선택했다.
처음에 AOP를 알게 되었을 때는 OOP(객체 지향 프로그래밍)과 반대의 개념인 줄 알았다. 그러나 공부를 하면서 AOP는 그냥 또다른 프로그래밍 방법이라는 것을 깨달았다. Spring의 3요소 중 하나인 AOP를 공부하면 Spring과 좀 더 가까워지지 않을까..,?
AOP는 한 문장으로 정의하자면 다음과 같다.
애플리케이션 전역에 퍼져있는 부가 기능 로직들을 비즈니스 로직과 분리하여 모듈화하는 프로그래밍 방법
즉, 애플리케이션에 공통으로 존재하는 보안, 로깅, 트랜잭션 처리 등을 비즈니스 로직과 따로 분리하는 것이다.
주요 용어를 알아보자.
1. Target: 어떤 객체에 부가 기능을 적용할 것인가?
2. Advice: 어떤 부가기능을 적용할 것인가?
3. Join Point: 어디에 적용할 것인가?
4. Point Cut: 실제 advice를 적용한 시점
용어를 공부하며 Join Point와 Point Cut이 약간 헷갈렸다. 찾아보니 Join Point는 적용이 가능한 지점이며, Point Cut은 실제로 Join Point를 특정한 것이다. 즉 A란, 메서드 호출 시점에 호출할 것과 같이 구체적으로 Advice가 적용될 시점으로 이해하면 될 것이다.
Spring AOP는 프록시 패턴을 사용하여 AOP를 지원한다.

이 코드를 보면, @preAuthorize를 통해 권한 검사를 수행한다. @preAuthorize는 내부에 Spring AOP로직을 사용하여 메소드 실행 전에 권한 검사를 하는데, 프로세스를 보면 다음과 같다.

이 프록시 패턴을 통해서 간단하게 어노테이션을 사용하여 권한을 갖고 있는 사람만 메소드를 실행한다던지 해줄 수 있는게 너무 편리한 것 같다.
그런데, Spring AOP에도 한계가 있다.
1. self - invocation
2. private, static, final 접근 불가
Target 안에서 내부 메서드를 호출하면, 이미 Target은 프록시를 거쳤기 때문에 호출된 메서드는 더 이상 프록시를 거치지 않고 실제 메서드를 바로 호출한다. 이러한 자기호출 문제로 인해 필요한 권한 검사가 무효화 될 수도 있을 것이다.
다음으로, private, static, final은 Spring AOP를 적용할 수 없는데 그 이유는 프록시 패턴에 있다. Spring AOP는 2가지 프록시를 사용한다.
1. JDK dynamic proxy
2. CGlib proxy
인터페이스가 있으면 JDK dynamic proxy를 사용하고 없으면 CGlib proxy를 사용하게 되는데, JDK dynamic proxy는 인터페이스 기반으로 인터페이스의 public 메서드를 오버라이드 해서 AOP의 advice를 끼워 넣는다.
CGlib proxy는 클래스를 상속받기 위해 바이트코드를 조작하는데, 그렇기 때문에 상속이 불가능한 접근 제어자는 Spring AOP를 사용할 수 없는 것이다.
Spring AOP의 이러한 한계를 해결하기 위해서는 AspectJ를 사용할 수 있다. AspectJ에 대해서 알아보기 전에 위빙에 대해서 알아보자.
위빙 : 횡단 관심사(Aspect)를 애플리케이션의 핵심 로직(Target)에 '엮어 넣는' 과정

이러한, 위빙은 자바 코드 실행 과정에서 일어나는데,
1.컴파일 타임 위빙 (CTW, Compile-Time Weaving )
컴파일하는 시점 2.post-Compile 위빙 (Binary Weaving)
(컴파일 후)3.로드 타임 위빙 ( LTW, Load-time Weaving )
로딩하는 시점4.런타임 위빙 ( RTW, Run-time Weaving )
실행되고 있는 도중Spring AOP는 런타임 위빙을 사용하고, AspectJ는 컴파일 타임 위빙과 로드 타임 위빙을 사용한다.
이제 AspectJ에 대해 알아보자.
AspectJ는 언어 차원에서 AOP를 지원한다. 즉, 자바 언어를 확장하여 AOP를 지원한다는 것인데 그래서 Spring AOP에서는 하지 못한 바이트코드 조작을 통해서 위빙을 할 수 있다.


SecureService의 sensitiveAction 메소드를 실행하면 어떻게 될까? sensitiveAction 메소드는 간단히 로그를 출력하는 코드이지만, 컴파일한 바이트코드를 보면 이렇게 된다.

SecureService.java를 컴파일한 파일인데, 분명 secureService 메소드 하나만 있었는데 Aspect의 인스턴스를 가져오는 코드, Aspect의 checkRole 함수를 실행하는 코드가 같이 끼어있다는 것을 알 수 있다.
즉, AspectJ의 CTW (Compile-time weaving)을 사용하면 이렇게 실제 메소드 + Aspect 코드가 합쳐서 하나의 파일로 컴파일된다는 것을 알 수 있다.
이제 간단히 Spring에서 지원하는 PetClinicApplication에 권한을 검사하는 로직을 구현하여 AspectJ의 동작을 알아보겠다

이러한 로직을 추가한다.




이렇게 보면, initCreateForm에 RequireRole 어노테이션을 사용하여 권한 검사를 수행하고 initCreateForm 메서드 안에서 processSensitiveData를 호출할 때도 권한 검사를 하고 싶어, ProcessSensitiveData 메서드 위에도 RequireRole을 사용해준다.
결과를 보면, vet 계정으로 접근했을 떄는, Admin 권한만 허용하는 processSensitiveData메서드의 접근 권한을 갖고 있지 않아 접근이 거부된다.

즉, Spring AOP를 못했던 자기호출 문제가 해결된 것이다.
결론: 사실, 실무에서는 Spring AOP만으로도 충분하다. Spring AOP는 단순하고 설정 역시 필요 없기 때문이다. 그러나, AspectJ의
포인트컷 문법을 차용하여제공하기 때문에 AspectJ와 어떤 차이가 있는지 정도는 알아두면 앞으로 개발자의 길에 더 도움이 되지 않을까 해서 이 주제를 공부해봤다.
정말 유익한 글이네요!
좋은 정보 얻고 갑니다~