개탕 IT FACTORY

디자인시스템(4).시스템 구축(라이브러리 제작) vite편 본문

Front-end

디자인시스템(4).시스템 구축(라이브러리 제작) vite편

rendar02 2024. 9. 4. 23:28
반응형

디자인시스템 구축

전 글에서 compound component와 Polymorphic component에 대해서 다뤄봤고,
추가적으로 디자인 토큰이 어떤 것인지에 대해서도 알아보았다.

이번에는 디자인시스템 라이브러리 구축을 위한 과정을 한번 다뤄볼까한다.

디자인 시스템 구축 과정

사실 Rollup으로 구축할려고했으나 vite의 강세와 더불어 내부로직으로 Rollup 설정이 가능한 vite로 빠르게 구축후 Rollup으로 포팅하는 과정을 할려고한다.

고려사항

  • 최소한의 번들 사이즈를 유지하게 만든다 (tree shacking 기능이 있는걸 사용한다)
  • storybook을 적극활용하여 컴포넌트를 관리한다
  • Typescript기반으로 개발한다
  • React 개발환경에 맞춰 개발한다 (다른 것들은 추후고려대상)

개발 구축

빠른 개발환경 구축을 위해 vite의 template를 사용하여 설정한다.
라이브러리이긴하지만 개발 내부를 볼수있게 코드베이스에서 확인이 가능하도록 react 템플릿사용

# npm 7+, '--'를 반드시 붙여주세요
npm create vite@latest rendar-design-system -- --template react-ts

# yarn
yarn create vite rendar-design-system --template react-ts

