
AI 시대의 메시징 (Messaging in the Age of AI)
요약
AI 에이전트의 등장으로 인해 기존의 결정론적 메시징 인프라가 직면한 변화를 분석합니다. 메시지가 단순 데이터를 넘어 추론 컨텍스트로 진화함에 따라 발생하는 페이로드 크기, 볼륨 변동성, 비결정론적 특성에 대응하는 새로운 패턴을 다룹니다.
핵심 포인트
- 메시지가 단순 데이터에서 추론 컨텍스트로 진화
- 대규모 토큰을 포함한 페이로드 크기 급증
- 멀티 에이전트 상호작용으로 인한 폭발적인 메시지 볼륨
- 재시도 시 결과가 달라지는 비결정론적 멱등성 문제
메시징 인프라(Messaging infrastructure)는 지난 10년 동안 지루할 정도로 안정적이었습니다. 큐(Queues), 토픽(Topics), 익스체인지(Exchanges)와 같은 기본 요소(Primitives)들이 자리를 잡았습니다. 그러다 AI 에이전트(AI agents)가 등장했고, 메시징을 지루하게 만들었던 기존의 가정들이 갑자기 더 이상 유효하지 않게 되었습니다. 메시지는 더 이상 단순한 데이터가 아닙니다. 그것은 컨텍스트(Context)입니다. 에이전트는 당신의 메시지를 읽고, 그에 대해 추론(Reasoning)하며, 그로 인해 도구(Tools)를 호출하고, 큐에 넣는 시점(Enqueue time)에는 토큰 수(Token count)를 예측할 수 없는 응답을 생성할 것입니다. 결정론적(Deterministic) 서비스에 잘 작동했던 전송 계층(Transport layer)은 교체가 아닌, 재고려 및 적응이 필요합니다.
이 글은 어떤 메시지 브로커(Message broker)를 선택할지에 관한 것이 아닙니다. 프로듀서(Producer)와 컨슈머(Consumer)가 모두 잠재적으로 비결정론적(Non-deterministic)인 추론 시스템일 때 무엇이 변하는지, 그리고 실제 운영 환경(Production)에서 어떤 패턴이 유효한지에 관한 것입니다. 예제는 Spring Boot와 Apache Kafka를 사용하는데, 이는 제가 대규모 환경에서 작동하는 것을 본 스택(Stack)이기 때문이지만, 이 패턴들은 모든 스택에 적용됩니다.
1. 왜 AI가 메시징을 변화시키는가
전통적인 메시징은 구조화되고 경계가 정해진 페이로드(Payloads)를 전달합니다. 주문 완료(Order-placed) 이벤트는 주문 ID, 고객 ID, 품목(Line items), 합계와 같이 알려진 형태를 가집니다. 결제 확인(Payment-confirmed) 이벤트는 트랜잭션 참조(Transaction reference)를 포함합니다. 이러한 메시지들은 크기가 작고(수백 바이트), 볼륨을 예측할 수 있으며, 설계 단계부터 멱등성(Idempotency)을 갖추고 있습니다. 즉, 동일한 주문 이벤트를 재처리해도 동일한 결과를 얻습니다.
AI에서 비롯된 메시지는 이 세 가지 가정을 모두 깨뜨립니다. 단일 에이전트 간(Agent-to-agent) 메시지는 100K 토큰의 컨텍스트 윈도우(Context window)를 담을 수 있으며, 이는 사실상 작은 소설 한 권 분량의 추론 상태(Reasoning state)와 같습니다. 볼륨은 사용자 활동과 상관관계가 없는 방식으로 폭발적(Bursty)으로 발생합니다. 예를 들어, 멀티 에이전트 합의 라운드(Multi-agent consensus round)는 단일 사용자 요청에 대해 50개의 내부 메시지를 생성할 수 있습니다. 그리고 멱등성은 더 이상 거저 얻어지는 것이 아닙니다. 왜냐하면 동일한 논리적 입력이라도 재시도(Retry)할 때마다 서로 다른 추론 경로(Reasoning paths)를 생성할 수 있기 때문입니다.
여기서 핵심적인 고려 사항은 AI 시스템을 위한 메시징이 "이 페이로드(Payload)를 안정적으로 전달하라"에서 "추론 컨텍스트(Reasoning context)를 대규모로 관리하라"로 전환된다는 점입니다. 신뢰성(Reliability)은 여전히 중요하며, 이전보다 더욱 중요해졌습니다. 하지만 기존의 메시징에서는 다룰 필요가 없었던 문제들, 즉 토큰 예산(Token budgets), 모델 지연 시간 변동성(Model latency variance), 그리고 추론 트레이스 무결성(Reasoning trace integrity)과 같은 우려 사항들이 결합되었습니다.
전통적인 모델에서 각 화살표는 경계가 정해져 있고 스키마 검증(Schema-validated)을 거친 메시지입니다. 반면 AI 모델에서 Planner(계획기)에서 Executor(실행기)로 향하는 화살표는 전체 추론 상태(Reasoning state)를 전달하며, 그 화살표에는 토큰으로 측정되는 비용이 발생합니다. 메시징 계층은 이 사실을 반드시 인지해야 합니다.
2. 에이전트에 의해 생성되는 새로운 워크로드 (New Workloads Created by Agents)
에이전트는 기존의 메시징 인프라가 설계된 방식과는 전혀 다른 트래픽 패턴을 생성합니다. 각각의 워크로드가 시스템의 서로 다른 부분을 압박하기 때문에, 새로운 워크로드들을 명시적으로 분류해 두는 것이 가치가 있습니다.
계획 출력물 (Planning outputs). 에이전트가 행동하기 전에 생각하며, 이 사고 과정은 구조화된 출력(Structured output)을 생성합니다. Planner 에이전트는 하위 에이전트들이 소비할 계획 객체(목표, 하위 목표, 제약 조건, 할당된 에이전트 등)를 방출합니다. 이러한 메시지는 중간 크기(2-8K 토큰)이며 시스템 내에서 가장 영향력이 큰 메시지입니다. 계획이 잘못되면 하위의 모든 과정에서 토큰을 낭비하게 됩니다.
도구 호출 결과 (Tool-call results). 에이전트가 도구(데이터베이스 쿼리, API 호출, 코드 실행 등)를 호출하면, 그 결과는 메시징 패브릭(Messaging fabric)에 일급 메시지(First-class message)로 진입합니다. 이러한 결과물은 크기를 예측할 수 없으며(SQL 쿼리는 한 행을 반환할 수도, 백만 행을 반환할 수도 있음), 컨텍스트 윈도우(Context window)를 초과하기 전에 청킹(Chunking)되거나 요약되거나 혹은 거부되어야 합니다.
사고의 사슬 (Chain-of-thought) 흔적. 일부 아키텍처는 에이전트의 추론 흔적(reasoning trace)이 스트리밍되는 동안 이를 유지합니다. 이는 단순히 디버깅을 위한 것이 아니라, 다른 에이전트와 공유되는 컨텍스트(context)로서의 역할을 합니다. 추론 흔적은 설계상 매우 장황합니다. 이를 메시지로 저장하고 전달하려면 단순한 로그 라인(log line)이 아닌 구조화된 산출물(structured artifact)로 취급해야 합니다.
멀티 에이전트 브로드캐스트 및 합의 (Multi-agent broadcast and consensus). 에이전트들은 종종 어떤 계획을 실행할지, 도구 호출(tool call) 결과가 유효한지, 응답이 정책을 준수하는지 등에 대해 합의에 도달해야 합니다. 이러한 합의 라운드는 팬아웃(fan-out) 메시지 폭주를 발생시킵니다. 한 에이전트가 제안을 게시하면, N개의 에이전트가 투표나 비판으로 응답합니다. 메시징 레이어는 전통적인 시스템이 하나의 메시지를 볼 곳에서 N+1개의 메시지를 보게 됩니다.
실제로 이는 여러분의 메시징 시스템이 5자릿수(5 orders of magnitude)에 달하는 메시지 크기(바이트에서 메가바이트까지), 일간 또는 주간 패턴을 따르지 않는 트래픽 폭주, 그리고 단일 메시지를 처리하는 데 수 초 또는 수 분이 걸릴 수 있으며 결과가 불확실할 경우 공격적으로 재시도(retry)하는 컨슈머(consumer)를 처리할 수 있어야 함을 의미합니다.
3. 실제로 작동하는 메시징 아키텍처 패턴
여러 팀의 프로덕션 환경에서 에이전트 시스템을 관찰한 결과, 일련의 패턴이 구체화되었습니다. 이는 추측이 아닙니다. 팀들이 첫 번째 프로덕션 장애를 겪은 후 결국 구축하게 되는 것들입니다.
패턴 1: 메시지 엔벨로프 (The Message Envelope)
AI 시스템의 모든 메시지는 상관관계 ID(correlation ID) 이상의 메타데이터를 반드시 포함해야 합니다. 엔벨로프(envelope)에는 페이로드(payload)의 토큰 수, 이를 생성한 모델, 트레이스 ID(trace ID), 송신자 유형(인간, 에이전트, 도구), 그리고 송신자가 에이전트인 경우 멱등성 키(idempotency key)가 포함되어야 합니다. 컨슈머는 페이로드 본문을 파싱하지 않고도 이 메타데이터를 사용하여 라우팅, 할당량(quota), 중복 제거(deduplication) 결정을 내립니다.
동반 프로젝트(companion project)는 이를 Java record로 구현합니다 — code/src/main/java/com/messaging/relay/model/MessageEnvelope.java를 참조하세요:
public record MessageEnvelope<T>(
String messageId,
String traceId,
...
패턴 2: 트래픽 레인 분리 (Separate Traffic Lanes)
사람 대 에이전트(Human-to-agent), 에이전트 대 에이전트(agent-to-agent), 에이전트 대 도구(agent-to-tool) 트래픽은 서로 다른 지연 시간 허용치(latency tolerances), 토큰 프로필(token profiles), 장애 모드(failure modes)를 가집니다. 이들을 별도의 Kafka 토픽에 배치하면 서로 다른 보관 정책(retention policies), 압축 전략(compaction strategies), 컨슈머 그룹 확장(consumer group scaling)을 독립적으로 적용할 수 있습니다. 관측성 에이전트(observability agent)는 운영 컨슈머(operational consumers)와 경쟁하지 않고 세 가지 토픽 모두에서 데이터를 소비할 수 있습니다.
패턴 3: 에이전트 트래픽을 위한 멱등성 키 (Idempotency Keys for Agent Traffic)
에이전트는 재시도(retry)를 합니다. 이는 에이전트 설계의 본질적인 특성입니다. 추론 단계(reasoning step)에서 낮은 신뢰도가 발생하면 에이전트는 이를 재실행합니다. 메시징 계층에 멱등성 키(idempotency keys)가 없다면, 모든 재시도는 새로운 트랜잭션이 되어 작업을 중복시키고 비용을 부풀리게 됩니다. 이 패턴은 간단합니다. 프로듀서(producer)가 논리적 작업에서 유도된 키(예: plan-{conversationId}-{stepNumber})를 설정하면, 컨슈머(consumer)가 설정 가능한 윈도우(window) 내에서 중복을 제거(deduplicate)합니다. Kafka의 로그 압축(log compaction)이 도움이 될 수 있지만, 에이전트 워크로드의 경우 재시도 의미론(retry semantics)이 Kafka 관점의 엄격한 '정확히 한 번(exactly-once)' 방식이 아니기 때문에 애플리케이션 계층의 중복 제거가 더 신뢰할 수 있습니다.
패턴 4: 청크 단위 컨텍스트 전달 (Chunked Context Delivery)
100K 토큰 규모의 컨텍스트 윈도우(context window)를 단일 Kafka 메시지로 보내지 마세요. 이를 요약(summary), 관련 히스토리(relevant history), 도구 출력(tool outputs), 추론 상태(reasoning state)와 같이 각각 고유한 엔벨로프 메타데이터(envelope metadata)를 가진 청크(chunks)로 나누십시오. 그러면 컨슈머는 관련성(relevance), 최신성(recency), 토큰 예산(token budget)을 기반으로 어떤 청크를 모델의 컨텍스트 윈도우에 로드할지 결정할 수 있습니다. 이를 통해 컨텍스트 조립(context assembly)은 프로듀서 측의 추측에서 컨슈머 측의 결정으로 전환됩니다.
동반 프로젝트의 ContextChunker(code/src/main/java/com/messaging/relay/chunking/ContextChunker.java 참조)는 설정 가능한 maxChunkTokens 임계값에 따라 콘텐츠를 분할합니다. KafkaConfig(code/src/main/java/com/messaging/relay/config/KafkaConfig.java 참조)는 각 레인(lane)별 보관 정책(retention policies)을 가진 4개 토픽 토폴로지(topology)를 정의합니다. 구체적으로는 휴먼 트래픽(human traffic)은 7일, 에이전트 트래픽(agent traffic, 감사 추적용)은 30일, 도구 호출(tool calls)은 압축(compaction)을 포함하여 3일, 그리고 데드 레터 토픽(dead letter topic)은 90일입니다.
4. 토큰 제한(Token Limits), 속도 제한(Rate Limits) 및 할당량 관리(Quota Management)
모든 요청의 비용이 거의 동일했을 때는 요청 횟수 기반의 속도 제한(Rate limiting)이 합리적이었습니다. 하지만 AI 시스템은 동일하게 "한 번의 요청"인 두 메시지를 받을 수 있는데, 하나는 $0.002가 들고 다른 하나는 $0.30가 들 수 있습니다. 이에 대한 해결책은 토큰 인식 속도 제한(token-aware rate limiting)입니다.
메커니즘은 간단합니다. 메시지를 Kafka에 인큐잉(enqueuing)하기 전에, 모델이 사용할 것과 동일한 토크나이저(tokenizer)를 사용하여 토큰 수를 계산합니다. 속도 제한을 분당 요청 횟수(requests-per-minute)가 아닌 분당 토큰 수(tokens-per-minute)로 적용합니다. 할당량(quota)을 분할하십시오. 70%는 응답성이 확보되어야 하는 휴먼 유래 트래픽(human-originated traffic)을 위해 예약하고, 30%는 지연되거나 성능이 저하되어도 괜찮은 에이전트 간 트래픽(agent-to-agent traffic)을 위해 할당합니다. 특정 파티션의 할당량이 소진되면 백프레셔(backpressure)를 적용합니다. 즉, 프로듀서(producer)에게 속도를 늦추거나, 배치(batch) 처리하거나, 더 저렴한 모델로 성능을 낮추도록(degrade) 신호를 보냅니다.
동반 프로젝트는 이를 TokenAwareRateLimiter(code/src/main/java/com/messaging/relay/ratelimit/TokenAwareRateLimiter.java 참조)에서 구현합니다:
public RateLimitDecision check(String serializedPayload, MessageEnvelope<?> envelope) {
int tokenCount = countTokens(serializedPayload);
SenderType senderType = envelope.senderType();
...
QuotaManager는 매분 초기화되는 레인별 슬라이딩 윈도우(sliding windows)를 유지하며, 설정 가능한 제한을 가집니다. 기본값은 휴먼 트래픽의 경우 분당 600K 토큰, 에이전트는 200K 토큰, 도구 호출은 100K 토큰입니다.
여기서 핵심적인 고려 사항은 AI 시스템에서의 속도 제한 (Rate Limiting)이 단순히 인프라를 보호하기 위한 것만이 아니라는 점입니다. 이는 비용 제어 (Cost Control)에 관한 것입니다. 수렴하기 전까지 50번을 재시도하는 폭주하는 에이전트 루프 (Agent Loop)가 갑작스러운 15달러의 비용을 발생시켜서는 안 됩니다. 메시징 레이어 (Messaging Layer)는 에이전트의 재시도 충동과 모델 제공업체의 미터링 엔드포인트 (Metering Endpoint) 사이에 위치하므로, 이를 강제하기에 가장 적합한 장소입니다.
5. 관찰 가능성 (Observability), 감사 (Auditing), 그리고 운영 안전성 (Operational Safety)
AI 메시징을 위한 관찰 가능성 (Observability)은 APM (Application Performance Monitoring)의 확장판이 아닙니다. APM은 토픽 (Topic)이 정체되어 있는지 여부를 알려줍니다. 반면 AI 메시징 관찰 가능성은 그곳을 통해 흐르는 메시지들이 정확하고, 안전하며, 비용 효율적인 결과를 생성하고 있는지를 알려줍니다. 이는 서로 다른 질문이며, 서로 다른 계측 (Instrumentation)을 필요로 합니다.
메시지당 로그에 기록해야 할 항목
시스템을 통과하는 모든 메시지는 구조화된 로그 항목을 포함해야 합니다. 이는 사후 고려 사항이 아니라 메시징 파이프라인 (Messaging Pipeline)의 일급 시민 (First-class part)으로서 다뤄져야 합니다. 최소 필드는 다음과 같습니다: traceId, senderType, tokenCount, modelId, latencyMs, retryCount, idempotencyKey, 그리고 blockedCheck (안전 가드레일 (Safety Guardrail)이 메시지를 차단했는지 여부)입니다. 이러한 필드들을 통해 원시 로그 (Raw Logs)로부터 무엇이, 누구에 의해, 어떤 비용으로, 어떤 결과와 함께 전송되었는지 모든 상호작용을 재구성할 수 있습니다.
동반 프로젝트의 ObservabilityFilter (code/src/main/java/com/messaging/relay/observability/ObservabilityFilter.java 참조)는 소비된 메시지당 구조화된 JSON 이벤트를 기록합니다:
public void logConsumption(MessageEnvelope<?> envelope, String topic, long offset) {
Map<String, Object> event = new LinkedHashMap<>();
event.put("trace_id", envelope.traceId());
...
별도의 passesSafetyCheck 메서드가 컨슈머(consumer) 처리 전에 실행되어, 메타데이터(metadata)에서 플래그가 지정된 메시지를 차단합니다. 프로덕션(production) 환경에서는 이를 개인정보(PII) 탐지 및 콘텐츠 정책 평가(content policy evaluation) 기능으로 확장하십시오.
메시지 계보 (Message Lineage)
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기

