全国オンライン対応受付 平日 9:00-18:00
お問い合わせ

開発記録 / toi-toi-toi制作記 / Vol.13 toi-toi-toi制作記 Vol.2

記事 03

カレンダーURLが長すぎる問題——EC2リダイレクト方式で解決した

オーナー通知のLINEを確認してもらったとき、オーナーからメッセージが来た。

2026-06-07 公開

オーナー通知のLINEを確認してもらったとき、オーナーからメッセージが来た。

「リンクがめちゃ長いんですけど」

LINEの画面でURLが折り返しながら延々と続く。スクロールしてもまだURLが続く。「カレンダーに追加」と書いたつもりのリンクが、読み物になっていた。


なぜ200文字を超えるか

Google Calendar URLにパラメータを付けると長くなる。原因は日本語タイトルだ。

「片づけレッスン 田中花子様」という30文字前後の文字列は、URLエンコードすると90文字以上になる。日本語1文字が%XX%XX%XXという形式になるからだ。それにベースURL・dates・actionパラメータが加わると、合計で200〜300文字になる。

https://calendar.google.com/calendar/render?action=TEMPLATE
&text=%E3%80%90%E4%BA%88%E7%B4%84%E3%80%91%E3%81%93%E3%82%93
%E3%81%BE%E3%82%8A%E6%B5%81%E7%89%87%E3%81%A5%E3%81%91%E3%83...
(まだ続く)

「detailsパラメータを削除する」という対策を試みたが、タイトルだけでも日本語が含まれる以上、根本的な解決にはならなかった。


EC2に短縮URLの仕組みを作った

既存のEC2(FastAPIサーバー)に、リダイレクト機能を追加することにした。URLを短くするのではなく、「短いURLから長いURLへのリダイレクト」を作る。

設計はシンプルだ。

予約が確定すると、GASが長いGoogle Calendar URLを生成する。GASがEC2に「booking_id→長いURL」のペアをPOSTする。EC2がそのペアをin-memoryのdictに保存する。LINEに送るURLはhttps://yoyaku.example.jp/gcal/{booking_id}という短い形にする。オーナーがそのURLをタップすると、EC2が長いURLに302リダイレクトする。

gcal_store: dict = {}

@app.post("/api/gcal/store")
async def store_gcal_url(request: Request):
    body = await request.json()
    bid = body.get("booking_id", "")
    url = body.get("gcal_url", "")
    if bid and url:
        gcal_store[bid] = url
    return {"ok": True}

@app.get("/gcal/{booking_id}")
async def redirect_gcal(booking_id: str):
    url = gcal_store.get(booking_id)
    if not url:
        return JSONResponse({"error": "not found"}, status_code=404)
    return RedirectResponse(url)

LINEに届くURLは「https://yoyaku.example.jp/gcal/abc123」という22文字になる。


in-memoryで永続化しないという決断

gcal_storeはPythonのdictだ。EC2のプロセスが再起動すると、保存していたURLが全て消える。

永続化しない設計にした理由がある。

このURLが使われるタイミングを考えた。予約確定と同時にLINEで通知が届く。オーナーはその通知を見てすぐにカレンダーリンクをタップする。「後で使う」という使い方よりも、「すぐ使う」が圧倒的に多い。

EC2が再起動するのは稀だ。デプロイ時か障害時だ。その稀なタイミングで「さっき届いたカレンダーリンクが開けない」という問題が起きたとして、致命的かどうか。オーナーが直接スプレッドシートを見て日時を確認すれば済む話だ。

リスクに対してコストをかけるかどうかの判断は、実際の被害規模で考える。Redisを追加するコストと、稀に起きる「リンクが開けない」問題のコストを比べたとき、in-memoryで十分と判断した。


GAS側のフォールバック

EC2へのURL登録が失敗したとき(EC2が落ちている・ネットワーク障害)のために、フォールバックを入れた。

try {
  UrlFetchApp.fetch('https://yoyaku.example.jp/api/gcal/store', {
    method: 'post',
    contentType: 'application/json',
    payload: JSON.stringify({ booking_id: bookingId, gcal_url: longUrl }),
    muteHttpExceptions: true,
  });
  gcalUrl = 'https://yoyaku.example.jp/gcal/' + bookingId;
} catch(e) {
  gcalUrl = longUrl;
}

EC2が落ちていたら、長いURLをそのままLINEに送る。「めちゃ長い」という体験にはなるが、リンク自体は機能する。カレンダーに追加できなくなるよりはいい。


応用できるパターン

EC2にリダイレクトストアを置いてLINEに短いURLを送る、このパターンは予約システム専用ではない。LINEで長いURLを送る必要があるあらゆる場面に使える。

外部のURL短縮サービスを使う方法もある。しかし外部サービスへの依存が増えると、そのサービスが終了したときに全リンクが死ぬ。自前のEC2がある場合は、そこに機能を追加する方が制御できる。


*次回は「Stripe Payment Links——個人コーチのオンライン決済をどう設計するか」*

*シンプルシステム株式会社 代表 伊藤勝彦*