
이 글은 Changesets로 모노레포 릴리즈를 관리하는 시리즈의 2편이다. 1편에서 changeset 파일로 변경 의도를 기록했다면, 이번 글에서는 그 파일을 기준으로 GitHub Actions가 Version Packages PR을 만들고 publish까지 이어지는 흐름을 다룬다.
- 1편: Changesets로 모노레포 버전 관리 시작하기
- 2편: Changesets와 GitHub Actions로 릴리즈 자동화하기
- 3편: Changesets 실무 운영 가이드
Changesets를 로컬에서만 쓰면 “버전 bump와 CHANGELOG 생성을 도와주는 CLI” 정도로 보일 수 있다. 하지만 실제로 유용해지는 지점은 CI와 연결했을 때다. main 브랜치에 changeset 파일이 들어오면 봇이 자동으로 Version Packages PR을 만들고, 그 PR이 머지되면 publish를 실행한다.
이 구조의 장점은 릴리즈를 하나의 리뷰 가능한 PR로 만든다는 점이다. 개발 PR에서는 코드와 changeset 파일을 리뷰하고, Version Packages PR에서는 실제로 어떤 패키지 버전이 올라가는지와 CHANGELOG가 어떻게 생성되는지를 확인한다. 릴리즈 판단이 숨겨진 명령 한 번에 묻히지 않고, 저장소의 일반적인 리뷰 흐름 안으로 들어온다.
전체 릴리즈 흐름
Changesets와 GitHub Actions를 함께 쓰면 릴리즈 흐름은 보통 다음처럼 나뉜다.
1. 기능 개발
└─ pnpm changeset 실행
└─ .changeset/some-slug.md 생성
2. PR 오픈 후 main 머지
3. GitHub Actions 실행
└─ changesets/action이 changeset 파일 감지
└─ "Version Packages" PR 생성 또는 업데이트
├─ package.json 버전 bump
└─ CHANGELOG.md 갱신
4. "Version Packages" PR 머지
5. GitHub Actions 다시 실행
└─ npm publish
├─ 변경된 패키지만 publish
├─ GitHub Release 생성
└─ git 태그 생성개발자가 직접 하는 일은 보통 두 가지다. 첫째, 기능 PR에 changeset 파일을 포함한다. 둘째, 릴리즈할 준비가 됐을 때 Version Packages PR을 확인하고 머지한다. 나머지는 CI가 처리한다.
이 흐름에서 중요한 점은 Version Packages PR이 릴리즈의 미리보기 역할을 한다는 것이다. 이 PR을 보면 어떤 패키지가 0.5.0에서 0.6.0으로 올라가는지, 어떤 패키지는 내부 의존성 때문에 patch로 따라 올라가는지, CHANGELOG에는 어떤 문장이 들어가는지 확인할 수 있다.
package.json 스크립트 준비
GitHub Actions에서 사용할 명령을 먼저 package.json에 정의해 둔다. fluo에서는 보통 다음처럼 나눈다.
{
"scripts": {
"version-packages": "changeset version",
"release": "pnpm build && changeset publish"
}
}두 명령의 책임은 분리되어 있다.
changeset version: changeset 파일을 읽어package.json버전을 올리고CHANGELOG.md를 갱신한다.changeset publish: 아직 npm에 올라가지 않은 버전을 publish한다.
release 스크립트 안에 pnpm build를 넣는 이유는 publish 직전에 실제 배포 산출물을 검증하기 위해서다. 패키지 구조에 따라 pnpm test, pnpm typecheck, 자체 검증 스크립트를 앞에 추가해도 된다.
GitHub Actions 워크플로우
changesets/action을 사용하면 Version Packages PR 생성과 publish 분기를 하나의 step으로 처리할 수 있다.
name: Release
on:
push:
branches:
- main
jobs:
release:
name: Release
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
id-token: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: pnpm
registry-url: https://registry.npmjs.org
- run: pnpm install --frozen-lockfile
- name: Create Release Pull Request or Publish
uses: changesets/action@v1
with:
publish: pnpm release
version: pnpm version-packages
title: "chore: version packages"
commit: "chore: version packages"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}이 workflow는 main에 push될 때마다 실행된다. main에 처리되지 않은 .changeset/*.md 파일이 있으면 Version Packages PR을 만들거나 기존 PR을 업데이트한다. 반대로 Version Packages PR이 머지되어 changeset 파일이 사라지고 package version이 올라간 상태라면 publish 명령을 실행한다.
changesets/action이 나누는 두 상황
changesets/action은 내부적으로 “아직 처리되지 않은 changeset 파일이 있는가?”를 기준으로 움직인다.
첫 번째 상황은 changeset 파일이 남아 있는 경우다. 이때 action은 version 명령을 실행해 버전 bump와 CHANGELOG 갱신 결과를 만들고, 그 결과를 담은 PR을 연다. 이 PR이 흔히 “Version Packages PR”이라고 부르는 PR이다.
두 번째 상황은 Version Packages PR이 머지된 경우다. 이 시점에는 changeset 파일이 이미 소비되었고, package version과 CHANGELOG 변경이 main에 들어와 있다. action은 publish할 패키지가 있는지 확인하고, publish 명령을 실행한다.
흐름을 조금 더 작게 쪼개면 다음과 같다.
main에 changeset 파일 있음
→ changeset version 실행
→ package.json / CHANGELOG.md 변경
→ Version Packages PR 생성 또는 업데이트
Version Packages PR 머지됨
→ main에 새 package version 반영
→ changeset publish 실행
→ npm publish / git tag / GitHub Release 생성이 구조 덕분에 릴리즈 PR을 사람이 한 번 더 확인할 수 있다. 자동화가 모든 판단을 숨기는 것이 아니라, 사람이 확인해야 할 지점을 PR로 만들어 준다.
권한과 토큰 설정
GitHub Actions에서 릴리즈를 수행하려면 workflow 권한을 명시해야 한다.
permissions:
contents: write
pull-requests: write
id-token: write각 권한은 다음 역할을 한다.
contents: write: git 태그와 GitHub Release 생성에 필요하다.pull-requests: write: Version Packages PR 생성과 업데이트에 필요하다.id-token: write: npm provenance를 사용할 때 필요하다.
환경 변수도 중요하다.
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}GITHUB_TOKEN은 GitHub가 자동으로 제공하는 토큰이고, NODE_AUTH_TOKEN에는 npm publish 권한이 있는 토큰을 넣는다. scoped 패키지를 공개 배포한다면 .changeset/config.json의 access 설정과 npm 권한도 함께 맞아야 한다.
fetch-depth: 0을 두는 이유
릴리즈 workflow에서 actions/checkout에 fetch-depth: 0을 두는 편이 좋다.
- uses: actions/checkout@v4
with:
fetch-depth: 0기본 checkout은 얕은 히스토리만 가져온다. 대부분의 단순 빌드에서는 문제가 없지만, Changesets가 태그를 만들거나 이전 릴리즈와 비교해야 하는 상황에서는 전체 git 히스토리가 필요할 수 있다. 릴리즈 workflow는 일반 CI보다 git 히스토리에 더 민감하므로 처음부터 전체 히스토리를 가져오게 두는 것이 안전하다.
Version Packages PR에서 확인할 것
Version Packages PR은 그냥 “봇이 만든 버전 변경 PR”이 아니다. 이 PR은 릴리즈 직전의 검토 지점이다. 머지하기 전에 최소한 다음을 확인하는 것이 좋다.
- 예상한 패키지만 버전이 올라갔는가?
major,minor,patch수준이 의도와 맞는가?- 내부 의존성 때문에 따라 올라간 패키지가 있는가?
CHANGELOG.md문장이 사용자 관점에서 읽을 만한가?- publish하면 안 되는 패키지가 포함되지 않았는가?
예를 들어 @fluojs/core에 minor changeset을 추가했는데 @fluojs/http와 @fluojs/platform-fastify도 patch로 올라간다면, 이것은 오류가 아니라 내부 의존성 전파 때문일 수 있다. 이런 변화는 3편에서 더 자세히 다룬다.
Version Packages PR이 계속 업데이트되는 이유
Version Packages PR이 이미 열려 있는 상태에서 다른 PR이 main에 머지되고, 그 PR에도 changeset 파일이 포함되어 있다면 봇은 기존 Version Packages PR을 업데이트한다. 새 릴리즈 PR을 매번 새로 여는 것이 아니라, 아직 publish되지 않은 changeset 파일 집합을 기준으로 하나의 PR을 유지하는 방식이다.
이 동작은 실무에서 꽤 편하다. 릴리즈를 당장 하지 않더라도 Version Packages PR을 열어둔 채로 다음 변경을 계속 쌓을 수 있다. 그러면 릴리즈할 시점에 해당 PR 하나만 보면 그동안 쌓인 변경사항을 한 번에 확인할 수 있다.
다만 “patch만 먼저 릴리즈하고 minor는 나중에 하고 싶다”처럼 릴리즈 대상을 나누고 싶다면 main에 들어가는 changeset 파일의 순서를 조절해야 한다. Changesets의 릴리즈 단위는 기본적으로 main에 남아 있는 changeset 파일 집합이기 때문이다.
로컬에서 흐름을 확인해 보기
CI에 올리기 전에 로컬에서 Version Packages PR이 어떤 변경을 만들지 확인하고 싶다면 changeset version을 직접 실행해 볼 수 있다.
pnpm version-packages이 명령은 실제 publish를 하지 않는다. 대신 package version과 CHANGELOG를 갱신한다. 결과가 예상과 다르면 changeset 파일의 프론트매터나 본문을 수정하고 다시 확인하면 된다.
확인이 끝난 뒤 로컬 변경을 유지할지 되돌릴지는 상황에 따라 다르다. GitHub Actions를 기준으로 운영한다면 로컬에서 만든 version 변경은 커밋하지 않고, 봇이 Version Packages PR에서 만들게 두는 편이 단순하다.
정리
Changesets와 GitHub Actions를 연결하면 릴리즈는 두 단계로 나뉜다. 먼저 changeset 파일을 기준으로 Version Packages PR을 만들고, 그 PR을 사람이 확인해 머지하면 publish가 실행된다. 이 구조는 릴리즈를 완전히 숨겨진 자동화로 만들지 않고, 리뷰 가능한 PR로 유지한다.
다음 글에서는 이 자동화 흐름을 실제 모노레포에서 운영할 때 자주 마주치는 문제를 다룬다. 내부 의존성 전파가 왜 일어나는지, CHANGELOG가 어떤 기준으로 만들어지는지, pre-release 모드는 어떻게 관리하는지, 그리고 patch와 minor changeset이 섞였을 때 릴리즈를 어떻게 나눌 수 있는지 살펴본다.
더 읽어보기
2026.04.17
Code Server
처음 코드 서버를 만들었던 건 아마도 3년쯤 전의 일이다. 맥미니만 있던 탓에 밖에서 개발하는 게 쉽지 않았고, 아이패드로 언제 어디서든 개발을 하고 싶었던 끝에 찾아낸 해결책이었다. 다행히 집에는 Synology NAS가 있었고, Docker를 통해 어렵지 않게 코드 서버를 만들 수…
2026.02.02
Git Worktree
Git으로 여러 작업을 병렬로 진행하다 보면 브랜치 전환 자체가 비용이 되는 순간이 잦다. 기능 개발 중에 긴급 핫픽스를 처리해야 하거나, PR 리뷰를 위해 다른 브랜치를 실행해 봐야 하는 상황에서 git checkout과 stash를 반복하는 방식은 작업 흐름을 지속적으로 끊는다. 이…
2025.11.05
Steiger
FSD(Feature-Sliced Design)는 프론트엔드 아키텍처를 장기적으로 유지하기에 매우 유용한 방법론이다. 레이어와 슬라이스를 명확히 구분하고 의존 방향을 제한함으로써, 프로젝트 규모가 커질수록 코드베이스의 복잡도를 구조적으로 통제할 수 있다. 다만 이 장점은 동시에 부담이…
2025.08.16
pnpm과 terborepo로 시작하는 모노레포
최근 개발 현장에서는 여러 프로젝트를 하나의 저장소에서 관리하는 모노레포 구조가 빠르게 확산되고 있다. 모노레포는 여러 패키지를 한 곳에서 관리하기 때문에 공통 코드 재사용이 쉽고, 여러 팀이 동시에 작업하더라도 의존성과 변경 이력을 일관되게 관리할 수 있다는 장점이 있다. 반면, 패키…
2026.05.07
Decorator와 Metadata — fluo가 reflect-metadata를 버린 이유
들어가며 NestJS를 써본 적이 있다면 이런 설정이 익숙할 것이다. // tsconfig.json { "compilerOptions": { "experimentalDecorators": true, "emitDecoratorMetadata": true } } 그리고 엔트리 파일 어딘가에…
2026.05.05
Changesets 실무 운영 가이드: 내부 의존성, prerelease, 운영 Q&A
이 글은 Changesets로 모노레포 릴리즈를 관리하는 시리즈의 3편이다. 1편에서는 changeset 파일을 작성하는 방법을, 2편에서는 GitHub Actions로 Version Packages PR과 publish를 자동화하는 흐름을 다뤘다. 이번 글에서는 실제 운영 중 자주 헷…
2026.05.05
Changesets로 모노레포 버전 관리 시작하기
이 글은 Changesets로 모노레포 릴리즈를 관리하는 시리즈의 1편이다. 여기서는 Changesets를 왜 도입하는지, 어떤 파일을 남기는지, PR 단계에서 semver 판단을 어떻게 기록하는지에 집중한다. 1편: Changesets로 모노레포 버전 관리 시작하기 2편: Change…
2026.04.29
필요한 사람들의 세상 - omocon 후기
이전 잡생각인 <너만의 월남쌈을 싸> 와 맥락적으로 이어지는 부분이 있지만, 꼭 가서 볼 필요는 없습니다. 나는 10년이라는 시간 동안 장편과 단편 소설을 썼다. 하지만 5년 전 어느 순간부터 더 이상 써야 할 이야기가 남지 않았다는 느낌을 받았고, 그 이후로는 단 한 번도 완성된 형태…
댓글
댓글을 불러오는 중...