Data.go.kr 이 tn_pubr_prkplce_info_api 를 HTTPS 로만 서비스하고 HTTP 요청은 301 로 리다이렉트하기 때문에 Node fetch 가 `response.ok=false` 로 떨어져 기능이 전체 실패하고 있었다. 이 커밋은 HTTPS 로 직접 호출하도록 수정하면서, 업스트림의 주소/지역 필터가 실제로는 동작하지 않고 페이지당 응답이 1000rows 기준 26s 에 달해 20s fetch timeout 에 꾸준히 걸리던 문제까지 함께 해결한다. ## What changed - packages/k-skill-proxy/src/parking-lots.js - PARKING_LOT_API_URL 을 `http://` → `https://` 로 고정 (root cause). - 업스트림 address/geo 필터가 신뢰 불가하므로 full-dataset 을 한 번 로드해 프로세스 메모리에 6시간 TTL 로 캐시하고, 동시 호출자는 in-flight promise 를 공유하도록 한다. nearby 쿼리는 캐시된 행을 좌표 거리로 필터링해 서비스한다. - DATASET_PAGE_SIZE=300, fetch timeout 30s 로 페이지당 응답이 20s 를 넘기지 않도록 맞췄다. - packages/k-skill-proxy/src/server.js - 더 이상 의미 없어진 numOfRows / maxPages 쿼리 파라미터를 라우트에서 제거하고, 응답 payload 의 query echo 도 정리했다. - packages/k-skill-proxy/test/server.test.js - 새 캐시 기반 동작을 검증하는 테스트로 교체: (1) full dataset load + 좌표 필터 + 프록시 응답 캐시 재사용, (2) public_only 기본값 및 해제 시 동작, (3) 좌표 검증 실패 400, (4) 업스트림 키 미설정 시 503. - packages/parking-lot-search/src/index.js - OFFICIAL_API_URL 도 HTTPS 로 맞춰 직접 호출 모드 사용자도 같은 버그를 밟지 않게 한다. - packages/parking-lot-search/src/parse.js - 업스트림이 `insttCode` / `insttNm` (camelCase) 를 돌려주는데 parser 가 snake_case (`instt_code`, `instt_nm`) 만 인식해 providerCode/providerName 이 비어 있던 문제를 수정. - packages/parking-lot-search/test/* 및 fixtures - HTTPS URL 매칭으로 업데이트하고, insttCode/insttNm 회귀 테스트를 fixture/assertion 에 추가. - docs/features/parking-lot-search.md, parking-lot-search/SKILL.md, packages/parking-lot-search/README.md - 공식 endpoint 표기를 HTTPS 로 통일. - .changeset/parking-lot-https-fix.md - parking-lot-search 패키지 patch 릴리즈 노트 추가. ## How it was verified - `npm run ci` (lint + typecheck + tests + pack:dry-run) 통과. - 로컬에서 실제 `DATA_GO_KR_API_KEY` 로 k-skill-proxy 를 기동해 live 호출 검증: - 광화문 (37.573713, 126.978338) cold cache: 30s 내 전체 18,868 rows 로드, 2km 내 47개 공영주차장 반환 (세종로 414m, 서린노외 456m 등). - 강남역 (37.497952, 127.027621) warm cache: 31ms 응답, 1.5km 내 13개 반환 (역삼문화공원 380m, 역삼푸른솔도서관 421m 등). - 업스트림 직접 HTTPS 호출로 `resultCode=00 NORMAL_SERVICE` 정상 동작 확인. |
||
|---|---|---|
| .. | ||
| src | ||
| test | ||
| package.json | ||
| README.md | ||
k-skill-proxy
k-skill용 Fastify 기반 프록시 서버입니다. AirKorea 미세먼지 조회, 기상청 단기예보, 서울 지하철 실시간 도착정보, 한강홍수통제소 수위 정보를 감싸고, 이후 무료/공공 API adapter를 추가하는 베이스로 씁니다.
현재 제공 엔드포인트
GET /healthGET /v1/fine-dust/reportGET /v1/korea-weather/forecastGET /v1/seoul-subway/arrivalGET /v1/han-river/water-levelGET /v1/household-waste/info— 생활쓰레기 배출정보(DATA_GO_KR_API_KEY;pageNo=1,numOfRows=100필수)GET /v1/parking-lots/search— 전국주차장정보표준데이터 기반 근처 공영주차장 검색(DATA_GO_KR_API_KEY)GET /v1/neis/school-search— 나이스 학교기본정보(교육청명·학교명 검색)GET /v1/neis/school-meal— 나이스 급식식단정보(일자별 메뉴)GET /v1/mfds/drug-safety/lookup— 식약처 의약품개요정보(e약은요) + 안전상비의약품 정보(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/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)
환경변수
AIR_KOREA_OPEN_API_KEY— 프록시 서버 쪽 AirKorea upstream keyKMA_OPEN_API_KEY— 프록시 서버 쪽 기상청 단기예보 upstream keySEOUL_OPEN_API_KEY— 프록시 서버 쪽 서울 열린데이터 광장 upstream keyHRFCO_OPEN_API_KEY— 프록시 서버 쪽 한강홍수통제소 upstream keyKEDU_INFO_KEY— 프록시 서버 쪽 나이스(NEIS) 교육정보 개방 포털 Open API 인증키 (school-search,school-meal)DATA4LIBRARY_AUTH_KEY— 프록시 서버 쪽 도서관 정보나루 Open API 인증키 (data4library/*)FOODSAFETYKOREA_API_KEY— 프록시 서버 쪽 식품안전나라 회수정보 live key (mfds/food-safety/search; 없으면 sample feed fallback)KRX_API_KEY— 프록시 서버 쪽 KRX Open API upstream keyNAVER_SEARCH_CLIENT_ID,NAVER_SEARCH_CLIENT_SECRET— 선택: 네이버 검색 Open API 쇼핑 검색(shop.json) 키. 설정되면 네이버 쇼핑 route가 bot-block 위험이 낮은 공식 API를 우선 사용하고, 없으면 공개 BFF JSON(ns-portal.shopping.naver.com/api/v2/shopping-paged-slot) 파서로 fallback. 공식 API는review정렬을 지원하지 않아meta.sort_applied: "unsupported"로 표시한다. no-key fallback은page를 BFF에 전달해 해당 페이지를 고르고,price_asc/price_dsc/review는 선택 페이지 안에서 로컬 정렬하며,date는meta.sort_applied: "unsupported"로 표시KSKILL_PROXY_HOST— 기본127.0.0.1KSKILL_PROXY_PORT— 기본4020KSKILL_PROXY_CACHE_TTL_MS— 기본300000KSKILL_PROXY_RATE_LIMIT_WINDOW_MS— 기본60000KSKILL_PROXY_RATE_LIMIT_MAX— 기본60DATA_GO_KR_API_KEY- 공공데이터포털 에서 쓰이는 API 인증키 (household-waste,parking-lots,real-estate,mfds-drug-safety,mfds-food-safety)
기본 정책은 무료 API 공개 프록시 = 무인증 이다. 대신 endpoint scope 를 좁게 유지하고, cache + rate limit 으로 남용을 늦춘다.
로컬 실행
node packages/k-skill-proxy/src/server.js
환경변수(AIR_KOREA_OPEN_API_KEY 등)가 이미 설정되어 있거나 ~/.config/k-skill/secrets.env를 source한 상태에서 실행한다.
서울 지하철 도착정보 예시:
curl -fsS --get 'http://127.0.0.1:4020/v1/seoul-subway/arrival' \
--data-urlencode 'stationName=강남'
한국 날씨 예시:
curl -fsS --get 'http://127.0.0.1:4020/v1/korea-weather/forecast' \
--data-urlencode 'lat=37.5665' \
--data-urlencode 'lon=126.9780'
한강 수위 정보 예시:
curl -fsS --get 'http://127.0.0.1:4020/v1/han-river/water-level' \
--data-urlencode 'stationName=한강대교'
나이스 학교 검색·급식 식단 예시 (KEDU_INFO_KEY 필요). 급식은 교육청 코드(ATPT_OFCDC_SC_CODE)와 학교 코드(SD_SCHUL_CODE)가 필요하므로 보통 아래 순서로 호출한다.
학교 검색:
curl -fsS --get 'http://127.0.0.1:4020/v1/neis/school-search' \
--data-urlencode 'educationOffice=서울특별시교육청' \
--data-urlencode 'schoolName=미래초등학교'
급식 식단:
curl -fsS --get 'http://127.0.0.1:4020/v1/neis/school-meal' \
--data-urlencode 'educationOfficeCode=B10' \
--data-urlencode 'schoolCode=7010123' \
--data-urlencode 'mealDate=20260410'
생활쓰레기 배출정보 예시 (DATA_GO_KR_API_KEY 필요). pageNo·numOfRows는 반드시 1·100:
curl -fsS --get 'http://127.0.0.1:4020/v1/household-waste/info' \
--data-urlencode 'cond[SGG_NM::LIKE]=강남구' \
--data-urlencode 'pageNo=1' \
--data-urlencode 'numOfRows=100'
공영주차장 검색 예시 (DATA_GO_KR_API_KEY 필요):
curl -fsS --get 'http://127.0.0.1:4020/v1/parking-lots/search' \
--data-urlencode 'latitude=37.573713' \
--data-urlencode 'longitude=126.978338' \
--data-urlencode 'address_hint=서울특별시 종로구' \
--data-urlencode 'limit=3' \
--data-urlencode 'radius=1500'
의약품 안전 체크 예시 (DATA_GO_KR_API_KEY 필요):
curl -fsS --get 'http://127.0.0.1:4020/v1/mfds/drug-safety/lookup' \
--data-urlencode 'itemName=타이레놀' \
--data-urlencode 'itemName=판콜' \
--data-urlencode 'limit=5'
식품 안전 체크 예시 (DATA_GO_KR_API_KEY 필요, FOODSAFETYKOREA_API_KEY 없으면 회수 정보는 sample fallback):
curl -fsS --get 'http://127.0.0.1:4020/v1/mfds/food-safety/search' \
--data-urlencode 'query=김밥' \
--data-urlencode 'limit=5'
네이버 쇼핑 가격비교 예시 (NAVER_SEARCH_CLIENT_ID/NAVER_SEARCH_CLIENT_SECRET이 있으면 공식 Search API를 우선 사용):
curl -fsS --get 'http://127.0.0.1:4020/v1/naver-shopping/search' \
--data-urlencode 'q=에어팟 프로 2세대' \
--data-urlencode 'limit=10'
도서관 정보나루 도서 검색 예시 (DATA4LIBRARY_AUTH_KEY 필요):
curl -fsS --get 'http://127.0.0.1:4020/v1/data4library/book-search' \
--data-urlencode 'keyword=역사' \
--data-urlencode 'pageNo=1' \
--data-urlencode 'pageSize=10'
도서관 정보나루 상세/소장 확인 예시:
curl -fsS --get 'http://127.0.0.1:4020/v1/data4library/book-detail' \
--data-urlencode 'isbn13=9788971998557' \
--data-urlencode 'loaninfoYN=Y'
curl -fsS --get 'http://127.0.0.1:4020/v1/data4library/book-exists' \
--data-urlencode 'libraryCode=111001' \
--data-urlencode 'isbn13=9788971998557'
한국 주식 검색 예시:
curl -fsS --get 'http://127.0.0.1:4020/v1/korean-stock/search' \
--data-urlencode 'q=삼성전자' \
--data-urlencode 'bas_dd=20260408'
프록시는 내부적으로 waterlevel/info.json 으로 관측소를 해석하고, waterlevel/list/10M/{WLOBSCD}.json 으로 최신 수위/유량을 조회합니다. 한국 주식 route는 KRX Open API에 AUTH_KEY 헤더를 서버 쪽에서만 주입합니다.
PM2 실행
루트의 ecosystem.config.cjs + scripts/run-k-skill-proxy.sh 조합을 사용하면 재부팅 이후에도 같은 환경변수로 다시 올라옵니다.