스프링 부트는 기본적으로 예외/sendError() 발생 시, 개발자가 에러 처리 컨트롤러를 등록하지 않게 되면
자동적으로 WAS에서 "/error"(수정 가능)을 재요청하여 BasicExceptionController에서 처리하게 만들어 져 있다.
BasicErrorController 컨트롤러는 기본적으로 아래와 같은 메서드를 제공한다.
위 코드를 분석해 보면 응답 에러 발생시
1] HTML 오류 페이지
2] JSON 방식
위 2가지 방식으로 오류 상태를 클라이언트에게 보내 준다.
아래의 코드는 ResponseEntity<>를 이용하여 API 응답 오류를 JSON으로 반환하는 예시이다.
@Slf4j
@Controller
public class ErrorPageController {
// RequestDispatcher 클래스에 final static으로 정의돼 있음
public final static String FORWARD_REQUEST_URI = "jakarta.servlet.forward.request_uri";
public final static String FORWARD_CONTEXT_PATH = "jakarta.servlet.forward.context_path";
public final static String FORWARD_MAPPING = "jakarta.servlet.forward.mapping";
public final static String FORWARD_PATH_INFO = "jakarta.servlet.forward.path_info";
public final static String FORWARD_SERVLET_PATH = "jakarta.servlet.forward.servlet_path";
public final static String FORWARD_QUERY_STRING = "jakarta.servlet.forward.query_string";
public final static String INCLUDE_REQUEST_URI = "jakarta.servlet.include.request_uri";
public final static String INCLUDE_CONTEXT_PATH = "jakarta.servlet.include.context_path";
public final static String INCLUDE_PATH_INFO = "jakarta.servlet.include.path_info";
public final static String INCLUDE_MAPPING = "jakarta.servlet.include.mapping";
public final static String INCLUDE_SERVLET_PATH = "jakarta.servlet.include.servlet_path";
public final static String INCLUDE_QUERY_STRING = "jakarta.servlet.include.query_string";
public final static String ERROR_EXCEPTION = "jakarta.servlet.error.exception";
public final static String ERROR_EXCEPTION_TYPE = "jakarta.servlet.error.exception_type";
public final static String ERROR_MESSAGE = "jakarta.servlet.error.message";
public final static String ERROR_REQUEST_URI = "jakarta.servlet.error.request_uri";
public final static String ERROR_SERVLET_NAME = "jakarta.servlet.error.servlet_name";
public final static String ERROR_STATUS_CODE = "jakarta.servlet.error.status_code";
// 1] HTML 오류 페이지
@RequestMapping("/error-page/500")
public String errorPage500(HttpServletRequest request, HttpServletResponse response){
log.info("errorPage 500");
printErrorInfo(request);
return "error-page/500";
}
// 2] API 응답 오류
@RequestMapping(value="/error-page/500",produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Map<String,Object>> errorPage500Api(HttpServletRequest request,HttpServletResponse response){
log.info("API Eroor Page : 500");
HashMap<String, Object> result = new HashMap<>();
Exception ex = (Exception)request.getAttribute(ERROR_EXCEPTION);
result.put("message",ex.getMessage());
result.put("status",request.getAttribute(ERROR_STATUS_CODE));
Integer statusCode = (Integer)request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
return new ResponseEntity<>(result, HttpStatus.valueOf(statusCode));
}
private void printErrorInfo(HttpServletRequest request){
log.info("FORWARD_REQUEST_URI : {}",request.getAttribute(FORWARD_REQUEST_URI ));
log.info("FORWARD_CONTEXT_PATH : {}",request.getAttribute(FORWARD_CONTEXT_PATH));
log.info("FORWARD_MAPPING :{}",request.getAttribute(FORWARD_MAPPING));
log.info("FORWARD_PATH_INFO : {}",request.getAttribute(FORWARD_PATH_INFO));
log.info("FORWARD_SERVLET_PATH : {}",request.getAttribute(FORWARD_SERVLET_PATH));
log.info("FORWARD_QUERY_STRING : {}",request.getAttribute(FORWARD_QUERY_STRING));
log.info("INCLUDE_REQUEST_URI : {}",request.getAttribute(INCLUDE_REQUEST_URI ));
log.info("INCLUDE_CONTEXT_PATH : {}",request.getAttribute(INCLUDE_CONTEXT_PATH));
log.info("INCLUDE_PATH_INFO : {}",request.getAttribute(INCLUDE_PATH_INFO ));
log.info("INCLUDE_MAPPING : {}",request.getAttribute(INCLUDE_MAPPING ));
log.info("INCLUDE_SERVLET_PATH : {}",request.getAttribute(INCLUDE_SERVLET_PATH));
log.info("INCLUDE_QUERY_STRING : {}",request.getAttribute(INCLUDE_QUERY_STRING));
log.info("ERROR_EXCEPTION: {}",request.getAttribute(ERROR_EXCEPTION ));
log.info("ERROR_EXCEPTION_TYPE : {}",request.getAttribute(ERROR_EXCEPTION_TYPE));
log.info("ERROR_MESSAGE : {}",request.getAttribute(ERROR_MESSAGE ));
log.info("ERROR_REQUEST_URI : {}",request.getAttribute(ERROR_REQUEST_URI));
log.info("Dispatcher Type : {}",request.getDispatcherType());
}
}
PostMan을 이용하여 위 url로 API 요청(Accept : application/json) 시, 컨트롤러는 ResponseEntity<>를 사용하여 아래의
그림과 같이 API 응답 오류 메시지를 전달한다.
BasicErrorController 또한 API 응답 메시지에 대한 지원을 위와 같이 지원을 하기는 한다.
그러나 실무에서는 API 오류 응답에 대해서는 BasciErrorController를 사용하면 안 되고 @ExceptionHandler를 사용해야
한다.
스프링 부트가 제공하는 BasicErrorController 는 HTML 페이지를 제공하는 경우에는 매우 편리하다.
4xx, ` ` 5xx 등등 모두 잘 처리해준다.
그런데 API 오류 처리는 다른 차원의 이야기이다.
API 마다, 각각의 컨트롤러나 예외마 다 서로 다른 응답 결과를 출력해야 할 수도 있다.
예를 들어서 회원과 관련된 API에서 예외가 발생할 때 응답과, 상품과 관련된 API에서 발생하는 예외에 따라 그 결과가 달
라질 수 있다.(각 API에 대한 메서드가 API 응답 메시지를 만들 때 비지니스 상황에 맞게 서로 다른 형태의 API 응답 메세
지를 만들어야만 한다. 구체적인 예시로는 API 마다 상태 코드를 다르게 설정을 해야 하거나, API 각 사용자마다 API 응답
메세지 스펙을 다르게 요구할 경우 등. 또한 API 사용자가 매개변수 타입을 잘못 보내어 서버의 컨트롤러에서
IllegalException 예외가 발생하면 Tomcat은 상태 코드 500, 즉 서버 문제로 인식한다. 이런 경우 상태 코드 400으로 바꾸
고 주고 싶은 경우 등의 경우도 존재.
그러나 BasicErrorController는 유연한 API 응답 메시지를 만들지 못한다. BasicErrorController의
ResponseEntity<> error 메서드 중 getErrorAttributes()가 있는데 이 부분을 오버라이딩을 하여 API 응답 메세지를 개발자
입맛에 맞게 고칠 수가 있지만, 이렇게 하면 API 마다 해당 부분을 오버라이딩해야 하는 번거로움이 존재)
결과적으로 매우 세밀하고 복잡하다.
따라서 이 방법 은 HTML 화면을 처리할 때 사용하고, API 오류 처리는 뒤에서 설명할 ` @ExceptionHandler `를 사용해야
한다.
'Springあるある' 카테고리의 다른 글
API 오류 처리 시 주의할 점!!(Feat. Accept 헤더) (0) | 2025.01.19 |
---|---|
WebServerFactoryCustomizer<>(Feat. 오류 페이지 처리, Error page) (0) | 2025.01.19 |
원하는 기능을 Annotation을 만들어서 활용하기(Feat. ArgumentResolver,Login, @Login) (0) | 2025.01.17 |
HandlerMethod 인터페이스(Feat. @RequestMapping,ResourceHttpHandler,preHandle()) (0) | 2025.01.17 |
ResourceHttpRequestHandler(Feat. 정적 리소스, static resource) (0) | 2025.01.17 |