
MCP 서버 통합 테스트: 3개의 숨겨진 버그를 찾아낸 패턴
요약
MCP 서버 개발 시 단위 테스트가 놓치기 쉬운 레이스 컨디션, JSON-RPC 프레이밍 오류, 상태 버그를 해결하기 위한 통합 테스트 패턴을 소개합니다. STDIO를 통해 실제 서버를 실행하고 실제 요청을 보내는 방식으로 컴포넌트 간 상호작용 문제를 검증합니다.
핵심 포인트
- 단위 테스트의 높은 커버리지로도 잡지 못하는 컴포넌트 간 상호작용 오류 확인
- STDIO를 통한 실제 MCP 서버 실행 및 JSON-RPC 요청 검증 패턴 제안
- 초기화 순서 문제로 발생하는 레이스 컨디션 및 데이터 불일치 방지
- 대용량 페이로드 처리 시 발생하는 JSON-RPC 프레이밍 오류 해결
STDIO를 통해 MCP 서버를 실행하고 실제 도구/호출(tools/call) 요청을 보냄으로써 통합 테스트를 수행하세요. 이를 통해 90%의 단위 테스트(Unit Test) 커버리지에서도 놓쳤던 시작 시점의 레이스 컨디션(Startup races), JSON-RPC 프레이밍 오류(framing errors), 그리고 상태 버그(state bugs)를 잡아낼 수 있습니다.
핵심 요약 (Key Takeaways)
- STDIO를 통해 MCP 서버를 실행하고 실제 도구/호출(tools/call) 요청을 보냄으로써 통합 테스트를 수행하세요.
- 이를 통해 90%의 단위 테스트 (Unit Test) 커버리지에서도 놓쳤던 시작 시점의 레이스 컨디션 (Startup races), JSON-RPC 프레이밍 오류 (framing errors), 그리고 상태 버그 (state bugs)를 잡아낼 수 있습니다.
문제점: 90%의 커버리지, 그럼에도 프로덕션에서 발생하는 오류
당신은 MCP 서버를 구축했습니다. 이것은 AI 에이전트들을 위한 공유 메모리 역할을 하며, 한 에이전트가 동일한 버그에 부딪히기 전에 다른 에이전트에게 경고를 줄 수 있도록 실패를 기록합니다. 단위 테스트 (Unit tests)는 통과합니다. SQLite 레이어? 확인 완료. 모의 객체(Mocks)를 사용한 MCP 전송(transport)? 통과. 재시도 로직 (Retry logic)? 견고합니다.
하지만 프로덕션 환경에서는 문제가 발생합니다. 격리된 상태에서는 작동하던 도구 호출(tool call)이 세션 추적(session tracking)이 초기화되지 않았을 때 실패합니다. 하나의 MCP 클라이언트에서는 통과하는 수정 사항이 다른 클라이언트에서는 충돌을 일으킵니다. 문제는 코드가 아니라, 컴포넌트 간의 **상호작용 (interactions)**입니다.
해결책: 실제 MCP 서버, 실제 STDIO, 실제 테스트
패턴: 서브프로세스(subprocess)를 통해 실제 MCP 서버를 실행하고, STDIO를 통해 실제 JSON-RPC 요청을 보낸 뒤, 응답을 검증(assert)하세요. 모의 객체(Mocks), 스텁(stubs), 가짜 객체(fakes)는 사용하지 않습니다.
import subprocess
import json
import time
...
이것이 단위 테스트가 놓치는 버그를 잡아내는 이유

1. 시작 순서가 중요합니다. MCP 서버는 데이터베이스 열기, 캐시 로드, 도구 등록, STDIO 리스닝 순으로 순차적으로 초기화됩니다. 이 테스트는 캐시 로드가 완료되기 전에 캐시를 조회하여 오래된 데이터(stale data)를 반환하는 버그를 잡아냈습니다. 단위 테스트는 수동 설정 후에 함수를 호출하기 때문에 이를 절대 발견할 수 없었습니다.
2. JSON-RPC 프레이밍 (framing)은 간단하지 않습니다. URL 인코딩된 문자가 포함된 4KB 크기의 실패 보고서로 인해 readlines()가 페이로드(payload) 중간에서 분할되는 문제가 발생했습니다. 실제 STDIO 대화에서만 이 문제가 드러납니다. 여러분의 모의 클라이언트(mock client)는 아마도 완벽하게 프레이밍된 메시지를 보낼 것입니다.
3. 상태(State)가 누적됩니다. 150개의 항목이 쌓인 후, 중복 제거 과정에서 ORDER BY 없이 LIMIT 1을 사용하여 무작위 일치 항목을 반환했습니다. 3~5개의 항목만 사용하는 단위 테스트(unit test)에서는 이를 절대 잡아낼 수 없었습니다. 실제 데이터 볼륨을 사용한 통합 테스트(integration test)가 이를 찾아냈습니다.
피해야 할 주의사항 (Gotchas)
- 운영 환경에서 테스트하지 마세요. 데이터베이스용으로
tempfile.mkstemp()를 사용하고finally블록에서 정리하세요. - 타임아웃 (timeouts)을 추가하세요. 서버가 STDIO 모드에서 영원히 오지 않을 신호를 기다릴 수 있습니다. 3초의 알람을 추가하세요.
- 프로세스를 정리하세요. 좀비 프로세스(zombie processes)를 방지하기 위해 항상
try/finally내에서terminate()와.wait()를 호출하세요.
여러분의 MCP 서버는 어떻습니까?
Claude Code 사용자들은 데이터베이스 쿼리, API 통합, 파일 작업과 같은 커스텀 도구를 위해 일상적으로 MCP 서버를 구축합니다. 만약 단위 테스트만 수행하고 있다면, 구성 요소들이 실제로 서로 통신할 때 발생하는 버그를 놓치고 있는 것입니다.
오늘 당장 통합 테스트를 하나 추가해 보세요. 여러분이 고장 난 줄도 몰랐던 문제들을 잡아낼 것입니다.
출처: dev.to
[30 Jul 업데이트, gn_mcp_protocol 제공]
정확성을 테스트하는 것을 넘어, MCP 서버는 보안 감사(security audits)도 필요합니다. 24개의 오픈 소스 MCP 프로젝트를 스캔한 결과, AgenticX (202⭐)에서 한 건의 심각한 샌드박스 명령 주입 (sandbox command injection, CVSS 9.8) 취약점이 발견되었습니다. 해당 프로젝트의 파일 작업 메서드에서 셸 명령(shell commands)에 검증되지 않은 f-string을 사용했기 때문입니다 [dev.to 참조]. 동일한 스캐너로 Cline (65k⭐) 및 OpenHands (82k⭐)와 같은 최상위 프로젝트를 조사했을 때는 실제 취약점이 발견되지 않았습니다. 이는 위에서 설명한 것과 같은 통합 테스트를 인젝션 패턴(injection patterns)을 다루는 범위까지 확장할 수 있음을 시사합니다.
원문은 gentic.news에 게시되었습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기