DEV Community

Cover image for 세계 최대 예측 시장 폴리마켓에서 본 API 디자인 패턴
Rihpig
Rihpig

Posted on • Originally published at apidog.com

세계 최대 예측 시장 폴리마켓에서 본 API 디자인 패턴

예측 시장 API는 구현 난도가 높은 편입니다. 만기가 있는 금융 상품, 실시간 확률 가격, 여러 결과가 자본 관계를 공유하는 이벤트, UI 사용자와 자동화된 트레이딩 봇을 동시에 지원해야 하기 때문입니다. 이런 환경에서는 인증, 상태 관리, 데이터 모델, 실시간 스트리밍 등 모든 설계 결정이 즉시 부하와 오류 상황에서 검증됩니다.

지금 Apidog를 사용해 보세요

거래량 기준 세계 최대 예측 시장 플랫폼인 Polymarket의 API는 이러한 문제를 다루는 좋은 사례입니다. 단순한 CRUD API가 아니라, 공개 데이터와 거래 권한, 실시간 상태와 이력 데이터, 전통 금융 모델과 암호화폐 기반 정산을 분리한 계층형 아키텍처입니다.

이 글에서는 Polymarket API에서 추출할 수 있는 8가지 설계 패턴을 구현 관점에서 정리합니다.


패턴 1: 엔티티가 아니라 도메인으로 API를 분리한다

Polymarket은 역할이 명확한 세 가지 API를 노출합니다.

  • 감마 API (gamma-api.polymarket.com) — 시장 탐색, 이벤트, 태그, 검색
  • CLOB API (clob.polymarket.com) — 주문장, 가격, 주문 제출
  • 데이터 API (data-api.polymarket.com) — 사용자 포지션, 거래, 분석, 리더보드

이 구조는 단순히 호스트를 나눈 것이 아닙니다. 각 API는 서로 다른 소비자와 운영 요구 사항을 가집니다.

API 주요 소비자 인증 특성 상태/지연 시간 특성
Gamma 검색 UI, 시장 탐색 서비스 공개 읽기 중심
CLOB 트레이딩 UI, 봇, 마켓 메이커 읽기 공개, 거래 인증 필요 저지연·실시간
Data 포트폴리오 UI, 분석 도구 공개, 지갑 주소 기반 이력·집계 중심

구현 원칙

/markets, /orders, /users처럼 엔티티별로 하나의 API에 모두 넣으면 처음에는 편해 보입니다. 하지만 검색, 주문 제출, 분석은 인증 방식·캐시 전략·확장 방식이 다릅니다.

도메인 분리를 적용할 때는 다음 질문부터 시작하세요.

  1. 이 API의 주된 사용자는 누구인가?
  2. 읽기와 쓰기의 보안 요구 사항은 같은가?
  3. 실시간성이 필요한가, 캐시 가능한가?
  4. 독립적으로 배포·확장·제한해야 하는가?

예를 들어 다음처럼 경계를 나눌 수 있습니다.

catalog.example.com    → 검색, 탐색, 메타데이터
trading.example.com    → 주문장, 주문, 체결
portfolio.example.com  → 포지션, 손익, 거래 이력
Enter fullscreen mode Exit fullscreen mode

핵심은 리소스 이름이 아니라 사용 목적과 운영 특성으로 API 경계를 잡는 것입니다.


패턴 2: 시장 데이터는 공개하고, 거래 권한만 제한한다

Polymarket에서는 가격, 주문장, 이벤트 메타데이터, 과거 거래 같은 시장 데이터를 공개적으로 조회할 수 있습니다.

curl "https://gamma-api.polymarket.com/events?limit=5"
Enter fullscreen mode Exit fullscreen mode

API 키나 OAuth 없이 데이터를 읽을 수 있습니다.

이 방식은 금융 데이터에 대한 전통적인 접근과 다릅니다. 많은 거래소는 시장 데이터를 유료 자산으로 취급하지만, Polymarket은 이를 생태계의 기반 인프라로 다룹니다. 데이터를 읽고 도구를 만드는 사용자가 많아질수록 시장의 유동성과 활용도도 높아집니다.

