본문 바로가기

Dev/Spring

[Spring] Spring Boot 시작하기 (6) - 에러 페이지 처리

 

포스팅 시리즈

 

 

 

이번 포스팅에서는 Spring Boot에서 잘못된 요청이 들어왔을 때 에러 페이지를 처리하는 방법에 대해 알아보겠습니다.

에러 페이지에 대한 아무런 정의가 없을 경우 Spring Boot에서 브라우저에 출력해주는 화면은 아래와 같습니다.

 

Spring Boot 기본 에러 페이지

 

Spring Framework Tomcat 에러 페이지

 

Spring Boot 이전의 버전을 사용하셨던 분들이라면 아마도 위 두 사진 중 아래의 사진이 익숙할 것입니다.

 

웹 사이트는 url을 통해 접근하는 애플리케이션이므로 얼마든지 위와 같이 서버에서 수용할 수 없는 요청을 받을 수 있으므로 이에 대한 적절한 처리를 해 줄 필요가 있습니다.

만약 상용화할 프로젝트인데 위와 같이 프레임워크 기본 에러 페이지를 출력해 준다면 상당히 완성도가 떨어져 보일 겁니다.

 

Github 404 Error Page

그렇기 때문에 대부분의 상용화된 프로젝트는 각자만의 에러 페이지를 정의하고 있습니다.

위 사진은 개인적으로 세련됐다고 생각하는 Github의 404 에러 페이지입니다.

 

404는 HTTP 응답 코드 중 하나로, 해당하는 리소스를 찾을 수 없다는 웹상에서 아주 흔하게 접하는 코드입니다.

그 외에도 접근 거부 오류인 403, 요청 시간 초과 오류인 408, 서버 측 오류인 500 등등 많은 응답 코드가 존재합니다.

 

이와 관련한 자세한 내용은 아래의 링크를 참고해 주세요.

developer.mozilla.org/en-US/docs/Web/HTTP/Status

 

HTTP response status codes

HTTP response status codes indicate whether a specific HTTP request has been successfully completed. Responses are grouped in five classes: Informational responses (100–199), Successful responses (200–299), Redirects (300–399), Client errors (400–4

developer.mozilla.org

 

1. application.properties 설정

사용자 에러 페이지 정의를 위해서는 application.properties에서 몇 가지 설정을 해 주어야 합니다.

아래의 내용을 application.properties에 정의해 주세요.

 

#오류 응답에 exception의 내용을 포함할지 여부
server.error.include-exception=TRUE

#오류 응답에 stacktrace 내용을 포함할지 여부 (ALWAYS, NEVER, ON_TRACE_PARAM)
server.error.include-stacktrace=ALWAYS

#브라우저 요청에 대해 서버 오류시 기본으로 노출할 페이지를 사용할지 여부
server.error.whitelabel.enabled=FALSE

우선 기본적으로 발생하게 된 에러 내용을 모두 포함해보겠습니다.

stacktrace는 오류가 발생하게 된 과정에 대한 로그를 의미합니다.

 

가장 아래에 있는 whitelabel.enabled는 본 포스팅의 처음 예시 이미지와 같이 프로젝트 기본 에러 페이지를 사용할 것인지에 대한 설정입니다.

예제에서는 사용자 정의 에러 페이지를 사용하기 위해서 FALSE로 설정합니다.

 

2. 에러를 처리하기 위한 컨트롤러 작성

 

controller 패키지에 ExceptionHadlingController 파일을 생성하고 아래와 같이 코드를 작성합니다.

 

@Controller
public class ExceptionHandlingController implements ErrorController {
	private final Logger logger = LoggerFactory.getLogger(this.getClass());

	// 에러 페이지 정의
	private final String ERROR_404_PAGE_PATH = "/error/404";
	private final String ERROR_500_PAGE_PATH = "/error/500";
	private final String ERROR_ETC_PAGE_PATH = "/error/error";

	@RequestMapping(value = "/error")
	public String handleError(HttpServletRequest request, Model model) {

		// 에러 코드를 획득한다.
		Object status = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);

		// 에러 코드에 대한 상태 정보
		HttpStatus httpStatus = HttpStatus.valueOf(Integer.valueOf(status.toString()));
        
		if (status != null) {
			// HttpStatus와 비교해 페이지 분기를 나누기 위한 변수
			int statusCode = Integer.valueOf(status.toString());

			// 로그로 상태값을 기록 및 출력
			logger.info("httpStatus : " + statusCode);

			// 404 error
			if (statusCode == HttpStatus.NOT_FOUND.value()) {
				// 에러 페이지에 표시할 정보
				model.addAttribute("code", status.toString());
				model.addAttribute("msg", httpStatus.getReasonPhrase());
				model.addAttribute("timestamp", new Date());
				return ERROR_404_PAGE_PATH;
			}
            
			// 500 error
			if (statusCode == HttpStatus.INTERNAL_SERVER_ERROR.value()) {
				// 서버에 대한 에러이기 때문에 사용자에게 정보를 제공하지 않는다.
				return ERROR_500_PAGE_PATH;
			}
		}

		// 정의한 에러 외 모든 에러는 error/error 페이지로 보낸다.
		return ERROR_ETC_PAGE_PATH;
	}

	@Override
	public String getErrorPath() {
		return "/error";
	}

}

 

