MSA 핵심 패턴: API Gateway, Circuit Breaker, Event-Driven 설계
좋은 마이크로서비스 논의가 서비스 개수나 기술 스택이 아니라, 조직 경계와 운영 경계에서 시작되는 이유도 여기에 있습니다.
첫 번째 결정은 정말 분산이 필요한가이다
많은 팀이 마이크로서비스에 기대하는 것은 대체로 아래입니다.
- 더 빠른 배포
- 더 분명한 팀 소유권
- 독립적인 스케일링
- 더 나은 장애 격리
모두 타당한 목표지만, 그렇다고 곧바로 서비스 분리가 답은 아닙니다. 실제로는 modular monolith가 같은 문제를 훨씬 싸게 해결해 주는 경우도 많습니다.
MSA가 정당화되려면 보통 이런 조건이 필요합니다.
- 여러 도메인이 실제로 다른 속도로 변한다
- 팀이 배포 독립성을 강하게 원한다
- 워크로드 특성이 달라 하나의 런타임 모델이 비효율적이다
- 도메인 결합이 실제로 관찰되고 있다
이 조건이 없으면 MSA는 대개 지연 시간, 운영 부담, 디버깅 비용만 늘립니다.
서비스 경계는 기술이 아니라 비즈니스 책임을 따라야 한다
실무에서 가장 흔한 실패는 도메인 책임이 아니라 기술 레이어나 팀 추측으로 경계를 자르는 것입니다.
건강한 서비스 경계는 보통 아래 특성이 있습니다.
- 하나의 비즈니스 능력을 분명히 소유한다
- 자신의 데이터 권한을 가진다
- 독립적으로 바뀔 이유가 있다
- 다른 서비스와의 계약이 명시적이다
반대로 나쁜 경계는 이런 모습입니다.
- CRUD 리소스 하나당 서비스 하나
- 여러 서비스가 같은 테이블을 만진다
- 비즈니스 의미가 모호한 utility service
- 다이어그램을 잘게 보이게 만들기 위한 분리
MSA의 숨은 비용은 네트워크보다, 잘못 자른 경계에서 훨씬 크게 발생합니다.
API Gateway는 라우터가 아니라 정책 경계다
API Gateway는 아래 역할을 일관되게 중앙화할 때 가치가 있습니다.
- 인증과 권한 확인
- rate limiting
- 요청 형태 조정
- public contract와 internal contract 분리
- 클라이언트 단순화를 위한 cross-service aggregation
반대로 아래처럼 되면 금방 해로워집니다.
- 비즈니스 규칙이 gateway 안으로 숨어드는 경우
- 내부 워크플로를 과도하게 orchestration 하는 경우
- 사실상 두 번째 모놀리스를 만드는 경우
좋은 gateway는 edge policy layer입니다. 핵심 비즈니스 로직이 이주하는 장소가 아닙니다.
동기 호출은 실패 결합을 만든다
요청 경로가 동기 서비스 체인에 많이 의존할수록 시스템은 자연스럽게 아래를 같이 떠안게 됩니다.
- latency amplification
- timeout 전파
- retry storm
- 더 어려운 장애 추적
이것이 monolith와 분산 시스템이 다르게 실패하는 지점입니다. 로컬 코드는 단순해 보여도, 전체 시스템 거동은 훨씬 예측하기 어려워집니다.
동기 호출이 나쁘다는 뜻은 아닙니다. 다만 아래 규율이 필요합니다.
- 호출 체인을 짧게 유지하기
- timeout 상한을 분명히 두기
- retry 책임을 명시하기
- 불안정한 의존성에는 bulkhead와 circuit breaker를 붙이기
마이크로서비스가 비싸지는 가장 빠른 길은 네트워크 호출을 지역 함수 호출처럼 다루는 것입니다.
이벤트 기반 설계는 결합을 줄이지만 일관성 모델을 바꾼다
async messaging은 동기 결합을 줄이는 데 효과적일 수 있습니다. 하지만 대가도 바뀝니다.
- 지연 시간 결합은 약해지고
- 시간적 분리는 쉬워지며
- 실패 처리는 더 분산되고
- 일관성은 즉시성이 아니라 eventual consistency로 이동합니다
그래서 Outbox, Inbox, idempotent consumer, Saga, 보상 트랜잭션 같은 패턴이 중요합니다. 이벤트 기반 설계는 “더 잘 확장되는 REST”가 아니라, 다른 운영 모델입니다.
핵심 질문은 이 비즈니스 프로세스가 비동기 진실 전파를 감당할 수 있느냐입니다.
데이터 소유권은 거의 예외 없는 규칙이다
MSA에서 가장 강한 규칙 중 하나는 각 서비스가 자기 persistence boundary를 가져야 한다는 점입니다.
서비스가 테이블을 공유하거나 서로의 저장소를 직접 건드리기 시작하면:
- 배포 독립성이 약해지고
- 스키마 변경이 결합되고
- 장애 책임 추적이 흐려지고
- MSA 비용만 남고 장점은 줄어듭니다
통합은 이벤트, API, 계약을 통해 일어나야지, 숨은 DB 결합을 통해 일어나면 안 됩니다.
예시: 더 건강한 주문 흐름
client
-> api gateway
-> order service
-> payment service (sync, bounded timeout)
-> publish order-created event
-> inventory service consumes
-> notification service consumes
이 흐름이 좋은 이유는 tradeoff가 다 보이기 때문입니다.
- 결제는 사용자 확인이 필요하니 동기
- 후속 부작용은 사용자 응답을 막을 이유가 없으니 비동기
- gateway는 edge에 머물고 비즈니스 workflow를 소유하지 않음
핵심은 분산됐다는 사실이 아니라, 왜 분산됐는지가 설명 가능하다는 점입니다.
관측성은 부가 기능이 아니다
강한 observability 없이 운영되는 마이크로서비스는 생각보다 훨씬 빨리 무너집니다.
최소한 필요한 것은:
- 서비스 경계를 넘는 request tracing
- correlation ID가 붙은 structured logs
- dependency latency metrics
- async 경로의 queue/retry 가시성
- 서비스별 SLO 책임
이게 없으면 incident 대응은 금방 감에 의존하게 됩니다.
자주 보는 안티패턴
- 도메인 경계가 성숙하기 전에 서비스부터 나누는 경우
- gateway를 orchestration engine으로 쓰는 경우
- 여러 서비스가 DB를 공유하는 경우
- 모든 상호작용을 동기 RPC로 만드는 경우
- idempotency와 replay 규칙 없이 이벤트만 도입하는 경우
이런 패턴은 단기 편의는 주지만, MSA가 원래 주려던 이점을 빠르게 무너뜨립니다.
리뷰 체크리스트
- 각 서비스가 실제 비즈니스 능력을 하나씩 소유하는가
- 즉시 일관성이 꼭 필요한 곳에만 sync call을 쓰는가
- async 메시징이 consistency tradeoff를 의식하고 도입됐는가
- 각 서비스가 자기 데이터 경계를 가지는가
- sync와 async 흐름 모두 incident 시 추적 가능한가
마무리 판단
좋은 마이크로서비스 설계는 서비스가 많다는 뜻이 아닙니다. 독립성이 분산 비용을 정당화하는 곳을 정확히 고르는 일에 가깝습니다. 경계가 비즈니스 현실과 맞고, 실패 모드가 의도적으로 설계되어 있으며, observability가 기본값일 때 MSA는 비로소 가치가 생깁니다.
Continue Reading
다음으로 읽기 좋은 글
백엔드 학습 경로: 입문부터 고급까지
API 기초, 안정성 패턴, 분산 아키텍처까지 백엔드 지식을 체계적으로 쌓을 수 있는 실전 로드맵입니다.
⚙️ BackendSaga 오케스트레이션 vs 코레오그래피
분산 워크플로가 복잡해질 때 오케스트레이션과 코레오그래피 중 무엇을 선택해야 하는지 실전 관점에서 정리합니다.
💬 LanguagePython Service Layer 패턴 실전 적용
Python 애플리케이션에서 transport, 비즈니스 규칙, persistence 책임을 분리하는 실전 구조를 정리합니다.
🖥️ Frontend마이크로 프론트엔드 — Module Federation 실전 적용
마이크로 프론트엔드를 기술 데모가 아니라 팀 경계와 배포 독립성의 관점에서 정리합니다. Module Federation 구조, shared 의존성, 런타임 로딩, 상태 공유, 운영 함정과 적용 기준까지 실무적으로 설명합니다.
다음 탐색