Files
sam-react-prod/claudedocs/_API_DESIGN_ITEM_MASTER_CONFIG.md
byeongcheolryu df3db155dd [feat]: Item Master 데이터 관리 기능 구현 및 타입 에러 수정
- ItemMasterDataManagement 컴포넌트 구조화 (tabs, dialogs, components 분리)
- HierarchyTab 타입 에러 수정 (BOMItem section_id, updated_at 추가)
- API 클라이언트 구현 (item-master.ts, 13개 엔드포인트)
- ItemMasterContext 구현 (상태 관리 및 데이터 흐름)
- 백엔드 요구사항 문서 작성 (CORS 설정, API 스펙 등)
- SSR 호환성 수정 (navigator API typeof window 체크)
- 미사용 변수 ESLint 에러 해결
- Context 리팩토링 (AuthContext, RootProvider 추가)
- API 유틸리티 추가 (error-handler, logger, transformers)

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-23 16:10:27 +09:00

26 KiB

품목기준관리 API 설계 문서

작성일: 2025-11-18 목적: 품목기준관리 페이지의 설정 데이터를 서버와 동기화하기 위한 API 구조 설계


📋 목차

  1. 개요
  2. 데이터 구조 분석
  3. API 엔드포인트 설계
  4. 데이터 모델
  5. 저장/불러오기 시나리오
  6. 버전 관리 전략
  7. 에러 처리

개요

테넌트 정보 구조

본 시스템은 로그인 시 받는 실제 테넌트 정보 구조를 기반으로 설계되었습니다.

// 로그인 성공 시 받는 실제 사용자 정보
{
  userId: "TestUser3",
  name: "드미트리",
  tenant: {
    id: 282,                          // ✅ 테넌트 고유 ID (number 타입)
    company_name: "(주)테크컴퍼니",     // 테넌트 회사명
    business_num: "123-45-67890",     // 사업자 번호
    tenant_st_code: "trial"           // 테넌트 상태 코드
  }
}

중요: API 엔드포인트의 {tenantId}는 위 구조의 tenant.id 값(number 타입, 예: 282)을 의미합니다.

시스템 흐름

┌──────────────────────────────┐
│   로그인 (Login)              │
│   tenant.id: 282 (number)   │
└────────┬─────────────────────┘
         │
         ▼
┌──────────────────────────────┐
│   테넌트 (Tenant)             │
│   고유 필드 구성               │
│   tenant.id 기반 격리         │
└────────┬─────────────────────┘
         │
         ▼
┌────────────────────────────────┐
│  품목기준관리 페이지              │
│  (Item Master Config Page)     │
│                                 │
│  - 페이지 구조 설정              │
│  - 섹션 구성                     │
│  - 필드 정의                     │
│  - 마스터 데이터 관리            │
│  - 버전 관리                     │
└────────┬───────────────────────┘
         │ Save (with tenant.id)
         ▼
┌────────────────────────────────┐
│    API Server                   │
│    (Backend)                    │
│                                 │
│  - 테넌트별 데이터 저장          │
│  - tenant.id 검증               │
│  - 버전 관리                     │
│  - 유효성 검증                   │
└────────┬───────────────────────┘
         │ Load (filtered by tenant.id)
         ▼
┌────────────────────────────────┐
│    품목관리 페이지               │
│    (Item Management Page)       │
│                                 │
│  - 설정 기반 동적 폼 생성        │
│  - 실제 품목 데이터 입력         │
└────────────────────────────────┘

핵심 요구사항

  1. 테넌트 격리: 각 테넌트별로 독립적인 설정 (tenant.id 기반 완전 격리)
  2. 계층 구조: Page → Section → Field 3단계 계층
  3. 버전 관리: 설정 변경 이력 추적
  4. 재사용성: 템플릿 기반 섹션/필드 재사용
  5. 동적 생성: 설정 기반 품목관리 페이지 동적 렌더링
  6. 서버 검증: JWT의 tenant.id와 API 요청의 tenantId 일치 검증

데이터 구조 분석

1. 계층 구조 (Hierarchical Structure)

