모노레포 vs 폴리레포: 선택이 결정한다

요약

2026년 기준으로 공유 패키지를 쓰는 솔로 개발자에게는 모노레포가 최선입니다. 이 가이드는 실제 이득, polyrepo 승리 케이스, CI/CD 비용, AI 도구의 변화를 설명합니다.

어두운 IDE에서 git 저장소 구조를 보여주는 이중 모니터 개발자 워크스테이션, 창밖으로 보이는 타이베이 밤 풍경

모노레포 vs 폴리레포 논쟁은 새 프로젝트마다 나타난다. 그 답은 몇 달간 CI/CD 설정, 리팩터링 오버헤드, 팀 접근 제어를 좌우한다. 공유 코드를 가진 솔로 개발자 대부분에게 2026년 최적의 선택은 모노레포다. 트렌드라서가 아니라, 두 패키지가 서로 통신하는 순간부터 폴리레포가 강제하는 배포 의식을 없애기 때문이다. 서비스가 완전히 독립적이고 절대 코드를 공유하지 않는다면, 폴리레포가 더 간단하다. 나머지는 모두 상황에 따라 다르다.

밤 10시다. 새 프로젝트가 형태를 잡고 있다. 백엔드 API, 공유 TypeScript 타입 패키지, 둘 다 쓸 프론트엔드 대시보드. 탭이 두 개 열려 있다. 커서가 깜빡인다.

이 주제에 관한 대부분의 글은 Bazel 설정을 즐기는 DevOps 엔지니어가 있는 20명 규모 팀을 염두에 두고 쓰인다. 이 글은 첫 커밋 전에 선택해야 하는 빌더를 위한 것이다. 실사용자가 있고 근육기억이 기억한 CI 파이프라인이 있는 6개월 후 구조를 바꾸는 것은 사이드 프로젝트의 모멘텀을 영구적으로 죽이는 종류의 일이기 때문이다.

모노레포가 실제로 주는 이득 (교과서 버전 아님)

표준 설명은 "한 저장소, 공유 의존성, atomic 변경"이다. 다 맞다. 하지만 솔로 빌더가 매일 마주치는 현실적인 부분은 더 구체적이다. 로컬에서 패키지를 쓸 때 배포할 필요가 없다는 것이다.

폴리레포 설정에서 shared-utils를 고쳐야 하고 그게 api-service에도 영향을 미친다면, shared-utils의 새 버전을 배포하거나 api-service의 의존성을 올리고 CI가 끝나고 배포할 때까지 기다린다. 아니면 npm link 해킹에 의존한다. 모노레포에 워크스페이스가 있으면 코드를 바꾸기만 하면 된다. 모든 패키지가 그 변경을 즉시 본다. 배포 의식이 없다.

Pnpm 워크스페이스에서 어떻게 보이는지, 최소한의 설정 오버헤드만 있는 방식:

/packages
  /shared-types      ← api와 web에서 직접 import
  /api
  /web
package.json         ← "workspaces" 필드가 있는 워크스페이스 루트
// api/package.json
{
  "dependencies": {
    "@myapp/shared-types": "workspace:*"
  }
}

배포 없음. 개발 중 버전 올림 없음. workspace:*는 로컬 패키지로 해결되고, TypeScript의 프로젝트 참조는 전체 그래프를 통한 증분 컴파일을 제공한다.

두 번째 실제 이득: 패키지 간 리팩터링이 한 PR에 담긴다. shared-types의 인터페이스 이름을 바꾸면? TypeScript가 같은 코드베이스의 모든 깨진 소비자를 같은 편집기 세션에서 보여준다. 폴리레포에서는 저장소 A에서 이름을 바꾸고, 새 버전을 배포하고, 3일 후 동료가 npm install을 실행할 때 저장소 B에서 타입이 런타임과 맞지 않는 걸 발견한다.

세 번째 이득으로, 솔로 개발자가 과소평가하는 것: 도구 설정 한 곳. 한 개의 .eslintrc, 한 개의 prettier.config.js, 한 개의 CI 워크플로우 파일. 변경마다 작은 절약이지만, 몇 개월 반복하면 누적된다.

