본문 바로가기
클라우드/쿠버네티스

🚀 마이크로서비스로 가는 가장 안전한 징검다리: 왜 '모듈러 모놀리스'를 거쳐야 할까?

by gasbugs 2025. 12. 20.
"우리는 넷플릭스처럼 마이크로서비스를 해야 해!"

 

개발자라면 한 번쯤 들어봤거나, 혹은 직접 외쳐봤을 문장입니다. 스타트업부터 대기업까지, 거대한 '레거시(Legacy)'를 청산하고 민첩한 조직이 되기 위해 마이크로서비스 아키텍처(MSA) 전환을 꿈꿉니다. 하지만 통계에 따르면, MSA 전환 프로젝트의 상당수가 실패하거나 오히려 이전보다 더 복잡한 '분산된 진흙 덩어리(Distributed Big Ball of Mud)'를 만드는 것으로 끝납니다.

왜 그럴까요? 바로 '준비되지 않은 분리' 때문입니다.

 

오늘은 MSA로 넘어가기 전, 반드시 거쳐야 할 필수 코스이자, 그 자체로도 훌륭한 아키텍처인 '모듈러 모놀리스(Modular Monolith)'에 대해 깊이 있게 이야기해 보려 합니다.


1. ⚠️ MSA의 환상과 현실: 네트워크는 공짜가 아니다

먼저 우리가 왜 MSA로 가려는지, 그리고 그 과정에서 무엇을 간과하는지 짚고 넘어가야 합니다.

MSA의 약속 (이상)

  • 민첩성: 서비스별로 독립적인 배포가 가능하다.
  • 확장성: 트래픽이 몰리는 서비스만 스케일 아웃(Scale-out) 할 수 있다.
  • 기술 자율성: A팀은 자바, B팀은 파이썬을 써도 된다.

MSA의 현실 (비용)

하지만 이 장점을 얻기 위해 지불해야 할 비용은 엄청납니다.

  • 네트워크 호출의 복잡성: 함수 호출(In-process)이 네트워크 호출(RPC/HTTP)로 바뀝니다. 실패 처리, 타임아웃, 재시도 로직이 필요해집니다.
  • 데이터 일관성 문제: DB가 쪼개지면서 트랜잭션 관리가 어려워집니다. 2PC나 Saga 패턴 같은 복잡한 기법이 강제됩니다.
  • 운영 오버헤드: 쿠버네티스, 서비스 메시, 분산 트레이싱 등 인프라 복잡도가 기하급수적으로 증가합니다.

가장 큰 문제는 '잘못 자른 경계'입니다.

서비스 간의 경계(Bounded Context)를 명확히 정의하지 못한 상태에서 물리적으로 서버를 떼어내면, 서비스 간에 API 호출이 난무하는 '스파게티 통신'이 발생합니다. 이는 모놀리스보다 성능은 느리고, 관리는 더 어려운 최악의 결과를 낳습니다.


2. 🧩 모듈러 모놀리스(Modular Monolith)란 무엇인가?

모듈러 모놀리스는 "배포 단위는 하나(Monolith)지만, 내부 구조는 마이크로서비스처럼 엄격하게 모듈화 된(Modular) 아키텍처"를 말합니다.

  • 물리적 통합: 하나의 JAR/WAR 파일, 하나의 바이너리로 배포됩니다.
  • 논리적 분리: 내부 패키지나 모듈은 철저하게 분리되어 있습니다. 모듈 간 참조는 엄격히 통제되며, 서로의 내부 데이터베이스 테이블에 직접 접근하는 것이 금지됩니다.

쉽게 말해, "한 지붕 아래 살지만, 방은 완벽하게 따로 쓰는 룸메이트"와 같습니다.


3. 💡 왜 '모듈러 모놀리스'를 먼저 해야 하는가? (핵심 이유 5가지)

이 글의 핵심입니다. 왜 바로 MSA로 가지 않고 이 단계를 거쳐야 할까요?

① 경계(Context)를 정의하는 비용이 저렴하다 🛠️

MSA의 핵심은 '어디를 자를 것인가'입니다. 하지만 도메인에 대한 이해가 부족한 초기 단계에서는 이 경계를 정확히 알기 어렵습니다.

  • MSA: 코드를 분리하여 별도 서버로 띄웠는데, 나중에 경계가 잘못되었다는 것을 깨닫습니다. 다시 합치거나 API를 뜯어고치는 비용은 엄청납니다.
  • 모듈러 모놀리스: 코드가 한 프로젝트 안에 있습니다. 패키지 구조를 바꾸거나 리팩토링 기능(Move Class)을 사용하는 것만으로 경계를 수정할 수 있습니다. 실수했을 때 되돌리는 비용이 거의 0에 가깝습니다.

② '분산 트랜잭션'의 악몽에서 벗어날 수 있다 💾

