【2024年1月】Next.js での新規アプリの構成 & Next.js ディレクトリ構成(features)
選定の方針
- ログインしての利用がメインで、ユーザーがあまり多くないサービスを想定しています。
- 開発効率の重視して、出来るだけWebアプリに集中できる構成を目指しています。
- コスト理由で中断しないように、個人でも支払える費用感を意識しています。
Next.js ライブラリ構成
メインで使っているライブラリです。Next.js + Vercelの開発体験が良すぎるので、できる限り活用して開発することを意識して作っています。
- フレームワーク
- メインで使うライブラリ
Style/CSS に関して
Vercelがリリースしたv0をいいなと思って、v0の出力で使われているTailwind CSS + shadcn/uiを使うようにしています。(v0活用は検証中です)
よく使うインフラ系サービス
- Vercel: Gitにpushするだけでプレビュー・本番がデプロイできて便利です。
- Supabase: PostgreSQLを中心に、認証や画像ホスティングにも対応。
- Cloudflare: ドメイン関連のサービスを中心に利用しています。
- Sentry: 無料枠があり、予期せぬエラーの検知に便利。
アクセスが増えた場合の想定
アクセスが増えてきたらCloudflareをうまく活用していけないかを考えています。
Vercelのコストが増えてきたら
Vercelのコストが増加したり、レイテンシが問題になった場合は、Cloudflare Workersをプロキシとして利用する方法を試してみたいです。
Catnoseさんの記事にもあるように、静的ファイルをスルーしつつコストを抑えられそうです。
まずは検索流入を獲得するための静的ページに、Cloudflare WorkersとHonoを組み合わせたプロキシ利用を試してみたいと思っています。
画像周りのアスセスが増えた場合
画像関連のアクセスが増加した場合は、Cloudflare R2とWorkersを利用することで、コストを抑えつつスケールできると考えています。
DB周りでボトルネックが発生した場合
DB周りでボトルネックが発生した場合は、Cloudflare WorkersとHonoを組み合わせて、Read系や重い処理を部分的にキャッシュすることを検討しています。
また、スケールして開発メンバーが増えてきた場合は、 Cloudflare Worker + GraphQLのアプローチで GraphQL のレイヤーで最適化を図っていくオプションも考えています。
ディレクトリ構成
Next.jsを使用してアプリを構築する際のディレクトリ構成について紹介します。この構成はいくつかの記事や過去の実装を参考を元に作成しています。気になる点があれば、ぜひコメントでお知らせください🙇
├─ app/
├─ components/
│ ├─ elements/
│ │ └─ Button
│ │ └─ Button.tsx
│ └─ layouts/
│ └─ Header
│ └─ Header.tsx
├─ features/
│ └─ /post
│ ├─ api/
│ │ └─ getPost.ts
│ ├─ styles/
│ ├─ components/
│ ├─ Post.tsx
│ └─ Posts.tsx
│ ├─ hooks/
│ └─ usePost.ts
│ └─ types/
│ └─ index.ts
├─ hooks/
├─ styles/
├─ types/
├─ libs/
└─ utils/
/app
- Next.js の App Router を使う場合に利用します。
- ( Page Router を使う場合は
/pages
)
/components
- アプリケーション全体で共通して使用されるコンポーネントを格納します。
- サブディレクトリには以下のようなものがあります。
- elements/ 例:Button
- layouts/ 例:Header
/features
- 特定の機能やドメイン専用のAPIアクセス方法、コンポーネントなどを含みます。
- 関心のある特定の領域にフォーカスすることで、影響範囲を限定し、管理が容易になります。
/hooks
- 共通的に使われる複数のリポジトリをまたぐ実装、ロジックなどを格納します。
/styles
- スタイリングに関するファイルを格納します。
/types
- 型定義を中心に格納します。
/libs
- ライブラリ関連の設定などを格納します。
/utils
- アプリ全体で共通して使用するユーティリティ関数を中心に格納
App Routerでfeaturesディレクトリいるのか?
(1/13追記)
はてブのコメント本当にありがとうございます。
featuresディレクトリがあった方が良いか、なくても良いのかについての意見を書かせてください。
- 大前提として必要ないと思うディレクトリは作らないほうが良い
- 必要性の感じないディレクトリは最初は作らずに、必要になったら追加で良い
- 個人で運営中のサービスではfeaturesは追加しました。今の所あった方が良いとの感想です
- プロジェクトの複雑性、Next.jsへのチームの理解度等々を踏まえて判断すると良い
- 大切なのは「適切・継続的なリファクタリング」
- Next.js のアップデートや、より良いプラクティスの共有でベスプラは変わっていく
- 生産性を上げるために必要なリファクタの時間をチームで確保していくことが重要
App Router についてはこの辺りの記事がとてもわかりやすかったです!ありがとうございます🙌
(7/21追記)
zennの記事へのコメントで Route Handlers について教えていただきました。
Route Handlersは、Next.jsのApp Routerで導入された機能で、APIエンドポイントを簡単に作成するための方法です。主な特徴は:
-
app
ディレクトリ内のroute.js
(または.ts
)ファイルで定義します。 - HTTP対応:GET、POST、PUT、DELETE等の標準的なHTTPメソッドをサポートしています。
- 柔軟性:Web APIを使用してカスタムリクエスト処理とレスポンス生成が可能です。
- NextRequestやNextResponseなどの拡張APIも提供されており、リクエストボディの読み取り、ヘッダーの設定、ステータスコードの制御など、HTTPリクエストとレスポンスの詳細な操作が可能
こちらが使えそうな時は導入を検討しても良いかもです。ありがとうございます!
ESlint
ESLint も詳しいわけでなく、少しずつ継ぎ足しながら、設定してきました。
もしより良い設定があればぜひ教えてください🙏
module.exports = {
// ...
extends: [
"eslint:recommended",
"next",
"next/core-web-vitals",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
"plugin:prettier/recommended",
"plugin:redos/recommended",
],
plugins: ["simple-import-sort", "import"],
rules: {
"@next/next/no-img-element": "error",
"@next/next/no-page-custom-font": "error",
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/ban-ts-ignore": "off",
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/no-floating-promises": "error",
"@typescript-eslint/no-explicit-any": "off",
curly: ["error"],
eqeqeq: ["error", "always", { null: "ignore" }],
"import/first": "error",
"import/newline-after-import": "error",
"import/no-duplicates": "error",
"import/no-extraneous-dependencies": ["error"],
"import/no-unresolved": "error",
"no-console": "error",
"no-debugger": "error",
"react-hooks/exhaustive-deps": "warn",
"react-hooks/rules-of-hooks": "error",
"react/jsx-key": [
"error",
{
checkFragmentShorthand: true,
checkKeyMustBeforeSpread: true,
warnOnDuplicates: true,
},
],
"react/jsx-no-bind": [
"error",
{
allowArrowFunctions: true,
allowBind: false,
allowFunctions: false,
ignoreDOMComponents: false,
ignoreRefs: false,
},
],
"react/no-unknown-property": ["error", { ignore: ["custom-element"] }],
"react/prop-types": "off",
"react/react-in-jsx-scope": "off",
"redos/no-vulnerable": "error",
"simple-import-sort/exports": "error",
"simple-import-sort/imports": "error",
"sort-keys": [
"error",
"asc",
{ caseSensitive: true, minKeys: 2, natural: false },
],
},
// ...
};
おすすめサービス
新規サービスはできるかぎりサービス開発に向き合った方が良いと思うので、外部サービスで使えるものは積極的に使っていきたいです。
リアルタイム性が高くなければ、料金やレイテンシを考えて、リクエスト時にAPI呼び出すよりはbuild時に呼び出して使うような形だと、無料枠で使いやすいと思います。
Airtable
テーブル設計ができてデータをAPI経由で取得できます。
エンジニアなどが使うマスタデータなどはこちらがおすすめです。
Newt (Headless CMS)
Headless CMS で無料枠が充実しています。
記事コンテンツなどの非エンジニアのライターさんなどに記事を書いてもらう場合は、こちらがおすすめです。
CodeRabbit
AIコードレビューツールです。GitHubのプルリクエストに対して、レビューコメントを書いてくれます。
満足度的には40%くらいですが、たまにケアレスミスや実装ミスを指摘してくれます。特に一人で開発している場合は導入してみるのも良いかもです。
Next.js周りの設定
statically-typed-links
Linkコンポーネントのhrefを型チェックするExperiemntalの設定です。
参考にさせていただいた記事
Discussion
初めまして、記事を執筆いただいてありがとうございます!大変参考になります!
大変恐縮ですが質問をお願いいたします。
api呼び出しの定義をfeaturesディレクトリで定義する構成ですが、Route Handlersは利用しない想定でしょうか?
公式リファレンスでは、Route Handlersの利用はappディレクトリ配下でのみ有効であるとの記載があるので質問させていただきました。 また、Route Handlersを利用しない場合は、理由を含めて何を利用するのか教えていただけると幸いです。
ご質問ありがとうございます。
まず、返信が遅れてしまって本当にすみませんでした💦
恥ずかしながら、 Route Handlers を今回初めて知りました!
tRPC との相性も悪くなさそうなので使う価値は十分にあると思います。
今後使えそうな場合は試してみます。深謝です。
( 内容を修正して Route hander のことを残しておくようにします )
一点だけあるとすると、Cacheの書き込み・読み込みは一定の基準を超えると Vercel では有料になると思っています。僕は一時期ここでかなり苦しめられた (月$20 => $355 に値上げ予告があった) 経験があるので、サービスの収益性とコスト面を含めて検討してみると良いかもです。