Files
sam-docs/projects/mes/00_baseline/BACKEND_DEVELOPMENT_ROADMAP_V2.md
hskwon 08a8259313 docs: 5130 레거시 분석 문서 및 기존 문서 초기 커밋
- 5130 레거시 시스템 분석 (00_OVERVIEW ~ 08_SAM_COMPARISON)
- MES 프로젝트 문서
- API/프론트엔드 스펙 문서
- 가이드 및 레퍼런스 문서
2025-12-04 18:47:19 +09:00

22 KiB

source, section, created, updated, bp_mes_phase, related, tags
source section created updated bp_mes_phase related tags
Phase 0 + 프론트엔드 가이드 통합 분석 BP-MES 백엔드 개발 로드맵 (개정판) 2025-11-13 2025-11-13 Phase 0 완료, Phase 1 준비
ITEM_MANAGEMENT_MIGRATION_GUIDE.md (프론트엔드 가이드)
PHASE_0_FINAL_REPORT.md
api_gap_validation_report.md
backend
roadmap
api
database
laravel

BP-MES 백엔드 개발 로드맵 V2

작성일: 2025-11-13 기반: Next.js 15 프론트엔드 가이드 + Phase 0 분석 대상: 백엔드 개발자 (프론트엔드 작업 제외) 프레임워크: Laravel 12 + PHP 8.2+


📋 Executive Summary

변경 사항

  • 프론트엔드 가이드 통합: Next.js 15 마이그레이션 가이드 기반 API 요구사항 반영
  • 우선순위 재조정: 프론트엔드 80% 물리적 페이지 → 백엔드 고정 필드 우선
  • 선택적 확장: 동적 템플릿 시스템 2차 목표로 후순위
  • ⚠️ 프론트엔드 작업 제외: 백엔드 API 개발에만 집중

핵심 방향

  1. 하이브리드 전략: 고정 필드 API (우선) + 메타데이터 API (선택)
  2. RESTful 설계: 프론트 가이드 엔드포인트 스펙 준수
  3. BOM 계산 강화: quantityFormula, 계층 구조, tree API
  4. 파일 관리: 절곡도, 시방서, 인정서 업로드

🎯 프론트엔드 요구사항 분석

1. 데이터 구조 (프론트엔드 가이드 기준)

1.1 ItemMaster 필드 구성

공통 필드 (ALL):

필수:
- id, itemCode, itemName, itemType, unit
- specification, isActive

분류:
- category1, category2, category3

가격:
- purchasePrice, salesPrice, marginRate
- processingCost, laborCost, installCost

BOM:
- bom[] (BOMLine 배열)
- bomCategories[]

메타:
- safetyStock, leadTime, isVariableSize
- revisions[]
- createdAt, updatedAt

FG (제품) 전용:

- productCategory: 'SCREEN' | 'STEEL'
- lotAbbreviation: string (예: "KD")
- note: string

PT (부품) 전용:

기본:
- partType: 'ASSEMBLY' | 'BENDING' | 'PURCHASED'
- partUsage: 'GUIDE_RAIL' | 'BOTTOM_FINISH' | 'CASE' | 'DOOR' | 'BRACKET' | 'GENERAL'

조립 부품:
- installationType, assemblyType
- sideSpecWidth, sideSpecHeight, assemblyLength
- guideRailModelType, guideRailModel (가이드레일)

절곡품:
- bendingDiagram: string (이미지 URL)
- bendingDetails: BendingDetail[] (전개도 데이터)
- material, length, bendingLength

인증 정보 (FG/PT):

- certificationNumber
- certificationStartDate, certificationEndDate
- specificationFile, specificationFileName
- certificationFile, certificationFileName

1.2 BOMLine 구조

필수:
- id, childItemCode, childItemName
- quantity, unit, unitPrice
- note

핵심:
- quantityFormula: string  // 🔴 "W * 2", "H + 100", "G/1000*1.02"

절곡품:
- isBending: boolean
- bendingDiagram: string
- bendingDetails: BendingDetail[]

백엔드 요구:

  • quantityFormula 저장 (text 컬럼)
  • 계산 로직 (수식 파싱 및 실행)

1.3 BendingDetail 구조

