fos-blog/study
01 / 홈02 / 카테고리03 / 시리즈
01 / 홈02 / 카테고리03 / 시리즈

카테고리

  • AI 페이지로 이동
    • RAG 페이지로 이동
    • agent 페이지로 이동
    • claude-code 페이지로 이동
    • harness 페이지로 이동
    • llm 페이지로 이동
    • ops 페이지로 이동
    • practice 페이지로 이동
  • algorithm 페이지로 이동
    • Alias Method: 가중치 랜덤 선택을 전처리 O(n), 추출 O(1)로 바꾸기
    • Welford's Online Algorithm: 값을 저장하지 않고 평균과 분산 계산하기
  • architecture 페이지로 이동
    • distributed-systems 페이지로 이동
    • domain 페이지로 이동
    • evolution 페이지로 이동
    • patterns 페이지로 이동
  • database 페이지로 이동
    • milvus 페이지로 이동
    • mysql 페이지로 이동
    • opensearch 페이지로 이동
    • postgresql 페이지로 이동
    • qdrant 페이지로 이동
    • redis 페이지로 이동
    • vespa 페이지로 이동
    • DB Connection Pool Saturation과 Thread Pool 격리
    • 커넥션 풀 크기는 얼마나 조정해야 할까?
    • gaps and islands: SQL 창 함수로 이어진 구간 찾기
    • MyBatis 기본기 — XML Mapper, resultMap, 동적 SQL, 운영 패턴 정리
    • MyBatis와 JPA/Hibernate 트레이드오프 — 레거시 백엔드를 다루는 시니어 관점
    • 한국어 형태소 분석기 Nori vs Lindera — OpenSearch에서 Milvus로 갈 때 어휘 검색은 유지되나
    • 정규화와 역정규화: 무결성과 조회 비용 사이의 선택
    • 벡터 DB 5종, 아키텍처는 어떻게 다른가
    • 벡터 DB 5종을 실제로 벤치마크했다 — 같은 recall에서 QPS는 얼마나 갈리나
    • 벡터 DB 어떻게 고를까 — OpenSearch · Milvus · Qdrant · Vespa · pgvector 비교
    • 벡터 DB를 실제로 도입한 사례 — 빅테크 프로덕션
  • devops 페이지로 이동
    • docker 페이지로 이동
    • k8s 페이지로 이동
    • Envoy Proxy
    • Graceful Shutdown
    • 종료 신호는 어디서 멈추는가
  • http 페이지로 이동
    • HTTP Connection Pool
    • HTTPS와 TLS: 핸드셰이크, 인증서, 종료 지점
  • java 페이지로 이동
    • concurrency 페이지로 이동
    • jdbc 페이지로 이동
    • spring 페이지로 이동
    • spring-batch 페이지로 이동
    • testing 페이지로 이동
    • Alpine 이미지의 Java 에서 링크 바꿔치기(TOCTOU)를 막지 못하는 이유
    • Java 동시성 락 정리 — 커머스 메뉴/프로모션 정책 캐시 갱신 관점
    • JVM 튜닝 실전: 메모리 구조부터 Virtual Threads, GC 튜닝, 프로파일링까지
    • 로그에 traceId 남기기 — MDC 부터 OpenTelemetry 까지
    • Java StampedLock — 읽기 폭주에도 쓰기가 밀리지 않는 락
    • Virtual Thread와 Project Loom
  • javascript 페이지로 이동
    • typescript 페이지로 이동
    • AbortController
    • Async Iterator와 제너레이터
    • CommonJS와 ECMAScript Modules
    • 제너레이터(Generator)
    • Http Client
    • Node 백엔드 운영 패턴 — Streams 백프레셔, pipe/pipeline, 멱등성 vs 분산 락
    • Node.js
    • `setImmediate()`
  • kafka 페이지로 이동
    • Kafka 클러스터 아키텍처: Broker, KRaft Controller와 복제
    • Kafka 기본 개념: Topic, Partition, Offset과 Segment
    • Kafka 실전 설계: 파티션 전략, 컨슈머 그룹, 전달 보장, 재시도, 순서 보장 트레이드오프
    • Kafka 파티션·리밸런스·컨슈머 지연 운영
    • Kafka를 로그로 이해하기: 복제, 재처리, 상태와 데이터 통합
    • Spring Kafka 컨슈머 오프셋 커밋과 트랜잭션 정렬: AckMode, manual ack, 멱등 처리
  • linux 페이지로 이동
    • fsync — 리눅스 파일 동기화 시스템 콜
    • SSH forced command 로 배포 키가 실행할 수 있는 명령 제한하기
  • mlops 페이지로 이동
    • llm-serving 페이지로 이동
    • model-router 페이지로 이동
    • Python CUDA 버전 생태계 — nvidia-smi, nvcc, pip, conda가 다 다른 버전을 말하는 이유
    • GPU 컨테이너의 CUDA 버전 호환성 — nvidia-smi부터 이미지 다이어트까지
    • Kubernetes GPU 노드에서 /run tmpfs가 꽉 차서 Pod가 안 뜰 때
    • GPU·CUDA·MPS 기초 — 자바 백엔드 개발자가 처음 만나는 그림
    • Multi-process GPU 워크로드 — 자바 ThreadPool 사용자가 만나는 모델 차이
    • ML 서비스 성능 분석 워크플로 — 자바 백엔드 트러블슈팅과 다른 점
    • 한 GPU 를 여러 프로세스가 나눠 쓰기 — Time-Slicing 과 MPS
  • network 페이지로 이동
    • Connection reset by peer는 누가 보낸 걸까 — 리버스 프록시 홉마다 TCP 연결은 따로 논다
    • L2(스위치)와 L3(라우터)의 역할 차이
    • L4와 VIP(Virtual IP Address)
    • IP Subnet
  • observability 페이지로 이동
    • Observability 입문: 시니어 백엔드가 장애를 탐지하고 대응하는 방식
    • K8s 위 Spring Boot 앱의 메트릭 수집
  • python 페이지로 이동
    • Python async/await — CompletableFuture·Reactor 와 다른 점, 그리고 blocking I/O 함정
    • Python 의존성 관리 — Java Maven/Gradle 사용자가 만나는 첫 충격
    • FastAPI 기초 — Spring Boot 사용자가 빠르게 익히는 법
    • Java 개발자를 위한 Python 심화 — OOP·데코레이터·컨텍스트 매니저
    • PyTorch 기초 — 텐서, 디바이스, 그리고 모델 로딩이 무거운 이유
    • Java 개발자를 위한 Python 문법 핵심
    • ThreadLocal 에서 contextvars 로 — Python 의 요청 컨텍스트 전파
    • OCR 동작 원리 — Layout · Text · Post-process 3단계
    • Python 서버의 RSS가 줄지 않는 이유: CPython, gc.collect(), malloc_trim
  • rabbitmq 페이지로 이동
    • RabbitMQ Basics — 실전 백엔드 관점에서 정리하는 메시지 브로커 기본기
    • RabbitMQ vs Kafka — 백엔드 메시징 선택 기준과 실전 운영 관점
  • resume 페이지로 이동
    • 경험 및 경력기술서
    • 김병태 경력기술서
    • 김병태 포트폴리오
  • task 페이지로 이동
    • ai-service-team 페이지로 이동
    • nsc-slot 페이지로 이동
    • personal-projects 페이지로 이동
    • sb-dev-team 페이지로 이동
    • the-future-company 페이지로 이동
  • thinking 페이지로 이동
    • 자기주도 학습: 질문에서 시작해 글로 검증하기
    • 좋은 일을 넘어 중요한 일을 하는 법
