[Spring] AOP의 기초 개념

Gogh·2023년 1월 2일
0

Spring

목록 보기
2/23

🎯 목표 : AOP의 기초 개념 이해

📒 AOP(Aspect-Oriented Programming)

관점 지향 프로그래밍을 뜻하며, 어떤 로직을 기준으로 핵심적인 관점과 부가적인 관점으로 나눠 관점을 기준으로 각각 모듈화한다. 

모듈화란, 공통된 로직이나 기능을 하나의 단위로 묶는 것을 말한다.

OOP의 모듈화의 핵심 단위는 클래스이고, AOP의 모듈화의 핵심 단위는 관점이라는 것에 차이가 있다.


📌 AOP의 핵심 기능과 부가 기능 그리고 필요성

  • 핵심 기능은 고유의 기능(비즈니스 로직 등)을 말한다.
  • 부가 기능은 핵심 기능을 보조 하기 위해 제공되는 기능이며, 각 기능을 추적하기 위한 로그 로직, 보안, 트랜잭션 기능등이 있다. 이러한 기능들을 횡단 관심이라 표현할수 있다.
  • 부가 기능은 단독으로 사용하지 않고 핵심 기능과 함께 사용된다.
  • 아래 코드 예제에서 A,B,C라는 핵심 기능 로직들에는 로그 추적 부가 기능이 필요하다 가정하고,
    A,B,C 로직을 구현할때, 세개의 핵심 기능에 모두 로그 추적 부가 기능로직을 구현하여 핵심 기능과 부가 기능이 함께 실행 된다.
class MyClassEx {
    void A(){
        System.out.println("Log Before"); //Log 추적
        System.out.println("A() Method Called.");  // 핵심 기능
        System.out.println("Log After"); //Log 추적
    }
    void B(){
        System.out.println("Log Before");  //Log 추적
        System.out.println("B() Method Called.");  // 핵심 기능
        System.out.println("Log After");  //Log 추적
    }
    void C(){
        System.out.println("Log Before");  //Log 추적
        System.out.println("C() Method Called.");  // 핵심 기능
        System.out.println("Log After");  //Log 추적
    }
}
  • 위 예제에서 여로 곳에서 공통적인 부가 기능을 사용하고자 한다면 중복 코드가 발생하는 것을 볼수 있다.
  • 부가 기능에 수정이 필요하게 되면 사용되는 클래스 또는 메소드에 일일이 찾아가면서 수정해야 하는 경우가 발생할수 있다.

👉 위 코드를 개선하기 위해서는 AOP가 필요하다

  • 변경 지점은 하나가 될 수 있도록 잘 모듈화 해야한다.
  • 부가 기능처럼 특정 로직을 프로그램 전반적으로 적용하는 문제는 객체 지향 프로그래밍의 방식으로 해결이 어렵기 때문에 핵심 기능과 부가 기능을 분리하는 AOP 방식이 필요한 것이다.
  • 아래 코드와 같이 위 예제를 핵심 기능과 부가 기능을 분리하여 AOP 방식으로 개선 할수 있다.
public class AopMain {
    public static void main(String[] args) throws Exception {
        MyClass myClass = new MyClass();
        MyAdvice myAdvice = new MyAdvice();

        for (Method m : myClass.getClass().getDeclaredMethods())
            myAdvice.invoke(m,new MyClass());
    }
}

//Advice
class MyAdvice { // 부가 기능이라 가정
    void invoke(Method m, Object obj) throws Exception{
        // Pointcut
        if(null != m.getAnnotation(Transactional.class)) {
            System.out.println("[Log Before] { ");
        }
        m.invoke(obj);
        if(null != m.getAnnotation(Transactional.class)) {
            System.out.println(" } [Log After]");
            System.out.println();
        }
    }
}


// Target
class MyClass { // 핵심 기능이라 가정
    @Transactional // Join Point
    void A(){
        System.out.println("A() Method Called.");
    }
    @Transactional
    void B(){
        System.out.println("B() Method Called.");
    }
    @Transactional
    void C(){
        System.out.println("C() Method Called.");
    }
    void D(){
        System.out.println("D() Method Called.");
    }
    void E(){
        System.out.println("E() Method Called.");
    }
}

// 출력값
[Log Before] {
A() Method Called.
 } [Log After]

[Log Before] {
B() Method Called.
 } [Log After]

[Log Before] {
C() Method Called.
 } [Log After]

D() Method Called.
E() Method Called.
  • 위 코드와 같이 개선하게 되면 코드는 복잡해지지만,
  • 부가 기능을 수정 하고자 하면 MyAdvice 클래스만 수정하면 되고, 각 클래스는 하나의 책임 하나의 역할만 가지게 되며, MyAdvice 클래스의 기능이 필요한 A,B,C 메소드 이외 에도 용이하게 적용할수 있게 된다.

📒 Spring AOP

b

Spring Framework에서 AOP 방식이 적용되는 흐름을 위 그림으로 보면 조금 더 이해하기 쉬울것 같다.

개념을 어떻게 적용하고 활용할수 있을까. 우선 기본 용어들을 먼저 정리 해보았다.