{
  id: string
  no: number           // 순서
  input: number        // 입력값
  elongation: number   // 연신율 (-1 기본)
  calculated: number   // 계산 후 값
  sum: number          // 합계
  shaded: boolean      // 음영 여부
  aAngle?: number      // A각
}

백엔드 요구:

  • JSON 컬럼으로 저장
  • ⚠️ 계산 로직: 프론트 vs 백엔드 협의 필요

2. API 엔드포인트 (프론트엔드 가이드)

2.1 품목 CRUD (필수)

GET    /api/items
  Query Parameters:
  - itemType?: string (FG,PT,SM,RM,CS)
  - search?: string
  - category1?: string

  Response:
  {
    data: ItemMaster[]
    meta: { total, page, perPage }
  }

GET    /api/items/:itemCode
  Response:
  {
    data: ItemMaster (BOM 포함)
  }

POST   /api/items
  Body: Partial<ItemMaster>
  Response: { data: ItemMaster }

PUT    /api/items/:itemCode
  Body: Partial<ItemMaster>
  Response: { data: ItemMaster }

DELETE /api/items/:itemCode
  Response: { message: "Deleted" }

2.2 BOM 관리 (필수)

GET    /api/items/:itemCode/bom
  Response:
  {
    data: BOMLine[] (flat list)
  }

GET    /api/items/:itemCode/bom/tree
  🔴 Phase 0에서 부재 확인
  Response:
  {
    data: {
      item: ItemMaster
      children: [
        {
          bomLine: BOMLine
          item: ItemMaster
          children: [...] // 재귀
        }
      ]
    }
  }

POST   /api/items/:itemCode/bom
  Body: Partial<BOMLine>
  Response: { data: BOMLine }

PUT    /api/items/:itemCode/bom/:lineId
  Body: Partial<BOMLine>
  Response: { data: BOMLine }

DELETE /api/items/:itemCode/bom/:lineId
  Response: { message: "Deleted" }

2.3 파일 관리 (필수)

POST   /api/items/:itemCode/files
  Body: FormData
  - type: 'specification' | 'certification' | 'bending_diagram'
  - file: File

  Response:
  {
    data: {
      url: string
      fileName: string
      type: string
    }
  }

DELETE /api/items/:itemCode/files/:type
  Response: { message: "Deleted" }

2.4 동적 템플릿 (선택적, 2차 목표)

GET    /api/item-templates
GET    /api/item-templates/:pageId
POST   /api/item-templates
PUT    /api/item-templates/:pageId
DELETE /api/item-templates/:pageId

우선순위: 🟡 중간 (Phase 2-3 이후)


🔍 Phase 0 Gap 통합 분석

기존 구현 현황 (Phase 0 검증 결과)

Gap 설명 상태 영향
#1 통합 품목 조회 API 해결 ItemsController 존재
#2 치수 연결 매핑 미해결 dimension_mappings 테이블 필요
#3 실시간 견적 계산 ⚠️ 부분 BomCalculationController (설계만)
#4 BOM formula 필드 미해결 product_components.formula 추가
#5 조건부 BOM 미해결 product_components.condition 추가
#6 options/dimensions ⚠️ 부분 Material만, Product 없음
#7 가격 통합 조회 해결 PricingService 완성

프론트엔드 가이드 vs Phase 0 Gap

프론트 요구사항 Phase 0 Gap 우선순위
ItemMaster 필드 #6 (options) 🔴 P0
BOMLine.quantityFormula #4 (formula) 🔴 P0
BOM tree API 신규 발견 🔴 P0
파일 업로드 기존 존재 🟢 완료
치수 연결 #2 🟡 P1
견적 계산 API #3 🟡 P1
조건부 BOM #5 🟡 P1

🗂️ 데이터베이스 설계

1. products 테이블 확장

현재 구조:

-- 기존 필드 (Phase 0 분석)
id, tenant_id, item_code, item_name, item_type
specification, unit, category1, category2, category3
purchase_price, sales_price
attributes (JSON)
created_by, updated_by, deleted_at, timestamps

추가 필요 필드 (프론트 가이드 기반):

