# 절곡품(Bending Parts) 관리 현황 분석

> **작성일**: 2026-03-16
> **분류**: 갭 분석 / 기능 비교
> **대상**: 경동기업(5130) 가이드레일 시스템 vs SAM 시스템

---

## 배경 및 질문

경동기업(5130)에는 절곡품(가이드레일) 관리 리스트가 있다.
(`5130.codebridge-x.com/guiderail/list.php`)

견적/수주를 하면 해당 제품의 가이드레일을 불러와 **이미지들을 보여주는** 역할을 하며,
개별 절곡품들을 **조합**하여 가이드레일을 구성하는 구조이다.

SAM 시스템에서도 견적(`dev.sam.kr/sales/quote-management/new`)에서 가이드레일이 나오는데,
경동기업과 같이 **절곡품 이미지를 보여주고 조합**할 수 있어야 한다.

### 구체적 질문

1. SAM의 `items` 테이블에 `BD-XX-XXX` 코드 품목들이 있는데, 경동기업 절곡품처럼 **이미지 표현과 조합 구성이 가능한 상태인가?**
2. 초기 개발 시 절곡품을 고려하지 않고 개발 후 추후 items에 밀어넣었는데, **경동기업 수준의 관리가 되고 있는가?**
3. 경동기업 절곡품 시스템과 SAM 시스템 간 **갭은 무엇인가?**

---

## 1. 경동기업(5130) 절곡품 관리 체계

### 1.1 시스템 구조

```
bending 테이블 (개별 절곡품)
  ├─ 품명, 대분류(스크린/철재), 중분류(가이드레일/케이스/하단마감재)
  ├─ 규격(120*70), 재질(SUS 1.2T), 모델명(KSS01)
  ├─ 인정/비인정 구분
  ├─ 치수 JSON (inputList), 연신율 JSON (bendingrateList)
  ├─ 합계 JSON (sumList), 색상 마킹 (colorList)
  └─ 검색 키워드 (130x75한빛, 주일-130x70 등)

guiderail 테이블 (조합 전개도)
  ├─ 모델별 부품 조합 정의 (proditem1~8)
  ├─ 전개도 이미지 저장
  └─ 견적에서 호출되어 이미지 표시
```

### 1.2 주요 기능

| 기능 | 구현 파일 | 설명 |
|------|----------|------|
| 절곡품 마스터 리스트 | `guiderail/list.php` | 22개 모델 정의, CRUD 관리 |
| 절곡품 이미지 관리 | `put_guiderail_image.php` | 이미지 업로드 + Canvas 드로잉 (`drawingTool.js`) |
| 모델별 부품 조합 | `fun_guiderail.php` | KSS01 등 모델마다 1~8개 부품 조합 (`getProductData()`) |
| 절곡품 검색 | `search_bending.php` | 대분류/규격/재질/인정여부 복합 필터 |
| 견적에서 호출 | `fetch_guiderail_detail.php` | 견적 시 해당 모델의 이미지 + 부품 표시 |
| 메타데이터 | `guiderail.json` | 22개 모델 정의 (이미지 경로, 분류, 마감 유형) |

### 1.3 모델별 부품 조합 예시 (KSS01 벽면형)

```
proditem1: ①②마감재 (SUS 1.2T, 수량2)
proditem2: ③본체    (EGI 1.55T, 수량1)
proditem3: ④벽면형-C (EGI 1.55T, 수량1)
proditem4: ⑤벽면형-D (EGI 1.55T, 수량1)
```

### 1.4 이미지 관리

- **저장 경로**: `/5130/guiderail/images/`, `/5130/bending/img/`
- **파일명 규칙**: `YYYY_MM_DD_HH_MM_SS_[모델명].png`
- **드로잉 도구**: Canvas 기반 `drawingTool.js`로 실시간 전개도 생성

### 1.5 bending 테이블 주요 컬럼

