![[시리즈 4/5] 텔레그램 알림 + 로그 전략](https://blog.flyerschal.com/uploads/posts/cicd-s4-cover.png)
[시리즈 4/5] 텔레그램 알림 + 로그 전략
[시리즈 4/5] 텔레그램 알림 + 로그 전략

이 글은 "Claude Code로 구축하는 무중단 자동 배포 시스템" 시리즈의 네 번째 글입니다.
이 글에서 다루는 것
배포 시스템이 자동화되면 "지금 배포가 잘 되고 있는지" 눈으로 확인하기 어렵습니다. 이 글에서는 텔레그램 알림으로 실시간 모니터링하고, 구조화된 로그로 문제를 추적하며, Claude Code에 로그를 전달하여 시스템을 지속적으로 개선하는 방법을 다룹니다.
1단계: 텔레그램 봇 생성
BotFather에서 봇 만들기
- 텔레그램에서 @BotFather 검색 후 대화 시작
/newbot명령어 입력- 봇 이름 입력 (예:
My Deploy Bot) - 봇 사용자명 입력 (예:
my_deploy_bot,_bot으로 끝나야 함) - 봇 토큰 복사
7123456789:AAHxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Chat ID 확인
봇에게 메시지를 보낸 후, 다음 URL로 Chat ID를 확인합니다:
https://api.telegram.org/bot{YOUR_TOKEN}/getUpdates
응답에서 "chat":{"id":1234567890} 부분이 Chat ID입니다.
.env.production에 저장
# ~/.Documents/services/blog/.env.production
TELEGRAM_BOT_TOKEN=7123456789:AAHxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
TELEGRAM_CHAT_ID=1234567890
2단계: 알림 함수 구현
기본 알림 함수
notify() {
local MSG="$1"
if [ -n "$TELEGRAM_BOT_TOKEN" ] && [ -n "$TELEGRAM_CHAT_ID" ]; then
curl -s -X POST \
"https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
-d "chat_id=${TELEGRAM_CHAT_ID}" \
-d "text=${MSG}" \
-d "parse_mode=Markdown" > /dev/null 2>&1
fi
}
단계별 알림 포인트
deploy.sh에 다음 위치에 알림을 추가합니다:
# 1. 배포 시작
notify "[blog] 배포 시작: ${CURRENT} → ${TARGET}"
# 2. 빌드 시작
notify "[blog] 빌드 진행 중..."
# 3. 빌드 실패 시
notify "[blog] 실패: Next.js 빌드 에러"
# 4. Docker 이미지 빌드 실패 시
notify "[blog] 실패: Docker 이미지 빌드 에러"
# 5. 헬스체크 실패 시
notify "[blog] 실패: 헬스체크 타임아웃 (60초), ${CURRENT} 유지"
# 6. 전환 완료
notify "[blog] 전환 완료: ${CURRENT} → ${TARGET}"
# 7. 배포 완료
notify "[blog] 배포 완료. 활성: ${TARGET} (port:${PORT})"
notify "접속 확인: https://blog.flyerschal.com"
알림 메시지 포맷
Markdown을 사용하면 가독성이 좋아집니다:
notify_start() {
notify "🚀 *배포 시작*
프로젝트: \`${SERVICE}\`
전환: ${CURRENT} → ${TARGET}
커밋: \`$(cd $BUILD_DIR && git rev-parse --short HEAD)\`
시간: $(date '+%H:%M:%S')"
}
notify_success() {
notify "✅ *배포 성공*
프로젝트: \`${SERVICE}\`
활성: ${TARGET} (port:${PORT})
소요: ${DURATION}초
확인: https://blog.flyerschal.com"
}
notify_failure() {
local REASON="$1"
notify "❌ *배포 실패*
프로젝트: \`${SERVICE}\`
원인: ${REASON}
유지: ${CURRENT}
로그: /deploy/logs/${SERVICE}-latest.log"
}
텔레그램에서 이렇게 보입니다
🚀 배포 시작
프로젝트: blog
전환: blue → green
커밋: abc1234
시간: 14:23:05
🐳 빌드 진행 중...
✅ 배포 성공
프로젝트: blog
활성: green (port:15001)
소요: 87초
확인: https://blog.flyerschal.com
3단계: 로그 구조화
로그가 왜 중요한가?
텔레그램 알림은 "성공/실패"만 알려줍니다. 하지만 왜 실패했는지, 빌드 시간이 왜 늘어났는지, 어떤 경고가 있었는지는 로그에서만 확인할 수 있습니다.
로그 파일 구조
~/Documents/services/blog/
├── deploy.log # 최신 배포 로그 (덮어쓰기)
├── watcher.log # deploy-watcher 로그
└── logs/ # (선택) 히스토리 보관
├── deploy-20260326-142305.log
└── deploy-20260326-153012.log
로그 함수 구현
LOG_FILE="${BLOG_DIR}/deploy.log"
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}
# 사용 예시
log "배포 시작: ${CURRENT} → ${TARGET}"
log "git clone 완료"
log "npm install 시작"
전체 출력 리다이렉트
스크립트의 모든 출력(stdout, stderr)을 로그 파일에 기록합니다:
# deploy.sh 시작 부분에 추가
exec > >(tee -a "$LOG_FILE") 2>&1
이렇게 하면 npm install, docker build 등 모든 명령의 출력이 로그에 남습니다.
히스토리 보관 (선택)
# 배포 시작 시 이전 로그 백업
if [ -f "$LOG_FILE" ]; then
mkdir -p "${BLOG_DIR}/logs"
cp "$LOG_FILE" "${BLOG_DIR}/logs/deploy-$(date '+%Y%m%d-%H%M%S').log"
fi
> "$LOG_FILE" # 새 로그 시작
로그에 포함해야 할 정보
| 항목 | 예시 | 이유 |
|---|---|---|
| 타임스탬프 | [2026-03-26 14:23:05] | 시간순 추적 |
| 단계 이름 | 빌드 시작, 헬스체크 | 어디서 실패했는지 |
| 소요 시간 | 빌드: 45초 | 성능 추적 |
| 커밋 해시 | abc1234 | 어떤 버전인지 |
| 에러 출력 | npm error, docker error | 원인 분석 |
| 최종 결과 | 성공 / 실패: 헬스체크 타임아웃 | 요약 |
4단계: deploy-watcher (파일 감시 방식)
Webhook이 직접 deploy.sh를 실행하는 대신, 트리거 파일을 생성하고 watcher가 감지하여 실행하는 패턴입니다.
왜 watcher를 사용하나?
- Webhook 컨테이너는 Docker 내부에서 실행됨 → 호스트의 모든 도구에 접근 어려움
- deploy.sh는 호스트에서 실행해야 npm, docker 등을 직접 사용 가능
- watcher가 호스트에서 트리거 파일을 감시하고 deploy.sh 실행
deploy-watcher.sh
#!/bin/bash
# 호스트에서 실행 — 트리거 파일을 감시하고 deploy.sh 실행
BLOG_DIR="$(cd "$(dirname "$0")" && pwd)"
TRIGGER_FILE="${BLOG_DIR}/.deploy-trigger"
LOG_FILE="${BLOG_DIR}/watcher.log"
echo "[$(date)] Watcher started" >> "$LOG_FILE"
while true; do
if [ -f "$TRIGGER_FILE" ]; then
echo "[$(date)] Trigger detected, starting deploy..." >> "$LOG_FILE"
rm -f "$TRIGGER_FILE"
bash "${BLOG_DIR}/deploy.sh" >> "$LOG_FILE" 2>&1
echo "[$(date)] Deploy finished (exit: $?)" >> "$LOG_FILE"
fi
sleep 5
done
Webhook에서 트리거 생성
// webhook 컨테이너 내 Node.js 서버
app.post('/webhook', (req, res) => {
// 서명 검증 후
fs.writeFileSync('/repo/.deploy-trigger', Date.now().toString());
console.log('배포 트리거 생성');
res.sendStatus(200);
});
watcher를 시스템 서비스로 등록
# macOS에서는 launchd 또는 단순히 백그라운드 실행
nohup bash ~/Documents/services/blog/deploy-watcher.sh &
5단계: Claude Code 피드백 루프
배포 로그를 Claude Code에 전달하면 문제 패턴을 자동으로 감지하고 스크립트를 개선할 수 있습니다.
피드백 루프란?
배포 실행 → 로그 생성 → Claude Code에 전달 → 분석 & 개선안
↑ │
└──────────── 스크립트 수정 ◄──────────────────┘
활용 사례 1: 빌드 시간 최적화
이 배포 로그를 분석해줘: ~/Documents/services/blog/deploy.log
확인해야 할 것:
1. npm install에 걸린 시간
2. docker build에 걸린 시간
3. 불필요한 단계가 있는지
4. 캐시를 더 활용할 수 있는지
Claude Code의 응답 예시:
npm install에 45초 걸리고 있습니다. package-lock.json이 변경되지 않았으므로 Docker 레이어 캐시를 활용하면 3초로 줄일 수 있습니다. Dockerfile의 COPY 순서를 조정하겠습니다.
활용 사례 2: 실패 원인 분석
배포가 실패했어. 로그를 확인하고 원인을 분석해줘:
~/Documents/services/blog/deploy.log
헬스체크 타임아웃으로 실패한 것 같은데,
앱 시작이 왜 느린지 확인해줘.
활용 사례 3: 반복 패턴 감지
최근 5개 배포 로그를 분석해서:
~/Documents/services/blog/logs/
1. 빌드 시간 추이 (늘어나고 있는지)
2. 반복적인 경고 메시지
3. 개선할 수 있는 부분
효과
- 빌드 시간 최적화 (불필요한 단계 제거)
- 반복 에러 방지 (패턴 인식)
- deploy.sh 자동 개선 (Claude Code가 직접 수정)
- 장기적 안정성 향상
6단계: 알림 + 로그 통합
실패 시 로그 일부를 텔레그램으로 전송
notify_failure_with_log() {
local REASON="$1"
local LAST_LINES=$(tail -5 "$LOG_FILE" | head -c 500)
notify "❌ *배포 실패*
원인: ${REASON}
\`\`\`
${LAST_LINES}
\`\`\`
전체 로그: deploy.log 확인"
}
성공 시 소요 시간 리포트
SECONDS=0 # 배포 시작 시 타이머 시작
# ... 배포 과정 ...
DURATION=$SECONDS
notify_success # DURATION 변수 사용
주의사항
텔레그램 Bot API 제한
| 제한 | 값 |
|---|---|
| 메시지 길이 | 4096자 |
| 메시지 전송 빈도 | 초당 1건 (같은 채팅) |
| 그룹 메시지 빈도 | 분당 20건 |
로그 전체를 텔레그램으로 보내면 안 됩니다. 요약만 보내세요.
봇 토큰 보안
.env.production에 저장하고.gitignore에 추가- 토큰이 노출되면 즉시 BotFather에서
/revoke - deploy.sh에 토큰을 하드코딩하지 마세요
이 단계에서 완성된 것
✅ 텔레그램 봇 생성 및 알림 함수 ✅ 단계별 알림 포인트 (시작, 빌드, 성공, 실패) ✅ 구조화된 로그 시스템 ✅ deploy-watcher 패턴 ✅ Claude Code 피드백 루프 활용법 ✅ 알림 + 로그 통합
다음 글에서는 Claude Code를 활용하여 이 모든 시스템을 대화하듯이 구축하는 방법과, 실전 운영에서 겪는 문제와 해결책을 다룹니다.

