Spring

[Spring Cloud Gateway] 2. Server WebFlux

noahkim_ 2026. 8. 20. 22:19

1. How It Works

동작 순서

순서 구성요소 역할
1 Client Gateway로 HTTP Request 전송
2 Gateway Handler Mapping 요청이 어떤 Route와 매칭되는지 판단
3 Gateway Web Handler 매칭된 Route의 GlobalFilter + GatewayFilter를 모아 Filter Chain 실행
4 Pre Filter 인증, Rate Limit, Header/Path 수정 등 Backend 호출 전 처리
5 Proxy Request 실제 Downstream Service로 HTTP Request 전달
6 Post Filter Backend Response가 돌아온 뒤 Header 추가, Logging 등 후처리
7 Client 최종 HTTP Response 수신

 

2. Route Predicate Factories

  • WebFlux의 HandlerMapping 인프라 안에서 Route를 매칭하고, 여러 Predicate를 AND 조건으로 조합할 수 있음

 

Predicate

  • 여러 Predicate를 설정하면 AND 조건이 적용됨
  • ✅ Path: spring.webflux.base-path가 설정되어 있으면 해당 Prefix까지 포함해서 매칭됨
Predicate 조건 주요 인자 핵심
After 특정 시각 이후 요청 ZonedDateTime 지정 시각 이후에만 Route 매칭
Before 특정 시각 이전 요청 ZonedDateTime 지정 시각 이전에만 Route 매칭
Between 특정 시간 구간 요청 시작/종료 ZonedDateTime 유지보수 시간대 등 기간 한정 Route
Cookie Cookie 이름/값 Cookie명, Regex 특정 Cookie 값이 Regex와 일치할 때
Header Header 이름/값 Header명, Regex 특정 Header 값이 Regex와 일치할 때
Host Host Header Host Pattern 특정 Domain/Subdomain 요청 매칭
Method HTTP Method GET, POST 특정 Method만 Route 매칭
Path Request Path Path Pattern 특정 URL Path와 일치할 때
Query Query Parameter 이름, 선택적 Regex 특정 Query Parameter 존재/값 검사
RemoteAddr 접속 IP CIDR 요청의 Remote IP가 특정 대역인지 검사
Weight Traffic 비율 Group, Weight 같은 Group 내 Route로 트래픽 비율 분산
XForwardedRemoteAddr X-Forwarded-For의 IP CIDR Proxy 앞 실제 IP 정보를 기준으로 매칭
ReadBody Request Body 내용 Body Type, Predicate<T> Body를 읽고 Java Predicate로 Route 결정

 

예시) Path + Method + Header + Cookie

더보기
.route("user-api-route", spec -> spec
        .path(apiPathProperties.user().pattern())
        .and()
        .method(HttpMethod.GET)
        .and()
        .header("X-Client-Type", "web|mobile")
        .and()
        .cookie("SESSION", ".+")
        .uri("lb://user-service")
)

 

 

예시) Path, Host: {} 변수 사용

더보기
.route("user-route", spec -> spec
        .path("/user/{id}")
        .filters(f -> f.filter((exchange, chain) -> {
            Map<String, String> variables =
                    ServerWebExchangeUtils.getUriTemplateVariables(exchange);

            String id = variables.get("id");

            return chain.filter(exchange);
        }))
        .uri("lb://user-service")
)
  • 추출된 값이 ServerWebExchange Attribute에 저장됨
  • GatewayFilter에서 사용 가능

 

예시) Host + Query + RemoteAddr

더보기
.route("market-internal-route", spec -> spec
        .host("api.crypto.com")
        .and()
        .query("type", "realtime")
        .and()
        .remoteAddr("10.0.0.0/8")
        .uri("lb://market-service")
)

 

예시) After + Before

더보기
ZonedDateTime openAt = ...;
ZonedDateTime closeAt = ...;

.route("event-route", spec -> spec
        .after(openAt)
        .and()
        .before(closeAt)
        .uri("lb://user-service")
)

 

예시) Between + ReadBody

더보기
ZonedDateTime start = ...;
ZonedDateTime end = ...;

.route("event-promotion-route", spec -> spec
        .between(start, end)
        .and()
        .readBody(String.class,
                body -> body.contains("\"event\":true"))
        .uri("lb://user-service")
)
  • 이벤트 기간 내이면서 Request Body가 조건을 만족할 때만 매칭

 

예시) RemoteAddr: XForwardedRemoteAddr + Weight

더보기

Gateway 앞에 Load Balancer가 있고, 새 market-service로 일부 트래픽을 보내는 상황.

.route("market-v1-route", spec -> spec
        .xForwardedRemoteAddr("192.168.0.0/16")
        .and()
        .weight("market-version", 8)
        .uri("lb://market-service-v1")
)
.route("market-v2-route", spec -> spec
        .xForwardedRemoteAddr("192.168.0.0/16")
        .and()
        .weight("market-version", 2)
        .uri("lb://market-service-v2")
)
  • XForwardedRemoteAddr: Proxy/LB가 앞에 있을 때 X-Forwarded-For를 기준으로 IP 조건을 검사하는 Predicate
  • ➡️ X-Forwarded-For 허용 대역 AND market-version 그룹 Weight → V1 약 80% / V2 약 20%

 

