Panda CSS로 디자인 시스템 만들기 (2) - npm 배포, Rollup과 트리쉐이킹

김유진·2024년 5월 6일
1
post-thumbnail

터보레포로 모노레포 구성하기

turborepo의 멀티태스킹 활용하기

먼저, 우리의 디자인 시스템은 총 3개의 패키지로 구성되어 있고, 디자인 시스템에 대한 문서화를 진행하게 되는 wow-docs 라는 웹 페이지가 존재한다.
웹 페이지에 대한 빌드를 진행하기 전, 패키지에 대한 빌드가 먼저 끝나야 웹 페이지의 빌드를 진행할 수 있다.

그렇기 때문에 이러한 과정들을 개발자가 일일이 기억하기 힘들기 때문에 이를 스크립트로 정리하여 편리하게 작업할 수 있도록 하려고 한다.

  1. 각각의 패키지의 빌드 순서를 정의하고,
  2. 패키지 배포 전 각각의 패키징에 대하여 빌드를 진행하기 위하여

스크립트 파일을 설정해야 하는데, yarn workspace를 사용하였다면 아래와 같은 스크립트를 작성했어야 했다.

yarn workspaces run lint
yarn workspaces run test
yarn workspaces run build

그런데 turborepo를 사용하기 때문에 아래 명령어만 입력해도 된다.

turbo run lint test build

해당 파일은 turbo.json 인데, 순서에 대한 파이프라인을 정의한 것이다.
여기서, 프로젝트의 루트에 해당하는 의존성에 대한 모든 build가 이루어져야지만 전체 빌드가 이루어질 수 있기 때문에, dependsOn 설정을 세팅해두었다.

{
  "$schema": "https://turbo.build/schema.json",
  "globalDependencies": ["**/.env.*local"],
  "pipeline": {
    "lint": {
      "dependsOn": ["^lint"]
    },
    "build": {
      "dependsOn": ["^build"],
      "outputs": [".next/**", "!.next/cache/**"]
    },
    "dev": {
      "cache": false,
      "persistent": true
    }
  }
}

터보레포 공식 홈페이지에서는 이와 같이 소개하고 있다.

preinstall로 모듈 미리 빌드하기

현재 docs 패키지는 wow-uiwow-tokens의 빌드 파일인 dist로부터 코드를 꺼내 쓰는 방식으로 구현되어 있다.
특히, 판다 CSS로 스타일을 적용하기 위해서는 panda.config.ts에 존재하는 모듈이 미리 정의되어 있어야 하는데 이 과정에서 외부 패키지를 dist파일로부터 찾아올 수 없으니 에러를 뱉으며 pnpm install을 진행하지 못하였다.

이 문제를 해결하기 위하여 pnpm install이라는 명령어를 입력할 때, 해당 패키지가 의존하고 있는 패키지가 먼저 빌드될 수 있도록 preinstall 스크립트를 작성하였다.

  "scripts": {
	...
    "preinstall": "turbo run build"
  },

트리쉐이킹이 정말 잘 되고 있는 것일까?

우리가 배포한 wow-ui 디자인의 트리쉐이킹이 정말 잘 되고 있는 것인지 궁금하였다! 그렇기 때문에 배포한 라이브러리를 이용하여 컴포넌트를 사용하여 본 뒤, 번들러의 구성을 살펴보고자 한다.

라이브러리 의존성 관리

우리의 wow-ui 라이브러리 내에는 Next를 기반으로 움직이는 Storybook을 테스트용으로 사용하고 있다.
npm으로 배포하고 나서 보니...

띠용?! 스토리북 관련된 Dev Dependencies들이 주루룩 나열되어 있는 것을 알 수 있었다. 스토리북은 우리 팀이 개발할 때에만 사용하는 것인데 이게 맞나?! 싶어서 다른 패키지들은 어떻게 되어 있는지 주루룩 살펴보았다.

유명 UI 라이브러리인 shadcn-ui에서는 어떻게 라이브러리 관리를 하고 있는지 살펴보니 여기도 동일하게 package.json에서 관리하고 있는 패키지들이 npm의 Dependencies에서 관리되고 있는 것을 확인하였다.

우리 프로젝트에서 설치되어 있는 next는 Storybook 사용시에만 필요한 것이므로, DevDependencies로 이동시켜야 한다.
아니면, peerDependencies로 이동한 뒤, Rollup에서 peerDependencies를 번들러에 포함시키지 않는 방법도 있다.

