파이썬과 자바스크립트를 사용하여 텔레그램 봇을 구현하고 AWS에 배포하기
새로운 텔레그램 봇을 AWS에 배포하고 있습니다.
다음은 Telegram 봇을 구현하고 AWS에 배포하는 단계별 튜토리얼의 노트입니다. Telegram 봇을 구현하고 AWS에 배포에 대한 간단한 시작 방법(long polling)과 프로덕션 준비 경로(webhooks)를 Python과 Node.js 예제와 함께 제공합니다.
Telegram 봇은 서버에서 실행되는 앱으로, 사용자와 Telegram을 통해 대화합니다. 봇을 등록하고 간단한 프로그램을 작성한 후, Telegram의 Bot API에 연결합니다.
TL;DR
- @BotFather를 통해 봇을 생성하고 토큰을 얻습니다.
- python-telegram-bot 또는 Telegraf을 사용하여 작은 앱을 작성합니다.
- 로컬에서는 long polling부터 시작하고, 프로덕션에서는 webhooks로 전환합니다.
필요한 사항
-
Telegram 계정 (모바일 또는 데스크탑)
-
기본 터미널 + 코드 편집기
-
다음 중 하나:
- Python 3.10+ 및
pip
- Node.js 18+ 및
npm
/pnpm
/yarn
- Python 3.10+ 및
BotFather를 사용하여 봇 생성 (토큰 얻기)
- Telegram에서 @BotFather를 검색하고 채팅을 시작합니다.
/newbot
을 보내고 봇 이름을 지정하고 고유한 사용자 이름(예:example_helper_bot
)을 선택합니다.- BotFather는
123456:ABC-DEF...
와 같은 API 토큰을 제공합니다. 이는 비밀번호처럼 취급해야 하며, Git에 커밋하지 마세요. 유출될 경우 BotFather를 통해 언제든지 재생성할 수 있습니다.
팁: Telegram 공식 튜토리얼은 이러한 단계와
getMe
를 사용하여 토큰을 확인하는 방법을 자세히 설명합니다.
스택 선택
Bot API를 직접 호출할 수도 있지만, 유지보수가 쉬운 라이브러리를 사용하는 것이 더 빠릅니다.
- Python:
python-telegram-bot
(비동기, 현대적). ([https://docs.python-telegram-bot.org]) - Node.js:
telegraf
(성숙, TypeScript 친화적). ([https://telegraf.js.org])
Bot API 자체는 여기에서 문서화되어 있으며, getUpdates
및 setWebhook
과 같은 메서드의 공식 출처입니다.
빠른 시작: 로컬에서 long polling 실행
long polling은 로컬 개발에 완벽합니다: 봇은 getUpdates
를 통해 Telegram에서 새로운 업데이트를 반복적으로 요청합니다. (프로덕션에서는 일반적으로 webhooks로 전환합니다.)
Telegram은 업데이트를 받는 데 두 가지 상호 배타적인 방법을 지원합니다:
getUpdates
(polling) 또는 webhooks; 업데이트는 최대 24시간 동안 보관됩니다.
옵션 A: Python (python-telegram-bot 사용)
설치 및 프로젝트 생성
python -m venv .venv && source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install python-telegram-bot
코드: bot.py
import os
from telegram import Update
from telegram.ext import ApplicationBuilder, CommandHandler, MessageHandler, ContextTypes, filters
TOKEN = os.environ["TELEGRAM_BOT_TOKEN"] # export TELEGRAM_BOT_TOKEN=123:ABC
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
await update.message.reply_text("👋 안녕하세요! 제가 받은 메시지를 다시 보내겠습니다.")
async def echo(update: Update, context: ContextTypes.DEFAULT_TYPE):
await update.message.reply_text(update.message.text)
def main():
app = ApplicationBuilder().token(TOKEN).build()
app.add_handler(CommandHandler("start", start))
app.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, echo))
app.run_polling() # long polling
if __name__ == "__main__":
main()
실행 방법:
export TELEGRAM_BOT_TOKEN=123456:ABC-DEF...
python bot.py
이 코드는 현재 안정 버전의 문서에서 제공하는 ApplicationBuilder
를 사용합니다.
옵션 B: Node.js (telegraf 사용)
설치 및 프로젝트 생성
npm init -y
npm i telegraf
코드: index.js
const { Telegraf } = require('telegraf');
const bot = new Telegraf(process.env.BOT_TOKEN); // BOT_TOKEN 환경 변수 설정
bot.start((ctx) => ctx.reply('👋 안녕하세요! 제가 받은 메시지를 다시 보내겠습니다.'));
bot.on('text', (ctx) => ctx.reply(ctx.message.text));
bot.launch();
// 부드러운 종료
process.once('SIGINT', () => bot.stop('SIGINT'));
process.once('SIGTERM', () => bot.stop('SIGTERM'));
실행 방법:
export BOT_TOKEN=123456:ABC-DEF...
node index.js
이 코드는 공식 Telegraf “Getting started” 예제와 동일합니다.
유용하게 만들기 (명령어 및 버튼)
/help
명령어 추가
Python
from telegram.ext import CommandHandler
async def help_cmd(update: Update, context: ContextTypes.DEFAULT_TYPE):
await update.message.reply_text("/start – 인사\n/help – 도움")
app.add_handler(CommandHandler("help", help_cmd))
CommandHandler
는 /명령어
를 바인딩하는 표준 방법입니다.
Node (Telegraf)
bot.help((ctx) => ctx.reply('/start – 인사\n/help – 도움'));
빠른 응답 버튼 (커스텀 키보드)
Python
from telegram import ReplyKeyboardMarkup
async def start(update: Update, context):
keyboard = [["도움", "소개"]]
await update.message.reply_text(
"옵션을 선택하세요:",
reply_markup=ReplyKeyboardMarkup(keyboard, resize_keyboard=True)
)
Node (Telegraf)
const { Markup } = require('telegraf');
bot.start((ctx) => {
ctx.reply("옵션을 선택하세요:", Markup.keyboard([["도움", "소개"]]).resize());
});
프로덕션: webhooks 전환
프로덕션에서는 Telegram이 HTTPS 엔드포인트로 업데이트를 푸시합니다. setWebhook
을 사용하여 설정할 수 있으며, 선택적으로 secret token을 제공하여 X-Telegram-Bot-Api-Secret-Token
헤더를 통해 요청을 검증할 수 있습니다.
webhook 설정 (curl 사용):
TOKEN=123456:ABC-DEF...
WEBHOOK_URL="https://your-domain.com/telegram/${TOKEN}" # 비밀 경로 포함
SECRET="my-secret-42"
curl -X POST "https://api.telegram.org/bot${TOKEN}/setWebhook" \
-d url="${WEBHOOK_URL}" \
-d secret_token="${SECRET}" \
-d drop_pending_updates=true
규격에서 주요 포인트:
setWebhook
은 HTTPS URL이 필요하며secret_token
을 지원합니다.- webhook이 설정되면
getUpdates
를 사용할 수 없습니다. - 지원되는 포트는 443, 80, 88, 8443입니다.
webhook 서버 옵션
- Node (Telegraf 내장 webhook):
bot.launch({
webhook: {
domain: "your-domain.com",
port: 8080,
// 선택적 비밀 헤더
secretToken: process.env.WEBHOOK_SECRET
}
});
Telegraf은 webhook 옵션을 첫 등급으로 제공하며, Express/Fastify/Cloudflare Workers 등과 통합됩니다.
- Python (
python-telegram-bot
) PTB는 ASGI/WSGI 프레임워크 (FastAPI, Starlette, Flask)와 잘 작동합니다.ApplicationBuilder().token(...).build()
를 사용하고, 들어오는 JSON 업데이트를 애플리케이션에 전달하는 POST 경로를 노출시키고bot.set_webhook(...)
을 호출하세요.ApplicationBuilder
문서에서 구축 패턴을 참조하세요. ([docs.python-telegram-bot.org][2])
로컬에서 테스트하려면 터널 (예:
ngrok
)을 사용하여https://...
을 Telegram에 노출시키고, 공개 URL로setWebhook
을 호출하세요.
봇 명령어 구성 (선택적이지만 사용자 친화적)
봇 명령어 목록 (사용자가 /
를 입력할 때 보이는 내용)은 다음 중 하나로 정의할 수 있습니다:
- BotFather를 통해 /mybots → Edit Bot → Edit Commands, 또는
- 라이브러리 또는 원시 Bot API를 사용하여
setMyCommands
로 프로그래밍적으로 정의할 수 있습니다.
Telegram 공식 “BotFather에서 ‘Hello World’까지” 가이드는 더 복잡한 명령어 처리 및 예제를 제공합니다.
배포 고려 사항
HTTPS 기능이 있는 호스트는 모두 사용 가능합니다:
- 작은 VM (Ubuntu + systemd)
- Serverless (AWS Lambda/Cloud Functions) 및 webhook 통합
- Fly.io/Render/Heroku와 같은 플랫폼의 컨테이너
Telegraf 문서에는 Lambda, GCF, Express, Fastify 등에 대한 예제가 포함되어 있습니다.
보안 및 신뢰성 체크리스트
- 토큰을 비밀로 유지 (환경 변수, 비밀 관리자). 유출 시 BotFather에서 취소하세요.
- webhook과 함께
secret_token
사용 및X-Telegram-Bot-Api-Secret-Token
헤더를 검증하세요. - polling과 webhooks를 혼합하지 마세요; 한 번에 하나만 선택하세요.
- 오류 및 재시도 처리: Telegram은 2xx가 아닌 webhook 응답을 재시도합니다. 적절히 로깅하세요.
- 업데이트 유형에 주의 (메시지, 콜백, 인라인 쿼리, 결제 등) 및 필요에 따라만 구독하세요 (
allowed_updates
).
HTTP API 직접 사용 (선택적)
다음과 같은 엔드포인트를 호출할 수 있습니다:
https://api.telegram.org/bot<token>/getMe
https://api.telegram.org/bot<token>/getUpdates
https://api.telegram.org/bot<token>/sendMessage
규격에 따라 GET 또는 POST와 JSON 또는 폼 데이터를 사용하세요.
다음으로 해야 할 일 및 읽어야 할 내용
- 공식 Bot API 참조: 메서드, 객체, 제한, 포맷, 결제, 인라인 모드.
- 공식 “BotFather에서 ‘Hello World’까지” 가이드: 더 깊은 튜토리얼 및 다국어 예제.
- python-telegram-bot 문서 (안정 버전): 현대적인 비동기 패턴.
- Telegraf 문서: 빠른 레시피, webhook 도우미, TS 타입.
Python 버전을 위한 AWS 배포 단계
Telegram 봇을 AWS 인프라에 배포할 때 두 가지 주요 옵션이 있습니다:
- A) Serverless (API Gateway + Lambda + Secrets Manager) — 가장 저렴하고 간단하며, 중간 트래픽에 적합합니다.
- B) Containerized (ECS Fargate + ALB + ACM) — 지속적인 트래픽과
python-telegram-bot
과 같은 장기 실행 라이브러리에 적합한 프로덕션 등급입니다.
A) AWS에서 Serverless (API Gateway + Lambda)
서버가 필요 없고 거의 없는 유휴 비용을 원할 때 사용하세요. 아래 코드는 Telegram webhooks를 직접 처리합니다 (장기 실행 이벤트 루프 없음).
- 최소한의 Lambda 핸들러 (Python) 준비
handler.py
생성:
import json, os, urllib.request
BOT_TOKEN = os.environ["TELEGRAM_BOT_TOKEN"]
SECRET_HEADER = os.environ.get("WEBHOOK_SECRET", "") # Bot API setWebhook secret_token과 일치해야 함
API_BASE = f"https://api.telegram.org/bot{BOT_TOKEN}"
def reply(chat_id: int, text: str):
data = json.dumps({"chat_id": chat_id, "text": text}).encode()
req = urllib.request.Request(f"{API_BASE}/sendMessage", data=data, headers={"Content-Type": "application/json"})
with urllib.request.urlopen(req) as resp:
return resp.read()
def lambda_handler(event, context):
# 1) Telegram의 비밀 헤더 확인 (webhook 설정 시 설정됨)
if SECRET_HEADER:
if event.get("headers", {}).get("X-Telegram-Bot-Api-Secret-Token") != SECRET_HEADER:
return {"statusCode": 401, "body": "invalid secret"}
# 2) 업데이트 파싱
body = event.get("body") or "{}"
update = json.loads(body)
message = update.get("message") or update.get("edited_message")
if not message:
# 현재는 콜백_쿼리, 인라인_쿼리 등 비메시지 업데이트 무시
return {"statusCode": 200, "body": "ok"}
chat_id = message["chat"]["id"]
text = (message.get("text") or "").strip()
# 3) 간단한 라우팅
if text.startswith("/start"):
reply(chat_id, "👋 AWS Lambda에서 인사드립니다! 어떤 텍스트를 보내면 다시 보내겠습니다.")
elif text.startswith("/help"):
reply(chat_id, "/start – 인사\n/help – 도움\n(AWS Lambda에 배포됨)")
elif text:
reply(chat_id, text) # 에코
return {"statusCode": 200, "body": "ok"}
이 코드는 Bot API에 직접 HTTPS 호출하여 Lambda가 가볍고 cold-start에 친화적이도록 유지합니다. 나중에 라우팅을 확장할 수 있습니다.
- Lambda 패키징 및 배포
# 프로젝트 구조
# .
# ├─ handler.py
# └─ requirements.txt # (이 최소 예제에서는 비워둠)
zip -r function.zip handler.py
aws lambda create-function \
--function-name telegram-bot-webhook \
--runtime python3.11 \
--role arn:aws:iam::<ACCOUNT_ID>:role/<LambdaExecutionRole> \
--handler handler.lambda_handler \
--timeout 10 \
--memory-size 256 \
--zip-file fileb://function.zip \
--environment Variables='{TELEGRAM_BOT_TOKEN=<TOKEN_FROM_BOTFATHER>,WEBHOOK_SECRET=my-secret-42}'
IAM 역할 (<LambdaExecutionRole>
)은 CloudWatch Logs를 위해 AWSLambdaBasicExecutionRole
이 필요합니다.
토큰을 AWS Secrets Manager에 저장하고 초기화 시 로드하는 것이 좋습니다—이 예제는 간결성을 위해 환경 변수를 사용합니다.
- HTTPS 엔드포인트 생성 (API Gateway)
# HTTP API (REST가 아닌)를 사용하여 지연 시간을 줄임
API_ID=$(aws apigatewayv2 create-api \
--name telegram-webhook \
--protocol-type HTTP \
--target arn:aws:lambda:<REGION>:<ACCOUNT_ID>:function:telegram-bot-webhook \
--query 'ApiId' --output text)
# 기본 경로 POST /webhook 추가
aws apigatewayv2 create-integration \
--api-id $API_ID \
--integration-type AWS_PROXY \
--integration-uri arn:aws:lambda:<REGION>:<ACCOUNT_ID>:function:telegram-bot-webhook \
--payload-format-version 2.0
aws apigatewayv2 create-route \
--api-id $API_ID \
--route-key "POST /webhook" \
--target "integrations/$(
aws apigatewayv2 get-integrations --api-id $API_ID --query 'Items[0].IntegrationId' --output text
)"
aws apigatewayv2 create-deployment --api-id $API_ID
GW_URL=$(aws apigatewayv2 get-apis --query "Items[?ApiId=='$API_ID'].ApiEndpoint" --output text)
echo $GW_URL # 예: https://abc123.execute-api.ap-somewhere.amazonaws.com
Lambda 권한이 자동으로 추가되도록 확인하세요; 아니라면:
aws lambda add-permission \
--function-name telegram-bot-webhook \
--statement-id apigw \
--action lambda:InvokeFunction \
--principal apigateway.amazonaws.com \
--source-arn "arn:aws:execute-api:<REGION>:<ACCOUNT_ID>:$API_ID/*/*/webhook"
- Telegram에 webhook 지정
TOKEN=<TOKEN_FROM_BOTFATHER>
WEBHOOK_URL="$GW_URL/webhook"
SECRET="my-secret-42"
curl -X POST "https://api.telegram.org/bot${TOKEN}/setWebhook" \
-d url="${WEBHOOK_URL}" \
-d secret_token="${SECRET}" \
-d drop_pending_updates=true
봇에 /start
를 보내면 메시지가 API Gateway → Lambda를 통해 흐르게 됩니다.
- 로그, 재시도 및 업데이트
- 로그 확인:
aws logs tail /aws/lambda/telegram-bot-webhook --follow
- Telegram은 엔드포인트가 2xx가 아닐 경우 재시도합니다; 응답을 빠르게 유지하세요.
- 새로운 코드 배포: 다시 zip +
aws lambda update-function-code --function-name telegram-bot-webhook --zip-file fileb://function.zip
.
B) ECS Fargate + ALB에서 컨테이너화된 python-telegram-bot
python-telegram-bot
(PTB)의 편의성과 비동기 앱, 지속적인 연결을 원할 때 사용하세요. PTB는 HTTPS 로드 밸런서 뒤에서 실행됩니다.
- 앱 코드 (FastAPI + PTB webhook)
app.py
:
import os, asyncio
from fastapi import FastAPI, Request, Header
from telegram import Update
from telegram.ext import ApplicationBuilder, CommandHandler, MessageHandler, ContextTypes, filters
TOKEN = os.environ["TELEGRAM_BOT_TOKEN"]
SECRET = os.environ.get("WEBHOOK_SECRET", "")
app = FastAPI()
tg_app = None # PTB 애플리케이션
async def start_cmd(update: Update, ctx: ContextTypes.DEFAULT_TYPE):
await update.message.reply_text("🚀 ECS Fargate + PTB에서 인사드립니다!")
async def echo(update: Update, ctx: ContextTypes.DEFAULT_TYPE):
await update.message.reply_text(update.message.text)
@app.on_event("startup")
async def on_startup():
global tg_app
tg_app = ApplicationBuilder().token(TOKEN).build()
tg_app.add_handler(CommandHandler("start", start_cmd))
tg_app.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, echo))
# run_polling() 실행하지 않음; 웹훅을 수동으로 피드합니다.
@app.post("/telegram/webhook")
async def telegram_webhook(request: Request, x_telegram_bot_api_secret_token: str | None = Header(default=None)):
if SECRET and (x_telegram_bot_api_secret_token != SECRET):
return {"ok": True} # 조용히 무시
data = await request.json()
update = Update.de_json(data, tg_app.bot)
await tg_app.process_update(update)
return {"ok": True}
uvicorn
실행 진입점: uvicorn app:app --host 0.0.0.0 --port 8080
- Dockerfile
FROM python:3.11-slim
WORKDIR /app
ENV PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1
RUN pip install --no-cache-dir fastapi uvicorn[standard] python-telegram-bot==21.*
COPY app.py .
EXPOSE 8080
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8080"]
빌드 및 푸시:
aws ecr create-repository --repository-name telegram-ptb || true
ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
REGION=<REGION>
ECR="$ACCOUNT_ID.dkr.ecr.$REGION.amazonaws.com/telegram-ptb"
aws ecr get-login-password --region $REGION | docker login --username AWS --password-stdin "$ACCOUNT_ID.dkr.ecr.$REGION.amazonaws.com"
docker build -t telegram-ptb .
docker tag telegram-ptb:latest $ECR:latest
docker push $ECR:latest
- ECS Fargate 서비스 (ALB 뒤에)
- 보안 그룹을 생성하여 ALB에 443을 허용하고, ECS 작업에 8080을 허용합니다.
- Application Load Balancer (ALB)를 생성하고 **HTTPS 리스너 (443)**와 ACM 인증서를 사용합니다.
- 타겟 그룹: HTTP 8080 (IP 타겟 유형), 건강 상태 확인 경로
/
(FastAPI는 404를 반환;@app.get("/")
건강 상태 경로를 추가할 수 있습니다).
Task Definition (Fargate) 생성:
- 컨테이너 이미지:
$ECR:latest
- 포트 매핑: 8080
- 환경 변수:
TELEGRAM_BOT_TOKEN
,WEBHOOK_SECRET
- Task 역할: 기본 CloudWatch 로깅.
ECS Service 생성:
- 실행 유형 Fargate, 원하는 수의 작업 수.
- ALB 타겟 그룹에 연결.
ALB의 공개 HTTPS 도메인을 확인하거나 Route 53을 사용하여 DNS 이름을 지정하세요.
- Telegram에 webhook URL 지정
TOKEN=<TOKEN_FROM_BOTFATHER>
SECRET="my-secret-42"
WEBHOOK_URL="https://your.domain.com/telegram/webhook"
curl -X POST "https://api.telegram.org/bot${TOKEN}/setWebhook" \
-d url="${WEBHOOK_URL}" \
-d secret_token="${SECRET}" \
-d drop_pending_updates=true \
-d allowed_updates='["message","edited_message","callback_query"]'
- 확장 및 운영
- ECS 작업 수를 증가/감소; ALB가 트래픽을 분산합니다.
- Secrets Manager에서 토큰을 회전하고, 서비스에 새 작업 환경 변수를 업데이트합니다.
- CloudWatch Logs를 사용하여 앱 로그 및 ALB 액세스 로그 (S3)를 확인합니다.
어떤 것을 선택해야 할까요?
- Lambda + API Gateway: 가장 간단하고 저비용이며, 분당 몇 번의 호출만 필요한 봇에 적합합니다.
- ECS Fargate + ALB:
python-telegram-bot
의 전체 경험, 커스텀 미들웨어, 백그라운드 작업, 지속적인 트래픽을 원할 때 최적입니다.
빠른 체크리스트 (두 가지 접근 모두)
- HTTPS 엔드포인트 +
secret_token
사용 및X-Telegram-Bot-Api-Secret-Token
헤더 검증. - webhook 또는 polling 중 하나 선택 (둘 다 사용하지 마세요).
- 구성은 Secrets Manager에 저장하고, 코드에 저장하지 마세요.
- 관측 가능성 추가: CloudWatch Logs + 메트릭 (5xx 경고).
- 필요한 업데이트 유형만 처리하고 나중에 확장하세요.
유용한 링크
- https://docs.python-telegram-bot.org
- https://telegraf.js.org
- https://core.telegram.org/bots/tutorial
- https://core.telegram.org/bots/api
- AWS CDK 개요, TypeScript 및 Python 예제 및 성능 고려사항
- AWS Lambda 성능: JavaScript vs Python vs Golang
- AWS SAM 및 Python을 사용한 계층화된 Lambda
- AWS SAM + AWS SQS + Python PowerTools
- Python 간단한 가이드
- uv - 새로운 Python 패키지, 프로젝트 및 환경 관리자
- Node.js 설치