DEV Community

박준현
박준현

Posted on

wide CSV 여러 개를 EAV로 모아 gold mart 만들기

wide CSV 여러 개를 EAV로 모아 gold mart 만들기

현실의 데이터 소스는 한 가지 모양으로 오지 않는다.

같은 의미의 값도 어떤 파일에서는 생산수량, 다른 파일에서는 units, 또 다른 파일에서는 made로 올 수 있다. 온도도 어떤 곳은 섭씨, 어떤 곳은 화씨일 수 있다. 이걸 매번 pipeline code에 if source == ...로 박기 시작하면 source가 늘 때마다 코드가 지저분해진다.

manufacturing-data-platform-mini의 EAV mini slice는 이 문제를 작게 다룬다. 여러 wide CSV를 mapping config로 표준 attribute에 맞춘 뒤, EAV long format으로 모으고, 다시 gold metric mart로 pivot/aggregate한다. 데이터는 모두 synthetic이고, 회사 코드·고객 데이터·실제 schema는 쓰지 않았다.

1. Scenario

서로 다른 공장/라인/벤더에서 비슷한 제조 지표 파일이 들어온다.

예:

plant_a.csv:
  설비ID, 생산수량, 불량수, 온도C, 압력kPa

plant_b.csv:
  machine_id, output_units, defects, temp_f, pressure_bar

vendor_d.csv:
  unit_name, made, scrap, deg_c, kpa
Enter fullscreen mode Exit fullscreen mode

비즈니스적으로는 같은 지표를 보고 싶다.

units_produced
defect_count
temperature_c
pressure_kpa
Enter fullscreen mode Exit fullscreen mode

문제는 source마다 column name과 unit이 다르다는 점이다.

2. Decision Pressure

단순 구현은 source마다 코드를 늘린다.

if source == "plant_a":
  생산수량을 units_produced로 읽는다

if source == "plant_b":
  output_units를 units_produced로 읽는다
  temp_f를 섭씨로 변환한다

if source == "vendor_d":
  made를 units_produced로 읽는다
Enter fullscreen mode Exit fullscreen mode

이 방식은 작게는 빨라 보이지만 source가 늘수록 문제가 된다.

새 파일 형식마다 pipeline code를 고쳐야 한다.
column mapping과 transform logic이 섞인다.
unit conversion이 흩어진다.
quality check가 source별로 갈라진다.
gold mart grain을 설명하기 어려워진다.
Enter fullscreen mode Exit fullscreen mode

그래서 mapping은 config로 빼고, pipeline은 표준 attribute를 처리하게 만들었다.

3. Options

option result risk
source별 hard-coded parser 처음엔 빠름 source가 늘 때 code change 반복
모든 source를 wide table 하나로 합치기 보기 쉬움 sparse/heterogeneous column 폭발
EAV long format 이종 attribute를 표준 형태로 모음 pivot/quality 설계가 필요
full mapping DSL/rules engine 유연함 mini project에는 과함

이 프로젝트의 선택은 단순한 JSON mapping + EAV long + gold pivot이다.

4. Decision

각 source는 JSON config로 자신의 column을 표준 attribute에 매핑한다.

source column -> standard attribute
output_units  -> units_produced
temp_f        -> temperature_c with f_to_c
pressure_bar  -> pressure_kpa with bar_to_kpa
Enter fullscreen mode Exit fullscreen mode

pipeline 흐름:

wide CSVs
-> mapping configs
-> EAV long rows
-> gold entity_daily_metrics
-> quality checks
-> catalog/lineage
Enter fullscreen mode Exit fullscreen mode

EAV row의 핵심 shape:

entity_id
business_date
attribute
value
value_type
source_id
source_file_id
Enter fullscreen mode Exit fullscreen mode

source_file_id는 file content hash다. 즉 EAV slice도 file-level idempotency를 갖는다.

더 정확히는 source_file_id가 각 source file의 hash이고, run-level idempotency key는 모든 source file hash를 합친 source_hash다. 그래서 같은 source 묶음을 같은 business_date로 다시 처리하면 기존 successful run을 재사용한다.

5. Evidence

관련 코드와 config:

src/manufacturing_data_platform/pipeline/eav.py
src/manufacturing_data_platform/pipeline/run_eav.py
src/manufacturing_data_platform/pipeline/sample_eav.py
config/eav_mappings/plant_a.json
config/eav_mappings/plant_b.json
config/eav_mappings/line_c.json
tests/test_eav_pipeline.py
Enter fullscreen mode Exit fullscreen mode

핵심 테스트:

test_transform_to_eav_maps_and_converts_units
test_transform_to_eav_captures_type_errors_gracefully
test_transform_eav_to_gold_aggregates_sum_and_avg
test_eav_run_passes_and_unifies_three_formats
test_new_format_is_onboarded_by_adding_one_config
test_eav_idempotent_rerun_is_skipped
test_mapping_coverage_fails_when_required_attribute_unmapped
test_unmapped_columns_warn_does_not_fail
Enter fullscreen mode Exit fullscreen mode

