React + Nextjs + TypeScript による Vercel CDN からの静的コンテンツ配信

弊社のホームページはこれまで WordPreass によって aws のエッジロケーションから配信していたのですが、これを Nextjs による Vercel CDN からの配信に変更することにしました

## Nextjs

React ホストはクライアントに対して JavaScript を送出しますので、Google クローラーは　Web　コンテンツとして認識してくれません。この問題に対し、Nextjs は SSG (Static Site Generation)、SSR (Server Side Rendering)、ISR (Incremental Static Regeneration) などのタイミングで静的ページを生成し、これを Google クローラーに見せてくれます。

そこで弊社のホームページも Nextjs を使用することによって、Google検索にヒットするようにしたいと思います。

## 開発環境構築

### Docker コンテナ

```
$ pwd
/Users/r/Docker/unrweb
$ cat docker-compose.yml
version: '3'

services:
  hreact:
    image: node:latest
    container_name: 'unrweb'
    hostname: 'unrweb'
    ports:
      - '3000:3000'
    stdin_open: true
    tty: true
    working_dir: '/var/www/html'
    volumes:
      - ./src:/var/www/html
    networks:
      - dumynet
networks:
  dumynet:
    external: true

$
```

### コンテナ起動

```
$ docker-compose up
Starting unrweb ... done
Attaching to unrweb
unrweb    | Welcome to Node.js v17.8.0.
unrweb    | Type ".help" for more information.
```

### yarn インストール

インストールは必要ありませんでした

```
$ docker container exec -it unrweb /bin/bash
root@unrweb:/var/www/html# npm install --global yarn
npm ERR! code EEXIST
npm ERR! path /usr/local/bin/yarn
npm ERR! EEXIST: file already exists
npm ERR! File exists: /usr/local/bin/yarn
npm ERR! Remove the existing file and try again, or run npm
npm ERR! with --force to overwrite files recklessly.

npm ERR! A complete log of this run can be found in:
npm ERR!     /root/.npm/_logs/2022-04-19T07_52_11_618Z-debug-0.log
root@unrweb:/var/www/html#
```

### create-next-app

```
root@unrweb:/var/www/html# npx create-next-app . --typescript
Need to install the following packages:
  create-next-app
Ok to proceed? (y) y
Creating a new Next.js app in /var/www/html.

(snip)

Success! Created html at /var/www/html
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 /var/www/html
  yarn dev

npm notice
npm notice New minor version of npm available! 8.5.5 -> 8.7.0
npm notice Changelog: https://github.com/npm/cli/releases/tag/v8.7.0
npm notice Run npm install -g npm@8.7.0 to update!
npm notice
root@unrweb:/var/www/html#
```

### 開発サーバ起動

```
root@unrweb:/var/www/html# yarn run dev
yarn run v1.22.18
$ next dev
ready - started server on 0.0.0.0:3000, url: http://localhost:3000
wait  - compiling...
event - compiled client and server successfully in 3.3s (124 modules)
Attention: Next.js now collects completely anonymous telemetry regarding usage.
This information is used to shape Next.js' roadmap and prioritize features.
You can learn more, including how to opt-out if you'd not like to participate in this anonymous program, by visiting the following URL:
https://nextjs.org/telemetry
```

![スクリーンショット 0004-04-19 17.06.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/539175/2ef2e198-496d-4cbf-c418-a0f047585ffa.png)

開発サーバは control + c で止める

ここで ./package.json の dependencies を見ると、インストールされたモジュールのバージョンが確認できる

### tailwindcss のインストール

```
root@unrweb:/var/www/html# yarn add -D tailwindcss postcss autoprefixer
yarn add v1.22.18
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
[4/4] Building fresh packages...
success Saved lockfile.
success Saved 32 new dependencies.

(snip)

Done in 6.59s.
root@unrweb:/var/www/html#
```

#### prettier plugin (prettier と連動して tailwind のクラスユーティリティを自動的にソーティングしてくれるもの)

```
root@unrweb:/var/www/html# yarn add -D prettier prettier-plugin-tailwindcss
yarn add v1.22.18
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
[4/4] Building fresh packages...
success Saved lockfile.
success Saved 2 new dependencies.
info Direct dependencies
├─ prettier-plugin-tailwindcss@0.1.8
└─ prettier@2.6.2
info All dependencies
├─ prettier-plugin-tailwindcss@0.1.8
└─ prettier@2.6.2
Done in 3.53s.
root@unrweb:/var/www/html#
```

