๐ŸŒˆ [Section3] 4. [ Spring MVC ] ์˜ˆ์™ธ ์ฒ˜๋ฆฌ

ํ˜„์ฃผยท2022๋…„ 10์›” 26์ผ
0

bootcamp

๋ชฉ๋ก ๋ณด๊ธฐ
44/71

๐Ÿ“• ์˜ค๋Š˜ ๋ฐฐ์šด ๋‚ด์šฉ!

  • @ExceptionHandler
  • @RestControllerAdvice
  • ์˜ˆ์™ธ Throw / Catch
  • Custom Exception

โœ๏ธ @ExceptionHandler ์˜ˆ์™ธ ์ฒ˜๋ฆฌ

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์˜ˆ์™ธ(Exception)๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ, ๋‚ด๋ถ€์ ์œผ๋กœ Spring์—์„œ ์˜ˆ์™ธ๋ฅผ ์ฒ˜๋ฆฌํ•˜์—ฌ ํด๋ผ์ด์–ธํŠธ์˜ ์‘๋‹ต์œผ๋กœ ์—๋Ÿฌ ๋ฉ”์„ธ์ง€๋ฅผ ๋ณด๋‚ด์ฃผ๋Š”๋ฐ, ์ด ๋•Œ ํด๋ผ์ด์–ธํŠธ๋Š” ์–ด๋””์—์„œ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋Š”์ง€ ์‰ฝ๊ฒŒ ์•Œ ์ˆ˜ ์—†์Œ

โžœ @ExceptionHandler ์‚ฌ์šฉํ•˜์—ฌ ์—๋Ÿฌ ์‘๋‹ต ๋ฉ”์„ธ์ง€๋ฅผ ์ง์ ‘ ์ž‘์„ฑํ•˜์—ฌ ์‘๋‹ต์œผ๋กœ ๋ณด๋‚ด์ค„ ์ˆ˜ ์žˆ์Œ !

  • Controller ๋ ˆ๋ฒจ์—์„œ๋งŒ ์‚ฌ์šฉ

  • ์˜ˆ์™ธ์ฒ˜๋ฆฌ๋ฅผ ํ•  ํ•ธ๋“ค๋Ÿฌ ๋ฉ”์„œ๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ณ , ๊ทธ ์œ„์— @Exception ์• ๋„ˆํ…Œ์ด์…˜์„ ๋ถ™์—ฌ ์—๋Ÿฌ ๋ฉ”์„ธ์ง€๋ฅผ ๊ตฌ์ฒด์ ์œผ๋กœ ์ „์†ก ๊ฐ€๋Šฅ

( But, ์œ„์˜ ๊ฒฝ์šฐ ์—๋Ÿฌ ์ •๋ณด ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ Response Body ์ „์ฒด ์ •๋ณด๊นŒ์ง€ ์ „๋‹ฌ๋จ ! )

โžœ ๊ณตํ†ต๋˜๋Š” ์—๋Ÿฌ๋Š” ErrorResponse ํด๋ž˜์Šค๋ฅผ ๋”ฐ๋กœ ๋งŒ๋“ค์–ด DTO ํด๋ž˜์Šค์˜ ์œ ํšจ์„ฑ ๊ฒ€์ฆ ์‹คํŒจ ์‹œ, ์‹คํŒจํ•œ ํ•„๋“œ(๋ฉค๋ฒ„ ๋ณ€์ˆ˜)์— ๋Œ€ํ•œ Error ์ •๋ณด๋งŒ ๋‹ด์•„์„œ ๊ทธ ํด๋ž˜์Šค ๋‚ด์˜ FieldError ํด๋ž˜์Šค๋กœ ์‘๋‹ต์œผ๋กœ ์ „์†ก ๊ฐ€๋Šฅ

