[Spring Starts Here] - Chapter 6 : Using aspects with Spring AOP (1/3)

청주는사과아님·2024년 8월 29일

Spring Starts Here

목록 보기
5/7
post-thumbnail

지금까지 우리가 알아본 스프링의 기능은 DI (Dependency Injection) 이었다.

스프링 context 에 우리가 원하는 개체를 넣어두고, 이들의 의존 관계를 @Autowired 를 이용해 스프링이 맺어주었다.

이번 챕터에서는 스프링의 또다른 핵심 기능인 관점 (Aspect) 에 대해서 알아본다.


Spring Aspect?

Aspect 는 스프링이 메서드 호출, 실행을 가로챌 수 있는 방법이다.

Aspect 로 메서드 호출 시점에 개입하여 특정 조건의 로그 남기기 같은 기능을 추가할 수 있고, 심지어 실행되는 메서드를 다른것으로 대체할 수도 있다.

때문에 aspect 는 실행되는 메서드의 특정 로직을 추출할 수 있고, 이로 인해 비즈니스 로직을 훨씬 간결하게 할 수 있다.

Aspect 는 많은 곳에 사용될 수 있지만, 그 중 가장 빈번하고 중요히 사용되는 부분은 트랜잭션성 (Transactionality) 과 보안 (Security) 일 것이다.

앱에서 DB 등의 퍼시스턴스 계층 (Persistence Layer) 이 존재하고 이들의 입출력 작업을 수행할 때, 우리는 항상 데이터의 트랜잭션성 을 보수해야 한다.

쉽게 말해 친구에게 500 원을 송금하다 인터넷이 끊어졌을 때, 그 500 원은 다시 나에게 돌아와야지 사라지면 안된다는 것이다.

스프링에서는 @Transactional 어노테이션을 사용해 메서드의 트랜잭션성을 보장할 수 있고, 이 기능의 근본적 원리는 aspect 로 메서드를 가로채는 원리이다.

보안의 경우 앱의 특정 서비스를 제공하지 전, 사용자 인증 또는 권한을 확인해야 할 경우가 존재한다. 이러한 기능을 aspect 를 이용하여 비즈니스 코드와 분리시켜 간결한 코드를 작성할 수 있다.


Essential of Aspect-Oriented Programming (AOP)

우리는 결국 aspect 를 이용해 코드를 간결하게 만들 수 있고, 우리의 주요 관심사항에 여러 부가 기능을 끼워넣을 수 있다.

이처럼 주요 관심 사항 (Core Concerns) 에 다른 관심 사항을 교차시켜 공통된 관심사항 (Cross-cutting Concerns) 으로 프로그래밍 하는 방식을 관점 지향 프로그래밍 (AOP, Aspect-Oriented Programming) 이라 부른다.


An Introduction to Aspect-Oriented Programming - SAIGON TECHNOLOGY Blog

하지만 이를 직접 사용하기 전, AOP 와 관련된 용어들을 정리하고, 그에 관한 개념을 익히는 것이 먼저이다. [1], [2]

NameDescription
Aspect핵심 관심사항에 부가적으로 적용시키고 싶은 로직들의 집합. (Modularization of a concern)
Join Point프로그램 실행 중 에러 처리, 메서드 실행 등 지칭할 수 있는 어느 한 지점. (Point during the execution of a program)
(+ Spring-aop 의 경우 항상 메서드 호출 지점)
Advice특정 Join Point 에 수행되고 싶은 행동을 지칭하는 단어로, 어느 메서드 실행 직전 (Before), 실행 전 후 (Around) 등의 실행되고픈 Join Point 정보 또한 포함된다.
(ex : A 메서드 (Where) 실행 전 (when) 에 어떤 기능 (what) 이 수행되었으면 한다 --> (Were + When + What) = Advice)
Pointcut프로그램의 Join Point"서술된 Join Point" (A predicate that matches join points) 로, 어느 Advice 가 프로그램의 어떤 Join Point(Where + When) 사용되어야 하는지 나타낸다.
(스프링은 기본적으로 AspectJ Pointcut 방식의 Pointcut 표현 방식을 사용함)
Target ObjectAspect 가 적용되는 개체

위 단어들은 Spring-aop 에만 사용되는 단어가 아닌, AOP 에서 통용되는 단어들이다.

때문에 스프링 aspect 를 이용하기 전, 위 개념들을 반드시 알고 있는 것이 좋다.


Join Point

Join Point 는 프로그램 실행 중 우리가 지칭할 수 있는 임의의 한 지점이다. 통상적인 AOP 의 Join Point(설명 그대로) 우리가 지칭만 할 수 있으면 모두 Join Point 라 할 수 있다.

// Before initializing some local variable  
// --> This can be a Join Point

int someValue = 10;     // Initializing local variable
// --> This can be also a Join Point!

System.out.println(someValue);  
// After calling System.out.println method
// --> And this can be Join Point!!

하지만 스프링 AOP 의 경우 기본적으로 Proxy Pattern 을 기반으로 aop 를 적용시키기 때문에, 스프링의 Join Point 는 항상 메서드 호출 부근을 지칭한다.


AspectAdvice

Aspect 는 핵심 관심사항에 적용시키고 싶은 로직들의 집합으로, 다수의 관심 사항을 그 종류와 특성에 맞춰 분류할 수 있다.

또한 Advice특정 Join Point (At a particular Join Point) 부가적으로 적용시키고 싶은 행동 (Action) 으로, 사실 행동과 실행 위치, 시점을 모두 포함하는 개념 이다.


