1. 탐구
1. Server Side Rendering
: 요청이 올 때마다 해당하는 HTML문서를 생성하여 반환
2. Server Static Generation
: 빌드 타임에 각 페이지별로 HTML을 생성하고, 해당 페이지로 요청이 올 때 이미 static하게 생성된 HTML문서를 반환
2. 고민
Gloddy프로젝트에서 정적 페이지 생성(SSG)은 불가능할까?
SSG는 빌드 타임 때 페이지를 정적으로 만드는 것이다. 로그인 이후의, 쿠키를 이용하여 데이터 fetching이 필요한 페이지는 SSG사용이 불가능하다.
Next.js 12 page routing에서 ISR은 어떻게 구현할까?
Next.js 12의 경우, getStaticProps의 revalidate옵션을 설정하면 된다.
export async function getStaticProps() {
const Next.js 12에서의 ISRres = await fetch("https://url");
const posts = await res.json();
return {
props: {
posts,
},
revalidate: 10,
};
}
만약 10으로 설정하면, 10초마다 페이지를 새로 만든다. 또는 handler를 사용하여 서버로부터 받은 응답에 따라 렌더링을 재실행할 수 있다.
export default async function handler(req, res) {
// 유효한 요청인지 확인한다.
if (req.query.secret !== process.env.MY_SECRET_TOKEN) {
return res.status(401).json({ message: "Invalid token" });
}
try {
// API 핸들러 내부에서 res.revalidate()를 사용해 원하는 상황에서 revalidate를 실행시킨다.
// 재작성된 경로가 아닌 실제 경로여야 한다.
// 예: "/blog/[slug]"의 경우 "/blog/post-1"로 작성해야 한다.
await res.revalidate("/path-to-revalidate");
return res.json({ revalidated: true });
} catch (err) {
// 에러가 발생하면 Next.js는 이전에 성공적으로 생성된 페이지를 계속 보여주게 된다.
return res.status(500).send("Error revalidating");
}
}
Next.js 13에서는 SSR 혹은 ISR을 어떻게 구현할까?
Next.js12에서 사용하던 getStaticProps 함수나 getServerSideProps함수가 필요 없어졌다. fetch함수를 사용하면 된다.
- Next.js extends the native
fetchWeb API to allow you to configure the caching and revalidating behavior for each fetch request on the server. React extendsfetchto automatically memoize fetch requests while rendering a React component tree.서버 측에서 데이터 캐싱과 갱신을 위해서 Web API인 fetch를 확장했다. React는
fetch를 사용하여 자동으로 fetch 요청을 저장한다.
cache옵션을 이용하여 캐싱을(SSR) 설정할 수 있고, revalidate(ISR) 또한 설정할 수 있다.
fetch(URL, {next:{revalidate:10}}; // ISR
fetch(URL, {cache:'force-cache'}; // SSG(default)
fetch(URL, {cache:'no-store'}; // SSR
axios를 이용해서는 SSG, ISR 구현이 불가능한 것일까?
현재 우리 프로젝트는 axios를 사용하고 있다. axios + react-query Hydration을 통해 서버 단에서 페이지를 미리 fetching하는 것은 구현을 했다. 하지만, axios는 cache기능을 따로 제공하지 않는다. Next.js에서도 fetch사용을 권장하고 있다. Next.js Discussion의 How to use Axios in Next 13의 코멘트들을 읽어보아도, 캐싱과 revalidating은 fetch만 지원한다고 한다. 사실 처음에 axios라이브러리를 사용하면서부터 나왔던 토론 내용이었는데, 그 때는 사용하기 편하고 익숙한 axios를 사용한 것이 이런 파장을 불러 일으킨 듯하다.
fetch로 마이그레이션을 해보고, 성능 비교를 해보자
SSR은 페이지에 접속할 때마다 서버에서 렌더링을 해서 클라이언트에 내려주기에 속도가 SSG보다 느릴 수밖에 없다. SSG로 마이그레이션을 해보자. fetch기반의 axios처럼 다양한 기능을 제공하는 ky를 사용할 것이다.
기존 문제점
기존에 사용하던 axios로는 Next.js에서 캐싱 기능을 지원하지 않아 SSG를 구현할 수 없었다.
이를 개선하고자 fetch 기반의 ky라이브러리로 마이그레이션 하고자한다. ky는 fetch를 axios처럼 baseUrl, headers 등을 설정할 수 있는 인스턴스를 만드는 등 다양한 기능을 제공하는 라이브러리이다.
ky Instance
export const kyApi = ky.create({
prefixUrl: `${BASE_API_URL}/api/v1`,
hooks: {
beforeRequest: [
async (request) => {
const token = await getTokenFromCookie();
if (token.accessToken) {
request.headers.set('X-AUTH-TOKEN', token.accessToken);
}
},
],
},
});
react-query에서 ky사용하기
react-query에서도 물론 ky를 사용할 수 있다. 기존 axios 패칭하던 것을 ky로 교체만 하면 된다.
react-query의 캐싱된 데이터와, 실제로 백엔드 서버에 요청한 것을 어떻게 구분할까?
개발 환경에서는 새로고침을 하면 데이터를 한 번 받아온다.
Next.js는 최초 페이지는 SSR을, 그 이후로는 CSR 방식으로 동작하는 하이브리드 프레임워크이다. 그런데 위 영상에서는 새로고침 했을 때 최초 페이지에서도 데이터 fetching이 네트워크에 찍히는 것을 확인할 수 있다.
<QueryAsyncBoundary rejectedFallback={RejectedFallback}>
<GroupingCardList />
</QueryAsyncBoundary>
서버 측에서 렌더링을 먼저 하는 QueryAsyncBoundary에 pendingFallback를 넘겨주지 않았기 때문이다. 첫 페이지의 경우, 무한 렌더링을 구현했는데 무한 렌더링의 첫 페이지만 SSR로 구현을 하니 에러가 발생하여 이 페이지는 SSR을 제외했다.
Next.js 13은 기본적으로 SSR이 작동하지 않고 React Server Component로만 동작하나?
Next.js 13이 업데이트 되며, React18에서 추가된 RSC를 제공한다. RSC는 SSR에서 페이지 단위로 렌더링된 HTML을 브라우저에 전송하던 방식과 달리, 컴포넌트 단위로 RSC라면 서버에서 렌더링을 하여서 JSON으로 직렬화하여 전송한다. 그리고 RCC는 렌더링하지 않고 브라우저에서 렌더링을 진행한다. 이러한 RSC는 서버에서만 필요한 데이터를 취급하고, 클라이언트 측 JS 번들 크기를 줄이는데 유용하다.
즉, Next.js13은 SSR과 RSC모두 지원한다.
Next.js 13에서 SSR과 SSG는 어떻게 구현하나?
Next.js 12에서는 getServerSideProps과 getStaticProps, getStaticPaths를 사용하여 SSR, SSG를 구현하던 방식과 달리 Next.js 13 에서는 위와같은 함수를 제공하지 않는다. 오로지 fetch를 이용하여 구현하다. 서버 컴포넌트내부에서 비동기적으로 fetch문을 실행하면 된다. fetch의 cache옵션을 끄면 SSR, 키면 SSG로 구현이 된다.
만약 페이지 단위로 서버에서 받아와 렌더링을 한다면 이는 SSR, 그리고 컴포넌트 단위로 서버에서 렌더링을 한다면 RSC인 것이다.
그럼, 우리 프로젝트에서 SSR과 RSC는 어떻게 실행이 되고 있을까?
오른쪽 돔들은 모두 HTML로 서버에서 넘어온 것이다. 즉, SSR로 구현이 된 것이다.
Header와 Footer만 서버 측에서 렌더링이 되었다.
그리고, 서버로부터 온 grouping문서를 더 살펴보면, 밑에 <script>self.__next_f.push()</script>가 많다. 이것이 바로, 서버에서 RSC같은 경우 렌더링을 한 후, JSON형태로 직렬화를 하여 브라우저에 넘겨준 것이다.
이 페이지는 RSC로 서버에서 미리 받아온 데이터가 없다. 이 페이지는 무한 스크롤이여서 다른 페이지와 구현 방식이 약간 다른데 거기서 발생한 이슈인 듯하다.
나의 모임 페이지 같은 경우 서버로부터 받은 데이터가 직렬화하여 담겨있다.
즉, RSC로 서버 측에서 데이터를 미리 받아서, 렌더링을 하고 있다는 것이다.
정리
정리하자면, 우리 프로젝트에서 현재 사용하고 있는 방식은 SSR + RSC이며(기본적으로 next.js 13에서는 이 둘을 지원한다) header, footer는 SSR로 서버에서 렌더링, 데이터 패칭이 필요한 컴포넌트는 RSC로 서버에서 렌더링하여 JSON형식으로 브라우저에 보내주고 있다. 이것이 react 18에서 제공이되는 Streaming기능이다.
3. 공식문서 : Next.js
Streaming
Streaming은 서버로부터 점진적으로 UI를 렌더링할 수 있습니다. 청크 단위로 쪼개지며, 준비가 되면 클라이언트로 stream됩니다. 전체 내용이 렌더링 되기 전에 사용자가 즉각적으로 페이지를 볼 수 있도록 합니다. Streaming은 App Router에 기본으로 설정되어 있습니다. 초기 페이지를 빠른 성능을 보여줍니다. 특히, 상품 페이지 같은 느린 데이터 패칭에 의존하고 있는 UI도요. loading.js나 Suspense 컴포넌트를 사용하여 Streaming 초기 페이지를 구현할 수 있습니다.
Loading UI and Streaming
loading.js는 React Suspense를 이용하여 로딩을 더 의미 있게 구현해줍니다. 페이지의 컴포넌트가 렌더링되는 동안 즉각적인 로딩 화면을 보여줄 수 있습니다. 새로운 컨텐츠는 자동으로 렌더링이 완료되기 전에 교체됩니다.당신은 skeloton이나 spinner같은 로딩 표시를 미리 렌더링할 수 있습니다.
Next.js가 최적화를 해두었으니, loading.js 사용을 권장합니다.
loading.js가 아니라, 수동으로 Suspense Boundary를 만들어도 됩니다. App Router는 Suspense를 이용한 streaming방식을 지원합니다.
Streaming이 뭔가요?
Streaming을 이해하기 위해서는, SSR과 이의 한계에 대해 이해해야 합니다. SSR은 페이지에 주어진 모든 데이터 패칭을 서버에서 완료하고, HTML로 렌더링 하고, 그 때 되서야 HTML, CSS, JS를 브라우저에 보내줍니다. 사용자에게 보여주고, 그 다음 Hydration을 이용하여 JS를 연결(유저와 상호작용 등)합니다.
이 과정은 순차적이고(페이지 단위로 렌더링이 서버에서 일어나고, 그 이후 전송됨), block됩니다(유저와 상호작용을 즉시 못함). React와 Next의 SSR은 상호작용이 없는 UI를 먼저 보여줌으로써 로딩 퍼포먼스를 향상시킵니다. 하지만, 이것은 모든 데이터가 서버에서 fetch할 때까지 기다려야하고, 이것이 완료 되어야 사용자에게 보여집니다.
Streaming은 페이지의 HTML을 작은 청크 당뉘로 쪼개고, 점진적으로 브라우저에 보내줍니다. 모든 데이터가 로드 되어, 렌더링 될 때까지 기다리게 하지 않습니다. Streaming은 React Component 모델과 함께 동작합니다. 각 컴포넌트는 청크로 취급되기 때문입니다. 더 높은 우선순위를 가진 컴포넌트나 데이터에 의존하지 않는 컴포넌트(layout 등)은 먼저 보내집니다. 그리고, React는 hydration을 먼저 시작합니다.
낮은 우선순위를 가진 컴포넌트는 데이터를 fetch하고 서버로부터 뒤늦게 보내집니다.
1번 : 먼저 클라이언트에 보내져서, 먼저 클라이언트에서 로딩되고, Hydration을 수행함
2,3번 : 청크 단위(컴포넌트 단위)로 쪼개어져서 서버에서 data가 fetch되는대로 클라이언트에 보내지고, Hydration을 수행함
- 예시
- Suspense로 감싸진, 비동기 동작을 수행하는 컴포넌트는 비동기 동작을 수행하는 동안 fallback UI를 보여줍니다. 그리고, 동작이 완료되면 컴포넌트를 교체합니다.
- 장점
- Streaming Server Rendering : 점진적으로 렌더링된 HTML을 서버에서 클라이언트로 보내줍니다.
- Selective HJydration : 사용자와 상호작용이 있는 컴포넌트를 먼저 Hydration을 수행합니다.
정리
- SSR은 페이지 단위로 일어나고, 모든 데이터 fetching과 렌더링이 완료되어야 클라이언트에 보내지고, 그 때 되서야 hydration이 일어난다. 즉, 순차적이다. 하지만, next.js 13의 주요 기능인 Streaming방식은 청크 단위로 data를 fetch하고 클라이언트에 전송한다. 그리고 먼저 hydration을 수행한다. 그리고, 사용자와 상호작용이 있다면 먼저 선택적으로 hydration을 수행한다.
- 그러한 점에서 SSR과 Streaming(RSC, RCC)와 차이점이 있다.
Server Compoents
RSC는 UI를 그리고 조건적에 따라 서버에서 캐싱을 할 수 있다. Next.js는 Streaming과 부분 렌더링을 하기 위해 라우트를 단위로 쪼갠다. 그리고 3가지의 다른 서버 렌더링 전략이 있다.
- 정적 Rendering
- 동적 Rendering
- 스트리밍
서버 렌더링의 장점
- 데이터 fetch : 데이터 소스와 가까운 서버에서 데이터를 전달받는다. 데이터 fetch하는 시간을 단축시키고, 클라이언트의 요청 횟수를 줄인다.
- 서버 컴포넌트는 토큰, API 키같은 서버에 접근하는 민감한 데이터와 로직을 지킨다.
- 캐싱
- 번들 사이즈 : 서버 컴포넌트는 클라이언트에 영향을 끼치는 큰 JS 번들 사이즈를 줄여준다.
- 초기 페이지와 FCP : 사용자에게 즉시 보여주기 위해 서버에서 HTML을 만들어서 보내준다.
- SEO와 SNS 공유
- 스트리밍
Next.js에서 RSC
기본적으로 Next.js는 RSC이다. 별다른 설정 없이 자동으로 Server Rendering을 이용할 수 있다. 그리고 필요에 따라 설정을 통해 RCC를 사용할 수 있다.
RSC 렌더링의 동작 원리
- 서버에서, React API를 이용하여 렌더링을 한다. 렌더링 단위는 청크 단위로 나뉜다.
- 여기서 청크는 각 라우트(페이지), 그리고 Suspense 경계이다.
- 각각의 청크는 두 단계를 밟는다.
- React는 RSC Payload라 불리는 특정한 데이터 포맷 형식으로 RSC를 렌더링한다.
- Next.js는 서버에서 HTML을 렌더링하기 위해 RSC Payload를 사용하고 RCC Javascript 설명서를 사용한다.
- 이제 클라이언트에서는
- 해당 페이지의 빠른 상호작용이 아직 없는 화면을 보여주기 위해 전달받은 HTML이 사용된다. 이것은 초기 페이지이다.
- RSC Payload가 클라이언트에서 채워지고, 돔을 업데이트한다.
- Javascript 설명서는 RCC를 hydrate하기 위해 사용된다.
RSC Payload가 뭐야?
- RSC Payload는 렌더링된 RSC 컴포넌트 트리의 압축된 binary 데이터이다. 이 데이터는 클라이언트에서 리액트에 의해 브라우저의 돔을 업데이트되는데 사용된다.
- RSC Payload는 다음을 가지고 있다.
- RSC의 결과값
- RCC가 교체되어야 하는 위치와 JS파일에 대한 참조
- RSC에서 RCC로 전달하는 props
정리
- Next.js에서 SSR을 수행하기 전, React에서 먼저 RSC를 렌더링을 수행하여 RSC Payload를 만든다. Next.js는 이 RSC Payload를 전달받아, 렌더링을 수행한다. RCC는 렌더링하지 않고, placeholder로 비워두고, Hydrate를 위해 JS설명서를 작성해둔다.
- 클라이언트에서는 SSR로 생성된 HTML을 전달받고, Hydrate를 수행한다.
- RSC Payload를 streaming방식으로 계속 전달받아, 빈 자리를 채운다. JS 설명서를 이용해 RCC를 Hydrate한다.
서버 렌더링 전략
3가지의 서버 렌더링 방법이 있다.
- SSG(정적 렌더링) : 빌드할 때 렌더링된다.
- 결과는 캐싱되어 CDN에 저장이 된다.
- 이 최적화는 사용자와 서버 요청간에 렌더링된 결과값을 공유할 수 있다.
- 블로그 포스팅, 제품 페이지처럼 사용자에게 개인화된 데이터를 제공하지 않아도 될 때 유용하다.
- SSR(동적 렌더링) : 매 요청마다 각 사용자에게 렌더링이 된다.
- 사람마다 개인화된 데이터가 있을 때 유용하다.
- 쿠키나 URL의 search parameter같은 매 요청마다 매 요청마다 알아야하는 정보가 있을 때 유용하다.
- 캐싱된 데이터와 동적인 라우트
- 제품페이지는 일정 주기를 가지고 정적인 페이지를 업데이트하고, 개인화된 페이지는 동적인 렌더링을 수행할 수 있다.
- 동적으로 캐싱된 데이터와 캐싱되지 안은 데이터를 혼합하여 사용할 수 있다. RSC Payload와 데이터가 분리되어 캐싱되기 때문이다.
- 동적 함수나 캐싱되지 안은 데이터 요청이 발견되면, Next.js는 동적 렌더링으로 해당 페이지를 바꾼다.
- 동적 함수 : 매 요청마다 알아야하는 값
- cookies
- headers
- URL의 search params(page props 사용)
- 동적 함수 : 매 요청마다 알아야하는 값
- .js는 최적의 렌더링 전략을 자동으로 고르기에 직접 고를 필요가 없다. 대신에, 당신은 언제 캐싱하고, 특정 데이터를 revalidate할 지, UI를 stream방식으로 전달받을 지 선택할 수 있다.
- 스트리밍
- 스트리밍은 서버로부터 점진적인 렌더링을 가능하게 한다. 청크 단위로 쪼개져 준비되는 대로 클라이언트에 스트림된다. 사용자가 전체 페이지가 렌더링 되기 이전에 페이지를 즉시 볼 수 있게 해준다.
- Next.js App Router에서는 기본으로 적용이 된다. 초기 페이지가 로딩되는 성능을 향상시키고, 제품 리뷰 페이지같은 데이터 패칭이 느린 부분에 의존하는 UI는 늦게 보여준다.
- React Suspense나 loading.js로 스트리밍 방식을 사용할 부분을 정할 수 있다.
정리
Next.js 13부터는 cookies, headers, URL의 search params같은 동적값이 데이터 요청에서 발견되면 동적 렌더링, 그 외에는 정적 렌더링을 기본적으로 수행한다.
스트리밍은 청크 단위(route단위/Suspense 단위)로 서버에서 렌더링하여 클라이언트에 넘겨줘 점진적인 UI구현 및 Hydration이 가능하게 한다.
Client Component (이하 RCC)
RCC는 클라이언트에서 요청 시 렌더링하는 상호작용가능한 UI를 그릴 수 있게 한다. 클라이언트에서 렌더링을 하는 RCC로 사용할 것인지 명시할 수 있다. RCC가 어떻게 작동하는지, 렌더링하는지, 언제 써야하는지 알려주겠다.
Client에서 렌더링할 때 장점
Client에서 렌더링할 때 여러가지 장점이 있다.
- 상호작용 : 유저에게 피드백을 주고 UI를 업데이트할 수 있는 state, effect, 이벤트 리스너를 사용할 수 있다.
- 브라우저 API : localStroage같은 브라우저 API를 사용할 수 있다.
Next.js에서의 RCC
- 파일 최상단에 ‘use client’를 명시해주세요
- RSC와 RCC의 경계에 작성해주세요. 즉, 안에서 import되는 모든 파일은 Client 번들로 고려된다.
- 모든 RCC에 ‘use client’를 작성할 필요는 없다.
RCC의 렌더링 방식
- Next.js에서 RCC는 요청이 페이지 전체냐 부분이냐에 따라다르게 렌더링을 수행한다.
- 전체 페이지 로드
- 초기 페이지를 빠르게 하기 위해, Next.js는 RSC와 RCC 모두 React API를 사용하여 정적인 HTML prview를 렌더링한다. 즉, 사용자가 페이지에 방문했을 때, 모든 내용을 다운로드 받기 전에 일부 내용을 즉시 볼 수 있다. RCC의 JS 번들을 다운로드 > 파싱 > 실행하기 전에 말이다.
- 서버단에서
- React는 RSC를 RSC Payload(특별한 데이터 형식)로 렌더링한다. 이 때, RSC에 대한 참조를 포함한다.
- Next.js는 RSC Payload와 RCC JS 설명서를 이용하여 HTMl을 서버단에서 렌더링한다.
- 클라이언트단에서
- 이 HTML은 즉시 상호작용 없는 첫 페이지로 보여진다.
- RSC Payload는 RSC와 RCC트리를 클라이언트단에서 조정하고, 돔을 업데이트하는데 사용한다.
- Javascript 설명서는 RCC를 Hydrate하는데 사용한다. 이를 통해 UI상호작용을 할 수 있게 해준다.
- 후속 탐색
- 서버 단의 HTML렌더링 없이 RCC 모두 클라이언트에서 렌더링된다.
- 즉, RCC의 JS 번들은 다운로드되고 파싱된다는 말이다. 번들이 준비가 되면, React는 RSC Payload를 RCC와 RSC 트리를 재조정하고 DOM을 업데이트하기 위해 RSC Payload를 사용한다,
정리하면
- React는 RSC만을 렌더링하여 RSC Payload로 변환한다.
- Next.js는 이 RSC Payload + RCC Instruction을 이용하여 페이지를 렌더링한다.
Composition Pattern
React 앱을 만들 때, 어떤 부분이 서버에서 그리고 클라이언트에서 렌더링될 지 고려를 하지 않았을 것이다. 이제 이 RSC와 RCC를 혼합하여 사용하는 것에 대해 설명하겠다.
언제 RSC, RCC를 써?
RSC 패턴
Client에서 레넏링하기 전에, 서버단에서 우선 데이터를 fetch하고, DB나 백엔드 서버에 접근하세요. 아래는 흔한 SRC패턴이다.
- 컴포넌트 사이의 데이터 공유
- 여러 컴포넌트와 서버로부터 전달받은 데이터를 공유할 때가 있다.
- Context를 사용하거나 props로 내려주는 대신에, fetch나 React의 cache 함수를 사용해서 컴포넌트 간에 공유할 수 있다. React가 fetch를 데이터를 저장하도록 확장했고, fetch가 사용불가능하다면 cache함수를 사용하여 캐싱할 수 있다.
- 서버에서만 사용하는 코드를 클라이언트 환경에서 빼세요.
- API_KEY 환경 변수는 NEXT_PUBLIC로 고정되지 않기 때문에, 서버에서만 접근 가능한 것은 private 변수이다. 클라이언트에서 환경 변수가 누수되지 않기 위해서, Next.js는 private 환경 변수를 빈 문자열로 교체한다.
- 결론적으로, getData는 예상치 않게 client에서 import되어 실행될 수 있다.
- 서버코드의 의도치 않은 클라이언트 실행을 방지하기 위해서는 ‘
server-only’패키지를 사용하세요.
npm install server-only
import 'server-only'
export async function getData() {
const res = await fetch('https://external-service.com/data', {
headers: {
authorization: process.env.API_KEY,
},
})
return res.json()
}
- 그리고, ‘client-only’는 클라이언트에서만 실행되는 코드에서 사용할 수 있어요.
- 외부 라이브러리와 Provider
- RSC는 새로운 기능이기에 외부 라이브러리와 Provider는 이제 막 ‘use client’를 컴포넌트 내부에 쓰기 시작했다.
- 외부 라이브러리의 Client 컴포넌트를 Server 컴포넌트에서 사용하기 위해서는 다음과 같이 한 번 래핑을 해주세요.
'use client'
import { Carousel } from 'acme-carousel'
export default Carousel
- Provider는 React state와 context에 의존하고 앱의 루트에 위치해야 하기 때문에 예외이다.
- Provider는 전형적으로 테마같은 전역 관심사를 공유할 루트에서 렌더링된다. Context는 RSC에서 지원하지 않기 때문에 루트에서 Context를 선언하면 에러가 발생한다.
- 이를 해결하기 위해서는 RCC를 만들어서 context를 생성하고 렌더링하세요. RSC는 이 컴포넌트가 RCC라고 마크해두기 때문에, 직접적으로 Provider를 렌더링할 수 있다. 루트에서 Provider를 렌더링하면, 앱에 있는 다른 모든 RCC는 이 Context를 사용할 수 있다.
- Provider는 트리에서 가능한 깊은 곳에서 렌더링해야한다. html전체가 아니라 {children}만을 감쌈으로써 Next.js가 정적으로 RSC를 최적화하도록 할 수 있다.
import ThemeProvider from './theme-provider'
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html>
<body>
<ThemeProvider>{children}</ThemeProvider>
</body>
</html>
)
}
정리하자면
- RSC에서는 백엔드/DB에 접근하는 코드를 쓰세요.
- 서버단에서만 실행시키고 싶다면 server-only, 클라이언트라면 client-only로 명시하세요.
RCC
- RCC를 트리의 아래쪽으로 이동시키세요.
- 클라이언트의 JS 번들 사이즈를 줄이기 위해, RCC를 컴포넌트 트리 아래쪽에 두세요.
- 예를들어, 로고, 링크같은 정적인 요소를 가지고 있고, state를 사용하는 검색 바가 있다고 합시다. 전체를 RCC로 만드는 대신, 검색 바를 RSC 컴포넌트로 분리하세요. 이렇게 함으로써 클라이언트에 전체 컴포넌트의 JS를 보내지 않아도 됩니다.
- RSC에서 RCC로 props 전달
- RCC에서 데이터를 받았다면, RCC에 데이터를 props로 내려줄 것입니다. RSC에서 RCC로 props 전달은 React에 의해 직렬화가 되어야한다.
- RCC가 직렬화되지 않는 데이터에 의존한다면, Route Handler나, SWR, react-query 등 타라이브러리를 활용해서 데이터를 fetch할 수 있다.