[Spring] AOP 개요


AOP 개요


Aspect Oriented Programming, 줄여서 AOP는 문제를 바라보는 관점을 기준으로 프로그래밍하는 기법을 말한다. AOP는 문제를 해결하기 위한 핵심 관심 사항과 전제에 적용되는 공통 관심 사항을 기준으로 프로그래밍 함으로써 공통 모듈을 여러 코드에 쉽게 적용할 수 있도록 도와 준다.


AOP를 구현하는 다양한 방법이 존재하지만, 기본적인 개념은 공통 관심 사항을 구현한 코드를 핵심 로직을 구현한 코드 안에 삽입한다는 것이다.



AOP 기법에서는 핵심 로직을 구현한 코드에서 공통 기능을 직접적으로 호출하지 않는다. 핵심 로직을 구현한 코드를 컴파일 하거나, 컴파일 된 클래스를 로딩하거나, 또는 로딩한 클래스의 객체를 생성할 때 AOP가 적용되어 핵심 로직 구현 코드 안에 공통 기능이 삽입된다.


AOP 프로그래밍에서는 AOP 라이브러리 공통 기능을 알맞게 삽입해주기 때문에 개발자는 게시글 쓰기나 목록 읽기와 같은 핵심 로직을 구현할 때 트랜잭션 적용이나 보안 검사와 같은 공통 기능을 처리하기 위한 코드를 핵심 로직 코드에 삽입할 필요가 없다. 핵심 로직을 구현한 코드에 공통 기능 관련 코드가 포함되어 있지 않기 때문에 적용해야 할 공통 기능이 변경되더라도 핵심 로직을 구현한 코드를 변경할 필요가 없다. 단지, 공통 기능 코드를 변경한 뒤 핵심 로직 구현 코드에 적용만 하면 된다.


1. AOP 용어


(1) Advice: 언제 공통 관심 기능을 핵심 로직에 적용할 지를 정의하고 있다. 예를 들어, '메서드를 호출하기 전'(언제)에 '트랜잭션을 시작한다.'(공통기능)기능을 적용한다는 것을 정의하고 있다.

조인 포인트에 삽입되어져 동작할 수 있는 코드를 '어드바이스'라 한다. 관점으로서 분리되고 실행시 모듈에 위빙된 구체적인 처리를 AOP에서는 Advice라고 한다. Advice를 어디에서 위빙하는지는 뒤에 나오는 PointCut이라는 단위로 정의한다. 또한 Advice가 위빙되는 인스턴스를 '대상객체'라고 한다.


cp.) 스프링의 Advice 타입

- Around Advice: Joinpoint 앞과 뒤에서 실행되는 Adcvice

- Before Advice: Joinpoint 앞에서 실행되는 Advice

- After Returning Advice: Jointpoint 메서드 호출이 정상적으로 종료된 뒤에 실행되는 Advice

- After Throwing Advice: 예외가 던져질 때 실행되는 Advice

- Introduction:  클래스에 인터페이스와 구현을 추가하는 특수한 Advice


(2) JoinPoint: Advice를 적용 가능한 지점을 의미한다. 메서드 호출, 필드 값 변경 등이 Joinpoint에 해당한다.

클래스의 인스턴스 생성 시점', '메소드 호출 시점', '예외 발생 시점'과 같이 어플리케이션을 실행할 때 특정 작업이 시작되는 시점을 '조인포인트'라고 한다. 실행시의 처리 플로우에서 Advice를 위빙하는 포인트를 JointPoint라고 한다. 구체적으로는 메서드 호출이나 예외발생이라는 포인트를 Joinpoint라고 한다.


(3) Pointcut: Joinpoint의 부분 집합으로서 실제로 Advice가 적용되는 Jointpoint를 나타낸다. 스프링에서는 정규 표현식이나 AspectJ 문법을 이용하여 Pointcut을 정의할 수 있다. 여러 개의 조인포인트를 하나로 결합한 것을 포인트 컷이라 한다.

하나 또는 복수의 Jointpoint를 하나로 묶은 것을 Pointcut 이라고 한다. Advice의 위빙 정의는 Pointcut을 대상으로 설정한다. 하나의 Pointcut에는 복수 Advice를 연결할 수 있다. 반대로 하나의 Advice를 복수 Pointcut에 연결하는 것도 가능하다.


(4) Weaving: Advice를 핵심 로직 코드에 적용하는 것을 weaving 이라고 한다. 즉 공통 코드를 핵심 로직 코드에 삽입하는 것이 weaving이다.

어드바이스를 핵심 로직 코드에 삽입하는 것을  위빙이라고 한다.


(5) Aspect: 여러 객체에 공통으로 적용되는 공통 관심 사항을 Aspect라고 한다. 트랜잭션이나 보안 등이 Aspect의 좋은 예이다.

