4
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【個人開発】Next.jsを使って技術ブログを作ってみた

4
Posted at

Next.jsの学習の一環で、技術ブログを開発してみました。

コンセプト

  • 社内のエンジニアの記事を集めて、社外に公開する
  • Qiitaで個人的に記事を書いている人もいるので、それを自動的に取得して技術ブログに反映する

技術スタック

  • Next.js 16 (App Router, React Compiler)
  • React 19
  • Tailwind CSS
  • daisyUI
  • Firebase App Hosting
  • Vitest / Testing Library
  • Qiita API
  • microCMS
  • openGraphScraper

表示例

トップページ 記事詳細ページ1 記事詳細ページ2

工夫した点

サーバーサイドとクライアントサイドを意識して実装

Qiita や microCMS のAPIに関するにシークレットが露出しないように、SSGで実装しました。

Qiitaのアイキャッチ画像を別途取得

Qiita の API のレスポンスには、OGP のアイキャッチ画像のURLが含まれていません。

アイキャッチ画像のないリストにしても良かったのですが、microCMS側でアイキャッチ付きのカードで記事一覧を作っていたので、せっかくならQiita側でもアイキャッチ取得を実装することにしました。

Open Graph Protocolのメタデータは、記事ページの本文の <head> タグ内に含まれています。

<head>
  <meta name="twitter:card" content="summary_large_image" />
  <meta content="@Qiita" name="twitter:site" />
  <meta content="@natsugure_" name="twitter:creator" />
  <meta property="og:type" content="article" />
  <meta
    property="og:title"
    content="UserDefaultsやAppStorageの初期値を正しく理解する - Qiita"
  />
  <meta
    property="og:image"
    content="https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-user-contents.imgix.net%2Fhttps%253A%252F%252Fcdn.qiita.com%252Fassets%252Fpublic%252Farticle-ogp-background-afbab5eb44e0b055cce1258705637a91.png%3F...(省略)"
  />
  <meta
    property="og:description"
    content="はじめに iOSにはUserDefaultsという、キーと値をペアにして永続的なデータとして保存できる便利なインタフェースがあります。 このUserDefaultsには初期値を登録しておけるUserDefaults.standard.register(defaults:)..."
  />
  <meta
    content="https://qiita.com/natsugure/items/5e9fca78b2746460f4d0"
    property="og:url"
  />
  <meta content="Qiita" property="og:site_name" />
</head>

これを普通にFetch APIやaxiosで取得しようとすると、不要なbody部分まですべて取得しなければならず、無駄な通信を多く発生させてしまいます。

openGraphScraper というライブラリを使うと、この問題を解消できます。

オプションに onlyGetOpenGraphInfo: true を付けることで、OGPのメタデータだけを効率よく取得できました。

async function fetchQiitaOgpImage(url: string): Promise<string> {
  const ogsOptions = await openGraphScraper({ url, onlyGetOpenGraphInfo: true })
  return ogsOptions.result.ogImage?.[0]?.url || ""
}

苦労したこと・反省点

daisyUIが初使用で、スタイリングの仕様を知らずに格闘

これまでMantineを多用してきたため、daisyUIの仕様を知らずに「ダークモードが外れない」と1時間も格闘していました。

キャッシュやTailwind CSSばかりを疑って、daisyUIの公式ドキュメントに行き着くまでかなり時間がかかってしまいました。

テストコードの実装に4時間かかる

15件のテストを実装するのに合計4時間くらいかけています。

テストコードの実装が本当に苦手で、必要なもののモック化、期待される結果の定義、依存する関数の把握などにいつも時間がかかってしまいます。

実務でテストコードを書く際はClaude Codeに頼り切りなので、もっと自分で意味を理解しながら高速でテストコードを書けるようになる努力が必要だと感じました。

JISOUのメンバー募集中!

プログラミングコーチングJISOUでは、新たなメンバーを募集しています。
日本一のアウトプットコミュニティでキャリアアップしませんか?
興味のある方は、ぜひホームページをのぞいてみてください!

4
7
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?