← 목록으로
SW공학·관리
#모노레포#폴리레포#의존성그래프#빌드캐시#형상관리
최종 업데이트 · 2026-11-08

모노레포(Monorepo) 전략과 폴리레포 비교

1. 개요

정의: 모노레포(Monorepo)는 여러 프로젝트·서비스·라이브러리의 소스 코드를 하나의 버전관리 저장소에 통합해 관리하는 형상관리 전략이다. 단일 저장소 안에서 코드를 공유하고, 하나의 커밋으로 여러 모듈을 원자적으로(atomically) 변경하며, 통합된 빌드·의존성 그래프 위에서 개발한다. 단일 배포 단위를 뜻하는 모놀리식 아키텍처와는 구별되는, 코드 조직화(code organization)의 문제다.

소프트웨어 조직이 성장하면 서비스와 라이브러리의 수가 수백·수천 개로 늘어난다. 각 산출물을 별도의 저장소에 두는 폴리레포(Polyrepo, Multi-repo) 방식은 초기에는 자연스럽지만, 공통 라이브러리를 여러 저장소가 버전으로 참조하면서 "어느 저장소가 어느 버전을 쓰는가"를 추적하기 어려워진다. 공통 모듈의 보안 패치 하나를 반영하려면 그 모듈을 쓰는 수십 개 저장소에 버전 업그레이드 PR을 각각 올려야 하는, 이른바 의존성 지옥(dependency hell) 과 다이아몬드 의존성 문제가 발생한다.

모노레포는 이 문제를 관점의 전환으로 해결한다. 코드를 "저장소 경계로 쪼개어 버전으로 결합"하는 대신, "하나의 저장소 안에서 소스 수준으로 결합(source dependency)"하는 것이다. 그 결과 공통 라이브러리를 수정하는 커밋과 그것을 사용하는 모든 소비자의 수정이 같은 커밋·같은 PR 안에서 원자적으로 이루어진다. "변경이 어디에 영향을 주는가"는 버전 매트릭스가 아니라 의존성 그래프에 대한 정적 분석으로 즉시 알 수 있다.

기술사 답안 관점에서 모노레포는 단순한 "큰 저장소"가 아니라, 원자적 변경·단일 진실 공급원·전역 가시성이라는 가치를 얻는 대신 빌드 성능·접근통제·확장성이라는 비용을 전용 도구로 상쇄하는 엔지니어링 트레이드오프로 서술해야 한다. Google·Meta·Microsoft 등 초대형 조직이 모노레포를 택했다는 사실 자체보다, "왜 그들이 전용 빌드 시스템 없이는 모노레포가 성립하지 않는가"를 설명하는 것이 핵심이다.

1.1 등장 배경과 필요성

첫째, 코드 공유와 중복 제거의 요구다. 마이크로서비스가 늘어나면 인증·로깅·직렬화 같은 공통 코드가 각 서비스에 복사되거나, 사내 패키지로 떼어내 버전으로 배포된다. 복사는 일관성을 깨뜨리고, 버전 배포는 업그레이드 지연을 낳는다. 모노레포는 공통 코드를 단일 소스로 두고 모든 소비자가 최신 HEAD를 직접 참조하게 하여 두 문제를 동시에 없앤다.

둘째, 대규모 리팩터링과 API 변경의 안전성이다. 폴리레포에서 공통 라이브러리의 함수 시그니처를 바꾸면, 소비자들은 아직 구버전을 참조하므로 변경이 "깨졌는지"를 즉시 알 수 없다. 모노레포에서는 라이브러리와 모든 호출부를 한 커밋으로 함께 고치고 한 번에 테스트하므로, 컴파일·테스트가 통과하면 조직 전체가 깨지지 않았음이 보장된다.

