본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 06. 04. 16:28

REST API는 AI 에이전트에게 최악입니다. 제가 MCP로 전환한 이유

요약

REST API 기반의 에이전트 툴링이 가진 파싱 오류와 스키마 변경 취약성을 지적하며, 이를 해결하기 위한 Model Context Protocol(MCP)로의 전환 과정을 설명합니다. MCP를 통해 에이전트가 도구를 스스로 발견하고 구조화된 응답을 받을 수 있는 표준화된 방식을 제안합니다.

핵심 포인트

  • REST API는 에이전트에게 불필요한 번역 계층과 글루 코드를 요구함
  • MCP는 자기 기술적 도구(Self-describing tools)를 통해 스키마 불일치 해결
  • 구조화된 출력과 스트리밍 지원으로 에이전트의 실행 안정성 향상
  • 에이전트를 API의 부가 존재가 아닌 일급 시민으로 취급하는 표준 제공

요약 (TL;DR): REST API는 AI 에이전트가 엔드포인트(endpoints)를 추측하게 만들고, 예측 불가능한 응답을 파싱(parse)하게 하며, 스키마(schema)가 변경될 때마다 작동을 멈추게 합니다. 저는 에이전트 툴링(tooling)을 Model Context Protocol (MCP)로 마이그레이션하여 통합 실패의 한 부류를 완전히 제거했습니다. REST가 무엇을 잘못했는지, MCP는 무엇이 다른지, 그리고 100줄 미만의 코드로 진행한 실제 마이그레이션 과정을 소개합니다.

제가 계속해서 마주쳤던 문제

외부 서비스와 상호작용하는 AI 에이전트를 구축하기 시작했을 때, 저는 모든 개발자가 그렇듯 REST 래퍼(wrappers)를 작성했습니다. 에이전트가 도구(tool)를 호출하면, 도구가 API를 호출하고, API가 JSON을 반환하면, 에이전트가 그 JSON을 읽는 방식입니다. 간단해 보이죠.
하지만 간단하지 않았습니다. 실제로 일어난 일은 다음과 같습니다:

  • 에이전트가 search_customers{"name": "Müller"}와 함께 호출합니다.
  • 도구가 GET /api/v2/customers?q=Müller를 전송합니다.
  • API가 { "data": { "items": [...] } }로 감싸진 페이지네이션(paginated) 결과를 반환합니다.
  • 에이전트가 중첩된 구조(nested structure)를 파싱하지 못하고 "고객을 찾을 수 없습니다"라고 응답합니다.
  • 로그를 확인해 보니 API는 14개의 결과를 반환했습니다. 에이전트가 단지 읽지 못한 것입니다.

이런 일이 매일 발생했습니다. 새로운 API를 통합할 때마다 API가 반환하는 것과 LLM(Large Language Model)이 이해할 수 있는 것 사이의 번역 계층(translation layer)을 작성해야 했습니다. API 측의 스키마(schema) 변경은 에이전트를 소리 없이 망가뜨렸습니다. 에러 응답은 LLM이 유효한 데이터로 해석해 버리는 난해한 JSON 덩어리였습니다. 저는 기능을 만드는 시간보다 글루 코드(glue code)를 작성하는 데 더 많은 시간을 쓰고 있었습니다. 그래서 저는 MCP로 전환했습니다.

MCP가 다르게 하는 점

Model Context Protocol (MCP)는 AI 에이전트가 도구를 발견하고 사용하는 방식에 대한 표준입니다. 에이전트가 엔드포인트를 추측하고 가공되지 않은 JSON을 파싱하는 대신, MCP는 다음을 제공합니다:

  • 자기 기술적 도구 (Self-describing tools). 서버가 에이전트에게 어떤 도구가 존재하는지, 어떤 파라미터(parameters)를 허용하는지, 무엇을 반환하는지를 정확하게 알려줍니다. OpenAPI 명세(spec)의 불일치(drift)나 추측이 필요 없습니다.
  • 구조화된 출력 (Structured output). 응답이 일관된 엔벨로프(envelope)를 따릅니다.

에이전트는 정규 표현식(regex-parsing)으로 HTTP 상태 코드를 분석할 필요 없이 성공과 실패가 무엇인지 알 수 있습니다.

  • 스트리밍 및 진행 상황 (Streaming and progress). 오래 걸리는 작업이 진행 상황을 보고하므로, 에이전트가 동기식 REST 호출을 기다리다가 타임아웃(timeout)이 발생하지 않습니다.

핵심 통찰: MCP는 에이전트를 인간용 API에 나중에 덧붙인 부가적인 존재가 아니라, 일급 시민(first-class client)으로 취급합니다.

전과 후: 실제 마이그레이션 사례

다음은 에이전트가 사용하는 전형적인 REST 기반 도구의 모습입니다:

REST 방식: 취약하고, 장황하며, 수동적임

import requests

