2020-7-6 스프링 MVC(3) 인터셉터

인터셉터

핸들러인터셉터 => 핸들러인터셉터와 AOP의 차이는??

  • 핸들러 맵핑에 설정할 수 있는 인터셉터.

  • 핸들러를 실행하기 전, 후(렌더링 전), 완료(렌더링이 끝난이후)시점에 부가작업을 하고 싶은 경우에 사용할 수 있다.

    • preHandler => 요청처리 => postHandler => 뷰 랜더링 => afterCompletation
  • 여러 핸들러에서 반복적으로 사용한느 코드를 줄이고 싶을때 쓸 수 있다.(로깅, 인증 체크, Locale…)

  • boolean preHandle(request, response, handler)

    • 핸들러 실행하기 전에 호출
    • 핸들러에 대한 정보가 제공되기때문에 서블릿 필터에 비해 로직을 더 잘 구현할 수 있다.
    • 리턴값으로 계속 다음 인터셉터 또는 핸들러로 요청, 응답을 전달할지(true), 이곳에서 처리가 끝났는지(false) 알린다.
  • void postHandle(request, response, modelAndView)

    • 핸들러 실행이 끝나고 아직 뷰를 렌더링하기 이전에 호출
    • 뷰에 전달할 추가적이거나 여러 핸들러에 공통적인 모델 정보를 담는데 사용
    • 인터셉터 역순으로 호출 => 인터셉터가 여러개일때.
      • preHandler1 => preHandler2 => 요청처리 => postHandle2 => postHandle1 => 뷰 => afterCompletion2 => afterCompletion1
    • 비동기적인 요청 처리 시에는 호출되지 않는다.
  • void afterCompletion(request, response, handler, ex)

    • 요청 처리가 완전 끝난 뒤(뷰 렌더링 끝난뒤)에 호출됨
    • preHandler에서 true를 리턴한 경우에만 호출된다.
    • 인터셉터 역순으로 호출
    • 비동기적인 요청에는 호출 안된다.
  • 서블릿필터와 어떤 차이가 있는가?

    • 서블릿보다 구체적인 처리가 가능하다.
    • 서블릿은 보다 일반적인 용도의 기능을 구현하는데 사용
    • XSS를 방지하기 위해서는 서블릿필터를 써라. 인터셉터랑은 전혀관계없다.
  • 인터셉터를 구현하기 위해서는 HandlerInterceptor를 구현해라. 사용하기 위해서는 WebConfigurer를 구현한 클래스에서 addInterceptor()를 구현해라

    • @Configuration
      public class WebConfig implements WebMvcConfigurer {
          @Override
          public void addInterceptors(InterceptorRegistry registry) {
              registry.addInterceptor(new GreetingInterceptor()); // .order(value)를 통해 순서를 정할 수 있다. order는 음수일수록 순위가 높다.
              registry.addInterceptor(new AnotherInterceptor());
          }
      }
      
      public class AnotherInterceptor implements HandlerInterceptor {
          @Override
          public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
              System.out.println("preHandle2");
              return true;
          }
      
          @Override
          public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
              System.out.println("postHandle 2");
          }
      
          @Override
          public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
              System.out.println("afterCompletion2");
          }
      }
      
      public class GreetingInterceptor implements HandlerInterceptor {
          @Override
          public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
              System.out.println("preHandle1");
              return true;
          }
      
          @Override
          public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
              System.out.println("postHandle 1");
          }
      
          @Override
          public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
              System.out.println("afterCompletion1");
          }
      }
      
      //result
      preHandle1
      preHandle2
      postHandle 2
      postHandle 1
      afterCompletion2
      afterCompletion1
      
      // order적용
      registry.addInterceptor(new GreetingInterceptor()).order(-1);
      registry.addInterceptor(new AnotherInterceptor()).order(-2);
      
      preHandle2
      preHandle1
      postHandle 1
      postHandle 2
      afterCompletion1
      afterCompletion2
      
      // 패턴적용
      registry.addInterceptor(new AnotherInterceptor())
                      .addPathPatterns("/hi")
                      .order(-2);
      preHandle1
      postHandle 1
      afterCompletion1
      
    • addPathPattern을 통해 특정패턴에만 적용시킬 수 있다.

리소스핸들러

  • 이미지, JS, CSS, HTML을 처리하는 핸들러 등록하는 방법

  • 디폴트 서플릿

    • 서블릿 컨테이너가 기본으로 제공하는 서블릿으로 정적인 리소스를 처리할 때 사용
  • 스프링MVC 리소스 핸들러 매핑등록

    • 가장낮은 우선순위로 등록.
    • 다른 핸들러매핑이 “/”이하의 요청을 처리하도록 허용하고 최종적으로 리소스핸들러가 처리
  • 설정

    • 어떤 요청 패턴을 지원할것인가
    • 어디서 리소스를 찾을까
    • 캐싱
    • ResourceResolver: 요청에 해당하는 리소스를 찾는 전략 => 캐싱, 인코딩(gzip, brotli), Webber
    • ResourceTransformer : 응답으로 보낼 리소스를 수정하는 전략 => 캐싱, CSS링크, HTML5
  • 스프링부트 : 기본 정적 리소스 핸들러와 캐싱제공(resources디렉토리의 하위에 있는 static, public이라는 디렉토리를 지원)

    • @Test
      public void helloStatic() throws Exception {
        this.mockMvc.perform(get("/index.html"))
          .andDo(print())
          .andExpect(status().isOk())
          .andExpect(content().string(Matchers.containsString("hello index")));
      }
      
  • 스프링부트가 기본으로 제공하는것이 아닌 임의로 리소스 핸들러를 등록하고 싶으면 WebConfigurer를 구현한 인터페이스에서 addResourceHandlers를 구현해라

    • @Configuration
      public class WebConfig implements WebMvcConfigurer {
        	....
          @Override
          public void addResourceHandlers(ResourceHandlerRegistry registry) {
              registry.addResourceHandler("/mobile/**")
                      .addResourceLocations("classpath:/mobile/")
                      .setCacheControl(CacheControl.maxAge(10, TimeUnit.MINUTES));
          }
      }
      
      @Test
      public void helloStatic() throws Exception {
        this.mockMvc.perform(get("/mobile/index.html"))
          .andDo(print())
          .andExpect(status().isOk())
          .andExpect(content().string(Matchers.containsString("hello mobile")))
          .andExpect(header().exists(HttpHeaders.CACHE_CONTROL));
      }
      
    • 캐시에 관련된 헤더가 응답헤더에 함께 추가된다. 리소스가 변경이 안되면 10분동안 유지. 10분 이내로 동일한 요청을 보내게 되면 304응답과 함께 캐시된 결과를 준다. 10분 안에 리소스가 변경되면 변경된 리소스를 리턴

    • addReourcehandlers에 있는 addResourceChain(boolean cacheResource) 운영중에는 캐시를 사용(true) 개발중에는 false를 주로 쓴다.
Written on July 6, 2020