๐Ÿฃ Spring/Spring ๊ธฐ๋ณธ

[Spring] ์Šคํ”„๋ง ์˜ˆ์™ธ ์ฒ˜๋ฆฌํ•˜๊ธฐ

TIlearn 2024. 2. 12. 16:32

์Šคํ”„๋ง์˜ ๊ธฐ๋ณธ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ ๋ฐฉ๋ฒ•

 

 

Spring์€ ์—๋Ÿฌ ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•ด ๊ธฐ๋ณธ์ ์œผ๋กœ _BasicErrorConroller_๋ฅผ ๊ตฌํ˜„ํ•ด ๋‘์—ˆ๋‹ค. ๊ทธ๋ž˜์„œ ๋งŒ์•ฝ ๋ณ„๋„๋กœ ์—๋Ÿฌ์— ๊ด€๋ จ๋œ ์„ค์ •์„ ํ•ด๋‘์ง€ ์•Š๋Š”๋‹ค๋ฉด, WAS์—์„œ _/error_๋กœ ์—๋Ÿฌ ์š”์ฒญ์„ ๋‹ค์‹œ ๋ณด๋‚ธ๋‹ค.

 

 

๊ทธ ํ๋ฆ„์€ ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

 

 

_WAS(ํ†ฐ์บฃ)_ โžก๏ธ _ํ•„ํ„ฐ_ โžก๏ธ _์„œ๋ธ”๋ฆฟ(DispatcherServlet)_ โžก๏ธ _์ธํ„ฐ์…‰ํ„ฐ_ โžก๏ธ _์ปจํŠธ๋กค๋Ÿฌ_ โžก๏ธ _์˜ˆ์™ธ ๋ฐœ์ƒ_ โžก๏ธ _์ธํ„ฐ์…‰ํ„ฐ_ โžก๏ธ _์„œ๋ธ”๋ฆฟ(DispatcherServlet)_ โžก๏ธ _ํ•„ํ„ฐ_ โžก๏ธ _WAS(ํ†ฐ์บฃ)_ โžก๏ธ _์„œ๋ธ”๋ฆฟ(DispatcherServlet)_ โžก๏ธ _์ธํ„ฐ์…‰ํ„ฐ_ โžก๏ธ _์ปจํŠธ๋กค๋Ÿฌ(BasicErrorController)_

 

 

์ค‘์š”ํ•œ ์ ์€ ์—๋Ÿฌ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด _WAS_๋กœ ๊ฐ”๋‹ค๊ฐ€ ๋‹ค์‹œํ•œ๋ฒˆ ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค๋Š” ์ ์ด๋‹ค. ์ฉ ๋งˆ์Œ์— ๋“œ๋Š” ๋ฐฉ์‹์€ ์•„๋‹ˆ๋‹ค.

 

 

๋˜ํ•œ _BasicErrorContoller_๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ _timestamp_, _status_, _path_ ๋“ฑ ์‘๋‹ต์„ ํ•ด์ฃผ์ง€๋งŒ, ํด๋ผ์ด์–ธํŠธ์˜ ์ž…์žฅ์—์„œ ๋”์šฑ ์œ ์šฉํ•œ ์‘๋‹ต์„ ๋ฐ›๊ธฐ ์–ด๋ ต๋‹ค.

 

 

_status_์˜ ๊ฒฝ์šฐ WAS์—์„œ ์—๋Ÿฌ๋ฅผ ์ „๋‹ฌ๋ฐ›์•˜๊ธฐ์— 500์œผ๋กœ๋งŒ ๋œฌ๋‹ค. ์ฆ‰, ์ƒํ™ฉ์— ์ ํ•ฉํ•œ ์—๋Ÿฌ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋ณ„๋„์˜ ์„ค์ •์ด ํ•„์š”ํ•˜๋‹ค.

 

 

 

 

 

 

์Šคํ”„๋ง์˜ ๊ธฐ๋ณธ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ ๋ฐฉ๋ฒ•

 

 

Java์—์„œ ์˜ˆ์™ธ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด _try-catch_๋ฌธ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ผ๋ฐ˜์ ์ด์ง€๋งŒ, ๋ชจ๋“  ์ฝ”๋“œ์— ๋ถ™์ด๋ฉด ๊ฐ€๋…์„ฑ์ด ๋–จ์–ด์ง€๊ณ  ๋น„ํšจ์œจ์ ์ด๋‹ค.

 

 

