TechFeedTechFeed
Startup / Product

8개 멀티레포를 Turborepo 모노레포로 합친 스타트업의 6개월 — 빌드 시간 72% 단축 기록

4년차 SaaS 스타트업이 8개 멀티레포를 Turborepo 기반 모노레포로 전환한 실제 과정. Lerna 실패 이유, Turborepo 도입 결정, 전환 중 막힌 지점 3가지, 6개월 후 CI 빌드 72% 단축 결과까지.

8개의 분리된 레포지토리를 운영하던 한 SaaS 스타트업 개발팀이 Turborepo 기반 모노레포로 전환한 뒤 CI 빌드 시간을 72% 줄이고, 공유 컴포넌트 배포 사이클을 8개 PR에서 1개 PR로 압축했다. 이 글은 그 팀이 6개월 동안 겪은 시행착오와 실제 수치를 3인칭으로 정리한 케이스 스터디다.

멀티레포를 운영 중이고 빌드 속도 문제나 패키지 의존성 충돌로 고통받고 있다면, 또는 모노레포 전환을 고민하고 있다면 이 팀의 경험이 판단 기준이 될 수 있다.

이 팀이 마주한 문제 — 8개 레포, 하나의 코드베이스

이 스타트업은 창업 4년차에 접어든 B2B SaaS 팀이다. 제품은 하나지만 코드베이스는 여러 해에 걸쳐 8개의 독립 레포지토리로 쪼개졌다. 프론트엔드 앱 3개(고객용 웹, 관리자 대시보드, 내부 운영 툴), 백엔드 서비스 3개(API 서버, 인증 서비스, 알림 서버), 그리고 공유 UI 컴포넌트 패키지와 공유 유틸리티 패키지 레포 2개로 구성된 구조였다.

팀 규모는 개발자 12명. 각 레포마다 CI 파이프라인이 독립적으로 운영되고 있었고, 코드 리뷰 채널도 사실상 8개가 병렬로 돌아가고 있었다.

핵심 문제는 공유 패키지였다. 예를 들어 공유 UI 컴포넌트의 버튼 스타일 하나를 수정하면, 이를 사용하는 3개 프론트엔드 앱 각각에서 패키지 버전을 올린 PR을 따로 생성하고, 별도로 리뷰받고, 별도로 배포해야 했다. 단순 색상 변경 하나에 최소 4개의 PR이 필요했고, 팀원들은 "버튼 색 바꾸는 데 하루가 걸린다"는 말을 반복했다고 한다.

이 팀의 시니어 엔지니어는 당시 상황을 이렇게 표현했다. "코드는 하나의 제품인데, 저장소는 8개 회사처럼 운영되고 있었다."

8개 멀티레포 구조 다이어그램
전환 전: 8개의 독립 레포지토리가 각각의 CI 파이프라인을 운영하던 구조

팀이 특히 고통받은 지점은 두 가지였다. 첫째, 의존성 버전 드리프트. 공유 유틸리티 패키지가 세 가지 다른 버전으로 서로 다른 앱에서 사용되다 보니, 버그가 어느 버전에서 발생했는지 추적하는 데만 수 시간이 소요됐다. 둘째, CI 비용. 8개 파이프라인이 각각 전체 빌드를 수행하면서 월 CI 시간이 800분을 넘겼다.

시도 1: Lerna로 해결하려 했으나 실패한 이유

팀은 2023년 초, 첫 번째 해결책으로 Lerna v3를 도입했다. 당시 팀 내에서 Lerna에 대한 경험이 있는 엔지니어가 있었고, 모노레포 전환 사례 자료도 Lerna 기반이 많았다. 마이그레이션은 약 3주 만에 완료됐고 팀은 기대감이 높았다.

그러나 문제는 곧 드러났다. Lerna의 의존성 호이스팅이 일부 패키지와 충돌을 일으켰다. 특히 두 개의 프론트엔드 앱이 동일 패키지의 서로 다른 버전을 필요로 하는 경우, 호이스팅이 예상과 다르게 작동하면서 런타임 에러가 발생했다.

