Turborepo로 모노레포 경험해보기

강예리·2022년 10월 16일
2

기록만이 살길

목록 보기
4/5

한달 전, 갑자기 회사에서 "각각의 흩어져있는 프로젝트를 모아서 하나의 코드베이스에 놔두는 것이 어떻겠냐?" 라는 팀장님의 의견이 제시되었고, 이에 따라 기존의 Polyrepo 방식에서 Monorepo 방식을 도입하기로 결정되었다.

MonoRepo란 무엇인가?

다양한 앱과 패키지의 모음들이 단일 코드 베이스에 있는 것이다.

좀 더 구체적으로 예를 들어서, app, docs, shared-utils라는 3개의 분리된 repositories가 있다. app, docs는 npm에 패키지로 게시되어 있는 shared-utils라는 유틸리티를 공유하고 있다.
하지만 만약 shared-utils에 심각한 이슈가 생성되었다면 어떻게 해야할까?

여기서 polyrepo와 monorepo의 방식의 차이가 현저하게 나타난다.

PolyRepo라면,

  1. shared-utils error를 수정하여 commit
  2. npm에 bumping version된 패키지를 게시
  3. app, docs에 bumping version으로 커밋
  4. app, docs는 이제 배포될 준비 완료

이러한 과정이 shared-utils에 의존하는 앱이 많아질수록 이 프로세스는 더 길어지고, 복잡해진다.

하지만 monorepo라면,

shared-utils와 app, docs는 모두 동일한 코드베이스에 존재해있다. 따라서 ,

1.shard-utils에서 수정된 부분 커밋
2.app,docs 배포 준비 완료

이는, docs, app이 npm에 있는 shared-utils의 버전에 의존하지 않기 때문이다. 그들은 코드베이스에 있는 버전에 의존하기 때문이다.

이를 통해, 여러 앱과 패키지의 버그를 한 번에 수정할 수 있는 단일 커밋을 만들 수 있다.

monorepo는 어떻게 작동할까?

monorepo의 주요 구성 요소는 workspace이다.
빌드하는 각각의 app, package들은 자체 workspace, package.json이 있다.

apps/docs/package.json

{
  "dependencies": {
    "shared-utils": "workspace:*"
  }
}

docs의 package.json의 내용이다.
이 말인 즉, docs의 workspace는 shared-utils에 의존하고 있다는 것이다.

왜 Turborepo를 택하였나?

크게 3가지 이유를 들 수 있다.

  1. Incremental builds

    • 작업 진행상황을 캐싱하여, 이미 계산된 내용은 건너뛴다. (빌드는 only once)
  2. Parallel execution

    • 지정된 태스크 단위로 의존성을 판단하여, 최대한 병렬적으로 작업을 진행 (같은 모노레포 툴 lerna와 비교)
  3. Task Pipelines

    • 태스크 간, 연결을 정의하여 빌드를 언제, 어떻게 실행해야 할지 판단해서 최적화해준다.

이제 TurboRepo 시작해볼까?

npx create-turbo@latest

쨔란 - ✨
나는 cli 폴더 그 자체에 패키지를 만들고 싶어 .의 위치에 패키지 매니저는 pnpm으로 시작하였다.
(팀장님의 추천으로 pnpm으로 시작했지만, turborepo 문서에서도 pnpm을 추천한다.)

이렇게 cli를 치고 난 후,

이렇게 생성된 apps, packages, modules가 보인다.

  • apps/web, apps/docs
    - Nextjs with Typescript / ui, tsconfig, eslint-config-custom에 의존
  • packages/ui
    - 공유되는 리액트 컴포넌트 라이브러리 / tsconfig, eslint-config-custom에 의존
  • packages/eslint-config-custom
    - 공유되는 구성 (ESLint) / no dependencies
  • packages/tsconfig
    - 공유되는 typescript config / no dependencies

만약, apps/web에서 ui 패키지 내 컴포넌트들을 import하고 싶다면, 위에서 보았듯이
package.json 내에 devendencies추가를 해준다.

apps/web/package.json

{
  	"dependencies": {
    ...
    "ui": "workspace:*"
  }
}

한 번에 하나의 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로 해결하였는데, 다음번에 회고해 보겠다!

profile
Stack My Knowledge

0개의 댓글