쌓고 쌓다

서블릿 컨테이너의 예외 처리, 오류 페이지 본문

프로그래밍/spring

서블릿 컨테이너의 예외 처리, 오류 페이지

승민아 2023. 10. 12. 14:28

 

서블릿은 2가지 방식으로 예외 처리를한다.

  • Exception(예외)
  • response.sendError(HTTP 상태 코드, 오류 메시지)

 

WAS 구조는 다음과 같이 볼 수 있다.

출처: 인프런 - 김영한

디스패처 서블릿이 서블릿 컨테이너와 스프링 컨테이너의 연결 짓는다.

 

1. Exception

자바 직접 실행

자바 메인 메서드를 실행하여 main이라는 이름의 쓰레드를 실행한다.

실행중 예외를 잡지 못해 Main 메서드를 넘어서 예외가 나오면 예외 정보가 나오고 쓰레드는 종료된다.

 

웹 애플리케이션

웹 애플리케이션에서는 사용자 요청별로 별도의 쓰레드가 할당되고, 서블릿 컨테이너 안에서 동작한다.

애플리케이션에서 try catch로 예외를 잡지 못하고 서블릿 밖인 WAS까지 예외가 넘어오게 되며 예외 페이지를 보이게 된다.

 

예외 발생 흐름

WAS(여기까지 전파)  <- 필터 <- 서블릿 <- 인터셉터 <- 컨트롤러(예외발생)

 

Exception WAS까지 전파해보기.

@Controller
public class HomeController {
    @GetMapping("/error-ex")
    public void errorEx() {
        throw new RuntimeException("예외 발생");
    }
}

예외가 WAS까지 전달되어

Exception 예외의 경우 서버 내부에서 처리할 수 없다는 상태코드 500 에러로

tomcat 기본 에러 페이지를 볼 수 있다.

 

2. response.sendError(HTTP 상태 코드, 오류 메시지)

오류가 발생했을 때, HttpServletResponse가 제공하는 sendError 메서드를 사용할 수 있다.

바로 예외가 발생하는 것은 아니고, 서블릿 컨테이너에 오류가 발생했다는것을 전달할 수 있다.

 

예외 발생 흐름

WAS(sendError 호출 기록 확인) <- 필터 <- 서블릿 <- 인터셉터 <- 컨트롤러 (response.sendError())

 

@Controller
public class HomeController {

    @GetMapping("/error-404")
    public void error404(HttpServletResponse response) throws IOException {
        response.sendError(404, "404 오류.!");
    }

    @GetMapping("/error-500")
    public void error500(HttpServletResponse response) throws IOException {
        response.sendError(500);
    }
}

response.sendError를 사용하면 response에 오류가 발생했다는 상태를 저장한다.

서블릿 컨테이너는 클라이언트에게 응답하기전에 response에 sendError()가 호출되었는지 확인하고 

호출되었다면 해당하는 오류 코드에 맞춰 에러 페이지를 보여준다.

 

 

오류 페이지 적용

 

application.properties

server.error.whitelabel.enabled=false

스프링 부트가 제공하는 기본 예외 페이지를 끄자.

 

현재 스프링 부트를 통해 서블릿 컨테이너가 실행하기에 

스프링 부트를 통해 서블릿 오류 페이지를 등록한다.

 

서블릿 오류 페이지 등록

import org.springframework.boot.web.server.ConfigurableWebServerFactory;
import org.springframework.boot.web.server.ErrorPage;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;

@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);
    }
}
  • ErrorPage(상태코드, 경로) : 404 에러라면 errorPage404가 호출된다. 이때 해당 경로로 요청을 보내게 된다.
  • errorPageEx : RunctimeException 예외 발생시 호출.
  • 오류 페이지는 RuntimeException처럼 예외를 다룰때 해당 자식 타입 오류도 함께 처리한다.
    • RuntimeException의 자식 예외들까지 처리한다는 말이다.

 

이제 ErrorPage에서 작성한 경로로 오류 요청이 들어왔을 때 처리할 컨트롤러를 작성해야한다.

package com.example.board.controller;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

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

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

경로에 맞춰 GetMapping으로 들어온 요청에 대해 

"error-page" 폴더의 해당 View를 응답으로 보낸다.

 

 

예외 발생부터 오류 페이지까지 응답 과정은 다음과 같다.

1. WAS(여기까지 전파) <- 필터 <- 서블릿 <- 인터셉터 <- 컨트롤러(예외발생)
2. WAS `/error-page/500` 다시 요청 -> 필터 -> 서블릿 -> 인터셉터 -> 컨트롤러(/errorpage/500) -> View

예외가 발생시 WAS 까지 전파되고,

WAS는 오류 페이지 정보를 보고 오류 페이지를 다시 호출한다.

이때, 오류 페이지 경로에 대한 필터, 서블릿, 인터셉터, 컨트롤러가 모두 다시 호출된다.

 

서버 내부에서 오류 페이지를 찾기 위해 추가적인 호출을 하기에 웹 브라우저(클라이언트)는 어떤 일이 일어나는지 모른다.

Comments