본문 바로가기
spring & boot/Spring & Spring Boot

[Spring Cloud] Spring Cloud Gateway - 개념 & 예제(로깅, 모니터링)

by lucas_owner 2025. 3. 18.

 

 

Spring Cloud Gateway

Spring Cloud Gateway 의 기본 개념과, 간단한 예제로 사용해보도록하자.

 

 

1. Gateway 란?

Gateway, 단어 그대로의 의미로 '관문', '통로' 라는 의미를 가지고 있다. 네트워크 개념에서의 Gateway 란

데이터(패킷)이 지나가는 통로이며, 출발지 Network 에서 도착지 Network 에 도달하기 까지 거쳐야하는 필수적인 통로(관문)이다.

즉 서로다른 네트워크간 통로의 역할을 수행하는것이다.

 

일상생활에서 흔하게 볼 수 있는 Wifi 공유기를 보자면 이는 Router 라고 하며, Gateway 의 개념과는 조금 다른 부분이다.

Gateway 는 특정 장비, 기기가 아닌 IP 주소를 기반으로 찾아가며

Router 는 외부 네트워크(WAN)를 내부 네트워크(LAN)를 연결해주는 장치이다.

다만 Wifi 공유기 즉 Router 가 내부 네트워크 기기(장비)들을 외부 네트워크와 통신하게 하기에 어떻게 보면 Gateway 라고도 할 수 있겠다.

(외부 네트워크와 직접연결되었다면 Gateway 라고 할 수 있고, 기존 공유기에 또다른 공유기가 연결되어 있다면 Gateway라 할 수 없음)

 

일반적으로 내부 네트워크의 192.168.x.1 이 공유기 IP 즉 Gateway IP 이며 (netstat -rn, route -n) 과 같은 명령어를 통해 Wifi 공유기 IP, Gateway 정보를 확인할 수 있다.

 

 

 

2. API Gateway ?

API Gateway 는 Client 의 요청을 받아서 적절한 Server(Service API) 로 Routing 하는 개념이다.

Wifi 공유기가 Network Gateway 역할을 하는것 처럼, API Gateway 는 API Request 의 Gateway 역할을 수행한다.

즉 Client 입장에서는 모든 Server 의 Endpoint 를 알고 있을 필요 없이 Gateway Endpoint 만 알고 있다면, 내부 서비스 구조에 상관없이 적절한 Response 를 받을 수 있다.

 

아래의 그림은 Spring Cloud 의 구조이다.

추후 기술하겠지만, MSA 환경에서 사용하기 좋은 아키텍처라고 할 수 있다.

 

 

 

3. Spring Cloud Gateway

Spring Cloud Gateway(SCG) 는 Spring Framework 에서 제공하는 API Gateway 서비스이다.

간단하면서, 효율적인 API Routing Solution 을 제공한다. Spring Eureka 등을 활용하여, Spring Framework 생태계에서 좀 더 많은 활용을 할 수 있다.

 

- API Gateway 가 필요한 이유?

MSA 환경에서는, 여러개로 나뉘어진 Server Modules 가 존재하게 되며, 각 Service 들이 Client 와 직접 통신하게 된다면 복잡도가 증가하고, 보안 및 성능 문제가 발생할 수 있다. 

또한 개발자들이 많이 고려하는 부분중 하나인 DRY(Do not Repeat yourself) 즉 공통의 관심사를 해결할 수 있는 좋은 해결방안도 된다.

1. Logging, Monitoring : Request, Response 에 대한 로깅, 모니터링을 한곳에서 수행할 수 있다.
2. 보안강화 : 모든 Request 를 Gateway 에서 체크하여 거부,허용 여부 결정
3. 부하 분산 : Load Balancing 기능 사용
4. 각 Service Module 의 Endpoint 를 단일 Endpoint 로 관리 사용

 

 

SCG 는 WebFlux(Netty) 비동기 방식으로 동작하며, 수 많은 Request 작업을 처리하기에 적절하다.

이전에 사용하던 Spring Cloud Zuul 은 Tomcat, MVC 방식으로 동작했으며 현재는 SCG 로의 이전이 완료된 상태이다.

즉, 단일 진입점(EndPoint) 의 역할로서는 최고의 효율을 보일 수 있다.

 

 

- 구조 및 장단점

SCG 는 Client 의 Request 를 목적 Serivce 까지 보내기전에 Filtering 을 할 수 있으며, 각 목적에 맞는 Filter 들을 구현 할 수 있다.

단순하게 Request -> Filters -> Service Module 로 통신하는 간단한 구조인것이다.

 

이미 언급하였듯, 공통의 관심사들을 Filter 로 해결할 수 있으며, yml 파일로 간단하게 목적지 Module 로 Routing 할 수 있다는것이 최대의 장점이다.

단점이라 하면, WebFlux 기반이기에 러닝커브가 높을수 있으며, Routing 대상 서버가 많아진다면 관리가 힘들 수 있다.

 

 

- Spring Cloud Gateway(SCG) 핵심 개념

 

