Controller에서 Request Body를 읽지 못하는 문제 해결
Interceptor를 통한 RequestBody 로깅 시 발생하는 문제와 해결 방법
Spring Boot 프로젝트를 진행하면서 Interceptor를 사용해 요청과 응답을 로깅하는 경우가 많습니다. 특히, API 요청 시 Request Body를 로그에 남기는 것은 디버깅과 모니터링에 큰 도움이 됩니다. 그러나 때로는 Interceptor에서 Request Body를 읽으려다 Controller에서 다시 Request Body를 읽지 못하는 문제가 발생합니다. 이 글에서는 이러한 현상의 원인과 효과적인 해결 방법을 알아보겠습니다.
문제의 원인
Spring MVC에서 Request Body를 읽는 방법인 HttpServletRequest
의 InputStream
은 한 번만 읽을 수 있습니다. 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 요청을 로깅하고 디버깅할 수 있습니다.
'IT 기술 > Spring boot' 카테고리의 다른 글
Spring vs Spring Boot: 차이점과 선택 기준 (1) | 2025.03.18 |
---|---|
Spring Boot에서 Security 적용 시 H2 Console(/h2-console) 접근 불가 문제 해결 방법 (0) | 2025.03.05 |
Connection Pool과 @Transactional: 효율적인 데이터베이스 관리를 위한 핵심 개념 (0) | 2025.02.25 |
/gradlew bootRun은 어떻게 동작하는가? (1) | 2025.02.22 |