프로젝트
오픈소스Redis 기반 분산 Rate Limiting

OpenFluxGate

서버가 늘어도 전체 허용량을 유지하도록 동시 요청의 토큰 확인·차감을 Lua 스크립트 하나로 묶어 Redis에서 원자적으로 실행했습니다. 규칙 변경은 Redis Pub/Sub으로 알려 각 앱이 감지하고 캐시를 갱신하게 했습니다. 제한 엔진과 Spring Boot 스타터를 Maven Central에 배포하고, 규칙을 관리하는 FluxGate Studio와 문서 포털을 별도로 개발했습니다.

Spring Boot 2.7 / 3.x 스타터, Maven Central v0.3.x 배포

기간
2025.11 — 진행 중
팀
개인 오픈소스
역할
설계 · 개발 · 배포

가입을 돕는 조회가 반복 요청에 쓰였을 때

사업자회원 가입 과정에서 사업자 정보 조회가 반복 호출되는 사례가 있었습니다. 가입 여부와 사업자 정보가 대량으로 확인되고, 외부 조회 비용도 증가했습니다. 이 사례를 계기로 가입 인증 단계의 요청량을 제어하고, 이후 로그인 등 다른 도메인에도 적용할 수 있는 공통 도구를 만들고자 했습니다.

운영 중에는 제한 기준을 바꿔야 할 수 있습니다. 그때마다 각 도메인의 코드를 수정하고 배포하는 부담을 줄이기 위해, 공통 규칙은 어드민에서 관리하고 서비스에서는 그 규칙을 적용하도록 구성했습니다. 제한 로직을 서비스마다 다시 만들지 않도록 엔진과 Spring Boot 스타터를 개인 오픈소스로 제공했습니다.

전체 아키텍처

애플리케이션에 붙는 요청 처리 경로와 운영자가 규칙을 바꾸는 관리 경로를 분리했습니다. Direct Redis와 HTTP API 연동을 구분하고, 규칙 저장소는 어댑터 설정에 따라 선택합니다.

FluxGate 전체 구조 · 요청 처리와 규칙 관리

FluxGate 전체 구조 · 요청 처리와 규칙 관리Client의 요청은 Spring Boot Filter에서 RateLimitHandler로 전달됩니다. Direct Redis는 앱 내 FluxGate 엔진이, HTTP API는 별도 Rate Limit Service의 엔진이 Redis Lua를 호출합니다. 규칙 관리는 Studio에서 Keycloak 인증을 적용한 Admin API를 거쳐 MongoDB에 저장합니다. Mongo adapter를 설정한 엔진은 Caffeine 캐시로 규칙을 조회하고, Pub/Sub 변경 알림은 캐시를 무효화해 다음 조회에서 원본을 다시 읽게 합니다. HTTP 샘플의 별도 Redis 규칙 원본과 Mongo 원본은 자동 통합되지 않습니다. 요청 처리 · 호출 방식에 따라 엔진 위치 선택 Client Spring Boot 요청 진입 Filter RateLimitHandler Direct Redis HTTP API 앱 내 FluxGate 엔진 스타터 · Redis 직접 연결 Rate Limit Service 별도 서비스의 FluxGate 엔진 Redis · Lua 허용량 확인·차감 허용 제한 · 429 HTTP 샘플의 규칙 원본은 별도 Redis입니다. Mongo 원본과 자동 통합되지 않습니다. 규칙 관리 · Mongo adapter를 설정한 엔진의 경로 Studio UI Next.js Admin API Keycloak 인증 저장 MongoDB 규칙 원본 Mongo adapter 설정한 엔진에서 조회 Caffeine 서비스별 규칙 캐시 저장 후 발행 Redis Pub/Sub 변경 알림 캐시 무효화 → 다음 조회에서 Mongo 원본 재적재 Lua는 요청 판단, Pub/Sub은 규칙 변경 알림을 담당합니다. FluxGate 전체 구조 · 요청 처리와 규칙 관리Client의 요청은 Spring Boot Filter에서 RateLimitHandler로 전달됩니다. Direct Redis는 앱 내 FluxGate 엔진이, HTTP API는 별도 Rate Limit Service의 엔진이 Redis Lua를 호출합니다. 규칙 관리는 Studio에서 Keycloak 인증을 적용한 Admin API를 거쳐 MongoDB에 저장합니다. Mongo adapter를 설정한 엔진은 Caffeine 캐시로 규칙을 조회하고, Pub/Sub 변경 알림은 캐시를 무효화해 다음 조회에서 원본을 다시 읽게 합니다. HTTP 샘플의 별도 Redis 규칙 원본과 Mongo 원본은 자동 통합되지 않습니다. 요청 처리 Client Spring Boot 요청 진입 Filter RateLimitHandler Direct Redis 앱 내 FluxGate 엔진 Redis 직접 연결 HTTP API Rate Limit Service FluxGate 엔진 Redis · Lua 허용량 확인·차감 허용 제한 · 429 HTTP 샘플은 별도 Redis 규칙을 읽습니다. Mongo 원본과 자동 통합되지 않습니다. 규칙 관리 · Mongo adapter 설정 시 Studio UI Next.js Admin API Keycloak 인증 저장 MongoDB 규칙 원본 Mongo adapter 설정한 엔진에서 조회 저장 후 발행 Redis Pub/Sub 다음 조회 시 원본 재적재 캐시 무효화 Caffeine 규칙 캐시 서비스별로 사용 Lua는 요청 판단, Pub/Sub은 규칙 변경 알림을 담당합니다.
요청은 Filter와 Handler를 거쳐 Redis에서 판단합니다. Studio의 MongoDB 규칙은 해당 어댑터를 설정한 엔진이 읽으며, HTTP 샘플의 Redis 규칙 저장소와는 별개입니다. 서비스별 내부 구성과 배치는 설정에 따라 달라집니다.

