"REST로 서비스를 연결하는 것과, 타입 안전한 계약으로 서비스를 연결하고 통신 비용을 줄이는 것은 다르다"
"
@FeignClient에 URL 붙이면 되겠지 — 와 — Protobuf 인코딩이 JSON 대비 왜 68% 작은지, HTTP/2 멀티플렉싱이 HTTP/1.1 Head-of-Line Blocking을 어떻게 없애는지,.proto파일이 어떻게 Breaking Change를 방지하는 API 계약서가 되는지 아는 것의 차이를 만드는 레포"
Tag-Length-Value 인코딩에서 필드 이름이 사라지고 번호만 남는 원리, 하나의 TCP 연결에서 수십 개의 RPC가 병렬로 흐르는 HTTP/2 스트림 구조, 서버/클라이언트/양방향 스트리밍의 적합한 시나리오,
Interceptor 체인으로 인증·추적·재시도를 미들웨어처럼 쌓는 방법까지
왜 이렇게 설계됐는가 라는 질문으로 gRPC 내부를 끝까지 파헤칩니다
gRPC에 관한 자료는 넘쳐납니다. 하지만 대부분은 "어떻게 쓰나" 에서 멈춥니다.
| 일반 자료 | 이 레포 |
|---|---|
| "gRPC는 REST보다 빠릅니다" | Protobuf TLV 인코딩이 JSON 대비 크기를 68% 줄이는 원리, HTTP/2 멀티플렉싱이 동시 RPC 처리량을 높이는 방식, 직렬화·연결 비용의 실제 벤치마크 수치 |
".proto 파일에 서비스를 정의하세요" |
protoc 컴파일러가 Stub·Server Skeleton을 생성하는 과정, 필드 번호가 API 계약의 실체인 이유, 필드 번호 재사용이 데이터 손상을 일으키는 시나리오 |
| "Server Streaming을 쓰면 됩니다" | HTTP/2 Frame/Stream/Message 계층에서 스트림 데이터가 어떻게 흐르는지, Backpressure와 Flow Control이 수신 측을 보호하는 원리 |
| "Interceptor로 인증하세요" | ClientInterceptor/ServerInterceptor 체인 구성, 메타데이터로 JWT·TraceID를 전파하는 방식, Deadline이 호출 체인 전체에 전파되는 Context Propagation 원리 |
| "gRPC-Gateway로 REST도 지원됩니다" | gRPC-Web의 브라우저 제약과 해결 방식, Envoy Proxy가 HTTP/1.1 → HTTP/2 변환을 처리하는 구조, L4 로드밸런서가 gRPC에서 효과 없는 이유 |
| "Buf로 proto를 관리하세요" | buf breaking으로 Breaking Change를 CI에서 감지하는 방법, Backward/Forward Compatibility 보장 조건, Consumer-Driven Contract Testing 적용 |
| 이론 나열 | 실행 가능한 grpcurl 실험 + Wireshark HTTP/2 Frame 캡처 + Protobuf 16진수 바이트 분석 + Docker Compose 환경 + Spring Boot 연결 |
각 챕터의 첫 문서부터 바로 학습을 시작하세요!
💡 각 섹션을 클릭하면 상세 문서 목록이 펼쳐집니다
핵심 질문: REST는 왜 마이크로서비스에서 한계를 드러내는가? gRPC는 어떤 문제를 해결하고, 어떤 비용을 치르는가? HTTP/2 위에서 하나의 연결이 어떻게 수십 개의 RPC를 동시에 처리하는가?
REST의 한계부터 gRPC 생태계까지 (5개 문서)
| 문서 | 다루는 내용 |
|---|---|
| 01. REST vs gRPC — 왜 REST가 마이크로서비스에서 한계를 보이는가 | 느슨한 스키마 계약으로 Breaking Change가 발생하는 시나리오, JSON 직렬화 비용과 HTTP/1.1 연결 오버헤드, gRPC가 해결하는 문제와 도입 비용(브라우저 미지원, 학습 곡선) |
| 02. gRPC 핵심 구성 요소 — .proto에서 Stub까지 | .proto 파일 → protoc 컴파일 → Stub 생성 → Channel → Server 전체 흐름, protoc 플러그인 동작 방식, Blocking/Async/Future Stub의 차이 |
| 03. HTTP/2 기반 통신 — Frame, Stream, Multiplexing | HTTP/2의 Frame/Stream/Message 3계층 구조, 하나의 TCP 연결에서 여러 RPC가 병렬로 흐르는 멀티플렉싱 원리, HTTP/1.1 Head-of-Line Blocking과의 차이, Wireshark로 HTTP/2 Frame 관찰 |
| 04. gRPC 통신 4가지 패턴 — 언제 무엇을 쓰는가 | Unary/Server Streaming/Client Streaming/Bidirectional Streaming 각각의 적합한 시나리오, 잘못된 패턴 선택이 만드는 문제(Polling을 Streaming으로, 스트리밍을 Unary로), grpcurl로 각 패턴 직접 실험 |
| 05. gRPC 생태계 — Web, Gateway, Envoy, Reflection | gRPC-Web이 브라우저에서 동작하기 위해 Proxy가 필요한 이유, gRPC-Gateway로 REST/gRPC 동시 지원, Envoy Proxy의 gRPC 트랜스코딩, gRPC Reflection으로 스키마 없이 서비스 탐색 |
핵심 질문: Protobuf는 어떻게 JSON보다 작고 빠른가? 필드 이름이 아닌 번호로 직렬화하는 이유는 무엇인가? 필드를 삭제하거나 추가할 때 왜 일부는 안전하고 일부는 데이터를 손상시키는가?
TLV 인코딩부터 직렬화 벤치마크까지 (7개 문서)
| 문서 | 다루는 내용 |
|---|---|
| 01. Protobuf 직렬화 원리 — Tag-Length-Value 인코딩 | 필드 번호 + Wire Type으로 구성된 TLV Tag 계산 방식, Varint 압축(작은 숫자는 더 작게 — ZigZag 인코딩), 실제 16진수 바이트로 보는 JSON vs Protobuf 크기 비교 |
| 02. 필드 번호가 API의 본체인 이유 | 컴파일 후 필드 이름은 사라지고 번호만 남는 원리, 필드 번호 재사용이 기존 클라이언트 데이터를 손상시키는 시나리오, 번호 관리를 API 설계의 핵심으로 다뤄야 하는 이유 |
| 03. 스칼라 타입과 기본값 — 없는 필드는 전송되지 않는다 | int32/int64/string/bool/bytes의 Wire Type과 직렬화 방식, 기본값(0, "", false)인 필드가 페이로드에 포함되지 않는 proto3 원칙, optional로 명시적 부재를 표현하는 방법 |
| 04. 복합 타입 — message 중첩, repeated, map, oneof | 중첩 message의 직렬화 구조(length-delimited), repeated 배열이 packed encoding으로 압축되는 방식, map이 내부적으로 repeated message로 변환되는 원리, oneof로 유니온 타입 표현 |
| 05. Well-Known Types — Timestamp, Any, Struct | google.protobuf.Timestamp/Duration/Any/Struct/FieldMask의 사용 목적과 JSON 변환 규칙, Any로 런타임 타입을 담는 방식, FieldMask로 부분 업데이트(PATCH) 표현 |
| 06. Protobuf 진화 규칙 — Backward/Forward Compatibility | 신규 필드 추가가 안전한 이유(구 클라이언트는 모르는 필드 무시), 필드 삭제 시 reserved 키워드가 필수인 이유, 타입 변경이 위험한 경우와 안전한 경우 구분 |
| 07. 직렬화 성능 측정 — JSON vs Protobuf vs MessagePack | 동일 메시지를 JSON/Protobuf/MessagePack으로 직렬화할 때의 크기와 처리 속도 벤치마크, 네트워크 비용 절감 효과 수치화, 언제 Protobuf의 이점이 크고 언제 JSON이 더 실용적인가 |
핵심 질문:
.proto파일을 어떻게 설계해야 Breaking Change 없이 서비스를 진화시킬 수 있는가? gRPC에서 에러, 메타데이터, 타임아웃은 어떻게 다루는가?
.proto 설계 원칙부터 서비스 계약 관리까지 (6개 문서)
| 문서 | 다루는 내용 |
|---|---|
| 01. .proto 파일 설계 원칙 | 서비스·메시지·필드 명명 규칙(snake_case vs CamelCase), Request/Response Wrapper 패턴으로 하위 호환성을 유지하는 이유, v1/v2 패키지 버전 관리 전략 |
| 02. 에러 처리 — gRPC Status Code와 google.rpc.Status | gRPC Status Code 13가지 의미와 HTTP 상태 코드 매핑, google.rpc.Status로 상세 에러 정보(ErrorDetails)를 전달하는 방법, 클라이언트에서 Status Code를 처리하는 패턴 |
| 03. 메타데이터 — gRPC의 HTTP 헤더 | 메타데이터가 HTTP/2 헤더 프레임으로 전송되는 방식, 인증 토큰·요청 ID·트레이스 ID를 메타데이터로 전달하는 패턴, Interceptor에서 메타데이터를 추출하고 Context에 전파하는 방법 |
| 04. 데드라인과 타임아웃 — Context Propagation | Deadline이 호출 체인을 따라 전파되는 원리(A→B→C에서 남은 시간이 자동 감소), 타임아웃 없는 gRPC 호출이 스레드 누수를 만드는 시나리오, Deadline Exceeded 에러의 올바른 처리 |
| 05. 로드밸런싱 전략 — 왜 L4가 gRPC에서 효과 없는가 | HTTP/2의 단일 TCP 연결 재사용이 L4 로드밸런서를 우회하는 원리, 클라이언트 사이드 로드밸런싱(Round-Robin/Pick-First), Envoy Proxy로 L7 레벨 로드밸런싱을 해결하는 방법 |
| 06. 서비스 계약 관리 — Buf Schema Registry | Buf CLI로 중앙화된 .proto 관리, buf breaking으로 CI에서 Breaking Change 자동 감지, Consumer-Driven Contract Testing으로 서비스 간 계약을 코드로 검증하는 방법 |
핵심 질문: 스트리밍은 어떤 상황에서 Polling보다 나은가? Backpressure와 Flow Control은 수신 측을 어떻게 보호하는가? 스트림 중간에 에러가 발생하면 어떻게 복구하는가?
Server Streaming부터 에러 복구까지 (5개 문서)
| 문서 | 다루는 내용 |
|---|---|
| 01. Server Streaming — 실시간 데이터 푸시 | 단일 요청에 여러 응답이 흐르는 HTTP/2 Stream 구조, 실시간 주식 시세·로그 스트리밍 구현, Polling 방식과의 연결 수·지연 비교, Backpressure가 필요한 시나리오 |
| 02. Client Streaming — 배치 데이터 전송 | 클라이언트가 여러 메시지를 보내고 단일 응답을 받는 패턴, 파일 업로드·배치 데이터 전송 구현, 청크 단위 전송으로 메모리 효율을 높이는 방법, 서버에서 스트림을 닫는 타이밍 |
| 03. Bidirectional Streaming — 채팅과 게임 상태 동기화 | 클라이언트·서버가 동시에 메시지를 보내는 양방향 스트림, 채팅 서비스·게임 상태 동기화 구현, 스트림 생명주기(시작·데이터·완료·에러) 관리, WebSocket과의 비교 |
| 04. 스트리밍 흐름 제어 — HTTP/2 Flow Control | HTTP/2 Window Size가 수신 측 버퍼를 보호하는 원리, WINDOW_UPDATE 프레임으로 수신 가능 용량을 알리는 방식, initialFlowControlWindow 설정과 처리량·메모리 트레이드오프 |
| 05. 스트리밍 에러 처리 — 재연결과 부분 실패 복구 | 스트림 중간 에러 발생 시 Status Code 전달 방식, Exponential Backoff 재연결 전략, 이미 전송된 메시지와 미전송 메시지의 처리 경계, 부분 실패를 응답 메시지로 표현하는 패턴 |
핵심 질문: 서비스 간 통신에서 신뢰는 어떻게 구축하는가? mTLS는 Zero Trust를 어떻게 구현하는가? Interceptor로 인증·로깅·추적을 어떻게 미들웨어처럼 쌓는가?
TLS/mTLS부터 Interceptor 체인까지 (5개 문서)
| 문서 | 다루는 내용 |
|---|---|
| 01. TLS와 mTLS — 서비스 간 Zero Trust | gRPC에서 TLS가 사실상 필수인 이유(HTTP/2 + Cleartext의 위험성), 서버 인증서 검증 흐름, mTLS로 클라이언트 신원까지 검증하는 Zero Trust 구현, 인증서 갱신 전략 |
| 02. JWT 기반 인증 — Interceptor로 토큰 주입/검증 | CallCredentials로 모든 RPC에 자동으로 JWT를 주입하는 방법, ServerInterceptor에서 Authorization 메타데이터를 추출·검증하는 방식, 만료된 토큰을 스트리밍 중간에 처리하는 패턴 |
| 03. API 키와 서비스 간 인증 | 내부 서비스 간 인증에 적합한 방식(JWT vs mTLS vs API Key), 인증 정보를 gRPC Context에 전파해 비즈니스 로직에서 꺼내 쓰는 패턴, 서비스 계정 기반 인증 구현 |
| 04. Interceptor 체인 — gRPC 미들웨어 패턴 | ClientInterceptor/ServerInterceptor 인터페이스 구조, 로깅·인증·분산 추적·재시도를 체인으로 조합하는 방법, Interceptor 실행 순서와 Context 전달, @GrpcGlobalInterceptor로 Spring Bean 전역 등록 |
| 05. 채널 보안 설정 — 개발부터 프로덕션까지 | ManagedChannelBuilder의 usePlaintext() vs useTransportSecurity() 선택 기준, 개발 환경에서 TLS 없이 테스트하는 방법, 프로덕션 인증서 설정과 핀닝 전략 |
핵심 질문: Spring Boot에서 gRPC 서버와 클라이언트를 어떻게 구성하는가? Spring Security, WebFlux, 예외 처리를 gRPC와 어떻게 통합하는가? 테스트는 어떻게 작성하는가?
grpc-spring-boot-starter부터 테스트 전략까지 (5개 문서)
| 문서 | 다루는 내용 |
|---|---|
| 01. grpc-spring-boot-starter 설정 | @GrpcService로 서비스 구현체 등록, @GrpcClient로 Stub 자동 주입, 서버 포트·최대 메시지 크기·keepAlive 설정, application.yml 기반 채널 구성 |
| 02. Spring Security 통합 — gRPC 메서드 레벨 권한 | gRPC 서버에 Spring Security를 적용하는 방법, JWT Interceptor와 SecurityContext 연동, @PreAuthorize로 메서드 레벨 권한 설정, 인증 실패 시 gRPC Status.UNAUTHENTICATED 반환 |
| 03. 예외 처리 통합 — Spring 예외를 gRPC Status로 | GrpcExceptionAdvice로 Spring 예외를 gRPC Status Code로 변환하는 전역 핸들러, @GrpcExceptionHandler를 활용한 서비스별 예외 매핑, ErrorDetails를 응답에 포함하는 방법 |
| 04. gRPC + Spring WebFlux — Reactive Stub 통합 | Reactive Stub(ReactorOrderServiceStub)으로 비동기 스트리밍 처리, gRPC Server Streaming을 Flux로, Client Streaming을 Mono로 래핑하는 방법, Backpressure와 Reactive Streams 연동 |
| 05. 테스트 전략 — 단위·통합·Mock Stub | GrpcServerExtension으로 내장 서버를 띄워 단위 테스트, Testcontainers로 Docker 기반 통합 테스트, InProcessChannel로 네트워크 없이 gRPC 호출 테스트, Mock Stub으로 클라이언트 코드 격리 테스트 |
핵심 질문: gRPC 서비스의 병목은 어디서 발생하는가? 모니터링과 분산 추적은 어떻게 설정하는가? 기존 REST API를 gRPC로 점진적으로 전환하는 전략은 무엇인가?
모니터링부터 마이그레이션 전략까지 (5개 문서)
| 문서 | 다루는 내용 |
|---|---|
| 01. gRPC 모니터링 — Micrometer + Prometheus | grpc-spring-boot-starter의 Micrometer 자동 계측, 요청 수·에러율·p99 응답시간 메트릭 수집, Grafana 대시보드 구성, gRPC Status Code별 에러율 알람 설정 |
| 02. 분산 추적 — OpenTelemetry gRPC Instrumentation | OpenTelemetry gRPC Instrumentation으로 서비스 간 TraceID 자동 전파, Baggage로 사용자 ID·요청 컨텍스트를 체인 전체에 전달하는 방법, Jaeger/Zipkin 연동 |
| 03. 연결 관리 튜닝 — keepAlive와 Channel Pool | keepAliveTime/keepAliveTimeout으로 유휴 TCP 연결을 유지하는 원리, 프록시·방화벽이 유휴 연결을 끊는 문제와 해결, 최대 동시 연결 수 설정, Channel Pool 구성 필요성 |
| 04. gRPC vs REST 성능 비교 — 수치로 보는 차이 | 동일 비즈니스 로직에서 JSON/REST vs Protobuf/gRPC의 직렬화 크기·시간·TPS·지연시간 벤치마크, gRPC 이점이 두드러지는 워크로드와 그렇지 않은 경우, 측정 방법론과 함정 |
| 05. 마이그레이션 전략 — REST에서 gRPC로 점진적 전환 | Strangler Fig 패턴으로 REST를 gRPC로 점진적 교체, gRPC-Gateway로 REST/gRPC 동시 지원하는 과도기 운영, 클라이언트 하위 호환성 유지, .proto 우선 설계로 팀 간 계약을 먼저 확정하는 방법 |
모든 실험은 Docker Compose 하나로 재현 가능합니다
# docker-compose.yml
services:
grpc-server:
build:
context: ./grpc-server
ports:
- "9090:9090" # gRPC
- "8080:8080" # gRPC-Gateway (REST 변환)
environment:
SPRING_PROFILES_ACTIVE: docker
grpc-client:
build:
context: ./grpc-client
depends_on:
- grpc-server
ports:
- "8081:8081"
envoy:
image: envoyproxy/envoy:v1.28-latest
volumes:
- ./envoy.yaml:/etc/envoy/envoy.yaml
ports:
- "9901:9901" # Admin
- "10000:10000" # gRPC 프록시
prometheus:
image: prom/prometheus:latest
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
ports:
- "9091:9090" # Prometheus UI
grafana:
image: grafana/grafana:latest
depends_on:
- prometheus
ports:
- "3000:3000" # Grafana UI (admin/admin)
environment:
GF_SECURITY_ADMIN_PASSWORD: admin
jaeger:
image: jaegertracing/all-in-one:latest
ports:
- "16686:16686" # Jaeger UI
- "4317:4317" # OTLP gRPC
wireshark:
image: linuxserver/wireshark:latest
network_mode: host # ⚠️ Linux 전용 — Mac Docker Desktop에서는 동작하지 않음
cap_add:
- NET_ADMIN# grpcurl로 gRPC 서비스 탐색 및 호출
grpcurl -plaintext localhost:9090 list
grpcurl -plaintext localhost:9090 describe order.v1.OrderService
grpcurl -plaintext localhost:9090 order.v1.OrderService/CreateOrder \
-d '{"user_id": "user-1", "items": [{"product_id": "prod-1", "quantity": 2}]}'
# 서버 스트리밍 호출
grpcurl -plaintext localhost:9090 order.v1.OrderService/WatchOrderStatus \
-d '{"order_id": "order-123"}'
# Wireshark HTTP/2 Frame 캡처
# Filter: http2 and tcp.port==9090이 레포는 다음 Deep Dive 시리즈와 연결됩니다:
| 레포 | 연결 지점 |
|---|---|
| network-deep-dive | HTTP/2 멀티플렉싱 · TLS 핸드쉐이크 · TCP Flow Control — Chapter 1·5의 전제 지식 |
| msa-deep-dive | 서비스 간 통신 패턴 · gRPC vs REST 선택 기준 · 서비스 메시 컨텍스트 — Chapter 3·7과 연결 |
| spring-webflux-deep-dive | Reactive Streams · Backpressure · Mono/Flux — Chapter 4·6의 WebFlux 통합에서 시너지 |
grpc-deep-dive/
├── grpc-fundamentals/ # Chapter 1: gRPC 설계 철학과 아키텍처 (5개)
├── protocol-buffers/ # Chapter 2: Protocol Buffers 완전 분해 (7개)
├── service-design/ # Chapter 3: gRPC 서비스 설계 (6개)
├── streaming-patterns/ # Chapter 4: gRPC 스트리밍 패턴 (5개)
├── security-auth/ # Chapter 5: gRPC 보안과 인증 (5개)
├── spring-grpc/ # Chapter 6: Spring Boot + gRPC 통합 (5개)
└── performance-operations/ # Chapter 7: 운영과 성능 튜닝 (5개)
총 38개 문서 · REST와의 대비로 시작해 내부 원리로 끝나는 구조