📑

【Next.js+ microCMS】Next.js環境構築とmicroCMS使ったJamstackなブログ構築を解説してみた

16 min read

はじめに

初めまして、おにかんと申します。
日々の業務や学習で学んだことをアウトプットするために、Next.js+microCMS構成で自分専用のブログサイトを構築してみました。

この記事では、Next.jsの環境構築とmicroCMS導入の解説をしていこうと思います。
何か間違っている点などございましたらご指摘いただけると幸いです。🙏

解説すること

  • 各パッケージの設定方法について
  • microCMSの導入方法と解説
  • Next.jsの環境構築手順と解説
  • ESlintPrettierについて

解説しないこと

  • Next.jsの機能について
  • 細かいスタイリングのあて方等

簡単な構成

最初に簡単な構成を書いておきます。

  • Next.js
  • TypeScript
  • ESLint
  • Prettier
  • Husky
  • lint-staged
  • microCMS

ではでは、次章から環境構築の手順を解説していきます〜

Next.jsプロジェクトを作成

ターミナル
$ npx create-next-app {プロジェクト名}

正常に作成されたら、下記のコマンドを実行してブラウザで確認してみましょう。

ターミナル
$ yarn dev

http://localhost:3000/にアクセスして下記のような画面が表示されていればOKです。

次にsrcディレクトリを作成して、pagesディレクトリとstylesディレクトリをsrcディレクトリの配下に移動させます。

ターミナル
$ mkdir src && mv pages src && mv styles src

TypeScriptの導入

次に、TypeScriptの導入を行なっていきます。
最初に設定ファイルを追加していきます。。

ターミナル
$ touch tsconfig.json

次に、設定ファイルの中身を書いていきます。tsconfig.jsonのオプションは色々あるので、公式ドキュメントを参照しながら書いていくことをお勧めします。(ちなみに、公式のドキュメントがめっちゃみやすい...)

tsconfig.json
{
  "compilerOptions": {
    "baseUrl": "./",
    "target": "es5",
    "module": "esnext",
    "jsx": "preserve",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "lib": [
      "dom",
      "dom.iterable",
      "esnext"
    ],
    "allowJs": true,
    "noEmit": true,
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true
  },
  "exclude": [
    "node_modules",
    "deployments",
    ".next",
    "out"
  ],
  "include": [
    "next-env.d.ts",
    "**/*.ts",
    "**/*.tsx"
  ]
}

tsconfigのオプション解説

以降からtsconfigのオプション解説をしていきます。特に必要のない方は飛ばしてください。

compilerOptions

"compilerOptions": {}

この中に設定のオプションを書いていきます。

baseUrl

"baseUrl": "./",

baseディレクトリを指定することで、ここに指定したディレクトリを起点として、モジュールをインポートすることができます。

例えば、下記のようなディレクトリ構造だったとします。

baseUrl
├── ex.ts
├── hello
│   └── world.ts
└── tsconfig.json

こうすることで、ex.tsファイルからhelloディレクトリ内のモジュールをインポートするには下記のように書くことができます。

ex.ts
import { helloWorld } from "hello/world";

これはbaseUrlが起点となって、絶対パス(のように)書くことができるからです。

target

"target": "es5",

どのバージョンのJSで出力するか指定しています。

module

出力するJSのモジュールを指定します。(CommonJSとか)

"module": "esnext",

jsx

"jsx": "preserve",

tsxファイルをコンパイルする形式を指定しています。preserveは下記のようにコンパイルされます。

コンパイル前

export const helloWorld = () => <h1>Hello world</h1>;

コンパイル後

export const helloWorld = () => <h1>Hello world</h1>;

つまりは変わらないってことです。デフォルトだと下記のようにコンパイルされます。

export const helloWorld = () => React.createElement("h1", null, "Hello world");

strict

"strict": true,

下記のオプションが全てtrueになります。

// "use strict";を全てのファイルの先頭行につける
--alwaysStrict

// 暗黙的なanyにエラーを出します。
--noImplicitAny

// 暗黙的なthisにエラーを出します。
--noImplicitThis

// nullの可能性のある(Nullableな)値の呼び出しにエラーを出す
--strictNullChecks

// bind, call, applyに厳密な型チェックが行われる
--strictBindCallApply

// 関数代入時の引数の型チェックで、Contravariantly(共変性と反変性)にチェックが入る
--strictFunctionTypes

// クラス定義するとき、インスタンス変数の初期化が宣言、コンストラクタの両方で行われていない場合にエラーを出す
--strictPropertyInitialization

esModuleInterop

"esModuleInterop": true,

例えば、CommonJS形式でexportされたものをrequireではなく、import~fromで読み込むことができる。(詳しい解説は割愛させていただきます。)

