Interceptor와 Filter는 뭐가 다를까?

2024. 5. 6. 17:19· CS지식
목차
  1. 최종 정리

오늘 다룰 주제는 인터셉터와 필터의 차이.

 

둘 다 요청을 가로채서 처리할 수 있는 기능을 가졌다는 공통점이 있어서 혼동하하기 쉬운 개념이라고 생각한다.

 

필터

 

오라클 공식문서에서는 다음과 같이 설명이 되어있다. 즉 요청과 응답에 대해 필터링 작업을 수행하는 주체.

 

 

필터는 DispatcherServlet 전, 후로 요청을 가로채서 동작하기에 스프링 영역 밖에서 관리된다는 특징이 있다.

 

 

자바에서는 인터페이스 형태로 필터를 제공하며 아래 3개의 메서드를 오버라이드 하여 사용할 수 있다.

 

1. init : 초기화 작업을 진행. (서블릿 컨테이너에서 필터 인스턴스를 만든 뒤 단 한 번만 호출된다.)

 

2. doFilter : 실제 필터링 로직을 구현. 요청을 처리하기 전후로 request, response를 수정할 수 있다.

 

3. destroy : 필터를 제거할 때 사용. 즉 자원을 해제한다.(예를 들면 데이터베이스 연결이나 파일 핸들러 같은 리소스 정리)

 

간단하게 특정 uri를 가로채는 필터를 만들어보자.

@Slf4j                                                                                                                               
@WebFilter(urlPatterns = "/hello")                                                                                                        
public class LogFilter implements Filter {                                                                                           
                                                                                                                                     
    @Override                                                                                                                        
    public void init(FilterConfig filterConfig) throws ServletException {                                                            
        Filter.super.init(filterConfig);                                                                                             
        log.info("로그 필터 초기화 진행");                                                                                                    
    }                                                                                                                                
                                                                                                                                     
    @Override                                                                                                                        
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { 
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;                                                        
                                                                                                                                     
        log.info("Request URI : {}", httpServletRequest.getRequestURI());                                                        
                                                                                                                                     
        // 다음 필터나 서블릿으로 요청 전달                                                                                                        
        chain.doFilter(request, response);                                                                                           
    }                                                                                                                                
                                                                                                                                     
    @Override                                                                                                                        
    public void destroy() {                                                                                                          
        Filter.super.destroy();                                                                                                      
        log.info("로그 필터 소멸");                                                                                                        
    }                                                                                                                                
}

 

@WebFilter라는 어노테이션을 사용해서 해당 클래스를 서블릿 필터로 등록시킨다.

 

@ServletComponentScan                                     
public class CsApplication{                               
	                                                      
	public static void main(String[] args) {              
		SpringApplication.run(CsApplication.class, args); 
	}                                                     
	                                                      
}

 

그리고 메인 어플리케이션에 @ServletComponentScan을 붙여야 서블릿 컴포넌트를 스캔하도록 만들 수 있다.

 

순서는 아래와 같이 동작한다.

 

1. 현재 코드의 순서로 가면 서블릿 컨테이너가 생성되면 서블릿 컴포넌트를 스캔하여 필터로 등록한다.

 

2. 스캔된 필터를 인스턴스화 시켜 init() 메서드를 호출하여 초기화를 진행한다.

 

3. 클라이언트한테 경로에 맞는 요청이 들어오면 doFilter() 실행

 

4. 서블릿 컨테이너 종료되면 destory() 실행

 

처음에 초기화를 진행하고

 

호출하면 doFilter 메서드를 실행해서 로그가 찍히는 걸 볼 수 있다.

 

이후 스프링을 종료시키면 destroy() 메서드가 호출된다.

 

파라미터를 보면 FilterChain을 받고 있다. 서블릿에 등록된 필터는 배열로 관리가 되고, pos라는 인덱스를 통해 필터를 찾아서 순서대로 실행시킨다.

 

 

서블릿에 현재 등록된 필터는 5개가 있고, 내가 커스텀으로 등록한 LogFilter도 3번 인덱스로 등록이 되어있다. 이렇게 순차대로 실행한 다음 doFilter를 호출해서 다음 인덱스를 부르게 되는것

 

 

스프링 인터셉터

이름에서 느껴지는 스프링 냄새. 말 그대로 스프링에서 사용되는 개념.

 

인터셉터 또한 인터페이스이며 인터페이스 기능을 오버라이딩해서 구현할 수 있다. 여기서는 HandlerInterceptor를 구현해서 사용한다.

(Adapter를 상속해서 구현하는 방법도 있으나 옛날 방식이다.)

 

 

인터셉터는 위 사진과 같이 DispatcherServlet과 Controller 사이에 위치해서 요청을 가로챈다.

즉 Controller의 전, 후의 작업을 처리하고 싶을 때 사용하는 방식.

 

 

인터셉터도 총 3가지 메서드를 구현해서 사용할 수 있다.

 

1. preHandle : 컨트롤러로 요청 보내기 전에 가로채서 로직 동작. boolean이 반환타입이며 true면 진행시키고 false면 요청을 반려시킨다.

 

2. postHandle : 컨트롤러 처리가 끝나고 응답을 가로채서 로직 동작. 컨트롤러에서 예외가 발생하면 실행되지 않는다.

 

