Files
sam-docs/dev/dev_plans/bending-management/step4-React연동.md
강영보 fbd0510cc1 docs: [bending] 절곡품 관리 개발 완료 상태 업데이트
- step1: 데이터 임포트 완료 (170+60건), artisan 커맨드 7개 실행 결과
- step2: API 12개 엔드포인트 완료, item_category 필수 필터 추가
- step3: MNG 샘플 완료 (4개 메뉴, 이미지 473건)
- step4: React 구현 가이드 전면 작성 (API 응답 구조, 컴포넌트 설계, 실무 노트)
- 코드 체계 변경 불가 사유, 265vs170 차이 설명, 운영 전 정리 항목 추가
2026-03-17 11:33:47 +09:00

413 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Step 4: React 절곡품 관리 화면 + 견적 이미지 연동 ⬜ 미착수
> **프로젝트**: React (`sam/react`)
> **선행 조건**: Step 2 (API ✅), Step 3 (MNG 샘플 ✅)
> **상태**: ⬜ 미착수
> **참조**: MNG 샘플 화면, 기존 GuideRailSection 컴포넌트
---
## 1. 개요
MNG에서 샘플로 구현/검증한 절곡품 관리 기능을 React 운영 화면으로 이관한다.
모든 API 엔드포인트는 Step 2에서 완료되어 있으므로, **프론트엔드 구현만** 필요.
---
## 2. API 엔드포인트 (Step 2 완료 — 그대로 사용)
### 2-1. 기초관리 (절곡 부품)
```
GET /api/v1/bending-items ← 목록 (필터/검색/페이지네이션)
GET /api/v1/bending-items/filters ← 필터 옵션 (분류/재질/모델 distinct)
GET /api/v1/bending-items/{id} ← 상세 (options 전체)
POST /api/v1/bending-items ← 등록
PUT /api/v1/bending-items/{id} ← 수정
DELETE /api/v1/bending-items/{id} ← 삭제 (soft delete)
```
**응답 구조** (목록):
```json
{
"success": true,
"data": {
"data": [
{
"id": 15862, "code": "BD-BE-30",
"name": "하단마감재(스크린) EGI 3000mm",
"item_type": "PT", "item_category": "BENDING",
"item_name": "하단마감재", "item_sep": "스크린",
"item_bending": "하단마감재", "item_spec": "60*40",
"material": "EGI 1.55T", "model_name": null,
"model_UA": "인정", "search_keyword": null,
"rail_width": null, "registration_date": "2025-07-21",
"author": "개발자", "memo": null,
"exit_direction": null, "front_bottom_width": null,
"box_width": null, "box_height": null,
"bendingData": [
{"no":1,"input":15,"rate":"","sum":15,"color":false,"aAngle":false},
{"no":2,"input":14,"rate":"-1","sum":28,"color":false,"aAngle":false}
],
"prefix": "BE", "length_code": "30", "length_mm": 3000,
"legacy_bending_num": 288,
"width_sum": 193, "bend_count": 5,
"created_at": "2026-02-21 19:47:01"
}
],
"current_page": 1, "total": 170, "last_page": 6, "per_page": 30
}
}
```
### 2-2. 절곡품 모델 (가이드레일/케이스/하단마감재 통합)
```
GET /api/v1/guiderail-models ← 모델 목록
GET /api/v1/guiderail-models/filters ← 필터 옵션
GET /api/v1/guiderail-models/{id} ← 모델 상세 (부품 조합)
POST /api/v1/guiderail-models ← 모델 등록
PUT /api/v1/guiderail-models/{id} ← 모델 수정
DELETE /api/v1/guiderail-models/{id} ← 모델 삭제
```
**카테고리 필터**: `?item_category=GUIDERAIL_MODEL|SHUTTERBOX_MODEL|BOTTOMBAR_MODEL`
**응답 구조** (상세):
```json
{
"success": true,
"data": {
"id": 15914, "code": "GR-KDSS01-벽면형-SUS",
"name": "KDSS01 벽면형 SUS마감",
"item_category": "GUIDERAIL_MODEL",
"model_name": "KDSS01", "check_type": "벽면형",
"rail_width": 150, "rail_length": 150,
"finishing_type": "SUS마감",
"item_sep": "스크린", "model_UA": "인정",
"components": [
{
"orderNumber": 1,
"itemName": "1번(마감제)", "material": "SUS 1.2T",
"quantity": 2, "width_sum": 227,
"bendingData": [
{"no":1,"input":15,"rate":"0","sum":15,"color":true,"aAngle":false},
{"no":2,"input":13,"rate":"0","sum":28,"color":false,"aAngle":false}
],
"legacy_bending_num": "170"
}
],
"material_summary": {"SUS 1.2T": 599, "EGI 1.55T": 894},
"component_count": 4
}
}
```
### 2-3. 이미지 (기존 API 재사용)
```
POST /api/v1/items/{id}/files ← 업로드 (field_key: 'bending_diagram')
GET /api/v1/items/{id}/files ← 목록 조회
GET /api/v1/files/{id}/view ← 인라인 표시
DELETE /api/v1/items/{id}/files/{fileId} ← 삭제
```
---
## 3. React 화면 구성
### 3-1. 메뉴 구조 (사이드바)
```
생산 관리
├─ ... (기존)
└─ 절곡품 관리
├─ 기초관리 /bending/base
├─ 절곡품 (가이드레일) /bending/products
├─ 케이스 /bending/cases
└─ 하단마감재 /bending/bottombars
```
### 3-2. 기초관리 화면
**목록 (`/bending/base`)**:
| 컬럼 | API 필드 |
|------|---------|
| NO | id |
| 코드 | code |
| 대분류 | item_sep (스크린=파란배지, 철재=주황배지) |
| 인정 | model_UA |
| 분류 | item_bending |
| 품명 | item_name |
| 규격 | item_spec |
| 재질 | material |
| 모델 | model_name |
| 폭합 | width_sum |
| 절곡수 | bend_count |
| 등록일 | created_at |
**필터**: item_sep, item_bending, material, search (HTMX 실시간 검색)
**폼 (`/bending/base/{id}/edit`)**:
기본 정보 (4열 그리드):
- 코드*, 이름*, 품명*, 대분류*(select)
- 분류*(datalist), 재질*(datalist), 규격, 모델(datalist)
- 인정여부(select), 등록일(date), 작성자, 검색어
케이스 전용 (분류=케이스 시 표시):
- 점검구 방향(select), 전면밑(mm), 레일폭(mm), 케이스 너비(mm), 케이스 높이(mm)
절곡 입력 테이블:
- 동적 열 추가/삭제
- 행: 입력(number) → 연신율(text) → 연신율후(자동) → 합계(자동) → 음영(checkbox) → A각(checkbox)
- 연신율 규칙: `-1` → input-1mm, `1` → input+1mm, 빈값 → 그대로
- 폭합계 + 절곡횟수 자동 표시
- `bendingData` JSON으로 직렬화하여 API 전송
이미지: 파일 업로드 + Ctrl+V 클립보드 + 미리보기
### 3-3. 절곡품 화면 (3가지 타입)
**목록** (공통 테이블):
| 컬럼 | API 필드 |
|------|---------|
| NO | id |
| 모델명 | model_name |
| 대분류 | item_sep |
| 인정 | model_UA |
| 형상 | check_type |
| 레일폭×높이 | rail_width × rail_length |
| 마감 | finishing_type |
| 부품수 | component_count |
| 소요자재량 | material_summary |
**필터**: `item_category`(필수!), item_sep, model_UA, check_type, model_name, search
> ⚠️ `item_category` 없이 호출하면 3개 카테고리 60건이 섞여서 나옴
**폼 — 타입별 헤더 차이**:
| 필드 | 가이드레일 | 케이스 | 하단마감재 |
|------|:---:|:---:|:---:|
| 등록일/작성자/비고 | ✅ | ✅ | ✅ |
| 외형치수 (가로×세로) | ✅ 너비×폭 | ✅ 폭×높이+전면밑+레일폭 | ✅ 폭×높이 |
| 대분류 라디오 | ✅ | ❌ | ✅ |
| 인정/비인정 라디오 | ✅ | ❌ | ✅ |
| 형태 라디오 (벽면/측면) | ✅ | ❌ | ❌ |
| 점검구 방향 라디오 | ❌ | ✅ | ❌ |
| 모델 select | ✅ | ❌ | ✅ |
| 마감 select | ✅ | ❌ | ✅ |
| 품목검색어 | ✅ | ✅ | ✅ |
**절곡 부품 조립 섹션**:
- 부품별 절곡 테이블 (inline 편집)
- 부품 추가: 기초관리 검색 모달 (필터+체크박스+선택적용)
- 부품 삭제: DOM 즉시 제거
- 품명/재질/수량 inline 편집
- 순서 변경 (위로/아래로)
- `components` JSON으로 직렬화하여 API 전송
**재질별 폭합**: components에서 자동 계산 (`material_summary`)
**작업지시서 PDF**: 별도 인쇄 페이지 (`/{type}/{id}/print`)
- 레거시 포맷: 번호 | 재질 | 절곡치수(합계+음영) | 폭합 | 수량
- A각 표시 행
- A4 가로 인쇄 최적화
---
## 4. React 컴포넌트 구조 (설계안)
```
src/pages/bending/
├─ base/
│ ├─ BendingBaseList.tsx ← 기초관리 목록
│ └─ BendingBaseForm.tsx ← 등록/수정/조회 (mode 분기)
├─ products/
│ ├─ BendingProductList.tsx ← 절곡품 목록 (category prop으로 3타입 공용)
│ ├─ BendingProductForm.tsx ← 등록/수정/조회 (타입별 헤더 분기)
│ └─ BendingProductPrint.tsx ← 작업지시서 인쇄
├─ components/
│ ├─ BendingTable.tsx ← 절곡 입력 테이블 (공용 컴포넌트)
│ ├─ BendingSearchModal.tsx ← 기초관리 부품 검색 모달
│ ├─ PartListEditor.tsx ← 부품 조립 편집기
│ ├─ MaterialSummary.tsx ← 재질별 폭합 표시
│ └─ GuiderailPreview.tsx ← 견적 페이지용 미리보기
└─ hooks/
├─ useBendingItems.ts ← API 호출 훅
└─ useGuiderailModels.ts ← API 호출 훅
```
### 4-1. BendingTable 컴포넌트 (핵심)
```tsx
interface BendingData {
no: number;
input: number;
rate: string; // '' | '-1' | '1'
sum: number; // 자동 계산
color: boolean; // 음영 마킹
aAngle: boolean; // A각 표시
}
interface BendingTableProps {
data: BendingData[];
onChange: (data: BendingData[]) => void;
readOnly?: boolean;
}
```
### 4-2. BendingSearchModal 컴포넌트
```tsx
interface BendingSearchModalProps {
open: boolean;
onClose: () => void;
onSelect: (items: BendingItem[]) => void;
filters?: { item_sep?: string; item_bending?: string; };
}
```
### 4-3. GuiderailPreview 컴포넌트 (견적 페이지 연동)
```
┌─────────────────────────────────────────────────────┐
│ 가이드레일: KSS01 벽면형 | 인정 | SUS마감 | 70×120 │
├──────────────────────┬──────────────────────────────┤
│ 전개도 이미지 │ 부품 조합 │
│ ┌────────────────┐ │ # │ 부품 │ 재질 │ 수량 │
│ │ │ │ 1 │ 마감재 │ SUS │ 2 │
│ │ (R2 이미지) │ │ 2 │ 본체 │ EGI │ 1 │
│ │ │ │ 3 │ C형 │ EGI │ 1 │
│ └────────────────┘ │ 4 │ D형 │ EGI │ 1 │
│ │ 재질별 폭합 │
│ │ SUS: 406 | EGI: 398 │
└──────────────────────┴──────────────────────────────┘
```
---
## 5. 데이터 현황 (Step 1~3 완료 시점)
| 항목 | 건수 | 상태 |
|------|:---:|:---:|
| 기초관리 (BENDING) | 170건 | ✅ |
| ├ 전개도 임포트 | 139/170건 | ✅ (31건 chandj 원본 없음) |
| 가이드레일 모델 (GUIDERAIL_MODEL) | 20건 | ✅ |
| 케이스 모델 (SHUTTERBOX_MODEL) | 30건 | ✅ |
| 하단마감재 모델 (BOTTOMBAR_MODEL) | 10건 | ✅ |
| DB 메뉴 | 4개 | ✅ |
---
## 6. 주의사항
- ✅ 기존 GuideRailSection (작업지시서용) 무변경 — 별도 컴포넌트
- ✅ 기존 BendingInfoBuilder / PrefixResolver 무변경
- ✅ 이미지 없는 모델: 텍스트만 표시 (graceful degradation)
- ✅ MNG는 **샘플 확인용** — React가 **운영용**
- ✅ MNG/React 모두 동일한 API 엔드포인트 호출
- ⚠️ tenant_id 287 하드코딩 → React에서는 Sanctum Bearer 토큰으로 자동 해결
- ⚠️ **인증 전환**: React 작업 시 `ApiKeyMiddleware.php``allowWithoutAuth`에서 bending 관련 4줄 제거 → 다른 API와 동일한 2중 인증 (API Key + Bearer) 적용
- ⚠️ 재고 데이터 (stocks 153건) 이미 존재 — React에서 재고 연동 시 기존 Stock API 사용
- ⚠️ **운영 배포 전 정리 필요** (options 불필요 키 삭제):
- `source` (5130_migration 등) — 마이그레이션 추적용, 운영 불필요
- `legacy_prod`, `legacy_spec`, `legacy_slength` — PREFIX 생성 완료, 삭제 가능
- `legacy_bending_num`, `legacy_num`, `legacy_guiderail_num` — 이미지 매핑 완료, 삭제 가능
- `lot_managed`, `consumption_method`, `production_source`, `input_tracking` — 재고 시스템 별도 관리
- `author = '개발자'` — 실제 담당자로 변경 필요
- ⚠️ 레거시 키 삭제 후 `bending:import-*` 커맨드 재실행 불가 — 운영 확정 후 정리
- ⚠️ **운영 안정화 후 마이그레이션 커맨드 삭제 가능** (1회성 도구):
- `BendingFillOptions.php`, `BendingImportLegacy.php`, `BendingImportImages.php`
- `GuiderailImportLegacy.php`, `BendingProductImportLegacy.php`
- `BendingModelImportImages.php`, `BendingModelImportAssemblyImages.php`
- ⚠️ R2 이미지 + files 레코드는 운영 데이터 — 삭제 불가
---
## 7. MNG 샘플에서 발견된 실무 구현 노트
> React 구현 시 참고할 MNG 작업 중 발견된 이슈 및 해결 방법
### 7-1. API 호출 주의사항
| 이슈 | 원인 | 해결 |
|------|------|------|
| PUT/POST JSON body 파싱 안 됨 | Laravel API가 form-data만 파싱 | React axios는 JSON 자동 처리되므로 문제 없음 |
| `bendingData` 전송 | MNG form은 hidden input JSON 문자열 | React는 객체 배열 그대로 전송 가능 |
| pagination 메타 누락 | `ResourceCollection` 감싸면 메타 사라짐 | `paginator.transform()` 방식으로 수정 완료 |
| `unique:items,code` | Store 시 코드 중복 체크 | React form에서 에러 메시지 표시 필요 |
### 7-2. 절곡 테이블 구현 핵심 로직
```
연신율 보정 규칙:
rate = "" → 보정 없음 (input 그대로)
rate = "-1" → input - 1mm (절곡 시 1mm 줄어듦)
rate = "1" → input + 1mm
합계 = 보정 후 값의 누적합
폭합계 = 마지막 합계값
절곡횟수 = rate가 빈 문자열이 아닌 열의 수
```
### 7-3. 부품 추가 모달 동작
```
1. [+ 부품 추가] 클릭 → 모달 열기
2. GET /api/v1/bending-items?item_sep=&item_bending=&material=&search=&size=100
3. 체크박스로 복수 선택
4. [선택 적용] → 선택된 아이템의 bendingData를 components에 push
5. components JSON 직렬화 → hidden input → form submit
```
### 7-4. 타입별 라우트 매핑
| React 라우트 | API 파라미터 | MNG 참고 |
|-------------|------------|---------|
| `/bending/base/*` | `/api/v1/bending-items` | `BendingBaseController` |
| `/bending/products/*` | `/api/v1/guiderail-models?item_category=GUIDERAIL_MODEL` (20건) | `BendingProductController` |
| `/bending/cases/*` | `/api/v1/guiderail-models?item_category=SHUTTERBOX_MODEL` (30건) | 동일 컨트롤러 |
| `/bending/bottombars/*` | `/api/v1/guiderail-models?item_category=BOTTOMBAR_MODEL` (10건) | 동일 컨트롤러 |
> ⚠️ `item_category` 파라미터 누락 시 60건 전부 반환됨 — React에서 반드시 포함할 것
### 7-5. 작업지시서 PDF
MNG에서는 `window.print()` 기반 별도 인쇄 페이지(`/print`)로 구현.
React에서는 동일 방식 또는 html2pdf.js / react-to-print 라이브러리 사용 가능.
**인쇄 포맷 (레거시 동일):**
- 헤더: 모델명, 형태, 규격, 마감
- 테이블: 번호 | 재질 | 절곡치수(합계+음영) | A각 | 폭합 | 수량
- 재질별 폭합 요약
- A4 가로
### 7-6. 이미지 업로드 흐름
```
React:
1. <input type="file"> 또는 Ctrl+V 클립보드
2. POST /api/v1/items/{itemId}/files (FormData: file + field_key=bending_diagram)
3. 응답: { file_id, file_url }
4. 표시: GET /api/v1/files/{fileId}/view (inline 이미지)
```
### 7-7. 메뉴 구조 (사이드바)
```
절곡품 관리
├─ 기초관리 /bending/base (170건)
├─ 가이드레일 /bending/products (20건, GUIDERAIL_MODEL)
├─ 케이스 /bending/cases (30건, SHUTTERBOX_MODEL)
└─ 하단마감재 /bending/bottombars (10건, BOTTOMBAR_MODEL)
```
React 사이드바 메뉴는 DB `menus` 테이블 기반 동적 렌더링 — 이미 등록 완료.
### 7-8. 재고 연동 (향후)
절곡 부품 재고는 SAM 기존 재고 시스템에 통합:
- `stocks` 테이블: `item_type = 'bent_part'` (153건)
- `stock_lots` 테이블: LOT 기반 FIFO 재고
- 기존 Stock API 사용 가능 — 별도 재고 API 불필요