Spring์€ ๋ฉ”์ธ ๋กœ์ง์œผ๋กœ๋ถ€ํ„ฐ ์ด๋Ÿฌํ•œ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ๋ฅผ ๋ถ„๋ฆฌํ•˜๋Š” ๊ฒƒ์„ ์›ํ–ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ ์ „๋žต์„ ์ถ”์ƒํ™”ํ•œ _HandlerExceptionResolver_๋ฅผ ๊ณ ์•ˆํ•˜๊ฒŒ ๋˜์—ˆ๋‹ค. (์ด๋Š” ์ „๋žต ํŒจํ„ด์„ ์‚ฌ์šฉํ•œ ๊ฒƒ์œผ๋กœ ์Šคํ”„๋ง์—์„œ ์‚ฌ์šฉํ•˜๋Š” ์˜์กด์„ฑ ์ฃผ์ž… ๋ฐฉ์‹์ด ์ด๋Ÿฌํ•œ ๊ฒƒ์ด๋‹ค.)

 

 

_ HandlerExceptionResolver_์€ ๋ฐœ์ƒํ•œ ์˜ˆ์™ธ๋ฅผ catch ํ•˜๊ณ  ์ด์— ๊ด€๋ จ๋œ ์ •๋ณด๋ฅผ ์„ค์ •ํ•œ๋‹ค. ๊ทธ๋ ‡๊ฒŒ ํ•จ์œผ๋กœ์จ WAS ์ธก์œผ๋กœ ์˜ˆ์™ธ๊ฐ€ ์ „๋‹ฌ๋˜์ง€ ์•Š๊ณ  WAS ์ž…์žฅ์—์„œ๋Š” ์ •์ƒ์ ์ธ ๋™์ž‘์œผ๋กœ ์ฒ˜๋ฆฌ๋˜์–ด _BasicErrorController_๋ฅผ ์š”์ฒญํ•˜์ง€ ์•Š๋Š”๋‹ค.

 

 

_HandlerExceptionResolver_์˜ ๊ตฌํ˜„์ฒด๋Š” ์ด 4๊ฐ€์ง€๋กœ ๋ชจ๋‘ _Bean_์œผ๋กœ ๋“ฑ๋ก๋˜์–ด ๊ด€๋ฆฌ๋œ๋‹ค.

 

 

1๏ธโƒฃ _DefaultErrorAttributes_ : ์ง์ ‘์ ์œผ๋กœ ์—๋Ÿฌ๋ฅผ ์ฒ˜๋ฆฌํ•˜์ง€ ์•Š๊ณ , ๊ทธ์— ๊ด€๋ จ๋œ ์†์„ฑ์„ ์ €์žฅํ•œ๋‹ค.

 

2๏ธโƒฃ _ExceptionHandlerExceptionResolver_ : ControllerAdvice์•ˆ์— ์žˆ๋Š” ExceptionHandler ์ฒ˜๋ฆฌ

 

3๏ธโƒฃ _ResponseStatusExceptionResolver_ : ResponseStatus, ResponseStatusException ์ฒ˜๋ฆฌ

 

4๏ธโƒฃ _DefaultHandlerExceptionResolver_ : ์Šคํ”„๋ง ๋‚ด๋ถ€ ๊ธฐ๋ณธ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ

 

 

์†์„ฑ๋งŒ์„ ์ €์žฅํ•˜๋Š” _DefaultErrorAttributes_๋ฅผ ์ œ์™ธํ•œ ๋‚˜๋จธ์ง€ _Resolver_์— ๋Œ€ํ•ด์„œ๋งŒ _HandlerExceptionResolverComposite_์—์„œ ๋ชจ์•„์„œ ๊ด€๋ฆฌํ•œ๋‹ค. (์ปดํฌ์ง€ํŠธ ํŒจํ„ด ์ ์šฉ)

 

 

์ด๋ ‡๊ฒŒ ๋ชจ์•„์ง„ _ExceptionResolver_๋ฅผ ๋™์ž‘์‹œํ‚ค๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•ด Spring์ด ์ œ๊ณตํ•˜๋Š” ๋„๊ตฌ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

 

