인터셉터란?
웹 요청(request)과 응답(response)의 흐름을 가로채서 특정 로직을 수행하는 역할을 한다.
사용 목적 :보통 로그인 체크, 권한 검사, 실행 시간 측정, 로깅, 공통 설정 등에 사용된다!
필터와 비슷해보이지만, 인터셉터는 bean을 다룰 수 있다.
동작원리..는 아래의 파일들이 유기적으로 연결되어 어떤 일을 하는지 참고해보자..
여기서는 어떤 인터셉터를 사용할지, 어떤 핸들러 메소드(컨트롤러 중..)를 대상으로 하거나
제외할지 등의 기본적인 웹 설정을 담당한다.
@Configuration
public class WebConfiguration implements WebMvcConfigurer {
private StopwatchInterceptor stopwatchInterceptor;
@Autowired
public WebConfiguration(StopwatchInterceptor stopwatchInterceptor) {
this.stopwatchInterceptor = stopwatchInterceptor;
}
Spring의 WebMvcConfigurer 인터페이스를 구현하여 웹을 설정한다.
@Configuration : 이 클래스가 설정 파일임을 의미.
WebMvcConfigurer : Spring MVC 관련 설정을 제공하는 인터페이스.
StopwatchInterceptor라는 인터셉터 클래스를 @Autowired로 주입받아 사용
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(stopwatchInterceptor)
.excludePathPatterns("/css/**");
}
addInterceptors 메소드를 통해 아까 주입받은 인터셉터(stopwatchInterceptor)를 추가하고, excludePathPatterns를 통해 특정경로는 제외해준다.
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/css/**")
.addResourceLocations("classpath:static/css/")
.setCachePeriod(100);
}
}
정적 리소스인 css파일의 위치를 classpath:static/css/로 설정하고 이를 setCachePeriod() 메소드를 이용하여 브라우저가 100초 동안 캐시를 유지하도록 설정.
사용자의 요청을 처리하는 컨트롤러 클래스!
@Controller
public class InterceptorTestController {
@GetMapping("stopwatch")
public String handlerMethod() throws InterruptedException {
System.out.println("핸들러 메소드 호출함...");
Thread.sleep(1000);
return "result";
}
}
@Controller : Spring MVC의 컨트롤러로 사용.
@GetMapping("stopwatch") : stopwatch 경로로 GET 요청이 오면 핸들러 메서드가 실행되는데,
Thread.sleep(1000): 으로 1초 후에 result 뷰를 반환하게 된다.
하지만 스레드가 강제로 중단(interrupt)되면 InterruptedException이 발생할 수 있어.
따라서 throws InterruptedException을 선언하여 이 예외가 발생할 가능성이 있음을 컴파일러에 알려줌.
비즈니스 로직을 처리하는 서비스 클래스!
@Service
public class InterceptorTestService {
public void test() {
System.out.println("서비스 bean 동작 확인");
}
}
이 서비스 클래스는 뒤에 나올 인터셉터 클래스에서 의존성 주입받아서 호출될 용도로 만들어짐
핸들러 실행 전후에 특정 로직을 실행하느 인터셉터 클래스!
@Component
public class StopwatchInterceptor implements HandlerInterceptor {
InterceptorTestService testService;
@Autowired
public StopwatchInterceptor(InterceptorTestService testService) {
this.testService = testService;
}
@Component : 스프링 빈으로 등록.
HandlerInterceptor 인터페이스를 구현하여 인터셉터 역할 수행.
@Autowired : 자동으로 InterceptorTestService를 주입받음.
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle 호출함...(핸들러 메소드 이전)");
/* 설명. Service bean에 있는 메소드 호출 */
testService.test();
/* 설명. requestHeader에서 지역 추출 */
Locale locale = request.getLocale();
System.out.println("locale = " + locale);
if("ko_KR".equals(locale.toString())) System.out.println("한국인이시군요");
long startTime = System.currentTimeMillis();
request.setAttribute("startTime", startTime);
return true;
}
prehandle : 핸들러 실행 전에 동작.
testService.test() : 주입받은 서비스 클래스의 서비스 로직 호출.
request.getLocale() : 요청한 사용자의 지역(Locale) 확인
request.setAttribute("startTime", startTime) : 요청 시작 시간 저장.
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle 호출함...(핸들러 메소드 이후)");
long startTime = (Long)request.getAttribute("startTime");
long endTime = System.currentTimeMillis();
modelAndView.addObject("interval", endTime - startTime);
}
핸들러 실행 후(뷰 렌더링 전에) 동작.
startTime을 가져와 요청 처리 시간을 계산 후 ModelAndView에 추가.
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
뷰 렌더링까지 완료된 후 실행.
처음에 설정했던 WebConfiguration 클래스에서 이미 설정했다!!
WebConfiguration에서 설정된 인터셉터의 대상 경로를 보면 확인할 수 있다.
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(stopwatchInterceptor)
.excludePathPatterns("/css/**"); // 정적 리소스(/css/)는 제외
}
stopwatchInterceptor는 모든 요청에 대해 인터셉트하지만,
컨트롤러말고 "/css/**" 경로는 제외됨.
따라서, 현재는 모든 컨트롤러가 기본적으로 인터셉트 대상이다!
특정 컨트롤러만 인터셉터 적용하려면 위
.addPathPatterns("/stopwatch") // "/stopwatch" 경로만 인터셉트
이렇게 하면 /stopwatch 요청을 처리하는 InterceptorTestController만 인터셉트됨
addPathPatterns()과 excludePathPatterns()를 사용하면 특정 컨트롤러만 인터셉트 가능.