#### npx tailwindcss init -p

```
root@unrweb:/var/www/html# npx tailwindcss init -p

Created Tailwind CSS config file: tailwind.config.js
Created PostCSS config file: postcss.config.js
root@unrweb:/var/www/html#
```

#### ./tailwind.config.json を更新

(更新前)

```
root@unrweb:/var/www/html# cat tailwind.config.js
module.exports = {
  content: [],
  theme: {
    extend: {},
  },
  plugins: [],
}
root@unrweb:/var/www/html#
```

(更新後)

```
root@unrweb:/var/www/html# cat tailwind.config.js
module.exports = {
  content: [
    "./pages/**/*.{js,ts,jsx,tsx}",
    "./components/**/*.{js,ts,jsx,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
};
root@unrweb:/var/www/html#
```

この設定をいれないと、デプロイ時に ./pages と ./components のすべてのクラスユーティリティが生成されてしまい、これはとても巨大なものとなる。この設定をいれることによって、デプロイ時には、./pages と ./components フォルダにある js ファイルで使用されているユーティリティだけが生成されるようになる。

#### ./styles/globals.css を更新

(更新前)

```
root@unrweb:/var/www/html/styles# cat globals.css
html,
body {
  padding: 0;
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
    Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
}

a {
  color: inherit;
  text-decoration: none;
}

* {
  box-sizing: border-box;
}
root@unrweb:/var/www/html/styles#
```

(更新後)

```
root@unrweb:/var/www/html/styles# cat globals.css
@tailwind base;
@tailwind components;
@tailwind utilities;
root@unrweb:/var/www/html/styles#
```

### 開発サーバを起動して設定を反映する

```
root@unrweb:/var/www/html# yarn run dev
yarn run v1.22.18
$ next dev
ready - started server on 0.0.0.0:3000, url: http://localhost:3000
info  - Loaded env from /var/www/html/.env.local
wait  - compiling...
event - compiled client and server successfully in 5.2s (124 modules)
wait  - compiling...
event - compiled successfully in 99 ms (97 modules)
wait  - compiling /_error (client and server)...
wait  - compiling...
event - compiled client and server successfully in 94 ms (125 modules)
```

### ./.prettierrc をつくる

```
root@unrweb:/var/www/html# cat .prettierrc
{
  "singleQuote": true,
  "semi": false
}
root@unrweb:/var/www/html#
```

## 各ページを作成する

```
$ pwd
/Users/r/Docker/unrweb/src/pages
$ ls -l
total 56
-rw-r--r--@ 1 r  staff   188  4 28 08:56 _app.tsx
drwxr-xr-x  3 r  staff    96  4 28 08:57 api
-rw-r--r--  1 r  staff   649  4 28 16:14 contact.tsx
-rw-r--r--@ 1 r  staff   197  4 28 10:57 index.tsx
-rw-r--r--  1 r  staff  1790  4 28 10:58 mission.tsx
-rw-r--r--  1 r  staff  1417  4 28 11:16 prof.tsx
-rw-r--r--  1 r  staff  3552  4 29 13:36 style.tsx
-rw-r--r--  1 r  staff  1805  4 28 18:23 times.tsx
$
```

### 動作確認

#### 開発サーバ起動

```
root@unrweb:/var/www/html# pwd
/var/www/html
root@unrweb:/var/www/html# yarn run dev
yarn run v1.22.18
$ next dev
ready - started server on 0.0.0.0:3000, url: http://localhost:3000
wait  - compiling...
event - compiled client and server successfully in 816 ms (124 modules)
```

#### 各ページを確認する

##### index (Home)

![スクリーンショット 0004-04-29 19.46.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2164727/9c9c80d9-9127-1076-8973-71b2fbde408b.png)

##### mission (ミッション)

![スクリーンショット 0004-04-29 19.48.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2164727/90e459cb-f896-7e1f-6688-20909d5bec1f.png)

##### prof (会社概要)

![スクリーンショット 0004-04-29 19.48.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2164727/52e1c04f-82f6-f1e1-1765-77714bb11ad4.png)

##### contact (お問い合わせ)

