마이크로서비스 아키텍처(MSA)는 하나의 애플리케이션을 작고 느슨하게 결합된(loosely coupled) 독립 서비스들의 집합으로 구성하는 아키텍처 스타일이다.
여기서 "느슨하게 결합"이라는 표현이 핵심이다. 서비스들이 서로를 알긴 하지만, 내부 구현이나 데이터 구조를 직접 공유하지는 않는다. 마치 레고 블록처럼, 각 블록은 독립적으로 존재하면서도 정해진 연결 규격(API/이벤트)을 통해 하나의 완성품을 만든다.
MSA는 컨테이너, Kubernetes, CI/CD, DevOps와 결합되면서 클라우드 네이티브 아키텍처의 핵심 스타일로 자리잡았다. 이 조합이 강력한 이유는, MSA가 제시하는 "서비스를 분리하라"는 설계 원칙이 컨테이너의 "가볍게 패키징하라", Kubernetes의 "자동으로 관리하라", CI/CD의 "빠르게 배포하라"와 자연스럽게 맞닿아 있기 때문이다.
MSA를 이해하는 가장 좋은 방법은 모놀리식과 비교해 보는 것이다.
| 구분 | 모놀리식(Monolith) | 마이크로서비스(MSA) |
|---|---|---|
| 코드 구조 | 하나의 큰 코드베이스 + 하나의 배포 단위 | 기능 단위로 분리된 여러 서비스, 각각 독립적으로 개발, 배포, 스케일링 |
| 초기 장점 | 작은 팀/초기 제품에 단순하고 빠름 | 복잡한 대규모 시스템에 유리 |
| 성장 후 문제 | 규모가 커질수록 변경, 배포, 스케일링이 어려워짐 | 운영, 네트워크, 데이터 복잡도 증가 |
| 복잡도 이동 | 코드 복잡도 낮음 | 운영 복잡도 높음 |
모놀리식의 문제는 처음에는 잘 드러나지 않는다. 프로젝트 초반에는 모든 것이 한 곳에 있어서 빠르고 단순하다. 그런데 코드가 수십만 줄이 되고, 팀이 10개 이상으로 늘어나고, 하루에 수백만 명이 접속하게 되면 이야기가 달라진다. 한 팀의 배포가 다른 팀의 서비스에 영향을 주고, 트래픽이 폭발하면 전체를 통째로 스케일 아웃해야 하며, 장애 하나가 전체 시스템을 다운시킨다. MSA는 이 문제에 대한 구조적 해답이다.
하나의 서비스는 하나의 비즈니스 기능, 즉 하나의 Bounded Context를 담당한다. 주문 서비스는 주문만, 결제 서비스는 결제만 담당하는 식이다. 서비스 크기의 기준은 코드 줄 수가 아니라 "이 서비스가 어떤 비즈니스 문제를 해결하는가" 이다.
다른 서비스와는 API 또는 이벤트를 통해서만 통신하고, 내부 구현은 완전히 숨긴다. 주문 서비스가 결제 서비스의 내부 DB 구조를 알 필요가 없다. 결제 서비스가 내부적으로 어떤 DB를 쓰든, 어떤 언어로 작성됐든 주문 서비스와는 무관하다. 이 덕분에 각 서비스는 서로 영향 없이 독립적으로 변경될 수 있다.
서비스별로 버전, 배포 주기, 스케일링 전략이 다를 수 있다. 결제 서비스에 버그가 생겼을 때 결제 서비스만 롤백하면 된다. 모놀리식이라면 전체 시스템을 롤백해야 한다. 독립 배포는 단순히 편의의 문제가 아니라 장애 영향 범위를 제한하는 안전망이다.
서비스별로 다른 언어, 프레임워크, DB를 선택할 수 있다. 실시간 스트리밍 처리가 필요한 서비스는 Go로, ML 추론이 필요한 서비스는 Python으로 작성할 수 있다. 물론 이는 각 팀이 그 기술에 대한 책임을 온전히 지겠다는 전제 하에서다.
실제 MSA 시스템은 다음 컴포넌트들이 계층적으로 결합되어 동작한다.

