AOP ( Aspect-Oriented Programming 관점 지향 프로그래밍) 는 IoC/DI, 서비스 추상화와 더불어 스프링의 3대 기반기술의 하나이다. 우리가 흔히 알고 있는 것중엔 @Transactional 이 대표적인 AOP의 예시이다. 어떤 로직을 기준으로 핵심적인 관점, 부가적인 관점으로 나누어서 보고 그 관점을 기준으로 각각 모듈화하겠다는 것이고, 이를 핵심적인 비즈니스 로직에서 분리하여 재사용하는 것이다.
게시물을 등록하거나 댓글을 등록하는 기능이 있다고 생각해보자.
그 등록 과정에서 항상 로깅을 하고, JPA에서의 트랜잭션기능을 이용한다면 같은 동작에 대한 반복되는 중복 코드가 들어가게 될 것이다. 이를 게시물 등록과, 댓글 등록이라는 핵심적인 관점에서 빼내와서, 부가적인 관점으로 모듈화해서 사용하면 중복 코드가 줄어들지 않을까?
여기서 두 번 정도야 괜찮다고 생각할 수 있겠지만, 블로그를 만드는 데 DB에 CRUD 중 CUD에 대한 성공과 실패를 로깅을 하려한다면 그 중복 코드는 상당할 것이고 이를 모듈화해 분리하면 더 좋은 코드가 될 수 있지 않을까? 하는 것이다.

주요 개념들은 다음과 같다.
- Aspect : 여러 곳에서 쓰이는 코드를 모듈화한 것
- Target : Aspect로 모듈화된 코드가 적용되는 곳
- Advice : Aspect 에서 실질적인 기능에 대한 구현체 (로깅에 대한 apect면 어떻게 로그를 남길 것 인지에 대한 코드)
- Joint point : Advice 가 Target 에 적용되는 시점. ( ex. 메서드 진입할 때, 생성자 호출할 때, 필드에서 값을 꺼낼 때 등)
- Point cut : Joint point 의 상세 스펙을 정의한 것
- Weaving : 지정된 객체에 애스팩트를 적용해서 새로운 프록시 객체를 생성하는 과정
이를 구현하는 방법에는 세 가지가 있다고 볼 수 있고 그것들은 다음과 같다.
- AspectJ
- CG Lib based proxy
- JDK based Proxy
가장 간단하게 적용가능한 것은 AspectJ를 이용하는 방법이고, 코드로 표현해보면 다음과 같다.
모듈화를 할 코드를 @Aspect라는 Annotation을 사용하는 방법(이를 사용하려면 커스텀 어노테이션을 생성이 필요하다.)이고, @Around 는 Joint Point와 연관 되어 있는 부분이다. 또한, Around 안에의 Annotation 은 point cut과 관련된 부분이다. 이는 아래서 더 다룰 것이다.
(또한 이를 사용하기 위해서는 @SpringBootApplcation 부분에 @EnableAspectJAutoProxy 를 추가해야한다. )
@Component
@Aspect
public class LoggingAdvice {
@Around("@annotation(OperationLogging)")
public Object logging(ProceedingJoinPoint pjp) throws Throwable {
// pre proceed
Object retVal = pjp.proceed();
// After procced
}
}
@Override
@OperationLogging
public Order createOrder(Long memberId, String itemName, int itemPrice) {
Member memeber = memberRepository.findById(memberId);
int discountPrice = discountPolicy.discount(memeber,itemPrice);
return new Order(memberId,itemPrice,itemPrice,discountPrice);
}
위에서 보았던 Around 말고는 또 다른 어떤 것들이 있을까?
- Around : 타겟의 메서드가 호출되기 이전(before) 시점과 이후 (after) 시점에 모두 처리해야 할 필요가 잇는
부가기능을 정의한다.
- Before : 타겟의 메서드가 실행되기 이전(before) 시점에 처리해야 할 필요가 있는 부가기능을 정의한다.
- After Returning : 타겟의 메서드가 정상적으로 실행된 이후(after) 시점에 처리해야 할 필요가 있는 부가기능을 정의한다.
- After Throwing : 타겟의 메서드가 예외를 발생된 이후(after) 시점에 처리해야 할 필요가 있는 부가기능을 정의한다.
또 Annotation 말고 또 어떤 것이 있을까? execution, within, bean, args, this, target 등이 있지만, 주로 사용하는 execution만 보면 다음과 같다.
@execution : execution([수식어] 리턴타입 [클래스이름].이름(파라미터)
- 수식어 : public, private 등 수식어를 명시 (생략 가능)
- Return 타입 : Return 타입을 명시
- 클래스 이름 및 매서드 이름 : 클래스이름과 메서드 이름을 명시.
- 파라미터 : 메서드의 파라미터를 명시
최근에 관련 기술을 이용해서 코딩을 했을 때는 Annotation 을 이용했고, 이때의 장점은 특정 파라미터를 전달이 가능하다는 것이다.
@Aspect
@Component
public class LoggingAdvice {
@Around("@annotation(OperationLogging) && @ annotation(target)")
public Object customAnnoTest(ProceedingJoinPoint joinPoint, TargetAnno target) throws Throwable {
System.out.println("command : " + target.command());
Object proceed = joinPoint.proceed();
return proceed;
}
}
@Override
@OperationLogging (command = "create")
public Order createOrder(Long memberId, String itemName, int itemPrice) {
Member memeber = memberRepository.findById(memberId);
int discountPrice = discountPolicy.discount(memeber,itemPrice);
return new Order(memberId,itemPrice,itemPrice,discountPrice);
}
이번 주에 사용하기 위해서 AOP에 대해서 꽤 많은 공부를 하였음에도 이를 알고 있는 것과, 잘 정리하는 것은 꽤 다른 일이라는 생각을 하였다. 더 잘 정리할 수 있게 공부하면서 조금씩 요약하여 기록해두는 습관을 들여야 할 것 같다.