ERP-node/frontend/docs/레이아웃_기능_설계서.md

20 KiB

화면관리 시스템 레이아웃 기능 설계서

1. 개요

1.1 목적

화면관리 시스템에 동적 레이아웃 기능을 추가하여 다양한 화면 구조를 효율적으로 설계할 수 있도록 한다. 레이아웃은 컴포넌트들을 구조화된 영역으로 배치할 수 있는 컨테이너 역할을 하며, 동적으로 생성하고 관리할 수 있도록 설계한다.

1.2 범위

  • 레이아웃 관리 메뉴 및 기능 개발
  • 다양한 레이아웃 타입 및 설정 기능
  • 레지스트리 기반 동적 레이아웃 컴포넌트 시스템
  • 기존 화면관리 시스템과의 통합

2. 현재 시스템 분석

2.1 기존 데이터베이스 구조

-- 현재 화면관리 관련 테이블들
screen_definitions      -- 화면 정의
screen_layouts         -- 화면 레이아웃 (컴포넌트 배치)
screen_widgets         -- 위젯 설정
screen_templates       -- 화면 템플릿
template_standards     -- 템플릿 표준
component_standards    -- 컴포넌트 표준

2.2 기존 컴포넌트 타입

type ComponentType = "container" | "row" | "column" | "widget" | "group" | "datatable" | "file" | "area";

2.3 현재 레지스트리 시스템

  • ComponentRegistry: 컴포넌트 동적 등록 및 관리
  • WebTypeRegistry: 웹타입 동적 등록 및 관리
  • DynamicComponentRenderer: 동적 컴포넌트 렌더링

3. 레이아웃 기능 설계

3.1 레이아웃 타입 정의

3.1.1 기본 레이아웃 타입

export type LayoutType =
  | "grid" // 그리드 레이아웃 (n x m 격자)
  | "flexbox" // 플렉스박스 레이아웃
  | "split" // 분할 레이아웃 (수직/수평)
  | "card" // 카드 레이아웃
  | "tabs" // 탭 레이아웃
  | "accordion" // 아코디언 레이아웃
  | "sidebar" // 사이드바 레이아웃
  | "header-footer" // 헤더-푸터 레이아웃
  | "three-column" // 3단 레이아웃
  | "dashboard" // 대시보드 레이아웃
  | "form" // 폼 레이아웃
  | "table" // 테이블 레이아웃
  | "custom"; // 커스텀 레이아웃

3.1.2 레이아웃 컴포넌트 인터페이스

export interface LayoutComponent extends BaseComponent {
  type: "layout";
  layoutType: LayoutType;
  layoutConfig: LayoutConfig;
  children: ComponentData[];
  zones: LayoutZone[]; // 레이아웃 영역 정의
  allowedComponentTypes?: ComponentType[]; // 허용된 자식 컴포넌트 타입
  dropZoneConfig?: DropZoneConfig; // 드롭존 설정
}

export interface LayoutZone {
  id: string;
  name: string;
  position: {
    row?: number;
    column?: number;
    x?: number;
    y?: number;
  };
  size: {
    width: number | string;
    height: number | string;
    minWidth?: number;
    minHeight?: number;
    maxWidth?: number;
    maxHeight?: number;
  };
  style?: ComponentStyle;
  allowedComponents?: ComponentType[];
  isResizable?: boolean;
  isRequired?: boolean; // 필수 영역 여부
}

export interface LayoutConfig {
  // 그리드 레이아웃 설정
  grid?: {
    rows: number;
    columns: number;
    gap: number;
    rowGap?: number;
    columnGap?: number;
    autoRows?: string;
    autoColumns?: string;
  };

  // 플렉스박스 설정
  flexbox?: {
    direction: "row" | "column" | "row-reverse" | "column-reverse";
    justify: "flex-start" | "flex-end" | "center" | "space-between" | "space-around" | "space-evenly";
    align: "flex-start" | "flex-end" | "center" | "stretch" | "baseline";
    wrap: "nowrap" | "wrap" | "wrap-reverse";
    gap: number;
  };

  // 분할 레이아웃 설정
  split?: {
    direction: "horizontal" | "vertical";
    ratio: number[]; // 각 영역의 비율 [30, 70]
    minSize: number[]; // 각 영역의 최소 크기
    resizable: boolean; // 크기 조절 가능 여부
    splitterSize: number; // 분할선 두께
  };

  // 탭 레이아웃 설정
  tabs?: {
    position: "top" | "bottom" | "left" | "right";
    variant: "default" | "pills" | "underline";
    size: "sm" | "md" | "lg";
    defaultTab: string; // 기본 선택 탭
    closable: boolean; // 탭 닫기 가능 여부
  };

