# 품목기준관리 API 연동 체크리스트

**작성일**: 2025-11-20
**목적**: LocalStorage → 백엔드 API 실시간 저장 방식 전환
**예상 작업 시간**: 6-8시간
**API 문서**: `claudedocs/itemmaster.txt`

---

## 🌐 API 엔드포인트 레퍼런스 (빠른 참조)

### Base URL
```
http://api.sam.kr/api/v1
또는
process.env.NEXT_PUBLIC_API_BASE_URL
```

### 인증
- **Header**: `X-API-KEY`, `Authorization: Bearer {token}`
- **환경변수**: `NEXT_PUBLIC_API_KEY`

### 주요 엔드포인트

#### 초기화
- `GET /item-master/init` - 전체 초기 데이터 로드

#### 페이지 관리
- `GET /item-master/pages` - 페이지 목록 조회
- `POST /item-master/pages` - 페이지 생성
  ```json
  { "page_name": "string", "item_type": "FG|PT|SM|RM|CS", "absolute_path": "string?" }
  ```
- `PUT /item-master/pages/{id}` - 페이지 수정
  ```json
  { "page_name": "string", "absolute_path": "string?" }
  ```
- `DELETE /item-master/pages/{id}` - 페이지 삭제 (Cascade)

#### 섹션 관리
- `POST /item-master/pages/{pageId}/sections` - 섹션 생성
  ```json
  { "title": "string", "type": "fields|bom" }
  ```
- `PUT /item-master/sections/{id}` - 섹션 수정
- `DELETE /item-master/sections/{id}` - 섹션 삭제
- `PUT /item-master/pages/{pageId}/sections/reorder` - 순서 변경
  ```json
  { "items": [{"id": 1, "order_no": 0}] }
  ```

#### 필드 관리
- `POST /item-master/sections/{sectionId}/fields` - 필드 생성
  ```json
  {
    "field_name": "string",
    "field_type": "textbox|number|dropdown|checkbox|date|textarea",
    "is_required": boolean,
    "placeholder": "string?",
    "options": object?,
    "validation_rules": object?
  }
  ```
- `PUT /item-master/fields/{id}` - 필드 수정
- `DELETE /item-master/fields/{id}` - 필드 삭제
- `PUT /item-master/sections/{sectionId}/fields/reorder` - 순서 변경

#### BOM 관리
- `POST /item-master/sections/{sectionId}/bom-items` - BOM 항목 생성
  ```json
  {
    "item_name": "string",
    "item_code": "string?",
    "quantity": number,
    "unit": "string?",
    "unit_price": number?,
    "spec": "string?"
  }
  ```
- `PUT /item-master/bom-items/{id}` - BOM 항목 수정
- `DELETE /item-master/bom-items/{id}` - BOM 항목 삭제

#### 템플릿 관리
- `GET /item-master/section-templates` - 템플릿 목록
- `POST /item-master/section-templates` - 템플릿 생성
- `PUT /item-master/section-templates/{id}` - 템플릿 수정
- `DELETE /item-master/section-templates/{id}` - 템플릿 삭제

#### 마스터 필드
- `GET /item-master/master-fields` - 마스터 필드 목록
- `POST /item-master/master-fields` - 마스터 필드 생성
- `PUT /item-master/master-fields/{id}` - 마스터 필드 수정
- `DELETE /item-master/master-fields/{id}` - 마스터 필드 삭제

#### 커스텀 탭
- `GET /item-master/custom-tabs` - 커스텀 탭 목록
- `POST /item-master/custom-tabs` - 커스텀 탭 생성
- `PUT /item-master/custom-tabs/{id}` - 커스텀 탭 수정
- `DELETE /item-master/custom-tabs/{id}` - 커스텀 탭 삭제
- `PUT /item-master/custom-tabs/reorder` - 순서 변경

#### 단위 옵션
- `GET /item-master/unit-options` - 단위 옵션 목록
- `POST /item-master/unit-options` - 단위 옵션 생성
  ```json
  { "label": "개", "value": "EA" }
  ```
- `DELETE /item-master/unit-options/{id}` - 단위 옵션 삭제

### 응답 형식
```typescript
// 성공 응답
{
  "success": true,
  "message": "string",
  "data": T  // 요청한 데이터
}

// 에러 응답
{
  "success": false,
  "message": "string",
  "errors": {  // Validation 에러 시
    "field_name": ["error message"]
  }
}
```

### 주요 필드명 (snake_case)
- `page_name`, `item_type`, `absolute_path`, `is_active`, `order_no`
- `section_id`, `section_name`, `section_type`
- `field_name`, `field_type`, `is_required`, `default_value`
- `created_at`, `updated_at`, `created_by`, `updated_by`, `tenant_id`

---

## 📊 전체 진행 상황

```
전체 진행률: 63/69 (91%)

Phase 0 (준비): 22/22 (100%) ✅ 완료! (필수 20개 + 선택 2개)
Phase 1 (초기화): 8/8 (100%) ✅ 완료!
Phase 2 (CRUD): 33/33 (100%) ✅ 완료! 🎉
Phase 3 (정리): 0/6 (0%) ⏳ API 필요
```

**작업 우선순위**:
- 🟢 **Phase 0**: 백엔드 API 없이 지금 바로 진행 가능
- 🟡 **Phase 1-3**: 백엔드 API 구현 완료 후 진행

---

## Phase 0: API 대기 전 준비 작업 (지금 가능) ⭐

### 📁 1. 파일 구조 준비 (3개)

- [x] **1.1** `src/lib/api/item-master.ts` API Client 파일 생성 ✅
  - **목적**: 모든 API 호출 함수 중앙 관리
  - **예상 시간**: 30분
  - **완료 조건**: 빈 파일에 기본 구조 작성
  - **완료일**: 2025-11-20
  ```typescript
  // src/lib/api/item-master.ts
  import { getAuthHeaders } from './auth-headers';

  const BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL || 'http://api.sam.kr/api/v1';

  export const itemMasterApi = {
    // 초기화
    init: async () => {
      // TODO: API 연동 시 구현
    },

    // 페이지 관리
    pages: {
      list: async () => { /* TODO */ },
      create: async (data: any) => { /* TODO */ },
      update: async (id: number, data: any) => { /* TODO */ },
      delete: async (id: number) => { /* TODO */ },
      reorder: async (orders: any[]) => { /* TODO */ },
    },

    // 섹션 관리
    sections: {
      create: async (pageId: number, data: any) => { /* TODO */ },
      update: async (id: number, data: any) => { /* TODO */ },
      delete: async (id: number) => { /* TODO */ },
      reorder: async (pageId: number, orders: any[]) => { /* TODO */ },
    },

    // 필드 관리
    fields: {
      create: async (sectionId: number, data: any) => { /* TODO */ },
      update: async (id: number, data: any) => { /* TODO */ },
      delete: async (id: number) => { /* TODO */ },
      reorder: async (sectionId: number, orders: any[]) => { /* TODO */ },
    },

    // BOM 관리
    bomItems: {
      create: async (sectionId: number, data: any) => { /* TODO */ },
      update: async (id: number, data: any) => { /* TODO */ },
      delete: async (id: number) => { /* TODO */ },
    },

    // 섹션 템플릿
    templates: {
      list: async () => { /* TODO */ },
      create: async (data: any) => { /* TODO */ },
      update: async (id: number, data: any) => { /* TODO */ },
      delete: async (id: number) => { /* TODO */ },
    },

    // 마스터 필드
    masterFields: {
      list: async () => { /* TODO */ },
      create: async (data: any) => { /* TODO */ },
      update: async (id: number, data: any) => { /* TODO */ },
      delete: async (id: number) => { /* TODO */ },
    },

    // 커스텀 탭
    customTabs: {
      list: async () => { /* TODO */ },
      create: async (data: any) => { /* TODO */ },
      update: async (id: number, data: any) => { /* TODO */ },
      delete: async (id: number) => { /* TODO */ },
      reorder: async (orders: any[]) => { /* TODO */ },
      updateColumns: async (id: number, columns: any[]) => { /* TODO */ },
    },

    // 단위 옵션
    units: {
      list: async () => { /* TODO */ },
      create: async (data: any) => { /* TODO */ },
      delete: async (id: number) => { /* TODO */ },
    },
  };
  ```

