Java에서 try-catch문을 직접 사용해 본 개발자라면, 예외 처리 경험이 썩 유쾌하지만은 않았을 것이다. 다행히 Spring은 기본적인 예외를 처리할 수 있도록 BasicErrorController를 제공한다.

위 그림처럼 컨트롤러에서 발생한 예외는 /error로 리다이렉트되어 BasicErrorController에서 일괄적으로 처리된다. 이때 클라이언트의 요청 헤더에 따라 응답 방식이 달라진다. 즉, 브라우저 요청이라면 HTML 에러 페이지가 내려가고, API 클라이언트 요청이라면 JSON 응답이 내려간다.
errorHtml(): htmlerror: json이때 Json으로 응답을 내려줄 때는 ErrorAttributes의 구현체인 DefaultErrorAttributes에서 정보를 추출해 내려주게 된다.
{
"timestamp": "2025-09-10T12:34:56.789+00:00",
"status": 400,
"error": "Bad Request",
"message": "Validation failed for object='userRequest'.",
"path": "/api/users"
}
그럼 Controller에서 터진 Error가 어떤 흐름으로 BasicErrorController로 갈까?

/error로 요청을 보낸다.이 흐름을 보면 Controller와 Servlet Container를 왕복하는 비효율적인 형태가 나타나게 된다.
위에서 언급한 HandlerExceptionResolver는 Dispatcher Servlet에서 Error를 캐치 시 Error 응답을 만드는 역할을 한다. Error를 캐치하게 되면 모든 HandlerExceptionResolver를 순서대로 탐색해서 적절한 것이 있으면, 이를 통해 처리해 응답을 내려주게 된다. 여기서 중요한 점은 처리가 되면 더 이상 Error가 전파되지 않아, BasicErrorController로 가지 않는다는 점이다.
HandlerExceptionResolver에는 기본적으로 구현체들이 존재한다.
@ExceptionHandler(처리할 예외 클래스) 에너테이션이 붙어 있는 메소드로 Error 처리.@ResponseStatus(HTTP 상태)의 HTTP 상태 코드로 Error 응답 생성.@ControllerAdvice는 위의 @ExceptionHandler를 매번 작성할 필요없이 전역적으로 사용할 수 있게 해주는 에너테이션이다. 이를 통해 컴포넌트를 새로 작성해 해당 컴포넌트에서 전역적으로 발생하는 Error들을 손쉽게 처리할 수 있게 해준다.
이를 구현하는 예시는 다음 포스팅에서 예제 코드와 함께 설명하겠다.