Next.js(Pages Router) + TypeScriptの環境構築

# ゴール
・ローカル環境で、Next.js + TypeScriptの環境ができる。
・開発サーバーや本番サーバーを立ち上げることができる。

# Next.jsとは?
Next.js は、フルスタック Web アプリケーションを構築するための React フレームワーク。
ユーザーインターフェイスは、「React Components」を使用して構築し、追加機能や最適化は、「Next.js」を使用して行う。

内部的に、Reactに必要なツールを抽象化し自動的に構成してくれるので、簡単に構築できる。

# はじめに
・node.jsの存在を確認（12.22.0以降であればOK）
```terminal:
$ node -v
v19.6.0
```
※Node.jsについて知りたい場合は、以下を参照してください。

https://nodejs.org/ja/about

※Node.jsのインストール方法は、以下を参考にしました。

https://kinsta.com/jp/blog/how-to-install-node-js/

# プロジェクト作成
1\. プロジェクトを作成したいディレクトリまで移動
```terminal:
$ cd ~
```

2\. 自動的セットアップ用のコマンドを実行
```terminal:
$ npx create-next-app@latest {プロジェクト名} --ts
```
| オプション | 内容 |
|:-|:-:|
| --ts（--typescript） | TypeScriptのプロジェクトで始めるオプション |

3\. いくつか質問されるので「Yes」か「No」を選択していく
```
:heavy_check_mark: Would you like to use ESLint? … No / Yes
:heavy_check_mark: Would you like to use Tailwind CSS? … No / Yes
:heavy_check_mark: Would you like to use `src/` directory? … No / Yes
:heavy_check_mark: Would you like to use App Router? (recommended) … No / Yes
:heavy_check_mark: Would you like to customize the default import alias? … No / Yes
```
今回はこのように回答しました。
参考：https://zenn.dev/ikkik/articles/51d97ff70bd0da
| 質問内容 | 回答 |
|:-|:-:|
| Would you like to use ESLint? | YES |
| Would you like to use Tailwind CSS? | YES |
| Would you like to use "src/" directory? | YES |
| Would you like to use App Ruter? (recommended)   |  NO |
| Would you like to customize the default import alias  |  NO |

4\. 最後に以下メッセージが表示されれば完了
```
...
Success! Created {プロジェクト名} at {プロジェクトルートパス}
```

# 開発サーバーの立ち上げ
※開発サーバーはソースコードを変更して保存すると、Webページにも反映される。

1\. プロジェクトルートに移動。
```terminal:
$ cd {プロジェクト名}
```

2\. 以下コマンドを実行して、開発サーバーを立ち上げる。
```terminal:
$ npm run dev
```

3\. 開発サーバーのURLが表示されるので、ブラウザから開く
```
- Local: http://localhost:3000
```

4\. Next.jsの初期画面が表示されれば完了
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/790127/20c31f4f-a3be-5a4e-0c32-f7d49d19cda6.png)

# 本番サーバーの立ち上げ
※開発サーバーを立ち上げている場合は、「Ctrl」 + 「C」で終了させてください。

1\. 以下コマンドを実行して、ビルドする。
```terminal:
$ npm run build
```

2\. 以下コマンドを実行して、本番サーバーを立ち上げる。
```terminal:
$ npm run start
```

3\. 本番サーバーのURLが表示されるので、ブラウザから開く
```
- Local: http://localhost:3000
```

4\. Next.jsの初期画面が表示されれば完了
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/790127/20c31f4f-a3be-5a4e-0c32-f7d49d19cda6.png)

# おまけ： スタイリングについて
Next.jsでは、スタイルを設定する以下の方法をサポートしている。

<dl>
  <dt>グローバルCSS</dt>
  <dd>クライアント側の全ページで適用されるcssのことで、globals.cssで管理する。</dd>
  <dt>CSSモジュール</dt>
  <dd>ローカルスコープのCSSクラスを作成して、名前の競合を回避し、保守性を向上させる。</dd>
  <dt>Tailwind CSS</dt>
  <dd>クラスベースのアプローチを採用しているCSSフレームワーク</dd>
  <dt>Sass</dt>
  <dd>CSSを拡張して、書きやすく、見やすくしたスタイルシート言語</dd>
  <dt>CSS-in-JS</dt>
  <dd>スタイル情報をJavaScript内で定義し、それをコンポーネントに適用する。</dd>