- [x] **1.2** `src/lib/api/auth-headers.ts` 인증 헤더 유틸 생성 ✅
  - **목적**: 모든 API 요청에 인증 헤더 자동 추가
  - **예상 시간**: 15분
  - **완료 조건**: getAuthHeaders 함수 구현
  - **완료일**: 2025-11-20
  ```typescript
  // src/lib/api/auth-headers.ts
  export const getAuthHeaders = (): HeadersInit => {
    // TODO: 실제 토큰 가져오기 로직 구현 필요
    // AuthContext나 쿠키에서 토큰 추출
    const token = typeof window !== 'undefined'
      ? document.cookie.split('; ').find(row => row.startsWith('auth_token='))?.split('=')[1]
      : '';

    return {
      'Content-Type': 'application/json',
      'X-API-KEY': process.env.NEXT_PUBLIC_API_KEY || '',
      'Authorization': token ? `Bearer ${token}` : '',
    };
  };

  export const getMultipartHeaders = (): HeadersInit => {
    const token = typeof window !== 'undefined'
      ? document.cookie.split('; ').find(row => row.startsWith('auth_token='))?.split('=')[1]
      : '';

    return {
      'X-API-KEY': process.env.NEXT_PUBLIC_API_KEY || '',
      'Authorization': token ? `Bearer ${token}` : '',
      // Content-Type은 자동 설정 (multipart/form-data)
    };
  };
  ```

- [x] **1.3** `src/types/item-master-api.ts` API 타입 정의 파일 생성 ✅
  - **목적**: Request/Response 타입 분리 및 명확화
  - **예상 시간**: 45분
  - **완료 조건**: 모든 API 엔드포인트의 Request/Response 타입 정의
  - **완료일**: 2025-11-20
  ```typescript
  // src/types/item-master-api.ts

  // ============================================
  // 공통 타입
  // ============================================
  export interface ApiResponse<T> {
    success: boolean;
    message: string;
    data: T;
  }

  export interface PaginationMeta {
    current_page: number;
    per_page: number;
    total: number;
    last_page: number;
  }

  // ============================================
  // 초기화 API
  // ============================================
  export interface InitResponse {
    pages: ItemPageResponse[];
    sections: ItemSectionResponse[];
    fields: ItemFieldResponse[];
    bomItems: BomItemResponse[];
    templates: SectionTemplateResponse[];
    masterFields: MasterFieldResponse[];
    customTabs: CustomTabResponse[];
    units: UnitOptionResponse[];
  }

  // ============================================
  // 페이지 관리
  // ============================================
  export interface ItemPageRequest {
    page_name: string;
    item_type: 'FG' | 'PT' | 'SM' | 'RM' | 'CS';
    description?: string;
    is_active?: boolean;
  }

  export interface ItemPageResponse {
    id: number;
    tenant_id: number;
    page_name: string;
    item_type: string;
    description: string | null;
    absolute_path: string;
    is_active: boolean;
    order_no: number;
    created_by: number | null;
    updated_by: number | null;
    created_at: string;
    updated_at: string;
    sections?: ItemSectionResponse[]; // Nested 조회 시 포함
  }

  export interface PageReorderRequest {
    page_orders: Array<{
      id: number;
      order_no: number;
    }>;
  }

  // ============================================
  // 섹션 관리
  // ============================================
  export interface ItemSectionRequest {
    section_name: string;
    section_type: 'BASIC' | 'BOM' | 'CUSTOM';
    description?: string;
    is_collapsible?: boolean;
    is_default_open?: boolean;
  }

  export interface ItemSectionResponse {
    id: number;
    tenant_id: number;
    page_id: number;
    section_template_id: number | null;
    section_name: string;
    section_type: string;
    description: string | null;
    order_no: number;
    is_collapsible: boolean;
    is_default_open: boolean;
    created_by: number | null;
    updated_by: number | null;
    created_at: string;
    updated_at: string;
    fields?: ItemFieldResponse[]; // Nested 조회 시 포함
    bomItems?: BomItemResponse[]; // Nested 조회 시 포함
  }

  export interface SectionReorderRequest {
    section_orders: Array<{
      id: number;
      order_no: number;
    }>;
  }

  // ============================================
  // 필드 관리
  // ============================================
  export interface ItemFieldRequest {
    field_name: string;
    field_type: 'TEXT' | 'NUMBER' | 'DATE' | 'SELECT' | 'TEXTAREA' | 'CHECKBOX';
    is_required?: boolean;
    placeholder?: string;
    default_value?: string;
    validation_rules?: Record<string, any>;
    properties?: Record<string, any>;
  }

  export interface ItemFieldResponse {
    id: number;
    tenant_id: number;
    section_id: number;
    master_field_id: number | null;
    field_name: string;
    field_type: string;
    order_no: number;
    is_required: boolean;
    placeholder: string | null;
    default_value: string | null;
    validation_rules: Record<string, any> | null;
    properties: Record<string, any> | null;
    created_by: number | null;
    updated_by: number | null;
    created_at: string;
    updated_at: string;
  }

  export interface FieldReorderRequest {
    field_orders: Array<{
      id: number;
      order_no: number;
    }>;
  }

  // ============================================
  // BOM 관리
  // ============================================
  export interface BomItemRequest {
    item_code?: string;
    item_name: string;
    quantity: number;
    unit?: string;
    unit_price?: number;
    total_price?: number;
    spec?: string;
    note?: string;
  }

  export interface BomItemResponse {
    id: number;
    tenant_id: number;
    section_id: number;
    item_code: string | null;
    item_name: string;
    quantity: number;
    unit: string | null;
    unit_price: number | null;
    total_price: number | null;
    spec: string | null;
    note: string | null;
    created_by: number | null;
    updated_by: number | null;
    created_at: string;
    updated_at: string;
  }

  // ============================================
  // 섹션 템플릿
  // ============================================
  export interface SectionTemplateRequest {
    template_name: string;
    section_type: 'BASIC' | 'BOM' | 'CUSTOM';
    description?: string;
    default_fields?: Record<string, any>;
  }

  export interface SectionTemplateResponse {
    id: number;
    tenant_id: number;
    template_name: string;
    section_type: string;
    description: string | null;
    default_fields: Record<string, any> | null;
    created_by: number | null;
    updated_by: number | null;
    created_at: string;
    updated_at: string;
  }

  // ============================================
  // 마스터 필드
  // ============================================
  export interface MasterFieldRequest {
    field_name: string;
    field_type: 'TEXT' | 'NUMBER' | 'DATE' | 'SELECT' | 'TEXTAREA' | 'CHECKBOX';
    category?: string;
    description?: string;
    default_validation?: Record<string, any>;
    default_properties?: Record<string, any>;
  }

  export interface MasterFieldResponse {
    id: number;
    tenant_id: number;
    field_name: string;
    field_type: string;
    category: string | null;
    description: string | null;
    default_validation: Record<string, any> | null;
    default_properties: Record<string, any> | null;
    created_by: number | null;
    updated_by: number | null;
    created_at: string;
    updated_at: string;
  }

  // ============================================
  // 커스텀 탭
  // ============================================
  export interface CustomTabRequest {
    tab_name: string;
    tab_key: string;
    description?: string;
    is_active?: boolean;
  }

  export interface CustomTabResponse {
    id: number;
    tenant_id: number;
    tab_name: string;
    tab_key: string;
    description: string | null;
    order_no: number;
    is_active: boolean;
    created_by: number | null;
    updated_by: number | null;
    created_at: string;
    updated_at: string;
    columns?: TabColumnResponse[]; // Nested 조회 시 포함
  }

  export interface TabReorderRequest {
    tab_orders: Array<{
      id: number;
      order_no: number;
    }>;
  }

  export interface TabColumnUpdateRequest {
    columns: Array<{
      column_key: string;
      column_name: string;
      column_type: string;
      is_visible: boolean;
      width?: number;
      order_no: number;
    }>;
  }

  export interface TabColumnResponse {
    id: number;
    tenant_id: number;
    tab_id: number;
    column_key: string;
    column_name: string;
    column_type: string;
    is_visible: boolean;
    width: number | null;
    order_no: number;
    created_at: string;
    updated_at: string;
  }

  // ============================================
  // 단위 옵션
  // ============================================
  export interface UnitOptionRequest {
    unit_name: string;
    unit_symbol?: string;
    description?: string;
  }

  export interface UnitOptionResponse {
    id: number;
    tenant_id: number;
    unit_name: string;
    unit_symbol: string | null;
    description: string | null;
    created_by: number | null;
    created_at: string;
    updated_at: string;
  }
  ```

