# 품목기준관리 API 요청서

**작성일**: 2025-11-25
**요청자**: 프론트엔드 개발팀
**대상**: 백엔드 개발팀
**프로젝트**: SAM MES System - 품목기준관리 (Item Master Data Management)

---

## 1. 개요

### 1.1 목적
품목기준관리 화면에서 품목의 메타데이터(페이지, 섹션, 필드)를 동적으로 정의하기 위한 백엔드 API 개발 요청

### 1.2 프론트엔드 구현 현황
- 프론트엔드 UI 구현 완료
- API 클라이언트 코드 작성 완료 (`src/lib/api/item-master.ts`)
- 타입 정의 완료 (`src/types/item-master-api.ts`)
- Next.js API 프록시 구조 적용 (HttpOnly 쿠키 인증)

### 1.3 API 기본 정보
| 항목 | 값 |
|------|-----|
| Base URL | `/api/v1/item-master` |
| 인증 방식 | `auth.apikey + auth:sanctum` (HttpOnly Cookie) |
| Content-Type | `application/json` |
| 응답 형식 | 표준 API 응답 래퍼 사용 |

### 1.4 표준 응답 형식
```json
{
  "success": true,
  "message": "message.fetched",
  "data": { ... }
}
```

---

## 2. 필수 API 엔드포인트

### 2.1 초기화 API (최우선)

#### `GET /api/v1/item-master/init`

**목적**: 화면 진입 시 전체 데이터를 한 번에 로드

**Request**: 없음 (JWT에서 tenant_id 자동 추출)

**Response**:
```typescript
interface InitResponse {
  pages: ItemPageResponse[];           // 페이지 목록 (섹션, 필드 포함)
  sectionTemplates: SectionTemplateResponse[];  // 섹션 템플릿 목록
  masterFields: MasterFieldResponse[];  // 마스터 필드 목록
  customTabs: CustomTabResponse[];      // 커스텀 탭 목록
  tabColumns: Record<number, TabColumnResponse[]>;  // 탭별 컬럼 설정
  unitOptions: UnitOptionResponse[];    // 단위 옵션 목록
}
```

**중요**: `pages` 응답 시 `sections`와 `fields`를 Nested로 포함해야 함

**예시 응답**:
```json
{
  "success": true,
  "message": "message.fetched",
  "data": {
    "pages": [
      {
        "id": 1,
        "page_name": "기본정보",
        "item_type": "FG",
        "is_active": true,
        "sections": [
          {
            "id": 1,
            "title": "품목코드 정보",
            "type": "fields",
            "order_no": 1,
            "fields": [
              {
                "id": 1,
                "field_name": "품목코드",
                "field_type": "textbox",
                "is_required": true,
                "master_field_id": null,
                "order_no": 1
              }
            ]
          }
        ]
      }
    ],
    "sectionTemplates": [...],
    "masterFields": [...],
    "customTabs": [...],
    "tabColumns": {...},
    "unitOptions": [...]
  }
}
```

---

### 2.2 페이지 관리 API

#### `POST /api/v1/item-master/pages`
**목적**: 새 페이지 생성

**Request Body**:
```typescript
interface ItemPageRequest {
  page_name: string;      // 페이지명 (필수)
  item_type: 'FG' | 'PT' | 'SM' | 'RM' | 'CS';  // 품목유형 (필수)
  absolute_path?: string; // 절대경로 (선택)
  is_active?: boolean;    // 활성화 여부 (기본: true)
}
```

**Response**: `ItemPageResponse`

---

#### `PUT /api/v1/item-master/pages/{id}`
**목적**: 페이지 수정

**Path Parameter**: `id` - 페이지 ID

**Request Body**: `Partial<ItemPageRequest>`

**Response**: `ItemPageResponse`

---

#### `DELETE /api/v1/item-master/pages/{id}`
**목적**: 페이지 삭제 (Soft Delete)

**Path Parameter**: `id` - 페이지 ID

**Response**: `{ success: true, message: "message.deleted" }`

---

### 2.3 섹션 관리 API

#### `POST /api/v1/item-master/pages/{pageId}/sections`
**목적**: 페이지에 새 섹션 추가

**Path Parameter**: `pageId` - 페이지 ID

