@Aspect 는 스프링에서 어노테이션 방식으로 Advisor 를 생성할 때 사용한다.
스프링 로딩 시점에 자동 프록시 생성기가 빈으로 등록된 @Aspect를 Advisor 로 변경하여 저장하며, Advisor 와 동일하게 사용한다.
실무에선 대부분 @Aspect 로 Advisor를 생성한다.
이번 포스팅에서는 AOP 에 대해 알아보고, 스프링에서 활용하는 AOP 방식에 대해 이해해보자.
어플리케이션 로직은 핵심기능과 부가기능으로 나눌 수 있다. 위 그림에서는 로그 추적 로직이 부가기능이다. 그런데 이 부가기능을 여러 곳에서 공통으로 사용하면 각 핵심기능마다 부가기능을 추가해줘야한다. 이 경우 코드의 중복이 발생하고, 핵심과 부가기능이 공존하기 때문에 유지보수가 어렵다.
이 공통 부가기능인 횡단 관심사를 따로 빼서 관리하면서 동시에 적용할 수는 없을까? 이러한 고민에서 AOP가 탄생하였다.
부가기능을 각각의 코드에 넣기보다 따로 빼서 관리하는 것이 유지보수면에서 효율적이다. 그래서 이 "부가기능"과 "부가기능을 어디에 적용할지 선택하는 기능"을 합쳐서 하나의 모듈로 만들었는데, 이것이 애스펙트(Aspect)이다. 스프링이 사용하는 Advisor 도 Advice 와 Pointcut 으로 이루어지기에 하나의 Aspect라 할 수 있다.
AOP 란 이 Aspect 를 사용하는 프로그래밍 방식을 말하며, 횡단 관심사를 처리하기 어려운 OOP 를 보조하는 목적으로 개발되었다.
AOP 의 대표적인 구현으로는 AspectJ 프레임워크가 있다. 스프링도 AOP 를 지원하지만 AspectJ 의 문법을 차용하고, AspectJ 의 기능중에서는 일부만 제공한다.
부가기능을 따로 관리하며, 부가기능을 어디에 적용할지도 알고 있다고 하였다. 그러면 이 부가기능을 어떻게 실제로직에 추가할까? 세가지 방법이 있다.
.java 코드를 .class 파일로 컴파일할때 AspectJ 의 컴파일러를 사용해 바이트코드 조작으로 부가기능을 핵심기능에 삽입시켜버리는 것이다. 하지만 특별한 컴파일러도 필요하고 복잡해 잘 사용하지 않는다.
.class 파일은 클래스로더로 가서 런타임 데이터 영역에 적재된다. 이 때 클래스로더 조작기를 통해 부가기능을 핵심기능에 삽입시킨 후 런타임 데이터 영역에 적재시키는 것이다. 하지만 클래스 로더 조작기가 별도로 필요하며 번거롭다.
스프링이 채택한 방식이다.
런타임 시점이란 자바의 Main 메서드가 실행된 뒤를 말하며, 코드는 이미 런타임 데이터 영역에 적재되어 수정이 불가능하다. 그래서 스프링 컨테이너의 도움을 받아 빈 후처리기로 프록시객체로 빈을 바꿔치기하여 이를 실현한다.
하지만 프록시객체는 실제객체와 같은 메서드를 오버라이딩하면서 추가기능을 수행하도록 했기 때문에, 메서드 실행시점에만 부가기능을 추가할 수 있고, 스프링빈에만 AOP를 적용할 수 있다.
스프링은 Aspect 의 문법만 사용하고 프록시 방식의 AOP를 사용한다. 프록시 방식은 요청객체와 실제객체의 수정을 없게하기 위해 실제객체의 메서드를 오버라이딩하는 형태로 구현했었다. 그래서 메서드 실행 지점에만 AOP를 적용할 수 있다. 즉 생성자 호출, 필드값 접근시 등에는 부가기능을 추가 할 수 없다. 또한 빈 후처리기로 프록시객체로 바꿔치기 방식을 사용하므로, 스프링 빈에만 AOP를 적용할 수 있다.
그럼 왜 기능이 더 많은 AspectJ를 안쓸까? AspectJ 를 쓰려면 특별한 컴파일러 등을 사용하기에 복잡하다. 스프링은 자동 프록시 생성기 등 다양한 기능을 미리 구현해놨기에 편리하게 프록시 방식 AOP 를 사용할 수 있으며, 대부분의 문제를 이로 해결 가능하다.
조인 포인트
조인포인트란 AOP를 적용할 수 있는 지점을 말한다. 스프링은 프록시 기반 AOP를 쓰므로 메서드 실행지점으로 조인포인트가 제한된다. 생성자를 실행하거나 필드에 접근할 때 AOP를 실행할 수 없다.
포인트컷
조인 포인트 중 어드바이스 적용될 위치를 정하는 기능이다. 즉 어느 메서드에 부가기능을 적용할지 정하는 기능을 말한다.
타겟
어드바이스를 적용할 실제객체를 말한다. 포인트컷의 메서드를 보유했는지에 따라 결정된다.
어드바이스
부가기능 그 자체를 가리킨다.
애스펙트
어드바이스 + 포인트컷을 모듈화한 것이다. @Aspect.
어드바이저
1어드바이스 + 1포인트컷을 스프링에서 부르는 말이다.
위빙
타겟의 조인포인트에 어드바이스를 적용하는 것을 말한다. 즉 실제객체 코드에 부가기능을 추가하는 것이다. 스프링은 프록시 방식 위빙을 사용한다.
AOP 프록시
AOP 기능을 구현하기 위한 프록시 객체이다. JDK 동적 프록시객체 또는 CGLIB 프록시객체이다.
Aspect는 "부가기능" + "부가기능을 어디에 적용할지" 를 합친 것. Aspect 를 활용한 프로그래밍을 AOP 라고 한다. OOP 로 해결힘든 공통 부가기능 처리를 보조한다.
AOP 는 컴파일, 로드, 런타임 시점에 적용가능한데 스프링은 런타임시점에 프록시객체를 통해 AOP를 적용한다.
스프링은 메서드를 오버라이딩한 프록시객체로 AOP를 수행하므로 조인 포인트가 메서드 실행지점으로 한정된다.
스프링은 자동 프록시 생성기가 빈 객체를 프록시객체로 바꿔치기하여 AOP 수행하므로 스프링빈만 AOP 적용 가능하다.