Expose narrow Seoul Open Data proxy surfaces for realtime bike availability, station master pages, and coordinate-based nearby lookups while keeping the upstream key server-side. Add a single Python skill entrypoint plus docs so agents can answer last-mile bike and dock availability questions. Constraint: Issue #274 requires , TDD, three proxy routes, branch feature/#274, and PR to dev. Rejected: Client-side Seoul OpenAPI key handling | would leak upstream credentials and violate existing proxy patterns. Confidence: high Scope-risk: moderate Directive: Keep these routes read-only; do not add rental/booking mutations or user-key requirements. Tested: node --test packages/k-skill-proxy/test/server.test.js --test-name-pattern 'seoul bike'; PYTHONPATH=.:scripts python3 -m unittest scripts.test_seoul_bike; local fake-proxy smoke run; PATH="/Users/jeffrey/.pyenv/versions/3.11.9/bin:/Users/jeffrey/.codex/tmp/arg0/codex-arg08RBix6:/opt/homebrew/lib/node_modules/@openai/codex/node_modules/@openai/codex-darwin-arm64/vendor/aarch64-apple-darwin/path:/Users/jeffrey/.cmuxterm/omo-bin:/opt/homebrew/share/android-commandlinetools/platform-tools:/opt/homebrew/share/android-commandlinetools/emulator:/opt/homebrew/share/android-commandlinetools/cmdline-tools/latest/bin:/Users/jeffrey/.local/bin:/Users/jeffrey/.bun/bin:/opt/homebrew/opt/node@22/bin:/opt/homebrew/opt/openjdk@21/bin:/opt/homebrew/opt/postgresql@18/bin:/Users/jeffrey/.jenv/shims:/Users/jeffrey/.jenv/bin:/opt/homebrew/opt/imagemagick/bin:/opt/homebrew/Cellar/pyenv-virtualenv/1.4.0/shims:/Users/jeffrey/.pyenv/shims:/opt/homebrew/opt/openssl@3/bin:/Users/jeffrey/.rbenv/shims:/Users/jeffrey/.rbenv/bin:/Users/jeffrey/google-cloud-sdk/bin:/Applications/cmux.app/Contents/Resources/bin:/Users/jeffrey/Library/pnpm:/Users/jeffrey/.nvm/versions/node/v24.13.0/bin:/Users/jeffrey/.cops/bin:/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin:/opt/pmk/env/global/bin:/Library/Apple/usr/bin:/Library/TeX/texbin:/Users/jeffrey/.cargo/bin:/Users/jeffrey/Library/Application Support/JetBrains/Toolbox/scripts:/Library/Java/JavaVirtualMachines/zulu-17.jdk/Contents/Home/bin:/Users/jeffrey/xcode-projects/marshroom/cli" npm run ci. Not-tested: Live hosted Seoul Open Data request with production SEOUL_OPEN_API_KEY.
13 KiB
k-skill 프록시 서버 가이드
이 기능으로 할 수 있는 일
- AirKorea 같은 무료/공공 API key를 서버에만 보관
k-skill클라이언트는 프록시만 호출- 캐시, 인증, rate limit, 로깅을 한곳에서 통제
기본 구조
client/skill -> k-skill-proxy -> upstream public API
현재 기본 엔드포인트는 아래와 같습니다.
GET /healthGET /v1/fine-dust/reportGET /v1/korea-weather/forecastGET /v1/seoul-subway/arrivalGET /v1/seoul-density/citydata(서울 실시간 도시데이터 핫스팟 혼잡도/추정 인구,SEOUL_OPEN_API_KEY)GET /v1/seoul-bike/realtime(서울 따릉이 실시간 대여정보bikeList,SEOUL_OPEN_API_KEY)GET /v1/seoul-bike/stations(서울 따릉이 대여소 마스터tbCycleStationInfo,SEOUL_OPEN_API_KEY)GET /v1/seoul-bike/nearby(좌표 주변 따릉이 실시간 대여소 필터링,SEOUL_OPEN_API_KEY)GET /v1/han-river/water-levelGET /v1/household-waste/info(생활쓰레기 배출정보,DATA_GO_KR_API_KEY; 쿼리pageNo·numOfRows필수, 값1·100)GET /v1/mfds/drug-safety/lookup(식약처 의약품개요정보 + 안전상비의약품 정보,DATA_GO_KR_API_KEY)GET /v1/mfds/food-safety/search(식약처 부적합 식품 + 식품안전나라 회수 정보,DATA_GO_KR_API_KEY, 선택적FOODSAFETYKOREA_API_KEY)GET /v1/korean-stock/searchGET /v1/korean-stock/base-infoGET /v1/korean-stock/trade-infoGET /v1/naver-shopping/search(네이버 검색 Open API 쇼핑 검색 우선, 키가 없으면 공개 BFF JSON 기반 상품/가격 후보 조회)GET /v1/opinet/aroundGET /v1/opinet/detailGET /v1/neis/school-search(나이스 학교기본정보,KEDU_INFO_KEY)GET /v1/neis/school-meal(나이스 급식식단정보,KEDU_INFO_KEY)GET /v1/data4library/library-search(도서관 정보나루 정보공개 도서관 조회,DATA4LIBRARY_AUTH_KEY)GET /v1/data4library/book-search(도서관 정보나루 도서 검색,DATA4LIBRARY_AUTH_KEY)GET /v1/data4library/book-detail(도서관 정보나루 도서 상세 조회,DATA4LIBRARY_AUTH_KEY)GET /v1/data4library/libraries-by-book(도서 소장 도서관 조회,DATA4LIBRARY_AUTH_KEY)GET /v1/data4library/book-exists(도서관별 도서 소장여부,DATA4LIBRARY_AUTH_KEY)GET /v1/kstartup/business-info(창업진흥원 K-Startup 통합공고 지원사업 정보,DATA_GO_KR_API_KEY)GET /v1/kstartup/announcements(창업진흥원 K-Startup 지원사업 공고 정보,DATA_GO_KR_API_KEY)GET /v1/kstartup/contents(창업진흥원 K-Startup 창업 콘텐츠 정보,DATA_GO_KR_API_KEY)GET /v1/kstartup/statistics(창업진흥원 K-Startup 통계보고서 정보,DATA_GO_KR_API_KEY)GET /B552584/:service/:operation(허용된 AirKorea route passthrough)
권장 환경변수
클라이언트(스킬) 쪽:
- 일반 hosted client는
KSKILL_PROXY_BASE_URL을 unset/empty로 비워 두면 hostedhttps://k-skill-proxy.nomadamas.org를 기본값으로 사용합니다. KSKILL_PROXY_BASE_URL=https://your-proxy.example.com은 self-host 또는 alternate proxy를 명시적으로 쓰는 경우에만 설정하는 override 예시입니다.
프록시 서버 쪽:
AIR_KOREA_OPEN_API_KEY=...KMA_OPEN_API_KEY=...SEOUL_OPEN_API_KEY=...HRFCO_OPEN_API_KEY=...OPINET_API_KEY=...DATA_GO_KR_API_KEY=...FOODSAFETYKOREA_API_KEY=...(선택: 식품안전나라 회수 live 결과, 없으면 sample fallback)KEDU_INFO_KEY=...(나이스 교육정보 개방 포털 Open API 인증키)DATA4LIBRARY_AUTH_KEY=...(도서관 정보나루 Open API 인증키)KRX_API_KEY=...NAVER_SEARCH_CLIENT_ID=...,NAVER_SEARCH_CLIENT_SECRET=...(선택: 네이버 검색 Open API 쇼핑 검색)KSKILL_PROXY_PORT=4020
프로덕션 배포 구조
프로덕션 proxy 서버는 Google Cloud Run에서 운영한다.
- GCP project:
k-skill-proxy - Region:
asia-northeast1(도쿄) - Cloud Run service:
k-skill-proxy - 공개 도메인:
k-skill-proxy.nomadamas.org(Cloud Run domain mapping) - 컨테이너 이미지 정의:
packages/k-skill-proxy/Dockerfile - 시크릿(upstream API key): GCP Secret Manager에 보관, Cloud Run runtime에 주입
자동 배포 (GitHub Actions)
main 브랜치에 push/merge되면 .github/workflows/deploy-k-skill-proxy.yml 워크플로가 실행되어 다음 순서로 동작한다.
- Workload Identity Federation으로 GCP 인증
packages/k-skill-proxy/Dockerfile로 이미지 빌드- Artifact Registry (
asia-northeast1-docker.pkg.dev/k-skill-proxy/k-skill/k-skill-proxy:<sha>)에 push - Cloud Run service
k-skill-proxy재배포 (Secret Manager 시크릿 + 런타임 환경변수 주입) - 직접 Cloud Run URL과
https://k-skill-proxy.nomadamas.org/healthsmoke test
따라서 main에 merge되어야 프로덕션에 반영된다. dev 브랜치 변경은 프로덕션에 영향 없음.
배포 상태와 로그는 GitHub Actions의 "Deploy k-skill-proxy to Cloud Run" 워크플로 실행 페이지와 GCP Console의 Cloud Run revision/log에서 확인한다.
초기 셋업 (운영자 1회 수행)
WIF pool/provider, deploy service account, Secret Manager 시크릿 생성 등 1회성 GCP 셋업 절차와 GitHub repository secrets/variables 등록 방법은 docs/deploy-k-skill-proxy.md에 정리되어 있다.
기본 공개 정책
- 이 프록시는 무료 API만 붙인다.
- 기본값은 무인증 공개 endpoint 다.
- 대신 read-only / allowlisted endpoint / cache / rate limit 을 유지한다.
- 문제가 생기면 그때 인증이나 더 강한 방어를 덧붙인다.
사용법
추가 client API 레이어는 불필요합니다. 필요한 쿼리를 그대로 프록시에 넣으면 되고, 프록시가 upstream API key 만 서버에서 주입합니다.
요약 endpoint:
curl -fsS --get 'https://k-skill-proxy.nomadamas.org/v1/fine-dust/report' \
--data-urlencode 'regionHint=서울 강남구'
서울 지하철 도착정보 endpoint:
BASE="${KSKILL_PROXY_BASE_URL:-https://k-skill-proxy.nomadamas.org}"
curl -fsS --get "${BASE}/v1/seoul-subway/arrival" \
--data-urlencode 'stationName=강남'
서울 실시간 혼잡도 endpoint:
BASE="${KSKILL_PROXY_BASE_URL:-https://k-skill-proxy.nomadamas.org}"
curl -fsS --get "${BASE}/v1/seoul-density/citydata" \
--data-urlencode 'area=강남역'
# 서울 따릉이 주변 대여소
curl -fsS --get "${BASE}/v1/seoul-bike/nearby" \
--data-urlencode 'lat=37.5717' \
--data-urlencode 'lon=126.9763' \
--data-urlencode 'radius_m=500'
한국 날씨 endpoint:
BASE="${KSKILL_PROXY_BASE_URL:-https://k-skill-proxy.nomadamas.org}"
curl -fsS --get "${BASE}/v1/korea-weather/forecast" \
--data-urlencode 'lat=37.5665' \
--data-urlencode 'lon=126.9780'
한강 수위 정보 endpoint:
curl -fsS --get 'https://k-skill-proxy.nomadamas.org/v1/han-river/water-level' \
--data-urlencode 'stationName=한강대교'
이 endpoint 는 내부적으로 HRFCO waterlevel/info.json 으로 관측소를 찾고, waterlevel/list/10M/{WLOBSCD}.json 으로 최신 10분 수위/유량을 가져옵니다.
Opinet 근처 주유소 가격 endpoint:
curl -fsS --get 'https://k-skill-proxy.nomadamas.org/v1/opinet/around' \
--data-urlencode 'x=313680' \
--data-urlencode 'y=545015' \
--data-urlencode 'radius=1500' \
--data-urlencode 'prodcd=B027'
Opinet 주유소 상세 endpoint:
curl -fsS --get 'https://k-skill-proxy.nomadamas.org/v1/opinet/detail' \
--data-urlencode 'id=A0009905'
나이스 학교 검색·급식 endpoint (학교 급식 식단 스킬에서 사용):
curl -fsS --get 'https://k-skill-proxy.nomadamas.org/v1/neis/school-search' \
--data-urlencode 'educationOffice=서울특별시교육청' \
--data-urlencode 'schoolName=미래초등학교'
curl -fsS --get 'https://k-skill-proxy.nomadamas.org/v1/neis/school-meal' \
--data-urlencode 'educationOfficeCode=B10' \
--data-urlencode 'schoolCode=7010123' \
--data-urlencode 'mealDate=20260410'
생활쓰레기 배출정보 endpoint. 쿼리에 pageNo와 numOfRows를 반드시 포함하고, 값은 각각 1, **100**만 허용한다(page_no / num_of_rows 동일). 누락·다른 값·숫자만이 아닌 문자열이면 400(upstream 미호출):
curl -fsS --get 'https://k-skill-proxy.nomadamas.org/v1/household-waste/info' \
--data-urlencode 'cond[SGG_NM::LIKE]=강남구' \
--data-urlencode 'pageNo=1' \
--data-urlencode 'numOfRows=100'
의약품 안전 체크 endpoint:
curl -fsS --get 'https://k-skill-proxy.nomadamas.org/v1/mfds/drug-safety/lookup' \
--data-urlencode 'itemName=타이레놀' \
--data-urlencode 'itemName=판콜' \
--data-urlencode 'limit=5'
식품 안전 체크 endpoint:
curl -fsS --get 'https://k-skill-proxy.nomadamas.org/v1/mfds/food-safety/search' \
--data-urlencode 'query=김밥' \
--data-urlencode 'limit=5'
KOSIS 통계 조회 endpoint (KOSIS_API_KEY 필요, caller apiKey는 무시하고 서버 쪽 키를 주입):
curl -fsS --get 'https://k-skill-proxy.nomadamas.org/v1/kosis/search' \
--data-urlencode 'q=1인 가구' \
--data-urlencode 'limit=3'
curl -fsS --get 'https://k-skill-proxy.nomadamas.org/v1/kosis/meta' \
--data-urlencode 'tableId=DT_1JC1501' \
--data-urlencode 'metaType=ITM'
curl -fsS --get 'https://k-skill-proxy.nomadamas.org/v1/kosis/data' \
--data-urlencode 'tableId=DT_1JC1501' \
--data-urlencode 'prdSe=Y' \
--data-urlencode 'start=2020' \
--data-urlencode 'end=2023' \
--data-urlencode 'objL1=ALL'
Kakao Local geocoding endpoint (KAKAO_REST_API_KEY 필요, caller apiKey는 무시하고 서버 쪽 키를 주입):
curl -fsS --get 'https://k-skill-proxy.nomadamas.org/v1/kakao-local/geocode' \
--data-urlencode 'q=서울역' \
--data-urlencode 'limit=1'
도서관 정보나루 도서 검색 endpoint (DATA4LIBRARY_AUTH_KEY 필요):
curl -fsS --get 'https://k-skill-proxy.nomadamas.org/v1/data4library/book-search' \
--data-urlencode 'keyword=역사' \
--data-urlencode 'pageNo=1' \
--data-urlencode 'pageSize=10'
도서 상세/소장 조회 endpoint:
curl -fsS --get 'https://k-skill-proxy.nomadamas.org/v1/data4library/book-detail' \
--data-urlencode 'isbn13=9788971998557' \
--data-urlencode 'loaninfoYN=Y'
curl -fsS --get 'https://k-skill-proxy.nomadamas.org/v1/data4library/libraries-by-book' \
--data-urlencode 'isbn=9788971998557' \
--data-urlencode 'region=11'
curl -fsS --get 'https://k-skill-proxy.nomadamas.org/v1/data4library/book-exists' \
--data-urlencode 'libraryCode=111001' \
--data-urlencode 'isbn13=9788971998557'
프록시는 caller가 넘긴 authKey/format을 무시하고 서버 쪽 DATA4LIBRARY_AUTH_KEY와 format=json을 주입한다.
네이버 쇼핑 가격비교 endpoint (NAVER_SEARCH_CLIENT_ID/NAVER_SEARCH_CLIENT_SECRET이 있으면 공식 Search API 우선):
curl -fsS --get 'https://k-skill-proxy.nomadamas.org/v1/naver-shopping/search' \
--data-urlencode 'q=에어팟 프로 2세대' \
--data-urlencode 'limit=10'
키가 없는 no-key fallback은 search.shopping.naver.com/search/all HTML 페이지 대신
ns-portal.shopping.naver.com/api/v2/shopping-paged-slot?query=<검색어>&source=shp_gui
공개 JSON path를 사용한다. page는 BFF에 전달한 뒤 해당 페이지 카드만 정규화하고, no-key
price_asc/price_dsc/review 정렬은 선택된 BFF 페이지 안에서 로컬 적용한다. BFF에는 날짜
필드가 없어 no-key date 요청은 meta.sort_applied: "unsupported"로 표시한다.
한국 주식 검색 endpoint:
curl -fsS --get 'https://k-skill-proxy.nomadamas.org/v1/korean-stock/search' \
--data-urlencode 'q=삼성전자' \
--data-urlencode 'bas_dd=20260408'
한국 주식 기본정보 endpoint:
curl -fsS --get 'https://k-skill-proxy.nomadamas.org/v1/korean-stock/base-info' \
--data-urlencode 'market=KOSPI' \
--data-urlencode 'code=005930' \
--data-urlencode 'bas_dd=20260408'
AirKorea passthrough endpoint:
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'
주의할 점
- upstream key는 프록시 서버에서만 관리합니다.
- 한국 주식 route도 사용자에게
KRX_API_KEY를 배포하지 않습니다. - client 쪽에는 upstream API key를 배포하지 않습니다.
- 도서관 정보나루 route도 사용자에게
DATA4LIBRARY_AUTH_KEY를 배포하지 않습니다. - self-host proxy 운영자는 동일 route를 local/self-host URL 로도 검증합니다.