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/같은 문제를 회사마다 다른 층에서 푼다
mlops

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

시리즈 1번에서 Model Router 라는 한 단어가 두 계층을 덮고 있다고 적었다. 공개 사례를 그 경계로 늘어놓으면 다른 사실이 하나 더 드러난다. 같은 문제를 회사마다 서로 다른 층에서 풀고 있고, 어느 층에서 풀지가 그 조직에 이미 있던 것과 요건에 따라 갈렸다. Netflix 는 기존 계층을 재사용했고 Uber 는 단일 지점을 새로 만들었다. 빈...

2026.09.08·8 min read·97 views·SERIES · Model Router · 8/8

Model Router 시리즈 8번이고 마지막 글이다. 앞 글 vLLM 지표를 라우팅 판단에 쓰는 방법까지 계층과 판단 방식과 설정을 다뤘다. 이 글은 공개된 사례를 그 계층 경계에 늘어놓는다.

시리즈 1번에서 Model Router 라는 한 단어가 두 계층을 덮고 있다고 적었다. 공개 사례를 그 경계로 늘어놓으면 다른 사실이 하나 더 드러난다. 같은 문제를 회사마다 서로 다른 층에서 풀고 있고, 어느 층에서 풀지가 그 조직에 이미 있던 것과 요건에 따라 갈렸다. Netflix 는 기존 계층을 재사용했고 Uber 는 단일 지점을 새로 만들었다.

계층이 어디까지 덮이는가

사례서빙 계층라우팅 계층특징
토스증권vLLM, Triton, KServe공개 글에 없다서빙 표준화에 집중했다
NetflixTriton, vLLM기존 JVM 서빙 계층 위에 얹었다라우팅을 새로 만들지 않고 있던 것을 재사용했다
UberUber 자체 호스팅 stackGo 기반 GenAI Gateway 를 따로 만들었다모든 호출이 한 지점을 지난다
KT공개 글에 언급이 없다LiteLLM 을 도입했다라우팅 정책만 직접 만들었다

빈칸이 정보다. 토스증권 공개 글에 라우팅 계층이 없다는 것은 그 조직에 없다는 뜻이 아니라, 그 글이 다루는 문제가 아래 계층이었다는 뜻이다. KT 공개 글에 서빙 계층 언급이 없는 것도 같다.

네 사례를 하나의 발전 단계로 읽으면 잘못 읽는다. 각각 다른 문제를 풀고 있었고, 그 문제가 어느 층에 있었는지가 선택을 정했다.

토스증권. 아래 계층을 표준화한 사례

Machine Learning 팀이 공개한 글이다. 문제와 판정 기준이 분명해 가장 먼저 읽을 만하다.

문제는 둘이었다.

  • Llama3 8B 정도의 모델도 단순한 질문에 수십 초가 걸렸다
  • 모델을 서빙하려면 vLLM, Triton, Docker, Kubernetes 지식이 모두 필요해 진입 장벽이 높았다

두 번째 문제가 라우팅 계층과 성격이 다르다. 비용도 품질도 아니고, 모델을 만든 사람이 그것을 서빙까지 보내는 데 필요한 지식의 양이 문제였다.

판정 기준을 셋으로 세웠다.

기준무엇을 봤나
정량 지표Token per Second 와 Time to First Token
사용성학습한 모델을 몇 단계로 서빙까지 보낼 수 있는지
모델 지원Llama, Gemma, Mixtral, Qwen 호환성

선택은 vLLM 과 Triton 과 KServe 조합이었다. vLLM 은 성능과 사용성이 균형을 이루고 컴파일이 필요하지 않았다. Triton 은 dynamic batching 과 continuous batching 을 제공한다. KServe 는 yaml 하나로 서빙하게 해 배포 단계를 줄였다.

운영에서 얻은 것으로 정형화된 로그를 들었다. KServe 와 Triton 이 정해진 형태로 로그를 남기므로, 새 서버가 뜨면 대시보드에 자동으로 붙었다.

