(이전 글을 읽지 않았더라도 본문을 읽는 데에는 지장이 없다.)

이전 글에서는 iOS 스타일 Date Picker 를 순수 자바스크립트로 구현한 오픈소스 코드 조각에 대해 코드 리뷰를 했다. 이후 (글로 정리하지는 않았지만) 약간의 리팩토링을 거쳐 해당 오픈소스를 React 컴포넌트로 구현했다.

그러면 이제는 구현한 것을 라이브러리로 만들어 NPM에 올려보자.

1. 빌드 환경 구성

빌드 환경 구성에는 원래 Webpack 을 즐겨 사용했으나, 이번에는 Vite 를 사용해보자.

1.1. Vite 설치 및 적용

아래 명령어를 사용하면 yarn 이 자동으로 Vite 프로젝트를 만들어준다.

yarn create vite ios-style-picker --template react-ts

프로젝트는 대강 아래의 형태로 구성된다.

ios-style-picker
|- public/
|- src/
|- .gitignore
|- index.html
|- package.json
|- tsconfig.json
|- tsconfig.node.json
|- vite.config.ts

여기서 Vite 설정은 vite.config.ts 에 작성한다. 생성된 기본 설정은 아래와 같다.

import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";

export default defineConfig({
  plugins: [react()],
});

라이브러리 빌드를 위해서는 아래 설정들을 추가해야한다.

  • 웹앱이 아니라 라이브러리를 빌드하도록 설정을 추가한다.
  • *.d.ts 파일을 생성하게끔 설정을 추가한다.
  • react 등 외부 디펜던시에 대한 설정을 추가한다.

1.1.1. build.lib

build.lib 설정은 라이브러리 빌드를 위해 Vite 에서 제공하는 설정이다. 이 설정도 추가해주자.

// ...
import path from "path";

export default defineConfig({
  // ...
  build: {
    lib: {
      // 빌드 타겟
      entry: path.resolve(__dirname, "src/index.ts"),
      // `'umd'` 모듈이 전역 변수로 모듈을 내보낼때 쓰는 이름.
      // Vite 가 기본적으로 `'es'`, `'umd'` 두 형식으로 모듈을 만들어주므로
      // 설정해주어야 한다.
      name: "IosStylePicker",
      // 빌드 결과 생성되는 소스코드의 파일명 형식
      fileName: (format) => `ios-style-picker.${format}.js`,
    },
  },
});

1.1.2. vite-plugin-dts

라이브러리 사용자에게 타입을 제공해야 하므로, *.d.ts 파일을 만들어주는 플러그인을 설치하자.

yarn add --dev vite-plugin-dts

설치한 플러그인을 설정 파일에 추가해주자.

//...
import dts from "vite-plugin-dts";

export default defineConfig({
  plugins: [react(), dts()], //<- 여기에 추가해준다.
});

1.1.3. build.rollupOptions

build.rollupOptions 설정은 Vite 내부적으로 사용되는 번들러 Rollup 의 설정을 직접 지정할 수 있는 설정이다.

여기서는 React 를 외부 라이브러리로 지정하기 위해 (빌드 결과에 React 코드를 포함시키지 않기 위해) 사용한다.

import path from "path";

export default defineConfig({
  // ...
  build: {
    // ...
    rollupOptions: {
      // 외부 라이브러리로 사용할 라이브러리들 이름.
      external: ["react", "react-dom"],
      // 외부 라이브러리에 접근할 때 사용할 전역변수명.
      // `'umd'` 빌드 시 사용된다.
      output: {
        globals: {
          react: "React",
          "react-dom": "ReactDOM",
        },
      },
    },
  },
});

1.2. package.json

package.json 도 수정해보자.

1.2.1. Entry Point

엔트리 포인트 관련 설정은 Vite 공식 문서에서 권장하는 설정이 있다. 아래와 같다. 우리도 그대로 넣어주자.

{
  // ...
  "type": "module",
  "files": ["dist"],
  "main": "./dist/ios-style-picker.umd.cjs",
  "module": "./dist/ios-style-picker.es.js",
  "exports": {
    ".": {
      "import": "./dist/ios-style-picker.es.js",
      "require": "./dist/ios-style-picker.umd.cjs"
    }
  }
}

