한달 전, 갑자기 회사에서 "각각의 흩어져있는 프로젝트를 모아서 하나의 코드베이스에 놔두는 것이 어떻겠냐?" 라는 팀장님의 의견이 제시되었고, 이에 따라 기존의 Polyrepo 방식에서 Monorepo 방식을 도입하기로 결정되었다.
다양한 앱과 패키지의 모음들이 단일 코드 베이스에 있는 것이다.
좀 더 구체적으로 예를 들어서, app, docs, shared-utils라는 3개의 분리된 repositories가 있다. app, docs는 npm에 패키지로 게시되어 있는 shared-utils라는 유틸리티를 공유하고 있다.
하지만 만약 shared-utils에 심각한 이슈가 생성되었다면 어떻게 해야할까?
여기서 polyrepo와 monorepo의 방식의 차이가 현저하게 나타난다.
이러한 과정이 shared-utils에 의존하는 앱이 많아질수록 이 프로세스는 더 길어지고, 복잡해진다.
shared-utils와 app, docs는 모두 동일한 코드베이스에 존재해있다. 따라서 ,
1.shard-utils에서 수정된 부분 커밋
2.app,docs 배포 준비 완료
이는, docs, app이 npm에 있는 shared-utils의 버전에 의존하지 않기 때문이다. 그들은 코드베이스에 있는 버전에 의존하기 때문이다.
이를 통해, 여러 앱과 패키지의 버그를 한 번에 수정할 수 있는 단일 커밋을 만들 수 있다.
monorepo의 주요 구성 요소는 workspace이다.
빌드하는 각각의 app, package들은 자체 workspace, package.json이 있다.
apps/docs/package.json
{
"dependencies": {
"shared-utils": "workspace:*"
}
}
docs의 package.json의 내용이다.
이 말인 즉, docs의 workspace는 shared-utils에 의존하고 있다는 것이다.
크게 3가지 이유를 들 수 있다.
Incremental builds
Parallel execution
Task Pipelines
npx create-turbo@latest
쨔란 - ✨
나는 cli 폴더 그 자체에 패키지를 만들고 싶어 .의 위치에 패키지 매니저는 pnpm으로 시작하였다.
(팀장님의 추천으로 pnpm으로 시작했지만, turborepo 문서에서도 pnpm을 추천한다.)
이렇게 cli를 치고 난 후,
이렇게 생성된 apps, packages, modules가 보인다.
만약, apps/web에서 ui 패키지 내 컴포넌트들을 import하고 싶다면, 위에서 보았듯이
package.json 내에 devendencies추가를 해준다.
apps/web/package.json
{
"dependencies": {
...
"ui": "workspace:*"
}
}
작업 공간을 하나만 선택해서 실행하고 싶을 때는, --filter 플래그를 명령에 추가한다.
--filter는 turbo CLI로 전달된다.
현재 내 모노레포의 root package.json에서는 이런 cli들이 존재해있다.
"scripts": {
"build": "turbo run build",
"dev": "turbo run dev --parallel",
// client, server를 함께 돌리는
"medi" : "turbo run dev --filter=medi-client --filter=medi-server",
// 병렬적으로 딱 한 개의 프로젝트만 run dev하고 싶을 때
"medi-client": "turbo run dev --parallel --filter=medi-client...",
"medi-server": "turbo run dev --parallel --scope='medi-server'"
}
실제로 모노레포로 개발을 해보니, shared되는 자원들(ui components, utils함수 등등)에 대해서는 정말 효율적이고, 빌드 속도도 빨라 만족스러웠다.
하지만, 외부 라이브러리들이 점점 많아질수록 버전 충돌 문제가 종종 발생하여 실행하는데 문제가 생기기 일쑤이기도 하였다.
이는 다음에 manypkg/cli로 해결하였는데, 다음번에 회고해 보겠다!