[Spring] Filter로 로그 활성화

우니·2023년 10월 19일
0

필터로 로깅을 할 경우 알아야할 핵심은 Body를 여러번 읽을 수 없다는 것

HttpServletRequest의 getReader() 또는 getInputStream() 메서드를 호출하여 body를 읽는 경우 다음 단계에서는 body가 비어있는 상태가 된다.

이는 서블릿 구조상 생기는 문제로 서블릿은 요청, 응답 객체를 단 한번만 읽을 수 있도록 설계되어있기 때문.

HttpServletRequest의 최상위 인터페이스를 따라가보면 ServletRequest인데, 드래그한 주석을 읽어보자.

한번 읽히면 IllegalStateException 발생. request의 body를 두번 읽은 순 없다.

이에 관해 해결방법이 두가지 있는데 HttpServletRequestWrapper, ContentCachingRequestWrapper

  • HttpServletRequestWrapper(form 요청에 대해 controller 단에서 데이터가 바인딩 되지 않는 문제가 있음 - 왜그런진 모르겟다) HttpServletRequestWrapper 상속해서 request 객체를 감싼 wrapper 객체를 만든다. 그런 다음getInputStream/getReader를 오버라이딩 하는데, 기존 객체를 사용하는게 아니라 새로운 객체를 반환하도록 오버라이딩한다. → 그러나 이 wrapper를 인터셉트단에서 만들어 쓰기에는 문제가 있다. 왜냐하면 인터셉터와 컨트롤러의 데이터를 바인딩하는 레벨은 같은 DispatcherServlet의 레벨이다. request를 래핑해서 반환해봤자, 컨트롤러에서 사용하지 않는다. DispatcherServlet에 들어오기 전에 wrapping 하고싶다면 filter 단에서
  • ContentCachingRequestWrapper 사실 body 값을 사용하기 위해 HttpServletRequestWrapper를 상속한 클래스를 직접 작성할 필요는 없다. Spring 자체에서 요청의 body를 여러번 읽을 수 있도록 구현한 객체인 ContentCachingRequestWrapper를 사용하면 된다.
    • response도 body를 한번만 읽을 수 있는데, 이렇게 되면 클라이언트에서는 body값을 못꺼내온다.
    • ContentCachingRequestWrapper는 객체 생성과 동시에 바디값을 저장하지만 ContentCacingResponseWrapper는 객체 생성을 하면, 내부적으로 원래 response를 super(response)로 셋팅하기만 함. ContentCachingResponseWrapper의 content라는 필드를 복사해놓는 과정이 필요하다.(copyBodyToResponse())
ContentCachingResponseWrapper httpServletResponse =
                new ContentCachingResponseWrapper((HttpServletResponse) response);

chain.doFilter(httpServletRequest, httpServletResponse);
String resContent = new String(httpServletResponse.getContentAsByteArray());
httpServletResponse.copyBodyToResponse();

0개의 댓글