work
AIエージェントとバイブコーディングしてサイト構築した

最近長らく放置していた個人ブログについて、これまでNotion+Wraptasという組み合わせで旧ドメインのサイトten-ezo.comを運用してきた。
しかし、バイブコーディングを取り巻く環境の発展も目まぐるしい昨今、心機一転してAstro+Cloudflare Pagesによる新サイトtenjimn.comへの移管を行った。
移管に踏み切った主な理由は、以下の通りである。
- Claude CodeやAntigravityなど、最新のAI技術を用いた開発ができるような、本業とは切り離した自分だけのsandboxを作って遊んでみたかった
- 最近のSNSは眺めても良くない事が増えたために距離を置いているが、それでも現在の自分を構築している所有コンテンツを改めてアーカイブし、純度の高い記録として残す場所をゼロから考えたかった
- せっかくなので、noteやXの記事投稿では実現できない、パフォーマンスとデザインに優れたフレームワークを使った、
いずれ訪れるであろうミッドライフクライシスからの避難場所自分だけの秘密基地・コンテンツパイプラインを構築したかった - 旧ドメインが
ten-ezo.comという、蝦夷イコール北海道に土着したものとなっており、予定はないが未来永劫北海道に居住するとも限らないため、持続可能な新ドメインに移管したかった - WraptasというNotionをラップする外部サービスに依存した状態がリスキーで、動的なコンテンツ更新や変換なども走るためかパフォーマンスにも満足できず、それでいて月額費用を払い続けていた
そこで今回は、移管に至るまでの技術選定過程、Antigravityを活用した開発プロセス、Cloudflareによるインフラ設定、NotionベースのコンテンツをAstroに流し込むに当たっての試行錯誤など、せっかくなので書き残しておく。
技術選定的なものをしてみる
フレームワーク:Astro
最近のWeb開発でも主流となっているAstroを採用した。Astroはコンテンツ主体のフレームワークとして優秀で、不要なJavaScriptを削ぎ落とした純粋なHTMLを出力できるため、サイト表示が爆速である。Markdown形式の記事との親和性も非常に高く、余計な装飾を削ぎ落としてコンテンツに集中するという点でも、自分の好みに一番合っていた。
ホスティングとインフラ:Cloudflare Pages
当初はGitHub Pagesでの公開を検討していたが、最終的にCloudflare Pagesに切り替えた。最大の理由は、画像配信のパフォーマンスと、データ容量のゆとりである。そして、新旧ともにドメインをCloudflareで取得していたため、設定などの親和性も高い。さらにトータルで年間十数ドルという安さ。選ばない理由を探すほうが難しかった。
開発環境:Antigravity
開発はAntigravityでやってみた。これまで本業ではVSCodeを使うことが多かったため、最近は例に漏れず慣れ親しんだIDEベースのCursorに乗り換え、さらにGoogle AI Proを契約したのでAntigravityも触っている。言い換えると、今回もClaude CodeやCodexなどは使っていない。
利用するモデルは、贅沢にもClaudeの最高峰であるOpus 4.6を頼りつつ、Gemini 3.1 Proも併用してリファクタリングや相互レビューを任せた。それぞれの推論能力や個性の違いを遊びながら体験できたので、とても面白かった。まさに直近Antigravityは上位モデルが制限付きの実質値上げとなったが、今回の開発程度であればGoogle AI Proの枠内+Gemini Flashで十分だった。
AntigravityとAstroを組み合わせているのもあり、AIエージェントが自動でブラウザを立ち上げ、スクリーンショットを撮りながらフロントエンドを確認・自動調整していたのは、本当に感動した。思考プロセスや処理実行、ユーザーへの承認を求める内容やタイミングなど、次世代の開発体験として学ぶことも多かったように思う。
運用方針を考える
記事執筆
Notionは偉大なプロダクトで、UIUXの素晴らしさによる執筆体験は譲れない。忙しい日々を送りながら、毎回腰を据えてPCで執筆できるわけではないし、AIに全てを任せるわけにもいかない。リニューアルに至ったモチベーションとしても、あくまでラッパーや表示パフォーマンスに不満なだけであった。
そこで、以下のような運用方針とした。
- Notionでの執筆:これまで通り、Notionで記事を書くし、画像やデータベースも気にせず活用する。スマホでもPCでも音声文字起こしでも良い、とにかくハードルを下げる。
- エクスポート:執筆完了後、Notion の機能で「Markdown & CSV(画像を含む)」として一式をエクスポートする
- 変換とpush:クローンしたリポジトリ配下のディレクトリに一式を放り込み、内容を記事としてマークダウン等に変換するスクリプトを実行した上で、出力結果をデプロイする
欲を言えば、記事の更新には未だMacを必要としてしまうため、より一層運用フローを削ぎ落とし、iPhoneだけで更新できるようにしたかった。NotionアプリからエクスポートしたZIPファイルを、GitHub Actions経由で自動変換・デプロイする仕組みも作れば出来そうではある。ただ、今回はあくまでゼロからの再構築を至上命題と置くため、将来の展望として見送ることとした。
Antigravityでサイト構築
エクスポートしたデータをAstro向けに出力するために、Antigravityの力を借りてPythonスクリプトを自作し、変換整形を自動化した。フロントマターの付与、画像パスの再構築、mdやhtmlへの置換などを、一括で行うものだ。変換スクリプトをPythonとしたのは、元々データ分析を生業としていた自分であれば、コードの意図もある程度読解できるため、システムが完全なブラックボックスにならずに済むという狙いもあった。
Cloudflareでインフラ設定する
AIエージェントによるバイブコーディングからは少し脱線するが、今回新しいサイトを公開するに当たっては、インフラ周りで幾多かのアクシデントと学びもあった。
1GB越えの巨大ファイル群
Notionからエクスポートした過去記事の大量の元画像を、そのままgitの管理下に入れてしまい、初回push時には1.22GBという巨大なサイズになり、GitHub側でリモートリジェクトされるという事態が発生した。不要な元データディレクトリを .gitignore でしっかり除外し、必要な src などのファイルだけに絞ることで解決した。結果として、ローカルでは何を残しておいて、何を変換してリモートに載せるのかを意識するきっかけにもなった。

