스프링/4. 스프링 MVC-2

62. 오류 화면 커스텀

sdafdq 2023. 9. 10. 15:32

오류 화면이 예쁘지 않다면 사용자는 망한 서비스 라고 생각한다.

 

우리가 할 일은

1. 커스터마이징을 한다고 스프링에게 일러준다.

 - 에러페이지 추가, 호출할 컨트롤러

@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");

        factory.addErrorPages(errorPage404, errorPage500, errorPageEx);
    }
}

WebServerFactory.. 우리가 뭔가 서블릿의 기본 동작에 대해 커스터마이징 하려면 저걸 구현하여 

customize()를 오버라이드 하여 등록해 줘야 한다.

새 에러페이지를 생성하고, 그 에러페이지에는 상태코드와, 호출할 컨트롤러 url를 넣는다.

이 에러코드에는 저 컨트롤러를 호출한다는 뜻이다.

그 다음 factory에 에러페이지들을 모두 등록 시켜준다.

참고로 RuntimeException.class는 많은 예외처리의 상위의 객체이다. 런타임 예외는 모두..

 

2. 에러 페이지 컨트롤러 생성

@Slf4j
@Controller
public class ErrorPageController {
    @RequestMapping("/error-page/404")
    public String errorPage404(HttpServletRequest request, HttpServletResponse response){
        log.info("errorPage 404");
        return "error-page/404";
    }

    @RequestMapping("/error-page/500")
    public String errorPage500(HttpServletRequest request, HttpServletResponse response){
        log.info("errorPage 500");
        return "error-page/500";
    }
}

에러페이지를 위한 컨트롤러를 만들었다.

위에 에러페이지 객체를 생성할 때 url도 같이 넣었는데, 이것을 호출되게끔 하는 것이다.

 

3. 템플릿 만들기

에러페이지 컨트롤러의 템플릿 논리 이름에 맞춰서 에러페이지를 만들면 된다.

 

 

 

순서는 이렇다.

WAS <- 필터 <- 서블릿 <- 인터셉터 <- 컨트롤러(예외 발생)

이렇게 쭉 올라갔다가,

 

WAS -> 필터 -> 서블릿 -> 인터셉터 -> 컨트롤러

 

우리가 등록한 url의 컨트롤러를 호출하는 거다.

 

 

 

물론 이건 서버 내부에서 일어나는 일이다.

클라이언트는 모른다.

 

서버 입장에서는 컨트롤러를 두번 호출하는 것이지만,

클라이언트가 받는 것은 에러페이지 하나이다.

 

컨트롤러에서 예외가 생겨서 WAS까지 갔다가 WAS가 예외를 catch하면 다시 예외처리 컨트롤러를 호출시킨다.

그 후 그게 클라이언트에게 전달 되는 것 이다.

 

 

이 때, 그냥 컨트롤러만 호출하는 게 아니라,

request에 오류 정보를 담아서 호출하는거다.

 

그래서, WAS가 request에 담아준 오류 정보를 확인해 볼 수 있는데,

 

@Slf4j
@Controller
public class ErrorPageController {

    public static final String ERROR_EXCEPTION = "jakarta.servlet.error.exception";
    public static final String ERROR_EXCEPTION_TYPE = "jakarta.servlet.error.exception_type";
    public static final String ERROR_MESSAGE = "jakarta.servlet.error.message";
    public static final String ERROR_REQUEST_URI = "jakarta.servlet.error.request_uri";
    public static final String ERROR_SERVLET_NAME = "jakarta.servlet.error.servlet_name";
    public static final String ERROR_STATUS_CODE = "jakarta.servlet.error.status_code";

    @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";
    }


    private void printErrorInfo(HttpServletRequest request){
        log.info("ERROR_EXCEPTION {}", request.getAttribute(ERROR_EXCEPTION));
        log.info("ERROR_EXCEPTION_TYPE {}", request.getAttribute(ERROR_EXCEPTION_TYPE));
        log.info("ERROR_MESSAGE {}",request.getAttribute(ERROR_MESSAGE));
        log.info("ERROR_REQUEST_URI {}", request.getAttribute(ERROR_REQUEST_URI));
        log.info("ERROR_SERVLET_NAME {}", request.getAttribute(ERROR_SERVLET_NAME));
        log.info("ERROR_STATUS_CODE {}", request.getAttribute(ERROR_STATUS_CODE));
        log.info("dispatchType={}", request.getDispatcherType());
    }
}

각자 저 긴 이름으로 담아준다..

예외 발생 시.

 

 

 

그냥 response.sendError() 했을 시