🤖 [STEP 6] 시스템 자동화 및 AI/크립토 자동화 시스템

14. 텔레그램 봇 연동: 내 주머니 속의 실시간 시스템 관제실 구축

Bobaero Booktech-Lab 2026. 7. 1. 12:05

안녕하세요! 보배로의 북테크랩입니다.

지난 시간, 우리는 네트워크 단선이나 도커 컨테이너 크래시 상황에서도 시스템이 기하급수적 백오프(Exponential Backoff)와 헬스체크를 통해 스스로 상처를 치료하는 [Self-Healing(자가 치유)] 인프라를 완성했습니다. 이제 우리의 매매 봇은 그 어떤 충격이 와도 스스로 일어나는 영원한 생명력을 얻게 되었습니다.

시스템이 완벽하게 자가 치유된다 하더라도, 지금 이 순간 봇이 어떤 포지션을 잡았는지, 오늘 수익은 정확히 얼마인지, 방금 어떤 장애를 극복했는지 스마트폰으로 실시간 확인할 수 없다면 답답할 수밖에 없습니다. 그렇다고 매번 무거운 노트북을 펼쳐 AWS 콘솔에 접속하고 SSH 터미널을 켜는 것은 지극히 비효율적입니다.

오늘은 별도의 앱을 개발하는 리소스 낭비 없이, 글로벌 메신저인 텔레그램(Telegram) API를 활용하여 언제 어디서나 주머니 속 스마트폰 하나로 봇의 상태를 보고받고, 원격으로 매매를 중단하거나 강제 청산 명령을 내릴 수 있는 실시간 무선 관제실을 구축해 보겠습니다.

1. 텔레그램 관제실의 이중 구조 : "단방향 알림을 넘어 양방향 통제로"

많은 초보 트레이더들이 텔레그램을 단순히 에러 메시지나 체결 결과를 던져주는 '일방향 알림창(Notification)'으로만 사용합니다. 하지만 우리의 Bobaero 시스템 2.0 아키텍처에서는 이를 넘어 봇을 완벽하게 지배하는 '양방향 원격 관제소(Command & Control)'로 격상시킵니다.

  • 실시간 푸시 알림 (Inbound): 익스포저 위험 신호, 카나리 배포 성공 여부, 자가 치유 발동 및 포지션 체결 정보를 0.1초 만에 스마트폰 팝업으로 전송합니다.
  • 원격 명령 하사 (Outbound): 텔레그램 채팅창에 /status를 치면 현재 가용 잔고와 CPU 상태를 즉시 응답하고, 시장 급변 시 /panic_sell을 전송하면 서버에 접속하지 않고도 봇의 모든 포지션을 즉시 시장가로 던지고 자산을 동결(Fail-Safe)할 수 있습니다.

2. [실습] 양방향 텔레그램 관제 봇 엔진 구현

파이썬 비동기 라이브러리인 python-telegram-bot을 활용하여, 실시간 알림과 원격 명령 체계를 동시에 수행하는 관제 모듈을 구현해 보겠습니다.

1단계: 텔레그램 봇 라이브러리 설치

가상 환경(venv)이 활성화된 상태에서 아래 명령어를 실행합니다.

Bash
 
pip install python-telegram-bot

2단계: 양방향 관제 엔진 코드 (telegram_controller.py) 작성

Python
 
import os
import logging
from telegram import Update
from telegram.ext import Application, CommandHandler, ContextTypes

# 💡 보안 경고: 텔레그램 토큰과 관리자 ID는 9강에서 구축한 
# HashiCorp Vault 또는 철저히 격리된 환경 변수를 통해서만 로드해야 합니다.
TELEGRAM_TOKEN = os.getenv("TELEGRAM_TOKEN", "YOUR_BOT_TOKEN")
ALLOWED_USER_ID = int(os.getenv("TELEGRAM_ADMIN_ID", "123456789")) # 오직 나만 명령 가능하도록 제한

def auth_filter(func):
    """타인이 내 매매 봇에 명령을 내리지 못하도록 차단하는 보안 데코레이터"""
    async def wrapper(update: Update, context: ContextTypes.DEFAULT_TYPE, *args, **kwargs):
        if update.effective_user.id != ALLOWED_USER_ID:
            logging.warning(f"[Security Violation] 인가되지 않은 접근 시도: {update.effective_user.id}")
            await update.message.reply_text("⛔ 접근 권한이 없습니다. 인증 실패가 기록되었습니다.")
            return
        return await func(update, context, *args, **kwargs)
    return wrapper

@auth_filter
async def start_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
    """/start 명령 처리"""
    await update.message.reply_text(
        "🤖 Bobaero 시스템 2.0 모바일 관제실에 접속했습니다.\n"
        "사용 가능 명령어:\n"
        "/status - 봇 건강 상태 및 자산 조회\n"
        "/panic_sell - 전 포지션 즉시 시장가 청산 및 멈춤"
    )

@auth_filter
async def status_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
    """/status 명령 처리: 실시간 시스템 정보 반환"""
    # 실전에서는 11강의 메트릭 데이터나 거래소 API 잔고를 동적으로 긁어옵니다.
    simulated_balance = "12,450,000 KRW"
    simulated_cpu = "24.5%"
    
    status_msg = (
        f"📊 [실시간 시스템 관제 보고]\n"
        f"• 시스템 상태: RUNNING (ACTIVE)\n"
        f"• AWS 리전: ap-northeast-2 (Seoul)\n"
        f"• 가용 자산: {simulated_balance}\n"
        f"• CPU 사용률: {simulated_cpu}\n"
        f"• 자가치유 엔진: 정상 작동 중"
    )
    await update.message.reply_text(status_msg)