-- 공통
is_active BOOLEAN DEFAULT true
margin_rate DECIMAL(5,2)
processing_cost DECIMAL(10,2)
labor_cost DECIMAL(10,2)
install_cost DECIMAL(10,2)
safety_stock INT
lead_time INT
is_variable_size BOOLEAN DEFAULT false

-- FG 전용
product_category VARCHAR(20)  -- SCREEN, STEEL
lot_abbreviation VARCHAR(10)
note TEXT

-- PT 전용
part_type VARCHAR(20)         -- ASSEMBLY, BENDING, PURCHASED
part_usage VARCHAR(30)        -- GUIDE_RAIL, BOTTOM_FINISH, ...
installation_type VARCHAR(20)
assembly_type VARCHAR(20)
side_spec_width VARCHAR(20)
side_spec_height VARCHAR(20)
assembly_length VARCHAR(20)
guide_rail_model_type VARCHAR(50)
guide_rail_model VARCHAR(50)

-- 절곡품
bending_diagram VARCHAR(255)  -- 이미지 URL
bending_details JSON          -- BendingDetail[]
material VARCHAR(50)
length VARCHAR(20)
bending_length VARCHAR(20)

-- 인증
certification_number VARCHAR(50)
certification_start_date DATE
certification_end_date DATE
specification_file VARCHAR(255)
specification_file_name VARCHAR(255)
certification_file VARCHAR(255)
certification_file_name VARCHAR(255)

-- 동적 확장 (기존 유지)
options JSON                  -- 🔴 신규 추가 필요

Migration 작업량: 2-3일


2. product_components 테이블 확장

현재 구조:

id, tenant_id
parent_product_id, child_product_id (또는 material_id)
quantity, unit, unit_price
note
created_by, updated_by, deleted_at, timestamps

추가 필요 필드:

-- 핵심 필드
quantity_formula TEXT         -- 🔴 "W * 2", "H + 100", "G/1000*1.02"
condition TEXT                -- 🔴 "MOTOR='Y'", "WIDTH > 3000"

-- 절곡품
is_bending BOOLEAN DEFAULT false
bending_diagram VARCHAR(255)
bending_details JSON

Migration 작업량: 1일


3. 신규 테이블: dimension_mappings

CREATE TABLE dimension_mappings (
  id BIGSERIAL PRIMARY KEY,
  tenant_id BIGINT NOT NULL,

  parent_item_code VARCHAR(50) NOT NULL,   -- 부모 품목 (예: 세트)
  child_item_code VARCHAR(50) NOT NULL,    -- 자식 품목 (예: 가이드레일)

  source_dimension VARCHAR(20) NOT NULL,   -- 부모 치수명 (예: "W", "H")
  target_dimension VARCHAR(20) NOT NULL,   -- 자식 치수명 (예: "G", "length")

  mapping_formula VARCHAR(100),            -- 변환 공식 (예: "W - 100")

  is_active BOOLEAN DEFAULT true,

  created_by BIGINT,
  updated_by BIGINT,
  deleted_at TIMESTAMP,
  created_at TIMESTAMP DEFAULT NOW(),
  updated_at TIMESTAMP DEFAULT NOW(),

  FOREIGN KEY (tenant_id) REFERENCES tenants(id),
  INDEX idx_parent_item (tenant_id, parent_item_code),
  INDEX idx_child_item (tenant_id, child_item_code)
);

Migration 작업량: 1일


4. 신규 테이블: item_templates (선택적)

CREATE TABLE item_templates (
  id BIGSERIAL PRIMARY KEY,
  tenant_id BIGINT NOT NULL,

  page_name VARCHAR(100) NOT NULL,
  item_type VARCHAR(10) NOT NULL,       -- FG, PT, SM, RM, CS

  sections JSON NOT NULL,               -- ItemSection[]

  is_active BOOLEAN DEFAULT true,
  absolute_path VARCHAR(255),

  created_by BIGINT,
  updated_by BIGINT,
  deleted_at TIMESTAMP,
  created_at TIMESTAMP DEFAULT NOW(),
  updated_at TIMESTAMP DEFAULT NOW(),

  FOREIGN KEY (tenant_id) REFERENCES tenants(id),
  INDEX idx_item_type (tenant_id, item_type, is_active)
);

우선순위: 🟡 Phase 2-3 (선택적)


🔧 백엔드 개발 작업 분류