Cloudflare Pagesという魔法
元々はGitHub Pagesによるデプロイを想定していたが、ファイルサイズや画像表示のパフォーマンスなどを考えた結果、元々ドメインを取得していたCloudflareの機能であるPagesを使ってみることにした。付随して、GitHub Pages用や旧サイトのWraptas用に設定していた、複数のAレコードなどの複雑なDNS設定は、Cloudflare Pagesと連携した瞬間に不要となった。カスタムドメイン設定でドメインを追加するだけで、ルートドメインのレコードが自動で最適化される。これにより、個人の静的サイトレベルであれば、インフラ管理の不安が一掃されたのであった。

旧ドメインからのリダイレクト設定
過去資産を活かすのと、旧ドメインで残った契約期間を利用するため、旧ドメインへのアクセスを新ドメインへ転送する仕組みも、Cloudflare Pagesで構築した。転送専用のGitHubリポジトリを用意し、そこに <meta http-equiv="refresh"> を仕込んだ index.html と 404.html だけを配置した。これをビルドなし・フレームワーク: Noneでデプロイすることで、旧サイトおよび配下の記事含めて、どんな古いURLパスに訪れたとしても、高速で新サイトへ誘導できるミニマルなリダイレクト環境を作った。

Notionエクスポート前提でスクリプトを組む
Markdownを直接編集しないルール策定
大前提として、スクリプトによる変換後のマークダウンを直接編集することは禁止とした。万が一過去記事に再変換をかけた際に、conflictしてしまうのを防ぐためである。表示がおかしい場合は、必ず汎用変換ロジックであるスクリプト側を修正する、という運用ルールを徹底した。加えて、今後の保守性担保やAI主導での開発を考えると、プロジェクトの設計方針や変換ルールをAGENTS.mdのドキュメントとして明文化し、AIエージェントに読ませる状態を作っておくことが非常に大切だと実感している。
保守性を高めるためには正直、まだまだやり切れていない気もしているが、今後はこういった思想がまさに次世代の開発に不可欠となっていくのであろうと、身をもって実感したのであった。
Notionエクスポートにおける癖にパッチ対応
例えば、Notionでギャラリービューなどのインラインデータベースを使うと、エクスポート時にはただのCSVファイルへのリンク[DB Name](path/to/file.csv)になってしまう。そのためスクリプトでは、正規表現でこの形式を検出してCSV内のデータを読み取り、Astro側の独自コンポーネントへ展開するパッチ処理を組み込んだ。
他にも、今回の組み合わせもあって発生したパターンと対応方針を例示する。
- 見出しと水平線:Notionの罫線
--が AstroのCSSと干渉したため、スクリプトで見出し前後の不要な水平線を強制削除 - 太字と日本語:Notionの太字
**テキスト**が正しく変換されないケースに対し、正規表現で強制的に<strong>タグに置き換えるパッチを適用 - X (Twitter) の埋め込み:Notionにembed展開されたURLに対し、
blockquote class="twitter-tweet"にマッピングされるように調整