参考

skipLibCheck

"skipLibCheck": true,

*.d.tsファイルの型チェックをスキップします。node_modules配下(つまり、パッケージとかライブラリとか)の型チェックをスキップするのによく使われます。主な理由は下記。

  • コンパイルの時間を削減。
  • 複数のパッケージがバージョンの違う同一ライブラリの型定義ファイルを読み込んでしまってエラーが出る。

forceConsistentCasingInFileNames

"forceConsistentCasingInFileNames": true,

importした時にファイルの大文字、小文字を区別するかどうか。

lib

"lib": ["dom","dom.iterable","esnext"],

コンパイル時に使用する組み込みライブラリを指定。targetで指定しているバージョンの組み込みライブラリは暗黙的に指定されます。

allowJs

"allowJs": true,

jsjsxファイルをコンパイル対象に入れるかどうかを指定する。この場合、型チェックは行われないが、記法を指定のバージョンに変換するという作業が行われる。

noEmit

"noEmit": true

コンパイル結果を出力しない。型チェック機能をだけを使いたい時にtrueにする。実際のコンパイルをBabelなどに任せているときに使う。(Next.jsは内部的にBabelが使われています。)

Next.js includes the next/babel preset to your app,

引用

moduleResolution

"moduleResolution": "node",

モジュール解決の方法を指定。基本的にはnodeでOK。

resolveJsonModule

"resolveJsonModule": true,

jsonファイルを使用するとき、interfaceを用意しなくて良くなる。

参考

isolatedModules

"isolatedModules": true

全てのファイルを単一のモジュールとしてコンパイルする。

include

コンパイルする対象のファイルを指定する。

exclude

コンパイルしない対象のファイルを指定する。

駆け足になりましたが、tsconfigに関しては以上です。繰り返しになりますが、詳しい解説は公式ドキュメントをご参照ください。

TypeScript関連のパッケージのインストール

次にパッケージの追加をしていきます。

ターミナル
$ yarn add -D typescript @types/react @types/react-dom @types/node

これでTypeScriptの設定は完了です。

ESlintの導入

パッケージのインストール

ターミナル
$ yarn add --dev eslint typescript @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint-plugin-react eslint-plugin-react-hooks

開発環境でしか必要ないので、--devオプションをつけています。次に設定ファイルを準備していきます。

TypeScript用の設定ファイルの追加

ターミナル
$ touch tsconfig.eslint.json

設定ファイルの記述

僕の環境では下記のように書きました。

tsconfig.eslint.json
{
  "extends": "./tsconfig.json",  
  "includes": [
    "src/**/*.ts",
    "src/**/*.tsx",
    ".eslintrc.json",
  ],
  "exclude": [
    "node_modules",
    "dist"
  ]
}

ESlint用の設定ファイルの追加

ターミナル
$ touch .eslintrc.json

設定ファイルの記述

.eslintrc.json
{
  "root": true,
  "env": {
    "browser": true,
    "es6": true,
    "node": true
  },
  "settings": {
    "react": {
      "version": "detect"
    }
  },
  "parser": "@typescript-eslint/parser",
  "parserOptions": {
    "sourceType": "module",
    "ecmaVersion": 2020,
    "ecmaFeatures": {
      "jsx": true
    },
    "project": "./tsconfig.eslint.json"
  },
  "plugins": ["react", "@typescript-eslint"],
  "extends": [
    "eslint:recommended",
    "plugin:@typescript-eslint/eslint-recommended",
    "plugin:@typescript-eslint/recommended",
    "plugin:react/recommended",
    "prettier"
  ],
  "rules": {
    "@typescript-eslint/explicit-function-return-type": 0,
    "@typescript-eslint/no-explicit-any": 0,
    "@typescript-eslint/no-empty-function": 0,
    "react/prop-types": 0,
    "react/react-in-jsx-scope": 0,
    "no-empty-function": 0,
    "@typescript-eslint/ban-ts-comment": 0
  }
}

設定ファイルのプロパティ解説

ESlintのプロパティについて簡単に解説していきます。(必要のない方は飛ばしてください。)

root

"root": true,

ルートに設定ファイルがある場合は、trueにしておきます。

env

"env": {
  // ブラウザのグローバル変数を有効にする。
 "browser": true,
  // Node.jsのグローバル変数やスコープを有効にします
  "node": true
  // ECMAScript6のモジュールを除いた全ての機能が使用可能になります。
  "es6": true,
},

環境変数を設定します。(それぞれの解説は上記にコメントとして書いておきました。)

settings

"settings": {
  "react": {
  "version": "detect"
  }
},

ここではReactのバージョンを指定しています。detectにすることで、インストールしているバージョンを参照してくれます。

