Skip to content

feat: Streamlit 디버그 UI를 examples/streamlit_debug_ui.py 템플릿으로 통합 - #27

Merged
digitie merged 1 commit into
mainfrom
feat/unify-streamlit-debug-ui
Aug 29, 2026
Merged

digitie merged 1 commit into
mainfrom
feat/unify-streamlit-debug-ui

Conversation

@digitie

@digitie digitie commented Aug 29, 2026

Copy link
Copy Markdown
Owner

요약

이번 세션에서 여러 저장소에 걸쳐 정한 표준 Streamlit 디버그 UI 템플릿(원본:
python-khoa-api의 examples/streamlit_debug_ui.py + src/khoa/debug.py)에
맞춰, 이 저장소의 tools/debug_streamlit.py(780줄, operation별 하드코딩
if service == ... 분기, Fixture 탭 미구현, APIHub 470개 endpoint 전부
실행 불가)를 새로 짰습니다. 이 저장소는 카탈로그 규모(data.go.kr 160개
operation + APIHub 470개 endpoint = 630개+)가 다른 저장소보다 훨씬 커서,
완벽한 커버리지보다 3단 계단식 UI 골격 + 대표 엔드포인트가 실제로 동작하는
수준
을 목표로 했습니다.

백엔드 선행 작업 (배경 격차 채우기)

  • src/kma/debug.py 신설 — khoa의 debug.py 구조를 그대로 따라
    DebugRun dataclass, jsonable(), redact_sensitive()(dict/list 재귀
    마스킹 + 문자열 값 내 credential query 패턴까지 이중으로 정리),
    debug_error()({type, message, traceback} + KmaError면
    provider/endpoint/status_code/result_code/failure_kind/retryable 추가),
    save_fixture()를 만들었습니다. 이전에는 이 패키지에 이런 공용 debug
    헬퍼가 전혀 없었습니다.
  • ApiCatalogEntry에 파라미터 메타데이터 필드 추가 —
    required_params/optional_params/param_defaults/response_kind/
    endpoint_path. 기존 필드/동작은 그대로 유지해 api_catalog()를 쓰는
    기존 테스트(len(rows) == 208 등)는 전부 그대로 통과합니다.
  • apihub_endpoint_catalog() 신설 — APIHub apiList.do/
    generateAPIUrl.do에서 수집한 470개 endpoint(APIHUB_ENDPOINTS)를
    실행 가능한 ApiCatalogEntry row로 노출합니다. 기존 api_catalog()가
    반환하던 apihub "LINK" placeholder dataset(실행 불가, service/operation
    없음)과는 별도 함수로 분리했습니다 — 섞으면 기존 dataset 카운트
    테스트가 깨지고, 애초에 서로 다른 두 소스(data.go.kr 카탈로그 vs
    APIHub 스크래핑 결과)라 의미도 다릅니다.
  • 제네릭 debug_fetch 메서드 추가
    • DataGoKrClient.debug_fetch(service, operation, params, ...) —
      기존 request_with_metadata()를 감싸 타이밍/trace/구조화 에러를
      붙이고, 성공하면 row를 실제 DataGoKrItem Pydantic 모델로 검증까지
      한 뒤 DebugRun으로 반환합니다.
    • ApiHubClient.debug_fetch_endpoint(spec, params) — ApiHubClient
      (hand-maintained 모듈)에 추가했습니다. apihub_endpoints.py는
      tools/update_apihub_endpoints.py가 통째로 재생성하는 파일
      이라
      거기 있는 ApiHubGeneratedClient 안에 직접 메서드를 추가하면 다음
      재생성 때 사라집니다 — 그래서 부모 클래스인 ApiHubClient에 추가해
      ApiHubGeneratedClient가 상속으로 그대로 받게 했습니다(재생성해도
      안전). response_kind(structured/text/image/file)에 따라 raw/처리
      결과를 다르게 정리합니다.
    • 두 메서드 모두 endpoint별 if 분기가 없습니다 — service/operation
      이름 또는 ApiHubEndpointSpec만으로 라우팅합니다. UI에서 gateway
      두 값(datagokr/apihub)으로 어느 client를 쓸지 고르는 것은 이
      패키지가 애초에 gateway별로 클라이언트 클래스가 분리돼 있기 때문이며,
      khoa처럼 단일 서비스 레지스트리가 없어 불가피한 최소 분기입니다(PR
      본문에 밝혀둡니다 — 고쳐야 할 안티패턴인 "operation별 분기"와는
      다릅니다).
  • pyproject.toml의 debug-ui extra에 pandas>=2를 추가했습니다
    (기존에는 streamlit만 있었습니다).

