MSA(마이크로서비스 아키텍처) 기본 개념 정리.

|

🧩 MSA 아키텍처 시리즈 (전체 3편)

  1. MSA 기본 개념 (현재 글)
  2. MSA 회복탄력성(Resilience) 패턴
  3. MSA 분산 트랜잭션과 Saga 패턴

2018년에 마소콘(마이크로소프트웨어 컨퍼런스) 후기를 몇 편 썼었는데, 그때 다뤘던 “사일로 해체”, “빠르고 자주 배포” 같은 문화적인 이야기(더 웨더 컴퍼니의 데브옵스 참고)를 실제로 가능하게 하는 아키텍처 형태가 MSA다. 몇 년이 지나 실무에서 이런저런 MSA 전환을 겪어보면서 다시 정리해본다.

이 글에서는 “MSA가 뭔지”와 “언제 써야(말아야) 하는지”를 먼저 정리하고, 이어지는 글에서 회복탄력성 패턴과 분산 트랜잭션을 다룬다.

모놀리스(Monolith) vs 마이크로서비스(Microservices)

  모놀리식 아키텍처 마이크로서비스 아키텍처
배포 단위 애플리케이션 전체가 하나의 배포 단위 서비스마다 독립적으로 배포
코드 구조 하나의 코드베이스, 모듈로 분리 서비스마다 별도 리포지토리(보통)
데이터베이스 보통 하나의 DB를 공유 서비스마다 자신의 DB를 소유 (Database per Service)
통신 방식 같은 프로세스 내 메서드 호출 네트워크를 통한 API 호출(HTTP/gRPC) 또는 메시징
스케일링 애플리케이션 전체를 통째로 스케일 트래픽이 몰리는 서비스만 선택적으로 스케일
장애 범위 한 부분의 버그가 전체에 영향을 줄 수 있음 원칙적으로는 장애가 해당 서비스로 국한(격리)
초기 개발 속도 빠름 (하나의 프로젝트, 트랜잭션 관리 쉬움) 느림 (분산 환경 설정, 서비스 간 계약 정의 필요)

MSA가 해결하려는 문제는 결국 “모놀리스가 커지면서 생기는 문제“들이다.

  • 코드베이스가 거대해져서 빌드/테스트 시간이 길어짐.
  • 한 팀이 배포하려면 다른 팀의 변경사항까지 다 같이 배포해야 해서(결합된 배포) 배포 주기가 느려짐.
  • 트래픽이 몰리는 기능(예: 주문) 하나 때문에 관계없는 기능(예: 리뷰)까지 통째로 스케일링해야 해서 자원 낭비.

Bounded Context — 서비스를 어떻게 나눌 것인가

DDD(Domain-Driven Design)에서 나온 개념. 하나의 “용어”가 문맥(context)에 따라 다른 의미를 가질 수 있다는 것을 인정하고, 그 문맥의 경계를 명확히 나누는 것이다.

예를 들어 전자상거래에서 “상품(Product)”이라는 단어는 카탈로그 서비스에서는 “이름, 설명, 이미지”를 뜻하지만, 재고 서비스에서는 “수량, 창고 위치”를 뜻하고, 결제 서비스에서는 “가격, 세금”만 관심사다. 이 세 가지를 하나의 거대한 Product 테이블/클래스로 억지로 합치면, 한 팀이 필드 하나만 바꿔도 다른 팀 코드가 깨지는 결합이 생긴다.

서비스 경계를 나누는 실전 기준은 비즈니스 능력(business capability) 단위다. “주문”, “결제”, “재고”, “배송”처럼 독립적으로 변경/배포될 수 있는 단위로 나눈다. 기술 계층(예: “모든 컨트롤러”, “모든 배치”)으로 나누는 건 잘못된 분리 기준이다.

Conway’s Law (콘웨이의 법칙)

“시스템을 설계하는 조직은, 자신들의 커뮤니케이션 구조를 그대로 닮은 설계를 만들어낸다.”

팀 구조와 시스템 구조는 서로 강하게 영향을 주고받는다. 앞서 언급한 “사일로 해체”가 여기서 다시 등장한다 — 팀이 사일로(부서 간 단절)로 나뉘어 있으면, 그 팀들이 만드는 시스템도 결국 사일로처럼 서로 강하게 결합되고 소통 비용이 큰 구조가 되기 쉽다.

MSA를 성공적으로 도입한 조직(아마존, 넷플릭스 등)은 대부분 팀 조직 자체를 서비스 단위(Two-Pizza Team)로 먼저 재편했다 — 아키텍처만 바꾼 게 아니라 조직 구조를 먼저/함께 바꾼 것이다. 이 순서를 거꾸로 해서 “조직은 그대로 두고 코드만 서비스로 쪼개면” 결국 팀 간 조율 비용이 그대로 남아 MSA의 이점(독립 배포)을 못 누리게 된다.

MSA 도입의 대가 (Trade-off) — 공짜가 아니다

  • 분산 시스템의 복잡도: 메서드 호출이 네트워크 호출로 바뀌면서, 지연시간·타임아웃·부분 실패(partial failure)를 항상 고려해야 함 → 다음 글(회복탄력성 패턴)의 주제.
  • 데이터 일관성: 서비스마다 DB가 분리되면 하나의 트랜잭션으로 묶을 수 없음 → 분산 트랜잭션 문제 발생 → 세 번째 글(Saga 패턴)의 주제.
  • 운영 복잡도: 서비스가 수십~수백 개가 되면, 로그/모니터링/추적(트레이싱)을 각 서비스마다 따로 볼 수 없어 분산 트레이싱(Zipkin/Jaeger), 중앙 로그 수집(ELK) 같은 인프라가 필수가 됨.
  • 테스트 복잡도: 서비스 하나를 테스트하려 해도 의존하는 다른 서비스들이 필요 — Contract Testing(Pact 등), 서비스 가상화(Mock 서버)로 대응.

“Monolith First” — 언제 MSA를 쓰지 말아야 하는가

Martin Fowler가 제안한 개념: 도메인 경계가 아직 불명확한 초기 단계에서는 모놀리스로 시작하고, 실제로 어디를 나눠야 하는지 경험을 통해 파악한 뒤에 MSA로 전환하는 게 안전하다는 접근이다.

MSA를 섣불리 도입하면 안 되는 신호

  • 팀이 작아서(2~3명) 서비스별로 팀을 나눌 수도 없는 규모인데 무작정 서비스만 쪼갬 → 한 사람이 여러 서비스를 오가며 유지보수 → 오히려 분산 시스템의 복잡도만 떠안음.
  • 도메인 경계가 자주 바뀜(스타트업 초기 피벗 단계) → 서비스 경계를 잘못 그으면, 잘못 그은 경계를 넘나드는 통신이 계속 생겨 오히려 모놀리스보다 못한 결합도가 생김.

즉 “MSA냐 모놀리스냐”는 기술적 우열의 문제가 아니라, 조직 규모·도메인 성숙도·운영 역량에 따라 달라지는 선택의 문제다.

참고자료


Comments