판단은 Redis 안에서 한 번에

남은 토큰을 확인하는 것과 차감하는 것이 분리되어 있으면, 동시에 들어온 요청이 같은 잔여량을 보고 함께 통과할 수 있습니다. 확인과 차감을 Lua 스크립트 하나로 묶어 Redis 안에서 원자적으로 실행합니다. 토큰 보충 시각은 Redis TIME을 사용해 애플리케이션 인스턴스 간 시계 차이의 영향을 줄였습니다.

SCRIPT LOAD로 Lua 본문을 등록하고 반환된 SHA1을 보관한 뒤, EVALSHA에 대상 키(KEYS)와 제한값(ARGV)을 전달해 같은 스크립트를 재사용합니다. 본문을 매번 전송하지 않도록 한 선택입니다. Redis 재시작 등으로 NOSCRIPT가 발생하면 EVAL로 해당 요청을 처리하고 스크립트를 다시 적재합니다. 요청 판단은 계속 Redis에서 실행하므로 핫키와 Redis 가용성은 별도로 고려해야 합니다.

무엇을 같은 대상으로 볼 것인가

제한 알고리즘보다 먼저 정해야 했던 건 "누구를 같은 대상으로 셀 것인가"였습니다. IP, 사용자 ID, API 키 중 하나로 제한할 수도 있고, IP와 사용자를 합친 복합 키로 더 잘게 나눌 수도 있게 했습니다. 초 · 분 · 시간 단위 제한을 함께 거는 멀티 밴드 규칙과, 즉시 거절하는 대신 토큰 보충을 기다리는 정책도 넣었습니다.

Envoy 검토에서 공통 라이브러리 도입으로

초기에는 Envoy를 통한 유량 제어 도입을 추진했습니다. 당시 사내 개발 스택은 Java·JVM 중심이었고, 새로운 프록시 계층의 설정과 운영, 장애 분석을 익혀야 한다는 부담이 있었습니다. 요청이 별도의 제어 지점에 집중될 경우 병목이나 장애의 영향이 커질 수 있다는 우려도 논의했습니다.

팀 내 논의를 거쳐 기존 Java 서비스에 연동할 수 있는 공통 라이브러리로 방향을 조정했습니다. 운영자는 어드민에서 공통 규칙을 관리하고, 서비스는 라이브러리를 통해 요청을 식별하고 허용·제한을 판단합니다. 같은 제한 로직을 도메인마다 반복 구현하는 부담을 줄이고, 익숙한 JVM 환경에서 적용과 운영을 이어 가기 위한 선택입니다.

재배포 없이 변경하는 범위는 요청 한도 등 운영 규칙입니다. 라이브러리 기능 자체를 수정할 때는 적용 서비스의 버전 업데이트와 배포가 필요합니다. Redis에 대한 공통 의존성도 남으므로, 라이브러리 도입만으로 병목과 장애 위험이 모두 사라지는 것은 아닙니다.

Spring Boot 애플리케이션의 Filter가 요청을 받아 RateLimitHandler에 판단을 위임합니다. 앱이 Redis를 직접 사용하는 Direct Redis 방식과 별도 Rate Limit Service를 HTTP API로 호출하는 방식을 제공했습니다. HTTP 경로에서도 실제 토큰 버킷 판단은 서비스 뒤의 Redis에서 처리합니다.

