o2o-site-AEO/docs/WEATHER.md
김성경 cd771719df [fix] site,solution/backend: 날씨 상태 라벨을 코드별 고유값으로 — 구간 뭉치기 제거
WMO weather_code 51~57(이슬비 3단계), 61~67(비/어는비 혼재) 등을 "이슬비"·"비"
같은 큰 구간으로 뭉쳐 표시하던 것을 코드 하나당 고유 라벨로 바꿨다. 서버
프리렌더 스냅샷(site_payload._WEATHER_CONDITION_BY_CODE)과 브라우저 재조회
(use-live-weather.ts WEATHER_CONDITION_BY_CODE)가 같은 표를 봐야 하이드레이션
전후로 문구가 안 바뀐다는 기존 불변식은 유지한다.

- docs/WEATHER.md: 27개 코드 전체를 "구간→분류" 표에서 "코드→고유 라벨" 표로 재작성
- weather_notes.json: 코드별 문구 갱신
- use-live-weather.ts/.test.ts, weather.test.tsx: 새 라벨 반영
2026-09-22 08:36:09 +09:00

82 lines
5.5 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 오늘의 날씨
`Open-Meteo → /v1/local/weather → useLiveWeather → WeatherSection`
관측값은 기존 API를 사용하며 브라우저에서 10분마다 갱신한다. 조회 실패 시 마지막 관측값과
관측 시각을 유지한다. 날씨 문구는 API 요청마다 생성하지 않는다. **API 키 없음** — Open-Meteo
는 키 발급 없이 쓰는 무료 공개 엔드포인트다(`services/external/open_meteo.py`).
## Open-Meteo 응답 → 내부 스냅샷
`GET https://api.open-meteo.com/v1/forecast?latitude=&longitude=&current=temperature_2m,weather_code,wind_speed_10m&timezone=auto`
원본 `current` 블록(`temperature_2m`·`weather_code`·`wind_speed_10m`·`time`)을 어댑터가
`{temperature, weather_code, wind_speed, observed_at, timezone, latitude, longitude}`
정규화한다(`open_meteo.py:fetch_current_weather`). `weather_code`는 WMO 표준 정수 코드 그대로
저장·전달되고, 하늘 상태 문구로 바꾸는 건 아래 두 곳뿐이다 — **반드시 같은 표여야 한다**
(하이드레이션 전엔 백엔드 값, 후엔 브라우저 값을 쓰는데 표가 다르면 같은 날씨인데 문구가 바뀐다):
- 서버: `site_payload._WEATHER_CONDITION_BY_CODE` (조회는 `_weather_condition()`) — 프리렌더 스냅샷에 쓰인다.
- 브라우저: `use-live-weather.ts:WEATHER_CONDITION_BY_CODE` (조회는 `condition()`) — 10분마다 재조회할 때 쓰인다.
둘 다 **코드마다 고유 라벨**을 반환하는 딕셔너리 조회다(구간 검사가 아니다) — 코드 하나가
분류 하나에 정확히 대응하므로 "51~57 은 다 이슬비" 식으로 뭉치지 않는다.
| 코드 | WMO 의미(영어) | 분류(=조건 라벨) |
|---|---|---|
| 0 | Clear sky | 맑음 |
| 1 | Mainly clear | 대체로 맑음 |
| 2 | Partly cloudy | 구름 조금 |
| 3 | Overcast | 흐림 |
| 45 | Fog | 안개 |
| 48 | Depositing rime fog | 착빙성 안개 |
| 51 | Drizzle: Light intensity | 가벼운 이슬비 |
| 53 | Drizzle: Moderate intensity | 보통 이슬비 |
| 55 | Drizzle: Dense intensity | 강한 이슬비 |
| 56 | Freezing Drizzle: Light intensity | 가벼운 착빙성 이슬비 |
| 57 | Freezing Drizzle: Dense intensity | 강한 착빙성 이슬비 |
| 61 | Rain: Slight intensity | 약한 비 |
| 63 | Rain: Moderate intensity | 보통 비 |
| 65 | Rain: Heavy intensity | 강한 비 |
| 66 | Freezing Rain: Light intensity | 약한 착빙성 비 |
| 67 | Freezing Rain: Heavy intensity | 강한 착빙성 비 |
| 71 | Snow fall: Slight intensity | 약한 눈 |
| 73 | Snow fall: Moderate intensity | 보통 눈 |
| 75 | Snow fall: Heavy intensity | 강한 눈 |
| 77 | Snow grains | 싸라기눈 |
| 80 | Rain showers: Slight | 약한 소나기 |
| 81 | Rain showers: Moderate | 보통 소나기 |
| 82 | Rain showers: Violent | 강한 소나기 |
| 85 | Snow showers: Slight | 약한 소나기눈 |
| 86 | Snow showers: Heavy | 강한 소나기눈 |
| 95 | Thunderstorm: Slight or moderate | 뇌우 |
| 96 | Thunderstorm with slight hail | 약한 우박 뇌우 |
| 99 | Thunderstorm with heavy hail | 강한 우박 뇌우 |
이 28개가 Open-Meteo `weather_code`의 전체 정의 값이다 — 표에 없는 값(파싱 실패 포함)만
안전하게 `흐림`으로 떨어진다(실제로는 도달하지 않는 방어 분기).
`weatherMood()`(`derive.ts`)는 위 28종을 화면 배경 그림용으로 다시 4종(맑음/흐림/비/눈)으로
뭉친다 — 정규식 기반이라 새 분류를 추가해도 대개 자동으로 걸린다(예: "가벼운 착빙성 이슬비"는
`/비|우|소나기/` 패턴에 "비"가 들어 있어 `비`로 걸리고, "약한 소나기눈"은 `/눈|설/` 이 먼저 걸려
`눈`이 된다 — 검사 순서가 그래서 중요하다). `WeatherSection``skyKey`는 노트에 그 조건 키가
실제로 있으면(`notes?.noteSets?.[condition]`) 그 조건 그대로 쓰고, 없으면(옛 payload 등)
`mood`로 내려간다 — 화이트리스트를 따로 유지하지 않는 일반화된 조회다.
`weather_notes.json → weather_notes.py → site_payload._weather → noteSets/tempNoteSets`
하늘 28종(위 표의 "분류" 열 전체)·기온 5구간에 각 5문구를 싣는다(28×5+5×5=165줄, 전부
고유해야 순환이 막히지 않는다). 첫 렌더는 첫 문장으로 고정해 하이드레이션을 맞추고,
브라우저에서는 무작위 시작 후 20초마다 하늘·기온 두 줄을 한 타이머로 같이 골라 한 바퀴 안에서
중복 없이 순환한다(`useWeatherNotes`). 기온 구간은 기존 `weatherBand`의 30·25·20·10도다.
**세분화 원칙**: 강도(약/보통/강)만 다른 코드도 문구를 따로 쓴다 — 약한 비는 "우산 하나면
충분", 강한 비는 "이동을 미루라"처럼 안내 자체가 달라지기 때문이다. 착빙성(어는 비/이슬비)은
안개·비·이슬비와 별도로 갈랐다 — 노면 결빙이라는, 세기와는 다른 축의 위험이라 "도로가
얼어붙을 수 있으니" 식의 안전 안내가 필요하다(2026-09-18).
목업 README 2.3의 순환 계약을 제품으로 옮겼다. 군산 전용 시설·장소를 다른 사업장에 복사하지
않도록 기본 문구는 장소·시설·영업시간을 주장하지 않는 공통 안내로 구성한다. **LLM 생성이 아니다.**
지역별 장소 추천을 자동 생성하는 작업은 포함하지 않았다. 목업의 수기 문구·산출물은 변경하지 않는다.
옛 단일 `note`·`notes`·`tempNotes` payload도 계속 지원한다. 이미 발행된 사이트는 재발행해야 반영된다.