ã¯ããã« ããã«ã¡ã¯ã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