관리 메뉴

나구리의 개발공부기록

예외 처리와 오류페이지 - 프로젝트 생성, 서블릿 예외처리 (시작/오류 화면 제공/오류 페이지 작동 원리/필터/인터셉터), 스프링부트 - 오류페이지 본문

인프런 - 스프링 완전정복 코스 로드맵/스프링 MVC 2편 - 백엔드 웹 개발 핵심 기술

예외 처리와 오류페이지 - 프로젝트 생성, 서블릿 예외처리 (시작/오류 화면 제공/오류 페이지 작동 원리/필터/인터셉터), 스프링부트 - 오류페이지

소소한나구리 2024. 9. 6. 20:20

  출처 : 인프런 - 스프링 MVC 2편 - 백엔드 웹 개발 핵심 기술 (유료) / 김영한님  
  유료 강의이므로 정리에 초점을 두고 코드는 일부만 인용  

https://inf.run/GMo43


1. 예외 처리와 오류페이지 - 프로젝트 생성

  • Project - Gradle
  • Language - Java(사용하는 버전 체크)
  • Group - hello
  • Artifact - exception
  • Packaging - Jar
  • Dependencies - Spring Web, Lombok, Thymeleaf, Validation


2. 서블릿 예외처리 - 시작

1) 순수 서블릿 컨테이너의 예외 처리

  • 서블릿은 2가지의 방식으로 예외를 처리함
  • Exception(예외)
  • response.sendError(Http 상태 코드, 오류 메시지)

2) Exception

(1) 자바 직접 실행

  • 자바의 main 메서드를 실행하는 경우 main이라는 쓰레드가 실행 되는데, 실행 도중 예외를 잡지 못하고 처음 실행한 main()메서들을 넘어가서 예외가 던져지면, 예외 정보를 남기고 해당 쓰레드는 종료 됨

(2) 웹 애플리케이션

  • 사용자 요청별로 별도의 쓰레드가 할당되고 서블릿 컨테이너 안에서 실행됨
  • 애플리케이션에서 에외가 발생되어 try - catch로 예외를 잡으면 문제가 안되지만 예외를 잡지 못하면 톰캣 같은 WAS까지 쭉 예외가 전달
  • WAS(여기까지 전파 됨) <- 필터 <- 서블릿 <- 인터셉터 <- 컨트롤러(예외 발생)

3) 서블릿 컨테이너의 예외 페이지

  • 스프링 부트가 제공하는 기본 예외 페이지 기능을 꺼두기
  • application.properties에  server.error.whitelabel.enabled=false를 입력
     

(1) ServletExController

  • 그냥 예외를 던지는 컨트롤러
@Slf4j
@Controller
public class ServletExController {

    @GetMapping("/error-ex")
    public void errorEx() {
        throw new RuntimeException("예외 발생");
    }
}
  • 컨트롤러 작성 후 서버를 시작하여 /error-ex 접속 하면 500에러가, 아무 사이트나 접속하면 404 에러가 아래의 이미지 처럼 뜸

톰캣이 기본으로 제공하는 오류 화면

3) response.sendError(HTTP 상태코드, 오류페이지)

  • HttpServletResponse가 제공하는 sendError 메서드를 사용하면 상태코드를 선택하고, 에러 메세지를 입력할 수도 있음
  • 당장 예외가 발생하는것은 아니지만 서블릿 컨테이너에게 오류가 발생했다는 점을 전달할 수 있음

(1) ServletExController - error404(), error500() 추가

  • 에러코드와 메세지, 에러코드만 전송하는 메서드를 생성
@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);
}
  • 재실행 후 /error-404, /error-500에 접속하면 response.sendError로 지정한 에러 메세지가 출력되는 것을 확인할 수 있음

(2) sendError 흐름

  • WAS(sendError 호출 기록 확인) <- 필터 <- 서블릿 <- 인터셉터 <- 컨트롤러(response.sendError())
  • response.sendError()를 호출하면 response내부에는 오류가 발생했는 상태를 저장해두고 서블릿 컨테이너는 고객에게 응답 전에 response에 sendError()가 호출 되어있는지 확인
  • 호출 되었다면 오류 코드에 맞추어 기본 오류 페이지를 보여줌

3. 서블릿 예외 처리 - 오류 화면 제공

  • 서블릿 컨테이너가 제공하는 기본 예외 처리 화면은 고객 친화적이지 않으므로 서블릿이 제공하는 오류 화면 기능을 사용하면 사용자에게 친절한 오류 처리 화면을 준비해서 고객에게 보여줄 수 있음
  • 과거에는 web.xml이라는 파일에 아래와 같이 작성하여 오류 화면을 등록했음
