TLDR;
SSR과 RSC는 다르다. 아주 다르다. 함부로 같다하지말거라.
SSR
•
페이지 전체를 그린다.
•
서버에서 한번, 브라우저에서 한번
•
서버에서 한번 돌아서 HTML만 보내고 브라우저에서 Hydration 하므로 “두번”돈다고 볼 수 있다.
리액트 서버 컴포넌트(RSC)?
•
브라우저가 아닌 서버에서만 실행되도록 만드는 리액트 컴포넌트 → 서버에서 실행되는 컴포넌트 란 말이 중요
•
app 디렉터리 내부에있는 모든것
•
서버 인프라를 전체적으로 사용할 수 있게 해준다.
•
예를 들어 많은 의존관계를 가지고 있어 자바스크립트 번들사이즈를 늘리는 컴포넌트들은 서버에 남아있다 → 퍼포먼스 증가
RSC 페이지 소스
•
리액트의 new line-based 내부 데이터 스트리밍 포맷
•
VDOM을 문자열로 나타낸 형태
•
JSON 형태는 아니고 line-based 포맷이다.
•
리액트 “내부적”으로만 사용하기위한 포맷이다. 개발자가 직접 개입할 여지가 없는 코드이므로 이해하려 할 필요는 없다.
•
해당 코드들을 이용해 Reconciliation을 수행한다.
RSC vs SSR
•
RSC 도입 이전에는 모든게 Client component였고 서버 사이드 행위를 getServerProps()를 이용해 수행했었다.
◦
RSC 부터는 모든게 ServerSideComponent고 클라이언트에서 돌아가는 컴포넌트를 직접 선택해야한다.
•
SSR 페이지 전체 리렌더 하므로 state가 사라진다
•
SSR은 HTML 페이지 전체를 그려보내준다.
•
SSR은 초기 렌더링시 에만 그려준다.
•
RSC는 HTML이 아닌 직렬화 가능한 형태의 데이터를 전달한다.
fetch
•
Nextjs는 fetch WebAPI를 extends해서 사용 → 해당 fetch를 이용하여 cache, revalidate 설정 가능
force-cache | - 기본값
- SSG, 빌드 때 fetch하여 빌드 |
no-store | - SSR, 요청 시 마다 fetch |
Revalidation
•
기존 캐시를 pure하고 새로운 데이터를 refetching 하는 과정 → fetch의 데이터를 최신화하는 방법
Time-based | - 시간에 따라 revalidate |
On-demand revalidation | - 특정 event가 발생할 때 revalidate
- tag-based, path-based(다수의 데이터를 revalidate 할 때 유용) |
Time-base Revalidation
fetch('https://...', { next: { revalidate: 3600 } })
JavaScript
복사
•
3600초 이후 revalidate하는 옵션
On-demand Revalidation
export default async function Page() {
const res = await fetch('https://...', { next: { tags: ['collection'] } })
const data = await res.json()
// ...
}
JavaScript
복사
•
“collenctions”라는 태그로 revalidate 할 수있음
•
해당 컴포넌트 또는 페이지에 모든 fetch를 Route Segment Config로 설정 할 수 있으나 추천 하지는 않고각 fetch를 개별적으로 설정해주는걸 추천한다.
Server Actions
// Server Component
export default function Page() {
// Server Action
async function create() {
'use server'
// ...
}
JavaScript
복사
•
서버에서 실행되는 비동기 함수
•
쓸일이 많이 있을까? → PHP의 새로운 악몽
•
RSC에서도 사용 가능하며, RSC에서 props로 client components로 전달하여 사용할 수 도 있다.
클라이언트 컴포넌트
특징
•
클라이언트에서 렌더링되는 컴포넌트 → 너무 단순한 정의, 실제 하는일과 너무 다르다. 여전히 서버에서 실행된다.
◦
서버에서도 실행되고 클라이언트에서도 실행되는 컴포넌트라고 보면 된다.
•
클라이언트 컴포넌트는 서버에서 프리렌더링 되고 클라이언트에서 hyrdrate될 수 있다. → 클라이언트 컴포넌트도 서버에서 프리렌더링된다.
•
for rich interactivity
만드는 법
'use client'; // 모든 임포트 전에 선언해준다 - 클라이언트 컴포넌트로 바뀜
import { useState } from 'react';
export default function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
);
}
JavaScript
복사
•
클라이언트 훅 (useEffect, useState)등을 사용 할 때 마크해줘야 함
•
클라이언트 훅 이 없는 컴포넌트는 그대로 내버려 두는게 제일 좋은 방법 - 서버 컴포넌트가 되게, 클라이언트 번들 용량을 최대한 줄이기 위해
주고받기
•
서버 컴포넌트와 클라이언트 컴포넌트는 같은 컴포넌트 트리에 존재할 수 있다.
•
클라이언트 컴포넌트 내에 서버 컴포넌트를 사용할 수는 없다.
'use client';
// ❌ This pattern will not work. You cannot import a Server
// Component into a Client Component
import ServerComponent from './ServerComponent';
export default function ClientComponent() {
return (
<>
<ServerComponent />
</>
);
}
JavaScript
복사
•
클라이언트 컴포넌트 내에 children props로는 줄 수 있음
// app/ClientComponent.js
'use client';
export default function ClientComponent({children}) {
return (
<>
{children}
</>
);
}
// app/page.js
// ✅ This pattern works. You can pass a Server Component
// as a child or prop of a Client Component.
import ClientComponent from "./ClientComponent";
import ServerComponent from "./ServerComponent";
// Pages are Server Components by default
export default function Page() {
return (
<ClientComponent>
<ServerComponent />
</ClientComponent>
);
}
JavaScript
복사