메인 콘텐츠로 건너뛰기
Beta — 이 기능은 현재 베타 버전입니다. API가 변경될 수 있습니다.
본 문서에서는 ChainStream Webhook의 작동 원리, 설정 방법, 모범 사례를 소개하여 실시간 온체인 이벤트 전달 구현을 돕습니다.
Webhook 기능은 모든 사용자가 이용할 수 있습니다.

작동 원리

데이터 흐름

핵심 기능

기능설명
실시간 전달이벤트 발생 후 밀리초 단위로 전달
안정적 전달실패 시 자동 재시도
서명 검증HMAC 서명 위조 방지
필터 규칙이벤트 타입 필터링 지원

지원 이벤트 타입

Webhook은 현재 다음 이벤트 타입(채널)을 지원합니다:
채널설명일반적 용도
sol.token.createdSolana 새 토큰 생성신규 토큰 발견, 초기 기회
sol.token.migratedSolana 토큰 졸업/마이그레이션Pump.fun 등의 플랫폼에서 졸업한 토큰 추적
추가 이벤트 타입이 개발 중입니다. 계속 지켜봐 주세요!

Webhook 엔드포인트 생성

API 엔드포인트

POST /v1/webhook/endpoint
Content-Type: application/json
Authorization: Bearer YOUR_ACCESS_TOKEN

요청 파라미터

파라미터타입필수설명
urlstringWebhook 콜백 URL (HTTPS 필수)
channelsarray구독할 이벤트 타입 목록
descriptionstring아니오엔드포인트 설명
disabledboolean아니오비활성화 여부, 기본값 false
filterTypesarray아니오필터 타입
metadataobject아니오사용자 정의 메타데이터
rateLimitinteger아니오속도 제한

요청 예시

{
  "url": "https://your-server.com/webhook",
  "channels": ["sol.token.created", "sol.token.migrated"],
  "description": "Monitor new tokens and graduated tokens"
}

응답 예시

{
  "id": "ep_abc123",
  "url": "https://your-server.com/webhook",
  "channels": ["sol.token.created", "sol.token.migrated"],
  "description": "Monitor new tokens and graduated tokens",
  "disabled": false
}

Webhook 알림 형식

Webhook 알림 데이터 구조는 WebSocket 푸시와 일관됩니다.

신규 토큰 생성 (sol.token.created)

{
  "channel": "sol.token.created",
  "timestamp": 1706947200000,
  "data": {
    "a": "6p6xgHyF7AeE6TZkSmFsko444wqoP15icUSqi2jfGiPN",
    "n": "Example Token",
    "s": "EXT",
    "dec": 9,
    "cts": 1706947200000,
    "lf": {
      "pa": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P",
      "pf": "pump_fun",
      "pn": "Pump.fun"
    }
  }
}
필드 설명:
필드설명
a토큰 주소
n토큰 이름
s토큰 심볼
dec소수점 자릿수
cts생성 타임스탬프 (밀리초)
lf.pa런치 플랫폼 프로그램 주소
lf.pf프로토콜 패밀리
lf.pn프로토콜 이름

토큰 졸업 (sol.token.migrated)

{
  "channel": "sol.token.migrated",
  "timestamp": 1706947200000,
  "data": {
    "a": "6p6xgHyF7AeE6TZkSmFsko444wqoP15icUSqi2jfGiPN",
    "n": "Example Token",
    "s": "EXT",
    "cts": 1706947200000,
    "lf": {
      "pa": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P",
      "pf": "pump_fun",
      "pn": "Pump.fun"
    },
    "mt": {
      "pa": "675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8",
      "pf": "raydium",
      "pn": "Raydium"
    }
  }
}
추가 필드:
필드설명
mt.pa마이그레이션 대상 플랫폼 프로그램 주소
mt.pf마이그레이션 대상 프로토콜 패밀리
mt.pn마이그레이션 대상 프로토콜 이름

Webhook URL 요구사항

요구사항설명
✅ HTTPSHTTPS 프로토콜 사용 필수
✅ 공개 접근 가능URL이 공용 인터넷에서 접근 가능해야 함
✅ 2xx 응답성공 시 2xx 상태 코드 반환 필수
✅ 응답 시간5초 이내 응답 필요
✅ 멱등 처리중복 요청 처리 가능해야 함

보안 검증

Webhook 시크릿 조회

엔드포인트 생성 후, 이 API로 시크릿을 조회합니다:
GET /v1/webhook/endpoint/{id}/secret
응답:
{
  "secret": "whsec_abcdXXX"
}

서명 검증

각 Webhook 요청에는 요청 출처를 검증하기 위한 서명 헤더가 포함됩니다:
X-Webhook-Signature: <signature>
X-Webhook-Timestamp: <timestamp>

