getReader() has already been called for this request
2026-01-07T08:09:10.277+09:00 ERROR 5032 --- [nio-8080-exec-3] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: java.lang.IllegalStateException: getReader() has already been called for this request] with root cause
java.lang.IllegalStateException: getReader() has already been called for this request
LoggingFilter ์์ request body๋ฅผ ์ฝ์ ํ Spring ์ด ๋ค์ ์ฝ์ผ๋ ค๊ณ ํด์ ๋ฐ์ํ๋ ์ค๋ฅ
@Slf4j
@Component
public class LoggingFilter implements Filter{
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
if(request instanceof HttpServletRequest httpServletRequest){
String url = httpServletRequest.getRequestURI();
String method = httpServletRequest.getMethod();
String body = getRequestBody(httpServletRequest);
log.info("Incoming Request: URL = {}, Method = {}, Body = {}", url, method, body);
}
chain.doFilter(request, response); // ๋ค์ ํํฐ ๋๋ ์ปจํธ๋กค๋ฌ๋ก ์์ฒญ ์ ๋ฌ
}
private String getRequestBody(HttpServletRequest request){
try (BufferedReader reader = request.getReader()) {
return reader.lines().collect(Collectors.joining(System.lineSeparator()));
} catch (IOException e){
log.error("Failed to read request body", e);
return "Unable to read request body";
}
}
}
HttpServletRequest์ InputStream/Reader๋ ํ ๋ฒ๋ง ์ฝ์ ์ ์๋ค.
1. LoggingFilter์์ request.getReader()๋ก body๋ฅผ ์ฝ์
2. chain.doFilter()๋ก ์์ฒญ ์ ๋ฌ
3. Spring์ด @RequestBody๋ฅผ ์ฒ๋ฆฌํ๊ธฐ ์ํด ๋ค์ body๋ฅผ ์ฝ์ผ๋ คํจ
4. ์ด๋ฏธ ์ฝ์ stream์ ๋ค์ ์ฝ์ผ๋ ค๊ณ ํด์ IllegalStateException ๋ฐ์
ContentCachingRequestWrapper ๋๋ ์ปค์คํ Wrapper๋ฅผ ์ฌ์ฉํด์ body๋ฅผ ์บ์ฑ
@Slf4j
@Component
public class LoggingFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
if (request instanceof HttpServletRequest httpServletRequest) {
ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper(httpServletRequest);
chain.doFilter(wrappedRequest, response);
String url = wrappedRequest.getRequestURI();
String method = wrappedRequest.getMethod();
String body = new String(wrappedRequest.getContentAsByteArray(), StandardCharsets.UTF_8);
log.info("Request: URL = {}, Method = {}, Body = {}", url, method, body);
} else {
chain.doFilter(request, response);
}
}
}
1. ContentCachingRequestWrapper: Spring์ด ์ ๊ณตํ๋ ๋ํผ ํด๋์ค๋ก, request body๋ฅผ ๋ด๋ถ์ ์บ์ฑ
2. ๋ก๊น
์์ ๋ณ๊ฒฝ: chain.doFilter() ํ์ ๋ก๊น
ํด์ผ getContentAsByteArray()์ ๋ฐ์ดํฐ๊ฐ ์ฑ์์ง
3. ๋ํ๋ request ์ ๋ฌ: chain.doFilter(wrappedRequest, response)๋ก ๋ํ๋ ๊ฐ์ฒด ์ ๋ฌ