스프링에서 예외를 잘! 처리하는 방법
Intro
자바 프로그램에서 예외가 발생하면 스레드가 종료됩니다. 하지만 스프링에서는 예외가 발생해도 어플리케이션이 종료되지 않고, 예외 응답을 반환합니다. 스프링은 어떻게 예외를 처리하기에 이렇게 작동하는 것일까요?🤔 이번 글에서는 스프링에서 어떻게 예외를 처리하는지와, 어떻게해야 예외를 잘~ 처리할 수 있는지 알아보겠습니다.
🔍 스프링의 기본 예외 처리 방식
어떠한 예외처리도 하지 않았을 때, 스프링에서 어 떤식으로 예외가 처리될까요?
우선 스프링의 기본 작동 방식에 대해 생각해봅시다.
스프링에 요청이 오면, WAS - Dispatcher Servlet - HandlerMapping - HandlerAdapter - Controller
를 거쳐 로직이 실행됩니다.
이때 예외가 발생하면 예외 내용은 WAS까지 거슬러 올라갑니다.
그럼 WAS는 어플리케이션에서 처리할 수 없는 예외라 판단하여 에러 컨트롤러로 예외 내용을 전달합니다.
즉, 예외가 발생하면 WAS - Controller - WAS - ErrorController
의 흐름을 갖게 됩니다.
이 과정을 통해 예외가 발생해도 어플리케이션을 종료하지 않고, 마치 정상 요청인 것처럼 예외 응답을 반환하는 것입니다.
BasicErrorController
이때 호출되는 에러 컨트롤러가 바로 BasicErrorController
입니다.
실제로 어떠한 예외처리도 하지 않고 BasicErrorController에 BreakPoint 를 걸어두고 디버깅하니,
BasicErrorController에서 예외가 핸들링되는 것을 확인할 수 있었습니다.
BasicErrorController의 응답
BasicErrorController는 DefaultErrorAttributes
의 getErrorAttributes()
함수를 호출해서 응답할 내용을 불러옵니다.
이때 getErrorAttributes()가 기본적으로 제공하는 속성과, 설정을 통해 추가할 수 있는 속성들은 다음과 같습니다.
- timestamp: 에러가 발생한 시간
- status: 에러의 Http 상태
- error: 에러 코드
- path: 에러가 발생한 uri
- exception: 최상위 예외 클래스의 이름(설정 필요)
- message: 에러에 대한 내용(설정 필요)
- errors: BindingExecption에 의해 생긴 에러 목록(설정 필요)
- trace: 에러 스택 트레이스(설정 필요)
// 어떤 설정도 하지 않았을 때의 응답
{
"timestamp": "2024-07-21T16:16:40.463+00:00",
"status": 500,
"error": "Internal Server Error",
"path": "/reviews/999"
}
// 추가할 수 있는 속성들
server:
error:
include-message: always
include-binding-errors: always
include-stacktrace: always
include-exception: true
// 모든 속성을 추가했을 때의 응답
{
"timestamp": "2024-07-21T16:19:58.729+00:00",
"status": 500,
"error": "Internal Server Error",
"exception": "org.springframework.orm.jpa.JpaObjectRetrievalFailureException",
"trace": "org.springframework.orm.jpa.JpaObjectRetrievalFailureException\n\tat ….",
"message": "No message available",
"path": "/reviews/999"
}
BasicErrorController를 통한 예외 처리의 한계
혹시 '이것만으로도 충분히 훌륭한데?' 라는 생각이 드시나요? 하지만 이 방식에는 몇가지 한계가 존재합니다. 첫째로, 이 방식은 WAS에서 컨트롤러를 거쳐, 다시 WAS로 왔다가, 에러 컨트롤러로 가는 흐름인데요, 이 과정이 길고 복잡하게 느껴집니다. 또 이는 필터나 인터셉터를 2번 호출하는 등 다른 문제를 야기할 수 있습니다. 그리고 결정적으로, 예외에 따라 다른 HttpStatusCode와 메세지를 줄 수도 없습니다😓