Priority 0 (즉시 시작, 1-2주)

Task 1.1: products 테이블 Migration

  • Migration 파일 작성
  • 프론트 가이드 필드 추가 (공통 + FG + PT + 인증)
  • options JSON 컬럼 추가
  • Rollback 스크립트 준비

예상 시간: 1일


Task 1.2: product_components 테이블 Migration

  • quantity_formula TEXT 컬럼 추가
  • condition TEXT 컬럼 추가
  • is_bending, bending_diagram, bending_details 추가
  • Rollback 스크립트

예상 시간: 0.5일


Task 1.3: ItemsController 확장

  • GET /api/items - 필터링 (itemType, search, category1)
  • GET /api/items/:itemCode - BOM 포함 응답
  • POST /api/items - 유형별 검증 (FG/PT/SM/RM/CS)
  • PUT /api/items/:itemCode
  • DELETE /api/items/:itemCode (Soft Delete)

예상 시간: 3일


Task 1.4: BOMController 신규 개발

  • GET /api/items/:itemCode/bom - flat list
  • GET /api/items/:itemCode/bom/tree - 계층 구조 (재귀)
  • POST /api/items/:itemCode/bom
  • PUT /api/items/:itemCode/bom/:lineId
  • DELETE /api/items/:itemCode/bom/:lineId

예상 시간: 3-4일


Task 1.5: FileController 확장

  • POST /api/items/:itemCode/files
    • type: specification, certification, bending_diagram
    • 파일 검증 (확장자, 크기)
    • Storage 저장 (local or S3)
    • URL 반환
  • DELETE /api/items/:itemCode/files/:type

예상 시간: 2일


Priority 1 (2주차, 1-2주)

Task 2.1: dimension_mappings 테이블

  • Migration 작성
  • DimensionMapping 모델 생성
  • API: GET/POST/PUT/DELETE /api/dimension-mappings

예상 시간: 2일


Task 2.2: BOM 계산 로직 강화

  • quantityFormula 파싱 (수식 계산)
  • 변수 지원 (W, H, G, length 등)
  • 에러 핸들링 (잘못된 수식)
  • POST /api/quotes/calculate
    • 입력: itemCode + dimensions (W, H)
    • 출력: BOM 전개 + 수량 계산 + 원가

예상 시간: 3-4일


Task 2.3: 조건부 BOM

  • condition 필드 파싱 (조건 평가)
  • 조건부 BOM 필터링 로직
  • API 통합

예상 시간: 2-3일


Priority 2 (3-4주차, 선택적)

Task 3.1: 동적 템플릿 시스템

  • item_templates 테이블
  • ItemTemplateController
  • GET/POST/PUT/DELETE /api/item-templates

예상 시간: 3-4일


Task 3.2: 성능 최적화

  • BOM tree 조회 성능 (N+1 문제 해결)
  • Eager Loading
  • Cache 전략 (Redis)
  • 인덱싱 최적화

예상 시간: 2-3일


📅 백엔드 개발 타임라인

Week 1-2: 핵심 API 개발 (P0)

Day 1-2:
✅ products 테이블 Migration
✅ product_components Migration
✅ Migration 실행 및 검증

Day 3-5:
✅ ItemsController 확장
  - GET /api/items (필터링)
  - GET /api/items/:itemCode (BOM 포함)
  - POST/PUT/DELETE

Day 6-9:
✅ BOMController 신규
  - CRUD API
  - Tree 구조 조회 (재귀)

Day 10-12:
✅ FileController 확장
  - 파일 업로드 (3가지 타입)
  - 파일 삭제
  - Storage 연동

Day 13-14:
✅ 통합 테스트
✅ Postman Collection 작성

Week 3-4: BOM 계산 및 매핑 (P1)

Day 15-16:
✅ dimension_mappings 테이블
✅ DimensionMappingController

Day 17-20:
✅ BOM 계산 로직
  - quantityFormula 파싱
  - 변수 지원 (W, H, G)
  - POST /api/quotes/calculate

Day 21-23:
✅ 조건부 BOM
  - condition 필드 파싱
  - 조건 평가 로직

Day 24-28:
✅ 통합 테스트
✅ 성능 테스트
✅ 버그 수정

