--- id: backend-grpc-streaming-deep title: gRPC Streaming — bidirectional / server / client category: Coding status: draft source_trust_level: B verification_status: conceptual created_at: 2026-05-09 updated_at: 2026-05-09 tags: [backend, grpc, streaming, vibe-coding] tech_stack: { language: "TS / Go", applicable_to: ["Backend"] } applied_in: [] aliases: [gRPC streaming, bidi, server stream, client stream, Protocol Buffers, Connect-RPC, Buf] --- # gRPC Streaming Deep > gRPC 의 큰 차별점. **4 mode: unary, server stream, client stream, bidi**. HTTP/2 + Protobuf. ## 📖 핵심 개념 - HTTP/2 multiplexing. - Protobuf binary (작은). - Type-safe (proto → 매 언어). - Bidi 가 WebSocket 식. ## 💻 코드 패턴 ### Proto definition ```protobuf syntax = 'proto3'; service ChatService { rpc Send(Message) returns (Ack); // unary rpc Subscribe(Filter) returns (stream Message); // server stream rpc Upload(stream Chunk) returns (FileInfo); // client stream rpc Chat(stream Message) returns (stream Message); // bidi } ``` ### Server (TS, Connect-RPC) ```ts import { ConnectRouter } from '@connectrpc/connect'; import { ChatService } from './gen/chat_connect'; export default (router: ConnectRouter) => router.service(ChatService, { async send(req) { return { ack: true }; }, async *subscribe(req) { for (let i = 0; i < 100; i++) { yield { text: `msg ${i}` }; await sleep(1000); } }, async upload(req) { let total = 0; for await (const chunk of req) { total += chunk.data.length; } return { size: total }; }, async *chat(req) { for await (const msg of req) { yield { text: 'echo: ' + msg.text }; } }, }); ``` ### Client (TS) ```ts import { createPromiseClient } from '@connectrpc/connect'; const client = createPromiseClient(ChatService, transport); // Server stream for await (const msg of client.subscribe({ topic: 'news' })) { console.log(msg.text); } // Client stream async function* chunks() { for (const chunk of file) yield { data: chunk }; } const result = await client.upload(chunks()); ``` ### Connect-RPC (modern) ``` gRPC + gRPC-Web + Connect protocol 동시. - Browser 에서 직접 (no envoy). - HTTP/1.1 + HTTP/2 둘 다. - TS-friendly. → gRPC 의 modern. ``` ### Buf (toolchain) ```yaml # buf.gen.yaml version: v2 plugins: - remote: buf.build/connectrpc/es out: gen - remote: buf.build/bufbuild/es out: gen ``` ```bash buf generate buf lint buf breaking --against '.git#branch=main' ``` → gRPC 의 npm. ### Bidi streaming ```ts // Server async *chat(reqs) { for await (const msg of reqs) { // 매 client message 받음. yield { reply: process(msg) }; } } // Client async function* messages() { yield { text: 'hi' }; await sleep(1000); yield { text: 'how are you?' }; } for await (const reply of client.chat(messages())) { console.log(reply); } ``` → Real-time bidirectional. Chat / game. ### Server stream use case ``` - Server-sent updates. - Live data feed. - Search results (incremental). - Log streaming. ``` ### Client stream use case ``` - Large upload (chunks). - Sensor data submission. - Telemetry batch. ``` ### Cancellation ```ts const ac = new AbortController(); setTimeout(() => ac.abort(), 5000); for await (const msg of client.subscribe({}, { signal: ac.signal })) { // ... } ``` → Stream 중단. ### Error handling ```ts try { for await (const msg of stream) { // ... } } catch (e) { if (e.code === Code.Cancelled) ...; if (e.code === Code.DeadlineExceeded) ...; } ``` ### Deadline ```ts const r = await client.send(req, { timeoutMs: 5000 }); ``` ### Interceptor (middleware) ```ts const authInterceptor: Interceptor = (next) => async (req) => { req.header.set('Authorization', `Bearer ${token}`); return next(req); }; const transport = createConnectTransport({ baseUrl: '...', interceptors: [authInterceptor], }); ``` ### vs REST ``` REST: - HTTP/1.1. - JSON (verbose). - Stateless. - 큰 ecosystem. gRPC: - HTTP/2 multiplexing. - Protobuf (작은). - Streaming native. - Type-safe (codegen). → Internal microservice = gRPC. Public API / web = REST / GraphQL. ``` ### vs WebSocket ``` WebSocket: bidi 만, schema 없음. gRPC bidi: typed schema + RPC. → gRPC 가 strict schema. WS 가 free-form. ``` ### vs GraphQL Subscription ``` GraphQL Subscription: query 식 + WS. gRPC streaming: RPC 식 + HTTP/2. → Different model. 같은 use case 가 가능. ``` ### Performance ``` gRPC 가 REST 보다 7-10x 빠름 (binary + multiplex). Streaming = 큰 throughput (multiple parallel). HTTP/2 head-of-line blocking 가 약간 (TCP level). → Internal high-performance. ``` ### gRPC-Web ``` Browser 가 native gRPC 안 됨. - gRPC-Web (Envoy 가 transcode). - 또는 Connect (native). → Connect 가 modern (envoy 없음). ``` ### Production ``` - Envoy (proxy). - gRPC LB (HTTP/2 LB). - Auth interceptor. - Tracing (OpenTelemetry). - Monitoring (latency p99, error rate). ``` ### Common pitfall ``` - gRPC LB 가 HTTP/2 aware 안 = sticky. - Stream 의 backpressure 무시. - Deadline 없음 = hang. - Compression 가 default off (큰 payload 가 비효율). - Schema breaking change. ``` ## 🤔 의사결정 기준 | 작업 | 추천 | |---|---| | Internal microservice | gRPC | | Streaming | gRPC bidi | | Web client | Connect / gRPC-Web | | Public API | REST + OpenAPI | | Real-time chat | gRPC bidi 또는 WS | | File upload | gRPC client stream | ## ❌ 안티패턴 - **gRPC + REST 둘 다 같은 service**: complexity. - **No deadline**: hang. - **gRPC + non-HTTP/2 LB**: sticky. - **모든 거 unary (streaming 안 사용)**: gRPC 의 가치 ↓. - **Schema breaking change**: client 깨짐. ## 🤖 LLM 활용 힌트 - Connect-RPC 가 modern (browser-friendly). - Buf 가 toolchain. - 4 mode (unary, server, client, bidi). - Internal microservice 의 default. ## 🔗 관련 문서 - [[Backend_gRPC_Patterns]] - [[Backend_GraphQL_Federation]] - [[Backend_WebSocket_Production]]