AIを使って複数サイトから記事をまとめるアプリを作ってみた話
最近AIって分刻みで進化してるよね。気づけば世界が加速してて、最先端のニュースを追いかけたいと思っていても、色んなサイトをまわって情報を漁るのは目が回りそう。いや、まわるなんて甘い表現じゃないフィンファンネルぐらい複雑に回らされる。だったら、「複数のサイトから自動で記事を集められるアプリ」を作ってしまおうじゃないかというお話を共有します。でも僕自身、プログラミングの専門家というわけではないのだけど、AIの力を借りてかなりラクに構築できたのがポイント。
作ろうと思ったきっかけ
大量のニュースを毎日追うのが大変
AIに関する情報は国内外のブログやニュースサイト、SNSなどそれこそ大量にあります。太平洋のように広いです。正直情報を集めるだけでめんどくさすぎ。
RSSリーダーだと一覧性はあるが、さらに自分仕様にしたい
従来のRSSリーダーでもある程度は管理できますが、もっと効率的に情報を集めたいし、どうせなら自分の好きな機能を盛り込みたいって思った。
結果、「だったら自分でオリジナルのニュース収集アプリを作ってみよう!」となったわけ。
使った技術とポイント
1. Supabase(データベース)
まず、集めた記事を保存するためのデータベースとしてSupabaseを使う。
理由:
ローカルにデータベースサーバーを立てるよりも手軽。
無料プランの範囲内でも個人のプロジェクトには十分。
利用者が増えていることもあって、日本語の解説やチュートリアルも比較的見つけやすい。
やったこと:
Supabaseプロジェクトを作成
記事を保存するためのテーブル(news など)を作成
2. Go言語でRSS取得
次に、「RSSフィードを定期的に巡回して新しい記事を拾う」仕組みをGo言語で実装する。とはいえ、僕自身はGo言語にほとんど馴染みがなかったのだけど、ここはCursorのAgent機能で実装していく。
ある程度やりたいことがはっきりしていれば、まるで社長のように自信満々で指示を出せば、あとはAgent機能でほとんど勝手にコードを書いてくれるから助かる。有能な部下で助かるよ、無茶を言っても反論してこないなんて、なんて都合のいい関係なんだろうか。
3. Next.jsで表示用のフロントエンド
仕上げに集めた記事を一覧表示するためのフロントエンドは、Next.js(Reactベースのフレームワーク)を使っていく。
こちらも、Agent機能で大体何とかなりますが、やっぱりエラーが発生するので、その辺は人間が何とかしないといけません。
世話の焼ける部下だよほんと。
正直アーキテクチャや開発ロードマップをREADMEなどに記載しておけば大体のことはやってくれます。
僕がやってたことはAIにまとめてもらった開発ロードマップをAIに教えて、エラーが起きたらそれをAgent君に投げつける。そうするとどうにかこうにか解決してくれるから「はいはい、それでいこうか」指示を出すだけの簡単なお仕事。
全体の流れ
簡単に図にするとこんな感じです。
┌────────────────────────────┐
│ Next.js Frontend │
│ (記事一覧を表示) │
└────────────┬───────────────┘
│ REST API
│
┌────────────┴───────────────┐
│ Supabase (DB) │
│ (ニュースを保存) │
└────────────┬───────────────┘
│
┌──────────────────┴──────────────────┐
│ Go言語 (RSS取得) │
│ RSSフィードを定期的にチェック │
└─────────────────────────────────────┘
Goプログラムが対象サイトのRSSを巡回し、新しい記事情報を見つけたらSupabaseに保存
Next.jsアプリがSupabaseのデータを取得し、ブラウザで記事一覧を表示
ユーザーは一覧から興味のある記事をクリックして元サイトへアクセス
実際に作ってみた感想
情報収集がグッとらくになった
そうやって作られたニュースアプリは、定期的にRSSを巡回し、見つけた新着記事をSupabaseに保存してくれる。あとはNext.jsがそのデータを一覧表示するから、僕はそこから適当に面白そうなタイトルをクリックして元サイトにアクセスするだけ。うん、素敵な作業工程だろ?本当に「クリックして読む」だけ。笑いが止まらない。
RSSの仕組みはシンプルで扱いやすい
ニュースサイトやブログなど、RSSフィードを提供しているメディアは案外多い。実際の取得処理はシンプルなHTTPリクエストだから、初学者にも組み立てやすいと感じた。というかぶっちゃけAIがすごいから。
運用コストが低い
Supabaseの無料枠や、Goのランタイムの軽量さ、Next.jsのホスティング先の選択肢など、お金をかけずに試作を回せるのも大きなメリット。

