시작하기 전에
이 글은 모노레포 도입기 시리즈의 2편입니다.
1편에서는 레거시 프로젝트들로 인한 문제 상황과 모노레포 도입 배경을 다뤘습니다.
👉 1편: 레거시 전쟁의 끝을 내러 왔다
이번 글에서는 모노레포 구축 과정에서 마주한 기술적 선택과 모노레포 도구 선택 시 고려해야 할 요소들을 다루어 볼 예정입니다.
지난 게시글 요약
지난 포스팅에서 아래와 같이 문제 상황과 목표를 정리했습니다.
문제 상황
- 노후화되고 통일되지 않은 노드 환경과 라이브러리 버전, 그리고 각기 다른 컨벤션으로 인해 더 이상 프로젝트 유지보수가 어려워졌습니다.
- UI 컴포넌트의 스타일 방식을 통일했으나, 통일된 UI 컴포넌트들을 각 프로젝트로 일일이 가져와 사용하고 있어 Storybook 레포의 UI 변경 사항을 프로젝트에 일일이 옮겨와 반영해야 했고, 혹여나 각각의 프로젝트에서 요구 사항이 조금씩 달라 조금씩 UI 컴포넌트를 수정할 경우 이를 모두 추적하기가 쉽지 않았습니다.
- 각 프로젝트의 공통 의존성과 환경을 모두 일일이 업그레이드 후 안정화한다고 하더라도, 그 이후의 의존성 및 환경 관리는 여전히 문제가 되는 상황입니다.
목표
- 안정된 LTS 노드 환경과 패키지 매니저 버전을 모든 프로젝트에서 공유하고, 이후 추가적인 업그레이드도 중앙에서 관리할 수 있도록 합니다.
- 여러 프로젝트에서 공통으로 사용하는 라이브러리 버전은 중앙에서 관리할 수 있도록 합니다.
- 공통 UI 컴포넌트의 경우 하나의 레포지토리에서 관리하고, 업데이트 시 추가적인 라이브러리 배포 없이 간단하게 import 하여 사용할 수 있도록 합니다.
- 코드 컨벤션, 린팅 등 기본적인 규칙들을 정하고, 모든 프로젝트에 동일하게 반영합니다.
- 모든 프로젝트에서 동일하게 사용하는 유틸함수, 타입 등을 공통 레포지토리에서 관리하고, 이를 모든 프로젝트에서 공유할 수 있도록 합니다.
- 위 모든 사항에 대한 변경 사항은 모든 레포지토리에 동일하게 반영될 수 있도록 합니다.
모노레포 구축 방식
모노레포를 구축하는 방식에는 크게 두 가지 방법이 있습니다.
- 패키지 매니저의 Workspace만으로 구축
NPM
,PNPM
,Yarn Berry
등의 Workspace 기능만 사용- 간단한 구조의 모노레포에 적합
- 기본적인 패키지 관리와 의존성 관리만 필요한 경우
- 패키지 매니저의 Workspace + 모노레포 매니저
- 패키지 매니저의 Workspace를 기반으로 하고 그 위에
Turborepo
,Nx
,Lerna
등을 추가로 사용 - 빌드 최적화, 캐싱 등 추가 기능이 필요한 경우
- 패키지 매니저의 Workspace를 기반으로 하고 그 위에

