[Spring] 예외 처리 - @ControllerAdvice, @ExceptionHandler로 처리하기

Jae Eon·2021년 11월 14일
1

백엔드 공부

목록 보기
14/17

실무에서 에러를 핸들링 하기 위해 사용된 @ControllerAdvice를 이해하기 위해 알아보고 작성된 포스트

🍎 예외처리를 위한 어노테이션이 필요한 이유

예외처리는 서버를 안정적으로 운영하기위해 필수적으로 해야하는 일이다.
예외 처리를 하는 방법은 아래와 같은 방법 이외에도 다양한데

  • 메서드 내에서 예외 상황을 예측해서 처리하는 try-catch문을 이용하는 방법
  • 입력 요구사항에 대한 예외 처리 (ex. @valid, @Validated 사용 > 특정 값의 범위, 자료형이 아니면 유효하지 않은 값으로 판단하고 예외 처리)
  • 스프링 시큐리티에서 인터셉터로 잡아서 UnauthorizedException 같은 예외 처리

기타 여러 예외 처리들을
if문으로 잡든 try-catch로 잡든 상위 메서드로 예외처리를 위임하든 코드는 복잡해지고 유지보수하기 어려워진다.
따라서 비즈니스 로직에 집중하기 어렵고, 비즈니스 로직과 관련된 코드보다 예외 처리를 위한 코드가 더 많아지는 경우도 생긴다.
이런 경우를 개선하기 위해 @ExceptionHandler와 @ControllerAdvice를 사용한다.

단, 두 어노테이션 모두 @Controller, @RestController에만 적용가능하다. (@Service같은 빈에서는 안됨.)

🍊 @ExceptionHandler

  • @Controller, @RestController가 적용된 Bean에서 동작하며, 메서드 단위로 동작한다.
  • 리턴 타입과 관계없이 동작한다.
  • 다른 클래스에 @Controller, @RestController가 적용되어 있어도 @ExceptionHandler가 없다면 동작하지 않는다.
  • 파라미터로 받는 Excetion만 처리 가능하다.
    (ex. IllegalArgumentException을 받으면 NullPointerException은 처리 못한다)
    @ExceptionHandler 처리 코드 참고

ControllerAdvice 사용시 고려해야 할 것

에러 인터페이스를 명확히 정의하는 것이 중요하다.
같은 유형의 모든 에러를 핸들링 하기 위해 사용하는 것 이기 때문에 클라이언트(프론트)측과 서버측 모두 명확히 이해할 수 있는 에러를 만들고 커뮤니케이션 하는 것이 중요하다(Ex. HTTP 상태코드 : 200이면 통과)

🍓 @ControllerAdvice

  • ControllerAdvice는 ExceptionHandler와 달리 별도의 속성을 지정해 주지 않으면 @Controller가 적용된 모든 @Bean 클래스 단위로 동작한다
    (별도 옵션을 통해 패키지 단위로 제한도 가능)

사용법 : 별도의 @ControllerAdvice를 적용한 에러 핸들링만을 위한 클래스를 하나 생성 후 에러 핸들링 메서드를 나열한다.

@ControllerAdvice
class ControllerAdvisor {
    @ExceptionHandler(NotFoundException::class)
    fun handleException() = "Not Found 에러 발생"
    
     @ExceptionHandler(CustomException::class)
    fun handleException() = "Custom 에러 발생"
    }

🍋 @RestControllerAdvice

RestControllerAdvice는 ControllerAdvice에 @ResponseBody가 추가된 어노테이션으로
API 응답을 리턴 할 수 있게해준다.

이때, 아래의 exception: NotFoundException 같은 파라미터는 직접 구현 해 줘야한다.

//-- 사용자 정의한 에러 클래스 --
@ResponseStatus(HttpStatus.NOT_FOUND)
class NotFoundException : RuntimeException {
    constructor(): super()
    constructor(message: String): super(message)
}
// -- 기존에서 리스폰스 추가 --
@RestControllerAdvice
class ControllerAdvisor {
    @ResponseStatus(HttpStatus.NOT_FOUND)
    @ExceptionHandler(NotFoundException::class)
    fun handleException(exception: NotFoundException) = response(exception)
    
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(CustomException::class)
    fun handleException(exception: CustomException) = response(exception)
    }
profile
🖋정리를 안하면 잊어버린다.👣한 발자국씩 가보자!

0개의 댓글