</dl>

**今回は、 公式が推奨している「CSSモジュール」を使ってスタイルを反映してみます。**

https://nextjs.org/docs/app/building-your-application/styling/css-modules

例）Buttonコンポーネントにスタイルを適用する。
```css:src/components/Button/button.module.css
.red {
    color: white;
    background-color: red;
  }
```
```tsx:src/components/Button/button.tsx
import styles from "./button.module.css";

type ButtonProp = {
  children: string;
};

const Button = (props: ButtonProp): JSX.Element => {
  return (
    <button type="button" className={styles.red}>
      {props.children}
    </button>
  );
};

export default Button;
```

# おまけ： Prettierの設定
コードフォーマッターの1つで、ユーザー環境に依存することなく、プロジェクト単位でコードフォーマットを統一できる。

1\. 以下のコマンドを実行して、モジュールをインストールする。

```terminal
$ npm install --save-dev prettier eslint-config-prettier
```

2\. .eslintrc.jsonにextendsオプションに"prettier"を追加する。
```diff_json:.eslintrc.json
{
- "extends": "next/core-web-vitals"
+ "extends": ["next/core-web-vitals", "prettier"]
}
```

3\. prettier用のスクリプトを追加する。
"format"：オプションに従って、対象ファイルのフォーマットをチェック
"format:fix"：オプションに従って、対象ファイルのフォーマットを自動整形
```diff_json:package.json
{
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint",
+   "format": "prettier --check --ignore-path .gitignore .",
+   "format:fix": "prettier --write --ignore-path .gitignore ."
  },
}
```

4\. スクリプトを実行して、フォーマットのチェック、自動整形されれば完了
```terminal
$ npm run format

> name-list@0.1.0 format
> prettier --check --ignore-path .gitignore .

Checking formatting...
[warn] .prettierrc.yaml
...
```
```terminal
$ npm run format:fix

> name-list@0.1.0 format:fix
> prettier --write --ignore-path .gitignore .

.eslintrc.json 40ms
...
```

※オプションをデフォルトのまま使用する場合は、設定ファイルは不要です。
※自分の場合は、オプションをコーディング規約として見るので、設定ファイルを作成してオプションも明示的に記載しています。
```yaml:.prettierrc.yaml
# 全てデフォルト値です。コーディング規約になるので明示的に記載
# https://prettier.io/docs/en/options

# 行の最大サイズ
printWidth: 80
# タブの幅
tabWidth: 2
# タブでインデントするか
useTabs: false
# 末尾にセミコロンを付与するか
semi: true
# 引用符を、シングルクォーテーションにするか
singleQuote: false
# オブジェクト内のプロパティのクォーテーション
quoteProps: "as-needed"
# JSX内の引用符を、シングルクォーテーションにするか
jsxSingleQuote: false
# 末尾のカンマ
trailingComma: "all"
# オブジェクト内の要素と括弧の間にスペースを入れるか
bracketSpacing: true
# HTMLの閉じタグを最後の行の末尾に設置するか
bracketSameLine: false
# アロー関数の引数の括弧有無
arrowParens: "always"
# フォーマット範囲の指定
rangeStart: 0
rangeEnd: 99999999
# プラグマ付きファイルをフォーマット対象とするか
requirePragma: false
# プラグマを挿入するか
insertPragma: false
# マークダウンテキストの折り返しを設定
proseWrap: "preserve"
# HTMLの空白の感度
htmlWhitespaceSensitivity: "css"
# Vueファイルのスクリプトとスタイルタグのインデントするか
vueIndentScriptAndStyle: false
# 改行コードの指定
endOfLine: "lf"
# 引用コードをフォーマットするか
embeddedLanguageFormatting: "auto"
# 属性を1行ごとにするか
singleAttributePerLine: false
```

