はじめに
SORACOM GPSマルチユニットで収集し、kintoneに蓄積されたGPSデータを、Google Apps Script(GAS)とLeaflet.jsを使って地図上に可視化するアプリをつくりました。
目的は、出かけた際の現在地の家族への共有。そして、Claude Codeを使ってみたかったことです。
やったことをClaude Codeにまとめてもらったので、ハマりポイントは、AI視点です。また、Claude(Proを使っています)のリミットが来たので、まとめからのこの記事の仕上げはGeminiと作業しています。
システム構成
- デバイス: SORACOM GPSマルチユニット
- データ保存: kintone(移動履歴アプリ + 道の駅訪問履歴アプリ + 除外エリアアプリ)
- バックエンド: Google Apps Script (Web App)
- フロントエンド: Leaflet.js
GPSの移動履歴アプリは、以前にこちらの記事で紹介したものです。
その後、道の駅マスタアプリを整備し、移動履歴が道の駅近辺と判定された場合は、道の駅訪問履歴アプリに記録するようにしました(記事未作成)。今回はその情報を、アイコン表示の判定に利用しています。
除外エリアアプリは、自宅周辺などを登録しておき、家族への共有には不要なデータの除外に使います。記録が集中して見づらいことを回避できるのと、この地図情報を他の方に見せることがあった場合のプライバシーの配慮した感じにも仕上がっています。
🗺️ 技術選定:なぜ Leaflet.js + GAS だったのか?
Google Mapのイメージしかなかったのですが、そういうのを作るのにどういう方法があるのかを相談したところ、Claude Codeの提案から、以下のメリットからLeaflet.jsを選択しました。
- APIキー設定が不要: 複雑なコンソール設定やカード登録なしですぐに動かせる。
また、公開するURLを設定する構成で手軽なものとして、GAS以外に、下記も候補として挙がりましたが、クリアすべき点もあったので、一番手軽なGASにしました。
- Lambda:HTMLの公開にS3のホスティングが必要
- GitHub Pages:プライベートリポジトリでの公開には有料プランが必要
🤝 AI(Claude Code)との役割分担
AIができること、人間がすべきことを明確に分けることで、スムーズな開発を実現しました。
Claude Code が担当したこと
-
ロジック実装:
Code.gs(サーバー側)とindex.html(クライアント側)の記述・修正。 -
CLI操作:
clasp pushによる反映、Gitによるバージョン管理。 - 技術調査: 詰まった際の原因調査とワークアラウンドの提案。
人の手が必要だったこと
-
認証と初期設定:
clasp loginやプロジェクト作成(権限関連)。 -
GASのデプロイ(重要):
clasp deployを使うとデプロイ種別が「API」となりWebアプリとして動作しなくなるため、GASエディタの「デプロイを管理」から手動でバージョン更新を実施。 - 物理・実機確認: PCでは再現できないスマホ特有の挙動の確認と、実測値(画面幅など)のフィードバック。
また、kintoneアプリの作成・編集は、今回はあまり操作がなかったので、特に設定せずに手動としました。
🛠 開発中に遭遇した「罠」と解決策
以前の別のAIツール(Antigravity)を使った時も、今回のClaude Codeでも、kintoneに関することは、一般的なことを試してから、うまくいかないとわかってから、APIの仕様の詳細を確認して結果が出るように改修するような傾向を感じます。ほかのツールについても、自分が知らないから気づくことができませんが、そんな感じなのかもしれません。
1. GAS Webアプリの「viewport 980px固定」問題
スマホで表示しても window.innerWidth が常に 980 を返すため、CSSのメディアクエリが機能しません。
-
解決策:
window.screen.widthを使ってデバイスの実際の幅を取得し、JavaScript側でスタイルを動的に書き換えることで解決。
スマホ(Pixel 10a)だと、表示が小さすぎて見づらかったので、大きめの表示に設定したかったのですが、なかなか変わらず…。モバイル判定がうまくいかず、Claude Codeが検証用のコードを入れてくれて、画面上にWidthのピクセル数とモバイル判定(true/false)を表示させて、それをAIに伝えるという試行錯誤をしながらの実装となりました。
2. kintone REST API の offset 仕様
URLパラメータに &offset=500 と書いても無視されます。
-
解決策: クエリ文字列(
queryパラメータ)の中にlimit 500 offset 500と記述。
3. 大量データのフィルタリング戦略
約300件の「道の駅」マッチングをkintoneのIN句で行うと、URL長制限でエラーになります。
- 解決策: 道の駅データを全件取得し、JavaScript上のオブジェクト(Map)でマッチングを行う方式に変更。
4. Leaflet マーカーの「フラッシュ」現象
setView() の後に setZoom() を呼ぶと、描画時にマーカーが一瞬大きく表示されるアニメーションが起きます。
-
解決策:
setView([lat, lng], zoom)のように、ズームレベルを第2引数に含めて同時に指定。
5. 非同期通信による「即時スピナー表示」
doGet() 内で全データを処理すると、画面が表示されるまで待機時間が発生します。
-
解決策:
-
doGet()は空のHTML(スピナー付き)を即レスポンス。 - クライアント側から
google.script.run.getPoints()を非同期で呼び出す。
これにより、ページを開いた瞬間にローディングが表示される快適なUXになるかと思いきや、数秒の待ち時間がありました。GASのコールドスタートによるものということで、設置の仕方から考えてやむなしということのようです。
-
📐 設計上の決断
- セキュリティ: APIトークン等はスクリプトプロパティで管理。
-
パフォーマンス:
CacheService(5分) を活用し、kintone APIのコール数を抑制。 - プライバシー: 除外エリアアプリに登録したエリア(自宅など)のGPS履歴データは除外してからクライアントへ送信。
GPSの履歴データは5分に1回追加されます。5分に1度は最新になってほしいので、それより長い時間、キャッシュを残しておきたくはないのです。キャッシュの件を提案されて、なるほど…とは思ったのですが、家族が現在位置を確認するとして、5分より短い頻度では見ないと思うので、今の用途では表示の高速化にもAPIの節約にも意味がないかもな~と思います。
まとめ
AI(Claude Code)を活用することで、面倒な環境整備やエラー調査のスピードが格段に上がりました。一方で、GASのデプロイ仕様や実機での表示など、「現場でしかわからない制約」への対応は、依然として人間の重要な役割です。
技術選定においても、AIの提案から「APIキー不要」「カスタマイズ性」といったメリットを人間が評価し、納得感を持って進めることが成功の鍵でした。
というのがAIのまとめ。
私としても、自分でかこうと思ったらこんなスピードでできないものができている驚きは大きいものの、できそうだなと知っている範囲しかできてないので、聞きかじりでも知ってることを増やしておくアンテナの感度が必要だなと思いました。
あと、AIだと、今日の分は色を変えるとか、最新の分は時間の経過で別の色でグラデーションさせるとか、無駄に凝ったことも簡単に頼めるので、やるやらないのラインの引き方が変わったなと思います。何もかも、いままでより高機能側に寄るんだろうな。
