Monorepo에서 Tailwind CSS v4와 @repo/ui 패키지의 디자인 토큰을 PostCSS 기반으로 확장하는 방법

kiwon kim·2025년 7월 26일

Frontend

목록 보기
30/30

1. 프로젝트 구조 및 패키지 설치

travel-planning-app/
  ├─ apps/
  │   └─ web/
  │       ├─ src/
  │       │   ├─ App.tsx
  │       │   ├─ index.css
  │       │   └─ ...
  │       ├─ package.json
  │       ├─ postcss.config.js
  │       └─ ...
  └─ packages/
      └─ ui/
          ├─ src/
          │   ├─ style.css
          │   └─ ...
          ├─ package.json
          ├─ postcss.config.mjs
          └─ ...

1-1. 필수 패키지 설치

루트 또는 각 패키지에서 다음을 설치합니다.

pnpm add -w tailwindcss@^4 postcss autoprefixer

2. @repo/ui 패키지의 exports 및 스타일 구조

2-1. package.json의 exports 필드

packages/ui/package.json:

{
  "name": "@repo/ui",
  "version": "1.0.0",
  "main": "src/index.ts",
  "types": "src/index.ts",
  "style": "src/style.css",
  "exports": {
    ".": {
      "import": "./src/index.ts",
      "require": "./src/index.ts"
    },
    "./style": {
      "import": "./src/style.css",
      "default": "./src/style.css"
    }
  },
  "files": [
    "src"
  ]
}

상세 설명

  • .: ...
    • @repo/ui로 import 시(예: 컴포넌트, 유틸리티 등)
    • ESM(import)과 CJS(require) 모두 지원
  • ./style:
    • @repo/ui/style로 import 시
    • CSS 파일을 직접 노출
    • "import"와 "default" 모두 지정하여 다양한 번들러 호환성 확보
  • style: "src/style.css"
    • 일부 도구에서 자동 스타일 인식(권장)
  • files: ["src"]
    • 패키지 배포 시 src 폴더만 포함

      Tip:
      실제 배포 환경에서는 빌드 결과물(dist/style.css)로 경로를 맞추는 것이 더 안전합니다.
      개발 중에는 src/style.css로도 충분히 동작합니다.

2-2. Tailwind v4 + 디자인 토큰 스타일

packages/ui/src/style.css:

@import "tailwindcss";

/* 디자인 토큰 */
:root {
  --color-primary: #2563eb;
  --color-secondary: #64748b;
  --radius-base: 8px;
  --font-sans: 'Inter', sans-serif;
}

/* 토큰 활용 커스텀 유틸리티 */
.btn {
  /* tailwind 유틸리티와 CSS 변수 혼합 */
  padding: 0.5rem 1rem;
  border-radius: var(--radius-base);
  background: var(--color-primary);
  color: #fff;
  font-family: var(--font-sans);
  transition: background 0.2s;
}
.btn-secondary {
  background: var(--color-secondary);
}
.card {
  border-radius: var(--radius-base);
  box-shadow: 0 2px 8px rgba(0,0,0,0.06);
  background: #fff;
  padding: 1.5rem;
}

핵심:
Tailwind v4에서는 @import "tailwindcss"; 한 줄로 모든 유틸리티가 자동 적용됩니다.
커스텀 유틸리티와 디자인 토큰은 그 아래에 자유롭게 작성하면 됩니다.

2-3. PostCSS 설정

packages/ui/postcss.config.mjs:

export default {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  },
};

3. apps/web에서 @repo/ui/style 확장

3-1. @repo/ui/style import

apps/web/src/index.css:

@import '@repo/ui/style';

:root {
  --color-primary: #10b981; /* 앱 전용 색상 오버라이드 */
}

/* 앱 전용 커스텀 유틸리티 확장 */
.card {
  border: 1px solid #e5e7eb; /* tailwind border-gray-200 */
}

3-2. PostCSS 설정

apps/web/postcss.config.js:

module.exports = {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  },
};

