본문 바로가기
[개발] 프레임워크/Spring

ResponseBodyAdvice의 beforeBodyWrite와 String 반환형

by Devsong26 2024. 8. 8.

@RestControllerAdvice를 이용하여 REST API의 모든 응답을 변환해주는 작업을 하려고 합니다.

 

ResponseBodyAdvice 인터페이스를 구현하여 beforeBodyWrite 를 오버라이드하면 됩니다. 

 

이것으로 API의 메서드 결과를 변환해서 새로운 결과를 응답하기 때문에 응답처리를 중앙화할 수 있다는 장점이 있습니다. 하지만 API의 메서드 반환값이 String일 경우에는 변환된 결과를 String class로 형변환을 할 수 없다는 예외를 발생시키기도 합니다.

 

문제의 코드는 AbstractMessageConverterMethodProcessor.writeWithMessageConverters 의 아래 표시된 영역이며, 형변환할 수 없다는 예외를 발생시킵니다.

 

 

String이 반환형일 때는 StringHttpMessageConverter 라는 컨버터에 의해 처리가 되는데 beforeBodyWrite 에 의해 변환된 결과를 String으로 형변환합니다.

 

StringHttpMessageConverter의 특수 처리
Spring에서는 String 타입의 응답을 처리하기 위해 StringHttpMessageConverter를 사용합니다.
StringHttpMessageConverter는 응답을 문자열로 변환할 때 특별한 처리를 합니다. 이 변환기는 응답 본문을 작성하는 단계에서 이미 문자열로 변환된 상태로 처리하기 때문에, beforeBodyWrite에서 다른 객체로 변환하면 변환기에서 제대로 처리되지 않습니다.

HttpMessageConverters의 순서
HttpMessageConverters의 순서에 따라 StringHttpMessageConverter가 먼저 적용되기 때문에, beforeBodyWrite에서 반환된 객체가 다시 문자열로 변환되는 과정을 거칠 수 있습니다.
이로 인해 beforeBodyWrite에서 반환된 객체가 올바르게 처리되지 않을 수 있습니다.

 

 

스택트레이스로 보게 되면 아래와 같습니다.

 

StringHttpMessageConverter.writeInternal   ---> 문제의 영역

AbstractHttpMessageConverter.write

AbstractMessageConverterMethodProcessor.writeWithMessageConverters

 

 

AbstractHttpMessageConverter.write, StringHttpMessageConverter.writeInternal 코드를 살펴보면 다음과 같습니다.

AbstractHttpMessageConverter.write

 

StringHttpMessageConverter.writeInternal

 

제네릭 타입인 t가 String으로 형변환하여 파라미터로 입력이 되어야 하는데 String이 아닌 객체가 들어가면서 형변환 예외가 발생한 것입니다. 

 

결국 결과 타입을 래핑하는 건 문자열을 제외하고 처리를 하여 해결했습니다.

 

@RestControllerAdvice
public class ApiResponseAdvice implements ResponseBodyAdvice<Object> {

    @Override
    public boolean supports(
            @NonNull MethodParameter returnType,
            @NonNull Class<? extends HttpMessageConverter<?>> converterType) {

        // String 타입이면 미적용        
        return returnType.getParameterType() != String.class;
    }
    
    ...
    
}