1. Filter
- 공통적인 요청 전처리와 응답 후처리에 사용. 로깅, 인코딩 등

서블릿이 있을 때 공통적으로 똑같이 전처리 작업, 후처리 작업이 들어간다면 이는 '중복'이다.
이렇게 중복 코드들이 여러 서블릿에 공통적으로 들어갈 때 중복을 제거해야한다.
-> 어떻게 제거?
서블릿들 앞에 Filter를 두면 된다! (서블릿은 컨트롤러라고 생각하기)
Filter가 발전한게 Interceptor이듯이 서블릿이 발전한게 컨트롤러이다.
요청이 왔을 때 요청을 필터가 먼저 받아서 전처리를 수행하고 서블릿을 호출한다.
(3개 중 두 번째 서블릿으로 간다고 가정) 호출한 서블릿으로 요청이 가고 해당 서블릿이 처리를 하고 처리가 끝나면
다시 돌아와서 후처리 작업을 수행하고 응답하게 된다. (DispatcherServlet이랑 유사하다)
- 보통은 필터가 1개이지만 2개 이상의 필터를 사용할 수도 있다. (필터가 여러 개일때)

* 처리 과정
클라이언트 요청이 오면 앞에 있는 필터 Filter1이 먼저 받고 전처리를 한다. 어떤 필터일지는 모르지만 그 다음 연결된
필터를 호출하면 다음 필터 Filter2의 전처리가 호출이 된다.
그 다음에 다음 필터 혹은 서블릿을 호출한다. (다음에 연결된 필터가 없다면 서블릿 호출)
그러면 서블릿이 처리를 한 다음 돌아와서 Filter2에서 후처리를 하고 다시 그 앞으로 돌아와서 Filter1에서 후처리를 한 후
응답을 하게 된다.
서블릿에서 응답을 하는건데 응답을 하면서도 2번의 후처리를 겪게 된다.
1) filter1 전처리
2) filter2 전처리
3) 서블릿 처리
4) filter2 후처리
5) filter3 후처리
* 아래 예제에서는 3개의 관심사가 존재한다.

