Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

2 © 2019-2026 OPTiM Corp. All rights reserved. 自己紹介 自己紹介

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

4 © 2019-2026 OPTiM Corp. All rights reserved. 目次

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

6 © 2019-2026 OPTiM Corp. All rights reserved. Athenaが遅すぎるので Next.js 16 のCache Componentsで倒してみた 発生していた問題 / 導入効果

Slide 7

Slide 7 text

© 2019-2026 OPTiM Corp. All rights reserved. 7 製品の保守システムにて、AWS Athenaを用いている部分が存在 S3に保存されているデータ量が多く、レスポンスまで1分~3分ほどかかる。 ページを表示するたびにクエリを叩いていた。 発生していた問題 リクエスト レスポンス 1~3分程度かかる 検索 Athena S3 検索結果 保守システム ※Athena(S3上に在るcsv等のデータを対象にSQLで検索が可能AWSのサービス)

Slide 8

Slide 8 text

© 2019-2026 OPTiM Corp. All rights reserved. 8 レスポンスが遅すぎる 複数人で同じ画面をみるのに、全員がレスポンスの遅いAPIを叩く フロントエンド側でできる施策は無いか?

Slide 9

Slide 9 text

© 2019-2026 OPTiM Corp. All rights reserved. 9 そうだCache Componentsを導入しよう Cache Componentsを導入しよう

Slide 10

Slide 10 text

© 2019-2026 OPTiM Corp. All rights reserved. 10 キャッシュを導入したことにより、キャッシュが有る場合は、即時レスポンスが返却されるよう に! キャッシュ存在時は 1秒 未満!! 導入効果 リクエスト レスポンス 1~3分程度かかる 検索 Athena S3 検索結果 保守システム Next.js Server キャッシュ キャッシュ なし

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

© 2019-2026 OPTiM Corp. All rights reserved. 14 ヘッダー ブログ投稿 おすすめ商品 (ユーザー固有) Next.js15での実装例 完全静的 ヘッダー 記事1 記事2 記事3 キャッシュ キャッシュ キャッシュ キャッシュ無し キャッシュ無し キャッシュ無し

Slide 15

Slide 15 text

© 2019-2026 OPTiM Corp. All rights reserved. 15 Next.js15では、どのように実装する?? Next.js15での実装例

Slide 16

Slide 16 text

© 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 ( <> {/* 毎回レンダリングされる(Dynamic Rendering) */}

おすすめ商品

    {reco.map((x: any) =>
  • {x.name}
  • )}
) } cookies() をページ直下で読むとルート全体が Dynamic 扱いになり、静的にできる部分まで Full Route Cache から外れて毎リクエスト再レンダリングになりうる。

Slide 17

Slide 17 text

© 2019-2026 OPTiM Corp. All rights reserved. 17 どうするか? Next.js15での実装例

Slide 18

Slide 18 text

© 2019-2026 OPTiM Corp. All rights reserved. 18 Next.js15での実装例 export default function Page() { // ここで cookies()/headers() を読まない(= ルートを動的化しない) return ( <> {/* fetch(,{next: { revalidate: 600 },})でキャッシュ設定しなくても Full Route Cacheにはなるはず */}

おすすめ商品

{/* クライアント側からcookieと一緒にAPIを叩く*/} ) } PageからDynamic API を除外

Slide 19

Slide 19 text

© 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 (

記事一覧

    {posts.map((p) =>
  • {p.title}
  • )}
) } fetchにキャッシュを設定

Slide 20

Slide 20 text

© 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(null) useEffect(() => { ;(async () => { const res = await fetch('/api/recommended', { method: 'GET', credentials: 'include', // cookie を送る cache: 'no-store', }) setItems(await res.json()) })() }, []) if (!items) return

読み込み中…

return
    {items.map((x) =>
  • {x.name}
  • )}
} ←アプリ仕様

Slide 21

Slide 21 text

© 2019-2026 OPTiM Corp. All rights reserved. 21 Next.js16 Cache Componentsでは? Next.js16での実装例

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

© 2019-2026 OPTiM Corp. All rights reserved. 23 Next.js16での実装例 export default function Page() { return ( <>

おすすめ商品

{/* (ユーザー固有)は Suspense で穴にする */} 読み込み中…

}> {/* サーバーサイドで実行される*/} ) } で分ける

Slide 24

Slide 24 text

© 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 (

記事一覧

    {posts.map((p) =>
  • {p.title}
  • )}
) }

Slide 25

Slide 25 text

© 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
    {products.map((x) =>
  • {x.name}
  • )}
}

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

27 © 2019-2026 OPTiM Corp. All rights reserved. Cache Handlers ~キャッシュはどこに保存されるのか~ Cacheの保存場所 / cacheHandlersとは

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

© 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 メモリ キャッシュ

Slide 31

Slide 31 text

© 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が共有されていない。

Slide 32

Slide 32 text

© 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 メモリ キャッシュ

Slide 33

Slide 33 text

© 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)

Slide 34

Slide 34 text

© 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) レート制限 遅いバックエンド 高コスト 不安定

Slide 35

Slide 35 text

© 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)

Slide 36

Slide 36 text

36 © 2019-2026 OPTiM Corp. All rights reserved. Cache Componentsへの所感

Slide 37

Slide 37 text

© 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によって、コンポーネント粒度で実現可能になった。

Slide 38

Slide 38 text

© 2019-2026 OPTiM Corp. All rights reserved. 38 Special Thanks!!!! 亀田さん 菅原さん • システムへのCache Componentes導入 • 実装レビュー • 実装レビュー

Slide 39

Slide 39 text

© 2019-2026 OPTiM Corp. All rights reserved. 39 Next.js 16 Cache Componentsで 細かいキャッシュの管理が可能に!! 全ユーザー共通の情報をキャッシュ化すると良い!! サーバーリソースにも配慮しよう!! まとめ

Slide 40

Slide 40 text

© 2019-2026 OPTiM Corp. All rights reserved.