MSA를 처음 도입할 때 가장 많이 하는 실수는 "어떻게 쪼갤지"에 집중하는 것이다. 올바른 질문은 "어디서 쪼갤지"다.
DDD(Domain-Driven Design) 는 소프트웨어의 복잡성을 도메인(비즈니스 영역)에 집중하여 해소하는 설계 방법론이다. 핵심 개념인 Bounded Context는 특정 도메인 모델이 일관적으로 적용되는 경계를 의미한다.
예를 들어 "사용자(User)"라는 개념은 도메인마다 다르게 정의된다.
같은 "사용자"이지만 각 도메인에서 관심 있는 속성과 행동이 전혀 다르다. 이처럼 개념이 의미 있게 유지되는 경계를 기준으로 서비스를 분리하는 것이 Bounded Context 기반 설계다.
기술 레이어로 분리하면 OrderController가 하나의 서비스, OrderService가 또 다른 서비스가 된다. 이렇게 하면 결국 API 호출로만 연결된 모놀리식이 된다. 서비스가 많아졌지만 복잡도는 오히려 더 늘어난다.
💡 Callout: DDD(도메인 주도 설계) 더 알아보기 (Aggregate, Domain Event, Context Map 항목은 강의자료 외 내용)
DDD는 소프트웨어의 복잡성을 도메인(비즈니스 영역)에 집중하여 해소하는 설계 방법론이다. MSA 설계에서 특히 중요한 개념들은 다음과 같다:
- Bounded Context: 특정 도메인 모델이 유효한 경계. MSA 서비스 경계와 일치시키는 것이 이상적이다.
- Aggregate: 일관성을 유지해야 하는 객체들의 묶음. 하나의 트랜잭션 단위가 된다.
- Domain Event: 도메인에서 일어난 중요한 사건. 서비스 간 비동기 통신의 기반이 된다.
- Context Map: 여러 Bounded Context 간의 관계를 그린 지도. 어떤 서비스가 어떻게 협력하는지를 시각화한다.
서비스들이 서로 통신하는 방식은 크게 두 가지다. 어느 방식을 선택하느냐에 따라 시스템의 특성이 크게 달라진다.
REST, gRPC 등 요청-응답 방식. 요청을 보내고 응답이 올 때까지 기다린다.
간단하고 직관적이지만, 서비스 체인이 길어질수록 문제가 생긴다. A → B → C → D 순서로 동기 호출이 이어진다면, D가 느리면 C가 느려지고, C가 느리면 B가 느려지고, B가 느리면 A까지 느려진다. 마치 고속도로에서 앞차가 막히면 뒤의 모든 차가 막히는 것과 같다.
메시지 브로커(Kafka, RabbitMQ) 기반 이벤트 전파. 요청을 보내고 응답을 기다리지 않는다.
A는 이벤트를 브로커에 발행하고 바로 자신의 일을 계속한다. B, C, D는 각자의 속도에 맞춰 이벤트를 처리한다. 한 서비스가 느리거나 장애가 나도 전체 흐름이 멈추지 않는다. 느슨한 결합과 장애 격리라는 MSA의 이점을 극대화하는 방식이다.
현실에서는 두 방식을 목적에 따라 혼합하는 것이 일반적이다.
서비스를 분리했지만 DB가 하나로 공유되어 있다면, 그것은 가짜 MSA다. 진짜 MSA는 각 서비스가 자신의 DB를 소유한다.
핵심 원칙: 다른 서비스의 데이터가 필요하다면, 그 서비스의 DB에 직접 접근하는 것이 아니라 해당 서비스의 API를 통해 요청해야 한다.
장점
단점과 해결
MSA에서 가장 까다로운 문제 중 하나는 트랜잭션이다. 주문, 결제, 재고 차감이 각각 다른 서비스에 있을 때, 하나가 실패하면 어떻게 모두를 롤백할 것인가?
기존의 2PC(Two-Phase Commit) 방식은 분산 환경에서 성능 문제와 교착 상태 위험이 있어 클라우드 네이티브에 적합하지 않다. 대신 Saga 패턴을 사용한다.
Saga 패턴: 각 서비스가 로컬 트랜잭션을 처리하고, 실패 시 이미 완료된 단계들을 되돌리는 보상(Compensation) 트랜잭션을 실행하는 방식이다. 강한 일관성(Strong Consistency) 대신 최종 일관성(Eventual Consistency) 을 추구한다.
예를 들어 주문 프로세스가 실패한 경우:
1. 주문 생성 ✅ → 결제 처리 ✅ → 재고 차감 ❌ (실패)
2. 재고 차감 실패를 감지
3. 결제 취소 보상 트랜잭션 실행 ↩️
4. 주문 취소 보상 트랜잭션 실행 ↩️
💡 Callout: Saga의 두 가지 구현 방식
Choreography (코레오그래피)
이벤트 기반. 각 서비스가 이전 서비스의 이벤트를 받아 자율적으로 다음 단계를 처리한다. 중앙 조율자가 없어서 서비스 간 결합도가 낮다. 하지만 전체 흐름이 코드 여러 곳에 분산되어 있어 추적이 어렵고, 새로운 서비스를 추가할 때 기존 서비스들이 어떤 이벤트를 발행하는지 알아야 한다.Orchestration (오케스트레이션)
Saga Orchestrator라는 중앙 조율자가 각 서비스를 순서대로 호출하고, 실패 시 보상 트랜잭션을 명시적으로 호출한다. 전체 흐름이 한 곳에 정의되어 있어 이해하기 쉽다. 단, Orchestrator가 단일 장애점(SPOF)이 될 수 있고 Orchestrator 자체가 복잡해진다.도메인 복잡도가 낮고 서비스 수가 적으면 Choreography, 흐름이 복잡하고 명확한 제어가 필요하면 Orchestration이 유리하다.
MSA의 각 서비스를 수동으로 배포하고 관리하는 것은 불가능하다. 컨테이너와 Kubernetes가 이 문제를 해결한다.
수십 개의 서비스가 동시에 동작할 때 장애가 발생하면, 어느 서비스에서 문제가 시작됐는지 파악하는 것이 우선이다. 이를 위해 세 가지 관측 수단이 필요하다.
MSA의 성공 여부는 기술보다 조직 구조에 달려 있다. 소프트웨어의 구조는 그것을 만든 조직의 커뮤니케이션 구조를 반영한다. MSA가 성공하려면 팀 구조도 MSA처럼 바뀌어야 한다.
MSA는 강력한 아키텍처지만, 모든 상황에 맞는 은탄환은 아니다.
장점
단점
적합한 상황
신중해야 할 상황
현실적인 접근법
처음부터 MSA로 시작하는 것은 오히려 독이 될 수 있다. 초기에는 내부적으로 모듈화가 잘 된 모듈형 모놀리식(Modular Monolith) 으로 시작하는 것이 현명하다. 도메인 경계가 명확해지고, 팀이 커지고, 트래픽이 실제로 문제가 될 때 점진적으로 분리하면 된다.
요약: MSA는 "코드를 쪼개는 기술"이 아니다. 도메인, 데이터, 팀, 플랫폼을 모두 아우르는 종합적인 아키텍처 스타일이다. 기술보다 설계 철학을 먼저 이해해야 한다.