필터

이영진·2022년 4월 26일
0
post-custom-banner

필터란무엇인가 왜만들어졌을까?

필터는 리소스를 액세스하기전에 클라이언트의 요청을 가로채기위해 클라이언트로 다시보내기전에 서버의 응답을 조작하는것이다

예를들어, 쿠팡에서 로그인한 사용자만 상품을 주문할수있는페이지에 들어갈수있다던지 등이있다.

물론, 이런페이지들을 컨트롤러에서 일일히 로그인여부를 체크하는 로직을 확인할수있겠지만 이러한 공통관심사들을 서블릿의 필터로 해결할수있다.

필터의 흐름

HTTP 요청 -> WAS -> 필터(적절하지 않은요청이면 여기서 처리해낼수있따) -> 서블릿-> 컨트롤러

서블릿을 거치기전, 여러필터를 체이닝할수도있습니다.

@Slf4j
public class LogFilter implements Filter {


    /**
     *  init() : 필터 초기화메서드, 서블릿 컨테이너가 생성될때 호출
     *  doFilter() : 요청이 올때마다 호출, 필터의로직을 구현한다
     *  destory: 필터종료메서드, 서블릿 컨테이너가 종료될때 호출된다.
     */
    
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

        log.info("log filter init");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        log.info("log filter doFilter");

        //ServletRequest 는 HttpServletRequest 의 부모인데 , 기능이 별로 없어서 다운캐스팅 함
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        String requestURI = httpRequest.getRequestURI();

        String uuid = UUID.randomUUID().toString();

        try {
            log.info("REQUEST [{}][{}]", uuid, requestURI);
            chain.doFilter(request,response);   //다음필터있으면 다음필터실행, 없으면 서블릿 실행
        } catch (Exception e) {
            throw e;
        }finally{
            log.info("RESPONSE [{}][{}]", uuid, requestURI);
        }
    }

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

필터는 javax.servlet.Filter 인터페이스를 오버라이딩하여 구현해야합니다.

@Configuration
public class WebConfig {

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

        return filterFilterRegistrationBean;
    }
}

xml로 등록하는방법도있겠지만 , 어노테이션으로 등록하는 방법을 선택했다.

Filter의 빈등록에대해서

그림출처 : https://mangkyu.tistory.com/221

그림을보면 스프링 컨테이너보다 더큰 범위의 서블릿컨테이너의 필터가 스프링 빈으로 등록된다? 이상할수있다. 과거에는 Filter를 Bean으로 등록할수없었지만 DelegatingFilterProxy라는 존재가 등장하면서 가능한일이 되었다.

DelegatingFilterProxy는 필터에도 스프링관련 기술을 필요하는 상황이 발생하면서 생긴대안이며, 우리가만든 필터를 가지고있다가 스프링 컨테이너의 빈으로 등록되는데, 요청이오면 DelegatingFilterProxy가 요청을받아서 우리가만든 필터에게 요청을 위임한다.

스프링부트를 사용한다면 FilterRegisterBean이라는 녀석이 컨테이너에 필터를 등록해줍니다.

스프링 인터셉터는 무엇인가

스프링 MVC가 제공하는 , 서블릿 필터와 같이 공통관심사항을 효과적으로 해결할수있는 기술이다.

HTTP요청 -> WAS -> 필터 -> 서블릿 -> 스프링 인터셉터 -> 컨트롤러 의 흐름으로 실행된다.

서블릿의 필터보다 조금더 편리하고 정교하다는 장점이있다.

prehandle : 핸들러 어댑터 호출전에 호출된다.
posthandle: 핸들러 어댑터 호출된다. 만약 컨트롤러에서 예외가 발생한다면 posthandle은 호출되지않는다.
afterCompletion: 뷰가 렌더링된 이후에 호출된다. 예외가 발생해도 항상호출되며, 어떤 예외가발생했는지 로그로 출력하는등 이용할수있다.

public class LogInterceptor implements HandlerInterceptor {


    private static final String LOG_ID = "logId";

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String requestURI = request.getRequestURI();
        String uuid = UUID.randomUUID().toString();
        
        request.setAttribute(LOG_ID, uuid);

        if (handler instanceof HandlerMethod) {
            HandlerMethod hm = (HandlerMethod) handler;
        }
        
        return true;
    }

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

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

HandlerInterceptor 인터페이스를 implements하여 등록하고, 필터와 마찬가지로 Bean으로 등록해서 사용한다.

@Configuration
public class WebConfig implements WebMvcConfigurer {
 @Override
public void addInterceptors(InterceptorRegistry registry) {
 	registry.addInterceptor(new LogInterceptor())
 		.order(1)
 		.addPathPatterns("/**")
	 	.excludePathPatterns("/css/**", "/*.ico", "/error");
 }
}

스프링 MVC빈 설정

dispatcherservlet.properties
우리가 별다른설정을 하지않는한 여기에 정의되어있는 기본전략을 사용하게된다.
우리는ViewResolver ,HandlerMapping,HandlerAdapter 등을 빈으로 등록하여 커스텀할수도있다. (가장 로우레벨, 스프링MVC가 제공하는 더 좋은방법이있다.)
기본설정만으로 한계에 부딪힐때 기능을 커스텀하여 사용할수있다.

@EnableWebMvc

편리하게 애노테이션기반 스프링MVC를사용할때 설정하기위한 애노테이션
SpringFramework 에서 여러 COnfig값을 알아서 세팅해준다.

WebMvcConfigurer 인터페이스

@EnableWebMvc가 제공하는 빈을 커스터마이징할 수 있는 기능을 제공하는 인터페이스 formatter를 추가하기도하고, ViewResolvers 의 규칙을 등록하기도하고, 컨버터를 추가하기도하고, 인터셉터를 추가하기도한다.

boot에서는 MVC설정을 어떻게 하느냐

application.properties: 가장손을안대고 커스텀할수있는방법

@Configuration + Implements WebMvcConfigurer: 스프링 부트의 스프링 MVC 자동설정 + 추가 설정 하려는 설정

@Configuration + @EnableWebMvc + Implements WebMvcConfigurer: 스프링 부트의 스프링 MVC 자동설정 사용하지 않는설정.

post-custom-banner

0개의 댓글