~/web-app-dev.md
WEB_APP_DEV

npm trusted publishing은 배포 자동화가 아니라 패키지 출처 계약이다

10분 읽기·2026.06.23·출처 5·00
오늘의 질문

우리 팀이 만드는 npm 패키지와 내부 UI 라이브러리는 “누가 빌드해서 올렸는지”를 어디까지 증명할 수 있을까?

web-app-dev.md
SURVIVE.exe

npm trusted publishing은 배포 자동화가 아니라 패키지 출처 계약이다

1. 왜 지금 봐야 하나

웹앱 개발자는 매일 npm 패키지 위에 제품을 올린다. React 컴포넌트 라이브러리, Hono 미들웨어, ESLint config, 디자인 시스템, SDK, 내부 CLI까지 모두 “패키지”가 된다. 작은 팀에서는 이런 패키지를 배포하는 방식이 대개 비슷하다. 누군가 npm automation token을 만들고, GitHub Actions secret에 NPM_TOKEN으로 저장한 뒤, 태그가 찍히면 npm publish를 실행한다.

이 방식은 오래 쓰였지만, AI 코딩 에이전트와 자동화가 늘어난 지금은 위험한 기본값이 됐다. 토큰은 한 번 새면 재사용될 수 있고, CI 로그·캐시·설정 파일·로컬 .env·권한이 넓은 에이전트 작업공간에 남을 수 있다. 패키지 배포 권한은 단순히 “릴리스 버튼을 누를 수 있는 권한”이 아니라, 그 패키지를 설치하는 모든 웹앱에 코드를 공급하는 권한이다.

그래서 Hono v4.12.26 릴리스의 작은 changelog 한 줄이 눈에 띈다. ci: publish to npm from CI with OIDC trusted publishing. Hono는 이미 6월 9일 v4.12.25에서 CORS, body limit, Windows path traversal, Set-Cookie, repeated header 관련 보안 수정을 공개했다. 그리고 6월 18일 v4.12.26에서는 패키지 배포 자체를 OIDC trusted publishing으로 옮기는 CI 변경을 포함했다.

오늘의 핵심은 “Hono가 새 버전을 냈다”가 아니다. npm trusted publishing은 배포 자동화 편의 기능이 아니라, 패키지 출처와 빌드 경로를 CI 워크플로 단위로 고정하는 공급망 계약이다.

2. 핵심 개념

기존 npm 배포는 보통 토큰 중심이다. npm 계정 또는 패키지 권한을 가진 사용자가 긴 수명의 토큰을 만들고, CI가 그 토큰으로 레지스트리에 로그인한다. CI 입장에서는 “이 문자열을 아는 사람은 publish할 수 있다”가 인증 모델이다.

trusted publishing은 이 모델을 바꾼다. npm 문서에 따르면 trusted publishing은 CI/CD workflow가 OpenID Connect, 즉 OIDC로 npm에 자신을 증명하게 한다. 패키지 설정에는 “이 npm 패키지는 GitHub Actions의 이 repository, 이 workflow file, 필요하면 이 environment에서 온 publish를 신뢰한다”는 관계를 등록한다. CI는 장기 토큰을 들고 있지 않고, 실행 중인 워크플로가 짧은 수명의 OIDC 토큰을 발급받아 npm에 교환한다.

즉 신뢰 단위가 바뀐다.

  • 예전 질문: NPM_TOKEN을 아는가?
  • 새로운 질문: npm이 허용한 repository/workflow/environment에서 실행 중인가?

이 차이는 매우 크다. 토큰 문자열은 복사될 수 있지만, OIDC claim은 실행 맥락과 묶인다. GitHub Actions라면 permissions: id-token: write가 필요하고, npm에 등록한 workflow filename이 실제 .github/workflows/ 아래 파일명과 맞아야 한다. npm CLI는 OIDC 환경을 감지하면 OIDC 인증을 먼저 사용하고, 필요하면 기존 토큰 방식으로 fallback한다.

또 하나의 핵심은 provenance다. npm provenance는 패키지가 어디에서, 어떤 소스와 빌드 지시로 만들어졌는지 공개 attestation으로 남기는 기능이다. npm 문서는 provenance가 악성 코드가 없음을 보장하지는 않는다고 분명히 말한다. 대신 패키지와 source code, build instructions, build environment 사이의 검증 가능한 연결을 제공한다. trusted publishing을 GitHub Actions나 GitLab CI/CD에서 쓰면 provenance attestation이 자동 생성된다.

