home

SSR, Server Components, Client Components

글 분류
sub
키워드
react
nextjs
생성일
2023/01/05 09:30
최근 수정일
2024/03/05 00:49
작성중

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
복사

언제 뭘 사용할까?

참조