얻을 판단은 판정 기준을 먼저 세운 순서다. 프레임워크를 고르기 전에 무엇을 재서 고를지 정했다. 라우팅 계층에서도 같은 순서가 필요하다. 어느 모델로 보낼지 무엇으로 정하는가에서 효과성과 효율성을 따로 재야 한다고 적은 것이 이 순서에 해당한다.

Netflix. 라우팅 계층을 새로 만들지 않은 사례

모델 배포부터 추론까지 전부 자체 운영한다. 별도 ML 환경을 두지 않고 기존 프로덕션 환경 안에 넣었다.

  • Triton 이 모델 로딩, 배칭, GPU 스케줄링, 여러 프레임워크 서빙을 담당한다
  • vLLM 이 추론을 수행하고 동작을 확장하는 방법을 제공한다
  • 작은 요청은 로컬에서 실행하고 큰 요청은 별도 서빙으로 위임한다
  • 애플리케이션 통합을 모델과 런타임과 호스팅 환경으로부터 분리했다

라우팅 계층을 새로 만들지 않았다. 기존 JVM 기반 서빙 계층이 라우팅, 기능 검색, 후보 생성, 후처리, 로깅을 이미 담당하고 있었고 그 위에 LLM 호출을 얹었다.

이 결정이 자바 백엔드 개발자에게 직접 닿는다. 추천 시스템의 라우팅과 A/B 테스트 로직을 담던 계층이 이미 있었다. 그 계층은 요청을 여러 후보로 보내고 결과를 모으고 로그를 남기는 일을 하고 있었으므로, 모델 호출이 하나 더 붙는 것을 감당할 수 있었다.

"라우팅 계층을 만들어야 하는가" 라는 질문 앞에서 먼저 확인할 것이 여기 있다. 지금 요청을 분기하고 결과를 로깅하는 계층이 이미 있는지, 그 계층에 모델 선택 판단을 넣을 수 있는지다. 있으면 새 계층을 세우는 것보다 싸게 끝난다.

세 번째 항목도 눈에 남는다. 작은 요청은 로컬에서, 큰 요청은 별도 서빙으로 보낸다. 같은 애플리케이션 안에서 요청 크기로 실행 위치를 가른 것이고, 이것도 라우팅 판단의 한 형태다.

Uber. 단일 지점을 새로 만든 사례

모든 모델 호출이 지나는 단일 지점을 Go 로 만들었다. 규모는 공개 시점 기준으로 약 30개 팀, 월 1,600만 쿼리, 피크 25 QPS 이고 LLM 활용 사례는 60개가 넘는다.

담당하는 것이 다섯이다.

  • 외부 제공자와 자체 호스팅 모델을 하나의 인터페이스로 제공한다. OpenAI API 인터페이스를 그대로 따르고 내부 fork 를 쓴다
  • Vertex AI 연동을 위해 Go 라이브러리를 직접 만들어 오픈소스로 공개했다
  • 감사 로그로 비용 귀속과 보안 감사와 품질 평가를 함께 한다
  • 팀별 사용량과 예산을 관리한다
  • PII 를 제거한 뒤 외부로 보낸다

PII 처리가 품질을 해친다

이 사례에서 얻을 판단이 가장 큰 곳이 여기다.

PII redactor 가 입력을 훑어 개인정보를 익명 자리표시자로 바꾼다. 이름은 ANONYMIZED_NAME_0, ANONYMIZED_NAME_1 처럼 순서대로 치환하고, 응답을 돌려줄 때 원래 값으로 복원한다.

그런데 이 처리가 지연과 결과 품질을 함께 해친다고 적혀 있다. 같은 단어가 위치에 따라 다른 번호로 치환돼 LLM 성능이 떨어질 수 있다. 공개 글은 문제를 밝히는 데까지만 가고 해결책은 제시하지 않는다.

보안 요건이 품질과 충돌하는 실제 사례다. "비용과 지연과 품질과 보안을 함께 맞춘다" 는 문장이 실제로 어떤 충돌을 뜻하는지 보여준다.