1๏ธโƒฃ _ResponseStatus_

 

2๏ธโƒฃ _ResponseStatusException_

 

3๏ธโƒฃ _ExceptionHandler_

 

4๏ธโƒฃ _ControllerAdvice_, _RestControllerAdvice_

 

 

 

 

ResponseStatus

 

_@ResponseStatus_ ์–ด๋…ธํ…Œ์ด์…˜์€ ์—๋Ÿฌ์— ๋Œ€ํ•œ ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝ์‹œ์ผœ์ฃผ๋Š” ์–ด๋…ธํ…Œ์ด์…˜์ด๋‹ค. ์ด์ „์— WAS๊นŒ์ง€ ๊ฐ”๊ธฐ์— 500์œผ๋กœ๋ฐ–์— ์ฒ˜๋ฆฌ ๋ชปํ–ˆ๋˜ ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝ์‹œ์ผœ ์ค„ ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ด๋‹ค.

 

 

์ ์šฉ์‹œ์ผœ์ค„ ์ˆ˜ ์žˆ๋Š” ๋Œ€์ƒ์œผ๋กœ๋Š” _Exception ํด๋ž˜์Šค_, ๋ฉ”์„œ๋“œ์— _@ExceptionHandler_์™€ ๊ฐ™์ด, ํด๋ž˜์Šค์— @RestControllerAdvice์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•œ๋‹ค.

 

 

ํ•˜์ง€๋งŒ ๊ฒฐ๊ตญ์—๋Š” WAS๋กœ๊นŒ์ง€ ์˜ˆ์™ธ๊ฐ€ ์ „๋‹ฌ๋˜๋ฉฐ ์—๋Ÿฌ์— ๋Œ€ํ•œ ์‘๋‹ต ๋‚ด์šฉ์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†๋‹ค๋Š” ๋‹จ์ ์ด ์žˆ๋‹ค.

 

 

 

 

 

ResponseStatusException

 

์˜ˆ์™ธ ํด๋ž˜์Šค์™€์˜ ๊ฒฐํ•ฉ๋„๋ฅผ ๋‚ฎ์ถ”๊ณ , ์‘๋‹ต ๋‚ด์šฉ์„ ์ง์ ‘ ํ”„๋กœ๊ทธ๋ž˜๋จธ๊ฐ€ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋Š” _ResponseStatus_์˜ ๋Œ€์•ˆ์œผ๋กœ ๋‚˜์˜จ ๊ฒƒ์ด๋‹ค.

 

@GetMapping("/product/{id}")
public ResponseEntity<Product> getProduct(@PathVariable String id) {
    try {
        return ResponseEntity.ok(productService.getProduct(id));
    } catch (NoSuchElementFoundException e) {
        throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Item Not Found");
    }
}

 

 

ํ•˜์ง€๋งŒ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ WAS๊นŒ์ง€ ์˜ˆ์™ธ๊ฐ€ ์ „๋‹ฌ๋˜๋ฉฐ Spring ๋‚ด๋ถ€์—์„œ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์—†๋‹ค.

 

 

 

 

 

ExceptionHandler

 

๊ฐ€์žฅ ๋งŽ์ด ์‚ฌ์šฉํ•˜๋Š” ์ปค์Šคํ…€ ์—๋Ÿฌ ์ฒ˜๋ฆฌ ๋ฐฉ์‹์ด๋‹ค. ์ ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋Œ€์ƒ์€ ์ปจํŠธ๋กค๋Ÿฌ์˜ ๋ฉ”์„œ๋“œ์ด๊ฑฐ๋‚˜ _@ConrollerAdvice_, ํ˜น์€ _@RestControllerAdvice_๊ฐ€ ๋ถ™์€ ํด๋ž˜์Šค์˜ ๋ฉ”์„œ๋“œ์ด๋‹ค.

 

@RestControllerAdvice
public class GlobalRestControllerAdvice extends ResponseEntityExceptionHandler {

    @ExceptionHandler(ReservationException.class)
    public ResponseEntity<Object> handleInvalidArgument(ReservationException e) {
        ErrorCode errorCode = e.getReservationErrorCode();
        return handleExceptionInternal(errorCode, e.getMessage());
    }

