🌏 인트로
MSA는 Microservice Architecture(마이크로서비스 아키텍처)의 약자로, 하나의 큰 애플리케이션을 여러 개의 독립적인 작은 서비스로 나누어 개발·배포·운영하는 아키텍처입니다.
각 서비스는 자체 DB와 로직을 가지며 서로 API나 이벤트로 통신합니다. 이렇게 분리하면 기능별 확장과 독립 배포가 쉬워지지만, 서비스 간 협력이 복잡해지고 일관성·장애 관리 문제가 함께 따라옵니다.
MSA에 대해서는 크게 장점으로는 나누어져 있으니 일부만 scale out 할 수 있어 비용이 절감된다, 그리고 단점으로는 운영이 복잡하다를 들곤 합니다. 근데 실제로 어떤 부분에 있어서 운영이 얼마나 복잡해질까요?
🌏 MSA 운영에서 발생하는 이슈들
MSA 구조를 실제로 운영해보면, 처음에 기대했던 서비스를 나눠서 확장 쉽게 한다는 포부와는 다르게 복잡한 문제가 곳곳에서 터지게 됩니다. 근본적인 이유는 하나의 비즈니스를 여러 서비스가 나눠 들고 있기 때문입니다.
예를 들어 커머스에서 흔히 볼 수 있는 구조를 생각해봅시당

