스프링이 아닌 순수 서블릿 컨테이너의 예외 처리 방식
(예외가 있다고 알려주는 두 가지 방식)
자바 직접 실행
웹 애플리케이션
사용자 요청별로 별도의 쓰레드가 할당, 서블릿 컨테이너 안에서 실행
만약 애플리케이션에서 예외를 잡지 못해 서블릿 밖으로 예외가 전달된다면?
WAS(여기까지 전파) <- 필터 <- 서블릿 <- 인터셉터 <- 컨트롤러(예외발생)
톰캣 같은 WAS 까지 예외가 전달
서버 내부에서 처리할 수 없는 오류가 발생한 것으로 생각하여 오류 화면을 띄움
톰캣 기본 오류 화면

response.sendError(HTTP 상태 코드)
response.sendError(HTTP 상태 코드, 오류 메시지)
sendError 흐름
WAS(sendError 호출 기록 확인) <- 필터 <- 서블릿 <- 인터셉터 <- 컨트롤러
(response.sendError())

서블릿이 제공하는 기능을 사용하여 오류 페이지를 변경할 수 있다
@Component
public class WebServerCustomizer implements WebServerFactoryCustomizer<ConfigurableWebServerFactory> {
@Override
public void customize(ConfigurableWebServerFactory factory) {
ErrorPage errorPage404 = new ErrorPage(HttpStatus.NOT_FOUND, "/error-page/404");
ErrorPage errorPage500 = new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/error-page/500");
ErrorPage errorPageEx = new ErrorPage(RuntimeException.class, "/error-page/500"); //자식 타입 예외도 errorPageEx 호출
factory.addErrorPages(errorPage404, errorPage500, errorPageEx);
}
}
response.sendError(404) : errorPage404 호출
response.sendError(500) : errorPage500 호출
RuntimeException 또는 그 자식 타입의 예외: errorPageEx 호출
처리할 컨트롤러도(오류 화면을 보여줄) 필요
@Slf4j
@Controller
public class ErrorPageController {
@RequestMapping("/error-page/404")
public String errorPage404(HttpServletRequest request, HttpServletResponse response) {
log.info("errorPage 404");
printErrorInfo(request);
return "error-page/404";
}
@RequestMapping("/error-page/500")
public String errorPage500(HttpServletRequest request, HttpServletResponse response) {
log.info("errorPage 500");
printErrorInfo(request);
return "error-page/500";
}
}
1. WAS(여기까지 전파) <- 필터 <- 서블릿 <- 인터셉터 <- 컨트롤러(예외발생)
2. WAS `/error-page/500` 다시 요청 -> 필터 -> 서블릿 -> 인터셉터 -> 컨트롤러(/error-page/500) -> View
또한 WAS는 오류 정보를 request의 attribute에 추가하여 넘겨준다
javax.servlet.error.exception : 예외
javax.servlet.error.exception_type : 예외 타입
javax.servlet.error.message : 오류 메시지
javax.servlet.error.request_uri : 클라이언트 요청 URI
javax.servlet.error.servlet_name : 오류가 발생한 서블릿 이름
javax.servlet.error.status_code : HTTP 상태 코드
🔎오류 발생 -> 오류 페이지를 출력하기 위해 필터, 서블릿, 인터셉터가 다시 호출 되는데 이는 비효율적
클라이언트로부터 발생한 정상 요청인지, 오류 페이지를 출력하기 위한 내부 요청인지 구분하기 위해 서블릿은 DispatcherType 이라는 추가 정보를 제공한다
log.info("dispatchType={}", request.getDispatcherType())
로그를 출력하는 부분에 추가
request.getDispatcherType()
필터 등록
filterRegistrationBean.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ERROR);
//두 가지의 경우에 필터 호출
오류 페이지 경로를 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/**");
}
}
스프링은 앞의 과정들을 모두 기본으로 제공한다
따라서 오류 페이지만 등록하면 된다
ErrorPage 자동으로 등록하고 /error 라는 경로로 기본 오류 페이지 설정
BasicErrorController 라는 스프링 컨트롤러를 자동으로 등록
🔎 ErrorMvcAutoConfiguration 이라는 클래스가 오류 페이지를 자동으로 등록하는 역할을 함
BasicErrorController 의 처리 순서
뷰 템플릿
resources/templates/error/500.html
resources/templates/error/5xx.html
정적 리소스( static , public )
resources/static/error/400.html
resources/static/error/404.html
resources/static/error/4xx.html
적용 대상이 없을 때 뷰 이름( error )
resources/templates/error.html
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, 내부 정보들을 노출하는 것은 좋지 않음
다음 오류 정보들을 model 에 포함할지 여부를 선택 가능
application.properties
server.error.include-exception=false : exception 포함 여부( true , false )
server.error.include-message=never : message 포함 여부
server.error.include-stacktrace=never : trace 포함 여부
server.error.include-binding-errors=never : errors 포함 여부
기본 값이 never 인 부분은 3가지 옵션을 사용 가능
never : 사용하지 않음
always : 항상 사용
on_param : 파라미터가 있을 때 사용
on_param은 파라미터가 있으면 해당 정보를 노출. 디버그 시 문제를 확인하기 위해 사용할 수 있지만 운영 서버에서는 권장하지 않음
on_param 으로 설정 후, 다음과 같이 HTTP 요청시 파라미터를 전달하면 해당 정보들이 model 에 담겨서 뷰 템플릿에서 출력된다
message=&errors=&trace=
server.error.whitelabel.enabled=true : 스프링 부트 제공 기본 오류 페이지
server.error.path=/error : 오류 페이지 경로, 스프링이 자동 등록하는 서블릿 글로벌 오류 페이지 경로와 BasicErrorController 오류 컨트롤러 경로에 함께 사용됨
에러 공통 처리 컨트롤러의 기능을 변경하고 싶으면