- 지난 포스팅까지 해서 HTML을 반환하는 예외 처리 방법에 대해서 알아보았다. 그런데 그 방법처럼 예외로서 HTML을 반환하면 어떻게 될까? 코드로 결과를 확인해보자
WebServerCustomizer
- 우선 지난번에 생성했던 에러페이지를 등록하는 클래스를 빈으로 등록해주자
코드는 바뀐 것이 없고 @Component의 주석만 제거하였다
// 이 부분
@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");
// 예외 발생 시
// RuntimeException의 자식 예외 또한 같이 처리해준다.
ErrorPage errorPageEx = new ErrorPage(RuntimeException.class, "/error-page/500");
factory.addErrorPages(errorPage404, errorPage500, errorPageEx);
}
}
RestController생성
- API응답 결과를 확인하기 위해 컨트롤러를 생성해주자
@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;
}
}
- 이제 파라미터로 넘어온 id값이 ex와 일치하면 예외를 발생시킬 것이다.

응답 결과를 확인해보면 json형식의 데이터가 아닌 html문서가 온것을 확인할 수 있다. 그런데 api의 경우 이런 문서가 아니라 json형식의 데이터가 올 것이라 예상할 것이기 때문에 이렇게 응답을 보내는 것은 문제가 있다. 그렇다면 어떻게 해야할까?
ErrorPageController수정
@Slf4j
@Controller
public class ErrorPageController {
//생략
@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));
}
//생략
}
- 기존에 생성해두었던 ErrorPageController에서 500에러를 처리하는 메서드를 추가로 생성했는데 @RequestMapping의 파라미터 중에 produces에 주목하자.
errorPage500과 errorPage500Api의 url패턴은 동일하다.
다만, errorPage500Api에는 produces = MediaType.APPLICATION_JSON_VALUE라는 값을 주었는데, 이는 클라이언트의 요청 헤더에 Accept의 value가 application/json일 경우 해당 메서드가 우선권을 가지게 된다.
- errorPage500Api가 수행될 경우 예외 객체에 담긴 메세지와 상태코드를 반환하게 된다.
ResponseEntity(T body, HttpStatus status)

Accept를 application/json으로 설정한 응답 결과다. 기존에 HTML문서가 반환된 것과 달리 json데이터 형식으로 반환된 것을 알 수 있다.
- 그런데 스프링 부트에서는 API 예외 처리도 기본적으로 제공한다.
스프링 부트 에러 처리
//@Component
public class WebServerCustomizer implements WebServerFactoryCustomizer<ConfigurableWebServerFactory> {
- WebServerCustomizer 빈 등록을 해제해주자.

따로 설정을 하지 않았음에도 스프링 부트에서 기본 메세지를 제공하는 것을 확인할 수 있다.
api도 마찬가지로 더 자세한 에러 메세지를 확인할 수 있는데, application.properties에 다음 설정을 추가해주자.
server.error.include-exception=true
server.error.include-message=always
server.error.include-stacktrace=always
server.error.include-binding-errors=always


- 응답 결과를 확인 해보면 더 자세한 정보가 반환되는 것을 확인할 수 있다. 다만 이것도 마찬가지로 보안상의 문제가 발생할 수 있으므로 최소한의 정보만 노출하고, 나머지는 로그로 확인하자!!!