20231224_Next.jsのApp RouterによるWeb開発について

2.7K Views

December 21, 23

スライド概要

profile-image

オタクなフロントエンドえんじにあです。

シェア

またはPlayer版

埋め込む »CMSなどでJSが使えない場合

ダウンロード

関連スライド

各ページのテキスト
1.

App Router による Web 開発について Next.js の App Router の特徴や機能、使い方を中心に紹介します。 とらのあなラボ 古賀 広隆 Copyright (C) 2023 Toranoana Inc. All Rights Reserved.

2.

自己紹介 所属 虎の穴ラボ株式会社 アーキテ クトチーム 主な担当 プロダクトのセキュリティ対 応、アーキテクト検討。 フロントエンド設計や実装な ど Copyright (C) 2023 Toranoana Inc. All Rights Reserved.

3.

App RouterによるWeb開発について はじめに Copyright (C) 2023 Toranoana Inc. All Rights Reserved.

4.

App Router によるWeb開発について App Router とは Next.js v13 から導入された、新しい仕組みです。 既存の Page Router が進化した新しい機能として、追加されました。 新しいアプリケーションを作る場合は、App Router を利用すること が推奨されています。 また、段階的に App Router へ切り替えることも可能です。 Copyright (C) 2023 Toranoana Inc. All Rights Reserved.

5.

トークの目的と概要など 今まで Page Router を使っていた人向けに、App Router の特徴や機 能、使い方を実践例などで紹介します。 内容は Page Router と変わった部分を中心に紹介します。 個人的には App Router を使ってみて、良さそうとは感じてます。 Copyright (C) 2023 Toranoana Inc. All Rights Reserved.

6.

App RouterによるWeb開発について あじぇんだ Copyright (C) 2023 Toranoana Inc. All Rights Reserved.

7.

App Router による Web 開発について 1. ルーティングの機能について 2. 4 つのキャッシュについて 3. 注意すべき点 4. 効果的に使うためのコツ 5. まとめ あじぇんだ

8.

App RouterによるWeb開発について 1. ルーティングの機能について ルーティングと基本的な機能(Page Router から変わった機能)をま とめました。 (解説は /app が起点です) Copyright (C) 2023 Toranoana Inc. All Rights Reserved.

9.

App RouterによるWeb開発について /app/hogehoge/page.tsx • App Router で /hogehoge にアクセスした時に表示されるページの置 き場 • Page Router では、/pages/hogehoge.tsx または /pages/hogehoge/index.tsx に相当する • 統一されて良い Copyright (C) 2023 Toranoana Inc. All Rights Reserved.

10.

