pnpm 모노레포의 버전 bump, CHANGELOG, GitHub Actions 배포를 Changesets로 자동화하는 방법
모노레포를 운영하다 보면 꼭 한 번씩 겪는 순간이 있습니다. 패키지 A를 고쳤는데 그걸 의존하는 패키지 B의 버전 올리는 걸 깜빡했거나, CHANGELOG를 수동으로 작성하다가 "이게 언제 바뀐 거지?" 싶어지는 상황이요. 저도 처음 모노레포를 맡았을 때 package.json을 네 개씩 열어놓고 버전을 하나씩 손으로 고쳤던 기억이 납니다. 지금 생각하면 꽤 아찔한 방식이었죠.
Changesets는 이 문제를 "변경의 의도"와 "배포 행위"를 분리하는 방식으로 풀어냅니다. 개발자는 코드를 바꿀 때 그 변경이 어느 패키지에 어느 정도 영향을 주는지 작은 마크다운 파일 하나로 기록하고, 나머지 버전 계산과 CHANGELOG 생성은 도구가 알아서 처리하는 구조입니다. 이 글은 pnpm 워크스페이스 기반 모노레포를 이미 운영 중이거나 구성해본 독자를 기준으로 합니다. GitHub Actions를 처음 접하거나 npm 배포 경험이 없다면 일부 섹션에서 추가 참고 자료가 필요할 수 있습니다.
이 글을 읽고 나면 pnpm 워크스페이스에서 Changesets를 설치하고, GitHub Actions로 버전 bump부터 npm 배포까지 자동화하는 전체 파이프라인을 직접 구성할 수 있게 됩니다.
핵심 개념
그렇다면 Changesets가 실제로 어떤 방식으로 이 문제를 푸는지, 개념부터 짚고 넘어가겠습니다.
Changesets가 버전 관리를 바라보는 방식
일반적인 버전 관리 도구들은 커밋 메시지를 파싱해서 버전을 결정합니다. feat:, fix: 같은 prefix를 보고 minor냐 patch냐를 판단하는 방식이죠. 이 방법도 잘 동작하지만, 모노레포에서는 한 커밋이 여러 패키지에 걸쳐 다른 수준의 변경을 일으킬 때 표현이 애매해집니다.
Changesets의 접근은 다릅니다. 변경 내용을 별도 파일에 명시적으로 적어두는 것입니다.
---
"@myorg/ui": minor
"@myorg/utils": patch
---
Button 컴포넌트에 `variant` prop 추가, 유틸 함수 버그 수정이 파일이 .changeset/ 디렉토리에 쌓이면, changeset version 커맨드가 이를 읽어 각 패키지의 버전을 SemVer 규칙에 따라 bump하고 CHANGELOG를 자동으로 작성합니다. 이 파일은 PR과 함께 커밋되므로, 릴리즈 의도가 git 히스토리에 자연스럽게 남게 됩니다.
SemVer (Semantic Versioning)
major.minor.patch형식의 버전 규칙입니다. 하위 호환이 깨지면 major, 기능 추가는 minor, 버그 수정은 patch를 올립니다.
3단계 워크플로
핵심 흐름은 단순합니다.
# 1. 변경 내용 기술 — .changeset/*.md 파일 생성
pnpm changeset
# 2. 버전 bump + CHANGELOG 업데이트
pnpm changeset version
# 3. npm 배포
pnpm changeset publish실무에서는 1단계는 개발자가 PR마다 직접 수행하고, 2~3단계는 GitHub Actions가 자동으로 처리하는 구조로 운영하는 경우가 많습니다.
pnpm 워크스페이스와의 통합
Changesets는 pnpm-workspace.yaml을 읽어 패키지 간 의존 관계를 자동으로 파악합니다. updateInternalDependencies 설정이 이 부분을 제어하는데, 내부 패키지 A의 버전이 올라갔을 때 이를 의존하는 패키지 B가 최소 어느 수준으로 bump될지를 결정하는 옵션입니다. 기본값인 "patch"로 설정하면, A가 patch로 올라갈 때 B의 package.json에 명시된 A 버전 범위도 patch 기준으로 자동 갱신됩니다. 수동으로 의존 패키지를 추적할 필요가 없어지는 부분이라 모노레포에서 특히 유용합니다.
# pnpm-workspace.yaml
packages:
- 'packages/*'
- 'apps/*'실전 적용
예시 1: 기본 설치부터 첫 changeset 생성까지
먼저 루트에서 CLI를 설치하고 초기화를 진행합니다. pnpm 최신 버전에서는 워크스페이스 루트에 설치할 때 -w 플래그가 필요합니다.
pnpm add -D -w @changesets/cli
pnpm changeset init초기화하면 .changeset/config.json이 생성됩니다. 자주 건드리게 되는 주요 옵션만 살펴보면 이렇습니다.
{
"$schema": "https://unpkg.com/@changesets/config/schema.json",
"changelog": "@changesets/cli/changelog",
"commit": false,
"fixed": [],
"linked": [],
"access": "restricted",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": []
}
"access": "restricted"가 기본값입니다.@myorg/ui처럼@로 시작하는 스코프 패키지를 공개 배포할 경우 반드시"public"으로 변경해야 합니다. 이 설정을 그대로 두고 배포를 시도하면 접근 권한 오류로 실패합니다.
저도 처음 init하고 이 설정을 지나쳤다가 첫 배포에서 오류를 만났던 기억이 납니다.
| 옵션 | 역할 | 주의사항 |
|---|---|---|
changelog |
CHANGELOG 생성 형식 지정 | GitHub 링크 포함하려면 @changesets/changelog-github로 교체 |
access |
npm 배포 접근 권한 | 스코프 패키지를 공개 배포하면 "public"으로 변경 필요 |
updateInternalDependencies |
내부 패키지가 버전업될 때 의존 패키지에 전파되는 최소 bump 타입 | "patch" 또는 "minor" 선택 가능 |
baseBranch |
PR 비교 기준 브랜치 | 기본값 main, 다른 전략 사용 시 수정 |
pnpm changeset을 실행하면 인터랙티브 CLI가 열립니다. 어떤 패키지가 변경됐는지 선택하고, 변경 유형(major/minor/patch)을 고르고, 한 줄 요약을 작성하면 .changeset/ 디렉토리에 랜덤한 이름의 마크다운 파일이 생성됩니다. 이 파일을 PR에 포함시켜 커밋하는 것으로 충분합니다.
package.json에 스크립트로 등록해두면 팀 전체가 일관된 명령어를 사용할 수 있습니다.
{
"scripts": {
"changeset": "changeset",
"version-packages": "changeset version",
"release": "pnpm build && changeset publish"
}
}예시 2: GitHub Actions로 자동 배포 파이프라인 구성
솔직히 이 부분이 처음에 가장 헷갈렸습니다. changesets/action이 상황에 따라 다른 동작을 한다는 게 문서를 읽어도 잘 안 와닿거든요.
# .github/workflows/release.yml
name: Release
on:
push:
branches:
- main
concurrency: ${{ github.workflow }}-${{ github.ref }}
jobs:
release:
name: Release
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup pnpm
uses: pnpm/action-setup@v3
with:
version: 9
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Create Release Pull Request or Publish
id: changesets
uses: changesets/action@v1
with:
publish: pnpm release
version: pnpm version-packages
commit: "chore: version packages"
title: "chore: version packages"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}이 워크플로는 두 가지 상황에서 다르게 동작합니다.
| 상황 | 동작 |
|---|---|
.changeset/에 미처리 파일이 있을 때 |
"chore: version packages" 제목의 PR 자동 생성 또는 업데이트 |
| 해당 PR이 main에 머지된 후 | pnpm release 실행 → npm 배포 |
fetch-depth: 0 옵션은 빠뜨리기 쉬운데, 태그 히스토리 전체를 가져오지 않으면 버전 계산이 꼬일 수 있어서 꼭 챙겨야 하는 설정입니다.
아래 예시 3, 4는 기본 설정이 익숙해진 뒤 필요에 따라 도입해볼 수 있는 고급 기능입니다. 처음 Changesets를 적용하는 상황이라면 예시 1, 2만으로도 충분합니다.
예시 3: Snapshot Release로 프리뷰 배포
정식 릴리즈 전에 CI에서 통합 테스트를 돌려보고 싶을 때 유용한 기능입니다. 실제 버전 번호 대신 타임스탬프가 붙은 임시 버전을 npm에 올려두는 방식입니다.
pnpm changeset version --snapshot canary
pnpm changeset publish --tag canary
# 결과: @myorg/ui@0.0.0-canary-20250512123456이렇게 배포된 버전은 @myorg/ui@canary 태그로 설치할 수 있어서, 실제 패키지를 쓰는 앱에서 PR 단계에서부터 미리 검증해볼 수 있습니다.
한 가지 주의할 점은, changeset version --snapshot 실행 후 변경된 package.json 파일들은 기본 브랜치에 커밋하지 않는 것이 좋습니다. 처음 snapshot 릴리즈를 써봤을 때 이 점을 몰라서 스냅샷 버전이 main에 올라가버린 적이 있었는데, 이후로는 별도 브랜치에서 작업하거나 git stash를 활용해 snapshot 버전 파일이 기본 브랜치로 들어가지 않도록 관리하고 있습니다.
예시 4: Fixed 모드로 패키지 묶음 버전 관리
UI 컴포넌트 라이브러리를 React, Vue, 코어 이렇게 세 패키지로 분리해두고 항상 같이 릴리즈하고 싶을 때 fixed 모드를 활용할 수 있습니다.
{
"fixed": [["@myorg/core", "@myorg/react", "@myorg/vue"]]
}셋 중 하나만 변경되더라도 나머지 둘도 동일 버전으로 함께 bump됩니다. 실제로 어떤 결과물이 나오는지 보면 이렇습니다.
// 변경 전
// packages/core/package.json
{ "name": "@myorg/core", "version": "2.0.1" }
// packages/react/package.json (직접 변경 없음)
{ "name": "@myorg/react", "version": "2.0.1" }
// @myorg/core에만 minor changeset이 있어도 결과:
// packages/core/package.json
{ "name": "@myorg/core", "version": "2.1.0" }
// packages/react/package.json
{ "name": "@myorg/react", "version": "2.1.0" }사용자 입장에서 @myorg/react@2.1.0과 @myorg/core@2.0.1을 혼용하는 혼란을 방지할 수 있습니다. 팀에서 UI 라이브러리를 분리 패키지로 운영하면서 버전 혼용 문의가 생겼는데, fixed 모드로 전환한 뒤로 그 문의가 뚝 끊겼습니다.
장단점 분석
장점
| 항목 | 내용 | 활용 팁 |
|---|---|---|
| 변경 의도가 git에 남는다 | changeset 파일이 PR과 함께 커밋되어 "왜 버전이 올라갔는지" 언제든 추적 가능 | @changesets/changelog-github로 PR 링크까지 CHANGELOG에 포함 가능 |
| 의존 패키지 버전 누락 방지 | 내부 패키지 변경 시 이를 의존하는 패키지 버전이 자동 bump됨 | updateInternalDependencies를 "minor"로 올리면 더 보수적으로 운영 가능 |
| CHANGELOG가 저절로 채워진다 | 패키지별 CHANGELOG.md가 changeset version 실행 시 자동 생성·갱신 |
커밋 메시지 대신 changeset 요약이 항목이 되므로 메시지 품질이 자연히 높아짐 |
| 릴리즈 준비가 PR 단위로 분산된다 | 담당자 한 명이 몰아서 하던 버전 관리 작업이 개발자별 PR로 자연스럽게 분산됨 | CI에서 changeset 파일 누락을 체크하면 팀 규율 유지가 쉬워짐 |
| 다양한 릴리즈 시나리오 지원 | fixed, linked, snapshot, prerelease 등 상황에 맞는 모드 선택 가능 | 초반엔 기본 모드만 써도 충분, 필요할 때 점진적으로 도입 권장 |
| 빌드 도구에 독립적 | Turborepo, Nx, 일반 pnpm workspace 어디서든 동일하게 동작 | 빌드 도구를 나중에 바꿔도 릴리즈 파이프라인은 그대로 유지됨 |
단점 및 주의사항
| 항목 | 내용 | 대응 방안 |
|---|---|---|
| PR마다 changeset 파일을 추가해야 한다 | 빠뜨리는 순간 자동화가 멈춤 | changesets/action의 hasChangesets 출력값으로 누락 PR 체크 가능 |
| 초기 설정에 손이 많이 간다 | GitHub Actions, npm 토큰, 저장소 권한 등 학습 비용 존재 | 이 글의 예시 워크플로를 그대로 붙여넣는 것으로 시작 권장 |
| 패키지 수가 많아지면 느려질 수 있다 | 수십 개 이상이면 changeset version 실행 시간이 길어짐 |
ignore 설정으로 배포 불필요한 패키지 제외 |
| 프리릴리즈 관리가 복잡하다 | alpha/beta/rc 관리는 별도 브랜치 전략이 필요 | 공식 문서의 pre-release 모드 가이드 참고 |
실무에서 가장 흔한 실수
-
.changeset/디렉토리를.gitignore에 넣는 경우 — changeset 파일은 반드시 git에 추적되어야 합니다. "자동 생성 파일이니까 무시해야지"라는 생각에.gitignore에 추가하면,changeset version이 읽을 파일이 없어서 아무 일도 일어나지 않습니다. -
GitHub Actions 체크아웃에서
fetch-depth: 0누락 — 기본값은 shallow clone이라 git 태그 히스토리가 없습니다. 버전 태그를 기반으로 동작하는 부분에서 예상치 못한 오류가 발생할 수 있어서, 꼭 챙겨두는 것을 권장합니다. -
pnpm release에서 빌드를 먼저 실행하지 않는 경우 —changeset publish는 빌드 결과물을 그대로 배포합니다."release": "changeset publish"만 넣으면 빌드 전 상태가 올라갈 수 있으니,pnpm build && changeset publish순서로 구성해두는 것이 좋습니다.
마치며
Changesets는 버전 관리의 책임을 도구에 넘기되, 변경의 의도만큼은 개발자가 직접 기록하도록 설계된 도구입니다. package.json을 여러 개 열어놓고 버전을 손으로 고치던 작업, CHANGELOG를 누군가 몰아서 작성하던 그 부담이 파이프라인 안으로 흡수됩니다.
지금 바로 시작해볼 수 있는 3단계:
- 기존 pnpm 모노레포 루트에서
pnpm add -D -w @changesets/cli && pnpm changeset init을 실행해보시면 됩니다..changeset/config.json이 생성되면access값과baseBranch만 프로젝트에 맞게 수정해두는 것으로 충분합니다. - 다음 PR을 올릴 때
pnpm changeset을 한 번 실행해 changeset 파일을 함께 커밋해보시면, 파일 내용이 얼마나 직관적인지 바로 체감됩니다. - 기본 동작에 익숙해지셨다면 이 글의 GitHub Actions 예시를
.github/workflows/release.yml에 붙여넣고GITHUB_TOKEN과NPM_TOKEN시크릿만 설정해두시면 배포까지 자동화됩니다.
참고 자료
- Using Changesets with pnpm | pnpm 공식 문서
- changesets/changesets | GitHub 공식 저장소
- Changesets 공식 문서 사이트
- changesets/action | GitHub Actions 공식 액션
- Config File Options | Changesets 공식 문서
- Snapshot Releases | Changesets 공식 문서
- Changesets for Versioning | Vercel Academy
- Complete Monorepo Guide: pnpm + Workspace + Changesets (2025) | jsdev.space
- Monorepo Architecture with pnpm Workspace, Turborepo & Changesets | DEV Community
- Automate changelog generation and publish with Changesets | DEV Community
- Frontend Handbook: Changesets | Infinum
- Introducing Changesets: Simplify Project Versioning | Liran Tal