What Is chain.doFilter() Doing in Spring Filter?
1. 소개
이 튜토리얼의 주요 초점은 Spring 프레임워크에서 chain.doFilter() 메서드의 목적을 이해하는 것입니다.
우리가 필터가 무엇인지, 필터 체인이 무엇인지, 그리고 필터를 활용하는 좋은 사용 사례들이 무엇인지 먼저 탐구할 것입니다. 그런 다음 chain.doFilter() 메서드의 목적과 중요성에 대해 논의하겠습니다. 추가로 Spring에서 사용자 정의 필터를 만드는 방법도 살펴볼 것입니다.
마지막으로 행동 설계 패턴 중 하나인 책임 연쇄 패턴과의 상관관계도 탐구할 것입니다.
2. Spring에서 필터란 무엇인가?
Spring 애플리케이션에서 필터는 요청과 응답을 가로채는 객체인 Java Servlet Filters를 기반으로 합니다. 필터는 Java Servlet API의 일부이며, 웹 애플리케이션에서 중요한 역할을 합니다. 필터는 클라이언트와 서버 처리 로직 사이에 위치합니다.
필터를 사용하면 요청이 서블릿에 도달하기 전이나 응답이 생성된 후에 작업을 수행할 수 있습니다. 필터의 일반적인 사용 사례는 다음과 같습니다:
- 인증 및 권한 부여
- 감사 및 로깅
- 요청/응답 수정
비록 Spring 프레임워크의 일부는 아니지만, 필터는 Spring과 완전히 호환됩니다. 우리는 필터를 Spring Beans로 등록하고 애플리케이션에서 사용할 수 있습니다. Spring은 몇 가지 필터 구현을 제공하며, 그 중 일반적인 것들은 OncePerRequestFilter와 CorsFilter입니다.
3. chain.doFilter() 메서드 이해하기
chain.doFilter() 메서드에 들어가기 전에, 필터 체인의 개념과 그 역할을 먼저 이해하는 것이 중요합니다.
필터 체인은 들어오는 요청이나 나가는 응답에 적용되는 처리 로직의 순차적 흐름을 나타냅니다. 즉, 요청을 사전 처리하거나 응답을 사후 처리하는 데 사용되는 필터의 모음입니다. 필터는 잘 정의된 순서로 배열되어 각 필터가 체인의 다음 단계로 요청이나 응답을 전달하기 전에 자신의 처리 로직을 실행하도록 보장합니다.
필터 체인을 정의하기 위해 우리는 Java Servlet API의 FilterChain 인터페이스를 사용하며, 이곳에 우리의 관심 메서드가 포함되어 있습니다. 메서드의 시그니처를 살펴보면:
void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException;
chain.doFilter() 메서드는 요청과 응답을 체인의 다음 필터로 전달합니다. 필터 체인에 필터가 더 이상 없으면 요청은 보통 서블릿인 대상 리소스로 전달되고, 응답은 클라이언트로 전송됩니다.
3.1. 왜 chain.doFilter()를 호출하는 것이 중요한가?
보시다시피, chain.doFilter() 메서드는 요청이 체인에 있는 모든 필터를 통해 계속 진행되도록 보장하므로 필수적인 역할을 합니다. 메서드 호출을 생략하면 요청이 후속 필터나 서블릿으로 진행되지 않는다는 것입니다. 이는 후속 필터가 처리해야 할 로직에 도달할 수 없게 되어 예기치 않은 애플리케이션 동작을 초래할 수 있습니다.
반면에, 인증 또는 권한 부여가 실패한 경우에는 메서드 호출을 건너뛰어 체인을 중단하는 것이 유익할 수 있습니다.
4. 책임 연쇄 패턴
이제 chain.doFilter()가 우리에게 무엇을 가능하게 하는지 이해했으니, 책임 연쇄 패턴과의 연결고리를 간략히 살펴보겠습니다. 자세히 다루지는 않고, 패턴이 무엇인지와 우리의 주제와 어떻게 관련되어 있는지 설명하겠습니다.
책임 연쇄 패턴은 디자인 패턴으로서 객체 간의 상호작용에 초점을 맞추고 있습니다. 이 패턴은 개별 처리 구성 요소인 핸들러들이 요청을 처리하기 위해 순차적으로 조직될 수 있는 방법을 설명합니다. 각 핸들러는 특정 작업을 수행한 후 요청을 다음 핸들러로 전달하여 추가 처리를 결정합니다.
패턴 로직을 서블릿 필터와 비교하면 필터가 책임 연쇄 패턴의 실제 사례라는 것을 알 수 있습니다. 각 필터는 처리 로직의 일부를 담당하는 개별 핸들러로 작용합니다.
이 패턴을 사용하면 유연성의 이점이 있으며, 필터를 추가, 제거 또는 재정렬할 수 있지만 다른 구성 요소를 수정할 필요가 없습니다. 게다가, 책임 연쇄 패턴은 필터가 단일 작업에 집중할 수 있도록 하여 관심사의 분리를 개선합니다.
5. 사용자 정의 필터 구현하기
사용자 정의 필터 구현은 상당히 간단합니다. 몇 가지 단계를 따라야 합니다. 예를 들어, 로그 문구가 있는 두 개의 간단한 필터를 생성하고 chain.doFilter() 메서드 사용 사례를 실습으로 보여줍니다.
5.1. Filter 생성하기
먼저, Filter 인터페이스를 구현하고 doFilter() 메서드를 재정의해야 합니다.
중요한 점은 이 메서드가 필터 체인 안의 doFilter() 메서드와 같지 않다는 것입니다. 필터의 doFilter() 메서드는 필터를 위한 특정 처리 로직을 구현하는 진입점 역할을 하며, chain.doFilter()는 요청과 응답을 체인의 다음 필터로 전달하는 데 사용됩니다.
다음은 필터를 구현하고 @Order 주석을 사용하여 정렬하는 방법입니다:
@Order(1)
@Component
public class FirstFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
LOG.info("Processing the First Filter");
// 의도적으로 chain.doFilter() 생략
}
}
@Order(2)
@Component
public class SecondFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
LOG.info("Processing the Second Filter");
chain.doFilter(request, response);
}
}
5.2. chain.doFilter() 실행 사례
현재 애플리케이션에서 요청을 실행할 때 응답이 반환되지 않는 것을 확인할 수 있습니다. 그 이유는 첫 번째 필터에서 chain.doFilter() 호출을 생략했기 때문에 체인이 끊어졌기 때문입니다. 콘솔을 관찰하면 두 번째 필터의 로그가 누락된 것을 알 수 있습니다:
11:02:35.253 [main] INFO c.baeldung.chaindofilter.FirstFilter - Processing the First Filter
필터 체인을 복구하려면 첫 번째 필터에 메서드 호출을 포함해야 합니다:
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
LOG.info("Processing the First Filter");
chain.doFilter(request, response);
}
이 경우 첫 번째 필터는 요청과 응답을 두 번째 필터로 성공적으로 전달하며 필터 체인은 지속적인 흐름을 유지합니다.
한 번 더 콘솔을 살펴보고 로그 문구가 있는지 확인해보겠습니다:
11:02:59.330 [main] INFO c.baeldung.chaindofilter.FirstFilter - Processing the First Filter
11:02:59.330 [main] INFO c.baeldung.chaindofilter.SecondFilter - Processing the Second Filter
5.3. Filter 등록하기
특히, 우리는 @Component 주석을 사용하여 필터를 Spring Bean으로 생성했습니다. 이 단계는 필터를 서블릿 컨테이너에 등록할 수 있게 해주기 때문에 중요합니다.
필터를 등록하는 다른 방법도 있으며, 이는 더 많은 제어 및 사용자 정의가 가능합니다. 이 방법은 FilterRegistrationBean 클래스를 사용하여 URL 패턴을 지정하고 필터의 순서를 정의할 수 있게 해줍니다.
6. 결론
이 기사에서는 필터, 필터 체인 및 chain.doFilter() 메서드의 올바른 사용에 대해 살펴보았습니다. 그뿐만 아니라 Spring에서 자신의 사용자 정의 필터를 생성하고 등록하는 방법도 보았습니다.
또한, 책임 연쇄 패턴과의 연결을 논의하였고, 필터가 제공하는 유연성과 관심사의 분리를 강조했습니다.
항상 그렇듯이 전체 코드 예제는 GitHub에서 확인할 수 있습니다.