reopt designreopt design
DocsExploreToolsPricingBuilder
시작하기
시작하기
Playground
핵심 개념
핵심 개념
Editor
Editor 개요
블록 타입
스트리밍
Markdown 변환
구축·운영
Skills
앱 조합 가이드
AI 연동
Authoring 플레이북
커스텀 블록
릴리즈 노트
Oopt-editor
reopt designreopt design

AI 시대를 위한 디자인 시스템

  • 문서
  • 가격
  • 릴리즈 노트
  • GitHub
  • 서비스 약관
  • 개인정보처리방침

© 2026 reopt-ai. All rights reserved.

Editor
  1. 문서
  2. /
  3. Editor
  4. /
  5. 스트리밍 프로토콜

스트리밍 프로토콜

RFC 6902 NDJSON 패치 프로토콜, StreamCompiler, useEditorStream 훅, 재시도/이어쓰기 메커니즘을 다룹니다.

reopt design · 업데이트 2026년 6월 26일

시작하기Playground핵심 개념개요블록 타입스트리밍Markdown 변환Skills앱 조합 가이드AI 연동Authoring 플레이북커스텀 블록릴리즈 노트

1. NDJSON 패치 프로토콜

AI 에이전트는 RFC 6902 JSON Patch 연산을 한 줄씩 출력합니다. 각 줄은 독립된 JSON 객체입니다.

jsonl
{"op":"add","path":"/elements/h1","value":{"id":"h1","type":"heading","props":{"text":"Title","level":1}}}
{"op":"add","path":"/root/-","value":"h1"}
{"op":"add","path":"/elements/p1","value":{"id":"p1","type":"paragraph","props":{"text":"Hello **world**"}}}
{"op":"add","path":"/root/-","value":"p1"}

2. 핵심 규칙

  1. 1. 원소 먼저, root 나중 — /elements/id에 원소를 추가한 뒤 /root/-에 ID를 추가
  2. 2. ID 일치— element.id는 /elements/{key}의 key와 동일해야 함
  3. 3. Inside-out 중첩 — table: cell → row → table 순서로 생성
  4. 4. 텍스트 업데이트— replace 연산으로 /elements/{id}/props/text 수정
  5. 5. 인라인 마크다운 — 텍스트에 **bold**, *italic*, `code`, ~~strike~~, [link](url) 사용 가능

3. 테이블 스트리밍 예제

3단 중첩 (cell → row → table) inside-out 패턴:

jsonl
{"op":"add","path":"/elements/tc1","value":{"id":"tc1","type":"table-cell","props":{"text":"Name","header":true}}}
{"op":"add","path":"/elements/tc2","value":{"id":"tc2","type":"table-cell","props":{"text":"Role","header":true}}}
{"op":"add","path":"/elements/tr1","value":{"id":"tr1","type":"table-row","props":{},"children":["tc1","tc2"]}}
{"op":"add","path":"/elements/tc3","value":{"id":"tc3","type":"table-cell","props":{"text":"Alice","header":false}}}
{"op":"add","path":"/elements/tc4","value":{"id":"tc4","type":"table-cell","props":{"text":"Engineer","header":false}}}
{"op":"add","path":"/elements/tr2","value":{"id":"tr2","type":"table-row","props":{},"children":["tc3","tc4"]}}
{"op":"add","path":"/elements/t1","value":{"id":"t1","type":"table","props":{},"children":["tr1","tr2"]}}
{"op":"add","path":"/root/-","value":"t1"}

4. StreamCompiler

부분 청크를 버퍼링하고 완전한 JSON 줄만 파싱합니다. 잘못된 JSON은 자동 스킵합니다.

tsx
import { StreamCompiler } from "@reopt-ai/opt-editor";

const compiler = new StreamCompiler();

// 부분 청크 입력
const ops1 = compiler.feed('{"op":"add","path":');    // [] (불완전)
const ops2 = compiler.feed('"/root/-","value":"p1"}\n'); // [{ op: "add", ... }]

// 스트림 종료 시
const remaining = compiler.flush();

compiler.status;       // "done"
compiler.appliedCount; // 성공 파싱된 패치 수

5. useEditorStream

tsx
import { useEditorStream, createEditorStore } from "@reopt-ai/opt-editor";

const store = createEditorStore();
const { start, resume, abort, status, error, appliedCount } = useEditorStream(store);

// 시작
await start(readableStream);

// 실패 시 이어쓰기 (같은 패치 시퀀스를 재생하는 새 스트림)
if (status === "error") {
  const newStream = await fetchNewStream();
  await resume(newStream);
  // → appliedCount만큼 자동 스킵 후 나머지만 적용
}

// 중단
abort();

6. 재시도/이어쓰기

스트림이 네트워크 오류로 중단되면, 같은 패치 시퀀스를 재생하는 새 스트림으로 resume()을 호출합니다.

  • appliedCount — 이미 적용된 패치 수 (UI 진행률 표시에도 사용)
  • resume(source) — appliedCount만큼 스킵 후 나머지만 store에 적용
  • start(source) — 전체 초기화 후 처음부터 시작

7. AI 프롬프트 자동 생성

catalog.prompt()는 등록된 모든 블록 타입, 스트리밍 규칙, 전체 예제를 포함하는 시스템 프롬프트를 생성합니다. catalog.jsonSchema()는 Structured Output API용 JSON Schema를 생성합니다.

tsx
const systemPrompt = catalog.prompt();
// → "# Document Schema\n## Structure\n..."

const jsonSchema = catalog.jsonSchema();
// → { $schema: "...", title: "EditorSpec", ... }

// AI API 호출
const response = await anthropic.messages.create({
  system: systemPrompt,
  messages: [{ role: "user", content: "Write an article about React" }],
  stream: true,
});
Previous블록 타입15개 블록 타입의 Props, 중첩 구조, 인라인 마크, AI 프롬프트 생성 방식Editor
블록 타입 페이지로 이동
NextMarkdown 변환specToMarkdown, markdownToSpec — AI 컨텍스트 전달과 마크다운 가져오기Editor