모놀리스 vs MSA — 언제 무엇을, 그리고 MSA를 지탱하는 것들
"스타트업 초기 멤버로 합류했다면 모놀리스와 MSA 중 무엇을 고르겠습니까?" 면접에서 흔히 나오는 질문입니다. "대세는 MSA죠", "넷플릭스도 쓰니까요" 같은 답은 절반만 맞습니다. 선택 기준과 MSA를 지탱하는 요소들을 정리합니다. (이런 구조를 다이어그램으로 표현하는 법은 C4 모델 참고.)
핵심 명제 하나 — 기술은 비용입니다. MSA는 공짜로 주어지지 않고, 복잡성이라는 값을 치러야 합니다.모놀리스 vs MSA — 무엇이 다른가
- 모놀리스(Monolith) — "거대한 덩어리". 하나의 애플리케이션 안에 비즈니스 로직과 DB가 모두 들어갑니다. 쇼핑몰이면 인증·주문·장바구니·결제가 한 애플리케이션에.
- 마이크로서비스(MSA) — 기능별로 나누고 DB까지 독립시킵니다. 유저·주문·결제 서비스가 각자 DB를 갖고, 앞단에 API 게이트웨이를 둡니다. 핵심 3속성은 독립 배포 · 장애 격리 · 독립 확장입니다.
맥가이버 칼에 빗대면 이해가 쉽습니다. 모놀리스는 멀티툴 — 하나만 챙기면 되니 간편하지만, 가위 하나가 고장 나도 칼 전체를 수리해야 합니다. MSA는 개별 전문 공구 — 망치만 고장 나면 망치만 교체하지만, 공구를 따로 들고 다녀야 해 번거롭습니다.
| 관점 | 모놀리스 | MSA |
|---|---|---|
| 배포 | 단순(하나만 배포) | 복잡(단, CD로 자동화) |
| 확장 | 전체를 통째로 → 비효율 | 특정 서비스만 독립 확장 |
| 장애 격리 | 하나가 멈추면 전체 영향 | 다른 서비스로 전파 안 됨 |
| 기술 복잡도 | 낮음(구조가 단순) | 높음(특히 트랜잭션) |
확장성이 MSA의 큰 장점입니다. 주문에만 트래픽이 몰리면 MSA는 주문 서비스만 스케일 아웃하지만, 모놀리스는 유저·결제까지 통째로 확장해야 합니다.
MSA의 대가 — 복잡성
여기서 복잡성은 내부 로직이 아니라 아키텍처 구조의 복잡성입니다.
- 네트워크 분리 — API가 나뉘면 서비스 간 통신이 네트워크를 타고, 그만큼 장애 지점이 늘어납니다.
- 분산 트랜잭션 — 하나의 DB면 주문+결제를 한 트랜잭션으로 묶기 쉽지만, DB가 분리되면 어렵습니다(그래서 Saga·Outbox 같은 패턴이 필요 — 메시지 큐 비교 참고).
- 관측성 — 여러 서비스를 거치니 로그 추적이 복잡해집니다.
MSA는 최신 기술이라기보다 조직 구조 관점의 아키텍처입니다. 콘웨이 법칙은 "시스템 구조는 그것을 만드는 조직의 소통 구조를 닮는다"고 말합니다. 팀이 하나면 시스템도 하나, 팀이 기능별로 나뉘면 시스템도 그렇게 나뉩니다. 넷플릭스·아마존도 필요해진 뒤에 MSA를 택했습니다. 조직이 준비되지 않으면 MSA도 실패합니다.
언제 무엇을 — 모놀리스로 시작해 모듈러 모놀리스를 거쳐
- 모놀리스가 맞는 경우 — 초기 스타트업, 작은 팀, 도메인이 아직 불확실할 때. 초기의 적은 확장성 부족이 아니라 과도한 복잡성입니다.
- MSA를 고려할 때 — 서비스가 충분히 크고, 조직이 기능 단위로 나뉘고, 배포 병목·장애 전파가 문제가 될 때.
그 사이 단계가 모듈러 모놀리스(Modular Monolith) 입니다. 단일 배포 단위는 유지하되, 내부를 도메인 경계로 명확히 분리해 두는 구조입니다. 과거 모놀리스가 경계 의식이 약했다면, 모듈러 모놀리스는 경계를 코드로 강제합니다. 그러면 나중에 팀이 커지고 서비스가 분화될 때 MSA로 자연스럽게 떼어낼 수 있습니다.
전환은 ① 도메인 경계 식별·의존성 정리 → ② 경계를 코드로 강제(인터페이스 명확화) → ③ 필요 시 서비스 분리 순입니다. 도구로는 Java의 Spring Modulith, .NET의 ABP, Node의 NestJS 등이 있습니다. 중요한 건, 모듈러 모놀리스가 최종 아키텍처일 수도 있다는 점입니다 — MSA의 복잡성을 감당할 만큼 성장했을 때만 넘어가면 됩니다.
모놀리스는 못해서 쓰는 게 아니라 의도적으로 고르는 구조입니다 — MSA는 성숙한 조직이 감당하는 구조고요.MSA를 지탱하는 세 요소
MSA로 가면 반드시 따라오는 구성 요소가 있습니다.
API 게이트웨이 — 단일 진입점
게이트웨이가 없으면 클라이언트가 모든 API 주소를 알아야 하고, 모든 API가 외부에 노출되며, 인증 같은 공통 기능을 API마다 반영해야 합니다. 게이트웨이는 호텔 프런트 데스크처럼 단일 진입점이 되어 라우팅 · 인증/인가 · 프로토콜 변환 · 레이트 리미팅 · 관측성을 담당합니다.
다만 게이트웨이는 교통경찰이지 요리사가 아닙니다. 비즈니스 DB에 직접 접근하거나 트랜잭션을 처리하면 그 자체가 거대한 모놀리스가 되어 병목이 됩니다. 공통 관심사만 두고 비즈니스 로직은 각 서비스로 넘깁니다. 또 게이트웨이는 단일 장애 지점이라 반드시 이중화합니다. 구현체로는 Spring Cloud Gateway, Nginx, Kong 등이 있습니다.
서킷 브레이커 — 연쇄 장애 차단
추천 API 하나가 과부하로 멈췄는데 메인 페이지까지 멈추는 일이 있습니다. 게이트웨이가 멈춘 API의 응답을 기다리다 자기 스레드가 고갈되기 때문입니다. 서킷 브레이커는 두꺼비집(누전 차단기)처럼, 특정 서비스가 실패하기 시작하면 회로를 끊어 즉시 실패(fail fast) 시킵니다.
상태는 신호등처럼 셋입니다 — Closed(정상), Open(에러율이 임계치(예: 50%)를 넘으면 차단해 즉시 에러 반환), Half-Open(일정 시간 뒤 요청 하나만 흘려 성공하면 Closed로 복귀). 서킷은 호출하는 쪽에 둡니다(멈춘 서비스에는 물어볼 수 없으니까요). 열렸을 때 500을 던지기보다 미리 만든 정적 결과 같은 폴백으로 우아한 성능 저하(graceful degradation) 를 노리는 게 좋습니다. 구현체는 Java의 Resilience4j(구 Hystrix) 등.
주의할 안티패턴이 있습니다.
- 재시도 폭풍 — 게이트웨이 3회 + 서비스 3회면 한 클릭에 9번 호출. 재시도는 한 계층에서만.
- 타임아웃 불일치 — 바깥이 1초, 안쪽이 5초면 바깥이 포기한 뒤에도 안쪽은 계속 일해 좀비 프로세스가 됩니다. 타임아웃은 안쪽이 더 짧아야 합니다(타임아웃 예산 참고).
- 과도하게 넓은 서킷 — 하나의 서킷을 모두가 공유하면 한 의존성 장애가 전체를 차단합니다. 서킷은 의존성 단위로 잘게 나눕니다.
관련해서 장애를 국소화하는 큰 그림은 내결함성과 고가용성에서 다뤘습니다.
서비스 디스커버리 — 동적 IP 관리
클라우드에선 오토스케일링으로 서버 IP가 계속 바뀝니다. IP를 하드코딩할 순 없으니, 전화번호부(114) 처럼 "서비스 이름을 주면 살아 있는 IP를 돌려주는" 중간 관리자를 둡니다. 대표적으로 Eureka가 있고, 동작은 ① 등록(서비스가 기동 시 자기 IP 신고) → ② 하트비트(매초 생존 신고, 끊기면 제거) → ③ 패치(게이트웨이가 30초마다 목록을 받아 로컬 캐싱)입니다.
방식은 둘로 나뉩니다. 클라이언트 사이드(Eureka — 클라이언트가 직접 주소를 알아와 로드밸런싱)와 서버 사이드(쿠버네티스 — 도메인 이름만 보내면 CoreDNS·Service가 매핑). 요즘은 쿠버네티스로 인프라 레벨에서 처리하는 추세입니다.
정리
- 기술은 비용 — MSA의 장점(독립 배포·확장·장애 격리)은 복잡성과 맞바꾼 것.
- 모놀리스로 시작 → 모듈러 모놀리스(경계를 코드로) → 필요할 때 MSA. 경계만 잘 잡아두면 분리가 쉽다.
- MSA엔 게이트웨이·서킷 브레이커·서비스 디스커버리가 따라온다 — 각각 단일 진입점·연쇄 장애 차단·동적 IP 관리.
- 게이트웨이엔 비즈니스 로직을 넣지 말고, 서킷은 호출자에 의존성 단위로, 타임아웃은 안쪽을 더 짧게.