모노레포가 주지 않는 것을 이름 붙일 가치가 있다. 서비스 결합도를 낮추지 않는다. 실제로 독립적인 서비스를 어쨌든 모노레포에 넣으면, 코드베이스가 갖지 않은 결합도를 위해 조정 오버헤드를 더했다. 모노레포의 이득은 서비스가 실제로 코드를 공유하거나 함께 변경해야 할 때만 나타난다. 저장소 구조는 의존성 구조를 반영해야 하지, 강요해서는 안 된다.

모노레포 단일 트리 vs 폴리레포 여러 박스를 비교하는 추상 시각화

폴리레포가 제 역할을 하는 경우

폴리레포는 실수가 아니다. 특정 상황에서 올바른 선택이고, 그렇게 안 하면 25분이 걸리는 40개 서비스 모노레포가 된다.

명확한 경우: 소유권, 배포 주기, 준수 요건이 정말 다른 서비스들. 결제 서비스가 PCI 범위에 있고 마케팅 사이트는 아니라면, 저장소를 분리하면 접근 제어, 감시 로그, 폭발 반경을 깨끗하게 분리한다. 그건 운영 오버헤드가 아니다. 그건 기능이다.

폴리레포는 코드의 일부를 오픈소스할 때도 이긴다. 전용 공개 저장소는 외부 기여자가 프라이빗 인프라를 범위에 끌어들이지 않고 포크하고 PR할 수 있게 한다. GitHub의 저장소 수준 권한 모델은 규모에서 경로 기반 접근 격리를 주지 않는다. 분리된 저장소가 제대로 처리한다.

그리고 완전히 다른 기술 스택의 순수 마이크로서비스: Go 서비스와 코드 수준에서 아무것도 공유하지 않는 React Native 앱. 모노레포는 이득 없이 조정 비용을 더한다. 공유할 패키지가 없으면 공유할 게 없다.

솔직한 버전: 폴리레포로 시작한 대부분의 인디 프로젝트는 결국 후회한다. 폴리레포가 나쁜 아키텍처라서가 아니라 부품들이 독립적일 거라고 과대평가했기 때문이다. 프론트엔드가 항상 백엔드의 타입이 필요해진다. 워커가 API의 유틸이 필요해진다. 두 개 저장소가 모든 기능마다 네 개의 PR이 된다.

아무도 언급하지 않는 CI/CD 비용

모노레포는 실제 운영 비용이 있다. CI가 모든 커밋에서 모든 걸 naively 실행하면 자신과 무관한 빌드와 테스트를 위해 시간과 돈을 쓴다.

web 패키지에 CSS 조정을 푸시한다. CI는 api, worker, shared-types의 전체 테스트 스위트를 실행한다. 색상 변경을 위해 6분의 GitHub Actions 컴퓨트다. 규모에서 CI 큐가 전체 팀을 막는다.

해결책이 존재하지만, 의도적인 설정이 필요하다. 의존성 그래프를 이해하는 빌드 오케스트레이션 도구다. Turborepo와 Nx 둘 다 풀이한다. Turborepo의 turbo.json은 변경 입력이 있는 패키지에만 각 작업을 실행하는 파이프라인을 정의한다:

