CodeGym /행동 /ChatGPT Apps /MCP Gateway: ChatGPT와 여러분의 서비스 사이에 왜 필요한가

MCP Gateway: ChatGPT와 여러분의 서비스 사이에 왜 필요한가

ChatGPT Apps
레벨 16 , 레슨 0
사용 가능

1. 왜 또 하나의 레이어가 필요한가?

대부분은 비슷하게 시작합니다: MCP‑서버 하나가 있고, 몇 개의 도구를 기술하며, ChatGPT가 HTTPS로 그 서버에 직접 연결합니다. 겉보기엔 모든 것이 훌륭해 보입니다. 개략적인 아키텍처는 다음과 같습니다:

ChatGPT  →  여러분의 MCP-서버  →  데이터베이스 / 외부 API

‘pet project’ 단계에서는 정말로 괜찮은 선택입니다. 하지만 애플리케이션에 기능이 늘고 개발팀이 커지기 시작하면, 문제들이 금방 드러납니다.

첫째, MCP‑서버가 “God‑object”로 변합니다. 그 안에 선물 추천 도구, checkout, 분석, 그리고 “여기에 리포팅도 같이 넣자” 같은 것들이 한데 모입니다. 코드의 각 부분은 서로 다른 SLA와 보안 요구사항을 가지지만, 하나의 프로세스로 엮여 있습니다.

둘째, ChatGPT와 다른 클라이언트가 여러분 서비스의 토폴로지를 알아야 합니다. 반년 뒤 commerce용 MCP‑서버가 하나 더 생기면, 클라이언트를 다시 연결하고, 설정과 설명을 바꿔야 합니다. “단일 진입점” 대신 URL의 동물원이 됩니다.

셋째, 모든 서비스에 공통인 것들을 어디에 구현할지가 애매해집니다: 인증, 로깅, 메트릭, rate limiting, 토큰 검증, 로컬라이제이션과 지역 라우팅. 이를 모든 MCP/Agent 서비스에 흩뿌리면 중복이 많아지고 서비스마다 동작이 달라집니다.

이 결합을 끊고 동시에 ChatGPT로부터 내부 복잡성을 숨기기 위해 MCP Gateway가 등장합니다 — MCP 트래픽 전체를 위한 네트워크 게이트웨이이자 단일 진입점입니다.

2. ChatGPT App 맥락에서 MCP Gateway란?

형식적으로 MCP Gateway는 MCP‑클라이언트(ChatGPT, MCP Jam, 내부 도구)와 여러분의 백엔드 서비스 집합(보통 REST/HTTP API, 마이크로서비스, Agents 서비스, commerce‑백엔드 등) 사이의 프록시 계층이자 단일 진입점입니다.

Gateway는 외부에는 MCP‑프로토콜을 자체 구현합니다(따라서 ChatGPT에게는 하나의 MCP‑서버처럼 보임). 내부적으로는 평범한 REST 엔드포인트를 HTTP/gRPC로 호출합니다.

tools/list 요청에 대해 gateway는 프록시하지 않고 자체 툴 목록을 반환합니다: 코드에 하드코딩되어 있거나 설정에서 수집됩니다. 각 tool은 특정 REST‑엔드포인트와 데이터 스키마에 매핑됩니다. tools/call 요청에서는 gateway가 도구 이름을 받아 해당 REST 라우트를 찾아 fetch/HTTP‑클라이언트로 호출합니다.

개략도는 다음과 같습니다:

flowchart LR
    ChatGPT["ChatGPT / 모델"] --> |MCP JSON-RPC| Gateway["MCP Gateway<br/>(단일 MCP 서버)"]
    Gateway --> GiftAPI["Gift REST API<br/>/ 선물 마이크로서비스"]
    Gateway --> CommerceAPI["Commerce REST API<br/>/ ACP / 결제"]
    Gateway --> AnalyticsAPI["Analytics Service<br/>/ 이벤트와 메트릭"]

ChatGPT 입장에서는 하나의 서버입니다: 하나의 URL, 하나의 도구 세트, 하나의 이벤트 스트림. 여러분에게는 다양한 콜드/핫 서비스로 트래픽을 라우팅하는 유연한 지점입니다.