| 컬럼 | 설명 | 예시 |
|------|------|------|
| `itemName` | 품명 | 가이드레일 |
| `item_sep` | 대분류 | 스크린, 철재 |
| `item_bending` | 중분류 | 가이드레일, 케이스, 하단마감재 |
| `item_spec` | 규격 | 120*70, 120*120 |
| `material` | 재질 | SUS 1.2T, EGI 1.55T |
| `model_UA` | 인정여부 | 인정, 비인정 |
| `model_name` | 모델명 | KSS01, KWE01, KQTS01 |
| `search_keyword` | 검색어 | 130x75한빛 |
| `inputList` | 치수 JSON | [...] |
| `bendingrateList` | 연신율 JSON | [...] |
| `sumList` | 합계 JSON | [...] |
| `colorList` | 색상 마킹 JSON | [...] |
| `rail_width` | 레일 폭 | 70, 120 |

---

## 2. SAM 시스템 현재 상태

### 2.1 BD-XX-XXX 코드 체계

`BendingItemSeeder`로 ~40개 품목이 `items` 테이블에 등록되어 있다.

**코드 구조**: `BD-{PREFIX}-{LENGTH_CODE}`

| PREFIX | 용도 | 예시 |
|--------|------|------|
| RS/RE | 가이드레일 마감재 (벽면) | BD-RS-30 |
| SS/SE | 가이드레일 마감재 (측면) | BD-SS-35 |
| RM | 가이드레일 본체 (벽면) | BD-RM-42 |
| SM | 가이드레일 본체 (측면) | BD-SM-24 |
| RC/RD | 가이드레일 C형/D형 (벽면) | BD-RC-30 |
| BS/TS | 하단마감재 (SUS/철재) | BD-BS-40 |
| LA | L-Bar | BD-LA-40 |
| HH | 보강평철 | BD-HH-30 |
| CF/CL/CP/CB | 셔터박스 부품 | BD-CF-35 |
| GI | 연기차단재 | BD-GI-54 |
| XX | 하부BASE/상부덮개/마구리 (공용) | BD-XX-35 |
| YY | 별도마감 | BD-YY-30 |

**길이코드 매핑**:

| 코드 | mm | 코드 | mm |
|------|-----|------|-----|
| 12 | 1219 | 40 | 4000 |
| 24 | 2438 | 41 | 4150 |
| 30 | 3000 | 42 | 4200 |
| 35 | 3500 | 43 | 4300 |

### 2.2 items 테이블 저장 구조

```json
{
  "item_type": "PT",
  "item_category": "BENDING",
  "code": "BD-RS-30",
  "name": "가이드레일 마감재 3000mm",
  "options": {
    "source": "bending_item_seeder",
    "lot_managed": true,
    "consumption_method": "auto",
    "production_source": "self_produced",
    "input_tracking": true,
    "prefix": "RS",
    "length_code": "30",
    "length_mm": 3000
  }
}
```

### 2.3 SAM에서 구현된 기능

| 기능 | 파일 | 설명 |
|------|------|------|
| 동적 BOM 생성 | `BendingInfoBuilder.php` (1172줄) | 제품코드별 자동 부품 조합 |
| Prefix 자동 결정 | `PrefixResolver.php` (300줄) | BD-코드 자동 매핑 |
| 작업지시서 절곡 표시 | `GuideRailSection` 등 (React) | 가이드레일/하단마감재/셔터박스/연기차단재 테이블 |
| 절곡 검사 (Phase 3) | `inspection-config` API | 공정 자동 판별, BOM 기반 구성품 로딩 |
| 정적 이미지 | `api/public/images/bending/` | 22개 이미지 (6개 모델 × 벽면/측면) |
| 재질 매핑 | `BendingInfoBuilder` | 제품코드별, 마감재질별 자동 결정 |

### 2.4 데이터 흐름

