DOCS/troubleshooting/250915_happybell80_Slack_메시징_운영_런북.md
happybell80 a8b8a12aa4 Update Slack 메시징 운영 런북 - 완전판
- 모든 파일 경로와 함수명 추가
- 포맷/토큰/채널/스레드/DM 공통점 추가
- 차이점 요약 섹션 추가
- 엔드투엔드 흐름 요약 추가

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-15 15:02:23 +09:00

11 KiB

Slack 메시징 운영 런북

작성일: 2025-09-15

작성자: happybell80

환경: rb8001, robeing-gateway, skill-slack


구성요소

rb8001 (로빙 컨테이너)

  • 파일: rb8001/main.py

    • 엔드포인트
      • POST /api/slack/eventsapp.router.slack_handler.handle() 위임
      • POST /api/slack/interactiveapp.router.slack_handler.handle_interactive() 위임
      • POST /api/dm/send, POST /api/dm/send-to-user, POST /api/dm/send-email-summary, POST /api/dm/send-daily-summary → DM 스킬 호출
    • 스케줄
      • APScheduler로 헤드라인/뉴스/DM 작업을 트리거, 실제 전송은 각 스킬에서 Slack API 호출
  • 파일: rb8001/app/router/slack_handler.py

    • 핵심 함수
      • handle_slack_events(request) → Slack Events 수신 처리
        • 토큰: Gateway가 전달한 X-Slack-Bot-TokenWebClient 생성(팀별 토큰)
        • 채널/스레드/DM 판단:
          • DM: event.channel_type == "im" → thread_ts 없음
          • 스레드: event.thread_ts 유지
          • 멘션(app_mention)인데 스레드가 아니면 원본 tsthread_ts로 사용해 스레드 시작
        • 포맷: chat_postMessage(channel, text, thread_ts?, unfurl_links=False, unfurl_media=False)
        • I/O: Slack 이벤트(JSON) → 내부 라우터 router.route_message() → Slack 메시지 전송
      • process_slack_message_async(text, user_id, context) → 실제 메시지 생성/전송
      • handle_interactive(request) → 인터랙티브 액션 처리
        • Slack 서명 검증 후 액션 분기
        • 게시 버튼 처리 시 비동기 _handle_news_publish_with_update(...) 실행
        • response_url로 에페메럴 업데이트 전송(JSON POST)
      • _handle_news_publish_with_update(...)
        • 뉴스 게시 처리 후 response_url로 진행/성공/실패 에페메럴 메시지 전송
  • 파일: rb8001/app/skills/dm_skill.py

    • 토큰: settings.ROBEING_SLACK_BOT_TOKEN 또는 settings.SLACK_BOT_TOKEN
    • DM 전송
      • send_daily_dm(custom_message?)chat_postMessage(channel=슬랙유저ID, text)
      • send_dm_to_user(slack_id, message)chat_postMessage(...)
      • send_email_summary_dm() → 이메일 요약 포함 DM, unfurl_links=False
      • send_daily_summary_dm() → 이메일/뉴스 합친 모닝 브리핑 DM, 전송 후 뉴스 ID 기록
    • 형식: 모두 텍스트, 필요 시 링크 미리보기 비활성화
  • 파일: rb8001/app/skills/news_posting_skill.py

    • 토큰: COMPANYX_SLACK_BOT_TOKEN
    • 채널 메시지(블록)
      • send_news_for_posting(channel?) → 블록 생성 후 chat_postMessage(channel, blocks, text=fallback)
      • add_status_to_message(channel, ts, blocks)chat_update로 블록 갱신
    • 인터랙티브 진행 업데이트
      • send_slack_update(response_url, message)response_url로 JSON POST(원본 미교체)
  • 파일: rb8001/app/skills/startup_news_skill.py

    • 토큰: COMPANYX_SLACK_BOT_TOKEN
    • 채널 메시지(텍스트)
      • WebClient.chat_postMessage(channel=env 채널, text=뉴스 요약)
  • 파일: rb8001/app/router/dm_endpoint.py

    • 엔드포인트(내부용): /api/dm/send, /api/dm/send-to-user → dm_skill 호출
    • 자체 Slack 호출은 없음(스킬이 수행)

robeing-gateway (게이트웨이)

  • 파일: robeing-gateway/app/main.py
    • POST /slack/events → rb8001로 프록시
      • 토큰: DB에서 팀별 봇 토큰 조회(get_slack_bot_token(team_id)) → 헤더 X-Slack-Bot-Token
      • 사용자: Slack User ID→UUID 매핑(slack_user_to_uuid) → 헤더 X-User-Id
      • I/O: Slack→Gateway→rb8001(/api/slack/events), 자체 Slack 전송 없음
    • POST /slack/interactive → rb8001로 프록시
      • Slack 서명/타임스탬프/봇토큰/UUID 헤더 전달
      • 자체 Slack 전송 없음

