프론트엔드 코드용 Monorepos 가이드

게시 됨: 2022-03-11

Monorepos는 토론의 뜨거운 주제입니다. 최근에 왜 이러한 유형의 아키텍처를 프로젝트에 사용해야 하고 사용하지 않아야 하는지에 대한 많은 기사가 있었지만 대부분은 어떤 식으로든 편향되어 있습니다. 이 시리즈는 모노레포를 언제 어떻게 사용해야 하는지 이해하기 위해 최대한 많은 정보를 수집하고 설명하려는 시도입니다.

Monorepository 는 기본적으로 제목에 모든 의미가 포함된 아키텍처 개념입니다. 여러 리포지토리를 관리하는 대신 격리된 모든 코드 부분을 하나의 리포지토리에 보관합니다. 격리 라는 단어를 염두에 두십시오. 즉, monorepo는 모놀리식 앱과 공통점이 없음을 의미합니다. 하나의 리포지토리에 여러 종류의 논리적 앱을 보관할 수 있습니다. 예를 들어 웹사이트와 해당 iOS 앱이 있습니다.

monorepo, single repo, multirepo 비교

이 개념은 비교적 오래되었고 약 10년 전에 등장했습니다. Google은 코드베이스를 관리하기 위해 이 접근 방식을 채택한 최초의 회사 중 하나였습니다. 10년 동안 존재했다면 왜 이제서야 그렇게 뜨거운 주제가 되었는지 물을 수 있습니다. 대부분 지난 5~6년 동안 많은 것들이 극적인 변화를 겪었습니다. ES6, SCSS 전처리기, 작업 관리자, npm 등 - 오늘날 작은 React 기반 앱을 유지하려면 프로젝트 번들러, 테스트 스위트, CI/CD 스크립트, Docker 구성을 처리해야 합니다. 이제 작은 앱 대신 많은 기능 영역으로 구성된 거대한 플랫폼을 유지 관리해야 한다고 상상해 보십시오. 아키텍처에 대해 생각하고 있다면 두 가지 주요 작업을 수행하기를 원할 것입니다. 관심사를 분리하고 코드 중복을 방지합니다.

이렇게 하려면 큰 기능을 일부 패키지로 분리한 다음 기본 앱의 단일 진입점을 통해 사용하고 싶을 것입니다. 그러나 이러한 패키지를 어떻게 관리합니까? 각 패키지에는 고유한 워크플로 환경 구성이 있어야 하므로 새 패키지를 만들 때마다 새 환경을 구성하고 모든 구성 파일을 복사하는 등의 작업을 수행해야 합니다. 또는 예를 들어 빌드 시스템에서 무언가를 변경해야 하는 경우 각 리포지토리를 살펴보고 커밋하고 풀 요청을 만들고 각 빌드를 기다려야 하므로 속도가 많이 느려집니다. 이 단계에서 우리는 모노레포를 만나고 있습니다.

자체 구성이 있는 많은 리포지토리 대신 단일 저장소, 즉 하나의 테스트 스위트 러너, 하나의 Docker 구성 파일 및 하나의 Webpack용 구성만 있으면 됩니다. 그리고 여전히 확장성, 관심사를 분리할 수 있는 기회, 공통 패키지와의 코드 공유 및 기타 많은 전문가가 있습니다. 좋은 것 같죠? 글쎄요. 그러나 몇 가지 단점도 있습니다. 야생에서 모노레포를 사용할 때의 정확한 장단점을 자세히 살펴보겠습니다.

모노레포 장점:

  • 모든 구성 및 테스트를 한 곳에서 저장할 수 있습니다. 모든 것이 하나의 리포지토리에 있기 때문에 CI/CD 및 번들러를 한 번 구성한 다음 원격에 게시하기 전에 구성을 재사용하여 모든 패키지를 빌드할 수 있습니다. 단위, e2e 및 통합 테스트도 마찬가지입니다. CI는 추가 구성을 처리할 필요 없이 모든 테스트를 시작할 수 있습니다.
  • 원자적 커밋으로 전역 기능을 쉽게 리팩토링합니다. 각 리포지토리에 대해 pull 요청을 수행하는 대신 변경 사항을 빌드하는 순서를 파악하는 대신 작업 중인 기능과 관련된 모든 커밋을 포함하는 원자적 pull 요청을 만들기만 하면 됩니다.
  • 간소화된 패키지 게시. 공유 코드가 있는 다른 패키지에 종속된 패키지 내부에 새 기능을 구현하려는 경우 단일 명령으로 수행할 수 있습니다. 몇 가지 추가 구성이 필요한 기능이며 이 기사의 도구 검토 부분에서 나중에 설명합니다. 현재 Lerna, Yarn Workspaces 및 Bazel을 비롯한 다양한 도구가 있습니다.
  • 더 쉬운 종속성 관리. 하나의 package.json 만. 종속성을 업데이트할 때마다 각 리포지토리에 종속성을 다시 설치할 필요가 없습니다.
  • 여전히 격리된 상태로 유지하면서 공유 패키지와 함께 코드를 재사용합니다. Monorepo를 사용하면 패키지를 서로 격리된 상태로 유지하면서 다른 패키지의 패키지를 재사용할 수 있습니다. 원격 패키지에 대한 참조를 사용하고 단일 진입점을 통해 사용할 수 있습니다. 로컬 버전을 사용하려면 로컬 심볼릭 링크를 사용할 수 있습니다. 이 기능은 bash 스크립트를 통해 구현하거나 Lerna 또는 Yarn과 같은 추가 도구를 도입하여 구현할 수 있습니다.