하나의 메소드에 여러 개의 관심사가 존재하므로 분리가 필요하다.
urlPatterns = /* : 필터의 적용대상 지정(*찍으면 모든 요청에 필터 적용)
여러 서블릿(컨트롤러)에 공통 코드를 추가하여 분리해준다.

<실습>
- PerformanceFilter 클래스 생성
package com.fastcampus.ch2;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
@WebFilter(urlPatterns = "/*") // 모든 요청에 PerformanceFilter 적용.
public class PerformanceFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
// 1. 전처리 작업
long startTime = System.currentTimeMillis();
// 2. 서블릿(컨트롤러)또는 다음 필터
filterChain.doFilter(request, response);
// 3. 후처리 작업
long endTime = System.currentTimeMillis();
System.out.print("["+ ((HttpServletRequest)request).getRequestURI() + "]"); // 어떤걸 호출했을 때 소요시간이 얼마인지 알도록 함
System.out.println("time=" + (endTime-startTime));
}
}
@WebFilter(urlPatterns = "/*")
이건 서블릿에서 쓰는 필터이기 때문에 한 가지 작업을 더 해줘야한다.
Ch2Application의 클래스 앞에 @ServletComponentScan을 지정해주어야 한다.

이 어노테이션을 넣어야 PerformanceFilter를 자동으로 스캔해서 등록해줄 수 있다.
이후 실행한 결과를 확인해보자.

루트를 요청했지만 페이지와 링크되어 있는 파일들도 다 요청이 따로 간다.
페이지를 하나 요청한다고 자동으로 같이 오는게 아니라 별개의 개별 요청이고, 개별 요청이 따로 자동으로 가는 것을 알 수 있다.
2. Interceptor
- Filter와 유사한 기능. Filter와 달리 WAC내에 위치. 빈 주입 가능

Filter는 Servlet Context 안에 있는데, Interceptor는 WebApplicationContext 내에 있다.
둘 다 저장소지만 Servlet Context는 Servlet 저장소이고, WebApplicationContext는 Spring에서 사용하는 저장소이다.
거의 Interceptor나 Filter 둘 다 거의 같지만, Interceptor에서는 WebApplicationContext안에 존재하는 Bean(자바 객체)를
다 이용할 수 있다.
하지만, Servlet Context에서는 Bean에 접근할 수 없다.
+ Interceptor가 매개 변수도 더 많다.
그래서 Filter가 발전된 것이 Interceptor라고 볼 수 있다.
Spring에서는 둘 다 써도 되지만, 기본적으로 Interceptor를 더 많이 사용한다.
요청이 오면 DispatcherServlet이 받아서 해당 컨트롤러의 메소드로 가기 전에 Interceptor를 거친다.
전처리 후 Controller 메소드 호출하고 처리한 다음에 다시 돌아와서 후처리한 다음 그 결과를 DispatcherServlet에게
전달을한다.
WebApplicationContext은 Spring 컨테이너라고도 한다.
<실습>
- PerformanceInterceptor 생성
package com.fastcampus.ch2;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
public class PerformanceInterceptor implements HandlerInterceptor { // 단일 책임의 원칙(SRP) - 하나의 메소드는 하나의 책임만 갖는다.
// long startTime; // iv 인스턴스 변수. 싱글톤(하나의 객체)이라서 여러 쓰레드가 하나의 객체를 공유.
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 1. 전처리 작업
long startTime = System.currentTimeMillis();
request.setAttribute("startTime", startTime); // request객체에 startTime을 저장
// handler - 요청하고 연결된 컨트롤러의 메소드
HandlerMethod method = (HandlerMethod) handler;
System.out.println("method.getMethod() = " + method.getMethod()); // URL하고 연결된 메소드
System.out.println("method.getBean() = " + method.getBean()); // URL하고 연결된 메소드
// return true; // 다음 인터셉터나 컨트롤러를 호출 false면 호출 안함.
return HandlerInterceptor.super.preHandle(request, response, handler);
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
// 2. 후처리 작업
long startTime = (long)request.getAttribute("startTime"); // 객체를 저장하기 때문에 long타입 형변환 필요
long endTime = System.currentTimeMillis();
System.out.print("["+ ((HttpServletRequest)request).getRequestURI() + "]"); // 어떤걸 호출했을 때 소요시간이 얼마인지 알도록 함
System.out.println("time=" + (endTime-startTime));
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
}
HandlerInterceptor 인터페이스 구현 후 메소드 구현
preHandle - 전처리 / postHandle - 후처리 메소드를 구현한다.
-> 메소드가 분리된다.
preHandle의 지역 변수를 postHandle로 전달해야 한다. request객체에 저장하면 그 결과가 postHandle까지 전달이 되기 때문에 request객체에 저장해준다.
handler - 요청하고 연결된 컨트롤러의 메소드. handler 매개변수를 통해 메소드에 대한 정보를 얻을 수 있다.
@Component 어노테이션을 이용해서 자동으로 PerformanceInterceptor가 ApplicationContext의 Bean이 되게 하는게
먼저이다.
그 다음 설정 파일 WebMvcConfig 클래스 하나 생성한다.
package com.fastcampus.ch2;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new PerformanceInterceptor()) // interceptor로 등록
.addPathPatterns("/**") // interceptor가 적용될 대상을 정함. -> 모든 대상
.excludePathPatterns("/css/**", "/js/**"); // 제외할 것. css파일이나 js파일 제외
}
}
설정 파일임을 알려주는 @Configuration 어노테이션을 붙여준다음 WebMvcConfigurer인터페이스를 구현한 후
addInterceptors() 메소드를 구현한다.

콘솔창에서 결과를 확인할 수 있는데, [/]time=9는 interceptor가 출력한것이고, 그 아래는 filter가 출력한 것이다.
filter는 2개의 요청을 처리했는데 interceptor는 하나의 요청만 처리한 것은 적용 제외 대상 파일들을 지정해줬기 때문이다.
'SpringBoot' 카테고리의 다른 글
| ch2 15. forward와 redirect - 실습 (0) | 2023.07.14 |
|---|---|
| ch2 14. forward와 redirect - 이론 (0) | 2023.07.14 |
| ch2 12. Thymeleaf로 레이아웃 적용하기 (0) | 2023.07.07 |
| ch2 11. @RequestMapping과 URL인코딩 (0) | 2023.07.07 |
| ch2 10. 로그인화면 만들기 (0) | 2023.07.07 |