ItemMasterConfig (전체 설정)
│
├─ ItemPage[] (페이지 배열)
│   ├─ id
│   ├─ pageName
│   ├─ itemType (FG/PT/SM/RM/CS)
│   └─ sections[]
│       │
│       ├─ ItemSection (섹션)
│       │   ├─ id
│       │   ├─ title
│       │   ├─ type ('fields' | 'bom')
│       │   ├─ order
│       │   └─ fields[]
│       │       │
│       │       └─ ItemField (필드)
│       │           ├─ id
│       │           ├─ name
│       │           ├─ fieldKey
│       │           ├─ property (ItemFieldProperty)
│       │           └─ displayCondition
│
├─ SectionTemplate[] (재사용 섹션 템플릿)
│
├─ ItemMasterField[] (재사용 필드 템플릿)
│
└─ MasterData (마스터 데이터들)
    ├─ SpecificationMaster[]
    ├─ MaterialItemName[]
    ├─ ItemCategory[]
    ├─ ItemUnit[]
    ├─ ItemMaterial[]
    ├─ SurfaceTreatment[]
    ├─ PartTypeOption[]
    ├─ PartUsageOption[]
    └─ GuideRailOption[]

2. 저장해야 할 데이터 범위

저장 필수 데이터

  1. 페이지 구조 (itemPages)
  2. 섹션 템플릿 (sectionTemplates)
  3. 항목 마스터 (itemMasterFields)
  4. 마스터 데이터 (9가지):
    • 규격 마스터 (specificationMasters)
    • 품목명 마스터 (materialItemNames)
    • 품목 분류 (itemCategories)
    • 단위 (itemUnits)
    • 재질 (itemMaterials)
    • 표면처리 (surfaceTreatments)
    • 부품 유형 옵션 (partTypeOptions)
    • 부품 용도 옵션 (partUsageOptions)
    • 가이드레일 옵션 (guideRailOptions)

저장 불필요 데이터

  • 실제 품목 데이터 (itemMasters) - 별도 API로 관리

API 엔드포인트 설계

Base URL

/api/tenants/{tenantId}/item-master-config

참고: {tenantId}는 로그인 응답의 tenant.id 값(number 타입)입니다. 예: /api/tenants/282/item-master-config

서버 검증 (Server-side Validation)

모든 API 요청에서 다음 검증을 수행해야 합니다:

// Middleware 예시
async function validateTenantAccess(req, res, next) {
  // 1. JWT에서 사용자의 tenant.id 추출
  const userTenantId = req.user.tenant.id;  // number (예: 282)

  // 2. URL 파라미터의 tenantId 추출 및 타입 변환
  const requestedTenantId = parseInt(req.params.tenantId, 10);

  // 3. 일치 검증
  if (userTenantId !== requestedTenantId) {
    return res.status(403).json({
      success: false,
      error: {
        code: "FORBIDDEN",
        message: "접근 권한이 없습니다.",
        details: {
          userTenantId,
          requestedTenantId,
          reason: "테넌트 ID 불일치"
        }
      }
    });
  }

  next();
}

1. 전체 설정 조회 (GET)

엔드포인트

GET /api/tenants/{tenantId}/item-master-config

예시: GET /api/tenants/282/item-master-config

Query Parameters

Parameter Type Required Description
version string No 버전 (기본값: latest)
includeInactive boolean No 비활성 항목 포함 여부 (기본값: false)

Response

{
  "success": true,
  "data": {
    "tenantId": 282,                    // ✅ number 타입
    "version": "1.0",
    "lastUpdated": "2025-11-18T10:30:00Z",
    "updatedBy": "TestUser3",
    "config": {
      // 페이지 구조
      "pages": ItemPage[],

      // 재사용 템플릿
      "sectionTemplates": SectionTemplate[],
      "itemMasterFields": ItemMasterField[],

      // 마스터 데이터
      "masters": {
        "specifications": SpecificationMaster[],
        "materialNames": MaterialItemName[],
        "categories": ItemCategory[],
        "units": ItemUnit[],
        "materials": ItemMaterial[],
        "surfaceTreatments": SurfaceTreatment[],
        "partTypes": PartTypeOption[],
        "partUsages": PartUsageOption[],
        "guideRailOptions": GuideRailOption[]
      }
    }
  }
}

2. 전체 설정 저장 (POST/PUT)

엔드포인트

POST /api/tenants/{tenantId}/item-master-config
PUT /api/tenants/{tenantId}/item-master-config/{version}

Request Body

{
  "version": "1.0",  // 버전 명시 (PUT의 경우 URL의 version과 일치해야 함)
  "comment": "초기 설정 저장",  // 변경 사유 (선택)
  "config": {
    "pages": ItemPage[],
    "sectionTemplates": SectionTemplate[],
    "itemMasterFields": ItemMasterField[],
    "masters": {
      "specifications": SpecificationMaster[],
      "materialNames": MaterialItemName[],
      "categories": ItemCategory[],
      "units": ItemUnit[],
      "materials": ItemMaterial[],
      "surfaceTreatments": SurfaceTreatment[],
      "partTypes": PartTypeOption[],
      "partUsages": PartUsageOption[],
      "guideRailOptions": GuideRailOption[]
    }
  }
}