라우터 설계에서 이 충돌이 생기는 자리가 어디인지도 드러난다. PII 처리는 모델을 고르기 전에 입력을 바꾸는 단계이므로, 라우팅 판단이 보는 텍스트도 이미 바뀐 텍스트다. 모델을 골라주는 계층이 대신 가져오는 제약에서 라우터가 요청의 일부만 읽고 판단한다고 적었는데, 이 사례에서는 읽는 것 자체가 원본이 아니다.

왜 새로 만들었나

Netflix 와 갈린 지점을 공개 글로 완전히 설명할 수는 없다. 다만 요건 목록에서 짐작할 수 있는 것이 있다.

요건기존 계층에 넣기 어려운 이유
30개 팀의 비용 귀속팀 경계를 넘는 집계가 필요하다. 한 서비스의 서빙 계층에는 그 경계가 없다
PII 제거외부로 나가는 모든 호출이 이 처리를 거쳐야 한다. 놓치는 경로가 있으면 요건이 성립하지 않는다
외부 제공자와 자체 호스팅을 한 인터페이스로제공자별 차이를 흡수하는 층이 따로 필요하다

두 번째가 단일 지점을 강제한다. "모든 호출이 반드시 이 지점을 지난다" 를 보장해야 하는 요건이 있으면 선택지가 좁아진다.

KT. 오픈소스를 쓰고 정책만 만든 사례

Agentic AI Lab 이 LiteLLM 을 도입하고 그 위에 라우팅 정책을 만들었다. 인증과 로깅과 모니터링은 LiteLLM 에 맡겼다.

얻을 판단은 분류 축을 넷으로 나눈 방식이다. 요청을 Task 와 Domain 과 Level 과 Capability 네 축으로 분류해 모델을 고른다. 공개 글은 축의 이름까지만 밝히고 각 축을 무엇으로 판정하는지는 적지 않는다.

난이도 하나로 고르지 않고 축을 나눈 것이 읽을 지점이다. 어느 모델로 보낼지 무엇으로 정하는가에서 다룬 판단 방식 넷은 각각 하나의 판정을 내놓는데, 축을 나누면 판정이 넷이 되고 그 조합으로 모델을 고른다.

축을 나누는 대가는 판단 비용이다. 축이 넷이면 각 축을 판정할 방법이 넷 필요하고, 그중 어느 축이 판정에 실패하면 전체 판단이 어긋난다. 축을 몇 개까지 둘 수 있는지, 각 축의 판정에 얼마가 드는지는 공개 글에 없다.

그 밖의 공개 사례

사례읽을 지점
카페24 LLM 라우터120여 개 모델을 하나로 묶은 규모
한화솔루션 사내 LLM Gateway거버넌스와 통제 관점에서 Gateway 를 세운 사례
Uber MCP Gatewayagentic coding 을 위해 MCP 서버들을 한 지점에 모은 사례
DigitalOcean inference router캐시를 보는 라우팅. 가장 싼 모델이 가장 싸지 않은 이유

앞의 둘은 규모와 통제 관점이고, 뒤의 둘은 이 시리즈에서 다룬 두 주제의 구현 사례다. MCP 게이트웨이는 LiteLLM 로 라우팅 정책을 설정하는 방법, 캐시 기준 라우팅은 캐시를 보고 인스턴스를 고른다에서 다뤘다.

사례에서 남는 질문

네 사례를 읽고도 답이 나오지 않은 것들이다. 공개 글이 다루지 않았거나, 조직마다 답이 다를 문제다.

  • Netflix 처럼 기존 서빙 계층을 재사용하는 것과 Uber 처럼 새로 만드는 것의 갈림은 무엇이 정하는가. 두 글 다 자기 선택의 이유를 다른 선택지와 비교해 설명하지 않는다
  • PII 치환이 품질을 떨어뜨린다면 그 손실을 어떻게 재는가. Uber 는 문제를 밝히고 해결책을 제시하지 않았다
  • KT 의 네 축 분류를 라우팅 판단에 넣으면 지연이 얼마나 늘어나는가. 축마다 판정 방법이 다르므로 하나의 값으로 답할 수 없다
  • 토스증권의 Token per Second 와 Time to First Token 두 지표만으로 라우팅 정책을 판정할 수 있는가. 두 지표는 아래 계층의 값이고, 위 계층의 판단에는 비용과 품질이 더 필요하다

