배경

스프링 클라우드 게이트웨이에서 처리율 제한 필터를 적용하였습니다.

처리율 제한 필터에서 제한을 초과하면 HTTP 429 (Too Many Requests) 응답을 반환하도록 설정하였습니다.

그리고 처리율 제한 필터 이전 단계에 위치한 다른 필터에서는, 응답 코드가 429일 경우 리디렉션(HTTP 303 See Other) 으로 전환하여 다른 URL로 유도하는 역할을 하도록 구현하였습니다.

최초 작성한 코드는 다음과 같습니다:

@Override
public GatewayFilter apply(Config config) {
    return (exchange, chain) ->
        chain.filter(exchange).then(Mono.defer(() -> {
            ServerHttpResponse response = exchange.getResponse();
            HttpStatus status = (HttpStatus) response.getStatusCode();

            if (status != null && status.value() == 429) {
                response.setStatusCode(HttpStatus.SEE_OTHER); // 303 redirect
                response.getHeaders().set("Location", config.getRedirectUrl());
                return response.setComplete();
            }
            return Mono.empty();
        }));
}

그러나 실행 시 아래와 같은 오류가 발생하였습니다:

java.lang.UnsupportedOperationException: null
	at org.springframework.http.ReadOnlyHttpHeaders.set(ReadOnlyHttpHeaders.java:112) ~[spring-web-6.2.8.jar:6.2.8]
	Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
Error has been observed at the following site(s):
	*__checkpoint ⇢ org.springframework.cloud.gateway.filter.WeightCalculatorWebFilter [DefaultWebFilterChain]
	*__checkpoint ⇢ HTTP POST "/api/reservation" [ExceptionHandlingWebHandler]

전개

예외의 원인

예외는 다음 코드에서 발생했습니다:

response.getHeaders().set("Location", config.getRedirectUrl());

response.getHeaders( ) 로 반환된 헤더는 ReadOnlyHttpHeaders 로 불변 상태인 객체에 set( ) 메서드를 호출했기 때문입니다. Spring WebFlux에서는 응답이 이미 commit된 이후에는 헤더 수정 불가 합니다.

<aside> 💡

응답이 commit된다는게 무슨 의미인가요?

commit = 응답이 확정되어 클라이언트 전송이 시작됨

Spring WebFlux에서의 동작

WebFlux는 비동기 스트림 기반