주의: Lerna는 여전히 유효한 도구다. 그러나 빌드 캐시 기능 없이는 CI 속도 문제를 근본적으로 해결할 수 없다. 이 팀의 경우, Lerna 도입 후 오히려 CI 빌드 시간이 평균 22분으로 늘어났다 — 모든 패키지를 순차적으로 빌드하는 구조 때문이었다.

더 큰 문제는 버전 관리였다. Lerna의 fixed 모드는 모든 패키지 버전을 동일하게 올려버렸고, independent 모드는 각 패키지 버전을 수동으로 관리해야 해서 12명의 팀에서 일관성을 유지하기 어려웠다. 결국 이 팀은 Lerna 도입 3개월 만에 재검토에 들어갔고, Turborepo로 방향을 바꿨다.

Turborepo 도입 결정 — 무엇이 달랐나

팀이 Turborepo를 선택한 이유는 세 가지로 정리된다.

  • 빌드 캐시: 변경되지 않은 패키지는 캐시된 결과를 재사용한다. Turborepo 공식 벤치마크 기준, 캐시 히트 시 빌드 시간을 최대 90%까지 줄일 수 있다.
  • 파이프라인 병렬화: 의존성 그래프를 분석해 독립적인 태스크를 병렬로 실행한다. Lerna의 순차 빌드와 달리 실제 의존 관계가 없는 패키지들은 동시에 빌드된다.
  • pnpm workspaces 통합: 기존에 npm을 사용하던 일부 레포를 pnpm으로 통일하면서 의존성 호이스팅 문제를 pnpm의 엄격한 격리 모드로 해결할 수 있었다.

팀은 6주짜리 마이그레이션 로드맵을 세웠다. 1~2주차에 레포 통합 및 pnpm-workspace.yaml 구성, 3~4주차에 turbo.json 파이프라인 설정 및 로컬 빌드 검증, 5~6주차에 CI 파이프라인 전환 및 Vercel Remote Cache 연결. 실제로는 7주가 걸렸다고 한다.

turbo.json — 이 팀이 사용한 기본 파이프라인 설정
{ "$schema": "https://turbo.build/schema.json", "pipeline": { "build": { "dependsOn": ["^build"], "outputs": [".next/**", "!.next/cache/**", "dist/**"] }, "lint": { "outputs": [] }, "test": { "dependsOn": ["build"], "outputs": [] }, "dev": { "cache": false, "persistent": true } } }
모노레포 디렉토리 구조 (전환 후)
monorepo/ ├── apps/ │ ├── web/ # 고객용 Next.js 앱 │ ├── admin/ # 관리자 Next.js 앱 │ └── api/ # Express API 서버 ├── packages/ │ ├── ui/ # 공유 UI 컴포넌트 │ ├── config/ # 공유 설정 (ESLint, TypeScript) │ └── utils/ # 공유 유틸 함수 ├── turbo.json └── pnpm-workspace.yaml
Turborepo 파이프라인 병렬화 시각화
Turborepo의 태스크 그래프: 의존 관계가 없는 패키지는 병렬로 빌드된다

전환 과정 — 막혔던 지점 3가지

전환이 순조롭지만은 않았다. 팀이 실제로 막혔던 지점 세 가지는 다음과 같다.

