본문 바로가기
☕ Java 웹 프로그래밍/Servlet & JSP

[Servlet&JSP] 서블릿 필터(Servlet Filter)

by 일단연 2023. 5. 25.

* 본 글은 [뉴렉처]의 Servlet&JSP 프로그래밍 강의를 듣고 정리한 글입니다.

 

2020 Servlet&JSP 프로그래밍

 

www.youtube.com

 

 서블릿 필터(Servlet Filter) 

  • 사용자로부터 요청이 들어오면 WAS(예: Tomcat)는 적절한 소프트웨어(Servlet)를 실행해 Servlet Container에 담아놓고 결과를 돌려줌
  • Servlet Container: 서블릿을 실행하면 메모리상에 존재하게 되는데, 그 존재하는 공간
    • Servlet이 더이상 실행되지 않으면 Servlet Container에서 삭제됨
  • 멀티 바이트 문자 전송 문제의 해결 방법 2가지
    • 1) Tomcat 홈 디렉토리>conf>server.xml의 <Connector>태그에 URIEncoding="UTF-8" 삽입
      • Tomcat은 여러 애플리케이션을 서비스하는 하나의 컨테이너 > 한 파일 때문에 설정을 바꿔서 다른 파일들에 영향을 주는 건 비효율적
    • 2) 서버 설정을 변경할 수 없는 경우, 서블릿마다 request.setCharacterEncoding(”UTF-8”); 작성
      • 매번 설정하는 것도 비효율적
  • 위의 문제를 해결하기 위해 서블릿 필터(Servlet Filter) 사용

 

서블릿 필터(Servlet Filter)

  • Servlet 2.3부터 추가된 인터페이스
  • 서블릿 실행 전 / 후에 어떤 작업을 할 때 사용
  • 클라이언트(브라우저)가 서버로 요청을 보낼 때, 요청이 서블릿으로 전달되기 전/후에 필터링하기 위한 기술
  • 사용하기 쉽도록 javax.servlet.Filter 인터페이스로 제공하고 있으며 이것을 구현하고 web.xml에 등록하기만 하면 간단하게 사용할 수 있음
  • 필터는 요청이 올 때마다 실행되기도 하지만, Tomcat 서버가 실행될 때도 한 번 실행됨
    • 처음 요청할 때 2번 실행되고, 그 다음 요청부터는 한 번 실행됨

        필터의 기능

  • 서블릿이 호출되기 전에, WAS로부터 전달된 요청(request)를 가로채어 조작/점검
    • 서블릿이 실행되기 전에 필터가 먼저 실행됨
    • 필터가 실행된 후에 자동으로 서블릿이 실행되는 게 아니라, 서블릿의 실행 여부를 필터가 결정함 (인증과 권한을 책임지는 수문장 역할)
  • 웹 컴포넌트(Servlet, Jsp)에서 공통적으로 처리해야 할 기본적인 설정들을 필터에 한 번만 설정하면 됨 (서블릿 각각에 설정할 필요 X)
  • 서블릿이 호출되기 전에 HTTP 요청의 헤더를 조작
  • 서블릿이 호출된 이후 응답(response)를 출력하기 전에 가로채어 조작
    • 서블릿이 실행된 결과 이후에 필터가 실행됨
  • 서블릿이 호출된 이후 응답 헤더를 조작

        필터 사용 예시

  • 로그인여부나 권한 검사와 같은 인증 기능
  • 요청이나 응답에 대한 로그(기록) 기능
  • 오류 처리 기능
  • 데이터 압축이나 변환 기능
  • 인코딩 처리 기능

        필터 흐름

  • HTTP 요청 > WAS > 필터 > 서블릿 > 컨트롤러

        필터 제한

  • HTTP 요청 > WAS > 필터 > 서블릿 > 컨트롤러 //로그인 사용자
  • HTTP 요청 > WAS > 필터(적절하지 않은 요청이라 판단, 서블릿 호출 X) //비 로그인 사용자
    • 필터에서 적절하지 않은 요청이라고 판단하면 거기에서 끝낼 수 있음

        필터 체인

  • HTTP 요청 > WAS > 필터 1 > 필터 2 > 필터 3 > 서블릿 > 컨트롤러
    • 필터는 이런 식으로 체이닝 형태로 추가할 수 있음
    • 예: 로그를 남기는 필터를 먼저 적용한 다음, 로그인 여부를 체크하는 필터를 추가할 수 있음

        필터(Filter) 인터페이스

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

 

서블릿 필터 만들기

  • 클래스 생성할 때 javax.servlet.Filter 인터페이스 구현하도록 설정
    • 필터는 요청이 올 때마다 실행되기도 하지만, Tomcat 서버가 실행될 때도 한 번 실행됨
      • 처음 요청할 때 2번 실행되고, 그 다음 요청부터는 한 번 실행됨
      • 아래의 예시에서는 Tomcat 서버를 실행하면 hello filter가 2번 출력됨
