Portfolio.
projects

사내 AI Hub

사내 AI Hub

사내 AI Hub 설계·구현 / 온프레미스 LLM 운영·NEXV 인턴 2026.01–06

사내 여러 서비스가 외부 AI API에 의존하면서 비용과 응답 지연이 동시에 커지고 있었습니다. 저는 AI Hub를 처음부터 설계·구현하고, 온프레미스 LLM 추론 서버를 구축·운영했습니다. 제가 맡은 추론 서버에 vLLM 전환, RAG, 양자화, 모델 선정·튜닝을 적용해 응답을 6초대에서 2~3초로 줄이고 동시 처리량을 4배 개선했습니다.

vLLM · RAG · 양자화 · gpt-oss-120B · qwen-image · MeloTTS

사내 AI Hub 화면

느리다는 감을 수치로 바꾸기 — Ollama 병목을 재고 vLLM으로 옮긴 사내 AI Hub 이야기

들어가며 — "왜 이렇게 느려요?"라는 한 마디에서 시작했습니다

NEXV 인턴으로 사내 AI Hub 설계·구현과 온프레미스 LLM 추론 서버 운영을 맡았던 임현우입니다. AI Hub는 텍스트 대화, 이미지 생성, 음성 합성(TTS)을 한곳에서 받아 쓰는 사내 공통 AI 백엔드인데요. 제가 합류하고 가장 자주 들은 말이 "왜 이렇게 느려요?"였습니다.

사내 여러 서비스가 외부 AI API에 기대고 있었습니다. 호출이 늘수록 비용이 쌓였고, 응답은 좀처럼 빨라지지 않았어요. 그래서 저희 팀은 자체 호스팅으로 방향을 잡았는데, 막상 띄워 보니 이번엔 다른 벽이 있었습니다. 동시 요청 세 건만 들어와도 응답이 6초 넘게 밀리는 거였어요. 데모에서는 멀쩡하던 게, 사람 몇 명이 동시에 누르는 순간 한 줄로 줄을 서 버렸습니다.

AI Playground — 텍스트·이미지·음성을 한 허브로 묶어, 사내 서비스가 같은 AI 백엔드를 쓰게 했습니다.
AI Playground — 텍스트·이미지·음성을 한 허브로 묶어, 사내 서비스가 같은 AI 백엔드를 쓰게 했습니다.

느리다는 건 모두가 느꼈지만, 정작 "얼마나, 어디서" 느린지는 아무도 숫자로 말하지 못했습니다. 이 글은 그 "느리다는 감"을 측정 가능한 값으로 바꾸고, 병목을 찾아 추론 서버를 갈아 끼운 과정을 적은 기록입니다.

왜 이 문제가 어려웠을까요? — 비용과 지연이 같이 걸려 있었습니다

외부 API가 비싸면 직접 호스팅하고, 느리면 더 좋은 GPU를 붙이면 된다 — 처음엔 비용과 지연을 그렇게 따로 봤습니다. 그런데 막상 들여다보니 비용과 지연이 한 줄로 엮여 있었습니다. 둘 중 하나만 건드리면 다른 하나가 따라 나빠지는 구조였어요.

외부 API를 줄이려고 자체 모델을 띄우면 GPU 비용이 들어옵니다. 큰 모델일수록 품질은 좋지만 GPU를 더 먹고, 그러면 한 장에 여러 요청을 태워야 단가가 맞는데, 여러 요청을 동시에 태우면 이번엔 지연이 다시 올라갑니다. 텍스트뿐이라면 그나마 단순했을 텐데, 저희 허브는 이미지 생성과 음성 합성까지 같은 자원을 나눠 써야 하는 멀티모달이었습니다.

그래서 "무엇을 먼저 바꿔야 하는가"부터가 어려웠습니다. 추론 서버가 문제인지, 모델 크기가 문제인지, 컨텍스트로 들어가는 토큰이 문제인지 — 감으로는 셋 다 의심스러웠지만, 손은 하나뿐이니 순서를 정해야 했거든요.

느린 곳부터 찾자 — "감"을 동시 처리량과 응답 시간으로 바꿨습니다

그래서 가장 먼저 한 일은 모델을 바꾸는 게 아니라 재는 것이었습니다. 막연한 "느림" 대신 두 가지 값으로 문제를 다시 적었어요. 하나는 동시에 몇 건까지 받아낼 수 있는가(동시 처리량), 다른 하나는 한 요청이 끝나기까지 얼마나 걸리는가(응답 시간)였습니다.

재 보니 그림이 분명해졌습니다. 기존 Ollama 추론 서버는 요청이 들어오는 족족 거의 한 줄로 처리됐고, 그래서 동시 3건만 겹쳐도 뒤 요청이 앞 요청을 기다리느라 6초대로 밀렸습니다. 모델 자체가 느리다기보다, 동시에 들어온 요청을 함께 처리하지 못하는 게 병목이었어요. 느린 원인은 모델 성능이 아니라 동시성 처리 방식이었습니다.

