| name | ui-designer |
| description | This skill generates production-ready React/Next.js UI code using shadcn/studio MCP server. Use this skill after plan-developer completes feature specifications to create UI components with shadcn blocks. Triggers on requests like "UI ์ฝ๋ ์์ฑํด์ค", "ํ๋ฉด ๋ง๋ค์ด์ค", "shadcn์ผ๋ก ๋์์ธํด์ค", "/cui", "/iui", "/rui", or when transitioning from plan-developer skill. |
UI Designer
Overview
UI ์ฝ๋ ์์ฑ ์คํฌ:
- /cui: ๊ธฐ์กด shadcn studio ๋ธ๋ก์ผ๋ก ์ ํ๋ฉด ์์ฑ
- /iui: ๋ฒ ์คํธ ํ๋ํฐ์ค ๊ธฐ๋ฐ ์ฐฝ์์ ๋์์ธ (Pro only)
- /rui: ๊ธฐ์กด ์ปดํฌ๋ํธ ์
๋ฐ์ดํธ/๊ฐ์
Output: Production-ready React/Next.js code using shadcn/ui components.
Reference Files
| ํ์ผ | ์ฉ๋ |
|---|
references/component-reuse.md | CRITICAL ์ปดํฌ๋ํธ ์ฌ์ฌ์ฉ ๋ถ์ ๊ฐ์ด๋ |
references/consistency-rules.md | CRITICAL UI ์ผ๊ด์ฑ ํ์ ๊ท์น |
references/component-templates.md | ์ฝ๋ ํ
ํ๋ฆฟ (List, Form, Dialog) |
references/agent-integration.md | ์์ด์ ํธ ํ์ฉ + Vercel Best Practices |
references/visual-verification.md | ์๊ฐ์ ๊ฒ์ฆ ๊ฐ์ด๋ |
Workflow
| Phase | ์ค๋ช
|
|---|
| 0 | ํ๋ก์ ํธ ๊ตฌ์กฐ + ์ปดํฌ๋ํธ ์ฌ์ฌ์ฉ ๋ถ์ |
| 1 | Planning Document Intake |
| 2 | Block Selection & Collection |
| 3 | Code Generation |
| 4 | ๋ฐ๋ณต ํจํด ์ปดํฌ๋ํธํ |
| 5 | shadcn Studio Workflows |
| 6 | Validation Schema |
| 7-8 | Test Planning & Visual Verification |
| 9 | UI Consistency Check |
Phase 0: Project Structure & Component Analysis (CRITICAL)
์์ธ: references/component-reuse.md
ํ์ ์ํ ์ฌํญ
feature-dev:code-explorer ์์ด์ ํธ๋ก ๊ตฌ์กฐ ํ์
- ๊ธฐ์กด ์ปดํฌ๋ํธ ํ์ (์ฌ์ฌ์ฉ ์ฐ์ )
- ๊ธฐ์กด import ํจํด, ๋ช
๋ช
๊ท์น ์ค์
- ๋์ผ ๋๋ฉ์ธ ์ ์ฌ ํ์ด์ง ์คํ์ผ ๋ณต์ฌ (Shift Left - ์๋ ์ฐธ์กฐ)
์ปดํฌ๋ํธ ํ์ ์ฐ์ ์์
packages/web-platform/src/components/ - ๊ณตํต
apps/[์ฑ๋ช
]/src/components/ - ์ฑ ๋ ๋ฒจ
apps/[์ฑ๋ช
]/src/[๋๋ฉ์ธ]/components/ - ๋๋ฉ์ธ
CRITICAL: ๊ธฐ์กด ์ปดํฌ๋ํธ๊ฐ ์กด์ฌํ๋ฉด ๋ฐ๋์ ์ฌ์ฌ์ฉ. ์ ๊ท ์์ฑ์ ๊ธฐ์กด์ด ์์ ๋๋ง.
๋์ผ ๋๋ฉ์ธ ์คํ์ผ ์ฐธ์กฐ (CRITICAL - Shift Left)
๋ชฉ์ : ui-improver์์ ๋ฐ๋ณต ๋ฐ๊ฒฌ๋๋ "๋๋ฉ์ธ ๋ด ์คํ์ผ ๋ถ์ผ์น"๋ฅผ ์ฝ๋ ์์ฑ ์ ์ฌ์ ๋ฐฉ์ง
ํ์ ์ ์ฐจ:
- ์ ์ฌ ํ์ด์ง ๊ฒ์: ๊ฐ์ ์ฑ/๋๋ฉ์ธ ํด๋์์ ์ด๋ฏธ ๊ตฌํ๋ ํ์ด์ง๋ฅผ Glob์ผ๋ก ์ฐพ๊ธฐ
- ์คํ์ผ ํจํด ์ถ์ถ: ํด๋น ํ์ด์ง์ ๋ค์ ์์๋ฅผ ์ฝ๊ณ ๋์ผํ๊ฒ ์ ์ฉ
- ํ
์ด๋ธ ์ธ๋ก ๊ตฌ๋ถ์ (
border-r ํจํด)
- ๋ฑ์ง ์ปดํฌ๋ํธ (LiquorTypeBadge, StatusBadge, EvidenceStatusBadge ๋ฑ)
- CardHeader ์ก์
๋ฒํผ ๋ฐฐ์น ํจํด
- ํํฐ/๊ฒ์ ๊ตฌ์ฑ
- ๊ธฐํ์ vs ๊ธฐ์กด ํจํด ์ถฉ๋ ์: ๊ธฐ์กด ํจํด์ ์ฐ์ ํ๊ณ ์ฌ์ฉ์์๊ฒ ์๋ฆผ
const domainPages = await Glob({ pattern: `apps/${app}/src/${domain}/**/page.tsx` });
๋น๋ฒํ ๋ถ์ผ์น ์ฌ๋ก (๊ณผ๊ฑฐ ui-improver ์ธ์
์์ ๋ฐ๊ฒฌ):
| ๋ถ์ผ์น ์ ํ | ๋ฐ์ ๋น๋ | ๋ฐฉ์ง ๋ฐฉ๋ฒ |
|---|
| ์ผ๋ฐ Badge โ ๋๋ฉ์ธ ์ ์ฉ ๋ฑ์ง | ๋์ | ๊ธฐ์กด ํ์ด์ง์์ import๋ ๋ฑ์ง ์ปดํฌ๋ํธ ํ์ธ |
| ํ
์ด๋ธ border-r ๋๋ฝ | ๋์ | ๊ฐ์ ํญ ๊ทธ๋ฃน์ ๋ค๋ฅธ ํญ๊ณผ ๋น๊ต |
| ๋ถํ์ํ ์์ฝ ์นด๋/ํํฐ | ์ค๊ฐ | ๋ฐ์ดํฐ ๊ท๋ชจ์ ๊ธฐํ ์๋ ์ฌํ์ธ |
| CardHeader ๋ฒํผ ์์น ๋ถ์ผ์น | ์ค๊ฐ | ๋์ผ ๋๋ฉ์ธ ํ์ด์ง์ ๋ฒํผ ๋ฐฐ์น ๋ณต์ฌ |
Preview ์ฑ ๊ตฌ์กฐ
apps/preview/src/router.tsx โ ๋ชจ๋ ์ฑ ๋ผ์ฐํธ ํตํฉ
โโโ /liquor, /manufacturing, /admin, /tax-office
๊ฐ๋ฐ ์๋ฒ: http://localhost:3000/[์ฑ๋ช
]/[๊ฒฝ๋ก] (๊ธฐ๋ณธ ํฌํธ)
์๋ฒ ์คํ: ์๋ฒ๊ฐ ์์ผ๋ฉด pnpm dev:preview ๋๋ cd apps/preview && pnpm dev --port 3000
Phase 1-2: Planning & Block Selection
Block ๋งคํ
| ํ๋ฉด ์ ํ | ๊ถ์ฅ ๋ธ๋ก |
|---|
| ๋์๋ณด๋ (D) | dashboard-, stats-, chart-* |
| ๋ชฉ๋ก (S) | table-, data-table- |
| ๋ฑ๋ก/์์ (F) | form-, input-, sheet-* |
| ํ์
(P) | dialog-, modal-, alert-* |
/cui Workflow
get-create-instructions โ 2. get-blocks-metadata โ 3. get-block-meta-content โ 4. collect_selected_blocks โ 5. get_add_command_for_items โ 6. Execute install โ 7. Customize
CRITICAL: ๋จ๊ณ๋ณ ์์ ์ค์. ๊ฑด๋๋ฐ๊ธฐ ๊ธ์ง.
Phase 3: Code Generation
์์ธ ํ
ํ๋ฆฟ: references/component-templates.md
ํ์ ๊ท์น: references/consistency-rules.md
4๋ ํ์ ๊ท์น
| ํญ๋ชฉ | ์ฌ๋ฐ๋ฅธ ์ฌ์ฉ |
|---|
| ํ์ด์ง ํ์ดํ | <PageTitle> ์ปดํฌ๋ํธ |
| Sheet ํจ๋ฉ | <FormSheet> ์ฌ์ฉ |
| ๋ ์ง ์ ํ | <DateRangeFilter> ๋๋ <DateRangePicker> |
| ํ
์ด๋ธ ํจ๋ฉ | overflow-x-auto px-4 py-2 ๋ํผ |
๋ฑ์ง ๋ฐ ์ํ ํ์ ๊ท์น (CRITICAL)
| ํญ๋ชฉ | ๊ท์น |
|---|
| ์ํ ๋ฑ์ง | ๋ชจ๋ ์ํ/์ ํ ํ์์ ํ๋ซํผ StatusBadge ์ฌ์ฉ. raw <span> ๋๋ <Badge>๋ก ์ํ ์์ ๋ถ๊ธฐ ๊ธ์ง |
| ๋ฑ์ง ์์ ํ์
| Record<DomainType, BadgeColor> ์ฌ์ฉ. Record<string, string> ์ฌ์ฉ ๊ธ์ง |
| labels ์์น | types.ts์ ์ ์ (๋๋ฉ์ธ ํ์
๊ณผ ํจ๊ป) |
| colors ์์น | constants.ts์ ์ ์ (BadgeColor import ํ์) |
| ์ธ๋ผ์ธ ์ค๋ณต ๊ธ์ง | 2ํ ์ด์ ์ฌ์ฉ๋๋ labels/colors๋ ๋ฐ๋์ ๊ณต์ ์์๋ก ์ถ์ถ |
์์:
export type MyStatus = 'PENDING' | 'ACTIVE' | 'CLOSED';
export const MY_STATUS_LABELS: Record<MyStatus, string> = {
PENDING: '๋๊ธฐ', ACTIVE: '์งํ์ค', CLOSED: '์ข
๋ฃ',
};
import type { BadgeColor } from '@plan-master/web-platform';
import type { MyStatus } from './types';
export const MY_STATUS_COLORS: Record<MyStatus, BadgeColor> = {
PENDING: 'yellow', ACTIVE: 'blue', CLOSED: 'gray',
};
<StatusBadge status={item.status} labels={MY_STATUS_LABELS} colors={MY_STATUS_COLORS} />
์ซ์ ์ปฌ๋ผ ์ ๋ ฌ ๊ท์น (CRITICAL)
| ํญ๋ชฉ | ๊ท์น |
|---|
| ์ซ์ ํค๋ | <TableHead className="text-right"> ๋๋ SortableHeader๋ฅผ <div className="flex justify-end"> ๋ํ |
| ์ซ์ ์
| <TableCell className="text-sm text-right tabular-nums"> |
| ๋์ ์ปฌ๋ผ | ์๋, ๊ธ์ก, ์ธ์ก, ๋จ๊ฐ, ๋น์จ ๋ฑ ๋ชจ๋ ์ซ์ ๋ฐ์ดํฐ |
Dialog/Sheet UX ๊ท์น (CRITICAL)
| ํญ๋ชฉ | ๊ท์น |
|---|
| State ์ด๊ธฐํ | Dialog/Sheet onOpenChange ํธ๋ค๋ฌ์์ ๋ซํ ์ ๋ด๋ถ useState ๊ฐ ๋ฆฌ์
ํ์ |
| DialogDescription | ๋ชจ๋ Dialog/AlertDialog์ DialogDescription ํ์ (์ ๊ทผ์ฑ + ๋งฅ๋ฝ ์ค๋ช
) |
| Destructive variant | ์ญ์ /ํ๊ธฐ/์ ๊ฑฐ ๋ฒํผ์ ๋ฐ๋์ variant="destructive" ์ ์ฉ |
| Back ๋ฒํผ ์์น | CRUDPageLayout์ headerActions์ ๋ฐฐ์น (๋ณธ๋ฌธ ์ธ๋ผ์ธ ๋ฐฐ์น ๊ธ์ง) |
State ์ด๊ธฐํ ์์:
const handleOpenChange = (open: boolean) => {
if (!open) {
setSelectedFiles([]);
setSearchTerm('');
form.reset();
}
onOpenChange(open);
};
DialogDescription ์์:
<DialogHeader>
<DialogTitle>์ผ๊ด ๋ฑ๋ก</DialogTitle>
<DialogDescription>CSV ํ์ผ์ ์
๋ก๋ํ์ฌ ๋ฐ์ดํฐ๋ฅผ ์ผ๊ด ๋ฑ๋กํฉ๋๋ค.</DialogDescription>
</DialogHeader>
headerActions ์์:
<CRUDPageLayout
title="๋ฑ๋ก"
headerActions={<Button variant="ghost" onClick={() => navigate(-1)}>โ ๋ชฉ๋ก์ผ๋ก</Button>}
>
React ์ฝ๋ ํ์ง ์ฒดํฌ๋ฆฌ์คํธ (CRITICAL)
| ํญ๋ชฉ | ๊ท์น |
|---|
| ๊ฒ์ ์
๋ ฅ | SearchInput ์ปดํฌ๋ํธ ์ฌ์ฉ (Search ์์ด์ฝ+Input ์ง์ ์กฐํฉ ๊ธ์ง) |
| Fragment key | .map() ๋ด ๋ค์ค ์์ โ <Fragment key={id}> ํ์ (<> ๊ธ์ง) |
| useMemo | ํํฐ๋ง/์ ๋ ฌ ํ์ ๋ฐ์ดํฐ๋ ๋ฐ๋์ useMemo ์ฌ์ฉ |
| console.log | ๋๋ฒ๊ทธ์ฉ console.log ๋ฏธํฌํจ ํ์ธ |
File Structure
apps/[์ฑ๋ช
]/src/pages/[๋๋ฉ์ธ]/
โโโ [Feature]Page.tsx
โโโ components/
โโโ [Feature]Sheet.tsx
โโโ [Feature]Dialog.tsx
Phase 4: Reusable Component Creation (CRITICAL)
์์ธ: references/component-reuse.md
๋ฐ๋ณต ํจํด ๊ท์น
| ๋ฐ๋ณต ํ์ | ์กฐ์น |
|---|
| 1-2ํ | ์ธ๋ผ์ธ ํ์ฉ |
| 3ํ ์ด์ | ๋ฐ๋์ ์ปดํฌ๋ํธํ |
| ์ฑ ๊ฐ ๊ณต์ | @bitda/web-platform์ ์ถ๊ฐ |
๊ธฐ๋ณธ ์ฌ์ฌ์ฉ ํจํด
| ํจํด | ์ปดํฌ๋ํธ |
|---|
| ๊ฒ์ ๊ฐ๋ฅํ ์ ํ | SearchableSelect |
| ๋ ์ง ๋ฒ์ | DateRangePicker |
| ์ํ ๋ฐฐ์ง | StatusBadge |
| ํ์ธ ๋ค์ด์ผ๋ก๊ทธ | ConfirmDialog |
| ์๊ฐ ์
๋ ฅ | TimeInput |
| ์๋+๋จ์ | QuantityUnitInput |
Phase 5: shadcn Studio Workflows
| ๋ช
๋ น | ์ฉ๋ |
|---|
| /cui | ๊ธฐ์กด ๋ธ๋ก์ผ๋ก ์ ํ๋ฉด ์์ฑ |
| /iui | ์๊ฐ๋ฐ์ ์ฐฝ์์ ๋์์ธ (Pro) |
| /rui | ๊ธฐ์กด ์ปดํฌ๋ํธ ์
๋ฐ์ดํธ |
Phase 6: Validation Schema
import { z } from "zod";
export const workOrderSchema = z.object({
productId: z.string().min(1, "์ ํ์ ์ ํํด์ฃผ์ธ์"),
quantity: z.number().min(1, "์๋์ 1 ์ด์"),
dueDate: z.date({ required_error: "์์
๊ธฐํ ํ์" }),
});
Phase 7-8: Test & Visual Verification
์์ธ: references/test-planning.md, references/visual-verification.md
- ํ
์คํธ ์๋๋ฆฌ์ค ๊ทธ๋ฃนํ
/agent-browser ์คํฌ๋ก UI ๊ฒ์ฆ (โ ๏ธ ์์ด์ ํธ ์๋, Skill ๋๊ตฌ๋ก ํธ์ถ)
- ์คํฌ๋ฆฐ์ท ์บก์ฒ ๋ฐ ๋ณด๊ณ
ํธ์ถ ๋ฐฉ๋ฒ:
Skill({ skill: "agent-browser", args: "http://localhost:3000/..." })
์๋ฒ ํ์ธ: lsof -i :3000์ผ๋ก ํ์ธ ํ, ์์ผ๋ฉด pnpm dev:preview ์คํ
Phase 9: UI Consistency Check (AUTO)
์ฝ๋ ์์ฑ ์๋ฃ ํ ui-supervisor ์์ด์ ํธ ์๋ ์คํ:
Task({
subagent_type: "ui-supervisor",
prompt: `UI ์ผ๊ด์ฑ ๊ฒ์ฌ: [์์ฑ๋ ํ์ผ ๋ชฉ๋ก]
1. ๊ธฐ์กด ํ์ด์ง์ ๋ ์ด์์ ์ผ๊ด์ฑ
2. ๊ณต์ ์ปดํฌ๋ํธ ์ฌ์ฌ์ฉ ์ฌ๋ถ
3. ํ
์ด๋ธ/ํผ/๋ค์ด์ผ๋ก๊ทธ ํจํด ์ค์`
})
| ๊ฒฐ๊ณผ | ์ก์
|
|---|
| Critical ๋ฐ๊ฒฌ | ์ฆ์ /ui-improver ์คํ |
| Major ๋ฐ๊ฒฌ | ์ฌ์ฉ์ ํ์ธ ํ ์ ์ฉ |
| Minor๋ง ๋ฐ๊ฒฌ | ๊ถ์ฅ์ฌํญ ์๋ด |
| ์ด์ ์์ | ๋ฐฐํฌ ์งํ |
Agent Summary
| Agent | ์ฉ๋ |
|---|
feature-dev:code-explorer | ํ๋ก์ ํธ ๊ตฌ์กฐ ๋ถ์ |
feature-dev:code-architect | ์ํคํ
์ฒ ์ค๊ณ |
invigo-agents:frontend-developer | ์ปดํฌ๋ํธ ์์ฑ |
invigo-agents:code-reviewer | ์ฝ๋ ๋ฆฌ๋ทฐ |
Error Handling
| ์๋ฌ | ๋์ |
|---|
| MCP Server Not Connected | shadcn-studio-mcp ์ค์ ํ์ธ |
| Block Not Found | ๋์ ๋ธ๋ก ๋๋ ์ปค์คํ
๊ตฌํ |
| Install Failed | ํ๋ก์ ํธ ์ค์ ํ์ธ |
์ํ๋ณ UI ํ์ ๊ตฌํ (๊ธฐํ๋ด ์ธ์ฌ์ดํธ #2)
5๊ฑด์ ๋ฐ๋ณต ์์ค์ปฌ๋ ์ด์
์์ ๋์ถ: ๊ธฐํ์์ ์ํ๋ณ UI ๋์์ด ๋ฏธ๋ช
์๋์ด ๊ตฌํ ์ ๋๋ฝ ๋ฐ์
์ฝ๋ ์์ฑ ์ ๋ค์์ ๋ฐ๋์ ๊ธฐํ์์์ ํ์ธํ๊ณ , ๋ฏธ๋ช
์ ์ ๊ธฐํ์์๊ฒ ํ์ธ ์์ฒญ:
Handoff
์ฝ๋ ์์ฑ ์๋ฃ ํ: Visual Verification โ ์ฝ๋ ๋ฆฌ๋ทฐ โ github-deployer ์คํฌ๋ก ๋ฐฐํฌ
Trigger: "GitHub์ ๋ฐฐํฌํด์ค", "์ฝ๋ ํธ์ํด์ค"