Upcoin 암호화폐 모의투자 거래소를 개발하며 배운 것들을 이 글을 작성해본다.
Upcoin 모의투자 거래소의 아키텍처는 모듈형 서비스 지향 아키텍처로 설계하였다. 솔직히 말해서 완전한 마이크로서비스 아키텍처는 아니지만, 실시간 시세 처리 서버, 매칭 엔진, PHP 애플리케이션 서버와 같은 개별 구성 요소로 기능을 분리하여 설계하였다. 아래 사진은 전체 시스템 아케텍처이다.
Data Source:
System Components & Data Flow:
데이터 수집: Upbit, 토스증권, 한국투자증권, Yahoo! Finance 등에서 데이터 수집
실시간 데이터 처리 (Node.js): 실시간 데이터 처리를 하며, Node.js로 구축하였다. 다음작업을 수행한다:
crypto_data
테이블에 저장한다.체결엔진
이 Redis
에 등록되어있는 지정가 주문을 처리한다.체결 엔진 (Node.js & Redis): 지정가 주문 처리
Database (DB - MariaDB)
crypto_data
: Upbit에서 수신받는 실시간 시세 데이터 저장.transactions
: 거래 기록 저장.wallets
: 사용자 코인, 원화 계좌.User
: 사용자 정보.Candle Data
: 1분 캔들 데이터.Fin_info
: 토스증권, 한국투자증권, Yahoo! Finance 등에서 수집한 데이터.1. Upbit로부터 연결된 웹소켓 연결 관리 하기
const WebSocket = require("ws");
// ...
const upbitWebSocket = createWebSocketWithReconnect(
"wss://api.upbit.com/websocket/v1",
null,
async (data) => { /* 시세데이터 처리 */ },
tradeSubscribeMessage,
"업비트 시세 데이터 웹소켓"
);
createWebSocketWithReconnect
함수를 사용하여 Upbit 웹소켓과 서버가 안 끊어지게 한다. 이 함수는 업비트와 연결이 끊어질 경우 자동으로 다시 연결은 시도한다. 연결 재시도 로직은 지수 백오프(Exponential Backoff)
를 사용하였다.
2. 데이터 처리 and 시세 업데이트 (updateCryptoDataInDB
)
async function updateCryptoDataInDB(data) {
const connection = await mysql.createConnection(dbConfig);
const insertQuery = `
INSERT INTO crypto_data
VALUES (?, ?, ?, /* ... 시세 데이터 ... */)
ON DUPLICATE KEY UPDATE
`;
try {
await connection.query(insertQuery, [/* ... 데이터 값 ... */]);
} catch (error) {
console.error("Error updating crypto_data:", error);
} finally {
connection.end();
}
}
updateCryptoDataInDB
함수는 웹소켓 처리 부분 내에서 실행된다. 이 함수는 업비트에서 송신하는 바이너리 데이터를 json으로 받아서 파싱하여 crypto_data 테이블에 있는 시세 데이터를 수정한다.
3. 체결 엔진
체결 엔진은 현재 시세를 받아서 매수일 경우 현재 시세 보다 낮은 매수 주문을 가지고 오고 매도일 경우 현재 시세보다 높은 주문을 Redis에서 가지고 온다.
주문 처리 (processOrders
, processBuyOrders
, processSellOrders
):
const orderMatchingModule = {
processOrders: async (parsedData, dbConfig) => { /* ... 매도 매수 처리 ... */ },
processBuyOrders: async (symbol, currentPrice, dbConfig, redisClient) => { /* ... 매수 주문 가져오기 ... */ },
processSellOrders: async (symbol, currentPrice, dbConfig, redisClient) => { /* ... 매도 주문 가져오기 ... */ },
executeBuyOrder: async (order, currentPrice, dbConfig, redisClient, buyOrdersKey, originalOrderJson) => { /* ... 매수 주문 처리 ... */ },
executeSellOrder: async (order, currentPrice, dbConfig, redisClient, sellOrdersKey, originalOrderJson) => { /* ... 매도 주문 처리 ... */ },
};
processOrders
함수는 새로운 시세 데이터를 수신 받을 때마다 processBuyOrders
and processSellOrders
함수를 호출한다.processBuyOrders
& processSellOrders
: 이 함수는 현재 가격을 기준으로 Redis에 있는 지정가 주문을 검색한다. 매수 주문의 경우 현재 가격보다 낮거나 같은 가격의 주문을 가져온다. 매도 주문의 경우 현재 가격보다 크거나 같은 가격의 주문을 검색한다. 만약 처리해야할 주문이 1000건이 넘으면 서버 과부하라고 판단하여 나의 휴대폰으로 알림이 가게 하였다.executeBuyOrder
& executeSellOrder
: 이 2개의 함수는 원자성과 동시성이 중요하다4. 1-Minute 캔들 데이터 가공 & 수집 (startCandleDataCollection
, saveMinuteCandle
):
async function saveMinuteCandle(coin, candleData, timestamp) { /* ... 데이터 DB 삽입 ... */ }
async function startCandleDataCollection() { /* ... 데이터 수집 ... */ }
startCandleDataCollection();
minuteData
: 1분 전까지의 시가, 고가, 저가, 종가, 거래량을 저장하는 객체saveMinuteCandle
: minuteData
객체에 저장된 데이터를 DB에 삽입하는 함수startCandleDataCollection
: 캔들 수집 시작
위와같이 반응형 웹 디자인으로 디자인하였다. 메인 화면에 국내 외경제와 가상자산 시장의 흐름을 알 수 있는 다양한 지수를 한눈에 볼 수 있도록 하였고 상단 헤더부분에는 한국증권거래소(KRX) 로비의 전광판처럼 현재 종목들의 시세가 나오며 움직이는 것으로 디자인하였다.
좌측에는 코인 목록 우측에는 뉴스를 배치하였다. 트레이딩에서 뉴스의 영향력은 크기 때문에 빠르게 접근할 수 있도록 거래소 우측에 배치하였다.
내 자산은 국내 가상자산 거래소 중 하나인 빗썸(Bithumb)의 내 자산 UI/UX을 참고하여 디자인 하였다.
보유 중인 가상자산의 평가손익이 실시간으로 업데이트되어 표시되는 등 사용자 편의성을 고려한 디자인하였다.
시세 차트는 TradingView의 Lightweight-charts 라이브러리를 활용하여 구현했다. 실시간 데이터 수신해야하기 때문에 웹소켓을 이용해 체결 내역과 호가 정보를 실시간으로 차트에 반영하고 화면에 표시했다. 사용자가 과거 시세를 조회할 경우, 이벤트 리스너를 통해 과거 캔들 데이터 API를 자동으로 호출하여 히스토리 캔들 데이터를 로드하는 방식으로 구현하였다.
이번 프로젝트를 개발을 하며 실시간 데이터를 빠르고 안정적으로 다루는 Redis와 Kafka라는 신기술을 배우고 사용하게 되었는데 새로운 땅을 개척하는 느낌이였다.
이제 체결 엔진을 개발하였으니, 공매도 기능을 만들어봐야겠다.
Upcoin - 암호화폐 모의투자 거래소
https://www.upcoin.kr/
안녕하세요! 글 재밌게 읽었어요. 저도 고등학생 개발자라 더 흥미롭게 봤네요. 많이 배웠습니다!
Redis로 실시간 주문 처리를 최적화한 거 정말 좋아요. 근데, 앞으로 트래픽이랑 주문량이 더 늘어나면 Node.js의 단일 스레드 구조가 좀 걱정되긴 해요. 지금은 Redis 같은 인메모리 DB로 성능을 끌어올리고 있지만, 나중에 더 큰 규모로 키우려면 멀티스레드 언어도 한번 고려해봐도 좋을 것 같아요.
하지만, 지금 정도 규모의 모의 투자 시스템이면 Node.js + Redis + Kafka 조합으로도 충분할 것 같네요!
항상 좋은 글 올려주셔서 감사해요. 다음 글도 기대할게요:)