**Request Body**:
```typescript
interface ItemSectionRequest {
  title: string;                    // 섹션명 (필수)
  type: 'fields' | 'bom';           // 섹션 타입 (필수)
  template_id?: number;             // 템플릿 ID (선택) - 템플릿에서 생성 시
}
```

**중요 - 템플릿 적용 로직**:
- `template_id`가 전달되면 해당 템플릿의 필드들을 복사하여 새 섹션에 추가
- 템플릿의 필드들은 `master_field_id` 연결 관계도 복사

**Response**: `ItemSectionResponse` (생성된 섹션 + 필드 포함)

---

#### `PUT /api/v1/item-master/sections/{id}`
**목적**: 섹션 수정 (제목 변경 등)

**Path Parameter**: `id` - 섹션 ID

**Request Body**: `Partial<ItemSectionRequest>`

**Response**: `ItemSectionResponse`

---

#### `DELETE /api/v1/item-master/sections/{id}`
**목적**: 섹션 삭제

**Path Parameter**: `id` - 섹션 ID

**Response**: `{ success: true, message: "message.deleted" }`

---

#### `PUT /api/v1/item-master/pages/{pageId}/sections/reorder`
**목적**: 섹션 순서 변경 (드래그앤드롭)

**Path Parameter**: `pageId` - 페이지 ID

**Request Body**:
```typescript
interface SectionReorderRequest {
  section_orders: Array<{
    id: number;       // 섹션 ID
    order_no: number; // 새 순서
  }>;
}
```

**Response**: `ItemSectionResponse[]`

---

### 2.4 필드 관리 API

#### `POST /api/v1/item-master/sections/{sectionId}/fields`
**목적**: 섹션에 새 필드 추가

**Path Parameter**: `sectionId` - 섹션 ID

**Request Body**:
```typescript
interface ItemFieldRequest {
  field_name: string;     // 필드명 (필수)
  field_type: 'textbox' | 'number' | 'dropdown' | 'checkbox' | 'date' | 'textarea';  // 필드 타입 (필수)

  // 마스터 필드 연결 (핵심 기능)
  master_field_id?: number;  // 마스터 필드 ID (마스터에서 선택한 경우)

  // 선택 속성
  is_required?: boolean;
  placeholder?: string;
  default_value?: string;
  options?: Array<{ label: string; value: string }>;  // dropdown 옵션
  validation_rules?: Record<string, any>;
  properties?: Record<string, any>;

  // 조건부 표시 설정 (신규 기능)
  display_condition?: {
    field_key: string;           // 조건 필드 키
    expected_value: string;      // 예상 값
    target_field_ids?: string[]; // 표시할 필드 ID 목록
    target_section_ids?: string[]; // 표시할 섹션 ID 목록
  }[];
}
```

**중요 - master_field_id 처리**:
- 프론트엔드에서 "마스터 항목 선택" 모드로 필드 추가 시 `master_field_id` 전달
- 백엔드에서 해당 마스터 필드의 속성을 참조하여 기본값 설정
- 마스터 필드가 수정되면 연결된 필드도 동기화 필요 (옵션)

**Response**: `ItemFieldResponse`

---

#### `PUT /api/v1/item-master/fields/{id}`
**목적**: 필드 수정

**Path Parameter**: `id` - 필드 ID

**Request Body**: `Partial<ItemFieldRequest>`

**Response**: `ItemFieldResponse`

---

#### `DELETE /api/v1/item-master/fields/{id}`
**목적**: 필드 삭제

**Path Parameter**: `id` - 필드 ID

**Response**: `{ success: true, message: "message.deleted" }`

---

#### `PUT /api/v1/item-master/sections/{sectionId}/fields/reorder`
**목적**: 필드 순서 변경 (드래그앤드롭)

**Path Parameter**: `sectionId` - 섹션 ID

**Request Body**:
```typescript
interface FieldReorderRequest {
  field_orders: Array<{
    id: number;       // 필드 ID
    order_no: number; // 새 순서
  }>;
}
```

**Response**: `ItemFieldResponse[]`

---

### 2.5 섹션 템플릿 API

#### `GET /api/v1/item-master/section-templates`
**목적**: 섹션 템플릿 목록 조회

**Response**: `SectionTemplateResponse[]`

---