まとめ
今回、複数のサイトから記事を自動収集して一覧表示するアプリを作ってみたところ、非エンジニアでもAI(Gemini2.5proなど)を活用することで、驚くほどスムーズに構築することができた。まあ、AIをフル活用してるとか言っても、結局は人間が最終チェックをしなきゃいけない部分は残る。Agent君も万能じゃないからね。抜けがあればフォローしてやらないと、気まぐれ猫みたいに不貞腐れることもある。ただその程度のフォローで済むなら安いものじゃない?「最新ニュースを全部自分で拾う苦労」を考えたら、ここまで肩代わりしてくれるだけでも感謝モノだよ。要は適度な距離感が大事ってことさ。
自分専用のニュースを作って、情報の洪水に溺れずに生き残るか、それとも毎日勢いに流されてバタ足するか。僕たちの選択は案外、こんな些細なところから左右されるのかもしれないよ。
もし「こんなの作ってみたいけど何から始めればいいか分からない」という方がいたら、ぜひ気軽にAIに聞くところから始めてみたらいんじゃないかな。意外とあっさり動くものができると思うからね。
ここからは一応設計図を書いておく、もし使いたければこれをAIにコピペすれば答えてくれるはず。
ニュースフィードシステム 設計図
1. システム概要
このシステムは、複数のRSSフィードからニュース記事を収集し、データベースに保存、Webフロントエンドで一覧表示およびブックマーク機能を提供するものです。
2. 全体構成図
graph TD
subgraph "外部サービス"
RSS[RSSフィード<br>(Zenn, Hatena, etc.)]
SUPA_DB[(Supabase Database<br>newsテーブル)]
end
subgraph "バックエンド (Go)"
GO_APP[Go アプリケーション<br>(package main.go)] --> |記事挿入/更新| SUPA_DB
GO_APP --> |データ取得| RSS
ENV_GO[`.env`<br>SUPABASE_URL<br>SUPABASE_KEY] --> GO_APP
end
subgraph "フロントエンド (Next.js)"
NEXT_APP[Next.js アプリケーション<br>(frontend/)] --> |記事読み取り| SUPA_DB
NEXT_APP --> |表示| BROWSER[ユーザーブラウザ]
BROWSER --> |ブックマーク操作| NEXT_APP
LOCALSTORAGE[(ブラウザ ローカルストレージ<br>ブックマークID)] <--> NEXT_APP
ENV_NEXT[`frontend/.env.local`<br>NEXT_PUBLIC_SUPABASE_URL<br>NEXT_PUBLIC_SUPABASE_ANON_KEY] --> NEXT_APP
end
BROWSER --> |ニュース閲覧| NEXT_APP主なコンポーネント:
Go バックエンド (`package main.go`):
役割: 定義されたRSSフィードから定期的にニュース記事を取得し、重複をチェックしながらSupabaseデータベースに保存する。
実行タイミング: 手動実行 (`go run "package main.go"`) またはcronジョブ等で定期実行。
Supabase:
役割: ニュース記事データ (`news`テーブル) を格納するデータベース。フロントエンドからの読み取り、バックエンドからの書き込みを処理する。
Next.js フロントエンド (`frontend/`):
役割: Supabaseからニュース記事を読み取り、ユーザーに一覧表示するWebアプリケーション。ローカルストレージを利用したブックマーク機能を提供する。
ユーザーブラウザ:
役割: フロントエンドアプリケーションを表示し、ユーザー操作を受け付ける。ブックマーク情報はローカルストレージに保存される。
3. データフロー
記事取得: Goバックエンドが設定されたRSSフィードURLにアクセスし、記事データを取得する。
記事保存: Goバックエンドが取得した記事データを整形し、Supabaseの`news`テーブルに挿入する。
挿入時、既存の記事との重複チェックを行う (一意性制約違反(23505)をハンドリング)。
記事表示: ユーザーがブラウザでフロントエンドにアクセスすると、Next.jsアプリがSupabaseから`news`テーブルのデータを取得する。
一覧レンダリング: Next.jsアプリが取得したデータを元に、ニュース記事カードのリストをレンダリングしてブラウザに表示する。
ブックマーク読み込み: フロントエンドは、初期表示時にブラウザのローカルストレージからブックマーク済みの記事IDリストを読み込む。
ブックマーク操作: ユーザーがブックマークボタンをクリックすると、フロントエンドがローカルストレージのIDリストを更新し、ボタンの表示状態を切り替える。
4. コンポーネント詳細
4.1. Go バックエンド (`package main.go`)
主要機能:
`.env` からSupabase接続情報を読み込み。
`gofeed` ライブラリを使用して複数のRSSフィードを解析。
`supabase-go` ライブラリを使用してSupabaseクライアントを初期化。
取得した記事を `news` テーブルに挿入 (重複はスキップ)。
依存ライブラリ: `github.com/joho/godotenv`, `github.com/mmcdole/gofeed`, `github.com/supabase-community/supabase-go`
設定: プロジェクトルートの `.env` ファイル
`SUPABASE_URL`: SupabaseプロジェクトURL
`SUPABASE_KEY`: Supabase Service Role Key (秘密鍵)
実行方法: `go run "package main.go"` (プロジェクトルートにて)
4.2. Supabase
テーブル: `news`
主なカラム (想定):
`id` (UUID, 主キー)
`created_at` (Timestamptz, デフォルト now())
`title` (Text)
`url` (Text, Unique) - URLでの重複チェックを想定
`published_at` (Timestamptz)
`source` (Text)
(必要に応じて他のカラムを追加: content, status など)
RLS (Row Level Security) ポリシー:
`anon` ロールに対して `news` テーブルへの `SELECT` 操作を許可する必要がある。 (例: `CREATE POLICY "Allow public read access" ON news FOR SELECT USING (true);`)
4.3. Next.js フロントエンド (`frontend/`)
主要機能:
`@supabase/ssr` の `createBrowserClient` を使用してSupabaseクライアントを初期化 (`frontend/src/lib/supabase/client.ts`)。
`useEffect` フック内でSupabaseからニュース記事を取得 (`frontend/src/app/page.tsx`)。
Tailwind CSS を使用したレスポンシブなグリッドレイアウトで記事一覧を表示。
`useState` と `useEffect` を使用し、ローカルストレージ (`localStorage`) を使ってブックマーク状態を管理・永続化。
`react-icons` を使用してブックマークアイコンを表示。
ローディング状態とエラーメッセージの表示。
依存ライブラリ: `next`, `react`, `react-dom`, `@supabase/ssr`, `react-icons`, `@types/node`, `@types/react`, `@types/react-dom`, `typescript`, `tailwindcss`, `postcss`, `autoprefixer`, `eslint`
設定: `frontend/.env.local` ファイル
`NEXT_PUBLIC_SUPABASE_URL`: SupabaseプロジェクトURL
`NEXT_PUBLIC_SUPABASE_ANON_KEY`: Supabase Anon Key (公開鍵)
実行方法: `cd frontend && npm run dev`
5. セットアップ手順 (概要)
前提ツール: Go, Node.js (npm含む) をインストール。
リポジトリ: プロジェクトリポジトリをクローン。
Supabase:
Supabaseプロジェクトを作成。
`news` テーブルを作成 (上記カラム定義参照、特に `url` にUnique制約)。
`news` テーブルに対するRLSポリシーを設定 (anonからのSELECT許可)。
プロジェクトURL、Anon Key、Service Role Key を取得。
環境変数設定:
プロジェクトルートに `.env` を作成し、`SUPABASE_URL` と `SUPABASE_KEY` (Service Role) を設定。
`frontend/` ディレクトリに `.env.local` を作成し、`NEXT_PUBLIC_SUPABASE_URL` と `NEXT_PUBLIC_SUPABASE_ANON_KEY` (Anon) を設定。
依存関係インストール:
プロジェクトルートで `go mod tidy` (必要であれば) を実行。
プロジェクトルートで `npm install` (ルートの package.json があれば)。
`frontend/` ディレクトリで `npm install` を実行。
初回データ投入: プロジェクトルートで `go run "package main.go"` を実行し、データベースに初期データを投入。
フロントエンド起動: `frontend/` ディレクトリで `npm run dev` を実行し、ブラウザでアクセス。
6. 運用・その他
データ更新: Goバックエンドは手動実行のため、最新ニュースを反映するには定期的に `go run "package main.go"` を実行する必要がある。cronやOSのタスクスケジューラ、CI/CD (GitHub Actionsなど) での自動実行を検討する。
エラーハンドリング: Goバックエンド、フロントエンドともに、エラー発生時のログ出力やユーザーへの通知を改善する余地がある。
