캐싱을 넘어선 Redis: Pub/Sub, Preflighting, 그리고 실시간 AI 에이전트
요약
AI 에이전트의 느린 응답 속도 문제를 해결하기 위해 Redis를 활용한 아키텍처 설계 방안을 다룹니다. MCP(Model Context Protocol) 기반 시스템에서 Pub/Sub, 실행 결과 캐시, 권한 프리플라이트 캐시를 통해 사용자 경험을 개선하는 방법을 설명합니다.
핵심 포인트
- AI 에이전트의 비동기적 지연 시간을 Redis로 최적화
- Pub/Sub을 활용한 실시간 진행 상황 스트리밍 구현
- 실행 결과 캐시를 통한 업스트림 데이터 계층 보호
- 권한 프리플라이트 캐시로 사용자별 권한 확인 속도 향상
- MCP 기반 마이크로서비스 아키텍처의 오케스트레이션 전략
AI 에이전트(AI agents)는 본질적으로 느립니다. 사용자가 AI 기반 챗봇에 메시지를 보낼 때, 단순해 보이는 상호작용이라도 내부적으로는 일련의 사건들을 트리거하는 경우가 많습니다. 에이전트는 요청에 대해 추론하고, 필요한 도구(tools)를 식별하며, 백엔드 서비스에 대해 해당 도구들을 실행하고, 응답을 기다린 다음, 최종 답변을 합성합니다. 잘 설계된 시스템에서 이 체인은 여러 마이크로서비스(microservices), LLM 호출, 그리고 외부 API 왕복(round-trips)에 걸쳐 있을 수 있습니다.
이 포스트에서 설명할 챗봇 아키텍처는 AI 에이전트가 백엔드 도구를 동적으로 발견하고 실행할 수 있게 해주는 표준인 모델 컨텍스트 프로토콜(Model Context Protocol, MCP)을 중심으로 구축되었습니다. 백엔드는 네 가지 마이크로서비스로 구성되며, 각 서비스는 대화 지속성(conversation persistence), RAG 기반 추천(RAG-powered recommendations), 도메인 특화 작업(domain-specific operations), 그리고 모든 도구 발견 및 실행을 중개하는 MCP 게이트웨이(MCP gateway)라는 별도의 도메인을 소유합니다.
우리가 계속 직면했던 문제는 명확했습니다. 백엔드는 비동기적(asynchronous)이고 느렸지만, 사용자 경험(user experience)은 빠르고 반응성이 좋아야 한다는 점이었습니다. 에이전트에게 기록을 가져오거나, 양식을 작성하거나, 지식 베이스를 조회하도록 요청하는 사용자는 5초에서 30초 사이의 시간을 기다려야 할 수도 있습니다. 프로덕션 제품에서 그 정도 시간 동안 빈 화면을 보여주는 것은 용납될 수 없습니다.
Redis는 우리에게 세 가지 별개의 문제를 해결해 주었으며, 각 솔루션은 근본적으로 다른 Redis 패턴을 사용했습니다. 이 포스트에서는 세 가지 모두를 살펴봅니다: 실시간 진행 상황 스트리밍을 위한 Pub/Sub 채널, 업스트림 데이터 계층을 보호하기 위한 실행 결과 캐시(execution result cache), 그리고 모든 요청마다 실행될 수 있을 만큼 사용자별 권한 확인을 빠르게 만들기 위한 권한 프리플라이트 캐시(permission preflight cache)입니다.
워크플로 오케스트레이터(Workflow Orchestrator)가 프론트엔드 문제를 일으키는 이유
MCP (Model Context Protocol) 기반 챗봇에서 마이크로서비스 (Microservices)는 의도적으로 상태를 유지하지 않는 Stateless 구조로 설계됩니다. 각 서비스는 자신의 도메인을 소유하며, 그 어떤 서비스도 대화 루프 (Conversation Loop) 자체를 관리하지 않습니다. 그 책임은 워크플로 오케스트레이터 (Workflow Orchestrator)에 있습니다. 사용자가 메시지를 보내면, 오케스트레이터는 이를 수신하여 AI 에이전트 (AI Agent)를 호출하고, 에이전트의 도구 호출 (Tool Call) 요청을 해석하며, 이를 MCP 게이트웨이 (MCP Gateway)로 라우팅하고, 결과를 수집하여 다음 추론 단계 (Reasoning Step)를 위해 에이전트에게 다시 전달합니다. 즉, 오케스트레이터는 전체 멀티턴 (Multi-turn) 상호작용의 지휘자 역할을 합니다.
이러한 설계는 깔끔하며 확장성 (Scalability)이 뛰어납니다. 하지만 프론트엔드 (Frontend)에는 특정한 문제를 야기합니다. 오케스트레이터는 백엔드 흐름을 관리하는 데는 탁월하지만, 사용자 인터페이스 (UI) 입장에서는 본질적으로 불투명 (Opaque)합니다. 오케스트레이터가 도구 호출을 트리거하고, LLM (Large Language Model) 응답을 기다리며, 서비스 간에 결과를 라우팅하느라 바쁜 동안, 프론트엔드는 무슨 일이 일어나고 있는지 전혀 알 수 없습니다. 20~30초 이상 소요되는 멀티턴 양식 채우기 (Form Filling) 워크플로나 심층 지식 베이스 (Knowledge Base) 쿼리의 경우, 빈 화면을 보여주는 것은 허용될 수 없습니다. 이것이 첫 번째 Redis 패턴이 해결하고자 하는 문제입니다.
패턴 1: 실시간 진행 상황 스트리밍을 위한 Redis Pub/Sub
프론트엔드가 엔드포인트 (Endpoint)에 반복적으로
각 채널은 세션 식별자(Session Identifier)와 메시지 식별자(Message Identifier)의 조합으로 키(Key)가 지정되어, 한 사용자의 활성 요청에 대한 진행 상황 업데이트가 다른 사용자의 동시 세션으로부터 완전히 격리되도록 보장합니다. 메시지에는 10분의 TTL(Time To Live)이 적용됩니다. 게시되는 업데이트의 종류는 상위 수준의 오케스트레이션(Orchestration) 신호부터 “Thinking…”, “Verifying answer…”, “Submitting…”과 같은 세밀한 단계별 메시지까지 다양하여, 사용자는 에이전트가 프로세스의 어느 단계에 있는지 정확히 알 수 있습니다.
패턴 2: 도구 실행 결과(Tool Execution Results)를 위한 Redis Key-Value 캐시
에이전트 시스템(Agentic Systems)을 구축할 때 가장 먼저 배우는 교훈 중 하나는 AI 모델이 매우 중복적이라는 점입니다. 에이전트가 다회차 대화(Multi-turn Conversation)를 통해 추론할 때, 동일한 데이터를 여러 번 반복해서 요청하는 경우가 빈번합니다. 이러한 모든 도구 호출(Tool Call)이 상위 GraphQL API에 직접 도달하게 되면, 백엔드는 빠르게 병목 현상(Bottleneck)이 됩니다.
우리는 MCP 도구 호출에 대한 실행 캐시(Execution Cache) 역할을 수행할 전용 Redis 인스턴스를 배포했습니다. API 호출을 캐싱하는 단순한 접근 방식은 요청의 전체 JSON 페이로드(Payload)를 해싱(Hashing)하는 것이지만, AI 도구의 경우 이는 함정이 될 수 있습니다. AI 에이전트는 종종 세션 식별자, 타임스탬프(Timestamp) 또는 UI 표시 설정과 같은 동적인 메타데이터(Metadata)를 도구 호출에 삽입합니다. 만약 전체 페이로드를 해싱한다면 모든 요청이 고유하게 보여 캐시 적중률(Cache Hit Rate)이 크게 떨어지게 됩니다.
대신 우리는 결정론적 캐시 키(Deterministic Cache Key) 전략을 설계했습니다. 도구의 고정된 식별자와 입력 파라미터(Input Parameter)의 엄격하게 필터링된 하위 집합을 결합하여 키를 생성하며, 이때 무관한 메타데이터는 제거하고 핵심 쿼리 인자(Core Query Arguments)에 대해서만 캐싱합니다. 이를 통해 에이전트의 요청 문구가 약간 다르더라도 캐시를 활용할 수 있도록 보장합니다. 우리는 이러한 캐시된 응답에 45분의 TTL을 할당했습니다. 이는 확장된 사용자 세션을 커버하기에는 충분히 길면서도, AI가 오래된 데이터(Stale Data)를 바탕으로 추론하는 것을 방지하기에는 충분히 짧은 시간입니다.
패턴 3: 권한 사전 점검(Permission Preflighting)을 위한 Redis 메모이제이션(Memoization)
MCP 기반 아키텍처에서의 모든 도구 호출(tool call)은 보안 질문을 수반합니다: 이 사용자가 이 데이터에 대해 이 도구를 실행할 권한이 있는가? 다양한 역할과 액세스 수준(access levels)이 존재하는 멀티 테넌트(multi-tenant) 플랫폼에서 이 질문은 생략될 수 없습니다. 하지만 모든 요청마다 이 질문에 단순하게(naively) 답변하는 것은 심각한 성능 문제를 야기합니다. 단일 세션 내에서 이는 수십 번의 사전 점검(preflight checks)을 의미할 수 있으며, 각 점검은 에이전트의 추론 루프(reasoning loop)의 핵심 경로(critical path)에 동기식 HTTP 왕복(round-trip)을 추가하게 됩니다.
해결책은 각 권한 확인(authorization check)의 결과를 캐싱(cache)하는 것입니다. MCP 게이트웨이가 특정 사용자에 대해 특정 도구에 대한 사전 점검을 수행한 후, 그 결과는 30분의 TTL(Time To Live)과 함께 저장됩니다. 해당 시간 동안 동일한 사용자의 동일 도구에 대한 후속 요청은 캐시에서 직접 해결됩니다. 캐시 키(cache key)는 사용자 식별자(user identifier)와 도구 식별자(tool identifier)의 복합체(composite)로 구성되어, 사용자별/도구별 조회를 가능하게 합니다. 도구에만 키를 지정한 포괄적인(blanket) 캐시는 한 사용자의 권한을 다른 사용자에게 잘못 제공할 수 있습니다.
이러한 트레이드오프(tradeoff)는 명시적으로 다룰 가치가 있습니다. 30분의 TTL은 사용자의 권한이 취소되더라도 MCP 게이트웨이가 최대 30분 동안 캐시된 결과를 계속 허용할 수 있음을 의미합니다. 대부분의 엔터프라이즈(enterprise) 사용 사례에서 권한 변경은 즉각적인 전파가 필요하지 않은 관리적 작업이므로 이는 허용 가능한 범위입니다. 하지만 이는 시스템을 운영하는 모든 사람이 문서화하고 이해해야 하는 의도적인 아키텍처 결정(architectural decision)입니다.
결론
Redis는 단 하나의 도구가 아닙니다. Redis는 여러 개의 뚜렷한 동작 모드(operating modes)를 가진 런타임(runtime)이며, 이를 가장 잘 활용하는 팀은 각 모드를 일급 아키텍처 결정(first class architectural decision)으로 취급하는 팀입니다. 이 시스템에서 Pub/Sub은 프론트엔드 가시성 격차를 해결했고, 키-값 캐싱(key-value caching)은 업스트림 데이터 계층을 보호했으며, 메모이제이션(memoization)은 대규모 환경에서 요청당 보안 강제(per-request security enforcement)를 가능하게 했습니다.
이 세 가지 용도를 별도의 인스턴스(instance)로 유지하기로 한 결정은 의도적인 것이었습니다. 각 용도는 서로 다른 TTL(Time To Live) 요구사항, 서로 다른 데이터 삭제 정책(eviction policy) 필요성, 그리고 서로 다른 장애 허용 범위(failure tolerance)를 가집니다. Pub/Sub 인스턴스가 다운되는 것은 사용자 경험(UX) 문제이며, 실행 캐시(execution cache)가 다운되는 것은 성능 문제이고, 권한 캐시(permission cache)가 다운되는 것은 보안 문제입니다. 이들을 단일 인스턴스로 통합하는 것은 한 도메인의 잘못 설정된 삭제 정책이 다른 도메인의 동작을 소리 없이 손상시킬 수 있음을 의미합니다.
만약 여러분이 MCP 또는 이와 유사한 도구 호출(tool-calling) 아키텍처를 기반으로 에이전트 시스템(agentic systems)을 구축하고 있다면, 각 문제를 독립적으로 모델링하고, 그에 맞는 적절한 Redis 패턴을 선택하며, 인스턴스를 분리하여 유지하십시오.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기