```
Quote (견적)
  └─ calculation_inputs: { items[], bomResults[] }
        ↓ OrderService::store()
OrderNode (개소별)
  └─ options: { product_code, bom_result }
        ↓ OrderService::createWorkOrders()
WorkOrder (작업지시)
  └─ work_order_items[].options: {
       bending_info: {
         productCode, finishMaterial,
         guideRail: { wall, side },
         bottomBar, shutterBox, smokeBarrier
       },
       slat_info: { joint_bar, glass_qty }
     }
        ↓ 프론트 렌더링
작업일지
  ├─ 가이드레일 (이미지 + 테이블)
  ├─ 하단마감재
  ├─ 셔터박스
  └─ 연기차단재
```

---

## 3. 갭 분석

### 3.1 핵심 갭 (경동기업에 있고 SAM에 없는 것)

#### 🔴 ① 절곡품 마스터 관리 화면

| 항목 | 경동기업(5130) | SAM |
|------|--------------|-----|
| 관리 화면 | `guiderail/list.php` — CRUD | ❌ 없음 |
| 필터링 | 대분류/규격/재질/인정여부 복합 필터 | ❌ 없음 |
| 검색 | 키워드 기반 통합 검색 | ❌ 없음 |

BD-XX-XXX 품목이 `items` 테이블에 등록만 되어 있고, **절곡품 전용 관리 화면이 없다.**

#### 🔴 ② 견적 시 가이드레일 이미지 표시

| 항목 | 경동기업(5130) | SAM |
|------|--------------|-----|
| 견적 화면 | 모델 선택 → 전개도 이미지 + 부품 조합 표시 | ❌ 이미지 미표시 |
| 호출 방식 | `fetch_guiderail_detail.php` | 해당 기능 없음 |

견적 페이지(`/sales/quote-management/new`)에서 가이드레일 선택은 되지만 **이미지가 표시되지 않는다.**

#### 🔴 ③ 절곡품 개별 전개도 데이터

| 항목 | 경동기업(5130) | SAM |
|------|--------------|-----|
| 치수 데이터 | `inputList` JSON | ❌ 없음 |
| 연신율 데이터 | `bendingrateList` JSON | ❌ 없음 |
| 합계 계산 | `sumList` JSON | ❌ 없음 |
| 색상 마킹 | `colorList` JSON | ❌ 없음 |
| 전개도 드로잉 | Canvas 기반 실시간 생성 | ❌ 없음 |

`BendingInfoBuilder`는 **조합 로직만** 있고, 개별 절곡품의 전개도 상세 데이터는 저장되지 않는다.

### 3.2 보조 갭

#### 🟡 ④ 절곡품 속성 풍부도

| 속성 | 경동기업 `bending` 테이블 | SAM `items.options` |
|------|------------------------|---------------------|
| 품명 | ✅ `itemName` | ✅ `name` |
| 대분류 | ✅ `item_sep` (스크린/철재) | ❌ 없음 |
| 중분류 | ✅ `item_bending` (가이드레일/케이스) | ❌ 없음 |
| 규격 | ✅ `item_spec` (120*70) | ❌ 없음 |
| 재질 | ✅ `material` (SUS 1.2T) | ❌ 없음 |
| 모델명 | ✅ `model_name` (KSS01) | ❌ 없음 |
| 인정여부 | ✅ `model_UA` | ❌ 없음 |
| 검색키워드 | ✅ `search_keyword` | ❌ 없음 |
| 레일 폭 | ✅ `rail_width` | ❌ 없음 |
| prefix | ❌ | ✅ `options.prefix` |
| 길이코드 | ❌ | ✅ `options.length_code` |
| 길이(mm) | ❌ | ✅ `options.length_mm` |

#### 🟡 ⑤ 이미지 업로드/드로잉 기능

| 항목 | 경동기업(5130) | SAM |
|------|--------------|-----|
| 이미지 업로드 | ✅ `put_guiderail_image.php` | ❌ 없음 |
| Canvas 드로잉 | ✅ `drawingTool.js` | ❌ 없음 |
| 이미지 저장 | 동적 생성 + 파일 저장 | 정적 파일 22개만 |