모노레포의 단점:

  • 앱의 일부에만 액세스를 제한할 수 있는 방법이 없습니다. 불행히도 모노리포지토리의 일부만 공유할 수는 없습니다. 전체 코드베이스에 대한 액세스 권한을 부여해야 하므로 일부 보안 문제가 발생할 수 있습니다.
  • 대규모 프로젝트에서 작업할 때 Git 성능이 좋지 않습니다. 이 문제는 백만 개 이상의 커밋과 수백 명의 개발자가 동일한 리포지토리에서 매일 동시에 작업을 수행하는 거대한 애플리케이션에서만 나타나기 시작합니다. 이는 Git이 DAG(방향성 비순환 그래프)를 사용하여 프로젝트의 이력을 나타내기 때문에 특히 문제가 됩니다. 커밋 수가 많으면 기록이 깊어짐에 따라 그래프를 이동하는 모든 명령이 느려질 수 있습니다. 참조의 수(즉, 더 이상 필요하지 않은 참조를 제거하여 해결 가능한 분기 또는 태그)와 추적된 파일의 양(무거운 파일 문제는 힘내 LFS).

    참고: 요즘 Facebook은 Mercurial을 패치하여 VCS 확장성 문제를 해결하려고 하며 아마도 곧 그렇게 큰 문제가 되지는 않을 것입니다.

  • 더 높은 빌드 시간. 한 곳에 많은 소스 코드가 있기 때문에 모든 PR을 승인하기 위해 CI가 모든 것을 실행하는 데 훨씬 더 많은 시간이 걸립니다.

도구 검토

모노리포지토리를 관리하기 위한 도구 세트는 지속적으로 증가하고 있으며 현재 모노리포지토리를 위한 다양한 빌딩 시스템에서 길을 잃기가 정말 쉽습니다. 이 리포지토리를 사용하면 항상 인기 있는 솔루션을 알 수 있습니다. 하지만 지금은 자바스크립트와 함께 요즘 많이 사용되는 도구를 간단히 살펴보겠습니다.

  • Bazel은 Google의 단일 저장소 지향 빌드 시스템입니다. Bazel에 대한 추가 정보: Awesome-bazel
  • Yarn은 작업 공간을 통해 monorepos를 지원하는 JavaScript 종속성 관리 도구입니다.
  • Lerna는 Yarn에 구축된 여러 패키지로 JavaScript 프로젝트를 관리하기 위한 도구입니다.

대부분의 도구는 매우 유사한 접근 방식을 사용하지만 약간의 뉘앙스가 있습니다.

monorepo git 저장소의 CI/CD 프로세스 그림

이 기사의 파트 2에서는 Lerna 워크플로우와 다른 도구에 대해 더 깊이 파고들 것입니다. 지금은 내부의 개요를 살펴보겠습니다.

레르나

이 도구는 시맨틱 버전을 처리하고, 워크플로를 구축하고, 패키지를 푸시하는 등의 작업을 수행하는 동안 정말 도움이 됩니다. Lerna의 기본 아이디어는 프로젝트에 격리된 모든 코드 부분이 포함된 패키지 폴더가 있다는 것입니다. 패키지 외에도 예를 들어 src 폴더에 있을 수 있는 기본 앱이 있습니다. Lerna의 거의 모든 작업은 간단한 규칙을 통해 작동합니다. 모든 패키지를 반복하고 패키지 버전 증가, 모든 패키지의 종속성 업데이트, 모든 패키지 빌드 등과 같은 일부 작업을 수행합니다.

Lerna를 사용하면 패키지를 사용하는 방법에 대해 두 가지 옵션이 있습니다.

  1. 원격(NPM)으로 푸시하지 않고
  2. 패키지를 원격으로 푸시

첫 번째 접근 방식을 사용하는 동안 패키지에 대한 로컬 참조를 사용할 수 있으며 기본적으로 해당 패키지를 해결하기 위한 심볼릭 링크에 신경 쓰지 않습니다.

그러나 두 번째 접근 방식을 사용하는 경우 원격에서 패키지를 가져와야 합니다. (예: import { something } from @yourcompanyname/packagename; ), 이는 항상 패키지의 원격 버전을 가져옴을 의미합니다. 로컬 개발의 경우 node_modules/ 내부에 있는 패키지를 사용하는 대신 번들러가 로컬 패키지를 확인하도록 폴더 루트에 심볼릭 링크를 생성해야 합니다. 그렇기 때문에 Webpack이나 선호하는 번들러를 시작하기 전에 모든 패키지를 자동으로 연결하는 lerna bootstrap 을 시작해야 합니다.

