[Spring] 스프링 AOP 란?

반응형

들어가기전에


핵심 기능과 부가 기능

어플리케이션 로직은 크게 핵심기능과 부가기능으로 나눌 수 있다.

  • 핵심기능
    객체가 제공하는 고유의 기능
    예) 회원 등록 로직, 주문 로직 등
  • 부가기능
    핵심기능을 보조하는 기능
    예) 로그 로직, 트랜젝션 로직, 보안 로직

 

보통 부가기능은 여러 곳에서 공통으로 사용되는 경우가 많다. 예를들어 모든 호출에서 로그를 남기는 경우, 보안을 강화하는 로직을 이용하는 경우 등이 있다. 이러한 부가 기능은 횡단 관심사 (cross-cutting concerns) 라고 한다.

이렇게 부가기능을 여러 클래스에 적용하려면 어떻게 해야할까? 클래스의 수 대로 모든 클래스에 동일한 코드를 추가하면 될까? 이는 매우 번거롭고 바람직하지 못하다. 만약, 로그 로직에 변경이 발생한다면 그 클래스 수 만큼 수정을 해야하기 때문이다.

 

부가기능 적용의 문제

  • 아주 많은 코드의 반복
  • 중복코드의 발생
  • 부가기능에 변경이 있다면 그에따른 많은 수정이 필요
  • 부가기능의 적용 대상을 변경한다면 그에따른 많은 수정이 필요

코드를 작성하는데 있어서 변경으로 다른 코드에 최대한 영향을 주지 않도록 잘 모듈화 해야 한다. 수정에는 수많은 위험이 도사리고 있기 때문이다. 그런데 이렇게 특정 로직을 어플리케이션 전반에 적용할때의 문제는 일반적인 OOP 방식으로는 해결이 어렵다.

 

 

 

AOP


Aspect - 핵심 기능과 부가 기능을 분리

부가 기능을 핵심 기능에서 분리하고 한 곳에서 관리하고 어디에 적용할지 선택할 수 있도록 하나의 모듈로 만들었는데 이것이 Aspect 이다. 스프링의 @Aspect 가 바로 이것이다.

 

스프링이 제공하는 Advisor 도 Advice (부가기능) 과 Pointcut (적용대상) 을 가지고 있으므로 개념상 Aspect 로 볼 수 있다.

 

Aspect 는 "관점" 이라는 의미이다. 이름 그대로 어플리케이션을 바라보는 관점을 하나하나의 기능에서 횡단 관심사 (cross-cutting concerns) 관점으로 달리 보는 것이다. 이렇게 Aspect 를 사용한 프로그래밍 방식관점 지향 프로그래밍 AOP (Aspect-Oriented Programming) 이라 한다.

 

AOP (관점 지향 프로그래밍) 는 OOP (객체 지향 프로그래밍) 를 대체하기 위한 것이 아니다. 횡단 관심사를 깔끔하게 처리하기 어려운 OOP 의 부족한 부분을 보완, 보조하는 목적으로 개발되었다.

 

 

 


(참고) AspectJ 프레임워크

AOP 의 대표적인 구현은 AspectJ 프레임워크 가 있다. 스프링도 AOP 를 지원하지만, 대부분 AspectJ 의 문법을 차용했고, AspectJ 가 제공하는 기능의 일부만 제공한다.

  • 자바 프로그래밍 언어에 대한 완벽한 관점지향 확장
  • 횡단 관심사의 깔끔한 모듈화
    • 오류 검사 및 처리
    • 동기화
    • 성능 최적화 (캐싱)
    • 모니터링 및 로깅

 

AOP 적용 방식

AOP 를 사용하면 핵심 기능과 부가 기능이 완전히 분리되어 관리된다. AOP 를 사용하면 부가기능 로직이 적용 되는 방식에 크게 3가지 방법이있다.

  1. 컴파일 시점
  2. 클래스 로딩 시점
  3. 런타임 시점 (프록시)

 

1. 컴파일 시점 AOP 적용

xxx.java 소스 코드를 컴파일러를 사용해서 xxx.class 를 만드는 시점에 부가기능 로직을 추가할 수 있다. 이때 AspectJ 가 제공하는 특별한 컴파일러를 사용해야 한다. 컴파일 된 xxx.class 를 디컴파일 해보면 aspect 관련 호출 코드가 들어간다. AspectJ 컴파일러는 Aspect 를 확인해서 해당 클래스가 적용 대상인지 먼저 확인하고, 적용 대상인 경우 부가기능 로직을 적용한다. 이렇게 원본 로직에 부가기능 로직이 추가되는 것을 위빙(Weaving) 이라 한다.

 

・ 위빙 (Weaving) : 옷감 따위를 짜다, 엮다 라는 의미이다. Aspect 와 실제 코드를 엮는 것을 의미한다.

 

 단점 : 컴파일 시점에 부가 기능을 적용하려면 AspectJ 컴파일러가 필요하며 복잡해진다.

 

2. 클래스 로딩 시점 AOP 적용

자바를 실행하면 xxx.class 파일을 JVM 내부의 클래스 로더에 보관한다. 이때 중간에서 xxx.class 파일을 조작한 다음 JVM 에 올릴 수 있다. 자바 언어는 xxx.class 를 JVM 에 저장하기 전에 조작할 수 있는 기능을 제공한다. (java Instrumentation) 수 많은 모니터링 툴들이 이 방식을 사용한다. 이 때 aspect 를 적용하는 것을 로드 타임 위빙이라 한다.

 

 단점 : 로드 타임 위빙은 자바를 실행할 때 특정 옵션 (java -javaagent) 를 통해 클래스 로더 조작기를 지정해야 하는데, 이 부분이 번거롭고 운영이 어렵다.

 

