[Spring AOP] 모든 페이지의 공통 헤더 영역에 공통 데이터 Model에 담아 전달

아래와 같이 모든페이지에는 Layout으로 적용한 Header 부분이 공통적으로 들어간다.

그렇기 때문에 모든 페이지에서 해당 알림 데이터 리스트들이 필요하다.

하지만 현재 해당 헤더가 적용된 view를 반환하는 컨트롤러 코드들은 로그인과 회원가입 페이지들을 제외하면 총 9개가 있다.

이 많은 컨트롤러마다 일일히 패키지를 열어서 알림 테이블에서 회원별 알림 내역을 조회한 데이터들을 model에 담아 일일이 View에 뿌려줘야할까?

공통으로 사용되는 부분을 단순 반복 작업을 통해 추가하는건 너무 개발자스럽지 않다. 

 

이 공통으로 사용되는 로직을 스프링 AOP를 적용해 해당 Contoller들의 Model에 알림 데이터들을 추가 해보자.


AOP 적용

우선 먼저 build.gradle에 해당 의존성을 추가해주어야 한다.

implementation 'org.springframework.boot:spring-boot-starter-aop'

 

NotificationsAspect
@Aspect
@Slf4j
@Component
@RequiredArgsConstructor
public class NotificationsAspect {
    private final NotificationsService notificationsService;


    @Around("execution(String com.fruit.mall..*Controller.*(..)) && @annotation(org.springframework.web.bind.annotation.GetMapping)")
    public Object showNotifications(ProceedingJoinPoint joinPoint) throws Throwable {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
        HttpSession session = request.getSession();
        SessionUser sessionUser = (SessionUser) session.getAttribute(LOGIN_USER);

        Object[] args = joinPoint.getArgs();

        for (Object arg : args) {
            if (arg instanceof Model) {
                Model model = (Model) arg;
                if (sessionUser != null){
                    List<NotificationsResDto> notificationsList = notificationsService.selectMessagesByUserId(sessionUser.getUserIdNo());
                    model.addAttribute("notificationsList", notificationsList);
                    log.info("Notifications Aspect 적용 메서드 정보 : {}", joinPoint.getSignature());
                }
            }
        }

        return joinPoint.proceed();
    }
}

 

간단하게 코드에 대해 설명하자면

포인트컷으로 해당 Advice com.fruit.mall 하위 패키지의 이름이 Controller로 끝나는 클래스들의 반환 타입이 String이고 GetMapping 어노테이션이 붙은 메서드들에 적용하였다.

그리고 알림 기능은 로그인 사용자에게만 해당되므로 현재 HTTP 요청에서 세션 정보를 가져와서 세션 유무를 판단하였다.

 

세션이 없으면, 적용 메서드를 실행하고 결과 반환.

세션이 있다면, Object[] args = joinPoint.getArgs()로 대상 메서드의 인자들을 배열로 가져온다.

그 다음 해당 인자의 타입이 Model인지 확인하고, 해당 메서드의 Model에 알림 테이블에서 조회된 회원별 알림 리스트 추가해주고, 적용된 메서드를 실행하도록 했다.

 


Spring AOP에 대해 더 알아보기

아래는 Spring AOP에 대해 각각의 주제별로 정리한 포스팅들이다.

AOP에 대해 더 알아보고 싶다면 아래 포스팅들을 참고하는 것을 추천한다.

  1. 2023.05.17 - [JAVA/Spring] - [Spring] 스프링 AOP 주의사항 - 프록시 내부호출
  2. 2023.05.16 - [JAVA/Spring] - [Spring] 스프링 AOP - Pointcut 표현식
  3. 2023.05.15 - [JAVA/Spring] - [Spring] 스프링 AOP (@Aspect) 사용법
  4. 2023.05.12 - [JAVA/Spring] - [Spring] 스프링 AOP(Aspect Oriented Programming)란? - @Aspect