Redis Production Configuration Guide (한국어)
현재 구성(코드 기본값)
섹션 제목: “현재 구성(코드 기본값)”| 설정 | 값 | 위치 |
|---|---|---|
REDIS_URL 환경 변수 |
redis://redis:6379(compose), 선택 사항 |
rateLimiter.ts:5, .env.example |
REDIS_KEY_PREFIX 환경 변수 |
omniroute:(기본값) |
rateLimiter.ts, redisQuotaStore.ts, redisCircuitBreakerStore.ts, .env.example |
QUOTA_STORE_REDIS_URL 환경 변수 |
별도 설정이며 REDIS_URL과 다를 수 있음 |
quota/storeFactory.ts |
QUOTA_STORE_DRIVER |
"sqlite"(기본값), 선택적으로 "redis" |
quota/storeFactory.ts |
ioredis maxRetriesPerRequest |
3 |
rateLimiter.ts 클라이언트 생성 |
enableReadyCheck |
설정되지 않음(ioredis 기본값: true) |
— |
lazyConnect |
설정되지 않음(ioredis 기본값: false) |
— |
retryStrategy |
설정되지 않음(ioredis 기본값: 200ms 기반, 지수 증가) | — |
| TLS / 비밀번호 / DB 인덱스 | 구성되지 않음 | — |
| Sentinel / Cluster | 구성되지 않음 — 독립 실행형 단일 노드만 지원 | — |
키 네임스페이스
섹션 제목: “키 네임스페이스”OmniRoute는 호스트에서 실행되는 다른 서비스와 Redis 인스턴스를 공유합니다. 네임스페이스가 없으면 auth:api_key:<sha256> 또는 rl:* 같은 키가 동일한 Redis를 사용하는 다른 애플리케이션의 키와 충돌할 수 있습니다(이 인스턴스는 다른 서비스와 함께 127.0.0.1:6379에서 Redis를 실행합니다).
모든 OmniRoute 키에 접두사를 추가하려면 REDIS_KEY_PREFIX를 비어 있지 않은 문자열로 설정하세요.
# .env — 모든 OmniRoute 키가 omniroute:rl:*, omniroute:auth:*, omniroute:quota:*, omniroute:warmup:cb:* 형식이 됩니다.REDIS_KEY_PREFIX=omniroute:- 기본값:
omniroute:(REDIS_KEY_PREFIX가 설정되지 않았거나 비어 있을 때 적용). - 적용 대상: 속도 제한기 + 인증 캐시(
keyPrefix를 통해 공유ioredis클라이언트 사용), 할당량 저장소(KEY_PREFIX = "${REDIS_KEY_PREFIX}quota"), 워밍업 회로 차단기(KEY_PREFIX = "${REDIS_KEY_PREFIX}warmup:cb:"). - Redis에 키가 이미 존재할 때 접두사를 변경하면 기존 키는 고립됩니다(TTL / LRU를 통해 만료됨). 안전하게 변경할 수 있으며 마이그레이션은 필요하지 않습니다. 단, 금지된 것으로 표시된 연결의 워밍업 회로 차단기 키는 예외입니다. 이 키는 TTL 없이 영구 저장되므로
redis-cli --scan --pattern '<old-prefix>warmup:cb:*'로 남은 키를 나열하고 삭제하세요. - **ioredis
keyPrefix**는 쓰기 시 자동으로 접두사를 추가하고 읽기 시에는 접두사를 제거하므로 애플리케이션 코드에는 접두사가 표시되지 않습니다.
권장 프로덕션 튜닝
섹션 제목: “권장 프로덕션 튜닝”1. 연결 풀 / 클라이언트 옵션(ioredis Redis 생성자)
섹션 제목: “1. 연결 풀 / 클라이언트 옵션(ioredis Redis 생성자)”현재 코드는 사용자 지정 옵션 없이 단일 new Redis(url)를 생성합니다. 프로덕션
다중 레플리카 배포에서는 코드에 클라이언트 팩토리를 전달하거나 getRedisClient()를 래핑하세요.
const redis = new Redis(REDIS_URL, { maxRetriesPerRequest: null, // 재시도 제한 없음. retryStrategy가 결정하도록 함 enableReadyCheck: true, // 호출을 수락하기 전에 서버가 준비되었는지 확인 lazyConnect: true, // 생성 시 연결하지 않고 첫 번째 호출까지 대기 retryStrategy: (times) => { if (times > 10) return null; // 10회 재시도 후 포기 → 나중에 다시 연결 return Math.min(times * 200, 5000); // 200ms, 400ms, …, 최대 5s }, enableAutoPipelining: true, // 동시 명령을 하나의 TCP 쓰기로 병합 keepAlive: 10000, // 10s마다 TCP 연결 유지});주요 트레이드오프:
maxRetriesPerRequest: null+retryStrategy— 일시적인 Redis 재시작으로 인해 모든 요청이 즉시 실패하지 않도록 하므로 프로덕션 환경에 적합합니다.checkRateLimit()의 인메모리 폴백이 실패 경로를 처리합니다.lazyConnect: true— 서버가 연결 수락을 시작하기 전에 Redis가 실행 중이어야 한다는 시작 시점의 의존성을 방지합니다.enableAutoPipelining: true— 동시 속도 제한 검사에서 왕복 횟수를 줄입니다. 단일 연결에서 50 RPS를 초과할 때 유용합니다.
2. Redis 서버 구성(redis.conf)
섹션 제목: “2. Redis 서버 구성(redis.conf)”# 메모리maxmemory 80% # OS 페이지 캐시를 위한 공간 확보maxmemory-policy allkeys-lru # 메모리 부족 시 오래된 인증 캐시 항목 제거
# 영속성(선택 사항 — OmniRoute는 영속성 없이도 충돌로부터 안전함)save 300 1 # 키가 1개 이상 변경된 경우 최소 5분마다 스냅샷 생성appendonly no # AOF는 불필요함. 데이터는 재생성 가능appendfsync no # fsync 오버헤드 없음(RDB로 충분함)
# 네트워킹timeout 0 # 유휴 연결을 끊지 않음tcp-keepalive 300 # 5분 연결 유지tcp-backlog 511 # 버스트 부하를 위한 연결 백로그
# 성능hz 10 # 기본값. 지연 시간에 민감한 경우 100activedefrag yes # 조각화가 10%를 초과하면 자동 조각 모음maxmemory-policy allkeys-lru의 트레이드오프: 메모리가 부족할 때 인증 캐시 항목이
제거될 수 있습니다. 이는 안전합니다. setCachedApiKey는 캐시 미스 발생 시 항상 다시
채우며, SQLite 폴백이 최종 데이터 원본입니다. 속도 제한기 Lua 스크립트는 설계상
수명이 짧은 작은 키를 생성합니다.
3. Docker Compose 설정
섹션 제목: “3. Docker Compose 설정”프로덕션 compose(docker-compose.prod.yml)는 redis:8.6.2-alpine을 사용합니다. 다음을 추가하세요.
redis: image: redis:8.6.2-alpine command: [ "redis-server", "--maxmemory", "512mb", "--maxmemory-policy", "allkeys-lru", "--activedefrag", "yes", "--save", "300 1", ] healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 10s timeout: 3s retries: 3 start_period: 5s4. 다중 인스턴스 / 확장 고려 사항
섹션 제목: “4. 다중 인스턴스 / 확장 고려 사항”모든 레플리카에 단일 Redis 사용 — 속도 제한기 Lua 스크립트는 하나의 권위 있는 키 공간에 의존합니다. 레플리카별로 여러 Redis 인스턴스를 사용하면 원자성이 사라지고 허용량이 두 배가 됩니다. 모든 애플리케이션 레플리카에 단일 Redis 또는 장애 조치 기능이 있는 Redis Sentinel 클러스터를 사용하세요.
연결 수: 각 애플리케이션 레플리카는 Redis에 2개의 TCP 연결을 엽니다 (속도 제한기 클라이언트 + 할당량 저장소 클라이언트). 레플리카 10개에서는 → 20개 연결이며, 기본 Redis 인스턴스의 연결 상한인 10k보다 훨씬 적습니다.
5. 모니터링
섹션 제목: “5. 모니터링”상태 확인 엔드포인트를 통해 노출하세요.
// src/app/api/monitoring/health/route.ts는 이미 rateLimiter 함수를 호출함// Redis 전용 검사 추가:// 1. ioredis .ping()을 통한 PING 지연 시간// 2. INFO memory를 통한 메모리 사용량// 3. INFO clients를 통한 연결 수// 4. maxmemory-policy의 적중률(evicted_keys / keyspace_hits)모니터링할 주요 메트릭:
- 초당 제거된 키 — 지속적으로 0이 아니면
maxmemory를 늘리세요 - 차단된 클라이언트 — 0이 아니면 느린 Lua 스크립트 또는 높은 경합을 의미할 수 있습니다
- 거부된 연결 — 연결 제한에 도달했음을 의미하며, 연결이 20개인 경우에는 드뭅니다
아키텍처 다이어그램
섹션 제목: “아키텍처 다이어그램”flowchart LR subgraph App["앱 복제본"] RL[rateLimiter.ts] AK[apiKeys.ts] QS[redisQuotaStore.ts] end RL -- "REDIS_URL" --> R1[(Redis\n공유)] AK -- "RL의 클라이언트 재사용" --> R1 QS -- "QUOTA_STORE_REDIS_URL" --> R2[(Redis\n할당량 저장소)] R1 --> R2 -- "동일한 인스턴스 사용 가능" --> R1참고 자료
섹션 제목: “참고 자료”| 파일 | 용도 |
|---|---|
src/shared/utils/rateLimiter.ts |
기본 Redis 클라이언트, Lua 요청 속도 제한 스크립트, 인메모리 폴백 |
src/lib/db/apiKeys.ts |
인증 캐시 — Redis→SQLite 폴백 |
src/lib/quota/redisQuotaStore.ts |
선택적 할당량 저장소를 위한 별도의 Redis 클라이언트 |
src/lib/quota/storeFactory.ts |
sqlite와 redis 할당량 드라이버 간 전환 |
docker-compose.prod.yml |
프로덕션 Redis 컨테이너(이미지 redis:8.6.2-alpine) |
.env.example |
Redis 환경 변수 문서 |
src/app/api/local/redis/ |
개발 컨테이너 오케스트레이션용 API 라우트 |
bin/cli/commands/redis.mjs |
개발 컨테이너 오케스트레이션용 CLI 명령어 |
HagiCode
HagiCode는 구조화된 워크플로, 다중 에이전트 실행, Hero Dungeon 뷰를 갖춘 에이전트 코딩 작업 공간입니다.
더 스마트하고 빠르며 즐거운 에이전트 워크플로로 유용한 소프트웨어를 만드세요.

- Smart구조화된 워크플로는 의도를 아이디어부터 배포까지 실행 가능한 경로로 바꿉니다.
- Efficient다중 에이전트 워크플로로 조사, 구현, 검토를 병렬로 진행합니다.
- FunHero Dungeon은 긴 코딩 세션을 시각적이고 협업적인 경험으로 만듭니다.