3. GiftGenius 아키텍처에서의 MCP Gateway

추상론을 줄이고 실제 시스템에서의 gateway를 보여주기 위해, 선물 추천과 ACP/Instant Checkout 주문을 처리하는 GiftGenius 예제를 이어가겠습니다.

간단한 버전에서는 하나의 MCP‑서버가 suggest_giftscheckout_start 둘 다를 처리했습니다. 이제 앱이 커졌으니 책임을 분리합니다:

  • Gift REST API — 선물 검색과 추천, 카탈로그/피드 처리(일반적인 HTTP/REST 서비스).
  • Commerce REST API — ACP, checkout 세션, 주문 상태, 결제 프로바이더 연동.
  • Analytics Service / REST API — 이벤트와 메트릭 수집(어떤 추천을 열고 무엇을 구매하는지).
  • 별도의 Agents 서비스(필요하다면) — 복잡한 다단계 시나리오. 이것도 MCP가 아니라 HTTP/REST로 접근합니다.

MCP Gateway는 이 모든 구성 요소의 단일 진입점이 됩니다. 역할:

  • 요청 tools/list에 대해 스스로 정의한 통합 툴 목록을 반환합니다: 각 tool은 특정 서비스의 REST‑엔드포인트에 매핑됩니다;
  • 요청 tools/call에 대해 도구 이름(params.name)을 보고 라우팅 테이블로 어느 REST 서비스로 갈지 결정한 뒤 해당 HTTP 메서드를 호출합니다(fetch, axios 등).

tools/callsuggest_gifts라면, gateway는 Gift REST API의 해당 REST‑엔드포인트를 호출합니다. checkout_start라면 요청은 Commerce REST API로 갑니다.

Express 스타일의 TypeScript 의사코드는 다음과 같습니다:

// 매우 단순화한 MCP 요청 핸들러
app.post("/mcp", async (req, res) => {
  const mcpReq = req.body as { method: string; params?: any };
  const ctx = buildContextFromHeaders(req); // auth, locale 등

  const toolName = mcpReq.params?.name;
  const backendRes = await callBackend(toolName, mcpReq, ctx);

  res.json(backendRes);
});

pickBackend 내부에서는 메서드 이름, 도구 이름, 사용자 로케일, 심지어 서비스 버전(“카나리”와 blue/green 릴리스 — 이는 뒤에서 다룰 예정)에 따라 선택할 수 있습니다.

4. MCP Gateway의 책임: 반드시 하는 일

GiftGenius 아키텍처에서 gateway가 어디에 위치하는지 살펴봤습니다. 이제 구체적인 애플리케이션과 상관없이, 독립 레이어로서 gateway가 맡는 책임을 명확히 정리합니다. 중요하게 gateway는 네트워크이자 크로스‑서비스 레이어입니다. 선물 비즈니스 로직을 고민하는 것이 아니라, 그 주변의 인프라 과제를 해결하는 것이 목적입니다.

요청 라우팅

첫 번째 역할은 라우터입니다. Gateway는 MCP‑요청을 받아, 요청 내용과 사용자 컨텍스트, 자신의 구성에 따라 대상 서비스를 선택합니다.

예를 들어, GiftGenius에서는 간단한 라우팅 테이블을 둘 수 있습니다:

const TOOL_ROUTES: Record<string, "gift" | "commerce" | "analytics"> = {
  suggest_gifts: "gift",
  get_similar_gifts: "gift",
  checkout_start: "commerce",
  get_order_status: "commerce",
  log_event: "analytics",
};

그리고 이렇게 사용합니다:

function pickBackend(req: McpRequest, ctx: GatewayContext): Backend {
  if (req.method === "tools/list") return "aggregator";
  if (req.method === "tools/call") {
    const toolName = req.params?.name;
    const group = TOOL_ROUTES[toolName] ?? "gift";
    return group === "commerce" ? commerceBackend : giftBackend;
  }
  return giftBackend;
}

우리의 경우 giftBackend, commerceBackend, analyticsBackend는 평범한 REST‑서비스입니다. 각자 기본 URL("https://gift-api.internal", "https://commerce-api.internal", …)을 가집니다. Gateway는 MCP를 내부로 전달하지 않고, MCP 호출을 적절한 REST‑엔드포인트에 대한 HTTP 요청으로 풀어냅니다.

