ERP-node/popdocs/ARCHITECTURE.md

7.2 KiB

POP 화면 시스템 아키텍처

최종 업데이트: 2026-02-05 (v5 그리드 시스템)

POP(Point of Production) 화면은 모바일/태블릿 환경에 최적화된 터치 기반 화면 시스템입니다.


현재 버전: v5 (CSS Grid)

항목 v5 (현재)
레이아웃 CSS Grid
배치 방식 좌표 기반 (col, row, colSpan, rowSpan)
모드 4개 (mobile_portrait, mobile_landscape, tablet_portrait, tablet_landscape)
칸 수 4/6/8/12칸

폴더 구조

frontend/
├── app/(pop)/                    # Next.js App Router
│   ├── layout.tsx               # POP 전용 레이아웃
│   └── pop/
│       ├── page.tsx             # 대시보드
│       ├── screens/[screenId]/  # 화면 뷰어 (v5)
│       └── work/                # 작업 화면
│
├── components/pop/              # POP 컴포넌트
│   ├── designer/                # 디자이너 모듈 ★
│   │   ├── PopDesigner.tsx      # 메인 (레이아웃 로드/저장)
│   │   ├── PopCanvas.tsx        # 캔버스 (DnD, 줌, 모드)
│   │   ├── panels/
│   │   │   └── ComponentEditorPanel.tsx  # 속성 편집
│   │   ├── renderers/
│   │   │   └── PopRenderer.tsx  # CSS Grid 렌더링
│   │   ├── types/
│   │   │   └── pop-layout.ts    # v5 타입 정의
│   │   └── utils/
│   │       └── gridUtils.ts     # 위치 계산
│   ├── management/              # 화면 관리
│   └── dashboard/               # 대시보드
│
└── lib/
    ├── api/screen.ts            # 화면 API
    └── registry/                # 컴포넌트 레지스트리

핵심 파일

1. PopDesigner.tsx (메인)

역할: 레이아웃 로드/저장, 컴포넌트 CRUD, 히스토리

// 상태 관리
const [layout, setLayout] = useState<PopLayoutDataV5>(createEmptyPopLayoutV5());
const [selectedComponentId, setSelectedComponentId] = useState<string | null>(null);
const [currentMode, setCurrentMode] = useState<GridMode>("tablet_landscape");
const [history, setHistory] = useState<PopLayoutDataV5[]>([]);

// 핵심 함수
handleSave()           // 레이아웃 저장
handleAddComponent()   // 컴포넌트 추가
handleUpdateComponent() // 컴포넌트 수정
handleDeleteComponent() // 컴포넌트 삭제
handleUndo() / handleRedo() // 히스토리

2. PopCanvas.tsx (캔버스)

역할: 그리드 캔버스, DnD, 줌, 패닝, 모드 전환

// DnD 설정
const DND_ITEM_TYPES = { COMPONENT: "component" };

// 뷰포트 프리셋 (4개 모드)
const VIEWPORT_PRESETS = [
  { id: "mobile_portrait", width: 375, columns: 4 },
  { id: "mobile_landscape", width: 667, columns: 6 },
  { id: "tablet_portrait", width: 768, columns: 8 },
  { id: "tablet_landscape", width: 1024, columns: 12 },
];

// 기능
- useDrop(): 팔레트에서 컴포넌트 드롭
- handleWheel():  (30%~150%)
- Space + 드래그: 패닝

3. PopRenderer.tsx (렌더러)

역할: CSS Grid 기반 레이아웃 렌더링

// Props
interface PopRendererProps {
  layout: PopLayoutDataV5;
  viewportWidth: number;
  currentMode: GridMode;
  isDesignMode: boolean;
  selectedComponentId?: string | null;
  onSelectComponent?: (id: string | null) => void;
}

// CSS Grid 스타일 생성
const gridStyle = useMemo(() => ({
  display: "grid",
  gridTemplateColumns: `repeat(${columns}, 1fr)`,
  gridTemplateRows: `repeat(${rows}, 1fr)`,
  gap: `${gap}px`,
  padding: `${padding}px`,
}), [mode]);

