ERP-node/docs/screen-management-dynamic-s...

1013 lines
34 KiB
Markdown
Raw Normal View History

2025-09-09 14:29:04 +09:00
# 화면관리 시스템 동적 설정 관리 계획서
> 하드코딩된 웹타입과 버튼 기능을 동적으로 관리할 수 있는 시스템 구축 계획
## 📋 목차
- [개요](#개요)
- [현재 상황 분석](#현재-상황-분석)
- [목표 시스템 아키텍처](#목표-시스템-아키텍처)
- [단계별 구현 계획](#단계별-구현-계획)
- [기대 효과](#기대-효과)
- [실행 일정](#실행-일정)
## 개요
### 🎯 목표
현재 화면관리 시스템에서 하드코딩되어 있는 웹타입과 버튼 기능을 동적으로 관리할 수 있는 설정 페이지를 구축하여, 개발자는 컴포넌트만 작성하고 비개발자는 관리 페이지에서 타입을 추가/수정할 수 있는 유연한 시스템으로 전환
### 🔧 핵심 과제
- 25개의 하드코딩된 웹타입을 데이터베이스 기반 동적 관리로 전환
- 11개의 하드코딩된 버튼 액션을 설정 가능한 시스템으로 변경
- 플러그인 방식의 컴포넌트 아키텍처 구축
- 비개발자도 사용 가능한 설정 관리 인터페이스 제공
## 현재 상황 분석
### 📊 하드코딩된 부분들
#### 1. 웹타입 (25개)
**위치**: `frontend/types/screen.ts`
```typescript
export type WebType =
| "text"
| "number"
| "date"
| "code"
| "entity"
| "textarea"
| "select"
| "checkbox"
| "radio"
| "file"
| "email"
| "tel"
| "datetime"
| "dropdown"
| "text_area"
| "boolean"
| "decimal"
| "button";
```
#### 2. 버튼 액션 (11개)
**위치**: `frontend/types/screen.ts`
```typescript
export type ButtonActionType =
| "save"
| "delete"
| "edit"
| "add"
| "search"
| "reset"
| "submit"
| "close"
| "popup"
| "navigate"
| "custom";
```
#### 3. 렌더링 로직
**위치**: `frontend/components/screen/RealtimePreview.tsx`
- 970줄의 switch-case 문으로 웹타입별 렌더링 처리
- 각 웹타입별 고정된 렌더링 로직
#### 4. 설정 패널
**위치**: `frontend/components/screen/panels/ButtonConfigPanel.tsx`
- 45줄의 하드코딩된 액션타입 옵션 배열
- 각 액션별 고정된 기본값 설정
### ⚠️ 현재 시스템의 문제점
- 새로운 웹타입 추가 시 여러 파일 수정 필요
- 타입별 설정 변경을 위해 코드 수정 및 배포 필요
- 회사별/프로젝트별 커스텀 타입 관리 어려움
- 비개발자의 시스템 설정 변경 불가능
## 목표 시스템 아키텍처
### 🎨 전체 시스템 구조
```
┌─────────────────────────────────────────────────────────────┐
│ 관리자 설정 페이지 │
├─────────────────────────────────────────────────────────────┤
│ 웹타입 관리 │ 버튼액션 관리 │ 스타일템플릿 │ 격자설정 │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ API Layer │
├─────────────────────────────────────────────────────────────┤
│ /api/admin/web-types │ /api/admin/button-actions │
│ /api/admin/style-templates │ /api/admin/grid-standards │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Database Layer │
├─────────────────────────────────────────────────────────────┤
│ web_type_standards │ button_action_standards │
│ style_templates │ grid_standards │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 웹타입 레지스트리 시스템 │
├─────────────────────────────────────────────────────────────┤
│ WebTypeRegistry.register() │ DynamicRenderer │
│ 플러그인 방식 컴포넌트 등록 │ 동적 컴포넌트 렌더링 │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 화면관리 시스템 │
├─────────────────────────────────────────────────────────────┤
│ ScreenDesigner │ RealtimePreview │
│ PropertiesPanel │ InteractiveScreenViewer │
└─────────────────────────────────────────────────────────────┘
```
### 🔌 플러그인 방식 컴포넌트 시스템
#### 웹타입 정의 인터페이스
```typescript
interface WebTypeDefinition {
webType: string; // 웹타입 식별자
name: string; // 표시명
category: string; // 카테고리 (input, select, display, special)
defaultConfig: any; // 기본 설정
validationRules: any; // 유효성 검사 규칙
defaultStyle: any; // 기본 스타일
inputProperties: any; // HTML input 속성
component: React.ComponentType; // 렌더링 컴포넌트
configPanel: React.ComponentType; // 설정 패널 컴포넌트
icon?: React.ComponentType; // 아이콘 컴포넌트
sortOrder?: number; // 정렬 순서
isActive?: boolean; // 활성화 여부
}
```
#### 웹타입 레지스트리
```typescript
class WebTypeRegistry {
private static types = new Map<string, WebTypeDefinition>();
// 웹타입 등록
static register(definition: WebTypeDefinition) {
this.types.set(definition.webType, definition);
}
// 웹타입 조회
static get(webType: string): WebTypeDefinition | undefined {
return this.types.get(webType);
}
// 모든 웹타입 조회
static getAll(): WebTypeDefinition[] {
return Array.from(this.types.values())
.filter((type) => type.isActive)
.sort((a, b) => (a.sortOrder || 0) - (b.sortOrder || 0));
}
// 카테고리별 조회
static getByCategory(category: string): WebTypeDefinition[] {
return this.getAll().filter((type) => type.category === category);
}
}
```
#### 동적 컴포넌트 렌더러
```typescript
const DynamicWebTypeRenderer: React.FC<{
widgetType: string;
component: WidgetComponent;
[key: string]: any;
}> = ({ widgetType, component, ...props }) => {
const definition = WebTypeRegistry.get(widgetType);
if (!definition) {
return (
<div className="flex items-center justify-center p-4 border-2 border-dashed border-red-300 bg-red-50">
<span className="text-red-600">알 수 없는 웹타입: {widgetType}</span>
</div>
);
}
const Component = definition.component;
return <Component component={component} {...props} />;
};
```
## 단계별 구현 계획
### 📅 Phase 1: 기반 구조 구축 (1-2주)
#### 1.1 데이터베이스 스키마 적용
-**이미 준비됨**: `db/08-create-webtype-standards.sql`
- 4개 테이블: `web_type_standards`, `button_action_standards`, `style_templates`, `grid_standards`
- 기본 데이터 25개 웹타입, 12개 버튼액션 포함
#### 1.2 Backend API 개발
```
backend-node/src/routes/admin/
├── web-types.ts # 웹타입 CRUD API
├── button-actions.ts # 버튼액션 CRUD API
├── style-templates.ts # 스타일템플릿 CRUD API
└── grid-standards.ts # 격자설정 CRUD API
```
**주요 API 엔드포인트:**
```typescript
// 웹타입 관리
GET /api/admin/web-types # 목록 조회
POST /api/admin/web-types # 생성
PUT /api/admin/web-types/:webType # 수정
DELETE /api/admin/web-types/:webType # 삭제
// 버튼액션 관리
GET /api/admin/button-actions # 목록 조회
POST /api/admin/button-actions # 생성
PUT /api/admin/button-actions/:actionType # 수정
DELETE /api/admin/button-actions/:actionType # 삭제
// 화면관리에서 사용할 조회 API
GET /api/screen/web-types?active=Y&category=input
GET /api/screen/button-actions?active=Y&category=crud
```
#### 1.3 프론트엔드 기반 구조
```
frontend/lib/registry/
├── WebTypeRegistry.ts # 웹타입 레지스트리
├── ButtonActionRegistry.ts # 버튼액션 레지스트리
└── types.ts # 레지스트리 관련 타입 정의
frontend/components/screen/dynamic/
├── DynamicWebTypeRenderer.tsx # 동적 웹타입 렌더러
├── DynamicConfigPanel.tsx # 동적 설정 패널
└── DynamicActionHandler.tsx # 동적 액션 핸들러
frontend/hooks/
├── useWebTypes.ts # 웹타입 관리 훅
├── useButtonActions.ts # 버튼액션 관리 훅
├── useStyleTemplates.ts # 스타일템플릿 관리 훅
└── useGridStandards.ts # 격자설정 관리 훅
```
### 📅 Phase 2: 설정 관리 페이지 개발 (2-3주)
#### 2.1 관리 페이지 라우팅 구조
```
frontend/app/(dashboard)/admin/system-settings/
├── page.tsx # 메인 설정 페이지
├── layout.tsx # 설정 페이지 레이아웃
├── web-types/
│ ├── page.tsx # 웹타입 목록
│ ├── new/
│ │ └── page.tsx # 새 웹타입 생성
│ ├── [webType]/
│ │ ├── page.tsx # 웹타입 상세/편집
│ │ └── preview/
│ │ └── page.tsx # 웹타입 미리보기
│ └── components/
│ ├── WebTypeList.tsx # 웹타입 목록 컴포넌트
│ ├── WebTypeForm.tsx # 웹타입 생성/편집 폼
│ ├── WebTypePreview.tsx # 웹타입 미리보기
│ └── WebTypeCard.tsx # 웹타입 카드
├── button-actions/
│ ├── page.tsx # 버튼액션 목록
│ ├── new/page.tsx # 새 액션 생성
│ ├── [actionType]/page.tsx # 액션 상세/편집
│ └── components/
│ ├── ActionList.tsx # 액션 목록
│ ├── ActionForm.tsx # 액션 생성/편집 폼
│ └── ActionPreview.tsx # 액션 미리보기
├── style-templates/
│ ├── page.tsx # 스타일 템플릿 목록
│ └── components/
│ ├── TemplateList.tsx # 템플릿 목록
│ ├── TemplateEditor.tsx # 템플릿 편집기
│ └── StylePreview.tsx # 스타일 미리보기
└── grid-standards/
├── page.tsx # 격자 설정 목록
└── components/
├── GridList.tsx # 격자 목록
├── GridEditor.tsx # 격자 편집기
└── GridPreview.tsx # 격자 미리보기
```
#### 2.2 웹타입 관리 페이지 주요 기능
**목록 페이지 (`web-types/page.tsx`)**
- 📋 웹타입 목록 조회 (카테고리별 필터링)
- 🔍 검색 및 정렬 기능
- ✅ 활성화/비활성화 토글
- 🎯 정렬 순서 드래그앤드롭 변경
- 새 웹타입 추가 버튼
**생성/편집 페이지 (`web-types/[webType]/page.tsx`)**
- 📝 기본 정보 입력 (이름, 설명, 카테고리)
- ⚙️ 기본 설정 JSON 편집기
- 🔒 유효성 검사 규칙 설정
- 🎨 기본 스타일 설정
- 🏷️ HTML 속성 설정
- 👀 실시간 미리보기
**미리보기 페이지 (`web-types/[webType]/preview/page.tsx`)**
- 📱 다양한 화면 크기별 미리보기
- 🎭 여러 테마 적용 테스트
- 📊 설정값별 렌더링 결과 확인
#### 2.3 버튼액션 관리 페이지 주요 기능
**목록 페이지 (`button-actions/page.tsx`)**
- 📋 액션 목록 조회 (카테고리별 필터링)
- 🏷️ 액션별 기본 설정 미리보기
- ✅ 활성화/비활성화 관리
- 🎯 정렬 순서 관리
**생성/편집 페이지 (`button-actions/[actionType]/page.tsx`)**
- 📝 기본 정보 (이름, 설명, 카테고리)
- 🎨 기본 스타일 (텍스트, 아이콘, 색상, 변형)
- ⚠️ 확인 메시지 설정
- 🔒 실행 조건 및 검증 규칙
- ⚙️ 액션별 추가 설정 (JSON)
### 📅 Phase 3: 컴포넌트 분리 및 등록 (3-4주)
#### 3.1 기존 웹타입 컴포넌트 분리
**Before (기존 구조)**:
```typescript
// RealtimePreview.tsx - 970줄의 거대한 switch문
const renderWidget = (component: ComponentData) => {
switch (widgetType) {
case "text":
return <Input type="text" {...commonProps} />;
case "number":
return <Input type="number" {...commonProps} />;
case "date":
return <Input type="date" {...commonProps} />;
// ... 25개 케이스
}
};
```
**After (새로운 구조)**:
```
frontend/components/screen/widgets/
├── base/
│ ├── BaseWebTypeComponent.tsx # 기본 웹타입 컴포넌트
│ ├── WebTypeProps.ts # 공통 프로퍼티 인터페이스
│ └── WebTypeHooks.ts # 공통 훅
├── input/
│ ├── TextWidget.tsx # 텍스트 입력
│ ├── NumberWidget.tsx # 숫자 입력
│ ├── DecimalWidget.tsx # 소수 입력
│ ├── DateWidget.tsx # 날짜 입력
│ ├── DateTimeWidget.tsx # 날짜시간 입력
│ ├── EmailWidget.tsx # 이메일 입력
│ ├── TelWidget.tsx # 전화번호 입력
│ └── TextareaWidget.tsx # 텍스트영역
├── select/
│ ├── SelectWidget.tsx # 선택박스
│ ├── DropdownWidget.tsx # 드롭다운
│ ├── RadioWidget.tsx # 라디오버튼
│ ├── CheckboxWidget.tsx # 체크박스
│ └── BooleanWidget.tsx # 불린 선택
├── special/
│ ├── FileWidget.tsx # 파일 업로드
│ ├── CodeWidget.tsx # 공통코드
│ ├── EntityWidget.tsx # 엔티티 참조
│ └── ButtonWidget.tsx # 버튼
└── registry/
├── index.ts # 모든 웹타입 등록
└── registerWebTypes.ts # 웹타입 등록 함수
```
**개별 컴포넌트 예시**:
```typescript
// TextWidget.tsx
import React from "react";
import { Input } from "@/components/ui/input";
import { BaseWebTypeComponent } from "../base/BaseWebTypeComponent";
import { TextTypeConfig } from "@/types/screen";
interface TextWidgetProps {
component: WidgetComponent;
value?: string;
onChange?: (value: string) => void;
readonly?: boolean;
}
export const TextWidget: React.FC<TextWidgetProps> = ({
component,
value,
onChange,
readonly = false,
}) => {
const config = component.webTypeConfig as TextTypeConfig;
return (
<BaseWebTypeComponent component={component}>
<Input
type="text"
value={value || ""}
onChange={(e) => onChange?.(e.target.value)}
placeholder={component.placeholder || config?.placeholder}
disabled={readonly}
maxLength={config?.maxLength}
minLength={config?.minLength}
pattern={config?.pattern}
className="w-full h-full"
/>
</BaseWebTypeComponent>
);
};
```
#### 3.2 설정 패널 분리
```
frontend/components/screen/config-panels/
├── base/
│ ├── BaseConfigPanel.tsx # 기본 설정 패널
│ ├── ConfigPanelProps.ts # 공통 프로퍼티
│ └── ConfigPanelHooks.ts # 공통 훅
├── input/
│ ├── TextConfigPanel.tsx # 텍스트 설정
│ ├── NumberConfigPanel.tsx # 숫자 설정
│ ├── DateConfigPanel.tsx # 날짜 설정
│ └── TextareaConfigPanel.tsx # 텍스트영역 설정
├── select/
│ ├── SelectConfigPanel.tsx # 선택박스 설정
│ ├── RadioConfigPanel.tsx # 라디오 설정
│ └── CheckboxConfigPanel.tsx # 체크박스 설정
├── special/
│ ├── FileConfigPanel.tsx # 파일 설정
│ ├── CodeConfigPanel.tsx # 코드 설정
│ ├── EntityConfigPanel.tsx # 엔티티 설정
│ └── ButtonConfigPanel.tsx # 버튼 설정 (기존 이전)
└── registry/
└── registerConfigPanels.ts # 설정 패널 등록
```
#### 3.3 웹타입 등록 시스템
```typescript
// frontend/lib/registry/registerWebTypes.ts
import { WebTypeRegistry } from "./WebTypeRegistry";
// 입력 타입 등록
import { TextWidget } from "@/components/screen/widgets/input/TextWidget";
import { TextConfigPanel } from "@/components/screen/config-panels/input/TextConfigPanel";
export const registerAllWebTypes = async () => {
// 데이터베이스에서 웹타입 설정 조회
const webTypeSettings = await fetch("/api/screen/web-types?active=Y").then(
(r) => r.json()
);
// 각 웹타입별 컴포넌트 매핑
const componentMap = {
text: { component: TextWidget, configPanel: TextConfigPanel },
number: { component: NumberWidget, configPanel: NumberConfigPanel },
// ... 기타 매핑
};
// 웹타입 등록
webTypeSettings.forEach((setting) => {
const components = componentMap[setting.webType];
if (components) {
WebTypeRegistry.register({
webType: setting.webType,
name: setting.typeName,
category: setting.category,
defaultConfig: setting.defaultConfig,
validationRules: setting.validationRules,
defaultStyle: setting.defaultStyle,
inputProperties: setting.inputProperties,
component: components.component,
configPanel: components.configPanel,
sortOrder: setting.sortOrder,
isActive: setting.isActive === "Y",
});
}
});
};
```
### 📅 Phase 4: 화면관리 시스템 연동 (2-3주)
#### 4.1 화면 설계 시 동적 웹타입 사용
**ScreenDesigner.tsx 수정**:
```typescript
const ScreenDesigner = () => {
// 동적 웹타입/버튼액션 조회
const { data: webTypes } = useWebTypes({ active: "Y" });
const { data: buttonActions } = useButtonActions({ active: "Y" });
// 웹타입 드롭다운 옵션 동적 생성
const webTypeOptions = useMemo(() => {
return (
webTypes?.map((type) => ({
value: type.webType,
label: type.typeName,
category: type.category,
icon: type.icon,
})) || []
);
}, [webTypes]);
// 카테고리별 그룹화
const webTypesByCategory = useMemo(() => {
return webTypeOptions.reduce((acc, type) => {
if (!acc[type.category]) acc[type.category] = [];
acc[type.category].push(type);
return acc;
}, {});
}, [webTypeOptions]);
// 버튼 액션 옵션 동적 생성
const buttonActionOptions = useMemo(() => {
return (
buttonActions?.map((action) => ({
value: action.actionType,
label: action.actionName,
category: action.category,
icon: action.defaultIcon,
color: action.defaultColor,
})) || []
);
}, [buttonActions]);
// ...기존 로직
};
```
**PropertiesPanel.tsx 수정**:
```typescript
const PropertiesPanel = ({ component, onUpdateComponent }) => {
const webTypes = WebTypeRegistry.getAll();
return (
<div>
{/* 웹타입 선택 드롭다운 */}
<Select
value={component.widgetType}
onValueChange={(value) => onUpdateComponent({ widgetType: value })}
>
{Object.entries(
webTypes.reduce((acc, type) => {
if (!acc[type.category]) acc[type.category] = [];
acc[type.category].push(type);
return acc;
}, {})
).map(([category, types]) => (
<SelectGroup key={category}>
<SelectLabel>{category}</SelectLabel>
{types.map((type) => (
<SelectItem key={type.webType} value={type.webType}>
<div className="flex items-center gap-2">
{type.icon && <type.icon className="h-4 w-4" />}
{type.name}
</div>
</SelectItem>
))}
</SelectGroup>
))}
</Select>
{/* 동적 설정 패널 */}
<DynamicConfigPanel
component={component}
onUpdateComponent={onUpdateComponent}
/>
</div>
);
};
```
#### 4.2 동적 렌더링 시스템 적용
**RealtimePreview.tsx 대폭 간소화**:
```typescript
// Before: 970줄의 거대한 switch문
const renderWidget = (component: ComponentData) => {
switch (widgetType) {
case "text": /* 복잡한 로직 */
case "number": /* 복잡한 로직 */
// ... 25개 케이스
}
};
// After: 간단한 동적 렌더링
const renderWidget = (component: ComponentData) => {
if (component.type !== "widget") {
return <div className="text-xs text-gray-500">위젯이 아닙니다</div>;
}
return (
<DynamicWebTypeRenderer
widgetType={component.widgetType}
component={component}
isSelected={isSelected}
onClick={onClick}
/>
);
};
```
**InteractiveScreenViewer.tsx 업데이트**:
```typescript
const InteractiveScreenViewer = ({ component, formData, onFormDataChange }) => {
const renderInteractiveWidget = (comp) => {
if (comp.type !== "widget") return null;
return (
<DynamicWebTypeRenderer
widgetType={comp.widgetType}
component={comp}
value={formData[comp.columnName]}
onChange={(value) => onFormDataChange(comp.columnName, value)}
readonly={comp.readonly}
/>
);
};
// 기존 switch문 제거, 동적 렌더링으로 대체
return renderInteractiveWidget(component);
};
```
### 📅 Phase 5: 테스트 및 최적화 (1-2주)
#### 5.1 기능 테스트 체크리스트
**관리 페이지 테스트**:
- [ ] 웹타입 생성/수정/삭제 기능
- [ ] 버튼액션 생성/수정/삭제 기능
- [ ] 활성화/비활성화 토글 기능
- [ ] 정렬 순서 변경 기능
- [ ] 설정값 변경 시 실시간 미리보기
- [ ] JSON 설정 유효성 검사
- [ ] 다국어 지원 테스트
**화면관리 시스템 테스트**:
- [ ] 동적 웹타입 드롭다운 표시
- [ ] 새로 추가된 웹타입 정상 렌더링
- [ ] 설정 변경 시 실시간 반영
- [ ] 기존 화면과의 호환성 확인
- [ ] 웹타입별 설정 패널 정상 동작
- [ ] 버튼 액션 동적 처리 확인
**성능 테스트**:
- [ ] 웹타입 정보 로딩 속도
- [ ] 대량 컴포넌트 렌더링 성능
- [ ] 메모리 사용량 최적화
- [ ] 불필요한 리렌더링 방지
#### 5.2 성능 최적화
**웹타입 정보 캐싱**:
```typescript
// React Query를 활용한 캐싱
const useWebTypes = (params = {}) => {
return useQuery({
queryKey: ["webTypes", params],
queryFn: () => fetchWebTypes(params),
staleTime: 5 * 60 * 1000, // 5분간 캐시 유지
cacheTime: 10 * 60 * 1000, // 10분간 메모리 보관
});
};
```
**컴포넌트 Lazy Loading**:
```typescript
// 웹타입 컴포넌트 지연 로딩
const LazyTextWidget = React.lazy(() => import("./widgets/input/TextWidget"));
const LazyNumberWidget = React.lazy(
() => import("./widgets/input/NumberWidget")
);
const DynamicWebTypeRenderer = ({ widgetType, ...props }) => {
const Component = useMemo(() => {
const definition = WebTypeRegistry.get(widgetType);
return definition?.component;
}, [widgetType]);
if (!Component) return <div>알 수 없는 웹타입</div>;
return (
<Suspense fallback={<div>로딩 중...</div>}>
<Component {...props} />
</Suspense>
);
};
```
**불필요한 리렌더링 방지**:
```typescript
// React.memo를 활용한 최적화
export const DynamicWebTypeRenderer = React.memo(
({ widgetType, component, ...props }) => {
// 렌더링 로직
},
(prevProps, nextProps) => {
// 얕은 비교로 리렌더링 최소화
return (
prevProps.widgetType === nextProps.widgetType &&
prevProps.component.id === nextProps.component.id &&
JSON.stringify(prevProps.component.webTypeConfig) ===
JSON.stringify(nextProps.component.webTypeConfig)
);
}
);
```
## 기대 효과
### 🚀 개발자 경험 개선
#### Before (기존 방식)
새로운 웹타입 '전화번호' 추가 시:
1. `types/screen.ts`에 타입 추가
2. `RealtimePreview.tsx`에 switch case 추가 (50줄)
3. `InteractiveScreenViewer.tsx`에 switch case 추가 (30줄)
4. `PropertiesPanel.tsx`에 설정 로직 추가 (100줄)
5. `ButtonConfigPanel.tsx`에 옵션 추가 (20줄)
6. 다국어 파일 업데이트
7. 테스트 코드 작성
8. **총 200줄+ 코드 수정, 7개 파일 변경**
#### After (새로운 방식)
새로운 웹타입 '전화번호' 추가 시:
1. **관리 페이지에서 웹타입 등록** (클릭만으로!)
2. **컴포넌트 파일 1개만 작성** (20줄):
```typescript
// PhoneWidget.tsx
export const PhoneWidget = ({ component, value, onChange, readonly }) => {
const config = component.webTypeConfig as PhoneTypeConfig;
return (
<Input
type="tel"
value={value}
onChange={(e) => onChange(e.target.value)}
placeholder={config.placeholder || "전화번호를 입력하세요"}
pattern={config.pattern || "[0-9]{3}-[0-9]{4}-[0-9]{4}"}
disabled={readonly}
/>
);
};
// 등록 (앱 초기화 시)
WebTypeRegistry.register({
webType: "phone",
name: "전화번호",
component: PhoneWidget,
configPanel: PhoneConfigPanel,
defaultConfig: { placeholder: "전화번호를 입력하세요" },
});
```
3. **총 20줄 코드 작성, 1개 파일 생성**
### 📈 비개발자 업무 효율성
#### 시스템 관리자 / 기획자가 할 수 있는 일
- ✅ 새로운 웹타입 추가 (개발자 도움 없이)
- ✅ 웹타입별 기본 설정 변경
- ✅ 버튼 액션 커스터마이징
- ✅ 스타일 템플릿 관리
- ✅ 회사별/프로젝트별 커스텀 설정
- ✅ A/B 테스트를 위한 임시 설정 변경
#### 실시간 설정 변경 시나리오
```
시나리오: 고객사 요청으로 '이메일' 입력 필드의 기본 검증 규칙 변경
Before (기존):
1. 개발자가 코드 수정
2. 테스트 환경 배포
3. 검수 후 운영 배포
4. 소요 시간: 1-2일
After (새로운):
1. 관리자가 웹타입 관리 페이지 접속
2. '이메일' 웹타입 편집
3. 검증 규칙 JSON 수정
4. 저장 → 즉시 반영
5. 소요 시간: 2-3분
```
### 🔧 확장성 및 유지보수성
#### 플러그인 방식의 장점
- **독립적 개발**: 각 웹타입별 독립적인 개발 및 테스트
- **점진적 확장**: 필요에 따른 점진적 기능 추가
- **버전 관리**: 웹타입별 버전 관리 가능
- **A/B 테스트**: 다른 구현체로 쉬운 교체 가능
- **재사용성**: 다른 프로젝트에서 컴포넌트 재사용
#### 코드 품질 향상
- **관심사 분리**: 각 웹타입별 로직 분리
- **테스트 용이성**: 작은 단위 컴포넌트 테스트
- **코드 리뷰**: 작은 단위로 리뷰 가능
- **문서화**: 웹타입별 독립적인 문서화
### ⚡ 성능 최적화
#### 지연 로딩 (Lazy Loading)
- 사용하지 않는 웹타입 컴포넌트는 로딩하지 않음
- 초기 번들 크기 50% 이상 감소 예상
- 화면 로딩 속도 향상
#### 효율적인 캐싱
- 웹타입 설정 정보는 앱 시작 시 한 번만 로딩
- 변경 시에만 갱신하는 무효화 정책
- 메모리 사용량 최적화
#### 렌더링 최적화
- 웹타입별 최적화된 렌더링 로직
- 불필요한 리렌더링 방지
- Virtual DOM 업데이트 최소화
## 실행 일정
### 📅 전체 일정표
| Phase | 기간 | 주요 작업 | 담당자 | 산출물 |
| ----------- | ---------- | ---------------- | ----------------- | ---------------------- |
| **Phase 1** | 1-2주 | 기반 구조 구축 | 백엔드/프론트엔드 | API, 레지스트리 시스템 |
| **Phase 2** | 2-3주 | 설정 관리 페이지 | 프론트엔드 | 관리 페이지 UI |
| **Phase 3** | 3-4주 | 컴포넌트 분리 | 프론트엔드 | 웹타입 컴포넌트들 |
| **Phase 4** | 2-3주 | 화면관리 연동 | 프론트엔드 | 동적 렌더링 시스템 |
| **Phase 5** | 1-2주 | 테스트/최적화 | 전체 팀 | 완성된 시스템 |
| **총 기간** | **9-14주** | | | |
### 🎯 마일스톤
#### Milestone 1 (2주 후)
- ✅ 데이터베이스 스키마 적용
- ✅ Backend API 완성
- ✅ 웹타입 레지스트리 시스템 구축
- ✅ 기본 관리 페이지 프레임워크
#### Milestone 2 (5주 후)
- ✅ 웹타입 관리 페이지 완성
- ✅ 버튼액션 관리 페이지 완성
- ✅ 스타일 템플릿 관리 페이지 완성
- ✅ 실시간 미리보기 기능
#### Milestone 3 (9주 후)
- ✅ 모든 웹타입 컴포넌트 분리 완성
- ✅ 설정 패널 분리 완성
- ✅ 웹타입 등록 시스템 완성
- ✅ 기존 화면과의 호환성 확보
#### Milestone 4 (12주 후)
- ✅ 화면관리 시스템 동적 연동 완성
- ✅ RealtimePreview/InteractiveScreenViewer 개선
- ✅ PropertiesPanel 동적 업데이트
- ✅ 성능 최적화 적용
#### Final Release (14주 후)
- ✅ 전체 시스템 통합 테스트 완료
- ✅ 성능 최적화 완료
- ✅ 문서화 완료
- ✅ 운영 배포 준비 완료
### 🚀 우선순위별 실행 전략
#### 🔥 즉시 시작 가능 (우선순위 High)
1. **데이터베이스 스키마 적용**
- 이미 준비된 SQL 파일 실행
- 기본 데이터 확인 및 검증
2. **Backend API 개발**
- 표준적인 CRUD API 구현
- 기존 패턴 재사용 가능
3. **웹타입 레지스트리 시스템**
- 핵심 아키텍처 구성요소
- 다른 모든 기능의 기반
#### 📋 병렬 진행 가능 (우선순위 Medium)
1. **관리 페이지 UI 개발**
- Backend API와 독립적으로 개발 가능
- 목업 데이터로 프로토타입 제작
2. **기존 컴포넌트 분석 및 분리 계획**
- 현재 RealtimePreview 분석
- 컴포넌트 분리 전략 수립
#### ⏳ 순차 진행 필요 (우선순위 Low)
1. **화면관리 시스템 연동**
- 웹타입 컴포넌트 분리 완료 후 진행
- 레지스트리 시스템 안정화 후 진행
2. **성능 최적화**
- 전체 시스템 완성 후 진행
- 실제 사용 패턴 분석 후 최적화
### 💡 성공을 위한 핵심 요소
#### 기술적 성공 요소
- **점진적 마이그레이션**: 기존 시스템과의 호환성 유지
- **철저한 테스트**: 각 단계별 충분한 테스트
- **성능 모니터링**: 성능 저하 없는 기능 확장
- **에러 핸들링**: 견고한 에러 처리 로직
#### 조직적 성공 요소
- **명확한 역할 분담**: 개발자/기획자/관리자 역할 정의
- **충분한 교육**: 새로운 시스템 사용법 교육
- **단계적 도입**: 파일럿 테스트 후 전면 도입
- **피드백 수집**: 사용자 피드백 기반 개선
---
## 🎉 결론
이 계획을 통해 화면관리 시스템은 **하드코딩된 정적 시스템**에서 **유연하고 확장 가능한 동적 시스템**으로 진화할 것입니다.
### 핵심 성과 지표
- **개발 효율성**: 새 웹타입 추가 시간 **95% 단축** (2일 → 2시간)
- **시스템 유연성**: **비개발자도 설정 변경 가능**
- **코드 품질**: **관심사 분리**로 유지보수성 **대폭 향상**
- **성능**: **지연 로딩**으로 초기 로딩 시간 **50% 이상 개선**
### 장기적 비전
- **플러그인 생태계**: 커뮤니티 기반 웹타입 확장
- **AI 기반 최적화**: 사용 패턴 기반 자동 설정 추천
- **마켓플레이스**: 웹타입/템플릿 공유 플랫폼
- **다중 플랫폼**: 모바일/데스크톱 앱에서도 동일한 시스템 사용
**이제 미래 지향적이고 확장 가능한 화면관리 시스템을 구축할 준비가 완료되었습니다!** 🚀