Mono-Repo 와 관련 라이브러리 툴(turborepo, yarn workspace)
Multi-Repo
멀티 레포는 여러 레포지토리에 패키지를 분산해 주는 것을 의미한다.
말 그대로 하나 또는 두개 이상의 패키지 관리는 하나 또는 두개이상의 레포지토리로 구성하여 관리하는 것이다.
예를 들어 A라는 패키지를 구성하는 B,C라는 패키지가 있을 경우 B-Repository, C-Repository 로 나눠 두는 것을 의미한다. 여기서 꼭 A라는 패키지가 없어도 상관 없다 의미자체를 보면 패키지별로 레포지토리를 분리한다고 보면 된다. 보통 패키지와 페포지토리는 1:1관계를 맺으며 통상 사용하는 방식으로 생각하면 된다.
장점
- 레포지토리멸 오너를 지정
- 패키지 별로 레포지토리가 분리되어있다는 것은 각 패키지 별로 관리가 가능하는 것이다. 이에 대해 각 레포지토리별 오너를 지정할 수 있고 이로 인해 패키지 관리가 수월해진다.
- 빠른 CI Build
- 각 패키지가 레포지토리로 분산된다면 하나의 레포지토리는 하나의 CI를 구성할 수 있다 이렇게 되면 하나의 큰 덩어리로 구성된 패키지 보다 리소스가 적기 때문 CI Build속도가 빨라지게 된다.
- 패키지의 명확한 분리로 인한 유연성 향상
- 레포지토리가 분리되어 있기 때문에 각 패키지는 마스터코드의 충돌을 방지할 수 있으며 레포지토리 상 서로 연계 관게가 없기 때문에 추가 수정 유지 관리에 있어서 유연성이 향상된다.단점
- 중복된 성정 및 반복된 설치
- 단일 패키지를 구성할 때 이 패키지를 구성하는 패키지의 레포지토리를 나눔으로써 모든 공통된 설정과 모듈들을 반복적으로 설정 및 설치해야 한다. 예를 들어 eslint와 babel을 설정한다고 하면 모든 레포지토리에 eslint와 babel 을 설치하고 설정 파일을 동일하게 구성해야한다.
- 이슈 분산
- 레포지토리가 분리되어 있기 떄문에 각 레포지토리는 이슈 트래킹을 가진다. 전혀 다른 패키지라면 이슈 트래킹이 분리되는 것이 정상이지만 하나의 큰 패키지를 구성하고 내부 sub 패키지가 필요한 상황이라면 이슈 트래킹을 분리되는 것이 옳지 않다. 이슈 뿐아니라 CHANGELOG 역시 분리됨으로써 괸리 포인트가 증가한다.
- Dependency Hell
- 프로젝트 및 패키지의 규모가 커짐에 따라 의존 그래프가 매우 복잡해지게 된다. 특정 패키지들이 같은 모듈을 사용하지만 버전 차이에 따라 종속성이 달라지고 이로 인한 충돌을 야기할 수 있다.
- 중복 코드의 가능성
- 중복된 설정 및 반복된 설치와 비슷한 이유이며 서로다른 레포지토리가 분리되어 있기 때문에 공통된 코드가 중복될 가능성이 커진다.
Mono-Repo
멀티 레포와 반대로 모노 레포는 여러 패키지를 하나의 레포지토리에서 관리하는 것을 의미한다.
멀티레포의 단점을 보완하면서 분리된 패키지를 하나의 레보지토리로 합쳐 관리한다 쉽사리 실 업무에 적용해보기 쉽지 않기 때문에 멀티 레포에 비해 활성화가 많이 되지는 않았다.
하지만 점차적으로 오픈 소스에서 지원하는 기능이 많이지면서 오픈소스에서 모노 레포를 많이 채택하고 있다.
이러한 모노 레포에 대해 어떤 장단점이 있는지 알아보자.
장점
- 공통 항목 단일화
- eslint, build, unit test 등 공통된 설정 및 필요한 노드 모듈을 한 번에 설치와 한번의 설정으로 모든 패키지를 사용할 수 있다. 레포지토리가 분산되어 있지 않고 하나의 레포지토리에 있기 때문에 디펜던시의 업데이트 역시 한번으로 해결이 가능하다.
- 쉬운 패키지 공유
- 멀티 레포의 경우 패키지가 분리되어 있기 때문에 패키지 간 공유가 쉽지 않다. 그렇기 대문에 중복 코드가 발생할 가능성이 있는데 모노 레포의 경우에는 하나의 레포지토리이기 때문에 패키지간 공유가 수월하며 중복 코드 역시 발생할 염려가 없다.
- 단일 이슈 트래킹
- 하나의 레포지토에 패키지가 모노 레포로 구성되어 있다면 그 패키지의 종속된 모든 패키지는 서로 연관 관계를 가질 수 있다. 이로 인해 이슈 트래킹은 분산 없이 하나로 처리가 가능하며 모노 래포는 이를 지원한다.
- 효율적인 의존성
- 전체 패키지의 의존 관계가 하나의 레포에서 이뤄지기 대문에 패키지간 의존성 관리가 수월해진다.단점
- 레포지토리의 거대화
- 분산되어 있던 모든 리소스를 하나의 레포지토리에서 관리가 되기 때문에 하나의 레포지토리의 규모가 커진다. 이로 인한 문제들은 나비효과처럼 커질 가능성을 보인다.
- 느린 CI Build
- 멀티 레포와 반대로 레포지토리가 하나로 되어 있기 때문에 CI가 하나로 구성된다는 장점이 있지만 패키지가 규모가 커짐으로인해 분산된 CI Build보다 속도가 느릴 수 밖에 없다.
- 무분별한 의존성
- 패키지 간의 의존성 관리가 쉽다는 장점이 있지만 오히려 이러한 장점으로 인해 과도한 의존 관계가 나타날 수 있다.
- Dev Tools의 인덱싱 저하
- 개발 도구를 사용할 대패키자가 분산되어 이싿면 각각의 패키지를 열어서 사용하면 되지만 모노 레포의 경우는 하나의 개발 도구로 열 경우 해당 패키지의 인덱싱 처리 속도가 길어진다.
Mono-Repo는 언제 사용해야할까?
Mono-Repo
를 구성하는 적절한 시기는 정확하지 않다. 그러므로 구글링 자체로도 정보를 얻기엔 힘든 부분이 있다.
이러한 이유는 모든 프로젝트와 Package의 구조가 규모, 형태, 구조가 모두 다르기 때문이다. 하지만 아래와 같은 상황이라면 ### Mono-Repo
를 구성해도 좋다.
- 서로 다른 패키지가 연관 관계를 가질 경우
- 첫 번째 항목이 고려된 상황에서 N개의 패키지의 형태와 목적이 유사한 경우
- 두 번째 항목이 고려된 상황에서 N개의 패키지 중 배포되어야 할 패키지의 비중이 큰 경우
모노레포 툴들
모노레포를 할수 있도록 기능을 제공하는 라이브러리나 툴들이 많이 나오고 있다.
Bazel, Lerna, NX, Turborepo, Yarn(or npm) workspace 등..
해당 라이브러리들이 어떤 기능들을 제공하고 장단점이 있는지는 Monorepo Explained에서 확인할 수 있다. 참고해서 필요한 혹은 해보고 싶은 라이브러리를 택하면 되겠다.
Turborepo
내가 알아본 것은 Turborepo 와 yarn workspace 였다.
현재 시점 터보레포는 yarn berry pnp 모드가 온전히 지원되지는 않는 것 같다.
yarn berry and turborepo
Add support for yarn berry v3 with PnP as install strategy · Issue #693 · vercel/turborepo · GitHub
Starter project fails with yarn pnp mode · Issue #1066 · vercel/turborepo · GitHub
공식문서에서는 version 1.2+ 부터는 지원가능하다 되어 있어서 해봤는데,
노드링커를 node_modules
로 설정했을 때는 되지만 그렇지 않으면 문제가 아직 있는 것 같았다.
yarn 1.*+ npm 으로 구축할 수 있는데
공식 홈페이지에서 많은 example을 또한 제공하고 있다.
example들을 보면 turborepo는apps
와 packages
로 나눠서 패키지를 관리한다.apps
는 실제 실행 어플리케이션들을 관리하고,packages
는 이곳에서 사용할 config, common components, util 등을 분리해서 괸리한다.
turbo.json 에서 script 및 pipeline을 설정해서 사용할 수 있다.
npx(yarn) create-turbo@latest .
명령어를 통해서 간단히 터보레포를 사용해볼 수 있다.
요즘 나오는 모노레포 라이브러리 2세대(?) 들처럼 터보레포도 여러 기능을 제공한다.
캐싱
이전에 실행한 파일 및 로그를 캐싱(.turbo 로그 기록)해서
현재 테스크에서 이미 완료한 부분은 제외하고 작업을 하기 때문에 작업시간을 효율적으로 줄일 수 있다.
파이프라인
turbo.json에서 각 패키지의 스크립트 간 작업 관계를 설정해줄 수 있다.
그래서 대표적으로 스크립트에 많이 쓰는 build
, test
, lint
등의 서로의 작업 순서나 디펜던시를 설정할 수 있다.
{
"$schema": "https://turborepo.org/schema.json",
"pipeline": {
"build": {
"dependsOn": ["^build"]
},
"test": {
"dependsOn": ["^build"]
},
"deploy": {
"dependsOn": ["build", "test", "lint"]
},
"lint": {}
}
}
이외에도 의존성 시각화, 프로파일, 원격 캐싱등 많은 기능을 제공한다.
yarn workspace
yarn berry 를 사용하기 때문에 yarn workspace를 사용해봤다.
yarn berry + yarn workspace를 사용하기 위해서는
- yarn 설치
- yarn berry 설치 및 설정
- yarn berry 프로젝트 시작
- workspace 세팅
- 프로젝트 생성
- typescript, eslint, prettier 설정
- 플러그인 설치
1. yarn 설치
npm install -g yarn
2. yarn berry 설치 및 설정
yarn && yarn set version berry
3. yarn berry 프로젝트 시작
yarn -v //3.2.1
yarn init
yarn init 으로 package.json
파일 등이 생성된다.
만약 .yarnrc.yml
파일에
nodeLinker: node-modules
노드 링커가 연결되어 있다면 해당 코드를 삭제해준다.
rm -rf node_modules && yarn
4. workspace 세팅
"name": "project",
"version": "0.0.0",
"workspaces": {
"packages": [
"package/*"
]
},
"packageManager": "yarn@3.2.1",
위와 같이 workspaces
를 설정하게 되면 해당 프로젝트들이 workspace 에 속하게 된다.
5. 프로젝트 생성
원하는 프로젝트 폴더를 만들고 해당 프로젝트 내에서
cra, cna 등 프로젝트를 진행하면 된다.
이번엔 라이브러리를 만들 것이기 때문에 yarn init
을 통해 package.json
파일을 만들고 리액트, 롤업 등 필요 라이드러리를 설치했다.
6. typescript, eslint, prettier 설정
루트 폴더로 이동 후에
yarn add -D typescript prettier eslint
위 3가지를 설치하고,
vscode를 사용중이라는 가정하에
패키지들이 zip 아카이브로 되어 있기 때문에 기존 방식으로 정상적으로 타입이 불러와지지 않는다.
그렇기 때문에 VSCode에서 zipfs 을 설치해준다.
이 extension은 zip 아카이브에서 직접 파일을 읽을 수 있도록 vsCode에 추가한다.
yarn dlx @yarnpkg/sdks vscode && yarn plugin import typescript
로 sdk를 설치한다.
tsc --init
으로 tsconfig.json 파일을 생성하고 원하는 설정을 하면 된다.
그리고 tsconfig.json은 루트에서 base를 만들어서
내부 패키지 tsconfig.json에서 extends
해서 사용할 수 있다.
주의할 점은 tsconfg에서 사용하는 모든 경로는 상대적이라는 것이다.
만약 tsconfig.base.json
에서 outDir
을 dist
라고 설정했다고
내부 패키지에서 dist
파일을 찾는 것이 아니라는 것이다.
base에서 설정된 경로를 가지고 오기 때문에
내부 패키지에서 따로 outDir: dist
로 경로 설정을 맞추어 주어야 한다.
마지막으로 command + shift + p키 (맥북 기준)를 눌러 TypeScript 버전 선택..을 검색해 Use Workspace Version을 선택해 workspace의 typescript sdk로 변경해준다.
7. 플러그인 설치
해당 타입스트립트 등의 설정을 끝내고
스크립트를 사용할 때 루트 스크립트에서 내부 모든 패키지를 한번에 실행시키기 위해서 foreach
명령어를 사용할 수 있는데
해당 명령어를 사용하기 위해서는
yarn plugin import workspace-tools
으로 플러그인을 설치해준다.
그 후 루트폴터 package.json
에서
"dev:all": "yarn workspaces foreach -p run dev",
과 같이 스크립트를 만들 수 있다.
+ 추가
만약 각각의 패키지 앱들을 따로 vs code에서 워크스페이스로 열어서 관리하고 싶다면
workspace.code-workspace
라는 파일을 만들어서
{
"folders": [
{
"path": "./package/tmp"
}
],
"settings": {
"typescript.tsdk": "../../.yarn/sdks/typescript/lib",
"typescript.enablePromptUseWorkspaceTsdk": true
}
}
로 설정해주면 path로 지정된 디렉토리를 따로 관리할 수 있다.
또한, 프로젝트간에 코드 공유를 할 수 있는데
터미널로 package 폴더 안에 만들었던 common으로 이동한두이에 yarn init
패키지 생성하고
common 폴더에 index.ts 파일을 생성하고 해당 파일을 열어서 간단히 무언가 만들고
이를 가져올 부분에 프로젝트로 이동하여 package.json을 아래와 같이 수정하면된다.
“dependencies”: {
“@monorepo/common”: “*”,
“react”: “^17.0.0”,
“react-dom”: “^17.0.0”
},
그리고 터미널에
yarn
하면 패키지끼리 연결할 수 있다.
+ 더하여 monorepo에서 yarn version 관리를 하는 법은 아래 링크를 참고하면 좋을 것 같다
[yarn version | Yarn](https://classic.yarnpkg.com/en/docs/cli/version)
[Release Workflow | Yarn - Package Manager](https://yarnpkg.com/features/release-workflow)
[Versions of dependencies | Yarn](https://classic.yarnpkg.com/en/docs/dependency-versions#toc-semantic-versioning)
참고)
https://kdydesign.github.io/2020/08/25/mono-repo-lerna/
Monorepo Explained
NAVER D2
node.js - paths is not working in tsconfig.app.json as expected - Stack Overflow
VScode 에서 Typescript 환경 설정하기. typescript설치에서 TSlint, prettier 까지 | by geonmo-nine | Mediumyarn workspaces foreach
| Yarn - Package Manager
yarn berry로 workspace(monorepo) 구성하기
yarn berry, workspace
Yarn berry workspace를 활용한 프론트엔드 모노레포 구축기 | 우아한형제들 기술블로그