# JSONLLM:  JSON 을 위한 바이브 모델

나는 빠르고 똑똑한 JSON 생성 API가 필요하다. AI 기반 앱을 만들다 보면 타입이 있는 데이터를 생성해야 하는 일이 정말 많다. 그때마다 출력 형식이 제대로 맞지 않거나, 결과를 기다리는 시간이 너무 길어서 고생한다.

그래서 [JSONLLM](https://github.com/delinoio/jsonllm)이라는 작은 POC를 만들었다. 내가 이 모델을 서빙해서 사업을 하려는 것은 아니다. OpenAI나 Anthropic처럼 기술력과 자원이 있는 회사가 이 문제를 제대로 풀어줬으면 한다. 하지만 내가 필요하다고 말한다고 해서 만들어줄 곳들은 아니니, 개인적으로라도 직접 실험해보고 결과를 공개하려고 했다.

나는 AI를 전문적으로 연구하는 사람이 아니라 개발자다. LLM으로 어디까지 할 수 있는지 궁금해서 이런저런 시도를 많이 해봤다. 요즘은 LLM이 코드를 잘 작성하니, 모델 훈련도 바이브코딩으로 진행했다. 그 과정에서 시행착오도 꽤 겪었다.

이번 실험에서는 필드를 묶어서 처리하는 방식의 효과를 확인했다. 동시에 정확도와 긴 응답의 지연 시간, 동시 요청 처리에서는 큰 한계도 확인했다. 이 글은 그 가능성과 한계를 함께 소개하려는 글이다.

개발하면서 반복해서 부딪힌 문제는 구조화된 출력이었다. 여기서 말하는 문제는 JSON 스키마 자체를 작성하는 일이 아니다. 스키마를 입력으로 주고, 그 스키마에 맞는 결과를 생성하는 과정이다. Structured Outputs를 사용할 수 있어도, 내가 만들려는 사용자 경험에 적용하기에는 너무 느리다고 느꼈다. 출력이 조금만 커져도 기다리는 시간이 심각해졌다.

JSON은 표현이 장황한 포맷이다. 모델이 JSON 전체를 텍스트로 생성하면 값뿐 아니라 필드 이름과 구조를 표현하는 토큰까지 출력해야 한다. 애플리케이션이 이미 알고 있는 구조도 생성 과정에 들어간다. 나는 이 방식에 줄일 수 있는 일이 많다고 생각했다.

이번 POC의 주 실험 대상은 GenUI다. 쇼핑몰이나 개인 비서처럼 GenUI를 적용할 수 있는 영역이 분명히 보이고, 앞으로 중요한 사용자 경험이 될 수 있다고 생각한다. 그런데 UI를 JSON으로 표현하고 그것을 모델이 생성하는 구조에서는 JSON 생성 속도가 그대로 사용자 경험의 제약이 된다. 화면에 필요한 데이터가 늦게 나오면, 사용자는 그만큼 기다려야 한다.

이 문제를 고민하던 중 [json-render에 Jev를 붙여본 실험](https://x.com/ctatedev/status/2101022101750571357)을 봤다. 원래부터 갖고 있던 고민이 그 게시물을 보면서 조금 더 구체적인 아이디어가 됐다. 내가 원한 것은 컴포넌트 선택을 넘어 프로퍼티의 값과 JSON 트리까지 빠르게 만들어내는 방식이었다.

이 과정에서 [TypeLLM](https://github.com/TypeLLM/TypeLLM)을 많이 참고했고, 아이디어도 많이 얻었다. TypeLLM은 기존 모델의 구조나 가중치를 바꾸지 않고 타입이 있는 출력을 생성한다. 문자열과 숫자 생성, 독립적인 필드의 병렬 처리, 필드 사이의 의존 관계, 공통 문맥 재사용도 이미 지원한다. 이런 기능 자체를 JSONLLM의 새로운 아이디어라고 주장하려는 것은 아니다.

JSONLLM에서는 이 실행 방식에 맞춰 모델을 학습시키는 실험을 했다. Qwen3.5-4B를 개별 필드의 판단과 값 생성에 맞춰 LoRA로 미세조정하고, 전용 런타임과 연결했다.

애플리케이션은 구조와 규칙을 제공한다. 모델은 아직 결정되지 않은 값에 답한다. 계산, 기존 데이터의 정확한 복사, 상태 변경, 최종 JSON 조립은 코드가 처리한다. 독립적인 필드는 함께 처리하고, 다른 필드의 답이 필요한 경우에는 의존 관계에 따라 순서대로 처리한다. 공통 문맥을 한 번 처리한 뒤 그 캐시를 각 필드에서 재사용하는 방식도 구현했다. [설계 문서](https://github.com/delinoio/jsonllm/blob/main/docs/design.md)

예를 들어 저장소의 주문 예제에는 “배송지를 바꾸고 싶고, 급하지는 않다”는 요청이 있다. 모델이 판단하는 것은 어떤 컴포넌트를 사용할지, 긴급한 요청인지 같은 항목이다. 주문 번호와 주소를 가져오고, 단가와 수량을 곱하고, 최종 UI 데이터를 조립하는 일은 코드가 담당한다.

현재 구현에서는 애플리케이션이 컴포넌트와 구조를 미리 정의한다. 임의의 JSON 트리를 자유롭게 설계하거나 일반적인 워크플로우를 만들어내는 단계까지 구현한 것은 아니다. 내가 기대하는 최종 모습과 지금 만든 POC의 범위에는 아직 거리가 있다.

모델은 Qwen3.5-4B를 사용했다. 개인적으로 진행하는 실험이고, 이걸로 돈을 벌려는 것도 아니어서 처음부터 큰 모델에 많은 비용을 쓰기는 어려웠다. 학습 데이터는 8,000개, 검증과 테스트 데이터는 각각 1,000개였고, 한국어와 영어를 절반씩 포함했다. 합성 데이터 생성에는 DeepSeek 모델을 사용했다. 필드 단위로 변환한 학습 예제 15,619개로 한 번의 epoch를 학습하는 데 약 102분이 걸렸다. [모델 카드](https://huggingface.co/kdy1/JSONLLM-Qwen3.5-4B-v0.1)

비용은 생각보다 많이 들었다. 훨씬 저렴하게 만들 수도 있었을 텐데, AI를 잘 모르다 보니 비효율적으로 진행한 부분이 있었다. 작업용 레포지토리도 여러 개 만들었고, 훈련도 여러 번 했다. 초기에 약관 확인을 제대로 하지 못해 다시 훈련한 일도 있었다. 지금 정리한 모델은 약관을 확인하고 다시 훈련한 결과물이다.

첫 번째 작업용 레포에서는 약 24달러를 썼다. 두 번째 레포의 비용은 다음과 같다.

| 작업 | 비용 |
| --- | --- |
| 초기 학습 및 속도 재현 실험 | $140 |
| 문맥 공유 모델 학습 및 평가 | $232 |
| 지연 최적화 A/B 테스트 | $22.50 |
| 데이터 합성, 재훈련 및 평가 | $29.07 |
| 합계 | $423.57 |

이후 비교 벤치마크에 약 64.44달러가 추가됐다. 앞선 시행착오까지 합하면 전체 지출은 약 512달러다. 추가 벤치마크 비용은 설치, 실패한 시도, 스토리지와 결과 회수까지 포함한 추정치다. 저장소에 기록된 마지막 학습·평가 비용 29.07달러를 전체 프로젝트 비용으로 보면 안 된다.

돈이 조금 아깝기는 했다. 그래도 실험을 시작했으면 끝까지 확인해봐야 한다고 생각해서 밀어붙였다. 지금 공개를 준비하는 저장소는 그 과정을 정리한 버전이다.

비교 벤치마크에서는 모델 가중치를 고정하고, 새로운 합성 과제로 실행 방식의 차이를 확인했다. JSON 전체 생성, 필드별 순차 생성, 독립 필드 배치 처리, 공통 문맥을 공유하는 필드 처리, vLLM의 JSON 생성 방식을 비교했다. 원본 Qwen과 미세조정한 JSONLLM 모두 같은 H100 80GB에서 평가했고, 계획한 540개 실험을 완료했다. 이 단계에서는 재훈련하지 않았다. [비교 방법과 전체 결과](https://github.com/delinoio/jsonllm/blob/main/docs/design-benchmark.md)

가장 명확한 긍정적인 결과는 독립 필드의 배치 처리였다. 같은 과제끼리 지연 시간을 비교했을 때, 필드를 하나씩 처리하는 방식보다 두 모델 모두 중앙값 기준 약 3.14배의 속도 개선이 있었다. 원본 모델의 정확도는 같았고, JSONLLM은 256개 중 한 개를 더 맞혔다. 이 표본에서는 정확도를 떨어뜨리지 않고 독립적인 작업을 함께 처리하는 효과가 나타났다.

공통 문맥 공유의 결과는 조건에 따라 달랐다. 짧은 문맥을 사용하는 기본 과제에서는 일반 배치 처리보다 느렸다. 공통 문맥을 반복해서 처리하는 양은 줄었지만, 캐시를 복사하는 작업도 발생했다. 문맥을 길게 만든 탐색 실험에서는 공유 방식이 더 빨랐다. JSONLLM에서는 공통 문맥이 약 513토큰일 때 약 1.46배, 1,026토큰일 때 약 1.64배 빨랐다. 다만 각 조건의 고유 과제가 32개였으므로, 더 큰 표본으로 확인할 필요가 있다.

그리고 JSON 전체 생성과 비교하면 정확도 문제가 드러났다. JSONLLM의 기본 과제 결과는 다음과 같다. 지연 시간은 다섯 번의 실험에서 얻은 통계의 중앙값이며, 정확도는 한 레코드의 모든 필드가 맞아야 정답으로 계산했다.

| 실행 방식 | 정확도 | p50 지연 | p95 지연 | 초당 완전 정답 수 |
| --- | --- | --- | --- | --- |
| JSON 전체 생성 | 78.52% | 2,262ms | 3,816ms | 0.308 |
| 필드별 순차 생성 | 64.84% | 595ms | 7,376ms | 0.297 |
| 독립 필드 배치 처리 | 65.23% | 191ms | 4,913ms | 0.499 |
| 문맥 공유 필드 처리 | 65.23% | 250ms | 4,900ms | 0.490 |
| vLLM JSON 생성 | 75.39% | 323ms | 851ms | 1.781 |

문맥 공유 방식은 JSON 전체 생성보다 중앙값 지연 시간이 짧았다. 하지만 정확도가 약 13.3%p 낮았고, p95 지연 시간도 더 길었다. 따라서 이 결과로 “같은 품질의 JSON을 더 빠르게 생성했다”고 말할 수는 없다.

vLLM은 이 비교에서 초당 완전 정답 수가 가장 많았고 p95도 가장 짧았다. 동시 요청을 늘리는 실험에서도 현재 JSONLLM 런타임보다 유리했다. JSONLLM에는 요청 단위의 잠금이 남아 있어서, 한 요청 안의 필드를 함께 처리하더라도 여러 요청을 효율적으로 처리하는 데는 한계가 있었다.

트리와 워크플로우 실험의 결과는 더 좋지 않았다. 여덟 개 후보 노드의 존재 여부, 종류, 연결 관계를 선택하는 제한된 과제에서도 필드별 생성 방식은 완전 정답 기준 0%였다. 형식에 맞는 값을 출력한다고 해서 올바른 구조가 만들어지는 것은 아니었다.

처음의 제한된 평가에서 얻었던 높은 정확도를 새로운 과제에 그대로 적용할 수 없다는 점도 확인했다. 이번 결과는 독립 필드 배치 처리의 효과를 보여주지만, 현재 모델과 런타임이 일반적인 JSON 생성이나 GenUI를 해결했다는 근거는 되지 않는다. TypeLLM과의 직접 성능 비교도 아직 하지 않았다.

그럼에도 내가 이 문제를 계속 중요하게 보는 이유는 단순하다. 나는 지금도 앱을 만들면서 이런 API가 필요하다. GenUI뿐 아니라 워크플로우나 일반적인 트리 형태의 데이터를 생성할 때도, 타입이 있는 값을 빠르고 정확하게 얻는 기능이 필요하다.

내 설계가 정답인지는 모르겠다. 이번 실험에서도 잘된 부분과 실패한 부분이 함께 나왔다. 다만 모델이 무엇을 판단하고, 코드가 무엇을 처리하며, 독립적인 판단을 어떻게 함께 실행할지를 모델과 런타임 양쪽에서 다뤄볼 가치가 있다고 생각한다.

이걸 내가 직접 서비스로 운영할 계획은 없다. 모델을 서빙할 자본도 넉넉하지 않고, 루나급의 지능을 가진 모델을 직접 만들고 운영하는 일도 쉽지 않다. OpenAI나 Anthropic 같은 회사가 더 좋은 모델을 기반으로 증류를 하든, 실행 방식을 개선하든, 빠른 구조화 데이터 생성에 맞는 모델과 API를 만들어줬으면 한다. 그리고 OpenRouter, Vercel AI Gateway, Cloudflare AI Gateway 같은 곳에서 편하게 사용할 수 있으면 좋겠다.

코드는 [GitHub](https://github.com/delinoio/jsonllm)에, 모델 가중치와 학습·평가 자료는 [Hugging Face](https://huggingface.co/kdy1/JSONLLM-Qwen3.5-4B-v0.1)에 정리했다. 코드와 파생 모델, 포함된 합성 데이터는 Apache-2.0 라이선스로 공개되어있다.

이 실험이 널리 알려져서, 더 잘 만들 수 있는 사람들이 이 문제에 관심을 가져줬으면 한다. 조금 특이한 목적일 수도 있지만, 나는 그런 API가 정말 필요하다.