# おまけ： Storybookの設定
UIの開発、ドキュメンテーション、テスト、デザインプレビューを支援するためのツール

1\. 以下コマンドで、ストーリブックをインストールする。
```terminal
$ npx storybook@latest init
```
これらの追加変更が行われる。
・必要なモジュールとスクリプトの追加
・デフォルトのStorybook設定とファイルの追加
・サンプルストーリーの追加

2\. イントールに成功して、ブラウザでストーリブックが立ち上げれば完了
※再度ストーリブックを立ち上げたい場合は、「npm run storybook」を実行する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/790127/1ec2dc38-3561-9595-5c6d-4a0449b25365.png)

例）Buttonコンポーネントにストーリーを作成する。
```tsx:src/components/Button/Button.stories.tsx
import type { Meta, StoryObj } from "@storybook/react";
import Button from "./button";

const meta = {
  title: "Components/Button",
  component: Button,
  parameters: {
    layout: "centered",
  },
  tags: ["autodocs"],
  argTypes: {
    children: { control: "text" },
  },
} satisfies Meta<typeof Button>;

export default meta;
type Story = StoryObj<typeof meta>;

export const redButton: Story = {
  args: {
    children: "ボタン"
  },
};
```

# おまけ： テスト環境構築
Next.jsでは、単体テストとして「Jest」と「React Testing Library」がよく使われる。
「Jest」を使用するには、以下の手順が行う必要がある。

1\. 必要なモジュールをインストールする。
```terminal
npm install --save-dev jest jest-environment-jsdom @types/jest @testing-library/react @testing-library/jest-dom @testing-library/user-event
```

2\. プロジェクトルート配下に、jest.config.mjsを作成する。
```js:jest.config.mjs
import nextJest from "next/jest.js"

const createJestConfig = nextJest({
  // Next.jsアプリへのパスを指定して、テスト環境に 「next.config.js」および「.env」ファイルをロードします
  dir: "./",
})

// 「Jest」に渡すカスタム構成を追加
const config = {
  // 各テストを実行する前に、さらにセットアップ オプションを追加
  // setupFilesAfterEnv: ["<rootDir>/jest.setup.js"],

  testEnvironment: "jest-environment-jsdom",
}

// createJestConfig は、next/jest が非同期の Next.js 構成をロードできるようにするために、この方法でエクスポートされます。
export default createJestConfig(config)
```
3\. Jest用のスクリプトを追加する。
```diff_json:package.json
{
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint",
    "format": "prettier --check --ignore-path .gitignore .",
    "format:fix": "prettier --write --ignore-path .gitignore .",
    "storybook": "storybook dev -p 6006",
    "build-storybook": "storybook build",
+   "test": "jest"
  },
}
```

4\. テストコードがある状態で以下コマンドを実行して、テスト開始されれば完了。
```terminal
$ npm run test

> name-list@0.1.0 test
> jest

 PASS  src/components/Button/Button.spec.tsx
  Button
    ✓ ボタンテキストが存在しているかチェック (59 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        1.881 s, estimated 2 s
Ran all test suites.
```
※今回実行したテストコード
```tsx:src/components/Button/Button.spec.tsx
import { render, screen } from "@testing-library/react";
import Button from "./button";
import "@testing-library/jest-dom";

describe("Button", () => {
  const buttonText = "ボタン"
  let buttonNode: HTMLButtonElement;

  beforeEach(() => {
    render(<Button>{ buttonText }</Button>);
    buttonNode = screen.getByRole("button");
  });

  it("ボタンテキストが存在しているかチェック", () => {
    expect(buttonNode).toHaveTextContent(buttonText);
  });
});
```


# 最後に
今回は、Next.js + TypeScriptの環境構築方法を書きました。
簡単に環境構築ができるので、楽でいいなと思いました。
また、App Routerにも挑戦してみたいなと思っています。

まだまだ学習中なので、最終的に何か作れるようになれればいいなと思っています。