그래서 추론 서버를 vLLM으로 옮겼습니다 — 단점도 같이 받아들이며

병목이 동시성에 있다는 게 보이자, 해결의 방향도 정해졌습니다. 들어온 요청들을 한 줄로 세우지 말고 묶어서 함께 처리하는 추론 서버가 필요했어요. 저희 팀은 Ollama를 vLLM 자체 호스팅으로 교체하기로 했습니다. vLLM은 들어온 요청을 토큰 단위로 묶는 continuous batching에 더해, KV 캐시를 페이지처럼 잘게 관리하는 PagedAttention으로 GPU 메모리 낭비를 줄여, 같은 GPU에 더 많은 요청을 동시에 태울 수 있는 구조였거든요.

물론 vLLM도 거저 얻은 건 아니었습니다. Ollama가 "한 줄로 띄우면 끝"에 가까웠다면, vLLM은 추론 설정을 직접 잡고 GPU 위에서 운영해야 하는 만큼 운영 복잡도가 올라갑니다. 그래서 운영은 복잡해지지만 측정된 지연과 동시 처리량이 확실히 좋아진다는 트레이드오프를 팀이 같이 확인하고 넘어갔어요. 단점을 덮지 않고 먼저 인정한 뒤 고른 결정이었습니다.

정리하면 이렇습니다. Ollama는 빠르게 띄워 보기 좋은 엔진이라 MVP 단계에선 잘 맞았고, 저희도 거기서 시작했어요. 그러다 트래픽이 늘고 프로덕션 단계로 넘어가면서, 대량 동시 요청을 감당하고 GPU 활용률을 끌어올릴 엔진이 필요해진 겁니다. 공정하게 덧붙이면 Ollama에도 동시 처리를 늘리는 옵션(OLLAMA_NUM_PARALLEL)이 있어서 "Ollama라서 무조건 느리다"고 말하긴 어렵습니다. 다만 텍스트·이미지·음성이 같은 GPU 자원을 나눠 쓰는 멀티모달 환경에서는, 요청을 묶는 continuous batching·KV 캐시를 페이지로 관리하는 PagedAttention·필요하면 여러 GPU로 펴는 Tensor Parallel까지 함께 가져가는 vLLM 쪽이 같은 자원에서 동시 처리량을 더 안정적으로 끌어올렸습니다.

AS-IS · OllamaTO-BE · vLLM
동시 요청 처리사실상 한 줄로 — 3건만 겹쳐도 밀림묶어서 함께 처리 → 동시처리 4배 개선
응답 시간6초대2~3초
운영 부담낮음 (띄우면 끝)올라감 (추론 설정·GPU 운영)
추론 서버를 바꾼 목적은 동시에 들어온 요청을 함께 처리하는 것이었습니다.
bash
# 운영 의도만 남긴 예시 vllm serve gpt-oss-120B \ --gpu-memory-utilization 0.45 \ --max-model-len 8192 # 양자화 모델과 KV 캐시 dtype은 # 모델·GPU 메모리에 맞춰 별도로 조정

한 가지 더 신경 쓴 건 GPU를 알뜰하게 쓰는 일이었습니다. 가진 자원은 H100 4장과 DGX Spark 2대였는데, 모델을 서버별로 나눠 올리면서도 한 장에 모델을 여러 개 공존시키려고 메모리 점유를 직접 통제했어요. vLLM은 기본값이면 GPU 메모리를 거의 다 잡아 버리는데, gpu-memory-utilization 값을 모델마다 낮춰 잡으면 같은 카드에 다른 모델도 함께 올라갑니다. KV 캐시는 컨텍스트가 길수록 메모리를 많이 먹어서, 빠듯한 모델에만 fp8로 줄이거나 컨텍스트 길이를 제한하는 식으로 필요할 때만 선택적으로 켰습니다.

텍스트 대화 — 모델과 파라미터를 직접 고르게 해, 운영자가 응답 품질과 비용을 조정할 수 있게 했습니다.
텍스트 대화 — 모델과 파라미터를 직접 고르게 해, 운영자가 응답 품질과 비용을 조정할 수 있게 했습니다.

비용은요? — 양자화와 컨텍스트 다이어트로 같이 잡았습니다

지연을 잡았으니, 처음의 다른 절반인 비용 차례였습니다. 큰 모델을 그대로 띄우면 GPU를 많이 먹는데, 사내 용도에서 매번 최상위 품질이 꼭 필요한 건 아니었어요. 그래서 대형 모델을 양자화해 GPU 비용을 낮췄습니다.

