오늘은 Filter, Interceptor에 이어 AOP에 대해 알아봤다.
AOP: Aspect Oriented Programming. 관점 지향 프로그래밍.
OOP의 모듈성을 더욱 향상시키는 방법이다.
비즈니스 로직을 기준으로 핵심 관점(비즈니스 로직) / 부가 관점(공통 모듈)으로 나누어서 보고 그 관점을 기준으로 각각을 모듈화 한다.
부가기능을 Aspect로 모듈화하고 핵심기능에서 분리해 필요한 시점에 비즈니스 로직에 삽입해 재사용하는 것이 AOP의 취지다.
AOP는 스프링 Context에서 관리되고, Interceptor 적용 후에 실행된다.
Filter, Interceptor와 다르게 URL 패턴이 아닌 주소, 파라미터, 어노테이션, 메서드, 컨트롤러 등 Pointcut이 지원하는 다양한 방식으로 호출위치를 지정할 수 있다.
그리고 메서드 전후 시점에 자유롭게 설정할 수 있다.
Filter와 Interceptor는 HttpServletRequest, HttpServletResponse를 파라미터로 사용한다.
AOP는 JoinPoint나 ProceedingJoinPoint 등을 활용해서 호출한다.
AOP에 대한 자세한 정리는 Spring 심화 #7을 참고.
Pointcut을 사용하기 때문에 Filter와 Interceptor보다 구체적이고 세세하게 부가기능을 다룰 수 있다.
글쓰기 시, admin 계정만 controller의 processtime을 측정하여 로깅하는 로직을 aop로 구현했다. 메서드의 parameter인 userdetails를 aop내에서 활용하여 admin 여부를 check했다. 포인트컷이 @Around로 지정되어 컨트롤러 전/후로 동작한다.
@Aspect
@Component
@Slf4j
public class ProcessTimeAop {
/*
Admin 계정으로 글을 작성할 때 processTime을 측정
writePost 메서드의 2번째 파라미터인 UserDetails를 활용해 admin 유저 여부 check
*/
@Around("execution(public * com.example.choonb.domain.post.controller.PostController.writePost(..)) && args(.., userDetails))")
public Object checkProcessTime(ProceedingJoinPoint joinPoint, UserDetailsImpl userDetails) throws Throwable {
boolean isAdmin = userDetails.getAuthorities().stream()
.anyMatch(authority -> authority.getAuthority().equals("ROLE_ADMIN"));
if (!isAdmin) return joinPoint.proceed();
long startTime = System.currentTimeMillis();
try {
return joinPoint.proceed();
} finally {
long processTime = System.currentTimeMillis() - startTime;
log.info("[ProcessTime] : " + processTime + "ms");
}
}
}