29:47Vercel
Log in to leave a comment
No posts yet
웹 개발자들을 괴롭히던 고질적인 문제가 있습니다. 단 하나의 cookies() 호출이나 헤더 접근 때문에 공들여 만든 정적 페이지 전체가 동적 렌더링으로 강제 전환되는 현상입니다. 기존의 Next.js App Router는 프레임워크가 캐싱을 자동으로 결정하는 암묵적 모델에 의존했습니다. 이 방식은 편리해 보이지만, 개발자가 의도치 않게 전체 컴포넌트 트리의 캐싱 이점을 깨뜨리는 전부 아니면 전무(All-or-Nothing) 상황을 빈번하게 만들었습니다.
Next.js 16은 이러한 이분법적 사고를 완전히 탈피했습니다. 이제 페이지 전체를 정적 또는 동적으로 정의할 필요가 없습니다. 한 페이지 내에서 정교하게 캐싱된 서버 컴포넌트인 빵(Bread)과 실시간 상호작용이 필요한 클라이언트 컴포넌트인 구멍(Hole)이 공존하는 복합 렌더링(Hybrid Rendering) 패러다임이 시작되었습니다. 이 변화를 이해하는 것은 단순히 기술적 호기심을 넘어, 서버 인프라 비용을 절감하고 Lighthouse 점수를 극대화하는 실무적인 열쇠가 됩니다.
Next.js 16의 가장 파격적인 변화는 캐싱이 옵트인(Opt-in) 방식으로 변경되었다는 점입니다. 프레임워크의 판단에 모든 것을 맡기던 시대는 지났습니다. 이제 개발자가 직접 use cache 지시어를 사용하여 함수나 컴포넌트 단위로 캐싱을 명시해야 합니다.
먼저 next.config.ts에서 실험적 기능을 활성화해야 합니다.
// next.config.ts
const nextConfig = {
experimental: {
dynamicIO: true, // 복합 렌더링 및 use cache 활성화
},
}
use cache 지시어는 파일 최상단, 컴포넌트 내부, 혹은 특정 비동기 함수 내부에서도 선언할 수 있습니다. 이를 통해 부분 사전 렌더링(PPR) 효율을 극대화하면 초기 응답 시간(TTFB)을 60~80%까지 단축할 수 있습니다. 과거에는 페이지 전체를 다시 그려야 했던 사소한 데이터 변경도 이제는 특정 캐시 경계 안에서만 처리됩니다.
데이터 페칭 로직은 데이터를 사용하는 컴포넌트와 가장 가까운 곳에 위치해야 합니다. 이를 데이터 콜로케이션(Data Colocation)이라 부릅니다. 상위 레이아웃에서 모든 데이터를 불러와 자식들에게 뿌려주는 방식은 컴포넌트 간 결합도를 높여 유지보수를 지옥으로 만듭니다.
Next.js 16은 React.cache와 use 훅을 결합해 이 문제를 해결합니다. 동일한 렌더링 패스 내에서 중복 요청을 방지하는 Request Memoization 덕분에, 여러 컴포넌트에서 동일한 API를 호출해도 네트워크 요청은 단 한 번만 발생합니다.
이 전략을 잘 활용하면 클라이언트 사이드 자바스크립트 양을 최대 70-80%까지 절감할 수 있습니다. 서버에서 미리 데이터를 처리하고 결과값만 전달하기 때문에 클라이언트는 무거운 로직을 짊어질 필요가 없습니다.
도넛 패턴은 정적인 부분(Donut)과 동적인 부분(Hole)을 명확히 분리하여 합성하는 모델입니다.
use cache가 적용된 서버 컴포넌트입니다. 데이터 페칭과 무거운 로직을 처리하고 결과물을 캐싱합니다.이 패턴의 핵심은 서버 컴포넌트가 클라이언트 컴포넌트를 children 속성으로 받아 렌더링하는 구조에 있습니다. 부모인 서버 컴포넌트가 캐싱되어 있어도 자식인 클라이언트 요소는 독립적인 수명 주기를 가지고 동작합니다.
useState나 useEffect가 필요한 로직을 가장 작은 단위의 클라이언트 컴포넌트로 쪼갭니다.use cache를 선언하고 데이터베이스 쿼리를 수행합니다.children으로 주입받게 만듭니다.Suspense로 감싸 정적 쉘이 즉각 렌더링되도록 만듭니다.use cache를 적용했음에도 페이지가 여전히 느리거나 동적으로 작동한다면 Dynamic API 유출을 의심해야 합니다. cookies()나 headers()가 캐시 경계 내에서 호출되면 해당 범위는 즉시 동적 렌더링으로 전환됩니다. 이 값들을 직접 호출하는 대신 인자로 전달하는 방식으로 구조를 개선해야 합니다.
또한 모든 비동기 데이터 접근은 반드시 Suspense 내부에 있어야 합니다. 그렇지 않으면 프레임워크는 캐싱되지 않은 데이터에 접근했다는 에러를 던지며 정적 생성을 포기합니다.
Next.js 16 아키텍처의 성능 개선 수치는 명확합니다.
| 성능 지표 | 개선 내용 | 기대 효과 |
|---|---|---|
| TTFB (첫 바이트 시간) | PPR 및 use cache 적용 시 60-80% 감소 |
서버 응답 대기 시간의 획기적 단축 |
| TBT (총 차단 시간) | 스크립트 defer 전략으로 메인 스레드 점유 감소 | 사용자 입력 반응성 개선 |
| Build Time (빌드 시간) | Turbopack 적용으로 2-5배 단축 | 개발 생산성 및 배포 속도 향상 |
Vercel 외부 환경(Docker 등)에서 운영한다면 Redis 캐시 어댑터를 활용하는 것이 필수입니다. 이를 통해 수천 대의 서버 인스턴스가 하나의 중앙 캐시 저장소를 공유하며 데이터베이스 부하를 최소화할 수 있습니다.
Next.js 16은 더 이상 개발자에게 정적과 동적 중 하나를 선택하라고 강요하지 않습니다. 이제 아키텍처의 설계 능력은 이 두 세계를 얼마나 정교하게 엮어내느냐에 달려 있습니다.
현명한 개발자라면 먼저 cookies() 남용으로 전체가 동적화된 페이지를 식별하는 것부터 시작해야 합니다. 그 다음 데이터 페칭 로직을 하위 컴포넌트로 이동시켜 독립성을 높이고, use cache와 도넛 패턴을 통해 무거운 라이브러리의 영향을 최소화하십시오. 빌드 리포트에서 페이지가 Static 또는 PPR로 표시되는 것을 확인하는 순간, 당신은 지속 가능한 고성능 서비스의 초석을 다진 것입니다.