Redis 직접 연결 데모(FluxGate 0.4.0)에서는 요청량 초과와 제한 처리 오류를 구분합니다. 한도를 초과하면 HTTP 429로 차단하고, Redis 오류로 판단할 수 없으면 요청을 통과시키는 Fail Open으로 동작합니다. 필터에 타임아웃 예외를 주입한 로컬 검증에서도 서비스로 요청이 전달되는 것을 확인했습니다.

제한 계층의 오류가 서비스 요청을 막지 않도록 하는 대신, 장애 중에는 요청 제한이 적용되지 않는 절충입니다. 오류가 확인되기 전까지 Redis 응답을 기다릴 수 있어 즉시 통과를 뜻하지는 않습니다. 이 설명은 데모에서 확인한 범위이며 실제 가입 서비스의 장애 정책과는 구분합니다.

규칙 관리와 요청 처리를 분리

FluxGate Studio는 Next.js UI와 Spring Boot Admin API로 구성했습니다. Admin API는 Keycloak 기반 인증을 사용하고, 규칙 생성·수정·활성화와 규칙 현황을 제공합니다. 규칙은 MongoDB에 저장하며 MongoDB 어댑터를 설정한 앱이 읽습니다. HTTP 샘플은 별도의 Redis 규칙 저장소를 사용합니다.

규칙 변경은 Redis Pub/Sub으로 알리고, 이를 구독한 앱의 Caffeine 규칙 캐시를 무효화합니다. 다음 조회에서 원본을 다시 읽어 캐시에 적재합니다. 현재 LTS 구현은 주기 조회도 두어 알림 누락을 보완합니다. 요청 차감의 Lua 원자성과 규칙 변경 알림은 별도 역할이며, Studio는 규칙 관리, Micrometer는 요청 처리 관측을 담당합니다.

Spring Boot 2.7과 3.x 스타터를 각각 제공하고 Maven Central에 배포했습니다. 적용 예제와 Docusaurus 문서 포털을 함께 관리합니다.

현재 LTS에서 다루는 원자성과 재시도 경계

현재 LTS 소스에서는 멀티 밴드의 조건을 모두 검사한 뒤 차감합니다. 여러 키가 같은 Redis 슬롯에 있을 때의 원자 처리와, 다른 슬롯에 걸친 실패 뒤 보상 환급은 구분합니다. 슬롯을 넘는 환급 전체는 원자적이지 않습니다.

실행 여부가 불명확한 차감은 자동 재시도를 제한해 이중 차감 위험을 다룹니다. 공개 배포본 0.3.7 이후 LTS 개발에서 추가한 개선입니다.

Lua 스크립트로 차감을 원자적으로 판단하고, Redis Pub/Sub으로 규칙 변경을 감지합니다

