콘텐츠로 이동

아키텍처 가이드

이 문서는 Continuum Router의 아키텍처, 설계 결정, 확장 지점에 대한 종합적인 개요를 제공합니다.

개요

Continuum Router는 깔끔한 4계층 아키텍처를 사용하여 명확한 관심사 분리, 테스트 가능성, 유지보수성을 제공하는 고성능, 프로덕션 레디 LLM API 라우터로 설계되었습니다. 이 아키텍처는 도메인 주도 설계 원칙과 의존성 역전을 따라 견고하고 확장 가능한 시스템을 만듭니다.

아키텍처 목표

  1. 관심사 분리: 각 계층은 단일의 명확하게 정의된 책임을 가집니다
  2. 의존성 역전: 상위 계층은 구체적인 구현이 아닌 추상화에 의존합니다
  3. 테스트 가능성: 각 컴포넌트를 독립적으로 단위 테스트할 수 있습니다
  4. 확장성: 기존 코드를 수정하지 않고 새로운 기능을 추가할 수 있습니다
  5. 성능: 깔끔한 아키텍처를 유지하면서 최소한의 오버헤드
  6. 안정성: 포괄적인 오류 처리를 갖춘 빠른 실패 설계

4계층 아키텍처

┌─────────────────────────────────────────────────────────────────┐
│                        HTTP Layer                               │
│  ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐   │
│  │     Routes      │ │   Middleware    │ │   Handlers      │   │
│  │                 │ │                 │ │                 │   │
│  │ • /v1/models    │ │ • Logging       │ │ • Streaming     │   │
│  │ • /v1/chat/*    │ │ • Metrics       │ │ • Responses API │   │
│  │ • /v1/responses │ │ • Rate Limit    │ │ • DTOs          │   │
│  │ • /admin/*      │ │ • Auth          │ │ • Error         │   │
│  └─────────────────┘ └─────────────────┘ └─────────────────┘   │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│                     Services Layer                              │
│  ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐   │
│  │ Backend Service │ │  Model Service  │ │ Proxy Service   │   │
│  │                 │ │                 │ │                 │   │
│  │ • Pool Mgmt     │ │ • Aggregation   │ │ • Routing       │   │
│  │ • Load Balance  │ │ • Caching       │ │ • Streaming     │   │
│  │ • Health Check  │ │ • Discovery     │ │ • Retry Logic   │   │
│  │                 │ │ • Metadata      │ │                 │   │
│  └─────────────────┘ └─────────────────┘ └─────────────────┘   │
│                                                                 │
│  ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐   │
│  │ Health Service  │ │ Service Registry│ │ Deduplication   │   │
│  │                 │ │                 │ │                 │   │
│  │ • Monitoring    │ │ • Lifecycle     │ │ • Cache         │   │
│  │ • Status Track  │ │ • Dependencies  │ │ • Request Hash  │   │
│  │ • Recovery      │ │ • Container     │ │ • TTL Mgmt      │   │
│  └─────────────────┘ └─────────────────┘ └─────────────────┘   │
│                                                                 │
│  ┌─────────────────┐                                            │
│  │  File Service   │  참조: architecture/file-storage.md        │
│  │                 │                                            │
│  │ • Upload/Delete │                                            │
│  │ • Metadata Mgmt │                                            │
│  │ • Persistence   │                                            │
│  └─────────────────┘                                            │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│                 Infrastructure Layer                            │
│  ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐   │
│  │    Backends     │ │      Cache      │ │     Common      │   │
│  │                 │ │                 │ │                 │   │
│  │ • OpenAI        │ │ • LRU Cache     │ │ • HTTP Client   │   │
│  │ • Anthropic     │ │ • TTL Cache     │ │ • Executor      │   │
│  │ • Gemini        │ │ • Retry Cache   │ │ • Statistics    │   │
│  │ • vLLM          │ │                 │ │ • URL Validator │   │
│  └─────────────────┘ └─────────────────┘ └─────────────────┘   │
│                                                                 │
│  ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐   │
│  │  Configuration  │ │  Backend Pool   │ │  Backend Factory  │ │
│  │                 │ │                 │ │                   │ │
│  │ • File Watcher  │ │ • Pool Mgmt     │ │ • Create Backend  │ │
│  │ • Env Override  │ │ • Connection    │ │ • Type Detection  │ │
│  │ • Validation    │ │ • Pre-warming   │ │ • Config Parsing  │ │
│  └─────────────────┘ └─────────────────┘ └─────────────────┘   │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│                       Core Layer                                │
│  ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐   │
│  │     Models      │ │     Traits      │ │     Errors      │   │
│  │                 │ │                 │ │                 │   │
│  │ • Backend       │ │ • BackendTrait  │ │ • CoreError     │   │
│  │ • Model         │ │ • ServiceTrait  │ │ • RouterError   │   │
│  │ • Request       │ │ • CacheTrait    │ │ • ErrorSeverity │   │
│  │ • Response      │ │ • HealthTrait   │ │ • ErrorDetail   │   │
│  └─────────────────┘ └─────────────────┘ └─────────────────┘   │
│                                                                 │
│  ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐   │
│  │  Retry Logic    │ │  Configuration  │ │   Container     │   │
│  │                 │ │                 │ │                 │   │
│  │ • Policies      │ │ • Models        │ │ • DI Container  │   │
│  │ • Strategies    │ │ • Validation    │ │ • Service Mgmt  │   │
│  │ • Backoff       │ │ • Defaults      │ │ • Lifecycle     │   │
│  └─────────────────┘ └─────────────────┘ └─────────────────┘   │
│                                                                 │
│  ┌─────────────────┐                                           │
│  │ Circuit Breaker │                                           │
│  │                 │                                           │
│  │ • State Machine │                                           │
│  │ • Failure Track │                                           │
│  │ • Auto Recovery │                                           │
│  └─────────────────┘                                           │
└─────────────────────────────────────────────────────────────────┘

계층 설명

1. HTTP Layer (src/http/)

책임: HTTP 요청, 응답 및 웹 관련 관심사 처리

구성 요소

  • Routes (routes.rs): HTTP 엔드포인트 및 라우트 처리 정의
  • Middleware (middleware/): 횡단 관심사 (인증, 로깅, 메트릭, 속도 제한)
  • DTOs (dto/): HTTP 직렬화/역직렬화를 위한 데이터 전송 객체
  • Streaming (streaming/): Server-Sent Events (SSE) 처리

주요 파일

src/http/
├── mod.rs              # HTTP 계층 내보내기
├── routes.rs           # 라우트 정의 및 핸들러
├── dto.rs              # 요청/응답 DTO
├── handlers/           # 요청 핸들러
│   ├── mod.rs
│   └── responses.rs    # Responses API 핸들러
├── middleware/         # HTTP 미들웨어 컴포넌트
│   ├── mod.rs
│   ├── auth.rs         # API 키 인증 미들웨어
│   ├── admin_auth.rs   # Admin API 인증 미들웨어
│   ├── files_auth.rs   # Files API 인증 미들웨어
│   ├── admin_audit.rs  # Admin 작업 감사 로깅
│   ├── logging.rs      # 요청/응답 로깅
│   ├── metrics.rs      # 메트릭 수집
│   ├── metrics_auth.rs # 메트릭 엔드포인트 인증
│   ├── model_extractor.rs # 요청에서 모델 추출
│   ├── prometheus.rs   # Prometheus 메트릭 통합
│   ├── rate_limit.rs   # 속도 제한 미들웨어 (레거시)
│   └── rate_limit_v2/  # 향상된 속도 제한 (모듈화)
│       ├── mod.rs      # 모듈 내보내기
│       ├── middleware.rs # 속도 제한 미들웨어
│       ├── store.rs    # 속도 제한 저장소 및 추적
│       └── token_bucket.rs # 토큰 버킷 알고리즘
└── streaming/          # SSE 스트리밍 핸들러
    ├── mod.rs
    └── handler.rs      # 스트리밍 응답 처리

미들웨어 구성 요소

HTTP 계층에는 횡단 관심사를 제공하는 여러 미들웨어 구성 요소가 포함됩니다:

  • auth.rs: 메인 엔드포인트 (/v1/chat/completions, /v1/models 등)에 대한 API 키 인증

    • Authorization: Bearer <key> 헤더에서 API 키 검증
    • config.yaml에 구성된 다중 API 키 지원
    • 유효하지 않거나 누락된 키에 대해 401 Unauthorized 반환
  • admin_auth.rs: admin 엔드포인트 (/admin/*)에 대한 별도 인증

    • 사용자 API 키와 구분되는 전용 admin API 키 사용
    • 민감한 작업 보호 (설정 리로드, 서킷 브레이커 제어, 헬스 관리)
    • 설정에서 admin.api_keys를 통해 구성 가능
  • files_auth.rs: Files API (/v1/files/*)에 대한 인증 미들웨어

    • 파일 업로드/다운로드/삭제 작업에 특화된 API 키 검증
    • 무단 파일 접근 및 조작 방지
    • 권한 확인을 위해 파일 저장소 서비스와 통합
  • admin_audit.rs: admin 작업에 대한 감사 로깅 미들웨어

    • 타임스탬프 및 호출자 식별과 함께 모든 admin API 호출 기록
    • 민감한 작업의 매개변수와 결과 로깅
    • 규정 준수 및 보안 모니터링을 위한 감사 추적 제공
    • 설정 가능한 로그 레벨 및 보존 정책
  • ratelimitv2/: 향상된 속도 제한 시스템 (속도 제한 섹션 참조)

    • 클라이언트별 추적이 있는 토큰 버킷 알고리즘
    • 지속적인 속도 및 버스트 보호를 위한 별도 제한
    • 만료된 클라이언트 항목의 자동 정리
    • 모니터링을 위한 상세 메트릭

2. Services Layer (src/services/)

책임: 비즈니스 로직 조율 및 인프라 구성 요소 간 조정

구성 요소

  • Backend Service (backend_service.rs): 백엔드 풀 관리, 로드 밸런싱, 헬스 체크
  • Model Service (model_service.rs): 백엔드에서 모델 집계, 캐싱 처리, 메타데이터로 보강
  • Proxy Service (proxy_service.rs): 요청 라우팅, 재시도 처리, 스트리밍 관리
  • Health Service (health_service.rs): 서비스 헬스 모니터링, 상태 추적
  • Service Registry (mod.rs): 서비스 라이프사이클 및 의존성 관리

주요 파일

src/services/
├── mod.rs              # 서비스 레지스트리 및 관리
├── backend_service.rs  # 백엔드 관리 서비스
├── model_service.rs    # 모델 집계 서비스
├── proxy_service.rs    # 요청 프록시 및 라우팅
├── health_service.rs   # 헬스 모니터링 서비스
├── deduplication.rs    # 요청 중복 제거 서비스
├── responses/          # Responses API 지원
│   ├── mod.rs
│   ├── converter.rs    # 응답 형식 변환
│   ├── session.rs      # 세션 관리
│   └── streaming.rs    # 스트리밍 응답 처리
└── streaming/          # 스트리밍 유틸리티
    ├── mod.rs
    ├── parser.rs       # 스트림 파싱 로직
    └── transformer.rs  # 스트림 변환 (OpenAI/Anthropic)

3. Infrastructure Layer (src/infrastructure/)

책임: 외부 시스템 및 기술적 기능의 구체적인 구현 제공

구성 요소

  • Backends (backends/): 특정 백엔드 구현 (OpenAI, vLLM, Ollama)
  • Cache (cache/): 캐싱 구현 (LRU, TTL 기반)
  • Configuration (config/): 설정 로딩, 감시, 검증
  • HTTP Client (http_client.rs): HTTP 클라이언트 관리 및 최적화

주요 파일

src/infrastructure/
├── mod.rs              # 인프라 내보내기 및 유틸리티
├── backends/           # 백엔드 구현
│   ├── mod.rs
│   ├── anthropic/      # 네이티브 Anthropic Claude 백엔드
│   │   ├── mod.rs      # 백엔드 구현 및 요청 변환
│   │   └── stream.rs   # SSE 스트림 변환기 (Anthropic → OpenAI)
│   ├── gemini/         # 네이티브 Google Gemini 백엔드
│   │   ├── mod.rs      # TTFB 최적화가 있는 백엔드 구현
│   │   └── stream.rs   # SSE 스트림 변환기 (Gemini → OpenAI)
│   ├── openai/         # OpenAI 호환 백엔드
│   │   ├── mod.rs
│   │   ├── backend.rs  # OpenAI 백엔드 구현
│   │   └── models/     # OpenAI 특화 모델 정의
│   ├── factory/        # 백엔드 팩토리 패턴
│   │   ├── mod.rs
│   │   └── backend_factory.rs  # 설정에서 백엔드 생성
│   ├── pool/           # 백엔드 풀링 및 관리
│   │   ├── mod.rs
│   │   ├── backend_pool.rs     # 연결 풀 관리
│   │   └── backend_manager.rs  # 백엔드 라이프사이클 관리
│   ├── generic/        # 제네릭 백엔드 구현
│   │   └── mod.rs
│   └── vllm.rs         # vLLM 백엔드 구현
├── common/             # 공유 인프라 유틸리티
│   ├── mod.rs
│   ├── executor.rs     # 재시도/메트릭이 있는 요청 실행
│   ├── headers.rs      # HTTP 헤더 유틸리티
│   ├── http_client.rs  # 풀링이 있는 HTTP 클라이언트 팩토리
│   ├── statistics.rs   # 백엔드 통계 수집
│   └── url_validator.rs # URL 검증 및 보안
├── cache/              # 캐싱 구현
│   ├── mod.rs
│   ├── lru_cache.rs    # LRU 캐시 구현
│   └── retry_cache.rs  # 재시도 인식 캐시
├── config/             # 설정 관리
│   ├── mod.rs
│   ├── loader.rs       # 설정 로딩
│   ├── validator.rs    # 설정 검증
│   ├── timeout_validator.rs # 타임아웃 설정 검증
│   ├── watcher.rs      # 핫 리로드를 위한 파일 감시
│   ├── migrator.rs     # 설정 마이그레이션 오케스트레이터
│   ├── migration.rs    # 마이그레이션 타입 및 트레이트
│   ├── migrations.rs   # 특정 마이그레이션 구현
│   ├── fixer.rs        # 자동 수정 로직
│   ├── backup.rs       # 백업 관리
│   └── secrets.rs      # 시크릿/API 키 관리
└── lock_optimization.rs # 락 및 동시성 최적화

4. Core Layer (src/core/)

책임: 도메인 모델, 비즈니스 규칙, 기본 추상화 정의

구성 요소

  • Models (models/): 핵심 도메인 엔티티 (Backend, Model, Request, Response)
  • Traits (traits.rs): 핵심 인터페이스 및 계약
  • Errors (errors.rs): 도메인 특화 오류 타입 및 처리
  • Retry (retry/): 재시도 정책 및 전략
  • Container (container.rs): 의존성 주입 컨테이너

주요 파일

src/core/
├── mod.rs              # 코어 내보내기 및 유틸리티
├── models/             # 도메인 모델
│   ├── mod.rs
│   ├── backend.rs      # 백엔드 도메인 모델
│   ├── model.rs        # LLM 모델 표현
│   ├── request.rs      # 요청 모델
│   └── responses.rs    # 응답 모델 (Responses API)
├── traits.rs           # 핵심 트레이트 및 인터페이스
├── errors.rs           # 오류 타입 및 처리
├── container.rs        # 의존성 주입 컨테이너
├── async_utils.rs      # 비동기 유틸리티 함수
├── duration_utils.rs   # Duration 파싱 유틸리티
├── streaming/          # 스트리밍 모델
│   ├── mod.rs
│   └── models.rs       # 스트리밍 특화 모델
├── retry/              # 재시도 메커니즘
│   ├── mod.rs
│   ├── policy.rs       # 재시도 정책
│   └── strategy.rs     # 재시도 전략
├── circuit_breaker/    # 서킷 브레이커 패턴
│   ├── mod.rs          # 모듈 내보내기
│   ├── config.rs       # 설정 모델
│   ├── state.rs        # 상태 머신 및 브레이커 로직
│   ├── error.rs        # 서킷 브레이커 오류
│   ├── metrics.rs      # Prometheus 메트릭
│   └── tests.rs        # 단위 테스트
├── files/              # 파일 처리 유틸리티
│   ├── mod.rs          # 모듈 내보내기
│   ├── resolver.rs     # 채팅 요청에서 파일 참조 해석
│   ├── transformer.rs  # 파일 콘텐츠가 있는 메시지 변환
│   └── transformer_utils.rs # 변환 유틸리티 함수
└── config/             # 설정 모델
    ├── mod.rs
    ├── models/            # 설정 데이터 모델 (모듈화 구조)
    │   ├── mod.rs         # 하위 호환성을 위한 재내보내기
    │   ├── config.rs      # 메인 Config 구조체, ServerConfig, BackendConfig
    │   ├── backend_type.rs # BackendType 열거형 정의
    │   ├── model_metadata.rs # ModelMetadata, PricingInfo, CapabilityInfo
    │   ├── global_prompts.rs # GlobalPrompts 설정
    │   ├── samples.rs     # 샘플 생성 설정
    │   ├── validation.rs  # 설정 검증 로직
    │   └── error.rs       # 설정 특화 오류
    ├── timeout_models.rs # 타임아웃 설정 모델
    ├── cached_timeout.rs # 캐시된 타임아웃 해석
    ├── optimized_retry.rs # 최적화된 재시도 설정
    ├── metrics.rs      # 메트릭 설정
    └── rate_limit.rs   # 속도 제한 설정

핵심 컴포넌트

백엔드 풀

위치: src/backend.rs (레거시) → src/services/backend_service.rs

목적: 지능형 로드 밸런싱으로 다중 LLM 백엔드 관리

pub struct BackendPool {
    backends: Arc<RwLock<Vec<Backend>>>,
    load_balancer: LoadBalancingStrategy,
    health_checker: Option<Arc<HealthChecker>>,
}

impl BackendPool {
    // 헬스 인식 라운드 로빈 로드 밸런싱
    pub async fn select_backend(&self) -> Option<Backend> { /* ... */ }

    // 모델 가용성으로 백엔드 필터링
    pub async fn backends_for_model(&self, model: &str) -> Vec<Backend> { /* ... */ }
}

