테마
Lerna: 모노레포 관리의 원조
Lerna는 자바스크립트 모노레포를 위한 최초의 도구로, 태스크 실행, 캐싱, 패키지 버전 관리 및 배포 기능을 제공하며, 현재는 NX 팀이 함께 관리하고 있다.
학습 목표
- Lerna의 역사와 모노레포 생태계에서의 위치를 이해한다
- Lerna의 핵심 기능(태스크 실행, 캐싱, 버전 관리)을 설명할 수 있다
- NX와의 합병 이후 Lerna가 어떻게 변화했는지 파악한다
- lerna.json 설정의 주요 항목을 이해한다
- fixed 모드와 independent 모드의 차이를 구분할 수 있다
1. Lerna 소개와 역사
Lerna는 **"자바스크립트 모노레포를 위한 오리지널 툴"**을 표방하는 도구다. 이름은 그리스 신화의 레르나(Lerna) 늪에 사는 히드라에서 유래했다. 여러 개의 머리를 가진 히드라처럼, 하나의 저장소에서 여러 패키지를 관리한다는 의미다.
Lerna의 주요 타임라인:
| 시기 | 사건 |
|---|---|
| 2015~2016 | Lerna 최초 공개. Babel 프로젝트에서 모노레포 관리를 위해 탄생 |
| 2016~2020 | 사실상 표준 도구로 자리잡음. React, Angular, Babel, Jest 등 주요 오픈소스에서 채택 |
| 2020~2022 | 개발 활동 둔화, 유지 보수 중단 위기 선언 |
| 2022 | Nrwl(NX 운영사)이 Lerna를 인수하여 계속 유지/관리하기로 결정 |
| 2022~ | NX의 기능이 Lerna에 통합. 태스크 러너, 캐싱 등을 NX 엔진으로 실행 |
Lerna의 가장 큰 의의는 모노레포 관리의 표준 워크플로를 정립했다는 것이다. "패키지 순회 스크립트 실행", "변경된 패키지 감지", "일괄 버전 범프 및 npm 게시"라는 패턴은 Lerna가 처음 대중화했고, 이후 등장한 도구들도 이 패턴을 기반으로 발전했다.
2. Lerna의 핵심 기능
2.1 태스크 실행
Lerna의 가장 기본적인 기능은 모노레포 내 패키지들에 대해 스크립트를 실행하는 것이다.
bash
# 모든 패키지에서 build 스크립트 실행
lerna run build
# 특정 패키지에서만 실행
lerna run build --scope=my-utils
# 여러 스크립트를 동시에 실행
lerna run lint,test,build--scope 옵션으로 특정 패키지를 지정할 수 있고, 콤마로 구분하여 여러 스크립트를 한 번에 실행할 수 있다. Lerna는 패키지 간 의존 관계를 분석하여 올바른 순서로 태스크를 실행한다.
2.2 캐싱 (NX 엔진 기반)
Lerna가 NX 팀에 인수된 이후, 캐싱 기능은 NX의 태스크 러너 엔진을 그대로 활용한다. 캐싱을 활성화하려면 lerna add-caching 명령을 실행하여 nx.json 설정을 생성한다.
bash
# 캐싱 설정 초기화 (nx.json 생성)
lerna add-caching캐싱이 활성화되면:
- 코드에 변화가 없을 때 이전 빌드 결과를 그대로 재사용한다.
- 캐싱의 출력 디렉토리(예:
dist/)를 지정하면, 삭제 후에도 캐시로부터 복원된다. - 수십 개 패키지의 빌드가 수 초 만에 완료될 수 있다.
2.3 버전 관리
Lerna는 모노레포 내 패키지들의 버전을 일괄적으로 관리하는 기능을 제공한다. 이 기능은 오픈소스 프로젝트에서 특히 유용하다.
bash
# 변경된 패키지의 버전 증가
lerna version
# private이 아닌 패키지만 버전 증가
lerna version --no-private
# 특정 버전으로 지정
lerna version 2.0.0lerna version을 실행하면:
- 마지막 릴리스 이후 변경된 패키지를 감지한다.
- 새로운 버전 번호를 제안한다 (major, minor, patch 등).
package.json의 버전을 업데이트한다.- 변경 로그(CHANGELOG)를 생성한다.
- git 태그를 생성하고 커밋한다.
2.4 배포
bash
# 변경된 패키지를 npm에 게시
lerna publish
# 이미 버전이 올라간 패키지를 게시 (버전 증가 없이)
lerna publish from-packagelerna publish는 버전 관리와 npm 게시를 한 번에 처리한다. 오픈소스 라이브러리를 관리할 때 수십 개의 패키지를 수동으로 게시할 필요 없이, 한 번의 명령으로 변경된 패키지만 자동으로 게시된다.
2.5 프로젝트 그래프
NX의 기능을 그대로 활용하여 패키지 간 의존 관계를 인터랙티브 그래프로 시각화할 수 있다.
bash
# 의존 관계 그래프를 브라우저에서 확인
npx nx graph이 그래프는 패키지를 노드로, 의존 관계를 엣지로 표현하며, 특정 패키지를 클릭하면 해당 패키지의 의존관계만 필터링하여 볼 수 있다.
2.6 변경 감시 (Watch)
파일 변경을 실시간으로 감지하여 자동으로 명령을 실행하는 기능이다.
bash
# 변경된 패키지의 이름과 파일 출력
lerna watch -- echo '$LERNA_PACKAGE_NAME changed: $LERNA_FILE_CHANGES'
# 변경된 패키지만 자동 빌드
lerna watch -- lerna run build --scope=$LERNA_PACKAGE_NAME3. NX와의 합병
Lerna의 역사에서 가장 중요한 전환점은 NX 팀(Nrwl)의 인수다.
합병으로 인한 변화:
| 항목 | 합병 이전 | 합병 이후 |
|---|---|---|
| 태스크 러너 | 자체 구현 (단순) | NX 엔진 사용 (고성능) |
| 캐싱 | 없음 | NX 기반 로컬/리모트 캐싱 |
| 프로젝트 그래프 | 없음 | NX 그래프 활용 |
| VS Code 확장 | 없음 | NX Console 사용 가능 |
| 분산 태스크 실행 | 없음 | NX Cloud DTE 활용 가능 |
| 버전 관리/배포 | Lerna 고유 기능 (유지) | Lerna 고유 기능 (유지) |
실질적으로 현재의 Lerna는 **"NX 엔진 위에 버전 관리/배포 기능을 얹은 도구"**라고 볼 수 있다. 태스크 실행과 캐싱의 핵심 로직은 NX가 담당하고, Lerna는 버전 관리와 게시라는 고유 영역을 담당한다.
4. lerna.json 설정
Lerna 프로젝트의 루트에는 lerna.json 설정 파일이 존재한다.
json
{
"$schema": "https://raw.githubusercontent.com/lerna/lerna/main/packages/lerna/schemas/lerna-schema.json",
"version": "1.0.0",
"npmClient": "pnpm",
"packages": [
"packages/*",
"apps/*"
],
"command": {
"version": {
"conventionalCommits": true,
"message": "chore(release): publish %s"
},
"publish": {
"registry": "https://registry.npmjs.org"
}
}
}주요 설정 항목:
| 항목 | 설명 |
|---|---|
version | 현재 버전. "independent"로 설정하면 independent 모드 |
npmClient | 사용할 패키지 매니저 (npm, yarn, pnpm) |
packages | 패키지가 위치한 디렉토리 글로브 패턴 |
command.version | 버전 관리 관련 설정 (커밋 컨벤션, 메시지 형식 등) |
command.publish | 배포 관련 설정 (레지스트리 URL 등) |
캐싱과 태스크 오케스트레이션 설정은 nx.json에 별도로 정의한다. lerna add-caching 명령으로 초기 설정을 생성할 수 있다.
5. 버전 관리 모드: Fixed vs Independent
Lerna는 두 가지 버전 관리 전략을 제공한다.
5.1 Fixed 모드 (기본값)
모든 패키지가 동일한 하나의 버전을 공유한다. lerna.json의 version 필드에 구체적인 버전 번호(예: "1.0.0")를 기입한다.
json
{
"version": "1.0.0"
}특징:
lerna version을 실행하면 변경된 패키지가 하나라도 있으면 전체 버전이 올라간다.- Babel, React 등 대규모 오픈소스 프로젝트에서 사용하는 방식이다.
- 모든 패키지의 버전이 동기화되어 있으므로 호환성 파악이 쉽다.
- 단, 하나의 패키지만 변경되어도 전체 버전이 올라가므로 불필요한 버전 증가가 발생할 수 있다.
5.2 Independent 모드
각 패키지가 독립적인 버전을 갖는다. lerna.json의 version 필드를 "independent"로 설정한다.
json
{
"version": "independent"
}특징:
lerna version을 실행하면 변경된 패키지마다 개별적으로 버전을 선택한다.- 패키지 A는
1.2.0, 패키지 B는3.0.1처럼 서로 다른 버전을 가질 수 있다. - 각 패키지의 변경 규모에 맞는 정확한 시맨틱 버저닝이 가능하다.
- 버전 관리가 복잡해지고, 패키지 간 호환성을 별도로 추적해야 한다.
모드 비교:
| 비교 항목 | Fixed 모드 | Independent 모드 |
|---|---|---|
| 버전 동기화 | 전체 동일 | 패키지별 독립 |
| 버전 증가 단위 | 전체 일괄 | 변경된 패키지만 |
| 호환성 추적 | 쉬움 (동일 버전 = 호환) | 어려움 (별도 추적 필요) |
| 사용 사례 | 통합 프레임워크, 대형 라이브러리 | 독립적인 유틸리티 패키지 모음 |
| 대표 프로젝트 | Babel, React | Gatsby 플러그인 |
6. Lerna 프로젝트 기본 구조
my-monorepo/
lerna.json # Lerna 설정
nx.json # NX 캐싱/태스크 설정 (lerna add-caching 후 생성)
package.json # 루트 패키지 (workspaces 정의)
packages/
my-utils/
package.json # name: "my-utils"
src/
index.ts
my-ui/
package.json # name: "my-ui", dependencies: { "my-utils": "..." }
src/
index.ts
apps/
my-app/
package.json # name: "my-app", dependencies: { "my-ui": "..." }
src/
index.ts7. Lerna를 선택해야 하는 경우
Lerna가 적합한 프로젝트:
- npm에 여러 패키지를 게시하는 오픈소스 라이브러리 프로젝트
- 기존에 Lerna를 사용하고 있어 NX 기능을 점진적으로 도입하고 싶은 프로젝트
- 버전 관리와 변경 로그 자동화가 중요한 프로젝트
- 학습 곡선을 최소화하면서 모노레포 관리를 시작하고 싶은 프로젝트
Lerna보다 다른 도구가 나은 경우:
- 태스크 오케스트레이션과 프로젝트 분석에 더 세밀한 제어가 필요하면 -> NX 직접 사용
- 초고성능 빌드와 단순한 설정을 원하면 -> TurboRepo
- 대규모 팀의 정책 관리와 거버넌스가 중요하면 -> Rush
핵심 정리
| 핵심 개념 | 요약 |
|---|---|
| Lerna의 위치 | 자바스크립트 모노레포 관리의 원조. 많은 오픈소스에서 채택한 사실상 표준이었다 |
| NX 합병 | 2022년 Nrwl이 인수하여 NX 엔진을 기반으로 캐싱, 그래프 등 기능을 강화했다 |
| 태스크 실행 | lerna run 명령으로 전체 또는 특정 패키지에서 스크립트를 실행한다 |
| 캐싱 | NX 엔진 기반 로컬/리모트 캐싱을 지원한다. lerna add-caching으로 활성화 |
| 버전 관리 | Fixed 모드(전체 동일 버전)와 Independent 모드(패키지별 독립 버전)를 제공한다 |
| 배포 | lerna publish로 변경된 패키지를 자동으로 npm에 게시한다 |
| 현재 상태 | NX 엔진 위에 버전 관리/배포를 얹은 형태. Lerna와 NX가 밀접하게 연동된다 |
다음 단계
이번 장에서는 Lerna의 역사, 핵심 기능, NX와의 관계를 살펴보았다. 다음 장에서는 Lerna가 엔진으로 사용하는 NX를 직접 살펴보고, NX가 제공하는 프로젝트 그래프 분석과 고급 빌드 시스템 기능을 학습한다.