![スクリーンショット 0004-04-29 19.50.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2164727/4fd7f4d8-1f76-9de3-283b-fb770818a3ba.png)

##### style (開発スタイル)

![スクリーンショット 0004-04-29 19.51.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2164727/5007c5b4-3879-2820-ff4e-5f9850db0c5c.png)

##### 時系列

![スクリーンショット 0004-04-29 20.04.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2164727/d2132339-3f07-01cf-ff25-da3986357a1a.png)

## Google クローラーからどう見えているのか？

- [一次情報](https://nextjs.org/learn/basics/data-fetching)

以前開発した demoru.net は create-react-app　を用いて開発環境を構築しています。この場合は JavaScript のみがクライアントにダウンロードされるため、ブラウザで JavaScript を無効にしている場合は、一切レンダリングされません。

- Chrome ブラウザにて demoru.net にアクセス

![スクリーンショット 0004-04-29 21.14.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2164727/24d6ddf8-e9eb-bc35-2ffc-939662fba28d.png)

- 設定 > デバッガ > [JavaScript を無効にする] をチェック
- ブラウザをリロード

![スクリーンショット 0004-04-29 21.17.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2164727/f9f03a87-12db-c164-efa0-76e1a44dba7a.png)

同じことを create-next-app したサイトで確認すると

![スクリーンショット 0004-04-29 21.22.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2164727/6662d0ef-70a9-e65a-2157-c16eac34778f.png)

なかなかいい感じです

### 静的ページを確認する

```
$ pwd
/Users/r/Docker/unrweb/src/.next/server/pages
$ ls -la
total 5072
drwxr-xr-x  11 r  staff     352  4 29 20:19 .
drwxr-xr-x   8 r  staff     256  4 29 19:43 ..
-rw-r--r--   1 r  staff    3740  4 29 19:43 _app.js
-rw-r--r--   1 r  staff  193943  4 29 19:43 _document.js
-rw-r--r--   1 r  staff   14572  4 29 19:43 _error.js
-rw-r--r--   1 r  staff  367245  4 29 20:19 contact.js
-rw-r--r--   1 r  staff  363754  4 29 20:15 index.js
-rw-r--r--   1 r  staff  372945  4 29 20:19 mission.js
-rw-r--r--   1 r  staff  500212  4 29 20:19 prof.js
-rw-r--r--   1 r  staff  383270  4 29 20:19 style.js
-rw-r--r--   1 r  staff  376868  4 29 20:03 times.js
$
```

- yarn build　する

```
root@unrweb:/var/www/html# pwd
/var/www/html
root@unrweb:/var/www/html# yarn build
yarn run v1.22.18
$ next build
info  - Checking validity of types
info  - Creating an optimized production build
info  - Compiled successfully
info  - Collecting page data
info  - Generating static pages (8/8)
info  - Finalizing page optimization

Page                                       Size     First Load JS
┌ ○ /                                      2.86 kB        77.5 kB
├   /_app                                  0 B            74.6 kB
├ ○ /404                                   192 B          74.8 kB
├ λ /api/hello                             0 B            74.6 kB
├ ○ /contact                               2.96 kB        77.6 kB
├ ○ /mission                               3.64 kB        78.3 kB
├ ○ /prof                                  7.31 kB        81.9 kB
├ ○ /style                                 3.56 kB        78.2 kB
└ ○ /times                                 3.5 kB         78.1 kB
+ First Load JS shared by all              74.6 kB
  ├ chunks/framework-1f10003e17636e37.js   45 kB
  ├ chunks/main-f4ae3437c92c1efc.js        28.3 kB
  ├ chunks/pages/_app-85d7488a393e293e.js  493 B
  ├ chunks/webpack-69bfa6990bb9e155.js     769 B
  └ css/1f1b683dcc8b2d62.css               1.98 kB

λ  (Server)  server-side renders at runtime (uses getInitialProps or getServerSideProps)
○  (Static)  automatically rendered as static HTML (uses no initial props)

Done in 20.04s.
root@unrweb:/var/www/html#
```

```
$ pwd
/Users/r/Docker/unrweb/src/.next/server/pages
$ ls -l
total 256
-rw-r--r--  1 r  staff   2240  4 29 21:45 404.html
-rw-r--r--  1 r  staff   2226  4 29 21:45 500.html
-rw-r--r--  1 r  staff   1311  4 29 21:45 _app.js
-rw-r--r--  1 r  staff    469  4 29 21:45 _app.js.nft.json
-rw-r--r--  1 r  staff  44233  4 29 21:45 _document.js
-rw-r--r--  1 r  staff   1454  4 29 21:45 _document.js.nft.json
-rw-r--r--  1 r  staff   3838  4 29 21:45 _error.js
-rw-r--r--  1 r  staff    677  4 29 21:45 _error.js.nft.json
drwxr-xr-x  4 r  staff    128  4 29 21:45 api
-rw-r--r--  1 r  staff   2781  4 29 21:45 contact.html
-rw-r--r--  1 r  staff   3128  4 29 21:45 contact.js.nft.json
-rw-r--r--  1 r  staff   2504  4 29 21:45 index.html
-rw-r--r--  1 r  staff   3128  4 29 21:45 index.js.nft.json
-rw-r--r--  1 r  staff   3814  4 29 21:45 mission.html
-rw-r--r--  1 r  staff   3128  4 29 21:45 mission.js.nft.json
-rw-r--r--  1 r  staff   4789  4 29 21:45 prof.html
-rw-r--r--  1 r  staff   3277  4 29 21:45 prof.js.nft.json
-rw-r--r--  1 r  staff   4639  4 29 21:45 style.html
-rw-r--r--  1 r  staff   3128  4 29 21:45 style.js.nft.json
-rw-r--r--  1 r  staff   3844  4 29 21:45 times.html
-rw-r--r--  1 r  staff   3128  4 29 21:45 times.js.nft.json
$ cat index.html
<!DOCTYPE html><html><head><meta name="viewport" content="width=device-width"/><meta charSet="utf-8"/><title>Home</title><meta name="next-head-count" content="3"/><link rel="preload" href="/_next/static/css/1f1b683dcc8b2d62.css" as="style"/><link rel="stylesheet" href="/_next/static/css/1f1b683dcc8b2d62.css" data-n-g=""/><noscript data-n-css=""></noscript><script defer="" nomodule="" src="/_next/static/chunks/polyfills-5cd94c89d3acac5f.js"></script><script src="/_next/static/chunks/webpack-69bfa6990bb9e155.js" defer=""></script><script src="/_next/static/chunks/framework-1f10003e17636e37.js" defer=""></script><script src="/_next/static/chunks/main-f4ae3437c92c1efc.js" defer=""></script><script src="/_next/static/chunks/pages/_app-85d7488a393e293e.js" defer=""></script><script src="/_next/static/chunks/pages/index-e7cf9bab6c002e10.js" defer=""></script><script src="/_next/static/dJ4cGJkj2WARI_mLDWehW/_buildManifest.js" defer=""></script><script src="/_next/static/dJ4cGJkj2WARI_mLDWehW/_ssgManifest.js" defer=""></script><script src="/_next/static/dJ4cGJkj2WARI_mLDWehW/_middlewareManifest.js" defer=""></script></head><body><div id="__next"><div class="flex min-h-screen flex-col items-center justify-center font-mono text-gray-800"><header><nav class="w-screen bg-gray-800"><div class="flex h-14 items-center pl-8"><div class="flex space-x-4"><a class="rounded px-3 py-2 text-gray-300 hover:bg-gray-700" href="/">Home</a><a class="rounded px-3 py-2 text-gray-300 hover:bg-gray-700" href="/mission">ミッション</a><a class="rounded px-3 py-2 text-gray-300 hover:bg-gray-700" href="/prof">会社概要</a><a class="rounded px-3 py-2 text-gray-300 hover:bg-gray-700" href="/contact">お問い合わせ</a><a class="rounded px-3 py-2 text-gray-300 hover:bg-gray-700" href="/style">開発スタイル</a><a class="rounded px-3 py-2 text-gray-300 hover:bg-gray-700" href="/times">時系列</a></div></div></nav></header><main class="flex w-screen flex-1 flex-col items-center justify-center"><p class="text-4xl">Welcome to unremoted.com</p></main><footer class="flex h-12 w-full items-center justify-center border-t"><a class="flex items-center" href="https://unremoted.com" target="_blank" rel="noopener noreferrer">We are unremoted.com</a></footer></div></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{}},"page":"/","query":{},"buildId":"dJ4cGJkj2WARI_mLDWehW","nextExport":true,"autoExport":true,"isFallback":false,"scriptLoader":[]}</script></body></html>%
$
```

確かに静的ページが生成されていました

## デプロイ

### Github に push

### Vercel に　To deploy a new Project

- [Let's build something new.](https://vercel.com/new)

![スクリーンショット 0004-04-29 23.17.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2164727/4c22354a-8043-aaca-d9ec-9939760637da.png)

- unrweb の [Import] をクリック

![スクリーンショット 0004-04-29 23.20.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2164727/abc4308b-25c4-aee6-c0b6-4d3d467f4db9.png)

- [Deploy] をクリック

![スクリーンショット 0004-04-29 23.22.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2164727/08ba5c0f-e62c-0d33-1955-942d80801eaf.png)

- [Go to Dashbord] をクリック

![スクリーンショット 0004-04-29 23.25.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2164727/15be3bed-b940-d05d-363a-5ed7702e1348.png)

- ブラウザから DOMAINS へアクセス

![スクリーンショット 0004-04-29 23.27.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2164727/0fe7936d-2214-7926-fe38-11566fdb1115.png)

## 独自ドメインからのアクセス

- Vercel の 設定したいプロジェクトから　Settings > Domains

![スクリーンショット 0004-04-30 9.10.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2164727/60683f22-f835-b696-2fc1-b2f190a7063a.png)

- [Add] をクリック

![スクリーンショット 0004-04-30 9.12.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2164727/8a780717-5cc0-5498-e002-b873ffc62edd.png)

- この IPアドレスを A レコードにセットする
- unremoted.com が Valid Configuration になる

![スクリーンショット 0004-04-30 9.24.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2164727/80b1a3d8-26e3-c02e-dd97-f11d824abbae.png)

- 画面に従って CNAME をセット
- www.unremoted.com も Valid Configuration になる

![スクリーンショット 0004-04-30 9.31.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2164727/2e592789-1ecd-b62f-1da2-e5a0a0d9d873.png)

しばらく (５分ほど) したらブラウザから HP にアクセスできました

![スクリーンショット 0004-04-30 9.42.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2164727/922acf5c-7ad8-2427-00b3-46813a3aa31e.png)

## コンテンツを更新する

- yarn run dev で開発サーバでの動作確認
- git push

```
$ pwd
/Users/r/Docker/unrweb/src
$ git status
On branch main
Your branch is up to date with 'origin/main'.

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	modified:   pages/style.tsx
	modified:   pages/times.tsx

no changes added to commit (use "git add" and/or "git commit -a")
$ git add .
$ git commit -m "Added"
[main ffd5c1c] Added
 2 files changed, 27 insertions(+), 10 deletions(-)
$ git status
On branch main
Your branch is ahead of 'origin/main' by 1 commit.
  (use "git push" to publish your local commits)

nothing to commit, working tree clean
$ git push origin main
Enumerating objects: 9, done.
Counting objects: 100% (9/9), done.
Delta compression using up to 8 threads
Compressing objects: 100% (5/5), done.
Writing objects: 100% (5/5), 792 bytes | 792.00 KiB/s, done.
Total 5 (delta 4), reused 0 (delta 0), pack-reused 0
remote: Resolving deltas: 100% (4/4), completed with 4 local objects.
To https://github.com/unrcom/unrweb.git
   c2783a9..ffd5c1c  main -> main
$
```

- Vercel の　CREATED　が更新される

![スクリーンショット 0004-04-30 10.36.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2164727/912828ea-bc73-c805-39e9-303e54300c88.png)

ブラウザで確認すると、確かに追記したページが更新されていました

## Vercel の料金体系

ここまで無料のプランで作業していましたが「For non-commercial & hobby sites」ということなので有料の Pro というプラン (月額 20 USD) に変更しなければなりません。今日は 4/30 なので、明日、プラン変更しようと思っています。(Vercel は aws の北米、東海岸がメインリージョンのようですので　5/1 の 18　時ころにプラン変更しようかと)

これで今まで WordPress で運用していたホームページ配信環境をとめることができるようになりました。aws の Lightsail インスタンス、静的IP、ディストリビューションをとめることができたのですが、より高額なサービスが待っていたとは皮肉なものです。

今後は Vercel に DB 連携した SSG による定期的なページの再生成を行うサイトを立ち上げて、元をとろうと思っています。