Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# 변경 이력

## 2026-10-23 — typed pagination receipt

- `ResponseMetadata.pagination`/`PaginationReceipt`로 페이지 완결 정보를 가산한다. 제공자 metadata/행 수 불일치와 중간 NO_DATA의 부분 성공을 거부한다. metadata 생략 응답의 기존 값·첫 NO_DATA03 빈 결과는 유지한다.

`python-kma-api`의 주요 변경 사항을 기록합니다.

## 0.1.0 - 미배포
Expand Down
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ Korea Meteorological Administration(KMA, 기상청) 공공데이터포털과 API
| 인증키 로딩 | `api_key_for_gateway`, `env_names_for_gateway`, `load_local_env` |
| 위치 값 객체 | `LatLon`, `GridPoint`, `normalize_location` |
| 좌표 변환 | `to_grid`, `to_latlon`, `wgs84_to_kma_grid`, `kma_grid_to_wgs84` |
| 응답 모델 | `WeatherSnapshot`, `ForecastItem`, `ForecastTimepoint`, `MidForecastItem`, `DataGoKrItem`, `BeachForecastItem`, `BeachWaveHeight`, `BeachWaterTemperature`, `BeachTideItem`, `BeachSunTime`, `ResponseMetadata` |
| 응답 모델 | `WeatherSnapshot`, `ForecastItem`, `ForecastTimepoint`, `MidForecastItem`, `DataGoKrItem`, `BeachForecastItem`, `BeachWaveHeight`, `BeachWaterTemperature`, `BeachTideItem`, `BeachSunTime`, `ResponseMetadata`, `PaginationReceipt` |
| timeline/pagination/cache | `pivot_forecast_items`, `has_next_page`, `next_page_no`, `iter_pages`, `make_cache_key`, `base_available_at`, `cache_expire_at`, `latest_mid_fcst_base`, `latest_mid_fcst_time`, `sanitize_request_params` |
| enum/라벨 | `KmaEndpoint`, `WeatherCategory`, `SkyCode`, `ObservedPrecipitationType`, `ForecastPrecipitationType`, `label_for`, `unit_for`, `parse_amount` |
| 예외 | `KmaError`, `KmaAuthError`, `KmaRequestError`, `KmaServerError`, `KmaParseError` |
Expand Down Expand Up @@ -935,3 +935,10 @@ KMA 원천 데이터의 저작권과 이용조건은 기상청 및 공공데이
## 변경 이력

- `0.1.0`: 초기 패키지 구조, KMA 단기예보 클라이언트, 좌표 변환, 시간 계산, 문서 보강.


### typed 응답의 페이지 완결 증거

`now()`의 `snapshot.metadata.pagination` 또는 비어 있지 않은 예보 item의 `metadata.pagination`은 `PaginationReceipt`다. `expected_total_count`, `received_item_count`, `page_count`, `complete`를 제공한다. 제공자가 모든 페이지에 일관된 `pageNo/numOfRows/totalCount`를 명시하고 실제 행 수가 일치할 때만 `complete=True`다. 수치·페이지 순서·중간 빈 응답 불일치는 `KmaParseError`로 반환하여 부분 집합을 정상 전체로 내보내지 않는다. metadata가 생략된 기존 응답은 기존 값을 반환하지만 `complete=False`다.