### 3.3 SAM이 우위인 부분

| 영역 | 설명 |
|------|------|
| 동적 BOM | `BendingInfoBuilder`가 제품코드별 자동 조합 (5130은 하드코딩) |
| Prefix 체계 | `PrefixResolver`로 BD-코드 자동 결정 (5130은 수동) |
| 작업지시 연동 | `work_order_items.options.bending_info`로 구조화된 데이터 전달 |
| 검사 동적 구현 | Phase 3에서 API 기반 검사 완료 (5130보다 진보적) |
| 멀티테넌시 | 테넌트별 독립 관리 가능 (5130은 단일) |

---

## 4. 종합 비교

```
경동기업(5130)                         SAM 시스템
─────────────────────────────         ─────────────────────────────
[절곡품 마스터 관리] ─────────────→    ❌ 없음 (items에 등록만)
  ├─ 이미지/전개도 관리                  ├─ 정적 이미지 22개만
  ├─ 치수/연신율 데이터                  ├─ ❌ 없음
  ├─ 검색/필터                          └─ ❌ 없음
  └─ CRUD 화면

[가이드레일 조합] ───────────────→    ✅ BendingInfoBuilder (더 체계적)
  ├─ 모델별 부품 정의                   ├─ ✅ 동적 BOM 생성
  ├─ 재질 자동 매핑                     ├─ ✅ PrefixResolver
  └─ 견적에서 이미지 표시                └─ ❌ 이미지 미표시

[절곡 검사] ─────────────────────→    ✅ Phase 3 완료 (더 진보적)
  ├─ 중간검사                           ├─ ✅ inspection-config API
  └─ 하드코딩 검사                       └─ ✅ 동적 검사
```

---

## 5. 결론

**SAM은 절곡품의 "계산과 조합"은 잘 되어 있지만, "관리와 시각화"가 빠져 있다.**

경동기업처럼 절곡품을 독립적으로 관리하고 견적에서 이미지를 보여주려면,
**절곡품 마스터 관리 기능**을 새로 구현해야 한다.

### 필요 작업 (예상)

1. **절곡품 마스터 관리 화면** — mng 또는 react에 CRUD + 필터링
2. **items.options 속성 확장** — 대분류, 중분류, 규격, 재질, 모델명 등
3. **견적 화면 이미지 연동** — 모델 선택 시 가이드레일 전개도 이미지 표시
4. **전개도 데이터 구조** — 치수/연신율/합계 JSON 저장 방안 설계
5. **이미지 업로드 기능** — 절곡품별 전개도 이미지 관리

---

## 6. API 구현 완료 현황 (2026-03-16)

### 6.1 절곡품 마스터 API (`/api/v1/bending-items`)

| Method | Path | 설명 | 상태 |
|--------|------|------|------|
| GET | `/api/v1/bending-items` | 목록 (필터+페이지네이션) | ✅ 완료 |
| GET | `/api/v1/bending-items/filters` | 필터 옵션 (드롭다운용) | ✅ 완료 |
| GET | `/api/v1/bending-items/{id}` | 상세 | ✅ 완료 |
| POST | `/api/v1/bending-items` | 등록 | ✅ 완료 |
| PUT | `/api/v1/bending-items/{id}` | 수정 | ✅ 완료 |
| DELETE | `/api/v1/bending-items/{id}` | 삭제 | ✅ 완료 |

**API 파일 목록**:

| 파일 | 설명 |
|------|------|
| `app/Http/Controllers/Api/V1/BendingItemController.php` | 컨트롤러 |
| `app/Services/BendingItemService.php` | 서비스 (OPTION_KEYS 정의) |
| `app/Http/Resources/Api/V1/BendingItemResource.php` | 응답 리소스 |
| `app/Http/Requests/Api/V1/BendingItemIndexRequest.php` | 목록 검증 |
| `app/Http/Requests/Api/V1/BendingItemStoreRequest.php` | 등록 검증 |
| `app/Http/Requests/Api/V1/BendingItemUpdateRequest.php` | 수정 검증 |
| `routes/api/v1/production.php` | 라우트 정의 (127~135행) |

