--- id: wiki-2026-0508-slack-bot-development title: Slack Bot Development category: 10_Wiki/Topics status: verified canonical_id: self aliases: [Slack App, Slack Integration, Bolt SDK] duplicate_of: none source_trust_level: A confidence_score: 0.9 verification_status: applied tags: [slack, bot, integration, automation] raw_sources: [] last_reinforced: 2026-05-10 github_commit: pending tech_stack: language: typescript framework: bolt-js --- # Slack Bot Development ## 매 한 줄 > **"매 Slack workspace 에 자동화/통합 logic 을 추가하는 app 개발"**. 매 2014 Slack API 출시, 매 2020 Bolt SDK 정식, 매 2026 현재 Block Kit 2.0 + AI Assistant App + Slack Connect 가 표준. Event-driven + interactive UI 의 양축. ## 매 핵심 ### 매 App 구성요소 - **Bot user**: identity for messaging. - **Event subscriptions**: 매 message, reaction, app_mention 등. - **Slash commands**: `/deploy`, `/oncall`. - **Interactive components**: buttons, modals, select menus. - **Block Kit**: rich UI JSON. - **OAuth scopes**: `chat:write`, `channels:read`, etc. ### 매 host 모델 - **HTTP (request URL)**: AWS Lambda, Cloudflare Worker. - **Socket Mode**: WebSocket — internal tools, no public URL. ### 매 응용 1. CI/CD notification + interactive deploy approval. 2. On-call escalation bot. 3. AI assistant (RAG over Slack history). 4. Survey / standup automation. 5. Customer support triage. ## 💻 패턴 ### Bolt SDK basic ```ts import { App } from '@slack/bolt'; const app = new App({ token: process.env.SLACK_BOT_TOKEN, signingSecret: process.env.SLACK_SIGNING_SECRET, socketMode: true, appToken: process.env.SLACK_APP_TOKEN, }); app.message('hello', async ({ message, say }) => { await say(`Hi <@${message.user}>!`); }); await app.start(); ``` ### Slash command + modal ```ts app.command('/deploy', async ({ ack, body, client }) => { await ack(); await client.views.open({ trigger_id: body.trigger_id, view: { type: 'modal', callback_id: 'deploy_modal', title: { type: 'plain_text', text: 'Deploy' }, submit: { type: 'plain_text', text: 'Go' }, blocks: [ { type: 'input', block_id: 'env', element: { type: 'static_select', action_id: 'sel', options: [{text:{type:'plain_text',text:'staging'},value:'stg'}]}, label: { type: 'plain_text', text: 'Environment' } } ] } }); }); app.view('deploy_modal', async ({ ack, view, client, body }) => { await ack(); const env = view.state.values.env.sel.selected_option!.value; await triggerDeploy(env); await client.chat.postMessage({ channel: body.user.id, text: `Deploying ${env}...` }); }); ``` ### Event subscription (app_mention) ```ts app.event('app_mention', async ({ event, client }) => { const ai = await callLLM(event.text); await client.chat.postMessage({ channel: event.channel, thread_ts: event.ts, blocks: [{ type: 'section', text: { type: 'mrkdwn', text: ai }}], }); }); ``` ### Interactive button → action ```ts app.action('approve_deploy', async ({ ack, body, client }) => { await ack(); const action = body as any; await client.chat.update({ channel: action.channel.id, ts: action.message.ts, text: `Approved by <@${action.user.id}>`, blocks: [] }); await triggerDeploy(action.actions[0].value); }); ``` ### Block Kit message ```ts const blocks = [ { type: 'header', text: { type: 'plain_text', text: '🚨 Production Alert' }}, { type: 'section', fields: [ { type: 'mrkdwn', text: '*Service:* api' }, { type: 'mrkdwn', text: '*Severity:* P1' }, ]}, { type: 'actions', elements: [ { type: 'button', text: {type:'plain_text',text:'Ack'}, action_id: 'ack', style: 'primary' }, { type: 'button', text: {type:'plain_text',text:'Page'}, action_id: 'page', style: 'danger' }, ]} ]; await client.chat.postMessage({ channel: '#alerts', blocks, text: 'Alert' }); ``` ### Signature verification (HTTP mode) ```ts import crypto from 'crypto'; function verifySlack(req: Request, secret: string): boolean { const ts = req.headers.get('x-slack-request-timestamp')!; const sig = req.headers.get('x-slack-signature')!; const body = req.body; const base = `v0:${ts}:${body}`; const expected = `v0=${crypto.createHmac('sha256', secret).update(base).digest('hex')}`; return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(sig)); } ``` ### Rate limit handling ```ts app.error(async ({ error, ...rest }) => { if (error.code === 'slack_webapi_rate_limited') { await new Promise(r => setTimeout(r, error.retryAfter * 1000)); return; } console.error(error); }); ``` ## 매 결정 기준 | 상황 | Approach | |---|---| | Internal tool, no public URL | Socket Mode | | Public app distribution | HTTP + signed requests | | High-volume events | Lambda/Workers + queue | | Stateful conversation | Bolt + Redis state | | AI assistant | Anthropic SDK + Bolt event handler | **기본값**: Bolt-js + Socket Mode for internal, HTTP + Cloudflare Workers for public. ## 🔗 Graph - Adjacent: [[Webhooks]] · [[OAuth]] ## 🤖 LLM 활용 **언제**: Block Kit JSON 생성, slash command boilerplate, signature verify code. **언제 X**: workspace OAuth flow debugging (회사 별 admin policy 가 다양). ## ❌ 안티패턴 - **No signature verify**: spoofed requests. - **Sync long-running**: 3s timeout — use ack() + async work. - **Hard-coded channel ID**: env var 의 X. - **No retry on send**: rate limit 시 message loss. ## 🧪 검증 / 중복 - Verified (Slack API docs 2026, Bolt-js v3). - 신뢰도 A. ## 🕓 Changelog | 날짜 | 변경 | |---|---| | 2026-05-08 | Phase 1 | | 2026-05-10 | Manual cleanup — Slack bot 2026 patterns |