헬스 체커

위치: src/health.rssrc/services/health_service.rs

목적: 설정 가능한 임계값과 자동 복구로 백엔드 헬스 모니터링

pub struct HealthChecker {
    backends: Arc<RwLock<Vec<Backend>>>,
    config: HealthConfig,
    status_map: Arc<RwLock<HashMap<String, HealthStatus>>>,
}

pub struct HealthConfig {
    pub interval: Duration,
    pub timeout: Duration,
    pub unhealthy_threshold: u32,  // 비정상으로 표시하기 전 실패 횟수
    pub healthy_threshold: u32,    // 정상으로 표시하기 전 성공 횟수
}

모델 집계 서비스

위치: src/models/ (모듈화 구조)

목적: 모든 백엔드에서 모델 정보를 집계하고 캐시하며 메타데이터로 보강

모듈 구조 (단일 models.rs 파일에서 리팩토링됨):

src/models/
├── mod.rs             # 하위 호환성을 위한 재내보내기
├── types.rs           # Model, AggregatedModel, ModelList 타입
├── metrics.rs         # ModelMetrics 추적
├── cache.rs           # ModelCache 구현
├── config.rs          # ModelAggregationConfig
├── fetcher.rs         # 백엔드에서 모델 가져오기
├── handlers.rs        # /v1/models 엔드포인트용 HTTP 핸들러
├── utils.rs           # 유틸리티 함수 (normalize_model_id 등)
└── aggregation/       # 핵심 집계 로직
    ├── mod.rs         # ModelAggregationService 구현
    └── tests.rs       # 단위 테스트

