이벤트 기반 아키텍처로 AI 워크로드를 처리하는 패턴 — 메시지 큐, 스트리밍 처리, 비동기 추론, 그리고 실시간 AI 파이프라인 설계를 다룹니다.
전통적인 동기 요청-응답 패턴은 처리 시간이 예측 가능할 때 잘 작동합니다. 그러나 LLM 추론은 본질적으로 다른 특성을 가집니다. 같은 모델이라도 입력과 출력 길이에 따라 응답 시간이 수백 밀리초에서 수십 초까지 달라지고, GPU 리소스를 장시간 점유하며, 문서 분석이나 에이전트 워크플로우는 수 분 이상 걸릴 수 있습니다.
이벤트 드리븐 아키텍처(Event-Driven Architecture)는 이러한 특성에 자연스럽게 대응합니다. 요청을 큐에 넣고, 워커가 비동기적으로 처리하며, 결과를 이벤트로 전파하는 구조입니다.
가장 기본적인 패턴은 요청을 큐에 넣고 워커 풀이 순차적으로 처리하는 구조입니다.
요청 흐름은 다음과 같습니다. 클라이언트가 API에 AI 작업을 요청하면, API 서버는 작업 ID를 즉시 반환하고 작업을 큐에 등록합니다. 워커가 큐에서 작업을 꺼내 LLM을 호출하고, 결과를 저장소에 기록한 뒤 웹훅으로 클라이언트에 알립니다.
from fastapi import FastAPI
from pydantic import BaseModel
import uuid
import redis.asyncio as redis
app = FastAPI()
redis_client = redis.from_url("redis://localhost:6379")
class InferenceRequest(BaseModel):
prompt: str
model: str = "claude-sonnet-4-20250514"
callback_url: str | None = None
priority: int = 5 # 1(최고) ~ 10(최저)
@app.post("/v1/inference")
async def submit_inference(request: InferenceRequest):
job_id = str(uuid.uuid4())
await redis_client.zadd("queue", {job_id: request.priority})
await redis_client.hset(f"job:{job_id}", mapping={
"prompt": request.prompt,
"model": request.model,
"status": "queued",
})
queue_length = await redis_client.zcard("queue")
return {"job_id": job_id, "estimated_wait": queue_length * 3}프로덕션 환경에서는 Redis 기반 직접 구현보다 Celery, Dramatiq, AWS SQS + Lambda 같은 검증된 도구를 사용하는 것이 안정적입니다. 위 코드는 개념 이해를 위한 단순화된 버전입니다.
사용자 대면 애플리케이션에서는 토큰이 생성되는 즉시 전달하는 스트리밍(Streaming)이 핵심입니다. 체감 지연 시간을 크게 줄여줍니다.
Server-Sent Events(SSE)를 사용하면 HTTP 연결 하나로 서버에서 클라이언트로 실시간 데이터를 스트리밍할 수 있습니다. text/event-stream 미디어 타입으로 응답하며, 각 토큰 청크를 data: 접두사와 함께 전송합니다.
이벤트 드리븐 패턴이 가장 효과적인 실무 시나리오 중 하나가 문서 처리 파이프라인입니다.
각 단계가 이벤트로 연결되어 있으므로 독립적으로 확장할 수 있습니다. 임베딩 생성이 병목이면 해당 워커만 증설하고, 메타데이터 추출이 실패해도 임베딩 생성은 계속됩니다. 특정 단계의 이벤트를 다시 발행하면 해당 단계부터 재처리할 수 있습니다.
AI 워크로드에서 백프레셔(Backpressure) 관리는 특히 중요합니다. LLM 프로바이더의 요율 제한, GPU 리소스 제한, 비용 제한을 모두 고려해야 합니다.
핵심 제어 파라미터는 세 가지입니다. 동시 처리 최대 수(세마포어로 제한), 분당 최대 요청 수(요율 제한), 일일 비용 한도(비용 제어)입니다. 비용 한도를 초과하면 작업을 데드 레터 큐로 이동시키고, 요율 제한에 근접하면 큐에 지연을 추가합니다.
백프레셔 설정이 너무 공격적이면 처리량이 떨어지고, 너무 느슨하면 비용 폭증이나 요율 제한 오류가 발생합니다. 보수적으로 시작하고 모니터링 데이터를 기반으로 점진적으로 조정하십시오.
모든 AI 작업이 동일한 우선순위를 가지지는 않습니다.
| 우선순위 | 작업 유형 | 목표 지연 |
|---|---|---|
| P1 (최고) | 실시간 채팅 응답 | 1초 이내 |
| P2 | 사용자 대면 비동기 작업 | 30초 이내 |
| P3 | 내부 분석/리포트 | 5분 이내 |
| P4 (최저) | 배치 처리, 재인덱싱 | 수 시간 |
Redis의 Sorted Set, AWS SQS의 우선순위 큐, 또는 Kafka의 토픽 분리로 구현할 수 있습니다. 높은 우선순위 작업이 항상 먼저 처리되도록 보장하면서, 낮은 우선순위 작업이 무한히 대기하지 않도록 에이징(Aging)을 적용하는 것이 핵심입니다.
| 구성 요소 | 소규모 | 중규모 | 대규모 |
|---|---|---|---|
| 메시지 큐 | Redis Streams | RabbitMQ / SQS | Apache Kafka |
| 워커 | asyncio + Redis | Celery / Dramatiq | Flink / Spark |
| 결과 저장 | Redis | PostgreSQL | S3 + DynamoDB |
| 오케스트레이션 | 직접 구현 | Temporal | Temporal / Airflow |
가장 단순한 구성(Redis + asyncio)으로 시작하고, 처리량과 안정성 요구가 증가하면 점진적으로 상위 도구로 마이그레이션하십시오. 초기부터 Kafka를 도입하면 운영 복잡도가 불필요하게 높아집니다.
5장에서는 AI 시스템의 캐싱 전략을 다룹니다. LLM 추론의 높은 비용과 지연을 줄이기 위한 다층 캐싱 — 정확 매칭 캐시, 의미론적 캐시, 프롬프트 캐시, KV 캐시 등 — 의 설계와 구현을 살펴보겠습니다.
이 글이 도움이 되셨나요?
관련 주제 더 보기
LLM 추론 비용과 지연을 줄이는 다층 캐싱 전략 — 의미론적 캐시, 프롬프트 캐시, KV 캐시, 임베딩 캐시, 그리고 캐시 무효화 전략을 다룹니다.
LLM을 시스템에 통합하는 핵심 아키텍처 패턴 — Gateway 패턴, Router 패턴, Chain 패턴, Orchestrator 패턴, 그리고 RAG 아키텍처의 설계를 다룹니다.
LLM API 비용을 제어하는 아키텍처 전략 — 토큰 예산 시스템, 모델 라우팅, 캐싱 경제학, 비용 모니터링, 그리고 비용 효율적 시스템 설계를 다룹니다.