TechFeedTechFeed
Backend

gRPC 완전 가이드 — Protocol Buffers 설계부터 Node.js 서버·클라이언트 구현, REST와 성능 비교까지

gRPC는 HTTP/2와 Protocol Buffers를 결합해 마이크로서비스 간 통신 속도와 타입 안전성을 높이는 프레임워크입니다. .proto 파일 작성법, Node.js gRPC 서버·클라이언트 구현, 4가지 통신 패턴(Unary·서버 스트리밍·클라이언트 스트리밍·양방향 스트리밍), 인터셉터, TypeScript 정적 코드 생성, 쿠버네티스 로드 밸런싱, 프로덕션 체크리스트 12항목, grpcurl 디버깅 도구까지 실전 중심으로 정리했습니다.

by

REST API를 쓰다 보면 마이크로서비스 간 통신이 느리거나, 타입 불일치로 디버깅에 시간을 쏟은 경험이 있을 겁니다. gRPC(gRPC Remote Procedure Call)는 그 문제를 구조적으로 해결하는 프레임워크입니다. 구글이 내부 서비스 통신을 위해 개발했고, 현재 넷플릭스·스퀘어·도커 등 수백 개 기업이 프로덕션에서 씁니다.


이 글에서는 Protocol Buffers(프로토버프) 파일 작성 → Node.js 서버·클라이언트 구현 → 4가지 통신 패턴 → 인터셉터 → TypeScript 정적 생성 → 프로덕션 체크리스트까지, gRPC를 실제로 돌리는 데 필요한 모든 것을 다룹니다. REST와 어디서 다르고, 언제 쓰면 이득인지도 구체적으로 짚겠습니다.


gRPC란 무엇인가 — HTTP/2와 프로토버프의 조합

gRPC는 HTTP/2 위에서 동작하는 RPC(원격 프로시저 호출) 프레임워크입니다. 데이터 직렬화에는 JSON 대신 Protocol Buffers(프로토버프)를 씁니다. 이 조합이 만드는 차이가 큽니다.


  • HTTP/2 멀티플렉싱: 하나의 TCP 연결에서 여러 요청·응답을 동시에 주고받습니다. HTTP/1.1의 head-of-line blocking이 없습니다.
  • 바이너리 직렬화: 프로토버프는 JSON 대비 평균 3~10배 작은 페이로드를 만듭니다. 네트워크 대역폭과 파싱 시간이 줄어듭니다.
  • 강력한 타입 시스템: .proto 파일 하나로 서버·클라이언트의 타입을 동시에 생성합니다. API 불일치 버그가 컴파일 시점에 잡힙니다.
  • 양방향 스트리밍: 서버 푸시, 클라이언트 스트리밍, 양방향 스트림을 네이티브로 지원합니다.

2016년 구글이 오픈소스로 공개한 이후, 클라우드 네이티브 마이크로서비스 아키텍처의 표준 내부 통신 방식으로 자리잡았습니다. 쿠버네티스 내부 컴포넌트 통신도 gRPC 기반입니다.


가장 큰 오해는 "gRPC는 성능만 좋다"는 것입니다. 실제로 많은 팀이 gRPC를 선택하는 주요 이유는 타입 안전성과 API 계약 관리입니다. 서비스가 10개를 넘어가면 서비스 간 인터페이스 불일치가 런타임 장애의 주요 원인이 됩니다. 프로토버프 파일 한 장이 그 문제를 컴파일 타임으로 끌어올립니다.


gRPC 아키텍처 — HTTP/2와 Protocol Buffers 기반 마이크로서비스 통신
gRPC는 HTTP/2 멀티플렉싱과 프로토버프 바이너리 직렬화를 결합해 마이크로서비스 간 통신 속도와 타입 안전성을 높입니다.

Protocol Buffers 기초 — .proto 파일 작성법

gRPC 개발은 .proto 파일 정의에서 시작합니다. 이 파일 하나가 API 계약서 역할을 합니다. Node.js·Python·Go·Java 모든 언어에서 같은 .proto를 공유합니다.