Response

{
  "success": true,
  "data": {
    "tenantId": 282,                    // ✅ number 타입
    "version": "1.0",
    "savedAt": "2025-11-18T10:30:00Z",
    "savedBy": "TestUser3"
  },
  "message": "설정이 성공적으로 저장되었습니다."
}

3. 특정 페이지 조회 (GET)

엔드포인트

GET /api/tenants/{tenantId}/item-master-config/pages/{pageId}

예시: GET /api/tenants/282/item-master-config/pages/PAGE-001

Response

{
  "success": true,
  "data": {
    "page": ItemPage,
    "metadata": {
      "tenantId": 282,                // ✅ number 타입
      "version": "1.0",
      "lastUpdated": "2025-11-18T10:30:00Z"
    }
  }
}

4. 특정 페이지 업데이트 (PUT)

엔드포인트

PUT /api/tenants/{tenantId}/item-master-config/pages/{pageId}

Request Body

{
  "page": ItemPage,
  "comment": "페이지 구조 변경"
}

5. 페이지 추가 (POST)

엔드포인트

POST /api/tenants/{tenantId}/item-master-config/pages

Request Body

{
  "page": {
    "id": "PAGE-001",
    "pageName": "제품 등록",
    "itemType": "FG",
    "sections": [],
    "isActive": true,
    "createdAt": "2025-11-18T10:30:00Z"
  }
}

6. 섹션 템플릿 관리

엔드포인트

GET    /api/tenants/{tenantId}/item-master-config/section-templates
POST   /api/tenants/{tenantId}/item-master-config/section-templates
PUT    /api/tenants/{tenantId}/item-master-config/section-templates/{templateId}
DELETE /api/tenants/{tenantId}/item-master-config/section-templates/{templateId}

7. 항목 마스터 관리

엔드포인트

GET    /api/tenants/{tenantId}/item-master-config/item-master-fields
POST   /api/tenants/{tenantId}/item-master-config/item-master-fields
PUT    /api/tenants/{tenantId}/item-master-config/item-master-fields/{fieldId}
DELETE /api/tenants/{tenantId}/item-master-config/item-master-fields/{fieldId}

8. 마스터 데이터 관리

각 마스터 데이터별 CRUD API

# 규격 마스터
GET    /api/tenants/{tenantId}/item-master-config/masters/specifications
POST   /api/tenants/{tenantId}/item-master-config/masters/specifications
PUT    /api/tenants/{tenantId}/item-master-config/masters/specifications/{id}
DELETE /api/tenants/{tenantId}/item-master-config/masters/specifications/{id}

# 품목명 마스터
GET    /api/tenants/{tenantId}/item-master-config/masters/material-names
POST   /api/tenants/{tenantId}/item-master-config/masters/material-names
PUT    /api/tenants/{tenantId}/item-master-config/masters/material-names/{id}
DELETE /api/tenants/{tenantId}/item-master-config/masters/material-names/{id}

# ... (나머지 마스터 데이터도 동일 패턴)

데이터 모델

1. ItemMasterConfig (전체 설정)

interface ItemMasterConfig {
  tenantId: number;              // ✅ 테넌트 ID (number 타입, 예: 282)
  version: string;               // 버전 (1.0, 1.1, 2.0...)
  lastUpdated: string;           // 마지막 업데이트 시간 (ISO 8601)
  updatedBy: string;             // 업데이트한 사용자 ID
  comment?: string;              // 변경 사유
  config: {
    pages: ItemPage[];
    sectionTemplates: SectionTemplate[];
    itemMasterFields: ItemMasterField[];
    masters: {
      specifications: SpecificationMaster[];
      materialNames: MaterialItemName[];
      categories: ItemCategory[];
      units: ItemUnit[];
      materials: ItemMaterial[];
      surfaceTreatments: SurfaceTreatment[];
      partTypes: PartTypeOption[];
      partUsages: PartUsageOption[];
      guideRailOptions: GuideRailOption[];
    };
  };
}

2. API Response 공통 형식

성공 응답

interface ApiSuccessResponse<T> {
  success: true;
  data: T;
  message?: string;
  metadata?: {
    total?: number;
    page?: number;
    pageSize?: number;
  };
}

