본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 06. 26. 10:36

OpenAPI에서 MCP로: 150줄의 코드로 자동 변환기를 만들며 겪은 과정과 문제점

요약

OpenAPI 명세를 기반으로 MCP(Model Context Protocol) 서버를 자동으로 생성하는 Java 구현 과정을 다룹니다. 반복적인 보일러플레이트 코드 작성을 줄이기 위해 150줄의 코드로 엔드포인트를 MCP 도구로 변환하는 자동화 방안을 제시합니다.

핵심 포인트

  • OpenAPI 명세를 활용해 MCP 서버 구축 자동화 가능
  • 반복적인 JSON 스키마 정의 및 핸들러 작성 공수 절감
  • OpenAPI 3.0 파싱부터 API 라우팅까지의 구현 로직 공유
  • AI 클라이언트와 기존 API 간의 효율적인 연결 가교 역할

OpenAPI에서 MCP로: 150줄의 코드로 자동 변환기를 만들며 겪은 과정과 문제점

솔직히 말해서, 이렇게 잘 작동할 줄은 몰랐습니다.

다양한 프로젝트를 위해 10개 이상의 MCP 서버를 구축하면서, 저는 똑같은 보일러플레이트 (Boilerplate) 코드를 반복해서 작성하는 것에 지쳤습니다. 여러분도 잘 아시겠지만, JSON으로 도구 (Tools)를 정의하고, 핸들러 함수 (Handler functions)를 작성하고, 파라미터 (Parameters)를 매핑하고, 입력을 검증하는 과정 말입니다... 어렵지는 않지만, 그저 지루할 뿐입니다.

하지만 여기서 핵심은 이겁니다. 대부분의 기존 API는 이미 OpenAPI 명세 (OpenAPI spec)를 가지고 있다는 점입니다. 왜 우리는 그것으로부터 MCP 서버를 자동으로 생성할 수 없는 걸까요?

그것이 바로 제가 시도한 일이었습니다. 며칠 동안 JSON 스키마 (JSON Schema), 서로 다른 명세 버전들, 그리고 MCP 프로토콜 (MCP protocol)과 씨름한 끝에, 약 150줄의 Java 코드로 이를 구현해냈습니다. 오늘 저는 제가 무엇을 배웠는지, 무엇이 망가졌는지, 그리고 여러분도 직접 시도해 볼 가치가 있는지 공유하고자 합니다.

아이디어: 보일러플레이트 작성을 멈추자

맥락을 설명해 보겠습니다. 저는 Papers라는 지식 베이스 프로젝트를 구축해 왔는데, 여기에는 저의 모든 개인적인 노트와 기술적 지식이 저장되어 있습니다. 저는 이미 이를 MCP 서버로 변환하여 어떤 AI 클라이언트 (AI client)라도 제 노트를 직접 접근할 수 있도록 만들었습니다. 그 부분은 아주 잘 작동했습니다. MCP는 제가 AI 통합 (AI integration)을 생각하는 방식을 정말로 바꾸어 놓았습니다.

하지만 최근에 이미 완벽한 OpenAPI 명세를 갖춘 또 다른 API를 추가하고 싶었습니다. 제가 정말로 모든 엔드포인트 (Endpoint)를 MCP 도구로 수동 매핑하는 데 또 다른 오후 시간을 쓰고 싶었을까요?

아니요, 전혀 그렇지 않았습니다.

MCP의 약속은 일단 MCP 서버를 갖추고 나면, 어떤 AI 클라이언트라도 이를 사용할 수 있다는 것입니다. 문제는 그 MCP 서버에 도달하기까지 여전히 모든 API에 대해 수동 작업이 필요하다는 점입니다. 만약 이 과정을 생략할 수 있다면 어떨까요?

OpenAPI 명세 (OpenAPI Spec) → 자동 생성된 MCP 서버 (Auto-Generated MCP Server) → 모든 MCP AI 클라이언트 (Any MCP AI Client)

그것이 바로 이상향입니다. 우리가 얼마나 그 목표에 근접했는지 보여드리겠습니다.

구현: 실제로 작동하는 150줄의 코드

제가 취한 접근 방식은 다음과 같습니다:

  1. OpenAPI 3.0 명세 (spec) 파싱
  2. 각 엔드포인트 (endpoint)를 MCP 도구 (tool) 정의로 변환
  3. OpenAPI 스키마 (schema)로부터 파라미터 (parameter)를 위한 JSON 스키마 (JSON Schema) 생성
  4. 들어오는 MCP 도구 호출을 실제 API로 라우팅 (route)
  5. MCP 형식에 맞춰 응답 반환