3. GatewayFilter Factories

  • 특정 Route의 Request/Response에 적용할 Filter를 만들어주는 Spring Cloud Gateway의 기본 제공 기능

 

RequestRateLimiter GatewayFilter Factory

  • 현재 요청을 제한할 대상을 찾고(Key), 설정된 RateLimiter에게 허용 여부를 물어보는 GatewayFilter
순서 컴포넌트 역할 핵심
1 RequestRateLimiter Rate Limit 처리 시작 해당 Route에 설정된 GatewayFilter
2 KeyResolver 요청의 제한 기준 Key 추출 ServerWebExchange → Mono<String>
3 RateLimiter 해당 Key의 요청 허용 여부 판단 Redis / Bucket4j / Custom 구현체 중 하나
4 RateLimiter 허용 여부 반환 ALLOW 또는 DENY
5-A Filter Chain 허용된 요청을 계속 처리 다음 Filter → Backend Proxy
5-B RequestRateLimiter 제한된 요청 차단 기본 429 Too Many Requests

 

표) 설정

더보기
설정 의미
keyResolver 누구를 기준으로 제한할지 결정
statusCode 제한됐을 때 반환할 HTTP Status, 기본 429
throwOnLimit false: Status만 설정 / true: 제한 시 Exception 발생
deny-empty-key KeyResolver가 Key를 못 찾았을 때 요청을 거부할지 여부
empty-key-status-code Key가 없어서 거부할 때 반환할 Status
  • (기본) PrincipalNameKeyResolver: ServerWebExchange → Principal → Principal.getName() → Key

 

구현체) Redis RateLimiter

더보기
  • Redis RateLimiter 구현체 제공
  • ✅ Redis에 Token Bucket 상태를 저장하고 요청 허용 여부를 판단하는 RateLimiter 구현체
  •  Token Bucket 알고리즘 사용

 

설정) Redis RateLimiter

더보기
implementation 'org.springframework.boot:spring-boot-starter-data-redis-reactive'
@Configuration
public class RateLimitConfig {

    @Bean
    public KeyResolver userKeyResolver() {
        return exchange ->
                exchange.getPrincipal()
                        .map(Principal::getName);
    }

    @Bean
    public RedisRateLimiter redisRateLimiter() {
        // 초당 10 Token 충전 / 최대 20 Token 저장 / 요청당 1 Token 소비 
        // → Token이 충분하면 순간 최대 20개 요청 허용
        return new RedisRateLimiter(
                10, // replenishRate
                20, // burstCapacity
                1   // requestedTokens
        );        
    }

    @Bean
    public RouteLocator userRoutes(
            RouteLocatorBuilder builder,
            KeyResolver userKeyResolver,
            RedisRateLimiter redisRateLimiter
    ) {
        return builder.routes()
                .route("user-route", spec -> spec
                        .path("/user/**")
                        .filters(f -> f
                                .requestRateLimiter(config -> {
                                    config.setKeyResolver(userKeyResolver);
                                    config.setRateLimiter(redisRateLimiter);
                                })
                                .addRequestHeader("X-From", "gateway")
                        )
                        .uri("lb://user-service")
                )
                .build();
    }
}

설정 역할 핵심
replenishRate Token 충전 속도 초당 몇 개의 Token을 충전할지
burstCapacity Bucket 최대 용량 Bucket에 최대 몇 Token까지 저장할 수 있는지
requestedTokens 요청당 소비량 Request 1개가 몇 Token을 소비하는지, 기본 1

 

4. Global Filters

  • 모든 Route에 공통으로 적용되는 Filter
  • ✅ Request → GlobalFilter → Route별 GatewayFilter → Backend → Response → GlobalFilter → Client

 

우선순위

  • Ordered 인터페이스를 구현하여 우선순위 부여할 수 있음
  • ✅ 우선순위가 높을수록 Pre는 먼저, Post는 마지막에 실행됨

 

구현체

필터 종류 설명 특징
GatewayMetricsFilter Gateway 요청의 Route, HTTP Method, Status, 처리 결과 등을 Metric으로 수집 Actuator와 연동해 Gateway 상태 모니터링 가능
LocalResponseCacheFilter 조건을 만족하는 GET Response를 Gateway 로컬 Cache에 저장 Caffeine 기반, TTL/Cache 크기 설정 가능
ForwardRoutingFilter forward:///... 요청을 Gateway 내부 DispatcherHandler로 전달 외부 Downstream이 아니라 Gateway 내부 Handler로 Forward
RouteToRequestUrlFilter 매칭된 Route의 URI를 이용해 실제 Routing에 사용할 Request URI 생성 생성된 URI를 GATEWAY_REQUEST_URL_ATTR에 저장
ReactiveLoadBalancerClientFilter lb://service-name을 실제 Service Instance의 Host/Port로 변환 Spring Cloud LoadBalancer 사용, 인스턴스 없으면 기본 503
NettyRoutingFilter http/https 요청을 Reactor Netty HttpClient로 Downstream에 전달 실제 HTTP Proxy 요청 수행
NettyWriteResponseFilter Downstream에서 받은 Netty Response를 Client Response에 기록 Downstream 응답을 Client Response에 기록
WebsocketRoutingFilter ws/wss 요청을 Downstream WebSocket Server로 전달 lb:ws://service 형태로 Load Balancing 가능

 

