학습 목표
동기 vs 비동기 서비스 간 통신의 결합·지연·일관성 트레이드오프를 설명할 수 있다.
REST와 gRPC를 동기 IPC 관점에서 비교·선택할 수 있다.
메시지 큐·이벤트 기반 비동기 통신의 역할을 설명할 수 있다.
Saga로 분산 트랜잭션을 보상(compensating) 으로 다루는 방식을 설명할 수 있다.
문제 상황
- 주문 API p99 2.8s — Payment·Inventory·Points 순차 REST, hop마다 +400ms
- Payment 503 → Order 503 — caller가 전체 실패, 재고는 이미 예약됨
- 모바일이 Catalog 20번 호출 — BFF 없이 chatty REST
- 팀 A는 JSON REST, 팀 B는 Protobuf gRPC — Gateway에서 변환 지옥
- "비동기로 바꿨다"는데 consumer 없이 큐만 쌓임 — 멱등·재시도 없음
- 주문·결제·재고 3서비스 — DB 3개인데 2PC 기대 → 불가
앞서 CAP·일관성을 봤다. 서비스 경계를 넘는 호출 방식이 일관성·가용성을 결정한다.
1. 동기 vs 비동기
| 동기 (request-response) | 비동기 (message / event) | |
|---|---|---|
| 호출 | 응답 올 때까지 대기 | 보내고 반환 (202·ACK) |
| 결합 | 시간·가용성 결합 | 느슨 (broker·큐) |
| 지연 | hop 합산 | caller 짧음, 처리 백그라운드 |
| 일관성 | 즉시 확인 쉬움 | eventual + 보상 |
| 장애 | callee down → caller 실패 | 재시도·DLQ·격리 |

동기가 맞는 때
| 케이스 | 이유 |
|---|---|
| 즉시 성공/실패 필요 | 결제 승인, 재고 확정 |
| read-your-writes | 방금 쓴 데이터 바로 조회 |
| 단순 1~2 hop | 오케스트레이션 짧음 |
비동기가 맞는 때
| 케이스 | 이유 |
|---|---|
| 부가 side effect | 이메일·알림·분석 |
| 피크 흡수 | 큐로 버퍼 |
| 다수 consumer | fan-out without caller change |
- 하이브리드 — 핵심 path 동기, 나머지 이벤트 (EDA 편과 연결)
- Network — HTTP 요청-응답 vs keep-alive·지연; 여기서는 아키텍처 선택
2. REST vs gRPC (동기 IPC)
| REST (HTTP/JSON) | gRPC (HTTP/2 + Protobuf) | |
|---|---|---|
| 계약 | OpenAPI·관례 | .proto 강한 스키마 |
| 성능 | JSON 직렬화·헤더 | 바이너리·multiplex |
| 스트리밍 | SSE·chunk (제한적) | 양방향 stream |
| 브라우저 | 네이티브 | gRPC-Web·Gateway 필요 |
| 디버깅 | curl·브라우저 | grpcurl·전용 도구 |
| 적합 | 공개 API·BFF·범용 | 내부 고QPS·typed RPC |

// catalog.proto (conceptual)
service CatalogService {
rpc GetProduct (GetProductRequest) returns (Product);
rpc ListProducts (ListRequest) returns (stream Product);
}
GET /products/sku-42 HTTP/1.1
Accept: application/json
- 외부 REST + 내부 gRPC — 흔한 strangler 패턴
- 버전 — REST URL·header; gRPC proto package·field number 호환
- 타임아웃·재시도 — 동기 IPC 필수 (내고장성 편)
3. 메시지 큐·비동기 패턴
메시지 큐 — producer가 메시지를 broker에 넣고, consumer가 비동기로 꺼낸다.

