Turborepo로 모노레포 적용하기
개발 중인 프로젝트에 모노레포 툴인 Turborepo를 적용하면서 생긴 경험과 느낀 점들을 공유해보고자 이번 포스팅을 준비했습니다.
들어가며: 프로젝트 확장과 함께 찾아온 고민
프로젝트의 개선작업을 진행하면서 스토리북과 테스트를 작성하고 확장하는 단계로 추후 서버 개발까지 고려하는 중이었습니다. 프로젝트 확장 단계에서 기존 단일 레포지토리 구조로 작업할 경우 한계가 있을 수 있다는 우려가 들었습니다.
예를 들어:
- 공통 컴포넌트 패키지 요소와 유틸, 설정 등을 공통 요소로서 분리하여 관리하면 좋겠다고 생각했습니다.
- 또 추가적으로 개발하게 될 서버 코드도 기존 레포지토리에 통합하여 관리하고 싶었습니다.
- 그리고 전체 빌드나 테스트 실행 구성을 효율적으로 개선하려는 고민도 했습니다.
이때 관심을 가졌던 모노레포 구조를 적용하면 프로젝트의 확장 작업을 진행하며 경험하고 배우기에 좋다고 생각했습니다.
모노레포 도입 배경과 Turborepo 선택 이유
먼저 모노레포란?
모노레포는 하나의 레포지토리에서 여러 프로젝트나 패키지를 관리하는 개발 방식입니다. 이를 통해 코드 재사용성을 높이고, 개발 환경을 일관되게 유지하며, 의존성 관리를 용이하게 할 수 있다는 장점이 있습니다.
- 코드 공유의 편리함: 공통 로직이나 UI 컴포넌트를 패키지로 분리하여 여러 프로젝트에서 쉽게 활용할 수 있습니다.
- 개발 환경 일관성 확보: 린트, 테스트, 빌드 설정을 중앙에서 관리하여 모든 패키지가 동일한 기준을 따르도록 할 수 있습니다.
- 효율적인 의존성 관리: 프로젝트 전체의 의존성을 한 곳에서 파악하고 관리하여 버전 충돌 등의 문제를 줄일 수 있습니다.
Turborepo를 선택한 이유: 속도와 단순성
모노레포 도입을 위해 여러 도구를 검토했습니다. 그중 모노레포 툴로 가장 많이 적용하는 툴, Nx와 Turborepo를 주로 비교했습니다.
- Nx: 다양한 플러그인과 상세한 설정 옵션을 제공하여 대규모 프로젝트 관리에 적합해 보였습니다.
- Turborepo: 상대적으로 기능은 적지만 핵심에 집중했고, 특히 빌드 속도와 간결한 설정 방식이 장점이었습니다.
제 프로젝트의 현재 규모를 고려했을 때, Nx의 모든 기능이 필요하지는 않다고 판단했습니다. Turborepo의 간결함과 빠른 빌드 성능이 더 실질적인 이점을 제공할 것이라고 생각했습니다. 최소한의 설정으로 효율을 높일 수 있다는 점이 개인 프로젝트 환경에 더 적합하다고 보았습니다.
모노레포 구조
먼저 프로젝트 코드를 실행 가능한 애플리케이션을 모아두는 apps 디렉토리와 여러 앱에서 재사용될 코드를 모아두는 packages 디렉토리로 구분했습니다.
apps는 frontend와 backend로 구분했고, packages 안에는 eslint, testing, storybook 등 공통 설정과 store, ui 컴포넌트와 같은 재사용 가능한 코드 패키지로 구분하여 적용했습니다.
├── apps/
│ ├── frontend/ # 프론트엔드 애플리케이션 (Next.js)
│ └── backend/ # 백엔드 애플리케이션 (예정)
└── packages/
├── eslint-config/ # 공통 ESLint 설정
├── testing-config/ # 공통 테스트 설정
├── storybook-config/ # 공통 Storybook 설정
├── store/ # 상태 관리 로직 (Zustand)
└── ui/ # 공통 UI 컴포넌트
Turborepo 적용 과정과 이슈
이슈 1: 패키지별 최적 빌드 설정 구성
공통 코드를 packages로 옮기고 CI/CD 파이프라인을 구축하는 과정에서 각 패키지의 특성에 맞는 빌드 설정을 구성하는 데 어려움이 있었습니다.
초기에는 대부분의 패키지에 tsc (TypeScript Compiler)를 사용해 빌드를 시도했습니다. 하지만 storybook-config 패키지에서 CSS 파일을 임포트하는 부분에서 tsc가 이를 제대로 처리하지 못하는 문제가 발생했습니다. tsc는 주로 타입스크립트 컴파일에 중점을 두기 때문이었습니다. 이 문제를 해결하기 위해 tsup이라는 번들러를 도입했습니다. tsup은 유연한 설정과 빠른 빌드 속도를 제공하여 CSS 파일 처리 문제를 해결할 수 있었습니다.
또한, packages/ui는 공통 UI 라이브러리로서 독립적인 관리가 필요하다고 판단했습니다. apps/frontend (Next.js 프로젝트)에 종속적으로 빌드하기보다는, React와 Storybook 환경을 고려하여 vite를 빌드 도구로 선택했습니다.
이 과정을 통해 각 패키지의 역할과 환경에 맞는 빌드 도구 및 설정을 적용하는 것이 중요하다는 점을 알게 되었습니다. 일괄적인 방식보다는 각 패키지의 독립성을 유지하며 최적화하는 것이 효율적이었습니다.
이슈 2: npm에서 pnpm으로 전환 후 의존성 문제 직면
초기에는 npm으로 Turborepo 환경을 구성했으나, pnpm의 효율적인 디스크 공간 사용과 엄격한 의존성 관리라는 장점을 고려하여 패키지 매니저를 pnpm으로 전환했습니다.
전환 후 pnpm install을 실행하자, npm 환경에서는 발생하지 않던 모듈 임포트 에러가 발생했습니다. 예를 들어, packages/ui에만 설치했던 clsx 라이브러리를 apps/frontend에서 별도 설치 없이 참조하던 코드가 pnpm 환경에서는 에러를 일으켰습니다.
이는 팬텀 디펜던시(Phantom Dependency) 문제였습니다. npm은 의존성을 호이스팅하여 상위 node_modules로 끌어올리기 때문에, 명시적으로 설치하지 않은 의존성도 접근 가능한 경우가 발생합니다. 이는 잠재적인 오류 가능성을 내포합니다.
pnpm은 이러한 팬텀 디펜던시를 허용하지 않고, 각 패키지가 명시적으로 선언한 의존성만 참조하도록 엄격하게 관리합니다. 따라서 에러가 발생한 부분에 필요한 의존성을 각 패키지에 직접 설치하여 문제를 해결했습니다. 이 경험을 통해 팬텀 디펜던시의 위험성과 pnpm의 엄격한 의존성 관리 방식의 이점을 분명하게 이해할 수 있었습니다.
Turborepo 적용을 통해 얻은 이점들
빌드 속도 개선 효과
Turborepo를 통해 모노레포를 구성했고 증분 빌드, 병렬 실행등으로 빌드 과정의 효율이 개선되었습니다.
- 증분 빌드: 변경된 코드와 그에 영향을 받는 패키지만 재빌드 합니다.
- 병렬 실행: 의존성이 없는 태스크는 동시에 실행합니다.
로컬 뿐만 아니라 CI/CD 환경에서 빌드 과정에서도 캐싱 기능을 활용하여 효율적으로 개선하려고 했습니다.
Vercel에서는 리모트 캐싱 기능을 지원하여 CI/CD 환경에서도 빌드 작업을 캐싱하여 시간을 줄일수 있었습니다.


