SpringBoot RestAPI 404 Not Found message Custom, @ControllerAdvice @ExceptionHandler
SpringBoot 로 API를 개발할 때 RequestMapping 되어있지 않은 주소로 요청하면
아래와 같은 에러 메시지를 전송합니다.
{
"timestamp": "2022-11-09T09:35:14.441+00:00",
"status": 404,
"error": "Not Found",
"message": "No message available",
"path": "/api/"
}
기존에 API 에서 전송하던 구조와 다르면 제공받는 입장에서도 처리하기 껄끄럽기 때문에
기존 응답 구조에 맞게 설정을 해주는 것이 좋습니다.
그럼 응답 구조에 맞게 처리하는 방법을 알아보겠습니다.
우선 application.yml 파일에 설정을 추가해 주어야 합니다.
1. spring boot properties 설정
[application.yml]
spring:
web:
resources:
add-mappings: 'false'
mvc:
throw-exception-if-no-handler-found: 'true'
[application.properties]
spring.web.resources.add-mapping=true
spring.mvc.throw-exception-if-no-handler-found=true
properties 설정 파일에 맞게 설정합니다.
2. CustomExceptionHandler 구현
[GlobalExceptionHandler]
@ControllerAdvice
public class GlobalExceptionHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(GlobalExceptionHandler.class);
@ExceptionHandler(NoHandlerFoundException.class)
public ResponseEntity<Object> handleNoHandlerFoundException(NoHandlerFoundException e) {
LOGGER.error(e.getMessage(), e);
ErrorCode errorCode = CommonErrorCode.NOT_FOUND;
return handleExceptionInternal(errorCode);
}
private ResponseEntity<Object> handleExceptionInternal(ErrorCode errorCode) {
return ResponseEntity.status(errorCode.getResultCode())
.body(makeErrorResponse(errorCode));
}
private String makeErrorResponse(ErrorCode errorCode) {
HeaderVO header = new HeaderVO();
header.setError(errorCode.getName());
header.setStatus(errorCode.getResultCode().value());
header.setMessage(errorCode.getResultMsg());
return CmmnVar.GSON.toJson(ErrorResponse.builder().header(header).build());
}
}
객체를 리턴해야 되는 경우에는 @RestControllerAdvice를 사용하면 되고, 저의 경우 json 을 전달하기 때문에 @ControllerAdvice를 사용하였습니다. @ControllerAdvice 는 모든 컨트롤러 스캐닝을 통해 @ExceptionHandler와 함께 전역 Exception 처리에 활용됩니다.
자세한 설명은 아래의 Link를 참고하세요.
다른 Exception 에 대해서 처리하고 싶으면 위의 코드에서 @ExceptionHander 부분을 복사하여 Exception 클래스쪽만 변경해주시면 됩니다.
[ErrorCode] code interface 입니다 CommonError에서 implements 받아 구현합니다.
public interface ErrorCode {
String name();
HttpStatus getResultCode();
String getResultMsg();
}
[CommonError] 타 Exception을 처리하기 위한 코드, 메시지 enum 입니다. 추가해서 사용하세요.
@Getter
@RequiredArgsConstructor
public enum CommonErrorCode implements ErrorCode {
NOT_FOUND(HttpStatus.NOT_FOUND, "요청하신 페이지를 찾을 수 없습니다.");
private final HttpStatus resultCode;
private final String resultMsg;
}
[ErrorResponse] 응답 구조체 입니다. 기존 api 에서 리턴하는 header를 사용합니다.
@Getter
@Builder
@RequiredArgsConstructor
public class ErrorResponse {
private final HeaderVO header;
}
[HeaderVO]
@Data
public class HeaderVO {
private int status;
private String message;
private Integer size;
private String error;
}
이제 api에서 RequestMapping되지 않는 주소를 요청하면 아래와 같이 전달됩니다.
{
"header": {
"status": 404,
"message": "요청하신 페이지를 찾을 수 없습니다.",
"error": "NOT_FOUND"
}
}