  // 아코디언 설정
  accordion?: {
    multiple: boolean; // 다중 확장 허용
    defaultExpanded: string[]; // 기본 확장 항목
    collapsible: boolean; // 모두 닫기 허용
  };

  // 사이드바 설정
  sidebar?: {
    position: "left" | "right";
    width: number | string;
    collapsible: boolean;
    collapsed: boolean;
    overlay: boolean; // 오버레이 모드
  };

  // 헤더-푸터 설정
  headerFooter?: {
    headerHeight: number | string;
    footerHeight: number | string;
    stickyHeader: boolean;
    stickyFooter: boolean;
  };

  // 대시보드 설정
  dashboard?: {
    columns: number;
    rowHeight: number;
    margin: [number, number];
    padding: [number, number];
    isDraggable: boolean;
    isResizable: boolean;
  };

  // 커스텀 설정
  custom?: {
    cssProperties: Record<string, string>;
    className: string;
    template: string; // HTML 템플릿
  };
}

export interface DropZoneConfig {
  showDropZones: boolean;
  dropZoneStyle?: ComponentStyle;
  highlightOnDragOver: boolean;
  allowedTypes?: ComponentType[];
}

3.2 데이터베이스 스키마 확장

3.2.1 레이아웃 표준 관리 테이블

-- 레이아웃 표준 관리 테이블
CREATE TABLE layout_standards (
  layout_code VARCHAR(50) PRIMARY KEY,
  layout_name VARCHAR(100) NOT NULL,
  layout_name_eng VARCHAR(100),
  description TEXT,
  layout_type VARCHAR(50) NOT NULL,
  category VARCHAR(50) NOT NULL,
  icon_name VARCHAR(50),
  default_size JSON,
  layout_config JSON NOT NULL,
  zones_config JSON NOT NULL,
  preview_image VARCHAR(255),
  sort_order INTEGER DEFAULT 0,
  is_active CHAR(1) DEFAULT 'Y',
  is_public CHAR(1) DEFAULT 'Y',
  company_code VARCHAR(50) NOT NULL,
  created_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  created_by VARCHAR(50),
  updated_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  updated_by VARCHAR(50)
);

-- 인덱스 생성
CREATE INDEX idx_layout_standards_type ON layout_standards(layout_type);
CREATE INDEX idx_layout_standards_category ON layout_standards(category);
CREATE INDEX idx_layout_standards_company ON layout_standards(company_code);

3.2.2 기존 테이블 확장

-- screen_layouts 테이블에 레이아웃 관련 컬럼 추가
ALTER TABLE screen_layouts ADD COLUMN layout_type VARCHAR(50);
ALTER TABLE screen_layouts ADD COLUMN layout_config JSON;
ALTER TABLE screen_layouts ADD COLUMN zones_config JSON;
ALTER TABLE screen_layouts ADD COLUMN zone_id VARCHAR(100);

-- component_standards 테이블에서 레이아웃 타입 지원
-- category에 'layout' 추가

3.3 레이아웃 카테고리 및 사전 정의 레이아웃

3.3.1 레이아웃 카테고리

export const LAYOUT_CATEGORIES = {
  BASIC: "basic", // 기본 레이아웃
  FORM: "form", // 폼 레이아웃
  TABLE: "table", // 테이블 레이아웃
  DASHBOARD: "dashboard", // 대시보드 레이아웃
  NAVIGATION: "navigation", // 네비게이션 레이아웃
  CONTENT: "content", // 컨텐츠 레이아웃
  BUSINESS: "business", // 업무용 레이아웃
};

3.3.2 사전 정의 레이아웃 템플릿

