JSON Mock-API 도구에 AI 도우미를 추가했습니다 — 하이브리드 설계와 Workers AI의 주의사항
요약
JSON Mock-API 도구인 Temp API에 AI 도우미를 통합하며 겪은 하이브리드 설계 방식과 Cloudflare Workers AI 사용 경험을 공유합니다. 데이터의 정확성을 보장하기 위해 결정론적 복구와 AI 기반 생성 기능을 엄격히 분리하는 설계 원칙을 강조합니다.
핵심 포인트
- 데이터 정확성을 위해 AI를 결정론적 복구 작업에서 완전히 격리함
- AI는 스키마 생성, 샘플 데이터 생성, 변경 사항 설명에만 활용
- Cloudflare Workers AI를 사용하여 에지 환경에서 모델 실행
- 사용자 신뢰를 위해 AI의 역할과 로컬 복구 기능을 명확히 구분
저는 TempTools를 운영하고 있습니다. 이는 가입이 필요 없고, 일정 시간이 지나면 만료되어 스스로 삭제되는 무료 웹 도구 모음입니다. 제가 가장 자주 사용하는 것은 Temp API입니다. JSON 또는 CSV를 붙여넣으면 몇 초 만에 라이브 Mock 엔드포인트(mock endpoint)를 생성할 수 있습니다.
최근 여기에 **AI 도우미 (AI helper)**를 추가했는데, 설계 과정이 단순히 "LLM을 호출하는 것"보다 훨씬 흥미롭게 흘러갔습니다. 제가 스스로 세운 규칙은 다음과 같습니다: AI는 절대로 정확성 (correctness)에 관여해서는 안 된다. 그 결과가 어떠했는지, 그리고 세 가지 기능 중 두 가지를 망가뜨렸던 Cloudflare Workers AI의 주의사항(gotcha)에 대해 공유하겠습니다.
도우미의 기능
Temp API 에디터에 세 가지 버튼이 있습니다:
- Fix & format (수정 및 포맷팅) — 지저분하거나 깨진 JSON을 정리하고 무엇이 변경되었는지 설명합니다.
- Generate schema (스키마 생성) — 데이터로부터 JSON Schema (draft 2020-12)를 생성합니다.
- Generate sample (샘플 생성) — 동일한 구조를 가진 현실적인 Mock 데이터(mock data)를 생성합니다.
설계 규칙: AI를 데이터로부터 격리하기
제가 원하지 않았던 상황은 이것입니다: AI가 "포맷팅"을 하는 척하면서 제 JSON을 조용히 다시 쓰는 (rewriting) 상황 말입니다. 만약 사용자가 {"id": 42}를 붙여넣었는데 도구가 {"id": 43}을 반환한다면, 그것은 수정이 아니라 한 시간 동안 추적해야 할 버그(bug)가 됩니다.
따라서 복구와 포맷팅은 **100% 결정론적 (deterministic)**으로 작동합니다. AI를 사용하지 않습니다. 저는 jsonrepair를 사용합니다:
export function formatOrRepair(input: string) {
try {
return { ok: true, formatted: JSON.stringify(JSON.parse(input), null, 2), repaired: false };
...
AI는 "대략적으로 맞으면" 괜찮고, 오염될 원본 데이터(source of truth)가 없는 작업에만 사용됩니다:
- 결정론적 복구(deterministic repair)가 무엇을 변경했는지 설명 (explaining)
- 스키마를 생성 (generating)
- 완전히 새로운 샘플 데이터를 생성 (generating)
이러한 분리는 카피라이팅(copy) 측면에서도 중요합니다. 이 기능을 "AI가 당신의 JSON을 수정해 줍니다!"라고 마케팅하고 싶은 유혹이 들 수도 있지만, 그것은 사실이 아니며 누군가는 이를 지적할 것입니다. UI에서는 복구가 로컬에서 실행됨을 명시하고, "AI"는 스키마/샘플/설명 용도로만 예약해 두었습니다. 이는 정직할 뿐만 아니라, 발생할 수 있는 온갖 종류의 불만을 사전에 방지합니다.
AI 호출 (Cloudflare Workers AI)
생성은 ai 바인딩을 사용하는 Workers AI에서 실행됩니다. 외부 API 키가 필요 없으며, 단순히 에지(edge)에서 실행됩니다:
export const AI_MODEL = "@cf/qwen/qwen2.5-coder-32b-instruct";
async function runText(ai, messages, maxTokens) {
...
generateSchema와 generateSample은 단순히 "마크다운 펜스(markdown fences) 없이 오직 순수 JSON(raw JSON)만 출력하라"는 시스템 프롬프트(system prompt)를 적용한 runText일 뿐이며, 파싱(parsing)하기 전에 방어적으로 혹시 모를 펜스나 산문(prose)을 제거합니다.
주의사항: response가 항상 문자열인 것은 아니다
한동안 저를 혼란스럽게 했던 버그가 여기 있습니다. 프로덕션(production) 환경에서:
- Fix & format은 (AI 설명 기능을 포함하여) 완벽하게 작동했습니다.
- Generate schema와 Generate sample은 모두 일반적인 502 오류와 함께 실패했습니다.
동일한 모델, 동일한 runText, 동일한 바인딩(binding)입니다. 그렇다면 왜 세 번의 AI 호출 중 하나만 성공하고 나머지 두 개는 실패했을까요?
임시로 응답(response)에서 실제 에러를 노출해 보았더니 다음과 같은 결과가 나왔습니다:
((intermediate value).response ?? "").trim is not a function
out.response가 문자열이 아니었기 때문에, .trim() 메서드가 존재하지 않았던 것입니다.
어떤 호출이 실패했는지를 확인하자 패턴이 명확해졌습니다. 설명(explanation) 프롬프트는 **산문(prose)**을 반환하므로 response가 문자열입니다. 반면 스키마(schema)와 샘플(sample) 프롬프트는 JSON을 반환합니다. 모델의 출력이 JSON일 때, Workers AI는 response를 문자열이 아닌 이미 파싱된 객체(object) 형태로 전달할 수 있습니다. 객체에 .trim()을 호출하면 에러가 발생합니다.
해결책은 지루하지만 알아둘 가치가 있습니다. response가 문자열이라고 가정하지 마세요.
async function runText(ai, messages, maxTokens) {
const out = await ai.run(AI_MODEL, { messages, max_tokens: maxTokens, temperature: 0.2 });
const r = (out as { response?: unknown }).response;
...
response가 문자열이면 그대로 사용합니다. 만약 객체(파싱된 JSON)라면 JSON.stringify를 통해 다시 문자열로 변환합니다. 이는 어차피 스키마/샘플 경로로 전달하고자 하는 방식과 정확히 일치합니다. null/undefined의 경우 크래시(crash)가 발생하는 대신 빈 문자열이 됩니다.
제가 계속해서 다시 배우고 있는 두 가지 디버깅 교훈은 다음과 같습니다:
- "어떤 호출은 작동하고, 어떤 것은 작동하지 않는다"는 것은 축복입니다. 작동하는 호출과 작동하지 않는 호출 사이의 차이점 그 자체가 버그입니다. 이 사례에서는 모델이나 바인딩 (binding)이 아니라 출력 타입 (output type) (산문 vs JSON)이 문제였습니다.
- 일반적인
catch → 502는 정답을 숨깁니다. 실제 에러 메시지를 출력하는 임시 코드 한 줄이 추측 게임을 단 한 줄의 수정으로 바꿔 놓았습니다. (그 후에는 다시 코드를 제거했습니다.)
저렴하고 남용에 강하게 유지하기
수정/포맷팅 (repair/formatting) 단계에서는 모델을 호출하지 않기 때문에, 일반적인 경우(유효해 보이는 JSON을 붙여넣고 포맷팅하기)에는 AI 비용이 0원입니다. 모델은 설명, 스키마 (schema), 또는 샘플을 요청할 때만 실행됩니다.
게다가, AI 호출은 아주 작은 롤링 로그 테이블 (rolling log table)을 사용하여 IP당 속도 제한 (rate-limited)이 적용되며 (제가 업로드 시 사용하는 것과 동일한 트릭입니다), 입력값은 모델에 도달하기 전에 크기 제한 (size-capped)이 적용됩니다. 이 모든 과정은 Cloudflare 무료 티어 (free tier) 범위 내에서 여유롭게 유지됩니다.
사용해 보기
temptools.webcli.jp/tools/temp-api 에서 바로 사용해 보실 수 있습니다. 거친 JSON을 붙여넣고 AI 버튼을 눌러보세요. 회원 가입은 필요 없으며, 생성한 엔드포인트 (endpoint)는 자동으로 만료됩니다.
Workers AI를 기반으로 구축하고 있다면, response 타입 관련 주의사항을 꼭 기억해 두시기 바랍니다. 그리고 Temp API에서 미흡한 점을 발견하신다면, 진심으로 의견을 듣고 싶습니다. 🛠️
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기