Files
Jeffrey (Dongkyu) Kim 4903256f27 Expose KRX partial failures instead of misreporting stock lookups
The Korean stock proxy used to silently drop failed market snapshots during
search and could turn an empty holiday trade snapshot into a 502 by falling
back into base-info lookup.

This change surfaces degraded market metadata on partial search success,
short-circuits empty trade snapshots to not_found, and refreshes the user
docs to use a real trading day in examples.

Constraint: KOSPI base-info approval is granted separately from other KRX routes
Constraint: Healthy markets should still return usable search results during a partial outage
Rejected: Return 502 on every partial search failure | hides still-usable markets and breaks current clients unnecessarily
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Keep degraded search metadata when any market snapshot fetch fails so partial outages stay visible
Tested: npm test --workspace k-skill-proxy
Tested: node --test scripts/skill-docs.test.js
Tested: npm run ci
Not-tested: Live KOSPI base-info behavior after the new KRX permission is approved
2026-04-16 13:17:36 +09:00

7.4 KiB

k-skill 프록시 서버 가이드

이 기능으로 할 수 있는 일

  • AirKorea 같은 무료/공공 API key를 서버에만 보관
  • k-skill 클라이언트는 프록시만 호출
  • 캐시, 인증, rate limit, 로깅을 한곳에서 통제

기본 구조

client/skill -> k-skill-proxy -> upstream public API

현재 기본 엔드포인트는 아래와 같습니다.

  • GET /health
  • GET /v1/fine-dust/report
  • GET /v1/korea-weather/forecast
  • GET /v1/seoul-subway/arrival
  • GET /v1/han-river/water-level
  • GET /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/search
  • GET /v1/korean-stock/base-info
  • GET /v1/korean-stock/trade-info
  • GET /v1/opinet/around
  • GET /v1/opinet/detail
  • GET /v1/neis/school-search (나이스 학교기본정보, KEDU_INFO_KEY)
  • GET /v1/neis/school-meal (나이스 급식식단정보, KEDU_INFO_KEY)
  • GET /B552584/:service/:operation (허용된 AirKorea route passthrough)

권장 환경변수

클라이언트(스킬) 쪽:

  • KSKILL_PROXY_BASE_URL=https://your-proxy.example.com

프록시 서버 쪽:

  • 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 인증키)
  • KRX_API_KEY=...
  • KSKILL_PROXY_PORT=4020

프로덕션 배포 구조

프로덕션 proxy 서버는 개발 repo와 분리된 별도 clone으로 운영한다.

  • 배포 디렉토리: ~/.local/share/k-skill-proxy (main 브랜치 단독 clone)
  • PM2 프로세스: k-skill-proxy
  • Cloudflare Tunnel ingress: k-skill-proxy.nomadamas.org -> http://localhost:4020

자동 배포 (cron)

~/.local/share/k-skill-proxy/scripts/auto-update-proxy.sh가 매시 정각에 실행된다.

0 * * * * PATH=/usr/bin:/opt/homebrew/bin:/opt/homebrew/lib/node_modules/.bin:$PATH ~/.local/share/k-skill-proxy/scripts/auto-update-proxy.sh >> /tmp/k-skill-proxy-update.log 2>&1

동작 순서:

  1. git fetch origin main
  2. local SHA == remote SHA 이면 종료 (up-to-date)
  3. git pull --ff-only
  4. package-lock.json 변경 시 npm ci
  5. pm2 restart k-skill-proxy --update-env

따라서 main에 merge되어야 프로덕션에 반영된다. dev 브랜치 변경은 프로덕션에 영향 없음.

로그: /tmp/k-skill-proxy-update.log

초기 설정 (PM2 + cloudflared)

  1. pm2 start ecosystem.config.cjs
  2. pm2 save
  3. pm2 startup 출력대로 launchd 등록
  4. Cloudflare Tunnel ingress 에 k-skill-proxy.nomadamas.org -> http://localhost:4020 추가

기본 공개 정책

  • 이 프록시는 무료 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:

curl -fsS --get 'http://127.0.0.1:4020/v1/seoul-subway/arrival' \
  --data-urlencode 'stationName=강남'

한국 날씨 endpoint:

curl -fsS --get 'http://127.0.0.1:4020/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'

한국 주식 검색 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를 배포하지 않습니다.
  • public hosted route rollout 이 끝나기 전에는 서울 지하철/한국 날씨 예시를 local/self-host URL 로 검증합니다.
  • public hosted route rollout 이 끝나기 전에는 한강 수위 route도 local/self-host 또는 배포 확인이 끝난 proxy URL 로 검증합니다.