Spring

@RestControllerAdvice 예외 처리

setung 2021. 10. 31. 23:01

기본적으로 스프링은 요청에 대해서 예외가 발생하면 Whitelabel Error Page가 보여진다 

하지만 일반 사용자한테 저런 에러 페이지를 보여주는 것보다 의미 있는(?), 알 수 있는 문구를 보여주면 좋을 것이다.

 

스프링에서는 @RestControllerAdvic를 사용해서 예외를 깔끔하게 처리할 수 있다. 

예외가 발생하면 미리 정의해놓은 응답으로 보낼 수 있다.

 

간단하게 예제를 만들어 보겠다.

ErrorCode

@RequiredArgsConstructor
@Getter
public enum ErrorCode {

    NOT_FOUND_USER(HttpStatus.NOT_FOUND, "회원을 찾을 수 없습니다."),
    NEED_TO_LOGIN(HttpStatus.UNAUTHORIZED, "로그인이 필요합니다.");

    private final HttpStatus httpStatus;
    private final String description;

}

에러 코드를 보관하는 enum 클래스이다. Http 상태와 설명을 가지고 있다. 필요에 따라 이 클래스에서 에러 코드를 추가하면 된다. Enum 클래스를 사용하기 전에는 하나하나 예외 클래스를 만들었었는데 관리하기가 까다로웠는데 Enum은 깔끔하다.

 

CustomException

@Getter
public class CustomException extends RuntimeException {

    private final ErrorCode errorCode;

    public CustomException(ErrorCode errorCode) {
        this.errorCode = errorCode;
    }

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

 

RuntimeExcepion을 상속하는 CustomException을 만들었다. RuntimeException에 간략히 설명하자면 Unchecked Exception으로 예외 처리를 강제하지 않는다. 서버 특성상 상태를 유지하지 않기 때문에 잘못된 요청이 들어오면 바로잡기보단 예외를 던지고 끝내는게 깔끔하다. 

 

ErrorResponse

@Getter
@Builder
public class ErrorResponse {
    private final LocalDateTime timestamp = LocalDateTime.now();
    private final int status;
    private final String error;
    private final String code;
    private final String message;
    private final String description;

    public static ResponseEntity<ErrorResponse> toResponseEntity(CustomException e) {
        ErrorCode errorCode = e.getErrorCode();
        return ResponseEntity
                .status(errorCode.getHttpStatus())
                .body(ErrorResponse.builder()
                        .status(errorCode.getHttpStatus().value())
                        .error(errorCode.getHttpStatus().name())
                        .code(errorCode.name())
                        .message(errorCode.getDetail())
                        .description(e.getMessage())
                        .build()
                );
    }
}

예외 발생 시 반환하는 응답 객체이다.

(여기서 좀 삽질을 했는데 @Getter가 없으면 제대로 작동이 안 한다. 내부적으로 get 프로퍼티가 필요한가 보다)

 

GlobalExceptionHandler

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(value = {CustomException.class})
    protected ResponseEntity<ErrorResponse> handleCustomException(CustomException e) {
        return ErrorResponse.toResponseEntity(e);
    }

}

@RestControllerAdvice을 클래스명 위에 추가해준다.

@ExceptionHandler의 value에 처리하고 싶은 예외 클래스를 기입하면 된다. 

CustomException.class 예외가 발생하면 handleCustomException 메소드가 실행이 되어 응답 객체를 반환한다.

 

AdviseController

@RestController
@RequestMapping("/advise")
public class AdviseController {

    @GetMapping
    public String throwException() {
        throw new CustomException(ErrorCode.NEED_TO_LOGIN);
    }

}

테스트를 해보니 정상적으로 ErrorResponse 객체가 json으로 반환되었다.

 

 

참고 : Spring Exception Handling :: 뱀귤 블로그 (tistory.com)