โžœ ์ด ๊ฒฝ์šฐ, ์—๋Ÿฌ๊ฐ€ ์—ฌ๋Ÿฌ๊ฐœ๊ฐ€ ๋  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, ํ•ธ๋“ค๋Ÿฌ ๋ฉ”์„œ๋“œ ๋‚ด์—์„œ FieldError ํด๋ž˜์Šค๋Š” List ๊ฐ์ฒด๋ฅผ ์ด์šฉํ•˜์—ฌ ์—๋Ÿฌ ์ •๋ณด๋ฅผ ๋‹ด๊ณ  ๋„˜๊ฒจ์ค„ ์ˆ˜ ์žˆ์Œ

โœ”๏ธ ErrorResponse ํด๋ž˜์Šค โžœ ์—๋Ÿฌ ๊ฒ€์‚ฌํ•  ํ•ญ๋ชฉ ์ •๋ฆฌํ•˜๋Š” ํด๋ž˜์Šค

โœ๏ธ @ExceptionHandler์˜ ๋‹จ์ 

  1. ๊ฐ ํด๋ž˜์Šค๋งˆ๋‹ค @Exception ์• ๋„ˆํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜์—ฌ ์—๋Ÿฌ ์ฒ˜๋ฆฌ๋ฅผ ํ•ด์•ผํ•˜๋ฏ€๋กœ
    ๊ฐ Controller ํด๋ž˜์Šค๋งˆ๋‹ค ์ฝ”๋“œ์˜ ์ค‘๋ณต์ด ๋ฐœ์ƒ

  2. ํ•˜๋‚˜์˜ Controller ํด๋ž˜์Šค ๋‚ด์— ์ฒ˜๋ฆฌํ•ด์•ผํ•  ์˜ˆ์™ธ๊ฐ€ ์œ ํšจ์„ฑ ๊ฒ€์ฆ ์‹คํŒจ์— ๋Œ€ํ•œ ์˜ˆ์™ธ(MethodArgumentNotValidException)๋งŒ ์žˆ๋Š”๊ฒƒ์ด ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์—,
    ํ•˜๋‚˜์˜ Controller ํด๋ž˜์Šค ๋‚ด์— @ExceptionHandler๋ฅผ ์ถ”๊ฐ€ํ•œ ์—๋Ÿฌ ์ฒ˜๋ฆฌ ํ•ธ๋“ค๋Ÿฌ ๋ฉ”์„œ๋“œ๊ฐ€ ๋Š˜์–ด๋‚จ

Ex. patchMember() ํ•ธ๋“ค๋Ÿฌ ๋ฉ”์„œ๋“œ์˜ URI ๋ณ€์ˆ˜์ธ โ€œ/{member-id}โ€์— 0์ด ๋„˜์–ด์˜ฌ ๊ฒฝ์šฐ,
ConstraintViolationException์ด ๋ฐœ์ƒ
โžœ ๊ทธ Controller ํด๋ž˜์Šค ์•ˆ์—๋Š” ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ํ•ธ๋“ค๋Ÿฌ ๋ฉ”์„œ๋“œ + ์ด ์˜ˆ์™ธ๋ฅผ ์ฒ˜๋ฆฌํ•  ํ•ธ๋“ค๋Ÿฌ ๋ฉ”์„œ๋“œ ์ด๋ ‡๊ฒŒ ๋‘๊ฐœ๊ฐ€ ์ƒ๊น€