    private ResponseEntity<Object> handleExceptionInternal(ErrorCode errorCode, String message) {
        return ResponseEntity.status(errorCode.getHttpStatus())
                .body(message);
    }
}

 

 

์œ„์™€ ๊ฐ™์ด _@ExceptionHandler_๋Š” Exception ํด๋ž˜์Šค๋“ค์„ ์†์„ฑ์œผ๋กœ ๋ฐ›์•„์„œ ์˜ˆ์™ธ๋ฅผ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ฝ”๋“œ๋ฅผ ๋ด๋„ ์•Œ ์ˆ˜ ์žˆ๋“ฏ์ด ์‘๋‹ต ๋‚ด์šฉ๊ณผ status๋ฅผ ๋งค์šฐ ์ž์œ ๋กญ๊ฒŒ ์ง€์ •ํ•ด์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

 

 

๋‹จ, ์ฃผ์˜ํ•  ์ ์€ _@ExceptionHandler_๋กœ ๋“ฑ๋ก๋œ ์˜ˆ์™ธ ํด๋ž˜์Šค์™€ ๋ฉ”์„œ๋“œ์˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋ฐ›๋Š” ์˜ˆ์™ธ ํด๋ž˜์Šค๊ฐ€ ๋™์ผํ•ด์•ผ ํ•œ๋‹ค. ์•„๋‹ˆ๋ฉด, ๋Ÿฐํƒ€์ž„ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

 

 

_@ExceptionHandler_์˜ ๋ฐ˜ํ™˜ ํƒ€์ž…์œผ๋กœ๋Š” ์—ฌ๋Ÿฌ ๊ฐ€์ง€๊ฐ€ ์˜ฌ ์ˆ˜ ์žˆ๋‹ค. ์œ„ ์˜ˆ์‹œ๋Š” _ResponseEntity_๊ฐ€ ์˜ค์ง€๋งŒ void, String ๋“ฑ ์—ฌ๋Ÿฌ ๊ฐ€์ง€ ๋ฐ˜ํ™˜ ๊ฐ’์ด ์˜ฌ ์ˆ˜๋„ ์žˆ๋‹ค.

 

 

 

 

 

 

 

@ControllerAdvice, @RestControllerAdvice

 

 

_@Controller_์™€ _@RestController_์˜ ์ฐจ์ด์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ _@ResponseBody_๊ฐ€ ๋ถ™์–ด์„œ json ํ˜•์‹์œผ๋กœ ์‘๋‹ต์„ ์ฃผ๋Š” ๊ฐ€์—์„œ ์ฐจ์ด๊ฐ€ ์žˆ๋‹ค.

 

 

_@ControllerAdvice_์—๋Š” _@Component_ ์–ด๋…ธํ…Œ์ด์…˜์ด ๋ถ™์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ์Šคํ”„๋ง ๋นˆ์œผ๋กœ์จ ๊ด€๋ฆฌ๋œ๋‹ค. ์ฆ‰, ์—ฌ๋Ÿฌ ์ปจํŠธ๋กค๋Ÿฌ์— ๋Œ€ํ•ด์„œ ์ „์—ญ์ ์œผ๋กœ _ExceptionHandler_๋ฅผ ์ ์šฉ์‹œ์ผœ์ค€๋‹ค.

 

 

๋งŒ์•ฝ ํŠน์ • ํด๋ž˜์Šค์—๋งŒ ํ•œ์ •์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด, _basePackages_๋“ฑ์„ ์„ค์ •ํ•˜์—ฌ ์ œํ•œํ•  ์ˆ˜ ์žˆ๋‹ค.

 

 

Spring์—์„œ๋Š” Spring์˜ ์˜ˆ์™ธ๋ฅผ ๋ฏธ๋ฆฌ ์ฒ˜๋ฆฌํ•ด ๋‘”, _ResponseEntityExceptionHandler_๋ผ๋Š” ์ถ”์ƒ ํด๋ž˜์Šค๊ฐ€ ์žˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ์ž˜๋ชป๋œ URI๋ฅผ ํ˜ธ์ถœํ–ˆ์„ ๊ฒฝ์šฐ ์ด _ResponseEntityExceptionHandler_๋ฅผ ์ƒ์†๋ฐ›์œผ๋ฉด ์—ฌ๊ธฐ์„œ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.

 

 