---

### 🎨 2. UI 컴포넌트 준비 (3개)

- [x] **2.1** `src/components/ui/loading-spinner.tsx` 로딩 스피너 컴포넌트 생성 ✅
  - **목적**: API 호출 중 로딩 상태 표시
  - **예상 시간**: 15분
  - **완료 조건**: 재사용 가능한 로딩 스피너 컴포넌트
  - **완료일**: 2025-11-20
  ```typescript
  // src/components/ui/loading-spinner.tsx
  import React from 'react';

  interface LoadingSpinnerProps {
    size?: 'sm' | 'md' | 'lg';
    className?: string;
    text?: string;
  }

  export const LoadingSpinner: React.FC<LoadingSpinnerProps> = ({
    size = 'md',
    className = '',
    text
  }) => {
    const sizeClasses = {
      sm: 'h-4 w-4',
      md: 'h-8 w-8',
      lg: 'h-12 w-12'
    };

    return (
      <div className={`flex flex-col items-center justify-center gap-2 ${className}`}>
        <div className={`animate-spin rounded-full border-b-2 border-primary ${sizeClasses[size]}`} />
        {text && <p className="text-sm text-muted-foreground">{text}</p>}
      </div>
    );
  };
  ```

- [x] **2.2** `src/components/ui/error-message.tsx` 에러 메시지 컴포넌트 생성 ✅
  - **목적**: API 오류 메시지 일관된 UI로 표시
  - **예상 시간**: 15분
  - **완료 조건**: 재사용 가능한 에러 메시지 컴포넌트
  - **완료일**: 2025-11-20
  ```typescript
  // src/components/ui/error-message.tsx
  import React from 'react';
  import { AlertCircle } from 'lucide-react';
  import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';

  interface ErrorMessageProps {
    title?: string;
    message: string;
    onRetry?: () => void;
    className?: string;
  }

  export const ErrorMessage: React.FC<ErrorMessageProps> = ({
    title = '오류 발생',
    message,
    onRetry,
    className = ''
  }) => {
    return (
      <Alert variant="destructive" className={className}>
        <AlertCircle className="h-4 w-4" />
        <AlertTitle>{title}</AlertTitle>
        <AlertDescription className="mt-2">
          <p>{message}</p>
          {onRetry && (
            <button
              onClick={onRetry}
              className="mt-2 text-sm underline hover:no-underline"
            >
              다시 시도
            </button>
          )}
        </AlertDescription>
      </Alert>
    );
  };
  ```

- [x] **2.3** `src/components/items/ItemMasterDataManagement.tsx`에 로딩/에러 state 추가 ✅
  - **목적**: 전역 로딩 및 에러 상태 관리
  - **예상 시간**: 10분
  - **완료 조건**: state 추가 및 초기값 설정
  - **완료일**: 2025-11-20
  ```typescript
  // ItemMasterDataManagement.tsx 상단에 추가
  const [isInitialLoading, setIsInitialLoading] = useState(true);
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);
  ```

---

### 🔄 3. State 타입 변경 준비 (6개)

- [x] **3.1** ItemPage 타입 변경 (ID: string → number) ✅
  - **파일**: `src/contexts/ItemMasterContext.tsx`
  - **예상 시간**: 10분
  - **완료일**: 2025-11-20
  - **작업 내용**:
    - 기존 `id: string` → `id: number`
    - `absolutePath` → `absolute_path`
    - `createdAt` → `created_at`, `updated_at` 추가
  ```typescript
  // 기존 타입 (주석 처리)
  // interface ItemPage {
  //   id: string; // "PAGE-123"
  //   pageName: string;
  //   itemType: string;
  //   absolutePath: string;
  //   createdAt: string;
  // }

  // 새로운 타입 (API 응답 기준)
  interface ItemPage {
    id: number; // 서버 생성 ID
    tenant_id?: number; // 백엔드에서 자동 추가
    page_name: string; // camelCase → snake_case
    item_type: string;
    description?: string | null;
    absolute_path: string;
    is_active: boolean;
    order_no: number;
    created_by?: number | null;
    updated_by?: number | null;
    created_at: string;
    updated_at: string;
    sections?: ItemSection[]; // Nested 데이터
  }
  ```

