Open21

【開発】Next.js App Routerでアプリを作る

ほこりぽんほこりぽん

フォルダ構成

前提

・app配下のlayoutにはグローバルなcontextだけ定義する→layoutのスタイルは設定しない。ただしスマートフォン対応のためbodyには min-height: 100dvh;を設定する。
・ページはuse clientにしない→metadataがexportできないから
・app配下には(default)フォルダを作成してそこをルートとする

コンポジション

app配下はpage毎、feature毎になるので、pageに属するcomponentやassetsはページのルートフォルダ配下に配置する

ほこりぽんほこりぽん

通信

以下のセキュリティブログを参考に組み立てる
https://nextjs.org/blog/security-nextjs-server-components-actions

CRUD手段

通信レイヤーを作成する
Next.jsだと2つある。

1. data layer

サーバーから呼ばれることが多い。読み取り系の通信を担当

2. Server Actions (SA)

書き込むようなものSAを使用する
SAはAPIのエンドポイントのようなものなのでエンドポイントさえ知っていれば誰でも叩けるので注意。必ず認証やバリデーションを行う。認証の場所は後述する。

ほこりぽんほこりぽん

デザイン

tailwindcssを使用する

レスポンシブデザイン

className="lg:xxx"のようにすることでlgサイズのスタイルを指定できる。

ダークモード

公式docにあるのでそちらを参照。
注意すべきは色の指定方法で、何でもかんでも色を付けるとダークモードが無視される。
例えば文字や背景の色を少し暗くするぐらいであれば、mutedやmuted-foregroundを使用する
テキストであればtext-muted-foregroundとすることで薄い文字にできる。

ほこりぽんほこりぽん

メタデータ

ルートレイアウトで以下を設定

export const metadata: Metadata = {
  title: {
    template: '%s | Test',
    default: 'Test',
  },
  description: "Shun's Blog",
}