๋งŒ์•ฝ ์ƒ์†๋ฐ›์ง€ ์•Š๋Š”๋‹ค๋ฉด _DefaultHandlerExceptionResolver_์—์„œ ์ฒ˜๋ฆฌํ•˜๊ฒŒ ๋˜์–ด ์ผ๊ด€๋˜์ง€ ๋ชปํ•œ ์‘๋‹ต์„ ๋ฐ›๊ธฐ์— _ResponseEntityExceptionHandler_๋ฅผ ์ƒ์†์‹œํ‚ค๋Š” ํŽธ์ด ์ข‹๋‹ค.

 

 

 

 

 

 

 

๊ทธ๋ ‡๋‹ค๋ฉด ์˜ˆ์™ธ ์ฒ˜๋ฆฌ๋Š”?

 

 

๊ฒฐ๊ตญ, 3๊ฐ€์ง€์˜ Resolver ์ค‘์—์„œ _ExceptionHandlerExceptionResolver_๋ฅผ ๋ฐ›๋Š” ํŽธ์ด ์ข‹๋‹ค. ๊ฒฐ๊ณผ์ ์œผ๋กœ WAS๊นŒ์ง€ ์˜ˆ์™ธ๊ฐ€ ์ „๋‹ฌ๋˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

 

 

๋”ฐ๋ผ์„œ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ ์ˆœ์„œ๋„ _ExceptionHandlerExceptionResolver_(_@ExceptionHandler_์ฒ˜๋ฆฌ)๊ฐ€ ๊ฐ€์žฅ ๋†’์œผ๋ฉฐ, ๊ทธ๋‹ค์Œ์œผ๋กœ๋Š” _ResponseStatusExceptionResolver_(_@ResponseStatus_, _ResponseStatusException_์ฒ˜๋ฆฌ), _DefaultHandlerExceptionResolver_(๊ธฐ๋ณธ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ)๊ฐ€ ๋งˆ์ง€๋ง‰์ด๋‹ค.

 

 

 

 

 

 

 

 

 

ControllerAdvice๋ฅผ ์‚ฌ์šฉํ•œ ์ปค์Šคํ…€ ์˜ˆ์™ธ ์„ค์ •

 

 

1๏ธโƒฃ ์˜ˆ์™ธ ํด๋ž˜์Šค๋ฅผ ์ƒ์„ฑํ•ด ์ฃผ์ž.

 

package roomescape.common.exception;

import lombok.Builder;
import lombok.Getter;
import lombok.RequiredArgsConstructor;

@Getter
@RequiredArgsConstructor
public class ReservationException extends RuntimeException {

    private ReservationErrorCode reservationErrorCode;
    private String message;

    public ReservationException(ReservationErrorCode reservationErrorCode) {
        this.reservationErrorCode = reservationErrorCode;
        this.message = reservationErrorCode.getMessage();
    }
}

 

 

์ธ์ž๋กœ๋Š” ์—๋Ÿฌ ์ฝ”๋“œ๋ฅผ ๋ฐ›๊ณ  ์žˆ์œผ๋ฉฐ message๋„ ์ง์ ‘ ์ฃผ์ž…๋ฐ›์•„ ์˜์กด๋„๋ฅผ ๋‚ฎ์ถ”์—ˆ๋‹ค.

 

 

์ด๋•Œ, _RuntimeException_(์–ธ์ฒดํฌ)์„ ์ƒ์†๋ฐ›์„ ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ์ด๋Ÿฌํ•œ ์ด์œ ๋Š” ์ปค์Šคํ…€์œผ๋กœ ์„ค์ •ํ•œ ์ผ๋ฐ˜์ ์ธ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์€ ๋”ฐ๋กœ catch ํ•˜์—ฌ ์„ค์ •ํ•ด ์ค„ ์ˆ˜ ์žˆ๋Š” ์žˆ๋Š” ๊ฒŒ ์—†๊ธฐ ๋•Œ๋ฌธ์ด๋ฉฐ ๋ฌด๋ถ„๋ณ„ํ•œ throw๋ฅผ ๋˜์ง€์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

 

 