에러 컨트롤러도 컨트롤러이기 때문에 @Controller 어노테이션을 붙여주어야 이 컨트롤러로 요청 처리가 넘어옵니다.

그리고 ErrorController 인터페이스를 상속받습니다.

 

HttpStatus에서 정의한 상태 enum 값과 비교해 적절한 페이지로 보냅니다.

이때, 404 에러는 사용자의 실수(url 오타 등)에 의해 반환하는 코드 값이므로 에러에 대한 정보를 같이 보내줍니다.

반면 500 에러는 서버 측 에러이기 때문에 사용자에게 정보를 보여주지 않습니다.

 

3. 에러 페이지 작성

에러 페이지는 thymeleaf로 생성해서 default_layout을 상속받는 형태로 작성해 보겠습니다.

 

resources 디렉터리의 templates 패키지안에 error 패키지를 생성하고 그림과 같이 404.html, 500.html, error.html 파일을 생성합니다.

 

그리고 각 파일에 다음과 같이 작성합니다.

 

404.html

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
	xmlns:th="http://www.thymeleaf.org"
	xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
	layout:decorate="~{layout/default_layout}">

<th:block layout:fragment="head">
	<title>Spring Boot 404 Error PAGE</title>
	<!--/* 이 영역에 공통으로 사용할 css, js library를 선언한다. */-->
	<link th:href="@{/css/common.css}" rel="stylesheet" />
</th:block>

<body>
	<th:block layout:fragment="header" th:include="@{/fragments/header}"></th:block>

	<h1>This is 404 Error Page!</h1>
	<div>
            CODE: <span th:text="${code}"></span><br>
            MSG: <span th:text="${msg}"></span><br>
            TIMESTAMP: <span th:text="${timestamp}"></span><br>
	</div>

	<th:block layout:fragment="footer" th:include="@{/fragments/footer}"></th:block>
</body>

</html>

 

 

500.html

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
	xmlns:th="http://www.thymeleaf.org"
	xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
	layout:decorate="~{layout/default_layout}">

<th:block layout:fragment="head">
	<title>Spring Boot 500 Error PAGE</title>
	<!--/* 이 영역에 공통으로 사용할 css, js library를 선언한다. */-->
	<link th:href="@{/css/common.css}" rel="stylesheet" />
</th:block>
<body>
	<th:block layout:fragment="header" th:include="@{/fragments/header}"></th:block>

	<h1>This is 500 Error Page!</h1>
	500 Server Error...
	<br>

	<th:block layout:fragment="footer" th:include="@{/fragments/footer}"></th:block>
</body>
</html>

 

error.html

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
	xmlns:th="http://www.thymeleaf.org"
	xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
	layout:decorate="~{layout/default_layout}">

<th:block layout:fragment="head">
	<title>Spring Boot Unknown Error PAGE</title>
	<!--/* 이 영역에 공통으로 사용할 css, js library를 선언한다. */-->
	<link th:href="@{/css/common.css}" rel="stylesheet" />
</th:block>
<body>
	<th:block layout:fragment="header" th:include="@{/fragments/header}"></th:block>

	<h1>This is Unknown Error Page!</h1>
	Unknown Server Error...
	<br>

	<th:block layout:fragment="footer" th:include="@{/fragments/footer}"></th:block>
</body>
</html>

 

이것으로 ExceptionHandlingController에서 정의한 각 에러와 대응하는 페이지를 모두 작성했습니다.

 

4. 에러 발생 테스트

본 포스팅에서는 404와 500 에러에 대한 테스트만 진행해 보겠습니다.

 

먼저, 컨트롤러에서 정의하지 않은 주소로 접속해 봅니다.

 

404 에러  테스트

404.html 페이지와 model을 통해 전달한 정보 값이 정상적으로 출력되고 있음을 확인했습니다.

 

다음으로 컨트롤러에 오류를 발생시켜서 500 페이지를 띄워 보겠습니다.

 

위와 같이 List 객체를 초기화하지 않고 모델 오브젝트로 지정했습니다.

이 코드는 확실하게 서버 에러를 발생시킬 것입니다.

 

저장하고 브라우저로 접속해 봅니다.

 

 

예상대로 500 에러가 발생했고 그에 따라 500.html을 출력해 주고 있습니다.

 

이것으로 Spring Boot에서의 에러 페이지 처리에 대한 포스팅을 마치겠습니다.