🗺️

「綺麗な写真」だけじゃ旅の感動は伝わらないから、Next.jsとPostGISで "移動" を共有するSNSを作った

に公開

はじめに

「この絶景スポット、どうやって行ったの?」

Instagramで綺麗な風景写真を見るたびに、いつもこうもどかしく思っていました。
写真は素晴らしいけど、そこに至るまでの「ルート」や「苦労」、そして「物語」が見えない。
本当の旅の価値は、目的地だけでなく 「そこへ向かう移動(体験)」 にあるはずだ。

そんな思いから、「写真だけでなく、移動の軌跡(ルート)そのものをシェアするSNS」 を開発しました。
名前は 『Mirutabi (ミルタビ)』 です。

技術的には、フロントエンドに Next.js 15 (App Router)、位置情報データベースに Supabase (PostGIS)、そしてアクセス解析基盤に Go言語 (Gin) を採用しています。

作ったもの:Mirutabi

「Visual Journey Archive」をコンセプトにしたWebアプリケーションです。

GitHub: https://github.com/naki0227/mirutabi

主な機能

  1. Visual Route Sharing: 地図上に自分の移動ルート(GPX/KML)を描画し、そのルート上のどこで写真を撮ったかを可視化。
  2. Trust Score: 「いいね」の数だけでなく、投稿の具体性や過去の実績から情報の信頼度を算出するアルゴリズム。
  3. Experience Archive: その場所で感じた「感情」をメタデータとして保存。

技術スタック:「地図」と「ログ」をどう捌くか

位置情報を扱うSNSは、データ量が膨大になりがちです。
そこで、読み込み(Next.js)と書き込み(Go)の責務を分ける構成にしました。

コンポーネント 技術 役割 こだわり
Frontend Next.js 15 UI/UX Mapbox GL JS との連携。App Router での高速なルーティング。
Database Supabase DB PostgreSQL + PostGIS による高度な位置情報検索。
Analytics Go (Gin) ログ基盤 ユーザー行動ログ(足跡)の高速書き込みを担当。
Auth Supabase Auth 認証 Next.js Middleware と連携したセキュアな認証。

こだわり①:PostGISによる「半径検索」の魔術

Mirutabiの核心は 「今いる場所の近くにある素敵なルートを探す」 機能です。
これを実現するために、Supabaseの拡張機能である PostGIS をフル活用しています。

単なる「緯度経度の一致」ではなく、地球の丸みを考慮した「半径Nキロメートル検索」をSQL一発で実現できるのがPostGISの強みです。

-- 実際の検索クエリのイメージ
select * from routes
where ST_DWithin(
  location, -- PostGISのGeography型カラム
  ST_SetSRID(ST_MakePoint(:user_lng, :user_lat), 4326),
  5000 -- 半径5km
);

これをSupabaseの RPC (Remote Procedure Call) として定義し、フロントエンドからはSDK経由で関数として呼び出すだけで、爆速な位置情報検索が可能になっています。

こだわり②:Go言語による「書き込み負荷」の分散

SNSを作ると決めた時、一番怖かったのが 「ログデータによるDBのパンク」 でした。
「どのルートが見られたか」「どこが離脱ポイントか」といった行動ログは、サービスが成長すると秒間数千リクエストに達する可能性があります。

これをメインのPostgreSQLにそのまま書き込むと、本来のSNS機能(投稿や取得)が遅くなってしまいます。
そこで、ログ収集部分だけを Go言語 (Gin) + Firestore のマイクロサービスとして切り出しました。

analytics/cmd/server/main.go
// Goによる超軽量ログサーバー
func main() {
    r := gin.Default()
    
    // Firestoreへの非同期書き込み
    r.POST("/log", func(c *gin.Context) {
        var entry LogEntry
        if err := c.ShouldBindJSON(&entry); err == nil {
            // ゴルーチンで裏で保存(レスポンスをブロックしない)
            go saveToFirestore(entry) 
            c.JSON(200, gin.H{"status": "captured"})
        }
    })
    
    r.Run(":8080")
}

Goの Goroutine を使うことで、ユーザーからのリクエストには数ミリ秒で「OK」を返しつつ、裏側で確実にログを永続化する仕組みを構築しました。
Next.js API Routesでも書けますが、この「同時接続の捌きやすさ」はGoならではの体験でした。

おわりに

「位置情報 × SNS」 という王道ジャンルですが、技術スタックを工夫することで、個人開発でもスケーラブルな構成が作れることがわかりました。

Next.jsの手軽さと、Go/PostGISの堅牢さ。
この「柔と剛」の組み合わせは、モダンなWeb開発において一つの最適解だと感じています。

もし旅行好きのエンジニアがいらっしゃれば、ぜひプルリク(または旅のルート共有)をお待ちしています!

GitHubリポジトリ
https://github.com/naki0227/mirutabi

Discussion