# 야드 관리 3D 기능 구현 계획서 ## 1. 기능 개요 ### 목적 대시보드에서 야드(Yard)의 자재 배치 상태를 3D로 시각화하고 관리하는 위젯 ### 주요 특징 - **대시보드 위젯**: 대시보드의 위젯 형태로 추가되는 기능 - **야드 레이아웃 관리**: 여러 야드 레이아웃을 생성, 선택, 수정, 삭제 가능 - **3D 시각화**: Three.js + React Three Fiber를 사용한 3D 렌더링 - **자재 배치**: 3D 공간에서 자재를 직접 배치 및 이동 가능 - **자재 정보**: 배치된 자재 클릭 시 상세 정보 표시 (읽기 전용 자재 정보 + 편집 가능한 배치 정보) ### 위젯 통합 - **위젯 타입**: `yard-management-3d` - **위치**: 대시보드 관리 > 데이터 위젯 > 야드 관리 3D - **표시 모드**: - 편집 모드: 플레이스홀더 표시 - 뷰 모드: 실제 야드 관리 기능 실행 --- ## 2. 데이터베이스 설계 ### 2.1. yard_layout 테이블 야드 레이아웃 정보 저장 ```sql CREATE TABLE yard_layout ( id SERIAL PRIMARY KEY, name VARCHAR(100) NOT NULL, -- 야드 이름 (예: "A구역", "1번 야드") description TEXT, -- 설명 created_by VARCHAR(50), -- 생성자 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); ``` ### 2.2. yard_material_placement 테이블 야드 내 자재 배치 정보 (외부 자재 데이터 참조) ```sql CREATE TABLE yard_material_placement ( id SERIAL PRIMARY KEY, yard_layout_id INTEGER REFERENCES yard_layout(id) ON DELETE CASCADE, -- 외부 자재 참조 (API로 받아올 데이터) external_material_id VARCHAR(100) NOT NULL, -- 외부 시스템 자재 ID material_code VARCHAR(50) NOT NULL, -- 자재 코드 (캐시) material_name VARCHAR(100) NOT NULL, -- 자재 이름 (캐시) quantity INTEGER NOT NULL DEFAULT 1, -- 수량 (캐시) unit VARCHAR(20) DEFAULT 'EA', -- 단위 (캐시) -- 3D 위치 정보 position_x NUMERIC(10, 2) NOT NULL DEFAULT 0, -- X 좌표 position_y NUMERIC(10, 2) NOT NULL DEFAULT 0, -- Y 좌표 (높이) position_z NUMERIC(10, 2) NOT NULL DEFAULT 0, -- Z 좌표 -- 3D 크기 정보 size_x NUMERIC(10, 2) NOT NULL DEFAULT 5, -- 너비 size_y NUMERIC(10, 2) NOT NULL DEFAULT 5, -- 높이 size_z NUMERIC(10, 2) NOT NULL DEFAULT 5, -- 깊이 -- 외관 정보 color VARCHAR(7) DEFAULT '#3b82f6', -- 색상 (HEX) -- 추가 정보 status VARCHAR(20) DEFAULT 'normal', -- 상태 (normal, alert, warning) memo TEXT, -- 메모 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- 외부 자재 ID와 야드 레이아웃의 조합은 유니크해야 함 (중복 배치 방지) CREATE UNIQUE INDEX idx_yard_material_unique ON yard_material_placement(yard_layout_id, external_material_id); ``` ### 2.3. temp_material_master 테이블 (임시 자재 마스터) 외부 API를 받기 전까지 사용할 임시 자재 데이터 ```sql CREATE TABLE temp_material_master ( id SERIAL PRIMARY KEY, material_code VARCHAR(50) UNIQUE NOT NULL, -- 자재 코드 material_name VARCHAR(100) NOT NULL, -- 자재 이름 category VARCHAR(50), -- 카테고리 unit VARCHAR(20) DEFAULT 'EA', -- 기본 단위 default_color VARCHAR(7) DEFAULT '#3b82f6', -- 기본 색상 description TEXT, -- 설명 is_active BOOLEAN DEFAULT true, -- 사용 여부 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- 임시 자재 마스터 샘플 데이터 INSERT INTO temp_material_master (material_code, material_name, category, unit, default_color, description) VALUES ('MAT-STEEL-001', '철판 A타입 (1200x2400)', '철강', 'EA', '#ef4444', '두께 10mm 철판'), ('MAT-STEEL-002', '철판 B타입 (1000x2000)', '철강', 'EA', '#dc2626', '두께 8mm 철판'), ('MAT-PIPE-001', '강관 파이프 (Φ100)', '파이프', 'EA', '#10b981', '길이 6m'), ('MAT-PIPE-002', '강관 파이프 (Φ150)', '파이프', 'EA', '#059669', '길이 6m'), ('MAT-BOLT-001', '볼트 세트 M12', '부품', 'BOX', '#f59e0b', '100개/박스'), ('MAT-BOLT-002', '볼트 세트 M16', '부품', 'BOX', '#d97706', '100개/박스'), ('MAT-ANGLE-001', '앵글 (75x75x6)', '형강', 'EA', '#8b5cf6', '길이 6m'), ('MAT-ANGLE-002', '앵글 (100x100x10)', '형강', 'EA', '#7c3aed', '길이 6m'), ('MAT-CHANNEL-001', '채널 (100x50x5)', '형강', 'EA', '#06b6d4', '길이 6m'), ('MAT-WIRE-001', '와이어 로프 (Φ12)', '케이블', 'M', '#ec4899', '롤 단위'); ``` ### 2.4. 초기 데이터 마이그레이션 스크립트 ```sql -- 샘플 야드 레이아웃 INSERT INTO yard_layout (name, description, created_by) VALUES ('A구역', '메인 야드 A구역', 'admin'), ('B구역', '메인 야드 B구역', 'admin'), ('C구역', '보조 야드 C구역', 'admin'); -- 샘플 자재 배치 (A구역) - 임시 자재 마스터 참조 INSERT INTO yard_material_placement (yard_layout_id, external_material_id, material_code, material_name, quantity, unit, position_x, position_y, position_z, size_x, size_y, size_z, color, status) VALUES (1, 'TEMP-1', 'MAT-STEEL-001', '철판 A타입 (1200x2400)', 50, 'EA', 10, 0, 10, 8, 4, 8, '#ef4444', 'normal'), (1, 'TEMP-2', 'MAT-STEEL-002', '철판 B타입 (1000x2000)', 30, 'EA', 25, 0, 10, 8, 4, 8, '#dc2626', 'normal'), (1, 'TEMP-3', 'MAT-PIPE-001', '강관 파이프 (Φ100)', 100, 'EA', 40, 0, 10, 6, 6, 6, '#10b981', 'normal'), (1, 'TEMP-4', 'MAT-BOLT-001', '볼트 세트 M12', 500, 'BOX', 55, 0, 10, 4, 4, 4, '#f59e0b', 'warning'); ``` ### 2.5. 외부 자재 API 연동 구조 **현재 (Phase 1)**: 임시 자재 마스터 사용 ```typescript // temp_material_master 테이블에서 조회 GET / api / materials / temp; ``` **향후 (Phase 2)**: 외부 API 연동 ```typescript // 외부 시스템 자재 API GET /api/external/materials Response: [ { id: "EXT-12345", code: "MAT-STEEL-001", name: "철판 A타입", quantity: 150, unit: "EA", location: "창고A-1", ... } ] ``` --- ## 3. 백엔드 API 설계 ### 3.1. YardLayoutService **경로**: `backend-node/src/services/YardLayoutService.ts` **구현 완료** 주요 메서드: - `getAllLayouts()`: 모든 야드 레이아웃 목록 조회 (배치 자재 개수 포함) - `getLayoutById(id)`: 특정 야드 레이아웃 상세 조회 - `createLayout(data)`: 새 야드 레이아웃 생성 - `updateLayout(id, data)`: 야드 레이아웃 수정 (이름, 설명만) - `deleteLayout(id)`: 야드 레이아웃 삭제 (CASCADE로 배치 자재도 함께 삭제) - `duplicateLayout(id, newName)`: 야드 레이아웃 복제 (배치 자재 포함) - `getPlacementsByLayoutId(layoutId)`: 특정 야드의 모든 배치 자재 조회 - `addMaterialPlacement(layoutId, data)`: 야드에 자재 배치 추가 - `updatePlacement(placementId, data)`: 배치 정보 수정 (위치, 크기, 색상, 메모만) - `removePlacement(placementId)`: 배치 해제 - `batchUpdatePlacements(layoutId, placements)`: 여러 배치 일괄 업데이트 (트랜잭션 처리) **중요**: 자재 마스터 데이터(코드, 이름, 수량, 단위)는 읽기 전용. 배치 정보만 수정 가능. ### 3.2. YardLayoutController **경로**: `backend-node/src/controllers/YardLayoutController.ts` **구현 완료** 엔드포인트: - `GET /api/yard-layouts`: 모든 레이아웃 목록 (배치 개수 포함) - `GET /api/yard-layouts/:id`: 특정 레이아웃 상세 - `POST /api/yard-layouts`: 새 레이아웃 생성 (name, description) - `PUT /api/yard-layouts/:id`: 레이아웃 수정 (이름, 설명만) - `DELETE /api/yard-layouts/:id`: 레이아웃 삭제 (CASCADE) - `POST /api/yard-layouts/:id/duplicate`: 레이아웃 복제 (name) - `GET /api/yard-layouts/:id/placements`: 레이아웃의 배치 자재 목록 - `POST /api/yard-layouts/:id/placements`: 자재 배치 추가 - `PUT /api/yard-layouts/placements/:id`: 배치 정보 수정 - `DELETE /api/yard-layouts/placements/:id`: 배치 해제 - `PUT /api/yard-layouts/:id/placements/batch`: 배치 일괄 업데이트 모든 엔드포인트는 `authMiddleware`로 인증 보호됨 ### 3.3. MaterialService **경로**: `backend-node/src/services/MaterialService.ts` **구현 완료** 주요 메서드: - `getTempMaterials(params)`: 임시 자재 목록 조회 (검색, 카테고리 필터, 페이징) - `getTempMaterialByCode(code)`: 특정 자재 상세 조회 - `getCategories()`: 카테고리 목록 조회 ### 3.4. MaterialController **경로**: `backend-node/src/controllers/MaterialController.ts` **구현 완료** 엔드포인트: - `GET /api/materials/temp`: 임시 자재 마스터 목록 (검색, 필터링, 페이징) - `GET /api/materials/temp/categories`: 카테고리 목록 - `GET /api/materials/temp/:code`: 특정 자재 상세 **향후**: 외부 API 프록시로 변경 예정 ### 3.5. Routes **경로**: - `backend-node/src/routes/yardLayoutRoutes.ts` - `backend-node/src/routes/materialRoutes.ts` **구현 완료** `app.ts`에 등록: - `app.use("/api/yard-layouts", yardLayoutRoutes)` - `app.use("/api/materials", materialRoutes)` --- ## 4. 프론트엔드 컴포넌트 설계 ### 4.1. YardManagement3DWidget (메인 위젯) **경로**: `frontend/components/admin/dashboard/widgets/YardManagement3DWidget.tsx` **구현 완료** **주요 기능**: 1. 레이아웃 선택/생성 모드와 3D 편집 모드 전환 2. 편집 모드와 뷰 모드 구분 (isEditMode props) 3. API 연동 (yardLayoutApi) **상태 관리**: ```typescript - layouts: YardLayout[] // 전체 레이아웃 목록 - selectedLayout: YardLayout | null // 선택된 레이아웃 - isCreateModalOpen: boolean // 생성 모달 표시 여부 - isLoading: boolean // 로딩 상태 ``` **하위 컴포넌트**: - `YardLayoutList`: 레이아웃 목록 표시 - `YardLayoutCreateModal`: 새 레이아웃 생성 모달 - `YardEditor`: 3D 편집 화면 ### 4.2. API 클라이언트 **경로**: `frontend/lib/api/yardLayoutApi.ts` **구현 완료** **yardLayoutApi**: - `getAllLayouts()`: 모든 레이아웃 목록 - `getLayoutById(id)`: 레이아웃 상세 - `createLayout(data)`: 레이아웃 생성 - `updateLayout(id, data)`: 레이아웃 수정 - `deleteLayout(id)`: 레이아웃 삭제 - `duplicateLayout(id, name)`: 레이아웃 복제 - `getPlacementsByLayoutId(layoutId)`: 배치 목록 - `addMaterialPlacement(layoutId, data)`: 배치 추가 - `updatePlacement(placementId, data)`: 배치 수정 - `removePlacement(placementId)`: 배치 삭제 - `batchUpdatePlacements(layoutId, placements)`: 일괄 업데이트 **materialApi**: - `getTempMaterials(params)`: 임시 자재 목록 - `getTempMaterialByCode(code)`: 자재 상세 - `getCategories()`: 카테고리 목록 ### 4.3. YardLayoutList (레이아웃 목록) **경로**: `frontend/components/admin/dashboard/widgets/yard-3d/YardLayoutList.tsx` **구현 예정** - 테이블 형식으로 레이아웃 목록 표시 - 검색 및 정렬 기능 - 행 클릭 시 레이아웃 선택 (편집 모드 진입) - 작업 메뉴 (편집, 복제, 삭제) ### 4.4. YardLayoutCreateModal (레이아웃 생성 모달) **경로**: `frontend/components/admin/dashboard/widgets/yard-3d/YardLayoutCreateModal.tsx` **구현 예정** - 야드 이름, 설명 입력 - Shadcn UI Dialog 사용 - 생성 완료 시 자동으로 편집 모드 진입 ### 4.5. YardEditor (3D 편집 화면) **경로**: `frontend/components/admin/dashboard/widgets/yard-3d/YardEditor.tsx` **구현 예정** **주요 구성**: - 상단 툴바 (뒤로가기, 저장, 자재 추가 등) - 좌측: 3D 캔버스 - 우측: 자재 정보 패널 (선택 시 표시) **기술 스택**: - React Three Fiber - @react-three/drei (OrbitControls, Grid, Box) - Three.js **주요 기능**: 1. 야드 바닥 그리드 표시 2. 자재 3D 박스 렌더링 3. 자재 클릭 이벤트 처리 4. 자재 드래그 앤 드롭 (위치 이동) 5. 카메라 컨트롤 (회전, 줌) ### 4.6. MaterialInfoPanel (자재 정보 패널) **경로**: `frontend/components/admin/dashboard/widgets/yard-3d/MaterialInfoPanel.tsx` **구현 예정** **읽기 전용 정보** (외부 자재 데이터): - 자재 코드 - 자재 이름 - 수량 - 단위 - 카테고리 **수정 가능 정보** (배치 데이터): - 3D 위치 (X, Y, Z) - 3D 크기 (너비, 높이, 깊이) - 색상 - 메모 **기능**: - 배치 정보 수정 - 배치 해제 (야드에서 자재 제거) ### 4.6. MaterialLibrary (자재 라이브러리) **경로**: `frontend/components/admin/dashboard/widgets/MaterialLibrary.tsx` - 사용 가능한 자재 목록 표시 - 자재 검색 기능 - 자재를 3D 캔버스로 드래그하여 배치 - 자재 마스터 데이터 조회 (기존 테이블 활용 가능) --- ## 5. UI/UX 설계 ### 5.1. 초기 화면 (선택/생성 모드) 야드 레이아웃 목록을 테이블 형식으로 표시 ``` ┌──────────────────────────────────────────────────────────────────────────┐ │ 야드 관리 3D [+ 새 야드 생성] │ ├──────────────────────────────────────────────────────────────────────────┤ │ │ │ [검색: ________________] [정렬: 최근순 ▼] │ │ │ │ ┌────────────────────────────────────────────────────────────────────┐ │ │ │ 야드명 │ 설명 │ 배치 자재 │ 최종 수정 │ 작업 │ │ │ ├────────────────────────────────────────────────────────────────────┤ │ │ │ A구역 │ 메인 야드 A구역 │ 15개 │ 2025-01-15 14:30 │ ⋮ │ │ │ │ │ │ │ │ │ │ │ ├────────────────────────────────────────────────────────────────────┤ │ │ │ B구역 │ 메인 야드 B구역 │ 8개 │ 2025-01-14 10:20 │ ⋮ │ │ │ │ │ │ │ │ │ │ │ ├────────────────────────────────────────────────────────────────────┤ │ │ │ C구역 │ 보조 야드 C구역 │ 3개 │ 2025-01-10 09:15 │ ⋮ │ │ │ │ │ │ │ │ │ │ │ └────────────────────────────────────────────────────────────────────┘ │ │ │ │ 총 3개 [1] 2 3 4 5 > │ │ │ └──────────────────────────────────────────────────────────────────────────┘ 작업 메뉴 (⋮ 클릭 시): ┌─────────────┐ │ 편집 │ │ 복제 │ │ 삭제 │ └─────────────┘ ``` ### 5.2. 레이아웃 생성 모달 새 야드 레이아웃을 생성할 때 표시되는 모달 ``` ┌─────────────────────────────────────────────────┐ │ 새 야드 레이아웃 생성 [X] │ ├─────────────────────────────────────────────────┤ │ │ │ 야드 이름 * │ │ [____________________________________] │ │ │ │ 설명 │ │ [____________________________________] │ │ [____________________________________] │ │ │ │ [취소] [생성] │ └─────────────────────────────────────────────────┘ ``` ### 5.3. 편집 모드 - 전체 레이아웃 야드 편집 화면의 전체 구성 ``` ┌─────────────────────────────────────────────────────────────────────────────┐ │ A구역 [저장] [미리보기] [취소] │ ├───────────────────────────────────────┬─────────────────────────────────────┤ │ │ 도구 패널 [최소화] │ │ ├─────────────────────────────────────┤ │ │ │ │ │ 자재 라이브러리 │ │ │ ───────────────────── │ │ │ [검색: ____________] [카테고리 ▼] │ │ │ │ │ 3D 캔버스 │ ┌───────────────────────┐ │ │ │ │ MAT-STEEL-001 │ │ │ (야드 그리드 + 자재 배치) │ │ 철판 A타입 │ │ │ │ │ 50 EA 재고 있음 │ │ │ - 마우스 드래그: 카메라 회전 │ │ [배치] │ │ │ - 휠: 줌 인/아웃 │ └───────────────────────┘ │ │ - 자재 클릭: 선택 │ │ │ - 자재 드래그: 이동 │ ┌───────────────────────┐ │ │ │ │ MAT-STEEL-002 │ │ │ │ │ 철판 B타입 │ │ │ │ │ 30 EA 재고 있음 │ │ │ │ │ [배치] │ │ │ │ └───────────────────────┘ │ │ │ │ │ │ ┌───────────────────────┐ │ │ │ │ MAT-PIPE-001 │ │ │ │ │ 강관 파이프 │ │ │ │ │ 100 EA 재고 있음 │ │ │ │ │ [배치] │ │ │ │ └───────────────────────┘ │ │ │ │ │ │ ... (스크롤 가능) │ │ │ │ ├───────────────────────────────────────┴─────────────────────────────────────┤ │ 자재 정보 │ │ ───────────────── │ │ 선택된 자재: MAT-STEEL-001 (철판 A타입) │ │ │ │ 기본 정보 (읽기 전용) │ │ 자재 코드: MAT-STEEL-001 │ │ 자재 이름: 철판 A타입 (1200x2400) │ │ 수량: 50 EA │ │ 카테고리: 철강 │ │ │ │ 배치 정보 (수정 가능) │ │ 3D 위치 │ │ X: [____10.00____] m Y: [____0.00____] m Z: [____10.00____] m │ │ │ │ 3D 크기 │ │ 너비: [____8.00____] m 높이: [____4.00____] m 깊이: [____8.00____] m │ │ │ │ 외관 │ │ 색상: [■ #ef4444] [색상 선택] │ │ │ │ 메모 │ │ [_____________________________________________________________________] │ │ │ │ [배치 해제] [변경 적용] [초기화] │ └─────────────────────────────────────────────────────────────────────────────┘ ``` ### 5.4. 3D 캔버스 상세 3D 캔버스 내부의 시각적 요소 ``` ┌─────────────────────────────────────────────────┐ │ 카메라 컨트롤 [리셋] │ │ 회전: 45° | 기울기: 30° | 줌: 100% │ ├─────────────────────────────────────────────────┤ │ Y (높이) │ │ ↑ │ │ │ │ │ │ │ │ │ ┌───────┐ (자재) │ │ │ │ │ │ │ Z (깊이) │ │ MAT-1 │ (선택됨) │ │ ↗ │ │ │ │ │ / └────┴───────┴──→ X (너비) │ │ / │ │ / ┌─┬─┬─┬─┬─┬─┬─┬─┬─┬─┐ │ │ ├─┼─┼─┼─┼─┼─┼─┼─┼─┼─┤ (그리드) │ │ ├─┼─┼─┼─┼─┼─┼─┼─┼─┼─┤ │ │ ├─┼─┼─┼─┼─┼─┼─┼─┼─┼─┤ │ │ │ │ │ │ │ │□│ │ │ │ │ ← MAT-2 │ │ ├─┼─┼─┼─┼─┼─┼─┼─┼─┼─┤ │ │ ├─┼─┼─┼─┼─┼─┼─┼─┼─┼─┤ │ │ └─┴─┴─┴─┴─┴─┴─┴─┴─┴─┘ │ │ │ │ 범례: │ │ ■ 선택된 자재 □ 일반 자재 │ │ ─ 그리드 라인 (5m 단위) │ │ │ │ 조작 가이드: │ │ • 마우스 왼쪽 드래그: 카메라 회전 │ │ • 마우스 휠: 줌 인/아웃 │ │ • 자재 클릭: 선택 │ │ • 선택된 자재 드래그: 위치 이동 │ └─────────────────────────────────────────────────┘ ``` ### 5.5. 자재 배치 플로우 자재를 배치하는 과정 ``` 1. 자재 라이브러리에서 자재 선택 ┌─────────────────┐ │ MAT-STEEL-001 │ ← 클릭 │ 철판 A타입 │ │ [배치] │ └─────────────────┘ ↓ 2. 3D 캔버스에 자재가 임시로 표시됨 (투명) ┌─────────────────┐ │ 3D 캔버스 │ │ │ │ [반투명 박스] │ ← 마우스 커서 따라 이동 │ │ └─────────────────┘ ↓ 3. 원하는 위치에 클릭하여 배치 ┌─────────────────┐ │ 3D 캔버스 │ │ │ │ [실제 박스] │ ← 배치 완료 │ │ └─────────────────┘ ↓ 4. 자재 정보 패널에 자동으로 선택됨 ┌─────────────────────────┐ │ 자재 정보 패널 │ │ ─────────────────── │ │ 선택된 자재: │ │ MAT-STEEL-001 │ │ │ │ 수량: 50 EA │ │ 위치: X:10, Y:0, Z:10 │ │ ... │ └─────────────────────────┘ ``` ### 5.6. 반응형 레이아웃 (모바일/태블릿) 모바일에서는 패널을 접거나 탭으로 전환 ``` 모바일 (세로 모드): ┌───────────────────────┐ │ A구역 [저장] │ ├───────────────────────┤ │ │ │ 3D 캔버스 │ │ (전체 화면) │ │ │ │ │ ├───────────────────────┤ │ [자재 라이브러리] [정보]│ ← 탭 전환 ├───────────────────────┤ │ 선택된 자재: │ │ MAT-STEEL-001 │ │ 수량: 50 EA │ │ ... │ └───────────────────────┘ 태블릿 (가로 모드): ┌─────────────────────────────────────┐ │ A구역 [저장] │ ├──────────────────┬──────────────────┤ │ │ 자재 라이브러리 │ │ 3D 캔버스 │ ────────────── │ │ │ [검색: ______] │ │ │ MAT-STEEL-001 │ │ │ ... │ ├──────────────────┴──────────────────┤ │ 선택된 자재: MAT-STEEL-001 │ │ 수량: 50 EA | 위치: X:10, Z:10 │ └─────────────────────────────────────┘ ``` --- ## 6. 구현 단계 ### Phase 1: 데이터베이스 및 백엔드 API ✅ **완료** 1. ✅ 테이블 생성 스크립트 작성 (`create_yard_management_tables.sql`) 2. ✅ 마이그레이션 실행 3. ✅ Service, Controller, Routes 구현 4. ✅ API 클라이언트 구현 (yardLayoutApi, materialApi) ### Phase 2: 메인 위젯 및 레이아웃 관리 🔄 **진행 중** 1. ✅ types.ts에 위젯 타입 추가 2. ✅ DashboardTopMenu에 위젯 추가 3. ✅ DashboardDesigner에 위젯 타이틀/컨텐츠 추가 4. ✅ YardManagement3DWidget 메인 컴포넌트 구현 5. ⏳ YardLayoutList 컴포넌트 구현 6. ⏳ YardLayoutCreateModal 컴포넌트 구현 ### Phase 3: 3D 편집 화면 ⏳ **대기 중** 1. ⏳ YardEditor 메인 컴포넌트 2. ⏳ 상단 툴바 (뒤로가기, 저장, 자재 추가) 3. ⏳ 레이아웃 구성 (좌측 캔버스 + 우측 패널) ### Phase 4: 3D 캔버스 기본 구조 ⏳ **대기 중** 1. ⏳ Yard3DCanvas 컴포넌트 기본 구조 2. ⏳ React Three Fiber 설정 3. ⏳ 야드 바닥 그리드 렌더링 4. ⏳ 카메라 컨트롤 (OrbitControls) 5. ⏳ 자재 3D 박스 렌더링 ### Phase 5: 자재 배치 및 인터랙션 ⏳ **대기 중** 1. ⏳ MaterialLibrary 컴포넌트 구현 2. ⏳ 자재 선택 및 추가 3. ⏳ 자재 드래그 앤 드롭 배치 4. ⏳ 자재 클릭 선택 5. ⏳ 자재 위치 이동 (드래그) ### Phase 6: 자재 정보 패널 및 편집 ⏳ **대기 중** 1. ⏳ MaterialInfoPanel 컴포넌트 구현 2. ⏳ 자재 정보 표시 (읽기 전용 + 편집 가능) 3. ⏳ 자재 배치 정보 수정 4. ⏳ 배치 해제 기능 5. ⏳ 변경사항 저장 ### Phase 7: 통합 및 최적화 ⏳ **대기 중** 1. YardManagement3DWidget 통합 2. 상태 관리 최적화 3. 성능 최적화 (대량 자재 렌더링) 4. 에러 처리 및 로딩 상태 5. 모바일/반응형 대응 (선택사항) ### Phase 7: 대시보드 위젯 등록 1. types.ts에 위젯 타입 추가 2. DashboardTopMenu에 위젯 추가 3. CanvasElement에 위젯 렌더링 추가 4. 위젯 설정 모달 (레이아웃 선택) --- ## 7. 기술적 고려사항 ### 7.1. 3D 렌더링 최적화 - 자재 수가 많을 경우 인스턴싱 사용 - LOD (Level of Detail) 적용 고려 - 카메라 거리에 따른 렌더링 최적화 ### 7.2. 드래그 앤 드롭 - 3D 공간에서의 레이캐스팅 - 그리드 스냅 기능 - 충돌 감지 (자재 간 겹침 방지) ### 7.3. 상태 관리 - 자재 위치 변경 시 실시간 업데이트 - Debounce를 사용한 API 호출 최적화 - 낙관적 업데이트 (Optimistic Update) ### 7.4. 데이터 동기화 - 여러 사용자가 동시에 편집하는 경우 충돌 처리 - WebSocket을 통한 실시간 동기화 (선택사항) ### 7.5. UI/UX 규칙 #### 이모지 사용 금지 #### 모달 사용 규칙 **`window.alert`, `window.confirm` 사용 금지** 모든 알림, 확인, 에러 메시지는 Shadcn UI 모달 컴포넌트 사용: - **일반 알림**: `Dialog` 컴포넌트 - **확인 필요**: `AlertDialog` 컴포넌트 - **삭제/해제 확인**: `AlertDialog` (Destructive 스타일) - **성공 메시지**: `Dialog` 또는 `Toast` - **에러 메시지**: `Dialog` (Error 스타일) **예시**: ```typescript // 잘못된 방법 ❌ window.alert("저장되었습니다"); if (window.confirm("삭제하시겠습니까?")) { ... } // 올바른 방법 ✅ 배치 해제 이 자재를 야드에서 제거하시겠습니까? 취소 확인 ``` --- ## 8. API 명세서 ### 8.1. 야드 레이아웃 API #### GET /api/yard-layouts **설명**: 모든 야드 레이아웃 목록 조회 **응답**: ```json { "success": true, "data": [ { "id": 1, "name": "A구역", "description": "메인 야드 A구역", "placement_count": 15, "created_at": "2025-01-01T00:00:00Z" } ] } ``` #### GET /api/yard-layouts/:id **설명**: 특정 야드 레이아웃 상세 조회 **응답**: ```json { "success": true, "data": { "id": 1, "name": "A구역", "description": "메인 야드 A구역", "created_at": "2025-01-01T00:00:00Z" } } ``` #### POST /api/yard-layouts **설명**: 새 야드 레이아웃 생성 **요청**: ```json { "name": "D구역", "description": "신규 야드" } ``` #### PUT /api/yard-layouts/:id **설명**: 야드 레이아웃 수정 (이름, 설명만) **요청**: ```json { "name": "D구역 (수정)", "description": "수정된 설명" } ``` #### DELETE /api/yard-layouts/:id **설명**: 야드 레이아웃 삭제 ### 8.2. 자재 배치 API #### GET /api/yard-layouts/:id/materials **설명**: 특정 야드의 모든 자재 조회 **응답**: ```json { "success": true, "data": [ { "id": 1, "material_code": "MAT-001", "material_name": "철판 A타입", "quantity": 50, "unit": "EA", "position_x": 10, "position_y": 0, "position_z": 10, "size_x": 8, "size_y": 4, "size_z": 8, "color": "#ef4444", "status": "normal", "memo": null } ] } ``` #### POST /api/yard-layouts/:id/materials **설명**: 야드에 자재 추가 **요청**: ```json { "material_code": "MAT-005", "material_name": "신규 자재", "quantity": 10, "unit": "EA", "position_x": 20, "position_y": 0, "position_z": 20, "size_x": 5, "size_y": 5, "size_z": 5, "color": "#3b82f6" } ``` #### PUT /api/yard-materials/:id **설명**: 자재 정보 수정 **요청**: ```json { "position_x": 25, "position_z": 25, "quantity": 55 } ``` #### DELETE /api/yard-materials/:id **설명**: 자재 삭제 #### PUT /api/yard-layouts/:id/materials/batch **설명**: 여러 자재 일괄 업데이트 (드래그로 여러 자재 이동 시) **요청**: ```json { "materials": [ { "id": 1, "position_x": 15, "position_z": 15 }, { "id": 2, "position_x": 30, "position_z": 15 } ] } ``` --- ## 9. 테스트 시나리오 ### 9.1. 기본 기능 테스트 - [ ] 야드 레이아웃 목록 조회 - [ ] 야드 레이아웃 생성 - [ ] 야드 레이아웃 선택 - [ ] 3D 캔버스 렌더링 - [ ] 자재 목록 조회 및 표시 ### 9.2. 자재 배치 테스트 - [ ] 자재 라이브러리에서 드래그하여 배치 - [ ] 배치된 자재 클릭하여 선택 - [ ] 선택된 자재 정보 패널 표시 - [ ] 자재 드래그하여 위치 이동 - [ ] 자재 정보 수정 (수량, 크기 등) - [ ] 자재 삭제 ### 9.3. 인터랙션 테스트 - [ ] 카메라 회전 (OrbitControls) - [ ] 카메라 줌 인/아웃 - [ ] 그리드 스냅 기능 - [ ] 여러 자재 동시 이동 - [ ] 자재 간 충돌 방지 ### 9.4. 저장 및 로드 테스트 - [ ] 자재 배치 후 저장 - [ ] 저장된 레이아웃 다시 로드 - [ ] 레이아웃 삭제 - [ ] 레이아웃 복제 (선택사항) --- ## 10. 향후 확장 가능성 - 자재 검색 및 필터링 (상태별, 자재 코드별) - 자재 배치 히스토리 (변경 이력) - 자재 배치 템플릿 (자주 사용하는 배치 저장) - 자재 입출고 연동 (실시간 재고 반영) - 자재 경로 최적화 (피킹 경로 표시) - AR/VR 지원 (모바일 AR로 실제 야드 확인) - 다중 사용자 동시 편집 (WebSocket) - 자재 배치 분석 (공간 활용률, 접근성 등) --- ## 11. 파일 구조 ``` backend-node/src/ ├── services/ │ └── YardLayoutService.ts (신규) ├── controllers/ │ └── YardLayoutController.ts (신규) ├── routes/ │ └── yardLayoutRoutes.ts (신규) └── app.ts (수정) frontend/components/admin/dashboard/ ├── widgets/ │ ├── YardManagement3DWidget.tsx (신규 - 메인) │ ├── YardLayoutSelector.tsx (신규) │ ├── YardLayoutCreator.tsx (신규) │ ├── Yard3DCanvas.tsx (신규) │ ├── MaterialInfoPanel.tsx (신규) │ └── MaterialLibrary.tsx (신규) ├── types.ts (수정 - 위젯 타입 추가) ├── DashboardTopMenu.tsx (수정 - 메뉴 추가) └── CanvasElement.tsx (수정 - 렌더링 추가) db/ └── migrations/ └── create_yard_tables.sql (신규) ``` --- ## 12. 예상 개발 기간 - Phase 1 (DB & API): 1일 - Phase 2 (선택/생성): 1일 - Phase 3 (3D 기본): 1일 - Phase 4 (배치 인터랙션): 2일 - Phase 5 (정보 패널): 1일 - Phase 6 (통합/최적화): 1일 - Phase 7 (대시보드 등록): 0.5일 **총 예상 기간: 7.5일** --- ## 13. 참고 자료 - React Three Fiber: https://docs.pmnd.rs/react-three-fiber - @react-three/drei: https://github.com/pmndrs/drei - Three.js: https://threejs.org/docs/