기타

레르나(Lerna)를 활용하여 모노레포를 관리하는 방법

Joonfluence 2021. 12. 23.

서론

대상독자

  • 규모가 큰 웹 어플리케이션 개발 상황에서 코드 재활용성을 높임으로써 프로젝트를 효과적으로 관리해주고 싶은 프론트엔드 개발자
  • 멀티레포 구조에서 매번 새롭게 코드를 클론하고 prettier나 eslint 등 개발환경을 셋팅하는데 번거로움을 느끼는 프론트엔드 개발자
  • 기존 멀티레포 구조에서의 의존성 관리를 lerna를 통해, 모노레포 구조에서 편한 방식으로 해결하고 싶은 개발자

오늘의 학습 목표

  • 레르나의 역할과 기능에 대하여 이해하고 활용할 줄 안다.
  • 모노레포의 정의와 필요성에 대해서 알고 실제 프로젝트에 적용할 수 있다.
  • 모노레포 환경 아래에서 특정 프로젝트에 필요한 라이브러리를 설치하는 방법에 대해서 안다.
  • 모노레포 환경 아래에서 프로젝트에 필요한 공통 라이브러리를 설치하는 방법에 대해서 안다.

본론

이번 글에서 다루고 싶은 주제는 두 가지입니다. 첫째는 멀티레포와 모노레포의 차이점을 비교함으로써 모노레포 구조의 장점을 파악하는 것이며, 두번째는 레르나를 활용하여 모노레포 프로젝트를 관리하는 방법에 대해서 아는 것입니다. 그럼 본격적인 설명에 앞서, 모노레포란 무엇인지. 레르나란 무엇인지 등에 대해서 먼저 알아보도록 하겠습니다.

모노레포란 무엇이며, 멀티레포와의 차이점은 무엇일까?

먼저 모노레포란 두 개 이상의 프로젝트 코드를 하나의 저장소(repository)에서 관리하는 기법을 말합니다. 이는 하나의 프로젝트 코드를 갖는 저장소를 여러개 두는 멀티레포 구조와 차이점이 있습니다. 그렇다면 모노레포와 멀티레포 중 어느 것이 프로젝트를 관리하는 데 더 적합할까요? 이에 답하려면, 버전관리의 측면에서 프로젝트를 살펴봐야 합니다. 버전 관리 시스템은 버전 관리의 주체로서 저장소를 사용합니다. 만약 저장소가 달라진다면 한 프로젝트를 진행하는 사람들은 다른 프로젝트의 버젼 기록을 확인할 수 없게 됩니다.

결국 코드 재활용성과 의존성 관리 측면에서 차이가 나게 됩니다. 전자는 하나의 저장소를 사용하므로 한 프로젝트에서 다른 프로젝트 코드를 참조할 수 있어 코드 재활용성을 높일 수 있게 될 것입니다. 또한 레르나라는 툴을 활용하면, 손쉽게 프로젝트 전체의 의존성를 할 수도 있어 각 저장소의 의존성 관리를 각각 관리할 필요가 없게 됩니다. 반대로 여러 레포지토리를 하나로 통합했을 때 발생할 수 있는 문제점은 프로젝트 용량이 그만큼 늘어나게 된다는 것입니다. 용량이 늘어나게 되면, 자연스레 배포에 걸리는 시간도 늘어나게 될 것입니다. 그만큼 관리하기도 어려워지겠죠. 또한 모노레포 구조에서 프로젝트를 진행한다면 불필요하게 작성된 공용 코드가 늘어날 가능성이 높아지게 됩니다. 한 프로젝트에서만 쓰일거란 보장이 없을 때, 일단 공용 컴포넌트에 넣는게 가장 빠른 길이기 때문이죠. 따라서 프로젝트 크기가 너무 커지지는 않는지 항상 점검하고 불필요한 코드들을 제거할 필요가 있습니다.

그러면 우리 팀은 어떤 구조를 선택해야 할까?

한 팀에서 단일하고 규모 있는 프로그램을 개발하는 상황이라면 전자가 더 적합한 형태일 것입니다. 반대로 프로젝트 간의 유사성이 적고 의존성을 분리해야 하는 상황이라면, 멀티레포 구조를 활용하는 것이 더 올바를 것입니다. 그런 경우는 어떤 경우일까요? 한 회사에서 독립적인 여러 프로젝트를 한다고 합시다. 프로젝트 내용이 겹치지 않는 경우, 여러 저장소를 합치면 되려 문제를 발생시킬 가능성이 높습니다. 프로젝트 내, 각각의 패키지들을 따로 관리해줘야 하는데 그게 불가능해지기 때문이죠.

레르나의 필요성과 npm과의 차이점

레르나를 통해 실제 모노레포 구조를 구성하고 프로젝트의 의존성을 관리하는 방법에 대해 알아보기 앞서, 프로젝트 의존성이란 무엇일까요? 이를 설명하기 위해선, npm(node package manager)도 함께 알 필요가 있습니다. 알다싶이, npm은 package.json 파일을 통해, 각 패키지에서 활용하는 라이브러리들(nodejs 라이브러리들)의 버젼 정보들을 관리해줍니다. 프로젝트를 진행하다보면 라이브러리 버젼이 업데이트되거나 depreacate되는 경우가 있기 때문에, 소프트웨어의 유지보수 측면에서 패키지 의존성 관리는 중요한 일 중 하나입니다. lerna 역시 내부적으로는 git과 npm (혹은 yarn을 선택할 수 있음)을 활용합니다. 그리고 모노레포로 구성된 프로젝트 전체의 의존성을 관리하는 용도로 활용됩니다.

