MSA에서 각 서비스는 독립적으로 배포·운영되며, 비즈니스 기능을 완수하기 위해 다른 서비스와 네트워크를 통해 통신해야 한다. 모놀리식에서는 메서드 호출(in-process call)이었던 것이 MSA에서는 네트워크 호출(remote call)로 바뀌기 때문에, 통신 방식의 선택이 시스템의 성능·신뢰성·결합도에 직접적인 영향을 미친다.
핵심 질문: "이 호출은 응답을 기다려야 하는가, 아니면 보내고 잊어도 되는가?"
서비스 간 통신
/ \
동기 (Sync) 비동기 (Async)
/ \ / \
REST gRPC 메시지 큐 이벤트 스트림
GraphQL (Point-to-Point) (Pub/Sub)
| 구분 | 동기 (Synchronous) | 비동기 (Asynchronous) |
|---|---|---|
| 요청-응답 | 요청 후 응답을 기다림 | 요청 후 즉시 반환 (응답을 기다리지 않음) |
| 결합도 | 상대적으로 높음 | 낮음 |
| 실시간성 | 즉시 결과 확인 가능 | 처리 완료 시점을 보장하지 않음 |
| 복잡도 | 단순 | 메시지 브로커 등 인프라 필요 |
| 장애 전파 | 높음 (호출 대상 장애 시 함께 장애) | 낮음 (브로커가 버퍼 역할) |
HTTP 프로토콜 기반의 가장 보편적인 서비스 간 통신 방식이다. 리소스를 URI로 식별하고, HTTP 메서드(GET, POST, PUT, DELETE)로 조작한다.
GET /api/orders/123 → 주문 조회
POST /api/orders → 주문 생성
PUT /api/orders/123 → 주문 수정
DELETE /api/orders/123 → 주문 삭제
요청/응답 예시:
// 요청
GET /api/users/42 HTTP/1.1
Host: user-service:8080
Accept: application/json
// 응답
HTTP/1.1 200 OK
Content-Type: application/json
{
"id": 42,
"name": "홍길동",
"email": "hong@example.com"
}
| 장점 | 단점 |
|---|---|
| 표준화된 HTTP 프로토콜 사용 | 텍스트 기반(JSON)으로 직렬화 오버헤드 |
| 언어/플랫폼 독립적 | 동기 블로킹 방식으로 지연 전파 |
| 학습 비용 낮음, 생태계 풍부 | HTTP/1.1 기준 연결당 하나의 요청 |
| 브라우저에서 직접 호출 가능 | 양방향 통신 불가 (서버 → 클라이언트 푸시 불가) |
| 디버깅/테스트 용이 (curl, Postman) |
적합한 경우: CRUD 중심의 단순한 서비스 간 호출, 외부 API 공개
Google이 개발한 고성능 RPC 프레임워크로, Protocol Buffers(Protobuf) 를 사용한 바이너리 직렬화와 HTTP/2 기반 통신을 제공한다.
Proto 파일 정의:
syntax = "proto3";
service UserService {
rpc GetUser (UserRequest) returns (UserResponse);
rpc ListUsers (Empty) returns (stream UserResponse); // 서버 스트리밍
}
message UserRequest {
int32 id = 1;
}
message UserResponse {
int32 id = 1;
string name = 2;
string email = 3;
}
통신 패턴:
| 패턴 | 설명 |
|---|---|
| Unary | 일반적인 요청-응답 (1:1) |
| Server Streaming | 하나의 요청에 서버가 스트림으로 응답 (1:N) |
| Client Streaming | 클라이언트가 스트림으로 요청, 서버가 하나로 응답 (N:1) |
| Bidirectional Streaming | 양방향 스트림 통신 (N:N) |
| 장점 | 단점 |
|---|---|
| 바이너리 직렬화로 높은 성능 (REST 대비 최대 10배) | 브라우저 직접 호출 어려움 (gRPC-Web 필요) |
| HTTP/2 멀티플렉싱으로 효율적 연결 관리 | Proto 파일 관리 및 배포 필요 |
| 양방향 스트리밍 지원 | 사람이 읽기 어려운 바이너리 포맷 |
| 강타입 계약 (Proto 파일) | 디버깅이 REST 대비 어려움 |
| 코드 자동 생성 (다국어 지원) | 학습 곡선 존재 |
적합한 경우: 서비스 내부 간 고성능 통신, 실시간 스트리밍, 다국어 서비스 환경
클라이언트가 필요한 데이터의 형태를 직접 정의하여 요청하는 쿼리 언어 기반 API이다. 주로 클라이언트 ↔ API Gateway 간 통신에 사용된다.
# 클라이언트가 필요한 필드만 요청
query {
user(id: 42) {
name
email
orders {
id
totalAmount
}
}
}
| 장점 | 단점 |
|---|---|
| Over-fetching / Under-fetching 해결 | 서버 구현 복잡도 증가 |
| 단일 엔드포인트로 다양한 요청 처리 | 캐싱이 REST 대비 어려움 |
| 클라이언트 중심 데이터 조회 | N+1 쿼리 문제 발생 가능 |
| 강타입 스키마 | 파일 업로드 등 비표준 작업 불편 |
적합한 경우: BFF(Backend for Frontend) 패턴, 다양한 클라이언트(웹/모바일)가 서로 다른 데이터를 요청하는 경우
| 항목 | REST | gRPC | GraphQL |
|---|---|---|---|
| 프로토콜 | HTTP/1.1 or HTTP/2 | HTTP/2 | HTTP |
| 데이터 포맷 | JSON (텍스트) | Protobuf (바이너리) | JSON (텍스트) |
| 성능 | 보통 | 높음 | 보통 |
| 스트리밍 | 제한적 | 양방향 지원 | Subscription (WebSocket) |
| 학습 비용 | 낮음 | 중간 | 중간 |
| 주요 사용처 | 외부 API, 범용 | 내부 서비스 간 | BFF, 클라이언트 API |
생산자(Producer)가 메시지를 큐에 넣으면, 소비자(Consumer) 하나가 해당 메시지를 가져가 처리하는 방식이다. 메시지는 한 번만 소비된다.
[주문 서비스] --주문 생성 메시지--> [ 큐 ] ---> [결제 서비스]
(Producer) (Queue) (Consumer)
대표 기술: RabbitMQ, Amazon SQS, ActiveMQ
| 장점 | 단점 |
|---|---|
| 서비스 간 시간적 결합 해소 | 메시지 브로커 인프라 필요 |
| 소비자 장애 시 메시지가 큐에 보관됨 | 메시지 순서 보장이 어려울 수 있음 |
| 부하 분산 (여러 소비자가 분담) | 메시지 유실 가능성 (설정에 따라) |
| 트래픽 버스트 흡수 | 디버깅/추적이 동기 대비 어려움 |
생산자가 이벤트를 토픽(Topic) 에 발행하면, 해당 토픽을 구독하는 모든 소비자가 이벤트를 수신하는 방식이다. 하나의 이벤트를 여러 서비스가 동시에 처리할 수 있다.
┌→ [결제 서비스] (구독자 1)
[주문 서비스] → [Topic] --┼→ [알림 서비스] (구독자 2)
└→ [재고 서비스] (구독자 3)
대표 기술: Apache Kafka, Amazon Kinesis, Apache Pulsar, NATS
| 장점 | 단점 |
|---|---|
| 매우 낮은 결합도 (발행자는 구독자를 모름) | 이벤트 스키마 관리 필요 |
| 하나의 이벤트를 여러 서비스가 독립 처리 | 이벤트 순서 보장 (파티션 단위) |
| 이벤트 재생(Replay) 가능 (Kafka) | 최종 일관성에 대한 설계 필요 |
| 이벤트 소싱 패턴과 자연스러운 결합 | 운영 복잡도 증가 |
| 항목 | 메시지 큐 (Point-to-Point) | 이벤트 스트리밍 (Pub/Sub) |
|---|---|---|
| 소비 모델 | 하나의 소비자가 메시지 처리 | 모든 구독자가 이벤트 수신 |
| 메시지 보존 | 소비 후 삭제 | 설정된 기간 동안 보존 |
| 재처리 | 불가 (이미 소비됨) | 가능 (오프셋 리셋) |
| 사용 목적 | 작업 분배, 비동기 명령 | 이벤트 전파, 데이터 동기화 |
| 대표 기술 | RabbitMQ, SQS | Kafka, Kinesis |
모든 외부 요청의 단일 진입점 역할을 한다. 라우팅, 인증/인가, 속도 제한, 로드 밸런싱, 요청/응답 변환 등을 처리한다.
[클라이언트] → [API Gateway] → [주문 서비스]
→ [사용자 서비스]
→ [상품 서비스]
대표 기술: Kong, AWS API Gateway, Spring Cloud Gateway, NGINX
동적으로 생성·삭제되는 서비스 인스턴스의 위치(IP, Port)를 자동으로 등록·검색하는 메커니즘이다.
| 방식 | 설명 |
|---|---|
| Client-Side Discovery | 클라이언트가 서비스 레지스트리에서 직접 인스턴스 목록을 조회 |
| Server-Side Discovery | 로드 밸런서가 서비스 레지스트리를 조회하여 라우팅 |
대표 기술: Eureka, Consul, Kubernetes Service, etcd
클라이언트 유형별(웹, 모바일, IoT)로 전용 API 계층을 두어, 각 클라이언트에 최적화된 응답을 제공하는 패턴이다.
[웹 브라우저] → [Web BFF] → [서비스 A, B, C]
[모바일 앱] → [Mobile BFF] → [서비스 A, B]
[IoT 디바이스] → [IoT BFF] → [서비스 C, D]
상태를 직접 저장하는 대신, 상태 변경을 일으킨 이벤트를 순서대로 저장하는 패턴이다. 현재 상태는 이벤트를 처음부터 재생(Replay)하여 도출한다.
// 기존 방식: 현재 상태만 저장
계좌 잔액: 150,000원
// 이벤트 소싱: 모든 변경 이벤트를 저장
1. AccountCreated { balance: 0 }
2. MoneyDeposited { amount: 200,000 }
3. MoneyWithdrawn { amount: 50,000 }
→ 현재 상태: 150,000원 (이벤트 재생으로 계산)
데이터의 쓰기(Command) 와 읽기(Query) 를 별도의 모델로 분리하는 패턴이다. 이벤트 소싱과 함께 사용되는 경우가 많다.
[쓰기 요청] → [Command Model] → [이벤트 저장소]
|
이벤트 발행
↓
[읽기 요청] → [Query Model] ← [읽기 전용 DB]
| 상황 | 권장 방식 | 이유 |
|---|---|---|
| 즉시 응답이 필요한 조회 | REST / gRPC (동기) | 사용자에게 바로 결과를 보여줘야 함 |
| 내부 서비스 간 고성능 호출 | gRPC | 바이너리 직렬화, 스트리밍 지원 |
| 외부 API 공개 | REST | 범용성, 호환성 |
| 주문 처리 등 비즈니스 워크플로우 | 이벤트 스트리밍 (Kafka) | 느슨한 결합, 이벤트 재생 가능 |
| 이메일/알림 발송 | 메시지 큐 (RabbitMQ/SQS) | 비동기 처리, 재시도 용이 |
| 다양한 클라이언트 대응 | GraphQL + BFF | 클라이언트별 최적화 |
| 실시간 데이터 동기화 | 이벤트 스트리밍 + CQRS | 읽기/쓰기 분리, 최종 일관성 |