Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Athenaが遅すぎるので Next.js 16の Cache Componentsで倒してみた

Avatar for OPTiM OPTiM
July 17, 2026

Athenaが遅すぎるので Next.js 16の Cache Componentsで倒してみた

2026/07/17 開催「OPTiM Frontend Meetup #1」でのオプティム 榎本の発表資料です。

https://optim.connpass.com/event/396222/

Avatar for OPTiM

OPTiM

July 17, 2026

More Decks by OPTiM

Other Decks in Technology

Transcript

  1. © 2019-2026 OPTiM Corp. All rights reserved. © 2019-2026 OPTiM

    Corp. All rights reserved. Athenaが遅すぎるので Next.js 16の Cache Componentsで倒してみた 榎本 登壇日:2026/07/17
  2. © 2019-2026 OPTiM Corp. All rights reserved. 3 自己紹介 榎本

    株式会社オプティム エンジニア 2026年4月 新卒エンジニアとして入社 運用管理システムの機能改善や追加開発を行う 趣味 ◼RPG(FFXIV) ◼電子工作(最近ちょっとできてない) ◼美味しいご飯 プロダクト ◼OPTiM Support & Growth Portal ◼OPTiM Hardware My Portal
  3. © 2019-2026 OPTiM Corp. All rights reserved. 5 目次 Athenaが遅すぎるのでNext.js

    16 のCache Componentsで倒してみた Cache Componentsとは Cache Handlers ~キャッシュはどこに保存されるのか~ Cashe Componentへの所感 まとめ
  4. 6 © 2019-2026 OPTiM Corp. All rights reserved. Athenaが遅すぎるので Next.js

    16 のCache Componentsで倒してみた 発生していた問題 / 導入効果
  5. © 2019-2026 OPTiM Corp. All rights reserved. 7 製品の保守システムにて、AWS Athenaを用いている部分が存在

    S3に保存されているデータ量が多く、レスポンスまで1分~3分ほどかかる。 ページを表示するたびにクエリを叩いていた。 発生していた問題 リクエスト レスポンス 1~3分程度かかる 検索 Athena S3 検索結果 保守システム ※Athena(S3上に在るcsv等のデータを対象にSQLで検索が可能AWSのサービス)
  6. © 2019-2026 OPTiM Corp. All rights reserved. 10 キャッシュを導入したことにより、キャッシュが有る場合は、即時レスポンスが返却されるよう に!

    キャッシュ存在時は 1秒 未満!! 導入効果 リクエスト レスポンス 1~3分程度かかる 検索 Athena S3 検索結果 保守システム Next.js Server キャッシュ キャッシュ なし
  7. © 2019-2026 OPTiM Corp. All rights reserved. 11 改善した点 ◼キャッシュが存在している間は、即レスポンスが返されるように

    ◼画面を行き来する際に、レスポンスを待つ時間が削減 ◼複数人操作時に同じリクエストを送る回数を削減できる 懸念点 ◼キャッシュを使うので、最新の検索結果は返却されない ⚫※強制的にキャッシュを更新するボタンを作ることで対応 改善しなかった点 ◼キャッシュが存在しないタイミングでのアクセスは引き続き時間がかかる 導入効果
  8. 12 © 2019-2026 OPTiM Corp. All rights reserved. Cache Componentsとは?

    Next.js15以前との比較 / Cache Componentsとは / 何が変わるのか?
  9. © 2019-2026 OPTiM Corp. All rights reserved. 13 Next.js 15以前

    ◼ルート設定(page/layout)+ fetch の挙動でキャッシュが決まる ◼どこがキャッシュ対象かが追いにくい Next.js 16 ◼use cache で 関数/コンポーネント単位に「ここをキャッシュ」を明示 ◼キャッシュ境界が読みやすい/追いやすい Next.js15以前との比較
  10. © 2019-2026 OPTiM Corp. All rights reserved. 14 ヘッダー ブログ投稿

    おすすめ商品 (ユーザー固有) Next.js15での実装例 完全静的 ヘッダー 記事1 記事2 記事3 キャッシュ キャッシュ キャッシュ キャッシュ無し キャッシュ無し キャッシュ無し
  11. © 2019-2026 OPTiM Corp. All rights reserved. 16 Next.js 15:意図せずDynamicになる例

    export default async function Page() { const userId = cookies().get('uid')?.value ?? 'guest' // cookie取得(Dynamic API) const reco = await getRecommended(userId) //おすすめ商品一覧 return ( <> <Header /> <main> <PostsList /> {/* 毎回レンダリングされる(Dynamic Rendering) */} <aside> <h2>おすすめ商品</h2> <ul>{reco.map((x: any) => <li key={x.id}>{x.name}</li>)}</ul> </aside> </main> </> ) } cookies() をページ直下で読むとルート全体が Dynamic 扱いになり、静的にできる部分まで Full Route Cache から外れて毎リクエスト再レンダリングになりうる。
  12. © 2019-2026 OPTiM Corp. All rights reserved. 18 Next.js15での実装例 export

    default function Page() { // ここで cookies()/headers() を読まない(= ルートを動的化しない) return ( <> <Header /> <main> <PostsList /> {/* fetch(,{next: { revalidate: 600 },})でキャッシュ設定しなくても Full Route Cacheにはなるはず */} <aside> <h2>おすすめ商品</h2> <RecommendedProductsClient /> {/* クライアント側からcookieと一緒にAPIを叩く*/} </aside> </main> </> ) } PageからDynamic API を除外
  13. © 2019-2026 OPTiM Corp. All rights reserved. 19 Next.js15での実装例 type

    Post = { id: string; title: string } export async function PostsList() { const res = await fetch('https://example.com/api/posts', { next: { revalidate: 600 }, // 10分 }) const posts: Post[] = await res.json() return ( <section> <h2>記事一覧</h2> <ul>{posts.map((p) => <li key={p.id}>{p.title}</li>)}</ul> </section> ) } fetchにキャッシュを設定
  14. © 2019-2026 OPTiM Corp. All rights reserved. 20 Next.js15での実装例 'use

    client' import { useEffect, useState } from 'react' type Product = { id: string; name: string } export function RecommendedProductsClient() { const [items, setItems] = useState<Product[] | null>(null) useEffect(() => { ;(async () => { const res = await fetch('/api/recommended', { method: 'GET', credentials: 'include', // cookie を送る cache: 'no-store', }) setItems(await res.json()) })() }, []) if (!items) return <p>読み込み中…</p> return <ul>{items.map((x) => <li key={x.id}>{x.name}</li>)}</ul> } ←アプリ仕様
  15. © 2019-2026 OPTiM Corp. All rights reserved. 21 Next.js16 Cache

    Componentsでは? Next.js16での実装例
  16. © 2019-2026 OPTiM Corp. All rights reserved. 22 ヘッダー ブログ投稿

    おすすめ商品 (ユーザー固有) Next.js16での実装例 完全静的 ヘッダー 記事1 記事2 記事3 キャッシュ キャッシュ キャッシュ キャッシュ無し キャッシュ無し キャッシュ無し use cache <Suspense>
  17. © 2019-2026 OPTiM Corp. All rights reserved. 23 Next.js16での実装例 export

    default function Page() { return ( <> <Header /> <main> <PostsList /> <aside> <h2>おすすめ商品</h2> {/* (ユーザー固有)は Suspense で穴にする */} <Suspense fallback={<p>読み込み中…</p>}> <RecommendedProducts /> {/* サーバーサイドで実行される*/} </Suspense> </aside> </main> </> ) } <Suspense>で分ける
  18. © 2019-2026 OPTiM Corp. All rights reserved. 24 Next.js16での実装例 ‘use

    cache‘でキャッシュを宣言 import { cacheLife } from 'next/cache' type Post = { id: string; title: string } export async function PostsList() { 'use cache' cacheLife('minutes') // 例:minutes/hours/days/max など(プロファイル) const res = await fetch('https://example.com/api/posts') const posts: Post[] = await res.json() return ( <section> <h2>記事一覧</h2> <ul>{posts.map((p) => <li key={p.id}>{p.title}</li>)}</ul> </section> ) }
  19. © 2019-2026 OPTiM Corp. All rights reserved. 25 Next.js16での実装例 //

    app/ui/RecommendedProducts.tsx (Next.js 16) import { cookies } from 'next/headers' type Product = { id: string; name: string } export async function RecommendedProducts() { // use cache しない = request-time(ユーザー固有を素直に扱う) const cookieStore = await cookies() const userId = cookieStore.get('uid')?.value ?? 'guest' const res = await fetch(`https://example.com/api/reco?userId=${userId}`, { cache: 'no-store', // 毎回最新 }) const products: Product[] = await res.json() return <ul>{products.map((x) => <li key={x.id}>{x.name}</li>)}</ul> }
  20. © 2019-2026 OPTiM Corp. All rights reserved. 26 Cache Componentsにより、

    “use cache”だけで 部品(関数・コンポーネント単位)ごとのキャッシュが 実現可能になった。 Cache Components で何が変わるか一言で? キャッシュの最適化が use cache のみで可能!!
  21. 27 © 2019-2026 OPTiM Corp. All rights reserved. Cache Handlers

    ~キャッシュはどこに保存されるのか~ Cacheの保存場所 / cacheHandlersとは
  22. © 2019-2026 OPTiM Corp. All rights reserved. 28 通常はサーバーのメモリ上 Cache

    Components の Cacheはどこに保存されるのか? リクエスト レスポンス 1~3分程度かかる 検索 Athena S3 検索結果 保守システム Next.js Server キャッシュ キャッシュ なし
  23. © 2019-2026 OPTiM Corp. All rights reserved. 29 Cache Components

    の Cacheはどこに保存されるのか? Next.js Server キャッシュ なし メモリ キャッシュ
  24. © 2019-2026 OPTiM Corp. All rights reserved. 30 Cache Components

    の Cacheはどこに保存されるのか? Cluster Node Node Pod Next.js Server メモリ キャッシュ Pod Next.js Server メモリ キャッシュ Pod Next.js Server メモリ キャッシュ Pod Next.js Server メモリ キャッシュ
  25. © 2019-2026 OPTiM Corp. All rights reserved. 31 Cache Components

    の Cacheはどこに保存されるのか? Cluster Node Node Pod Next.js Server メモリ キャッシュ Pod Next.js Server メモリ キャッシュ Pod Next.js Server メモリ キャッシュ Pod Next.js Server メモリ キャッシュ Pod間でCacheが共有されていない。
  26. © 2019-2026 OPTiM Corp. All rights reserved. 32 Cache Components

    の Cacheはどこに保存されるのか? Cluster Node Node Pod Next.js Server メモリ キャッシュ Pod Pod Next.js Server メモリ キャッシュ Pod Next.js Server メモリ キャッシュ Pod Next.js Server メモリ キャッシュ
  27. © 2019-2026 OPTiM Corp. All rights reserved. 33 Next.jsの'use cache'

    系が作るキャッシュの“保存先”を差し替える仕組み ◼デフォルト(use cache) : サーバーのインメモリ ◼カスタム(use cache: remote) : Redis or KV database 等 ⚫ハンドラ設定が前提 ◼※(use cache: private):ユーザーのブラウザのメモリ上 ⚫experimental機能 Cache Handlersとは? カスタム(use cache: remote) デフォルト(use cache)
  28. © 2019-2026 OPTiM Corp. All rights reserved. 34 Cache Handlersとは?

     カスタム(remote)ハンドラが有効なケース ◼レート制限のある API:上流サービスにレート制限/リクエスト上限があり、上限到達リスクを避けたい ◼遅いバックエンドの保護:高トラフィック時に DB や API がボトルネックになりやすいので、負荷を逃がした い ◼高コストな処理:DB クエリや計算など、繰り返し実行するとコスト(時間・料金)が大きい処理を減らしたい ◼不安定・信頼性の低いサービス:外部サービスがたまに失敗したり、可用性にムラがあるため、安定化したい 出典: Next.js 公式ドキュメントuse cache: remoteをChatGPTにて翻訳 (https://nextjs.org/docs/app/api-reference/directives/use-cache-remote) レート制限 遅いバックエンド 高コスト 不安定
  29. © 2019-2026 OPTiM Corp. All rights reserved. 35 Cache Handlersとは?

    カスタム(remote)ハンドラを避けた方がよいケース ◼すでにデータ層の前にサーバー側のKVSキャッシュ(Key-Value Store)がある場合: データ取得層を包むキャッシュが既にあるなら、use cache だけで(追加のキャッシュ層を増やさずに) 静的シェルにデータを含められることがある ◼処理がすでに十分速い場合(< 50ms): 近接・ローカルアクセス等で元処理が速いなら、remote キャッシュの参照(ネットワーク往復)の分だ け改善しない可能性がある ◼リクエストごとにキャッシュキーがほぼユニークになる場合: 例:検索フィルタ、価格帯、ユーザー固有パラメータなど → キャッシュがほとんど再利用されず、利用率がほぼゼロになりがち ◼データ更新が頻繁な場合(秒〜分単位): キャッシュがすぐ古くなり、ヒットしてもすぐ無効/ミスが増える → 上流の再検証(revalidation)待ちが頻発しやすい 出典: Next.js 公式ドキュメントuse cache: remoteをChatGPTにて翻訳 (https://nextjs.org/docs/app/api-reference/directives/use-cache-remote)
  30. © 2019-2026 OPTiM Corp. All rights reserved. 37 Next.js 14から発表されていたPPR(Partial

    Prerendering)※1が、 Next.js 16にて非常にシンプルに実現することができるようになりました。 キャッシュ化して高速化できる反面、サーバーリソース等々には気をつけていきたい 開発者として、フロントエンドでも自由にキャッシュを操作できるのは魅力的 Cache Componentsへの所感 ※1 PPR(Partial Prerendering) Static/Dynamicを同一ルートで混在させる仕組み Cache Componentsによって、コンポーネント粒度で実現可能になった。
  31. © 2019-2026 OPTiM Corp. All rights reserved. 38 Special Thanks!!!!

    亀田さん 菅原さん • システムへのCache Componentes導入 • 実装レビュー • 実装レビュー
  32. © 2019-2026 OPTiM Corp. All rights reserved. 39 Next.js 16

    Cache Componentsで 細かいキャッシュの管理が可能に!! 全ユーザー共通の情報をキャッシュ化すると良い!! サーバーリソースにも配慮しよう!! まとめ