개발 일기

[Spring Cloud] Spring Cloud Gateway & Spring Cloud LoadBalancer 본문

Back-End/Spring Cloud + MSA + Kubernetes

[Spring Cloud] Spring Cloud Gateway & Spring Cloud LoadBalancer

개발 일기장 주인 2025. 2. 10. 14:33

기존에 단일 프로젝트의 멀티 모듈 아키텍처에서 인증·인가를 담당하는 auth 모듈을 구축했다.

이때 해당 auth 모듈을 통해 각 api 모듈로 요청을 라우팅할 수 있으면 좋았겠지만, 단일 애플리케이션 내에서는 API Gateway 패턴을 적용할 수 없었고 결국, auth 모듈을 각 api 모듈에서 직접 의존하여 인증·인가를 처리하는 방식으로 구현해야 했다.

 

해당 방식은 어쨋든 인증·인가 로직을 하나의 모듈에서 처리하고, 라이브러리처럼 필요한 api 모듈에서 이를 의존하여 사용하는 형태였지만, 아키텍처적으로는 중앙 집중식으로 처리하는 API Gateway 패턴과는 차이가 있어 아쉬웠다.

 

그러나 이번에 Eureka 기반의 MSA 아키텍처를 구축하면서 Spring Cloud Gateway를 도입하게 되어 정리해보게 됐다.


Spring Cloud Gateway를 다루기에 앞서 API Gateway 패턴에 대해서 알고 가야할 것 같다.

API Gateway Pattern?

API Gateway라고 했을때 당장 떠오르는 것은 Amazon API Gateway와 Spring Cloud Gateway가 있다.

https://www.connecting-software.com/blog/what-is-an-api-gateway-how-it-can-actually-deliver-practical-results/

API Gateway 패턴은 마이크로서비스 아키텍처(MSA)에서 중요한 역할을 하는 디자인 패턴으로, 여러 서비스가 개별적으로 처리하는 대신 모든 클라이언트 요청을 하나의 API Gateway를 통해 처리하는 방식이다.

API Gateway는 클라이언트와 마이크로서비스 간의 중간 계층 역할을 하며, 클라이언트가 직접 여러 서비스에 접근하는 것을 방지하고, 요청을 대신 처리한다.

즉, 서비스에 대한 단일 진입점을 만들어 준다.

여러 클라이언트가 여러 개의 서버 서비스를 각각 호출하게 되면 코드(로직)의 중복이 발생하며 매우 복잡해 질 수 있으나 이를 API Gateway를 통해 단일 진입점을 만들어 주고 각 서비스 접근 시 필요한 인증/인가 로직을 한방에 처리해 줄 수 있다.

이때 L4 같은 하드웨어 장비로도 서비스 라우팅이 가능하지만 API Gateway Framework를 통해 어플리케이션 레벨에서도 라우팅이 가능해지고 로드밸런싱 , 필터 등 여러 처리가 가능해진다.


Spring Cloud Gateway(SCG)란?

우선 Spring Cloud Gateway는 Spring Framework에서 제공하는 오픈 소스 기반의 API Gateway 서비스이다.

이전에는 Netflix의 Zuul을 사용했다면 이제는 해당 서비스를 사용한다.

비동기 논블로킹 아키텍처 기반의 API Gateway로, 라우팅 정책, 필터, 서킷 브레이커 등을 유연하게 구성할 수 있다. 

  • Netty 기반 웹 서버 : Zuul은 Tomcat기반 이였지만 SCG는 Netty 기반이다.
  • Reactive Programming 지원: WebFlux 기반으로 높은 성능을 보장한다.
  • 동적 라우팅: 서비스 디스커버리(Eureka)와 연동하여 실시간으로 서비스 경로를 탐색한다.
  • 모듈화된 구성: Predicate(조건)와 Filter(필터)를 조합하여 복잡한 라우팅 로직을 구현할 수 있다.

Spring Cloud Gateway의 동작 흐름

  1. 요청 수신: 클라이언트에서 HTTP 요청이 들어온다.
  2. 라우트 매칭: Gateway는 설정된 라우팅 규칙을 기반으로 요청을 특정 서비스로 전달할지 결정한다.
  3. 필터 처리: 요청은 전처리(전역 필터, 라우트 필터)에 의해 처리됩니다. 예를 들어 인증, 로깅, 헤더 수정 등.
  4. 서비스 호출: 요청이 최종적으로 지정된 서비스로 전달된다.
  5. 응답 처리: 서비스로부터 응답이 오면, 다시 필터를 통해 후처리된 후 클라이언트에게 응답이 반환된다.

 

