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

반응형

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

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

사이드바가 메인, 장바구니, 결제, 상품상세 페이지 등에 적용이되어서 해당 컨트롤러들마다 

사이드바에 필요한 데이터를 응답하는 로직이 중복되어 @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