여러 객체에 공통으로 적용되는 공통 관점 사항을 에스펙트라 한다.


(6) Target

핵심 로직을 구현하는 클래스를 말한다.


(7) advisor

어드바이스와 포인트컷을 하나로 묶어 취급한 것을 '어드바이저'라 부른다.

advisor와 Pointcut을 하나로 묶어 다루는 것을 Advisor라고 한다. Advisor는 스프링 AOP에만 있는 것인데, 관점 지향에서 관점을 나타내는 개념이라고 할 수 있다.


2. 세 가지 Weaving 방식

advice를 Weaving하는 방식에는 다음과 같이 세 가지 방식이 존재한다.


(1) 컴파일시에 Weaving 하기

컴파일 시에 코드를 삽입하는 방법은 AspectJ에서 사용하는 방식이다. 컴파일 방식에서는 핵심 로직을 구현한 자바 소스 코드를 컴파일할 때에 알맞은 위치에 공통 코드에 삽입한다.

따라서 AOP가 적용된 클래스 파일이 생성된다. 컴파일 방식을 제공하는 AOP 도구는 공통 코드를 알맞은 위치에 삽입할 수 있도록 도와주는 컴파일러나 IDE를 함께 제공한다.


(2) 클래스 로딩 시에 Weaving 하기

클래스를 로딩할 때에 AOP를 적용할 수도 있다. AOP 라이브러리는 JVM이 클래스를 로딩할 때 클래스 정보를 변경할 수 있는 에이전트를 제공한다. 이 에이전트는 로딩한 클래스의 바이너리 정보를 변경하여 알맞은 위치에 공통 코드를 삽입한 새로운 클래스 바이너리 코드를 사용하도록 한다. 즉 원본 클래스 파일은 변경하지 않고 클래스를 로딩할 때에 JVM이 변경된 바이트 코드를 사용하도록 함으로써 AOP를 적용한다.


(3) 런타임 시에 Weaving 하기

런타임 시에 AOP를 적용할 때에는 소스 코드나 클래스 정보 자체를 변경하지 않는다. 대신 프록시에 이용하여 AOP를 적용한다.

프록시 기반의 AOP는 핵심 로직을 구현한 객체에 직접 접근하는 것이 아니라 중간에 프록시를 생성하여 프록시를 통해서 핵심 로직을 구현한 객체에 접근하게 된다.

이때, 프록시의 핵심 로직을 실행하기 전 또는 후에 공통 기능을 적용하는 방식으로 AOP를 적용하게 된다. 프록시 기반에서는 메서드가 호출될 때에만 Advice를 적용할 수 있기 때문에 필드 값 변경과 같은 Jointpoint에 대해서는 적용할 수 없는 한계가 있다.


4. 프록시를 이용한 AOP 구현

스프링은 프록시를 이용하여 AOP를 구현하고 있다. 스프링은 Aspect의 적용 대상이 되는 객체에 대한 프록시를 만들어 제공하며, 대상 객체를 사용하는 코드는 대상 객체에 직접 접근하기 보다는 프록시를 통해서 간접적으로 접근하게 된다. 이 과정에서 프록시는 공통 기능을 실행한 뒤 대상 객체의 실제 메서드를 호출하거나 또는 대상 객체의 실제 메서드가 호출된 뒤에 공통 기능을 실행하게 된다.

스프링에서 어떤 대상 객체에 대해 AOP를 적용할 지의 여부는 설정 파일을 통해서 지정할 수 있으며, 스프링은 설정 정보를 이용하여 런타임에 대상 객체에 대한 프록시 객체를 생성하게 된다.

프록시 객체를 생성하는 방식은 대상 객체가 인터페이스를 구현하고 있으냐 없느냐 여부에 따라 달라진다.

대상 객체가 인터페이스를 구현하고 있다면, 스프링은 자바 리플렉션 API가 제공하는 java.lang.reflect.Proxy를 이용하여 프록시 객체를 생성한다. 이때 생성된 프록시 객체는 대상 객체와 동일한 인터페이스를 구현하게 되며, 클라이언트는 인터페이스를 통해서 필요한 메서드를 호출하게 된다. 하지만, 인터페이스를 기반으로 프록시 객체를 생성하기 때문에 인터페이스에 정의되어 있지 않은 메서드에 대해서는 AOP가 적용되지 않는 점에 유의해야 한다.

대상 객체가 인터페이스를 구현하고 있지 않다면, 스프링은 CGLIB를 이용하여 클래스에 대한 프록시 객체를 생성한다. CGLIB는 대상 클래스를 상속받아 프록시를 구현한다.

따라서, 대상 클래스가 final인 경우 프록시를 생성할 수 없으며, final인 메서드에 대해서는 AOP를 적용할 수 없게 된다.