특히 test_new_format_is_onboarded_by_adding_one_config는 중요하다.

이 테스트는 기존 3개 sample source가 있는 상태에서 vendor_d.csvvendor_d.json을 추가한다. pipeline code는 바꾸지 않는다. 그 다음 run 결과에 새 entity VD-1이 들어오는지 확인한다.

즉 claim은 이렇다.

새 파일 형식 하나는 mapping config 추가만으로 온보딩된다.
pipeline code change는 없다.
Enter fullscreen mode Exit fullscreen mode

검증 로그:

2026-06-30 EAV mini slice:
  pytest: 33 passed
  EAV CLI run 1: status=processed, quality_passed=true
  EAV CLI run 2: status=skipped
  conservation: EAV units 540 == gold units 540

2026-07-08 publication readiness check:
  pytest: 33 passed
  EAV JSON CLI: passed, status=processed, quality_passed=true

2026-07-10 B3 publication evidence check:
  pytest: 35 passed
  EAV JSON CLI run 1: status=processed, quality_passed=true
  EAV JSON CLI run 2: status=skipped
  gold_rows=4, gold_units_total=540, gold_defects_total=12
Enter fullscreen mode Exit fullscreen mode

실제 CLI 검증 요약:

{
  "run1_status": "processed",
  "run2_status": "skipped",
  "dataset_id": "manufacturing_wide_eav",
  "business_date": "2026-06-29",
  "gold_rows": 4,
  "entities": ["EQP-A1", "EQP-A2", "LN-C1", "MC-B1"],
  "gold_units_total": 540,
  "gold_defects_total": 12,
  "lineage_layers": ["bronze", "silver_eav", "gold"]
}
Enter fullscreen mode Exit fullscreen mode

6. Quality Checks

EAV slice도 단순 transform으로 끝나지 않는다.

quality checks:

mapping_coverage
unmapped_source_columns (warn)
not_null_value
accepted_values_attribute
value_type_valid
numeric_range_within_bounds
eav_to_gold_conservation
freshness_business_date
schema_drift
Enter fullscreen mode Exit fullscreen mode

EAV에서 gold로 pivot/aggregate할 때 additive measure가 보존되는지도 확인한다.

EAV units_produced total == gold units_produced total
EAV defect_count total == gold defect_count total
Enter fullscreen mode Exit fullscreen mode

그리고 manufacturing slice와 달리, EAV slice는 unparseable value를 바로 crash하지 않는다.

unparseable value
-> value = None
-> value_type_valid = fail
Enter fullscreen mode Exit fullscreen mode

즉 bad value를 quality failure로 드러낸다.

7. Claim Boundary

정직한 한계:

데이터는 fully synthetic이다.
회사 코드, 고객 데이터, 실제 schema를 쓰지 않았다.
full mapping DSL이나 UI는 없다.
EAV가 모든 데이터 모델링 문제의 정답이라는 주장이 아니다.
Spark/Iceberg/Kafka는 이 slice에 구현되어 있지 않다.
Enter fullscreen mode Exit fullscreen mode

실무 경험 claim도 조심해서 말해야 한다.

말해도 되는 것:
  실무에서는 EAV 기반 구조를 운영·개선하며 다양한 파일 양식을 처리했다.
  개인 프로젝트에서는 synthetic data로 wide -> EAV -> gold flow를 직접 구현했다.

말하면 안 되는 것:
  실무 EAV 시스템을 내가 처음부터 설계/구현했다.
  회사 schema를 이 프로젝트에 가져왔다.
  real customer data로 검증했다.
Enter fullscreen mode Exit fullscreen mode

8. Why This Matters For The Portfolio

이 slice는 단순히 EAV라는 단어를 넣기 위한 것이 아니다.

보여주는 능력:

이종 source를 표준 attribute로 harmonize
mapping config와 transform logic 분리
wide -> long -> mart 모델링
unit conversion
data quality check 설계
새 format 온보딩 contract
clean-room boundary 관리
Enter fullscreen mode Exit fullscreen mode

즉 데이터 엔지니어링 포트폴리오에서 "파이프라인을 만들었다"보다 한 단계 더 나아가, source 다양성과 모델링 압력을 다뤘다는 증거가 된다.

9. 정리

서로 다른 wide CSV를 하나의 mart로 합치려면 컬럼 이름만 맞추는 것으로는 부족하다. 어떤 source column이 어떤 표준 attribute가 되는지, 단위 변환은 어디서 하는지, 새 format은 어떤 계약으로 들어오는지까지 정해야 한다.

이 slice에서는 synthetic data만 사용해 wide -> EAV -> gold 흐름을 만들었다. 새 파일 형식은 pipeline code 변경 없이 mapping config 하나로 온보딩되고, mapping coverage, value type, conservation check로 결과를 검증한다. 실무 경험과 개인 프로젝트 구현 claim은 분리해서 말한다.

Top comments (0)