pub struct ModelAggregationService {
    cache: Arc<RwLock<ModelCache>>,
    config: ModelAggregationConfig,
    backends: Arc<BackendPool>,
}

impl ModelAggregationService {
    // 모든 정상 백엔드에서 모델 집계
    pub async fn get_aggregated_models(&self) -> Result<ModelList, Error> { /* ... */ }

    // 설정의 메타데이터로 모델 보강
    pub fn merge_config_metadata(&self, models: &mut Vec<Model>) { /* ... */ }

    // TTL 및 중복 제거가 있는 캐시
    pub async fn refresh_models(&self) -> Result<(), Error> { /* ... */ }
}

프록시 모듈

위치: src/proxy/ (모듈화 구조)

목적: 요청 프록시, 백엔드 선택, 파일 해석, 이미지 생성/편집 처리

모듈 구조 (단일 proxy.rs 파일에서 리팩토링됨):

src/proxy/
├── mod.rs             # 하위 호환성을 위한 재내보내기
├── backend.rs         # 백엔드 선택 및 라우팅 로직
├── request.rs         # 재시도 로직이 있는 요청 실행
├── files.rs           # 요청에서 파일 참조 해석
├── image_gen.rs       # 이미지 생성 처리 (DALL-E, Gemini, GPT Image)
├── image_edit.rs      # 이미지 편집 지원 (/v1/images/edits)
├── image_utils.rs     # 이미지 처리 유틸리티 (멀티파트, 검증)
├── handlers.rs        # 프록시 엔드포인트용 HTTP 핸들러
├── utils.rs           # 유틸리티 함수 (오류 응답 등)
└── tests.rs           # 단위 테스트

주요 책임

  • 백엔드 선택: 사용 가능한 백엔드로 지능형 라우팅
  • 파일 해석: 채팅 요청에서 파일 참조 해석
  • 이미지 생성: OpenAI (DALL-E, GPT Image) 및 Gemini (Nano Banana) 이미지 모델 지원
  • 이미지 편집: 이미지 편집 및 변형 엔드포인트
  • 요청 재시도: 지수 백오프가 있는 자동 재시도
  • 오류 처리: OpenAI 형식의 표준화된 오류 응답

재시도 핸들러

위치: src/services/deduplication.rs

목적: 지터가 있는 지수 백오프 및 요청 중복 제거 구현

pub struct EnhancedRetryHandler {
    config: RetryConfig,
    dedup_cache: Arc<Mutex<HashMap<String, CachedResponse>>>,
    dedup_ttl: Duration,
}

pub struct RetryConfig {
    pub max_attempts: u32,
    pub base_delay: Duration,
    pub max_delay: Duration,
    pub exponential_backoff: bool,
    pub jitter: bool,
}

서킷 브레이커

위치: src/core/circuit_breaker/

목적: 실패하는 백엔드에 대한 요청을 자동으로 중지하여 연쇄 장애 방지

pub struct CircuitBreaker {
    states: Arc<DashMap<String, BackendCircuitState>>,
    config: CircuitBreakerConfig,
    metrics: Option<CircuitBreakerMetrics>,
}

pub struct CircuitBreakerConfig {
    pub enabled: bool,
    pub failure_threshold: u32,           // 열리기 전 실패 횟수 (기본값: 5)
    pub failure_rate_threshold: f64,      // 실패율 임계값 (기본값: 0.5)
    pub minimum_requests: u32,            // 비율 계산 전 최소 요청 수
    pub timeout_seconds: u64,             // 서킷이 열린 상태로 유지되는 시간 (기본값: 60초)
    pub half_open_max_requests: u32,      // half-open 상태에서 최대 요청 수
    pub half_open_success_threshold: u32, // 닫히는 데 필요한 성공 횟수
}

pub enum CircuitState {
    Closed,    // 정상 작동 - 요청 통과
    Open,      // 빠른 실패 - 요청 즉시 거부
    HalfOpen,  // 복구 테스트 - 제한된 요청 허용
}

