UI library

UI Library

코드의 재사용성을 향상시키고 팀간의 협업을 간소화하여, 제품의 UI를 빠르게 구축하는데 도움됨

CSS Library

  • 하나의 서비스에서 여러 스타일 라이브러리를 사용하면, 번들 사이즈나, 컨텍스트 측면에서 번거로운 점이 큼

  • 모든 팀, 서비스가 동일한 스타일 라이브러리를 사용한다하면 문제 없음

  • 서버 컴포넌트를 사용해야하는 팀이나 서비스라면 Emotion 같이 런타임에서 스타일을 생성해주는 라이브러리에는 제약이 생김 -> 스타일 라이브러리가 개발 환경에 제약을 두는 문제 발생

  • 가장 이상적인 방법은 UI 라이브러리의 컴포넌트를 별도의 CSS로 제공해주는것

    • CSS는 어떤 스타일 라이브러리를 쓰든 제약이 없다.

    • 단, CSS 만을 사용해서 만드는건 비효율적이고 불편함이 큼

    • 제로 런타임 기반의 CSS 라이브러리를 사용하는 편이 가장 좋음 -> Vanila Extract

Vanila Extract

  • 빌드 단계에서 CSS 파일로 변환 및 중복 코드 제거

  • Typescript로 전처리

  • UI 라이브러리를 만들 때 사용하면 사용하는 서비스에서 어떤 스타일 라이브러리를 쓰든간에 문제 없다.

패키지 구조

  • 모든 UI 컴포넌트를 패키징해서 배포 vs 컴포넌트 별로 패키징해서 배포

  • 어느정도 완성된 라이브러리의 경우 문제 없지만, 초기 단계에서 하나씩 만들어나가게 되면 문제가 발생하게됨

    • A 컴포넌트를 수정한 배포 사이클에 B 컴포넌트도 함께 추가됐는데, A에 이슈가 생겨 이전 버전으로 롤백해야 하는 상황이 되면 B까지 함께 롤백되어 난감한 상황이 생김

    • 자주 버전 업데이트를 해줘야하는 번거로움

    • 라이브러리를 사용하는 입장에서 버전업을 할 때 어떤 문제가 발생할지 모르기 때문에 두려움

  • 고로, 각 컴포넌트를 별도로 패키징해서 배포하는게 좋을 수도 있음

    • 각각의 컴포넌트에 버전을 일괄적으로 업데이트 할 때 사용하는 서비스에서 각 컴포넌트 패키지를 업데이트 해줘야함 -> Dependency bot 으로 대응 가능

모노레포

여러 패키지를 하나의 레포지토리에서 관리하는 방법

멀티 레포의 문제

  • 신규 프로젝트를 만들 때 생성 비용이 큼

    • 프로젝트의 구성을 세팅하는데 비용이 발생함

    • Typescript, ESLint, CI/CD, bundler 등

  • 코드를 변경하거나 동일한 이슈를 수정하기 위해 각각의 레포에 대응해줘야함

  • 각 프로젝트별로 설정등이 상이하기 쉬워서 개발자 경험이 일관적이지 못하게됨

  • 프로젝트간 코드와 컨텍스트 관리가 어려움

모노 레포의 장점

  • 신규 프로젝트를 만들 때 생성 비용이 적음

  • 코드 변경 & 동일 이슈에 대해 일괄적으로 적용 가능

  • 설정등을 한 곳에서 관리 -> 일관된 DX

  • 프로젝트간 코드, 컨텍스트 관리하기 쉬움

Packages

Services에서 사용되는 라이브러리들을 관리하는 곳

  • themes, ui component, config, utils 등

esbuild-config
// packages/esbuild-config/index.js
const { build } = require('esbuild');

const run = ({
  entryPoints = ['src/index.ts'],
  pkg,
  config = {},
}) => {
  const dev = process.argv.includes("--dev");
  const minify = !dev;

  const watch = process.argv.includes("--watch");

  const external = Object.keys({
    ...pkg.dependencies,
    ...pkg.peerDependencies,
  });

  const baseConfig = {
    entryPoints,
    bundle: true,
    minify,
    sourcemap: true,
    outdir: "dist",
    target: "es2019",
    watch,
    external,
    ...config
  };

  Promise.all([
    build({
      ...baseConfig,
      format: "esm",
    }),
    build({
      ...baseConfig,
      format: "cjs",
      outExtension: {
        ".js": ".cjs",
      },
    }),
  ]).catch(() => {
    console.error("Build failed");
    process.exit(1);
  });
};

module.exports = run;
// packages/esbuild-config/package.json
{
  "name": "@[service-name]/esbuild-config",
  "packageManager": "yarn@3.6.3",
  "main": "index.js",
  "files": [
    "index.js"
  ],
  "dependencies": {
    "esbuild": "0.16.17"
  }
}
// packages/ui/button/build.js
import run from '@[service-name]/esbuild-config';
import pkg from './package.json' assert { type: 'json' };
import { vanillaExtractPlugin } from '@vanilla-extract/esbuild-plugin';

const config = {
  plugins: [vanillaExtractPlugin()],
};

run({
  pkg,
  config,
});
// packages/ui/button/package.json
{
  "name": "@[service-name]/react-components-button",
  "version": "0.0.1",
  "type": "module",
  "main": "dist/index.js",
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "import": "./dist/index.js",
      "require": "./dist/index.cjs"
    },
    "./style.css": "./dist/index.css"
  },
  "scripts": {
    "build": "yarn clean && yarn build:js && yarn build:type && yarn build:css",
    "build:js": "node build.js",
    "build:type": "yarn tsc --emitDeclarationOnly",
    "clean": "rm -rf dist",
    "dev": "yarn build:js --watch && yarn build:type --watch"
  },
  "devDependencies": {
    "@[service-name]/esbuild-config": "workspace:^",
    "@[service-name]/themes": "workspace:^",
    "@types/react": "^18.2.21",
    "@types/react-dom": "^18.2.7",
    "@vanilla-extract/css": "^1.13.0",
    "@vanilla-extract/dynamic": "^2.0.3",
    "@vanilla-extract/esbuild-plugin": "^2.3.0",
    "@vanilla-extract/recipes": "^0.5.0",
    "@vanilla-extract/sprinkles": "^1.6.1",
    "autoprefixer": "^10.4.15",
    "postcss": "^8.4.29",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "typescript": "^5.2.2"
  },
  "peerDependencies": {
    "@[service-name]/themes": "workspace:^",
    "react": "*"
  },
  "dependencies": {
    "clsx": "^2.0.0"
  }
}

Services

제품을 관리하는 곳

  • Storybook, Docs, 웹 앱 빌더 서비스 등

Last updated