레르나로 모노레포 구조를 실제 프로젝트에 적용하는 방법

이를 위해 레르나를 설치해줍니다. 실제 프로젝트에 적용해보며 레르나 활용 방법에 대해서 알아보도록 하겠습니다. 먼저, 레르나를 설치해줍니다. 레르나는 두 가지 방법으로 설치할 수 있습니다.

npm install -g lerna or npx lerna init

Git Repository에 등록될 경로를 등록해줍니다.

mkdir [DIRECTORY]
cd [DIRECTORY]
git init [DIRECTORY]

이제 git 대신 lerna로 해당 저장소를 관리해주겠습니다.

lerna init --independent // or -i

그러면 3가지 폴더가 생성되는 것을 알 수 있습니다. /packages, lerna.json, package.json 입니다. 각각의 역할에 대해서 살펴보도록 하겠습니다. 먼저 packages에는 모노레포 구조에 속하는 프로젝트들이 위치합니다. lerna.json에선 레르나 관련 설정을 해줍니다. packages에선 배포와 관리된 packages를 명시해줄 수 있습니다. npmClient에선 npm 혹은 yarn 중 어떤 패키지 관리자를 사용할 것인지 설정해줄 수 있습니다. version에선 버젼 정책을 명시할 수 있습니다.

{
  "packages": [
    "packages/*"
  ],
  "version": "independent",
  "npmClient": "npm"
}

package.json에는 lerna가 설치된 것을 볼 수 있습니다. 참고로 모노레포 구조에 속할 각 프로젝트에는 package.json 파일이 존재하게 될 겁니다. 그러나 이 파일은 Root 경로에 있기 때문에 공통된 node_module들의 의존성을 관리해줍니다.

{
  "name": "root",
  "private": true,
  "devDependencies": {
    "lerna": "^4.0.0"
  }
}

그러면 본격적으로 하위 프로젝트들을 생성해주도록 하겠습니다. 수동으로 직접 생성할 수도 있지만 lerna의 create 명령을 통해서도 기본 패키지의 구조를 지정해 줄 수 있습니다.

lerna create [PACKAGE_NAME]

실행 결과는 다음과 같습니다. 그러면 성공적으로 하위프로젝트가 생성된 것을 볼 수 있습니다.

├── lerna.json
├── package.json
└── packages
    └── package_1
        ├── README.md
        ├── __tests__
        │   └── package_1.test.js
        ├── lib
        │   └── package_1.js
        └── package.json

이번에는 Root 경로에 프로젝트 전체에서 공통적으로 사용할 수 있는 요소를 다운로드하는 방법에 대해서 알아보도록 하겠습니다. 루트 경로에서

npm install [PACKAGE_NAME] --dev

각 패키지에 모듈을 설치하기 위해서는 아래와 같이, 스코프를 지정해줘야 합니다.

lerna add [PACKAGE_NAME] --scope [하위프로젝트명]

각 패키지의 모든 외부 종속성을 한번에 설치해줄 수 있습니다. 그럴 땐, 아래 명령어를 활용할 수 있습니다. 실행하면, 레르나에서 각 패키지들을 npm install로 설치해주고 자동으로 내부 종속성도 관리해줍니다. 이때 공통된 module 들은 root 의 node_modules 에 install 하고 각 package 들에 연결됩니다. 만약 각 package 별로 버전이 다를 경우, warning 메시지를 띄우고, package 의 node_modules 에 install 됩니다.

참고로 레르나에서는 모듈을 삭제하는 명령어는 따로 존재하지 않습니다. 따라서 pacakge.json의 해당 모듈을 제거하고 lerna bootstrap을 실행해줘야 합니다.

lerna bootstrap 

설치 뿐 아니라, 삭제도 가능합니다. 패키지 간 의존성이 꼬일 때, leana clean 이후 lerna bootstrap으로 다시 설치해줄 수 있습니다.

lerna clean 

lerna를 통해 패키지를 NPM Repository로 배포할 때 사용합니다.

lerna publish

마무리

이것으로 모노레포에 대하여 간단하게 알아보고 레르나 활용 방법에 대해서 정리하는 시간을 갖았습니다. 그 외에도 다양한 명령어들이 있지만, 주요한 명령어들은 다 살펴보았으니 추가 학습을 원하시는 분들은 (공식문서)[]를 참조하시면 좋을 것 같습니다. 다음번에는 이러한 레르나의 기술적인 특성은 무엇이며, 실제 패키지를 레르나를 통해 NPM Repository에 직접 배포해보는 시간을 갖도록 하겠습니다. 읽어주셔서 감사합니다.

참고한 사이트

다중 저장소 vs 단일 저장소
Mono Repo 를 위한 Lerna 간단 정리하기
Lerna를 활용한 Mono-Repo 구축 완벽 가이드 - 예제를 통한 완벽 파악

반응형

댓글