SCG 의 핵심 개념 3가지만 기억하면 이해하는데 더 빠를것 같다.

  1. Route
    1. Client 의 요청을 '어디'로 보낼지 결정
  2. Predicate(조건)
    1. 특정 조건에 따라 Routing 수행
    2. Ex) Path, Cookie, Header, IP 등등
  3. Filter
    1. Request/Response 에 대한 수정(Header 추가)
    2. 적절한 요청인지 검사 및 응답 거부
    3. 인증, 로깅 등과 같은 공통관심사 해결

 

- Routing 방법 및 Predicate

이미 언급했듯, yml 파일 하나로 간단하게 Routing 을 설정할 수 있으며, Java 코드로도 설정이 가능하다 (RouteLocator 를 참고)

spring:
  application:
      name: cloud-gateway
  cloud:
    gateway:

      # Global Filter List
      default-filters:
        - name: GlobalFilter
        - name: LoggingFilter

      # Routing
      routes:
        - id: recipt
          uri: http://localhost:8080
          predicates:
            - Path=/api/recipt # 해당 경로로 들어오는 요청을 Routing
            - After=2020-01-01T00:00:00+08:00[Asia/Seoul] # 이 시간 이후부터는 해당 서버로 요청
          filters:
            - CustomFilter # Custom Filter(order=1)
        - id: product
          uri: http://localhost:8081
          predicates:
            - Host=example.com # 해당 endpoint 로 들어오는 요청을 Routing

Routing 에 대한 Predicate 나 방법들을 공식 문서에 더 자세하고, 쉽게 설명이 되어있기에 추가적으로 작성하지는 않겠다.

https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gateway-request-predicates-factories

 

Spring Cloud Gateway

This project provides an API Gateway built on top of the Spring Ecosystem, including: Spring 6, Spring Boot 3 and Project Reactor. Spring Cloud Gateway aims to provide a simple, yet effective way to route to APIs and provide cross cutting concerns to them

docs.spring.io

 

 

 

 

 

4. 간단한 예제 구현

Module 을 생성할때 필요한 Dependecy 는 단 2가지만 존재해도 된다.(필요에 따라 추가)

implementation 'org.springframework.cloud:spring-cloud-starter-gateway'

compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'

간혹 Server 기동시 Console 창에 Netty ~ 가 아닌 Tomcat ~ 이 출력 된다면 

implementation 'org.springframework.cloud:spring-cloud-starter-gateway-mvc' 에서 mvc 를 빼주도록 하자.

 

server:
  port: 18080

spring:
  application:
      name: cloud-gateway
  cloud:
    gateway:

      # Global Filter List
      default-filters:
        - name: GlobalFilter
        - name: LoggingFilter

      # Routing
      routes:
        - id: boot-basic
          uri: http://localhost:8080
          predicates:
            - Path=/api/basic
            - After=2020-01-01T00:00:00+08:00[Asia/Seoul]

 

yml 설정하나로 /api/basic path 로 들어오는 모든 요청은 localhost:8080 서버로 Routing 되게 설정이 완료되었다.

여기서 predicates 를 요구사항에 맞춰 수정하여 사용하면 Routing 에 대해서는 완료이다. 

 

 

간단하게 /api/basic 에 GET 요청을 보내면 "boot-basic" 이라는 String 을 Return 받도록 API 를 작성해주고

localhost:18081/api/basic 즉 Gateway 서버로 요청을 보내보도록 하자.

 

8080 서버의 Response 가 잘 오는것을 확인할 수 있다. 

 

 

4-1 Filtering

추가로 간단하게 Filter 를 설정하여, Stopwatch, Logging 기능을 추가해 보도록 하겠다.

Filter 는 2개를 생성할것이며 (GlobalFilter, LoggingFilter) 

 

Request 발생시 GlobalFilter 에서 RequestID 를 log 로 출력 -> 

Logging Filter 에서 Request 에 대한 정보들을 log 로 출력 ->

요청 완료시 StopWatch 로 몇 ms 가 걸렸는지 출력 하는 예제이다.

GlobalFilter

implement GlobalFilter, Ordered 를 통해서 FIlter 를 직접 구현하여, Bean 등록시 자동실행의 힘을 빌릴 수도 있었지만,

AbstractGatewayFilterFactory 를 구현하는 방식으로 yml 에 default-filters 를 등록하는 방식을 사용하였다. 

추후 각 Router 에 Custom Filter 구현까지 고려하여 Order 보장성을 위해 차용하였다.

이때 apply() 를 구현하는 방식으로 사용할 수 있으며, config{} 부분을 yml 에 추가하여 설정들을 동적으로 변경할 수 있다.

 

GlobalFilter 에서는 간단하게 Request 발생시 , RequestID 로깅

POST 응답시 ms 를 Log 출력 하는 간단한 로직이다.

LoggingFilter

LoggingFilter 에서는 Request 에 대한 Logging 을 수행하였고, TODO 에 보이는대로 MQ(Kafka, RabbitMQ) 등에 Logging 을

전송하고 저장하는 용도로 활용할 수 있겠다.