FOS-BLOG · FOOTERall systems normal·v0.1 · 2026.04.27·seoul, kr
Ffos-blog/study

개발 학습 기록을 정리하는 블로그입니다. 공부하면서 기록하고, 기록하면서 다시 배웁니다.

visitors
01site
  • Home↗
  • Posts↗
  • Categories↗
  • Glossary↗
  • About↗
02policy
  • 소개/about
  • 개인정보처리방침/privacy
  • 연락처/contact
03categories
  • AI↗
  • Algorithm↗
  • DB↗
  • DevOps↗
  • Java/Spring↗
  • JS/TS↗
  • React↗
  • Next.js↗
  • System↗
04connect
  • GitHub@jon890↗
  • Source repositoryjon890/fos-study↗
  • RSS feed/rss.xml↗
  • Newsletter매주 1 회 · 한 편의 글→
© 2026 FOS Study. All posts MIT-licensed.
built with·Next.js·Tailwind v4·Geist·Pretendard·oklch
fos-blog/mlops/Model Router 란 무엇인가
mlops

Model Router 란 무엇인가

애플리케이션에서 LLM 을 처음 붙일 때는 SDK 하나를 호출하면 끝난다. 그런데 모델이 둘 이상 되고, 어떤 요청은 싼 모델로 충분하고 어떤 요청은 비싼 모델이 필요하고, 제공자 한 곳이 죽었을 때 다른 곳으로 넘겨야 하는 상황이 오면 그 판단을 어디에 둘지가 문제가 된다. 애플리케이션 코드마다 if 로 흩어 놓으면 팀이 늘어날 때마다 같은 판단이 복제된...

