MCP 서버 응답 포맷 전쟁: XML vs JSON vs TOON

Minhan Cho·2026년 3월 30일

MCP 개발기

목록 보기
4/5

이 글은 LexLink MCP 서버의 응답 포맷을 XML에서 JSON 또는 TOON으로 전환할지 결정하기 위해 진행한 PoC 결과를 정리한 글입니다.


0. 배경: 왜 포맷을 바꾸려 했나

LexLink는 국가법령정보 Open API(law.go.kr)를 MCP 프로토콜로 감싸서, AI 에이전트가 한국 법률 데이터에 접근할 수 있게 해주는 서버다. 현재 44개 tool을 제공하고 있고, Smithery.aiKakao PlayMCP를 통해 서비스 중이다.

문제는 토큰이었다.

MCP 서버가 LLM에 데이터를 넘길 때, 포맷에 따라 같은 데이터도 토큰 소비량이 크게 달라진다. XML은 닫는 태그 때문에 가장 비싸고, PlayMCP는 응답 크기 20KB 제한도 있다. 그러던 중 LLM 토큰 최적화를 위해 새로 나온 포맷인 TOON이 눈에 들어왔다.

그래서 물어봤다 — XML, JSON, TOON 중 뭐가 최선인가?

감이 아니라 숫자로 답하기로 했다.


1. TOON이 뭔데?

TOON (Token-Oriented Object Notation)은 LLM에 구조화 데이터를 보낼 때 토큰을 최소화하도록 설계된 새로운 직렬화 포맷이다. 스펙 v3.0 Working Draft 단계.

핵심 아이디어는 간단하다: 반복되는 키를 한 번만 쓴다.

JSON은 배열 안의 각 객체마다 키를 반복한다:

{"law": [
  {"법령명한글": "민법", "법령ID": "001706", "공포일자": "20260317"},
  {"법령명한글": "형법", "법령ID": "001692", "공포일자": "20260101"}
]}

TOON은 헤더에 키를 한 번 선언하고, 데이터는 CSV처럼 쓴다(자세한 사항은 TOON github 참조):

law[2]{"법령명한글","법령ID","공포일자"}:
  민법,001706,20260317
  형법,001692,20260101

리스트형 데이터(검색 결과)에서 이게 극적인 차이를 만든다. 결과가 10개, 20개씩 나오면 같은 키가 10번, 20번 반복되는데, TOON은 딱 한 번.

다만 걱정되는 점이 있었다:

  • Python SDK가 아직 개발 중
  • LLM들이 TOON을 제대로 파싱할 수 있을까?
  • 법률 텍스트처럼 산문(prose) 위주 데이터에서도 효과가 있을까?

2. 실험 설계

규모

  • 102개 API 호출 × XML, JSON 2개 포맷 = 204 HTTP 요청
  • JSON → TOON 변환은 클라이언트 측에서 수행
  • 16개 tool 카테고리 전수 커버 (법령검색, 판례, 조약, 자치법규, 위원회 결정문 등)
  • 6개 이상 쿼리 세트 (민법, 형법, 담보권, 횡령, 무역, 청소년 등)
  • 10개 LLM 모델 × 3 포맷 × 5 쿼리 = 150 LLM API 호출

측정 지표

지표측정 방법
토큰 수tiktoken o200k_base (GPT-4o/Claude 호환)
바이트 크기UTF-8 인코딩 기준
API 응답 시간law.go.kr 서버 응답 median (3회)
LLM 파싱 정확도3개 필드 추출 exact match
PlayMCP 적합률20KB 미만 여부

테스트 모델 (10개, 3개 프로바이더)

ProviderModels
Googlegemini-2.0-flash, 2.5-flash, 2.5-pro, 3-flash-preview, 3.1-pro-preview
OpenAIgpt-4.1-mini, gpt-4.1, o4-mini
Anthropicclaude-haiku-4-5, claude-sonnet-4-6

3. 결과: 토큰 효율성

전체 요약 (102 쿼리)

포맷총 토큰XML 대비 절감
XML179,736
JSON152,675-15.1%
TOON92,089-48.8%

TOON이 XML 대비 거의 절반의 토큰을 사용한다. 눈으로 보면 이렇다:

카테고리별 토큰 절감률