#### `POST /api/v1/item-master/section-templates`
**목적**: 새 섹션 템플릿 생성

**Request Body**:
```typescript
interface SectionTemplateRequest {
  title: string;           // 템플릿명 (필수)
  type: 'fields' | 'bom';  // 타입 (필수)
  description?: string;    // 설명 (선택)
  is_default?: boolean;    // 기본 템플릿 여부 (선택)

  // 템플릿에 포함될 필드들
  fields?: Array<{
    field_name: string;
    field_type: string;
    master_field_id?: number;
    is_required?: boolean;
    options?: Array<{ label: string; value: string }>;
    properties?: Record<string, any>;
  }>;
}
```

**Response**: `SectionTemplateResponse`

---

#### `PUT /api/v1/item-master/section-templates/{id}`
**목적**: 섹션 템플릿 수정

**Response**: `SectionTemplateResponse`

---

#### `DELETE /api/v1/item-master/section-templates/{id}`
**목적**: 섹션 템플릿 삭제

**Response**: `{ success: true, message: "message.deleted" }`

---

### 2.6 마스터 필드 API

#### `GET /api/v1/item-master/master-fields`
**목적**: 마스터 필드 목록 조회

**Response**: `MasterFieldResponse[]`

---

#### `POST /api/v1/item-master/master-fields`
**목적**: 새 마스터 필드 생성

**Request Body**:
```typescript
interface MasterFieldRequest {
  field_name: string;     // 필드명 (필수)
  field_type: 'textbox' | 'number' | 'dropdown' | 'checkbox' | 'date' | 'textarea';  // 필드 타입 (필수)
  category?: string;      // 카테고리 (선택) - 예: "기본정보", "스펙정보"
  description?: string;   // 설명 (선택)
  is_common?: boolean;    // 공통 항목 여부 (선택)
  default_value?: string;
  options?: Array<{ label: string; value: string }>;
  validation_rules?: Record<string, any>;
  properties?: Record<string, any>;
}
```

**Response**: `MasterFieldResponse`

---

#### `PUT /api/v1/item-master/master-fields/{id}`
**목적**: 마스터 필드 수정

**Response**: `MasterFieldResponse`

---

#### `DELETE /api/v1/item-master/master-fields/{id}`
**목적**: 마스터 필드 삭제

**주의**: 해당 마스터 필드를 참조하는 필드(`master_field_id`)가 있을 경우 처리 방안 필요
- 옵션 1: 삭제 불가 (참조 무결성)
- 옵션 2: 참조 해제 후 삭제

**Response**: `{ success: true, message: "message.deleted" }`

---

### 2.7 BOM 관리 API

#### `POST /api/v1/item-master/sections/{sectionId}/bom-items`
**목적**: BOM 항목 추가

**Request Body**:
```typescript
interface BomItemRequest {
  item_code?: string;
  item_name: string;    // 필수
  quantity: number;     // 필수
  unit?: string;
  unit_price?: number;
  total_price?: number;
  spec?: string;
  note?: string;
}
```

**Response**: `BomItemResponse`

---

#### `PUT /api/v1/item-master/bom-items/{id}`
**목적**: BOM 항목 수정

**Response**: `BomItemResponse`

---

#### `DELETE /api/v1/item-master/bom-items/{id}`
**목적**: BOM 항목 삭제

**Response**: `{ success: true, message: "message.deleted" }`

---

### 2.8 커스텀 탭 API

#### `GET /api/v1/item-master/custom-tabs`
**목적**: 커스텀 탭 목록 조회

**Response**: `CustomTabResponse[]`

---

#### `POST /api/v1/item-master/custom-tabs`
**목적**: 새 커스텀 탭 생성

**Request Body**:
```typescript
interface CustomTabRequest {
  label: string;        // 탭 레이블 (필수)
  icon?: string;        // 아이콘 (선택)
  is_default?: boolean; // 기본 탭 여부 (선택)
}
```

**Response**: `CustomTabResponse`

---

#### `PUT /api/v1/item-master/custom-tabs/{id}`
**목적**: 커스텀 탭 수정

**Response**: `CustomTabResponse`

---

#### `DELETE /api/v1/item-master/custom-tabs/{id}`
**목적**: 커스텀 탭 삭제

**Response**: `{ success: true, message: "message.deleted" }`

