안녕하세요. 똑똑한개발자의 프론트엔드 개발자 시스템 팀 박성혜입니다.
지난 5월, 똑똑한개발자 프론트엔드 시스템팀은 이렇게 일해요. 글에서 패키지, 템플릿 관리를 위해 단일 레포지토리에서 모노레포로 전환하게 되었다는 소식을 전해드렸는데요.
오늘은 활발하게 관리하고 있는 모노레포 환경에서 패키지 버전 관리를 효율적으로 할 수 있게 도와준 Changesets를 소개해 드리겠습니다. (기본적인 배포 플로우 설명과 유용한 Tip들은 덤!)
모노레포란 여러 패키지, 애플리케이션, 또는 모듈을 하나의 대규모 저장소에서 관리할 수 있도록 구성한 환경을 말합니다. 똑똑한개발자의 모노레포에는 npm에 배포된 26개의 패키지와 보일러플레이트, 그리고 각 모듈를 위한 문서 앱 등이 포함되어 있습니다.
이렇게 많은 패키지를 각기 버전 관리하고 배포하는 것은 굉장히 번거로운 작업입니다. 일일이 수동으로 처리해야 할까요? 한두 개 정도는 금방 하겠지만 무려 26개나 되는걸요..😅

사실 이 부분에 있어서 저희는 처음부터 자동화 작업을 고려하고 있었습니다. 이미 모노레포를 사용하기 이전부터 Semantic-release라는 버전관리 툴을 사용하고 있었어요.
여기서 말하는 배포 자동화를 시킨다는 게 어디서부터 어디까지 일까요?
자세히 설명드리기 위해서 하나의 패키지가 배포되는 과정을 설명드릴게요.
패키지 수정부터 배포까지
1. 패키지 수정
@toktokhan-dev/cli-plugin-gen-route-pages라는 패키지가 있습니다. 해당 패키지는 @toktokhan-dev/cli 에 등록할 수 있는 플러그인으로, nextjs 프레임워크의 page 라우터 버전에서 사용할 수 있어요. 이 패키지를 사용하면 pages 폴더를 조회하여 route 경로를 포함한 객체를 생성해 줍니다. 타이핑으로 경로를 가져오는 것이 아닌, 객체의 key 값을 가져옴으로써 편의성과 안정성을 제공하고 있는 패키지입니다.
아래의 경로를
pages
├── index.tsx
├── login
│ └── index.tsx
└── post
└── [id].tsx
이렇게 객체 상수로 변환을 해줍니다.
export const ROUTES = {
MAIN: "/",
LOGIN_MAIN: "/login",
SOCIAL_CALLBACK: "/social/callback",
POST: {
dynamic_id: {
MAIN: "/post/[id]",
}
}
}
};
그런데 위의 변환된 객체 상수에서 이상한 점이 보이시나요? dynamic id에 해당하는 경로의 key값이 소문자로 변환이 되었습니다. 이는 일관되지 않은 변환 결과로 이슈로 판단이 되네요. 대문자로 수정을 해줍니다.

issue fix-example image
2. 버전 업데이트
이제 수정한 내용을 반영하여 패키지 버전을 업데이트할 차례입니다. Node.js 생태계에서는 npm을 통해 패키지를 관리하며, 이는 Semantic Versioning을 따릅니다. 따라서, 이번 수정 사항에 맞게 적절한 버전으로 업데이트를 진행해야 합니다.
이번 수정은 간단한 버그 수정이며, 새로운 기능을 추가한 것은 아니기 때문에 PATCH 버전에 해당합니다. 현재 해당 패키지의 버전이 v0.0.10이라면, v0.0.11로 업데이트하는 것이 적절합니다.
똑똑한개발자의 패키지들은 아직 초기 단계라 0.x.x 버전을 따르고 있습니다.

package.json version update
3. Changelog 작성과 Release 및 Tag 등록
이후에는 어떤 변경사항이 발생했는지, Release Note와 ChangeLog를 를 적어주면 되는데요.
수동 작업 기준으로 설명을 드리면, Release를 만들기 위해서는 tag등록도 필요하고, Changelog를 만들기 위해서는 하나의 CHANGELOG.md에 버전과 변경 내용을 써줘야 합니다. 아래 이미지처럼요.


