Telegram 봇 만들기 파이썬으로 나만의 알림 비서 30분 완성

슬랙 유료 플랜 끊고 텔레그램 봇으로 갈아탄 날

우리 팀이 슬랙 무료 플랜으로 자동 알림을 쓰다가 메시지 저장 용량 한도에 걸렸다. 월 9달러짜리 유료 플랜으로 올리자니 비용 부담이 꽤 컸고, 개인 프로젝트 알림도 한곳에 몰아보려는 욕심이 생겼다. 그래서 텔레그램 봇을 직접 만들어보기로 마음먹었다.

텔레그램 봇은 기본적으로 완전 무료다. 메시지 저장 한도가 없고 개인이 쓰는 수준에서는 API 호출 제한 걱정도 거의 필요 없다. 대신 제가 써보면서 느낀 건, 메시지 포맷이 너무 제한적이라는 점이다. 원하는 알림 형식이 항상 깔끔하게 나오지 않아 아쉬웠다. 그래도 파이썬 코드 몇 줄로 주식 가격 변동, 서버 상태, 크롤링 결과 같은 알림을 바로 내 스마트폰으로 받아볼 수 있다는 점은 확실히 매력적이었다. BotFather에서 봇 만들고 첫 메시지 받기까지 걸린 시간은 딱 8분. 이 과정을 차근차근 공개한다.



1. 텔레그램 봇 API 구조와 두 가지 수신 방식

텔레그램 봇이 메시지를 받는 방법은 크게 두 가지다. 첫 번째는 Polling 방식인데, 봇이 주기적으로 텔레그램 서버에 “새 메시지 있나요?” 하고 묻는다. 로컬 환경에서 개발하거나 테스트할 때 이게 꽤 편하다. 서버 세팅 따로 안 해도 되니까 마음이 훨씬 가볍다.

두 번째는 Webhook 방식이다. 텔레그램 서버가 메시지가 도착하면 내 서버로 바로 데이터를 밀어주는 방식인데, 실시간 서비스에는 적합하다. 하지만 서버 설정도 복잡하고 SSL 인증서도 직접 관리해야 해서 개인 프로젝트에는 부담이 큰 편이었다. 저는 당분간 Polling에만 집중했다. 복잡한 서버 없이 파이썬 스크립트 한 개로 바로 시작할 수 있어서 마음에 들었다.

2. BotFather에서 봇 토큰 발급하기

텔레그램 앱에서 @BotFather를 검색하면 공식 봇 관리 계정이 나온다. 파란색 인증 체크마크가 꼭 붙어있는지 확인해야 한다. 저는 처음에 이름과 사용자명을 제대로 못 적어서 다시 만든 적 있다. 사용자명은 반드시 bot으로 끝나야 하는데, 이를 놓치면 다시 시작해야 해서 조심해야 한다.

  1. /newbot 명령어 입력
  2. 봇의 표시 이름 입력 (예: My Alert Bot)
  3. 봇 사용자명 입력 — 반드시 bot으로 끝나야 한다 (예: my_alert_123bot)
  4. BotFather가 HTTP API 토큰을 발급해준다 — 이 토큰이 봇 작동의 핵심 열쇠다

받은 토큰은 반드시 .env 파일에 저장해야 한다. 이 토큰을 누군가 알면 내 봇을 마음대로 조작할 수 있으니, 깃허브 같은 공개 저장소에 올리면 큰일 난다. 다음은 내 Chat ID를 알아내야 하는데, 봇에게 아무 메시지나 보내고 아래 URL을 브라우저에 넣어 확인한다.

https://api.telegram.org/bot여기에_토큰/getUpdates

응답 JSON에서 "chat":{"id": 123456789} 부분을 찾아 내 숫자 Chat ID를 확인할 수 있다. 이 역시 .env에 꼭 기록해 둬야 한다.

TELEGRAM_BOT_TOKEN=여기에_봇_토큰
TELEGRAM_CHAT_ID=여기에_chat_id

3. 파이썬 봇 완성 코드: 단방향 알림 + 명령어 응답

1단계: 단방향 알림 전송 (가장 간단한 활용)

내가 자동화 스크립트 결과를 내 휴대폰으로 바로 받아보고 싶다고 생각해서 만든 게 단방향 알림 시스템이다. 보통 python-telegram-bot 같은 무거운 라이브러리 대신 requests만 쓰면서 최대한 간단하게 구현했다. 그런데 단순한 만큼 메시지 포맷에 제한이 꽤 심하다. 예를 들어, 내가 원하는 맞춤형 스타일링이나 복잡한 레이아웃은 거의 불가능하다 보니, 가끔은 메시지를 보낼 때 아쉬움이 남는다.

pip install requests python-dotenv
import os
import requests
from dotenv import load_dotenv

load_dotenv()

BOT_TOKEN = os.getenv("TELEGRAM_BOT_TOKEN")
CHAT_ID = os.getenv("TELEGRAM_CHAT_ID")

def send_message(text: str) -> bool:
    """텔레그램으로 메시지를 전송하는 함수"""
    url = f"https://api.telegram.org/bot{BOT_TOKEN}/sendMessage"
    payload = {
        "chat_id": CHAT_ID,
        "text": text,
        "parse_mode": "Markdown"  # **굵게**, `코드` 형식 지원
    }
    response = requests.post(url, json=payload)
    if response.status_code == 200:
        print("메시지 전송 성공")
        return True
    else:
        print(f"전송 실패: {response.text}")
        return False

# 사용 예시 — 크롤링 봇 완료 알림
send_message("*뉴스 수집 완료* ✅n수집된 기사: 47건n소요 시간: 3.2초")

2단계: 명령어에 응답하는 양방향 봇 완성

