ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • API ์˜ˆ์™ธ ์ฒ˜๋ฆฌ - HandlerExceptionResolver(1)
    ๊ฐœ๋ฐœ/Spring Boot 2022. 7. 7. 22:55

     

     

    ์Šคํ”„๋ง ๋ถ€ํŠธ์˜ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ 


    ์Šคํ”„๋ง ๋ถ€ํŠธ์˜ ๊ธฐ๋ณธ ์„ค์ •์€ ์˜ค๋ฅ˜ ๋ฐœ์ƒ์‹œ /error ๋ฅผ ์˜ค๋ฅ˜ ํŽ˜์ด์ง€๋กœ ์š”์ฒญํ•œ๋‹ค. BasicErrorController ๋Š” ์ด ๊ฒฝ๋กœ๋ฅผ ๊ธฐ๋ณธ์œผ๋กœ ๋ฐ›๋Š”๋‹ค. ( server.error.path ๋กœ ์ˆ˜์ • ๊ฐ€๋Šฅ, ๊ธฐ๋ณธ ๊ฒฝ๋กœ / error )

    ์Šคํ”„๋ง ๋ถ€ํŠธ๋Š” BasicErrorController ๊ฐ€ ์ œ๊ณตํ•˜๋Š” ๊ธฐ๋ณธ ์ •๋ณด๋“ค์„ ํ™œ์šฉํ•ด์„œ ์˜ค๋ฅ˜ API๋ฅผ ์ƒ์„ฑํ•ด์ค€๋‹ค.

     

    ๋‹ค์Œ ์˜ต์…˜๋“ค์„ ์„ค์ •ํ•˜๋ฉด ๋” ์ž์„ธํ•œ ์˜ค๋ฅ˜ ์ •๋ณด๋ฅผ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋‹ค.

     

    server.error.include-binding-errors=always
    server.error.include-exception=true
    server.error.include-message=always
    server.error.include-stacktrace=always

    ๋ฌผ๋ก  ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€๋Š” ์ด๋ ‡๊ฒŒ ๋ง‰ ์ถ”๊ฐ€ํ•˜๋ฉด ๋ณด์•ˆ์ƒ ์œ„ํ—˜ํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ฐ„๊ฒฐํ•œ ๋ฉ”์‹œ์ง€๋งŒ ๋…ธ์ถœํ•˜๊ณ , ๋กœ๊ทธ๋ฅผ ํ†ตํ•ด์„œ ํ™•์ธํ•˜์ž.

     

     

    Html ํŽ˜์ด์ง€ vs API ์˜ค๋ฅ˜


    ์Šคํ”„๋ง ๋ถ€ํŠธ๊ฐ€ ์ œ๊ณตํ•˜๋Š” BasicErrorController ๋Š” HTML ํŽ˜์ด์ง€๋ฅผ ์ œ๊ณตํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š” ๋งค์šฐ ํŽธ๋ฆฌํ•˜๋‹ค. 4xx, 5xx ๋“ฑ๋“ฑ ๋ชจ๋‘ ์ž˜ ์ฒ˜๋ฆฌํ•ด์ค€๋‹ค. ๊ทธ๋Ÿฐ๋ฐ API ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ๋Š” ๋‹ค๋ฅธ ์ฐจ์›์˜ ์ด์•ผ๊ธฐ์ด๋‹ค. API ๋งˆ๋‹ค, ๊ฐ๊ฐ์˜ ์ปจํŠธ๋กค๋Ÿฌ๋‚˜ ์˜ˆ์™ธ๋งˆ๋‹ค ์„œ๋กœ ๋‹ค๋ฅธ ์‘๋‹ต ๊ฒฐ๊ณผ๋ฅผ ์ถœ๋ ฅํ•ด์•ผ ํ•  ์ˆ˜๋„ ์žˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด์„œ ํšŒ์›๊ณผ ๊ด€๋ จ๋œ API์—์„œ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•  ๋•Œ ์‘๋‹ต๊ณผ, ์ƒํ’ˆ๊ณผ ๊ด€๋ จ๋œ API์—์„œ ๋ฐœ์ƒํ•˜๋Š” ์˜ˆ์™ธ์— ๋”ฐ๋ผ ๊ทธ ๊ฒฐ๊ณผ๊ฐ€ ๋‹ฌ๋ผ์งˆ ์ˆ˜ ์žˆ๋‹ค. ๊ฒฐ๊ณผ์ ์œผ๋กœ ๋งค์šฐ ์„ธ๋ฐ€ํ•˜๊ณ  ๋ณต์žกํ•˜๋‹ค. (๋”ฐ๋ผ์„œ ์ด ๋ฐฉ๋ฒ•์€ HTML ํ™”๋ฉด์„ ์ฒ˜๋ฆฌํ•  ๋•Œ ์‚ฌ์šฉํ•˜๊ณ , ๊ฒฐ๋ก ์ ์œผ๋กœ API๋Š” ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ๋Š” ๋‹ค์Œ ๊ธ€์—์„œ ์„ค๋ช…ํ•  @ExceptionHandler ๋ฅผ ์‚ฌ์šฉํ•˜์ž.)

     

     

    HandlerExceptionResolver


    ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•ด์„œ ์„œ๋ธ”๋ฆฟ์„ ๋„˜์–ด WAS๊นŒ์ง€ ์˜ˆ์™ธ๊ฐ€ ์ „๋‹ฌ๋˜๋ฉด HTTP ์ƒํƒœ์ฝ”๋“œ๊ฐ€ 500์œผ๋กœ ์ฒ˜๋ฆฌ๋œ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ๋ฐœ์ƒํ•˜๋Š” ์˜ˆ์™ธ์— ๋”ฐ๋ผ์„œ 400, 404 ๋“ฑ๋“ฑ ๋‹ค๋ฅธ ์ƒํƒœ์ฝ”๋“œ๋„ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•  ๊ฒฝ์šฐ๋„ ์žˆ๊ณ , ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€๋‚˜ ํ˜•์‹๋“ฑ์„ API๋งˆ๋‹ค ๋‹ค๋ฅด๊ฒŒ ์ฒ˜๋ฆฌํ•˜๊ณ  ์‹ถ์€ ๊ฒฝ์šฐ๊ฐ€ ์žˆ๋‹ค. 

    ์Šคํ”„๋ง MVC๋Š” ์ปจํŠธ๋กค๋Ÿฌ(ํ•ธ๋“ค๋Ÿฌ) ๋ฐ–์œผ๋กœ ์˜ˆ์™ธ๊ฐ€ ๋˜์ ธ์ง„ ๊ฒฝ์šฐ ์˜ˆ์™ธ๋ฅผ ํ•ด๊ฒฐํ•˜๊ณ , ๋™์ž‘์„ ์ƒˆ๋กœ ์ •์˜ํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์„ ์ œ๊ณตํ•œ๋‹ค. ์ปจํŠธ๋กค๋Ÿฌ ๋ฐ–์œผ๋กœ ๋˜์ ธ์ง„ ์˜ˆ์™ธ๋ฅผ ํ•ด๊ฒฐํ•˜๊ณ , ๋™์ž‘ ๋ฐฉ์‹์„ ๋ณ€๊ฒฝํ•˜๊ณ  ์‹ถ์œผ๋ฉด HandlerExceptionResolver ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค. ์ค„์—ฌ์„œ ExceptionResolver ๋ผ ํ•œ๋‹ค.

    ์ถœ์ฒ˜ : ๊น€์˜ํ•œ - ์Šคํ”„๋ง MVC

    ์œ„์˜ ๊ทธ๋ฆผ ์ฒ˜๋Ÿผ, ExceptionResolver๋ฅผ ์ ์šฉํ•˜๋ฉด, ์ด ๋ฆฌ์กธ๋ฒ„๋ฅผ ํ˜ธ์ถœํ•˜๊ณ  ์˜ˆ์™ธ ํ•ด๊ฒฐ ์‹œ๋„๋ฅผ ํ•จ์œผ๋กœ์จ ์ดํ›„ ์ •์ƒ์ ์œผ๋กœ model์„ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค. (๋‹ค๋งŒ, ExceptionResolver๋กœ ์˜ˆ์™ธ๋ฅผ ํ•ด๊ฒฐํ•ด๋„ postHandle()์€ ํ˜ธ์ถœ๋˜์ง€ ์•Š๋Š”๋‹ค.)

     

     

    ์ฝ”๋“œ๋กœ ํ™•์ธํ•ด๋ณด์ž. ์ด๋Ÿฐ์‹์œผ๋กœ ์ž‘์„ฑํ•ด์ฃผ๋ฉด ๋œ๋‹ค.

    @Slf4j
    public class MyHandlerExceptionResolver implements HandlerExceptionResolver {
    
        @Override
        public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
    
            log.info("call resolver", ex);
    
            try {
                if (ex instanceof IllegalArgumentException) {
                    log.info("IllegalArgumentException resolver to 400");
                    response.sendError(HttpServletResponse.SC_BAD_REQUEST, ex.getMessage());
                    return new ModelAndView();
                }
    
            } catch (IOException e) {
                log.error("resolver ex", e);
            }
    
            return null;
        }
    }

     

    ExceptionResolver ๊ฐ€ ModelAndView ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ์ด์œ ๋Š” ๋งˆ์น˜ try, catch๋ฅผ ํ•˜๋“ฏ์ด, Exception ์„ ์ฒ˜๋ฆฌํ•ด์„œ ์ •์ƒ ํ๋ฆ„ ์ฒ˜๋Ÿผ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒƒ์ด ๋ชฉ์ ์ด๋‹ค. ์ด๋ฆ„ ๊ทธ๋Œ€๋กœ Exception ์„ Resolver(ํ•ด๊ฒฐ)ํ•˜๋Š” ๊ฒƒ์ด ๋ชฉ์ ์ด๋‹ค.

     

    ์—ฌ๊ธฐ์„œ๋Š” IllegalArgumentException ์ด ๋ฐœ์ƒํ•˜๋ฉด response.sendError(400) ๋ฅผ ํ˜ธ์ถœํ•ด์„œ HTTP ์ƒํƒœ ์ฝ”๋“œ๋ฅผ 400์œผ๋กœ ์ง€์ •ํ•˜๊ณ , ๋นˆ ModelAndView ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

     

     

    HandlerExceptionResolver ์˜ ๋ฐ˜ํ™˜ ๊ฐ’์— ๋”ฐ๋ฅธ DispatcherServlet ์˜ ๋™์ž‘ ๋ฐฉ์‹์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

    ๋นˆ ModelAndView
    new ModelAndView() ์ฒ˜๋Ÿผ ๋นˆ ModelAndView ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉด ๋ทฐ๋ฅผ ๋ Œ๋”๋ง ํ•˜์ง€ ์•Š๊ณ , ์ •์ƒ ํ๋ฆ„์œผ๋กœ ์„œ๋ธ”๋ฆฟ์ด ๋ฆฌํ„ด๋œ๋‹ค.

    ModelAndView ์ง€์ •
    ModelAndView ์— View , Model ๋“ฑ์˜ ์ •๋ณด๋ฅผ ์ง€์ •ํ•ด์„œ ๋ฐ˜ํ™˜ํ•˜๋ฉด ๋ทฐ๋ฅผ ๋ Œ๋”๋ง ํ•œ๋‹ค.

    null
    null ์„ ๋ฐ˜ํ™˜ํ•˜๋ฉด, ๋‹ค์Œ ExceptionResolver ๋ฅผ ์ฐพ์•„์„œ ์‹คํ–‰ํ•œ๋‹ค. ๋งŒ์•ฝ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ExceptionResolver ๊ฐ€ ์—†์œผ๋ฉด ์˜ˆ์™ธ ์ฒ˜๋ฆฌ๊ฐ€ ์•ˆ๋˜๊ณ , ๊ธฐ์กด์— ๋ฐœ์ƒํ•œ ์˜ˆ์™ธ๋ฅผ ์„œ๋ธ”๋ฆฟ ๋ฐ–์œผ๋กœ ๋˜์ง„๋‹ค.

     

    ์ถ”๊ฐ€)

    ์˜ˆ์™ธ๋ฅผ ์˜ค๋ฅ˜ ์ฝ”๋“œ๋ฅผ ๋„ฃ์–ด response.sendError(xxx) ํ˜ธ์ถœ๋กœ ๋ณ€๊ฒฝํ•ด์„œ ์„œ๋ธ”๋ฆฟ์—์„œ ์ƒํƒœ ์ฝ”๋“œ์— ๋”ฐ๋ฅธ ์˜ค๋ฅ˜๋ฅผ ์ฒ˜๋ฆฌํ•˜๋„๋ก ์œ„์ž„ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ดํ›„ WAS๋Š” ์„œ๋ธ”๋ฆฟ ์˜ค๋ฅ˜ํŽ˜์ด์ง€๋ฅผ ์ฐพ์•„์„œ ๋‚ด๋ถ€ ํ˜ธ์ถœํ•  ๊ฒƒ์ด๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด์„œ ์Šคํ”„๋ง๋ถ€ํŠธ๊ฐ€ ๊ธฐ๋ณธ์œผ๋กœ ์„ค์ •ํ•œ /error ๊ฐ€ ํ˜ธ์ถœ๋œ๋‹ค.

     

    ๋˜ํ•œ ModelAndView ์— ๊ฐ’์„ ์ฑ„์›Œ์„œ ์˜ˆ์™ธ์— ๋”ฐ๋ฅธ ์ƒˆ๋กœ์šด ์˜ค๋ฅ˜ ํ™”๋ฉด ๋ทฐ ๋ Œ๋”๋ง ํ•ด์„œ ๊ณ ๊ฐ์—๊ฒŒ ์ œ๊ณตํ•  ์ˆ˜๋„ ์žˆ๋‹ค.

     

    response.getWriter().println("hello"); ์ฒ˜๋Ÿผ HTTP ์‘๋‹ต ๋ฐ”๋””์— ์ง์ ‘ ๋ฐ์ดํ„ฐ๋ฅผ ๋„ฃ์–ด์ฃผ๋Š” ๊ฒƒ๋„ ๊ฐ€๋Šฅํ•˜๋‹ค. ์—ฌ๊ธฐ์— JSON ์œผ๋กœ ์‘๋‹ตํ•˜๋ฉด API ์‘๋‹ต ์ฒ˜๋ฆฌ๋ฅผ ํ•  ์ˆ˜ ์žˆ๋‹ค.

     

    ๊ทธ๋ฆฌ๊ณ  ์žŠ์ง€๋ง๊ณ  WebConfig์— ๋“ฑ๋ก์„ ํ•ด์ฃผ์ž. ( configureHandlerExceptionResolvers(..) ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์Šคํ”„๋ง์ด ๊ธฐ๋ณธ์œผ๋กœ ๋“ฑ๋กํ•˜๋Š” ExceptionResolver ๊ฐ€ ์ œ๊ฑฐ๋˜๋ฏ€๋กœ ์ฃผ์˜, extendHandlerExceptionResolvers ๋ฅผ ์‚ฌ์šฉํ•˜์ž.)

    @Configuration
    public class WebConfig implements WebMvcConfigurer {
      
        @Override
        public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
            resolvers.add(new MyHandlerExceptionResolver());
        }
    }

     

    ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด WAS๊นŒ์ง€ ์˜ˆ์™ธ๊ฐ€ ๋˜์ ธ์ง€๊ณ , WAS์—์„œ ์˜ค๋ฅ˜ ํŽ˜์ด์ง€ ์ •๋ณด๋ฅผ ์ฐพ์•„์„œ ๋‹ค์‹œ /error ๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ณผ์ •์€ ์ƒ๊ฐํ•ด๋ณด๋ฉด ๋„ˆ๋ฌด ๋ณต์žกํ•˜๋‹ค. ExceptionResolver ๋ฅผ ํ™œ์šฉํ•˜๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ์ด๋Ÿฐ ๋ณต์žกํ•œ ๊ณผ์ • ์—†์ด ์—ฌ๊ธฐ์—์„œ ๋ฌธ์ œ๋ฅผ ๊น”๋”ํ•˜๊ฒŒ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค

     ExceptionResolver ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•ด๋„ ExceptionResolver ์—์„œ ์˜ˆ์™ธ๋ฅผ ์ฒ˜๋ฆฌํ•ด๋ฒ„๋ฆฐ๋‹ค. ๋”ฐ๋ผ์„œ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•ด๋„ ์„œ๋ธ”๋ฆฟ ์ปจํ…Œ์ด๋„ˆ๊นŒ์ง€ ์˜ˆ์™ธ๊ฐ€ ์ „๋‹ฌ๋˜์ง€ ์•Š๊ณ , ์Šคํ”„๋ง MVC์—์„œ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ๋Š” ๋์ด ๋‚œ๋‹ค.

    ๊ฒฐ๊ณผ์ ์œผ๋กœ WAS ์ž…์žฅ์—์„œ๋Š” ์ •์ƒ ์ฒ˜๋ฆฌ๊ฐ€ ๋œ ๊ฒƒ์ด๋‹ค. ์ด๋ ‡๊ฒŒ ์˜ˆ์™ธ๋ฅผ ์ด๊ณณ์—์„œ ๋ชจ๋‘ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์ด ํ•ต์‹ฌ์ด๋‹ค.

    ์„œ๋ธ”๋ฆฟ ์ปจํ…Œ์ด๋„ˆ๊นŒ์ง€ ์˜ˆ์™ธ๊ฐ€ ์˜ฌ๋ผ๊ฐ€๋ฉด ๋ณต์žกํ•˜๊ณ  ์ง€์ €๋ถ„ํ•˜๊ฒŒ ์ถ”๊ฐ€ ํ”„๋กœ์„ธ์Šค๊ฐ€ ์‹คํ–‰๋œ๋‹ค. ๋ฐ˜๋ฉด์— ExceptionResolver ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์˜ˆ์™ธ์ฒ˜๋ฆฌ๊ฐ€ ์ƒ๋‹นํžˆ ๊น”๋”ํ•ด์ง„๋‹ค.

     

    ๊ทธ๋Ÿฐ๋ฐ ์ง์ ‘ ExceptionResolver ๋ฅผ ๊ตฌํ˜„ํ•˜๋ ค๊ณ  ํ•˜๋‹ˆ ์ƒ๋‹นํžˆ ๋ณต์žกํ•˜๋‹ค.

    ๋‹ค์Œ ๊ธ€ ๋ถ€ํ„ฐ๋Š” ์Šคํ”„๋ง์ด ์ œ๊ณตํ•˜๋Š” ExceptionResolver ๋“ค์„ ์•Œ์•„๋ณด์ž.

     

     

    ๋Œ“๊ธ€

Designed by Tistory.