AI 모델을 학습하고 나면 “어떻게 서빙할 것인가”라는 질문이 반드시 따라옵니다. 실시간으로 응답해야 하는 챗봇과, 밤새 수백만 건의 상품 추천을 계산해야 하는 배치 잡은 근본적으로 다른 인프라를 요구합니다. 잘못된 선택은 GPU 비용을 수 배로 불리거나, 사용자 경험을 망가뜨리는 지연을 만들어냅니다. 이 글에서는 온라인 추론과 배치 추론의 구조적 차이, 결정 기준, 그리고 실무에서 자주 쓰는 하이브리드 패턴까지 체계적으로 다룹니다.
온라인 추론 vs 배치 추론: 핵심 차이
두 방식의 가장 근본적인 차이는 요청이 언제 도착하고, 결과를 언제 소비하느냐입니다. 온라인 추론은 요청이 들어오는 즉시 모델을 실행하고 수십~수백 ms 내에 응답을 반환합니다. 배치 추론은 요청을 일정 기간 모아두었다가 한꺼번에 처리하며, 결과를 데이터베이스나 파일 시스템에 저장해 놓습니다.
| 구분 | 온라인 추론 (Online Inference) | 배치 추론 (Batch Inference) |
|---|---|---|
| 지연 시간(Latency) | p99 < 200ms 목표 | 분~시간 단위 허용 |
| 처리량(Throughput) | 수백 RPS (스케일아웃으로 확장) | GPU 포화 상태로 수십~수백만 건/시간 |
| 인프라 형태 | REST/gRPC API 서버, 오토스케일링 | Spark, Ray, Airflow DAG, Kubernetes Job |
| GPU 활용률 | 유휴 시간 多 (트래픽 변동) | 90%+ 포화 가능 |
| 비용 패턴 | 트래픽 비례 (idle 비용 발생) | 실행 시간 비례 (spot 인스턴스 적합) |
| 대표 사례 | 챗봇, 실시간 사기 감지, 이미지 검색 | 야간 추천 계산, 전체 고객 리스크 스코어링 |
| 모델 업데이트 | 블루/그린 배포, 카나리 필요 | 잡 실행 전 새 모델 버전 지정으로 충분 |
지연 시간과 처리량의 트레이드오프
온라인 추론에서 지연 시간(latency)은 SLO(Service Level Objective)의 핵심 지표입니다. 일반적으로 p50/p95/p99 기준으로 측정하며, 사용자가 체감하는 것은 p99입니다. 모델이 아무리 빠르더라도 네트워크 왕복(RTT), 직렬화/역직렬화, 큐 대기 시간이 누적됩니다. NVIDIA Triton Inference Server를 예로 들면, TensorRT로 최적화한 ResNet-50은 배치 사이즈 1 기준 A100에서 약 1~2ms 내외이지만, 실제 서비스 레이턴시는 네트워크·로드밸런서 오버헤드를 더해 10~30ms 수준이 됩니다.
배치 추론에서는 처리량(throughput)이 핵심입니다. 배치 사이즈를 키울수록 GPU 메모리 대역폭 활용률이 높아져 건당 처리 비용이 낮아집니다. LLM 추론의 경우 배치 사이즈 1 대비 배치 사이즈 32에서 토큰당 처리 비용이 10배 이상 개선되는 사례도 있습니다. 단, 배치 사이즈를 무한히 늘리면 OOM(Out of Memory)이 발생하므로 GPU 메모리 용량과 시퀀스 길이를 고려한 동적 배치 크기 결정이 필요합니다.
# Triton Dynamic Batching 설정 예시 (config.pbtxt)
dynamic_batching {
preferred_batch_size: [8, 16, 32]
max_queue_delay_microseconds: 5000 # 5ms 이내 대기 후 배치 실행
default_queue_policy {
timeout_action: DELAY
default_timeout_microseconds: 10000
allow_timeout_override: true
max_queue_size: 256
}
}
instance_group [
{
count: 2
kind: KIND_GPU
gpus: [0, 1]
}
]
큐 기반 비동기 추론: 두 세계의 중간 지점
모든 요청이 즉각적인 응답을 필요로 하지는 않습니다. 문서 요약, 대용량 이미지 분석, 리포트 생성처럼 “요청은 지금 하지만 결과는 나중에 받아도 되는” 유스케이스가 많습니다. 이 경우 큐 기반 비동기 추론(Queue-based Async Inference) 패턴이 효과적입니다.
클라이언트가 요청을 제출하면 즉시 작업 ID를 반환받습니다. 워커 풀이 큐에서 작업을 꺼내 추론을 실행하고, 완료 시 결과를 저장소(Redis, S3, DB)에 기록합니다. 클라이언트는 폴링(polling)이나 웹훅(webhook)으로 결과를 수신합니다. 이 패턴은 트래픽 스파이크를 큐로 흡수하므로 온라인 추론처럼 공격적인 오토스케일링 없이도 안정적입니다.
# Celery + Redis 기반 비동기 추론 워커 예시
from celery import Celery
import torch
app = Celery("inference", broker="redis://localhost:6379/0",
backend="redis://localhost:6379/1")
# 워커 시작 시 모델 1회 로드 (요청마다 로드하지 않음)
model = None
@app.on_after_configure.connect
def load_model(sender, **kwargs):
global model
model = torch.jit.load("model.pt").cuda().eval()
@app.task(bind=True, max_retries=3)
def run_inference(self, input_data: list) -> dict:
try:
tensor = torch.tensor(input_data).cuda()
with torch.no_grad():
output = model(tensor)
return {"result": output.cpu().tolist(), "status": "success"}
except Exception as exc:
raise self.retry(exc=exc, countdown=2 ** self.request.retries)
언제 무엇을 선택할 것인가: 결정 트리
서빙 방식을 결정할 때 다음 순서로 질문하세요.
- 사용자가 결과를 실시간으로 기다리는가? — Yes이면 온라인 추론. 챗봇, 실시간 번역, API 응답이 여기 해당합니다.
- 결과를 수 분 내에 받아도 무방한가? — Yes이면 큐 기반 비동기 추론을 검토하세요. 문서 분석, 긴 영상 처리 등이 해당합니다.
- 대상 데이터가 사전에 정해져 있고 주기적으로 갱신되는가? — Yes이면 배치 추론이 최적입니다. 전체 사용자 추천 갱신, 주간 리스크 스코어링 등이 해당합니다.
- 트래픽이 예측 불가능하게 스파이크하는가? — 온라인 추론이라면 HPA(Horizontal Pod Autoscaler) + GPU 노드 프로비저닝 전략이 필요합니다. 배치라면 큐 깊이 기반 워커 오토스케일링으로 대응합니다.
- 비용이 최우선 제약인가? — 배치 추론을 Spot/Preemptible 인스턴스로 운영하면 온라인 추론 대비 60~80% 비용 절감이 가능합니다.
대부분의 성숙한 ML 플랫폼은 두 방식을 혼합합니다. 예를 들어, 추천 시스템은 배치로 후보군 수백 개를 미리 계산해 캐시에 저장하고, 실시간 요청 시에는 캐시된 후보를 가져와 경량 랭킹 모델로 온라인 추론합니다. 이를 Two-stage Retrieval + Ranking 패턴이라 합니다.
실전 팁: 서빙 인프라 선택과 운영
- 모델 최적화 우선: 인프라 투자 전에 TensorRT, ONNX Runtime, 양자화(INT8/FP16)로 모델 자체 레이턴시를 줄이세요. 같은 GPU에서 2~4배 처리량 향상이 가능합니다.
- 웜업(Warm-up): 온라인 추론 서버는 콜드 스타트 시 첫 요청 레이턴시가 급등합니다. 배포 후 더미 요청으로 JIT 컴파일과 CUDA 커널을 워밍업하세요.
- 배치 추론 재시작 전략: Spark나 Ray 잡이 중간에 실패하면 처음부터 재실행하는 대신, 체크포인트(처리 완료된 파티션 추적)를 활용해 이어서 실행하세요.
- 레이턴시 SLO 설정: p50이 아닌 p99, p999 기준으로 SLO를 정의하세요. 꼬리 레이턴시(tail latency)가 사용자 경험에 직결됩니다.
- 비용 모니터링: GPU 시간당 처리 건수(throughput/GPU-hour)를 핵심 비용 효율 지표로 추적하세요. 모델 업데이트나 배치 사이즈 변경 후 이 지표를 반드시 확인합니다.
마무리
온라인 추론과 배치 추론은 우열의 문제가 아니라 사용 맥락의 문제입니다. 실시간 사용자 응답이 필요하면 온라인, 대량 데이터를 경제적으로 처리해야 하면 배치, 그 중간 어딘가라면 큐 기반 비동기가 답입니다. 대부분의 프로덕션 ML 시스템은 세 방식을 모두 사용하며, 각 방식의 트레이드오프를 명확히 이해해야 비용과 성능 두 마리 토끼를 잡을 수 있습니다. 초기에 온라인으로 시작했더라도, 데이터 규모와 비용이 증가하면 배치나 하이브리드로 전환하는 것을 두려워하지 마세요.
자주 묻는 질문
Q. 온라인 추론 서버가 트래픽 스파이크를 버티지 못할 때 어떻게 해야 하나요?
A. 단기적으로는 요청 큐잉과 타임아웃 정책을 강화하고, 중기적으로는 HPA + Karpenter(AWS) 또는 GKE Node Auto Provisioner로 GPU 노드를 동적 확장하세요. 근본적으로는 요청 중 일부를 큐 기반 비동기로 전환하거나, 캐시 가능한 추론 결과를 Redis에 저장해 모델 호출 자체를 줄이는 것이 가장 효과적입니다.
Q. 배치 추론에서 Spot 인스턴스를 쓸 때 인터럽트가 발생하면 어떻게 되나요?
A. Spot 인터럽트는 약 2분 전에 통보됩니다. Ray나 Spark는 체크포인트 기반 재실행을 지원하므로, 처리 단위(파티션, 미니배치)를 작게 유지하고 중간 결과를 주기적으로 영구 스토리지(S3, GCS)에 기록하면 인터럽트 후 재개 시 손실을 최소화할 수 있습니다. AWS SageMaker Managed Spot Training은 이 패턴을 자동화해 줍니다.
Q. LLM(대형 언어 모델) 서빙은 온라인과 배치 중 어느 쪽이 적합한가요?
A. 용도에 따라 다릅니다. 챗봇·코파일럿은 스트리밍 온라인 추론(vLLM, TGI)이 필수입니다. 대량 문서 요약, 데이터 라벨링, 오프라인 평가는 배치가 훨씬 경제적입니다. vLLM의 continuous batching 기능은 온라인 환경에서도 배치 효율을 가져올 수 있어, 두 방식의 장점을 절충하는 현실적인 선택지입니다.