[Spring MVC] API 예외 처리
시작
ApiExceptionController
@Slf4j
@RestController
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;
}
}
URL에 전달된 id
의 값이 ex
이면 예외가 발생하도록 했다.
WebServerCustomizer.class
(new ErrorPage)가 동작하여 HTML 화면을 렌더링해준다.
정상 호출
{
"memberId": "spring",
"name": "hello spring"
}
API를 요청해서 정상의 경우 JSON 형식으로 데이터가 정상 반환된다.
하지만, 오류가 발생하면 JSON반환이 아닌 미리 만들어둔 오류 HTML 페이지가 반환된다.
문제를 해결하려면 오류 페이지 컨트롤러도 JSON 응답을 할 수 있도록 수정해야 한다.
ErrorPageController
// @RequestMapping("/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));
}
produces = MediaType.APPLICATION_JSON_VALUE
는 클라이언트가 요청하는 HTTP Header의 Accept
값이 application/json
일 때 해당 메서드가 호출되게 한다.
결국, 클라이언트가 받고 싶은 미디어타입이 json이면 이 컨트롤러의 메서드가 호출된다.
ResponseEntity
를 사용해서 응답하기 때문에 메시지 컨버터가 동작하면서 클라이언트에 JSON이 반환된다.
예외 호출
{
"message": "잘못된 사용자",
"status": 500
}
스프링 부트 기본 오류 처리
API 예외 처리도 스프링 부트가 제공하는 BasicErrorController
를 통해 처리할 수 있다.
BasicErrorController
@RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {}
@RequestMapping
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {}
errorHtml()
:produces = MediaType.TEXT_HTML_VALUE
: 클라이언트 요청의Accept
헤더 값이text/html
인 경우에errorHtml()
를 호출해서 View를 제공한다.error()
: 그 외의 경우에 호출되고,ResponseEntity
로 HTTP Body에 JSON 데이터를 반환한다.
호출 결과
{
"timestamp": "2021-04-28T00:00:00.000+00:00",
"status": 500,
"error": "Internal Server Error",
"exception": "java.lang.RuntimeException",
"trace": "java.lang.RuntimeException: 잘못된 사용자\n\tat
hello.exception.web.api.ApiExceptionController.getMember(ApiExceptionController.java:19...,
"message": "잘못된 사용자",
"path": "/api/members/ex"
}
스프링 부트는 BasicErrorController
가 제공하는 기본 정보들을 활용해서 오류 API를 생성해준다.
스프링 부트의 예외 처리
스프링 부트의 기본 설정은 오류 발생시 /error
를 오류 페이지로 요청한다.
BasicErrorController
는 이 경로를 기본으로 받는다.(server.error.path
로 수정 가능.)
HTML vs API
BasicErrorController
를 확장하면 JSON 메시지도 변경할 수 있다. 하지만 이보다 더 좋은 기능인 @ExceptionHandler가 제공되니 이는 참고만 해두면 된다.
BasicErrorController
는 HTML 페이지를 제공하는 경우에는 매우 편리하기 때문에 이 방법은 HTML 화면을 처리할 때 사용되고, API 오류 처리는 @ExceptionHandler
를 사용하면 된다.
<출처 : 인프런 스프링 MVC 2편 - 백엔드 웹 개발 활용 기술(김영한)>
댓글남기기