이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.
- 어떤 기능이 MSA 분리 후보가 되는가?
- 서비스 분리는 성능, 정합성, 장애 격리, 운영 복잡도 중 무엇을 얻고 무엇을 잃는가?
- Monolith에서 바로 service를 만들지 않고 어떤 준비를 먼저 해야 하는가?
개요
MSA는 application을 여러 독립 service로 나누는 구조다. 좋은 MSA의 단위는 controller나 table이 아니라 책임과 owner다. 서비스가 나뉘려면 그 기능을 책임지는 팀, 데이터 owner, API 계약, 배포 권한, 장애 대응 책임이 함께 나뉘어야 한다. 단순히 코드가 많거나 트래픽이 늘었다는 이유만으로 나누면 network와 운영 복잡도만 증가한다. MSA 분리는 “더 멋진 구조”가 아니라 “분리 비용을 감수할 만큼 독립성이 필요한가”를 판단하는 일이다.
이 문서가 바꾸는 설계 축
| 축 | 바뀌는 내용 |
|---|---|
| 성능 | 기능별 scale은 가능하지만 network latency와 serialization 비용이 생긴다. |
| 정합성 | 단일 transaction 대신 saga, event, read model, 보상 상태가 필요해진다. |
| 장애 격리 | service별 장애 격리가 가능하지만 sync fanout은 장애 전파를 키운다. |
| 운영 복잡도 | 배포, tracing, alert, contract test, schema migration 단위가 늘어난다. |
분리 후보 기준
| 기준 | 분리 근거가 강한 경우 | 약한 경우 |
|---|---|---|
| 팀 소유권 | payment 팀이 독립 roadmap과 on-call을 가진다. | 같은 팀이 모든 기능을 같이 배포한다. |
| 데이터 소유권 | payment 데이터 write owner가 명확하다. | 여러 기능이 같은 table을 직접 수정한다. |
| 배포 독립성 | 결제 변경을 주문 배포와 분리해야 한다. | 항상 같은 release train으로 나간다. |
| 장애 격리 | 결제 장애가 주문 조회를 막으면 안 된다. | 장애 시 전체 기능을 같이 내리는 것이 허용된다. |
| scaling 차이 | 알림 worker만 10배 scale해야 한다. | 모든 기능 트래픽 패턴이 비슷하다. |
| 규제/보안 | 결제, 개인정보, 정산처럼 접근 통제가 다르다. | 보안 경계가 동일하다. |
| 이 중 하나만으로는 부족하다. | ||
| 보통 여러 기준이 동시에 맞을 때 분리 근거가 강해진다. |
대표 시나리오
주문 서비스에서 결제 기능을 분리할지 검토한다고 하자. 현재 문제는 다음과 같다.
결제 provider 변경이 월 6회 발생
주문 기능 배포는 주 1회 release train
결제 장애 때 주문 조회 p95가 2초 이상 증가
결제 테이블 schema 변경 승인자가 불명확
결제 로그와 알림 owner가 주문팀과 섞여 있음이 상태에서 바로 service를 만들면 위험하다. 먼저 결제 module owner, table owner, API 계약, timeout, event, on-call 책임을 정해야 한다. 그 다음에도 독립 배포 요구가 남으면 service 분리를 검토한다.
분리하면 안 되는 경우
MSA가 문제를 키우는 경우도 많다. 다음 상황에서는 분리보다 module 정리가 먼저다.
- 기능 경계가 계속 바뀐다.
- 하나의 transaction으로 묶어야 하는 불변식이 많다.
- 팀이 하나이고 on-call도 하나다.
- service별 SLO와 alert를 운영할 수 없다.
- shared DB를 유지할 계획이다.
- synchronous call chain이 길어질 것이 명확하다. 이 경우 service 분리는 구조 개선이 아니라 latency와 장애 모델 추가가 된다.
분리 후보 평가표
| 후보 | owner | data | deploy | failure isolation | 판단 |
|---|---|---|---|---|---|
| payment | 별도 팀 가능 | 결제 table owner 명확 | 독립 배포 필요 | 주문 조회와 격리 필요 | 강한 후보 |
| notification | worker scale 차이 큼 | 발송 이력 owner 명확 | 비동기 가능 | 장애 시 지연 허용 | 강한 후보 |
| coupon | 주문 transaction과 강하게 연결 | 쿠폰/주문 동시 불변식 많음 | 독립 배포 낮음 | 장애 시 주문 영향 큼 | module 경계 우선 |
| admin report | read 부하 큼 | source data owner 아님 | 배포 독립 낮음 | read model로 격리 가능 | service보다 projection |
| 평가표는 점수 놀이가 아니라 논의의 누락을 줄이는 도구다. |
서비스 계약
Service 분리는 API 계약을 만든다. 결제 예약 API를 예로 들면 주문 서비스는 결제 내부 table을 알지 못해야 한다.
POST /payments/reservations
Idempotency-Key: order-123
{
"orderId": "order-123",
"memberId": "member-9",
"amount": 42000
}응답도 성공만 있으면 안 된다.
{
"paymentReservationId": "pay-rsv-77",
"status": "PENDING_AUTHORIZATION",
"retryable": true
}MSA에서는 pending, retryable, duplicate, timeout, unknown 상태가 API 계약에 들어와야 한다.
Sync와 Async 선택
서비스가 나뉘면 호출 방식이 설계 결정이 된다.
| 경계 | 동기 호출 적합 | 비동기 event 적합 |
|---|---|---|
| 결제 승인 | 사용자 응답 전에 결과가 필요할 수 있다. | 승인 후 알림, 영수증 |
| 재고 예약 | 실패 시 주문을 막아야 한다. | 재고 변경 projection |
| 알림 발송 | 대부분 동기 불필요 | 발송 지연 허용 |
| 정산 집계 | 사용자 요청과 분리 | batch/event 처리 |
| 동기 호출이 많아질수록 latency와 partial failure 비용이 커진다. | ||
| 비동기 event는 사용자 응답을 빠르게 하지만 중복과 지연을 설계해야 한다. |
분리 순서
MSA 전환은 새 repository를 만드는 일이 아니다. 안전한 순서는 보통 다음과 같다.
- Monolith 안에서 module boundary를 만든다.
- table owner와 public API를 정한다.
- 다른 module의 direct repository access를 제거한다.
- event/outbox와 idempotency를 도입한다.
- contract test와 observability를 준비한다.
- 일부 traffic 또는 한 기능부터 service로 뺀다.
- old path를 제거하기 전 rollback 기준을 둔다. 이 순서는 느려 보이지만 데이터 사고와 분산 monolith를 줄인다.
운영 준비도
다음이 없으면 분리 후 장애 대응이 어려워진다.
| 준비 항목 | 필요한 이유 |
|---|---|
| distributed tracing | 요청이 어느 service에서 느려졌는지 추적 |
| service SLO | 장애 판단 기준 분리 |
| contract test | API 변경으로 다른 service를 깨뜨리지 않음 |
| deployment rollback | 독립 배포의 실제 안전장치 |
| owner/on-call | 장애와 schema 변경 책임 명확화 |
| runbook | timeout, DLQ, lag, partial failure 대응 |
개인 프로젝트 기준
- MSA를 구현하지 않아도 분리 후보 평가표를 작성한다.
- shared DB를 유지하는 구조가 왜 위험한지 설명한다.
- notification처럼 비동기 분리에 적합한 기능과 payment처럼 동기 판단이 필요한 기능을 구분한다.
- API 계약에 pending, duplicate, timeout 응답을 넣어본다.
- 포트폴리오에서는 “MSA로 나눴다”보다 “어떤 기준으로 나눴는가”를 먼저 말한다.
기업 운영 기준
- service 분리 RFC에는 owner, data, API, SLO, migration, rollback, cost를 포함한다.
- 분리 전후 latency와 incident blast radius를 비교한다.
- shared DB 제거 계획이 없으면 MSA 승인 조건을 만족하지 못한 것으로 본다.
- service별 dashboard와 alert를 배포 전에 만든다.
- team topology와 service topology가 맞는지 정기적으로 검토한다.
위험 신호
- controller 단위로 service를 나눈다.
- 하나의 기능 요청이 5개 이상 service를 동기 호출한다.
- service는 나뉘었지만 DB schema는 모든 팀이 수정한다.
- API 계약 없이 내부 DTO를 그대로 노출한다.
- tracing 없이 service를 늘린다.
- 독립 배포를 말하지만 실제로는 모든 service를 같은 시간에 배포한다.
확인 질문
확인 질문
- 확인 질문: MSA 분리 후보를 판단하는 핵심 기준은 무엇인가?
- 답변: 팀 소유권, 데이터 소유권, 배포 독립성, 장애 격리 필요, scaling 차이, 운영 준비도다.
- 확인 질문: shared DB가 남아 있으면 왜 분리 효과가 약한가?
- 답변: schema 변경과 데이터 write 책임이 여전히 묶여 있어 독립 배포와 owner 분리가 깨지기 때문이다.
- 확인 질문: MSA 분리 전 Monolith 안에서 먼저 해야 할 일은 무엇인가?
- 답변: module boundary, public API, table owner, event/outbox, 관측과 contract test를 먼저 검증하는 것이다.