SCG 구조

 

Route(라우팅)

  • 클라이언트의 요청을 처리할 경로를 정의
  • id는 "member-service"는 라우트의 식별자(ID)
  • 이 Route는 특정 조건(Predicate)을 만족하는 요청을 MEMBER-SERVICE로 라우팅

Predicates (조건)

  • 주어진 요청이 주어진 조건을 충족하는지 테스트하는 구성 요소이며, 하나 이상의 조건자를 정할 수 있다.
  • 만약 Predicate에 매칭되지 않을 경우 SCG 자체적으로 HTTP 404 Not Found로 응답
  • 즉, Predicate는 라우팅 조건을 정의하는 역할

Filters & Filter Chain (필터)

  • Global Filters: 모든 라우트에 적용되는 필터
  • Route Filters: 특정 라우트에만 적용되는 필터
  • 필터는 요청을 처리하기 전에 또는 응답을 클라이언트에 전달하기 전에 추가적인 처리를 수행 (전처리 / 후처리)

 

🔹 기본 동작 방식
Predicate
→ "이 요청이 /member/로 시작하는가?"
Filter → "그럼, 요청에 인증 헤더를 추가해야겠어."
Route → "좋아, 이제 MEMBER-SERVICE로 전달하자!"

 


Spring Cloud LoadBalancer

Spring Cloud LoadBalancer는 Spring Cloud에서 제공하는 클라이언트 측 부하 분산(Client-Side Load Balancing) 라이브러리

"Spring Cloud Gateway는 Spring Cloud LoadBalancer를 자동으로 포함"하고 있다.

spring-cloud-starter-gateway 내부적으로 spring-cloud-starter-loadbalancer를 포함

 

Spring Cloud Gateway에서 라우팅을 설정할 때, 고정된 URL을 사용할 수도 있습니다.

  • http://localhost:8081 HTTP 요청을 특정 서버(8081 포트)로 전달
  • ws://localhost:8082  WebSocket 요청을 특정 서버(8082 포트)로 전달

하지만 MSA(Microservices Architecture)에서는 각 마이크로서비스의 IP와 Port가 동적으로 변할 수 있기 때문에,

고정된 URL을 사용하면 확장성과 가용성이 떨어진다.

 

✅ uri: lb://{마이크로 서비스 명}

  • Spring Cloud LoadBalancer는 서비스 디스커버리(Eureka 등)와 연동하여 동적으로 서비스 인스턴스를 찾고 부하 분산을 수행하는 역할
  • 따라서 lb://{마이크로서비스명}을 사용하면, 고정된 URL 없이도 자동으로 서비스 인스턴스를 찾아 요청을 라우팅
🔹 기본 동작 방식
1. 클라이언트가 lb://MEMBER-SERVICE 요청
2. 서비스 디스커버리(Eureka 등)에서 MEMBER-SERVICE 인스턴스 목록 조회
3. 부하 분산 알고리즘 적용하여 하나의 인스턴스 선택
4. 선택된 인스턴스의 실제 URL(http://192.168.0.10:8081)로 요청 전송

 

‼️ Load Balancer 처리 방식

Spring Cloud LoadBalancer는 기본적으로 "라운드 로빈(Round Robin) 알고리즘"을 사용하여 부하를 분산한다.
하지만 필요에 따라 커스텀 로드 밸런싱 알고리즘을 적용 가능하다.

즉, 요청이 올 때마다 등록된 인스턴스들 사이에서 순차적으로 하나씩 선택하여 라우팅합니다.

 

🔹 예시 (Eureka에 3개의 MEMBER-SERVICE 인스턴스가 등록됨)

첫 번째 요청 → MEMBER-SERVICE (8081) 
두 번째 요청 → MEMBER-SERVICE (8082) 
세 번째 요청 → MEMBER-SERVICE (8083) 
네 번째 요청 → MEMBER-SERVICE (8081) (다시 처음으로 돌아감)

 

👉 장점:

  • 요청이 균등하게 분배됨
  • 특별한 설정 없이 자동으로 적용됨