05.Exception
Exception
์์ธ ์ฒ๋ฆฌ์ ์ค๋ฅ ํ์ด์ง
์๋ธ๋ฆฟ ์์ธ ์ฒ๋ฆฌ
์์ ์๋ธ๋ฆฟ ์ปจํ ์ด๋์ ์์ธ ์ฒ๋ฆฌ ์ง์ ๋ฐฉ์
.
Exception
์๋ฐ ์ง์ ์คํ ์ ์์ธ ๋ฐ์
์๋ฐ ๋ฉ์ธ ๋ฉ์๋๋ฅผ ์ง์ ์คํํ ๊ฒฝ์ฐ main ์ฐ๋ ๋ ์คํ
main ์ฐ๋ ๋ ์คํ ๋์ค์ ์์ธ๋ฅผ ์ก์ง ๋ชปํ๊ณ ์์ธ๊ฐ ๋์ ธ์ง๋ฉด,
์์ธ ์ ๋ณด๋ฅผ ๋จ๊ธฐ๊ณ ํด๋น ์ฐ๋ ๋ ์ข ๋ฃ
์น ์ ํ๋ฆฌ์ผ์ด์ ์์ ์์ธ ๋ฐ์
@GetMapping("/error-ex")
public void errorEx() {
throw new RuntimeException("์์ธ ๋ฐ์!");
}
์ฌ์ฉ์ ์์ฒญ๋ณ๋ก ๋ณ๋์ ์ฐ๋ ๋๊ฐ ํ ๋น๋๊ณ , ์๋ธ๋ฆฟ ์ปจํ ์ด๋ ์์์ ์คํ
์ ํ๋ฆฌ์ผ์ด์ ์์ ์์ธ ๋ฐ์ ์, try~catch ๋ก ์์ธ๋ฅผ ์ก์์ ์ฒ๋ฆฌํ๋ฉด ๋ฌธ์ ๊ฐ ์์.
ํ์ง๋ง, ์์ธ๋ฅผ ์ก์ง ๋ชปํ๊ณ ,
์๋ธ๋ฆฟ ๋ฐ์ผ๋ก ์์ธ๊ฐ ์ ๋ฌ๋ ๊ฒฝ์ฐ WAS(tomcat)๊น์ง ์์ธ ์ ๋ฌ
WAS <- ํํฐ <- ์๋ธ๋ฆฟ <- ์ธํฐ์ ํฐ <- ์ปจํธ๋กค๋ฌ(์์ธ๋ฐ์)
WAS ๋ Exception ์์ธ๊ฐ ์ฌ๋ผ์ค๋ฉด ์๋ฒ ๋ด๋ถ์์ ์ฒ๋ฆฌํ ์ ์๋ ์ค๋ฅ๊ฐ ๋ฐ์ํ ๊ฒ์ผ๋ก ์ธ์งํ๊ณ
HTTP Status 500 โ Internal Server Error ๋ฐํ
# ์คํ๋ง ๋ถํธ๊ฐ ์ ๊ณตํ๋ ๊ธฐ๋ณธ ์์ธ ํ์ด์ง OFF server.error.whitelabel.enabled=false
.
response.sendError(HTTP ์ํ ์ฝ๋, ์ค๋ฅ ๋ฉ์์ง)
@GetMapping("/error-404")
public void error404(HttpServletResponse response) throws IOException {
response.sendError(404, "404 ์ค๋ฅ!"); // HTTP ์ํ ์ฝ๋์ ์ค๋ฅ ๋ฉ์์ง ์ถ๊ฐ ๊ฐ๋ฅ
}
์ค๋ฅ ๋ฐ์ ์
HttpServletResponse.sendError
๋ฉ์๋ ์ฌ์ฉ ๊ฐ๋ฅsendError ํธ์ถ ์ ๋ฐ๋ก ์์ธ๊ฐ ๋ฐ์ํ๋ ๊ฒ์ ์๋์ง๋ง,
response ๋ด๋ถ์ ์ค๋ฅ ๋ฐ์ ์ํ๋ฅผ ์ ์ฅ
ํ์ฌ WAS(Servlet container) ์๊ฒ ์ ๋ฌWAS(Servlet container) ๋ ํด๋ผ์ด์ธํธ์๊ฒ ์๋ต ์ ์ response ์
sendError() ํธ์ถ ๊ธฐ๋ก ํ์ธ
ํ, ํธ์ถ๋์๋ค๋ฉด์ค์ ํ ์ค๋ฅ ์ฝ๋์ ๋ง๋ ๊ธฐ๋ณธ ์ค๋ฅ ํ์ด์ง ์ถ๋ ฅ
sendError ํ๋ฆ
WAS(sendError ํธ์ถ ๊ธฐ๋ก ํ์ธ) <- ํํฐ <- ์๋ธ๋ฆฟ <- ์ธํฐ์ ํฐ <- ์ปจํธ๋กค๋ฌ
์๋ธ๋ฆฟ ์ค๋ฅ ํ์ด์ง
์๋ธ๋ฆฟ์ Exception
์ด ์๋ธ๋ฆฟ ๋ฐ์ผ๋ก ์ ๋ฌ๋๊ฑฐ๋ response.sendError()
ํธ์ถ ์ ๊ฐ ์ํฉ์ ๋ง์ถ ์ค๋ฅ ์ฒ๋ฆฌ ๊ธฐ๋ฅ ์ ๊ณต
์๋ธ๋ฆฟ ์ค๋ฅ ํ์ด์ง ๋ฑ๋ก
@Component
public class WebServerCustomizer implements WebServerFactoryCustomizer<ConfigurableWebServerFactory> {
@Override
public void customize(ConfigurableWebServerFactory factory) {
ErrorPage errorPage404 = new ErrorPage(HttpStatus.NOT_FOUND, "/error-page/404"); // response.sendError(404)
ErrorPage errorPage500 = new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/error-page/500"); // response.sendError(500)
ErrorPage errorPageEx = new ErrorPage(RuntimeException.class, "/error-page/500"); // RuntimeException ๋๋ ๊ทธ ์์ ํ์
์ ์์ธ
factory.addErrorPages(errorPage404, errorPage500, errorPageEx);
}
}
์ค๋ฅ ํ์ด์ง๋ ์์ธ๋ฅผ ๋ค๋ฃฐ ๋ ํด๋น ์์ธ์ ๊ทธ ์์ ํ์ ์ ์ค๋ฅ๋ฅผ ํจ๊ป ์ฒ๋ฆฌ
์ค๋ฅ ์ฒ๋ฆฌ ์ปจํธ๋กค๋ฌ
@Slf4j
@Controller
public class ErrorPageController {
@RequestMapping("/error-page/404")
public String errorPage404(HttpServletRequest request, HttpServletResponse response) {
log.info("errorPage 404");
return "error-page/404";
}
@RequestMapping("/error-page/500")
public String errorPage500(HttpServletRequest request, HttpServletResponse response) {
log.info("errorPage 500");
return "error-page/500";
}
}
์ค๋ฅ ํ์ด์ง ์์ฒญ ํ๋ฆ
# ์์ธ ๋ฐ์ ํ๋ฆ
์ปจํธ๋กค๋ฌ(์์ธ ๋ฐ์) โ ์คํ๋ง ์ธํฐ์
ํฐ โ ์๋ธ๋ฆฟ โ ํํฐ โ WAS
# sendError ํ๋ฆ
์ปจํธ๋กค๋ฌ(response.sendError()) โ ์คํ๋ง ์ธํฐ์
ํฐ โ ์๋ธ๋ฆฟ โ ํํฐ โ WAS(sendError ํธ์ถ ๊ธฐ๋ก ํ์ธ)
---
# ์ค๋ฅ ํ์ด์ง ์์ฒญ ํ๋ฆ
WAS '/error-page/500' ์์ฒญ โ ํํฐ โ ์๋ธ๋ฆฟ โ ์คํ๋ง ์ธํฐ์
ํฐ โ ์ปจํธ๋กค๋ฌ(/error-page/500) โ View
WAS ๊น์ง ์์ธ๊ฐ ์ ํ๋ ๊ฒฝ์ฐ, WAS ๋ ํด๋น ์์ธ๋ฅผ ์ฒ๋ฆฌํ๋
์ค๋ฅ ํ์ด์ง ์ ๋ณด
ํ์ธ ํ ์ค๋ฅ ํ์ด์ง๋ฅผ ๋ค์ ์์ฒญWAS ๋ ์ค๋ฅ ํ์ด์ง ์์ฒญ ์ ์ค๋ฅ ์ ๋ณด๋ฅผ request.attribute ์ ์ถ๊ฐํด์ ์ ๋ฌ
private void printErrorInfo(HttpServletRequest request) {
log.info("ERROR_EXCEPTION: ex=", request.getAttribute(RequestDispatcher.ERROR_EXCEPTION));
log.info("ERROR_EXCEPTION_TYPE: {}", request.getAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE));
log.info("ERROR_MESSAGE: {}", request.getAttribute(RequestDispatcher.ERROR_MESSAGE)); // ex์ ๊ฒฝ์ฐ NestedServletException ์คํ๋ง์ด ํ๋ฒ ๊ฐ์ธ์ ๋ฐํ
log.info("ERROR_REQUEST_URI: {}", request.getAttribute(RequestDispatcher.ERROR_REQUEST_URI));
log.info("ERROR_SERVLET_NAME: {}", request.getAttribute(RequestDispatcher.ERROR_SERVLET_NAME));
log.info("ERROR_STATUS_CODE: {}", request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE));
log.info("dispatchType={}", request.getDispatcherType());
}
DispatcherType ๐
ํด๋ผ์ด์ธํธ๋ก ๋ถํฐ ๋ฐ์ํ ์ ์ ์์ฒญ์ธ์ง, ์ค๋ฅ ํ์ด์ง๋ฅผ ์ถ๋ ฅํ๊ธฐ ์ํ ๋ด๋ถ ์์ฒญ์ธ์ง ๊ตฌ๋ถํ๊ธฐ ์ํด ์๋ธ๋ฆฟ์ DispatcherType
์ ๋ณด ์ ๊ณต
์ค๋ฅ ๋ฐ์ ์ ์ค๋ฅ ํ์ด์ง ์ถ๋ ฅ์ ์ํด WAS ๋ด๋ถ์์ ํํฐ, ์๋ธ๋ฆฟ, ์ธํฐ์ ํฐ๋ฅผ ๋ค์ ํธ์ถ
์ด๋ฏธ ์ด๊ธฐ ์์ฒญ์์ ๊ฒ์ฆ์ด ์๋ฃ๋ ๋ถ๋ถ์ ์ฌํธ์ถ ๋ ๊ฒฝ์ฐ ๋นํจ์จ์
.
DispatcherType
์๋ธ๋ฆฟ ์คํ์ ์ค์ ๊ณ ๊ฐ์ด ์์ฒญํ ๊ฒ์ธ์ง, ์๋ฒ๊ฐ ๋ด๋ถ์์ ์ค๋ฅ ํ์ด์ง๋ฅผ ์์ฒญํ๋ ๊ฒ์ธ์ง
DispatcherType
์ผ๋ก ๊ตฌ๋ถํ ์ ์๋ ๋ฐฉ๋ฒ์ ์ ๊ณต
public enum DispatcherType {
FORWARD, // ๋ค๋ฅธ ์๋ธ๋ฆฟ์ด๋ JSP ํธ์ถ โ RequestDispatcher.forward(request, response)
INCLUDE, // ๋ค๋ฅธ ์๋ธ๋ฆฟ์ด๋ JSP ๊ฒฐ๊ณผ ํฌํจ โ RequestDispatcher.include(request, response)
REQUEST, // ํด๋ผ์ด์ธํธ ์์ฒญ
ASYNC, // ์๋ธ๋ฆฟ ๋น๋๊ธฐ ํธ์ถ
ERROR // ์ค๋ฅ ์์ฒญ
}
.
ํํฐ์ DispatcherType
DispatcherType ๋ก๊ทธ ํํฐ
@Slf4j public class LogFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { log.info("log filter init"); } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpRequest = (HttpServletRequest) request; String requestURI = httpRequest.getRequestURI(); String uuid = UUID.randomUUID().toString(); try { log.info("REQUEST [{}][{}][{}]", uuid, request.getDispatcherType(), requestURI); chain.doFilter(request, response); } catch (Exception e) { throw e; } finally { log.info("RESPONSE [{}][{}][{}]", uuid, request.getDispatcherType(), requestURI); } } @Override public void destroy() { log.info("log filter destroy"); } }
๋ก๊ทธ ํํฐ ๋ฑ๋ก
@Configuration public class DispatcherTypeWebConfig implements WebMvcConfigurer { @Bean public FilterRegistrationBean logFilter() { FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>(); filterRegistrationBean.setFilter(new LogFilter()); filterRegistrationBean.setOrder(1); filterRegistrationBean.addUrlPatterns("/*"); // default: REQUEST. ํด๋ผ์ด์ธํธ ์์ฒญ ์์๋ง ํํฐ ์ ์ฉ filterRegistrationBean.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ERROR); return filterRegistrationBean; } }
.
ํํฐ์ Interceptor
DispatcherType ๋ก๊ทธ ์ธํฐ์ ํฐ
@Slf4j public class LogInterceptor implements HandlerInterceptor { public static final String LOG_ID = "logId"; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String requestURI = request.getRequestURI(); String uuid = UUID.randomUUID().toString(); request.setAttribute(LOG_ID, uuid); log.info("REQUEST [{}][{}][{}][{}]", uuid, request.getDispatcherType(), requestURI, handler); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { log.info("postHandle [{}]", modelAndView); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { String requestURI = request.getRequestURI(); String logId = (String) request.getAttribute(LOG_ID); log.info("RESPONSE [{}][{}][{}]", logId, request.getDispatcherType(), requestURI); if (ex != null) { log.error("afterCompletion error!!", ex); } } }
๋ก๊ทธ ์ธํฐ์ ํฐ ๋ฑ๋ก
@Configuration
public class DispatcherTypeWebConfig implements WebMvcConfigurer {
/**
* ํํฐ๋ ํํฐ ๋ฑ๋ก ์ ํน์ DispatcherType ์ธ ๊ฒฝ์ฐ ํํฐ๊ฐ ์ ์ฉ๋๋๋ก ์ค์ ์ด ๊ฐ๋ฅํ์ง๋ง,
* ์ธํฐ์
ํฐ๋ ์คํ๋ง์ด ์ ๊ณตํ๋ ๊ธฐ๋ฅ์ด๋ผ์ DispatcherType ์ ๋ฌด๊ดํ๊ฒ ํญ์ ํธ์ถ
*
* ๋์ ์ธํฐ์
ํฐ์ excludePathPatterns ๋ฅผ ์ฌ์ฉํด์ ํน์ ๊ฒฝ๋ก ์ ์ธ ๊ฐ๋ฅ
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LogInterceptor())
.order(1)
.addPathPatterns("/**")
.excludePathPatterns(
"/css/**", "/*.ico"
, "/error", "/error-page/**" //์ค๋ฅ ํ์ด์ง ๊ฒฝ๋ก
);
}
}
.
DispatcherType ํ๋ฆ
1. WAS(/error-ex, dispatchType=REQUEST) โ ํํฐ โ ์๋ธ๋ฆฟ โ ์ธํฐ์
ํฐ โ ์ปจํธ๋กค๋ฌ
2. ์ปจํธ๋กค๋ฌ(์์ธ๋ฐ์) โ ์ธํฐ์
ํฐ โ ์๋ธ๋ฆฟ โ ํํฐ โ WAS
3. WAS ์ค๋ฅ ํ์ด์ง ํ์ธ
4. WAS(/error-page/500, dispatchType=ERROR) โ ํํฐ(x) โ ์๋ธ๋ฆฟ โ ์ธํฐ์
ํฐ(x) โ
์ปจํธ๋กค๋ฌ(/error-page/500) โ View
์คํ๋ง ๋ถํธ ์ค๋ฅ ํ์ด์ง ๐
์คํ๋ง ๋ถํธ๋ ์๋ธ๋ฆฟ ์ค๋ฅ ํ์ด์ง ํธ์ถ์ ํ์ํ๋ ์๋ ๋ณต์กํ ๊ณผ์ ์ ๊ธฐ๋ณธ์ผ๋ก ์ ๊ณต
WebServerCustomizer ๋ง๋ค๊ธฐ
์์ธ ์ข ๋ฅ์ ๋ฐ๋ผ ErrorPage ์ถ๊ฐ โ ErrorPage ์๋ ๋ฑ๋ก
/error
๊ฒฝ๋ก๋ฅผ ๊ธฐ๋ณธ ์ค๋ฅ ํ์ด์ง๋ก ์ค์ ์๋ธ๋ฆฟ ๋ฐ์ผ๋ก ์์ธ๊ฐ ๋์ ธ์ง๊ฑฐ๋,
response.sendError(...)
ํธ์ถ ์ ๋ชจ๋ ์ค๋ฅ๋/error
ํธ์ถErrorMvcAutoConfiguration
ํด๋์ค๊ฐ ์ค๋ฅ ํ์ด์ง๋ฅผ ์๋์ผ๋ก ๋ฑ๋กํ๋ ์ญํ
์์ธ ์ฒ๋ฆฌ์ฉ ์ปจํธ๋กค๋ฌ(ErrorPageController) ์์ฑ โ ์๋ ๋ฑ๋ก(
BasicErrorController
)ErrorPage ์์ ๋ฑ๋กํ /error ๋ฅผ ๋งคํํด์ ์ฒ๋ฆฌํ๋ ์ปจํธ๋กค๋ฌ
.
BasicErrorController
๊ธฐ๋ณธ์ ์ธ ์ค๋ฅ ํ์ด์ง ๋ก์ง์ด ๋ชจ๋ ๊ตฌํ
๊ฐ๋ฐ์๋ ์ค๋ฅ ํ์ด์ง ํ๋ฉด๋ง BasicErrorController ๊ฐ ์ ๊ณตํ๋ ๋ฃฐ๊ณผ ์ฐ์ ์์์ ๋ฐ๋ผ์ ๋ฑ๋ก
์ ์ HTML ์ผ ๊ฒฝ์ฐ ์ ์ ๋ฆฌ์์ค, ๋ทฐ ํ ํ๋ฆฟ์ ์ฌ์ฉํ ๋์ ์ค๋ฅ ํ๋ฉด์ผ ๊ฒฝ์ฐ ๋ทฐ ํ ํ๋ฆฟ ๊ฒฝ๋ก์ ์ค๋ฅ ํ์ด์ง ํ์ผ ์์ฑ
.
BasicErrorController View ์ ํ ์ฐ์ ์์
.1. ๋ทฐ ํ ํ๋ฆฟ
resources/templates/error/500.html
resources/templates/error/5xx.html
.2. ์ ์ ๋ฆฌ์์ค(static, public)
resources/static/error/400.html
resources/static/error/404.html
resources/static/error/4xx.html
.3. ์ ์ฉ ๋์์ด ์์ ๋ ๋ทฐ ์ด๋ฆ(error)
resources/templates/error.html
ํด๋น ๊ฒฝ๋ก ์์น์ HTTP ์ํ ์ฝ๋ ์ด๋ฆ์ ๋ทฐ ํ์ผ์ ๋ฃ์ด๋์.
๋ทฐ ํ ํ๋ฆฟ์ด ์ ์ ๋ฆฌ์์ค๋ณด๋ค ์ฐ์ ์์๊ฐ ๋๊ณ ,
404, 500 ์ฒ๋ผ ๊ตฌ์ฒด์ ์ธ ๊ฒ์ด 5xx์ฒ๋ผ ๋ ๊ตฌ์ฒด์ ์ธ ๊ฒ ๋ณด๋ค ์ฐ์ ์์๊ฐ ๋๋ค.
.
BasicErrorController ์ ๊ณต ๊ธฐ๋ณธ ์ ๋ณด
๊ธฐ๋ณธ ์ ๋ณด๋ฅผ model ์ ๋ด์ View ์ ์ ๋ฌ
timestamp: Fri Feb 05 00:00:00 KST 2021
path: `/hello` (Client ์์ฒญ ๊ฒฝ๋ก )
status: 400
message: Validation failed for object='data'. Error count: 1
error: Bad Request
exception: org.springframework.validation.BindException
errors: Errors(BindingResult)
trace: ์์ธ trace
message
,exception
,errors
,trace
์ ๋ณด๋ ๋ณด์์ default ๋ก ํฌํจ์ด ๋์ด์์ง ์์properties ์ค์ ์ ํตํด ์ค๋ฅ ์ ๋ณด๋ฅผ model ์ ํฌํจํ ์ง ์ฌ๋ถ ์ ํ
# exception ํฌํจ ์ฌ๋ถ(true, false) server.error.include-exception=true # message ํฌํจ ์ฌ๋ถ server.error.include-message=always # trace ํฌํจ ์ฌ๋ถ server.error.include-stacktrace=always # errors ํฌํจ ์ฌ๋ถ server.error.include-binding-errors=always
never
: ์ฌ์ฉํ์ง ์์always
: ํญ์ ์ฌ์ฉon_param
: ํ๋ผ๋ฏธํฐ๊ฐ ์์ ๋ ํด๋น ์ ๋ณด ๋ ธ์ถHTTP ์์ฒญ์ ํ๋ผ๋ฏธํฐ(?message=&errorsa=&trace=)๋ฅผ ์ ๋ฌํ๋ฉด ํด๋น ์ ๋ณด๋ค์ด model ์ ๋ด๊ฒจ ๋ทฐ ํ ํ๋ฆฟ์ ์ถ๋ ฅ
์ด์ ์๋ฒ์์๋ ๋น๊ถ์ฅ
์ค๋ฌด์์๋ ์ด ์ ๋ณด๋ค์ ๋ ธ์ถํ๋ฉด ์๋๋ค.
์ฌ์ฉ์์๊ฒ๋ ๊น๋ํ ์ค๋ฅ ํ์ด์ง์ ๊ณ ๊ฐ์ด ์ดํดํ ์ ์๋ ๊ฐ๋จํ ์ค๋ฅ ๋ฉ์์ง๋ฅผ ๋ณด์ฌ์ฃผ๊ณ ,
์ค๋ฅ๋ ์๋ฒ์ ๋ก๊ทธ๋ก ๋จ๊ฒจ์ ๋ก๊ทธ๋ก ํ์ธํ์.
.
์คํ๋ง ๋ถํธ ์ค๋ฅ ๊ด๋ จ ์ต์
# ์ค๋ฅ ์ฒ๋ฆฌ ํ๋ฉด์ ๋ชป ์ฐพ์ ๊ฒฝ์ฐ, ์คํ๋ง whitelabel ์ค๋ฅ ํ์ด์ง ์ ์ฉ
server.error.whitelabel.enabled=true
# ์ค๋ฅ ํ์ด์ง ๊ฒฝ๋ก
# ์คํ๋ง์ด ์๋ ๋ฑ๋กํ๋ ์๋ธ๋ฆฟ ๊ธ๋ก๋ฒ ์ค๋ฅ ํ์ด์ง ๊ฒฝ๋ก์ BasicErrorController ์ค๋ฅ ์ปจํธ๋กค๋ฌ ๊ฒฝ๋ก์ ํจ๊ป ์ฌ์ฉ
server.error.path=/error
ํ์ฅ ํฌ์ธํธ
์๋ฌ ๊ณตํต ์ฒ๋ฆฌ ์ปจํธ๋กค๋ฌ์ ๊ธฐ๋ฅ์ ๋ณ๊ฒฝํ๊ณ ์ถ์ ๊ฒฝ์ฐ ErrorController ์ธํฐํ์ด์ค๋ฅผ ์์ ๋ฐ์์ ๊ตฌํํ๊ฑฐ๋, BasicErrorController ๋ฅผ ์์ ๋ฐ์์ ๊ธฐ๋ฅ์ ์ถ๊ฐํด๋ณด์.
์คํ๋ง ๋ถํธ ๊ธฐ๋ณธ ์ ๊ณต ์ค๋ฅ ํ์ด์ง๋ฅผ ํ์ฉํ๋ฉด ์ค๋ฅ ํ์ด์ง ๊ด๋ จ ๋๋ถ๋ถ์ ๋ฌธ์ ๋ ์์ฝ๊ฒ ํด๊ฒฐ ๊ฐ๋ฅํ๋ค.
API ์์ธ ์ฒ๋ฆฌ
์ค๋ฅ ํ์ด์ง
๋ ๋จ์ํ ๊ณ ๊ฐ์๊ฒ ์ค๋ฅ ํ๋ฉด์ ๋ณด์ฌ์ฃผ๋ฉด ๋์ง๋ง, API
๋ ๊ฐ ์ค๋ฅ ์ํฉ์ ๋ง๋ ์ค๋ฅ ์๋ต ์คํ์ ์ ํ๊ณ , JSON์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ์๋ตํด ์ฃผ์ด์ผ ํ๋ค.
produces ์ค์
@RequestMapping(value = "/error-page/500", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Map<String, Object>> errorPage500Api(HttpServletRequest request, HttpServletResponse response) {
// ...
return new ResponseEntity(result, HttpStatus.valueOf(statusCode));
}
produces = MediaType.APPLICATION_JSON_VALUE
ํด๋ผ์ด์ธํธ๊ฐ ์์ฒญํ๋ HTTP Header Accept ๊ฐ์ด application/json ์ผ ๋ ํด๋น ๋ฉ์๋ ํธ์ถ
ResponseEntity ๋ ๋ฉ์์ง ์ปจ๋ฒํฐ๊ฐ ๋์ํ๋ฉด์ ํด๋ผ์ด์ธํธ์๊ฒ JSON ๊ตฌ์กฐ๋ก ๋ณํํ์ฌ ๋ฐํ
Spring Boot ๊ธฐ๋ณธ ์ค๋ฅ ์ฒ๋ฆฌ
API ์์ธ ์ฒ๋ฆฌ๋ ์คํ๋ง ๋ถํธ๊ฐ ์ ๊ณตํ๋ ๊ธฐ๋ณธ ์ค๋ฅ ๋ฐฉ์์ ์ฌ์ฉ
BasicErrorController
์ฝ๋ ์ผ๋ถ
// ํด๋ผ์ด์ธํธ ์์ฒญ์ Accept ํด๋ ๊ฐ์ด text/html ์ธ ๊ฒฝ์ฐ ํธ์ถ(view ์ ๊ณต)
@RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) { ... }
// ๊ทธ์ธ ๊ฒฝ์ฐ์ ํธ์ถ(ResponseEntity ๋ก HTTP Body ์ JSON ๋ฐ์ดํฐ ๋ฐํ)
@RequestMapping
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) { ... }
์คํ๋ง ๋ถํธ์ ๊ธฐ๋ณธ ์ค์ ์ ์ค๋ฅ ๋ฐ์์
/error
๋ฅผ ์ค๋ฅ ํ์ด์ง๋ก ์์ฒญBasicErrorController
๋/error
๊ฒฝ๋ก๋ฅผ ๊ธฐ๋ณธ์ผ๋ก ๋ฐ์.(server.error.path
๋ก ์์ ๊ฐ๋ฅ
BasicErrorController
๋ HTML ์ค๋ฅ ํ์ด์ง๋ฅผ ์ ๊ณตํ๋ ๊ฒฝ์ฐ ๋งค์ฐ ํธ๋ฆฌ๋จ, API ๋ ๊ฐ ์ปจํธ๋กค๋ฌ๋ ์์ธ๋ง๋ค ์๋ก ๋ค๋ฅธ ์๋ต ๊ฒฐ๊ณผ๋ฅผ ์ถ๋ ฅํด์ผ ํ๋ฏ๋ก
@ExceptionHandler
์ฌ์ฉ ๊ถ์ฅ
ExceptionResolver
HandlerExceptionResolver
public interface HandlerExceptionResolver {
ModelAndView resolveException(
HttpServletRequest request,
HttpServletResponse response,
Object handler, // ํธ๋ค๋ฌ(์ปจํธ๋กค๋ฌ) ์ ๋ณด
Exception ex // ํธ๋ค๋ฌ(์ปจํธ๋กค๋ฌ)์์ ๋ฐ์ํ ์์ธ
);
}
์คํ๋ง MVC ๋ ์ปจํธ๋กค๋ฌ(ํธ๋ค๋ฌ) ๋ฐ์ผ๋ก ์์ธ๊ฐ ๋์ ธ์ง ๊ฒฝ์ฐ ์์ธ๋ฅผ ํด๊ฒฐํ๊ณ , ๋์์ ์๋ก ์ ์ํ ์ ์๋
HandlerExceptionResolver
์ ๊ณตExceptionResolver ์ ์ฉ ์ ์์ธ์ฒ๋ฆฌ
Result ExceptionResolver ์ ์ฉ ํ ์์ธ์ฒ๋ฆฌ
Result
์ฐธ๊ณ . ExceptionResolver ๋ก ์์ธ๋ฅผ ํด๊ฒฐํด๋ postHandle() ํธ์ถ X
HandlerExceptionResolver Interface ๊ตฌํ
@Slf4j
public class MyHandlerExceptionResolver implements HandlerExceptionResolver {
/**
* ModelAndView ๋ฅผ ๋ฐํํ๋ ์ด์ ๋ try-catch ์ฒ๋ผ Exception ์ ์ฒ๋ฆฌํด์ ์ ์ ํ๋ฆ์ฒ๋ผ ๋ณ๊ฒฝํ์ฌ ์์ธ๋ฅผ ํด๊ฒฐํ๋ ๊ฒ์ด ๋ชฉ์
*/
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
try {
// IllegalArgumentException ๋ฐ์ ์ ์์ธ๋ฅผ HTTP ์ํ ์ฝ๋ 400์ผ๋ก ์ ๋ฌ
if (ex instanceof IllegalArgumentException) {
log.info("IllegalArgumentException resolver to 400");
response.sendError(HttpServletResponse.SC_BAD_REQUEST, ex.getMessage());
//1. ๋น ModelAndView ๋ฐํ ์ ๋ทฐ ๋ ๋๋ง์ ํ์ง ์๊ณ ์ ์ ํ๋ฆ์ผ๋ก ์๋ธ๋ฆฟ ๋ฐํ
//2. ModelAndView ์ View, Model ๋ฑ์ ์ ๋ณด๋ฅผ ์ง์ ํ์ฌ ๋ฐํํ๋ฉด ๋ทฐ ๋ ๋๋ง
return new ModelAndView();
}
} catch (IOException e) {
log.error("resolver ex", e);
}
//3. null ๋ฐํ ์ ๋ค์ ExceptionResolver ์ฐพ์์ ์คํ
//์ฒ๋ฆฌ ๊ฐ๋ฅํ ExceptionResolver ๊ฐ ์์ ๊ฒฝ์ฐ ๊ธฐ์กด ๋ฐ์ํ ์์ธ๋ฅผ ์๋ธ๋ฆฟ ๋ฐ์ผ๋ก ์ ๋ฌ
return null;
}
}
.
HandlerExceptionResolver ๋ฐํ ๊ฐ์ ๋ฐ๋ฅธ DispatcherServlet ๋์ ๋ฐฉ์
๋น ModelAndView
new ModelAndView() ์ฒ๋ผ ๋น ModelAndView ๋ฅผ ๋ฐํํ๋ฉด ๋ทฐ๋ฅผ ๋ ๋๋ง ํ์ง์๊ณ , ์ ์ ํ๋ฆ์ผ๋ก ์๋ธ๋ฆฟ์ด ๋ฆฌํด
ModelAndView ์ง์
ModelAndView ์ View, Model ๋ฑ์ ์ ๋ณด๋ฅผ ์ง์ ํด์ ๋ฐํํ๋ฉด ๋ทฐ๋ฅผ ๋ ๋๋ง
nul
null ์ ๋ฐํํ๋ฉด, ๋ค์ ExceptionResolver ๋ฅผ ์ฐพ์์ ์คํ
๋ง์ฝ ์ฒ๋ฆฌํ ์ ์๋ ExceptionResolver ๊ฐ ์์ผ๋ฉด ์์ธ ์ฒ๋ฆฌ๊ฐ ์๋๊ณ , ๊ธฐ์กด์ ๋ฐ์ํ ์์ธ๋ฅผ ์๋ธ๋ฆฟ ๋ฐ์ผ๋ก ๋์ง
.
ExceptionResolver ํ์ฉ
์์ธ ์ํ ์ฝ๋ ๋ณํ
์์ธ๋ฅผ response.sendError(..) ํธ์ถ๋ก ๋ณ๊ฒฝํด์ ์๋ธ๋ฆฟ์์ ์ํ ์ฝ๋์ ๋ฐ๋ฅธ ์ค๋ฅ๋ฅผ ์ฒ๋ฆฌํ๋๋ก ์์
์ดํ WAS ๋ ์๋ธ๋ฆฟ ์ค๋ฅ ํ์ด์ง๋ฅผ ์ฐพ์์ ๋ด๋ถ ํธ์ถ(default. /error)
ex. ์ค์ ์๋ฒ์์๋ 500 ์๋ฌ๊ฐ ๋ฐ์ํ์์ง๋ง Client ์๊ฒ๋ 4xx ์ฝ๋ ์ ๋ฌ
๋ทฐ ํ ํ๋ฆฟ ์ฒ๋ฆฌ
ModelAndView ์ ๊ฐ์ ์ฑ์์ ์์ธ์ ๋ฐ๋ฅธ ์๋ก์ด ์ค๋ฅ ํ๋ฉด์ ๋ทฐ ๋ ๋๋ง์ ํตํด ๊ณ ๊ฐ์๊ฒ ์ ๊ณต
return new ModelAndView("error/400");
API ์๋ต ์ฒ๋ฆฌ
response.getWriter().println("hello"); ์ฒ๋ผ HTTP ์๋ต ๋ฐ๋์ ์ง์ ๋ฐ์ดํฐ๋ฅผ ๋ฃ์ด์ ์ ๋ฌ
์ฌ๊ธฐ์ JSON ์ผ๋ก ์๋ตํ๋ฉด API ์๋ต ์ฒ๋ฆฌ
.
WebMvcConfigurer ์ ๋ฑ๋ก
@Override
public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
resolvers.add(new MyHandlerExceptionResolver());
}
configureHandlerExceptionResolvers
์ฌ์ฉ ์ ์คํ๋ง์ด ๊ธฐ๋ณธ์ผ๋ก ๋ฑ๋กํ๋ExceptionResolver
๊ฐ ์ ๊ฑฐ๋๋ฏ๋กextendHandlerExceptionResolvers
๋ฅผ ์ฌ์ฉ
ExceptionResolver ์ ์ฉ
Exception
public class UserException extends RuntimeException {
public UserException() {
super();
}
public UserException(String message) {
super(message);
}
public UserException(String message, Throwable cause) {
super(message, cause);
}
public UserException(Throwable cause) {
super(cause);
}
protected UserException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
...
// ์์ธ ์์ฑ
throw new UserException("์ฌ์ฉ์ ์ค๋ฅ");
HandlerExceptionResolver ๊ตฌํ
@Slf4j
public class UserHandlerExceptionResolver implements HandlerExceptionResolver {
private final ObjectMapper objectMapper = new ObjectMapper();
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
try {
if (ex instanceof UserException) {
log.info("UserException resolver to 400");
String acceptHeader = request.getHeader("accept");
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
// HTTP ์์ฒญ ํด๋์ ACCEPT ๊ฐ์ด application/json ์ผ ๊ฒฝ์ฐ JSON ์๋ต
if ("application/json".equals(acceptHeader)) {
Map<String, Object> errorResult = new HashMap<>();
errorResult.put("ex", ex.getClass());
errorResult.put("message", ex.getMessage());
String result = objectMapper.writeValueAsString(errorResult);
response.setContentType("application/json");
response.setCharacterEncoding("utf-8");
response.getWriter().write(result);
return new ModelAndView();
} else { // ๊ทธ ์ธ์ ๊ฒฝ์ฐ์๋ TEXT/HTML ์ค๋ฅ ํ์ด์ง ๋
ธ์ถ
return new ModelAndView("error/500");
}
}
} catch (IOException e) {
log.error("resolver ex", e);
}
return null;
}
}
WebConfig ์ HandlerExceptionResolver ๋ฑ๋ก
@Override
public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
resolvers.add(new MyHandlerExceptionResolver());
resolvers.add(new UserHandlerExceptionResolver());
}
์ ๋ฆฌ.
์ปจํธ๋กค๋ฌ์์ ์์ธ๊ฐ ๋ฐ์ํด๋ ExceptionResolver ์์ ์์ธ๋ฅผ ์ฒ๋ฆฌ
์์ธ๊ฐ ๋ฐ์ํด๋ ์๋ธ๋ฆฟ ์ปจํ ์ด๋๊น์ง ์์ธ๊ฐ ์ ๋ฌ๋์ง ์๊ณ (๊ฒฐ๊ณผ์ ์ผ๋ก WAS ์ ์ฅ์์๋ ์ ์ ์ฒ๋ฆฌ)
์คํ๋ง MVC ์์ ์์ธ ์ฒ๋ฆฌ๋ ์ข ๋ฃ
Spring ExceptionResolver
Spring Boot ๊ธฐ๋ณธ์ ์ผ๋ก ์ ๊ณตํ๋ ExceptionResolver
HandlerExceptionResolverComposite
์ ์๋ ์์๋ก ๋ฑ๋ก
ExceptionHandlerExceptionResolver
@ExceptionHandler
์ฒ๋ฆฌAPI ์์ธ ์ฒ๋ฆฌ๋ ๋๋ถ๋ถ ์ด ๊ธฐ๋ฅ์ผ๋ก ํด๊ฒฐ
ResponseStatusExceptionResolver
HTTP ์ํ ์ฝ๋ ๋ณ๊ฒฝ
DefaultHandlerExceptionResolver
์คํ๋ง ๋ด๋ถ ๊ธฐ๋ณธ ์์ธ ์ฒ๋ฆฌ
ExceptionHandler ๐
ExceptionHandlerExceptionResolver
์คํ๋ง์ API ์์ธ ์ฒ๋ฆฌ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด @ExceptionHandler
๋ฅผ ์ฌ์ฉํ ํธ๋ฆฌํ ์์ธ ์ฒ๋ฆฌ ๊ธฐ๋ฅ ์ ๊ณต
๊ฐ ์์คํ ๋ง๋ค ๋ค๋ฅธ ์๋ต ๋ชจ์๊ณผ ์คํ
์์ธ์ ๋ฐ๋ฅธ ๊ฐ๊ธฐ ๋ค๋ฅธ ๋ฐ์ดํฐ ์๋ต
์ปจํธ๋กค๋ฌ์ ๋ฐ๋ผ ๋ค๋ฅธ ์์ธ ์๋ต
ModelAndView ๊ฐ ์๋ Json ํํ๋ก ๋ฐํ
๋ฑ.. ์ธ๋ฐํ ์ ์ด ํ์
.
@ExceptionHandler
์์ธ ์ฒ๋ฆฌ ๋ฐฉ๋ฒ
@ExceptionHandler ์ ์ธ ํ ํด๋น ์ปจํธ๋กค๋ฌ์์ ์ฒ๋ฆฌํ๊ณ ์ถ์ ์์ธ ์ง์
ํด๋น ์ปจํธ๋กค๋ฌ์์ ์์ธ ๋ฐ์ ์ ํด๋น ๋ฉ์๋๊ฐ ํธ์ถ
์ง์ ํ ์์ธ ๋๋ ํ์ ์์ ํด๋์ค ๋ชจ๋ ์ฒ๋ฆฌ
/** * ๋ถ๋ชจ, ์์ ํด๋์ค ๋ชจ๋ ์ง์ ๋์ด ์์ ๊ฒฝ์ฐ ์์ธํ ๊ฒ์ด ์ฐ์ ๊ถ */ @ExceptionHandler(๋ถ๋ชจ์์ธ.class) public String ๋ถ๋ชจ์์ธ์ฒ๋ฆฌ()(๋ถ๋ชจ์์ธ e) {} @ExceptionHandler(์์์์ธ.class) public String ์์์์ธ์ฒ๋ฆฌ()(์์์์ธ e) {}
๋ค์ํ ์์ธ๋ฅผ ํ ๋ฒ์ ์ฒ๋ฆฌ ๊ฐ๋ฅ
@ExceptionHandler({AException.class, BException.class}) public String ex(Exception e) { log.info("exception e", e); }
์์ธ ์๋ต
/** * ์์ธ ์๋ต ์ ๋ฉ์๋ ํ๋ผ๋ฏธํฐ์ ์์ธ(UserException)๊ฐ ์ง์ */ @ExceptionHandler public ResponseEntity<ErrorResult> userExHandle(UserException e) {}
ํ๋ผ๋ฏธํฐ์ ์๋ต
๋ค์ํ ํ๋ผ๋ฏธํฐ์ ์๋ต ์ง์ ๊ฐ๋ฅ
.
@ExceptionHandler
์คํ ํ๋ฆ
throw new IllegalArgumentException("์๋ชป๋ ์
๋ ฅ ๊ฐ");
...
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(IllegalArgumentException.class)
public ErrorResult illegalExHandle(IllegalArgumentException e) {
return new ErrorResult("BAD", e.getMessage());
}
์ปจํธ๋กค๋ฌ ํธ์ถ ๊ฒฐ๊ณผ๋ก ์์ธ(IllegalArgumentException)๊ฐ ์ปจํธ๋กค๋ฌ ๋ฐ์ผ๋ก ๋์ ธ์ง
DispatcherServlet ์ ๊ฑฐ์ณ ์์ธ ๋ฐ์์ผ๋ก
ExceptionResolver
์๋๊ฐ์ฅ ์ฐ์ ์์๊ฐ ๋์
ExceptionHandlerExceptionResolver
์คํ
ExceptionHandlerExceptionResolver
๋ ํด๋น ์ปจํธ๋กค๋ฌ์IllegalArgumentException
์ ์ฒ๋ฆฌํ ์ ์๋@ExceptionHandler
๊ฐ ์๋์ง ํ์ธ@ExceptionHandler
์ ์ธ ๋ฉ์๋ ์คํ@RestController ์ด๋ฏ๋ก @ResponseBody ์ ์ฉ โ HTTP ์ปจ๋ฒํฐ๊ฐ ์ฌ์ฉ๋๊ณ JSON ์๋ต
@ResponseStatus(HttpStatus.BAD_REQUEST) ๋ฅผ ์ง์ ํ์ผ๋ฏ๋ก HTTP ์ํ ์ฝ๋ 400 ์๋ต
์๋ธ๋ฆฟ ์ปจํ ์ด๋๊น์ง ๋ด๋ ค๊ฐ์ง ์๊ณ ์ ์ ํ๋ฆ์ผ๋ก ๋ฐํ
.
์ํฉ์ ๋ฐ๋ฅธ @ExceptionHandler
ํ์ฉ
/**
* ์์ธ ์ฒ๋ฆฌ์ฉ ํด๋์ค๋ฅผ ๋ง๋ค์ด์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ
* ํ์ฌ Controller ์์ IllegalArgumentException ๋ฐ์ ์ ํธ์ถ
*
* @ResponseStatus ๋ ์ ๋
ธํ
์ด์
์ด๋ฏ๋ก HTTP ์๋ต ์ฝ๋๋ฅผ ๋์ ์ผ๋ก ๋ณ๊ฒฝ ๋ถ๊ฐ
*/
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(IllegalArgumentException.class)
public ErrorResult illegalExHandle(IllegalArgumentException e) {
return new ErrorResult("BAD", e.getMessage());
}
/**
* ํ์ฌ Controller ์์ UserException ๋ฐ์ ์ ํธ์ถ
*
* ResponseEntity ๋ฅผ ์ฌ์ฉํด์ HTTP ๋ฉ์์ง ๋ฐ๋์ ์ง์ ์๋ต(HTTP ์ปจ๋ฒํฐ ์ฌ์ฉ)
* HTTP ์๋ต ์ฝ๋๋ฅผ ํ๋ก๊ทธ๋๋ฐํด์ ๋์ ์ผ๋ก ๋ณ๊ฒฝ ๊ฐ๋ฅ
*/
@ExceptionHandler
public ResponseEntity<ErrorResult> userExHandle(UserException e) {
ErrorResult errorResult = new ErrorResult("USER-EX", e.getMessage());
return new ResponseEntity<>(errorResult, HttpStatus.BAD_REQUEST);
}
/**
* ํ์ฌ Controller ์์ RuntimeException(Exception ์ ์์ ํด๋์ค) ๋ฐ์ ์ ํธ์ถ
*
* ์ฒ๋ฆฌ๋์ง ๋ชปํ ๋จ์ ์์ธ๋ฅผ ์ฒ๋ฆฌ
*/
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler
public ErrorResult exHandle(Exception e) {
return new ErrorResult("EX", "๋ด๋ถ ์ค๋ฅ");
}
/**
* ModelAndView ๋ฅผ ์ฌ์ฉํด์ ์ค๋ฅ ํ๋ฉด(HTML) ์๋ต
*/
@ExceptionHandler(ViewException.class)
public ModelAndView ex(ViewException e) {
return new ModelAndView("error");
}
@ControllerAdvice ๐
์ฌ๋ฌ ์ปจํธ๋กค๋ฌ์์ ๋ฐ์ํ๋ ์ค๋ฅ๋ฅผ ๋ชจ์์ ์ฒ๋ฆฌ
@ExceptionHandler
๋ฅผ ์ฌ์ฉํด์ ์์ธ๋ฅผ ๊น๋ํ๊ฒ ์ฒ๋ฆฌ๊ฐ ๊ฐ๋ฅํ์ง๋ง, ์ ์ ์ฝ๋์ ์์ธ ์ฒ๋ฆฌ ์ฝ๋๊ฐ ํ๋์ ์ปจํธ๋กค๋ฌ์ ์์ฌ ์๋ ๋จ์ ์ด ์กด์ฌ@ControllerAdvice
๋๋@RestControllerAdvice
๋ฅผ ์ฌ์ฉํด์ ๋ถ๋ฆฌํด ๋ณด์.
@ControllerAdvice
@RestControllerAdvice
public class ExControllerAdvice {
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(IllegalArgumentException.class)
public ErrorResult illegalExHandle(IllegalArgumentException e) {
return new ErrorResult("BAD", e.getMessage());
}
@ExceptionHandler
public ResponseEntity<ErrorResult> userExHandle(UserException e) {
ErrorResult errorResult = new ErrorResult("USER-EX", e.getMessage());
return new ResponseEntity<>(errorResult, HttpStatus.BAD_REQUEST);
}
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler
public ErrorResult exHandle(Exception e) {
return new ErrorResult("EX", "๋ด๋ถ ์ค๋ฅ");
}
}
๋์์ผ๋ก ์ง์ ํ ์ฌ๋ฌ ์ปจํธ๋กค๋ฌ์
@ExceptionHandler
,@InitBinder
๊ธฐ๋ฅ์ ๋ถ์ฌ๋์ ์ปจํธ๋กค๋ฌ ์ง์ โ ๋ณดํต ํจํค์ง๋ช ์ ๋ ์ง์
// Target all Controllers annotated with @RestController @ControllerAdvice(annotations = RestController.class) public class ExampleAdvice1 {} // Target all Controllers within specific packages @ControllerAdvice("org.example.controllers") public class ExampleAdvice2 {} // Target all Controllers assignable to specific classes @ControllerAdvice(assignableTypes = {ControllerInterface.class, AbstractController.class}) public class ExampleAdvice3 {}
@ControllerAdvice ์ ๋์์ ์ง์ ํ์ง ์์ผ๋ฉด ๋ชจ๋ ์ปจํธ๋กค๋ฌ์ ์ ์ฉ(๊ธ๋ก๋ฒ ์ ์ฉ)
@RestControllerAdvice
๋ @ControllerAdvice ์ ๋์ผํ๊ณ , @ResponseBody ๊ฐ ์ถ๊ฐ@Controller, @RestController ์ฐจ์ด์ ๋์ผ
@ExceptionHandler ์ @ControllerAdvice ๋ฅผ ์กฐํฉํ๋ฉด ์์ธ๋ฅผ ๊น๋ํ๊ฒ ํด๊ฒฐ ๊ฐ๋ฅ
ResponseStatusExceptionResolver
์์ธ์ ๋ฐ๋ผ HTTP ์ํ ์ฝ๋ ์ง์
@ResponseStatus
๊ฐ ๋ฌ๋ ค์๋ ์์ธResponseStatusException
์์ธ
.
@ResponseStatus ์์ธ
@ResponseStatus(code = HttpStatus.BAD_REQUEST, reason = "์๋ชป๋ ์์ฒญ ์ค๋ฅ")
public class BadRequestException extends RuntimeException { }
...
throw new BadRequestException();
ํด๋น ์์ธ๊ฐ ์ปจํธ๋กค๋ฌ ๋ฐ์ผ๋ก ๋์ด๊ฐ๋ฉด ResponseStatusExceptionResolver ์์ธ๊ฐ ํด๋น ์ ๋ ธํ ์ด์ ์ ํ์ธํด์
HTTP ์ํ ์ฝ๋๋ฅผ ๋ณ๊ฒฝ
(HttpStatus.BAD_REQUEST(400))ํ๊ณ , ๋ฉ์์ง ํฌํจResponseStatusExceptionResolver ์์ response.sendError(statusCode, resolvedReason) ํธ์ถ
sendError(400) ํธ์ถ๋ก WAS ์์ ๋ค์
์ค๋ฅ ํ์ด์ง
(/error)๋ด๋ถ ์์ฒญ
reason ์ MessageSource ์์ ์ฐพ๋
๋ฉ์ธ์ง ๊ธฐ๋ฅ
์ ๊ณต โreason = "error.bad"
`
ResponseStatusException ์์ธ
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "error.bad", new IllegalArgumentException());
์ง์ ๋ณ๊ฒฝํ ์ ์๋ ์์ธ์
ResponseStatusException
์ ์ฉ@ResponseStatus ๋ ์ ๋ ธํ ์ด์ ์ ์ฌ์ฉํ๋ฏ๋ก ์ง์ (์กฐ๊ฑด์ ๋ฐ๋ผ ๋์ ์ผ๋ก)๋ณ๊ฒฝํ ์ ์๋ ์์ธ์๋ ์ ์ฉ ๋ถ๊ฐ
DefaultHandlerExceptionResolver
์คํ๋ง ๋ด๋ถ์์ ๋ฐ์ํ๋ ์คํ๋ง ์์ธ ์ฒ๋ฆฌ
์คํ๋ง ๋ด๋ถ ์ค๋ฅ๋ฅผ ์ด๋ป๊ฒ ์ฒ๋ฆฌํ ์ง์ ๋ํ ๋ง์ ๋ด์ฉ์ด ์ ์
TypeMismatchException
์ผ๋ก ๋ฐ์ํ๋ 500 ์ค๋ฅ๋ฅผDefaultHandlerExceptionResolver
๊ฐ 400 ์ค๋ฅ๋ก ๋ณ๊ฒฝ
DefaultHandlerExceptionResolver.handleTypeMismatch
protected ModelAndView handleTypeMismatch(TypeMismatchException ex, HttpServletRequest request, HttpServletResponse response, @Nullable Object handler) throws IOException {
// response.sendError() ๋ฅผ ํตํด ๋ฌธ์ ํด๊ฒฐ.
// sendError(400) ๋ฅผ ํธ์ถํ์ผ๋ฏ๋ก WAS ์์ ๋ค์ ์ค๋ฅ ํ์ด์ง(/error) ๋ด๋ถ ์์ฒญ
response.sendError(HttpServletResponse.SC_BAD_REQUEST);
return new ModelAndView();
}
Last updated