どんなシステムにも、「経験者なら5秒で気づく罠」がある。GAS(Google Apps Script)とLINEの組み合わせには、それが1つある。知らなければ何時間でも詰まる。知っていれば回避に10分かからない。
最初の設計は、1時間後に崩れた
LINEのMessaging APIでは、ユーザーがメッセージを送ると、LINEのサーバーが指定したURLにPOSTリクエストを送信する。そのURLでリクエストを受け取り、GASが処理する——設計としては明快だ。
GASでWebアプリを公開する方法は知っていた。doPost関数を定義してデプロイすれば、外部からPOSTを受け取れる。LINEのWebhook URLにGASのURLを設定した。配信テストを実行した。
失敗、と返ってきた。
302が返ってきた
ログを見た。LINEはHTTPステータス302を受け取ったと記録していた。302は「移動しました」というリダイレクト応答だ。「成功」でも「失敗」でもない、「別の場所を見てください」という応答だ。
GASのWebアプリはGoogleの認証基盤を経由する仕組みになっている。LINEのサーバーのような未認証の外部リクエストに対して、Googleは認証ページへのリダイレクトを返す。これがGASとLINE Webhookの相性の悪さの根本だ。
LINEはリダイレクト先に追従しない。302を受け取った時点で配信失敗として扱い、通知は届かない。
公開設定を「全員」にしても変わらない。問題はGASの仕組みの深いところにある。
EC2を間に挟む
詰まっているときに有効な問いは「問題を分割できないか」だ。
問題は「LINEからのPOSTをGASが受け取れない」だった。これを「LINEからのPOSTを誰かが受け取る」と「その誰かがGASを呼ぶ」に分割した。
既にEC2(Amazon Linux上のFastAPIサーバー)が稼働していた。DX経営ゲームや予約システムがそこにいた。このEC2がプロキシとして機能すればいい。
LINE → EC2(FastAPI)→ GAS(doGet)→ 処理完了
EC2はLINEからのPOSTを問題なく受け取れる。EC2が受け取った後、GASをGETで呼ぶ。GASのdoGetはGETリクエストを普通に受け取れる。302は返ってこない。
即座に200を返すことの重要性
LINEのWebhookには「数秒以内に200を返すこと」というルールがある。遅れると配信失敗とみなされてリトライが来る。GASの処理には数秒かかることがある。
EC2での実装はこうなる。
LINEからPOSTが届く。ボディからユーザーIDとメッセージを取り出す。LINEには即座に200を返す。それと並行して、バックグラウンドでGASのURLをhttpxで非同期呼び出しする。
返答と処理を切り離す。これは焼き菓子店や予約システムで確立したパターンだ。同じ問題が出たとき、同じ解法が使えた。
GETで情報を渡す設計の判断
GASをGETで呼ぶとき、生徒コードやLINEユーザーIDをクエリパラメータとして渡す。URLに情報が載る。「セキュリティ的に大丈夫か」と考えた。
生徒コードは「J001」のような記号だ。単独では個人を特定できない。LINEユーザーIDはLINE内部の識別子で、外部から悪用できるものではない。通信はHTTPS(暗号化済み)だ。
これらを踏まえて、許容と判断した。もし氏名や電話番号を渡す設計だったら判断は変わる。渡す情報の性質に応じた判断が必要だ。
この罠を知っていれば
GASとLINEの組み合わせで詰まったとき、まずこれを疑う。LINEからのWebhookはGASに直接当てない。EC2なりCloud Functionsなり、中継サーバーを挟む。それを最初のルールにしておけば、入退室管理システムの実装はもっと早く終わっていた。
同じことを焼き菓子店で学び、予約システムで確認し、入退室管理システムで当然のこととして設計した。知識は経験の数だけ深くなる。
*次回は「Google SheetsのDate型に気づかず2日詰まった話」*
*シンプルシステム株式会社 代表 伊藤勝彦*