| 조건 | 빌드 시간 |
|---|---|
| 리모트 캐싱 적용 전 | 47.33s |
| 리모트 캐싱 적용 후 | 2.74s |
실제 CI/CD 환경에서 빌드 속도를 비교한 결과, 빌드 시간이 47.33s → 2.74s로 줄었습니다. 이는 약 94.2% 단축된 것이고, 적용 후 빌드 시간은 기존 대비 약 5.8% 수준이었습니다.
효율적인 패키지 확장
Turborepo 기반 모노레포 환경에서 ESLint, TypeScript 설정 등 공통 개발 도구 및 설정을 내부 패키지 형태로 중앙 관리하고 재활용했습니다.
이를 통해 새로운 Node.js 서버를 추가할 때 일관성있는 개발 환경을 빠르게 구축하고 설정 시간을 줄여, 효율적으로 패키지를 확장하며 개발 생산성을 높일 수 있었습니다.
추후 현업에서 기대되는 점들
- 개발팀 협업 효율 증대: 공통 설정 및 컨벤션 공유를 통해 코드 이해도를 높이고 설정 차이로 인한 혼란을 줄여 효율적인 협업이 가능할 것으로 예상되었습니다.
- 서비스 안정성 향상: pnpm의 overrides 기능 등을 활용해 핵심 라이브러리 버전을 관리하고 의존성 충돌 가능성을 낮춰 서비스 안정성을 높일 수 있습니다.
- 신속한 서비스 제공 기여: Turborepo의 빠른 빌드 속도는 CI/CD 효율성을 높여 새로운 기능이나 개선 사항을 사용자에게 더 빠르게 배포하는 데 도움이 될 것이라 생각했습니다.
마무리하며
이번 사이드 프로젝트에 Turborepo를 적용하면서 모노레포 구조의 장점을 직접 경험하고, 실제 구축 과정에서 마주하는 현실적인 이슈들과 이를 해결하는 과정을 통해 많은 것을 배우는 성장의 기회가 되었습니다.
모노레포와 Turborepo는 프로젝트 규모가 커지거나 여러 팀이 협업하는 환경에서 유용하게 활용될 수 있는 도구라고 생각됩니다.
마지막으로 이 글이 모노레포 도입을 고려하는 분들께 참고가 되기를 바라면서 마무리 하겠습니다.