이전 포스트에서 서블릿 예외 처리를 구현했습니다. (저번 포스트에서 코드 그대로 활용합니다.)
저번 포스트에서 마지막에 동작 과정을 보면 아래와 같습니다.
1. WebServerCustomizer <- WAS(sendError 호출 기록 확인) <- 필터 <- 서블릿 <- 인터셉터 <- ServletExController(sendError 호출)
2. WAS (/error-page/500 다시 요청) -> 필터 -> 서블릿 -> 인터셉터 -> 컨트롤러(/error-page/500) -> 타임리프 View(클라이언트에게 보여주는 페이지)
오류가 발생하면 오류 페이지를 출력하기 위해서 2번과 같이 WAS내부에서 호출이 다시 한번 발생하고, 그 과정에서 필터, 인터셉터등이 모두 호출됩니다.
로그인 인증 체크 같은 경우, 이미 한번 필터나 인터셉터에서 로그인 체크를 완료한 상태이기 때문에, 서버 내부에서 오류 페이지를 호출하면서 똑같은 필터나 인터셉터가 한번 더 호출되는 것은 비효율적입니다.
결국 클라이언트로 부터 발생한 정상 요청인지, 아니면 오류 페이지를 출력하기 위해서 내부 요청인지 구분해야 다시 호출되는 것을 막을 수 있습니다.
서블릿은 이런 문제를 해결하기 위해 DispatcherType 이라는 추가 정보를 제공합니다.
DispatcherType
DispatcherType은 아래와 같습니다. 고객이 처음 요청하면 dispatcherType=REQUEST 이고, 오류 페이지 호출을 위해 내부에서 호출하면 dispatcherType=ERROR입니다.
public enum DispatcherType {
FORWARD,
INCLUDE,
REQUEST,
ASYNC,
ERROR
}
LogFilter에 DispatcherType 추가
코드는 이전 서플릿 필터 포스트에 코드를 가져와서 수정하겠습니다. (logfilter, webconfig)
filter/LogFilter
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) {
log.info("EXCEPTION {}", e.getMessage());
throw e;
} finally {
log.info("RESPONSE [{}][{}][{}]", uuid, request.getDispatcherType(), requestURI);
}
}
@Override
public void destroy() {
log.info("log filter destroy");
}
}
아래와 같이 DispatcherType도 같이 로그에 나오도록 수정했습니다.
log.info("REQUEST [{}][{}]", uuid, requestURI);
-->
log.info("RESPONSE [{}][{}][{}]", uuid, request.getDispatcherType(), requestURI);
실행 결과
로그에 DispatcherType이 같이 필터를 통해 출력되는 것이 보입니다.
저희 목표는 ERROR 요청에는 필터가 실행되지 않도록 하는것입니다.
WebConfig에 DispatcherType 추가
Logfilter를 적용시켜주는 파일 WebConfig에서 filterRegistrationBean.setDispatcherTypes(DispatcherType.REQUEST);를 추가해서 오류 호출이 아닌 사용자 호출에만 로그 필터를 적용하도록 할 수 있습니다.
WebConfig
package hello.exception;
import hello.exception.filter.LogFilter;
import hello.exception.interceptor.LogInterceptor;
import hello.exception.resolver.MyHandlerExceptionResolver;
import hello.exception.resolver.UserHandlerExceptionResolver;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import javax.servlet.DispatcherType;
import javax.servlet.Filter;
import java.util.List;
@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/**");//오류 페이지 경로
// }
@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("/*");
// REQUEST과 ERROR일때 필터 적용
filterRegistrationBean.setDispatcherTypes(DispatcherType.REQUEST);
return filterRegistrationBean;
}
}
실행결과
이전과 다르게 ERROR 요청에 필터가 적용이 안되고, REQUEST요청에만 필터가 적용되는것을 볼 수 있습니다.
'Back-End > 🌱 Spring Boot (java)' 카테고리의 다른 글
[Spring] 스프링 MVC 예외 처리 시 인터셉터 재호출 해결법 (0) | 2024.08.09 |
---|---|
[Spring] 서블릿 예외 처리와 오류 페이지 (0) | 2024.07.28 |
[Spring] 서블릿 인터셉터 개념과 로그인(인증) 필터 구현 (0) | 2023.05.21 |
[Spring] 서블릿 필터 개념과 로그인 필터 구현 (0) | 2023.05.06 |
[Spring] HTTP 세션의 개념과 로그인 구현 (0) | 2023.05.06 |
댓글