데이터셋 퍼블리싱 가이드¶
이 문서는 scripts/publish_to_hf.py 스크립트를 사용하여 공공데이터를 HuggingFace Hub 및 Kaggle에 퍼블리싱하는 전체 과정을 설명합니다.
개요¶
publish_to_hf.py는 config-driven 방식의 end-to-end 퍼블리싱 스크립트입니다. 하나의 YAML config 파일로 데이터 수집 → 변환 → Parquet 저장 → Dataset Card 생성 → HuggingFace 업로드까지 처리합니다.
이 스크립트는 향후 Builder의 Medallion Architecture(Bronze/Silver/Gold/Exporter/Publisher) 모듈로 분해될 레퍼런스 구현입니다.
[YAML Config] → fetch → transform → write_parquet → generate_card → upload_to_hf / upload_to_kaggle
│ │ │ │ │
Bronze Silver Gold Export Publish (HF + Kaggle)
사전 준비¶
1. 환경변수 설정¶
~/.zshrc (또는 ~/.bashrc)에 다음 환경변수를 추가합니다:
# 공공데이터포털 API 키 (https://www.data.go.kr 에서 발급)
export KPUBDATA_DATAGO_API_KEY="your-api-key"
# HuggingFace API 토큰 (https://huggingface.co/settings/tokens 에서 발급)
export HF_TOKEN="hf_..."
# Kaggle API 인증 (https://www.kaggle.com/settings → API → Create New Token)
export KAGGLE_USERNAME="your-username"
export KAGGLE_KEY="your-api-key"
설정 후 반영:
source ~/.zshrc
Kaggle 대체 인증 방법:
~/.kaggle/kaggle.json파일에{"username": "...", "key": "..."}형태로 저장해도 됩니다. 환경변수가 우선합니다.
2. HuggingFace 토큰 발급¶
- https://huggingface.co/settings/tokens 접속
- Create new Access Token 클릭
- 설정:
- Token type: Fine-grained
- Token name:
kpubdata-publish(또는 원하는 이름) - User permissions > Repositories: ✅ Write access to contents/settings
- Org permissions: 대상 org 선택 후 ✅ Write access to contents/settings
- 나머지 권한은 체크 해제
- 토큰 생성 후
HF_TOKEN환경변수에 저장
3. HuggingFace Organization (선택)¶
org 단위로 데이터셋을 관리하려면:
- https://huggingface.co/organizations/new 에서 org 생성
- 토큰 발급 시 해당 org에 대한 write 권한 부여
4. Kaggle API 토큰 발급¶
- https://www.kaggle.com/settings 접속
- API 섹션에서 Create New Token 클릭
- 다운로드된
kaggle.json에서username과key를 환경변수에 설정 - 주의: 새 토큰을 발급하면 이전 토큰은 즉시 폐기됨
Kaggle은 organization을 지원하지 않습니다. 모든 데이터셋은 개인 계정 소속입니다.
5. 의존성 설치¶
cd kpubdata-builder
uv sync --extra publish
publish extra에는 polars, huggingface-hub, kaggle이 포함됩니다.
사용법¶
기본 실행 (HuggingFace + Kaggle 모두 업로드)¶
uv run python scripts/publish_to_hf.py scripts/configs/seoul_apartment_trades.yaml
HuggingFace만 업로드¶
uv run python scripts/publish_to_hf.py scripts/configs/seoul_apartment_trades.yaml --target hf
Kaggle만 업로드¶
uv run python scripts/publish_to_hf.py scripts/configs/seoul_apartment_trades.yaml --target kaggle
로컬에서만 파일 생성 (업로드 안 함)¶
uv run python scripts/publish_to_hf.py scripts/configs/seoul_apartment_trades.yaml --local-only
드라이런 (업로드 시뮬레이션)¶
uv run python scripts/publish_to_hf.py scripts/configs/seoul_apartment_trades.yaml --dry-run
디버그 로깅¶
uv run python scripts/publish_to_hf.py scripts/configs/seoul_apartment_trades.yaml -v
CLI 옵션 요약¶
| 옵션 | 설명 |
|---|---|
config (필수) |
YAML config 파일 경로 |
--target |
업로드 대상: hf (HuggingFace), kaggle, all (기본값: all) |
--dry-run |
업로드를 건너뛰고 로컬 파일만 생성 (업로드 시뮬레이션 로그 출력) |
--local-only |
로컬 파일 생성까지만 실행 (업로드 로직 자체를 건너뜀) |
--verbose, -v |
DEBUG 레벨 로깅 활성화 |
--confirm-non-commercial |
비상업 조건(KOGL 제2유형) 데이터셋을 비공개 Kaggle 로만 게시하겠다고 확인 (#688) |
재배포 게이트 (#688)¶
--local-only 가 아니면, 무엇이든 가져오기 전에 config 의 card.license/license_name 으로
재배포 가능 여부를 판정하고 막을 것은 종료 코드 2 로 막는다.
| 판정 | 조건 | 게시 |
|---|---|---|
allowed |
korea-public-data-unrestricted, kogl-type-1 |
허용 |
non_commercial |
kogl-type-2 |
비공개 Kaggle 에만, --confirm-non-commercial 과 함께. HF 업로드는 항상 공개라 거부 |
forbidden |
kogl-type-3, kogl-type-4 — 이 경로는 가공 데이터를 게시하므로 변경금지 조건과 충돌 |
거부 |
unknown |
그 밖 전부 — 조건을 적지 않았거나(license 없음), 확인하지 않은 cc-by-4.0 등 |
거부 (모름은 허락이 아니다) |
이 게이트는 마지막 config 가 BuildSpec 으로 옮겨질 때 레거시 코드와 함께 사라진다(ADR 0018).
BuildSpec 경로의 재배포 게이트 (#688)¶
BuildSpec 으로 만든 run 은 kpubdata 가 데이터셋마다 선언한 조건
(DatasetRef.license.redistribution, kpubdata 0.8)으로 판정한다. 아무것도 선언하지 않은 데이터셋,
카탈로그에 없는 데이터셋, file·url 소스는 모두 unknown 이다. 한 run 의 판정은 가장 제한적인 소스의
판정이다 (forbidden > unknown > non_commercial > allowed).
| 판정 | 게시 | 질의·미리보기·다운로드 |
|---|---|---|
allowed |
허용 (BuildSpec license 선언은 여전히 필요, #443) |
허용 |
non_commercial |
confirm_non_commercial: true 필요. 공개 게시에는 데이터셋 licence 에 비영리 표시(cc-by-nc-4.0 등)도 필요 |
허용 |
unknown |
비공개만 — 공개 게시는 redistribution_unknown 으로 거부 |
허용 |
forbidden |
전부 거부 (redistribution_forbidden) |
거부 — /query, /preview, warehouse 프로파일·질의·rows·집계·export·export 다운로드, 분석 생성·실행, artifact 다운로드가 403, stage 상세는 sample 을 빼고 준다 |
- HTTP:
GET /builds/{run_id}/publish/readiness가redistribution(판정과 소스별 이유)을 돌려주고, 막힌POST .../publish도 같은 값을 준다. 판정은 target 의 기본 옵션(비공개)으로 계산하고, POST 가 실제 옵션으로 다시 확인한다. - 비공개로만 허용되는 게시(
unknown,non_commercial)는 게시 전에 대상의 현재 공개 여부를 호출자 자격증명으로 읽는다. 이미 있는 저장소에 게시해도 공개 여부는 바뀌지 않으므로, 이미 공개인 Hugging Face repo·Kaggle dataset 이면destination_public으로 막고, 공개 여부를 읽지 못하면destination_visibility_unknown으로 막는다(모르는 것은 허가가 아니다). 없는 대상(새로 만들 것)과 비공개 대상은 통과한다. CLI 도 같다. - 성공한 게시는 응답과 receipt 의
redistribution에 판정, 판정을 읽은 kpubdata 버전(kpubdata_version),confirm_non_commercial을 남긴다.confirm_non_commercial은 Builder 의 확인이라 publisher 로 넘기지 않는다. - CLI:
kpubdata-builder publish에도 같은 게이트가 있다. 막히면 종료 코드 2, 비영리 확인은--confirm-non-commercial. - 2026-09-30 현재 kpubdata 카탈로그의 어떤 데이터셋도
redistribution을 선언하지 않았으므로, BuildSpec 경로의 공개 게시는 모두 막힌다. 조건을 정하는 일은 kpubdata#524 다.
Config YAML 스키마¶
Config 파일은 4개의 최상위 섹션으로 구성됩니다.
source — 데이터 수집 설정¶
source:
provider: datago # kpubdata provider 키
dataset: apt_trade # dataset 키
list_all: true # true면 자동 페이지네이션
fetch_params: # 각 항목이 하나의 API 호출
- LAWD_CD: "11680"
DEAL_YMD: "202401"
- LAWD_CD: "11650"
DEAL_YMD: "202401"
| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
provider |
str | ✅ | kpubdata의 provider 이름 |
dataset |
str | ✅ | kpubdata의 dataset 이름 |
list_all |
bool | true면 ds.list_all() 사용 (자동 페이지네이션) |
|
fetch_params |
list[dict] | ✅ | API 호출 파라미터 목록. 각 항목이 별도 호출, 결과는 합산 |
transform — 데이터 변환 설정¶
transform:
column_mapping:
sggCd: district_code # raw 필드명 → clean 컬럼명
dealAmount: deal_amount_10k_krw
dtypes:
district_code: str
deal_amount_10k_krw: int_comma # "82,500" → 82500
derived:
- name: deal_date
expr: "concat_date(deal_year, deal_month, deal_day)"
dtype: str
filters:
- "deal_amount_10k_krw > 0"
| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
column_mapping |
dict[str, str] | ✅ | raw API 필드 → clean 컬럼 이름 매핑. 매핑되지 않은 컬럼은 제거됨 |
dtypes |
dict[str, str] | 타입 캐스팅. 지원: int, float, str, int_comma |
|
derived |
list[dict] | 파생 컬럼 정의 | |
filters |
list[str] | 행 필터 표현식 |
지원하는 타입 (dtypes)¶
| 타입 | 설명 | 예시 |
|---|---|---|
str |
문자열 변환 | "11680" → "11680" |
int |
정수 변환 (null-safe) | "2024" → 2024, "-" → null |
float |
실수 변환 (null-safe) | "84.5" → 84.5, "" → null |
int_comma |
콤마 제거 후 정수 변환 | "82,500" → 82500 |
null-safe 처리: 빈 문자열(""), "-", "N/A", "null", "None"은 자동으로 null로 변환됩니다.
파생 컬럼 (derived)¶
| 표현식 | 설명 | 예시 |
|---|---|---|
concat_date(y, m, d) |
날짜 문자열 조합 (zero-padded) | concat_date(deal_year, deal_month, deal_day) → "2024-01-15" |
format(fmt, col1, col2, ...) |
Polars pl.format() 사용 |
format("{}-{}", year, month) → "2024-01" |
필터 표현식 (filters)¶
"컬럼명 연산자 값" 형태의 문자열. 지원 연산자: >, <, >=, <=, ==, !=
filters:
- "deal_amount_10k_krw > 0"
- "floor >= 1"
모든 필터를 동시에 만족하는 행만 유지됩니다 (AND 조건).
output — 출력 설정¶
output:
hf_repo: "kpubdata/seoul-apartment-trades"
kaggle_slug: "yschoe/seoul-apartment-trades"
parquet_filename: "data/train.parquet"
staging_dir: "./staging/seoul-apartment-trades"
| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
hf_repo |
str | ✅ | HuggingFace 레포 ID (org/dataset-name) |
kaggle_slug |
str | Kaggle 데이터셋 슬러그 (username/dataset-name). 없으면 Kaggle 업로드 건너뜀 |
|
parquet_filename |
str | ✅ | staging 내 parquet 파일 경로 |
staging_dir |
str | ✅ | 로컬 staging 디렉토리 (스크립트 실행 위치 기준 상대경로) |
card — Dataset Card 설정¶
card:
title: "Korean Apartment Trades (아파트매매 실거래가)"
description: |
Real transaction prices for apartment sales...
license: "cc-by-4.0"
language:
- ko
tags:
- real-estate
- tabular
features:
- name: district_code
description: "시군구 코드 (5-digit administrative district code)"
| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
title |
str | ✅ | 데이터셋 제목 |
description |
str | ✅ | 데이터셋 설명 |
license |
str | 라이선스 (기본값: cc-by-4.0) |
|
language |
list[str] | 언어 코드 (기본값: ["ko"]) |
|
tags |
list[str] | HuggingFace 태그 | |
features |
list[dict] | 피처별 이름과 설명. Dataset Card의 Features 테이블에 렌더링됨 |
스크립트 내부 구조¶
스크립트의 각 함수는 Builder의 Medallion Architecture stage에 1:1 대응됩니다.
함수 → Builder Stage 설명
──────────────────────────────────────────────────────────────
load_config() → (설정) YAML config 로드 및 검증
fetch_records() → Bronze kpubdata Client로 raw 데이터 수집
transform_records() → Silver Polars 기반 컬럼 매핑, 타입 변환, 필터링
├── _cast_column() null-safe 타입 캐스팅
├── _nullify_tokens() 빈값/"-"/"N/A" → null 변환
├── _add_derived_column() 파생 컬럼 생성
└── _apply_filter() 비교 필터 적용
write_parquet() → Gold Parquet 패키징
generate_dataset_card() → Export HF Dataset Card (README.md) 생성
├── _build_features_table() 피처 설명 마크다운 테이블
├── _build_sample_table() 샘플 데이터 테이블
└── _build_stats_section() 수치 컬럼 통계
upload_to_hf() → Publish whitelist 기반 HF Hub 업로드
upload_to_kaggle() → Publish dataset-metadata.json 생성 + Kaggle API 업로드
└── _map_kaggle_license() HF→Kaggle 라이선스 매핑
main() → CLI argparse 기반 진입점 (--target hf|kaggle|all)
업로드 보안¶
upload_to_hf()는 staging 디렉토리 전체를 업로드하지 않습니다. 임시 .hf_upload/ 디렉토리에 다음 파일만 복사 후 업로드합니다:
README.md(dataset card)data/*.parquet(데이터 파일)
업로드 완료 후 .hf_upload/ 디렉토리는 자동 삭제됩니다.
제공되는 Config 파일¶
seoul_apartment_trades.yaml¶
국토교통부 아파트매매 실거래가 데이터. 서울 25개구 전체, 2020-01~2024-12 (60개월). 거래가격(deal_amount_10k_krw)을 타겟 변수로 하는 tabular regression / time-series 벤치마크.
- 소스: data.go.kr MOLIT 아파트 실거래가 API
- 지역: 서울 25개구 전체
- 기간: 2020년 1월 ~ 2024년 12월 (60개월)
- API 호출: 1,500회 (25개구 × 60개월)
- 예상 레코드: ~250,000건
- HF 레포:
kpubdata/seoul-apartment-trades
korea_base_rate.yaml¶
한국은행 기준금리 데이터. config-driven 재사용성 검증용.
- 소스: BOK 기준금리 API
- HF 레포:
kpubdata/korea-base-rate
새 데이터셋 추가하기¶
scripts/configs/에 새 YAML config 파일 생성- 위 스키마에 맞춰
source,transform,output,card섹션 작성 --local-only로 먼저 로컬 테스트:uv run python scripts/publish_to_hf.py scripts/configs/my_new_dataset.yaml --local-only -v- staging 디렉토리에서 parquet과 README.md 확인
- 문제없으면 실행:
uv run python scripts/publish_to_hf.py scripts/configs/my_new_dataset.yaml
Config 작성 팁¶
column_mapping에 포함되지 않은 raw 필드는 자동으로 제거됩니다.fetch_params의 각 항목은 별도의 API 호출이 됩니다. 여러 지역/기간을 조합하려면 항목을 추가하세요.list_all: true를 사용하면 페이지네이션을 자동으로 처리합니다.int_comma타입은"82,500"같은 콤마가 포함된 숫자 문자열을 처리합니다.- 파생 컬럼은 타입 캐스팅 이후에 계산되므로, 참조하는 컬럼이 올바른 타입인지 확인하세요.
트러블슈팅¶
No records fetched 에러¶
KPUBDATA_DATAGO_API_KEY환경변수가 올바르게 설정되었는지 확인fetch_params의 파라미터 값이 API 스펙에 맞는지 확인 (예:LAWD_CD는 5자리 시군구 코드)
huggingface_hub not installed 에러¶
uv sync --extra publish
HF 업로드 401/403 에러¶
HF_TOKEN환경변수 확인- 토큰에 대상 org/repo에 대한 write 권한이 있는지 확인
- Fine-grained 토큰의 경우 Org permissions에서 대상 org 선택 필요
Polars 타입 캐스팅 실패¶
- 원본 데이터에 빈 문자열,
"-","N/A"등이 포함되어 있을 수 있습니다 →int/float타입은 자동으로 null-safe 처리됩니다 - 콤마가 포함된 숫자 필드는
int_comma타입을 사용하세요
Kaggle 업로드 401 에러¶
KAGGLE_USERNAME과KAGGLE_KEY환경변수 확인- 또는
~/.kaggle/kaggle.json파일 존재 여부 확인 - Kaggle 토큰을 재발급하면 이전 토큰이 즉시 폐기됨. 새 토큰으로 환경변수/파일 모두 갱신 필요
Kaggle dataset_view AttributeError¶
- kaggle SDK 1.6+ 에서
dataset_view()메서드가 제거됨 - 스크립트는
dataset_list(mine=True, search=slug)방식으로 데이터셋 존재 여부를 판별
Kaggle organization 미지원¶
- Kaggle은 HuggingFace와 달리 organization 계정을 지원하지 않음
- 모든 데이터셋은 개인 계정 소속 (
username/dataset-name) kaggle_slug은hf_repo와 다른 namespace를 가질 수 있음
Builder 모듈 분해 가이드¶
이 스크립트는 학생들이 Builder의 Medallion Architecture 모듈로 분해하는 레퍼런스입니다.
| 스크립트 함수 | 분해 대상 모듈 | 디렉토리 |
|---|---|---|
fetch_records() |
Bronze stage | src/kpubdata_builder/stages/bronze/ |
transform_records() |
Silver stage (Polars engine) | src/kpubdata_builder/stages/silver/, tabular/ |
write_parquet() |
Gold stage | src/kpubdata_builder/stages/gold/ |
generate_dataset_card() |
HF Layout Exporter | src/kpubdata_builder/exporters/ |
upload_to_hf() |
HF Publisher | src/kpubdata_builder/publishers/ |
upload_to_kaggle() |
Kaggle Publisher | src/kpubdata_builder/publishers/ |
| Config YAML | BuildSpec model | src/kpubdata_builder/spec.py |
참고 이슈: #50, #8, #9, #10, #28, #37, #40