ランダム郵便番号 をリリースしました!


日本全国の郵便番号データを使って、ボタンひとつでランダムに郵便番号と住所を選んでくれるウェブアプリ ランダム郵便番号 を公開しました!
URL: https://randomzip.yokubari-tentan.com

🎉 まずは遊んでみよう!

ボタンを押すだけで、スロット風の演出とともに郵便番号が決定。
決定後に「詳細を表示」ボタンを押すと、都道府県・市区町村・町域と Google Map が表示されます。

トップページのスクリーンショット ランダム郵便番号 トップページ

  • 旅行先決めに
    行き先を迷ったときに、遊び感覚で抽選できます
  • ワークショップのアイスブレイクに
    チームでランダムにグループ分けしたり、話題作りに
  • 学習・遊び教材に
    郵便番号を覚えるクイズとしても使えます

気に入ったらぜひサイトをブックマークしてお使いください!
➡️ https://randomzip.yokubari-tentan.com

🛠️ 技術的にどう作ったの?

ここからはエンジニア向けに、主な技術スタックと仕組みをざっくり解説します。

1. 定期的にデータ更新:GitHub Actions × 日本郵便CSV

  • 月1回のバッチ処理 で、日本郵便公式のCSVを取得
  • スクリプトでCSVをパースし、JSONに変換
  • 出力されたJSONをcommitし、GitHubにpush

GitHub Actions ワークフロー

name: 郵便番号データの月次更新

on:
  schedule:
    - cron: '0 18 1 * *'  # 毎月1日の18:00に実行
  workflow_dispatch:  # 手動でのトリガーも可能

jobs:
  update:
    runs-on: ubuntu-latest

    steps:
      - name: リポジトリのチェックアウト
        uses: actions/checkout@v4

      - name: Nodeのセットアップ
        uses: actions/setup-node@v4
        with:
          node-version: '22'

      - name: 依存パッケージのインストール
        run: npm ci

      - name: 郵便番号データの取得
        run: |
          mkdir -p /tmp/zipcode-data
          cd /tmp/zipcode-data

          curl -L -o utf_ken_all.zip https://www.post.japanpost.jp/zipcode/dl/utf/zip/utf_ken_all.zip
          unzip -o utf_ken_all.zip
          ls -l ./

          cp ./utf_ken_all.csv $GITHUB_WORKSPACE/scripts/utf_ken_all.csv

      - name: CSV から JSON へ変換
        run: |
          npx tsx scripts/update_zipcode_json.ts --input ./scripts/utf_ken_all.csv --output ./public/data/zipcode.json

      - name: 変更をコミットしてプッシュ
        run: |
          # git のユーザー情報(Actions Bot)を設定
          git config user.name "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"

          # 変更がなければ exit 0
          git diff --quiet public/data/zipcode.json && echo "No changes in zipcode.json" && exit 0

          # 変更があればコミットしてプッシュ
          git add public/data/zipcode.json
          git commit -m "郵便番号データ更新 ($(date '+%Y-%m-%d'))"
          git push

作成したJSONを使って、ランダムに郵便番号を選ぶロジックは、フロントエンドの React コンポーネントで実装しています。

2. お問い合わせフォーム:Cloudflare Pages Function + Turnstile

お問い合わせフォームでは、スパムを防ぐためにCloudflare の CAPTCHA「Turnstile」を導入しました。

お問い合わせフォーム1

お問い合わせフォーム2

フロントエンドではReactを使っているのですが、以下のようにuseEffectを使ってTurnstileのレンダリングをしています。callbackで設定しているTurnstileのtokenは、フォーム送信時にサーバーサイドに送信しています。

const [turnstileToken, setTurnstileToken] = useState<string | null>(null);
const turnstileRef = useRef<HTMLDivElement>(null);

useEffect(() => {
  if (!turnstileRef.current) return;
  if (!window.turnstile) return;

  const widgetId = window.turnstile.render(turnstileRef.current, {
    sitekey: SITEKEY,
    callback: (token: string) => {
      setTurnstileToken(token);
      setErrors(prev => ({ ...prev, turnstileToken: undefined }));
    },
    'error-callback': () => {
      setTurnstileToken(null);
    },
    'expired-callback': () => {
      setTurnstileToken(null);
    },
  });

  return () => {
    if (window.turnstile) {
      window.turnstile.remove(widgetId);
    }
  };
}, []);

Turnstileが動作するように、<head>タグ内に以下のスクリプトを追加しています。

<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>

サーバーサイドでは、Cloudflare Pages Function を使って Turnstile のトークンを検証しています。以下のように、POSTリクエストで受け取ったトークンを検証します。

const verifyResp = await fetch('https://challenges.cloudflare.com/turnstile/v0/siteverify', {
  method: 'POST',
  headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
  body: new URLSearchParams({
    secret: context.env.TURNSTILE_SECRET_KEY,
    response: req.turnstileToken,
  }),
});
const verifyJson = await verifyResp.json();
if (!verifyJson.success) {
  return new Response(JSON.stringify({ error: 'Captcha の検証に失敗しました' }), {
    status: 400,
    headers: { 'Content-Type': 'application/json' },
  });
}

Turnstileを使うときには、本番環境のdomainをホスト名として登録するとは思いますが、開発環境のホスト名も登録しておくと開発環境でもTurnstileの動作検証ができるので便利です。

🌟 まとめ

ぜひ一度、ランダム郵便番号 を試してみてください! ご感想やアイデア、お問い合わせはフォームからお気軽にどうぞ😊