검증 흐름

코드 예시

const crypto = require('crypto');

function verifyWebhook(req, secret) {
  const signature = req.headers['x-webhook-signature'];
  const timestamp = req.headers['x-webhook-timestamp'];
  const body = JSON.stringify(req.body);
  
  // 타임스탬프 확인 (5분 윈도우)
  const now = Date.now();
  if (Math.abs(now - parseInt(timestamp)) > 300000) {
    return false;
  }
  
  // 서명 계산
  const message = `${timestamp}.${body}`;
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(message)
    .digest('hex');
  
  // 안전한 비교
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expectedSignature)
  );
}

Webhook 엔드포인트 관리

엔드포인트 목록

GET /v1/webhook/endpoint
쿼리 파라미터:
파라미터타입설명
limitinteger페이지당 항목 수 (1-100, 기본값 100)
iteratorstring페이지네이션 이터레이터
orderstring정렬 순서 (오름차순/내림차순)

엔드포인트 상세 조회

GET /v1/webhook/endpoint/{id}

엔드포인트 업데이트

PATCH /v1/webhook/endpoint
{
  "endpointId": "ep_abc123",
  "channels": ["sol.token.created"],
  "description": "Monitor new tokens only"
}

엔드포인트 삭제

DELETE /v1/webhook/endpoint/{id}

시크릿 로테이션

POST /v1/webhook/endpoint/{id}/secret/rotate

모범 사례

✅ 빠른 응답

# 권장: 먼저 응답하고 나중에 처리
@app.route('/webhook', methods=['POST'])
def webhook():
    # 서명 검증
    if not verify_webhook(request, SECRET):
        return "Invalid signature", 401
    
    # 큐에 넣어 비동기 처리
    queue.put(request.json)
    
    # 즉시 200 반환
    return "OK", 200

✅ 멱등성 처리

각 이벤트에는 고유 식별자가 포함됩니다. 서버에서 처리된 이벤트를 기록하세요:
# Redis를 사용하여 처리된 이벤트 기록
def process_webhook(event):
    event_id = f"{event['channel']}:{event['data']['a']}:{event['timestamp']}"
    
    # 이미 처리되었는지 확인
    if redis.exists(f"processed:{event_id}"):
        return {"status": "already_processed"}
    
    # 이벤트 처리
    handle_event(event)
    
    # 처리됨으로 표시 (TTL 24시간)
    redis.setex(f"processed:{event_id}", 86400, "1")
    
    return {"status": "ok"}

✅ 보안

항상 서명 검증

모든 요청에서 서명 검증

HTTPS 사용

전송 보안 확보

시크릿 정기 로테이션

90일마다 권장

민감한 데이터 보호

민감한 데이터를 로그에 기록하지 않기

✅ 안정성

멱등성 구현

중복 요청 처리

메시지 큐 버퍼

큐를 사용한 비동기 처리

적절한 타임아웃

장시간 블로킹 방지

포괄적 로깅

트러블슈팅을 위한 핵심 정보 기록

FAQ

문제 해결 단계:
  1. URL 접근 가능 여부 확인 — URL이 공용 인터넷에서 도달 가능한지 테스트
  2. HTTPS 확인 — 유효한 SSL 인증서를 사용하고 있는지 확인
  3. 엔드포인트 상태 확인disabledtrue가 아닌지 확인
  4. 채널 확인 — 올바른 이벤트 타입을 구독하고 있는지 확인
이는 재시도 메커니즘 때문일 수 있습니다. 멱등성 처리를 구현하세요:
  1. 고유한 이벤트 식별자 사용 (채널 + 토큰 주소 + 타임스탬프)
  2. 요청 수신 시 이미 처리되었는지 확인
  3. TTL이 있는 캐시 (Redis 등)를 사용하여 저장
  1. ngrok을 사용하여 로컬 서비스 노출
  2. ngrok URL을 가리키는 Webhook 엔드포인트 생성
  3. 실제 이벤트 트리거를 기다리거나 테스트 환경 사용
  4. 로컬 서비스 로그 확인

API 엔드포인트 요약

기능엔드포인트
엔드포인트 목록GET /v1/webhook/endpoint
엔드포인트 생성POST /v1/webhook/endpoint
엔드포인트 업데이트PATCH /v1/webhook/endpoint
엔드포인트 상세 조회GET /v1/webhook/endpoint/{id}
엔드포인트 삭제DELETE /v1/webhook/endpoint/{id}
시크릿 조회GET /v1/webhook/endpoint/{id}/secret
시크릿 로테이션POST /v1/webhook/endpoint/{id}/secret/rotate

관련 문서

WebSocket API

실시간 데이터 구독

엔드포인트 API 레퍼런스

전체 API 문서