이 글은 LexLink MCP 서버의 응답 포맷을 XML에서 JSON 또는 TOON으로 전환할지 결정하기 위해 진행한 PoC 결과를 정리한 글입니다.
LexLink는 국가법령정보 Open API(law.go.kr)를 MCP 프로토콜로 감싸서, AI 에이전트가 한국 법률 데이터에 접근할 수 있게 해주는 서버다. 현재 44개 tool을 제공하고 있고, Smithery.ai와 Kakao PlayMCP를 통해 서비스 중이다.
문제는 토큰이었다.
MCP 서버가 LLM에 데이터를 넘길 때, 포맷에 따라 같은 데이터도 토큰 소비량이 크게 달라진다. XML은 닫는 태그 때문에 가장 비싸고, PlayMCP는 응답 크기 20KB 제한도 있다. 그러던 중 LLM 토큰 최적화를 위해 새로 나온 포맷인 TOON이 눈에 들어왔다.
그래서 물어봤다 — XML, JSON, 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은 딱 한 번.
다만 걱정되는 점이 있었다:
| 지표 | 측정 방법 |
|---|---|
| 토큰 수 | tiktoken o200k_base (GPT-4o/Claude 호환) |
| 바이트 크기 | UTF-8 인코딩 기준 |
| API 응답 시간 | law.go.kr 서버 응답 median (3회) |
| LLM 파싱 정확도 | 3개 필드 추출 exact match |
| PlayMCP 적합률 | 20KB 미만 여부 |
| Provider | Models |
|---|---|
| gemini-2.0-flash, 2.5-flash, 2.5-pro, 3-flash-preview, 3.1-pro-preview | |
| OpenAI | gpt-4.1-mini, gpt-4.1, o4-mini |
| Anthropic | claude-haiku-4-5, claude-sonnet-4-6 |
| 포맷 | 총 토큰 | XML 대비 절감 |
|---|---|---|
| XML | 179,736 | — |
| JSON | 152,675 | -15.1% |
| TOON | 92,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% |
패턴이 명확했다:
| 포맷 | 토큰 | 바이트 |
|---|---|---|
| XML | 2,900 | 8,126 |
| JSON | 2,342 | 7,852 |
| TOON | 1,010 | 2,550 |
같은 데이터를 TOON으로 보내면 XML의 1/3 토큰이면 된다.
| 도구 유형 | XML 평균 | JSON 평균 | 차이 |
|---|---|---|---|
| 검색 도구 | 205ms | 239ms | JSON +17% 😐 |
| 서비스 도구 | 369ms | 418ms | JSON +13% |
| AI 검색 | 410ms | 440ms | JSON +7% |
law.go.kr은 원래 XML을 기본으로 만들어져서, JSON은 서버 측에서 XML→JSON 변환하는 오버헤드가 있다. 다만 절대값으로 보면 200ms → 240ms 수준이라, 체감상 문제는 없다.
TOON은 JSON을 클라이언트에서 변환하는 것이므로 추가 지연은 ~1ms로 무시 가능.
토큰이 아무리 적어도, LLM이 데이터를 못 읽으면 의미가 없다.
10개 모델에 XML, JSON, TOON 세 포맷으로 같은 데이터를 주고 3개 필드를 추출하라고 시켰다. 5개 다른 쿼리 유형(법령, 판례, 조약, 행정규칙, 자치법규) × 각 5건 = 모델당 25건.
| Model | Provider | XML | JSON | TOON | TOON 레이턴시 |
|---|---|---|---|---|---|
| gemini-2.0-flash | 25/25 ✅ | 25/25 ✅ | 21/25 ❌ (84%) | 2.4s | |
| gemini-2.5-flash | 25/25 ✅ | 25/25 ✅ | 25/25 ✅ | 5.2s | |
| gemini-2.5-pro | 25/25 ✅ | 25/25 ✅ | 25/25 ✅ | 11.4s | |
| gemini-3-flash-preview | 25/25 ✅ | 25/25 ✅ | 25/25 ✅ | 8.0s | |
| gemini-3.1-pro-preview | 25/25 ✅ | 25/25 ✅ | 25/25 ✅ | 14.0s | |
| gpt-4.1-mini | OpenAI | 25/25 ✅ | 25/25 ✅ | 25/25 ✅ | 3.8s |
| gpt-4.1 | OpenAI | 25/25 ✅ | 25/25 ✅ | 25/25 ✅ | 3.2s |
| o4-mini | OpenAI | 25/25 ✅ | 25/25 ✅ | 20/25 ❌ (80%) | 11.4s |
| claude-haiku-4-5 | Anthropic | 25/25 ✅ | 25/25 ✅ | 25/25 ✅ | 1.7s |
| claude-sonnet-4-6 | Anthropic | 25/25 ✅ | 25/25 ✅ | 25/25 ✅ | 4.0s |
| 포맷 | 정확도 |
|---|---|
| XML | 250/250 (100%) |
| JSON | 250/250 (100%) |
| TOON | 241/250 (96.4%) |
XML과 JSON은 10개 모델 전원 100%. 단 하나의 실수도 없었다.
TOON은 2개 모델에서 실패:
흥미로운 점:
이게 원래 TOON을 검토한 이유였다. 판례 본문(prec_service)이 20KB를 넘기는 문제.
결론부터 말하면: 포맷으로는 안 된다.
| 시나리오 | XML | JSON | TOON | <20KB? |
|---|---|---|---|---|
| 민법 (전문) | 1,175KB | 1,149KB | 1,006KB | ✗ 셋 다 안 됨 |
| 형법 (전문) | 421KB | 406KB | 359KB | ✗ |
| 판례 228541 | 29.4KB | 29.4KB | 29.2KB | ✗ |
| 민법 제3조 (단일 조문) | 2.4KB | 2.3KB | 2.1KB | ✓ 셋 다 OK |
법률 전문은 200KB~1.2MB. TOON이 15% 깎아봤자 여전히 수십~수백 KB.
산문형 법률 텍스트는 어떤 포맷을 써도 압축이 안 된다. 내용 자체가 크기의 대부분을 차지하기 때문.
이 문제는 결국 sections="summary" 파라미터로 해결했다 — 판례 서비스 도구에 요약 모드를 추가해서 판례내용(본문)을 빼고 판시사항+판결요지만 반환하게 하니까 29.4KB → 7.2KB로 줄었다. 결국 문제는 포맷이 아니라 아키텍처였나...
JSON을 골랐다.
| XML | JSON ✅ | 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은 나중에 재검토한다:
TOON은 좋은 아이디어다. 리스트형 데이터에서 60%+ 토큰 절감은 실제로 인상적이고, MCP 서버처럼 구조화 데이터를 LLM에 대량 전달하는 유스케이스에 딱 맞는 포맷이다.
하지만 아직 때가 아니다.
LLM들이 JSON/XML 위주로 학습되어 있고, TOON의 CSV-like 테이블 형식은 일부 모델에서 혼동을 일으킨다. 특히 추론 모델(o4-mini)이 TOON에서 오히려 2배 느려진 건 의외였다 — 익숙하지 않은 포맷을 "이해"하는 데 추론 토큰을 더 쓰는 것으로 보인다.
그래도 6개월~1년 후면 상황이 달라질 수 있다. TOON이 학습 데이터에 포함되고, SDK가 안정화되면, 특히 검색 결과 위주의 MCP 서버에서는 게임 체인저가 될 가능성이 있다.
지금은 JSON으로 간다. 그래도 TOON, 기억해두자.