spinny:~/writing $ vim nextjs-16-cache-components.md
1~2수년간 Next.js에서 가장 짜증나는 질문 중 하나는 "이 페이지가 정적인가요, 아니면 동적인가요?"였습니다. `cookies()`에 대한 호출, 다양한 옵션이 있는 `fetch`, 데이터베이스 클라이언트, CMS, 장바구니 또는 사용자 정의 컨텐츠를 추가하기 전까지는 간단한 질문처럼 보입니다.3~4Next.js 16은 이 대화를 덜 신비롭게 만들려고 노력한다는 점에서 흥미롭습니다. 복잡성을 제거하지는 않지만 정신 모델을 전환합니다. 경로는 기본적으로 동적이며, 캐시는 필요한 경우 자체적으로 선언되며, `Suspense`은 최신 상태를 유지하는 부분으로 빠른 셸을 구성하는 자연스러운 방법이 됩니다.5~6이해해야 할 기능은 캐시 구성 요소입니다. 안정적인 Turbopack, React Compiler, `proxy.ts` 및 새로운 무효화 API는 중요하지만 프레임워크가 배후에서 결정한 것을 추측할 필요 없이 빠른 앱을 구축한다는 동일한 문제를 중심으로 진행됩니다.7~8## 이게 중요하니까9~10실제 앱에는 "정적 페이지"와 "동적 페이지"만 있는 것이 아닙니다. 요구 사항이 다른 다양한 부품이 있습니다.11~12제품 시트는 하루에 몇 번씩 변경될 수 있습니다. 가격은 더 자주 변경될 수 있습니다. 가용성은 거의 실시간이어야 합니다. 사용자 이름은 개인적인 것입니다. 리뷰를 스트리밍할 수 있습니다. 사이드바가 안정적일 수 있습니다. 카트는 그렇지 않습니다.13~14모든 것을 하나의 단위로 취급하면 항상 두 가지 극단 중 하나에 이르게 됩니다.15~16- 공격적인 캐싱 및 오래된 데이터를 볼 위험이 있습니다.17- 모든 곳에서 동적 렌더링이 이루어지고 성능이 필요 이상으로 저하됩니다.18~19캐시 구성 요소는 이러한 잘못된 선택을 방지하는 역할을 합니다.20~21## 실제 모델22~23`cacheComponents: true`을 사용하면 `"use cache"`을 사용하여 캐시 가능한 항목을 선언할 수 있습니다. 그런 다음 기간과 태그를 `cacheLife()` 및 `cacheTag()`과 연결할 수 있습니다. 동적 부분은 동적으로 유지되며 `Suspense`를 사용하여 격리할 수 있습니다.24~25```mermaid26flowchart TD27 Request[사용자 요청] --> Shell[캐시된 쉘]28 Request --> Dynamic[동적 섹션]29 Shell --> FirstPaint[첫 번째 빠른 콘텐츠]30 Dynamic --> Stream[서스펜스 내부 스트리밍]31 Stream --> Complete[전체 페이지]32```33~34설정은 작습니다.35~36```typescript37// next.config.ts38import type { NextConfig } from 'next';39~40const nextConfig: NextConfig = {41 cacheComponents: true,42};43~44export default nextConfig;45```46~47큰 변화는 구성에 없습니다. 구성 요소 작성을 시작하는 방법에 있습니다.48~49```tsx50// app/products/[slug]/page.tsx51import { Suspense } from 'react';52import { cacheLife, cacheTag } from 'next/cache';53~54async function getProduct(slug: string) {55 'use cache';56~57 cacheLife('hours');58 cacheTag(`product:${slug}`);59~60 return db.product.findUnique({ where: { slug } });61}62~63async function ProductDetails({ slug }: { slug: string }) {64 const product = await getProduct(slug);65~66 return (67 <section>68 <h1>{product.name}</h1>69 <p>{product.description}</p>70 </section>71 );72}73~74async function LiveInventory({ slug }: { slug: string }) {75 const inventory = await db.inventory.findFirst({ where: { slug } });76 return <p>{inventory.quantity} pezzi disponibili</p>;77}78~79export default async function ProductPage({ params }: { params: Promise<{ slug: string }> }) {80 const { slug } = await params;81~82 return (83 <>84 <ProductDetails slug={slug} />85 <Suspense fallback={<p>Controllo disponibilità...</p>}>86 <LiveInventory slug={slug} />87 </Suspense>88 </>89 );90}91```92~93페이지가 모두 캐시되거나 모두 동적일 필요는 없습니다. 제품 시트는 빠르고 재사용이 가능합니다. 재고를 최신 상태로 유지할 수 있습니다. 사용자는 가장 느린 부분을 기다리지 않고 바로 무언가를 보게 됩니다.94~95## `use cache`은 실행 가능한 문서입니다.96~97`"use cache"`에 대해 내가 좋아하는 점은 의도를 명시적으로 나타내도록 강요한다는 것입니다. 함수를 읽으면 누군가가 "이 데이터를 재사용할 수 있다"고 결정했다는 것을 즉시 이해할 수 있습니다.98~99`fetch`을 사용하지 않을 때 특히 유용합니다. 많은 앱이 Prisma, Drizzle, 내부 SDK, CMS 클라이언트 또는 서비스 기능에서 데이터를 읽습니다. 이러한 경우 `fetch` 옵션에만 기반한 기존 추론으로는 충분하지 않았습니다.100~101경험 법칙:102~103- 캐시아 함량은 상대적으로 안정적입니다.104- 세분화된 태그를 사용합니다.105- 동적 권한, 세션, 장바구니, 알림 및 거래 상태를 유지합니다.106- `Suspense` 안에 느린 부분을 넣으세요;107- “성능을 향상시켰다”라고 말하기 전에 먼저 측정해 보세요.108~109## 모든 것을 버리지 않고 무효화110~111캐시는 정확하게 업데이트할 수 있는 경우에만 유용합니다. 여기서 `cacheTag`, `revalidateTag` 및 `updateTag`가 중요해집니다.112~113예:114~115```typescript116'use server';117~118import { updateTag } from 'next/cache';119~120export async function updateProductName(productId: string, name: string) {121 await db.product.update({122 where: { id: productId },123 data: { name },124 });125~126 updateTag(`product:${productId}`);127}128```129~130중요한 세부 사항은 태그입니다. `product:${productId}`은 정확한 경계를 알려줍니다. `products`은 거대한 양동이를 알려줍니다. 처음에는 거대한 양동이가 편안합니다. 몇 달 후에는 제목을 변경하기 위해 앱 절반을 무효화하는 이유가 됩니다.131~132## 안정적인 터보팩: 매일 듣는 부분133~134Next.js 16은 Turbopack을 개발 및 빌드의 중심으로 가져왔습니다. 가장 시적인 기능은 아니지만 작업하는 동안 느끼는 기능입니다. 더 일찍 시작되는 서버, 더 빠른 새로 고침, 강제로 커피를 마시는 듯한 느낌을 멈추는 빌드입니다.135~136그렇긴 하지만, 나는 눈을 감고 커스텀 플러그인으로 가득 찬 코드베이스를 마이그레이션하지 않을 것입니다. 나는 다음을 확인하겠다:137~138- 로컬 빌드;139- 비표준 수입;140- MDX, SVG 및 CSS;141- Webpack 플러그인이 남았습니다.142- 중요한 페이지;143- 빌드 시간의 차이.144~145새 프로젝트의 경우 기본값부터 시작합니다. 성숙한 경우에는 측정된 마이그레이션을 수행합니다.146~147## React Compiler: 생각이 아닌 노이즈 제거148~149React Compiler 1.0은 안정적이며 Next.js 16은 `reactCompiler`을 통해 이를 지원합니다. 약속은 수동 메모를 많이 줄이는 것입니다. "안전을 위해" 사용되는 `memo`, `useMemo`, `useCallback`을 줄입니다.150~151```typescript152// next.config.ts153import type { NextConfig } from 'next';154~155const nextConfig: NextConfig = {156 reactCompiler: true,157};158~159export default nextConfig;160```161~162나는 그것을 마법의 가루처럼 취급하지 않을 것입니다. 컴파일러는 코드가 React 규칙을 잘 따를 때 도움이 됩니다. 구성 요소에 이상한 부작용, 숨겨진 돌연변이 또는 잘못 사용된 후크가 있는 경우 이를 먼저 수정해야 합니다.163~164그것을 시도하는 건강한 방법:165~1661. `eslint-plugin-react-hooks` 업데이트;1672. 실제 위반 사항을 수정합니다.1683. 통제된 구역에서 활성화하십시오.1694. 빌드 시간과 동작을 측정합니다.1705. 더 이상 필요하지 않은 경우에만 수동 메모를 제거하십시오.171~172목표는 `useMemo`마다 삭제하는 것이 아닙니다. 렌더링이 두려워 예방 메모 작성을 중단하는 것이 목표입니다.173~174## `proxy.ts` 및 네트워크 경계175~176이전 `middleware.ts`은(는) `proxy.ts`이 됩니다. 이름이 변경되었지만 의미가 있습니다. 해당 파일은 요청 경계에 있고 전통적인 백엔드 스타일의 일반 미들웨어가 아닙니다.177~178```typescript179// proxy.ts180import { NextRequest, NextResponse } from 'next/server';181~182export default function proxy(request: NextRequest) {183 const isLoggedIn = Boolean(request.cookies.get('session'));184~185 if (!isLoggedIn && request.nextUrl.pathname.startsWith('/dashboard')) {186 return NextResponse.redirect(new URL('/login', request.url));187 }188~189 return NextResponse.next();190}191```192~193여기서의 규칙은 간단합니다. 작게 유지하는 것입니다. 리디렉션, 인증 라우팅, 헤더, 필수 재작성. 두 번째 백엔드처럼 느껴지기 시작하면 아마도 너무 많은 일을 하고 있는 것일 수 있습니다.194~195## 실제로 마이그레이션하는 방법196~197모든 기능을 한 번에 활성화하지는 않습니다. 나는 이렇게 할 것이다:198~1991. Next, React 및 React DOM을 업데이트합니다.2002. 공식 codemod를 시작합니다.2013. `params`, `searchParams`, `cookies()`, `headers()` 및 `draftMode()`에 대한 주요 변경 사항을 수정합니다.2024. `middleware.ts`을(를) `proxy.ts`(으)로 마이그레이션합니다.2035. 빌드와 중요 페이지를 확인합니다.2046. 현재 캐시가 마찰을 일으키는 섹션에서 캐시 구성 요소를 활성화합니다.2057. 나는 태그와 무효화에 대한 규칙을 정의합니다.2068. 저는 React Compiler를 별도로 사용해 봅니다.2079. 측정 전과 후의 비교.208~209좋은 마이그레이션은 새로운 기능을 모두 사용하는 것이 아닙니다. 이것이 앱의 동작을 더 읽기 쉽게 만드는 것입니다.210~211## 생각의 방식이 바뀌는 것212~213Next.js 16의 가장 유용한 점은 의도의 이름을 더 잘 지정하도록 한다는 것입니다. 기능은 단지 "데이터베이스에서 제품을 가져오는 것"이 아닙니다. "제품을 구입하고 몇 시간 동안 캐시할 수 있으며 이 태그로 무효화합니다"입니다. 구성 요소는 단순한 "페이지 렌더링"이 아닙니다. "이것은 빠른 셸이고 이 작품은 개인적인 것이며 스트리밍으로 제공됩니다."214~215처음에는 더 많은 일처럼 보입니다. 그러면 그것은 평온한 형태가 됩니다. 성능 결정은 더 이상 기본값, 경험적 방법, 부족 기억의 조합에 숨겨져 있지 않습니다. 그것들은 코드에 있습니다.216~217## 유용한 소스218~219- [Next.js 16 릴리스 노트](https://nextjs.org/blog/next-16)220- [캐시 구성 요소 - Next.js 문서](https://nextjs.org/docs/app/getting-started/cache-components)221- [캐시 사용 - Next.js 문서](https://nextjs.org/docs/app/api-reference/directives/use-cache)222- [리액트 컴파일러 v1.0](https://react.dev/blog/2025/10/07/react-compiler-1)223~
NORMAL · nextjs-16-cache-components.md [readonly]223 lines · :q to close