셋째, 개발자 경험과 온보딩이다. 신규 개발자가 저장소 하나만 클론하면 전체 코드베이스를 탐색·검색·변경할 수 있고, 통합된 코드 스타일·린트·CI 규칙을 일괄 적용할 수 있다. 다만 이 장점은 저장소가 수천만 파일 규모가 되면 클론·빌드 성능 문제로 반전되므로, 반드시 전용 도구와 함께 논의해야 한다.

2. 전체 구조와 의존성 모델

모노레포의 본질은 저장소 레이아웃이 아니라 프로젝트 간 의존성을 소스 수준 그래프로 표현하는 데 있다. 아래 구조도는 단일 저장소 안에 여러 애플리케이션과 공유 라이브러리가 하나의 의존성 그래프로 묶여, 공통 빌드·CI·도구 계층 위에서 동작하는 모습을 보여준다.

graph TD
  subgraph REPO["단일 저장소(Monorepo)"]
    subgraph APPS["애플리케이션 계층"]
      WEB["web-app"]
      API["api-service"]
      BATCH["batch-worker"]
    end
    subgraph LIBS["공유 라이브러리 계층"]
      AUTH["lib-auth"]
      LOG["lib-logging"]
      MODEL["lib-domain-model"]
    end
    WEB --> AUTH
    WEB --> LOG
    API --> AUTH
    API --> MODEL
    BATCH --> MODEL
    BATCH --> LOG
    AUTH --> LOG
  end
  TOOL["빌드 시스템·CI(그래프 인지)"] -.분석.-> REPO

위 그래프에서 lib-logging을 수정하면 그것을 (직접·간접으로) 참조하는 lib-auth·web-app·api-service·batch-worker가 영향권(affected set)에 들어온다. 모노레포 전용 도구의 핵심 역량은 바로 이 영향 그래프를 정확히 계산하여, 변경된 커밋에 대해 "다시 빌드·테스트해야 하는 프로젝트만" 선별하는 것이다. 이를 affected/impacted 분석이라 하며, 저장소 전체를 매번 빌드하지 않고도 안전성을 유지하는 성능 비결이다.

2.1 소스 의존성과 원자적 커밋

폴리레포가 산출물을 버전 번호(예: lib-auth@1.4.2)로 참조하는 바이너리 의존성(binary dependency) 인 반면, 모노레포는 소스 트리 안의 경로를 직접 참조하는 소스 의존성(source dependency) 을 기본으로 한다. 이 차이가 모노레포의 거의 모든 장단점을 파생시킨다.

소스 의존성에서는 라이브러리의 "현재 HEAD"가 유일한 버전이므로, 여러 버전이 동시에 떠 있어 생기는 다이아몬드 의존성 충돌이 원천적으로 사라진다. 동시에 원자적 커밋(atomic commit) 이 가능해진다 — 예컨대 lib-domain-model의 필드명을 바꾸는 변경과 그 필드를 쓰는 api-service·batch-worker의 수정이 하나의 커밋으로 묶여, 저장소는 항상 "빌드 가능한 일관된 상태"를 유지한다. 되돌리기도 그 커밋 하나를 git revert하면 전체가 함께 복원된다.

반대로 이는 "내가 모르는 사이 누군가 공통 라이브러리를 바꿔 내 서비스가 깨질 수 있다"는 밀착 결합의 위험도 뜻한다. 그래서 모노레포는 소유권 경계를 코드로 강제하는 CODEOWNERS, 가시성 규칙(visibility), 그리고 변경 전 전체 영향 테스트를 CI에서 돌리는 체계가 반드시 뒤따라야 한다.

2.2 빌드·CI 파이프라인의 동작

모노레포에서 "전체를 빌드한다"는 소박한 접근은 저장소가 커지는 순간 붕괴한다. 수천 개 프로젝트를 매 커밋마다 빌드·테스트하면 CI 시간이 수 시간으로 폭증하기 때문이다. 따라서 모노레포 도구는 다음 파이프라인으로 필요한 작업만 수행한다.