### 6.2 이미지 업로드 API (`/api/v1/items/{id}/files`)

| Method | Path | 설명 | 상태 |
|--------|------|------|------|
| POST | `/api/v1/items/{id}/files` | 업로드 (`field_key=bending_diagram`) | ✅ 완료 |
| GET | `/api/v1/items/{id}/files` | 파일 목록 (`?field_key=bending_diagram`) | ✅ 완료 |
| DELETE | `/api/v1/items/{id}/files/{fileId}` | 파일 삭제 | ✅ 완료 |
| GET | `/api/v1/files/{id}/view` | 인라인 보기 (이미지 표시) | ✅ 완료 |
| GET | `/api/v1/files/{id}/download` | 다운로드 | ✅ 완료 |

**R2 저장 경로**: `{tenant_id}/items/{year}/{month}/{hex}.{ext}`
**예시**: `287/items/2026/03/1b4eba14ff5a832b.jpg`

### 6.3 API 응답 구조

#### 절곡품 상세 (`GET /api/v1/bending-items/{id}`)

```json
{
  "success": true,
  "message": "조회 성공",
  "data": {
    "id": 15862,
    "code": "BD-BE-30",
    "name": "하단마감재(스크린) EGI 3000mm",
    "item_type": "PT",
    "item_category": "BENDING",
    "unit": "EA",
    "is_active": true,
    "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": "메모",
    "exit_direction": null,
    "front_bottom_width": null,
    "box_width": null,
    "box_height": null,
    "bendingData": [
      { "no": 1, "input": 15, "rate": null, "sum": 15, "color": false, "aAngle": false },
      { "no": 2, "input": 14, "rate": "-1", "sum": 28, "color": false, "aAngle": false },
      { "no": 3, "input": 40, "rate": "-1", "sum": 67, "color": false, "aAngle": false }
    ],
    "prefix": "BE",
    "length_code": "30",
    "length_mm": 3000,
    "width_sum": 193,
    "bend_count": 6,
    "created_at": "2026-02-21 19:47:01",
    "updated_at": "2026-03-16 21:11:12"
  }
}
```

#### 필터 옵션 (`GET /api/v1/bending-items/filters`)

```json
{
  "success": true,
  "data": {
    "item_sep": ["스크린", "철재"],
    "item_bending": ["가이드레일", "케이스", "하단마감재", "마구리", "L-BAR"],
    "material": ["EGI 1.15T", "EGI 1.55T", "SUS 1.2T", "SUS 1.5T"],
    "model_UA": ["비인정", "인정"],
    "model_name": ["KSS01", "KSS02", "KSE01"]
  }
}
```

### 6.4 MNG 관리 화면 (완료)

| 화면 | URL | 설명 |
|------|-----|------|
| 목록 | `/bending/base` | 필터 + 페이지네이션 |
| 등록 | `/bending/base/create` | 폼 + 전개도 테이블 + 이미지 업로드 |
| 상세 | `/bending/base/{id}` | 읽기 전용 |
| 수정 | `/bending/base/{id}/edit` | 수정 모드 + 이미지 교체 |

### 6.5 레거시 분류 조건 분석

`item_bending` 컬럼으로 타입 구분 (같은 bending 테이블 사용):

| `item_bending` 값 | 전용 필드 | 레거시 디렉토리 |
|-------------------|----------|----------------|
| `가이드레일` | `rail_width` | `/bending/` |
| `케이스` | `exit_direction`, `box_width`, `box_height`, `front_bottom_width` | `/shutterbox/` |
| `하단마감재` | 없음 (공통 필드만) | `/bottombar/` |
| `마구리` | 없음 | - |
| `L-BAR` | 없음 | - |
| `보강평철` | 없음 | - |
| `연기차단재` | 없음 | - |