@auth_filter
async def panic_sell_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
    """/panic_sell 명령 처리: 비상 자산 동결"""
    logging.critical("[Command] 관리자로부터 패닉 셀(Emergency) 명령을 수신했습니다!")
    
    # 💡 [Fail-Safe 핵심 로직 연동]
    # 이곳에 CCXT를 호출하여 모든 미체결 주문을 취소하고 현물/선물 포지션을 
    # 시장가로 정리하는 함수를 결합합니다.
    
    await update.message.reply_text("🚨 [EMERGENCY] 모든 매매를 중단하고 전 포지션을 즉시 청산했습니다. 시스템을 동결합니다.")

def main():
    logging.basicConfig(level=logging.INFO, format='%(asctime)s %(levelname)s: %(message)s')
    
    # 텔레그램 애플리케이션 빌드
    application = Application.builder().token(TELEGRAM_TOKEN).build()

    # 핸들러 등록 (명령어와 함수 바인딩)
    application.add_handler(CommandHandler("start", start_command))
    application.add_handler(CommandHandler("status", status_command))
    application.add_handler(CommandHandler("panic_sell", panic_sell_command))

    # 비동기 폴링 시작 (봇 가동)
    logging.info("[Telegram] 모바일 관제 엔진이 활성화되었습니다.")
    application.run_polling()

if __name__ == "__main__":
    main()

📖 실제 장애 사고 사례: "비밀번호 없는 관제실과 해커의 놀이터"

과거 한 개발자가 편리하게 봇을 제어하겠다는 생각으로 텔레그램 양방향 명령 봇을 구현해 서버에 배포했습니다. 코드는 완벽히 작동했고, 밖에서도 스마트폰으로 매매를 조절할 수 있어 대단히 만족스러워했죠.

하지만 그는 결정적인 공학적 실수를 범했습니다. 텔레그램 봇은 생성 시 고유한 주소만 알면 전 세계 누구나 대화를 걸 수 있다는 사실을 망각하고, 사용자의 ID를 검증하는 보안 인증 필터(Authorization Filter)를 누락한 것입니다. 어느 날, 봇의 아이디를 우연히 검색해 찾아낸 한 악의적인 유저가 호기심에 /panic_sell 명령과 임의의 주문 명령들을 무작정 입력하기 시작했습니다.

봇은 들어오는 명령이 주인의 것인지 해커의 것인지 구분하지 못하고, 실시간으로 수천만 원 상당의 비트코인 포지션을 시장가로 계속 샀다 팔았다를 반복했습니다. 이로 인해 발생한 지독한 수수료와 슬리피지로 인해 단 몇 시간 만에 계좌 잔고의 절반이 허공으로 증발해 버렸습니다. 인증과 격리가 없는 관제실은 해커에게 무기를 쥐여주는 것과 같습니다.

🛡️ 텔레그램 시스템 관제실 운영 체크리스트

  • [ ] 관리자 ID 화이트리스트: effective_user.id를 철저히 검증하여, 등록된 내 고유 텔레그램 계정 외에는 그 어떤 명령어도 실행하지 못하도록 방어벽을 세웠는가?
  • [ ] 토큰 은닉: 텔레그램 봇 토큰(TELEGRAM_TOKEN)이 소스 코드나 GitHub에 날것으로 노출되지 않도록 Vault나 환경 변수로 완전히 격리했는가?
  • [ ] 알림 빈도 조절(Throttling): 틱 데이터 체결 시마다 알림을 보내 시끄러운 '알림 공해'가 발생하지 않도록, 치명적 에러나 주요 포지션 진입/청산 시에만 알림이 오도록 필터링했는가?
  • [ ] 비상 정지 명령어 무기화: 시장 변동성이 인프라 한계를 초과했을 때를 대비해, 단 한 번의 터치로 시스템을 완전히 잠글 수 있는 /panic_sell 로직이 완벽히 작동하는가?

📝 보배로의 통찰: "통제할 수 없는 인프라는 시스템이 아니라 도박이다"

스마트폰 화면에 실시간으로 찍히는 봇의 관제 메시지는 단순한 텍스트가 아닙니다. 그것은 우리가 설계한 수많은 아키텍처—보안, 웹소켓 시세 동기화, 자가 치유 엔진—가 거대한 금융 전선에서 완벽하게 살아 움직이고 있다는 공학적 신뢰의 증거입니다. 주머니 속 관제실을 통해 시스템 트레이딩의 시공간적 제약을 완벽히 극복하십시오. 내가 침대에 누워있든, 카페에서 커피를 마시고 있든, 데이터에 기반한 완벽한 통제력은 항상 내 손안에 있어야 합니다.

모바일 관제실을 통해 언제 어디서나 시스템을 지배하는 법을 배웠으니, 이제 아키텍처의 한계를 한 단계 더 끌어올려 클라우드 인프라 자체의 물리적 파멸에 대비할 차례입니다.

다음 시간에는 현재 봇이 돌고 있는 AWS 서울 리전의 EC2 인프라가 통째로 불타거나 마비되었을 때, 10초 만에 도쿄나 싱가포르의 예비 서버가 지휘권을 이어받아 무중단 매매를 이어가는 최고 통수권 프로토콜, [15. Disaster Recovery] EC2 장애 시 백업 서버 자동 전환(Failover) 프로토콜에 대해 알아보겠습니다.

보배로의 북테크랩(Booktech-lab) 공학적으로 투자하고, 데이터로 증명하며, 시스템으로 승리합니다.