경계에서의 인증과 권한 부여

두 번째 핵심 기능은 경계 보호입니다. Gateway는 토큰을 확인하고 사용자가 누구인지, 어떤 조직에서 왔는지, 어떤 권한을 가졌는지 판단하기에 좋은 위치입니다.

예를 들어, ChatGPT 또는 여러분의 MCP Auth‑서버가 발행한 OAuth 토큰을 받아 검증하고(직접 암호화 로직을 만드는 대신 검증된 라이브러리를 쓰는 것이 바람직), 깔끔한 컨텍스트 객체로 변환할 수 있습니다:

type GatewayContext = {
  userId: string | null;
  tenantId: string | null;
  locale: string;
};

function buildContextFromHeaders(req: Request): GatewayContext {
  const token = req.headers["authorization"]; // "Bearer ..."
  const claims = token ? verifyJwt(token) : null;

  return {
    userId: claims?.sub ?? null,
    tenantId: claims?.tenant ?? null,
    locale: (req.headers["x-openai-locale"] as string) || "en-US",
  };
}

그렇게 되면 내부 백엔드/REST‑서비스는 원시 HTTP 헤더와 토큰을 직접 파싱할 필요 없이, 이미 정규화된 context를 받아 userId, tenantId, locale을 사용할 수 있습니다. MCP 문서의 권고도 동일합니다: 토큰 검증을 “처음부터” 구현하지 말고, 검증된 라이브러리와 단명 토큰을 사용하세요.

로깅, 트레이싱, 메트릭

세 번째 역할은 관측 가능성입니다. Gateway는 모든 들어오는 MCP 요청과 응답을 보기 때문에, correlation‑id를 부여하고(민감 정보 없이) 도구 파라미터를 로깅하고, 응답 시간과 상태를 기록하기에 최적의 지점입니다.

아주 간단한 아이디어:

app.use((req, res, next) => {
  const requestId = crypto.randomUUID();
  (req as any).requestId = requestId;

  const start = Date.now();
  res.on("finish", () => {
    const ms = Date.now() - start;
    console.log(
      `[${requestId}] ${req.method} ${req.url} -> ${res.statusCode} in ${ms}ms`
    );
  });

  next();
});

이후 관측 모듈에서는 이러한 데이터를 단순 console.log가 아니라 구조적 저장소로 전송하고, 그 위에 대시보드를 구성할 수 있습니다.

기본 부하 제어

네 번째지만 중요한 과제는 1차 부하 제어입니다. Gateway에서 사용자, 조직, 도구, 엔드포인트별 호출 카운터를 두면, “폭주”하는 클라이언트 하나가 여러분의 클러스터와 모델 비용을 태워버리는 일을 막을 수 있습니다.

이 모듈에서는 아이디어만 고정합니다: rate limiting과 큐는 gateway 레벨에 존재하며, 구현 세부(예: Redis, 토큰 버킷, 리킹 버킷)는 다음 강의의 경계 보호에서 다룹니다.

요청 컨텍스트 보강

마지막으로, gateway는 MCP‑클라이언트의 원시 컨텍스트를 내부 도구에 맞는 깔끔한 인자로 변환하기 좋은 장소입니다.

예를 들어, ChatGPT는 openai/locale_meta["openai/userLocation"]을 통해 사용자 로케일을 전달할 수 있습니다. Gateway는 다음을 수행할 수 있습니다:

  • 적절한 지역 서비스를 선택(ru 서버, en 서버 등);
  • 도구의 JSON Schema에 로케일이 명시적으로 없더라도, locale을 도구 호출 인자에 추가(예: 선택적 필드).

예시적으로:

function enrichToolArgs(args: any, ctx: GatewayContext) {
  return {
    ...args,
    locale: args.locale ?? ctx.locale,
    tenantId: ctx.tenantId,
  };
}

그 결과 Gift API는 “풍부한 컨텍스트”를 바로 받아 "ru-RU"에는 러시아어, "en-US"에는 영어 설명을 가져올 수 있습니다.

5. MCP Gateway가 해서는 안 되는 일