주요 기능

  • 독립적인 상태를 가진 백엔드별 서킷 브레이커
  • 핫 패스에서 락 프리 상태 확인을 위한 원자적 연산
  • 성공/실패 패턴에 기반한 자동 상태 전환
  • 실패율 계산을 위한 슬라이딩 윈도우
  • 관찰 가능성을 위한 Prometheus 메트릭
  • 수동 제어를 위한 admin 엔드포인트

컨테이너 (의존성 주입)

위치: src/core/container.rs

목적: 서비스 라이프사이클 및 의존성 관리

pub struct Container {
    services: Arc<RwLock<HashMap<TypeId, Box<dyn Any + Send + Sync>>>>,
    singletons: Arc<RwLock<HashMap<TypeId, Arc<dyn Any + Send + Sync>>>>,
}

impl Container {
    // 싱글톤 서비스 등록
    pub async fn register_singleton<T>(&self, instance: Arc<T>) -> CoreResult<()>
    where T: 'static + Send + Sync { /* ... */ }

    // 서비스 의존성 해석
    pub async fn resolve<T>(&self) -> CoreResult<Arc<T>>
    where T: 'static + Send + Sync { /* ... */ }
}

데이터 흐름

요청 처리 흐름

sequenceDiagram
    participant Client
    participant HTTPLayer as HTTP Layer
    participant ProxyService as Proxy Service
    participant BackendService as Backend Service
    participant ModelService as Model Service
    participant Backend as LLM Backend

    Client->>HTTPLayer: POST /v1/chat/completions
    HTTPLayer->>HTTPLayer: 미들웨어 적용 (인증, 로깅, 메트릭)
    HTTPLayer->>ProxyService: 요청 전달

    ProxyService->>ModelService: 모델 정보 가져오기
    ModelService->>ModelService: 캐시 확인
    alt 캐시 미스
        ModelService->>BackendService: 모델용 백엔드 가져오기
        BackendService->>Backend: 모델 쿼리
        Backend-->>BackendService: 모델 목록
        BackendService-->>ModelService: 필터링된 백엔드
        ModelService->>ModelService: 캐시 업데이트
    end
    ModelService-->>ProxyService: 백엔드에서 모델 사용 가능

    ProxyService->>BackendService: 정상 백엔드 선택
    BackendService->>BackendService: 로드 밸런싱 적용
    BackendService-->>ProxyService: 선택된 백엔드

    ProxyService->>Backend: 요청 전달
    Backend-->>ProxyService: 응답 (스트리밍 또는 비스트리밍)

    ProxyService->>ProxyService: 응답 처리 적용
    ProxyService-->>HTTPLayer: 처리된 응답
    HTTPLayer-->>Client: HTTP 응답

헬스 체크 흐름

sequenceDiagram
    participant HealthService as Health Service
    participant BackendPool as Backend Pool
    participant Backend as LLM Backend
    participant Cache as Health Cache

    loop 매 간격마다
        HealthService->>BackendPool: 모든 백엔드 가져오기
        BackendPool-->>HealthService: 백엔드 목록

        par 각 백엔드에 대해
            HealthService->>Backend: GET /v1/models (또는 /health)
            alt 성공
                Backend-->>HealthService: 200 OK + 모델 목록
                HealthService->>Cache: 업데이트: consecutive_successes++
                HealthService->>HealthService: 임계값 충족 시 정상으로 표시
            else 실패
                Backend-->>HealthService: 오류/타임아웃
                HealthService->>Cache: 업데이트: consecutive_failures++
                HealthService->>HealthService: 임계값 충족 시 비정상으로 표시
            end
        end

        HealthService->>BackendPool: 백엔드 헬스 상태 업데이트
    end

핫 리로드 서비스

위치: src/infrastructure/config/hot_reload.rs, src/services/hot_reload_service.rs

목적: 서버 재시작 없이 런타임 설정 업데이트 제공

핫 리로드 시스템은 자동 파일 감시와 지능형 컴포넌트 업데이트를 통해 무중단 설정 변경을 가능하게 합니다.

주요 아키텍처 컴포넌트

  • ConfigManager: notify 크레이트를 사용한 파일 시스템 감시, tokio::sync::watch 채널을 통해 업데이트 게시
  • HotReloadService: 설정 차이점 계산, 변경 사항 분류 (즉시/점진적/재시작)
  • 컴포넌트 업데이트: HealthChecker, CircuitBreaker, RateLimitStore에 대한 원자적 업데이트를 위한 내부 가변성 패턴 (RwLock)

변경 분류

  • 즉시 업데이트: logging.level, rate_limiting., circuit_breaker., retry., global_prompts.
  • 점진적 업데이트: backends., health_checks., timeouts.*
  • 재시작 필요: server.bind_address, server.workers

Admin API: 핫 리로드 기능 검사를 위한 /admin/config/hot-reload-status

핫 리로드 설정, 프로세스 흐름, 사용 예제에 대한 자세한 내용은 configuration.md 핫 리로드 섹션을 참조하세요.

설정 마이그레이션 시스템

위치: src/infrastructure/config/{migrator,migration,migrations,fixer,backup}.rs

목적: 설정 문제를 자동으로 감지 및 수정하고, 스키마를 마이그레이션하며, 설정 유효성을 보장

설정 마이그레이션 시스템은 설정 진화 및 유지보수를 위한 포괄적인 솔루션을 제공합니다. 자동으로: - 오래된 스키마 버전 감지 및 마이그레이션 - YAML/TOML 파일의 일반적인 구문 오류 수정 - 설정 값 검증 및 수정 - 변경 전 백업 생성 - 변경 사항 미리보기를 위한 dry-run 기능 제공

아키텍처 컴포넌트

1. 마이그레이션 오케스트레이터 (migrator.rs) - 마이그레이션 작업의 메인 진입점 - 전체 마이그레이션 워크플로우 조율 - 백업 생성 및 복원 관리 - 보안 검증 구현 (경로 탐색, 파일 크기 제한)

2. 마이그레이션 프레임워크 (migration.rs) - 마이그레이션을 위한 핵심 타입 및 트레이트 정의 - 버전 업그레이드 구현을 위한 Migration 트레이트 - 문제 분류를 위한 ConfigIssue 열거형 - 변경 사항 추적을 위한 MigrationResult

3. 스키마 마이그레이션 (migrations.rs) - 구체적인 마이그레이션 구현 (예: V1ToV2Migration) - 버전 간 설정 구조 변환 - 예시: backend_urlbackends 배열로 변환

4. 자동 수정 엔진 (fixer.rs) - 일반적인 설정 오류 감지 및 수정 - Duration 형식 수정 (예: "10 seconds" → "10s") - URL 검증 및 프로토콜 추가 - 필드 비권장화 처리

5. 백업 관리자 (backup.rs) - 수정 전 타임스탬프가 있는 백업 생성 - 리소스 제한 구현 (파일당 10MB, 총 100MB, 최대 50개 백업) - 오래된 백업의 자동 정리 - 파일 권한 보존

마이그레이션 워크플로우

graph TD
    A[설정 파일 읽기] --> B[경로 및 크기 검증]
    B --> C[백업 생성]
    C --> D[설정 파싱]
    D --> E{파싱 성공?}
    E -->|아니오| F[구문 오류 수정]
    F --> D
    E -->|예| G[스키마 버전 감지]
    G --> H{마이그레이션 필요?}
    H -->|예| I[마이그레이션 적용]
    H -->|아니오| J[값 검증]
    I --> J
    J --> K{문제 발견?}
    K -->|예| L[자동 수정 적용]
    K -->|아니오| M[설정 반환]
    L --> N[업데이트된 설정 쓰기]
    N --> M

보안 기능

  1. 경로 탐색 보호: 디렉토리 탐색 공격 방지를 위한 경로 검증
  2. 파일 크기 제한: DoS 방지를 위한 최대 10MB 설정 파일
  3. 형식 검증: .yaml, .yml, .toml 파일만 처리
  4. 시스템 디렉토리 보호: 민감한 시스템 경로 접근 차단
  5. 테스트 모드 완화: 테스트 친화적 검증을 위한 조건부 컴파일 사용