package com.newlecture.filter;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class CharacterEncodingFilter implements Filter {

  @Override
  public void doFilter(ServletRequest request
               , ServletResponse response
               , FilterChain chain)
               throws IOException, ServletException {
    System.out.println("hello filter");
  }
}

   

서블릿 필터 설정하는 방법 1: web.xml 설정의 <filter>태그

        WebContent>WEB-INF>web.xml에서 필터 설정

<filter>
  <filter-name>CharacterEncodingFilter</filter-name>
  <filter-class>com.newlecture.web.filter.CharacterEncodingFilter</filter-class>
</filter>
<filter-mapping>
  <filter-name>CharacterEncodingFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>
  • 요청이 들어오면 URL 매핑에 맞게 무조건 필터가 실행됨
  • 태그 설명
    • <filter>태그
      • <filter-name>태그: <url-pattern>태그의 매핑이 <filter-class>의 클래스에 의해 정의될 수 있도록 이름 지정
      • <filter-class>태그: 어떤 필터 클래스를 사용할지 지정 - 경로까지 다 적어줘야 함 (예: com.newlecture.web.filter.CharacterEncodingFilter)
    • <filter-mapping>태그
      • <filter-name>태그: 어떤 필터에 대해 어떤 매핑을 할 것이다
      • <url-pattern>태그: 어떤 URL 패턴에 대해서만 필터를 적용하도록 설정
        • <url-pattern>/*</url-pattern>: 어떤 요청이 와도 모든 URL 패턴에 필터를 적용
  • 결과 (NoticeReg.java 실행)
    • 필터에 설정해둔 문장이 콘솔에 2번 뜸 (Tomcat 실행할 때 한 번, 요청이 들어올 때 한 번)
    • 브라우저엔 아무것도 뜨지 않음
    • CharacterEncodingFilter는 실행됐으나, NoticeReg는 실행되지 않음

 

필터 설정 후 서블릿이 실행되지 않는 문제 해결

  • 필터(Filter) 인터페이스
    • init( ): 필터 초기화 메소드 서블릿 컨테이너가 생성될 때 호출됨
    • doFilter( ): 고객의 요청이 올 때마다 해당 메서드가 호출됨. 필터의 로직을 구현하면 됨.
    • destroy( ): 필터 종료 메소드. 서블릿 컨테이너가 종료될 때 호출됨
  • 필터가 실행된 후 다음으로 넘겨줄지 말지는 **필터의 체인(FilterChain chain)**이 결정
    • chain.doFilter(request, response);
    • 다음 실행을 관할
    • 조건 검사(화이트박스테스트의 종류)를 통해 다음 실행을 어디로 가게 할 것인지 결정할 수도 있음
      • 예정대로 가게 할 거라면 doFilter( )를 호출
      • 다른 곳으로 전이할 거라면 doFilter( ) 호출하지 않으면 됨
    • doFilter( ) 앞에는 필터가 실행되기 전에 쓸 코드를 넣고, doFilter( ) 뒤에는 필터 실행 후 쓸 코드를 넣음
  • 필터 실행 순서
    • 1) 요청(request)이 오면 doFilter( ) 앞의 필터 코드 실행
    • 2) 흐름을 넘겨 다음 필터나 서블릿 실행
    • 3) 2번을 실행한 결과는 response로 돌아오고 doFilter( ) 뒤의 필터 코드 실행
System.out.println("before filter");  //1번
chain.doFilter(request, response);    //2번
System.out.println("after filter");   //3번
  • NoticeReg에 필터 적용 후 서블릿이 실행되지 않는 문제 해결
    • 서블릿을 실행하기 전에 요청을 UTF-8로 인코딩하는 필터 먼저 실행
public class CharacterEncodingFilter implements Filter {

  @Override
  public void doFilter(ServletRequest request
               , ServletResponse response
               , FilterChain chain)
               throws IOException, ServletException {

    request.setCharacterEncoding("UTF-8");
		
		chain.doFilter(request, response);
  }
}
  • NoticeReg 서블릿에 요청을 보내는 reg.html 실행 결과
    • 1) reg.html 실행
    • 2) 요청이 오니까 CharacterEncodingFilter의 doFilter 앞의 코드(요청을 UTF-8로 인코딩하는 코드) 실행
    • 3) 다음 흐름으로 넘어가 NoticeReg 서블릿 실행
    • 4) 응답을 다시 클라이언트로 보내서 결과 도출
    • NoticeReg 서블릿에서는 입력 값을 가져올 때 인코딩 방식이 UTF-8인지 아닌지를 신경쓸 필요가 없음 (필터가 일괄 처리 해주니까)

        서블릿 필터 설정하는 방법 2: 어노테이션 @WebFilter

  • 1) web.xml의 <filter>, <filter-mapping>태그 제거
  • 2) 필터 클래스 위에 어노테이션 @WebFilter 작성
    • 예: @WebFilter("/*") (어떤 요청이 와도 모든 URL 패턴에 필터를 적용)
    • 어노테이션을 사용하면 불필요한 설정을 할 필요가 없음
  • 결과