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 |