구현 원칙

읽기와 쓰기를 같은 인증 정책으로 묶지 마세요.

GET  /markets          → 공개
GET  /orderbook/{id}   → 공개
GET  /prices/{id}      → 공개

POST /orders           → 인증 필요
DELETE /orders/{id}    → 인증 필요
POST /withdrawals      → 강한 인증 필요
Enter fullscreen mode Exit fullscreen mode

특히 데이터 소비가 많은 플랫폼이라면 다음 구조가 효과적입니다.

  • 공개 읽기 API: 캐시, CDN, 높은 처리량에 최적화
  • 인증 쓰기 API: 서명 검증, 감사 로그, 권한 제어에 최적화
  • 민감한 사용자 데이터 API: 사용자 또는 지갑 주소 기준 접근 제어

사용자가 자격 증명 없이 가격과 시장을 조회할 수 있다면, 첫 통합 단계의 마찰을 크게 줄일 수 있습니다. 인증은 실제로 권한이 필요한 작업에만 추가하는 것이 좋습니다.


패턴 3: 신원 증명과 요청 승인을 분리하는 2단계 인증

Polymarket의 거래 API는 두 수준의 인증을 사용합니다.

  • L1 인증: 개인 키 기반 EIP-712 서명으로 지갑 소유권 증명
  • L2 인증: 파생된 API 자격 증명 기반 HMAC-SHA256 요청 서명

L1 인증은 API 자격 증명을 만들거나 파생할 때 사용합니다.

// L1: 개인 키를 사용해 API 자격 증명 파생
const credentials = await client.createOrDeriveApiKey();

// → { key: "...", secret: "...", passphrase: "..." }
Enter fullscreen mode Exit fullscreen mode

그 후 일상적인 거래 요청에는 L2 인증 헤더를 사용합니다.

{
  "POLY_ADDRESS": "0x...",
  "POLY_SIGNATURE": "<hmac-sha256>",
  "POLY_TIMESTAMP": "1716000000",
  "POLY_API_KEY": "550e8400-...",
  "POLY_PASSPHRASE": "..."
}
Enter fullscreen mode Exit fullscreen mode

구현 원칙

모든 요청에 가장 강한 자격 증명을 사용하면 보안은 높아 보이지만 운영 비용과 노출 위험도 커집니다. 대신 작업의 위험도에 따라 인증 강도를 나누세요.

고위험·저빈도 작업
- API 키 발급
- 계정 복구
- 출금 주소 변경
- 권한 위임
→ 강한 서명 또는 재인증

일상적·고빈도 작업
- 주문 제출
- 주문 취소
- 상태 조회
→ 세션 키, API 키, HMAC 서명
Enter fullscreen mode Exit fullscreen mode

이 패턴은 암호화폐에만 적용되는 방식이 아닙니다.

  • L1: “당신이 계정 소유자임을 증명하세요.”
  • L2: “이 요청이 유효한 세션 또는 위임된 자격 증명에서 왔음을 증명하세요.”

민감한 작업과 반복 호출을 분리하면 보안과 사용성을 함께 관리할 수 있습니다.


패턴 4: 주문을 단순 요청이 아니라 서명된 권한 문서로 취급한다

Polymarket에서 주문을 제출하는 것은 서버에 데이터를 보내는 것 이상입니다. 주문은 거래를 승인하는 암호화 서명된 메시지입니다.

const response = await client.createAndPostOrder(
  {
    tokenID: "71321045679...",
    price: 0.65,
    size: 100,
    side: Side.BUY,
  },
  {
    tickSize: "0.01",
    negRisk: false,
  },
  OrderType.GTC
);
Enter fullscreen mode Exit fullscreen mode

SDK는 내부적으로 EIP-712 형식의 주문 데이터를 만들고, 개인 키로 서명한 뒤, 서명과 주문 정보를 함께 제출합니다. 매칭 엔진은 오프체인에서 동작하지만, 거래가 매칭되면 해당 서명을 기반으로 Polygon에서 온체인 정산이 이루어집니다.