“모든 것이 지나는 마법의 장소”가 생기면, 과거에 각 서비스에 있던 모든 것을 그 안에 밀어 넣고 싶은 유혹이 생깁니다. 그렇게 gateway는 괴물로 변할 위험이 있습니다.

일반적으로 이 레이어에서 하지 말아야 할 것들이 몇 가지 있습니다.

첫째, 복잡한 비즈니스 로직. 선물 추천, 할인 규칙, 배송비 계산, ACP 로직 등은 전문화된 백엔드/commerce 서비스 내부에 있어야 합니다. Gateway는 많아야 가벼운 사전 검증(예: 가격이 음수가 아닌지 확인) 정도만 하고, SKU 선택이나 지역별 세금 계산은 하지 않습니다.

둘째, 장기 사용자 상태. Gateway는 전형적인 stateless‑서비스입니다. 수평 확장이 쉬워야 하고, 로컬 메모리에 의존하지 않으며, 재시작에 내성이 있어야 합니다. 여기서 checkout 마법사의 상태나 임시 장바구니를 저장하기 시작하면, 인스턴스 간 동기화 문제로 금방 고통을 겪게 됩니다.

셋째, 특정 기능은 해당 백엔드 서비스(Gift API, Commerce API) 내부에 두는 것이 논리적입니다. 예컨대 Gift 백엔드가 선물 검색 결과를 캐싱하고 싶다면, Redis 등을 활용해 자체적으로 하면 됩니다. Gateway는 이런 내부 최적화를 알 필요가 없습니다. 경계 보호 파트에서 다시 강조하겠지만, 게이트웨이는 네트워크와 크로스‑서비스 기능에 관한 것이지, 추천 비즈니스 규칙이 아닙니다.

넷째, 무거운 계산. Gateway 내부에서 LLM 모델을 호출하고, 복잡한 변환과 집계를 수행하기 시작하면, “가벼운 프런트”가 아니라 또 하나의 비대한 백엔드가 되어 확장과 디버깅이 어려워집니다.

6. Gateway, 로컬라이제이션과 서비스 버전

이제 gateway의 기본 책임과 넣지 말아야 할 것들을 봤습니다. 이번에는 이 레이어에서 특히 다루기 좋은 “고급” 과제 두 가지, 즉 로컬라이제이션과 서비스 버전 관리를 보겠습니다. 또 다른 흥미로운 역할은 로케일과 서비스 버전에 따른 스마트 라우팅입니다.

ChatGPT가 여러분의 App을 호출할 때 이미 사용자 언어에 대한 정보(openai/locale)와 종종 지리 정보(_meta["openai/userLocation"])를 갖고 있습니다. Gateway는 이 정보를 활용해 적절한 백엔드 서비스로 요청을 보낼 수 있습니다.

예컨대 “gateway 하나 — 다수의 단일 언어 백엔드 서버” 아키텍처를 만들 수 있습니다:

  • ru‑Gift API — 러시아어 전용 카탈로그와 텍스트.
  • en‑Gift API — 영어 전용.
  • jp‑Gift API — 일본어(세계 정복을 결심했을 때).

이 경우 Gateway는 ChatGPT에 대해 MCP‑서버로 동작하고, localeuserLocation으로 적절한 내부 서비스를 선택합니다.

예시적으로:

function pickGiftBackendByLocale(ctx: GatewayContext): Backend {
  if (ctx.locale.startsWith("ru")) return giftRuBackend;
  if (ctx.locale.startsWith("ja")) return giftJpBackend;
  return giftEnBackend;
}

같은 곳에서 가장 단순한 카나리 라우팅도 구현하기 좋습니다. 이 프로덕션 아키텍처 모듈에서는 gateway를 활용해 서비스의 새 클러스터로 일부 트래픽만 보내고, 나머지는 기존으로 유지하는 방식을 권장합니다.

아주 거친 카나리 예시:

function pickGiftBackendCanary(ctx: GatewayContext): Backend {
  const hash = hashUser(ctx.userId ?? "anonymous");
  const bucket = hash % 100;
  return bucket < 5 ? giftBackendV2 : giftBackendV1; // 트래픽의 5%를 v2로 보냄
}

이렇게 하면 Gift API의 새 버전을 전체 프로덕션을 한 번에 깨뜨리지 않고, 메트릭과 오류를 보며 안전하게 출시할 수 있습니다.

