Next.js+TailwindCSS製 個人Blog構築の流れ -前編-

※本記事は、私が作成した [こちらのブログ](https://brightful.ga) ①〜④の内容をまとめて上げたものです。
語弊を恐れずなるべく平易に書こうとしておりますので、細かなニュアンスの違い等はご容赦頂ければと思います。誤植や記述内容の間違い等ございましたら遠慮なくコメント頂ければ幸いです。
[後編はこちら](https://qiita.com/Daisuke_PHPer/items/3d55e40f4cb5f89efdc1)

# ｜開発動機
Reactの基礎的な学習を終え、何か１つサンプル的なアプリを作りたいなと考えておりまして...
ふと、「情報発信するための自身のBlog」を持っていないなと思い、せっかくなら WordPress ではなく、自作の Blog を作ってみようと思い立ち開発に至りました。
ただ、サクッと作ってしまいたいという気持ちもあったので、Reactのフレームワークである`Next.js`を採用することにしました！
また、CSSが超絶苦手な私は`TailwindCSS`さんのお力を借りる事にいたしました^^;

### 【技術スタック】
```
 ・ Next.js v10.0.7
 ・ React v17.0.1
 ・ TailwindCSS v2.0.3
```

記事につきましては`Markdown`ファイルで作成したものを読み込み、レンダリングするようにしました。
読み込み・変換 用のライブラリとして以下のものを活用しました。

```
 ・ raw-loader v4.0.2
 ・ gray-matter v4.0.2
 ・ react-markdown v5.0.3
 ・ react-syntax-highlighter v15.4.3
```

・`raw-loader`はファイルを文字列としてインポートできるwebpackのローダーです。
・`gray-matter` は文字列またはファイルからFrontMatterを読み取り解析するものです。今回はMarkdownファイルの冒頭に以下のように書き込んでおくとJSONデータとして吐き出せるようにしました。  

```
---
title: 'ここにブログのタイトルを書きます'
author: 'ここには作者名を書きます'
date: '20XX年XX月XX日'
excerpt: 'ここに記事の概要を書き込んでおくとトップページで表示できます'
---
```

・`react-markdown`はローダーで読み取ったMarkdownファイルのテキストデータをHTMLに変換するためのライブラリです。
・`react-syntax-highlighter`はコードブロックのシンタックスハイライトを付けてくれるライブラリです。

以上がこのブログで使っているライブラリです。

### 【デプロイどうしようか】
今回構築したブログはDBを持たず、`/posts`ディレクトリにMarkdownファイルを設置してビルドするだけで完了という構成にしています。
つまり、静的なサイトを生成している(SSG)という感じです。
サーバー自体はレンタルサーバーなどでもよかったのですが、せっかくなら色んな事にチャレンジしたいと思い、`Netlify`のようなホスティングサービスを利用し、加え、GitHubとの連携をしてPUSH時に自動ビルドが走るようにしてみようと考えました！なんちゃってCI/CD環境の構築を目指してみたわけです^^;  

# ｜環境構築
それでは、早速`Next.js`のインストールから行なっていきたいと思います。  
とその前に、Next.js アプリを作成するためには `Node.js`をインストールしておく必要があります。[公式ドキュメント](https://nextjs.org/docs)によりますと、Node.js 10.13 以上のバージョンがインストールされていることが条件となります。この記事を進められる前に、Node.js のインストール作業をお願い致します。

### アプリ作成
では、早速アプリの作成をしていきましょう！
ターミナルを開いて、アプリを作成したいディレクトリに移動してください。移動先で以下のコマンドを入力してください。

```
 npx create-next-app [project-name]
```
`[project-name]`の部分はご自身でお好きな名前を付けてください！[]は不要です。
以下のような文言が出ていれば完了です！

```
Success! Created sample at /保存先/[project-name]
Inside that directory, you can run several commands:

  yarn dev
    Starts the development server.

  yarn build
    Builds the app for production.

  yarn start
    Runs the built app in production mode.

We suggest that you begin by typing:

  cd [project-name]
  yarn dev
```
一度`Next.js`のTOP画面を拝んでおきましょう！


```
 cd [project-name]  // アプリのディレクトリに移動して
 npm run dev        // または yarn dev でもいいです
```
すると、コンパイルが始まって、しばらく経つと`compiled successfully`という文字が表示されると思いますので、ブラウザから `http://localhost:3000` にアクセスすると以下のようなTOPページが確認できるかと思います！
![top_page.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/641212/a769fec7-3e19-1452-3af3-f15dc73e4f44.png)
やりましたね！ ^^v
次に`TailwindCSS`のセットアップに移りましょう！
ターミナルにて以下のコマンドを入力
`(※以下のコマンドはプロジェクトディレクトリ内で入力してください)`

```
npm install -D tailwindcss@latest postcss@latest autoprefixer@latest
```
次にコンフィグファイルを作成していきます。
以下のコマンドを入力すると`tailwind.config.js`と`postcss.config.js`が自動的に生成されます。

```
npx tailwindcss init -p
```
生成された`tailwind.config.js`ファイルを編集していきましょう。生成時は`purge`の値が空っぽになっているかと思いますので、以下のように追記します。

``` tailwind.config.js
module.exports = {
  purge: ['./pages/**/*.{js,ts,jsx,tsx}', './components/**/*.{js,ts,jsx,tsx}'],
  darkMode: false, // or 'media' or 'class'
  theme: {
    extend: {},
  },
  variants: {
    extend: {},
  },
  plugins: [],
}
```
続いて`CSS`ファイルにTailwindCSSの設定を読み込むように変更します。
`/styles`ディレクトリ内にある`globals.css`を開き、中身を全て消去し、以下の３行に書き換えてください。

``` globals.css
@tailwind base;
@tailwind components;
@tailwind utilities;
```
以上で TailwindCSS の設定は完了です！ ちなみに、このCSSファイルは`pages/_app.js`の中でインポートしています。
もし、他のCSSファイルを作成した時は、適宜読み込みを行なってください！
ということで、一応確認のためと Next.js の動きの確認のために`Hello World!!`を表示させてみましょう！
`pages/index.js`ファイルを開いてください。以下のようになっているかと思います。

``` index.js
import Head from 'next/head'
import styles from '../styles/Home.module.css'

export default function Home() {
  return (
    <div className={styles.container}>
      <Head>
        ...
        中略
        ...
      </footer>
    </div>
  )
}
```
このファイルのインポート部分の削除と`Home()`ファンクションの return の中の`<Head>`タグから`</footer>`を全て消去し、以下のように書き換えてください。

``` index.js
export default function Home() {
  return (
    <div className="text-red-400">
      Hello World!
    </div>
  )
}
```
変更後、ターミナルで`npm run dev`を実行してください。※コマンドを実行する場所はアプリのあるディレクトリ内でです。  
実行後に`http://localhost:3000`にアクセスすると...
![hello_world.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/641212/837eb06f-39dd-b108-9414-d6162c6e739f.png)
`赤字`で Hello World!! が表示されましたね ^^v  
React で class を付与させるには class="hoge" と書くのではなく、`className="hoge"`と書く必要があります。  
今回の divタグ内の`text-red-400`は TailwindCSS のクラスで、文字色を赤の400にするというものです。色見本については [TailwindCSS公式ドキュメント](https://tailwindcss.com/docs/customizing-colors) で確認してみてください！また、他のどのようなクラスがあるのかも見てみてください〜
 
続いて、簡単にルーティングについても確認しておきたいと思います。
`npm run dev` は止めずに、そのままで進めていきましょう！止めた方は、再度実行しておいてください！
それでは、まず`pages`ディレクトリ内に`about.js`ファイルを作成してください。そして、以下のように書き加えてください。

``` about.js
const About = () => {
  return (
    <div className="text-center mt-10">
      about
    </div>
  )
}
export default About
```
変更を保存後、`http://localhost:3000/about`にアクセスすると about という文字列が表示されているかと思います。
Next.js は /pages ディレクトリに js のエクスポートファイルを設置すると、ビルド時にファイル名をURLとしてルーティングに組み込んでくれます^^（便利！）

# ｜Component分割とレイアウトの作成
コンポーネントの分割の思想については、様々な考え方がございますので、この記事内では深く言及することは避けます。
かなりフランクに捉えて頂いて、分割するにはどうすればいいかという部分のみ掴んでいただければ幸いです。

では、早速コーディングをしていきましょう！と、その前に今回使うことのない、不要なファイル達を削除しておきたいと思います。
削除後に残るディレクトリやファイルは以下のようになりますので、それ以外のファイルは削除してしまいましょう！  
一応具体的に削除するファイルやディレクトリを挙げておくと、`public/vercel.svg`、`styles/Home.module.css`と`api`ディレクトリは中の`hello.js`ごと削除してしまってください。

```
.next/
node_modules/
pages/
  _app.js
  about.js
  index.js
public/
  favicon.ico
styles/
  global.css
.gitignore
package-lock.json
package.json
postcss.config.js
README.md
tailwind.config.js
yarn.lock
```
それでは、ここからファイルやディレクトリを作成していきます！

### ●簡易ファイルローディングの体験
コンポーネント分割の前に、JSONファイルを読み込んで画面にデータを表示するということをしてみましょう。
tailwind.config.js と同じ階層に`siteconfig.json`ファイルを作成し、以下のような記述をします。

``` siteconfig.json
{
  "title": "ブログのタイトルを記述",
  "description": "ブログの概要をここに記述"
}
```
記述を終えたら次に`index.js`ファイルを編集していきます。
先ほど作成した siteconfig.json のデータをインポートしていきます。以下のように中身を書き換えてしまってください。

``` index.js
const Index = ({ title, description }) => {
  return (
    <div className="text-center mt-10">
      site_title: {title} <br />
      description: {description}
    </div>
  )
}
export default Index

export async function getStaticProps() {
  const configData = await import(`../siteconfig.json`)

  return {
    props: {
      title: configData.default.title,
      description: configData.default.description,
    },
  }
}
```
![siteconfig.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/641212/9f12fa85-8cd9-ef0a-07c9-6e322fa91757.png)
上の画像ような形でJSONファイル内に書かれた内容を表示することができたでしょうか？
今回記述した内容の中で`getStaticProps()`について`簡単に`解説しておきます。  
getStaticProps()はNext.jsが用意しているファンクションで、そのファンクション内で返却したデータをビルド時にページコンポーネントに引き渡す役割を担ってくれています。今回のソースコード内でのことをザックリと捉えると、props というものの中に`title`と`description`という変数で siteconfig.json のデータを取得・格納し、Indexページのコンポーネント内に引き渡すということを行なっています。それを経て画面に表示されているということなんですね^^  
`getStaticProps()`について、詳しくは[Next公式データフェッチ](https://nextjs.org/docs/basic-features/data-fetching)の部分をご参照いただければと思います。

### ●レイアウトの作成
それでは、今度こそレイアウトのコンポーネントを作成していきましょう！
まず、`components`ディレクトリをpagesと同階層に作成してください。その中に、`Header.js`、`Footer.js`、`Layout.js`、`PostList.js`の４ファイルを空の状態でいいので作成してください。
そのあと、`Layout.js`を以下のように編集していってください。

``` Layout.js
import Head from 'next/head'
import Link from 'next/link'

export default function Layout({ children, pageTitle, ...props }) {
  return (
    <>
      <Head>
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <title>{pageTitle}</title>
      </Head>
      <section className="text-center mt-10">
        <header className="bg-blue-300 h-10">
          <nav>
            <Link href="/">
              <a className="pr-10">My Blog</a>
            </Link>
            <Link href="/about">
              <a>About</a>
            </Link>
          </nav>
        </header>
        <div className="bg-yellow-300 h-20">{children}</div>
      </section>
      <footer className="bg-green-300 text-center h-10">Footerのテキスト</footer>
    </>
  )
}
```
- `<Head>`コンポーネント

まず、`next/head`から読み込んでいる`<Head>`コンポーネント内ですが、`<meta>`情報や`<title>`などを書き込むことができます。
あらかじめサイト全体で読み込ませておきたい情報は、ここに置いておきましょう！

- `<Link>`コンポーネント

続いて、`next/link`から読み込んでいる`<Link>`コンポーネント内ですが、シンプルに捉えると、飛ばしたい先のリンクを作成することができます。

- `{children}`パーツ

最後に`{children}`の部分ですが、各ページでのコンテンツを表示する場所です。
詳しくは後ほど書く`index.js`で確認していただければと思いますが、各pagesのファイルの中でLayoutコンポーネントで挟んであげれば、
`{children}`の部分にコンテンツを表示することができます。  
では、`pages/index.js`で先ほどの`Layout`コンポーネントを読み込み、コンテンツを表示させてみましょう！以下のように変更してください。

``` index.js
import Layout from '../components/Layout'

const Index = ({ title, description }) => {
  return (
      <Layout pageTitle={title}>
        <div>ここがLayoutコンポーネントのChildren部分です</div>
        <div>{description}</div>
      </Layout>
  )
}
export default Index

export async function getStaticProps() {
  const configData = await import(`../siteconfig.json`)
  return {
    props: {
      title: configData.default.title,
      description: configData.default.description,
    },
  }
}
```
編集を保存したあと、`http://localhost:3000`にアクセスすると以下のような画面が表示されていれば成功です！
![layout.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/641212/d5db76b3-8aa7-af3e-05d8-df32865df481.png)

# ｜Component分割
先ほども少し触れたのですが、コンポーネントの分割に関しては、様々な思想がございますので、この記事内では深くは触れません。
分割ってこうやってやるんだな〜ぐらいの捉え方をして頂けるとありがたいです。  
それでは、前回作成した`Layout`コンポーネントの分割を行なっていきましょう！前回の時点での components ディレクトリの中は以下のような状態かと思います。

```
components/
  - Footer.js // 空ファイル
  - Header.js // 空ファイル
  - Layout.js
  - PostList.js // 空ファイル
```
また、`Layout.js`ファイル内の Layoutファンクションが返却している部分は以下のようになっているかと思います。

```Layout.js
// Layoutファンクション return()内
  <Head>
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>{pageTitle}</title>
  </Head>
  <section className="text-center mt-10">
    <header className="bg-blue-300 h-10">
      <nav>
        <Link href="/">
          <a className="pr-10">My Blog</a>
        </Link>
        <Link href="/about">
          <a>About</a>
        </Link>
      </nav>
    </header>
    <div className="bg-yellow-300 h-20">{children}</div>
  </section>
  <footer className="bg-green-300 text-center h-10">Footerのテキスト</footer>
```
この部分を`Header.js`と`Footer.js`に分割していきたいと思います。  
まずは Header から分けていきましょう！先ほどのコードの`<header>`タグの始まりから終わりまでを切り取りし、Header.jsファイルを以下のように書き加えます。

```Header.js
import Link from 'next/link'
export default function Header() {
  return (
    <header className="bg-blue-300  h-10">
      <nav>
        <Link href="/">
          <a className="pr-10">My Blog</a>
        </Link>
        <Link href="/about">
          <a>About</a>
        </Link>
      </nav>
    </header>
  )
}
```
その次に`Layout.js`ファイル内で、今ほど作った Header コンポーネントを読み込んでいきましょう！

```Layout.js
import Head from 'next/head'
import Header from '../components/Header' // ←追記

export default function Layout({ children, pageTitle }) {
  return (
    <>
      <Head>
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <title>{pageTitle}</title>
      </Head>
      <section className="text-center">
        <Header />  {/* ←変更箇所 */}
        <div className="bg-yellow-300 h-20">{children}</div>
      </section>
      <footer className="bg-green-300 text-center h-10">Footerのテキスト</footer>
    </>
  )
}
```
一度画面を確認してみてください！前回と同様に以下のような画面が表示されましたか？
![layout.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/641212/d5db76b3-8aa7-af3e-05d8-df32865df481.png)
確認ができましたら、Footerも同様に分割してみてください！やり方は同じなので、一度ご自身でチャレンジしてみてくださいね ^^b

では、私の方もやっていきます！
`Footer.js`ファイルを以下のように書き加えます。

```Footer.js
export default function Footer() {
  return (
    <footer className="bg-green-300 text-center h-10">
      Footerのテキスト
    </footer>
  )
}
```
Header の時と同様に `Layout.js` で読み込みます。

```Layout.js
import Head from 'next/head'
import Header from '../components/Header'
import Footer from '../components/Footer' // ←追記

export default function Layout({ children, pageTitle }) {
  return (
    <>
      <Head>
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <title>{pageTitle}</title>
      </Head>
      <section className="text-center">
        <Header />
        <div className="bg-yellow-300 h-20">{children}</div>
      </section>
      <Footer />  {/* ←変更箇所 */}
    </>
  )
}
```
再度画面を確認し、同じ状態を保てていれば成功です！
以上のような感じでコンポーネントの分割を行なっていくことができます！
なぜ分割を行うの？という疑問が浮かんだ方もいらっしゃると思いますので、この作ったものを他のページで再利用する中で少しでも良さを実感してみようと思います！
ということで、"about" としか表示のされないアバウトページにレイアウトを反映させてみましょう！`about.js`を以下のように編集してみてください！

```about.js
import Layout from '../components/Layout'

const About = () => {
  return (
    <Layout pageTitle="ここはアバウトページだよ">
      <div>Aboutページのコンテンツをここに書いていく！</div>
    </Layout>
  )
}
export default About
```
これだけの変更で以下の画像のようにレイアウトの反映ができました！
![about.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/641212/e3b2b3dc-e7d9-d2f8-499c-c53aa9340de1.png)
ん？何か違和感が...
![title.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/641212/9bc0572d-fbb9-8caf-716c-c746732b39de.png)
そうなんです！タイトルも変わっているんです！  
実はレイアウトコンポーネントで挟む時に`<Layout pageTitle="ここはアバウトページだよ">`という感じでレイアウトコンポーネントに`pageTitle`を引き渡しています。レイアウトコンポーネント側にはこのpageTitleの受付口を用意していましたので、「はいはい〜タイトルの変更ね！」という感じで書き換えてくれているわけですね！

このように、コンポーネントを分割し、共通パーツとして作成しておくと、複数ページ、複数箇所での使い回しができるので便利だし、コーディングの量が減るので良いよね！という感じだと思っていただければ ^^v  

### ●動的ルート
それでは続いて動的ルーティングについて確認しましょう！
今までの状態で作られているルーティングは`/`と`/about`の２ページ分です。
必要に応じて`pages`ディレクトリにページを追加していけば良いのはご理解いただけたかと思います。
ただ、ブログ記事などのように、画面のデザインはほぼ変わらず、コンテンツの部分のみ変わっていくようなページに関しては、記事作成ごとに、js ファイルを生成するのはどうも手間な気がしますよね...  
そこで、`Dynamic Routes`(動的ルート)の出番です。`/post/3`や`/post/156`、`/post/nextjs`のような記事のIDや記事のタイトルといった固有の情報を含んだURLにアクセスされた場合に、記事のデータを取得し表示できるようにしてあげると、毎回毎回記事を書くたびにjsファイル（ページコンポーネント）を生成しなくても済むようになりますね！^^v  
ということで、簡易的に動的ルートを体験してみましょう！
`pages`ディレクトリ内に`post`ディレクトリを作成し、その中に`[post].js`ファイルを作成してください。

```
pages/
  post/         // ←作成
   - [post].js  // ←作成
 - _app.js
 - about.js
 - index.js
```
その次に、`[post].js`の中身を以下のようにしてください。

```[post].js
import { useRouter } from 'next/router'
const Post = () => {
  const router = useRouter()
  const { post } = router.query
  return (
    <p className="m-10">
      Post: {post}
    </p>
  )
}
export default Post
```
ファイルを保存し`http://localhost:3000/post/1`や`http://localhost:3000/post/nextjs`にアクセスしてみてください。
Post: 1 や Post: nextjs という文字が画面に表示されたかと思います。  
簡単に解説しておきますと、基本的に Next.js では pages ディレクトリ以下のファイル名(.jsより前の部分)がルーティングを構成する要素となりますので、今回作成した [post].js で考えると、`[post]`の部分がルーティングを構成する要素となります。
Next.js では、このあたりを動的にキャッチし、クエリオブジェクトとして持つことができるんですね！
また、複数の動的な値をキャッチしたい場合は、先ほど作成した post ディレクトリの中に`[hoge]`ディレクトリを作成し、その中に`[huga].js`を作成すると、`http://localhost:3000/post/today/comment`というリクエストに対して today や comment と行った値をキャッチすることもできます。
その他いくつかのパターンが存在しますので、詳しくは[Next公式動的ルート](https://nextjs.org/docs/routing/dynamic-routes)をご確認いただければと思います。

以上、少し長くなってきましたので、今回はここまでにして、次回に続けたいと思います。
次回は、この動的ルートを活用し、ルーティングの作成と、該当するマークダウンファイル（ブログ記事）の読み取りを行っていきたいと思います。最終的にはレイアウトを整え、サーバーにデプロイまでいきましょう！
乞うご期待！

[続きはこちら](https://qiita.com/Daisuke_PHPer/items/3d55e40f4cb5f89efdc1)