2026.09.07·9 min read·78 views·SERIES · Model Router · 1/8

GPU로 LLM을 서빙한다는 것이 모델 한 대를 어떻게 굴리는지를 다뤘다면, 이 글은 그 위 계층을 다룬다. 모델이 여러 개가 되고 제공자가 섞이는 순간 생기는 문제다.

애플리케이션에서 LLM 을 처음 붙일 때는 SDK 하나를 호출하면 끝난다. 그런데 모델이 둘 이상 되고, 어떤 요청은 싼 모델로 충분하고 어떤 요청은 비싼 모델이 필요하고, 제공자 한 곳이 죽었을 때 다른 곳으로 넘겨야 하는 상황이 오면 그 판단을 어디에 둘지가 문제가 된다. 애플리케이션 코드마다 if 로 흩어 놓으면 팀이 늘어날 때마다 같은 판단이 복제된다.

Model Router 는 그 판단을 한 지점으로 모은 계층이다. 모든 모델 호출이 그 지점을 지나고, 거기서 어느 모델로 보낼지가 결정된다.

한 문장 결론부터 적는다. Model Router 라는 한 단어가 실제로는 성격이 다른 두 계층을 가리키고 있고, 그 둘을 구분하지 않으면 도구 선택이 어긋난다.

백엔드의 API Gateway 와 같은 자리

자바 백엔드를 해봤다면 이 구조는 이미 익숙하다. 인증, rate limit, 경로 변환, 감사 로그를 서비스마다 구현하지 않고 API Gateway 한 곳에 모으는 것과 같은 발상이다. Model Router 가 모으는 것은 아래와 같다.

관심사애플리케이션마다 두면라우터에 모으면
모델 선택팀마다 다른 기준으로 고른다한 정책을 모든 호출에 적용한다
인증 정보제공자 API 키가 여러 저장소에 흩어진다라우터만 키를 갖는다
비용 귀속어느 팀이 얼마 썼는지 합산할 곳이 없다호출 단위로 팀에 붙인다
fallback제공자 장애마다 각자 구현한다실패 시 다음 대상으로 넘긴다
감사 로그형식이 서비스마다 다르다정해진 형태로 한 곳에 쌓인다

Uber 가 공개한 GenAI Gateway 가 이 형태를 그대로 보여준다. Go 로 만든 단일 지점을 두고 외부 제공자와 자체 호스팅 모델을 하나의 인터페이스로 제공한다. 인터페이스는 OpenAI API 를 그대로 따랐다. 규모는 공개 시점 기준으로 약 30개 팀, 월 1,600만 쿼리, 피크 25 QPS 이고 LLM 활용 사례는 60개가 넘는다.

애플리케이션 입장에서 얻는 것이 하나 더 있다. 호출하는 쪽은 계속 하나의 인터페이스만 알면 되고, 모델을 갈아치우는 일이 라우터 설정 변경으로 끝난다.

두 계층을 구분한다

여기가 이 시리즈에서 가장 먼저 못박아야 할 지점이다. "라우팅" 이라는 한 단어가 서로 다른 두 판단을 덮고 있다.

