# HNS 물질 Import 파이프라인 `C:\Projects\MeterialDB\유해물질 화물적부도 검색툴.xlsm` 외부 자료를 `HNS_SUBSTANCE` DB로 변환하는 1회성 파이프라인. ## 파이프라인 구조 ``` [Excel xlsm] ├─ (1) extract-excel.py → out/base.json (1,345종 기본 정보) └─ (2) extract-images.py → out/images/*.png (225종 상세 카드 이미지) ↓ (3) ocr-images.ts → out/ocr.json (Claude Vision → 물성/위험도/EmS JSON) ↓ (4) merge-data.ts → frontend/src/data/hnsSubstanceData.json ↓ (5) tsx src/db/seedHns.ts → HNS_SUBSTANCE 테이블 ``` ## 전제 조건 - Python 3.9+ with `openpyxl` - Node.js 20 - `ANTHROPIC_API_KEY` 환경변수 (Claude Vision API) - Excel 원본 파일: `C:\Projects\MeterialDB\유해물질 화물적부도 검색툴.xlsm` ## 실행 순서 ### 1) Excel 메타 시트 파싱 ```bash cd backend python scripts/hns-import/extract-excel.py ``` - 입력: `C:\Projects\MeterialDB\유해물질 화물적부도 검색툴.xlsm` - 처리 시트: `화물적부도 화물코드`(1,345개), `동의어`(215개), `IBC CODE`(분류) - 출력: `scripts/hns-import/out/base.json` ### 2) 이미지 225개 추출 ```bash python scripts/hns-import/extract-images.py ``` - 출력: - `scripts/hns-import/out/images/{nameKr}.png` - `scripts/hns-import/out/image-map.json` (파일명↔시트명 매핑) ### 3) Claude Vision OCR ```bash export ANTHROPIC_API_KEY="sk-ant-..." cd backend npx tsx scripts/hns-import/ocr-images.ts ``` - 이미지 한 장당 Claude API 1회 호출 - 동시 5개 병렬, 실패 시 3회 재시도 - 출력: `scripts/hns-import/out/ocr.json` `{ [nameKr]: OcrResult }` ### 4) 최종 JSON 병합 ```bash npx tsx scripts/hns-import/merge-data.ts ``` - 입력: `out/base.json` + `out/ocr.json` - 출력: `frontend/src/data/hnsSubstanceData.json` (전량 덮어쓰기) ### 5) DB 재시드 ```bash cd backend npx tsx src/db/seedHns.ts ``` - 기존 `DELETE FROM HNS_SUBSTANCE` → 새 1,345종 INSERT ## 재실행 안내 - `out/` 디렉토리는 `.gitignore` 처리되어 커밋되지 않음 - OCR 결과는 비결정적이므로 재실행 시 약간 달라질 수 있음 - 비용 절감을 위해 OCR 결과는 보존하고 `ocr-images.ts --resume` 로 실패 항목만 재시도 가능 ## 비용/시간 가이드 - 이미지 225장 × Claude Sonnet 4.6 기준 약 $3~10 - 전체 파이프라인: Excel 파싱 ~30초, 이미지 추출 ~10초, OCR ~10~20분, 병합/시드 ~1분 ## 알려진 이슈 (후속 작업 필요) ### 1) OCR ↔ base.json 국문명 매칭 실패 (136건) `merge-data.ts` 실행 시 base.json의 국문명과 `ocr.json` 키 표기가 달라 상세정보가 연결되지 않는 물질이 다수 존재. 실제 514종 중 **상세 정보 보유는 73종** 수준. **예시:** | base.json 국문명 | ocr.json 키 | |---|---| | `1메톡시2프로판올` | `1-메톡시-2프로판올` | | `c810이소알코올` | `(C8-10)이소 알코올` | | `메탄올` | (OCR 없음, 이미지 누락) | | `가솔린` | `휘발유` (동의어) | **원인:** - 하이픈/공백/괄호 제거 수준 차이 (Excel 시트명과 이미지 파일명 추출 로직 불일치) - 동의어 처리 미흡 (동의어 시트 215개 활용 필요) - OCR 대상 225종 외 나머지 1,120종은 기본정보만 존재 **권장 해결 방향:** - `merge-data.ts` 에 정규화 함수 추가 (공백/하이픈/괄호 제거 후 매칭) - `base.json` 의 동의어(synonyms) 배열을 역인덱스로 활용해 OCR 키와 교차 매칭 - 매칭 실패 목록을 `out/merge-unmatched.json` 으로 출력하여 수동 검토 ### 2) SEBC/CAS/UN 번호 varchar 길이 초과 `base.json` 생성 시 Excel에서 복수 CAS/UN 번호를 줄바꿈으로 결합해 저장하여, `HNS_SUBSTANCE` 테이블의 `VARCHAR(20)` 등 제약을 초과했음. 현재는 [`seedHns.ts`](../../../backend/src/db/seedHns.ts) 의 `firstToken()` 헬퍼로 첫 토큰만 검색 컬럼에 저장하고 원본 전체는 `DATA` JSONB에 보존. **영향:** - `CAS_NO`, `UN_NO` 검색 시 두 번째 이후 번호로는 매칭 불가 - 프론트엔드 표시 시 JSONB의 원본 값을 참조해야 함 **권장 해결 방향:** - `extract-excel.py` 에서 복수 CAS/UN 을 배열로 분리 저장 - 스키마에 `CAS_NO_LIST TEXT[]`, `UN_NO_LIST TEXT[]` 추가 후 GIN 인덱스 구성 ### 3) MSDS 요약시트 포맷 이미지 (약 5건) 원본 xlsm 일부 시트는 표준 HNS 카드가 아닌 KOSHA MSDS 요약시트 포맷으로, 저해상도 + 필드 구조 상이로 OCR 정확도가 떨어짐. **해당 물질:** 프로필벤젠, 프르푸필 알콜, 프로필렌 클리콜 알긴산, 휘발유, 헥사메틸렌 디이소시안산 **권장 해결 방향:** - `ANTHROPIC_API_KEY` 설정 후 `HNS_OCR_ONLY` 로 해당 5종만 재OCR - 이미지 누락 필드는 화학 문헌값(ICSC, PubChem)으로 보강