Next.js入門ガイド

プロジェクトの作成

Next.jsプロジェクトを始めるには、以下のコマンドを使用します:

npx create-next-app@latest

ファイルベースのルーティング

Next.jsは、pagesディレクトリ内のファイル構造に基づいて自動的にルーティングを設定します。たとえば、pages/about.jsファイルは/aboutパスに対応します。

動的ルートパラメータ

注意:URLマッチング時、静的ルートが動的ルートよりも優先されます。

1. 基本的な使用法

http://localhost:3000/about/1 のようなURLで動的パラメータを取得できます。

import { useRouter } from 'next/router';

export default function ProjectDetailPage() {
  const router = useRouter();
  
  return (
    <div>
      プロジェクト詳細。IDは: {router.query.id}
    </div>
  );
}

2. ネストされたルートパラメータ

複数階層のルートパラメータも使用できます。

3. 複数のパラメータ

複数のパラメータは配列として扱われます。

ナビゲーション

1. Linkコンポーネント

(1) 基本的な使用法

import Link from 'next/link';

<Link href="/">
  ホームページ
</Link>

(2) UrlObjectの使用

interface UrlObject {
  auth?: string | null | undefined;
  hash?: string | null | undefined;
  host?: string | null | undefined;
  hostname?: string | null | undefined;
  href?: string | null | undefined;
  pathname?: string | null | undefined;
  protocol?: string | null | undefined;
  search?: string | null | undefined;
  slashes?: boolean | null | undefined;
  port?: string | number | null | undefined;
  query?: string | null | ParsedUrlQueryInput | undefined;
}

2. プログラムによるナビゲーション(router.push、router.replace)

3. 404ページ

pagesディレクトリに404.jsという特殊なファイルを作成すると、Next.jsは404エラー発生時に自動的にこのコンポーネントをロードします。これにより、カスタムの404ページを実装できます。

静的ファイル

ルートディレクトリのpublicフォルダーに配置された静的ファイルはNext.jsによって自動的に処理されます。このフォルダー外の静的ファイルにはアクセスできません。

CSSモジュール

1. コンポーネントスタイル

Next.jsは、JSコンポーネント名.module.cssというファイル名の規約によってCSSモジュールをサポートします。これにより、スタイルはバインドされたコンポーネントにのみ影響します。

2. グローバルスタイル

ルートディレクトリのstylesフォルダーにglobals.cssを作成し、アプリケーション全体で使用できます。

静的サイト生成(SSG)

SSGはビルド段階でページを事前に生成する静的サイト生成技術です。Next.jsはデフォルトで動的データのないページを事前にレンダリングしますが、動的データはReactと同様にクライアント側でレンダリングされます。

HTMLソースコードに動的データを表示するには、ページ内のgetStaticPropsメソッドを使用します。このメソッドはサーバーサイド環境で実行され、データを取得してレンダリングし、クライアントにはメソッド内のコードは送信されません。

1. getStaticProps

getStaticPropsメソッドの戻り値の型は以下の通りです:

export type GetStaticPropsResult<P> =
  | { props: P; revalidate?: number | boolean }
  | { redirect: Redirect; revalidate?: number | boolean }
  | { notFound: true; revalidate?: number | boolean }

(1) 基本的な使用法

通常、データを正常に取得した後は最初の形式で返します。

(2) revalidate

(3) redirect、notFound

データ取得に失敗した場合、ユーザーを次の操作に誘導するためにリダイレクトまたは404エラーを返します。

(4) 動的ルートパラメータの取得(context)

pages/[pid].jsファイルでは、[pid]が動的であるためNext.jsが事前にレンダリングできません。getStaticPathsを使用する必要があります。

2. getStaticPaths

[]を含むファイル名のJSファイルで静的ページを生成するには、getStaticPathsメソッドを使用します。getStaticPathsメソッドは、静的ページを生成するための一連のパスを定義し、各データに対してgetStaticPropsが呼び出されてデータを取得します。したがって、getStaticPathsを使用するには必ずgetStaticPropsを定義する必要があります。

(1) 基本的な使用法

pages/[pid].jsで使用します。この場合、ページを開くとすべてのJSONデータがブラウザに送信されます。

(2) fallback

  1. fallbackがfalseの場合、URLリクエストのパラメータがpathsプロパティで定義されていないと、直接404ページが返されます。
  2. fallbackがtrueの場合、Linkコンポーネントでページ内をナビゲートする問題はありません。しかし、URLで直接pathsにリストされていないパスにアクセスするとエラーが発生します。Reactコンポーネント内でpropsのパラメータを判断し、サーバーが準備できる前にロード中のメッセージを返す必要があります。
  3. ブール値のほかに、fallbackには'blocking'を割り当てることもできます。これにより、データが準備できていない場合リクエストをブロックし、ページのレンダリングが完了してからページを返します。2番目のケースと比較して、コンポーネント内での判断という手間が省けます。

fallbackがtrueの場合、存在しないリクエストパスにアクセスする際、getStaticProps内で直接{ notFound: true }を返して404ページを返すことができます。

