Next.js 14와 관련된 새로운 기능과 개념을 잘 이해하고 이를 응용할 수 있는 능력을 키울 수 있었다. 배운 이론을 적절하게 적용할 수 있도록 실제 프로젝트를 해보고, 더 깊이 있는 연구와 실험을 해봐야 할 것 같다.
서버에서만 렌더링되고 클라이언트(브라우저)로 전송되지 않는 컴포넌트로, data fetching을 주로 담당함
axios
나 fetch
등을 사용하여 데이터를 서버에서 요청할 수 있음async
와 await
를 사용하여 비동기 작업을 처리할 수 있음➡️ RSC로 data fetching을 하는 이유
서버에서 데이터를 빠르게 가져와 클라이언트 측에서의 데이터 로딩 시간을 줄여 사용자 경험을 개선할 수 있음
클라이언트 컴포넌트는 브라우저에서 실행되며, 사용자와의 상호작용을 처리함
use client
지시어를 추가해야 함useState
, useEffect
와 같은 React 훅을 사용할 수 있음클라이언트 컴포넌트 밑에 서버 컴포넌트가 있으면 더 이상 서버 컴포넌트로서의 역할을 못 하므로
루트에 서버 컴포넌트를 두고 컴포넌트 트리의 말단으로 클라이언트 컴포넌트를 밀어 넣는 것이 좋음
정말 클라이언트 컴포넌트는 내부에서 서버 컴포넌트가 제 역할을 못하는지 알아보자.
console.log
로 메세지를 찍었을 때 터미널에도 RCC의 메세지가 나옴 -> RCC는 서버에서도 렌더링 된다는 사실을 알 수 있음page.tsx
: 라우트의 경로를 지정할 때 사용하는 페이지
layout.tsx
: Next.js는 각 라우트 경로마다 layout.tsx 파일을 지정하여 공통된 레이아웃을 처리해줄 수 있음
not-found.tsx
: 페이지를 찾을 수 없을 때 보이는 페이지
notFound
함수를 이용해야 함if (params.id === "2") notFound();
error.tsx
: 컴포넌트에서 에러가 발생했을 때 사용자에게 보여주는 커스텀 페이지
error.tsx
컴포넌트를 우선 렌더링함error
와 reset
객체를 props로 받을 수 있음loading.tsx
: 컴포넌트의 로딩 상태를 관리해 주는 페이지
loading.tsx
컴포넌트를 우선 렌더링함loading.tsx
파일이 없으면 서버 컴포넌트 로딩 발생시 화면이 표시되지 않음검색 엔진 최적화, 검색 엔진에서 사이트의 노출도를 올리게 하는 코드 최적화 기법으로 크게 두가지 방법으로 나눌 수 있음
metadata 변수로 정의한 객체를 내보내는 방법
export const metadata = {
title: "Meta Data Welcome!"
}
약속된 함수 generateMetadata()
를 정의해서 내보내는 방법으로 앞으로 더 공부해 볼 것!
Metadata Object and generateMetadata Options
export const metadata: Metadata = {
title: { template: '%s | 수코딩', default: 'Home | 수코딩' },
description: 'Generated by create next app',
};
async function getData() {
await new Promise((resolve) => setTimeout(resolve, 2000));
const res = await fetch('https://jsonplaceholder.typicode.com/posts');
const data = await res.json();
return data;
}
async function getData2() {
await new Promise((resolve) => setTimeout(resolve, 2000));
const res = await fetch('https://jsonplaceholder.typicode.com/posts');
const data = await res.json();
return data;
}
export default async function Home() {
const [data, data2] = await Promise.all([getData(), getData2()]);
return (
<>
{JSON.stringify(data, null, 2)}
<hr />
{JSON.stringify(data2, null, 2)}
</>
);
}
loading.tsx
컴포넌트 상태를 공유함// page.tsx
import Fetching1 from '@/components/Fetching1';
import Fetching2 from '@/components/Fetching2';
export default function Home() {
return (
<>
<Fetching1 />
<Fetching2 />
</>
);
}
// Fetching1.tsx
async function getData() {
await new Promise((resolve) => setTimeout(resolve, 5000));
const res = await fetch('https://jsonplaceholder.typicode.com/posts');
const data = await res.json();
return data;
}
export default async function Fetching1() {
const data = await getData();
return (
<>
<h1>Fetching1</h1>
{JSON.stringify(data, null, 2)}
</>
);
}
import Fetching1 from '@/components/Fetching1';
import Fetching2 from '@/components/Fetching2';
import { Suspense } from 'react';
export default function Home() {
return (
<>
<h1>Home</h1>
<Suspense fallback={<h1>loading1...</h1>}>
<Fetching1 />
</Suspense>
<Suspense fallback={<h1>loading2...</h1>}>
<Fetching2 />
</Suspense>
</>
);
}
자주 사용하는 데이터를 메모리에 저장해놓고 빠르게 가져와서 쓰는 시스템
정확한 캐싱 적용을 위해서 개발자 모드는 끄고 코드 실행하기
Next.js 애플리케이션에서 브라우저 측에서 작동하는 임시 데이터 저장 시스템으로 사용자가 페이지를 이동할 때 더 빠른 내비게이션을 제공할 수 있음
사용자가 웹사이트를 탐색하는 동안 RSC Payload(React Server Component Payload)를 개별 라우트 세그먼트 단위로 나누어 브라우저의 임시 메모리에 저장함
저장된 데이터는 사용자 세션 동안 유지되며, 페이지 이동 시 서버 요청 없이 빠른 로딩을 가능하게 함
다만, 사용자가 웹사이트를 떠나거나 페이지를 새로 고침하면 이 캐시는 삭제됨
📍RSC Payload (React Server Component Payload)
즉, 서버 컴포넌트의 렌더링 결과물을 포함하는 데이터를 의미하며, 주로 서버에서 클라이언트로 전송되는 HTML과 초기화된 상태 데이터를 포함함
<Link>
컴포넌트를 기반으로 예측됨)link
때문이 아니므로 30초가 지나도 변하지 않음)사용자에게 향상된 내비게이션 경험을 제공할 수 있음
캐시는 브라우저의 임시 메모리에 저장되는데 지속 시간은 두 가지 요인에 의해 결정됨
세션:
자동 무효화 기간:
페이지 새로 고침은 모든 캐시된 세그먼트를 지우지만, 자동 무효화 기간은 개별 세그먼트가 미리 가져온 시점부터 적용됩니다.
라우터 캐시를 무효화 하는 방법은 두 가지가 있음
Server Action
revalidatePath
또는 revalidateTag
함수 이용router.refresh
호출
router.refresh
를 호출하면 라우터 캐시가 무효화되고, 현재 사용자가 보고 있는 페이지에 대해 서버로부터 최신 데이터를 다시 요청함<Link>
컴포넌트의 prefetch
속성을 false
로 설정하여 프리패칭을 비활성화할 수 있음장점:
Next.js에서 서버 측에서 작동하는 캐시 시스템으로, HTML과 RSC Payload를 저장함
이는 사용자가 페이지를 요청할 때마다 서버에서 다시 렌더링하지 않고 캐시된 결과를 제공함으로써 빠른 로딩 속도를 제공함
Full Route Cache는 빌드 시 또는 재검증 시 정적으로 렌더링된 라우트를 서버에 캐시하며 이를 통해 서버 요청에 대한 렌더링 비용을 줄이고 성능을 향상시킴
서버에서의 React 렌더링:
서버에서의 캐싱:
클라이언트에서의 React 하이드레이션 및 조정:
클라이언트에서 요청시,
클라이언트에서의 캐싱 (라우터 캐시)
위에서 언급한 라우터 캐시 과정을 통해 RSC Payload는 클라이언트 측 라우터 캐시에 저장됨
이후 내비게이션 시
📍정적 렌더링 vs. 동적 렌더링
라우트가 빌드 시에 캐시되는지 여부는 해당 라우트가 정적으로 렌더링되는지 동적으로 렌더링되는지에 따라 다름
정적 라우트는 기본적으로 캐시되지만, 동적 라우트는 요청 시에 렌더링되며 캐시되지 않음
아래 그림은 정적으로 렌더링된 라우트와 동적으로 렌더링된 라우트 간의 차이를, 캐시된 데이터와 캐시되지 않은 데이터를 통해 보여줌
기본적으로 Full Route Cache는 계속 유지됨
즉, 한 번 렌더링된 페이지 결과가 여러 사용자가 요청할 때마다 계속 사용된다는 뜻임
Full Route Cache를 무효화 하는 방법은 2가지가 있음
Full Route Cache를 사용하지 않도록 설정해서 모든 요청마다 동적으로 컴포넌트를 렌더링 할 수 있음
dynamic = 'force-dynamic'
또는 revalidate = 0
라우트 세그먼트 설정 옵션을 사용하면 Full Route Cache와 Data Cache를 건너뛸 수 있음 React 컴포넌트 트리 내에서 동일한 URL과 옵션으로 fetch 요청을 할 때 자동으로 메모이제이션하는 기능
이를 통해 동일한 데이터를 여러 곳에서 사용할 때 중복된 네트워크 요청을 피할 수 있음
첫 번째 요청:
이후 요청:
클라이언트가 서버에 요청을 보내고 서버가 그 요청에 대해 응답을 반환할 때까지 지속되며, React 컴포넌트 트리가 렌더링을 완료할 때까지 유지됨
Request Memoization은 서버 요청 간에 공유되지 않으므로 재검증이 필요 없음
Next.js에는 가져온 데이터를 서버 요청과 배포 후에도 계속 유지하는 기능이 있음
이 기능은 Next.js가 fetch 기능을 확장하여, 서버에서 요청할 때마다 데이터를 캐시에 저장할 수 있도록 하기 때문에 가능함
기본적으로, fetch를 사용하여 데이터를 요청하면 그 데이터는 캐시되며 fetch의 cache와 next.revalidate 옵션을 사용하여 캐싱 동작을 설정할 수 있음
첫 번째 fetch 요청:
캐시되지 않은 데이터:
{ cache: 'no-store' }
설정)는 항상 데이터 소스에서 가져오고 메모이제이션됨데이터가 캐시되었든 캐시되지 않았든, 동일한 데이터를 반복해서 요청하는 것을 피하기 위해 모든 요청은 메모이제이션됨
📍 Data Cache와 Request Memoization의 차이점
Data Cache와 Request Memoization 둘 다 캐시된 데이터를 재사용하여 성능을 향상시키지만, Data Cache는 여러 요청과 배포에 걸쳐 지속되는 반면, 메모이제이션은 요청 과정 동안(클라이언트가 서버에 요청을 보내고 서버가 그 요청에 대해 응답을 반환할 때까지)만 지속됨
요약하면, Request Memoization은 한 번의 렌더링 과정에서 동일한 데이터를 여러 번 요청하지 않도록 도와주고,
Data Cache는 여러 사용자 요청이나 애플리케이션 배포 후에도 데이터를 캐시에 저장하여 원본 데이터 소스로의 요청을 줄여줌
Data Cache는 기본적으로 계속 유지되며, 재검증하거나 옵트 아웃하지 않는 한 유지됨
// Revalidate at most every hour
fetch('https://...', { next: { revalidate: 3600 } })
revalidateTag
) 또는 경로(revalidatePath
)에 따라 데이터를 재검증할 수 있음cache: 'no-store'
설정으로 캐싱을 비활성화할 수 있음// 개별 `fetch` 요청에 대해 캐싱을 비활성화
fetch(`https://...`, { cache: 'no-store' })
// 라우트 세그먼트 내의 모든 데이터 요청에 대해 캐싱을 비활성화
export const dynamic = 'force-dynamic'
.next.cache
파일지우기next
속성을 추가하여 revalidate 같은 기능을 사용할 수 있게 함Next.js에서는 code splitting를 사용하지 않아도 되나?
——————————————————————————
본 후기는 본 후기는 [유데미x스나이퍼팩토리] 프로젝트 캠프 : Next.js 2기 과정(B-log) 리뷰로 작성 되었습니다.