์ต๊ทผ Spring Cloud Gateway๋ ๊ธฐ์กด์ Reactive ๊ธฐ๋ฐ ์ธ์๋ MVC ๊ธฐ๋ฐ์ ๊ตฌํ์ ์ง์ํ๊ฒ ๋์์ต๋๋ค. ์ด๋ฒ ๊ธ์์๋ MVC Gateway์ ๋ฌธ์ ์ ๊ณผ Reactive Gateway์์ ๋น๊ต๋ฅผ ํตํด ์ด๋ค ๋ฐฉ์์ด ๋ ์ ์ ํ์ง ์ดํด๋ณด๊ฒ ์ต๋๋ค.
// Reactive Gateway
implementation 'org.springframework.cloud:spring-cloud-starter-gateway'
// MVC Gateway
implementation 'org.springframework.cloud:spring-cloud-starter-gateway-mvc'
spring:
cloud:
gateway:
routes:
- id: demo
uri: http://localhost:10001
predicates:
- Path=/demo/**
filters:
- StripPrefix=1
spring:
cloud:
gateway:
mvc:
routes:
- id: demo
uri: http://localhost:10001
predicates:
- Path=/demo/**
filters:
- StripPrefix=1
routes๋ spring.cloud.gateway ๋์ spring.cloud.gateway.mvc์์ ์ ์๋ฉ๋๋ค.
Reactive Gateway์์๋ ๋ค์ํ ํํฐ๋ฅผ ์ ๊ณตํฉ๋๋ค. ์์๋ก ๋ชจ๋ ์์ฒญ์ ๋์ํ๋ Global Filter
์ ํน์ ๋ผ์ฐํธ์๋ง ๋์ํ๋ Gateway Filter
๋ฑ์ด ์์ต๋๋ค.
AuthenticationGlobalFilter.java
public class AuthenticationGlobalFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest originalRequest = exchange.getRequest();
...
}
}
MVC Gateway์์๋ ๊ธฐ๋ณธ์ผ๋ก ์ ๊ณตํ๋ ํํฐ๊ฐ ์๊ธฐ ๋๋ฌธ์ OncePerRequestFilter
๊ฐ์ Spring MVC์ ๊ธฐ์กด ํํฐ ํด๋์ค๋ฅผ ์ฌ์ฉํด์ผ ํฉ๋๋ค.
AuthenticationFilter.java
public class AuthenticationFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
...
}
}
MVC์์๋ ๋ชจ๋ ์์ฒญ์ ๋ํด ํํฐ๋ง์ด ์ ์ฉ๋๋ฏ๋ก ํน์ URL ํจํด์ ๋ฑ๋กํ๋ ๊ณผ์ ์ด ํ์ํฉ๋๋ค.
๋ฐ๋ผ์ routes์ ๋ฑ๋กํ ํ ๋ค์ filterConfig์๋ ์ถ๊ฐ ๋ฑ๋กํด์ผ ํ๋ ๋ฒ๊ฑฐ๋ก์์ด ์์ต๋๋ค.
application.yaml
spring:
cloud:
gateway:
mvc:
routes:
- id: demo
uri: http://localhost:10001
predicates:
- Path=/demo/**
filters:
- StripPrefix=1
FilterConfig.java
@Bean
public FilterRegistrationBean<LoggingFilter> loggingFilter() {
FilterRegistrationBean<LoggingFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new LoggingFilter());
registrationBean.addUrlPatterns("/demo/*");
registrationBean.setOrder(1);
return registrationBean;
}
ํ์ง๋ง ๋ชจ๋ ์์ฒญ์ ๋ํด ํํฐ๋ง์ด ์ ์ฉ๋์ด๋ ์๊ด์๊ณ , Health Check์ ๊ฐ์ ํน์ URL๋ง ์ ์ธํ๋ ๋ฐฉ์์ด ๋ ๊ฐ๋จํ๊ณ ์ ์ง๋ณด์๊ฐ ์ฌ์ธ ์ ์์ต๋๋ค.
์ด๋ฅผ ์ํด OncePerRequestFilter
ํด๋์ค์ shouldNotFilter() ๋ฉ์๋๋ฅผ ํ์ฉํ ์ ์์ต๋๋ค.
@Override
protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
return request.getRequestURI().startsWith("/health");
}
Reactive Gateway์์๋ ServerHttpRequest์ mutate() ๋ฉ์๋๋ฅผ ํ์ฉํ์ฌ ์์ฝ๊ฒ ์์ฒญ ํค๋๋ฅผ ์ถ๊ฐํ ์ ์์ต๋๋ค.
ServerHttpRequest mutatedRequest = originalRequest.mutate()
.header("X-Account-Info", encodedInfo)
.build();
MVC Gateway์์๋ HttpServletRequest์ ์ง์ ํค๋๋ฅผ ์ถ๊ฐํ ์ ์๊ธฐ ๋๋ฌธ์, ์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ๋ณ๋์ ๋ํ ํด๋์ค๋ฅผ ๊ตฌํํด์ผ ํฉ๋๋ค.
์์ ์์๋ ์ปค์คํ ModifiableHeaderRequest ๋ํ ํด๋์ค ํ์ฉ
private static class ModifiableHeaderRequest extends HttpServletRequestWrapper {
private final Map<String, String> customHeaders = new HashMap<>();
public ModifiableHeaderRequest(HttpServletRequest request) {
super(request);
}
public void addHeader(String name, String value) {
customHeaders.put(name, value);
}
@Override
public String getHeader(String name) {
return customHeaders.getOrDefault(name, super.getHeader(name));
}
@Override
public Enumeration<String> getHeaderNames() {
Set<String> headerNames = new HashSet<>();
Enumeration<String> originalHeaderNames = super.getHeaderNames();
while (originalHeaderNames.hasMoreElements()) {
headerNames.add(originalHeaderNames.nextElement());
}
headerNames.addAll(customHeaders.keySet());
return Collections.enumeration(headerNames);
}
@Override
public Enumeration<String> getHeaders(String name) {
if (customHeaders.containsKey(name)) {
return Collections.enumeration(Collections.singleton(customHeaders.get(name)));
}
return super.getHeaders(name);
}
}
// ํํฐ์์ ํ์ฉ
ModifiableHeaderRequest modifiableRequest = new ModifiableHeaderRequest(request);
modifiableRequest.addHeader("X-Account-Info", encodedInfo);
ํน์ง | Reactive Gateway | MVC Gateway |
---|---|---|
๋์์ฑ | ๋์ ๋์์ฑ๊ณผ ํ์ฅ์ฑ ์ ๊ณต | Virtual Thread ์ฌ์ฉ ์ ์ฑ๋ฅ ๊ฐ์ ๊ฐ๋ฅ |
๊ธฐ๋ฅ ์ ๊ณต | Spring Cloud Gateway์ ๋ค์ํ ๊ธฐ๋ฅ ์ ๊ณต | ๊ธฐ๋ณธ ํํฐ ๋ฏธ์ ๊ณต์ผ๋ก ์ผ๋ถ ๊ธฐ๋ฅ ์ปค์คํ ํ์ |
์ฝ๋ ๋ณต์ก๋ | ๋ฌ๋ ์ปค๋ธ๊ฐ ๋๊ณ ์ ์ง๋ณด์๊ฐ ์ด๋ ค์ธ ์ ์์ | ์ฝ๋๊ฐ ๋ ์ง๊ด์ ์ด๋ฉฐ ์ ์ง๋ณด์ ์ฉ์ด |
์์ธ ์ฒ๋ฆฌ | ๋ ผ๋ธ๋กํน ๋ฐฉ์์ผ๋ก ์์ธ ์ฒ๋ฆฌ ๋ฐ ์คํ ํธ๋ ์ด์ค ํ์ธ์ด ์ด๋ ค์ธ ์ ์์ | ๋๊ธฐ ์ฝ๋ ๊ธฐ๋ฐ์ผ๋ก ์์ธ ์ฒ๋ฆฌ ๋ฐ ๋๋ฒ๊น ์ด ์ฌ์ |
Spring Cloud Gateway MVC๋ ๊ธฐ์กด์ Spring MVC ๋ฐฉ์๊ณผ ํธํ๋๋ฉด์๋, Reactive ๊ธฐ๋ฐ๊ณผ ๊ฑฐ์ ๋์ผํ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค. ๊ทธ๋ฌ๋ ๋ช ๊ฐ์ง ๋ถ์กฑํ ๊ธฐ๋ฅ(์: ๊ธฐ๋ณธ ํํฐ ์ ๊ณต)์ด ์์ด ์ด๋ฅผ ์ง์ ํด๊ฒฐํด์ผ ํฉ๋๋ค.
default-filter์ ๋ํ ์ด์
https://github.com/spring-cloud/spring-cloud-gateway/issues/3177