티스토리 뷰

필터와 인터셉터

필터 는 서블릿에서 제공하는 기능이며, 인터셉터 는 스프링에서 제공하는 기능이다.

필터란?

웹 애플리케이션을 구성하다보면 공통적으로 처리하는 로직이 존재하게 된다.

예를들어, 어떠한 페이지는 로그인 에 성공하는 유저만 접근할 수 있다고 하자.
그럼 해당 페이지의 컨트롤러마다 로그인 에 성공했는지 검증하는 코드가 필요하게 된다.
이렇게 여러 로직에서 공통으로 관심이 있는 것을 공통 관심사 라고 한다.

이러한 공통 관심사를 해결하는 방법에는 스프링의 AOP를 사용하는 방법도 있지만 웹과 관련된 공통 관심사는 서블릿 필터스프링 인터셉터 를 사용하는 것이 더 유리한 경우가 있다.

필터의 흐름은 다음과 같다.

HTTP 요청 -> 서블릿 컨테이너 -> 필터 -> 서블릿 -> 컨트롤러

필터를 적용하면 필터가 적용 된 후에 서블릿이 호출된다.
만약 모든 HTTP요청을 로깅해야 한다면 필터를 사용하면 된다.

필터는 체인으로 구성되어 있어 필터를 자유롭게 추가하여 여러가지의 필터를 함께 적용할 수 있다.

필터 사용

Filter 라는 인터페이스를 구현하면 다음과 같은 메소드를 작성할 수 있다.

init(): 필터 초기화 메서드
doFilter(): 필터를 수행하는 메소드
destroy(): 필터가 종료되는 메소드로 서블릿 컨테이너가 종료될때 호출된다.

서블릿 필터를 통해 모든 요청에 대한 로그를 남기는 코드를 작성해보자.

@Slf4j
public class LogFilter implements Filter {

  @Override
  public void init(FilterConfig filterConfig) throws ServletException {
    log.info("init");
  }

  @Override
  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
      throws IOException, ServletException {

    HttpServletRequest httpServletRequest = (HttpServletRequest) request;
    log.info("[IN] Request URI = {}", httpServletRequest.getRequestURI());

    try{
      chain.doFilter(request, response);
    } catch (Exception e) {
      throw e;
    } finally {
      log.info("[OUT]Request URI = {}", httpServletRequest.getRequestURI());
    }
  }

  @Override
  public void destroy() {
    log.info("destroy");
  }
}

위 코드는 모든 요청에 대해 Request URI를 로그로 남기는 필터이다.

doFilter 함수 내에서 필터를 수행할 로직을 작성하면 된다.
그리고 이후에는 만드시 chain.dofilter() 함수를 통해 다음 필터를 호출해야 한다.
다음 필터를 호출하지 않으면 제대로 동작하지 않는다. 체인을 따라 필터를 계속 호출하다 체인의 끝에 다다르면 그때 서블릿을 호출하게 된다.

try-finally 로 감싸준 부분은 HTTP요청이 모두 처리되고, 다시 사용자에게 응답이 가는 시점에 로그를 찍기 위함이다.

이렇게 필터를 생성했으면 이 필터를 스프링에서 인식할 수 있도록 등록해줘야 한다.

스프링부트를 사용한다면 다음과 같이 필터를 작성할 수 있다.

@Configuration
public class WebConfig {
    @Bean
     public FilterRegistrationBean logFilter() {
        FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>();
        filterRegistrationBean.setFilter(new LogFilter());
        filterRegistrationBean.setOrder(1);
        filterRegistrationBean.addUrlPatterns("/*");

        return filterRegistrationBean;
     }
}

필터를 사용한 인증처리

필터를 사용해서 로그인 이 되어있는 유저만 서블릿에 요청할 수 있도록 해보자.
이 점을 응용하면 인증안된 유저는 특정 서블릿or컨트롤러의 접근을 완전 차단할 수 있다.

@Slf4j
public class LoginFilter implements Filter {

  @Override
  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
      throws IOException, ServletException {

    HttpServletRequest httpServletRequest = (HttpServletRequest) request;
    try {
      log.info("[IN] Request URI = {}", httpServletRequest.getRequestURI());

      HttpSession session = httpServletRequest.getSession();
      if (session == null || session.getAttribute("login") == null) {
        //로그인 실패 시 로직
        return;
      }

      //로그인에 성공되었으므로 이후 로직 수행
      chain.doFilter(request, response);
    } catch (Exception e) {
      throw e;
    } finally {
      log.info("[OUT]Request URI = {}", httpServletRequest.getRequestURI());
    }
  }
}

이처럼 doFilter 함수 내에서 session정보를 가져오고 세션이 있는지 확인하여 세션이 존재할 경우에만 다음 로직을 수행하도록 한다.

이렇게하면 로그인 인증이 되지 않은 유저의 요청은 서블릿 까지도 못하고 차단되게 되는 것이다.

필터에는 인터셉터에서 제공하지 않는 강력한 기능이 있다.
바로 chain.dofilter(request, response) 를 호출할 때 request와 response를 바꿀 수 있는 점이다.

인터셉터란?

앞서 설명한 필터 는 서블릿에서 지원하는 기능이였다. 반면 인터셉터 는 스프링에서 제공하는 기능이다.

둘다 웹과 관련된 공통관심사를 처리하지만 적용되는 순서나 범위, 그리고 사용방법은 다르다.

인터셉터필터보다 더 많은 기능을 제공한다.

스프링인터셉터의 흐름은 다음과 같다.

HTTP 요청 -> 서블릿 컨테이너 -> 필터 -> 서블릿 -> 인터셉터 -> 컨트롤러

스프링 인터셉터는 스프링MVC가 제공하는 기능이므로 디스패처 서블릿이 실행된 이후에 컨트롤러를 실행하기 전 호출된다.

또한 스프링 인터셉터도 체인으로 구성할 수 있다. 이렇게 보면 필터 와 다른 점을 찾기 힘들지만 인터셉터는 필터보다 더 편리하고 정교하고 다양한 기능을 제공한다.

스프링 인터셉터 사용하기

스프링 인터셉터를 사용하려면 HandlerInterceptor 인터페이스를 구현하면 된다.

public class LoginInterceptor implements HandlerInterceptor {

  @Override
  public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
      throws Exception {
    return HandlerInterceptor.super.preHandle(request, response, handler);
  }

  @Override
  public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
      ModelAndView modelAndView) throws Exception {
    HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
  }

  @Override
  public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
      Object handler, Exception ex) throws Exception {
    HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
  }
}

이렇게 preHandlepostHandle , afterCompletion 가 있으며 받는 파라미터도 필터에 비해 더 다양한 것을 알 수 있다.

preHandle : 컨트롤러 호출전에 실행된다
postHandle : 컨트롤러 호출 후에 실행된다. (컨트롤러에서 예외 발생 시 호출되지 않는다.)
afterCompletion : 뷰가 렌더링 된 이후에 호출된다. (예외가 발생해도 예외정보와 함께 호출된다.)

인터셉터를 등록할때도 필터와 마찬가지라WebMvcConfigurer 인터페이스를 구현한 WebConfiguration

@Override
public void addInterceptors(InterceptorRegistry registry) {
  registry.addInterceptor(new LoginInterceptor())
      .order(1)
      .addPathPatterns("/**");
}

다음과 같은 코드로 등록하면 된다.

인터셉터는 필터보다 더 정밀한 경로 지정이 가능하다. 인터셉터를 등록하는 시점에 addPathPatternsexcludePathPatterns 을 통해 허용/금지 경로를 지정할 수 있다.

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/10   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
글 보관함