상세 컨텐츠

본문 제목

ResponseBodyAdvice 인터페이스 파해치기

Spring/JAVA

by Chan.94 2024. 12. 27. 11:03

본문

반응형

Intro

  • ResponseBodyAdvice는 Spring MVC에서 Handler(Controller) 처리 후, 클라이언트로 전달되기 전에 공통적으로 처리하거나 수정하고자 할 때 사용된다.
  • ResponseBodyAdvice는 @ResponseBody가 설정된 controller가 반환된 이후 실행된다.
  • ResponseBodyAdvice 인터페이스를 상속받아 구현하고 구현체에는 @ControllerAdvice어노테이션을 추가한다.
  • @ExceptionHandler을 사용하고 있다면 @ExceptionHandler가 먼저 호출되어 발생한 예외를 처리한 후 ResponseBodyAdvice 구현체가 호출된다.
  • ResponseBodyAdvice는 여러 개를 등록할 수 있다.
    • Spring은 @ControllerAdvice로 등록된 모든 ResponseBodyAdvice를 확인한다.
    • @Order를 활용해 실행 순서를 조정할 수 있다.
    • supports 메서드로 적용 범위를 제한할 수 있다.
    • 여러 ResponseBodyAdvice를 사용할 때는 데이터 일관성과 중복 작업에 주의해야 한다.

실행 흐름

  1. Client → DispatcherServlet (요청 수신)
  2. HandlerMapping
  3. RequestBodyAdvice의 supports(), beforeBodyRead() 실행
  4. HttpMessageConverter 실행 (요청 본문 변환)
  5. RequestBodyAdvice의 afterBodyRead() 실행
  6. ArgumentResolver실행
  7. Handler(Controller) 로직 실행
  8. ReturnValueHandler실행
    • ReturnValueHandler는 반환값을 처리하는 메인 흐름이다.
    • 반환값을 처리하기 위해 적절한 ReturnValueHandler를 선택한다.
    • 반환값을 HTTP 응답으로 변환하기 위한 준비 작업을 수행한다.
  9. ResponseBodyAdvice의 supports(), beforeBodyWrite() 실행
    • ResponseBodyAdvice는 ReturnValueHandler가 반환값을 처리한 후 추가 처리를 수행한다.
    • supports() 메서드를 호출하여 적용 여부를 판단한다.
    • beforeBodyWrite() 메서드는 응답 본문(Handler가 반환한 데이터)을 처리하기 전에 수정하거나 추가 작업을 수행한다.
  10. HttpMessageConverter 실행 (응답 본문 변환)
    • 반환값을 HTTP 응답 본문으로 변환한다.
      • 반환값의 타입과 요청의 Accept 헤더를 기반으로 적절한 HttpMessageConverter를 선택한다.
      • 자바 객체를 HTTP 본문에 맞는 데이터 형식(JSON, XML 등)으로 변환한다.
  11. DispatcherServlet → Client (응답 반환)
    • 변환된 응답 본문과 상태 코드, 응답 헤더가 클라이언트로 전송한다.

ResponseBodyAdvice

ResponseBodyAdvice 인터페이스

Handler(Controller)에서 반환된 @ResponseBody 또는 HTTP 메시지 변환 결과를 가로채어 추가적인 처리를 할 수 있도록 제공되는 인터페이스

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
    • 특정 Handler(Controller) 반환 값에 대해 ResponseBodyAdvice가 적용될지를 결정한다.
      특정 조건을 설정하여 필요한 타입에만 적용할 수 있다.
    • 반환 값이 true인 경우만 beforeBodyWrite 메서드가 실행된다.
  • beforeBodyWrite
    • Handler(Controller)가 반환하는 데이터를 가공할 수 있다.
    • body가 Handler가 반환한 데이터다.

주요 역할

  • 공통 응답 포맷 통일
    모든 API 응답에 동일한 포맷을 적용한다.
    Ex) { "status": "success", "data":... }와 같은 구조로 변환.
  • 데이터 후처리
    컨트롤러에서 반환된 데이터를 수정하거나 추가 작업을 수행한다.
    Ex) 특정 필드를 추가하거나 값을 변환.
  • 보안
    민감한 데이터를 마스킹하거나 제거한다.
    Ex) 사용자 정보를 반환할 때 비밀번호 필드를 제거.
  • 로깅 및 디버깅
    응답 데이터를 로깅하거나 모니터링 도구로 전달.

Example

공통 응답 포맷 적용

@RestControllerAdvice
public class CustomResponseBodyAdvice implements ResponseBodyAdvice<Object> {

    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        // 모든 컨트롤러에 적용하거나 특정 조건을 설정 가능
        return true;
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
                                  Class<? extends HttpMessageConverter<?>> selectedConverterType,
                                  ServerHttpRequest request, ServerHttpResponse response) {
        // 공통 응답 포맷 적용
        if (body instanceof ApiResponse) {
            return body; // 이미 포맷이 적용된 경우 그대로 반환
        }
        return new ApiResponse("success", body);
    }
}

// 공통 응답 구조를 위한 DTO
public class ApiResponse {
    private String status;
    private Object data;

    public ApiResponse(String status, Object data) {
        this.status = status;
        this.data = data;
    }
}

보안 - 민감한 데이터 제거

@RestControllerAdvice
public class SensitiveDataMaskingAdvice implements ResponseBodyAdvice<Object> {

    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        // 특정 데이터 타입에만 적용
        return returnType.getParameterType().equals(UserDto.class);
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
                                  Class<? extends HttpMessageConverter<?>> selectedConverterType,
                                  ServerHttpRequest request, ServerHttpResponse response) {
        if (body instanceof UserDto) {
            UserDto user = (UserDto) body;
            user.setPassword(null); // 비밀번호 제거
            return user;
        }
        return body;
    }
}

데이터 후처리

@Slf4j
@RequiredArgsConstructor
@RestControllerAdvice
public class CustomResponseBodyAdvice implements ResponseBodyAdvice{
    
    private final ObjectMapper objectMapper;
    
    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return returnType.getParameterType().equals(ResponseVo.class);
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
            Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        
        this.setDefaultValue(body);
        this.logging(body, selectedContentType);
        
        return body;
    }

    private void setDefaultValue(ResponseVo<?> responseVo) {
        ResponseVo<?> responseVo = (ResponseVo<?>) body;
        responseVo.setTimestamp(LocalDateTime.now());
    }
    
    private void logging(Object body, MediaType selectedContentType) {
        try {
            // JSON 변환 가능한 경우만 처리
            if (body != null && MediaType.APPLICATION_JSON.includes(selectedContentType)) {
                
                String json = objectMapper.writeValueAsString(body);
                log.info("Response Body (JSON): {}", json);
            }
        } catch (Exception e) {
            log.error("Error while converting response body to JSON", e);
        }
    }
}

RequestBodyAdvice에 대한 내용은 아래글을 참고하기 바란다.

 

RequestBodyAdvice 인터페이스 파해치기

 

RequestBodyAdvice 인터페이스 파해치기

IntroRequestBodyAdvice는 Spring MVC에서 요청 본문을 읽고 처리하기 전에 특정 작업을 수행하거나, 읽은 본문 데이터를 변경하고자 할 때 사용된다. 주로 JSON, XML 등 요청 본문에 포함된 데이터를 Handler(C

fvor001.tistory.com

 

반응형

관련글 더보기

댓글 영역

>