예시 마이그레이션: v1.0에서 v2.0으로

// V1ToV2Migration 구현
fn migrate(&self, config: &mut Value) -> Result<(), MigrationError> {
    // 단일 backend_url을 backends 배열로 변환
    if let Some(backend_url) = config.get("backend_url") {
        let mut backends = Vec::new();
        let mut backend = Map::new();
        backend.insert("url".to_string(), backend_url.clone());

        // 모델을 백엔드로 이동
        if let Some(model) = config.get("model") {
            backend.insert("models".to_string(),
                Value::Sequence(vec![model.clone()]));
        }

        backends.push(Value::Mapping(backend));
        config["backends"] = Value::Sequence(backends);

        // 이전 필드 제거
        config.remove("backend_url");
        config.remove("model");
    }
    Ok(())
}

설정 로딩 흐름

graph TD
    A[애플리케이션 시작] --> B[Config Manager 초기화]
    B --> C{설정 파일 지정됨?}
    C -->|예| D[지정된 파일 로드]
    C -->|아니오| E[표준 위치 검색]

    E --> F{설정 파일 발견?}
    F -->|예| G[설정 파일 로드]
    F -->|아니오| H[CLI 인자 + 환경 변수 + 기본값 사용]

    D --> I[YAML 파싱]
    G --> I
    H --> J[인자에서 설정 생성]

    I --> K[환경 변수 오버라이드 적용]
    J --> K

    K --> L[CLI 인자 오버라이드 적용]
    L --> M[설정 검증]
    M --> N{유효함?}
    N -->|예| O[설정 반환]
    N -->|아니오| P[오류와 함께 종료]

    O --> Q[핫 리로드를 위한 파일 감시 시작]
    Q --> R[애플리케이션 실행 중]

    Q --> S[설정 파일 변경됨]
    S --> T[리로드 및 검증]
    T --> U{유효함?}
    U -->|예| V[새 설정 적용]
    U -->|아니오| W[오류 로깅, 이전 설정 유지]

    V --> R
    W --> R

의존성 주입

서비스 등록

서비스는 애플리케이션 시작 시 컨테이너에 등록됩니다:

// main.rs에서
async fn setup_services(config: Config) -> Result<ServiceRegistry, Error> {
    let container = Arc::new(Container::new());

    // 인프라 서비스 등록
    container.register_singleton(Arc::new(
        HttpClient::new(&config.http_client)?
    )).await?;

    container.register_singleton(Arc::new(
        BackendManager::new(&config.backends)?
    )).await?;

    // 핵심 서비스 등록
    container.register_singleton(Arc::new(
        BackendServiceImpl::new(container.clone())
    )).await?;

    container.register_singleton(Arc::new(
        ModelServiceImpl::new(container.clone())
    )).await?;

    // 서비스 레지스트리 생성
    let registry = ServiceRegistry::new(container);
    registry.initialize().await?;

    Ok(registry)
}

서비스 의존성

서비스는 생성자 주입을 통해 의존성을 선언합니다:

pub struct ProxyServiceImpl {
    backend_service: Arc<dyn BackendService>,
    model_service: Arc<dyn ModelService>,
    retry_handler: Arc<dyn RetryHandler>,
    http_client: Arc<HttpClient>,
}

impl ProxyServiceImpl {
    pub fn new(container: Arc<Container>) -> CoreResult<Self> {
        Ok(Self {
            backend_service: container.resolve()?,
            model_service: container.resolve()?,
            retry_handler: container.resolve()?,
            http_client: container.resolve()?,
        })
    }
}

이점

  • 테스트 가능성: 서비스를 단위 테스트용으로 모킹할 수 있음
  • 유연성: 코드 변경 없이 구현을 교체할 수 있음
  • 라이프사이클 관리: 컨테이너가 서비스 초기화 및 정리 관리
  • 순환 의존성 감지: 컨테이너가 순환 의존성 방지

오류 처리 전략

라우터는 타입화된 오류, 지능형 복구, 사용자 친화적 응답을 갖춘 포괄적인 오류 처리 전략을 구현합니다.

오류 타입 계층

  • CoreError: 도메인 레벨 오류 (검증, 서비스 실패, 타임아웃, 설정)
  • RouterError: Core, HTTP, Backend, Model 오류를 결합하는 애플리케이션 레벨 오류
  • HttpError: HTTP 특화 오류 (400 BadRequest, 401 Unauthorized, 404 NotFound, 500 InternalServerError 등)

오류 처리 원칙

  1. 빠른 실패: 명확한 오류 메시지와 함께 입력을 조기에 검증
  2. 오류 컨텍스트: 관련 컨텍스트 포함 (필드 이름, 작업 세부 정보)
  3. 재시도 가능 분류: 재시도 가능(타임아웃, 503)과 불가능(400, 401) 오류 구분
  4. 사용자 친화적 응답: 내부 오류를 OpenAI 호환 오류 형식으로 변환
  5. 구조화된 로깅: 적절한 심각도와 컨텍스트로 오류 로깅

오류 복구 메커니즘

  • 서킷 브레이커: 연쇄 장애 방지 (서킷 브레이커 참조)
  • 지수 백오프가 있는 재시도: 일시적 장애 자동 재시도
  • 모델 폴백: 기본 모델을 사용할 수 없을 때 대체 모델로 라우팅 (모델 폴백 시스템 참조)
  • 우아한 성능 저하: 컴포넌트 실패 시 감소된 기능으로 계속 작동

오류 처리, 복구 전략, 모니터링, 문제 해결에 대한 자세한 내용은 error-handling.md를 참조하세요.

확장 지점

백엔드 타입 아키텍처

라우터는 다른 API 형식을 가진 여러 백엔드 타입을 지원합니다. 각 백엔드 타입은 요청/응답 변환을 자동으로 처리합니다.

지원되는 백엔드 타입

백엔드 타입 API 형식 인증 사용 사례
openai OpenAI Chat Completions Authorization: Bearer OpenAI, Azure OpenAI, vLLM, LocalAI
anthropic Anthropic Messages API x-api-key 헤더 네이티브 API를 통한 Claude 모델
gemini OpenAI 호환 Authorization: Bearer OpenAI 호환 레이어를 통한 Google Gemini

Anthropic 백엔드 아키텍처

Anthropic 백엔드는 자동 형식 변환과 함께 Claude 모델에 대한 네이티브 지원을 제공합니다:

┌─────────────────────────────────────────────────────────────────┐
│                   OpenAI 형식 요청                                │
│  POST /v1/chat/completions                                       │
│  { "model": "claude-haiku-4-5", "messages": [...] }             │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│              요청 변환 레이어                                     │
│  transform_openai_to_anthropic_request()                        │
│  • 시스템 메시지 추출 → 별도의 `system` 매개변수                   │
│  • image_url 변환 → Anthropic 이미지 형식                        │
│  • max_tokens / max_completion_tokens 매핑                       │
│  • reasoning_effort 변환 → thinking 매개변수                     │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│                  Anthropic Messages API                          │
│  POST https://api.anthropic.com/v1/messages                     │
│  Headers: x-api-key, anthropic-version: 2023-06-01              │
│  { "model": "...", "system": "...", "messages": [...] }         │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│              AnthropicStreamTransformer                          │
│  SSE 이벤트 변환 (Anthropic → OpenAI 형식)                        │
│  • message_start → role이 있는 초기 청크                          │
│  • content_block_delta → 콘텐츠 청크                              │
│  • thinking_delta → reasoning_content (확장된 사고)               │
│  • message_delta → finish_reason 매핑                            │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│                   OpenAI 형식 응답                                │
│  data: {"choices":[{"delta":{"content":"..."}}]}                │
└─────────────────────────────────────────────────────────────────┘

주요 변환

요청 형식 차이:

