Open2

学び:Next.js

kouichi.hoshikouichi.hoshi

useRouter()

App Routerを使用している場合、next/routerではなくnext/navigationを使う。
useRouter()はカスタムコンポーネントなので、'use client'を付ける。

'use client'

import { useRouter } from 'next/navigation'

export default function BackButton() {
  const router = useRouter()

  return (
    <button onClick={() => router.back()}>
      もどる
    </button>
  )
}
kouichi.hoshikouichi.hoshi

Middleware

概要

/quiz/tag/**にアクセスされたら、/quiz/tag/${tag}/p/1へリダイレクトするコードを実装したときのメモ。

実装

tag階層以下に動的にページ分割ページを実装した。tag階層のトップディレクトリは特に必要ないのでアクセスされたらページ分割の最初のページに移動する挙動を仕様として決定。それを実現する方法としてNext.jsのMiddlewareにて以下コードを実装した。

import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";

export function middleware(request: NextRequest) {
  const { pathname } = request.nextUrl; // リクエストされたURLを取得
  const segments = pathname.split('/'); // パスをスラッシュで分割
  const tag = segments[segments.length - 1]; // 最後のセグメントをタグとして取得

  // パスが/quiz/tag/{tag}形式で、ページ番号が指定されていない場合にリダイレクト
  if (segments.length === 4 && segments[2] === 'tag' && tag && tag !== 'p') {
    // /quiz/tag/{tag}形式のURLにページ番号を追加してリダイレクトする
    return NextResponse.redirect(new URL(`/quiz/tag/${tag}/p/1`, request.url));
  }

  return NextResponse.next(); // それ以外の場合は通常の処理を続行
}

export const config = {
  // タグページのURLをマッチングする
  matcher: "/quiz/tag/:tag*",
};

気になること

  • サーバーにどの程度負荷がかかるのか?静的ページとしてビルドするなら負荷が低減するのか?
  • Vercelなどキャッシュが効く環境であれば負荷は軽いのか?
AIに解説してもらった。

Next.js Middlewareコード解析

以下は、提供いただいたNext.jsのミドルウェアコードの行ごとの解析と詳細な解説です。コードの各ステップで何を実行しているのか解説します。

import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
  1. NextResponseNextRequestnext/serverからインポートしています。
    • NextResponse: ミドルウェアやAPIルートでレスポンスを生成するためのクラスです。
    • NextRequest: リクエストの情報を格納するクラスで、リクエストURLやヘッダー、クエリパラメータなどにアクセスするために使用します。
export function middleware(request: NextRequest) {
  1. middleware関数をエクスポートしています。
    • この関数はミドルウェアとして動作し、NextRequest型のリクエストオブジェクトを受け取ります。
    • middleware関数はURLのチェックやリダイレクトなどの処理を行い、リクエストが続行するかリダイレクトされるかを決定します。
  const { pathname } = request.nextUrl; // リクエストされたURLを取得
  1. リクエストされたURLのパス部分(pathname)を取得します。
    • request.nextUrlはURLオブジェクトであり、その中のpathnameを使用してリクエストされたパスを取得しています。
    • 例えば、URLが/quiz/tag/javascriptの場合、pathname/quiz/tag/javascriptとなります。
  const segments = pathname.split('/'); // パスをスラッシュで分割
  1. pathname/で分割し、配列segmentsに格納します。
    • これにより、パスをセグメントごとに分割し、各セグメントに簡単にアクセスできるようにしています。
    • 例えば、pathname/quiz/tag/javascriptの場合、segments["", "quiz", "tag", "javascript"]になります。
  const tag = segments[segments.length - 1]; // 最後のセグメントをタグとして取得
  1. segments配列の最後の要素をtagとして取得します。
    • segments.length - 1を使って、配列の最後の要素を取得します。
    • これは/quiz/tag/javascriptの場合、tag"javascript"となり、タグとして使用されます。
  if (segments.length === 4 && segments[2] === 'tag' && tag && tag !== 'p') {
  1. リダイレクト条件を指定するif文です。
    • segments.length === 4: セグメントが4つであることを確認します(例: /quiz/tag/javascript)。
    • segments[2] === 'tag': 第3のセグメントが"tag"であることを確認します。
    • tag && tag !== 'p': tagが存在し、"p"でないことを確認します。
    • これらすべての条件が満たされた場合に、リダイレクト処理が行われます。
    return NextResponse.redirect(new URL(/quiz/tag/${tag}/p/1, request.url));
  1. リダイレクトを実行し、ページ番号を追加します。
    • /quiz/tag/${tag}/p/1という形式のURLを生成し、ページ番号1を付加したURLへリダイレクトします。
    • new URL()はリダイレクト先のURLを生成するために使われ、request.urlでベースURLを指定しています。
  return NextResponse.next(); // それ以外の場合は通常の処理を続行
  1. それ以外のリクエストについては通常の処理を続行します。
    • NextResponse.next()は処理を次に進めるメソッドで、リクエストがリダイレクトされずにそのまま次のミドルウェアやルートハンドラに渡されます。
export const config = {
  // タグページのURLをマッチングする
  matcher: "/quiz/tag/:tag*",
};
  1. ミドルウェアの設定オブジェクトをエクスポートしています。
    • matcherプロパティにより、特定のパスに対してのみミドルウェアが適用されるように指定します。
    • "/quiz/tag/:tag*"で指定されているため、/quiz/tag/に続くパスに対してこのミドルウェアが適用されます。

このコードは、特定のURLパターンにマッチした場合にリダイレクトを行うためのミドルウェアです。リクエストされたURLのパスに基づいて、必要に応じてリダイレクト先にページ番号を追加する動作を実現しています。