wowds-ui 써보자!

npm i wowds-ui

위의 명령어를 통해 패키지를 설치해보자. 그리고 사용해보자!

그리고 dev 환경을 실행시켜보자!

롤업 세팅 문제인것 같다. 절대경로 세팅을 styled/css를 인식할 수 있도록 변경하고, 번들러를 만들어 시각화를 해 보았다.

다단... wowds-ui/dist/index/js 라고 export한 파일이 한번에 번들러에 포함되어 있다!
우리가 사용한 Button에 대한 파일만이 번들러에 포함되어 있어야 하는데, 트리쉐이킹이 잘 안된 모양이었던 것 같다.

Rollup 세팅을 통해 코드 스플리팅을 할 수는 없을까?

현재 롤업 세팅에서 코드 스플리팅이 제대로 되지 않는 것 같아 보였다. 그 이유는 빌드된 모든 컴포넌트의 파일이 index.js에 포함되어 있고, index에 존재하는 파일 모듈별로 관리가 되고 있지 않았기 때문이다.

롤업 세팅을 아래와 같이 변경하였다.

(기존)

export default {
  input: "./src/components/index.ts",
  output: [
    {
      format: "es",
      file:"./dist/index.js",
    },
    {
      file: "./dist/index.cjs",
      format: "cjs",
    },
  ],
  ...

(변경)

export default {
  input: "./src/components/index.ts",
  output: [
    {
      format: "esm",
      dir: "dist",
      preserveModules: true,
      preserveModulesRoot: "src/components",
      sourcemap: true,
    },
    {
      file: "./dist/index.cjs",
      format: "cjs",
    },
  ],

preserveModules를 해야 index.js 파일로 모아지지 않고, 각자의 모듈을 살려 import할 수 있음을 알게 되었고, 이대로 세팅을 변경해보았다.

짜잔! 내가 사용한 Button의 코드만 포함된 것을 확인할 수 있었다.
이렇게 라이브러리에서 불러온 코드의 번들러가 잘 찢겨져 나오는 것을 확인하니, 디자인 시스템이 index.js에 묶여서 무겁게 올라가지 않아 초기 로드 속도를 더욱 줄일 수 있을 것이라는 것을 알게 되었다.

잠깐만, cjs 방식은..?

우리의 라이브러리는 cjs도 지원하는 라이브러이다. 하지만 cjs와 호환되지 않는 롤업 세팅을 사용하게 된다면 어떤 곳은 지원이 되고, 어떤 곳은 지원이 되지 않는 문제가 생기게 될 것이다.

우리의 라이브러리가 갖고 있는 공식적인 문제점은 배럴 파일을 사용하고 있다는 것이다. (배럴 파일은 아래 참고)

// src/utils/index.ts
export * from './color.js'
export * from './dom.js'
export * from './slash.js'

이렇게 파일을 내보내고 있으니, 당연히 번들러가 생성될 때, index.js 파일 하나로 있는 그대로 빌드되는 것이었다.

물론 위의 방법처럼, preserveModules설정을 활성화하면 되겠지만, index.js파일로만을 진입점으로 설정하는 것이 아니라 각 파일에 따른 여러 진입점을 생성하여 주면 해결할 수 있는 문제인 것이다.
실제로, vite 공식문서에서는 성능을 개선하기 위한 방법 중의 하나로, 배럴 파일을 피하는 것을 권하고 있다.

여러 진입점 제공하기

참고할 수 있는 롤업 공식문서

export default {
  input: { Box: "./src/components/Box", Button: "./src/components/Button" },
  output: [
    {
      format: "esm",
      dir: "dist",
      entryFileNames: "[name].js",
    },
    {
      format: "cjs",
      dir: "dist",
      entryFileNames: "[name].cjs",
    },
  ],
  ...

이렇게 진입점을 제공하게 된다면, 각자의 파일을 접근할 수 있는 경로가 생성된다.
또한, 빌드 결과는 각각의 폴더 구조를 유지하면서, js, cjs 형식의 파일이 생성된다.

다음 글에서는 Panda CSS의 작동 원리를 바탕으로, 우리 팀에서는 디자인 시스템에 해당 CSS 라이브러리를 어떻게 도입하였고, 어떤 방법으로 배포하였는지 논의하였던 기록에 대해 다루고자 한다.

0개의 댓글

관련 채용 정보