def search_customers_rest(query: str) -> str:  
    try:  
        r = requests.get(  
            "https://api.example.com/v2/customers",  
            params={"q": query},  
            headers={"Authorization": f"Bearer {API_KEY}"},  
            timeout=10  
        )  
        r.raise_for_status()  
    except requests.Timeout:  
        return "Error: API timed out."
    except requests.HTTPError as e:  
        return f"Error: API returned {r.status_code}."

    data = r.json()
    # REST API는 데이터를 감싸는 것을 좋아하기 때문에 3단계의 중첩이 발생합니다
    items = data.get("data", {}).get("items", [])
    ...

이제 MCP 버전을 보겠습니다:

MCP 방식: 자기 기술적(self-describing)이고, 구조화되었으며, 회복 탄력성이 있음

from mcp import Client

mcp = Client("https://api.example.com/mcp")

result = await mcp.call_tool("search_customers", {"query": "Müller"})

if result.error:  
    return f"Error: {result.error.message}"

customers = result.content  
if not customers:  
    return "No customers found."

lines = [f"- {c.display_name} ({c.email})" for c in customers[:5]]  
return "\n".join(lines)

차이점은 단순히 코드 줄 수가 줄어든 것이 아닙니다. 모든 실패 모드(failure mode)가 이제 명시적이라는 점입니다. 에이전트는 result.error.message가 구조화되어 있기 때문에 이에 대해 추론할 수 있습니다.

MCP 도구 검색(Tool Discovery) 작동 방식

MCP 클라이언트가 연결되면, 서버는 사용 가능한 모든 도구를 설명하는 JSON 매니페스트 (manifest)를 전송합니다. 이 매니페스트는 에이전트의 함수 정의 (function definition)로 자동 변환됩니다. 저는 OpenAPI 명세 (specs)를 수동으로 작성하지 않습니다. 별도의 도구 레지스트리 (tool registry)를 유지 관리하지도 않습니다. 서버가 신뢰할 수 있는 유일한 원천 (source of truth)이며, 여기에 연결되는 모든 에이전트는 동기화된 상태를 유지합니다.

MCP가 정답이 아닌 경우

MCP가 만능 해결책 (silver bullet)은 아닙니다. REST가 여전히 유효한 경우는 다음과 같습니다:

시나리오REST 사용MCP 사용
UI가 있는 사용자 대상 API
에이전트 전용 도구 서버
기존 서드파티 (third-party) 서비스
직접 제어하는 내부 서비스
단순 읽기 전용 데이터 가져오기
다단계, 상태 유지 (stateful) 작업

제가 제어할 수 있는 서비스의 경우, 이제 기본적으로 MCP 서버를 구축합니다. 서드파티 API의 경우, 한 번만 중앙에서 REST-to-MCP 변환을 처리하는 MCP 서버로 감싸서(wrap) 사용합니다.

과거의 나에게 해주고 싶은 말

1년 전, 저는 모든 에이전트 통합을 위해 REST 래퍼 (wrappers)를 작성하고 있었습니다. tools/라는 폴더에는 47개의 파일이 있었고, 각 파일은 API와 LLM 사이의 취약한 번역기 역할을 했습니다. 만약 제가 과거로 메시지를 보낼 수 있다면:

  • 래퍼(wrapper)가 아닌 MCP 서버를 구축하세요. 모든 도구를 설명하는 단 하나의 서버를 만드세요. 모든 에이전트가 동일한 단일 진실 공급원(source of truth)에 연결됩니다.
  • 서버가 포맷팅을 처리하게 하세요. 에이전트는 구조화된 데이터(structured data)를 받아야 하며, 가공되지 않은 API 응답(raw API responses)을 받아서는 안 됩니다.
  • 코드를 테스트하듯 도구 설명(tool descriptions)을 테스트하세요. 스키마 불일치(Schema mismatches)는 새벽 3시가 아니라 연결 시점에 드러나야 합니다.

결론

REST API는 인간과 프론트엔드 앱을 위해 설계되었습니다. REST API는 페이지네이션(pagination), 에러 코드(error codes), 중첩된 응답 엔벨로프(nested response envelopes)를 이해하는 클라이언트를 가정합니다. AI 에이전트는 그렇지 않습니다. 에이전트에게는 구조화되고 자기 기술적인(self-describing) 인터페이스가 필요합니다. MCP는 에이전트에게 바로 그것을 제공합니다.

만약 서비스와 통신하는 AI 에이전트를 구축하고 있다면, REST 래퍼(wrapper) 작성을 멈추세요. MCP 서버를 구축하세요. 그러면 에이전트의 오류는 줄어들고, 코드는 더 단순해지며, 여러분은 접착 코드(glue code) 대신 기능 구현에 시간을 더 쓸 수 있을 것입니다.

저는 centerbit에서 AI 에이전트 인프라를 구축하고 있습니다. MCP, 에이전트 툴링(agent tooling), 또는 인간 참여형(HITL) 워크플로우에 관심이 있다면 centerbit.co에서 더 많은 내용을 확인하세요.

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0