# Create Component

> 프로젝트의 shadcn/ui + Tailwind 패턴에 따라 새 React 컴포넌트를 생성합니다. UI 프리미티브(버튼, 다이얼로그 등) 또는 도메인 컴포넌트(ServerMap, Inspector 등)를 새로 만들 때 반드시 이 스킬을 사용하세요.

- Skill: `pinpoint-apm/create-component` (Agent Skill)
- Install (CLI): `npx skillmds@latest add pinpoint-apm/create-component`
- Raw SKILL.md: https://api.skillmd.com/api/skills/pinpoint-apm/create-component/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- Author: pinpoint-apm (https://skillmd.com/u/pinpoint-apm)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/pinpoint-apm/create-component

---


# 새 컴포넌트 생성

`$ARGUMENTS` 설명을 가진 `$0` 컴포넌트를 생성합니다.

## 1. 컴포넌트 유형 결정

### UI 프리미티브 (packages/ui/src/components/ui/)
재사용 가능한 일반 UI 컴포넌트(버튼 변형, 다이얼로그, 입력 등):
- shadcn/ui 패턴 준수: Radix UI + CVA + Tailwind
- `@pinpoint-fe/ui/src/lib/utils`의 `cn()` 사용
- 적절한 경우 Radix Slot을 통한 `asChild` 지원
- ref 전달을 위한 `React.forwardRef` 사용
- CVA(class-variance-authority)로 variants 정의

### 도메인 컴포넌트 (packages/ui/src/components/{DomainName}/)
기능별 컴포넌트(ServerMap, ErrorAnalysis 등):
- `packages/ui/src/components/` 아래 서브디렉토리 생성
- UI 프리미티브를 조합하여 구성
- 데이터 페칭에 React Query 훅 사용
- 공유 상태에 Jotai 아톰 사용
- 비동기 콘텐츠를 Suspense + ErrorBoundary로 감싸기

## 2. 기존 예시 읽기
- UI 프리미티브: `packages/ui/src/components/ui/button.tsx` 또는 `dialog.tsx` 읽기
- 도메인 컴포넌트: 타겟과 유사한 기존 도메인 컴포넌트 읽기

## 3. 컴포넌트 생성

### UI 프리미티브 템플릿
```tsx
import * as React from 'react';
import { cva, type VariantProps } from 'class-variance-authority';
import { cn } from '@pinpoint-fe/ui/src/lib/utils';

const componentVariants = cva('base-classes', {
  variants: { variant: { default: '...', outline: '...' }, size: { default: '...', sm: '...' } },
  defaultVariants: { variant: 'default', size: 'default' },
});

export interface ComponentProps
  extends React.HTMLAttributes<HTMLDivElement>,
    VariantProps<typeof componentVariants> {}

const Component = React.forwardRef<HTMLDivElement, ComponentProps>(
  ({ className, variant, size, ...props }, ref) => (
    <div ref={ref} className={cn(componentVariants({ variant, size, className }))} {...props} />
  ),
);
Component.displayName = 'Component';
export { Component, componentVariants };
```

### 도메인 컴포넌트 템플릿
```tsx
import React, { Suspense } from 'react';
import { ErrorBoundary } from 'react-error-boundary';
import { useTranslation } from 'react-i18next';
import { useAtomValue } from 'jotai';
import { cn } from '@pinpoint-fe/ui/src/lib/utils';
import { someSearchParamAtom } from '@pinpoint-fe/ui/src/atoms';
import { useGetMyData } from '@pinpoint-fe/ui/src/hooks/api';

export interface MyComponentProps {
  className?: string;
}

const MyComponentContent = ({ className }: MyComponentProps) => {
  const { t } = useTranslation();
  const searchParam = useAtomValue(someSearchParamAtom);
  const { data } = useGetMyData({ param: searchParam });

  if (!data) return null;

  return <div className={cn('...', className)}>{/* 렌더링 */}</div>;
};

export const MyComponent = ({ className }: MyComponentProps) => (
  <ErrorBoundary fallback={<div>Error</div>}>
    <Suspense fallback={<div>Loading...</div>}>
      <MyComponentContent className={className} />
    </Suspense>
  </ErrorBoundary>
);
```

## 4. 스타일링 규칙
- Tailwind CSS만 사용 — 인라인 스타일, CSS 모듈 금지
- 테마에는 CSS 변수 사용: `var(--ui-primary)`, `var(--ui-border)`
- 모든 className 합성에 `cn()` 사용
- Tailwind 브레이크포인트로 반응형 디자인

## 5. 완료
- `packages/ui/src/components/index.ts` (또는 해당 서브디렉토리의 `index.ts`)에서 컴포넌트 익스포트
- `yarn build`로 TypeScript 오류 없음 확인