단일 노드 패키지 내부의 모듈 네임스페이스 그림

Yarn은 처음에 monorepos를 지원하도록 구축되지 않은 NPM 패키지의 종속성 관리자입니다. 그러나 버전 1.0에서 Yarn 개발자는 Workspaces 라는 기능을 출시했습니다. 출시 당시에는 그렇게 안정적이지 않았지만 잠시 후 프로덕션 프로젝트에 사용할 수 있게 되었습니다.

Workspace 는 기본적으로 자체 package.json 이 있고 몇 가지 특정 빌드 규칙을 가질 수 있는 패키지입니다(예: 프로젝트에서 TypeScript를 사용하는 경우 별도의 tsconfig.json ). 실제로 bash를 사용하여 Yarn Workspace 없이 어떻게든 관리할 수 있고 완전히 동일한 설정을 가질 수 있지만 이 도구는 패키지당 종속성을 업데이트하고 설치 프로세스를 쉽게 하는 데 도움이 됩니다.

작업 공간이 있는 Yarn은 다음과 같은 유용한 기능을 제공합니다.

  1. 모든 패키지의 루트에 있는 단일 node_modules 폴더. 예를 들어 자체 package.json 이 있는 packages/package_apackages/package_b 가 있는 경우 모든 종속성은 루트에만 설치됩니다. 이것이 Yarn과 Lerna가 작동하는 방식의 차이점 중 하나입니다.
  2. 로컬 패키지 개발을 허용하는 종속성 심볼릭 링크.
  3. 모든 종속성에 대한 단일 잠금 파일.
  4. 하나의 패키지에 대해서만 종속성을 다시 설치하려는 경우에 집중된 종속성 업데이트. 이것은 -focus 플래그를 사용하여 수행할 수 있습니다.
  5. Lerna와의 통합. Yarn이 모든 설치/심볼링을 쉽게 처리하고 Lerna가 게시 및 버전 제어를 처리하도록 할 수 있습니다. 이것은 노력이 덜 필요하고 작업하기 쉽기 때문에 지금까지 가장 인기 있는 설정입니다.

유용한 링크:

  • 원사 작업 공간
  • TypeScript 모노 리포지토리 프로젝트를 빌드하는 방법

바젤

Bazel은 다국어 종속성을 처리하고 많은 현대 언어(Java, JS, Go, C++ 등)를 지원할 수 있는 대규모 애플리케이션을 위한 빌드 도구입니다. 대부분의 경우 중소 규모의 JS 애플리케이션에 Bazel을 사용하는 것은 무리지만 대규모에서는 성능으로 인해 많은 이점을 제공할 수 있습니다.

본질적으로 Bazel은 빌드 규칙 및 프로젝트 종속성에 대한 설명이 포함된 파일을 기반으로 프로젝트 빌드를 허용하는 Make, Gradle, Maven 및 기타 도구와 유사합니다. Bazel에 있는 동일한 파일을 BUILD 라고 하며 Bazel 프로젝트의 작업 공간 내부에 있습니다. BUILD 파일은 Python과 매우 유사한 사람이 읽을 수 있는 고급 빌드 언어인 Starlark를 사용합니다.

일반적으로 웹에서 쉽게 찾을 수 있고 이미 구성되어 개발 준비가 된 상용구가 많기 때문에 BUILD 를 많이 다루지 않을 것입니다. 프로젝트를 빌드하려고 할 때마다 Bazel은 기본적으로 다음을 수행합니다.

  1. 대상과 관련된 BUILD 파일을 로드 합니다.
  2. 입력 및 해당 종속성을 분석 하고 지정된 빌드 규칙을 적용하고 작업 그래프를 생성합니다.
  3. 최종 빌드 출력이 생성될 때까지 입력에 대한 빌드 작업을 실행 합니다.

유용한 링크:

  • JavaScript 및 Bazel – JS용 Bazel 프로젝트를 처음부터 설정하기 위한 문서입니다.
  • Bazel용 JavaScript 및 TypeScript 규칙 – JS용 상용구.

결론

Monorepos는 도구일 뿐입니다. 미래가 있는지 없는지에 대해서는 여러 주장이 있지만 사실 이 도구가 제 역할을 하고 효율적으로 처리하는 경우도 있습니다. 지난 몇 년 동안 이 도구는 발전했고 훨씬 더 많은 유연성을 얻었으며 많은 문제를 극복했으며 구성 측면에서 복잡성 계층을 제거했습니다.

열악한 Git 성능과 같이 아직 해결해야 할 문제가 많지만 가까운 시일 내에 이 문제가 해결되기를 바랍니다.

앱을 위한 강력한 CI/CD 파이프라인 을 구축하는 방법을 배우고 싶다면 GitLab CI를 사용하여 효과적인 초기 배포 파이프라인을 구축하는 방법을 추천합니다.

관련: 향상된 Git 흐름 설명