โœ๏ธ @RestControllerAdvice๋ฅผ ์‚ฌ์šฉํ•œ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ ๊ณตํ†ตํ™”

  • Controller ํด๋ž˜์Šค ๋‚ด์—์„œ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ๋ฅผ ํ•˜๋Š” ๋Œ€์‹  @RestControllerAdvice ์• ๋„ˆํ…Œ์ด์…˜ ์‚ฌ์šฉํ•˜์—ฌ ์ƒˆ๋กœ ํด๋ž˜์Šค ๋งŒ๋“ค๋ฉด,
    ๊ทธ ํด๋ž˜์Šค์—์„œ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ๋ฅผ ๊ณตํ†ตํ™”ํ•˜์—ฌ ์—ฌ๋Ÿฌ Controller ํด๋ž˜์Šค์—์„œ@ExceptionHandler/@InitBinder/@ModelAttribute๊ฐ€ ๋ถ™์€ ๋ฉ”์„œ๋“œ ๊ณต์œ ํ•˜์—ฌ ์‚ฌ์šฉ ๊ฐ€๋Šฅ

    ๐Ÿ’ก @InitBinder/@ModelAttribute ์• ๋„ˆํ…Œ์ด์…˜์€ ์„œ๋ฒ„ ์‚ฌ์ด๋“œ ๋ Œ๋”๋ง(SSR, Server Side Rendering) ๋ฐฉ์‹์—์„œ ์ฃผ๋กœ ์‚ฌ์šฉ๋˜๋Š” ๋ฐฉ์‹
    Ex. JSP, Thymeleaf

  • @RestControllerAdvice๊ฐ€ ๋ถ™์€ ํด๋ž˜์Šค์—๋Š” @ExceptionHandler ์• ๋„ˆํ…Œ์ด์…˜์ด ๋ถ™์—ˆ๋˜ ์—๋Ÿฌ ์ฒ˜๋ฆฌ ํ•ธ๋“ค๋Ÿฌ ๋ฉ”์„œ๋“œ๋“ค์„ ๋„ฃ์œผ๋ฉด ๋จ

  • ๊ฐ ์ปจํŠธ๋กค๋Ÿฌ๋งˆ๋‹ค ๋ฐœ๊ฒฌ๋˜๋Š” ์—๋Ÿฌ๋ฅผ @RestControllerAdvice๊ฐ€ ์ฒ˜๋ฆฌ

โœ”๏ธ @RestControllerAdvice ์• ๋„ˆํ…Œ์ด์…˜์ด ๋ถ™์€ ํด๋ž˜์Šค
โžœ ๊ฐ ํ•ธ๋“ค๋Ÿฌ ๋ฉ”์„ธ์ง€์˜ ์—๋Ÿฌ ์‘๋‹ต ์ „๋‹ฌํ•˜๋Š” ํด๋ž˜์Šค

  • ์ด ํด๋ž˜์Šค์—๋Š” @ResponseStatus ์• ๋„ˆํ…Œ์ด์…˜์ด ๋ถ™์„ ์ˆ˜ ์žˆ์Œ
    ( ResponseEntity๋กœ ๋ฐ์ดํ„ฐ ๋ž˜ํ•‘ํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š” ์‚ฌ์šฉ X / ErrorResponse๋กœ ๋ž˜ํ•‘ํ•˜๋Š” ๊ฒฝ์šฐ์—๋งŒ ์‚ฌ์šฉ )

    โœ”๏ธ @ResponseStatus ์• ๋„ˆํ…Œ์ด์…˜
    โžœ HTTP Status๋ฅผ ๋Œ€์‹  ํ‘œํ˜„
    Ex. @ResponseStatus(HttpStatus.BAD_REQUEST)

๐Ÿ’ก @RestControllerAdvice vs @ControllerAdvice

  • @RestControllerAdvice = @ControllerAdvice + @ResponseBody
    โ €
    โžœ @RestControllerAdvice ์• ๋„ˆํ…Œ์ด์…˜์€ JSON ํ˜•์‹์˜ ๋ฐ์ดํ„ฐ๋ฅผ Response Body๋กœ ์ „์†กํ•˜๊ธฐ ์œ„ํ•ด์„œ ResponseEntity๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋ž˜ํ•‘ํ•  ํ•„์š”๊ฐ€ ์—†์Œ