두 계층이 보는 정보와 판단 주기가 다르다.

축결정 계층Gateway 계층
정하는 것어느 모델, 어느 제공자로 보낼지고른 모델의 어느 인스턴스로 보낼지
보는 정보요청 본문, 요청 성격, 모델별 단가와 품질인스턴스별 대기 요청 수, KV cache 채움 비율, 응답 지연
바뀌는 속도정책 단위. 사람이 바꾼다요청 단위. 초 단위로 상태가 변한다
실패 대응다른 모델로 fallback같은 모델의 다른 인스턴스로 재시도
대표 도구Azure Foundry model router, LiteLLM proxyvLLM production stack, llm-d

아래 계층은 자체 호스팅 모델이 있을 때만 존재한다. 외부 제공자 API 만 쓰면 인스턴스가 몇 대인지는 제공자 쪽 사정이라 내가 고를 대상이 아니다. 자체 호스팅 vLLM 을 여러 대 띄우는 순간부터 아래 계층이 내 몫이 된다.

이 구분이 도구 선택으로 바로 이어진다. LiteLLM proxy 의 routing_strategy 는 simple-shuffle(기본), least-busy, usage-based-routing, latency-based-routing, cost-based-routing 다섯 가지인데, 다섯 중 캐시 상태를 보는 것이 없다. 캐시를 보는 판단은 vLLM production stack 과 llm-d 가 아래 계층에서 따로 제공한다. 위 계층 도구에 아래 계층 기대를 걸면 없는 기능을 찾게 된다.

관리형 라우터가 하는 것

직접 만들기 전에 관리형 상품이 어디까지 해주는지를 봐야 한다. Azure AI Foundry 의 model router 가 대표적이다. 특징은 라우터 자신이 학습된 언어 모델이라는 점이다. 규칙표가 아니라 모델이 요청을 보고 판단한다.

판정 기준을 품질 예산으로 표현한 것이 이 상품의 핵심이다. 사용자가 모드를 고르면 그 예산 안에서 가장 싼 모델을 고른다.

모드판정
Balanced (기본)그 요청의 최고 품질 모델 대비 1에서 2% 품질 범위에서 가장 싼 모델
Cost같은 방식으로 5에서 6% 범위까지
Quality비용을 무시하고 최고 품질 모델

"비용을 얼마나 줄여준다" 가 아니라 "품질을 얼마나 포기할지" 를 사용자가 고르게 한 구조다. 비용 절감 폭은 요청 분포에 따라 달라지므로 상품이 약속할 수 없다.

관리형 라우터가 못 하는 것

제약이 분명하게 문서화돼 있고, 이 제약이 자체 구축 판단의 근거가 된다.

  • 유효 컨텍스트 한도가 라우팅 대상 중 가장 작은 모델의 한도로 내려앉는다. 어느 모델로 갈지 미리 알 수 없으니 가장 좁은 쪽에 맞춰야 한다. 긴 문서를 넣는 용도에서 이것이 먼저 걸린다.
  • 라우팅 판단은 텍스트만 본다. 이미지 입력을 받기는 하지만 판단 근거로는 쓰지 않고, 오디오는 받지 않는다.
  • 라우팅 대상은 Azure 안의 모델이다. 자체 호스팅 모델을 대상 목록에 넣을 수 없다.

반대로 통제 장치는 갖춰져 있다. 라우팅 대상 목록을 고정할 수 있고, 새 모델은 명시적으로 넣지 않으면 편입되지 않는다. fallback 대상도 그 목록이 겸하므로 승인하지 않은 모델로 넘어가지 않는다. 요청별로 시도한 모델 순서와 fallback 발생 여부를 조회하는 별도 API 도 있다.

결정 추적이 가능한지는 라우터를 운영할 때 반드시 확인할 항목이다. 사용자가 "답이 이상하다" 고 할 때 어느 모델이 응답했는지 모르면 원인을 좁힐 수 없다.

자체 구축으로 넘어가는 조건

관리형으로 충분한 경우가 많다. 아래 조건이 하나라도 걸리면 자체 구축을 검토하게 된다.

