ヘッドレスCMS × Static Site GeneratorでJamstackな爆速表示サイトを作ってみるチュートリアル

Jamstackについて調べてみたので、自分なりにまとめておきます。

## はじめに
### Jamstackとは:thinking:
- 公式サイト：https://jamstack.org/
- ファイルを事前(デプロイ時)にレンダリングし、静的サイトとしてCDN経由で配信する手法。
- **J**(avaScript)**A**(PIs)**M**(arkup)の頭文字を並べたもの。
    - **JavaScript**：動的コンテンツを取得し、描画するJavaScript部分。
    - **APIs**：動的コンテンツを提供するAPI部分。
    - **Markup**：デプロイ時にビルドされた静的サイト部分。
- 以下のような特徴がある
    - 静的サイトをCDN経由で配信するため、より**高速**。
    - 悪意のある攻撃の対象領域がAPI部分のみになるため、**セキュリティ対策がしやすい**。　
    - スケーリングする際はCDNのみでよいため、**スケーリングが安価かつ容易**。
    - **フロントエンド側とAPI側が疎結合**となるため、それぞれ技術選定の自由度があがる。（デバッグやテストもしやすい。）
- 最近表記が「JAMstack」から「**Jamstack**」に変わった。

今回はAPI部分に **ヘッドレスCMS** 、JavaScript/Markup部分に **SSG（Static Site Generator）** を利用する。

