Auto-generate CRUD when user mentions "CRUD 추가", "관리 페이지 만들어", "[기능] 리스트/등록/상세/수정"
Automatically generates complete Admin CRUD (Create, Read, Update, Delete) system with React Query.
반드시 첫 단계로 Orval 확인:
src/shared/api/orval/types/, orval/admin-[domain]/)자연어에서 도메인 정보 추출:
| User Input | Domain | Display | Icon |
|---|
| "쿠폰 CRUD" | coupon | 쿠폰 | Ticket |
| "상품 카테고리 관리" | product-category | 상품 카테고리 | Package |
⚠️ 중요: Admin 전용 - admin 경로 사용
src/
├── entities/[domain]/
│ ├── api/[domain].api.ts (React Query hooks)
│ ├── model/[domain].schema.ts (Zod schema)
│ └── index.ts
├── features/admin/ (관리자 전용)
│ ├── [domain]-form/ (Form component)
│ └── [domain]-table/ (DataTable)
├── views/admin/ (관리자 전용)
│ └── [domain]/ (list/detail/create/edit)
└── widgets/admin-sidebar/
app/admin/[domain]s/ (4개 페이지)
서비스(일반 사용자)용이면:
features/service/[domain]-form/
views/service/[domain]/
app/(layout)/[domain]s/
src/shared/constants/routes.ts: /admin/[domain]s 경로src/widgets/admin-sidebar/lib/sidebarData.ts: 메뉴 항목Schema (entities/[domain]/model/[domain].schema.ts):
// Orval CreateRequest → Zod 변환
export const create[Domain]Schema = z.object({
// 모든 필드
});
// Update는 partial()
export const update[Domain]Schema = create[Domain]Schema.partial();
API (entities/[domain]/api/[domain].api.ts):
export const [domain]QueryKeys = {
all: ["[domain]s"] as const,
lists: () => [[domain]QueryKeys.all, "list"] as const,
details: () => [[domain]QueryKeys.all, "detail"] as const,
detail: (id: number) => [[domain]QueryKeys.details(), id] as const,
};
// useGetAll[Domain]s, useGet[Domain], useCreate[Domain], useUpdate[Domain], useDelete[Domain]
Schema (features/[domain]-form/model/[domain]-form.schema.ts):
// ❌ NO transform - UI 데이터 구조 유지
// ✅ Entity 상속 + 복합 검증만
export const create[Domain]FormSchema = create[Domain]Schema.extend({
// 추가 검증만
}).refine(...);
// Edit 전용 필드 추가 후 partial()
export const update[Domain]FormSchema = create[Domain]FormSchema
.extend({
// Edit 모드 전용 필드 (예: isActive)
})
.partial();
Component (features/admin/[domain]-form/ui/[Domain]Form.tsx) - 관리자 전용:
// Discriminated Union Props
type CreateProps = {
mode: "create";
initialValues?: never;
onSubmit: (data: [Domain]CreateRequest) => Promise<void>;
};
type EditProps = {
mode: "edit";
initialValues: Partial<Update[Domain]FormInput>;
onSubmit: (data: Partial<[Domain]CreateRequest>) => Promise<void>;
};
// Mode 기반 스키마 선택
const form = useForm({
resolver: zodResolver(
mode === "edit" ? update[Domain]FormSchema : create[Domain]FormSchema
),
});
// dirtyFields 기반 제출 (Edit 모드에서 변경된 필드만)
const handleOnSubmit = async (values) => {
if (mode === "edit") {
const { dirtyFields } = form.formState;
const dirtyData = {};
Object.keys(dirtyFields).forEach((key) => {
// 필드별 변환 로직 (날짜 등)
});
await onSubmit(dirtyData);
} else {
// Create 모드: 전체 데이터 변환
}
};
// List: useGetAll[Domain]s + DataTable
const { data, isLoading } = useGetAll[Domain]s();
// Detail: useGet[Domain]
const { data } = useGet[Domain](id);
// Create: useCreate[Domain] + toast
const { mutateAsync } = useCreate[Domain]();
// Edit: useGet[Domain] + useUpdate[Domain]
// ISO → YYYY-MM-DD 변환: isoString.split("T")[0]
app/admin/[domain]s/
page.tsx (List)
[id]/page.tsx (Detail)
create/page.tsx (Create)
[id]/edit/page.tsx (Edit)
partial() + Edit 전용 필드 extend()CreateProps | EditPropsmode === "edit" && (...)[Domain]CreateRequestAll rules follow F:\work\smarter-store-fe\CLAUDE.md