html 같은 경우는 그냥 4xx, 5xx등의 오류 페이지만 있으면 거의 해결 가능하다.
근데 API는,
각 오류 상황에 맞는 오류 응답 스펙을 클라이언트 사이드와 서버 사이드에서 약속을 하고 데이터를 내려주어야 한다.
먼저, 그냥 가상으로 /api/members/ex 라는 uri로 호출되면 에러를 뿜게 해줬고, 그 외에는 그냥 오브젝트를 만들어서 return 해 줬다.
@RestController라 Json객체로 변환되어 들어간다.
@RestController
@Slf4j
public class ApiExceptionController {
@GetMapping("/api/members/{id}")
public MemberDto getMember(@PathVariable("id") String id){
if(id.equals("ex")){
throw new RuntimeException("잘못된 사용자");
}
return new MemberDto(id, "hello " + id);
}
@Data
@AllArgsConstructor
static class MemberDto{
private String memberId;
private String name;
}
}
원래 컨셉대로라면 멤버조회라서 repository넣고 거기에서 {id}를 조회해서 찾아와야 하는데, 그냥 예시만 보여주기 위해 이렇게 한 거다.
@Component
public class WebServerCustomizer implements WebServerFactoryCustomizer<ConfigurableWebServerFactory> {
@Override
public void customize(ConfigurableWebServerFactory factory) {
ErrorPage errorPage404 = new ErrorPage(HttpStatus.NOT_FOUND, "/error-page/404");
ErrorPage errorPage500 = new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/error-page/500");
ErrorPage errorPageEx = new ErrorPage(RuntimeException.class, "/error-page/500");
factory.addErrorPages(errorPage404, errorPage500, errorPageEx);
}
}
우리가 스프링 구성에 등록해놨던 ErrorPage다. 에러를 뿌리면 /error-page/500 컨트롤러를 호출하게 되어 있다.
@Slf4j
@Controller
public class ErrorPageController {
public static final String ERROR_EXCEPTION = "jakarta.servlet.error.exception";
public static final String ERROR_EXCEPTION_TYPE = "jakarta.servlet.error.exception_type";
public static final String ERROR_MESSAGE = "jakarta.servlet.error.message";
public static final String ERROR_REQUEST_URI = "jakarta.servlet.error.request_uri";
public static final String ERROR_SERVLET_NAME = "jakarta.servlet.error.servlet_name";
public static final String ERROR_STATUS_CODE = "jakarta.servlet.error.status_code";
@RequestMapping("/error-page/500")
public String errorPage500(HttpServletRequest request, HttpServletResponse response){
log.info("errorPage 500");
printErrorInfo(request);
return "error-page/500";
}
@RequestMapping(value = "/error-page/500", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Map<String, Object>> errorPage500Api( HttpServletRequest request, HttpServletResponse response){
log.info("API errorPage 500");
Map<String, Object> result = new HashMap<>();
Exception ex = (Exception) request.getAttribute(ERROR_EXCEPTION);
result.put("status", request.getAttribute(ERROR_STATUS_CODE));
result.put("message", ex.getMessage());
Integer statusCode = (Integer) request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
return new ResponseEntity<>(result, HttpStatus.valueOf(statusCode));
}
}
/error-page/500로 uri가 똑같다. 그러나, produces = MediaType.APPLICATION_JSON_VALUE로 얘는 json 전용이야. 라고 이야기 해 줘서 괜찮다. 구분되서 오류 안뜬다.
Accept가 */*면 그냥 위에꺼가 호출 되지만,
Accept가 application/json이면 아래 것이 우선순위를 가진다.
내용은 Key Value로 이루어진 컬렉션 HashMap을 만들어서,
거기에 상태코드, 에러를 뿜을 때 넣어놨던 메시지를 꺼내서 넣어놨다.
그리고, RequestDispatcher.ERROR_STATUS_CODE는
우리가 위에 ERROR_STATUS_CODE 해서 정의해놨는데, 사실 RequestDispatcher 안에 다 있다.
에러에 관한 상수 뿐 아니라 여러 Request에 기본으로 들어가는 것들이 key가 다 상수로 정의되어 있다.
여튼
return new ResponseEntity<>(객체, 상태코드)
넣어서, ResponseEntity니 HttpMessageConverter에 의해 객체가 Json 형태로 변환되고,
또 거기에 상태코드까지 같이 넣어 반환해야 한다.
이렇게 응답 온다.
'스프링 > 4. 스프링 MVC-2' 카테고리의 다른 글
69. API를 위한 예외처리 기능 (0) | 2023.09.13 |
---|---|
68. 스프링 부트의 API 오류 처리 (0) | 2023.09.12 |
66. BasicErrorController가 제공하는 정보들 (0) | 2023.09.10 |
65. 스프링 부트 오류페이지 (0) | 2023.09.10 |
64. 서블릿 예외처리 인터셉터 (0) | 2023.09.10 |