[Spring] AOP(Aspect Oriented Programming) 핵심 내용 정리

rockstar·2023년 6월 15일

Spring

목록 보기
7/10

AOP

AOP는 Aspect Oriented Programming의 약자다. 관점 지향 프로그래밍이라는 뜻을 가지고 있는 하나의 프로그래밍 방식이라고 생각하면 된다.
여기서 말하는 관점(Aspect)은 로깅(logging)이나 보안(security)과 같은 크로스 컷팅(concern) 관심사를 캡슐화하고, 이를 어떻게 그리고 언제 기본 코드에 적용할지를 지정하는 모듈이다.

쉽게 이해할 수 있게 설명해보자면, 만약에 Controller, Service, Repository 각각의 계층이 있을 때, 계층마다 로깅을 하고 싶을 때가 있을 것이다. 일일이 메서드 내부에 @Sl4j애너테이션을 사용해서 log.info("[log] ResultTime = {}", resultTime)
이런 식으로 사용할 수도 있겠지만, 이것을 만약 수 백 개 또는 수 천 개의 메서드에 일일이 작업해야 하는 상황이 온다면 그때도 이렇게 일일이 작성할 수 있을까?라는 의문을 가져야 한다.

이렇게 모든 기능 모듈에 로깅이라는 공통되는 관심사를 가지게 되는데, 이러한 공통 관심사를 횡단 관심사라고 하고, 이러한 횡단 관심사를 각각의 모듈에 적용시킬 수 있도록 AspectJ라는 프레임워크가 도움을 주기도 하지만, 복잡한 설정 때문에 사용하기란 쉽지 않다.

예를 들면, 컴파일 시점에 부가 기능을 적용한다던지, 클래스 로드 시점에 조작기를 지정한다던지.. 생각만 해도 머리가 복잡해지지 않는가? 이러한 복잡한 설정들은 사실 실무에서 그렇게 쓸 일이 많지는 않다고 하니 큰 걱정을 하지 않아도 된다. 우리가 원하는 대부분의 기능들은 Spring이 제공하는 Spring AOP만으로도 충분히 해결이 가능하다.

SpringAOP

방금 설명한 것과 같이 Spring에서는 AOP 관련 기능들을 제공한다. 물론 AspectJ를 쓸 수 있다면 그게 가장 Best이긴 하겠지만, Spring도 만만치 않은 기능들을 제공하기 때문에 지금 당장은 Spring에서 제공하는 AOP만 써도 충분하다고 생각이 든다.

implementation 'org.springframework.boot:spring-boot-starter-aop'

위와 같이 build.gradle에 작성하여, AOP를 사용할 수 있다.

우리는 Spring AOP를 사용하기 전에 기본적인 개념을 알 필요가 있다. 너무 어렵지 않게 정리를 할 생각이니, 지레 겁먹지 않았으면 좋겠다.

Target

우선 가장 먼저 알아야 할 개념은 Target이다. Target은 정말 어렵지 않다. Target은 이름 그대로 하나의 목표물이라고 생각하면 된다. 내가 로깅을 적용하고 싶은 객체를 나타내고, 일반적으로 Proxy 객체를 Target으로 한다.
이전 포스트에서도 설명했지만, 비즈니스 로직에 직접 로깅을 작성하는 것은 단일 책임 원칙에 어긋나게 되는데, 그 이유는 하나의 클래스가 하나의 책임만을 가지는 게 가장 바람직하며, 후에 유지보수나 확장을 할 때도 힘을 들이지 않고 할 수 있기 때문이다. 추가적으로 Spring이 내부적으로 기존 코드를 유지하고 해당 객체를 상속받는 Proxy 객체를 만들어서 Bean으로 등록해서 관리해주기 때문에 AOP 관련된 키워드들만 익혀서 사용하면 충분히 원하는 부가 기능을 추가하여 사용할 수 있으리라 생각한다.

Advice

Advice 단어를 그대로 해석하면 장치이고, 해석한 것과 같이 내가 원하는 기능을 수행하는 장치라고 생각하면 된다. 예를 들면, 로직을 수행하기 전에만 적용하고 싶은 경우도 있을 것이고, 로직을 수행하기 전과 후 또는, 예외가 발생했을 때 등등 사용을 원하는 시기가 있을텐데, 이러한 것들을 Advice와 관련된 애너테이션을 사용하여 편하게 설정할 수 있다고 보면 된다.

Pointcut

본인은 Pointcut이 영상편집하는 것과 유사하다고 생각한다. 영상편집을 하다 보면, 원하는 편집점이 있을 것이고 해당 영상을 분할해서 편집하곤 할텐데, 여기서도 마찬가지로 내가 원하는 부분이 있다면 Pointcut으로 설정하는 방식을 사용한다. 사용할 때는, Aspectj에서 지원하는 표현식을 사용하면 된다.

Advisor

Advice와 Pointcut을 하나의 Advisor로 묶어서 표현한다. Spring AOP에서만 사용하는 용어이며, 하나의 Proxy 객체는 여러 Advisor가 있는 경우 체인처럼 연결하여 다양한 Advisor를 가질 수 있고, 이 Advisor는 Advice와 Pointcut을 알고 있다는 게 중요한 포인트이다.

Join Point

Advice가 적용될 수 있는 위치를 Join Point라고 한다. 조금은 추상적인 개념이라 AOP를 적용시킬 수 있는 모든 지점이라고 생각하면 되고, Spring AOP는 프록시 방식을 사용하기 때문에 Join Point는 항상 메서드 실행 시점으로 제한된다는 것을 알 필요가 있다.

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.core.annotation.Order;

@Slf4j
@Aspect
public class LogAspect {

  @Around("hello.aop.order.aop.Pointcuts.allOrder()")
  public Object doLog(ProceedingJoinPoint joinPoint) throws Throwable {
     log.info("[log] {}", joinPoint.getSignature());
     return joinPoint.proceed();
  }

}

*Advice와 Pointcut을 좀 더 직관적으로 설명하기 위한 코드이다. @Around는 나중에 배우겠지만, 내가 원하는 로직이 있을 때 해당 로직을 수행하기 전과 수행하기 후 모두 적용하고 싶을 때 사용하는 Advice라고 생각하면 된다. Advice에는 여러 종류가 있다. 역시나 밑에서 좀 더 자세히 다루겠다.
그리고 @Around 내부를 보면 패키지와 메서드가 나와있는데, "해당 조건이 충족하는 메서드에 아래의 로직을 적용하겠다"라고 생각하면 매우 이해가 쉬울 것이다. 정말 이게 끝이다. 물론 복잡하게 설정한다면 어려워지겠지만, 당장 로깅을 하는 데는 큰 어려움이 없기에 받아들이기 어렵지 않을 것이다.

요약

정리를 하기 전에 말하고 싶은 건, 어느 정도 Java의 기본 개념과 Spring Core에 대한 개념을 먼저 공부한 후에 JDK 동적 프록시 & CGLIB 프록시를 공부하고 나서 AOP를 공부하는 게 좋을 것 같다. 그래야 이해하기 쉬우며, 적어도 내가 쓰는 기능의 원리 정도는 알고 쓰려는 노력 정도는 필요하다고 생각하기 때문에 시간이 조금 걸리더라도 이러한 공부 순서를 지키는 게 정신 건강에 좋을 것이다.

시간적인 여유가 된다면, Advice 종류와 Pointcut 표현식에 대해서 정리할 수 있도록 해봐야겠다.


잘못된 정보는 지적해주시면 감사하겠습니다.

0개의 댓글