Claude Code + Telegram 자동 알림 시스템 구축기 마지막(과연?)

소개

시도하고자 했던 것:
Claude Code 작업 완료 시 Telegram으로 100% 확실하게 알림을 받고, 양방향 통신하는 시스템 구축

근본적인 문제 - MCP의 한계:

MCP 방식의 치명적 문제

MCP = Claude가 "선택적으로" 호출하는 도구

  • Claude가 telegram_send 호출 → ✅ 알림 감

  • Claude가 telegram_send 깜빡함 → ❌ 알림 안 감

→ Claude의 "의지"에 의존 = 신뢰할 수 없음

안 보내지는 경우를 대비해서 PreToolUse Hook을 사용하기는 했는데, 이마저도 Claude 의존적이었습니다.

실제 겪은 상황:

  • Claude에게 "작업 끝나면 텔레그램으로 알려줘"라고 했지만 종종 깜빡함

  • PreToolUse Hook으로 "AskUserQuestion 전에 telegram_send 먼저!" 경고해도 무시하는 경우 발생

  • CLAUDE.md에 굵은 글씨로 강조해도 100% 준수 불가능

해결책 - Telegram MCP 제거, 완전 Hook 기반 전환 및 Stop Hook방식으로 변경

Hook 방식의 핵심

Hook = Claude 턴 종료 시 "무조건" 실행되는 스크립트

Claude 턴 종료 → Hook 자동 실행 → 알림 100% 보장

→ 시스템 이벤트 기반 = Claude 의지와 무관

방식

실행 주체

신뢰도

MCP

Claude가 호출해야 함

80~90% (깜빡할 수 있음)

Hook

시스템이 자동 실행

100% (무조건 실행)

PreToolUse vs Stop Hook 비교

구분

PreToolUse Hook

Stop Hook

실행 시점

특정 도구 호출

턴 종료

역할

Claude에게 "경고" 메시지 전달

Hook이 직접 작업 수행

Claude 의존

⚠️ 경고를 무시할 수 있음

✅ Claude 의지와 무관

신뢰도

90% (경고해도 무시 가능)

100% (무조건 실행)

턴 종료가 발생하는 6가지 상황

Stop Hook은 아래 모든 상황에서 자동 실행됩니다:

#

상황

설명

예시

1

명시적 작업 완료

사용자가 요청한 작업을 모두 수행한 후

코드 수정, 파일 생성, 커밋 등 완료

2

사용자 입력 대기

AskUserQuestion 도구 호출 시

"A와 B 중 어떤 방식으로 진행할까요?"

3

정보 제공 후 대기

코드 분석 결과 설명 후

"더 볼 것 있나요?"

4

에러/블로커 발생

진행 불가능한 상황 발생

빌드 실패, 권한 문제

5

도구 사용 후 응답 대기

외부 작업 완료 보고 후

API 호출 결과 전달

6

Idle 상태

다음 지시를 기다리는 순간

응답 완료 후 대기


진행 방법

전체 아키텍처

1. 발송 흐름 (Claude → Telegram)

[Claude Code]
     │
     │ 턴 종료
     ▼
[Stop Hook] ──── POST /send ────▶ [Bridge Daemon]
(Python)                           (Node.js)
                                       │
                                       │ Telegram Bot API
                                       ▼
                                 [Telegram Bot]
                                       │
                                       ▼
                                 [사용자 수신] 📱

2. 수신 흐름 (Telegram → Claude)

[사용자 Reply] 📱
     │
     ▼
[Telegram Bot]
     │
     │ Long Polling
     ▼
[Bridge Daemon] ◀─── 메시지 감지 + 세션 매칭
     │
     │ 응답 저장
     ▼
[Stop Hook] ◀─────── GET /poll ─────────┘
(Python)              응답 수신
     │
     │ block 반환
     ▼
[Claude Code] ◀───── 다음 지시 전달

Bridge Daemon 역할

Hook과 Telegram 사이의 중계 서버 역할을 합니다:

1. HTTP API 서버 (포트 9876)

  • POST /send - 메시지 전송

  • GET /poll - 응답 대기

  • GET /health - 상태 확인

2. Telegram Long Polling

  • Bot API로 새 메시지 수신

  • Reply 메시지 감지 및 세션 매칭

3. 세션 관리

  • 질문-응답 매핑

  • 타임아웃 처리 (5분)

  • 자동 정리 (60분 후 만료)

Bridge를 사용하는 이유:

직접 호출

Bridge 경유

매번 Bot Token 노출

Token은 Bridge에만 저장

응답 수신 불가 (단방향)

Long Polling으로 양방향 통신

세션 관리 불가

질문-응답 매핑 가능

MCP 방식 vs Hook 방식 비교

MCP 방식 - Claude 의존

Claude 작업 완료
     ↓
Claude가 telegram_send 호출? ─── NO ──→ ❌ 알림 없음
     │
    YES
     ↓
MCP Server → Bridge → Telegram

Hook 방식 - 시스템 자동

Claude 작업 완료 (턴 종료)
     ↓
Stop Hook 자동 실행 (100%)
     ↓
Python urllib → Bridge HTTP API → Telegram ✅

Hook에서 MCP 사용 불가 (Telegram MCP를 제거한 또 하나의 이유)

핵심 포인트

  • Hook = Claude Code와 별개의 Python 프로세스

  • MCP는 Claude Code 내부에서만 사용 가능

  • Hook(외부 프로세스)에서는 MCP 호출 불가능

  • → Hook이 Telegram 보내려면 직접 HTTP 호출 필요

