필터 & 인터셉터
특정 페이지는 로그인이나 권한 없이는 접근할수 없도록 해야하는 경우가 있다. 예를들면
로그인을 해야만 접근가능한 페이지가 존재할것이다.(ex 프로필수정 등) 그러나 비 로그인 사용자도 URL을 직접 호출하면 해당 페이지에 접근이 가능하다. 이를 방지하기 위해서
앞서 쿠키& 세션을 통해 로그인 상태를 유지하도록 했었다.
모든 컨트롤러에 로그인 상태를 검사하는 로직을 작성하게 되면 많은 코드 중복과 수정발생시 난감한 상황이 생길것이다.
모든 애플리케이션 단위에서 공통적으로 로그인 여부를 확인할수있어야하고 더 쉽게 로직 수정이 가능하게 할수는 없을까?
모든 애플리케이션 단위에서 공통적으로 처리해야하는 것을 공통관심사 라고한다.
관련: AOP
이런 공통 관심사는 스프링 AOP로 해결할수있지만 웹 관련 공통 관심사는 서블릿이 제공하는 필터 혹은 스프링이 제공하는 인터셉터를 활용하여 처리하는게 좋다.
이 포스트에서는 두 제공을 비교해 보고자 한다.
서블릿 필터 vs 스프링 인터셉터
웹과 관련된 공통 관심사를 처리할 때는 HTTP의 헤더나 URL의 정보들이 필요한데, 서블릿 필터나 스프링 인터셉터는 HttpServletRequest 를 제공한다
서블릿 필터
HTTP 요청 -> WAS -> 필터 -> 스프링 디스패처 서블릿 -> 컨트롤러
필터는 수문장의 역할을 한다.
요청에 대하여 필터를 적용해 두면 적합하지 않은 요청엔 디스패처 서블릿이 동작하지 않는다.
HTTP 요청 -> WAS -> 필터 -> 디스패처 서블릿 -> 컨트롤러 //로그인 사용자
HTTP 요청 -> WAS -> 필터(적절하지 않은 요청이라 판단, 서블릿 호출X) //비 로그인 사용자
필터 여러개를 체이닝 할수도있다.
HTTP 요청 -> WAS -> 필터1 -> 필터2 -> 필터3 -> 서블릿 -> 컨트롤러
필터 인터페이스를 구현하고 등록하면 서블릿 컨테이너가 필터를 싱글톤 객체로 생성하고, 관리한다.
스프링 인터셉터
서블릿 필터와 같이 공통 관심사항을 처리하지만 그 범위와 순서 사용법이 다르다.
스프링 인터셉터 동작 순서
HTTP 요청 -> WAS -> 필터 -> 디스패쳐 서블릿 -> 스프링 인터셉터 -> 컨트롤러
서블릿 필터와 다르게 디스패쳐 서블릿 이후에 등장한다.
스프링 인터셉터
HTTP 요청 -> WAS -> 필터 -> 디스패쳐 서블릿 -> 스프링 인터셉터 -> 컨트롤러
//로그인 사용자
HTTP 요청 -> WAS -> 필터 -> 디스패쳐 서블릿 -> 스프링 인터셉터 (적절하지 않은 요청이라 판단, 컨트롤러 호출 X)
// 비 로그인 사용자
스프링 인터셉터는 디스패처 서블릿과 컨트롤러 사이에서 컨트롤러 호출 직전에 호출 된다.
스프링 인터셉터는 스프링 MVC가 제공하는 기능이기 때문에 결국 디스패처 서블릿 이후에 등장하게 된다.
스프링 MVC의 시작점이 디스패처 서블릿이기 떄문이다.
HTTP 요청 -> WAS -> 필터 -> 서블릿 -> 인터셉터1 -> 인터셉터2 -> 컨트롤러
당연히 체이닝도 가능하다.
서블릿 필터와 호출 되는 순서만 다르고, 제공하는 기능은 비슷해 보이지만 스프링 인터셉터는 서블릿 필터보다 편리하고, 더 정교하고 다양한 기능을 지원한다.
스프링 인터셉터 구현
스프링이 제공하는 HandlerInterceptor를 구현한다.
@Slf4j
public class LogInterceptor implements HandlerInterceptor {
//컨트롤러 호출 전에 호출된다
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//로직구현
return true; //false 진행X
}
//컨트롤러에서 예외가 발생하면 postHandle 은 호출되지 않는다.
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
//로직구현
}
//afterCompletion 은 항상 호출된다.
@Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
//로직구현
}
}
흐름
preHandle , postHandle , afterCompletion 메서드들의 호출시점을 확인하자
preHandle : 컨트롤러 호출 전에 호출된다. (더 정확히는 핸들러 어댑터 호출 전에 호출된다.)
'''
preHandle 의 응답값이 true 이면 다음으로 진행하고, false 이면 더는 진행하지 않는다.
false 인 경우 나머지 인터셉터는 물론이고, 핸들러 어댑터도 호출되지 않는다.
그림에서 1번에서 끝이 나버린다.
'''
postHandle : 컨트롤러 호출 후에 호출된다. (더 정확히는 핸들러 어댑터 호출 후에 호출된다.)
'''
컨트롤러에서 예외가 발생하면 postHandle은 실행되지 않는다.
'''
afterCompletion : 뷰가 렌더링 된 이후에 호출된다
'''
항상 실행된다.
'''
정리
필터나 인터셉터를 이용하면 로직부분과 로직수행의 책임부분을 분리할수있다.
핵심로직과 책임로직을 분리해서 처리
따라서 이후에 로그인 관련 정책이 바뀌어도 필터나 인터셉터 부분만 수정하면 된다.
즉, 필터는 요청 자체를 수문장 처럼 걸러내 버리지만 인터셉터는 요청 처리 과정에서 개입되어 섬세하게 조율할수있다.
preHandle , postHandle , afterCompletion 호출 시점을 이용해서 섬세하게 제어 가능
특별한 문제가 없다면 인터셉터를 활용하는것이 더 좋다.
'개발자 준비 > Spring' 카테고리의 다른 글
@RequestBody, @ModelAttribute , @RequestParam , 추가로 @PathVariable (0) | 2022.01.13 |
---|---|
스프링으로 서블릿(Servlet)을 다룬다는 것 (0) | 2022.01.03 |
[스프링 핵심 정복] 스프링으로 서블릿(Servlet)을 다룬다는 것 (0) | 2022.01.01 |
스프링 MVC 구조 (0) | 2021.12.28 |
서블릿 (0) | 2021.11.17 |