更なるデジタルアーカイブを再構築する
過去の旅動画もコンテンツとして統合
新サイトにおいては、いまの自分を構成している経験をよりアーカイブしたいという思いもあった。具体的には、昨今ほど動画撮影・投稿が各種プラットフォームで主流ではなかった10年以上前から、当時ようやく見れる画質になり始めたGoProなどを用いて、SNSに投稿していた旅の動画を若かりし自分に対する羞恥心を捨ててコンテンツとして統合することだ。
ただし作成した過去の動画において、特にアーティストの音源を使用したコンテンツを個人で扱うため、その最大の懸念は著作権である。趣味で作成した動画ファイルをアップロード・ホスティングすることは、権利観点から到底許容できるリスクではない。とはいえ、今更それらの動画を再編集するほどのモチベーションはないし、選曲にも編集時の思想がそのまま思い出として閉じ込められているはずだ。
IT巨人の肩に乗る
そこで、自前のサーバーに直接動画ファイルを配置するのではなく、IT巨人が提供するプラットフォームの公式埋め込み機能を使うことにした。プラットフォーム側が権利者と結んでいる包括的ライセンス契約の傘に入ることで、法的なクリーンさを保ちながら、当時の空気感をそのままアーカイブすることが可能だ。万が一権利者から申し立てがあった場合でも、プラットフォーム側で動画がミュートされるだけであり、サイト全体のインフラやドメインパワーにダメージが及ぶことはないという安心感もあった。
初めは、動画をストック済みでスマホとの親和性も高い、InstagramリールをAPI等で呼び出して運用するつもりだった。Astroの採用で目指したシンプルかつ軽量なサイトにおいて、Instagramの埋め込みはモバイルファーストで余計な装飾が少なく、CSSの干渉も最小限に抑えられると思ったからだ。しかし実装を進めると、Meta側の仕様や制約が思ったよりも複雑であった。その結果、一部の動画はインラインで再生できるが、一部の動画はInstagramのサイトやアプリへ強制的に飛ばされてしまうという、微妙な体験になってしまった。

試行錯誤の結果、苦肉の策としてInstagramだけでなく、YouTubeへ再アップロードした限定公開リンクも併用する方針に落ち着いた。単なる埋め込み目的でYouTubeにアップロードするのは怖かったが、限定公開であれば能動的に検索されることはなく、圧倒的に安心感のあるインライン再生の恩恵をフルに受けることができる。著作権観点からの法的安全性と、サイトのミニマルなフロントエンドを担保するための、現時点での最適解だ。そのため、動画によってはいずれかのプラットフォームが使い分けられている。

結びに
そうして完成したのが、この新サイトである。
今回いわゆるバイブコーディングというやつでサイト構築してみて、開発体験には終始感動して驚かされるばかりであったし、圧倒的な表示速度と自己満足のデザインを両立できたアウトプットにも満足している。思い起こせば遠い昔、お名前ドットコム+XServer+WordPressでブログ運営してた頃に比べると、自分レベルでも一人でここまでやれるほどに、AIエージェントとCloudflareのおかげで世界が一変している。作りたいものや叶えたい思想があれば、AIと対話しながらそれを成し遂げられることを、身を持って実感した。
同時に、あくまで本ケースは静的な個人サイトだから成り立つのであって、業務アプリケーションなどで似たような性能や感動を体感できるのかは、少なくとも現時点ではわからない。フロントエンドばかりに目が行って「これでいいじゃん」と思い込んでしまい、インフラ・バックエンド・保守性・取得ログデータ等が蔑ろにされてしまうのは、世の常でもある。
(記事冒頭画像のヴェネツィア・サンマルコ寺院は、地盤沈下に悩む土壌と煌びやかで歴史ある上物のメタファーとなっているが、皮肉を効かせ過ぎだろうか)
変化の激しい世の中を生きていくために、改めて今回の経験を本業にも活かせねばと、兜の緒を締めるのだった。

追伸:最近公開された以下のPodcastが非常に胸を打つ内容であった。今回の自身の経験を踏まえても、未来の事業活動・組織・キャリアはどうなるのだろうか?今回は自己満足な営みだから楽しめたものの、果たして仕事の楽しみはどこに見いだしていくべきなのだろうか?などなど、言語化がとても素晴らしかった。