Open23

Next.jsで電卓をつくってみる

fuutotfuutot

背景

新卒社会人に向けて開発力を身につけていきたい。
以下のPostにこんな順番でアプリを開発したらどうかという提案がされていたのでできそうなものから取り組んでみる。

Next.jsを選んだのは、フロントエンドもバックエンドも開発できるらしく、アプリがどのような技術で動いているのか一通り経験できるのではと考えたため。まずは電卓を作ってみる。

fuutotfuutot

機能

開発の流れをさらうためにシンプルな機能に限定する

  • 足し算
  • 引き算
  • 掛け算
  • 割り算
fuutotfuutot

一般的な計算機のように計算結果に対して再度計算できるようにする

fuutotfuutot

プロジェクトの作成

npx create-next-app@latest calculator

npxコマンドってなんだっけ?

fuutotfuutot

https://qiita.com/kohta9521/items/ee3ed4a2360add80ad79

  • npm
    • node package manager
    • 依存性、パッケージのマネージャー
    • Node.jsをインストールするとついてくる
  • npx
    • node package executer
    • パッケージの実行ツール
    • npm 5.20から一緒についてくる

両方パッケージの実行はできるけど、パッケージのインストールが必要かどうかが違うポイント?

fuutotfuutot

https://zenn.dev/osachi/articles/9f0a684fc7f0db
そうかも?
npmは実行にパッケージのインストールが必要で、npxは実行にパッケージのインストールが必要ではない

だからパッケージがインストールされていないプロジェクトの初期化はnpxコマンドなのかも

fuutotfuutot

開発サーバーの実行

npm run dev

裏側で何か動いているのか?

fuutotfuutot

package.jsonのscriptsdevの部分が動いているみたい
以下の例だと、next dev --turbopackが動いている

package.json
{
  "name": "calculator",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev --turbopack",
    "build": "next build --turbopack",
    "start": "next start",
    "lint": "eslint"
  },
  "dependencies": {
    "react": "19.1.0",
    "react-dom": "19.1.0",
    "next": "15.5.2"
  },
  "devDependencies": {
    "typescript": "^5",
    "@types/node": "^20",
    "@types/react": "^19",
    "@types/react-dom": "^19",
    "@tailwindcss/postcss": "^4",
    "tailwindcss": "^4",
    "eslint": "^9",
    "eslint-config-next": "15.5.2",
    "@eslint/eslintrc": "^3"
  }
}
fuutotfuutot

package.json はプロジェクトの設定ファイル
scripts: よく使うコマンドをまとめられる
dependencies: 本番環境でも必要なライブラリを記載
devDependencies: 開発時だけ必要なライブラリを記載

fuutotfuutot

直接実行できなかった

next dev --turbopack

グローバルインストールしていないから?
グローバルインストールしてみる

npm install -g next@15.5.2

実行できた
直接実行するためにはグローバルインストールが必要みたい

fuutotfuutot

Next.jsのディレクトリ構成

.
├── README.md
├── app/ : Next.jsのアプリのディレクトリ
├── eslint.config.mjs : Lintの設定ファイル
├── next-env.d.ts : Next.js用のTypeScript型定義ファイル
├── next.config.ts : Next.jsの設定ファイル
├── node_modules/
├── package-lock.json
├── package.json : 依存パッケージやスクリプト、プロジェクト情報を管理
├── postcss.config.mjs : 
├── public/ : URLで直接アクセス可能なファイル
└── tsconfig.json
fuutotfuutot

appディレクトリには以下のファイルが入っている

.
├── favicon.ico
├── globals.css : グローバルなCSSスタイル
├── layout.tsx : 全体レイアウトを定義するコンポーネント
└── page.tsx : トップページのコンポーネント
fuutotfuutot

Huskyの導入
サバイバルTypeScriptのチュートリアルからHuskyの存在を知った
チュートリアルは工事中だったため調べながらやってみる
https://typicode.github.io/husky/

fuutotfuutot

Huskyとは
Gitのフックを簡単に管理するためのツール
Gitの特定のイベントに特定のプログラムを実行させることができる
コード整形とか、テスト実行を自動化する目的に使われる様子

fuutotfuutot

セットアップ

  • インストール
    Gitのフックを管理するためのツールなので開発環境のみにインストールする
npm install --save-dev husky
  • 初期化
    huskyを初期化する
    コミット前に実行されるフックが設定されたり、プロジェクトがインストールされるたびにHuskyが自動的にセットアップされるようになる
npx husky init
fuutotfuutot

動作確認
huskyを初期化するとpre-comit(コミット時のフック)にnpm testが追加されるみたい
npm testはまだ使えないので、npm run lintに変更すると、動作することを確認できた

fuutotfuutot

lint-stagedの導入
Huskyをもっと便利に使うことができるみたい
ステージされたgit fileに対してフォーマッターやリンターを適応することができるみたい
https://github.com/lint-staged/lint-staged

fuutotfuutot

セットアップ

  • list-stagedのインストール
npm install --save-dev lint-staged
  • Prettierのインストール
npm install --save-dev --save-exact prettier

save-exactオプションは指定したバージョンで固定するもの

  • lint-stagedの設定
    フックにlint-stagedを追加
echo "npx lint-staged" > .husky/pre-commit
  • 何に対して、何を実行するのかを追加
.lintstagedrc
{
  "*.{js,jsx,ts,tsx}": [
    "eslint -c .eslintrc.js --fix",
    "prettier --write '**/*.{js,jsx,ts,tsx,json}'"
  ]
}

https://zenn.dev/nikawa2161/articles/7b2b0dcce35b49#lint-stagedの設定

fuutotfuutot

.eslintrc.jsが存在しておらず、エラーが発生したため以下のように修正

.lintstagedrc
{
  "*.{js,jsx,ts,tsx}": [
    "eslint -c eslint.config.mjs --fix",
    "prettier --write '**/*.{js,jsx,ts,tsx,json}'"
  ]
}
fuutotfuutot

電卓の見た目を作る
とりあえずiPhoneの電卓みたいな感じがいいな

fuutotfuutot

まず中央に電卓をもってくるために、中央寄せするコンテナが欲しいな
Next.jsでは共通レイアウトをlayout.tsxに定義する

layout.tsx
import './globals.css';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="ja">
      <body>
        <div className="center-container">
          {children}
        </div>
      </body>
    </html>
  );
}

スタイルはglobal.cssに定義する

global.css
.center-container {
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 100vh;
  padding: 1rem;
}

https://zenn.dev/engineerhikaru/books/660440f08dde44/viewer/f2b524

fuutotfuutot

export defaultはNext.js側が要求しているもの
defaultとある通り、1つのファイルで1つしかexport defaultできない