7. 전형적인 아키텍처: 올인원에서 Gateway로

이 강의에서 여러분은 이미 ChatGPT App의 여러 프로덕션 아키텍처 변형을 봤습니다. 이 모듈에서는 90%의 경우에 충분한 세 가지 기본 토폴로지를 정리합니다.

첫째 — “올인원”. App‑위젯(Next.js), MCP‑서버, Agents 로직, 간단한 commerce‑백엔드가 한 서비스에 있습니다. 종종 한 저장소, 심지어 하나의 Vercel 앱이기도 합니다. 장점 — DevOps 부담이 거의 없고, 배포가 간단하며, 지연 시간이 최소입니다. 단점 — 개별 부분을 확장하기 어렵고, 한 핫한 기능이 전체 애플리케이션을 다운시킬 수 있으며, 구성 요소 간 경계가 흐립니다.

둘째 — App + MCP Gateway + 여러 백엔드 서비스. 여기서 Next.js‑위젯은 분리(Vercel 등)되고, 모든 MCP‑트래픽은 Gateway를 통해 Gift REST API, Commerce REST API, Agents 서비스, ACP‑백엔드 등으로 라우팅됩니다. 지금 GiftGenius 맥락에서 살펴보는 바로 그 구조이며, 현실 세계의 90% 프로덕션 케이스에 적합합니다.

셋째 — 동일한 구성을 여러 지역(multi‑region)으로 확장하고, gateway 앞에 글로벌 로드 밸런서를 둡니다. 그러면 유럽 사용자는 eu‑클러스터, 미국 사용자는 us‑클러스터로 연결되고, 각 지역은 “Gateway + 여러 백엔드 서비스” 구성을 따릅니다. 이는 전 세계 사용자를 가진 꽤 큰 프로젝트의 이야기입니다.

지금 중요한 것은 모든 변형을 외우는 것이 아니라, 초기 단계에서 MCP‑모놀리식 또는 App의 백엔드가 역할을 대신하더라도 gateway를 아키텍처의 별도 논리 컴포넌트로 사고하는 습관을 갖는 것입니다.

8. MCP Gateway는 어디에 배치되는가

좋은 소식: MCP Gateway가 반드시 Kubernetes 위의 거대한 별도 서비스일 필요는 없습니다. 대개는 몇 단계의 성숙 과정을 거칩니다.

아주 작은 규모에서는 MCP‑서버 자체가 gateway 역할을 수행할 수 있습니다. 이 경우 코드를 깔끔히 구조화하면 됩니다: 라우팅, 인증, 로깅을 하나의 모듈로 빼고, 도구 로직은 다른 모듈로 분리하세요. 이 모듈에서 우리는 작은 시스템에서는 gateway 기능이 MCP‑서버 또는 App의 백엔드(예: Next.js API route) 안에 있을 수 있다고 명시합니다.

다음 단계는 별도의 Node/TypeScript 서비스입니다. "/mcp"를 수신하고 내부의 여러 HTTP 서비스를 호출하는 Express/Fastify 애플리케이션일 수 있습니다. 많은 팀에게 익숙한 DevOps 도구들과 잘 맞아 편합니다.

이런 서비스의 간단한 스켈레톤:

const app = express();
app.use(express.json());

app.post("/mcp", handleMcpRequest); // 여기서 gateway의 마법이 일어남

app.listen(4000, () => {
  console.log("MCP Gateway listening on :4000");
});

더 성숙해지면 AWS API Gateway, Cloudflare Workers/Routes, NGINX/Envoy(라우팅 구성과 Lua/JS 스크립트) 같은 매니지드 해법 위에 gateway를 구현할 수 있습니다. 이는 개념이 아니라 구현의 변화입니다. 아키텍처적으로 ChatGPT는 여전히 한 지점으로 접속하며, 세부 사항은 gateway가 처리합니다.

9. 미니 예시: GiftGenius용 간단한 MCP Gateway

이미 라우팅, 컨텍스트, tools/list 처리를 각각 살펴봤습니다. 이제 이를 작은 하나의 예로 모아봅니다. 내부에 두 개의 REST‑서비스가 있다고 합시다:

  • GIFT_API_BASE = "https://gift-api.internal";
  • COMMERCE_API_BASE = "https://commerce-api.internal".

