--- id: backend-nats-jetstream title: NATS JetStream — modern messaging category: Coding status: draft source_trust_level: B verification_status: conceptual created_at: 2026-05-09 updated_at: 2026-05-09 tags: [backend, messaging, vibe-coding] tech_stack: { language: "TS / Go", applicable_to: ["Backend"] } applied_in: [] aliases: [NATS, JetStream, pub/sub, KV store, persistent stream, NATS subject] --- # NATS JetStream > Kafka 보다 simple + 빠름. **NATS = pub/sub + request-reply. JetStream = persistent stream + KV + ObjectStore**. Single binary, multi-cluster, edge. ## 📖 핵심 개념 - Subject: routing key (`orders.user.123`). - Wildcard: `orders.user.*` (1 token), `orders.>` (rest). - JetStream: persistent. - Core NATS: ephemeral pub/sub. ## 💻 코드 패턴 ### Setup (single binary) ```bash # Download nats-server -js # JetStream enabled. ``` ### Connect (TS) ```ts import { connect, JSONCodec } from 'nats'; const nc = await connect({ servers: 'nats://localhost:4222' }); const jc = JSONCodec(); // Pub nc.publish('orders.created', jc.encode({ id: '1', total: 99 })); // Sub const sub = nc.subscribe('orders.>'); for await (const m of sub) { console.log(m.subject, jc.decode(m.data)); } ``` ### Request-reply (RPC) ```ts // Server const sub = nc.subscribe('rpc.add'); for await (const m of sub) { const { a, b } = jc.decode(m.data) as any; m.respond(jc.encode({ result: a + b })); } // Client const r = await nc.request('rpc.add', jc.encode({ a: 2, b: 3 }), { timeout: 1000 }); const result = jc.decode(r.data); // { result: 5 } ``` → Built-in RPC. ### JetStream (persistent stream) ```ts const jsm = await nc.jetstreamManager(); await jsm.streams.add({ name: 'ORDERS', subjects: ['orders.>'], retention: 'limits', // 또는 'workqueue', 'interest' max_msgs: 1_000_000, max_age: 7 * 24 * 3600 * 1e9, // 7 days }); // Publish const js = nc.jetstream(); await js.publish('orders.created', jc.encode({ id: '1' })); ``` ### Consumer (durable) ```ts const consumer = await js.consumers.get('ORDERS', 'order-processor'); const messages = await consumer.consume(); for await (const m of messages) { const data = jc.decode(m.data); await process(data); m.ack(); } ``` → Kafka consumer 와 비슷. Durable. ### Push vs Pull ```ts // Pull (default, robust) const consumer = await js.consumers.get('ORDERS', 'pull-c'); await consumer.consume(); // Push (server pushes to subject) await jsm.consumers.add('ORDERS', { durable_name: 'push-c', deliver_subject: 'inbox.push-c', }); ``` → Pull 가 modern default. ### Subject hierarchy ``` orders.user.123 # specific orders.user.* # any user (1 token) orders.> # any orders.* deep ``` ```ts nc.subscribe('orders.user.*'); // all users nc.subscribe('orders.>'); // all orders nc.subscribe('orders.user.alice'); // specific ``` ### Queue group (load balance) ```ts nc.subscribe('jobs', { queue: 'workers' }); ``` → "workers" group 의 1 instance 만 받음 (load balance). ### Retention ``` limits: max_msgs / max_age / max_bytes 까지 유지. workqueue: ack 즉시 삭제 (작업 queue). interest: subscriber 있는 동안 유지. ``` ### Ack 종류 ```ts m.ack(); // 성공 m.nak(); // 다시 deliver m.term(); // 영원히 X m.working(); // 진행 중 (timeout 연장) ``` ### Replay ```ts // 옛 message 다시 read const consumer = await jsm.consumers.add('ORDERS', { durable_name: 'replay-c', deliver_policy: 'by_start_time', opt_start_time: '2026-05-01T00:00:00Z', }); ``` → Time-based replay. ### Sequence-based ```ts deliver_policy: 'by_start_sequence', opt_start_seq: 12345, ``` → Specific sequence 부터. ### KV store ```ts const js = nc.jetstream(); const kv = await js.views.kv('config'); await kv.put('theme', jc.encode('dark')); const entry = await kv.get('theme'); console.log(jc.decode(entry.value)); // Watch const watch = await kv.watch(); for await (const e of watch) { console.log(e.key, jc.decode(e.value)); } ``` → Real-time config / state. ### Object store ```ts const os = await js.views.os('files'); // Upload await os.put({ name: 'image.jpg' }, fs.createReadStream('./image.jpg')); // Download const r = await os.get('image.jpg'); const stream = r.data; ``` → S3 비슷. NATS native. ### Cluster (HA) ```bash nats-server -c cluster.conf # cluster: routes = [...] ``` ```hcl cluster { port: 6222 routes = [ nats://node1:6222 nats://node2:6222 nats://node3:6222 ] } ``` → 3+ node = HA. ### Replication ```ts await jsm.streams.add({ name: 'ORDERS', subjects: ['orders.>'], num_replicas: 3, }); ``` → Stream 가 N 곳 복제. Raft consensus. ### Multi-cluster (super-cluster) ``` Region A cluster ↔ Region B cluster (gateway). - 매 region 가 자기 stream. - Cross-region 가 selective replicate. ``` → Multi-region 가 native. ### Leaf node (edge) ```hcl leafnodes { remotes = [{ url: "nats://main-cluster:7422" }] } ``` → Edge / IoT 의 작은 NATS 가 main cluster 와 연결. ### Encryption / Auth ```hcl # JWT-based (decentralized) operator: ./op.jwt resolver: MEM # 또는 nkeys # 또는 mTLS ``` → Multi-tenant 친화. ### vs Kafka ``` Kafka: - Persistent log - 매우 큰 throughput - 큰 ecosystem (Connect, Streams) - ZooKeeper / KRaft 필요 - 무거움 NATS JetStream: - Persistent stream + ephemeral pub/sub - 단일 binary - Cluster simple - KV / Object store native - Edge / multi-region 친화 → 큰 enterprise = Kafka. 작은-중간 = NATS. ``` ### vs RabbitMQ ``` RabbitMQ: - AMQP - 복잡 routing (exchange, binding) - Mature NATS: - Subject-based (simple) - 빠름 - KV / Object store → 단순 = NATS. 복잡 routing = RabbitMQ. ``` ### vs Redis Pub/Sub ``` Redis: ephemeral. NATS: ephemeral + persistent. → Redis 가 cache + pub/sub 가 main. NATS 가 messaging 가 main. ``` ### Use case ``` - Microservice 통신 (sync request-reply) - Event stream (async) - IoT / edge - Real-time config (KV watch) - File / blob (Object store) - LLM agent 의 message (Temporal alternative) ``` ### Production 설정 ```hcl listen: 4222 http: 8222 jetstream { store_dir: /data/nats max_memory_store: 1G max_file_store: 10G } cluster { name: prod port: 6222 routes: [...] } ``` ### Monitoring ``` NATS HTTP /varz, /connz, /streamz, /consumerz. Prometheus exporter 가 metric. NATS-CLI 가 admin. ``` ```bash nats stream list nats stream info ORDERS nats consumer list ORDERS ``` ### LLM agent ```ts // Agent 가 NATS 으로 통신 nc.subscribe('agent.task.>'); nc.publish('agent.result.123', encode(result)); // 또는 KV 가 state await kv.put('agent.state.123', encode({ step: 'reasoning' })); ``` → Temporal / Cadence alternative for simpler use. ## 🤔 의사결정 기준 | 상황 | 추천 | |---|---| | 단순 pub/sub | Redis / NATS Core | | Persistent + edge | NATS JetStream | | 큰 throughput | Kafka | | 복잡 routing | RabbitMQ | | AWS native | SNS / SQS | | Cloud managed NATS | Synadia | | IoT / edge | NATS leaf | | Microservice RPC | NATS request-reply | ## ❌ 안티패턴 - **Subject 가 random**: organization 깨짐. - **No queue group + parallel sub**: 중복 처리. - **WorkQueue retention + multi-consumer**: 1 consumer 만 받음. - **No replication**: data lose. - **Persistent everything**: 큰 disk. - **Wildcard 폭발 (`>`)**: 모든 subject 받음. - **Auth 없음**: 외부 노출. ## 🤖 LLM 활용 힌트 - NATS = simple Kafka alternative. - JetStream = persistent + KV + Object. - Subject 기반 routing 이 powerful. - Edge / multi-region 친화. ## 🔗 관련 문서 - [[Messaging_Kafka_Patterns]] - [[Messaging_NATS_RabbitMQ_Comparison]] - [[Backend_WebSocket_Scaling]]