examples/streamlit_debug_ui.py

  • 사이드바: Data source(datagokr/apihub) → Category → API 3단
    계단식(datagokr는 Category=dataset명, apihub는 Category=관측/예특보
    등 11개 분류). 선택한 API 설명 2줄, Environment(실제 env var 이름 표시)
    vs 수동 입력, Auth(실제 파라미터명인 serviceKey/authKey 입력창),
    서비스키 발급 링크, Timeout, Fixture 기본 디렉터리(tests/fixtures 기본
    • Custom 옵션) 순서로 고정했습니다.
  • 파라미터 폼: st.form()으로 감싸고 카탈로그의
    required_params/optional_params/param_defaults에서 위젯을
    자동 생성합니다. dataType/type처럼 고정 선택지가 있는 파라미터는
    selectbox, 나머지는 text input입니다(kma의 enums.py는 응답 값
    분류용이라 요청 파라미터로 재사용할 대상이 없었습니다 — 실제로
    존재하는 고정 선택지만 selectbox로 구현했습니다). 폼에 없는 파라미터는
    Extra params JSON으로 보충합니다.
  • 고정 6탭: Raw Response / Pydantic Model / Processed Result /
    Validation Errors / Debug Trace / Fixture · Testcase. Processed
    Result는 list 응답일 때만 pd.json_normalize로 dataframe을 보여주고
    단일 object면 st.json으로 배타적으로 표시합니다. Fixture ·
    Testcase 탭은 실제로 save_fixture()를 호출해
    tests/fixtures/<function>/<case>.json에 저장합니다
    — 이전
    구현은 "별도 단계에서 연결합니다" 안내만 있는 완전 미구현 스텁이었습니다.
  • 세션 상태: 마지막 실행 결과 키를
    {gateway}:{dataset_id}:{service}:{operation}으로 스코프해서, 데이터
    소스를 전환하거나 사이드바(폼 밖) 위젯을 조작해 rerun이 나도 이전 결과가
    섞이거나 사라지지 않습니다(kasi에서 발견됐던 버그 패턴을 처음부터
    피하는 설계).
  • 커스텀 CSS/브랜딩 없이 layout="wide"만 사용합니다.

검증

  • python -m pytest -q -m "not integration" — 149 passed, 12
    deselected
    (이 저장소는 integration marker를 씁니다). 기존
    api_catalog()/ApiCatalogEntry 관련 테스트를 포함해 전부 그대로
    통과합니다.
  • python -m ruff check . — 통과.
  • python -m mypy src/kma — 통과(src/kma 22개 파일, strict 모드).
  • AppTest 스모크 체크 — examples/streamlit_debug_ui.py를
    AppTest.from_file(...).run()으로 초기 렌더했고 at.exception이
    비어 있음을 확인했습니다. 추가로 Data source/Category/API 위젯을
    실제로 조작해(datagokr ↔ apihub 전환, category 변경) 재렌더해도
    예외가 없음을 확인했고, 로컬 .env에 있는 실제 개발용 키로 폼을
    제출해 getUltraSrtNcst를 라이브로 호출 → 성공 응답 처리 → Fixture
    저장 버튼까지 눌러 실제로 tests/fixtures/...json 파일이 생성되는
    것
    을 확인했습니다(스모크 테스트용으로 생성된 fixture 파일은 커밋
    전에 삭제했습니다). 빈 인증값으로 제출했을 때도 크래시 없이 구조화된
    에러가 표시되는 것을 확인했습니다.

