(1) 서블릿 예외처리

CJY·2023년 4월 10일
0

스프링

목록 보기
13/14

서블릿의 예외 처리 2가지 방식

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

서버 오류의 흐름을 파악하고 어떤 에러 응답 코드인지 확인하기 위해 아래 application.properties에 다음 내용을 추가하자.

application.properties에 추가
server.error.whitelabel.enabled=false

Exception

자바는 메인 메서드를 실행하면 main이라는 이름으로 쓰레드가 실행된다. 이 메서드 안에서 예외를 잡지 못하고 넘어가면 해당 쓰레드는 종료하고 예외 정보를 남긴다.

우리는 웹 어플리케이션에 대해 더 자세히 알아보자.

WAS \larr 필터 \larr 서블릿 \larr 인터셉터 \larr 컨트롤러

컨트롤러에서 예외가 발생했지만 중간의 경로에서 예외처리를 하지않는다면 결국 WAS까지 예외가 전달된다. WAS는 서버 개발에서 예외를 처리하지 못했기에 500상태 코드로 판단하고 톰캣이 기본으로 제공하는 오류 화면을 보여준다.

response.sendError()

response.sendError()를 호출시 서블릿 컨테이너는 고객에게 응답하기 전에 responsesendError()를 확인하고 거기에 맞는 오류 페이지를 응답한다.

WAS(sendError 기록 확인) \larr 필터 \larr 서블릿 \larr 인터셉터 \larr 컨트롤러 (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, "/errorpage/500");
 		factory.addErrorPages(errorPage404, errorPage500, errorPageEx);
 		}
}

지정한 오류 종류에 따라 URL에 이동하도록 설정한 것이다. 이제 이를 토대로 컨트롤러와 뷰 템플릿을 작성하자.

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

뷰 템플릿은 생략하겠다.

흐름

다시 오류 페이지 처리에 대한 흐름에 대해 살펴보자. 아까 위에서 봤듯이 오류가 WAS까지 넘어가는 것을 확인해봤다. 근데 이번엔 new ErrorPage를 팩토리에 지정했다. 따라서 WAS가 받은 예외를 처리할 수 있는지 확인하는 과정에서 URL정보를 얻는다.

곧바로 클라이언트에게 응답하는 것이 아니라 해당 URL에 대한 매핑을 통해 컨트롤러까지 다시 들어간다. 이제 우리가 설정한 뷰 템플릿 'error-page/*'를 클라이언트에게 넘겨주게된다.

오류 정보

WAS가 오류 페이지를 찾을 때 request에 오류에 대한 정보를 담는다. 따라서 request.getAttribute()를 통해 오류에 대한 정보를 알 수 있다.

파라미터로 들어갈 수 있는 오류정보는 다음과 같다.

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 상태 코드

필터

이전 시간에 로그인 인증 처리를 위해 필터와 인터셉터 코드를 작성했다. 근데 만약 오류를 통해 WAS까지 넘어간 이후에 오류 페이지를 찾기 위해 컨트롤러로 다시 넘어오는 과정에 필터와 인터셉터를 한번 더 거치게 된다. 이 상황은 매우 비효율적이므로 서블릿은 DispatcherType이라는 추가 정보를 제공한다.

request.getDispatcherType()

오류 정보와 함께 현재 디스페처 타입에 대한 정보도 확인할 수 있다. 디스패처 타입의 종류는 아래와 같다.
javax.servlet.DispatcherType

public enum DispatcherType {
 	FORWARD,
 	INCLUDE,
 	REQUEST,
 	ASYNC,
 	ERROR
}

DispatcherType

  • REQUEST : 클라이언트 요청
  • ERROR : 오류 요청
  • FORWARD : MVC에서 배웠던 서블릿에서 다른 서블릿이나 JSP를 호출할 때
    RequestDispatcher.forward(request, response);
  • INCLUDE : 서블릿에서 다른 서블릿이나 JSP의 결과를 포함할 때
    RequestDispatcher.include(request, response);
  • ASYNC : 서블릿 비동기 호출

기본적으로 dispatcher type이 request인 경우에만 필터가 호출된다. 만약 다른 타입에서도 필터를 호출하고싶다면 FilterRegistrationBeansetDispatcherTypes() 함수를 통해 설정해야한다.

인터셉터

방금 필터처럼 인터셉터도 구분이 되면 좋겠지만 디스패처 타입은 서블릿이 제공하는 기능이고 인터셉터는 스프링이 제공하는 기능이므로 다른 방법을 사용해야 한다.

이전에 필터에 비해 인터셉터의 강력한 기능인 excludePathPatterns를 사용해서 오류 페이지 경로를 제거하면 된다.

정리

스프링 부트가 제공하는 오류 이전에 서블릿의 오류처리에 대해 알아봤다. 전체적인 오류의 흐름에 대한 깊은 이해를 할 수 있는 시간이었고 필터와 인터셉터와도 연관지어 학습해봤다. 다음 시간에는 스프링 부트가 예외처리를 어떻게 도와주는지 확인해볼 예정이다.

profile
열심히 성장 중인 백엔드

0개의 댓글