App RouterによるWeb開発について /app/hogehoge/layout.tsx • /hogehoge/* にアクセスした時に利用されるレイアウトのコンポーネ ント • ヘッダーやフッター、サイドバーなどの共通部分を定義する • Page Router では決まりはない components フォルダーとかに普通のコンポーネントとして置くこと が多かった。 Copyright (C) 2023 Toranoana Inc. All Rights Reserved.

11.
[beta]
App RouterによるWeb開発について
/app/hogehoge/[param]/page.tsx
• /hogehoge/[param] にアクセスした時に表示されるページ
• [param] は、Page Router のときと同様に動的なパスパラメーターを
表します
export default function HogeHoge({ params }: { params: { param: string } }) {
return <div>hogehoge: {params.param}</div>;
}
generateStaticParams で SSG を利用することも可
Copyright (C) 2023 Toranoana Inc. All Rights Reserved.
12.

App RouterによるWeb開発について app/hogehoge/default.tsx • /hogehoge/* にアクセスした時にそのパスに page.tsx がない場合に 表示されるページ • 404 ページ、共通メニューなどを定義する • Page Router では明確な代用はなかった (一応、pages/[[...slug]].tsx とかで行ける) Copyright (C) 2023 Toranoana Inc. All Rights Reserved.

13.

App RouterによるWeb開発について ここからは、Page Router になかった機能になります。 Copyright (C) 2023 Toranoana Inc. All Rights Reserved.

14.

App RouterによるWeb開発について ルーティングに関係のないファイルが置ける App Router は関係のないファイル (.ts、.tsx など) を無視します。 そのルートのみに関係する子コンポーネントを置くと便利です。 (詳しくは後述、とくに React Server Component とかでしたくない 場面がある) Copyright (C) 2023 Toranoana Inc. All Rights Reserved.

15.

App RouterによるWeb開発について (RouteGroup) RouteGroup は、App Router でルートをグループ化するための機能 です。 • グループごとにレイアウトを指定できる • グループごとにデフォルトのページを変更できる Copyright (C) 2023 Toranoana Inc. All Rights Reserved.

16.

(A機能) layout.tsx hogehoge page.tsx (A機能)/layout.tsxが適 用される /hogehogeでアクセス可能 (B機能) layout.tsx fugafuga page.tsx (B機能)/layout.tsxが適 用される /fugafugaでアクセス可能

17.

App RouterによるWeb開発について @ParallelRoute パラレルルートは、App Router で複数のルートをひとつの画面に表 示するための機能です。 Copyright (C) 2023 Toranoana Inc. All Rights Reserved.

18.

. hoge @Aルート layout.tsx hogehoge page.tsx @Bルート layout.tsx hogehoge page.tsx /hoge/hogehogeでアクセスすると @Aと@Bが読み込まれる @Aと@Bの両方のルートを置く layout.tsx

19.
[beta]
App RouterによるWeb開発について
layout.tsx の書き方
export default function RootLayout({
children,
aRoute,
bRoute,
}: {
children: React.ReactNode; // デフォルト
aRoute: React.ReactNode; // Aルート
bRoute: React.ReactNode; // Bルート
}) {
return (
<div className={clsx("...")}>
<div className={clsx("...")}>{children}</div>
<div className={clsx("...")}>{aRoute}</div>
<div className={clsx("...")}>{bRoute}</div>
</div>
);
}
Copyright (C) 2023 Toranoana Inc. All Rights Reserved.
20.

App RouterによるWeb開発について Intercept Route (インターセプトルート) 別ルートの子や孫階層のページをそのルートのページとして表示する ための割り込み機能、 パラレルルートと組み合わせて使います。 “インターセプトルートの (.) は同一階層、(..) はひとつ前の階 層、(...) はルートの階層からの割り込みを表します。” Copyright (C) 2023 Toranoana Inc. All Rights Reserved.

21.

. hoge @Aルート (.)hogehoge page.tsx next-link、router.pushなどで /hoge/hogehogeへアクセスした場合に読み込まれる URLリンク、F5更新などで /hoge/hogehogeへアクセスした場合に読み込まれる hogehoge page.tsx @Aとdefaultの両方のルートを置く layout.tsx

22.

App RouterによるWeb開発について _filename.tsx or _directory アンダーバーをファイル名の先頭につけると、そのファイルはルーテ ィングから無視されます。 ルーティングに関係のないファイルを明示的に置くときに使う。 Copyright (C) 2023 Toranoana Inc. All Rights Reserved.

23.

App RouterによるWeb開発について 2. 4 つのキャッシュについて HTML や RSC (React Server Compoments) のペイロード、Fetch API のデータなどをキャッシュする仕組みについて、説明します。 Copyright (C) 2023 Toranoana Inc. All Rights Reserved.

24.

App RouterによるWeb開発について React Server Components について React Server Components は、React コンポーネントをサーバー上 でレンダリングするための仕組みです。 Copyright (C) 2023 Toranoana Inc. All Rights Reserved.

25.

App RouterによるWeb開発について いままでは、ページ全体を SSR するときにページ表示がデータ取得ま でブロックされていた。 これをクライアントで useEffect や useState、SWR、Suspense な どを使ってブロックを回避するのではなく React Server Components も使えるようになった。 気になる人は、Vercel のブログ記事を参照してください。 Copyright (C) 2023 Toranoana Inc. All Rights Reserved.

26.

App RouterによるWeb開発について React Server Component Payload について クライアントコンポーネントをレンダリングする場所のプレースホル ダーなどを含む一時データのこと 以下の情報なども含みます。 • クライアント向けの JavaScript ファイルへの参照 • サーバーコンポーネントからクライアントコンポーネントに渡された コンポーネント Copyright (C) 2023 Toranoana Inc. All Rights Reserved.

27.

App RouterによるWeb開発について RSC を利用したレンダリングの仕組み Copyright (C) 2023 Toranoana Inc. All Rights Reserved.

28.

クライアント(ブラウザ) ルート (/hello等) へ訪問 Router Cache (Next.js) RSC Payload取得 保管(未訪問) JavaScript内部メモリー RSC Payload 取得 (未訪問) 取得 (訪問済み) サーバー Next.jsのビルドまたはレンダリングフェーズ ルートをレンダリング RSC Payload生成 (React) Full Route Cache (Next.js) HTML生成 取得 保管 ファイルストレージ RSC Payload HTML 保管 取得

29.

App RouterによるWeb開発について 1. React で、サーバーコンポーネントをストリーミング用に最適化され た特別なデータ形式 (RSC Payload) を生成する 2. Next.js は、1 のペイロードとクライアントコンポーネントの JavaScript 命令を使用して、サーバー上で HTML を生成する 3. Next.js は、その HTML を使用して、静的なプレビューを表示します 4. 次にペイロードを使って、クライアントコンポーネントと JavaScript の指示で DOM を更新します Copyright (C) 2023 Toranoana Inc. All Rights Reserved.

30.

App RouterによるWeb開発について この過程において、ペイロードはサーバとクライアント(ブラウザ) にキャッシュされ、HTML はサーバ上にキャッシュされます。 クライアント側は、Route Cache (リロード時 (F5 や更新ボタン押 下)、「30 秒」または「5 分」) サーバ側は、Full Route Cache (ビルド時に実行、永続的。後述の Data Cache の再検証時に再検証) と Next.js は呼んでいます。 Copyright (C) 2023 Toranoana Inc. All Rights Reserved.

31.

App RouterによるWeb開発について Fatch API のキャッシュについて Copyright (C) 2023 Toranoana Inc. All Rights Reserved.

32.

サーバー Next.jsのビルドまたはレンダリングフェーズ ルートをレンダリング RSC Payload生成 (React) Request Memoization (React) データリクエスト 関数 保管 Node.js内部メモリー 関数の戻り値 取得 Data Cache (Next.js) 保管 Fetch Fetch以外 取得 JSON ファイルストレージ 取得 データベース または外部APIなど 取得

33.

App RouterによるWeb開発について React.js が RSC のコンポーネントツリー内で fetch API の GET デー タを再利用させる → Request Memoization → React コンポーネントツリーのレンダリングが開始して完了するま で再利用される → fetch API 以外も同様にキャッシュすることもできる (React cache function) Copyright (C) 2023 Toranoana Inc. All Rights Reserved.

34.

App RouterによるWeb開発について Next.js がサーバー上の fetch API のデータをキャッシュする (リク エストユーザー間のリクエストも含めて) → Data Cache → 永続的(時間ベース、または任意のタイミングで再検証が可能) Copyright (C) 2023 Toranoana Inc. All Rights Reserved.

35.

App RouterによるWeb開発について キャッシュが無効になる条件 ビルド時に特定の条件に当てはまる場合は、キャッシュが無効になり ます。基本的には意識する必要はありませんが、思わぬ落とし穴にな る可能性があります。 更新されない現象に陥った時は、最後の参考情報を確認してみてくだ さい。 Copyright (C) 2023 Toranoana Inc. All Rights Reserved.

36.

App RouterによるWeb開発について 3. 注意すべき点 RSC はブラウザ上でのみ動作するコードを含むことはできません。 • onClick などのイベント • useState、useEffect などの Hooks • window、document などのブラウザ API この場合はコンポーネントに "use client" を指定して、従来の Page Router と同じように動作させるようにします。 Copyright (C) 2023 Toranoana Inc. All Rights Reserved.

37.
[beta]
App RouterによるWeb開発について
Page Router の SSR
type Repo = {
name: string;
stargazers_count: number;
};
export const getServerSideProps: GetServerSideProps<{
repo: Repo;
}> = async () => {
const res = await db.query("select ~");
const repo = await res.json();
return { props: { repo } };
};
export default function Page({
repo,
}: InferGetServerSidePropsType<typeof getServerSideProps>) {
return repo.stargazers_count;
}
Copyright (C) 2023 Toranoana Inc. All Rights Reserved.
38.
[beta]
App RouterによるWeb開発について
App Router の SSR (極端な例)
async function getData() {
const res = await db.query("select ~");
if (!res.ok) {
throw new Error("Failed to fetch data");
}
return res.json();
}
export default async function Page() {
const data = await getData();
return <main data={data}></main>;
}
RSC に await で直接、サーバ処理を書ける。
Copyright (C) 2023 Toranoana Inc. All Rights Reserved.
39.

App RouterによるWeb開発について 4. 効果的に使うためのコツ Copyright (C) 2023 Toranoana Inc. All Rights Reserved.

40.

App RouterによるWeb開発について • なるべく部分的にクライアントコンポーネントを利用する (page.tsx を丸ごと"use client"すると従来と変わらない動きをす る。脳死で"use client"しない。) • その際、ルートのみに関係がある子コンポーネントは、そのルートの ディレクトリに置ける • 登録や検索など、ユーザ入力を API へ送信、API で受信する場合は、 zod-form-data/zod を活用する (Next.js 公式にも記載)、v14 以降 でベータが外れた Server Action でも良さそう。 Copyright (C) 2023 Toranoana Inc. All Rights Reserved.

41.
[beta]
App RouterによるWeb開発について
zod-form-data/zodで検証をして、Form データを API に送信する
// FormDataをzfdで検証する
const result = ReserveDetail.safeParse(formData);
if (result.success === false) {
console.error("validation error", result.error);
}
const { realName, tel, time } = result.data;
const res = await fetch(`/reserve/${time}/details/register`, {
body: formData,
method: "POST",
});
Copyright (C) 2023 Toranoana Inc. All Rights Reserved.
42.

App RouterによるWeb開発について 定義する schema /** * FormDataのスキーマー、zfdを使う。 **/ export const ReserveDetail = zfd.formData({ realName: zfd.text(), tel: zfd.text(z.string().refine(validator.isMobilePhone)), time: zfd.numeric(z.number().nonnegative()), }); これのお陰で、型安全に FormData が扱える。データ送信のための useState や useRef などを使わなくて良い。 Copyright (C) 2023 Toranoana Inc. All Rights Reserved.