- [x] **3.2** ItemSection 타입 변경 ✅
  - **파일**: `src/contexts/ItemMasterContext.tsx`
  - **예상 시간**: 10분
  - **완료일**: 2025-11-20
  ```typescript
  interface ItemSection {
    id: number; // string → number
    tenant_id?: number;
    page_id: number; // 외래키
    section_template_id?: number | null;
    section_name: string;
    section_type: 'BASIC' | 'BOM' | 'CUSTOM';
    description?: string | null;
    order_no: number;
    is_collapsible: boolean;
    is_default_open: boolean;
    created_by?: number | null;
    updated_by?: number | null;
    created_at: string;
    updated_at: string;
    fields?: ItemField[];
    bomItems?: BomItem[];
  }
  ```

- [x] **3.3** ItemField 타입 변경 ✅
  - **파일**: `src/contexts/ItemMasterContext.tsx`
  - **예상 시간**: 10분
  - **완료일**: 2025-11-20
  ```typescript
  interface ItemField {
    id: number;
    tenant_id?: number;
    section_id: number;
    master_field_id?: number | null;
    field_name: string;
    field_type: 'textbox' | 'number' | 'dropdown' | 'checkbox' | 'date' | 'textarea';
    order_no: number;
    is_required: boolean;
    placeholder?: string | null;
    default_value?: string | null;
    validation_rules?: Record<string, any> | null;
    properties?: Record<string, any> | null;
    display_condition?: Record<string, any> | null;
    options?: Array<{ label: string; value: string }> | null;
    created_by?: number | null;
    updated_by?: number | null;
    created_at: string;
    updated_at: string;
  }
  ```

- [x] **3.4** BomItem 타입 변경 ✅
  - **파일**: `src/contexts/ItemMasterContext.tsx`
  - **예상 시간**: 10분
  - **완료일**: 2025-11-20
  ```typescript
  interface BOMItem {
    id: number;
    tenant_id?: number;
    section_id: number;
    item_code?: string | null;
    item_name: string;
    quantity: number;
    unit?: string | null;
    unit_price?: number | null;
    total_price?: number | null;
    spec?: string | null;
    note?: string | null;
    created_by?: number | null;
    updated_by?: number | null;
    created_at: string;
    updated_at: string;
  }
  ```

- [x] **3.5** SectionTemplate 타입 변경 ✅
  - **파일**: `src/contexts/ItemMasterContext.tsx`
  - **예상 시간**: 5분
  - **완료일**: 2025-11-20
  ```typescript
  interface SectionTemplate {
    id: number;
    tenant_id?: number;
    template_name: string;
    section_type: 'BASIC' | 'BOM' | 'CUSTOM';
    description?: string | null;
    default_fields?: Record<string, any> | null;
    created_by?: number | null;
    updated_by?: number | null;
    created_at: string;
    updated_at: string;
  }
  ```

- [x] **3.6** MasterField 타입 변경 ✅
  - **파일**: `src/contexts/ItemMasterContext.tsx`
  - **예상 시간**: 5분
  - **완료일**: 2025-11-20
  ```typescript
  interface ItemMasterField {
    id: number;
    tenant_id?: number;
    field_name: string;
    field_type: 'TEXT' | 'NUMBER' | 'DATE' | 'SELECT' | 'TEXTAREA' | 'CHECKBOX';
    category?: string | null;
    description?: string | null;
    default_validation?: Record<string, any> | null;
    default_properties?: Record<string, any> | null;
    created_by?: number | null;
    updated_by?: number | null;
    created_at: string;
    updated_at: string;
  }
  ```

---

### 🛠️ 4. 헬퍼 함수 준비 (4개)

- [x] **4.1** API 에러 핸들링 헬퍼 함수 생성 ✅
  - **파일**: `src/lib/api/error-handler.ts` (신규)
  - **예상 시간**: 20분
  - **완료일**: 2025-11-20
  ```typescript
  // src/lib/api/error-handler.ts
  export class ApiError extends Error {
    constructor(
      public status: number,
      public message: string,
      public errors?: Record<string, string[]>
    ) {
      super(message);
      this.name = 'ApiError';
    }
  }

  export const handleApiError = async (response: Response): Promise<never> => {
    const data = await response.json().catch(() => ({}));

    throw new ApiError(
      response.status,
      data.message || '서버 오류가 발생했습니다',
      data.errors
    );
  };

  export const getErrorMessage = (error: unknown): string => {
    if (error instanceof ApiError) {
      return error.message;
    }
    if (error instanceof Error) {
      return error.message;
    }
    return '알 수 없는 오류가 발생했습니다';
  };
  ```

- [x] **4.2** API 응답 데이터 변환 헬퍼 함수 생성 ✅
  - **파일**: `src/lib/api/transformers.ts` (신규)
  - **목적**: API 타입 값 변환 (type → section_type, field_type 값 변환 등)
  - **예상 시간**: 20분
  - **완료일**: 2025-11-20
  ```typescript
  // src/lib/api/transformers.ts
  import type { ItemPageResponse, ItemSectionResponse } from '@/types/item-master-api';
  import type { ItemPage, ItemSection } from '@/components/items/ItemMasterDataManagement';

  // API Response → Frontend State
  export const transformPageResponse = (apiPage: ItemPageResponse): ItemPage => ({
    id: apiPage.id,
    tenant_id: apiPage.tenant_id,
    page_name: apiPage.page_name,
    item_type: apiPage.item_type,
    description: apiPage.description,
    absolute_path: apiPage.absolute_path,
    is_active: apiPage.is_active,
    order_no: apiPage.order_no,
    created_by: apiPage.created_by,
    updated_by: apiPage.updated_by,
    created_at: apiPage.created_at,
    updated_at: apiPage.updated_at,
    sections: apiPage.sections?.map(transformSectionResponse),
  });

  export const transformSectionResponse = (apiSection: ItemSectionResponse): ItemSection => ({
    id: apiSection.id,
    tenant_id: apiSection.tenant_id,
    page_id: apiSection.page_id,
    section_template_id: apiSection.section_template_id,
    section_name: apiSection.section_name,
    section_type: apiSection.section_type as 'BASIC' | 'BOM' | 'CUSTOM',
    description: apiSection.description,
    order_no: apiSection.order_no,
    is_collapsible: apiSection.is_collapsible,
    is_default_open: apiSection.is_default_open,
    created_by: apiSection.created_by,
    updated_by: apiSection.updated_by,
    created_at: apiSection.created_at,
    updated_at: apiSection.updated_at,
    fields: apiSection.fields?.map(transformFieldResponse),
    bomItems: apiSection.bomItems?.map(transformBomItemResponse),
  });

  // TODO: transformFieldResponse, transformBomItemResponse 등 추가
  ```

