본문 바로가기
기술/작성 중

AOP

by jhdevtrace 2024. 9. 10.

 

AOP는 핵심 기능과 횡단 관심사(부가 기능)를 분리해서 관리하는 것!!

핵심기능 : 시스템의 주요 목적

부가기능 : 주요 목적이 아닌 기능

횡단 관심사 : 부가 기능이 여러 곳에서 반복적으로 적용되는 상황

 

AOP 키워드

  1. 어드바이스 Advice
  2. 포인트컷 PointCut
  3. 타겟 Target
  4. 조인포인트 JoinPoint
  5. 애스펙트 Aspect

 

간략 정리

어드바이스 는 반복되는 횡단관심사를 정의해 놓은곳 입니다.

그 횡단관심사를 어느 범위에 적용할지 선택하는 것이 포인트컷 입니다.

그리고 그 범위 안에서 선택받은 객체들 혹은 객체가 타겟 이 되는 것입니다.

그 타겟 내에서 어드바이스 실제로 실행되는 시점을 조인포인트 라고 합니다.

 

 

종류별 개념

어드바이스 : 실제로 실행되는 횡단관심사(부가기능) 코드를 의미합니다.(시간측정로직)

다양한 프로젝트에선느 어드바이스가 로깅처리, 인증인가처리, 트랜잭션 처리와 같은 부가기능으로 활용될 수 있습니다.

 

 

포인트컷 : AOP 에서 어드바이스를 적용할 구체적인 범위를 선택하는 규칙입니다. 해당 범위는 특정 패키지 내의 모든 메서드들일 수도 있고, 특정 패키지내의 클래스의 모든 메서드들일 수도 있습니다. 가장 많이 쓰이는 특정 애너테이션이 달린 메서드일 수도 있습니다.

포인트컷을 통해 어드바이스를 어느 범위에 적용할지 정확하게 지정할 수 있습니다.

 

 

타겟 : AOP 에서 어드바이스가 적용되는 객체를 의미합니다. 예를 들면 포인트컷으로 적용된 특정 클래스(CourseService) 가 타겟이 되면, 그 클래스 내의 모든 메서드들이 어드바이스의 적용을 받습니다.

execution(int createCourse(int, int)) // 반환타입 int, createCourse 이라는 매서드, 매개변수는 int 형 2개
execution(* createCourse(int, int)) // 반환타입 상관없음, createCourse 이라는 매서드, 매개변수는 int 형 2개
execution(* createCourse(..)) // 반환타입 상관없음, createCourse 이라는 매서드, 매개변수 상관없음
execution(* *(..)) // 반환타입 상관없음, 메서드이름 상관없음, 매개변수 상관없음

 

 

조인포인트 : 어드바이스(횡단관심사) 가 적용될 수 있는 실행 지점을 의미합니다. 우리가 메서드 기준으로 어드바이스를 동작시킴으로 메서드들이 조인포인트가 됩니다.

 

 

애스팩트 : 어드바이스(횡단관심사)와 포인트컷(횡단 관심사를 어디에 적용할지) 를 하나로 묶은 모듈입니다.

 

 

어드바이스 종류 살펴보기

Before

    /**
     * 어드바이스: @Before
     * 메서드 실행 전에 수행되는 로직을 처리할때 사용합니다.
     */
    @Before("serviceLayer()")
    public void beforeMethod(){
        log.info("::: BEFORE 실행 :::");
    }

AfterReturning

    /**
     * 어드바이스: @AfterReturning
     * 메서드가 정상적으로 반환된 후에 실행됩니다.
     * 예외가 발생하지 않고 정상적으로 결과값을 반환했을때만 동작합니다.
     * @param result 성공시 반환값
     */
   @AfterReturning(pointcut = "serviceLayer()", returning = "result")
    public void afterReturningMethod(Object result) {
       // result 에 대한 작업을 수행할 수 있겠죠?
       log.info("::: AFTER RETURNING :::");
    }

AfterThrowing

    /**
     * 어드바이스: @AfterThrowing
     * 메서드 실행 중 예외가 발생했을 때만 실행됩니다.
     * @param joinPoint 조인포인트
     * @param ex 발생한예외 객체
     */
    @AfterThrowing(pointcut = "serviceLayer()", throwing = "ex")
    public void afterThrowingMethod(JoinPoint joinPoint, Throwable ex) {
        // 서비스에서 발생한 예외 ex.getMessage() <- 예외 조작 가능
        log.info("::: AFTER THROWING :::");
    }

After

    /**
     * 어드바이스: @After
     * 메서드가 정상적으로 실행되던 예외가 발생하던 메서드가 완료된 후에 항상 실행됩니다.
     */
    @After("serviceLayer()")
    public void afterMethod(JoinPoint joinPoint) {
        log.info("::: AFTER :::");
    }

Around

    /**
     * 어드바이스: 가장 강력한 어드바이저, 전체 흐름을 제어할 수 있습니다.
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    @Around("serviceLayer()")  // 어노테이션 기반으로 포인트컷 설정
    public Object aroundMethod(ProceedingJoinPoint joinPoint) throws Throwable {

        log.info("::: BEFORE :::");
        try {
            Object result = joinPoint.proceed();
            System.out.println(result);
            log.info("::: AFTER RETURNING :::");
            return result;

        } catch (Exception e) {
            log.info("::: AFTER THROWING :::");
            throw e;

        } finally {
            log.info("::: AFTER :::");
        }
    }

 

 

패키지 범위 기반 어드바이스 적용

    /**
     * 어드바이스: 패키지 범위 기반
     */
    @Around("serviceLayer()")
    public Object advicePackage(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();

        try {
            return joinPoint.proceed();

        } finally {
            long endTime = System.currentTimeMillis();
            long executionTime = endTime - startTime;
            log.info("::: ExecutionTime: {}ms", executionTime);
        }
    }

 

 

 

 

어노테이션 범위 기반 어드바이스 적용

어노테이션 생성

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TrackTime {
}

 

 

포인트컷 생성

    /**
     * 포인트컷: 어토네이션 기반
     */
    @Pointcut("@annotation(com.standard.sparta.aop.annotation.TrackTime)")
    private void trackTimeAnnotation() {}

 

 

어드바이스

    /**
     * 어드바이스: 어노테이션 범위 기반
     */
    @Around("trackTimeAnnotation()")
    public Object adviceAnnotation(ProceedingJoinPoint joinPoint) throws Throwable {
        //측정 시작
        long startTime = System.currentTimeMillis();

        try {
            Object result = joinPoint.proceed();
            return result;
        } finally {
            // 측정 완료
            long endTime = System.currentTimeMillis();
            long executionTime = endTime - startTime;
            log.info("::: ExecutionTime: {}ms", executionTime);
        }
    }