flowchart LR
  COMMIT["커밋/PR 변경 파일"] --> GRAPH["의존성 그래프 계산"]
  GRAPH --> AFFECTED["영향받는 프로젝트 선별(affected)"]
  AFFECTED --> CACHE{"빌드 캐시 적중?"}
  CACHE -->|"적중(hit)"| REUSE["캐시 산출물 재사용"]
  CACHE -->|"불일치(miss)"| RUN["해당 작업만 빌드·테스트"]
  RUN --> STORE["원격 캐시에 저장"]
  REUSE --> RESULT["CI 결과"]
  STORE --> RESULT

핵심은 두 가지 최적화다. 첫째, 영향 기반 선별: 변경 파일에서 역방향으로 의존성 그래프를 따라가 다시 검증해야 할 프로젝트만 고른다. 둘째, 콘텐츠 주소 지정 캐시(content-addressed cache): 작업의 입력(소스·의존성·명령·툴체인)을 해시로 지문화하여, 같은 입력이면 이미 계산된 산출물을 원격 캐시(remote cache) 에서 내려받아 재사용한다. 개발자 A가 로컬에서 빌드한 결과를 개발자 B와 CI가 재사용하는 이 분산 캐시 공유가 대규모 모노레포의 생산성을 좌우한다.

3. 유형과 대표 도구

모노레포를 지탱하는 빌드 시스템은 설계 철학에 따라 크게 그래프 기반 작업 러너와 엄격한 밀폐형 빌드 시스템으로 나뉜다. 전자는 기존 생태계(npm·Gradle 등) 위에 영향 분석과 캐시를 얹는 가벼운 접근이고, 후자는 빌드의 입력·출력을 완전히 통제해 재현성과 원격 실행까지 보장하는 무거운 접근이다.

구분 그래프 기반 러너 밀폐형(hermetic) 빌드 시스템
대표 도구 Nx, Turborepo, Lerna Bazel, Buck2, Pants
철학 기존 패키지 매니저 위 오케스트레이션 입력·출력 완전 통제, 재현 가능 빌드
캐시 작업 단위 로컬·원격 캐시 액션 단위 콘텐츠 주소 캐시 + 원격 실행
도입 난이도 낮음(점진 적용 용이) 높음(BUILD 파일로 전면 재기술)
적합 규모 중소~대규모 초대형·다언어·장기 유지

여기서 유의할 점은 도구 선택이 "무엇이 더 좋은가"가 아니라 조직 규모와 재현성 요구 수준에 대한 적합성의 문제라는 것이다. 수백 명 규모의 웹 중심 조직이 Bazel을 전면 도입하면 BUILD 파일 작성·유지 비용이 생산성을 압도할 수 있고, 반대로 수천 명·다언어 조직이 그래프 러너만 쓰면 빌드 재현성과 원격 실행의 한계에 부딪힌다.

3.1 밀폐형 빌드와 재현성

밀폐형 빌드는 빌드 작업이 선언한 입력(소스·의존성·툴체인 버전)만 볼 수 있고 네트워크·시스템 상태 같은 비선언 입력에 의존하지 못하도록 격리한다. 그 덕분에 "같은 입력 → 항상 같은 출력"이 성립해 캐시 적중률이 극대화되고, 빌드를 수백 대 원격 워커에 분산(remote execution)할 수 있다. Google의 내부 빌드 시스템 Blaze(오픈소스판 Bazel)가 수십억 줄 규모 단일 저장소를 감당하는 비결이 바로 이 밀폐성과 원격 캐시·실행의 결합이다.

반면 밀폐성은 모든 의존성을 명시적으로 선언해야 하므로 초기 전환 비용이 크다. 기존에 암묵적으로 참조하던 시스템 라이브러리나 전역 도구를 전부 BUILD 규칙으로 끌어와야 하므로, 레거시 코드베이스를 한 번에 밀폐화하는 것은 사실상 불가능하고 점진적 마이그레이션이 필요하다.

4. 폴리레포와의 비교 — 차이가 생기는 이유

모노레포와 폴리레포의 대립은 "저장소를 몇 개로 나눌 것인가"가 아니라 결합 시점을 소스로 당길 것인가, 바이너리로 미룰 것인가의 선택이다. 이 근본 차이에서 아래의 모든 실무적 차이가 파생된다.

관점 모노레포 폴리레포
의존성 결합 소스(최신 HEAD) 바이너리(버전 핀)
코드 공유 직접 참조, 즉시 반영 패키지 배포·버전 업 필요
원자적 변경 한 커밋으로 전역 변경 여러 저장소 조율 필요
접근통제 세분화 어려움(별도 도구 필요) 저장소 단위로 명확
빌드/CI 전용 도구 없으면 폭증 저장소별 독립, 단순
팀 자율성 공통 규칙 강제, 자율성↓ 팀별 기술 선택 자유↑

원자적 변경이 가능한 이유는 소스 의존성 때문이다. 폴리레포에서 공통 API를 바꾸려면 "라이브러리 배포 → 각 소비자가 버전 업 → 각자 배포"라는 다단계 조율이 필요하고, 이 과정에서 신·구 버전이 공존하며 호환성 부담이 생긴다. 모노레포는 이 조율을 컴파일러·테스트로 치환해 한 번에 끝낸다.

접근통제가 어려운 이유는 하나의 저장소가 곧 하나의 권한 경계이기 때문이다. 금융·국방처럼 소스 접근을 팀·등급별로 엄격히 분리해야 하는 조직에서는, 저장소 단위로 권한이 명확한 폴리레포가 규제 대응에 유리하다. 모노레포에서 세분화된 접근통제를 하려면 희소 체크아웃(sparse checkout)·서브트리 권한 등 추가 메커니즘이 필요하며 운영 복잡도가 올라간다.

실무적 함의로, 많은 조직이 하이브리드를 택한다. 제품군·도메인 단위로 모노레포를 두되(예: 프런트엔드 모노레포, 데이터 플랫폼 모노레포), 규제·외부 공개·전혀 다른 생애주기를 가진 영역은 별도 저장소로 분리하는 식이다. 즉 "하나의 거대한 저장소"는 목표가 아니며, 응집도 높은 경계 안에서의 통합이 실천적 지향점이다.

5. 심화 — 초대형 모노레포의 확장 기법과 사례

모노레포의 가장 큰 기술적 난제는 Git 자체의 확장 한계다. Git은 전체 이력과 작업 트리를 로컬에 복제하는 분산 모델이라, 파일이 수백만~수천만 개가 되면 git status·clone·checkout이 감당 불가능해진다. 이를 해결하기 위해 다음 기법들이 발전했다.

첫째, 부분 복제(partial clone)와 희소 체크아웃(sparse checkout) 이다. git clone --filter=blob:none로 blob을 지연 로딩하고, 작업에 필요한 디렉터리만 작업 트리에 펼쳐 로컬 부담을 줄인다. Microsoft는 Windows 소스(약 300GB, 350만+ 파일)를 Git으로 옮기면서 VFS for Git(이후 Scalar) 로 파일을 요청 시점에 가상 파일시스템으로 받아오게 하여 전체 클론 없이 작업하도록 했다.

둘째, 가상 파일시스템과 파일 모니터다. Meta는 Mercurial 기반 모노레포에서 Watchman(파일 변경 감시)과 EdenFS(요청 시 로딩 가상 파일시스템), 빌드 도구 Buck2를 결합해, 수천만 파일 저장소에서도 상태 조회를 상수 시간에 가깝게 유지한다. Google은 아예 Git을 쓰지 않고 Piper라는 중앙집중형 VCS와 가상 파일시스템 CitC, 빌드 시스템 Blaze로 수십억 줄·2만여 명 개발자가 동시에 작업하는 단일 저장소를 운영한다(2016년 공개 기준 약 86TB, 하루 수만 건 커밋).