---

#### `PUT /api/v1/item-master/custom-tabs/reorder`
**목적**: 탭 순서 변경

**Request Body**:
```typescript
interface TabReorderRequest {
  tab_orders: Array<{
    id: number;
    order_no: number;
  }>;
}
```

**Response**: `{ success: true }`

---

#### `PUT /api/v1/item-master/custom-tabs/{id}/columns`
**목적**: 탭별 컬럼 설정 업데이트

**Request Body**:
```typescript
interface TabColumnUpdateRequest {
  columns: Array<{
    key: string;
    label: string;
    visible: boolean;
    order: number;
  }>;
}
```

**Response**: `TabColumnResponse[]`

---

### 2.9 단위 옵션 API

#### `GET /api/v1/item-master/unit-options`
**목적**: 단위 옵션 목록 조회

**Response**: `UnitOptionResponse[]`

---

#### `POST /api/v1/item-master/unit-options`
**목적**: 새 단위 옵션 추가

**Request Body**:
```typescript
interface UnitOptionRequest {
  label: string;  // 표시명 (예: "개")
  value: string;  // 값 (예: "EA")
}
```

**Response**: `UnitOptionResponse`

---

#### `DELETE /api/v1/item-master/unit-options/{id}`
**목적**: 단위 옵션 삭제

**Response**: `{ success: true, message: "message.deleted" }`

---

## 3. 데이터베이스 스키마 제안

### 3.1 item_master_pages
```sql
CREATE TABLE item_master_pages (
    id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
    tenant_id BIGINT UNSIGNED NOT NULL,
    page_name VARCHAR(100) NOT NULL,
    item_type ENUM('FG', 'PT', 'SM', 'RM', 'CS') NOT NULL,
    absolute_path VARCHAR(500) NULL,
    order_no INT NOT NULL DEFAULT 0,
    is_active BOOLEAN NOT NULL DEFAULT TRUE,
    created_by BIGINT UNSIGNED NULL,
    updated_by BIGINT UNSIGNED NULL,
    deleted_by BIGINT UNSIGNED NULL,
    created_at TIMESTAMP NULL,
    updated_at TIMESTAMP NULL,
    deleted_at TIMESTAMP NULL,

    INDEX idx_tenant (tenant_id),
    FOREIGN KEY (tenant_id) REFERENCES tenants(id)
);
```

### 3.2 item_master_sections
```sql
CREATE TABLE item_master_sections (
    id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
    tenant_id BIGINT UNSIGNED NOT NULL,
    page_id BIGINT UNSIGNED NOT NULL,
    title VARCHAR(100) NOT NULL,
    type ENUM('fields', 'bom') NOT NULL DEFAULT 'fields',
    order_no INT NOT NULL DEFAULT 0,
    created_by BIGINT UNSIGNED NULL,
    updated_by BIGINT UNSIGNED NULL,
    deleted_by BIGINT UNSIGNED NULL,
    created_at TIMESTAMP NULL,
    updated_at TIMESTAMP NULL,
    deleted_at TIMESTAMP NULL,

    INDEX idx_tenant_page (tenant_id, page_id),
    FOREIGN KEY (tenant_id) REFERENCES tenants(id),
    FOREIGN KEY (page_id) REFERENCES item_master_pages(id) ON DELETE CASCADE
);
```

### 3.3 item_master_fields
```sql
CREATE TABLE item_master_fields (
    id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
    tenant_id BIGINT UNSIGNED NOT NULL,
    section_id BIGINT UNSIGNED NOT NULL,
    master_field_id BIGINT UNSIGNED NULL,  -- 마스터 필드 참조
    field_name VARCHAR(100) NOT NULL,
    field_type ENUM('textbox', 'number', 'dropdown', 'checkbox', 'date', 'textarea') NOT NULL,
    order_no INT NOT NULL DEFAULT 0,
    is_required BOOLEAN NOT NULL DEFAULT FALSE,
    placeholder VARCHAR(200) NULL,
    default_value VARCHAR(500) NULL,
    display_condition JSON NULL,   -- 조건부 표시 설정
    validation_rules JSON NULL,
    options JSON NULL,             -- dropdown 옵션
    properties JSON NULL,          -- 추가 속성 (컬럼 설정 등)
    created_by BIGINT UNSIGNED NULL,
    updated_by BIGINT UNSIGNED NULL,
    deleted_by BIGINT UNSIGNED NULL,
    created_at TIMESTAMP NULL,
    updated_at TIMESTAMP NULL,
    deleted_at TIMESTAMP NULL,

    INDEX idx_tenant_section (tenant_id, section_id),
    INDEX idx_master_field (master_field_id),
    FOREIGN KEY (tenant_id) REFERENCES tenants(id),
    FOREIGN KEY (section_id) REFERENCES item_master_sections(id) ON DELETE CASCADE,
    FOREIGN KEY (master_field_id) REFERENCES item_master_master_fields(id) ON DELETE SET NULL
);
```

