> For the complete documentation index, see [llms.txt](https://docs.sysout.co.kr/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.sysout.co.kr/web/back-end/what-is-spring-framework/spring-aop/filter.md).

# Filter

## Spring Filter

## 필터의 이해

필터란 사용자의 요청(request)가 처리되기 전에 수행되도록 고안된 도구를 말한다.\
자바 EE에서는 `javax.servlet.Filter` 인터페이스와 구현체로 구성되어 있다. [보기](https://tomcat.apache.org/tomcat-9.0-doc/servletapi/javax/servlet/Filter.html)

웹 프로그래밍에서는 필터를 이용하여 인코딩, 인증, 접근제어 등 다양한 작업들을 처리할 수 있다.\
따라서 필터를 사용하는 방법을 필수적으로 이해해야 한다.

> 참조 이미지(출처 : [https://ko.m.wikipedia.org/wiki/파일:JSP\_Model\_2.svg](https://ko.m.wikipedia.org/wiki/%ED%8C%8C%EC%9D%BC:JSP_Model_2.svg))

![](/files/-MdFjxMDM7in26QNr6Tu)

## Java EE 필터

Java EE에서는 필터를 다음과 같이 사용한다.

```java
import javax.servlet.Filter;

public class CustomFilter implements Filter{
    //생략
}
```

`javax.servlet.Filter` 인터페이스를 상속받아 메소드를 재정의한 뒤 `web.xml`에 등록하면 적용이 완료된다.

재정의할 메소드는 다음과 같다.

* ***void doFilter(ServletRequest, ServletResponse, FilterChain)***
  * 필터에서 수행할 내용에 대해 정의하는 메소드
  * 필수로 재정의 해야하며, `IOException`과 `ServletException`이 발생함
  * `ServletRequest` 는 HttpServletRequest를 up-casting 시킨 객체
  * `ServletResponse` 는 HttpServletResponse를 up-casting 시킨 객체
  * `FilterChain` 은 연속적으로 처리하기 위한 경로 정보를 가지고 있는 객체
    * 요청을 통과 시킬 경우 `chain.doFilter(request, response)` 형태의 명령을 작성
    * 요청을 거절 시킬 경우 `response.sendRedirect(url)` 또는 `response.sendError(status)` 형태의 명령을 작성
* ***void init(FilterConfig)***
  * 필터의 초기 설정을 수행하는 메소드
  * 선택적으로 재정의가 가능하며 `ServletException`이 발생함
  * `FilterConfig`는 Servlet 내장 영역들에 접근할 수 있는 설정 객체
* ***void destroy()***
  * 필터의 종료 설정을 수행하는 메소드
  * 선택적으로 재정의가 가능함
  * 일반적으로 `init`에서 생성한 작업들을 마무리하는 용도로 구현

위의 설명을 토대로 완성한 필터의 일반적인 형태는 다음과 같다.

```java
public class CustomFilter implements Filter{
    @Override
	public void init(FilterConfig filterConfig) throws ServletException {
		
	}
	
	@Override
	public void destroy() {
		
	}
	
	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		
	}
}
```

## Java EE 필터의 등록

필터는 `web.xml` 파일에 등록해야 정상적으로 작동하며, 직접 작성하거나 `Annotation`을 이용하는 방법이 있다.

### web.xml 등록

```xml
<web-app ...>
    
       <!-- ...(생략)... -->

	<filter>
		<filter-name>encoding</filter-name>	
		<filter-class>com.hakademy.CustomFilter</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>encoding</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
    
    <!-- ...(생략)... -->
    
</web-app>
```

각각의 태그에 대한 설명은 다음과 같다.

* **filter**
  * 필터를 등록하기 위한 설정
* **filter-mapping**
  * 필터에 대한 주소 매핑을 위한 설정
* **filter-name**
  * 필터의 이름을 지정하여 필터와 매핑을 연결하기 위한 설정
  * 이름은 애플리케이션 내에서 유일해야함
* **filter-class**
  * 등록할 필터의 실제 클래스 경로
  * 패키지를 포함하여 작성해야 함
* **url-pattern**
  * 필터를 실행할 요청 주소 설정
  * 와일드카드(\*) 기호를 사용하여 범위 지정이 가능

### Annotation을 사용하여 등록

`Annotation`을 사용하면 web.xml에 등록한 효과가 발생하며, 필터에 직접 작성하므로 편리하다는 이점이 있다.

```
@WebFilter(urlPattern="/*")
public class CustomFilter implements Filter{
    //생략
}
```

위와 같이 `urlPattern`만 지정해주면 기본적인 등록이 완료되며, 필요하다면 다른 설정도 해줄 수 있다.

## Java EE url-pattern 작성 규칙

filter/servlet 등록 시 사용 가능한 url pattern의 규칙은 다음과 같다.

* **주소 하나만 지정할 경우 해당 주소만 작성**
  * `/home/test` 라고 설정한 경우 `http[s]://아이피:포트/컨텍스트/home/test`의 경로를 의미
* **와일드카드를 사용할 경우 다음의 규칙을 준수**
  * /로 시작할 경우 \*로 끝나야함
    * 이 경우는 엔드포인트 이후의 모든 내용을 필터에 연결하기 위해서 사용함
    * `/*` , `/member/*`, `/board/reply/*` 등과 같이 사용
  * \*로 시작할 경우 확장자로 끝나야 함
    * 이 경우는 전체 주소에서 특정한 확장자(.jpg, .mp3 등)를 필터에 연결하기 위해 사용함
    * `*.jsp` , `*.mp3` 등과 같이 사용

## Java EE 필터를 스프링에서 사용할 경우의 문제점

스프링은 Java EE Project를 기반으로 내부에서 컨테이너를 구축하여 필요한 작업들을 수행한다.\
Java EE Filter는 web.xml에 직접 등록하여 사용하므로 스프링에서 제어하거나 스프링의 도구들을 이용할 수 없다.\
따라서, 스프링과 연동하여 사용하고 싶은 경우에는 스프링에서 지원하는 프록시 필터(proxy filter) 방식을 이용해야 한다.

## 스프링 프록시 필터

스프링에서 관리할 수 있는 필터를 만들 경우 다음과 같이 클래스를 구성한다.

### 클래스 코드

```java
@Service
public class CustomFilter implements Filter{
    //생략
}
```

Java EE 필터와 동일하게 `javax.servlet.Filter` 를 상속받으며, 차이점은 `@WebFilter` 대신 `@Service`를 이용하여 등록한다는 것이다.\
`@Service` 대신 `@Component`등 다른 등록 애노테이션을 사용해도 된다.

### 등록 코드

등록은 Proxy Filter 형태로 해야하므로 스프링에서 제공되는 클래스를 등록해야한다.\
따라서 `web.xml`에 직접 작성해야 하고 애노테이션 방식은 지원되지 않는다.(`Java Configuration`으로는 등록 가능)

```xml
<filter>
    <filter-name>customFilter</filter-name>
    <filter-class>
        org.springframework.web.filter.DelegatingFilterProxy
    </filter-class>
</filter>
<filter-mapping>
    <filter-name>customFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
```

주의사항은 `<filter-name>`을 작성할 때 반드시 등록된 필터의 bean id를 작성해야 한다는 것이다.\
지금의 경우 `CustomFilter`에 별도의 이름을 부여하지 않았기 때문에 자동적으로 이름은 `customFilter`가 된다.\
따라서 `<filter-name>`에 `customFilter`라고 작성해주면 정상적으로 등록이 이루어진다.

만약 이름을 별도로 부여하고 싶다면 다음과 같이 애노테이션을 작성한다.

```java
@Service("custom")
public class CustomFilter implements Filter{...}
```

```xml
<filter>
    <filter-name>custom</filter-name>
    ...
</filter>
<filter-mapping>
    <filter-name>custom</filter-name>
    ...
</filter-mapping>
```

## 스프링 프록시 필터 등록 원리

<div align="left"><img src="/files/-MdFk-b7ZL1L8IccXBrE" alt=""></div>

필터를 직접 등록하는 것이 아니라 proxy 형태로 등록하기 때문에 web.xml에 `DelegatingFilterProxy` 형태로 필터를 등록하고 연동 설정을 통해 Spring Bean 과 연결되도록 만드는 구조이다.

이처럼 등록할 경우 실제 등록되는 것은 Spring Bean인 `customFilter`이므로 스프링에서 제공하는 기능들(ex : `@Autowired`)을 모두 이용할 수 있다.