- Gateway 서버에서 직접 Logging 저장하지 않는 이유? 

Log 적재를 해당 서버에서 직접 적재하게 된다면, 저장하는 과정에서 blocking 이 발생할 수 있고 
이는 서버의 부하와 함께, 성능 저하까지 이어질 수 있다.

Log 를 적재하는 별도의 모듈에 비동기로 전송(적재) 하게 되면, 확장성, 성능을 잡을 수 있으며
장애 발생시 영향도를 줄일 수 있다.

 

spring:
  application:
      name: cloud-gateway
  cloud:
    gateway:

      # Global Filter
      default-filters:
        - name: GlobalFilter
        - name: LoggingFilter

 

yml 에 default-filters 에 Filter 들을 등록하여 기능을 활성화 시켜주자.

Log

 

 

 

4-2. RouteLocator

yml 이외에 Java 코드로도 Route 를 설정할 수 있다. 

yml 로 설정하는것에 비해서, 문법 오류를 잡을수도 있고 RDB, NoSQL 을 연결하여 서버실행시 동적으로도 설정주입이 가능하다.

Spring Framework 생태계를 잘 이용한다면 Spring Cloud Config 를 활용하여 git 또는 DB 와 같은 외부 저장소에 Route 정보를 저장해두고, 실시간으로 반영할 수 도 있다.

 

정말 간단한 몇가지의 Sample 을 보도록 하자.

 

// 수동으로 추가
@Configuration
public class SampleRouteConfig {

    @Bean
    public RouteLocator locator(RouteLocatorBuilder builder) {
        return builder
                .routes()
                .route("server-one", r -> r
                        .path("/api/one/**")
                        .uri("http://localhost:8081"))

                .route("server-two", r -> r
                        .path("/api/two/**")
                        .uri("http://localhost:8082")
                )
                .build();
    }
}

- 가장 기본이 되는 필수값들만 넣어서 바로 사용이 가능하다. 보이는것과 같이 동적생성이 아니기 때문에 Route 정보 변경, 추가 이벤트가 발생한다면 서버를 재기동해야 하는 불편함이 발생한다.

 

- 동적인 Config 설정

@Configuration
public class RouteLocatorConfig {

    private final MongoTemplate mongoTemplate;

    public RouteLocatorConfig(MongoTemplate mongoTemplate) {
        this.mongoTemplate = mongoTemplate;
    }

    @Bean
    public RouteLocator dynamicRoutes(RouteLocatorBuilder builder) {
        RouteLocatorBuilder.Builder routes = builder.routes();

        // MongoDB에서 모든 라우트 정보 조회
        List<RouteEntity> routeList = mongoTemplate.findAll(RouteEntity.class, "routes");

        for (RouteEntity route : routeList) {
            routes.route(route.getRouteId(),
                    r -> r.path(route.getPath()).uri(route.getUri()));
        }

        return routes.build();
    }
}

- 이처럼 MongoDB 나, RDB 등을 활용해 서버 기동시 동적으로 생성할수도 있다. 다만, 수동설정과 동일하게 서버의 재기동이 필요하다는 단점이 존재한다.

 

물론 @Scheduled 를 사용해서 동적으로 계속 생성해 줄수도 있지만, 기본적으로 RouteLocator 는 Immutable 하기 때문에

RouteDefinitionLocator, RouteDefinitionWriter 를 @Scheduled 과 함께 사용하여, 기존의 Route 를 지우고 새로 반영하는 방법도 존재하긴한다. (다만 이때 발생하는 순단현상이 발생할 수 있을것 같다.)

 

 

 

 

5. 번외 - Monitoring

Gateway 의 공통 관심사중 Monitoring 에 대한 부분을 잠깐 보도록 하자.

일반적으로 많이 사용하는 Prometheus, Grafana 를 사용할 수 있으며 

Prometheus 의 의존성 추가, 연결 설정등만 yml 에 해주면 간단하게 할 수 있다.(설정에 대해선 다루지 않는다)

 

필자는 간단하게 Docker 로 Prometheus , Grafana 를 띄워서 확인했다.

implementation 'io.micrometer:micrometer-registry-prometheus'

 

이처럼 Metrics 수집을 통해 모니터링까지 간단하게 수행할 수 있다.

 

 

 

MSA 아키텍처를 꼭 사용하지 않더라도, N 개의 Module 을 사용하는 서비스라면 Spring Cloud Gateway 를 사용해도 좋을것 같다는 생각이 든다. Request 를 Routing 하는 방식이 생각보다 간단하고, 공통관심사도 여러모로 해결할 수 있으니 말이다.

최소한의 노력으로 최대의 효율을 만들기에 좋은 방식이라고 생각이 든다.

다만, 서비스의 확장이 폭발적으로 이루어질때 SCG 를 얼마나 활용할 수 있을것인지, 성장하는 서비스에 맞춰 적합한 framework 일지는 잘 모르겠다. (사용을 잘하고 있는 기업또한 많다. 카카오페이, 토스 ,,,,, 등등)

반응형

댓글