- NDJSON(Newline Delimited JSON)μ μ€λ°κΏ(
\n)μΌλ‘ JSON κ°μ²΄λ₯Ό ꡬλΆνλ μ§λ ¬ν ν¬λ§· - κ° μ€μ΄ νλμ μμ ν JSON κ°μ²΄ β κ·Έκ² μ λΆ
- TCP, Unix pipe, stdio λ± μ€νΈλ¦¬λ° μ±λμμ μ¬λ¬ JSONμ μμλλ‘ μ μ‘ν λ μ¬μ©
- JSON-RPC, SSE κ°μ βνλ‘ν μ½βμ΄ μλλΌ ν¬λ§·(νμ) β μ€ μμ λ΄μ© ꡬ쑰λ₯Ό κ°μ νμ§ μμ
ν΄λΉ κ°λ μ΄ νμν μ΄μ
- μ€νΈλ¦¬λ° μ±λ(TCP, stdio, pipe)μμ μ¬λ¬ JSONμ λκΉ μμ΄ μμλλ‘ μ μ‘ν΄μΌ ν λ
- λ‘κ·Έ νμΌ, μ΄λ²€νΈ μ€νΈλ¦Ό, λμ©λ λ°μ΄ν°λ₯Ό ν μ€μ© μ½μΌλ©° μ²λ¦¬ν΄μΌ ν λ (λ©λͺ¨λ¦¬ ν¨μ¨)
- μλ²-ν΄λΌμ΄μΈνΈ κ° μ€μκ° μ΄λ²€νΈ pushκ° νμν λ (AI μλ΅ streaming, λ‘κ·Έ μμ§ λ±)
- JSON λ°°μ΄(
[{...},{...}])μ λκΉμ§ λ°μμΌ νμ± κ°λ₯νμ§λ§, NDJSONμ ν μ€ λμ°©ν λλ§λ€ μ¦μ μ²λ¦¬ κ°λ₯
AS-IS: JSON λ°°μ΄ β μ 체λ₯Ό λ°μμΌ νμ± κ°λ₯
sequenceDiagram autonumber participant Producer as Producer participant Consumer as Consumer Producer->>Consumer: [{...},{...},{...}] μ μ‘ μμ Note over Consumer: μμ§ νμ± λΆκ° (λ°°μ΄μ΄ λ«νμ§ μμ) Producer->>Consumer: μ μ‘ μλ£ (']' λμ°©) Consumer->>Consumer: μ 체 νμ± Note over Consumer: λ§μ§λ§ λ°μ΄νΈκΉμ§ κΈ°λ€λ €μΌ μ²λ¦¬ κ°λ₯
TO-BE: NDJSON β ν μ€λ§λ€ μ¦μ μ²λ¦¬ (Claude Code CLI μμ)
sequenceDiagram autonumber participant App as Application participant CLI as claude CLI (--output-format stream-json) CLI-->>App: {"type":"system","subtype":"init",...}\n App->>App: μ¦μ νμ± β μΈμ μ΄κΈ°ν μ²λ¦¬ CLI-->>App: {"type":"stream_event","event":{"delta":{"type":"text_delta","text":"Hello"}}}\n App->>App: μ¦μ νμ± β UIμ ν ν° μΆλ ₯ CLI-->>App: {"type":"result","result":"...","session_id":"abc"}\n App->>App: μ¦μ νμ± β μλ£ μ²λ¦¬
NDJSON ν¬λ§· κ·μΉ 3κ°μ§
κ·μΉ 1. κ° μ€ = νλμ μμ ν JSON
{"name":"Alice","age":30}\n
{"name":"Bob","age":25}\n
{"name":"Carol","age":35}\n
JSON λ΄λΆμ 리ν°λ΄ μ€λ°κΏμ΄ μμΌλ©΄ λ°λμ \\nμΌλ‘ escapeν΄μΌ νλ€. μ€λ°κΏ = λ©μμ§ κ΅¬λΆμμ΄κΈ° λλ¬Έμ΄λ€.
κ·μΉ 2. λ―Έλμ΄ νμ κ³Ό νμΌ νμ₯μ
| νλͺ© | κ° |
|---|---|
| λ―Έλμ΄ νμ | application/x-ndjson |
| νμΌ νμ₯μ | .ndjson |
| μΈμ½λ© | UTF-8 νμ |
HTTP API μλ΅μΌλ‘ μ€νΈλ¦¬λ°ν λ:
HTTP/1.1 200 OK
Content-Type: application/x-ndjson
{"id":1,"status":"processing"}\n
{"id":1,"status":"done","result":"ok"}\nνμΌλ‘ μ μ₯ν λ:
# logs.ndjson
{"ts":"2026-04-17T10:00:00Z","level":"info","msg":"μλ² μμ"}
{"ts":"2026-04-17T10:00:01Z","level":"info","msg":"μμ² μμ "}
{"ts":"2026-04-17T10:00:02Z","level":"error","msg":"DB μ°κ²° μ€ν¨"}κ·μΉ 3. λΉ μ€μ 무μ κ°λ₯
νμλ λΉ μ€(\n\n)μ 건λλΈ μ μλ€. heartbeatλ keep-alive μ©λλ‘ νμ©λλ€.
Claude Code CLIμ NDJSON ν΅μ
--output-format stream-json νλκ·Έλ‘ νμ±ννλ€. 곡μ λ¬Έμμμ βFormat: NDJSON (newline-delimited JSON)β μΌλ‘ λͺ
μνλ€.
λ¨λ°©ν₯ μΆλ ₯ μ€νΈλ¦Ό (κΈ°λ³Έ νμ©)
claude -p "Fix the bug in auth.py" \
--output-format stream-json \
--allowedTools "Read,Edit,Bash"{"type":"system","subtype":"init","session_id":"abc","model":"claude-sonnet-4-6",...}
{"type":"assistant","message":{"role":"assistant","content":[...]}}
{"type":"result","subtype":"success","result":"Fixed the bug.","session_id":"abc"}Delta Streaming (ν ν° λ¨μ μμ )
--include-partial-messages μΆκ° μ stream_event νμ
μ delta μ΄λ²€νΈκ° μΆκ°λλ€:
claude -p "Explain recursion" \
--output-format stream-json \
--include-partial-messages{"type":"stream_event","event":{"type":"content_block_delta","delta":{"type":"text_delta","text":"μ¬κ·"}}}
{"type":"stream_event","event":{"type":"content_block_delta","delta":{"type":"text_delta","text":"λ ν¨μκ°"}}}
{"type":"result","subtype":"success","result":"...","session_id":"abc"}jqλ‘ deltaλ§ μΆμΆ:
claude -p "Write a poem" --output-format stream-json --include-partial-messages | \
jq -rj 'select(.type == "stream_event" and .event.delta.type? == "text_delta") | .event.delta.text'μλ°©ν₯ ν΅μ (stdin + stdout λͺ¨λ NDJSON)
claude -p \
--input-format stream-json \
--output-format stream-json \
--replay-user-messagesstdinμ NDJSON νμμ μ¬μ©μ λ©μμ§λ₯Ό 보λ΄κ³ , stdoutμμ μ΄λ²€νΈ μ€νΈλ¦Όμ λ°λλ€:
β stdin: {"type":"user","message":{"role":"user","content":[{"type":"text","text":"Fix auth.py"}]}}
β stdout: {"type":"user","message":{...}} β --replay-user-messagesλ‘ echoλ¨
β stdout: {"type":"assistant","message":{...}}
β stdout: {"type":"result","result":"Fixed.","session_id":"abc"}Codex JSON-RPC 2.0 vs Claude SDKMessage
λ μμ€ν λͺ¨λ NDJSONμ μ μ‘ ν¬λ§·μΌλ‘ μ¬μ©νμ§λ§, μ€ μμ κ΅¬μ‘°κ° λ€λ₯΄λ€.
νλ‘ν μ½ λΉκ΅ν
| νλͺ© | Codex app-server | Claude Code CLI |
|---|---|---|
| μ μ‘ ν¬λ§· | NDJSON | NDJSON |
| λ΄λΆ νλ‘ν μ½ | JSON-RPC 2.0 (κΈ°μ‘΄ νμ€) | SDKMessage (Claude κ³ μ ) |
| 곡μ μ μ | jsonrpc.org μ€ν | SDKMessage νμ
μ λμ¨ (TypeScript SDK 곡μ λ¬Έμ) |
| λ©μμ§ κ΅¬μ‘° | jsonrpc / method / id / params | type / subtype / session_id |
| μμ²-μλ΅ λ§€μΉ | id νλλ‘ λ§€μΉ | μμ (λ¨λ°©ν₯ push) |
| ν΅μ λ°©ν₯ | μμ²βμλ΅ μλ°©ν₯ | μΆλ ₯ λ¨λ°©ν₯ (μ΅μ μΌλ‘ μλ°©ν₯ κ°λ₯) |
SDKMessage 곡μ νμ μ μ
type SDKMessage =
| SDKSystemMessage // μΈμ
μ΄κΈ°ν
| SDKAssistantMessage // Claude μλ΅
| SDKUserMessage // μ¬μ©μ μ
λ ₯
| SDKResultMessage // μ΅μ’
κ²°κ³Ό
| SDKPartialAssistantMessage // delta streaming (stream_event)
| SDKHookStartedMessage // hook μ΄λ²€νΈ
| SDKHookResponseMessage
| SDKRateLimitEvent
| SDKPluginInstallMessage
// ... μ΄ 20κ° νμ
μ€μ wire λ°μ΄ν° λΉκ΅
κ°μ μμ βFix the bugβλ₯Ό μμ²ν λ:
# Codex app-server (JSON-RPC 2.0 over NDJSON)
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β stdin: {"jsonrpc":"2.0","method":"turn/start","params":{"message":"Fix the bug"},"id":1}
β stdout: {"jsonrpc":"2.0","id":1,"result":{"threadId":"t_abc"}} β id:1 μλ΅ λ§€μΉ
β stdout: {"jsonrpc":"2.0","method":"item/agentMessage/delta","params":{"delta":"I'll fix"}}
β stdout: {"jsonrpc":"2.0","method":"item/agentMessage/delta","params":{"delta":" the bug"}}
β stdout: {"jsonrpc":"2.0","method":"turn/completed","params":{}}
# Claude Code CLI (SDKMessage over NDJSON)
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β μ€ν: claude -p "Fix the bug" --output-format stream-json
β stdout: {"type":"system","subtype":"init","session_id":"abc","model":"claude-sonnet-4-6"}
β stdout: {"type":"stream_event","event":{"delta":{"type":"text_delta","text":"I'll fix"}}}
β stdout: {"type":"stream_event","event":{"delta":{"type":"text_delta","text":" the bug"}}}
β stdout: {"type":"result","subtype":"success","result":"Fixed.","session_id":"abc"}
λ μΆλ ₯ λͺ¨λ μ€λ°κΏμΌλ‘ μλ₯Έλ€ = NDJSON λμΌ.
μ€ μμ κ΅¬μ‘°κ° method/id vs type/subtype = νλ‘ν μ½ λ€λ¦.
[Codex] μ€ = {"jsonrpc":"2.0", "method":"...", "id":1, "params":{...}}
β
μμ²-μλ΅ id λ§€μΉ μμ
[Claude] μ€ = {"type":"stream_event", "session_id":"abc", "event":{...}}
β
μΈμ
ꡬλΆλ§, id λ§€μΉ μμ
NDJSONμ βμ΄λ»κ² μλ₯Ό κ²μΈκ°β (μ€λ°κΏμΌλ‘ JSON ꡬλΆ)μ΄κ³ , νλ‘ν μ½μ βκ·Έ μμ 무μμ λ΄μ κ²μΈκ°βλ€.
μ΄λ²€νΈ νμ μ°Έμ‘° (SDKMessage)
type | subtype | μ€λͺ |
|---|---|---|
system | init | μΈμ μμ, λͺ¨λΈ/ν΄ μ 보 |
system | api_retry | API μ¬μλ μ΄λ²€νΈ |
assistant | β | Claude μλ΅ λ©μμ§ |
user | β | μ¬μ©μ λ©μμ§ (replay μ) |
stream_event | β | delta streaming μ΄λ²€νΈ |
result | success | μ΅μ’ κ²°κ³Ό + session_id |
result | error_* | μ€λ₯ κ²°κ³Ό |
μ°Έκ³ λ¬Έμ
- Claude Agent SDK TypeScript Reference β SDKMessage νμ μ μ, NDJSON λͺ μ
- Claude Code Headless / Programmatic Usage β stream-json CLI μ¬μ©λ²
- Stream responses in real-time β delta streaming μμΈ
- NDJSON Spec (GitHub) β NDJSON 곡μ μ€ν
- JSON-RPC β Codex app-serverκ° μ¬μ©νλ νλ‘ν μ½
- Codex SDK νκ³μ app-server λͺ¨λ_1 β Codex app-server ꡬν λΆμ