본문 바로가기
Frontend2024년 7월 10일6분 읽기

Monorepo에서 공유 컴포넌트 라이브러리 구축하기

YS
김영삼
조회 586

왜 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

아직 댓글이 없습니다.
Ctrl+Enter로 등록