- [x] **4.3** ID 생성 헬퍼 제거 준비 ✅
  - **파일**: `src/components/items/ItemMasterDataManagement.tsx`
  - **예상 시간**: 5분
  - **완료일**: 2025-11-20
  - **작업 내용**: Skip - 별도 ID 생성 함수가 존재하지 않음 (인라인 코드로 구현됨)
  - **비고**: ID 생성은 `\`PAGE-${Date.now()}\`` 형태로 인라인 구현되어 있음. API 연동 시 해당 코드들을 서버 생성 ID로 교체 예정.

- [x] **4.4** 절대 경로 생성 함수 검토 ✅
  - **파일**: `src/components/items/ItemMasterDataManagement.tsx`
  - **예상 시간**: 10분
  - **완료일**: 2025-11-20
  - **결정**: 프론트에서 계속 생성 → API 요청 시 포함 (옵션 A 선택)
  - **함수 위치**: Line 745-755
  ```typescript
  // 현재 함수 (유지)
  const generateAbsolutePath = (itemType: string, pageName: string): string => {
    const typeMap: Record<string, string> = {
      'FG': '제품관리',
      'PT': '부품관리',
      'SM': '부자재관리',
      'RM': '원자재관리',
      'CS': '소모품관리'
    };
    const category = typeMap[itemType] || '기타';
    return `/${category}/${pageName}`;
  };
  ```
  - **이유**: 백엔드가 absolute_path를 자동 생성하는지 불확실하므로, 프론트에서 생성하여 전송하는 것이 안전함. 추후 백엔드에서 자동 생성 시 제거 가능.

---

### 📝 5. 기존 코드 주석 처리 (4개)

- [x] **5.1** localStorage 관련 코드 주석 처리 (삭제 예정 표시) ✅
  - **파일**: `src/components/items/ItemMasterDataManagement.tsx`
  - **예상 시간**: 15분
  - **완료일**: 2025-11-20
  - **작업 내용**: 모든 localStorage 코드에 `// ❌ API 연동 후 삭제 예정 - localStorage 제거` 주석 추가
  - **추가된 주석 위치**:
    - Lines 159-160: Tab loading useEffect
    - Lines 181-182: Tab saving useEffect
    - Lines 350-369: Initial state loading (unitOptions, materialOptions, surfaceTreatmentOptions)

- [x] **5.2** trackChange 함수 주석 추가 ✅
  - **파일**: `src/components/items/ItemMasterDataManagement.tsx`
  - **예상 시간**: 5분
  - **완료일**: 2025-11-20
  - **작업 내용**: trackChange 함수와 pendingChanges 관련 코드에 `// ❌ API 연동 후 삭제 예정 - 실시간 저장으로 변경사항 추적 불필요` 주석 추가
  - **추가된 주석 위치**:
    - Lines 528-529: pendingChanges state 정의
    - Lines 545-546: hasUnsavedChanges computed value
    - Lines 1993-1994: trackChange 함수 정의

- [x] **5.3** SSR 관련 코드 검토 ✅
  - **파일**: `src/components/items/ItemMasterDataManagement.tsx`
  - **예상 시간**: 10분
  - **완료일**: 2025-11-20
  - **작업 내용**: `typeof window !== 'undefined'` 체크가 SSR 호환성을 위한 것임을 확인 (주석 불필요 - 이미 명확함)
  - **검토 결과**: Lines 140-142, 196-203 등에서 SSR 호환성 체크가 적절하게 구현되어 있음

- [x] **5.4** 초기 state 로직 주석 추가 ✅
  - **파일**: `src/components/items/ItemMasterDataManagement.tsx`
  - **예상 시간**: 10분
  - **완료일**: 2025-11-20
  - **작업 내용**: 이미 Task 5.1에서 완료됨 (Lines 350-369에서 unitOptions, materialOptions, surfaceTreatmentOptions 초기 state 로직에 주석 추가)

---

### 🧪 6. 테스트 환경 준비 (3개)

- [x] **6.1** 환경 변수 설정 확인 ✅
  - **파일**: `.env.local`
  - **예상 시간**: 5분
  - **완료일**: 2025-11-20
  - **작업 내용**: API 관련 환경 변수 확인 완료
  - **확인 결과**:
    - ✅ NEXT_PUBLIC_API_URL: https://api.codebridge-x.com
    - ✅ NEXT_PUBLIC_API_KEY: 42Jfwc6EaRQ04GNRmLR5kzJp5UudSOzGGqjmdk1a
  - **비고**: API 연동에 필요한 모든 환경 변수가 이미 설정되어 있음

- [x] **6.2** API Mock 데이터 준비 (선택) ✅
  - **파일**: `src/lib/api/mock-data.ts` (신규, 선택)
  - **목적**: 백엔드 API 구현 전 프론트 개발 계속 진행
  - **예상 시간**: 30분
  - **완료일**: 2025-11-20
  ```typescript
  // src/lib/api/mock-data.ts
  import type { InitResponse } from '@/types/item-master-api';

  export const mockInitData: InitResponse = {
    pages: [
      {
        id: 1,
        tenant_id: 1,
        page_name: '완제품 페이지',
        item_type: 'FG',
        description: null,
        absolute_path: '완제품 > 완제품 페이지',
        is_active: true,
        order_no: 0,
        created_by: null,
        updated_by: null,
        created_at: '2025-11-20T00:00:00Z',
        updated_at: '2025-11-20T00:00:00Z',
      },
    ],
    sections: [],
    fields: [],
    bomItems: [],
    templates: [],
    masterFields: [],
    customTabs: [],
    units: [],
  };

  // Mock API 함수 (개발용)
  export const useMockApi = process.env.NEXT_PUBLIC_USE_MOCK_API === 'true';
  ```

- [x] **6.3** API 호출 로그 유틸 추가 ✅
  - **파일**: `src/lib/api/logger.ts` (신규)
  - **목적**: 개발 중 API 호출 디버깅
  - **예상 시간**: 10분
  - **완료일**: 2025-11-20
  ```typescript
  // src/lib/api/logger.ts
  export const apiLogger = {
    request: (method: string, url: string, data?: any) => {
      if (process.env.NODE_ENV === 'development') {
        console.log(`[API Request] ${method} ${url}`, data);
      }
    },
    response: (method: string, url: string, data: any) => {
      if (process.env.NODE_ENV === 'development') {
        console.log(`[API Response] ${method} ${url}`, data);
      }
    },
    error: (method: string, url: string, error: any) => {
      console.error(`[API Error] ${method} ${url}`, error);
    },
  };
  ```

---

## Phase 1: 초기화 API 연동 (API 필요) ⏳

**백엔드 필요**: `GET /v1/item-master/init` 구현 완료 필요

### 📡 7. 초기 데이터 로딩 (5개)

- [x] **7.1** init API 함수 구현
  - **파일**: `src/lib/api/item-master.ts`
  - **예상 시간**: 20분
  ```typescript
  export const itemMasterApi = {
    init: async (): Promise<InitResponse> => {
      const headers = getAuthHeaders();
      apiLogger.request('GET', '/item-master/init');

      const response = await fetch(`${BASE_URL}/item-master/init`, {
        method: 'GET',
        headers,
      });

      if (!response.ok) {
        await handleApiError(response);
      }

      const result = await response.json();
      apiLogger.response('GET', '/item-master/init', result);

      return result.data;
    },
    // ...
  };
  ```

