gRPC + Spring Boot 마스터 노트 시리즈 10편 (마지막). Reflection으로 동적 스키마 발견, Health Check 표준 프로토콜, gzip·snappy 압축, 클라이언트 사이드·프록시 사이드 로드 밸런싱, gRPC-Web으로 브라우저 통합, Channel KeepAlive 관리, Service Discovery 통합, 운영 체크리스트까지 — 시리즈 마무리.
이 글은 gRPC + Spring Boot 마스터 노트 시리즈의 마지막 열 번째 편입니다. 1~9편이 핵심이었다면, 이번엔 운영 환경의 고급 주제 — Reflection·Health·LB·gRPC-Web·Channel 관리.
운영에서 부딪히는 디테일 모음. 한 번 정리해두면 두고두고 참고. 시리즈 마무리.
처음 고급 주제가 어렵게 느껴지는 이유
처음 이 단원이 어렵게 느껴지는 이유는 두 가지예요. 첫째, 각 주제가 독립적입니다. 둘째, 실제 시나리오 없이는 막연합니다.
해결법은 한 가지예요. 각 주제를 "운영의 어떤 문제를 풀까"로 보기. Reflection = 디버깅, Health = 모니터링, LB = 확장, gRPC-Web = 브라우저, Channel = 연결 관리. 각자 명확한 답.
Reflection — 동적 스키마 발견
@Configuration
public class GrpcConfig {
@Bean
public ServerInterceptor protoReflection() {
return ProtoReflectionService.newInstance();
}
}
또는:
@GrpcService
public class ReflectionService extends ProtoReflectionService { }
서버가 자기 스키마 노출 → 클라이언트 자동 발견.
# grpcurl로 자동 발견
grpcurl -plaintext localhost:9090 list
# 서비스 메서드
grpcurl -plaintext localhost:9090 list UserService
# 메시지 스키마
grpcurl -plaintext localhost:9090 describe UserRequest
여기서 시험 함정이 하나 있어요. Reflection은 운영 환경 OFF 권장. 스키마 노출 = 보안 위험. 개발·스테이징만.
Health Check — 표준 프로토콜
import io.grpc.health.v1.HealthGrpc;
import io.grpc.health.v1.HealthCheckRequest;
import io.grpc.health.v1.HealthCheckResponse;
@Bean
public HealthStatusManager healthStatusManager() {
HealthStatusManager manager = new HealthStatusManager();
manager.setStatus("UserService", HealthCheckResponse.ServingStatus.SERVING);
return manager;
}
grpcurl -plaintext -d '{"service": "UserService"}' \
localhost:9090 grpc.health.v1.Health/Check
# {"status": "SERVING"}
Kubernetes Liveness·Readiness Probe
livenessProbe:
exec:
command: ["grpc_health_probe", "-addr=:9090"]
readinessProbe:
exec:
command: ["grpc_health_probe", "-addr=:9090"]
grpc_health_probe 바이너리 사용.
여기서 정말 중요한 시험 함정 — Health Check는 운영 표준. K8s·로드 밸런서가 의존. 항상 활성화.
Compression — 메시지 압축
서버
grpc:
server:
compression: gzip
클라이언트
@GrpcClient("user-service")
private UserServiceGrpc.UserServiceBlockingStub stub;
public User getUser(String id) {
return stub.withCompression("gzip").getUser(request);
}
Custom
@Bean
public GrpcServerConfigurer compressionConfigurer() {
return serverBuilder -> ((NettyServerBuilder) serverBuilder)
.compressorRegistry(CompressorRegistry.getDefaultInstance())
.decompressorRegistry(DecompressorRegistry.getDefaultInstance());
}
| 알고리즘 | 비율 | 속도 |
|---|---|---|
| gzip | 높음 | 중간 |
| snappy | 중간 | 빠름 |
| identity (압축 X) | - | 가장 빠름 |
여기서 시험 함정이 하나 있어요. 작은 메시지 (< 1KB) = 압축 비효율. CPU 소비 > 네트워크 절감. 큰 페이로드만 압축 권장.
로드 밸런싱
1. 클라이언트 사이드 LB
grpc:
client:
user-service:
address: dns:///user-service.default.svc.cluster.local
DNS A 레코드 = 여러 IP → 자동 round-robin.
@GrpcClient(value = "user-service", configurations = LoadBalancingConfiguration.class)
private UserServiceGrpc.UserServiceBlockingStub stub;
2. 프록시 사이드 LB — Envoy·Linkerd
Client → Envoy/Linkerd Proxy → Server 1, 2, 3
Service Mesh 표준. 자동 LB·관찰성·재시도.
3. K8s Headless Service + 클라이언트 LB
apiVersion: v1
kind: Service
metadata:
name: user-service-headless
spec:
clusterIP: None
selector:
app: user-service
여기서 정말 중요한 시험 함정 — gRPC + 일반 K8s Service = LB 안 됨 (RSocket과 같은 문제). Headless Service + 클라이언트 LB 또는 Envoy.
Service Discovery 통합
grpc:
client:
user-service:
address: discovery:///user-service # Eureka·Consul·Nacos
gRPC-Web — 브라우저 통합
브라우저는 HTTP/2 직접 X (특히 Trailers·full-duplex). gRPC-Web으로 변환.
Browser → HTTP/1.1 또는 fetch → gRPC-Web Proxy → gRPC Server
(Envoy·gRPC-Web Proxy·grpc-web-spring)
서버 — gRPC-Web 활성화
implementation 'org.lognet:grpc-spring-boot-starter:5.x'
grpc:
server:
in-process-name: my-app # gRPC-Web 활성화
또는 Envoy 프록시:
# envoy.yaml
http_filters:
- name: envoy.filters.http.grpc_web
TypeScript 클라이언트
import { UserServiceClient } from './generated/user_service_grpc_web_pb';
const client = new UserServiceClient('https://api.example.com');
client.getUser(req, {}, (err, response) => { ... });
여기서 시험 함정이 하나 있어요. gRPC-Web 제한 — Bidirectional Streaming 미지원 (또는 부분). 단방향 (Unary·Server Streaming만 안전).
Channel 관리
KeepAlive
grpc:
client:
user-service:
enable-keep-alive: true
keep-alive-time: 30s
keep-alive-timeout: 5s
keep-alive-without-calls: true
연결 유휴 시 ping → 끊긴 연결 빠르게 감지.
서버 측
grpc:
server:
enable-keep-alive: true
keep-alive-time: 30s
permit-keep-alive-without-calls: true
permit-keep-alive-time: 5s
여기서 정말 중요한 시험 함정 — 클라이언트·서버 keep-alive 시간 일치. 불일치 시 GOAWAY 프레임. 서버 = 클라이언트 keep-alive 허용해야.
Connection Pool
ManagedChannel channel = ManagedChannelBuilder
.forTarget("user-service:9090")
.keepAliveTime(30, TimeUnit.SECONDS)
.build();
// 단일 채널 = 다중 RPC 멀티플렉싱 (HTTP/2)
// Pool 거의 불필요
HTTP/2 multiplexing으로 단일 채널이 충분.
Deadline 전파
// Service A
@GrpcService
public class ServiceA extends ServiceAImpl {
@Autowired
private ServiceBClient serviceB;
@Override
public void process(Request req, StreamObserver<Response> observer) {
// Service B 호출 — Deadline 자동 전파
Response b = serviceB.callB(req); // 클라이언트 Deadline → B 자동 적용
observer.onNext(toResponse(b));
observer.onCompleted();
}
}
여기서 정말 중요한 시험 함정 — Deadline 자동 전파. A 호출이 5초 → B 호출 시 자동 5초 (또는 남은 시간). 체인 전체 시간 제한.
Reflective Stub — 동적 호출
import io.grpc.stub.AbstractStub;
// 스키마 모르고 호출 — Reflection 사용
ServerReflectionGrpc.ServerReflectionStub stub = ServerReflectionGrpc.newStub(channel);
// 메서드 동적 발견 + 호출
특수 시나리오. 일반은 자동 생성 Stub 사용.
모니터링 — Prometheus
grpc:
server:
metric-server:
enabled: true
자동 메트릭:
grpc.server.calls.duration(히스토그램)grpc.server.calls.received(카운터)- 서비스·메서드·status별 분리
Grafana 대시보드
grpc-server-spring-boot-starter 표준 대시보드 활용.
분산 추적 — OpenTelemetry
implementation 'io.opentelemetry.instrumentation:opentelemetry-grpc-1.6'
@Bean
public ServerInterceptor otelInterceptor(OpenTelemetry otel) {
return GrpcTelemetry.create(otel).newServerInterceptor();
}
@Bean
public ClientInterceptor otelClientInterceptor(OpenTelemetry otel) {
return GrpcTelemetry.create(otel).newClientInterceptor();
}
자동 trace ID 전파·서비스 체인 추적.
시리즈 마무리 — 10편 종합
1편부터 10편까지의 흐름:
| 편 | 주제 | 한 줄 |
|---|---|---|
| 1 | 기초 | gRPC = HTTP/2 + Protobuf, 4 RPC 모드 |
| 2 | Protobuf | 메시지·필드 번호·진화 규칙 |
| 3 | Unary RPC | 1:1, Stub 3종, Deadline·Metadata |
| 4 | Server Streaming | 1:N, 페이징·실시간·로그 |
| 5 | Client Streaming | N:1, 업로드·집계 |
| 6 | Bidirectional | N:N, 채팅·게임·트레이딩 |
| 7 | Interceptors | 인증·로깅·메트릭 횡단 관심사 |
| 8 | Error Handling | 16 Status Code·Rich Errors |
| 9 | Security | TLS·mTLS·JWT·Spring Security |
| 10 | 고급 | Reflection·Health·LB·gRPC-Web·Channel |
gRPC의 거의 모든 운영 패턴을 한 번에 통찰할 토대.
운영 체크리스트
✓ TLS 활성화 (운영)
✓ mTLS (마이크로서비스 사이) 또는 Service Mesh
✓ Health Check (K8s probe·grpc_health_probe)
✓ Reflection 운영 OFF
✓ Compression (큰 페이로드)
✓ Deadline (클라이언트·자동 전파)
✓ KeepAlive (클라이언트·서버 시간 일치)
✓ 클라이언트 사이드 LB 또는 Envoy
✓ 헤드리스 Service (K8s)
✓ Prometheus 메트릭
✓ OpenTelemetry tracing
✓ JWT 또는 OAuth2 (인증)
✓ @PreAuthorize (인가)
✓ Rate Limit (인터셉터)
✓ Retry Policy (재시도 가능 status만)
✓ 명시적 이미지 태그 (latest X)
✓ Audit Log
✓ ExceptionHandler 글로벌
✓ 입력 검증 (Bean Validation)
✓ Streaming 자원 정리 (취소 감지)
시험 직전 한 번 더 — 자주 헷갈리는 함정 모음
여기까지가 10편의 핵심입니다. 시험 직전 또는 실무에서 헷갈릴 때 다시 펼쳐 볼 수 있게 압축 노트로 마무리할게요.
- Reflection = 동적 스키마 발견 (grpcurl 친화)
- 운영 환경 OFF (보안 위험)
- Health Check =
grpc.health.v1.Health/Check표준 - K8s probe —
grpc_health_probe바이너리 - 운영 표준
- Compression — gzip / snappy / identity
- 작은 메시지 (< 1KB) = 압축 비효율
- 로드 밸런싱 — 클라이언트 사이드 (DNS·Headless Service) / Envoy 프록시 / Service Mesh
- gRPC + 일반 K8s Service = LB 안 됨 (Headless Service 필요)
- gRPC-Web = 브라우저 통합, Envoy·proxy 변환
- Bidirectional 미지원 또는 부분
- Channel KeepAlive — 클라이언트·서버 시간 일치
- HTTP/2 multiplexing = 단일 채널이면 충분
- Deadline 자동 전파 (체인 전체 시간 제한)
- Prometheus 메트릭 자동 (
grpc-server-spring-boot-starter) - OpenTelemetry tracing =
GrpcTelemetry.newServerInterceptor - 자동 trace ID 전파
- 운영 체크 — TLS·mTLS·Health·Reflection OFF·Compression·Deadline·KeepAlive·LB·메트릭·tracing·JWT·@PreAuthorize·Rate Limit·Retry·Audit·ExceptionHandler·Validation·Streaming 정리
시리즈 다른 편 (시리즈 마지막)
- 1편 — 기본 개념·HTTP/2·4 RPC 모드
- 2편 — Protocol Buffers
- 3편 — Unary RPC
- 4편 — Server Streaming
- 5편 — Client Streaming
- 6편 — Bidirectional Streaming
- 7편 — Interceptors
- 8편 — Error Handling
- 9편 — Security
- 10편 — 고급 (Reflection·Health·LB·gRPC-Web) (현재 글, 시리즈 마지막)
공식 문서: gRPC Docs / grpc-spring-boot-starter / Envoy gRPC 에서 더 깊이.
gRPC + Spring Boot 마스터 시리즈는 여기서 마무리. 1편부터 10편까지의 흐름이 머리에 남으면 마이크로서비스 통신·CNCF 표준·운영 패턴을 한 번에 통찰할 토대가 됩니다.