IT 기술/Spring boot / / 2025. 4. 6. 14:56

Controller에서 Request Body를 읽지 못하는 문제 해결 - Interceptor를 통한 RequestBody 로그 시도인 경우

Controller에서 Request Body를 읽지 못하는 문제 해결

Interceptor를 통한 RequestBody 로깅 시 발생하는 문제와 해결 방법

Spring Boot 프로젝트를 진행하면서 Interceptor를 사용해 요청과 응답을 로깅하는 경우가 많습니다. 특히, API 요청 시 Request Body를 로그에 남기는 것은 디버깅과 모니터링에 큰 도움이 됩니다. 그러나 때로는 Interceptor에서 Request Body를 읽으려다 Controller에서 다시 Request Body를 읽지 못하는 문제가 발생합니다. 이 글에서는 이러한 현상의 원인과 효과적인 해결 방법을 알아보겠습니다.

문제의 원인

Spring MVC에서 Request Body를 읽는 방법인 HttpServletRequestInputStream은 한 번만 읽을 수 있습니다. Interceptor나 Filter에서 이미 InputStream을 읽었다면, Controller에서는 다시 읽을 수 없는 상태가 되어 버립니다. 이로 인해 Controller에서 @RequestBody로 데이터를 바인딩하려고 하면 빈 값이나 에러가 발생합니다.

문제 상황 예시

Interceptor에서 다음과 같은 코드로 Request Body를 로깅했다고 가정해 봅시다.

@Component
public class RequestBodyLoggingInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
        String body = StreamUtils.copyToString(request.getInputStream(), StandardCharsets.UTF_8);
        log.info("Request Body: {}", body);
        return true;
    }
}

위의 코드를 사용하면 InputStream이 비워져, Controller에서 RequestBody 데이터를 읽지 못하게 됩니다.

해결 방법: ContentCachingRequestWrapper 사용

이 문제를 해결하는 가장 효과적인 방법은 Spring에서 제공하는 ContentCachingRequestWrapper를 사용하는 것입니다. 이 클래스는 요청 내용을 캐시하고 여러 번 읽을 수 있도록 도와줍니다.

해결 예시 코드

다음과 같이 Interceptor 대신 Filter를 사용하여 ContentCachingRequestWrapper로 Request를 래핑합니다.

@Component
public class RequestBodyLoggingFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {

        ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper(request);
        filterChain.doFilter(wrappedRequest, response);

        String requestBody = new String(wrappedRequest.getContentAsByteArray(), StandardCharsets.UTF_8);
        log.info("Request Body: {}", requestBody);
    }
}

이렇게 하면 요청이 Controller에 도달하기 전에 이미 읽힌 InputStream이 캐시되어 다시 읽을 수 있는 상태로 전달됩니다.

주의사항

  • Filter의 순서가 중요합니다. ContentCachingRequestWrapper를 적용하는 Filter는 Request Body를 읽는 다른 Filter보다 앞에 위치해야 합니다.
  • 로깅은 성능에 영향을 줄 수 있으므로, 프로덕션 환경에서는 로깅 레벨을 잘 관리하거나 일부 요청에 대해서만 로깅을 수행하도록 설정하는 것이 좋습니다.
  • HttpServletRequestWrapper를 활용한 로깅 방법도 있습니다.

정리

Interceptor에서 Request Body를 로깅할 때 발생하는 문제는 InputStream의 일회성 특성 때문입니다. ContentCachingRequestWrapper를 사용하면 Request Body를 여러 번 읽을 수 있게 하여 문제를 간단히 해결할 수 있습니다. 이 방법을 활용하면 보다 효율적으로 API 요청을 로깅하고 디버깅할 수 있습니다.

  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유