일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
- n+1
- jpa
- 쿠키의 정의
- abap value in field Data Class error
- .orelseThrow
- 구글 보안 api 활용
- springSecurityFilterChain 오류
- 세션이란
- 유연한 컨트롤러1 - v5
- 세션의 정의
- optional
- @Controller
- application-properties
- 401오류
- Validation
- controller
- SpringMVC
- 김영한
- 세션vs쿠키
- spring MVC
- spring
- filter vs interceptor
- 필터vs인터셉터
- MVC
- BindingResult
- Testcode
- 필터의 정의
- java.lang.AssertionError
- 인터셉터의 정의
- 쿠키란
- Today
- Total
ABAP DUMP ERROR 24시
[Spring MVC] 예외처리 + 오류페이지(AllInOne) -part1 본문
# 인프런 김영한의 스프링 MVC 2편 - 백엔드 웹 개발 핵심 기술을 개인적으로 정리한 글입니다.
정리
Q.순수 서블릿 컨테이너는 예외를 어떻게 처리하나?
1.Exception을 활용한 예외 처리(예외)
2.response.sendError(HTTP 상태 코드, 오류 메시지)
를 활용하여 예외를 처리한다.
Q. 서블릿 컨테이너의 오류 처리방식
자바는
메인 메서드 실행시 main 쓰레드가 실행.
실행 도중에 예외를 잡지 못하고
main() 메소드를 넘어 예외가 던져지면,
예외 정보를 남기고 쓰레드가 종료.
웹 어플리케이션은
사용자 요청별로 쓰레드가 할당이되고, 서블릿 컨테이너 안에서 실행.
App에서 예외가 발생했는데 이 예외를 잡지 못하고 서블릿 밖(WAS까지)으로 까지 예외가 전달이 된다.
어떻게 전달이 되는걸까?
Q. 웹 어플리케이션의 예외가 전달 되는 방법
<Response.sendError 순서정리>
1.오류 발생시 HTTPServletResponse의 response.sendError를 통해 상태코드와, 오류 메세지를 보낸다.
2. WAS가 서블릿의 sendError 호출 기록을 받는다.
3.WAS에서 서블릿의 sendError 호출 기록이 있다면 오류 코드에 맞추어서 오류 페이지를 내보낸다.
1. Exception (예외)
public class ServletExController {
@GetMapping("/error-ex")
public void errorEx(){
throw new RuntimeException("예외 발생");
}
정말 간단하게 Controller에서 throw new RuntimeException("오류메시지")를 사용하여 나타낸다.
Tomcat에서 제공하는 기본 오류 화면을 보여주고 HTTP 상태코드 또한 500으로 보내준다.
Exception의 경우 서버 내부에서 처리할수 없는 오류가 발생한것으로 생각했기 때문이다.
2.response.sendError
<순서정리>
1.오류 발생시 HTTPServletResponse의 response.sendError를 통해 상태코드와, 오류 메세지를 보낸다.
2. WAS가 서블릿의 sendError 호출 기록을 받는다.
3.WAS에서 서블릿의 sendError 호출 기록이 있다면 오류 코드에 맞추어서 오류 페이지를 내보낸다.
오류 발생시 서블릿은
HTTPServletResponse의 response.sendError 활용하여 오류를 해결한다.
response.sendError는 HTTP상태 코드와 오류메세지를 추가할수 있다.
response.sendError(Http상태코드 , 오류 메세지)
response.sendError()를 호출하면 response 내부에 오류가 발생했다는 상태를 저장해둔다.
서블릿 컨테이너는
고객응답전에 response에 sendError가 호출되어있는지를 확인한다.
호출되어 있다면 설정한 오류 코드에 맞추어서 기본 오류 페이지를 내보낸다.
# 서블릿 컨테이너란? =>서블릿을 지원하는 WAS를 서블릿 컨테이너라고 한다.
Q. 필터와 인터셉터의 중복 제거 방법
필터의 경우 서블릿이 제공하는 기능이다.
필터의 경우에는 필터를 등록할 때 DispatcherType을 선정해서 필터를 적용여부를 선택할 수 있었다.
인터셉터는 스프링이 제공하는 기능이다. 따라서 DispatcherType 과 무관하게 항상 호출
인터셉터는 요청 경로에 따라서 추가하거나 제외하기 쉽게 되어 있기 때문에,
이러한 설정을 사용해서 오류 페이지 경로를 excludePathPatterns 를 사용.
살펴보기
1.서블릿 오류 페이지 등록
review>
ErrorPage를 일일히 만들어서 사용하겠다는 전략.
과거에는 .xml을 활용한 정적인 뷰를 만들어서 제공
현제에는 SpringBoot가 제공하는 WebSeverFactoryCustomizer를 활용해서 사용하자.
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);
}
}
2. 해당 404 , 500 오류를 처리할 Controller 제작.
review>
단순히 Request , Response를 활용해서 에러페이지를 제공.
// error-page/500관련된 error-page/500 view를 호출해줘~
public class ErrorPageController {
// static 오류//
@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";
}
3. 흐름도 정리
review>
예를 들어 RunTimeException이 발생했다면
1. WAS는 WebSeverFactoryCustomizer의 ErrorPage를 확인.
2. RuntimeExceptiom.class를 찾아 "/error-page/500"를 확인.
3. "/error-page/500" 가지고 있는 컨트롤러를 호출.
4. @RequestMapping("error-page/500") 메서드를 통해 error-page/500.html을 찾는다.
4. 서블릿의 추가 기능
review>
이때 WAS는 오류 페이지를 단순히 요청하는 것뿐만 아니라 request.getAttribute()에 추가해서 넘겨줄수 있다.
따라서 요류 페이지에서 전달된 오류 정보를 사용할수 있다.
public class ErrorPageController {
// static 오류//
@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";
}
//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"; // 클라이언트 요청 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"; // HTTP 상태 코드
private void printErrorInfo(HttpServletRequest request) {
log.info("ERROR_EXCEPTION: ex=", request.getAttribute(ERROR_EXCEPTION));
log.info("ERROR_EXCEPTION_TYPE: {}", request.getAttribute(ERROR_EXCEPTION_TYPE));
log.info("ERROR_MESSAGE: {}", request.getAttribute(ERROR_MESSAGE)); //ex의 경우 NestedServletException 스프링이 한번 감싸서 반환
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());
}
5. 한계와 해결방법
한계>
review>
WAS는 오류 페이지 경로를 찾아서 내부에서 오류 페이지를 호출한다.
이때 오류 페이지 경로로 필터, 서블릿, 인터셉터, 컨트롤러가 모두 다시 호출된다.
서블릿은 이런 문제를 해결하기 위해 DispatcherType 이라는 추가 정보를 제공
log.info("dispatchType={}", request.getDispatcherType())
해결방법>
1.필터 중복 제거
review>
핵심코드 WebConfig에 filterResgistartionBean.setDispathcerTypes()를 활용한다.
filterRegistrationBean.setDispatcherTypes(DispatcherType.REQUEST,DispatcherType.ERROR);
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
resolvers.add(new MyHandlerExceptionResolver());
resolvers.add(new UserHandlerExceptionResolver());
}
// @Bean
public FilterRegistrationBean logFilter(){
FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>();
filterRegistrationBean.setFilter(new LogFilter());
filterRegistrationBean.setOrder(1);
filterRegistrationBean.addUrlPatterns("/*");
/**
과거 filterRegistrationBean 추가된 부분.
DispatcherType.REQUEST => 클라이언트 요청시 Filter 호출
DispatcherType.ERROR => 오류 페이지 용청시 Filter 호출
동시에 사용했으므로 클라이언트 요청, 오류 페이지 요청에 Filter가 적용된다.
요류 페이지 전용 필터를 적용하고싶으면
filterRegistrationBean.setDispatcherTypes(DispatcherType.ERROR)만 적용하면 된다.
*/
filterRegistrationBean.setDispatcherTypes(DispatcherType.REQUEST,DispatcherType.ERROR);
return filterRegistrationBean;
filterRegistrationBean을 통해서 DispathcerType을 정의해서 클라이언트가 요청할때 제공할지, 오류가 날때만 제공할지 일일히 선택해서 제공이 가능하다.
2. 인터셉터 중복 제거
필터의 경우 서블릿이 제공하는 기능이다.
필터의 경우에는 필터를 등록할 때 DispatcherType을 선정해서 필터를 적용여부를 선택할 수 있었다.
인터셉터는 스프링이 제공하는 기능이다. 따라서 DispatcherType 과 무관하게 항상 호출
인터셉터는 요청 경로에 따라서 추가하거나 제외하기 쉽게 되어 있기 때문에,
이러한 설정을 사용해서 오류 페이지 경로를 excludePathPatterns 를 사용.
즉,
필터는 WebConfig에서 dispatcherType을 제공해서 해결을하고
인터셉터는 WebConfig 에서 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/**");
}
6.중복 제거시 흐름도
'[WEB]Back-end > Spring MVC' 카테고리의 다른 글
[Spring MVC] 서블릿 필터와 인터셉터 (AllInOne) (0) | 2022.04.03 |
---|---|
[Spring MVC] 쿠키와 세션 (AllInOne) (0) | 2022.03.28 |
[유연한 컨트롤러1 - v5] Java code로 Spirng MVC 핵심 기능 만들어 보기 (0) | 2022.03.17 |
AppConfig란? , 사용방법(@bean 사용안하는 version) (0) | 2022.03.08 |
SPRING MVC 가 뭐야? (0) | 2022.03.04 |