๐ŸŒ Spring Cloud Gateway MVC - Reactive์—์„œ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜

์กฐ์ œยท2025๋…„ 3์›” 26์ผ
0

์ตœ๊ทผ Spring Cloud Gateway๋Š” ๊ธฐ์กด์˜ Reactive ๊ธฐ๋ฐ˜ ์™ธ์—๋„ MVC ๊ธฐ๋ฐ˜์˜ ๊ตฌํ˜„์„ ์ง€์›ํ•˜๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฒˆ ๊ธ€์—์„œ๋Š” MVC Gateway์˜ ๋ฌธ์ œ์ ๊ณผ Reactive Gateway์™€์˜ ๋น„๊ต๋ฅผ ํ†ตํ•ด ์–ด๋–ค ๋ฐฉ์‹์ด ๋” ์ ์ ˆํ•œ์ง€ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

๐Ÿ› ๏ธ build.gradle

    // Reactive Gateway
    implementation 'org.springframework.cloud:spring-cloud-starter-gateway'
    
    // MVC Gateway
    implementation 'org.springframework.cloud:spring-cloud-starter-gateway-mvc'

๐Ÿ”€ Route

Reactive Gateway์˜ application.yaml

spring:
  cloud:
    gateway:
      routes:
        - id: demo
          uri: http://localhost:10001
          predicates:
            - Path=/demo/**
          filters:
            - StripPrefix=1

MVC Gateway์˜ application.yaml

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์—์„œ ์ •์˜๋ฉ๋‹ˆ๋‹ค.

๐Ÿงฐ Filter

Reactive Gateway

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

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 {
    	...
    }
    
}

๐Ÿš‘ Health Check์™€ ๊ฐ™์€ ์š”์ฒญ์—๋„ ํ•„ํ„ฐ๊ฐ€ ๊ฑธ๋ฆฌ๋Š” ๋ฌธ์ œ

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");
    }

๐Ÿ“ก Request

Reactive Gateway

Reactive Gateway์—์„œ๋Š” ServerHttpRequest์˜ mutate() ๋ฉ”์„œ๋“œ๋ฅผ ํ™œ์šฉํ•˜์—ฌ ์†์‰ฝ๊ฒŒ ์š”์ฒญ ํ—ค๋”๋ฅผ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

	ServerHttpRequest mutatedRequest = originalRequest.mutate()
    	.header("X-Account-Info", encodedInfo)
        .build();

MVC Gateway

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 GatewayMVC Gateway
๋™์‹œ์„ฑ๋†’์€ ๋™์‹œ์„ฑ๊ณผ ํ™•์žฅ์„ฑ ์ œ๊ณตVirtual Thread ์‚ฌ์šฉ ์‹œ ์„ฑ๋Šฅ ๊ฐœ์„  ๊ฐ€๋Šฅ
๊ธฐ๋Šฅ ์ œ๊ณตSpring Cloud Gateway์˜ ๋‹ค์–‘ํ•œ ๊ธฐ๋Šฅ ์ œ๊ณต๊ธฐ๋ณธ ํ•„ํ„ฐ ๋ฏธ์ œ๊ณต์œผ๋กœ ์ผ๋ถ€ ๊ธฐ๋Šฅ ์ปค์Šคํ…€ ํ•„์š”
์ฝ”๋“œ ๋ณต์žก๋„๋Ÿฌ๋‹ ์ปค๋ธŒ๊ฐ€ ๋†’๊ณ  ์œ ์ง€๋ณด์ˆ˜๊ฐ€ ์–ด๋ ค์šธ ์ˆ˜ ์žˆ์Œ์ฝ”๋“œ๊ฐ€ ๋” ์ง๊ด€์ ์ด๋ฉฐ ์œ ์ง€๋ณด์ˆ˜ ์šฉ์ด
์˜ˆ์™ธ ์ฒ˜๋ฆฌ๋…ผ๋ธ”๋กœํ‚น ๋ฐฉ์‹์œผ๋กœ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ ๋ฐ ์Šคํƒ ํŠธ๋ ˆ์ด์Šค ํ™•์ธ์ด ์–ด๋ ค์šธ ์ˆ˜ ์žˆ์Œ๋™๊ธฐ ์ฝ”๋“œ ๊ธฐ๋ฐ˜์œผ๋กœ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ ๋ฐ ๋””๋ฒ„๊น…์ด ์‰ฌ์›€

๐Ÿ“ ์ •๋ฆฌ

Spring Cloud Gateway MVC๋Š” ๊ธฐ์กด์˜ Spring MVC ๋ฐฉ์‹๊ณผ ํ˜ธํ™˜๋˜๋ฉด์„œ๋„, Reactive ๊ธฐ๋ฐ˜๊ณผ ๊ฑฐ์˜ ๋™์ผํ•œ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋ช‡ ๊ฐ€์ง€ ๋ถ€์กฑํ•œ ๊ธฐ๋Šฅ(์˜ˆ: ๊ธฐ๋ณธ ํ•„ํ„ฐ ์ œ๊ณต)์ด ์žˆ์–ด ์ด๋ฅผ ์ง์ ‘ ํ•ด๊ฒฐํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

  • ๋†’์€ ๋™์‹œ์„ฑ๊ณผ ํ™•์žฅ์„ฑ์ด ํ•„์š”ํ•˜๋‹ค๋ฉด Reactive Gateway๋ฅผ ์„ ํƒํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.
  • ์ฝ”๋“œ์˜ ๊ฐ€๋…์„ฑ๊ณผ ์œ ์ง€๋ณด์ˆ˜์„ฑ์„ ๊ณ ๋ คํ•œ๋‹ค๋ฉด MVC Gateway๊ฐ€ ์ ํ•ฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

default-filter์— ๋Œ€ํ•œ ์ด์Šˆ
https://github.com/spring-cloud/spring-cloud-gateway/issues/3177

profile
์กฐ์ œ

0๊ฐœ์˜ ๋Œ“๊ธ€