Files
2nd/10_Wiki/Topics/Coding/Backend_gRPC_Streaming_Deep.md
T
2026-05-10 22:08:15 +09:00

6.0 KiB

id, title, category, status, source_trust_level, verification_status, created_at, updated_at, tags, tech_stack, applied_in, aliases
id title category status source_trust_level verification_status created_at updated_at tags tech_stack applied_in aliases
backend-grpc-streaming-deep gRPC Streaming — bidirectional / server / client Coding draft B conceptual 2026-05-09 2026-05-09
backend
grpc
streaming
vibe-coding
language applicable_to
TS / Go
Backend
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

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)

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)

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)

# buf.gen.yaml
version: v2
plugins:
  - remote: buf.build/connectrpc/es
    out: gen
  - remote: buf.build/bufbuild/es
    out: gen
buf generate
buf lint
buf breaking --against '.git#branch=main'

→ gRPC 의 npm.

Bidi streaming

// 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

const ac = new AbortController();
setTimeout(() => ac.abort(), 5000);

for await (const msg of client.subscribe({}, { signal: ac.signal })) {
  // ...
}

→ Stream 중단.

Error handling

try {
  for await (const msg of stream) {
    // ...
  }
} catch (e) {
  if (e.code === Code.Cancelled) ...;
  if (e.code === Code.DeadlineExceeded) ...;
}

Deadline

const r = await client.send(req, { timeoutMs: 5000 });

Interceptor (middleware)

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.

🔗 관련 문서