Step 10. 경계가 프로세스 밖으로 — 분산의 유혹과 대가

만화로 보는 요약 — 먼저 읽어보세요
분산은 팀 자율성을 주지만 부분 실패와 CAP·운영 비용을 부르므로, 경계의 이득은 모듈로 먼저 얻고 배포 분리는 필요할 때 선택한다.
면접 실전 질문: ① 타임아웃이 왜 모른다는 뜻인가? ② CAP에서 실제 선택은 무엇인가? ③ 모듈러 모놀리스는 무엇을 얻고 무엇을 피하는가?
이 장의 질문: 모듈 경계를 네트워크 경계로 바꾸는 순간, 정확히 무엇이 달라지는가? 그리고 그 청구서를 다 받아본 업계는 왜 진자를 되돌렸는가?
1. 2014년, 이름이 붙다
앞 장에서 본 조직의 고통 — 100명의 단일 배포 열차 — 에 대한 응답은 2010년대 초 여러 회사(Netflix, Amazon, SoundCloud…)에서 각자 진행되고 있었다. 2014년 3월, Martin Fowler와 James Lewis가 이 관행들에 이름과 정의를 부여한다 — 마이크로서비스(Microservices). 그들이 열거한 특징 중 이 책의 서사에 맞물리는 세 가지만 뽑자.
- 비즈니스 능력 중심의 조직화. 서비스는 기술 층이 아니라 주문, 정산, 배송 같은 업무 능력 단위로 자른다 — 앞 장의 삼중 정렬(컨텍스트·모듈·팀)을 배포 단위까지 확장한 것이고, 그 경계 도구로 업계가 꺼내 든 것이 Step 07의 바운디드 컨텍스트다.
- 분권화된 데이터 관리. 서비스마다 자기 데이터베이스를 가진다. 공유 DB(공통 결합)의 금지 — 이것이 다음 장 전체의 주제가 될 만큼 깊은 결정이다.
- 스마트 엔드포인트, 멍청한 파이프. 지능은 서비스 끝점에, 통신은 단순한 HTTP/메시징으로. Step 05의 묘지에서 읽은 ESB 비문(碑文)이 공식 강령이 된 것이다.
얻는 것은 앞 장에서 이미 정리했다: 팀 자율성과 배포 독립성. 이 장의 일은 반대쪽 — 지불하는 것 — 의 목록화다. 그리고 그 목록은 취향이나 성숙도의 문제가 아니라 물리학이다.
2. 물리학 청구서 1 — 원격은 로컬이 아니다
1994년, Sun의 Jim Waldo와 동료들은 「A Note on Distributed Computing」에서 당대(그리고 오늘날까지)의 꿈 — 원격 객체를 로컬 객체처럼 투명하게 다루겠다는 — 을 정면으로 격추한다. 원격 호출은 로컬 호출과 정도가 아니라 종류가 다르다는 것이다. 네 가지가 다르다: 지연(수백만 배 느리다), 메모리 접근(포인터가 무의미하다), 동시성(호출 순서가 보장되지 않는다), 그리고 가장 치명적인 부분 실패(partial failure).
부분 실패를 정확히 이해하는 것이 이 장의 절반이다. 한 프로세스 안에서 함수 호출은 성공하거나, 예외와 함께 실패하거나 — 둘 중 하나이고, 어느 쪽인지 항상 안다. 네트워크 호출에는 세 번째 상태가 있다: 타임아웃. 타임아웃은 “실패했다”가 아니라 **“모른다”**다. 상대가 요청을 받기 전에 죽었는지(미실행), 처리하고 응답만 유실됐는지(실행됨) 구별할 수 없다. 재시도하면 중복 실행일 수 있고, 안 하면 유실일 수 있다 — 결제 서비스에 이 질문을 대입해보면 왜 이것이 물리학이자 동시에 돈 문제인지 즉시 보인다. (멱등성이라는 단어가 떠올랐다면 정확하다 — 다음 장의 재료다.)
같은 시기에 정리된 경고 목록이 **분산 컴퓨팅의 8가지 오류(8 Fallacies)**다 — Sun의 Peter Deutsch가 7개를 정식화하고(1994) James Gosling이 여덟 번째를 더했다. 네트워크는 신뢰할 수 있다, 지연은 0이다, 대역폭은 무한하다, 네트워크는 안전하다, 토폴로지는 변하지 않는다, 관리자는 한 명이다, 전송 비용은 0이다, 네트워크는 균질하다 — 여덟 문장 모두 거짓이고, 분산 시스템의 장애 사례집은 사실상 이 여덟 개의 오답 노트다. Step 05에서 CORBA의 사인(死因)으로 지목한 “분산 투명성의 거짓말”의 전거가 바로 이 두 문헌이다.
물리학 밖의 실무 청구서도 하나 적어두자 — 관측가능성 세금. 한 프로세스에서는 스택트레이스 한 장이던 것이, 스무 개 서비스에서는 분산 추적과 상관 ID 없이는 재구성조차 불가능한 퍼즐이 된다. 분산에서 관측가능성은 선택이 아니라 입장료다.
3. 물리학 청구서 2 — CAP, 갈림길의 정리
두 번째 청구서는 데이터의 일관성에 온다. 2000년 Eric Brewer가 추측으로 제시하고 2002년 Gilbert와 Lynch가 증명한 CAP 정리다. 유통되는 “일관성(C)·가용성(A)·분할내성(P) 중 둘을 골라라”는 요약은 편리하지만 부정확하다. Brewer 자신이 12년 뒤 해설(2012)에서 바로잡은 정확한 진술은 이렇다:
네트워크 분할(partition)은 선택지가 아니라 물리적 사실이다 — 네트워크는 반드시, 언젠가 끊긴다. 따라서 P는 “고르는” 것이 아니다. 정리가 실제로 말하는 것은: 분할이 일어난 동안, 시스템은 일관성(모든 노드가 같은 답)과 가용성(모든 요청에 응답) 중 하나를 포기해야 한다.
정산 서비스가 조회 성능을 위해 주문 데이터의 복제본을 들고 있다고 하자(다음 장에서 볼 표준 구도다). 그 복제본과 원본 사이의 네트워크가 끊겼다 — CAP가 말하는 분할은 정확히 이 상황, 같은 데이터의 사본들이 서로 맞출 수 없게 된 상황이다. 이때 정산 쪽에 주문 조회가 오면? 오래된 복제본이라도 응답한다(A 선택, C 포기 — 최신이 아닐 수 있음), 아니면 원본과 다시 맞춰질 때까지 에러를 반환한다(C 선택, A 포기). 제3의 선택지는 정리에 의해 존재하지 않는다. Step 04에서 예고한 가용성 vs 일관성 트레이드오프가 “설계 취향”이 아니라 증명된 불가능성이라는 것 — 이것이 CAP의 진짜 내용이다. (덧붙여 Abadi의 PACELC(2012)는 분할이 없을 때도 지연과 일관성의 교환이 상존함을 지적한다 — 동기 복제는 느리고, 비동기 복제는 뒤처진다.)
도메인이 답을 정한다는 Step 04의 원칙이 여기서 실전이 된다. 은행 잔액은 C를 고른다 — 틀린 잔액을 보여주느니 멈춘다. SNS 피드는 A를 고른다 — 3초 뒤처진 피드를 아무도 눈치채지 못한다. 한 시스템 안에서도 경계마다 답이 다르다.
4. 새 어휘들 — 청구서와 함께 사는 법
분산을 선택한 이상 청구서는 피할 수 없고, 업계는 그것과 사는 어휘들을 만들었다.
결과적 일관성(eventual consistency). Vogels의 정식화(2008): 새 갱신이 멈추면 모든 복제본이 결국 같은 값에 수렴한다는 보증. “대충 일관성”이 아니라 수렴이 보증된, 다만 시점을 약속하지 않는 일관성이다. Step 07에서 심어둔 문장 — “애그리거트 사이의 정합성은 트랜잭션이 아니라 결과적으로 맞춘다” — 이 서비스 경계로 확장된 것이다.
Saga. 두 서비스에 걸친 “주문 확정 + 포인트 차감”은 하나의 트랜잭션이 될 수 없다(2절의 물리학). 분산 트랜잭션 프로토콜(2단계 커밋, 2PC)이 있긴 하지만, 조정자가 멈추면 참여자 전원이 잠긴 채 기다리는 블로킹 프로토콜이라 서비스 규모에서는 기피된다. Saga는 이를 로컬 트랜잭션의 연쇄 + 실패 시 보상(compensation) 트랜잭션으로 바꾼다: 포인트 차감이 실패하면 주문 확정을 롤백하는 게 아니라 “주문 취소”라는 역방향 업무 행위를 실행한다. 이 아이디어의 원전이 마이크로서비스보다 사반세기 앞선 1987년(Garcia-Molina & Salem, 장기 트랜잭션 논문)이라는 사실은, 분산의 문제들이 얼마나 오래된 문제인지의 증거로 기억할 만하다.
이벤트 기반 아키텍처. 서비스 간 결합을 낮추는 표준 수단 — “정산 서비스를 호출한다”(내가 너를 안다) 대신 “주문확정 이벤트를 발행한다”(누가 듣는지 모른다). 화살표의 방향이 바뀌는 것에 주목하라: 호출자→피호출자의 결합이, 발행자를 향한 구독자들의 결합으로 역전된다. DIP의 메시징 판이라 불러도 좋다 — 다만 소유권의 방향은 다르다. 이벤트 계약(스키마)은 보통 발행자의 것이라, “계약은 사용자의 것”이던 DIP의 소유권 역전과는 반대다. 둘의 공통점은 직접 호출 결합의 제거이지, 소유권의 이동이 아니다.
CQRS. 족보부터 바로잡자. 뿌리는 Bertrand Meyer의 CQS(Command-Query Separation, 1988) — “메서드는 상태를 바꾸거나(command) 답을 되돌리거나(query), 둘 중 하나만 하라”는 객체 수준 원칙이다. Greg Young이 이를 시스템 수준으로 끌어올린 것이 CQRS(Command Query Responsibility Segregation) — 쓰기 모델과 읽기 모델을 아예 분리한다. 쓰기는 불변식을 지키는 도메인 모델(애그리거트)로, 읽기는 화면에 맞게 비정규화된 별도 모델로. 둘 사이는 (보통 이벤트로) 결과적으로 동기화된다. 언제 정당한가 — 읽기와 쓰기의 요구가 실제로 갈릴 때다: 쓰기는 초당 수십 건에 복잡한 규칙, 읽기는 초당 수만 건에 단순 조회라면, 한 모델로 둘을 섬기는 것이 병목이다.
그리고 반드시 분리해야 할 개념: CQRS ≠ 이벤트 소싱. 이벤트 소싱은 “상태 대신 사건의 로그를 저장하고 상태는 재생으로 얻는” 별개의 저장 전략이다. 둘은 궁합이 좋아 자주 함께 쓰일 뿐, 어느 쪽도 다른 쪽을 요구하지 않는다 — CQRS는 읽기 테이블 하나 더 두는 것만으로 성립한다. (이벤트 소싱 자체의 손익은 다음 장에서 다룬다.)
5. 진자의 반환 — 모듈러 모놀리스
2010년대 후반, 청구서를 다 받아본 업계의 회계 감사가 시작된다. Fowler는 이미 2015년에 마이크로서비스 프리미엄이라는 표현으로 경고했다 — 이 스타일은 기본값이 아니라 할증료이며, 시스템이 충분히 복잡할 때만 그 할증이 정당화된다. 같은 해의 “모놀리스 우선(Monolith First)” 조언도 같은 맥락이다. 그리고 상징적인 사례들 — 대표적으로 2023년 Amazon Prime Video 팀이 모니터링 시스템을 분산 구성(서버리스/마이크로서비스)에서 모놀리스로 되돌려 비용을 90% 줄였다는 공개 회고(이 사례의 해석을 두고 논쟁도 있었다) — 가 진자의 방향 전환을 가시화했다.
돌아온 자리에 이름이 붙었다: 모듈러 모놀리스(modular monolith). Simon Brown의 오래된 반문이 이 접근의 정신이다 — “구조화된 모놀리스도 못 만들면서, 마이크로서비스로 쪼개면 잘될 거라 믿는 근거가 무엇인가?” 아이디어는 간단하다. 경계의 논리적 이득과 물리적 비용을 분리하라. 바운디드 컨텍스트별 모듈, 모듈 사이는 공개 API로만, 데이터 소유권도 모듈별로 — 여기까지는 마이크로서비스와 같다. 다만 전부를 한 프로세스, 한 배포로 묶는다. 모듈성(변경의 지역화, Step 08이 남긴 수직 절단의 숙제)은 얻고, 분산의 물리학(부분 실패, CAP, 운영 복잡도)은 지불하지 않는다. Shopify가 대규모로 실증했고, Spring Modulith 같은 도구가 모듈 경계의 규율을 빌드에서 강제해준다.
이 장의 결론이자 Part 2 진자 서사의 클라이맥스를 적자. 모놀리스 → 마이크로서비스 → 모듈러 모놀리스의 왕복은 유행의 변덕이 아니라 학습이다. 돌아온 자리는 출발점이 아니다 — 처음의 모놀리스는 진흙공이었지만, 돌아온 모놀리스는 컨텍스트 경계·데이터 소유권·모듈 규율을 갖췄다. 그리고 배포 경계는 이제 이분법이 아니라 미룰 수 있는 결정이 됐다: 모듈로 시작하고, 조직이 커져 조율이 진짜 병목이 된 모듈만 서비스로 떼어낸다. Step 02의 기준 — 좋은 아키텍처는 결정을 미룰 수 있게 한다 — 이 배포 토폴로지에까지 적용된 것이다. 진자는 왕복했지만, 궤적은 원이 아니라 나선이다.
6. 흔한 오해 바로잡기
오해 1: “마이크로서비스가 현대적 기본값이다.” 프리미엄이다. 갚을 조직 규모(다수 팀의 조율 병목)와 도메인 복잡도가 없으면 할증료만 낸다. 앞 장의 인과를 기억하라 — 조직의 고통이 없는데 서비스부터 쪼개면, 얻는 것 없이 이 장의 청구서 전부를 받는다.
오해 2: “CAP는 C, A, P 중 둘을 고르는 문제다.” P는 물리적 사실이라 고를 수 없다. 정확한 질문은 “분할이 일어난 동안 C와 A 중 무엇을 포기하는가”이고, 분할이 없을 때도 지연 vs 일관성의 교환(PACELC)은 상존한다.
오해 3: “결과적 일관성 = 데이터가 대충 맞는다.” 수렴이 보증된 모델이다 — 약속하지 않는 것은 정확성이 아니라 시점이다. 진짜 설계 질문은 “그 수렴 지연 동안 사용자와 업무가 무엇을 보게 되는가”이고, 그것은 기술이 아니라 도메인 질문이다(잔액이면 안 되고, 피드면 된다).
오해 4: “CQRS를 하려면 이벤트 소싱이 필요하다.” 독립이다. CQRS의 최소 구현은 읽기 전용 테이블 하나다. 이 혼동으로 “CQRS = 무겁다”는 인상이 생겼지만, 무거운 쪽은 대개 함께 도입된 이벤트 소싱이다.
오해 5: “모듈러 모놀리스는 그냥 모놀리스의 재포장이다.” 차이는 규율이다. 진흙 모놀리스는 모듈 경계가 없거나 장식이고, 모듈러 모놀리스는 경계 위반이 빌드에서 깨진다. “그냥 모놀리스”로 퇴화하는 것을 막는 것이 도구와 규율(그리고 뒤에서 볼 적합도 함수)의 몫이다.
7. 요약, 그리고 다음 장으로
- 마이크로서비스(2014)의 핵심은 비즈니스 능력 중심 절단 + 분권 데이터 + 멍청한 파이프 — 얻는 것은 앞 장의 결론(팀 자율성, 배포 독립성)이다.
- 청구서 1: 원격은 로컬과 종류가 다르다(Waldo) — 지연, 그리고 “모른다”라는 세 번째 상태(부분 실패). 8가지 오류는 그 오답 노트다.
- 청구서 2: CAP — 분할은 사실이고, 분할 동안 C와 A는 양립 불가(증명됨). 무엇을 포기할지는 도메인이 정한다.
- 함께 사는 어휘: 결과적 일관성(수렴 보증), Saga(보상 트랜잭션, 1987년생), 이벤트 기반(결합의 역전), CQRS(뿌리는 CQS, 이벤트 소싱과는 독립).
- 진자의 반환: 마이크로서비스 프리미엄 → 모듈러 모놀리스 — 경계의 논리적 이득과 물리적 비용의 분리. 돌아온 자리는 출발점이 아니라 나선의 한 칸 위다.
다음 장 예고: 이 장에서 가장 조용히 지나간 문장이 가장 비싼 문장이다 — “서비스마다 자기 데이터베이스를 가진다.” 트랜잭션 경계와 아키텍처 경계가 충돌하는 곳, 실무 고통의 대부분이 발원하는 곳 — 데이터. 금융 도메인이라면 더더욱, 진짜 전장은 코드가 아니라 거기다.
원전
| 연도 | 문헌 | 이 장에서의 역할 |
|---|---|---|
| 1987 | Garcia-Molina & Salem, Sagas | 보상 트랜잭션의 원전 |
| 1988 | Meyer, Object-Oriented Software Construction | CQS — CQRS의 뿌리 |
| 1994 | Waldo 외, A Note on Distributed Computing | 원격≠로컬, 부분 실패 |
| 1994/1997 | Deutsch(7)+Gosling(+1), 8 Fallacies of Distributed Computing | 분산의 오답 노트 |
| 2000/2002 | Brewer 추측 / Gilbert & Lynch 증명, CAP | 분할 중 C vs A의 불가능성 정리 |
| 2008 | Vogels, Eventually Consistent (ACM Queue) | 결과적 일관성의 정식화 |
| 2010경 | Young, CQRS Documents | 읽기/쓰기 모델 분리 |
| 2012 | Brewer, CAP Twelve Years Later / Abadi, PACELC | ”2 of 3” 오해 교정 / 지연-일관성 상존 |
| 2014 | Fowler & Lewis, Microservices | 정의와 특징 |
| 2015 | Fowler, MicroservicePremium / MonolithFirst; Newman, Building Microservices | 프리미엄 경고, 실무 지침 |
| 2019~ | Shopify 엔지니어링 블로그, Spring Modulith 등 | 모듈러 모놀리스 실증·도구 |