배경
모듈을 만들어서 배포를 자동화하는 과정에서 겪은 이슈를 적어봅니다. 이 글은 pnpm으로 구성한 모노레포 환경에서 발생한 문제와 그 해결 과정을 다룹니다.
프로젝트 구조
- pnpm으로 구성한 모노레포
- packages 하위에 사용하고자 하는 커스텀 모듈 위치
- main에 push 발생 시 GitHub Actions를 통해 packages/package-A를 GitHub Packages로 배포
문제 상황
npm 모듈의 버전은 달라야 하기에 package.json의 version을 변경하고 배포를 진행해야 합니다.
매번 PR에서 이 package.json의 버전을 직접 바꾸는 것보다는 이를 자동화하는 것을 목표로 했고, 액션의 배포 단계에서 마이너 패치 버전을 자동으로 올리도록 설정하기로 했습니다.
에러 발생
packages/package-A 디렉토리에서 다음 명령어가 실행될 때 에러가 발생
pnpm version patch
에러 메시지:
npm error code EUNSUPPORTEDPROTOCOL
npm error Unsupported URL Type "workspace:": workspace:*
원인 분석
모노레포의 구성에서 기인하는 문제였습니다. package/package-A는 example(개발용 react 환경, 예시로도 이용) 패키지의 package.json에서 다음과 같이 참조되고 있었습니다. version 스크립트가 워크스페이스의 모든 패키지의 의존성을 업데이트하려 하지만, 워크스페이스 프로토콜로 선언된 부분에서 에러가 발생합니다.
"dependencies": {
"package-A": "workspace:*"
}
workspace:*로 지정되어, 이는 package-A가 어떤 버전이어도 현재 워크스페이스의 버전을 사용하겠다는 의미입니다.
해결 방법
npm 공식 문서의 npm version 부분을 참조하여 workspaces-update 옵션을 사용했습니다. 이 옵션은 기본값은 true로, version 스크립트의 실행 시 워크스페이스 전체의 의존성을 확인하여 함께 업데이트합니다.
다음의 명령어를 사용하여 배포 시 package-A의 버전만을 업데이트하고 워크스페이스 전체의 의존성 검사를 건너뛸 수 있었습니다.
pnpm version patch --workspaces-update=false
여기서 npm-version을 참조한 이유는 pnpm version patch 시 발생한 에러 메시지가 npm으로 표시되었던 점과, pnpm docs에는 version에 대한 안내가 없다는 점 때문이었습니다.
최종 스크립트
해당 명령을 적용하여 완성된 publish workflow의 bash script는 다음과 같습니다.
# package-A의 현재 버전을 확인합니다.
current_version=$(node -p "require('./package.json').version")
echo "Current local version: $current_version"
# package-A의 배포된 버전을 확인하고, 오류가 발생하면 그 메시지는 무시하고 else로 진행합니다.
if published_version=$(pnpm view . version 2>/dev/null); then
# 배포된 최신 버전을 확인합니다.
echo "Latest published version: $published_version"
# 로컬 버전과 배포된 버전을 비교하여, 배포된 버전이 더 높으면 로컬 버전을 배포 버전으로 업데이트 후 패치를 진행합니다.
if [ "$(printf '%s\n' "$current_version" "$published_version" | sort -V | tail -n1)" = "$published_version" ]; then
echo "Published version is newer. Updating local version and then patching."
# 로컬 버전을 배포 버전으로 업데이트합니다.
pnpm version $published_version --workspaces-update=false --no-git-tag-version --allow-same-version
# 패치를 진행합니다.
new_version=$(pnpm version patch --workspaces-update=false --no-git-tag-version)
echo "Updated to new version: $new_version"
git add package.json
git commit -m "chore: Bump version to $new_version"
git push
# 로컬 버전과 배포 버전이 같으면 패치를 진행합니다.
elif [ "$current_version" = "$published_version" ]; then
echo "Local version is equal to published version. Patching."
new_version=$(pnpm version patch --workspaces-update=false --no-git-tag-version)
echo "New version: $new_version"
git add package.json
git commit -m "chore: Bump version to $new_version"
git push
# 로컬 버전이 더 높은 경우 그대로 사용합니다.
else
echo "Local version is newer. Preparing to publish current version."
new_version=$current_version
fi
else
# 배포된 버전이 없는 경우, 로컬 버전을 그대로 사용합니다.
echo "Package not published yet. Preparing for first publish."
new_version=$current_version
fi
# 배포를 진행합니다.
echo "Publishing version $new_version"
pnpm publish --no-git-checks
결론
모노레포 환경에서 pnpm을 사용할 때, 패키지 버전 관리와 자동 배포 설정에 주의가 필요합니다. workspaces-update 옵션을 적절히 활용하면 원하는 패키지만 버전 업데이트할 수 있습니다. 이 경험을 통해 모노레포 구조에서 모듈의 버전 관리를 어떻게 하면 좋을 지 알게 되었습니다.
참조
https://docs.npmjs.com/cli/v10/commands/npm-version#workspaces-update
npm-version | npm Docs
Bump a package version
docs.npmjs.com
https://pnpm.io/workspaces#referencing-workspace-packages-through-aliases
Workspace | pnpm
pnpm has built-in support for monorepositories (AKA multi-package repositories,
pnpm.io