k-skill/docs/features/fine-dust-location.md
Jeffrey (Dongkyu) Kim ef2c69b81c Replace sops+age encryption with plain dotenv and agent-native credential resolution
Agent environments (OpenClaw, Claude Code, Codex) assume users delegate
credentials to the agent. sops+age added setup friction without real
security benefit since the agent decrypts on every call anyway.

New model: skills declare required env var names; how they are supplied
is up to the agent (own vault, shell env, or ~/.config/k-skill/secrets.env
as the default fallback with 0600 permissions).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 22:12:04 +09:00

128 lines
4.9 KiB
Markdown

# 사용자 위치 미세먼지 조회 가이드
## 이 기능으로 할 수 있는 일
- 지역명/행정구역 힌트로 측정소 후보 찾기
- 단일 측정소 확정이 어려우면 후보 측정소 목록 반환
- 정확한 측정소명으로 재조회
- PM10, PM2.5, 등급, 조회 시각 요약
## 먼저 필요한 것
- [공통 설정 가이드](../setup.md) 완료
- [보안/시크릿 정책](../security-and-secrets.md) 확인
- `k-skill-proxy` 또는 에어코리아 OpenAPI key
## 필요한 환경변수
클라이언트 기본값:
- 기본 external proxy URL: `https://k-skill-proxy.nomadamas.org`
- `KSKILL_PROXY_BASE_URL` 는 override 가 필요할 때만 사용
프록시 없이 direct fallback 으로 쓸 때만:
- `AIR_KOREA_OPEN_API_KEY`
### Credential resolution order
1. **이미 환경변수에 있으면** 그대로 사용한다.
2. **에이전트가 자체 secret vault(1Password CLI, Bitwarden CLI, macOS Keychain 등)를 사용 중이면** 거기서 꺼내 환경변수로 주입해도 된다.
3. **`~/.config/k-skill/secrets.env`** (기본 fallback) — plain dotenv 파일, 퍼미션 `0600`.
4. **아무것도 없으면** 유저에게 물어서 2 또는 3에 저장한다.
## 입력값
- 기본: 지역명/행정구역 힌트(`regionHint`)
- 재조회: 정확한 측정소명(`stationName`)
## 기본 흐름
1. `KSKILL_PROXY_BASE_URL` 가 있으면 먼저 `k-skill-proxy``/v1/fine-dust/report` endpoint 를 호출합니다.
2. `regionHint` 가 들어오면 프록시는 먼저 시도명을 추출하고, `getCtprvnRltmMesureDnsty` 로 해당 시도 측정소 목록을 확보합니다.
3. region token 이 시도 내 실제 측정소명과 **유일하게** 대응하면 그 측정소로 `getMsrstnAcctoRltmMesureDnsty` 를 호출합니다.
4. 단일 측정소 확정이 어려우면 `ambiguous_location``candidate_stations` 를 반환합니다.
5. 클라이언트/사용자는 후보 중 정확한 측정소명으로 다시 `/v1/fine-dust/report?stationName=...` 를 호출합니다.
6. PM10, PM2.5, 등급, 조회 시점/조회 시각을 함께 요약합니다.
프록시 예시:
```bash
python3 scripts/fine_dust.py report --region-hint "서울 강남구" --json
```
후보 반환 예시:
```bash
curl -fsS --get 'https://k-skill-proxy.nomadamas.org/v1/fine-dust/report' \
--data-urlencode 'regionHint=광주 광산구'
```
정확한 측정소명 재조회:
```bash
curl -fsS --get 'https://k-skill-proxy.nomadamas.org/v1/fine-dust/report' \
--data-urlencode 'stationName=우산동(광주)'
```
원본 AirKorea endpoint 형태를 거의 그대로 쓰고 싶으면 passthrough endpoint 도 사용할 수 있습니다. 별도 client API 는 불필요하고, 프록시가 `serviceKey` 만 서버에서 주입합니다.
```bash
curl -fsS --get 'https://k-skill-proxy.nomadamas.org/B552584/ArpltnInforInqireSvc/getMsrstnAcctoRltmMesureDnsty' \
--data-urlencode 'returnType=json' \
--data-urlencode 'numOfRows=1' \
--data-urlencode 'pageNo=1' \
--data-urlencode 'stationName=강남구' \
--data-urlencode 'dataTerm=DAILY' \
--data-urlencode 'ver=1.4'
```
## 예시
지역 기반 direct fallback:
```bash
curl -sG "http://apis.data.go.kr/B552584/MsrstnInfoInqireSvc/getMsrstnList" \
--data-urlencode "serviceKey=${AIR_KOREA_OPEN_API_KEY}" \
--data-urlencode "returnType=json" \
--data-urlencode "numOfRows=50" \
--data-urlencode "pageNo=1" \
--data-urlencode "addr=서울 강남구"
```
실시간 측정값:
```bash
curl -sG "http://apis.data.go.kr/B552584/ArpltnInforInqireSvc/getMsrstnAcctoRltmMesureDnsty" \
--data-urlencode "serviceKey=${AIR_KOREA_OPEN_API_KEY}" \
--data-urlencode "returnType=json" \
--data-urlencode "numOfRows=100" \
--data-urlencode "pageNo=1" \
--data-urlencode "stationName=중구" \
--data-urlencode "dataTerm=DAILY" \
--data-urlencode "ver=1.4"
```
helper script 반복 검증:
```bash
python3 scripts/fine_dust.py report \
--station-file scripts/fixtures/fine-dust-stations.json \
--measurement-file scripts/fixtures/fine-dust-measurements.json \
--region-hint "서울 강남구"
```
## fallback / 대체 흐름
- 지역명/행정구역을 먼저 받습니다
- 단일 측정소를 확정하지 못하면 후보 측정소 목록을 돌려줍니다
- 사용자는 후보 중 하나를 선택해 `stationName` 으로 다시 조회합니다
- 측정소 목록 API가 403 이어도 `getCtprvnRltmMesureDnsty` 와 측정소별 실측 API 조합으로 우회합니다
## 주의할 점
- 실시간 수치라 조회 시각을 같이 적어야 합니다
- PM10/PM2.5 값이 `-` 이거나 비정상이면 등급도 함께 재확인합니다
- API 가 `khaiGrade` 를 비워 보내면 통합대기등급은 `정보없음` 으로 표시합니다
- regionHint 는 자연어이므로 단일 측정소가 안 잡히는 경우가 자주 있습니다
- hosted 모드에서는 upstream AirKorea key 를 클라이언트에 배포하지 않고 proxy 에만 둡니다