はじめに
最近、Next.jsでの開発を進めていて、いつも使っている 「npx create-next-app
って何してるんだ?」という疑問が頭をよぎりました。
よく、ふとした瞬間に、「これって本当に理解して使ってる?」と自問自答することってありますよね。特に、誰かに説明しようとすると、理解を曖昧にしているせいで思った以上に言葉に詰まることが多いです。
例えば、公式ドキュメントの「Installation」を参照してプロジェクトをセットアップする際に、手順をコピー&ペーストして「動いたからOK」としてしまうことが多いかと思います。実は私もそんなタイプです😅
この記事では、Next.jsでプロジェクトを作る過程で、「なぜそうなるのか」を掘り下げながら、より深く理解していくことを目指します。
Next.jsプロジェクトの作成
では、Next.jsのプロジェクトを作成していきます。
まずはいつも通り、Next.js 公式ドキュメント に記載されているまま進めていこうと思います。
-
Node.jsをインストールする
現在は Node.js 18.17 or later. と書いてあるので、Node.jsのダウンロードページ から v18.17以降 の Node.js をインストールします。
-
Next.jsのプロジェクトを作成する
以下のコマンドを実行してNext.jsのプロジェクトを作成します。
npx create-next-app@latest
-
開発サーバーを起動します。
以下のコマンドを実行して、開発サーバーを起動します。
cd my-app npm run dev
ここまでが、Next.jsのプロジェクトを作成し、Next.jsアプリケーションを起動する手順となります。
初めてNext.jsのプロジェクトを作成する方やnpmを使用する方にとっては、これらの説明の中で、以下のような疑問が出てくるかと思います。
- Next.js に Node.js が必要な理由は?
-
npx
とは何か? -
npx create-next-app
で何が行われているのか? -
npm run dev
で何が行われているのか?
私自身、基礎を疎かにしがちな人間なので、何度も「あれ、これ前も調べたよね?」という状況になってしまうことがあります。そんな経験を活かして、今回はこれらの疑問について自身も学習しながら解説していきたいと思います。
疑問1. Next.js に Node.jsが必要な理由は?
『Node.jsとは何者なのか?』という説明を始めると、それだけで記事一本書けてしまう程のボリュームになってしまうので、本記事ではNode.jsは以下の役割をするツールだと思ってください。
役割1. JavaScriptをサーバーサイドで実行するためのランタイム
Next.jsは Node.js上で動作するフレームワーク です。
そのため、Next.jsのアプリを実行するためには、Node.jsが必須となります。
これだけではイメージが湧きにくいかもしれませんので、図で表現してみます。ここでは、ローカルPCからWebサーバー上にデプロイされたNext.jsアプリケーションにリクエストを送信するまでの流れを図解します。
- 「ローカルPC」からリクエストが「Webサーバー」へ送信されます。これは一般的なクライアントとサーバー間の通信です。
- 「Webサーバー」上の「Next.jsアプリケーション」がそのリクエストを受け取ります。Next.jsはNode.jsを使用してWebサーバー機能を提供します。
- Next.jsアプリケーションはNode.jsランタイム上で動作しており、リクエストを処理することができます。この段階で、サーバーサイドレンダリングやAPIの呼び出しなどが行われます。
- 「Next.jsアプリケーション」がリクエストを処理し、「ローカルPC」にレスポンスのHTMLを返します。このHTMLはNext.jsによって生成され、必要に応じてプリレンダリングされたページや動的に生成されたページを含むことができます。
このことから「Next.jsアプリケーション」は Node.jsランタイム上 で動作していることがわかります。
Node.jsは一般的に サーバーサイド でJavaScriptを実行するランタイム環境として知られていますが、この説明ではインターネット越しの リモートサーバーで実行される というイメージが強調されがちです。しかし、ローカルPCにNode.jsをインストールし、Next.jsの開発サーバーを立ち上げて、Node.jsランタイム上でNext.jsアプリケーションを実行することも可能ですので、ローカルサイドでもNode.jsは使用される というイメージで理解していただければと思います。
現在、Node.jsはサーバーサイドのアプリケーション開発だけでなく、フロントエンドの開発ツール としても広く使われています。
役割2. Node.jsのパッケージを使用するためのもの
Node.jsは、サーバーサイドでJavaScriptを実行するランタイム環境として知られていますが、それだけでなく、npm というパッケージマネージャーも提供しています。(Node.jsをインストールすると、npmも一緒にインストールされます)
Node.jsのパッケージとは、「TypeScript」や「Prettier」などのツールやライブラリのことを指します。これらを使用するためには、Node.jsをインストールする際に同梱される npm を利用してパッケージをインストールする必要があります。
Node.jsのパッケージは npmレジストリ で公開されており、npmコマンドを使用してインストールできます。
例えば、TypeScriptを使用するためには、以下のコマンドを実行してTypeScriptをインストールします。
npm install -g typescript
Next.jsのプロジェクトを作成する際にも、Node.jsのパッケージである create-next-app
を使用して、Next.jsのプロジェクトを作成しています。
create-next-app
も以下のnpmレジストリで公開されており、npmコマンドを使用してインストールすることができます。
疑問2. npxとは?
次に npx
について説明していきます。
コマンドはどのように実行されているのか?
まずは node index.js
のように「nodeコマンド」を実行する方法を改めて確認しましょう。
『そんなの知ってるよ!』と思う方もいるかもしれませんが、改めて確認したいと思います。
まず、コマンドを実行するためには コマンドラインツール を使用します。
コマンドラインツールのありかは、以下のコマンドで確認できます。
$ echo $PATH | tr ':' '\n'
・・・
/usr/local/bin
・・・
一般的なコマンドラインツールは /usr/local/bin
に配置されており、ここに配置することでコマンドが実行できるようになります。このパスはデフォルトで設定されているパスとなります。
Unix系のオペレーティングシステム(macOSやLinuxなど)では、多くのコマンドラインツールやプログラムが /usr/local/bin
ディレクトリにインストールされます。
実際に node のパスを確認すると、以下のようになります。
$ which node
/usr/local/bin/node
node index.js
を実行すると、以下のような実行フローになります。
npmとは何か?
npm は Node.js インストール時に /usr/local/bin
にインストールされるので、「npmコマンド」を実行できます。
$ which npm
/usr/local/bin/npm
しかし、/usr/local/bin/npm
は、シンボリックリンクが設定されたエイリアスファイルになっています。
Node.jsのパッケージは通常 /usr/local/lib/node_modules
にインストールされます。そのため、グローバルにコマンドを実行するためには、/usr/local/bin
にシンボリックリンクを設定することが一般的です。このシンボリックリンクは、/usr/local/lib/node_modules
の実行可能ファイルを刺しています。
npmのシンボリックリンクを確認すると、以下のようになっています。
$ ls -l $(which npm)
lrwxr-xr-x 1 root wheel 38 5 11 14:33 /usr/local/bin/npm -> ../lib/node_modules/npm/bin/npm-cli.js
これは、/usr/local/bin/npm
が /usr/local/lib/node_modules/npm/bin/npm-cli.js
へのシンボリックリンクであることを示しています。
そのため、npmコマンドを実行すると、/usr/local/lib/node_modules/npm/bin/npm-cli.js
のJavaScriptプログラムを実行していることがわかります。
Node.jsパッケージをインストールして使用する
Node.jsのパッケージをインストールする際には、「npm」を使用します。
npm install -g {パッケージ名}
このコマンドでパッケージをグローバルにインストールすると、システム全体で使用できるようになります。たとえば、TypeScriptをグローバルにインストールすると、どのディレクトリにいてもTypeScriptコマンドを実行できるようになります。
npm install -g typescript
cd {任意のディレクトリ}
tsc init ← カレントディレクトリがどこでも実行できる
グローバルにインストールされたパッケージは、実行可能ファイルが /usr/local/bin/
に配置されるため、どこからでもコマンドを実行できます。
$ which tsc
/usr/local/bin/tsc
なぜ typescript 〜
ではなく tsc 〜
で実行するのかというと、これはnpmパッケージのpackage.json
ファイル内の binプロパティ によるものです。このプロパティを利用して、 パッケージ名とは異なるコマンド名を指定できます。
TypeScriptの場合、binプロパティに tsc
が設定されているため、tsc 〜
で実行することができます。
{
"name": "typescript",
・・・
"bin": {
"tsc": "./bin/tsc"
},
・・・
}
Node.jsのパッケージは通常 /usr/local/lib/node_modules
にインストールされますので、TypeScriptも /usr/local/lib/node_modules/typescript/bin/tsc
にインストールされることになります。
$ ls -l $(which tsc)
lrwxr-xr-x 1 root wheel 38 5 11 15:25 /usr/local/bin/tsc -> ../lib/node_modules/typescript/bin/tsc
この出力結果は、/usr/local/bin/tsc
が /usr/local/lib/node_modules/typescript/bin/tsc
へのシンボリックリンクであることを示しています。これにより、tsc
コマンドをどこからでも実行できるようになっています。
グローバルにインストールされているパッケージの一覧を見てみましょう。以下のコマンドを使用すると、/usr/local/lib/node_modules
にインストールされたNode.jsのパッケージを確認できます。
$ npm -g list
/usr/local/lib
・・・・
├── npm@10.5.2
└── typescript@5.4.5
npxとは何か?
ようやく、npxについての説明となります。
npmはパッケージを管理するためのツールで、インストールしたパッケージを管理 します。一方で、npxはパッケージを インストールせずに実行 するためのツールです。これにより、一時的なパッケージの実行や、プロジェクト固有のパッケージの実行が可能になります。
npxのパスを確認すると、以下のようになっています。
$ ls -l $(which npx)
lrwxr-xr-x 1 root wheel 38 5 11 14:33 /usr/local/bin/npx -> ../lib/node_modules/npm/bin/npx-cli.js
この出力から、/usr/local/bin/npx
は /usr/local/lib/node_modules/npm/bin/npx-cli.js
へのシンボリックリンクであることがわかります。つまり、npxは npmに付属しているツール ということになります。
疑問3. npx create-next-app
で何が行われているのか?
「create-next-app」は、Next.jsのプロジェクトを作成するためのパッケージです。このパッケージをグローバルにインストールして使用することもできますが、npxを使用することで、インストールせずに直接実行することが可能です。
npxの動作を理解するためには、実際にコマンドを実行してみるのが最も分かりやすいと思いますので、以下のコマンドを試してみます。
npx create-next-app@latest
このコマンドを実行すると、Next.jsのプロジェクトが作成されます。具体的には、以下の処理が行われています。
-
まず、カレントディレクトリのnpmプロジェクトに create-next-app というパッケージがインストールされているかを確認します。
-
もしインストールされていなければ、npmレジストリから create-next-app パッケージをダウンロードし、一時的にインストール します。
npmレジストリからnpxでパッケージを使用した場合、
/Users/{ユーザ名}/.npm/_npx
に「一時ディレクトリ」が作成され、その配下に「node_modules」が作成されます。ここにパッケージが一時的にインストールされ、使用されます。 -
パッケージがインストールされたら、create-next-app を実行し、Next.jsのプロジェクトが作成されます。
このプロセスにより、グローバルにパッケージをインストールせずにパッケージを実行することができる のがnpxの特徴です。
疑問4. npm run dev
とは何をしているのか?
npm run {スクリプト名}
は、package.json
の scripts セクションに記述されたスクリプトを実行するコマンドです。例えば、Next.jsプロジェクトの package.json
で dev というスクリプトが next dev
というコマンドを実行するように定義されています。
このため、devスクリプト は実質的に next dev
を実行します。
next dev
コマンドを使用すると、Next.jsの開発サーバーが起動します。ただし、「nextパッケージ」はNext.jsプロジェクトの「node_modules」に配置されているため、カレントディレクトリがNext.jsプロジェクト内である場合のみ 使用できます。
プロジェクト固有のパッケージを使用したい場合は npx
を利用します。この場合、npx next dev
と実行することで、プロジェクト内で使用可能な next
パッケージ使用し、npm run dev
と同様の動作を行うことができます。
まとめ
今回は、Next.jsプロジェクト作成の裏側で行われていることを学びながら解説しました。以前はこの辺りの知識が曖昧で、不明確な説明をしてしまうことがありましたが、今回の学習を記事にまとめたことで、より詳細で正確な説明ができるようになったと思います。
基本的なコマンド実行の仕組みやnpmの仕組みを理解することで、今後の開発での問題解決や応用に役立つため、他の技術に対しても同じアプローチで、基礎からしっかりと学習を進めることが重要だと感じました。