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에 대해서 꽤 많은 공부를 하였음에도 이를 알고 있는 것과, 잘 정리하는 것은 꽤 다른 일이라는 생각을 하였다. 더 잘 정리할 수 있게 공부하면서 조금씩 요약하여 기록해두는 습관을 들여야 할 것 같다. 

+ Recent posts