{
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": ["dist/**"]
    },
    "test": {
      "dependsOn": ["build"],
      "cache": true
    }
  }
}

원격 캐싱이 활성화되면 (소 팀을 위한 Vercel의 Turborepo Cloud 무료 티어), 변경 안 된 패키지의 캐시 히트는 즉시다. 빌드 없음, 재테스트 없음. 빌드 아티팩트가 따뜻해진 후 사이드 프로젝트의 솔로 개발자에게 이건 대부분의 푸시에서 CI를 2분 이하로 유지한다.

비용: 필요하기 전에 Turborepo나 Nx를 배워야 한다. 대략 반나절 설정이다. 건너뛰고 CI가 살이 붙으면 모노레포 CI가 느려져서 전체 아키텍처 선택을 의심하기 시작한다. 그리고 보통 개발자는 폴리레포가 맞았다고 결정하는데, 실제 문제는 단지 누락된 설정 파일이었다.

모노레포 아키텍처를 위한 평행 빌드 작업과 배포 상태를 보이는 CI/CD 파이프라인 대시보드

AI 코딩 도구가 이 논쟁의 수식을 어떻게 바꿨는가

최근까지, 폴리레포를 위한 한 가지 진정한 주장은 인지 부하였다. 작은 저장소가 이해하기 쉽다. 격리되기 때문이다. 새 서비스로 맥락 전환, 그 서비스와 관련된 것만 본다. 그 주장은 타당했다.

AI 코딩 도구가 그 계산을 바꾼다. Cursor, Claude Code, GitHub Copilot으로 작업할 때, 도구는 전체 코드베이스를 맥락에서 작업한다. 교차 서비스 의존성을 본다. 어느 인터페이스가 어느 서비스에 소비되는지 이해한다. 이름 바꾼 필드를 모든 소비자를 통해 따라가지만, 자신이 전체 의존성 그래프를 들고 있어야 한다. "격리된 저장소가 이해하기 쉽다"는 주장은 AI 어시스턴트가 어쨌든 전체 의존성 그래프를 들 때 크게 약해진다.

구체적 예: 폴리레포에서 API 엔드포인트를 리팩터하고 공유 타입도 영향을 미치게 AI 어시스턴트에 물으면, 보통 한 세션에서 두 저장소를 모두 볼 수 없다. 정확히 모노레포가 없애려던 오버헤드를 손으로 조정한다. 모노레포에서 같은 리팩터는 한 대화다.

이게 완전히 결정을 뒤집지는 않는다. 하지만 작은 팀과 솔로 개발자에게 폴리레포를 위한 역사적 정당화 하나를 제거하고, 코드가 정말 결합될 때 기본값을 모노레포로 약간 더 기울인다.

지금 저장소를 분리해야 할 세 가지 신호

모노레포로 시작했다. 좋다. 하지만 분리가 올바른 선택이 되었다는 구체적 신호들이 여기 있다:

접근 제어가 핵심이 되고 있다. 계약자는 프론트엔드 접근이 필요하지만 백엔드는 아니다. 파트너 통합 팀은 API 스키마를 읽어야 하지만 프라이빗은 아니다. GitHub의 Codeowners는 누가 어느 경로를 검토할 수 있는지 제한할 수 있지만, 읽기 접근을 제한하지 않는다. 준수나 보안을 위해 읽기 격리가 중요하면, 분리된 저장소가 깨끗한 답이다. Codeowners 체조는 저장소 수준 권한을 완전히 대체하지 않는다.

한 서비스의 CI 실패가 무관한 것의 배포를 막고 있다. payment-service의 깨진 테스트가 marketing-site에서 배포해야 할 긴급 수정을 막으면, 모노레포가 코드베이스가 갖지 않은 결합도를 만들고 있다. 빌드 설정을 고쳐야 한다 (Turborepo로 작업 범위를 고쳐 풀린다). 아니면 두 서비스가 정말 같은 저장소에 있지 않아야 한다.

한 부분이 오픈소스가 되고 다른 하나는 아니다. 이게 가장 깨끗한 분리 경우다. 오픈소스 조각을 자기 공개 저장소로 빼낸다. GitHub 모노레포에서 섞인 공개/프라이빗 코드는 정말 고통스럽다. 분리된 조직이나 영구 유지보수 오버헤드를 만드는 수동 가지치기가 필요하다.

대부분의 빌더가 착지하는 실전 설정

경험 있는 개발자 대부분이 정착한 답변: 제품 도메인당 한 개의 모노레포. "자신이 만든 모든 걸 위한 한 개의 모노레포"도 아니고 "패키지마다 한 개 저장소"도 아니다.

웹 프론트엔드, API, 공유 타입 라이브러리가 있는 SaaS를 만들고 있다면, 그게 한 제품이다. 한 모노레포에 유지한다. 다른 프로젝트가 쓰는 오픈소스 CLI 유틸을 유지한다면, 그건 다른 저장소고 다른 관객과 배포 주기가 있다.

실수는 모노레포/폴리레포 선택을 이데올로기적으로 대하는 것이다. "모노레포 팀" vs "폴리레포 팀"이 아니다. 세 질문에 기초한 구조 결정이다. 서비스 간의 의존성 그래프는 어떤가? 누가 뭐에 접근해야 하고, 격리가 중요한가? CI/CD 설정 복잡도에 대한 허용도는 어떤가?

노트북을 사용하는 솔로 개발자가 프로젝트 아키텍처 결정을 고려 중

사이드 프로젝트 구체적으로: pnpm 워크스페이스로 (npm 워크스페이스도 작동하지만, 덜 인체공학) 모노레포로 시작한다. CI가 정기적으로 4분 이상을 치고 있을 때만 Turborepo를 더한다. 구체적 이유가 있을 때만 다른 저장소로 분리한다. 오픈소스, 접근 제어, 준수, 또는 다른 배포 주기를 가진 파트너 팀. 아키텍처적으로 더 깨끗하게 느껴진다고 해서가 아니라.

모노레포 vs 폴리레포 논쟁은 그 선택 중 하나다. 대부분의 솔로 개발자에게 올바른 답은 같다. 간단하게 시작하고, 특정 마찰이 나타날 때 최적화한다. 커서가 여전히 깜빡인다. 첫 커밋을 배포한다.

자주 묻는 질문

AI 코딩 도구 (Cursor나 GitHub Copilot)에 모노레포가 더 좋은가?
일반적으로 그렇다. AI 코딩 어시스턴트는 한 컨텍스트 윈도우에서 전체 의존성 그래프를 볼 때 최고로 작동한다. 모노레포에서 Cursor 같은 도구는 공유 타입에서의 변경을 한 세션의 모든 소비자까지 추적할 수 있다. 폴리레포에서는 교차 저장소 컨텍스트가 없거나 수동 설정이 필요해서 조정을 다시 자신에게 넘긴다.
Turborepo와 Nx의 실제 차이는 뭔가?
둘 다 의존성 그래프 분석으로 변경 안 된 패키지의 빌드를 건너뛰는 모노레포 빌드 오케스트레이션 도구다. Turborepo는 설정이 더 단순하고 JavaScript/TypeScript 프로젝트에 잘 작동한다. Nx는 내장 생성기, 프로젝트 그래프 UI, 다중 언어 지원으로 더 풍부하다. 사이드 프로젝트에는 보통 Turborepo가 올바른 시작점이다.
나중에 폴리레포에서 모노레포로 마이그레이션할 수 있나?
그렇다. 하지만 충격이 충분해서 의도적으로 해야 한다. 프로세스는 각 패키지를 워크스페이스 구조로 옮기고, import를 업데이트하고, CI 파이프라인을 조정하고, 선택적으로 git subtree나 git-filter-repo를 써서 git 히스토리를 다시 쓰는 것을 포함한다. 이걸 한 대부분의 팀은 가치 있었다고 말하지만, 의미 있는 크기의 프로젝트에는 최소 2-3일을 예산하라.
큰 기업들이 모노레포를 쓰나?
Google, Meta, Microsoft, Twitter는 모두 역사적으로 메인 코드베이스를 위해 모노레포를 썼다. Uber의 iOS와 Android 팀은 교차 서비스 조정 오버헤드를 줄이기 위해 모두 모노레포로 전환했다. 그렇다고 그 회사들이 쓰는 도구 (Bazel, Buck, Pants)는 솔로 개발자가 필요한 것보다 훨씬 복잡하다. Turborepo와 pnpm 워크스페이스는 인프라 오버헤드 없이 이득의 95%를 제공한다.
모노레포가 Vercel이나 다른 플랫폼으로의 배포에 영향을 주나?
대부분의 현대 플랫폼은 모노레포를 기본으로 다룬다. Vercel은 프로젝트마다 루트 디렉터리를 지정할 수 있어서 packages/web을 배포하면서 빌드 명령을 모노레포 루트에 지정할 수 있다. Netlify와 Railway는 비슷한 지원이 있다. 설정은 약 10분 걸리고 이미 있는 것 이상의 특수 인프라가 필요 없다.
솔로 개발자가 첫 날부터 Turborepo를 설정해야 하나?
꼭 필요하지는 않다. pnpm 워크스페이스로 공유 패키지 이득부터 시작한다. CI가 정기적으로 3-4분 이상 걸리거나 원격 캐싱으로 로컬 빌드를 빠르게 하고 싶을 때 Turborepo를 더한다. 기존 워크스페이스 설정에 Turborepo를 더하는 건 대략 30분이니까, 기다려도 자신을 잠그지 않는다.