모놀리식에서 마이크로서비스로의 진화
들어가며
'가장 좋은' 아키텍처는 없고, '현재 단계에 가장 적합한' 아키텍처만 있습니다. 모놀리식에서 마이크로서비스로의 전환은 한 번에 이루어지는 도약이 아니라, 비즈니스 규모와 팀 규모가 성장함에 따라 점진적으로 진화하는 과정입니다. 너무 이른 마이크로서비스 분할은 너무 늦은 분할만큼 위험합니다.
이 글에서 무엇을 배우게 되나요?
이 장을 마치면 다음을 얻게 됩니다:
- 진화 경로: 모놀리식에서 마이크로서비스로의 네 단계 이해
- 분할 시점: 언제 분할해야 하고, 언제 하지 말아야 하는지 파악
- 분할 전략: 비즈니스 도메인별 분할 방법론 숙지
- 통신 패턴: 서비스 간 동기 및 비동기 통신의 선택 이해
- 데이터 분할: 데이터베이스 분할의 도전과 해결方案 이해
| 장 | 내용 | 핵심 개념 |
|---|---|---|
| 1장 | 아키텍처 진화 경로 | 모놀리식 → 모듈형 → SOA → 마이크로서비스 |
| 2장 | 분할 시점과 원칙 | Conway의 법칙, 팀 자치 |
| 3장 | 분할 전략 | DDD 바운디드 컨텍스트, 스트랭글러 패턴 |
| 4장 | 서비스 통신 | REST, gRPC, 메시지 큐 |
| 5장 | 데이터 분할 | 데이터베이스 분할, 데이터 동기화 |
1. 아키텍처 진화 경로
아키텍처 진화는 기술이 아니라 조직 규모에 의해 주도됩니다. 팀이 5명에서 500명으로 성장하면, 모놀리식 아키텍처의 협업 효율이 급격히 떨어집니다.
| 단계 | 아키텍처 | 팀 규모 | 특징 |
|---|---|---|---|
| 시작기 | 모놀리식 애플리케이션 | 1~10명 | 모든 코드가 하나의 프로젝트에 있고, 배포가 단순함 |
| 성장기 | 모듈형 모놀리식 | 10~50명 | 코드는 모듈별로 나뉘지만, 여전히 함께 배포됨 |
| 확장기 | SOA(서비스 지향) | 50~200명 | 비즈니스 라인별로 조립 단위 서비스로 분할 |
| 규모기 | 마이크로서비스 | 200명 이상 | 세분화된 서비스, 각 팀이 독립적으로 개발 및 배포 |
Conway의 법칙
"시스템을 설계하는 조직은 그 조직의 소통 구조와 동일한 아키텍처를 만들어낸다." — Melvin Conway
간단히 말해: 3개의 팀이 하나의 시스템을 만들면 결국 3개의 서비스가 됩니다. 아키텍처 분할의 본질은 조직 분할입니다.
역 Conway의 법칙: 조직 구조가 시스템 아키텍처를 결정한다면, 원하는 아키텍처를 얻으려면 먼저 그에 맞는 조직 구조로 조정하면 됩니다. 예를 들어 독립적인 결제 서비스를 분리하고 싶다면, 먼저 독립적인 결제 팀을 구성하세요. 많은 기업에서 마이크로서비스 분할이 실패하는 이유는 기술 문제가 아니라 조직이 함께 변하지 않았기 때문입니다.
2. 언제 마이크로서비스로 분할해야 하나요?
모든 시스템이 마이크로서비스를 필요로 하는 것은 아닙니다. 너무 이른 분할은 불필요한 복잡성을 가져옵니다.
| 신호 | 설명 | 제안 |
|---|---|---|
| 배포 충돌 빈번 | 여러 팀이 같은 코드 베이스를 수정하여 자주 충돌 | 분할 고려 |
| 특정 모듈만 독립적 확장 필요 | 검색 모듈이 다른 모듈의 10배 자원이 필요 | 분할 고려 |
| 기술 스택 차별화 필요 | AI 모듈은 Python, 메인 사이트는 Java | 분할 고려 |
| 팀 규모 < 10명 | 소통 비용이 낮고, 모놀리식으로 충분 | 분할하지 마세요 |
| 비즈니스가 여전히 탐색 단계 | 요구사항 변화가 빠르고, 경계가 불명확 | 분할하지 마세요 |
| DevOps 역량이 없음 | CI/CD, 컨테이너화, 모니터링 체계가 없음 | 분할하지 마세요 |
3. 분할 전략
3.1 비즈니스 도메인별 분할(DDD 바운디드 컨텍스트)
DDD(도메인 주도 설계)의 바운디드 컨텍스트(Bounded Context)는 마이크로서비스 분할의 최적의 지침 원칙입니다. 각 바운디드 컨텍스트는 하나의 독립적인 비즈니스 도메인에 해당하며, 자체 데이터 모델과 비즈니스 규칙을 가집니다.
바운디드 컨텍스트란? 같은 단어라도 비즈니스 도메인에 따라 의미가 다릅니다. 예를 들어 '사용자'는 사용자 도메인에서는 가입 정보(이름, 이메일)를, 주문 도메인에서는 주문자(배송지, 결제 수단)를, 추천 도메인에서는 행동 프로필(검색 기록, 선호 태그)을 의미합니다. 바운디드 컨텍스트는 경계를划定하여, 그 경계 내에서 용어와 모델이 명확하고 통일된 의미를 갖도록 합니다.
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 사용자 도메인 │ │ 주문 도메인 │ │ 결제 도메인 │
│ │ │ │ │ │
│ User │ │ Order │ │ Payment │
│ Profile │ │ OrderItem │ │ Refund │
│ Address │ │ Cart │ │ Transaction │
│ │ │ │ │ │
│ 사용자 서비스 │ │ 주문 서비스 │ │ 결제 서비스 │
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ │ │
└────── API 호출 / 이벤트 통신 ───────┘| 바운디드 컨텍스트 | 핵심 엔티티 | 해당 서비스 |
|---|---|---|
| 사용자 도메인 | User, Profile, Address | 사용자 서비스 |
| 상품 도메인 | Product, Category, SKU | 상품 서비스 |
| 주문 도메인 | Order, OrderItem | 주문 서비스 |
| 결제 도메인 | Payment, Refund | 결제 서비스 |
| 물류 도메인 | Shipment, Tracking | 물류 서비스 |
3.2 스트랭글러 무화과 패턴(Strangler Fig Pattern)
전체 모놀리식을 한 번에 재작성하지 말고, 스트랭글러 무화과나무처럼 점진적으로 새 서비스로 이전 모듈을 교체하세요:
- 모놀리식 외부에 새 서비스 생성
- 프록시 계층을 통해 일부 트래픽을 새 서비스로 라우팅
- 새 서비스가 안정적인지 검증 후, 점진적으로 더 많은 트래픽을 이관
- 최종적으로 이전 모듈을 완전히 교체
4. 서비스 통신 패턴
| 방식 | 프로토콜 | 특징 | 적용 시나리오 |
|---|---|---|---|
| REST | HTTP/JSON | 단순하고 범용적, 생태계가 풍부함 | 외부 API, CRUD 연산 |
| gRPC | HTTP/2 + Protobuf | 고성능, 강 타입 | 내부 서비스 간 고빈도 호출 |
| 메시지 큐 | AMQP/Kafka | 비동기 결합 해제, 피크 평탄화 | 이벤트 알림, 비동기 작업 |
| GraphQL | HTTP/JSON | 클라이언트가 필요한 데이터만 쿼리 | BFF 계층, 모바일 |
동기 vs 비동기 선택
- 즉시 결과를 반환해야 함 → 동기(REST/gRPC)
- 즉시 반환할 필요가 없음 → 비동기(메시지 큐)
- 하나의 이벤트가 여러 동작을 트리거 → 비동기(발행-구독)
경험 법칙: 비동기로 할 수 있으면 비동기로 하세요. 동기 호출 체인이 길어질수록 시스템은 취약해집니다.
5. 데이터 분할: 가장 어려운 부분
마이크로서비스 분할에서 가장 고통스러운 것은 코드 분할이 아니라 데이터베이스 분할입니다. 각 서비스는 자체 데이터베이스를 가져야 하지만, 이는 크로스 서비스 쿼리를 어렵게 만듭니다.
| 도전 과제 | 설명 | 해결책 |
|---|---|---|
| 크로스 서비스 JOIN | 두 서비스의 테이블을 직접 JOIN할 수 없음 | API 조합 쿼리, 데이터 중복 |
| 분산 트랜잭션 | 크로스 DB 트랜잭션에 로컬 트랜잭션 사용 불가 | Saga, 로컬 메시지 테이블 |
| 데이터 일관성 | 여러 서비스의 데이터가 일시적으로 불일치할 수 있음 | 최종 일관성, 이벤트 주도 |
| 데이터 마이그레이션 | 공유 DB에서 독립 DB로 이관 | 이중 쓰기 전환, 데이터 동기화 도구 |
요약
모놀리식에서 마이크로서비스로의 전환은 점진적인 과정이며, 한 번에 이루어지는 혁명이 아닙니다.
이 장의 핵심 요점을 되돌아보겠습니다:
- 진화 경로: 모놀리식 → 모듈형 모놀리식 → SOA → 마이크로서비스, 각 단계마다 명확한 구동력이 있습니다
- 분할 시점: 팀 규모, 배포 충돌, 확장 요구는 분할의 신호입니다
- 분할 전략: DDD 바운디드 컨텍스트로 분할을 안내하고, 스트랭글러 패턴으로 점진적 이관하세요
- 통신 선택: 비동기로 할 수 있으면 비동기로, 동기 호출 체인은 짧을수록 좋습니다
- 데이터 분할: 가장 어렵지만 가장 중요하며, 최종 일관성을 받아들이는 것이 핵심적인 마인드셋 전환입니다
더 읽어보기
- Building Microservices - Sam Newman의 마이크로서비스 고전
- Monolith to Microservices - 점진적 마이그레이션 가이드
- Domain-Driven Design - Eric Evans의 DDD 고전
- The Strangler Fig Pattern - Martin Fowler의 스트랭글러 패턴