시리즈를 마치며

여덟 편에서 정리한 것을 한 줄씩 남긴다.

편글남는 한 줄
1Model Router 란 무엇인가한 단어가 두 계층을 덮고 있다. 어느 모델인가와 어느 인스턴스인가는 다른 판단이다
2어느 모델로 보낼지 무엇으로 정하는가판단 지연은 대부분 병목이 아니다. 비용을 정하는 것은 그 판단이 고른 모델이다
3모델을 골라주는 계층이 대신 가져오는 제약어느 모델로 갈지 모른다는 상태가 컨텍스트 한도와 캐시 적중으로 돌아온다
4캐시를 보고 인스턴스를 고른다위 계층의 캐시는 비용 문제이고 아래 계층의 캐시는 지연 문제다
5첫 토큰이 나간 뒤에는 재시도할 수 없다그 경계가 HTTP 와 모델 두 층에서 온다. 경계 뒤에 남는 것은 셋이다
6LiteLLM 로 라우팅 정책을 설정하는 방법오류 종류마다 옮겨갈 곳이 다르므로 fallback 키가 셋으로 나뉘어 있다
7vLLM 지표를 라우팅 판단에 쓰는 방법요청 경로에서는 gauge 를 보고 histogram 은 사후 진단에 쓴다
8같은 문제를 회사마다 다른 층에서 푼다어느 층에서 풀지가 그 조직에 이미 있던 것과 요건에 따라 갈렸다

시리즈 전체를 관통하는 것이 하나 있다. 정밀도를 올리는 판단이 요청 경로에 무언가를 얹는다. usage-based-routing 이 Redis 를 읽고, KV cache aware 가 인스턴스 상태를 조회하고, 계단식이 모델을 두 번 부르고, PII 처리가 입력을 다시 쓴다. 라우팅 계층을 설계하는 일은 무엇을 더 정확히 판단할지가 아니라, 어느 정확도를 어느 지연으로 살지를 정하는 일이다.

참고 링크

  • 토스증권. LLM 쉽고 빠르게 서빙하기
  • Netflix. In-House LLM Serving
  • InfoQ. Netflix LLM 플랫폼 정리
  • Uber. GenAI Gateway
  • KT. Model Orchestrator
  • ZDNet. 카페24 LLM 라우터
  • AWS 기술 블로그. 한화솔루션 사내 LLM Gateway
  • Port. Uber 의 software factory 와 MCP Gateway
  • DigitalOcean. cache-aware inference router
  • CLOVA. 토큰 한 알의 질주
on this page
  • 01계층이 어디까지 덮이는가
  • 02토스증권. 아래 계층을 표준화한 사례
  • 03Netflix. 라우팅 계층을 새로 만들지 않은 사례
  • 04Uber. 단일 지점을 새로 만든 사례
  • PII 처리가 품질을 해친다
  • 왜 새로 만들었나
  • 05KT. 오픈소스를 쓰고 정책만 만든 사례
  • 06그 밖의 공개 사례
  • 07사례에서 남는 질문
  • 08시리즈를 마치며
  • 09참고 링크
tags
#Model Router#LLM 서빙#사례#study#insights📚 Model Router
← PREVIOUSvLLM 지표를 라우팅 판단에 쓰는 방법

이런 글도

  • mlops

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

    2026.09.08
  • mlops

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

    2026.09.08
  • mlops

    어느 모델로 보낼지 무엇으로 정하는가

    2026.09.08
  • mlops

    모델을 골라주는 계층이 대신 가져오는 제약

    2026.09.08

댓글 (0)