인터셉터

 

서블릿 필터에 이어 웹과 관련된 공통 관심사를 해결할 수 있는 방법으로 스프링 MVC가 제공하는 인터셉터가 있다.

인터셉터는 컨트롤러 호출 직전에 호출이 되며 필터처럼 체인으로 구성이 되는 등 비슷한 부분이 많지만 필터에 비해 좀 더 편리하고 정교한 기능을 지원한다.

 

 

 

먼저, 인터셉터를 사용하려면. HandlerIntercepter 인터페이스를 구현해야 한다.

  • preHandle : 컨트롤러 호출 전 동작
  • postHandle : 컨트롤러 호출 후 동작, 컨트롤러 예외 발생 시 호출되지 않는다.
  • afterCompletion : 마지막에 항상 호출, 예외를 받아서 처리할 수 있다.

DispatcherServlet -> preHandle -> handle() -> Controller -> postHandle -> render() -> View -> afterCompletion

 

public class LogInterceptor implements HandlerInterceptor {
    public static final String LOG_ID = "logId";

	@Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
    	Object handler) throws Exception {

        HttpSession session = request.getSession(false);

        //@Controller, @RequestMapping으로 핸들러 매핑을 할 경우, 핸들러 정보로 HandlerMethod가 넘어온다.
        if (handler instanceof HandlerMethod) {
            //호출할 컨트롤러 메서드의 모든 정보가 포함되어 있다.
            HandlerMethod hm = (HandlerMethod) handler;
        }
       
      	if (session == null || session.getAttribute("loginMember") == null) {
            redirect response.sendRedirect("/login?redirectURL=" + requestURI);
            return false;
        }
        return true;
    }

	@Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response,
    	Object handler, ModelAndView modelAndView) throws Exception {
        //ModelAndView를 받을 수 있다.
    }

	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
    	Object handler, Exception ex) throws Exception {
      	// Exception을 받아서 처리하거나 넘길 수 있다.
        
	}
}

 

인터셉터는 WebMvcConfigurer가 제공하는 addInterceptors() 를 사용해서 등록할 수 있다.

 

@Configuration
public class WebConfig implements WebMvcConfigurer {

	@Override
	public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(new LogInterceptor())
                .order(1) // 필터와 마찬가지로 호출 우선 순위
                .addPathPatterns("/**") // URL 패턴
                .excludePathPatterns("/css/**", "/*.ico", "/error"); // 인터셉터 제외 패턴
            
            // 2번째 인터셉터 등록            
            registry.addInterceptor(new LoginCheckInterceptor())
                .order(2)
                .addPathPatterns("/**")
              	.excludePathPatterns("/", "/members/add", "/login", "/logout",
                	"/css/**", "/*.ico", "/error");
    }
}

 

필터보다 URL 패턴을 더욱 세밀하게 설정할 수 있다.

(https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/util/pattern/PathPattern.html)

 


 

ArgumentResolver를 활용한 로그인 회원 조회

 

직접 만든 ArgumentResolver를 추가해서 로그인 회원을 편리하게 조회할 수 있다.

ArgumentResolver는 핸들러 어댑터에서 핸들러(컨트롤러)를 호출할 때 컨트롤러의 파라미터, 애노테이션 정보를 기반으로 전달 데이터를 생성하는 역할을 한다.

(ArgumentResolver는 요청 값을 처리하고 응답 값을 변환하고 처리하는 것은 ReturnValueHandler가 한다.)

 

@Login 애노테이션을 새로 만들어서 컨트롤러 파라미터에 입력하고 직접 만든 ArgumentResolver에서 이를 이용할 수 있다.

 

//@GetMapping을 제거하고 @Login 애노테이션을 선언
//@GetMapping("/")
public String homeLogin(@Login Member loginMember, Model model) {
	...
}

//@Login 애노테이션 생성
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface Login {

}

 

스프링은 30개가 넘는 ArgumentResovler를 기본으로 제공하는데 여기에 직접 만든 ArgumentResolver를 추가할 수 있다.

HandlerMethodArgumentResolver의 supportsParameter()를 호출해서 해당 파라미터를 지원하는지 체크하고, 지원하면 resolveArgument()를 호출해서 실제 객체를 생성하고 컨트롤러 호출 시 전달할 수 있다.

 

public class LoginMemberArgumentResolver implements HandlerMethodArgumentResolver {

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        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 {
		
        HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();
        HttpSession session = request.getSession(false);
		
        if (session == null) {
            return null;
        }
        // 컨트롤러 파라미터에 선언된 @Login Member loginMember에 member 객체를 전달
        return session.getAttribute("loginMember");
    }
}

 

생성한 ArgumentResolver는 다음과 같이 등록할 수 있다.

 

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        resolvers.add(new LoginMemberArgumentResolver());
    }
    ...
}

+ Recent posts