# SmartExcelUpload

설정(Config) 기반 엑셀 업로드 공통 모듈. Config 객체와 데이터를 넘기면 템플릿 생성, 업로드, 검증, 미리보기까지 자동 처리된다. 화면별로 Config 정의 + 데이터 조회 + 저장 콜백만 작성하면 어디든 적용 가능.

## 기존 ExcelUploadModal과의 차이

기존 `ExcelUploadModal`은 단일 테이블의 단순 데이터를 일괄 업로드하는 용도(거래처 목록 등).

`SmartExcelUpload`는 아래와 같이 **기존 컴포넌트로 처리하기 어려운 복잡한 구조**에서 사용한다.

| 상황 | 예시 |
|------|------|
| 셀 간 연동이 필요할 때 | A 컬럼 선택 → B 컬럼 자동 입력 |
| 선택한 값에 따라 드롭다운이 달라질 때 | 마스터 데이터별로 선택 가능한 하위 항목이 다름 |
| 조건에 따라 입력 가능/불가가 바뀔 때 | 특정 유형일 때만 특정 컬럼 입력 가능 |
| 멀티 시트로 유형을 구분할 때 | 유형별 시트 분리 |
| 참조 데이터 기반 자동 검증이 필요할 때 | 기준 데이터 변경 시 템플릿 재다운로드 유도 (해시 검증) |
| 1:N 관계의 데이터를 등록할 때 | 마스터 1개에 디테일 N개, 유형 다수 |

단순 일괄 등록은 기존 `ExcelUploadModal`을 쓰면 된다.

> **참고**: 셀 간 연동 수식(VLOOKUP, INDEX/MATCH 등)은 화면마다 다르다. SmartExcelUpload가 제공하는 것은 수식 자체가 아니라 **수식을 적용하는 메커니즘**(autoFill, customFormula, INDIRECT 등)이다. 어떤 컬럼에 어떤 수식이 들어갈지는 Config에서 화면별로 정의한다.

## 파일 구조

```
SmartExcelUpload/
  index.ts                  # export
  types.ts                  # Config 인터페이스, 검증 타입
  templateGenerator.ts      # ExcelJS 기반 엑셀 템플릿 생성
  templateParser.ts         # 업로드 파일 파싱 + 해시 검증 + 데이터 검증
  SmartExcelUploadModal.tsx  # 모달 UI (다운로드 → 업로드 → 검증 → 미리보기)
```

## 핵심 개념

### Config 기반 동작

모든 동작은 `SmartExcelUploadConfig`로 결정된다.

```typescript
const config: SmartExcelUploadConfig = {
  templateName: "파일명",
  sheets: [...],              // 시트 정의 (단일/멀티)
  referenceSheet: {...},      // 참조 데이터 숨김시트 (선택)
  conditionalRules: [...],    // 조건부 검증 규칙 (선택)
  indirectOptions: {...},     // ACC_ 동적 드롭다운 옵션 정의 (선택)
};
```

### 엑셀 템플릿 구조

```
[시트: 안내]              ← Config 기반 자동 생성 (컬럼 설명, 입력 규칙, 사용법)
[시트: 데이터1]           ← 사용자가 작성하는 시트 (여러 개 가능)
[시트: 데이터2]           
[숨김: 참조시트]          ← VLOOKUP 참조 데이터 (referenceSheet 설정 시)
[숨김: _품목공정]         ← INDIRECT 이름 범위 (itemProcessMappings 설정 시)
[숨김: _품목목록]         ← 마스터 드롭다운 소스 (itemProcessMappings 설정 시)
[숨김: _합격기준옵션]     ← INDIRECT ACC_ 이름 범위 (indirectOptions 설정 시)
[숨김: _meta]             ← 버전 해시, 생성일
```

숨김시트들은 해당 기능을 사용하는 Config일 때만 생성된다.

### 버전 해시 검증

- 템플릿 생성 시: 참조 데이터 + 드롭다운 옵션 + 매핑 데이터 → 해시 생성 → `_meta` 시트에 저장
- 업로드 시: 현재 DB 데이터로 해시 재생성 → 일치 여부 확인
- 불일치 시: "기준 데이터가 변경되었습니다. 최신 템플릿을 다시 다운로드해주세요" 경고

### 안내시트 자동 생성

Config의 컬럼 정의를 기반으로 안내시트가 자동 생성된다.

- 컬럼별 설명 (필수 여부, 자동 입력 여부, 드롭다운 유형 등)
- 조건부 규칙 설명 (conditionalRules 기반)
- 잠금 셀 목록 (autoFill/readOnly/customFormula 컬럼)
- 사용 방법 단계

---

## 컬럼 타입

### 기본 타입

| type | 설명 |
|------|------|
| `text` | 자유 텍스트 |
| `number` | 숫자 (천단위 서식 자동 적용) |
| `date` | 날짜 |
| `dropdown` | 드롭다운 선택 |

### 드롭다운 source 유형

