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
6 changes: 4 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# AGENTS.md

네트워크 I/O는 비동기 전용이다(ADR-006). `await`/`async for`/`async with`를 사용하며, Async 접두사 별칭·aio 팩터리·동기 네트워크 경로를 추가하지 않는다. JSON/XML `resultCode=03`은 최신 계약에 따라 빈 결과로 정규화한다.

## 문서 언어 정책

이 저장소의 모든 Markdown/RST 문서는 한글로 작성합니다. 공식 API 필드명, 코드 식별자, 명령어, URL, provider 원문처럼 그대로 보존해야 하는 값만 영어를 유지합니다. 새 문서나 기존 문서를 수정할 때도 이 규칙을 우선합니다.
Expand Down Expand Up @@ -57,7 +59,7 @@ PC 개발은 Windows 호스트에서 직접 진행합니다. 본 저장소는 Py
3. **기본 테스트에서 실제 API 호출 금지** — 네트워크 호출 없는 mock/fixture 기반으로 검증해야 합니다. 실제 호출 테스트를 추가할 경우 `DATA_GO_KR_SERVICE_KEY`가 있을 때만 실행되도록 `integration` marker를 사용합니다.
4. **`nx`/`ny`를 위도/경도로 취급 금지** — WGS84 좌표는 항상 `lat/lon` 순서로 다루며, KMA 격자 좌표(`nx/ny`)와 엄격히 구분합니다. 외부 프로그램용 위치 입력은 `LatLon`/`GridPoint` 또는 `location=`으로 표준화합니다.
5. **`PCP`, `SNO` 범주 문자열을 무조건 float로 변환 금지** — `"1.0mm 미만"`, `"30.0~50.0mm"`, `"강수없음"` 같은 범주 문자열은 무리하게 숫자로 바꾸지 않고 보존합니다. 대표값이 필요할 때만 `parse_amount()`를 제공합니다.
6. **KMA result code 실패를 빈 리스트 성공처럼 반환 금지** — `resultCode != "00"`은 반드시 명시적인 typed exception으로 surface합니다.
6. **KMA result code 실패를 빈 리스트 성공처럼 반환 금지** — `resultCode`가 "00"/"03" 이외인 경우는 반드시 명시적인 typed exception으로 surface합니다.
7. **data.go.kr와 APIHub의 인증 파라미터 혼용 금지** — data.go.kr 키는 `DATA_GO_KR_SERVICE_KEY`, APIHub 키는 `KMA_APIHUB_AUTH_KEY`로 엄격히 분리하여 사용합니다.
8. **APIHub endpoint가 항상 JSON을 반환한다고 가정 금지** — 텍스트, 이미지, 바이너리 응답이 섞여 있으므로 `response_kind`나 `content` 타입을 명확히 처리합니다.
9. **불필요한 wrapper/adapter 계층 추가 금지** — 단순 전달용 wrapper, 장기 호환 alias, 임시 facade를 지양하고, 다른 라이브러리에 검증된 구현이 있으면 라이선스와 출처를 확인한 뒤 프로젝트 내부 구현으로 직접 반영합니다.
Expand Down Expand Up @@ -122,7 +124,7 @@ PC 개발은 Windows 호스트에서 직접 진행합니다. 본 저장소는 Py
- `dataType=JSON`을 기본으로 둡니다.
- `pageNo`, `numOfRows` 기본값이 있습니다.
- fake session 테스트가 요청 파라미터를 검증합니다.
- `resultCode != "00"`은 typed exception입니다.
- `resultCode`가 "00"/"03" 이외인 경우는 typed exception입니다.

### data.go.kr 범용 클라이언트

Expand Down
47 changes: 31 additions & 16 deletions AI_AGENT_GUIDE.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# AI 에이전트 가이드: python-kma-api (kma)

네트워크 I/O는 비동기 전용이다(ADR-006). `await`/`async for`/`async with`를 사용하며, Async 접두사 별칭·aio 팩터리·동기 네트워크 경로를 추가하지 않는다. JSON/XML `resultCode=03`은 최신 계약에 따라 빈 결과로 정규화한다.

이 라이브러리(`kma`)를 임포트하여 사용하는 외부 기상 정보 서비스 및 소비자 앱(예: TripMate, tour-map, kraddr 등)의 코드를 생성하는 AI 코딩 어시스턴트(Cursor, Copilot, ChatGPT, Claude Code 등)를 위한 컨텍스트 문서입니다.

