Servlet 예외 처리 및 오류 처리 예제 튜토리얼

오늘은 Servlet 예외와 오류 처리에 대해 살펴보겠습니다. 얼마 전에 제가 자바에서 예외 처리에 대한 포스트를 썼는데, 웹 애플리케이션에서는 일반적인 자바 예외 처리보다 더 많은 처리가 필요합니다.

Servlet 예외

doGet()과 doPost() 메서드에서는 javax.servlet.ServletExceptionIOException을 throw합니다. 이 예외를 응용 프로그램에서 throw할 때 어떤 일이 발생하는지 살펴봅시다. 간단한 서블릿을 작성하여 ServletException을 throw하는 서블릿을 작성해보겠습니다.

package com.journaldev.servlet.exception;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/MyExceptionServlet")
public class MyExceptionServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		throw new ServletException("GET method is not supported.");
	}

}

이제 이 서블릿을 GET 방식으로 브라우저를 통해 호출하면 아래 이미지와 같은 응답을 받게 됩니다. 브라우저는 HTML만 이해하기 때문에 우리의 애플리케이션이 예외를 throw하면 서블릿 컨테이너가 예외를 처리하고 HTML 응답을 생성합니다. 이 로직은 서블릿 컨테이너에 특화되어 있습니다. 저는 톰캣을 사용하고 있고 이 에러 페이지를 받고 있습니다. 만약 JBoss나 Glassfish와 같은 다른 서버를 사용하면 다른 에러 HTML 응답을 받을 수 있습니다. 이 응답의 문제는 사용자에게 가치가 없다는 점입니다. 또한 사용자에게는 의미가 없고 보안적인 측면에서도 좋지 않은 응용 프로그램 클래스와 서버 세부 정보를 표시합니다.

서블릿 오류

I am sure you must have seen 404 error when you are trying to hit a URL that doesn’t exists. Let’s see how our servlet container responds to 404 error. If we send request for an invalid URL, we get response HTML like below image. Again it’s a generic HTML generated by server on our application behalf and hold little to no value to the user.

서블릿 예외 및 오류 처리

서블릿 API는 배포 기술자(deployment descriptor)에서 구성할 수 있는 사용자 정의 예외 및 오류 처리 서블릿을 지원합니다. 이러한 서블릿의 전체 목적은 응용 프로그램에서 발생한 예외나 오류를 처리하고 유용한 HTML 응답을 사용자에게 보내는 것입니다. 우리는 응용 프로그램 홈페이지로의 링크를 제공하거나 사용자가 어떤 문제가 발생했는지 알 수 있도록 일부 세부 정보를 제공할 수 있습니다. 그러므로 우선적으로 사용자 정의 예외 및 오류 처리 서블릿을 생성해야 합니다. 응용 프로그램에는 여러 예외 및 오류 처리 서블릿이 있을 수 있지만 간단하게 하기 위해 단일 서블릿을 생성하고 예외와 오류 모두에 사용할 것입니다. AppExceptionHandler.java

package com.journaldev.servlet.exception;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/AppExceptionHandler")
public class AppExceptionHandler extends HttpServlet {
	private static final long serialVersionUID = 1L;

	protected void doGet(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		processError(request, response);
	}

	protected void doPost(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		processError(request, response);
	}

