docs: [bending] 절곡품 관리 개발 완료 상태 업데이트

- step1: 데이터 임포트 완료 (170+60건), artisan 커맨드 7개 실행 결과
- step2: API 12개 엔드포인트 완료, item_category 필수 필터 추가
- step3: MNG 샘플 완료 (4개 메뉴, 이미지 473건)
- step4: React 구현 가이드 전면 작성 (API 응답 구조, 컴포넌트 설계, 실무 노트)
- 코드 체계 변경 불가 사유, 265vs170 차이 설명, 운영 전 정리 항목 추가
This commit is contained in:
강영보
2026-03-17 11:33:47 +09:00
parent 828b452186
commit fbd0510cc1
5 changed files with 564 additions and 91 deletions

View File

@@ -42,22 +42,42 @@ SAM은 절곡품의 "계산과 조합"(BendingInfoBuilder/PrefixResolver)은 잘
---
## 작업 순서
## 작업 순서 및 진행 상태
```
Step 1 (DB분석) → Step 2 (API) → Step 3 (MNG 화면) → Step 4 (React 연동)
레거시 매핑 options 확장 Blade + HTMX 견적 이미지
+ 데이터 정리 + 엔드포인트 + 메뉴 등록
✅ 완료 ✅ 완료 ✅ 완료 (샘플용) ⬜ 미착수
```
상세 계획: 아래 문서 참조
| 문서 | 내용 | 상태 |
|------|------|:---:|
| `step1-데이터분석.md` | 레거시 매핑 + options 확장 | ✅ 완료 |
| `step2-API.md` | API 엔드포인트 + 컨트롤러 설계 | ✅ 완료 |
| `step3-MNG화면.md` | Blade 뷰 + HTMX + 메뉴 등록 | ✅ 완료 |
| `step4-React연동.md` | React 운영 화면 구현 | ⬜ 미착수 |
| 문서 | 내용 |
|------|------|
| `step1-데이터분석.md` | 레거시 매핑 + options 확장 |
| `step2-API.md` | API 엔드포인트 + 컨트롤러 설계 |
| `step3-MNG화면.md` | Blade 뷰 + HTMX + 메뉴 등록 |
| `step4-React연동.md` | 견적 페이지 이미지 컴포넌트 |
### 완료된 작업 (2026-03-16~17)
**Step 1 완료:**
- `bending:fill-options` — BD-* prefix/분류 속성 자동 보강 (170건)
- `bending:import-legacy` — chandj 전개도(bendingData) 임포트 (139/170건)
- `guiderail:import-legacy` — chandj guiderail 20건 임포트
- `bending-product:import-legacy` — chandj shutterbox 30건 + bottombar 10건 임포트
**Step 2 완료:**
- `BendingItemController` — CRUD + filters + pagination (6 엔드포인트)
- `GuiderailModelController` — CRUD + filters (6 엔드포인트, 3개 카테고리 통합)
- `BendingItemResource` / `GuiderailModelResource` — API 응답 포맷
- `FormRequest` — Index/Store/Update 유효성 검증
- `ApiKeyMiddleware` — bending/guiderail/files 화이트리스트
**Step 3 완료 (MNG 샘플용):**
- 기초관리: 목록(13컬럼) + 폼(기본정보12필드 + 케이스전용5필드 + 절곡테이블 + 이미지업로드)
- 절곡품: 가이드레일/케이스/하단마감재 별도 메뉴 + 타입별 헤더 분기
- 절곡품 폼: 부품 추가(기초관리 검색 모달) + 삭제 + 수량/품명/재질 편집 + 절곡테이블 inline 편집
- 작업지시서: 레거시 포맷 인쇄 페이지 (`/print`)
- 파일: FileViewController (API R2 프록시) + 이미지 업로드/표시
- DB 메뉴: 기초관리 + 절곡품 + 케이스 + 하단마감재 (4개)
---
@@ -271,7 +291,7 @@ AList: [false,false,false...] sum:21, color:false, aAngle:false },
```
┌──────────────────────────────────────────────────────────────┐
│ MNG 사이드바
│ MNG
│ │
│ 생산 관리 │
│ ├─ 품목기준 필드 관리 │

View File

@@ -103,14 +103,58 @@
| `search_keyword` | varchar(50) | 검색어 |
| `material_summary` | text | 재질별 폭합 |
### 1-1b. 추가 파악 필요
### 1-1b. 추가 분석 결과 (완료)
| 작업 | 대상 | 비고 |
|------|------|------|
| BendingInfoBuilder.php 지원 모델 추출 | 코드 분석 | 기존 로직 파악 |
| PrefixResolver.php PREFIX 규칙 추출 | 코드 분석 | 기존 로직 파악 |
| bendingfee 82건 구조 | 절곡 단가 — SAM 연동 필요 여부 | |
| bendingmap 9건 구조 | 매핑 용도 확인 | |
#### BendingInfoBuilder 지원 모델
`getMaterialMapping()` (line 924~960)에서 정의:
| 그룹 | 모델 | 마감재 재질 | 본체 재질 |
|------|------|-----------|----------|
| Group 1 (SUS 전용) | KQTS01, KSS01, KSS02 | SUS 1.2T | EGI 1.55T |
| Group 2 (철재) | KTE01 | EGI 1.55T (SUS마감 시 +SUS 1.2T) | EGI 1.55T |
| Group 3 (기타) | KSE01, KWE01 등 | EGI 1.55T (SUS마감 시 +SUS 1.2T) | EGI 1.55T |
**미지원 모델**: KDSS01, 스크린비인정 (guiderail 테이블에는 있지만 Builder에 미등록)
#### 🔴 CRITICAL: BOM 카테고리 분류가 한글 BD 코드 의존
`BendingInfoBuilder.categorizeBomItem()` (line 401~434):
```php
str_starts_with($code, 'BD-가이드레일') guideRail
str_starts_with($code, 'BD-케이스') shutterBox_case
str_starts_with($code, 'BD-마구리') shutterBox_finCover
str_starts_with($code, 'BD-L-BAR') detail_lbar
str_starts_with($code, 'BD-보강평철') detail_reinforce
```
**→ BD-한글 패턴(58건)을 PREFIX로 변환하면 BOM 분류 로직이 깨짐!**
**→ 결정: 한글 코드는 유지하고, `options`에 속성만 추가하는 방식으로 진행**
#### PrefixResolver PREFIX 규칙
| 카테고리 | partType → prefix | 비고 |
|----------|-------------------|------|
| **가이드레일(벽면)** | finish→RS/RE, body→RM, c_type→RC, d_type→RD, extra→YY, base→XX | KTE body→RT |
| **가이드레일(측면)** | finish→SS/SE, body→SM, c_type→SC, d_type→SD, extra→YY, base→XX | KTE body→ST |
| **하단마감재** | main→BE/BS/TS, lbar→LA, reinforce→HH, extra→YY | 재질 기반 분기 |
| **셔터박스** | front→CF, lintel→CL, inspection→CP, rear_corner→CB, top/fin→XX | 고정 |
| **연기차단재** | smoke→GI | 전용 길이코드(53/54/83/84) |
#### bendingfee (82건) — `price_bend` 테이블
- **용도**: 절곡 BOM 단가 (모델별 단가 JSON)
- **구조**: `itemList` (JSON), `registedate`, `is_deleted`
- **사용법**: 최신 1건만 사용 (`ORDER BY num DESC LIMIT 1`)
- **관련 테이블**: `BDmodels` (모델별 조회)
- **SAM 연동**: 1차 제외 (단가 관리는 별도 Phase)
#### bendingmap (9건)
- **용도**: 절곡품 그룹화/매핑 (생산 현장용)
- **컬럼**: prodcode, railtype, boxsize, boxexit, frontbottom, railwidth, search_tag
- **SAM 연동**: 1차 제외 (그룹화는 options.item_bending으로 대체)
### 1-2. SAM BD 품목 현황 (170건)
@@ -173,9 +217,64 @@ num:101 본체 EGI 120*70 ↔ BD-RM-30 (PREFIX-LEN — 길이 기준)
```
**핵심 결정사항**:
- BD-한글 패턴(58건)을 BD-PREFIX-LEN으로 통일할지
- 레거시 265건 중 SAM에 없는 항목 → 신규 생성 범위
- 매핑 애매한 항목 → 사업부 확인 목록
- ~~BD-한글 패턴(58건)을 BD-PREFIX 으로 통일~~ **취소** — BendingInfoBuilder.categorizeBomItem()이 한글 코드 의존
- **BD-한글 패턴(58건)은 코드 유지, `options`에 속성만 추가**
- BD-PREFIX-LEN(112건)도 options 속성 보강 (item_sep, item_bending 등)
- 레거시 265건 중 SAM에 없는 항목 → 신규 생성 범위 확인 필요
### 🔴 chandj 265건 vs SAM 170건 차이 설명
**구조가 다르지만 데이터는 동일**:
```
chandj 265건 = 절곡 "형상" (규격별 1건)
예: 마감재 SUS 120*70 → 전개도 [10,11,110,30,15,15,15] ← 1건
SAM 170건 = 절곡 "제품" (길이별 확장)
예: BD-RS-24 (2438mm) ┐
BD-RS-30 (3000mm) │ 모두 같은 전개도
BD-RS-35 (3500mm) │ (chandj 1건의 형상을 공유)
BD-RS-40 (4000mm) │
BD-RS-43 (4300mm) ┘ ← 5건
```
| 항목 | chandj | SAM | 관계 |
|------|:---:|:---:|------|
| 가이드레일 부품 | 71건 (규격별) | 74건 (길이별) | 1:N (형상 1 → 길이 여러 개) |
| 케이스 부품 | 160건 (규격×크기별) | 34건 (PREFIX-LEN) | N:1 (여러 규격 → 모델 components로 통합) |
| 하단마감재 부품 | 11건 | 21건 | 1:N |
| 마구리/기타 | 23건 | 41건 | 1:N |
**누락 없는 이유**:
- chandj 265건의 **전개도 데이터**는 SAM 170건의 `options.bendingData`에 포함 (139건 매핑)
- chandj에서 SAM에 직접 없는 부품들은 **모델(GR/SB/BB)의 components**에서 `legacy_bending_num`으로 참조
- 추가 임포트 **불필요**
---
### 🔴 코드 체계 변경 불가 사유
**BD 코드(절곡 부품)는 변경 금지:**
`BendingInfoBuilder.categorizeBomItem()` (line 401~434)에서 코드 접두사 기반으로 BOM 카테고리 분류:
```php
str_starts_with($code, 'BD-가이드레일') guideRail
str_starts_with($code, 'BD-케이스') shutterBox_case
str_starts_with($code, 'BD-마구리') shutterBox_finCover
str_starts_with($code, 'BD-L-BAR') detail_lbar
str_starts_with($code, 'BD-보강평철') detail_reinforce
```
BD-PREFIX-LEN 코드(BD-RS-30 등)도 `PrefixResolver` + LOT 재고에서 참조.
**견적→BOM→작업지시 전체 흐름이 깨지므로 코드 변경 불가**
**모델 코드(GR/SB/BB)는 변경 가능** — 신규 생성이라 참조 없음:
| 코드 | 카테고리 | 변경 가능 | 이유 |
|------|---------|:---:|------|
| `BD-*` (170건) | BENDING | ❌ | BendingInfoBuilder + PrefixResolver + LOT 의존 |
| `GR-*` (20건) | GUIDERAIL_MODEL | ✅ | 신규, 참조 없음 |
| `SB-*` (30건) | SHUTTERBOX_MODEL | ✅ | 신규, 참조 없음 |
| `BB-*` (10건) | BOTTOMBAR_MODEL | ✅ | 신규, 참조 없음 |
---
@@ -247,36 +346,45 @@ num:101 본체 EGI 120*70 ↔ BD-RM-30 (PREFIX-LEN — 길이 기준)
→ 레거시 inputList/bendingrateList/sumList/colorList
```
### 2-3. artisan command
### 2-3. artisan command (✅ 전체 실행 완료)
```bash
# 1단계
php artisan bending:fill-options --dry-run # 미리보기
php artisan bending:fill-options # 실행
# 1단계: prefix/분류 속성 보강 (170건)
php artisan bending:fill-options # ✅ 완료
# 2단계
php artisan bending:import-legacy --dry-run
php artisan bending:import-legacy
# 2단계+3단계: 전개도(bendingData) + 속성 임포트 (139/170건)
php artisan bending:import-legacy # ✅ 완료 (31건 chandj 원본 없음)
# 3단계 (2단계에 포함 가능)
# 가이드레일 모델 임포트 (20건)
php artisan guiderail:import-legacy # ✅ 완료
# 케이스+하단마감재 모델 임포트 (30+10건)
php artisan bending-product:import-legacy # ✅ 완료
# 이미지 마이그레이션
php artisan bending:import-images # ✅ 기초관리 부품 이미지 138건
php artisan bending-model:import-images # ✅ 모델 부품별 이미지 275건
php artisan bending-model:import-assembly-images # ✅ 결합형태 이미지 60건
```
---
## 3. 회귀 테스트 (필수)
## 3. 회귀 테스트
| 테스트 | 확인 내용 | 판정 기준 |
|--------|----------|----------|
| BendingInfoBuilder | 견적 BOM 계산 결과 | 변경 전/후 동일 |
| PrefixResolver | BD 코드 자동 결정 | 변경 전/후 동일 |
| 작업지시서 절곡 섹션 | GuideRailSection 렌더링 | 정상 표시 |
| 절곡 검사 | inspection-config API | 정상 응답 |
| 견적→수주→작업지시 | 전체 흐름 1건 | 오류 없음 |
| 테스트 | 확인 내용 | 판정 기준 | 상태 |
|--------|----------|----------|:---:|
| BendingInfoBuilder | 견적 BOM 계산 결과 | 변경 전/후 동일 | ⚠️ BD 코드 무변경이므로 영향 없음 |
| PrefixResolver | BD 코드 자동 결정 | 변경 전/후 동일 | ⚠️ 무변경 |
| 작업지시서 절곡 섹션 | GuideRailSection 렌더링 | 정상 표시 | ⚠️ 무변경 |
| CRUD 테스트 | 기초관리/모델 전체 | 생성/조회/수정/삭제 | ✅ 검증 완료 |
| 테넌트 격리 | 287 vs 1 데이터 분리 | 각 테넌트 독립 조회 | ✅ 검증 완료 |
| 이미지 표시 | R2→API→MNG 프록시 | 정상 표시 | ✅ 검증 완료 |
---
## 4. 산출물
- [ ] 매핑 테이블 (레거시 num ↔ SAM item_id)
- [ ] artisan command (bending:fill-options, bending:import-legacy)
- [ ] 회귀 테스트 결과
- [x] 매핑 테이블 (legacy_bending_num으로 chandj↔SAM 연결)
- [x] artisan command 7개 (위 목록 참조)
- [x] CRUD 검증 완료
- [x] 이미지 마이그레이션 완료 (총 473건 R2 업로드)

View File

@@ -1,7 +1,8 @@
# Step 2: API 엔드포인트
# Step 2: API 엔드포인트 ✅ 완료
> **프로젝트**: API (`sam/api`)
> **선행 조건**: Step 1 완료
> **상태**: ✅ 구현 완료 (2026-03-16~17)
> **참조**: `standards/api-rules.md`, `standards/options-column-policy.md`, `rules/item-policy.md`
---
@@ -58,21 +59,41 @@
?item_sep=스크린 # 대분류
&item_bending=가이드레일 # 중분류
&material=SUS # 재질 (부분 매칭)
&model_UA=인정 # 인정여부
&model_UA=인정 # 인정여부
&search=KSS01 # 통합 검색 (이름/검색어/규격)
&page=1&size=50 # 페이지네이션 (size — api-rules 기준)
```
### 2-2. 절곡품 모델 관리 (합)
### 2-2. 절곡품 모델 관리 (가이드레일/케이스/하단마감재 통합)
| Method | Path | 설명 | 비고 |
|--------|------|------|------|
| GET | `/api/v1/guiderail-models` | 모델 목록 (타입별) | ?type=가이드레일 |
| GET | `/api/v1/guiderail-models` | 모델 목록 | `?item_category=` 필수 |
| 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}` | 모델 삭제 (soft delete) | |
**카테고리 구분** (필수 파라미터):
| item_category | 메뉴 | 건수 | 코드 패턴 |
|--------------|------|:---:|---------|
| `GUIDERAIL_MODEL` | 가이드레일 | 20 | `GR-KSS01-벽면형-SUS` |
| `SHUTTERBOX_MODEL` | 케이스 | 30 | `SB-500*350-밑면` |
| `BOTTOMBAR_MODEL` | 하단마감재 | 10 | `BB-KSS01-SUS` |
**필터 파라미터** (GET /api/v1/guiderail-models):
```
?item_category=GUIDERAIL_MODEL # 필수: 카테고리 구분
&item_sep=스크린 # 대분류
&model_UA=인정 # 인정여부
&check_type=벽면형 # 형상 (가이드레일만)
&model_name=KSS01 # 모델명
&search=KSS01 # 통합 검색
&page=1&size=50
```
---
## 3. 구현 파일 구조

View File

@@ -1,7 +1,8 @@
# Step 3: MNG 관리 화면 (Blade + HTMX)
# Step 3: MNG 관리 화면 (Blade + HTMX) ✅ 완료
> **프로젝트**: MNG (`sam/mng`)
> **선행 조건**: Step 2 (API 엔드포인트) 완료
> **상태**: ✅ 샘플 구현 완료 (2026-03-16~17)
> **참조**: 프로토타입 `SAM/work/절곡/`, MNG 기존 Blade 패턴
---
@@ -293,7 +294,7 @@ class FileViewController extends Controller
┌───────────────────────────────────┬──────────────────┐
│ [기본 정보] │ [형상 이미지] │
│ 등록일 | 작성자 │ 이미지 업로드 │
├───────────────────────────────────┤ │
├───────────────────────────────────┤****
│ [케이스 정보] │ 품목검색어 │
│ 가로(폭) × 세로(높이) │ 비고 │
│ 전면밑: [50] | 레일폭: [75] │ │

View File

@@ -1,43 +1,272 @@
# Step 4: React 견적 화면 이미지 연동
# Step 4: React 절곡품 관리 화면 + 견적 이미지 연동 ⬜ 미착수
> **프로젝트**: React (`sam/react`)
> **선행 조건**: Step 2 (API), Step 3 (MNG에서 데이터 등록 후)
> **참조**: 기존 GuideRailSection 컴포넌트
> **선행 조건**: Step 2 (API), Step 3 (MNG 샘플 ✅)
> **상태**: ⬜ 미착수
> **참조**: MNG 샘플 화면, 기존 GuideRailSection 컴포넌트
---
## 1. 목적
## 1. 개요
견적 페이지(`/sales/quote-management/new`)에서 가이드레일 모델 선택 시
전개도 이미지 + 부품 조합 테이블을 표시한다.
MNG에서 샘플로 구현/검증한 절곡품 관리 기능을 React 운영 화면으로 이관한다.
모든 API 엔드포인트는 Step 2에서 완료되어 있으므로, **프론트엔드 구현만** 필요.
---
## 2. 현재 흐름
## 2. API 엔드포인트 (Step 2 완료 — 그대로 사용)
### 2-1. 기초관리 (절곡 부품)
```
제품 선택 → BOM 계산 (BendingInfoBuilder)
→ product_code, finish_material 확정
→ 가이드레일 모델 결정
→ 텍스트만 표시 ❌ 이미지 없음
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)
```
## 3. 목표 흐름
**응답 구조** (목록):
```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
}
}
```
제품 선택 → BOM 계산 (기존 그대로)
→ 모델 확정
→ GET /api/guiderail-models/{id} 호출 🆕
→ GuiderailPreview 컴포넌트 렌더링 🆕
├─ 전개도 이미지
└─ 부품 조합 테이블 (부품명/재질/수량/전개폭)
### 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} ← 삭제
```
---
## 4. 구현 사항
## 3. React 화면 구성
### 4-1. GuiderailPreview 컴포넌트
### 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 컴포넌트 (견적 페이지 연동)
```
┌─────────────────────────────────────────────────────┐
@@ -46,44 +275,138 @@
│ 전개도 이미지 │ 부품 조합 │
│ ┌────────────────┐ │ # │ 부품 │ 재질 │ 수량 │
│ │ │ │ 1 │ 마감재 │ SUS │ 2 │
│ │ (이미지) │ │ 2 │ 본체 │ EGI │ 1 │
│ │ (R2 이미지) │ │ 2 │ 본체 │ EGI │ 1 │
│ │ │ │ 3 │ C형 │ EGI │ 1 │
│ └────────────────┘ │ 4 │ D형 │ EGI │ 1 │
│ │ 재질별 폭합 │
│ │ SUS: 406 | EGI: 398 │
└──────────────────────┴──────────────────────────────┘
```
### 4-2. 삽입 위치
---
견적 페이지에서 BOM 결과 표시 영역 하단 (기존 레이아웃 무변경)
## 5. 데이터 현황 (Step 1~3 완료 시점)
### 4-3. 데이터 흐름
```
BOM 계산 결과 → product_code + finish_material
→ API 호출: GET /api/guiderail-models?model={code}&check_type={형상}
→ 응답: image_url + components + material_summary
→ GuiderailPreview 렌더링
```
| 항목 | 건수 | 상태 |
|------|:---:|:---:|
| 기초관리 (BENDING) | 170건 | ✅ |
| ├ 전개도 임포트 | 139/170건 | ✅ (31건 chandj 원본 없음) |
| 가이드레일 모델 (GUIDERAIL_MODEL) | 20건 | ✅ |
| 케이스 모델 (SHUTTERBOX_MODEL) | 30건 | ✅ |
| 하단마감재 모델 (BOTTOMBAR_MODEL) | 10건 | ✅ |
| DB 메뉴 | 4개 | ✅ |
---
## 5. 주의사항
## 6. 주의사항
- 기존 견적 계산 로직 무변경
- 기존 GuideRailSection (작업지시서용) 무변경 — 별도 컴포넌트
- 이미지 없는 모델: 텍스트만 표시 (graceful degradation)
- 모바일 반응형 처리
- 기존 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 레코드는 운영 데이터 — 삭제 불가
---
## 6. 범위 (추후 확정)
## 7. MNG 샘플에서 발견된 실무 구현 노트
| 영역 | 설명 | 시점 |
> React 구현 시 참고할 MNG 작업 중 발견된 이슈 및 해결 방법
### 7-1. API 호출 주의사항
| 이슈 | 원인 | 해결 |
|------|------|------|
| 견적 이미지 연동 | GuiderailPreview 컴포넌트 | Step 1~3 완료 후 |
| 절곡품 관리 화면 | React 버전 CRUD (MNG 대체) | MNG 샘플 검증 후 |
| 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에서 에러 메시지 표시 필요 |
- MNG는 **샘플 확인용** — API 연동 검증이 목적
- **실제 운영 화면은 React**에서 구현 (MNG와 동일한 API 호출)
- React 화면 상세 설계는 MNG 검증 후 별도 문서로 작성 예정
- 현재 문서는 견적 이미지 연동 범위만 정의
### 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 불필요