---

## 7. React 연동 시 참고사항

### 7.1 서버 액션 추가 필요

React에 `/api/v1/bending-items` 전용 서버 액션이 없음. 추가 필요:

```
예상 파일: src/components/bending/actions.ts

필요 함수:
- fetchBendingItems(params)     → GET /api/v1/bending-items
- fetchBendingFilters()         → GET /api/v1/bending-items/filters
- fetchBendingItem(id)          → GET /api/v1/bending-items/{id}
- createBendingItem(data)       → POST /api/v1/bending-items
- updateBendingItem(id, data)   → PUT /api/v1/bending-items/{id}
- deleteBendingItem(id)         → DELETE /api/v1/bending-items/{id}
- uploadBendingImage(itemId, file) → POST /api/v1/items/{id}/files
```

### 7.2 bendingData 필드 매핑 (API → React)

API 응답의 `bendingData`와 기존 React `BendingDetail` 타입 불일치:

| API 필드 | API 타입 | React 기존 타입 (`BendingDetail`) | 조치 |
|----------|---------|----------------------------------|------|
| `no` | integer | `no: number` | ✅ 일치 |
| `input` | numeric | `input: number` | ✅ 일치 |
| `rate` | string \| null | `elongation: number` | ⚠️ 이름+타입 다름 |
| `sum` | numeric | `sum: number` | ✅ 일치 |
| `color` | boolean | `shaded: boolean` | ⚠️ 이름 다름 |
| `aAngle` | boolean | `aAngle?: number` | ⚠️ 타입 다름 |
| - | - | `id: string` | React에만 존재 (클라이언트 전용) |
| - | - | `calculated: number` | React에만 존재 (클라이언트 계산값) |

**권장**: React 타입을 API에 맞추거나, 변환 레이어 추가
```typescript
// API → React 변환 예시
function transformBendingData(apiData: ApiBendingData[]): BendingDetail[] {
  return apiData.map((d, i) => ({
    id: `detail-${i}`,
    no: d.no,
    input: d.input,
    elongation: d.rate ? parseFloat(d.rate) : -1,  // rate → elongation
    calculated: d.input + (d.rate ? parseFloat(d.rate) : 0),
    sum: d.sum,
    shaded: d.color,           // color → shaded
    aAngle: d.aAngle ? 1 : 0, // boolean → number
  }));
}
```

### 7.3 인증 방식

API는 현재 **Bearer 토큰 없이 X-TENANT-ID 헤더**로 동작 (화이트리스트):
- `api/v1/bending-items`, `api/v1/bending-items/*`
- `api/v1/items/*/files`
- `api/v1/files/*/view`, `api/v1/files/*/download`

React는 **HttpOnly Cookie + Next.js 프록시**로 인증하므로, Bearer 토큰이 자동 전달됨.
React 연동 시 화이트리스트 의존 없이 정상 인증 경로로 동작할 것.

### 7.4 이미지 표시 경로

```
React에서 이미지 표시:
  프록시 경로: /api/proxy/files/{fileId}/view
  → Next.js API Route가 Bearer 토큰 붙여서
  → API: GET /api/v1/files/{fileId}/view
  → R2에서 stream
```

### 7.5 Update 시 code unique 검증

`BendingItemUpdateRequest`에 자기 자신 제외 unique 체크 누락:
```php
// 현재 (미흡)
'code' => 'sometimes|string|max:100',

// 수정 필요
'code' => 'sometimes|string|max:100|unique:items,code,' . $this->route('id'),
```

### 7.6 React API 호환성 검증 결과 (2026-03-17)

#### 호환 항목 (API 수정 불필요)