# pnpm
pnpm create vite rendar-design-system --template react-ts
  • storybook 설치

      npx storybook@latest init
    
      # 위와 같아 설치하면 스토리북은 자동으로 설치된다 
  • vite.config.ts 수정

    처음 설정되어있을시에는 아래와 같을것이다

      import { defineConfig } from "vite";
      import react from "@vitejs/plugin-react";
    
      // https://vitejs.dev/config/
      export default defineConfig({
        plugins: [react()],
      });
    

    변경해주기위해서 2개의 라이브러리 설치가 필요하다

    vite-plugin-dts: TypeScript 정의 파일을 생성해준다 ex) index.d.ts

    vite-tsconfig-paths: tsconfig.json의 경올 설정을 Vite에 반영해주는 라이브러리

    위 라이브러리 외 필요한 라이브러리는 설치해서 사용하면된다

      # yarn
      yarn add vite-plugin-dts vite-tsconfig-paths --dev
    
      # npm 
      npm install vite-plugin-dts vite-tsconfig-paths --save-dev

    그리고 아래와 같이 변경

      import { defineConfig } from "vite"; // Vite 설정을 정의하기 위한 함수
      import path from "path"; // 파일 및 디렉토리 경로 작업을 위한 Node.js 모듈
      import react from "@vitejs/plugin-react"; // React와의 통합을 위한 Vite 플러그인
      import dts from "vite-plugin-dts"; // TypeScript 정의 파일 생성을 위한 플러그인
      import tsconfigPaths from "vite-tsconfig-paths"; // tsconfig.json의 경로 설정을 Vite에 반영하기 위한 플러그인
    
      export default defineConfig({
        plugins: [
          react(), // React 플러그인 사용
          dts({
            insertTypesEntry: true, // 타입 정의 파일을 생성할 때 entry point를 추가
          }),
          tsconfigPaths(), // tsconfig.json의 paths 옵션을 Vite에서 사용할 수 있게 해줌
        ],
        build: {
          lib: {
            entry: path.resolve(__dirname, "index.ts"), // 라이브러리의 진입점 파일
            name: "rendar-design-system", // 라이브러리의 글로벌 네임스페이스
            formats: ["es", "cjs"], // 번들링 포맷: ES Module과 CommonJS
            fileName: (format) => `rendar-design-system.${format}.js`, // 생성될 파일 이름
          },
          rollupOptions: {
            external: ["react", "react-dom", "@emotion/react", "@emotion/styled", "@emotion/server"], // 번들링에서 제외할 외부 모듈
          },
          sourcemap: true, // 소스맵 생성 여부 (디버깅 용이)
          emptyOutDir: true, // 빌드 시 출력 디렉토리를 비울지 여부
        },
      });

    주석으로 설명을 해놨지만 설명을 해보자면

    build.lib : Vite의 라이브러리로 빌드할수 있게 설정하는 옵션

    entry : 라이브러리 실행 진입점

    name : 라이브러리 글로벌 네이밍

    formats : 번들링 포맷을 써준다 (현재 ES module과 CommonJS를 지원하게끔 설정)

    fileName : 빌드해서 생성될 패키지 파일명

    rollupOptions : 기존 Rollup 번들을 커스텀합니다. (Rollup 옵션과 동일)

    external : 번들링시 제외할 외부 모듈 (설치됬다고 가정한다)

    sourcemap : 소스맵 생성여부

    emptyOutDir : 빌드시 출력 디렉토리 비울지

  • package.json

    package.json은 아래와 같이 변경해준다 (주석으로 설명)

    main : 패키지 진입점

    types : typescript 타입 정의 파일 경로

    export : main 필드와 동일하기 진입 경로 설정 가능, 추가적으로 conditional exports ,subpath exports 지원

    files : 패키지 설치시 포함될 항목

      {
        "name": "rendar-design-system", // 패키지 이름
        "version": "0.0.4", // 패키지 버전
        "type": "module", // 이 패키지가 ES 모듈임을 명시
        "main": "dist/index.es.js", // 패키지의 진입점 파일 (기본적으로 ES 모듈을 사용)
        "types": "dist/index.d.ts", // TypeScript 타입 정의 파일 경로
        "exports": { // 패키지 내보내기 설정
          ".": { // 기본 경로 설정
            "import": "./dist/index.es.js", // ES 모듈 사용 시의 진입점
            "require": "./dist/index.cjs.js", // CommonJS 모듈 사용 시의 진입점
            "types": "./dist/index.d.ts" // 타입 정의 파일
          },
          "./package.json": "./package.json", // package.json 파일 경로
          "./dist/*": "./dist/*" // dist 디렉토리 내 모든 파일 접근 허용
        },
        "files": [
          "/dist" // 패키지에 포함될 파일 또는 디렉토리 목록 (여기서는 dist 디렉토리만 포함)
        ],
          //.. 이하 패키지 동일
      }
  • tsconfig.json

    typescript를 지원하기 때문에 아래와 같이 해준다

    vite설치시 기본항목은 동일하나
    include 부분에 진입점 index.ts를 추가해줘야된다

      {
        "compilerOptions": {
          "target": "ES2020", // 컴파일된 JavaScript 코드가 호환될 ECMAScript 버전을 지정 (ES2020).
          "useDefineForClassFields": true, // 클래스 필드에 대해 define 대신 ESNext 방식 사용.
          "lib": ["ES2020", "DOM", "DOM.Iterable"], // 컴파일 시 포함할 라이브러리 설정 (ES2020, DOM, DOM.Iterable).
          "module": "ESNext", // 모듈 시스템 설정 (ESNext).
          "skipLibCheck": true, // 라이브러리 파일의 타입 검사를 생략하여 컴파일 속도 향상.
    
          /* Bundler mode */
          "moduleResolution": "bundler", // 모듈 해석 방식을 번들러 모드로 설정.
          "allowImportingTsExtensions": true, // TypeScript 확장자를 포함한 import 허용.
          "resolveJsonModule": true, // JSON 파일을 모듈로서 import 허용.
          "isolatedModules": true, // 각 파일을 개별적인 모듈로 컴파일.
    
          "noEmit": true, // 출력 파일을 생성하지 않음.
          "jsx": "react-jsx", // JSX 코드의 변환 방식을 설정 (React 17 이상의 jsx 변환 방식 사용).
    
          /* Linting */
          "strict": true, // 엄격한 타입 검사 설정.
          "noUnusedLocals": true, // 사용되지 않는 지역 변수를 허용하지 않음.
          "noUnusedParameters": true, // 사용되지 않는 파라미터를 허용하지 않음.
          "noFallthroughCasesInSwitch": true, // switch 문에서 case의 fallthrough를 허용하지 않음.
    
          "outDir": "./dist", // 컴파일된 출력 파일의 디렉토리 설정.
          "rootDir": "./", // 소스 파일의 루트 디렉토리 설정.
          "paths": {
            "@/*": ["./src/*"] // 모듈 경로 별칭 설정.
          }
        },
        "include": ["index.ts", "src"], // 포함할 파일과 디렉토리 설정 (라이브러리 index 파일도 포함).
        "references": [{ "path": "./tsconfig.node.json" }] // 참조할 다른 tsconfig 파일 경로 설정.
      }
    

