gRPC 스트리밍으로 실시간 로그 전송 구현하기

문제 상황

여러 마이크로서비스에서 발생하는 로그를 중앙 서버로 실시간 수집해야 하는 요구사항이 생겼다. 기존에는 REST API로 주기적으로 폴링하는 방식이었는데, 지연이 크고 불필요한 요청이 많았다.

gRPC Server Streaming 선택

HTTP/2 기반의 gRPC는 단일 연결로 양방향 스트리밍을 지원한다. 특히 Server Streaming은 클라이언트 요청 하나로 서버가 지속적으로 데이터를 푸시할 수 있어 로그 수집에 적합했다.

Proto 정의

syntax = "proto3";

service LogService {
  rpc StreamLogs(LogRequest) returns (stream LogEntry) {}
}

message LogRequest {
  string service_name = 1;
  string level = 2;
}

message LogEntry {
  string timestamp = 1;
  string level = 2;
  string message = 3;
}

Node.js 서버 구현

const grpc = require('grpc');
const protoLoader = require('@grpc/proto-loader');

function streamLogs(call) {
  const { service_name, level } = call.request;
  
  const interval = setInterval(() => {
    if (call.cancelled) {
      clearInterval(interval);
      return;
    }
    
    call.write({
      timestamp: new Date().toISOString(),
      level: level,
      message: `Log from ${service_name}`
    });
  }, 1000);
  
  call.on('cancelled', () => {
    clearInterval(interval);
  });
}

const server = new grpc.Server();
server.addService(logService.LogService.service, {
  streamLogs: streamLogs
});

결과

REST 폴링 대비 네트워크 오버헤드가 70% 감소했다. HTTP/2의 멀티플렉싱 덕분에 여러 서비스가 동시에 연결해도 성능 저하가 없었다. 다만 프로토콜 버퍼 스키마 관리와 버전 호환성은 별도로 신경써야 했다.

연결 끊김 처리를 위한 재연결 로직과 백프레셔 처리는 추가 작업이 필요했지만, 실시간성이 중요한 로그 수집에는 확실히 적합한 선택이었다.