1. 패키지 매니저 워크스페이스
현재 패키지 매니저는 아래와 같이 크게 네 가지 종류가 있습니다.
- NPM
- Yarn Classic
- Yarn Berry
- PNPM
NPM(^7
), PNPM, Yarn Berry 등 주요 패키지 매니저들은 모두 Workspace 기능을 기본적으로 제공합니다. 하지만 각 패키지 매니저마다 특징과 모노레포를 구축하는 방식이 다르기 때문에, 단순히 Workspace 기능의 유무가 아닌 아래와 같은 요소들을 고려하여 패키지 매니저를 신중하게 선택해야 합니다.
- 기존 프로젝트 및 워크플로와의 호환성
- 의존성 관리 방식과 성능
- 학습 비용
- 프로젝트의 규모와 특성
여러분이 이미 충분한 논의 과정을 거쳐 조직 내에서 어떤 패키지 매니저를 사용할지 선택했다면 해당 패키지 매니저의 워크스페이스 기능을 살펴보면 됩니다. 하지만 만약 지금까지 패키지 매니저에 대한 충분한 논의가 없었다면, 이번 기회에 각 패키지 매니저의 특징을 자세히 살펴보고 조직의 상황에 가장 적합한 패키지 매니저를 선택하는 것을 추천드립니다.
패키지 매니저 소개 및 비교는 이 포스팅을 참고해주세요 !
저의 경우에는 Yarn Berry와 PNPM 사이에서 고민 끝에 PNPM을 패키지 매니저로 채택했는데요. 그 이유는 다음과 같습니다.
PNPM, 너로 정했다
- 유지보수 중인 사내 서비스의 라이브러리 중 일부의 버전이 오래되어 Yarn Berry의 PnP 방식과 호환되지 않았습니다. 대체로 Yarn Berry의 설정 파일에서
NodeLinker: node-modules
로 스위칭하면 해결되었지만, 이 경우 Yarn Berry의 장점인 PnP를 사용할 수 없어 Yarn Berry를 사용하는 의미가 없다고 생각했습니다. - 소규모 개발 조직에서 Zero-install의 이점을 크게 활용하기 어려웠습니다. CI/CD 환경이 단순하고, 프로젝트의 수가 많지만 각각의 규모 자체는 그렇게 크지 않았기 때문입니다.
- NPM의
node_modules
환경에서 크게 변경되는 점이 없어 호환성 면에서 매력적이라고 생각했습니다.
이러한 이유로 Yarn Berry가 제공하는 PnP, Zero-install의 가치를 제대로 활용하기 어렵다고 생각했고, 대신 기존 NPM과 호환성이 좋으면서, 유령 의존성을 해결하고, 하드링크를 활용한 디스크 공간 절약과 빠른 설치 속도를 자랑하는 PNPM을 최종적으로 선택하게 되었습니다.
새로운 모노레포 환경에서 사용할 패키지 매니저를 정했으니, 자연스럽게 PNPM Workspace 단일 사용
혹은 PNPM Workspace + 모노레포 매니저
방식 중 하나로 선택지가 좁혀지게 됩니다.
PNPM Workspace
PNPM Workspace을 통한 모노레포 구축은 매우 간단합니다. pnpm
을 설치하고, 프로젝트 루트에 pnpm-workspace.yaml
설정 파일과 워크스페이스로 활용할 디렉토리를 생성한 뒤, 설정 파일에서 워크스페이스 디렉토리를 지정해주면 됩니다.
apps 디렉토리와 packages 디렉토리를 워크스페이스로 지정한 예시# pnpm-workspace.yaml packages: - "apps/*" - "packages/*"
이후 원하는 워크스페이스 내에 패키지로 활용할 디렉토리를 생성하고, pnpm init
명령어를 통해 해당 패키지를 초기화하면 됩니다.
packages/ 내에 ui 패키지를 생성한 예시cd packages mkdir ui cd ui pnpm init
위 설정만으로 apps/
, packages/
디렉토리 아래에 있는 모든 패키지들은 자동으로 패키지 매니저의 워크스페이스로 인식되어 관리됩니다.
각 패키지를 의존성으로 사용하고자 할 경우, workspace:*
형식으로 의존성을 추가하면 간단히 해당 패키지를 사용할 수 있습니다.
다음은 packages
디렉토리 아래에 ui
패키지를 만들고, 해당 패키지의 index.ts 파일을 @repo/ui
라는 의존성 이름으로 사용하는 예시입니다.
packages/ui/package.json{ "name": "@repo/ui", "version": "1.0.0", "main": "./index.ts", "dependencies": { "react": "^18.0.0" } }
apps/web/package.json{ "name": "@repo/web", "dependencies": { "@repo/ui": "workspace:*" } }
apps/web/src/index.tsximport { Button } from "@repo/ui";
이처럼 패키지 매니저가 제공하는 워크스페이스만으로도 매우 간단히 모노레포를 구축할 수 있습니다.
그렇다면, 모노레포 매니저는 굳이 왜 필요할까요?
2. 모노레포 매니저의 필요성
모노레포 환경에서 프로젝트들이 점차 규모가 커지고, 의존 관계가 복잡해질수록 여러 가지 문제가 발생할 수 있습니다.
1️⃣ 순환 의존성
순환 의존성은 하나의 모노레포 내에서 두 개 이상의 프로젝트가 서로 의존하여 폐쇄 루프를 만들 때 발생합니다. 이는 빌드 실패, 무한 루프 등 개발 워크플로우에 예상치 못한 에러를 야기할 수 있습니다.
마치 모듈 시스템에서 모듈 간 순환 의존성이 발생하는 것처럼, 모노레포 내에서도 프로젝트 간 순환 의존성이 발생할 수 있는 것입니다.

