ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • API ์˜ˆ์™ธ ์ฒ˜๋ฆฌ - HandlerExceptionResolver(2) : ResponseStatusExceptionResolver, DefaultHandlerExceptionResolver, ExceptionHandlerExceptionResolver
    ๊ฐœ๋ฐœ/Spring Boot 2022. 7. 8. 01:04

     

     

    https://mong-dev.tistory.com/43
     

    API ์˜ˆ์™ธ ์ฒ˜๋ฆฌ - HandlerExceptionResolver(1)

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

    mong-dev.tistory.com

     

     

    ์Šคํ”„๋ง ๋ถ€ํŠธ๊ฐ€ ๊ธฐ๋ณธ์œผ๋กœ ์ œ๊ณตํ•˜๋Š” ExceptionResolver ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

    HandlerExceptionResolverComposite ์— ๋‹ค์Œ ์ˆœ์„œ๋กœ ๋“ฑ๋ก๋˜์–ด ์žˆ๋‹ค.

    1. ExceptionHandlerExceptionResolver
    @ExceptionHandler ์„ ์ฒ˜๋ฆฌํ•œ๋‹ค. API ์˜ˆ์™ธ ์ฒ˜๋ฆฌ๋Š” ๋Œ€๋ถ€๋ถ„ ์ด ๊ธฐ๋Šฅ์œผ๋กœ ํ•ด๊ฒฐํ•œ๋‹ค. 

    2. ResponseStatusExceptionResolver
    HTTP ์ƒํƒœ ์ฝ”๋“œ๋ฅผ ์ง€์ •ํ•ด์ค€๋‹ค. ์˜ˆ) @ResponseStatus(value = HttpStatus.NOT_FOUND)

    3. DefaultHandlerExceptionResolver ์šฐ์„  ์ˆœ์œ„๊ฐ€ ๊ฐ€์žฅ ๋‚ฎ๋‹ค.
    ์Šคํ”„๋ง ๋‚ด๋ถ€ ๊ธฐ๋ณธ ์˜ˆ์™ธ๋ฅผ ์ฒ˜๋ฆฌํ•œ๋‹ค.

     

     

     

     

    ResponseStatusExceptionResolver


    ResponseStatusExceptionResolver ๋Š” ์˜ˆ์™ธ์— ๋”ฐ๋ผ์„œ HTTP ์ƒํƒœ ์ฝ”๋“œ๋ฅผ ์ง€์ •ํ•ด์ฃผ๋Š” ์—ญํ• ์„ ํ•œ๋‹ค.

    - @ResponseStatus ๊ฐ€ ๋‹ฌ๋ ค์žˆ๋Š” ์˜ˆ์™ธ
    - ResponseStatusException ์˜ˆ์™ธ

     

    ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด์„œ ํ…Œ์ŠคํŠธ๋ฅผ ํ•ด๋ณผ ์ˆ˜ ์žˆ๋‹ค.

    @ResponseStatus(code = HttpStatus.BAD_REQUEST, reason = "์ž˜๋ชป๋œ ์š”์ฒญ ์˜ค๋ฅ˜") 
    public class BadRequestException extends RuntimeException {
    }
     @GetMapping("/api/response-status-ex1")
        public String responseStatusEx1() {
            throw new BadRequestException();
        }

     

    ์›๋ž˜๋Š” 500์—๋Ÿฌ๊ฐ€ ๋–ด์ง€๋งŒ, ์ด์ œ 400์—๋Ÿฌ๋กœ ๋ฐ”๊ปด์žˆ์„ ๊ฒƒ์ด๋‹ค.

    {
        "status": 400,
        "error": "Bad Request",
        "exception": "hello.exception.exception.BadRequestException", "message": "์ž˜๋ชป๋œ ์š”์ฒญ ์˜ค๋ฅ˜",
        "path": "/api/response-status-ex1"'
    }

     

    reason ์„ MessageSource ์—์„œ ์ฐพ๋Š” ๊ธฐ๋Šฅ๋„ ์ œ๊ณตํ•œ๋‹ค. reason = "error.bad" 

    @ResponseStatus(code = HttpStatus.BAD_REQUEST, reason = "error.bad")
    public class BadRequestException extends RuntimeException {
    }

     

    messages.properties

    error.bad=์ž˜๋ชป๋œ ์š”์ฒญ ์˜ค๋ฅ˜์ž…๋‹ˆ๋‹ค. ๋ฉ”์‹œ์ง€ ์‚ฌ์šฉ

     

    ๊ฒฐ๊ณผ

    {
    	"status": 400,
    	"error": "Bad Request",
    	"exception": "hello.exception.exception.BadRequestException", 
    	"message": "์ž˜๋ชป๋œ ์š”์ฒญ ์˜ค๋ฅ˜์ž…๋‹ˆ๋‹ค. ๋ฉ”์‹œ์ง€ ์‚ฌ์šฉ",
    	"path": "/api/response-status-ex1"
    }

     

     

    ๋‹จ์ 

    @ResponseStatus ๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ ์ง์ ‘ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†๋Š” ์˜ˆ์™ธ์—๋Š” ์ ์šฉํ•  ์ˆ˜ ์—†๋‹ค. (์• ๋…ธํ…Œ์ด์…˜์„ ์ง์ ‘ ๋„ฃ์–ด์•ผ ํ•˜๋Š”๋ฐ, ๋‚ด๊ฐ€ ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ•  ์ˆ˜ ์—†๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ์˜ˆ์™ธ ์ฝ”๋“œ ๊ฐ™์€ ๊ณณ์—๋Š” ์ ์šฉํ•  ์ˆ˜ ์—†๋‹ค.) ์ถ”๊ฐ€๋กœ ์• ๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์กฐ๊ฑด์— ๋”ฐ๋ผ ๋™์ ์œผ๋กœ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒƒ๋„ ์–ด๋ ต๋‹ค. ์ด๋•Œ๋Š” ResponseStatusException ์˜ˆ์™ธ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

      @GetMapping("/api/response-status-ex2")
      public String responseStatusEx2() {
            throw new ResponseStatusException(HttpStatus.NOT_FOUND
                                              , "error.bad" 
                                              , new IllegalArgumentException());
      }
      {
          "status": 404,
          "error": "Not Found",
          "exception": "org.springframework.web.server.ResponseStatusException", 
          "message": "์ž˜๋ชป๋œ ์š”์ฒญ ์˜ค๋ฅ˜์ž…๋‹ˆ๋‹ค. ๋ฉ”์‹œ์ง€ ์‚ฌ์šฉ",
           "path": "/api/response-status-ex2"
      }

     

     

     

     

    DefaultHandlerExceptionResolver


    DefaultHandlerExceptionResolver ๋Š” ์Šคํ”„๋ง ๋‚ด๋ถ€์—์„œ ๋ฐœ์ƒํ•˜๋Š” ์Šคํ”„๋ง ์˜ˆ์™ธ๋ฅผ ํ•ด๊ฒฐํ•œ๋‹ค. ๋Œ€ํ‘œ์ ์œผ๋กœ ํŒŒ๋ผ๋ฏธํ„ฐ ๋ฐ”์ธ๋”ฉ ์‹œ์ ์— ํƒ€์ž…์ด ๋งž์ง€ ์•Š์œผ๋ฉด ๋‚ด๋ถ€์—์„œ TypeMismatchException ์ด ๋ฐœ์ƒํ•˜๋Š”๋ฐ, ์ด ๊ฒฝ์šฐ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ๊ทธ๋ƒฅ ๋‘๋ฉด ์„œ๋ธ”๋ฆฟ ์ปจํ…Œ์ด๋„ˆ๊นŒ์ง€ ์˜ค๋ฅ˜๊ฐ€ ์˜ฌ๋ผ๊ฐ€๊ณ , ๊ฒฐ๊ณผ์ ์œผ๋กœ 500 ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

    ๊ทธ๋Ÿฐ๋ฐ ํŒŒ๋ผ๋ฏธํ„ฐ ๋ฐ”์ธ๋”ฉ์€ ๋Œ€๋ถ€๋ถ„ ํด๋ผ์ด์–ธํŠธ๊ฐ€ HTTP ์š”์ฒญ ์ •๋ณด๋ฅผ ์ž˜๋ชป ํ˜ธ์ถœํ•ด์„œ ๋ฐœ์ƒํ•˜๋Š” ๋ฌธ์ œ์ด๋‹ค. HTTP ์—์„œ๋Š” ์ด๋Ÿฐ ๊ฒฝ์šฐ HTTP ์ƒํƒœ ์ฝ”๋“œ 400์„ ์‚ฌ์šฉํ•˜๋„๋ก ๋˜์–ด ์žˆ๋‹ค. DefaultHandlerExceptionResolver ๋Š” ์ด๊ฒƒ์„ 500 ์˜ค๋ฅ˜๊ฐ€ ์•„๋‹ˆ๋ผ HTTP ์ƒํƒœ ์ฝ”๋“œ 400 ์˜ค๋ฅ˜๋กœ๋ณ€๊ฒฝํ•œ๋‹ค. ์Šคํ”„๋ง ๋‚ด๋ถ€ ์˜ค๋ฅ˜๋ฅผ ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌํ• ์ง€ ์ˆ˜ ๋งŽ์€ ๋‚ด์šฉ์ด ์ •์˜๋˜์–ด ์žˆ๋‹ค.

     

    DefaultHandlerExceptionResolver.handleTypeMismatch ๋ฅผ ๋ณด๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ฝ”๋“œ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

    response.sendError(HttpServletResponse.SC_BAD_REQUEST) (400)

     

    ๊ฒฐ๊ตญ response.sendError() ๋ฅผ ํ†ตํ•ด์„œ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•œ๋‹ค.

    sendError(400) ๋ฅผ ํ˜ธ์ถœํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— WAS์—์„œ ๋‹ค์‹œ ์˜ค๋ฅ˜ ํŽ˜์ด์ง€( /error )๋ฅผ ๋‚ด๋ถ€ ์š”์ฒญํ•œ๋‹ค.

     

    .

    .

     

    ์ง€๊ธˆ๊นŒ์ง€ HTTP ์ƒํƒœ ์ฝ”๋“œ๋ฅผ ๋ณ€๊ฒฝํ•˜๊ณ , ์Šคํ”„๋ง ๋‚ด๋ถ€ ์˜ˆ์™ธ์˜ ์ƒํƒœ์ฝ”๋“œ๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ๊ธฐ๋Šฅ๋„ ์•Œ์•„๋ณด์•˜๋‹ค.

    ๊ทธ๋Ÿฐ๋ฐ HandlerExceptionResolver ๋ฅผ ์ง์ ‘ ์‚ฌ์šฉํ•˜๊ธฐ๋Š” ๋ณต์žกํ•˜๋‹ค. API ์˜ค๋ฅ˜ ์‘๋‹ต์˜ ๊ฒฝ์šฐ response ์— ์ง์ ‘ ๋ฐ์ดํ„ฐ๋ฅผ ๋„ฃ์–ด์•ผ ํ•ด์„œ ๋งค์šฐ ๋ถˆํŽธํ•˜๊ณ  ๋ฒˆ๊ฑฐ๋กญ๋‹ค. ModelAndView ๋ฅผ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•˜๋Š” ๊ฒƒ๋„ API์—๋Š” ์ž˜ ๋งž์ง€ ์•Š๋Š”๋‹ค.

     

    ์Šคํ”„๋ง์€ ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด @ExceptionHandler ๋ผ๋Š” ๋งค์šฐ ํ˜์‹ ์ ์ธ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•œ๋‹ค.

    ๊ทธ๊ฒƒ์ด ExceptionHandlerExceptionResolver ์ด๋‹ค.

     

     

     

    ExceptionHandlerExceptionResolver


    ์Šคํ”„๋ง์€ API ์˜ˆ์™ธ ์ฒ˜๋ฆฌ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด @ExceptionHandler ๋ผ๋Š” ์• ๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜๋Š” ๋งค์šฐ ํŽธ๋ฆฌํ•œ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š”๋ฐ, ์ด๊ฒƒ์ด ๋ฐ”๋กœ ExceptionHandlerExceptionResolver ์ด๋‹ค. ์Šคํ”„๋ง์€ ExceptionHandlerExceptionResolver ๋ฅผ ๊ธฐ๋ณธ์œผ๋กœ ์ œ๊ณตํ•˜๊ณ , ๊ธฐ๋ณธ์œผ๋กœ ์ œ๊ณตํ•˜๋Š” ExceptionResolver ์ค‘์— ์šฐ์„ ์ˆœ์œ„๋„ ๊ฐ€์žฅ ๋†’๋‹ค. ์‹ค๋ฌด์—์„œ API ์˜ˆ์™ธ ์ฒ˜๋ฆฌ๋Š” ๋Œ€๋ถ€๋ถ„ ์ด ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•œ๋‹ค.

     @ExceptionHandler ์• ๋…ธํ…Œ์ด์…˜์„ ์„ ์–ธํ•˜๊ณ , ํ•ด๋‹น ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ์ฒ˜๋ฆฌํ•˜๊ณ  ์‹ถ์€ ์˜ˆ์™ธ๋ฅผ ์ง€์ •ํ•ด์ฃผ๋ฉด ๋œ๋‹ค. ํ•ด๋‹น ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ์ด ๋ฉ”์„œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋œ๋‹ค. ์ฐธ๊ณ ๋กœ ์ง€์ •ํ•œ ์˜ˆ์™ธ ๋˜๋Š” ๊ทธ ์˜ˆ์™ธ์˜ ์ž์‹ ํด๋ž˜์Šค๋Š” ๋ชจ๋‘ ์žก์„ ์ˆ˜ ์žˆ๋‹ค.

     

    @Data
    @AllArgsConstructor
    public class ErrorResult {
        private String code;
        private String message;
    }
    @Slf4j
    @RestController
    public class ApiExceptionV2Controller {
    
        //์ƒํƒœ์ฝ”๋“œ๋„ ๋ณ€๊ฒฝํ•˜๊ณ  ์‹ถ์œผ๋ฉด ์ด๊ฑธ ๋ถ™์—ฌ์ค€๋‹ค.
        @ResponseStatus(HttpStatus.BAD_REQUEST)
        //ํ•ด๋‹น ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ ํ˜ธ์ถœ๋  ๋•Œ, ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ์žก๋Š”๋‹ค.
        @ExceptionHandler(IllegalArgumentException.class)
        public ErrorResult illegalExHandler(IllegalArgumentException e) {
            log.error("[exceptionHandler] ex", e);
            return new ErrorResult("BAD", e.getMessage());
        }
        
        //ํŒŒ๋ผ๋ฏธํ„ฐ์™€ ์ด๋ฆ„์ด ๊ฐ™์œผ๋ฉด ๊ด„ํ˜ธ์ƒ๋žต๊ฐ€๋Šฅ.
        @ExceptionHandler
        public ResponseEntity<ErrorResult> userExHandler(UserException e) {
            log.error("[exceptionHandler] ex", e);
            ErrorResult errorResult = new ErrorResult("USER-EX", e.getMessage());
            return new ResponseEntity(errorResult, HttpStatus.BAD_REQUEST);
        }
        
        
        @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
        @ExceptionHandler
        public ErrorResult exExHandler(Exception e) {
            log.error("[exceptionHandler] ex", e);
            return new ErrorResult("EX", "๋‚ด๋ถ€์˜ค๋ฅ˜");
        }
        
    
        @GetMapping("/api2/members/{id}")
        public MemberDto getMember(@PathVariable("id") String id) {
    
            if (id.equals("ex")) {
                throw new RuntimeException("์ž˜๋ชป๋œ ์‚ฌ์šฉ์ž");
            }
            if (id.equals("bad")) {
                throw new IllegalArgumentException("์ž˜๋ชป๋œ ์ž…๋ ฅ ๊ฐ’");
            }
            if (id.equals("user-ex")) {
                throw new UserException("์‚ฌ์šฉ์ž ์˜ค๋ฅ˜");
            }
    
            return new MemberDto(id, "hello " + id);
        }
    
        @Data
        @AllArgsConstructor
        static class MemberDto {
            private String memberId;
            private String name;
        }
    
    }

     

    Controller์—์„œ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด, ExceptionHandlerExceptionResolver ๊ฐ€ ๋จผ์ € Controller์— @ExceptionHandler๊ฐ€ ์žˆ๋Š” ์ง€ ์ฐพ๊ณ , ์žˆ์œผ๋ฉด ํ˜ธ์ถœํ•ด์ค€๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์˜ค๋ฅ˜ํ•ด๊ฒฐ์„ ์‹œ๋„ํ•˜๊ณ , ์ •์ƒํ๋ฆ„์œผ๋กœ ๋ณ€๊ฒฝ๋œ๋‹ค. (200์ฝ”๋“œ๋กœ ๋‚˜์˜ค๊ฒŒ ๋œ๋‹ค.) ๊ทธ๋Ÿฐ๋ฐ, ๋งŒ์•ฝ์— ์ƒํƒœ์ฝ”๋“œ๋„ ๋ณ€๊ฒฝํ•˜๊ณ  ์‹ถ์œผ๋ฉด @ResponseStatus๋ฅผ ๋ถ™์—ฌ์ค€๋‹ค. ํ˜น์€ ๋‘๋ฒˆ์งธ ์ฒ˜๋Ÿผ ReponseEntity๋ฅผ ์‚ฌ์šฉํ•ด์ค€๋‹ค.

     

    ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋‹ค์–‘ํ•œ ์˜ˆ์™ธ๋ฅผ ํ•œ๋ฒˆ์— ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.

      @ExceptionHandler({AException.class, BException.class})
      public String ex(Exception e) {
            log.info("exception e", e);
      }

     

     

    @ExceptionHandler ์—๋Š” ๋งˆ์น˜ ์Šคํ”„๋ง์˜ ์ปจํŠธ๋กค๋Ÿฌ์˜ ํŒŒ๋ผ๋ฏธํ„ฐ ์‘๋‹ต์ฒ˜๋Ÿผ ๋‹ค์–‘ํ•œ ํŒŒ๋ผ๋ฏธํ„ฐ์™€ ์‘๋‹ต์„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

    ์ž์„ธํ•œ ํŒŒ๋ผ๋ฏธํ„ฐ์™€ ์‘๋‹ต์€ ๋‹ค์Œ ๊ณต์‹ ๋ฉ”๋‰ด์–ผ์„ ์ฐธ๊ณ ํ•˜์ž.

    https://docs.spring.io/springframework/docs/current/reference/html/web.html#mvc-ann-exceptionhandler-args 

     
     
     
     
    ์ด๋ฒˆ ์ฃผ์ œ์˜ ๋งˆ์ง€๋ง‰ ๊ธ€์ธ @ControllerAdvice์— ๋Œ€ํ•ด์„œ ๋‹ค์Œ ๊ธ€์—์„œ ์•Œ์•„๋ณด๊ฒ ๋‹ค :)
     
     
     

     

     

    ๋Œ“๊ธ€

Designed by Tistory.