# 화면관리 시스템 컴포넌트 개발 가이드 화면관리 시스템에서 새로운 컴포넌트, 템플릿, 웹타입을 추가하는 완전한 가이드입니다. ## 🎯 목차 1. [컴포넌트 추가하기](#1-컴포넌트-추가하기) 2. [웹타입 추가하기](#2-웹타입-추가하기) 3. [템플릿 추가하기](#3-템플릿-추가하기) 4. [설정 패널 개발](#4-설정-패널-개발) 5. [데이터베이스 설정](#5-데이터베이스-설정) 6. [테스트 및 검증](#6-테스트-및-검증) --- ## 1. 컴포넌트 추가하기 ### 1.1 컴포넌트 렌더러 생성 새로운 컴포넌트 렌더러를 생성합니다. **파일 위치**: `frontend/lib/registry/components/{ComponentName}Renderer.tsx` ```typescript // 예시: AlertRenderer.tsx import React from "react"; import { ComponentRenderer } from "../DynamicComponentRenderer"; import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; import { AlertTriangle, Info, CheckCircle, XCircle } from "lucide-react"; const AlertRenderer: ComponentRenderer = ({ component, children, isInteractive, ...props }) => { const config = component.componentConfig || {}; const { title = "알림", message = "알림 메시지입니다.", type = "info", // info, warning, success, error showIcon = true, style = {} } = config; // 타입별 아이콘 매핑 const iconMap = { info: Info, warning: AlertTriangle, success: CheckCircle, error: XCircle, }; const Icon = iconMap[type as keyof typeof iconMap] || Info; return ( {showIcon && } {title} {isInteractive ? ( // 실제 할당된 화면에서는 설정된 메시지 표시 message ) : ( // 디자이너에서는 플레이스홀더 + children 표시 children && React.Children.count(children) > 0 ? ( children ) : (
{message}
알림 컴포넌트 - {type} 타입
) )}
); }; export default AlertRenderer; ``` ### 1.2 컴포넌트 등록 **파일**: `frontend/lib/registry/index.ts` ```typescript // 컴포넌트 렌더러 import 추가 import AlertRenderer from "./components/AlertRenderer"; // 컴포넌트 레지스트리에 등록 export const registerComponents = () => { // 기존 컴포넌트들... ComponentRegistry.register("alert", AlertRenderer); ComponentRegistry.register("alert-info", AlertRenderer); ComponentRegistry.register("alert-warning", AlertRenderer); }; ``` ### 1.3 InteractiveScreenViewer에 등록 **파일**: `frontend/components/screen/InteractiveScreenViewerDynamic.tsx` ```typescript // 컴포넌트 렌더러들을 강제로 로드하여 레지스트리에 등록 import "@/lib/registry/components/ButtonRenderer"; import "@/lib/registry/components/CardRenderer"; import "@/lib/registry/components/DashboardRenderer"; import "@/lib/registry/components/AlertRenderer"; // 추가 import "@/lib/registry/components/WidgetRenderer"; ``` --- ## 2. 웹타입 추가하기 ### 2.1 웹타입 컴포넌트 생성 **파일 위치**: `frontend/components/screen/widgets/types/{WebTypeName}Widget.tsx` ```typescript // 예시: ColorPickerWidget.tsx import React from "react"; import { WebTypeComponentProps } from "@/types/screen"; import { WidgetComponent } from "@/types/screen"; interface ColorPickerConfig { defaultColor?: string; showAlpha?: boolean; presetColors?: string[]; } export const ColorPickerWidget: React.FC = ({ component, value, onChange, readonly = false }) => { const widget = component as WidgetComponent; const { placeholder, required, style } = widget || {}; const config = widget?.webTypeConfig as ColorPickerConfig | undefined; const handleColorChange = (color: string) => { if (!readonly && onChange) { onChange(color); } }; return (
{/* 라벨 표시 */} {widget?.label && ( )}
{/* 색상 입력 */} handleColorChange(e.target.value)} disabled={readonly} className="h-10 w-16 rounded border border-gray-300 cursor-pointer disabled:cursor-not-allowed" /> {/* 색상 값 표시 */} handleColorChange(e.target.value)} placeholder={placeholder || "색상을 선택하세요"} disabled={readonly} className="flex-1 h-10 px-3 rounded border border-gray-300 focus:border-blue-500 focus:ring-1 focus:ring-blue-500 disabled:bg-gray-100" />
{/* 미리 설정된 색상들 */} {config?.presetColors && (
{config.presetColors.map((color, idx) => (
)}
); }; ``` ### 2.2 웹타입 등록 **파일**: `frontend/lib/registry/index.ts` ```typescript // 웹타입 컴포넌트 import 추가 import { ColorPickerWidget } from "@/components/screen/widgets/types/ColorPickerWidget"; // 웹타입 레지스트리에 등록 export const registerWebTypes = () => { // 기존 웹타입들... WebTypeRegistry.register("color", ColorPickerWidget); WebTypeRegistry.register("colorpicker", ColorPickerWidget); }; ``` ### 2.3 웹타입 설정 인터페이스 추가 **파일**: `frontend/types/screen.ts` ```typescript // 웹타입별 설정 인터페이스에 추가 export interface ColorPickerTypeConfig { defaultColor?: string; showAlpha?: boolean; presetColors?: string[]; } // 전체 웹타입 설정 유니온에 추가 export type WebTypeConfig = | TextTypeConfig | NumberTypeConfig | DateTypeConfig | SelectTypeConfig | FileTypeConfig | ColorPickerTypeConfig; // 추가 ``` --- ## 3. 템플릿 추가하기 ### 3.1 템플릿 컴포넌트 생성 **파일 위치**: `frontend/components/screen/templates/{TemplateName}Template.tsx` ```typescript // 예시: ContactFormTemplate.tsx import React from "react"; import { ComponentData } from "@/types/screen"; import { generateComponentId } from "@/lib/utils/componentUtils"; interface ContactFormTemplateProps { onAddComponents: (components: ComponentData[]) => void; position: { x: number; y: number }; } export const ContactFormTemplate: React.FC = ({ onAddComponents, position, }) => { const createContactForm = () => { const components: ComponentData[] = [ // 컨테이너 { id: generateComponentId(), type: "container", position: position, size: { width: 500, height: 600 }, style: { backgroundColor: "#ffffff", border: "1px solid #e5e7eb", borderRadius: "8px", padding: "24px", }, children: [], }, // 제목 { id: generateComponentId(), type: "widget", webType: "text", position: { x: position.x + 24, y: position.y + 24 }, size: { width: 452, height: 40 }, label: "연락처 양식", placeholder: "연락처를 입력해주세요", style: { fontSize: "24px", fontWeight: "bold", color: "#1f2937", }, }, // 이름 입력 { id: generateComponentId(), type: "widget", webType: "text", position: { x: position.x + 24, y: position.y + 84 }, size: { width: 452, height: 40 }, label: "이름", placeholder: "이름을 입력하세요", required: true, }, // 이메일 입력 { id: generateComponentId(), type: "widget", webType: "email", position: { x: position.x + 24, y: position.y + 144 }, size: { width: 452, height: 40 }, label: "이메일", placeholder: "이메일을 입력하세요", required: true, }, // 전화번호 입력 { id: generateComponentId(), type: "widget", webType: "tel", position: { x: position.x + 24, y: position.y + 204 }, size: { width: 452, height: 40 }, label: "전화번호", placeholder: "전화번호를 입력하세요", }, // 메시지 입력 { id: generateComponentId(), type: "widget", webType: "textarea", position: { x: position.x + 24, y: position.y + 264 }, size: { width: 452, height: 120 }, label: "메시지", placeholder: "메시지를 입력하세요", required: true, }, // 제출 버튼 { id: generateComponentId(), type: "button", componentType: "button-primary", position: { x: position.x + 24, y: position.y + 404 }, size: { width: 120, height: 40 }, componentConfig: { text: "제출", actionType: "submit", style: "primary", }, }, // 취소 버튼 { id: generateComponentId(), type: "button", componentType: "button-secondary", position: { x: position.x + 164, y: position.y + 404 }, size: { width: 120, height: 40 }, componentConfig: { text: "취소", actionType: "cancel", style: "secondary", }, }, ]; onAddComponents(components); }; return (
연락처 양식
이름, 이메일, 전화번호, 메시지 입력이 포함된 연락처 양식
); }; ``` ### 3.2 템플릿 패널에 등록 **파일**: `frontend/components/screen/panels/TemplatesPanel.tsx` ```typescript // 템플릿 import 추가 import { ContactFormTemplate } from "@/components/screen/templates/ContactFormTemplate"; // 템플릿 목록에 추가 const templates = [ // 기존 템플릿들... { id: "contact-form", name: "연락처 양식", description: "이름, 이메일, 전화번호, 메시지가 포함된 연락처 양식", component: ContactFormTemplate, }, ]; ``` --- ## 4. 설정 패널 개발 ### 4.1 설정 패널 컴포넌트 생성 **파일 위치**: `frontend/components/screen/config-panels/{ComponentName}ConfigPanel.tsx` ```typescript // 예시: AlertConfigPanel.tsx import React from "react"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Label } from "@/components/ui/label"; import { Input } from "@/components/ui/input"; import { Textarea } from "@/components/ui/textarea"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Switch } from "@/components/ui/switch"; import { ComponentData } from "@/types/screen"; interface AlertConfigPanelProps { component: ComponentData; onUpdateProperty: (path: string, value: any) => void; } export const AlertConfigPanel: React.FC = ({ component, onUpdateProperty, }) => { const config = component.componentConfig || {}; return ( 알림 설정 {/* 제목 설정 */}
onUpdateProperty("componentConfig.title", e.target.value)} placeholder="알림 제목" className="h-8" />
{/* 메시지 설정 */}