카테고리JSON 절감TOON 절감비고
법령검색 (eflaw/law)-18.8%-63.0%리스트 데이터 → TOON 최강
영문법령 (elaw)-16.4%-57.7%
행정규칙 (admrul)-17.9%-59.5%
판례 (prec)-12.1%-49.9%
조약 (trty)-14.1%-52.4%
자치법규 (ordin)-18.6%-60.4%
위원회결정문 (committee)-15.1%-47.6%
특별행정심판 (special)-16.7%-53.3%
지식베이스 (kb)-20.9%-51.4%
AI 검색 (aiSearch)-9.6%-25.9%본문 포함이라 절감 제한적
AI 연관법령 (aiRltLs)-16.4%-66.0%TOON 최대 절감
서비스 (본문 조회)-8.0%-14.5%산문 위주 → 포맷 효과 미미
연계정보 (linkage)-14.9%-46.8%

패턴이 명확했다:

  • 리스트형 데이터 (검색 결과): TOON이 45~67% 절감 — 테이블 헤더 한 번 선언의 위력
  • 산문형 데이터 (판례 본문): 세 포맷 모두 비슷함 — 본문 텍스트가 대부분이라 포맷 오버헤드가 미미

실제 예시: eflaw_search(민법, 10건)

포맷토큰바이트
XML2,9008,126
JSON2,3427,852
TOON1,0102,550

같은 데이터를 TOON으로 보내면 XML의 1/3 토큰이면 된다.


4. 결과: API 레이턴시

도구 유형XML 평균JSON 평균차이
검색 도구205ms239msJSON +17% 😐
서비스 도구369ms418msJSON +13%
AI 검색410ms440msJSON +7%

law.go.kr은 원래 XML을 기본으로 만들어져서, JSON은 서버 측에서 XML→JSON 변환하는 오버헤드가 있다. 다만 절대값으로 보면 200ms → 240ms 수준이라, 체감상 문제는 없다.

TOON은 JSON을 클라이언트에서 변환하는 것이므로 추가 지연은 ~1ms로 무시 가능.


5. 결과: LLM 파싱 정확도 — 이게 진짜 중요하다

토큰이 아무리 적어도, LLM이 데이터를 못 읽으면 의미가 없다.

10개 모델에 XML, JSON, TOON 세 포맷으로 같은 데이터를 주고 3개 필드를 추출하라고 시켰다. 5개 다른 쿼리 유형(법령, 판례, 조약, 행정규칙, 자치법규) × 각 5건 = 모델당 25건.

결과 테이블

ModelProviderXMLJSONTOONTOON 레이턴시
gemini-2.0-flashGoogle25/25 ✅25/25 ✅21/25 ❌ (84%)2.4s
gemini-2.5-flashGoogle25/25 ✅25/25 ✅25/25 ✅5.2s
gemini-2.5-proGoogle25/25 ✅25/25 ✅25/25 ✅11.4s
gemini-3-flash-previewGoogle25/25 ✅25/25 ✅25/25 ✅8.0s
gemini-3.1-pro-previewGoogle25/25 ✅25/25 ✅25/25 ✅14.0s
gpt-4.1-miniOpenAI25/25 ✅25/25 ✅25/25 ✅3.8s
gpt-4.1OpenAI25/25 ✅25/25 ✅25/25 ✅3.2s
o4-miniOpenAI25/25 ✅25/25 ✅20/25 ❌ (80%)11.4s
claude-haiku-4-5Anthropic25/25 ✅25/25 ✅25/25 ✅1.7s
claude-sonnet-4-6Anthropic25/25 ✅25/25 ✅25/25 ✅4.0s

포맷별 총계

포맷정확도
XML250/250 (100%)
JSON250/250 (100%)
TOON241/250 (96.4%)

무슨 일이 있었나

XML과 JSON은 10개 모델 전원 100%. 단 하나의 실수도 없었다.

TOON은 2개 모델에서 실패:

  • gemini-2.0-flash (84%): 가장 오래된/빠른 Gemini 모델. TOON의 CSV 테이블 형식을 정확히 파싱하지 못한 케이스 발생
  • o4-mini (80%): 추론 모델인데, 자치법규 데이터에서 일부 TOON 행을 잘못 해석. 게다가 TOON 처리에 11.4초나 걸렸다 (JSON은 5.4초)

