
터미널의 검은 화면을 넘어, 나만의 종합 상황실로
안녕하세요! [계좌 리팩토링] 시리즈 11편입니다. 우리는 지금까지 Node.js를 이용해 백엔드에서 묵묵히 돌아가는 강력한 트레이딩 봇을 완성했습니다. 10편의 일목균형표 알고리즘이 매일 시장을 분석하고, 9편의 텔레그램 연동을 통해 결과도 스마트폰으로 잘 받아보고 계실 겁니다.
하지만 진정한 시스템 트레이더의 로망은 무엇일까요? 바로 영화에 나오는 펀드매니저들처럼 내 봇의 실시간 수익률, 현재 포지션, 그리고 매매 로그가 차례대로 올라오는 멋진 '웹 대시보드(Dashboard)'를 띄워두는 것 아닐까요?
이번 시간에는 모던 웹 프레임워크인 Nuxt(Vue.js 기반)를 활용하여, 우리 봇의 상태를 시각적으로 모니터링할 수 있는 프론트엔드 웹 페이지를 구축해 보겠습니다.
1단계: 아키텍처 설계 - 봇과 웹은 어떻게 통신할까?
현재 우리의 봇은 단순히 스크립트로 동작하고 있습니다. 웹 브라우저(Nuxt)가 봇의 데이터를 가져가려면, 봇 자체에 작은 웹 서버 기능(API)을 달아주어야 합니다.
가장 가벼운 express 프레임워크를 봇 코드에 추가하여, 프론트엔드에서 요청이 오면 현재 계좌 잔고나 매매 로그를 JSON 형태로 반환하도록 만들어 주겠습니다. 또한, 실시간 데이터(호가, 체결 로그)는 SSE(Server-Sent Events) 방식을 사용하여 브라우저로 끊임없이 밀어주도록(Push) 구성할 것입니다.
2단계: 백엔드(봇)에 Express와 SSE 세팅하기
기존 봇 프로젝트 터미널을 열고 express와 cors를 설치합니다.
pnpm add express cors
// (기존 app.js 에 추가하는 코드 예시)
const express = require('express');
const cors = require('cors');
const app = express();
app.use(cors());
// 1. 현재 잔고를 반환하는 단순 API (REST)
app.get('/api/balance', async (req, res) => {
// 4편에서 만든 잔고 조회 함수 호출 결과를 반환한다고 가정
const balanceData = { total_amt: 15000000, profit_rate: 3.5 };
res.json(balanceData);
});
// 2. 실시간 매매 로그를 보내주는 SSE (Server-Sent Events) 엔드포인트
app.get('/api/stream-logs', (req, res) => {
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
// 봇 로직 어딘가에서 이벤트가 발생하면 res.write()로 데이터를 쏴줍니다.
const sendLog = (message) => {
res.write(`data: ${JSON.stringify({ time: new Date(), msg: message })}\n\n`);
};
// 예시: 5초마다 더미 로그 전송
const intervalId = setInterval(() => sendLog('봇 정상 모니터링 중...'), 5000);
req.on('close', () => clearInterval(intervalId));
});
app.listen(3001, () => console.log('📊 대시보드 API 서버가 3001번 포트에서 실행 중입니다.'));
3단계: Nuxt 프로젝트 생성 및 UI 구성
이제 새로운 터미널을 열고 봇 프로젝트 폴더 바깥에 프론트엔드 프로젝트를 생성합니다. (사내망이나 제한된 네트워크 환경이라면 사설 레지스트리를 바라보는 .npmrc 설정이 미리 되어있는지 확인하세요.)
# Nuxt 프로젝트 초기화 (pnpm 권장)
pnpm dlx nuxi@latest init bot-dashboard
cd bot-dashboard
pnpm install
생성된 프로젝트의 app.vue (또는 pages/index.vue) 파일을 열고 아래와 같이 코드를 작성합니다.
<template>
<div class="dashboard-container">
<h1>📈 내 계좌 리팩토링 종합 상황실</h1>
<div class="status-card">
<h2>💰 현재 계좌 상태</h2>
<p v-if="pending">데이터를 불러오는 중...</p>
<div v-else>
<p>총 자산: {{ balance?.total_amt.toLocaleString() }} 원</p>
<p>수익률: <span :class="{'profit': balance?.profit_rate > 0}">{{ balance?.profit_rate }}%</span></p>
</div>
</div>
<div class="log-card">
<h2>⚡ 실시간 매매 로그 (SSE)</h2>
<ul>
<li v-for="(log, index) in botLogs" :key="index">
[{{ new Date(log.time).toLocaleTimeString() }}] {{ log.msg }}
</li>
</ul>
</div>
</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
// 1. Nuxt의 강력한 Data Fetching (REST API 호출)
const { data: balance, pending } = useFetch('http://localhost:3001/api/balance');
// 2. 실시간 로그를 받기 위한 SSE 상태 관리
const botLogs = ref([]);
let eventSource = null;
onMounted(() => {
// 브라우저의 EventSource API를 사용하여 봇의 SSE 엔드포인트 구독
eventSource = new EventSource('http://localhost:3001/api/stream-logs');
eventSource.onmessage = (event) => {
const newLog = JSON.parse(event.data);
botLogs.value.unshift(newLog); // 최신 로그를 배열 맨 앞에 추가
// 로그가 너무 길어지지 않게 50개만 유지
if (botLogs.value.length > 50) botLogs.value.pop();
};
});
onUnmounted(() => {
if (eventSource) eventSource.close();
});
</script>
<style scoped>
/* 심플한 CSS 스타일링 (다크 모드 컨셉 추천!) */
.dashboard-container { padding: 20px; font-family: sans-serif; }
.status-card, .log-card { border: 1px solid #ccc; padding: 15px; margin-bottom: 20px; border-radius: 8px; }
.profit { color: red; font-weight: bold; } /* 한국은 상승이 빨간색! */
</style>

풀스택 트레이딩 시스템의 완성
이제 pnpm dev를 입력하여 Nuxt 서버를 띄우고 브라우저로 접속해 보세요. 백엔드의 봇이 보내주는 데이터와 실시간 로그(SSE)가 Vue.js의 반응성(Reactivity)을 타고 화면에 즉각적으로 업데이트되는 것을 볼 수 있습니다.
이 대시보드를 확장하면 나중에는 브라우저에서 직접 버튼을 눌러 봇을 켜고 끄거나(Control), 새로운 매매 전략의 파라미터를 수정하는 등 완벽한 프론트엔드-백엔드 연동 시스템을 구축할 수 있습니다. 차트 라이브러리(예: Chart.js 또는 ApexCharts)를 연동해 일목균형표를 직접 그려보는 것도 아주 훌륭한 리팩토링 목표가 될 것입니다.
터미널의 흑백 텍스트를 넘어 나만의 시각적인 무기를 가지게 된 것을 축하합니다! 다음 단계는 무엇이 될까요? 여러분의 상상력이 곧 다음 전략이 됩니다.