본문 바로가기

테크 노트/소소한 개발 팁

spring, aop 특정 조건에서 실행 제외하기

AOP에 대한 자세한 설명은 생략합니다. 아주 간략히 정리하면
특정 조건(어느 패키지, 어느 클래스, 어느 메소드 등) 메소드를 수행할 때
그 메소드의 실행 전, 실행 후, 실행 전 후에 참견하는 기능입니다.
(그 특정 조건을 pointcut이라고 합니다.)

Spring에서는 AspectJ라는 라이브러리를 이용합니다.

 

준비물

Spring boot에서 AOP 설정 하는 방법을 간략하게 정리하겠습니다.
포스트 제목의 답을 구하신다면 과감히 스킵해주세요.

<!-- pom.xml -->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

pom.xml에 의존성을 추가해봅시다.

@Configuration
@EnableAspectJAutoProxy // 이 어노테이션으로 활성화 해줍시다
class SpringConfig {
    ...
    
 }

설정은 위와같이 하면 되겠습니다. 

import org.aspectj.lang.ProceedingJoinPoint
import org.aspectj.lang.annotation.Around
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.Pointcut
import org.springframework.stereotype.Component

@Aspect
@Component
class LogExecutionTime {
    @Pointcut("execution(public * com.your.package.controller.rest.*.*(..))")

    @Around("controllerMethods()") // 물론 이 안에 직접 pointcut을 넣어도 됩니다
    fun logExecutionTime(joinPoint: ProceedingJoinPoint): Any? {
        val before = System.currentTimeMillis()
        val result = joinPoint.proceed()
        val after = System.currentTimeMillis()
        println(after - before)

        return result
    }
}

(참고로 kotlin입니다.)

메소드 실행 전 후의 timestamp를 통해서 메소드 실행 시간을 체크하는 예제입니다.
@PointCut 부분을 참고하시면 알 수 있듯이
com.your.package.controller.rest. 패키지 밑에 있는 모든 클래스의 모든 메소드에서 수행됩니다.

 

특정 메소드만 실행 제외하기

그런데 저기서 특정 메소드만 제외하고 싶으면 어떻게 할까요?
방법은 두가지입니다.
첫째, 특정 메소드 빼고 모든 메소드를 명시하기 (이럼 안되겠죠)
둘째, 제외할 메소드를 마킹하고 그것만 안타게 처리하기

여기서 두번째 방법을 소개합니다.

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

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

위 처럼 NoLogging이라는 어노테이션을 하나 만듭시다.
(@Target, @Retention 등의 설명은 생략합니다.)

import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
import com.your.package.annotation.NoLogging

@RestController
class AdminController {
    @NoLogging
    @GetMapping("ping")
    fun ping(): String {
        return "pong"
    }
}

위처럼 생략하고자하는 메소드 위에 @NoLogging 어노테이션을 붙여봅시다.

@Pointcut("execution(public * com.your.package.controller.rest.*.*(..)) 
			&& !@target(com.your.package.annotation.NoLogging)")
fun controllerMethods() {}

@PointCut 안의 내용을 위와같이 수정합니다.
!@Target 부분을 참고해주세요 (!@Annotation도 가능할겁니다.)

@Around로 걸려있는 메소드에 break point를 걸며 테스트해봅시다.
skip 되는것을 알 수 있습니다.

 

마치며

생각보다 별거 아니네요.