Lua 스크립트로 차감을 원자적으로 판단하고, Redis Pub/Sub으로 규칙 변경을 감지합니다요청마다 Redis에서 Lua 스크립트 하나가 실행됩니다. 스크립트는 Redis TIME으로 현재 시각을 읽고, 경과 시간만큼 토큰을 보충하고, 모든 밴드의 잔여를 확인한 뒤 전부 차감하거나 하나도 차감하지 않습니다. Redis는 스크립트 실행 중 다른 명령을 끼워 넣지 않으므로 잔여 1을 동시에 노린 A와 B 중 먼저 실행된 A만 허용되고 B는 429로 거부됩니다. 앱이 Redis를 직접 쓰는 Direct Redis 방식과 HTTP API로 Rate Limit Service를 호출하는 방식 모두 같은 스크립트로 판단합니다. 규칙은 Studio UI에서 Keycloak 인증을 쓰는 Admin API로 바꾸고 MongoDB에 저장합니다. 변경이 커밋되면 Admin API가 Redis Pub/Sub 채널에 발행하고, 구독한 앱 인스턴스가 규칙 캐시를 무효화한 뒤 다시 읽습니다. 현재 LTS는 주기 조회로 알림 누락을 보완합니다.① 차감 판단 · Lua 스크립트 한 번의 실행요청 A요청 B먼저 실행A가 끝난 뒤 실행Redistoken_bucket_consume.lua1Redis TIME으로 현재 시각 읽기2경과 시간만큼 토큰 보충3모든 밴드의 잔여 확인4전부 차감 또는 하나도 차감 안 함실행 중에는 다른 명령이 끼어들지 않습니다A 허용잔여 1 → 0B 429 거부잔여 0 · 상태 유지Direct Redis와 HTTP API(Rate Limit Service) 두 연동 방식 모두 이 스크립트로 판단합니다.② 규칙 변경 감지 · Redis Pub/SubStudio UINext.jsAdmin APIKeycloak 인증저장MongoDB커밋 후 발행Redis Pub/Subrule-reload 채널앱 인스턴스 1앱 인스턴스 2앱 인스턴스 N구독캐시 무효화→ 재조회알림 누락은 주기 조회로 보완 (LTS)차감 판단의 원자성은 Lua가, 규칙 변경 전파는 Pub/Sub이 맡습니다.Lua 스크립트로 차감을 원자적으로 판단하고, Redis Pub/Sub으로 규칙 변경을 감지합니다요청마다 Redis에서 Lua 스크립트 하나가 실행됩니다. 스크립트는 Redis TIME으로 현재 시각을 읽고, 경과 시간만큼 토큰을 보충하고, 모든 밴드의 잔여를 확인한 뒤 전부 차감하거나 하나도 차감하지 않습니다. Redis는 스크립트 실행 중 다른 명령을 끼워 넣지 않으므로 잔여 1을 동시에 노린 A와 B 중 먼저 실행된 A만 허용되고 B는 429로 거부됩니다. 앱이 Redis를 직접 쓰는 Direct Redis 방식과 HTTP API로 Rate Limit Service를 호출하는 방식 모두 같은 스크립트로 판단합니다. 규칙은 Studio UI에서 Keycloak 인증을 쓰는 Admin API로 바꾸고 MongoDB에 저장합니다. 변경이 커밋되면 Admin API가 Redis Pub/Sub 채널에 발행하고, 구독한 앱 인스턴스가 규칙 캐시를 무효화한 뒤 다시 읽습니다. 현재 LTS는 주기 조회로 알림 누락을 보완합니다.① 차감 판단 · Lua 스크립트요청 A요청 B먼저 실행A가 끝난 뒤Redisconsume.lua1Redis TIME으로 현재 시각2경과 시간만큼 토큰 보충3모든 밴드의 잔여 확인4전부 차감 또는 전부 안 함실행 중 다른 명령이 끼어들지 않음A 허용잔여 1 → 0B 429 거부잔여 0Direct Redis · HTTP API 모두 같은 스크립트② 규칙 변경 감지 · Pub/SubStudio UINext.jsAdmin APIKeycloak 인증저장MongoDB커밋 후 발행Redis Pub/Subrule-reload구독앱 인스턴스들캐시 무효화재조회알림 누락은 주기 조회로 보완 (LTS)차감의 원자성은 Lua가,규칙 변경 전파는 Pub/Sub이 맡습니다.
LTS 예정 (1.0.0)의 요청 판단과 변경 전파입니다. 한 Lua 실행 안의 차감 판단과 Pub/Sub 알림은 별개이며, 알림 누락은 주기 조회로 보완합니다. A·B는 설명용 예시입니다.

지금 상태

제한 엔진·Spring Boot 스타터를 Maven Central의 io.github.openfluxgate로 v0.3.7 공개 배포했습니다. 사내 POC를 거쳐 현재 지마켓 가입 서비스에 연동·도입되어 있습니다. 로그인 서비스로의 확대는 향후 적용 대상으로 두고 있습니다.

현재는 LTS 예정 (1.0.0)에서 멀티 밴드 처리와 장애 시 재시도 경계를 개선하고 있습니다.

구현 범위 · 사용 기술

상세 기능과 사용 기술 보기
  • Java
  • Spring Boot 2.7 / 3.x
  • Bucket4j
  • Redis · Lua
  • MongoDB
  • Micrometer
  • Next.js
  • Keycloak
  • fluxgate-coreBucket4j 기반 제한 엔진
  • fluxgate-redis-ratelimiterLua 스크립트 기반 분산 제한
  • fluxgate-mongo-adapterMongoDB 규칙 저장 · 동적 갱신
  • spring-boot3 / boot2 starterJava 17+ jakarta / Java 11+ javax 자동 설정
  • fluxgate-testkit통합 테스트 유틸리티와 샘플 앱
  • FluxGate Studio규칙 CRUD · 활성화 설정 · 규칙 현황

돌아보면

반복 조회와 비용 증가라는 실제 문제에서 출발해, 도입 방식을 조율하고 가입 서비스 연동까지 이어 갔습니다. 제한 엔진뿐 아니라 스타터와 규칙 관리 도구를 함께 제공해 적용과 운영에 필요한 부분을 갖췄습니다.