"""무엇을 이미 알렸는지 기억한다 — **DB 가 아니라 파일이다.** ★ 표를 만들지 않은 이유: 이 모듈은 `solution/backend` 를 고치지 않는다는 제약 아래 있고 (geo/README.md '제약'), 스키마는 `init.sql` 과 마이그레이션 **양쪽**을 고쳐야 한다. 그리고 여기 담기는 건 "마지막으로 통보한 시각" 하나라, 표가 필요해지는 종류가 아니다. 추이·리포트가 요구사항이 되면 그때 표를 정한다. ★ 잃어도 치명적이지 않게 설계한다. 파일이 사라지면 **전부 다시 통보**할 뿐이다 — 같은 URL 을 다시 알리는 건 규격상 정상이고(변경 통보), 손해는 호출 몇 번이다. 반대로 "안 알린 것을 알렸다고 기억" 하는 쪽이 위험해서, **통보 성공 뒤에만 기록한다.** ★ 자리는 `GEO_STATE_DIR`. 기본은 `geo/state/` 이고 git 에 올리지 않는다. """ import json import os from pathlib import Path STATE_DIR_ENV = "GEO_STATE_DIR" def state_dir() -> Path: raw = os.environ.get(STATE_DIR_ENV, "").strip() return Path(raw) if raw else Path(__file__).resolve().parent / "state" def _path(name: str) -> Path: return state_dir() / f"{name}.json" def load(name: str) -> dict: path = _path(name) if not path.is_file(): return {} try: return json.loads(path.read_text("utf-8")) except (OSError, ValueError): # 깨진 파일 때문에 통보가 멈추면 안 된다 — 빈 상태로 보고 다시 알린다. return {} def save(name: str, data: dict) -> None: path = _path(name) path.parent.mkdir(parents=True, exist_ok=True) # 같은 디렉토리에 쓰고 바꿔치기한다 — 쓰는 중에 죽어도 반쪽 파일이 남지 않는다. tmp = path.with_suffix(".tmp") tmp.write_text(json.dumps(data, ensure_ascii=False, indent=2), "utf-8") tmp.replace(path)