MCP가 바꾸는 연동 비용: 시스템마다 도구를 새로 만드는 반복을 끊다
붙일 시스템이 늘 때마다 도구 정의와 스키마를 새로 쓰는 일이 반복됐습니다. MCP를 표준 인터페이스로 두고 연동 비용이 곱셈에서 덧셈으로 바뀌는 지점을 정리했습니다.

TL;DR
- 도구를 직접 붙이면 연동 비용이 에이전트 수 × 시스템 수로 늘어납니다. MCP는 이걸 합으로 바꾸려는 시도입니다.
- 이득은 "한 번 만들면 여러 곳에서 쓴다"가 아니라 "같은 규약을 쓰니 갈아끼울 수 있다" 쪽이 큽니다.
- 그렇다고 공짜는 아닙니다. 프로세스 하나가 더 늘고, 인증·권한 경계가 한 겹 더 생깁니다.
- 붙일 대상이 하나뿐이면 MCP를 쓸 이유가 없습니다. 셋을 넘어갈 때부터 계산이 바뀝니다.
지난 편에서 도구를 추리고 권한을 작업 단위로 끊었습니다. 하네스가 정리되니 에이전트가 훨씬 안정적으로 움직였습니다.
그런데 다음 요구가 들어왔습니다. "같은 걸 다른 서비스에도 붙여 주세요."
그때 알았습니다. 제가 만든 건 그 서비스 전용이었습니다. @Tool로 감싼 메서드, 그 스키마, 그 인증 방식이 전부 한 애플리케이션에 묶여 있었습니다. 다른 곳에 붙이려면 처음부터 다시 였습니다.
비용이 곱으로 늘어나는 구조
붙일 것이 늘어날수록 무엇이 늘어나는지 세어봤습니다.
| 개별 연동 | 표준 프로토콜 | |
|---|---|---|
| 도구 정의 | 에이전트마다 새로 작성 | 서버에 한 번 |
| 스키마 | 팀·서비스마다 제각각 | 규약 고정 |
| 인증 | 연동마다 별도 설계 | 전송 계층에서 공통 처리 |
| 교체 | 붙인 쪽 코드를 고쳐야 함 | 서버만 갈아끼움 |
| 총 구현 수 | N × M | N + M |
에이전트가 3개, 붙일 시스템이 4개면 개별 연동은 12벌입니다. 표준을 두면 7벌입니다. 숫자가 커질수록 차이가 벌어집니다.
MCP(Model Context Protocol)는 이 N × M을 N + M으로 바꾸려는 규약입니다. 도구·리소스·프롬프트를 서버가 제공하고, 클라이언트(에이전트)가 공통 방식으로 발견해서 씁니다.
기존 REST 연동과 뭐가 다른가
처음 들었을 때 든 의문이 이거였습니다. "REST API 있는데 왜 또?"
직접 붙여보고 정리한 차이입니다.
| 항목 | REST API 직접 호출 | MCP |
|---|---|---|
| 발견 | 문서 읽고 사람이 도구로 감쌈 | 런타임에 목록 조회 |
| 설명 | OpenAPI 설명 ≠ LLM용 설명 | LLM이 읽을 것을 전제로 작성 |
| 스키마 | 엔드포인트마다 제각각 | 도구 스키마 규약 고정 |
| 상태 | 요청-응답 단발 | 세션 · 리소스 구독 가능 |
| 권한 | 서비스마다 다름 | 서버 경계에서 일괄 |
핵심은 "발견"입니다. REST는 사람이 문서를 읽고 @Tool 메서드를 만들어야 합니다. MCP는 클라이언트가 서버에 "너 뭐 할 수 있어?"를 물어 목록을 받아옵니다. 도구가 추가돼도 에이전트 코드를 안 고칩니다.
두 번째로 중요한 게 설명의 용도입니다. OpenAPI의 description은 개발자가 읽으라고 쓴 문장입니다. MCP의 도구 설명은 모델이 읽고 고르라고 쓰는 문장입니다. 지난 편에서 "하지 말아야 할 것을 적는 게 효과가 컸다"고 했는데, 그게 들어갈 자리가 규약으로 생긴 셈입니다.
기존 서비스에 서버를 얹을 때
이미 돌아가는 Spring 애플리케이션이 있다면, 서비스 로직을 다시 짤 일은 없습니다. 노출 계층만 하나 더 만드는 구조가 됩니다.
@Component
public class OrderMcpTools {
private final OrderQueryService orderQueryService; // 기존 서비스 그대로
@McpTool(
name = "order.find",
description = """
주문번호로 단건 주문의 상태와 결제 정보를 조회합니다.
기간 검색이나 목록 조회에는 사용하지 마세요. order.search 를 쓰세요.
""")
public OrderView find(
@McpParam(description = "주문번호. 예: 20260917-0032") String orderNo) {
return orderQueryService.findByOrderNo(orderNo);
}
}바뀐 건 어노테이션과 설명 문장뿐입니다. OrderQueryService는 건드리지 않았습니다. Tool Calling을 붙였을 때와 코드 모양이 거의 같은데, 그 정의가 애플리케이션 밖에서도 통한다는 점만 다릅니다.
그래서 실제로 뭐가 좋아졌나
솔직히 말하면 "한 번 만들어 여러 곳에서 쓴다"는 기대만큼은 아니었습니다. 도구 하나 재사용하자고 프로토콜을 얹는 건 과합니다.
실제로 체감한 이득은 다른 데 있었습니다.
첫째, 교체가 가능해졌습니다. 조회 대상을 사내 DB에서 외부 SaaS로 바꿔야 했을 때, 서버 쪽 구현만 갈아끼우고 에이전트는 건드리지 않았습니다. 이전 같으면 도구 정의부터 다시 썼을 일입니다.
둘째, 권한 경계가 한 곳으로 모였습니다. 예전엔 도구마다 권한 검사를 흩뿌렸는데, 이제 서버 경계에서 봅니다. 지난 편의 작업 단위 권한 등급을 서버 하나에 적용하면 그 서버가 제공하는 도구 전체에 걸립니다.
셋째, 도구 목록이 코드에서 빠졌습니다. 어떤 도구를 등록할지 에이전트가 런타임에 정합니다. 컨텍스트 예산 관점에서도 유리합니다 — 필요한 서버만 연결하면 그만큼만 실립니다.
공짜가 아닌 부분
도입하면서 치른 비용도 분명히 있었습니다.
| 비용 | 내용 |
|---|---|
| 프로세스 증가 | 서버가 하나 더 뜹니다. 배포·모니터링 대상이 늘어납니다 |
| 인증 이중화 | 에이전트 → 서버 인증과, 서버 → 원 시스템 인증이 따로 생깁니다 |
| 디버깅 경로 | 실패 지점이 한 단계 늘어납니다. 추적 없이는 어디서 깨졌는지 모릅니다 |
| 버전 관리 | 서버가 제공하는 도구를 바꾸면 여러 클라이언트가 동시에 영향받습니다 |
세 번째가 특히 아팠습니다. 도구 호출이 실패했는데 에이전트 문제인지, 서버 문제인지, 원 시스템 문제인지 구분이 안 됐습니다. 분산 추적을 붙여둔 게 여기서 값을 했습니다. 트레이스 ID를 MCP 호출에 실어 보내니 한 줄로 이어져 보였습니다.
언제 쓰고 언제 쓰지 말까
판단 기준을 이렇게 정리했습니다.
| 상황 | 판단 |
|---|---|
| 붙일 시스템이 1개 | 쓰지 않습니다. 직접 @Tool이 훨씬 간단 |
| 붙일 시스템 2~3개, 에이전트 1개 | 아직 애매. 중복 정도를 보고 판단 |
| 시스템 3개 이상 × 에이전트 2개 이상 | 씁니다. 곱셈이 체감되기 시작 |
| 외부 팀·외부 조직에 노출 | 씁니다. 규약이 계약 역할을 함 |
| 도구가 자주 바뀜 | 씁니다. 런타임 발견의 이득이 큼 |
| 지연에 극도로 민감 | 재검토. 홉이 하나 늘어납니다 |
"표준이니까 일단 쓰자"는 잘못된 이유입니다. 붙일 게 하나뿐인데 프로토콜을 얹으면 프로세스만 늘어납니다. REST API 버전 관리를 정리할 때와 같은 결론이었습니다 — 추상화는 대상이 둘 이상일 때부터 값을 합니다.
정리
MCP를 한 줄로 요약하면 "도구를 애플리케이션 안에서 밖으로 꺼내는 일"입니다.
꺼내면 재사용·교체·권한 통합이라는 이득이 생기고, 대신 프로세스와 홉이 늘어납니다. 그 교환이 유리해지는 지점은 붙일 것이 많아졌을 때입니다.
지금 한 곳에만 붙이고 있다면 아직입니다. 다만 두 번째 요청이 들어오는 순간 계산이 바뀐다는 건 기억해 둘 만합니다. 저는 그 두 번째에서야 알았습니다.
다음 편
도구를 정리하고 표준으로 꺼내는 동안, 정작 그 코드를 쓰는 도구 자체도 빠르게 바뀌고 있었습니다. 코딩 에이전트를 몇 개 바꿔 쓰며 기준이 생겼습니다.
다음 편에서는 코딩 에이전트를 무엇으로 고를 것인가를 다룹니다.