[Spring] ResponseBodyAdvice 를 이용하여 ControllerAdvice 활용하기
공통 응답 형식을 만들 때, 이전에는 컨트롤러 단에서 ResponseDto
를 객체를 이용하여 응답값을 필드에 set 하여 내려주었다.
이 경우, 모든 API 에 ResponseDto
를 직접 적어주어야 한다. 누락되는 케이스가 존재할 수 있고, 공통되는 영역이니 한 곳에서 관리하고자 하는 욕구가 생긴다.
스프링에서 제공하는 ResponseBodyAdvice
인터페이스를 이용하여 공통 응답 형식을 만들 수 있다.
public interface ResponseBodyAdvice<T> {
boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType);
@Nullable
T beforeBodyWrite(@Nullable T body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response);
}
supports()
메서드는 controller 작업이 끝난 response를 beforeBodyWrite 메서드에 보낼지 판단한다. 컨트롤러의 returnType, messageConverter 정보를 이용하여 beforeBodyWrite() 로 보낼지 여부를 결정한다.
beforeBodyWrite()
메서드는, controller 작업이 끝나고 어떠한 Converter를 통해 응답을 보낼지 결정된 후에 호출된다. 이 메서드를 구현할 때 실제 원하는 바디 형식 또는 헤더 정보 등을 추가하여 커스터마이징 할 수 있다.
ResponseBodyAdvice 인터페이스를 구현한 클래스에 @ControllerAdvice
어노테이션을 추가하여 스프링 빈으로 등록한다. 이를 통해 스프링부트 애플리케이션 전역에서 핸들링 할 수 있도록 한다.
@Slf4j
@ControllerAdvice(basePackages = "com.sample")
public class ResponseAdvice implements ResponseBodyAdvice {
@Override
public boolean supports(MethodParameter returnType, Class converterType) {
return !returnType.getParameterType().equals(ResponseWrapper.class);
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
if (body == null) {
return null;
}
return new ResponseWrapper(response, body);
}
swagger 의 값은 공통 응답 형식을 적용하지 않도록 basePackages
설정을 추가하고, ResponseBodyAdvice
인터페이스를 구현한다. beforeBodyWrite()
를 오버라이드하여 응답 바디를 쓰기 전에 공통 응답 Wrapper 로 감싸 응답을 내려줄 수 있도록 한다.
public class ResponseWrapper {
private Integer status;
private String message;
private Object data; // 결과 데이터
public ResponseWrapper() {
HttpStatus httpStatus = HttpStatus.OK;
status = httpStatus.value();
message = httpStatus.getReasonPhrase();
}
public ResponseWrapper(ServerHttpResponse response, Object body) {
HttpStatus httpStatus = HttpStatus.OK;
status = httpStatus.value();
message = httpStatus.getReasonPhrase();
data = body;
}
}