
사이드 프로젝트를 수행하면서, FE 개발자분이 BE에 API 요청을 날릴 때, 에러가 발생하는 경우 각 도메인과 상황에 맞는 예외 처리를 적용하기 위해 공통 예외 처리를 어떻게 하는지 찾아보고 적용하게 되었다.
Spring의 예외 처리 방법
Spring은 만들어질 때부터 에러처리를 위한 BasicErrorController를 구현해두었고, 스프링 부트는 예외가 발생하면 기본적으로 /error로 에러 요청을 다시 전달하도록 WAS 설정이 되어있다.


각각 컨트롤러, 필터, 인터셉터가 두 번씩 호출되는 비효율적인 과정과 더불어서, 기본 에러는 클라이언트에게 status 500 "Internel Server Error"로만 응답하기 때문에, 클라이언트는 무슨 에러인지 구체적으로 파악할 수 없다.
이처럼 바람직하지 못한 에러 처리를 위해 별도의 예외처리 전략을 사용해야한다.
스프링이 제공하는 예외처리 방법
에러 처리를 메인 로직으로부터 분리
대부분의 HandlerExceptionResolver는 발생한 exception을 catch하고 HTTP 상태나 응답 메세지 등을 설정한다.
에러 응답을 위한 Controller나 ControllerAdvice에 있는 ExceptionHandler를 처리한다.
Spring에서 ExceptionResolver를 동작시켜 에러를 처리할 때 사용하는 어노테이션
전역으로는 사용이 불가하다.에러 응답을 JSON으로 내려준다는 점이다.CustomException 추상 클래스 생성


각 도메인별로 Exception class를 만들어 사용자가 예외를 정의하게 될텐데, 에러 응답을 통일하기 위해 추상 클래스를 만들고 하위에서 상속받아서 구현하도록 설계하였습니다.

에러 코드 객체를 생성할 때, 에러 메세지와 HTTPStatus를 반환하는 인터페이스를 설계했습니다.
각 도메인별로 인터페이스를 상속받아, 사용자가 에러코드를 커스텀하되, 필수 정보에 접근할 수 있게 구성했습니다.
예시)

마지막으로 에러 상황에서 공통 응답을 반환하는 class를 생성하겠습니다.
공통 GlobalResponse

일부만 발췌하여 캡쳐했습니다..
아까 설명한 @RestControllerAdvice에서 @ExceptionHandler를 통해 전역에 대한 예외 처리를 수행할 수 있도록 handler를 생성하겠습니다.
GlobalHandler

마찬가지로 일부만 발췌하였습니다.
@ExceptionHandler가 처리하는 대상을 보면 아까 최상위 계층으로 선언한 추상 클래스인 AbstractCustomException 클래스입니다.
즉, AbstractCustomException를 상속받은 하위 계층 전부에서 Exception이 발생했을 때, 이 메서드가 에러 처리를 위임받아 공통 응답 객체를 만들어 response를 보냅니다.

이를 통해 이전에는 500 에러와, server error만 내뱉었던 에러 정보를 다음처럼 커스터마이징해 클라이언트에게 구체적인 에러 정보를 안내할 수 있게 되었습니다.
Exception, ExceptionCode의 최상위 class를 설계하고 각 도메인에서 이를 상속받아 구현함으로써, 코드 전체의 일관성을 향상할 수 있었습니다. 또한, 서비스 계층에서 에러 메세지를 throw할 때마다, 에러 메세지를 만들어서 서비스의 변경에서 취약한 점을 극복할 수 있었습니다.
마지막으로, fe 개발자가 swagger를 통해 api를 요청할 때 어떤 문제점이 있는지 WAS 로그를 보지 않고도 전달할 수 있어 협업의 효율성을 극대화할 수 있었습니다.