아래와 같은 사이드바에 장바구니에 담긴 상품 개수, 찜 개수, 최근 본 상품이 표시되도록 하는 기능을 구현하게 되었다.
그런데 개수 같은 경우에는 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
2023.05.15 - [JAVA/Spring] - [Spring] 스프링 AOP (@Aspect) 사용법
2023.05.16 - [JAVA/Spring] - [Spring] 스프링 AOP - Pointcut 표현식
2023.05.17 - [JAVA/Spring] - [Spring] 스프링 AOP 주의사항 - 프록시 내부호출
'◼ Spring' 카테고리의 다른 글
[Spring] 로그인 어노테이션으로 세션 정보가져오기 (Argument Resolver 활용) (0) | 2023.08.31 |
---|---|
[Spring] Cookie(쿠키)를 활용한 최근 본 상품 기능 구현 (Session과 비교) (0) | 2023.08.30 |
[Spring] GET요청으로 url 쿼리 파라미터에 포함된 JSON 데이터 사용하기 (0) | 2023.08.30 |
@ModelAttribute가 객체에 바인딩하는 과정 및 방법 (0) | 2023.07.29 |
Spring Data JPA + QueryDSL 페이징 처리 (0) | 2023.07.11 |