글 작성자: 택시 운전사
반응형

Monorepo vs Polyrepo

☝️ Monorepo

Monorepo는 여러 프로젝트의 소스 코드를 관리하는 데에 하나의 저장소(repository)만 사용하는 것을 의미합니다. 예를 들어 Monorepo를 이용하면 웹 앱 프로젝트, 모바일 앱 프로젝트, 서버 프로젝트가 한 저장소에 있게 됩니다.

Monorepo와 대비되는 개념으로는 Polyrepo(혹은 Multirepo)가 있습니다.

🖐 Polyrepo

Polyrepo는 여러 프로젝트의 소스 코드를 관리하는 데에 여러 개의 저장소를 사용하는 것을 의미합니다. Monorepo와 달리 Polyrepo에서는 웹 앱 프로젝트, 모바일 앱 프로젝트, 서버 프로젝트가 각각의 저장소를 갖게 됩니다.

👍 Monorepo의 장점

통합적인 test, build, release 작업

한 저장소에서 관리되기 때문에 한 번의 커맨드로 여러 패키지의 스크립트(test, build, release)를 실행할 수 있습니다.

코드의 통일성 증가

여러 개의 저장소로 관리될 경우 다른 저장소를 보게 될 기회가 많이 없기 때문에 전체적인 코드 스타일, 아키텍쳐 패턴, 테스트 방법 등에 통일성이 없이 코드가 중구난방이 됩니다. 혹여 통일성을 맞추려해도 통일성에 대한 문서화 작업과 복잡한 적용 과정을 거치게 되면서 많은 시간을 허비하게 됩니다. 또한 같은 기능을 가진 함수를 각자의 방식으로 10개의 저장소에서 사용되었다고하면 불필요한 공간을 차지한다고도 볼 수 있습니다. 그러나, Monorepo로 관리될 경우 이러한 통일성의 문제를 해결할 수 있습니다. 전체 프로젝트에 적용되는 eslint로 코드 스타일을 맞추고 다른 프로젝트 코드를 보면서 아키텍쳐 패턴을 확인할 수 있습니다.

분산된 이슈의 통합

대주제에 해당하는 하나의 저장소에서 이슈를 처리하기 때문에 여러 저장소에서 이슈를 올리면서 논의가 분산되는 것을 막을 수 있습니다.

개별적 모듈 버전 관리

Monorepo로 관리되어도 내부의 프로젝트들은 각각 개별적인 모듈이기 때문에 각각 버전 관리를 할 수 있습니다.

쉬운 코드 공유

기존의 방식대로라면 npm등의 패키지 매니저에 등록하고 해당 패키지를 설치하여 사용하는 방식이지만, Monorepo로 관리된다면 다음과 같이 패키지 매니저 등록 없이 코드를 공유할 수 있습니다.

// ./package.json
{
  "name": "@foo/server",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "dependencies": {
    "@foo/common": "^1.0.1",
    ...
  },
  "scripts": {
      "test": "echo testing server with version: $npm_package_version"
  }
}

공유한 패키지 사용 방법은 다음과 같습니다.

// ./packages/server/*
const commonFunction = require("@foo/common");

commonFunction();
  Monorepo Polyrepo
저장소 갯수 1개 패키지의 수만큼 필요
통일성 높음 낮음
이슈 관리 한 저장소에서 관리 각 저장소에서 관리
코드 재사용성 높음 낮음

🐉 lerna

lerna

lerna는 다중의 패키지가 있는 Monorepo 구조로 된 자바스크립트 프로젝트의 test, build, release 같은 작업을 최적화 시켜주는 툴입니다. lerna는 위에 언급했던 것처럼 여러 저장소의 의존성을 묶어주는 역할과 각 패키지별 버전 업데이트를 도와줍니다.

lerna 프로젝트 구성

lerna를 이용한 프로젝트는 다음과 같은 구조를 가집니다.

lerna-repo/
  packages/
    package_1/package.json
    package_2/package.json
  package.json
  lerna.json

packages

packages는 필요한 node 패키지들을 관리하는 폴더입니다. Polyrepo에서 Monorepo로 전환하는 경우 사용하던 node 패키지를 그대로 넣어서 사용 가능합니다.

package.json

최상위에 있는 package.json는 기존의 package.json처럼 script를 추가하여 개별적으로 혹은 동시에 패키지의 스크립트를 실행할 수 있게 합니다.

lerna.json

lerna.json에서는 lerna관련 커맨드를 사용하는 옵션을 결정합니다.

{
 "packages": [ "packages/*" ], // node 패키지가 들어갈 폴더
 "npmClient": "npm", // npm 혹은 yarn 중 어떤 것을 사용할 지
 "bootstrap": {
  "ignore": // lerna bootstrap에서 제외되는 부분
  "npmClientArgs": // npm install 명령 인자
 },
 "version": "0.0.0"
}

lerna 시작하기

lerna를 이용한 기본적인 프로젝트를 만들어봅시다.
우선, lerna관련 커맨드를 사용해야하기 때문에 lerna를 전역으로 설치합니다.

$ npm install --global lerna

새로운 git 저장소를 생성하고 해당 폴더로 들어갑니다.

$ git init lerna-repo && cd lerna-repo

해당 프로젝트를 lerna 기본 구조로 바꿉니다.

$ npx lerna init

위 작업까지 끝나면 최종적으로 다음과 같은 구조를 가진 프로젝트가 생성됩니다.

lerna-repo/
  packages/
  package.json
  lerna.json

lerna에서 사용할 수 있는 커맨드

lerna init

새로운 lerna 저장소를 생성하거나 현재 저장소를 새로운 lerna 버전으로 업데이트합니다.

lerna bootstrap

각 패키지별로 겹치는 의존성(dependency)은 연결하면서 현재 lerna 저장소의 패키지들의 모든 의존성을 설치합니다. 예를 들어 @foo/server@foo/client에서 같은 lodash 라이브러리를 사용한다면, 겹치는 의존성은 연결되어서 루트 디렉토리의 node_modules에 설치되고, 겹치지 않는 의존성은 각각의 패키지 프로젝트의 node_modules에 설치됩니다.

lerna import <pathToRepo>

로컬에 위치하는 <pathToRepo>에 해당하는 패키지를 커밋 히스토리와 함께 packages/<directory-name>으로 import합니다.

lerna publish

업데이트된 패키지의 새로운 배포를 생성합니다. 새 버전으로 올리고 git과 npm에서 모든 패키지를 업데이트합니다.

lerna changed

지난 배포 이후 어떤 패키지에 변화가 있었는 지 확인합니다.

lerna diff [package?]

지난 배포 이후 개별 혹은 모든 패키지의 diff를 보여줍니다.

lerna run [script]

해당 script를 포함하는 각 패키지에 해당 npm script를 실행합니다. 각 프로젝트에 test관련 script가 존재한다면, 루트 디렉토리에서 lerna run test 커맨드로 모든 프로젝트의 테스트 코드를 실행할 수 있습니다.

lerna ls

현재 lerna 저장소에 있는 모든 public 패키지를 나열합니다.

참고자료

반응형

'Design Pattern' 카테고리의 다른 글

[디자인 패턴] 👆 Monorepo란?  (0) 2019.08.16
[Design Pattern] Atomic Design  (0) 2019.05.30