- 주문 서비스
- 재고 서비스
- 매장 서비스 (오프라인 매장 재고 포함)
- 결제 서비스
- 배송 서비스
보기에는 역할이 명확하게 분리된 것 같지만, 실제 요청 흐름에서는 이 서비스들이 서로 강하게 연결되어 있습니다.
✅ 데이터 일관성 문제
예를 들어 고객이 상품을 주문할 때는 반드시 재고가 있어야 합니다.
하지만 MSA에서는 재고 정보가 재고 서비스의 DB에 존재하고, 주문 정보는 주문 서비스의 DB에 존재합니다. 두가지 DB는 다른 서버에 있습니다.
만약 단일 DB였다면 재고 조회, 주문 생성, 재고 감소를 모두 하나의 트랜잭션 안에서 처리해 어느 한 단계에서 오류가 나면 전체가 깔끔하게 롤백됩니다.
그러나 MSA에서는 각 서비스가 서로 다른 DB를 사용하기 때문에, 더 이상 조인을 수행할 수 없습니다. 다음과 같은 쿼리는 물리적으로 불가능합니다. (외부 DB 간 조인은 지원되지 않습니당)
따라서 실시간 JOIN은 불가능하며, 애플리케이션 레벨 조합(A 서비스+ B 서비스 조회 후 백엔드에서 결합)하거나 조회 전용 모델을 별도로 구성해야 합니다.
SELECT * FROM order_db.orders o
JOIN stock_db.stocks s ON ...
결과적으로 주문을 생성하는 시점에 재고 서비스에서 재고를 조회해보고 믿는 방식으로만 처리할 수밖에 없습니다. 이 과정에서 데이터 일관성 문제가 발생합니다.
주문은 성공했지만 재고 차감은 실패 → 판매량은 증가했지만 재고는 그대로
재고 차감은 성공했지만 주문은 실패 → 재고만 빠진 상태가 됨
✅ 트랜잭션 적용 문제
트랜잭션의 경계가 서비스 간에 넘어가지 않습니다.
트랜잭션은 하나의 DB 커넥션 안에서만 동작합니다. 따라서 주문 DB와 재고 DB를 한 번에 묶어서 commit 또는 rollback하는 것은 원칙적으로 불가능합니다.
- 서비스 A는 성공
- 서비스 B는 실패
- 두 서비스의 상태가 어긋남
이 문제는 주문/결제/재고처럼 서로 강하게 연관된 작업에서 특히 심각하게 드러납니다. (그래서 뒤에 등장할 SAGA 패턴이 등장했습니다.)
✅ 장애가 전체로 전파되는 문제
MSA의 가장 치명적인 문제는 한 서비스의 느림이나 장애가 전체로 퍼진다는 점입니다. 예시 상황을 들어보겟습니당.
- 세일 시간에 재고 확인 요청이 폭주합니다.
- 재고 서비스가 느려지거나 타임아웃이 발생합니다.
- 주문 서비스는 재고 서비스를 계속 호출합니다.
- 호출이 모두 타임아웃되면서 주문 서비스의 스레드 풀을 소모합니다.
- 결국 주문 서비스 전체가 멈추고, 결제 서비스까지 장애가 확산됩니다.
하나의 서비스가 느려지게 되면 타임아웃 동안 해당 서비스를 호출한 서비스의 스레드가 대기하기 때문에, 호출한 쪽의 스레드 풀이 소진되고, 이건 결국 전체 요청 실패로 이어지는 장애 전파의 대표적인 형태입니다.
단일 모놀리식 구조에서는 같은 프로세스 내부에서 최적화할 수 있지만, MSA는 네트워크 호출에 의존하기 때문에 작은 장애도 전체 장애로 이어질 가능성이 높습니다.
(이 문제를 해결하기 위해 Bulkhead, Circuit Breaker 같은 패턴이 사용됩니다.)
✅ 이벤트 전달과 동기화 문제
서비스 간 DB를 공유할 수 없기 때문에 대부분 이벤트 기반(Kafka 등)으로 상태를 동기화합니다.
그런데 이벤트 전달 과정에서 이런 문제들이 발생할 수 있습니다.
- 재고 감소 이벤트가 Kafka 장애로 누락됨 (흔치 않긴 하지만)
- 이벤트 전송은 성공했지만 DB 커밋이 실패함
- 이벤트 순서가 뒤바뀜
- 중복 이벤트 발생
이런 문제가 발생하면 서비스 간 상태가 쉽게 불일치하게 됩니다.
- 실제 재고는 0인데, 주문 서비스 화면에는 재고가 있는 것처럼 보임
- 주문 서비스는 “결제 완료” 상태인데 결제 서비스는 아직 처리 안 됨
- 두 명이 동시에 같은 상품을 주문해버림
(이런 문제를 해결하기 위해 Outbox 패턴, Event Sourcing 패턴 등이 등장합니다.)
✅ 조회 성능 문제
한 화면을 그리기 위해 여러 서비스를 호출해야 하는 상황이 생깁니다. 예를 들어 주문 상세 페이지를 띄우려면 다음 정보가 필요합니다.
- 주문 정보 (주문 서비스)
- 상품 정보 (상품 서비스)
- 재고 정보 (재고 서비스)
- 매장 재고 정보 (매장 서비스)
- 배송 옵션 (배송 서비스)
- 쿠폰 및 할인 정책 (쿠폰 서비스)
단일 DB였다면 JOIN 또는 여러 서브쿼리로 한 번에 가져올 수 있지만, MSA에서는 네트워크를 통한 서비스 호출이 필요합니다.
각 서비스를 별도로 호출해야 하므로 화면 하나 로딩에 6~8개의 API가 호출되는 ‘서비스 버전의 N+1 문제’가 발생합니다.
일일이 다른 API로 해당 정보들을 모아오려면 서비스 호출이 많아 응답이 느려지고, 이에 따라 병목 구간이 생기게 됩니다.
또 트래픽이 많은 상황이라면 특정 서비스 장애가 전체 요청을 실패시킬 수도 있고, 화면 렌더링이 불안정해집니다.
(그래서 조회 전용 모델을 따로 두는 CQRS 패턴이나, 비정규화된 검색 엔진(Elasticsearch·Redis)을 사용하는 방식이 등장합니다.)
🌏 결론
MSA 구조를 운영할 때 발생하는 문제들은 대부분 다음 다섯 가지가 발생합니다.
- 단일 DB가 아니기 때문에 조인이 불가능합니다.
- 트랜잭션이 서비스 간에 확장되지 않습니다.
- 장애가 네트워크 타고 전체로 번집니다.
- 이벤트 전달이 실패하거나 순서가 바뀔 수 있습니다.
- 조회가 여러 서비스 호출로 변하면서 성능 문제가 생깁니다.
이 문제들을 해결하기 위해 SAGA, CQRS, Outbox, Bulkhead, Circuit Breaker, Event Sourcing 같은 패턴이 등장했습니다.
다음 포스팅에서는 MSA에서 발생하는 문제들을 해결하기 위한 패턴들에 대해서 알아보겠습니다.


도움이 되었다면, 공감/댓글을 달아주면 깃짱에게 큰 힘이 됩니다!🌟
비밀댓글과 메일을 통해 오는 개인적인 질문은 받지 않고 있습니다. 꼭 공개댓글로 남겨주세요!
'아키텍처+MSA' 카테고리의 다른 글
| [MSA] MSA 운영 이슈를 해결하는 패턴: SAGA/CQRS/Outbox/Bulkhead/Circuit Breaker/Event Sourcing (0) | 2025.11.27 |
|---|---|
| [아키텍처/캐시] CDN 완전 정복: 개념·헤더·보안, CloudFront vs Cloudflare 비교까지 (0) | 2025.11.21 |
| [아키텍처/캐시] Redis 캐시 완전 정복: 개념·전략·TTL·일관성·모니터링 총정리 (0) | 2025.11.20 |
| [아키텍처] 로드밸런싱 완전 정복: L4/L7 구조부터 SPOF 해결까지 (0) | 2025.11.20 |
| [아키텍처/Kafka] Kafka로 강한 결합 탈출하기: 회원가입 비동기 처리 미니 프로젝트 (0) | 2025.11.05 |