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 전체 구조 · 요청 처리와 규칙 관리
판단은 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으로 규칙 변경을 감지합니다
지금 상태
제한 엔진·Spring Boot 스타터를 Maven Central의 io.github.openfluxgate로 v0.3.7 공개 배포했습니다. 사내 POC를 거쳐 현재 지마켓 가입 서비스에 연동·도입되어 있습니다. 로그인 서비스로의 확대는 향후 적용 대상으로 두고 있습니다.
현재는 LTS 예정 (1.0.0)에서 멀티 밴드 처리와 장애 시 재시도 경계를 개선하고 있습니다.
구현 범위 · 사용 기술
상세 기능과 사용 기술 보기
- fluxgate-coreBucket4j 기반 제한 엔진
- fluxgate-redis-ratelimiterLua 스크립트 기반 분산 제한
- fluxgate-mongo-adapterMongoDB 규칙 저장 · 동적 갱신
- spring-boot3 / boot2 starterJava 17+ jakarta / Java 11+ javax 자동 설정
- fluxgate-testkit통합 테스트 유틸리티와 샘플 앱
- FluxGate Studio규칙 CRUD · 활성화 설정 · 규칙 현황
돌아보면
반복 조회와 비용 증가라는 실제 문제에서 출발해, 도입 방식을 조율하고 가입 서비스 연동까지 이어 갔습니다. 제한 엔진뿐 아니라 스타터와 규칙 관리 도구를 함께 제공해 적용과 운영에 필요한 부분을 갖췄습니다.