바이낸스 Node.js API 연결의 표준 경로는 네이티브 crypto 모듈을 사용하여 HMAC-SHA256 서명을 생성하고 axios 또는 undici로 요청을 보내거나, node-binance-api 또는 binance-api-node와 같은 커뮤니티 라이브러리를 직접 사용하는 것입니다. 이 글에서는 네이티브 캡슐화, 인기 SDK 비교, WebSocket 구독, TypeScript 타입 정의의 4개 모듈에 대한 전체 코드를 제공하며, 모두 npm install 후 즉시 실행 가능합니다. 아직 바이낸스 계정이 없다면 바이낸스 공식 사이트에서 가입을 완료할 수 있으며, 신규 사용자는 무료 회원가입을 통해 계정을 개설할 수 있습니다.
1. Node.js SDK 비교표
| 라이브러리명 | 개발자 | 주간 다운로드 | 특징 | 추천 시나리오 |
|---|---|---|---|---|
| node-binance-api | jaggedsoft | 30k+ | 포괄적인 기능, 콜백 방식 | 스크립트 빠른 검증 |
| binance-api-node | Ashlar | 50k+ | Promise 기반, TypeScript 친화적 | 운영 환경 TypeScript |
| @binance/connector | 공식 | 40k+ | 가볍고 얇은 캡슐화 | 최신 API 업데이트 반영 |
| ccxt | 커뮤니티 | 80k+ | 여러 거래소 통합 지원 | 거래소 간 양적 매매 |
| 자체 개발 axios + crypto | — | — | 극도의 제어 가능성 | 고빈도 마켓 메이킹 |
2. 네이티브 crypto 서명 + axios 캡슐화
1. 프로젝트 초기화
mkdir binance-node && cd binance-node
npm init -y
npm install axios dotenv
.env 파일:
BINANCE_API_KEY=your_key_here
BINANCE_SECRET_KEY=your_secret_here
2. 핵심 서명 캡슐화
src/client.js:
const crypto = require('crypto');
const axios = require('axios');
require('dotenv').config();
const API_KEY = process.env.BINANCE_API_KEY;
const SECRET_KEY = process.env.BINANCE_SECRET_KEY;
const BASE_URL = 'https://api.binance.com';
function sign(params) {
const query = new URLSearchParams(params).toString();
return crypto
.createHmac('sha256', SECRET_KEY)
.update(query)
.digest('hex');
}
async function request(method, path, params = {}, signed = false) {
const headers = {};
if (signed) {
params.timestamp = Date.now();
params.recvWindow = 5000;
params.signature = sign(params);
headers['X-MBX-APIKEY'] = API_KEY;
}
try {
const config = {
method,
url: `${BASE_URL}${path}`,
headers,
timeout: 10000,
};
if (method === 'GET' || method === 'DELETE') {
config.params = params;
} else {
config.data = new URLSearchParams(params).toString();
config.headers['Content-Type'] = 'application/x-www-form-urlencoded';
}
const r = await axios(config);
return r.data;
} catch (err) {
if (err.response) {
const { code, msg } = err.response.data;
throw new Error(`Binance ${code}: ${msg}`);
}
throw err;
}
}
module.exports = { request, sign };
3. 계정 및 시세 호출
src/account.js:
const { request } = require('./client');
async function getAccount() {
return request('GET', '/api/v3/account', {}, true);
}
async function getPrice(symbol) {
const r = await request('GET', '/api/v3/ticker/price', { symbol });
return parseFloat(r.price);
}
async function getDepth(symbol, limit = 20) {
return request('GET', '/api/v3/depth', { symbol, limit });
}
async function placeLimitOrder(symbol, side, quantity, price) {
return request('POST', '/api/v3/order', {
symbol,
side,
type: 'LIMIT',
timeInForce: 'GTC',
quantity,
price,
}, true);
}
async function cancelOrder(symbol, orderId) {
return request('DELETE', '/api/v3/order', { symbol, orderId }, true);
}
module.exports = { getAccount, getPrice, getDepth, placeLimitOrder, cancelOrder };
4. 실행 예제
index.js:
const { getAccount, getPrice, placeLimitOrder } = require('./src/account');
(async () => {
try {
const account = await getAccount();
const nonZero = account.balances.filter(b => parseFloat(b.free) > 0);
console.log('0이 아닌 잔고:');
nonZero.forEach(b => console.log(` ${b.asset}: ${b.free}`));
const btc = await getPrice('BTCUSDT');
console.log(`BTC 최신가: $${btc}`);
// 한도 주문 (체결 방지를 위해 현재가 대비 -10% 설정)
const testPrice = (btc * 0.9).toFixed(2);
const order = await placeLimitOrder('BTCUSDT', 'BUY', '0.001', testPrice);
console.log(`주문 ${order.orderId} 생성됨`);
} catch (e) {
console.error('실패:', e.message);
}
})();
실행:
node index.js
3. node-binance-api 빠른 솔루션
1. 설치
npm install node-binance-api
2. 사용 예제
const Binance = require('node-binance-api');
const binance = new Binance().options({
APIKEY: process.env.BINANCE_API_KEY,
APISECRET: process.env.BINANCE_SECRET_KEY,
useServerTime: true, // 서버 시간 자동 동기화
recvWindow: 10000,
test: false, // true 설정 시 테스트넷 사용
});
// 잔고 확인
binance.balance((error, balances) => {
if (error) return console.error(error);
Object.keys(balances).forEach(asset => {
const available = parseFloat(balances[asset].available);
if (available > 0) {
console.log(`${asset}: ${available}`);
}
});
});
// 시장가 매수
binance.marketBuy('BTCUSDT', 0.001, (error, response) => {
if (error) return console.error(error.body);
console.log('시장가 매수 성공:', response);
});
// 지정가 주문
binance.buy('BTCUSDT', 0.001, 60000, { type: 'LIMIT' }, (error, response) => {
if (error) return console.error(error.body);
console.log('지정가 주문 완료:', response.orderId);
});
// Promise 스타일
(async () => {
const ticker = await binance.prices('BTCUSDT');
console.log('현재가:', ticker.BTCUSDT);
const candles = await binance.candlesticks('BTCUSDT', '1h', false, { limit: 100 });
console.log('최근 K라인 수:', candles.length);
})();
4. binance-api-node (Promise / TypeScript)
1. 설치
npm install binance-api-node
npm install -D typescript @types/node
2. TypeScript 예제
import Binance, { Binance as BinanceClient, OrderType, OrderSide } from 'binance-api-node';
const client: BinanceClient = Binance({
apiKey: process.env.BINANCE_API_KEY!,
apiSecret: process.env.BINANCE_SECRET_KEY!,
});
async function main() {
// 자동 타입 추론 지원
const time = await client.time();
console.log('서버 시간:', new Date(time));
const account = await client.accountInfo();
const nonZero = account.balances.filter(b => parseFloat(b.free) > 0);
console.log(`${nonZero.length}종의 자산 보유 중`);
const depth = await client.book({ symbol: 'BTCUSDT', limit: 20 });
console.log(`매수1: ${depth.bids[0].price}, 매도1: ${depth.asks[0].price}`);
// 타입 체크가 포함된 주문
const order = await client.order({
symbol: 'BTCUSDT',
side: OrderSide.BUY,
type: OrderType.LIMIT,
timeInForce: 'GTC',
quantity: '0.001',
price: '60000.00',
});
console.log('주문 ID:', order.orderId);
}
main().catch(console.error);
5. WebSocket 실시간 구독 (ws 라이브러리)
const WebSocket = require('ws');
function subscribeTickers(symbols) {
const streams = symbols.map(s => `${s.toLowerCase()}@ticker`).join('/');
const url = `wss://stream.binance.com:9443/stream?streams=${streams}`;
function connect() {
const ws = new WebSocket(url);
ws.on('open', () => console.log(`${symbols.length}개 거래쌍 구독 시작`));
ws.on('message', (raw) => {
const { stream, data } = JSON.parse(raw);
console.log(`${data.s}: 최신가 ${data.c}, 24h 변동률 ${data.P}%`);
});
ws.on('pong', () => console.log('pong 수신'));
ws.on('close', () => {
console.log('연결 끊김, 5초 후 재연결');
setTimeout(connect, 5000);
});
ws.on('error', (err) => console.error('WS 오류:', err.message));
// 3분마다 ping을 보내 연결 유지
setInterval(() => {
if (ws.readyState === WebSocket.OPEN) ws.ping();
}, 180000);
}
connect();
}
subscribeTickers(['BTCUSDT', 'ETHUSDT', 'BNBUSDT', 'SOLUSDT']);
6. 선물 API (Futures) Node.js 예제
const crypto = require('crypto');
const axios = require('axios');
const FAPI = 'https://fapi.binance.com';
function signFutures(params, secret) {
const query = new URLSearchParams(params).toString();
return crypto.createHmac('sha256', secret).update(query).digest('hex');
}
async function futuresPlaceOrder(apiKey, secret, symbol, side, quantity, price) {
const params = {
symbol,
side,
type: 'LIMIT',
timeInForce: 'GTC',
quantity: quantity.toString(),
price: price.toString(),
timestamp: Date.now(),
};
params.signature = signFutures(params, secret);
const r = await axios.post(
`${FAPI}/fapi/v1/order`,
new URLSearchParams(params).toString(),
{
headers: {
'X-MBX-APIKEY': apiKey,
'Content-Type': 'application/x-www-form-urlencoded',
},
}
);
return r.data;
}
async function futuresSetLeverage(apiKey, secret, symbol, leverage) {
const params = { symbol, leverage, timestamp: Date.now() };
params.signature = signFutures(params, secret);
const r = await axios.post(
`${FAPI}/fapi/v1/leverage`,
new URLSearchParams(params).toString(),
{ headers: { 'X-MBX-APIKEY': apiKey } }
);
return r.data;
}
7. 자주 묻는 질문 (FAQ)
Q1: Node.js의 Date.now() 정밀도가 충분한가요?
A: 네, 충분합니다. Node.js의 Date.now()는 밀리초 정밀도의 타임스탬프(13자리)를 반환하며, 바이낸스 API가 요구하는 밀리초 수준을 완전히 충족합니다. 다만 컨테이너 환경에서는 호스트 시계가 정확해야 합니다(chronyd 또는 systemd-timesyncd 권장).
Q2: axios와 node-fetch 중 어느 것이 바이낸스 API에 더 적합한가요?
A: axios의 기본 동작이 REST에 더 적합합니다(자동 JSON 파싱, 오류 재시도, 커넥션 풀 지원). fetch(Node 18+ 네이티브 지원)는 더 가볍지만 오류 처리를 수동으로 해야 합니다. 고빈도 시나리오에서는 axios보다 성능이 30%가량 높은 undici 또는 네이티브 http.Agent를 권장합니다.
Q3: TypeScript에서 as any를 피하려면 어떻게 하나요?
A: 전체 타입 정의가 포함된 binance-api-node를 사용하거나, 공식 OpenAPI 사양을 기반으로 openapi-typescript를 사용하여 직접 타입을 생성하세요. request<any>와 같은 범용 캡슐화는 피하는 것이 좋습니다.
Q4: pm2 프로세스 관리자 사용 시 주의사항은?
A: WebSocket 연결이 끊겨도 Node 이벤트 루프가 종료되지 않으므로 pm2가 자동으로 재시작되지 않습니다. 코드 내부에서 재연결 로직을 구현해야 하며(5절 참조), 메모리 누수 방지를 위해 pm2 설정에서 max_memory_restart: '500M'를 추가하는 것이 좋습니다.
Q5: Node.js에서 요청 속도를 제한(Rate Limit)하려면?
A: bottleneck 라이브러리를 사용하세요:
const Bottleneck = require('bottleneck');
const limiter = new Bottleneck({
minTime: 10, // 최소 10ms 간격
maxConcurrent: 20, // 최대 20개 동시 요청
});
const wrappedGet = limiter.wrap(axios.get);
Node.js 솔루션을 확인하셨다면, 카테고리로 돌아가 「API 연동」 분류에서 다른 언어의 SDK 튜토리얼을 확인해 보세요.