Next.js フルスタックブログ開発記

힘센캥거루
2026년 1월 13일(수정됨)
4
8

Web開発に初めて触れてから1年ほど経った頃、自分だけのブログを持ちたいと思うようになった。

そこで、およそ6か月これだけにかかりきりになって作ってみることにした。

Next.js フルスタックブログ開発記-1

フロントエンド側の機能については、下記のキム・ドヒョンさんのブログを参考にするだけで十分だと思う。

自分も mdx を使ってブログを作るだけなら、1週間もかからなかった。

実のところ、ブログを運営するだけならフロントエンドだけあっても大きな問題はなかった。

しばらくは mdx を書いてフロントエンド開発だけで十分だと思っていたが、どこからでもWebにアクセスして記事を書けるフルスタックブログへの夢は捨てきれなかった。

1. 機能

機能は大したことはない。

記事を投稿して、確認できる。

Next.js フルスタックブログ開発記-2

ログインすると、右上のボタンの形が変わる。

そしてそれをクリックすると記事をアップロードできる。

下の動画を見ると、記事を書いてアップロードする一連の流れが分かる。

画像変換の処理があり、アップロードされたすべての画像は、最も効率が良い avif 形式で管理している。

YouTube は、URL を貼り付けるだけで上のように埋め込むことができる。

表(テーブル)管理機能のアイデアは Tistory から得た。

Next.js フルスタックブログ開発記-3

表にフォーカスが on になると、上部に表を追加・削除するツールバーが表示される。

もちろん下書き保存も可能だ。

エディタ内で cmd+s もしくは ctrl+s を押すと保存される。

2. アーキテクチャ

アーキテクチャというと何かカッコよく聞こえるが、韓国語にするとただの構造だ。

最初にサーバーへ入ってきたリクエストは、Caddy がリバースプロキシとして振り分けてくれる。

フロントエンドだけなら mdx をそのまま変換して返すだけでよかったが、バックエンドを作ったので、これを Next.js と Prisma でつないだ。

Next.js フルスタックブログ開発記-4

みんな分かっていることだが、ユーザーから来たデータをそのままバックエンドに突っ込んだら即ハッキングされる。

必ず API で検証してから保存する。

Next.js フルスタックブログ開発記-5

一度、ブログにコメントがたくさん付いてほしいという思いから、誰でもコメントを書けるように API を開放しておいたことがある。

するとあるヤツがその API に対してクエリインジェクション攻撃を浴びせ続け、コメントが100件ほど付いてしまっていた。

svg 画像の中に fetch とコマンドを埋め込んでハッキングを試みたヤツもいた。

もしかしたら中国のどこかで、知らないうちに自分の分身が元気に歩き回っているかもしれない。

3. エディタ

Next.js フルスタックブログ開発記-6

一番手をかけて作ったのがエディタだ。

エディタは Tiptap にさまざまな機能を追加して実装した。

その結果、Tiptap 関連の Component だけで10個を超えてしまった。

Next.js フルスタックブログ開発記-7

画像挿入のケースは、下記のように実装した。

  const editor = useEditor({
    extensions: editorExtensions,
    content,
    onUpdate: ({ editor }) => {
      onChange(editor.getHTML());
    },
    editorProps,
    immediatelyRender: false, 
  });

  const { uploadProgress, uploadImage, clearProgress } = useImageUpload({
    onUploadComplete: (imageUrl, isAnimated) => {
      if (editor) {
        try {
          editor
            .chain()
            .focus()
            .insertContent([
              {
                type: "image",
                attrs: {
                  src: imageUrl,
                  alt: "アップロードされた画像",
                  "data-animated": isAnimated || false,
                },
              },
              {
                type: "paragraph",
                content: [],
              },
            ])
            .run();
        } catch (error) {
          console.error("画像挿入エラー:", error);
        }
      }
    },
  });

パフォーマンス上の利点を得るために、HTML をそのまま保存・ロードするようにした。

Tiptap からバックエンドに保存するときには、いくつか処理を追加した。

コードブロック、Google マップ、見出し、画像、リンクプレビューなど、さまざまな処理を事前に行ったうえで、content と processedContent を別々に保存した。

4. バックエンドスキーマ

最近、多言語サービスを実装してみて、一度作ったバックエンドスキーマを変更するのは災難だということを思い知った。

あらためて、スキーマの重要性を強調していた先生の言葉を思い出した。

もしブログのバックエンドまで考えているなら、どのようなサービスを提供するのかをじっくり長く考えてみてほしい。

