동적 라우팅 인프라, 그 다음의 도전 과제
지난 편에서는 Spring Cloud Gateway(이하 SCG)를 활용하여 기존의 경직된 정적 YAML 설정 구조를 탈피하고, 데이터베이스 기반의 무중단 동적 라우팅 아키텍처를 구축하는 과정에 대해 서술했습니다. 인프라의 핵심 설정 요소를 애플리케이션의 비즈니스 데이터 영역으로 전환함으로써, 시스템 재빌드와 배포 없이 운영 중에 실시간으로 라우팅 규칙을 변경할 수 있는 유연성을 확보했습니다. 이는 복잡한 멀티 벤더 환경에서 각 개발사 간의 배포 시점 차이와 엔드포인트 변경에 유연하게 대응할 수 있는 강력한 완충 지대가 되었습니다.
그러나 실무 관점에서 무중단 동적 라우팅 환경을 완성한 것은 거대한 목적지를 향한 첫걸음에 불과했습니다. 인프라 레벨에서 트래픽의 통로를 유연하게 열어준 순간, 분산된 마이크로서비스 아키텍처(MSA) 환경이 마주하는 더 본질적이고 치명적인 실무적 과제들이 수면 위로 떠올랐기 때문입니다. 그 중심에는 수십 개의 파편화된 서비스 위에서 어떻게 공통된 보안 표준을 유지할 것인가, 그리고 특정 시점에 예측 불가능하게 밀려드는 대규모 트래픽으로부터 내부 시스템을 어떻게 안전하게 보호할 것인가라는 두 가지 핵심 도전 과제가 자리 잡고 있었습니다. 바로 '보안 기능의 중앙화'와 '시스템 가용성 확보를 위한 트래픽 제어 및 대기열 관리'입니다.
이번 편에서는 지난 편에서 설계한 GatewayRoute 도메인 모델과 동적 인프라를 기반으로, 통합 인증 및 인가 필터의 구현 과정과, 대규모 트래픽을 안정적으로 수용하고 제어하는 큐 관리 서비스의 실제 구현 전략을 심도 있게 다루어 보겠습니다.
통합 인증 및 인가(Authorization) 필터
MSA를 멀티 벤더 환경에서 수행할 때 가장 먼저 맞닥뜨리는 기술적 아킬레스건은 바로 '보안 표준의 통일성'입니다. 도메인별로 전문화된 다수의 개발사가 각자의 마이크로서비스를 독립적으로 개발하고 배포하는 구조에서는, 인증(Authentication)과 인가(Authorization) 메커니즘이 각 서비스 내부로 파편화되기 쉽습니다. 특히 본 프로젝트가 구축된 인프라는 외부와의 무분별한 접촉이 차단된 내부 폐쇄망 환경이었습니다. 이로 인해 내부 마이크로서비스들의 네트워크 토폴로지(Topology)를 외부 연동 접점에 직접 노출하지 않으면서, 들어오는 모든 요청의 신원을 일관되게 검증하고 비즈니스 권한에 따라 완벽하게 통제하는 강력한 '보안 관문(Secure Gate)'의 존재가 필수적이었습니다. 각 서비스가 비즈니스 로직에만 순수하게 집중할 수 있도록 보안 오버헤드를 완전히 걷어내고, 시스템 전체의 보안 수준을 상향 동기화하기 위해 Spring Cloud Gateway 레이어에 전역적인 비동기 인증/인가 필터를 구현하였습니다.
첫째, Spring Cloud Gateway는 내부적으로 Spring WebFlux와 Netty 엔진을 기반으로 동작하는 리액티브(Reactive) 아키텍처를 가집니다. 따라서 스레드당 하나의 요청을 처리하는 전통적인 서블릿(Servlet) 기반의 동기식 필터가 아닌, 적은 수의 이벤트 루프(Event Loop) 스레드로 대규모 동시 연결을 논블로킹(Non-blocking)으로 처리할 수 있는 리액티브 웹 필터(WebFilter) 설계가 요구됩니다. 구현한 통합 인증 및 인가 필터는 단순한 JWT(JSON Web Token) 복호화를 넘어, 다중 벤더 간의 복잡한 연동 규격을 단일 필터 내에서 유기적으로 중재하고, 데이터베이스 기반의 API 경로별 허용 서비스와 실시간으로 매핑하여 인가 제어를 수행합니다. 이 복잡한 필터 아키텍처가 논블로킹 API 게이트웨이 환경에서 단 한 번의 지연도 없이 안정적이고 기민하게 동작할 수 있었던 비결은, 리액티브 스트림의 특성을 완벽히 이해하고 적재적소에 배치한 세 가지 핵심적인 기술적 장치에 기인합니다.
둘째, 다중 연동 규격의 투명한 수용을 위한 동적 토큰 스왑 및 프로토콜 어댑터 메커니즘입니다. 이 필터는 단순한 게이트웨이의 진입 관문을 넘어, 다수의 개발사와 레거시 플랫폼이 얽혀 있는 내부 폐쇄망의 통신 규격을 유연하게 중재하는 중앙 집중식 어댑터 역할을 훌륭히 수행합니다. 멀티 벤더 환경에서는 각 시스템이 요구하는 자격 증명 체계가 제각각인 경우가 많습니다. 이를 클라이언트 단에서 일일이 대응하게 만드는 대신, 게이트웨이가 요청의 목적지를 파악하여 다운스트림 서비스가 원하는 형태로 요청을 동적으로 실시간 변조하도록 설계했습니다. 특히 레거시 플랫폼 연동 시 외부 원본 토큰을 검증한 뒤, 내부 비즈니스 도메인 통신에 필수적인 전용 인프라 토큰을 백그라운드에서 동적으로 발행받고, 새로운 자격 증명으로 헤더를 치환하여 요청을 포워딩하는 동적 토큰 스왑 아키텍처를 구현했습니다. 그 결과, 다중 개발사 및 레거시 인터페이스가 강제하는 상이하고 복잡한 인증/인가 규격을 게이트웨이 레이어 밑으로 완벽하게 은닉할 수 있었으며, 내부 시스템들 간의 결합도를 전례 없는 수준으로 낮추는 성과를 거두었습니다.
셋째, 논블로킹 로그 처리 프로세스입니다. 보안 기밀성을 유지하기 위한 로그(GateLogCdo) 적재 프로세스는 메인 라우팅 스트림의 응답 속도에 부정적인 영향도 주지 않도록 극도로 정교하게 격리되었습니다. 이는 감사 로그를 데이터베이스에 쓰고 네트워크로 전송하는 I/O 오버헤드 전체를 메인 런타임 흐름과 완전히 디커플링된 독립된 백그라운드 스레드 풀로 위임하는 구조입니다. 이로써 단 하나의 요청도 로그 적재 지연으로 인해 응답이 늦어지는 현상을 원천 차단하였으며, 게이트웨이 본연의 의무인 보안 검증과 라우팅 중재 기능이 무결하게 유지되는 아키텍처를 구성할 수 있었습니다.
큐 관리 시스템
MSA 환경에서 특정 도메인 서비스의 장애나 성능 저하는 단일 컴포넌트의 중단으로 끝나지 않습니다. 분산 시스템 환경에서 특정 마이크로서비스에 마케팅 이벤트나 갑작스러운 트래픽 폭주가 집중될 경우, 해당 서비스의 응답 지연이 상위 호출 레이어인 API 게이트웨이의 커넥션 풀 고갈로 이어지는 장애 전파 현상이 빈번히 발생합니다. 특히 다중 개발사가 각 도메인을 독립적으로 관리하는 멀티 벤더 환경에서는, 하드웨어 장비 수준의 일률적인 IP 차단이나 인프라 대역폭 제한만으로는 비즈니스 요구사항에 유연하게 대응하기 어렵습니다. 인프라 설정에 종속되지 않으면서, 각 API 라우트별 중요도와 가용 용량에 따라 트래픽 임계치를 동적으로 변경하고, 임계치를 초과한 사용자에게는 무조건적인 거부 대신 순차적인 진입 기회를 부여하는 대기열 및 가용성 통제 아키텍처가 요구되는 이유입니다. 이를 위해 데이터베이스 기반으로, 게이트웨이 런타임 중에 실시간으로 서비스별 가용 용량을 설정하고, 초과된 트래픽을 리액티브 논블로킹 스트림 내에서 안전하게 대기시키는 커스텀 동적 대기열 시스템을 구축하였습니다.
대기열 제어의 진입 관문이 되는 QueueFilter는 SCG의 표준 확장 규격인 AbstractGatewayFilterFactory를 상속받아 구현되었습니다. 이 필터는 매 요청마다 라우트 ID를 추출하여 독립된 트래픽 격리벽을 세우고, 사용자의 차례가 올 때까지 Netty의 이벤트 루프 스레드를 점유하지 않는 논블로킹 방식으로 대기 시그널을 중재합니다.
이러한 대기열 시스템이 게이트웨이 자체의 자원 고갈 없이 백엔드를 방어할 수 있는 비결은 리액티브 스트림과 자바 표준 동시성 API를 매끄럽게 연결한 아키텍처적 유기성에 있습니다.
첫째, CompletableFuture와 리액티브 스트림 간의 '논블로킹 브릿지'입니다. 전통적인 블로킹 구조에서 대기열을 구현하려면 스레드를 잠재우는 방식을 사용해야 하므로, 대기하는 사용자 수만큼 스레드가 고갈되어 게이트웨이가 먼저 무너지게 됩니다. 이를 극복하기 위해 QueueContext 내부에서 각 대기 세션마다 비어있는 CompletableFuture를 생성하고 이를 ConcurrentHashMap에 관리하도록 설계했습니다. 사용자는 자신의 차례가 올 때까지 Netty의 귀중한 이벤트 루프 스레드를 단 1ms도 붙잡지 않고 가용 스레드를 반환하게 되며, 내부 연산이 완료되어 QueueContext로부터 차례 완료 시그널이 떨어지는 순간에만 리액티브 파이프라인이 깨어나 후속 라우팅 체인을 타게 되는 극도의 자원 효율성을 달성했습니다.
둘째, 시스템 한계 수용을 위한 투 트랙 차단 전략과 비동기 백엔드 연동입니다. 인메모리 큐가 허용하는 범위 안의 트래픽은 QueueContext에 적재되어 리액티브 계류 상태로 안전하게 순서를 기다립니다. 반면, 인메모리 대기열마저 포화 상태에 이르면 대기 세션 정보를 백엔드로 넘겨 분산 이벤트를 유도한 후, QueueFullException을 던져 예외 처리 합니다. 이로 인해 게이트웨이는 인메모리 오버플로우 위험으로부터 완벽히 보호되며, 클라이언트는 표준 HTTP 응답을 통해 대기 ID를 부여받아 백엔드와 아웃 오브 밴드로 소통할 수 있는 단단한 아키텍처적 완충 지대를 확보하게 되었습니다.
셋째, 리액티브 라이프사이클 기반의 자원 누수 원천 차단 메커니즘입니다. 분산 대기열 환경에서 가장 빈번하게 발생하는 실무적 장애는 사용자가 대기 도중 새로고침을 누르거나 창을 닫아 연결이 끊어졌음에도, 인메모리 자원이나 동시성 슬롯이 해제되지 않고 남아있는 데드락 현상입니다. 이 시스템은 리액티브 스트림의 라이프사이클 시그널을 정교하게 낚아채 이 문제를 원천 차단합니다. 즉, 스트림 취소 시 발동하는 doOnCancel로 고스트 세션을 즉시 소거하고, doFinally 블록을 통해 요청의 정상 처리 완료는 물론 네트워크 에러나 타임아웃 등 어떠한 이유로든 HTTP 요청-응답 사이클이 최종 종료되면 대기 중이던 다음 사용자를 연쇄적으로 깨워 올립니다. 만약 가용 슬롯을 점유하기 전에 취소된 유령 대기자를 만나면 메모리에서 조용히 소거하는 정화 로직까지 수행합니다. 이처럼 톱니바퀴처럼 맞물린 라이프사이클 통제를 통해, 단 한 건의 자원 누수나 교착 상태 없이 안정성을 유지하는 쾌거를 이루어 낼 수 있었습니다.
마치며
지금까지 통합 인증 및 인가 필터 구현과 대기열 관리 서비스 구현 방법을 살펴보았습니다. 단순히 외부 요청을 백엔드로 전달하던 API 게이트웨이를 이용해, 보안과 트래픽 제어의 중앙화를 통한 멀티 벤더 개발 생산성의 극대화 및 큐 관리 서비스를 통한 인프라 안정성 확보를 이루어냈습니다.
결국 아키텍처의 진화는 경직된 하드웨어나 인프라의 제약에 비즈니스를 맞추는 것이 아니라, 급변하는 비즈니스 요구사항과 복잡한 협업 구조를 뒷받침할 수 있도록 소프트웨어를 유연하게 진화시키는 과정 그 자체입니다. 본 프로젝트에서 SCG를 중심으로 만들어 낸 동적 제어 아키텍처가, 멀티 벤더 환경에서 MSA를 도입하며 분산 보안 파편화와 대규모 트래픽 통제를 고민하는 다른 개발자들에게 좋은 참고가 되길 바랍니다.
Hustle Paul