728x90

Filter와 Interceptor의 차이로 Dispatcher Servlet의 앞단에서 처리하냐, 뒷단에서 처리하냐의 차이가 존재합니다.
그림을 보시면 아시겠지만 Filter는 Servlet의 앞단에서 처리하며, Interceptor는 뒷단에서 처리합니다.
1. Filter
Filter
Spring boot frameWork 에서 Client로 부터 오는 요청/응답 에 대해서 최초/최종 단계에 위치,
이를통해 요청/응답의 정보를 변경하거나 데이터가 변환되기 전의 순수한 Client
요청값을 확일 할 수 있다.
- 유일하게 ServletRequest , ServletResponse의 객체를 변환 할 수 있다.
- logging 용도 , 인증 로직을 Filter 에서 처리한다.
- import javax.servlet.*;

Dto
________________________________________________________
// @Getter
// @Setter
@Data // getter , setter , toString.. 등
@NoArgsConstructor // 기본생성자
@AllArgsConstructor // 전체 생성자
public class User {
private String name;
private int age;
}
Controller
________________________________________________________
@Slf4j // 로깅처리
@RestController
@RequestMapping("/api/user")
public class ApiController {
@PostMapping("")
public User user(@RequestBody User user){
log.info("User : {} , {}" , user , user ); // @Slf4j sysout
return user;
}
}
filter
________________________________________________________
@Slf4j
// @Component :: 클래스 단위로 Bean 등록함을 의미
// @ServletComponentScan 미사용시 @Component 사용 해야함
@WebFilter(urlPatterns = "/api/user/*") :: 특정 클래스, 특정 컨트롤러 에만 filter 적용
public class GlobalFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
:: 전 처리 구간 : ContentCachingResponseWrapper 읽은내용 다시 읽을수 있도록
ContentCachingRequestWrapper httpServletRequest = new ContentCachingRequestWrapper((HttpServletRequest)request);
ContentCachingResponseWrapper httpServletResponse = new ContentCachingResponseWrapper((HttpServletResponse)response);
System.out.println("__________________________");
chain.doFilter(httpServletRequest, httpServletResponse);
System.out.println("__________________________");
:: 후 처리 구간
String uri = httpServletRequest.getRequestURI();
String reqContent = new String(httpServletRequest.getContentAsByteArray());
log.info("request uri : {} , request Body : {}" , uri , reqContent);
String resContent = new String(httpServletResponse.getContentAsByteArray());
int httpStatus = httpServletResponse.getStatus();
:: 한번 읽으면 다시 사용 불가능 하므로 복사하여 응답 body 에 보여줌
httpServletResponse.copyBodyToResponse();
log.info("response status : {} , response Body : {}" , httpStatus , reqContent);
}
}
main
________________________________________________________
@ServletComponentScan :: 특정 클래스, 특정 컨트롤러 에만 filter 적용
:: Filter > @WebFilter(urlPatterns = "/api/user/*") 추가해야함
@SpringBootApplication
public class FilterApplication {
public static void main(String[] args) {
SpringApplication.run(FilterApplication.class, args);
}
}
2. interCeptor
interceptor
- 스프링에서 인터셉터는 HandlerInterceptorAdapter를 상속받아 구현합니다.
- 인터셉터는 이름 그대로 "무언가를 가로챈다."라는 의미를 가지는데요,
인터셉터는 컨트롤러의 URI에 접근하는 과정에서 무언가를 제어할 필요가 있을 때 사용됩니다.
정확히는 컨트롤러에 접근하기 전과 후로 나뉩니다.
____________________________________________________________________
HandlerInterceptorAdapter
-preHandler : 컨트롤러에 도착하기전에 동작하는 메소드로
return값이 true이면 진행, false이면 멈춥니다.
-postHandler : 컨트롤러에 도착하여 view가 랜더링되기 전에 동작합니다.
-afterCompletion: view가 정상적으로 랜더링된 후에 마지막에 실행됩니다.
____________________________________________________________________
@Component 어노테이션
@Target, @Rentetion, @Documneted , @Indexed 어노테이션을 갖고 있습니다.
@Documented : Java doc에 문서화 여부를 결정합니다.
@Retention : 어노테이션의 지속 시간을 정합니다.
* RetentionPolicy
* SOURCE : 어노테이션을 사실상 주석처럼 사용하는 것
* CLASS : 컴파일에서는 어노테이션의 메모리를 가져가지만
실질적으로 런타임시에는 사라지게 됩니다.
* RUNTIME : 어노테이션을 런타임시에까지 사용할 수 있습니다.
@Target : 어노테이션을 작성할 곳 입니다. default 값은 모든 대상입니다.
* ElementType.TYPE (class, interface, enum)
* ElementType.FIELD (instance variable)
* ElementType.METHOD
* ElementType.PARAMETER
* ElementType.CONSTRUCTOR
* ElementType.LOCAL_VARIABLE
* ElementType.ANNOTATION_TYPE (on another annotation)
* ElementType.PACKAGE (remember package-info.java)
@Inherited : 자식클래스에 상속할지 결정
____________________________________________________________________
@Autowired
필요한 의존 객체의 “타입"에 해당하는 빈을 찾아 주입한다.
생성자 , setter, 필드 3가지의 경우에 Autowired를 사용할 수 있다.
그리고 Autowired는 기본값이 true이기 때문에
의존성 주입을 할 대상을 찾지 못한다면 애플리케이션 구동에 실패한다.


