최근 turbo-repo를 흥미롭게 보며 학습하고있습니다.
yarn workspace 기능만으로 바닥부터 만들어나갈때의 불편함들이 많이 해소된 형태로 api 들이 제공되어서 DX에 매번 놀라고 있는데요
그렇다보니 크게 막히는 부분이 있지는 않았지만 조금 헷갈렸던 부분이 있어서 정리해봅니다.
큰 내용만 먼저 정의를 해보자면 다음과 같은 내용입니다.
tailwindcss를 사용할 때 모노레포 프로젝트라면 configuration을 어디서 어떻게 적용해야하는가?
https://turbo.build/repo/docs/guides/tools/storybook
turbo repo의 storybook 툴링에 대한 가이드라인을 참고해보면 한 모노레포에서 하나의 스토리북을 이용하는 것을 기본 가이드로 알려주고 있습니다.
즉 가이드라인에 따라 스토리북을 터보레포 프로젝트에 추가하고 난 뒤의 구조는 다음과 같습니다.
apps/storybook
packages/ui
이렇게 실제로 스토리북을 통해 테스트하고 싶은 ui 컴포넌트들은 packages/ui에 위치하면서 스토리북에 대한 코드는 apps/storybook에 배치하는 형태이죠
스토리북과 ui 컴포넌트의 관심사가 다르다. 라는 측면에서나 스토리북이라는 무거운 의존성이 여러 프로젝트마다 산재되는 것보다는 이런형태로 한번에 관리하는 편이 확실히 효율적이어 보이는 면이 있습니다.
따라서 이런 기조 자체에는 공감이 갑니다만 이렇게 구조가 잡히게되면 이런 상황이 발생합니다.
apps/storybook/src/button.stories.tsx
packages/ui/src/button.tsx
이렇게 버튼의 스토리북에 대한 코드와 버튼에 대한 코드가 서로 다른 패키지에 존재하게 됩니다.
이런 경우 tailwindcss를 적절히 적용하려면 어떻게 tailwind.config.ts를 작성해야하고 어떻게 css 파일을 가져와야할까요?
1. storybook 프로젝트에 tailwind를 설치하고 storybook 프로젝트에서는 css를 storybook에서 가져온다
2. package/ui 프로젝트에 tailwind를 설치하고 storybook 프로젝트에서는 css를 packages/ui에서 가져온다.
둘 중에 어떤 방법이 동작할까요?
아쉽게도 저 두 질문 모두 완전한 답은 아닙니다만
굳이 따지자면 storybook 프로젝트에서 tailwind를 설정하는 것이 옳을 확률이 높습니다.
이것은 tailwindcss의 동작방식을 떠올려보면 쉽게 이해할 수 있는 문제인데요
tailwindcss는 기본적으로 사용하지 않는 css는 생성하지 않는 특성을 가지고 있습니다.
https://tailwindcss.com/blog/just-in-time-the-next-generation-of-tailwind-css
기존에는 내부적으로 purge-css를 통해 사용되지 않은 css들을 삭제하는 형태로 동작했지만
JIT 엔진을 사용하는 tailwindcss를 이용하게되면 실제로 사용하는 css만 생성하고 있답니다.
그런데 그렇다면 어떤 css가 사용되지 않는 css라고 판단할 수 있는걸까요?
그 답은 tailwind.config.ts에 어느정도 드러나있습니다.
import type { Config } from "tailwindcss";
const config: Config = {
content: [
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
"./components/**/*.{js,ts,jsx,tsx,mdx}",
"./app/**/*.{js,ts,jsx,tsx,mdx}",
],
theme: {
extend: {
colors: {
background: "var(--background)",
foreground: "var(--foreground)",
},
fontFamily: {
sans: ["var(--font-geist-sans)"],
mono: ["var(--font-geist-mono)"],
},
},
},
plugins: [],
};
export default config;
일반적으로 initialize를 끝낸 tailwindconfig는 다음과같은 형태를 띕니다.
여기에서 주목할점은 content 에 들어오는 배열들이라고 할 수 있는데 내용을 살펴보면 경로와 glob 패턴으로 작성된 string들의 모음이 들어있는것을 볼 수 있습니다.
이 content의 역할은 해당 배열의 규칙에 맞는 파일들이 tailwindcss가 적용되어야하는 파일이다. 라는 선언문과도 같은데요
즉 content안에 들어가는 규칙들을 만족하는 파일에서 사용하고 있는 클래스이름들만 실제로 css로 생성하겠다는 내용입니다.
여기까지 생각을 하고나면 무엇을 해야하는지는 어느정도 명확합니다.
"ui packages는 사실 tailwindcss 에 대한 설정 자체가 필요하지 않으며 ui packages가 사용하고있는 클래스네임을 적절하게 받아와서 css를 미리 생성해두는데에만 성공한다면 적절한 스타일링을 넣어줄 수 있다."
라는거죠
다만 tailwindcss에 대한 intelisense는 받아가면서 개발하는 것이 편하니 tailwindconfig 정도만 packages/ui에 추가해두고 실제로 css를 평가하고 주입하는 책임은 packages/ui를 사용하는 측에 넘겨주면 됩니다.
지금은 스토리북 이야기를 하고있지만 만약 다른 apps 에 들어갈 프로젝트도 마찬가지라는 것입니다.
apps/storybook/tailwind.config.ts
import type { Config } from "tailwindcss";
const config: Config = {
content: [
"./src/**/*.{js,ts,jsx,tsx,mdx}",
"./app/**/*.{js,ts,jsx,tsx,mdx}",
"../../packages/ui/src/**/*.{js,ts,jsx,tsx,mdx}",
],
};
export default config;
따라서 이렇게 해당 컴포넌트를 사용하는 입장인 패키지의 tailwind.config에서 ui 패키지에 대한 참조를 추가해주면 tailwindcss는 적절하게 해당 클래스네임이 필요로하는 css를 생성하게 됩니다.
다음과 같이 content 부분에 대한 처리를 추가하고 storybook을 열어보면 적절하게 스타일이 적용된 것을 확인할 수 있었습니다.
마치며
엄청나게 어려운 이슈는 아니었지만 해당 케이스에 대한 처리는 docs에서도 찾아볼 수 없다보니 헤매기 쉬운 문제라고 생각이 들었습니다.
읽어주셔서 감사합니다.
'frontend' 카테고리의 다른 글
storybook, chromatic 대신 vercel로 배포하기 (1) | 2024.09.29 |
---|---|
모노레포에서 Internal Packages를 관리하는 3가지 방법 (0) | 2024.06.13 |
Feature-Sliced Design을 직접 사용하면서 느낀 장점과 단점 (4) | 2024.05.29 |
변경에 자유로운 프론트엔드 코드 작성하기 (1) | 2024.05.25 |
프론트엔드 테스트 환경 설정하기 (0) | 2024.05.22 |