- step1: 데이터 임포트 완료 (170+60건), artisan 커맨드 7개 실행 결과 - step2: API 12개 엔드포인트 완료, item_category 필수 필터 추가 - step3: MNG 샘플 완료 (4개 메뉴, 이미지 473건) - step4: React 구현 가이드 전면 작성 (API 응답 구조, 컴포넌트 설계, 실무 노트) - 코드 체계 변경 불가 사유, 265vs170 차이 설명, 운영 전 정리 항목 추가
413 lines
16 KiB
Markdown
413 lines
16 KiB
Markdown
# 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 불필요
|