@@ -0,0 +1,83 @@
+package nextstep.security.config;
+
+import jakarta.servlet.Filter;
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.ServletRequest;
+import jakarta.servlet.ServletResponse;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.springframework.web.filter.GenericFilterBean;
+
+import java.io.IOException;
+import java.util.List;
+
+public class FilterChainProxy extends GenericFilterBean {
+
+    private final List<SecurityFilterChain> filterChains;
+
+    private VirtualFilterChainDecorator virtualFilterChainDecorator = new VirtualFilterChainDecorator();
+
+    public FilterChainProxy(List<SecurityFilterChain> filterChains) {
+        this.filterChains = filterChains;
+    }
+
+    @Override
+    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
+        HttpServletRequest request = (HttpServletRequest) servletRequest;
+        HttpServletResponse response = (HttpServletResponse) servletResponse;
+
+        List<Filter> filters = getFilters(request);
+        
+        if (filters == null || filters.isEmpty()) {
+            filterChain.doFilter(request, response);
+            return;
+        }
+        this.virtualFilterChainDecorator.decorate(filterChain, filters).doFilter(request, response);
+    }
+
+    private List<Filter> getFilters(HttpServletRequest request) {
+        return this.filterChains.stream()
+                .filter(chain -> chain.matches(request))
+                .findAny()
+                .map(SecurityFilterChain::getFilters)
+                .orElse(null);
+    }
+
+    public static final class VirtualFilterChainDecorator {
+
+        public FilterChain decorate(FilterChain original, List<Filter> filters) {
+            return new VirtualFilterChain(original, filters);
+        }
+
+    }
+
+    private static class VirtualFilterChain implements FilterChain {
+
+        private final FilterChain originalChain;
+        private final List<Filter> additionalFilters;
+        private final int size;
+
+        private int currentPosition = 0;
+
+        public VirtualFilterChain(FilterChain originalChain, List<Filter> additionalFilters) {
+            this.originalChain = originalChain;
+            this.additionalFilters = additionalFilters;
+            this.size = additionalFilters.size();
+        }
+
+        @Override
+        public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
+            if (this.currentPosition == this.size) {
+                this.originalChain.doFilter(request, response);
+                return;
+            }
+            this.currentPosition++;
+            Filter nextFilter = this.additionalFilters.get(this.currentPosition - 1);
+
+            nextFilter.doFilter(request, response, this);
+        }
+
+    }
+
+}

리뷰: > FilterChainDecorator 디자인 패턴의 데코레이터 패턴과는 다른 그냥 래퍼역할을 하는 인터페이스인것 같은데 이름의 연관성이 있는지 궁금합니다.

정석적인 데코레이터 패턴은 아니지만 FilterChain을 감싸서 기존 필터를 수정하지 않고 추가적인 필터를 실행한다는 관점에선 데코레이터의 역할을 하고 있다고 보여집니다.
따라서 `데코레이터 디자인 패턴의 완벽한 구현체이다!`는 아니지만, 데코레이터 패턴에서 얘기하는 핵심 개념인 `원본을 유지한 상태로 확장이 가능하다.` 라는 점에서는 데코레이터라는 이름이 어색하진 않은 것 같다는 의견 드립니다 :)