mason
mason-log.

npm 스코프 배포.

mason

masonfe-hyunsu

🧵 1. 개요.

npm org와 패키지 아이콘
  • npm 패키지는 unscopedscoped 타입으로 나눌 수 있다.

  • unscoped는 패키지 이름만 있는 형태라 앞에 @…/가 없고, npm 계정만 있으면 올릴 수 있다.

  • scoped는 이름 앞에 @팀이름/이 붙는 형태라, 그 @팀이름을 npm에 먼저 만들어 둬야 하고, 보통은 org(organization, 조직 계정)로 만든다.

  • 예를 들면 mason-ds@mason-ds/tokens, @mason-ds/react, @mason-ds/vue처럼 scoped 패키지를 나누고, 버전은 하나로 묶어서 배포한다.

  • 이 경우엔 배포 절차가 달라져, publish 전에 org 생성, 권한, 인증을 먼저 맞춰야 한다.

  • 이 포스팅에서는 organization 스코프, 2FA(Two-Factor Authentication, 2차 인증), Granular 토큰, Changesets(모노레포에서 버전 bump와 publish를 기록으로 묶는 도구) linked 흐름을 기준으로 정리해 본다.

한 줄로 요약하면, npm 스코프 배포는 패키지 빌드보다 스코프, 권한, 인증 경로를 먼저 맞추는 절차가 필요하다.


🗂️ 2. 개인 패키지와 org 스코프.

Personal vs Org scope
  • unscoped는 개인 계정만 있으면 registry에 자리를 잡을 수 있고, 그 이름이 패키지 ID가 된다.

unscoped 배포는 아래 명령어로 진행하고, 로그인 후 패키지 디렉터리에서 publish하면 된다.

# npm
npm login
npm publish

# pnpm
pnpm login
pnpm publish
  • @scope/name처럼 scoped package(이름 앞에 @조직또는유저/가 붙는 패키지)는 그 스코프를 소유할 org(또는 유저)가 npm에 있어야 한다.

  • org를 만들기 전에 publish를 치면 E404 Scope not found로 떨어지게 되는데, 원인은 빌드오류가 아니라 네임스페이스가 없는 상태다. (경험담ㅠ)

  • npm org 생성으로 쓸 org-name(조직 이름)을 만들고, 배포 계정이 owner 또는 publish 권한을 갖는지 npm org ls <org-name>로 확인했다.

npm Create a New Organization 화면
  • 스코프 패키지는 기본이 private 쪽에 가깝다. 공개하려면 publishConfig.access: "public"(또는 npm publish --access public)이 필요하다.

  • docs, 루트 워크스페이스처럼 올리면 안 되는 것private: true로 남겨 두고, registry에 올릴 패키지만 publish한다.

org와 스코프가 준비된 뒤, scoped 패키지를 public으로 올릴 때는 access를 붙인다.

# npm
npm publish --access public

# pnpm
pnpm publish --access public

# package.json에 publishConfig.access: "public"이 있으면
# publish 만으로도 된다

publish 명령은 npm, pnpm 둘 다 같고 차이는 패키지 매니저 앞자리뿐이며, mason-ds처럼 Changesets로 여러 패키지를 묶을 때는 레포 스크립트(pnpm release 등)를 따를 뿐이다.


🔐 3. 인증 : 2FA와 Granular 토큰.

  • 요즘은 패키지 생성, publish에 2FA(2차 인증) 또는 Bypass 2FA(배포할 때 2차 인증 입력을 건너뛰게 하는 옵션)가 켜진 Granular Access Token(권한 범위만 열어 둔 접근 토큰)이 사실상 필요하다. (npm 2FA 문서)

  • 계정에 2FA를 켜 두면, 예전에 쓰던 로그인 방식의 CLI(터미널) publish가 EOTP(일회용 비밀번호를 넣으라는 오류)를 요구하는 경우가 많다.

  • 웹은 패스키, 보안키만으로 들어가게 두면, CLI가 받을 OTP(One-Time Password, 일회용 비밀번호) 앱이 없어 더 답답해진다.

그래서 로컬, CI 모두 OTP에 기대지 않는 쪽으로 옮겼다.

  • Access Tokens(npm이 발급하는 접근 키 화면)에서 Granular Access Token을 만들고, 배포용으로 Bypass 2FA를 켠다.

  • 조직/패키지 범위를 org-name, @org-name/*로 좁히고 Read and write(읽기, 쓰기)를 준다.

  • ~/.npmrc//registry.npmjs.org/:_authToken=에 넣고, CI에는 같은 값을 repo secret(저장소에만 숨겨 두는 값, 예: NPM_TOKEN)으로 둔다.

npm whoami
  • Bypass 2FA가 켜진 Granular 토큰이면 pnpm release가 OTP 프롬프트(일회용 비밀번호 입력창) 없이 통과했다.

  • org 설정용으로 토큰에 org만 열어 두면, 그 org의 패키지를 publish할 권한까지 자동으로 생기지는 않아서 패키지, 스코프 쪽 write를 따로 봐야 한다. (Access tokens)

  • 장기적으로는 Trusted Publishing(CI가 장기 토큰 대신 신원 증명으로 publish하는 방식, OIDC)도 선택지다. 이 포스팅 시점의 mason-ds 루틴은 Granular 토큰(Bypass 2FA) + Changesets를 기준으로 둔다.


📦 4. Changesets linked 배포 흐름.

  • Changesets는 모노레포에서 "이번엔 어떤 패키지를 얼마나 올릴지"를 .changeset 파일로 남겨 두고, 나중에 버전, CHANGELOG, publish를 한 흐름으로 처리하는 도구다. (Changesets)

  • linked는 그중 설정으로, 묶어 둔 패키지들의 버전을 항상 같이 맞춘다. 패키지마다 따로 bump하지 않아도 된다.

  • 예를 들면 디자인 시스템처럼 패키지를 나눈 뒤 linked로 두면, 버전을 어긋나게 올리지 않고 항상 같은 버전으로 맞출 수 있다.

  • 로컬 순서는 대략 이렇다.

Changesets 배포 흐름
  1. 기능 커밋 후 pnpm changeset으로 bump 의도를 .changeset/*.md에 남기고 그 파일을 커밋한다.

  2. pnpm version-packagespackage.json, CHANGELOG를 맞춘다.

  3. packages/tokensTOKENS_VERSION과 관련 테스트를 수동으로 같은 문자에 맞추며, CSS 헤더, docs GNB의 v…가 여기서 나온다.

  4. pnpm release(build + changeset publish)로 registry에 올리고, 성공하면 패키지별 git tag가 생기며 원격에는 --follow-tags로 밀어 준다.

CI(.github/workflows/release.yml)는 main push 기준이다.

  • changeset만 있으면 Version PR을 만든다.

  • 이미 버전 bump 커밋이 있으면 publish만 탄다.

로컬에서 version까지 맞춘 뒤 push하면, secret만 있으면 CI가 publish를 이어 받는 식도 가능하다.

반대로 changeset만 올리고 Version PR을 머지하는 흐름도 되고, 둘 다 Changesets action 패턴이다.

Changesets 로컬 명령은 pnpm 스크립트와 npm/npx 둘 다로 적을 수 있다.

# pnpm (mason-ds)
pnpm changeset
pnpm version-packages
pnpm release

# npm / npx
npx changeset
npx changeset version
npx changeset publish

이렇게 org 스코프 패키지가 registry에 등록된다.

npm org 스코프 패키지 배포 결과

🔗 5. 참고 자료.

이전 글
오케스트레이션과 플래닝.

댓글

불러오는 중...
목록으로