どうも、かめさんです。
普段はバックエンドエンジニアとして自社サービスや自社サービスじゃないものを作っています。
今回、自分の持っているドメイン上に自分専用のブログシステムを作成したので、そのブログ自体をどう作ったかを書いてみようと思います。
この記事は実際に作成したブログシステム上でも公開しています
以下のリンクから閲覧可能です
TL;DR
- ブログ・つぶやき・Vlogを一つのタイムラインに流せる、自分専用の個人サイトを作った
- Next.js + SQLite + FFmpegで、自宅サーバー上に構築
- 投稿はパスキー認証した自分だけ。公開側は普通の個人サイトとして誰でも見られる
- ChatGPTからPR作成、CI、マージまで進め、自宅サーバー側がGitHubを定期確認して自動デプロイ
- チャット履歴に依存しないよう、仕様・ADR・開発フロー・SkillをGitHubに残すようにした
- とにかく「面倒でやらなくなる」を減らして、投稿も開発もスマホからできるようにした
どんなサイトを作ったのか
今回作ったのは、ブログ・つぶやき・Vlogを全部ひとつのタイムラインに流せる個人サイトです。
この3つを別々のサービスに分けず、全部同じ場所から投稿して、同じタイムラインに流れるようにしています。
公開側は普通の個人サイトとして誰でも見られますが、投稿できるのは自分だけです。ログインすると投稿やプロフィール編集などの操作が使えるようになります。
ブログではMarkdown、画像、Mermaid、YouTubeやXの埋め込みなどが使えます。
つぶやきは画像を最大4枚まで添付でき、Vlogはブラウザから短い動画を撮ってそのまま投稿できます。
要するに、XとZennとsetlogを自分用に一か所へまとめたものです。
ドメインとサーバーが余っていたのでなにか作りたかった
学生時代にハードオフで9,000円くらいで買ったデスクトップPCと、Cloudflareで激安で手に入れたイケてるドメインを持っています。せっかく持っているのですが、最近は全然使えていませんでした。
そんな中、友達が技術ブログを自作しているのを見て、自分もやりたい!と思いました。
ただ、腰を据えてブログを書くほど集中力があるかというとかなり怪しく、せっかく作ってもどうせ続かなそうだなとも思いました。
一方で、ツイートとsetlogは普通に続いています。
じゃあ、ブログもつぶやきもVlogも全部同じサイトから投稿できるようにしたら続くんじゃね?
と思ったので、つくりました。
だったら、長文を書きたいときはブログ、1行だけ書きたいときはつぶやき、動画だけ残したいときはVlog、という感じで全部同じところに投げられればいいじゃん、となりました。
要件
このシステムを作るにあたって、以下の項目を要件としてまず決めて、ChatGPTと仕様を詰めていきました。
-
とにかくZennみたいな洗練されたMarkdownベースのブログシステム、Xみたいな即時つぶやきUI、setlogみたいなVlog要素を一つのタイムラインに流す
-
PCとスマホ両対応
-
Notionっぽいデザイン
-
XっぽいUX
-
投稿するのは自分だけ
-
完全にバイブコーディングする
- もはやCodex CLIすら開きたくない
- スマホからPR→デプロイまで完結させる
特に大事にしたのは、投稿する際の負荷を極限まで減らすことです。
僕は暇さえあればXとChatGPTの往復をする生粋のツイ廃かつドパガキなので、思い立ったら一切止まらずに投稿できるのがマストでした。なので、Xにポストする時と全く同じ操作感で投稿できることを目指してUIを設計しました。
また、技術ブログでは、Markdownを書いてGitHubにpushすると公開される構成もよくありますが、わざわざ記事をまとめてmdにしてGitにあげて…なんて操作は、自分の場合はたぶんすぐやらなくなると思ったのでやめました。
このサイトでは普通のWebサービスと同じように、ブラウザから直接投稿します。
ブログならMarkdownエディタを開く、つぶやきならその場ですぐ入力する、Vlogなら動画を撮ってそのまま投稿する、という感じです。
構成
インフラ
インフラ構成は以下で組みました。
サーバーは元ジャンクのオンボロPCにメモリとSSDぶっ込んだだけの限界ハードです。
-
自宅サーバー
- Ubuntu Server
- Cloudflareで名前解決からトンネリングまで
- 特にProxmoxとかの仮想化技術は入れない
- とりあえずDockerとsystemdだけ
-
外部サービスは極力使わないように
- なにせお金を使いたくない
- APIキーや外部サービスを増やすと管理するものも増えるので、できるだけ減らしたかった
-
デプロイは5分に1回、GitHubからmainブランチを確認するpull方式を採用
- GitHub側から自宅サーバーへ接続させたくなかった
- GitHubリポジトリやActions側に自宅サーバーへ入るための情報を持たせたくなかったため
デプロイはGitHub ActionsからSSHするのではなく、自宅サーバー側からGitHubを見にいきます。
mainの最新commitとCIが成功したcommitが一致していたら更新します。
これならGitHub側から自宅サーバーへ入る経路を用意しなくて済みます。
技術スタック
-
Next.js
- フロントはさっぱりなので特に理由もなく選定
- なんなら記事を書くにあたって技術スタックを調べた今、Next.jsなことを知った
-
SQLite
- デプロイ先がオンボロなので、バックアップのために簡単に持ち運べるほうが良かった
- 書き込むのもほぼ自分一人なので、今の規模ならこれでいいだろうという判断
- とはいえ使ってみると意外と制約も多い
- PostgreSQLやMySQLのバックアップもそこまで面倒というわけでもなさそうなので、乗り換えるか検討中(まだちゃんと調べてないです)
-
FFmpeg
- 動画用
- Vlogの動画を16:9にしたり、長さを制限したりしている
-
メディアストレージはOSのファイルシステムをそのまま
- 画像や動画はサーバーのディスクに保存している
- データが飛んだら嫌なので、S3だかR2だかに置くようにしてもいいかも
技術ブログならAstroなどを使って静的サイトにする方法もありますが、自分の場合は投稿するときにGit pushする方式にしたくありませんでした。
AIにやらせること自体はできますが、それでも投稿するたびにGitを経由する仕組みだと面倒になってすぐ飽きそうです。
なので、普通にAPIを持ったWebアプリとして作っています。
認証
-
パスキーを採用
- 外部の認証サービスを使っても良かった
- ただ、自分しかログインしないのにわざわざSSOのプロバイダーと契約したくなかった。面倒なので
-
かといって、いつハッキングされるかわからない自宅サーバーにパスワードを持ちたくない
-
なのでWebAuthnを使ったパスキー認証を採用した
- 秘密鍵はサーバーには置かず、端末側の認証器が持つ
- サーバー側では公開鍵を使って署名を検証する
-
WebAuthnの署名検証自体は自作せず、SimpleWebAuthnを使用
-
最初のパスキー登録だけbootstrap tokenを使う
開発環境
とにかく楽に開発できることを至上の命題としました。
VS Codeはおろかターミナルすらほとんど触らなくて済むようにしているので、ChatGPTのプロジェクト機能を使って、初回デプロイ以外のほとんどはスマホアプリから指示を出すだけで開発しています。
そのため、以下の工夫を入れています。
-
開発を続けるために必要な情報はGitHubリポジトリに集約
- 要件
- 仕様
- ADR(意思決定の履歴)
- 開発フロー
- エージェント向けのskill
-
当然CIもGitHub Actionsで管理
チャット履歴を読まなくても、リポジトリだけ見れば誰が、いつ、どんなエージェントで開発したとしてもすぐにメンテナンスを続けられるようにしました。
また、作成したコードは以下のルールでMainに入れています。
- AIにPRを出させて、CIがOKになるまでマージさせない
- ただしコードは一切レビューしない
AIが出した数百行のdiffを毎回スマホで全部読むようにしたら、たぶん自分は開発自体をやらなくなります。
その代わり、
- unit test
- TypeScriptの型チェック
- lint
- PlaywrightによるE2E
- Docker imageのbuildと起動確認
- データ永続化
- WebAuthnの異常系
- バックアップ・リストア
- UIを勝手に崩していないかのチェック
などをCIに入れています。
個人開発なんだから全部main直コミットでいいだろ、という人もいると思いますが、僕は個人的には個人開発こそテストが全てだと思っています。
コードレビューやQAは集中力を使うし、趣味の開発ではあまりそこに時間をかけたくないものです(少なくとも自分は)。なので、今回はテストコード自体の基盤を最初に作り込むことで、システムに品質の保証をさせ、人間が創造的なことだけにコミットできるような環境をはじめに作りました。
思ったこと
E2Eを書いても意外とフロントはバグが出る
-
普段はバックエンドエンジニアなので、ユニットテストを書いたらそれだけで滅茶苦茶なバグはなかなか出なくなる
-
フロントは意外とすぐにバグる
- 例えば、確認モーダルのボタンが透明になっていて、押せるけど見えないとか
-
E2Eで「ボタンが存在する」「押せる」までは確認できても、「人間から見てちゃんと見えている」までは検証してくれないのかと思った
パスキーが登録できない問題が発生した
- パソコンではパスキーが登録できたのに、スマホのブラウザだと登録できない問題が発生
- アプリ側では公開URLを
https://前提で設定していた - しかし
http://でアクセスしたとき、そのままHTTPとしてアプリまで到達していた - WebAuthn側ではHTTPSのOriginを期待しているのに、実際のアクセスはHTTP
- Originが一致せず、バックエンドでエラーになっていた
- CloudflareでHTTP→HTTPSに自動転送する設定を入れて解決した
かなり基礎的なことの気がしますが、HTTPSで公開しているつもりでも、HTTPでアクセスされたときにどうなるかは別途ちゃんと考えないとダメでした。
デプロイするとサイトが落ちる
- デプロイ時にバックアップを取るようにしている
- 手順はこんな感じ
サーバー停止
↓
バックアップ
↓
新しいコードへ更新
↓
build
↓
起動
- 当然その間サイトが落ちる
バックアップを安全に取りたくてアプリを止めるようにした結果、デプロイのたびにダウンタイムが発生するようになりました。
個人サイトなので今のところ大事故ではないですが、そのうち直したいです。
全体としてはめちゃくちゃ楽に開発できた
- たかがブログなので、本番でぶっつけ本番動作確認になる部分もある
- それでもわけのわからないバグは殆ど出ず、思った通りの出来栄えのものを継続的に動かせている
- 「直して」とスマホから言うだけでPRが出て、CIが通って、マージされて、しばらくすると自宅サーバーにも反映される
これは自分にはかなり合っているんじゃないかなと思います。
まとめ
「面倒なことはChatGPTにやらせよう」の精神で自動化基盤を整えたおかげで、お話するだけでアプリを作ったり直したりできる環境ができました。
おかげで布団の中でも開発がやめられなくなりました。
そして、このサイトを作った理由は、単に技術ブログが欲しかったからだけではありません。
考えることすらAIがやってくれる時代で、自分のようなドパガキが生きた証を残すためには、すべての考え、すべての経験をデータ化して残しておくことが大事なんじゃないかと思っています。
長く考えたことはブログ、脳直で思いついたことはつぶやき、そのとき見ていたものはVlogにする。
このサイトは、そういうものを全部まとめて残しておくための場所でもあります。
皆さんも脳内直結データベースを作ってみてはいかがでしょうか。


