Spring에서 필터와 인터셉터 사용법

Jang990·2023년 4월 12일
0

필터와 인터셉터

필터는 서블릿이 제공하는 기능이고, 인터셉터는 스프링이 제공하는 기능이다.

클라이언트 요청 -> WAS -> 필터 -> 서블릿 -> 인터셉터 -> 핸들러(컨트롤러)

이미지 출처 - https://mangkyu.tistory.com/173

인터셉터는 디스패처 서블릿에서 핸들러 어댑터를 통해 핸들러를 호출하기 전에 호출이 된다.
하지만 필터는 클라이언트의 요청이 들어와서 WAS를 통해 서블릿으로 가기 전에 호출된다.

필터 사용법

필터 생성

package hello.login.web.test;

import lombok.extern.slf4j.Slf4j;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

@Slf4j
public class LogFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        log.info("필터-init: 필터 초기화 메서드 - 서블릿 컨테이너가 생성될 때 호출");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        // 필터 기능을 여기에 구현하면 된다.
        log.info("필터-doFilter: 클라이언트 요청 시 호출");

        /*
        HttpServletRequest httpRequest = (HttpServletRequest) request; // 캐스팅
        if(httpRequest.getSession(false) == null) {
            log.info("필터-doFilter: 세션이 없는 클라이언트");
            return;
        }
        */

        chain.doFilter(request, response);
        log.info("필터-doFilter: 컨트롤러 로직 이후 호출");
    }

    @Override
    public void destroy() {
        log.info("필터-destroy: 필터 종료 메서드 - 서블릿 컨테이너 종료 시 호출");
    }
}

필터에서 사용하는 request는 HTTP가 아닌 여러 요청을 받기위해 ServletRequest를 사용한다.
그러므로 HTTP 기능을 처리하려면 HttpServletRequest로 다운 캐스팅해준다.

필터 등록

스프링 부트에서 필터를 등록할 때는 FilterRegistrationBean를 빈으로 등록하고 해당 빈에 만든 Filter를 설정해주면 된다.

package hello.login.web;

import hello.login.web.filter.LogFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;

@Configuration
public class WebConfig {
    @Bean
    public FilterRegistrationBean filter1() {
        FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>();
        filterRegistrationBean.addUrlPatterns("/*"); // 필터 적용 Url
        filterRegistrationBean.setOrder(1); // 우선 순위
        filterRegistrationBean.setFilter(new LogFilter()); // 필터 등록
        return filterRegistrationBean;
    }
    
    // 다른 필터
    @Bean
    public FilterRegistrationBean filter2() {
        FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>();
        ...
        return filterRegistrationBean;
    }
}

만약 여러 필터를 등록할 때는 여러 Bean을 등록하면 된다.

인터셉터 사용법

인터셉터 생성

package hello.login.web.test;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Slf4j
public class LogInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.info("인터셉터-preHandle: 컨트롤러 호출 전에 호출(정확히는 핸들러 어댑터 호출 전에 호출)");
        /*
        // 실제 인터셉터 로직
        if(request.getSession(false) == null) {
            log.info("인터셉터: 세선이 없는 사용자");
            // false를 반환하면 여기서 여기서 끝남
            return false;
        }
        */

        // true를 반환하면 진행하며 컨트롤러까지 진행
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        log.info("인터셉터-postHandle: 컨트롤러 호출 후에 호출(정확히는 핸들러 어댑터 호출 후 호출)");
        // exception이 핸들러에서 발생해서 던져진다면 호출되지 않음
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        log.info("인터셉터-afterCompletion: 뷰가 렌더링 된 이후 호출");
        // exception이 핸들러에서 발생해서 던져진다해도 호출된다.
        // ex로 던져진 예외를 볼 수 있다.
    }
}

인터셉터 등록

WebMvcConfigureraddInterceptors를 구현하고 register에 인터셉터를 등록하면 된다.

package hello.login.web;

import hello.login.web.test.LogFilter;
import hello.login.web.test.LogInterceptor;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import javax.servlet.Filter;

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LogInterceptor())
                .addPathPatterns("/**")
                .excludePathPatterns("/css/**", "/members/add") // 이 부분이 필터와 차별화된 기능
                .order(1);
    }
    
    ...
}

excludePathPatterns와 같이 인터셉터를 적용하지 않을 부분은 따로 빼낼 수 있다. 이 부분은 필터와 차별화된 기능이다.
이외에도 인터셉터는 추가적인 기능을 제공한다.

필터-인터셉터-컨트롤러 실행 흐름

로그를 확인하기 위해 다음과 같은 컨트롤러를 추가했다.

로그를 확인해보자.

뭐가 더 좋을까?

인터셉터는 필터에 비해 훨씬 다양한 기능을 제공한다.
필터보다는 인터셉터를 사용하는 것이 훨씬 좋다.
특별한 경우가 아니라면 인터셉터를 사용하는 것이 좋다.

내용 출처

인프런 - 스프링 MVC 2편 - 백엔드 웹 개발 활용 기술

profile
공부한 내용을 적지 말고 이해한 내용을 설명하자

0개의 댓글