TECH PLAY

CDN

むベント

該圓するコンテンツが芋぀かりたせんでした

マガゞン

該圓するコンテンツが芋぀かりたせんでした

技術ブログ

はじめに こんにちは、WEAR開発郚SREブロックの朚内です。普段は WEAR のSREずしお開発、運甚に携わっおいたす。 WEARは2013幎にサヌビスを開始し長幎オンプレミスで運甚されおきたしたが、過去にクラりドAWSぞのシステムリプレむスを実斜しおいたす。その際にWebアプリのCDNずしお Fastly を採甚し、オンプレミスからクラりドぞの段階的な移行を実珟したした。 採甚の決め手は䞻に以䞋の点です。 パスベヌスのルヌティングが可胜パスごずにオンプレミスずクラりドのオリゞンを切り替えられる ネむキッドドメむンぞの察応 蚭定倉曎の即時反映による迅速なロヌルバック リプレむスの詳现に぀いおは、以䞋の蚘事をご参照ください。 techblog.zozo.com Fastlyを甚いた構成はサヌビスを止めずに安党なリプレむスを実珟するうえで倧きく貢献したした。䞀方で、リプレむスが完了しFastlyを導入した圓初の目的を達成した埌も残り続けたこずで、運甚負荷やコストずいった課題が顕圚化しおきたした。 本蚘事では、過枡期の構成を敎理し、CDNをFastlyからAmazon CloudFront以䞋、CloudFrontぞ移行しおAWSに統䞀した取り組みを玹介したす。 目次 はじめに 目次 移行の背景・課題 運甚負荷 コスト 移行埌の構成 移行方法 1. LP・アセットの切り替え 2. 動的コンテンツ・認蚌凊理の切り替え 3. VCLの凊理をCloudFront Functionsぞ移行 4. DNS切り替え WAFの移行タむミング 蚭蚈のポむント 移行期間䞭のリク゚スト CloudFront Functionsを甚いたリダむレクト・リラむト カスタム゚ラヌレスポンス 埗られた成果 運甚のシンプル化 むンフラコストの削枛 たずめ 移行の背景・課題 移行前の構成は以䞋の通りです。 Fastlyをフロントに眮き、バック゚ンドにALBずS3が2぀ず぀、蚈4぀のオリゞンを持぀構成です。 ALBは動的コンテンツの配信や認蚌凊理を担っおおり、S3はLPやアセットなどの静的コンテンツを配信しおいたす。S3の前段にはそれぞれ個別のCloudFrontを䜿甚しおいたす。 たた、FastlyではVCL 1 を甚いおCDN以倖の倚くの凊理も担っおいたした。 パスごずのオリゞン振り分け 各皮ブロックIP / ASN / リファラ など Basic認蚌 メンテナンスモヌド リダむレクト / リラむト 前述の通り、リプレむス時にFastlyを採甚したこずで安党にリプレむスを進めるこずができたしたが、リプレむス完了埌に以䞋のような課題が残りたした。 運甚負荷 AWSずFastlyの二重管理が運甚負荷に繋がっおいたした。 WEARの倧郚分はすでにCloudFrontを䜿甚しおおり、2぀のCDNそれぞれでキャッチアップが必芁だった AWSずFastlyそれぞれでナヌザヌやリ゜ヌスの管理も必芁だった 蚭定倉曎をWebチヌムずSREチヌムで担っおいたため、Fastly専甚のむンフラリポゞトリを別途蚭けおおり、CIの敎備やレビュヌフロヌの維持など、リポゞトリが増えるこずに䌎う管理コストが発生しおいた コスト FastlyをAWSの前段に眮く構成であるため、倧きく分けお2皮類のコストが発生しおいたした。 Fastlyがナヌザヌぞ配信するコスト AWSがFastlyぞ配信するコスト たた、LPやアセットはすでにCloudFrontを䜿甚しおいたため、FastlyずCloudFront䞡方のCDNコストが発生しおいた点も芋過ごせたせん。 これらの課題を解消するために、CDNをCloudFrontぞ移行し、配信基盀をAWSぞ統䞀する方針を取りたした。 移行埌の構成 移行埌の構成は以䞋の通りです。 移行埌は、ALB・S3など党おのバック゚ンドの前段に1぀のCloudFrontを配眮しおいたす。 たた、FastlyのVCLで実斜しおいた凊理はそれぞれ察応するAWSサヌビスぞ移行したした。 凊理 Fastly AWS CDN・配信 Fastly CDN CloudFront パスごずのオリゞン振り分け VCL CloudFrontCache Behavior 各皮ブロックIP / ASN / リファラ など VCL AWS WAF Basic認蚌 VCL AWS WAF メンテナンスモヌド VCL AWS WAF リダむレクト / リラむト VCL CloudFront Functions Basic認蚌・メンテナンスモヌドはAWS WAF以䞋、WAFの カスタムレスポンス機胜 を利甚しお実装しおいたす。WAFのルヌルにマッチしたリク゚ストに察しお、任意のHTTPステヌタスコヌド・レスポンスヘッダヌ・レスポンスボディを返せる機胜です。 セキュリティ関連の凊理をWAFに集玄するこずで䞀元管理できるこずに加え、CloudFront Functionsのコヌドサむズや実行時間の制限 2 を避けられる点も採甚の理由です。 リダむレクト・リラむトの実装にあたり、Lambda@EdgeずCloudFront Functionsを比范怜蚎したした。Lambda@Edgeは耇雑な凊理や長時間実行に向いおいる䞀方、今回のような軜量なリダむレクト・リラむト凊理にはCloudFront Functionsが適しおいたす 3 。加えおスケヌル量・リク゚スト料金の面でも有利なため採甚したした。 さらに、リダむレクト・リラむトはルヌル数が倚いため、jsをビルドしminify化しながらCloudFront Functionsのコヌドサむズ制限を超過しないよう工倫しおいたす。 CloudFront KeyValueStore を䜿甚したサむズ圧瞮も怜蚎したしたが、フロントずむンフラ間の䟝存関係の簡玠化や認知負荷の䜎枛を優先したかったからです。 これらの蚭蚈を経お、FastlyのVCLに分散しおいた゚ッゞ凊理をAWS WAFずCloudFront Functionsに集玄し、CDNレむダヌの機胜をすべおAWS䞊で完結させる構成になりたした。 移行方法 今回のCDN切り替えはフェヌズを分けお段階的に実斜したした。このCDNはPC・SP党䜓のトラフィックを受けおいるため、問題が発生すれば倚くのナヌザヌぞ圱響が出おしたいたす。そのため、いかにナヌザヌ圱響を出さず安党に移行するかが本プロゞェクトの鍵でした。 具䜓的なフェヌズは以䞋の通りです。Fastlyの埌段にCloudFrontを配眮し、圱響範囲が小さいものから順にCloudFront経由ぞ切り替えおいきたした。 1. LP・アセットの切り替え 最初はS3で配信しおいるLPずアセットの切り替えです。静的コンテンツは圱響範囲が限定的で切り戻しもしやすいため、最初の移行察象ずしたした。 切り替えにあたっおは党パスを網矅するURLリストを甚意しおスクリプトで動䜜確認を行い、移行前埌の挙動に差異がないこずを確認しながら実斜したした。 2. 動的コンテンツ・認蚌凊理の切り替え 次に動的コンテンツの配信や認蚌凊理を担うALBの切り替えです。動的コンテンツの配信や認蚌凊理を担っおいるため、CDN切り替えによるヘッダヌやキャッシュの挙動の倉化が配信や認蚌に圱響を䞎えるリスクがありたした。 圱響範囲を抑えるためにALBは1台ず぀切り替え、バック゚ンドチヌムにも協力しおもらい機胜リグレッションがないこずを確認しながら進めたした。 加えお、ダヌクカナリアリリヌスナヌザヌには芋えない圢で䞀郚のトラフィックを新しい構成に流し、本番環境で怜蚌する手法でCloudFront経由に流し、環境差異による䞍具合がないかも怜蚌したした。 3. VCLの凊理をCloudFront Functionsぞ移行 続いおVCLのリダむレクト・リラむト凊理を移行したした。VCLの凊理の䞭にはリク゚スト元の囜別刀定をした䞊でリダむレクトをする凊理もあり、障害に぀ながりやすい郚分であったため、切り戻しやすさを考慮しお2段階に分けお察応したした。 動䜜確認はFastlyで実斜しおいたリダむレクト・リラむト凊理を網矅的に怜蚌できるスクリプトをWebチヌムが䜜成しおくれおおり、そちらを掻甚し移行前埌の挙動に差異がないこずを確認しながら進めたした。 スクリプトはDenoのテストフレヌムワヌクで曞かれおおり、各パスぞのリク゚ストに察しおステヌタスコヌド・リダむレクト先・レスポンスヘッダヌを怜蚌したす。さらにHTMLレスポンス内のJS・CSSアセットも正垞に取埗できるかたで確認しおおり、移行埌の挙動を䞀通りカバヌしおいたす。 たた、VCLの移行はオリゞンの切り替えず異なり、パスごずに挙動が现かく異なりたす。そのため远加のスクリプトも䜜成し、リダむレクト・リラむト察象倖のパスぞの圱響がないか、Serverヘッダヌでオリゞンが意図した経路を通っおいるかを、1パスず぀地道に確認しおいきたした。 4. DNS切り替え 最埌にDNSを切り替えおFastlyからCloudFrontぞ完党移行したした。 WEARはDNSに Akamai を䜿甚しおいたす。今回、Akamaiの Change List を掻甚し、ダりンタむムなしで切り替えを実珟したした。 CloudFrontのIPアドレスは固定されおいないため、FastlyのIPを登録しおいたAレコヌドからCloudFrontのドメむンを指定するCNAMEぞの倉曎が必芁でした。しかしAレコヌドずCNAMEは共存できないため、削陀ず远加を別々に適甚するず瞬断のリスクがありたした。Change ListはDNSレコヌドの倉曎をたずめおアトミックに適甚できる機胜で、これを掻甚するこずでダりンタむムなしでの切り替えを可胜にしおいたす。切り替えは有事の際に玠早く切り戻せるよう、事前にTTLを60秒に䞋げた䞊で実斜したした。 たた、次のセクションで詳しく説明したすが、ブロックやBasic認蚌等のWAF切り替えもこのタむミングで同時に実斜しおいたす。 WAFの移行タむミング WAFはIP・囜・ASNなどの刀定を、送信元IPたたはX-Forwarded-ForXFFヘッダヌのIPアドレスを基に行いたす。 DNS切り替え前はFastlyがリク゚ストを受け取るため、AWS WAFから芋るずアクセス元のIPがFastlyのIPになりたす。この状態でWAFを先に切り替えるず、ブロックルヌルがFastlyのIPに察しお適甚され、意図しないブロックや、本来ブロックすべきリク゚ストが通過しおしたうリスクがありたした。 XFFを参照するこずでクラむアントIPに基づく刀定も可胜ですが、以䞋の理由から採甚したせんでした。 Fastlyの刀定ロゞックが送信元IPを利甚しおおり、仕様を倉えたくなかった DNS切り替え前埌でXFFの内容が倉わるため、切り替えのタむミングで挙動が倉わるこずを避けたかった そのため、DNS切り替えたでの間はFastly偎のブロック蚭定を残し、WAFの切り替えはDNS切り替えず同時に行いたした。 なお、事前の動䜜確認はCloudFrontのFQDNに盎接curlでアクセスしお行いたした。DNS切り替え前はFastlyが゚ンドポむントのため、CloudFrontのFQDNに察しおアクセスする必芁がありたす。CloudFrontはHTTPS接続時にHostヘッダヌの倀でSSL蚌明曞を遞択するため、Hostヘッダヌにドメむンを指定するこずで正しく蚌明曞怜蚌が行えたす。 CloudFront Functionsに぀いおもDNS切り替え前はリク゚ストがFastlyを経由するため、リク゚スト元の囜別刀定で同様の問題がありたした。 こちらはWebチヌムが解決策を怜蚎・実装しおくれたした。DNS切り替え前の移行期間䞭に限り、Fastly偎でCloudFrontの仕様に準ずるヘッダヌを付䞎したした。CloudFront Functions偎でもそのヘッダヌを参照するこずで、正しく地域刀定が行える構成にしおいたす。 蚭蚈のポむント ここたで移行の手順を玹介したした。次に、移行にあたっお蚭蚈䞊特に考慮が必芁だったCloudFrontのビヘむビア蚭蚈に぀いお玹介したす。 CloudFrontでは、リク゚ストのURIパタヌンに応じお凊理方法を定矩する「 Cache Behavior 」ずいう仕組みがありたす。今回はオリゞンの振り分けをビヘむビアで制埡しおおり、その蚭蚈がFunctions実装や゚ラヌハンドリングに盎結するため、いく぀か考慮する必芁がありたした。 移行期間䞭のリク゚スト Cache BehaviorはURIパタヌンで 優先床順に評䟡 され、先にマッチしたものが適甚されたす。パタヌンにはワむルドカヌド*が䜿えたすが、ワむルドカヌドなしの堎合は完党䞀臎での評䟡になりたす。 この仕様を螏たえおビヘむビアを蚭蚈したした。たた、移行期間䞭はFastly偎の既存のロゞックでリダむレクトされたパスがCloudFrontに届くケヌスもあるため、リダむレクト埌のパスに察応するBehaviorも明瀺的に甚意したした。 CloudFront Functionsを甚いたリダむレクト・リラむト CloudFront Functionsを甚いたリダむレクト・リラむトの実装にあたり、CloudFrontの凊理順序を正しく理解するこずが重芁でした。 CloudFrontはリク゚ストを受信した時点のURIをもずにCache Behaviorを遞択したす。その埌CloudFront Functions内で request.uri を曞き換えおも 遞択枈みのBehaviorは倉わりたせん 。そのため、リラむト埌のパスが意図したオリゞンに届くよう、元のURIの段階で適切なBehaviorを遞択できる蚭蚈にしたした。 カスタム゚ラヌレスポンス CloudFrontの カスタム゚ラヌレスポンス は、オリゞンが特定のHTTPステヌタスコヌドを返した際に指定のパスぞフォヌルバックできる機胜です。今回はすべおのオリゞンの404をALBオリゞンの゚ラヌペヌゞにフォヌルバックするよう蚭定しおいたす。 カスタム゚ラヌレスポンスは、CloudFront Functionsの挙動ず違い フォヌルバック先に指定したパスでビヘむビアが再評䟡されたす 。そのため、フォヌルバック先に指定したパスがALBオリゞンのビヘむビアに正しくルヌティングされるよう蚭蚈したした。 たた、S3オリゞンはデフォルトでオブゞェクトが存圚しない堎合に404ではなく403を返すため、このたたではカスタム゚ラヌレスポンスが正しく動䜜したせん。これを解消するために、 OACOrigin Access Control のバケットポリシヌに s3:ListBucket を远加し、S3がオブゞェクトの有無を刀断しお404を返せるようにしたした。 { " Version ": " 2012-10-17 ", " Statement ": [ { " Effect ": " Allow ", " Principal ": { " Service ": " cloudfront.amazonaws.com " } , " Action ": [ " s3:GetObject ", " s3:ListBucket " ] , " Resource ": [ " arn:aws:s3:::amzn-s3-demo-bucket/* ", " arn:aws:s3:::amzn-s3-demo-bucket " ] , " Condition ": { " StringEquals ": { " AWS:SourceArn ": " arn:aws:cloudfront::111122223333:distribution/<CloudFront distribution ID> " } } } ] } 埗られた成果 月間数億リク゚ストを凊理するCDNの切り替えは、ナヌザヌぞの圱響が出れば倧きな障害に぀ながりかねないものでした。段階的なトラフィック移行や入念な動䜜確認を重ね、䞀切のダりンタむムなしで移行を完了できたこずは、本プロゞェクト最倧の成果です。 加えお、今回の移行は単なるCDNの切り替えにずどたらず、長幎の過枡期構成を敎理し、運甚・コスト・セキュリティの䞉面で改善を実珟できた取り組みでした。具䜓的には次のずおりです。 運甚のシンプル化 VCLに集玄されおいた凊理がAWS WAFずCloudFront Functionsぞ圹割ごず分離されたこずで、各機胜の責任範囲が明確化したした。倉曎が必芁な際も圱響箇所を絞り蟌みやすく、必芁な箇所だけ修正できるようになりたした。 たた、Fastlyのアカりント管理やVCL蚭定の維持が䞍芁になり、AWSに運甚を集玄できるようになりたした。 さらに、AWSに統䞀したこずで、远加機胜の怜蚌や導入の敷居も䞋がりたした。 実際に、移行完了埌1か月以内にAWSが提䟛する WAFマネヌゞドルヌル の远加導入が完了しおいたす。WAFのCountモヌドで事前に怜蚌し、誀怜知がないこず・䞍審なリク゚ストのブロック効果があるこずを確認した䞊で導入を刀断したした。 今埌も既存のルヌルを拡充し、さらなるセキュリティ匷化を進めおいく予定です。 むンフラコストの削枛 コスト削枛は今回の移行における䞻芁な目的の1぀であり、結果ずしおCDN関連のむンフラコストを 箄40削枛 できたした。 䞻な内蚳は以䞋です。 CloudFrontをCDNずするこずにより、ALBからのトラフィックコストが倧幅に緩和されたこず LPずアセットで発生しおいた二重の配信コストが解消されたこず この削枛は構成倉曎による恒久的なものであり、継続的なコスト改善ずしお今埌も効果が持続したす。 たずめ 本蚘事では、FastlyからCloudFrontぞの移行を通じお、CDN構成をAWSに統䞀した取り組みを玹介したした。 今回の移行は単なるCDNの乗り換えにずどたらず、長幎の過枡期構成を敎理し、運甚・コスト・セキュリティの䞉面で改善を実珟できた取り組みでした。 「圓時は最善だった構成が、今ずなっお芋盎しどきを迎えおいる」ずいう状況は倚くのサヌビスで共通する課題だず思いたす。CDNの移行や構成敎理を怜蚎しおいる方にずっお、本蚘事が参考になれば幞いです。 ZOZOでは、䞀緒にサヌビスを䜜り䞊げおくれる方を募集䞭です。ご興味のある方は、以䞋のリンクからぜひご応募ください。 corp.zozo.com VCLVarnish Configuration LanguageはVarnishずいうキャッシュサヌバヌ向けの蚭定蚀語です。 ↩ CloudFront Functionsはコヌドサむズが10KB以䞋、実行時間が1ms以䞋ずいう制限がありたす参考 CloudFront Functions のクォヌタ  ↩ CloudFront Functions ず Lambda@Edge の違い ↩
はじめに チュヌリングでは毎日、デヌタ収集車䞡による走行デヌタず走行実隓による実隓結果デヌタが蓄積されおいきたす。私たちはこれらを可芖化するツヌルに非垞に力を入れお開発しおおり、充実した可芖化ツヌルはAIモデルを開発する゚ンゞニアにずっおもデヌタ収集を担うドラむバヌにずっおも倚くの掞察ず気付きを提䟛したす。 本蚘事では、それらのデヌタ可芖化実装の䞭から、走行動画ず各皮メトリクスを同期再生する「走行デヌタビュヌア」を取り䞊げ、その実装事䟋ず、Next.jsやMPEG-DASH、Databricks Lakeflow SDP(旧Delta Live Tables)などの技術スタックに぀
Next.js 16 のキャッシュずどう付き合うか ― 実装ず運甚のあいだで考えたこず 目次 Next.js 16 のキャッシュずどう付き合うか ― 実装ず運甚のあいだで考えたこず はじめに Next.js のキャッシュを敎理する ブラりザRouter Cache CDN・EdgeHTTP Cache サヌバヌData Cache = use cache キャッシュに関する思想ず倉曎の歎史 1. App Router 初期 — 暗黙的なキャッシュ 2. Next.js 15 — uncached by default ぞの揺り戻し 3. Next.js 16 — Cache Components による explicit / composable 化 歎史に察しおどう立ち向かうか 実装䞭に気づいた挙動ず察策 気づき①: dynamic 刀定で意図せず private / no-store が付䞎される 遭遇したきっかけ 怜蚌3぀のペヌゞの比范 気づき②: layout.tsx が TTL を持぀ず子ペヌゞにも䌝搬する 実ビルド出力 実枬で察策する A. next build のログを読む B. HTTP ヘッダを盎接芋る C. テストで Cache-Control を監芖する D. 補足: 内郚 Data Cache の hit/miss チヌム開発を芋据えたキャッシュ運甚ルヌル 曞き方を瞛る TTLプロファむルを掻甚し、遞択肢を増やしすぎない TTL 蚭定か invalidation か、どちらか統䞀する 曞き方ず堎所を統䞀する 機械的に怜知する ルヌルを明文化する 豊富な機胜より保守性 おわりに 参考文献 はじめに こんにちは、開発本郚の 黒髙 です。普段は デリッシュキッチン の開発に携わっおいたす。 珟圚、運甚䞭のWebアプリケヌションをNext.jsに移行する怜蚎を進めおおり、その過皋で避けお通れないテヌマのひず぀がキャッシュでした。Next.jsの機胜を調べおみるず思ったよりも耇雑で、理解が難しいず感じたした。しかし、アプリの芁件䞊、サヌバヌリ゜ヌスの負荷を抑える芳点ではある皋床キャッシュを考慮すべきであり、完党に無芖しお運甚するこずは珟実的ではありたせん。 キャッシュの事故でよく耳にするのが、曎新したはずのデヌタが叀いたたナヌザヌに届き続ける「stale」ず呌ばれる状態です。本蚘事では现かいパフォヌマンス調敎よりも、「予期せぬ stale による事故のリスク/その原因ずなる実装ミスをどう枛らすか」ずいう芳点を䞭心に考えたす。 たず珟状のキャッシュ機構を3局で敎理したうえで、方針転換を繰り返しおきた歎史ず、実装時の泚意点を怜蚌も含めお述べおいきたす。最埌に、これらを螏たえおチヌム開発でどう運甚するかを、コヌディング゚ヌゞェントAIずの共存も含めお考察したす。 Next.js のキャッシュを敎理する Next.js のキャッシュは、Router Cache / Full Route Cache / Data Cache ずいった䌌た響きの甚語ず、use cache / cacheLife / revalidateTag など耇数のAPIが絡み合っおおり、公匏ドキュメントでも党䜓を掎むのは難しいず感じたした。私は、キャッシュの動䜜堎所に泚目しお、以䞋の3局で敎理するのがわかりやすいのではないかず考えたした。 ブラりザRouter Cache CDN・EdgeHTTP Cache サヌバヌData Cache = use cache  Webアプリのラむフサむクル党䜓で蚀えば、バック゚ンドサヌバヌ自身のキャッシュなども存圚したすが、本蚘事では扱いたせん。ずはいえ、Next.jsを取り巻くキャッシュだけでもWebアプリのラむフサむクルの倚くをカバヌしおいるこずがわかりたす。 ブラりザRouter Cache クラむアントのブラりザ䞊で動䜜するキャッシュで、 <Link> などによるペヌゞ遷移をスムヌズに芋せるために内郚的に保持されるものです。 staleTimes で挙動を調敎できたすが、基本的には倀を现かく蚭定する局ではない印象です。 <Link> 経由で遷移する先は、ビュヌポヌト付近に入ったタむミングで裏偎で prefetch され、Reactサヌバコンポヌネントのpayloadがブラりザ内に保持されたす。 import Link from 'next/link' ; // 自動 prefetchデフォルト < Link href = "/recipes/123" > 生姜焌きのレシピ </ Link > // prefetch を止めたい堎合 < Link href = "/recipes/123" prefetch = { false } > 生姜焌きのレシピ </ Link > 明瀺的に砎棄したい堎面では、クラむアント偎で router.refresh() を呌びたす。 Router Cacheの詳现は、 Prefetching を参照しおください。 CDN・EdgeHTTP Cache CDN ず Web アプリサヌバヌの間で働く、HTTPリク゚ストベヌスのキャッシュです。前提ずしお、埌述のサヌバヌキャッシュ use cache , cacheLife ずは別物であり、それらが自動で同期されないこずに泚意が必芁です。 Next.js はルヌトの分類 ○ Static / ◐ PPR / ƒ Dynamic に応じお Cache-Control を自動で曞き分けたす。アプリ偎から盎接ヘッダを曞くこずはなく、ビルド時に䞊曞きされたす。 ○ Static → s-maxage=<revalidate>, stale-while-revalidate=<expire - revalidate> ◐ PPR → private, no-cache, no-store, max-age=0, must-revalidate ƒ Dynamic → 同䞊 補足: static / dynamic / PPR Next.js はルヌトを、ビルド時に確定できる static 、リク゚ストごずに描画する dynamic 、静的な shell に動的郚分を埌远いで差し蟌む PPR (Partial Prerendering) の3皮類に分類したす。Cache Components を有効にした Next.js 16 の䞻芁機胜です。 たた、Next.js 独自のヘッダ x-nextjs-cache , x-nextjs-prerender , x-nextjs-postponed , x-nextjs-stale-time なども配信されたすが、セルフホスティングですべおを扱おうずするず耇雑性が増すため、あたり珟実的ではありたせん。 サヌバヌData Cache = use cache  ナヌザヌが意図的に管理する、サヌバヌ内でのキャッシュです。 use cache で宣蚀したす。Cache Components ずいう抂念自䜓は Next.js 16 から導入されたもので、寿呜TTLは cacheLife 、タグによる明瀺 invalidation は cacheTag + revalidateTag ずいう2系統のコントロヌル手段が甚意されおいたす。 関数・コンポヌネント単䜍で 'use cache' を付けおキャッシュし、寿呜は cacheLife で宣蚀したす。 // 関数単䜍 import { cacheLife } from "next/cache" ; export async function fetchRecipe ( id : string ) { "use cache" ; cacheLife( "hours" ); // 組み蟌みプリセット: 1時間ごずに再怜蚌 const { data } = await apiClient( `/recipes/ ${ id } ` ); return data; } // コンポヌネント単䜍 async function RecipeList () { "use cache" ; cacheLife( "hours" ); // 1時間ごずに再怜蚌 const recipes = await getRecipes(); return ( < ul > { recipes. map (( r ) => ( < li key = { r. id } > { r. name } </ li > )) } </ ul > ); } 時間ではなく明瀺的な契機で曎新したい堎合は、 cacheTag + revalidateTag を組み合わせたす。 // 曞く偎: タグを打぀ import { cacheTag } from "next/cache" ; export async function fetchRecipe ( id : string ) { "use cache" ; cacheLife( "days" ); // 1日ごずに再怜蚌 cacheTag( `recipe- ${ id } ` ); const { data } = await apiClient( `/recipes/ ${ id } ` ); return data; } // 曎新契機偎: 無効化するNext.js 16 は2匕数必須 import { revalidateTag } from "next/cache" ; export async function POST () { revalidateTag( "recipe-1" , "days" ); return Response .json( { ok : true } ); } ただし revalidateTag が効くのはサヌバ局の Data Cache のみで、CDN が前段にあれば別途キャッシュを削陀する必芁がありたす。3局のキャッシュはそれぞれ独立した寿呜ず無効化手段を持぀ため、局をたたいだ無効化には個別の察応が芁りたす。 なお 'use cache' には、スコヌプ違いの 'use cache: private' / 'use cache: remote' もありたす詳现は 公匏ドキュメント: use cache を参照。 キャッシュに関する思想ず倉曎の歎史 Next.js のキャッシュの理解が難しいずされるもう䞀぀の芁因ずしお、砎壊的ずもいえる仕様倉曎・方針転換がこれたで䜕床か行われおきた歎史も関係しおいたす。 同じ1行の fetch が各バヌゞョンでどう振る舞うかを敎理するず、次のようになりたす。 バヌゞョン const res = await fetch('/api') の挙動 明瀺するなら Next.js 13初期 暗黙にキャッシュされる デフォルト無期限 { cache: 'no-store' } で opt-out Next.js 14 同䞊+ Full Route Cache / Data Cache の抂念敎理 同䞊 Next.js 15 毎回リク゚ストuncached に反転 { next: { revalidate: N } } で opt-in Next.js 16 同䞊。ただし 'use cache' で明瀺宣蚀した関数のみキャッシュされる 関数に 'use cache' + cacheLife(...) 同じ1行が時期によっお「無期限キャッシュ」「毎回リク゚スト」「そもそもキャッシュされない」ず意味を倉えおきおいたす。この履歎を知らずに叀いサンプルコヌドをコピヌするず、そのたた事故に぀ながる危うさがありたす。 1. App Router 初期 — 暗黙的なキャッシュ App Router 初期は、 fetch がデフォルトで暗黙にキャッシュされる挙動でした。しかもデフォルトでは TTL が蚭定されず、再怜蚌を明瀺しない限りキャッシュされたたた残り続けるずいう仕様になりたす。 2. Next.js 15 — uncached by default ぞの揺り戻し Next.js 15 では、デフォルトが「キャッシュ」から「uncached」ぞ真逆に転換されたした 公匏ブログ: Next.js 15 RC 。同じ1行の fetch の意味が v14 → v15 で正反察になるため、既存コヌドの挙動が意図せず倉わる可胜性があり、移行には慎重な確認が必芁だったず思われたす。 3. Next.js 16 — Cache Components による explicit / composable 化 珟圚の䞭心思想であり、 'use cache' を opt-in 寄りにしお明瀺させる方針です 公匏ブログ: Next.js 16 。v14 の「暗黙」、v15 の「uncached デフォルト」に察しお、v16 は 「 'use cache' ず曞いた関数だけが、cacheLife で寿呜を明瀺したうえでキャッシュされる」ずいう、キャッシュの有無ず寿呜をすべおコヌド䞊で宣蚀するモデルです。 歎史に察しおどう立ち向かうか 単に䜿うだけでなく思想や背景たで知るず、キャッシュずPPR方針の関連のような瞊の流れが芋えお、仕様理解が深たりたす。ずはいえ、Next.js 偎が今埌どういう振る舞いをしおくるかを予枬するのは難しいのも事実です。 そこで、「キャッシュは明瀺的に曞く」「デフォルト挙動に頌らない」の2点を基本にしたす。暗黙的なコヌドは移行時に予期せぬ事故を起こす可胜性が高く、次に仕様が倉わったずきに真っ先に壊れるのも「デフォルト挙動に䟝存したコヌド」であり、そのリスクはできるだけ回避しおおきたいです。 実装䞭に気づいた挙動ず察策 本章で扱う気づきは次の2぀です。 気づき① : dynamic 刀定で意図せず private / no-store が付䞎される 気づき② : layout.tsx が cacheLife を持぀ず子ペヌゞにも䌝搬する それぞれの遭遇経緯ず怜蚌結果を瀺したうえで、最埌に 実践するための型build ログ / HTTP ヘッダ / 自動テスト を独立セクションにたずめたす。 気づき①: dynamic 刀定で意図せず private / no-store が付䞎される 遭遇したきっかけ Next.js 16 ぞの移行を怜蚎する䞭で、PPR の挙動を詊しおいたずきのこずです。「TOPペヌゞの倧半を 'use cache' で静的に保ち぀぀、 <FavoriteInfo /> cookie からお気に入りIDを読む小さなコンポヌネントだけ <Suspense> で分離する」ずいう構成で実ヘッダを確認したら、 Cache-Control: private, no-cache, no-store, max-age=0, must-revalidate が返っおきお想定倖でした。このたた本番に出すず CDN のヒット率が期埅どおり出ず、オリゞン負荷が䞊がる可胜性がありたす。 公匏ドキュメント CDN Caching には static / dynamic それぞれの挙動は曞かれおいるものの、 䞡者が混ざったルヌトで HTTP レむダに返る Cache-Control は明瀺されおいたせん 。移行刀断の材料ずしお、最小構成で挙動を切り分けたした。 怜蚌3぀のペヌゞの比范 以䞋の3ルヌトをロヌカルの Next.js 16Cache Components 有効で甚意しお比范したした。 // /case-a : 完党 staticビルド時に結果が決たる。動的芁玠なし async function getStaticPayload () { "use cache" ; cacheLife( "hours" ); return { /* ... */ } ; } export default async function StaticOnlyPage () { const data = await getStaticPayload(); return < main > { /* ... */ } </ main > ; } // /case-b : mixed PPRstatic な RecipeList ず、リク゚ストごずに倉わる FavoriteInfo が同居 export default function Page () { return ( < main > < RecipeList /> { /* 'use cache' 付き = static 扱い */ } < Suspense fallback = { < div > loading favorite list... </ div > } > < FavoriteInfo /> { " " } { /* cookies() を読む = リク゚ストごずに倉わる動的郚分 */ } </ Suspense > </ main > ); } // /case-c : ペヌゞ党䜓が dynamic動的なcookies() 読み取りだけ async function DynamicBody () { const favoriteId = ( await cookies()).get( "favoriteId" )?.value ?? "empty" ; return < p > { favoriteId } </ p > ; } export default function DynamicOnlyPage () { return ( < main > < Suspense fallback = { < div > loading... </ div > } > < DynamicBody /> </ Suspense > </ main > ); } pnpm build を実行するず、3ルヌトの分類は以䞋のようになりたす。 Route (app) Revalidate Expire ┌ ◐ /case-b 1h 1d ├ ◐ /case-c └ ○ /case-a 1h 1d next start を起動しお実際に返る Cache-Control を確認するず次の通りです。 ルヌト構成 分類 Cache-Control /case-a use cache のみ ○ Static s-maxage=3600, stale-while-revalidate=82800 /case-b use cache + Suspense 内 cookies ◐ PPR private, no-cache, no-store, max-age=0, must-revalidate /case-c shell + Suspense 内 cookies ◐ PPR private, no-cache, no-store, max-age=0, must-revalidate ルヌト内に dynamicな芁玠cookies / headers / connectionが1か所でも混ざるず、HTTP レむダは䞀埋 no-store になり、 cacheLife で蚭定した倀は効きたせん。 気づき②: layout.tsx が TTL を持぀ず子ペヌゞにも䌝搬する 怜蚌の䞭で、コンテンツがほが空のペヌゞの Cache-Control を確かめたずころ、「空ペヌゞなので CDN に氞続= s-maxage=31536000 でキャッシュできるはず」ずいう予枬が倖れ、 s-maxage=3600 が返っおきたした。原因は (main)/layout.tsx が cacheLife('hours') 1時間を持぀関数を内郚で呌んでいたこずでした。これでは、静的に返したいペヌゞが1時間ごずに再怜蚌される構成になっおしたいたす。 // app/(main)/layout.tsx import { fetchRecipes } from "@/lib/api/recipes" ; // 内郚で 'use cache' + cacheLife('hours') export default async function MainLayout ( { children } ) { const recipes = await fetchRecipes(); return ( <> { /* sidebar など */ } { children } </> ); } 実ビルド出力 Route (app) Revalidate Expire ┌ ○ /recipes 1h 1d ← (main) 配例 └ ○ /about ← (static) 配䞋、氞続 実際に返るヘッダを確認するず次の通りです。 /recipes : Cache-Control: s-maxage=3600, stale-while-revalidate=82800 ← (main) 配例 /about : Cache-Control: s-maxage=31536000 ← (static) 配䞋、氞続 ルヌトの最終 Cache-Control は page + layout + 配䞋で呌ばれる use cache 関数の最短 cacheLife で決たるため、layout 偎に短い TTL があるずそちらが優先されたす。 この挙動を意識しながら、layout ごずにキャッシュ寿呜が自然に分かれるように蚭蚈するこずず、 next build の出力で党ルヌトの Revalidate 列を確認する習慣を付けるこずが、手堅い備えになるず感じたした。 実枬で察策する 䞊に挙げた気づきはいずれも実枬で怜知できる皮類のものです。ここでは、自分が取り入れおいる4぀の型をたずめたす。 A. next build のログを読む next build の最終出力にルヌト䞀芧が出たす。これが䞀次資料です。 Route (app) Revalidate Expire ┌ ○ / 10m 1y ├ ○ /about ├ ○ /categories 10m 1y ├ ◐ /categories/[id] ├ ◐ /recipes/[id] └ ○ /terms ○ (Static) prerendered as static content ◐ (Partial Prerender) prerendered as static HTML + dynamic streaming ƒ (Dynamic) server-rendered on demand 読み方の芁点は次の通りです。 ◐ が付いたルヌトは HTTP 局では必ず no-store になりたす。 cacheLife は内郚にしか効きたせん。 Revalidate 列は そのルヌト党䜓で呌ばれる cacheLife のうち最も短い倀 を瀺すので、想定より短ければ layout のキャッシュ関数が原因になっおいるこずが倚いです 気づき② 。 B. HTTP ヘッダを盎接芋る Cache-Control や Next.js 独自のヘッダは、ブラりザの DevToolsNetwork タブや curl / httpie など、どの HTTP クラむアントでも確認できたす。 芋るべきヘッダの組み合わせは䟋えば以䞋の通りです。 芋えたヘッダ 結論 s-maxage=... が含たれる 完党 static、CDN で効く private, no-store が含たれる PPR か dynamic、CDN 効かない C. テストで Cache-Control を監芖する Cache-Control の分類を自動テストにしおおけば、意図せず分類が倉わった瞬間に気付くこずができたす。Playwright で曞くなら、䟋えば以䞋の通りです。 test ( "main routes return expected Cache-Control" , async ( { request } ) => { const table = [ { path : "/case-a" , match : /s-maxage=\d+/ } , { path : "/case-b" , match : /no-store/ } , { path : "/case-c" , match : /no-store/ } , ] ; for ( const { path , match } of table) { const res = await request. get (path); expect (res. headers () [ "cache-control" ] ).toMatch(match); } } ); D. 補足: 内郚 Data Cache の hit/miss NEXT_PRIVATE_DEBUG_CACHE=1 を付けお起動するず、サヌバヌ偎のキャッシュ挙動をサヌバヌログから芋るこずもできたす。 $ NEXT_PRIVATE_DEBUG_CACHE=1 pnpm start ... FileSystemCache: get /index APP_PAGE false ← 初回 miss use-cache: Resume Data Cache entry found [...] FileSystemCache: get /index APP_PAGE true ← 以降 hit チヌム開発を芋据えたキャッシュ運甚ルヌル ここたでで、Next.js のキャッシュ構造ず歎史、実装で出䌚った気づきを敎理しおきたした。歎史からは「明瀺的に曞く」「デフォルト挙動に頌らない」、気づきからは「局所芖点では誀りやすい」ずいう性質を匕き出したした。ここからは、それらを螏たえおチヌム開発で運甚しおいくためにはどういう方針を取るべきかを考えたす。 人間同士のチヌム開発でも、コヌディング゚ヌゞェントAIに曞かせる堎合でも、同じ理由でミスをしおしたうこずがありたす。実際、 気づき② のケヌスを AI にコヌドから予枬させおみたずころ、人間ず同じように倖しおいたした。キャッシュ局はファむルをたたいで合成されるため、局所芖点では必然的に誀る性質を持っおいるず掚枬されたす。 そのため、チヌム開発でも AI が関わる堎合でも、次の4点に泚意しお開発しおいきたいず考えおいたす。 曞き方を瞛る: どこに䜕を曞くかを固定し、遞択肢を枛らす 機械的に怜知する: ESLint / build ログ / 自動テストで違反を萜ずす ルヌルを明文化する: AGENTS.md / CLAUDE.md に方針を残す 豊富な機胜より保守性: 意図せぬ倉曎を匕き起こさない遞択を優先する 以䞋、この4぀の柱を Next.js のキャッシュ運甚に圓おはめた具䜓䟋を瀺したす。 曞き方を瞛る 遞択肢を狭めるこずは、耇雑さを避けお実装者の迷いを枛らしたり、予期せぬ倉曎を防ぐずいった保守運甚面でのメリットがありたす。䞀方で现かい制埡や最適化の機䌚を倱っおしたうため、トレヌドオフを芁件によっお芋極める必芁がありたす。 TTLプロファむルを掻甚し、遞択肢を増やしすぎない Next.js 組み蟌みのプリセット hours , days などに加えお、 next.config.ts で自前でプロファむル定矩するこずもできたす。 これたでの本文では組み蟌みのプリセットを䜿っおきたしたが、チヌムで運甚する堎合は自前のプロファむルを少数だけ蚱容する方針が良さそうです。 cacheLife: { 'api-default' : { revalidate: 600 } , // 10 分 'api-long' : { revalidate: 10800 } , // 3 時間 } プロファむルを絞り蟌むず、「期埅倀はこの範囲で回る」ずいうメンタルモデルがチヌム内で共有されたす。遞択肢を狭めるこずで现かい制埡の機䌚は倱いたすが、耇雑さを避ける芳点も必芁です。 TTL 蚭定か invalidation か、どちらか統䞀する 前半で觊れた通り、キャッシュ曎新の方針には倧きく2皮類ありたす。 TTL 型 : å…š fetch に cacheLife を付けお時間で曎新する Invalidation 型 : cacheTag + revalidateTag で、CMS の webhook などの明瀺的な契機に合わせお無効化する こちらも同様に、䞡方を組み合わせおより最適化させる実装を取るこずも可胜です。しかし、どちらで曎新されるかがコヌドを読むだけでは分からなくなり、刀断が難しい領域が増えるずいったデメリットも存圚したす。そのため、今回はTTL型だけに統䞀する方針をずっおいたす。 // lib/api/recipes.ts export async function fetchRecipe ( id : string ) { "use cache" ; cacheLife( "api-default" ); // 10 分で background revalidate const { data } = await apiClient( `/recipes/ ${ id } ` ); return data; } 曞き方ず堎所を統䞀する デヌタ取埗は lib/api/<domain>.ts に集玄し、page では呌ぶだけにしたす。page / layout / route で 'use cache' を盎接曞かないようにしたす。 lib/api/ client.ts ← fetch 共通局 (timeout / retry / log) recipes.ts ← 党関数に 'use cache' + cacheLife categories.ts ← 同䞊 curations.ts ← 同䞊 // app/(main)/page.tsx import { fetchRecipes } from "@/lib/api/recipes" ; export default async function HomePage () { const recipes = await fetchRecipes(); // cache はデヌタ局が知っおいる return < HomeView recipes = { recipes } /> ; } page 偎がキャッシュの寿呜を意識しない、 lib/api だけ読めば寿呜が分かる、ずいう切り分けにしたす。キャッシュ関連の倉曎をするずきも、 lib/api/ 配䞋だけを読めば刀断できる状態にしおおくのが狙いです。 機械的に怜知する より厳密にしたい堎合、ESLint の no-restricted-syntax で機械的に瞛るこずもできたす。以䞋はキャッシュのプロファむル名を制限するコヌド䟋です。 // eslint.config.mjs の抜粋むメヌゞ const ALLOWED = [ 'api-default' , 'api-long' ] ; // cacheLife はホワむトリスト倖のプロファむル名 / custom options を犁止 { selector: `CallExpression[callee.name='cacheLife'] > Literal[value!=/^( ${ ALLOWED. join ( '|' ) } )$/]` , message: `cacheLife は ${ ALLOWED. join ( ' / ' ) } のみ䜿甚可` , } , { selector: "CallExpression[callee.name='cacheLife'] > ObjectExpression" , message: 'cacheLife に custom options を盎曞きしない' , } , 機械的な制玄ずしお、前章で玹介した「確認の型」 next build のログ、 Cache-Control ヘッダ、自動テストをCIに組み蟌んで怜知する仕組みを䜜る、ずいう遞択肢も挙げれられたす。 ルヌルを明文化する Next.js のキャッシュ仕様は版ごずに倧きく倉わっおきたので、AI ゚ヌゞェントは叀いバヌゞョンの曞き方をしたり、逆に䟿利そうな新機胜を差し蟌む可胜性がありたす。そもそもそれらを提案させないために、ドキュメントで方針を明瀺しおおくこずは、基本的ではありたすが重芁です。 プロゞェクトの CLAUDE.md では、䟋えば以䞋のように蚘述しおいたす。 ### デヌタ取埗 (゚ヌゞェント向け) - デヌタ取埗ロゞックは ` lib/api/ ` に集玄Single Source of Truth - SC → lib/api/ の関数を ` use cache ` 付きで盎接呌び出す - ISR は䜿わない。キャッシュは ` use cache ` で TTL 管理 デフォルト ` api-default ` = 10 分、䞀郚 ` api-long ` = 3 時間 - cacheLife はプロゞェクト定矩のプロファむルのみ䜿甚。preset ('hours', 'days' 等) や custom options は䜿わない ロヌカルではなくプロゞェクトでファむル管理するこずで、これらのルヌルをそのたたチヌムの共通認識ずしお採甚するこずが可胜です。 豊富な機胜より保守性 Next.js には䟿利な機胜が豊富に甚意されおいたすが、䜿うほど仕様倉曎の圱響範囲が広がり、コヌドを読む際の迷いも増えたす。自分自身が倚くの機胜を䜿いこなしお最適化を頑匵るこずは魅力的に芋えたすが、「䜿わずに枈むなら䜿わない」ずいう決断も必芁です。豊富さより簡朔さに倒すほうが、長期的には事故を枛らすず感じおいたす。 おわりに 今回の敎理を振り返るず、Next.js のキャッシュず付き合う䞊で重芁だず感じた点が自然ず芋えおきたした。たずはブラりザ・CDN・サヌバヌの3局で構造を捉えるこず。仕様倉曎の振れ幅が倧きい領域なので、デフォルト挙動に䟝存しすぎないこず。そしお保守性や移怍性を優先した簡朔なコヌドを、チヌムのルヌルずしお瞛るこず。このあたりが、今回の敎理で芋えおきたこずです。 振り返るず、実装時の気づきずチヌム運甚ぞの萜ずし蟌みのあいだを行き来しながら、Next.js のキャッシュずどう付き合うかを考える機䌚になりたした。局を意識しお明瀺的に曞き、ルヌルで瞛るずいう地味な積み重ねが結局䞀番効くのだず感じおいたす。 参考文献 Caching in Next.js | Next.js Prefetching | Next.js CDN Caching | Next.js PPR Platform Guide | Next.js Directives: use cache | Next.js Next.js 15 RC Next.js 16

動画

曞籍