plugins

"plugins": [
  "react",
  "@typescript-eslint"
],

サードパーティ用のプラグインを追加しています。

extends

"extends": [
  "eslint:recommended",
  "plugin:@typescript-eslint/recommended",
  "plugin:react/recommended",
  "plugin:react-hooks/recommended",
  "prettier"
]

追加でまとめられたルールを設定できます。prettierについては後で解説します。

rules

"rules": {
  "@typescript-eslint/explicit-function-return-type": "off",
  "@typescript-eslint/explicit-module-boundary-types": "off",
  "react/prop-types": "off",
  "react/react-in-jsx-scope": "off",
  "@typescript-eslint/ban-ts-comment": "off"
}

各プロパティの設定を変更することができます。例外的に適応したくないルールがある場合は、ここの設定を変えるとプロジェクトごとにカスタマイズすることができます。

ESlintのスクリプトを記述

次に型チェックや修正を行うスクリプトを書いていきます。

package.json
{
  "scripts": {
    "check-types": "tsc --noEmit",
    "lint": "eslint src/**/*.{ts,tsx}",
    "lint:fix": "eslint src/**/*.{ts,tsx} --fix",
    "test-all": "npx yarn-run-all lint check-types lint:fix"
  },
}
  • check-types:型チェックを行います。(tsc
  • lint:リンターを実行します。(ESlint
  • lint:fix:修正可能であれば修正します。

以上で、ESlintの設定は完了です。次にコード整形のためのPrettierを導入していきます。

Prettierの導入

次にコードを整形してくれる、Prettierを導入していきます。まずはパッケージのインストールからです。

パッケージのインストール

ターミナル
$ yarn add --dev prettier eslint-config-prettier 

ESlintとの競合対策

実はこれはもう行なっていて、.eslintrc.jsonextendsprettierを入れることでいい感じに解決してくれています。詳しくはeslint-config-prettierのGithubをご参照ください。

設定ファイルの追加と記述

設定ファイルを追加します。

ターミナル
$ touch .prettierrc

追加した設定ファイルに設定内容を書き込んでいきます。

{
  "singleQuote": true,
  "semi": false,
  "trailingComma": "all"
}

実行スクリプトを記述

ESlintと同様にpackage.jsonにスクリプトを記述していきます。

package.json
"scripts": {
   "format": "prettier --write .",
    "test-all": "npx yarn-run-all lint check-types format lint:fix"
  },

以上でPrettierの導入は完了です。次にhuskylint-stagedを導入していきます!

husky

huskyはpre-commit(コミットの前)やpre-push(プッシュの前)にpackage.jsonに記述したスクリプトを実行してくれます。このタイミングでlintなどを実行することによって、コードの保守性が保たれるというわけです。

huskyを設定する

ターミナル
$ yarn husky install

このコマンドを実行することによって、.huskyディレクトリが作成され、設定の雛形が自動作成されます。次にpackage.jsonpreparehuskyのインストールコマンドを設定しておきます。(メンバー各自の手元でインストールされるように)

package.json
"scripts": {
  // 下記を追加
  "prepare": "husky install"
  },

pre-commitで実行されるように設定

まずは、package.jsonpre-commitで実行するコマンドを定義していきます。下記のように追加してください。

package.json
"devDependencies": {
   ・・・
   いろんなやつ
  },
  "lint-staged": {
    "*.{ts,tsx}": [
      "yarn lint",
      "yarn format",
      "yarn lint:fix"
    ]
  }

次にスクリプトをhuskyが実行するように設定していきます。まずは、pre-commit用のファイルを作成していきます。

ターミナル
$ touch .husky/pre-commit

このファイルに実行したいスクリプトを記述します。

pre-commit
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

yarn lint-staged

こうすることでコミット前にlint関係のスクリプトが実行されます。

pre-pushで実行されるように設定

push前にも同じように設定していきましょう。まずはファイルを作成します。

ターミナル
$ touch .husky/pre-push

push前には型チェックを走らせたいので今回は下記のような設定になります。

pre-push
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

yarn check-types

実行権限の付与

ターミナル
$ chmod a+x .husky/pre-push
$ chmod a+x .husky/pre-commit

lint-staged

こちらはほとんど設定をしてしまったので簡単です。パッケージをインストールしていきます。

ターミナル
$ yarn add --dev lint-staged

これでpackage.jsonに既に記述したlint-stagedの部分のスクリプトをステージに上がっているファイルに対してlintを実行することができます。

既存のファイルの修正

Next.jsはデフォルトではjsファイルが生成されるので、修正していきます。修正しないと、コミット時にlintが下記のように怒ってきます。

ターミナル
yarn lint:

Oops! Something went wrong! :(

ESLint: 7.28.0

No files matching the pattern "src/**/*.ts" were found.
Please check for typing mistakes in the pattern.

変更を加えるのは下記のファイルです。

  • _app.tsx_app.jsから)
  • index.tsxindex.jsから)

私は下記のように変更しました。

_app.tsx
import { NextPage } from 'next'
import { AppProps } from 'next/dist/next-server/lib/router/router'
import Head from 'next/head'

const MyApp: NextPage<AppProps> = ({ Component, pageProps }: AppProps) => {
  return (
    <>
      <Head>
        <meta name="viewport" content="initial-scale=1.0, width=device-width" />
      </Head>
      <Component {...pageProps} />
    </>
  )
}

export default MyApp
index.tsx
import { NextPage } from 'next'
import Head from 'next/head'

const Home: NextPage = () => {
  return (
    <>
      <Head>
        <title>Blog</title>
        <link rel="icon" href="/favicon.ico" />
      </Head>
      <div />
    </>
  )
}

export default Home

さらに、このままコミットしても同じようにlintがエラーを吐きます。

ターミナル
yarn lint:

Oops! Something went wrong! :(

ESLint: 7.28.0

No files matching the pattern "src/**/*.ts" were found.
Please check for typing mistakes in the pattern.

これは、src/**/*.tsのファイルが存在しないことを示しています。なので、一時的にこのパターンに該当するファイルを作成してあげればOK。

ターミナル
$ mkdir src/constants/index.ts
index.ts
export const hoge = 'hoge'

これでコミットしてもエラーが出なくなるはずです。

お疲れ様でした!ここまででNext.js環境構築は完了です。ここまでで基本的なものはさらえていると思います!次章からは主にmicroCMSとの連携方法について解説していきます。

microCMSを導入する

microCMSとは?

microCMSAPIベースのヘッドレスCMSです。 ブログ機能などはこのサービスを使うことでサーバーレスに構築することができます。

ではでは、導入手順を解説していきます!

アカウント登録+サービス登録

特につまりこともないと思うので、割愛させていただきます🙏

公式ドキュメント-signup

APIの作成

アカウント登録、サービス登録が済んだら、下記のような画面に遷移すると思います。

ここからAPIを作成できます。私のプロジェクトでは下記のようにしました。

  • API名:ブログ名
  • エンドポイント:articles

APIの型を選択

今回はリスト形式を選択しました。

APIのスキーマ定義

次にAPIのスキーマ(構造)を定義していきます。今回は下記の構成にしました。

  • title:テキストフィールド
  • body:リッチエディタ

コンテンツの追加

次にコンテンツを追加していきます。とりあえず中身は適当です。

API_KEYを環境変数に追加

まずは環境変数を管理するファイルを作成していきます。

ターミナル
$ touch .env.development.local

次に.gitignoreに設定を追加して環境変数関係のファイルをGitの管理下から除外します。


// 追加
.env*

設定からAPIKEYをコピペします。下記の場所にある値をコピーして

.env.development.localにコピーします。

.env.development.local
API_KEY=******

これで設定は完了です。実際にNext.jsでAPI経由でデータを取得できるか確認してみます。

API連携を確認

今回はお試しなので、無理矢理ルートでデータをフェッチしてみました。下記はサンプルコードです。型定義は適当ですが、お試しなのでご容赦ください🙇‍♂️

あと、エンドポイントは念の為、環境変数で管理したほうが良いかもしれません。

src/pages/index.tsx
import { NextPage } from 'next'
import Head from 'next/head'

type Article = {
  id: string,
  title: string,
  body: string
}

type props = {
  articles: Article[]
}

const Home: React.FC<props> = ({ articles }) => {
  return (
    <>
      <Head>
        <title>Blog</title>
        <link rel="icon" href="/favicon.ico" />
      </Head>
      {
        articles.map((article) => (
          <div key={article.id}>
            <h1>{article.title}</h1>C
            <p>{article.id}</p>
          </div>
        ))
      }
    </>
  )
}

export const getStaticProps = async () => {

  const key = {
    headers: { 'X-API-KEY': process.env.API_KEY || '', },
  };

  const res = await fetch('https://my-service/api/v1/articles', key)
  const data = await res.json();
  return {
    props: {
      articles: data.contents,
    },
  };
};

export default Home

では、http://localhost:3000/にアクセスしてみてください。下記のように、microCMSで設定した値が取得できていれば、成功です。

あとは各自、好きな方法でスタイリングあてて、Vercelにでもデプロイすれば自分だけのブログサイトの完成です!

最後に

解説は以上です。何かご指摘等ございましたら、コメントしていただけると幸いです!

参考記事

Discussion

ログインするとコメントできます