자체 호스팅 모델을 라우팅 대상에 넣어야 한다. 가장 흔하고 가장 결정적인 조건이다. 사내 데이터로 학습한 모델, 외부로 보낼 수 없는 요청을 처리하는 모델, 제공자 API 가 없는 오픈소스 모델이 대상에 들어가야 하면 관리형 라우터의 대상 목록에 올릴 방법이 없다. 이 조건이 걸리는 순간 위 계층과 아래 계층을 모두 내가 갖게 된다.

인스턴스 선택을 캐시 상태로 해야 한다. 자체 호스팅 vLLM 을 여러 대 띄웠다면, 어느 인스턴스로 보내는지가 응답 속도를 바꾼다. 같은 앞부분을 가진 요청이 이미 그 앞부분을 계산해 둔 인스턴스로 가면 prefill 을 다시 하지 않는다. 일반 로드 밸런서는 이 상태를 모르고 요청을 흩어 보내므로 prefix cache 적중이 떨어진다.

여기서 두 방식이 갈린다. 캐시를 보고 인스턴스를 고른다에서 자세히 다룬다.

방식판단
prefix aware같은 앞부분이면 항상 같은 인스턴스로 보낸다. 그 캐시가 이미 밀려났어도
KV cache aware실제 적중률이 가장 높은 인스턴스로 보낸다

판단 근거는 vLLM 이 /metrics 로 노출하는 지표다. OpenAI 호환 API 와 같은 포트를 쓰고 기본값은 8000 이며, 지표 이름의 접두사는 vllm: 이다.

지표종류뜻
vllm:num_requests_waitinggauge처리를 기다리는 요청 수
vllm:kv_cache_usage_percgauge사용 중인 KV cache 블록 비율. 0 에서 1
vllm:prefix_cache_queriescounterprefix cache 조회 수
vllm:prefix_cache_hitscounterprefix cache 적중 수

적중률 지표는 따로 없어서 prefix_cache_hits 를 prefix_cache_queries 로 나눠 직접 구한다.

제공자 캐시 회계를 정책에 넣어야 한다. 외부 제공자의 prompt caching 은 값이 셋이다. cache write 는 기본 입력 단가에 25% 프리미엄이 붙고, cache read 는 기본 입력 단가의 10% 수준이다. Claude Haiku 4.5 를 예로 들면 100만 토큰당 cache read 0.10대기본입력1.00 이다. 유효 시간은 제공자마다 다르다.

같은 앞부분에 적중이 2회 이상 나면 손익이 넘어간다. 한 번 쓰고 버리는 앞부분에 캐시를 붙이면 25% 프리미엄만 내고 끝난다. 그래서 "가장 싼 모델로 보내기" 가 실제로 가장 싼 선택이 아닐 수 있다. 캐시가 붙어 있는 비싼 모델이 더 싸게 끝나는 구간이 있다.

요청을 외부로 보내기 전에 처리해야 할 것이 있다. Uber 는 GenAI Gateway 에 PII redactor 를 두고 개인정보를 익명 자리표시자로 바꾼 뒤 외부로 보낸다. 이름은 ANONYMIZED_NAME_0, ANONYMIZED_NAME_1 처럼 순서대로 치환하고 응답을 돌려줄 때 원래 값으로 복원한다. Uber 는 이 처리가 지연을 늘리고 결과 품질도 함께 해친다고 밝혔다. 같은 단어가 위치에 따라 다른 번호로 치환돼 모델 성능이 떨어질 수 있다는 것이다. 공개 글은 문제를 밝히는 데까지만 가고 해결책은 제시하지 않았다.

이 사례가 라우터 설계의 성격을 잘 보여준다. 비용, 지연, 품질, 보안 넷을 함께 맞춘다는 말은 넷이 서로 충돌한다는 뜻이다.

같은 문제를 회사마다 다른 층에서 푼다

공개된 사례를 계층 경계로 늘어놓으면 선택지가 하나가 아니라는 것이 보인다.

