[Spring]@ExceptionHandler

๊น€์ •๋ฏผยท2024๋…„ 4์›” 18์ผ
1
post-thumbnail

์Šคํ”„๋ง์—์„œ ์—๋Ÿฌ ํ•ธ๋“ค๋ง์„ ํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ธ @ExceptionHandler์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ณด์ž.

๐Ÿ’ก @ExceptionHandler

@ExceptionHandler๋Š” Exception ํด๋ž˜์Šค๋“ค์„ ์†์„ฑ์œผ๋กœ ๋ฐ›์•„ ์ฒ˜๋ฆฌํ•  ์˜ˆ์™ธ๋ฅผ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

@RestController
public class SimpleController {

    // ...

    @ExceptionHandler(value = IllegalArgumentException.class)
    public ResponseEntity<String> invokeError(IllegalArgumentException e) {
         ...
				return new ResponseEntity<>("error Message", HttpStatus.BAD_REQUEST);
    }
}

์—ฌ๋Ÿฌ๊ฐœ์˜ Exception์„ ์žก์•„์•ผํ•œ๋‹ค๋ฉด, @ExceptionHandler(IOException.class)์ฒ˜๋Ÿผ ํฌ๊ด„์ ์ธ๊ฒŒ ์•„๋‹Œ @ExceptionHandler(FileSystemException.class, RemoteException.class)๋กœ ๊ตฌ์ฒด์ ์œผ๋กœ ๋ช…์‹œํ•ด์ฃผ๋Š” ๊ฒƒ์„ ๊ถŒ์žฅํ•œ๋‹ค๊ณ  ํ•œ๋‹ค.

@ExceptionHandler๋Š” ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•œ ์ปจํŠธ๋กค๋Ÿฌ์—์„œ๋งŒ ๋ฐœ์ƒํ•˜๋Š” ์˜ˆ์™ธ๋งŒ ์ฒ˜๋ฆฌ๋œ๋‹ค.

๐Ÿ’ก @ControllerAdvice, @RestControllerAdvice

์ „์—ญ์ ์œผ๋กœ @ExceptionHandler๋ฅผ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์–ด๋…ธํ…Œ์ด์…˜์ด๋‹ค. ๋‘ ๊ฐœ์˜ ์ฐจ์ด๋Š” @Controller์™€ RestController์™€ ๊ฐ™์ด @ResponseBody๊ฐ€ ๋ถ™์–ด ์žˆ์–ด ์‘๋‹ต์„ Json์œผ๋กœ ๋‚ด๋ ค์ค€๋‹ค๋Š” ์ ์ด ์ฐจ์ด์ ์ด๋‹ค.

๐ŸŒธ ์žฅ์ 

  1. ํ•˜๋‚˜์˜ ํด๋ž˜์Šค๋กœ ๋ชจ๋“  ์ปจํŠธ๋กค๋Ÿฌ์— ๋Œ€ํ•ด ์ „์—ญ์ ์œผ๋กœ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅํ•จ

  2. ์ง์ ‘ ์ •์˜ํ•œ ์—๋Ÿฌ ์‘๋‹ต์„ ์ผ๊ด€์„ฑ์žˆ๊ฒŒ ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ๋‚ด๋ ค์ค„ ์ˆ˜ ์žˆ์Œ

  3. ๋ณ„๋„์˜ try-catch๋ฌธ์ด ์—†์–ด ์ฝ”๋“œ์˜ ๊ฐ€๋…์„ฑ์ด ๋†’์•„์ง

๐Ÿงจ ์ฃผ์˜์‚ฌํ•ญ

  1. ํ•œ ํ”„๋กœ์ ํŠธ๋‹น ํ•˜๋‚˜์˜ ControllerAdvice๋งŒ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.

  2. ๋งŒ์•ฝ ์—ฌ๋Ÿฌ ControllerAdvice๊ฐ€ ํ•„์š”ํ•˜๋‹ค๋ฉด basePackages๋‚˜ annotations ๋“ฑ์„ ์ง€์ •ํ•ด์•ผ ํ•œ๋‹ค.

  3. ์ง์ ‘ ๊ตฌํ˜„ํ•œ Exception ํด๋ž˜์Šค๋“ค์€ ํ•œ ๊ณต๊ฐ„์—์„œ ๊ด€๋ฆฌํ•œ๋‹ค.