간단해 보이죠? 실제 코드를 살펴보겠습니다.

1단계: 메인 컨트롤러 (Main Controller)

먼저, MCP 엔드포인트를 처리하는 기본 컨트롤러입니다:

@RestController
@RequestMapping("/mcp")
public class OpenApiMcpController {
...

지금까지는 꽤 깔끔합니다. tools/list 엔드포인트는 OpenAPI 명세를 파싱하여 MCP 도구로 변환한 뒤 반환하기만 합니다. tools/call 엔드포인트는 실제로 API를 호출하는 인보커 (invoker)에게 작업을 위임합니다.

2단계: 엔드포인트를 도구로 변환하기

여기가 마법이 일어나는 지점입니다. OpenAPI 엔드포인트를 어떻게 MCP 도구로 변환할까요?

public List<Tool> convertToTools(OpenApiSpec spec) {
    List<Tool> tools = new ArrayList<>();

...

여기서 핵심적인 통찰은 모든 OpenAPI 엔드포인트가 자연스럽게 하나의 MCP 도구로 매핑된다는 점입니다. 이는 일대일 (one-to-one) 변환입니다. 정말 아름답죠.

엔드포인트는 AI가 파라미터를 사용하여 호출할 수 있는 함수일 뿐입니다. MCP 도구도 정확히 그렇습니다. 매핑 과정은 거의 사소할 정도입니다.

3단계: 까다로운 부분 — JSON 스키마 변환

여기서 첫 번째 큰 벽에 부딪혔습니다. OpenAPI는 JSON 스키마의 변형된 형태를 사용하지만, MCP는 입력 검증 (input validation)을 위해 표준 JSON 스키마를 기대합니다. 이 둘은 100% 동일하지 않습니다.

좋은 소식은? 대부분의 경우 간단한 변환만으로도 잘 작동한다는 것입니다:

private JsonSchema buildParametersSchema(PathItem path, Operation op) {
    ObjectNode schema = nodeFactory.objectNode();
    schema.put("type", "object");
...

copySchema 메서드는 OpenAPI에서 JSON 스키마로 스키마 구조를 재귀적으로 복사하기만 합니다. 90%의 사례에서는 이 방식이 완벽하게 작동합니다.

4단계: 실제로 도구 호출하기

AI가 도구를 호출하면, 우리는 실제로 기반이 되는 API를 호출하고 응답을 반환해야 합니다:

public McpResponse invoke(McpCallRequest request) {
    try {
        // 요청에서 도구 이름을 추출합니다
...

저는 이 방식이 정말 마음에 드는데, 에러 처리 (error handling)가 매우 직관적이기 때문입니다. 무언가 잘못되면 단순히 에러 메시지를 AI에게 다시 보내기만 하면 되고, 그러면 AI가 무엇이 잘못되었는지 스스로 파악할 수 있습니다. 복잡하게 만들 필요가 없습니다.

놀라운 점들: 작동하지 않았던 것들

좋습니다, 코드는 단순해 보입니다. 하지만 저는 이를 파악하는 데 꽤 오랜 시간이 걸린 몇 가지 문제에 부딪혔습니다. 여러분의 시간을 아껴드리기 위해 공유하겠습니다.

놀라움 1: 모든 OpenAPI 명세(Spec)가 유효한 것은 아니다

저는 세 가지 서로 다른 공개 API로 이를 테스트했습니다:

  • GitHub API v3 — 명세가 대부분 작동하지만, 저의 단순한 변환기를 망가뜨리는 이상한 anyOf 구조가 포함되어 있었습니다.
  • Stripe API — 거대한 명세이며, 하나의 MCP 서버에 담기에는 엔드포인트 (endpoints)가 너무 많았습니다 (이 부분은 나중에 다루겠습니다).
  • 더 작은 규모의 내부 API — 첫 시도에 완벽하게 작동했습니다.

진실은 이렇습니다: 많은 실제 OpenAPI 명세에는 불일치 사항이 존재합니다. 필수(Required)로 표시되지 않은 필수 필드, 누락된 타입 정보 (type information), 해결되지 않는 참조 (references) 등등...

교훈: 이 접근 방식은 여러분이 OpenAPI 명세를 직접 제어할 수 있을 때 가장 잘 작동합니다. 만약 자신의 코드로부터 명세를 생성했다면, 아마 충분히 깨끗할 것입니다. 하지만 다른 사람의 엉망인 명세를 변환하려고 한다면, 먼저 이를 수정할 준비를 해야 합니다.

놀라움 2: 너무 많은 도구 = AI를 혼란스럽게 함

이것은 제가 예상하지 못했던 부분입니다. 전형적인 REST API는 3050개의 엔드포인트를 가질 수 있습니다. 모든 엔드포인트를 MCP 도구 (tool)로 변환하면, 결국 3050개의 도구가 생기게 됩니다.

그런데 결과가 어땠을까요? AI가 혼란을 느낍니다. 옵션이 그렇게 많으면 AI가 항상 올바른 도구를 선택할 수 있는 것은 아닙니다. 때로는 유사한 도구 이름을 혼동하기도 합니다.

저는 Claude Desktop으로 이를 테스트했습니다. 도구가 10개일 때는 아주 잘 작동합니다. 도구가 30개일 때는 여전히 작동하지만 실수가 더 많아집니다. 도구가 50개가 넘어가면 꽤 자주 잘못된 도구를 선택하기 시작합니다.

배운 점: API 규모가 크다면 어떤 엔드포인트(endpoint)를 변환할지 필터링해야 합니다. 모든 것을 노출하지 마세요. 가장 유용한 것들만 노출하세요. 또는 도구 목록(tools/list)에 대한 페이지네이션(pagination)을 구현해야 하지만, 제가 마지막으로 확인했을 때 MCP는 아직 이를 지원하지 않습니다.

놀라움 3: 파일 업로드는 까다롭습니다

OpenAPI는 multipart/form-data를 통해 파일 업로드를 지원하지만, 이를 MCP 도구 파라미터(tool parameters)로 매핑하는 것은 간단하지 않습니다. MCP에는 콘텐츠 타입(content types)이 있지만, AI는 언제 문자열(string) 대신 파일을 보내야 하는지 이해해야 합니다.

저는 아직 이 문제를 완전히 해결하지 못했습니다. 현재로서는 파일 업로드를 예상하는 엔드포인트를 그냥 건너뜁니다. 제 사용 사례에는 작동하지만, 이는 분명한 한계점입니다.

놀라움 4: 인증은 여전히 당신의 문제입니다

자동 생성기(auto-generator)는 API 엔드포인트를 변환하는 작업은 처리하지만, MCP 서버와 기반이 되는 API 사이의 인증(authentication)은 여전히 당신의 문제입니다. API 키(API keys), OAuth, 또는 당신의 API가 사용하는 방식이 무엇이든 이를 처리해야 합니다.

이것이 중요한가요? 사실 그렇지 않습니다. 어차피 직접 해야 했을 일이니까요. 하지만 이는 자동 생성기가 모든 것을 해결해주지는 않는다는 의미입니다. 여전히 약간의 설정(configuration)이 필요합니다.

현재 제 설정에서 이를 처리하는 방식은 다음과 같습니다:

// 모든 나가는 요청에 API 키 추가
public class ApiKeyInterceptor implements ClientHttpRequestInterceptor {
    @Override
...

충분히 간단하지만, 자동 생성되지는 않습니다.

장단점: 솔직해져 봅시다

그렇다면 이 방식을 사용해야 할까요? 솔직하게 분석해 보겠습니다.

✅ 장점

  1. 실제로 유용합니다 — OpenAPI 명세 (spec)가 있는 API를 가지고 있다면, 반나절이 걸릴 일을 단 5분 만에 MCP 서버로 실행할 수 있습니다. 이는 엄청난 시간 절약입니다.

  2. 유지보수가 필요 없습니다 — API에 새로운 엔드포인트 (endpoint)를 추가하면 MCP에 자동으로 나타납니다. 수동 업데이트가 필요하지 않습니다.

  3. 단 150줄의 코드입니다 — 이해하기 쉽고, 수정하기 쉬우며, 문제가 발생했을 때 고치기도 쉽습니다. 매번 다시 실행해야 하는 복잡한 코드 생성 (code generation) 단계가 없습니다.

  4. 모든 OpenAPI 3.0 명세와 호환됩니다 — Java Spring, Node.js, Python 등 무엇이든 상관없습니다. 명세 (spec)만 있다면 MCP 서버를 생성할 수 있습니다.

❌ 단점

  1. 이름이 항상 완벽하지는 않습니다 — 경로 (path)와 메서드 (method)로부터 도구 (tool) 이름을 생성할 때, get_users_id와 같은 이름이 만들어집니다. 작동은 하지만 직접 정한 이름만큼 깔끔하지는 않습니다. 하지만 AI는 여전히 이를 이해합니다.

  2. 복잡한 스키마 (schema)는 깨질 수 있습니다 — 중첩된 oneOf/anyOf가 많은 매우 복잡한 요청 본문 (request body)이 있는 경우, 변환이 완벽하게 작동하지 않을 수 있습니다. 결과는 상황에 따라 다를 수 있습니다 (YMMV).

  3. 도구가 너무 많으면 AI가 혼란을 느낍니다 — 앞서 언급했듯이, 수십 개의 엔드포인트가 있는 대규모 API는 AI를 혼란스럽게 할 수 있습니다. 수동으로 필터링해야 합니다.

  4. 커스텀 비즈니스 로직이 없습니다 — API 호출 전후에 변환 작업을 수행해야 한다면, 이를 수동으로 추가해야 합니다. 하지만 이는 예상된 부분입니다.

실제 사례: 작동합니다!

저는 직장의 내부 API를 위해 이 자동 생성된 MCP 서버를 몇 주 동안 실행해 왔습니다. 실제로 일어나는 일은 다음과 같습니다:

  • API에 새로운 엔드포인트를 추가합니다 → OpenAPI 명세를 업데이트합니다 → MCP에 자동으로 나타납니다 → 끝.
  • AI는 10번 중 9번은 도구를 정확하게 사용합니다.
  • 실수를 할 때는 보통 이름이 유사한 두 개의 엔드포인트가 있을 때이며, 그럴 때는 노출 목록에서 하나를 제거하기만 하면 됩니다.
  • 이것을 유지보수하는 데 사용한 총 시간은 약 20분입니다. 그게 전부입니다.

이전에는 각 MCP 도구 (tool)를 수동으로 작성했습니다. 도구 하나당 10~15분이 걸렸죠. 도구가 20개라면 5시간이 소요됩니다. 이제는 총 20분이면 충분합니다. 충분히 가치 있는 일이었습니다.

전체 코드

완성된 작동 코드를 보고 싶다면 GitHub의 Papers 프로젝트를 확인하세요. 자동 변환기 (auto-converter)는 mcp 패키지에 들어 있습니다.

참고로, Papers는 제가 모든 MCP 실험 내용을 기록하고 있는 개인 지식 베이스 (knowledge base) 프로젝트입니다. MCP에 관심이 있다면 별 (star)을 눌러주세요. 저는 항상 이와 같은 새로운 패턴과 교훈들을 추가하고 있습니다.

다시 한다면?

당연히 다시 할 것입니다. 솔직히 말해서, 투입된 시간(며칠간의 실험)에 비해, 보일러플레이트 (boilerplate) 코드를 작성하지 않음으로써 이미 그 시간을 수없이 많이 회수했습니다.

이것이 MCP의 미래일까요? 저는 그렇다고 생각합니다. 더 많은 API가 OpenAPI를 채택함에 따라, 자동 생성된 MCP 서버 (MCP server)는 매우 합리적입니다. 이미 완벽한 명세 (spec)를 갖춘 API를 위해 왜 모든 개발자가 수동으로 MCP 서버를 작성해야 할까요?

그렇다고 해서 이것이 만능 해결책 (silver bullet)이라는 뜻은 아닙니다. 훌륭한 이름과 커스텀 비즈니스 로직 (custom business logic)을 갖춘 깔끔하고 잘 정리된 MCP API가 필요하다면, 여전히 직접 작성하는 것이 더 낫습니다. 하지만 내부 API, 프로토타이핑 (prototyping), 또는 기존 API에 MCP 지원을 빠르게 추가하려는 경우에는 이 방식이 따라올 자가 없습니다.

여러분의 차례

OpenAPI로부터 MCP 서버를 자동 생성해 보셨나요? 제가 겪은 것과는 다른 문제에 직면하셨나요? 저는 여전히 이 방식을 실험 중이므로, 여러분이 배운 점들을 듣고 싶습니다. 아래에 댓글을 남겨 알려주세요!

이 글이 즐거우셨다면, 제가 MCP 서버를 어렵게 구축하며 얻은 교훈들을 모아두는 GitHub의 Papers를 확인해 보세요. 도움이 되었다면 저장소에 별 (star)을 눌러주세요!

AI 자동 생성 콘텐츠

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

원문 바로가기
0

댓글

0