← スキル一覧に戻る

component-dev
by hhj4861
⭐ 0🍴 0📅 2026年1月14日
SKILL.md
name: component-dev description: React 컴포넌트 개발 스킬. 컴포넌트 템플릿, 패턴, 작성 규칙을 제공한다.
Component Development Skill
React 컴포넌트 개발을 위한 템플릿과 패턴을 제공합니다.
컴포넌트 유형별 템플릿
1. 기본 컴포넌트 (Simple)
단순한 wrapper 컴포넌트에 사용:
import * as React from "react";
import { cn } from "../../lib/utils";
export interface CardProps extends React.HTMLAttributes<HTMLDivElement> {}
function Card(
{ className, ...props }: CardProps,
ref: React.ForwardedRef<HTMLDivElement>
) {
return (
<div
ref={ref}
className={cn("rounded-lg border bg-card text-card-foreground", className)}
{...props}
/>
);
}
Card.displayName = "Card";
export const ForwardedCard = React.forwardRef(Card);
export { ForwardedCard as Card };
2. Variant 컴포넌트 (CVA)
여러 스타일 변형이 필요한 컴포넌트:
import * as React from "react";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "../../lib/utils";
const buttonVariants = cva(
"inline-flex items-center justify-center rounded-md font-medium transition-colors focus-visible:outline-none focus-visible:ring-2",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground hover:bg-primary/90",
destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
ghost: "hover:bg-accent hover:text-accent-foreground",
},
size: {
sm: "h-9 px-3 text-sm",
md: "h-10 px-4 py-2",
lg: "h-11 px-8 text-lg",
icon: "h-10 w-10",
},
},
defaultVariants: {
variant: "default",
size: "md",
},
}
);
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
loading?: boolean;
}
function Button(
{ className, variant, size, loading, disabled, children, ...props }: ButtonProps,
ref: React.ForwardedRef<HTMLButtonElement>
) {
return (
<button
ref={ref}
className={cn(buttonVariants({ variant, size, className }))}
disabled={disabled || loading}
{...props}
>
{loading ? <span className="animate-spin mr-2">...</span> : null}
{children}
</button>
);
}
Button.displayName = "Button";
export const ForwardedButton = React.forwardRef(Button);
export { ForwardedButton as Button, buttonVariants };
3. Radix 기반 컴포넌트
Radix UI primitives를 wrapping:
import * as React from "react";
import * as DialogPrimitive from "@radix-ui/react-dialog";
import { cn } from "../../lib/utils";
const Dialog = DialogPrimitive.Root;
const DialogTrigger = DialogPrimitive.Trigger;
const DialogPortal = DialogPrimitive.Portal;
const DialogClose = DialogPrimitive.Close;
function DialogOverlay(
{ className, ...props }: React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>,
ref: React.ForwardedRef<React.ElementRef<typeof DialogPrimitive.Overlay>>
) {
return (
<DialogPrimitive.Overlay
ref={ref}
className={cn(
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out",
className
)}
{...props}
/>
);
}
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
const ForwardedDialogOverlay = React.forwardRef(DialogOverlay);
function DialogContent(
{ className, children, ...props }: React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>,
ref: React.ForwardedRef<React.ElementRef<typeof DialogPrimitive.Content>>
) {
return (
<DialogPortal>
<ForwardedDialogOverlay />
<DialogPrimitive.Content
ref={ref}
className={cn(
"fixed left-[50%] top-[50%] z-50 translate-x-[-50%] translate-y-[-50%]",
"w-full max-w-lg rounded-lg border bg-background p-6 shadow-lg",
className
)}
{...props}
>
{children}
</DialogPrimitive.Content>
</DialogPortal>
);
}
DialogContent.displayName = DialogPrimitive.Content.displayName;
const ForwardedDialogContent = React.forwardRef(DialogContent);
export {
Dialog,
DialogTrigger,
DialogPortal,
DialogClose,
ForwardedDialogOverlay as DialogOverlay,
ForwardedDialogContent as DialogContent,
};
4. Compound 컴포넌트
여러 하위 컴포넌트로 구성:
import * as React from "react";
import { cn } from "../../lib/utils";
// Context
interface TabsContextValue {
value: string;
onValueChange: (value: string) => void;
}
const TabsContext = React.createContext<TabsContextValue | null>(null);
function useTabs() {
const context = React.useContext(TabsContext);
if (!context) throw new Error("useTabs must be used within Tabs");
return context;
}
// Root
interface TabsProps {
value: string;
onValueChange: (value: string) => void;
children: React.ReactNode;
className?: string;
}
function Tabs({ value, onValueChange, children, className }: TabsProps) {
return (
<TabsContext.Provider value={{ value, onValueChange }}>
<div className={cn("w-full", className)}>{children}</div>
</TabsContext.Provider>
);
}
// List
interface TabsListProps extends React.HTMLAttributes<HTMLDivElement> {}
function TabsList({ className, ...props }: TabsListProps) {
return (
<div
className={cn("inline-flex h-10 items-center justify-center rounded-md bg-muted p-1", className)}
{...props}
/>
);
}
// Trigger
interface TabsTriggerProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
value: string;
}
function TabsTrigger({ value, className, ...props }: TabsTriggerProps) {
const { value: selectedValue, onValueChange } = useTabs();
const isSelected = selectedValue === value;
return (
<button
className={cn(
"inline-flex items-center justify-center px-3 py-1.5 text-sm font-medium transition-all",
isSelected && "bg-background text-foreground shadow-sm",
className
)}
onClick={() => onValueChange(value)}
{...props}
/>
);
}
// Content
interface TabsContentProps extends React.HTMLAttributes<HTMLDivElement> {
value: string;
}
function TabsContent({ value, className, ...props }: TabsContentProps) {
const { value: selectedValue } = useTabs();
if (selectedValue !== value) return null;
return <div className={cn("mt-2", className)} {...props} />;
}
export { Tabs, TabsList, TabsTrigger, TabsContent };
Import 패턴
필수 import
import * as React from "react";
import { cn } from "../../lib/utils";
CVA 사용 시
import { cva, type VariantProps } from "class-variance-authority";
Radix 사용 시
import * as ComponentPrimitive from "@radix-ui/react-component";
체크리스트
[ ] Props 인터페이스 export (ComponentNameProps)
[ ] forwardRef 사용 (DOM 요소 반환 시)
[ ] displayName 설정
[ ] cn() 유틸리티로 className 병합
[ ] 기존 HTML attributes 확장 (extends React.HTMLAttributes<>)
[ ] Breaking change 없음
スコア
総合スコア
50/100
リポジトリの品質指標に基づく評価
✓SKILL.md
SKILL.mdファイルが含まれている
+20
○LICENSE
ライセンスが設定されている
0/10
○説明文
100文字以上の説明がある
0/10
○人気
GitHub Stars 100以上
0/15
○最近の活動
3ヶ月以内に更新がある
0/10
○フォーク
10回以上フォークされている
0/5
✓Issue管理
オープンIssueが50未満
+5
✓言語
プログラミング言語が設定されている
+5
○タグ
1つ以上のタグが設定されている
0/5
レビュー
💬
レビュー機能は近日公開予定です