Skip to content

Lerna: 모노레포 관리의 원조

Lerna는 자바스크립트 모노레포를 위한 최초의 도구로, 태스크 실행, 캐싱, 패키지 버전 관리 및 배포 기능을 제공하며, 현재는 NX 팀이 함께 관리하고 있다.

학습 목표

  • Lerna의 역사와 모노레포 생태계에서의 위치를 이해한다
  • Lerna의 핵심 기능(태스크 실행, 캐싱, 버전 관리)을 설명할 수 있다
  • NX와의 합병 이후 Lerna가 어떻게 변화했는지 파악한다
  • lerna.json 설정의 주요 항목을 이해한다
  • fixed 모드와 independent 모드의 차이를 구분할 수 있다

1. Lerna 소개와 역사

Lerna는 **"자바스크립트 모노레포를 위한 오리지널 툴"**을 표방하는 도구다. 이름은 그리스 신화의 레르나(Lerna) 늪에 사는 히드라에서 유래했다. 여러 개의 머리를 가진 히드라처럼, 하나의 저장소에서 여러 패키지를 관리한다는 의미다.

Lerna의 주요 타임라인:

시기사건
2015~2016Lerna 최초 공개. Babel 프로젝트에서 모노레포 관리를 위해 탄생
2016~2020사실상 표준 도구로 자리잡음. React, Angular, Babel, Jest 등 주요 오픈소스에서 채택
2020~2022개발 활동 둔화, 유지 보수 중단 위기 선언
2022Nrwl(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.0

lerna version을 실행하면:

  1. 마지막 릴리스 이후 변경된 패키지를 감지한다.
  2. 새로운 버전 번호를 제안한다 (major, minor, patch 등).
  3. package.json의 버전을 업데이트한다.
  4. 변경 로그(CHANGELOG)를 생성한다.
  5. git 태그를 생성하고 커밋한다.

2.4 배포

bash
# 변경된 패키지를 npm에 게시
lerna publish

# 이미 버전이 올라간 패키지를 게시 (버전 증가 없이)
lerna publish from-package

lerna 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_NAME

3. 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.jsonversion 필드에 구체적인 버전 번호(예: "1.0.0")를 기입한다.

json
{
  "version": "1.0.0"
}

특징:

  • lerna version을 실행하면 변경된 패키지가 하나라도 있으면 전체 버전이 올라간다.
  • Babel, React 등 대규모 오픈소스 프로젝트에서 사용하는 방식이다.
  • 모든 패키지의 버전이 동기화되어 있으므로 호환성 파악이 쉽다.
  • 단, 하나의 패키지만 변경되어도 전체 버전이 올라가므로 불필요한 버전 증가가 발생할 수 있다.

5.2 Independent 모드

각 패키지가 독립적인 버전을 갖는다. lerna.jsonversion 필드를 "independent"로 설정한다.

json
{
  "version": "independent"
}

특징:

  • lerna version을 실행하면 변경된 패키지마다 개별적으로 버전을 선택한다.
  • 패키지 A는 1.2.0, 패키지 B는 3.0.1처럼 서로 다른 버전을 가질 수 있다.
  • 각 패키지의 변경 규모에 맞는 정확한 시맨틱 버저닝이 가능하다.
  • 버전 관리가 복잡해지고, 패키지 간 호환성을 별도로 추적해야 한다.

모드 비교:

비교 항목Fixed 모드Independent 모드
버전 동기화전체 동일패키지별 독립
버전 증가 단위전체 일괄변경된 패키지만
호환성 추적쉬움 (동일 버전 = 호환)어려움 (별도 추적 필요)
사용 사례통합 프레임워크, 대형 라이브러리독립적인 유틸리티 패키지 모음
대표 프로젝트Babel, ReactGatsby 플러그인

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.ts

7. 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가 제공하는 프로젝트 그래프 분석과 고급 빌드 시스템 기능을 학습한다.

다음: NX - 고급 빌드 시스템 ->