첫 `NO_DATA(03)`는 기존 정상 빈 결과이고 now snapshot에는 명시0 receipt가 남는다. 예보가 빈 list이면 item metadata도 없으므로 빈 list만으로 정상0 완결을 승인하지 않는다. 페이지 완결은 항목 유일성·의미·관측/예보 revision·downstream 데이터셋 전체 권위를 보장하지 않는다. `collected_at`은 실제 조회 시각이며 데이터 발표 revision으로 쓰지 않는다.
5 changes: 5 additions & 0 deletions docs/api-coverage.md
Original file line number Diff line number Diff line change
Expand Up @@ -214,3 +214,8 @@ data.go.kr의 KMA REST API는 `http://apis.data.go.kr/1360000/{service}/{operati
- **범용 클라이언트까지 포함하면 data.go.kr 임의 service/operation과 APIHub `/api/...` path를 호출할 수 있습니다.**
- **APIHub 470개는 endpoint별 함수 이름을 제공하지만, 모든 응답을 endpoint별 Pydantic 모델로 강제 변환하지는 않습니다.**
- **위치/코드 타입 계층은 endpoint 개수를 늘리는 항목은 아니며, 외부 프로그램에서 좌표계와 category 문자열을 안정적으로 다루기 위한 public API입니다.**


### typed pagination receipt (2026-10-23)

기존 endpoint 개수는 그대로다. 공개 모델 `PaginationReceipt`와 `ResponseMetadata.pagination`을 가산해 typed 조회의 원천 총수·실제 행 수·페이지 수·완결 여부를 제공한다. metadata 없는 레거시는 complete=False, 명시 불일치는 KmaParseError다. 예보 빈 list에는 item metadata가 없으며 receipt만으로 source 전체 권위/의미 유일성은 보장하지 않는다.
5 changes: 5 additions & 0 deletions docs/decisions.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,8 @@
다른 API 라이브러리와 같은 AsyncTokenBucket 로직을 적용해 재시도·리다이렉트도 합산 제한한다.
파싱·모델·카탈로그와 CLI/UI 진입점은 일반 함수로 유지한다. 기존 동기 소비자는 await,
async for, async with로 전환해야 한다. 자세한 설정은 async-tps.md에 기록한다.


## ADR-007: typed pagination receipt와 불일치 거부 (2026-10-23)

전체 페이지 순회 종료를 완결 증거로 사용하지 않는다. typed 조회에 공개 `PaginationReceipt`를 가산하고 페이지 번호·총수/크기 일관성·실제 행 수를 검증한다. 명시 metadata가 틀리면 typed parse 오류이며, metadata가 없는 레거시는 값 호환을 유지하되 complete=False다. NO_DATA03 정상 빈 계약은 유지하고 중간03의 부분 집합은 거부한다. receipt는 transport pagination의 증거이며 데이터 의미/유일성·downstream authoritative replacement는 소비자가 별도로 검증한다. 기존 서비스키·async·TPS·endpoint·시각 유틸은 바꾸지 않는다.
7 changes: 7 additions & 0 deletions docs/journal.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# JOURNAL — 작업 일지

## 2026-10-23 — typed 페이지 완결 receipt (구현·검증 중)

- Weather 성공 export가 부분 KMA 응답을 전체 집합으로 오인하지 않도록 `ResponseMetadata.pagination`과 공개 frozen `PaginationReceipt`를 추가한다. typed now/forecast는 페이지 echo·고정 totalCount/numOfRows·페이지별/누적 실제 행 수를 검증한다. 불일치는 typed KmaParseError이며 metadata 생략 레거시는 값을 유지하되 complete=False다.
- 첫 NO_DATA03은 기존 정상 빈 결과를 유지하며 now snapshot에는 명시0 receipt가 남는다. 중간 페이지03은 부분 성공으로 반환하지 않는다. 빈 예보 list에는 item metadata가 없으므로 그것만으로 정상0 완결을 승인하지 않는다.
- pagination complete는 원천 페이지/행 수 증거이며 항목 의미·유일성·발표 revision·downstream 전체 scope 권위를 대신하지 않는다. Weather의 collector/3B/실제API 배선·운영 적용은 별도다.
- Windows Python3.10 집중38 PASS·전체263 PASS/12 live deselected/14 subtests, Ruff·mypy26개파일 PASS. CI·2리뷰·live는 진행 중이다. 기존 로컬 .venv에 pytest-asyncio가 없어 거절된 환경 실패를 별도 전용 dev venv로 해소했고 기존 .venv/미커밋 .gitignore/.claude는 변경하지 않았다.

## 2026-09-14 — 비동기 전용 API와 공통 TPS

- 원격 최신 main 기준으로 Kma/DataGoKr/ApiHub와 생성 endpoint 470개를 비동기로 통합했다.
Expand Down
5 changes: 5 additions & 0 deletions docs/repeated-mistakes.md
Original file line number Diff line number Diff line change
Expand Up @@ -281,3 +281,8 @@ Get-Content -Path docs/repeated-mistakes.md -Encoding UTF8
```

**방지 테스트:** 한글이 깨져 보이면 먼저 UTF-8 명시 명령으로 다시 읽고, 파일 자체가 깨졌는지는 diff나 테스트 결과로 확인합니다.


## 2026-10-23 — 페이지 반복 종료와 완결을 혼동하지 않는다

