管理者は管理画面を開いたまま、インタビューが終わるのを待っている。
AiInterviewの管理者は、事業者がLINEでインタビューを受けている間、管理画面でその進捗を確認したい。いつ終わったか、記事が生成されたか。それを知るために画面をリロードし続けるのは不便だ。
自動で更新してほしい。
WebSocketという選択肢がある。サーバーとブラウザが双方向の接続を保ち、サーバーからプッシュで通知できる。リアルタイム性が高い。
しかし実装コストが高い。FastAPIにWebSocketのエンドポイントを追加して、接続を管理して、インタビュー完了イベントを接続中の管理画面に送る。セッション管理とWebSocket管理が絡み合う。
ポーリングにした。
管理画面のJavaScriptが一定間隔でAPIを叩いて、状態を確認する。シンプルな実装で目的を達成できる。
ポーリングの間隔は10秒にした。
インタビューは平均で10〜20分かかる。10秒ごとに更新すれば、完了してから10秒以内に管理画面が変わる。体感的に「すぐ反映された」と感じる範囲だ。
間隔が短すぎるとサーバーへのリクエスト数が増える。10秒は間隔と即時性のバランスとして妥当だと判断した。
let pollingTimer = null;
function startPolling() {
pollingTimer = setInterval(async () => {
const res = await fetch('/api/interviews');
const data = await res.json();
renderTable(data.interviews);
}, 10000);
}
function stopPolling() {
if (pollingTimer) clearInterval(pollingTimer);
}
管理画面を開いたときにポーリングを開始し、閉じたときに止める。visibilitychangeイベントを使って、タブが非表示になったときはポーリングを一時停止する。不要なリクエストを減らす。
表示の更新は差分を見て行う必要があった。
テーブルを毎回全件再描画すると、ユーザーが行を選択していた状態がリセットされる。スクロール位置も戻る。
対策として、新しいデータと現在表示されているデータを比較して、変更があった行だけを更新するようにした。
インタビューのIDをキーにして、状態(in_progress・completed・article_generated)が変わった行を検知する。変わった行のセルだけを書き換える。
function renderTable(interviews) {
interviews.forEach(interview => {
const row = document.querySelector(`tr[data-id="${interview.id}"]`);
if (!row) {
appendNewRow(interview);
} else {
updateRowIfChanged(row, interview);
}
});
}
状態がarticle_generatedになったとき、「記事を確認する」ボタンをその行に表示した。
ポーリングが更新を検知して、ボタンが自然に現れる。管理者が手動でリロードしなくても、インタビューが終わった事業者の行に「確認」ボタンが出る。
「さっきなかったボタンが出た」という変化は目を引く。通知音よりも、静かで確実な案内になる。
ポーリングは古い技術だ。
WebSocketやServer-Sent Eventsの方がリアルタイム性も効率も高い。しかし「管理者が一人、画面を開いている」という用途では、ポーリングで十分だ。
技術の選択は要件の複雑さに合わせる。シンプルな実装で要件を満たせるなら、それが正解だ。複雑な技術は複雑な問題のためにある。
次回は、DX経営ゲームの検証スクリプトによる品質管理の仕組みを書く——ではなく、本稿がAiInterviewシリーズの最終回となる。次回からは、1cto.jpの記事プラットフォームとして98本が揃った今、Stripeの価格設定と購入フローの完成について書く予定だ。
シンプルシステム株式会社 代表 伊藤勝彦