Next.js、Contentful、Vercelでポートフォリオサイトを作った手順メモ

Next.js（Reactベースのフレームワーク）を使ってポートフォリオサイトを作って見たので、その時の手順などを簡単にまとめました。
コンテンツの部分は[Contentful](https://contentful.com/)、ホスティングは[Vercel](https://vercel.com/docs)です。

## Next.jsをインストール

```
npx create-next-app
```
https://nextjs.org/

## pagesディレクトリでページを増やす

![next.js page.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559347/fe315eaf-c5bd-0e95-0f11-51830e9dd7a8.jpeg)

Next.jsでは、pagesディレクトリにファイルを増やしていくことで、自動的にルーティングもおこなってくれます。
ファイル名を404とすると、自動的に404のエラー用のページになります。
動的なページは[id]のように[]で囲む必要があります。

## Reactで記述
```jsx
const Home = () => {
  return (
    <div>
      <h1>hello</h1>
    </div>
  )
}
export default Home

```

Next.jsはReact製のフレームワークなので、記述はjsx(tsx)になります。

## 適時ライブラリを追加する
適時ライブラリ等を追加します。

### tailwindcss（CSSフレームワーク）

CSSフレームワークは、tailwindcssを使用しました。

```
# If you’re using npm
npm install -D tailwindcss@latest postcss@latest autoprefixer@latest

# If you’re using Yarn
yarn add  tailwindcss@latest postcss@latest autoprefixer@latest
```

```
npx tailwindcss init -p  #tailwindcssの設定をファイルを生成するコマンド
```


https://tailwindcss.com/docs/guides/nextjs

下記はtailwindcssのチートシートになります。cssのプロパティが一覧になっていてかなり便利です。
https://nerdcave.com/tailwind-cheat-sheet


### TypeScript
TypeScriptを導入する。

```
# If you’re using npm
npm install --save-dev typescript @types/react @types/node

# If you’re using Yarn
yarn add --dev typescript @types/react @types/node
```

```
tsc --init
```
上記のコマンドで、tsconfig.jsonを作成できますので、オプションを指定します。

```tsconfig.json

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


https://nextjs.org/learn/excel/typescript/create-tsconfig


### TSLint

```
# If you’re using npm
npm install --save-dev tslint tslint-config-airbnb
npm install --save-dev tslint-react-hooks  # react hookを使用する場合

# If you’re using Yarn
yarn add --dev tslint tslint-config-airbnb
yarn add --dev tslint-react-hooks  # react hookを使用する場合
```

```package.json
"tslint": "tslint -p . -c tslint.json"
```

`tslint.json`ファイルを作り、設定を追加します。

```tslint.json
{
  "extends": ["tslint-config-airbnb", "tslint-react-hooks"],
  "rules": {
    "import-name": false,
    "align": false,
    "react-hooks-nesting": false,
    "semicolon": false,
    "trailing-comma": false,
    "ter-arrow-parens": [true, "always"],
    "object-literal-shorthand": false,
    "function-name": [
      true,
      {
        "function-regex": "^[a-zA-Z$][\\w\\d]+$",
        "method-regex": "^[a-z$][\\w\\d]+$",
        "private-method-regex": "^[a-z$][\\w\\d]+$",
        "protected-method-regex": "^[a-z$][\\w\\d]+$",
        "static-method-regex": "^[a-z$][\\w\\d]+$"
      }
    ],
    "variable-name": {
      "options": [
        "ban-keywords",
        "check-format",
        "allow-leading-underscore",
        "allow-pascal-case"
      ]
    }
  }
}

```

```
npm run tslint
yarn tslint
```
上記のコマンドでlintのチェックをおこないます。

https://dev.to/oahehc/how-to-config-react-project-with-next-js-typescript-tslint-and-jest-11l0


#### リアルタイムでlintチェックする
save時などにlintチェックしたい場合は、下記のプラグインを追加します。

```
# If you’re using npm
npm install --save-dev fork-ts-checker-webpack-plugin

# If you’re using Yarn
yarn add -D fork-ts-checker-webpack-plugin
```

`next.config.js`に設定を追加。

```next.config.js
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin')

module.exports = {
  /**
   * Custom Webpack Config
   * https://nextjs.org/docs/api-reference/next.config.js/custom-webpack-config
   */
  webpack(config, options) {
    const { dev, isServer } = options

    // Do not run type checking twice:
    if (dev && isServer) {
      config.plugins.push(new ForkTsCheckerWebpackPlugin())
    }

    return config
  }
}

```
https://dev.to/chromygabor/add-typescript-type-check-to-next-js-2nbb


## ContentfulとNext.jsを連携させる。

ContentfulとNext.jsを連携させる為のライブラリをインストールします。

```
# If you’re using npm
npm install contentful

# If you’re using Yarn
yarn add contentful
```
設定方法は下記の記事でまとめています。
[Next.jsとContentfulを連携する方法](https://qiita.com/TK-C/items/e6a073faa81fa99db317)

## リッチテキストに対応

Contentfulのリッチテキストを表示するための対応です。

```
# If you’re using npm
npm install react-markdown

If you’re using Yarn
yarn add react-markdown
```

```jsx
import ReactMarkdown from 'react-markdown'

const Home = (props) => {
  return (
    <div>
      <ReactMarkdown>{props}</ReactMarkdown>
    </div>
  )
}

export default Home
```
https://github.com/remarkjs/react-markdown


## 環境変数の設定

```env:env.local
CTF_SPACE_ID=demo123

CTF_CDA_ACCESS_TOKEN=demo1234

NEXT_PUBLIC_PATH=https://demo.com   // クライアントサイドで使う場合
```
Contenfulなどの環境変数の設定をおこないます。
環境変数をクライアントサイドで使いたい場合は、`NEXT_PUBLIC`で始まる形にします。

```js
const path = process.env.NEXT_PUBLIC_PATH
```
https://nextjs.org/docs/basic-features/environment-variables

## ページ遷移はnext/link
ページ遷移は`next/link`を使用します。

```jsx
import Link from 'next/link'

const LinkButton = ({ id, name }) => {
  return (
    <div>
      <Link href={`/tag/${id}`}>
        <a>{name}</a>
      </Link>
    </div>
  )
}

export default LinkButton
```
https://nextjs.org/docs/api-reference/next/link

## 画像の扱いはnext/imageが便利
Next.jsで画像を利用するときは、`next/image`が便利です。

```jsx
import Image from 'next/image'

const ProfileImage = ({ image }) => {
  return (
    <div>
      <Image
        src={image.src}
        alt={image.alt}
        width={image.width}
        height={image.height}
        loading="lazy"
        objectFit="contain"
        className="bg-gray-400"
      />
    </div>
  )
}

export default ProfileImage

```
https://nextjs.org/docs/api-reference/next/image

## meta、ogpの設定はnext/head
meta、ogpの設定は`next/head`を利用します。

```jsx
import Head from 'next/head'

const Seo = ({
  pageTitle,
  pageDescription,
  pagePath,
  pageImg,
  pageImgWidth,
  pageImgHeight
}) => {
  const defaultTitle = 'demo'
  const defaultDescription = 'demo'

  const title = pageTitle ? `${pageTitle} | ${defaultTitle}` : defaultTitle
  const description = pageDescription ? pageDescription : defaultDescription
  const url = pagePath
  const imgUrl = pageImg
  const imgWidth = pageImgWidth ? pageImgWidth : 1280
  const imgHeight = pageImgHeight ? pageImgHeight : 640

  return (
    <Head>
      <title>{title}</title>
      <meta name="viewport" content="width=device-width,initial-scale=1.0" />
      <meta name="description" content={description} />
      <meta property="og:url" content={url} />
      <meta property="og:title" content={title} />
      <meta property="og:site_name" content={title} />
      <meta property="og:description" content={description} />
      <meta property="og:type" content="website" />
      <meta property="og:image" content={imgUrl} />
      <meta property="og:image:width" content={String(imgWidth)} />
      <meta property="og:image:height" content={String(imgHeight)} />
      <link rel="preconnect" href="https://fonts.gstatic.com" />
      <link
        href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900&amp;display=swap"
        rel="stylesheet"
      />
      <link rel="canonical" href={url} />
    </Head>
  )
}

export default Seo
```

propsの値で設定するようにすることで、ページごとにmetaタグの設定が可能です。

https://qiita.com/TK-C/items/cd34d0f6d4b001053443

## デプロイはVercel
Vercelとgithubを連携させることで簡単にデプロイすることが可能です。

Conentfulの環境変数は別途設定する必要があります。
![vercel 環境変数.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559347/752f37da-8ece-158e-d723-62c109957a55.jpeg)

https://vercel.com/docs