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가지만 기억하면 이해하는데 더 빠를것 같다.
- Route
- Client 의 요청을 '어디'로 보낼지 결정
- Predicate(조건)
- 특정 조건에 따라 Routing 수행
- Ex) Path, Cookie, Header, IP 등등
- Filter
- Request/Response 에 대한 수정(Header 추가)
- 적절한 요청인지 검사 및 응답 거부
- 인증, 로깅 등과 같은 공통관심사 해결
- 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 나 방법들을 공식 문서에 더 자세하고, 쉽게 설명이 되어있기에 추가적으로 작성하지는 않겠다.
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 가 걸렸는지 출력 하는 예제이다.
implement GlobalFilter, Ordered 를 통해서 FIlter 를 직접 구현하여, Bean 등록시 자동실행의 힘을 빌릴 수도 있었지만,
AbstractGatewayFilterFactory 를 구현하는 방식으로 yml 에 default-filters 를 등록하는 방식을 사용하였다.
추후 각 Router 에 Custom Filter 구현까지 고려하여 Order 보장성을 위해 차용하였다.
이때 apply() 를 구현하는 방식으로 사용할 수 있으며, config{} 부분을 yml 에 추가하여 설정들을 동적으로 변경할 수 있다.
GlobalFilter 에서는 간단하게 Request 발생시 , RequestID 로깅
POST 응답시 ms 를 Log 출력 하는 간단한 로직이다.
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 들을 등록하여 기능을 활성화 시켜주자.
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 일지는 잘 모르겠다. (사용을 잘하고 있는 기업또한 많다. 카카오페이, 토스 ,,,,, 등등)
'spring & boot > Spring & Spring Boot' 카테고리의 다른 글
[Spring] Gateway - 해외 IP 차단(필터링) (0) | 2025.04.02 |
---|---|
[Spring Boot] @Qualifier 무시 오류(Lombok @RequiredArgsConstructor) (1) | 2025.02.05 |
[Spring boot] Spring Boot 라이브러리(Library) 개념 및 만들기(1) (1) | 2024.12.27 |
[Spring Boot]OAuth2: Authorization-Server (Custom 인증 서버 구축)(2) (4) | 2024.11.20 |
[Spring boot] Spring Boot 3.x^ - Swagger 적용(2) (0) | 2024.11.14 |
댓글