이전 포스트에서 @ControllerAdvice를 통한 예외 처리에 대해서 살펴보았다. 이번 포스트에서는 SpringBoot에서 특화된 처리방법을 살펴보자.
SpringBoot의 오류 페이지
BasicErrorController
SpringBoot에서 /error에 대한 처리를 담당하는 @Controller는 BasicErrorController이다. 이 녀석의 핵심되는 코드만 잠깐 살펴보자.
@Controller
// ${server.error.path} 또는 ${error.path} 또는 /error 요청에 대해 처리
@RequestMapping("${server.error.path:${error.path:/error}}")
public class BasicErrorController extends AbstractErrorController {
// 일반 HTTP 요청이 왔을 때의 처리
@RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
HttpStatus status = getStatus(request);
Map<String, Object> model = Collections
.unmodifiableMap(getErrorAttributes(request,
getErrorAttributeOptions(request, MediaType.TEXT_HTML)));
response.setStatus(status.value());
ModelAndView modelAndView = resolveErrorView(request, response, status, model);
return (modelAndView != null) ? modelAndView : new ModelAndView("error", model);
}
// errorHtml에서 처리하지 않는 즉 ajax 요청에 대해 처리
@RequestMapping
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
HttpStatus status = getStatus(request);
if (status == HttpStatus.NO_CONTENT) {
return new ResponseEntity<>(status);
}
Map<String, Object> body = getErrorAttributes(request,
getErrorAttributeOptions(request, MediaType.ALL));
return new ResponseEntity<>(body, status);
}
}
주석에 적힌바대로 BasicErrorController는 /error 요청에 대해서 일반 http 요청(errorHtml)과 ajax 요청(error)을 구분해서 처리하고 있다. 여기에 추가로 처리하고 싶은 내용이 있다면 BasicErrorController를 상속 받아서 위 두 개의 메서드를 재정의 해주면 된다.
BasicErrorController 확장
이제 BasicErrorController를 확장해서 사용자 정의 ErrorController를 작성해보자.
@Controller
@Slf4j
public class MyErrorController extends BasicErrorController {
public MyErrorController(ErrorAttributes errorAttributes,
ServerProperties serverProperties,
List<ErrorViewResolver> errorViewResolvers) {
super(errorAttributes, serverProperties.getError(), errorViewResolvers);
}
@Override
public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
ModelAndView mnv = super.errorHtml(request, response);
// 부가적으로 필요한 작업
log.debug("model: {}", mnv.getModel());
log.debug("view: {}", mnv.getViewName());
HttpStatus hs = getStatus(request);
switch (hs) {
case UNAUTHORIZED:
mnv.setViewName("/error/401");
break;
case NOT_ACCEPTABLE:
mnv.setViewName("/error/406");
break;
...
default:
mnv.setViewName("/error/500");
break;
}
return mnv;
}
@Override
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
ResponseEntity<Map<String, Object>> entity = super.error(request);
// 부가적으로 필요한 작업
return entity;
}
}
@ControllerAdvice가 사실 예외 처리를 전담하는 녀석도 아니고 일반 요청인지 ajax요청인지 직접 판단해서 처리해야했던 것에 비해 훨씬 깔끔해졌다.