Loading...

Next.jsでブログを作った!

なぜブログを作ろうと思ったか

日々の思ったことを残すため

日々の生活で得る情報を、文章としてアウトプットするための場が欲しかったからです。インプットした情報を文章でアウトプットすると、頭の中が整理ができて、わかったこと・わからなかったことが明確になります。
日記や備忘が主な目的ですが、情報のアウトプットで少しでも誰かの役に立てたら嬉しいです。

ReactやNext.jsの勉強のため

スキルアップのためにブログを自作しました。仕事ではWeb業界で流行の技術に触れる機会が無く、強く危機感を抱きました😱 アウトプットだけなら既存のサービスを利用しても良いのですが、スキルを身に着ける点で自分でブログを作りたいと思いました。

ReactとNext.jsを選択した理由は次の3点です。

  • 流行しているから
  • 無料で使えるから
  • サクサク動くから

作ったもの(成果物)

当ブログです。

技術構成

Jamstack構成です。DB、CSSフレームワーク、CMSなどは使っていません。

Reactとは

Reactは、UI(ユーザーインターフェース)を構築するためのライブラリです。生みの親のMeta社(旧Facebook社)はもちろん、多くの有名IT企業がReactで開発をしています。JavaScriptまたはTypeScriptで記述できます。

今までのWebサイトは、画面をHTMLとJavaScriptを分けて記述して実装していたのですが、Reactは「HTMLもJavaScriptで書いたらいいのでは🤔」という発想から生まれたそうです。

Reactは、機能ごとのパーツ=コンポーネントを作って組み合わせたり使いまわして画面を作るコンポーネント指向のライブラリです。コンポーネントを使って感じたメリットは次の3点です。

  • 影響範囲が狭い
  • 流用できる・拡張しやすい
  • デザインの統一がしやすい

大きなプロジェクトほど、このメリットを強く実感できると思います。

Next.jsとは

Next.jsは、Reactをベースとしたフロントエンドフレームワークです。

多くの機能を提供してくれる素晴らしいフレームワークです。React単体では手間のかかる部分の実装を簡単にしてくれます。
代表的な機能は、

  • サーバーサイドでのページのレンダリング
  • ディレクトリでのルーティング
  • 最適化によるパフォーマンス向上

です。

サーバーサイドレンダリング(SSR)やスタティックサイト生成(SSG)によってページをサーバーサイドで事前にレンダリングすることで、初回のロード時間を短縮し、パフォーマンスを向上できます。サクサクです。

当ブログ作成前に、チュートリアルをやりました。このチュートリアルで作成したブログをもとに、機能の実装やCSSの追加などを施しながら当ブログを作成しました。

Pages Router | Next.js
nextjs.org
Pages Router | Next.js

TypeScriptとは

TypeScriptは、JavaScriptの拡張版です。構文はJavaScriptと基本的に同じですが、型をつけられるため正確かつ安全なコードを書くことができます。

JavaScriptは、変数の型を定義する必要がないため、想定していない型のデータを入れてエラーが出たり、エラーが出てから原因を調べなければならなかったりと、何かと非効率でした😢
TypeScriptは、この問題を解決するために、型をつけられます。型を見ればどんな値が入るのかわかりますし、事前にエラーを表示してくれます。おかげでコードの品質が上がり保守性も高まります。

引数にnumber型、戻り値にstring型を定義した例です。:の後ろに型を記述します。引数がnumber型ではない場合、エラーが出ます。

const isOddOrEven = (x: number): string => {
  if (x % 2 === 0) {
    return `${x}は偶数です`
  } else {
    return `${x}は奇数です`
  }
}
 
console.log(isOddOrEven(1))
// OK! → "1は奇数です"
 
console.log(isOddOrEven('abc'))
// Error! → Argument of type 'string' is not assignable to parameter of type 'number'.

MDXとは

MDXは、Markdownの拡張版です。通常のMarkdownと同じように文章を書ける上、JavaScriptのコードやReactコンポーネントを埋め込むこともできます。拡張子は.mdxです。

Markdownの中で、importを使ったりできます。当ブログでは、リンクカードなどの自作のReactコンポーネントをMDXファイル内で使用しています。下記は一例です。

import Button from 'button.js'
 
# 見出しです
 
本文です。
 
<Button>ボタンです</Button>

開発環境

  • Node.jsをインストール済み
  • VSCodeをインストール済み
  • 楽しむ心の余裕を持つ

機能の実装

開設時までに実装した機能は下記です。

  • MDX形式での記事の作成
  • カテゴリーやタグでの記事分類
  • カテゴリーおよびタグ別記事一覧
  • ページネーション
  • 記事の検索

MDX形式での記事の作成

Next.jsのチュートリアルではMarkdownを使用していますが、Reactコンポーネントを使いたいからMDXを採用しました。

MDXを使うためにnext-mdx-remoteを使用しています。記事のMDXファイルは下記のように記述しています。
Frontmatter(ファイル冒頭の---で囲まれた部分。yaml形式)には、記事タイトル、日付、タグ、サムネイル画像URLを記述しています。本文はMarkdown形式で書いています。

---
title: 記事タイトル
date: '2024-01-01'
tags: [タグ1, タグ2, タグ3]
thumbnail: test.jpg
---
 