순환 의존성 뿐만 아니라 모노레포 내 패키지 간에는 shared
→ ui
→ web
같이 순차적 빌드가 필요한 경우가 있습니다. 이를 자동으로 보장할 수 없다면, 모노레포 내 빌드 프로세스는 비효율적이고 리스크가 높아집니다.
만약 모노레포 매니저를 사용하지 않고 빌드 시 패키지간 순환 의존성을 발견할 경우 다음과 같은 에러메시지가 출력됩니다.
순환 의존성이 발견된 모습× Invalid package dependency graph: Cyclic dependency detected: │ @repo/logger, storefront, @repo/ui, blog │ api, admin ╰─▶ Cyclic dependency detected: @repo/logger, storefront, @repo/ui, blog api, admin
이 경우, 각 패키지가 서로를 의존하기 때문에 어떤 패키지가 선행 빌드되어야 하는 지 알 수 없습니다.
하지만 모노레포 매니저를 사용할 경우, 패키지 의존성 그래프에서 사이클을 제거하기 위해 어떤 의존 관계를 분해해야 하는지 가이드를 제공합니다.
Turborepo가 순환 의존성을 감지한 경우× Invalid package dependency graph: ╰─▶ Cyclic dependency detected: │ @repo/logger, blog, storefront, @repo/ui │ The cycle can be broken by removing any of these sets of dependencies: │ { @repo/ui -> @repo/logger, @repo/ui -> storefront } │ { @repo/logger -> @repo/ui, @repo/ui -> storefront } │ ╰─▶ api, admin The cycle can be broken by removing any of these sets of dependencies: { admin -> api } { api -> admin }
즉, 모노레포 매니저는 순환 의존성이 발견되었을 때 코드 또는 의존성을 재구조화하여 순환을 끊는 방법을 제안하고, 빌드 순서를 명시적으로 정의하여 의존성을 의도한 방식으로 해결할 수 있도록 합니다.
2️⃣ 빌드 프로세스의 비효율
모노레포의 규모가 커질수록 모노레포 구조 자체의 유지 관리 부담이 증가하고, 한 번에 빌드에 여러 패키지를 빌드해야 하는 등 속도, 효율, 리스크 등 다양한 면에서 빌드 프로세스의 비효율이 발생할 수 있습니다.
모노레포 매니저는 이러한 빌드 프로세스의 비효율을 해결할 수 있도록 각자 다양한 기능을 제공합니다.
Turborepo를 예시로 들면, 대표적으로 다음과 같은 기능을 제공합니다.
- 증분 빌드: 이전에 빌드된 패키지는 캐싱을 통해 빌드 결과물을 재사용합니다.
- 내용 기반 해싱: 패키지의 타임 스탬프가 아닌 파일 내용을 기반으로 빌드 결과물을 해싱하여 캐싱을 통해 빌드 결과물을 재사용합니다.
- 원격 캐싱: 서로 다른 팀원이 같은 빌드 결과물을 재사용(캐싱)할 수 있도록 합니다.


위 그림은 터보레포에서 두 번째 빌드 시 이전 빌드 결과물을 캐싱하는 모습을 보여줍니다.