그리고 ChatGPT가 "https://gateway.giftgenius.com/mcp" 주소로 호출하는 gateway 하나가 있습니다.

먼저 몇 가지 타입을 정의합니다:

type Backend = "gift" | "commerce";

type ToolRoute = {
  backend: Backend;
  method: "GET" | "POST";
  path: string;
};

const TOOL_ROUTES: Record<string, ToolRoute> = {
  suggest_gifts: {
    backend: "gift",
    method: "POST",
    path: "/api/gifts/suggest",
  },
  checkout_start: {
    backend: "commerce",
    method: "POST",
    path: "/api/checkout/start",
  },
  get_order_status: {
    backend: "commerce",
    method: "GET",
    path: "/api/orders/status",
  },
};

다음으로 백엔드 선택과 호출을 구현합니다:

async function callBackend(toolName: string, mcpReq: McpRequest, ctx: GatewayContext) {
  const route = TOOL_ROUTES[toolName];
  if (!route) {
    throw new Error(`Unknown tool: ${toolName}`);
  }

  const base =
    route.backend === "gift" ? GIFT_API_BASE : COMMERCE_API_BASE;

  const url = base + route.path;

  // tools/call MCP 호출로부터 전달된 args
  const args = {
    ...(mcpReq.params?.arguments ?? {}),
    locale: ctx.locale,
  };

  const res = await fetch(url, {
    method: route.method,
    headers: { "content-type": "application/json" },
    body: route.method === "POST" ? JSON.stringify(args) : undefined,
  });

  const data = await res.json();

  // REST-서비스 응답을 MCP 응답으로 감싸기
  return {
    result: data,
  } satisfies McpResponse;
}

마지막으로, 다음을 수행하는 메인 핸들러입니다:

  1. 헤더에서 컨텍스트 구성(auth, locale).
  2. 백엔드 선택.
  3. tools/list를 집계하거나 tools/call을 프록시.
app.post("/mcp", async (req, res) => {
  const mcpReq = req.body as McpRequest;
  const ctx = buildContextFromHeaders(req);

  if (mcpReq.method === "tools/list") {
    // Gateway가 직접 도구와 그 스키마를 선언
    const tools = [
      {
        name: "suggest_gifts",
        description: "예산과 관심사에 따라 선물을 추천합니다.",
        inputSchema: { /* ... JSON Schema ... */ },
      },
      {
        name: "checkout_start",
        description: "주문 초안을 만들고 checkout을 시작합니다.",
        inputSchema: { /* ... */ },
      },
      // ...
    ];

    return res.json({ result: { tools } });
  }

  if (mcpReq.method === "tools/call") {
    const toolName = mcpReq.params?.name;
    const backendRes = await callBackend(toolName, mcpReq, ctx);
    return res.json(backendRes);
  }

  res.status(400).json({ error: { message: "Unsupported MCP method" } });
});

물론 단순화된 구성지만, 핵심 아이디어를 보여줍니다:

  • gateway는 Gift API가 선물을 어떻게 추천하는지 알지 못합니다;
  • 단지 요청을 깔끔히 라우팅하고, 인자를 보강하며, 원한다면 호출을 로깅하고 제한합니다.

10. 이 모든 것이 모듈의 다음 주제와 어떻게 연결되는가

MCP Gateway는 이 모듈의 나머지 강의에서 다룰 모든 주제의 초석입니다:

  • 다음 주제에서는 경계 보호를 다룹니다: rate limiting, 큐, backpressure. 이 모든 것은 우선 gateway 레벨에서 동작합니다. gateway가 전체 인입 트래픽을 보기 때문에, 요청이 백엔드를 덮치기 전에 “과한 부분을 잘라낼” 수 있습니다.
  • 그다음은 탄력성입니다: timeouts, circuit breakers, bulkheads. Gateway는 외부 호출의 타임아웃을 중앙에서 설정하고 문제 서비스(예: 오류가 잦은 Commerce API)를 일시적으로 껐다 켜기 좋은 지점입니다.
  • 마지막으로, 확장과 배포를 논할 때 gateway를 별도 클러스터로 보고, 로드 밸런싱, blue/green, canary 배포를 적용하고, 내부 MCP‑서비스와 독립적으로 롤백하는 방법을 살펴봅니다.