## 見出しだよ
 
記事本文だよ。

カテゴリーの記事分類

ブログサービスによくある、記事をカテゴリーで分けられる機能です。
カテゴリーごとにディレクトリを作成して記事を管理しています。

先人の知恵をお借りしたかったのですが、Next.jsで実装している例がなかなか見つからず……。当ブログでの実装例を一部ですが紹介します。

ディレクトリ構成

記事のファイルを管理するためのディレクトリ_postsをプロジェクトの直下に作成します。
次に、_posts下にカテゴリーのディレクトリを作ります。そして、その下に記事のファイルを作成します。
次のようなディレクトリ構成で記事を管理しています。

_posts
  ├── category1
   ├── post1.mdx
   ├── post2.mdx
   └── post3.mdx
  ├── category2
   ├── post1.mdx
   ├── post2.mdx
   └── post3.mdx
  └── category3
      ├── post1.mdx
      ├── post2.mdx
      └── post3.mdx

記事ページのディレクトリ構成は次のようになっています。_posts下のディレクトリ名とファイル名は各記事にアクセスする際のURLの一部になります。この記事だと/post/tech/create-myblogになります。

app
  └──post
      └── [category]
            └── [slug]
                  └── page.tsx

page.tsx内のgenerateStaticParams()で、カテゴリーと記事ファイル名を使ってルーティングを設定します。カテゴリーのディレクトリ名とファイル名を配列で渡します。

export async function generateStaticParams() {
  const params = await getAllPostParams()
  // [{ category: 'category1', slug: 'post1' },{ category: 'category1', slug: 'post2' },{ category: 'category1', slug: 'post3' },{ category: 'category2', slug: 'post1' },...]
  return params.map((param) => ({ category: param.category, slug: param.slug }))
}

カテゴリー名の管理

カテゴリーは、ディレクトリ名と表示名をyamlファイルで管理しています。プロジェクト直下にmetaディレクトリを作成し、その下にcategories.yamlを作成します。

yamlファイルは次のように記述します。slugはディレクトリ名、nameはサイトでの表示名です。

categories:
  - slug: category1
    name: カテゴリー1
  - slug: category2
    name: カテゴリー2
  - slug: category3
    name: カテゴリー3

yamlファイルを扱うために、yaml-loaderをインストールします。

npm install yaml-loader

next.config.jsファイルに追記して使えるようにします。

module.exports = {
  webpack: function (config) {
    config.module.rules.push({
      test: /\.ya?ml$/,
      oneOf: [
        {
          resourceQuery: /stream/,
          options: { asStream: true },
          loader: 'yaml-loader',
        },
        { loader: 'yaml-loader' },
      ],
    })
    return config
  },
}

categories.tsを作成して、カテゴリーを取得する処理を作ります。記事の取得やルーティングのために使用します。なお、yamlファイルに記載していないディレクトリは取得しません。

import categories from '@meta/categories.yaml'
 
type CategoryContent = {
  slug: string
  name: string
}
 
function generateCategoryMap(): { [key: string]: CategoryContent } {
  const result: { [key: string]: CategoryContent } = {}
  for (const category of categories.categories) {
    result[category.slug] = category
  }
  return result
}

カテゴリーおよびタグ別記事一覧

一覧ページです。カテゴリーに一致する記事、タグを含む記事を取得して一覧で表示します。

ページネーションは下記を参考にしました。

[Next.js]App Router時代の静的サイトの作り方
Zenn
[Next.js]App Router時代の静的サイトの作り方

App Routerでのルーティングと画面の表示

当初はPage Routerで作っていましたが、App Routerで作り直しました。App Routerのほうがわかりやすくコンポーネントやデータフェッチを扱える印象です。Page Routerでは、GetStaticPropsで取得したデータをコンポーネントに渡してリレーしていたのですが、App Routerでそのあたりの無駄な処理を減らせました。

デザイン

お菓子のカスタードプリンが好きなので、Webサイトのデザインにしました🍮🍮🍮🍮🍮

CSSフレームワークは使っていません。CSSは勉強のためにチマチマと書きました。SVGファイルも作成したりフリー素材から拝借しました。楽しいです。

好き勝手に作っているので、良いデザインを学ばないといけないと痛感しています。

参考書籍・サイト

公式ドキュメントやさまざまなサイトを参考にしました。偉大な先人たちに感謝です。

ES2015(ES6)以降のJavaScriptの基本の勉強になった書籍です。いまいち自信が無かったため、基本から学び直しました。

https://www.amazon.co.jp/dp/B09BV2HGN3

Next.jsのApp Routerの参考になりました。

[Next.js]App Router時代の静的サイトの作り方
Zenn
[Next.js]App Router時代の静的サイトの作り方

おわりに

Reactを使ってみて、開発スピードの向上を感じました。コンポーネントを使いまわせるだけでもすごい便利です。Next.jsも、設定などが複雑ではなく、SSGなどを実現する機能もシンプルでわかりやすいため、開発しやすいと思いました。大きな労力を必要とせずにサクサク動くWebサイトを作成できて、楽しいです。

まだまだわかっていない部分ばかりですし、どんどんアップデートされていっていますので、勉強の余地しかないです。当ブログにも機能を追加していきたいです。

Share