이번 글에서는 MCP 툴 없이 직접 모델과 통신한 구현MCP 툴 기반 구조를 나란히 비교해보려 합니다.

두 방식 모두 질문 → SQL 생성 → 실행 → 요약이라는 흐름은 같지만, 구현 철학과 역할 분담 구조가 완전히 다릅니다.


1. 직접 모델 호출 방식 (비-MCP 방식)

먼저, 모델을 직접 호출해 SQL을 만들고, 그 결과를 요약하는 전통적인 접근입니다.

const result = await model.generateContent(prompt);
const text = (await result.response.text()).trim();

그리고 결과 rows를 직접 넣어서 요약 프롬프트를 또 호출하죠.

const prompt = `
  질문: ${question}
  결과: ${JSON.stringify(rows)}
  → 요약해줘
`;
const summary = (await model.generateContent(prompt)).response.text();

이 구조의 특징은:

  • 모델에게 질문과 함께 컨텍스트를 주입
  • 직접적인 요청 흐름
  • 개발자가 모든 흐름을 설계함

2. MCP 툴 기반 구조

이번엔 MCP 툴 기반 구조입니다. 모델에게 툴 목록을 미리 등록해두고, 질문이 오면 모델이 스스로 "어떤 툴을 어떤 순서로 호출할지" 결정하도록 합니다.

MCP 방식에서는 이런 3개의 툴이 등록되어 있습니다:

🔧 1) generate-sql

server.tool(
  'generate-sql',
  { properties: { question, tables } },
  async (args) => {
    const prompt = `질문: ${args.question}
테이블: ${args.tables.join(', ')}`;
    const result = await model.generateContent(prompt);
    return result.response.text();
  }
);

🔧 2) execute-sql

server.tool(
  'execute-sql',
  { properties: { sql: { type: 'string' } } },
  async (args) => {
    const result = await pool.query(args.sql);
    return JSON.stringify(result.rows);
  }
);

🔧 3) summarize-sql-result

server.tool(
  'summarize-sql-result',
  { properties: { sql, rows } },
  async (args) => {
    const prompt = `질문 결과 요약:
SQL: ${args.sql}
데이터: ${JSON.stringify(args.rows)}`;
    const result = await model.generateContent(prompt);
    return result.response.text();
  }
);

각 툴은 독립적으로 실행되며, 모델이 "연결 순서"를 판단하여 generate → execute → summarize 흐름을 조립합니다.


💡 동일한 기능을 직접 호출 방식으로 구현하면?

MCP 없이 구현한 코드에서는 아래처럼 3개의 함수를 직접 호출합니다:

📦 1) SQL 생성

async function generateSQL(question, tables) {
  const prompt = `질문: ${question}
테이블: ${tables.join(', ')}`;
  const result = await model.generateContent(prompt);
  return result.response.text();
}

📦 2) SQL 실행

const rows = await pool.query(sql);

📦 3) 결과 요약

async function generateSummary(question, rows) {
  const prompt = `질문: ${question}
데이터: ${JSON.stringify(rows)}`;
  const result = await model.generateContent(prompt);
  return result.response.text();
}

결론적으로 MCP 방식은 동일한 함수 단위를 툴로 감싸서 등록해두고, 모델이 필요한 순서로 호출하게 만든 구조입니다.

예: 등록된 MCP 툴 목록

  • generate-sql: 자연어 → SQL
  • execute-sql: SQL 실행
  • summarize-sql-result: 결과 요약
const parsedSteps = JSON.parse(await model.generateContent(prompt));
for (const step of parsedSteps) {
  const toolResult = await server.callTool(step.tool, step.args);
}

 

이 구조는:

  • 툴을 기반으로 설계됨
  • 모델이 실행 순서를 조립함
  • 서버는 단순히 툴 실행자 역할

구조적 비교

항목 직접 모델 호출 방식 MCP 툴 기반 구조
흐름 설계 개발자가 모두 설계 모델이 조립
확장성 프롬프트마다 추가 구현 필요 툴만 추가하면 됨
복잡도 처음은 단순하나 점점 커짐 초반 설계가 필요하지만 구조는 깔끔
모델 의존도 단발성 호출 중심 연속된 흐름도 모델이 주도

실제로 구현해보니

기능적인 결과는 거의 같습니다. 질문하면 SQL 생성되고, 실행되고, 요약도 됩니다. 사용자 입장에서는 두 방식 차이를 못 느낄 정도죠.

하지만 서버 입장에서 느껴지는 구조적 차이는 꽤 큽니다.

  • 비-MCP는 일회성 처리가 편하고 빠름
  • MCP는 구조를 한번 만들면 계속 재사용, 확장이 쉬움

또한 툴 단위로 분리해두면, 각 도구의 로깅, 테스트, 교체가 훨씬 유리해집니다.


마치며

두 가지 구조를 비교해보며 느낀 점은:

“작고 단순한 프로젝트는 직접 호출이 빠르지만, 복잡해질수록 MCP 기반이 구조적으로 더 낫다.”

 

 

긴 글 읽어 주셔서 감사합니다.

 

그리고 지금까지 소스 공개합니다.


REST API 기준

https://github.com/pm2makeq/chatbot_restapi

 

GitHub - pm2makeq/chatbot_restapi: restapi chatbot

restapi chatbot. Contribute to pm2makeq/chatbot_restapi development by creating an account on GitHub.

github.com

 

MCP 기준

https://github.com/pm2makeq/mcp-project

 

GitHub - pm2makeq/mcp-project

Contribute to pm2makeq/mcp-project development by creating an account on GitHub.

github.com

 

전체글

+ Recent posts