
이 글은 Changesets로 모노레포 릴리즈를 관리하는 시리즈의 1편이다. 여기서는 Changesets를 왜 도입하는지, 어떤 파일을 남기는지, PR 단계에서 semver 판단을 어떻게 기록하는지에 집중한다.
- 1편: Changesets로 모노레포 버전 관리 시작하기
- 2편: Changesets와 GitHub Actions로 릴리즈 자동화하기
- 3편: Changesets 실무 운영 가이드
처음 fluo의 패키지를 배포할 때는 모든 과정을 수동으로 처리했다. GitHub Actions를 붙인 뒤에는 npm publish를 실행하는 단계 자체는 자동화됐지만, 릴리즈에서 가장 중요한 판단은 여전히 사람의 기억에 남아 있었다. 어떤 패키지를 올릴지, 내부 의존 패키지의 버전도 같이 올려야 하는지, CHANGELOG에는 어떤 문장을 남길지 같은 결정은 자동화되지 않았다.
패키지가 한두 개라면 이 방식도 버틸 수 있다. 하지만 모노레포 안의 패키지가 늘어나기 시작하면 상황이 달라진다. 어느 PR은 @fluojs/core만 수정하고, 어느 PR은 @fluojs/http의 새 API를 추가하고, 또 다른 PR은 platform-fastify의 내부 구현만 바꾼다. 릴리즈 시점에 이 모든 변경을 다시 떠올리며 semver를 고르는 방식은 오래 유지하기 어렵다.
Changesets는 이 문제를 “배포 직전에 기억해 내기”가 아니라 “PR 시점에 변경 의도를 기록하기”로 바꾼다. 코드를 작성한 사람이 이 변경이 어떤 패키지에 영향을 주는지, 그리고 major, minor, patch 중 무엇인지 함께 남긴다. 이후 버전 bump, CHANGELOG 생성, publish 대상 선별은 Changesets가 그 기록을 기준으로 처리한다.
수동 릴리즈가 무너지는 지점
수동 릴리즈에서 가장 먼저 무너지는 부분은 명령 실행이 아니라 판단의 일관성이다. 예를 들어 @fluojs/core의 공개 API가 바뀌었다면 해당 패키지만 올리면 될까? @fluojs/http가 core에 의존하고 있다면 http도 patch로 올려야 할까? 릴리즈 노트에는 구현 변경을 적어야 할까, 사용자에게 보이는 동작 변경만 적어야 할까?
이런 질문들은 모두 릴리즈 품질과 직접 연결된다.
- 어떤 패키지를 publish해야 하는지 놓치면 사용자는 새 코드를 받을 수 없다.
- 내부 의존성 버전이 맞지 않으면 워크스페이스 안에서는 되던 코드가 외부 설치 환경에서 깨질 수 있다.
- CHANGELOG가 “bump version” 같은 문장으로 채워지면 사용자는 업그레이드 여부를 판단할 수 없다.
- 잘못된 버전을 npm에 publish하면 이미 올라간 버전은 수정할 수 없고, 다음 버전에서 정정해야 한다.
자동화가 필요한 지점은 단순히 npm publish 명령을 대신 실행하는 곳이 아니다. 어떤 변경을 어떤 버전으로 내보낼지 결정하고, 그 결정이 리뷰 가능한 형태로 남도록 만드는 과정이 더 중요하다.
Changesets의 핵심 관점
Changesets의 핵심은 .changeset/*.md 파일이다. 기능을 추가하거나 버그를 고칠 때, 코드 변경과 함께 “이 변경은 어떤 패키지의 어떤 수준 bump인가”를 Markdown 파일로 남긴다. 이 파일은 나중에 changeset version 명령의 입력이 되고, 각 패키지의 package.json 버전과 CHANGELOG.md를 갱신하는 데 사용된다.
예를 들어 HTTP 패키지에 새 옵션을 추가하고, 그 과정에서 core 패키지의 내부 메타데이터 처리를 조금 고쳤다면 changeset 파일은 다음처럼 작성할 수 있다.
---
"@fluojs/http": minor
"@fluojs/core": patch
---
Add timeout option to the HTTP client.
Patch `@fluojs/core` to align the internal metadata store with the new HTTP client configuration flow.프론트매터에는 패키지별 bump 수준을 적는다. 본문은 CHANGELOG에 들어갈 설명이므로 커밋 메시지보다 사용자 관점에 가깝게 쓰는 편이 좋다. “refactor internal code”처럼 작성자만 이해할 수 있는 문장보다 “HTTP client에 timeout 옵션을 추가했다”처럼 업그레이드 판단에 도움이 되는 문장이 낫다.
설치와 초기화
Changesets는 별도의 서버나 외부 서비스가 아니라 CLI와 저장소 안의 파일로 동작한다. pnpm 워크스페이스라면 루트에서 다음 명령을 실행한다.
pnpm add -D @changesets/cli
pnpm changeset init초기화가 끝나면 .changeset/ 폴더와 .changeset/config.json 파일이 생긴다. Changesets는 워크스페이스 루트의 패키지 목록을 읽기 때문에, pnpm 기준으로는 pnpm-workspace.yaml의 packages 설정이 올바르게 잡혀 있어야 한다.
설치 직후에는 다음 명령으로 Changesets가 패키지들을 제대로 인식하는지 확인하는 것이 좋다.
pnpm changeset status이 명령에서 패키지 목록이 비어 있거나 예상과 다르다면 Changesets 설정 문제가 아니라 워크스페이스 설정 문제일 가능성이 높다. 이 단계에서 패키지 목록이 올바르게 잡혀야 이후 버전 bump와 내부 의존성 전파도 정상적으로 동작한다.
알아야 할 파일 세 가지
Changesets를 처음 도입할 때 자주 보는 파일은 세 가지다. 각각의 역할을 분리해서 이해하면 이후 자동화 흐름도 훨씬 읽기 쉬워진다.
1. .changeset/{slug}.md
가장 자주 만들고 리뷰하는 파일이다. 파일명은 Changesets가 자동으로 생성하며, 의미를 직접 관리할 필요는 거의 없다. 중요한 것은 프론트매터와 본문이다.
---
"@fluojs/cli": minor
"@fluojs/platform-fastify": patch
---
CLI에 `--watch` 플래그를 추가했다. 기존 명령어는 그대로 동작하며, `--watch`는 선택적으로 사용할 수 있다.
`platform-fastify` 어댑터는 watch 모드에서 서버를 재시작하지 않고 라우트만 다시 등록하도록 수정했다.한 PR에 여러 패키지가 영향을 받는다면 하나의 changeset 파일에 함께 적을 수 있다. 반대로 서로 다른 CHANGELOG 항목으로 남기고 싶은 변경이라면 한 PR 안에 changeset 파일을 여러 개 둬도 된다.
2. .changeset/config.json
Changesets의 동작 방식을 정하는 설정 파일이다. 예를 들어 fluo에서는 다음과 같은 형태를 사용한다.
{
"$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json",
"changelog": "@changesets/cli/changelog",
"commit": false,
"fixed": [],
"linked": [],
"access": "restricted",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": ["@fluojs/playground"]
}처음에는 모든 옵션을 다 외울 필요는 없다. 자주 마주치는 값은 이 정도다.
access: npm 공개 범위다. scoped 패키지를 공개 배포하려면 보통public이 필요하다.baseBranch: changeset 상태를 비교할 기준 브랜치다. 일반적으로main을 둔다.updateInternalDependencies: 내부 의존성 전파 수준이다. 모노레포에서는 보통patch를 둔다.ignore: 배포 대상에서 제외할 패키지 목록이다. playground나 예제 앱처럼 publish하지 않는 패키지를 넣을 수 있다.commit:changeset version실행 시 변경사항을 자동 커밋할지 여부다. GitHub Actions에서 커밋 메시지를 직접 지정할 예정이라면false로 두는 경우가 많다.
3. .changeset/pre.json
이 파일은 평소에는 없다. pre-release 모드에 들어갔을 때만 생긴다. 예를 들어 beta 릴리즈를 시작하면 다음과 같은 파일이 만들어진다.
{
"mode": "pre",
"tag": "beta",
"initialVersions": {
"@fluojs/core": "0.9.0",
"@fluojs/http": "0.4.2"
},
"changesets": []
}pre.json이 있으면 일반 버전 대신 1.0.0-beta.1, 1.0.0-beta.2 같은 버전이 생성된다. pre-release는 운영 주제에 가까우므로 3편에서 더 자세히 다룬다. 1편에서는 “이 파일이 있으면 일반 릴리즈가 아니라 pre-release 모드다” 정도만 기억해도 충분하다.
changeset 파일 만들기
일상적인 개발 흐름에서 직접 실행하는 명령은 보통 하나다.
pnpm changesetCLI는 변경된 패키지를 선택하고, bump 수준을 고르고, CHANGELOG에 들어갈 요약을 입력하는 흐름으로 진행된다.
Which packages would you like to include?
Which packages should have a major bump?
Which packages should have a minor bump?
Please enter a summary for this change.명령이 끝나면 .changeset/ 아래에 Markdown 파일이 만들어진다. 이 파일을 코드 변경과 함께 커밋하면 된다.
git add .changeset/curvy-buses-lie.md
git commit -m "feat(http): add timeout option to HTTP client"changeset 파일은 코드와 같은 PR에서 리뷰되는 것이 좋다. 코드만 보고 “기능이 추가됐다”고 판단하는 것과, changeset까지 같이 보며 “이 패키지는 minor로 올리는 게 맞다”고 확인하는 것은 다르다. Changesets를 쓰면 semver 판단이 PR 리뷰의 일부가 된다.
Semver 고르는 기준
semver 기준은 팀마다 조금씩 다르게 운영할 수 있지만, 처음에는 단순하게 잡는 편이 좋다. 기준이 복잡하면 changeset 작성 자체가 부담이 되고, 결국 파일을 빠뜨리기 쉽다.
- 기존 함수 시그니처에서 파라미터를 제거하거나 타입을 바꾸면
major다. - 기존 함수에 optional 파라미터를 추가하면 보통
minor다. - 새 API나 새 export를 추가하면
minor다. - 외부 동작이 같은 내부 구현 변경은
patch다. - 오탈자, 주석, 문서성 수정은 대체로
patch다.
예를 들어 다음 변경은 minor에 가깝다. 기존 사용자는 아무것도 바꾸지 않아도 되고, 새 사용자는 선택적으로 timeout을 사용할 수 있기 때문이다.
const client = new HttpClient({ timeout: 5000 });반대로 기존 생성자 인자의 이름을 바꾸거나 필수 옵션을 추가했다면 major를 검토해야 한다.
- new HttpClient({ baseUrl: "https://api.example.com" })
+ new HttpClient({ origin: "https://api.example.com" })0.x 단계에서는 breaking change를 보통 minor로 처리하는 팀도 많다. 1.0.0부터 semver 계약을 본격적으로 지키겠다는 의미가 강하기 때문이다. 중요한 것은 이 기준을 문서화하고 PR마다 드러나게 만드는 것이다. Changesets는 그 판단을 파일로 남기게 해 준다.
좋은 changeset 본문 쓰기
changeset 본문은 그대로 CHANGELOG에 들어간다. 그래서 커밋 메시지처럼 내부 작업 중심으로 쓰기보다, 패키지 사용자가 읽을 릴리즈 노트라고 생각하고 작성하는 편이 좋다.
아쉬운 예시는 다음과 같다.
---
"@fluojs/http": patch
---
Fix bug.이 문장은 변경이 있었다는 사실만 말할 뿐, 사용자가 업그레이드해야 하는 이유를 알려주지 않는다. 같은 patch라도 다음처럼 쓰면 훨씬 낫다.
---
"@fluojs/http": patch
---
Fix route matching when a controller-level prefix and method-level path both include leading slashes.
Previously, `@Controller("/api")` and `@Get("/users")` could produce a duplicated slash in the final route. The router now normalizes both segments before registration.좋은 changeset 본문에는 보통 세 가지가 들어간다.
- 무엇이 바뀌었는지
- 사용자가 어떤 상황에서 영향을 받는지
- 가능하다면 이전 동작과 새 동작의 차이가 무엇인지
이 정도만 지켜도 CHANGELOG의 품질이 크게 올라간다. 결국 사용자는 CHANGELOG를 보고 업그레이드 여부를 판단한다.
정리
Changesets의 출발점은 “릴리즈 자동화 도구”라기보다 “변경 의도를 PR에 남기는 방식”에 가깝다. 코드 작성자가 변경된 패키지와 semver 수준을 선언하고, 리뷰어는 그 판단을 코드와 함께 검토한다. 이 작은 파일 하나가 나중에 버전 bump, CHANGELOG 생성, publish 대상 선별의 입력이 된다.
다음 글에서는 이 changeset 파일들이 main 브랜치에 쌓였을 때 GitHub Actions와 changesets/action이 어떻게 Version Packages PR을 만들고, 그 PR이 머지된 뒤 어떻게 실제 publish로 이어지는지 살펴본다.
더 읽어보기
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와 GitHub Actions로 릴리즈 자동화하기
이 글은 Changesets로 모노레포 릴리즈를 관리하는 시리즈의 2편이다. 1편에서 changeset 파일로 변경 의도를 기록했다면, 이번 글에서는 그 파일을 기준으로 GitHub Actions가 Version Packages PR을 만들고 publish까지 이어지는 흐름을 다룬다.…
2026.04.29
필요한 사람들의 세상 - omocon 후기
이전 잡생각인 <너만의 월남쌈을 싸> 와 맥락적으로 이어지는 부분이 있지만, 꼭 가서 볼 필요는 없습니다. 나는 10년이라는 시간 동안 장편과 단편 소설을 썼다. 하지만 5년 전 어느 순간부터 더 이상 써야 할 이야기가 남지 않았다는 느낌을 받았고, 그 이후로는 단 한 번도 완성된 형태…
댓글
댓글을 불러오는 중...