학습 순서
서블릿 예외 처리 -> 스프링 예외 처리
1. 서블릿 예외 처리
스프링이 아닌 순수 서블릿 컨테이너의 예외 처리
📌서블릿의 예외 처리 방식 2가지
- Exception (예외) -> 무조건 500 에러 (WAS까지 예외가 왔을 때)
- response.sendError (HTTP 상태 코드, 오류 메시지) -> 상태 코드 지정 가능
✔ 스프링 부트가 제공하는 기본 예외 페이지 끄기
* application.properties
server.error.whitelabel.enabled=false
1-1. Exception (예외)
- 자바의 기본 예외 동작 방식
- 자바의 메인 메소드 직접 실행
-> main 쓰레드 실행
-> exception 발생
-> 예외를 못잡으면, 나를 호출한 메소드한테로 예외를 계속 던짐
-> main() 메소드를 넘어서 예외가 던져지면
-> 예외 정보를 남기고 해당 쓰레드 종료
- 자바의 메인 메소드 직접 실행
- 웹 애플리케이션에서의 예외 동작 방식
- 사용자 요청별로 별도의 쓰레드 할당 (멀티 쓰레드)
-> 해당 쓰레드가 서블릿 컨테이너 안에서 실행
-> exception 발생
-> 어디서 try ~ catch 로 예외를 잡아서 처리하면 문제 X
-> 애플리케이션에서 예외를 잡지 못하고, 서블릿 밖으로까지 예외가 전달되면?
(인터셉터에서는 completion에서 예외를 잡아주긴 하지만, 만약 못잡고 넘어간다면)
- 사용자 요청별로 별도의 쓰레드 할당 (멀티 쓰레드)
HTTP 요청 -> WAS -> 필터 -> 서블릿 -> 인터셉터 -> 컨트롤러 (예외 발생)
WAS <- 필터 <- 서블릿 <- 인터셉터 <- 컨트롤러 (예외 발생)
- WAS(톰캣)까지 예외 전달
- 예외에 따라 지정해 둔 예외 페이지가 있으면 -> 그 페이지로 url 재요청
- 예외에 따라 지정해 둔 예외 페이지가 없으면 -> 서블릿이 기본으로 제공하는 오류 페이지로 500 응답
1) 서블릿 예외 컨트롤러
* ServletExController
- RuntimeExceiption -> throws로 예외를 직접 던지지 않아도 알아서 위로 전달됨
@Slf4j
@Controller
public class ServletExController {
@GetMapping("/error-ex")
public void errorEx() {
throw new RuntimeException("예외 발생!");
}
2) 실행
* 톰캣이 기본으로 제공하는 오류 화면
(1) Exception 발생
WAS는 서버 내부에서 처리할 수 없는 오류가 발생했다고 인식 -> 500 에러 반환 -> 톰캣이 기본으로 제공하는 오류 화면
(2) 없는 url 호출
기존의 해당 에러 페이지가 아닌 톰캣 404 에러 페이지 반환
1-2. response.sendError (HTTP 상태 코드, 오류 메시지)
서블릿 컨테이너에게 오류가 발생했다는 점 전달
(당장 오류 발생 X -> 정상 동작)
1) 컨트롤러
* ServletExController
@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);
}
HTTP 요청 -> WAS -> 필터 -> 서블릿 -> 인터셉터 -> 컨트롤러 (response.sendError() 호출)
WAS <- 필터 <- 서블릿 <- 인터셉터 <- 컨트롤러 (response.sendError() 호출)
- sendError() 호출 -> response 내부에 오류가 발생했다는 상태 저장
- WAS(톰캣)에서 response에 sendError() 호출 여부 확인
- 예외에 따라 지정해 둔 예외 페이지가 있으면 -> 그 페이지로 url 재요청
- 예외에 따라 지정해 둔 예외 페이지가 없으면 -> 설정한 오류 코드에 맞춰 기본 오류 페이지 응답
2) 정리
Exception : 예외 발생 시, 모두 500으로 반환
sendError() : 지정한 상태 코드에 따라 반환
1-3. 서블릿이 기본으로 제공하는 오류 화면 커스텀
서블릿 컨테이너가 제공하는 못생긴 기본 예외 처리 화면 커스텀하기
1) 과거 오류 화면 등록 방법 - web.xml
<web-app>
<error-page>
<error-code>404</error-code>
<location>/error-page/404.html</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/error-page/500.html</location>
</error-page>
<error-page>
<exception-type>java.lang.RuntimeException</exception-type>
<location>/error-page/500.html</location>
</error-page>
</web-app>
지금은 스프링 부트로 서블릿 컨테이너 실행
-> 스프링 부트가 제공하는 기능 사용해서 서블릿 오류 페이지 등록
2) 서블릿 오류 페이지 등록 - WebServerCustumizer
package hello.exception;
import org.springframework.boot.web.server.ConfigurableWebServerFactory;
import org.springframework.boot.web.server.ErrorPage;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
@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");
// Exception 발생
ErrorPage errorPageEx = new ErrorPage(RuntimeException.class, "/error-page/500");
factory.addErrorPages(errorPage404, errorPage500, errorPageEx);
}
}
- @Component : 스프링 빈에 등록 !
- response.sendError(404) -> errorPage404 호출
- response.sendError(500) -> errorPage500 호출
- RuntimeException 또는 그 자식 예외 -> errorPageEx 호출
3) 오류 처리 컨트롤러
package hello.exception.servlet;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@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";
}
}
4) 오류 처리 뷰 (에러페이지)
* error-page/404
<!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>404 오류 화면</h2>
</div>
<div>
<p>오류 화면 입니다.</p>
</div>
<hr class="my-4">
</div> <!-- /container -->
</body>
</html>
* error-page/500
<!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>
<hr class="my-4">
</div> <!-- /container -->
</body>
</html>
5) 실행
- 서블릿 예외 처리 방식 2가지
- Exception (예외) 발생해서 서블릿 밖으로 전달
- response.sendError() 호출
- 서블릿 오류페이지 요청 흐름
WAS (여기까지 전달) <- 필터 <- 서블릿 <- 인터셉터 <- 컨트롤러 (예외 발생 / sendError() 호출)
WAS (오류페이지 다시 요청) -> 필터 -> 서블릿 -> 인터셉터 -> 컨트롤러 (오류페이지 호출) -> View
- 중요한 점
- 웹 브라우저(클라이언트)는 서버 내부에서 이런 일이 일어나는 것을 모름 !
- WAS에서 웹 브라우저로 내보내기 전에 확인하고 다시 추가 요청하기 때문 -> 최종 응답 결과만 봄
(1) Exception 예외 발생의 경우
① 컨트롤러 - 예외 발생
@GetMapping("/error-ex")
public void errorEx() {
throw new RuntimeException("예외 발생!");
}
② WAS 에서 오류 페이지 정보 확인 (RuntimeException 확인) -> 내부에서 오류페이지 호출
@Component
public class WebServerCustomizer implements WebServerFactoryCustomizer<ConfigurableWebServerFactory> {
@Override
public void customize(ConfigurableWebServerFactory factory) {
// Exception 발생
ErrorPage errorPageEx = new ErrorPage(RuntimeException.class, "/error-page/500");
factory.addErrorPages(errorPage404, errorPage500, errorPageEx);
}
}
③ 오류 페이지 경로로 다시 요청(호출)
@RequestMapping("/error-page/500")
public String errorPage500(HttpServletRequest request, HttpServletResponse response) {
log.info("errorPage 500");
return "error-page/500";
}
④ 오류 페이지 반환
(2) sendError() 를 호출하는 경우
① 컨트롤러 - sendError() 호출
@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);
}
② WAS 에서 오류 페이지 정보 확인 (404, 500 상태 확인) -> 내부에서 오류페이지 호출
@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");
factory.addErrorPages(errorPage404, errorPage500, errorPageEx);
}
}
③ 오류 페이지 경로로 다시 요청(호출)
@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";
}
④ 오류 페이지 반환
6) 오류 정보 추가
WAS가 오류 페이지를 다시 요청할 때, 오류 정보를 request의 attribute에 추가해서 넘길 수 있음
(1) 오류 정보 추가 - 컨트롤러
package hello.exception.servlet;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Slf4j
@Controller
public class ErrorPageController {
// RequestDispatcher 상수로 정의되어 있음
public static final String ERROR_EXCEPTION = "javax.servlet.error.exception";
public static final String ERROR_EXCEPTION_TYPE = "javax.servlet.error.exception_type";
public static final String ERROR_MESSAGE = "javax.servlet.error.message";
public static final String ERROR_REQUEST_URI = "javax.servlet.error.request_uri";
public static final String ERROR_SERVLET_NAME = "javax.servlet.error.servlet_name";
public static final String ERROR_STATUS_CODE = "javax.servlet.error.status_code";
@RequestMapping("/error-page/404")
public String errorPage404(HttpServletRequest request, HttpServletResponse response) {
log.info("errorPage 404");
printErrorInfo(request);
return "error-page/404";
}
@RequestMapping("/error-page/500")
public String errorPage500(HttpServletRequest request, HttpServletResponse response) {
log.info("errorPage 500");
printErrorInfo(request);
return "error-page/500";
}
private void printErrorInfo(HttpServletRequest request) {
log.info("ERROR_EXCEPTION: {}", request.getAttribute(ERROR_EXCEPTION));
log.info("ERROR_EXCEPTION_TYPE: {}", request.getAttribute(ERROR_EXCEPTION_TYPE));
log.info("ERROR_MESSAGE: {}", request.getAttribute(ERROR_MESSAGE));
log.info("ERROR_REQUEST_URI: {}", request.getAttribute(ERROR_REQUEST_URI));
log.info("ERROR_SERVLET_NAME: {}", request.getAttribute(ERROR_SERVLET_NAME));
log.info("ERROR_STATUS_CODE: {}", request.getAttribute(ERROR_STATUS_CODE));
log.info("dispatchType={}", request.getDispatcherType());
}
}
- 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 상태 코드
- dispatcherType : 필터 옵션
(2) 실행
① Exception 예외 발생 시 ( /error-ex 호출 ) 로그 출력
② sendError() 호출 시 ( /error-404, /error-500 호출 ) 로그 출력
1-4. 필터 중복호출 제거 (오류페이지로 재요청 시)
1) DispatcherType의 필요성
- 서블릿 오류페이지 요청 흐름
WAS (여기까지 전달) <- 필터 <- 서블릿 <- 인터셉터 <- 컨트롤러 (예외 발생 / sendError() 호출)
WAS (오류페이지 다시 요청) -> 필터 -> 서블릿 -> 인터셉터 -> 컨트롤러 (오류페이지 호출) -> View
오류 발생 -> 필터, 서블릿, 인터셉터도 모두 다시 호출 (예- 로그인 체크 필터 등)
-> 오류페이지를 호출할 때 모두 다시 호출되는 것은 비효율적
-> 클라이언트로부터 발생한 정상 요청인지, 오류 페이지를 출력하기 위한 내부 요청인지 구분 필요
-> 서블릿이 DispatcherType 이라는 추가 정보 제공
-> 오류페이지를 호출할 때는 필터를 호출하지 않도록 dispatcherType 설정하기
2) DispatcherType
실제 고객이 요청한 것인지, 서버가 내부에서 오류페이지를 요청하는 것인지 구분
log.info("dispatchType={}", request.getDispatcherType())
로그 출력시
dispatchType = REQUEST // 고객이 처음 요청
dispatchType = ERROR // 오류 페이지 요청
- javax.servlet.DispatcherType
- REQUEST : 클라이언트 요청
- ERROR : 오류 요청
- FORWARD : 서블릿에서 다른 서블릿이나 JSP 호출 시 (RequestDispatcher.forward(request, response))
- INCLUDE : 서블릿에서 다른 서블릿이나 JSP의 결과 포함 시 (RequestDispatcher.include(request, response))
- ASYNC : 서블릿 비동기 호출
public enum DispatcherType {
FORWARD,
INCLUDE,
REQUEST,
ASYNC,
ERROR
}
3) 필터와 DispatcherType
(1) DispatcherType 확인을 위한 로그 추가 - LogFilter
- log.info("REQUEST [{}][{}][{}]", uuid, request.getDispatcherType(), requestURI);
- log.info("RESPONSE [{}][{}][{}]", uuid, request.getDispatcherType(), requestURI);
package hello.exception.filter;
import lombok.extern.slf4j.Slf4j;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.UUID;
@Slf4j
public class LogFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
log.info("log filter init");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
String requestURI = httpRequest.getRequestURI();
String uuid = UUID.randomUUID().toString();
try {
log.info("REQUEST [{}][{}][{}]", uuid, request.getDispatcherType(), requestURI);
chain.doFilter(request, response);
} catch (Exception e) {
throw e;
} finally {
log.info("RESPONSE [{}][{}][{}]", uuid, request.getDispatcherType(), requestURI);
}
}
@Override
public void destroy() {
log.info("log filter destroy");
}
}
(2) 필터 설정 - WebConfig
package hello.exception;
import hello.exception.filter.LogFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import javax.servlet.DispatcherType;
import javax.servlet.Filter;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Bean
public FilterRegistrationBean logFilter() {
FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>();
filterRegistrationBean.setFilter(new LogFilter());
filterRegistrationBean.setOrder(1);
filterRegistrationBean.addUrlPatterns("/*");
filterRegistrationBean.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ERROR);
return filterRegistrationBean;
}
}
- filterRegistrationBean.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ERROR);
- 클라이언트 요청, 오류 페이지 요청 시 모두 필터 호출
- 기본 값 : DispatcherType.REQUEST (클라이언트의 요청이 있는 경우만 필터 적용 -> 중복 필터 X)
(3) 로그 출력 ( /error-ex 호출 시 )
- 클라이언트의 요청
- LogFIlter - try - 필터 실행 ( request.getDispatcherType() - REQUEST )
- doFilter - 필터->서블릿->인터셉터->컨트롤러 (예외 발생)
- catch 잡아서 WAS로 던짐
- finally - 필터 나옴 ( request.getDispatcherType() - REQUEST ) - WAS에서 예외 확인 -> 오류 페이지 요청 -> 필터 다시 호출
- LogFIlter - try - 필터 실행 ( request.getDispatcherType() - REQUEST )
- 내부에서 오류 페이지 요청 (LogFilter 중복 호출)
- LogFilter - try - 필터 실행 ( request.getDispatcherType() - ERROR )
- doFIlter - 필터->서블릿->인터셉터->컨트롤러 (오류 페이지, 오류 정보 로그 출력)
- finally - 필터 나옴 ( request.getDispatcherType() - ERROR )
- LogFilter - try - 필터 실행 ( request.getDispatcherType() - ERROR )
=> filterRegistrationBean.setDispatcherTypes(DispatcherType.REQUEST) 으로 중복 필터 제거
1-5. 인터셉터 중복 제거 (오류페이지 재요청 시)
1) 로그 추가 - LogInterceptor
- log.info("REQUEST [{}][{}][{}]", uuid, request.getDispatcherType(), requestURI);
- log.info("RESPONSE [{}][{}][{}]", uuid, request.getDispatcherType(), requestURI);
package hello.exception.interceptor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.UUID;
@Slf4j
public class LogInterceptor implements HandlerInterceptor {
public static final String LOG_ID = "logId";
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String requestURI = request.getRequestURI();
String uuid = UUID.randomUUID().toString();
request.setAttribute(LOG_ID, uuid);
log.info("REQUEST [{}][{}][{}][{}]", uuid, request.getDispatcherType(), requestURI, handler);
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.info("postHandle [{}]", modelAndView);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
String requestURI = request.getRequestURI();
String logId = (String)request.getAttribute(LOG_ID);
log.info("RESPONSE [{}][{}][{}]", logId, request.getDispatcherType(), requestURI);
if (ex != null) {
log.error("afterCompletion error!!", ex);
}
}
}
2) 인터셉터 설정 - WebConfig
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LogInterceptor())
.order(1)
.addPathPatterns("/**")
.excludePathPatterns("/css/**", "*.ico", "/error", "/error-page/**"); // 오류 페이지 경로 및 그 하위 페이지를 뺌
}
- 필터 -> DispatcherType에 따라 필터를 적용할지 말지 선택 O
- 인터셉터 -> 서블릿이 아닌 스프링이 제공하는 기능 -> DispatcherType과 무관하게 항상 호출
=> 오류페이지 경로를 빼줌
( 오류페이지 경로를 빼지 않으면, "error-page/500"같은 내부 호출의 경우에도 인터셉터 호출 )
3) 로그
- 예외 발생 -> WAS로 갔다가 -> 인터셉터 중복 호출 X -> 바로 컨트롤러 -> 예외 페이지 출력
4) 정리
- /hello 정상 요청
WAS(/hello, dispatchType=REQUEST) -> 필터 -> 서블릿 -> 인터셉터 -> 컨트롤러 -> View
- /error-ex 오류 요청
- 필터 : DispatchType으로 중복 호출 제거 (setDispatcherTypes(DispatcherType.REQUEST))
- 인터셉터 : 경로 정보로 중복 호출 제거 (excludePathPatterns("/error-page/**"))
1. WAS(/error-ex, dispatchType=REQUEST) -> 필터 -> 서블릿 -> 인터셉터 -> 컨트롤러
2. WAS(여기까지 전파) <- 필터 <- 서블릿 <- 인터셉터 <- 컨트롤러(예외발생)
3. WAS 오류 페이지 확인
4. WAS(/error-page/500, dispatchType=ERROR) ->필터(x)-> 서블릿 ->인터셉터(x)-> 컨트롤러(/error-page/500) -> View
2. 스프링 부트 예외 처리
- 순수 서블릿 예외 처리 과정 -> 복잡
- WebServerCustomizer 생성
- 예외 종류에 따라서 ErrorPage 추가
- 예외 처리용 컨트롤러 ErrorPageController 생성
- 스프링 부트 예외 처리
- ErrorPage 자동 등록 -> /error 라는 경로로 기본 오류 페이지 설정
- new ErrorPage("/error") - 상태코드와 예외를 설정하지 않으면 기본 오류 페이지로 사용
- 서블릿 밖으로 예외 발생(Exception)하거나, response.sendError(...) 호출되면
-> 모든 오류는 /error 호출(재요청)하게 됨
- BasicErrorController라는 스프링 컨트롤러 자동 등록
- ErrorPage에서 등록한 /error를 매핑해서 처리
- ErrorPage에서 등록한 /error를 매핑해서 처리
- 오류 발생
-> /error 를 기본 요청
-> BasicErrorController가 이 경로를 기본으로 매핑해서 처리
-> 개발자는 오류 페이지 화면만 등록
- ErrorPage 자동 등록 -> /error 라는 경로로 기본 오류 페이지 설정
- 뷰 선택 우선순위 (BasicErrorController의 처리 순서)
- 뷰 템플릿
- resources/templates/error/500.html
- resources/templates/error/5xx.html
- 정적 리소스 (static, public)
- resources/static/error/400.html
- resources/static/error/404.html
- resources/static/error/4xx.html
- 적용 대상이 없을 때 뷰 이름 (error)
- resources/templates/error.html
- 뷰 템플릿
1) 오류 뷰 템플릿 추가
* resources/templates/error/4xx.html (+400, 500)
<!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>4xx 오류 화면 스프링 부트 제공</h2>
</div>
<div>
<p>오류 화면 입니다.</p>
</div>
<hr class="my-4">
</div> <!-- /container -->
</body>
</html>
2) BasicErrorController가 제공하는 기본 정보들
아래 정보들을 model에 담아서 뷰에 전달
<!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>
근데 이렇게 오류 관련 내부 정보 노출하면 X
-> 오류 정보를 model에 포함할지 여부 선택 가능
* application.properties
server.error.include-exception=true // exception 포함여부
server.error.include-message=on_param // message 포함 여부
server.error.include-stacktrace=on_param // trace 포함 여부
server.error.include-binding-errors=on_param // errors 포함 여부
- never : 사용 X
- always : 항상 사용
- on_param : 파라미터 있을 때 (message=&errors=&trace=)
'Spring' 카테고리의 다른 글
[인프런/스프링 MVC 2편] 10. 스프링 타입 컨버터 (0) | 2023.05.31 |
---|---|
[인프런/스프링 MVC 2편] 9. API 예외 처리 (1) | 2023.05.31 |
[Spring] 스프링 빈 등록 어노테이션 @Bean, @Configuration, @Component (0) | 2023.05.27 |
[인프런/스프링 MVC 2편] 7. 로그인 처리2 - 필터, 인터셉터 (0) | 2023.05.26 |
[인프런/스프링 MVC 2편] 6. 로그인 처리1 - 쿠키, 세션 (0) | 2023.05.25 |