문서를 읽었고, TOON이 표 형식 데이터에서 토큰 수를 절반으로 줄인다는 것을 알고 있습니다. 이제 실제로 연결하고 싶습니다. 이 문서는 배관에 관한 것입니다: .toon 파일 읽고 쓰기, 시스템 경계에서 TOON 검증, TOON 요청 본문을 파싱하는 Express 미들웨어 구축, TOON을 LLM에 직접 피드하는 데이터베이스-에서-프롬프트 파이프라인 조립. 실제 코드, 실제 패턴 — 장난감 예시 없음.
설정
npm에서 패키지를 설치하세요. ESM 전용이므로 package.json에 "type": "module"이 있거나 .mjs 확장자를 사용해야 합니다. Node.js 18+이 필요한 전부입니다 — 설정 파일 없음, 플러그인 없음.
npm install @toon-format/toonimport { encode, decode } from '@toon-format/toon';
// That's it. encode() → TOON string, decode() → JS value.TOON 파일 읽고 쓰기
Node.js fs 모듈이 I/O를 처리합니다. 파일 내용을 decode()에 직접 전달하거나, 데이터를 encode()에 전달하고 결과를 디스크에 씁니다. 아래는 두 패턴 모두입니다 — 스크립트와 CLI 도구에는 동기적, 서버 라우트에는 비동기적.
// --- Sync (scripts, CLI tools) ---
import { readFileSync, writeFileSync } from 'fs';
import { encode, decode } from '@toon-format/toon';
// Read a .toon file and decode it to a JS value
const raw = readFileSync('./data/products.toon', 'utf8');
const products = decode(raw);
console.log(products); // → JS array or object
// Encode a JS value and write it to a .toon file
const inventory = [
{ sku: 'WDG-001', name: 'Widget A', qty: 142, price: 9.99 },
{ sku: 'WDG-002', name: 'Widget B', qty: 87, price: 14.49 },
{ sku: 'GDG-001', name: 'Gadget X', qty: 31, price: 49.99 },
];
writeFileSync('./data/inventory.toon', encode(inventory, { indent: 2 }), 'utf8');
// --- Async (server routes, pipelines) ---
import { promises as fs } from 'fs';
import { encode, decode } from '@toon-format/toon';
// Read
async function loadReportData(filePath) {
const raw = await fs.readFile(filePath, 'utf8');
return decode(raw); // throws if malformed — handle upstream
}
// Write
async function saveSnapshot(data, filePath) {
const toon = encode(data, { indent: 2 });
await fs.writeFile(filePath, toon, 'utf8');
}'utf8' 인코딩을 사용하세요. TOON 파일은 일반 텍스트입니다. 인코딩 인수를 생략하면 Buffer가 반환됩니다 — decode()는 문자열을 기대하며 Buffer가 전달되면 타입 오류를 발생시킵니다.시스템 경계에서 TOON 검증
decode()는 유효하지 않은 입력에서 예외를 발생시킵니다, 이는 파서에게 올바른 동작이지만 구조화된 결과가 필요한 API 경계나 메시지 큐 소비자에서는 불편합니다 — 잡히지 않은 예외가 아닌. 해결책은 발생을 반환 값으로 전환하는 얇은 래퍼입니다. Express 라우트 핸들러, 큐 프로세서, 외부 데이터가 시스템에 들어오는 어느 곳에서나 사용하는 패턴입니다.
import { decode } from '@toon-format/toon';
/**
* Safely parse a TOON string.
* Returns { valid: true, data } on success,
* or { valid: false, error } on failure — never throws.
*/
export function validateToon(input) {
if (typeof input !== 'string') {
return { valid: false, error: 'Input must be a string' };
}
try {
const data = decode(input);
return { valid: true, data };
} catch (err) {
return { valid: false, error: err.message };
}
}
Express 라우트나 큐 소비자에서의 사용은 동일합니다 — validateToon()을 호출하고, valid로 분기하며, data로 진행하거나 error 문자열과 함께 400을 반환하거나 메시지를 dead-letter 처리합니다. try/catch 패턴은 호출 코드를 깨끗하고 예측 가능하게 유지합니다.
// Example: queue consumer
queue.process('ingest-toon', async (job) => {
const result = validateToon(job.data.payload);
if (!result.valid) {
console.error('Rejecting malformed TOON:', result.error);
return; // dead-letter, skip, or throw depending on your queue
}
await db.insert(result.data);
});
Express를 위한 TOON 미들웨어 구축
express.json()은 application/json 본문을 파싱하고 결과를 req.body에 넣습니다. 다음은 application/toon에 대한 동일한 것입니다. 라우트 핸들러 앞에 넣으면 스택의 나머지 부분은 차이를 알 수 없습니다.
import { decode } from '@toon-format/toon';
/**
* Express middleware: parses application/toon request bodies
* and attaches the decoded value to req.body.
*/
export function toonBodyParser(req, res, next) {
const contentType = req.headers['content-type'] ?? '';
if (!contentType.includes('application/toon')) {
return next(); // not our content type, pass through
}
let body = '';
req.setEncoding('utf8');
req.on('data', (chunk) => { body += chunk; });
req.on('end', () => {
try {
req.body = decode(body);
next();
} catch (err) {
res.status(400).json({ error: 'Invalid TOON body', detail: err.message });
}
});
req.on('error', (err) => {
res.status(500).json({ error: 'Request stream error', detail: err.message });
});
}
// Wire it up:
// app.use(toonBodyParser);
// app.post('/api/import', (req, res) => {
// // req.body is already the decoded JS value
// res.json({ received: Array.isArray(req.body) ? req.body.length : 1 });
// });LLM에 보내기 전에 데이터베이스 결과를 TOON으로 변환
이것이 TOON이 구축된 패턴입니다. 데이터베이스를 쿼리하고, 행 배열을 돌려받고, TOON으로 인코딩하고, 프롬프트에 직접 넣습니다. LLM은 JSON의 키 반복 오버헤드 없이 모든 구조를 얻습니다. 다음은 node-postgres (pg)를 사용하는 현실적인 파이프라인입니다:
import pg from 'pg';
import { encode } from '@toon-format/toon';
const pool = new pg.Pool({ connectionString: process.env.DATABASE_URL });
async function buildOrderPrompt(customerId) {
// Step 1: query the database
const { rows } = await pool.query(
`SELECT order_id, created_at, status, total_cents, item_count
FROM orders
WHERE customer_id = $1
ORDER BY created_at DESC
LIMIT 50`,
[customerId]
);
if (rows.length === 0) {
return null;
}
// Step 2: encode rows to TOON
// encode() handles all quoting automatically — no pre-processing needed
const toonData = encode(rows, { indent: 2 });
// Step 3: build the prompt
return [
'Analyse the following order history for a customer support case.',
'Data is in TOON tabular format: name[count]{col1,col2,...}: followed by one row per line.',
'',
toonData,
'',
'Summarise any patterns that suggest the customer has a recurring issue.'
].join('\n');
}
// Calling code:
const prompt = await buildOrderPrompt('cust_8821');
if (prompt) {
const reply = await callLlm(prompt); // your LLM client here
console.log(reply);
}동일한 패턴은 모든 SQL 클라이언트나 ORM에 작동합니다 — Prisma, Drizzle, Knex, Sequelize — 쿼리가 일반 JS 객체를 반환하는 한. encode()는 첫 번째 행에서 키 이름을 가져와 열 헤더로 사용합니다; 후속 행은 쉼표로 구분된 값으로 작성됩니다. JSON 배열로 약 ~1,500 토큰이 드는 50행 결과 집합은 TOON으로 일반적으로 ~600~700 토큰이 듭니다.
오류 및 엣지 케이스 처리
배포 전에 알아두어야 할 몇 가지 사항들:
- LLM이 잘못된 TOON을 반환합니다. 모델이 항상 형식을 완벽하게 재현하지는 않으며, 특히 첫 번째 시도에서.
decode()를 try/catch로 감싸거나(또는 위의validateToon()사용). 실패하면 원시 응답을 기록하고 호출자에게 오류를 반환하고 — 구조화된 출력이 안정적으로 필요한 경우 — 명시적인 수정 프롬프트로 재시도를 추가하세요: "마지막 응답이 유효한 TOON이 아니었습니다. 다시 형식을 맞춰주세요." - 쉼표나 콜론이 포함된 값. TOON은 쉼표를 사용하여 값을 구분하고 콜론을 객체 구문에 사용합니다 — 둘 다 의미 있는 문자입니다.
encode()가 이것을 자동으로 감지하고 영향 받는 값을 이중 따옴표로 묶습니다. 데이터를 사전 처리할 필요가 없습니다; 원시 문자열을 그냥 전달하세요. - Null과 undefined.
encode()는null을null(베어, 따옴표 없음)로 직렬화하고undefined속성은 완전히 생략합니다 —JSON.stringify()와 동일한 동작. 디코딩 시, 베어null은 JSnull로 반환됩니다. - 빈 배열.
encode([])는 유효한 빈 TOON 배열을 반환합니다.decode()는 깔끔하게 반환합니다. LLM 프롬프트에 빈 데이터셋이 포함되지 않아야 하는 경우 업스트림에서 보호하세요. - 매우 큰 결과 집합. 라이브러리에 하드 제한이 없지만, LLM에는 컨텍스트 창 제한이 있습니다. 인코딩 전에 쿼리를 페이지네이션하거나
LIMIT을 적용하세요 — 대부분의 프롬프트에는 100~200행이 합리적인 상한선입니다.
validateToon()을 실행하세요. 잘못된 페이로드가 DB 레이어에 도달하게 허용하면 경계에서 잡는 것보다 디버깅이 훨씬 어려워집니다.마무리
이 문서의 패턴들은 실제 Node.js 코드베이스에 TOON을 통합하는 데 필요한 대부분을 커버합니다: 파일 I/O를 위한 fs, 안전한 경계 파싱을 위한 validateToon() 래퍼, application/toon 본문을 위한 드롭인 Express 미들웨어, SQL 행을 토큰 효율적인 LLM 입력으로 변환하는 DB-에서-프롬프트 파이프라인. 라이브러리 자체 — @toon-format/toon — 는 방해가 되지 않습니다: 두 가지 함수, 설정 없음, 유효하지 않은 입력에서 예외 발생. 개발 중 출력을 확인하려면 TOON Validator를 사용하고, 인코딩된 데이터를 검사하려면 TOON Formatter를, LLM에 붙여넣기 전에 기존 데이터셋을 변환하려면 JSON to TOON을, 디코딩된 응답을 JSON을 기대하는 다운스트림 시스템에 넘겨줘야 할 때는 TOON to JSON을 사용하세요.