측면 OpenAI 형식 Anthropic 형식
시스템 프롬프트 messages[0].role="system" 별도의 system 매개변수
인증 헤더 Authorization: Bearer x-api-key
최대 토큰 선택적 필수 (max_tokens)
이미지 image_url.url source.type + source.data

확장된 사고 지원:

// OpenAI reasoning_effort → Anthropic thinking
{
  "reasoning_effort": "high"  // OpenAI 형식
}
// 다음으로 변환:
{
  "thinking": {
    "type": "enabled",
    "budget_tokens": 32768   // effort 레벨에서 매핑
  }
}

새 백엔드 타입 추가하기

  1. 백엔드 트레이트 구현:

    // src/infrastructure/backends/custom_backend.rs에서
    pub struct CustomBackend {
        client: Arc<HttpClient>,
        config: CustomBackendConfig,
    }
    
    #[async_trait]
    impl BackendTrait for CustomBackend {
        async fn health_check(&self) -> CoreResult<()> { /* ... */ }
        async fn list_models(&self) -> CoreResult<Vec<Model>> { /* ... */ }
        async fn chat_completion(&self, request: ChatRequest) -> CoreResult<Response> { /* ... */ }
    }
    

  2. 백엔드 팩토리에 등록:

    // src/infrastructure/backends/mod.rs에서
    pub fn create_backend(backend_type: &str, config: &BackendConfig) -> CoreResult<Box<dyn BackendTrait>> {
        match backend_type {
            "openai" => Ok(Box::new(OpenAIBackend::new(config)?)),
            "vllm" => Ok(Box::new(VLLMBackend::new(config)?)),
            "custom" => Ok(Box::new(CustomBackend::new(config)?)), // 새 백엔드
            _ => Err(CoreError::ValidationFailed {
                message: format!("알 수 없는 백엔드 타입: {}", backend_type),
                field: Some("backend_type".to_string()),
            }),
        }
    }
    

새 미들웨어 추가하기

  1. 미들웨어 트레이트 구현:

    // src/http/middleware/custom_middleware.rs에서
    pub struct CustomMiddleware {
        config: CustomConfig,
    }
    
    impl<S> tower::Layer<S> for CustomMiddleware {
        type Service = CustomMiddlewareService<S>;
    
        fn layer(&self, inner: S) -> Self::Service {
            CustomMiddlewareService { inner, config: self.config.clone() }
        }
    }
    

  2. HTTP 라우터에 등록:

    // src/main.rs에서
    let app = Router::new()
        .route("/v1/models", get(list_models))
        .layer(CustomMiddleware::new(config.custom))
        .layer(LoggingMiddleware::new())
        .with_state(state);
    

새 캐시 타입 추가하기

  1. 캐시 트레이트 구현:

    // src/infrastructure/cache/redis_cache.rs에서
    pub struct RedisCache {
        client: redis::Client,
        ttl: Duration,
    }
    
    #[async_trait]
    impl CacheTrait for RedisCache {
        async fn get<T>(&self, key: &str) -> CoreResult<Option<T>>
        where T: DeserializeOwned { /* ... */ }
    
        async fn set<T>(&self, key: &str, value: &T, ttl: Option<Duration>) -> CoreResult<()>
        where T: Serialize { /* ... */ }
    }
    

  2. 서비스에서 사용:

    // 서비스는 어떤 캐시 구현도 사용 가능
    pub struct ModelServiceImpl<C: CacheTrait> {
        cache: Arc<C>,
        // ... 기타 의존성
    }
    

새 로드 밸런싱 전략 추가하기

// src/services/load_balancer.rs에서
pub enum LoadBalancingStrategy {
    RoundRobin,
    WeightedRoundRobin,
    LeastConnections,  // 새 전략
    Random,
}

impl LoadBalancingStrategy {
    pub fn select_backend(&self, backends: &[Backend]) -> Option<&Backend> {
        match self {
            Self::RoundRobin => /* ... */,
            Self::WeightedRoundRobin => /* ... */,
            Self::LeastConnections => self.select_least_connections(backends),
            Self::Random => /* ... */,
        }
    }
}

설계 결정

왜 4계층 아키텍처인가?

결정: 4계층 아키텍처 사용 (HTTP → Services → Infrastructure → Core)

이유

  • 명확한 분리: 각 계층이 구별되는 책임을 가짐
  • 테스트 가능성: 계층을 독립적으로 테스트 가능
  • 유지보수성: 한 계층의 변경이 다른 계층에 영향을 미치지 않음
  • 유연성: 구현을 쉽게 교체 가능 (예: 다른 캐시 백엔드)

트레이드오프

  • 장점: 깔끔하고, 유지보수 가능하고, 테스트 가능하고, 확장 가능
  • 단점: 더 많은 복잡성, 약간의 성능 오버헤드
  • 결론: 프로덕션 시스템에서는 이점이 비용을 초과함

왜 의존성 주입인가?

결정: 컴파일 타임 주입 대신 커스텀 DI 컨테이너 사용

이유

  • 런타임 유연성: 설정에 기반하여 구현을 교체 가능
  • 서비스 라이프사이클: 서비스 초기화/정리의 중앙 집중 관리
  • 테스트: 목 및 테스트 더블을 쉽게 주입 가능

고려된 대안

  • 수동 의존성 전달: 너무 장황하고 오류 발생 가능성 높음
  • 컴파일 타임 DI (제네릭): 유연성이 낮고 설정이 어려움

왜 Arc>를 공유 상태에 사용하는가?

결정: 공유 가변 상태에 Arc<RwLock<T>> 사용

이유

  • 읽기-쓰기 시맨틱스: 다중 리더, 배타적 쓰기
  • 성능: 읽기가 많은 워크로드에서 Arc<Mutex<T>>보다 좋음
  • 안전성: 컴파일 타임에 데이터 레이스 방지

고려된 대안

  • Arc<Mutex<T>>: 더 간단하지만 읽기 성능이 나쁨
  • 채널: 단순한 공유 상태에는 너무 복잡함
  • 원자 타입: 복잡한 데이터 구조에 적합하지 않음

왜 전체적으로 async/await인가?

결정: 모든 I/O 작업에 async/await 사용

이유

  • 성능: 논블로킹 I/O가 높은 동시성 허용
  • 리소스 효율성: 요청당 스레드보다 낮은 메모리 사용
  • 에코시스템: Rust 비동기 에코시스템 (Tokio, reqwest, axum)이 성숙함

트레이드오프

  • 장점: 높은 성능, 낮은 리소스 사용, 좋은 에코시스템
  • 단점: 복잡성, 학습 곡선, 디버깅 어려움
  • 결론: 고성능 네트워크 서비스에 필수적

왜 설정 핫 리로드인가?

결정: 파일 감시를 사용한 설정 핫 리로드 지원

이유

  • 제로 다운타임: 재시작 없이 설정 업데이트
  • 운영 친화적: 프로덕션에서 설정 조정이 쉬움
  • 개발: 개발 중 빠른 반복

구현

  • 파일 시스템 감시자가 변경 감지
  • 적용 전 새 설정 검증
  • 일관성 없는 상태를 피하기 위한 원자적 업데이트
  • 검증 오류 시 이전 설정으로 폴백

성능 고려 사항

메모리 관리

  1. 연결 풀링: HTTP 연결을 재사용하여 할당 오버헤드 감소
  2. 스마트 캐싱: LRU 제거로 무한 메모리 증가 방지
  3. Arc 복제: 딥 복제 대신 저렴한 참조 카운팅
  4. 스트리밍: 큰 응답을 메모리에 로드하지 않도록 청크 단위로 응답 처리

동시성

  1. 읽기 중심 워크로드를 위한 RwLock: 백엔드 풀과 모델 캐시에 대한 다중 동시 리더
  2. 가능한 경우 락 프리: 카운터와 간단한 상태에 원자 사용
  3. 비동기 태스크 스폰: 헬스 체크와 캐시 업데이트를 위한 백그라운드 태스크
  4. 바운드된 채널: 태스크의 무제한 큐잉 방지