โœ”๏ธ of() ๋ฉ”์„œ๋“œ

  • ๋„ค์ด๋ฐ ์ปจ๋ฒค์…˜(Naming Convention)

  • ๊ฐ์ฒด ์ƒ์„ฑ์‹œ ์–ด๋–ค ๊ฐ’๋“ค์˜(of~) ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•œ๋‹ค๋Š” ์˜๋ฏธ

  • ์ƒ์„ฑ์ž์— private ์ ‘๊ทผ ์ œ์–ด์ž๊ฐ€ ๋ถ™์—ˆ์„ ๊ฒฝ์šฐ, ๊ฐ ์—ญํ•  ๊ตฌ๋ถ„์„ ์œ„ํ•ด
    ์ƒ์„ฑ์ž์™€ ๊ฐ™์€ ์—ญํ• ์„ ํ•˜๋Š” ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•  ๋•Œ ์‚ฌ์šฉ
    โ €
    Ex. ErrorResponse of(BindingResult bindingResult)
    โžœ An ErrorResponse instance is made of BindingResult object


โœ๏ธ ์˜ˆ์™ธ ๋˜์ง€๊ธฐ(throw) ๋ฐ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ

โœ”๏ธ ์ฒดํฌ ์˜ˆ์™ธ (Checked Exception)

  • ๋ฐœ์ƒํ•œ ์˜ˆ์™ธ๋ฅผ ์žก์•„์„œ(catch) ์ฒดํฌํ•œ ํ›„์— ํ•ด๋‹น ์˜ˆ์™ธ๋ฅผ ๋ณต๊ตฌ ํ•˜๋“ ๊ฐ€ ์•„๋‹ˆ๋ฉด ํšŒํ”ผ ํ•˜๋“ ๊ฐ€ ๋“ฑ์˜ ์–ด๋–ค ๊ตฌ์ฒด์ ์ธ ์ฒ˜๋ฆฌ๋ฅผ ํ•ด์•ผ ํ•˜๋Š” ์˜ˆ์™ธ

โœ”๏ธ ์–ธ์ฒดํฌ ์˜ˆ์™ธ (Unchecked Exception)

  • ์˜ˆ์™ธ๋ฅผ ์žก์•„์„œ(catch) ํ•ด๋‹น ์˜ˆ์™ธ์— ๋Œ€ํ•œ ์–ด๋–ค ์ฒ˜๋ฆฌ๋ฅผ ํ•  ํ•„์š”๊ฐ€ ์—†๋Š” ์˜ˆ์™ธ
  • RuntimeException์ด ์ด์— ํ•ด๋‹น ( ์ด๋ฅผ ์ƒ์†๋ฐ›์€ ํ•˜์œ„ ํด๋ž˜์Šค๋„ ๋ชจ๋‘ )

โ— Java๋‚˜ Spring์—์„œ ์ˆ˜๋งŽ์€ RuntimeException์„ ์ง€์›ํ•ด์ฃผ์ง€๋งŒ, ๊ฐœ๋ฐœ์ž๊ฐ€ ์ง์ ‘ ์˜ˆ์™ธ(Exception)๋ฅผ ๋งŒ๋“ค์–ด์•ผ ํ•  ๊ฒฝ์šฐ๋„ ์žˆ์Œ !

โžœ ์˜ˆ์™ธ๋ฅผ ๋˜์ง„๋‹ค๊ณ  ํ•จ