에러 응답

interface ApiErrorResponse {
  success: false;
  error: {
    code: string;           // 에러 코드 (VALIDATION_ERROR, NOT_FOUND 등)
    message: string;        // 사용자용 에러 메시지
    details?: any;          // 상세 에러 정보
    timestamp: string;      // 에러 발생 시간
  };
}

저장/불러오기 시나리오

시나리오 1: 초기 설정 저장

상황: 품목기준관리 페이지에서 처음으로 설정을 저장

// 1. 사용자가 Save 버튼 클릭
// 2. Frontend에서 전체 설정 데이터 준비
const configData = {
  version: "1.0",
  comment: "초기 설정",
  config: {
    pages: itemPages,
    sectionTemplates: sectionTemplates,
    itemMasterFields: itemMasterFields,
    masters: {
      specifications: specificationMasters,
      materialNames: materialItemNames,
      // ... 나머지 마스터 데이터
    }
  }
};

// 3. API 호출
const response = await fetch(`/api/tenants/${tenantId}/item-master-config`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify(configData)
});

// 4. 성공 시 localStorage 업데이트
if (response.ok) {
  localStorage.setItem('mes-itemMasterConfig-version', '1.0');
  localStorage.setItem('mes-itemMasterConfig-lastSync', new Date().toISOString());
}

시나리오 2: 설정 불러오기 (페이지 로드)

상황: 품목기준관리 페이지 접속 시

// 1. 컴포넌트 마운트 시 useEffect
useEffect(() => {
  const loadConfig = async () => {
    try {
      // 2. 서버에서 최신 설정 조회
      const response = await fetch(
        `/api/tenants/${tenantId}/item-master-config?version=latest`
      );

      const { data } = await response.json();

      // 3. Context 상태 업데이트
      setItemPages(data.config.pages);
      setSectionTemplates(data.config.sectionTemplates);
      setItemMasterFields(data.config.itemMasterFields);
      setSpecificationMasters(data.config.masters.specifications);
      // ... 나머지 데이터 설정

      // 4. localStorage에 캐시
      localStorage.setItem('mes-itemMasterConfig', JSON.stringify(data));
      localStorage.setItem('mes-itemMasterConfig-version', data.version);
      localStorage.setItem('mes-itemMasterConfig-lastSync', new Date().toISOString());

    } catch (error) {
      // 5. 에러 시 localStorage 폴백
      const cachedConfig = localStorage.getItem('mes-itemMasterConfig');
      if (cachedConfig) {
        const data = JSON.parse(cachedConfig);
        // ... 캐시된 데이터로 설정
      }
    }
  };

  loadConfig();
}, [tenantId]);

시나리오 3: 특정 항목만 업데이트

상황: 규격 마스터 1개만 추가

// 1. 새 규격 마스터 추가
const newSpec = {
  id: "SPEC-NEW-001",
  specificationCode: "2.0T x 1219 x 2438",
  itemType: "RM",
  // ... 나머지 필드
};

// 2. 부분 업데이트 API 호출
const response = await fetch(
  `/api/tenants/${tenantId}/item-master-config/masters/specifications`,
  {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(newSpec)
  }
);

// 3. Context 상태 업데이트
if (response.ok) {
  addSpecificationMaster(newSpec);
}

시나리오 4: 버전 업그레이드

상황: 기존 설정을 기반으로 새 버전 생성

// 1. 현재 버전 조회
const currentConfig = await fetch(
  `/api/tenants/${tenantId}/item-master-config?version=1.0`
).then(res => res.json());

// 2. 수정사항 반영
const updatedConfig = {
  ...currentConfig.data.config,
  pages: [...currentConfig.data.config.pages, newPage]
};

// 3. 새 버전으로 저장
const response = await fetch(
  `/api/tenants/${tenantId}/item-master-config`,
  {
    method: 'POST',
    body: JSON.stringify({
      version: "1.1",
      comment: "신규 페이지 추가",
      config: updatedConfig
    })
  }
);

버전 관리 전략

1. 버전 네이밍 규칙

{MAJOR}.{MINOR}

MAJOR: 구조적 변경 (페이지 추가/삭제, 필드 타입 변경)
MINOR: 데이터 추가 (마스터 데이터 추가, 섹션 추가)

예시:
1.0 - 초기 버전
1.1 - 마스터 데이터 추가
1.2 - 섹션 추가
2.0 - 페이지 구조 변경