skill-slack (슬랙 도우미 스킬)

  • 파일: skill-slack/app/api/endpoints/messages.py

    • 토큰: settings.SLACK_BOT_TOKEN
    • 엔드포인트
      • POST /sendchat_postMessage(channel, text, blocks?)
      • POST /updatechat_update(channel, ts, text?, blocks?)
    • 요청/응답 모델: app/models/requests.py(채널/텍스트/블록/ts/thread_ts 필드 정의), app/models/responses.py
    • 현재 rb8001가 직접 호출하는 경로는 확인되지 않음(요약/액션 추출은 별도 API를 호출)
  • 파일: skill-slack/app/services/slack_client.py

    • Slack 데이터 조회용 HTTP 호출(threads, history, users.info) 구현
    • 메시지 전송은 위 /send가 수행

기타 서비스

  • robeing-monitor: Slack 메시지 전송 없음(아이템/프리퍼런스 API)
  • skill-email/skill-news: Slack 메시지 전송 없음(내부 로직 또는 외부 서비스 호출)
  • rb10508_micro: Slack 전송 없음(채널/스레드 ID를 컨텍스트로만 이동)
  • frontend-base, auth-server: Slack OAuth/관리 API, Slack 메시지 전송 없음

토큰/식별자

  • 게이트웨이: Slack 요청에서 팀 ID를 추출해 slack_workspace.bot_token 조회 후 rb8001 호출 시 헤더 X-Slack-Bot-Token로 전달. Slack 사용자 ID→내부 UUID는 user.oauth_provider='slack' AND oauth_id=slack_user_id 매핑으로 조회해 X-User-Id로 전달.
  • rb8001: Slack 이벤트 응답 시 게이트웨이가 전달한 X-Slack-Bot-Token으로 WebClient 생성. DM 전송은 ROBEING_SLACK_BOT_TOKEN 또는 SLACK_BOT_TOKEN 사용. Company-X 뉴스 전송은 COMPANYX_SLACK_BOT_TOKEN 사용.
  • skill-slack: 자체 SLACK_BOT_TOKEN으로 WebClient 사용.

엔드포인트(I/O)

게이트웨이

  • POST /slack/events: 요청 본문을 rb8001 POST /api/slack/events로 전달, 헤더 X-Slack-Bot-Token, X-User-Id 추가.
  • POST /slack/interactive: 요청 본문을 rb8001 POST /api/slack/interactive로 전달, 헤더 X-Slack-*X-User-Id 전달.

rb8001

  • POST /api/slack/events: Slack 이벤트 처리 후 chat_postMessage로 채널/스레드/DM에 응답.
  • POST /api/slack/interactive: Slack 인터랙티브 처리, 후속 안내는 response_url로 전송.
  • POST /api/dm/send: 모든 사용자 대상 DM 전송 트리거(dmskill).
  • POST /api/dm/send-to-user: 특정 Slack 사용자 ID로 DM 전송(dmskill).

skill-slack

  • POST /api/v1/messages/send: chat_postMessage(channel, text, blocks?) 실행.
  • POST /api/v1/messages/update: chat_update(channel, ts, text?, blocks?) 실행.

메시지 전송 경로(플로우)

  • 이벤트: Slack → 게이트웨이(/slack/events) → rb8001(/api/slack/events) → rb8001이 Slack에 chat_postMessage 전송.
  • 인터랙티브: Slack → 게이트웨이(/slack/interactive) → rb8001(/api/slack/interactive) → 처리 후 response_url로 에페메럴 메시지 전송.
  • 정기/수동 DM: rb8001 스케줄러 또는 API → dm_skill → chat_postMessage(channel=슬랙 유저 ID).
  • 뉴스 채널 게시: rb8001 스케줄러 또는 명령 → news_posting_skill → chat_postMessage(channel=채널 ID, blocks).

채널/스레드/DM 규칙

  • DM: channel에 Slack 사용자 ID를 지정해 chat_postMessage 전송, thread_ts 미사용.
  • 스레드: event.thread_ts가 있으면 유지하여 스레드 응답. app_mention이 스레드가 아니면 원본 메시지 ts를 thread_ts로 사용해 스레드 시작.
  • 채널: 채널 ID로 전송하며, 뉴스 게시 시 Block Kit 블록 리스트를 사용.

