RequestWrapper 활용
  • Request로 들어온 데이터는 Filter, Interceptor에서 사용하게 되면,
    그 이후 사용이 불가능하므로 Caching 할 수 있는 Wrapper를 새로 만든다.

소스 코드

MultiReadRequestWrapper

java
public class MultiReadRequestWrapper extends HttpServletRequestWrapper { private byte[] cachedBody; public MultiReadRequestWrapper(HttpServletRequest request) throws IOException { super(request); // 생성 시점에 바디를 미리 읽어서 보관합니다. InputStream is = request.getInputStream(); this.cachedBody = StreamUtils.copyToByteArray(is); } @Override public ServletInputStream getInputStream() { // 호출될 때마다 보관된 바이트 배열로 새 스트림을 만듭니다. ByteArrayInputStream bais = new ByteArrayInputStream(this.cachedBody); return new ServletInputStream() { @Override public int read() { return bais.read(); } @Override public boolean isFinished() { return bais.available() == 0; } @Override public boolean isReady() { return true; } @Override public void setReadListener(ReadListener readListener) { } }; } @Override public BufferedReader getReader() { return new BufferedReader(new InputStreamReader(this.getInputStream())); } }

MultiReadResponseWrapper

java
public class MultiReadResponseWrapper extends HttpServletResponseWrapper { private final ByteArrayOutputStream capture = new ByteArrayOutputStream(); private ServletOutputStream output; private PrintWriter writer; public MultiReadResponseWrapper(HttpServletResponse response) { super(response); } @Override public ServletOutputStream getOutputStream() { if (writer != null) throw new IllegalStateException("Writer already in use"); if (output == null) { output = new ServletOutputStream() { @Override public void write(int b) { capture.write(b); } @Override public void setWriteListener(WriteListener writeListener) {} @Override public boolean isReady() { return true; } }; } return output; } @Override public PrintWriter getWriter() { if (output != null) throw new IllegalStateException("Stream already in use"); if (writer == null) { writer = new PrintWriter(new OutputStreamWriter(capture, StandardCharsets.UTF_8)); } return writer; } @Override public void flushBuffer() throws IOException { if (writer != null) writer.flush(); if (output != null) output.flush(); } // 캐싱된 응답 바디를 꺼내보는 메서드 public byte[] getContentAsByteArray() { return capture.toByteArray(); } // 최종적으로 클라이언트에게 데이터를 전송하는 메서드 (필수!) public void copyBodyToResponse() throws IOException { flushBuffer(); byte[] content = capture.toByteArray(); ServletOutputStream out = getResponse().getOutputStream(); out.write(content); out.flush(); } }

사용법

  • Filter에서 사용 예시

    java
    @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { MultiReadRequestWrapper requestWrapper = new MultiReadRequestWrapper(request); MultiReadResponseWrapper responseWrapper = new MultiReadResponseWrapper(response); try { filterChain.doFilter(requestWrapper, responseWrapper); } finally { // 1. 여기서 응답 바디를 마음껏 읽을 수 있습니다. byte[] responseBody = responseWrapper.getContentAsByteArray(); logResponse(new String(responseBody, StandardCharsets.UTF_8)); // 2. 매우 중요: 캐싱된 데이터를 실제 클라이언트 응답 스트림에 밀어 넣어줍니다. responseWrapper.copyBodyToResponse(); } }