위 그림은 터보레포에서 리모트 캐싱을 통해 다양한 서로 다른 환경에서, 동일한 빌드 결과물을 재사용할 수 있는 모습을 보여줍니다.
3️⃣ CI/CD 통합의 번거로움
모노레포의 규모와 복잡성이 증가할수록 여러 프로젝트를 동시에 빌드하고 테스트, 배포하는 것이 어려워지고, 자동화된 CI/CD 파이프라인을 구축하는 것이 어려워집니다.
모노레포 매니저는 기본적인 환경에서 이러한 문제를 해결할 수 있는 기능을 제공해 프로젝트의 확장성을 보장합니다. 모든 모노레포 매니저의 개발 문서에는 GitHub Actions, GitLab CI 등 기본적인 파이프라인을 쉽게 적용할 수 있는 방법이 제공되고 있습니다.
물론 직접 스크립트를 작성하거나 커스터마이징 가능한 플러그인을 통해 조직에 필요한 추가적인 작업들을 만들어 사용할 수도 있습니다. 실제로 Toss에서는 Yarn Berry Workspace를 사용하면서 어떤 워크스페이스가 변경되었는지 계산하는 플러그인을 직접 만들어 사용하고 있다고 합니다.
하지만 특별히 추가적인 플러그인이 필요하지 않다면, 필수적인 CI/CD 기능을 손쉽게 적용할 수 있는 모노레포 매니저를 사용하는 것이 좋은 선택이 될 것입니다.
4️⃣ 정돈된 팀 협업 환경의 필요성 증가
프로젝트의 규모가 커지고 개발 팀원의 수, 프로젝트의 수, 커밋의 수가 많아질수록 정돈된 팀 협업 환경을 개선하는 것이 중요해집니다.
팀의 코드 베이스가 일관성있게 정리되어야 하고, 서로 간 끼칠 수 있는 영향을 파악해야 합니다.
앞서 소개한 원격 캐싱과 같은 기능을 통해 모든 팀원이 같은 빌드 결과물을 공유하여 빌드 속도와 일관성을 보장할 수 있도록 하고, 변경된 패키지와 그에 영향을 받는 패키지만 선택적으로 빌드하거나 테스트할 수 있어 코드 리뷰 등의 상황에 불필요한 빌드를 최소화할 수 있습니다.
또한 모노레포 매니저는 의존성 그래프를 기반으로 패키지 간 의존성을 자동으로 분석하여 특정 패키지의 변경이 다른 패키지에 미치는 영향을 즉시 파악할 수 있도록 하여 선제적으로 사이드 이펙트에 대응할 수 있도록 돕습니다.
3. 모노레포 매니저를 통한 모노레포 구축
지금까지 모노레포 매니저의 필요성에 대해 알아보고, 간단히 어떤 대표적인 역할을 수행하는 지 알아보았습니다. 이제 모노레포 매니저에는 어떤 도구들이 있는지 알아보고, 각 도구들의 특징과 장단점에 대해 알아보도록 하겠습니다.
현재 대표적으로 널리 알려진 모노레포 매니저에는 Lerna, Nx, Turborepo가 있습니다.
1️⃣ Lerna

- 특징
- 현재 가장 오래된 JavaScript 모노레포 도구 (2015년 등장)
- 패키지 간 종속성 관리 및 버전 제어 프로세스 최적화에 용이
- 여러 패키지를 동시에 릴리즈해야하는 시나리오에 적합
- 특히 NPM 패키지를 개발하고 배포하는 프로젝트에 적합
- 2022년 5월 Nx 팀에 인수됨
- 장단점
- ✅ 서로 종속된 패키지를 포함하여 버전 관리 용이
- ✅ 단일 명령으로 여러 패키지 릴리즈 가능
- ✅ 스크립트를 여러 패키지에서 공유해 CI 작업 통합 가능
- ✅ 넓은 생태계와 폭넓은 커뮤니티 지원
- ❌ 다양한 기능과 필요한 구성 옵션은 직접 학습하여 적용해야 함
2️⃣ Nx

