스프링/4. 스프링 MVC-2

68. 스프링 부트의 API 오류 처리

sdafdq 2023. 9. 12. 21:38

과거 우리가 BasicErrorController를 사용했을 때(별다른 설정 없어도 자동으로 등록 해주는), 

우리가 템플릿에 /error 경로에 에러 페이지를 에러코드.html로 만들어 두면

아무것도 하지 않아도 에러 발생 시(에러를 뿜거나 아니면 response.sendError()를 하던가) 저 에러 페이지 템플릿을 호출하게끔 해 주었다.

 

그럼 text/html 같은 경우는 이랬고, API는 어떻게 할까?

아무것도 안해도 된다.

 

먼저, BasicErrorController를 들어가 보자.

 

@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class BasicErrorController extends AbstractErrorController {
	@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);
	}

	@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의 일부분이며, 보면 text/html 뿐만 아니라

ResponseEntity도 등록해놨다.

 

html같은 경우는 ModelAndView를 반환하여 템플릿을 호출하고, ResponseEntity<>()는 응답 바디에 그대로 넣는다.

 

실제로, 아까 ErrorPage 설정해 놨던 걸 @Controller를 빼서 스프링에 등록하는 것들에서 제외시켜 주고,

포스트맨으로 Accept를 application/json으로 한 다음에 에러를 뿜는 컨트롤러를 호출해 보면,

이렇게 뜬다.

 

 

 

참고로, 저 @RequestMapping("${server.error.path:${error.path:/error}}")

에러 경로는

application.properties에서 

server.error.path로 수정 가능하다.

 

 

//@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);
    }
}

이걸 @Component를 주석처리하며 스프링 빈에 등록되는 것들에서 빼 줬는데, 이렇게 따로 등록해 주지 않아야 기본 BasicErrorController가 호출된다.

 

 

참고로, 

https://qwefdg3.tistory.com/442

여기에서 말 했던 application.properties에 관한 설정도 API도 똑같이 적용된다.

 

또 마찬가지로, BasicErrorController를 상속받아서 등록하면, 우리가 등록된 걸로 적용된다.

확장할 수 있는거다.

거기서 JSON 오류 메시지를 변경하거나 가능하다.

 

그런데 @ExceptionHandler라고  더 좋은 방법이 있다고 한다.

 

API 오류처리는 API마다(예: 상품API, 멤버API) 다르게 처리해야 할 수도 있고, 아니면 우리 회사의 서버를 여러 회사에서 이용한다면 회사마다 API가 다를 수 있다.

 

그럴 땐 @ExceptionHandler를 사용하면 된다고 한다.

아마 뭔가 예외처리 관련 인터페이스를 상속받고, 저 태그를 붙이고, 특정 함수를 override 할 것 같다. 왠지 좀 어댑터패턴? 이려나?