๐Ÿช„ ์ ์šฉ

๐Ÿ’Ž 1. ์—๋Ÿฌ์ฝ”๋“œ ์ •์˜

@JsonFormat(shape = Shape.OBJECT) //์ง๋ ฌํ™”ํ• ๋•Œ ์–ด๋–ค ๊ฐ’์˜ ๋ชจ์Šต์œผ๋กœ ์ง๋ ฌํ™”๋ ์ง€
@RequiredArgsConstructor // ํ•„์ˆ˜(final)๋ณ€์ˆ˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๊ฐ€์ง€๋Š” ์ƒ์„ฑ์ž ์ƒ์„ฑ
@Getter
public enum ErrorCode {// enum ํด๋ž˜์Šค

    //Common -> http ์š”์ฒญ์‹œ ๋ฐœ์ƒํ• ๋งŒํ•œ ์˜ˆ์™ธ
    INVALID_INPUT_VALUE(HttpStatus.BAD_REQUEST, "Common-001", " Invalid Input Value"),
    METHOD_NOT_ALLOWED(HttpStatus.METHOD_NOT_ALLOWED, "Common-002", " Invalid Http Method"),
    ENTITY_NOT_FOUND(HttpStatus.BAD_REQUEST,"Common-003", " Entity Not Found"),
    INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "Common-004", "Server Error"),
    INVALID_TYPE_VALUE(HttpStatus.BAD_REQUEST,"Common-005", " Invalid Type Value"),
    HANDLE_ACCESS_DENIED(HttpStatus.FORBIDDEN, "Common-006", "Access is Denied"),

    //Member Validation
    EMAIL_DUPLICATION(HttpStatus.BAD_REQUEST, "Member-001", "Email is Duplication"),
    LOGIN_INPUT_INVALID(HttpStatus.BAD_REQUEST, "Member-002", "Login input is invalid"),

    //room Validation


    //...๋“ฑ
    ;

    private final HttpStatus status; // http ์ƒํƒœ์ฝ”๋“œ
    private final String code;//์—๋Ÿฌ์ฝ”๋“œ
    private final String message;//์—๋Ÿฌ๋ฉ”์‹œ์ง€

}

๐Ÿ’Ž 2. ์ปค์Šคํ…€ ์˜ˆ์™ธ

  • ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์•ˆ์—์„œ ์˜ˆ์™ธ๋ฅผ ๋ฐœ์ƒ์‹œ์ผœ์•ผํ• ๋•Œ ์—๋Ÿฌ์ฝ”๋“œ๋ฅผ ์ด์šฉํ•˜์—ฌ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด์„œ ๋งŒ๋“  ์˜ˆ์™ธ
@Getter
public class BusinessException extends RuntimeException{ //api ๋‚ด๋ถ€๋™์ž‘ํ• ๋•Œ(๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์—์„œ) ๋ฐœ์ƒํ•˜๋Š” ์ผ๋ฐ˜์ ์ธ ์˜ˆ์™ธ๋“ค

    private final ErrorCode errorCode;

    public BusinessException(String message, ErrorCode errorCode) {
        super(message);
        this.errorCode = errorCode;
    }

    public BusinessException(ErrorCode errorCode) {
        super(errorCode.getMessage());
        this.errorCode = errorCode;
    }
}

๐Ÿ’Ž 3. ์ „์—ญ ์˜ˆ์™ธ์ฒ˜๋ฆฌ

@RestControllerAdvice
@Slf4j
public class ExceptionHandlerAdvice {