あれこれ追加していたら、あまりにも巨大になってしまったスキーマなので恥ずかしいが公開してみる。

model Post {
  id               String          @id @default(cuid())
  title            String
  slug             Int             @unique @default(autoincrement())
  excerpt          String
  content          String
  processedContent String?         
  thumbnail        String?
  published        Boolean         @default(false)
  createdAt        DateTime        @default(now())
  updatedAt        DateTime?
  likes            Int             @default(0)
  views            Int             @default(0)
  comments         Comment[]
  likesRows        PostLike[]
  viewDedups       PostViewDedup[]
  notifications    Notification[]
  locales          PostLocale[]
  author           User            @relation(fields: [authorId], references: [id], onDelete: Cascade)
  authorId         String
  category         String
  @@map("posts")
}

5. 感想

ここで次の質問に答えてみようと思う。

ブログを作るのにバックエンドまで必要なのか?

全部作り終えてみると、わざわざ?という気もしてくる。

結局 WordPress がものすごく楽な道だった、ということに気づかされた。

バックエンドと API を実装した瞬間から、本当に考えなければならないことが一気に増える。

最近の react2shell 問題のときには、数分おきに攻撃を受け、ターミナルに見たこともないコマンドがどんどん出てきて、数日間つらい思いをした。

今でも数日に一度は npm audit でセキュリティチェックをして修正している。

たまに DB スキーマを変更してマイグレーションがこじれると、冷や汗が出る。

それでも振り返ってみると、バックエンドを実装しながら多くのことを学べたし、自分の手で機能を実装できるブログを持てたのは良かったと思う。

これからはブログの機能追加よりも、他のプロジェクトを進めて自分のポートフォリオをもっと充実させていきたい。

관련 글

Caddyを利用した Next.js 無停止デプロイ(ローカルサーバー)
Caddyを利用した Next.js 無停止デプロイ(ローカルサーバー)
ホームページに何かをちょこちょこ載せたくなるたびにビルドしていたら、そのタイミング에 가끔 접속하는人がいるようだった。そうしているうちに、サーチコンソールでスコアがどんどん下がる現象が起きた。このままではまずいと思い、無停止デプロイをする方法を考えてみることにした。1. プロジェクトフォルダ2つ+...
Google検索インデックスの自動化 - Web Search Indexing API
Google検索インデックスの自動化 - Web Search Indexing API
前回の IndexNow に続いて、Google でも自動化してみることにした。調べてみると、Google は API として Web Search Indexing というものを提供していた。1. 対象範囲公式にこの API がサポートしている範囲は、求人情報とストリーミング動画サービスである。リ...
検索インデックス生成の自動化 - IndexNow
検索インデックス生成の自動化 - IndexNow
Bingにサイト登録をしている途中で知ったのだが、Bingでは IndexNow という機能を提供している。ポイントは、APIキーを使って、記事を書いた直後にすぐインデックス作成リクエストを飛ばせるということ。下のようなリクエストを fetch で作り、投稿機能に連動させておけば、記事をDBに保存す...
Nextjs、React サーバーがハッキングされた経験
Nextjs、React サーバーがハッキングされた経験
最初にセキュリティ問題に気づいたのは、12月5日の未明だった。React で認証なしにリモートコード実行が可能だということ。そのニュースを見て他の人には共有したものの、自分は大丈夫だろうと思って特に気にも留めなかった。1. ハッキングの痕跡を発見ところが、ブログのコードを更新しようとアクセスしてみる...
ブログに多言語機能を追加する(NextJS・next-intl・Vercel AI SDK)
ブログに多言語機能を追加する(NextJS・next-intl・Vercel AI SDK)
最近、ブログに多言語対応機能が必要だと感じた。そこで next-intl を使って多言語サービスを実装してみることにした。1.i18nまず多言語サービスを行うときには、守るべき原則がある。これを internationalization と呼ぶが、かなり長いので、先頭の i と末尾の n、そしてその...
Nextjsで多言語設定をする方法
Nextjsで多言語設定をする方法
私のブログへのアクセスを増やすためにどうすればいいか考えて、多言語設定を試してみることにしました。さまざまな方法を探しましたが、まずは言語ごとに異なるルーティングをすることから始めることに。next-intlを使って試してみました。1. インストールyarn add next-intl2. 適用適用は少し面倒ですが、次のような手順で進めれば大丈夫です。

댓글을 불러오는 중...