셋째, 원격 캐시·원격 실행이다. 앞서 설명한 콘텐츠 주소 캐시를 조직 전체가 공유하면, 신규 개발자가 처음 빌드할 때조차 대부분의 산출물을 캐시에서 받아 수십 분 걸릴 빌드를 수 분으로 줄인다. 실측 사례로, Nx·Turborepo 도입 조직들은 원격 캐시 적중 시 CI 시간이 50~90% 단축되는 효과를 보고한다(변경 범위와 캐시 적중률에 따라 편차가 크므로 단정보다는 경향으로 이해해야 한다).

이들 사례의 공통 교훈은 명확하다 — 모노레포는 전략이 아니라 투자다. 전용 빌드 시스템, 가상 파일시스템, 원격 캐시 인프라, 소유권·가시성 거버넌스가 함께 갖춰질 때만 성립하며, 이 인프라 없이 저장소만 합치면 오히려 생산성이 급락한다.

6. 고려사항 및 시사점 (기술사 관점)

  • 적용 전략(점진적 채택): 처음부터 전사 단일 저장소를 지향하기보다, 밀접하게 함께 변경되는 응집도 높은 도메인을 먼저 하나의 모노레포로 묶고, 그래프 기반 러너(Nx·Turborepo)로 영향 분석과 원격 캐시를 도입한 뒤, 규모가 커지면 밀폐형 빌드와 가상 파일시스템으로 확장하는 단계적 성숙 로드맵이 안전하다. "합치는 것"보다 "경계를 어떻게 그을 것인가"가 먼저다.
  • 트레이드오프(가시성 vs 자율성·성능): 모노레포는 전역 가시성·원자적 변경·일관된 도구라는 가치를 주지만, 팀별 기술 선택 자유를 제약하고 Git·CI 확장 비용을 유발한다. 전용 도구 투자 여력이 없는 소규모 조직이나 완전히 독립적인 생애주기를 가진 제품은 폴리레포가 더 합리적일 수 있다. 정답은 조직 규모·결합도·규제 요건에 따라 달라진다.
  • 보안·거버넌스: 단일 저장소는 권한 경계가 넓어지므로 CODEOWNERS 기반 리뷰 강제, 디렉터리별 가시성 규칙, 희소 체크아웃을 통한 접근 범위 제한, 그리고 공급망 보안(SBOM·커밋 서명)을 CI에 결합해 밀착 결합의 위험을 통제해야 한다. 저장소 자체가 최상위 공격 표적이자 단일 장애점이 되므로 백업·접근통제·보호 브랜치가 필수다.
  • 조직·문화: 모노레포는 "모두가 하나의 코드베이스를 함께 책임진다"는 협업 문화의 전환을 요구한다. 공통 린트·테스트·버전 정책을 전사 합의로 수립해야 하며, 트렁크 기반 개발(trunk-based development)·대규모 자동 리팩터링(codemod) 문화와 결합할 때 효과가 극대화된다.
  • 전망 및 연계: 모노레포는 플랫폼 엔지니어링·DevSecOps·CI/CD의 토대로 자리 잡고 있으며, 원격 실행·분산 캐시는 클라우드 빌드 서비스로 상품화되고 있다. 나아가 AI 코드 어시스턴트가 전체 코드베이스의 맥락을 한 저장소에서 참조할 수 있다는 점에서, 조직 지식의 단일 공급원으로서 가치가 더 커질 전망이다.

참고자료


한 줄 요약: 모노레포는 여러 프로젝트를 단일 저장소에서 소스 의존성으로 통합해 원자적 변경·전역 가시성·코드 공유를 얻는 전략으로, 영향 기반 선별 빌드·원격 캐시·가상 파일시스템 같은 전용 인프라가 뒷받침될 때만 확장이 성립하며, 접근통제·팀 자율성·Git 확장 비용이라는 트레이드오프를 조직 규모와 결합도에 맞춰 설계해야 한다.