구분

Claude Code 내부

Hook (외부 프로세스)

MCP 사용

✅ 가능

❌ 불가능

HTTP API

✅ 가능

✅ 가능

결론: Hook에서 MCP를 못 쓰니까, 어차피 HTTP API로 Bridge 직접 호출해야 함 → MCP 존재 이유 없음 → 제거

마찬가지로 curl로 Telegram API를 직접 호출하던 /task 명령어(task.md)도 삭제함. Hook이 자동으로 알림을 보내므로 불필요해짐.


핵심 구현

Stop Hook --> Python 호출

#!/usr/bin/env python3
"""
Stop Hook: 턴 종료 시 telegram 자동 전송 + 응답 대기 (v3.9)

핵심: Claude가 뭘 하든 상관없이, 턴이 끝나면 무조건 실행됨
"""

def main():
    # 1. Telegram 모드 ON인지 확인
    if not is_telegram_mode_on(input_data):
        return 0

    # 2. Claude가 이미 telegram_send를 호출했는지 확인
    if has_telegram_send_in_turn(lines, last_user_idx):
        return 0  # Claude가 보냈으면 중복 방지

    # 3. Claude가 안 보냈으면 Hook이 대신 전송
    summary = extract_last_assistant_summary(lines, last_user_idx)
    send_telegram_auto(session_id, summary,
                       project_name=project_name,
                       session_name=session_name,
                       project_dir=project_dir)

    # 4. 응답 대기 (Telegram Reply)
    poll_response(session_id, question_id, timeout=300)

Bridge Daemon 시작

# Bridge 시작 (백그라운드 실행)
./scripts/telegram-bridge.sh start

# 상태 확인
./scripts/telegram-bridge.sh status
# 출력: ✅ Daemon 실행 중 (PID: 58952)

# 중지
./scripts/telegram-bridge.sh stop

설정 (settings.json)

{
  "hooks": {
    "Stop": [{
      "hooks": [{
        "type": "command",
        "command": "python3 ~/.claude/hooks/check-completion-notification.py"
      }]
    }]
  }
}

Telegram 메시지 포맷

메시지 포맷은 Claude Code에게 변경해 달라고 요청하면 변경해 줍니다.

🤖 itdalife-new / feature-auth

📁 /Users/eyeson/workspaces/itdalife-new

✅ Hook v3.9 업데이트 완료
- 세션명/프로젝트명 표시 추가
- 작업 요약 최대 600자

━━━━━━━━━━━━━━━━━━━━━━━━━
💬 Reply로 다음 지시를 보내주세요
💤 done 입력 시 대기 종료

세션 관리

하나의 Telegram 채팅방으로 수신하다보니, 여러 개의 터미널 띄워 놨을 때 구분이 잘 안되어서 세션명을 지정할 수 있게 만들었습니다.

# 세션명 설정 (여러 세션 구분용)
/session feature-auth

# Telegram 모드 ON/OFF
/telegram on
/telegram off

파일 구조

전역으로 적용해야 하는 부분과 프로젝트 단위로 적용해야 하는 부분을 잘 구분해야 합니다.

전역 설정 (~/.claude/)

~/.claude/
├── hooks/
│   └── check-completion-notification.py  # Stop Hook (v3.9)
├── commands/
│   └── session.md                        # /session 명령어
└── settings.json                         # Hook 설정

프로젝트별 설정 (/project/)

/project/
├── .telegram-mode                        # ON/OFF 플래그
├── .session-name                         # 세션명
├── mcp-servers/
│   └── telegram/
│       └── src/
│           ├── bridge/                   # HTTP 서버 + Telegram Polling
│           └── mcp/                      # (레거시) MCP 서버
└── scripts/
    └── telegram-bridge.sh                # Bridge 데몬 관리 스크립트

결과와 배운 점

핵심 배운 점

"AI에게 중요한 작업을 맡길 때는 의지가 아닌 시스템으로 강제해야 한다"

  • ❌ "꼭 알림 보내줘" (프롬프트 의존) → 깜빡할 수 있음

  • ❌ PreToolUse Hook으로 경고 → 무시할 수 있음

  • ✅ Stop Hook으로 자동 실행 → 100% 보장

시행착오

시도

결과

교훈

CLAUDE.md에 "반드시 telegram_send 호출" 강조

80% 준수

프롬프트만으로는 한계

PreToolUse Hook으로 경고 메시지

90% 준수

경고도 무시 가능

Stop Hook으로 자동 전송

100% 보장

시스템 강제가 답

추가 개선사항

  1. 타임아웃 일치: Hook(5분)과 Bridge(5분) 동일하게 설정 → 메모리 낭비 방지

  2. done 세션 관리: 사용자가 "done" 입력 시 알림 중단, 새 작업 시 자동 재개

  3. 프로젝트/세션 구분: 여러 Claude Code 세션 동시 운영 시 어디서 온 메시지인지 구분

  4. 재부팅 시 bridge daemon 시작 안되는 경우 발생 메시지 발송/수신안됨: 재부팅시 자동 구동되게 변경

꿀팁

  1. MCP 완전 제거: Hook만으로 충분, MCP는 오히려 중복/혼란 유발

  2. 양방향 통신: Telegram Reply로 다음 지시 가능 → 터미널 앞에 안 있어도 됨

  3. Ctrl+C 안전: Hook은 자식 프로세스라 중단 시 자동 종료, 좀비 프로세스 없음

앞으로의 계획

잘 써보려 합니다.

1

뉴스레터 무료 구독

👉 이 게시글도 읽어보세요