Week 5-6: 선택적 확장 (P2)

Day 29-32:
🎯 동적 템플릿 시스템 (선택)
  - item_templates 테이블
  - Template API

Day 33-35:
🎯 성능 최적화
  - Eager Loading
  - Cache
  - 인덱싱

Day 36-42:
🎯 문서화
🎯 배포 준비

🧪 테스트 전략

Unit Test (PHPUnit)

// tests/Unit/Services/BomCalculationServiceTest.php
class BomCalculationServiceTest extends TestCase
{
    /** @test */
    public function it_calculates_formula_with_variables()
    {
        $service = new BomCalculationService();

        $formula = "W * 2 + H";
        $variables = ['W' => 3500, 'H' => 2000];

        $result = $service->evaluateFormula($formula, $variables);

        $this->assertEquals(9000, $result); // 3500*2 + 2000
    }

    /** @test */
    public function it_builds_bom_tree_recursively()
    {
        $item = Product::factory()->create();
        // ...

        $tree = $service->buildBomTree($item->item_code);

        $this->assertArrayHasKey('children', $tree);
    }
}

API Test (Feature Test)

// tests/Feature/Api/ItemsControllerTest.php
class ItemsControllerTest extends TestCase
{
    /** @test */
    public function it_lists_items_with_filtering()
    {
        $response = $this->getJson('/api/items?itemType=FG&search=test');

        $response->assertStatus(200)
                 ->assertJsonStructure([
                     'data' => [
                         '*' => ['id', 'itemCode', 'itemName', 'itemType']
                     ],
                     'meta' => ['total', 'page', 'perPage']
                 ]);
    }

    /** @test */
    public function it_creates_item_with_validation()
    {
        $data = [
            'itemName' => 'Test Product',
            'itemType' => 'FG',
            'unit' => 'EA',
            // ...
        ];

        $response = $this->postJson('/api/items', $data);

        $response->assertStatus(201)
                 ->assertJsonFragment(['itemName' => 'Test Product']);
    }
}

Postman Collection

