# 김교보-인포맥스 API 연동 안내

Base URL: https://bridge.racoon.pe.kr

## 01. 연동 시작하기

기준 주소는 https://bridge.racoon.pe.kr 입니다. 응용프로그램은 Linux 서버의 /api/*를 호출합니다. Windows PC의 IP, 로컬 포트, 장치 토큰을 직접 사용하지 않습니다.

현재 데이터 API 인증은 김교보 계정 로그인 세션 쿠키입니다. 데이터 소비자용 장기 API 키·Bearer 토큰 발급은 아직 지원하지 않습니다. 브릿지 등록 코드를 API 키로 사용하지 마세요.

POST /api/auth/login에 username/password JSON을 보내고 응답의 Set-Cookie를 쿠키 저장소에 보관합니다. /api/auth/me로 로그인 여부를 확인하고, POST /api/auth/logout으로 해당 클라이언트의 쿠키를 지웁니다. 만료 시간은 서버 설정에 따릅니다.

비밀번호·세션 쿠키·장치 토큰은 소스, 로그, LLM 프롬프트에 넣지 마세요. 예제는 환경변수에서 계정을 읽습니다. 다른 출처의 브라우저 앱을 위한 CORS 개방은 제공하지 않으므로 해당 앱의 백엔드에서 호출하세요.


### cURL · 로그인 → 뉴스 조회 → 로그아웃

```bash
# KKB_USERNAME / KKB_PASSWORD는 안전한 환경변수로 주입
umask 077
cookie_file=$(mktemp)
trap 'rm -f "$cookie_file"' EXIT
python3 -c 'import os,json; print(json.dumps({"username":os.environ["KKB_USERNAME"],"password":os.environ["KKB_PASSWORD"]}))' |
  curl --fail-with-body -sS -c "$cookie_file" -H 'Content-Type: application/json'     --data-binary @- https://bridge.racoon.pe.kr/api/auth/login
curl --fail-with-body -sS -b "$cookie_file" --get   --data-urlencode 'keywords=채권' --data-urlencode 'limit=10'   https://bridge.racoon.pe.kr/api/news
curl --fail-with-body -sS -b "$cookie_file" -c "$cookie_file" -X POST   https://bridge.racoon.pe.kr/api/auth/logout
```


### Python · 쿠키 자동 유지

```python
import os
import httpx

with httpx.Client(base_url="https://bridge.racoon.pe.kr", timeout=60) as client:
    login = client.post("/api/auth/login", json={
        "username": os.environ["KKB_USERNAME"],
        "password": os.environ["KKB_PASSWORD"],
    })
    login.raise_for_status()
    try:
        response = client.get("/api/news", params={"feed": "all", "limit": 10})
        response.raise_for_status()
        payload = response.json()
        if payload.get("cached"):
            print("주의: 원천 장애 시 저장된 뉴스 결과일 수 있습니다.")
        for article in payload.get("news", []):
            print(article.get("ktitle") or article.get("title"))
    finally:
        client.post("/api/auth/logout").raise_for_status()
```

## 02. 어떤 데이터를 제공하나요?

지원 범위와 현재 수신 가능 여부는 다릅니다. 인포맥스가 로그인된 Windows 브릿지가 연결되어 있어야 하며 PC의 설치 모듈·계약·권한에 따라 사용 가능한 종목과 필드가 달라집니다. 운영 현황의 원천 진단을 함께 확인하세요.

데이터 | 제공 범위 | 조회 순서
--- | --- | ---
국고채 금리 | 1·2·3·5·10·20·30·50년 수익률(%) / 스냅샷·SSE | /api/rates 또는 /api/rates/stream
뉴스 | all 통합 / yonhap 연합뉴스 / third_party 타사뉴스 | /api/news/feeds → /api/news
히스토리 | STK·IDX·FUT·OPT·FX·IR·BND·STB·FRN·ECO·FND·FD2 | types → catalog → history/query