2. 버전 관리 테이블 구조

CREATE TABLE item_master_config_versions (
  id VARCHAR(50) PRIMARY KEY,
  tenant_id BIGINT NOT NULL,           -- ✅ number 타입 (tenant.id와 일치)
  version VARCHAR(10) NOT NULL,
  config JSON NOT NULL,
  comment TEXT,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  created_by VARCHAR(50),
  is_active BOOLEAN DEFAULT TRUE,

  UNIQUE KEY unique_tenant_version (tenant_id, version),
  INDEX idx_tenant_active (tenant_id, is_active),
  FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE
);

참고: tenant_id는 BIGINT 타입으로 정의하여 로그인 응답의 tenant.id(number) 값과 정확히 일치하도록 합니다.

3. 버전 조회 전략

// Latest 버전 조회
GET /api/tenants/{tenantId}/item-master-config?version=latest

// 특정 버전 조회
GET /api/tenants/{tenantId}/item-master-config?version=1.0

// 버전 목록 조회
GET /api/tenants/{tenantId}/item-master-config/versions
// Response:
{
  "versions": [
    { "version": "1.0", "createdAt": "2025-11-01", "comment": "초기 버전" },
    { "version": "1.1", "createdAt": "2025-11-10", "comment": "마스터 데이터 추가" },
    { "version": "2.0", "createdAt": "2025-11-18", "comment": "페이지 구조 변경" }
  ],
  "current": "2.0"
}

에러 처리

1. 에러 코드 정의

Code HTTP Status Description
VALIDATION_ERROR 400 데이터 유효성 검증 실패
UNAUTHORIZED 401 인증 실패
FORBIDDEN 403 권한 없음 (테넌트 접근 권한 없음)
NOT_FOUND 404 설정 또는 버전을 찾을 수 없음
CONFLICT 409 버전 충돌 (이미 존재하는 버전)
VERSION_MISMATCH 409 버전 불일치 (동시 수정 충돌)
SERVER_ERROR 500 서버 내부 오류

2. 에러 응답 예시

Validation Error

{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "입력 데이터가 올바르지 않습니다.",
    "details": {
      "field": "config.pages[0].sections[0].fields[0].property.inputType",
      "message": "inputType은 필수입니다.",
      "value": null
    },
    "timestamp": "2025-11-18T10:30:00Z"
  }
}

Version Conflict

{
  "success": false,
  "error": {
    "code": "CONFLICT",
    "message": "버전 1.0이 이미 존재합니다.",
    "details": {
      "existingVersion": "1.0",
      "suggestedVersion": "1.1"
    },
    "timestamp": "2025-11-18T10:30:00Z"
  }
}

프론트엔드 구현 가이드

1. API 클라이언트 생성

// src/lib/api/itemMasterConfigApi.ts

export const ItemMasterConfigAPI = {
  // 전체 설정 조회
  async getConfig(tenantId: number, version = 'latest') {  // ✅ number 타입
    const response = await fetch(
      `/api/tenants/${tenantId}/item-master-config?version=${version}`
    );
    if (!response.ok) throw new Error('설정 조회 실패');
    return response.json();
  },

  // 전체 설정 저장
  async saveConfig(tenantId: number, config: ItemMasterConfig) {  // ✅ number 타입
    const response = await fetch(
      `/api/tenants/${tenantId}/item-master-config`,
      {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(config)
      }
    );
    if (!response.ok) throw new Error('설정 저장 실패');
    return response.json();
  },

  // 페이지 조회
  async getPage(tenantId: number, pageId: string) {  // ✅ number 타입
    const response = await fetch(
      `/api/tenants/${tenantId}/item-master-config/pages/${pageId}`
    );
    if (!response.ok) throw new Error('페이지 조회 실패');
    return response.json();
  },

  // 규격 마스터 추가
  async addSpecification(tenantId: number, spec: SpecificationMaster) {  // ✅ number 타입
    const response = await fetch(
      `/api/tenants/${tenantId}/item-master-config/masters/specifications`,
      {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(spec)
      }
    );
    if (!response.ok) throw new Error('규격 추가 실패');
    return response.json();
  }
};

사용 예시:

// AuthContext에서 tenant.id를 추출하여 사용
const { user } = useAuth();
const tenantId = user.tenant.id;  // number 타입 (예: 282)

// API 호출
const config = await ItemMasterConfigAPI.getConfig(tenantId);

2. Context 통합

// ItemMasterContext.tsx

