먼저, 우리의 디자인 시스템은 총 3개의 패키지로 구성되어 있고, 디자인 시스템에 대한 문서화를 진행하게 되는 wow-docs
라는 웹 페이지가 존재한다.
웹 페이지에 대한 빌드를 진행하기 전, 패키지에 대한 빌드가 먼저 끝나야 웹 페이지의 빌드를 진행할 수 있다.
그렇기 때문에 이러한 과정들을 개발자가 일일이 기억하기 힘들기 때문에 이를 스크립트로 정리하여 편리하게 작업할 수 있도록 하려고 한다.
스크립트 파일을 설정해야 하는데, 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
}
}
}
터보레포 공식 홈페이지에서는 이와 같이 소개하고 있다.
현재 docs
패키지는 wow-ui
와 wow-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
를 번들러에 포함시키지 않는 방법도 있다.
npm i wowds-ui
위의 명령어를 통해 패키지를 설치해보자. 그리고 사용해보자!
그리고 dev 환경을 실행시켜보자!
롤업 세팅 문제인것 같다. 절대경로 세팅을 styled/css
를 인식할 수 있도록 변경하고, 번들러를 만들어 시각화를 해 보았다.
다단... wowds-ui/dist/index/js
라고 export한 파일이 한번에 번들러에 포함되어 있다!
우리가 사용한 Button
에 대한 파일만이 번들러에 포함되어 있어야 하는데, 트리쉐이킹이 잘 안된 모양이었던 것 같다.
현재 롤업 세팅에서 코드 스플리팅이 제대로 되지 않는 것 같아 보였다. 그 이유는 빌드된 모든 컴포넌트의 파일이 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와 호환되지 않는 롤업 세팅을 사용하게 된다면 어떤 곳은 지원이 되고, 어떤 곳은 지원이 되지 않는 문제가 생기게 될 것이다.
우리의 라이브러리가 갖고 있는 공식적인 문제점은 배럴 파일을 사용하고 있다는 것이다. (배럴 파일은 아래 참고)
// 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 라이브러리를 어떻게 도입하였고, 어떤 방법으로 배포하였는지 논의하였던 기록에 대해 다루고자 한다.