모놀리식 아키텍처는 하나의 DB에 여러 데이터를 저장하므로
단일 DB에서 SQL 조인으로 데이터를 쉽게 조회가 가능했다
반면 마이크로서비스 아키텍처에서는 여러 서비스, 여러 DB에 분산된 데이터를 조회해야하는데
API 조합 패턴과 CQRS 패턴으로 쿼리를 구현한다.
서비스 클라이언트가 데이터를 가진 여러 서비스를 직접 호출하여 그 결과를 조합하는 패턴이다.
API 조합기에서 데이터 요청 수신
API 조합기에서 필요한 데이터를 제공하는 프로바이더 서비스 호출

API 조합 패턴에는 두 가지 설계 이슈가 존재한다.
- 어느 컴포넌트를 쿼리 작업의 API 조합기로 선정할 것인가?
- 어떻게 해야 효율적으로 취합 로직을 작성할 것인가?
1-1 서비스 클라이언트를 API 조합기로 임명
클라이언트가 직접 여러 서비스를 호출하고 데이터를 병합하는 방식

| 항목 | 장점 | 단점 |
|---|---|---|
| 네트워크 성능 | 낮은 레이턴시와 높은 대역폭으로 효율적인 데이터 조회 가능 | - |
| 아키텍처 단순성 | 별도의 중간 계층(API Gateway, Aggregator 서비스) 없이 간단히 구현 가능 | - |
| 개발 및 유지보수 | 중간 계층이 없으므로 추가적인 인프라 관리가 필요하지 않음 | 클라이언트 코드가 복잡해지고, 병합 로직이 클라이언트에 포함됨 |
| 서비스 의존성 | 서비스 간 통신이 빠르고 안정적이며, 방화벽/NAT 변환 없이 직접 통신 가능 | 서비스 API 변경 시 모든 클라이언트를 수정해야 하므로 유지보수 부담 증가 |
| 항목 | 장점 | 단점 |
|---|---|---|
| 네트워크 성능 | - | WAN 환경에서 네트워크 병목 현상 발생: 레이턴시 증가 및 패킷 손실 가능성 |
| 아키텍처 단순성 | - | 여러 서비스 호출로 인해 네트워크 왕복 시간(RTT)이 누적되어 성능 저하 |
| 보안성 | - | 모든 서비스가 외부에 노출되므로 보안 취약성이 증가하며, 공격 표면이 넓어짐 |
| 개발 및 유지보수 | - | 클라이언트 코드 복잡성 증가: 병합 로직과 다중 호출 로직을 모두 포함해야 함 |
1-2 애플리케이션 외부 API가 구현된 API 게이트웨이를 API 조합기로 만들기

클라이언트 요청 - 단일 API 엔드포인트를 호출
ex) GET /order-details/1234
API 게이트웨이 - 요청을 분석하고 필요한 데이터를 제공하는 여러 백엔드 서비스를 호출
데이터 병합 및 응답 생성 - 게이트웨이가 각 서비스에서 받은 데이터를 병합하여 단일 응답 생성
최종 응답 반환 - 병합된 데이터를 클라이언트에 반환
| 장점 | 단점 |
|---|---|
| 클라이언트가 단일 API만 호출하면 되므로 복잡성이 감소 | 복잡한 비즈니스 로직에는 적합하지 않음 (게이트웨이에서 처리하기 어렵기 때문) |
| 네트워크 왕복 횟수를 줄여 성능 최적화 가능 | 게이트웨이에 과도한 로직 추가 시 성능 저하 가능 |
| 기존 게이트웨이 기능(라우팅, 인증 등)과 통합 가능 | 단일 장애점(Single Point of Failure) 발생 가능 |
| 간단한 데이터 조회 및 병합 작업에 적합 | 대규모 데이터 변환이나 계산에는 별도의 Aggregator 서비스 필요 |
1-3 API 조합기를 스탠드얼론 서비스로 구현하기

