Spring

[SpringBoot] Filter, Interceptor 개념 정리 및 로그인 처리

꾸준함. 2021. 8. 1. 23:07

개요

기존 게시글에서는 쿠키와 세션 개념을 정리하면서 이 두 개념을 이용하여 로그인 관련 코드 적용도 해봤습니다.

(https://jaimemin.tistory.com/1885, https://jaimemin.tistory.com/1886)

하지만, 로그인 처리를 할 때 쿠키와 세션만으로는 해결 안 되는 점이 있습니다.

예를 들자면, 로그인을 해야지만 접근 가능한 페이지들이 있을 때 쿠키와 세션만 적용했을 때는 해당 페이지 url을 직접 쳐서 들어갈 경우 그대로 화면이 노출된다는 문제점이 있습니다.

사실 시스템 요구사항에서는 로그인하지 않은 상태에서 해당 페이지들에 접근할 경우 우선 로그인 페이지로 리다이렉트 한 후 다시 그 페이지로 리다이렉트 하는 방식을 원할 것입니다.

여기서 필터와 인터셉터가 쓰이는데 필터와 인터셉터는 웹과 관련된 공통 관심사를 처리할 때 사용됩니다. (AOP와 유사)

 

저를 포함해서 필터와 인터셉터의 차이를 혼동하는 사람들이 많다는 것을 실무 개발을 하면서 느꼈습니다.

따라서, 이번 게시글을 통해 필터와 인터셉터의 차이에 대해 확실히 정리해보겠습니다.

 

1. 필터

  • 정확히 명칭은 서블릿 필터 (스프링에서 제공하는 기능이 아님)
  • 필터를 적용할 경우 필터가 호출된 다음에 서블릿이 호출
  • 따라서, 모든 고객의 요청 로그를 남기는 요구사항이 있을 경우 필터를 사용
  • 기존 Spring MVC 구조를 떠올려보면 아래와 같은 구조를 그릴 수 있음 (https://jaimemin.tistory.com/1820)

 

 

구조를 보면 DispatcherServlet이 호출되기 전에 필터를 거치는 것을 확인할 수 있습니다.

 

1.1 필터 체인

  • 필터는 체인으로 구성되며, 중간에 필터를 자유롭게 추가 및 제거할 수 있음
  • 예를 들어 로그를 남기는 필터를 먼저 적용한 후 로그인 여부를 체크하는 필터를 이후에 추가할 수 있음
  • ex) HTTP 요청 -> WAS -> 필터 1 -> 필터 2 ->... -> 필터 N -> 디스패처 서블릿 -> 컨트롤러

 

1.2 필터의 역할

  • 필터는 적절하지 않은 요청이 들어올 경우 HTTP 요청이 서블릿까지 도달하지 못하게 하는 역할이 있음
  • 따라서, Spring Security를 적용할 때 필터에 적용하기도 함
  • 정상적인 요청: HTTP 요청 -> WAS -> 필터1 -> 필터 2 ->... -> 필터 N -> 디스패처 서블릿 -> 컨트롤러
  • 잘 못된 요청: HTTP 요청 -> WAS -> 필터1 -> 필터 2 -> 서블릿 호출 X 

 

1.3 필터 인터페이스 살펴보기

 

 

필터 인터페이스

  • 인터페이스에는 세 가지 메서드가 있고 init과 destory 메서드 같은 경우 default 키워드가 있기 때문에 필요가 없을 경우 별도로 정의하지 않아도 됨
  • 개요에서도 언급했드시 웹 관련 공통사항을 처리하기 때문에 파라미터로 ServletRequest와 ServletResponse 객체가 제공되는 것을 확인할 수 있음
    • 스프링 프레임워크는 확장성을 위해 ServletRequest와 ServletResponse 객체로 제공했지만 대부분 HttpServletRequest와 HttpServletResponse를 사용하기 때문에 다운 캐스팅하여 사용하면 됨

 

메서드 간단 설명

  • init() 메서드: 필터 초기화 메서드이며 서블릿 컨테이너가 생성될 때 호출
  • doFilter() 메서드: 고객의 요청이 올 때마다 해당 메서드가 호출되며 메인 로직을 이 메서드에 구현하면 됨
  • destory() 메서드: 필터 종료 메서드이며 서블릿 컨테이너가 소멸될 때 호출

 

1.4 로그인 여부 확인하는 필터 예시

  • 앞선 게시글에서는 각 컨트롤러마다 로그인 여부를 확인했는데 필터를 적용하면서 반복되는 공통 코드를 한번에 처리할 수 있음
  • 로그인 확인 여부 체크하는 필터를 구현한 뒤 @Configuration에 필터를 등록해주면 적용 가능
  • 아래 코드 참고

 

LoginCheckFilter.java

 

 

WebConfig.java

 

 

코드 부연설명

  • LoginCheckFilter 내 whitelist 같은 경우 로그인하지 않아도 접근할 수 있는 자원들 목록
  • whitelist에 포함되지 않은 요청 URL들은 로그인이 필수인 상황이므로 만약 로그인이 되어있지 않은 상태에서 요청이 올 경우 필터가 해당 요청을 튕겨내고 login 페이지로 리다이렉트 시켜야 함
    • 이때, 기존 요청을 쿼리 파라미터로 redirectURL로 지정함으로써 로그인한 이후에는 기존 요청 페이지로 리다이렉트 될 수 있도록 처리하는 것이 고객 입장에서 편리 (httpResponse.sendRedirect("/login?redirectURL=" + requestURI);)
    • 더 이상 필터 체인을 타지 않기 위해 return; 문을 넣는 것이 중요
  • WebConfig 내 필터를 등록하기 위해서는 각 필터를 빈으로 등록해줘야 함
    • 이후 설명하겠지만 인터셉터의 경우 하나의 메서드 내 여러 인터셉터를 등록할 수 있고 필터의 경우 필터 개수만큼 메서드를 선언하여 빈으로 등록해줘야 함
    • 모든 요청에 로그인 필터를 적용한 뒤 whitelist에 포함되었는지 여부는 로그인 필터 내부 로직에서 확인 (인터셉터의 경우 이 같은 처리를 편리하게 적용하기 위해 보다 편한 메서드 제공)

 

2. 인터셉터

  • 필터는 서블릿이 제공하는 기술이지만 스프링 인터셉터는 스프링 MVC가 제공하는 기술
  • 웹 관련 공통 관심 사항을 처리한다는 점에서는 필터와 비슷
  • 인터셉터는 필터와 달리 디스패처 서블릿과 컨트롤러 사이에서 컨트롤러 호출 직전에 호출됨
  • 기존 Spring MVC 구조를 떠올려보면 아래와 같은 구조를 그릴 수 있음 (https://jaimemin.tistory.com/1820)

 

 

2.1 스프링 인터셉터 체인

  • 스프링 인터셉터 또한 체인으로 구성되며 중간에 자유롭게 인터셉터를 추가 및 제거 가능
  • 예를 들어 로그를 남기는 인터셉터를 먼저 적용한 후 로그인 여부를 체크하는 인터셉터를 이후에 추가할 수 있음
  • ex) HTTP 요청 -> WAS -> 필터 -> 서블릿 -> 인터셉터 1 -> 인터셉터 2 ->... -> 인터셉터 N -> 컨트롤러

 

2.2 스프링 인터셉터의 역할

  • 스프링 인터셉터 또한 필터처럼 적절하지 않은 요청이 들어올 경우 HTTP 요청이 서블릿까지 도달하지 못하게 하는 역할이 있음
  • 따라서, Spring Security를 적용할 때 스프링 인터셉터에 적용하기도 함
  • 단, 필터는 서블릿에 도달하기 전에 호출되고 스프링 인터셉터는 서블릿에서 컨트롤러를 호출하기 전에 호출됨
  • 정상적인 요청: HTTP 요청 -> WAS -> 필터 -> 디스패처 서블릿 -> 인터셉터 1 -> 인터셉터 2 ->... -> 인터셉터 N -> 컨트롤러
  • 잘 못된 요청: HTTP 요청 -> WAS -> 필터 -> 서블릿 -> 인터셉터 1 -> 인터셉터 2 -> 컨트롤러 호출 안됨 

 

2.3 스프링 인터셉터 인터페이스 살펴보기

 

 

스프링 인터셉터 인터페이스

  • 인터페이스에는 세 가지 메서드가 있고 필터와 달리 세 가지 메서드 모두 default 키워드가 있음
  • 서플릿 필터의 경우 단순하게 doFilter 메서드만 제공되었지만 스프링 인터셉터의 경우 컨트롤러 호출 전에 호출되는 preHandle 메서드, 컨트롤러 호출 후 호출되는 postHandle 메서드, 그리고 요청이 완료된 이후 호출되는 afterCompletion 메서드가 단계적으로 잘 세분화되어 필터보다 많은 기능을 제공
  • 서블릿 필터의 경우 단순히 request, response만 제공했지만 스프링 인터셉터는 어떤 컨트롤러(handler)가 호출되었는지에 관한 호출 정보도 받을 수 있음
  • 또한, 어떤 ModelAndView가 반환되었는지 응답 정보도 받을 수 있음
  • 정리를 하자면 스프링 인터셉터는 필터와 비슷하게 작동하지만 호출 시점이 다르고 인터셉터가 필터보다 더 많은 기능을 제공

 

메서드 간단 설명

  • preHandle() 메서드: 컨트롤러 호출 전, 더 정확히 말하자면 HandlerAdapter 호출 전에 호출
    • preHandle의 반환 값이 true일 경우 다음 체인으로 진행이 되고, false일 경우 더 이상 진행이 되지 않으며 핸들러 어댑터 또한 호출이 되지 않음
  • postHandle() 메서드: 컨트롤러 호출 후, 더 정확히 말하자면 HandlerAdapter 호출 후에 호출
  • afterCompletion() 메서드: 뷰가 렌더링 된 이후에 호출되며 try catch finally 절에서 finally처럼 무조건 호출됨

 

2.4 스프링 인터셉터에서 예외 발생할 경우

  • 컨트롤러에서 예외가 발생할 경우 postHandle 메서드는 호출되지 않음
  • 앞서 언급했듯이 afterCompletion 메서드는 무조건 호출되므로 예외 발생 시 파라미터로 어떤 예외가 발생했는지 알 수 있으며 로그로 출력 가능
    • 필터의 경우 예외를 로깅하기 위해서는 톰캣까지 예외를 보내줘야 하지만 인터셉터의 경우 바로 로깅 가능

 

2.5 로그인 여부 확인하는 스프링 인터셉터 예시

  • 필터처럼 반복되는 공통 코드를 한 번에 처리할 수 있음
  • 로그인 확인 여부 체크하는 필터를 구현한 뒤 @Configuration에 필터를 등록해주면 적용 가능
    • 필터와 달리 WebConfig가 WebMvcConfigurer 인터페이스를 구현해야 함
  • 아래 코드 참고

 

LoginCheckInterceptor.java

 

 

WebConfig.java

 

 

코드 부연설명

  • 로그인 확인 여부는 컨트롤러 호출 전에 이루어지므로 preHandle만 구현
  • 필터와 마찬가지로 만약 로그인이 되어있지 않은 상태에서 요청이 올 경우 필터가 해당 요청을 튕겨내고 login 페이지로 리다이렉트 시켜야 함
    • 이때, 기존 요청을 쿼리 파라미터로 redirectURL로 지정함으로써 로그인한 이후에는 기존 요청 페이지로 리다이렉트 될 수 있도록 처리하는 것이 고객 입장에서 편리 (httpResponse.sendRedirect("/login?redirectURL=" + requestURI);)
    • 여기서는 whitelist를 인터셉터 내에서 명시하지 않았다는 것을 주목
  • WebConfig 내 인터셉터를 등록하기 위해서는 인터셉터들을 addInterceptors 메서드에서 등록해주면 됨
    • 필터와 달리 단 하나의 메서드에서 여러 개 인터셉터 등록 가능
    • 필터와 달리 excludePathPatterns 메서드를 지원하여 whitelist를 WebConfig에서 등록 가능

 

최종 정리

  • 필터는 서블릿에서 제공하며 디스패처 서블릿 호출되기 전에 호출됨
  • 스프링 인터셉터는 Spring MVC에서 제공되며 컨트롤러, 정확히 말하면 HandlerAdapter 호출 직전에 호출됨
  • 이처럼 필터와 인터셉터는 호출 시점이 다르지만 웹 공통 관심 사항을 처리한다는 점은 동일
  • 하지만, 스프링 인터셉터가 필터보다 개발자 친화적으로 다양한 기능을 제공하므로 서블릿이 호출되기 직전에 처리해야 하는 로직이 아니라면 스프링 인터셉터를 활용하는 것을 추천

 

비고

 

PathPattern (Spring Framework 5.3.9 API)

Representation of a parsed path pattern. Includes a chain of path elements for fast matching and accumulates computed state for quick comparison of patterns. PathPattern matches URL paths using the following rules: ? matches one character * matches zero or

docs.spring.io

 

출처

인프런 스프링 MVC 2편 (김영한 강사님)

반응형