spinny:~/writing $ less nextjs-16-cache-components.md
12수년간 Next.js에서 가장 짜증나는 질문 중 하나는 "이 페이지가 정적인가요, 아니면 동적인가요?"였습니다. `cookies()`에 대한 호출, 다양한 옵션이 있는 `fetch`, 데이터베이스 클라이언트, CMS, 장바구니 또는 사용자 정의 컨텐츠를 추가하기 전까지는 간단한 질문처럼 보입니다.34Next.js 16은 이 대화를 덜 신비롭게 만들려고 노력한다는 점에서 흥미롭습니다. 복잡성을 제거하지는 않지만 정신 모델을 전환합니다. 경로는 기본적으로 동적이며, 캐시는 필요한 경우 자체적으로 선언되며, `Suspense`은 최신 상태를 유지하는 부분으로 빠른 셸을 구성하는 자연스러운 방법이 됩니다.56이해해야 할 기능은 캐시 구성 요소입니다. 안정적인 Turbopack, React Compiler, `proxy.ts` 및 새로운 무효화 API는 중요하지만 프레임워크가 배후에서 결정한 것을 추측할 필요 없이 빠른 앱을 구축한다는 동일한 문제를 중심으로 진행됩니다.78## 이게 중요하니까910실제 앱에는 "정적 페이지"와 "동적 페이지"만 있는 것이 아닙니다. 요구 사항이 다른 다양한 부품이 있습니다.1112제품 시트는 하루에 몇 번씩 변경될 수 있습니다. 가격은 더 자주 변경될 수 있습니다. 가용성은 거의 실시간이어야 합니다. 사용자 이름은 개인적인 것입니다. 리뷰를 스트리밍할 수 있습니다. 사이드바가 안정적일 수 있습니다. 카트는 그렇지 않습니다.1314모든 것을 하나의 단위로 취급하면 항상 두 가지 극단 중 하나에 이르게 됩니다.1516- 공격적인 캐싱 및 오래된 데이터를 볼 위험이 있습니다.17- 모든 곳에서 동적 렌더링이 이루어지고 성능이 필요 이상으로 저하됩니다.1819캐시 구성 요소는 이러한 잘못된 선택을 방지하는 역할을 합니다.2021## 실제 모델2223`cacheComponents: true`을 사용하면 `"use cache"`을 사용하여 캐시 가능한 항목을 선언할 수 있습니다. 그런 다음 기간과 태그를 `cacheLife()` 및 `cacheTag()`과 연결할 수 있습니다. 동적 부분은 동적으로 유지되며 `Suspense`를 사용하여 격리할 수 있습니다.2425```mermaid26flowchart TD27 Request[사용자 요청] --> Shell[캐시된 쉘]28 Request --> Dynamic[동적 섹션]29 Shell --> FirstPaint[첫 번째 빠른 콘텐츠]30 Dynamic --> Stream[서스펜스 내부 스트리밍]31 Stream --> Complete[전체 페이지]32```3334설정은 작습니다.3536```typescript37// next.config.ts38import type { NextConfig } from 'next';3940const nextConfig: NextConfig = {41 cacheComponents: true,42};4344export default nextConfig;45```4647큰 변화는 구성에 없습니다. 구성 요소 작성을 시작하는 방법에 있습니다.4849```tsx50// app/products/[slug]/page.tsx51import { Suspense } from 'react';52import { cacheLife, cacheTag } from 'next/cache';5354async function getProduct(slug: string) {55 'use cache';5657 cacheLife('hours');58 cacheTag(`product:${slug}`);5960 return db.product.findUnique({ where: { slug } });61}6263async function ProductDetails({ slug }: { slug: string }) {64 const product = await getProduct(slug);6566 return (67 <section>68 <h1>{product.name}</h1>69 <p>{product.description}</p>70 </section>71 );72}7374async function LiveInventory({ slug }: { slug: string }) {75 const inventory = await db.inventory.findFirst({ where: { slug } });76 return <p>{inventory.quantity} pezzi disponibili</p>;77}7879export default async function ProductPage({ params }: { params: Promise<{ slug: string }> }) {80 const { slug } = await params;8182 return (83 <>84 <ProductDetails slug={slug} />85 <Suspense fallback={<p>Controllo disponibilità...</p>}>86 <LiveInventory slug={slug} />87 </Suspense>88 </>89 );90}91```9293페이지가 모두 캐시되거나 모두 동적일 필요는 없습니다. 제품 시트는 빠르고 재사용이 가능합니다. 재고를 최신 상태로 유지할 수 있습니다. 사용자는 가장 느린 부분을 기다리지 않고 바로 무언가를 보게 됩니다.9495## `use cache`은 실행 가능한 문서입니다.9697`"use cache"`에 대해 내가 좋아하는 점은 의도를 명시적으로 나타내도록 강요한다는 것입니다. 함수를 읽으면 누군가가 "이 데이터를 재사용할 수 있다"고 결정했다는 것을 즉시 이해할 수 있습니다.9899`fetch`을 사용하지 않을 때 특히 유용합니다. 많은 앱이 Prisma, Drizzle, 내부 SDK, CMS 클라이언트 또는 서비스 기능에서 데이터를 읽습니다. 이러한 경우 `fetch` 옵션에만 기반한 기존 추론으로는 충분하지 않았습니다.100101경험 법칙:102103- 캐시아 함량은 상대적으로 안정적입니다.104- 세분화된 태그를 사용합니다.105- 동적 권한, 세션, 장바구니, 알림 및 거래 상태를 유지합니다.106- `Suspense` 안에 느린 부분을 넣으세요;107- “성능을 향상시켰다”라고 말하기 전에 먼저 측정해 보세요.108109## 모든 것을 버리지 않고 무효화110111캐시는 정확하게 업데이트할 수 있는 경우에만 유용합니다. 여기서 `cacheTag`, `revalidateTag` 및 `updateTag`가 중요해집니다.112113예:114115```typescript116'use server';117118import { updateTag } from 'next/cache';119120export async function updateProductName(productId: string, name: string) {121 await db.product.update({122 where: { id: productId },123 data: { name },124 });125126 updateTag(`product:${productId}`);127}128```129130중요한 세부 사항은 태그입니다. `product:${productId}`은 정확한 경계를 알려줍니다. `products`은 거대한 양동이를 알려줍니다. 처음에는 거대한 양동이가 편안합니다. 몇 달 후에는 제목을 변경하기 위해 앱 절반을 무효화하는 이유가 됩니다.131132## 안정적인 터보팩: 매일 듣는 부분133134Next.js 16은 Turbopack을 개발 및 빌드의 중심으로 가져왔습니다. 가장 시적인 기능은 아니지만 작업하는 동안 느끼는 기능입니다. 더 일찍 시작되는 서버, 더 빠른 새로 고침, 강제로 커피를 마시는 듯한 느낌을 멈추는 빌드입니다.135136그렇긴 하지만, 나는 눈을 감고 커스텀 플러그인으로 가득 찬 코드베이스를 마이그레이션하지 않을 것입니다. 나는 다음을 확인하겠다:137138- 로컬 빌드;139- 비표준 수입;140- MDX, SVG 및 CSS;141- Webpack 플러그인이 남았습니다.142- 중요한 페이지;143- 빌드 시간의 차이.144145새 프로젝트의 경우 기본값부터 시작합니다. 성숙한 경우에는 측정된 마이그레이션을 수행합니다.146147## React Compiler: 생각이 아닌 노이즈 제거148149React Compiler 1.0은 안정적이며 Next.js 16은 `reactCompiler`을 통해 이를 지원합니다. 약속은 수동 메모를 많이 줄이는 것입니다. "안전을 위해" 사용되는 `memo`, `useMemo`, `useCallback`을 줄입니다.150151```typescript152// next.config.ts153import type { NextConfig } from 'next';154155const nextConfig: NextConfig = {156 reactCompiler: true,157};158159export default nextConfig;160```161162나는 그것을 마법의 가루처럼 취급하지 않을 것입니다. 컴파일러는 코드가 React 규칙을 잘 따를 때 도움이 됩니다. 구성 요소에 이상한 부작용, 숨겨진 돌연변이 또는 잘못 사용된 후크가 있는 경우 이를 먼저 수정해야 합니다.163164그것을 시도하는 건강한 방법:1651661. `eslint-plugin-react-hooks` 업데이트;1672. 실제 위반 사항을 수정합니다.1683. 통제된 구역에서 활성화하십시오.1694. 빌드 시간과 동작을 측정합니다.1705. 더 이상 필요하지 않은 경우에만 수동 메모를 제거하십시오.171172목표는 `useMemo`마다 삭제하는 것이 아닙니다. 렌더링이 두려워 예방 메모 작성을 중단하는 것이 목표입니다.173174## `proxy.ts` 및 네트워크 경계175176이전 `middleware.ts`은(는) `proxy.ts`이 됩니다. 이름이 변경되었지만 의미가 있습니다. 해당 파일은 요청 경계에 있고 전통적인 백엔드 스타일의 일반 미들웨어가 아닙니다.177178```typescript179// proxy.ts180import { NextRequest, NextResponse } from 'next/server';181182export default function proxy(request: NextRequest) {183 const isLoggedIn = Boolean(request.cookies.get('session'));184185 if (!isLoggedIn && request.nextUrl.pathname.startsWith('/dashboard')) {186 return NextResponse.redirect(new URL('/login', request.url));187 }188189 return NextResponse.next();190}191```192193여기서의 규칙은 간단합니다. 작게 유지하는 것입니다. 리디렉션, 인증 라우팅, 헤더, 필수 재작성. 두 번째 백엔드처럼 느껴지기 시작하면 아마도 너무 많은 일을 하고 있는 것일 수 있습니다.194195## 실제로 마이그레이션하는 방법196197모든 기능을 한 번에 활성화하지는 않습니다. 나는 이렇게 할 것이다:1981991. Next, React 및 React DOM을 업데이트합니다.2002. 공식 codemod를 시작합니다.2013. `params`, `searchParams`, `cookies()`, `headers()` 및 `draftMode()`에 대한 주요 변경 사항을 수정합니다.2024. `middleware.ts`을(를) `proxy.ts`(으)로 마이그레이션합니다.2035. 빌드와 중요 페이지를 확인합니다.2046. 현재 캐시가 마찰을 일으키는 섹션에서 캐시 구성 요소를 활성화합니다.2057. 나는 태그와 무효화에 대한 규칙을 정의합니다.2068. 저는 React Compiler를 별도로 사용해 봅니다.2079. 측정 전과 후의 비교.208209좋은 마이그레이션은 새로운 기능을 모두 사용하는 것이 아닙니다. 이것이 앱의 동작을 더 읽기 쉽게 만드는 것입니다.210211## 생각의 방식이 바뀌는 것212213Next.js 16의 가장 유용한 점은 의도의 이름을 더 잘 지정하도록 한다는 것입니다. 기능은 단지 "데이터베이스에서 제품을 가져오는 것"이 아닙니다. "제품을 구입하고 몇 시간 동안 캐시할 수 있으며 이 태그로 무효화합니다"입니다. 구성 요소는 단순한 "페이지 렌더링"이 아닙니다. "이것은 빠른 셸이고 이 작품은 개인적인 것이며 스트리밍으로 제공됩니다."214215처음에는 더 많은 일처럼 보입니다. 그러면 그것은 평온한 형태가 됩니다. 성능 결정은 더 이상 기본값, 경험적 방법, 부족 기억의 조합에 숨겨져 있지 않습니다. 그것들은 코드에 있습니다.216217## 유용한 소스218219- [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
:Next.js 16, 캐시 구성 요소 및 React 컴파일러: 실제로 변경되는 사항lines 1-223 (END) — press q to close