Files
sam-docs/dev/dev_plans/bending-parts-analysis.md
강영보 220ab78041 docs: [bending] 절곡품 전용 테이블 분리 완료 문서
- README: bending_items 266건 + bending_models 62건 DB 검증 완료
- README: 하장바 검색 문제 해결 (10건 정상)
- README: bending_data JSON 통합, bending_item_mappings DROP
- README: LOT 코드 체계, 테이블 관계도, 레거시 대응표 갱신
- step1: 데이터분석 업데이트
- step5: canvas 그리기 추가
- .gitattributes CRLF→LF 정규화
2026-03-19 20:04:17 +09:00

22 KiB
Raw Blame History

절곡품(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 규격 12070, 120120
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 테이블 저장 구조

{
  "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})

{
  "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)

{
  "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에 맞추거나, 변환 레이어 추가

// 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 체크 누락:

// 현재 (미흡)
'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 동일 일치

변환 예시:

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