proto/user.proto
syntax = "proto3"; package user; // 서비스 정의 — RPC 메서드 목록 service UserService { // Unary RPC: 요청 1개 → 응답 1개 rpc GetUser (GetUserRequest) returns (User); rpc CreateUser (CreateUserRequest) returns (User); rpc UpdateUser (UpdateUserRequest) returns (User); rpc DeleteUser (DeleteUserRequest) returns (DeleteUserResponse); // 서버 스트리밍: 요청 1개 → 응답 스트림 rpc ListUsers (ListUsersRequest) returns (stream User); } // 메시지 타입 정의 message User { string id = 1; string email = 2; string name = 3; string role = 4; int64 created_at = 5; // Unix timestamp (밀리초) } message GetUserRequest { string id = 1; } message CreateUserRequest { string email = 1; string name = 2; string role = 3; } message ListUsersRequest { int32 page = 1; int32 page_size = 2; string role_filter = 3; // 비어있으면 전체 조회 } message UpdateUserRequest { string id = 1; optional string email = 2; // proto3 optional: 필드 존재 여부 구분 optional string name = 3; } message DeleteUserRequest { string id = 1; } message DeleteUserResponse { bool success = 1; string message = 2; }

필드 번호(1, 2, 3...)는 직렬화 시 필드를 식별하는 태그입니다. 한 번 할당한 번호는 절대 바꾸지 마십시오. 기존 클라이언트와 역호환성이 깨집니다. 필드를 제거할 때는 reserved 4;로 번호를 예약해 두는 것이 안전합니다.


proto3에서 모든 필드는 기본값이 있습니다(string은 빈 문자열, int32는 0). 명시적으로 "설정되지 않음"을 표현하려면 optional 키워드를 씁니다. optional 필드는 C#의 nullable, TypeScript의 T | undefined와 유사하게 동작합니다.


Node.js에서는 두 가지 방식으로 프로토버프를 사용할 수 있습니다.


  • 동적 로딩(@grpc/proto-loader): 런타임에 .proto를 읽습니다. 코드 생성 없이 빠르게 시작할 때 적합합니다.
  • 정적 코드 생성(protoc + ts-proto): 빌드 시 TypeScript 타입과 클라이언트를 생성합니다. 프로덕션에서는 이 방식을 권장합니다.

Node.js gRPC 서버 구현 — 단계별 완전 예시

먼저 의존성을 설치합니다. @grpc/grpc-js는 순수 JavaScript gRPC 구현으로, C 바인딩 없이 Node.js에서 바로 동작합니다.


bash — 의존성 설치
npm install @grpc/grpc-js @grpc/proto-loader # TypeScript 사용 시 npm install -D typescript @types/node ts-node
server.js — gRPC 서버 전체 구현
const grpc = require('@grpc/grpc-js'); const protoLoader = require('@grpc/proto-loader'); const path = require('path'); // .proto 파일 로드 const PROTO_PATH = path.join(__dirname, 'proto/user.proto'); const packageDefinition = protoLoader.loadSync(PROTO_PATH, { keepCase: true, longs: String, enums: String, defaults: true, oneofs: true }); const userProto = grpc.loadPackageDefinition(packageDefinition).user; // 인메모리 저장소 (실제 서비스에서는 DB로 교체) const users = new Map(); const userServiceHandlers = { // Unary RPC: 사용자 단건 조회 GetUser: (call, callback) => { const user = users.get(call.request.id); if (!user) { return callback({ code: grpc.status.NOT_FOUND, message: `사용자 ${call.request.id}를 찾을 수 없습니다` }); } callback(null, user); }, // Unary RPC: 사용자 생성 CreateUser: (call, callback) => { const { email, name, role } = call.request; if (!email || !name) { return callback({ code: grpc.status.INVALID_ARGUMENT, message: 'email과 name은 필수입니다' }); } const id = `user_${Date.now()}`; const user = { id, email, name, role: role || 'user', created_at: Date.now() }; users.set(id, user); callback(null, user); }, // 서버 스트리밍: 조건에 맞는 사용자를 하나씩 전송 ListUsers: (call) => { const { role_filter } = call.request; for (const user of users.values()) { if (!role_filter || user.role === role_filter) { call.write(user); // 스트림으로 순차 전송 } } call.end(); // 스트림 종료 신호 }, UpdateUser: (call, callback) => { const { id, email, name } = call.request; const user = users.get(id); if (!user) { return callback({ code: grpc.status.NOT_FOUND, message: '사용자 없음' }); } if (email) user.email = email; if (name) user.name = name; users.set(id, user); callback(null, user); }, DeleteUser: (call, callback) => { const deleted = users.delete(call.request.id); callback(null, { success: deleted, message: deleted ? '삭제됨' : '존재하지 않는 ID' }); } }; function main() { const server = new grpc.Server(); server.addService(userProto.UserService.service, userServiceHandlers); // 프로덕션에서는 createSsl()로 TLS 적용 server.bindAsync('0.0.0.0:50051', grpc.ServerCredentials.createInsecure(), (err, port) => { if (err) { console.error('서버 시작 실패:', err); return; } console.log(`gRPC 서버가 포트 ${port}에서 시작됨`); }); } main();

