본문 바로가기

Back-end/SpringBoot

[ SpringBoot] AOP

✅ AOP(Aspect Oriented Programming)란?

 

AOP는 관점 지향 프로그래밍이다.

 AOP는 자바와 같은 객체 지향 프로그래밍(OOP)을 더욱 OOP 답게 사용할 수 있도록 도와주는 역할을 한다.

 

AOP는 여러 개의 핵심 비즈니스 로직 외에 공통으로 처리되어야 하는 로그 출력, 보안 처리, 예외 처리와 같은 코드를 별도로 분리해서 하나의 단위로 묶는 모듈화의 개념으로 생각할 수 있다.

 

AOP에서 관점은 핵심적인 관점부가적인 관점으로 나눌 수 있다.

 

핵심적인 관점은 핵심 비즈니스 로직을 의미하고, 부가적인 관점은 앞에서 말씀드린 공통으로 처리되어야 하는 코드를 의미

 

아래는 이해를 돕기 위한 이미지!

 

 

 

각각의 화살표는 하나의 기능을 구현하는 데 필요한 작업을 의미한다. MVC 패턴의 특성상, 컨트롤러(Controller) ▶ 서비스(Service) ▶ 매퍼(Mapper) 순으로 작동한다.

 

만약, 필수적으로 처리되어야 하는 로그, 보안, 트랜잭션, 예외 처리와 같은 부가적인 기능들이, 규모가 커다란 시스템에서 각각의 기능마다 추가된다면 코드가 얼마나 길어지게 될까?

 

AOP는 이러한 문제를 관점이라는 개념을 통해 해결할 수 있다. 앞에서 말씀드린 부가적인 관점에서는 핵심 비즈니스 로직이 어떤 기능을 수행하는지에 대해 전혀 알 필요가 없다. 단지, 핵심 비즈니스 로직 안에서 필요한 시점에 부가적인 관점이 포함되기만 하면 되는 것이다.

 

 이미지를 하나 더 첨부하도록 하면!

 

 

객체 지향 프로그래밍과 달리, 부가적인 관점이 핵심 비즈니스 로직의 바깥에 포함되어 있다. 

 

즉 AOP를 적용하면 로그, 보안, 트랜잭션, 예외 처리와 같은 부가적인 기능들을 비즈니스 로직마다 일일이 추가하지 않아도 됩니다.

 

 

 

🔎  AOP가 동작하는 흐름

  1. 공통 관심사항을 Aspect로 만든다.
    • 로깅, 보안, 성능 모두 하나의 Aspect이다.
  2. PointCut을 정의한다.
    • Aspect를 어디에 적용할 것인지를 명시하는 로직을 정의

Java진영에서 AOP를 위한 프레임워크에는 SpringAOP와 AspectJ가 있다.
  

 

 

✅ AOP 용어 알아보기

AOP를 제대로 이해하기 위해서는 몇 가지 용어를 기억해야 한다.

 

컴파일 시점 용어

  • Advice : 실행 하려는 코드를 의미한다.
    • ex) logger.info("Before Aspect - Method is called - {}", joinPoint);
  • Pointcut : 인터셉터 하려는 메서드 호출에 대한 표현식
    • ex) execution(* org.example.service..(..))
  • Aspect : Advice와 Pointcut의 조합
    • Advice : 무엇을 할 것인가.
    • Pointcut : 언제 메서드 호출을 인터셉트 할 것인가.
  • Weaver : AOP를 구현한 프레임워크를 의미한다.
    • AspectJ나 Spring AOP도 위버이다.
      • 위의 프레임워크가 하는 AOP를 실행하는 과정을 통틀어 위빙이라 한다.

 

🖥️ 예제

package shyun.devtoon.common.aop;

import org.aspectj.lang.annotation.Pointcut;

public class PointcutConfig {

    /**
     * presentation 계층의 모든 메서드에 적용하는 포인트컷 경로.
     */
    @Pointcut("execution(* shyun.devtoon.*.presentation.*.*(..))")
    public void presentationPackageConfig() {
    }

}

 

 

package shyun.devtoon.common.aop;

import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

@Slf4j
@Aspect
@Configuration
public class LoggingAspect {

    @AfterReturning("shyun.devtoon.common.aop.PointcutConfig.presentationPackageConfig()")
    public void logMethodCall(final JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        String className = signature.getDeclaringType().getSimpleName();
        String methodName = signature.getName();

        HttpServletRequest request =
                ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();

        log.info("Request: {} {} ({}), {}.class -> {}()", request.getMethod(), request.getRequestURI(), request.getRemoteHost(),
                className, methodName);
    }

}

 

 

 

런타임 시점 용어

  • Join Point : Pointcut 조건이 참이면 Advice가 실행되는데, Advice가 실행되는 인스턴스를 JoinPoint 라고 한다. 메서드 실행 관련 필요한 여러 정보를 담고있다.

 

유용한 어노테이션

  • @Before : 메서드가 호출되기 전 로그를 출력하거나 인증이 필요할 때 유용하다.
  • @After : 메서드가 실행되고 난 뒤 수행할 작업을 지정한다.
    • 메서드가 성공하든 실패든 익셉션을 던지든 상관없이 실행
  • @AfterReturning : 오직 메서드가 성공했을 경우에서 실행, 익셉션 발생하면 실행하지 않음
  • @Around : 메서드 실행 전과 후에 동작한다.

 

 

✅ 스프링 AOP 동작방식

 

스프링에서는 사용할 Bean(객체)를 생성하고, 컨테이너에 등록하는 방식으로 동작한다.

 

스프링에서 AOP를 동작시키는 원리는 아래와 같다.

1. Bean으로 등록된 객체를 생성한 뒤 후처리기에 전달한다.

2. 빈 후처리기는 포인트컷을 확인하여 전달받은 빈이 프록시 적용 대상이지 확인한다.

3. 프록시 생성 대상 Bean이라면 프록시 객체로 대체하여 빈 후처리기가 컨테이너에 등록한다.

 

즉, Bean을 생성하여 컨테이너에 등록하는 시점에 AOP로 활용할 객체인지를 판단하여, 일단 객체 대신 프록시 객체로 대체해서 컨테이너에 등록하는게 핵심이다.

 

앞서 사용했던 @Async도 스프링 AOP처럼 @Async가 붙은 메서드의 클래스는 일단 객체(Bean)가 아니라 프록시 객체로 스프링 컨테이너에 등록된 것이다.

이를 통해서 @Async가 붙은 메서드에는 스레드풀에서 스레드를 할당받아서 별도로 비동기 방식으로 동작시킬 수 있었다.

 

 

✏️ 공부한점

 

AOP가 무엇이고 어떤식으로 동작하는지 간단히 알게되었다

 

즉 로깅, 보안, 트랜잭션 관리 등과 같은 공통적인 관심사를 모듈화 하여 코드 중복을 줄이고 유지 보수성을 향상하는데 도움을 준다.

 

그런데 공부하다보니 프록시라든지 빈 후처리기같은 용어가 무엇인지 공부가 더필요하다고 느꼈다.