### ヘッドレスCMSとは:thinking:
- コンテンツを管理する管理画面と、コンテンツを(JSON形式などで)公開するAPIからなる。
- 以下のようなものがある。
    - [Wordpress](https://ja.wordpress.org/) (ヘッドレスモード)
    - [Contentful](https://www.contentful.com/)
    - [microCMS](https://microcms.io/)
    - [strapi](https://strapi.io/)

今回は、**microCMS** を利用する。（**無料** & **サーバ管理不要** & **日本語UI**）

### SSG（Static Site Generator）とは:thinking:
- markdownやJSONをを使って、静的サイト（普通のHTML/CSS/JavaScript）を生成できる。
- 以下のようなものがある。
    - [Gatsby](https://www.gatsbyjs.org/)
    - [React Static](https://github.com/react-static/react-static)
    - [NuxtJS](https://ja.nuxtjs.org/) (generate)
    - [Next.js](https://nextjs.org/) (exportPathMap)

今回は、 Vuejsベースの **Nuxt.js** を利用する。

## 今回作るもの
![PL2z3e8m4Dxx53SP67W1OWm8YPCCDiH172gSiH7QsXu3Roy_4GNNx_ykSYGijKtDH4dQGbmrKZ7JOxAKHYY2HfPM9qU-0FUjh2kqKOPKotktu98qc_4fD7Te6HkZ8TZDYI7afXA4XKgq8bJas8SQPAkE42vnI_fQg3FU7be_sKemU7wwl_fH1nxd1nysaAAfTSU6JN-E4xvQT3IHss1DEgELw-Twd4H_Bd19wBC8LJKyy0q0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242655/47a72e84-5d40-a315-26ba-064cb071502d.png)

1. コンテンツ管理者がmicroCMS上でコンテンツ更新を行う。
2. microCMSがNetlifyへWebhookを送信する。
3. NetlifyがWebhookを受信し、静的HTMLを生成、CDNへデプロイを行う。

　※ ユーザからのリクエストはCDNで処理されレスポンスを返却する。


## 事前準備
- [microCMS](https://microcms.io/) でユーザ登録しておく。（**無料**）
- [Github](https://github.com/) でユーザ登録し、(プライベート)リポジトリを作成しておく。（**無料**）
- [Netlify](https://www.netlify.com/) でユーザ登録し、Github連携しておく。（**無料**）

## 作り方

### Step.1 ヘッドレスCMS側

#### Step.1-1 サービスの作成
サービスの名称やURLのサブドメイン部分を設定していく。
![app.microcms.io_create-service.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242655/54df21a8-c535-1355-bbd7-21b6de9083c2.png)
![app.microcms.io_create-service (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242655/3597db48-91eb-fb0e-ba69-6b3517ee7c07.png)
![app.microcms.io_create-service (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242655/ff012810-bbbe-cb47-3ec3-c65e2909c51d.png)

#### Step.1-2 APIの作成
APIの名称やエンドポイント、返却値の形式などを設定していく。
![jamstack-demo.microcms.io_.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242655/4146ace1-66d0-51ee-8aae-a0f479d43a76.png)
![jamstack-demo.microcms.io_ (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242655/fec6b0eb-52ef-f00e-bd0a-cd73af247cb0.png)
![jamstack-demo.microcms.io_ (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242655/681c8526-8f58-c036-41ea-ff614f35b911.png)
![jamstack-demo.microcms.io_ (3).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242655/d664b27d-1996-9f4c-8097-38eacff9673c.png)

#### Step.1-3 コンテンツの投入
コンテンツを投入していく。
![jamstack-demo.microcms.io_ (5).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242655/02accc60-1e29-2337-0eb9-2c3eaf016677.png)
![jamstack-demo.microcms.io_apis_gorilla_create.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242655/39d887a3-d2ea-2e9b-9687-f24caa599a70.png)

#### Step.1-4 APIキーの確認
画面上部の**APIリファレンス**からAPIを呼び出すためのキーを取得する。
（**試してみる** からレスポンス値の確認も出来る。）
![jamstack-demo.microcms.io_apis_gorilla_create (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242655/7d820509-5c04-448e-f10f-1acdd02107ca.png)

### Step.2 SSG側

#### Step.2-1 Nuxt.jsのプロジェクト作成
```sh:console
# nodeのバージョン確認
% node -v
v12.13.0

# npm のバージョン確認
% npm -v
6.13.6

# nuxtプロジェクトの作成
% npx create-nuxt-app jamstack-demo

create-nuxt-app v2.12.0
✨  Generating Nuxt.js project in jamstack-demo
? Project name jamstack-demo
? Project description My terrific Nuxt.js project
? Author name ohnaka0410
? Choose the package manager Npm
? Choose UI framework Element
? Choose custom server framework None (Recommended)
? Choose Nuxt.js modules Axios, DotEnv
? Choose linting tools Prettier
? Choose test framework None
? Choose rendering mode Universal (SSR)
? Choose development tools (Press <space> to select, <a> to toggle all, <i> to invert selection)

                                                              
                                      :-:                     
                                    .==-+:                    
                                   .==. :+- .-=-              
                                  .==.   :==++-+=.            
                                 :==.     -**: :+=.           
                                :+-      :*+++. .++.          
                               :+-      -*= .++: .=+.         
                              -+:      =*-   .+*: .=+:        
                             -+:     .=*-     .=*-  =+:       

🎉  Successfully created project jamstack-demo

  To get started:

	cd jamstack-demo
	npm run dev

  To build & start for production:

	cd jamstack-demo
	npm run build
	npm run start

% cd jamstack-demo
% npm run dev
```
ブラウザで `http://localhost:3000/`へアクセスし表示確認を行う。
![localhost_3000_.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242655/0f489c3c-962a-819b-8a58-48b0ac41b3bc.png)

#### Step.2-2 コンテンツ表示部分の作成
```diff:/pages/index.vue
          GitHub
        </a>
      </div>
+      <el-table
+        :data="items">
+        <el-table-column
+          prop="name"
+          label="名称">
+        </el-table-column>
+        <el-table-column
+          prop="scientificName"
+          label="学名">
+        </el-table-column>
+        <el-table-column
+          label="画像">
+          <template slot-scope="scope">
+            <img :src="scope.row.image.url" />
+          </template>
+        </el-table-column>
+      </el-table>
    </div>
  </div>
</template>

<script>
 import Logo from '~/components/Logo.vue'
+import axios from 'axios'
 
 export default {
   components: {
     Logo
-  }
+  },
+  data () {
+    return {
+      items: []
+    }
+  },
+  async asyncData () {
+    const { data } = await axios.get('** microCMSで取得したENDPOINTURL **', {
+      headers: { 'X-API-KEY': 'エンドポイントのAPIキー' }
+    })
+    return {
+      items: data.contents
+    }
+  },
 }
 </script>

```

修正後、Githubのリポジトリにプッシュしておく。

#### Step.2-4 Netlifyのへデプロイ
Githubリポジトリと連携し、**ビルドコマンド**、**ビルド出力先ディレクトリ**を設定する。

![app.netlify.com_start_repos_ohnaka0410%2FJamstack-Sample.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242655/5fd05826-370d-56b3-4d4a-9e95f5543c5e.png)

数分でビルド+デプロイが完了。

### Step.3 表示確認
デプロイ先のURLへアクセスし、表示確認を行う。
![sharp-allen-3e32d1.netlify.com_.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242655/f697d0ea-4e17-332a-5a82-bde0fc12f7cb.png)

ついでに**audits**でパフォーマンス測定を行う。

<img width="553" alt="スクリーンショット 2020-02-01 1.14.05.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242655/f076a121-4fb7-2fc1-01a0-5681d1f79fc7.png">
パフォーマンスは無事**100**が出ました。

### Step.4 Webhookの設定
Webhookを利用して、microCMSでコンテンツ変更時に、Netlify上で自動的にコンテンツをビルドし、デプロイできるように設定する。

#### Step.4-1 Netlify側の設定
**Settings>Build&Deploy**から**Build hooks**の設定を行う。
<img width="686" alt="スクリーンショット 2020-02-01 1.18.36.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242655/139de32f-83e7-85c2-d099-668fee5292fe.png">

#### Step.4-2 microCMS側の設定
**API設定>Webhook**から上記で作成したWebhookのURLを入力する。
<img width="833" alt="スクリーンショット 2020-02-01 1.19.12.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242655/b41d0eef-924d-b5b4-d056-65d5f1eecd25.png">

#### Step.4-3 デプロイの確認
microCMSで記事更新後、Netlifyの**Deploys**を確認し、**Building**となっていることを確認する。
<img width="687" alt="スクリーンショット 2020-02-01 1.23.11.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242655/c5025878-0f05-94aa-7703-c618e7eaacdd.png">

## おわりに
- ちょっとしたお知らせ、ニュース、ブログ（運営者→ユーザへの一方向配信コンテンツ）であれば、フロントエンドだけで完結できるため、コーポレートサイトやLP、サイネージなどで導入しやすそう。
- できれば、(axiosとかで叩ける)お問い合わせ用APIとかあれば良いけど、なにか良いサービスないかな:thinking:
   - (追記)<br>microCMSがAPIによる下記書き込みに対応したので、試してみました。<br>→ [Vue.js (+TypeScript) × microCMSでお問い合わせフォームを作成する](https://qiita.com/ohnaka0410/items/8dc4cb0e8a24f4e3bbda)