- 금리 예: 국고채 2년 코드 BONDKSDCAL19, 필드 대표수익률. 주식 예: 삼성전자 005930, 현재가·누적거래량. 반드시 현재 카탈로그에서 필드 조합을 확인하세요.
- 히스토리 종류: STK 국내주식 / IDX 국내지수 / FUT 국내선물 / OPT 국내옵션 / FX 외환 / IR 금리 / BND 채권 / STB 단기금융 / FRN 해외종목·지수 / ECO 경제지표 / FND 펀드 / FD2 생명보험 펀드.
- 현재 PostgreSQL에는 요청 감사 로그와 장치 등록 정보가 저장됩니다. 원천 시계열 전체 저장·누락분 수집·DB 우선 응답은 향후 데이터룸 연계 목표이며, 현재 보장하지 않습니다.
- 브릿지에서 제공하는 뉴스 장애 대응 캐시 및 금리 source=cache는 Linux 시계열 DB 적재와 다릅니다. cached/source, fetched_at/as_of/updated_at과 원천 오류를 확인하세요.
## 03. 데이터 호출 예제

아래 값은 형식 설명용 예시이며 현재 시장 데이터가 아닙니다. 예제의 client는 위 Python 로그인 코드와 같은 인증된 httpx.Client입니다.


### 히스토리 · 필드 확인 후 작은 기간부터 조회

```python
catalog = client.get("/api/history/catalog", params={"kind": "IR", "q": "대표수익률", "limit": 200})
catalog.raise_for_status()
# catalog.json()에서 데이터셋과 정확한 필드명을 확인한 후:
response = client.post("/api/history/query", json={
    "kind": "IR", "code": "BONDKSDCAL19", "fields": ["대표수익률"],
    "start": "2025-01-02", "end": "2025-01-03",
    "period": "D", "sort": "A", "limit": 100, "include_date": True,
})
response.raise_for_status()
result = response.json()
print(result["columns"], result["rows"])
```


### 히스토리 응답 예시 · 일부 필드

```json
{
  "row_count": 1,
  "column_count": 2,
  "columns": [{"name": "일자", "data_type": "string"}, {"name": "대표수익률", "data_type": "double"}],
  "rows": [{"일자": "20250102", "대표수익률": 2.7}],
  "proxy_duration_ms": 120,
  "_relay": {"bridge_id": "장치 UUID", "attempt": 1, "selection_policy": "random-with-failover"}
}
```


### 뉴스 응답 예시 · 일부 필드

```json
{
  "feed": "all", "count": 1, "cached": false,
  "fetched_at": "2025-01-02T09:00:00+09:00",
  "news": [{"ktitle": "예시 뉴스 제목", "feed_type": "yonhap"}]
}
```


### 브라우저 SSE · 서버와 같은 출처, 로그인 후

```javascript
const stream = new EventSource("/api/rates/stream");
stream.addEventListener("rates", event => {
  const payload = JSON.parse(event.data);
  if (!payload.connected || payload.error) {
    console.warn("원천 상태를 확인하세요", payload.error);
    return;
  }
  console.log(payload.rates); // 갱신 시각과 캐시 출처도 확인
});
stream.onerror = () => console.warn("연결/세션 확인 필요");
// 사용 종료: stream.close();
```

## 04. 오류·재시도·데이터 신뢰도

일반 JSON 오류는 detail에 설명이 담깁니다. 422의 detail은 위치/유형을 포함한 배열입니다. 서버 경유 중 일부 브릿지 검증 오류가 502로 전달될 수 있으므로 상태 코드뿐 아니라 detail도 확인하세요.

