gRPC와 REST Callback (Webhook) - 서비스 간 통신 패턴 비교
TL;DR
- gRPC와 REST Callback (Webhook) - 서비스 간 통신 패턴 비교의 핵심 개념을 빠르게 파악할 수 있다.
- 배경과 이유를 통해 왜 필요한지 맥락을 이해할 수 있다.
- 특징과 상세 내용을 통해 실무 적용 포인트를 확인할 수 있다.
1. 개념
gRPC와 REST Callback (Webhook) - 서비스 간 통신 패턴 비교의 핵심 정의와 문제 공간을 간단히 정리한다.
2. 배경
이 주제가 등장한 기술적·조직적 배경과 기존 접근의 한계를 설명한다.
3. 이유
왜 지금 이 방식을 채택해야 하는지, 기대 효과와 트레이드오프를 함께 정리한다.
4. 특징
핵심 동작 방식, 장단점, 적용 시 주의점을 빠르게 훑을 수 있도록 요약한다.
5. 상세 내용
gRPC와 REST Callback (Webhook) - 서비스 간 통신 패턴 비교
작성일: 2026-02-26 카테고리: Backend / Communication / API Design 포함 내용: gRPC, REST Callback, Webhook, Protocol Buffers, HTTP/2, mTLS, HMAC, 마이크로서비스, 스트리밍, 비동기 통신
1. 탄생 배경
1.1 분산 시스템 통신의 진화
┌─────────────────────────────────────────────────────────────────┐
│ 분산 시스템 통신의 진화 타임라인 │
│ │
│ 1991 CORBA ──────── 복잡한 바이너리, 언어 독립 시도 │
│ │ │
│ 1998 SOAP ───────── XML 기반, WSDL 필수, 너무 무거움 │
│ │ │
│ 2000 REST ───────── HTTP 활용, 단순하고 직관적 │
│ │ → 웹 API의 사실상 표준이 됨 │
│ │ │
│ 2007 Webhook ────── Jeff Lindsay가 용어 제안 │
│ │ → 이벤트 기반 알림의 시작 │
│ │ │
│ 2015 gRPC ──────── Google이 오픈소스화 │
│ → 마이크로서비스 시대의 고성능 통신 │
│ │
│ REST가 성공했지만 마이크로서비스 시대에 한계 드러남: │
│ ├── 텍스트(JSON) 직렬화 → 바이너리 대비 느림 │
│ ├── 스키마 강제 없음 → 서비스 간 계약 불명확 │
│ ├── 단방향 통신 → 스트리밍 불가 │
│ └── 폴링 기반 알림 → 리소스 낭비 │
│ │
│ 이 한계를 해결하기 위해 두 가지 방향이 등장: │
│ ├── 동기 고성능 통신: gRPC (HTTP/2 + Protocol Buffers) │
│ └── 비동기 이벤트 알림: REST Callback (Webhook) │
│ │
└─────────────────────────────────────────────────────────────────┘
1.2 gRPC의 탄생 (Google, 2015)
┌─────────────────────────────────────────────────────────────────┐
│ gRPC의 탄생 배경 │
│ │
│ Google 내부 시스템 "Stubby" (2001~): │
│ ├── Google 내부 모든 마이크로서비스 간 통신에 사용 │
│ ├── 초당 수십억 건의 RPC 호출 처리 │
│ ├── 10년 넘게 실전 검증 │
│ └── 하지만 비공개, Google 인프라에 강하게 결합 │
│ │
│ 2015년, Google이 Stubby를 오픈소스화 → gRPC 탄생 │
│ ├── g = gRPC (재귀 약어, 또는 Google) │
│ ├── HTTP/2 기반 (Stubby는 자체 프로토콜) │
│ ├── Protocol Buffers (protobuf) 직렬화 │
│ └── 2017년 CNCF (Cloud Native Computing Foundation) 편입 │
│ │
│ 왜 만들었나? │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ REST의 한계 → gRPC의 해결 │ │
│ │ ───────────────────────────────────────────────── │ │
│ │ JSON 텍스트 직렬화 → Protocol Buffers 바이너리 │ │
│ │ 스키마 부재 → .proto 파일 강타입 계약 │ │
│ │ 단방향 요청-응답 → 양방향 스트리밍 │ │
│ │ HTTP/1.1 제약 → HTTP/2 멀티플렉싱 │ │
│ │ 수동 클라이언트 코드 → protoc 자동 코드 생성 │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
1.3 REST Callback (Webhook)의 탄생
┌─────────────────────────────────────────────────────────────────┐
│ REST Callback (Webhook)의 탄생 │
│ │
│ 2007년, Jeff Lindsay가 "Webhook" 용어를 처음 제안 │
│ "웹의 파이프(pipe)를 만들자 - 서버가 서버에게 알려주자" │
│ │
│ 대중화 과정: │
│ ├── 2010 GitHub → push 이벤트 Webhook 지원 │
│ ├── 2012 Stripe → 결제 이벤트 Webhook │
│ ├── 2014 Slack → 봇/통합 Webhook │
│ └── 현재 거의 모든 SaaS가 Webhook 지원 │
│ │
│ 왜 만들었나? │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ ❌ 폴링 (Polling) 방식: │ │
│ │ │ │
│ │ Client ──► "새 데이터 있어?" ──► Server │ │
│ │ Client ◄── "없어" ◄── Server │ │
│ │ Client ──► "지금은?" ──► Server │ │
│ │ Client ◄── "아직 없어" ◄── Server │ │
│ │ Client ──► "지금은??" ──► Server │ │
│ │ Client ◄── "있어! 여기" ◄── Server │ │
│ │ │ │
│ │ → 99%의 요청이 "없어"... 리소스 낭비! │ │
│ │ │ │
│ │ ✅ Webhook (Callback) 방식: │ │
│ │ │ │
│ │ Client ──► "변화 생기면 이 URL로 알려줘" ──► Server │ │
│ │ (등록) │ │
│ │ │ │
│ │ ... (시간 경과, Client는 다른 일 함) ... │ │
│ │ │ │
│ │ Client ◄── "이벤트 발생! POST 전송" ◄── Server │ │
│ │ │ │
│ │ → 이벤트 있을 때만 알려줌. 효율적! │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
2. 핵심 개념
2.1 gRPC란?
┌─────────────────────────────────────────────────────────────────┐
│ gRPC란? │
│ │
│ gRPC = Google Remote Procedure Call │
│ = 원격 함수를 마치 로컬 함수처럼 호출하는 프레임워크 │
│ │
│ 구성 요소: │
│ ├── .proto 파일: 서비스와 메시지 구조 정의 │
│ ├── protoc 컴파일러: .proto → 각 언어 코드 자동 생성 │
│ ├── HTTP/2: 전송 프로토콜 (멀티플렉싱, 헤더 압축) │
│ └── Protocol Buffers: 바이너리 직렬화 (JSON 대비 ~10배 빠름) │
│ │
│ .proto 파일 예시: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ syntax = "proto3"; │ │
│ │ │ │
│ │ package order; │ │
│ │ │ │
│ │ service OrderService { │ │
│ │ // Unary: 단일 요청 → 단일 응답 │ │
│ │ rpc GetOrder (OrderRequest) │ │
│ │ returns (OrderResponse); │ │
│ │ │ │
│ │ // Server Streaming: 단일 요청 → 응답 스트림 │ │
│ │ rpc ListOrders (ListRequest) │ │
│ │ returns (stream OrderResponse); │ │
│ │ │ │
│ │ // Client Streaming: 요청 스트림 → 단일 응답 │ │
│ │ rpc BatchCreate (stream CreateRequest) │ │
│ │ returns (BatchResponse); │ │
│ │ │ │
│ │ // Bidirectional: 양방향 스트림 │ │
│ │ rpc OrderChat (stream ChatMessage) │ │
│ │ returns (stream ChatMessage); │ │
│ │ } │ │
│ │ │ │
│ │ message OrderRequest { │ │
│ │ int64 order_id = 1; │ │
│ │ } │ │
│ │ │ │
│ │ message OrderResponse { │ │
│ │ int64 order_id = 1; │ │
│ │ string status = 2; │ │
│ │ int32 total_amount = 3; │ │
│ │ repeated Item items = 4; │ │
│ │ } │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ 4가지 통신 패턴: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 1. Unary (단일): │ │
│ │ Client ──[요청]──► Server │ │
│ │ Client ◄──[응답]── Server │ │
│ │ → REST와 유사, 가장 기본 │ │
│ │ │ │
│ │ 2. Server Streaming (서버 스트리밍): │ │
│ │ Client ──[요청]──► Server │ │
│ │ Client ◄──[응답1]── Server │ │
│ │ Client ◄──[응답2]── Server │ │
│ │ Client ◄──[응답N]── Server │ │
│ │ → 실시간 피드, 대량 데이터 전송 │ │
│ │ │ │
│ │ 3. Client Streaming (클라이언트 스트리밍): │ │
│ │ Client ──[요청1]──► Server │ │
│ │ Client ──[요청2]──► Server │ │
│ │ Client ──[요청N]──► Server │ │
│ │ Client ◄──[응답]── Server │ │
│ │ → 파일 업로드, 센서 데이터 수집 │ │
│ │ │ │
│ │ 4. Bidirectional Streaming (양방향 스트리밍): │ │
│ │ Client ──[요청1]──► Server │ │
│ │ Client ◄──[응답1]── Server │ │
│ │ Client ──[요청2]──► Server │ │
│ │ Client ◄──[응답2]── Server │ │
│ │ → 채팅, 실시간 게임, 양방향 데이터 교환 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
2.2 REST Callback (Webhook)이란?
┌─────────────────────────────────────────────────────────────────┐
│ REST Callback (Webhook)이란? │
│ │
│ Webhook = "역방향 API" │
│ 일반 API: 클라이언트가 서버에게 데이터 요청 │
│ Webhook: 서버가 클라이언트에게 이벤트 알려줌 │
│ │
│ 핵심 구조: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 1) 등록 단계: │ │
│ │ 수신자 ──► "이 URL로 알려줘" ──► 발신자 │ │
│ │ POST /webhooks │ │
│ │ { │ │
│ │ "url": "https://my-app.com/webhook/payment", │ │
│ │ "events": ["payment.completed", "payment.failed"] │ │
│ │ } │ │
│ │ │ │
│ │ 2) 이벤트 발생 시: │ │
│ │ 발신자 ──► HTTP POST ──► 수신자 등록 URL │ │
│ │ POST https://my-app.com/webhook/payment │ │
│ │ { │ │
│ │ "event": "payment.completed", │ │
│ │ "data": { │ │
│ │ "payment_id": "pay_abc123", │ │
│ │ "amount": 50000, │ │
│ │ "currency": "KRW" │ │
│ │ }, │ │
│ │ "timestamp": "2026-02-26T10:30:00Z" │ │
│ │ } │ │
│ │ │ │
│ │ 3) 수신자 응답: │ │
│ │ 200 OK → 정상 수신 확인 │ │
│ │ 5xx → 실패, 발신자가 재시도 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ 실제 사용 사례: │
│ ├── GitHub: push, PR, issue 이벤트 알림 │
│ ├── Stripe: 결제 완료, 환불, 구독 변경 알림 │
│ ├── Slack: 메시지 이벤트, 봇 상호작용 │
│ └── AWS SNS: 클라우드 이벤트 HTTP 구독 │
│ │
└─────────────────────────────────────────────────────────────────┘
3. 동작 원리 비교
3.1 gRPC 통신 흐름
┌─────────────────────────────────────────────────────────────────┐
│ gRPC 통신 흐름 상세 │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ [Client Application] │ │
│ │ │ │ │
│ │ │ orderService.getOrder(request) │ │
│ │ ▼ │ │
│ │ [Generated Stub] ← protoc이 자동 생성한 코드 │ │
│ │ │ │ │
│ │ │ Protocol Buffers 직렬화 (객체 → 바이너리) │ │
│ │ ▼ │ │
│ │ [Channel] ← HTTP/2 연결 관리 (연결 풀링, 멀티플렉싱) │ │
│ │ │ │ │
│ │ │ HTTP/2 프레임으로 전송 │ │
│ │ ▼ │ │
│ │ ═══════════ 네트워크 (HTTP/2 + TLS) ════════════ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ [Server Interceptor] ← 인증, 로깅, 메트릭 │ │
│ │ │ │ │
│ │ │ Protocol Buffers 역직렬화 (바이너리 → 객체) │ │
│ │ ▼ │ │
│ │ [Service Implementation] ← 실제 비즈니스 로직 │ │
│ │ │ │ │
│ │ │ 응답 생성 → 직렬화 → 역순으로 전송 │ │
│ │ ▼ │ │
│ │ [Client]가 응답 수신 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ 핵심 특징: │
│ ├── 하나의 TCP 연결에 여러 RPC 동시 처리 (멀티플렉싱) │
│ ├── 바이너리 직렬화로 JSON 대비 페이로드 크기 ~60% 감소 │
│ ├── 헤더 압축 (HPACK)으로 반복 헤더 최소화 │
│ └── Deadline/Timeout 내장: 요청별 제한 시간 설정 │
│ │
└─────────────────────────────────────────────────────────────────┘
3.2 REST Callback 통신 흐름
┌─────────────────────────────────────────────────────────────────┐
│ REST Callback (Webhook) 통신 흐름 │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ [단계 1: Webhook 등록] │ │
│ │ 수신자 ──► POST /webhooks ──► 발신자(Provider) │ │
│ │ {url, events, secret} │ │
│ │ 수신자 ◄── 201 Created ◄── 발신자 │ │
│ │ {webhook_id, status: "active"} │ │
│ │ │ │
│ │ [단계 2: 이벤트 발생 감지] │ │
│ │ 발신자 내부에서 이벤트 발생 │ │
│ │ (예: 결제 완료, 코드 push, 주문 상태 변경) │ │
│ │ │ │
│ │ [단계 3: Webhook 전송] │ │
│ │ 발신자 ──► POST (수신자 URL) ──► 수신자 │ │
│ │ Headers: │ │
│ │ Content-Type: application/json │ │
│ │ X-Webhook-Signature: sha256=abc123... │ │
│ │ X-Webhook-ID: evt_12345 │ │
│ │ Body: {event, data, timestamp} │ │
│ │ │ │
│ │ [단계 4: 수신자 처리 및 응답] │ │
│ │ 수신자 ──► 200 OK ──► 발신자 │ │
│ │ (5초 이내 응답 권장) │ │
│ │ │ │
│ │ [단계 5: 실패 시 재시도] │ │
│ │ 수신자가 5xx 또는 timeout이면: │ │
│ │ ├── 1차 재시도: 1분 후 │ │
│ │ ├── 2차 재시도: 5분 후 │ │
│ │ ├── 3차 재시도: 30분 후 │ │
│ │ ├── 4차 재시도: 2시간 후 │ │
│ │ └── 최종 실패: 알림 또는 Webhook 비활성화 │ │
│ │ (Exponential Backoff 패턴) │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ 주의 사항: │
│ ├── 수신 측은 반드시 public URL 필요 (방화벽 뒤면 안 됨) │
│ ├── 멱등성 처리 필수 (같은 이벤트 중복 수신 가능) │
│ ├── Webhook ID로 중복 감지 │
│ └── 빠른 응답 후 비동기 처리 권장 (큐에 넣고 200 반환) │
│ │
└─────────────────────────────────────────────────────────────────┘
4. 상세 비교표
┌─────────────────────────────────────────────────────────────────┐
│ gRPC vs REST Callback 상세 비교 │
│ │
│ ┌────────────┬─────────────────┬─────────────────────────┐ │
│ │ 항목 │ gRPC │ REST Callback │ │
│ ├────────────┼─────────────────┼─────────────────────────┤ │
│ │ 프로토콜 │ HTTP/2 │ HTTP/1.1 (주로) │ │
│ ├────────────┼─────────────────┼─────────────────────────┤ │
│ │ 데이터형식 │ Protocol Buffers │ JSON (텍스트) │ │
│ │ │ (바이너리) │ │ │
│ ├────────────┼─────────────────┼─────────────────────────┤ │
│ │ 통신방향 │ 양방향 스트리밍 │ 단방향 │ │
│ │ │ │ (서버→클라이언트) │ │
│ ├────────────┼─────────────────┼─────────────────────────┤ │
│ │ 통신패턴 │ 동기 │ 비동기 │ │
│ │ │ (요청-응답) │ (이벤트 기반) │ │
│ ├────────────┼─────────────────┼─────────────────────────┤ │
│ │ 스키마 │ .proto (강타입) │ 자유 형식 │ │
│ │ │ │ (OpenAPI 선택적) │ │
│ ├────────────┼─────────────────┼─────────────────────────┤ │
│ │ 성능 │ 높음 │ 보통 │ │
│ │ │ (바이너리, │ (텍스트, │ │
│ │ │ 멀티플렉싱) │ 연결당 1요청) │ │
│ ├────────────┼─────────────────┼─────────────────────────┤ │
│ │ 코드 생성 │ 자동 (protoc) │ 수동 또는 │ │
│ │ │ 11개 언어 지원 │ OpenAPI codegen │ │
│ ├────────────┼─────────────────┼─────────────────────────┤ │
│ │ 브라우저 │ 제한적 │ 완전 지원 │ │
│ │ │ (gRPC-Web 필요) │ │ │
│ ├────────────┼─────────────────┼─────────────────────────┤ │
│ │ 디버깅 │ 어려움 │ 쉬움 │ │
│ │ │ (바이너리) │ (JSON 가독성) │ │
│ ├────────────┼─────────────────┼─────────────────────────┤ │
│ │ 연결 모델 │ 지속적 │ 일회성 │ │
│ │ │ (Long-lived) │ (이벤트마다 새 연결) │ │
│ ├────────────┼─────────────────┼─────────────────────────┤ │
│ │ 결합도 │ 강한 결합 │ 느슨한 결합 │ │
│ │ │ (.proto 공유) │ (URL만 알면 됨) │ │
│ ├────────────┼─────────────────┼─────────────────────────┤ │
│ │ 적합한 │ 내부 서비스 간 │ 외부 시스템 알림 │ │
│ │ 상황 │ 고성능 동기 통신 │ 이벤트 기반 통지 │ │
│ └────────────┴─────────────────┴─────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
5. 장단점
5.1 gRPC 장점/단점
┌─────────────────────────────────────────────────────────────────┐
│ gRPC 장점/단점 │
│ │
│ ✅ 장점: │
│ ├── 높은 성능 │
│ │ ├── Protocol Buffers: JSON 대비 직렬화 ~10배 빠름 │
│ │ ├── 페이로드 크기 ~60% 감소 │
│ │ └── HTTP/2 멀티플렉싱: 하나의 연결에 다중 요청 │
│ │ │
│ ├── 강타입 계약 (.proto) │
│ │ ├── 컴파일 타임에 타입 오류 감지 │
│ │ ├── Breaking change 자동 감지 │
│ │ └── 서비스 간 명확한 인터페이스 계약 │
│ │ │
│ ├── 양방향 스트리밍 │
│ │ ├── 실시간 데이터 전송 가능 │
│ │ └── 채팅, 모니터링, IoT에 적합 │
│ │ │
│ ├── 코드 자동 생성 │
│ │ ├── protoc으로 11개 언어 클라이언트/서버 코드 생성 │
│ │ └── Java, Go, Python, C++, Kotlin, Swift 등 │
│ │ │
│ └── 내장 Deadline/Timeout │
│ └── 요청별 제한 시간 설정으로 장애 전파 방지 │
│ │
│ ❌ 단점: │
│ ├── 브라우저 직접 지원 불가 │
│ │ └── gRPC-Web 또는 Envoy 프록시 필요 │
│ │ │
│ ├── 바이너리라서 디버깅 어려움 │
│ │ ├── curl로 바로 테스트 불가 │
│ │ └── grpcurl, Postman gRPC 등 별도 도구 필요 │
│ │ │
│ ├── HTTP/2 필수 │
│ │ ├── 일부 프록시/로드밸런서가 HTTP/2 미지원 │
│ │ └── AWS ALB는 gRPC 지원하지만 설정 필요 │
│ │ │
│ ├── 학습 곡선 │
│ │ ├── Protocol Buffers 문법 학습 필요 │
│ │ └── 빌드 파이프라인에 protoc 통합 필요 │
│ │ │
│ └── REST 대비 작은 생태계 │
│ └── 도구, 문서, 커뮤니티가 REST보다 적음 │
│ │
└─────────────────────────────────────────────────────────────────┘
5.2 REST Callback (Webhook) 장점/단점
┌─────────────────────────────────────────────────────────────────┐
│ REST Callback (Webhook) 장점/단점 │
│ │
│ ✅ 장점: │
│ ├── 구현 간단 │
│ │ ├── HTTP POST + JSON만 있으면 됨 │
│ │ ├── 어떤 언어/프레임워크든 쉽게 구현 │
│ │ └── curl로 테스트 가능 │
│ │ │
│ ├── 표준 HTTP → 인프라 친화적 │
│ │ ├── 방화벽, 프록시, CDN 모두 통과 │
│ │ └── 별도 프로토콜 지원 불필요 │
│ │ │
│ ├── 디버깅 쉬움 │
│ │ ├── JSON 페이로드 사람이 읽기 쉬움 │
│ │ └── 웹 브라우저, Postman 등으로 확인 가능 │
│ │ │
│ ├── 기존 인프라 활용 │
│ │ └── 웹 서버만 있으면 수신 가능 │
│ │ │
│ └── 폴링 대비 효율적 │
│ ├── 이벤트 발생 시에만 네트워크 사용 │
│ └── 불필요한 요청 제거 │
│ │
│ ❌ 단점: │
│ ├── 수신 측이 public endpoint 필요 │
│ │ ├── NAT 뒤, localhost에서 수신 불가 │
│ │ └── 개발 시 ngrok 등 터널링 도구 필요 │
│ │ │
│ ├── 전달 보장 어려움 (at-least-once) │
│ │ ├── 네트워크 장애 시 메시지 유실 가능 │
│ │ └── 재시도 시 중복 수신 가능 → 멱등성 처리 필수 │
│ │ │
│ ├── 순서 보장 없음 │
│ │ ├── 이벤트 발생 순서와 수신 순서가 다를 수 있음 │
│ │ └── 재시도로 인해 순서 역전 가능 │
│ │ │
│ ├── 수신 측 장애 시 메시지 유실 가능 │
│ │ ├── 재시도 횟수 초과 시 영구 유실 │
│ │ └── Dead Letter Queue 등 별도 대비 필요 │
│ │ │
│ └── 스키마 강제 없음 │
│ ├── 발신자가 페이로드 구조 변경 가능 │
│ └── 버전 관리를 수동으로 해야 함 │
│ │
└─────────────────────────────────────────────────────────────────┘
6. 신뢰할 수 있는 통신 - 상호 인증과 보안
6.1 gRPC의 신뢰 기반 통신
┌─────────────────────────────────────────────────────────────────┐
│ gRPC의 신뢰 기반 통신 │
│ │
│ gRPC는 3가지 보안 메커니즘을 제공: │
│ ├── 1. mTLS (Mutual TLS) - 가장 일반적 │
│ ├── 2. Token 기반 인증 (JWT, OAuth2) │
│ └── 3. Channel/Call Credentials 결합 │
│ │
└─────────────────────────────────────────────────────────────────┘
mTLS (Mutual TLS)
┌─────────────────────────────────────────────────────────────────┐
│ mTLS (Mutual TLS) - 상호 인증서 검증 │
│ │
│ 일반 TLS vs mTLS: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 일반 TLS (한쪽만 검증): │ │
│ │ Client ──► "너 진짜 서버 맞아?" ──► Server │ │
│ │ Client ◄── "여기 내 인증서" ◄── Server │ │
│ │ Client: "OK, 믿을게" → 통신 시작 │ │
│ │ → 서버만 검증, 클라이언트는 누구든 접속 가능 │ │
│ │ │ │
│ │ mTLS (양쪽 모두 검증): │ │
│ │ Client ──► "너 진짜 서버 맞아?" ──► Server │ │
│ │ Client ◄── "내 인증서, 너 인증서도 줘" ◄── Server │ │
│ │ Client ──► "여기 내 인증서" ──► Server │ │
│ │ 양쪽 모두 검증 완료 → 통신 시작 │ │
│ │ → 서버도 클라이언트도 서로 신원 확인! │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ mTLS Handshake 과정: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ [1] Client → Server: ClientHello │ │
│ │ (지원 TLS 버전, 암호화 목록) │ │
│ │ │ │
│ │ [2] Server → Client: ServerHello │ │
│ │ + Server Certificate (서버 인증서) │ │
│ │ + CertificateRequest (클라이언트 인증서 요청) │ │
│ │ │ │
│ │ [3] Client: 서버 인증서를 CA로 검증 │ │
│ │ │ │
│ │ [4] Client → Server: Client Certificate │ │
│ │ (클라이언트 인증서 제시) │ │
│ │ │ │
│ │ [5] Server: 클라이언트 인증서를 CA로 검증 │ │
│ │ │ │
│ │ [6] 양쪽 검증 완료 → 암호화된 채널 생성 │ │
│ │ 모든 gRPC 메시지가 이 채널로 전송 │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ Kubernetes에서의 mTLS: │
│ ├── Istio (Service Mesh): 자동으로 Pod 간 mTLS 주입 │
│ │ └── 애플리케이션 코드 수정 없이 sidecar proxy가 처리 │
│ ├── Linkerd: 마찬가지로 자동 mTLS │
│ └── 인증서 갱신도 자동 (cert-manager 등) │
│ │
│ 장점: │
│ ├── 양쪽 모두 신원 확인 → 스푸핑 방지 │
│ ├── 통신 전체 암호화 → 도청 불가 │
│ └── Service Mesh와 결합 시 무설정(Zero-config) │
│ │
└─────────────────────────────────────────────────────────────────┘
Token 기반 인증 및 Channel 수준 보안
┌─────────────────────────────────────────────────────────────────┐
│ gRPC Token 기반 인증 & Channel 보안 │
│ │
│ Token 기반 인증: │
│ ├── JWT 또는 OAuth2 토큰을 gRPC Metadata(헤더)에 포함 │
│ ├── Server Interceptor에서 매 요청마다 토큰 검증 │
│ └── Call Credentials API로 자동 토큰 주입 │
│ │
│ Channel 수준 보안 체계: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ ChannelCredentials (채널 수준): │ │
│ │ └── TLS 채널 설정 (암호화, 서버 인증서 검증) │ │
│ │ │ │
│ │ CallCredentials (요청 수준): │ │
│ │ └── 매 요청마다 인증 정보 첨부 (JWT, OAuth Token) │ │
│ │ │ │
│ │ CompositeCredentials (결합): │ │
│ │ └── ChannelCredentials + CallCredentials 동시 적용 │ │
│ │ └── TLS로 채널 암호화 + JWT로 요청 인증 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ Kotlin 구현 예시 (서버 측 Interceptor): │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ class AuthInterceptor : ServerInterceptor { │ │
│ │ │ │
│ │ override fun <Req, Resp> interceptCall( │ │
│ │ call: ServerCall<Req, Resp>, │ │
│ │ headers: Metadata, │ │
│ │ next: ServerCallHandler<Req, Resp> │ │
│ │ ): ServerCall.Listener<Req> { │ │
│ │ │ │
│ │ val token = headers.get( │ │
│ │ Metadata.Key.of("authorization", │ │
│ │ Metadata.ASCII_STRING_MARSHALLER) │ │
│ │ ) │ │
│ │ │ │
│ │ if (token == null || !validateJwt(token)) { │ │
│ │ call.close( │ │
│ │ Status.UNAUTHENTICATED │ │
│ │ .withDescription("Invalid token"), │ │
│ │ Metadata() │ │
│ │ ) │ │
│ │ return object : ServerCall.Listener<Req>() {} │ │
│ │ } │ │
│ │ │ │
│ │ return next.startCall(call, headers) │ │
│ │ } │ │
│ │ } │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ Kotlin 구현 예시 (클라이언트 측 mTLS + Token): │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ // TLS 채널 생성 (mTLS) │ │
│ │ val channelCreds = TlsChannelCredentials.newBuilder() │ │
│ │ .keyManager(clientCert, clientKey) // 클라이언트 인증│ │
│ │ .trustManager(caCert) // CA 인증서 │ │
│ │ .build() │ │
│ │ │ │
│ │ // 요청별 JWT 토큰 첨부 │ │
│ │ val callCreds = CallCredentials { applier -> │ │
│ │ val headers = Metadata() │ │
│ │ headers.put(authKey, "Bearer $jwtToken") │ │
│ │ applier.apply(headers) │ │
│ │ } │ │
│ │ │ │
│ │ // 채널 + 토큰 결합 │ │
│ │ val channel = Grpc.newChannelBuilder( │ │
│ │ "order-service:50051", channelCreds │ │
│ │ ).build() │ │
│ │ │ │
│ │ val stub = OrderServiceGrpc.newBlockingStub(channel) │ │
│ │ .withCallCredentials(callCreds) │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
6.2 REST Callback (Webhook)의 신뢰 기반 통신
HMAC 서명 검증
┌─────────────────────────────────────────────────────────────────┐
│ HMAC 서명 검증 - Webhook 보안의 핵심 │
│ │
│ 가장 널리 쓰이는 Webhook 보안 방식 │
│ GitHub, Stripe, Slack 등이 채택 │
│ │
│ 원리: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ [사전 준비] │ │
│ │ 발신자와 수신자가 Secret Key를 공유 │ │
│ │ (Webhook 등록 시 생성, 예: whsec_abc123...) │ │
│ │ │ │
│ │ [전송 시 - 발신자] │ │
│ │ 1. JSON payload 준비 │ │
│ │ 2. HMAC-SHA256(secret, payload) → 서명 생성 │ │
│ │ 3. 서명을 헤더에 포함하여 전송 │ │
│ │ │ │
│ │ POST https://my-app.com/webhook │ │
│ │ X-Hub-Signature-256: sha256=a1b2c3d4e5f6... │ │
│ │ Content-Type: application/json │ │
│ │ {"event": "push", "ref": "refs/heads/main", ...} │ │
│ │ │ │
│ │ [수신 시 - 수신자] │ │
│ │ 1. 요청 body(payload) 추출 │ │
│ │ 2. 같은 Secret으로 HMAC-SHA256 재계산 │ │
│ │ 3. 헤더의 서명과 비교 │ │
│ │ 4. 일치 → 신뢰, 불일치 → 거부 (403) │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ GitHub Webhook 서명 검증 (Kotlin): │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ fun verifyGitHubSignature( │ │
│ │ payload: String, │ │
│ │ signatureHeader: String, // "sha256=abc..." │ │
│ │ secret: String │ │
│ │ ): Boolean { │ │
│ │ val mac = Mac.getInstance("HmacSHA256") │ │
│ │ mac.init(SecretKeySpec( │ │
│ │ secret.toByteArray(), "HmacSHA256" │ │
│ │ )) │ │
│ │ val computed = "sha256=" + mac │ │
│ │ .doFinal(payload.toByteArray()) │ │
│ │ .joinToString("") { "%02x".format(it) } │ │
│ │ │ │
│ │ // Timing-safe 비교 (타이밍 공격 방지) │ │
│ │ return MessageDigest.isEqual( │ │
│ │ computed.toByteArray(), │ │
│ │ signatureHeader.toByteArray() │ │
│ │ ) │ │
│ │ } │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ 왜 HMAC인가? │
│ ├── Secret을 모르면 서명 위조 불가 │
│ ├── payload가 변조되면 서명 불일치 │
│ └── 발신자 신원 + 메시지 무결성 동시 보장 │
│ │
└─────────────────────────────────────────────────────────────────┘
Webhook Secret + Timestamp (Replay Attack 방지)
┌─────────────────────────────────────────────────────────────────┐
│ Timestamp 기반 Replay Attack 방지 │
│ │
│ HMAC만으로는 부족한 이유: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ ❌ Replay Attack (재전송 공격): │ │
│ │ 1. 공격자가 정상 Webhook 요청을 네트워크에서 캡처 │ │
│ │ 2. 동일 요청을 나중에 그대로 재전송 │ │
│ │ 3. 서명이 유효하므로 수신자가 수락! │ │
│ │ → 결제 Webhook이면 이중 결제 처리 위험 │ │
│ │ │ │
│ │ ✅ Timestamp + HMAC으로 방지: │ │
│ │ 1. 서명에 타임스탬프 포함 │ │
│ │ 2. 수신자가 현재 시각과 비교 │ │
│ │ 3. 5분 이상 지난 요청은 거부 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ Stripe 방식 (v1 서명 스킴): │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 서명 대상 = "{timestamp}.{payload}" │ │
│ │ │ │
│ │ Stripe-Signature 헤더: │ │
│ │ t=1708934400, │ │
│ │ v1=5257a869e7ecebeda32affa62cdca3fa51c..., │ │
│ │ v0=6ffbb59b2300aae63f272406069a9788598b... │ │
│ │ │ │
│ │ 수신자 검증 로직: │ │
│ │ 1. t (타임스탬프) 추출 │ │
│ │ 2. 현재 시각 - t > 300초면 거부 (5분 초과) │ │
│ │ 3. signed_payload = "{t}.{body}" 구성 │ │
│ │ 4. HMAC-SHA256(secret, signed_payload) 계산 │ │
│ │ 5. v1 값과 비교 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ Kotlin 구현 예시 (Stripe 방식): │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ fun verifyStripeWebhook( │ │
│ │ payload: String, │ │
│ │ sigHeader: String, │ │
│ │ secret: String, │ │
│ │ tolerance: Long = 300 // 5분 │ │
│ │ ): Boolean { │ │
│ │ val parts = sigHeader.split(",") │ │
│ │ .associate { │ │
│ │ val (k, v) = it.split("=", limit = 2) │ │
│ │ k.trim() to v.trim() │ │
│ │ } │ │
│ │ │ │
│ │ val timestamp = parts["t"]?.toLongOrNull() │ │
│ │ ?: return false │ │
│ │ │ │
│ │ // Replay attack 방지: 5분 초과 거부 │ │
│ │ val now = Instant.now().epochSecond │ │
│ │ if (abs(now - timestamp) > tolerance) │ │
│ │ return false │ │
│ │ │ │
│ │ // 서명 검증 │ │
│ │ val signedPayload = "$timestamp.$payload" │ │
│ │ val mac = Mac.getInstance("HmacSHA256") │ │
│ │ mac.init(SecretKeySpec( │ │
│ │ secret.toByteArray(), "HmacSHA256")) │ │
│ │ val expected = mac.doFinal( │ │
│ │ signedPayload.toByteArray() │ │
│ │ ).toHex() │ │
│ │ │ │
│ │ return MessageDigest.isEqual( │ │
│ │ expected.toByteArray(), │ │
│ │ (parts["v1"] ?: "").toByteArray() │ │
│ │ ) │ │
│ │ } │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
기타 Webhook 보안 방식
┌─────────────────────────────────────────────────────────────────┐
│ 기타 REST Callback 보안 방식 │
│ │
│ 1. mTLS (양방향 TLS): │
│ ├── REST Callback에서도 사용 가능 │
│ ├── 발신자가 수신자 URL 호출 시 양쪽 인증서 교환 │
│ ├── 가장 강력하지만 설정 복잡 │
│ └── 수신자가 클라이언트 인증서 관리해야 함 │
│ │
│ 2. IP Whitelist (IP 허용 목록): │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 수신자 방화벽/리버스 프록시 설정: │ │
│ │ allow 192.30.252.0/22; # GitHub Webhook IP 대역 │ │
│ │ allow 140.82.112.0/20; # GitHub Webhook IP 대역 │ │
│ │ deny all; │ │
│ │ │ │
│ │ 장점: 단순, 네트워크 수준 차단 │ │
│ │ 단점: IP 변경 시 수동 업데이트, CDN 사용 시 복잡 │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ 3. OAuth2 Bearer Token: │
│ ├── Webhook 등록 시 수신자가 Bearer Token 발급 │
│ ├── 발신자가 콜백 요청 시 Authorization 헤더에 포함 │
│ ├── 수신자가 토큰 유효성 검증 │
│ └── 토큰 만료/갱신 관리 필요 │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 발신자 → 수신자 Webhook 전송: │ │
│ │ POST /webhook/payment │ │
│ │ Authorization: Bearer eyJhbGciOiJIUzI1NiI... │ │
│ │ Content-Type: application/json │ │
│ │ {"event": "payment.completed", ...} │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ 권장 조합 (Defense in Depth): │
│ ├── HMAC 서명 + Timestamp (필수) │
│ ├── IP Whitelist (추가 보안) │
│ └── HTTPS 필수 (전송 구간 암호화) │
│ │
└─────────────────────────────────────────────────────────────────┘
6.3 보안 비교 다이어그램 및 요약표
┌─────────────────────────────────────────────────────────────────┐
│ gRPC mTLS vs Webhook HMAC 서명 흐름 비교 │
│ │
│ [gRPC mTLS 흐름] │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ Client Server │ │
│ │ │ │ │ │
│ │ │── ClientHello ────────────────►│ │ │
│ │ │◄── ServerHello + ServerCert ───│ │ │
│ │ │ + CertificateRequest │ │ │
│ │ │── ClientCert ─────────────────►│ │ │
│ │ │ (상호 인증서 검증 완료) │ │ │
│ │ │◄══ 암호화된 gRPC 채널 수립 ═══►│ │ │
│ │ │── RPC Call (바이너리) ────────►│ │ │
│ │ │◄── RPC Response ──────────────│ │ │
│ │ │ │ │ │
│ │ → 채널 수립 후 모든 통신이 암호화됨 │ │
│ │ → 인증은 연결 시 1회, 이후 자동 적용 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ [Webhook HMAC 서명 흐름] │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 발신자(Provider) 수신자(Consumer) │ │
│ │ │ │ │ │
│ │ │ [이벤트 발생] │ │ │
│ │ │ 1. payload 생성 │ │ │
│ │ │ 2. HMAC-SHA256(secret, │ │ │
│ │ │ timestamp+payload) = sig │ │ │
│ │ │ │ │ │
│ │ │── POST + sig헤더 ────────────►│ │ │
│ │ │ │ 1. sig 추출 │ │
│ │ │ │ 2. timestamp확인 │ │
│ │ │ │ 3. HMAC 재계산 │ │
│ │ │ │ 4. 서명 비교 │ │
│ │ │◄── 200 OK ────────────────────│ │ │
│ │ │ │ │ │
│ │ → 매 요청마다 서명 계산/검증 │ │
│ │ → HTTPS로 전송 구간 암호화 (별도) │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ 보안 비교 요약표 │
│ │
│ ┌──────────────┬──────────────────┬──────────────────────┐ │
│ │ 보안 항목 │ gRPC │ REST Callback │ │
│ ├──────────────┼──────────────────┼──────────────────────┤ │
│ │ 주요 인증 │ mTLS │ HMAC 서명 │ │
│ │ 방식 │ (상호 인증서) │ (공유 Secret) │ │
│ ├──────────────┼──────────────────┼──────────────────────┤ │
│ │ 전송 암호화 │ TLS 내장 │ HTTPS (별도 설정) │ │
│ │ │ (HTTP/2 + TLS) │ │ │
│ ├──────────────┼──────────────────┼──────────────────────┤ │
│ │ 메시지 │ .proto 스키마 │ HMAC-SHA256 │ │
│ │ 무결성 │ + TLS │ 서명 검증 │ │
│ ├──────────────┼──────────────────┼──────────────────────┤ │
│ │ Replay │ TLS가 자체 방지 │ Timestamp 검증 │ │
│ │ 방지 │ │ (수동 구현) │ │
│ ├──────────────┼──────────────────┼──────────────────────┤ │
│ │ 토큰 인증 │ Metadata에 │ Authorization │ │
│ │ │ JWT/OAuth2 │ Bearer Token │ │
│ ├──────────────┼──────────────────┼──────────────────────┤ │
│ │ IP 제한 │ 네트워크 정책 │ IP Whitelist │ │
│ │ │ (K8s NetworkPolicy)│ │ │
│ ├──────────────┼──────────────────┼──────────────────────┤ │
│ │ 설정 난이도 │ Service Mesh면 │ HMAC은 간단 │ │
│ │ │ 자동 (쉬움) │ mTLS는 복잡 │ │
│ ├──────────────┼──────────────────┼──────────────────────┤ │
│ │ 적합한 환경 │ 내부 서비스 │ 외부 시스템 │ │
│ │ │ (신뢰 경계 내) │ (인터넷 경유) │ │
│ └──────────────┴──────────────────┴──────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
7. 실전 선택 가이드
7.1 이것을 선택하라
┌─────────────────────────────────────────────────────────────────┐
│ 실전 선택 가이드 │
│ │
│ gRPC를 선택하라: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ ✅ 내부 마이크로서비스 간 동기 통신 │ │
│ │ ✅ 고성능이 필요한 경우 (대량 데이터, 낮은 지연) │ │
│ │ ✅ 양방향 스트리밍이 필요한 경우 │ │
│ │ ✅ 강타입 계약으로 서비스 간 안정성 확보 │ │
│ │ ✅ 다중 언어(polyglot) 마이크로서비스 환경 │ │
│ │ ✅ Kubernetes / Service Mesh 환경 │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ REST Callback (Webhook)을 선택하라: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ ✅ 외부 시스템에 이벤트 알림 전송 │ │
│ │ ✅ 느슨한 결합이 필요한 경우 │ │
│ │ ✅ 기존 HTTP 인프라만으로 충분한 경우 │ │
│ │ ✅ 이벤트 기반 비동기 통지 │ │
│ │ ✅ 파트너/고객에게 실시간 알림 제공 │ │
│ │ ✅ 빠른 통합이 필요한 경우 (간단한 구현) │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ 둘 다 사용하라 (가장 흔한 패턴): │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ ✅ 내부 서비스: gRPC (고성능 동기 통신) │ │
│ │ ✅ 외부 알림: REST Callback (이벤트 통지) │ │
│ │ │ │
│ │ 예: 결제 시스템 │ │
│ │ ├── 주문 서비스 ─[gRPC]─► 결제 서비스 (내부) │ │
│ │ ├── 결제 서비스 ─[gRPC]─► 재고 서비스 (내부) │ │
│ │ └── 결제 서비스 ─[Webhook]─► 가맹점 알림 (외부) │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
7.2 실제 아키텍처 예시
┌─────────────────────────────────────────────────────────────────┐
│ 실제 아키텍처 예시 │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ [Kubernetes 클러스터 (내부)] │ │
│ │ ┌──────────────────────────────────────────────┐ │ │
│ │ │ Service Mesh (Istio) - 자동 mTLS │ │ │
│ │ │ │ │ │
│ │ │ ┌───────────┐ gRPC ┌──────────────┐ │ │ │
│ │ │ │ API │────────►│ Order │ │ │ │
│ │ │ │ Gateway │ │ Service │ │ │ │
│ │ │ └───────────┘ └──────┬───────┘ │ │ │
│ │ │ │ gRPC │ │ │
│ │ │ ▼ │ │ │
│ │ │ ┌──────────────┐ │ │ │
│ │ │ │ Payment │ │ │ │
│ │ │ │ Service │ │ │ │
│ │ │ └──────┬───────┘ │ │ │
│ │ │ │ gRPC │ │ │
│ │ │ ▼ │ │ │
│ │ │ ┌──────────────┐ │ │ │
│ │ │ │ Inventory │ │ │ │
│ │ │ │ Service │ │ │ │
│ │ │ └──────────────┘ │ │ │
│ │ └────────────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ │ REST Callback │ │
│ │ │ (HMAC 서명 + HTTPS) │ │
│ │ ▼ │ │
│ │ [외부 시스템] │ │
│ │ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ 가맹점 │ │ 파트너 │ │ │
│ │ │ Webhook 수신 │ │ Webhook 수신 │ │ │
│ │ └──────────────┘ └──────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ 내부 통신 (gRPC): │
│ ├── Istio sidecar가 자동으로 mTLS 적용 │
│ ├── 서비스 간 .proto 파일 공유 (git submodule 등) │
│ ├── HTTP/2 멀티플렉싱으로 고성능 │
│ └── 서비스별 Interceptor로 인증/인가 처리 │
│ │
│ 외부 알림 (REST Callback): │
│ ├── HMAC-SHA256 서명 + Timestamp (Replay 방지) │
│ ├── HTTPS 필수 (전송 구간 암호화) │
│ ├── 재시도 with Exponential Backoff │
│ ├── Webhook 이벤트 로그 저장 (감사 추적) │
│ └── 수신 측 장애 대비 Dead Letter Queue │
│ │
│ 핵심 원칙: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ "신뢰 경계(Trust Boundary)를 기준으로 분리" │ │
│ │ │ │
│ │ 내부 (신뢰 경계 안) → gRPC + mTLS │ │
│ │ 외부 (신뢰 경계 밖) → REST Callback + HMAC │ │
│ │ │ │
│ │ 이유: │ │
│ │ ├── gRPC: 스키마 공유 가능, 고성능, 양방향 │ │
│ │ │ → 내부 서비스끼리는 .proto 공유가 자연스럽 │ │
│ │ │ │ │
│ │ └── Webhook: 느슨한 결합, 표준 HTTP │ │
│ │ → 외부 파트너에게 .proto 강제할 수 없음 │ │
│ │ → HTTP + JSON이 범용적 │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
8. 정리
┌─────────────────────────────────────────────────────────────────┐
│ gRPC vs REST Callback 핵심 정리 │
│ │
│ gRPC: │
│ ├── Google이 Stubby를 오픈소스화 (2015) │
│ ├── HTTP/2 + Protocol Buffers = 고성능 바이너리 통신 │
│ ├── 4가지 스트리밍 패턴 (Unary, Server, Client, Bidi) │
│ ├── .proto 파일로 강타입 계약 + 자동 코드 생성 │
│ ├── 보안: mTLS (Service Mesh 자동) + JWT/OAuth2 │
│ └── 적합: 내부 마이크로서비스 간 동기 고성능 통신 │
│ │
│ REST Callback (Webhook): │
│ ├── Jeff Lindsay가 2007년 용어 제안 │
│ ├── HTTP POST + JSON = 역방향 API (서버→클라이언트 알림) │
│ ├── 이벤트 기반 비동기 통지 (폴링의 대안) │
│ ├── 재시도 + Exponential Backoff로 전달 보장 │
│ ├── 보안: HMAC 서명 + Timestamp (Replay 방지) │
│ └── 적합: 외부 시스템 이벤트 알림, 느슨한 결합 │
│ │
│ 보안의 핵심: │
│ ├── gRPC: mTLS로 양쪽 인증서 검증 (K8s에선 자동) │
│ ├── Webhook: HMAC-SHA256 서명 + Timestamp + HTTPS │
│ └── 둘 다: 신뢰 경계에 따라 적절히 선택 │
│ │
│ 실전 패턴: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 내부 서비스 ←─ gRPC (mTLS) ─→ 내부 서비스 │ │
│ │ │ │ │
│ │ └── REST Callback (HMAC) ──→ 외부 파트너/고객 │ │
│ │ │ │
│ │ "내부는 gRPC, 외부는 Webhook" │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
관련 키워드
gRPC, REST Callback, Webhook, Protocol Buffers, HTTP/2, mTLS, HMAC-SHA256, 마이크로서비스, 스트리밍, 비동기 통신, Service Mesh, Istio, protobuf, Replay Attack