Files
sam-react-prod/claudedocs/item-master/[IMPL-2025-11-20] item-master-api-integration-checklist.md
byeongcheolryu 65a8510c0b fix: 품목기준관리 실시간 동기화 수정
- BOM 항목 추가/수정/삭제 시 섹션탭 즉시 반영
- 섹션 복제 시 UI 즉시 업데이트 (null vs undefined 이슈 해결)
- 항목 수정 기능 추가 (useTemplateManagement)
- 실시간 동기화 문서 추가

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-27 22:19:50 +09:00

50 KiB

품목기준관리 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 - 페이지 생성
    { "page_name": "string", "item_type": "FG|PT|SM|RM|CS", "absolute_path": "string?" }
    
  • PUT /item-master/pages/{id} - 페이지 수정
    { "page_name": "string", "absolute_path": "string?" }
    
  • DELETE /item-master/pages/{id} - 페이지 삭제 (Cascade)

섹션 관리

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

필드 관리

  • POST /item-master/sections/{sectionId}/fields - 필드 생성
    {
      "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 항목 생성
    {
      "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 - 단위 옵션 생성
    { "label": "개", "value": "EA" }
    
  • DELETE /item-master/unit-options/{id} - 단위 옵션 삭제

응답 형식

// 성공 응답
{
  "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개)

  • 1.1 src/lib/api/item-master.ts API Client 파일 생성

    • 목적: 모든 API 호출 함수 중앙 관리
    • 예상 시간: 30분
    • 완료 조건: 빈 파일에 기본 구조 작성
    • 완료일: 2025-11-20
    // 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 */ },
      },
    };
    
  • 1.2 src/lib/api/auth-headers.ts 인증 헤더 유틸 생성

    • 목적: 모든 API 요청에 인증 헤더 자동 추가
    • 예상 시간: 15분
    • 완료 조건: getAuthHeaders 함수 구현
    • 완료일: 2025-11-20
    // 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)
      };
    };
    
  • 1.3 src/types/item-master-api.ts API 타입 정의 파일 생성

    • 목적: Request/Response 타입 분리 및 명확화
    • 예상 시간: 45분
    • 완료 조건: 모든 API 엔드포인트의 Request/Response 타입 정의
    • 완료일: 2025-11-20
    // 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개)

  • 2.1 src/components/ui/loading-spinner.tsx 로딩 스피너 컴포넌트 생성

    • 목적: API 호출 중 로딩 상태 표시
    • 예상 시간: 15분
    • 완료 조건: 재사용 가능한 로딩 스피너 컴포넌트
    • 완료일: 2025-11-20
    // 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>
      );
    };
    
  • 2.2 src/components/ui/error-message.tsx 에러 메시지 컴포넌트 생성

    • 목적: API 오류 메시지 일관된 UI로 표시
    • 예상 시간: 15분
    • 완료 조건: 재사용 가능한 에러 메시지 컴포넌트
    • 완료일: 2025-11-20
    // 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>
      );
    };
    
  • 2.3 src/components/items/ItemMasterDataManagement.tsx에 로딩/에러 state 추가

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

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

  • 3.1 ItemPage 타입 변경 (ID: string → number)

    • 파일: src/contexts/ItemMasterContext.tsx
    • 예상 시간: 10분
    • 완료일: 2025-11-20
    • 작업 내용:
      • 기존 id: stringid: number
      • absolutePathabsolute_path
      • createdAtcreated_at, updated_at 추가
    // 기존 타입 (주석 처리)
    // 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 데이터
    }
    
  • 3.2 ItemSection 타입 변경

    • 파일: src/contexts/ItemMasterContext.tsx
    • 예상 시간: 10분
    • 완료일: 2025-11-20
    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[];
    }
    
  • 3.3 ItemField 타입 변경

    • 파일: src/contexts/ItemMasterContext.tsx
    • 예상 시간: 10분
    • 완료일: 2025-11-20
    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;
    }
    
  • 3.4 BomItem 타입 변경

    • 파일: src/contexts/ItemMasterContext.tsx
    • 예상 시간: 10분
    • 완료일: 2025-11-20
    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;
    }
    
  • 3.5 SectionTemplate 타입 변경

    • 파일: src/contexts/ItemMasterContext.tsx
    • 예상 시간: 5분
    • 완료일: 2025-11-20
    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;
    }
    
  • 3.6 MasterField 타입 변경

    • 파일: src/contexts/ItemMasterContext.tsx
    • 예상 시간: 5분
    • 완료일: 2025-11-20
    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개)

  • 4.1 API 에러 핸들링 헬퍼 함수 생성

    • 파일: src/lib/api/error-handler.ts (신규)
    • 예상 시간: 20분
    • 완료일: 2025-11-20
    // 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 '알 수 없는 오류가 발생했습니다';
    };
    
  • 4.2 API 응답 데이터 변환 헬퍼 함수 생성

    • 파일: src/lib/api/transformers.ts (신규)
    • 목적: API 타입 값 변환 (type → section_type, field_type 값 변환 등)
    • 예상 시간: 20분
    • 완료일: 2025-11-20
    // 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 등 추가
    
  • 4.3 ID 생성 헬퍼 제거 준비

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

    • 파일: src/components/items/ItemMasterDataManagement.tsx
    • 예상 시간: 10분
    • 완료일: 2025-11-20
    • 결정: 프론트에서 계속 생성 → API 요청 시 포함 (옵션 A 선택)
    • 함수 위치: Line 745-755
    // 현재 함수 (유지)
    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개)

  • 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)
  • 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 함수 정의
  • 5.3 SSR 관련 코드 검토

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

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

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

  • 6.1 환경 변수 설정 확인

    • 파일: .env.local
    • 예상 시간: 5분
    • 완료일: 2025-11-20
    • 작업 내용: API 관련 환경 변수 확인 완료
    • 확인 결과:
    • 비고: API 연동에 필요한 모든 환경 변수가 이미 설정되어 있음
  • 6.2 API Mock 데이터 준비 (선택)

    • 파일: src/lib/api/mock-data.ts (신규, 선택)
    • 목적: 백엔드 API 구현 전 프론트 개발 계속 진행
    • 예상 시간: 30분
    • 완료일: 2025-11-20
    // 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';
    
  • 6.3 API 호출 로그 유틸 추가

    • 파일: src/lib/api/logger.ts (신규)
    • 목적: 개발 중 API 호출 디버깅
    • 예상 시간: 10분
    • 완료일: 2025-11-20
    // 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개)

  • 7.1 init API 함수 구현

    • 파일: src/lib/api/item-master.ts
    • 예상 시간: 20분
    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;
      },
      // ...
    };
    
  • 7.2 컴포넌트 초기 로딩 로직 수정

    • 파일: src/components/items/ItemMasterDataManagement.tsx
    • 예상 시간: 30분
    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();
    }, []);
    
  • 7.3 초기 로딩 UI 추가

    • 파일: src/components/items/ItemMasterDataManagement.tsx
    • 예상 시간: 15분
    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>
      );
    }
    
  • 7.4 데이터 변환 및 state 설정

    • 파일: src/components/items/ItemMasterDataManagement.tsx
    • 예상 시간: 20분
    • 작업 내용: API 응답 데이터를 프론트 state에 매핑
    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));
    
  • 7.5 초기 로딩 테스트

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

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

  • 8.1 토큰 만료 처리

    • 파일: src/lib/api/error-handler.ts
    • 예상 시간: 20분
    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
      );
    };
    
  • 8.2 네트워크 오류 처리

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

    • 파일: src/components/items/ItemMasterDataManagement.tsx
    • 예상 시간: 20분
    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개)

  • 9.1 페이지 생성 API 연동

    • 파일: src/lib/api/item-master.ts + ItemMasterDataManagement.tsx
    • 예상 시간: 30분
    • 완료일: 2025-11-21
    // 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);
      }
    };
    
  • 9.2 페이지 수정 API 연동

    • 예상 시간: 25분
    • 완료일: 2025-11-21
    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);
      }
    };
    
  • 9.3 페이지 삭제 API 연동

    • 예상 시간: 20분
    • 완료일: 2025-11-21
    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);
      }
    };
    
  • 9.4 페이지 순서 변경 API 연동

    • 예상 시간: 25분
    • 완료일: 2025-11-21
    • 구현 내용:
      • itemMasterApi.pages.reorder() 함수 구현 완료
      • ItemMasterContext.reorderPages() 함수 구현 완료
      • Optimistic UI 업데이트 적용
      • 에러 발생 시 롤백 로직 포함
    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);
      }
    };
    
  • 9.5 페이지 관리 테스트

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

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

  • 10.1 섹션 생성 API 연동

    • 예상 시간: 30분
    • 완료일: 2025-11-21
    • 구현 내용: itemMasterApi.sections.create() 함수 구현 완료
    • 엔드포인트: POST /v1/item-master/pages/{pageId}/sections
  • 10.2 섹션 수정 API 연동

    • 예상 시간: 25분
    • 완료일: 2025-11-21
    • 구현 내용: itemMasterApi.sections.update() 함수 구현 완료
    • 엔드포인트: PUT /v1/item-master/sections/{id}
  • 10.3 섹션 삭제 API 연동

    • 예상 시간: 20분
    • 완료일: 2025-11-21
    • 구현 내용: itemMasterApi.sections.delete() 함수 구현 완료
    • 엔드포인트: DELETE /v1/item-master/sections/{id}
  • 10.4 섹션 순서 변경 API 연동

    • 예상 시간: 25분
    • 완료일: 2025-11-21
    • 구현 내용: itemMasterApi.sections.reorder() 함수 구현 완료
    • 엔드포인트: PUT /v1/item-master/pages/{pageId}/sections/reorder
  • 10.5 섹션 관리 테스트

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

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

  • 11.1 필드 생성 API 연동

    • 예상 시간: 30분
    • 완료일: 2025-11-21
    • 구현 내용: itemMasterApi.fields.create() 함수 구현 완료
    • 엔드포인트: POST /v1/item-master/sections/{sectionId}/fields
  • 11.2 필드 수정 API 연동

    • 예상 시간: 25분
    • 완료일: 2025-11-21
    • 구현 내용: itemMasterApi.fields.update() 함수 구현 완료
    • 엔드포인트: PUT /v1/item-master/fields/{id}
  • 11.3 필드 삭제 API 연동

    • 예상 시간: 20분
    • 완료일: 2025-11-21
    • 구현 내용: itemMasterApi.fields.delete() 함수 구현 완료
    • 엔드포인트: DELETE /v1/item-master/fields/{id}
  • 11.4 필드 순서 변경 API 연동

    • 예상 시간: 25분
    • 완료일: 2025-11-21
    • 구현 내용: itemMasterApi.fields.reorder() 함수 구현 완료
    • 엔드포인트: PUT /v1/item-master/sections/{sectionId}/fields/reorder
  • 11.5 필드 관리 테스트

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

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

  • 12.1 BOM 항목 생성 API 연동 (2025-11-21)

    • 예상 시간: 25분
    • 구현: POST /v1/item-master/sections/{sectionId}/bom-items
  • 12.2 BOM 항목 수정 API 연동 (2025-11-21)

    • 예상 시간: 20분
    • 구현: PUT /v1/item-master/bom-items/{id}
  • 12.3 BOM 항목 삭제 API 연동 (2025-11-21)

    • 예상 시간: 15분
    • 구현: DELETE /v1/item-master/bom-items/{id}
  • 12.4 BOM 관리 테스트 (2025-11-21)

    • 예상 시간: 15분
    • 검증 완료: 타입 import, API 함수, 엔드포인트, 에러 처리 모두 정상

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

  • 13.1 템플릿 목록 조회 (init에 포함되므로 Skip 가능) (2025-11-21)

    • 예상 시간: 10분
    • 구현: GET /v1/item-master/section-templates
  • 13.2 템플릿 생성 API 연동 (2025-11-21)

    • 예상 시간: 20분
    • 구현: POST /v1/item-master/section-templates
  • 13.3 템플릿 수정 API 연동 (2025-11-21)

    • 예상 시간: 20분
    • 구현: PUT /v1/item-master/section-templates/{id}
  • 13.4 템플릿 삭제 API 연동 (2025-11-21)

    • 예상 시간: 15분
    • 구현: DELETE /v1/item-master/section-templates/{id}

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

  • 14.1 마스터 필드 목록 조회 (init에 포함) (2025-11-21)

    • 예상 시간: 10분
    • 구현: GET /v1/item-master/master-fields
  • 14.2 마스터 필드 생성 API 연동 (2025-11-21)

    • 예상 시간: 20분
    • 구현: POST /v1/item-master/master-fields
  • 14.3 마스터 필드 수정 API 연동 (2025-11-21)

    • 예상 시간: 20분
    • 구현: PUT /v1/item-master/master-fields/{id}
  • 14.4 마스터 필드 삭제 API 연동 (2025-11-21)

    • 예상 시간: 15분
    • 구현: DELETE /v1/item-master/master-fields/{id}

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

  • 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)
  • 15.2 탭 순서 변경 API 연동 (2025-11-21)

    • 예상 시간: 20분
    • 구현: PUT /v1/item-master/custom-tabs/reorder
  • 15.3 탭 컬럼 설정 API 연동 (2025-11-21)

    • 예상 시간: 30분
    • 구현: PUT /v1/item-master/custom-tabs/{id}/columns

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

  • 16.1 단위 목록 조회 (init에 포함) (2025-11-21)

    • 예상 시간: 10분
    • 구현: GET /v1/item-master/unit-options
  • 16.2 단위 생성 API 연동 (2025-11-21)

    • 예상 시간: 15분
    • 구현: POST /v1/item-master/unit-options
  • 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