Pointcut

PointcutJoin Point 의 일종으로, 프로그램 상 모든 Join Point 중 우리의 Aspect 가 개입된 Join Point 를 지칭한다.

이는 Advice 와 유사하게 실행 위치와 시점 을 포함하는 개념으로, 이 둘의 차이점은 우리가 지시하는 행동 이 포함되는지 이다.


위 개념들을 정리해서 나타내면 다음과 같다.

AOP 는 결국 프로그램 내 우리가 원하는 지점들에 관심사항을 끼워넣는 것일 뿐, 전혀 어려운 개념이 아니다.


Spring-AOP, how it works? - The Proxy

그렇다면 스프링은 내부적으로 어떻게 작동하길래 이런 AOP 가 가능한 것일까?

이는 사실 이전 챕터에서 언급한 적이 있듯이, 스프링이 제공하는 bean 은 정확히 해당 개체가 아니라 모방품 (Proxy) 일 수 있기 때문이다.

다음 예시를 통해 자세히 알아보자.

[자세한 코드...]
class NormalBean {}

class AdvisedBean {
   public void someMethodToIntercepted() {}
}

@Aspect
@Component
class SomeAspect {
   @Around("execution(* AdvisedBean.*())")
   public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
      return joinPoint.proceed();
  }
}

@Configuration
@EnableAspectJAutoProxy
@ComponentScan
public class Config {
    @Bean
    @Lazy
    public NormalBean normalBean() {
        NormalBean bean = new NormalBean();
        showIdentity(bean);
        return bean;
    }
  
    @Bean
    @Lazy
    public AdvisedBean advisedBean() {
        AdvisedBean bean = new AdvisedBean();
        showIdentity(bean);
        return bean;
    }
  
    public static void showIdentity(Object o) {
        System.out.printf("[%s] \t: 0x%08x\n", 
            o.getClass().getSimpleName(), 
            System.identityHashCode(o));
    }
}
var context 
        = new AnnotationConfigApplicationContext(
        Config.class
);

NormalBean normalBean = context.getBean(NormalBean.class);
Config.showIdentity(normalBean);

System.out.println();
AdvisedBean advisedBean = context.getBean(AdvisedBean.class);
Config.showIdentity(advisedBean);
[NormalBean] 	: 0x2dfaea86    // identity of real object
[NormalBean] 	: 0x2dfaea86    // idnetity of object provided by spring context

[AdvisedBean] 	: 0x69c81773    // We expect THIS object will be provided by spring context
[AdvisedBean$$SpringCGLIB$$0] 	: 0x3e62d773  // BUT IT'S NOT! IT'S PROXY!!

위 코드를 보면 System.identityHashcode 로 각 개체의 정확한 identity 를 확인하고 있다.

Config 클래스에 bean 으로 제공할 개체의 identity 를 확인하고, context 에서 제공받은 bean 의 identity 를 확인하고 있다.

따라서 위 코드는 결국 스프링에 등록되는 개체와 제공받는 개체가 동일한지 확인하는 목적이다.

그런데 출력에서 볼 수 있듯, NormalBean 은 동일하지만 AdvisedBean 은 다른것을 볼 수 있다!

기본적으로 스프링은 context 에 bean 이 등록될 때 (runtime), 해당 bean 이 Proxy 이어야 하는지 확인한다.

NormalBean 의 경우 Proxy 로 설정되어 제공되어야할 기능이 존재하지 않는다. 때문에 context 에 등록될 때와 bean 으로 제공되었을 때 모두 0x2dfaea86 로 동일하다.

하지만 AdvisedBean 의 경우 SomeAspect 에 의해 존재하는 모든 메서드가 가로채어진다. 때문에 스프링은 AdvisedBean bean 을 Proxy 로 제공할 필요성을 느끼고 AdvisedBean$$SpringCGLIB$$0 : 0x3e62d773 개체를 bean 으로서 제공하는 것이다.

결국 스프링은 모방품을 bean 으로 제공함으로서 메서드 호출을 가로챌 수 있고, 이를 근간으로 AOP 기술을 실현한다.

더불어 스프링의 Proxy 는 JDK Dynamic Proxy 를 이용하는 방법과, CGLIB 를 이용하는 방법이 있다.

이들을 아주 간단하게 정리하자면 JDK Dynamic Proxy 는 자바의 Reflection API 를 이용해 Proxy 를 생성하는 방식이고, CGLIB 는 bean 클래스의 Byte Code 를 조작해 Proxy 로 만드는 방식이다.

더 자세한 설명은 아래 좋은 글이 있으니 이들을 참고하는 것이 좋을 듯 하다. [3], [4]

이들에 대한 Spring 공식 문서도 존재한다. [5]


Summary

  • 관점 지향 프로그래밍은 다음 5 가지 주된 개념이 존재한다.
    • Aspect, Join Point, Advice, Pointcut, Target Object
  • 스프링은 bean 으로 Proxy 를 제공하여 AOP 기술을 가능케 한다.
  • Spring-aop 가 Proxy 를 생성하는 방법은 JDK Dynamic Proxy, CGLIB 를 이용하는 방식이 있으며, 이는 모두 Runtime-weaving 이다. [6]
  • Spring-aop 는 Proxy 가 필요한 개체들만 Proxy 로 제공한다.

(Chapter 6 : Using aspects with Spring AOP (2/3) 에 계속...)


Reference

profile
나 같은게... 취준?!

0개의 댓글