- [x] **7.2** 컴포넌트 초기 로딩 로직 수정
  - **파일**: `src/components/items/ItemMasterDataManagement.tsx`
  - **예상 시간**: 30분
  ```typescript
  useEffect(() => {
    const loadInitialData = async () => {
      try {
        setIsInitialLoading(true);
        setError(null);

        const data = await itemMasterApi.init();

        setItemPages(data.pages.map(transformPageResponse));
        // TODO: sections, fields, bomItems 등도 설정

      } catch (err) {
        setError(getErrorMessage(err));
      } finally {
        setIsInitialLoading(false);
      }
    };

    loadInitialData();
  }, []);
  ```

- [x] **7.3** 초기 로딩 UI 추가
  - **파일**: `src/components/items/ItemMasterDataManagement.tsx`
  - **예상 시간**: 15분
  ```typescript
  if (isInitialLoading) {
    return (
      <div className="flex items-center justify-center min-h-screen">
        <LoadingSpinner size="lg" text="데이터를 불러오는 중..." />
      </div>
    );
  }

  if (error) {
    return (
      <div className="flex items-center justify-center min-h-screen p-4">
        <ErrorMessage
          message={error}
          onRetry={() => window.location.reload()}
        />
      </div>
    );
  }
  ```

- [x] **7.4** 데이터 변환 및 state 설정
  - **파일**: `src/components/items/ItemMasterDataManagement.tsx`
  - **예상 시간**: 20분
  - **작업 내용**: API 응답 데이터를 프론트 state에 매핑
  ```typescript
  const data = await itemMasterApi.init();

  // 페이지 데이터
  setItemPages(data.pages.map(transformPageResponse));

  // 섹션 템플릿
  setSectionTemplates(data.templates.map(transformTemplateResponse));

  // 마스터 필드
  setItemMasterFields(data.masterFields.map(transformMasterFieldResponse));

  // 커스텀 탭
  setCustomTabs(data.customTabs.map(transformCustomTabResponse));

  // 단위 옵션
  setUnitOptions(data.units.map(transformUnitResponse));
  ```

- [x] **7.5** 초기 로딩 테스트
  - **예상 시간**: 15분
  - **테스트 항목**:
    - [x] API 호출 성공 시 데이터 정상 표시
    - [x] API 호출 실패 시 에러 메시지 표시
    - [x] 로딩 중 스피너 표시
    - [x] 새로고침 시 최신 데이터 로드

---

### 🔐 8. 인증 및 에러 처리 (3개)

- [x] **8.1** 토큰 만료 처리
  - **파일**: `src/lib/api/error-handler.ts`
  - **예상 시간**: 20분
  ```typescript
  export const handleApiError = async (response: Response): Promise<never> => {
    const data = await response.json().catch(() => ({}));

    // 401 Unauthorized - 토큰 만료
    if (response.status === 401) {
      // TODO: 로그인 페이지로 리다이렉트
      if (typeof window !== 'undefined') {
        window.location.href = '/login';
      }
    }

    // 403 Forbidden - 권한 없음
    if (response.status === 403) {
      throw new ApiError(403, '접근 권한이 없습니다', data.errors);
    }

    throw new ApiError(
      response.status,
      data.message || '서버 오류가 발생했습니다',
      data.errors
    );
  };
  ```

- [x] **8.2** 네트워크 오류 처리
  - **파일**: `src/lib/api/item-master.ts`
  - **예상 시간**: 15분
  ```typescript
  try {
    const response = await fetch(url, options);
    // ...
  } catch (error) {
    // 네트워크 오류 (서버 연결 실패 등)
    if (error instanceof TypeError) {
      throw new ApiError(0, '네트워크 연결을 확인해주세요');
    }
    throw error;
  }
  ```

- [x] **8.3** Validation 에러 표시
  - **파일**: `src/components/items/ItemMasterDataManagement.tsx`
  - **예상 시간**: 20분
  ```typescript
  try {
    await itemMasterApi.pages.create(data);
  } catch (error) {
    if (error instanceof ApiError && error.errors) {
      // Validation 에러 (422)
      const errorMessages = Object.entries(error.errors)
        .map(([field, messages]) => `${field}: ${messages.join(', ')}`)
        .join('\n');
      toast.error(errorMessages);
    } else {
      toast.error(getErrorMessage(error));
    }
  }
  ```

---

## Phase 2: CRUD API 연동 (API 필요) ⏳

**백엔드 필요**: 모든 CRUD 엔드포인트 구현 완료 필요

### 📄 9. 페이지 관리 API (5개)

- [x] **9.1** 페이지 생성 API 연동 ✅
  - **파일**: `src/lib/api/item-master.ts` + `ItemMasterDataManagement.tsx`
  - **예상 시간**: 30분
  - **완료일**: 2025-11-21
  ```typescript
  // API Client
  pages: {
    create: async (data: ItemPageRequest): Promise<ItemPageResponse> => {
      const headers = getAuthHeaders();
      const response = await fetch(`${BASE_URL}/item-master/pages`, {
        method: 'POST',
        headers,
        body: JSON.stringify(data),
      });

      if (!response.ok) await handleApiError(response);
      const result = await response.json();
      return result.data;
    },
  }

  // Component
  const addItemPage = async (page: Omit<ItemPage, 'id' | 'created_at' | 'updated_at'>) => {
    try {
      setIsLoading(true);
      const savedPage = await itemMasterApi.pages.create({
        page_name: page.page_name,
        item_type: page.item_type,
        description: page.description,
        is_active: page.is_active,
      });

      setItemPages(prev => [...prev, transformPageResponse(savedPage)]);
      toast.success('페이지가 추가되었습니다');
    } catch (error) {
      toast.error(getErrorMessage(error));
    } finally {
      setIsLoading(false);
    }
  };
  ```

- [x] **9.2** 페이지 수정 API 연동 ✅
  - **예상 시간**: 25분
  - **완료일**: 2025-11-21
  ```typescript
  const updateItemPage = async (id: number, updates: Partial<ItemPage>) => {
    try {
      setIsLoading(true);
      const updatedPage = await itemMasterApi.pages.update(id, {
        page_name: updates.page_name,
        item_type: updates.item_type,
        description: updates.description,
        is_active: updates.is_active,
      });

      setItemPages(prev =>
        prev.map(p => p.id === id ? transformPageResponse(updatedPage) : p)
      );
      toast.success('페이지가 수정되었습니다');
    } catch (error) {
      toast.error(getErrorMessage(error));
    } finally {
      setIsLoading(false);
    }
  };
  ```

