조달청 MVP — 누락 데이터 분석
실제 데이터 검증 후 DB / API / UI 에서 빠진 것 진단. 데모일 영향 + 데모 후 보강 plan.
작성: 2026-05-21
검증 데이터: 실제 김기열 주무관님 hwpx 2건 (JK알에스티 + 스마트파워)
대상: alpha 브랜치 MVP
1TL;DR
발견: ProductRecord 의 풍부한 데이터 (특허 / 모델·사양 / 시험성적 / 이미지 / 출처 추적) 가 JSONB 안에 다 있음. 정규화 컬럼은 6개만 (검색·정렬용). 데모 4 기능엔 충분하지만, 평가 단계 진입 시 추가 entity 필요.
| 분류 | 건수 | 예시 |
| ✅ 정규화 컬럼 (검색·정렬용) | 6개 | company_name / designation_no / product_category / title_ko / parser_version / parse_confidence |
| ⚠️ JSONB 안 보존 (조회만 OK, 검색·집계 시 unnest 필요) | 8 영역 | patents / detailed_items / quality_tests / image_assets / warranty / manufacturing / source_refs / parse_stats |
| ❌ DB 전체 누락 (데모 후 보강) | 5 entity | bids / requirement_items / users / evaluations / audit_log |
| ❌ 파일/이미지 영구 저장 누락 | 1 영역 | MinIO 미연동 — image_assets[].data_uri 가 base64 로 row 안에 부풀어 있음 |
2실제 데이터로 본 ProductRecord 의 풍부함
JK알에스티 row 의 extraction JSONB 안에 실제로 들어있는 데이터:
| 영역 | 실제 데이터 | JSONB 내 위치 | 정규화? |
| identity | title_ko, designation_no, author_or_org, product_category, scope, source_refs[] | extraction.identity | 4개만 정규화 / scope · source_refs JSONB |
| feature_summary | summary(text) + bullets[] + source_ref | extraction.feature_summary | ❌ 정규화 안 됨 |
| patents (특허) | 1건: type / name / number / date / issuer / description / source_ref | extraction.patents | ❌ 카운트만 가능 (집계 SQL 복잡) |
| detailed_items (모델·사양) | 4그룹: item_name_ko / item_code / rows[] (identifier / model_name / size_wxhxd_mm / specification_text / note) | extraction.detailed_items | ❌ 모델 검색 / 사이즈 필터 안 됨 |
| quality_tests (시험성적) | 1건: standard_title / test_type / test_items[] / confidence / missing_reason | extraction.quality_tests | ❌ 시험기준 검색 안 됨 |
| image_assets (이미지) | 15장: asset_type / filename / caption_ko / related_model_names[] / para_index / data_uri (base64!) / extraction_confidence | extraction.image_assets | ❌ data_uri 가 base64 → row 가 거대 |
| manufacturing (제조 공정) | 0건 (이 파일엔 없음) | extraction.manufacturing | ❌ |
| warranty (A/S) | {period: "2년", conditions: "납품·설치일로부터 2년", source_ref: {...}} | extraction.warranty | ❌ |
| parse_stats (파서 통계) | {text_chars: 178474, paragraphs: 427, tables: 32, images: 16, image_mime_dist: {png:10, bmp:3, jpeg:3}} | extraction.parse_stats | ❌ |
3데모 4 기능 vs 데이터 가용성
| 데모 기능 | 필요 데이터 | 현재 가용? |
| 다중 업로드 | file_name, file_size_bytes, status | ✅ 정규화 컬럼으로 OK |
| 파싱 | hwpx 가 처리 (BFF 는 forward) | ✅ |
| DB 저장 | proposals 테이블 | ✅ alpha MVP 에서 신설 완료 |
| 비교표 — 기본 (제목/번호/카테고리) | company_name, designation_no, product_category, title_ko | ✅ 정규화 컬럼으로 OK |
| 비교표 — 카운트 (특허 N건 / 모델 N그룹) | patents/detailed_items/quality_tests/image_assets 크기 | ⚠️ JSONB unnest 또는 frontend 에서 length 계산 — OK 지만 비효율 |
| 비교표 — 상세 drill-down (특허 번호 / 모델명 / 시험 기준) | 특정 patent / model / test 의 필드 | ⚠️ JSONB 경로 추출 (SELECT extraction->'patents'->0->>'number') |
| 이미지 표시 (시험성적서 사진 등) | image_assets[].data_uri | ⚠️ data_uri 가 base64 → 직접 가능하나 row 무거움 |
| 출처 인용 ("이 정보는 본문 ○○쪽") | 모든 필드의 source_ref | ⚠️ JSONB 안에만 — UI 가 표시하려면 추가 매핑 |
결론: 데모 4 기능 전부 동작. 일부는 JSONB unnest 가 필요하지만 frontend 가 JSON 으로 받아 length 계산 / 셀 클릭 시 상세 표시 가능.
4❌ DB 전체 누락된 entity 5개 (데모 후 보강)
| Entity | 용도 | 데모 영향 | 우선순위 |
bids (입찰/공고) | RFP 본문 + 공고번호 + 마감일 등. 현재 demo bid UUID 하드코딩 | 1회 데모는 OK (단일 demo bid) | P2 (데모 후 1주) |
requirement_items (요구사항 마스터) | RFP 의 주요기술 / 요구사항 / 평가항목 마스터. 비교 시 "이 업체가 ○○ 요구사항 만족?" 검증 | 현재 fixture 가 가짜로 채움 — 데모엔 OK 지만 진짜 평가엔 필요 | P2 |
users (평가위원) | 로그인 / 권한 / 작성자 추적 | 현재 anonymous OK | P3 (실서비스 진입 시) |
evaluations (평가 의견) | 평가위원이 작성한 점수 / 코멘트 | 현재 데모 시연만 — 평가 의견 저장 안 됨 | P3 |
audit_log (감사 로그) | 업로드 / 수정 / 평가 변경 이력 | 없어도 동작 | P4 (감사 요건 발생 시) |
5⚠️ 파일/이미지 저장 누락 — MinIO 미연동
현재 문제: ProductRecord.image_assets 의 각 이미지가 data_uri: "data:image/png;base64,iVBORw0KG..." 형식으로 들어옴. 15장 이미지 = 수 MB. DB row 가 부풀어 list 응답이 무거워짐.
| 항목 | 현재 | 권장 |
| HWPX 원본 파일 | 파일 저장 0 (BFF 가 hwpx 로 forward 후 버림) | MinIO 에 업로드 + proposals.file_storage_key 컬럼 추가 |
| 이미지 (image_assets) | data_uri base64 → JSONB 안 | MinIO 에 별도 업로드 + JSONB 에는 storage_key 만 |
| row 크기 영향 | 15장 × 평균 200KB = 3MB / row (base64 inflation 33%) | storage_key 만 → 1KB / row |
| list endpoint 응답 크기 | 10 proposals = 30MB+ (느림) | 10 proposals = 10KB (빠름) |
데모일 영향: 1~3 파일 시연이면 base64 도 OK. 실서비스 (10+ 업체 비교) 진입 시 필수 보강.
6🌐 i18n / 다국어 누락
| 필드 | 현재 | 누락 |
| title_ko | 한국어만 | title_en / title_ja 등 |
| product_category | 한국어만 | 영문 카테고리 |
| company_name | 등록 그대로 (㈜/(주) 혼재) | 정규화 안 됨 — "(주)스마트파워" vs "㈜스마트파워" |
데모 영향 작음. 단 ㈜ / (주) 정규화는 검색 정확도에 영향. 데모 후 보강 권장.
7🔍 검색·집계 누락 — JSONB 의 한계
현재 JSONB 에만 있어서 SQL 으로 직접 검색·집계 어려운 케이스:
| 질의 | 현재 SQL | 정규화 시 SQL |
| "특허 번호 10-2024-XXX 가진 업체" | WHERE extraction->'patents' @> '[{"number":"..."}]'::jsonb (postgres JSONB 만) | JOIN patents ON patent_number = '...' |
| "방열 키워드 들어간 모델" | WHERE extraction->'detailed_items' @> ... (복잡) | JOIN model_rows ON specification_text ILIKE '%방열%' |
| "KS C 3604 시험기준 통과한 업체" | JSONB unnest 후 ILIKE | JOIN quality_tests ON standard_title = '...' |
| "파일당 평균 이미지 수" | jsonb_array_length(extraction->'image_assets') 평균 | COUNT(*) FROM proposal_images GROUP BY proposal_id |
데모 영향 0. 데모는 "비교 표 표시" 까지만. 검색·집계는 데모 후 사용량 증가 시 정규화 테이블 추가 권장 (proposal_patents / proposal_model_rows / proposal_quality_tests / proposal_images).
8📊 ProductRecord 의 source_ref — 출처 추적 보존 OK
hwpx-intelligence 가 모든 필드에 source_ref (섹션·문단 위치 + 신뢰도 + extractor 종류) 를 부착. 평가위원이 "이 정보 어디서 추출됐나" 추적 가능.
// 예: warranty 의 source_ref
warranty: {
period: "2년",
conditions: "납품·설치일로부터 2년",
source_ref: {
section: "7",
para_start: 395,
para_end: 396,
file: "6fd3844af2df_1. 폐쇄형배전반_JK알에스티(2025057).hwpx",
confidence: 1.0,
extractor: "programmatic"
}
}
| 측면 | 현재 상태 |
| JSONB 안 보존 | ✅ 모든 필드의 source_ref 그대로 |
| frontend 표시 | ❌ 미구현 — 셀 클릭 시 source_ref 표시 UI 없음 |
| 가치 | 평가위원의 신뢰도 ↑ — "이 점수 어떤 근거?" 즉답 가능 |
9📈 parse_stats — 풍부한 메타 (활용 안 됨)
JK알에스티 파일의 parse_stats:
| 지표 | 값 | 활용 가능 |
| text_chars | 178,474 | 제안서 분량 비교 (긴 = 정성?) |
| paragraphs | 427 | 구조 복잡도 |
| tables | 32 | 표 많음 = 사양서 충실? |
| images | 16 | 시각 자료 풍부 |
| image_mime_dist | {png:10, bmp:3, jpeg:3} | 이미지 품질 — bmp 는 압축 안 됨 (제출 품질 낮음) |
이런 메타는 frontend 비교 표에 "분량 / 표 수 / 이미지 수" 행으로 노출 가능. 정규화 없이 JSONB 그대로 표시.
10최종 누락 매트릭스
| # | 누락 | 저장 위치 | 데모 영향 | 후속 우선순위 |
| 1 | patents 정규화 테이블 | JSONB 안 | 없음 (frontend 가 length 계산) | P3 (검색 필요 시) |
| 2 | detailed_items 정규화 | JSONB 안 | 없음 | P3 |
| 3 | quality_tests 정규화 | JSONB 안 | 없음 | P3 |
| 4 | image_assets 정규화 + MinIO | JSONB 안 base64 | 1~3 파일 시연 OK / 10+ 비교 시 무거움 | P2 (실서비스) |
| 5 | bids 테이블 | 없음 (UUID 하드코딩) | 1 입찰 데모 OK | P2 |
| 6 | requirement_items 테이블 | 없음 (T6 제거됨) | 비교 점수 fixture 였음 — 진짜 평가엔 필요 | P2 |
| 7 | users / evaluations | 없음 | 익명 데모 OK | P3 |
| 8 | HWPX 원본 파일 저장 (MinIO) | 없음 (forward 후 버림) | 재파싱 불가 (단 hwpx 가 자기 측에 보관) | P3 |
| 9 | source_ref 표시 UI | JSONB 안 보존 | 없음 | P3 (UX 강화) |
| 10 | company_name 정규화 ((주)/㈜) | 그대로 저장 | 검색 정확도 영향 | P3 |
| 11 | i18n (영문 title 등) | 없음 | 없음 | P4 (글로벌 확장 시) |
| 12 | audit_log | 없음 | 없음 | P4 |
11데모일 (5/22) 관점 결론
현 MVP 가 데모 4 기능 모두 커버. 누락된 데이터는 대부분 (1) JSONB 안에 보존되어 frontend 가 풀어서 표시 가능, (2) 실서비스 진입 시에만 정규화 필요.
데모일 직전 (D-1) 추가 작업할 필요 없음. 데모 후 보강 순서:
| 우선순위 | 작업 | 시점 |
| P1 (이미 완료) | proposals 테이블 + extraction JSONB | alpha |
| P2 (데모 후 1주) | bids + requirement_items + MinIO 이미지 저장 | 실서비스 진입 직전 |
| P3 (데모 후 1개월) | users + evaluations + patents/items/tests 정규화 | 평가 의견 작성 필요 시 |
| P4 (필요 시) | audit_log + i18n | 감사 요건 / 글로벌 |