# 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); } }; ``` ### 2. 고급 사용 (참조시트 + INDIRECT + 조건부 검증) ```tsx ``` ### 3. Props | prop | 타입 | 필수 | 설명 | |------|------|------|------| | `open` | boolean | O | 모달 열림 상태 | | `onOpenChange` | (open: boolean) => void | O | 모달 상태 변경 | | `config` | SmartExcelUploadConfig | O | 전체 설정 | | `referenceData` | Record[] | | 참조시트 데이터 | | `dropdownOptions` | Record | | 드롭다운 옵션 (키: `시트명:컬럼key` 또는 `컬럼key`) | | `itemProcessMappings` | ItemProcessMapping[] | | 마스터-디테일 매핑 데이터 | | `labelToCodeMap` | Record> | | 라벨→코드 변환 | | `extraMeta` | Record | | _meta 시트에 추가할 정보 | | `onUpload` | (data: ParsedSheetData[]) => Promise | 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)으로 범위 조절 가능