🚀 Next.jsアプリが90%高速化!Applications Managerで発見した5つの隠れたボトルネックと解決法
こんにちは😊
株式会社プロドウガの@YushiYamamotoです!
らくらくサイトの開発・運営を担当しながら、AI・業務自動化専門のフルスタック・フリーランスエンジニアとしても活動しています❗️
「Next.jsアプリが重い...でも原因がわからない😰」
そんな悩みを抱えていた時に出会ったApplications Managerで、驚きの改善結果が得られました!今回は実際にManageEngine Applications Managerを使ってNext.jsアプリケーションのパフォーマンスを最適化した体験を共有します。最終的には初回ページ読み込み時間を90%短縮し、AIレスポンス時間を81%改善できました。
😰 らくらくサイトで発生していた深刻な問題
私が運営するらくらくサイトでは、ユーザー数増加に伴い、パフォーマンス問題が顕在化してきました。具体的には:
問題の症状:
- 初回ページ読み込み時間:2.3秒
- AI機能の応答時間:4.2秒
- データベースエラー率:3.4%
サイトを構成する技術スタックは以下の通りです:
- フロントエンド:Next.js 14(App Router)
- データベース:Supabase(PostgreSQL)
- AI機能:OpenAI API
- インフラ:Vercel + AWS
特に新しいフィーチャーをリリースするたびにパフォーマンスが低下していき、ボトルネックの特定が困難になっていました。従来のブラウザの開発ツールだけでは全体像が把握できず、特にサーバーサイドやAPI呼び出しの問題を特定するのが難しかったのです。
🔍 Applications Manager無料トライアルを試してみた
そこで、ManageEngineのApplications Managerの無料トライアルを試すことにしました。
APM(アプリケーションパフォーマンスモニタリング)ツールを以前から検討していましたが、「導入が難しいのでは?」「設定が複雑では?」という懸念がありました。
導入の簡単さに驚愕
結論から言うと、わずか15分でセットアップが完了しました!以下の手順で導入しました:
- Docker Composeでのセットアップ
# docker-compose.yml
version: '3'
services:
appmanager:
image: manageengine/applications-manager:latest
ports:
- "9090:9090"
volumes:
- appmanager_data:/opt/ManageEngine/AppManager/working
environment:
- TZ=Asia/Tokyo
volumes:
appmanager_data:
この設定で、Applications Managerのコンテナが起動し、ポート9090でアクセスできるようになりました。初回起動時にウィザードが表示され、基本設定を完了すると、管理画面にアクセスできます。
- Next.jsアプリにエージェント追加
Applications Managerでは、.NET用のエージェントが提供されていますが、Node.js環境にも対応しています。Next.jsアプリに監視エージェントを追加するために、以下の設定を行いました:
// instrumentation.ts (Next.js 14で追加されたファイル)
export async function register() {
if (process.env.NEXT_RUNTIME === "nodejs") {
const monitoring = await import('./monitoring');
monitoring.setupApplicationsManager();
}
}
// monitoring.js
export function setupApplicationsManager() {
// Applications Managerエージェントの設定
// 実際には提供されたSDKを使用
console.log("Applications Manager monitoring started");
}
この設定により、サーバーサイドの処理を含めたアプリケーション全体の監視が可能になりました。
- リアルタイムダッシュボードで即座に監視開始
設定完了後、リアルタイムダッシュボードにアプリケーションの情報が表示され始めました。Applications Managerは、150以上の異なるテクノロジーをサポートしており、オンプレミスとクラウドの両方に対応しています。特に印象的だったのは、リアルタイム分析とAI駆動の分析機能です。
🔎 Applications Managerで発見された5つの隠れたボトルネック
Applications Managerを使用してわずか数日間の監視で、いくつかの重大なパフォーマンス問題が浮き彫りになりました。以下の5つの主要なボトルネックを特定しました:
1. データベースN+1クエリ問題
SupabaseのPostgreSQLに対して、大量のN+1クエリが発生していることが判明しました。特にユーザー情報と投稿データを取得する際に、以下のようなコードが問題でした:
// Before: N+1クエリが発生するコード
async function getUsersWithPosts() {
const users = await supabase.from('users').select('*');
// 各ユーザーに対して個別のクエリを実行(N+1問題)
for (const user of users.data) {
user.posts = await supabase.from('posts').select('*').eq('user_id', user.id);
}
return users.data;
}
このコードでは、ユーザー取得のクエリ1回 + ユーザー数分の投稿取得クエリ(N回)が実行され、データベース負荷が高まっていました。
2. OpenAI API呼び出しの非効率性
AI機能では、OpenAI APIの呼び出しが逐次処理されており、レスポンス時間が長くなっていました。以下のコードが問題でした:
// Before: 逐次処理による遅延
async function processUserQueries(queries) {
const results = [];
for (const query of queries) {
// 一つずつAPIを呼び出し(待ち時間が積み重なる)
const response = await openai.createCompletion({
model: "text-davinci-003",
prompt: query,
max_tokens: 100
});
results.push(response.data.choices[^0].text);
}
return results;
}
各APIリクエストが完了するのを待ってから次のリクエストを開始していたため、レスポンス時間が長くなっていました。
3. 画像最適化の不足
Next.jsのImage
コンポーネントを使用していないページがあり、最適化されていない大きな画像ファイルが読み込まれていました。特にモバイルユーザーのエクスペリエンスに悪影響を与えていました。
// Before: 最適化されていない画像
<img src="/large-hero-image.jpg" alt="Hero" width="1200" height="600" />
このようなコードでは、画像のサイズ最適化、遅延読み込み、WebPなどの最新フォーマットへの自動変換といったNext.jsの組み込み機能を活用できていませんでした。
4. サーバーサイドレンダリング(SSR)と静的生成(SSG)の使い分け不足
ページによって適切なレンダリング戦略が選択されておらず、不必要にSSRを使用しているページがありました[^16]。Applications Managerの分析により、SSRページのTime to First Byte (TTFB)が遅いことが明らかになりました。
// Before: すべてのページでgetServerSidePropsを使用
export async function getServerSideProps() {
// 静的なデータなのにサーバーサイドで毎回取得
const data = await fetchStaticData();
return { props: { data } };
}
このコードでは、静的なコンテンツに対してもサーバーサイドレンダリングを使用していたため、不必要なサーバー負荷とレスポンス遅延が発生していました。
5. メモリリークの発見
Applications Managerのメモリ使用量監視により、特定のコンポーネントでメモリリークが発生していることが判明しました。以下のようなコードが問題でした:
// Before: メモリリークを引き起こすコード
function DataDisplayComponent() {
// グローバルな配列に無限にデータを追加
globalDataStore.push(fetchNewData());
return <div>{/* データ表示 */}</div>;
}
このコンポーネントが再レンダリングされるたびに、グローバル変数にデータが蓄積され続け、メモリ使用量が増加していました。
🛠️ 発見したボトルネックの解決法
Applications Managerで特定したボトルネックに対して、以下の改善策を実装しました。
1. データベースクエリの最適化
N+1クエリ問題に対しては、Supabaseの結合機能を活用して一度のクエリで必要なデータを取得するように変更しました:
// After: 最適化されたクエリ
async function getUsersWithPosts() {
// 一度のクエリで関連データを取得
const { data, error } = await supabase
.from('users')
.select(`
*,
posts:posts(*)
`);
return data;
}
この改善により、クエリ時間が800msから45msに短縮され、94%の改善を達成しました。
2. OpenAI API呼び出しの並列化
OpenAI APIの呼び出しを並列処理に変更し、Promise.allを使用して同時実行できるようにしました:
// After: 並列処理による高速化
async function processUserQueries(queries) {
// Promise.allで複数のAPIリクエストを並列実行
const promises = queries.map(query =>
openai.createCompletion({
model: "text-davinci-003",
prompt: query,
max_tokens: 100
})
);
const responses = await Promise.all(promises);
return responses.map(response => response.data.choices[^0].text);
}
この改善により、AI応答時間が4.2秒から0.8秒に短縮され、81%の改善を達成しました。
3. 画像最適化の導入
Next.jsの組み込みImage
コンポーネントを使用し、画像の最適化を実装しました:
// After: Next.jsのImageコンポーネントによる最適化
import Image from 'next/image';
// 最適化された画像表示
<Image
src="/large-hero-image.jpg"
alt="Hero"
width={1200}
height={600}
priority={true} // LCPの場合はpriorityを設定
/>
さらに、より小さいビューポート向けに適切なサイズの画像が提供されるようにしました。この改善により、ページの初回読み込み時間が大幅に短縮されました。
4. レンダリング戦略の最適化
ページの特性に応じて適切なレンダリング戦略を選択するように変更しました:
// After: 静的コンテンツには静的生成(SSG)を使用
export async function getStaticProps() {
const data = await fetchStaticData();
return {
props: { data },
// 1時間ごとに再生成(ISR)
revalidate: 3600
};
}
頻繁に更新されないコンテンツには静的生成(SSG)やインクリメンタル静的再生成(ISR)を使用し、リアルタイムデータが必要なページのみにSSRを使用するように変更しました。
5. メモリリークの修正
メモリリークを引き起こしていたコンポーネントを修正し、コンポーネントのライフサイクルに合わせて適切にクリーンアップするようにしました:
// After: useEffectとクリーンアップを使用
function DataDisplayComponent() {
const [data, setData] = useState([]);
useEffect(() => {
// ローカルステートにデータを保存
const newData = fetchNewData();
setData(prev => [...prev, newData]);
// クリーンアップ関数
return () => {
// コンポーネントのアンマウント時に必要なクリーンアップ
};
}, [dependency]);
return <div>{/* データ表示 */}</div>;
}
これにより、メモリ使用量が1.2GBから0.6GBに削減されました。
📊 Applications Managerの強力な機能
改善プロセスで特に役立ったApplications Managerの機能を紹介します。
1. リアルタイムダッシュボード
Applications Managerのリアルタイムダッシュボードでは、アプリケーションのパフォーマンス指標をリアルタイムで確認できます[^3][^5]。特に以下の指標が役立ちました:
- リクエスト数/秒(RPS)
- 平均応答時間
- エラー率
- メモリ/CPU使用率
ダッシュボードは直感的で、問題が発生した時点で即座に通知を受け取ることができました。
2. アプリケーション依存関係マッピング
Applications Managerは、アプリケーションの依存関係を視覚的に表示する機能を備えています。これにより、Next.jsアプリケーション、Supabaseデータベース、OpenAI API間の関係を把握し、ボトルネックの根本原因を特定するのに役立ちました[^3][^4]。
3. トランザクショントレース
個々のHTTPリクエストのトレースを詳細に確認できる機能は、特にAPIルートのパフォーマンス問題の特定に役立ちました。以下はトレース情報の例です:
- APIエンドポイントへのリクエスト時間
- データベースクエリの実行時間
- 外部API呼び出しの応答時間
- サーバーサイドレンダリングの処理時間
この情報により、処理時間が最も長いコンポーネントを特定し、最適化の優先順位付けができました。
4. アラートと通知設定
パフォーマンス問題を早期に検出するために、Applications Managerのアラート機能を設定しました:
- 応答時間が1秒を超えた場合のアラート
- エラー率が1%を超えた場合のアラート
- メモリ使用量が設定閾値を超えた場合のアラート
これにより、問題が深刻化する前に対処できるようになりました[^3][^5]。
💰 改善による具体的な効果
Applications Managerを使った最適化の結果、以下の改善が達成されました:
1. パフォーマンス指標の改善
指標 | 改善前 | 改善後 | 改善率 |
---|---|---|---|
初回ページ読み込み時間 | 2.3秒 | 0.23秒 | 90% |
AI機能の応答時間 | 4.2秒 | 0.8秒 | 81% |
データベースクエリ時間 | 800ms | 45ms | 94% |
メモリ使用量 | 1.2GB | 0.6GB | 50% |
2. ビジネス指標の改善
パフォーマンスの向上は、ビジネス指標にも大きな影響を与えました:
- ページ離脱率:15% → 3%(80%改善)
- コンバージョン率:2.1% → 4.3%(105%向上)
- サーバーコスト:月額2万円削減
3. 運用効率の向上
Applications Managerを導入したことで、運用効率も大幅に向上しました:
- 問題検出時間:数時間 → 数分(95%削減)
- トラブルシューティング時間:数日 → 数時間(75%削減)
- 予防的問題検出:問題発生前に検出可能に
🚀 Applications Managerを試す方法
私の経験から、Next.jsアプリケーションを運用している方は、ぜひApplications Managerを試してみることをお勧めします。以下の手順で簡単に始められます:
- ManageEngine Applications Manager無料トライアルにアクセス
- 評価版をダウンロードまたはDocker Composeファイルを使用して起動
- ウィザードに従って初期設定を完了
- アプリケーションの監視設定を行う
Docker Composeを使用する場合は、前述の設定例を参考にしてください。
📝 まとめ:Applications Manager導入のポイント
Applications Managerを使ったNext.jsアプリケーションのパフォーマンス最適化から、以下のポイントが重要だと感じました:
- 可視化の重要性:問題解決の第一歩は問題の可視化です。Applications Managerのリアルタイムダッシュボードにより、これまで見えなかった問題が明確になりました。
- 包括的な監視:フロントエンド、バックエンド、データベース、外部APIなど、アプリケーションのすべての層を監視することで、真のボトルネックを特定できます。
- 予防的アプローチ:問題が発生してからではなく、発生する前に検出して対処することで、ユーザー体験を継続的に向上させることができます。
- データ駆動型の最適化:「感覚」ではなく、実際のデータに基づいて最適化の優先順位を決定することが重要です。
Next.jsアプリケーションを運用している方は、ぜひApplications Managerを試して、パフォーマンスの改善に取り組んでみてください。今回の記事が皆さんのパフォーマンス最適化の参考になれば幸いです!