[Spring] 예외 처리와 오류 페이지
Exception이 발생했을 때 애플리케이션에서 해당 에러를 잡지 못하고 서블릿 밖까지 전달된다면 어떻게 될까?
다음 과정을 통해 WAS(톰캣 등)까지 전달된다.
WAS(도착) <- 필터 <- 서블릿 <- 인터셉터 <- 컨트롤러(예외발생)
오류 발생 시 컨트롤러에서 HttpServletResponse.sendError()
로 서블릿 컨테이너에게 오류가 발생했다는 걸
알릴 수 있다. 메소드에 파라미터로 HTTP 상태 코드와 메시지를 전달할 수 있으며, 위에서의 Exception과 같은 과정
으로 WAS까지 전달된다.
HttpServletResponse.sendError(HTTP 상태 코드)
HttpServletResponse.sendError(HTTP 상태 코드, 오류 메시지)
둘 중 어떤 과정으로든 예외가 발생하면 서블릿은 그에 맞는 설정된 오류 페이지를 찾는다. 그러면 서버 내부에서
설정된 오류 페이지를 사용자에게 뷰로 띄워주기 위해 WAS에서 컨트롤러로 요청을 보내게 된다.
WAS(에러페이지 요청) -> 필터 -> 서블릿 -> 인터셉터 -> 컨트롤러(도착)
이런 과정을 거치면 필터 또는 인터셉터가 2번 호출될 수 있어 비효율적이다. 반드시 2번 거쳐야 하는 요청이 아니라면
꼭 2번 거칠 필요가 없으므로 사용자로부터 발생한 요청인지, 오류 페이지 출력을 위한 서버 내부 요청인지 구분할 수
있어야 한다. 이를 위해 서블릿은 DispatcherType
라는 정보를 제공한다.
DispatcherType
public enum DispatcherType {
FORWARD,
INCLUDE,
REQUEST,
ASYNC,
ERROR
}
REQUEST
: 클라이언트 요청ERROR
: 오류 요청FORWARD
: 서블릿이 다른 서블릿이나 JSP를 호출할 때INCLUDE
: 서블릿이 다른 서블릿이나 JSP의 결과를 포함할 때ASYNC
: 서블릿 비동기 호출
DispatcherType
는 5가지 값을 가지며 사용자 요청은 REQUEST
, 오류는 ERROR
이다. (이 2가지가 중요하다)
DispatcherType
에 어떤 값이 담겨있냐에 따라 필터, 인터셉터를 거칠지 여부를 결정하면 된다!
서블릿 필터는 WebMvcConfigurer
에 등록할 때 setDispatcherTypes()
에 DispatcherType
값을 설정해줄 수
있어서 오류 페이지 요청만 하고 싶다면 다음처럼 설정하면 된다.
filterRegistrationBean.setDispatcherTypes(DispatcherType.ERROR);
스프링 인터셉터는 excludePathPatterns()
에서 에러 페이지 경로를 넣어주면 에러 발생 시 인터셉터를 거치지
않도록 할 수 있다. (인터셉터는 서블릿이 아닌 스프링이 제공하는 기능이어서 DispatcherType
에 무관하게 항상
호출된다)
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LogInterceptor())
.order(1)
.addPathPatterns("/**")
.excludePathPatterns("/error-page/**"); //오류 페이지 경로 포함
}
}
스프링 부트에서 오류 페이지 처리
위처럼 오류 페이지를 등록하고 처리하는데 과정이 복잡하고 필요할 때마다 반복적인 과정이 필요하므로 스프링 부트
에서는 간단하게 오류 페이지를 처리할 수 있도록 하고있다. 결론만 말하면 정해진 경로(/error
)에 오류 페이지를
삽입해주면 끝난다.
스프링 부트에서는 오류 페이지 처리를 위해 자동 등록한 BasicErrorController
를 거쳐 /error
에 있는
오류 페이지를 가져오는데, 파일 이름에 따라 처리되는 우선순위가 다르다.
- 파일 이름이 정확할 수록 우선순위가 높음
500.html
>5xx.html
404.html
>4xx.html
- 뷰 템플릿(
/templates
) > 정적 리소스(/static
) >/templates/error.html
- 4xx는 400번대 오류를, 5xx는 500번대 오류를 처리
BasicErrorController
는 에러가 포함된 많은 정보를 model에 담아 뷰로 전달한다. 하지만 에러에 대한 정보를
사용자에게 노출하면 악용의 여지가 있어 위험하다. 기본값은 노출이 안되도록 설정돼 있으나 application.properties
에서 다음과 같이 수정할 수 있다.
server.error.include-exception=true
server.error.include-message=always
server.error.include-stacktrace=always
server.error.include-binding-errors=always
never
: 노출Xalways
: 항상 노출on_param
: 파라미터가 있을 때만 노출
실무에서는 사용자에게 오류 정보가 반드시 노출되지 않도록 하고 간단한 에러 페이지만 노출되도록 해야한다!
오류 정보는 서버에 로그로 남겨서 확인하자.
추가
- 스프링 whitelabel 오류 페이지 비활성화:
server.error.whitelabel.enabled=false
- 스프링 글로벌 오류 페이지 경로 설정:
server.error.path=/error
댓글남기기