1. 인터셉터의 예외 처리
인터셉터의 예외 처리 방식과 설정 방법을 알아본다.
LogInterceptor 작성
이전과 마찬가지로 LogInterceptor를 작성한다. 다만, 필터를 학습할 때와 마찬가지로 request.getDispatcherType()을 로그로 남기도록 한다.
java/hello/exception/interceptor/LogInterceptor.java
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
32
|
@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);
}
}
}
|
cs |
빈 등록
빈으로 등록한다. 혼선을 막기 위해서 logFilter 빈은 주석처리하고, addInterceptors 메서드를 [Ctrl + O] 단축키를 통해서 Override 해온다.
인터셉터의 옵션 설정은 이전에 배운바와 같다. 다만, 여기서는 필터에서처럼 DispatcherTypes을 따로 add 해주는 것이 아니라, excludePathPatterns에 인터셉터를 거치지 않도록 하고 싶은 URL 패턴을 입력해주면 된다. 이 부분도 필터보다 편한 것 같다. 아무튼, /error-page 이하 모든 url을 excludePathPatterns로 등록해서 에러 발생 시 인터셉터를 거치지 않도록 해주면 된다.
java/hello/exception/WebConfig.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
@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;
}
}
|
cs |
처리 방식 이해하기
전체 흐름을 이해하는 것이 중요하다. excludePathPatterns에 등록했을 때는 아래와 같다.
WAS <- 필터 <- 서블릿 <- 인터셉터 <- 컨트롤러(에러 발생)
등록이 됬을 때는, 인터셉터를 제외하게 되므로, 1차적으로 컨트롤러를 거쳐서 발생된 에러가 서블릿을 거쳐 preHandle에서 REQUEST를 호출한다. 그러나 에러가 발생한 상황이므로 postHandle은 호출되지 않고 afterCompletion만 호출되어 RESPONSE가 출력되는 것을 확인할 수 있다.
WAS -> 필터 (X)-> 서블릿 -> 인터셉터(X) -> 컨트롤러 -> View(error-page)
반대로 서버 내부 요청이 올라갈때는 서블릿을 통해 에러만 호출되어 dispatcherServlet이 호출된 후 에러가 표현되었고, 이후 errorPage 500의 View가 호출된 것을 확인할 수 있다.
참고로, excludePathPatterns에 /error-page/** 부분을 넣지 않으면, WAS <- ... <- 컨트롤러로 가는 부분에서는 에러가 발생한 상황이므로 preHandle이 호출되지 않지만, WAS -> ... -> 컨트롤러로 가는 상황에서는 preHandle이 호출된다.
2. 스프링 부트 에러 페이지
스프링 부트의 기본 제공 에러페이지
스프링부트에서는 이때까지 직접 처리했던 WebServerCustomizer를 만들고, 예외 종류에 따라 ErrorPage를 추가하고, ErrorPageController를 만드는 과정 전체를 자동으로 해준다. 사용자가 할 것은 HttpStatusCode에 맞는 View 페이지만 구성해주면 된다.
스프링부트는 BasicErrorController라는 스프링 컨트롤러를 자동으로 등록하는 방식으로 각 에러들을 View 템플릿으로 이동시켜준다.
View 템플릿 추가
예를 들어, HttpStatusCode가 400대라면, resources/templates/error/4xx.html 이라는 경로의 View 템플릿 파일을 추가하면 된다. 이렇게 하면 자동으로 400대 에러 발생 시 해당 View 템플릿으로 이동한다. 보다 구체적으로 401.html 파일등을 작성하여 세밀하게 에러 페이지를 조정할 수도 있다.
다만, 이전까지 사용해왔던 WebServerCustomizer 파일은 @Component 부분을 주석처리하여 스프링부트의 기본 에러 페이지 제공 기능이 작동하도록 해주어야만 한다.
WhiteLabelErrorPage
그 동안 자주 봐왔던 WhiteLabelErrorPage의 비밀도 여기에 숨어있다. 스프링 부트는 위에서 설명한 4xx.html View 템플릿과 같이 발생한 에러와 일치하는 View 템플릿이 존재하지 않으면 기본적으로 WhiteLabelErrorPage를 보여주게 된다.
참고 : 에러 정보 추가
아래와 같이 에러 정보를 추가할 수 있다. 그러나 굳이 사용자에게 에러 정보를 보여줄 필요도 없고, 보여주면 오히려 혼란이 가중되고 해킹 등 보안에 취약해질 수 있다. 사용하는 라이브러리 등이 공개되버리면, 해커들은 해당 라이브러리의 취약점을 통해 서버를 공격할 수도 있다고 한다. 아래 사항은 참고로만 알아두자.
예를 들어서, 500.html 페이지에 아래 th:text로 구성된 부분을 추가하면 500 에러 시 추가 정보를 볼 수 있게 된다.
resources/templates/error/500.html
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
32
|
<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>
|
cs |
설정 변경
각 값들이 null 이 뜬 것은 상기 언급한 혼선 및 보안의 이유 때문이다. 굳이 표시하고 싶다면, 아래 설정을 바꿔주면 된다. always 부분은 never 또는 on-param으로 바꿀 수 있다. never는 출력을 하지 않겠다는 것이고, on-param은 url 상에 해당 파라미터가 존재하면 출력하겠다는 의미가 된다. 예를 들어, 아래의 message, stacktrace 부분을 on-param으로 설정을 바꾼 뒤, localhost:9090/error-ex?message=&stacktract= 이라는 url을 호출하면 message, stacktrace 부분은 표시된다.
application.properties
server.error.whitelabel.enabled=false
server.error.include-exception=true
server.error.include-message=always
server.error.include-stacktrace=always
server.error.include-binding-errors=always
참조
1. 인프런_스프링 MVC 2편 - 백엔드 웹개발 핵심 기술_김영한 님 강의
https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-2
'Programming-[Backend] > Spring' 카테고리의 다른 글
[스프링 웹MVC-2] 17. API 예외 처리 - 스프링 ExceptionResolver (0) | 2021.09.13 |
---|---|
[스프링 웹MVC-2] 16. API 예외 처리 - 스프링부트 기본 오류 처리, HandlerExceptionResolver (0) | 2021.09.09 |
[스프링 웹MVC-2] 14. 예외 - 서블릿 오류 페이지 (0) | 2021.09.06 |
[스프링 웹MVC-2] 13. 로그인 - 스프링 인터셉터, ArgumentResolver 활용 (0) | 2021.09.05 |
[스프링 웹MVC-2] 12. 로그인 - 서블릿 필터 (0) | 2021.09.01 |