본문 바로가기

Spring Boot

[Spring Boot] AOP 포인트 컷 사용하기

728x90

포인트컷(Pointcut)의 주요 용도

포인트컷(Pointcut)은 AOP(Aspect-Oriented Programming)에서 Aspect(부가기능)가 적용될 지점을 결정하는 역할을 합니다. 즉, 어떤 메소드, 클래스 또는 패키지에 Advice(부가기능 로직)를 실행할지를 지정하는 것입니다. 이를 통해 특정 로직을 선택적으로 적용하고 불필요한 부분을 배제할 수 있습니다.

 

 

포인트컷(Pointcut) 왜 쓸까?

특정 메소드나 클래스에 부가기능을 정확히 적용하기 위해

  • 모든 메소드에 일괄적으로 적용하면 불필요한 코드 실행이 발생할 수 있습니다.
  • 포인트컷을 통해 정확히 필요한 클래스/메소드만 필터링해 부가기능(로깅, 트랜잭션)을 적용할 수 있습니다.
  • 예시  : 특정 컨트롤러 클래스에만 로깅을 적용할 때.

유지보수성과 코드 간결성 향상

  • 부가기능을 여러 곳에 반복적으로 추가하는 대신 한 번의 선언으로 특정 지점들에 부가기능을 적용할 수 있습니다.
  • 만약 수정이 필요하면 포인트컷만 변경하면 되기 때문에 유지보수가 용이합니다.
  • 예시 : 서비스 계층의 모든 메소드에 동일한 트랜잭션 관리 적용.

공통 관심사의 분리와 코드 중복 제거

  • 로깅, 예외 처리 등과 같은 공통 기능을 Aspect로 분리하고, 포인트컷을 통해 여러 메소드에 적용하면 코드 중복을 줄이고 가독성을 높일 수 있습니다.
  • 예시 : 모든 컨트롤러의 요청을 로깅.

유연하고 세밀한 부가기능 적용

  • 포인트컷 표현식(within, execution, args 등)을 조합해 특정 조건에 맞는 지점에만 부가기능을 유연하게 적용할 수 있습니다.
  • 예시: 매개변수로 특정 인자를 받는 메소드에만 부가기능 적용.

 

 

AOP 주요 어노테이션

  • @Aspect : AOP를 정의하는 Class에 할당합니다
  • @Pointcut : 어디(메소드, 어노테이션 등등)에 적용시킬지 지정하는 어노테이션입니다.
  • @Before : 메소드 실행하기 이전에 실행됩니다 .
  • @After : 메소드가 성공적으로 실행 후, 예외가 발생하더라도 실행됩니다 .
  • @AfterReturing : 메소드 호출에 성공 했을 때 실행됩니다 .
  • @AfterThrowing : 메소드 호출 실패(예외) 했을 때 실행됩니다 .
  • @Around : @Before + @After 메소드 실행 전, 후로 실행됩니다.

 

AOP Pointcut 지시자(PCD : PointCut Designators)

  • execution : 반환타입, 타입, 메소드, 파라미터 기준으로 지정(가장 세밀하게 제어가능)
  • within : 특정 경로의 타입을 기준으로 지정
  • this : 특정 타입의 객체를 기준으로 지정
  • target : 특정 타입의 객체를 기준으로 지정
  • args : 특정 타입의 파라미터를 가지는 메소드를 기준으로 지정
  • @target : 특정 어노테이션을 가지는 객체를 기준으로 지정
  • @args : 특정 어노테이션의 파라미터를 가지는 메소드를 기준
  • @within : 특정 클래스의 경로의 어노테이션을 기준
  • @annotation : 특정 메소드의 어노테이션을 기준
  • bean : 스프링 빈을 기준으로 저징

자세한 내용은 여기를 클릭!!

 

간단한 포인트컷 예제

// AOP를 사용하여 메소드 실행 시간을 측정하고, 요청 인자를 수정하는 기능을 정의하는 클래스입니다.
@Aspect
@Component
public class TimerAop {

    // 특정 클래스에 대해 AOP를 적용하기 위한 포인트컷을 정의합니다.
    @Pointcut(value = "within(com.example.pointcut.controller.TestController)")
    public void timerPointCut() {
        // 포인트컷 메소드는 내용이 없으며, AOP에서 이 메소드를 참조하여 특정 지점을 지정합니다.
    }

    // 포인트컷에 정의된 메소드 실행 전 호출되는 메소드입니다.
    @Before(value = "timerPointCut()")
    public void before() {
        System.out.println("Before");
    }

    // 포인트컷에 정의된 메소드 실행 후 호출되는 메소드입니다.
    @After(value = "timerPointCut()")
    public void after() {
        System.out.println("After");
    }

    // 포인트컷에 정의된 메소드가 정상적으로 실행된 후 호출되는 메소드입니다.
    @AfterReturning(value = "timerPointCut()", returning = "result")
    public void afterReturning(JoinPoint joinPoint, Object result) {
        System.out.println("after returning");
        // 여기서 result를 통해 메소드의 반환 값을 확인할 수 있습니다.
    }

    // 포인트컷에 정의된 메소드 실행 중 예외가 발생했을 때 호출되는 메소드입니다.
    @AfterThrowing(value = "timerPointCut()", throwing = "ex")
    public void afterThrowing(JoinPoint joinPoint, Throwable ex) {
        System.out.println("afterThrowing");
        // 발생한 예외를 로깅하거나 처리할 수 있습니다.
    }

    // 포인트컷에 정의된 메소드를 감싸고, 실행 전후의 처리를 할 수 있는 메소드입니다.
    @Around(value = "timerPointCut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        // 메소드 실행 전 로깅
        System.out.println("메소드 실행 시작: " + joinPoint.getSignature().getName());

        // 실행 시간을 측정하기 위한 StopWatch 객체 생성
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();

        Object result;

        try {
            // 실제 메소드 실행
            result = joinPoint.proceed();
        } catch (Throwable throwable) {
            // 예외 발생 시 로깅
            System.out.println("메소드 실행 중 예외 발생: " + throwable.getMessage());
            throw throwable; // 예외를 다시 던져서 호출자에게 전달
        }

        // 메소드 실행 시간 측정
        stopWatch.stop();
        System.out.println("메소드 실행 완료: " + joinPoint.getSignature().getName() +
                ", 총 소요 시간: " + stopWatch.getTotalTimeMillis() + " ms");

        // 메소드의 결과를 반환
        return result;
    }
}

 

728x90