### 3.4 item_master_master_fields (마스터 필드)
```sql
CREATE TABLE item_master_master_fields (
    id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
    tenant_id BIGINT UNSIGNED NOT NULL,
    field_name VARCHAR(100) NOT NULL,
    field_type ENUM('textbox', 'number', 'dropdown', 'checkbox', 'date', 'textarea') NOT NULL,
    category VARCHAR(50) NULL,
    description TEXT NULL,
    is_common BOOLEAN NOT NULL DEFAULT FALSE,
    default_value VARCHAR(500) NULL,
    options JSON NULL,
    validation_rules JSON NULL,
    properties JSON NULL,
    created_by BIGINT UNSIGNED NULL,
    updated_by BIGINT UNSIGNED NULL,
    deleted_by BIGINT UNSIGNED NULL,
    created_at TIMESTAMP NULL,
    updated_at TIMESTAMP NULL,
    deleted_at TIMESTAMP NULL,

    INDEX idx_tenant (tenant_id),
    INDEX idx_category (tenant_id, category),
    FOREIGN KEY (tenant_id) REFERENCES tenants(id)
);
```

### 3.5 item_master_section_templates (섹션 템플릿)
```sql
CREATE TABLE item_master_section_templates (
    id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
    tenant_id BIGINT UNSIGNED NOT NULL,
    title VARCHAR(100) NOT NULL,
    type ENUM('fields', 'bom') NOT NULL DEFAULT 'fields',
    description TEXT NULL,
    is_default BOOLEAN NOT NULL DEFAULT FALSE,
    created_by BIGINT UNSIGNED NULL,
    updated_by BIGINT UNSIGNED NULL,
    deleted_by BIGINT UNSIGNED NULL,
    created_at TIMESTAMP NULL,
    updated_at TIMESTAMP NULL,
    deleted_at TIMESTAMP NULL,

    INDEX idx_tenant (tenant_id),
    FOREIGN KEY (tenant_id) REFERENCES tenants(id)
);
```

### 3.6 item_master_template_fields (템플릿 필드)
```sql
CREATE TABLE item_master_template_fields (
    id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
    tenant_id BIGINT UNSIGNED NOT NULL,
    template_id BIGINT UNSIGNED NOT NULL,
    master_field_id BIGINT UNSIGNED NULL,
    field_name VARCHAR(100) NOT NULL,
    field_type ENUM('textbox', 'number', 'dropdown', 'checkbox', 'date', 'textarea') NOT NULL,
    order_no INT NOT NULL DEFAULT 0,
    is_required BOOLEAN NOT NULL DEFAULT FALSE,
    placeholder VARCHAR(200) NULL,
    default_value VARCHAR(500) NULL,
    options JSON NULL,
    properties JSON NULL,
    created_at TIMESTAMP NULL,
    updated_at TIMESTAMP NULL,

    INDEX idx_template (template_id),
    FOREIGN KEY (tenant_id) REFERENCES tenants(id),
    FOREIGN KEY (template_id) REFERENCES item_master_section_templates(id) ON DELETE CASCADE,
    FOREIGN KEY (master_field_id) REFERENCES item_master_master_fields(id) ON DELETE SET NULL
);
```

---

## 4. 핵심 비즈니스 로직

### 4.1 마스터 필드 연결 (`master_field_id`)

**시나리오**: 사용자가 필드 추가 시 "마스터 항목 선택" 모드로 추가

**프론트엔드 동작**:
1. 마스터 필드 목록에서 선택
2. 선택된 마스터 필드의 속성을 폼에 자동 채움
3. 저장 시 `master_field_id` 포함하여 전송