메시지 형식/옵션

  • 텍스트: chat_postMessage(channel, text, thread_ts?) 사용.
  • 블록: 뉴스 게시 시 Block Kit 블록 배열(blocks) 사용, 필요 시 여러 메시지로 분할.
  • 링크 미리보기: 일부 전송 경로에서 unfurl_links=False, unfurl_media=False 사용.
  • 인터랙티브 업데이트: response_url에 JSON POST로 에페메럴 메시지 전송(replace_original=False, delete_original=False).

서명/검증

  • rb8001 인터랙티브 처리 시 Slack 서명 검증(X-Slack-Request-Timestamp, X-Slack-Signature) 수행.
  • 게이트웨이는 Slack 이벤트를 rb8001로 프록시하며 자체 Slack 메시지 전송 로직은 없음.

관련 설정/데이터

  • rb8001 설정 키: SLACK_BOT_TOKEN, ROBEING_SLACK_BOT_TOKEN, COMPANYX_SLACK_BOT_TOKEN, SLACK_SIGNING_SECRET.
  • 게이트웨이 DB 테이블: slack_workspace에서 팀별 bot_token 조회, user의 oauth_id(provider='slack')로 UUID 매핑.
  • skill-slack 설정 키: SLACK_BOT_TOKEN.

로깅 포인트

  • rb8001 slack_handler: 토큰 유효성(auth_test), 채널/스레드/이벤트 타임스탬프, 전송 성공 여부를 로그로 남김.
  • rb8001 dm_skill/news_posting_skill: 사용자별 전송 결과, Slack API 에러를 로그로 남김.
  • 게이트웨이: Slack 팀/사용자 추출, 토큰/UUID 조회 결과, 프록시 응답 코드를 로그로 남김.

포맷·토큰·채널/스레드/DM 공통점

공통

  • Slack SDK WebClient.chat_postMessage/chat_update 사용 또는 Slack response_url로 JSON POST
  • 텍스트 기본, 뉴스의 경우 Block Kit 사용
  • 링크 미리보기 비활성화 옵션을 종종 사용(unfurl_links, unfurl_media)

토큰 소스

  • rb8001 Slack 이벤트 응답: Gateway가 전달한 팀별 X-Slack-Bot-Token 사용
  • rb8001 DM: ROBEING_SLACK_BOT_TOKEN(없으면 SLACK_BOT_TOKEN)
  • Company-X 뉴스: COMPANYX_SLACK_BOT_TOKEN
  • skill-slack: 자체 SLACK_BOT_TOKEN

채널/스레드/DM

  • DM: channel=슬랙 유저 ID로 chat_postMessage (thread_ts 없이)
  • 채널: channel=채널 ID, 뉴스 포스트는 Block Kit로 다중 메시지 가능
  • 스레드: 멘션이 비스레드면 원본 ts를 thread_ts로 사용해 스레드 시작, 기존 스레드는 thread_ts 유지

에페메럴/진행상황

  • 인터랙티브 후속 안내는 response_url로 에페메럴 메시지(원본 미교체, 삭제하지 않음)

차이점 요약

전송 위치

  • 이벤트/인터랙티브 응답: rb8001 slack_handler가 채널/스레드로 직접 전송
  • 정기 DM: rb8001 dm_skill이 사용자 DM으로 전송
  • 뉴스 브로드캐스트: rb8001 news_posting_skill/startup_news_skill이 채널로 블록/텍스트 전송

스레드 처리

  • rb8001 slack_handler만 자동 스레드 생성/유지 로직 보유
  • skill-slack /send는 현재 코드에서 thread_ts를 파라미터로 받지만 미사용(전달 안 함)

토큰 선택

  • rb8001 이벤트 응답은 게이트웨이로부터 팀별 토큰 주입(워크스페이스별 분리)
  • DM/Company-X는 고정 환경변수 토큰 사용

엔드투엔드 흐름 요약

Slack 이벤트

  • Slack → Gateway(/slack/events) → DB에서 팀별 봇 토큰/UUID 조회 → rb8001(/api/slack/events) → rb8001가 채널/스레드/DM 판정 후 chat_postMessage

Slack 인터랙티브

  • Slack → Gateway(/slack/interactive) → rb8001 → 액션 처리(뉴스 게시 등) → response_url로 진행/완료 에페메럴 메시지

스케줄링/DM/뉴스

  • APScheduler → rb8001 스킬 → chat_postMessage 또는 chat_update 사용해 DM/채널 전송