From aa969f0cb2da4eceaec0ae6c65fda1dfbf590ed8 Mon Sep 17 00:00:00 2001 From: kjs Date: Mon, 1 Sep 2025 10:19:47 +0900 Subject: [PATCH] =?UTF-8?q?=ED=99=94=EB=A9=B4=EA=B4=80=EB=A6=AC=20?= =?UTF-8?q?=EC=84=A4=EA=B3=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/화면관리_시스템_설계.md | 530 +++++++++++++++++++++++++++++------ 1 file changed, 448 insertions(+), 82 deletions(-) diff --git a/docs/화면관리_시스템_설계.md b/docs/화면관리_시스템_설계.md index d39421a8..69b96058 100644 --- a/docs/화면관리_시스템_설계.md +++ b/docs/화면관리_시스템_설계.md @@ -19,15 +19,16 @@ ### 화면관리 시스템이란? -화면관리 시스템은 실제 서비스되는 화면을 드래그앤드롭으로 설계하고 관리할 수 있는 시스템입니다. 테이블 타입관리와 연계하여 각 필드가 웹에서 어떻게 표시될지를 정의하고, 사용자가 직관적으로 화면을 구성할 수 있습니다. +화면관리 시스템은 사용자가 속한 회사에 맞춰 화면을 드래그앤드롭으로 설계하고 관리할 수 있는 시스템입니다. 테이블 타입관리와 연계하여 각 필드가 웹에서 어떻게 표시될지를 정의하고, 사용자가 직관적으로 화면을 구성할 수 있습니다. ### 주요 특징 +- **회사별 화면 관리**: 사용자 회사 코드에 따른 화면 접근 제어 - **드래그앤드롭 인터페이스**: 직관적인 화면 설계 +- **컨테이너 그룹화**: 컴포넌트를 깔끔하게 정렬하는 그룹 기능 - **테이블 타입 연계**: 컬럼의 웹 타입에 따른 자동 위젯 생성 -- **실시간 미리보기**: 설계한 화면을 즉시 확인 가능 -- **반응형 디자인**: 다양한 화면 크기에 대응 -- **템플릿 시스템**: 재사용 가능한 화면 템플릿 제공 +- **실시간 미리보기**: 설계한 화면을 실제 화면과 동일하게 확인 가능 +- **메뉴 연동**: 각 회사의 메뉴에 화면 할당 및 관리 ### 🎯 **현재 테이블 구조와 100% 호환** @@ -42,6 +43,15 @@ **별도의 테이블 구조 변경 없이 바로 개발 가능!** 🚀 +### 🏢 **회사별 화면 관리 시스템** + +**사용자 권한에 따른 화면 접근 제어:** + +- ✅ **일반 사용자**: 자신이 속한 회사의 화면만 제작/수정 가능 +- ✅ **관리자 (회사코드 '\*')**: 모든 회사의 화면을 제어 가능 +- ✅ **회사별 메뉴 할당**: 각 회사의 메뉴에만 화면 할당 가능 +- ✅ **권한 격리**: 회사 간 화면 데이터 완전 분리 + ### 지원하는 웹 타입 테이블 타입관리에서 각 컬럼별로 설정할 수 있는 웹 타입입니다: @@ -118,6 +128,25 @@ └─────────────────┘ └─────────────────┘ └─────────────────┘ ``` +### 회사별 권한 관리 구조 + +``` +┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ +│ 사용자 │ │ 권한 검증 │ │ 화면 데이터 │ +│ │ │ │ │ │ +│ ┌─────────────┐ │ │ ┌─────────────┐ │ │ ┌─────────────┐ │ +│ │ 회사코드 │ │───▶│ │ 권한 검증 │ │───▶│ │ 회사별 │ │ +│ │ (company_ │ │ │ │ 미들웨어 │ │ │ │ 화면 │ │ +│ │ code) │ │ │ │ │ │ │ │ 격리 │ │ +│ └─────────────┘ │ │ └─────────────┘ │ │ └─────────────┘ │ +│ │ │ │ │ │ +│ ┌─────────────┐ │ │ ┌─────────────┐ │ │ ┌─────────────┐ │ +│ │ 권한 레벨 │ │ │ │ 회사별 │ │ │ │ 메뉴 할당 │ │ +│ │ (admin: '*')│ │ │ │ 데이터 │ │ │ │ 제한 │ │ +│ └─────────────┘ │ │ │ 필터링 │ │ │ └─────────────┘ │ +└─────────────────┘ └─────────────────┘ └─────────────────┘ +``` + ### 데이터 흐름 1. **테이블 타입 정의**: 테이블 타입관리에서 컬럼의 웹 타입 설정 @@ -132,28 +161,42 @@ - **드래그앤드롭 인터페이스**: 컴포넌트를 캔버스에 배치 - **그리드 시스템**: 12컬럼 그리드 기반 레이아웃 -- **반응형 설정**: 화면 크기별 레이아웃 조정 -- **실시간 미리보기**: 설계한 화면을 즉시 확인 +- **컨테이너 그룹화**: 컴포넌트를 깔끔하게 정렬하는 그룹 기능 +- **실시간 미리보기**: 설계한 화면을 실제 화면과 동일하게 확인 ### 2. 컴포넌트 라이브러리 - **입력 컴포넌트**: text, number, date, textarea 등 - **선택 컴포넌트**: select, checkbox, radio 등 - **표시 컴포넌트**: label, display, image 등 -- **레이아웃 컴포넌트**: container, row, column 등 +- **레이아웃 컴포넌트**: container, row, column, group 등 +- **컨테이너 컴포넌트**: 컴포넌트들을 그룹으로 묶는 기능 -### 3. 테이블 연계 시스템 +### 3. 회사별 권한 관리 + +- **회사 코드 기반 접근 제어**: 사용자 회사 코드에 따른 화면 접근 +- **관리자 권한**: 회사 코드 '\*'인 사용자는 모든 회사 화면 제어 +- **회사별 메뉴 할당**: 각 회사의 메뉴에만 화면 할당 가능 +- **데이터 격리**: 회사 간 화면 데이터 완전 분리 + +### 4. 테이블 연계 시스템 - **자동 위젯 생성**: 컬럼의 웹 타입에 따른 위젯 자동 생성 - **데이터 바인딩**: 컬럼과 위젯의 자동 연결 - **유효성 검증**: 컬럼 설정에 따른 자동 검증 규칙 적용 -### 4. 템플릿 시스템 +### 5. 템플릿 시스템 - **기본 템플릿**: CRUD, 목록, 상세 등 기본 패턴 - **사용자 정의 템플릿**: 자주 사용하는 레이아웃 저장 - **템플릿 공유**: 팀원 간 템플릿 공유 및 재사용 +### 6. 메뉴 연동 시스템 + +- **회사별 메뉴 할당**: 각 회사의 메뉴에만 화면 할당 +- **메뉴-화면 연결**: 메뉴와 화면의 1:1 또는 1:N 연결 +- **권한 기반 메뉴 표시**: 사용자 권한에 따른 메뉴 표시 제어 + ## 🗄️ 데이터베이스 설계 ### 1. 기존 테이블 구조 (테이블 타입관리) @@ -221,6 +264,7 @@ CREATE TABLE screen_definitions ( screen_name VARCHAR(100) NOT NULL, screen_code VARCHAR(50) UNIQUE NOT NULL, table_name VARCHAR(100) NOT NULL, -- 🎯 table_labels와 연계 + company_code VARCHAR(50) NOT NULL, -- 🎯 회사 코드 (권한 관리용) description TEXT, is_active CHAR(1) DEFAULT 'Y', created_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP, @@ -232,6 +276,9 @@ CREATE TABLE screen_definitions ( CONSTRAINT fk_screen_definitions_table_name FOREIGN KEY (table_name) REFERENCES table_labels(table_name) ); + +-- 회사 코드 인덱스 추가 +CREATE INDEX idx_screen_definitions_company_code ON screen_definitions(company_code); ``` #### screen_layouts (화면 레이아웃) @@ -283,12 +330,43 @@ CREATE TABLE screen_templates ( template_id SERIAL PRIMARY KEY, template_name VARCHAR(100) NOT NULL, template_type VARCHAR(50) NOT NULL, -- CRUD, LIST, DETAIL 등 + company_code VARCHAR(50) NOT NULL, -- 🎯 회사 코드 (권한 관리용) description TEXT, layout_data JSONB, -- 레이아웃 데이터 is_public BOOLEAN DEFAULT FALSE, -- 공개 여부 created_by VARCHAR(50), created_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); + +-- 회사 코드 인덱스 추가 +CREATE INDEX idx_screen_templates_company_code ON screen_templates(company_code); +``` + +#### screen_menu_assignments (화면-메뉴 할당) + +```sql +CREATE TABLE screen_menu_assignments ( + assignment_id SERIAL PRIMARY KEY, + screen_id INTEGER NOT NULL, + menu_id INTEGER NOT NULL, + company_code VARCHAR(50) NOT NULL, -- 🎯 회사 코드 (권한 관리용) + display_order INTEGER DEFAULT 0, + is_active CHAR(1) DEFAULT 'Y', + created_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + created_by VARCHAR(50), + + -- 외래키 제약조건 + CONSTRAINT fk_screen_menu_assignments_screen_id + FOREIGN KEY (screen_id) REFERENCES screen_definitions(screen_id), + CONSTRAINT fk_screen_menu_assignments_menu_id + FOREIGN KEY (menu_id) REFERENCES menu_info(menu_id), + + -- 유니크 제약조건 (한 메뉴에 같은 화면 중복 할당 방지) + CONSTRAINT uk_screen_menu_company UNIQUE (screen_id, menu_id, company_code) +); + +-- 회사 코드 인덱스 추가 +CREATE INDEX idx_screen_menu_assignments_company_code ON screen_menu_assignments(company_code); ``` ### 3. 테이블 간 연계 관계 @@ -303,6 +381,8 @@ screen_definitions (화면 정의) screen_layouts (화면 레이아웃) ↓ (1:N) screen_widgets (화면 위젯) + ↓ (1:N) +screen_menu_assignments (화면-메뉴 할당) ``` **핵심 연계 포인트:** @@ -310,6 +390,8 @@ screen_widgets (화면 위젯) - ✅ `screen_definitions.table_name` ↔ `table_labels.table_name` - ✅ `screen_widgets.table_name, column_name` ↔ `column_labels.table_name, column_name` - ✅ `screen_widgets.widget_type` ↔ `column_labels.web_type` (자동 동기화) +- ✅ `screen_definitions.company_code` ↔ 사용자 회사 코드 (권한 관리) +- ✅ `screen_menu_assignments.company_code` ↔ 메뉴 회사 코드 (메뉴 할당 제한) ## 🎨 화면 구성 요소 @@ -327,6 +409,29 @@ interface ContainerProps { margin: number; backgroundColor?: string; border?: string; + borderRadius?: number; + shadow?: string; +} +``` + +#### Group (그룹) + +```typescript +interface GroupProps { + id: string; + type: "group"; + title?: string; + width: number; // 1-12 + height: number; + padding: number; + margin: number; + backgroundColor?: string; + border?: string; + borderRadius?: number; + shadow?: string; + collapsible?: boolean; // 접을 수 있는 그룹 + collapsed?: boolean; // 접힌 상태 + children: string[]; // 포함된 컴포넌트 ID 목록 } ``` @@ -440,16 +545,27 @@ interface DragState { draggedItem: ComponentData | null; dragSource: "toolbox" | "canvas"; dropTarget: string | null; + dropZone?: DropZone; // 드롭 가능한 영역 정보 } +// 그룹화 상태 관리 +interface GroupState { + isGrouping: boolean; + selectedComponents: string[]; + groupTarget: string | null; + groupMode: "create" | "add" | "remove"; +} +``` + // 드롭 영역 정의 interface DropZone { - id: string; - accepts: string[]; // 허용되는 컴포넌트 타입 - position: { x: number; y: number }; - size: { width: number; height: number }; +id: string; +accepts: string[]; // 허용되는 컴포넌트 타입 +position: { x: number; y: number }; +size: { width: number; height: number }; } -``` + +```` ### 2. 컴포넌트 배치 로직 @@ -478,7 +594,7 @@ function resizeComponent( height: Math.max(50, newHeight), }; } -``` +```` ### 3. 실시간 미리보기 @@ -492,21 +608,40 @@ function generatePreview(layout: LayoutData): React.ReactElement { ); } -// 컴포넌트 렌더링 -function renderComponent(component: ComponentData): React.ReactElement { - switch (component.type) { - case "text": - return ; - case "select": - return ; +case "date": +return ; +default: +return
Unknown component
; +} +} + +```` + ## 🔗 테이블 타입 연계 ### 1. 웹 타입 설정 방법 @@ -552,7 +687,7 @@ async function getTableColumnWebTypes( ): Promise { return api.get(`/table-management/tables/${tableName}/columns/web-types`); } -``` +```` #### 웹 타입별 추가 설정 (현재 테이블 구조 기반) @@ -798,10 +933,15 @@ function generateValidationRules(column: ColumnInfo): ValidationRule[] { ### 1. 화면 정의 API -#### 화면 목록 조회 +#### 화면 목록 조회 (회사별) ```typescript GET /api/screen-management/screens +Query: { + companyCode?: string; // 회사 코드 (관리자는 생략 가능) + page?: number; + size?: number; +} Response: { success: boolean; data: ScreenDefinition[]; @@ -809,6 +949,19 @@ Response: { } ``` +#### 화면 생성 (회사별) + +```typescript +POST /api/screen-management/screens +Body: { + screenName: string; + screenCode: string; + tableName: string; + companyCode: string; // 사용자 회사 코드 자동 설정 + description?: string; +} +``` + #### 화면 생성 ```typescript @@ -896,17 +1049,44 @@ Body: { ### 4. 템플릿 API -#### 템플릿 목록 조회 +#### 템플릿 목록 조회 (회사별) ```typescript GET /api/screen-management/templates Query: { + companyCode?: string; // 회사 코드 (관리자는 생략 가능) type?: string; isPublic?: boolean; createdBy?: string; } ``` +### 5. 메뉴 할당 API + +#### 화면-메뉴 할당 + +```typescript +POST /api/screen-management/screens/:screenId/menu-assignments +Body: { + menuId: number; + companyCode: string; // 사용자 회사 코드 자동 설정 + displayOrder?: number; +} +``` + +#### 메뉴별 화면 목록 조회 + +```typescript +GET /api/screen-management/menus/:menuId/screens +Query: { + companyCode: string; // 회사 코드 필수 +} +Response: { + success: boolean; + data: ScreenDefinition[]; +} +``` + #### 템플릿 적용 ```typescript @@ -934,37 +1114,88 @@ export default function ScreenDesigner() { dragSource: "toolbox", dropTarget: null, }); + const [groupState, setGroupState] = useState({ + isGrouping: false, + selectedComponents: [], + groupTarget: null, + groupMode: "create", + }); + const [userCompanyCode, setUserCompanyCode] = useState(""); +``` + +// 컴포넌트 추가 +const addComponent = (component: ComponentData) => { +setLayout((prev) => ({ +...prev, +components: [...prev.components, component], +})); +}; + +// 컴포넌트 삭제 +const removeComponent = (componentId: string) => { +setLayout((prev) => ({ +...prev, +components: prev.components.filter((c) => c.id !== componentId), +})); +}; + +// 컴포넌트 이동 +const moveComponent = (componentId: string, newPosition: Position) => { +setLayout((prev) => ({ +...prev, +components: prev.components.map((c) => +c.id === componentId ? { ...c, position: newPosition } : c +), +})); +}; + +// 컴포넌트 그룹화 +const groupComponents = (componentIds: string[], groupTitle?: string) => { +const groupId = generateId(); +const groupComponent: GroupProps = { +id: groupId, +type: "group", +title: groupTitle || "그룹", +width: 12, +height: 200, +padding: 16, +margin: 8, +backgroundColor: "#f8f9fa", +border: "1px solid #dee2e6", +borderRadius: 8, +shadow: "0 2px 4px rgba(0,0,0,0.1)", +collapsible: true, +collapsed: false, +children: componentIds, +}; - // 컴포넌트 추가 - const addComponent = (component: ComponentData) => { setLayout((prev) => ({ ...prev, - components: [...prev.components, component], + components: [...prev.components, groupComponent], })); - }; - // 컴포넌트 삭제 - const removeComponent = (componentId: string) => { - setLayout((prev) => ({ - ...prev, - components: prev.components.filter((c) => c.id !== componentId), - })); - }; +}; - // 컴포넌트 이동 - const moveComponent = (componentId: string, newPosition: Position) => { - setLayout((prev) => ({ - ...prev, - components: prev.components.map((c) => - c.id === componentId ? { ...c, position: newPosition } : c - ), - })); - }; +// 그룹에서 컴포넌트 제거 +const ungroupComponent = (componentId: string, groupId: string) => { +setLayout((prev) => ({ +...prev, +components: prev.components.map((c) => { +if (c.id === groupId && c.type === "group") { +return { +...c, +children: c.children.filter((id) => id !== componentId), +}; +} +return c; +}), +})); +}; - return ( -
- - + + - c.id === selectedComponent)} - onPropertyChange={updateComponentProperty} + c.id === selectedComponent)} +onPropertyChange={updateComponentProperty} +onGroupCreate={groupComponents} +onGroupRemove={ungroupComponent} +/> + + - -
- ); + +); } -``` + +```` ### 2. 드래그앤드롭 구현 @@ -1024,7 +1264,7 @@ export function useDragAndDrop() { updateDropTarget, }; } -``` +```` ### 3. 그리드 시스템 @@ -1045,6 +1285,56 @@ export default function GridSystem({ children, columns = 12 }) { ); } +// 그룹화 도구 모음 +export function GroupingToolbar({ + groupState, + onGroupStateChange, + onGroupCreate, + onGroupRemove, +}) { + const handleGroupCreate = () => { + if (groupState.selectedComponents.length > 1) { + const groupTitle = prompt("그룹 제목을 입력하세요:"); + onGroupCreate(groupState.selectedComponents, groupTitle); + onGroupStateChange({ + ...groupState, + isGrouping: false, + selectedComponents: [], + }); + } + }; + + const handleGroupRemove = () => { + if (groupState.groupTarget) { + onGroupRemove(groupState.selectedComponents[0], groupState.groupTarget); + onGroupStateChange({ + ...groupState, + isGrouping: false, + selectedComponents: [], + }); + } + }; + + return ( +
+ + +
+ ); +} + // GridItem.tsx interface GridItemProps { width: number; // 1-12 @@ -1080,15 +1370,22 @@ export default function GridItem({ ```typescript // screenManagementService.ts export class ScreenManagementService { - // 화면 정의 생성 + // 화면 정의 생성 (회사별) async createScreen( - screenData: CreateScreenRequest + screenData: CreateScreenRequest, + userCompanyCode: string ): Promise { + // 권한 검증: 사용자 회사 코드 확인 + if (userCompanyCode !== "*" && userCompanyCode !== screenData.companyCode) { + throw new Error("해당 회사의 화면을 생성할 권한이 없습니다."); + } + const screen = await prisma.screen_definitions.create({ data: { screen_name: screenData.screenName, screen_code: screenData.screenCode, table_name: screenData.tableName, + company_code: screenData.companyCode, description: screenData.description, created_by: screenData.createdBy, }, @@ -1097,6 +1394,30 @@ export class ScreenManagementService { return this.mapToScreenDefinition(screen); } + // 회사별 화면 목록 조회 + async getScreensByCompany( + companyCode: string, + page: number = 1, + size: number = 20 + ): Promise<{ screens: ScreenDefinition[]; total: number }> { + const whereClause = companyCode === "*" ? {} : { company_code: companyCode }; + + const [screens, total] = await Promise.all([ + prisma.screen_definitions.findMany({ + where: whereClause, + skip: (page - 1) * size, + take: size, + orderBy: { created_date: "desc" }, + }), + prisma.screen_definitions.count({ where: whereClause }), + ]); + + return { + screens: screens.map(this.mapToScreenDefinition), + total, + }; + + // 레이아웃 저장 async saveLayout(screenId: number, layoutData: LayoutData): Promise { // 기존 레이아웃 삭제 @@ -1449,7 +1770,23 @@ export class TableTypeIntegrationService { ## 🎬 사용 시나리오 -### 1. 웹 타입 설정 (테이블 타입관리) +### 1. 회사별 화면 관리 + +#### 일반 사용자 (회사 코드: 'COMP001') + +1. **로그인**: 자신의 회사 코드로 시스템 로그인 +2. **화면 목록 조회**: 자신이 속한 회사의 화면만 표시 +3. **화면 생성**: 회사 코드가 자동으로 설정되어 생성 +4. **메뉴 할당**: 자신의 회사 메뉴에만 화면 할당 가능 + +#### 관리자 (회사 코드: '\*') + +1. **로그인**: 관리자 권한으로 시스템 로그인 +2. **전체 화면 조회**: 모든 회사의 화면을 조회/수정 가능 +3. **회사별 화면 관리**: 각 회사별로 화면 생성/수정/삭제 +4. **크로스 회사 메뉴 할당**: 모든 회사의 메뉴에 화면 할당 가능 + +### 2. 웹 타입 설정 (테이블 타입관리) 1. **테이블 선택**: 테이블 타입관리에서 웹 타입을 설정할 테이블 선택 2. **컬럼 관리**: 해당 테이블의 컬럼 목록에서 웹 타입을 설정할 컬럼 선택 @@ -1462,38 +1799,48 @@ export class TableTypeIntegrationService { 5. **저장**: 웹 타입 설정을 데이터베이스에 저장 6. **연계 확인**: 화면관리 시스템에서 자동 위젯 생성 확인 -### 2. 새로운 화면 설계 +### 3. 새로운 화면 설계 1. **테이블 선택**: 테이블 타입관리에서 설계할 테이블 선택 2. **웹 타입 확인**: 각 컬럼의 웹 타입 설정 상태 확인 -3. **화면 생성**: 화면명과 코드를 입력하여 새 화면 생성 +3. **화면 생성**: 화면명과 코드를 입력하여 새 화면 생성 (회사 코드 자동 설정) 4. **자동 위젯 생성**: 컬럼의 웹 타입에 따라 자동으로 위젯 생성 5. **컴포넌트 배치**: 드래그앤드롭으로 컴포넌트를 캔버스에 배치 -6. **속성 설정**: 각 컴포넌트의 속성을 Properties 패널에서 설정 -7. **미리보기**: 실시간으로 설계한 화면 확인 -8. **저장**: 완성된 화면 레이아웃을 데이터베이스에 저장 +6. **컨테이너 그룹화**: 관련 컴포넌트들을 그룹으로 묶어 깔끔하게 정렬 +7. **속성 설정**: 각 컴포넌트의 속성을 Properties 패널에서 설정 +8. **실시간 미리보기**: 설계한 화면을 실제 화면과 동일하게 확인 +9. **저장**: 완성된 화면 레이아웃을 데이터베이스에 저장 -### 2. 기존 화면 수정 +### 4. 기존 화면 수정 -1. **화면 선택**: 수정할 화면을 목록에서 선택 +1. **화면 선택**: 수정할 화면을 목록에서 선택 (권한 확인) 2. **레이아웃 로드**: 기존 레이아웃을 캔버스에 로드 3. **컴포넌트 수정**: 컴포넌트 추가/삭제/이동/수정 -4. **속성 변경**: 컴포넌트 속성 변경 -5. **변경사항 확인**: 미리보기로 변경사항 확인 -6. **저장**: 수정된 레이아웃 저장 +4. **그룹 구조 조정**: 컴포넌트 그룹화/그룹 해제/그룹 속성 변경 +5. **속성 변경**: 컴포넌트 속성 변경 +6. **변경사항 확인**: 실시간 미리보기로 변경사항 확인 +7. **저장**: 수정된 레이아웃 저장 -### 3. 템플릿 활용 +### 5. 템플릿 활용 -1. **템플릿 선택**: 적합한 템플릿을 목록에서 선택 +1. **템플릿 선택**: 적합한 템플릿을 목록에서 선택 (회사별 템플릿) 2. **템플릿 적용**: 선택한 템플릿을 현재 화면에 적용 3. **커스터마이징**: 템플릿을 기반으로 필요한 부분 수정 4. **저장**: 커스터마이징된 화면 저장 -### 4. 화면 배포 +### 6. 메뉴 할당 및 관리 + +1. **메뉴 선택**: 화면을 할당할 메뉴 선택 (회사별 메뉴만 표시) +2. **화면 할당**: 선택한 화면을 메뉴에 할당 +3. **할당 순서 조정**: 메뉴 내 화면 표시 순서 조정 +4. **할당 해제**: 메뉴에서 화면 할당 해제 +5. **권한 확인**: 메뉴 할당 시 회사 코드 일치 여부 확인 + +### 7. 화면 배포 1. **화면 활성화**: 설계 완료된 화면을 활성 상태로 변경 -2. **권한 설정**: 화면 접근 권한 설정 -3. **메뉴 연결**: 메뉴 시스템에 화면 연결 +2. **권한 설정**: 화면 접근 권한 설정 (회사별 권한) +3. **메뉴 연결**: 메뉴 시스템에 화면 연결 (회사별 메뉴) 4. **테스트**: 실제 환경에서 화면 동작 테스트 5. **배포**: 운영 환경에 화면 배포 @@ -1545,6 +1892,25 @@ export class TableTypeIntegrationService { ## 🎯 결론 -화면관리 시스템은 테이블 타입관리와 연계하여 사용자가 직관적으로 웹 화면을 설계할 수 있는 강력한 도구입니다. 드래그앤드롭 인터페이스와 자동 위젯 생성 기능을 통해 개발자가 아닌 사용자도 전문적인 웹 화면을 쉽게 구성할 수 있습니다. +화면관리 시스템은 **회사별 권한 관리**와 **테이블 타입관리 연계**를 통해 사용자가 직관적으로 웹 화면을 설계할 수 있는 강력한 도구입니다. -이 시스템을 통해 ERP 시스템의 화면 개발 생산성을 크게 향상시키고, 사용자 요구사항에 따른 빠른 화면 구성이 가능해질 것입니다. +### 🏢 **회사별 화면 관리의 핵심 가치** + +- **권한 격리**: 사용자는 자신이 속한 회사의 화면만 제작/수정 가능 +- **관리자 통제**: 회사 코드 '\*'인 관리자는 모든 회사의 화면을 제어 +- **메뉴 연동**: 각 회사의 메뉴에만 화면 할당하여 완벽한 데이터 분리 + +### 🎨 **향상된 사용자 경험** + +- **드래그앤드롭 인터페이스**: 직관적인 화면 설계 +- **컨테이너 그룹화**: 컴포넌트를 깔끔하게 정렬하는 그룹 기능 +- **실시간 미리보기**: 설계한 화면을 실제 화면과 동일하게 확인 +- **자동 위젯 생성**: 컬럼의 웹 타입에 따른 스마트한 위젯 생성 + +### 🚀 **기술적 혜택** + +- **기존 테이블 구조 100% 호환**: 별도 스키마 변경 없이 바로 개발 가능 +- **권한 기반 보안**: 회사 간 데이터 완전 격리 +- **확장 가능한 아키텍처**: 새로운 웹 타입과 컴포넌트 쉽게 추가 + +이 시스템을 통해 ERP 시스템의 화면 개발 생산성을 크게 향상시키고, **회사별 맞춤형 화면 구성**과 **사용자 요구사항에 따른 빠른 화면 구성**이 가능해질 것입니다.