더보기
<web-app>
     <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>
  • 지금은 스프링 부트를 통해서 서블릿 컨테이너를 실행하기 때문에 스프링 부트가 제공하는 기능을 사용해서 서브릿 오류 페이지를 등록하면 됨
     

1) 서블릿 오류 페이지 등록

  • 404, 500, RuntimeException을 경로를 설정하여 ErrorPage를 생성하고 addErrorPages()로 등록
  • 오류페이지는 예외를 다룰 때 해당 예외와 그 자식 타입의 오류를 함께 처리하게 됨
package hello.exception;

@Component
public class WebServerCustomizer implements WebServerFactoryCustomizer<ConfigurableWebServerFactory> {

    @Override
    public void customize(ConfigurableWebServerFactory factory) {
        // Http 상태 코드 에러
        ErrorPage errorPage404 = new ErrorPage(HttpStatus.NOT_FOUND, "/error-page/404");
        ErrorPage errorPage500 = new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/error-page/500");

        // RuntimeException 및 그 자식타입의 예외
        ErrorPage errorPageEx = new ErrorPage(RuntimeException.class, "/error-page/500");

        // 등록
        factory.addErrorPages(errorPage404, errorPage500, errorPageEx);
    }
}

2) 컨트롤러 생성

  • 등록한 오류페이지를 보여주기 위한 컨트롤러
  • 에러가 발생하면 등록한 에러 페이지의 경로로 보내서 에러페이지를 보여줌
package hello.exception.servlet;

@Slf4j
@Controller
public class ErrorPageController {

    @RequestMapping("/error-page/404")
    public String errorPage404(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {
        log.info("errorPage 404");
        return "error-page/404";
    }

    @RequestMapping("/error-page/500")
    public String errorPage500(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {
        log.info("errorPage 404");
        return "error-page/500";
    }
}

3) 오류 처리 View 생성

  • ~/templates/error-page/에 404.html, 500html을 생성
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="utf-8">
</head>
<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>

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="utf-8">
</head>
<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>

4) 실행 결과

  • error-ex, error-404, error-500으로 접속하면 등록한 에러페이지가 나옴
  • 등록된 에러가 발생하면 WAS가 예외가 터졌다는것을 알고 에러페이지를 보여줌

출력된 오류페이지들


4. 서블릿 예외 처리 - 오류 페이지 작동 원리

1) 예외 발생과 오류 페이지 요청 흐름

  • 서블릿은 예외가 발생해서 서블릿 밖으로 전달되거나 response.sendError()가 호출되면 설정된 오류페이지를 찾음
  • WAS는 해당 예외를 처리하는 오류 페이지 정보를 확인하고 지정되어있는 오류페이지로 다시 재요청함

(1) 구조:  WAS - 필터 - 서블릿 - 인터셉터 - 컨트롤러

  1. WAS(여기까지 전파) <- 필터 <- 서블릿 <- 인터셉터 <-컨트롤러(예외발생)
  2. WAS(예외를 확인하고 지정된 경로로 다시 재요청) -> 필터 -> 서블릿 -> 인터셉터 -> 컨트롤러(지정된 경로 호출) -> View

(2) 중요한점

  • 웹 브라우저(클라이언트)는 서버 내부에서 이런 일이 일어나는지 전혀 모르고 오직 서버 내부에서 오류 페이지를 찾기 위해 추가적인 호출을 함
  • 한마디로 재요청되는 경로에 있는 필터, 서블릿, 인터셉트, 컨트롤러가 모두 다시 호출 됨

2) 오류 정보 추가

  • WAS는 오류 페이지를 다시 요청만 하는 것이 아니라 오류 정보를 request의 attribute에 추가해서 넘기기 때문에 필요하면 오류 페이지에서 전달된 오류 정보를 사용할 수 있음

(1) ErrorPageController - 오류 출력을 위한 수정

  • RequestDispatcher에 가면 오류 정보가 있어서 필요한 정보를 가져와서 출력해볼 수 있음
  • 아래 컨트롤러를 적용하고 실행해서 에러가 발생하는 경로로 이동하면 에러 정보들을 출력할 수 있음
package hello.exception.servlet;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Slf4j
@Controller
public class ErrorPageController {

    /* RequestDispatcher: 오류 정보가 상수로 정의 되어 있음 */