즉, 운영자는 사용자를 대신해 임의로 거래를 승인하는 주체가 아닙니다. 서명된 주문 자체가 권한 부여 역할을 합니다.

구현 원칙

금융 거래, 계약 승인, 고위험 상태 변경처럼 부인 방지가 중요한 작업에서는 요청 본문 자체에 권한 정보를 포함하는 방식을 고려할 수 있습니다.

일반적인 API 호출은 다음 의미에 가깝습니다.

"서버가 나를 대신해서 이 작업을 수행해 주세요."
Enter fullscreen mode Exit fullscreen mode

서명된 요청은 다음에 가깝습니다.

"이 작업을 승인한다는 검증 가능한 서명 문서입니다."
Enter fullscreen mode Exit fullscreen mode

서명 대상에는 최소한 다음 항목이 포함되어야 합니다.

type SignedOrder = {
  assetId: string;
  side: "BUY" | "SELL";
  price: string;
  size: string;
  expiration: number;
  nonce: number;
};
Enter fullscreen mode Exit fullscreen mode

그리고 서버는 다음을 검증해야 합니다.

  1. 서명이 주문 페이로드와 일치하는가?
  2. 서명자가 실제 권한 보유자인가?
  3. 만료 시간이 지나지 않았는가?
  4. nonce가 재사용되지 않았는가?
  5. 주문 가격과 수량이 시장 제약을 만족하는가?

이 방식은 전송 계층 인증만으로는 얻기 어려운 검증 가능성과 부인 방지 특성을 제공합니다.


패턴 5: 데이터 모델에 도메인 관계를 명시적으로 표현한다

Polymarket의 핵심 데이터 모델은 이벤트(Events)시장(Markets) 을 구분합니다.

  • 이벤트: 해결해야 할 상위 질문
  • 시장: 이벤트 안에서 거래 가능한 구체적인 결과

예를 들어 “2026년 펜실베이니아 상원 선거에서 누가 이길 것인가?”는 이벤트입니다. “Bob Casey가 이길 것인가?”는 그 이벤트에 속한 시장입니다.