명령어를 받아 서버 상태나 오늘 데이터 현황을 바로 알려주는 기능이 필요해서 python-telegram-bot 라이브러리를 썼다. 그런데 이 라이브러리가 자주 업데이트되면서 API가 달라져서 문서랑 코드가 자주 안 맞았다. 덕분에 버전별 차이 때문에 시간을 꽤 들여서 맞추는 작업을 했다. 게다가 모바일 환경에서 명령어 입력 메뉴가 엉켜서, 제대로 작동하는지 확인하는 데도 신경 쓸 부분이 많았다.

pip install python-telegram-bot
import os
import psutil
from datetime import datetime
from dotenv import load_dotenv
from telegram import Update
from telegram.ext import ApplicationBuilder, CommandHandler, ContextTypes

load_dotenv()

async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
    await update.message.reply_text(
        "안녕하세요! 사용 가능한 명령어:n"
        "/status — 서버 상태 확인n"
        "/report — 오늘 수집 현황"
    )

async def status(update: Update, context: ContextTypes.DEFAULT_TYPE):
    cpu = psutil.cpu_percent(interval=1)
    memory = psutil.virtual_memory().percent
    now = datetime.now().strftime("%Y-%m-%d %H:%M")
    msg = f"*서버 상태* ({now})nCPU: {cpu}%n메모리: {memory}%"
    await update.message.reply_text(msg, parse_mode="Markdown")

async def report(update: Update, context: ContextTypes.DEFAULT_TYPE):
    # 실제 프로젝트에서는 DB나 파일에서 데이터를 읽어옴
    msg = "*오늘 수집 현황*n기사: 47건n처리 완료: 47건n에러: 0건"
    await update.message.reply_text(msg, parse_mode="Markdown")

if __name__ == "__main__":
    app = ApplicationBuilder().token(os.getenv("TELEGRAM_BOT_TOKEN")).build()
    app.add_handler(CommandHandler("start", start))
    app.add_handler(CommandHandler("status", status))
    app.add_handler(CommandHandler("report", report))
    print("봇 실행 중... (중지: Ctrl+C)")
    app.run_polling()

4. 직접 맞은 에러 2가지와 해결법

에러 ① 400 Bad Request — Chat ID를 문자열로 보낸 경우

처음에 .env에서 뽑아온 Chat ID를 그대로 썼다가 400 에러가 떴다. 저는 이게 API 문제라고 생각해서 한참을 코드를 뒤졌는데, 문제는 os.getenv()가 무조건 문자열을 반환한다는 점이었다. 텔레그램 API는 chat_id를 숫자로 받아야 하는데, 문자열을 넣으니 에러가 난다.

그래서 int(os.getenv("TELEGRAM_CHAT_ID"))로 명확히 숫자 변환을 해주니 바로 해결됐다. 실제로 payload를 만들 때 "chat_id": int(CHAT_ID)로 바꾸는 게 핵심이다. 이 부분이 공식 문서 어디에도 딱 집어 설명돼 있지 않아서 꽤 시간을 잡아먹었다. 환경 변수에서 값이 문자열로 오는 걸 의심하지 않고 다른 부분만 의심했던 제 실수도 컸다.

에러 ② Conflict: terminated by other getUpdates — 봇 중복 실행

이 에러는 좀 당황스러웠다. 봇을 켰더니 Conflict: terminated by other getUpdates라는 메시지가 뜨면서 작동을 멈췄다. 원인을 보니, 전에 켜둔 봇 프로세스가 백그라운드에서 여전히 돌아가고 있었다. 텔레그램은 같은 봇 토큰으로 두 개 이상의 Polling을 동시에 띄우는 걸 허용하지 않는다.

저는 작업 관리자에서 이전 파이썬 프로세스를 찾아서 종료하고, 터미널도 완전히 닫았다가 다시 열었다. VS Code에서 터미널 탭을 여러 개 켜두는 습관 때문에 이 문제가 자주 발생했다. 이런 프로세스 관리가 얼마나 중요한지 새삼 깨달았다. 자동화한다고 해놓고 이런 기본 운영을 놓치면 결국 시간만 낭비한다는 걸 뼈저리게 느꼈다.

5. 실전 확장: 이 봇으로 자동화할 수 있는 것들

제가 만든 send_message() 함수는 단순한 구조지만, 거의 만능 알림 도구 역할을 한다. 이 함수 하나만 있으면 어떤 파이썬 스크립트에서도 바로 내 폰으로 메시지를 받아볼 수 있어, 작업 흐름이 훨씬 수월해졌다.

  • API 에러 즉시 감지: try-except 문 안에서 except 절에 send_message(f"에러 발생: {e}")를 넣으면, 에러가 나자마자 텔레그램 알림이 온다. 이 덕분에 다음 날 출근해서 에러를 발견하는 일이 거의 없어졌다.
  • 주식·코인 가격 조건 알림: 특정 종목 가격이 목표치에 도달했을 때 자동으로 알림이 오게 만들어두면, 하루 종일 차트를 들여다보지 않아도 된다. 저는 이 기능 덕분에 시간 낭비가 크게 줄었다.
  • GitHub Actions 연동: 자동화 워크플로우 마지막에 텔레그램 메시지를 넣으면, 어디서든 스크립트 실행 상태를 실시간으로 확인할 수 있다. CI/CD 파이프라인에 이걸 넣으니 관리가 훨씬 편했다.

사실 30분도 안 걸려서 만들었지만, 한 번 만들어 놓으니 계속 손이 가는 도구가 됐다. 예전에 슬랙 유료 플랜을 썼던 게 아까울 정도다. 텔레그램 봇 하나로 알림 시스템 구축이 이렇게 간단할 줄은 상상도 못 했다.