사례서빙 계층라우팅 계층
토스증권vLLM, Triton, KServe공개 글에 라우팅 계층 언급이 없다
NetflixTriton, vLLM기존 JVM 서빙 계층 위에 얹었다
UberUber 자체 호스팅 stackGo 기반 GenAI Gateway 를 따로 만들었다
KT공개 글에 언급이 없다LiteLLM 을 도입하고 라우팅 정책만 직접 만들었다

읽을 지점은 셋이다.

  • 서빙 계층을 표준화한 조직이 그 위층을 아직 만들지 않은 상태가 있다. 토스증권 공개 글은 vLLM 과 Triton 과 KServe 선택까지 다루고 라우팅 계층은 다루지 않는다.
  • 라우팅 계층을 새로 만들지 않는 선택도 있다. Netflix 는 추천 시스템의 라우팅과 A/B 테스트를 이미 담당하던 JVM 서빙 계층에 LLM 호출을 붙였다.
  • 오픈소스를 쓰고 정책만 직접 만드는 절충이 있다. KT 는 인증과 로깅과 모니터링을 LiteLLM 에 맡기고, 요청을 Task 와 Domain 과 Level 과 Capability 네 축으로 분류하는 정책을 그 위에 얹었다.

각 사례의 판정 기준과 배경은 같은 문제를 회사마다 다른 층에서 푼다에서 따로 다룬다.

정리

  • Model Router 는 모델 호출이 지나는 한 지점이고, 백엔드의 API Gateway 가 인증과 rate limit 을 모으는 것과 같은 자리에 있다.
  • 한 단어가 두 계층을 덮고 있다. 어느 모델을 고르는 계층과, 고른 모델의 어느 인스턴스로 보내는 계층이다.
  • 아래 계층은 자체 호스팅 모델이 있을 때만 생긴다. 외부 제공자 API 만 쓰면 위 계층만 있다.
  • 관리형 라우터는 품질 예산으로 모델을 고르고, 대상은 그 클라우드 안의 모델로 한정되며 유효 컨텍스트 한도가 대상 중 가장 작은 모델에 맞춰진다.
  • 자체 호스팅 모델을 대상에 넣어야 하거나 캐시 상태로 인스턴스를 골라야 하면 자체 구축으로 넘어간다.

다음 편

다음 글에서는 결정 계층이 실제로 무엇을 보고 모델을 고르는지를 다룬다. 규칙, 임베딩 유사도, 분류 모델, 계단식 네 가지 기준이 있고 각각 라우팅 판단 자체에 지연을 얹는다. "싼 모델로 보내 비용을 줄인다" 는 말이 그 판단 비용을 계산에 넣으면 어떻게 달라지는지가 그 글의 주제다.

어느 모델로 보낼지 무엇으로 정하는가로 이어진다.

참고 링크

  • Azure AI Foundry model router
  • LiteLLM Router
  • LiteLLM proxy 설정 값
  • vLLM Metrics 설계
  • vLLM production-stack KV Cache Aware Routing
  • llm-d Precise Prefix Cache Aware Routing
  • prompt caching 가격 구조
  • TrueFoundry. KV cache routing
  • Uber GenAI Gateway
  • Netflix 사내 LLM 서빙
  • Netflix LLM 플랫폼 정리 (InfoQ)
on this page
  • 01백엔드의 API Gateway 와 같은 자리
  • 02두 계층을 구분한다
  • 03관리형 라우터가 하는 것
  • 04관리형 라우터가 못 하는 것
  • 05자체 구축으로 넘어가는 조건
  • 06같은 문제를 회사마다 다른 층에서 푼다
  • 07정리
  • 08다음 편
  • 09참고 링크
tags
#Model Router#LLM 서빙#입문#study📚 Model Router
NEXT →어느 모델로 보낼지 무엇으로 정하는가

이런 글도

  • mlops

    같은 문제를 회사마다 다른 층에서 푼다

    2026.09.08
  • mlops

    캐시를 보고 인스턴스를 고른다

    2026.09.08
  • mlops

    첫 토큰이 나간 뒤에는 재시도할 수 없다

    2026.09.08
  • mlops

    LiteLLM 로 라우팅 정책을 설정하는 방법

    2026.09.08

댓글 (0)