백앤드(스프링)

AOP 1편 [@Aspect, AOP 1편 정리]

유승혁 2022. 10. 30. 12:51

 이젠 Advisor의 구현체를 만들고 빈으로 등록하면 스프링 자동 프록시 생성기가 어드바이저를 찾아 포인트 컷이 맞으면 프록시를 만들어 주었다. 이때 @Aspect 어노테이션으로 매우 편리하게 어드바이저를 만들 수 있다.

 

1. @Aspect

 @Aspect를 사용해도 중요한 것은 포인트컷과 어드바이스를 구성해야 한다. 해당 어노테이션은 이를 편리하게 도와주는 도구이다. 코드로서 보자!

@Slf4j
@Aspect
public class LogTraceAspect {

    private final LogTrace logTrace;

    public LogTraceAspect(LogTrace logTrace) {
        this.logTrace = logTrace;
    }

    @Around("execution(* hello.proxy.app..*(..))")//포인트 컷
    public Object execute(ProceedingJoinPoint joinPoint)throws Throwable{
        //내부 로직이 어드바이스
        TraceStatus status = null;
        try {
            String message = joinPoint.getSignature().toShortString();
            status = logTrace.begin(message);

            //로직 호출
            Object result = joinPoint.proceed();

            logTrace.end(status);
            return result;
        }catch (Exception e){
            logTrace.exception(status, e);
            throw e;
        }
    }//어드바이저 탄생
}

  위 처럼 @Aspect를 클래스 레벨에 선언한다. 그리고 @Around가 바로 포인트 컷이 되고 내부는 어드바이스를 구현하던 코드와 비슷하다. 중요한 것은 joinPoint.proceed()를 통해 target의 메서드를 호출을 해야한다. 안그러면 프록시에서 끝난다.

 추가로 @Around 말고 다양한 어노테이션이 있다. 좀 이따 다루어 보자. 어노테이션 이름 그대로 핵심 기능의 주변부에 들어갈 코드를 작성하는 느낌이다.

 

1.1 동작 과정

 자동 프록시 생성기(AnnotationAwareAspectJAutoProxyCreator)는 Advisor 구현체를 확인해서 빈 후처리기 역할을 한다고 했다. 여기에 추가로 @Aspect 어노테이션이 붙은 클래스에 대해서도 Advisor로 만들어 낸다. 아래 사진을 보자.

 스프링이 실행 되면서 자동 프록시 생성기가 Advisor 또는 @Aspect를 확인해서 어드바이저를 생성해서 어드바이저 빌더 안에 넣어둔다. 이후 스프링 빈이 생성될 때마다 빈 객체를 만들고 빈 후처리기(자동 프록시 생성기)에 넘긴다. 이후 Advisor 빈을 전부 조회한 다음 포인트 컷에 하나라도 만족을 한다면 프록시를 생성하고 프록시를 스프링 컨테이너에 등록한다.

 

2. AOP에 대하여

 지금까지 AOP를 완벽히 이해하기 위한 걸음들을 진행했다. 어렵다는 말이 많았지만 막상 공부하다 보니 잘 이해가 되었다. 이제 개념을 배웠으니 다양한 적용법, 제약사항 등(AOP 2편)을 알아보자. 그 전에 AOP를 좀 더 명확하게 정리하자.

 AOP는 위 그림 처럼 여러 로직에서 공통으로 호출할 기능들. 즉 횡단 관심사 문제를 해결하기 위해 등장했다. 횡단 관심사를 다른말로 부가 기능이라 불렀고 각 고유 로직을 핵심 기능이라 불렀다. 부가 기능은 핵심기능 없이 존재 할 수 없으며 이전 글에서는 데코레이팅이라는 표현도 사용했다. 부가기능의 특징은 아래와 같다.

1. 적용 시 많은 반복이 필요.
2. 중복 코드를 생성해냄.
3. 수정 시 많은 부분에 변화가 일어남.(SRP위반)
4. 적용 대상 변경 시 많은 수정이 필요.

 이러한 문제를 해결하기 위해 등장한 것이 AspectJ 프레임 워크이다. 대부분 AOP를 사용한다고 하면 대게 AspectJ프레임 워크를 사용한다. 예시로 @Aspect가 있다. 사용하기 너무 편리하잖아?

 

2.1 AOP 적용 방식

 크게 3가지 방법이 있다. 엥 우리가 한게 다가 아냐? 우리가 한 방식을 런타임 시점(프록시)라고 부른다. 다른 방식에 대해 알아보자.

 - 컴파일 시점: AspectJ 컴파일러가 .class 파일을 .java 파일로 만드는 시점에 부가 기능 코드를 말그대로 붙여 버린다. 마치 @Getter 같은 롬복 어노테이션이 컴파일 시점에 사라지면서 getXxx 방식으로 바뀌듯이 말이다. 하지만 이를 적용하려면 특별한 컴파일러가 필요하고 사용법도 복잡하다.

 - 로드타임: AspectJ 클래스 로더 조작기가 .class 파일을 jvm에 올리기 직전에 코드를 덧붙인다. 이 방식도 마찬가지로 클래스 로더 조작기를 이용해야 하는데 쉽지 않다.

 - 런타임 시점: 우리가 배운 그대로 이미 자바는 빌드가 다 완료 되고 스프링이 실행되는 시점에 프록시를 생성한다. 자바의 main이 실행된 이후에 동작한다는 것. 스프링과 같은 컨테이너의 도움을 받고 프록시와 DI, 빈 포스트 프로세서 같은 개념들을 총 동원해야 한다. 이렇게 하면 최종적으로 프록시를 통해 스프링 빈에 부가 기능을 적용할 수 있다. 지금까지 우리가 학습한 것이 바로 프록시 방식의 AOP이다.

 

2.2 AOP 용어 정리

 - 위빙: 위 3 방식 처럼 코드를 덧붙이는 방식 자체를 위빙이라 한다.핵심 기능을 수정하지 않고 부가기능을 덧붙이는 걸 말한다.

 - 조인 포인트: 어드바이스가 적용 될 수 있는 위치이다. 생성자, 필드 값 접근, 메서드 접근 등 다양한 위치가 존재할 수 있다. 스프링 AOP는 런타임 시점에 적용되기 때문에 조인 포인트는 메서드 접근으로 제한되어 있다.

 - 포인트 컷: 조인 포인트 중에서 어떤 조인 포인트에 적용할지 판단하는 if문이다.

- 타켓: 어드바이스를 받는 객체, 부가 기능을 부여할 대상

 - 어드바이스: 부가기능

 - Aspect: 어드바이스와 포인트컷의 묶음. 하나의 애스펙트에 여러 어드바이스와 포인트컷 존재 가능

 - 어드바이저: 하나의 어드바이스와 포인트컷의 묶음.

- AOP 프록시: 스프링에서 사용하는 동적 프록시. JDK 동적 프록시와 CGLIB 프록시가 존재한다.

 

마무리

 이렇게 AOP 1편이 끝났다. AOP가 만들어지기의 발전 과정과 동작 방식을 자세히 다루었다. 개념 정리를 잘 다루었으니! AOP 2편에서는 어떻게 사용하는지, 어떠한 예시가 있는지, 스프링 프록시에 대한 제약사항들과 이를 극복해낸 과정에 대해 다루어보자.