2026-10-12
目的
趣味チャンネルを無作為に作成した結果、似たチャンネルが重複し、別のチャンネルに活動が集中する問題を減らす。既存のコミュニティを見つけやすくするため、Slackの趣味チャンネルを分類図上にマッピングする社内向けWebアプリを作る。
初期版の仕様
Slack APIから、名前が 趣味_ で始まるチャンネル一覧を取得する。公開・非公開チャンネルを対象とする。ただし、各利用者がアクセスできるチャンネルだけを表示する。全社員が閲覧・配置・タグ編集できる。背景画像には、音楽・ゲーム・創作・スポーツなどの領域を示す分類図を使う。境界は厳密にせず、複数の領域にまたがる趣味を間に配置できるようにする。チャンネルを選択し、画像上をクリックしてピンを配置する。ピンをドラッグして位置を調整し、共有保存する。名前・説明・タグで検索し、似た既存チャンネルを探せるようにする。ピンからチャンネルの説明・参加人数などの詳細を確認し、Slackへ移動できるようにする。新しく取得したチャンネルは「未配置一覧」に表示する。Slack一覧の更新は、初期版では手動の更新ボタンで行う。権限・セキュリティ
全員が編集できることと、非公開チャンネルを閲覧できることは分けて扱う。利用者ごとにSlack認証し、その利用者がSlack上でアクセスできるチャンネルだけを表示・編集できる設計とする。権限のない利用者には、非公開チャンネルの名前・説明・検索結果・ピンを返さない。座標は共通保存するが、表示・編集対象の権限チェックはサーバー側で行う。Slackトークンはサーバー側で扱い、ブラウザには渡さない。開発仕様
採用方針
インフラはAWS CDKで定義・デプロイする。Vercelは使用しない。初期版はサーバーレス構成とし、Slack一覧の手動更新・手動配置・手動タグ付けに絞る。以下のAWS構成・API・データモデルは開発案とする。既存のCDK運用規約に合わせ、認証方式やスタック分割を実装前に確定する。技術構成案
| 役割 | 技術・サービス | 方針 |
|---|
| フロントエンド | React+TypeScript | 静的SPAを第一候補とする。Next.jsを選ぶ場合は静的エクスポート可能な構成にする |
| Web配信 | S3+CloudFront | S3は公開せず、CloudFrontのOAC経由で配信する |
| API | API Gateway+Lambda(TypeScript) | Slack通信・権限確認・入力検証・保存をサーバー側で行う |
| データ保存 | DynamoDB | チャンネル情報・配置・タグ・更新バージョンを保存する |
| 背景画像 | 非公開S3 | 認証後に取得できる方式を採用する。必要に応じて短時間の署名付きURLを使う |
| 秘密情報 | Secrets Manager+KMS | Slackアプリの秘密情報を管理する。利用者別トークンの保存方式は別途確定する |
| ログ・監視 | CloudWatch | エラー・API利用状況を監視する。トークンや非公開チャンネル情報はログに出さない |
| IaC | AWS CDK(TypeScript) | 開発・本番の環境を分離し、権限は最小限にする |
画面・操作
左側に検索・チャンネル一覧・未配置一覧、右側に分類図とピンを表示する。一覧からチャンネルを選び、画像上のクリックで配置する。配置済みピンはドラッグで移動する。チャンネル詳細で説明・参加人数・タグ・Slackへのリンクを表示する。初期版はリアルタイム共同編集を必須にせず、保存後の再取得・更新ボタンで他の利用者の変更を反映する。背景画像の差し替えは確認付き操作とし、既存座標の扱いを明示する。データモデル案
| データ | 主な項目 |
|---|
| マップ | mapId、teamId、画像のS3キー、画像バージョン、更新日時 |
| チャンネル情報 | teamId、channelId、名前、説明、参加人数、公開区分、アーカイブ状態、取得日時 |
| 配置・タグ | mapId、teamId、channelId、x、y、tags、version、updatedBy、updatedAt |
| 利用者別アクセス情報 | teamId、userId、取得可能なchannelId、確認日時。有効期限を設け、共有キャッシュと分離する |
名前変更後も配置を維持できるよう、チャンネルはteamId+channelIdで識別する。座標は画像そのものの描画領域に対する0〜1の相対座標とする。余白・拡縮・スクロールを考慮し、範囲外の値を拒否する。Slack情報を更新しても、手動設定した座標・タグは上書きしない。一覧取得の失敗や権限による非表示を、チャンネル削除と見なさない。アーカイブ・削除・取得不能は区別して扱う。API案
| API | 用途 |
|---|
| GET /api/me | ログイン利用者と対象ワークスペースを確認する |
| GET /api/maps/{mapId} | 背景画像情報と、閲覧可能なチャンネルの配置を取得する |
| GET /api/channels | 閲覧可能なチャンネル一覧・検索結果を取得する |
| POST /api/channels/refresh | 利用者の権限でSlack一覧を取得・更新する |
| PATCH /api/maps/{mapId}/channels/{channelId} | 座標・タグを更新する |
| DELETE /api/maps/{mapId}/channels/{channelId}/placement | 配置だけを解除する。Slackチャンネルは削除しない |
すべてのAPIで認証と対象ワークスペースの検証を行う。読み取りだけでなく、更新・配置解除でもチャンネル単位の権限を確認する。更新時にversionを送り、DynamoDBの条件付き更新で競合を検知する。競合時は409を返し、再読み込みを案内する。座標・タグ数・文字数・画像サイズの上限を設定し、サーバー側で検証する。具体値は実装時に確定する。Slack連携・認証
アプリへのログインと、Slack APIを呼び出すためのOAuth認可を分けて設計する。Slackでログインできることだけを、チャンネル閲覧権限の証明にしない。会社の対象Slackワークスペースに利用を限定する。利用者別のSlackユーザートークンを使う方式を候補とし、OAuth構成・社内承認・必要スコープを実装前に検証する。公開チャンネルは channels:read、非公開チャンネルは groups:read を使用する。取得範囲はトークンのアクセス権に依存する。conversations.list を next_cursor がなくなるまで取得し、アプリ側で name.startsWith("趣味_") によって絞り込む。非公開チャンネルを含む共通Botトークンの取得結果を、そのまま全社員に配信しない。API応答・検索・未配置一覧・件数・ピン・エラー文に、権限のないチャンネル情報を含めない。非公開情報を含むAPI応答は共有CDNキャッシュに載せない。Slackトークンはブラウザやブラウザストレージに保存しない。利用者別トークンはサーバー側で暗号化し、参照権限を制限する。OAuthのstate検証、セキュアなセッション管理、CSRF対策を行う。トークン失効・権限喪失時は古いアクセス情報を無効化し、再認可を案内する。Slackの429応答はRetry-Afterに従って待機し、更新ボタンの連打を制限する。一部取得失敗を成功した全件更新として扱わない。開発・デプロイ
リポジトリ構成案:frontend/、backend/、infra/。フロント・API・CDKをTypeScriptで管理し、型チェック・Lint・自動テストを実施する。CDKで環境別設定を管理し、秘密情報はソースコード・Git・CDK出力に含めない。デプロイ前に cdk synth と cdk diff を確認する。本番DB・画像は保持方針を設定する。AWS Budgets等で費用を監視する。API利用量・ログ保持期間・画像容量に上限を設ける。予算通知は自動的な課金停止ではない点に注意する。初期版の受け入れ条件
2ページ以上のSlack一覧を取得しても対象チャンネルに漏れがない。趣味_ 以外のチャンネルを対象一覧に含めない。非公開チャンネルに参加している利用者には表示され、アクセス権のない利用者には検索・件数・直接APIアクセスでも情報が漏れない。チャンネル名変更後も座標・タグが維持される。ピンの配置・移動・解除・タグ変更が保存され、再読み込み後も保持される。画像の表示サイズを変えてもピン位置がずれない。同時更新で古い値が無言で上書きされず、競合を通知できる。Slackのレート制限・トークン失効・取得失敗時に、保存済み配置を失わない。実装前の確認事項
アプリのログイン方式とSlack OAuthの社内承認。既存のCDK構成・AWSアカウント・リージョン・ドメイン・CI/CD方式。背景画像の初期デザインとアップロード・差し替えの権限。更新履歴・復元機能を初期版に含めるかどうか。追加検討
共同編集で配置が崩れても戻せるよう、変更履歴・復元機能を検討する。必要になったら、似たチャンネル候補の提示を追加する。「盛り上がり」は参加人数だけでは判断せず、投稿履歴の取得・集計は初期版とは分けて検討する。運用方針
Slack上の重複チャンネル作成をアプリで禁止するのではなく、「新しく作る前に、まずマップで探す」導線として利用する。