설정) GatewayMetricsFilter

더보기
implementation 'org.springframework.boot:spring-boot-starter-actuator'
  • "/actuator/metrics/spring.cloud.gateway.requests" 경로로 요청함
  • "spring.cloud.gateway.requests" 메트릭 확인

 

설정) LocalResponseCacheFilter

더보기
implementation 'com.github.ben-manes.caffeine:caffeine'
implementation 'org.springframework.boot:spring-boot-starter-cache'
spring:
  cloud:
    gateway:
      global-filter:
        local-response-cache:
          enabled: true
spring:
  cloud:
    gateway:
      filter:
        local-response-cache:
          size: 100MB
          time-to-live: 5m
  • Cache-Control에서 Cache 허용해야 사용 가능
  • Body 없는 GET 에서 유용함 (200, 206, 301)

 

예시) ForwardRoutingFilter

더보기
forward:///internal/users
  • Request → ForwardRoutingFilter → DispatcherHandler → Gateway 내부 Controller / Handler

 

예시) RouteToRequestUrlFilter

더보기
  • .uri("lb://user-service") 이면 이 Filter가 Routing용 URI를 만들어 GATEWAY_REQUEST_URL_ATTR에 저장함
  • Route URI → RouteToRequestUrlFilter → 실제 Routing에 사용할 URI 생성

 

ServerWebExchangeUtils

  • Gateway 내부 Routing Filter들이 상태를 공유할 떄 쓰는 유틸리티
  •  Routing 완료 여부를 ServerWebExchange에 표시
  • ✅ 한 요청이 여러 Routing Filter에 의해 두 번 이상 Downstream으로 전달되는 걸 막기 위한 표시
  • ➡️ Custom Routing Filter를 직접 만들 때 중요

 

코드) ServerWebExchangeUtils

더보기
ServerWebExchangeUtils.isAlreadyRouted(exchange); // 이미 라우팅되었는지 확인
ServerWebExchangeUtils.setAlreadyRouted(exchange); // 해당 요청이 라우팅 완료되었음을 표시

 

5. HttpHeadersFilters

  • Gateway가 Downstream으로 요청을 보내기 직전에 Http Header를 정리하거나 추가하는 FIlter

 

구현체

필터 종류 설명 특징
ForwardedHeadersFilter 표준 Forwarded Header 생성 원래 요청의 Host, Scheme, Port 정보를 Downstream에 전달
RemoveHopByHopHeadersFilter Proxy 사이에서 전달하면 안 되는 Header 제거 Connection, Keep-Alive, Transfer-Encoding 등 기본 제거
XForwardedHeadersFilter X-Forwarded-* Header 생성 원래 Client의 Host, Protocol, Port, Path 등의 정보 전달

 

설정) ForwardedHeadersFilter

더보기
spring:
  cloud:
    gateway:
      server:
        webflux:
          trusted-proxies: "..."
spring:
  cloud:
    gateway:
      server:
        webflux:
          forwarded:
            by:
              enabled: true

 

설정) RemoveHopByHopHeadersFilter

더보기
spring:
  cloud:
    gateway:
      server:
        webflux:
          filter:
            remove-hop-by-hop:
              headers:
                - Connection
                - Keep-Alive

 

설정) XForwardedHeadersFilter

더보기
spring:
  cloud:
    gateway:
      server:
        webflux:
          x-forwarded:
            for-enabled: true
            host-enabled: true
            port-enabled: true
            proto-enabled: true
            prefix-enabled: true
X-Forwarded-For     → 원래 Client IP
X-Forwarded-Host    → 원래 Host
X-Forwarded-Port    → 원래 Port
X-Forwarded-Proto   → 원래 Protocol(http/https)
X-Forwarded-Prefix  → 원래 Path Prefix
X-Forwarded-For: 203.0.113.10
X-Forwarded-Host: api.crypto.com
X-Forwarded-Port: 443
X-Forwarded-Proto: https

 

 

출처

'Spring' 카테고리의 다른 글

[Spring WebClient] 1. Request / Response  (0) 2026.08.19
[Spring WebFlux] 3. DispatcherHandler  (0) 2026.08.19
[Spring WebFlux] 2. Reactive Core  (0) 2026.08.19
[Spring WebFlux] 1. Overview  (0) 2026.08.18