본문 바로가기
업비트 트레이더

4편: 실전 배포 — 24시간 무중단 서버 구축과 실시간 모니터링 시스템

by WELab74 2026. 3. 22.
반응형

4편: 실전 배포 — Oracle Cloud 서버 & 웹 어드민 구축

3편에서 전략을 확정했다. 이제 실제로 돌려야 한다.
문제는 내 PC를 24시간 켜둘 수 없다는 것이었다.


왜 서버가 필요한가

자동매매 봇의 가장 큰 함정 중 하나가 "실행 중단" 이다.

매수 신호가 새벽 3시에 발생했는데 PC가 꺼져 있으면 그냥 놓치는 것이다. 더 심각한 건 매도 신호다. 손절 타이밍을 놓치면 손실이 커진다. 자동매매는 24시간 무중단이 기본 전제다.

선택지는 3가지였다.

옵션 비용 안정성 선택
내 PC 상시 가동 전기료 낮음 (재부팅, 정전) 탈락
AWS/GCP 유료 서버 월 3~5만원 높음 비용 부담
Oracle Cloud Free Tier 무료 높음 ✅ 채택

Oracle Cloud의 Always Free 티어는 ARM 기반 VM을 무료로 제공한다. 24GB RAM, 4코어까지 무료다. 파이썬 봇 하나 돌리기엔 넘치는 사양이다.


Oracle Cloud 서버 세팅

인스턴스 생성

Oracle Cloud에서 VM.Standard.A1.Flex (ARM) 인스턴스를 생성했다. OS는 Ubuntu 22.04.

주요 설정:

  • CPU: 2코어
  • RAM: 12GB
  • 스토리지: 50GB
  • 리전: 한국 춘천  *한번 정하면 못고친단다. 무료니 뭐..

방화벽 규칙

웹 어드민 접속을 위해 포트를 열어야 했다.

# Ubuntu UFW 설정
sudo ufw allow 22      # SSH
sudo ufw allow 5000    # 웹 어드민
sudo ufw enable

Oracle Cloud는 UFW 외에 보안 목록(Security List) 도 별도로 설정해야 한다. 이걸 몰라서 30분을 낭비했다. 콘솔 → VCN → 서브넷 → 보안 목록에서 인그레스 규칙을 추가해야 한다.

Python 환경 구성

sudo apt update && sudo apt install -y python3-pip python3-venv
cd ~
git clone https://github.com/...  # 또는 scp로 파일 업로드
pip install -r requirements.txt

systemd 서비스 등록

수동으로 실행하면 SSH 세션이 끊길 때 프로세스도 죽는다. systemd에 등록해야 서버 재시작 후에도 자동으로 실행된다.

# /etc/systemd/system/upbit-trader.service
[Unit]
Description=Upbit Auto Trader
After=network.target

[Service]
Type=simple
User=ubuntu
WorkingDirectory=/home/ubuntu/upbit-trader
ExecStart=/usr/bin/python3 main.py
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable upbit-trader
sudo systemctl start upbit-trader

Restart=always 덕분에 봇이 예상치 못하게 죽어도 10초 후 자동 재시작된다.

업비트 API IP 제한 설정

처음 실행했을 때 no_authorization_ip 오류가 발생했다. 업비트 API 키에 IP 제한이 걸려 있었기 때문이다. 기존에는 내 PC IP로만 제한했는데, 서버 IP로 변경해야 했다.

업비트 마이페이지 → Open API 관리 → IP 허용 목록에 Oracle Cloud 서버 IP를 추가했다.


웹 어드민 구축

서버에서 봇이 돌아가면 상태를 어떻게 확인하나? 텔레그램으로 기본 상태는 볼 수 있지만, 거래 내역, 수익 차트, 히트맵 같은 것들은 별도 대시보드가 필요했다.

Flask로 웹 어드민을 만들었다.

주요 기능

1. 로그인 페이지

  • ID/PW 인증 (.env에 저장)
  • 로그인 유지 (remember me 체크박스, 쿠키 7일)
  • 세션 기반 인증

2. 자동매매 거래내역 탭

  • SQLite DB에서 거래 기록 조회
  • 컬럼별 필터 (코인 선택, 매수/매도 구분, 사유)
  • 가격/금액 우측 정렬, 손절 행은 빨간색 표시

3. 시각화 차트 (Chart.js)

차트 내용
에쿼티 커브 누적 수익 변화 (시간순)
수익 기여도 파이차트 코인별 수익 비율 + 금액 표시
시간대별 히트맵 요일 × 시간 매트릭스로 수익 패턴 시각화

4. 내 거래내역 탭

  • 업비트 API로 실제 주문 내역 조회
  • 단, API IP 제한으로 로컬에서는 조회 불가 → 서버 배포 후 사용 가능

핵심 코드 구조

# admin.py
from flask import Flask, session, redirect, request, jsonify
from functools import wraps

app = Flask(__name__)

def login_required(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        if 'user' not in session:
            return redirect('/login')
        return f(*args, **kwargs)
    return decorated

@app.route('/')
@login_required
def index():
    db = get_session()
    trades = db.query(Trade).order_by(Trade.timestamp.desc()).all()
    ...

삽질 기록

 - 세션 변수 충돌: Flask의 session과 SQLAlchemy의 session이 같은 이름이라 변수 충돌이 발생했다. DB 세션을 db로 변경해서 해결.

 - 포트 충돌: 개발 중에 5000번 포트가 계속 점유되어 있었다. 백그라운드에 죽지 않은 프로세스가 쌓인 것이었다. 포트를 5007까지 올려가며 테스트했다.

 - Chrome 다크모드: CSS를 입력하지 않았는데 텍스트가 보이지 않는 문제가 있었다. Chrome이 자체적으로 다크모드를 적용해버린 것이었다. 전체 다크 테마를 명시적으로 지정해서 해결했다.


텔레그램 봇 기능 확장

서버 배포와 함께 텔레그램 봇 기능도 보강했다.

30분 주기 상태 알림 추가

기존엔 매수/매도 시에만 알림이 왔다. 거래가 없는 구간에선 봇이 살아있는지조차 알 수 없었다. 매 배치 실행 후 3가지 중 하나를 알린다.

[보유중] KRW-BTC
현재가: 114,000,000 KRW
수익률: +7.72%
[매매없음] KRW-BTC
신호: HOLD | RSI: 54.2
[쿨다운] KRW-BTC 거래 중단 중

버튼 2개 추가

하단 고정 키보드에 현재조건 버튼과 용어해설 버튼을 추가했다.

  • 현재조건: 현재 운용 중인 파라미터 전체를 출력 (TP, SL, trail_activate 등)
  • 용어해설: 트레일링 스탑, 쿨다운, 골든크로스 등 주요 용어 설명

나중에 설정을 변경하거나 누군가에게 설명할 때 유용하다.


현재 운용 상태

Oracle Cloud 서버에서 24시간 자동 매매가 돌아가고 있다.

# 서버에서 상태 확인
sudo systemctl status upbit-trader

# 실시간 로그 확인
journalctl -u upbit-trader -f

실제 거래가 발생하기 전까지는 매 30분마다 "[매매없음]" 알림이 꾸준히 오고 있다. 신호가 없다는 게 나쁜 게 아니다. 조건을 지키고 있다는 뜻이다.


다음 편 예고

서버는 돌아가고 있다. 그런데 한 가지 의문이 생겼다.

"트레일링 스탑이 진입 즉시 활성화되는 게 맞을까?
+1% 올랐다가 -3% 빠지면 손절도 아닌데 트레일로 청산되는 거 아닌가?"

이 질문이 다음 편의 주제다.

반응형