스프링 부트 - HandlerInterceptor

Spring

Posted by kwon on 2020-06-19

스프링 부트 - HandlerInterceptor

  • Interceptor는 말 그대로 가로채는 것을 의미한다. 예를 들어, 세션 검증이나 로그 처리 같은 기능을 수행하기 위해 특정 url에 요청이 들어오면 그 요청을 가로채 특정한 작업을 수행하는 기능이다.

  • 현재 진행중인 프로젝트에 알림 기능이 있는데, 알림이 존재하는 경우에 알림이 있다는 표시를 해주기 위해 view에 hasNotification이라는 모델을 넘겨주고자 한다. 이 작업을 모든 요청에 적용하기 위해서 Interceptor를 적용할 것이다.

  • Interceptor는 HandlerInterceptor인터페이스를 구현하여 만들 수 있다.

  • HandlerInterceptor의 메서드는 아래의 세 가지가 있다.

    • boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
      • Controller실행 직전에 동작을 하며, 반환 값에 따라 진행 여부를 결정한다.
    • void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
      • Conroller진입 후 view가 렌더링 되기 전에 수행을 한다.
      • modelAndView를 사용하여 model에 데이터를 추가하는 조작이 가능하다.
    • void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
      • Conroller진입 후 view가 렌더링 된 이후에 마지막으로 실행이 되는 메서드이다.
  • 나는 핸들러 처리 이후 뷰가 렌더링 되기 전에 알림 여부를 가지는 모델을 추가해주기 위해 postHandle메서드를 구현하였다.

  • NotificationInterceptor.java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    @Component
    @RequiredArgsConstructor
    public class NotificationInterceptor implements HandlerInterceptor {

    private final NotificationRepository notificationRepository;

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    // 뷰 렌더링 전, 핸들러 처리 이후
    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    if (modelAndView != null && !isRedirectView(modelAndView) && authentication != null && authentication.getPrincipal() instanceof UserAccount) {
    Account account = ((UserAccount)authentication.getPrincipal()).getAccount();
    long count = notificationRepository.countByAccount(account);
    modelAndView.addObject("hasNotification", count > 0); // 알람이 있는 경우 true, 없으면 false
    }
    }

    private boolean isRedirectView(ModelAndView modelAndView) { // 리다이렉트 요청인지 확인하는 메서드
    // 뷰의 이름이 redirect: 로 시작하거나, RedirectView 타입인 경우
    return modelAndView.getViewName().startsWith("redirect:") || modelAndView.getView() instanceof RedirectView;
    }
    }
  • 로그인된 사용자에게 적용하고 리다이렉트 요청에는 적용하지 않기 위해

  • 인증 객체를 가져와 modelAndView 를 사용하고, RedirectView 가 아닌 경우, 인증 정보가 있고 principalUserAccount 타입일 때 적용한다.

  • principal 안에 들어있는 객체를 UserAccount 로 형변환하여 account 객체를 가져와 해당 사용자의 알림 개수를 구한다.

  • 알림이 있는 경우 true, 없는 경우 false를 모델에 넣어준다.

HandlerInterceptor 적용하기

  • 이제 작성한 HandlerInterceptor를 실제로 적용하기 위한 설정을 해주어야 한다.

  • WebMvcConfigurer를 구현한 클래스를 작성하여 addInterceptors메서드에 해당 인터셉터를 등록하면 된다.

  • WebConfig.java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    @Configuration
    //@EnableWebMvc : 이 설정을 주면 스프링 부트가 제공하는 웹mvc 자동설정을 사용하지 않는다는 뜻.
    // 지금은 추가설정만 하는 것
    @RequiredArgsConstructor
    public class WebConfig implements WebMvcConfigurer {

    private final NotificationInterceptor notificationInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {

    registry.addInterceptor(notificationInterceptor)
    .excludePathPatterns('url패턴 추가'); // 인터셉터를 적용하지 않을 url 패턴
    }
    }
  • excludePathPatterns()에 추가한 url패턴에는 인터셉터를 적용하지 않도록 설정할 수 있다.

    • 예를들어, /css/**, /js/**등과 같은 static 리소스들에 적용하고 싶지 않은 경우에 추가할 수 있다.
    • String 배열이나 리스트 타입으로 넣어줄 수 있다.

참조
https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-JPA-%EC%9B%B9%EC%95%B1/dashboard
https://elfinlas.github.io/2017/12/28/SpringBootInterceptor/