[spring boot] 필터(Filter)에 대해서

주현·2024년 1월 10일
2

springboot

목록 보기
7/7

로그인을 옛날에 구현을 해보고 했었는데, spring security에 대한 포스팅을 보고 든 생각이, 로그인 과정에서 굳이 spring security기능을 사용하는 이유가 있을까? 라는 의문이 생기기 시작했다.

로그인 서비스에선 id,password를 dto로 받은 후 id에 해당하는 계정을 db에서 가져와서 확인해서 맞으면, 토큰을 그냥 발급하면 되는거 아닌가? 싶은 생각이 들었다. -> 이에 대한 답변은 LoginService에 해당 로직을 구현하는 것이 적합하지 않다이다. 그 이유는 로그인 되지 않은 사용자의 요청이 스프링 컨테이너 내부까지 들어온다고 한다. 인증되지 않은 사용자의 요청이라면 악의적 의도가 있을 수 있으므로, 비지니스 로직들이 처리되는 스프링 컨테이너에 들이기전에, 검증 단계를 거쳐 차단한다고 한다. 이를 방지하기 위해서 스프링 컨테이너 전에 요청을 처리하는 필터를 도입해야한다고 한다.
그렇다면 이제 필터에 로직을 추가해야하는데, 이를 일일이 구현하는 것 보다는 당연히 미리 만들어진 필터를 사용하면 더욱 유용할 것입니다. 그 외에도 SpringSecurity는 결국 하나의 보안 프레임워크이기 때문에 체계적이며 기본적인 보안 공통 모듈들을 상당히 많이 제공하고 있으며, 기본적인 흐름이 정의되어 있으므로 SpringSecurity를 아는 개발자라면 새로운 프로젝트에 투입되어도 흐름을 따라가기 쉽다는 것입니다. 그래서 필터에 대해서 쪼금 정리를 해보려고 합니다.


필터(Filter)

필터는 디스패처 서블릿에 요청이 전달되기 전/후에 url 패턴에 맞는 모든 요청에 대해 부가작업을 처리할 수 있는 기능을 제공합니다. 디스패티 서블릿은 스프링의 가장 앞단에 존재하는 프론트 컨트롤러이므로, 필터는 스프링 범위 밖에서 처리가 되는 것입니다. 즉!!!! 스프링 컨테이너가 아닌 톰캣과 같은 웹 컨테이너에 의해 관리가 되는 것이고, 디스패치 서블릿 전/후에 처리하는 것입니다.

Filter의 메소드

필터를 추가하기 위해서는 javax.servlet의 Filter 인터페이스 구현해야하며, 3가지 메소드를 가지고 있음.

  • init method
  • doFilter method
  • destroy method

init method

init 메소드는 필터 객체를 초기화하고 서비스에 추가하기위한 메소드임. 웹 컨테이너가 1회 init 메소드를 호출하여 필터 객체를 초기화하면 이후의 요청들은 doFilter를 통해 처리됩니다.

doFilter method

doFilter 메소드는 url-pattern에 모든 http 요청이 디스패처 서블릿으로 전달되기 전에 웹 컨테이너에 의해 실행되는 메소드이다. doFilter의 파라미터로는 FilterChain이 있는데, FilterChain의 doFilter 통해 다음 대상으로 요청을 전달하게 된다. chain.doFilter() 전/후에 우리가 필요한 처리 과정을 넣어줌으로써 원하는 처리를 진행할 수 있다.

destroy method

destroy 메소드는 필터 객체를 서비스에서 제거하고 사용하는 자원을 반환하기 위한 메소드이다.


인터셉터(Interceptor)

인터셉터는 필터와 달리, Spring이 제공하는 기술로써, 디스패처 서블릿이 컨트롤러를 호출하기전 과 후에 요청과 응답을 참조하거나 가공할 수 있는 기능을 제공합니다. 디스패처 서블릿은 핸들러 매핑을 통해 적절한 컨트롤러를 찾도록 요청하는데, 그 결과로 실행체인(HandlerExecutionChain)을 돌려줍니다. 그래서 이 실행 체인은 1개 이상의 인터셉터가 등록되어 있다면, 순차적으로 인터셉터들을 거쳐 컨트롤러가 실행되도록하고, 업다면 바로 컨트롤러를 실행합니다.

인터셉터의 메소드

인터셉터를 추가하기 위해서는 org.springframework.web.wervlet의 HandlerInterceptor 인터페이스를 구현해야 하며, 이는 3가지 메소드를 가지고 있습니다.

public interface HandlerInterceptor {

    default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
        throws Exception {
        
        return true;
    }

    default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
        @Nullable ModelAndView modelAndView) throws Exception {
    }

    default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
        @Nullable Exception ex) throws Exception {
    }
}

preHandle 메소드

preHandle 메소드 컨트롤러가 호출되기 전에 실행됩니다. 그렇기 때문에, 컨트롤러 이전에 처리해야 하는 전처리 작업이나 요청 정보를 가공하거나 추가하는 경우에 사용할 수 있습니다. preHandle의 3번째 파라미터인 handler 파라미터는 핸들러 매핑이 찾아준 컨트롤러 빈에 매핑되는 HandlerMethod라는 새로운 타입의 객체로써, @RequestMapping이 붙은 메소드의 정보를 추상화한 객체이다. 이에 반환 타입은 boolean인데, 참이면 다음 단계, 거짓이면 작업을 중단하여, 이후의 작업은 진행되지 않습니다.