문제 1 — Next.js App Router와 shared packages 타입 충돌: 공유 UI 패키지가 React.FC 타입을 export할 때 Next.js App Router 환경에서 use client 지시어 없이 사용하면 TypeScript 타입 에러가 발생했다. 해결책은 패키지 진입점에 "use client"를 명시하거나, Server Component와 Client Component용 진입점을 분리하는 것이었다. 이 팀은 packages/ui/src/server.tspackages/ui/src/client.ts로 진입점을 나누는 방식을 택했다.
문제 2 — CI에서 원격 캐시 설정: 로컬에서는 캐시가 잘 작동했지만, CI 환경(GitHub Actions)에서는 캐시가 무효화됐다. Vercel Remote Cache를 사용하려면 TURBO_TOKENTURBO_TEAM 환경변수를 GitHub Secrets에 등록해야 한다. 이 설정 없이는 CI 빌드마다 전체 캐시가 초기화되어 캐시 효과가 없었다. Self-hosted 원격 캐시 대안으로는 turborepo-remote-cache 오픈소스가 있다.
문제 3 — 팀원 로컬 환경 불일치: Node.js 버전이 팀원마다 달랐다(v18~v22 혼재). 특정 패키지가 Node.js 버전에 따라 빌드 결과가 달라지는 현상이 발생했다. 이 팀은 루트에 .nvmrc 파일을 추가하고 CI에서도 동일 버전을 강제했다. pnpm 버전 통일에는 package.jsonengines 필드와 packageManager 필드를 함께 사용했다.
pnpm-workspace.yaml + package.json engines 설정
# pnpm-workspace.yaml packages: - 'apps/*' - 'packages/*' # 루트 package.json (일부) { "engines": { "node": ">=20.0.0", "pnpm": ">=9.0.0" }, "packageManager": "pnpm@9.1.0" }

6개월 후 결과 — 수치로 본 변화

전환 완료 후 6개월이 지난 시점에서 이 팀이 측정한 수치들이다. Turborepo 원격 캐시 히트율이 안정적으로 유지되는 시점(전환 후 약 4주)을 기준으로 비교했다고 한다.

  • CI 빌드 시간: 평균 18분 → 5분 (72% 단축). 최장 빌드는 34분에서 9분으로 줄었다.
  • 공유 컴포넌트 배포 사이클: 8개 레포에 각각 PR → 단일 PR 1개. PR 생성부터 프로덕션 반영까지 평균 소요 시간이 2일에서 4시간으로 단축됐다.
  • 월 CI 사용 시간: 약 800분 → 약 180분. GitHub Actions 비용이 약 75% 감소했다.
  • 온보딩 시간: 신규 팀원이 첫 PR을 올리기까지 걸리는 시간이 평균 3일에서 1일로 단축됐다. 레포 8개를 각각 클론하고 환경 설정하는 과정이 사라졌기 때문이다.
CI 빌드 시간 비교 그래프
전환 전후 CI 빌드 시간 추이 — 원격 캐시 히트율이 안정화되는 4주차부터 효과가 두드러진다

이 팀이 모노레포 전환 전에 알았으면 좋았을 것들

이 팀의 테크리드는 6개월이 지난 시점에서 회고 문서를 작성했다. 핵심 내용 세 가지를 정리하면 다음과 같다.

이 팀의 전환은 Turborepo가 만능 해결책이어서 성공한 게 아니었다. 패키지 경계 설계, 원격 캐시 설정, 팀 환경 표준화라는 세 가지 기반 작업이 선행됐기 때문이었다. Turborepo는 그 기반 위에서 빌드 시간과 배포 사이클을 줄여준 도구였다.

참고 자료: Turborepo 공식 문서, pnpm Workspaces 가이드

Turborepo모노레포멀티레포pnpm workspacesCI/CD빌드 최적화LernaNext.js스타트업개발 생산성

관련 포스트

Vercel 청구서 $1,243을 $78로 줄인 방법 — Next.js 미들웨어 matcher, Server Component 직접 쿼리, ISR 캐싱2026-04-20스타트업 기술 스택 선택 가이드 2026 — 0에서 PMF까지, 단계별 아키텍처 결정 프레임워크2026-04-22Resend vs SendGrid vs AWS SES 2026 — 스타트업 트랜잭션 이메일 서비스 실전 비교2026-04-18개발자 포트폴리오 사이트 만들기 20262026-03-12