배포전 테스트

배포이전에 테스트를 하고 싶으면 build시킨 파일을 link 시키면된다

https://yarnpkg.com/cli/link

yarn link 
# or 
npm link 

node_modules 를 확인해보면 제작한 라이브러리 이름으로 설치가 된 것을 확인해 볼 수 있다.

테스트가 끝났을경우 아래 unlink 명령어를 통해 제거해주어야한다

yarn unlink 
# or 
npm unlink 

컴포넌트 제작하기

배포를하기 위해 간단한 버튼을 만들어볼까한다
components/Button/Button.tsx

import styled from "@emotion/styled";
import { ReactNode } from "react";

const ButtonContainer = styled.button`
  border: 1px solid gray;
  border-radius: 8px;
  padding: 20px;
`;

interface ButtonProps {
  children: ReactNode;
}

export const Button = ({ children, ...props }: ButtonProps) => {
  return <ButtonContainer {...props}>{children}</ButtonContainer>;
};

components/index.tsx

컴포넌트를 모으기 위해 export 파일들을 관리해준다

export { Button } from "./Button/Button";

배포하기

먼저 CLI를 통해서 NPM 계정에 로그인을 해봅시다

npm login

CLI에 로그인을 하였다면 배포를 진행해봅시다
유료로 계정을 결제중이라면 private를 사용해두되지만 저같이 유료결제를 하지 않았을시에는 public을 명시해줍니다.

npm publish --access=public

배포 확인하기

배포이후에는 계정으로 들어가 배포가되었는지 확인을해본다
profile → packages → 내가 만든 라이브러리

npm: rendar-design-system

아주 잘 배포가 되고 있었다.

결론

나만의 라이브러리, 나만의 디자인 시스템 구축이라는 말만해보고 실천해보지 못하였다

라이브러리는 나보다 더 나은 사람이 만드는거야 라는 생각으로 차일 피일 미루다보니 이렇게 되었는데
막상 만들고 보니 별거 아니였다

물론 시중에 나온 유명한 라이브러리는 최적화 및 여러명의 컨트리뷰터, 구글등의 유명 회사 개발자분들이 만들다보니 내부 로직이나 성능 등은 뛰어날 것이다.

하지만 해본것과 안해본것은 천지차이 아닌가? 라이브러리를 제작해본 경험만으로도 충분히 추후 회사나 나의 사이드프로젝트를 통해 라이브러리를 개발할수있다는 자신감을 가질수 있을 것 같다

이번 프로젝트는 vite 번들러를 사용했지만, 라이브러리에 최적화 되어있는 rollup으로 마이그레이션을 진행해볼까한다.
아마 구현하면서 모노레포 형식으로 vanilla-extract 버전이나 tailwind 버전등을 제작해서 여러 부분으로 제작을 해볼까한다

반응형