[Spring] 공통 기능을 @Aspect를 사용해 적용하기

아래와 같은 사이드바에 장바구니에 담긴 상품 개수, 찜 개수, 최근 본 상품이 표시되도록 하는 기능을 구현하게 되었다.

그런데 개수 같은 경우에는 count 쿼리로 구해 model에 담아 view넘기는데

사이드바가 메인, 장바구니, 결제, 상품상세 페이지 등에 적용이되어서 해당 컨트롤러들마다 count 쿼리를 날리는 건 낭비라 생각해 @Aspect로 AOP를 적용하게 되었다.


AOP 적용

아래는 사이드바에 공통으로 적용할 코드들을 담은 Aspect를 적용한 클래스이다.

@Aspect
@Component
@Slf4j
@RequiredArgsConstructor
public class SidebarAspect {
    private final LikeService likeService;
    private final CartService cartService;
    private final RecentProductService recentProductService;

    @Around("execution(* com.fruit.mall..*Controller.*(..)) && args(sessionUser,model,..)")
    public Object addSidebar(ProceedingJoinPoint joinPoint, @Login SessionUser sessionUser, Model model) throws Throwable {
        if (sessionUser != null) {
            int likesCount = likeService.countLikesByUserId(sessionUser.getUserIdNo());
            model.addAttribute("likesCount", likesCount);

            int userCartsCount = cartService.countCartByUserId(sessionUser.getUserIdNo());
            model.addAttribute("userCartsCount", userCartsCount);
        }

        HttpServletRequest request =
                ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
        List<RecentProduct> recentProducts = recentProductService.getRecentProductsFromCookie(request);
        if (recentProducts != null) {
            model.addAttribute(RECENT_PRODUCTS, recentProducts);
        }

        log.info("Sidebar Aspect 적용 메서드 정보 : {}", joinPoint.getSignature());
        return joinPoint.proceed();
    }
}

 

포인트 컷으로 적용할 대상을 설정했는데 나는 com.fruit.mall 하위의 Controller라는 이름이 붙은 클래스들에 파라미터가  sessionUser,model 이 있고 그 뒤는 모든 파라미터를 허용하였다. 

@Around("execution(* com.fruit.mall..*Controller.*(..)) && args(sessionUser,model,..)")

이렇게 포인트 컷을 적용하면 해당 포인트컷 조건에 맞는 컨트롤러의 메서드들에 해당 기능이 적용된다.

 

추가로 아래는 해당 Aspect가 적용된 컨트롤러 중 하나이다.

포인트 컷에 args에 작성한 sessionUser, Model, 기타 등등 이 파라미터에 포함되어 있는 것을 볼 수 있다. (순서도 일치해야 한다.)

해당 컨트롤러의 showDetail 메서드에는 addSidebar() 메서드가 적용되고 addSidebar() 메서드가 종료되면 joinPoint.proceed()를 반환하여 원래 메서드를 호출하게 된다.

@Controller
@RequiredArgsConstructor
public class ProductDetailController {
    private final ProductService productService;

    @GetMapping("/user/detail/{productId}")
    public String showDetail(@Login SessionUser sessionUser, Model model, @PathVariable("productId") Long productId) {
        ProductDetailForm productDetailForm = productService.selectProductDetailByProductId(productId, sessionUser);
        model.addAttribute("productDetailForm", productDetailForm);
        model.addAttribute("productId", productId);

        return "user/detail";
    }
}

Spring AOP 관련 내용 더 알아보기

2023.05.12 - [JAVA/Spring] - [Spring] 스프링 AOP(Aspect Oriented Programming)란? - @Aspect

 

[Spring] 스프링 AOP(Aspect Oriented Programming)란? - @Aspect

AOP를 사용하지 않는다면 ? AOP에 대해 설명하기 전에 AOP를 사용하는 이유에 대해 먼저 알아 봅시다. 애플리케이션 로직은 크게 핵심 기능과 부가 기능으로 나눌 수 있습니다. 핵심 기능 : 해당 객

hstory0208.tistory.com

 

2023.05.15 - [JAVA/Spring] - [Spring] 스프링 AOP (@Aspect) 사용법

 

[Spring] 스프링 AOP (@Aspect) 사용법

스프링 AOP 적용법 스프링 AOP를 사용하기 위해서는 build.gradle에 아래의 라이브러리를 의존성 추가해줘야합니다. implementation 'org.springframework.boot:spring-boot-starter-aop' // aop 추가 스프링 AOP를 적용하

hstory0208.tistory.com

 

2023.05.16 - [JAVA/Spring] - [Spring] 스프링 AOP - Pointcut 표현식

 

[Spring] 스프링 AOP - Pointcut 표현식

AspectJ는 포인트컷을 편리하게 표현하기 위한 특별한 표현식을 제공합니다. 스프링에서는 포인트컷 표현식에서 사용하기 위해 AspectJ가 제공하는 포인트컷 지시자(Pointcut Designator) 줄여서 PCD를 지

hstory0208.tistory.com

 

2023.05.17 - [JAVA/Spring] - [Spring] 스프링 AOP 주의사항 - 프록시 내부호출

 

[Spring] 스프링 AOP 주의사항 - 프록시 내부호출

스프링은 프록시 방식의 AOP를 사용합니다. 스프링 AOP를 적용하면 스프링은 대상 객체 대신에 프록시를 스프링 빈으로 등록하고, 스프링은 의존관계 주입시에 항상 프록시 객체를 주입합니다.

hstory0208.tistory.com