| 항목 | 상태 | 비고 |
|------|------|------|
| 응답 래핑 `{success, message, data}` | ✅ 호환 | ApiResponse 헬퍼 공용 |
| 페이지네이션 구조 | ✅ 호환 | `toPaginationMeta()` 재사용 가능 |
| 에러 응답 (422/404/500) | ✅ 호환 | 동일 에러 핸들링 구조 |
| Null 처리 | ✅ 호환 | 선택적 필드 패턴 일치 |
| 날짜 형식 | ✅ 호환 | `Y-m-d` 동일 |
| snake_case → camelCase | ✅ 호환 | 기존 `transformItemFromApi()` 재사용 |

#### React 측 작업 필요 항목

**1. bendingData 필드 매핑** (Server Action 레벨)

| API 응답 (`bendingData`) | React 타입 (`BendingDetail`) | 조치 |
|--------------------------|------------------------------|------|
| `rate` (string\|null) | `elongation` (number) | 이름+타입 변환 |
| `color` (boolean) | `shaded` (boolean) | 이름 변환 |
| `aAngle` (boolean) | `aAngle` (number) | 타입 변환 |
| `input`, `sum`, `no` | 동일 | ✅ 일치 |

변환 예시:
```typescript
function transformBendingData(apiData: ApiBendingData[]): BendingDetail[] {
  return apiData.map((d, i) => ({
    id: `detail-${i}`,
    no: d.no,
    input: d.input,
    elongation: d.rate ? parseFloat(d.rate) : -1,
    calculated: d.input + (d.rate ? parseFloat(d.rate) : 0),
    sum: d.sum,
    shaded: d.color,
    aAngle: d.aAngle ? 1 : 0,
  }));
}
```

**2. GuiderailModel 타입 + API 클라이언트 신규 작성**
- React에 `GuiderailModel` 관련 타입 없음
- `components[]`, `material_summary` 필드 구조 정의 필요
- Server Action: `fetchGuiderailModels()`, `fetchGuiderailModel(id)` 등

**3. 파일 URL 프록시 처리**
- API: `/api/v1/files/{id}/view`
- React: `/api/proxy/files/{id}/view` (Next.js 프록시 경로)

#### 인증 방식 차이 (자동 호환)

| 호출자 | X-API-KEY | Bearer | tenant_id 소스 | 비고 |
|--------|-----------|--------|---------------|------|
| **MNG** | ✅ | ❌ | X-TENANT-ID 헤더 (`ensureContext`) | 현재 |
| **React** | ✅ (프록시) | ✅ (쿠키) | Bearer → User → userTenants | 향후 |

React는 Next.js 프록시가 HttpOnly 쿠키에서 Bearer 토큰을 읽어 자동 첨부하므로,
API의 `allowWithoutAuth` 화이트리스트에 의존하지 않고 정상 인증 경로로 동작함.
`ensureContext()`는 Bearer 없을 때만 동작하는 fallback이라 충돌 없음.

### 7.7 MNG 부품 추가 시 리다이렉트 수정 (2026-03-17)

**문제**: `/bending/cases/{id}/edit`에서 부품 추가 시 `form.submit()` → 컨트롤러 `update()`가 show 페이지로 redirect, edit 모드 종료됨

**수정 내용**:
- `form.blade.php`: `submitAndStayEdit()` 함수 추가 — hidden input `_redirect=edit` 세팅 후 submit
- `BendingProductController::update()`: `_redirect=edit`이면 edit 페이지로 리다이렉트
- `movePart()` 순서 변경도 동일 패턴 적용 (`location.reload()` → `submitAndStayEdit()`)

---

### 관련 문서

- 통합 마스터 플랜: `docs/dev/dev_plans/integrated-master-plan.md`
- Phase 2 (절곡 분석/설계): `docs/dev/dev_plans/integrated-phase-2.md`
- Phase 3 (절곡 검사, 완료): `docs/dev/dev_plans/integrated-phase-3.md`
- 품목 정책: `docs/rules/item-policy.md`

---

**최종 업데이트**: 2026-03-17