요약
모듈러 모놀리스는 “하나로 배포하되 내부 경계는 분명히 나누는” 아키텍처입니다. 요즘처럼 1인 개발자나 작은 팀이 AI와 함께 기능을 빠르게 만들 때, MSA의 운영 복잡도를 먼저 떠안기보다 모듈러 모놀리스로 시작하는 선택이 꽤 현실적입니다.
저도 회사를 운영하는 입장, 그리고 개발자로서 AI와 같이 일하는 시간이 늘었습니다. 예전에는 “나중에 커질 수 있으니 MSA로 가야 하나?”를 먼저 고민했다면, 요즘은 “AI가 이해할 수 있는 작은 모듈 경계를 만들고, 배포는 단순하게 가져가는 게 더 빠르지 않나?”를 먼저 생각하게 됩니다. 이 글은 모놀리스, 모듈러 모놀리스, MSA를 비교하고 실제 선택 기준을 정리한 글입니다.
목차
이 글에서 다루는 내용
배경
한동안 MSA는 “큰 서비스의 정답”처럼 이야기됐습니다. 기능별로 서비스를 나누고, 각 서비스가 독립 배포되고, 장애도 격리되고, 팀도 독립적으로 움직인다. 말만 들으면 좋아 보입니다. 실제로 대규모 조직과 트래픽이 큰 서비스에서는 큰 장점이 있습니다.
그런데 작은 팀에서는 이야기가 달라집니다. 서비스가 5개만 되어도 인증, 네트워크, 배포 파이프라인, 로그, 트레이싱, 장애 대응, 데이터 정합성 문제가 같이 따라옵니다. 기능 개발보다 “서비스 사이를 연결하는 일”이 더 커지는 순간이 옵니다. 여기서 자주 막힙니다.
모듈러 모놀리스는 이 지점에서 다시 주목받는 선택입니다. 옛날식 “아무거나 한 프로젝트에 다 넣은 모놀리스”로 돌아가자는 뜻이 아닙니다. 도메인 경계는 MSA처럼 신경 쓰되, 배포와 운영은 단순하게 유지하자는 접근에 가깝습니다.
세 가지 아키텍처 한 번에 정리
일반 모놀리스
모놀리스는 하나의 코드베이스, 하나의 배포 단위, 하나의 런타임으로 동작하는 구조입니다. 처음 만들 때는 가장 빠릅니다. 로컬 실행도 쉽고, 트랜잭션도 단순하고, 디버깅도 한 프로세스 안에서 끝납니다.
문제는 시간이 지나면서 경계가 흐려질 때 생깁니다. 주문 코드가 회원 테이블을 직접 만지고, 결제 코드가 배송 내부 구현을 알고, 테스트 없이 여기저기 의존성이 엉키면 “하나로 배포하는 구조”가 아니라 “하나라서 고치기 무서운 구조”가 됩니다.
모듈러 모놀리스
모듈러 모놀리스도 배포 단위는 하나입니다. 대신 내부를 도메인별 모듈로 나누고, 모듈 간 접근 규칙을 정합니다. 예를 들어 member, order, payment, notification 모듈이 있다면 각 모듈은 자기 데이터와 비즈니스 규칙을 소유하고, 다른 모듈은 공개 API나 이벤트를 통해서만 접근하게 만듭니다.
src/
member/
application/
domain/
infrastructure/
order/
application/
domain/
infrastructure/
payment/
application/
domain/
infrastructure/
shared-kernel/ # 정말 공통인 것만 작게 유지
이러면 정상입니다. 로컬에서는 하나의 애플리케이션처럼 실행되지만, 코드를 읽을 때는 “어느 도메인의 책임인가?”가 보입니다.
MSA / Microservices
MSA는 기능 또는 도메인 경계별로 서비스를 분리하고, 각 서비스가 독립 배포됩니다. 각 서비스는 자기 데이터 저장소를 소유하는 방향이 일반적이며, 서비스 간 통신은 HTTP, gRPC, 메시지 브로커, 이벤트 등을 사용합니다.
장점은 분명합니다. 특정 서비스만 확장하거나, 팀별로 배포 주기를 다르게 가져가거나, 장애 범위를 줄일 수 있습니다. 대신 분산 시스템 운영 능력이 필요합니다. 네트워크 실패, 재시도, 타임아웃, 멱등성, 관측성, 분산 트랜잭션 문제가 본문으로 들어옵니다.
Monolith vs Modular Monolith vs MSA 비교
| 기준 | Monolith | Modular Monolith | MSA / Microservices |
|---|---|---|---|
| 배포 단위 | 하나 | 하나 | 여러 서비스 |
| 내부 경계 | 약해지기 쉬움 | 도메인 모듈 경계를 의도적으로 강제 | 서비스 경계로 강제 |
| 초기 개발 속도 | 빠름 | 빠르지만 설계 규칙이 필요 | 느려지기 쉬움 |
| 배포/운영 복잡도 | 낮음 | 낮음~중간 | 높음 |
| 데이터 정합성 | 단일 DB 트랜잭션으로 단순 | 모듈별 소유권을 지키면 비교적 단순 | 분산 트랜잭션/최종 일관성 고민 필요 |
| 장애 격리 | 약함 | 프로세스 격리는 약하지만 코드 경계는 가능 | 상대적으로 강함 |
| 팀 구조 | 작은 팀에 적합 | 1인~소규모 팀, 도메인별 책임 분리에 적합 | 도메인별 팀이 나뉜 조직에 적합 |
| AI와 협업 | 컨텍스트가 커지면 어려움 | 모듈 단위로 컨텍스트를 잘라 주기 좋음 | 서비스별 컨텍스트는 작지만 운영 지식이 많이 필요 |
| 나중에 확장 | 경계가 없으면 분리 비용 큼 | 경계가 잘 잡혀 있으면 서비스 추출이 쉬움 | 이미 분리되어 있지만 변경 비용은 분산 시스템 비용으로 이동 |
1인 개발 + AI에 왜 모듈러 모놀리스가 잘 맞나
AI에게 줄 컨텍스트를 모듈 단위로 줄일 수 있다
AI와 개발할 때 가장 큰 차이는 “한 번에 보여줄 코드의 범위”입니다. 프로젝트 전체가 뒤엉켜 있으면 AI에게도 설명이 길어집니다. 반대로 모듈 경계가 분명하면 이렇게 말할 수 있습니다.
이번 작업은 order 모듈 안에서만 처리해줘.
payment 모듈은 공개 인터페이스만 호출하고 내부 구현은 수정하지 마.
이 지시가 통하려면 코드 구조가 실제로 그렇게 되어 있어야 합니다. 모듈러 모놀리스는 AI에게도 사람에게도 “여기까지만 만져라”는 경계를 만들어 줍니다.
배포는 단순해야 개발 속도가 유지된다
1인 개발에서 서비스가 여러 개로 나뉘면 배포 파이프라인, 환경 변수, 로그 위치, 장애 지점이 함께 늘어납니다. AI가 코드를 빨리 써줘도 운영자가 한 명이면 병목은 운영에서 생깁니다. 이 경우에는 독립 배포의 장점보다 단일 배포의 단순함이 더 큽니다.
테스트 경계가 생긴다
모듈별 테스트를 만들면 AI가 수정한 코드가 다른 도메인을 깨뜨렸는지 빠르게 확인할 수 있습니다. 최소한 아래 수준은 갖추는 편이 좋습니다.
module unit test → module integration test → application smoke test
이러면 정상입니다. MSA처럼 서비스별 배포는 하지 않더라도, 변경 검증은 모듈 단위로 할 수 있습니다.
실제로 설계할 때 보는 기준
1. 도메인 경계부터 잡는다
폴더를 예쁘게 나누는 것보다 먼저 “어떤 비즈니스 책임을 누가 소유하는가?”를 정해야 합니다. 회원, 주문, 결제, 정산, 알림은 서로 다른 이유로 변경됩니다. 변경 이유가 다르면 모듈 후보입니다.
2. 모듈 간 호출 규칙을 만든다
가장 흔한 실패는 모듈을 나눠 놓고 내부 클래스를 서로 직접 import하는 것입니다. 처음에는 편하지만 금방 경계가 무너집니다. 모듈 외부에는 application service, facade, event 같은 공개 지점만 열어 두는 편이 안전합니다.
3. DB는 하나로 시작하되 소유권은 나눈다
모듈러 모놀리스에서 꼭 DB를 여러 개로 나눌 필요는 없습니다. 하지만 테이블 소유권은 나눠야 합니다. order 모듈이 payment 테이블을 마음대로 update하기 시작하면 나중에 서비스 추출은 거의 다시 설계에 가까워집니다.
4. 공유 모듈을 작게 유지한다
common, shared, util이 커지는 순간 모든 모듈이 다시 하나로 묶입니다. 공통 모듈에는 날짜 유틸, 공통 타입처럼 정말 안정적인 것만 둡니다. 비즈니스 로직은 각 도메인에 남기는 게 좋습니다.
개발과 운영에서 달라지는 부분
관측성은 MSA처럼 생각한다
단일 애플리케이션이라고 로그를 한 덩어리로 보면 나중에 찾기 어렵습니다. 로그에는 모듈명, 요청 ID, 주요 도메인 ID를 남기는 편이 좋습니다.
module=order request_id=... order_id=... event=OrderCreated
이 습관이 있으면 나중에 일부 모듈을 서비스로 분리해도 추적 방식이 크게 바뀌지 않습니다.
배포는 하나, 릴리스 관리는 작게
배포 단위가 하나라고 모든 기능을 한 번에 크게 묶을 필요는 없습니다. feature flag, 작은 PR, 모듈별 변경 로그를 사용하면 단일 배포에서도 안전하게 움직일 수 있습니다.
성능 병목은 먼저 측정한다
“언젠가 결제만 따로 확장해야 할 것 같아서”라는 이유만으로 서비스를 분리하면 대부분 복잡도만 늘어납니다. 실제 병목이 어디인지, 독립 확장이 정말 필요한지, 캐시나 쿼리 개선으로 해결되는지 먼저 봐야 합니다.
모듈러 모놀리스에서 MSA로 넘어가는 전략
좋은 전환 전략은 “처음부터 MSA로 만들기”가 아니라 “나중에 뽑아낼 수 있게 만든 뒤, 필요가 생기면 뽑기”입니다.
1단계: 모듈 경계를 코드에서 먼저 강제
패키지 의존성 검사, ArchUnit 같은 아키텍처 테스트, Spring Modulith 같은 도구를 사용해 모듈 간 접근 규칙을 테스트로 걸 수 있습니다. 핵심은 사람이 기억하는 규칙이 아니라 CI가 막아주는 규칙이어야 한다는 점입니다.
2단계: 데이터 소유권을 정리
서비스로 분리할 후보 모듈은 자기 테이블을 소유해야 합니다. 다른 모듈이 직접 SQL로 건드리는 부분을 줄이고, 공개 API나 이벤트로 우회시킵니다.
3단계: 이벤트를 내부에서 먼저 사용
처음부터 Kafka나 복잡한 메시징을 붙이라는 뜻은 아닙니다. 내부 도메인 이벤트로 “주문 생성됨”, “결제 완료됨” 같은 사실을 표현해 두면, 나중에 외부 메시지 브로커로 옮길 때 사고방식이 자연스럽습니다.
4단계: 정말 필요한 모듈만 서비스로 추출
추출 기준은 명확해야 합니다. 독립 확장, 독립 배포, 장애 격리, 팀 분리 중 하나라도 강한 이유가 있어야 합니다. 이유가 약하면 모듈러 모놀리스에 남겨 두는 편이 낫습니다.
실제로 헷갈리는 부분과 주의점
사례 1. “폴더만 나누면 모듈러 모놀리스 아닌가요?”
아닙니다. 폴더 구조는 시작일 뿐입니다. 다른 모듈의 repository를 직접 호출하거나 테이블을 마음대로 수정하면 사실상 일반 모놀리스입니다. 확인할 것은 폴더 이름이 아니라 의존성 방향입니다.
사례 2. “MSA가 더 현대적이니까 무조건 낫지 않나요?”
MSA는 현대적인 선택일 수 있지만 공짜가 아닙니다. 서비스 디스커버리, API gateway, 관측성, 장애 대응, 데이터 일관성, 배포 자동화가 따라옵니다. 이 역량이 없으면 기능 개발 속도보다 운영 복잡도가 더 빨리 커집니다.
사례 3. “DB 하나를 쓰면 나중에 분리 못 하나요?”
DB가 하나인 것보다 더 위험한 것은 소유권이 없는 것입니다. 같은 DB를 쓰더라도 모듈별 테이블 소유권과 접근 규칙이 있으면 분리 가능성이 남습니다. 반대로 서비스는 나뉘었는데 여러 서비스가 같은 테이블을 같이 쓰면 그것도 분산 모놀리스가 됩니다.
사례 4. “AI가 다 해주면 아키텍처가 덜 중요하지 않나요?”
오히려 더 중요해집니다. AI는 주어진 경계 안에서는 빠르게 움직이지만, 경계가 없으면 넓은 범위를 한 번에 고치려 합니다. 모듈 경계, 테스트, 공개 인터페이스는 AI와 협업할 때 안전장치가 됩니다.
어떤 경우에 무엇을 선택할까
일반 모놀리스가 맞는 경우
프로토타입, 관리자 도구, 수명이 짧은 내부 서비스, 도메인이 단순한 CRUD 서비스라면 일반 모놀리스로도 충분합니다. 다만 오래 갈 가능성이 보이면 초기에 최소한의 모듈 경계는 만들어 두는 것이 좋습니다.
모듈러 모놀리스가 맞는 경우
1인 개발, AI와 함께 빠르게 만드는 SaaS, 작은 스타트업 팀, 도메인은 조금 복잡하지만 아직 서비스별 팀은 없는 상황에 잘 맞습니다. 제가 요즘 가장 현실적으로 보는 기본값도 여기에 가깝습니다.
MSA가 맞는 경우
서비스별 팀이 나뉘어 있고, 독립 배포가 실제로 필요하고, 특정 도메인의 트래픽/장애/보안 요구가 뚜렷하며, 분산 시스템을 운영할 역량이 있다면 MSA가 맞습니다. 이때는 아키텍처가 조직 구조와 맞아야 합니다. Conway’s Law를 무시하고 작은 한 팀이 서비스만 여러 개 운영하면 이점보다 비용이 커질 가능성이 높습니다.
결론
제 결론은 단순합니다. 1인 개발 + AI, 또는 작은 팀의 기본 선택지는 모듈러 모놀리스가 가장 현실적입니다. 단일 배포로 속도를 유지하면서도, 모듈 경계로 AI와 사람이 함께 이해할 수 있는 구조를 만들 수 있기 때문입니다.
다만 “무조건 모듈러 모놀리스”는 아닙니다. 조직이 커지고, 팀이 도메인별로 나뉘고, 독립 배포와 장애 격리의 가치가 운영 비용보다 커지는 순간에는 MSA로 갈 수 있어야 합니다. 그래서 처음부터 중요한 것은 서비스 개수가 아니라 경계입니다.
여러분은 어떻게 생각하시나요? AI와 함께 개발하는 시대에, 1인 개발자와 작은 팀의 기본 아키텍처는 모듈러 모놀리스가 맞다고 보시나요?
참고 자료
- Microsoft Azure Architecture Center – Microservices architecture style: https://learn.microsoft.com/en-us/azure/architecture/guide/architecture-styles/microservices
- Microsoft Azure Architecture Center – Use domain analysis to model microservices: https://learn.microsoft.com/en-us/azure/architecture/microservices/model/domain-analysis
- AWS Prescriptive Guidance – Decomposing monoliths into microservices: https://docs.aws.amazon.com/prescriptive-guidance/latest/modernization-decomposing-monoliths/welcome.html
- Martin Fowler – Microservices: https://martinfowler.com/articles/microservices.html
- Martin Fowler – Monolith First: https://martinfowler.com/bliki/MonolithFirst.html
- Spring Modulith Reference Documentation: https://docs.spring.io/spring-modulith/reference/
- Thoughtworks – When (modular) monolith is the better way to build software: https://www.thoughtworks.com/insights/blog/microservices/modular-monolith-better-way-build-software
- Team Topologies – Key concepts: https://teamtopologies.com/key-concepts
답글 남기기