주목할 점은 에러 처리 방식입니다. gRPC는 HTTP 상태 코드 대신 자체 상태 코드를 씁니다. 표준 코드는 총 16가지입니다.


  • NOT_FOUND (5): 리소스 없음 — HTTP 404에 대응
  • INVALID_ARGUMENT (3): 잘못된 입력 — HTTP 400에 대응
  • PERMISSION_DENIED (7): 권한 없음 — HTTP 403에 대응
  • UNAUTHENTICATED (16): 인증 필요 — HTTP 401에 대응
  • INTERNAL (13): 서버 내부 오류 — HTTP 500에 대응
  • DEADLINE_EXCEEDED (4): 타임아웃 — HTTP 504에 대응

클라이언트는 이 코드를 기준으로 재시도 여부를 결정합니다. INTERNAL, UNAVAILABLE, DEADLINE_EXCEEDED는 재시도 가능. NOT_FOUND, INVALID_ARGUMENT는 재시도해도 의미 없습니다.


gRPC 클라이언트 작성과 서버 스트리밍 소비

client.js — gRPC 클라이언트 전체 구현
const grpc = require('@grpc/grpc-js'); const protoLoader = require('@grpc/proto-loader'); const path = require('path'); const PROTO_PATH = path.join(__dirname, 'proto/user.proto'); const packageDefinition = protoLoader.loadSync(PROTO_PATH, { keepCase: true, longs: String, enums: String, defaults: true, oneofs: true }); const userProto = grpc.loadPackageDefinition(packageDefinition).user; // 클라이언트 생성 const client = new userProto.UserService( 'localhost:50051', grpc.credentials.createInsecure() ); // Unary RPC: Promise 래퍼 function createUser(email, name) { return new Promise((resolve, reject) => { // deadline 지정 — 미지정 시 영구 대기 const deadline = new Date(Date.now() + 5000); // 5초 client.CreateUser({ email, name, role: 'user' }, { deadline }, (err, response) => { if (err) return reject(err); resolve(response); }); }); } // 서버 스트리밍: 이벤트 기반 소비 function listAllUsers() { const stream = client.ListUsers({ page: 1, page_size: 100 }); const users = []; stream.on('data', (user) => { users.push(user); console.log('수신:', user.name, user.email); }); stream.on('error', (err) => { console.error('스트림 에러:', err.message); }); stream.on('end', () => { console.log(`총 ${users.length}명 수신 완료`); }); } // 메타데이터로 인증 토큰 전달 function getUserWithAuth(userId, token) { const metadata = new grpc.Metadata(); metadata.add('authorization', `Bearer ${token}`); return new Promise((resolve, reject) => { client.GetUser({ id: userId }, metadata, (err, response) => { if (err) return reject(err); resolve(response); }); }); } (async () => { try { const user = await createUser('dev@example.com', '개발자'); console.log('생성됨:', user); listAllUsers(); } catch (err) { // gRPC 에러 코드 확인 if (err.code === grpc.status.DEADLINE_EXCEEDED) { console.error('타임아웃 — 서버 응답 없음'); } else { console.error('에러:', err.message); } } })();

deadline 설정은 가장 많이 놓치는 부분입니다. gRPC 클라이언트는 기본값이 없어 서버 응답이 없으면 영구 대기합니다. 모든 클라이언트 호출에 deadline을 지정하는 것을 팀 표준으로 정해두십시오.


인증 정보는 메타데이터(Metadata)로 전달합니다. HTTP 헤더와 유사한 개념입니다. 서버 인터셉터에서 메타데이터를 읽어 JWT를 검증하는 방식이 일반적입니다.


gRPC 4가지 통신 패턴 — 언제 어떤 것을 쓸까

gRPC의 핵심 강점은 4가지 통신 패턴을 네이티브로 지원한다는 점입니다. REST는 요청-응답만 지원하지만, gRPC는 스트리밍까지 표준 사양으로 포함합니다.


패턴설명적합한 사용 사례
Unary RPC요청 1개 → 응답 1개사용자 조회, 상품 주문 등 일반 CRUD
서버 스트리밍요청 1개 → 응답 스트림대량 데이터 조회, 실시간 로그, 파일 다운로드
클라이언트 스트리밍요청 스트림 → 응답 1개파일 업로드, 대량 데이터 입력, IoT 센서 일괄 수집
양방향 스트리밍요청 스트림 ↔ 응답 스트림채팅, 실시간 협업, 게임 서버, AI 토큰 스트리밍