I/O 최적화

  1. 연결 Keep-Alive: TCP 연결이 재사용을 위해 열린 상태 유지
  2. 스트리밍 응답: 버퍼링 없이 SSE 청크 전달
  3. 타임아웃: 느린 백엔드에서 행잉 방지
  4. 백오프가 있는 재시도: 실패하는 백엔드에 과부하 방지

메모리 레이아웃

// 캐시 효율성을 위해 최적화된 데이터 구조
pub struct Backend {
    pub name: String,          // 작은 이름을 위한 인라인 문자열
    pub url: Arc<str>,         // URL을 위한 공유 문자열
    pub weight: u32,           // 컴팩트한 정수
    pub is_healthy: AtomicBool, // 락 프리 헬스 상태
}

// 캐시 친화적 모델 저장소
pub struct ModelCache {
    models: HashMap<String, Arc<ModelInfo>>, // 공유된 모델 정보
    last_updated: AtomicU64,                 // 락 프리 타임스탬프
    ttl: Duration,
}

벤치마킹 결과

벤치마크 기준 (benches/performance_benchmarks.rs 참조):

  • 요청 지연 시간: 라우팅 결정에 < 5ms 오버헤드
  • 메모리 사용량: ~50MB 기본 메모리, 백엔드에 따라 선형 확장
  • 처리량: 보통 하드웨어에서 1000+ 요청/초
  • 연결 효율성: 최소 메모리 오버헤드로 백엔드당 100+ 동시 연결

속도 제한

라우터는 남용으로부터 보호하고 클라이언트 간 공정한 리소스 할당을 보장하기 위해 정교한 속도 제한을 구현합니다.

주요 기능: - 이중 윈도우 접근 방식: 지속 제한 (100 req/min) + 버스트 보호 (20 req/5s) - API 키 (선호) 또는 IP 주소 (폴백)로 클라이언트 식별 - 자동 캐시 정리가 있는 클라이언트별 격리 - 빈 응답에 대한 짧은 TTL로 DoS 방지

속도 제한 V2 아키텍처

향상된 속도 제한 시스템 (rate_limit_v2/)은 모듈화된 고성능 구현을 제공합니다:

모듈 구조

src/http/middleware/rate_limit_v2/
├── mod.rs              # 공개 API 및 모듈 내보내기
├── middleware.rs       # Axum 미들웨어 통합
├── store.rs            # 속도 제한 저장소 및 클라이언트 추적
└── token_bucket.rs     # 토큰 버킷 알고리즘 구현

구성 요소

  1. 토큰 버킷 알고리즘 (token_bucket.rs)
  2. 설정 가능한 버킷 용량 및 리필 속도
  3. 락 프리 토큰 소비를 위한 원자적 연산
  4. 경과 시간에 기반한 자동 토큰 보충
  5. 지속 및 버스트 제한을 위한 별도 버킷

  6. 속도 제한 저장소 (store.rs)

  7. 동시 접근을 위한 DashMap을 사용한 클라이언트별 상태 추적
  8. 만료된 클라이언트 항목의 자동 정리
  9. 비활성 클라이언트에 대한 설정 가능한 TTL (기본값: 1시간)
  10. 바운드된 저장소로 메모리 효율적

  11. 미들웨어 통합 (middleware.rs)

  12. 클라이언트 식별자 추출 (API 키 → IP 주소 폴백)
  13. 처리 전 지속 및 버스트 제한 모두 확인
  14. Retry-After 헤더와 함께 HTTP 429 (Too Many Requests) 반환
  15. 속도 제한 히트 모니터링을 위한 Prometheus 메트릭

설정 예시

rate_limiting:
  enabled: true
  sustained:
    max_requests: 100
    window_seconds: 60
  burst:
    max_requests: 20
    window_seconds: 5
  cleanup_interval_seconds: 300

결정 흐름

요청 도착
클라이언트 ID 추출 (API 키 또는 IP)
지속 제한 확인 (100 req/min)
    ↓ OK
버스트 제한 확인 (20 req/5s)
    ↓ OK
요청 처리

속도 제한 설정에 대한 자세한 내용은 configuration.md 속도 제한 섹션을 참조하세요.

모델 폴백 시스템

라우터는 기본 모델을 사용할 수 없을 때 자동으로 대체 모델로 요청을 라우팅하는 설정 가능한 모델 폴백 시스템을 구현합니다.

주요 기능: - 자동 폴백 체인 실행 (예: gpt-4o → gpt-4-turbo → gpt-3.5-turbo) - 매개변수 변환이 있는 크로스 프로바이더 폴백 지원 - 지능형 트리거링을 위한 서킷 브레이커 통합 - 폴백 사용 모니터링을 위한 Prometheus 메트릭

자세한 설정 및 구현은 error-handling.md 모델 폴백 섹션을 참조하세요.

서킷 브레이커

라우터는 연쇄 장애를 방지하고 백엔드가 비정상이 될 때 자동 장애 조치를 제공하기 위해 서킷 브레이커 패턴을 구현합니다.

3-상태 머신:

상태 동작
Closed 정상 작동. 실패 카운트됨.
Open 빠른 실패 모드. 요청 즉시 거부.
HalfOpen 복구 테스트. 제한된 요청 허용.

