✏️ [Spring] AOP(Aspect Oriented Programming)란?

박상민·2024년 7월 6일

Spring

목록 보기
7/12
post-thumbnail

📌 AOP를 사용하지 않는다면?

AOP를 알기 전에 AOP를 사용하는 이유에 대해 먼저 알아보려고한다.

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

  • 핵심 기능: 해당 객체가 제공하는 고유의 기능
  • 부가 기능: 핵심 기능을 보조하기 위해 제공되는 기능(ex: 로그 추적 기능, 트랜잭션 기능)

보통 기존 프로젝트에 부가 기능을 추가하게 되면 하나의 클래스가 아닌 여러 클래스에 추가하게 된다.

예를 들어서 프로젝트의 모든 클래스에 로그 기능을 추가한다면 하나의 부가 기능(로그 추적)을 여러 곳에서 동일하게 사용하게 된다.

이러한 부가 기능을 바로 횡단 관심사(cross-cutting concerns)라고 한다.

부가 기능 적용시 문제점
그런데 이러한 기존 프로젝트에 부가 기능을 추가하게 된다면 여러 문제점이 있다.
만약 부가 기능을 적용해야할 클래스가 100개라면 100개의 클래스에 각각 부가 기능 코드를 추가 해야하고, 거기에 더해 단순 호출이 아닌 try ~ catch ~ finally 구문이 필요하다면 더욱 복잡해진다.
모든 클래스에 부가 기능을 추가했는데, 여기서 만약 수정이 필요하다면?
또 모든 클래스를 수정해야 하는 지루한 반복 작업을 해야할 것이다.

⭐️ AOP(Aspect Oriented Programming)란?

부가 기능을 적용할 때 위와 같은 문제점들로 인해 수 많은 개발자들이 오랜 시간 고민해왔다.

그 고민 끝에 나타난 용어가 AOP이다.

AOP

  • 부가 기능을 핵심 기능에서 분리해 한 곳으로 관리하도록 하고, 이 부가 기능을 어디에 적용할 지 선택하는 기능을 합한 하나의 모듈
  • AOP의 A에 해당하는 Aspect는 우리말로 번약하면 "관점"이라는 뜻이다.
    - 즉, 이름 그대로 애플리케이션을 바라보는 관점을 하나하나의 기능에서 횡당 관심사(cross-cutting concerns) 관점으로 달리 보는 것이다.

이러한 프로그래밍 방식을 관점 지향 프로그래밍(AOP)라고 한다.

📌 AOP 용어 정리

AOP 프록시

  • AOP 기능을 구현하기 위해 만든 프록시 객체, 스프링에서 AOP 프록시는 JDK 동적 프록시 또는 CGLIB 프록시이다.

어드바이스(Advice)

  • 부가 기능
  • 특정 조인 포인트에서 Aspect에 의해 취해지는 조치
  • Around(주변), Before(전), After(후)와 같은 다양한 종류의 어드바이스가 있다.

조인 포인트(Join Point)

  • 어드바이스가 적용될 수 있는 위치로, AOP를 적용할 수 있는 모든 지점
  • 스프링 AOP는 프록시 방식을 사용하므로 조인 포인트는 항상 메소드 실행 지점으로 제한된다.

포인트컷(Pointcut)

  • 조인 포인트 중에서 어드바이스(부가 기능)을 어디에 적용할 지/적용하지 않을 지 위치를 판단하는 필터링 기능
    - 주로 AspectJ 표현식을 사용해서 지정
  • 프록시를 사용하는 스프링 AOP는 메서드 실행 지점을 포인트 컷으로 필터링 한다.

타겟(Target)

  • 어드바이스를 받는 객체, 포인트컷으로 결정

Aspect

  • 어드바이스 + 포인트컷을 모듈화 한 것
  • 하나의 어드바이스만이 아닌 여러 어드바이스와 포인트 컷이 함께 존재할 수 있다.

어드바이저(Advisor)

  • 하나의 어드바이스와 하나의 포인트 컷으로 구성
  • 즉, 어드바이스 + 포인트 컷 = 어드바이저

📌 AOP 적용 방식

AOP의 적용 방식은 크게 3가지가 있다.

  • 컴파일 시점
  • 클래스 로딩 시점
  • 런타임 시점(프록시 사용)

컴파일 시점과 클래스 로딩 시점 적용 방식은 AspectJ 프레임워크를 직접 사용해야 하고, 이 AspectJ를 학습하기 위해선 많은 분량과 설정의 번거로움이 있다.

따라서, 주로 런타임 시점 적용 방식을 사용하는 Spring AOP를 사용한다.

  • 스프링은 AspectJ의 문법을 사용해 프록시 방식의 AOP를 적용, AspectJ를 직접 사용하지 않는다.


런타임 시점은 컴파일도 다 끝나고, 클래스 로더에 클래스도 다 올라가서 이미 자바가 실행되고 난 다음을 말한다. (Main 메서드가 실행된 다음)

