2026. 8. 16. 00:17ㆍPROJECT/Toy Project
[LLM Router #1] 프로젝트 소개와 학습 목표
1. 시작하며
앞선 세 프로젝트에서는 서로 다른 백엔드 문제를 다뤘다.
Playback Gate에서는 하나의 API에 많은 요청이 들어왔을 때 발생하는 Latency, Connection Pool, Cache, 동시성 문제를 확인했다.
Live Event Stream에서는 Kafka를 이용해 많은 Event를 비동기로 처리하면서 Consumer Lag, Partition, Idempotency, Retry, Replay 같은 문제를 다뤘다.
ContentOps Agent에서는 RAG와 Tool Calling을 구현하고 Retrieval, Generation, Grounding, Tool Selection을 Evaluation Dataset으로 평가했다.
이번에는 LLM을 사용하는 서비스의 조금 더 아래쪽을 다뤄보려고 한다.
ContentOps Agent에서는 애플리케이션에서 하나의 LLM을 호출했다.
Application
↓
LLM
↓
Response
기능을 만드는 입장에서는 이 구조만으로도 충분할 수 있다.
하지만 실제 서비스에서 LLM 사용량이 늘어나면 다른 문제가 생길 수 있다.
특정 Provider가 느려지면 어떻게 할까?
요청이 Timeout되면 다시 호출해야 할까?
재시도하면 같은 Provider를 다시 호출할까?
Provider가 계속 실패하면 언제 호출을 중단해야 할까?
하나의 Provider가 장애 나면 다른 Provider로 보낼 수 있을까?
모델마다 Request / Response 형식이 다른데 애플리케이션은 어떻게 통일할까?
Streaming 응답도 Provider마다 다르게 처리해야 할까?
어떤 Provider가 더 빠른지 어떻게 알 수 있을까?
어떤 Provider가 더 비싼지 어떻게 비교할까?
모든 요청을 가장 좋은 모델에 보내는 것이 맞을까?
예를 들어 하나의 서비스에서 여러 LLM Provider를 사용할 수 있다고 가정해본다.
OpenAI
Anthropic
Google
애플리케이션이 각각의 API를 직접 호출하기 시작하면 구조가 빠르게 복잡해질 수 있다.
Application
├─ OpenAI Client
├─ Anthropic Client
└─ Gemini Client
각 Provider마다:
Request Format
Response Format
Streaming
Error
Timeout
Token Usage
Model Name
등이 다르기 때문이다.
그래서 애플리케이션과 여러 LLM Provider 사이에 하나의 Gateway를 두는 구조를 만들어보려고 한다.
프로젝트 이름은 LLM Router로 정했다.
2. LLM Router란?
LLM Router는 여러 LLM Provider를 하나의 API 뒤에서 사용할 수 있도록 만들고, 요청 상황에 따라 적절한 Provider를 선택하는 Multi-LLM Gateway다.
기본적인 구조는 다음과 같다.
Client
↓
LLM Router
↓
┌─────────┼─────────┐
↓ ↓ ↓
OpenAI Anthropic Gemini
Client는 어떤 Provider를 사용하는지 반드시 알 필요가 없다.
예를 들어 Client는 다음처럼 하나의 형식으로 요청한다.
{
"prompt": "이 문장을 요약해줘."
}
LLM Router는 내부에서 Provider에 맞는 요청 형식으로 변환한다.
Unified Request
↓
Provider Adapter
↓
Provider API
응답 역시 다시 공통 형식으로 변환한다.
Provider Response
↓
Normalization
↓
Unified Response
즉 애플리케이션은:
OpenAI를 어떻게 호출하지?
Claude Streaming 형식은 어떻게 다르지?
Gemini Error Response는 어떻게 처리하지?
를 직접 처리하지 않고 LLM Router와 통신한다.
3. 왜 여러 LLM을 사용할까?
처음에는 하나의 Provider만 사용해도 충분하다.
하지만 하나의 Provider에 모든 요청이 의존하면 몇 가지 문제가 생길 수 있다.
예를 들어:
Provider A
Latency 증가
가 발생할 수 있다.
또는:
Provider A
5xx Error 증가
가 발생할 수도 있다.
Rate Limit에 도달하는 상황도 생각할 수 있다.
Application
↓
Provider A
↓
429
이런 상황에서 서비스 전체가 같은 장애를 그대로 전달받아야 하는지 고민할 필요가 있다.
여러 Provider를 사용할 수 있다면 다음과 같은 선택이 가능해진다.
Provider A 실패
↓
Provider B
또는:
Provider A 느림
Provider B 정상
↓
일부 Traffic 이동
비용 차이를 이용할 수도 있다.
단순한 요청
→ 상대적으로 저렴한 Model
복잡한 요청
→ 더 높은 성능의 Model
다만 실제로 이런 Routing이 항상 좋은지는 아직 모른다.
Routing 자체가 복잡성을 만들 수도 있고, Failover가 Latency를 더 늘릴 수도 있다.
이번 프로젝트에서는 바로 이 부분을 직접 확인해보려고 한다.
4. 단순한 Provider Wrapper로 끝내지 않는다
LLM Router를 다음 정도로 만드는 것은 어렵지 않다.
POST /chat
↓
OpenAI API 호출
↓
Response
그리고 Provider만 몇 개 추가할 수도 있다.
if provider == OPENAI
if provider == CLAUDE
if provider == GEMINI
하지만 이번 프로젝트의 목표는 단순한 API Wrapper가 아니다.
실제로 확인하고 싶은 것은 다음과 같다.
Provider 장애가 발생하면 어떻게 되는가?
Timeout은 전체 응답시간에 어떤 영향을 주는가?
Retry는 언제 도움이 되고 언제 오히려 느려지는가?
Failover를 하면 성공률은 얼마나 달라지는가?
Circuit Breaker는 어떤 상황에서 필요한가?
Routing 전략에 따라 Latency와 Cost가 어떻게 달라지는가?
Streaming 중 장애가 발생하면 Failover가 가능한가?
Provider 추상화가 어디까지 가능한가?
따라서 이전 프로젝트와 마찬가지로 가장 단순한 Baseline에서 시작한다.
5. 초기 구조는 최대한 단순하게 시작한다
처음부터 지능적인 Routing Engine을 만들지는 않는다.
Baseline은 다음 정도로 시작한다.
Client
↓
LLM Router API
↓
Fixed Provider
↓
LLM
↓
Response
먼저 하나의 공통 API를 만들고 하나의 Provider만 연결한다.
그다음 두 번째 Provider를 추가하면서 공통화할 수 있는 영역과 Provider별로 남겨야 하는 영역을 확인한다.
Unified API
↓
Provider Interface
↓
┌──────────┴──────────┐
↓ ↓
Provider A Provider B
이것을 Multi-Provider Baseline으로 사용한다.
초기에는 다음 기능을 모두 넣지 않는다.
Retry
Failover
Circuit Breaker
Dynamic Routing
Cost-aware Routing
Latency-aware Routing
Weighted Routing
Health Score
Rate Limit 분산
복잡한 Model Scoring
먼저 정상 요청과 실패 요청을 관찰할 수 있는 구조부터 만든다.
6. 학습 목표
6.1 Multi-Provider 추상화 이해
가장 먼저 여러 LLM Provider의 차이를 하나의 Application Interface 뒤에서 어떻게 다룰지 고민한다.
개념적으로는 다음 구조가 될 수 있다.
LLM Router
↓
Provider
├─ OpenAI
├─ Anthropic
└─ Gemini
하지만 Provider마다 완전히 동일한 기능을 제공한다고 가정할 수는 없다.
예를 들어:
Request Parameter
Response Metadata
Token Usage
Streaming Event
Error Type
이 서로 다를 수 있다.
따라서 이번 프로젝트에서는:
무엇을 공통 Interface로 만들고 무엇을 Provider-specific 정보로 남겨야 하는가?
를 직접 확인한다.
목표는 모든 차이를 억지로 숨기는 것이 아니다.
공통화가 가능한 부분과 불가능한 부분의 경계를 찾는 것이 목표다.
6.2 Unified Request / Response 설계
Client가 Provider별 API 구조를 알지 않아도 사용할 수 있는 Request / Response를 만든다.
예를 들어:
UnifiedChatRequest
prompt
model
temperature
maxTokens
stream
같은 공통 Request를 정의할 수 있다.
Response 역시:
content
provider
model
inputTokens
outputTokens
latency
처럼 공통화할 수 있다.
하지만 모든 Provider의 기능을 하나의 구조에 억지로 맞추면:
Provider A에는 존재
Provider B에는 없음
같은 문제가 생길 수 있다.
따라서 최소 공통 기능과 Provider-specific 기능을 어떻게 나눌지도 확인한다.
6.3 Streaming 처리 이해
LLM 서비스에서는 전체 답변이 만들어질 때까지 기다리는 대신 Token이 생성되는 대로 응답하는 Streaming이 자주 사용된다.
Client
↓
LLM Router
↓
Provider
Token
↓
Token
↓
Token
↓
Token
Non-streaming에서는:
Request
↓
LLM
↓
전체 Response
만 처리하면 된다.
Streaming에서는:
연결 유지
Chunk 전달
Client Disconnect
Provider Stream Error
Timeout
Backpressure
등 추가로 고려할 부분이 생긴다.
이번 프로젝트에서는 Non-streaming과 Streaming의 차이를 직접 구현하면서 확인한다.
6.4 Timeout 이해
외부 LLM API는 애플리케이션이 직접 통제할 수 없는 외부 시스템이다.
Provider 응답이 느려질 수 있다.
Client
↓
Router
↓
Provider
↓
...
↓
...
아무 제한 없이 기다리면 Router의 Connection과 Thread 같은 Resource가 오랫동안 점유될 수 있다.
따라서 Timeout을 설정한다.
하지만:
Timeout 짧음
→ 정상적으로 조금 느린 요청도 실패
Timeout 김
→ 장애 Provider를 오래 기다림
이라는 Trade-off가 있다.
이번 프로젝트에서는 실제 지연 상황을 만들어 Timeout이 전체 응답시간과 성공률에 어떤 영향을 주는지 확인한다.
6.5 Retry 이해
Provider 호출이 실패했다고 바로 실패 응답을 반환하는 것이 항상 최선은 아닐 수 있다.
일시적인 오류라면 재시도로 성공할 수도 있다.
Request
↓
Provider
↓
실패
↓
Retry
↓
성공
하지만 Retry 역시 비용이 있다.
1차 요청
2초 대기
↓
실패
Retry
2초 대기
↓
실패
Retry
2초 대기
결과적으로 사용자는 더 오랫동안 기다린 뒤 실패할 수도 있다.
LLM API라면 재시도 자체가 추가 API 호출 비용으로 이어질 수도 있다.
따라서:
어떤 오류를 Retry해야 하는가?
몇 번 Retry해야 하는가?
Backoff는 필요한가?
Timeout과 Retry를 어떻게 같이 설정해야 하는가?
를 직접 실험한다.
6.6 Failover 이해
Retry와 Failover는 같은 문제가 아니다.
Retry는 보통 같은 요청을 다시 시도하는 것이다.
Provider A
↓
실패
↓
Provider A 다시 호출
Failover는 다른 Provider로 요청을 넘긴다.
Provider A
↓
실패
↓
Provider B
Failover를 사용하면 가용성을 높일 가능성이 있다.
하지만 새로운 문제가 생긴다.
Provider마다 답변 특성이 다름
지원 기능이 다름
Model 성능이 다름
비용이 다름
응답시간이 다름
즉:
성공 응답을 받았다
=
동일한 결과를 받았다
는 아니다.
이번 프로젝트에서는 Failover가 성공률과 Latency에 실제로 어떤 영향을 주는지 확인한다.
6.7 Circuit Breaker 이해
Provider가 계속 실패하고 있는데 모든 요청을 계속 보내면 어떻게 될까?
Request 1 → Provider → 실패
Request 2 → Provider → 실패
Request 3 → Provider → 실패
Request 4 → Provider → 실패
이미 장애 상태임을 알고 있는데도 계속 Timeout까지 기다리는 것은 비효율적일 수 있다.
Circuit Breaker는 일정 조건에서 Provider 호출을 잠시 차단하는 방법이다.
정상
CLOSED
↓
실패 증가
↓
OPEN
↓
호출 차단
↓
일정 시간
↓
HALF_OPEN
↓
시험 호출
이번 프로젝트에서는 단순히 Circuit Breaker 라이브러리를 붙이는 것이 아니라:
실패율이 어느 정도일 때 열리는가?
Circuit Open 이후 Latency는 어떻게 달라지는가?
Failover와 같이 사용하면 어떤 일이 발생하는가?
복구 후 다시 Traffic을 보내도 되는 시점은 언제인가?
를 확인한다.
6.8 Rate Limit 이해
외부 Provider는 무한한 요청을 받아주는 시스템이 아니다.
Provider 또는 Model별로 요청 제한이 있을 수 있다.
Router
↓
Provider
↓
Rate Limit
↓
429
이런 상황에서:
Retry
대기
다른 Provider로 Failover
Request 거절
중 어떤 처리가 적절한지 고민한다.
또 여러 Provider가 있다면 한 Provider의 제한 때문에 전체 서비스가 막히지 않도록 Routing할 수도 있다.
다만 처음부터 복잡한 Rate Limiter를 만들지 않는다.
실제 제한 상황을 재현할 수 있는 Harness를 먼저 만든다.
6.9 Provider Health 이해
Routing을 하기 위해서는 현재 Provider 상태를 알아야 할 수도 있다.
예를 들어:
Provider A
최근 성공률 99%
평균 Latency 800ms
Provider B
최근 성공률 70%
평균 Latency 4,000ms
라면 동일하게 Traffic을 보내는 것이 맞는지 고민할 수 있다.
Provider Health는 단순한:
UP
DOWN
으로만 표현되지 않을 수도 있다.
다음과 같은 값도 참고할 수 있다.
Success Rate
Error Rate
Timeout Rate
Latency
Circuit State
이번 프로젝트에서는 Routing 판단에 필요한 상태가 무엇인지 확인한다.
7. Routing 전략 이해
LLM Router의 핵심 기능 중 하나다.
하지만 처음부터 AI가 알아서 최적의 Provider를 선택하는 복잡한 Router를 만들지는 않는다.
가장 단순한 전략부터 비교한다.
예를 들어:
Fixed Routing
은 모든 요청을 하나의 Provider로 보낸다.
Request
↓
Provider A
Round Robin을 사용할 수도 있다.
Request 1 → A
Request 2 → B
Request 3 → C
Request 4 → A
또는 가중치를 줄 수도 있다.
A 60%
B 30%
C 10%
그 이후 실제 Metric이 쌓이면 다음과 같은 Routing도 후보가 될 수 있다.
Latency-aware
Cost-aware
Health-aware
Model Capability-aware
중요한 것은 Routing 전략을 많이 구현하는 것이 아니다.
어떤 문제를 해결하기 위해 어떤 Routing이 필요한지 확인하는 것
이 목표다.
8. Latency-aware Routing
Provider마다 응답속도가 다를 수 있다.
예를 들어 실제 측정에서:
Provider A
p95 = 측정값
Provider B
p95 = 측정값
처럼 차이가 발생할 수 있다.
이때:
최근 Latency가 낮은 Provider
를 우선 선택하는 전략을 생각할 수 있다.
하지만 가장 빠른 Provider로 Traffic이 집중되면 다시 Latency가 증가할 수도 있다.
따라서 단순히:
가장 빠른 Provider
=
항상 선택
이 정답인지는 실험이 필요하다.
9. Cost-aware Routing
LLM API를 사용하면 호출량이 곧 비용으로 연결된다.
요청 하나의 비용은 대략 다음 요소와 관계될 수 있다.
Model
Input Token
Output Token
Router에서는 각 요청이 실제로 얼마나 많은 Token을 사용했는지 기록한다.
Request
↓
Provider
↓
Usage
├─ Input Tokens
└─ Output Tokens
그리고 Provider / Model의 비용 정보를 이용할 수 있다면 예상 비용도 계산한다.
이를 통해:
모든 요청을 높은 성능 Model에 보내는 경우
vs
일부 요청을 더 저렴한 Model로 보내는 경우
를 비교할 수 있다.
하지만 비용만 낮추는 것이 목표는 아니다.
Cost 감소
↓
Latency 증가?
실패율 증가?
응답 품질 변화?
같은 Trade-off를 함께 봐야 한다.
10. Token Usage를 직접 추적한다
LLM Router에서는 일반적인 HTTP Gateway와 달리 Token이라는 비용 단위가 중요하다.
따라서 요청마다 최소한 다음 정보를 기록한다.
Provider
Model
Input Tokens
Output Tokens
Total Tokens
이를 통해:
요청 수는 비슷한데 비용이 왜 증가했는가?
특정 Model의 Output이 유독 긴가?
Retry 때문에 Token 사용량이 증가했는가?
Failover 때문에 하나의 사용자 요청에 API 호출이 두 번 발생했는가?
같은 문제를 확인할 수 있다.
11. Retry와 비용의 관계도 확인한다
일반적인 HTTP Retry에서는 주로 성공률과 Latency를 본다.
LLM에서는 비용도 추가된다.
예를 들어:
Request
↓
Provider A
Input 1,000 Tokens
Output 생성 중 실패
↓
Retry
↓
Provider A
다시 호출
하면 하나의 사용자 요청 때문에 Provider 호출이 여러 번 발생할 수 있다.
Failover도 마찬가지다.
Provider A 실패
↓
Provider B 성공
최종적으로 Client는 하나의 Response만 받지만 내부에서는 두 번 호출했을 수도 있다.
따라서 Router에서는:
Client Request 수
Provider Call 수
를 구분해서 기록한다.
12. 실패 유형을 구분한다
모든 실패를 하나의 LLM_ERROR로 처리하지 않는다.
예를 들어 다음 실패는 성격이 다르다.
Timeout
Rate Limit
Provider 5xx
Authentication Error
Invalid Request
Streaming Error
Client Disconnect
Retry 가능한 오류와 Retry하면 안 되는 오류도 다를 수 있다.
따라서 Provider별 Error를 Router 내부의 공통 Error Category로 변환하는 구조를 고민한다.
Provider Error
↓
Normalization
↓
Router Error
이를 통해 Retry / Failover / Circuit Breaker 판단이 Provider별 예외 클래스에 직접 묶이지 않도록 한다.
13. 장애를 실제 Provider에만 의존해서 테스트하지 않는다
이번 프로젝트에서 중요한 Harness 중 하나가 될 부분이다.
Provider 장애가 실제로 발생하기를 기다릴 수는 없다.
OpenAI 장애가 날 때까지 기다림
Anthropic 429가 나올 때까지 요청
Gemini 응답이 느려질 때까지 기다림
같은 방식으로 실험할 수는 없다.
따라서 Provider의 상태를 의도적으로 만들어낼 수 있는 테스트 환경을 구성한다.
예:
정상 응답
500 Error
429 Rate Limit
2초 지연
10초 지연
연속 실패
Streaming 중간 실패
이런 상황을 재현할 수 있어야 한다.
실제 Provider 호출과 장애 Simulation을 분리함으로써 같은 조건을 반복해서 실험할 수 있도록 한다.
14. Resilience도 측정한다
이번 프로젝트에서는:
Retry를 넣었다.
Circuit Breaker를 넣었다.
Failover를 넣었다.
로 끝내지 않는다.
실제 결과를 비교한다.
예를 들어 Baseline에서 Provider 장애가 발생했을 때:
Success Rate
Error Rate
p95
p99
를 측정한다.
Retry를 적용한 뒤 다시 같은 장애를 발생시킨다.
Baseline
↓
Retry
↓
동일 Failure Scenario
↓
재측정
Failover도 마찬가지다.
최종적으로 다음과 같은 질문에 답할 수 있어야 한다.
성공률은 올라갔는가?
p95 / p99는 얼마나 증가했는가?
Provider Call 수는 얼마나 증가했는가?
추가 Token 사용량은 얼마인가?
어떤 실패에서는 Retry가 도움이 되었는가?
어떤 실패에서는 바로 Failover하는 편이 나았는가?
15. Benchmark를 별도 기능으로 본다
Routing 전략을 만들려면 Provider의 특성을 알아야 한다.
따라서 같은 Request Dataset을 여러 Provider에 보내고 결과를 비교하는 Benchmark를 만든다.
Benchmark Dataset
↓
┌─────┼─────┐
↓ ↓ ↓
A B C
↓ ↓ ↓
Metric 수집
↓
비교
최소한 다음은 비교할 수 있도록 한다.
Success Rate
Latency
TTFT
Token Usage
Estimated Cost
Streaming을 사용한다면 전체 응답시간뿐 아니라 첫 응답이 시작될 때까지 걸리는 시간도 중요해진다.
Routing 정책은 이러한 실제 측정값을 기반으로 검토한다.
16. 응답 품질은 어떻게 볼 것인가?
LLM Provider를 비교하면 가장 어려운 문제가 하나 생긴다.
어떤 답변이 더 좋은가?
이다.
Latency나 Token은 숫자로 쉽게 비교할 수 있지만 답변 품질은 그렇지 않다.
이번 프로젝트의 핵심은 LLM Evaluation 프로젝트를 하나 더 만드는 것이 아니다.
ContentOps Agent에서 이미 AI 품질을 평가해봤기 때문에 이번에는 Gateway와 Routing, Resilience, Cost에 더 집중한다.
따라서 초기 Benchmark의 핵심 Metric은:
Latency
Success Rate
Error Rate
Token Usage
Cost
Failover 결과
로 둔다.
다만 Routing에 실제 답변 품질이 중요한 문제가 생긴다면 간단한 고정 Evaluation Dataset을 추가하는 것은 검토할 수 있다.
17. Streaming에서는 Failover가 더 어려워진다
Non-streaming Request는 Response가 Client에 전달되기 전에 Provider가 실패했다면 다른 Provider로 재시도할 수 있다.
Provider A 실패
↓
Provider B 호출
↓
Client Response
하지만 Streaming에서는 일부 Token이 이미 Client에게 전달됐을 수 있다.
Provider A
"오늘 날씨는"
↓
Client 전달 완료
↓
Provider A 실패
여기서 Provider B로 바꾸면:
이미 전달한 응답은 어떻게 할까?
Provider B의 답변 시작점은 어디인가?
같은 문장을 다시 생성하면 어떻게 할까?
라는 문제가 생긴다.
따라서:
Streaming Failover는 Non-streaming Failover와 같은 문제인가?
도 직접 확인해보려고 한다.
모든 Streaming 실패를 자동 Failover할 수 있다고 미리 가정하지 않는다.
18. Provider 추상화도 과도하게 만들지 않는다
Multi-Provider 프로젝트를 시작하면 처음부터 다음과 같은 구조를 만들기 쉽다.
ProviderFactory
ProviderStrategy
ProviderRegistry
RoutingStrategyFactory
ModelCapabilityResolver
ProviderSelector
하지만 초기에는 이런 구조가 모두 필요하지 않을 수 있다.
먼저 최소한의 Provider Adapter 구조로 시작한다.
LLM Provider
├─ Provider A
└─ Provider B
실제 Routing 전략이 여러 개 생겼을 때 Strategy 분리를 검토한다.
이번에도:
미래 확장을 위해 미리 복잡한 추상화를 만들지 않는다.
는 원칙을 유지한다.
19. 이전 프로젝트와 다른 점
지금까지 프로젝트의 중심 문제를 비교하면 다음과 같다.
프로젝트핵심 문제
| Playback Gate | 동기 API 성능과 정합성 |
| Live Event Stream | 비동기 Event 처리와 안정성 |
| ContentOps Agent | RAG / Agent 품질 평가 |
| LLM Router | 외부 LLM Provider Routing과 장애 대응 |
관찰하는 값도 달라진다.
Playback GateLive Event StreamContentOps AgentLLM Router
| RPS | Events/sec | Hit Rate@K | Provider Success Rate |
| p95 / p99 | Consumer Lag | MRR | p95 / p99 |
| DB Query | Event Latency | Grounding | TTFT |
| Connection Pool | Retry / DLT | Tool Selection | Retry |
| Race Condition | Duplicate Event | Hallucination | Failover |
| Lock | Partition | Token Usage | Circuit Breaker |
| Cache | Replay | LLM Latency | Cost / Token |
ContentOps Agent에서는:
LLM이 올바른 결과를 만드는가?
를 중심으로 봤다면,
LLM Router에서는:
LLM을 호출하는 인프라가 장애와 비용, Latency 변화 속에서도 안정적으로 동작하는가?
를 중심으로 보게 된다.
20. 이번 프로젝트에서 Harness가 달라지는 점
ContentOps Agent에서는 Evaluation Dataset이 Harness의 중요한 부분이었다.
Question
↓
AI System
↓
Expected와 비교
LLM Router에서는 여기에 Failure Scenario와 Benchmark Dataset을 추가하려고 한다.
Benchmark Dataset
+
Failure Scenario
+
Provider Simulator
예를 들어:
Scenario 1
정상 Provider
Scenario 2
Latency 5초
Scenario 3
429
Scenario 4
연속 500 Error
Scenario 5
Streaming 중단
같은 환경을 반복 실행한다.
즉 이번 프로젝트의 Harness는 대략:
Docs
+
Project Rule
+
Test
+
Benchmark
+
Failure Injection
+
Experiment
+
Git
형태가 된다.
ContentOps Agent에서:
결과 품질을 반복해서 평가할 수 있는 환경
을 만들었다면,
이번에는:
Provider 장애와 성능 특성을 반복해서 재현할 수 있는 환경
까지 만들어보려고 한다.
21. 이번에도 기술을 미리 정답으로 두지 않는다
LLM Gateway를 검색하면 여러 기술이 바로 떠오를 수 있다.
Retry
Circuit Breaker
Fallback
Load Balancing
Rate Limiting
Dynamic Routing
하지만 처음부터 모두 적용하지 않는다.
예를 들어 Timeout이 발생했다고 해서 반드시 Retry가 답은 아닐 수 있다.
Timeout
↓
Retry
↓
또 Timeout
↓
Latency만 증가
할 수도 있다.
반대로 바로 Failover하는 편이 나을 수도 있다.
Circuit Breaker 역시 장애율이 낮다면 복잡성만 늘어날 수 있다.
따라서 이번에도 다음 순서를 유지한다.
Baseline
↓
Failure 재현
↓
Metric 확인
↓
원인 분석
↓
후보 비교
↓
Human Gate
↓
변경
↓
동일 Scenario 재측정
22. 학습 목표를 정리하면
이번 프로젝트에서 다뤄볼 핵심 주제는 다음과 같다.
Multi-Provider Gateway
Provider Adapter
Unified Request / Response
Provider-specific Capability
Streaming
SSE
TTFT
Client Disconnect
Stream Error
Resilience
Timeout
Retry
Backoff
Failover
Circuit Breaker
Routing
Fixed Routing
Round Robin
Weighted Routing
Latency-aware Routing
Cost-aware Routing
Health-aware Routing
Observability
Provider Latency
Success Rate
Error Rate
Timeout Rate
Circuit State
Token Usage
Provider Call Count
Cost
Input Tokens
Output Tokens
Provider별 사용량
Retry / Failover 추가 호출
예상 비용
Benchmark
동일 Dataset
동일 Scenario
Provider 비교
Routing 전후 비교
중요한 것은 이 기술을 전부 최종 시스템에 넣는 것이 아니다.
실험 결과에 따라 일부는 최종 구조에서 빠질 수도 있다.
23. 최종 목표
LLM Router 프로젝트를 통해 단순히 OpenAI, Anthropic, Gemini API를 하나씩 호출해보는 것이 아니라,
여러 LLM Provider를 하나의 Gateway 뒤에서 일관된 Interface로 사용할 수 있도록 만들고, Provider의 Latency·Error·Rate Limit·Timeout 같은 문제를 직접 재현한 뒤 Retry·Failover·Circuit Breaker·Routing 전략을 비교하여 가용성·응답시간·Token 사용량·비용 사이의 Trade-off를 실제 측정값으로 설명할 수 있는 경험
을 만드는 것이 최종 목표다.
특히 마지막에는 다음 질문에 직접 답할 수 있도록 해보려고 한다.
Provider마다 다른 Request / Response를 어디까지 공통화할 수 있는가?
Streaming과 Non-streaming은 장애 처리 방식이 어떻게 다른가?
Timeout은 어느 정도로 설정해야 하는가?
어떤 Error를 Retry해야 하고 어떤 Error는 Retry하면 안 되는가?
Retry 횟수를 늘리면 성공률과 Latency는 어떻게 달라지는가?
같은 Provider Retry와 다른 Provider Failover는 어떤 차이가 있는가?
장애 Provider를 계속 호출하지 않으려면 Circuit Breaker가 필요한가?
Circuit Breaker 적용 전후 Failover Latency는 어떻게 달라지는가?
Provider마다 Latency 차이가 실제로 존재하는가?
가장 빠른 Provider만 선택하는 Routing이 항상 좋은가?
Cost-aware Routing은 실제 비용을 줄일 수 있는가?
Retry와 Failover 때문에 하나의 Client Request가 몇 번의 Provider Call로 증가하는가?
Streaming 도중 Provider가 실패하면 Failover할 수 있는가?
최종 Routing 전략이 Fixed Routing보다 실제로 나은가?
이번 프로젝트에서도 기본 원칙은 같다.
Baseline
↓
측정
↓
Failure 재현
↓
원인 분석
↓
후보 비교
↓
Human Gate
↓
개선
↓
동일 Scenario 재측정
Playback Gate에서는 서버의 성능을,
Live Event Stream에서는 Event Pipeline의 안정성을,
ContentOps Agent에서는 AI의 결과 품질을 측정했다면,
이번에는 LLM을 호출하는 Gateway 자체의 안정성과 비용, Latency를 측정 가능한 문제로 만들어보려고 한다.
다음 글에서는 LLM Router가 실제로 어떤 요청을 받고, 어떤 Provider를 지원하며, Timeout·Retry·Failover·Streaming·Routing을 어느 범위까지 책임질지 정리하기 위해 기능 요구사항과 비기능 요구사항을 정의해보려고 한다.
'PROJECT > Toy Project' 카테고리의 다른 글
| [LLM Router #3] 바이브 코딩과 Harness 전략 (0) | 2026.08.16 |
|---|---|
| [LLM Router #2] 요구사항 정의 (0) | 2026.08.16 |
| [ContentOps Agent #4] 학습 목표, 요구사항, Harness 회고 (0) | 2026.08.16 |
| [ContentOps Agent #3] AI 프로젝트에서 Evaluation을 Harness로 사용하는 방법 (0) | 2026.08.15 |
| [ContentOps Agent #2] 요구사항 정의 (0) | 2026.08.15 |