> **본 저장소(`python-kma-api`) 자체를 수정하려는 에이전트는 다른 문서를 봅니다**:
Expand All @@ -26,30 +28,43 @@

### 2.1. `KmaClient` (단기예보 3종 및 버전)

동기 호출은 `KmaClient.from_env()`, 비동기 호출은 `KmaClient.aio_from_env()` 패턴을 활용합니다.
`KmaClient.from_env()`로 생성하고 `async with`/`await`로 호출합니다.

```python
import asyncio
from kma import KmaClient, LatLon, GridPoint

# 동기 호출 예시
with KmaClient.from_env() as kma:
# 1. 초단기실황 (현재 날씨 스냅샷)
snap = kma.forecast.now(lat=37.5665, lon=126.9780) # 서울시청
print(f"기온: {snap.temperature}°C, 하늘상태: {snap.sky_label}, 강수: {snap.precipitation_label}")

async def main() -> None:
# 조회 예시
async with KmaClient.from_env() as kma:
# 1. 초단기실황 (현재 날씨 스냅샷)
snap = (await kma.forecast.now(lat=37.5665, lon=126.9780)) # 서울시청
print(f"기온: {snap.temperature}°C, 하늘상태: {snap.sky_label}, 강수: {snap.precipitation_label}")

# 2. 단기예보 (향후 3일 일기예보 목록)
items = kma.forecast.vilage(location=LatLon(37.5665, 126.9780))
for item in items[:5]:
print(item.forecast_at, item.category, item.value, item.label)
# 2. 단기예보 (향후 3일 일기예보 목록)
items = (await kma.forecast.vilage(location=LatLon(37.5665, 126.9780)))
for item in items[:5]:
print(item.forecast_at, item.category, item.value, item.label)


asyncio.run(main())
```

```python
# 비동기 호출 예시
import asyncio
from kma import KmaClient

async with KmaClient.aio_from_env() as kma:
# 3. 초단기예보 (향후 6시간 대략 예보)
short_items = await kma.forecast.short(nx=60, ny=127)

async def main() -> None:
# 비동기 호출 예시

async with KmaClient.from_env() as kma:
# 3. 초단기예보 (향후 6시간 대략 예보)
short_items = await kma.forecast.short(nx=60, ny=127)


asyncio.run(main())
```

---
Expand All @@ -69,7 +84,7 @@ async with KmaClient.aio_from_env() as kma:
kma.forecast.now(location=LatLon(37.5665, 126.9780))
kma.forecast.now(location=GridPoint(60, 127))
kma.forecast.now(location={"latitude": 37.5665, "longitude": 126.9780})
```
```

### 3.2. PCP/SNO 강수량/적설량의 무리한 수치 변환 금지
- 기상청 단기예보 응답에서 강수량(`PCP`), 적설량(`SNO`)은 `"1.0mm 미만"`, `"30.0~50.0mm"`, `"강수없음"` 같은 범주 문자열을 반환합니다.
Expand All @@ -81,7 +96,7 @@ async with KmaClient.aio_from_env() as kma:
parse_amount("1.0mm 미만") # 0.5 (반환됨)
parse_amount("30.0~50.0mm") # 40.0
parse_amount("강수없음") # 0.0
```
```

### 3.3. KST 발표시각의 이해
- 기상청 API는 실시간 기상 데이터를 반환하지 못하고, 특정 발표 주기(정각, 30분, 단기예보 하루 8회)가 있으며 서버 반영 지연시간(10분~40분)이 있습니다.
Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@

## 0.1.0 - 미배포

### 비동기 전용 전환

- 네트워크 클라이언트를 단일 async API로 통합하고 Async/aio 별칭을 제거했다.
- 공통 AsyncTokenBucket의 max_rps/rate_limiter로 첫 송신·재시도·redirect TPS를 제어한다.
- 생성기·CLI·디버그·페이지도 같은 계약을 적용하며 기존 모델·파싱·NO_DATA03 동작을 유지한다.


### 수정

- asyncio 전환 재검증을 위한 2인 적대적 리뷰어 서브에이전트(동시성/자원관리 관점, 보안/데이터
Expand Down
Loading
Loading