{
  "info": {
    "name": "BP-MES Items API",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "item": [
    {
      "name": "품목 목록 조회",
      "request": {
        "method": "GET",
        "url": "{{baseUrl}}/api/items?itemType=FG&search=&category1="
      }
    },
    {
      "name": "품목 상세 조회",
      "request": {
        "method": "GET",
        "url": "{{baseUrl}}/api/items/:itemCode"
      }
    },
    {
      "name": "BOM Tree 조회",
      "request": {
        "method": "GET",
        "url": "{{baseUrl}}/api/items/:itemCode/bom/tree"
      }
    }
  ]
}

📝 API 문서화 (Swagger)

Swagger 주석 예시

/**
 * @OA\Get(
 *     path="/api/items",
 *     summary="품목 목록 조회",
 *     tags={"Items"},
 *     security={{"sanctum":{}}},
 *     @OA\Parameter(
 *         name="itemType",
 *         in="query",
 *         description="품목 유형 (FG,PT,SM,RM,CS)",
 *         required=false,
 *         @OA\Schema(type="string", enum={"FG","PT","SM","RM","CS"})
 *     ),
 *     @OA\Parameter(
 *         name="search",
 *         in="query",
 *         description="검색어 (품목명, 품목코드)",
 *         required=false,
 *         @OA\Schema(type="string")
 *     ),
 *     @OA\Response(
 *         response=200,
 *         description="성공",
 *         @OA\JsonContent(
 *             @OA\Property(property="data", type="array", @OA\Items(ref="#/components/schemas/ItemMaster")),
 *             @OA\Property(property="meta", ref="#/components/schemas/PaginationMeta")
 *         )
 *     )
 * )
 */
public function index(Request $request)
{
    // ...
}

⚙️ 개발 환경 설정

.env 설정

# Laravel API
APP_NAME="BP-MES API"
APP_ENV=local
APP_DEBUG=true
APP_URL=http://localhost:8000

# Database
DB_CONNECTION=pgsql
DB_HOST=127.0.0.1
DB_PORT=5432
DB_DATABASE=bp_mes
DB_USERNAME=postgres
DB_PASSWORD=

# File Storage
FILESYSTEM_DISK=local
# FILESYSTEM_DISK=s3
# AWS_ACCESS_KEY_ID=
# AWS_SECRET_ACCESS_KEY=
# AWS_DEFAULT_REGION=
# AWS_BUCKET=

# CORS (Next.js 프론트엔드)
CORS_ALLOWED_ORIGINS=http://localhost:3000,https://your-frontend-domain.com

CORS 설정

// config/cors.php
return [
    'paths' => ['api/*', 'sanctum/csrf-cookie'],
    'allowed_methods' => ['*'],
    'allowed_origins' => explode(',', env('CORS_ALLOWED_ORIGINS', '*')),
    'allowed_origins_patterns' => [],
    'allowed_headers' => ['*'],
    'exposed_headers' => [],
    'max_age' => 0,
    'supports_credentials' => true,
];

🚨 주의사항

1. Multi-tenancy

모든 API는 tenant_id 기반 필터링 필수:

// ItemsController.php
public function index(Request $request)
{
    $tenantId = auth()->user()->tenant_id;

    $items = Product::where('tenant_id', $tenantId)
        ->when($request->itemType, fn($q, $v) => $q->where('item_type', $v))
        ->when($request->search, fn($q, $v) => $q->where('item_name', 'like', "%{$v}%"))
        ->get();

    return response()->json(['data' => $items]);
}

2. Soft Delete

삭제는 항상 Soft Delete:

// Product 모델
use Illuminate\Database\Eloquent\SoftDeletes;

class Product extends Model
{
    use SoftDeletes, BelongsToTenant;

    protected $dates = ['deleted_at'];
}

3. Audit Log

변경 이력 기록:

// 품목 수정 시
AuditLog::create([
    'tenant_id' => $tenantId,
    'user_id' => auth()->id(),
    'action' => 'update',
    'model' => 'Product',
    'model_id' => $product->id,
    'changes' => [
        'before' => $product->getOriginal(),
        'after' => $product->getAttributes(),
    ],
]);

4. 파일 업로드 검증

// FileController.php
public function upload(Request $request, $itemCode)
{
    $request->validate([
        'type' => 'required|in:specification,certification,bending_diagram',
        'file' => 'required|file|max:10240|mimes:pdf,jpg,jpeg,png',
    ]);

    $type = $request->type;
    $file = $request->file('file');

    // 파일 저장
    $path = $file->store("items/{$itemCode}/{$type}", 'public');
    $url = Storage::url($path);

    // DB 업데이트
    $item = Product::where('item_code', $itemCode)->firstOrFail();

    if ($type === 'specification') {
        $item->specification_file = $url;
        $item->specification_file_name = $file->getClientOriginalName();
    } elseif ($type === 'certification') {
        $item->certification_file = $url;
        $item->certification_file_name = $file->getClientOriginalName();
    } elseif ($type === 'bending_diagram') {
        $item->bending_diagram = $url;
    }

    $item->save();

    return response()->json([
        'data' => [
            'url' => $url,
            'fileName' => $file->getClientOriginalName(),
            'type' => $type,
        ]
    ]);
}

📚 참고 문서

프로젝트 문서

  • 프론트엔드 가이드: ITEM_MANAGEMENT_MIGRATION_GUIDE.md
  • Phase 0 보고서: PHASE_0_FINAL_REPORT.md
  • Gap 검증: api_gap_validation_report.md
  • 아키텍처 옵션: ARCHITECTURE_OPTIONS_CORE.md

Laravel 문서


🎯 다음 단계

즉시 시작:

  1. Migration 파일 작성 (products, product_components 확장)
  2. ItemsController 리뷰 (기존 구현 확인)
  3. BOMController 설계 (tree API 구조)

협의 필요:

  • 절곡품 계산 로직: 프론트 vs 백엔드?
  • 파일 저장소: Local vs S3?
  • BOM 계산 API 응답 형식 확정
  • quantityFormula 수식 파싱 라이브러리 선택

준비 사항:

  • 개발 서버 환경 확인
  • PostgreSQL/MySQL 준비
  • Postman 설치
  • Git branch 전략

작성일: 2025-11-13 버전: 2.0 (프론트엔드 가이드 통합) 다음 업데이트: Migration 완료 후