주요 기능: - 독립적인 상태를 가진 백엔드별 격리 - 핫 패스 오버헤드를 최소화하기 위한 락 프리 원자적 연산 - 수동 제어를 위한 admin 엔드포인트 (/admin/circuit/*) - 관찰 가능성을 위한 Prometheus 메트릭

자세한 설정 및 구현은 error-handling.md 서킷 브레이커 섹션을 참조하세요.

파일 저장소

라우터는 영구 메타데이터가 있는 OpenAI Files API 호환 파일 저장소를 제공합니다.

주요 기능: - 사이드카 JSON 파일이 있는 영구 메타데이터 저장소 - 서버 재시작 시 자동 복구 - 고아 파일 감지 및 정리 - 플러그 가능한 백엔드 (메모리/영구)

자세한 아키텍처 및 구현은 파일 저장소 가이드를 참조하세요.

이미지 생성 아키텍처

라우터는 자동 매개변수 변환과 함께 여러 백엔드 (OpenAI GPT Image, DALL-E, Google Gemini/Nano Banana)에 걸친 이미지 생성을 위한 통합 인터페이스를 제공합니다.

다중 백엔드 이미지 생성

┌─────────────────────────────────────────────────────────────────┐
│                   OpenAI 호환 요청                                │
│  POST /v1/images/generations                                     │
│  { "model": "...", "prompt": "...", "size": "1536x1024" }       │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│                    모델 라우터 (image_gen.rs)                     │
│  • 모델 타입 감지 (GPT Image, DALL-E, Nano Banana)                │
│  • 적절한 핸들러로 라우팅                                          │
│  • 스트리밍 vs 비스트리밍 처리                                     │
└─────────────────────────────────────────────────────────────────┘
                   │                              │
        ┌──────────┘                              └──────────┐
        ▼                                                     ▼
┌───────────────────────────┐           ┌───────────────────────────┐
│    OpenAI 백엔드           │           │    Gemini 백엔드           │
│    (GPT Image, DALL-E)    │           │    (Nano Banana)           │
│                           │           │                            │
│  • 요청 패스스루          │           │  • Gemini API로 변환       │
│  • SSE 스트리밍 지원      │           │  • size → aspectRatio 매핑  │
│  • output_format 지원     │           │  • imageConfig 생성        │
└───────────────────────────┘           └───────────────────────────┘

OpenAI → Gemini 매개변수 변환

Nano Banana (Gemini) 모델을 사용할 때, OpenAI 스타일 매개변수가 자동으로 Gemini 네이티브 형식으로 변환됩니다:

크기에서 종횡비 매핑

OpenAI size Gemini aspectRatio Gemini imageSize 비고
256x256 1:1 1K 최소 Gemini 크기
512x512 1:1 1K 최소 Gemini 크기
1024x1024 1:1 1K 기본값
1536x1024 3:2 1K 가로 (신규)
1024x1536 2:3 1K 세로 (신규)
1792x1024 16:9 1K 와이드 가로
1024x1792 9:16 1K 톨 세로
2048x2048 1:1 2K Pro 모델 전용
4096x4096 1:1 4K Pro 모델 전용
auto 1:1 1K 기본 폴백

요청 변환

OpenAI 형식 (입력):

{
  "model": "nano-banana",
  "prompt": "고요한 일본 정원",
  "size": "1536x1024",
  "n": 1
}

Gemini 형식 (변환됨):

{
  "contents": [
    {
      "parts": [{"text": "고요한 일본 정원"}]
    }
  ],
  "generationConfig": {
    "imageConfig": {
      "aspectRatio": "3:2",
      "imageSize": "1K"
    }
  }
}

변환 구현

변환은 src/infrastructure/backends/gemini/image_generation.rs에서 처리됩니다:

pub fn convert_openai_to_gemini(request: &OpenAIImageRequest)
    -> CoreResult<(String, GeminiImageRequest)>
{
    // 1. 모델 이름 매핑
    let gemini_model = map_model_to_gemini(&request.model);

    // 2. 크기를 종횡비 및 크기 카테고리로 파싱
    let parsed_size = parse_openai_size(&request.size, &request.model)?;

    // 3. imageConfig가 있는 Gemini 요청 빌드
    let gemini_request = GeminiImageRequest {
        contents: vec![GeminiContent { parts: vec![...] }],
        generation_config: Some(GeminiGenerationConfig {
            image_config: Some(GeminiImageConfig {
                aspect_ratio: Some(parsed_size.aspect_ratio.to_gemini_string()),
                image_size: Some(parsed_size.size_category.to_gemini_image_size()),
            }),
        }),
    };

    Ok((gemini_model, gemini_request))
}

스트리밍 이미지 생성 (SSE)

GPT Image 모델의 경우, 라우터는 스트리밍 이미지 생성을 위한 진정한 SSE 패스스루를 지원합니다:

┌─────────────┐                ┌─────────────┐                ┌─────────────┐
│   클라이언트 │────stream:true─▶│   라우터    │────stream:true─▶│   OpenAI    │
│             │                │             │                │             │
│             │◀───SSE 이벤트──│  패스스루   │◀───SSE 이벤트──│             │
└─────────────┘                └─────────────┘                └─────────────┘

SSE 이벤트 타입:

이벤트 설명
image_generation.partial_image 생성 중 중간 미리보기
image_generation.complete 최종 이미지 데이터
image_generation.usage 청구를 위한 토큰 사용량
done 스트림 완료

구현 (src/proxy/image_gen.rs):

async fn handle_streaming_image_generation(...) -> Result<Response, StatusCode> {
    // 1. 백엔드 요청에서 stream: true 유지
    // 2. bytes_stream()을 통해 스트리밍 요청 수행
    // 3. tokio 채널을 통해 SSE 이벤트 전달

    let (tx, rx) = tokio::sync::mpsc::unbounded_channel();

    tokio::spawn(async move {
        let mut stream = backend_response.bytes_stream();
        while let Some(chunk) = stream.next().await {
            // SSE 형식 파싱 (event:/data: 라인)
            // 클라이언트에 이벤트 전달
            for line in chunk_str.lines() {
                if let Some(event_type) = line.strip_prefix("event:") { ... }
                if let Some(data) = line.strip_prefix("data:") {
                    let event = Event::default().event(event_type).data(data);
                    tx.send(Ok(event));
                }
            }
        }
    });

    Ok(Sse::new(UnboundedReceiverStream::new(rx)).into_response())
}

GPT Image 모델 기능

라우터는 GPT Image 모델 (gpt-image-1, gpt-image-1.5, gpt-image-1-mini)에 대한 향상된 매개변수를 지원합니다:

매개변수 설명
output_format 이미지 파일 형식 png, jpeg, webp
output_compression 압축 레벨 0-100 (jpeg/webp 전용)
background 투명도 제어 transparent, opaque, auto
quality 생성 품질 low, medium, high, auto
stream SSE 스트리밍 활성화 true, false
partial_images 미리보기 수 0-3

모델 지원 매트릭스

기능 GPT Image 1.5 GPT Image 1 GPT Image 1 Mini DALL-E 3 DALL-E 2 Nano Banana Nano Banana Pro
스트리밍
output_format
background
커스텀 품질 standard/hd
이미지 편집
이미지 변형
최대 해상도 1536px 1536px 1536px 1792px 1024px 1024px 4096px

이미지 편집 및 변형

라우터는 /v1/images/edits/v1/images/variations를 통해 OpenAI 호환 이미지 편집 및 변형 엔드포인트를 제공합니다.

이미지 편집 (/v1/images/edits)

엔드포인트: POST /v1/images/edits

텍스트 프롬프트와 선택적 마스크로 기존 이미지를 편집할 수 있습니다. GPT Image 모델 및 DALL-E 2에서 지원됩니다.

요청 형식 (multipart/form-data):

image: <file>           # 원본 이미지 (PNG, 필수)
prompt: <string>        # 편집 지침 (필수)
mask: <file>            # 선택적 마스크 이미지 (PNG)
model: <string>         # 모델 이름 (예: "gpt-image-1", "dall-e-2")
n: <integer>            # 이미지 수 (기본값: 1)
size: <string>          # 출력 크기 (예: "1024x1024")
response_format: <string> # "url" 또는 "b64_json"

구현 (src/proxy/image_edit.rs): - 이미지 및 마스크 파일의 멀티파트 폼 파싱 - 이미지 검증 (형식, 크기, 종횡비) - 모델별 매개변수 변환 - 잘못된 입력에 대한 적절한 오류 처리

지원 기능

  • 타겟 편집을 위한 투명 PNG 마스크 지원
  • 다중 이미지 생성 (n 매개변수)
  • 유연한 출력 크기
  • URL 및 base64 응답 형식 모두

이미지 변형 (/v1/images/variations)

엔드포인트: POST /v1/images/variations

주어진 이미지의 변형을 생성합니다. DALL-E 2에서만 지원됩니다.

요청 형식 (multipart/form-data):

image: <file>           # 소스 이미지 (PNG, 필수)
model: <string>         # 모델 이름 (기본값: "dall-e-2")
n: <integer>            # 변형 수 (기본값: 1, 최대: 10)
size: <string>          # 출력 크기 ("256x256", "512x512", "1024x1024")
response_format: <string> # "url" 또는 "b64_json"

구현 (src/proxy/image_edit.rs): - 이미지 파일 검증 및 전처리 - DALL-E 2 특화 라우팅 - 지원되지 않는 모델에 대한 오류 처리 - 일관된 응답 형식화

주요 기능

  • 단일 요청에서 여러 변형 생성
  • 자동 이미지 형식 검증
  • 표준 OpenAI 응답 형식 호환성

이미지 유틸리티 모듈

image_utils.rs 모듈은 이미지 처리를 위한 공유 유틸리티를 제공합니다:

함수

  • validate_image_format(): PNG/JPEG 형식 및 크기 검증
  • parse_multipart_image_request(): 멀티파트 폼에서 이미지 추출
  • check_image_dimensions(): 크기 제약 검증
  • format_image_error_response(): 표준화된 오류 응답

검증 규칙

  • 최대 파일 크기: 4MB (설정 가능)
  • 지원 형식: PNG (편집/변형에 필수), JPEG (생성 전용)
  • 모델별 종횡비 제약
  • 마스크에 대한 투명 PNG 요구 사항

이 아키텍처는 유지보수성과 확장성을 유지하면서 수천 개의 요청을 처리할 수 있도록 확장 가능한 프로덕션 레디 LLM 라우터를 구축하기 위한 견고한 기반을 제공합니다. 깔끔한 관심사 분리로 새로운 기능을 추가하고, 구현을 교체하며, 각 컴포넌트를 철저히 테스트하기 쉽습니다.