1. AOP (Aspect-Oriented Programming)란?


AOP는 어플리케이션 전체에 걸쳐 사용되는 하나의 공통된 기능을 한 곳에서 관리할 수 있도록 하는 기법입니다.

AOP는 관점 지향 프로그래밍이라는 말로 번역되는데, 이는 말그대로 관점에 따른 프로그래밍 방법을 말합니다.

쉽게 얘기해서, 비즈니스 로직을 살짝 다른 관점에서 생각해보자라는 것입니다.

 

비즈니스 로직을 담고있는 *Service 클래스를 기준으로 바라본다고 생각해보겠습니다.

[그림 1] 비즈니스 로직을 담고있는 service 객체

Board와 관련된 비즈니스 로직을 담당하는 BoardService와 Member와 관련된 비즈니스 로직을 담당하는 MemberService입니다.

하나의 서비스를 기준으로 바라보게되면 둘은 공통점이 없습니다. 하지만, 관점을 조금 달리해서 측면을 기준으로 바라본다면 어떨까요?

[그림 2] 측면, 즉 가로방향으로 바라보기

(이러한 이유로 AOP라는 이름이 붙은 듯합니다.)

기존에는 *Service라는 객체 단위(세로 방향)로 바라봤지만, AOP에선 한 단계 더 깊게 들어가 *Service 안에서의 기능(메서드) 단위로 바라봅니다. AOP의 대표적인 예로는 로깅, 트랜잭션, 보안 등이 있습니다.

2. AOP 관련 용어


해당 AOP 용어는 Spring에서만 사용되는 용어가 아니라, AOP 프레임워크 전반적으로 사용되는 용어입니다.

1. Target: 공통된 기능을 적용할 대상을 말합니다.

2. Aspect: 객체지향에서는 객체 단위를 모듈이라고 하고, 관점지향에서는 Aspect 단위를 모듈이라고 합니다.
    (물론 둘다 Java로치면 둘다 하나의 클래스이기는 합니다.)
3. Advice
    실질적인 공통된 기능을 말합니다. (Aspect가 클래스고, Advice가 메서드인 것이죠. 🧐 )
    이 Advice(메서드)에는 '언제', '어떤 대상의 메서드' 에 적용할지가 같이 정의되어 있습니다.
    (흔히 메서드 위에 @Before( execution(* *.*(..)) ) 처럼 정의되어 있습니다.)
    위의 예에서 @Before이 '언제'에 해당하는 부분인데, Advice는 이 외에도 여러가지 '언제'의 시점을 제공합니다.
    @Before, @After, @AfterReturning, @AfterThrowing, @Around
4. PointCut
    공통된 기능이 적용될 대상을 선정하는 방법을 말합니다.
    위의 Advice에서 execution(* *.*(..)) 처럼 표시했는데, 이것을 하나의 메서드로 정의해서 표현할 수도 있습니다.
5. JoinPoint
    Advice가 적용될 수 있는 위치를 말합니다. 
    다른 AOP 프레임워크와는 달리 Spring은 메서드 JoinPoint만 제공합니다.
6. Proxy
    위에서 말한 Target을 감싸서 요청을 대신 받아주는 Wrapping Object입니다.
    클라이언트 코드가 타겟을 호출하면 Proxy 객체가 대신 요청을 대신 받아 선처리-타겟 메서드 실행-후처리 순으로 실행합니다.

[그림 3] 요청에 대한 중간다리 역할을 하는 프록시 객체

7. Weaving
    타겟 객체에 Aspect를 적용해서 새로운 Proxy 객체가 생성되는 과정을 말합니다.
    타겟 객체에 트랜잭션을 담당하는 Aspect가 적용되어 있다고 가정해보겠습니다.
    타겟 객체가 실행되기 전에 Aspect에 의해 커넥션이 열리고, 타겟 객체 실행 후에는 커넥션이 닫히게하는 기능이 담긴 프록시 객체가 생      성됩니다. 이 때 생성된 프록시 객체가 앞으로 적용된 모든 타겟의 호출에 관여할텐데, 이 때 이 프록시 객체가 생성되는 과정을 위빙              (Weaving)이라 합니다. 컴파일 타임, 클래스로드 타임, 런타임 중에서 Spring AOP는 런타임에 프록시 객체가 생성됩니다.

3. 사용법


코드는 다음과 같이 작성해줄 수 있습니다.

@Component
@Aspect
public class LoggingAspect {
    private Logger logger = LoggerFactory.getLogger(LoggingAspect.class);

    @Before(value = "execution(* com.pangtrue.model..*.*(..))")
    public void loggin(JoinPoint jp) {
        logger.debug("메서드 선언부 : {} 전달 파라미터 : {}", jp.getSignature(), Arrays.toString(jp.getArgs()));
    }
}

가장 먼저 Advice를 살펴보겠습니다.

@Before( execution(* com.pangtrue.practice.BoardService.getBoardList(..)) )
public void adviceMethod() {
    // do somthing...
}

위의 코드에서 메서드 위의 어노테이션 부분들을 나눠서 살펴보겠습니다.

[그림 4] Advice 설정

Advice는 '언제' 라는 시점을 나타내는건데, 총 5가지를 지원합니다. 🤔

  • @Before - 타겟 메서드가 실행되기 전에 해당 Advice 기능을 실행.
  • @After - 타겟 메서드가 실행된 후에 해당 Advice 기능을 실행.
  • @AfterReturning - 타겟 메서드가 성공적으로 결과값이 반환되었다면, 그 후에 Advice 기능을 실행.
  • @AfterThrowing - 타겟 메서드가 실행 중 예외를 던졌다면, 그때 해당 Advice 기능을 실행.
  • @Around - 타겟 메서드 실행 전후로 Advice 기능을 실행.

또한, 위에서 PointCut 지정자로 execution()을 사용했는데, 이외에도 다음과 같은 지정자를 사용할 수 있습니다.

  • args() - 타겟 메서드의 파라미터 형식을 지정하고 싶은 경우.
  • @args() - 타겟 메서드의 파라미터에 특정 어노테이션이 붙어있는 경우.
  • execution() - 접근 지정자, 리턴 타입, 패키지명.클래스명.메서드명, 파라미터 타입을 세심하게 지정할 수 있는 지정자.
  • @annotation() - 타겟 메서드에 특정 어노테이션이 적용되어 있는 경우. 위의 @args()는 메서드의 파라미터 안에 어노테이션이 있는 경우고, 이건 메서드 자체에 어노테이션이 붙어있는 경우.

4. 참고사이트


[1] https://jojoldu.tistory.com/71