웹사이트를 운영하다보면 404, 500 등의 에러페이지를 서버에서 기본으로 제공해주는 화면이 아닌 각 사이트의 디자인에 맞춰 변경해야 한다.
Tomcat 같은 경우 아래의 페이지를 기본으로 제공해준다.
나는 위 페이지를 변경하기위해 검색해보니 대부분 web.xml, pom.xml 등의 .xml 설정파일을 통해 셋팅하는 방식이 많았다.
하지만, 내 프로젝트의 개발환경이 .xml을 사용하지 않고 view 코드를 resource 하위 폴더가 아닌 spring.mvc.view.prefix 설정을 따르고 있었기때문에 정보찾기가 힘들었다. (공식 사이트도 이해하기가 힘들었다..)
기본적으로 Spring Boot는 xml 파일을 강요하지 않기 때문에 프로젝트를 생성시에 web.xml 파일이 자동 생성되지 않는다.
web.xml 파일로 에러페이지를 설정하는 방식은 여러가지가 있으며 구글 검색으로 많이 찾을 수 있다.
그래도 기준이 되는 것이 공식 사이트의 현재 버전을 따르는 것이 좋으므로 이 곳을 참고하길 바란다.
공식 사이트 요약
27.1.11 Error Handling
기본적으로 Spring Boot는 모든 오류를 적적한 방식으로 처리하는 /error
맵핑을 제공하며, servlet container에 “global” 에러 페이지로 등록된다. 시스템 클라이언트의 경우, error, HTTP status 및 exception message 함께 JSON 응답을 생성한다. 브라우저 클라이언트의 경우, 동일한 데이터를HTML 형식(커스텀하기위해 error
를 해결하는 View
추가)으로 렌더링하는 “whitelabel” error view가 있다. 기본 동작을 완전히 바꾸기위해서, ErrorController
를 구현하고 해당 유형의 bean 정의를 등록하거나 ErrorAttributes 유형의 bean을 추가하여 기존 메커니즘을 사용하지만 내용을 대체 할 수 있다.
설정 방법1
위에 설명했다시피 Spring Boot는 기본적으로 모든 에러를 /error
로 맵핑해놨기때문에 해당하는 경로에 View
를 만들어주면 된다. 아래와 같이 /error
폴더를 만들어주고 status code에 맞춰 HTML 파일을 생성해주면 된다.
src/
+- main/
+- java/
| + <source code>
+- resources/
+- public/
+- error/
| +- 404.html
+- <other public assets>
src/
+- main/
+- java/
| + <source code>
+- resources/
+- templates/
+- error/
| +- 5xx.ftl
+- <other templates>
- WEB-INF
src/
+- main/
+- java/
| + <source code>
+- resources/
| + <static code>
+- webapp/
+- WEB_INF/
+- error/
| +- 404.jsp
+- <other jsps>
설정 방법2
만약 설정 방법1처럼 경로를 /error 가 아닌 다른 경로에 넣고 싶을 경우 ErrorController
를 implements
받은 custom controller 를 추가하면 된다.
@Controller
public class CustomErrorController implements ErrorController { private String PATH = "/error";
private String VIEW_PATH = "/errors"; @RequestMapping(value = PATH)
public String error(HttpRequest request) {
Object status = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
String statusCode = String.valueOf(status);
if (statusCode.equalsIgnoreCase(HttpStatus.NOT_FOUND.toString())) {
return VIEW_PATH + "404";
} ... return "error";
}
@Override
public String getErrorPath() {
return PATH;
}
}
getErrorPath
를 override 하여 error page 경로를 적어준다. (설정값을 바꾸지 않았으면 기본적으로 /error
이다.)
설정 방법3
application.yml
spring:
...
mvc:
view:
prefix: /WEB-INF/
suffix: .jsp
...
@Configuration 사용
발생하는 에러코드(HttpStatus)에 나타낼 ErrorPage를 설정한다.
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.server.ErrorPage;
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
@Configuration
public class ErrorConfig {
@Bean
public ConfigurableServletWebServerFactory webServerFactory() {
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
factory.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/error"));
return factory;
}
}
ErrorController 상속
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.RequestDispatcher;
import javax.servlet.http.HttpServletRequest;
@Controller
public class CustomErrorController implements ErrorController {
private static final String PATH = "/error"; // configure 에서 Redirect 될 path
@RequestMapping(value = PATH)
public String error(HttpServletRequest request) {
Object status = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
if (String.valueOf(status).equalsIgnoreCase(HttpStatus.NOT_FOUND.toString())) {
return "errors/404"; // /WEB-INF/errors/404.jsp
}
return "error";
}
@Override
public String getErrorPath() {
return PATH;
}
}
/WEB-INF/errors/404.jsp
<!DOCTYPE html>
<html lang="en">
<head>
<title>NOT FOUND</title>
</head>
<body>
<div class="cover">
<h1>404 NOT-FOUND</h1>
</div>
</body>
</html>
주의할 점은 config에서 new ErrorPage 시에 적은 path와 ErrorController 에 적은 PATH 가 동일해야하며, ErrorController 에서 return 해주는 값이 jsp 폴더 경로와 일치해야 한다는 점이다.
jsp 에서 error 내용 보여주기
나는 500 에러인 경우에 jsp 페이지에서 에러 내용을 보여주고 싶었다.
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
...<c:out value="${requestScope['javax.servlet.error.exception']}"/>
...
위에서 생성한 500.jsp 파일에 taglib 를 import 시켜준 후, c:out 을 이용하여 exception을 출력해줄 수 있다.
exception 외에도 status code 및 message 등 종류는 아래와 같다.
javax.servlet.error.status_code
- 에러 상태 코드 출력
javax.servlet.error.exception_type
- 예외처리 클래스 출력
javax.servlet.error.message
- 오류 메세지 출력
javax.servlet.error.request_uri
- 문제가 되는 request uri 정보를 출력
javax.servlet.error.exception
- 발생한 예외처리 내용 출력
javax.servlet.error.servlet_name
- 에러가 난 서블릿 명 출력
위 에러 정보를 이용하면 커스텀 에러페이지를 한 페이지로 구현할 수도 있을 것 같다.
+ 추가로 AWS BeanStalk 에 Spring Boot를 실행시키는데 위에서 설정한 custom error page가 동작되지 않는 현상이 발생할 수 있다. 정확한 원인은 파악하지 못했지만, @EnableAutoConfiguration 어노테이션을 사용하면 위의 설정이 동작되지 않는 것을 발견했다.
참조
1. Apache FreeMarker (https://freemarker.apache.org/)
2. Custom Error Pages (https://docs.spring.io/autorepo/docs/spring-boot/current/reference/htmlsingle/#boot-features-error-handling-custom-error-pages)
3. Spring Boot 에러페이지 설정 (http://mirotic91.tistory.com/5)
4. EmbeddedServletContainerCustomizer in spring boot 2.0 (https://stackoverflow.com/questions/49406779/embeddedservletcontainercustomizer-in-spring-boot-2-0)