생산적인 모노레포를 만들기 위한 필수 요소
(blog.swgillespie.me)- 모노레포 도입은 조직의 일관성, 코드 공유, 공동 툴링 환경 강화 등 장점이 있으나, 빅테크 사례를 그대로 따라가면 새로운 문제와 도전에 직면하게 됨
- 성공적인 모노레포를 위해서는 모든 주요 작업을 O(repo)가 아닌 O(change) 로 만드는 원칙을 지켜야 하며, 빌드·테스트·CI/CD 각 단계에서 이에 맞는 도구와 전략이 필요함
- 소스 컨트롤은 git을 시작으로 규모가 커질수록 sparse checkout, 가상 파일시스템 등 점진적 확장 고려 필요
- 빌드 시스템은 가능한 한 단일 언어 유지, 각 언어의 기본 빌드 툴로 최대한 버티고, 꼭 필요할 때 Bazel/Buck2 등으로 점진적 전환 권장
- 테스트·CI/CD는 변화 영향 범위만 빠르게 감지하여 빌드·테스트·배포해야 하며, 대규모 모노레포에서는 테스트 자동 재시도, flaky test 격리 등 신뢰성 확보 전략도 필수
서론: 모노레포를 향한 여정의 시작
- 새로운 Developer Productivity 팀에 있는 엔지니어라면, 모노레포 도입 결정 이후 어떤 준비와 노력이 필요한지 고민이 커짐
- Google, Meta, Uber 등 대기업의 모범 사례는 근사해 보이지만, 현실적으로 그들과 동일한 수준의 결과는 불가능함
- 각 조직은 자기만의 이유와 필요성에 맞춰 모노레포 도입을 결정해야 하며, 그 과정에서 일관성(consistency), 조직적 통합, 공동 툴링 등에서 이점을 추구할 수 있음
모노레포의 필요성을 명확히 하기
- 대기업 사례는 결과적으로 도달한 모습일 뿐, 초기에 참고할 근거로 삼기에는 부적절함
- 실제로는 새로운 문제들이 발생하며, 기존 여러 레포 관리 체계와는 다른 유형의 이슈가 발생함
- 모노레포의 도입 목적은 일관성 유지, 조직 전반에 걸친 툴링 통합, 엔지니어링 표준 및 컨벤션 적용 등에 있음
- 각 팀은 자체 문화와 방향에 맞는 목표를 명확히 하여야 효과적인 결과를 얻을 수 있음
골든 룰: O(change)의 원칙
- 모든 저장소 관련 도구들은 빠른 동작을 위해 O(repo) 가 아니라 O(change) 복잡도를 가져야 함
- 실제로 대규모 모노레포가 클수록 기존 도구들의 비효율성이 두드러지므로, 성능 문제를 극복하는 구조적 설계가 필수임
- 대기업 기술 블로그에서 언급되는 혁신 역시, 대부분 O(repo)로 인한 비효율 극복에 집중되어 있음
소스 컨트롤
- 대부분 소프트웨어 조직은 Git을 기본으로 사용하나, Git은 중앙집중식 모노레포 환경에서 대규모로 확장될 때 성능 한계가 있음
- 현실적으로 대부분의 조직은 git+GitHub로 상당히 오래 버틸 수 있음
- 성장이 빨라질수록, sparse checkout(부분 클론) 기능과 가상 파일시스템(필요시 파일을 서버에서 동적으로 다운로드) 같은 구조가 필요해짐
- 대기업은 이에 맞춰 Git을 포킹하거나 별도의 시스템을 개발함 (Microsoft: 자체 Git 포크, Meta: Mercurial 포크, Google: Piper 등)
- Jujutsu 등 차세대 소스컨트롤도 고려할 만함
- 규모가 작을 때는 Git을 무리없이 사용 가능하나, 성장 과정에서 확장 전략을 염두에 둘 필요가 있음
- 소스코드에 IDL(Interface Definition Language) 로 생성된 코드가 포함되면 저장소 크기가 기하급수적으로 증가하는 현실적인 문제도 존재함
빌드 시스템
- Bazel, Buck2 등은 대표적인 모노레포 빌드 툴로 다수 언어와 복잡한 빌드 그래프를 지원함
- 강력하지만 복잡성/운영 부담 큼
- 빌드를 단일 언어로 유지하면 삶이 훨씬 편해지며, 각 언어별 빌드 시스템(예: Maven, Gradle, Cargo, Go 등) 역시 높은 확장성을 가짐
- 빌드 시스템의 핵심 역할은 “지정한 빌드 타겟을 효율적으로 빌드(효율적 아티팩트 생성)”하고 “변경된 파일로 인해 영향받는 타겟을 신속하게 산출”하는 것임
- 이를 위해 target determinator(타겟 결정 도구) 개념이 필요하며, Rust, Go, Bazel 등 생태계에는 이미 다양한 해결책이 있음
- Rust:
guppy
- Go:
go/packages
- Bazel:
target-determinator
- Rust:
- Remote execution과 캐싱은 초대형 규모에서만 실제적으로 필요하며, 일반적인 기업에선 target determination이 더 실용적으로 활용됨
테스트
- 전체 테스트를 매번 실행하는 것은 비효율적이므로, 변경 영향 범위만을 테스트하는 시스템이 요구됨
- 플레이키(Flaky) 테스트는 대규모 테스트 시스템에서 더욱 심각한 이슈가 될 수 있음
- 테스트 시스템은 자동 재시도, 테스트 영향 범위 자동 판단, 플레이키 테스트 격리 등이 필요함
- 일부 언어(예: Rust의 nextest, Java의 JUnit 등)는 이러한 고급 기능을 기본이나 확장으로 제공함
- 모노레포의 테스트 체계는 빌드 시스템과 밀접히 통합되어야 효과적임
지속적 통합(CI)
- CI 시스템은 변경사항에 따라 필요한 빌드 아티팩트와 유효성 검증을 자동 수행해야 함
- Target determinator 성능과 효율성이 CI 파이프라인의 핵심 요소로 작동함
- 현대 CI는 “Merge Queue” 등 다양한 전략을 사용해, 코드 품질 유지와 병합 속도 최적화 간의 균형을 찾아야 함
- 단일 커밋/PR 마다 모든 검증 작업을 실행할지, 일부만 선별할지, 여러 PR을 배치 처리할지 등
- Throughput(처리량), Correctness(정확성), Tail latency(최대 대기 시간) 간의 트레이드오프를 자체적으로 정의하고 설계해야 함
- 대용량 모노레포의 병합 관리와 CI 효율성 강화는 아직까지도 완벽한 해법이 없는 도전 과제임
- Rust(bors), Chromium, Uber 등은 각자 다른 병합/검증 전략을 선택함
지속적 배포(CD)
- 모노레포 내 모든 변경이 원자적으로 배포될 것이라는 환상은 현실과 다름
- 하나의 PR로 다수 서비스의 인터페이스와 구현, 클라이언트까지 한꺼번에 변경할 수 있지만, 결국 실제 배포(Deploy)는 비동기적으로 진행되어 배포 시점에 문제 발생 가능
- 서비스 간 계약(Contract)을 깨뜨리는 변경은 배포시 심각한 장애를 유발할 수 있음
- 효과적인 모노레포 CD 전략은 배포 시스템 주기, 서비스 계약 검증, 문제 발생 시 신속감지 및 대응 능력 등이 필요함
결론
- 모노레포는 조직적 일관성과 엔지니어링 문화 강화를 위한 강력한 도구이나, 지속적인 엔지니어링·툴링 투자가 필요
- 각 단계별로 O(change) 원칙에 맞춘 자동화·도구·문화 구축이 핵심
- 성장과 함께 도구 또한 지속적으로 진화시키며, 조직 목표와 문화를 반영하는 체계적인 관리 노력이 중요함
- 충분한 각오와 헌신, 지속적 투자가 있다면, 모노레포는 궁극적으로 그만한 가치를 만들어줌
석사 시절에 지도교수가 구글 출신 엔지니어분과 식사하면서 모노레포 얘기를 듣고 오셨는지 우리도 앞으로 모노레포로 관리하자고 제안한 적이 있었는데 말리느라 고생했었습니다...
모노레포가 좋은 점이 많긴 하지만 저희 연구실은 그 특성상 성과물을 외부 사람들에게 공유해야 하는 경우가 잦은데, 모노레포로 성과물을 관리해왔다면 여기서 특히 고생했을 것 같습니다. 멀티레포면 그냥 성과물 별로 공개범위를 조절하면 되니까요.
모노레포를 하면서 고통 받는 경우는 대부분 이미 프로젝트를 너무 잘게 쪼개 놓은 경우인 거 같아요. 원래 한두 개면 될 프로젝트를 10여개로 쪼개놓고, 그걸 모노레포로 통합해서 관리하려니까 모노레포 관리 툴도 써야 하고 복잡도도 올라가죠. 그냥 프로젝트 자체를 한두 개로 통합하는 게 좋고, 두 개 이상의 프로젝트라도 관리툴 따로 쓰지 말고 그냥 디렉토리만 나눠서 한 저장소에 넣는다는 개념으로 생각하면 더 속편하게 관리할 수 있어요.
Hacker News 의견
-
이 스레드를 보면서 예전의 complexity merchants에 대한 이야기가 떠오르는 경험 공유 요청. 모노레포로 이동하면 기술적 희생이 있다는 의견에 전혀 동의하지 않는 입장. 계층적 파일 시스템의 파워를 이해하면 모노레포의 가치를 알 수 있음. CI/CD가 여기저기 흩어진 구성보다 모노레포 하나로 구성하는 게 훨씬 더 명확. 모노레포의 핵심은 전체 조직이 원자적인 커밋을 할 수 있다는 점임. 많은 개발자를 조율할 때 그 효용이 압도적임. 한 번에 리베이스하고 한 번의 큰 미팅이면 충분. 팀원들이 서로 안 좋아해 협업하지 않아도 관리 측면에서 모노레포는 큰 HR 도구 역할.
-
최근 개발자들은 지나치게 분리, 마이크로서비스, 다수의 작은 레포지토리, 모놀리스를 극도로 피하는 경향이 있음. 이는 복잡성을 키워 조직 구조 문제를 미래의 기술적 문제로 전환하는 결과. 소프트웨어 시스템의 내부 종속성도 제대로 인식하지 못함. 이전 직장에서 프로토콜 버퍼 스키마 파일을 업데이트하는 데 낭비된 시간이 믿기지 않을 정도. 다행히 지금 회사는 그렇지 않음.
-
여러 프로젝트에서 커밋을 추적하는 것은 있으면 좋은 정도이며, 실제로 의존성 추적이나 다운스트림 테스트 트리거 면에서 큰 차이 없음. 멀티레포 자동화로도 충분히 가능. 모노레포가 도움은 되지만 완전하지도 않고, 비용도 큼. 배포나 빌드가 원자적으로 처리되지 않음. 모노레포 규모가 커지면 git에서 벗어나 새로운 툴이 필요하고 이건 아주 큰 작업. 경험이 없으면 쉽게 말할 수 있는 부분 아님.
-
모노레포의 장점은 분명 존재하지만, 관리 비용이 polyrepo보다 더 비쌈. 어떤 상황에서든 무조건 모노레포가 좋은 것은 아님. 자세한 설명은 이 글 참고. 비용 대비 효과는 상황에 따라 다름.
-
프로그래밍 환경 설계에서 팀에 더 많은 파워를 줄수록 문제도 늘어난다는 게 유익한 경험칙. 기술적으로는 원자적 커밋이 더 강력한 파워가 아니고 오히려 적은 파워지만, 나쁜 인터페이스로 일하는 걸 가능하게 하므로 오히려 문제를 유발하는 파워임.
-
모노레포로 바꾸면 변화가 더 원자적이라는 믿음은 함정이라는 의견. [원문 인용: 모노레포의 가장 큰 허상은 전체 코드베이스에 원자적 커밋이 가능하다는 것. 실제로는 다양한 배포 아티팩트가 있는데, 서비스와 클라이언트 등을 한 번에 바꿔도 배포는 비동기적으로 일어남. 여러 레포에서는 여러 PR로 작업해야 하므로 위험 인식이 깔림. 모노레포의 CI는 주로 서비스 계약(CI job) 검증 역할을 하며, 필요시 변경 이유 명시가 요구됨.]
-
-
빅테크 모노레포에는 두 가지 유형이 있음. 첫째는 글에서 말한 전사적 단일 "THE" 모노레포로, 커스텀 VCS/CI가 필요하고 200명 엔지니어가 지원함. Google, Meta, Uber가 이 방식. 이 경지에 오르기까지 고통은 상상 이상이며, 보통 더 작게 나눈 "팀 단위" 모노레포에서 점차 확장. 각 스택/언어/팀마다 Bazel, Turborepo, Poetry 같은 도구로 각자 관리하다 시간이 지나면 더 큰 모노레포로 합쳐짐. 그러나 이 둘 모두 개발자와 비즈니스 모두 수백만 달러, 수백만 시간의 투자가 들어가며, 결국은 이 과정을 견딘 개발자들의 지원으로 유지.
-
대형 모노레포 회사에서 일했을 때 모노레포를 훨씬 더 선호. 단일 모노레포가 서비스 그래프, 코드 호출 구조 등 전체를 투명하게 파악하는 데 아주 도움이 됨. 폴리레포의 경우 지식이 팀별로 분산, 신규 코드 인수도 어렵고, 코드 아카이브 파악은 마치 미궁 탐험. 폴리레포는 오래된 디스코드/슬랙 메시지처럼 잊혀지는 느낌. 모노레포가 비용이 많이 들면, 폴리레포도 마찬가지로 다른 방식의 비용 발생. 모노레포는 거대한 대륙의 초식동물, 폴리레포는 다양한 종이 어둠에 묻힘.
-
현재 회사에서 백엔드가 약 11개 git 레포로 나뉘어 있으며, 기능 한 건을 위해 4~5개 머지 리퀘스트가 필요해 매우 번거로움. 여러 프로젝트를 모으기 위해 모노레포 도입을 검토 중. 그런데 레포를 합칠 수 없다면 모노레포 대안은 무엇인지 궁금.
-
언어와 무관하게 쉽고 강력한 모노레포 오케스트레이션 시스템은 아직 없음. Bazel은 복잡하고 배우기 어렵지만 최근엔 문서화가 많이 개선됨. Buck, NX, Pants 등 다른 선택지도 있지만 각각 특징이 있고, 특히 웹 지원은 제한적임. 대부분의 CI가 이런 툴을 제대로 지원하지 않아 설정이 까다로움. 참고로 Microsoft의 Rush가 최고의 경험 제공, 특히 프론트엔드/노드JS 모노레포에는 Rush 추천 Rush 공식 사이트.
-
대부분의 모노레포가 Google, Uber, Meta같은 대기업 규모까지 커지지는 않는 현실 언급. 서비스 수도 회사마다 다르고, 많아도 100개 정도라면 VCS 스케일 문제 없고 LSP 태그도 무리 없이 랩톱에서 다 돌아감. 모든 테스트를 CI에서 무작정 돌려도 거의 무난. 결론: 모든 회사가 구글 규모를 필요로 하진 않음.
-
현재 회사는 언어 스택별로 모노레포를 구축하는 중. 꽤 괜찮은 절충안임.
-
-
모노레포 vs 멀티레포 논의에서 자주 나오지 않는 포인트는 '역 콘웨이의 법칙' 발생. 레포 구조가 조직 구조와 문제 해결 방식에 영향을 준다는 점. 모노레포는 공통 인프라 팀에 영웅적 작업을 유발, 공통 영역을 건드릴 때 잠재적 깨짐이 많아져 기능 하나 개발에도 난이도 상승. 멀티레포에서는 팀 간 여러 PR과 조율, 내부 정치 등이 필요하지만 더 다양한 개발자가 역할을 분산해 처리 가능.
-
모노레포에서도 중앙에 깊이 연결된 변경이라면 여러 단계로 나눠 적용할 수 있음. 그 과정에서 여러 PR 및 조정, 정치적 이슈도 다뤄야 하지만, 오히려 모노레포라서 롤아웃 상황을 더 명확하게 볼 수 있음.
-
폴리레포에서는 공통 영역에서의 변경이 다운스트림 레포들에 반영되지 않아 각 레포가 다른 버전에 고정되고, 수년간 업데이트가 안 되어 고생하는 사례가 훨씬 더 흔함.
-
조직이 애초 방향성을 레포 구조로 선택하고 나중에 기술 선택이 따라온다는 가정이 맞는지 질문. 실제론 구체적인 repo 구조보다 더 근본적인 조직 철학(파편화 vs 공유) 결정이 선행. 방향이 바뀌더라도 코드 관리 방식은 수정할 수 있음. 멀티레포라도 엔지니어가 거의 모든 코드 접근 가능하고, 모노레포도 강한 격리와 별도의 CI나 배포 관리 규칙 적용 가능.
-
모노레포에서는 프로젝트 간 손쉬운 변경이 폴리레포에서는 너무 번거로워서 아예 시도조차 안 되는 경우가 훨씬 더 많음.
-
-
대형 테크 기업 경험상 빌드 시스템 관리에는 아예 전담 팀이 필요. 대형 모노레포는 소스 파일을 필요 시 다운로드하는 가상 파일 시스템 기반. 기사에서 언급 안 된 점은, 거의 모든 개발이 데이터센터에서 동작하는 개발 서버에서 진행, 50~100코어 환경 혹은 온디맨드 컨테이너(수시로 최신 커밋으로 업데이트) 활용. IDE가 dev server와 통합돼 언어나 서비스별로 사전준비/자동설정까지 chef/ansible로 자동화. 랩톱에서 직접 대규모 모노레포 개발할 일은 매우 드뭄(예외: 모바일/맥앱 등).
-
아마 같은 빌드 팀에서 일한 경험자. 모노레포 개발 환경이 로컬이든 원격이든, 재현성(reproducibility)이 더 중요. 이미징되는 원격 dev server면 더 쉽고 신뢰도 높음.
-
적은 규모 팀에서도 데이터센터 개발환경 활용 경험. 요즘 하드웨어 가격과 밀도를 보면 자체 랙을 꾸려 dev/staging/test 등 온디맨드 툴 다 돌리는 게 훨씬 합리적. 프로덕션과 유사한 개발 환경을 공유하게 되면 모노레포 방식이 아주 달라보임. 하지만 중소 기업은 빌드 시스템에 투자할 여력이 없고, 그런 대형 빌드 시스템 문제 자체가 생기지 않음(최소 10~20인 규모, 아주 복잡한 제품이어도 유지보수는 파트타임이 전부일 수도 있음).
-
-
Molnett(serverless cloud)에서 Bazel 기반 모노레포로 엄청난 효율을 경험한 소규모 팀(풀타임 1.5명) 이야기. Tilt+Bazel+Kind로 전체 플랫폼과 쿠버네티스 오퍼레이터까지 랩톱에서 기동, Mac/Linux 모두 지원. Bottlerocket 기반 OS 및 Firecracker까지 로컬에서 검증 가능. tool layer 구축으로 모든 개발자 동일 버전 go/kubectl 사용, 로컬 설치 필요 없음. 관리에 노력은 들지만, ex Google SRE 멤버 덕에 가능. 앞으로도 이런 방식만 원함. (주요 언어는 Golang, Bash, Rust)
-
1.5명 소규모라면 단일 레포가 당연. Bazel 경험은 아주 나빴지만, 대규모 프로젝트에는 쓸 가치가 있을 수도. 2명 미만 규모에는 오히려 Kind+Tilt만으로 충분. tool layer도 Go가 이미 go.mod로 어느 정도 해결. kubectl도 비슷하게 가능. ex-Googler의 연봉 수준도 고민 필요. Bazel 유지비용이 앞으로도 가치 있길 바람.
-
우리 회사는 시스템드(systemd) 기반 서비스 및 ansible playbooks로 배포, tmuxinator로 백엔드/DB/검색엔진/프론트엔드까지 모든 서비스 dev 모드로 터미널 한 번에 자동 기동. root에 ‘tmuxinator’ 명령 한번이면 전체 dev 환경이 뚝딱. 단일 모노레포가 이전보다 압도적으로 편리.
-
비슷한 상황, Bazel 도입 효과 극대화 경험 공유. tool layer 덕에 일관되게 개발 환경 유지 가능. 직접 bazel run을 써야 하는데, 좀 더 나은 자동화 방법을 궁금해함. 어떻게 동작하는지 알려주기를 요청.
-
2명 규모에서 마이크로서비스/K8s 패턴 자체가 오버엔지니어링. 이 정도 인원 규모에서는 어떤 방식이든 문제 없음. 예전엔 Dropbox/SVN/MS VCS 등 어떤 방식이든 다 돌아갔고(불편한 점은 있었지만), 다 문제가 되지 않았음. 이 규모에서는 모두가 전체 프로세스를 머릿속에 그릴 수 있음. 복잡한 도구나 인프라가 성공 요인이 아니라는 경험 공유.
-
-
지난 4년간 여러 회사에서 세 번이나 모노레포 설정 작업한 프리랜서 경험 공유. 프론트엔드에 한정해 JavaScript/TypeScript 생태계만 써서 그나마 관리 쉬움. 실제 좋은 모노레포는 내부적으로 폴리레포처럼 동작, 각각의 프로젝트가 독립적으로 개발/배포/호스팅 가능하지만 하나의 코드베이스에 공존하며 공통 구성요소(UI 등)도 자유롭게 공유, 일관된 룩앤필 보장. 실전 가이드로 참고 자료 추천.
- 이건 실제로 폴리레포가 아니라 모노레포를 제대로 구축한 사례임.
-
결국, 모든 것은 경우에 따라 다름. 우리 회사는 약 40여 개의 git 레포를 별도 CI로 관리, 빌드/테스트/패키징 후 최종적으로 통합 파일 시스템 이미지를 만들어 인티그레이션 테스트. 컴포넌트 간 Flatbuffers 메시지로 통신하며 flatbuffers도 서브모듈로 관리. 다운스트림 의존성 처리가 힘들긴 하나, progressive enhancement로 어느 정도 유연성 확보. 이런 경우 멀티레포인지, 서브모듈 많은 모노레포인지 진단 자체가 애매. 모노레포로 바꾸면 장점이 있을지는 미지수. 결국 트레이드오프와 감내할 불편의 종류 선택 문제.
-
모노레포 도구 관련 블로그 작성자 경험. 사람들이 모노레포의 장점만 강조하지만, 실제로 성공적인 모노레포 운영의 복잡함은 대부분 이면에서 devops/devtools 팀이 감당. 따라서 도입은 신중해야 하지만, 잘 구축하면 충분한 가치 제공.
-
잘 관리된 모노레포 경험은 너무 좋아서 다른 워크플로우로 되돌아가기 싫음. 하지만 준비 안 된 "우리도 모노레포 하자" 식은 악몽. 만약 준비된 모노레포 환경과 도구를 패키지로 팔면 비즈니스 기회가 클 거라 생각.
- NX가 이미 그런 사업을 하고 있음. 이전 스타트업에서 처음부터 NX로 개발해 15명 R&D팀으로도 100명 규모의 표준화를 실현. 새로운 회사(스타트업 인수한 곳)는 무계획 "우리도 모노레포" 시도로 참사. 지금 NX로 이관 중인데 효과 아주 좋음.
-
대형 조직에서 모노레포가 오히려 팀 간 의존성을 극도로 제한해 코드 재사용을 저하시킬 수도 있다는 점을 경험. 라이브러리 팀이 바꾸려면 하위 사용자 모두 업데이트해야 하는데, 예상치 못한 방식으로 사용하는 팀 때문에 수정이 복잡하게 꼬임(Hyrum's Law). 결국, 대기업은 내부 복붙, 포크, 엄격한 접근제어, 느린 변경 승인 등으로 귀결.
-
범용으로 활용할 라이브러리를 만들 땐 API 설계에 신중해야 함. 가능하면 API는 바꾸지 않고, 바꿔야 한다면 대규모 변경을 확실히 기획하거나 새 함수로 대체+구버전 deprecated 처리 권장. 소규모 코드라면 복붙도 괜찮음.
-
그래도 모노레포의 장점은 모든 사용처를 쉽게 찾고, 필요시 원자적으로 변경/수정 가능하다는 점.
-
모든 소프트웨어는 의존관계를 고려해야 하며, 모노레포는 오히려 라이브러리나 사용자 입장에 있어서 서로를 바꿀 권한이 증가.
-
모노레포에서는 내 상황에 맞게 변경이 쉬우므로 코드 재사용 확률이 폴리레포보다 높음.
-