- Client API CRUD 플로우 테스트 JSON 추가 (11단계) - Client Group API CRUD 플로우 테스트 JSON 추가 (10단계) - client_groups 테이블 스키마 정정 (group_code, group_name, price_rate 필드) - API 응답 구조 및 필드 타입 주의사항 문서화 - Flow Tester 섹션 추가 (사용법 및 주의사항)
16 KiB
16 KiB
품목 등록/수정 백엔드 API 수정 요청
프론트엔드 품목관리 기능 개발 중 발견된 백엔드 수정 필요 사항 정리
🔴 [핵심] field_key 통일 필요 - 근본적 구조 개선
상태: 🔴 구조 개선 필요
발견일: 2025-12-06
요약
| 항목 | 내용 |
|---|---|
| 근본 원인 | 품목기준관리 / 품목관리 API 요청서 분리로 키값 불일치 |
| 해결 방향 | 품목기준관리 field_key = Single Source of Truth |
| 백엔드 요청 | 동적 필드 → attributes JSON 저장, field_key 통일 |
| 프론트 작업 | 백엔드 완료 후 하드코딩 매핑 제거 |
현재 문제
품목기준관리 field_key 프론트엔드 변환 백엔드 API 응답
───────────────────── ───────────────── ─────────────────
Part_type → part_type → part_type (불일치!)
Installation_type_1 → installation_type → 저장 안됨
side_dimensions_horizontal → side_spec_width → 저장 안됨
두 개의 요청서가 따로 놀면서 백엔드에서 각각 다르게 구현 → 키 불일치 발생
이상적인 구조
품목기준관리 (field_key 정의)
│
▼
┌────────────┐
│ field_key │ ← Single Source of Truth
└────────────┘
│
┌────┴────┬────────┬────────┐
▼ ▼ ▼ ▼
등록 수정 상세 리스트
(전부 동일한 field_key로 저장/조회/렌더링)
기대 효과
- 단위 필드 혼란 해결: field_key가 "unit"이면 그게 단위 (명확!)
- 필드 타입 자동 인식: 품목기준관리 field_type 보고 자동 렌더링
- 누락 데이터 분석 용이: field_key 하나만 확인하면 끝
- 디버깅 속도 향상: API 응답 = 폼 데이터 (변환 없음)
수정 요청
- 품목기준관리 field_key를 기준으로 API 요청/응답 키 통일
- 동적 필드는
attributesJSON에 field_key 그대로 저장 - 조회 시에도 field_key 그대로 응답
영향 범위
- 품목관리 전체 (등록/수정/상세/리스트)
- 모든 품목 유형 (FG, PT, SM, RM, CS)
우선순위
🔴 최우선 - 이 구조 개선 후 아래 개별 이슈들 대부분 자동 해결
1. 소모품(CS) 등록 시 규격(specification) 저장 안됨
상태: 🔴 수정 필요
발견일: 2025-12-06
파일 위치
/app/Http/Requests/Item/ItemStoreRequest.php - rules() 메서드 (Line 14-42)
현재 문제
specification필드가 validation rules에 없음- Laravel FormRequest는 rules에 정의되지 않은 필드를
$request->validated()결과에서 제외 - 프론트엔드에서
specification: "테스트"값을 보내도 백엔드에서 무시됨 - DB에 규격 값이 null로 저장됨
프론트엔드 확인 사항
DynamicItemForm에서97_specification→spec→specification으로 정상 변환됨- API 요청 payload에
specification필드 포함 확인됨 - 백엔드
ItemsService.createMaterial()에서$data['specification']참조하나 값이 없음
수정 요청
// /app/Http/Requests/Item/ItemStoreRequest.php
public function rules(): array
{
return [
// 필수 필드
'code' => 'required|string|max:50',
'name' => 'required|string|max:255',
'product_type' => 'required|string|in:FG,PT,SM,RM,CS',
'unit' => 'required|string|max:20',
// 선택 필드
'category_id' => 'nullable|integer|exists:categories,id',
'description' => 'nullable|string',
'specification' => 'nullable|string|max:255', // ✅ 추가 필요
// ... 나머지 기존 필드들 ...
];
}
영향 범위
- 소모품(CS) 등록
- 원자재(RM) 등록 (해당 시)
- 부자재(SM) 등록 (해당 시)
2. Material(SM, RM, CS) 수정 시 material_code 중복 에러
상태: 🔴 수정 필요
발견일: 2025-12-06
현재 문제
- Material 수정 시
material_code중복 체크 에러 발생 - 케이스 1: 값을 변경하지 않아도 자기 자신과 중복 체크됨
- 케이스 2: 소프트 삭제된 품목의 코드와도 중복 체크됨
- 에러 메시지:
Duplicate entry '알루미늄-옵션2-2' for key 'materials.materials_material_code_unique'
원인
- UPDATE 시 자기 자신의 ID를 제외하지 않음
- 소프트 삭제(
deleted_at)된 레코드도 unique 체크에 포함됨
수정 요청
// Material 수정 Request 파일 (MaterialUpdateRequest.php 또는 해당 파일)
use Illuminate\Validation\Rule;
public function rules(): array
{
return [
// ... 기존 필드들 ...
// ✅ 수정 시 자기 자신 제외 + 소프트삭제 제외하고 unique 체크
'material_code' => [
'required',
'string',
'max:255',
Rule::unique('materials', 'material_code')
->whereNull('deleted_at') // 소프트삭제된 건 제외
->ignore($this->route('id')), // 자기 자신 제외
],
// ... 나머지 필드들 ...
];
}
영향 범위
- 원자재(RM) 수정
- 부자재(SM) 수정
- 소모품(CS) 수정
비고
- 현재 수정 자체가 불가능하여 수정 후 데이터 검증이 어려움
- 이 이슈 해결 후 수정 기능 재검증 필요
3. Material(SM, RM) 수정 시 규격(specification) 로딩 안됨
상태: 🔴 확인 필요
발견일: 2025-12-06
현재 문제
- SM(부자재), RM(원자재) 수정 페이지 진입 시 규격 값이 표시 안됨
- 1회 수정 후 다시 수정 페이지 진입 시 규격 값 없음
- CS(소모품)과 동일한 문제로 추정
원인 추정
- 백엔드에서
options배열이 제대로 저장되지 않거나 반환되지 않음 - SM/RM은
standard_*필드 조합으로specification을 생성하고, 이 값을options배열에도 저장 - 수정 페이지에서는
options배열을 읽어서 폼에 표시 options가 null이면 규격 필드들이 빈 값으로 표시됨
확인 요청
// Material 조회 API 응답에서 options 필드 확인 필요
// GET /api/v1/items/{id}?item_type=MATERIAL
// 응답 예시 (정상)
{
"id": 396,
"name": "썬더볼트",
"specification": "부자재2-2",
"options": [
{"label": "standard_1", "value": "부자재2"},
{"label": "standard_2", "value": "2"}
] // ✅ options 배열이 있어야 함
}
// 응답 예시 (문제)
{
"id": 396,
"name": "썬더볼트",
"specification": "부자재2-2",
"options": null // ❌ options가 null이면 규격 로딩 불가
}
수정 요청
- Material 저장 시
options배열 정상 저장 확인 - Material 조회 시
options필드 반환 확인 options가 JSON 컬럼이라면 정상적인 JSON 형식으로 저장되는지 확인
영향 범위
- 원자재(RM) 수정
- 부자재(SM) 수정
4. Material(SM, RM, CS) 비고(remarks) 저장 안됨
상태: 🔴 수정 필요
발견일: 2025-12-06
현재 문제
- 소모품(CS), 원자재(RM), 부자재(SM) 등록/수정 시 비고(remarks)가 저장되지 않음
- 프론트에서
note→remarks로 정상 변환하여 전송 - 백엔드 Service에서
$data['remarks']참조하지만 값이 없음
원인 분석
- 프론트엔드:
note→remarks변환 ✅ (materialTransform.ts:115) - 백엔드 Service:
'remarks' => $data['remarks'] ?? null✅ (ItemsService.php:301) - 백엔드 Model:
remarks컬럼 존재 ✅ (Material.php:31) - 백엔드 Request:
remarksvalidation rule 없음 ❌ 누락
수정 요청
// /app/Http/Requests/Item/ItemStoreRequest.php
// /app/Http/Requests/Item/ItemUpdateRequest.php
public function rules(): array
{
return [
// 기존 필드들...
'description' => 'nullable|string',
// ✅ 추가 필요
'remarks' => 'nullable|string', // 비고 필드
// ... 나머지 필드들 ...
];
}
영향 범위
- 소모품(CS) 등록/수정
- 원자재(RM) 등록/수정
- 부자재(SM) 등록/수정
비고
- 1번 이슈(specification)와 동일한 원인: Request validation 누락
- 함께 처리하면 효율적
5. 품목기준관리 옵션 필드 식별자 필요 (장기 개선)
상태: 🟡 개선 권장
발견일: 2025-12-06
현재 문제
- 품목기준관리에서 옵션 필드의
field_key를 자유롭게 입력 가능 - 프론트엔드는
standard_*,option_*패턴으로 옵션 필드를 식별 - 패턴에 맞지 않는 field_key (예:
st_2)는 규격(specification) 조합에서 누락됨 - 결과: 부자재(SM)의 규격값이 저장되지 않음
임시 해결 (프론트엔드)
- 품목기준관리에서 field_key를
standard_*패턴으로 통일 - 예:
st_2→standard_2
근본 해결 요청 (백엔드)
// 품목기준관리 API 응답에 옵션 필드 여부 표시
// 현재 응답
{
"field_key": "st_2",
"field_type": "select",
"field_name": "규격옵션"
}
// 개선 요청
{
"field_key": "st_2",
"field_type": "select",
"field_name": "규격옵션",
"is_spec_option": true // ✅ 규격 조합용 옵션 필드인지 표시
}
기대 효과
- 프론트엔드가 field_key 패턴에 의존하지 않음
is_spec_option: true인 필드만 규격 조합에 사용- 새로운 field_key 패턴이 추가되어도 프론트 수정 불필요
영향 범위
- 원자재(RM) 등록/수정
- 부자재(SM) 등록/수정
- 향후 추가되는 Material 유형
6. 조립부품(PT-ASSEMBLY) 필드 저장 안됨 - fillable 누락
상태: 🔴 수정 필요
발견일: 2025-12-06
현재 문제
- 조립부품 등록 후 상세보기/수정 페이지에서 데이터가 제대로 표시되지 않음
- 프론트엔드는 데이터를 정상 전송하고 있음 ✅
- 백엔드에서 조립부품 필드들이 저장되지 않음 ❌
원인 분석
프론트엔드 전송 데이터 (정상)
// DynamicItemForm/index.tsx - handleFormSubmit()
const submitData = {
...convertedData, // 폼 데이터 (installation_type, assembly_type 등 포함)
product_type: 'PT',
part_type: 'ASSEMBLY', // ✅ 명시적 추가
bending_diagram: base64, // ✅ 캔버스 이미지
bom: [...], // ✅ BOM 데이터
};
백엔드 저장 로직 (문제)
// ItemsService.php - createProduct()
private function createProduct(array $data, int $tenantId, int $userId): Product
{
$payload = $data;
// ... 기본 필드만 설정
return Product::create($payload); // Mass Assignment
}
Product 모델 fillable (누락 필드 있음)
// Product.php
protected $fillable = [
'tenant_id', 'code', 'name', 'unit', 'category_id',
'product_type',
'attributes', 'description', // ✅ attributes JSON 있음
'part_type', // ✅ 있음
'bending_diagram', 'bending_details', // ✅ 있음
// ❌ installation_type 없음
// ❌ assembly_type 없음
// ❌ side_spec_width 없음
// ❌ side_spec_height 없음
// ❌ length 없음
];
필드별 저장 상태
| 필드 | 프론트 전송 | fillable | 저장 여부 |
|---|---|---|---|
part_type |
✅ | ✅ 컬럼 | ✅ 저장됨 |
bending_diagram |
✅ | ✅ 컬럼 | ⚠️ 파일 업로드 별도 처리 |
installation_type |
✅ | ❌ 없음 | ❌ 저장 안됨 |
assembly_type |
✅ | ❌ 없음 | ❌ 저장 안됨 |
side_spec_width |
✅ | ❌ 없음 | ❌ 저장 안됨 |
side_spec_height |
✅ | ❌ 없음 | ❌ 저장 안됨 |
length |
✅ | ❌ 없음 | ❌ 저장 안됨 |
bom |
✅ | ⚠️ 별도 처리 | ❓ 확인 필요 |
수정 요청
방법 1: attributes JSON에 저장 (권장)
// ItemsService.php - createProduct() 수정
private function createProduct(array $data, int $tenantId, int $userId): Product
{
// 조립부품 전용 필드들을 attributes JSON으로 묶기
$assemblyFields = ['installation_type', 'assembly_type', 'side_spec_width', 'side_spec_height', 'length'];
$attributes = $data['attributes'] ?? [];
foreach ($assemblyFields as $field) {
if (isset($data[$field])) {
$attributes[$field] = $data[$field];
unset($data[$field]); // payload에서 제거
}
}
$payload = $data;
$payload['tenant_id'] = $tenantId;
$payload['created_by'] = $userId;
$payload['attributes'] = !empty($attributes) ? $attributes : null;
// ... 나머지 동일
return Product::create($payload);
}
방법 2: 컬럼 추가 + fillable 등록
// Product.php - fillable에 추가
protected $fillable = [
// 기존 필드들...
'installation_type', // ✅ 추가
'assembly_type', // ✅ 추가
'side_spec_width', // ✅ 추가
'side_spec_height', // ✅ 추가
'length', // ✅ 추가 (또는 assembly_length)
];
// + migration으로 컬럼 추가 필요
프론트엔드 대응 (완료)
- 프론트에서
data.xxx또는data.attributes.xxx둘 다에서 값을 찾도록 수정 완료 - 상세보기:
items/[id]/page.tsx-mapApiResponseToItemMaster함수 - 수정페이지:
items/[id]/edit/page.tsx-mapApiResponseToFormData함수
영향 범위
- 조립부품(PT-ASSEMBLY) 등록/조회/수정
- 설치유형, 마감, 측면규격, 길이 모든 필드
우선순위
🔴 높음 - 조립부품 등록 기능이 완전히 동작하지 않음
7. 파일 업로드 API 500 에러 - ApiResponse 클래스 네임스페이스 불일치
상태: 🔴 수정 필요
발견일: 2025-12-06
현재 문제
- 품목 파일 업로드 (전개도, 시방서, 인정서) 시 500 에러 발생
- 절곡부품, 조립부품 모두 동일한 에러
- 파일이 업로드되지 않음
에러 로그
[2025-12-06 17:28:22] DEV.ERROR: Class "App\Http\Responses\ApiResponse" not found
{"exception":"[object] (Error(code: 0): Class \"App\\Http\\Responses\\ApiResponse\" not found
at /home/webservice/api/app/Http/Controllers/Api/V1/ItemsFileController.php:31)
원인 분석
네임스페이스 불일치
| 위치 | 네임스페이스 |
|---|---|
ItemsFileController.php (Line 7) |
use App\Http\Responses\ApiResponse ❌ |
| 실제 파일 위치 | App\Helpers\ApiResponse ✅ |
파일 위치
/app/Http/Controllers/Api/V1/ItemsFileController.php (Line 7)
현재 코드
<?php
namespace App\Http\Controllers\Api\V1;
use App\Http\Controllers\Controller;
use App\Http\Requests\ItemsFileUploadRequest;
use App\Http\Responses\ApiResponse; // ❌ 잘못된 경로
// ...
수정 요청
<?php
namespace App\Http\Controllers\Api\V1;
use App\Http\Controllers\Controller;
use App\Http\Requests\ItemsFileUploadRequest;
use App\Helpers\ApiResponse; // ✅ 올바른 경로
// ...
영향 범위
- 절곡부품(PT-BENDING) 전개도 업로드
- 조립부품(PT-ASSEMBLY) 전개도 업로드
- 제품(FG) 시방서 업로드
- 제품(FG) 인정서 업로드
우선순위
🔴 긴급 - 모든 파일 업로드 기능이 동작하지 않음 (한 줄 수정으로 해결 가능)
수정 완료 내역
수정 완료된 항목은 아래로 이동
(아직 없음)
참고 사항
관련 파일 (프론트엔드)
src/app/[locale]/(protected)/items/create/page.tsx- 품목 등록 페이지src/app/[locale]/(protected)/items/[id]/edit/page.tsx- 품목 수정 페이지src/components/items/DynamicItemForm/index.tsx- 동적 폼 컴포넌트
관련 파일 (백엔드)
/app/Http/Controllers/Api/V1/ItemsController.php- 품목 API 컨트롤러/app/Services/ItemsService.php- 품목 서비스 레이어/app/Http/Requests/Item/ItemStoreRequest.php- 등록 요청 검증/app/Http/Requests/Item/ItemUpdateRequest.php- 수정 요청 검증/app/Models/Materials/Material.php- Material 모델