サーバーサイドレンダリング(SSR)

SSRはサーバーサイドレンダリングであり、getServerSidePropsメソッドは各リクエストに対して処理を行うことができ、データ頻繁に変化するページに適しています。

getStaticPropsとgetServerSidePropsはどちらか一方のみ使用できます。

getServerSidePropsもサーバー上で実行されるメソッドで、このメソッドのパラメータcontextにはリクエストのすべてのデータを取得できます。contextの型は以下の通りです:

export type GetServerSidePropsContext<Q extends ParsedUrlQuery = ParsedUrlQuery, D extends PreviewData = PreviewData> = {
  req: IncomingMessage & {
    cookies: NextApiRequestCookies
  }
  res: ServerResponse
  params?: Q
  query: ParsedUrlQuery
  preview?: boolean
  previewData?: D
  resolvedUrl: string
  locale?: string
  locales?: string[]
  defaultLocale?: string
}

getServerSidePropsの戻り値の型:

export type GetServerSidePropsResult<P> =
  | { props: P | Promise<P> }
  | { redirect: Redirect }
  | { notFound: true }

getServerSidePropsの戻り値の型は基本的にgetStaticPropsと同じですが、revalidateプロパティがありません。これはgetServerSidePropsが各リクエストに対して再レンダリングを行うためです。

(1) 基本的な使用法

事前レンダリングに適さないケース

  1. データが非常に頻繁に変化するページ(株価データなど)
  2. ユーザーIDと密に結びついたページ(ユーザーの最近のアクティビティなど)
  3. ページ内の一部のデータのみが異なる場合

これらのケースでは、クライアント側でuseEffect内でfetchを使用してデータを取得するのが適しています。Next.jsチームはクライアントリクエストを簡素化するためのReactフックライブラリSWRも開発しました:

https://swr.vercel.app/zh-CN

ページがフォーカスを得るたびに、SWRフックはデータを取得するためにリクエストを再送信します。

npm i swr
import useSWR from 'swr'

const fetcher = (...args) => fetch(...args).then((res) => res.json())

function UserProfile() {
  const { data, error } = useSWR('/api/user-profile', fetcher)

  if (error) return <div>読み込みに失敗しました</div>
  if (!data) return <div>読み込み中...</div>

  return (
    <div>
      <h1>{data.name}</h1>
      <p>{data.bio}</p>
    </div>
  )
}

メタ情報の追加

1. 基本的な使用法

2. Headコンポーネントの再利用

3. グローバルなHead

グローバルなHeadコンポーネントは_app.jsファイルに追加できます。同じヘッダータグはマージされ、マージのルールは最後にレンダリングされたHeadが以前のHeadを上書きします。

もう一つのグローバルな特殊なJSファイルは/pages/_document.jsで、このファイルのデフォルト値は以下の通りです:

import { Html, Head, Main, NextScript } from 'next/document'

export default function CustomDocument() {
  return (
    <Html>
      <Head />
      <body>
        <Main />
        <NextScript />
      </body>
    </Html>
  )
}

_app.jsファイルはbody内の内容に相当し、_document.jsはHTML全体に相当します。ここでのHeadコンポーネントのインポートパッケージは通常のページでインポートするパッケージと異なるので、間違えないように注意してください。

画像の最適化

Next.jsは画像を最適化するためのImageコンポーネントを提供します。

Imageコンポーネントを使用する4つの利点:

  1. 各デバイスに適したサイズとフォーマットを使用(Chromeでページにアクセスすると、画像はwebp形式に変換されます)
  2. 累積レイアウトシフト(CLS)を防止
  3. 画像がビュー内にのみロードされる
  4. 画像サイズをカスタマイズ可能
import Image from 'next/image'

export default function AboutPage(props) {
  return (
    <>
      {/* ... */}
      <Image
        src={'/sample-image.jpg'}
        alt="サンプル画像"
        width={100}
        height={100}
      />
      <img
        src={'/sample-image.jpg'}
        alt="サンプル画像"
      />
    <>
  )
}

Next.jsはImageのwidthとheightの値に基づき、ページリクエスト時に対応するサイズの画像を変換してキャッシュします。

APIルート

