Rollup을 이용하여 컴포넌트 라이브러리 만들기 ! (React, Ts, Sass, Storybook)
오랜만에 작성하는 글..!
(사실 쓰다가 만 비공개 글이 10개는 넘지만...)
이번 글에선 Rollup을 이용해 커스텀 컴포넌트 라이브러리를 만들어보는 토이 프로젝트를 작성해볼 것이다 !!
rollup을 왜 사용하는가에 대한 질문이 있을 수 있는데,
rollup은 webpack과 같은 번들러지만 웹팩과 다르게 esm빌드가 가능해서
라이브러리/패키지를 만들 때 유용하다
그리고 웹팩보다는 빌드 설정이 좀 더 간편한 편이기도 하고.. 중복 제거에 특화된 번들러다.
자 그러면 개발 환경 세팅을 해주자 !
1. 개발 환경 세팅
1) 필요 모듈 설치
React, Ts, Sass, Storybook, Rollup 을 설치 해줄 것이다
npm i -D react typescript @types/react sass postcss rollup
npx sb init // storybook 설치
storybook을 실행해보면...
npm run storybook
위와 같은 화면이 나오는 것을 잘 확인할 수 있다.
+ tsconfig 세팅
tsc --init
tsconfig은 아래와 같이 세팅 해주자.
{
"compilerOptions": {
"target": "es5",
"module": "esnext",
"jsx": "react",
"sourceMap": true,
"outDir": "dist",
"strict": true,
"moduleResolution": "node",
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
},
"include": [
"src/**/*"
],
"exclude": [
"node_modules",
"dist",
"src/stories/**",
]
}
2) custom-alias 세팅
추가로, 프로젝트 내에서 절대경로 사용을 위해 custom-alias를 세팅해보자.
개인적으로 import 할때 ../../../ 이런식으로 나오는거 불편해서..
딱히 필요하지 않으신 분은 생략하셔도 됨
tsconfig의 conpilerOptions에서 path를 설정해주자
// tsconfig.json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"],
}
}
}
ts에서 설정을 해줘도 storybook은 해당 path를 모르기에 storybook에서도 세팅해줘야 함
.storybook/main.ts 에서 아래 설정을 해준다.
const path = require('path');
module.exports = {
...(생략),
webpackFinal: async (config, { configType }) => {
if (config?.resolve?.alias) {
config.resolve.alias = {
...config.resolve.alias,
"@": path.resolve(__dirname, "../src"),
}
}
return config
}
}
그러면 alias 세팅 끗
3) 디렉토리 구조 변경
그리고, 프로젝트 디렉토리 구조를 우리의 프로젝트 특성에 맞게 변경해줄 것이다.
(아래와 같은 형태)
stories/ 내부에 있는 파일(Button만)을 src/components/ 에 옮겨주고 storeis폴더는 따로 만들어 두었다.
그리고 각 컴포넌트 파일 내부에는 index.ts를 만들어 주었는데(default export할 용도) 안 만들어도 무방하다.
// 각 컴포넌트 폴더 내부 index.ts 형태
import {Button} from "@/components/Button/Button";
export default Button;
scss로 작업할 예정이기 때문에 각 컴포넌트 css확장자를 scss로 바꿔주었고,
assets폴더는 이미지 파일 들어갈 용도로 폴더 위치만 변경해주었다.
그 다음 src 아래에 index.ts , index.scss 파일을 생성해준다.
- index.ts : 빌드할 컴포넌트 파일을 모아둔 곳
- index.scss : 글로벌하게 적용할 css를 모아둔 곳
index.ts에는 아래와 같이 작성해주자.
import "@/index.scss"
import Button from "@/components/Button";
export {Button};
index.scss는 .storybook/preview.ts에 import 해주면 전체적으로 적용된 모습을 확인할 수있다.
import type { Preview } from "@storybook/react";
import "@/index.scss" // 해당 파일 추가해준다
const preview: Preview = {
parameters: {
actions: { argTypesRegex: "^on[A-Z].*" },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
},
};
export default preview;
그리고 .storybook/main.ts에서 storeis경로를 바꿔줘야된다. (디렉토리 구조 변경했기 때문에)
또한 scss파일을 storybook에서 처리하기 위해 모듈 설치가 필요하다.(아래 링크 참고)
https://storybook.js.org/addons/storybook-addon-sass-postcss
.storybook/main.ts
import type { StorybookConfig } from "@storybook/react-webpack5";
const path = require('path');
const config: StorybookConfig = {
stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"],
addons: [
"@storybook/addon-links",
"@storybook/addon-essentials",
"@storybook/addon-interactions",
{
name: 'storybook-addon-sass-postcss',
options: {
postcssLoaderOptions: {
implementation: require('postcss'),
},
},
},
],
framework: {
name: "@storybook/react-webpack5",
options: {},
},
docs: {
autodocs: "tag",
},
webpackFinal: async (config, { configType }) => {
if (config?.resolve?.alias) {
config.resolve.alias = {
...config.resolve.alias,
"@": path.resolve(__dirname, "../src"),
}
}
return config
}
};
export default config;
여기까지 했으면, storybook을 실행해서 잘 되는지 확인 !
2. rollup config 작성
자 그러면, 우리의 라이브러리를 배포할 번들러로서 rollup 설정파일을 작성해보자
1) rollup 플러그인 설치
- rollup-plugin-peer-deps-external : peerDependencies번들을 자동으로 외부화 시켜주는 플러그인
- @rollup/plugin-node-resolve : node-modules를 찾는 플러그인
- @rollup/plugin-commonjs: Commonjs 파일을 es6으로 변환시켜주는 플러그
- @rollup/plugin-typescript: typescript를 해석하기 위한 플러그인
- rollup-plugin-postcss : css/sass파일을 해석하기 위한 플러그인
- postcss-url : css내 image url를 data url로 변환하기 위한 플러그인
- @rollup/plugin-alias : custom alias를 처리해주는 플러그인
- rollup-plugin-delete : 파일/폴더 삭제해주는 플러그인(빌드할 때 dist 내부 비우기 위해 사용)
npm i -D rollup-plugin-peer-deps-external @rollup/plugin-node-resolve @rollup/plugin-commonjs @rollup/plugin-typescript rollup-plugin-postcss @rollup/plugin-alias rollup-plugin-delete postcss-url
2) rollup.config.mjs
먼저 package.json에 아래 내용을 추가한다.
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
script에는 rollup을 실행하는 명령어를 추가한다.
"scripts": {
"build": "rollup -c",
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build"
},
rollup.config.mjs를 루트 디렉토리에 생성해주자
rollup.config.mjs
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import typescript from '@rollup/plugin-typescript';
import external from 'rollup-plugin-peer-deps-external';
import postcss from 'rollup-plugin-postcss'
import url from 'postcss-url';
import alias from "@rollup/plugin-alias";
import del from "rollup-plugin-delete";
import path from "path";
import packageJson from './package.json' assert {type: 'json'};
const __dirname = path.resolve();
export default [
{
input: 'src/index.ts',
output: [
{
file: packageJson.main,
format: 'cjs',
sourcemap: true,
name: 'components-lib'
},
{
file: packageJson.module,
format: 'esm',
sourcemap: true
}
],
plugins: [
del({targets: 'dist/'}),
external(),
alias({ entries: [{find: '@', replacement: path.resolve(__dirname, "./src")}]}),
resolve(),
commonjs(),
typescript({ tsconfig: './tsconfig.json' }),
postcss({
plugins: [
url({
url: "inline",
}),
],
}),
]
},
]
그 후 npm run build를 터미널에 입력해주면 ?
dist 폴더 하위에 cjs, esm으로 나누어서 빌드된 결과를 확인할 수 있다.
2-1) 타입 파일 제공(d.ts)
여기에서 추가로, ts를 이용하는 사용자들을 위해 타입 파일(d.ts)을 제공할 것이다.
먼저, tsconfig에 몇가지 설정을 추가해야한다.
"declaration": true,
"declarationDir": "types",
"emitDeclarationOnly": true,
그리고, rollup-plugin-dts를 설치한다.
npm i -D rollup-plugin-dts
그 다음, rollup.config.mjs에 dts을 빌드하는 코드를 추가해준다.
{
input: 'dist/esm/types/index.d.ts',
output: [{ file: 'dist/index.d.ts', format: "esm" }],
external: [/\.scss$/],
plugins: [
dts(),
alias({ entries: [{find: '@', replacement: path.resolve( "dist/esm/types")}]}),
],
},
자 그럼 빌드를 해보자 !!!!
npm run build
위와 같은 형태로 잘 나오는 것을 확인 할 수 있다.
3. npm에 올리기
우리가 만든 컴포넌트 라이브러리를 npm에 직접 올려보자 !
npm 계정이 없으면 회원가입해서 계정을 만들고...
npm 로그인을 해주자!
npm login
UserId, password, email, otp 인증하고 나면 로그인이 될 것이다.
내가 작성한 package.json은 아래와 같은데,
name에는 내가 배포할 패키지명이 들어가야 된다.
그리고 자신의 organazation이름/패키지 명 형태로 작성해야 에러가 나지 않는다.
{
"name": "@beni1026-dev/rollup-components-library",
"version": "1.0.1",
"description": "",
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
"private": false,
"scripts": {
"build": "rollup -c",
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build"
},
"keywords": [],
"author": "BeNI",
"license": "ISC",
"devDependencies": {
"@babel/preset-env": "^7.23.3",
"@babel/preset-react": "^7.23.3",
"@babel/preset-typescript": "^7.23.3",
"@rollup/plugin-alias": "^5.0.1",
"@rollup/plugin-commonjs": "^25.0.7",
"@rollup/plugin-node-resolve": "^15.2.3",
"@rollup/plugin-typescript": "^11.1.5",
"@storybook/addon-essentials": "^7.5.3",
"@storybook/addon-interactions": "^7.5.3",
"@storybook/addon-links": "^7.5.3",
"@storybook/blocks": "^7.5.3",
"@storybook/react": "^7.5.3",
"@storybook/react-webpack5": "^7.5.3",
"@storybook/testing-library": "^0.2.2",
"@types/react": "^18.2.38",
"postcss": "^8.4.31",
"postcss-url": "^10.1.3",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"rollup": "^4.5.2",
"rollup-plugin-delete": "^2.0.0",
"rollup-plugin-dts": "^6.1.0",
"rollup-plugin-peer-deps-external": "^2.2.4",
"rollup-plugin-postcss": "^4.0.2",
"sass": "^1.69.5",
"storybook": "^7.5.3",
"storybook-addon-sass-postcss": "^0.1.3",
"typescript": "^5.3.2"
}
}
그 다음, 배포하는 명령어 !
npm publish --access=public
하면 정상적으로 npm에 올라간 모습을 볼 수 있다. 야호!
그러면 이제 패키지를 다운받아 나만의 커스텀 라이브러리 컴포넌트를 이용해 개발할 수 있다.!
npm i @beni1026-dev/rollup-components-library
위 글에서 궁금한 점 있으시면 언제든지 댓글 남겨주세요 :)
참고 링크
https://dev.to/siddharthvenkatesh/component-library-setup-with-react-typescript-and-rollup-onj
https://blog.itcode.dev/projects/2022/06/10/react-components-library-starter