【Next.js】SSGの実装方法

## はじめに
本記事は、プログラミング初学者が学習を進めていて疑問に思った点について調べた結果を備忘録も兼ねてまとめたものです。  
そのため、記事の内容に誤りが含まれている可能性があります。ご容赦ください。  
間違いを見つけた方は、お手数ですが、ご指摘いただけますと幸いです。  

## SSGの実装方法
以下のようにすることでSSGを実装できます。

### 表示するデータを準備する
まずは、表示するデータを準備します。  
以下のように`posts`フォルダを作成し、その中に`sample1.md`と`sample2.md`を作成します。  
また`public`フォルダに`images`フォルダを作成、`sample1.jpg`と`sample2.jpg`の２つを保存します。

![スクリーンショット 2022-04-19 2.36.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342443/2c56a682-c821-a2a1-216f-1de35df07944.png)


`sample1.md`と`sample2.md`の内容は以下のようになっています。

```sample1.md
---
title: "sample1"
thumbnail: "/images/sample1.jpg"
---

## サンプル1の文章
```

```sample2.md
---
title: "sample2"
thumbnail: "/images/sample2.jpg"
---

## サンプル2の文章
```

### MD解析のライブラリを作る
マークダウン解析のためのライブラリを作成していきます。  
`lib`フォルダを作成し、`post.js`を作成します。

![スクリーンショット 2022-04-19 2.47.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342443/a925ba81-c6af-4cf6-c71d-b9e7f8c76adb.png)


`post.js`に以下のように記述します。

コメントで各記述の説明を入れています。



```post.js
// ディレクトリを取得するためにインポート
import path from "path";
import fs from "fs";
// マークダウンを解析するためにインポート
import matter from "gray-matter";

// カレントディレクトリとpostsのpathを結合→cwd/posts
const postsDirectory = path.join(process.cwd(), "posts");

// mdファイルのデータを取り出すための関数
export function getPostsData() {
  // postDirectory(= cwd/postsディレクトリ内のファイル名が配列で入っている)をfs.readdirSyncで読み出してfileNamesに格納
  const fileNames = fs.readdirSync(postsDirectory);
  // fileNamesからファイル名を一つずつ取り出す
  const allPostData = fileNames.map((fileName) => {
    // 取り出す際にreplace関数で拡張子を取り除く
    // 第一引数に/\.md$/を指定し、第二引数に空文字を指定することで除外することができる
    const id = fileName.replace(/\.md$/, "");  // ファイル名＝id

    // マークダウンファイルを文字列として読み取る
    // postDirectory(= cwd/posts)とfileName(= ssg-ssr等)を結合してfullPathに格納
    const fullPath = path.join(postsDirectory, fileName);
    // それぞれの.mdファイルの内容を読み出し。文字列で読み出したいのでutf8を指定
    const fileContents= fs.readFileSync(fullPath, "utf8");
    // マークダウンファイルのメタデータの分析
    // matterResultには.mdのtitle等がオブシェトの配列として格納されている
    const matterResult = matter(fileContents);
    // idおよびデータを返す(allPostData)
    return {
      id,
      ...matterResult.data,
    }
  });
  // getPostsDataに関する返り値
  return allPostData;
}
```

インポートしている`gray-matter`はインストールする必要があるため、ターミナルで以下を実行してください。

```:ターミナル
$ npm i gray-matter
```

### `getStaticProps`でSSGを実装する
`getStaticProps`を使用してSSGを実装します。
`index.js`を以下のように記述します。

```index.js
import Head from 'next/head'
import Image from 'next/image'
import Link from 'next/link'
import styles from "../styles/Home.module.css"
import { getPostsData } from '../lib/post'  // 追記


// SSGのための記述
export async function getStaticProps() {
  // データの取得先を指定
  const allPostsData = getPostsData();  // id, title, date, thumbnail のpathが格納されている
// 以下のように記述することでallPostsDataをHomeにpropsとして渡すことができるようになる。
  return {
    props: {
      allPostsData,
    }
  };
}

export default function Home({ allPostsData }) {
  return (
    <>
      <Head>
        <title>Sample App</title>
        <meta name="description" content="Generated by create next app" />
        <link rel="icon" href="/favicon.ico" />
      </Head>
        <div className={styles.main}>
          <h1>
            Welcome to
            <Link href="/posts/samplePost">
              <a> Sample App!!</a>
            </Link>
          </h1>
            {/* map関数で受け取ったデータを出力する */}
            {allPostsData.map(({id, title, thumbnail}) => (
              <article key={id}>
                <Link href={`/posts/${id}`}>
                  <Image
                    src={`${thumbnail}`}
                    width={500} height={500}
                    objectFit="contain"
                  />
                </Link>
                <Link href={`/posts/${id}`}>
                  <a className={styles.title}>{title}</a>
                </Link>
              </article>
            ))}
        </div>
    </>
  )
}


```

すると、以下のように表示されます。

![1D31EB97-1DA9-4A54-B0E1-35CDB25D5CD0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342443/c9f1a228-97fd-feda-4279-b1c4168723ca.png)