모듈러 모놀리스

Modular Monolith

개념

서버에 올리는 단위는 하나로 두되, 내부를 모듈로 갈라 놓고 모듈끼리는 정해진 창구로만 부르게 막아 둔 구조입니다.

등장 배경

서비스를 처음 만들 때는 전부 한 덩어리에 담는 편이 빠릅니다. 결제도 알림도 문서 저장도 같은 프로그램 안에 있으니 필요한 함수를 바로 부르면 되고, 급할 때 남의 코드를 직접 고치는 것도 막을 이유가 없었습니다. 2년쯤 지나면 그 편의가 청구서로 돌아옵니다.

페이지덱에서 태오가 알림 문구를 한 줄 고쳤다가 결제 화면이 깨진 적이 있습니다. 알림 쪽 코드가 결제 쪽 내부 데이터를 직접 읽고 있었고, 그런 연결이 어디에 몇 개나 있는지 아는 사람이 없었습니다. 그 뒤로 팀은 배포를 두려워하게 됐고, 작은 수정도 금요일에는 올리지 않는 규칙이 생겼습니다.

이때 흔히 나오는 답이 서비스를 기능별로 쪼개 따로 올리는 쪽입니다. 다만 구성원 45명 규모에서 서비스를 여덟 개로 나누면 배포와 감시와 장애 대응도 여덟 벌이 되고, 정작 경계를 어디에 그어야 할지는 아직 확실하지 않습니다. 모듈러 모놀리스는 그 사이를 메웁니다. 코드를 가르는 일과 배포를 가르는 일을 분리해, 먼저 안쪽만 갈라 두고 밖으로 나가는 단위는 그대로 하나로 둡니다.

작동 방식

배포 단위는 하나 그대로 두고 코드 안에 경계를 세웁니다. 그 경계는 약속이 아니라 도구가 지키게 만든다는 점이 모놀리스와 갈리는 자리입니다.

  1. 기능 단위로 모듈을 나눕니다. 결제, 알림, 문서, 계정처럼 업무에서 다르게 부르는 덩어리가 각각 하나가 됩니다. 화면·로직·데이터 같은 기술 층이 아니라 업무 단위로 자르는 것이 기준입니다.
  2. 모듈마다 바깥에 내놓는 창구를 정합니다. 알림 모듈은 "이 사용자에게 이 알림을 보내라" 한 가지만 공개하고, 그 안에서 무슨 표를 읽고 어떤 순서로 처리하는지는 감춥니다.
  3. 창구 밖으로는 못 부르게 막습니다. 알림 모듈이 문서 모듈의 내부 데이터를 직접 읽으면 빌드가 실패하도록 의존성 규칙을 걸어 둡니다. 이 단계가 없으면 남는 것은 폴더 이름뿐입니다.
  4. 데이터도 모듈이 소유합니다. 결제 표는 결제 모듈만 직접 읽고 쓰고, 다른 모듈은 창구를 통해 값을 받아 갑니다. 같은 데이터베이스 안에 있어도 이 규칙은 그대로 겁니다.

이렇게 하면 결합도는 내려가고 응집도는 올라갑니다. 결합도는 구성 요소가 서로에게 얽혀 있는 정도이고, 응집도는 한 모듈 안의 코드가 한 가지 일에 모여 있는 정도입니다. 앞의 사고를 이 말로 옮기면 알림과 결제 사이의 결합도가 높았던 것이고, 경계를 세운 뒤에는 알림 문구를 고쳐도 결제 코드가 그 변경을 볼 수 없습니다.

모듈끼리는 여전히 같은 프로그램 안에서 함수로 부릅니다. 네트워크를 타지 않으니 호출이 실패할 걱정이 없고, 오류가 나면 한 줄짜리 호출 기록으로 어디서 났는지 따라갈 수 있습니다. 대신 한 모듈이 메모리를 다 쓰면 전부 같이 멈추고, 알림만 고쳐도 전체를 다시 올려야 합니다.

쓰이는 곳

경계를 어디에 그어야 할지 아직 확신이 없고, 팀 규모가 서비스를 여럿 굴릴 만큼 크지 않은 구간에서 고릅니다. 페이지덱처럼 구성원 45명에 배포가 하루 두세 번인 회사가 전형적인 자리입니다.

경계를 어디서 자를지는 도메인 주도 설계가 답을 줍니다. 기술이 아니라 업무의 말로 선을 긋는 방법인데, 데이터베이스 표 모양이 아니라 업무에서 결제와 문서를 다르게 부르는지를 기준으로 삼습니다. 모듈 경계는 나중에 서비스를 쪼갤 때 그대로 쪼개는 선이 되므로, 한 번 제대로 그어 두면 두 번 일하지 않습니다.

