web/SpringBoot

[REST API] CustomException 처리(@StandardException)

뽀리님 2023. 11. 16. 13:00

개발하다보면 에러를 애플리케이션에  맞는 Exception 만들 필요가 생긴다.

RestFul API 를 만들시 공통된 포맷으로 클라이언트에게 뿌려주고 싶은 경우!

 

그래서 간단하게 만들어봄

 

일단 내가 새롭게 만들 예외가 Checked 예외인지 Unchecked 예외인지 판단한 후에 Checked예외라면, Exception 상속받고 Unchecked(컴파일할때 잡히지 않는) 예외라면 RuntimeException 상속받아서 구현하자.

나는 컴파일할때 잡히지 않는 에러처리를 위해 RuntimeException 상속을 받아서 구현하였다.

 

Lombok에서 제공하는 @StandardException 어노테이션으로 간편하게 커스텀 예외를 만들수있다.

 

* 생성자를 만들자

상황에 따라서 다르겠지만 기본적으로 아래의 4종류의 생성자를 구현해주면 모든 상황에서 사용이 가능하다.
(반드시 4가지 모두 구현해야하는 것은 아니고, 필요한 것만 구현해도 괜찮다.)

  • 기본 생성자 (No arguments)
  • Message-only 생성자
  • Cause-only 생성자
  • 완전체(?) 생성자

1. 그냥 @StandardException 을 쓰자.

Lombok에서 제공하는 @StandardException 쓰면, 기본적인 예외처리용 생성자 4가지를 모두 만들어준다. , 아래와 같이 아주 심플하게 사용할 있게 된다.

 

- ResultException.java

@StandardException
public class ResultException extends RuntimeException {
    @Getter
    ResultErrorCode resultErrorCode;

    public ResultException(ResultErrorCode errorCode){
        super(errorCode.getMessage());
        this.resultErrorCode = errorCode;
    }

    public ResultException(String errorMsg){
        super(errorMsg);
    }

}

 

에러코드도 어플리케이션에 맞게 커스텀하기위해 따로 만들어주었다.

- ResultErrorCode.java

@AllArgsConstructor
@Getter
public enum ResultErrorCode {
    // API 관련
    API_RESPONSE_ERROR(4000, "API ERROR RESPONSE"),

    // 공통에러
    COMMON_UNKNOWN_ERROR(9999, "시스템 에러입니다.");
    private final int code;
    private final String message;
}

 

 

2. @RestControllerAdvice로 GlobalException 도 처리하자

개발하다보면 try-catch  문으로 예외처리를 할텐데, 다른 예외면 모를까 중복적으로 catch 되는 부분이 많아지면 가독성도 떨어지고, 코드라인도 길어진다. 이부분을 해결하고자 나온게 @ControllerAdvice 다.

 

@ControllerAdvice?

@ExceptionHandler, @ModelAttribute, @InitBinder 가 적용된 메서드들에 AOP를 적용해 Controller 단에 적용하기 위해 고안된 어노테이션이라고 한다.

클래스에 선언하면 되며, 모든 @Controller 대한, 전역적으로 발생할 있는 예외를 잡아서 처리할 있다.

 

@RestControllerAdvice?

@ControllerAdvice와 @ResponseBody를 합쳐놓은 어노테이션이다. @ControllerAdvice와 동일한 역할을 수행하고, 추가적으로 @ResponseBody를 통해 객체를 리턴할 수도 있다.

따라서 단순히 예외만 처리하고 싶다면 @ControllerAdvice 적용하면 되고, 응답으로 객체를 리턴해야 한다면 @RestControllerAdvice 적용하면 된다.

 

나는 응답으로 객체를 리턴할 것이므로 @RestControllerAdvice 를 썼다.

 

어노테이션 모두 적용 범위를 클래스나 패키지 단위로 제한할 수도 있으며, 아래와 같이 사용하면 된다.

참조 https://velog.io/@banjjoknim/RestControllerAdvice

 

@ExceptionHandler?

@ControllerAdvice에 대해서 이야기할 때 언급한 어노테이션이다.

이 어노테이션을 메서드에 선언하고 특정 예외 클래스를 지정해주면 해당 예외가 발생했을 때 메서드에 정의한 로직으로 처리할 수 있다. @ControllerAdvice 또는 @RestControllerAdvice에 정의된 메서드가 아닌 일반 컨트롤러 단에 존재하는 메서드에 선언할 경우, 해당 Controller에만 적용된다.

단, Controller, RestController에만 적용이 가능하다(@Service 등의 빈에서는 안된다)

 

 

예제

- GlobalExceptionHandler.java

@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
    @ExceptionHandler(Exception.class)
    public ResponseEntity systemException(Exception ex){
        log.error("시스템 에러",ex);
        return new ResponseEntity(AmexResponse.nok(AmexErrorCode.COMMON_UNKNOWN_ERROR),HttpStatus.INTERNAL_SERVER_ERROR);
    }
}

 

Exception 발생하면 에러 메시지를 콘솔에 출력할 것이다. Controller에서 Exception 은 예외처리를 따로 하지 않아도 된다.

 

 

참고 : 

https://velog.io/@juhyeon1114/Spring-%EC%BB%A4%EC%8A%A4%ED%85%80-Exception-%EB%A7%8C%EB%93%A4%EA%B8%B0-w.-Lombok

https://velog.io/@banjjoknim/RestControllerAdvice