Next.jsでカウントアップアプリを０から作成する

# はじめに
こんにちは、プログラミングを始めて約3年のエンジニアのkeitaMaxです。
Next.jsを使ってフロントエンドの開発をしたいと思い、今回開発環境を整えていこうと思います。もしやり方が違ったり、もっといいやり方があるというのがあればぜひ教えていただきたいです。

今回は以下を目標としたいと思います。

1. 自分のローカル環境でNext.jsの環境を作り、ブラウザで初期表示画面を見る。 
2. カウントアップの画面を作成する。 


# 1. 自分のローカル環境でNext.jsの環境を作り、ブラウザで初期表示画面を見る 
まず、自分の開発したいフォルダの中で以下のコマンドを実行します。
```
npx create-next-app@latest
```
するといくつか質問が来るので、以下のように答えます。
```
PS C:\develop> npx create-next-app@latest
Need to install the following packages:
create-next-app@14.1.0
Ok to proceed? (y) y
√ What is your project named? ... next-example-app
√ Would you like to use TypeScript? ...  Yes
√ 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 Router? (recommended) ... No / Yes
√ Would you like to customize the default import alias (@/*)? ...  Yes
√ What import alias would you like configured? ... @/*
Creating a new Next.js app in C:\develop\next-example-app.
```
質問が終わると以下のように表示されます。
```
Success! Created next-example-app at C:\develop\next-example-app
```
これでNext.jsの開発環境が構築できました。

次にブラウザで初期画面を表示します。
以下のコマンドをたたいて実行します。
```
npm run dev
```
すると以下のように実行結果が表示されます。
```
> next-example-app@0.1.0 dev
> next dev

   ▲ Next.js 14.1.0
   - Local:        http://localhost:3000

 ✓ Ready in 2.3s
 ○ Compiling / ...
 ✓ Compiled / in 3s (510 modules)
 ✓ Compiled in 236ms (240 modules)
 ✓ Compiled /favicon.ico in 450ms (515 modules)
 ```
 ここに表示されている`http://localhost:3000`にブラウザでアクセスすると、以下のようにNext.jsの初期画面が表示されました。
 ![localhost_3000_.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3715123/a622ce1e-59e6-ae99-1f34-0d29c5c84255.png)


# 2. カウントアップの画面を作成する
まずはsrc配下にフォルダを新しく作成します。pagesとviews、componentsフォルダを作成します。
フォルダ構成は以下のようになりました。
```
next-example-app/
└── src/
     ├── components/
     ├── pages/
     └── views/
```
pagesはルーティングに使用します。
viewsはpagesからimportして画面表示を主に担当させます。
componentsはviewsで使用するレイアウトの部品を入れておくのに使用します。

次に、pages配下に新しく`count.tsx`ファイルを作成します。
```react:count.tsx
import React, { useEffect, useState } from "react";

const CountPage = React.memo(() => {
  const [isClient, setIsClient] = useState(false);
  useEffect(() => {
    setIsClient(true);
  }, []);
  if (!isClient) {
    return null;
  }

  return (
    <div>
      <p>ここはカウントアップのページです!</p>
    </div>
  );
});

export default CountPage;
CountPage.displayName = "CountPage";
```


そして、以下URLにアクセスをすると`ここはカウントアップのページです!`という文字が表示されていると思います。 
```
http://localhost:3000/count
```
![スクリーンショット 2024-02-17 223433.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3715123/e1766d98-6984-272a-8ded-3bf54509d093.png)

次に、viewsフォルダの配下に`CountView`というフォルダを作成します。その配下に`index.tsx`ファイルを作成します。
```react:index.tsx
import React from "react";

export const CountView = React.memo(() => {
  return (
    <div>
      <p>ここはカウントアップのページです!CountViewを表示しています！</p>
    </div>
  );
});
CountView.displayName = "CountView";

```
この`index.tsx`を`count.tsx`から読み込ませるように修正します。

```react:count.tsx
import { CountView } from "@/views/CountView";
import React, { useEffect, useState } from "react";

const CountPage = React.memo(() => {
  const [isClient, setIsClient] = useState(false);
  useEffect(() => {
    setIsClient(true);
  }, []);
  if (!isClient) {
    return null;
  }

  return <CountView />;
});

export default CountPage;
CountPage.displayName = "CountPage";

```
そして再度以下URLにアクセスすると、`ここはカウントアップのページです!CountViewを表示しています！`と表示されていると思います。
```
http://localhost:3000/count
```
![スクリーンショット 2024-02-17 224237.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3715123/1d05e940-627e-eedb-7675-4e91093bae37.png)


次に実際のカウントアップの機能を作成していきます。
`CountView/index.tsx`を以下のように修正し、新しくCountViewフォルダ配下に`hooks.ts`を作成しました。
`hooks.ts`は`CountView/index.tsx`で使用する処理をフォルダを分けておいています。
```react:index.tsx
import React from "react";
import { useCountView } from "./hooks";

export const CountView = React.memo(() => {
  const { count, addCount } = useCountView();
  return (
    <div>
      <p>{count}</p>
      <button onClick={addCount}>カウントアップ</button>
    </div>
  );
});
CountView.displayName = "CountView";

```
```typescript:hooks.ts
import { useState } from "react";

export const useCountView = () => {
  const [count, setCount] = useState<number>(0);
  const addCount = () => {
    setCount(count + 1);
  };
  return {
    count,
    addCount,
  };
};

```
先ほどのURLにアクセスして確認すると以下のように表示され、カウントアップボタンを押すと数字がカウントアップされます。
![スクリーンショット 2024-02-17 232224.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3715123/9ab1a658-3f96-fe8e-b3ba-95a047017875.png)
![スクリーンショット 2024-02-17 232301.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3715123/6a8b3f54-1b0e-5da6-d263-7eec6569e65f.png)


# おわりに
今回作成したカウントアップのアプリを次回以降、Tailwind CSSを使用して装飾したり、Storybookを使用してテストコードを作成する予定です。

また、最終的にはGitにPushしたときに自動テストを回してOKかNGか判定したり、自動デプロイといったCI/CDができる環境を作成していきたいと思っております。

この記事で質問や、間違っているや、もっといい方法があるといったご意見などありましたらご指摘していただけると幸いです。

また、CI/CDに関してこうしたほうがいいといったアドバイスもしていただけると嬉しいです。

最後まで読んでいただきありがとうございました！


# 次の記事

https://qiita.com/keitaMax/items/81ecc44a7a657455f5bd