Spring/초기 setting

8) API Response(@ControllerAdvice) 설정

쿠카이든 2023. 4. 20. 12:09
728x90

아래 내용은 https://github.com/beaver84/setting-test 에서 실제 소스를 확인할 수 있습니다.

  • @ControllerAdvice는 Spring에서 제공하는 Annotation입니다.
  • Spring 애플리케이션에서 예외 처리를 관리하는 데 사용됩니다.
  • 단일 클래스 또는 하나의 메서드가 아닌 애플리케이션의 모든 클래스에서 발생하는 모든 예외를 처리할 수 있습니다.

 

  • API 서버일 경우, 기본적으로 성공함을 가정한 API 응답을 정의하고, 그다음 Exception이 발생했을 때, 응답을 정의하는 것이 일반적이다(성공 → 실패 advice 참조)

OK Response or Exception(https://www.bootng.com/2020/06/spring-boot-controlleradvise-example.html)

 

  • 200(성공)일 때, 적용 예시
@ControllerAdvice(basePackages = "com.marvrus.moon_app_api.controller.api")
public class ControllerResponseAdvise implements ResponseBodyAdvice {
    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return true;
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
                                  Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        ApiResponse apiResponse = new ApiResponse();
        apiResponse.setSuccess(true);
        apiResponse.setData(body);
        return apiResponse;
    }
}

 

  • ApiResonse.class - 위에서 success, data를 getter, setter로 빼놓음, 200일 때 전용
public class ApiResponse {
    private boolean success;
    private Object data;

    public boolean isSuccess() {
        return success;
    }

    public void setSuccess(boolean success) {
        this.success = success;
    }

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }
}
728x90
  • (예외 처리)ApiExceptionHandler.class - 200이 아닐경우 ControllerAdvice
@ControllerAdvice
public class ApiExceptionHandler {

	private Logger log = LogManager.getLogger(this.getClass());

    @ExceptionHandler(ApiException.class)
    protected ResponseEntity<ApiExceptionInfo> handleMethodArgumentNotValidException(ApiException e) {
        log.error("API Exception : {}", e.getMessage());
        for (StackTraceElement element : e.getStackTrace()) {
            log.error("{}", element.toString());
        }
        final ApiExceptionInfo response = new ApiExceptionInfo();
        response.setHttpStatus(e.getStatusCode());
        response.setMessage(e.getMessage());
        response.setSuccess(false);
        return new ResponseEntity<>(response, e.getStatusCode());
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    protected ResponseEntity<ApiExceptionInfo> handleMethodArgumentNotValidException(
            MethodArgumentNotValidException e) {
    	log.error("API Exception : {}", e.getMessage());
        for (StackTraceElement element : e.getStackTrace()) {
        	log.error("{}", element.toString());
        }
        final ApiExceptionInfo response = new ApiExceptionInfo();
        response.setHttpStatus(HttpStatus.BAD_REQUEST);
        response.setMessage(e.getBindingResult().getAllErrors().get(0).getDefaultMessage());
        response.setSuccess(false);
        return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST);
    }

    @ExceptionHandler(Exception.class)
    protected ResponseEntity<ApiExceptionInfo> handleMethodArgumentNotValidException(Exception e) {
        log.error("API Exception : {}", e.getMessage());
        for (StackTraceElement element : e.getStackTrace()) {
        	log.error("{}", element.toString());
        }
        final ApiExceptionInfo response = new ApiExceptionInfo();
        response.setHttpStatus(HttpStatus.INTERNAL_SERVER_ERROR);
        response.setMessage(e.getMessage());
        response.setSuccess(false);
        return new ResponseEntity<>(response, HttpStatus.INTERNAL_SERVER_ERROR);
    }
}

 

  • ApiExceptionInfo.class - 200이 아닐경우, 메시지를 내려주는 DTO 정의
public class ApiExceptionInfo {
    private String message;
    private HttpStatus httpStatus;
    private Boolean success;

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public HttpStatus getHttpStatus() {
        return httpStatus;
    }

    public void setHttpStatus(HttpStatus httpStatus) {
        this.httpStatus = httpStatus;
    }

    public Boolean getSuccess() {
        return success;
    }

    public void setSuccess(Boolean success) {
        this.success = success;
    }
}

 

  • ApiException.class - 200(정상)이 아닐경우, 실제 Api 요청의 응답 형식이 정의되어 있는 곳
public class ApiException extends HttpStatusCodeException {
    public ApiException(HttpStatus statusCode) {
        super(statusCode);
    }

    public ApiException(HttpStatus statusCode, String statusText) {
        super(statusText, statusCode, statusText, null, null, null);
    }

    public ApiException(HttpStatus statusCode, String statusText, byte[] responseBody, Charset responseCharset) {
        super(statusCode, statusText, responseBody, responseCharset);
    }

    public ApiException(String message, HttpStatus statusCode, String statusText, HttpHeaders responseHeaders,
                        byte[] responseBody, Charset responseCharset) {
        super(message, statusCode, statusText, responseHeaders, responseBody, responseCharset);
    }

    public ApiException(HttpStatus statusCode, String statusText,
                        @Nullable HttpHeaders responseHeaders, @Nullable byte[] responseBody,
                        @Nullable Charset responseCharset) {

        super(statusText, statusCode, statusText, responseHeaders, responseBody, responseCharset);
    }
}
728x90