Published 2021. 9. 24. 22:26
반응형

✔️AOP(Aspect Oriented Programming)

  • 관점 지향 프로그래밍
  • 원하는 관점을 기준으로 프로그래밍을 하는 방식입니다.
    음... 오히려 번역하고 설명하려면 더욱 이해하기 어려운 용어 같아서
    간단하게 설명하자면, 중복된 기능을 클라이언트의 코드 변경없이 재사용하도록 하는 기법
    이라고 보는게 좋을 것 같습니다.

🔍 순수 자바로 보는 AOP

순수 자바로 AOP를 구현 해보겠습니다.
AOP에는 여러가지 방법이 있지만, 그 중 스프링에서 사용되는 Proxy 패턴에 대하여,
알아보도록 하겠습니다.
Proxy는 아주 얕은 지식으로 이해한 바는 대부분 가짜라는 의미를 가지고 있습니다.
즉, 실제 객체를 사용하는 것이 아닌 관련된 다른 가짜를 만들어서 적용하는 패턴을
의미합니다.
코드로 살펴보겠습니다.

Payment

public interface Payment {

    void pay(int amount);
}

Cash

public class Cash implements Payment {

    // target
    @Override
    public void pay(int amount) {
        System.out.println(amount + " 현금 결제");
    }
}

CashPerf

package com.example.demo.proxy;

import org.springframework.util.StopWatch;

public class CashPerf implements Payment {

    Payment cash = new Cash();

    // Aspect
    @Override
    public void pay(int amount) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();

        cash.pay(amount);

        stopWatch.stop();
        System.out.println(stopWatch.prettyPrint());
    }
}

Store

public class Store {

    private Payment payment;

    public Store(Payment payment) {
        this.payment = payment;
    }

    public void buySomething(int amount) {
        payment.pay(amount);
    }
}

여기서 Proxy객체는 CashPerf입니다.
CashPerf는 Cash를 생성하여 Cash의 기능을 사용하기 전 후로
성능 측정을 하는 부가 기능을 가지고 있습니다.
실제 사용하는 코드를 아래 보면, Cash를 사용하지 않고,
CashPerf를 Store에 주입하였습니다.
이렇게 사용하게 되면 Cash를 직접 사용하지 않기 때문에,
Cash가 변경 되어도 성능 측정 기능은 변함이 없습니다.
어떤 클라이언트 코드도 변경이 필요없는 것이죠.
이런 패턴은 Proxy 패턴이라고 하며, 실제 로직이 있는 객체(Cash)
사용하지 않고, Cash를 감싼 CashPerf를 사용하는 패턴으로 사용되고 있습니다.
이런 CashPerf와 같은 프록시를 스프링에서는 자동으로 Store에게 주입을 해주게 됩니다.

class StoreTest {

    @Test
    void testPay() {
        Payment cashPerf = new CashPerf();
        Store store = new Store(cashPerf);
        store.buySomething(100);
    }
}

🔍 스프링에서 AOP

📌 Aop

  • 공통적인 부가 기능을 모듈화 하여 재사용하는 기법!!
  • Aop에는 Advice, JoinPoint, Pointcut, Target이 있습니다.

📌 Advice

  • 어떤 부가 기능을 언제 사용할지에 대한 정의입니다.
    언제 사용할지에 대한 시점은 총 5가지가 있습니다.
  1. Before : target 실행 전
  2. AfterReturning : target 이상없이 실행 후
  3. AfterThrowing : target 실행 중 예외 발생 시
  4. After : AfterReturning or AfterThrowing
  5. Around : 위의 모든 상황

📌 JoinPoint

  • Advice가 적용될 수 있는 위치(스프링에서는 메서드 호출 시에만 가능합니다.
  1. 메서드 호출 시
  2. 변수에 접근 시
  3. 객체 초기화 시
  4. 객체에 접근 시

📌 Pointcut

  • Advice를 적용할 JoinPoint를 선별하는 과정
    즉, 스프링은 메서드 호출 시에만 가능하므로, 적용할 메서드를 선별하는 과정입니다.

📌 Target

  • Advice가 적용될 대상으로, 해당 메서드를 의미합니다.

소스 예제

직접 스프링 코드를 보겠습니다.
Controller
get1메서드는 Target이 되며, Pointcut에서 Tartget인 get1만 지정하게 됩니다.

@RestController
public class Controller {

    // Target
    @GetMapping("/1")
    @LogExecutionTime
    public String get1() {
        return "get1";
    }

    @GetMapping("/2")
    public String get2() {
        return "get2";
    }
}

@LogExecutionTime

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

LogAspect
Advice를 지정하는 곳입니다.
시간 측정하는 부가 기능을 Around 시점에 적용하도록 되어 있습니다.
즉, 순수 자바에서 설명한 CashPerf와 같은 곳이라고 보시면 됩니다.

@Component
@Aspect
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();
        System.out.println(stopWatch.prettyPrint());

        return proceed;
    }
}

📌 aop로 많이 사용되는 기능

  • 성능 검사, 트랜잭션 처리, 로깅 등...
반응형
복사했습니다!