module, exports 속성은 NPM 공식 문서에는 없는 속성이다. 속성 이름과 지정된 값을 보면 직관적으로 어떤 값일지 추정이 가능하지만, 왜 문서에 없는 값들을 쓰고 있는지는 이 스택오버플로우 글을 참고하자.

1.2.2. types

타입스크립트 타입 엔트리 포인트 정보다.

{
  // ...
  "types": "dist/index.d.ts",
}

1.2.3. peerDependencies

이 라이브러리를 사용하기 위해서는 react 와 react-dom 을 반드시 별도로 설치해주어야 한다. 그런 디펜던시는 peerDependencies 에 따로 명시해주자.

{
  // ...
  "peerDependencies": {
    "react": ">=17.0.0",
    "react-dom": ">=17.0.0"
  }
}

1.2.4. 기타 정보

NPM 패지키로 올리려는 만큼, 패키지와 관련된 세부 내용도 작성해주는 것이 좋다. 필드 이름과 값 모두 직관적이므로 따로 설명을 적진 않겠다.

{
  "name": "ios-style-picker",
  "version": "0.0.3",
  "author": {
    "name": "ricale",
    "email": "kim.kangseong@gmail.com"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/ricale/ios-style-picker.git"
  },
  "bugs": {
    "url": "https://github.com/ricale/ios-style-picker/issues"
  },
  "license": "MIT",
  "keywords": [
    "React date picker",
    "React datepicker",
    "iOS style datepicker",
    "iOS style date picker"
  ],
  "homepage": "https://github.com/ricale/ios-style-picker#readme",
  // ...
}

3. 빌드 및 배포

3.1. 빌드

아래 명령어를 실행하면

yarn build

dist/ 디렉토리에 빌드 결과 소스파일이 추가된다.

dist/ 디렉토리는 기본적으로 .gitignore에 포함되어있다. 하지만 우리는 빌드 결과를 프로젝트에 포함시켜야 하므로, .gitignore 에서 해당 내용을 삭제하거나 Vite 설정을 수정해 빌드 결과 소스파일이 저장될 위치를 변경해주어야 한다. (나는 전자를 선택했다.)

3.2. 배포 전 테스트

NPM 에 배포하기 전에 로컬에서 설치를 테스트해볼 수 있다고 한다.

테스트용 프로젝트를 하나 만들고, 아래 형식의 명령어로 구현중인 라이브러리를 추가해서 사용해보면 된다.

npm install path/to/my-package

하지만 나의 경우에는 테스트용 프로젝트에 설치 후 사용 시 Invalid Hook Call 에러가 발생해서 사용해볼 수가 없었다. 아마 react 를 (라이브러리의 devDependencies 와 테스트 프로젝트의 dependencies 로부터) 중복 참조해서 생기는 일 같은데 끝내 해결하지 못했다.

(이 글에서는 npm link 명령어를 사용하면 임시로 해결 가능하다고 하는데 어디까지나 임시일 뿐이고 그다지 매끄럽지도 않다.)

하지만 이 테스트에 통과하지 못해도 상관 없다. NPM 배포 후에 배포된 패키지를 설치해보면 문제 없이 동작하는 걸 확인할 수 있다. 그러니 이 테스트 결과에 너무 목메이지 말자.

3.3. 배포

아래 명령어로 로그인 후 배포하자.

npm adduser # 회원가입 혹은 로그인
npm publish # 현재 디렉토리의 패키지를 배포

이제 배포가 완료되었다. 배포된 패키지는 https://www.npmjs.com/package/패키지이름 에서 확인할 수 있다. (이 패키지는 https://www.npmjs.com/package/ios-style-picker 에서 확인할 수 있다.) 만약 README.md 까지 작성해뒀다면 접속했을 때 아래 같은 형식으로 보일 것이다.

3.4. 업데이트

업데이트를 하고 싶다면? package.json 에서 version 값을 올려준 뒤

{
  // ...
  "version": "0.0.4"
}

다시 빌드 및 배포를 실행하면 된다.

yarn build # 빌드
npm publish # 배포

4. 다음

내가 만든 iOS 스타일 Date Picker 를 NPM 에 올렸다. 이제 다음으로 해야할 일은 아래와 같다.

  • 리팩토링한 내용 정리
  • Storybook 으로 데모 페이지 간단 구현 및 배포
  • i18n 구현 (현재는 한국어만 지원)
  • 몇몇 상황에서 애니메이션을 더 매끄럽게 처리
    • variant="infinite" 일 때

5. 참고