| 패턴 | 설명 |
|---|---|
| Point-to-point | 큐 하나 ↔ consumer group (work queue) |
| Pub/Sub | topic · 여러 subscriber |
| Competing consumers | 수평 scale-out |
| DLQ | N번 실패 → 격리·수동 조사 |
| 보장 | 의미 | 설계 |
|---|---|---|
| At-most-once | 유실 가능 | 로그 드물 |
| At-least-once | 중복 가능 | 멱등 consumer |
| Exactly-once | 구현 비용 | Kafka txn·idempotent producer |
{
"messageId": "msg-8f2a",
"type": "OrderPlaced",
"orderId": "ord-991",
"occurredAt": "2026-07-02T06:00:00Z",
"payload": { "sku": "sku-42", "qty": 2 }
}
- Correlation ID — 동기 hop + async 한 trace로
- Outbox — DB commit + 같은 TX에 outbox row → relay (데이터 편 예고)
- RabbitMQ·SQS·Kafka — 순서·지연·보장 다름 → 워크로드 맞춤
4. Saga — 분산 트랜잭션
2PC/XA across services — 느리고 가용성·운영 나쁨. 실무는 Saga.
여러 로컬 TX + 실패 시 보상 TX (rollback 대신 undo)

Choreography (이벤트)
각 서비스가 이벤트 구독 → 다음 이벤트 발행. 중앙 orchestrator 없음.
OrderPlaced → InventoryReserved → PaymentCaptured → OrderConfirmed
(실패) → InventoryReleased
Orchestration (오케스트레이터)
Saga orchestrator가 단계 지시·보상 호출.
Orchestrator → reserve inventory → charge payment → confirm order
← fail payment ← compensate inventory
| Choreography | Orchestration | |
|---|---|---|
| 결합 | 이벤트 계약 | orchestrator API |
| 가시성 | 흐름 분산 | 한 곳에서 상태 |
| 복잡도 | 단계 적을 때 | 긴 workflow |
| 위험 | 순환 이벤트 | orchestrator SPOF (stateless + durable) |
{
"sagaId": "saga-771",
"step": "PaymentCapture",
"compensations": [
{ "service": "inventory", "action": "ReleaseReservation" },
{ "service": "order", "action": "MarkFailed" }
]
}
- Saga = eventual consistency — 각 step 로컬 ACID
- 멱등 + 보상 가능한 operation 설계 — "취소"가 비즈니스로 정의돼야 함
5. 선택 가이드
| 질문 | 방향 |
|---|---|
| 사용자가 결과를 기다리나 | 동기 (또는 폴링·WebSocket) |
| 실패 시 즉시 알려야 하나 | 동기 + 명확 error |
| N개 downstream | 비동기 fan-out |
| 트랜잭션 여러 DB | Saga + 보상 |
| 공개 API | REST (+ OpenAPI) |
| 내부 고성능 | gRPC |
| hop 3+ 동기 | 재설계 — BFF·async·saga |
| 안티패턴 | 대안 |
|---|---|
| Distributed monolith sync mesh | 이벤트·API 경계 재정의 |
| 무한 retry 동기 | timeout·circuit breaker |
| Chatty REST | BFF·batch·gRPC |
| Saga 없이 다단계 write | 보상 설계·outbox |
6. 정리
- 동기 — 즉시·강일관·짧은 chain; 비동기 — decouple·버퍼·eventual
- REST 공개·범용, gRPC 내부·typed·stream
- 큐 — at-least-once 전제, 멱등·DLQ
- Saga — 2PC 대신 로컬 TX + 보상
- 다음 — 내고장성 (timeout·retry·circuit breaker)
다음에 다룰 것
- 내고장성과 복원력
- 타임아웃, 재시도, circuit breaker, bulkhead
해당 내용은 Fundamentals of Software Architecture (Mark Richards, Neal Ford) 및 Designing Data-Intensive Applications (Martin Kleppmann) 의 내용을 기반으로 합니다.
'소프트웨어 아키텍처' 카테고리의 다른 글
| 데이터 저장소 선택 (0) | 2026.07.04 |
|---|---|
| 내고장성과 복원력 (0) | 2026.07.03 |
| CAP과 일관성 모델 (0) | 2026.07.01 |
| 분산 시스템 기초 (0) | 2026.06.30 |
| 도메인 주도 설계 입문 (0) | 2026.06.29 |