export const PREDEFINED_LAYOUTS = [
  // 기본 레이아웃
  {
    code: "GRID_2X2",
    name: "2x2 그리드",
    type: "grid",
    category: "basic",
    config: {
      grid: { rows: 2, columns: 2, gap: 16 },
    },
    zones: [
      { id: "zone1", name: "상단 좌측", position: { row: 0, column: 0 } },
      { id: "zone2", name: "상단 우측", position: { row: 0, column: 1 } },
      { id: "zone3", name: "하단 좌측", position: { row: 1, column: 0 } },
      { id: "zone4", name: "하단 우측", position: { row: 1, column: 1 } },
    ],
  },

  // 폼 레이아웃
  {
    code: "FORM_TWO_COLUMN",
    name: "2단 폼 레이아웃",
    type: "grid",
    category: "form",
    config: {
      grid: { rows: 1, columns: 2, gap: 24 },
    },
    zones: [
      { id: "left", name: "좌측 입력 영역", position: { row: 0, column: 0 } },
      { id: "right", name: "우측 입력 영역", position: { row: 0, column: 1 } },
    ],
  },

  // 대시보드 레이아웃
  {
    code: "DASHBOARD_MAIN",
    name: "메인 대시보드",
    type: "grid",
    category: "dashboard",
    config: {
      grid: { rows: 3, columns: 4, gap: 16 },
    },
    zones: [
      { id: "header", name: "헤더", position: { row: 0, column: 0 }, size: { width: "100%", height: "80px" } },
      { id: "sidebar", name: "사이드바", position: { row: 1, column: 0 }, size: { width: "250px", height: "100%" } },
      {
        id: "main",
        name: "메인 컨텐츠",
        position: { row: 1, column: 1 },
        size: { width: "calc(100% - 250px)", height: "100%" },
      },
    ],
  },

  // 테이블 레이아웃
  {
    code: "TABLE_WITH_FILTERS",
    name: "필터가 있는 테이블",
    type: "flexbox",
    category: "table",
    config: {
      flexbox: { direction: "column", gap: 16 },
    },
    zones: [
      { id: "filters", name: "검색 필터", size: { width: "100%", height: "auto" } },
      { id: "table", name: "데이터 테이블", size: { width: "100%", height: "1fr" } },
    ],
  },

  // 분할 레이아웃
  {
    code: "SPLIT_HORIZONTAL",
    name: "수평 분할",
    type: "split",
    category: "basic",
    config: {
      split: { direction: "horizontal", ratio: [50, 50], resizable: true },
    },
    zones: [
      { id: "left", name: "좌측 영역", isResizable: true },
      { id: "right", name: "우측 영역", isResizable: true },
    ],
  },

  // 탭 레이아웃
  {
    code: "TABS_HORIZONTAL",
    name: "수평 탭",
    type: "tabs",
    category: "navigation",
    config: {
      tabs: { position: "top", variant: "default", defaultTab: "tab1" },
    },
    zones: [
      { id: "tab1", name: "첫 번째 탭" },
      { id: "tab2", name: "두 번째 탭" },
      { id: "tab3", name: "세 번째 탭" },
    ],
  },
];

4. 구현 계획

4.1 Phase 1: 기본 인프라 구축

4.1.1 데이터베이스 스키마 생성

  • layout_standards 테이블 생성
  • 기존 테이블 확장
  • 기본 레이아웃 데이터 삽입

4.1.2 타입 정의 및 인터페이스

  • frontend/types/layout.ts 생성
  • 기존 screen.ts 확장

4.1.3 레이아웃 레지스트리 시스템

// frontend/lib/registry/LayoutRegistry.ts
export class LayoutRegistry {
  private static layouts = new Map<string, LayoutDefinition>();

  static registerLayout(definition: LayoutDefinition): void;
  static getLayout(layoutType: string): LayoutDefinition | undefined;
  static getLayoutsByCategory(category: string): LayoutDefinition[];
  static getAllLayouts(): LayoutDefinition[];
}

4.2 Phase 2: 레이아웃 관리 기능

4.2.1 레이아웃 관리 메뉴

// frontend/app/(main)/admin/layouts/page.tsx
- 레이아웃 목록 조회
- 레이아웃 생성/수정/삭제
- 레이아웃 미리보기
- 레이아웃 내보내기/가져오기

4.2.2 레이아웃 편집기

// frontend/components/layout/LayoutDesigner.tsx
- 드래그앤드롭 레이아웃 편집
- 실시간 미리보기
-  설정 편집
- 레이아웃 설정 편집

4.2.3 백엔드 API

// backend-node/src/routes/layoutRoutes.ts
GET    /api/layouts                    // 레이아웃 목록 조회
GET    /api/layouts/:id               // 레이아웃 상세 조회
POST   /api/layouts                   // 레이아웃 생성
PUT    /api/layouts/:id               // 레이아웃 수정
DELETE /api/layouts/:id               // 레이아웃 삭제
POST   /api/layouts/:id/duplicate     // 레이아웃 복제

4.3 Phase 3: 레이아웃 컴포넌트 구현

4.3.1 기본 레이아웃 컴포넌트들

