fe-scaffold
1
总安装量
1
周安装量
#43469
全站排名
安装命令
npx skills add https://github.com/ingpdw/pdw-fe-dev-tool --skill fe-scaffold
Agent 安装分布
mcpjam
1
claude-code
1
replit
1
junie
1
windsurf
1
zencoder
1
Skill 文档
FE Scaffolding
$ARGUMENTS를 íì±íì¬ í´ë¹íë íì
ì ë³´ì¼ë¬íë ì´í¸ë¥¼ ìì±íë¤.
ì§ì íì
| íì | ì¤ëª | ìì |
|---|---|---|
component |
React ì»´í¬ëí¸ + í ì¤í¸ | /fe-scaffold component UserProfile |
page |
Next.js App Router íì´ì§ | /fe-scaffold page dashboard |
api |
Route Handler (API) | /fe-scaffold api users |
hook |
커ì¤í í + í ì¤í¸ | /fe-scaffold hook useDebounce |
store |
Zustand ì¤í ì´ | /fe-scaffold store auth |
feature |
ê¸°ë¥ ëª¨ë (ì»´í¬ëí¸ + í + íì + í ì¤í¸) | /fe-scaffold feature checkout |
form |
React Hook Form + Zod ì¤í¤ë§ í¼ | /fe-scaffold form LoginForm |
ì¸ìê° ìì¼ë©´ ì¬ì©ììê² ì´ë¤ íì ì ìì±í ì§ ì§ë¬¸íë¤.
í í릿 ê·ì¹
component
íì¼ ìì± ìì¹: src/components/[ì´ë¦]/[ì´ë¦].tsx + [ì´ë¦].test.tsx
// src/components/[Name]/[Name].tsx
import { cn } from "@/lib/utils";
interface [Name]Props {
className?: string;
children?: React.ReactNode;
}
function [Name]({ className, children }: [Name]Props) {
return (
<div className={cn("", className)}>
{children}
</div>
);
}
export { [Name] };
export type { [Name]Props };
// src/components/[Name]/[Name].test.tsx
import { render, screen } from "@testing-library/react";
import { describe, expect, it } from "vitest";
import { [Name] } from "./[Name]";
describe("[Name]", () => {
it("renders children", () => {
render(<[Name]>test</[Name]>);
expect(screen.getByText("test")).toBeInTheDocument();
});
});
page
íì¼ ìì± ìì¹: src/app/[ì´ë¦]/page.tsx + layout.tsx (íì ì) + loading.tsx
// src/app/[name]/page.tsx
import type { Metadata } from "next";
export const metadata: Metadata = {
title: "[Name]",
description: "[Name] page",
};
export default function [Name]Page() {
return (
<main>
<h1>[Name]</h1>
</main>
);
}
// src/app/[name]/loading.tsx
import { Skeleton } from "@/components/ui/skeleton";
export default function [Name]Loading() {
return <Skeleton className="h-screen w-full" />;
}
api
íì¼ ìì± ìì¹: src/app/api/[ì´ë¦]/route.ts
// src/app/api/[name]/route.ts
import { NextRequest, NextResponse } from "next/server";
export async function GET(request: NextRequest) {
try {
// TODO: implement
return NextResponse.json({ data: [] });
} catch (error) {
return NextResponse.json(
{ error: "Internal Server Error" },
{ status: 500 }
);
}
}
export async function POST(request: NextRequest) {
try {
const body = await request.json();
// TODO: implement
return NextResponse.json({ data: body }, { status: 201 });
} catch (error) {
return NextResponse.json(
{ error: "Internal Server Error" },
{ status: 500 }
);
}
}
hook
íì¼ ìì± ìì¹: src/hooks/[ì´ë¦].ts + [ì´ë¦].test.ts
// src/hooks/[useName].ts
import { useCallback, useState } from "react";
function [useName]() {
// TODO: implement
return {};
}
export { [useName] };
// src/hooks/[useName].test.ts
import { renderHook, act } from "@testing-library/react";
import { describe, expect, it } from "vitest";
import { [useName] } from "./[useName]";
describe("[useName]", () => {
it("should work", () => {
const { result } = renderHook(() => [useName]());
expect(result.current).toBeDefined();
});
});
store
íì¼ ìì± ìì¹: src/stores/[ì´ë¦]Store.ts
// src/stores/[name]Store.ts
import { create } from "zustand";
import { devtools } from "zustand/middleware";
interface [Name]State {
// TODO: define state
}
interface [Name]Actions {
// TODO: define actions
reset: () => void;
}
const initial[Name]State: [Name]State = {
// TODO: initial values
};
const use[Name]Store = create<[Name]State & [Name]Actions>()(
devtools(
(set) => ({
...initial[Name]State,
reset: () => set(initial[Name]State),
}),
{ name: "[name]-store" }
)
);
export { use[Name]Store };
export type { [Name]State, [Name]Actions };
feature
ê¸°ë¥ ëª¨ë ì 체를 ìì±íë¤. ìì¹: src/components/[feature]/
src/components/[feature]/
âââ [Feature].tsx # ë©ì¸ ì»´í¬ëí¸
âââ [Feature].test.tsx # í
ì¤í¸
âââ use[Feature].ts # ê¸°ë¥ ì ì© í
âââ [feature].types.ts # íì
ì ì
âââ index.ts # barrel export
form
React Hook Form + Zod ê¸°ë° í¼ ì»´í¬ëí¸ë¥¼ ìì±íë¤.
// src/components/[Name]/[Name].tsx
"use client";
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";
import { z } from "zod";
import { Button } from "@/components/ui/button";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
const [name]Schema = z.object({
// TODO: define schema
email: z.string().email(),
});
type [Name]Values = z.infer<typeof [name]Schema>;
interface [Name]Props {
onSubmit: (values: [Name]Values) => void;
}
function [Name]({ onSubmit }: [Name]Props) {
const form = useForm<[Name]Values>({
resolver: zodResolver([name]Schema),
defaultValues: {
email: "",
},
});
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem>
<FormLabel>Email</FormLabel>
<FormControl>
<Input placeholder="email@example.com" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<Button type="submit">Submit</Button>
</form>
</Form>
);
}
export { [Name] };
export type { [Name]Values };
ì¤í ê·ì¹
$ARGUMENTSìì íì ê³¼ ì´ë¦ì íì±- íë¡ì í¸ì 기존 구조를 Glob/Readë¡ íì¸íì¬ ì¤ì ê²½ë¡ í¨í´ì ë§ì¶¤
- ì´ë¯¸ ì¡´ì¬íë íì¼ì´ ìì¼ë©´ ë®ì´ì°ì§ ìê³ ì¬ì©ììê² íì¸
- íì¼ ìì± í ìì±ë íì¼ ëª©ë¡ì ì¶ë ¥
- shadcn/ui ì»´í¬ëí¸ê° íìíë° ìì¼ë©´ ì¤ì¹ ëª ë ¹ì´ ìë´