-
API ์์ธ ์ฒ๋ฆฌ - HandlerExceptionResolver(2) : ResponseStatusExceptionResolver, DefaultHandlerExceptionResolver, ExceptionHandlerExceptionResolver๊ฐ๋ฐ/Spring Boot 2022. 7. 8. 01:04
https://mong-dev.tistory.com/43
์คํ๋ง ๋ถํธ๊ฐ ๊ธฐ๋ณธ์ผ๋ก ์ ๊ณตํ๋ 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 ์๋ ๋ง์น ์คํ๋ง์ ์ปจํธ๋กค๋ฌ์ ํ๋ผ๋ฏธํฐ ์๋ต์ฒ๋ผ ๋ค์ํ ํ๋ผ๋ฏธํฐ์ ์๋ต์ ์ง์ ํ ์ ์๋ค.
์์ธํ ํ๋ผ๋ฏธํฐ์ ์๋ต์ ๋ค์ ๊ณต์ ๋ฉ๋ด์ผ์ ์ฐธ๊ณ ํ์.
์ด๋ฒ ์ฃผ์ ์ ๋ง์ง๋ง ๊ธ์ธ @ControllerAdvice์ ๋ํด์ ๋ค์ ๊ธ์์ ์์๋ณด๊ฒ ๋ค :)'๊ฐ๋ฐ > Spring Boot' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
API ์์ธ ์ฒ๋ฆฌ - @ControllerAdvice (0) 2022.07.08 API ์์ธ ์ฒ๋ฆฌ - HandlerExceptionResolver(1) (0) 2022.07.07 Http Message Converter ์ด์ผ๊ธฐ(2) (0) 2022.07.07 HTTP Message Converter ์ด์ผ๊ธฐ(1) (0) 2022.07.07 HTTP API ์๋ต ์ ๋ฆฌ (0) 2022.07.07