티스토리 뷰
API Gateway 패턴을 공부하다 보면 결국 다음 질문으로 이어지게 됩니다. 실제 서비스에서는 Gateway를 어떻게 설계해야 할까입니다. 개념적으로는 클라이언트와 마이크로서비스 사이에 하나의 진입점을 둔다고 설명할 수 있지만, 실제 운영 환경에서는 훨씬 더 많은 설계 판단이 필요합니다.
특히 Spring Cloud Gateway를 사용해 API Gateway를 구성할 경우 보안 정책, 트래픽 제어, 장애 대응, 로그 관측 같은 다양한 요구사항을 함께 고려해야 합니다. Gateway는 단순한 라우터가 아니라 서비스 경계에서 트래픽을 통제하는 인프라 역할을 하기 때문입니다.
이 글에서는 Spring Cloud Gateway를 기반으로 실제 프로젝트에서 사용할 수 있는 설계 방식을 정리해 보려고 합니다. 필터 체인 설계, 보안 계층의 위치, 회복탄력성 패턴 적용, 관측성 설계, 그리고 성능 튜닝까지 운영 환경에서 자주 고민하게 되는 주제를 중심으로 살펴봅니다.
Spring Cloud Gateway 필터 체인 설계
Spring Cloud Gateway는 필터 체인을 기반으로 요청을 처리합니다. 요청이 Gateway에 도착하면 여러 필터가 순차적으로 실행되면서 요청을 검증하고 변환하고 라우팅합니다. 따라서 필터의 역할뿐 아니라 실행 순서를 어떻게 구성하느냐가 중요한 설계 요소가 됩니다.
실무에서 자주 사용하는 구조 중 하나는 다음과 같은 순서입니다.
HeaderSanitize → RequestId → JWT 인증 → Route → Access Log
이 순서는 단순히 구현 편의를 위한 것이 아니라 보안과 관측성 측면에서 나름의 이유가 있습니다.
가장 먼저 수행되는 단계는 헤더 위생화입니다. 클라이언트 요청에는 다양한 HTTP 헤더가 포함될 수 있으며, 일부 헤더는 신뢰할 수 없는 값일 수 있습니다. 특히 X-Forwarded-For나 X-Request-ID 같은 헤더는 클라이언트가 임의로 설정할 수도 있기 때문에 이를 그대로 신뢰하면 보안 문제가 발생할 수 있습니다. 따라서 Gateway에서는 먼저 신뢰할 수 없는 헤더를 제거하거나 재설정하는 과정이 필요합니다.
그 다음 단계는 Request ID 생성입니다. 분산 시스템에서는 하나의 요청이 여러 서비스로 전파되기 때문에 요청을 추적할 수 있는 식별자가 필요합니다. Gateway에서 Request ID를 생성하고 이후 서비스 호출 시 헤더로 전달하면 로그 분석이나 분산 트레이싱에 도움이 됩니다.
그 다음 단계에서 JWT 인증을 수행합니다. 인증을 통과한 요청만 실제 서비스로 전달하도록 구성하면 내부 서비스는 인증 처리에 대한 부담을 줄일 수 있습니다.
라우팅 단계에서는 실제 요청을 적절한 마이크로서비스로 전달합니다. 마지막으로 Access Log를 기록하면 Gateway에서 처리된 모든 요청을 일관된 형태로 기록할 수 있습니다.
보안 계층 설계
API Gateway는 외부 요청이 처음으로 들어오는 지점이기 때문에 보안 정책을 어디에서 적용할지에 대한 설계가 중요합니다.
특히 JWT 인증을 Gateway에서 처리할지 서비스에서 처리할지에 대해서는 다양한 접근 방식이 존재합니다.
Gateway에서 JWT를 검증하면 인증 로직을 중앙화할 수 있다는 장점이 있습니다. 모든 서비스에서 동일한 인증 정책을 적용할 수 있고, 인증 실패 요청을 Gateway 단계에서 차단할 수 있습니다. 반면 서비스 내부에서 인증을 처리하면 서비스가 독립적으로 인증 정책을 제어할 수 있습니다. 일부 조직에서는 두 방식을 함께 사용하는 경우도 있습니다.
헤더 위생화가 JWT 검증보다 먼저 수행되어야 하는 이유도 보안 측면에서 중요한 설계 포인트입니다. 일부 공격 시나리오에서는 클라이언트가 Gateway가 신뢰하는 헤더를 위조하여 인증 로직을 우회하려는 시도가 발생할 수 있습니다. 따라서 인증 검증을 수행하기 전에 먼저 신뢰할 수 없는 헤더를 정리하는 과정이 필요합니다.
경로별 접근 제어 역시 Gateway에서 자주 구현되는 기능입니다. 예를 들어 특정 API는 관리자만 접근할 수 있도록 설정하거나, 내부 서비스 전용 API를 외부 요청에서 차단할 수 있습니다. 이런 정책을 Gateway에서 관리하면 서비스 코드에 보안 정책이 분산되는 문제를 줄일 수 있습니다.
CORS 설정 역시 환경에 따라 전략이 달라질 수 있습니다. 개발 환경에서는 비교적 자유로운 정책을 사용할 수 있지만 운영 환경에서는 허용 도메인을 명확하게 제한하는 것이 일반적입니다. 특히 Public API를 제공하는 경우에는 CORS 정책을 신중하게 설정해야 합니다.
회복탄력성 설계
Gateway는 트래픽이 집중되는 지점이기 때문에 장애 상황에서도 안정적으로 동작하도록 설계하는 것이 중요합니다. 이때 자주 사용하는 접근 방식이 Circuit Breaker, Rate Limiting, Retry 같은 회복탄력성 패턴입니다.
Circuit Breaker는 특정 서비스가 지속적으로 실패할 때 추가 요청을 차단하여 시스템 전체의 장애 확산을 방지하는 패턴입니다. Spring Cloud Gateway에서는 Resilience4j와 같은 라이브러리를 통해 Circuit Breaker를 적용할 수 있습니다.
서비스 특성에 따라 Circuit Breaker 설정을 다르게 가져가는 것도 중요한 전략입니다. 예를 들어 일반적인 CRUD 서비스와 LLM API 같은 고지연 서비스는 실패 패턴이 다를 수 있습니다. 따라서 모든 서비스에 동일한 임계값을 적용하기보다는 서비스 특성에 맞게 설정을 조정하는 것이 현실적인 접근 방식일 수 있습니다.
Rate Limiting은 특정 사용자가 과도한 요청을 보내는 상황을 제어하기 위한 기능입니다. Gateway에서는 일반적으로 IP 기반 또는 사용자 기반 Key Resolver를 사용하여 요청 제한을 설정합니다. 이를 통해 시스템 전체의 안정성을 유지할 수 있습니다.
Retry 전략 역시 Gateway에서 사용할 수 있지만 신중하게 적용해야 합니다. 멱등성이 보장되는 요청에 대해서만 Retry를 수행하는 것이 일반적이며, 지수 백오프를 적용하여 서비스에 과도한 부하가 발생하지 않도록 하는 것이 중요합니다.
관측성 설계
Gateway는 모든 트래픽이 지나가는 지점이기 때문에 관측성 측면에서도 중요한 역할을 합니다.
Access Log를 구조화된 형태로 기록하면 이후 로그 분석이 훨씬 쉬워집니다. 요청 경로, 응답 코드, 처리 시간, Request ID 같은 정보를 포함하면 트래픽 패턴을 분석하거나 장애 원인을 파악하는 데 도움이 됩니다.
Request ID 전파 역시 분산 시스템에서 중요한 설계 요소입니다. Gateway에서 생성된 Request ID를 내부 서비스로 전달하면 여러 서비스에 걸친 요청 흐름을 추적할 수 있습니다.
Prometheus 기반 메트릭 수집도 운영 환경에서 자주 사용하는 방식입니다. Gateway의 요청 수, 응답 시간, 오류 비율 같은 메트릭을 수집하면 시스템 상태를 실시간으로 모니터링할 수 있습니다.
Reactive 환경에서는 예외 처리 방식도 조금 다르게 접근해야 합니다. 예를 들어 502, 503, 504 같은 오류 코드는 각각 다른 상황을 의미합니다. 502는 일반적으로 upstream 서비스 응답 오류를 의미하고, 503은 서비스가 일시적으로 요청을 처리할 수 없는 상황을 나타냅니다. 504는 Gateway가 upstream 서비스 응답을 기다리다 타임아웃이 발생한 경우입니다. 이러한 차이를 명확히 구분하여 로그와 메트릭에 반영하면 운영 관점에서 많은 도움이 됩니다.
성능 튜닝
Spring Cloud Gateway는 내부적으로 Reactor Netty를 기반으로 동작합니다. 따라서 성능 튜닝 역시 Reactor Netty 설정과 밀접하게 관련되어 있습니다.
Connection Pool 설정은 성능에 큰 영향을 미치는 요소 중 하나입니다. 너무 작은 풀을 사용하면 연결 대기 시간이 증가할 수 있고, 반대로 너무 큰 풀을 설정하면 시스템 리소스를 과도하게 사용할 수 있습니다.
Timeout 설정 역시 중요합니다. 일반적으로 Connection Timeout, Response Timeout, Read Timeout 같은 여러 계층의 타임아웃을 함께 고려해야 합니다. 이러한 설정을 통해 느린 서비스가 전체 시스템의 응답 시간을 지연시키는 상황을 방지할 수 있습니다.
Reactive 기반 시스템에서는 스레드 모델도 중요한 고려 요소입니다. Netty 이벤트 루프 스레드는 블로킹 작업을 수행하지 않는 것이 원칙이기 때문에 Gateway에서는 블로킹 I/O를 사용하지 않도록 주의해야 합니다.
Gateway 안티패턴
Gateway를 설계하다 보면 의도치 않게 아키텍처가 복잡해지는 경우도 있습니다. 특히 몇 가지 안티패턴은 실제 프로젝트에서도 자주 등장합니다.
대표적인 사례는 Gateway에 비즈니스 로직을 포함시키는 경우입니다. Gateway는 트래픽 제어와 정책 적용을 담당하는 계층이지 비즈니스 로직을 처리하는 계층은 아닙니다. 비즈니스 로직이 Gateway에 들어가기 시작하면 시스템 구조가 점점 복잡해질 수 있습니다.
WebFlux 환경에서 블로킹 I/O를 사용하는 것도 흔한 문제입니다. Reactor 기반 애플리케이션에서는 블로킹 작업이 이벤트 루프를 차단할 수 있기 때문에 전체 시스템 성능에 영향을 줄 수 있습니다.
요청과 응답을 과도하게 변환하는 것도 또 다른 문제입니다. Gateway에서 지나치게 많은 데이터 변환을 수행하면 응답 지연이 증가할 수 있고 시스템 구조도 복잡해질 수 있습니다.
정리
Spring Cloud Gateway는 단순한 라우팅 도구라기보다 서비스 경계에서 트래픽을 통제하는 중요한 인프라 구성 요소라고 볼 수 있습니다. 보안, 트래픽 제어, 장애 대응, 관측성 같은 다양한 기능이 Gateway 계층에 집중되기 때문입니다.
다만 Gateway가 모든 문제를 해결하는 계층은 아닙니다. Gateway에 너무 많은 책임을 부여하면 또 다른 형태의 복잡성이 생길 수도 있습니다. 따라서 Gateway는 어디까지나 트래픽 관리와 정책 적용을 담당하는 계층이라는 관점을 유지하는 것이 중요해 보입니다.
개인적으로 Gateway 아키텍처를 공부하면서 느낀 점은, 기술 자체보다 경계(boundary)를 어떻게 설계하느냐가 더 중요하다는 것입니다. 어떤 기능을 Gateway에 두고 어떤 기능을 서비스에 둘 것인지에 대한 판단이 시스템 구조에 큰 영향을 주기 때문입니다. 이런 관점에서 Gateway 설계는 단순한 설정 작업이라기보다 아키텍처적인 고민이 필요한 영역이라고 생각합니다.
'STUDY' 카테고리의 다른 글
- Total
- Today
- Yesterday
- TTL 설계
- 백엔드 아키텍처
- 캐시 성능 비교
- 스레드 생명주기
- 트래픽 처리
- Double-Checked Locking
- Redis vs DB
- 백엔드 성능 튜닝
- Eager Initialization
- Hot Key 문제
- mybatis
- Enum 기반 싱글톤
- InterruptedException
- spring batch 5
- DB 트랜잭션
- 캐시와 인덱스
- Cache Penetration
- 백엔드 성능 설계
- Redis 캐시 전략
- Redis 성능 개선
- Cache Avalanche
- 동시성처리
- Spring Batch
- Java Performance
- Cache Aside
- 백엔드 성능
- 트랜잭션 관리
- DB 인덱스 성능
- Initialization-on-Demand Holder Idiom
- 캐시 장애
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| 8 | 9 | 10 | 11 | 12 | 13 | 14 |
| 15 | 16 | 17 | 18 | 19 | 20 | 21 |
| 22 | 23 | 24 | 25 | 26 | 27 | 28 |
| 29 | 30 | 31 |

