about.Programing/Spring

Global Exception으로 컨트롤러에서 뱉내는 예외 일괄 처리하기

Logan. 2022. 7. 7. 19:58

자바에서는 Try - catch를 제공한다. 하지만 이 코드들이 여기저기 뿌려져 있다면 코드의 가독성은 정말 읽기 싫을 정도로 최악일 것이다. 이러한 것들을 위해 spring에서는 예외발생을 하나의 관심사로 보고 일괄처리할 수 있는 기능들을 제공한다. 

 

HandlerExceptionResolver인터페이스를 중심으로 4개의 구현체가 존재한다. 

 

  • DefaultErrorAttribute : 에러속성 정의를 위한 구현체이고 예외를 따로 처리하지 않는다.
  • DefaultHandlerExceptionResolver : 기본적인 스프링 예외들을 처리한다. 
  • ResponseStatusExceptionResolver : @ResponseStatus 또는 ResponseStatusException에 의한 예외를 처리한다.
  • ExceptionHandlerExceptionResolver : Controller나 ControllerAdvice에 있는 ExceptionHandler에 의한 예외를 처리한다.

HandlerExceptionResolverComposite가 DefaultErrorAttribute를 제외하고 나머지를 처리해준다. 

이제 예외를 처리할 수 있는 기능의 보기를 4가지 확인하고 직접 확인해볼 2가지만 자세히 보자.

  • @ResponseStatus
  • @ ExceptionHandler(*)
  • @ (Rest)ControllerAdvice (*)
  • @ ResponseStatusException

(*)이렇게 표시된 두가지만 보자.

#ExceptionHandler

ExceptionHandler의 경우는 두가지 상황에서 사용이 가능하다. 

  1. 어떠한 컨트롤러에 메서드로 지정할 경우
  2. @(Rest)ControllerAdvice를 사용할 경우

ExceptionHandler는 ResponseStatus와 달리 전달할 내부 내용 즉 바디를 자유롭게 설정 할 수 있다.

1번을 위한 코드를 보면 보면 하나의 컨트롤러에 연결된 로직에서 발생한 예외를 처리할 수 있다.

이제 Try-catch를 사용할 필요가 없다. 선언시 주의할게 있다. ExceptionHandler에 Value값으로 지정해 놓은 예외 클래스와 매개변수로 받는 클래스가 같아야한다. 만약 다르다면 런타입시 에러가 발생할 수 있다.

 

너무 편하게 사용 가능하지만 이럴 경우 다른 컨트롤러에서 같은 예외를 해결하는 ExceptionHandler를 이용한 에러처리 코드가 중복될 수 있다. 따라서 스프링은 @ControllerAdvice를 제공해준다. 이를 통해 전역 적으로 예외처리가 가능하다. 이 어노테이션을 통해 갖는 이점은 아래와 같다.

  • 하나의 클래스로 모든 컨트롤러에 대해 전역적으로 예외처리가 가능하다.
  • 직접 정의한 에러 응답을 일관성있게 클라이언트에게 내려줄 수 있다.
  • 별도의 try-catch문 없이 코드의 가독성이 높아진다. 

하지만 사용시에 다음 내용들을 주의해야한다. 

  • 프로젝트당 하나의 ControllerAdvice만 관리할 것.
  • 만약 모든 처리가 필요없다면 basePackages나 annotation을 지정해야한다. 
  • 직접 구현한 Exception(CustomException)은 한 공간에서 관리한다.

나는 상황에 따라 status와 메세지를 바꿔서 반환할 수 있는 CustomException 클래스를 만들고 객체 생성시마다 Enum에 작성 에러 메세지와 코드를 반환해주는 방식으로 구현했다.