콘텐츠로 이동
Study Note웹 개발 일반

9. 모노레포

모노레포는 “큰 회사 것”이 아니다 — 패키지 두 개가 타입 하나를 공유하는 순간 시작된다

이 장에서 처음 나오는 말3개
모노레포Monorepo
여러 패키지(프론트·백·공유 코드)를 저장소 하나에서 관리하는 방식. 반대는 패키지마다 저장소를 두는 멀티레포(폴리레포)다.
워크스페이스Workspace
패키지 매니저가 "이 저장소 안의 이 폴더들은 각각 패키지다"라고 인식하는 단위. pnpm에선 pnpm-workspace.yaml이 그 선언이다.
태스크 러너Task Runner
여러 패키지의 build·test를 의존 순서에 맞게, 안 바뀐 것은 건너뛰며 돌려 주는 도구. Turborepo, Nx.

프론트(Next.js)와 백(NestJS)을 별도 저장소로 운영하면 반드시 이 사고가 난다 —

  1. 백엔드가 API 응답의 필드를 이름 바꾸거나 제거하고 배포한다
  2. 프론트 저장소는 그 사실을 모른다 — 타입 정의가 서로 딴 파일이니까
  3. 컴파일은 통과하고, 런타임에 undefined가 화면에 뜬다

같은 저장소에 두고 타입·스키마를 한 곳에서 공유하면 이 부류의 사고를 컴파일 타임에 잡힌다. API 형태를 바꾸는 커밋이 프론트 수정까지 한 커밋에 담기게 되는 것 — 이게 모노레포의 본질 가치다.

pnpm workspace — 선언 두 개면 된다

섹션 제목: “pnpm workspace — 선언 두 개면 된다”

pnpm이 모노레포에 잘 맞는 이유가 이 단순함이다(5장에서 본 엄격한 의존성 구조도 함께 얻는다).

  • 디렉터리my-service/
    • pnpm-workspace.yaml “apps/, packages/ 가 각각 패키지다”라는 선언
    • package.json 루트 — 공통 스크립트와 개발 도구만
    • 디렉터리apps/
      • 디렉터리web/ Next.js — shared를 참조
        • package.json
      • 디렉터리api/ NestJS — shared를 참조
        • package.json
    • 디렉터리packages/
      • 디렉터리shared/ 타입 · 스키마 · 상수 — 공유의 핵심
        • package.json
pnpm-workspace.yaml
packages:
- 'apps/*'
- 'packages/*'
// apps/web/package.json — 내부 패키지 참조
{
"dependencies": {
"@my-service/shared": "workspace:*"
}
}

workspace:*는 “npm 저장소가 아니라 이 저장소 안의 shared를 링크하라”는 프로토콜이다. shared를 고치면 web과 api에 즉시 반영된다 — 버전 올리고 배포하고 다시 설치하는 왕복이 없다.

Turborepo — 패키지가 늘면 필요해지는 것

섹션 제목: “Turborepo — 패키지가 늘면 필요해지는 것”

workspace만으로 부족해지는 지점은 빌드 순서와 반복이다. shared → api → web 순서를 지켜야 하고, 안 바뀐 패키지도 매번 다시 빌드하게 된다.

Turborepo는 그 위에 두 가지를 얹는다 —

shared 빌드가 끝나야 api 빌드와 web 빌드가 돌아가고, 안 바뀐 api는 캐시에서 가져와 건너뛰는 모노레포 빌드 그래프
  • 태스크 그래프 — turbo.json에 “build는 의존 패키지의 build 뒤에”를 선언하면 순서와 병렬화를 알아서 푼다
  • 캐싱 — 입력(소스·설정)이 안 바뀐 태스크는 이전 결과를 재사용한다. CI 시간이 여기서 크게 줄어든다
상황판단
앱 하나뿐모노레포 불필요 — 폴더 구조로 충분하다
프론트 + 백이 타입을 공유workspace 도입 시점. 이것만으로 충분한 경우가 많다
패키지 4~5개 이상, 빌드가 느려짐Turborepo(또는 Nx)를 얹는다
배포Vercel 등은 모노레포를 인식한다 — 앱별 Root Directory 지정, 바뀐 앱만 빌드

참고 — 파일 이름만 보고 모노레포로 단정하지 않기

섹션 제목: “참고 — 파일 이름만 보고 모노레포로 단정하지 않기”

혼동 방지용 각주 하나. 이 스터디 사이트 레포도 pnpm을 쓰던 시절 pnpm-workspace.yaml을 갖고 있었지만 모노레포여서가 아니었다 — pnpm의 보안 기본값(5장의 설치 스크립트 차단)에서 sharp·esbuild만 허용하는 allowBuilds 설정용이었다. 파일 이름이 같아도 쓰임은 다를 수 있다.

  • 모노레포의 본질 가치 — API 형태 변경이 한 커밋에 담기고, 타입 불일치가 컴파일에서 잡힌다
  • pnpm workspace는 선언 두 개 — pnpm-workspace.yaml + workspace:* 참조
  • 공유 패키지의 일순위는 타입과 검증 스키마 — 프론트/백이 같은 코드로 검증하게 된다
  • Turborepo는 순서(태스크 그래프)와 반복(캐시)을 푼다 — 패키지가 늘어난 뒤의 도구
  • 모노레포 ≠ 모놀리스 — 저장소는 하나, 배포는 따로