프록시를 사용하기 때문에 AOP 기능에 일부 제약이 있지만, 특별한 컴파일러나 자바를 실행할 때 복잡한 옵션과 클래스 로더 조작기를 설정하지 않아도 스프링이 알아서 자동으로 설정 해주기 때문에 훨씬 편리하다.

AOP 적용 가능 위치
주로 사용하는 런타임 시점 적용 방식의 스프링 AOP 관점에서만 설명하자면

  • 스프링 AOP는 메서드 실행 지점에만 AOP를 적용할 수 있다.
  • 프록시 방식을 사용하는 스프링 AOP는 스프링 컨테이너에 해당 @Aspect을 빈 등록을 해야 AOP를 적용할 수 있다.

Spring AOP 적용하기 (@Aspect)

스프링 AOP를 적용하기 위해서는 아래의 라이브러리를 build.gradle에 의존성을 추가해야 한다.

implementation 'org.springframework.boot:spring-boot-starter-aop' // 스프링 aop 추가

라이브러리를 추가하면 AspectJ 관련 라이브러리를 등록하고, Spring Boot가 AOP 관련 클래스를 자동으로 스프링 빈에 등록해준다.

이러한 스프링 부트의 자동 설정은 AnnotationAwareAspectJAutoProxyCreator라는 빈 후처리기가 스프링 빈에 자동으로 등록해주는데, 이름 그대로 자동으로 프록시를 생성해주는 빈 후처리기이다.

AnnotationAwareAspectJAutoProxyCreator의 역할
1. @Aspect 어노테이션이 붙은 클래스를 Advisor(어드바이저)로 변환하여 저장
2. Advisor(어드바이저)를 자동으로 찾아와 프록시를 생성하고 Pointcut(프록시 적용 대상 필터링)을 보고 프록시가 필요한 곳에 Advice(부가 기능)을 적용한다.

스프링 AOP 적용
스프링 애플리케이션에 프록시를 적용하려면 포인트컷과 어드바이스로 구성되어 있는 어드바이저(Advisor)를 만들어서 스프링 빈으로 등록하면 나머지는 AnnotationAwareAspectJAutoProxyCreator 자동 프록시 생성기가 모두 자동으로 처리해준다.

자동 프록시 생성기는 스프링 빈으로 등록된 어드바이저들을 찾고, 스프링 빈들에 자동으로 포인트컷이 매칭되는 경우에 프록시를 적용해준다.

스프링은 @Aspect 애노테이션으로 매우 편리하게 포인트컷과 어드바이스로 구성되어 있는 어드바이저 생성 기능을 지원하며 어드바이저로 사용할 클래스에 @Aspect 어노테이션을 붙여줌으로써 스프링 AOP를 적용할 수 있다.

@Aspect // 해당 클래스를 Advisor로 변환하여 저장
public class LogTraceAspect {
 
    @Around("execution(* hello.proxy.app..*(..))")  // 포인트컷 (AspectJ 표현식)
    public Object execute(ProceedingJoinPoint joinPoint) { // 어드바이스
        // 어드바이스 로직
    }
}

주의

  • Spring AOP 적용 시에는 private, final 메서드는 AOP 적용이 불가능하다.

ProceedingJoinPoint
@Around 어드바이스를 사용할 경우 메서드의 파라미터로 ProceedingJoinPoint를 꼭 넣어줘야 한다.

ProceedingJoinPoint의 proceed()는 다음 어드바이스나 타켓을 호출하는 것으로, 어드바이스를 사용하기 위해서는 꼭 proceed() 메서드를 호출해줘야 한다.

이 외에도 호출되는 대상 객체에 대한 정보, 실행되는 메서드에 대한 정보 등이 필요할 때가 있는데 이 경우에는 ProceedingJoinPoint interface가 제공하는 아래의 메서드를 사용할 수 있다. (일부만 설명)

메서드설명
Signature getSignature()호출되는 메서드에 대한 정보를 반환
Object getTarget()대상 객체를 반환
String getName메서드의 이름을 반환
String toLongString()메서드를 완전하게 표현환 문장을 반환(메서드의 리턴 타입, 파라미터 타입 모두 표시)

이렇게 ProceedingJoinPoint로 호출되는 객체에 대한 정보나, 실행되는 메서드의 정보를 알 수 있는 이유
스프링 부트 자동 설정으로 AnnotationAwareAspectJAutoProxyCreator이라는 자동 프록시 생성기가 빈 등록되어 있는데, 이 자동 프록시 생성기가 @Aspect가 붙은 클랴스를 보고 Advisor(어드바이저)로 변환해 저장해준다.
그리고 이 Advisor(어드바이저)를 보고 포인트컷의 대상이 되는 것들은 ProxyFactory에 인자로 넘겨 자동으로 프록시를 생성하고 적용해준다.

여기서 생성된 프록시 객체가 메서드를 호출할 때, ProceedingJoinPoint 객체를 생성하고 이를 advice에 전달한다.

즉, ProceedingJoinPoint는 프록시가 메서드를 호출하는 시점의 정보를 가져 어드바이스가 적용되는 대상을 이미 알고 있다.


출처
https://hstory0208.tistory.com/243
https://hstory0208.tistory.com/244

0개의 댓글