여기에도 트레이드오프가 있습니다. 양자화는 약간의 품질 손실을 동반하거든요. 다만 사내에서 쓰는 작업들에서는 그 손실이 체감되지 않는 선이었고, 줄어드는 GPU 비용이 훨씬 컸습니다. 약간의 품질을 내주고 비용을 크게 아끼는 균형을, 화려한 벤치마크가 아니라 실제 사내 용도에 맞춰 골랐어요. 솔직히 말하면 이 '체감되지 않는다'는 판단은 양자화 전후 출력을 실제 사내 작업으로 돌려 보고 내린 정성 평가였습니다. 표준 벤치마크나 블라인드 비교 같은 정량 검증까지 한 건 아니라서, 더 엄밀한 품질 측정은 남은 과제로 두고 있어요.

비용은 GPU에만 있는 게 아니었습니다. 컨텍스트로 들어가는 토큰도 곧 돈이었어요. 그래서 RAG로 필요한 맥락만 추려 넣고, 긴 내용은 계층적 요약(2단계)으로 압축해 컨텍스트 토큰 자체를 줄였습니다. 모델에게 다 읽으라는 대신 필요한 것만 읽으라고 한 거죠.

  1. 1

    추론 서버 교체

    Ollama 병목 → vLLM 자체 호스팅으로 동시 처리량을 끌어올림

  2. 2

    모델 양자화

    대형 모델 양자화로 GPU 비용 절감 (사내 용도에 맞춘 품질-비용 균형)

  3. 3

    컨텍스트 다이어트

    RAG + 계층적 요약(2단계)으로 토큰 비용 관리

텍스트만으로는 부족했습니다 — 이미지·음성까지 한 허브로

사내에서 필요로 한 건 채팅만이 아니었습니다. 어떤 서비스는 이미지를 만들어야 했고, 어떤 서비스는 음성이 필요했어요. 이걸 서비스마다 따로 붙이면 다시 외부 API와 비용·관리 부담이 흩어집니다. 그래서 저희 팀은 텍스트·이미지·음성을 한곳에서 제공하는 공통 허브로 묶었습니다.

텍스트는 gpt-oss-120B, 이미지는 qwen-image, 음성은 MeloTTS를 자체 호스팅으로 올렸습니다. 저는 이 과정에서 모델을 다운로드하고, 튜닝하고, 어떤 작업엔 어떤 모델이 맞는지 고르는 과정을 맡았어요. 모델 하나를 띄우는 것보다, 이 용도엔 이 모델 저 용도엔 저 모델을 정리해 두는 일이 실제로는 더 손이 갔습니다.

이미지 생성 — 생성 결과와 편집 옵션을 같은 화면에 두어, 이미지 모델도 공통 허브 안에서 운영했습니다.
이미지 생성 — 생성 결과와 편집 옵션을 같은 화면에 두어, 이미지 모델도 공통 허브 안에서 운영했습니다.

결과적으로 사내 서비스들이 외부 API를 각자 부르는 대신, 공통 허브 하나로 텍스트·이미지·음성을 모두 받아 가는 그림이 됐습니다.

그래서 무엇이 달라졌나요?

감으로 말하던 "느림"을 숫자로 바꾸고 병목부터 손대니, 결과도 같은 단위로 말할 수 있게 됐습니다.

지표
추론 처리량4배 개선 (동시처리 기준)
응답 시간6초대 → 2~3초
허브 연동사내 서비스 3곳 이상이 공통 AI 백엔드로 사용
본문에 등장한 정량 값 정리. H100 4장·DGX Spark 2대에 모델별로 나눠 올린 자체 호스팅 환경에서, 제가 담당한 텍스트 추론 서버 전환 전후를 비교한 값입니다.

외부 API에 흩어져 있던 비용·지연을 한 허브로 모아, 텍스트·이미지·음성을 더 낮은 비용과 지연으로 제공하게 됐다는 게 가장 큰 변화였습니다.

배운 것과 남은 과제 — 인프라에선 직관보다 데이터였습니다

가장 크게 남은 건 측정하고 나서야 병목이 보였다는 경험이었습니다. "느리다"를 감으로 두었을 때는 더 큰 GPU나 더 좋은 모델을 떠올렸지만, 동시 처리량과 응답 시간으로 다시 보니 진짜 병목은 추론 서버의 동시성 처리 방식이었습니다. 인프라에서 직관보다 데이터로 판단한다는 감각을 이 프로젝트에서 처음 체감했습니다.

물론 끝난 이야기는 아닙니다. 양자화로 내준 품질을 어디까지 허용할지, 텍스트·이미지·음성 모델들이 같은 GPU 자원을 두고 경합할 때 어떻게 나눌지, 지금 고른 모델이 시간이 지나도 여전히 최선인지 — 재고 다시 정해야 할 질문들이 남아 있습니다.

저는 이 일을 거치며 느리다는 감을 숫자로 바꾸고 병목부터 손대는 순서를 제 일하는 방식으로 익혔습니다. 다음 병목을 만나도 먼저 측정하고, 그다음 바꾸는 방식으로 접근하려 합니다.

전체 프로젝트연락하기