// 위치 변환 (12칸 → 다른 모드)
const convertPosition = (pos: PopGridPosition, targetMode: GridMode) => {
  const ratio = GRID_BREAKPOINTS[targetMode].columns / 12;
  return {
    col: Math.max(1, Math.round(pos.col * ratio)),
    colSpan: Math.max(1, Math.round(pos.colSpan * ratio)),
    row: pos.row,
    rowSpan: pos.rowSpan,
  };
};

4. ComponentEditorPanel.tsx (속성 패널)

역할: 선택된 컴포넌트 속성 편집

// 탭 구조
- grid: col, row, colSpan, rowSpan (기본 모드에서만 편집)
- settings: label, type 
- data: 데이터 바인딩 (미구현)
- visibility: 모드별 표시/숨김

5. pop-layout.ts (타입 정의)

역할: v5 타입 정의

// 그리드 모드
type GridMode = "mobile_portrait" | "mobile_landscape" | "tablet_portrait" | "tablet_landscape";

// 브레이크포인트 설정
const GRID_BREAKPOINTS = {
  mobile_portrait: { columns: 4, rowHeight: 48, gap: 8, padding: 12 },
  mobile_landscape: { columns: 6, rowHeight: 44, gap: 8, padding: 16 },
  tablet_portrait: { columns: 8, rowHeight: 52, gap: 12, padding: 20 },
  tablet_landscape: { columns: 12, rowHeight: 56, gap: 12, padding: 24 },
};

// 레이아웃 데이터
interface PopLayoutDataV5 {
  version: "pop-5.0";
  metadata: PopLayoutMetadata;
  gridConfig: PopGridConfig;
  components: PopComponentDefinitionV5[];
  globalSettings: PopGlobalSettingsV5;
}

// 컴포넌트 정의
interface PopComponentDefinitionV5 {
  id: string;
  type: PopComponentType;
  label: string;
  gridPosition: PopGridPosition;  // col, row, colSpan, rowSpan
  config: PopComponentConfig;
  visibility: Record<GridMode, boolean>;
  modeOverrides?: Record<GridMode, PopModeOverrideV5>;
}

// 위치
interface PopGridPosition {
  col: number;      // 시작 열 (1부터)
  row: number;      // 시작 행 (1부터)
  colSpan: number;  // 열 크기 (1~12)
  rowSpan: number;  // 행 크기 (1~)
}

6. gridUtils.ts (유틸리티)

역할: 그리드 위치 계산

// 위치 변환
convertPositionToMode(pos, targetMode)

// 겹침 감지
isOverlapping(posA, posB)

// 빈 위치 찾기
findNextEmptyPosition(layout, mode)

// 마우스 → 그리드 좌표
mouseToGridPosition(mouseX, mouseY, canvasRect, mode)

데이터 흐름

[사용자 액션]
     ↓
[PopDesigner] ← 상태 관리 (layout, selectedComponentId, history)
     ↓
[PopCanvas] ← DnD, 줌, 모드 전환
     ↓
[PopRenderer] ← CSS Grid 렌더링
     ↓
[컴포넌트 표시]

저장 흐름

[저장 버튼]
     ↓
PopDesigner.handleSave()
     ↓
screenApi.saveLayoutPop(screenId, layout)
     ↓
[백엔드] screenManagementService.saveLayoutPop()
     ↓
[DB] screen_layouts_pop 테이블

로드 흐름

[페이지 로드]
     ↓
PopDesigner useEffect
     ↓
screenApi.getLayoutPop(screenId)
     ↓
isV5Layout(data) 체크
     ↓
setLayout(data) 또는 createEmptyPopLayoutV5()

API 엔드포인트

메서드 경로 설명
GET /api/screen-management/layout-pop/:screenId 레이아웃 조회
POST /api/screen-management/layout-pop/:screenId 레이아웃 저장

삭제된 레거시 (참고용)

파일 버전 이유
PopCanvasV4.tsx v4 Flexbox 기반, v5로 대체
PopFlexRenderer.tsx v4 Flexbox 렌더러, v5로 대체
PopLayoutRenderer.tsx v3 절대 좌표 기반, v5로 대체
ComponentEditorPanelV4.tsx v4 v5 전용으로 통합

상세 스펙: SPEC.md | 파일 목록: FILES.md