앞서 만든 컴포넌트를 다른 프로젝트에서 사용하려면 패키지를 만들어서 npm에 배포를 해야한다.
디자인 시스템을 배포하며 UI 컴포넌트를 패키징하는 것부터 그 컴포넌트를 다른 프로젝트에 import하여 사용하는 방법을 알아보겠다.
또한, 배포와 버전관리의 효율성을 높이기 위한 버전 자동화 방법에 대해서도 다룰 것이다.
라이브러리를 배포하려면, 웹 애플리케이션을 webpack/parcel과 같은 도구로 번들링하는 것처럼 라이브러리도 번들링을 해줘야한다. 번들링이 필수적인 것은 아니지만, 단순히 TypeScript 또는 Babel로 코드를 트랜스파일해서 등록할 수도 있다.
하지만 svg를 컴포넌트로 사용하기 위해 svgr을 사용중이라면, webpack 또는 rollup을 꼭 사용해야 한다.
본 프로젝트에서는 webpack이 아닌 rollup을 사용하는데 그 이유는, webpack은 ES Module 형태로 번들을 할 수 없기 때문이다.
webpack은 기본적으로 CommonJS 형태로 번들링을 하게 되는데, CommonJS로 번들링한 라이브러리를 다른 프로젝트에서 사용하게 되면 Tree-shaking이 지원되지 않는다. 간단히 말해서 Tree-shaking이 되지 않는다면 빌드할 때 사용하지 않는 관련 코드가 포함된다. 반대로 Tree-shaking을 하게되면 빌드할 때 사용하지 않는 관련 코드가 포함되지 않는다.
rollup 패키지 및 설정
템플릿을 확인해보면 rollup에 필요한 패키지와 rollup.config.mjs
설정파일을 확인할 수 있다.
// package.json
"devDependencies": {
"rollup": "^3.25.0",
"rollup-plugin-dts": "^5.3.0",
"rollup-plugin-peer-deps-external": "^2.2.4",
}
- rollup-plugin-dts: 여러
.d.ts
파일을 하나로 묶는다. - rollup-plugin-peer-deps-external: peerDependency로 설치된 라이브러리의 코드가 번들링된 결과에 포함되지 않고, import 구문으로 불러와서 사용할 수 있게 만들어준다.
// rollup.config.mjs
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import terser from '@rollup/plugin-terser';
import peerDepsExternal from 'rollup-plugin-peer-deps-external';
import { babel } from '@rollup/plugin-babel';
// This is required to read package.json file when
// using Native ES modules in Node.js
// https://rollupjs.org/command-line-interface/#importing-package-json
import { createRequire } from 'node:module';
const requireFile = createRequire(import.meta.url);
const packageJson = requireFile('./package.json');
export default [
{
input: 'src/index.js', // 어떤 파일부터 불러올지 정함.
output: [
{
file: packageJson.main,
format: 'cjs',
sourcemap: true,
},
{
file: packageJson.module,
format: 'esm',
exports: 'named',
sourcemap: true,
},
],
plugins: [
peerDepsExternal(), /* peerDependencies로 설치한 라이브러리들을 external 모듈로 설정
즉, 번들링된 결과에 포함시키지 않음 */
resolve({
extensions: ['.js', '.jsx'], // node_modules 에서 모듈을 불러올 수 있게 해줌. ts/tsx 파일도 불러올 수 있게 해줌
}),
commonjs(),
terser(),
babel({
extensions: ['.js', '.jsx'],
exclude: 'node_modules/**',
}), // Babel을 사용 할 수 있게 해줌
],
external: ['react', 'react-dom', '@emotion/react', '@emotion/styled'],
},
];
디자인 시스템 배포 작업하기
컴포넌트 내보내기
빌드를 하기 전에 src/index.ts 파일을 만들어준다.
디자인 시스템의 공통 도입부가 이 파일이다! 이 파일에서 모든 디자인 토큰과 컴포넌트를 export 해준다.
// src/index.ts
import { PovProvider } from './PovProvider';
import { useTheme } from './PovProvider';
import { themes } from './styles/Theme';
import * as icons from './assets/icons';
import Avatar from './components/Avatar/Avatar';
import Button from './components/Button/Button';
import Badge from './components/Badge/Badge';
export { styles, global, animation, icons };
export {
PovProvider,
Avatar,
Button,
Badge,
themes,
icons
}
추가로 개발 패키지가 필요한데, 빌드(build) 과정에서 도와줄 @babel/cli와 cross-env을 사용할 것이다.
$ npm add --dev @babel/cli cross-env
패키지 설치가 완료되었다면, 빌드 과정에서 추가시켜준다. 스크립트를 수정해주자.
// package.json
{
"scripts": {
"build": "cross-env BABEL_ENV=production babel src -d dist"
}
}
빌드 과정이 구현되었고, .babelrc.json에서 babel의 키를 찾아 다음과 같이 내용을 업데이트한다.
// .babelrc.json
"babel": {
"presets": [
[
"react-app",
{
"absoluteRuntime": false
}
]
]
}
패키지 메타데이터 추가
패키지의 사용자에게 정보를 제공하기 위해 package.json을 수정해야 한다.
가장 쉬운 방법은 npm init을 실행하는 것이다. - 이 명령어는 배포를 위한 패키지의 초기 상태를 설정한다.
$ npm init --scope=@your-npm-username
질문에 대한 답을 모두 완료했으면, 아래 사진처럼 package.json에 정보가 업데이트 된다. ✨
이렇게 하면 패키지 준비가 끝난 것이다~! 이제 npm에 최초 배포를 하도록 해보자! 🚀
Auto로 배포 관리
먼저, 버전을 자동으로 관리하기 위한 Auto 설정을 추가해주자.
Auto는 배포 관리시 일어날 수 있는 다양한 것들을 수행할 때 사용할 수 있는 커맨드 라인 툴이다. Auto 문서 사이트에서 Auto에 대해 더 알 수 있다.
$ npm i --dev auto
다음과 같은 기능을 위해 도입했다.
- 변경된 부분을 기록하는 changelog를 업데이트
- 유효한 버전의 숫자 입력
- 저장소의 commit에 있는 버전 숫자와 git tag가 연결되도록 만드는 과정을 거쳐 npm에 배포
Npm에 등록하기
GitHub 토큰과 npm 토큰 생성
Git을 통해 Auto로 버전관리를 하기 위해 Auto에서 깃허브(GitHub)와 npm과 통신을 해야한다.
GitHub 토큰 발급
먼저 깃허브에서 토큰을 발급받자.
깃허브에서 classic 토큰이 아닌 A fine-grained personal access token으로 발급받아야 한다.
Resource owner를 조직으로 바꿔주고 진행한다.
권한요청은 일단 사용하는 스킬들을 포함해서 때려박았다.
그럼 조직의 설정에서 Personal access tokens에서 Active tokens 탭에 들어가면 토큰이 활성화 되어있는 걸 확인할 수 있다.
npm에서 토큰 발급
npm에서는, 다음 URL에서 토큰을 생성할 수 있다. - https://www.npmjs.com/settings/<your-username>/tokens
이렇게 깃허브와 npm에서 각각 토큰을 발급받고 .env 파일에 추가한다. .gitignore 에 .env을 추가하는 것도 잊지말자
GH_TOKEN=""
NPM_TOKEN=""
깃허브에 라벨 생성
Auto로 가장 우선적으로 해야할 것은 깃허브에 라벨 종류를 생성하는 것이다.
이 라벨은 나중에 패키지를 수정할 때 사용하는데, auto
가 패키지 버전을 합리적으로 업데이트하고 changelog와 릴리즈 노트를 생성할 수 있다.
아래 명령어로 라벨을 생성해준다.
$ npx auto create-labels
깃허브 이슈를 확인해보면 이제 auto가 추천하는 라벨 종류가 생성된 것을 볼 수 있다.
merge 하기 전에 모든 PR시 아래 라벨 중 하나를 선택하여 태그해야 한다.
major
,minor
,patch
,skip-release
,prerelease
,internal
,documentation
Auto를 이용해 첫 배포
첫 배포를 진행하면서 changelog 생성, 커밋, 버전 업데이트, npm 배포, GitHub 릴리즈 생성의 과정을 거치게 된다.
나중에는 새로운 버전 숫자를 스크립트를 이용해서 auto
로 계산하겠지만, 처음 배포할 때는 changelog 명령어를 처음으로 발생시킨다.
$ npx auto changelog
auto changelog
명령어를 실행하면 프로젝트의 기존 커밋 기록을 분석하여 변경 사항을 정리한CHANGELOG.md
파일을 생성한다.- auto는 기본적으로 커밋 메시지와 PR 제목을 기반으로 변경 사항을 분류한다.
처음 생성된 CHANGELOG.md을 확인하고 업데이트 필요 시 수행한 후 이를 commit한다.
// CHANGELOG.md
# v0.0.1 (Tue Nov 19 2024)
- Created first version of the design system, with `Button` components.
#### Authors: 1
- DaYeonKim ([@dyeon-dev](https://github.com/dyeon-dev))
새로 생성된 CHANGELOG.md 파일을 Git의 스테이징 영역에 추가한다. 해당 변경 사항을 커밋하면서 메시지 끝에 [skip ci]
를 추가한다.
[skip ci]
사용하지 않으면 커밋->빌드->새 버전 생성->커밋.. 이런식으로 빌드와 배포의 순환이 계속된다. 때문에 자동 빌드 트리거를 방지하기 위해 명령어를 써준다.
git add CHANGELOG.md
git commit -m "Changelog for v0.0.1 [skip ci]"
버전 업데이트 및 배포
$ npm --allow-same-version version 0.0.1 -m "Bump version to: %s [skip ci]"
-> 해당 명령어는 package.json의 version 필드를 0.0.1로 설정해주고 package-lock.json도 자동으로 업데이트된다.
$ npm publish --access=public
-> 현재 프로젝트를 npm 패키지로 배포한다. --access=public 옵션을 추가하면 공개 패키지로 배포된다.
(기본적으로 조직 스코프(@your-org/package)가 있는 경우 private으로 설정되므로, 명시적으로 public으로 지정해야 한다.)
여기까지 진행되면, npm 패키지의 새로운 버전이 npm 레지스트리에 등록된다.
Auto를 사용해서 깃허브에 릴리즈를 생성
이제 GitHub의 릴리즈(Release) 페이지에서 버전을 자동으로 기록해주는 작업을 해줄 것이다.
만약 변경된 파일을 Git에 버전 태그와 함께 커밋 푸시하게 되면, 새 버전(v0.0.x)을 자동으로 생성한다.
CHANGELOG.md 내용을 기반으로 릴리즈 내용이 작성되고, 릴리즈에 대한 태그가 자동으로 추가된다.
$ git push --follow-tags origin main
$ npx run release
Npm을 확인해보면 성공적으로 첫 번째 버전(v0.0.1)이 배포되었다!!
이후에는 auto를 사용해 자동으로 버전을 계산하고 릴리즈하는 과정을 진행해준다.
Auto를 사용한 스크립트 설정
아래처럼 release 스크립트를 수정해주면 npm release를 실행할 때 (자동 생성 changelog를 사용할 때 제외하고) 자동적으로 위와 같은 모든 과정을 밟는다.
기본 브랜치에 push한 모든 commit들은 자동으로 배포된다.
{
"scripts": {
"release": "auto shipit --base-branch=main"
}
}
여기까지 해주면 디자인 시스템을 배포하기 위한 기본적인 인프라 설정을 완료한 것이다!
이제 지속적인 통합(CI)으로 어떻게 자동으로 배포할 것인지 알아볼 것이다. 🚀
자동 릴리즈 배포 설정
지속적인 통합을 위해서 GitHub Actions를 사용한다.
앞에서 언급했던 깃허브 토큰과 NPM 토큰을 안전하게 저장해야한다. 그래야 Actions가 깃허브와 NPM에 접근할 수 있다.
GitHub Secrets에 토큰 저장
GitHub Secrets은 저장소에 보안이 필요한 정보를 저장할 수 있게 한다.
깃허브 저장소 설정의 Secrets 섹션에서 New secret 버튼을 클릭하여 NPM_TOKEN
을 붙여넣어준다.
저장소에 npm 토큰을 추가하고, secrets.NPM_TOKEN으로써 이를 접근할 수 있다.
Github Actions으로 릴리즈 자동화
팀원들과 공동 작업을 하거나, 빠르게 업데이트한 내용을 패키지로 배포하기 위해 PR을 merge할 때마다, 자동적으로 디자인 시스템에 업데이트되도록 해줄 것이다.
push.yml
이라는 새로운 이름의 파일을 .github/workflows/chromatic.yml 파일과 같은 위치에 생성하고 다음과 같이 내용을 입력한다.
// .github/workflows/push.yml
# Name of our action
name: Release
# The event that will trigger the action
on:
push:
branches: [main]
# what the action will do
jobs:
release:
# The operating system it will run on
runs-on: ubuntu-latest
# This check needs to be in place to prevent a publish loop with auto and github actions
if: "!contains(github.event.head_commit.message, 'ci skip') && !contains(github.event.head_commit.message, 'skip ci')"
# The list of steps that the action will go through
steps:
- uses: actions/checkout@v2
- name: Prepare repository
run: git fetch --unshallow --tags
- name: Use Node.js 12.x
uses: actions/setup-node@v1
with:
node-version: 12.x
- name: Cache node modules
uses: actions/cache@v1
with:
path: node_modules
key: yarn-deps-${{ hashFiles('yarn.lock') }}
restore-keys: |
yarn-deps-${{ hashFiles('yarn.lock') }}
- name: Create Release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
#👇 npm token, see https://storybook.js.org/tutorials/design-systems-for-developers/react/ko/distribute/ to obtain it
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
run: |
yarn install --frozen-lockfile
yarn build
yarn release
이렇게 Github action 설정까지 해주면 main 브랜치에 PR을 merge 할 때마다 자동으로 새로운 버전이 배포되며, 추가한 라벨에 맞춰 버전 숫자도 업데이트 된다! 🎉🎉
다음 글에서는 디자인 시스템을 우리의 프로젝트에 적용시켜 사용하는 방법을 알아보도록 하겠다!
또한, CDD(Component-Driven Development)를 적용하여 프로젝트에 컴포넌트 주도 개발을 도입한 개발 방식에 대해 정리해보겠다.