(좌) Release (우) Changelog
4. 배포
모든 준비가 완료되었으니, 비로소 배포를 할 수 있게 되었습니다. npm publish 명령어를 사용하면 package.json버전을 읽어 배포가 됩니다.
자동화의 필요성: Changesets
지금까지 하나의 패키지를 배포하는 과정을 설명드렸습니다. Changelog나 Release 같은 작업이 배포 과정에서 꼭 필요한 요소는 아닙니다. 하지만 누구나 사용할 수 있는 공개된 패키지라면, 사용자에게 버전 업데이트 내용을 문서로 전달하는 작업은 필수적이라고 생각합니다. 게다가 로그가 쌓이면 개발자 입장에서 관리도 훨씬 편리해지죠.
만약 이 과정이 자동화될 수 있다면 얼마나 좋을까요? 이제 Changesets를 소개할 때가 된 것 같네요😉
Changesets 소개
Changesets는 프로젝트에서 버전 관리와 변경 로그 생성을 도와주는 도구입니다. 앞서 설명드린 배포 과정의 2~4단계를 자동으로 처리해 줍니다..🫶 물론, Changesets의 장점은 이 뿐만이 아닙니다. 다른 장점들은 이전에 사용했던 Semantic-release와 비교해 가며 설명드릴게요.
- semantic-release는 커밋 메시지 기반으로 버전을 자동으로 결정하지만, Changesets은 개발자가 직접 변경 사항에 대한 설명과 버전 업데이트의 종류(major, minor, patch)를 결정할 수 있습니다.
- semantic-release는 별도의 플러그인을 통해 모노레포를 지원하지만, Changesets은 기본적으로 모노레포를 지원합니다.
- semantic-release는 CI 환경에서 main브랜치에 병합될 때마다 자동으로 버전을 업데이트하지만 Changesets은./changesets폴더의 임시 md파일을 읽어 해당 md파일이 포함된 커밋이 병합이 되면, [Version Packages]라는 PR생성을 통해 배포여부를 결정할 수 있습니다.
note. 이 기능은 Changesets GitHub App & Changesets GitHub Action의 조합으로 사용할 수 있습니다.
직접 느낀 Changesets 장점
저희가 Semantic-release에서 Changesets로 변경하게 된 이유이자 사용하면서 극명하게 느꼈던 Changesets의 장점을 다시 정리해볼게요.
- 개발자가 직접 변경 사항 설명과 버전 업데이트 종류(major, minor, patch)를 선택할 수 있습니다.
- 커밋 기반으로 Changelog 기록이 가능합니다.
- 커밋 기반으로 쌓인 로그는 하나의 PR에 쌓이며, 개발자가 PR를 머지하기 전까지는 배포가 되지 않습니다.
단점이라면, 공식 문서가 너무 간단하다는 점입니다. 문서도 md 파일로 되어 있어요. 그렇다고 구현하기에 부족한 문서는 아닙니다. 커뮤니티도 꽤 활성화되어 있고요.
또 Semantic-release가 제공하는 Slack action bot처럼 편리한 기능은 Changesets에서는 따로 구현해야 한다는 점은 조금 아쉽기는 한 부분이네요.
Changesets를 활용한 배포 과정
과정을 설명드리기 전에, 저희는 패키지 매니저 도구로 pnpm을 사용하며, Changesets Github App과 Changesets Github Action을 함께 사용했음을 알려드립니다. (말씀드린 장점들은 이 두 가지를 통합하여 사용해야 제대로 체감하실 수 있을 거예요.)
준비. Changesets 설치 및 config 설정
Changesets 공식문서 를 통해 해당 필요한 패키지를 설치해주신 후 changeset init 명령어를 입력하시면 기본적으로 필요한 설정 파일이 생성됩니다.
그런 다음 .changesets/config.json 경로에서 프로젝트 성격에 맞게 설정을 해주세요.
{
"$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json",
"changelog": [
"@changesets/changelog-github",
{ "repo": "TOKTOKHAN-DEV/toktokhan-dev" }
],
"commit": false,
"fixed": [],
"linked": [],
"access": "public",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": ["Toktoken", "@toktokhan-dev/docs"]
}
저희는 @changesets/cli 와 @changesets/changelog-github를 사용하고 있습니다.
기본 설정 외 자세한 설명은 공식문서를 참고해주세요.
1. 변경사항 문서 작성
터미널에서 pnpm changeset 명령어를 입력하면, Changesets가 지정된 패키지 스코프 안에서 변경사항을 감지하고 대화형 프롬프트를 실행합니다. 변경사항이 있는 패키지가 우선으로 뜨기 때문에 선택도 편하고, 다음 프롬프트로 넘어가게 되면 버전 종류(major, minor, patch)도 지정할 수 있으며, 간략하게 description을 작성할 수도 있습니다.


pnpm changeset 프롬포트: 패키지 선택
2. 변경사항 커밋
summary로 간단한 설명을 넣어준 후 프롬포트를 종료시키면 .changeset 경로에 임의의 md 파일이 생성됩니다. 이 md 파일을 파일 변경사항 내용에 맞게 좀 더 자세하게 작성을 해주세요.
이후 변경 된 파일과 함께 커밋해주시면 됩니다.

생성된 md
틈새 어필: 이 때 커밋 메시지는 변경 사항을 잘 설명할 수 있도록 작성하는 것이 좋은데요.저희는 사내 CLI 플러그인을 통해 이 md 파일의 내용을 읽어, 커밋 시 자동으로 메시지 입력을 제안하는 기능을 개발했습니다. (패키지 보러 가기)