요약하면, 예전에는 “App과 MCP‑서버가 있다”고 생각했다면, 이제는 “App, MCP Gateway, 여러 백엔드/Agents 클러스터, commerce‑백엔드가 있다”로 확장됩니다. 그리고 gateway 덕분에 ChatGPT 구성은 복잡해지지 않습니다 — 여전히 하나의 MCP 지점만 보입니다.

11. MCP Gateway 작업 시 흔한 실수

실수 №1: gateway를 “비즈니스 괴물”로 만드는 것.
흔한 함정입니다. 모든 요청이 gateway를 지나가니, 할인 계산, SKU 선택, 복잡한 카테고리 규칙, 프로모션 코드 검증까지 거기에 넣고 싶어집니다. 결과적으로 확장과 변경이 어려운 비대한 서비스를 얻게 되고, Gift API, Commerce API 등 전문 컴포넌트로 나눈 본래의 의미를 잃습니다. Gateway는 얇은 네트워크 레이어로 유지하고, 도메인 특화 로직은 해당 서비스에 남기세요.

실수 №2: gateway에 장기 사용자 상태를 저장하는 것.
“사용자 장바구니를 gateway 메모리에 저장하자”는 아이디어는 인스턴스가 하나일 때만 그럴듯합니다. 두 번째 인스턴스가 생기면 고통이 시작됩니다: 진짜 장바구니는 A와 B 중 어디에 있나요? 재시작하면요? Gateway는 stateless로 남아야 합니다. 많아야 작은 핸드셰이크/설정 캐시 정도이고, 세션/주문 상태는 DB나 전문 서비스에 보관해야 합니다.

실수 №3: ChatGPT를 내부 토폴로지에 정통하게 만드는 것.
ChatGPT에 여러 API 서버(Gift API, Commerce API 등)를 직접 노출하고, gateway를 “부분적으로만” 쓰기 시작하면, 단일 진입점이라는 가장 큰 장점을 잃습니다. 토폴로지를 바꿀 때마다 여러 곳의 설정을 바꿔야 합니다. MCP Gateway를 App의 공식 엔드포인트로 한 번만 제대로 설정하고, 모든 내부 변화를 그 뒤로 숨기는 편이 훨씬 간단합니다.

실수 №4: 크로스‑서비스 로직을 모든 백엔드에 중복 구현.
가끔 팀이 인증, rate limiting, 로깅, 로컬라이제이션을 각 REST‑서비스에 따로 구현하려 합니다. 그 결과 권한/제한 정책이 Gift API와 Commerce API에서 달라지고, App 동작이 예측 불가능해집니다. Gateway는 바로 이 것들을 중앙집중화하려고 존재합니다: 토큰 검증, 테넌트/로케일 결정, 호출 로깅, 제한 적용을 여기서 하고, 이후에 구체 서비스로 가세요.

실수 №5: 무거운 계산과 LLM 호출로 gateway를 과부하.
기술적으로는 gateway에서 LLM 모델을 추가로 호출하고, 복잡한 집계를 하거나, 오래 걸리는 배치 작업을 수행할 수도 있습니다. 하지만 그러면 곧 확장과 격리가 어려운 또 하나의 무거운 백엔드가 됩니다. Gateway는 빠르고 예측 가능해야 합니다: 가벼운 변환과 라우팅까지만. 무거운 작업은 REST‑서비스 내부나(또는 이 모듈에서 다룰) 큐/워커로 옮기세요.

실수 №6: 너무 이른 인프라 복잡화.
반대 극단은 작은 학습용 App에 대해 곧장 별도 Kubernetes 클러스터, NGINX 스택, Cloudflare Workers와 복잡한 설정을 쌓는 것입니다. 실제 트래픽과 가용성 요구가 없을 때는 의미가 없습니다. 하나의 MCP‑모놀리식이나 단순 Node‑gateway로 시작해도 충분하며, 성장에 따라 컴포넌트를 클러스터와 매니지드 서비스로 점진적으로 분리해도 늦지 않습니다.

코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION