시세 API 사용 가이드

REST JSON + SSE로 해외선물·국내선물 시세를 제공합니다. 외부 플랫폼은 고정 symbol(NQ, ES …)만 키로 쓰고, contract(근월물)는 서버가 자동 전환합니다.

고정 19종목 REST SSE 근월물 자동

전체 마크다운: 프로젝트 docs/API.md

1. 빠른 시작

# 로그인
curl -c cookies.txt -X POST "http://localhost:8080/login" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "username=YOUR_ID&password=YOUR_PASSWORD"

# 고정 종목 카탈로그 (권장)
curl -b cookies.txt "http://localhost:8080/api/market/catalog"

# 스냅샷
curl -b cookies.txt "http://localhost:8080/api/market/live"

# 실시간 SSE
curl -N -b cookies.txt "http://localhost:8080/api/market/stream"
  1. POST /login → 쿠키 유지
  2. GET /api/market/catalog → 초기 종목·가격
  3. GET /api/market/stream → 틱 구독
  4. (선택) 주기적으로 catalog/live로 보정

2. 인증

POST /login

승인된 계정만 사용 가능합니다. 성공 시 JSESSIONID 쿠키가 발급됩니다.

Content-Type: application/x-www-form-urlencoded

username=USER&password=PASS

이후 모든 API에 쿠키를 포함하세요.

fetch("/api/market/catalog", { credentials: "include" });
경로권한
/api/market/catalog로그인 사용자
/api/market/live로그인 사용자
/api/market/stream로그인 사용자
/api/market/symbols, /status, /selectionADMIN

외부 연동은 catalog / live / stream만 쓰면 됩니다.

3. 핵심 개념

고정 symbol vs 근월물 contract

필드의미외부에서
symbol상품 고정 코드 (NQ)필수 키
marketOVERSEAS_FUTURE 등구분용
contract실제 월물 (NQU26)표시·참고만
contractExpiryyyyyMM참고
fixed고정 유통 여부true면 항상 제공
statusLIVE / PENDING시세 유무

월물 코드(NQU26)를 DB 키로 저장하지 마세요. 만기마다 끊깁니다.

서버 수집 방식

해외선물 → WebSocket(주) + REST(PENDING 보완)
국내선물(KOSPI200) → REST만

SSE source: websocket | poll — 클라이언트는 price만 반영하면 됩니다.

4. 고정 유통 종목 (라이브)

서버에서 항상 활성화됩니다. 해제할 수 없습니다.

symbol market name 근월물 현재가 상태
불러오는 중...

복합 키 예: OVERSEAS_FUTURE:NQ, DOMESTIC_FUTURE:KOSPI200

5. GET /api/market/catalog ★권장

고정 19종목 + 현재가. 외부 연동 기본 API.

{
  "count": 19,
  "note": "고정 유통 종목...",
  "data": [
    {
      "market": "OVERSEAS_FUTURE",
      "symbol": "NQ",
      "name": "E-mini Nasdaq100",
      "source": "alias",
      "contract": "NQU26",
      "contractExpiry": "202609",
      "fixed": true,
      "price": 2936700.0,
      "updatedAt": "2026-08-25T19:50:00.123",
      "status": "LIVE",
      "selected": true
    }
  ]
}

가격은 KIS 원본 스케일입니다. (예: Nasdaq 2936700 ≈ 표시 29367.00) 상품별 소수점 규칙은 클라이언트에서 적용하세요.

6. GET /api/market/live

현재 수집 대상 스냅샷(고정 + 관리자 추가 선택 가능). 고정 19개만 필요하면 catalog를 쓰세요.

{
  "page": 0,
  "size": 1000,
  "total": 19,
  "liveCount": 15,
  "selectedOnly": true,
  "data": [ /* catalog data[] 와 동일 스키마 */ ]
}

7. GET /api/market/stream (SSE)

text/event-stream. 끊기면 재연결하세요.

event설명
connected연결 성공 {"status":"ok"}
market시세 틱
heartbeat약 15초마다 (연결 유지 확인)
event: market
data: {
  "source": "websocket",
  "market": "OVERSEAS_FUTURE",
  "symbol": "NQ",
  "contract": "NQU26",
  "price": 2936725.0,
  "updatedAt": "2026-08-25T19:51:02.456"
}

브라우저 예시

const prices = new Map();

function connect() {
  const es = new EventSource("/api/market/stream");

  es.addEventListener("market", (e) => {
    const tick = JSON.parse(e.data);
    // 반드시 symbol 로 매칭
    prices.set(tick.symbol, {
      price: tick.price,
      contract: tick.contract,
      updatedAt: tick.updatedAt,
    });
  });

  es.onerror = () => {
    es.close();
    setTimeout(connect, 3000);
  };
}

connect();

8. 관리자 API

GET /api/market/status

수집기 상태. wsConnected, wsFutureReady, lastError, liveCount 등.

GET /api/market/symbols

Query: query, page, size, selectedOnly

GET /api/market/selection

fixedKeys, selectedKeys 반환.

POST /api/market/selection

{ "keys": ["OVERSEAS_FUTURE:NQ", "KOSPI:005930"] }

고정 alias는 요청에서 빠져도 서버가 다시 포함합니다.

9. 권장 연동 패턴

catalog 초기화 + SSE (권장)

async function bootstrap() {
  const catalog = await fetch("/api/market/catalog", { credentials: "include" })
    .then((r) => r.json());

  const state = Object.fromEntries(
    catalog.data.map((row) => [
      row.symbol,
      { price: row.price, contract: row.contract, status: row.status },
    ])
  );

  const es = new EventSource("/api/market/stream");
  es.addEventListener("market", (e) => {
    const t = JSON.parse(e.data);
    state[t.symbol] = {
      price: t.price,
      contract: t.contract ?? state[t.symbol]?.contract,
      status: "LIVE",
    };
    render(state);
  });
}

스냅샷 폴링만 (단순)

setInterval(async () => {
  const { data } = await fetch("/api/market/catalog", { credentials: "include" })
    .then((r) => r.json());
  data.forEach((row) => updateUi(row.symbol, row.price, row.status));
}, 2000);

10. 에러·주의사항

HTTP의미대응
200성공
401 / 로그인 페이지미인증재로그인
403권한 부족ADMIN API는 관리자만
500서버 오류status.lastError 확인
  • PENDING + price: null → 휴장·틱 공백·월물 전환·KIS 권한 문제 가능
  • SSE는 프록시 타임아웃으로 끊길 수 있음 → onerror 재연결
  • 세션 만료 시 REST·SSE 모두 실패
  • 다른 도메인 연동 시 CORS / SameSite 쿠키 설정 필요

11. 연동 체크리스트

  • 승인 계정으로 POST /login 성공
  • /api/market/catalog에서 19개 symbol 확인
  • 가격 매칭 키가 symbol인지 확인 (contract 아님)
  • /api/market/stream에서 market 이벤트 수신
  • SSE 끊김 시 자동 재연결
  • PENDING 종목 UI 구분