	private void processError(HttpServletRequest request,
			HttpServletResponse response) throws IOException {
		// 서블릿 예외 분석
		Throwable throwable = (Throwable) request
				.getAttribute("javax.servlet.error.exception");
		Integer statusCode = (Integer) request
				.getAttribute("javax.servlet.error.status_code");
		String servletName = (String) request
				.getAttribute("javax.servlet.error.servlet_name");
		if (servletName == null) {
			servletName = "Unknown";
		}
		String requestUri = (String) request
				.getAttribute("javax.servlet.error.request_uri");
		if (requestUri == null) {
			requestUri = "Unknown";
		}
		
		// 응답 콘텐츠 유형 설정
	      response.setContentType("text/html");
	 
	      PrintWriter out = response.getWriter();
	      out.write("Exception/Error Details");
	      if(statusCode != 500){
	    	  out.write("

Error Details

"); out.write("Status Code:"+statusCode+"
"); out.write("Requested URI:"+requestUri); }else{ out.write("

Exception Details

"); out.write("
  • Servlet Name:"+servletName+"
  • "); out.write("
  • Exception Name:"+throwable.getClass().getName()+"
  • "); out.write("
  • Requested URI:"+requestUri+"
  • "); out.write("
  • Exception Message:"+throwable.getMessage()+"
  • "); out.write("
"); } out.write("

"); out.write("Home Page"); out.write(""); } }

배포 기술자에서 이를 구성하는 방법을 살펴보고 구현 및 작동 방식을 이해해 보겠습니다.

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xmlns="https://java.sun.com/xml/ns/javaee" xsi:schemaLocation="https://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">
  <display-name>ServletExceptionHandling</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
  </welcome-file-list>
  
  <error-page>
  	<error-code>404</error-code>
  	<location>/AppExceptionHandler</location>
  </error-page>
  
  <error-page>
  <exception-type>javax.servlet.ServletException</exception-type>
  <location>/AppExceptionHandler</location>
  </error-page>
</web-app>

위에서 볼 수 있듯이, error-page 요소를 사용하여 애플리케이션에 예외 처리기 서블릿을 지정하는 것은 매우 쉽습니다. 각 error-page 요소는 error-code 또는 exception-type 요소 중 하나를 가져야 합니다. 우리는 예외 처리기 서블릿을 location 요소에서 정의합니다. 위의 구성에 따르면, 애플리케이션이 404 오류 또는 ServletException을 발생시키면 AppExceptionHandler 서블릿이 처리합니다. 이러한 예외 및 오류 시나리오가 발생하면 서블릿 컨테이너가 예외 처리기 서블릿의 해당 HTTP 메서드를 호출하고 요청 및 응답 객체를 전달합니다. 예외를 처리하기 위해 서블릿 컨테이너가 서블릿을 호출하기 전에 요청에 일부 속성을 설정하여 예외에 관한 유용한 정보를 가져옵니다. 그 중 일부는 javax.servlet.error.exception, javax.servlet.error.status_code, javax.servlet.error.servlet_name, javax.servlet.error.request_uri 등입니다. 예외의 경우, 상태 코드는 항상 “내부 서버 오류”에 해당하는 500입니다. 기타 유형의 오류의 경우 404, 403 등과 같은 다른 오류 코드를 얻습니다. 상태 코드를 사용하여 우리의 구현은 사용자에게 다른 유형의 HTML 응답을 제공합니다. 또한 애플리케이션의 홈 페이지로의 하이퍼링크를 제공합니다. 이제 ServletException을 throw하는 서블릿에 접근하면 아래 이미지와 같은 응답을 받게 됩니다. 404 응답을 초래하는 잘못된 URL에 액세스하려고 하면 아래 이미지와 같은 응답을 받게 됩니다. 이것은 보기 좋으며 사용자가 발생한 상황을 쉽게 이해하고 올바른 위치로 이동할 수 있는 방법을 제공합니다. 또한 사용자에게 애플리케이션에 대한 민감한 정보를 보내지 않습니다. 웹 애플리케이션에 항상 예외 처리기를 설정해야 합니다. 런타임 예외 및 기타 모든 예외를 단일 예외 처리기에서 처리하려면 exception-type으로 Throwable을 제공할 수 있습니다.

<error-page>
  <exception-type>java.lang.Throwable</exception-type>
  <location>/AppExceptionHandler</location>
</error-page>

만약 여러 오류 페이지 항목이 있는 경우, Throwable 및 IOException에 대한 하나씩 있다고 가정하고 애플리케이션이 FileNotFoundException을 throw하면 IOException의 오류 핸들러에서 처리될 것입니다. 또한 예외 핸들러로 JSP 페이지를 사용할 수 있으며 서블릿 매핑 대신 jsp 파일의 위치를 제공하면 됩니다. 이것으로 웹 애플리케이션에서의 서블릿 예외 처리에 대한 설명이 끝났습니다. 마음에 드셨으면 좋겠습니다.

Servlet 예외 처리 예제 프로젝트 다운로드

이 시리즈의 다른 문서도 확인하세요:

  1. Java 웹 애플리케이션
  2. Java Servlet 튜토리얼
  3. Java에서의 세션 관리
  4. Servlet 필터
  5. Servlet 리스너
  6. 서블릿에서의 쿠키
  7. 서블릿 파일 업로드 및 다운로드 예제

Source:
https://www.digitalocean.com/community/tutorials/servlet-exception-and-error-handling-example-tutorial