스프링에서는 Request를 핸들링하기 전 필터링을 거친다.
입력 혹은 출력에 대한 예외나 오류를 확인하는 경우로
클라이언트가 잘못 입력하거나 서버의 로직이 잘못된 경우와 같은 상황의 로그를 확인하기 위함이다.
@PostMapping("")
public UserRequest register(
@RequestBody
UserRequest userRequest
){
log.info("{}", userRequest);
return userRequest;
}
body를 가져오는 POST 요청이 있다고 해보자.
@JsonNaming(value = PropertyNamingStrategies.SnakeCaseStrategy.class)
public class UserRequest {
private String name;
private String phoneNumber;
private String email;
private Integer age;
}
네 가지의 필드 중 몇 가지를 잘못 입력했을 때 우리는 null로 서버에 입력된다는 것을 알고 있다.
그래서 Filter라는 interface를 구현할 수 있다.

이름을 Camel Case로, 전화번호를 잘못 입력했다고 가정해보자.
@Component
public class LoggerFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
var req = new HttpServletRequestWrapper((HttpServletRequest) servletRequest);
var res = new HttpServletResponseWrapper((HttpServletResponse) servletResponse);
var br = req.getReader();
var list = br.lines().collect(Collectors.toList());
list.forEach(it ->{
log.info("{}", it);
});
filterChain.doFilter(req, res);
}
}
doFilter로 들어오는 요청과 리턴되는 응답에 필터링을 할 수 있다.
요청과 응답을 HttpServlet으로 형변환하고 getReader를 통해 body를 모두 읽어보면

꽤 성공적으로 요청을 읽어왔지만
getReader() has already been called for this request 라는 에러를 발생시켰다.
이유는 getReader를 통해 body를 받을 때 inputStream을 Filter에서 읽었기 때문에 Controller에서 body를 읽지 못하는 문제가 발생한 것이다.
var req = new ContentCachingRequestWrapper((HttpServletRequest) servletRequest);
var res = new ContentCachingResponseWrapper((HttpServletResponse) servletResponse);
Request와 Response를 ContentCachingWrapper 객체로 감싸주면 된다.
이름에 나와있듯이 body를 캐싱해서 가지고 있을 수 있다.
InputStream으로 읽히는 요청을 log에 찍어보기 위해 문자열로 바꿔서 찍는다.
var reqJson = new String(req.getContentAsByteArray());
log.info("req {}", reqJson);
var resJson = new String(res.getContentAsByteArray());
log.info("res {}", resJson);
content를 byte 배열로 읽겠다는 뜻일 것이다.
getReader로 읽지 않고 다른 방식을 취했으니 body에 전달되고 에러도 뜨지 않았을까 ?

무사히 에러는 뜨지 않았다 !

하지만 body에는 전달되지 않았다.
getReader와 같은 이유로 어찌됐든 get으로 읽었기 때문에 body에는 전달되지 않은 것이다.
그래서 마지막 response를 body에 덮어씌우는 작업이 필요하다.
response는 내려갔지만 비어있는 body가 전달이된 것이기 때문에
res.copyBodyToResponse();
덮어씌워준다면

에러도 없고 response도 잘 내려간 모습을 볼 수 있다.