@toktokhan-dev/cli-plugin-commit
3. Pull Request 생성
변경사항을 커밋한 후, 변경 사항을 병합하기 위해 Pull Request를 생성합니다. 이 PR에서 Changesets이 생성한 문서가 자동으로 포함되어 배포될 준비가 됩니다.

Version Packages PR
4. PR 머지 및 자동 배포
PR을 머지하면 Changesets는 main 브랜치에 병합된 문서들을 바탕으로 자동으로 버전을 업데이트하고, Changelog 및 Release Note를 생성해 줍니다. 이때 1번의 과정에서 생성하고 커밋한 md파일은 삭제됩니다. (해당 md파일은 단순 문서 작성 용도로만 쓰인 임시 파일이기 때문이에요.)

PR merge
5. 배포
이후에는 새롭게 업데이트된 패키지가 NPM에 배포되게 됩니다.

Publish npm Package
Tips
여기까지 기본적인 패키지 배포 플로우와 Changesets를 활용해 버전관리, 및 자동배포 과정을 설명드렸습니다. 이제 마지막으로, 소소하지만 유용한 몇 가지 팁을 공유해드릴게요.
Tip 1 : 기여자에게 감사 태그 및 관련 PR 링크를 자동으로 추가할 수 있어요.
@changesets/changelog-github 모듈을 사용하면, CHANGELOG.md와 Release note에 기여자에게 감사태그와, 관련 PR링크까지 걸어줍니다. 다만 @changesets/cli를 설치하면 제공되는 @changesets/chagelog-git모듈과는 역할이 다르니 별도 패키지 설치가 필요합니다. (Docs-changesets/changelog )


@changesets/changelog-github 모듈을 사용한 자동 생성 PR
// ./changeset/config.json
{
...
"changelog": [
"@changesets/changelog-github",
{ "repo": "TOKTOKHAN-DEV/toktokhan-dev" }
],
}
Tip #2: npm에 배포하고 싶지는 않은데 버전관리는 하고 싶어요.
Changesets의 tag 명령어를 사용하면 npm에 패키지를 게시하지 않고도 프로젝트 버전 관리를 할 수 있습니다. 저희 사내 프런트엔드 템플릿은 Git 태그를 통해서만 버전 관리를 하고 있으며, 이를 활용해 각 버전별로 템플릿을 관리하고 다운로드할 수 있는 Tokit도 개발했습니다.
틈새 어필: Tokit 은 똑똑한개발자에서 관리하는 프런트엔드 보일러 템플릿을 관리하고, 로컬 환경에 설치하기 위한 CLI tool입니다. create-react-app처럼 커멘드라인 한 줄로 쉽게 설치가 가능하며 원격 레포지토리에 생성하고, 연동시킬 수 있는 옵션이 있습니다. [Tokit 가이드 보러 가기]


tokit
// .github/workflows/release.yml
name: Release
...
jobs:
release:
...
steps:
...
- name: Create Release Pull Request
id: changesets
uses: changesets/action@v1
with:
publish: pnpm changeset tag
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
... ...
Tip #3: 배포가 되면 슬랙 방에 알림을 보내고 싶어요.
Changesets는 슬랙 액션 기능을 제공하지 않습니다🥲 하지만 Git Action Context를 통해 배포 성공 여부, pr number, 패키지 정보를 제공해 줍니다. 이를 활용하여 슬랙 알림도 자동화했습니다. (Docs-Changesets actions-outputs)
slack 알림 템플릿은 webhook을 통해 구현하였습니다.

똑똑한개발자 슬랙 배포알림 이미지// .github/workflows/release.yml
name: Release
...
jobs:
release:
...
steps:
...
- name: Slack notification
if: steps.changesets.outputs.published == 'true'
run: pnpm slack ${{ steps.pr.outputs.pull_request_number }} '${{steps.changesets.outputs.publishedPackages}}'
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
continue-on-error: true
... ...
마무리
어떠한 일을 진행하는 데에 있어서 자동화는 생산성과 정확성을 위해 중요한 부분이라고 생각합니다. 그만큼 좋은 도구들이 너무 많아, 자동화 이전의 개발자들분들은 얼마나 반복적이고 힘든 작업을 했을지 새삼 대단하게 느껴집니다.
자동화를 통해 많은 작업이 편리해지지만, 때때로 예기치 못한 상황에서는 수동적인 해결이 필요할 때가 있습니다. 이럴 때 근본적인 이해가 뒷받침되어야만 유연하게 대처할 수 있습니다.
저 또한 앞으로도 유용한 도구들을 효과적으로 활용하되, 기본 원칙에 충실하며 안정적이고 효율적인 개발 환경을 구축해 나가야겠다고 생각합니다.
긴 글 읽어주셔서 감사합니다. 이후 늦지 않게 또 다른 글로 찾아올게요.
References
🍀 당신의 비전을 성공적인 제품과 비즈니스로 완성시킵니다. 사랑받는 IT 비즈니스를 향한 첫 스텝, 똑똑한개발자입니다.
[똑똑한개발자 홈페이지 바로가기]