よくあるアンチパターン
よくあるアンチパターン
Section titled “よくあるアンチパターン”Next.jsでよくあるアンチパターンと、実際に事故った構造を詳しく解説します。
A. サーバーサイドでのメモリリーク
Section titled “A. サーバーサイドでのメモリリーク”実際に事故った構造
Section titled “実際に事故った構造”// ❌ アンチパターン: サーバーサイドでのメモリリークconst cache = new Map<string, any>();
export async function getServerSideProps() { // 問題: キャッシュが削除されず、メモリリークが発生 const key = 'data'; if (!cache.has(key)) { const data = await fetch('https://api.example.com/data').then(r => r.json()); cache.set(key, data); }
return { props: { data: cache.get(key), }, };}なぜ事故るか:
- キャッシュの削除: キャッシュが削除されず、メモリリークが発生
- メモリ使用量の増加: キャッシュが増加し続け、メモリ使用量が増加
- サーバーのクラッシュ: メモリ使用量が増加し、サーバーがクラッシュ
設計レビューでの指摘文例:
【指摘】サーバーサイドでメモリリークが発生しています。【問題】キャッシュが削除されず、メモリリークが発生しています。【影響】メモリ使用量の増加、サーバーのクラッシュ【推奨】TTLを設定し、期限切れのキャッシュを削除するB. クライアントサイドでのメモリリーク
Section titled “B. クライアントサイドでのメモリリーク”実際に事故った構造
Section titled “実際に事故った構造”// ❌ アンチパターン: クライアントサイドでのメモリリーク'use client';
export default function Page() { const [data, setData] = useState<any[]>([]);
useEffect(() => { // 問題: イベントリスナーが削除されない const handleScroll = () => { // 処理... };
window.addEventListener('scroll', handleScroll); // 問題: クリーンアップ関数がない }, []);
return <div>Page</div>;}なぜ事故るか:
- イベントリスナーのリーク: イベントリスナーが削除されず、メモリリークが発生
- 参照の保持: クロージャーで参照が保持され、ガベージコレクションが動作しない
- ブラウザのクラッシュ: メモリ使用量が増加し、ブラウザがクラッシュ
C. 大量のデータの取得
Section titled “C. 大量のデータの取得”実際に事故った構造
Section titled “実際に事故った構造”// ❌ アンチパターン: 大量のデータの取得export async function getServerSideProps() { // 問題: 大量のデータを一度に取得 const res = await fetch('https://api.example.com/posts'); const posts = await res.json(); // 10,000件のデータ
return { props: { posts, // 10,000件のデータをpropsに渡す }, };}
export default function PostsPage({ posts }: { posts: Post[] }) { return ( <div> {posts.map(post => ( <div key={post.id}>{post.title}</div> ))} </div> );}なぜ事故るか:
- メモリ使用量の増加: 大量のデータをメモリに保持し、メモリ使用量が増加
- DOMノード数の増加: 大量のDOMノードが作成され、レンダリングパフォーマンスが低下
- ネットワーク負荷の増加: 大量のデータを一度に取得し、ネットワーク負荷が増加
設計レビューでの指摘文例:
【指摘】大量のデータを一度に取得しています。【問題】10,000件のデータを一度に取得し、メモリ使用量が増加しています。【影響】メモリ使用量の増加、レンダリングパフォーマンスの低下【推奨】ページネーションを使用してデータを分割するD. バンドルサイズの増加
Section titled “D. バンドルサイズの増加”実際に事故った構造
Section titled “実際に事故った構造”// ❌ アンチパターン: バンドルサイズの増加import HeavyLibrary from 'heavy-library';import AnotherHeavyLibrary from 'another-heavy-library';
export default function Page() { // 問題: すべてのライブラリがバンドルに含まれる const result = HeavyLibrary.process(); const anotherResult = AnotherHeavyLibrary.process();
return <div>Page</div>;}なぜ事故るか:
- バンドルサイズの増加: すべてのライブラリがバンドルに含まれ、バンドルサイズが増加
- 初回ロード時間の増加: バンドルサイズが増加し、初回ロード時間が増加
- メモリ使用量の増加: すべてのライブラリがメモリにロードされ、メモリ使用量が増加
設計レビューでの指摘文例:
【指摘】バンドルサイズが過剰です。【問題】すべてのライブラリがバンドルに含まれています。【影響】初回ロード時間の増加、メモリ使用量の増加【推奨】動的インポートを使用してコード分割するよくあるアンチパターンのポイント:
- A. サーバーサイドでのメモリリーク: キャッシュが削除されず、メモリリークが発生 → サーバーのクラッシュ
- B. クライアントサイドでのメモリリーク: イベントリスナーが削除されない → ブラウザのクラッシュ
- C. 大量のデータの取得: 大量のデータを一度に取得 → メモリ使用量の増加、レンダリングパフォーマンスの低下
- D. バンドルサイズの増加: すべてのライブラリがバンドルに含まれる → 初回ロード時間の増加
これらのアンチパターンを避けることで、安全で信頼性の高いNext.jsアプリケーションを構築できます。
重要な原則: 「正常に動く」よりも「異常時に安全に壊れる」ことを優先する。