{
  "id": "501",
  "title": "2026 Pennsylvania Senate Race",
  "negRisk": true,
  "markets": [
    {
      "id": "2301",
      "question": "Will Bob Casey win?",
      "outcomePrices": "[\"0.42\", \"0.58\"]"
    },
    {
      "id": "2302",
      "question": "Will Dave McCormick win?",
      "outcomePrices": "[\"0.35\", \"0.65\"]"
    },
    {
      "id": "2303",
      "question": "Will a third candidate win?",
      "outcomePrices": "[\"0.23\", \"0.77\"]"
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

이 모델은 단순한 데이터 저장 구조가 아닙니다. 이벤트와 시장의 개념적 관계, 그리고 이벤트 수준의 자본 관계를 API에 드러냅니다.

구현 원칙

중요한 도메인 관계를 클라이언트가 추론하게 하지 마세요. 응답 모델에 명시적으로 포함하세요.

특히 다음처럼 병렬 배열을 제공하는 경우에는 인덱스 관계를 명확히 문서화해야 합니다.

outcomes[0]       ↔ outcomePrices[0]
outcomes[1]       ↔ outcomePrices[1]
Enter fullscreen mode Exit fullscreen mode

클라이언트 구현에서는 가능하다면 즉시 더 안전한 형태로 정규화하는 것이 좋습니다.

const outcomes = ["Yes", "No"];
const prices = ["0.42", "0.58"];

const normalizedOutcomes = outcomes.map((name, index) => ({
  name,
  price: Number(prices[index]),
}));

// [
//   { name: "Yes", price: 0.42 },
//   { name: "No", price: 0.58 }
// ]
Enter fullscreen mode Exit fullscreen mode

자동 거래 시스템에서 이벤트 수준의 속성이나 시장 간 관계를 놓치면 포지션 계산 자체가 잘못될 수 있습니다. 따라서 도메인 제약은 API 모델에서 숨기지 않는 편이 안전합니다.


패턴 6: NegRisk처럼 중요한 자본 관계를 API 필드로 노출한다

이벤트의 negRisk 플래그는 금융 등가 관계를 프로그래밍 가능한 상태로 만드는 사례입니다.

일반적인 다중 결과 이벤트에서는 시장이 서로 독립적일 수 있습니다. 하지만 정확히 하나의 결과만 승리하는 이벤트에서는 포지션 간에 수학적 관계가 생깁니다.

결과 A의 ‘아니요’ 토큰 1개 ≡ 나머지 모든 결과의 ‘예’ 토큰 1개

예를 들어 펜실베이니아 상원 선거에서 “기타 후보 승리”에 대한 ‘아니요’ 포지션은 다음과 같은 형태로 변환될 수 있습니다.

이전 이후
1× 아니요 (기타) 1× 예 (Casey) + 1× 예 (McCormick)

Polymarket API는 이 특성을 숨기지 않습니다. 시장 객체에 negRisk: true를 노출하고, 해당 시장에서 주문을 생성할 때도 주문 옵션에 negRisk: true를 전달해야 합니다.

const orderOptions = {
  tickSize: "0.01",
  negRisk: true,
};
Enter fullscreen mode Exit fullscreen mode

구현 원칙

도메인 불변성을 문서의 각주로만 남기지 마세요. 잘못된 사용이 금전적 손실이나 데이터 불일치로 이어질 수 있다면 API 계약에 포함해야 합니다.

좋은 API 필드는 다음 조건을 만족합니다.

  • 생략하면 잘못된 동작이 발생할 수 있다.
  • 클라이언트 로직에 직접 영향을 준다.
  • 서버 검증에도 사용된다.
  • 상태 조회 응답과 요청 페이로드에서 일관되게 나타난다.

예를 들어 서버에서도 시장 메타데이터와 주문 옵션의 일치 여부를 검증해야 합니다.

if (market.negRisk !== request.negRisk) {
  throw new Error("negRisk 설정이 시장 구성과 일치하지 않습니다.");
}
Enter fullscreen mode Exit fullscreen mode

패턴 7: 동적 틱 크기를 정적 설정이 아니라 실시간 상태로 다룬다

많은 금융 API는 틱 크기를 고정된 설정 값으로 다룹니다. Polymarket은 시장 가격에 따라 틱 크기가 바뀔 수 있으며, 이 변경을 실시간 이벤트로 제공합니다.

가격이 극단값에 가까워지면 최소 틱 크기가 0.01에서 0.001로 변경될 수 있습니다.

{
  "event_type": "tick_size_change",
  "asset_id": "65818619657...",
  "old_tick_size": "0.01",
  "new_tick_size": "0.001",
  "timestamp": "100000000"
}
Enter fullscreen mode Exit fullscreen mode

가격이 0.04일 때 0.01 단위 변화는 큰 비율 변동입니다. 0.04 → 0.03은 25% 변화이므로, 극단적인 확률 영역에서는 더 작은 가격 단위가 필요합니다.

구현 원칙

틱 크기를 하드코딩하지 마세요. 주문 생성 전에 현재 시장 메타데이터를 조회하고, WebSocket 상태 변경도 반영해야 합니다.

const marketState = new Map<string, { tickSize: string }>();

function onTickSizeChange(event: {
  asset_id: string;
  new_tick_size: string;
}) {
  marketState.set(event.asset_id, {
    tickSize: event.new_tick_size,
  });
}
Enter fullscreen mode Exit fullscreen mode

주문 가격을 만들 때는 현재 틱 크기에 맞게 정규화합니다.

function roundToTick(price: number, tickSize: number) {
  return Math.round(price / tickSize) * tickSize;
}

const tickSize = 0.001;
const price = roundToTick(0.9734, tickSize);

// 0.973
Enter fullscreen mode Exit fullscreen mode

중요한 점은 틱 크기가 설정값이 아니라 시장 상태라는 것입니다. 상태가 변경될 수 있다면 API는 이를 명시적으로 전달해야 하고, 클라이언트는 이를 구독해 로컬 상태를 갱신해야 합니다.


패턴 8: 소비자 프로필이 다르면 WebSocket 인프라도 분리한다

Polymarket은 두 개의 WebSocket 시스템을 운영합니다.

시장 채널

wss://ws-subscriptions-clob.polymarket.com/ws/market

시장 채널은 트레이딩 소비자용입니다. 토큰 ID를 기준으로 구독하며 주문장 스냅샷, 가격 변경, 거래 체결, 틱 크기 변경을 받습니다.

{
  "assets_ids": [
    "65818619657568813474341868652308942079804919287380422192892211131408793125422"
  ],
  "type": "market"
}
Enter fullscreen mode Exit fullscreen mode

실시간 데이터 소켓

wss://ws-live-data.polymarket.com

실시간 데이터 소켓은 댓글, Binance 및 Chainlink 암호화폐 가격, 주식 가격, 소셜 상호작용 이벤트 등을 제공합니다. 토픽 기반으로 구독합니다.

{
  "action": "subscribe",
  "subscriptions": [
    {
      "topic": "crypto_prices",
      "type": "update",
      "filters": "btcusdt,ethusd"
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

두 채널의 사용자 요구 사항은 다릅니다.

소비자 필요한 데이터 주요 요구 사항
트레이딩 봇·마켓 메이커 주문장, 체결, 틱 크기 낮은 지연 시간, 순서 보장, 빠른 복구
일반 UI 댓글, 소셜 활동, 외부 가격 폭넓은 이벤트, 유연한 구독
분석 도구 집계 데이터, 이력 데이터 재처리 가능성, 안정적인 전달

구현 원칙

한 WebSocket 엔드포인트에 모든 실시간 이벤트를 넣으면 결국 다음 둘 중 하나가 발생합니다.

  • 소셜·UI 이벤트까지 트레이딩 수준의 인프라로 처리하게 된다.
  • 주문장 이벤트까지 일반적인 UI 수준의 전달 보장으로 처리하게 된다.

둘 다 비효율적입니다.

실시간 채널을 설계할 때는 먼저 소비자를 분리하세요.

trading-ws.example.com
- orderbook
- trades
- execution reports
- market configuration changes

activity-ws.example.com
- comments
- notifications
- social activity
- public feed updates
Enter fullscreen mode Exit fullscreen mode

소비자 간에 지연 시간 허용 범위, 데이터량, 순서 보장, 재연결 전략, 실패 허용 수준이 다르다면 별도 인프라가 더 적합합니다.


이 패턴들의 공통점

Polymarket의 API 설계는 하나의 원칙으로 정리할 수 있습니다.

API는 도메인의 실제 구조를 숨기기보다 드러내야 합니다.

  • 세 개의 API 계층은 실제 도메인 경계에 맞춰져 있습니다.
  • 공개 우선 접근은 시장 데이터가 생태계 인프라라는 특성을 반영합니다.
  • 2단계 인증은 신원 증명과 반복 요청 승인을 분리합니다.
  • 서명된 주문은 비수탁형 거래 권한을 표현합니다.
  • 이벤트와 시장 모델은 실제 개념적 계층을 유지합니다.
  • negRisk는 자본 관계를 명시적인 상태로 노출합니다.
  • 동적 틱 크기는 시장 상태 변경을 클라이언트에 전달합니다.
  • 분리된 WebSocket 계층은 서로 다른 실시간 소비자를 지원합니다.

API 설계에서는 호출하기 쉬운 URL, 일관된 명명, 예측 가능한 오류 처리도 중요합니다. 하지만 금융·거래·고위험 도메인에서는 그보다 한 단계 더 나아가야 합니다.

도메인에 중요한 구분이 있다면 API에도 그 구분이 있어야 합니다.

도메인에 제약이 있다면 API가 이를 검증해야 합니다.

도메인 상태가 바뀐다면 API가 이를 이벤트로 전달해야 합니다.

이 원칙을 따르면 클라이언트 구현은 다소 더 많은 상태와 규칙을 다뤄야 합니다. 대신 소비자는 자신이 연결한 시스템의 실제 동작 방식을 이해한 상태에서 더 안전하게 통합할 수 있습니다.

Top comments (0)