**백엔드 처리**:
```php
// ItemFieldService.php
public function create(int $sectionId, array $data): ItemField
{
    // master_field_id가 있으면 마스터 필드에서 기본값 가져오기
    if (!empty($data['master_field_id'])) {
        $masterField = MasterField::findOrFail($data['master_field_id']);

        // 마스터 필드의 속성을 기본값으로 사용 (명시적 값이 없는 경우)
        $data = array_merge([
            'field_type' => $masterField->field_type,
            'options' => $masterField->options,
            'validation_rules' => $masterField->validation_rules,
            'properties' => $masterField->properties,
        ], $data);
    }

    return ItemField::create($data);
}
```

### 4.2 섹션 템플릿 적용

**시나리오**: 사용자가 섹션 추가 시 "템플릿에서 선택" 모드로 추가

**프론트엔드 동작**:
1. 템플릿 목록에서 선택
2. 선택된 템플릿 정보로 섹션 생성 요청
3. `template_id` 포함하여 전송

**백엔드 처리**:
```php
// ItemSectionService.php
public function create(int $pageId, array $data): ItemSection
{
    $section = ItemSection::create([
        'page_id' => $pageId,
        'title' => $data['title'],
        'type' => $data['type'],
    ]);

    // template_id가 있으면 템플릿의 필드들을 복사
    if (!empty($data['template_id'])) {
        $templateFields = TemplateField::where('template_id', $data['template_id'])
            ->orderBy('order_no')
            ->get();

        foreach ($templateFields as $index => $tf) {
            ItemField::create([
                'section_id' => $section->id,
                'master_field_id' => $tf->master_field_id,  // 마스터 연결 유지
                'field_name' => $tf->field_name,
                'field_type' => $tf->field_type,
                'order_no' => $index,
                'is_required' => $tf->is_required,
                'options' => $tf->options,
                'properties' => $tf->properties,
            ]);
        }
    }

    return $section->load('fields');
}
```

### 4.3 조건부 표시 설정

**JSON 구조**:
```json
{
  "display_condition": [
    {
      "field_key": "item_type",
      "expected_value": "FG",
      "target_field_ids": ["5", "6", "7"]
    },
    {
      "field_key": "item_type",
      "expected_value": "PT",
      "target_section_ids": ["3"]
    }
  ]
}
```

**활용**: 프론트엔드에서 품목 데이터 입력 시 해당 조건에 따라 필드/섹션을 동적으로 표시/숨김

---

## 5. 우선순위

### Phase 1 (필수 - 즉시)
1. `GET /api/v1/item-master/init` - 초기화 API
2. 페이지 CRUD API
3. 섹션 CRUD API (순서변경 포함)
4. 필드 CRUD API (순서변경 포함, `master_field_id` 지원)

### Phase 2 (중요 - 1주 내)
5. 마스터 필드 CRUD API
6. 섹션 템플릿 CRUD API
7. 템플릿 필드 관리

### Phase 3 (선택 - 2주 내)
8. BOM 항목 관리 API
9. 커스텀 탭 API
10. 단위 옵션 API

---

## 6. 참고 사항

### 6.1 프론트엔드 코드 위치
- API 클라이언트: `src/lib/api/item-master.ts`
- 타입 정의: `src/types/item-master-api.ts`
- 메인 컴포넌트: `src/components/items/ItemMasterDataManagement.tsx`

### 6.2 기존 API 문서
- `claudedocs/[API-2025-11-24] item-management-dynamic-api-spec.md` - 품목관리 동적 화면 API

### 6.3 Multi-Tenancy
- 모든 테이블에 `tenant_id` 컬럼 필수
- JWT에서 tenant_id 자동 추출
- `BelongsToTenant` Trait 적용 필요

### 6.4 에러 응답 형식
```json
{
  "success": false,
  "message": "error.validation_failed",
  "errors": {
    "field_name": ["필드명은 필수입니다."],
    "field_type": ["유효하지 않은 필드 타입입니다."]
  }
}
```

---

## 7. 연락처

질문이나 협의 사항이 있으면 언제든 연락 바랍니다.

**프론트엔드 담당**: [담당자명]
**작성일**: 2025-11-25