정리하면 trusted publishing은 세 가지 계약을 만든다.

  1. 배포 주체 계약: 특정 CI provider와 workflow만 publish할 수 있다.
  2. 자격 증명 계약: 장기 토큰이 아니라 짧은 수명의 OIDC credential을 쓴다.
  3. 출처 증명 계약: 가능한 경우 package, source, build environment의 연결을 provenance로 남긴다.

3. 최신 이슈와 연결

npm trusted publishing 자체는 GitHub Changelog 기준 2025년 7월 31일 일반 공개됐다. 하지만 지금 다시 볼 이유는 2026년 npm 문서와 실제 프로젝트 채택 흐름 때문이다.

npm trusted publishers 문서는 2026년 6월 4일 기준으로 GitHub Actions, GitLab CI/CD, CircleCI를 지원한다고 설명한다. 또한 trusted publishing에는 npm CLI 11.5.1 이상과 Node.js 22.14.0 이상이 필요하다고 적고 있다. GitHub Actions에서는 GitHub-hosted runner를 사용해야 하고, id-token: write 권한이 필요하다. self-hosted runner는 아직 지원되지 않는다고 안내한다.

npm CLI v11의 npm trust 문서도 중요하다. 이 명령은 npm 웹사이트에서 하던 trusted publisher 설정을 CLI로 관리하는 기능이다. 예를 들어 GitHub Actions에 대해 package, workflow file, repository, environment, --allow-publish, --allow-stage-publish 같은 옵션을 지정할 수 있다. 단 npm registry는 현재 package당 trusted publisher configuration을 하나만 지원한다. 이미 관계가 있으면 list하고 revoke한 뒤 새로 만들어야 한다.

Hono v4.12.26의 changelog는 이 변화가 문서 속 이상론이 아니라 실제 TypeScript 웹 프레임워크 프로젝트의 릴리스 파이프라인으로 들어오고 있음을 보여준다. Hono는 여러 런타임에서 쓰이는 웹 프레임워크다. Cloudflare Workers, Deno, Bun, Node.js, AWS Lambda, Lambda@Edge 같은 환경에서 쓰이는 패키지가 CI publish를 OIDC trusted publishing으로 옮긴다는 것은, 웹앱 개발자가 “내가 설치하는 패키지가 어떤 경로로 올라왔는가”를 점점 더 중요하게 봐야 한다는 신호다.

4. 개발자 관점 해석

웹앱 팀이 이 주제를 “오픈소스 메인테이너 이야기”로만 보면 놓치는 것이 있다. 대부분의 제품 팀도 이미 패키지 발행자다.

  • @company/ui
  • @company/api-client
  • @company/eslint-config
  • @company/hono-middlewares
  • @company/supabase-types
  • @company/feature-flags-sdk

이런 내부 패키지는 제품 전체에 퍼진다. 배포 권한이 뚫리면 하나의 앱이 아니라 여러 프론트엔드, 백엔드, 워커, 관리자 도구가 동시에 영향을 받는다. 더 무서운 점은 패키지 업데이트가 정상적인 개발 흐름처럼 보인다는 것이다. 버전이 올라가고 lockfile이 바뀌고 CI가 통과하면 사람은 쉽게 지나친다.

따라서 npm publish 파이프라인은 다음 질문으로 리뷰해야 한다.

첫째, 배포 권한이 사람 계정에 붙어 있는가, 워크플로에 붙어 있는가? 사람 계정 토큰은 로컬 머신, secret store, 자동화 도구 사이를 이동한다. 반면 trusted publisher는 “이 repository의 이 workflow”라는 실행 경계에 붙는다.

둘째, release workflow가 build/test/install과 분리되어 있는가? npm 문서의 예시는 release build에서 package manager cache를 쓰지 말라고 권한다. 캐시는 속도를 주지만, release 산출물에는 불필요한 상태 오염 가능성을 만든다. 특히 AI 에이전트가 lockfile, build script, postinstall script를 만지는 환경이라면 release job은 더 보수적으로 다뤄야 한다.

셋째, provenance를 ‘안전 보증서’로 오해하지 않는가? provenance는 “이 코드가 어디서 만들어졌는지”를 보여준다. 그러나 그 코드가 안전한지, 리뷰가 충분했는지, 의존성이 깨끗한지는 별개다. provenance는 감사의 시작점이지, 보안 검토의 끝이 아니다.

넷째, private repository와 self-hosted runner 제약을 알고 있는가? npm 문서와 GitHub changelog는 private source repository의 provenance 제한, self-hosted runner 미지원 같은 조건을 언급한다. 내부 패키지 팀은 “우리는 private repo라서 같은 수준의 공개 provenance를 기대할 수 있는가?”를 먼저 확인해야 한다.

