이야기박스

Spring - Filter 본문

Programming Language/Spring

Spring - Filter

박스님 2019. 8. 13. 20:04
반응형

# 개요

Spring으로 구성된 프로젝트에서 생긴 이슈를 처리하면서 공부한 내용을 정리하려고 합니다.

HTTP Request를 받아 처리하는 작업을 하던 도중, InputStream을 못 읽는 문제가 발생하였습니다. 원인을 찾아보니 Spring Filter 때문이란 걸 알게 되었습니다. 

Filter의 내용을 간략히 정리하며 비슷한 기능을 가지는 Interceptor도 가볍게 집고 넘어가려 합니다.

 

# Spring Architecture

https://justforchangesake.wordpress.com/2014/05/07/spring-mvc-request-life-cycle/

## Filter

위 사진은 스프링에서 Request가 들어오는 경우의 아키텍처입니다. 사진과 같이 Filter는 DispatcherServlet 보다 앞단에서 Request를 받아들이는 역할을 합니다. 

Web Application에 등록이 이루어집니다. 때문에 예외가 발생하면 Web Application에서 처리를 진행해야 합니다.

## Interceptor

Spring에서 지원하며, DispatcherServlet에서 Controller로 가기 전의 정보를 처리하는 구간입니다.

Spring Context에 등록이 이루어집니다. 때문에 예외가 발생한다면 Spring내에서 처리를 진행할 수 있습니다.

 

# InputStream 이슈

제가 다루었던 프로젝트에서는 HTTP POST 방식으로 전달된 데이터를 읽어 들이는 작업을 하였습니다. Spring에서는 이러한 데이터의 처리를 하려면 HttpServletRequest의 InputStream을 읽어 들여야 하는데 여기서 이슈가 발생하였습니다.

  • application/x-www-form-urlencoded 헤더로 들어온 데이터는 문제 발생
  • application/octet-stream 헤더로 들어온 데이터는 문제 미 발생

원인은 다음과 같습니다.

톰캣 개발자들은 HttpServletRequest의 InputStream을 한 번 읽으면 다시 읽을 수 없게 설계를 해두었다.

그렇기 때문에 Filter나 Interceptor에서 ServletRequest를 한 번 읽게 되면, 이후 Spring에서는 이 데이터를 읽을 수 없게 됩니다.

저의 경우에는 Spring Boot에서 Default로 제공되는 Filter에서 InputStream을 읽고 있었습니다. application/octet-stream의 경우 바이너리로 구성되어 있어 Filter에서 읽히지 않고 무사히 통과했지만, x-www-form-urlencoded의 경우는 Filter에서 모두 InputStream이 읽혀 문제가 발생했습니다.

그렇기 때문에 위 문제를 Custom Filter 구성을 통하여 해결하였습니다.

## Custom Filter 구성

간략한 해결방법은 다음과 같습니다.

Custom Filter Class 구성 --> InputStream을 읽은 후, Attribute로 저장 --> Controller에서 Attribute를 읽음

자세한 구성은 아래 내용들을 참조하시면 됩니다.

우선 javax.servlet.Filter를 상속받아 커스텀 Filter를 구성 후, Component로 등록합니다. 이후, 가장 먼저 동작하도록 우선순위를 높여 두었습니다.

@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class CustomFilter implements Filter {
}

이후, doFilter() 메서드에서 InputStream을 읽은 후, Attribute에 넣어주는 작업을 진행합니다.

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpReq = (HttpServletRequest) request;
  
        String body = Reader.readInputStream(httpReq.getInputStream());
        request.setAttribute("body", body);

        chain.doFilter(request, response);
}

마지막으로 Controller에서 위 'body' Attribute를 읽어오면 됩니다.

 

# 마무리

Spring Servlet Lifecycle에 대해 공부할 좋은 기회였던 것 같습니다. 공유하기 위해 내용 정리하여 올립니다.

 

# Reference

https://meetup.toast.com/posts/44

 

Spring Interceptor(혹은 Servlet Filter)에서 POST 방식으로 전달된 JSON 데이터 처리하기 : TOAST Meetup

Spring Interceptor(혹은 Servlet Filter)에서 POST 방식으로 전달된 JSON 데이터 처리하기

meetup.toast.com

https://justforchangesake.wordpress.com/2014/05/07/spring-mvc-request-life-cycle/

 

Spring MVC Request Life Cycle

Let’s see how Spring MVC handles a request. Figure 17-3 shows the main components involved in handling a request in Spring MVC. The figure is based on the one described in the Spring Framework foru…

justforchangesake.wordpress.com

반응형