    String ERROR_EXCEPTION = "jakarta.servlet.error.exception";                 // 예외
    String ERROR_EXCEPTION_TYPE = "jakarta.servlet.error.exception_type";       // 예외타입
    String ERROR_MESSAGE = "jakarta.servlet.error.message";                     // 오류 메세지
    String ERROR_REQUEST_URI = "jakarta.servlet.error.request_uri";             // 클라이언트 요청 URI
    String ERROR_SERVLET_NAME = "jakarta.servlet.error.servlet_name";           // 오류가 발생한 서블릿 이름
    String ERROR_STATUS_CODE = "jakarta.servlet.error.status_code";             // HTTP 상태 코드

    // 주요 정보만 가져와서 출력
    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());
    }

    @RequestMapping("/error-page/404")
    public String errorPage404(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {
        log.info("error-page/404");
        printErrorInfo(httpServletRequest);
        return "error-page/404";
    }

    @RequestMapping("/error-page/500")
    public String errorPage500(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {
        log.info("error-page/500");
        printErrorInfo(httpServletRequest);
        return "error-page/500";
    }
}

5. 서블릿 예외 처리 - 필터

  • 오류가 발생하면 오류 페이지를 출력하기 위해 WAS 내부에서 다시한번 호출이 발생 되는데 이때 필터, 서블릿, 인터셉터도 모두 다시 호출되어 중복 호출이 발생 됨
  • 로그인 인증 체크 같은 경우 한번 필터나 인터셉터에서 체크를 완료했는데 오류 페이지를 출력한다고 다시 체크하는 것은 비효율 적이고 중복 호출 되면 안되는 필터나 인터셉터도 존재 할 수 있기에 방지를 해야함
  • 클라이언트로부터 발생한 정상 요청인지, 아니면 오류 페이지를 출력하기 위한 내부 요청인지 구분할 수 있어야 하는데  서블릿이 제공하는 DispatcherType이라는 정보로 해결할 수 있음

1) DispatcherType

  • 고객이 처음 요청하면 REQUEST로 오류 정보면 ERROR로 dispatcherType을 반환함
public enum DispatcherType {
	FORWARD,	// MVC에서 배웠던 서블릿에서 다른 서블릿이나 JSP를 호출할 때
	INCLUDE,	// 서블릿에서 다른 서블릿이나 JSP의 결과를 포함할 때
	REQUEST,	// 클라이언트 요청
	ASYNC,  	// 서블릿 비동기 호출
	ERROR   	// 오류 요청
}

2) 필터와 DispatcherType

(1) LogFilter - DispatcherType 로그 추가

  • Filter 수업때 배웠던 코드와 동일하므로 추가 부분만 작성
package hello.exception.filter;

@Slf4j
public class LogFilter implements Filter {

    // ... 기존 코드 동일

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
                         FilterChain filterChain) throws IOException, ServletException {

      	// ... 기존 코드 동일

        // 출력에 getDispatcherType()으로 디스패처타입 정보를 출력
        try {
            log.info("REQUEST [{}][{}][{}]", uuid, httpRequest.getDispatcherType(), requestURI);
            filterChain.doFilter(servletRequest, servletResponse);
        } catch (Exception e) {
            throw e;
        } finally {
            log.info("RESPONSE [{}][{}][{}]", uuid, httpRequest.getDispatcherType(), requestURI);
        }
    }
    
    // ... 기존 코드 동일

}

 

(2) WebConfig - 필터 등록

  • Filter 수업때 배웠던 코드와 동일하므로 추가 부분만 작성
  • 필터 설정에 filterRegistrationBean.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ERROR); 등록
  • DispatcherType이 REQUEST, ERROR일때 해당 필터를 적용하도록 설정
package hello.exception;

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Bean
    public FilterRegistrationBean logFilter() {
        // ... 기존코드 동일
        
        // 해당 필터는 DispatcherType이 REQUEST, ERROR 일때 사용이 된다는 뜻
        filterRegistrationBean.setDispatcherTypes(DispatcherType.REQUEST,DispatcherType.ERROR);
        return filterRegistrationBean;
    }
}

 

(3) 로그 확인

  • 로그를 확인해보면 필터가 두번 동작하는 것을 확인할 수 있음
  • setDispatcherTypes()로 Dispatcher타입별로 필터를 동작되게 만들어서 중복 요청을 막을 수 있음
  • 해당 값에 아무것도 넣지 않으면 기본값으로 DispatcherType.REQUEST이므로 특별히 오류 페이지 경로도 필터를 적용할 것이 아니면 기본값을 그대로 사용하면 됨
  • 오류 페이지 요청 전용 필터를 적용하고 싶으면 DispatcherType.ERROR만 지정하면 됨

