일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- Kotlin
- 쿠버네티스
- GC
- spring
- 이스티오
- IntellJ
- mysql
- thread
- 토비의 스프링 정리
- Stack
- MSA
- 보조스트림
- 자바
- 토비의 스프링
- 백준
- gradle
- K8s
- JPA
- SpringBoot
- list
- 자바 ORM 표준 JPA 프로그래밍
- 스트림
- jvm
- 스프링
- OS
- redis
- Real MySQL
- Collection
- Java
- Stream
- Today
- Total
인생을 코딩하다.
[Java][Spring] Filter와 Interceptor 본문
첫 화면에서 로그인을 해야 기능을 사용할 수 있는 어떤 웹 사이트를 가정해보자.일반적으로 첫 화면에서 로그인을 하고, 나머지 기능들을 ui 클릭을 통해 사용할 것이다.
하지만, 어떤 사용자가 로그인을 해야만 하는 어떤 기능의 url을 복사해 뒀다가 로그인 없이 주소창에 그 url을 붙여넣는다면 어떨까?
'별도의 처리' 가 없다면 아마 로그인 없이도 기능을 사용하게 되거나, 에러가 발생할 것이다.
그 '별도의 처리'가 포함된 로직은 일반적으로 아래와 같을 것이다.
1. 사용자는 로그인을 한다.
2. 시스템은 로그인한 사용자 정보를 세션에 저장한다.
3. 사용자는 기능을 요청한다.
4. 시스템은 사용자가 요청한 기능을 수행하기 전에 요청한 사용자의 세션을 체크한다.
5. 시스템은 올바른 세션일 경우 기능 승인, 세션이 없거나 올바르지 않을 경우 사용자를 로그인 페이지로 이동시킨다.
그렇다면 이 중 4번과 5번의 처리를 어떻게 해야할까?
Spring MVC를 사용한다면 클라이언트의 기능 사용 요청이 Controller로 들어올 테니, 각 컨트롤러마다 기능 로직을 수행하기 전 세션 체크를 해 주면 될 것이다.1000개의 기능이 있다면 1000개의 세션 체크 로직이 들어가게 되며 이는 중복된 코드이다.
그렇다면 세션을 체크하는 로직을 하나의 유틸로 만들고, 각 컨트롤러에서 이 유틸을 한번씩 호출하면 되지 않을까? 상당히 응집도가 높아 졌지만, 아직 1000개의 컨트롤러에는 세션 체크 유틸을 호출하는 코드가 중복해서 남아있다.
이런 상황에서 사용할 수 있도록, 컨트롤러로 오는 모든(혹은 일부 조건의) 요청들을 컨트롤러가 처리하기 전에 가로채(intercept해) 로직을 끼워 넣는 기능이 존재한다.
위 로그인 예시와 같은 상황에서 프로젝트에 따라 두가지 방법이 존재한다.
1. Filter(필터)를 사용한다.
2. Interceptor(인터셉터)를 사용한다.
위 그림을 볼 때 Filter와 Interceptor의 가장 큰 차이는 이렇다
둘 다 컨트롤러 전에 작업을 처리하는 용도로 사용되지만 호출되는 시점이 다르다.
필터(Filter)는 dispatcherServlet으로 요청이 가기전에 실행되고, 인터셉터(Interceptor)는 Controller로 요청이 가기전에 실행이된다.
Filter
public interface Filter {
public void init(FilterConfig filterConfig) throws ServletException;
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain)
throws IOException, ServletException;
public void destroy();
}
Filter 는 Servlet Container 에 의해 동작이 제어되는 Java Class 로 HTTP Request 가 Service 에 도착하기 전에, HTTP Response 가 Client 에 도착하기 전에 제어할 수 있다.
Filter 는 Request 를 처리할 때 Dispatcher Servlet 이 작업을 처리하기 전에 동작하고, Response 를 처리할 때에는 Dispatcher Servlet 에 의해 작업이 끝난 이후에 동작한다.
- DispatcherServlet 앞단에서 정보 처리
- J2EE 표준스펙에 정의되어 있는 기능
- Controller 이후 자원 처리가 끝난 후 응답 처리에 대해서도 변경, 조작을 수행할 수 있다.
- 일반적으로 인코딩 변환 처리, XSS 방어를 개발할 때 사용한다.
Filter 실행메소드
- init() - 필터 인스턴스 초기화
- doFilter() - 실제 처리 로직
- destroy() - 필터 인스턴스 종료
정확히 분류하자면 Filter 는 J2EE 의 표준이며 Servlet 2.3 부터 지원되는 기능으로 Spring Framework 에서 지원을 하고는 있지만 Spring 프레임워크만의 기능은 아님을 알아두자.
Filter 는 Filter Chain 을 갖고 있으며 Application Context 에 등록된 필터들이 WAS 구동 시에 Context Layer 에 설정된 순서대로 필터 체인을 구성한다.
구성된 체인은 맨 처음 인스턴스 초기화(init())를 거친 후 각 필터에서 doFilter() 를 통해 필터링 작업을 처리하고 Destroy 된다.
이 때의 환경 설정은 주로 톰캣을 사용할 경우 web.xml 또는 Java Configuration 을 이용해서 구현하게 된다.
Interceptor
public interface HandlerInterceptor {
boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception;
void postHandle(
HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
throws Exception;
void afterCompletion(
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception;
}
Interceptor 는 필터와는 다르게 Spring 레벨에서 지원하는 Servlet Filter 이다. Spring Context 내에서 HTTPRequest 와 HTTPResponse 처리에 대해 강력한 기능을 제공한다.
Java Servlet 레벨에서 동작하는 Filter 와 다르게 Spring Context 레벨에서 동작하므로 Dispatcher Servlet 이 Request 및 Response 를 처리하는 시점에 Interceptor Handler 가 동작한다.
- DispatcherServlet에서 Handler(Controller)로 가기전에 정보 처리
- 스프링의 DispatcherSevlet이 Controller를 호출하기 전, 후에 끼어들기 때문에 스프링 컨텍스트 내부에서 Controller에 관한 요청과 응답에 관여한다.
- SpringFramework에서 자체적으로 제공하는 기능
- 로그인 체크, 권한 체크, 프로그램 실행시간 계산 작업 로그확인 등의 업무 처리
- preHandler에서 전처리가 이루어지고, postHandler에서 후처리를 한다.
Interceptor 실행메소드
- preHandler() - Controller 실행 전
- postHandler() - Controller 실행 후 view Rendering 실행 전
- afterCompletion() - view Rendering 이후
Dispatcher Servlet 에서 요청이 처리되고 나면 요청받은 URL 에 대해 등록된 Interceptor 가 호출되며 Prehandle - Controller 실행 - PostHandle - AfterCompletion 의 순서로 인터셉터가 작업을 처리한다.
이 때의 환경 설정은 주로 servletContext.xml 또는 Java Configuration 을 이용해서 구현하게 된다.
----------------------------------------------------------------------------------------------------------------------------------
맨 위에 설명한 상황에 관해 답을 내리자면..
정확히 어떤 상황에 어떤 기능을 사용해야 하는가에 대해서는 갑론을박이 많지만, 인코딩이나 보안 관련 처리와 같은 web app의 전역적으로 처리해야 하는 로직은 필터로 구현하고 클라이언트에서 들어오는 디테일한 처리(인증, 권한 등)에 대해서는 주로 인터셉터에서 처리하는듯 하다.
그렇다면, 위에서 예를 든 로그인 세션 인증에 관해서는 인터셉터에서 처리하는 것이 더 나을 것이다.
'Java' 카테고리의 다른 글
[Java]블로킹 큐(Blocking Queues) (0) | 2021.03.28 |
---|---|
[JAVA]64비트는 왜 원자적이지 않을까? 및 연산의 원자성 (0) | 2021.03.27 |
[Java]마커 인터페이스와 Serializable 동작 방식 (0) | 2021.02.18 |
[Java]ArrayDeque (0) | 2021.02.15 |
[Java]팩터리 메서드 패턴 (0) | 2021.02.14 |