본문 바로가기

FrontEnd

[프론트엔드 아키텍처] 모노레포 1분만에 이해하기 + 터보레포

반응형

실무에서 모노레포(monorepo) + 터보레포(turborepo)를 사용하며 배운 점을 공유합니다.

Turborepo

모노레포는 무엇인가?

보통 프로젝트 별로 레포지토리를 따로 파는 관행이 있습니다.

이 경우의 문제는 코드 공유가 어렵다는 것입니다.

또한 독립성과 코드 공유를 공존하게끔 하기 위한 복잡성이 추가됩니다.

독립적인 프로젝트

package/shared의 버그가 apps/web과 apps/docs 모두에서 심각한 문제를 일으킨다고 가정해 보겠습니다.
 
다음을 수행해야 합니다.
  1. package/shared의 오류 수정을 커밋합니다.
  2. package/shared을 publish 합니다.
  3. apps/web에서 package/shared의 오류를 수정하기 위한 범핑 커밋을 만듭니다.
  4. apps/docs에서 package/shared의 오류를 수정하기 위한 범핑 커밋을 만듭니다.

각 레포의 담당 팀이 나누어져 있을 경우 의사소통 비용을 수반합니다.

모노레포를 사용하면 다음 작업만 하면 됩니다.

  1. package/shared의 오류 수정을 커밋합니다.

모노레포를 한 줄로 요약하면 다음과 같습니다.

같이 깨지는건 같이 깨지도록 관련된 프로젝트를 하나에 모아둔 레포지토리.
  • 모노레포를 공유하는 한 팀
  • 각 워크스페이스 별 개발자
  • 모든 프로젝트 공유 모듈(shared)

터보레포

물론 모노레포를 사용하게 되면,

여러 프로젝트가 한 프로젝트에 공존하기에, 여러 프로젝트를 통합 관리하는 복잡성이 수반됩니다.

 

각 프로젝트 별로 독립적으로 빌드를 수행해야 한다고 가정해 봅시다.

명령어를 몇번이나 쳐야 할까요?

 

물론 해당 작업을 위해 스크립트를 개발할 수도 있습니다만,

Turborepo를 사용하면, 해당 작업들을 간단하게 수행할 수 있습니다.

 

devops 엔지니어가 파이프라인을 작업하는 것처럼, turbo.json에 파이프라인을 설정하면,

모든 레포지토리, 혹은 개별 레포지토리의 작업을 루트 레포지토리에서 개별적으로 수행할 수 있습니다.

터보레포 활용하기

아래와 같이 turbo.json에 파이프라인을 정의하기만 하면, 파이프라인이 생성되며
turbo run build와 같이 실행할 수 있습니다.

{
  "$schema": "https://turbo.build/schema.json",
  "pipeline": {
    "build": {
+      // A workspace's `build` task depends on that workspace's
+      // topological dependencies' and devDependencies'
+      // `build` tasks  being completed first. The `^` symbol
+      // indicates an upstream dependency.
      "dependsOn": ["^build"]
    },
    "test": {
+      // A workspace's `test` task depends on that workspace's
+      // own `build` task being completed first.
      "dependsOn": ["build"],
      "outputs": [],
+      // A workspace's `test` task should only be rerun when
+      // either a `.tsx` or `.ts` file has changed.
      "inputs": ["src/**/*.tsx", "src/**/*.ts", "test/**/*.ts", "test/**/*.tsx"]
    },
    "lint": {
+      // A workspace's `lint` task has no dependencies and
+      // can be run whenever.
      "outputs": []
    },
    "deploy": {
+      // A workspace's `deploy` task depends on the `build`,
+      // `test`, and `lint` tasks of the same workspace
+      // being completed.
      "dependsOn": ["build", "test", "lint"],
      "outputs": []
    }
  }
}

터보레포의 이점

이전에 언급한 태스크 오케스트레이션을 제외하고.

터포레포의 이점은 단 두줄로 요약할 수 있습니다.

  • 같은 작업을 두번 다시 하지 않는다.
  • 최대 멀티태스킹

같은 작업을 두번 다시 하지 않는다

캐시 hit 결과

터보레포는 hashing을 사용하여 task의 수행 결과를 캐싱해 둡니다.

해싱 대상을 전역 환경변수, 특정 파일, 특정 포맷 등으로 커스터마이징 할 수 있으며

해시에 매핑되는 타겟 또한 직접 지정하는 것부터,

심지어 지정하지 않을 경우 로그 결과까지 재활용 할 수 있습니다.

lint 결과도 재사용 가능합니다.

최대 멀티태스킹

아래 이미지와 같이 터보레포가 아닌 패키지 매니저의 monorepo 기능을 활용하면 다음과 같이 각 단계가 sequential 하게 수행됩니다.

yarn workspaces run lint
yarn workspaces run test
yarn workspaces run build

sequential한 task 실행

다음과 같이 turbo.json을 설정하고

{
  "$schema": "https://turbo.build/schema.json",
  "pipeline": {
    "build": {
      // ^build means build must be run in dependencies
      // before it can be run in this workspace
      "dependsOn": ["^build"]
    },
    "test": {
      "outputs": []
    },
    "lint": {
      "outputs": []
    }
  }
}

turbo run으로 각 작업을 패러랠하게 수행할 수 있습니다.

turbo run lint test build
실행하면 Turborepo는 사용 가능한 모든 CPU에서 가능한 한 많은 작업을 멀티태스킹합니다.
즉, 작업이 다음과 같이 실행됩니다.

태스크 멀티태스킹


터보레포의 이점을 가장 크게 누릴 수 있는 대상은 프론트엔드 플랫폼 엔지니어라 할 수 있습니다.

가장 큰 이득은 빌드 시간을 줄이는 것인데,

보통 CI 러너는 stateless하므로

remote-caching을 이용하여 빌드 아티팩트를 재활용해야 turborepo의 퍼포먼스를 최대한 누릴 수 있을 것입니다.

또한 turborepo를 사용해 프론트엔드 태스크 파이프라인을 쉽게 작성할 수 있습니다.

 

아쉽게도 빌드 아티팩트를 재사용할 수 없는 로컬 개발자들한테는 린트 시간 절약 정도의 효과가 다일 듯 합니다.

보통 개발 모드는 캐시를 disable하고 사용하니까요.


참고

https://turbo.build/repo/docs/handbook/what-is-a-monorepo

 

What is a Monorepo? – Turborepo

Subscribe to our newsletter Subscribe to the Turbo newsletter and stay updated on new releases and features, guides, and case studies.

turbo.build

 

 

반응형