API 계약 — KPubData Builder¶
1. 문서 목적¶
이 문서는 Studio 중심 계약이 아니라 Builder 중심 계약을 정의합니다.
- Builder는 BuildSpec 검증, preview, build 실행, manifest 조회, publish 실행을 제공하는 실행 서비스입니다.
- Studio는 이 계약을 호출하는 외부 UI 클라이언트일 뿐입니다.
- CLI와 향후 HTTP service mode는 같은 도메인 계약을 공유해야 합니다.
2. 실행 모델¶
Builder는 두 가지 실행 모델을 가집니다.
| 모델 | 설명 | 적합한 작업 |
|---|---|---|
| 동기식 | 요청-응답 안에서 결과를 바로 반환 | /validate, /preview |
| 비동기식 | build를 생성하고 상태를 폴링 | /build, /artifacts/{run_id} |
원칙:
- 검증과 preview는 동기식으로 제공 가능합니다.
- 실제 build와 publish는 비동기식으로 모델링하는 것을 기본값으로 둡니다.
추가 방향:
- 현재 엔드포인트는 build 단위 계약을 유지합니다.
- 향후 버전에서는 Medallion stage별 artifact/preview 조회를 위해
/builds/{id}/stages/{stage}/artifacts같은 stage-specific endpoint를 노출할 수 있습니다.
3. 응답 코드 정책¶
| 상황 | 상태 코드 | body 형태 |
|---|---|---|
| 정상 | 200 |
각 엔드포인트별 정상 응답 참고 |
| BuildSpec 파싱/로드 실패 | 400 |
{"status": "error", "error": "<메시지>"} |
| BuildSpec 검증 실패 | 400 |
{"status": "invalid", "problems": ["...","..."]} |
| 빌드 실패 (upstream 소스 오류 등) | 502 |
{"status": "failed", "outcomes": [...], ...} |
| 리소스 없음 | 404 |
{"error": "<메시지>"} |
실제 구현:
POST /validate에서 검증 실패 시422가 아닌400을 반환하며, body는 error 봉투 형식이 아닌{"status": "invalid", "problems": [...]}형태입니다.POST /build에서 빌드 실패 시502를 반환하며, body에outcomes배열이 실립니다.
4. 엔드포인트 요약¶
| 엔드포인트 | 메서드 | 목적 | 실행 모델 |
|---|---|---|---|
/version |
GET |
Builder API 계약 버전 조회 | 동기식 |
/validate |
POST |
BuildSpec 검증 | 동기식 |
/preview |
POST |
샘플 실행 및 소스별 스키마 preview | 동기식 |
/build |
POST |
빌드 실행 (동기; 계약은 비동기 /builds 지향) |
동기식(현재) |
/artifacts/{run_id} |
GET |
실행 워크스페이스 산출물 목록 조회 | 동기식 |
5. 엔드포인트 상세¶
5.1 GET /version¶
Builder API 계약 버전을 반환합니다.
응답 200¶
{
"service": "kpubdata-builder",
"api_version": "1.0.0"
}
5.2 POST /validate¶
BuildSpec을 실행 전에 검증합니다. body의 spec 키에 YAML 문자열을 전달합니다.
요청¶
{
"spec": "dataset_id: weather-village-forecast\ntitle: 날씨 데이터\ndescription: 설명\nsources:\n - provider: datago\n dataset: village_fcst\nexports:\n - kind: markdown\n output_path: out.md\n"
}
응답 200 (유효한 스펙)¶
{
"status": "valid",
"dataset_id": "weather-village-forecast",
"api_version": "1.0.0"
}
응답 400 (스펙 로드 실패)¶
{
"status": "error",
"error": "Failed to parse build spec: dataset_id is required"
}
응답 400 (검증 실패)¶
{
"status": "invalid",
"problems": [
"at least one source is required",
"at least one export target is required"
]
}
5.3 POST /preview¶
각 소스의 스키마와 샘플 행을 산출합니다 (파일 미기록).
요청¶
{
"spec": "...",
"limit": 5
}
응답 200¶
{
"dataset_id": "weather-village-forecast",
"previews": [
{
"source_key": "datago.village_fcst",
"status": "ok",
"error": null,
"schema": [
{"name": "baseDate", "dtype": "Utf8", "nullable": false, "unique_count": 1}
],
"sample": [["20250401", "0500", "TMP", "15"]],
"total_rows": 288
}
]
}
5.4 POST /build¶
파이프라인을 실행하고 결과를 반환합니다.
요청¶
{
"spec": "...",
"run_id": "my-run-001"
}
응답 200 (빌드 성공)¶
{
"status": "ok",
"run_id": "my-run-001",
"outcomes": [
{
"source_key": "datago.village_fcst",
"status": "ok",
"stages_completed": ["bronze", "silver", "gold"],
"error": null
}
],
"manifest": "/path/to/dist/my-run-001/manifest.json",
"api_version": "1.0.0"
}
응답 502 (빌드 실패 — upstream 소스 오류 등)¶
{
"status": "failed",
"run_id": "my-run-001",
"outcomes": [
{
"source_key": "datago.village_fcst",
"status": "failed",
"stages_completed": ["bronze"],
"error": "fetch failed: ..."
}
],
"manifest": "/path/to/dist/my-run-001/manifest.json",
"api_version": "1.0.0"
}
5.5 GET /artifacts/{run_id}¶
실행 워크스페이스의 산출물 파일 목록을 반환합니다.
응답 200¶
{
"run_id": "my-run-001",
"files": [
"gold/artifacts/weather_report.md",
"gold/artifacts/data.jsonl",
"manifest.json"
]
}
응답 404¶
{
"error": "run not found: my-run-001"
}
6. 상태와 응답 원칙¶
BuildResult.status는"ok"|"failed"두 값을 가집니다.manifest는 빌드 성공/실패 모두에서 생성 시도됩니다.artifacts는 빌드 완료 후/artifacts/{run_id}엔드포인트로 조회 가능합니다.
7. CLI 대응 관계¶
| CLI | 대응 API |
|---|---|
kpubdata-builder validate spec.yaml |
POST /validate |
kpubdata-builder preview spec.yaml |
POST /preview |
kpubdata-builder build spec.yaml |
POST /build |
8. 구현 현황과 Studio 향 계획¶
본 계약(contract/builder-api.yaml, info.version)은 단일 소스이며, 코드의
kpubdata_builder.service.API_CONTRACT_VERSION과 일치해야 합니다
(test_service_contract가 강제). 소비자는 GET /version으로 계약 버전을 먼저
확인할 수 있고, POST /validate·POST /build 응답에도 api_version이 실립니다.
| 계약 operationId | 상태 | 현재 구현 경로 |
|---|---|---|
validateSpec |
구현됨 | POST /validate (동기) |
previewBuild |
구현됨 | POST /preview (동기) |
createBuild |
구현됨 | POST /build (동기; 계약은 비동기 POST /builds 지향) |
listBuildArtifacts |
구현됨 | GET /artifacts/{run_id} |
listDatasets |
계획(planned)/미구현 | — |
getBuild |
계획(planned)/미구현 | — |
getBuildManifest |
계획(planned)/미구현 | — |
publishArtifacts |
계획(planned)/미구현 | — |
| (메타) | 구현됨 | GET /version → {service, api_version} |
Studio 향 에러 봉투 (계획/미구현)¶
Studio 연동을 위해 향후 구조화된 에러 봉투 형식을 도입할 예정입니다. 현재는 미구현이며, 아래는 목표 형태입니다.
{
"error": {
"code": "INVALID_BUILD_SPEC",
"message": "sources must not be empty",
"details": [
{"field": "sources", "reason": "missing"}
]
}
}
이 형식은 Studio와의 교차 레포 조율이 필요한 후속 작업에서 활성화될 예정입니다.
9. Python API — BuilderService¶
Python 코드에서 직접 사용하는 경우 BuilderService를 통해 HTTP 없이 같은 로직을 호출할 수 있습니다.
from pathlib import Path
from kpubdata_builder.service import BuilderService
service = BuilderService(
output_root=Path("./dist"),
client_factory=lambda: my_kpubdata_client,
)
# 검증
response = service.validate(spec_yaml_str)
# response.status_code: 200 (valid) 또는 400 (error/invalid)
# response.body: {"status": "valid", ...} 또는 {"status": "invalid", "problems": [...]}
# 빌드
response = service.build(spec_yaml_str, run_id="my-run-001")
# response.status_code: 200 (ok) 또는 502 (failed)
# response.body: {"status": "ok"|"failed", "outcomes": [...], ...}
10. 관련 문서¶
| 문서 | 설명 |
|---|---|
| BUILD_SPEC.md | BuildSpec 입력 계약 |
| BUILD_STATE.md | build 상태 머신 |
| BOUNDARY.md | Builder-Studio 경계 |