필터, 인터셉터

김남연·2025년 9월 14일

SpringMVC

목록 보기
8/8

로그인하지 않은 사용자가 로그인멤버 전용 페이지(ex 관리자 전용 상품관리 페이지)에 접근하면 안되기 때문에 이를 제어할 수 있어야한다.
컨트롤러마다 로그인 체크 로직을 작성하면 되지만 유지보수에서 큰 문제가 생길 수 있다
이때 스프링 필터, 인터셉터를 사용한다

Servlet Filter
HTTP 요청 -> WAS -> 필터 -> 서블릿 -> 컨트롤러
적절하지않은 요청인 경우 (비로그인) 필터 이후 서블릿을 호출하지않는다

HTTP 요청 -> WAS -> 필터1 -> 필터2 -> 필터3...
필터는 체인으로 구성되어 로그를 남기고 로그인을 체크하는 등의 구성이 가능하다

스프링 인터셉터
HTTP 요청 -> WAS -> 서블릿 -> 스프링 인터셉터 -> 컨트롤러
스프링 인터셉터는 디스패처 서블릿과 컨트롤러 사이에서 컨트롤러 호출 직전에 호출된다
적절하지않은 요청인 경우 컨트롤러를 호출하지 않는다
필터와 같이 체인으로 구성된다

스프링 인터셉터 인터페이스
스프링 인터셉터를 사용하려면 HandlerInterceptor 인터페이스를 구현하면 된다

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() → Controller → postHandle() → View 렌더링 → afterCompletion()

prehandle
컨트롤러 실행 직전에 호출된다

prehandle 응답이 true이면 다음으로 진행하고 false이면 진행하지 않는다
false인 경우 인터셉터는 물론 컨트롤러도 호출되지 않는다

컨트롤러 진입 전 인증/권한

보통 로그인 체크 같은 걸 여기서 확인

postHandle
컨트롤러 실행 후 뷰 렌더링 전 호출

컨트롤러에서 예외가 발생하면 호출되지 않음

정상 결과에 따른 처리

afterCompletion
뷰가 렌더링 된 이후에 호출된다

예외 발생 여부와 상관없이 항상 호출

리소스정리, 로그 출력 같은 것을 처리

컨트롤러에서 예외가 발생하면 예외(ex)를 파라미터로 받아서 어떤 예외가 발생했는지 로그로 출력할 수 있다

@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);
 
 		//@RequestMapping: HandlerMethod
 		//정적 리소스: ResourceHttpRequestHandler
 		if (handler instanceof HandlerMethod) {
 			HandlerMethod hm = (HandlerMethod) handler; //호출할 컨트롤러 메서드의 모든 정보가 포함되어 있다.
 		}
 
 		log.info("REQUEST [{}][{}][{}]", uuid, requestURI, handler);
 		return true; //false 진행X
 		}
 
 		@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, requestURI);
 			if (ex != null) {
 				log.error("afterCompletion error!!", ex);
 			}
 		}
}
@Slf4j
public class LoginCheckInterceptor implements HandlerInterceptor {
 	@Override
 	public boolean preHandle(HttpServletRequest request, HttpServletResponse
response, Object handler) throws Exception {
 		
        String requestURI = request.getRequestURI();
 		
        log.info("인증 체크 인터셉터 실행 {}", requestURI);
 		
        HttpSession session = request.getSession(false);
 		
        if (session == null || session.getAttribute(SessionConst.LOGIN_MEMBER)
== null) {
 			log.info("미인증 사용자 요청");
 			//로그인으로 redirect
 			response.sendRedirect("/login?redirectURL=" + requestURI);
 			return false;
     	}
 		
        return true;
 	}
}

인증은 컨트롤러 호출 전에만 호출되면 되기 때문에 preHandle만 구현하면 된다

인터셉터 등록

@Configuration
	public class WebConfig implements WebMvcConfigurer {
 	
    @Override
 	public void addInterceptors(InterceptorRegistry registry) {
 		registry.addInterceptor(new LogInterceptor())
 				.order(1)
 				.addPathPatterns("/**")
 				.excludePathPatterns("/css/**", "/*.ico", "/error");
 
 		registry.addInterceptor(new LoginCheckInterceptor())
 				.order(2)
 				.addPathPatterns("/**")
 				.excludePathPatterns(
 					"/", "/members/add", "/login", "/logout",
 					"/css/**", "/*.ico", "/error"
 				);
 	}
 	//...
}

0개의 댓글