서블릿 필터

나무·2023년 11월 20일

스프링 MVC

목록 보기
9/12
post-thumbnail

0. 로그인의 문제

앞선 시간에는 쿠키와 세션을 활용하여 로그인을 구현하였다. 하지만 아직 가장 중요한것을 빼먹었는데 바로 페이지의 접근을 제한하는 것이다.

무슨 말이냐면 "마이페이지" 나 "구매하기" 페이지 같이 반드시 로그인을 한 사람만이 접속 할 수있는 페이지들의 경우 사용자가 요청을 할 경우 이사람이 로그인을 했는지 안했는지를 서버에서 모두 검증을 해줘야한다.

즉, 저번 시간에는 단순히 홈화면에 대해서만 로그인 검증을 했지만, 실제로는 아주 소수를 제외한 모든 페이지들에 대해 로그인 검증 로직을 추가해줘야한다는 뜻이다.

// 로그인 검증 로직
if (loginMember == null) {
	return "home";
}

그런데 페이지가 수십 수백개가 되는 웹사이트의 경우 모든 컨트롤러 메소드에 위와 같은 로직을 추가한다는것은 굉장히 비효율적이고 코드 중복이 심하다.

로그인 검증은 대부분 클라이언트 요청을 받은 직후 컨트롤러의 메인 로직들이 실행되기 전 제일 처음으로 하는 작업이다.

즉, 요청을 받은 직후, 컨트롤러가 동작하기 전에 수행시키는 무언가가 필요로하다.

이런경우 사용되는 기술이 바로 필터인터셉터 이다.

1. 필터

우선 필터부터 살펴보자

Filter 는 앞서 간단히 설명했듯이 수문장과도 같은 역할을 한다. 그렇기에 순서도를 살펴보면

HTTP요청 -> WAS -> Filter -> Servlet -> Controller

순으로 흐름이 진행된다. 여기서 servlet은 dispatcherServlet 을 뜻한다.

필터 제한

이렇기 때문에 로그인의 경우 비회원인지 회원인지 필터는 request세션Id 를 확인한다음, 회원이면 컨트롤러를 실행시키고 그렇지 않으면 바로 거기서 끝내버리는 동작을 수행 할 수 있는것이다.

[성공]
HTTP요청 -> WAS -> Filter (OK) -> Servlet -> Controller

[실패]
HTTP요청 -> WAS -> Filter (Fail)

필터 체인

필터의 경우 체인형식으로 구성을 할 수 있다. 그래서 특정 기준에 대해 한번 거르고, 그 다음 기준으로 또 한번 거르고, 또 다른 기준으로 또 거르는 식으로 filter1 -> filter2 -> filter3,,, 과 같이 연속적으로 필터링을 할 수있도록 지원해준다.

Filter 인터페이스

package hello.login.web.filter;


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

public interface Filter {

    public default void init(FilterConfig filterConfig) throws ServletException {}

    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException;

    public default void destroy() {}
}

Filter 객체의 경우 생성과 소멸을 서블릿 컨테이너가 관리하며 스레드 풀에 미리 스레드를 여러개 생성해놓고 요청할때마다 나누어준다.

스프링 빈과 마찬가지로 싱글톤 으로 생성된다.

init(FilterConfig)

이름에서 알 수 있듯이 filter가 인스턴스화 될 때 제일 처음 수행되는 녀석이다.

서블릿 컨테이너에 의해 수행되는 녀석이며 스프링 코어 시간에 배웠듯이 init() 의 경우 생성자를 통한 초기화 작업 외에 따로 수행해야할 작업만을 분리해서 떨어져나온 메서드로 생성자와 마찬가지로 제일 처음 수행이된다.

default 메서드 이기 때문에 반드시 오버라이드해서 구현할 필요는 없다. 필요할 때만 구현하면 된다.

FilterConfig

Filter에 대한 설정 정보를 가지고 있다.

ServletException

만일 초기화 작업에 실패할 경우 ServletException 이 발생한다.

doFilter()

진짜 filter 기능을 담당하는 메서드이다.

1) 기본적으로는 requestresponse 를 검사해서 필터를 수행한다.

2) 하지만 원한다면 이곳에서 requestresponse 객체의 내용을 개발자의 입맛에 맞춰 변경 할 수 있다.

3) 또한 filter를 끝내고 또 다른 filter를 수행하고 싶다면 파라미터의 chain 객체를 이용해서 chain,doFilter() 다음 filter를 호출 할 수 있다.

4) 만일 필터링 작업중 문제가 발생한다면 다음 filter로 넘어가지 못하고 발생한 지점에서 필터링 작업이 끝난다.

※ 여기서 적용된 디자인패턴이 바로 Chain Of Responsibility 이다

destroy()

init() 과 거의 비슷하게 서블릿 컨테이너에 의해 호출되며 딱 한번만 호출된다.

서블릿은 스레드 풀을 가지고 있기 때문에 다수의 클라이언트의 요청마다 스레드를 나누어주어 병렬처리를 할 수 있다.

그렇기 때문에 한번 필터작업이 다 끝나면 filter 객체는 destroy() 를 호출해 사용되었던 메모리, 스레드, 파일들을 회수해야한다.

이 또한 default 메서드이므로 구현이 강제화 되진 않기에 싱글 스레드 환경에서는 굳이 구현해줄 필요가 없지만 실제 멀티스레드 환경에서는 개발자가 회수하는 로직을 구현해줘야한다.

본 포스트는
김영한의 스프링 MVC 2편 - 백엔드 웹 개발 활용 기술 강의 를 보고 정리했습니다.

profile
🍀 개발을 통해 지속 가능한 미래를 만드는데 기여하고 싶습니다 🍀

0개의 댓글