6. 서블릿 예외 처리 - 인터셉터

1) 인터셉터 중복 호출 제거

(1) LogInterceptor - DispatcherType 로그 추가

  • 인터셉터 수업때 배웠던 코드와 동일하므로 추가 부분만 작성
package hello.exception.interceptor;

@Slf4j
public class LogInterceptor implements HandlerInterceptor {

    // ... 기존 코드 동일

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
                             Object handler) throws Exception {
        // ... 기존 코드 동일

        log.info("REQUEST [{}][{}][{}][{}]", uuid, request.getDispatcherType(), requestURI, handler);
        return true;
    }
    // ... 기존 코드 동일
}

 

(2) WebConfig 등록

  • 인터셉터는 등록 시 설정에 DispatcherType을 설정하는 것이 아닌 exludePathPatterns에 오류 페이지 URL을 허용하면 중복 호출을 막을 수 있음
  • 만약 오류페이지 경로를 등록하지 않으면 중복 호출되고 에러페이지를 호출하는 정상 호출로 간주되어 postHandler까지 호출됨
package hello.exception;

@Configuration
public class WebConfig implements WebMvcConfigurer {

    // 인터셉터는 설정에 DispatcherType 설정을 할 수 없어서 excludePathPatterns 에 오류페이지 URL을 등록하면 됨
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LogInterceptor())
                .order(1).addPathPatterns("/**")
                // "/error-page/**" 로 오류페이지 하위 경로의 모든 파일을 허용
                .excludePathPatterns("/css/**", "/*.ico", "/error", "/error-page/**");
    }

}

 

2) 전체 흐름 정리

(1) 정상 요청 - /hello

  • WAS(/hello, dispatchType=REQUEST) -> 필터 -> 서블릿 -> 인터셉터 -> 컨트롤러 -> View

(2) 오류 요청 - /error-ex

  • 필터는 DispatchType 으로 중복 호출 제거: dispatchType=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

7. 스프링부트 - 오류페이지

  • 지금까지 예외 처리 페이지를 만들기 위해 WebServerCustomizer를 만들고 ErrorPage를 추가하고 ErrorPageController를 만드는 번거롭고 복잡한 과정을 거쳤음
  • 스프링 부트는 이런 과정을 모두 기본으로 자동 제공함

1) 스프링 부트가 기본으로 제공

(1) ErrorPage 자동 등록

  • /error라는 경로로 기본 오류 페이지를 설정하여 추가로 설정하지 않는 오류들은 디폴트 경로인 /error로 이동하게 됨
  • 상태코드와 예외를 설정하지 않으면 기본 오류 페이지로 사용됨, new ErrorPage("/error")
  • 서블릿 밖으로 예외가 발생하거나 response.sendError(...)가 호출되면 별도로 지정하지 않은 모든 오류는 /error를 호출하게됨
  • ErrorMvcAutoConfiguration이라는 클래스가 오류 페이지를 자동으로 등록하는 역할을 함

(2) BasicErrorController 자동 등록

  • ErrorPage에서 등록한 /error를 매핑해서 처리하는 컨트롤러를 자동으로 등록함

** 실습 주의

  • 스프링 부트가 제공하는 기본 오류 메커니즘을 사용하기 위해 만들어 두었던 WebServerCustomizer의 @Component를 주석 처리하여 빈 등록을 제거

2) 개발자는 오류 페이지만 등록

  • BasicErrorController는 기본적인 로직이 개발 되어있어서 제공하는 룰과 우선순위에 따라서 오류 페이지 화면만 등록하면 됨
  • 정적 오류 화면이면 static, 동적 오류 화면이면 뷰 템플릿을 활용해서 경로에 오류 페이지 파일을 넣어두면 됨
  • 5xx.html, 4xx.html 이렇게 파일이름을 작성하여 경로에 넣어놓으면 500번대, 400번대의 에러를 모두 출력하는 에러페이지가 됨
  • 404.html ,500.html처럼 에러코드를 정확하게 파일을 작성하면 해당 에러가 발생했을때 동일한 이름의 파일을 출력하게 됨

(1) 뷰 선택 우선순위

  • 뷰 템플릿이 정적 리소스보다 우선순위가 높음
  • 404, 500 처럼 구체적인것이 5xx처럼 덜 구체적인 것보다 우선순위가 높음
