본 게시물은 스스로의 공부를 위한 글입니다.
틀린 내용이 있을 수 있습니다.
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.htmlresources/templates/error/5xx.htmlresources/static/error/400.htmlresources/static/error/404.htmlresources/static/error/4xx.htmlresources/templates/error.htmlresources/templates/error/4xx.htmlresources/templates/error/404.htmlresources/templates/error/500.html404오류 -> 404.html400 오류 -> 4xx.html500 오류 -> 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=/errorBasicErrorController 오류 컨트롤러 경로에 함께 사용된다.인프런의 '스프링 MVC 2편(김영한)'을 스스로 정리한 글입니다.
자세한 내용은 해당 강의를 참고해주세요.