// 서버 동기화 함수 추가
const syncWithServer = async () => {
  try {
    const { data } = await ItemMasterConfigAPI.getConfig(tenantId);

    // 모든 상태 업데이트
    setItemPages(data.config.pages);
    setSectionTemplates(data.config.sectionTemplates);
    // ... 나머지 데이터

    // localStorage 캐시
    localStorage.setItem('mes-itemMasterConfig', JSON.stringify(data));
    localStorage.setItem('mes-itemMasterConfig-lastSync', new Date().toISOString());

  } catch (error) {
    console.error('서버 동기화 실패:', error);
    // localStorage 폴백
  }
};

// 저장 함수 추가
const saveToServer = async () => {
  try {
    const configData = {
      version: currentVersion,
      comment: saveComment,
      config: {
        pages: itemPages,
        sectionTemplates: sectionTemplates,
        itemMasterFields: itemMasterFields,
        masters: {
          specifications: specificationMasters,
          materialNames: materialItemNames,
          // ... 나머지
        }
      }
    };

    await ItemMasterConfigAPI.saveConfig(tenantId, configData);

  } catch (error) {
    console.error('저장 실패:', error);
    throw error;
  }
};

다음 단계

Phase 1: API 모킹 (현재)

  1. API 구조 설계 완료
  2. Mock API 서버 구현 (MSW 또는 json-server)
  3. 프론트엔드 API 클라이언트 구현
  4. Context와 API 통합

Phase 2: 백엔드 구현

  1. 데이터베이스 스키마 설계
  2. API 엔드포인트 구현
  3. 인증/권한 처리
  4. 버전 관리 로직 구현

Phase 3: 품목관리 페이지 동적 생성

  1. 설정 기반 폼 렌더러 구현
  2. 조건부 표시 로직 구현
  3. 유효성 검증 구현
  4. 실제 품목 데이터 저장 API 연동

부록

A. localStorage 키 규칙

기존 (tenant.id 없음 - 데이터 오염 위험):

// 테넌트 ID가 없어서 테넌트 전환 시 데이터 오염 발생!
'mes-itemMasterConfig'
'mes-specificationMasters'

권장 (tenant.id 포함 - 완전한 격리):

// 설정 데이터 (tenant.id 포함)
`mes-${tenantId}-itemMasterConfig`         // 예: 'mes-282-itemMasterConfig'
`mes-${tenantId}-itemMasterConfig-version`
`mes-${tenantId}-itemMasterConfig-lastSync`

// 개별 마스터 데이터 (tenant.id + 버전 포함)
`mes-${tenantId}-specificationMasters`     // 예: 'mes-282-specificationMasters'
`mes-${tenantId}-specificationMasters-version`
`mes-${tenantId}-materialItemNames`
`mes-${tenantId}-materialItemNames-version`
`mes-${tenantId}-itemCategories`
`mes-${tenantId}-itemUnits`
`mes-${tenantId}-itemMaterials`
`mes-${tenantId}-surfaceTreatments`
`mes-${tenantId}-partTypeOptions`
`mes-${tenantId}-partUsageOptions`
`mes-${tenantId}-guideRailOptions`

구현 예시:

// TenantAwareCache 클래스 사용 (권장)
// 자세한 구현은 [REF-2025-11-19] multi-tenancy-implementation.md 참조
const cache = new TenantAwareCache(user.tenant.id);
cache.set('itemMasterConfig', configData);

// 또는 직접 구현
const key = `mes-${user.tenant.id}-itemMasterConfig`;  // 'mes-282-itemMasterConfig'
localStorage.setItem(key, JSON.stringify(configData));

테넌트 전환 시 캐시 삭제:

// 로그아웃 또는 테넌트 전환 시
function clearTenantCache(tenantId: number) {
  const keys = Object.keys(localStorage);
  const prefix = `mes-${tenantId}-`;
  keys.forEach(key => {
    if (key.startsWith(prefix)) {
      localStorage.removeItem(key);
    }
  });
}

B. 타입 정의 파일 위치

src/
├─ types/
│  ├─ itemMaster.ts           # 품목 관련 타입
│  ├─ itemMasterConfig.ts     # 설정 관련 타입
│  └─ api.ts                  # API 응답 타입
├─ lib/
│  └─ api/
│     └─ itemMasterConfigApi.ts  # API 클라이언트
└─ contexts/
   └─ ItemMasterContext.tsx   # Context (기존)

문서 버전: 1.0 마지막 업데이트: 2025-11-18