현재 속한 조직에서 저는 private npm registry를 도입하자는 의견을 제시하게 되었습니다.
기존에는 하나의 repository 내부에서 사용하는 package를 관리했었는데요, 그렇게 되면 다음과 같은 문제들이 있었습니다.
이 글에서는...
- turborepo와 changeset을 이용해서 private npm registry 환경을 구축합니다.
package manager로는 pnpm을 사용했습니다.
pnpm dlx create-turbo@latest
프로젝트가 생성되면 JSON을 다음과 같이 설정합니다.
{
"name": "[패키지명]", // 해당 패키지명에 따라 하위 패키지가 @[패키지명]/** 으로 설정됩니다.
"version": "0.0.0"
"private": true, // root repository는 배포 대상이 아니기에 private을 true로 설정합니다.
"workspaces": [
"packages/*"
],
"scripts": {
...
"changeset": "changeset",
"changeset:publish": "pnpm prepack && changeset publish",
"changeset:version": "changeset version && pnpm i --lockfile-only",
...
"publish-packages": "turbo run build && changeset version && changeset publish",
"version-packages": "changeset version",
"release": "turbo build && changeset publish",
"prepack": "turbo run prepack",
},
"dependencies": {
"@[패키지명]/a": "workspace:^",
"@[패키지명]/b": "workspace:^",
"@[패키지명]/c": "workspace:^"
},
"devDependencies": {
"@changesets/changelog-github": "^0.5.0",
"@changesets/cli": "^2.27.11",
"husky": "^9.1.7",
"prettier": "^3.5.0",
"turbo": "^2.4.1",
"typescript": "5.7.3"
}
}
위와 같이 package.json에서 기본적인 설정을 마치고, 하위 패키지에서도 세팅을 진행합니다.
turborepo에서 기본으로 세팅해주는 typescript-config을 기준으로 세팅해보겠습니다.
{
"name": "@testtest/typescript-config",
"version": "0.0.0",
"private": false, // private은 꼭 false 또는 해당 라인을 삭제해주세요. private npm registry 배포와 무관하게 해당 패키지의 배포 가능 여부를 따지는 필드입니다.
"license": "MIT",
"publishConfig": {
"access": "public"
}
}
pnpm i
를 통해 명시된 dependencies를 설치합니다.
혹시나 turborepo에서 default로 생성하는 어플리케이션에 dependency가 걸려있어 install이 안 되고 있는지 확인해주세요. 만약 그런 경우, 사용하지 않는 어플리케이션이나 패키지를 삭제하고(ex) docs, web, ui ...) 필요한 패키지만을 명시해주세요.
다음 명령어를 실행합니다.
pnpm changeset init
성공적으로 initialize가 되면 프로젝트의 root에 .changeset 디렉터리가 생깁니다.
changeset setup은 매우 간단해서, 기본적으로는 이게 끝입니다.
추가적으로 changeset에 세팅할 다를 내용들이 있다면, config.json에서 수정할 수 있습니다.
이제 changeset을 성공적으로 publish할 수 있습니다.
pnpm changeset // git add와 유사합니다. 어떤 패키지를 업데이트 대상에 포함할지 정합니다.
pnpm changeset:version // git commit과 유사합니다.
pnpm changeset:publish // git push와 유사합니다. 실제로 패키지가 publish됩니다.
작업사항이 생길 떄마다 다음 세 가지 커맨드로 changeset을 사용할 수 있습니다.
여기서부터가 중요한데요, private npm registry에 배포하려면 registry url을 가리키는 코드가 필요합니다.
프로젝트의 root에 .npmrc 파일을 생성한 후 다음과 같이 세팅합니다.
@[패키지명]:registry=[registry server url]
//[https를 제외한 registry server url]/:_authToken=${NPM_TOKEN}
그런 다음, packages 하위의 각 package에 있는 package.json에서 registry url을
바라보게 하는 셋업 코드 두 가지를 추가합니다.
...
"publishConfig": {
"registry": "[registry server url]"
},
"repository": {
"type": "git",
"url": "[해당 repository의 git 주소]"
},
...
publish를 진행할 때 package를 순회하면서 스크립트를 실행하기 때문에, 꼭 root가 아닌
각 package마다 해당 코드가 위치해있어야 합니다.
저는 작업사항이 생기면 git push를 진행할 때마다 로컬에서 직접 changeset의 업데이트 과정을
거쳐야 한다는 게 번거롭다고 느껴서, github actions와 함께 해당 이슈를 해결했습니다.
name: Release
on:
push:
branches:
- main
concurrency: ${{ github.workflow }}-${{ github.ref }}
jobs:
release:
name: Release
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@v4
- name: Setup pnpm 8
uses: pnpm/action-setup@v3
with:
version: 8
- name: Setup Node.js 20.x
uses: actions/setup-node@v4
with:
node-version: 20.x
- name: Install Dependencies
run: pnpm i
- name: Create Release Pull Request or Publish to npm
id: changesets
uses: changesets/action@v1
with:
# This expects you to have a script called release which does a build for your packages and calls changeset publish
publish: pnpm release
env:
GITHUB_TOKEN: ${{ secrets.GIT_TOKEN }}
NPM_TOKEN: ${{ secrets.GIT_TOKEN }}
NODE_AUTH_TOKEN: ${{ secrets.GIT_TOKEN }}
permissions:
contents: write
id-token: write
pnpm release는 결국 changeser version
, changeset publish
를 진행하는 로직이기에, 해당 워크플로우가 돌기 전에 changeset
명령어를 수행해야 합니다.
그래서 git push를 진행하면 올리는 동시에 사용자가 changeset을 세팅하도록 husky를 사용했습니다.
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
# GitHub Actions에서 실행되는 경우 pre-push 훅을 건너뛰는 로직입니다
if [ "$GITHUB_ACTIONS" = "true" ]; then
echo "Skipping pre-push hook in GitHub Actions"
exit 0
fi
echo "Running pre-push hook..."
# 해당 명령어를 작성해주지 않으면 husky가 cli의 changeset을 강제로 skip합니다.
exec < /dev/tty
pnpm build
pnpm changeset
# changeset을 진행하면 .changeset에 임시 업데이트 파일이 생기는데, 해당 파일을 반영하기 위해 pre-push 단계에서 같이 파일을 commit합니다.
git add .changeset/
git commit -m "add changeset dump file"
이렇게 되면 PR을 올리고, 머지한 다음 github actions가 실행됩니다.
github actions 실행이 끝마치게 되면 changeset이 Version Packages
PR을 업로드하고, 해당 PR을 머지하면 자동으로 태깅 + 릴리즈 + CHANGELOG + version update가 완료됩니다.