gRPC와 REST Callback (Webhook) - 서비스 간 통신 패턴 비교
TL;DR
- gRPC는 HTTP/2와 Protobuf로 고성능 동기 통신과 스트리밍을 제공한다.
- REST Callback(Webhook)은 이벤트 기반 비동기 알림으로 폴링을 대체한다.
- 요구 성능·결합도·실시간성에 따라 두 패턴을 목적에 맞게 선택한다.
1. 개념
gRPC는 강타입 계약과 스트리밍을 지원하는 RPC 프레임워크이고, REST Callback(Webhook)은 서버가 이벤트를 비동기로 통지하는 역방향 API 패턴이다.
2. 배경
REST 기반 API가 널리 쓰였지만 마이크로서비스 시대에 성능, 스키마 불명확, 스트리밍 부재 같은 한계가 드러나면서 고성능 RPC와 이벤트 알림 패턴이 함께 부상했다.
3. 이유
서비스 간 통신에서 지연·대역폭·계약 안정성을 개선하고, 폴링 낭비를 줄여 이벤트 기반 통지를 구현하기 위해 두 방식이 사용된다.
4. 특징
gRPC는 바이너리 직렬화, HTTP/2 멀티플렉싱, 자동 코드 생성, 양방향 스트리밍을 제공하고, Webhook은 간단한 HTTP POST와 재시도/멱등성 설계를 통한 느슨한 결합이 특징이다.
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