mosya

Next.js入門 - Next.js導入まで

この記事はすでにReactの基礎を学習している方が対象になります。
また、App RouterというNext.jsの新機能については解説しておりませんのでご注意ください。

Next.jsとは

Next.jsとはReactを使ってサイトやWebアプリを制作するのに特化したフレームワークです。
ページ全体をReactを使って構築することができます。またクライアントサイドだけでReactが動作するだけではなく、あまりサーバーサイドを意識しなくてもReactを使って事前にページのHTMLを生成しておくことができます。
Next.jsを使わない場合、Expressなどのサーバーサイドライブラリを使い、ページをサーバーサイドで事前レンダリングする必要があるのですがサーバーサイドからフロントエンドへの繋ぎ込みが複雑になりがちです。
サーバーサイドでの描画とフロントエンドでの描画が一致するようにHTMLを生成する必要がありますし、またフロントエンドに描画に必要なデータをサーバーサイドから渡してあげる必要があります。
僕にはとても管理できる自信がないです。

そこでNext.jsの登場です。Next.jsでは静的ファイル生成やサーバーサイドレンダリングなど様々な手段でフロントエンドへの繋ぎ込みが行えます。

サーバーサイドレンダリングと静的ファイル生成

Next.jsを使えば以下のような手段で事前にHTMLを生成することができます。Next.jsの肝になる機能なのでこの3つは覚えておきましょう

  • SSR(サーバーサイドレンダリング)
  • SSG(スタティックサイトジェネレーション)
  • ISR(インクリメンタルスタティックリジェネレーション)

それぞれ解説していきましょう。

SSR

まずはSSRについてです。SSRとはサーバーサイドでReactを使ってHTMLをレンダリングすることができます。
SSRはリクエストがあるたびに実行される機能なのでマイページなどユーザー属性に応じてレンダリングが必要とされるページに最適です。

SSG

SSGは最初からHTML自体をビルド時に吐き出しておくことを指します。サーバーサイドではキャッシュを返すだけで済むので分素早くページが表示されます。
リクエストに応じて別々の結果を返す必要のないページ、例えば投稿記事などに使える機能になります。

ISR

ISRはSSGの発展系です。SSGははビルド時に一度だけHTMLを吐き出しておくのに対してISRはページ閲覧時に以前事前に生成しておいたHTMLを返しつつ、裏では新たなHTMLを生成しなおします。
たまにコンテンツが書き変わる可能性のあるページなどはISRを利用すると良いでしょう。

ルーティング

またNext.jsを使うとURLごとのルーティングが本当に楽です。Next.jsを使わない場合、react-router-domなどのルーティングライブラリを別途使ってURLごとのルーティングを実装しないといけないですが意外と面倒です。
Next.jsの場合、pages/直下にファイルを置いていくだけでそのファイル名がそのままURLとなります。
例えば、pages/sample.tsxというファイルを設置するとhttps://localhost:3030/sampleというURLが自動で作られます。
また、URLにパラメーターを設置することができます。
例えば、pages/articles/[slug].tsxというファイルを設置すると、https://localhost:3030/articles/**という形でURLが生成され、**に入っているパスを変数として取り出して処理することができます。
このようにページごとにファイルを設置していくのは視覚的にとてもわかりやすいです。

開発体験

Next.jsは開発体験がとても良いです。通常、Reactを使った開発の場合、webpack-devserverなどの外部ライブラリを用いてホットリロードの設定をする必要がありますが、Next.jsは最初からそれが組み込まれています。
ファイルを保存したら即時にブラウザに適用されるのでストレスがありません。

API

Next.jsはAPIの開発も可能です。pages/api/以下にファイルを設置することでそのファイル名がapi名として使われます。
例えば、pages/api/items.tsというファイルを設置すると/api/itemsというAPIが作られます。

ただし、Next.jsのAPIはとても簡素なもので込んだサーバーを作りたい場合は、別途別のサーバーサイドライブラリや言語を用いてAPIを作った方がいいかもしれません。

他のフレームワークとの違い

Next.jsと似たようなフレームワークとしてNuxt.jsというものがあります。
Nuxt.jsはVue.jsというUIライブラリをベースとしたフレームワークです。基本的にNext.jsとNuxt.jsは同じような機能を有していますが、Next.jsはサイトのパフォーマンス向上と開発体験に力を入れているような印象です。
また、Next.jsで作られたプロジェクトはNext.jsに最適化されたVercelというサービスにGit連携で簡単にデプロイすることができます。

Next.jsを使ってみる

さていよいよNext.jsを使ってみましょう。
Next.jsはNode.jsさえあれば簡単に環境をセットアップできます。
Node.jsをインストールされていない方は以下のリンクからNode.jsをインストールしセットアップすることができます
https://nodejs.org/ja/

Node.jsをインストール済みの方はターミナルを開き以下のコマンドを入力してみましょう。

npx create-next-app

Next.jsをTypeScriptで始めたい方は--typescriptフラグをつけて以下のコマンドを入力してみましょう

npx create-next-app --typescript

ページを作ってみる

次にNext.jsを使って一つページを作ってみましょう。pages/sample.tsxファイルを作ってみましょう。
作った後、ファイルに以下のように記述してみます。

const Sample = () => {
  return <div>サンプルページ</p>
}

export default Sample;

https://localhost:3030/sample にアクセスしてみて、サンプルページが表示されたら成功です。

サーバーサイドで処理をしてページを表示する(SSR)