흥미로운 점:

  • Claude 모델들은 TOON을 완벽하게 처리했고, haiku-4-5는 오히려 TOON에서 가장 빠른 응답(1.7초)을 보여줬다
  • GPT-4.1 계열도 TOON 100% 정확도, 레이턴시 패널티 없음
  • 하지만 같은 회사의 o4-mini(추론 특화 모델)는 TOON에서 실패 — 모델 특성에 따라 결과가 갈린다

6. PlayMCP 20KB 제한은?

이게 원래 TOON을 검토한 이유였다. 판례 본문(prec_service)이 20KB를 넘기는 문제.

결론부터 말하면: 포맷으로는 안 된다.

시나리오XMLJSONTOON<20KB?
민법 (전문)1,175KB1,149KB1,006KB✗ 셋 다 안 됨
형법 (전문)421KB406KB359KB
판례 22854129.4KB29.4KB29.2KB
민법 제3조 (단일 조문)2.4KB2.3KB2.1KB✓ 셋 다 OK

법률 전문은 200KB~1.2MB. TOON이 15% 깎아봤자 여전히 수십~수백 KB.

산문형 법률 텍스트는 어떤 포맷을 써도 압축이 안 된다. 내용 자체가 크기의 대부분을 차지하기 때문.

이 문제는 결국 sections="summary" 파라미터로 해결했다 — 판례 서비스 도구에 요약 모드를 추가해서 판례내용(본문)을 빼고 판시사항+판결요지만 반환하게 하니까 29.4KB → 7.2KB로 줄었다. 결국 문제는 포맷이 아니라 아키텍처였나...


7. 결론: 그래서 뭘 골랐나

JSON을 골랐다.

XMLJSON ✅TOON
토큰 절감-15%-49%
LLM 파싱100%100%96.4%
API 레이턴시가장 빠름+7~17%+7~17%
SDK불필요stdlib개발 중
리스크없음낮음중상

TOON의 49% 절감은 매력적이지만:
1. LLM 2개 모델이 실패한다 — 실패한 모델은 gemini-2.0-flash, o4-mini 2건밖에 없었지만, 실 사용자들이 어떤 LLM을 사용할지 모르고, 앞으로 나올 모델들이 TOON을 소화할지 확신이 없었다.
2. Python SDK가 아직 개발 중 — 자체 컨버터를 유지보수해야 함
3. 스펙이 v3.0 Working Draft — 바뀔 수 있음
4. 산문형 데이터에는 효과 없음 — LexLink의 서비스 도구(판례/법령 본문)에서는 무의미

JSON은:
1. 10개 모델 전원 100% 파싱 성공
2. 15% 토큰 절감 — 꾸준하고 안정적
3. 의존성 제로 — Python stdlib
4. 7~17% 레이턴시 증가 — 200ms → 240ms, 체감 불가

프로덕션에서 3.6%의 파싱 실패율은 받아들일 수 없다.
다시 말하지만, 운영은 지옥이다.

TOON은 나중에 재검토한다:

  • Python SDK 안정화 시
  • LLM 학습 데이터에 TOON이 포함되기 시작할 때
  • 포맷 자체가 더 널리 채택될 때

8. TOON에 대한 개인적 평가

TOON은 좋은 아이디어다. 리스트형 데이터에서 60%+ 토큰 절감은 실제로 인상적이고, MCP 서버처럼 구조화 데이터를 LLM에 대량 전달하는 유스케이스에 딱 맞는 포맷이다.

하지만 아직 때가 아니다.

LLM들이 JSON/XML 위주로 학습되어 있고, TOON의 CSV-like 테이블 형식은 일부 모델에서 혼동을 일으킨다. 특히 추론 모델(o4-mini)이 TOON에서 오히려 2배 느려진 건 의외였다 — 익숙하지 않은 포맷을 "이해"하는 데 추론 토큰을 더 쓰는 것으로 보인다.

그래도 6개월~1년 후면 상황이 달라질 수 있다. TOON이 학습 데이터에 포함되고, SDK가 안정화되면, 특히 검색 결과 위주의 MCP 서버에서는 게임 체인저가 될 가능성이 있다.

지금은 JSON으로 간다. 그래도 TOON, 기억해두자.


References

profile
multidisciplinary

0개의 댓글