MSA의 가장 큰 골칫거리는 데이터 정합성입니다. 주문 서비스와 결제 서비스가 나뉘면, 하나의 트랜잭션으로 묶을 수 없습니다.

  • 모듈러 모놀리스: 논리적으로는 모듈이 나뉘어 있지만, 물리적으로는 하나의 DB를 사용할 수 있습니다(물론 스키마는 분리하는 것이 좋습니다). 필요하다면 강력한 RDBMS의 트랜잭션 기능을 그대로 사용할 수 있습니다. 비즈니스 로직이 안정화된 후, 나중에 DB를 찢어도 늦지 않습니다.

③ 리팩토링 도구의 지원을 100% 받는다 ⚡

IntelliJ나 Eclipse 같은 강력한 IDE는 단일 프로젝트 내에서의 코드 추적과 리팩토링에 최적화되어 있습니다.

  • "이 함수를 누가 호출하지?"라고 물었을 때, MSA 환경에서는 grep으로 코드를 뒤지거나 분산 트레이싱 툴을 봐야 합니다.
  • 모듈러 모놀리스에서는 Cmd + Click 한 번이면 모든 호출 관계가 파악됩니다. 이는 개발 생산성과 코드 품질 유지에 결정적인 차이를 만듭니다.

④ 인프라 복잡도 없이 '모듈성'만 챙긴다 🏗️

MSA를 하려면 Docker, k8s, Istio, CI/CD 파이프라인의 다각화 등 DevOps 엔지니어링 리소스가 많이 듭니다. 비즈니스 로직 짜기도 바쁜데 인프라까지 신경 써야 하죠.

모듈러 모놀리스는 배포 파이프라인이 단순합니다. 인프라 복잡도 없이 "좋은 아키텍처(높은 응집도, 낮은 결합도)"를 연습하고 구현하는 데 집중할 수 있게 해줍니다.

⑤ 성능 손실이 없다 (No Network Latency) 🚀

아무리 빠른 네트워크도 인메모리(In-memory) 함수 호출을 이길 수는 없습니다.

모듈러 모놀리스는 모듈 간 통신이 단순한 메서드 호출입니다. 직렬화/역직렬화(JSON Serialization) 오버헤드도 없고, 네트워크 타임아웃 걱정도 없습니다.


4. 📝 어떻게 전환해야 할까? (실천 가이드)

무작정 모듈러 모놀리스라고 우긴다고 되는 것이 아닙니다. 다음 원칙들을 지켜야 합니다.

Step 1. 도메인 단위로 패키지 격리하기

기존의 계층형 아키텍처(Layered Architecture: Controller, Service, DAO끼리 모아둔 구조)를 버려야 합니다. 대신 도메인(기능)별로 패키지를 묶으세요.

  • Bad: com.mycompany.controllers, com.mycompany.services
  • Good: com.mycompany.order, com.mycompany.payment, com.mycompany.user

Step 2. 의존성 규칙 강제하기 (ArchUnit 활용)

말로만 "참조하지 마"라고 하면 아무도 안 지킵니다. Java의 경우 ArchUnit 같은 도구를 사용해 테스트 코드로 아키텍처를 강제하세요.

"주문(Order) 모듈은 결제(Payment) 모듈을 직접 참조할 수 없다.
오직 공통 인터페이스(Event)를 통해서만 통신해야 한다."

Step 3. 데이터베이스 논리적 분리

가장 중요합니다. 다른 모듈의 테이블에 조인(Join)을 거는 순간 모듈화는 실패입니다.

  • 모듈별로 스키마(Schema)를 나누거나, 테이블명에 접두사를 붙여 관리하세요.
  • 다른 모듈의 데이터가 필요하면, 조인이 아니라 해당 모듈의 API(서비스 메서드)를 호출해서 가져와야 합니다.

Step 4. 내부 통신은 인터페이스로

모듈 간 통신은 철저하게 공개된 인터페이스(Public Interface)를 통해서만 이루어져야 합니다. 구현체(Implementation)는 패키지 프라이빗(Package-private)으로 숨겨야 합니다.


5. 🎓 결론: 모듈러 모놀리스는 '목적지'일 수도 있다

쇼피파이(Shopify)와 스택오버플로우(Stack Overflow) 같은 거대 테크 기업들도 MSA에서 다시 모듈러 모놀리스로 회귀하거나, 이를 핵심 아키텍처로 유지하고 있습니다.

모듈러 모놀리스는 단순히 MSA로 가기 위한 '과도기적 징검다리'일 뿐만 아니라, 많은 조직에게 '가장 현실적이고 효율적인 최종 목적지'일 수 있습니다.

기억하세요.

"하나의 프로세스 안에서도 모듈을 깔끔하게 분리하지 못한다면,
마이크로서비스로 쪼개는 순간 지옥(Distributed Hell)이 펼쳐질 것이다."

지금 당장 k8s 클러스터를 구축하는 것보다, 우리 코드의 import 문을 정리하고 도메인 경계를 긋는 것부터 시작해 보는 건 어떨까요? 그것이 마이크로서비스로 가는 가장 빠른 지름길입니다.