HTTP는 클라이언트가 서버를 찾는 구조인데, 서버가 어떻게 역으로 푸시할 수 있을까? 답은 두 개의 SSE 연결이다
요약
이 글은 Anthropic Academy의 내용을 바탕으로, HTTP의 단방향성 제한을 극복하는 '이중 연결 모델(dual connection model)'을 설명합니다. 서버 주도 요청과 진행 상황 알림은 항상 열려 있는 메인 SSE 연결로 전송되며, 도구 호출 결과는 별도의 임시 연결을 사용합니다. 모든 통신에는 클라이언트 식별자인 mcp-session-id가 필수적으로 포함되어야 합니다.
핵심 포인트
- SSE를 활용하여 HTTP의 단방향성 제한을 우회하는 것이 핵심입니다.
- 서버 주도 요청은 메인 SSE 연결로, 도구 결과는 전용 연결로 분리됩니다.
- 모든 통신 메시지에는 클라이언트 식별자인 mcp-session-id가 반드시 포함되어야 합니다.
- 진행 상황 알림과 도구 결과를 같은 연결에서 처리하는 실수를 피해야 합니다.
HTTP는 분명 클라이언트가 서버를 찾는데, 서버가 어떻게 반대로 푸시할 수 있을까요? 그 답은 두 개의 SSE(Server-Sent Events) 연결입니다.
이 글은 Anthropic Academy의 《Model Context Protocol: Advanced Topics》 제12과 실습 강의 내용이며, 제목을 직역하면 《StreamableHTTP in depth (심층 StreamableHTTP)》입니다. 결론부터 말씀드리겠습니다. 이 내용은 상식에 반합니다:
StreamableHTTP는 Server-Sent Events(SSE)를 사용하여 HTTP의 단방향 제한을 우회했습니다. 초기화 과정에서 클라이언트가 GET 요청을 보내 SSE 연결을 구축하고, 이를 통해 서버가 언제든지 메시지를 푸시할 수 있는 장기 연결이 형성되며, 서버 주도 요청은 이 채널을 통해 전송됩니다.
먼저 용어를 명확히 하겠습니다. **Server-Sent Events(SSE)**는 서버가 하나의 장기 연결을 통해 클라이언트에게 지속적으로 메시지를 푸시하는 HTTP 메커니즘입니다. mcp-session-id는 서버가 초기화 결과에서 제공하는 특수 헤더로, 특정 클라이언트를 고유하게 식별하며 이후 모든 요청에 포함되어야 합니다. **주 SSE 연결(main SSE connection)**은 초기화 후에 구축되며 항상 열려 있는 연결로, 서버 주도 요청 전용입니다. **도구 전용 SSE 연결(tool-specific SSE connection)**은 각 도구 호출마다 별도로 생성되는 연결이며, 도구 결과가 전송되면 자동으로 닫힙니다.
실제 예시를 들어보겠습니다: 클라이언트가 Initialize Request를 보내고, 서버는 Initialize Result를 반환하며 여기에 특수한 mcp-session-id 헤더를 포함합니다. 이후 클라이언트는 session ID를 담아 Initialized Notification을 보내 초기화를 완료합니다. 그 후 클라이언트가 GET 요청을 보내 SSE 연결을 구축하면, 서버는 이 지속적인 채널을 통해 요청이나 알림 등의 메시지를 보낼 수 있습니다. 도구를 호출할 때는 별도의 도구 전용 연결이 생성되며, 결과가 전송되면 닫힙니다.
그들은 사람들이 흔히 오해하는 '익숙한 구식 경로'를 지적합니다. 마치 HTTP에서 서버 주도 메시지 전송은 어떤 마법에 의존하거나, 하나의 연결로 모든 것을 처리할 수 있다고 생각하는 식입니다. 공식적인 해명은 매우 직설적입니다: 우회 방법은 이중 연결 모델(dual connection model)입니다. 메인 연결은 계속 열어두고 서버 주도 요청을 전송하며, 도구 연결은 사용 후 닫습니다. 두 연결은 각각의 역할이 있습니다.
모든 메시지가 같은 연결을 통과하는 것은 아닙니다. 분기 규칙은 다음과 같습니다:
- 진행 상황 알림(Progress notification)은 메인 SSE 연결을 통해 전송됩니다;
- 로그 메시지 및 도구 결과는 도구 전용 SSE 연결을 통해 전송됩니다;
- 두 연결 모두 session ID를 포함해야 하며, 누락되면 식별할 수 없습니다.
1. 이중 연결 모델을 이해하지 못해 발생하는 세 가지 실수
- 진행 상황 알림의 경로 상실: 진행 상황이 도구 연결로 전송될 것이라고 생각하여, 도구 결과가 전송되면서 연결이 닫히자마자 진행 상황도 끊기는 경우.
- session ID 누락: 초기화 이후의 요청에 mcp-session-id를 빠뜨려 서버가 어떤 클라이언트인지 식별하지 못하는 경우.
- 제한적 설정으로 우회 방법 파괴: stateless_http나 json_response를 True로 설정하면, SSE 우회 메커니즘이 직접적으로 손상되어 서버 주도 기능 전체가 마비됩니다.
2. 대안 방안: 초기화 → 연결 구축 → 분기 처리의 삼단계 과정
초기화 3단계: 클라이언트가 Initialize Request를 보내고 → 서버는 mcp-session-id 헤더가 포함된 Initialize Result를 반환하며 → 클라이언트는 session ID를 담아 Initialized Notification을 보냅니다. 연결 구축 1단계: 초기화 후 클라이언트가 GET 요청을 보내 SSE 연결을 생성하여, 서버가 언제든지 푸시할 수 있는 장기 연결을 만듭니다. 분기 처리 1단계: 메인 SSE 연결은 서버 주도 요청을 전송하며 계속 열려 있고; 각 도구 호출마다 별도의 도구 전용 연결이 만들어져 결과 전송과 동시에 닫힙니다. 진행 상황 알림은 메인 연결로, 로그 및 도구 결과는 도구 연결로 전송됩니다. 이 세 단계는 동일한 목표를 수행합니다: 먼저 사용자를 식별하고(인식), 통로를 열고(연결), 마지막으로 메시지 종류에 따라 분기하는 것(분류).
3. 목표 형태: 시스템이 다중 연결을 자동 관리하는 상태
이상적인 상태는 개발자가 수동으로 연결을 관리할 필요가 없는 것입니다. 초기화 후 모든 요청은 session ID를 포함하고, 시스템이 여러 SSE 연결을 자동으로 관리하며, 메시지는 종류에 따라 자동으로 분기됩니다. 하지만 '자동'이라는 전제에는 모델에 대한 이해가 선행되어야 합니다. 이중 연결 모델을 모르면 디버깅 시 두 개의 연결을 보고 버그라고 생각할 수 있고, 최적화할 때도 어느 쪽을 수정해야 할지 알지 못합니다.
4. 전환 3단계: 단일 연결 이해에서 이중 연결 이해로의 변화
첫 번째 단계: 초기화 3단계를 정확히 이해하기
session ID를 확보했다면, 이후 모든 요청에 이를 포함해야 합니다. 이것이 바로 이중 연결 모델의 근간입니다.
두 번째 단계: 메시지 분기 규칙을 디버깅 목록에 작성하기
진행 상황 알림은 메인 연결에서 확인하고, 로그 및 결과는 도구 연결에서 확인합니다. 오류가 발생하면 두 연결을 동시에 추측하지 말고, 먼저 어떤 경로로 메시지가 이동하는지 질문해야 합니다.
세 번째 단계: 제한적 설정을 적용하기 전에 대가를 계산하기
특정 상황에서는 stateless_http나 json_response를 활성화하고 싶을 수 있지만, 이들은 SSE 우회 기능을 파괴하며 서버가 클라이언트로 통신하는 모든 기능에 의존성을 제한합니다. 따라서 설정 변경 전 그 대가를 먼저 목록화해야 합니다.
5. 팀이 동일한 연결 이해도를 구축하는 방법
이중 연결 모델(雙連接模型)의 가장 큰 함정은 기술 자체가 아니라 디버깅 과정에 있습니다. 진행 상황을 잃었을 때 도구 연결(工具連接)을 확인하고, 로그를 잃었을 때 주 연결(主連接)을 확인하는 식입니다. 팀원들은 세 가지 규칙을 먼저 정해야 합니다. 메시지 분배 규칙은 디버깅 매뉴얼에 작성하여 감으로 추측하며 확인하지 않도록 하고, session ID 전달 방식은 공용 코드(共用程式碼)로 작성하여 요청마다 다르게 처리하지 않도록 하며, 제한적 설정값(限制性配置)의 값은 검토해야 합니다. 변경된 경우 서버가 시작하는 기능에 대해 재검증이 필요합니다.
6. 공식 툴킷 외에 기억할 것들
이번 강의는 완벽한 도구 상자를 제공하지 않았지만, 경계(boundary)를 매우 명확하게 설명했습니다. StreamableHTTP는 HTTP 제한을 우회해야 하기 때문에 다른 전송 방식보다 복잡합니다. 이중 연결 모델을 이해하는 것은 디버깅과 최적화에 매우 중요하며, 애플리케이션을 구축할 때는 초기화 후 모든 요청이 session ID를 필요로 한다는 점을 기억하고, 시스템이 여러 개의 SSE(Server-Sent Events) 연결을 자동으로 관리한다는 것을 인지해야 합니다.
결론: 원칙이 아키텍처보다 중요하다
우회 방법은 마법이 아니라 모델입니다. 원칙이 아키텍처보다 중요합니다—먼저 이중 연결 모델을 이해한 후에 디버깅과 최적화에 대해 논해야 합니다. 모델이 명확하지 않으면, 잘못된 연결만 계속 확인하게 됩니다.
반복 가능한 디버깅 규칙은 다음과 같습니다: 다음에 StreamableHTTP를 디버깅할 때는 세 가지 질문을 먼저 던지세요—session ID가 포함되었는가? 메시지는 어느 연결을 사용했는가? 제한적 설정이 활성화되었는가?—세 가지에 답할 수 있다면, 문제의 절반은 해결된 것입니다.
원본 링크: https://t.co/mLgqaNisz4
원본: Anthropic Academy (S1 형 컴파일)
S1 형 관점
이 강의에서 가장 강력한 부분은 많은 사람들이 가진 '하나의 연결로 모든 것을 처리한다'는 환상을 산산조각 냈다는 점입니다. 과거에는 모두 HTTP에 단 하나의 경로만 있다고 생각했지만, 이제 공식적으로 두 개의 경로가 있으며 각각 분담하는 역할이 있다는 것입니다. 진정으로 비용이 많이 드는 것은 추가적인 연결을 구축하는 비용 자체가 아니라, 단일 연결로 이해하고 하루 종일 디버깅하며 진행 상황 손실 시 도구 연결을 확인하고 로그 손실 시 주 연결을 확인하는 등, 모든 것을 잘못된 곳에서 확인하는 행위입니다.
저는 이 쪽에 서 있습니다: 모델을 먼저 분별해야 합니다. 이중 연결 모델을 모르고 추측에 의존하여 디버깅하는 개발자는 '모르겠다'라고 말하는 것보다 더 위험합니다—전자는 이미 위치를 찾았다는 착각을 가지고 코드를 수정하게 만들지만, 후자는 적어도 멈추게 만듭니다.
그래서 한 가지 질문을 드리겠습니다: 현재 여러분의 StreamableHTTP 디버깅에서 잘못된 연결을 확인한 적이 몇 번이나 있습니까? 댓글로 숫자를 보고해 주세요. 솔직하게 말할 수 있겠습니까?
AI 자동 생성 콘텐츠
본 콘텐츠는 X Claude/Anthropic의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기