- [x] **9.3** 페이지 삭제 API 연동 ✅
  - **예상 시간**: 20분
  - **완료일**: 2025-11-21
  ```typescript
  const deleteItemPage = async (id: number) => {
    if (!confirm('페이지를 삭제하시겠습니까?')) return;

    try {
      setIsLoading(true);
      await itemMasterApi.pages.delete(id);

      setItemPages(prev => prev.filter(p => p.id !== id));
      toast.success('페이지가 삭제되었습니다');
    } catch (error) {
      toast.error(getErrorMessage(error));
    } finally {
      setIsLoading(false);
    }
  };
  ```

- [x] **9.4** 페이지 순서 변경 API 연동 ✅
  - **예상 시간**: 25분
  - **완료일**: 2025-11-21
  - **구현 내용**:
    - `itemMasterApi.pages.reorder()` 함수 구현 완료
    - `ItemMasterContext.reorderPages()` 함수 구현 완료
    - Optimistic UI 업데이트 적용
    - 에러 발생 시 롤백 로직 포함
  ```typescript
  const reorderPages = async (newOrder: Array<{ id: number; order_no: number }>) => {
    try {
      setIsLoading(true);
      await itemMasterApi.pages.reorder({ page_orders: newOrder });

      // Optimistic UI 업데이트
      setItemPages(prev => {
        const updated = [...prev];
        updated.sort((a, b) => {
          const orderA = newOrder.find(o => o.id === a.id)?.order_no ?? 0;
          const orderB = newOrder.find(o => o.id === b.id)?.order_no ?? 0;
          return orderA - orderB;
        });
        return updated;
      });

      toast.success('페이지 순서가 변경되었습니다');
    } catch (error) {
      toast.error(getErrorMessage(error));
      // 실패 시 데이터 다시 로드
      await loadInitialData();
    } finally {
      setIsLoading(false);
    }
  };
  ```

- [x] **9.5** 페이지 관리 테스트 ✅
  - **예상 시간**: 20분
  - **완료일**: 2025-11-21
  - **검증 항목** (코드 수준 검증 완료):
    - [x] API 함수 구현 확인 (create, update, delete, reorder)
    - [x] Context 함수 구현 확인 (addItemPage, updateItemPage, deleteItemPage, reorderPages)
    - [x] 타입 정의 및 import 확인 (PageReorderRequest 타입 추가)
    - [x] 에러 처리 로직 확인 (네트워크 오류, API 오류)
    - [x] Context export 확인 (ItemMasterContextType 및 value)
  - **발견 사항**:
    - PageReorderRequest 타입 import 누락 → 수정 완료 (src/lib/api/item-master.ts:9)
  - **비고**: 실제 백엔드 API 구현 완료 후 E2E 테스트 필요

---

### 📦 10. 섹션 관리 API (5개)

- [x] **10.1** 섹션 생성 API 연동 ✅
  - **예상 시간**: 30분
  - **완료일**: 2025-11-21
  - **구현 내용**: `itemMasterApi.sections.create()` 함수 구현 완료
  - **엔드포인트**: POST `/v1/item-master/pages/{pageId}/sections`

- [x] **10.2** 섹션 수정 API 연동 ✅
  - **예상 시간**: 25분
  - **완료일**: 2025-11-21
  - **구현 내용**: `itemMasterApi.sections.update()` 함수 구현 완료
  - **엔드포인트**: PUT `/v1/item-master/sections/{id}`

- [x] **10.3** 섹션 삭제 API 연동 ✅
  - **예상 시간**: 20분
  - **완료일**: 2025-11-21
  - **구현 내용**: `itemMasterApi.sections.delete()` 함수 구현 완료
  - **엔드포인트**: DELETE `/v1/item-master/sections/{id}`

- [x] **10.4** 섹션 순서 변경 API 연동 ✅
  - **예상 시간**: 25분
  - **완료일**: 2025-11-21
  - **구현 내용**: `itemMasterApi.sections.reorder()` 함수 구현 완료
  - **엔드포인트**: PUT `/v1/item-master/pages/{pageId}/sections/reorder`

- [x] **10.5** 섹션 관리 테스트 ✅
  - **예상 시간**: 20분
  - **완료일**: 2025-11-21
  - **검증 항목** (코드 수준 검증 완료):
    - [x] API 함수 구현 확인 (create, update, delete, reorder)
    - [x] 타입 정의 및 import 확인 (ItemSectionRequest, ItemSectionResponse, SectionReorderRequest)
    - [x] 에러 처리 로직 확인 (네트워크 오류, API 오류)
    - [x] API 엔드포인트 정확성 확인
  - **비고**: 실제 백엔드 API 구현 완료 후 E2E 테스트 필요

---

### 🔤 11. 필드 관리 API (5개)

- [x] **11.1** 필드 생성 API 연동 ✅
  - **예상 시간**: 30분
  - **완료일**: 2025-11-21
  - **구현 내용**: `itemMasterApi.fields.create()` 함수 구현 완료
  - **엔드포인트**: POST `/v1/item-master/sections/{sectionId}/fields`

- [x] **11.2** 필드 수정 API 연동 ✅
  - **예상 시간**: 25분
  - **완료일**: 2025-11-21
  - **구현 내용**: `itemMasterApi.fields.update()` 함수 구현 완료
  - **엔드포인트**: PUT `/v1/item-master/fields/{id}`

- [x] **11.3** 필드 삭제 API 연동 ✅
  - **예상 시간**: 20분
  - **완료일**: 2025-11-21
  - **구현 내용**: `itemMasterApi.fields.delete()` 함수 구현 완료
  - **엔드포인트**: DELETE `/v1/item-master/fields/{id}`

- [x] **11.4** 필드 순서 변경 API 연동 ✅
  - **예상 시간**: 25분
  - **완료일**: 2025-11-21
  - **구현 내용**: `itemMasterApi.fields.reorder()` 함수 구현 완료
  - **엔드포인트**: PUT `/v1/item-master/sections/{sectionId}/fields/reorder`

- [x] **11.5** 필드 관리 테스트 ✅
  - **예상 시간**: 20분
  - **완료일**: 2025-11-21
  - **검증 항목** (코드 수준 검증 완료):
    - [x] API 함수 구현 확인 (create, update, delete, reorder)
    - [x] 타입 정의 및 import 확인 (ItemFieldRequest, ItemFieldResponse, FieldReorderRequest)
    - [x] 에러 처리 로직 확인 (네트워크 오류, API 오류)
    - [x] API 엔드포인트 정확성 확인
  - **비고**: 실제 백엔드 API 구현 완료 후 E2E 테스트 필요

---

### 🏗️ 12. BOM 관리 API (4개)

- [x] **12.1** BOM 항목 생성 API 연동 ✅ (2025-11-21)
  - **예상 시간**: 25분
  - **구현**: `POST /v1/item-master/sections/{sectionId}/bom-items`

- [x] **12.2** BOM 항목 수정 API 연동 ✅ (2025-11-21)
  - **예상 시간**: 20분
  - **구현**: `PUT /v1/item-master/bom-items/{id}`

