mirror of
https://github.com/NomaDamas/k-skill.git
synced 2026-09-14 16:37:18 +08:00
322 lines
13 KiB
Markdown
322 lines
13 KiB
Markdown
# k-skill 프록시 서버 가이드
|
|
|
|
## 이 기능으로 할 수 있는 일
|
|
|
|
- AirKorea 같은 무료/공공 API key를 서버에만 보관
|
|
- `k-skill` 클라이언트는 프록시만 호출
|
|
- 캐시, 인증, rate limit, 로깅을 한곳에서 통제
|
|
|
|
## 기본 구조
|
|
|
|
```text
|
|
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/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-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/naver-shopping/search` (네이버 검색 Open API 쇼핑 검색 우선, 키가 없으면 공개 BFF JSON 기반 상품/가격 후보 조회)
|
|
- `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 /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로 비워 두면 hosted `https://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 서버는 **gpu01**의 Docker 컨테이너로 운영한다.
|
|
|
|
- 공개 도메인: `k-skill-proxy.nomadamas.org`
|
|
- 컨테이너 이미지 정의: `packages/k-skill-proxy/Dockerfile`
|
|
- 컨테이너 이름: `k-skill-proxy`
|
|
- gpu01 배포 helper: `scripts/deploy-k-skill-proxy-gpu01.sh`
|
|
- 시크릿(upstream API key): gpu01의 `/etc/k-skill-proxy/secrets.env`에만 보관하고 컨테이너 env로 주입
|
|
- 배포 대상 config: gpu01의 `/etc/k-skill-proxy/deploy.env`
|
|
|
|
### 자동 배포 (gpu01 cron)
|
|
|
|
gpu01 cron이 주기적으로 배포 helper를 실행한다. cron은 `main` 브랜치를 자동 배포하지 않고, `/etc/k-skill-proxy/deploy.env`에 명시된 `KSKILL_PROXY_DEPLOY_SHA` 또는 `KSKILL_PROXY_DEPLOY_REF`만 배포한다. 배포 대상이 없으면 fail-closed로 종료하며, helper는 절대 `origin/main`을 기본값으로 삼지 않는다.
|
|
|
|
배포 성공 조건은 다음 순서다.
|
|
|
|
1. 명시된 deploy SHA/ref를 commit SHA로 해석
|
|
2. checkout을 resolved SHA로 detach/force 이동한 뒤 `packages/k-skill-proxy/Dockerfile`로 repo root에서 Docker image build
|
|
3. 후보 컨테이너 local `/health` smoke test
|
|
4. production 컨테이너/route 전환
|
|
5. `https://k-skill-proxy.nomadamas.org/health` public smoke test
|
|
6. 대표 read-only public route smoke test
|
|
7. 모든 public smoke가 통과한 뒤에만 deployed-state 갱신
|
|
|
|
Docker daemon/socket/`docker` group 접근은 컨테이너 env를 읽을 수 있으므로 production secret 접근 권한과 동일하게 제한한다.
|
|
|
|
배포 상태와 로그는 gpu01의 `/var/log/k-skill-proxy/deploy.log`, `docker logs k-skill-proxy`, reverse proxy 로그에서 확인한다. 1회성 gpu01 셋업, cron entry, env/secrets, rollback, legacy GCP cleanup은 [`docs/deploy-k-skill-proxy.md`](../deploy-k-skill-proxy.md)에 정리되어 있다.
|
|
|
|
## 기본 공개 정책
|
|
|
|
- 이 프록시는 **무료 API만** 붙인다.
|
|
- 기본값은 **무인증 공개 endpoint** 다.
|
|
- 대신 read-only / allowlisted endpoint / cache / rate limit 을 유지한다.
|
|
- 문제가 생기면 그때 인증이나 더 강한 방어를 덧붙인다.
|
|
|
|
## 사용법
|
|
|
|
추가 client API 레이어는 불필요합니다. 필요한 쿼리를 그대로 프록시에 넣으면 되고, 프록시가 upstream API key 만 서버에서 주입합니다.
|
|
|
|
요약 endpoint:
|
|
|
|
```bash
|
|
curl -fsS --get 'https://k-skill-proxy.nomadamas.org/v1/fine-dust/report' \
|
|
--data-urlencode 'regionHint=서울 강남구'
|
|
```
|
|
|
|
서울 지하철 도착정보 endpoint:
|
|
|
|
```bash
|
|
BASE="${KSKILL_PROXY_BASE_URL:-https://k-skill-proxy.nomadamas.org}"
|
|
curl -fsS --get "${BASE}/v1/seoul-subway/arrival" \
|
|
--data-urlencode 'stationName=강남'
|
|
```
|
|
|
|
서울 실시간 혼잡도 endpoint:
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
curl -fsS --get 'https://k-skill-proxy.nomadamas.org/v1/opinet/detail' \
|
|
--data-urlencode 'id=A0009905'
|
|
```
|
|
|
|
나이스 학교 검색·급식 endpoint (학교 급식 식단 스킬에서 사용):
|
|
|
|
```bash
|
|
curl -fsS --get 'https://k-skill-proxy.nomadamas.org/v1/neis/school-search' \
|
|
--data-urlencode 'educationOffice=서울특별시교육청' \
|
|
--data-urlencode 'schoolName=미래초등학교'
|
|
```
|
|
|
|
```bash
|
|
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 미호출):
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
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`는 무시하고 서버 쪽 키를 주입):
|
|
|
|
```bash
|
|
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`는 무시하고 서버 쪽 키를 주입):
|
|
|
|
```bash
|
|
curl -fsS --get 'https://k-skill-proxy.nomadamas.org/v1/kakao-local/geocode' \
|
|
--data-urlencode 'q=서울역' \
|
|
--data-urlencode 'limit=1'
|
|
```
|
|
|
|
|
|
|
|
도서관 정보나루 도서 검색 endpoint (`DATA4LIBRARY_AUTH_KEY` 필요):
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
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 우선):
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
curl -fsS --get 'https://k-skill-proxy.nomadamas.org/v1/korean-stock/search' \
|
|
--data-urlencode 'q=삼성전자' \
|
|
--data-urlencode 'bas_dd=20260408'
|
|
```
|
|
|
|
한국 주식 기본정보 endpoint:
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
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 로도 검증합니다.
|