/pages/apiディレクトリ内のJSファイルはページコンポーネントをエクスポートせず、Next.jsはこれらのファイルを/api/*のエンドポイントにマッピングします。Next.jsチームはNode.jsのhttpモジュールの上にexpressのようなWebサーバー開発機能を提供するラッパーを実装しています。

これらのファイル内ではサーバーサイドのロジックを記述でき、getStaticPropsメソッドと同様、これらのロジックはクライアント側には見えません。

これらのAPIルートの基本的な形式は以下の通りです:

export default function apiHandler(req, res) {
  if (req.method === 'POST') {
    // POSTリクエストの処理
  } else {
    // その他のHTTPメソッドのリクエストの処理
  }
}

1. 基本的な使用法

pages/index.jsでGETリクエストとPOSTリクエストを処理できます。pages/api/feedback.jsでも同様に処理を実装できます。

2. 動的APIルート

動的APIルートを使用することもできます。

Next.jsのデプロイ

1. ビルド

Next.jsアプリケーションをビルドするには2つの方法があります:

(1) next build

最初の方法は「標準ビルド」で、next buildコマンドを使用します。この方法でビルドすると、最適化されたフロントエンドプロジェクトとNode.jsサーバーサイドプログラムが生成されます。このサーバーサイドプログラムはAPIルート、SSR、ページの再検証などの機能を提供します。したがって、このアプリケーションをデプロイするにはサーバーにNode.js環境が必要です。

(2) next export

2番目のビルド方法は静的エクスポートで、next exportコマンドを使用します。この方法で生成されるコードは、純粋なフロントエンドの内容(HTML、CSS、JS、および静的リソース)のみを含みます。Node.jsサーバーサイドプログラムは含まれないため、デプロイ時にNode.js環境は不要です。もちろん、その代わりにAPIルート、SSRなどのNext.jsが提供する機能は使用できません。

2. 設定

プロジェクトのルートディレクトリにあるnext.config.jsファイルでNext.jsを設定できます。

/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
}

module.exports = nextConfig

このファイルのコードもサーバーサイドのコードであり、ビルドプロセスとビルドされたNode.jsサーバーサイドプログラムで使用されます。また、このファイルはWebpack、Babel、TypeScriptによって処理されないため、使用するNode.jsバージョンと互換性のある構文を使用してください。

暗号化

bcryptjsパッケージを使用して暗号化ロジックを実装します。以下のコマンドでインストールします:

npm i bcryptjs

bcryptjsパッケージでは、ハッシュ化(hash)と比較(compare)の2つの関数に注目します。両方のメソッドは非同期です。

import { hash, compare } from 'bcryptjs'

// ...

// ハッシュ関数で平文パスワードを暗号化
const hashedPassword = await hash(password, 12)

// ...

// compare関数で2つのパスワードが同じか比較し、ブール値を返す
const isValid = await compare(newPassword, hashedPassword)

認証

npm install next-auth

1. サーバーサイドでのJWTトークン生成

next-authパッケージは認証に必要なロジックを提供します。APIルート内で特殊なファイル/api/auth/[...nextauth].jsを作成し、ファイル内でnext-authパッケージをインポートして関連ロジックを実装します:

import NextAuth from "next-auth"
import CredentialsProvider from "next-auth/providers/credentials"

export const authOptions = {
  providers: [
    CredentialsProvider({
      name: 'Credentials',
      session: {
        strategy: "jwt",
      },
      async authorize(credentials, req) {
        // 独自の検証ロジックを実装
        const response = await fetch("/api/authenticate", {
          method: 'POST',
          body: JSON.stringify(credentials),
          headers: { "Content-Type": "application/json" }
        })
        const user = await response.json()

        // 問題がなければユーザー情報を返す
        if (response.ok && user) {
          return user
        }
        // ユーザー情報が取得できなければnullを返す
        return null
      }
    })
    // ...他のプロバイダー
  ],
}
export default NextAuth(authOptions)

2. フロントエンドコンポーネントでのsignIn関数の使用

import { signIn } from "next-auth/react"

export default () => <button onClick={() => 
    signIn('credentials', { redirect: false, username: 'username', password: 'password' })}>ログイン</button>

3. クライアント側でのuseSessionフックによる認証情報の取得

import { useSession } from "next-auth/react"
export default function UserPage(props) {
  const { data: session, status } = useSession()

  console.log('セッション情報: ', session)
  console.log('ステータス: ', status)

  return (
    <>
      {/* ... */}
    <>
  )
}

v4バージョンのnext-authでは、SessionProviderを使用しないと上記のフックは使用できません。_app.jsに以下のコードを追加します:

import { SessionProvider } from "next-auth/react"
import '../styles/globals.css'

export default function MyApp({ Component, pageProps: { session, ...pageProps }, }) {
  return <SessionProvider session={session}>
    <Component {...pageProps} />
  </SessionProvider>
}

4. signOut関数によるユーザーのログアウト

import { signOut } from "next-auth/react"

export default () => <button onClick={() => signOut()}>ログアウト</button>

ルートガード

クライアント側では、getSessionを使用して現在の認証情報を取得できます。権限が必要なページでは、sessionの値を判断してさらに操作を行います。

サーバーサイド側では、getStaticPropsメソッドでunstable_getServerSession関数を使用してsessionを取得できます。v4バージョンでは、getSessionはサーバーサイドでは使用できません。

import { unstable_getServerSession } from "next-auth/next"
import { authOptions } from "./api/auth/[...nextauth]"

export async function getServerSideProps(context) {
  return {
    props: {
      session: await unstable_getServerSession(
        context.req,
        context.res,
        authOptions
      ),
    },
  }
}

タグ: Next.js React SSR SSG 静的サイト生成

7月19日 02:43 投稿