사용하는 쪽에서 postcss.config.js가 이렇게 안되어있다면 tailwindcss가 적용이 되지 않습니다. 따라서 @repo/ui를 사용하는 쪽인 web에서도 postcss.config.js가 필요합니다

3-3. Vite에서 CSS import

apps/web/src/main.tsx:

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import './index.css'; // 반드시 import

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

4. 디자인 토큰 확장 및 커스텀

4-1. 앱에서 토큰 오버라이드

apps/web/src/index.css에서 원하는 토큰만 덮어쓰기:

:root {
  --color-primary: #10b981; /* 앱 전용 색상 */
}

4-2. 컴포넌트에서 Tailwind + 토큰 활용

apps/web/src/App.tsx:

import React from 'react';

export default function App() {
  return (
    <div className="card">
      <h1 className="text-2xl font-bold mb-4" style={{ color: 'var(--color-primary)' }}>
        여행 플래너
      </h1>
      <button className="btn">시작하기</button>
      <button className="btn btn-secondary ml-2">둘러보기</button>
      <div className="mt-6 p-4 bg-gray-50 rounded-lg">
        <p className="text-gray-600">Tailwind v4 유틸리티와 디자인 토큰이 함께 동작합니다.</p>
      </div>
    </div>
  );
}

5. 전체 코드 예시

packages/ui/package.json

{
  "name": "@repo/ui",
  "version": "1.0.0",
  "main": "src/index.ts",
  "types": "src/index.ts",
  "style": "src/style.css",
  "exports": {
    ".": {
      "import": "./src/index.ts",
      "require": "./src/index.ts"
    },
    "./style": {
      "import": "./src/style.css",
      "default": "./src/style.css"
    }
  },
  "files": [
    "src"
  ]
}

packages/ui/src/style.css

@import "tailwindcss";

:root {
  --color-primary: #2563eb;
  --color-secondary: #64748b;
  --radius-base: 8px;
  --font-sans: 'Inter', sans-serif;
}

.btn {
  padding: 0.5rem 1rem;
  border-radius: var(--radius-base);
  background: var(--color-primary);
  color: #fff;
  font-family: var(--font-sans);
  transition: background 0.2s;
}
.btn-secondary {
  background: var(--color-secondary);
}
.card {
  border-radius: var(--radius-base);
  box-shadow: 0 2px 8px rgba(0,0,0,0.06);
  background: #fff;
  padding: 1.5rem;
}

apps/web/src/index.css

@import '@repo/ui/style';

:root {
  --color-primary: #10b981; /* 앱 전용 색상 */
}

.card {
  border: 1px solid #e5e7eb; /* tailwind border-gray-200 */
}

apps/web/postcss.config.js

module.exports = {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  },
};

apps/web/src/App.tsx

import React from 'react';

export default function App() {
  return (
    <div className="card">
      <h1 className="text-2xl font-bold mb-4" style={{ color: 'var(--color-primary)' }}>
        여행 플래너
      </h1>
      <button className="btn">시작하기</button>
      <button className="btn btn-secondary ml-2">둘러보기</button>
      <div className="mt-6 p-4 bg-gray-50 rounded-lg">
        <p className="text-gray-600">Tailwind v4 유틸리티와 디자인 토큰이 함께 동작합니다.</p>
      </div>
    </div>
  );
}

6. 장점 및 확장성

  • @import "tailwindcss" 한 줄로 Tailwind v4의 모든 유틸리티가 자동 적용됩니다.
  • exports 필드@repo/ui/style을 import하여, 디자인 토큰과 커스텀 유틸리티를 손쉽게 공유할 수 있습니다.
  • tailwind.config.js 없이도 기본 프리셋과 커스텀 CSS 변수, 유틸리티를 자유롭게 확장할 수 있습니다.
  • 앱별로 토큰 오버라이드가 가능해, 브랜드/테마 확장이 매우 쉽습니다.

결론

이 방식은 Tailwind CSS v4의 최신 방식과
디자인 토큰/유틸리티의 패키지화를 결합한
현대적인 monorepo UI 확장 패턴입니다.

profile
FOR_THE_BEST_DEVELOPER

0개의 댓글