상태 | 의미 | 처리
--- | --- | ---
400 / 422 | 입력 오류·등록 코드 만료/재사용 | 본문/날짜/필드/코드를 수정. 같은 요청을 무한 재시도하지 않음
401 | 로그인 없음·만료 | 안전한 자격 증명으로 다시 로그인
403 | 관리자 권한 필요·내부 전용 경로 | 권한 및 공개 API 경로 확인
404 | 지원하지 않는 만기·경로·파일 | 지원 목록과 URL 확인
429 | 로그인/등록 실패 횟수 제한 | 대기 후 재시도. 데이터 API 전체의 호출량 정책으로 해석하지 않음
502 / 503 | 원천 연결 없음·응답 오류·전달 실패 | 브릿지와 인포맥스 상태 확인. 읽기 요청은 제한된 횟수로 점진적 재시도

- HTTP 200도 데이터 최신성을 보장하지 않습니다. 빈 결과, null, 원천 연결 상태, 시각과 cached/source를 확인하세요. 서버 연결 성공과 DDE/IMDH 성공은 독립적입니다.
- 대량 히스토리는 기간을 나눠 순차 조회하고 소비자 timeout을 충분히 설정하세요. 임의 병렬 부하나 무제한 재시도를 피하세요. POST 조회 재시도는 중복 감사 로그가 생길 수 있습니다.
- 다중 브릿지는 요청별 임의 선택 후 실패 시 다른 장치로 재시도합니다. _relay의 bridge_id/attempt를 진단에 활용하세요. 서로 다른 PC의 이용 권한이 동일하다고 가정하지 마세요.
## 05. 브릿지 등록과 운영 API

관리자가 서비스 운영현황 → 클라이언트·등록에서 PC 이름을 입력해 코드를 발급합니다. Windows에서 설치 프로그램을 실행하고 최초 연결 창에 코드를 한 번 입력하면 됩니다.

브릿지는 일회용 코드를 POST /api/bridge/enroll에 제출해 장치별 비밀 토큰을 받습니다. 토큰은 해당 PC 설정에 저장되고 서버 DB에는 해시만 보관됩니다. 토큰 원문 조회·복사 기능은 제공하지 않습니다.

장치는 wss://bridge.racoon.pe.kr/bridge-hub/v1/bridges/connect로 직접 연결합니다. 외부 TCP 443만 필요하며 사설 IP나 인바운드 포트 개방은 필요 없습니다. 이 연결은 장치 전용이고 응용프로그램의 데이터 조회 주소가 아닙니다.

등록 해제는 연결과 토큰을 무효화합니다. 재등록하려면 브릿지를 종료하고 PC 설정 파일의 hub 객체만 제거한 후 새 코드로 등록하세요. 앱 전체 설정이나 다른 토큰을 임의로 삭제하지 마세요.

Windows 로그인 자동 실행과 시작 시 서명 검증 자동 업데이트를 지원합니다. 인포맥스 자체 로그인은 별도로 필요합니다. 로그인 사용자 세션의 상태와 권한이 원천 연결에 영향을 줄 수 있습니다.

POST /api/internal/history/query는 기존 로컬 업무 서비스 전용입니다. 외부 앱/LLM의 연동 경로로 사용하지 마세요. /api/dashboard와 /api/bridge/relay는 로그인한 운영 화면의 진단 보조 API입니다.


## API 레퍼런스

### POST /api/bloomberg/reference

현재값·참조정보

인증: 로그인 세션

Bloomberg Desktop API가 설치된 김교보 브릿지에서 조회합니다. Terminal 로그인 및 데이터 권한이 필요합니다. 현재값은 요청 시점 스냅샷입니다.

- securities (body · 필수): Bloomberg 종목 배열 1~50개, 예: IBM US Equity
- fields (body · 필수): 필드 배열 1~50개, 예: PX_LAST, NAME

응답: provider=bloomberg, request_type=reference, securities[], count, _relay. securityError와 fieldExceptions를 반드시 확인하세요.

### POST /api/bloomberg/history

기간별 히스토리

인증: 로그인 세션

Bloomberg 원천에서 히스토리를 중개합니다. 영속 저장과 조회 감사 로그 적재는 제공하지 않습니다.

- securities / fields (body · 필수): 각각 1~50개의 문자열 배열
- start / end (body · 필수): YYYY-MM-DD, 시작≤종료, 최대 3660일
- periodicity (body · 기본 DAILY): DAILY, WEEKLY, MONTHLY

응답: provider=bloomberg, request_type=history, securities[].fieldData[], count, _relay. 부분 응답을 병합하며 종목/필드 오류를 보존합니다.

### GET /api/rates

국고채 수익률 전체

인증: 로그인 세션

1·2·3·5·10·20·30·50년 대표 수익률 스냅샷. 값의 단위는 percent(%)이며 3.25는 3.25%입니다.


응답: market, unit, connected, as_of, rates[]. 각 금리는 tenor, value, updated_at 등을 포함합니다. source/cache 여부와 갱신 시각을 확인하세요.

### GET /api/rates/{tenor}

만기별 수익률

인증: 로그인 세션

국고채 한 만기의 현재 값을 조회합니다.

- tenor (path · 필수): 1Y, 2Y, 3Y, 5Y, 10Y, 20Y, 30Y, 50Y (대소문자 무관)

응답: 단일 금리 객체(tenor, value 등). 지원하지 않는 만기는 404.

### GET /api/rates/stream

금리 변경 스트림 · SSE

인증: 로그인 세션

WebSocket이 아닌 text/event-stream입니다. event: rates의 data JSON을 처리하세요. 브라우저 EventSource는 같은 출처의 로그인 쿠키를 사용합니다.


응답: event: rates / data: JSON. connected, error, rates를 확인합니다. 원천 장애가 HTTP 200 스트림 내부의 상태로 전달될 수 있습니다. 정확히 한 번 전달·재접속 재생을 보장하지 않습니다.

### GET /api/news/feeds

뉴스 피드 목록

인증: 로그인 세션

조회할 수 있는 피드 식별자를 확인합니다.


응답: default: all, feeds: [{id: all|yonhap|third_party, name: ...}]

### GET /api/news

뉴스 검색

인증: 로그인 세션

통합·연합뉴스·타사뉴스를 제목/키워드 및 공급자 분류로 검색합니다. 기사 객체의 필드는 공급자별로 다를 수 있습니다.

- feed (query · 기본 all): all / yonhap / third_party
- limit (query · 기본 50): 1~200
- keywords (query · 선택): 최대 200자. URL 인코딩 필요
- cursor (query · 선택): 17자리 숫자. 원천 커서 규칙을 따르며 임의 ID를 변환해서 사용하지 마세요
- index / source / category (query · 선택·반복 가능): 같은 키를 반복해 전달. 공급자가 사용하는 분류 식별자

응답: provider, fetched_at, cached, feed, count, total, news[]. 제목은 ktitle/title 등을 확인합니다. cached=true이면 원천 실패 시 과거 수집 결과일 수 있고 upstream_error가 추가됩니다.

### GET /api/history/types

지원 데이터 종류

인증: 로그인 세션

접속된 브릿지의 설치된 인포맥스 정의를 조회합니다. 종류 코드만 지원된다고 모든 종목/필드가 제공되는 것은 아닙니다.


응답: count, types[]. kind와 description을 이용해 다음 카탈로그 조회 대상을 선택합니다.

### GET /api/history/catalog

데이터셋·필드 카탈로그

인증: 로그인 세션

쿼리 전에 사용 가능한 데이터셋과 정확한 필드명을 확인합니다. 필드명은 임의로 번역하거나 추측하지 마세요.

- kind (query · 선택): 최대 4자, 예: IR / STK
- dataset (query · 선택): 데이터셋 이름, 최대 100자
- q (query · 기본 빈 문자열): 필드/데이터셋 검색어, 최대 100자
- offset (query · 기본 0): 0 이상
- limit (query · 기본 200): 1~5000. 다음 페이지는 offset을 증가시켜 조회