// frontend/lib/registry/layouts/
├── GridLayoutRenderer.tsx          // 그리드 레이아웃
├── FlexboxLayoutRenderer.tsx       // 플렉스박스 레이아웃
├── SplitLayoutRenderer.tsx         // 분할 레이아웃
├── TabsLayoutRenderer.tsx          // 탭 레이아웃
├── AccordionLayoutRenderer.tsx     // 아코디언 레이아웃
├── SidebarLayoutRenderer.tsx       // 사이드바 레이아웃
├── HeaderFooterLayoutRenderer.tsx  // 헤더-푸터 레이아웃
└── CustomLayoutRenderer.tsx        // 커스텀 레이아웃

4.3.2 레이아웃 설정 패널

// frontend/components/layout/config-panels/
├── GridConfigPanel.tsx
├── FlexboxConfigPanel.tsx
├── SplitConfigPanel.tsx
├── TabsConfigPanel.tsx
└── ...

4.4 Phase 4: 화면관리 시스템 통합

4.4.1 화면 디자이너 확장

  • 레이아웃 팔레트 추가
  • 레이아웃 드래그앤드롭 지원
  • 레이아웃 존에 컴포넌트 배치

4.4.2 실시간 미리보기 지원

  • 레이아웃 렌더링 지원
  • 존별 컴포넌트 렌더링
  • 레이아웃 상호작용 지원

5. 기술적 구현 세부사항

5.1 레이아웃 렌더러 기본 구조

// frontend/lib/registry/layouts/BaseLayoutRenderer.tsx
interface LayoutRendererProps {
  layout: LayoutComponent;
  children: ComponentData[];
  isDesignMode?: boolean;
  onZoneClick?: (zoneId: string) => void;
  onComponentDrop?: (zoneId: string, component: ComponentData) => void;
}

export abstract class BaseLayoutRenderer extends React.Component<LayoutRendererProps> {
  abstract render(): React.ReactElement;

  protected renderZone(zone: LayoutZone, children: ComponentData[]): React.ReactElement {
    return (
      <div
        className={`layout-zone ${this.props.isDesignMode ? 'design-mode' : ''}`}
        data-zone-id={zone.id}
        onClick={() => this.props.onZoneClick?.(zone.id)}
        onDrop={this.handleDrop}
        onDragOver={this.handleDragOver}
      >
        {children.map(child => (
          <DynamicComponentRenderer key={child.id} component={child} />
        ))}
      </div>
    );
  }

  private handleDrop = (e: React.DragEvent) => {
    // 드롭 처리 로직
  };

  private handleDragOver = (e: React.DragEvent) => {
    // 드래그오버 처리 로직
  };
}

5.2 동적 레이아웃 등록 시스템

// frontend/lib/registry/layouts/index.ts
import { LayoutRegistry } from "../LayoutRegistry";
import GridLayoutRenderer from "./GridLayoutRenderer";
import FlexboxLayoutRenderer from "./FlexboxLayoutRenderer";
// ... 다른 레이아웃 import

// 레이아웃 컴포넌트들을 레지스트리에 등록
LayoutRegistry.registerLayout({
  id: "grid",
  name: "그리드 레이아웃",
  component: GridLayoutRenderer,
  category: "basic",
  icon: "grid",
  defaultConfig: {
    grid: { rows: 2, columns: 2, gap: 16 },
  },
});

LayoutRegistry.registerLayout({
  id: "flexbox",
  name: "플렉스박스 레이아웃",
  component: FlexboxLayoutRenderer,
  category: "basic",
  icon: "flex",
  defaultConfig: {
    flexbox: { direction: "row", justify: "flex-start", align: "stretch" },
  },
});

5.3 레이아웃 팔레트 컴포넌트