커버하지 못한 부분 (정직하게 명시)

  • data.go.kr 파라미터 명세: 160개 operation 중 로컬로 정리된
    필수/선택 파라미터 명세가 있는 것은 **10개 service(약 30개
    operation)**뿐입니다(단기예보, 중기예보, ASOS 일/시간자료, 기상특보,
    관광지 날씨, 통보문, 생활기상지수, 지진정보, 해수욕장 날씨). 나머지
    ~130개 operation은 required_params/optional_params가 비어 있어
    Extra params JSON으로 직접 파라미터를 채워야 합니다.
  • APIHub 파싱 정확도: 470개 endpoint 전부 실행은 가능하지만,
    response_kind가 text인 legacy endpoint(다수)는
    parse_apihub_text_table()의 공백 기반 관대한 파싱을 그대로 쓰므로
    일부 endpoint에서 컬럼 정렬이 정확하지 않을 수 있습니다(예:
    스모크 테스트에서 kma_sfctm2 샘플 응답이 STN 컬럼에 여러 값이
    뭉쳐 들어감을 확인). image/file kind는 내용을 해석하지 않고
    크기/타입 metadata만 보여줍니다.
  • APIHub 필수 파라미터 판정 불가: APIHub 카탈로그(apiList.do
    스크래핑)에는 어떤 파라미터가 진짜 필수인지 기록이 없어, 470개
    전부 optional_params로 두고 실제 관찰된 sample 값으로 미리
    채웠습니다. 사용자가 값을 지우고 실행하면 API가 직접 오류를
    돌려줍니다.
  • 비동기 debug_fetch 없음: AsyncDataGoKrClient/AsyncApiHubClient용
    비동기 adebug_fetch는 만들지 않았습니다(Streamlit UI는 동기 호출만
    필요).

참고

  • 템플릿 원본: python-khoa-api의 examples/streamlit_debug_ui.py,
    src/khoa/debug.py

khoa의 streamlit_debug_ui.py 템플릿에 맞춰 tools/debug_streamlit.py(780줄,
operation별 하드코딩 분기, Fixture 탭 미구현, APIHub 470개 endpoint 전부
실행 불가)를 새로 짰다.

- src/kma/debug.py 신설: DebugRun/jsonable/redact_sensitive/debug_error/
  save_fixture — khoa의 debug.py 구조를 그대로 따른다.
- ApiCatalogEntry에 required_params/optional_params/param_defaults/
  response_kind/endpoint_path 메타데이터 필드를 추가하고, catalog.py에
  apihub_endpoint_catalog()를 새로 만들어 APIHub 470개 endpoint를 실제
  호출 가능한 카탈로그 row로 노출했다(기존 api_catalog()는 하위 호환 유지,
  테스트 불변 확인됨).
- DataGoKrClient.debug_fetch()/ApiHubClient.debug_fetch_endpoint() 제네릭
  메서드 추가 — endpoint별 분기 없이 카탈로그가 넘겨주는 service/operation
  또는 ApiHubEndpointSpec만으로 요청을 라우팅하고 구조화된 DebugRun을
  반환한다(예외도 traceback 포함 구조화 dict로, redact_sensitive 통과).
- examples/streamlit_debug_ui.py: Data source -> Category -> API 3단
  계단식 사이드바, st.form() 기반 파라미터 폼을 카탈로그 메타데이터에서
  자동 생성(하드코딩 if function_name== 분기 없음), 고정 6탭(Raw Response/
  Pydantic Model/Processed Result/Validation Errors/Debug Trace/
  Fixture-Testcase), 세션 상태는 gateway:dataset_id:service:operation으로
  스코프해 데이터소스 전환 시 이전 결과가 섞이지 않게 했다. Fixture 탭은
  save_fixture()를 실제로 호출해 파일을 저장한다.
- pyproject.toml debug-ui extra에 pandas>=2 추가.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01XF9V2q4mAmhmXn6t5G9Hfe
@digitie
digitie merged commit a75d1e1 into main Aug 29, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant