[Spring] 필터, 인터셉터
in Spring on Spring
서블릿 필터
공통의 관심사는
AOP에서 처리하게 되어 있다. 하지만HTTP의 헤더나URL의 정보들이 필요할때는,서블릿 필터나 스프링인터셉터의HttpServletRequest를 활용한다.
필터의 흐름
HTTP 요청 -> WAS -> 필터 -> 서블릿(디스패처 서블릿) -> 컨트롤러
필터는 체인으로 구성 가능, 중간에 자유롭게 추가가 가능하다.
HTTP 요청 -> WAS -> 필터1 -> 필터2 -> 필터3 -> 서블릿 -> 컨트롤러
필터 인터페이스
public interface Filter {
public default void init(FilterConfig filterConfig) throws ServletException {}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException;
public default void destroy() {}
}
서블릿 컨테이너가 싱글톤 객체로 생성하고, 관리한다.
init():필터 초기화 메서드, 서블릿 컨테이너가 생성될 때 호출된다.doFilter():고객의 요청이 올 때 마다 해당 메서드가 호출된다. 필터의 로직을 구현하면 된다.destroy():필터 종료 메서드, 서블릿 컨테이너가 종료될 때 호출된다.
필터 설정
@Configuration
public class WebConfig {
@Bean
public FilterRegistrationBean logFilter() {
FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>();
filterRegistrationBean.setFilter(new LogFilter()); // 등록할 필터를 지정
filterRegistrationBean.setOrder(1); // 순서
filterRegistrationBean.addUrlPatterns("/*"); // URL 패턴
return filterRegistrationBean;
}
}
인터셉터
스프링 MVC가 제공하는 기술 필터와 같이 웹과 관련 된 공통 관심 사항을 처리, 하지만 필터보다 편리하고, 정교하고 다양한 기능 지원 한다.
흐름
HTTP 요청 -> WAS -> 필터 -> 서블릿 -> 스프링 인터셉터 -> 컨트롤러
인터셉터도 마찬가지로 체인으로 관리 가능
HTTP 요청 -> WAS -> 필터 -> 서블릿 -> 인터셉터1 -> 인터셉터2 -> 컨트롤러
인터페이스
public interface HandlerInterceptor {
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {}
default void postHandle(HttpServletRequest request, HttpServletResponse response,
Object handler, @Nullable ModelAndView modelAndView) throws Exception {}
default void afterCompletion(HttpServletRequest request, HttpServletResponse
response, Object handler, @Nullable Exception ex) throws Exception {}
}
preHandle: 컨트롤러 호출 전에 호출 (true이면 다음으로 진행하고,false이면 더는 진행하지 않는다.)postHandle: 컨트롤러 호출 후에 호출afterCompletion: 뷰가 렌더링 된 이후에 호출

예외 발생시
preHandle: 컨트롤러 호출 전에 호출된다.postHandle: 컨트롤러에서 예외가 발생하면postHandle은 호출되지 않는다.afterCompletion:afterCompletion은 항상 호출된다. 이 경우 예외(ex)를 파라미터로 받아서 어떤 예외가 발생했는지 로그로 출력할 수 있다.
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LogInterceptor()) // 인터셉터를 등록
.order(1) // 순서
.addPathPatterns("/**") // URL 패턴
.excludePathPatterns("/css/**", "/*.ico", "/error"); // 제외할 패턴
}
//...
}
스프링 URL 경로
서블릿 기술이 제공하는 URL 경로와 완전히 다르다. 더욱 자세하고 세밀하게 가능 하다.
? 한 문자 일치 * 경로(/) 안에서 0개 이상의 문자 일치 ** 경로 끝까지 0개 이상의 경로(/) 일치 {spring} 경로(/)와 일치하고 spring이라는 변수로 캡처 {spring:[a-z]+} matches the regexp [a-z]+ as a path variable named "spring" {spring:[a-z]+} regexp [a-z]+ 와 일치하고, "spring" 경로 변수로 캡처 {*spring} 경로가 끝날 때 까지 0개 이상의 경로(/)와 일치하고 spring이라는 변수로 캡처
인터셉터가 더 편하므로, 특별한 이유 없으면 인터셉터 사용을 권장 한다.
ArgumentResolver 활용
Controller
@GetMapping("/")
public String homeLoginV3ArgumentResolver(@Login Member loginMember, Model model) {
//세션에 회원 데이터가 없으면 home
if (loginMember == null) {
return "home";
}
//세션이 유지되면 로그인으로 이동
model.addAttribute("member", loginMember);
return "loginHome";
}
@Login 애노테이션 생성
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface Login {
}
LoginMemberArgumentResolver 생성
@Slf4j
public class LoginMemberArgumentResolver implements
HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
log.info("supportsParameter 실행");
boolean hasLoginAnnotation = parameter.hasParameterAnnotation(Login.class);
boolean hasMemberType = Member.class.isAssignableFrom(parameter.getParameterType());
return hasLoginAnnotation && hasMemberType;
}
@Override
public Object resolveArgument(MethodParameter parameter,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws Exception {
log.info("resolveArgument 실행");
HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();
HttpSession session = request.getSession(false);
if (session == null) {
return null;
}
return session.getAttribute(SessionConst.LOGIN_MEMBER);
}
}
WebMvcConfigurer에 설정 추가
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(new LoginMemberArgumentResolver());
}
//...
}