Frontend/Article

Rollup을 이용하여 컴포넌트 라이브러리 만들기 ! (React, Ts, Sass, Storybook)

BeNI 2023. 11. 26. 00:07
728x90

 

오랜만에 작성하는 글..!

(사실 쓰다가 만 비공개 글이 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-addon-sass-postcss Addon | Storybook: Frontend workshop for UI development

Storybook addon used to run the PostCSS preprocessor with Sass support against your stories.

storybook.js.org

 

.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 플러그인 설치

 

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

https://velog.io/@_junukim/Typescript-React-Rollup%EC%9C%BC%EB%A1%9C-%ED%92%80%EC%84%B8%ED%8A%B8-Component-Library%EB%A7%8C%EB%93%A4%EA%B8%B0

728x90