그림 그리는 개발자
  • npm 에 내가 만든 패키지 배포하기 (feat. github action 으로 배포 자동화)
    2023년 09월 15일 20시 26분 23초에 업로드 된 글입니다.
    작성자: 루루개발자

    개발을 하다 보면 자주 사용되는 것들은 패키지화 하여 필요할 때 불러와 사용하곤 합니다. 저번 포스팅에선 github 에 패키지를 배포하는 과정에 대해 공유드렸었는데, 이번에는 github action 을 이용하여 npm 에 배포하는 방법에 대해 공유드리고자 합니다. 

     

    ✻ 필요 패키지 설치

    배포하고자 하는 패키지의 레포지토리의 루트 경로에서 아래 명령어를 통해 빌드시 필요한 패키지들을 설치합니다.

    npm i -D esbuild esbuild-css-modules-plugin
    이 외에도 레포지토리에서 필요로 하는 패키지들은 모두 devDependencies 로 설치하거나 옮기실 것을 권장합니다. 나중에 esbuild 로 번들링 할 때 필요 패키지들은 같이 묶여서 번들링 될 것이기 때문입니다.

     

    ✻ package.json 설정

    1) name

    {
      ...
      "name": "@github이름/패키지이름",
      ...
    }

    package.json 에서 "name" 부분을 위와 같이 작성해주세요. (예시. "@myname/react-calendar")

     

    2) license

    {
      ...
      "license": "MIT",
      ...
    }

    package.json 에서 "license" 부분을 위와 같이 작성해주세요. MIT 로 작성하긴 했지만, 다른 라이센스를 원하실 경우에는 해당 라이선스로 작성하시면 됩니다. 

     

    3) private

    {
      ...
      "private": false,
      ...
    }

    package.json 에서 "private" 부분을 위와 같이 작성해주세요.

     

    4) scripts

    {
      ...
      "scripts": {
        ...
        "pack:build": "node ./build.esbuild.js",
        "pack:build:tsc": "tsc --project ./tsconfig.package.json",
        ...
      },
      ...
    }

    package.json 에서 "scripts" 에 위 항목을 추가해주세요.

     

    5) repository

    {
      ...
      "repository": "배포할 레포지토리의 github 주소",
      ...
    }

    package.json 에서 "repository" 부분을 위와 같이 작성해주세요. (예시. "https://github.com/myname/react-calendar")

     

    6) main

    {
      // ...
      "main": "index.ts",
      // ...
    }

    package.json 에서 "main" 부분을 위와 같이 작성해주세요.

     

    7) peerDependencies

    {
      ...
      "peerDependencies": {
        ...
        "@types/react": "^18.2.15",
        "@types/react-dom": "^18.2.7",
        "react": "^18.2.0",
        "react-dom": "^18.2.0"
        ...
      },
      ...
    }

    package.json 에서 "peerDependencies" 부분을 작성해주세요. 본 패키지가 돌아가는데 필요한 부가적인 패키지들을 명시하는 곳입니다. 가령 리액트의 패키지라고 한다면 리액트 같은 경우는 번들링시에 포함시키지 않고 사용자측의 node_modules 에 설치된 리액트 패키지를 사용하는 것이 더 나을 수 있습니다. 이런 경우에는 아래에서도 설명하겠지만 esbuild 로 빌드시 리액트 관련된 패키지들은 번들링에서 제외하고 이를 peerDependencies 에 명시하는 것이 좋습니다. 위에 명시한 4개 패키지는 예시이며 개발하시는 패키지에 따라 위 내용은 달라질 수 있습니다.

     

    ✻ LICENSE 파일 작성

    레포지토리 루트 경로에 LICENSE 파일을 생성 하신 후 아래 내용을 작성합니다. MIT 기준이니 다른 라이센스를 사용하실 분들은 다른 내용을 기입하시길 바랍니다.

    MIT License
    
    Copyright (c) 2023 {{username}}
    
    Permission is hereby granted, free of charge, to any person obtaining a copy
    of this software and associated documentation files (the "Software"), to deal
    in the Software without restriction, including without limitation the rights
    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the Software is
    furnished to do so, subject to the following conditions:
    
    The above copyright notice and this permission notice shall be included in all
    copies or substantial portions of the Software.
    
    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    SOFTWARE.

    {{username}} 부분은 본인 github 계정의 이름으로 대체해 주세요.

     

    tsconfig.package.json 파일 작성

    {
      "compilerOptions": {
        "target": "es6",
        "lib": ["dom", "dom.iterable", "esnext"],
        "declaration": true,
        "declarationDir": "lib",
        "emitDeclarationOnly": true,
        "allowJs": true,
        "skipLibCheck": true,
        "strict": true,
        "forceConsistentCasingInFileNames": true,
        "esModuleInterop": true,
        "module": "esnext",
        "moduleResolution": "node",
        "resolveJsonModule": true,
        "isolatedModules": true,
        "jsx": "preserve",
        "incremental": true,
        "paths": {
          "@/*": ["./src/*"]
        }
      },
      "include": ["next-env.d.ts", "src/hooks/**/*.ts", "src/hooks/**/*.tsx", "src/components/**/*.ts", "src/components/**/*.tsx", "index.ts"],
      "exclude": ["node_modules"],
    }

    본 파일은 패키지를 내보낼 때 *.d.ts 타입 정의 파일을 생성하기 위한 typescript 설정 정보입니다. "include" 항목에는 타입이 정의 되어야 할 항목들이 위치한 경로들을 작성해주세요. 그 밖에 옵션들을 그대로 사용하셔도 되지만 필요시 본인 환경에 맞게 적절히 수정해주세요.

     

    build.esbuild.js 파일 작성

    const esbuild = require("esbuild");
    const cssModulesPlugin = require("esbuild-css-modules-plugin");
    
    esbuild.build({
      entryPoints: ["index.ts"],
      target: ['es5', 'es6', 'es2017'],
      bundle: true,
      sourcemap: false,
      minify: true,
      format: 'cjs',
      platform: 'browser',
      jsx: 'automatic',
      outfile: "index.js",
      tsconfig: "./tsconfig.package.json",
      treeShaking: true,
      external: ['react-dom', 'react'],
      plugins: [
        cssModulesPlugin({
          inject: true,
          localsConvention: 'dashes',
          filter: /\.module?\.css$/i,
          v2: false,
        }),
      ],
    });

    본 파일은 패키지로 내보낼 코드들을 하나의 index.js 파일로 번들링 하기 위한 esbuild 의 설정 정보입니다. external 에는 번들링시 제외할 항목들을 적어주시면 됩니다. 패키지가 리액트용 패키지일 경우로 가정한다면 리액트는 사용자측에 설치된 것을 사용할 예정이므로, external 에 react 관련 패키지들을 명시한 상태입니다. 모든 옵션들은 본인 환경에 맞게 적절히 수정하여 사용해주세요.

     

    .gitignore 파일 작성

    lib

    .gitignore 파일에 위 항목을 추가 해주세요.

     

    index.ts 파일 작성

    export * from './src/hooks/...';
    export * from './src/components/...';

    패키지로 내보낼 컴포넌트나 함수 등을 작성해주세요.

     

    npm 에서 access token 발급

    1) npm 접속 후 로그인 진행

    https://www.npmjs.com/

    2) 프로필 클릭 후 "Access Tokens" 클릭

    3) Generate New Token 클릭 후 "Classic Token" 클릭

    4) 토큰 Name 입력, Automation 선택 후 "Generate Token" 버튼 클릭

    생성된 토큰을 복사하여 잘 보관합니다.

     

    github action 에 secret key 등록

    1) 레포지토리 Settings 클릭

    2) Secrets and variables 의 Actions 클릭

    3) New repository secret 클릭

    4) key 이름은 "PACKAGE_PUBLISH_KEY_NPM" 으로 지정하며, 토큰은 앞서 npm 에서 발급받은 access token 입력 후 Add secret 클릭

    5) 생성된 key 확인

     

    .github/workflows/package-deploy-release.yml 파일 작성

    name: package-deploy-release
    
    on:
      push:
        tags:
          - 'v[0-9]+.[0-9]+.[0-9]+'
    
    jobs:
      publish-release-registry:
        runs-on: ubuntu-latest
        permissions:
          contents: read
          packages: write
        steps:
          - uses: actions/checkout@v2
          - uses: actions/setup-node@v1
            with:
              node-version: 18
              registry-url: https://registry.npmjs.org/
          - run: npm install
          - run: npm run pack:build
          - run: npm run pack:build:tsc
          - run: mv ./index.ts ./index.d.ts
          - run: rm -rf ./src
          - run: rm -rf ./public
          - run: rm -rf ./.github
          - run: rm -rf ./package-lock.json
          - run: rm -rf ./tailwind.config.js
          - run: rm -rf ./postcss.config.js
          - run: rm -rf ./tsconfig.json
          - run: rm -rf ./tsconfig.package.json
          - run: rm -rf ./build.esbuild.js
          - run: rm -rf ./next.config.js
          - run: rm -rf ./next-env.d.ts
          - run: rm -rf ./.eslintrc.json
          - run: mv ./lib/src ./src
          - run: rm -rf ./lib
          - run: npm publish --access=public
            env:
              NODE_AUTH_TOKEN: ${{secrets.PACKAGE_PUBLISH_KEY_NPM}}

    github 에서 레포지토리에 v.*.*.* 패턴의 새로운 태그가 생성되면 npm 에 배포하는 코드를 실행하는 내용이 작성되어 있는 github 의 workflow 내용입니다. rm -rf 부분은 패키지 배포시 굳이 포함하지 않아도 될 파일들을 삭제하는 용도로 작성해둔 것이니 본인 환경에 맞게 적절히 수정하시면 됩니다.

     

    새로운 tag 생성 및 release 추가할 시 npm 배포 진행

    이제 github action 으로 npm 에 배포하는 설정은 모두 완료되었습니다. github 레포지토리에 들어가서 v0.0.1 과 같은 형태로 새로운 태그 및 릴리즈를 발행하신 후, 레포지토리의 Actions 탭에 들어가 확인해보시면 저희가 작성했던 github workflow 가 실행되면서 npm 에 배포가 될 것입니다.

     

    npm 에 배포하기 전에 github packages 에 배포하여 테스트 (선택사항)

    npm 에 배포하기 전에, 먼저 배포가 되었을 때 정상 작동하는지 여부를 테스트 해보고 싶을 수도 있습니다. 이럴 때 npm 에 배포하기 전에 먼저 github packages 에 배포하여 배포 후의 테스트를 진행해볼 수 있습니다. 

    1) github 설정 페이지에 들어가서 token 생성 페이지에 진입

    https://github.com/settings/tokens

    2) 토큰 이름은 "PACKAGE_PUBLISH_KEY_GITHUB" 으로 지정하고 scope 는 "write:packages" 를 선택 후 "Generate token" 클릭

    3) .github/workflows/package-deploy-test.yml 파일 작성

    name: package-deploy-test
    
    on:
      push:
        tags:
          - '*test.[0-9]+'
    
    jobs:
      publish-github-registry:
        runs-on: ubuntu-latest
        permissions:
          contents: read
          packages: write
        steps:
          - uses: actions/checkout@v2
          - uses: actions/setup-node@v1
            with:
              node-version: 18
              registry-url: https://npm.pkg.github.com/
          - run: npm install
          - run: npm run pack:build
          - run: npm run pack:build:tsc
          - run: mv ./index.ts ./index.d.ts
          - run: rm -rf ./src
          - run: rm -rf ./public
          - run: rm -rf ./.github
          - run: rm -rf ./package-lock.json
          - run: rm -rf ./tailwind.config.js
          - run: rm -rf ./postcss.config.js
          - run: rm -rf ./tsconfig.json
          - run: rm -rf ./tsconfig.package.json
          - run: rm -rf ./build.esbuild.js
          - run: rm -rf ./next.config.js
          - run: rm -rf ./next-env.d.ts
          - run: rm -rf ./.eslintrc.json
          - run: mv ./lib/src ./src
          - run: rm -rf ./lib
          - run: npm publish
            env:
              NODE_AUTH_TOKEN: ${{secrets.PACKAGE_PUBLISH_KEY_GITHUB}}

    github 에서 레포지토리에 *test.* 패턴의 새로운 태그가 생성되면 npm 에 배포하는 코드를 실행하는 내용이 작성되어 있는 github 의 workflow 내용입니다. 이제 새로운 tag 및 릴리즈를 생성할 때 버전명을 v.0.0.1-test.1 와 같이 생성할 경우 앞서 작성한 "package-deploy-test" workflow 가 실행되며 github packages 에 패키지가 배포됩니다.

    4) .npmrc 파일 작성

    @username:registry=https://npm.pkg.github.com/
    //npm.pkg.github.com/:_authToken=...

    github packages 에 배포된 패키지를 내려받아 테스트할 레포지토리의 루트에서 .npmrc 파일을 작성하고 username 에는 본인의 github 계정의 이름을, _authToken 값에는 앞서 발급받았던 PACKAGE_PUBLISH_KEY_GITHUB 토큰을 기재해줍니다.

    5) .gitignore 파일 작성

    .npmrc

    github packages 에 배포된 패키지를 내려받아 테스트할 레포지토리의 루트에서 .npmrc 파일을 작성했다면 이를 .gitignore 에 추가하여 git 저장소에 포함되지 않도록 합니다. (.npmrc 는 .env 처럼 따로 관리해주시는 것이 좋습니다.)

    6) github packages 에 배포한 패키지 설치 및 테스트 진행

    npm install @username/패키지명@0.0.1-test.1

    위 형태와 같이 github packages 에 배포했던 패키지를 직접 설치하여 테스트를 진행 해 본 후 이상이 없다면 v0.0.1 형태로 릴리즈를 생성하여 npm 에 배포를 마저 진행하시면 되겠습니다.

     

     마치며

    본 포스팅글은 리액트용 프론트엔드 패키지를 배포할 경우에 초점을 맞춰 내용들이 작성되었기 때문에, 만약 리액트용 패키지가 아니고 다른 기반의 패키지이거나 백엔드용 패키지일 경우에는 위에서 언급하였던 옵션들을 일부 수정하셔야 될 수 있습니다. 이점 참고하시고 본 글이 도움이 많이 되었으면 좋겠습니다. 감사합니다. :)

     

    댓글