증상: 요청 페이지가 끝났어도 실제 rows와 totalCount가 다른 부분 응답을 정상 typed 값으로 반환할 수 있었다. 규칙: echo/고정 metadata와 페이지별·누적 수를 검증하고 공개 receipt에 완결 여부를 남긴다. metadata 생략은 unknown이며 첫 NO_DATA와 중간 NO_DATA를 구분한다. `tests/test_typed_pagination_receipt.py`가 short page·수치/순서 변경·중간03·legacy·민감값 미노출을 검증한다.
7 changes: 7 additions & 0 deletions docs/resume.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# RESUME — 작업 재개 가이드

## 2026-10-23 — typed 페이지 완결 receipt (구현·검증 중)

- Weather 성공 export가 부분 KMA 응답을 전체 집합으로 오인하지 않도록 `ResponseMetadata.pagination`과 공개 frozen `PaginationReceipt`를 추가한다. typed now/forecast는 페이지 echo·고정 totalCount/numOfRows·페이지별/누적 실제 행 수를 검증한다. 불일치는 typed KmaParseError이며 metadata 생략 레거시는 값을 유지하되 complete=False다.
- 첫 NO_DATA03은 기존 정상 빈 결과를 유지하며 now snapshot에는 명시0 receipt가 남는다. 중간 페이지03은 부분 성공으로 반환하지 않는다. 빈 예보 list에는 item metadata가 없으므로 그것만으로 정상0 완결을 승인하지 않는다.
- pagination complete는 원천 페이지/행 수 증거이며 항목 의미·유일성·발표 revision·downstream 전체 scope 권위를 대신하지 않는다. Weather의 collector/3B/실제API 배선·운영 적용은 별도다.
- Windows Python3.10 집중38 PASS·전체263 PASS/12 live deselected/14 subtests, Ruff·mypy26개파일 PASS. CI·2리뷰·live는 진행 중이다. 기존 로컬 .venv에 pytest-asyncio가 없어 거절된 환경 실패를 별도 전용 dev venv로 해소했고 기존 .venv/미커밋 .gitignore/.claude는 변경하지 않았다.

2026-09-14: 비동기 전용 및 공통 TPS 전환 완료. 검증/실제 API 결과는 verification-async-tps.md에 기록한다.

새 에이전트 세션이 시작될 때 "지금 어디까지 했고, 다음은 뭐 하면 되나"를 한 화면에서 답한다.
Expand Down
7 changes: 7 additions & 0 deletions kma-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -348,3 +348,10 @@ label_for(WeatherCategory.PRECIPITATION_TYPE, "4", endpoint=KmaEndpoint.VILAGE_F
- [ ] `PCP`/`SNO`를 무조건 float로 변환하지 않는다.
- [ ] `PTY` 매핑은 endpoint-aware로 처리한다.
- [ ] 실제 네트워크 호출 없는 단위 테스트를 먼저 작성한다.


## typed 페이지 일관성과 receipt

`KmaClient.now/forecast_short/forecast`의 내부 수집은 한 격자/base 선택에 대해 일관된 페이지 번호·크기·총건수를 검증한다. 페이지별 예상 행 수와 누적 수가 맞지 않거나 중간 NO_DATA면 `KmaParseError(failure_kind="parse",retryable=False)`다. 기존 페이지 상한을 유지하고 인증·rate limiter·시각 선택은 바꾸지 않는다.

`ResponseMetadata.pagination`의 frozen `PaginationReceipt`는 선언된 totalCount, 실제 수, 페이지 수와 완결 여부를 보존한다. metadata 생략 응답은 기존 호환을 유지하면서 완결로 표시하지 않는다. 첫 NO_DATA03은 명시0 증거이며, 빈 예보 list의 반환형은 그대로라 별도 source 완결 증거 없이 downstream 전체 삭제에 쓰지 않는다. 이 증거만으로 데이터 row의 semantic uniqueness나 source 전체 권위를 승인하지 않는다.
3 changes: 2 additions & 1 deletion src/kma/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
)
from .grid import kma_grid_to_wgs84, to_grid, to_latlon, wgs84_to_kma_grid
from .locations import GridPoint, LatLon, normalize_location
from .metadata import ResponseMetadata, make_cache_key, sanitize_request_params
from .metadata import PaginationReceipt, ResponseMetadata, make_cache_key, sanitize_request_params
from .missing import KMA_MISSING_ABS_THRESHOLD, is_missing
from .models import (
AsosDailyItem,
Expand Down Expand Up @@ -108,6 +108,7 @@
"LatLon",
"MidForecastItem",
"ObservedPrecipitationType",
"PaginationReceipt",
"ResponseMetadata",
"SENSITIVE_KEYS",
"SkyCode",
Expand Down
Loading
Loading