이야기박스
Spring - Filter 본문
# 개요
Spring으로 구성된 프로젝트에서 생긴 이슈를 처리하면서 공부한 내용을 정리하려고 합니다.
HTTP Request를 받아 처리하는 작업을 하던 도중, InputStream을 못 읽는 문제가 발생하였습니다. 원인을 찾아보니 Spring Filter 때문이란 걸 알게 되었습니다.
Filter의 내용을 간략히 정리하며 비슷한 기능을 가지는 Interceptor도 가볍게 집고 넘어가려 합니다.
# Spring Architecture
## 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
https://justforchangesake.wordpress.com/2014/05/07/spring-mvc-request-life-cycle/
'Programming Language > Spring' 카테고리의 다른 글
(작성중) Spring Test & JUNIT (0) | 2019.11.15 |
---|---|
Spring. SpringJDBC - ibatis 환경에서 Transaction 진행 (1) | 2019.10.18 |
Spring JPA. Batch Insert 에러 (0) | 2019.03.28 |
Chapter 8. 스프링이란 무엇인가? (0) | 2019.03.22 |
Chapter 7. 스프링 핵심 기술의 응용 (0) | 2019.03.21 |