๋˜ํ•œ Spring์€ ๋‚ด๋ถ€์ ์œผ๋กœ ๋ฐœ์ƒํ•œ ์˜ˆ์™ธ์—์„œ ์–ธ์ฒดํฌ ์˜ˆ์™ธ๋Š” ์ž๋™์œผ๋กœ ๋กค๋ฐฑํ•˜๋„๋ก ์ฒ˜๋ฆฌํ•œ๋‹ค.

 

 

 

2๏ธโƒฃ ENUM ํด๋ž˜์Šค ์ƒ์„ฑ

 

package roomescape.common.exception;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;


@Getter
@RequiredArgsConstructor
public enum ReservationErrorCode implements ErrorCode {

    INVALID_ARGUMENT_ERROR(HttpStatus.BAD_REQUEST, "ํ•„์š”ํ•œ ์ธ์ž๊ฐ€ ์ฑ„์›Œ์ง€์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค."),
    NO_DELETE_RESERVATION_FOUND(HttpStatus.BAD_REQUEST, "์‚ญ์ œํ•  ์˜ˆ์•ฝ์ด ์—†์Šต๋‹ˆ๋‹ค.");

    private final HttpStatus httpStatus;
    private final String message;
}

 

 

Exception์— ๋Œ€ํ•œ ์ƒํƒœ ์ฝ”๋“œ์™€ ๋‚ด์šฉ์„ ๋ฏธ๋ฆฌ ์ƒ์„ฑํ•ด ๋‘์–ด, ์‹ค์ œ ๋กœ์ง ์ค‘์— ์‰ฝ๊ฒŒ ๊บผ๋‚ด ์“ธ ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ค€๋‹ค.

 

 

 

 

3๏ธโƒฃ _@RestControllerAdvice_ ํด๋ž˜์Šค ์ถ”๊ฐ€

 

์ด์ œ ์ „์—ญ์ ์œผ๋กœ ์—๋Ÿฌ๋ฅผ ์ฒ˜๋ฆฌํ•ด ์ฃผ๊ธฐ ์œ„ํ•ด _@RestControllerAdvice_๋ฅผ ์ถ”๊ฐ€ํ•ด ์ฃผ์ž.

 

package roomescape.common.exception;

import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

@RestControllerAdvice
public class GlobalRestControllerAdvice extends ResponseEntityExceptionHandler {

    @ExceptionHandler(ReservationException.class)
    public ResponseEntity<Object> handleInvalidArgument(ReservationException e) {
        ErrorCode errorCode = e.getReservationErrorCode();
        return handleExceptionInternal(errorCode, e.getMessage());
    }

    private ResponseEntity<Object> handleExceptionInternal(ErrorCode errorCode, String message) {
        return ResponseEntity.status(errorCode.getHttpStatus())
                .body(message);
    }
}

 

์—ฌ๊ธฐ์„œ ๋ฐ˜ํ™˜ ๊ฐ’์— ๋Œ€ํ•œ ์ปค์Šคํ…€์„ ์ง„ํ–‰ํ•ด ์ค„ ์ˆ˜ ์žˆ๋‹ค. ์ด๋•Œ, ์—๋Ÿฌ ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค์–ด ๋ฐ˜ํ™˜ ํฌ๋งท์„ ์„ค์ •ํ•ด ์ฃผ๊ธฐ๋„ ํ•œ๋‹ค.

 

 

 

 

4๏ธโƒฃ ์˜ˆ์™ธ ์ฒ˜๋ฆฌํ•˜๊ธฐ

 

@Override
public void deleteReservation(Long id) {
    final String sql = "delete from reservation where id = ?";
    int rowsAffected = jdbcTemplate.update(sql, id);
    if (rowsAffected == 0) {
        throw new ReservationException(ReservationErrorCode.NO_DELETE_RESERVATION_FOUND);
    }
}

 

์ด์ œ ๋“ฑ๋ก๋œ ์—๋Ÿฌ ํด๋ž˜์Šค๋ฅผ ํ†ตํ•ด์„œ Spring ๋‚ด๋ถ€์—์„œ ์—๋Ÿฌ๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ๋‹ค.

 

 

 

 

 

References