Twilio Segment가 마이크로서비스에서 모놀리식 아키텍처로 회귀한 이유
(twilio.com)- Twilio Segment는 수백 개의 마이크로서비스 구조를 운영하다가 복잡성과 유지보수 부담으로 인해 단일 서비스(모놀리식) 로 전환함
- 초기에는 각 데스티네이션 API 를 분리해 장애 격리와 확장성을 확보했으나, 서비스 수가 140개 이상으로 늘며 운영 오버헤드가 급증함
- 다수의 레포지토리와 공유 라이브러리 관리가 어려워지고, 테스트·배포 시 전체 서비스에 영향을 주는 문제가 발생함
- 이를 해결하기 위해 Centrifuge 시스템과 모노레포 구조를 도입하고, 테스트 자동화를 위해 Traffic Recorder를 구축함
- 결과적으로 개발 속도와 안정성이 크게 향상되었으며, Twilio Segment는 생산성과 운영 효율성을 위해 모놀리식 구조를 유지 중임
마이크로서비스 도입과 한계
- Twilio Segment는 고객 데이터 인프라를 위해 마이크로서비스 아키텍처를 채택, 각 목적별 서비스가 독립적으로 이벤트를 처리하도록 설계
- 수백 개의 서버사이드 데스티네이션(예: Google Analytics, Optimizely 등)에 데이터를 전달
- 초기에는 단일 큐를 사용했으나, 특정 데스티네이션 장애 시 전체 지연이 발생하는 헤드 오브 라인 블로킹 문제가 발생
- 이를 해결하기 위해 각 데스티네이션별 별도 서비스와 큐를 구성, 장애 격리와 독립적 확장을 달성
- 그러나 서비스 수가 늘면서 운영 복잡도와 유지보수 비용이 급격히 증가, 개발 속도 저하와 결함률 상승으로 이어짐
개별 레포지토리와 공유 라이브러리의 문제
- 각 데스티네이션은 서로 다른 API 포맷을 사용해 커스텀 변환 코드가 필요
- 초기에는 단일 레포에서 관리했으나, 테스트 실패가 전체에 영향을 주어 레포 분리를 단행
- 이후 50개 이상의 신규 데스티네이션 추가로 50개 이상의 레포지토리가 생김
- 공통 기능을 위해 공유 라이브러리를 도입했으나, 버전 불일치와 배포 부담이 커짐
- 서비스별 부하 패턴이 달라 자동 확장 설정이 어려웠고, 운영자가 수동으로 조정해야 하는 경우도 발생
모놀리식 전환과 Centrifuge 도입
- 140개 이상의 서비스를 단일 서비스로 통합하기로 결정
- 개별 큐를 대체하기 위해 Centrifuge 시스템을 개발, 모든 이벤트를 단일 서비스로 전달
- Centrifuge는 이후 Twilio Segment의 Connections 백엔드 인프라로 발전
- 단일 서비스 구조로 전환하면서 운영 부담 감소와 장애 대응 단순화를 달성
모노레포와 테스트 자동화
- 모든 데스티네이션 코드를 하나의 레포지토리로 병합, 120개 이상의 의존성을 단일 버전으로 통일
- 버전 관리 단순화와 유지보수 효율성 향상
- 테스트 자동화를 위해 Traffic Recorder를 도입
- 실제 HTTP 요청·응답을 기록 후 재생하여 외부 네트워크 의존성을 제거
- 테스트 속도가 수 분에서 밀리초 단위로 단축, 안정성 대폭 향상
- 테스트 실패율이 낮아지고, 개발자 생산성이 크게 개선됨
모놀리식 구조의 효과와 트레이드오프
- 단일 서비스로 통합 후 배포 속도와 개발 효율이 크게 향상
- 1년간 공유 라이브러리 개선 횟수가 32건에서 46건으로 증가
- 단일 엔지니어가 수 분 내 배포 가능
- 운영 효율성도 개선되어, 부하 급증 시에도 대규모 워커 풀로 흡수 가능
- 그러나 결함 격리 어려움, 캐시 효율 저하, 의존성 업데이트 리스크 등의 단점 존재
- 일부 손실은 운영 단순성과 생산성 향상으로 상쇄
결론
- 마이크로서비스는 초기 성능 문제를 해결했지만, 대규모 확장과 일괄 업데이트에는 부적합
- 모놀리식 전환으로 운영 안정성과 개발 속도를 모두 개선
- 성공적 전환을 위해 견고한 테스트 체계와 트레이드오프 수용이 필수
- Twilio Segment는 일부 인프라에는 여전히 마이크로서비스를 유지하지만, 서버사이드 데스티네이션에는 모놀리식이 더 적합한 구조로 평가됨
Hacker News 의견들
-
모든 목적지의 코드를 하나의 repo에 모으자, 단일 서비스로 병합할 수 있었음
그 결과 개발 생산성이 크게 향상되었음. 이제는 공유 라이브러리 하나를 수정할 때마다 140개 이상의 서비스를 배포할 필요가 없음
한 명의 엔지니어가 몇 분 만에 배포할 수 있게 되었음
만약 라이브러리 변경 때문에 모든 서비스를 다시 배포해야 한다면, 그것은 진정한 서비스가 아니라 분산 모놀리식 구조임
공유 라이브러리를 전체 서비스에 강제로 동기화해야 한다는 개념 자체가 서비스 아키텍처의 철학과 맞지 않음- 네 말이 일리는 있지만, 실제로는 훨씬 더 복잡한 상황임
이건 “모든 라이브러리 업데이트마다 전체 재배포”라기보다는 Amazon식 공유 빌드·배포 시스템에 가까움
중앙에서 관리되는 단일 소스에서 라이브러리를 가져오는데, 버전이 다르면 호환성 문제로 모두 마이그레이션해야 함
보안 취약점으로 특정 버전을 제거해야 할 때는 전체 재배포가 필요하지만, 중앙화된 관리의 이점이 훨씬 큼
이런 시스템은 여전히 마이크로서비스로 분류되지만, 비용과 운영 효율 측면에서 공유 환경처럼 동작함
이걸 분산 모놀리식이라 부르는 건 과도한 해석임 - 말은 쉽지만, 실제로는 서비스 간 미묘한 버그나 비호환성을 유발하기 쉬움
마이크로서비스 패턴을 따르다 보면 배포 리스크가 늘어나는데, 처음엔 잘 안 보임
예를 들어 돈 관련 라이브러리의 버그를 수정했다면, 현실적으로 모든 서비스를 재배포해야 할지 고민하게 됨 - 라이브러리를 전체적으로 업그레이드해야 한다고 해서 반드시 잘못된 결합이라고 볼 수는 없음
보안 취약점이 있는 라이브러리는 시스템 설계와 무관하게 전면 교체해야 함
이런 경우에는 오히려 모놀리식 구조가 더 다루기 쉬움 - 공유 라이브러리 개념 자체가 모든 서비스를 강하게 결합시킴
진짜 마이크로서비스라면 메시지를 주고받고, JSON을 사용해야 함
코드가 아니라 API만 알면 충분해야 함. 그래야 각자 독립적으로 배포하고 확장할 수 있음 - 그렇다면 140개 서비스 각각에 로깅 코드를 새로 써야 한다는 말인가?
공유 모듈을 활용하는 게 더 합리적이지 않음?
- 네 말이 일리는 있지만, 실제로는 훨씬 더 복잡한 상황임
-
이전 회사에서는 모든 걸 마이크로서비스로 운영했고, 그 전 회사는 AWS 서버리스였음
두 경우 모두 서비스 간 통신이 가장 큰 문제였음. 계약(contracts)을 동기화하기 어렵고, 배포도 복잡했음
초기에는 빠르게 움직였지만, 시간이 지나면서 복잡성이 폭발했음. 공포 기반 개발이 일어났고, 회의가 너무 많았음
지금 회사는 모놀리식 구조인데 훨씬 다루기 쉬움. 타입이 명확하고, 리팩터링이 간단함
자체 플랫폼 위에 구축된 AI 에이전트들이 코드베이스 안에서 스스로 개선되는 걸 보는 게 흥미로움
단점은 빌드 시간이 길다는 점뿐이지만, 도구 체인의 발전으로 2026년에는 10배 빠른 배포를 기대함
내 결론은, 모놀리식 구조 덕분에 훨씬 빠르게 성장하고 확장할 수 있었음- 내 경험은 정반대임. SendGrid에서 10년간 일하며 12명에서 500명 규모로 확장했는데, 서비스 아키텍처 덕분에 가능했음
모놀리식 구조에서는 항상 관심사 분리가 깨지고, 팀 간 결합이 심했음
진짜 속도와 확장은 팀이 분리되어 있을 때만 가능했음
ORM에서 DTO로 전환하는 데 2년, 50개 팀, 150명 이상이 필요했음
이런 복잡한 작업은 마이크로서비스가 아니었다면 불가능했음
- 내 경험은 정반대임. SendGrid에서 10년간 일하며 12명에서 500명 규모로 확장했는데, 서비스 아키텍처 덕분에 가능했음
-
이 글을 보면, 문제의 핵심은 마이크로서비스 vs 모놀리식의 기술적 선택이 아니라
엔지니어링 조직의 품질과 구조에 있음
코드 저장소와 테스트 구조가 조직의 수준을 그대로 드러냄- 많은 팀이 기술적 규율이 부족함
“그건 하지 말자”고 말할 수 있는 사람이 없으면 복잡성이 폭발함
권한 있는 리더가 있어야 팀이 멈추고 생각할 수 있음 - Twilio 프로젝트를 함께 했는데, 정말 엉망이었음
API 문제 발생 시 원인을 분석하지 않고, 데이터만 수정하고 티켓을 닫음
같은 문제가 반복돼도 근본 원인을 해결하지 않음 -
Conway의 법칙이 또 한 번 증명됨
인터뷰만 해도 회사의 코드베이스 구조를 어느 정도 예측할 수 있을 정도임
- 많은 팀이 기술적 규율이 부족함
-
이건 진짜 모놀리식 전환이라기보다는 여전히 SOA 구조임
단지 서비스의 범위가 커졌을 뿐임
140개 서비스를 한 팀이 관리한다면, SOA는 팀 확장을 위한 구조이지 서비스 확장을 위한 게 아님
한 팀이 모든 공유 라이브러리를 관리하면 버전 불일치와 API 혼란이 생김
결국 조직 구조가 아키텍처를 결정함. 한 팀이 복잡성을 줄이기 위해 통합한 것임
이건 “모놀리식”이 아니라 팀 단위로 적절히 범위가 조정된 서비스 수준임
이런 구조가 가장 이상적이라 생각함. 팀이 커지면 다시 분리해야 함 -
나는 마이크로서비스 지지자는 아니지만, “모노레포 vs 마이크로서비스”의 가짜 이분법이 눈에 띔
너무 많은 도구가 서비스와 repo를 1:1로 가정함
하지만 모든 걸 한 repo에 두고도 독립적으로 배포할 수 있음
GitHub 같은 곳에서 폴더 단위로 독립된 서비스로 다룰 수 있으면 좋겠음- 이전 회사에서 이걸 직접 구현했음
Bazel로 의존성 트리를 관리하고,bazel query로 영향받는 타깃을 찾아 테스트를 자동 실행했음
GitHub Actions와 연동해 PR을 차단하는 워크플로를 만들었음
잘 작동했지만, 구축에 몇 달이 걸렸음 - “마이크로서비스에서 모놀리식으로 바꾸니 문제가 사라졌다”는 말이 거슬림
실제 문제는 운영과 도구 부족 때문이었음 — CI, 오토스케일링, 온콜 체계 모두 미흡했음
- 이전 회사에서 이걸 직접 구현했음
-
두 접근 모두 실패할 수 있음
Node.js나 Python 같은 환경에서는 이벤트 루프가 처리할 수 있는 코드량에 한계가 있음
6~8명이 200개 서비스를 관리한 적도 있고, 80명이 하나의 모놀리스를 관리한 적도 있음
마이크로서비스는 작은 변경엔 유리하지만, 전체 변경은 어렵고
모놀리식은 그 반대임
결국 중요한 건 아키텍처가 아니라 추상화와 테스트, 디커플링 방식임- “하나의 비즈니스 문제를 해결하는 소프트웨어라면, 하나의 마이크로서비스로 묶는 게 맞음”
마이크로의 기준은 기술이 아니라 비즈니스 단위임
그 이하로 쪼개면 나노서비스가 됨 - 이런 합리화는 결국 런타임의 한계를 덮는 임시방편처럼 느껴짐
Beam, JVM, Rust, Go 같은 환경에서는 이미 해결된 문제임 - 이벤트 루프가 처리할 수 있는 코드량의 한계가 구체적으로 어떤 단위인지 궁금함
CPU 캐시 문제인가? - 대규모 환경에서도 정말 Node.js나 Python을 쓰는지 의문임
보통은 Go, Java, C#을 쓰는 줄 알았음
- “하나의 비즈니스 문제를 해결하는 소프트웨어라면, 하나의 마이크로서비스로 묶는 게 맞음”
-
대부분의 회사에서 마이크로서비스는 오히려 문제의 90% 원인이었음
AWS, Google, Netflix 같은 거대 조직이 아니면 맞지 않음
시스템을 조립 가능한 단위로 나누는 것 자체가 이미 어려운데, 거기에 네트워크 경계를 추가하는 건 어리석음
다음 트렌드는 React나 SPA에서 벗어나 서버 중심 전환으로 갈 것 같음 -
마이크로서비스로 전환한 이유가 “테스트가 자주 깨져서”라니, 너무 거꾸로 된 접근처럼 보임
테스트가 깨진다고 코드베이스 구조를 완전히 바꾸는 건 이상함- 우리도 비슷한 문제를 겪었지만, 각 팀에 독립된 개발 환경을 주는 방식으로 해결했음
팀별로 VM과 CI/CD 설정을 분리하니, 테스트 충돌이 사라졌음
단점은 기능 간 충돌을 즉시 발견하지 못한다는 점이지만, 코드 소유권이 명확해 큰 문제는 없었음
- 우리도 비슷한 문제를 겪었지만, 각 팀에 독립된 개발 환경을 주는 방식으로 해결했음
-
제목에 [2018]을 추가해달라는 요청이 있었음
- 혹시 지금은 다시 마이크로서비스로 회귀했는지 궁금함
- 7년 전 글을 다시 끌어올리는 건 좀 그렇다고 생각함. 기술 세계에서는 이미 고대사임
-
“테스트가 깨지면 관련 없는 코드도 수정해야 했다”는 이유로 repo를 분리했다는데,
테스트 실행 방식을 바꾸거나 수동 배포를 허용하는 등 다른 해결책도 있었을 것 같음
repo 분리가 유일한 해법은 아니었음