export default function RootLayout({

page.tsxのmetadataのtitle部分が%sと対応する

export const metadata: Metadata = {
  title: 'hoge',
  description: "hogeのページです",
}

export default function Home() {

Homeへ遷移した場合、タブには「hoge | Test」と表示される

ほこりぽんほこりぽん

静的ページのビルド

next.config.mjsに以下を設定する

const nextConfig = {
    output: "export"
}
ほこりぽんほこりぽん

エラーハンドリング

https://www.wantedly.com/companies/wantedly/post_articles/492456

fetchのcatchパターン

fetch関数の結果がcatchに入るパターンについて、HTTPステータスコードに当てはまるかどうかを説明します。

  1. ネットワークエラー:
    ネットワーク接続が失敗した場合(例:サーバーがダウンしている、インターネット接続がないなど)。
    HTTPステータスコード: 該当なし。ネットワークエラーはHTTPリクエストがサーバーに到達しないため、ステータスコードは存在しません。
  2. CORSエラー:
    クロスオリジンリソース共有(CORS)ポリシーに違反している場合。
    HTTPステータスコード: 該当なし。CORSエラーはブラウザによってブロックされるため、ステータスコードは返されません。
  3. リクエストのタイムアウト:
    リクエストがタイムアウトした場合(ただし、fetch自体にはタイムアウト機能がないため、AbortControllerを使用して手動でタイムアウトを設定する必要があります)。
    HTTPステータスコード: 該当なし。タイムアウトはクライアント側の設定であり、サーバーからのレスポンスがないためステータスコードは存在しません。
    DNS解決エラー:
    ドメイン名が解決できない場合。
    HTTPステータスコード: 該当なし。DNS解決エラーはHTTPリクエストがサーバーに到達しないため、ステータスコードは存在しません。
    これらのエラーはすべてfetch関数のcatchブロックに入りますが、HTTPステータスコードは返されません。

response.ok

fetch関数のレスポンスオブジェクトにはokプロパティがあり、これはレスポンスが正常(ステータスコードが200-299の範囲)であるかどうかを示します。response.okがtrueの場合、レスポンスは正常です。falseの場合、エラーが発生したことを示します。

fetch('https://example.com/data')
  .then(response => {
    if (response.ok) {
      return response.json();
    } else {
      throw new Error('Network response was not ok');
    }
  })
  .then(data => {
    console.log(data);
  })
  .catch(error => {
    console.error('Fetch error:', error);
  });

axiosの場合

axiosを使用した場合、HTTPステータスコードが400以上(例えば、500エラー)の場合でもcatchブロックに入ります。これはaxiosのデフォルトの動作です。

ほこりぽんほこりぽん

フォーム検証の仕組み

3回の検証が行われる

  1. フロントでreact-hook-formを使用して検証
  2. サーバーサイドでzodの検証
  3. supabaseならDB側でRLSによる検証

フロントの検証は改ざん可能なため、zodの検証は必須。検証が漏れてもDBでも拾えるようにしておくことでより堅牢なアプリとなる。

ほこりぽんほこりぽん

フォルダ構成

https://x.com/d151005/status/1825120204818690061
◆ コロケーション
◆ 関連処理は1ファイルにまとめて書く
◆ 共通処理は /components 等にフラット設置
◆ ファイル名はケバブ
◆ 状態管理ライブラリやテストが見当たらない


https://github.com/vercel/ai-chatbot/tree/main

共通パーツは /components にフラットに置かれがち。これは賛否あるかもだけど個人的には「これどのドメイン(機能)にしよっか..)と悩んだり仕様変更時のお引越ししたりが面倒なのでフラット設置はあり。

ファイル名ケバブについては Next.js がそもそも not-found.tsx などケバブベースなのと、大文字小文字のリネームで git がバグる問題もあり個人的にもケバブの方が Next.js に馴染む気がする。

Next.js 公式 Learn でもケバブでガイドされている。
https://nextjs.org/learn/dashboard-app/navigating-between-pages

状態管理ツール不在については
◆ App Router のキャッシュによりDBソースをグローバルステート感覚で使える
◆ UI周りの状態は shadcn/ui 等に委ねる
◆ RSC からは swr 等でステート管理
◆ 余計にコンポーネント分割せずベタ書きすることで props リレーを軽減

という背景のもと不要説。

テストについてはユニットテストが async コンポーネント非対応なのでサンプルリポで混ぜようがない状況。公式Doc曰く一旦 e2e で頑張れとのこと。

にしても shadcn や Vercel メンバーのProd リポでもテストの気配がないのは一体...?🤔
https://nextjs.org/docs/app/building-your-application/testing#async-server-components

ちなみに test や story をパッケージする場合、Bパターンが一般的だった。元 Atlassian エンジニアも index.tsx 量産するとファイル名で現場直行がむずくなるから命名をおすすめしてる。

ほこりぽんほこりぽん

favicon

safariはsvgのfaviconに対応していないので注意
https://caniuse.com/?search=favicon

以下で生成できる
https://favicon.io/
apple-taouch-icon.png:iPhoneでお気に入り入れた時の表示とか
android-chrome:アンドロイド関係

apple-taouch-iconはapple-icon.pngにして、他はicon1.pngにしてappフォルダ配下に配置する

OG image

OG image確認(Next.jsの開発ツールでも確認できる)
https://www.opengraph.xyz/

PORT 3000のアプリをwebに公開するコマンド

npx ngrok http 3000
ほこりぽんほこりぽん

キャッシュ、動的、静的

ƒ:動的レンダリング
○:静的レンダリング

静的ページ

全てのリクエストに対して同じレンダリング結果を返却する

静的fetch

cacheオプションがないので、buildしたとき以外はfetchが再度実行されることはない。

fetch("https://")

動的ページ

リクエストの内容に応じて異なるレンダリング結果を返却する
3つの要因がある

1. 動的fetch

no-storeにすると毎回fetchされるので静的ページではなくなる

fetch("https://", {cache: "no-store"})

2. 動的関数

cookies()

// クエリパラメータのこと
searchParams

3. Dynamic Segment

[xxx]のページは動的レンダリングとなる。

ほこりぽんほこりぽん

URL操作

new URLSearchParams

  const searchParams = new URLSearchParams({
    page: '1',
    per_page: '10',
  })
  console.log(`api/v/${searchParams}`) // => "api/v/page=1&per_page=10"