다섯째, 패키지별 trusted publisher가 하나뿐이라는 제약을 배포 설계에 반영했는가? monorepo에서 여러 release workflow가 같은 package를 publish하려 하거나, hotfix용 별도 workflow를 만들고 싶다면 npm의 현재 제한과 충돌할 수 있다. 그래서 package마다 “정식 publish 경로는 하나”라는 원칙을 세우는 편이 안전하다.

5. 내 프로젝트에 적용할 체크포인트

다음 체크리스트를 내부 패키지 하나에 적용해보자.

배포 권한

  • GitHub Actions secret에 NPM_TOKEN이 남아 있는가?
  • 그 토큰은 package 단위 granular token인가, account 전체 권한인가?
  • 토큰에 2FA bypass 권한이 붙어 있는가?
  • 릴리스 담당자가 퇴사하거나 권한이 바뀌었을 때 token rotation 절차가 있는가?

workflow 고정

  • publish는 .github/workflows/publish.yml 같은 하나의 파일에서만 일어나는가?
  • workflow filename이 npm trusted publisher 설정과 정확히 일치하는가?
  • release job에 permissions: id-token: writecontents: read처럼 필요한 권한만 있는가?
  • branch push가 아니라 tag 또는 GitHub Release 이벤트처럼 의도된 release signal에서만 동작하는가?

빌드 재현성과 검증

  • npm ci를 사용해 lockfile 기준으로 설치하는가?
  • npm test, npm run build --if-present, 타입 체크가 publish 전에 실행되는가?
  • release build에서 불필요한 package manager cache를 끄는가?
  • prepublishOnly, prepare, postinstall 스크립트가 예상치 못한 네트워크 호출이나 파일 생성을 하지 않는가?

provenance와 감사

  • public package라면 provenance attestation이 생성되는 조건을 만족하는가?
  • repository 필드가 실제 repo와 정확히 연결되어 있는가?
  • private package라면 provenance 제한을 팀 문서에 명시했는가?
  • 패키지를 소비하는 앱에서 “provenance가 있다 = 안전하다”로 오해하지 않도록 리뷰 기준을 분리했는가?

6. 오늘 10분 액션

오늘은 모든 패키지를 바꾸지 말고 하나만 고르자. 가장 많이 설치되는 내부 패키지 하나를 선택한다. 예를 들어 @company/ui@company/api-client가 좋다.

  1. npm package settings 또는 registry 설정에서 trusted publisher 섹션이 있는지 확인한다.
  2. 현재 publish workflow를 열고 NPM_TOKEN, NODE_AUTH_TOKEN, permissions, trigger 조건을 확인한다.
  3. workflow 파일 이름, repository, environment를 적고 “이 경로만 publish할 수 있어야 한다”는 문장을 만든다.
  4. GitHub-hosted runner를 쓰는지, npm CLI와 Node.js 버전이 trusted publishing 요구사항을 만족하는지 확인한다.
  5. 바로 마이그레이션하지 못한다면 최소한 NPM_TOKEN의 scope와 만료·rotation 담당자를 문서화한다.

10분 안에 끝낼 수 있는 목표는 migration 완료가 아니다. 패키지 배포 권한이 문자열 토큰에 묶여 있는지, 워크플로 출처 계약에 묶여 있는지 확인하는 것이다.

7. 더 볼 자료

중복 회피 메모

로컬 content/generated와 Supabase의 최근 web_app_dev 글을 확인했다. 최근 글은 Supabase OAuth/RLS 위임 권한, WebMCP 브라우저 도구 계약, View Transition 상태 변화 계약, Supabase 브랜칭/pg-delta, agent-friendly content negotiation, React Compiler, Next.js Cache Components, Hono v4.12.25 보안 릴리스를 다뤘다. 이번 글은 Hono를 다시 언급하지만 CORS/body limit/static file 같은 런타임 보안 이슈가 아니라, Hono v4.12.26에 포함된 npm OIDC trusted publishing 전환을 출발점으로 패키지 배포 권한, provenance, CI workflow 출처 계약을 웹앱 팀의 공급망 설계 관점에서 다뤘다.

오늘 10분 액션+5%

댓글 0

최신순 ▾
한 줄 남기기

혹시 이 글을 읽는 동료 개발자가 있다면, GitHub으로 로그인하고 한 줄 흔적을 남겨줘요. (스팸 방지용 로그인이에요)