본문 바로가기
IT 개인 공부/Spring

[Spring] AOP란?

by Libi 2021. 9. 9.
반응형

스프링 프레임워크에는 스프링 트라이앵글이라는 것이 존재한다.

 

이는 스프링 프레임워크의 핵심 요소를 나타내는 것인데 이 중 하나인 AOP에 대해서 알아보도록 하자.

 

AOP는 Aspect-Oriented Programming의 약자로써 그대로 번역하면 관점-지향 프로그래밍을 의미한다.

그렇다면 관점이라는 것이 무엇을 의미하는 것일까? 이는 말보다 그림으로 이해하는 것이 편하다.

 

A, B, C의 클래스가 존재한다고 해보자. 각 클래스는 특정 로직을 수행하는 메서드를 하나씩 가진다.

 

그런데 성능 측정을 위해서 메서드가 수행되는 시간을 측정해야 하는 요구사항이 들어왔다고 생각해보자.

가장 단순한 방법은 A, B, C 클래스에 시간 측정 로직을 모두 넣는 방법일 것이다.

 

근데 이 방법은 상당히 비효율적이다. 매번 메서드가 추가될 때마다 해당 로직을 추가해야 하기 때문에 중복 코드가 많아진다.

또한, 시간 측정 로직을 수정하거나 제거할 경우 해당 로직이 들어간 모든 메서드를 찾아서 일일이 변경해줘야 하는 번거로움이 존재한다.

이를 해결해주는 방법이 바로 AOP이다.

 

AOP는 관점 지향 프로그래밍이라고 하였다.

간단히 말하면 특정 로직에서 핵심적인 관점과 부가적인 관점으로 나누고 그 관점을 기준으로 공통 관심사를 분리 및 모듈화하여 프로그래밍하겠다는 의미이다.

 

위의 상황에서 핵심적인 기능은 method A, B, C일 것이며 부가적인 기능은 시간을 측정하는 로직일 것이다.

즉, 부가적인 기능을 공통 관심사로 모듈화하여 보다 효율적으로 프로그래밍할 수 있도록 해주는 것이다. AOP는 이처럼 공통 관심사를 잘라냈다고 하여 크로스 컷팅(Cross-Cutting)이라고도 부른다.

Spring에서는 로깅, 보안, 트랜잭션 등에 AOP를 적용하고 있다.

 

AOP에서 사용되는 주요 용어는 다음과 같다.

  • Target : 부가기능(Aspect)을 적용할 대상,  
  • Aspect : 시간을 측정하는 기능처럼 흩어진 관심사를 분리하여 모듈화 한 것, Aspect는 부가될 기능을 정의한 Advice와 Advice를 어디에 적용할지를 결정하는 PointCut을 함께 가지고 있음
  • Advice : 실질적으로 Aspect가 '무엇'을 '언제' 수행할지를 정의
  • JoinPoint : Advice가 적용될 위치, 적용할 위치는 굉장히 다양함
  • PointCut : Advice가 적용될 위치를 보다 구체적으로 정의한 것, JoinPoint는 Advice를 적용할 수 있는 위치의 모음이라면 PointCut은 그중 실제로 적용할 위치를 의미

 

스프링에서는 AspectJ나 스프링 AOP를 사용하여 프록시 객체를 생성하여 AOP를 제공해준다.

이때 Spring은 인터페이스는 JDK Dynamic Proxy를 통해 프록시 객체를 생성하고, 클래스는 CGLIB를 통해 프록시 객체를 생성해준다.

성능상 CGLIB가 이점이 많기 때문에 SpringBoot는 기본적으로 CGLIB를 통해 프록시 객체를 제공해준다.

또한, 이처럼 프록시 객체가 생성되는 과정을 Weaving이라고 한다.

 

스프링 AOP를 적용하는 방법은 어떤 시점에 AOP를 적용하는지에 따라 3가지가 존재한다.

  • 컴파일 시점 : .java 파일이 Java 컴파일러에 의해 .class 파일로 컴파일될 때 AOP를 적용, AspectJ
  • 로딩 시점 : .class 파일이 Class Loader에 의해 메모리에 로딩될 때 AOP를 적용, AspectJ
  • 런타임 시점 : 프로그램이 실행될 때 AOP를 적용, 스프링 AOP

 

그렇다면 간단하게 시간 측정 AOP를 구현해보도록 하자.

먼저 시간을 측정하는 로직을 담은 Aspect를 구현하자.

@Slf4j
@Aspect
@Component
public class LogAspect {

    @Around("@annotation(LogExecutionTime)")
    public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();

        Object proceed = joinPoint.proceed();

        stopWatch.stop();
        log.info(stopWatch.prettyPrint());

        return proceed;
    }
}

 

@Aspect을 통해 Aspect라는 것을 알려주고 @Component을 통해 빈으로 등록해준다.

@Around는 Advice를 의미한다.

@Before, @After 등 다양한 Advice가 존재하며 @Around는 타겟 메서드를 감싸서 타겟 메서드 호출 전/후에 Advice 기능을 수행한다.

@annotation(LogExecutionTime)은 PointCut을 의미하며 annotation은 지정자, LogExecutionTime은 타겟 명세라고 한다.

즉, LogExecutionTime 어노테이션이 붙은 애들한테 시간 측정 AOP를 적용하겠다는 의미이다.

joinPoint는 AOP로 감싼 실제 메서드를 생각하면 된다.

 

다음으로 LogExecutionTime 어노테이션을 구현해준다.

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

 

@Target은 Target을 의미하며 메서드에만 적용하겠다고 설정하였고, Retention은 RUNTIME 시에만 적용하겠다는 의미이다.

 

그렇다면 실제 메서드에 AOP를 적용시켜보자.

@LogExecutionTime
@ResponseBody
@GetMapping("/aopTest")
    public String aopTest() {
    log.info("aopTest");
    return "ok";
}

 

결과를 보면 aopTest() 메서드에는 시간 측정하는 로직이 없지만 AOP를 통해 시간이 측정되는 것을 확인할 수 있다. 

 

 

 

[ Reference ]

· https://jojoldu.tistory.com/71?category=635883

· https://gmoon92.github.io/spring/aop/2019/04/20/jdk-dynamic-proxy-and-cglib.html

· https://www.inflearn.com/course/spring_revised_edition/dashboard

반응형

댓글