JAVA-SOLID

[OOP] 스프링 삼각형(AOP)

EJUN 2023. 6. 21. 19:03

AOP(Aspects - Oriented Programming)
핵심관심사와 횡단관심사를 분리하여 모듈화하여 재사용할 수 있도록 지원하는 것
관점 지향 프로그래밍이라고도 함
스프링 AOP는 인터페이스 기반으로 함.
class Teacher{
    public void sleep(){};
    public void eat(){};
    public void teach(){};
}

class Student{
    public void sleep(){};
    public void eat(){};
    public void learn(){};
}

위의 코드를 보게 되면

먹고 자는 행동은 학생과 선생님 둘 다 공통적으로 수행하는 부분이기 때문에  횡단관심사라고 하고

선생님의 역할을 가르치는 것이고 학생의 역할은 배우는 것으로 서로 핵심적인 역할이기 때문에 이를 핵심관심사라고 함.

 

핵심관심사와 횡단관심사 분리

AOP를 쉽게 말하면 공통적인 부분을 재사용하는 기법이라고 할 수 있다.

스프링 AOP는 런타임에 적용되는 방식만을 추구하고 있다.

또한 AOP는 Proxy Pattern을 이용하는 방식이다.

Proxy Pattern에 대해서는 아래의 글을 참고바란다.

2023.06.08 - [JAVA-SOLID] - [OOP] 디자인 패턴(Adapter, Proxy, Decorator Pattern)

AOP 용어

AOP에는 많은 용어가 있다. 대표적으로 Joinpoint, Pointcut, Aspect, Advice등이 있다.

Joinpoint
  • Aspect 적용이 가능한 모든 지점을 의미한다.
  • 스프링 AOP는 인터페이스 기반이고, 인터페이스는 추상메소드의 집합체이기 때문에 메소드에만 적용이 가능함.   즉 Joinpoint 또한 메소드 실행시점만 가능하다.
Pointcut
  • 공통적인 로직을 담당하는 횡단관심사를 적용할 타킷 메소드를 선택하는 지시자
  •  Pointcut 또한 메소드 실행시점만 가능하다.

Advice
  • Pointcut에 언제(when)라는 개념까지 포함한다.
    즉, Pointcut에 언제, 무엇을 적용할지 정의한 것이다.
  • @Around, @Before, @After, @AfterReturning, @AfterThrowing 총 5가지의 종류가 있다.  각각의 어노테이션은    AOP가 적용되는 시점을 의미한다.
<@Before>
Joinpoint 이전에 실행을 의미

<@AfterReturning>
Joinpoint 정상완료 후 실행

<@AfterThrowing>
만약 메소드가 예외를 던지는 경우에 실행

<@After>
Joinpoint의 정상, 예외 동작과는 무관하게 실

<@Around>
앞에 말한 4가지의 어노테이션을 모두 포함

 

@Aspect

해당 어노테이션을 사용하면 해당 클래스를 이제 AOP에서 사용하겠다는 의미이다.

Aspect = Advice + Pointcut을 의미한다.

 

AOP 예제
@Aspect
@Component
public class TimeTraceAop {
    @Around("@annotation(auhh)")
    public Object execute(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        System.out.println("START: " + joinPoint.toString());
        try {
            return joinPoint.proceed();
        } finally {
            long finish = System.currentTimeMillis();
            long timeMs = finish - start;
            System.out.println("END: " + joinPoint.toString() + " " + timeMs + "ms");
        }
    }
}

위의 코드를 보면 @Component어노테이션을 이용하여 TimeTraceAop클래스를 빈으로 등록하고

@Aspect을 이용하여 AOP로 등록을 하였다.

 

AOP에서 타깃메소드를 정하는 방식은 대표적으로 2가지가 있는데 첫번째로는 

execution(*)을 이용하여 Aspect를 실행할 수 있고 위 코드에서 사용한 어노테이션을 이용하는 방법도 있다.

 

우선 어노테이션을 이용한 Aspect실행하는 방법은 사용자가 직접 어노테이션을 구현해서 사용해야 한다.

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

위의 코드가 사용자가 지정한 어노테이션인데 @Target은 어떤 타입에 Aspect를 적용할 지 나타내는 것이고,

@Retention은 언제 적용할 지를 나타내는 어노테이션이다.

@Component
public class HelloController {
    @auhh
    public String Hello(Model model) {
        model.addAttribute("data", "hello!!");
        return "hello";
    }

즉 auhh가 붙은 어노테이션은 메소드타입에 적용하고 런타임시에 적용한다는 의미이다.

위처럼 빈으로 등록된 클래스에 @auhh가 붙어있기 때문에 Hello()메소드는 AOP를 적용되었다.

 

@Aspect
@Component
public class TimeTraceAop {
    @Around("execution(* Hello.hello_spring..*(..))")
    public Object execute(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        System.out.println("START: " + joinPoint.toString());
        try {
            return joinPoint.proceed();
        } finally {
            long finish = System.currentTimeMillis();
            long timeMs = finish - start;
            System.out.println("END: " + joinPoint.toString() + " " + timeMs + "ms");
        }
    }
}

※  execution([접근제어자], 리턴타입 [패키지] 메서드이름(파라미터))

위의 코드는 execution(*)키워드를 이용하여 Aspect를 이용한다는 의미인데

Hello.hello_spring패키지 하위의 모든 클래스에게 적용을 한다는 의미이다.