

모노레포(Monorepo)는 여러 프로젝트를 하나의 Git 저장소에서 관리하는 방식을 말합니다.
우리가 지금까지 흔히 사용했던 멀티레포(Multirepo), 즉 프로젝트나 서비스별로 각각 독립된 레포지토리에서 관리하는 방식과는 대조되는 방식입니다.

모노레포의 등장 배경에는 현대 웹 개발의 복잡성이 그 시작입니다. 하나의 서비스를 개발한다고 했을 때 웹 프론트엔드, 모바일 앱, 관리자 대시보드 등 여러 프로젝트가 필요한데, 이들이 서로 밀접하게 연관되어 있음에도 멀티레포 방식을 채택했을 때 아래와 같은 여러 문제점들이 발생합니다.
[ 전통적인 멀티레포 방식의 문제점 ]
위와 같은 문제로 인해, 특정 레포지토리 담당자 외의 팀원이 다른 레포지토리에서 작업을 하려면 코드와 개발 환경을 처음부터 다시 파악하기 위해 많은 시간이 소요되고, 초기 온보딩 비용 또한 증가하게 됩니다. 또한 공통된 코드에 대한 유지 보수 및 관리 난이도도 높아집니다.

모노레포를 잘 도입하면 여러 이점을 얻을 수 있습니다.
공통 컴포넌트가 같은 비지니스 로직이 여러 프로젝트에 걸쳐 사용되었을 때, 한 곳에서 관리하고 즉시 다른 프로젝트에서 사용할 수 있습니다. 뿐만 아니라, 기존에는 오류가 발생했을 때 각각의 레포지토리에서 수정하고 반영해야 하지만 이제는 한 곳에서 관리하기 때문에 모든 프로젝트에 즉시 적용됩니다. 마지막으로 컴포넌트와 유틸리티의 버전 관리가 용이합니다.
전체 프로젝트의 의존성을 분산하지 않고 중앙에서 관리할 수 있습니다. 라이브러리를 사용한다고 했을 때, 모든 프로젝트에서 동일한 버전의 라이브러리를 사용할 수 있고 의존성 업데이트 시 한 번에 모든 프로젝트에 적용할 수 있습니다.
모든 프로젝트가 동일한 lint 규칙을 적용받기 때문에, 코드 컨벤션을 동일하게 유지할 수 있습니다. 뿐만 아니라 일관된 빌드 프로세스와 동일한 테스트 환경으로 품질 관리가 용이합니다.
전체 코드 베이스에 대한 변경이나 프로젝트 간 의존성이 있는 변경도 단일 PR로 관리할 수 있습니다.
모든 프로젝트의 코드를 하나의 레포지토리에서 찾고 수정할 수 있기 때문에, 프로젝트 간 지식 공유가 쉽고 일관된 코딩 스타일과 패턴을 유지할 수 있습니다.

사실 어떤 것이든 무조건적인 것은 없습니다. 각자의 상황에서 모노레포와 멀티레포를 선택했을 때의 장단점을 비교해보고 현재 팀의 상황에 더 적절한 방식을 선택하면 됩니다.
모노레포는 중앙 집중형으로 개발 환경, 코드, 컨벤션 등을 한번에 관리하고 공유하기 때문에 효율성을 극대화합니다. 그 덕분에 개발자들은 불필요하게 매번 동일한 작업을 반복하지 않아도 되고, 비지니스 가치에 더욱 집중할 수 있다는 이점이 있습니다.
멀티레포의 경우, 프로젝트를 작은 단위로 쪼개서 격리하고 권한을 세분화하기 때문에 부분적인 실패가 전체로 확산되는 것을 방지합니다. 만약 안정적인 운영이 비지니스 가치 및 신뢰와 직결되는 프로젝트라면 적합할 수도 있습니다. 뿐만 아니라 프로젝트 단위로 빠르게 개발 환경을 설정하고 개발을 진행한다면 멀티레포 방식을 선택하는게 더 나을 수도 있습니다.
모노레포는 여러 프로젝트를 하나의 저장소에서 관리하며 코드 재사용성을 높이지만, 규모가 커질수록 코드 공유 및 의존성 관리의 어려움이 발생합니다. 이러한 문제점을 해결하기 위해, 여러 모노레포 도구들이 등장했습니다.

Turborepo는 빌드 성능 최적화라는 목표에 초점을 맞춘 도구입니다.
지능적인 빌드 캐싱을 지원합니다. 명령어를 실행할 때마다, 파일의 내용과 현재 상태를 기억해두고 만약 코드가 수정되지 않은 상태에서 다시 빌드 명령을 내리면 작업을 반복하는 것이 아니라 이전에 저장해 둔 결과물을 즉시 복원합니다. 이를 통해 빌드 시간을 획기적으로 단축할 수 있습니다.
뿐만 아니라 작업의 순서를 파악하여 병렬 실행을 지원합니다. 기존 방식이 작업을 하나씩 순서대로 처리하며 시간을 소모했다면, Turborepo는 서로 의존성이 없는 작업들을 동시에 병렬로 수행합니다. 결과적으로 프로젝트의 규모가 커지고 앱이 늘어나도 개발 환경의 속도가 느려지지 않도록 방지합니다.
Nx와 같은 모노레포 도구와 비교했을 때, 새로운 명령어를 배울 필요가 없고 설정 파일이 하나뿐이기에 러닝 커브가 높지 않습니다. 처음부터 완벽하게 세팅하고 시작하지 않아도, 추후에 필요 시 하나씩 기능을 붙여나가는 점진적 도입이 가능하다는 점도 장점입니다.