| source | 설명 | 예시 |
|--------|------|------|
| `custom` | 고정 값 목록 | `values: ["Y", "N"]` |
| `category` | 카테고리 테이블 조회 | `tableName: "...", columnName: "..."` |
| `indirect` | 다른 셀 값에 따라 동적 변경 | `indirectKeyColumn: "...", indirectPrefix: "P_"` |

### 컬럼 속성

| 속성 | 설명 |
|------|------|
| `required` | 필수 여부 — 업로드 검증 시 빈 값 체크 |
| `readOnly` | 읽기전용 — 셀 잠금 |
| `autoFill` | 참조시트에서 VLOOKUP 자동 입력 — 셀 잠금, 회색 배경 |
| `customFormula` | 커스텀 엑셀 수식 — `{col:key}` 플레이스홀더로 같은 행 참조 |
| `enableWhen` | 조건부 활성화 — 참조시트에서 직접 조회하여 판단 (VLOOKUP 미계산 문제 없음) |
| `disableWhen` | 조건부 비활성화 — 특정 조건일 때 입력 차단 |
| `width` | 컬럼 너비 (기본 18) |

---

## 주요 기능

### 1. VLOOKUP 자동 입력 (autoFill)

참조시트의 데이터를 기반으로 다른 셀 값에 연동되어 자동 입력된다.

```typescript
{
  key: "detail_column",
  label: "상세정보",
  readOnly: true,
  autoFill: {
    lookupColumn: "master_key",       // 같은 행의 이 컬럼 값을 기준으로
    referenceColumn: "detail",        // 참조시트에서 이 컬럼 값을 가져옴
  }
}
```

### 2. 커스텀 수식 (customFormula)

`{col:key}` 플레이스홀더를 사용하여 같은 행의 다른 컬럼을 참조하는 수식을 정의한다. 절대참조(`$`)는 행 치환에서 자동 보호된다.

```typescript
{
  key: "code_column",
  readOnly: true,
  customFormula: `IFERROR(INDEX('_시트명'!$A$1:$A$9999,MATCH({col:name_column},'_시트명'!$B$1:$B$9999,0)),"")`
}
```

### 3. INDIRECT 동적 드롭다운

다른 셀 값에 따라 드롭다운 옵션이 동적으로 변경된다.

**P_ prefix (이름 범위 직접 참조):**
```typescript
{
  key: "sub_item",
  type: "dropdown",
  dropdown: {
    source: "indirect",
    indirectKeyColumn: "master_code",   // 이 컬럼 값을 기준으로
    indirectPrefix: "P_",               // P_{값} 이름 범위 참조
  }
}
```

**ACC_ prefix (MATCH 인덱스 기반 참조):**
```typescript
{
  key: "criteria_value",
  type: "dropdown",
  dropdown: {
    source: "indirect",
    indirectKeyColumn: "standard_key",
    indirectPrefix: "ACC_",             // ACC_{인덱스} — MATCH로 인덱스 조회
  }
}
```

ACC_ prefix 사용 시 Config에 `indirectOptions` 설정 필요:
```typescript
indirectOptions: {
  conditionColumn: "condition_type",           // 참조시트에서 조건 판단할 컬럼
  optionsByCondition: { "타입A": ["O", "X"] }, // 조건값별 고정 옵션
  selectionOptionsColumn: "options_column",     // 동적 옵션 (콤마 구분 문자열)
}
```

### 4. 조건부 활성화/비활성화

다른 컬럼 값에 따라 셀 입력 가능 여부가 결정된다. 참조시트에서 직접 조회하는 방식이라 VLOOKUP 미계산 문제가 없다.

```typescript
{ key: "value_a", type: "number", enableWhen: { column: "condition_col", equals: "특정값" } }
```

### 5. 조건부 검증 규칙

업로드 시 특정 조건에 따라 필수/무시 컬럼이 달라진다. autoFill 컬럼의 값도 참조데이터에서 직접 조회하여 조건 판단.

```typescript
conditionalRules: [
  {
    when: { column: "condition_col", equals: "타입A" },
    require: ["required_col"],     // 필수
    ignore: ["optional_col"],      // 무시
  },
]
```

### 6. ItemProcessMapping (마스터-디테일 매핑)

마스터 항목별로 선택 가능한 하위 항목이 다를 때 사용. 벌크 API로 전체 데이터를 한 번에 조회하여 INDIRECT 이름 범위로 등록.

```typescript
itemProcessMappings: [
  { itemCode: "M-001", itemName: "마스터A", processes: [{ code: "S01", name: "하위1" }] },
  { itemCode: "M-002", itemName: "마스터B", processes: [{ code: "S02", name: "하위2" }, { code: "S03", name: "하위3" }] },
]
```

업로드 검증 시 마스터에 맞지 않는 하위 항목은 자동으로 에러 처리된다.

---

## 성능 구조