โœ” ์˜๋„์ ์œผ๋กœ ์˜ˆ์™ธ๋ฅผ ๋˜์งˆ ์ˆ˜(throw) ์žˆ๋Š” ์ƒํ™ฉ

  • ๋ฐฑ์—”๋“œ ์„œ๋ฒ„์™€ ์™ธ๋ถ€ ์‹œ์Šคํ…œ๊ณผ์˜ ์—ฐ๋™์—์„œ ๋ฐœ์ƒํ•˜๋Š” ์—๋Ÿฌ ์ฒ˜๋ฆฌ
    โžœ ๋ฐฑ์—”๋“œ ์„œ๋ฒ„ ์ชฝ์—์„œ ์˜ˆ์™ธ๋ฅผ ์˜๋„์ ์œผ๋กœ ๋˜์ ธ์„œ ํด๋ผ์ด์–ธํŠธ ์ชฝ์— ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ ์ •๋ณด๋ฅผ ์•Œ๋ ค์ค„ ์ˆ˜ ์žˆ์Œ

  • ์‹œ์Šคํ…œ ๋‚ด๋ถ€์—์„œ ์กฐํšŒํ•˜๋ ค๋Š” ๋ฆฌ์†Œ์Šค(์ž์›, Resource)๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ
    โžœ ์„œ๋น„์Šค ๊ณ„์ธต์—์„œ ํ•ด๋‹น ํšŒ์› ์ •๋ณด๊ฐ€ ์—†๋‹ค๋Š” ์˜ˆ์™ธ๋ฅผ ์˜๋„์ ์œผ๋กœ ์ „์†กํ•ด์„œ ํด๋ผ์ด์–ธํŠธ ์ชฝ์— ์•Œ๋ ค์ค„ ์ˆ˜ ์žˆ์Œ

โœ” ์˜ˆ์™ธ ๋˜์ง€๊ธฐ & ์ฒ˜๋ฆฌ ๊ณผ์ •

  1. ํด๋ผ์ด์–ธํŠธ๊ฐ€ Controller์˜ ํ•ด๋‹น ํ•ธ๋“ค๋Ÿฌ ๋ฉ”์„œ๋“œ๋กœ ์š”์ฒญ ๋ณด๋ƒ„

  2. ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค๋ฉด, Service ํด๋ž˜์Šค์—์„œ RuntimeException์„ ๋˜์ง

    Service ํด๋ž˜์Šค ๋‚ด์˜ ํ•ด๋‹น ํ•ธ๋“ค๋Ÿฌ ๋ฉ”์„œ๋“œ์—๋Š” throw ํ‚ค์›Œ๋“œ ์‚ฌ์šฉํ•˜์—ฌ RuntimeException ๊ฐ์ฒด์— ์ ์ ˆํ•œ ์˜ˆ์™ธ ๋ฉ”์‹œ์ง€๋ฅผ ํฌํ•จ
    โ €
    Ex. throw new RuntimeException("Not found member");
    throw new BusinessLogicException(ExceptionCode.MEMBER_NOT_FOUND);

  3. @RestControllerAdvice ์• ๋„ˆํ…Œ์ด์…˜์ด ๋ถ™์€ ํด๋ž˜์Šค์˜ ํ•ด๋‹น ์˜ˆ์™ธ์˜ ์‘๋‹ต์„ ๊ตฌํ˜„ํ•˜๊ณ  ์žˆ๋Š” ๋ฉ”์„œ๋“œ์—์„œ ๊ทธ throwํ•œ ์˜ˆ์™ธ๋ฅผ catchํ•˜์—ฌ ๊ฐ™์ด ๋‚ ๋ผ์˜จ ์˜ˆ์™ธ ๋ฉ”์„ธ์ง€๋ฅผ ์ถœ๋ ฅํ•ด์คŒ


โœ๏ธ ์‚ฌ์šฉ์ž ์ •์˜ ์˜ˆ์™ธ(Custom Exception)

  • ์ƒ์œ„ ํด๋ž˜์Šค์ธ RuntimeException์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹Œ ๊ทธ์˜ ํ•˜์œ„ ํด๋ž˜์Šค๋“ค์ธ ๋”์šฑ ๊ตฌ์ฒด์ ์ธ ์˜ˆ์™ธ๋ฅผ ํ‘œํ˜„ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ

โœ” ์ ์šฉ ๋ฐฉ๋ฒ•

  1. class๊ฐ€ ์•„๋‹Œ enum์œผ๋กœ ์„œ๋น„์Šค ๊ณ„์ธต์—์„œ ๋˜์งˆ Custom Exception์— ์‚ฌ์šฉํ•  ExceptionCode๋ฅผ ์ •์˜
    โžœ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์—์„œ ๋ฐœ์ƒํ•˜๋Š” ๋‹ค์–‘ํ•œ ์œ ํ˜•์˜ ์˜ˆ์™ธ๋ฅผ enum์— ์ถ”๊ฐ€ํ•ด์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Œ

  2. ์˜ˆ์™ธ๋“ค์„ ์ •๋ฆฌํ•œ ExceptionCode๋ฅผ ๋ฉค๋ฒ„ ๋ณ€์ˆ˜๋กœ ๋ฐ›๊ณ  RuntimeException์„ ์ƒ์†๋ฐ›๋Š” BusinessLogicException ํด๋ž˜์Šค๋ฅผ ์ •์˜

( ์ด ๋•Œ, ์ƒ์„ฑ์ž์—์„œ exceptionCode.getMessage()์˜ ๊ฒฝ์šฐ์—๋Š” ์ƒ์œ„ ํด๋ž˜์Šค์˜ ์ƒ์„ฑ์ž๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์— supuer() ํ‚ค์›Œ๋“œ ์‚ฌ์šฉ )

โ— @RestControllerAdvice ์• ๋„ˆํ…Œ์ด์…˜์„ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ํด๋ž˜์Šค์—๋Š” ResponseEntity์™€ ์šฐ๋ฆฌ๊ฐ€ ์ •์˜ํ•œ ErrorResponse ์‚ฌ์šฉ ๊ฐ€๋Šฅ

โœ”๏ธ ResponseEntity
โžœ ๋ฆฌํ„ด๊ฐ’์œผ๋กœ HttpStatus๋ฅผ ๋™์ ์œผ๋กœ ์ง€์ • ๊ฐ€๋Šฅ
โ €
โœ”๏ธ ErrorResponse
โžœ ๊ณ ์ •๋œ HttpStatus๋ฅผ ์ง€์ •ํ•˜๊ธฐ ๋•Œ๋ฌธ์— @ResponseStatus ์• ๋„ˆํ…Œ์ด์…˜์œผ๋กœ HttpStatus๋ฅผ ์ง€์ •ํ•ด์ค˜์•ผํ•จ


๐Ÿ˜œ ์‹ค์Šต

  • projects ๋‚ด์˜ be-template-exception-handle ํŒŒ์ผ

  • git ๋‚ด์˜ be-homework-exception ํŒŒ์ผ


๐ŸŒˆ ๋Š๋‚€์ 

์ €๋ฒˆ ํ•™์Šต๊นŒ์ง€๋Š” ๋”ฐ๋ผ๊ฐ€๊ธฐ ๊ดœ์ฐฎ์•˜๋Š”๋ฐ ์˜ค๋Š˜์€ ๊ฐ‘์ž๊ธฐ ์ฝ”๋“œ ์–‘๋„ ๋งŽ์•„์ง€๋ฉด์„œ ์ดํ•ด๊ฐ€ ์–ด๋ ค์šด ๋ถ€๋ถ„์ด ๊ฝค ์žˆ์—ˆ๋‹ค !!
๊ทธ๋ž˜๋„ ํŽ˜์–ด ํ•™์Šต ํ•˜๋ฉด์„œ ํŽ˜์–ด๋‹˜์ด ๋งŽ์ด ์•Œ๋ ค์ฃผ์…”์„œ ์ดํ•ด๊ฐ€ ๋งŽ์ด ๋˜์—ˆ๋‹ค! ๐Ÿ™Œ
๊ทผ๋ฐ ํ•˜๋ฃจํ•˜๋ฃจ ์‹ค์Šตํ•ด๋ณด๋Š” ์ฝ”๋“œ์˜ ์–‘์ด ๋งŽ๋‹ค๋ณด๋‹ˆ ๋ณต์Šต์„ ์ž์ฃผ ํ•ด์ค˜์•ผ ์žŠ์ง€ ์•Š์„ ๊ฒƒ ๊ฐ™๋‹ค ใ… 

0๊ฐœ์˜ ๋Œ“๊ธ€