- [x] **12.3** BOM 항목 삭제 API 연동 ✅ (2025-11-21)
  - **예상 시간**: 15분
  - **구현**: `DELETE /v1/item-master/bom-items/{id}`

- [x] **12.4** BOM 관리 테스트 ✅ (2025-11-21)
  - **예상 시간**: 15분
  - **검증 완료**: 타입 import, API 함수, 엔드포인트, 에러 처리 모두 정상

---

### 📋 13. 섹션 템플릿 API (4개)

- [x] **13.1** 템플릿 목록 조회 (init에 포함되므로 Skip 가능) ✅ (2025-11-21)
  - **예상 시간**: 10분
  - **구현**: `GET /v1/item-master/section-templates`

- [x] **13.2** 템플릿 생성 API 연동 ✅ (2025-11-21)
  - **예상 시간**: 20분
  - **구현**: `POST /v1/item-master/section-templates`

- [x] **13.3** 템플릿 수정 API 연동 ✅ (2025-11-21)
  - **예상 시간**: 20분
  - **구현**: `PUT /v1/item-master/section-templates/{id}`

- [x] **13.4** 템플릿 삭제 API 연동 ✅ (2025-11-21)
  - **예상 시간**: 15분
  - **구현**: `DELETE /v1/item-master/section-templates/{id}`

---

### 🎯 14. 마스터 필드 API (4개)

- [x] **14.1** 마스터 필드 목록 조회 (init에 포함) ✅ (2025-11-21)
  - **예상 시간**: 10분
  - **구현**: `GET /v1/item-master/master-fields`

- [x] **14.2** 마스터 필드 생성 API 연동 ✅ (2025-11-21)
  - **예상 시간**: 20분
  - **구현**: `POST /v1/item-master/master-fields`

- [x] **14.3** 마스터 필드 수정 API 연동 ✅ (2025-11-21)
  - **예상 시간**: 20분
  - **구현**: `PUT /v1/item-master/master-fields/{id}`

- [x] **14.4** 마스터 필드 삭제 API 연동 ✅ (2025-11-21)
  - **예상 시간**: 15분
  - **구현**: `DELETE /v1/item-master/master-fields/{id}`

---

### 📑 15. 커스텀 탭 API (3개)

- [x] **15.1** 커스텀 탭 CRUD API 연동 ✅ (2025-11-21)
  - **예상 시간**: 40분
  - **구현**:
    - `GET /v1/item-master/custom-tabs` (list)
    - `POST /v1/item-master/custom-tabs` (create)
    - `PUT /v1/item-master/custom-tabs/{id}` (update)
    - `DELETE /v1/item-master/custom-tabs/{id}` (delete)

- [x] **15.2** 탭 순서 변경 API 연동 ✅ (2025-11-21)
  - **예상 시간**: 20분
  - **구현**: `PUT /v1/item-master/custom-tabs/reorder`

- [x] **15.3** 탭 컬럼 설정 API 연동 ✅ (2025-11-21)
  - **예상 시간**: 30분
  - **구현**: `PUT /v1/item-master/custom-tabs/{id}/columns`

---

### 📏 16. 단위 옵션 API (3개)

- [x] **16.1** 단위 목록 조회 (init에 포함) ✅ (2025-11-21)
  - **예상 시간**: 10분
  - **구현**: `GET /v1/item-master/unit-options`

- [x] **16.2** 단위 생성 API 연동 ✅ (2025-11-21)
  - **예상 시간**: 15분
  - **구현**: `POST /v1/item-master/unit-options`

- [x] **16.3** 단위 삭제 API 연동 ✅ (2025-11-21)
  - **예상 시간**: 15분
  - **구현**: `DELETE /v1/item-master/unit-options/{id}`

---

## Phase 3: 정리 및 최적화 (API 필요) ⏳

### 🧹 17. 코드 정리 (4개)

- [ ] **17.1** localStorage 관련 코드 완전 삭제
  - **파일**: `src/components/items/ItemMasterDataManagement.tsx`
  - **예상 시간**: 30분
  - **작업 내용**: 주석 처리된 모든 localStorage 코드 제거

- [ ] **17.2** trackChange, pendingChanges 관련 코드 삭제
  - **예상 시간**: 20분

- [ ] **17.3** ID 생성 함수 삭제
  - **예상 시간**: 10분

- [ ] **17.4** 불필요한 import 및 주석 정리
  - **예상 시간**: 15min

---

### 🧪 18. 통합 테스트 (2개)

- [ ] **18.1** 전체 CRUD 흐름 테스트
  - **예상 시간**: 30분
  - **테스트 시나리오**:
    1. 페이지 생성 → 섹션 추가 → 필드 추가
    2. 필드 수정 → 섹션 순서 변경
    3. BOM 섹션 생성 → BOM 항목 추가
    4. 페이지 삭제 (Cascade 확인)

- [ ] **18.2** 에러 케이스 테스트
  - **예상 시간**: 20분
  - **테스트 시나리오**:
    1. 네트워크 끊김 상태에서 작업
    2. 토큰 만료 처리
    3. Validation 에러 표시
    4. 중복 요청 방지

---

## 📝 진행 상황 기록

### Phase 0 완료일
- **시작일**: YYYY-MM-DD
- **완료일**: YYYY-MM-DD
- **실제 소요 시간**: X시간

### Phase 1 완료일
- **시작일**: YYYY-MM-DD
- **완료일**: YYYY-MM-DD
- **실제 소요 시간**: X시간

### Phase 2 완료일
- **시작일**: YYYY-MM-DD
- **완료일**: YYYY-MM-DD
- **실제 소요 시간**: X시간

### Phase 3 완료일
- **시작일**: YYYY-MM-DD
- **완료일**: YYYY-MM-DD
- **실제 소요 시간**: X시간

---

## 🚨 주의사항 및 팁

### 1. 점진적 작업
- Phase 0는 백엔드 API 없이 진행 가능 → 지금 바로 시작
- Phase 1-3은 백엔드 완성 후 순차적으로 진행

### 2. 타입 안정성
- TypeScript strict 모드 유지
- API 응답 타입과 프론트 state 타입 분리
- 변환 함수(transformer) 활용

### 3. 에러 처리
- 모든 API 호출은 try-catch로 감싸기
- 사용자 친화적인 에러 메시지 표시
- 로그 남기기 (개발 환경)

### 4. 성능 최적화
- Optimistic UI 업데이트 활용
- Debounce/Throttle 필요 시 적용 (Phase 2 완료 후)
- React.memo, useMemo 활용 검토

### 5. 테스트
- 각 Phase 완료 후 반드시 테스트
- 실패 케이스 시나리오 확인
- 브라우저 콘솔 에러 체크

---

## 📞 문의 및 이슈

**문서 관련 문의**: 이 체크리스트 기준으로 작업 진행
**백엔드 API 문의**: `[API-2025-11-20] item-master-specification.md` 참조
**이슈 발생 시**: claudedocs에 별도 문서 작성 권장

---

**마지막 업데이트**: 2025-11-20