proto/chat.proto — 양방향 스트리밍 RPC 정의
syntax = "proto3"; package chat; service ChatService { // 양방향 스트리밍 RPC rpc Chat (stream ChatMessage) returns (stream ChatMessage); } message ChatMessage { string user_id = 1; string content = 2; int64 timestamp = 3; string room_id = 4; }
양방향 스트리밍 서버 핸들러 구현
const chatHandlers = { Chat: (call) => { // call은 읽기+쓰기 동시 가능한 양방향 스트림 call.on('data', (message) => { console.log(`[${message.room_id}] ${message.user_id}: ${message.content}`); // 수신 즉시 응답 전송 (실제에서는 같은 룸 사용자들에게 브로드캐스트) const reply = { user_id: 'server', content: `[에코] ${message.content}`, timestamp: Date.now(), room_id: message.room_id }; call.write(reply); }); call.on('end', () => { call.end(); // 클라이언트가 스트림을 닫으면 서버도 닫기 }); call.on('error', (err) => { console.error('스트림 에러:', err); }); } }; // 클라이언트 쪽 양방향 스트리밍 사용법 // const stream = client.Chat(); // stream.write({ user_id: 'user1', content: '안녕하세요', room_id: 'room1' }); // stream.on('data', (msg) => console.log('수신:', msg.content)); // stream.end(); // 전송 완료

AI 토큰 스트리밍에 gRPC 양방향 스트리밍을 적용하면 특히 효과적입니다. 클라이언트가 질문을 스트림으로 보내고, 서버가 LLM 응답 토큰을 생성하면서 실시간으로 전송합니다. 기존 HTTP/SSE(Server-Sent Events) 방식 대비 연결 관리와 에러 처리가 훨씬 간결해집니다.


gRPC 4가지 통신 패턴 — Unary, 서버 스트리밍, 클라이언트 스트리밍, 양방향 스트리밍 비교
gRPC는 REST가 지원하지 않는 서버·클라이언트 스트리밍과 양방향 통신을 기본 사양으로 제공합니다. AI 토큰 스트리밍에는 서버 스트리밍 패턴이 가장 많이 쓰입니다.

gRPC vs REST — 실무 선택 기준

gRPC를 선택해야 할 때:


  • 마이크로서비스 간 내부 통신이 주 목적일 때
  • 대량 데이터를 고빈도로 교환하는 서비스 (지연 시간 민감)
  • 실시간 양방향 통신이 필요할 때 (채팅, 게임, AI 토큰 스트리밍)
  • 여러 언어로 서비스가 구성된 폴리글랏 환경
  • API 계약을 엄격하게 관리해야 하는 팀

REST를 유지해야 할 때:


  • 외부 공개 API (브라우저 직접 호출 또는 서드파티 개발자 대상)
  • 캐싱이 중요한 조회 중심 API (HTTP 캐시 헤더 활용)
  • 팀이 HTTP 생태계에 익숙하고 프로토버프 도입 비용이 큰 경우
  • 단순 CRUD만 필요한 소규모 서비스

많은 기업이 내부 마이크로서비스는 gRPC, 외부 API는 REST로 혼용합니다. Envoy·Kong 같은 API 게이트웨이가 gRPC ↔ REST 트랜스코딩을 지원해 두 방식을 함께 운영할 수 있습니다.


인터셉터 — 인증·로깅·재시도 공통 처리

gRPC 인터셉터는 Express 미들웨어와 유사한 개념입니다. 모든 RPC 호출 전후에 공통 로직을 삽입합니다. 인증 토큰 검증, 요청 로깅, 지연 측정, 재시도 등에 활용합니다.


interceptors.js — 클라이언트 재시도 인터셉터
const grpc = require('@grpc/grpc-js'); // 클라이언트 재시도 인터셉터 — UNAVAILABLE, DEADLINE_EXCEEDED 시 자동 재시도 function retryInterceptor(options, nextCall) { const MAX_RETRIES = 3; let retries = 0; let savedMessage; let savedMetadata; return new grpc.InterceptingCall(nextCall(options), { start: function(metadata, listener, next) { savedMetadata = metadata; const newListener = { onReceiveStatus: (status, next) => { const retryable = [ grpc.status.UNAVAILABLE, grpc.status.DEADLINE_EXCEEDED ]; if (retryable.includes(status.code) && retries < MAX_RETRIES) { retries++; const delay = 1000 * Math.pow(2, retries - 1); // 지수 백오프 console.log(`재시도 ${retries}/${MAX_RETRIES} — ${delay}ms 후`); setTimeout(() => { const newCall = nextCall(options); newCall.start(savedMetadata, newListener); if (savedMessage) newCall.sendMessage(savedMessage); newCall.halfClose(); }, delay); return; } next(status); }, onReceiveMessage: (message, next) => next(message), onReceiveMetadata: (metadata, next) => next(metadata) }; next(metadata, newListener); }, sendMessage: (message, next) => { savedMessage = message; next(message); }, halfClose: (next) => next() }); } // 클라이언트 생성 시 인터셉터 등록 const client = new userProto.UserService( 'localhost:50051', grpc.credentials.createInsecure(), { interceptors: [retryInterceptor] } );

서버 쪽에서는 인터셉터로 모든 RPC 호출 전에 JWT 검증을 추가합니다. 각 핸들러마다 인증 코드를 반복하지 않아도 됩니다. 메타데이터에서 authorization 헤더를 읽어 토큰을 검증하고, 실패 시 UNAUTHENTICATED 코드와 함께 호출을 차단합니다.


로깅 인터셉터는 메서드명, 상태 코드, 소요 시간을 기록합니다. 프로메테우스 메트릭과 연결하면 서비스별 P50/P99 지연 시간과 에러율을 실시간으로 모니터링할 수 있습니다.


TypeScript 정적 코드 생성 — 프로덕션 권장 방식

동적 로딩은 간편하지만 TypeScript 타입 추론이 동작하지 않습니다. 프로덕션에서는 protocts-proto로 타입을 미리 생성합니다.


package.json — 코드 생성 스크립트
{ "scripts": { "proto:gen": "protoc --plugin=./node_modules/.bin/protoc-gen-ts_proto --ts_proto_out=./generated --ts_proto_opt=outputServices=grpc-js,esModuleInterop=true -I ./proto ./proto/*.proto", "proto:check": "buf breaking --against .git#branch=main" }, "devDependencies": { "ts-proto": "^1.180.0", "grpc-tools": "^1.12.4", "@bufbuild/buf": "^1.30.0" } }

코드 생성 방식의 가장 큰 장점은 .proto 파일 변경 시 컴파일 에러로 즉시 불일치를 감지할 수 있다는 점입니다. CI 파이프라인에 proto:gen 단계를 추가하면 서버·클라이언트 타입 불일치를 빌드 시점에 차단합니다.


proto:check(buf breaking)는 API 계약 위반을 자동으로 탐지합니다. 기존 필드 번호 변경, 필드 타입 변경, 필드 제거 같은 하위 비호환 변경이 있으면 CI를 실패시킵니다. 마이크로서비스 팀에서는 이 검사를 PR 머지 차단 조건으로 설정하는 것이 일반적입니다.


gRPC 프로덕션 아키텍처 — Envoy 사이드카, 쿠버네티스 서비스 메시 구성
프로덕션 gRPC 서비스는 사이드카 패턴(Envoy/Istio)으로 로드 밸런싱·TLS 종료·메트릭 수집을 처리하는 구조가 일반적입니다.

gRPC 프로덕션 배포 체크리스트 12항목

로드 밸런서 선택은 특히 중요합니다. gRPC는 HTTP/2 위에서 하나의 TCP 연결을 장시간 유지합니다. L4 로드 밸런서(AWS NLB 등)는 연결 단위로 분산하기 때문에 하나의 파드에 모든 요청이 몰릴 수 있습니다. 쿠버네티스 환경에서는 Envoy, Istio, Linkerd 같은 L7 서비스 메시를 사용해야 요청 단위 분산이 제대로 동작합니다.


디버깅 도구 — grpcurl, gRPC-UI, Buf

바이너리 직렬화 때문에 curl로 직접 테스트할 수 없습니다. 대신 전용 도구를 씁니다.


  • grpcurl: curl의 gRPC 버전. 터미널에서 RPC 직접 호출 가능. brew install grpcurl
  • gRPC-UI: 브라우저 기반 gRPC 클라이언트. Postman의 gRPC 버전. brew install grpcui
  • Evans: 대화형 CLI 클라이언트. 인터랙티브 모드로 API 탐색.
  • Buf: 프로토버프 전용 린터·포매터·하위 호환성 검사·스키마 레지스트리. brew install bufbuild/buf/buf

grpcurl 사용 예시
# 서비스 목록 확인 (서버에 reflection 활성화 필요) grpcurl -plaintext localhost:50051 list # 특정 서비스 메서드 확인 grpcurl -plaintext localhost:50051 list user.UserService # Unary RPC 호출 (-d로 JSON 형식 요청 전달) grpcurl -plaintext \ -d '{"email":"dev@example.com","name":"개발자","role":"user"}' \ localhost:50051 user.UserService/CreateUser # 메타데이터(인증 토큰) 포함 호출 grpcurl -plaintext \ -H 'authorization: Bearer eyJhbGciOiJIUzI1NiIs...' \ -d '{"id":"user_123"}' \ localhost:50051 user.UserService/GetUser # gRPC-UI 브라우저 UI 실행 grpcui -plaintext localhost:50051

참고 자료


Q. gRPC와 REST를 동시에 제공할 수 있나요?

네. grpc-gateway(Go) 또는 Envoy의 gRPC-JSON 트랜스코딩 기능을 쓰면 .proto 파일 하나로 gRPC와 REST JSON API를 동시에 제공할 수 있습니다. Node.js에서는 @connectrpc/connect가 같은 역할을 합니다. 외부 공개 API는 REST, 내부 서비스는 gRPC로 분리하면서 코드 중복 없이 두 프로토콜을 지원하는 가장 실용적인 방법입니다.


Q. 쿠버네티스에서 gRPC 로드 밸런싱이 왜 복잡한가요?

HTTP/2는 하나의 TCP 연결을 유지하면서 여러 요청을 멀티플렉싱합니다. L4 로드 밸런서(NLB 등)는 연결 단위로 분산하기 때문에 하나의 파드에 모든 요청이 몰릴 수 있습니다. 쿠버네티스에서 올바르게 분산하려면 Envoy, Istio 같은 L7 프록시나 Linkerd 서비스 메시가 필요합니다. 또는 클라이언트 사이드 로드 밸런싱도 선택지입니다.


Q. proto 파일을 안전하게 업데이트하는 방법은?

핵심 규칙은 두 가지입니다. 첫째, 기존 필드 번호를 절대 바꾸지 않는다. 둘째, 기존 필드를 제거할 때는 reserved 4;로 해당 번호를 예약한다. 새 필드 추가는 언제나 안전합니다. 기존 클라이언트는 모르는 필드를 무시합니다. CI에서 buf breaking --against .git#branch=main으로 계약 위반을 자동 탐지하는 것을 강력히 권장합니다.


Q. gRPC 서비스의 헬스체크는 어떻게 구현하나요?

gRPC 표준 헬스체크 프로토콜(grpc.health.v1)을 구현하면 쿠버네티스·Envoy·AWS NLB 등이 자동으로 인식합니다. Node.js에서는 grpc-health-check 패키지를 설치하고 HealthImplementation을 서버에 추가합니다. grpcurl -plaintext localhost:50051 grpc.health.v1.Health/Check로 상태를 확인합니다.


Q. gRPC 성능이 REST보다 얼마나 빠른가요?

워크로드에 따라 다르지만, 공개된 벤치마크에서 gRPC는 REST/JSON 대비 처리량 2~7배, 지연 시간 20~50% 감소를 보입니다. 이 차이는 페이로드가 클수록, 요청 빈도가 높을수록 두드러집니다. 단순 CRUD 서비스에서는 차이가 작습니다. 많은 팀이 성능보다 타입 안전성과 스트리밍 지원을 gRPC 선택의 실질적 이유로 꼽습니다.


Q. gRPC-Web은 gRPC와 어떻게 다른가요?

브라우저는 HTTP/2 Trailer(gRPC가 상태 코드 전달에 사용)를 직접 접근할 수 없습니다. gRPC-Web은 이 제약을 해결하기 위해 Envoy 프록시를 통해 브라우저 요청을 gRPC로 트랜스코딩합니다. 단, 양방향 스트리밍은 지원하지 않습니다(단방향 서버 스트리밍만 가능). 더 간단한 대안으로 ConnectRPC를 쓰면 Envoy 없이 브라우저에서 직접 gRPC 서비스를 호출할 수 있습니다.


gRPCProtocol BuffersNode.js마이크로서비스API백엔드TypeScriptHTTP2스트리밍쿠버네티스

함께 보면 좋은 문제 해결

EXPLORE / Backend

이어서 읽어보기

전체 토픽 둘러보기