MCP 세션 아키텍처: 스티키 서버(Sticky Servers) 없이 에이전트 통합 확장하기
요약
MCP(Model Context Protocol)를 프로덕션 환경에서 확장할 때 발생하는 세션 관리 문제를 다룹니다. 인메모리 상태 저장 방식의 한계를 지적하며, 스티키 세션 없이도 확장 가능한 에이전트 워크플로 아키텍처의 필요성을 설명합니다.
핵심 포인트
- 로컬 환경의 인메모리 세션 방식은 프로덕션 확장 시 병목 현상을 초래함
- 스티키 세션 사용 시 부하 분산 및 오토스케일링 효율 저하 문제 발생
- 워크플로의 상태를 단일 프로세스에 의존하지 않는 설계가 핵심
- MCP를 웹 네이티브한 설계로 전환하여 확장성 확보 필요
AI 에이전트가 실패하는 이유는 데모가 나빠서인 경우가 드뭅니다. 에이전트가 실패하는 시점은 동일한 워크플로(Workflow)가 수많은 사용자, 수많은 도구, 실제 로드 밸런서(Load Balancers) 뒤에서 로그, 재시도(Retries), 인증(Auth), 비용 제한(Cost limits)과 함께 실행되어야 할 때입니다. 바로 이 지점에서 노트북 한 대에서 잘 작동하던 작은 MCP 서버가 프로덕션의 병목 현상(Bottleneck)으로 변할 수 있습니다.
중요한 변화: MCP는 "하나의 클라이언트가 기억된 하나의 서버와 통신하는 방식"에서 더 웹 네이티브(Web-native)한 설계로 이동하고 있습니다. 만약 에이전트 통합(Agent integrations)을 구축하고 있다면, 지금이 스티키 세션(Sticky sessions), 취약한 인메모리 상태(In-memory state), 그리고 컨테이너가 재시작될 때 사라져 버리는 도구 호출(Tool calls)을 피할 수 있는 기회입니다.
이 가이드는 통제 불능 상태에 빠지지 않고 확장 가능한 에이전트 워크플로를 원하는 빌더들을 위한 실질적인 MCP 세션 아키텍처를 보여줍니다.
왜 MCP 세션 설계가 갑자기 중요해졌는가
Model Context Protocol, 즉 MCP는 AI 에이전트가 도구(Tools), 파일, 데이터베이스, API 및 내부 시스템에 접근할 수 있는 표준화된 방법을 제공합니다. 모든 팀이 각자 맞춤형 커넥터 패턴을 발명하는 대신, MCP는 클라이언트와 서버에 공유된 프로토콜을 제공합니다.
이러한 표준화는 유용하지만, 동시에 확장성(Scaling) 문제를 드러내기도 합니다.
로컬 MCP 서버는 메모리에 상태(State)를 유지할 수 있습니다. 하지만 프로덕션 MCP 서버는 대개 그럴 수 없습니다. 여러 인스턴스, 지역 라우팅(Regional routing), 오토스케일링(Autoscaling), 컨테이너 재시작, 그리고 장시간 실행되는 에이전트 워크플로를 추가하게 되면, 다음과 같은 단순한 질문에 대해 명확한 답이 필요합니다.
다음 도구 호출(Tool call)이 도착했을 때, 이전에 무슨 일이 일어났는지 누가 기억하고 있는가?
최근 MCP를 둘러싼 업계의 논의는 세션 ID(Session IDs)를 대규모 환경에서 더 쉽게 운영하는 방법에 집중되어 왔습니다. 빌더들을 위한 실질적인 교훈은 "세션이 사라졌다"가 아닙니다. 바로 이것입니다:
워크플로의 진실(Workflow truth)이 살아있는 유일한 장소를 단 하나의 프로세스로 만들지 마십시오.
흔한 MCP 확장성 함정
기본적인 MCP 설정은 종종 다음과 같은 형태를 띱니다:
에이전트 클라이언트(Agent client) -> MCP 서버 프로세스(MCP server process) -> 내부 도구/API(Internal tool/API)
이는 로컬 개발에는 괜찮습니다. 서버가 세션 메타데이터를 메모리에 저장할 수 있기 때문입니다:
const sessions = new Map();
function createSession(clientId) {
...
서버 인스턴스를 하나 이상 배포하면 이 방식은 무너집니다:
+----------------+
Agent client -> | Load balancer |
+-------+--------+
...
첫 번째 요청이 MCP A에 도달하고 다음 요청이 MCP B에 도달하면, 인메모리 (in-memory) 세션 상태가 사라집니다. 스티키 세션 (sticky sessions)을 강제할 수도 있지만, 이는 다음과 같은 자체적인 문제들을 야기합니다:
- 불균등한 부하 분산 (uneven load distribution)
- 더 어려워지는 오토스케일링 (autoscaling)
- 복잡한 페일오버 (failover)
- 취약한 지역적 라우팅 (regional routing)
- 불안정한 블루/그린 배포 (blue/green deploys)
- 연결과 상태 사이의 숨겨진 결합 (hidden coupling)
스티키 세션은 때때로 임시적인 가교로서 허용될 수 있습니다. 하지만 에이전트 플랫폼의 기반이 되어서는 안 됩니다.
더 나은 사고 모델: 세션 상태는 프로세스 메모리가 아니라 데이터이다
프로덕션 환경의 MCP 세션은 더 엄격한 요구 사항을 가진 일반적인 웹 세션처럼 취급되어야 합니다.
서버 프로세스가 도구 호출 (tool call)을 실행할 수는 있지만, 지속 가능한 워크플로우의 진실 (workflow truth)은 해당 프로세스 외부에 존재해야 합니다.
Agent client
|
v
...
이 설계는 모든 인스턴스가 다음과 같은 동일한 최소 상태에 접근할 수 있도록 합니다:
- 세션 식별자 (session identity)
- 테넌트 (tenant) 또는 워크스페이스 ID
- 인증된 사용자
- 허용된 도구
- 예산 제한 (budget limits)
- 스트림 커서 (stream cursor) 또는 이벤트 ID
- 최신 워크플로우 체크포인트 (workflow checkpoint)
- 취소 상태 (cancellation status)
- 감사 추적 ID (audit trace ID)
목표는 모든 토큰과 모든 생각을 저장하는 것이 아닙니다. 목표는 라우팅, 재개, 거부, 재시도, 감사 및 복구를 수행하기에 충분한 진실을 저장하는 것입니다.
권장되는 프로덕션 아키텍처
1. 전면에 MCP 게이트웨이 배치
모든 도구 서버를 모든 에이전트 클라이언트에 직접 노출하지 마십시오. 다음과 같은 공통 관심사를 처리하는 게이트웨이 또는 에지 레이어 (edge layer)를 사용하십시오:
- 인증 (authentication)
- 테넌트 조회 (tenant lookup)
- 속도 제한 (rate limits)
- 요청 크기 제한 (request size limits)
- 스키마 검증 (schema validation)
- HTTP 전송을 위한 Origin 체크
- 세션 조회 (session lookup)
- 추적 ID (trace ID) 생성
- 적절한 MCP 서비스로의 라우팅
게이트웨이는 전용 서비스, API 게이트웨이, 또는 소규모 리버스 프록시 (reverse proxy)와 미들웨어의 조합이 될 수 있습니다. 핵심은 모든 도구 서버에 중복되어 존재해서는 안 되는 규칙들을 중앙 집중화하는 것입니다.
2. 호스팅된 서버의 경우 Streamable HTTP 선호
MCP 전송(transport) 문서에서는 로컬 하위 프로세스 통신을 위한 stdio와 독립적인 서버를 위한 Streamable HTTP를 정의합니다. 호스팅된 멀티 유저 배포 환경에서는 일반적으로 Streamable HTTP가 더 나은 기본값입니다.
도움이 되는 이유:
- 각 클라이언트 메시지가 HTTP POST입니다.
- 서버는 JSON을 반환하거나 필요할 때 SSE(Server-Sent Events)로 스트리밍할 수 있습니다.
- 표준 로드 밸런서(load balancers)가 트래픽 형태를 이해합니다.
- 서버를 독립적인 서비스로 실행할 수 있습니다.
- 지원되는 경우 클라이언트가 재연결하고 재개할 수 있습니다.
간단한 호스팅 흐름:
POST /mcp
Accept: application/json, text/event-stream
Mcp-Session-Id: sess_123
...
정확한 헤더와 프로토콜 버전은 MCP 구현에 따라 다르지만, 설계 원칙은 안정적입니다. 즉, 모든 요청이 충분히 자기 기술적(self-describing)이어서 어떤 정상적인 인스턴스라도 이를 처리할 수 있도록 만드는 것입니다.
3. MCP 서버 외부에서 세션 상태 저장
필요에 따라 Redis, Postgres, DynamoDB 또는 다른 저지연 저장소(low-latency store)를 사용하세요.
Redis는 종종 핫 상태(hot state)에 유용합니다:
type McpSession = {
sessionId: string;
tenantId: string;
...
Postgres는 내구성이 있는 감사 이력(durable audit history)에 더 적합합니다:
create table mcp_tool_calls (
id uuid primary key,
session_id text not null,
...
유용한 분리 방식:
| 상태 유형 | 적합한 저장소 | 이유 |
|---|---|---|
| 활성 세션 메타데이터 (active session metadata) | Redis | 빠른 읽기, TTL 지원 |
| ... |
4. 도구 호출을 멱등하게(idempotent) 만들기
에이전트는 재시도합니다. 네트워크는 끊깁니다. 스트림은 깨집니다. 사용자는 탭을 새로고침합니다. 제공업체는 타임아웃이 발생합니다.
도구 호출이 고객 데이터를 생성, 삭제, 결제, 이메일 발송 또는 변경할 수 있다면, 멱등성 키(idempotency key)가 필요합니다.
function makeToolCallKey(input: {
sessionId: string;
toolName: string;
...
도구를 실행하기 전에 동일한 작업이 이미 완료되었는지 확인하세요:
async function runToolCall(call) {
const key = makeToolCallKey(call);
const existing = await db.toolResult.findByIdempotencyKey(key);
...
이 한 가지 패턴만으로도 많은 중복 부작용(side effects)을 방지할 수 있습니다.
5. 스트림 복구(stream recovery)를 위한 설계
SSE 또는 장시간 실행되는 응답(long-running responses)을 사용하는 경우, 연결 끊김이 발생할 수 있음을 가정해야 합니다.
이벤트 ID 또는 체크포인트(checkpoints)를 추적하세요:
type StreamCheckpoint = {
sessionId: string;
requestId: string;
...
클라이언트가 재연결될 때, 서버는 다음 질문에 답할 수 있어야 합니다:
- 도구 호출(tool call)이 완료되었는가?
- 어떤 이벤트들이 이미 전달되었는가?
- 스트림을 계속할 수 있는가?
- 대신 클라이언트가 최종 결과를 폴링(poll)해야 하는가?
- 요청이 취소되었는가?
첫날부터 완벽한 재생(replay) 시스템을 구축할 필요는 없습니다. 하지만 명확한 복구 계약(recovery contract)은 반드시 필요합니다.
생략해서는 안 될 보안 요구사항
MCP 서버는 에이전트를 실제 시스템에 연결합니다. 이를 단순한 보조 스크립트가 아닌, 프로덕션 API 인프라처럼 취급하십시오.
최소한 다음 사항을 준수해야 합니다:
- 스트리밍 가능한 HTTP 연결에 대해
Origin검증 - 호스팅된 서버에 대한 인증(authentication) 요구
- 가능한 경우 로컬 서버를 localhost에 바인딩
- JSON-RPC 메서드 이름 및 스키마(schemas) 검증
- 기본적으로 알 수 없는 도구는 거부
- 테넌트(tenant) 및 사용자별로 자격 증명(credentials) 범위 제한
- 로깅 전 비밀 정보(secrets) 마스킹(redact)
- 모든 도구 호출에 트레이스 ID(trace IDs) 첨부
- 요청 본문(request body) 제한 설정
- 타임아웃(timeouts) 강제 적용
작은 정책 검사만으로도 대규모의 실수를 방지할 수 있습니다:
function authorizeToolCall(session: McpSession, toolName: string) {
if (session.status !== "active") {
throw new Error("Session is not active");
...
프롬프트(Prompts)는 보안 경계가 아닙니다. 런타임 검사(Runtime checks)가 보안 경계입니다.
상태를 유지하지 말아야 할 것들 (What to Keep Stateless)
상태가 없는(stateless) MCP 서버라는 것이 "상태가 존재하지 않는다"는 의미는 아닙니다. 이는 프로세스가 다른 프로세스가 복구할 수 없는 상태를 소유하지 않음을 의미합니다.
상태가 없는(stateless) 처리에 적합한 대상:
- 요청 파싱(request parsing)
- 스키마 검증(schema validation)
- 인증 토큰 검증(auth token verification)
- 외부 설정에 기반한 라우팅 결정(routing decisions)
- 도구 실행 워커(tool execution workers)
- 응답 포맷팅(response formatting)
- 메트릭 방출(metrics emission)
프로세스 전용 메모리에 두기에 부적절한 대상:
- 테넌트 권한 (tenant permissions)
- 잔여 예산 (remaining budget)
- 승인 상태 (approval status)
- 장기 실행 워크플로 진행 상황 (long-running workflow progress)
- 멱등성 기록 (idempotency records)
- 감사 로그 (audit logs)
- 스트림 복구 커서 (stream recovery cursor)
- 취소 상태 (cancellation state)
컨테이너 하나를 잃었을 때 워크플로가 손상될 수 있다면, 해당 데이터는 해당 컨테이너 내부에만 존재해서는 안 됩니다.
로드 밸런서 전략 (Load Balancer Strategy)
단순하게 시작하세요:
- 하나의 공개 MCP 엔드포인트 (public MCP endpoint).
- 각 서버 인스턴스에 대한 상태 확인 (Health checks).
- 일반적인 JSON 응답을 위한 짧은 요청 타임아웃 (request timeouts).
- 스트림 (streams)을 위한 더 길지만 제한된 타임아웃.
- 외부 세션 저장소 (External session store).
- 특정 레거시 전송 방식 (legacy transport)이 요구하지 않는 한 스티키 세션 (sticky sessions) 사용 금지.
스트리밍 워크로드 (streaming workloads)의 경우, 실제 인프라를 테스트하십시오. 일부 프록시 (proxies)는 응답을 버퍼링 (buffer)합니다. 일부는 유휴 타임아웃 (idle timeouts)을 강제합니다. 일부는 HTTP/2, SSE, 그리고 청크 전송 (chunked transfer)에 대해 다르게 동작합니다.
출시 전에 다음 테스트를 실행하십시오:
- 도구 호출 (tool call) 중 클라이언트 연결 끊김
- 스트림 도중 서버 재시작
- 중복된 POST 재시도
- 만료된 세션 ID
- 취소된 워크플로
- 도구 타임아웃
- 잘못된 JSON-RPC 바디 (body)
- 로드 밸런서가 다음 요청을 다른 인스턴스로 전송
이 테스트들을 통과한다면, 귀하의 에이전트 플랫폼은 이미 많은 프로토타입보다 앞서 있는 것입니다.
비용 및 신뢰성 제어 (Cost and Reliability Controls)
세션 아키텍처는 곧 비용 아키텍처입니다.
세션은 지출을 제어할 수 있는 충분한 메타데이터 (metadata)를 포함해야 합니다:
type Budget = {
maxToolCalls: number;
maxModelCalls: number;
...
게이트웨이 (gateway)와 도구 실행 (tool execution) 내부에서 예산을 강제하십시오. 에이전트 루프 (agent loop)가 80번의 도구 호출을 수행했다는 사실을 최종 답변이 나온 후에야 알게 되어서는 안 됩니다.
유용한 메트릭 (metrics):
- 세션당 도구 호출 횟수 (tool calls per session)
- 도구별 실패한 도구 호출 횟수 (failed tool calls per tool)
- 세션당 재시도 횟수 (retry count per session)
- 평균 세션 지속 시간 (average session duration)
- 스트림 연결 끊김 비율 (stream disconnect rate)
- 수락된 결과당 비용 (cost per accepted outcome)
- 정책에 의해 취소된 세션 (sessions cancelled by policy)
- 성공적으로 재개된 세션 (sessions resumed successfully)
가장 좋은 메트릭은 "사용된 토큰"이 아닙니다. 그것은 **비용 단위당 완료된 유용한 작업 (useful work completed per unit of cost)**입니다.
실무 구축 체크리스트 (A Practical Build Checklist)
MCP 서버를 실제 사용자에게 노출하기 전에 이 체크리스트를 사용하십시오:
- 호스팅된 MCP 엔드포인트가 적절한 경우 인증된 Streamable HTTP를 사용하는가?
- 세션 상태(Session state)가 프로세스 메모리 외부(outside process memory)에 저장되는가?
- 모든 도구 호출(tool call)에 추적 ID(trace ID)가 있는가?
- 위험한 도구 호출은 멱등성 키(idempotency keys)를 사용하는가?
- 알 수 없는 도구는 기본적으로 거부되는가?
- 각 도구 호출 전에 세션 예산(Session budget)을 확인하는가?
- 도구 스키마(Tool schemas)가 런타임(runtime)에 검증되는가?
- 스트림(Stream) 연결 끊김에 대한 복구 경로가 있는가?
- 감사 로그(Audit logs)에 누가, 무엇을, 언제, 왜 호출했는지 기록되는가?
- 로드 밸런싱(Load balancing)이 스티키 세션(sticky sessions) 없이 작동하는가?
- 컨테이너 재시작 시 워크플로우의 진실성(workflow truth)이 손실되지 않는가?
- 비밀 정보(Secrets)가 로그에 절대 기록되지 않는가?
최종 요약 (Final Takeaway)
MCP는 에이전트 통합을 표준화하기 더 쉽게 만들어 줍니다. 하지만 이것이 프로덕션 아키텍처(production architecture)의 필요성을 없애주는 것은 아닙니다.
만약 귀하의 MCP 서버가 스티키 세션(sticky sessions)과 인메모리 워크플로우 상태(in-memory workflow state)에 의존한다면, 데모에서는 작동할지 몰라도 실제 사용 환경에서는 실패할 수 있습니다. 더 나은 설계는 세션의 진실성(session truth)을 공유 저장소(shared stores)에 두고, 도구 호출을 멱등하게(idempotent) 유지하며, 스트림을 복구 가능한 것으로 취급하고, 로드 밸런서가 제 역할을 할 수 있도록 하는 것입니다.
가장 단순한 규칙이 가장 안전한 규칙입니다:
건강한 모든 MCP 서버 인스턴스는 요청(request), 인증된 신원(authenticated identity), 그리고 공유된 세션 상태(shared session state)만으로 워크플로우의 다음 단계를 계속 수행할 수 있어야 합니다.
그런 방식으로 구축한다면, 귀하의 에이전트는 취약해지지 않고 확장(scale)할 수 있습니다.
FAQ
MCP 세션 아키텍처(MCP session architecture)란 무엇인가요?
MCP 세션 아키텍처는 MCP 배포 환경에서 서버 인스턴스 전반에 걸쳐 클라이언트 세션, 도구 호출, 스트림 상태, 권한, 재시도 및 감사 데이터(audit data)를 추적하는 방식입니다. 프로덕션 환경에서는 일반적으로 중요한 상태를 MCP 프로세스 외부에 저장하여, 어떤 건강한 인스턴스라도 워크플로우를 계속할 수 있도록 하는 것을 의미합니다.
MCP 서버에 스티키 세션(sticky sessions)이 필요한가요?
항상 그런 것은 아닙니다. 스티키 세션은 오래되었거나 상태가 매우 많은(highly stateful) 설계에는 도움이 될 수 있지만, 확장(scaling)과 장애 조치(failover)를 어렵게 만듭니다. 더 강력한 패턴은 세션 데이터를 Redis, Postgres 또는 다른 공유 저장소에 유지하여 요청이 인스턴스 간에 안전하게 이동할 수 있도록 하는 것입니다.
무상태 (Stateless) MCP 서버란 무엇인가요?
무상태 (Stateless) MCP 서버는 프로세스가 메모리 내에 고유한 워크플로우 진실성 (workflow truth)을 보유하지 않는 서버를 의미합니다. 여전히 상태를 읽고 쓸 수는 있지만, 해당 상태는 세션 저장소 (session store), 데이터베이스 (database), 큐 (queue) 또는 감사 로그 (audit log)와 같은 외부 시스템에 존재합니다.
프로덕션 MCP 배포에는 stdio보다 Streamable HTTP가 더 나은가요?
호스팅되는 다중 사용자 시스템의 경우, Streamable HTTP가 일반적으로 더 적합합니다. 이는 표준 HTTP 인프라, 독립적인 서버 프로세스, 인증 계층 (authentication layers) 및 로드 밸런서 (load balancers)와 함께 작동하기 때문입니다. Stdio는 서브프로세스 (subprocesses)로 실행되는 로컬 도구의 경우 여전히 유용합니다.
MCP 서버는 장시간 실행되는 도구 호출 (long-running tool calls)을 어떻게 처리해야 하나요?
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기