모노레포 구축기: Turborepo와 pnpm으로 생산성 폭발시키기 (feat. 5년차 프론트엔드 개발자의 실전 가이드)
모노레포 구축에 대한 실제 경험을 바탕으로, Turborepo와 pnpm을 활용한 현대적인 모노레포 구성 가이드를 제시합니다. 빌드 속도 최적화, 효율적인 의존성 관리, 그리고 팀 생산성 향상에 기여하는 핵심 전략을 코드 예제와 비교 분석을 통해 깊이 있게 다룹니다.
안녕하세요, 5년차 프론트엔드 개발자입니다. 여러분의 팀은 혹시 여러 개의 프론트엔드 프로젝트(클라이언트 앱, 어드민, 마케팅 페이지, 공통 컴포넌트 라이브러리 등)를 각기 다른 레포지토리에서 관리하고 있지는 않으신가요? 매번 새로운 레포를 클론하고, 의존성을 다시 설치하고, 빌드 스크립트를 통일하고, 버전 관리에 애를 먹는 지루하고 반복적인 과정에 지치셨을 겁니다. 공통 컴포넌트 하나 수정했는데, 모든 프로젝트에서 수동으로 버전을 업데이트하고 배포해야 하는 지옥을 경험해보셨다면, 이제 모노레포(Monorepo)의 세계에 오신 것을 환영합니다. 특히, 고성능 빌드 시스템인 Turborepo와 효율적인 패키지 매니저 pnpm의 조합은 이러한 고통을 단번에 날려줄 강력한 해결책이 되어줄 것입니다. 이 글에서는 실제 프로젝트에 Turborepo를 도입하며 얻은 경험과 실질적인 팁을 공유하고자 합니다.
1. Monorepo, 왜 지금 다시 주목받는가?
모노레포는 이름 그대로 '하나의 레포지토리' 안에 여러 개의 프로젝트(패키지)를 담는 개발 방식입니다. 과거에는 Lerna 같은 툴이 있었지만, 설정의 복잡성이나 빌드 속도 문제로 외면받기도 했습니다. 하지만 최근에는 Nx, Turborepo와 같이 성능과 개발 경험을 극대화한 도구들이 등장하며 다시 각광받고 있습니다. 왜 그럴까요?
- 코드 공유 및 재사용성 증대: 공통 UI 컴포넌트, 유틸리티 함수, 타입 정의 등을
packages폴더에 모아두고 여러apps에서 쉽게 공유하고 사용할 수 있습니다.npm link같은 수동 작업 없이workspace:프로토콜로 간편하게 연결됩니다. - Atomic Changes: 하나의 변경사항이 여러 프로젝트에 영향을 미칠 때, 단일 커밋으로 모든 변경을 관리할 수 있어 일관성을 유지하기 쉽습니다. 예를 들어, 공통 컴포넌트 라이브러리의 API를 변경하더라도, 사용하는 모든 앱의 코드를 한 번에 수정하고 테스트하여 안정적으로 배포할 수 있습니다.
- 단일화된 Tooling 및 설정: ESLint, Prettier, TypeScript 설정 등을 최상위 레벨에서 관리하거나,
packages/config와 같은 형태로 공유하여 모든 프로젝트에 일관된 개발 환경을 제공합니다. - 간소화된 의존성 관리: pnpm의 워크스페이스 기능과 함께 사용하면, 중복되는 의존성을 최상위
node_modules에 한 번만 설치하여 디스크 공간을 절약하고 설치 시간을 단축할 수 있습니다.
2. Turborepo, 그 속도의 비결은?
모노레포의 장점은 알겠지만, 여러 프로젝트를 한 번에 빌드하면 느려지지 않을까 하는 걱정이 앞설 수 있습니다. 여기서 Turborepo의 진가가 발휘됩니다. Turborepo는 Vercel에서 개발한 고성능 빌드 시스템으로, 압도적인 속도를 자랑합니다. 그 비결은 다음과 같습니다.
- 증분 빌드(Incremental Builds) & 캐싱: Turborepo는 이전에 빌드했던 작업의 결과를 캐시합니다.
turbo run build명령을 실행하면, 변경된 파일이 없는 패키지는 다시 빌드하지 않고 캐시된 결과를 재사용합니다. 로컬 캐싱은 물론, Vercel Remote Caching을 통해 CI/CD 환경에서도 캐시를 공유하여 빌드 시간을 획기적으로 단축할 수 있습니다. - 병렬 실행(Parallel Execution): 의존성 그래프를 분석하여 독립적인 작업들을 동시에 병렬로 실행합니다. 예를 들어,
ui패키지에 의존하는web과docs앱이 있다면,ui패키지가 빌드된 후web과docs는 동시에 빌드될 수 있습니다. - 파일 시스템 감지(Filesystem Pruning): 변경된 파일만 정확하게 감지하여 불필요한 재빌드를 최소화합니다.
3. pnpm과의 시너지: 효율적인 의존성 관리
Turborepo와 pnpm은 모노레포 환경에서 최고의 시너지를 낼 수 있는 조합입니다. pnpm은 node_modules 구조를 효율적으로 관리하여 여러 이점을 제공합니다.
- 하드 링크 및 심볼릭 링크: pnpm은
node_modules에 패키지를 평면적으로 설치하지 않고, 전역 스토어에 패키지를 한 번만 저장한 후 하드 링크와 심볼릭 링크를 사용하여node_modules를 구성합니다. 이는 디스크 공간을 절약하고 설치 시간을 단축하며, 호이스팅(hoisting)으로 인한 문제를 줄여줍니다. - 워크스페이스(Workspace) 기능:
pnpm-workspace.yaml파일을 통해 모노레포 내의 여러 패키지를 통합 관리할 수 있습니다.package.json의dependencies에workspace:프로토콜을 사용하여 다른 패키지에 의존성을 선언할 수 있습니다.
4. 실전! Monorepo with Turborepo & pnpm 구축하기
이제 실제 프로젝트를 구축하는 과정을 살펴보겠습니다.
1단계: 모노레포 초기화
1mkdir my-monorepo 2cd my-monorepo 3pnpm init -w # pnpm workspace 초기화 4pnpm add -w turborepo # 최상위에 turborepo 설치
pnpm init -w 명령은 pnpm-workspace.yaml 파일을 생성하고 package.json에 "private": true를 추가합니다.
pnpm-workspace.yaml:
1packages: 2 - 'apps/*' 3 - 'packages/*'
2단계: 패키지 생성
apps 폴더와 packages 폴더를 만들고 그 안에 프로젝트들을 생성합니다.
1mkdir -p apps/web apps/docs 2mkdir -p packages/ui packages/config 3 4# 각 폴더에서 pnpm init 실행 또는 수동으로 package.json 생성 5cd apps/web && pnpm init 6cd ../../apps/docs && pnpm init 7cd ../../packages/ui && pnpm init 8cd ../../packages/config && pnpm init
3단계: 패키지 의존성 설정 (package.json)
apps/web이 packages/ui에 의존하고, packages/config가 tsconfig와 eslint-config를 제공한다고 가정해봅시다.
packages/ui/package.json:
1{ 2