응답: 카탈로그 데이터셋과 fields 목록. 실제 응답에서 데이터셋 이름과 필드를 선택한 후 history/query에 전달합니다.

### POST /api/history/query

기간별 데이터 조회

인증: 로그인 세션

JSON 본문으로 종목·필드·기간을 지정합니다. 성공/실패, 사용자, 조건, 건수와 소요 시간을 PostgreSQL 요청 이력에 기록합니다. 결과 시계열 자체의 DB 우선 적재는 아직 구현되지 않았습니다.

- kind (body · 필수): STK, IDX, FUT, OPT, FX, IR, BND, STB, FRN, ECO, FND, FD2
- code (body · 필수): 인포맥스 코드 1~80자. 예: BONDKSDCAL19 / 005930
- fields (body · 필수): 카탈로그의 정확한 필드명 배열, 1~100개. 중복은 제거
- start / end (body · 필수): YYYYMMDD / YYYY-MM-DD / YYYY.MM.DD. 시작≤종료. '-'는 원천 기본값이 필요할 때만 사용
- limit (body · 기본 1000): 1~100000. 큰 조회는 기간을 나눠 순차 요청
- period (body · 기본 D): D 일 / W 주 / M 월 / Q 분기 / Y 년 / T 틱 / SS 초 / MM 분. 원천 지원에 따름
- sort (body · 기본 A): A 오름차순 / D 내림차순
- include_date (body · 기본 true): 날짜 열 포함
- cycle / time_range (body · 선택): cycle 1~86400; time_range HH:MM-HH:MM. 원천 주기별 지원 확인
- 기타 고급 옵션 (body · 선택): cycle_sync, real, bizday(0~199), country, quote, close, cons(1/2), comp(1/2), trai(0~4), unit. 정확한 타입은 OpenAPI HistoryQuery 참조

응답: row_count, column_count, columns[{name, wire_type, data_type}], rows[], proxy_duration_ms. null·결측을 0으로 바꾸지 마세요. 날짜 열 이름/열 타입은 columns를 기준으로 해석합니다.

### GET /api/health

서비스·원천 상태

인증: 공개

서버 상태와 브릿지 연결 수, 인포맥스 원천 상태를 확인합니다. 서버 ok=true가 데이터 정상 수신을 뜻하지는 않습니다.


응답: ok, database, bridge, upstream.connected_count, upstream.connected, bridgeError, bridgeCompatibility

### GET /api/bridges

등록 클라이언트·실시간 연결

인증: 로그인 세션

등록된 장치와 현재 연결 풀을 나누어 반환합니다. 토큰 원문은 포함하지 않습니다.


응답: devices[], live.bridges[], live.connected_count. 등록 해제 여부와 원천 연결은 별도 상태입니다.

### POST /api/bridges/pairing

클라이언트 등록 코드 발급

인증: 관리자 세션

장치 등록에만 쓰는 10분·1회용 코드입니다. 데이터 조회용 Bearer 토큰이 아닙니다.

- label (body · 필수): PC 표시 이름, 1~100자

응답: code, expires_at. Windows 브릿지의 최초 연결 창에 입력하세요.

### DELETE /api/bridges/{device_id}

클라이언트 등록 해제

인증: 관리자 세션

접속을 종료하고 장치 토큰을 무효화합니다. 다시 연결하려면 새로 등록해야 합니다.

- device_id (path · 필수): 등록된 장치 UUID

응답: revoked: true|false

### GET /api/query-history

히스토리 조회 요청 이력

인증: 로그인 세션

최신순의 히스토리 조회 감사 로그입니다. 뉴스·금리·SSE 호출, 요청 본문 검증에서 거부된 422 요청, 모든 HTTP 접속을 기록한 로그가 아닙니다.

- limit (query · 기본 50): 1~200

응답: count, items[]. requested_at, username, query_type, request_payload, status, duration_ms, result_count, error_message
