본문 바로가기
개발/Spring

[Spring] 커스텀 Annotation 대상 AOP 적용하기

by baau 2024. 4. 13.

공부하게 된 계기

최근 업무를 하면서 선임 개발자분께서 AOP를 적용한 코드를 보고, 테스트를 해야 할 일이 있었다. 작년에 강의를 들으면서 Spring AOP에 대해 공부한 적이 있어 어느 정도 코드 이해했지만,

  • 선임과 대화하는 과정에서 헷갈리는 용어가 많아 원할한 커뮤니케이션이 힘들었고,
  • 다음에 AOP를 적용할 부분이 있을 때, 막상 적용하기는 많이 헤맬 것 같다는 생각이 들었다.

그래서 오늘은 김영한님의 스프링 핵심원리 고급 편 PDF를 다시 복기하면서 개념을 복습하고, Annotation를 대상으로 AOP를 적용하는 예시를 통해서 실무적으로도 학습하려고 한다.

 

AOP 탄생 배경

핵심 기능과 부가 기능

  • 핵심 기능
    • 해당 객체가 제공하는 고유의 기능
  • 부가 기능
    • 핵심 기능을 보조하기 위해 제공되는 기능 (로그 추적, 트랜잭션 기능 등등)
    • 여러 클래스에 걸쳐서 함께 사용되는 경우가 많다. (횡단 관심사)
    • 위 사례 경우, 많은 반복, 여러 곳에 퍼져있는 중복 코드, 변경시의 많은 중복 코드 수정의 문제점이 있다.

 

AOP 탄생

핵심 기능과 부가 기능 분리

  • 애스펙트(Aspect) : 부가 기능과 부가 기능을 어디에 적용할지 선택하는 기능을 하나의 모듈로 만든 것
  • @Aspect : 스프링이 제공하는 어드바이저(부가 기능)과 포인트컷(적용 대상)을 가지고 있다.
  • 애스펙트를 사용해서 프로그래밍 방식을 관점 지향 프로그래밍 AOP라고 한다.

 

AOP 적용 방식

  • 컴파일 시점
    • .java 소스 코드를 컴파일러를 사용해서. class를 만드는 시점에 부가 기능 로직 추가
    • AspectJ가 제공하는 특별한 컴파일러를 사용해야 한다.
    • 단점 : 특별한 컴파일러가 필요하며 복잡하다.
  • 클래스 로딩 시점
    • 자바를 실행하면 자바 언어는 .class 파일을 JVM 내부의 클래스 로더에 보관하는데, 이때 중간에. class 파일을 조작한 다음에 JVM에 올릴 수 있다. (로드 타임 위빙)
    • 단점 : 로드 타임 위빙은 자바를 실행할 때 특별한 옵션(java -javaagent)을 통해 클래스 로더 조작기를 지정해야 하기 때문에 번거롭고 운영하기 어렵다.
  • 런타임 시점 (프록시)
    • 런타임 시점은 컴파일 이후, 클래스 로더에 클래스도 다 올라가서 이미 자바가 실행된 다음을 말한다.
    • 프록시를 통해 스프링 빈에 부가 기능을 적용할 수 있다.
    • 프록시를 사용하기에 AOP 기능에 일부 제약이 있다. 하지만 컴파일 시점, 클래스 로딩 시점의 단점을 모두 극복하고, 스프링만 있으면 얼마든지 AOP를 적용할 수 있다.

 

용어 정리

  • 조인 포인트 (Join Point)
    • 어드바이스가 적용될 수 있는 위치 (AOP가 적용할 수 있는 모든 지점)
    • 스프링 AOP는 프록시 방식을 사용하므로 조인 포인트는 항상 메서드 실행 지점으로 제한된다.
  • 포인트컷 (Pointcut)
    • 조인 포인트 중에서 어드바이스가 적용될 위치를 선별하는 기능
    • 프록시를 사용하는 스프링 AOP는 메서드 실행 지점만 포인트컷으로 선별 가능하다.
  • 타켓 (Target)
    • 어드바이스를 받는 객체
    • 포인트컷을 통해 지정된다.
  • 어드바이스 (Advice)
    • 부가 기능
    • 특정 조인 포인트에서 Aspect에 의해 취해지는 조치
    • Around, Before, After와 같은 다양한 종류의 어드바이스가 있다.
  • 애스팩트 (Aspect)
    • 어드바이스 + 포인트컷을 모듈화 한 것
  • 어드바이저 (Advisor)
    • 하나의 어드바이스와 하나의 포인트 컷으로 구성
    • 스프링 AOP에서만 사용되는 특별한 언어
  • 위빙 (Weaving)
    • 포인트 컷으로 결정한 타깃으로 조인 포인트에 어드바이스를 적용하는 것
    • 위빙을 통해 핵심 기능 코드에 영향을 주지 않고 부가 기능을 추가할 수 있다.
  • AOP 프록시
    • AOP 기능을 구현하기 위해 만든 프록시 객체
    • 스프링에서 프록시는 JDK 동적 프록시 또는 CGLIB 프록시이다.

 

커스텀 Annotation 대상으로 AOP 적용하기

1. Custom Annotation 만들기

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface DoSomething {

}

 

2. Aspect 정의하기

public class PointCuts {

    @Pointcut("@annotation(com.min.advanced.aop.DoSomething)")
    public void doSomethingPointcut() {}
}
@Aspect
@Slf4j
@Component
public class ExecuteSomethingAspect {

    @Around("com.min.advanced.aop.PointCuts.doSomethingPointcut()")
    public void doSomething(ProceedingJoinPoint joinPoint) throws Throwable {

        log.info("[doSomethingAop] do something start");
        joinPoint.proceed();
        log.info("[doSomethingAop] do something end");
    }
}

 

@DoSomething 을 원하는 메서드에 붙여주면, AOP 적용 결과를 확인 할 수 있다.