왜 Monorepo에서 공유 라이브러리인가?
여러 프론트엔드 앱이 동일한 디자인 시스템과 UI 컴포넌트를 사용하는 경우, Monorepo 내에 공유 패키지를 두면 코드 중복을 없애고 일관된 UI를 유지할 수 있습니다.
프로젝트 구조
my-monorepo/
├── apps/
│ ├── web/
│ └── admin/
├── packages/
│ ├── ui/
│ │ ├── src/
│ │ │ ├── Button.tsx
│ │ │ ├── Modal.tsx
│ │ │ └── index.ts
│ │ ├── package.json
│ │ └── tsconfig.json
│ ├── config-eslint/
│ └── config-typescript/
├── turbo.json
├── package.json
└── pnpm-workspace.yaml
pnpm-workspace.yaml
packages:
- 'apps/*'
- 'packages/*'
공유 UI 패키지 설정
{
"name": "@repo/ui",
"version": "0.1.0",
"private": true,
"exports": {
".": "./src/index.ts",
"./button": "./src/Button.tsx",
"./modal": "./src/Modal.tsx"
},
"peerDependencies": {
"react": "^18.0.0",
"react-dom": "^18.0.0"
}
}
컴포넌트 구현
import { forwardRef, type ButtonHTMLAttributes } from 'react';
export interface ButtonProps extends ButtonHTMLAttributes {
variant?: 'primary' | 'secondary' | 'ghost' | 'danger';
size?: 'sm' | 'md' | 'lg';
loading?: boolean;
}
export const Button = forwardRef(
({ variant = 'primary', size = 'md', loading, children, disabled, className, ...props }, ref) => {
const baseStyles = 'inline-flex items-center justify-center rounded-lg font-medium transition-colors';
const variants = {
primary: 'bg-blue-600 text-white hover:bg-blue-700',
secondary: 'bg-gray-100 text-gray-900 hover:bg-gray-200',
ghost: 'bg-transparent hover:bg-gray-100',
danger: 'bg-red-600 text-white hover:bg-red-700'
};
const sizes = { sm: 'h-8 px-3 text-sm', md: 'h-10 px-4 text-sm', lg: 'h-12 px-6 text-base' };
return (
{loading && ◠}
{children}
);
}
);
Button.displayName = 'Button';
앱에서 사용
import { Button } from '@repo/ui/button';
export default function Home() {
return (
시작하기
저장 중...
);
}
Turborepo 설정
{
"$schema": "https://turbo.build/schema.json",
"tasks": {
"build": { "dependsOn": ["^build"], "outputs": [".next/**", "dist/**"] },
"dev": { "cache": false, "persistent": true },
"lint": { "dependsOn": ["^lint"] },
"type-check": { "dependsOn": ["^type-check"] }
}
}
빌드 없는 Internal Package vs 빌드하는 방식
| 방식 | 장점 | 단점 |
|---|---|---|
| Internal (빌드 X) | 설정 간단, HMR 즉시 반영 | 소비자 앱의 빌드 도구에 의존 |
| Compiled (빌드 O) | 독립적 배포 가능, 빠른 소비 | 빌드 파이프라인 필요 |
실전 팁
- Monorepo 내부 패키지는 빌드 없이 소스를 직접 참조하는 Internal Package 방식이 개발 경험이 좋습니다.
- peerDependencies로 React를 선언하여, 앱과 공유 라이브러리가 동일한 React 인스턴스를 사용하게 하세요.
- Storybook을 packages/ui에 설정하면, 컴포넌트를 독립적으로 개발하고 문서화할 수 있습니다.
- Changesets를 도입하면 패키지 버전 관리와 변경 로그를 자동화할 수 있습니다.
댓글 0