// frontend/components/screen/panels/LayoutsPanel.tsx
export default function LayoutsPanel({ onDragStart }: LayoutsPanelProps) {
  const [layouts] = useState(() => LayoutRegistry.getAllLayouts());
  const [selectedCategory, setSelectedCategory] = useState<string>('all');

  const filteredLayouts = useMemo(() => {
    if (selectedCategory === 'all') return layouts;
    return layouts.filter(layout => layout.category === selectedCategory);
  }, [layouts, selectedCategory]);

  return (
    <div className="layouts-panel">
      <div className="category-tabs">
        {LAYOUT_CATEGORIES.map(category => (
          <Button
            key={category.id}
            variant={selectedCategory === category.id ? 'default' : 'ghost'}
            onClick={() => setSelectedCategory(category.id)}
          >
            {category.name}
          </Button>
        ))}
      </div>

      <div className="layout-grid">
        {filteredLayouts.map(layout => (
          <div
            key={layout.id}
            className="layout-item"
            draggable
            onDragStart={(e) => onDragStart(e, layout)}
          >
            <div className="layout-preview">
              <layout.icon />
            </div>
            <div className="layout-info">
              <h4>{layout.name}</h4>
              <p>{layout.description}</p>
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

6. 사용자 인터페이스 설계

6.1 레이아웃 관리 화면

  • 레이아웃 목록: 그리드 형태로 레이아웃 목록 표시
  • 카테고리 필터: 카테고리별 레이아웃 필터링
  • 검색 기능: 레이아웃 이름/설명으로 검색
  • 미리보기: 레이아웃 구조 미리보기
  • 편집 버튼: 레이아웃 편집 모드 진입

6.2 레이아웃 편집기

  • 캔버스 영역: 레이아웃 시각적 편집
  • 존 편집: 각 존의 크기/위치 조정
  • 속성 패널: 레이아웃 설정 편집
  • 미리보기 모드: 실제 렌더링 미리보기

6.3 화면 디자이너 확장

  • 레이아웃 팔레트: 사용 가능한 레이아웃 목록
  • 드래그앤드롭: 레이아웃을 캔버스에 배치
  • 존 하이라이트: 컴포넌트 드롭 가능한 존 표시

7. 보안 및 권한 관리

7.1 레이아웃 접근 권한

  • 생성 권한: 레이아웃 생성 권한
  • 수정 권한: 레이아웃 수정 권한
  • 삭제 권한: 레이아웃 삭제 권한
  • 공개 설정: 다른 사용자와 레이아웃 공유

7.2 회사별 레이아웃 관리

  • 회사 코드: 레이아웃의 회사 소속 관리
  • 공개 레이아웃: 모든 회사에서 사용 가능한 레이아웃
  • 비공개 레이아웃: 특정 회사에서만 사용 가능한 레이아웃

8. 성능 최적화

8.1 레이아웃 렌더링 최적화

  • 지연 로딩: 필요한 레이아웃 컴포넌트만 로딩
  • 메모이제이션: 레이아웃 설정 변경 시에만 리렌더링
  • 가상화: 대량의 레이아웃 목록 가상화

8.2 캐싱 전략

  • 레이아웃 정의 캐싱: 자주 사용되는 레이아웃 정의 캐싱
  • 렌더링 결과 캐싱: 동일한 설정의 레이아웃 렌더링 결과 캐싱

9. 테스트 계획

9.1 단위 테스트

  • 레이아웃 컴포넌트 렌더링 테스트
  • 레이아웃 설정 변경 테스트
  • 드래그앤드롭 기능 테스트

9.2 통합 테스트

  • 화면관리 시스템과의 통합 테스트
  • 데이터베이스 연동 테스트
  • API 엔드포인트 테스트

9.3 사용자 테스트

  • 레이아웃 생성/편집 시나리오 테스트
  • 다양한 브라우저 호환성 테스트

10. 마이그레이션 계획

10.1 기존 화면 마이그레이션

  • 기존 컨테이너 컴포넌트를 레이아웃으로 변환
  • 기존 화면 구조를 레이아웃 기반으로 재구성

10.2 단계별 배포

  1. Phase 1: 레이아웃 관리 기능 배포
  2. Phase 2: 기본 레이아웃 컴포넌트 배포
  3. Phase 3: 화면관리 시스템 통합
  4. Phase 4: 기존 화면 마이그레이션

11. 향후 확장 계획

11.1 고급 레이아웃 기능

  • 반응형 레이아웃: 화면 크기에 따른 레이아웃 변경
  • 애니메이션: 레이아웃 전환 애니메이션
  • 테마 지원: 레이아웃별 테마 설정

11.2 AI 기반 레이아웃 추천

  • 데이터 타입에 따른 레이아웃 자동 추천
  • 사용 패턴 분석을 통한 최적 레이아웃 제안

11.3 협업 기능

  • 실시간 편집: 여러 사용자가 동시에 레이아웃 편집
  • 버전 관리: 레이아웃 변경 이력 관리
  • 댓글 시스템: 레이아웃에 대한 피드백 시스템

12. 결론

이 설계서에 따라 레이아웃 기능을 구현하면, 화면관리 시스템의 유연성과 확장성이 크게 향상될 것입니다. 동적 레지스트리 시스템을 통해 새로운 레이아웃 타입을 쉽게 추가할 수 있으며, 사용자는 다양한 화면 구조를 효율적으로 설계할 수 있게 됩니다.

주요 장점:

  • 확장성: 새로운 레이아웃 타입 쉽게 추가
  • 재사용성: 레이아웃 템플릿 재사용으로 개발 효율성 향상
  • 유연성: 다양한 화면 요구사항에 대응 가능
  • 일관성: 표준화된 레이아웃을 통한 UI 일관성 확보