우선순위 구분 경로
1 뷰 템플릿 resources/templates/error/500.html
2 resources/templates/error/5xx.html
3 정적 리소스(static, public) resources/static/error/400.html
4 resources/static/error/404.html
5 resources/static/error/4xx.html
6 적용 대상이 없을 때 - 뷰이름 (error) resources/templates/error.html

 

(2) 테스트

  • 테스트 페이지와 동작하는 컨트롤러를 생성하여 테스트 해보면 아래와같이 우선순위에 따라 동작하는 것을 확인할 수 있음

호출한 결과 페이지

3) BasicErrorController가 제공하는 기본 정보들

  • BasicErrorController는 다음 정보를 model에 담아서 뷰에 전달하므로 동적으로 에러페이지를 구성할때 이 값을 활용해서 출력할 수 있음
  • 시간, 상태, 에러 정보, 예외 정보, 클라이언 요청 경로 등등 
더보기

* timestamp: Fri Sep 06 19:57:44 KST 2024
* status: 400
* error: Bad Request
* exception: org.springframework.validation.BindException * trace: 예외 trace
* message: Validation failed for object='data'. Error count: 1
* errors: Errors(BindingResult)
* path: 클라이언트 요청 경로 (`/hello`)

(1) 오류 정보를 추가한 500.html 템플릿

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="utf-8">
</head>
<div class="container" style="max-width: 600px">
    <div class="py-5 text-center">
        <h2>500 오류 화면 - 스프링 부트가 제공함</h2></div>
    <div>
        <p>500 템플릿.</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>
  • 대부분 정보는 출력되지만 일부 정보는 출력되지 않았음 - 기본값은 일부로 일부 정보가 막혀있음
  • 오류 관련 내부 정보들을 고객에게 노출하는것은 좋지 않음
  • 고객은 해당 정보를 읽어도 혼란만 더해지고 해커에게 중요 정보를 노출하는 보안상의 문제가 될 수 있어서 노출하지 않아야 함
  • application.properties에서 설정을 조정하여 오류정보를 model 포함할지 여부를 선택할 수 있음

(2) 오류정보 출력 기본값

  • server.error.include-exception=false : exception 포함 여부(true , false)
  • server.error.include-message=never : message 포함 여부
  • server.error.include-stacktrace=never : trace 포함 여부
  • server.error.include-binding-errors=never : errors 포함 여부

 

(3) 오류정보 모두 출력

  • 아래처럼 설정하면 null 값이였던 정보가 모두 출력됨
server.error.include-exception=true
server.error.include-message=always
server.error.include-stacktrace=always
server.error.include-binding-errors=always

특정 파라미터가 있을 때만 출력
server.error.include-message=on_param
server.error.include-stacktrace=on_param
server.error.include-binding-errors=on_param

이렇게 파라미터만 있으면 정보가 출력됨
http://localhost:8080/error-ex?message=?stacktrace=

좌) 전체를 오류가 출력된 오류 페이지 / 우) 기본 정보만 출력된 오류 페이지

(4) 스프링 부트 오류 관련 옵션

  • server.error.whitelabel.enabled=true : 오류 처리 화면을 못 찾을 시, 스프링 whitelabel 오류 페이지 적용(프로젝트 처음 만들었을때 실행하면 나오는 화면)
  • server.error.path=/error: 오류 페이지 경로, 스프링이 자동 등록하는 서블릿 글로벌 오류 페이지 경로와 BasicErrorConroller 오류 컨트롤러 경로에 함께 사용됨 - 보통 기본값을 사용해서 건들일이 거의없음

(5) 확장 포인트

  • 에러 공통 처리 컨트롤러의 기능을 변경하고 싶으면 ErrorController 인터페이스를 상속받아서 구현하거나 BasicErrorController 상속 받아서 기능을 추가하면 됨
  • protected 붙어있는 메서드들을 등록하면 되는데 크게 등록할 일이 많이 없어 사용할 일이 있으면 검색

(6) 정리

  • 개발 서버라면 몰라도 실무에서는 이런 에러정보들을 절대 노출하면 안됨 - 보안 위험
  • 이쁜 오류 화면과 고객이 이해할 수 있는 간단한 오류 메시지를 보여주고 오류 정보는 서버에 로그로 남겨서 로그로 확인해야함
  • 스프링 부트가 기본으로 제공하는 오류 페이지를 활용하면 오류 페이지와 관련된 대부분의 문제는 쉽게 해결할 수 있음