본 게시물은 스스로의 공부를 위한 글입니다.
틀린 내용이 있을 수 있습니다.
Exception
(예외)response.sendError(HTTP 상태코드, 오류 메시지)
WAS(여기까지 전파) <- 필터 <- 서블릿 <- 인터셉터 <- 컨트롤러(예외발생)
Exception
의 경우 서버 내부에서 처리할 수 없는 오류 발생으로 생각해서 상태코드 500을 반환해준다.cf) 없는 페이지를 호출하면 HTTP 상태코드로 404를 반환해준다.
response.sendError
을 보낸다고 호출한다고 당장 예외가 발생하는건 아니다.
WAS까지 가서야 에러구나!
를 알 수 있다.
이 메서드를 사용하면 HTTP 상태 코드와 오류 메시지도 추가할 수 있다.
response.sendError(HTTP 상태 코드)
response.sendError(HTTP 상태 코드, 오류 메시지)
@GetMapping("/error-404")
public void error404(HttpServletResponse response) throws IOException {
response.sendError(404, "404 오류!");
}
sendError
가 있는것을 보고 어? 에러를 보내줬구나
를 알고 처리해준다.WAS(sendError 호출 기록 확인) <- 필터 <- 서블릿 <- 인터셉터 <- 컨트롤러 (response.sendError())
Exception
이든 response.sendError
든 WAS까지 오류가 전달되면, WAS에서는 오류 페이지를 띄우기 위해 오류 페이지 요청
을 하게 된다.
오류 화면을 제공하기 위한 html 파일 설정은 web.xml
또는 스프링부트에서 제공하는 기능을 사용해서 설정할 수 있다.
<web-app>
<error-page>
<error-code>404</error-code>
<location>/error-page/404.html</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/error-page/500.html</location>
</error-page>
<error-page>
<exception-type>java.lang.RuntimeException</exception-type>
<location>/error-page/500.html</location>
</error-page>
</web-app>
@Component
public class WebServerCustomizer implements WebServerFactoryCustomizer<ConfigurableWebServerFactory> {
@Override
public void customize(ConfigurableWebServerFactory factory) {
ErrorPage errorPage404 = new ErrorPage(HttpStatus.NOT_FOUND, "/errorpage/404");
ErrorPage errorPage500 = new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/error-page/500");
ErrorPage errorPageEx = new ErrorPage(RuntimeException.class, "/errorpage/500");
factory.addErrorPages(errorPage404, errorPage500, errorPageEx);
}
}
WAS
/error-page/500
다시 요청 -> 필터 -> 서블릿 -> 인터셉터 -> 컨트롤러(/error-page/500)-> View
오류페이지를 보여줄때도 필터와 인터셉터를 실행하지 않게 하는 방법이 있을까? 혹은 오류 페이지를 띄울때 특별히 호출되게 하고 싶은 필터나 인터셉터가 있을까?
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Bean public FilterRegistrationBean logFilter() {
FilterRegistrationBean<Filter> filterRegistrationBean = new
FilterRegistrationBean<>();
filterRegistrationBean.setFilter(new LogFilter());
filterRegistrationBean.setOrder(1);
filterRegistrationBean.addUrlPatterns("/*");
filterRegistrationBean.setDispatcherTypes(DispatcherType.REQUEST,
DispatcherType.ERROR);
return filterRegistrationBean;
}
위 코드에서 filterRegistrationBean.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ERROR);
을 집중하자
평상시 정상적인 호출에서는 DispatcherType
이 REQUEST
로 넘어온다.
하지만 WAS에서 오류가 발생하고 오류 페이지를 띄우기 위한 호출에서는 DispatcherType
이ERROR
로 넘어온다.
이를 이용해 오류 페이지 요청 전용 필터를 적용할 수 있는데, DispatcherType.ERROR
만 지정하면 된다.
디폴트값으론 DispatcherType.REQUEST
만 선언되어 있다.
DispatcherType
과 무관하게 항상 호출된다.excludePathPatterns
를 사용해서 빼주면 된다.@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LogInterceptor())
.order(1)
.addPathPatterns("/**")
.excludePathPatterns(
"/css/**", "/*.ico"
, "/error", "/error-page/**" //오류 페이지 경로
);
}
}
/hello
에서 예외가 터진다.1. WAS(/hello, dispatchType=REQUEST) -> 필터 -> 서블릿 -> 인터셉터 -> 컨트롤러
2. WAS(여기까지 전파) <- 필터 <- 서블릿 <- 인터셉터 <- 컨트롤러(예외발생)
3. WAS 오류 페이지 확인
4. WAS(/error-page/500, dispatchType=ERROR) -> 서블릿 -> 컨트롤러(/error-page/500) -> View
스프링 부트는 오류 페이지 기본 경로를 /error
로 설정해준다.
즉, 서블릿 밖으로 예외가 발생하거나, response.sendError(...)
가 호출되면 모든 오류는 /error
를 호출하게 된다.
스프링 부트는BasicErrorController
라는 스프링 컨트롤러를 자동으로 등록해주는데, 이 컨트롤러는 /error
를 매핑해서 어느 페이지를 보여줄지 처리하는 컨트롤러다.
BasicErrorController
에 기본적인 로직이 다 개발되어 있으므로, 개발자는 오류 페이지를 만든 후 뷰 템플릿 경로에 넣어두기만 하면 된다.
resources/templates/error/500.html
resources/templates/error/5xx.html
resources/static/error/400.html
resources/static/error/404.html
resources/static/error/4xx.html
resources/templates/error.html
resources/templates/error/4xx.html
resources/templates/error/404.html
resources/templates/error/500.html
404
오류 -> 404.html
400
오류 -> 4xx.html
500
오류 -> 500.html
예외 발생
오류 -> 500.html
(예외는 500으로 처리한다.)BasicErrorController
는 뷰를 호출할 때 model
에 다음 정보를 담아서 전달해준다. 뷰 템플릿이 이 값을 활용해 출력할 수 있다.* timestamp: Fri Feb 05 00:00:00 KST 2021
* status: 400
* error: Bad Request
* exception: org.springframework.validation.BindException
* trace: 예외 trace
* message: Validation failed for object='data'. Error count: 1
* errors: Errors(BindingResult)
* path: 클라이언트 요청 경로 (`/hello`)
but.. 오류 관련 내부 정보들을 고객에게 노출하는 것은 좋지 않다. 고객이 해당 정보 를 읽어도 혼란만 더해지고, 보안상 문제가 될 수도 있다.
그래서 BasicErrorController 오류 컨트롤러에서 다음 오류 정보를 model 에 포함할지 여부 선택할 수 있다.
application.properties
에다가
server.error.include-exception=true #exception 포함 여부(t, f)
server.error.include-message=on_param #message 포함 여부
server.error.include-stacktrace=on_param #trace 포함 여부
server.error.include-binding-errors=on_param #errors 포함 여부
3가지 옵션을 사용할 수 있다. (기본 값은 never
이다.)
never
: 사용하지 않음always
:항상 사용on_param
: 파라미터가 있을 때 사용실무에서는 이것들을 노출하면 안된다.
고객에게는 이쁜 오류 화면을, 오류는 서버에 로그로 남겨야 된다.
cf) 스프링 부트 오류 관련 옵션
server.error.whitelabel.enabled=true
server.error.path=/error
BasicErrorController
오류 컨트롤러 경로에 함께 사용된다.인프런의 '스프링 MVC 2편(김영한)'을 스스로 정리한 글입니다.
자세한 내용은 해당 강의를 참고해주세요.