TimeTreeの二重管理に疲れ、Googleカレンダー裏側の「パートナーと予定を選んで共有できるWebアプリ」をAIと自作した話
はじめに:カレンダー二重管理の苦しみ
パートナーや家族との予定共有に TimeTree などの共有カレンダーアプリを使っている人は多いと思います。
しかし、普段から Googleカレンダー をメインに使っている人にとって、以下の問題に直面したことはないでしょうか?
-
二重管理のめんどくささ:
自分の予定をGoogleカレンダーに入れた後、共有したい予定だけをTimeTreeにもう一度手作業で入力する。コピペの手間と転記ミスが多発する。 -
Googleカレンダー全共有の気まずさ:
かといって、自分のGoogleカレンダーをそのまま相手に共有すると、仕事・研究・バイトのシフトや私用など、「全部見せるのはちょっと恥ずかしい・ノイズになる」 予定まで筒抜けになってしまう。 -
相手の導入ハードル:
「相手に新しくGoogleアカウントを作らせる」「複雑な共有設定をさせる」のは地味にハードルが高い。
そこで、「本体は使い慣れた自分のGoogleカレンダーをそのまま使い、予定ごとに公開範囲(自分だけ/パートナー/家族)を選べるWebアプリ」 を作ることにしました。
しかも、Claude と Gemini(Antigravity)という2つのAIとペアプログラミングを行い、構想から本番デプロイ、TimeTree風のタイムラインやWeb Push通知の実装まで、わずか半日で完成しました。
今回はその設計思想、完全無料のサーバーレス構成、そしてAI開発のリアルなプロセスを紹介します。
出来上がったもの
- UI: TimeTreeライクな月カレンダー & 1週間の空きコマが分かるウィークリータイムライン & 1日のタイムスケジュール。
- 公開範囲の制御: 予定作成時に「パートナーに公開」「家族に公開」をチェックするだけ。
-
相手はログイン不要: URLに含まれる専用の安全な鍵(
?k=...)だけで権限を識別。 - PWA & Web Push通知: iPhone(Safariでホーム画面追加)やAndroidで、相手が予定を入れたらスマホにプッシュ通知が届く。
- Googleカレンダー双方向同期: Googleカレンダー側で色を変えてもアプリ側に即時反映。
【画面イメージ(週表示タイムライン)】
┌──────────────────────────────────────────────┐
│ ‹ 10月11日 – 10月17日 › [今日] [月][週][日] 🔔 │
├──────────────────────────────────────────────┤
│ 終日: [旅行の準備] │
├──────┬──────┬──────┬──────┬──────┬──────┬────┤
│ │ 日 │ 月 │ 火 │ 水 │ 木 │ 金 │
│ │ 11 │ 12 │ 13 │ 14 │ 15 │ 16 │
├──────┼──────┼──────┼──────┼──────┼──────┼────┤
│10:00 │ │ │ │ │ │ │
│12:00 │ │[映画]│ │ │ │ │
│14:00 │ │ │ │[カフェ] │ │
│18:00 │[買い物] │ │ │ │ │
│20:00 │ │ │ │[夕食] │ │ │
└──────┴──────┴──────┴──────┴──────┴──────┴────┘
アーキテクチャの工夫:DBを持たない「サーバー代0円」構成
個人開発で一番避けたいのは「サーバー代」と「DB管理の手間」です。
今回は以下のように割り切った設計にしました。
[クライアント(ブラウザ/PWA)]
│
▼ (HTTPS / ?k=秘密鍵)
[Cloudflare Workers] (バックエンド & 静的ホスティング / 無料)
│
├─► [Cloudflare KV] (OAuthトークン & Push購読情報の保存)
│
└─► [Google Calendar API] (予定データの保存先 = DB不要!)
1. Googleカレンダーの「隠しプロパティ」をDB代わりにする
Google Calendar APIには、一般にはあまり知られていない extendedProperties.private(非公開の拡張プロパティ) という機能があります。
ここにメタデータをJSONライクに埋め込めます:
{
"summary": "ランチ",
"start": { "dateTime": "2026-10-14T12:00:00+09:00" },
"extendedProperties": {
"private": {
"audience": "partner",
"createdBy": "owner"
}
}
}
- オーナー(自分)が見るとき:全予定を取得して表示。
- パートナーが見るとき:
audienceにpartnerが含まれる予定だけをWorker側でフィルタリングして返却。 - Googleカレンダー公式アプリで直接作った予定(プロパティなし):自動的に自分だけの非公開扱い。
これにより、データベース(RDBMS)を一切契約することなく、Googleカレンダー自体をセキュアなDBとして利用できます。
2. URLの鍵(?k=...)によるロール管理
相手に「OAuthログインしてアカウント連携して…」と頼むのはハードルが高すぎます。
そこで、Cloudflare Workers Secretsに保存したランダムな長文トークンをURLパラメータに付与する方式を採用しました。
-
?k=<OWNER_KEY>: 自分用。全予定の閲覧・作成・編集・削除 -
?k=<PARTNER_KEY>: パートナー用。「パートナーに公開」予定の閲覧、追加、自身が作った予定の編集 -
?k=<FAMILY_KEY>: 家族用。閲覧専用
パートナーにはLINE等で https://xxx.workers.dev/?k=... を送り、「Safariで開いてホーム画面に追加してね」と伝えるだけで導入完了です。
AIエージェント(Claude ⇄ Gemini)との開発リレー
今回の開発で一番面白かったのが、2つのAIエージェントによるリレー開発でした。
1. Claude:最小限の骨組みとGitHubリポジトリの爆速立ち上げ
最初にWeb版Claudeでコンセプトを壁打ちし、Cloudflare WorkersのコードとHTML/CSS/JSのベースを生成、GitHubリポジトリへの初期コミットまでを完了。
2. Gemini(Antigravity):ローカル引き継ぎから難所突破・機能拡張
その後、手元の開発環境(Antigravity)でGeminiに「Claudeでやってた共有カレンダーの件を引き継いで進めて」と一言投げてバトンタッチ。
Geminiはリポジトリのクローンから、Cloudflareアカウントの特定、GitHub SecretsやOAuth認証情報の登録、初回デプロイまでを自律的に実行してくれました。
実際に直面したトラブルとリアルタイム解決
AIとのペアプロで特に助かったのが、ブラウザ特有のハマりどころのデバッグです。
-
TDZ(Temporal Dead Zone)エラー:
関数宣言と変数初期化の順序不備によるReferenceError: Cannot access 'pad' before initializationが発生。エージェントがブラウザサブエージェントでDOMエラーを検知し、数秒でコードを修正して再デプロイ。 -
プライバシー保護ブラウザ(Brave)の壁:
Chromeでは動くのにBraveで動かない現象が発生。Braveの強力なキャッシュとカスタムヘッダー(x-key)の干渉を疑い、キャッシュバスター(クエリパラメータ)とURLフォールバックを実装して完全対応。
その後、「タイムラインが欲しい」「色も選びたい」「祝日も」「週表示も」「繰り返しも」とリクエストを投げるたびに、数分単位で機能が追加されデプロイされていきました。
まとめ:個人開発は「既存インフラのハック」が一番手軽で楽しい
- 既存の巨大インフラ(Googleカレンダー)のAPIをハックする
- Cloudflare Workers(サーバーレス)でコストと管理をゼロにする
- AIエージェントに実装・デバッグ・デプロイを伴走させる
この3つを組み合わせることで、「休日の午後」だけで実用に耐えるパートナー共有Webアプリを作り切ることができました。
パートナーとのカレンダー共有に悩んでいる方は、ぜひ「Googleカレンダーを裏側にした専用フロントエンド自作」、試してみてはいかがでしょうか!