📌 AOP 기본 용어 정리

  • Aspect :
    • 여러 객체에 공통으로 적용되는 기능을 말한다(부가 기능). 어드바이스와 포인트컷을 모듈화 하여 프로그램에 포함되는 횡단 기능이다.
  • Join Point :
    • 프로그램 흐름에서의 기능들이 실행되는 특정 포인트를 의미한다. AOP를 적용 가능한 모든 지점이라 생각하면 되겠다.
  • Advice :
    • 조인 포인트에서 수행되는 코드를 의미한다, Aspect를 언제 핵심 기능에 적용할 지 정의하며 프로그램 전체에 Aspect API 호출을 제공한다.
  • Pointcut :
    • 조인 포인트 중 어드바이스가 적용될 위치를 선별하는 기능을 한다. AspectJ 표현식을 사용하여 적용될 위치를 지정한다. Spring AOP는 프록시를 사용하는데 메소드 실행 지점만 포인트컷으로 선별 가능하다.
  • Weaving :
    • 포인트컷으로 결정한 타겟의 조인 포인트에 어드바이스를 적용하는 것을 말한다.
  • AOP Proxy :
    • AOP 기능을 구현하기 위해 만든 프록시 객체다, 핵심 기능과 부가 기능이 합쳐진 형태의 객체라고 이해하면 되겠다.
      Spring AOP 프록시는 JDK 동적 프록시 또는 CGLIB 프록시 이다.
      JDK 동적 프록시 또는 CGLIB 프록시에 대해서는 따로 정리해서 블로깅 할 예정이다.
  • Target :
    • 핵심 기능을 담고 있는 모듈로 타켓은 부가 기능을 부여할 대상을 말한다. 포인트 컷으로 결정된다.
  • Advisor :
    • Spring AOP에서만 사용되는 용어로, 하나의 어드바이스와 하나의 포인트컷으로 구성된다.

📌 AOP 구현

  • Spring에서 어떻게 활용할 수 있는지 기초적인 내용으로 예제를 만들었다.
@SpringBootApplication
public class AopMain2 {
    public static void main(String[] args) {
        GenericApplicationContext context =
                new AnnotationConfigApplicationContext(config.class);
        MyClass2 myClass = context.getBean(MyClass2.class);
        myClass.aaa();
        myClass.bbb();
        myClass.ccc();
        myClass.ddd();
        myClass.eee();
    }
}
  • Target 클래스의 메소드들이 실행될 메인 메소드다 myClass의 각 메소드들이 하나의 핵심 기능으로 생각하면 되겠다.
//Target
public class MyClass2 {

    @MyPoint  // Pointcut
    void aaa(){
        System.out.println("aaa() Method Called.");
    }
    @MyPoint
    void bbb(){
        System.out.println("bbb() Method Called.");
    }
    void ccc(){
        System.out.println("ccc() Method Called.");
    }
    @MyPoint
    void ddd(){
        System.out.println("ddd() Method Called.");
    }
    void eee(){
        System.out.println("eee() Method Called.");
    }
}
  • Target 클래스 MyClass2 
  • 포인트 컷을 조금 간단하게 설정하기위해 어노테이션을 만들어서 지정하였다.
  • 포인트 컷으로 설정한 메소드가 실행될 때 Advice도 같이 실행될 것이다.
@Retention(RetentionPolicy.CLASS) // Retention(유지 범위) 기본값은 CLASS
@Documented // 자바 도큐먼트 작성
@Target(ElementType.METHOD) // 메소드 위에 어노테이션을 설정
public @interface MyPoint {  }
  • 포인트컷을 간단하게 설정하기 위한 어노테이션 MyPoint 이다.
@Configuration
@ComponentScan
public class config {
    @Bean
    public MyClass2 getMyClass(){
        return new MyClass2();
    }
}
  • MyClass2를 Bean등록 해준다.
@Aspect // Advice 설정
@Component // Bean 등록 해야한다
public class MyAdvice2 {

/*  @Around("execution(* com.codestates.chapter2.aop.MyClass2.aaa*(..))")
	위와 같이 execution 표현식으로 적용할 Joint Point를 설정할수 있다. */

    @Around("@annotation(MyPoint)") // 적용할 Joint Point 설정
    public Object invoke(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("[Advice Before] { ");

        Object result = pjp.proceed();

        System.out.println(" } [Advice After]");
        System.out.println();

        return result;
    }
}
  • Advice를 설정해준 클래스 MyAdvice2다. 
  • Aspect도 Bean등록을 해주어야 하며, Aspect의 적용 시점을 지정하는 다양한 어노테이션이 있지만 여기서는 다루지 않고, @Around 어노테이션만 활용했다.
  • @Arount 어노테이션은 포인트컷이 적용된 조인포인트의 실행 전, 후에 Aspect를 적용해준다.
출력값

[Advice Before] {
aaa() Method Called.
 } [Advice After]

[Advice Before] {
bbb() Method Called.
 } [Advice After]

[Advice Before] {
ddd() Method Called.
 } [Advice After]

ccc() Method Called.
eee() Method Called.

Advice의 세부 종류와 Pointcut의 표현식, JoinPoint 인터페이스에 대해서는 따로 정리를 해서 블로깅 예정이다.

profile
컴퓨터가 할일은 컴퓨터가

0개의 댓글