자동매매 텔레그램 알림 봇 만들기 — 체결·에러·헬스체크는 왜 나눠서 보내야 할까
화면을 계속 들여다볼 수 없다는 전제에서 시작한다
무인 자동매매를 돌리다 보면 결국 "이 상황을 내가 어떻게 알 수 있나"라는 질문으로 돌아온다. 24시간 무인 운영 글에서 다룬 것처럼, 무인 운영은 손이 덜 가는 상태가 아니라 사람이 즉시 알아차리지 못한다는 전제를 깔고 대비해야 하는 상태다. 그 대비의 가장 기본적인 도구가 메신저로 오는 알림 봇이다.
이 글은 특정 서비스의 실제 토큰 값이나 인증 정보를 다루지 않는다. 텔레그램 봇을 이용해 자동매매 시스템에서 알림을 보내는 흐름을 개념 수준으로 정리하고, 알림을 설계할 때 무엇을 언제 보낼지, 그리고 이 과정에서 흔히 저지르는 실수를 다룬다.
왜 무인 자동매매에 알림이 필수인가
사람이 화면 앞에 계속 앉아 있다면 알림은 편의 기능에 가깝다. 하지만 무인으로 돌아가는 시스템이라면 이야기가 다르다. 시스템이 멈췄는지, 주문이 실패했는지, 리스크 한도에 걸렸는지를 사람이 스스로 알아낼 방법이 없다면, 그 문제는 다음에 화면을 열어볼 때까지 방치된다.
알림 봇은 이 공백을 메우는 장치다. 시스템이 능동적으로 사람에게 신호를 보내야, 사람이 수동적으로 시스템을 확인하는 구조에서 벗어날 수 있다. 그리고 이 신호를 어디로 보낼지는 여러 선택지가 있지만, 개인이 서버를 따로 두지 않고도 스마트폰으로 즉시 받아볼 수 있다는 점에서 텔레그램 봇이 진입장벽이 낮은 선택지 중 하나다.
텔레그램 봇 생성과 토큰·chat_id 개념
텔레그램에서 봇을 만들면 그 봇을 식별하고 인증하는 데 쓰는 고유한 문자열을 하나 발급받는다. 이 값을 흔히 봇 토큰이라 부르고, 이후 메시지를 보내는 모든 API 호출은 이 토큰으로 "이 요청이 어느 봇의 요청인지"를 증명하는 방식으로 이뤄진다. 이 글에서는 이 값을 <BOT_TOKEN>으로만 표기한다.
메시지를 어디로 보낼지도 별도로 알아야 한다. 봇과 대화방(개인 채팅이든 그룹이든)을 식별하는 고유 번호가 있는데, 이를 흔히 chat_id라 부른다. 봇 토큰이 "누가 보내는가"를 증명한다면, chat_id는 "어디로 보내는가"를 정한다. 이 값도 이 글에서는 <CHAT_ID>로만 표기한다.
두 값 모두 코드에 문자열로 직접 박아 넣지 않는다. 시크릿·설정값 안전하게 관리하기에서 다룬 원칙이 여기에도 그대로 적용된다. 환경변수나 별도 설정 파일로 분리하고, 그 파일은 버전관리에서 제외한다.
파이썬에서 메시지를 보내는 흐름
메시지를 보내는 흐름 자체는 단순하다. 토큰과 chat_id, 보낼 텍스트를 묶어서 텔레그램이 제공하는 메시지 전송 API를 호출하고, 그 응답이 성공했는지 확인하면 된다. 아래는 이 흐름을 개념적으로 보여주는 의사코드다.
python 코드 보기
import os
BOT_TOKEN = os.environ.get("TELEGRAM_BOT_TOKEN") # <BOT_TOKEN>
CHAT_ID = os.environ.get("TELEGRAM_CHAT_ID") # <CHAT_ID>
def send_alert(message: str, level: str = "info") -> bool:
if not BOT_TOKEN or not CHAT_ID:
log_local_failure("알림 설정값이 비어 있음")
return False
payload = {"chat_id": CHAT_ID, "text": f"[{level}] {message}"}
try:
response = call_telegram_api(BOT_TOKEN, payload, timeout=5)
except NetworkError as e:
log_local_failure(f"알림 전송 요청 자체가 실패: {e}")
return False
if not response.ok:
log_local_failure(f"알림 전송 실패: {response.status_code}")
return False
return True여기서 중요한 건 두 가지다. 하나는 토큰·chat_id를 환경변수에서 읽어오되, 값이 비어 있는 경우를 코드에서 명시적으로 확인한다는 점이다. 다른 하나는 전송 자체가 실패했을 때(네트워크 오류, API 응답 실패)를 조용히 넘기지 않고 어딘가에 그 사실을 남긴다는 점이다. 이 부분은 아래 "자주 하는 실수"에서 다시 다룬다.
무엇을 언제 알릴까 — 체결·에러·헬스체크를 심각도별로 나눈다
봇을 만들고 나면 다음 질문은 "무엇을 알릴 것인가"다. 모든 로그 라인을 알림으로 보내면 무인 운영 글에서 이미 다룬 것처럼 알림이 너무 잦아져서 정작 중요한 신호를 무시하게 되는 역효과가 생긴다. 증권사 API 에러코드 핸들링에서 에러를 일시적·설정·치명적 세 갈래로 분류했던 것과 같은 방식으로, 알림도 상황의 심각도에 따라 나눠서 설계하는 편이 낫다.
- 체결 알림: 주문이 실제로 체결됐을 때. 급하게 대응할 일은 아니지만, 시스템이 의도한 대로 동작하고 있다는 근거가 되므로 남긴다.
- 에러 알림: 설정 오류나 치명적 오류처럼 사람의 확인이 필요한 상황. 일시적 오류(재시도로 해결되는 것)까지 매번 알리면 알림이 금방 소음이 된다.
- 헬스체크 알림: 시스템이 아직 정상적으로 살아 있다는 것을 주기적으로 알리는 신호. 역설적으로 "알림이 오지 않는 것" 자체가 이상 신호가 될 수 있다는 점에서, 체결·에러 알림과는 다른 목적을 가진다.
이 세 종류를 같은 채널로 뭉뚱그려 보내기보다, 메시지 앞에 태그를 붙이거나 필요하면 채널 자체를 분리해서 나중에 로그를 검토할 때도 구분이 되도록 해두는 편이 낫다.
자주 하는 실수
- 같은 에러를 반복해서 그대로 알린다. 재시도 루프에서 매번 같은 에러가 발생할 때마다 알림을 보내면, 몇 분 사이에 알림이 수십 건씩 쌓인다. 알림이 폭주하면 사람은 결국 알림 자체를 무시하거나 음소거하게 되고, 그 순간 알림 시스템은 있으나 마나 한 것이 된다. 같은 종류의 에러는 일정 시간 동안 한 번만 보내거나, 횟수를 모아서 요약으로 보내는 방식이 필요하다.
- 토큰과 chat_id를 코드에 하드코딩해서 노출한다. 편의를 위해 값을 문자열로 코드에 직접 적어 넣고 그대로 커밋하는 경우가 있다. 이렇게 노출된 토큰은 제3자가 그 봇을 이용해 임의로 메시지를 보내거나, 최악의 경우 봇의 다른 권한을 악용할 수 있는 통로가 된다. 반드시 환경변수나 별도 설정 파일로 분리하고 버전관리에서 제외해야 한다.
- 알림 전송 자체의 실패를 감지하지 못한다. 알림 함수 안에서 네트워크 오류나 API 응답 실패가 나도 그 실패를 그냥 삼켜버리면, 정작 시스템에 진짜 문제가 생겼을 때 "알림을 보내려 했지만 실패해서 아무도 몰랐다"는 상황이 벌어진다. 알림 전송이 실패하면 최소한 로컬 로그에는 남기거나, 대체 경로(다른 채널, 파일 기록)로라도 실패 사실을 남겨야 한다.
- 알림 문구만 보고는 무슨 상황인지 알 수 없게 만든다. "에러 발생"처럼 정보가 없는 메시지는 결국 사람이 로그를 다시 뒤지게 만든다. 어떤 종류의 상황인지, 어떤 대응이 필요한지 정도는 알림 메시지 자체에 담아야 알림의 가치가 생긴다.
오늘의 정리
- 무인 자동매매에서 알림 봇은 사람이 시스템을 능동적으로 확인하지 않아도 되게 만드는 최소한의 장치다.
- 텔레그램 봇 토큰은 "누가 보내는가", chat_id는 "어디로 보내는가"를 결정하며, 둘 다 코드가 아니라 환경변수 등으로 분리해서 관리해야 한다.
- 메시지 전송 흐름은 토큰·chat_id·텍스트를 묶어 API를 호출하고 응답 성공 여부를 확인하는 단순한 구조지만, 값이 비어 있거나 전송이 실패하는 경우를 반드시 코드에서 다뤄야 한다.
- 체결·에러·헬스체크는 목적이 다른 신호이므로 심각도에 따라 나눠서 설계해야 알림이 소음이 되지 않는다.
- 알림 폭주, 토큰 하드코딩, 알림 전송 실패를 감지하지 못하는 것은 알림 봇을 만들 때 가장 흔히 저지르는 실수다.
이 글은 알림 봇 연동의 일반적인 설계 개념을 정리한 것이며, 실제 토큰·키 값은 코드에 직접 넣지 말고 환경변수 등으로 분리해 관리해야 합니다. 투자 조언이 아니고, 수익 보장은 없으며, 투자 손실의 책임은 투자자 본인에게 있습니다.