스프링 부트는 기본적으로 오류페이지를 제공한다.
BasicErrorController ==> 스프링이 기본적으로 제공하는 오류관련 컨트롤러로 기본적인 로직이 모두 개발되어 있다.
<BasicErrorController>
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package org.springframework.boot.autoconfigure.web.servlet.error;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.springframework.boot.autoconfigure.web.ErrorProperties;
import org.springframework.boot.web.error.ErrorAttributeOptions;
import org.springframework.boot.web.error.ErrorAttributeOptions.Include;
import org.springframework.boot.web.servlet.error.ErrorAttributes;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.util.Assert;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
@Controller
@RequestMapping({"${server.error.path:${error.path:/error}}"})
public class BasicErrorController extends AbstractErrorController {
private final ErrorProperties errorProperties;
public BasicErrorController(ErrorAttributes errorAttributes, ErrorProperties errorProperties) {
this(errorAttributes, errorProperties, Collections.emptyList());
}
public BasicErrorController(ErrorAttributes errorAttributes, ErrorProperties errorProperties, List<ErrorViewResolver> errorViewResolvers) {
super(errorAttributes, errorViewResolvers);
Assert.notNull(errorProperties, "ErrorProperties must not be null");
this.errorProperties = errorProperties;
}
@RequestMapping(
produces = {"text/html"}
)
public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
HttpStatus status = this.getStatus(request);
Map<String, Object> model = Collections.unmodifiableMap(this.getErrorAttributes(request, this.getErrorAttributeOptions(request, MediaType.TEXT_HTML)));
response.setStatus(status.value());
ModelAndView modelAndView = this.resolveErrorView(request, response, status, model);
return modelAndView != null ? modelAndView : new ModelAndView("error", model);
}
@RequestMapping
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
HttpStatus status = this.getStatus(request);
if (status == HttpStatus.NO_CONTENT) {
return new ResponseEntity(status);
} else {
Map<String, Object> body = this.getErrorAttributes(request, this.getErrorAttributeOptions(request, MediaType.ALL));
return new ResponseEntity(body, status);
}
}
@ExceptionHandler({HttpMediaTypeNotAcceptableException.class})
public ResponseEntity<String> mediaTypeNotAcceptable(HttpServletRequest request) {
HttpStatus status = this.getStatus(request);
return ResponseEntity.status(status).build();
}
protected ErrorAttributeOptions getErrorAttributeOptions(HttpServletRequest request, MediaType mediaType) {
ErrorAttributeOptions options = ErrorAttributeOptions.defaults();
if (this.errorProperties.isIncludeException()) {
options = options.including(new ErrorAttributeOptions.Include[]{Include.EXCEPTION});
}
if (this.isIncludeStackTrace(request, mediaType)) {
options = options.including(new ErrorAttributeOptions.Include[]{Include.STACK_TRACE});
}
if (this.isIncludeMessage(request, mediaType)) {
options = options.including(new ErrorAttributeOptions.Include[]{Include.MESSAGE});
}
if (this.isIncludeBindingErrors(request, mediaType)) {
options = options.including(new ErrorAttributeOptions.Include[]{Include.BINDING_ERRORS});
}
options = this.isIncludePath(request, mediaType) ? options.including(new ErrorAttributeOptions.Include[]{Include.PATH}) : options.excluding(new ErrorAttributeOptions.Include[]{Include.PATH});
return options;
}
protected boolean isIncludeStackTrace(HttpServletRequest request, MediaType produces) {
boolean var10000;
switch (this.getErrorProperties().getIncludeStacktrace()) {
case ALWAYS -> var10000 = true;
case ON_PARAM -> var10000 = this.getTraceParameter(request);
case NEVER -> var10000 = false;
default -> throw new IncompatibleClassChangeError();
}
return var10000;
}
protected boolean isIncludeMessage(HttpServletRequest request, MediaType produces) {
boolean var10000;
switch (this.getErrorProperties().getIncludeMessage()) {
case ALWAYS -> var10000 = true;
case ON_PARAM -> var10000 = this.getMessageParameter(request);
case NEVER -> var10000 = false;
default -> throw new IncompatibleClassChangeError();
}
return var10000;
}
protected boolean isIncludeBindingErrors(HttpServletRequest request, MediaType produces) {
boolean var10000;
switch (this.getErrorProperties().getIncludeBindingErrors()) {
case ALWAYS -> var10000 = true;
case ON_PARAM -> var10000 = this.getErrorsParameter(request);
case NEVER -> var10000 = false;
default -> throw new IncompatibleClassChangeError();
}
return var10000;
}
protected boolean isIncludePath(HttpServletRequest request, MediaType produces) {
boolean var10000;
switch (this.getErrorProperties().getIncludePath()) {
case ALWAYS -> var10000 = true;
case ON_PARAM -> var10000 = this.getPathParameter(request);
case NEVER -> var10000 = false;
default -> throw new IncompatibleClassChangeError();
}
return var10000;
}
protected ErrorProperties getErrorProperties() {
return this.errorProperties;
}
}
컨트롤러를 스프링에서 기본적으로 제공하기 때문에 개발자는 오류 페이지만 만들어주면 된다.
오류 페이지 생성 방법
- templates 디렉터리 아래 error 디렉터리 생성
- 500.html , 5xx.html, 400.html, 404.html, 4xx.html 등과 같이 오류 코드에 맞는 html 파일 생성
이때 404나 500과 같이 구체적인 것이 4xx나 5xx보다 우선순위가 높다.
테스트
예외 발생 시키는 컨트롤러
package com.example.exception.servlet;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import java.io.IOException;
@Slf4j
@Controller
public class ServletExController {
@GetMapping("/error-ex")
public void errorEx(){
throw new RuntimeException("예외 발생!");
}
@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,"500 오류!");
}
}
<500.html>
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
</head>
<body>
<div class="container" style="max-width: 600px">
<div class="py-5 text-center">
<h2>500 오류 화면 스프링 부트 제공</h2>
</div>
<div>
<p>오류 화면 입니다.</p>
</div>
<ul>
<li>오류 정보</li>
<ul>
<li th:text="|timestamp: ${timestamp}|"></li>
<li th:text="|path: ${path}|"></li>
<li th:text="|status: ${status}|"></li>
<li th:text="|message: ${message}|"></li>
<li th:text="|error: ${error}|"></li>
<li th:text="|exception: ${exception}|"></li>
<li th:text="|errors: ${errors}|"></li>
<li th:text="|trace: ${trace}|"></li>
</ul>
</li>
</ul>
<hr class="my-4">
</div> <!-- /container -->
</body>
</html>
결과
'BackEnd > 스프링 MVC' 카테고리의 다른 글
스프링 부트 - ExceptionResolver (0) | 2025.01.09 |
---|---|
HandlerExceptionResolver (0) | 2025.01.07 |
스프링 인터셉터 - 인증 체크 (0) | 2025.01.05 |
서블릿 필터 - 인증 체크 (0) | 2025.01.04 |
로그인 처리 - 서블릿 HTTP 세션,@SessionAttribute,세션 타임아웃 설정 (0) | 2025.01.01 |