"use client"; import { LayoutDefinition, LayoutType, LayoutCategory } from "@/types/layout"; import React from "react"; /** * 레이아웃 정의를 생성하는 헬퍼 함수 * 타입 안전성과 기본값을 보장합니다. */ export interface CreateLayoutDefinitionOptions { id: string; name: string; nameEng?: string; description: string; category: LayoutCategory; icon?: string; component: React.ComponentType; defaultConfig?: Record; defaultZones?: Array<{ id: string; name: string; position?: Record; size?: { width: number | string; height: number | string; minWidth?: number; minHeight?: number; maxWidth?: number; maxHeight?: number; }; [key: string]: any; }>; tags?: string[]; isActive?: boolean; version?: string; author?: string; documentation?: string; } /** * 레이아웃 정의를 생성합니다. */ export function createLayoutDefinition(options: CreateLayoutDefinitionOptions): LayoutDefinition { const { id, name, nameEng = name, description, category, icon = "layout", component, defaultConfig = {}, defaultZones = [], tags = [], isActive = true, version = "1.0.0", author = "Developer", documentation = "", } = options; // ID 유효성 검사 if (!id || typeof id !== "string" || !/^[a-z][a-z0-9-]*$/.test(id)) { throw new Error( `Invalid layout ID: "${id}". ID must start with a letter and contain only lowercase letters, numbers, and hyphens.`, ); } // 필수 필드 검사 if (!name.trim()) { throw new Error("Layout name is required"); } if (!description.trim()) { throw new Error("Layout description is required"); } if (!component) { throw new Error("Layout component is required"); } // 기본 태그 자동 추가 const autoTags = [id, category, "layout"]; const allTags = [...new Set([...autoTags, ...tags])]; const definition: LayoutDefinition = { id: id as LayoutType, name, nameEng, description, category, icon, component, defaultConfig, defaultZones, tags: allTags, isActive, metadata: { version, author, documentation, createdAt: new Date().toISOString(), lastUpdated: new Date().toISOString(), }, }; console.log(`📦 레이아웃 정의 생성: ${id} (${name})`); return definition; } /** * 레이아웃 정의의 유효성을 검사합니다. */ export function validateLayoutDefinition(definition: LayoutDefinition): { isValid: boolean; errors: string[]; warnings: string[]; } { const errors: string[] = []; const warnings: string[] = []; // 필수 필드 검사 if (!definition.id) errors.push("ID is required"); if (!definition.name) errors.push("Name is required"); if (!definition.description) errors.push("Description is required"); if (!definition.category) errors.push("Category is required"); if (!definition.component) errors.push("Component is required"); // ID 형식 검사 if (definition.id && !/^[a-z][a-z0-9-]*$/.test(definition.id)) { errors.push("ID must start with a letter and contain only lowercase letters, numbers, and hyphens"); } // defaultZones 검사 if (definition.defaultZones) { definition.defaultZones.forEach((zone, index) => { if (!zone.id) errors.push(`Zone ${index}: ID is required`); if (!zone.name) errors.push(`Zone ${index}: Name is required`); }); } // 경고사항 검사 if (!definition.nameEng) warnings.push("English name is recommended"); if (!definition.icon || definition.icon === "layout") warnings.push("Custom icon is recommended"); if (!definition.defaultZones || definition.defaultZones.length === 0) { warnings.push("Default zones are recommended for better UX"); } return { isValid: errors.length === 0, errors, warnings, }; } /** * 레이아웃 정의를 디버그 정보와 함께 출력합니다. */ export function debugLayoutDefinition(definition: LayoutDefinition): void { console.group(`🔍 Layout Debug: ${definition.id}`); console.log("📋 Definition:", definition); const validation = validateLayoutDefinition(definition); if (validation.errors.length > 0) { console.error("❌ Errors:", validation.errors); } if (validation.warnings.length > 0) { console.warn("⚠️ Warnings:", validation.warnings); } if (validation.isValid) { console.log("✅ Definition is valid"); } console.groupEnd(); }