그래서 많은 팀이 이 구조를 옮겨 가는 길로 씁니다. 순서는 대개 이렇습니다.

  1. 한 덩어리로 시작합니다. 사용자가 몇 명인지도 모르는 단계에서 나누는 일에 시간을 쓰지 않습니다.
  2. 아픈 곳이 생기면 모듈로 가릅니다. 앞의 알림·결제 사고처럼 한쪽을 고칠 때 다른 쪽이 깨지기 시작하는 시점입니다.
  3. 정말 따로 굴려야 하는 모듈만 떼어 서비스로 올립니다. 대개 트래픽이 크게 다르거나 담당 팀이 따로 생긴 모듈입니다.

3번까지 가지 않는 팀도 많습니다. 모듈로 가른 것만으로 배포 공포가 사라지면 거기서 멈춰도 됩니다. 모노레포와 헷갈리는 자리도 여기입니다. 모노레포는 여러 프로젝트를 한 저장소에서 관리하는 방식이라 코드를 어디에 두느냐의 문제이고, 모듈러 모놀리스는 무엇을 한 번에 올리느냐의 문제입니다. 서비스 여덟 개를 한 저장소에서 관리하면 모노레포이면서 모듈러 모놀리스는 아닙니다.

실무 용례

"이번엔 서비스로 떼지 말고 모듈로만 가릅시다. 아직 경계가 확실하지 않아요."

배포까지 나누는 결정을 뒤로 미루고 코드만 먼저 정리하자는 제안입니다.

"알림 모듈에서 문서 표를 직접 읽고 있어서 빌드가 막혔어요."

경계를 도구가 실제로 지키고 있다는 뜻입니다.

"결제만 트래픽이 달라서 그것만 떼어 낼까 합니다."

전체를 쪼개는 대신 필요한 모듈 하나만 서비스로 옮기겠다는 말입니다.

페이지덱은 앞의 사고 뒤에 반년에 걸쳐 이 구조로 옮겼습니다. 한 번에 다 가르지 않고 사고가 났던 알림과 결제부터 떼어 창구를 정했고, 나머지는 손댈 일이 생길 때마다 하나씩 옮겼습니다. 규원이 들은 보고는 기술 용어가 아니라 숫자였습니다. 배포 뒤 장애 건수가 분기당 일곱 건에서 두 건으로 줄었고, 금요일 배포 금지 규칙이 없어졌습니다.

지금도 서비스는 한 덩어리로 올라가고, 태오는 당분간 그대로 둘 계획입니다. 나눌 준비가 끝난 상태와 실제로 나눈 상태는 다르고, 운영 비용은 나눈 순간부터 붙기 때문입니다.

흔한 오해

폴더를 기능별로 나눠 두면 이 구조라고 읽는 경우가 가장 많습니다. 겉으로 보이는 모습이 실제로 비슷합니다. 하지만 규칙을 강제하는 장치가 없으면 경계는 사람의 기억력에 기대게 됩니다. 급한 장애를 잡던 새벽에 누군가 옆 모듈의 표를 직접 읽고, 그 코드가 리뷰를 통과하고, 반년 뒤에는 폴더만 나뉜 예전 덩어리로 돌아가 있습니다. 경계는 빌드가 실패해야 유지됩니다.

마이크로서비스로 가기 전에 잠깐 거치는 단계로 읽는 것도 흔합니다. 덜 나눈 상태처럼 보이니 그럴듯합니다. 하지만 나누는 데는 값이 붙습니다. 서비스마다 배포와 감시와 장애 대응이 따로 필요하고, 함수 호출이던 것이 네트워크 호출이 되면서 실패할 수 있는 지점이 새로 생깁니다. 팀이 마흔다섯 명이고 배포가 하루 두세 번이면 그 값을 치를 이유가 없습니다. 목적지로 두고 오래 머무는 팀이 실제로 많습니다.

자주 헷갈리는 용어

용어배포 단위모듈러 모놀리스와의 차이
모놀리스하나배포 단위는 같습니다. 안쪽 경계를 도구로 강제하느냐만 다르고, 그 차이가 알림 코드가 결제 데이터를 읽을 수 있느냐를 가릅니다
마이크로서비스기능마다 하나씩경계를 긋는다는 점은 같고 배포를 나누느냐가 다릅니다. 나누면 따로 올리고 따로 늘릴 수 있는 대신 배포·감시·장애 대응이 서비스 수만큼 늘어납니다

같이 보면 좋은 페이지

  • 소프트웨어 아키텍처

    이 구조가 어떤 선택지들 사이에 놓인 것인지 먼저 봅니다

  • 모놀리스

    출발점이 되는 구조라 무엇이 달라졌는지 바로 견줄 수 있습니다

연관 용어

마지막 업데이트 2026-09-16