프로세스 흐름은 API 게이트웨이를 API 조합기로 사용하는 방식과 유사하지만
아래와 같은 차이점이 존재한다
| 특징 | API 게이트웨이를 API 조합기로 사용하는 경우 | 스탠드얼론 API 조합기를 구현하는 경우 |
|---|---|---|
| 역할 | 게이트웨이가 데이터 병합과 라우팅을 동시에 처리. | 데이터 병합과 로직 처리를 전담하는 독립적인 마이크로서비스. |
| 책임 분리 | 게이트웨이가 데이터 병합 외에도 인증/인가, 로깅, 라우팅 등 다양한 역할 수행. | API 조합기가 오직 데이터 병합 및 복잡한 비즈니스 로직 처리를 담당. |
| 복잡한 로직 처리 가능 여부 | 제한적: 간단한 데이터 병합 작업에 적합. | 가능: 고도화된 데이터 변환 및 계산 작업도 처리 가능. |
| 확장성 | 제한적: 게이트웨이는 라우팅과 병합 작업을 동시에 처리하므로 확장성에 한계가 있음. | 높음: 스탠드얼론 서비스로 독립적으로 확장 가능. |
| 적용 사례 | 단순한 데이터 조회 및 병합 작업(예: 쿼리 작업). | 복잡한 데이터 변환, 대규모 트래픽 처리, 캐싱 및 성능 최적화가 필요한 경우. |
즉 내부적으로 여러 서비스가 사용하는 쿼리 작업이나 취합 로직이 너무 복잡해서 API 게이트웨이 일부로 만들기는 곤란하고 외부에서 접근 가능한 쿼리 작업을 구현할 경우 좋은 방법이다.
분산 시스템 개발 시 지연 시간을 최소화하는 문제가 존재한다.
쿼리 작업의 반응 시간을 최대한 줄이려면 API 조합기가 프로바이더 서비스를 병렬 호출해야 한다.
| 특징 | 설명 |
|---|---|
| 병렬 처리 | 여러 서비스를 동시에 호출하여 응답 시간을 단축 가능. |
| 순차 처리 | 의존성이 있는 작업을 순서대로 실행 가능(예: 주문 상태 확인 후 결제 정보 가져오기). |
| 비동기 및 논블로킹 | 쓰레드 블로킹 없이 동시에 여러 요청 처리 가능. |
| 복잡한 데이터 병합 지원 | 데이터 스트림과 연산자를 활용해 간결하고 효율적인 병합 로직 구현 가능. |
| 백프레셔 지원 | 대량의 데이터 흐름을 제어하여 안정성을 유지. |
| 효율적인 에러 처리 | 스트림 내에서 에러를 우아하게 처리하여 시스템 안정성 향상. |
주의점
순차/병렬 서비스 호출이 뒤섞인 실행 로직은 복잡해질 수 있어
관리성이 용이하고 성능/확정성이 우수한 API 조합기를 작성하려면
CompletableFuture, RxJava의 옵저버블 등 추상체에 기반한 리엑티브 설계 기법을 동원해야함
MSA에서 쉽고 단순하게 쿼리 작업을 구현할 수 있지만
아래와 같은 단점들도 존재한다.
여러 서비스를 호출하고 여러 DB를 쿼리하는 오버헤드는 불가피하다
그만큼 컴퓨팅/네트워크 리소스가 더 많이 소모되고 애플리케이션 운영 비용도 증가한다.
이러한 가용성 저하에 대한 가용성을 높이는 전략이 2가지 존재한다.
프로바이더 서비스가 불능일 경우 API 조합기가 이전에 캐시한 데이터 반환
프로바이더가 내려가더라도 API 조합기는 (시간 경과에 따라 안 맞는 데이터 존재)
캐시 데이터를 반환할 수 있다.
API 조합기가 미완성된 데이터를 반환
하나의 서비스가 중단되어도 해당 서비스의 데이터만 제외한 나머지 데이터를 반환
UI에서 유용한 정보를 표시하는 데 별 지장이 없을 수 있음
| 해결 방법 | 설명 | 장점 | 단점 |
|---|---|---|---|
| Eventual Consistency | 시간이 지나면 모든 서비스가 동일한 상태로 동기화됨. | 높은 가용성과 확장성 제공 | 임시적인 불일치 상태 허용 필요 |
| Saga Pattern | 분산 트랜잭션을 순차적으로 처리하고 실패 시 롤백 수행. | 분산 환경에서도 데이터 일관성 유지 가능 | 구현 복잡성 증가 |
| Event-Driven Architecture | 이벤트 브로커를 통해 비동기로 데이터 변경 사항 전달. | 높은 성능과 확장성 제공 | 즉각적인 동기화 어려움 |
| Data Partitioning & Contextual Boundaries | 각 서비스가 자신의 데이터만 관리하도록 경계 설정. | 마이크로서비스 간 의존성 감소 | 교차 참조 시 추가적인 API 호출 필요 |
| Caching & Read Models | 캐싱 또는 읽기 전용 모델을 활용해 최신 데이터를 빠르게 제공. | 빠른 응답 시간 제공 | 캐시 갱신 주기에 따라 최신성과 불일치 가능성 존재 |
APi 조합 패턴은 꽤 많은 쿼리 기능을 쉽게 구현할 수 있는 수단이다
하지만 효율적으로 구현하기 어려운 쿼리 작업은 CQRS 패턴으로 구현하는 것이 좋다.