43.
[beta]
App RouterによるWeb開発について
zod-form-data/zodで検証をして、Form データを API で受信する
export async function POST( request: Request ) {
const { realName, tel, time } = ReserveDetail.parse(await request.formData());
const session = await getServerSession(authOptions);
const userId = session?.user?.id;
// 必要あれば、別途バリデーションを入れる
await db.transaction(async (tx) => {
const r1 = db .insert(reserveDateTimes).values({
reserved_at: new Date(time),
userId,
})
.run();
フロントもバックも同じように検証(バリデーション)ができる。
Copyright (C) 2023 Toranoana Inc. All Rights Reserved.
44.

App RouterによるWeb開発について Server Action の例 Copyright (C) 2023 Toranoana Inc. All Rights Reserved.

45.
[beta]
ServerActionの例
export default function Page() {
async function create(formData: FormData) {
"use server";
// バリデーション
const params = ReserveDetail.safeParse(formData);
if (params.success === false) {
// return は、useFormState (useOptimistic) で受け取れる。
return { message: `${pathName}の入力に誤りがあります。` };
}
const { realName, tel, time } = params.data;
// 登録(省略。ORMを使ったり、外部APIを呼び出したり)
// 完了
revalidatePath("/fix/reserve");
redirect(`/fix/reserve/completed`);
}
return <form action={create}>...</form>;
}
Copyright (C) 2023 Toranoana Inc. All Rights Reserved.
46.

App RouterによるWeb開発について まとめ Copyright (C) 2023 Toranoana Inc. All Rights Reserved.

47.

App RouterによるWeb開発について まとめ App Router/Server Actions 等を使うと、下記のようなメリットがあ りそうです。 • キャッシュによる高速な Web アプリケーション開発 • モーダルや複数ルートの作り込みが減る • API の作り込みが減る Copyright (C) 2023 Toranoana Inc. All Rights Reserved.

48.

App RouterによるWeb開発について デメリットとしては、下記が考えられました。 • 場合によっては、トークンによる CSRF 対策が必要になる • 一見、従来の PHP、JSP/Servlet のイメージに近い(!?) Copyright (C) 2023 Toranoana Inc. All Rights Reserved.

49.

App RouterによるWeb開発について 最後に一言 App Router/Sever Action は、まだまだ特定のパターンで不具合があ ることがあります。 うまく動かない場合は、Next.js のリポジトリの Issue を確認する か、部分的に Page Router を使ったりなどの対応を検討してくださ い。 Copyright (C) 2023 Toranoana Inc. All Rights Reserved.

50.

App RouterによるWeb開発について ご清聴いただき、ありがとうございました! Copyright (C) 2023 Toranoana Inc. All Rights Reserved.

51.

App RouterによるWeb開発について 参考:App Router のキャッシュが無効になる 条件 Copyright (C) 2023 Toranoana Inc. All Rights Reserved.

52.

App RouterによるWeb開発について Fetch API 関連 • fetch API のオプションに cache: 'no-store' を追加した • fetch API のオプションに revalidate: 0 を追加した • POST リクエストの fetch API が含まれている • fetch API のリクエスト後に動的関数 ( cookies 、 headers ) を利用し ている • fetch API のリクエストに Authorization ヘッダーまたは Cookie ヘ ッダーを使用している Copyright (C) 2023 Toranoana Inc. All Rights Reserved.

53.

App RouterによるWeb開発について RSC 関連 • export const dynamic = "force-dynamic" を追加した • export const fetchCache = "force-no-store" | "default-no-store" な どを追加した • import { cookies } from 'next/headers' でインポートし、 cookies() を呼び出す。 • import { headers } from 'next/headers' でインポートし、 headers() を呼び出す。 • import { useSearchParams } from 'next/navigation' でインポートし、 useSearchParams() を呼び出す。(Suspense 内は OK) • page.tsx または page.jsx に Props を定義する Copyright (C) 2023 Toranoana Inc. All Rights Reserved.