Written by
Sunwoo Han
on
on
예외 처리와 오류 페이지
스프링 MVC 2편 - 백엔드 웹 개발 활용 기술을 공부하고 정리하는 포스트입니다.
서블릿 예외 처리
스프링이 아닌 순수 서블릿 컨테이너의 예외 처리
- 2가지 방식을 지원
Exception
(예외)response.sendError(HTTP status code, error message)
Exception 예외
- 자바 직접 실행
- 메인 메서드를 직접 실행하는 경우
main
쓰레드가 실행 - 실행 도중에 예외를 잡지 못하고 처음 실행한
main()
메서드를 넘어서 예외가 던져지면 → 예외정보를 남기고 쓰레드 종료
- 메인 메서드를 직접 실행하는 경우
- 웹 애플리케이션
- 사용자 요청별로 별도의 쓰레드가 할당, 서블릿 컨테이너 안에서 실행
- try ~ catch로 예외를 잡아서 처리하면 → No problem!
- 서블릿 밖까지 예외가 전달되면? → 톰캣 같은 WAS 까지 예외가 전달
WAS ← 필터 ← 서블릿 ← 스프링 인터셉터 ← 컨트롤러(예외 발생)
오류 처리 페이지 흐름
WAS(예외 처리 경로) → 필터 → 서블릿 → 인터셉터 → 컨트롤러(예외 처리 경로)
- 컨트롤러에서 발생한 예외가 WAS까지 전파 → 서버 내부에서 처리할 수 없는 오류가 발생한 것으로 판단 → HTTP 상태 코드 500 반환
- WAS에서 오류 처리 페이지 경로로 다시 수행 → 페이지 경로를 컨트롤러에서 호출하여 오류 페이지 출력
@Slf4j
@Controller
public class ServletExController {
@GetMapping("/error-ex")
public void errorEx() {
throw new RuntimeException("예외 발생!");
}
}
- 리소스가 없는 아무 페이지나 호출하면, 톰캣이 기본적으로 제공하는 404 - Not Found가 출력
response.sendError(HTTP 상태 코드, 오류 메시지)
HttpServletResponse
가 제공하는sendError
라는 메서드를 사용해도 됨
→ 바로 예외가 발생하는 것은 X, 서블릿 컨테이너에게 오류가 발생했다는 점을 전달할 수 있음-
HTTP 상태 코드와 오류 메시지 추가 가능
response.sendError(HTTP 상태 코드)
response.sendError(HTTP 상태 코드, 오류 메시지)
WAS(response.sendError 호출 기록 확인) ← 필터 ← 서블릿 ← 스프링 인터셉터 ← 컨트롤러(response.sndError())
오류 처리 페이지 흐름
WAS(예외 처리 경로) → 필터 → 서블릿 → 인터셉터 → 컨트롤러(예외 처리 경로)
- 컨트롤러에서 예외 발생 →
response.sendError()
로 예외 저장 - 저장된 에러를 WAS에서 확인 → 설정된 오류 코드에 맞춰 예외 처리
sendError(Http 상태 코드, 오류 메시지)
에서 Http 상태 코드로 예외 설정
@Slf4j
@Controller
public class 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, "500 오류!");
}
}
- 설정한 예외 코드에 맞춰 기본 오류 페이지 출력
서블릿 예외처리 - 오류 화면 제공
-
서블릿 컨테이너가 제공하는 기본 예외 처리화면은 불친절함 → 서블릿이 제공하는 오류 처리 기능 사용
-
Exception
이 발생해서 서블릿 밖으로 전달되거나response.sendError()
가 호출되었을 때
→ 각각 상황에 맞춘 오류 처리 기능을 서블릿이 제공
서블릿 오류 페이지 등록
@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");
ErrorPage errorPageEx = new ErrorPage(RuntimeException.class, "/error-page/500");
factory.addErrorPages(errorPage404, errorPage500, errorPageEx);
}
}
-
오류 페이지는 예외를 다룰 때 해당 예외와 그 자식 타입의 오류를 함께 처리
→ 예를 들어RuntimeException
과RuntimeException
의 자식도 함께 처리 -
오류 발생 처리 컨트롤러
@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";
}
}
- 오류 처리할 error-page 디렉터리 내부에
404.html
,500.html
생성
서블릿 예외 처리 - 오류 페이지 작동 원리
-
Exception
이 발생해서 서블릿 밖으로 전달되거나response.sendError()
가 호출되었을 때 설정된 오류페이지 탐색 -
예외 발생 흐름
WAS(여기까지 전파) ← 필터 ← 서블릿 ← 인터셉터 ← 컨트롤러(예외 발생)
- sendError 흐름
WAS(sendError 호출 기록 확인) ← 필터 ← 서블릿 ← 인터셉터 ← 컨트롤러(`response.sendError()`)
- WAS는 해당 예외를 처리하는 오류 페이지 정보를 확인
RuntimeException
예외가 WAS에 전달 → 오류 페이지 정보 확인 →/error-page/500
으로 지정되어 있음 → WAS는/error-page/500
요청
- 오류 페이지 요청 흐름
WAS `/error-page/500` 다시 요청 → 필터 → 서블릿 → 인터셉터 → 컨트롤러(/error-page/500) → View
- 예외 발생과 오류 페이지 요청 흐름
1. WAS(여기까지 전파) ← 필터 ← 서블릿 ← 인터셉터 ← 컨트롤러(예외발생)
2. WAS `/error-page/500` 다시 요청 → 필터 → 서블릿 → 인터셉터 → 컨트롤러(`/errorpage/500`) -> View
웹 브라우저(클라이언트)는 서버 내부에서 이런 일이 발생하는 것을 전혀 모른다는 것이 중요! 오직 서버 내부에서 오류 페이지를 찾기 위해 추가적인 호출을 하는 것!
📌 참고
- WAS는 오류 페이지를 단순히 요청하는 것이 아니라, 오류 정보를 request attribute에 추가해서 넘겨준다.
그러면 오류 페이지에서 이렇게 전달된 오류 정보를 사용할 수 있다.javax.servlet.error.exception
: 예외javax.servlet.error.exception_type
: 예외 타입javax.servlet.error.message
: 오류 메시지javax.servlet.error.request_uri
: 클라이언트 요청 URIjavax.servlet.error.servlet_name
: 오류가 발생한 서블릿 이름javax.servlet.error.status_code
: HTTP 상태 코드
서블릿 예외 처리 - 필터
- 예외 발생과 오류 페이지 요청 흐름
1. WAS(여기까지 전파) ← 필터 ← 서블릿 ← 인터셉터 ← 컨트롤러(예외발생)
2. WAS `/error-page/500` 다시 요청 → 필터 → 서블릿 → 인터셉터 → 컨트롤러(`/errorpage/500`) -> View
- 오류 발생 → 서버 내부에서 URI를 재요청
- 이 때 필터, 서블릿, 인터셉터도 호출 → 매우 비효율적
- 따라서, 정상요청과 내부요청을 구분해야함 → 이를 해결하기 위해 서블릿은
DispatcherType
이라는 정보를 제공
DispatcherType
-
이런 경우를 위해서 필터는
dispatcherTypes
라는 옵션을 제공 -
다양한 DispatcherType이 존재
REQUEST
: 클라이언트 요청ERROR
: 오류 요청FORWARD
: MVC에서 배웠던 서블릿에서 다른 서블릿이나 JSP를 호출할 때
RequestDispatcher.forward(request, response)
;INCLUDE
: 서블릿에서 다른 서블릿이나 JSP의 결과를 포함할 때
RequestDispatcher.include(request, response);ASYNC
: 서블릿 비동기 호출
filterRegistrationBean.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ERROR)
- 두 가지를 모두 넣으면 클라이언트 요청과 오류 페이지 요청 모두에서 필터가 호출됨
- 아무것도 넣지 않으면 기본 값은
DispatcherType.REQUEST
, 클라이언트 요청만 필터가 적용
- 아무것도 넣지 않으면 기본 값은
서블릿 예외 처리 - 인터셉터
- 필터는
DispatcherType
에 따라 필터를 적용할지 선택 가능 - 인터셉터는 서블릿이 아닌 스프링이 제공 → 즉,
DispatcherType
과 무관하게 항상 호출- 대신 요청 경로에 따라 추가하거나 제외하기 쉬움 → 오류페이지 경로를
excludePathPatterns
를 사용해서 변경 가능
- 대신 요청 경로에 따라 추가하거나 제외하기 쉬움 → 오류페이지 경로를
@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/**"); //오류 페이지 경로
}
//@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;
}
}
스프링 부트 오류 페이지 처리
- 스프링 부트를 통해 서블릿 컨테이너 실행 → 스프링 부트에서 제공하는 기능을 사용해서 서블릿 오류 페이지 등록
- ErrorPage 자동 등록 →
/error
경로로 기본 오류페이지 설정 BasicController
라는 스프링 컨트롤러 자동 등록 → ErrorPage에서 등록한/error
를 매핑 후 처리함
- ErrorPage 자동 등록 →
- BasicErrorController의 처리 순서 (우선 순위)
- 뷰 템플릿
resource/templates/error/500.html
resource/templates/error/5xx.html
- 정적 리소스 (static, public)
resource/static/error/400.html
resource/static/error/404.html
resource/static/error/4xx.html
- 적용 대상이 없을 시 뷰 이름 (error)
resource/templates/error.html
- 뷰 템플릿
- 해당 경로 위치에 HTTP 상태 코드 이름의 뷰 파일을 넣으면 끝
<!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>
📌 참고
BasicController
는 오류 정보를 model에 담아 뷰에 전달함
Comments
SPRING 의 다른 글
-
스프링 타입 컨터버 24 Jun 2022
-
API 예외 처리 17 Jun 2022
-
예외 처리와 오류 페이지 12 Jun 2022
-
로그인 처리 - 인터셉터 08 Jun 2022
-
로그인 처리 - 필터 06 Jun 2022
-
로그인 처리 - 쿠키, 세션 31 May 2022
-
Bean Validation 22 May 2022
-
검증 22 May 2022
-
메시지, 국제화 21 May 2022
-
타임리프 - 스프링 통합과 폼 19 May 2022
-
타임리프 - 기본 기능 10 May 2022
-
스프링 MVC 기본 기능 - 웹 페이지 만들기 02 May 2022
-
스프링 MVC 기본 기능 - HTTP 응답 30 Apr 2022
-
스프링 MVC 기본 기능 - HTTP 요청 24 Apr 2022
-
스프링 MVC 기본 기능 - 요청 매핑 19 Apr 2022
-
스프링 MVC 기본 기능 19 Apr 2022
-
스프링 MVC 구조 이해 14 Apr 2022
-
MVC 프레임워크 만들기 - V4, V5 12 Apr 2022
-
MVC 프레임워크 만들기 - V1, V2, V3 09 Apr 2022
-
서블릿, JSP, MVC 패턴 05 Apr 2022
-
서블릿 29 Mar 2022
-
웹 애플리케이션 이해 24 Mar 2022
-
스프링 웹 계층이란? 05 Nov 2021
-
스프링 시큐리티 공식문서 번역 27 Sep 2021
-
스프링 AOP 총정리 : 개념, 프록시 기반 AOP, @AOP 27 Apr 2021
-
SpEL (스프링 Expression Language) 25 Apr 2021
-
데이터 바인딩 추상화 : Converter와 Formatter 21 Apr 2021
-
데이터 바인딩 추상화 : PropertyEditor 12 Apr 2021
-
Validation 추상화 10 Apr 2021
-
Resource 추상화 08 Apr 2021
-
IoC 컨테이너 9부 07 Apr 2021
-
IoC 컨테이너 8부 06 Apr 2021
-
IoC 컨테이너 7부 02 Apr 2021
-
IoC 컨테이너 6부 29 Mar 2021
-
IoC 컨테이너 5부 27 Mar 2021
-
IoC 컨테이너 4부 23 Mar 2021
-
IoC 컨테이너 3부 20 Mar 2021
-
IoC 컨테이너 2부 18 Mar 2021
-
IoC 컨테이너 1부 12 Mar 2021
-
스프링 PSA 07 Jan 2021
-
스프링 @AOP 실습 07 Jan 2021
-
프록시 패턴 06 Jan 2021
-
스프링 AOP 04 Jan 2021
-
의존성 주입(Dependency Injection) 04 Jan 2021
-
스프링 빈(Bean) 02 Jan 2021
-
스프링 IoC 컨테이너 01 Jan 2021
-
스프링 IoC 01 Jan 2021