| 항목 | 방식 | 범위 |
|------|------|------|
| 드롭다운/validation | **컬럼 범위 1회** 설정 | 65,000행 |
| 수식 (VLOOKUP, customFormula) | 행별 개별 삽입 | 2,000행 (FORMULA_END) |
| 셀 보호 (잠금/해제) | 행별 개별 설정 | 2,000행 |
| 셀 스타일 (배경, 테두리) | 행별 개별 설정 | 2,000행 |
| 데이터 캐싱 | 최초 로드 후 재사용 | 페이지 세션 |

드롭다운은 범위 단위라 행 수 제한 없음. 수식/스타일은 `FORMULA_END` 상수로 조절 가능.

---

## 사용법

### 1. 기본 사용 (단순 드롭다운만)

```tsx
import { SmartExcelUploadModal } from "@/components/common/SmartExcelUpload";
import type { SmartExcelUploadConfig, ParsedSheetData } from "@/components/common/SmartExcelUpload";

const config: SmartExcelUploadConfig = {
  templateName: "거래처",
  sheets: [{
    name: "거래처",
    columns: [
      { key: "name", label: "거래처명", required: true, type: "text", width: 24 },
      { key: "division", label: "구분", type: "dropdown",
        dropdown: { source: "custom", values: ["매출처", "매입처"] } },
    ],
  }],
};

const handleUpload = async (data: ParsedSheetData[]) => {
  for (const sheet of data) {
    for (const row of sheet.rows) await api.create(row);
  }
};

<SmartExcelUploadModal
  open={open}
  onOpenChange={setOpen}
  config={config}
  dropdownOptions={{ division: ["매출처", "매입처"] }}
  onUpload={handleUpload}
/>
```

### 2. 고급 사용 (참조시트 + INDIRECT + 조건부 검증)

```tsx
<SmartExcelUploadModal
  open={open}
  onOpenChange={setOpen}
  config={config}                          // 시트/컬럼/규칙 정의
  referenceData={refData}                  // 참조시트 데이터
  dropdownOptions={dropdownOpts}           // 드롭다운 옵션
  itemProcessMappings={processMappings}    // 마스터-디테일 매핑
  labelToCodeMap={labelMap}                // 라벨→코드 변환
  onUpload={handleUpload}                  // 저장 콜백
  dataLoading={loading}                    // 로딩 상태
  loadProgress={{ loaded: 100, total: 500 }} // 진행률
/>
```

### 3. Props

| prop | 타입 | 필수 | 설명 |
|------|------|------|------|
| `open` | boolean | O | 모달 열림 상태 |
| `onOpenChange` | (open: boolean) => void | O | 모달 상태 변경 |
| `config` | SmartExcelUploadConfig | O | 전체 설정 |
| `referenceData` | Record[] | | 참조시트 데이터 |
| `dropdownOptions` | Record<string, string[]> | | 드롭다운 옵션 (키: `시트명:컬럼key` 또는 `컬럼key`) |
| `itemProcessMappings` | ItemProcessMapping[] | | 마스터-디테일 매핑 데이터 |
| `labelToCodeMap` | Record<string, Record<string, string>> | | 라벨→코드 변환 |
| `extraMeta` | Record<string, string> | | _meta 시트에 추가할 정보 |
| `onUpload` | (data: ParsedSheetData[]) => Promise<void> | O | 업로드 완료 콜백 |
| `subtitle` | string | | 제목 아래 부가 설명 |
| `dataLoading` | boolean | | 외부 데이터 로딩 중 표시 |
| `loadProgress` | { loaded, total } | | 로딩 진행률 표시 |

### 4. 벌크 조회 API (백엔드)

마스터별 하위 항목을 한 번에 조회하는 API. 5,000건 단위 청크 분할로 대량 데이터 대응.

```
POST /work-instruction/routing-versions-bulk
Body: { itemCodes: ["M-001", "M-002", ...] }
Response: { success: true, data: { "M-001": [{ code, name }], "M-002": [...] } }
```

---

## 검증 흐름

```
업로드 → 메타 해시 검증 → 시트별 파싱 (사용자 입력 컬럼만 빈 행 체크)
  → 필수값 검증 (conditionalRules 적용, autoFill 값은 참조데이터에서 직접 조회)
  → 드롭다운 유효성 검증
  → INDIRECT 매핑 검증 (마스터에 맞지 않는 하위 항목 에러)
  → 에러 있으면 에러 리포트 / 없으면 미리보기 → 저장
```

---

## 확장 시 참고

- 새 화면에 적용할 때: Config 정의 + 데이터 조회 + 저장 콜백만 작성
- 단일 시트 / 멀티 시트 모두 지원 (`sheets` 배열 크기로 결정)
- 참조시트 필요 없으면 `referenceSheet` 생략 → 숨김시트 미생성
- 조건부 검증 필요 없으면 `conditionalRules` 생략 → 단순 필수값 체크만
- INDIRECT 필요 없으면 `itemProcessMappings` 생략 → 일반 드롭다운만
- ACC_ 동적 드롭다운 필요 없으면 `indirectOptions` 생략 → 해당 시트 미생성
- `FORMULA_END` (기본 2,000) / `VALIDATION_END` (기본 65,000)으로 범위 조절 가능