- 특징
- Lerna 이후 차세대 모노레포 도구로 주목받은 구글 오픈소스 프로젝트
- 2022년 5월, Lerna의 프로젝트 관리 권한 및 경영권을 이어받음
- 패키지 관리를 넘어 개발 워크플로우 최적화를 목표로 함
- React, Node.js 등 프론트엔드/백엔드 전체를 폭넓게 지원
- 기본적인 테스트 환경 설정
- 장단점
- ✅ 증분 빌드와 캐싱을 통한 빌드 속도 개선
- ✅ 의존성 그래프 시각화 기능
- ✅ 코드 변화에 영향을 받는 프로젝트만 선별적으로 테스트 가능
- ✅ 다양한 플러그인 및 커스터마이징 지원
- ❌ 높은 학습 곡선
- ❌ 복잡한 설정과 보일러플레이트
Lerna는 패키지 버전 관리와 배포에 특화된 도구, Nx는 Lerna보다 좀 더 넓은 범위의 개발 워크플로우(빌드, 테스트, 린트 등)를 최적화하는 데 중점을 두고 있습니다.
3️⃣ Turborepo

- 특징
- Vercel이 개발한 최신 모노레포 빌드 시스템
- Rust로 작성되어 비교적 빠른 빌드 속도
- 설정이 매우 간단하고 직관적
- 모노레포 환경에서의 증분 빌드, 캐싱, 테스트 등 기본적인 기능을 손쉽게 최적화 가능
- 장단점
- ✅ 쉽고, 빠르고, 직관적인 기본 설정
- ✅ 효율적인 빌드 캐싱
- ✅ Next.js와 호환성이 좋음
- ✅ 원격 캐싱으로 팀원간 빌드 캐시 공유 가능
- ❌ 복잡한 고급 기능 부족, 커스터마이징 제한
- ❌ 점차 늘어나는 추세지만 여전히 비교적 적은 커뮤니티
4. 모노레포 매니저 선택
저는 여러 모노레포 매니저 중 Turborepo를 모노레포 매니저로 선택했습니다.
그 이유는 다음과 같습니다.
- 소규모의 팀원으로 운영되는 개발 조직이었고, 코드베이스의 크기가 그렇게 크지 않아 증분 빌드, 캐싱, 빌드 간 태스크 실행 순서 보장 외의 고급 기능 및 세밀한 커스터마이징은 크게 필요하지 않았습니다.
- 관리하는 프로젝트 중 Next 13, 14를 사용하는 프로젝트가 존재했는데, 배포과정에서 Vercel과의 통합이 비교적 쉬울 것이라 판단했습니다.
- 학습에 대한 비용을 최소화하고, 빠른 모노레포 도입을 위해 쉽고 직관적인 설정과 다양한 템플릿을 제공하는 Turborepo를 선택했습니다.
마치며
최종적으로 저는 모노레포 도입을 위해 PNPM + Turborepo 조합을 선택하게 되었습니다.
- PNPM: NPM과의 높은 호환성, 유령 의존성 해결, 하드링크를 통한 디스크 공간 절약이라는 장점을 모두 취할 수 있었습니다. 특히 기존 프로젝트들과의 호환성 문제를 최소화하면서도 모노레포 환경을 구축할 수 있다는 점이 큰 장점이었습니다.
- Turborepo: 효율적인 빌드 캐싱, Vercel과의 높은 호환성 뿐만 아니라, 여러 모노레포 매니저 중 가장 쉽고 빠르게 모노레포를 구축할 수 있었던 직관적인 설정과 템플릿 제공이 주요 선택 요인이었습니다.
실제 모노레포 구축 과정과 도입 후의 변화, 그리고 아쉬웠던 점들에 대해서는 다음 글에서 마저 다루어보도록 하겠습니다. 모노레포 도구들의 특징과 장단점을 살펴보다 보니 글이 꽤 길어졌네요 🥲
궁금하신 점이 있으시다면 댓글로 남겨주세요 !
출처
D2 - 모던 프론트엔드 프로젝트 구성 기법 - 모노레포 개념 편
D2 - 모던 프론트엔드 프로젝트 구성 기법 - 모노레포 도구 편
How to Choose Monorepo Tools in 2024