shadcn/ui: Claude Code 활용 가이드
shadcn/ui: Claude Code 활용. 실용적인 팁과 코드 예시를 포함합니다.
shadcn/uiで프로덕션品質のUIを구축する
shadcn/uiは、Radix UIとTailwind CSSをベースにした컴포넌트コレクションです。npm 패키지ではなく、コードを直接프로젝트にコピーして使う설계のため、自由に커스터마이징할 수 있습니다。Claude Code는 컴포넌트の도입から커스터마이징まで強力にサポートします。
初期セットアップ
Claude Code에프로젝트への도입を依頼합시다。
> Next.js프로젝트にshadcn/uiをセットアップして。
> ダークモード대응、日本語폰트설정も含めて。
// tailwind.config.ts
import type { Config } from "tailwindcss";
const config: Config = {
darkMode: ["class"],
content: [
"./src/**/*.{ts,tsx}",
"./components/**/*.{ts,tsx}",
],
theme: {
extend: {
fontFamily: {
sans: [
"Noto Sans JP",
"Inter",
"system-ui",
"sans-serif",
],
},
colors: {
border: "hsl(var(--border))",
background: "hsl(var(--background))",
foreground: "hsl(var(--foreground))",
primary: {
DEFAULT: "hsl(var(--primary))",
foreground: "hsl(var(--primary-foreground))",
},
secondary: {
DEFAULT: "hsl(var(--secondary))",
foreground: "hsl(var(--secondary-foreground))",
},
destructive: {
DEFAULT: "hsl(var(--destructive))",
foreground: "hsl(var(--destructive-foreground))",
},
muted: {
DEFAULT: "hsl(var(--muted))",
foreground: "hsl(var(--muted-foreground))",
},
accent: {
DEFAULT: "hsl(var(--accent))",
foreground: "hsl(var(--accent-foreground))",
},
},
borderRadius: {
lg: "var(--radius)",
md: "calc(var(--radius) - 2px)",
sm: "calc(var(--radius) - 4px)",
},
},
},
};
export default config;
데이터테이블の구축
shadcn/uiのDataTable컴포넌트を커스터마이징する例です。
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
interface DataTableProps<T> {
data: T[];
columns: ColumnDef<T>[];
searchPlaceholder?: string;
onRowClick?: (row: T) => void;
}
function DataTable<T extends { id: string }>({
data,
columns,
searchPlaceholder = "検索...",
onRowClick,
}: DataTableProps<T>) {
const [search, setSearch] = useState("");
const [sorting, setSorting] = useState<SortingState>([]);
const table = useReactTable({
data,
columns,
state: { sorting, globalFilter: search },
onSortingChange: setSorting,
onGlobalFilterChange: setSearch,
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(),
getFilteredRowModel: getFilteredRowModel(),
getPaginationRowModel: getPaginationRowModel(),
});
return (
<div>
<div className="flex items-center py-4">
<Input
placeholder={searchPlaceholder}
value={search}
onChange={(e) => setSearch(e.target.value)}
className="max-w-sm"
/>
</div>
<div className="rounded-md border">
<Table>
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => (
<TableHead key={header.id}>
{flexRender(header.column.columnDef.header, header.getContext())}
</TableHead>
))}
</TableRow>
))}
</TableHeader>
<TableBody>
{table.getRowModel().rows.map((row) => (
<TableRow
key={row.id}
onClick={() => onRowClick?.(row.original)}
className={onRowClick ? "cursor-pointer" : ""}
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</TableCell>
))}
</TableRow>
))}
</TableBody>
</Table>
</div>
<div className="flex items-center justify-end space-x-2 py-4">
<Button
variant="outline"
size="sm"
onClick={() => table.previousPage()}
disabled={!table.getCanPreviousPage()}
>
前へ
</Button>
<Button
variant="outline"
size="sm"
onClick={() => table.nextPage()}
disabled={!table.getCanNextPage()}
>
次へ
</Button>
</div>
</div>
);
}
폼통합
shadcn/uiのForm컴포넌트とReact Hook Form + Zodの통합です。
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
const profileSchema = z.object({
displayName: z.string().min(2, "2文字以上入力してください"),
email: z.string().email("有効なメールアドレスを入力してください"),
role: z.enum(["developer", "designer", "manager"]),
bio: z.string().max(200).optional(),
});
function ProfileForm() {
const form = useForm<z.infer<typeof profileSchema>>({
resolver: zodResolver(profileSchema),
});
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
<FormField
control={form.control}
name="displayName"
render={({ field }) => (
<FormItem>
<FormLabel>表示名</FormLabel>
<FormControl>
<Input placeholder="表示名を入力" {...field} />
</FormControl>
<FormDescription>公開プロフィールに表示される名前です。</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="role"
render={({ field }) => (
<FormItem>
<FormLabel>役職</FormLabel>
<Select onValueChange={field.onChange} defaultValue={field.value}>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="役職を選択" />
</SelectTrigger>
</FormControl>
<SelectContent>
<SelectItem value="developer">開発者</SelectItem>
<SelectItem value="designer">デザイナー</SelectItem>
<SelectItem value="manager">マネージャー</SelectItem>
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
<Button type="submit">保存</Button>
</form>
</Form>
);
}
カスタム테마の설계
프로젝트固有の컬러スキームを설정するパターンです。
/* globals.css */
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 222 47% 11%;
--primary: 221 83% 53%;
--primary-foreground: 210 40% 98%;
--secondary: 210 40% 96%;
--secondary-foreground: 222 47% 11%;
--muted: 210 40% 96%;
--muted-foreground: 215 16% 47%;
--accent: 210 40% 96%;
--accent-foreground: 222 47% 11%;
--destructive: 0 84% 60%;
--destructive-foreground: 210 40% 98%;
--border: 214 32% 91%;
--radius: 0.5rem;
}
.dark {
--background: 222 47% 11%;
--foreground: 210 40% 98%;
--primary: 217 91% 60%;
--primary-foreground: 222 47% 11%;
--secondary: 217 33% 17%;
--secondary-foreground: 210 40% 98%;
--muted: 217 33% 17%;
--muted-foreground: 215 20% 65%;
--accent: 217 33% 17%;
--accent-foreground: 210 40% 98%;
--destructive: 0 63% 31%;
--destructive-foreground: 210 40% 98%;
--border: 217 33% 17%;
}
}
정리
shadcn/uiはコードを프로젝트に直接取り込む설계のため、制約なく自由に커스터마이징할 수 있습니다。Claude Codeを활용すれば、컴포넌트の추가から테마설계、폼통합まで효율적으로구현가능합니다。
Radix UIの基礎はRadix UI컴포넌트활용を、폼구현의 상세 정보는React Hook Form完全攻略를 참고하세요.shadcn/ui공식 사이트도 확인해 두세요.
Related Posts
Claude Code로 리팩토링을 자동화하는 방법
Claude Code를 활용해 코드 리팩토링을 효율적으로 자동화하는 방법을 알아봅니다. 실전 프롬프트와 구체적인 리팩토링 패턴을 소개합니다.
Claude Code로 사이드 프로젝트 개발 속도를 극대화하는 방법 [예제 포함]
Claude Code를 활용해 개인 프로젝트 개발 속도를 획기적으로 높이는 방법을 알아봅니다. 실전 예제와 아이디어부터 배포까지의 워크플로를 포함합니다.
Complete CORS Configuration Guide: Claude Code 활용 가이드
complete cors configuration guide: Claude Code 활용. 실용적인 팁과 코드 예시를 포함합니다.