[Spring]Spring Filter

김피자·2023년 3월 15일
0

Spring

목록 보기
21/28
post-thumbnail

웹 애플리케이션을 개발하다보면 필수적으로 개발해야하는 요소들이 있다.

  • 사용자 인증
  • 권한 검사(인가)
  • 보안
    • XSS, CORS
  • 로깅
  • 이미지 압축 및 데이터 형식 변환

FilterInterceptor은 위 요소들에 대한 책임 및 구현의 역할을 한다.
이를통해 들어온 요청이 DispatcherServlet에 전달되기 전에 헤더를 검사해 인증 토큰이 있는지 없는지, 올바른지 올바르지 않은지 등을 검사할 수 있다.

FilterInterceptor가 없다면 어떨까?

우리는 클라이언트의 요청에 대해 Controller, Service에서 저 동작들을 처리할 수 밖에 없다.
위 요소들은 모든 API에 공통적으로 수행되어야 하는 것들인데 애플리케이션이 커지면 커질수록 코드의 양은 많아지고, 중복이 생기고 애플리케이션이 무거워질 수 밖에 없다.

이 문제를 해결하기 위해서는 관심사의 분리가 필요하다!

관심사의 분리 장점

  • 유지보수가 쉬워진다.
  • 핵심 비즈니스 로직 개발에 집중할 수 있다.
  • 코드의 재사용이 가능하다.

이 글에서는 Filter에 대해서 일단 알아보자.


Spring MVC 내부 동작

스프링 프레임워크는 들어온 요청이 DispatcherServlet에 의해 컨트롤러에 매핑된다. Filter는 이 요청이 DispatcherServlet에 의해 다뤄지기 전, 후에 동작한다.


Filter

Filter는 웹 애플리케이션(web.xml...)에 등록한다.
요청 스레드가 DispatcherServlet에 도착하기 전, 즉 스프링 컨테이너 외부에 존재하여 스프링과 무관한 자원에 대해 동작한다.

Filter에서는 사용자의 요청 정보에 대한 검증, 데이터 추가나 변조, 자원 처리 후 응답 정보에 대한 변경 등의 처리가 가능하다.

주로 전역으로 처리해야하는 인코딩 변환, XSS 방어 등 웹 보안 관련 기능, 인증 기능 등을 수행한다.

Filter 사용하기

간단하게 필터를 사용해보자

firstFilter 생성

import lombok.extern.slf4j.Slf4j;
import javax.servlet.*;
import java.io.IOException;

@Slf4j
public class firstFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        log.info("첫 필터가 생성됐어요! > <");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        log.info("==================필터 시작=====================");
        chain.doFilter(request, response);
        log.info("==================필터 종료=====================");
    }

    @Override
    public void destroy() {
        log.info("첫 필터가 사라집니다!");
    }
}

init() 필터가 생성될 때 수행되는 메소드
doFilter() Request, Response가 필터를 거칠 때 수행되는 메소드
destroy() 필터가 소멸될 때 수행되는 메소드

필터 클래스를 만들었으니 이제 이 필터를 Spring Bean으로 등록해야한다.

Config 생성

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class Config {

    @Bean
    public FilterRegistrationBean myfirstFilter(){
        FilterRegistrationBean registrationBean = new FilterRegistrationBean(new firstFilter());
        return registrationBean;
    }
}

필터를 빈으로 등록하기 위해 스프링 설정에 FilterRegistrationBean을 이용해 직접 만든 필터를 등록할 수 있다.
나는 위에서 만든 firstFilter를 FilterRegistrationBean을 이용해 스프링 필터로 등록했다.

TestController 생성

등록한 필터가 어떻게 동작하는지 보기 위해 임시로 컨트롤러를 만들었다.

import lombok.extern.log4j.Log4j2;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@Log4j2
@RestController
public class TestController {
    @GetMapping("/")
    public String hello(){
        log.info("Hello!!");
        return "Hello!!!";
    }
}

이제 서버를 실행해서 url 요청을 보내면

스프링이 시작하면서 myFilter의 init()메소드가 수행됐고 요청이 들어왔을 때, filter > Controller > filter 순서로 결과가 나온 것을 확인할 수 있다.


Filter 2개 이상 사용하기

secondFilter 생성

import lombok.extern.slf4j.Slf4j;
import javax.servlet.*;
import java.io.IOException;

@Slf4j
public class secondFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        log.info("두번째 필터가 생성됐어요!!!!");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        log.info("==================두번째 필터 시작=====================");
        chain.doFilter(request, response);
        log.info("==================두번째 필터 종료=====================");
    }

    @Override
    public void destroy() {
        log.info("내 두번째 필터가 사라집니다!");
    }
}

Config 수정

	@Bean
    public FilterRegistrationBean myfirstFilter(){
        ....
    }

    @Bean
    public FilterRegistrationBean mySecondFilter(){
        FilterRegistrationBean registrationBean = new FilterRegistrationBean(new secondFilter());
        return registrationBean;
    }

secondFilter도 스프링 빈으로 등록해주고 실행하면

필터가 정상적으로 수행되는 것을 볼 수 있다.
순서는 FilterRegistrationBean의 setOrder() 메소드를 통해 결정할 수 있다.


내부 동작


Filter의 동작 순서를 더 자세히 살펴보자

  1. 서버를 실행시키고 서블릿이 올라오는 동안 Filter.init()이 호출된다.
  2. doFilter가 호출된다.
  3. 컨트롤러에 들어가기 전에 preHandler가 실행된다.
  4. 컨트롤러에서 나와 postHandler, afterCompletion, doFilter 순으로 실행된다.
  5. 컨트롤러의 메소드 처리 결과가 return 되고 화면에 띄워주는 처리가 되기 직전에 preHandler가 호출된다.
  6. 서블릿 종료 시 Filter.destroy가 실행된다.

출처
https://twofootdog.github.io/Spring-%ED%95%84%ED%84%B0%28Filter%29,-%EC%9D%B8%ED%84%B0%EC%85%89%ED%84%B0%28Interceptor%29,-AOP-%EC%B0%A8%EC%9D%B4%EC%A0%90/
https://velog.io/@_koiil/Filter-Interceptor-AOP
https://gardeny.tistory.com/35
https://goddaehee.tistory.com/154

profile
제로부터시작하는코딩생활

0개의 댓글