postHandle 메소드

postHandle 메소드는 컨트롤러 호출된 후에 실행된다. 그렇기 때문에 컨트롤러 이후에 처리해야 하는 후처리 작업이 있을 때 사용할 수 있습니다. 이 메소드는 컨트롤러가 반환하는 modelandview 타입의 정보가 제공되는데, 최근에는 json 형태로 데이터를 제공하는 RestAPI기반의 컨트롤러를 만들면서 자주 상용되지 않습니다.또한 컨트롤러 하위 계층에서 작업을 진행하다가 중간에 예외가 발생하면 postHandle은 호출되지 않는다.

afterCompletion 메소드

afterCompletion 메소드는 이름 그대로 모든 뷰에서 최종 결과를 생성하는 일을 포함해 모든 작업이 완료된 후에 실행된다. postHandler과 달리 컨트롤러 하위 계층에서 작업을 진행하다가 중간에 예외가 발생하더라도 afterCompletion은 반드시 호출된다.


필터 vs 인터셉터 차이 및 용도

관리되는 컨테이너

필터와 인터셉터는 관리되는 영역이 다르다. 필터는 스프링 이전의 서블릿 영역에서 관리되며, 인터셉터는 스프링 영역에서 관리되는 영역이다. 그맇기에 필터는 스프링이 처리해주는 내용들을 적용 받을 수 없습니다.

스프링의 예외처리 여부

일반적으로 스프링을 사용한다면 ControllerAdviced와 ExceptionHandler를 이용한 예외처리 기능을 주로 사용합니다. 예를 들어 원하는 멤버를 찾지 못하여 로직에서 에러를 던졌다면 404 응답을 반환받기를 원할 것이다. 하지만 앞서 설명하였듯 필터는 스프링 앞의 서블릿 영역에서 관리되기에 스프링의 지원을 받을 수 없다. 그래서 만약 필터에서 에러가 던져졌다면, 에러가 처리되지 않고 서블릿까지 전달됩니다.
서블릿은 예외가 핸들링 되기를 기대했지만, 예외가 그대로 올라와서 예상치 못한 Exception을 만난 상황이다. 따라서 내부에 문제가 있다고 판단하여 500 Status로 응답을 반환한다. 이를 해결하려면 필터에서 다음과 같이 응답(Response) 객체에 예외 처리가 필요하다.

Request/Response 객체 조작 가능 여부

필터는 Request, Response를 조작할 수 있지만 인터셉터는 조작할 수 없습니다. 여기서 조작한다는 것은 내부 상태를 변경한다는 것이 아니라 다른 객체로 바꿔친다는 의미입니다. 필터가 다음 필터를 호출하기 위해서는 필터 체이닝을 해주어야 합니다. 그리고 이때 Request/Response 객체를 넘겨주므로 우리가 원하는 Request/Response 객체를 넣어줄 수 있다.

하지만 인터셉터는 처리 과정이 필터와 다릅니다. 르다. 디스패처 서블릿이 여러 인터셉터 목록을 가지고 있고, for문으로 순차적으로 실행합니다. 그리고 true를 반환하면 다음 인터셉터가 실행되거나 컨트롤러로 요청이 전달되며, false가 반환되면 요청이 중단된다. 그러므로 우리가 다른 Request/Response 객체를 넘겨줄 수 없습니다. 그리고 이러한 부분이 필터와 확실히 다른 점습니다.

필터와 인터셉터의 용도 및 예시

<필터의 용도 및 예시>

  • 공통된 보안 및 인증/인가 관련 작업
  • 모든 요청에 대한 로깅 또는 감사
  • 이미지/데이터 압축 및 문자열 인코딩
  • Spring과 분리되어야 하는 기능

필터에서는 기본적으로 스프링과 무관하게 전역적으로 처리해야 하는 작업들을 처리할 수 있습니다. 대표적으로 보안 공통 작업이 있습니다. 스프링 컨테이너까지 요청이 전달되지 못하고 차단되므로 안정성을 더욱 높일 수 있습니다. 필터는 이미지나 데이터의 압축이나 문자열 인코딩과 같이 웹 애플리케이션에 전반적으로 사용되는 기능을 구현하기에 적당하다. 

<인터셉터의 용도 및 예시>

  • 세부적인 보안 및 인증/인가 공통 작업
  • API 호출에 대한 로깅 또는 감사
  • Controller로 넘겨주는 정보(데이터)의 가공

인터센터에서는 클라이언트의 요청과 관려되어 전역적으로 처리해야 하는 작업들을 처리할 수 있습니다. 대표적으로 세부적으로 적용해야 하는 인증이나 인가와 같이 클라이언트 요청과 관련된 작업 등이 있다.

-> 대표적으로 필터를 인증과 인가에 사용하는 도구로는 SpringSecurity가 있습니다. SpringSecurity의 특징 중 하나는 Spring MVC에 종속적이지 않다는 것인데, 이러한 이유로는 필터 기반의 인증/인가 처리를 하기 때문입니다.

profile
Just fucking do it!! 개발자가 꿈인 25살 학부생입니다!!

0개의 댓글