3. afterCompletion : 뷰가 클라이언트한테 응답을 전송한 뒤 실행. 컨트롤러에서 예외가 발생해도 동작한다.

 

 

간단하게 특정 컨트롤러를 가로채는 인터셉터를 만들어보자.

 

@Slf4j                                                                                                                                             
public class LogInterceptor implements HandlerInterceptor {                                                                                        
                                                                                                                                                   
    @Override                                                                                                                                      
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {                          
                                                                                                                                                   
        log.info("{}로 요청 가로채기", request.getRequestURI());                                                                                          
                                                                                                                                                   
        return true;                                                                                                                               
    }                                                                                                                                              
                                                                                                                                                   
    @Override                                                                                                                                      
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { 
                                                                                                                                                   
        log.info("컨트롤러 응답 가로채기");                                                                                                                  
        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);                                                             
    }                                                                                                                                              
                                                                                                                                                   
    @Override                                                                                                                                      
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {         
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);                                                                  
    }                                                                                                                                              
}

컨트롤러 응답을 가로채는 인터셉터를 만들었다. 여기서는 HandlerInterceptor를 구현하는 방법을 선택.

 

이제 어떤 컨트롤러에 오는 요청을 가로챌지 정해는 부분이 필요하다.

 

@Configuration                                                   
public class WebConfig implements WebMvcConfigurer {             
                                                                 
    @Override                                                    
    public void addInterceptors(InterceptorRegistry registry) {  
        registry.addInterceptor(new LogInterceptor())            
                .addPathPatterns("/hello");                      
    }                                                            
}

 

그 작업은 WebConfig를 하나 만들고, WebMvcConfigurer를 구현해서 addInterceptors 메서드를 오버라이딩하면 특정 경로로 들어오는 요청에 대해 인터셉터를 실행하도록 설정할 수 있다.

 

(WebMvcConfigurer 인터페이스는 스프링 MVC의 기본 설정을 확장하거나 재정하는 데 사용한다. cors, 뷰리졸버, 인터셉터 등등... 설정이 가능해진다)

 

그중에 addInterceptors를 사용해서 개발자가 만든 커스텀 인터셉터를 추가해 준다. 또 허용 경로를 설정해 주었다.

 

 

이렇게 하고 http://localhost:8080/hello로 요청을 보내면 LogInterceptor가 동작하게 되고, preHandle 메서드가 실행돼서 로그 출력 -> true를 반환하니 컨트롤러로 넘겨준다.

컨트롤러가 리턴되면 LogInterceptor가 다시 가로채서 postHandle을 동작해서 응답에 대한 인터셉터를 처리한다.

 

 

 

 

최종 정리

  필터 인터셉터
스펙 서블릿 스프링
위치 디스패쳐 서블릿 전, 후 컨트롤러 전, 후
대상 모든 서블릿 요청 스프링 컨트롤러 요청
사용 목적 인증/권한 검사
요청 수정/응답 변경
로깅
...
인증/권한 검사
비즈니스 로직 실행 전, 후 작업
로깅
특정 핸들러만 필터링
...
제어 체인 방식(다음 필터 호출 여부 결정) preHandle에서 boolean으로 컨트롤러 접근 제어

 

반응형
저작자표시 (새창열림)

'CS지식' 카테고리의 다른 글

Tomcat은 정확히 어떤 역할을 하는 도구일까?  (0) 2024.05.12
@Transactional 어노테이션의 역할  (1) 2024.05.09
스프링 IOC와 DI를 어떻게 설명해야 할까?  (0) 2024.05.05
final 키워드를 사용하면 어떤 이점이 존재할까?(JAVA)  (0) 2024.04.29
  1. 최종 정리
'CS지식' 카테고리의 다른 글
  • Tomcat은 정확히 어떤 역할을 하는 도구일까?
  • @Transactional 어노테이션의 역할
  • 스프링 IOC와 DI를 어떻게 설명해야 할까?
  • final 키워드를 사용하면 어떤 이점이 존재할까?(JAVA)
indeep
indeep
백준 - https://www.acmicpc.net/user/esu08259 깃허브 - https://github.com/qkrrlgus114
indeep
불편한 게 싫어
indeep
글쓰기방명록관리자
전체
오늘
어제
  • 분류 전체보기 (181)
    • 알고리즘문제 (11)
      • 백준 (10)
      • swea (1)
    • CS지식 (27)
      • HTTP 웹 지식 (4)
    • 일상 (2)
    • 스프링 강의(인프런) (4)
    • JAVA 강의(인프런) (11)
    • JAVA (8)
    • 오류해결 (31)
    • Vue (5)
    • 싸피 (4)
    • 스프링 개념 (2)
    • git 관련 (1)
    • 면접 (4)
    • 기타 (14)
    • 프로젝트 (45)
      • RESTAPI 추천 서비스 (34)
      • 씈크럼 프로젝트 (11)
    • 독서 (3)
    • 행사 || 컨퍼런스 (4)
    • 회고 (3)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

최근 댓글

hELLO · Designed By 정상우.v4.2.2
indeep
Interceptor와 Filter는 뭐가 다를까?
상단으로

티스토리툴바

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.