interceptor
_____________________________________________
@Slf4j
@Component // 클래스 단위로 Bean 등록
public class AuthInterCeptor implements HandlerInterceptor {
_____________________________________________
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
String url = request.getRequestURI();
URI uri = UriComponentsBuilder.fromUriString(request.getRequestURI())
.query(request.getQueryString()).build().toUri();
log.info("request url : {}", url);
boolean hasAnnotation = checkAnnotation(handler , Auth.class);
log.info("has Annotation : {}", hasAnnotation);
// 나의 서버는 모두 public 으로 동작하는데
// 단! Auth 권한을 가진 요청에 대해서는 세션, 쿠키
if(hasAnnotation){
//권한 체크
String query = uri.getQuery();
if(query.equals("name=steve")){
return true;
}
throw new AuthException();
}
return true;
}
_____________________________________________
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler,
@Nullable ModelAndView modelAndView) throws Exception {
}
_____________________________________________
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object object,
@Nullable Exception arg3) throws Exception {
}
_____________________________________________
private boolean checkAnnotation(Object handler , Class clazz){
// resource javaScript , html.
if( handler instanceof ResourceHttpRequestHandler){
return true;
}
HandlerMethod handlerMethod = (HandlerMethod) handler;
if( null != handlerMethod.getMethodAnnotation(clazz) ||
null != handlerMethod.getBeanType().getAnnotation(clazz) ){
return true; :: Auth annotation 이 있으면 true
}
return false;
}
_____________________________________________
}
WebWebMvcConfigurer를 통한 Interceptor Bean 등록처리
- addIntercepotrs를 통해 사용할 Interceptor를 등록하고. 패턴을 등록해줍니다.
- addPathPatterns : 해당 메소드는 동작해야할 url패턴을 설정합니다.
- excludePathPatterns: 해당 메소드는 적용한 인터셉터에서 제외할 url패턴을 설정합니다.
_________________________________________________________________________________________
@Configuration :: 1개 이상의 bean을 명시하고 있음
@RequiredArgsConstructor :: final로 선언된 객체들을 생성자에서 주입 받을수 있게 한다.
public class MvcConfig implements WebMvcConfigurer {
:: @Autowired > 순환참조가 일어날 수 있어서 사용하지 않는다.
private final AuthInterCeptor authInterCeptor;
:: ============== @RequiredArgsConstructor ==============
:: private final AuthInterCeptor authInterCeptor;
:: =======================================================
:: private AuthInterCeptor authInterCeptor;
:: public MvcConfig ( AuthInterCeptor authInterCeptor ){
:: this.authInterCeptor = authInterCeptor;
:: }
:: =======================================================
:: 인터셉터 주소 세팅
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authInterCeptor)
.addPathPatterns("/api/private/*");
}
}

3. Async( 비동기 )
@EnableAsync :: 명시하기!!
@SpringBootApplication
public class AsyncApplication {
public static void main(String[] args) {
SpringApplication.run(AsyncApplication.class, args);
}
}
@Slf4j
@RestController
@RequestMapping("/api")
public class ApiController {
private final AsyncService asyncServices;
public ApiController(AsyncService asyncServices) {
this.asyncServices = asyncServices;
}
@GetMapping("/hello")
public CompletableFuture hello(){
log.info("completetable reature init");
return asyncServices.run();
}
}
@Slf4j
@Service
public class AsyncService {
:: CompletableFuture : 다른 쓰레드 에서 실행하고 결과를 받는 형태
@Async("async-thread") :: public method 에서만 동작
public CompletableFuture run(){
:: hello(); 동작안됨. 같은 클래스 내에서 같은 메소드 호출 안된다.
return new AsyncResult(hello()).completable();
}
public String hello() {
for ( int i = 0 ; i < 10 ; i++ ){
try{
Thread.sleep(2000);
log.info("thread sleep");
}catch (Exception e){
e.printStackTrace();
}
}
return "async hello";
}
}
:: Thread pool 지정하기
@Configuration
public class AppConfig {
@Bean("async-thread")
public Executor asyncThread(){
ThreadPoolTaskExecutor threadPoolExecutor = new ThreadPoolTaskExecutor();
threadPoolExecutor.setCorePoolSize(10);
threadPoolExecutor.setQueueCapacity(10);
threadPoolExecutor.setMaxPoolSize(100);
threadPoolExecutor.setThreadNamePrefix("Async-");
return threadPoolExecutor;
}
}
728x90