pnpm workspace는 효율적인 의존성 관리에 초점을 맞춘 도구입니다.
의존성을 한번만 설치하면, 여러 프로젝트가 공유하기 때문에 디스크 공간을 절약하고 설치 속도가 빠릅니다. 또한 파일 하나로 간단하게 설정할 수 있기 때문에, 러닝 커브 또한 높지 않습니다.

Nx는 빌드 속도 최적화를 넘어 엔터프라이즈급 모노레포 관리 도구를 지향합니다.
구글 Angular 팀 출신들이 개발한 도구이고, 다양한 기술 스택을 지원합니다. 핵심적인 기능은 지능형 캐싱과 병렬 빌드를 통한 강력한 성능 최적화이고 시각화된 의존성 그래프를 확인할 수 있습니다. 이를 통해 복잡한 의존 관계를 한눈에 파악하고, 일관된 개발 환경을 구축하는데 도움을 줍니다.
Nx는 학습해야 할 독자적인 개념이 많고, 추상화 수준이 높고 기능이 방대하므로 꽤 높은 러닝 커브를 가지고 있습니다. 도입 시, 현재 팀 상황에 필요하다면 신중히 도입하는 것이 좋습니다.

현재 우리 팀의 서비스는 초반 온보딩 페이지는 공유하고, 그 이후에 주최사/관객으로 분리되는 Y자형 구조입니다.
주최사/관객 각각의 비지니스 로직은 다르지만, 공연 정보 등 서버에서 내려주는 데이터는 양쪽 모두 동일하게 공유해야 하고 공유하는 컴포넌트 또한 꽤 많이 존재합니다. 추후 확장성을 고려하고, 프로젝트 전반에 걸쳐 디자인 시스템 관리를 용이하게 하기 위해 모노레포 도입을 고려하게 되었습니다.
이러한 상황에서 모노레포는 코드의 중복을 차단하고 유지보수 효율을 높일 수 있다고 판단했습니다.

Turborepo를 선택한 첫 번째 이유는 앞서 언급한 것처럼 지능적인 빌드 캐싱입니다. 기존에는 하나의 앱만 집중 개발하고 있음에도, 불필요하게 코드 변경이 없는 다른 앱까지 재빌드됩니다. Turborepo는 빌드 성능 최적화에 초점을 맞추고, 코드 수정 없이 발생한 빌드 명령에 대해서는 이전에 저장해 둔 결과물을 즉시 가져오므로 빌드 시간을 단축할 수 있습니다.
또한 현재 팀 내 모노레포에 대한 이해도가 높지 않은 상황에서, 비교적 설정 복잡도가 낮고 러닝 커브가 높지 않다는 점도 선택 이유입니다. Turborepo는 비교적 단순한 설정으로 원하는 캐싱과 병렬 빌드 기능을 빠르게 적용할 수 있습니다.
pnpm은 node_modules 구조를 매우 엄격하게 만들고, package.json 에 명시하지 않은 라이브러리는 import 자체가 불가하므로 유령 의존성을 차단합니다. 뿐만 아니라 모노레포는 구조 상 node_modules 용량이 커질 가능성이 높은데, pnpm을 사용하면 디스크 용량을 절약하고 설치 속도를 향상할 수 있습니다.
AMP-CLIENT/
├── package.json
├── turbo.json # Turborepo 파이프라인 설정
├── pnpm-workspace.yaml # 워크스페이스 정의
│
├── apps/ # 실제 실행되는 서비스 (Deploy targets)
│ ├── host/ # 주최사 웹
│ │ ├── src/
│ │ │ ├── app/
│ │ │ ├── pages/
│ │ │ ├── widgets/
│ │ │ ├── features/
│ │ │ ├── entities/
│ │ │ └── shared/
│ │ └── package.json
│ │
│ └── audience/ # 관객 웹
│ ├── src/
│ │ ├── app/
│ │ ├── pages/
│ │ ├── widgets/
│ │ ├── features/
│ │ ├── entities/
│ │ └── shared/
│ └── package.json
│
└── packages/ # 조립 부품 및 공통 기능 (Shared Code)
├── ads-ui/ # ADS (AMP DESIGN SYSTEM)
│ └── src/
│ ├── components/
│ ├── styles/
│ └── icons/
│
└── configs/ # 개발 환경 설정 (ESLint, TSConfig)
어디까지나 예시일 뿐이고, FSD 구조를 도입할 때에는 초반부터 과도하게 레이어를 분리하는 것은 지양하는 것을 권장하고 있기 때문에 추후 변경 가능성이 높습니다.
더 논의해볼 사항은 아래와 같습니다.
shared 레이어와 모노레포 packages의 역할 충돌shared/ui와 packages/ads-ui 등)apps/host와 apps/audience 내부 FSD 구조에서 widgets, features, entities 중복 가능성