    /*
    @Valid ๋˜๋Š” @Validated๋กœ binding error ๋ฐœ์ƒ์‹œ ๋ฐœ์ƒํ•˜๋Š” ์˜ˆ์™ธ
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    protected ResponseEntity<Object> handleMethodArgumentNotValid(
        MethodArgumentNotValidException e) {
        log.warn("handleMethodArgumentNotValidException", e);
        final ErrorResponse response = ErrorResponse.of(ErrorCode.INVALID_INPUT_VALUE,
            e.getBindingResult());
        return new ResponseEntity<>(response, HttpStatus.valueOf(response.getStatus()));
    }
    /*
    enum type์ด ์ผ์น˜ํ•˜์ง€ ์•Š์•„ binding ๋ชปํ• ๊ฒฝ์šฐ ๋ฐœ์ƒ
     */
    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    protected ResponseEntity<ErrorResponse> handleMethodArgumentTypeMismatchException(
        MethodArgumentTypeMismatchException e) {
        log.error("handleMethodArgumentTypeMismatchException", e);
        final ErrorResponse response = ErrorResponse.of(e);
        return new ResponseEntity<>(response, HttpStatus.valueOf(response.getStatus()));
    }
    /*
    ์ง€์›ํ•˜์ง€ ์•Š์€ Http Method๋ฐฉ์‹์œผ๋กœ ํ˜ธ์ถœํ•  ๊ฒฝ์šฐ ๋ฐœ์ƒ
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    protected ResponseEntity<Object> handleHttpRequestMethodNotSupported(
        HttpRequestMethodNotSupportedException e) {
        log.error("handleHttpRequestMethodNotSupportedException", e);
        final ErrorResponse response = ErrorResponse.of(ErrorCode.METHOD_NOT_ALLOWED);
        return new ResponseEntity<>(response, HttpStatus.valueOf(response.getStatus()));
    }

    @ExceptionHandler(BusinessException.class) //๋น„์ฆˆ๋‹ˆ์Šค๋กœ์ง์—์„œ ๋˜์ ธ์ง€๋Š”,๋ฐœ์ƒ๋˜๋Š” ์˜ˆ์™ธ๋“ค์— ๋Œ€ํ•œ ์ฒ˜๋ฆฌ (validation์˜ค๋ฅ˜๋“ค๊ณผ ๊ฐ™์€)
    protected ResponseEntity<ErrorResponse> handleBusinessException(BusinessException e) {
        log.error("handle๋‚ด๋ถ€๋กœ์ง์—๋Ÿฌ(BusinessException)", e);
        final ErrorCode code = e.getErrorCode();
        final ErrorResponse response = ErrorResponse.of(code);
        return new ResponseEntity<>(response, HttpStatus.valueOf(response.getStatus()));
    }

    @ExceptionHandler(Exception.class) // ์œ„์˜ ์˜ˆ์™ธ๋“ค์—์„œ ๊ฑธ๋Ÿฌ์ง€์ง€์•Š์€ ๋‚˜๋จธ์ง€ ์˜ˆ์™ธ๋“ค์— ๋Œ€ํ•œ ์ฒ˜๋ฆฌ
    protected ResponseEntity<ErrorResponse> handleException(Exception e) {
        log.error("handleAllException", e);
        final ErrorResponse response = ErrorResponse.of(ErrorCode.INTERNAL_SERVER_ERROR);
        return new ResponseEntity<>(response, HttpStatus.valueOf(response.getStatus()));
    }
}

๋งˆ๋ฌด๋ฆฌ

์—๋Ÿฌ ์ฒ˜๋ฆฌํ•  ๋•Œ try catch ๋ฌธ์„ ๋งŽ์ด ์ž‘์„ฑํ•ด์„œ ์†Œ์Šค๊ฐ€ ๋ณต์žกํ•˜๊ณ  ๊ฐ€๋…์„ฑ์ด ๋–จ์–ด์กŒ์ง€๋งŒ @ExceptionHandler์„ ์‚ฌ์šฉํ•จ์œผ๋กœ ์ „์—ญ์ ์ธ ์—๋Ÿฌ ๊ด€๋ฆฌ๋ฅผ ํ•  ์ˆ˜ ์žˆ์–ด ๊ต‰์žฅํžˆ ํŽธ๋ฆฌํ–ˆ๋‹ค. ์ž์ฃผ ์ด์šฉํ•ด์•ผ๊ฒ ๋‹ค.


์ถœ์ฒ˜: https://mangkyu.tistory.com/204 [MangKyu's Diary:ํ‹ฐ์Šคํ† ๋ฆฌ]

์ถœ์ฒ˜: https://keeeeeepgoing.tistory.com/175 [Spring] ์˜ˆ์™ธ์ฒ˜๋ฆฌ (@ExceptionHandler , @ControllerAdvice,@RestControllerAdvice), ์—๋Ÿฌ์ฝ”๋“œ,์‹ค์ œ ์ ์šฉ [๋ฏธ์™„]

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