次に事前にサーバーサイドでAPIからデータを受け取ってそれを表示するページを作ってみましょう。
写真一覧を表示するページを作ってみます。
まずはpages/photos.tsxファイルを作ってみましょう。
事前にサーバーサイドで処理をするために以下のようにgetInitialPropsというメソッドを使います。

const Photos = ({ photos }) => {
  return <div>
    {photos.map(photo => <div key={photo.id}>
      <img src={photo.thumbnailUrl} alt={photo.title} />
    </div>)}
  </p>
}

Photos.getInitialProps = async () => {
  const res = await fetch("https://jsonplaceholder.typicode.com/photos")
  const photos = await res.json()
  return { photos }
}

export default Photos

https://localhost:3030/photos にアクセスしてみましょう。
写真一覧が表示されましたね。

サーバーサイドではなく事前にページを生成してみる(SSG)

次はSSGに挑戦してみましょう。先程のphotos.tsxに一工夫加えて、SSRではなくSSGしてみます。
ただしSSGしてしまうとビルド以降ページが更新されることはないのでAPIの結果が変わったとしてもページに何も変更が発生しない点にご注意ください。
Photos.getInitialPropsをgetStaticPropsに書き直します。ここでgetStaticPropsの場合は、returnする際にpropsでネストしてあげる必要がある店にご注意ください。

const Photos = ({ photos }) => {
  return (
    <div>
      {photos.map((photo) => (
        <div key={photo.id}>
          <img
            src={photo.thumbnailUrl}
            alt={photo.title}
          />
        </div>
      ))}
    </div>
  );
};

export default Photos;

export const getStaticProps =
  async () => {
    const res = await fetch(
      "https://jsonplaceholder.typicode.com/photos"
    );
    const photos = await res.json();
    return {
      props: {
        photos,
      },
    };
  };

https://localhost:3030/photos にアクセスしてみましょう。
先程と同じように写真一覧が表示されていますが今回はサーバーサイドで処理されているわけではなく静的ファイルがそのまま表示されています。

ユーザーのリクエストに応じて裏側で定期的にページ生成してみる(ISR)

最後にISRしてみましょう。ISRすることでHTMLキャッシュが定期的に作られるためAPIが更新されてもそのAPIに追従したページが再生成されます。
以下のページではこのページがよばれて10秒後には裏側で新しいHTMLキャッシュが作られるようにするサンプルです。
先程のソースコードにrevalidate設定を追加しただけです。簡単ですね!

const Photos = ({ photos }) => {
  return (
    <div>
      {photos.map((photo) => (
        <div key={photo.id}>
          <img
            src={photo.thumbnailUrl}
            alt={photo.title}
          />
        </div>
      ))}
    </div>
  );
};

export default Photos;

export const getStaticProps =
  async () => {
    const res = await fetch(
      "https://jsonplaceholder.typicode.com/photos"
    );
    const photos = await res.json();
    return {
      props: {
        photos,
      },
      revalidate: 10,
    };
  };

ISRを利用して個別ページも作ってみる

写真一覧ページを作ったので次は写真の個別ページを作ってみましょう。
写真APIから取得できる写真ごとのidを使って写真ごとにページを作ります。
まず、pages/photos/[id].tsxというファイル名でファイルを使います。文字通り[id].tsxというファイル名で作ってください。
今回はgetStaticPropsに加えgetStaticPaths関数を定義して、[id]に対するパスを解決します。
例えば、APIから取得できるアイテムのidが1,2,3...の場合、この関数でphotos/1, photos/2, photos/3のパスが存在することを定義します。
また、getStaticPropsの返却値に対してrevalidateを定義することでリクエストがあってから何秒後にHTMLを再ビルドするかを決定します。

const Photo = ({ photo }) => {
  return (
    <div>
      <h1>{photo.title}</h1>
      <img src={photo.url} />
    </div>
  );
};

export default Photo;

export const getStaticPaths =
  async () => {
    const res = await fetch(
      "https://jsonplaceholder.typicode.com/photos"
    );
    const photos = await res.json();
    const paths = photos.map(
      ({ id }) => ({
        params: {
          id,
        },
      })
    );
    return { paths, fallback: false };
  };

export const getStaticProps = async ({
  params,
}) => {
  const { id } = params;
  const res = await fetch(
    `https://jsonplaceholder.typicode.com/photos/${id}`
  );
  const photo = await res.json();
  return {
    props: {
      photo,
    },
    revalidate: 10,
  };
};

https://localhost:3030/photos/1 などにアクセスしてみましょう。
無事にページが表示できていることが確認できます。

まとめ

以上、Next.jsの紹介でした。他にも細かい機能はたくさんありますが、ファイルを使ったルーティングやSSR, SSG, ISRを理解していればNext.jsの肝は抑えたと言ってもいいでしょう。

細かい機能はまだたくさんありますが検索していけば解決できる場合が多いと思います。

次の記事ではより詳しいNext.jsの機能を紹介できればと思います。

Authored by

筆者の写真

Godai@steelydylan

Webサービスを作るのが好きなWebエンジニア。子供が産まれたことをきっかけに独立し法人化。サービス開発が大好き。
好きな言語はTypeScript。

ReactやTypeScriptなどの周辺技術が学べる
オンライン学習サービスを作りました!

詳しくはこちら
mosya

mosyaはオンラインでHTML,CSS,JavaScriptを基本から学習できるサービスです。現役エンジニアが作成した豊富なカリキュラムに沿って学習を進めましょう。

© 2023 - mosya. All rights reserved.