こんにちは、PR TIMESでインターンをしている工藤(@k8035004287922)です。
今回は、社内の一部部署で必須運用されていたGmail送信前の誤送信確認用Chrome拡張を、社内要件に合わせて内製した取り組みを紹介します。
背景
社内の一部部署では、誤送信を防ぐChrome拡張を必須インストールして運用していました。
一方で、Chrome拡張はブラウザ上で強い権限を取り得るため、社外製の拡張機能を業務上必須にすること自体が、セキュリティ上の懸念になり得ます。
そこで今回は、誤送信防止という目的は維持したまま、セキュリティ上の懸念を払拭するために内製化することにしました。
一般に、内製開発では保守コストが課題になりがちです。一方で、近年のAIの発展により、実装・テスト整備・改修のハードルが下がり、継続的にメンテできる見通しが立ったことも、内製化に踏み切れた大きな理由でした。
目指す仕様
本取り組みでは、メール送信時の確認をより確実にしつつ、日常業務の使いやすさを損なわない仕組みを目指しました。
送信前にワンステップ確認を挟むことで、利用者が落ち着いて内容を見直せるようにしながら、普段の操作フローに無理なく馴染むことを重視しました。
設計方針
メールという機微な情報を扱うため、以下を設計原則にしました。
- Manifest V3
- コンテンツスクリプト中心の単純な構成
- 外部通信なし(fetch / XHR / beacon を使わない)
- データ保存なし(メール内容をストレージに残さない)
- 最小権限(
host_permissionsはhttps://mail.google.com/*のみに限定)
また、GmailはSPAでDOM変化が多いため、初回パッチだけに依存せず、MutationObserver で監視しながら都度アタッチする構成にしています。
開発内容
1. 送信導線のフック
送信操作の取りこぼしを防ぐために、キャプチャフェーズでイベントを監視しています。
document.addEventListener("click", onClickCapture, true);
document.addEventListener("keydown", onKeydownCapture, true);
document.addEventListener("focusin", onFocusInCapture, true);クリック送信だけでなく、Ctrl/Cmd+Enter や分割メニュー経由の送信も同じ確認フローに集約しています。
2. Compose判定と宛先抽出
GmailのDOMは固定ではないため、送信ボタン周辺から親要素をたどって compose root を推定し、To/CC/BCC/件名 を取得しています。
宛先抽出では、入力フィールドだけでなくGmail上のチップ要素も対象にし、重複メールアドレスを除外した上でドメイン単位にグルーピングして表示します。
3. 確認モーダルと送信再実行
確認モーダルでは、件名と宛先をユーザーが順に確認し、全項目がチェック済みになるまで送信ボタンを有効化しないようにしました。
また、送信日時指定のようなメニュー送信でも、確認後に元の送信オプションを再実行できるようにし、Gmailの既存UXを極力崩さない形にしました。
開発時のテスト方針
型安全性の担保と不具合の早期検知の観点から、以下のコマンドで確認できるようにしました。
npm run typecheck(型チェック)npm run test:e2e(PlaywrightによるE2Eテスト)
なお、E2Eは実環境のGmailではなく、Gmailに模したHTMLを使用しています。DOM変化やログイン状態の影響を受けにくくし、テストの再現性を高めるためです。
具体的なE2Eテストとしては、43ケースのテストを実施しており、例えば次を検証しています。
- 確認モーダルが表示されること
- 全項目確認後のみ送信可能になること
- Ctrl/Cmd+Enter でも確認フローに入ること
- 送信メニュー(送信日時指定 / 今すぐ送信)経由でも挙動が崩れないこと
- To空欄や件名空欄時に安全側の挙動として送信を停止
CI(GitHub Actions)では、npm run typecheck / npm run build / npm run test:e2e を実行し、型チェック・ビルド・E2Eテストが常に通る状態を維持できるようにしました。あわせて、GitHub Actions上で npm run test:e2e が失敗した際には、発生箇所のスクリーンショットなどのPlaywright Artifactsを保存し、原因を追いやすくしました。
公開方法
この拡張は社内の業務フローに組み込むことが目的で、社外への配布・公開は想定していません。
そこで、Chrome ウェブストアでは社内限定公開(ドメイン限定)で配布する形にしました。
公開範囲を社内に絞ることで、将来の仕様変更に追従しやすくしつつ、審査要件が厳しくなるなど外部公開特有の負担増も避けやすくなります。
参照(公開手順):
苦労した点
特に苦労した点は、既存拡張からの置き換えに伴う心理的ハードルが高かったことです。すでに必須運用されているものの代替を作るため、「壊したら現場が困る」「事故が起きたら責任が大きい」というプレッシャーがありました。機能要件だけでなく、レビュー・テスト・CI・公開手順を揃えて、“安心して置き換えられる状態”を作ること自体が一つの課題だと感じていました。
そこで、送信導線の網羅と、「不確実なときは送らない」という方針を仕様に落とし込み、E2EテストとCIで継続的に担保できる状態を整えることで、この課題を乗り切りました。
もう一つの課題は、これまで拡張機能開発の経験がほとんどなかったことです。当初、この拡張機能の開発を引き受けたときは、「生成AIにプロンプトを一つ渡せば簡単にできるだろう」と考えていました。しかし、実際にはそう簡単ではありませんでした。
Chrome拡張には Manifest V3 による制約や、コンテンツスクリプトならではの実装上の前提があり、さらに今回は Gmail という DOM 変化の多い画面上で安定して動かす必要がありました。そのため、単にコードを生成するだけでは不十分で、「どのイベントをどこで捕まえるべきか」「どこまでを安全側の仕様にするか」「Gmailの既存UXをどう崩さずに組み込むか」といった設計判断が何度も必要になりました。
この課題については、AIの使い方を工夫し、適切に役割分担することを意識することで乗り越えることができました。
まとめ
誤送信防止の仕組みを、社内要件に合わせたGmail送信前確認用Chrome拡張として内製化し、現在は全社展開することができました。
本取り組みでは、単に既存の外部拡張を置き換えるのではなく、最小権限・外部通信なし・データ保存なしという方針のもとで設計を行い、安心して運用できる形を目指しました。加えて、E2EテストやCI、公開手順まで含めて整備することで、継続的にメンテナンスできる状態も構築しました。
また今回の経験を通じて、AIを活用した開発では、いきなりコード生成に頼るのではなく、先に要件や開発プロセスを言語化したうえで活用することの重要性を実感しました。