3. 런타임 시점 AOP 적용 (프록시)

컴파일도 다 끝나고, 클래스 로더에 클래스도 다 올라가서 이미 자바가 실행되고 난 다음을 말한다. 자바의 main 메소드가 이미 실행된 다음이다. 즉, 자바 언어가 제공하는 범위 안에서 부가기능을 적용해야 한다. 스프링과 같은 컨테이너의 도움을 받고 프록시와 DI, BeanPostProcessor 와 같은 개념들이 요구된다. 이렇게 하면 최종적으로 프록시를 통해 스프링 빈에 부가기능을 적용할 수 있다.

 

프록시를 사용하기 때문에 AOP 기능에 일부 제약이 있지만 스프링만 있으면 AOP 를 적용할 수 있다. 특정 컴파일러나 자바 실행시의 복잡한 옵션, 클래스 로더 조작기를 설정하지 않아도 된다.

 

 

4. AOP 적용 시점 정리

  1. 컴파일 시점 : 원본 코드에 aspect 통한 부가기능 호출코드가 포함된다. AspectJ 를 직접 사용해야 한다.
  2. 클래스 로딩 시점 : 원본 코드에 aspect 통한 부가기능 호출코드가 포함된다. AspectJ 를 직접 사용해야 한다.
  3. 런타임 시점 : 원본 코드는 그대로 유지되며 프록시를 통해 부가기능이 적용된다. 즉 항상 프록시를 통해야 부가기능을 사용할 수 있다. 스프링 AOP 가 사용하는 방식이다.

 

5. AOP 적용 위치

  • 적용 가능 시점 (조인 포인트) : 생성자, 필드값 접근, static 메소드 접근, 메소드 실행 등 AOP를 적용할 수 있는 지점을 조인 포인트 (Join Point) 라 한다.
  • AspectJ 를 사용해서 컴파일 시점, 클래스 로딩 시점에 적용하는 AOP : 바이트코드를 실제 조작하기 때문에 해당 기능을 모든 지점에 다 적용할 수 있다.
  • 프록시 방식을 사용하는 스프링 AOP : 메소드 실행 시점에만 AOP 를 적용할 수 있다.
    • 프록시는 메소드 오버라이딩 개념으로 동작한다. 따라서 생성자, static 메소드, 필드값 접근에는 프록시 개념이 적용될 수 없다.
    • 프록시를 사용하는 스프링 AOP 의 조인포인트는 메소드 실행으로 제한 된다.
  • 프록시 방식을 사용하는 스프링 AOP 는 스프링 컨테이너가 관리할 수 있는 스프링 빈에만 AOP 를 적용할 수 있다.

 

 

AOP 용어 정리


  • 조인 포인트 (Join Point)
    • 어드바이스가 적용될 수 있는 위치, 메소드 실행, 생성자 호출, 필드값 접근, static 메소드 접근 같은 프로그램 실행 중 지점
    • 추상적인 개념. AOP 를 적용할 수 있는 모든 지점
    • 스프링 AOP 는 프록시 방식을 사용하므로 조인 포인트는 항상 메소드 실행 시점으로 제한된다.
  • 포인트컷 (Pointcut)
    • 조인포인트 중에서 어드바이스가 적용될 위치를 선별하는 기능
    • 주로 AspectJ 표현식을 사용해서 지정
      • (예) execution(* hello.proxy.app..*(..))
      • → hello.proxt.app 패키지화 하위 패키지의 모든 메소드에 적용
    • 프록시를 사용하는 스프링 AOP 는 메소드 실행 지점만 포인트컷으로 선별 가능
  • 타겟 (Target)
    • 어드바이스를 받는 객체, 포인트컷으로 결정
  • 어드바이스 (Advice)
    • 부가 기능
    • 특정 조인포인트에서 Aspect 에 의해 취해지는 조치
    • Around (주변) , Before (전) , After (후) 와 같은 다양한 어드바이스가 있음
      • @Around, @Before, @After, @AfterReturning, @AfterThrowing
  • 애스펙트 (Aspect)
    • 어드바이스 + 포인트컷 을 모듈화 한 것
    • @Aspect
    • 여러 어드바이스와 포인트컷이 함께 존재
@Component // 프록시방식을 사용하는 스프링AOP는 스프링컨테이너가 관리할 수 있는 스프링빈에만 적용가능
@Aspect // 스프링은 어노테이션의 선언만으로 Aspect 를 인식한다
public class MyAspect{

}
  • 어드바이저 (Advisor)
    • 하나의 어드바이스 + 하나의 포인트컷 으로 구성
    • 스프링 AOP 에서만 사용
  • 위빙 (Weaving)
    • 포인트컷으로 결정한 타겟의 조인포인트에 어드바이스를 적용하는 것
    • 위빙을 통해 핵심기능 코드에 영향을 주지 않고 부가기능을 추가할 수 있음
    • AOP 적용을 위해 애스펙트를 객체에 연결한 상태
      • 컴파일 타임 (Aspect Compiler)
      • 클래스 로드 타임
      • 런타임 (스프링 AOP 사용) , 프록시 방식
  • AOP 프록시
    • AOP 기능을 구현하기 위해 만든 프록시 객체, 스프링에서 AOP 프록시는 JDK 동적 프록시 또는 CGLIB 프록시 이다.

 

 

 

반응형