리액트(React)에서 SSR(Server-Side Rendering)와 서버 컴포넌트(Server Components)는 모두 웹 애플리케이션의 성능을 향상시키고 사용자 경험을 개선하기 위해 사용되지만, 그 방식과 목적에 있어서 차이가 있다.
SSR은 클라이언트(브라우저)에 보내기 전에 서버에서 HTML을 미리 생성하는 방식이다.
(화면의 렌더링이 서버에서 이루어지는 아키텍처)
사전적인 의미는 이러하지만 일반적인 현대의 SSR은 "첫 HTML 렌더링을 서버에서 처리하고, 이후의 렌더링 사이클은 클라이언트에서 처리" 하는 하이브리드 형태의 SSR을 가리킨다.
위 이미지는 CSR과 SSR이 어떠한 차이를 보여주는지 나타내는 그림이다. 웹 성능 지표에 관련된 내용은 아래와 같다.
1. First Paint : 사용자가 텅 빈 화면(흰색) 이상의 화면을 볼 수 있는 단계이다. 일반적인 레이아웃은 렌더링 되었지만 콘텐츠는 누락된 상태. 이를 FCP 라고 한다.
2. Page Interactive : 번들링파일이 다운로드 되었고, Hydration 과정을 통해 유저가 인터렉티브 할 수 있는 단계이다. 이 단계를 TTI 라고 한다.
3. Content Painted : 데이터베이스에서 데이터를 가져와 사용자가 관심있는 내용을 UI에 렌더링한 상태이다. 이 단계를 LCP 라고 한다.
만약에 데이터 작업이 서버에서 이뤄진다면 어떻게 될까?
SSR이 초기 로딩속도를 개선 했다고 하지만, 어떤 한 페이지의 데이터가 엄청 클 경우 데이터가 다 결합 될 때까지 사용자는 텅 빈 화면만 보게 될 것이다. 결국 CSR이랑 크게 다를게 없다는 의미다.
또한 SSR은 화면에 컨텐츠가 그려졌다 해도 모든 컴포넌트가 Hydrate가 되어야 인터렉티브한 웹 페이지를 사용할 수 있다. 즉 페이지단위로 구성되기 때문에 이러한 문제들이 발생하는 것이다.
그래서 리액트 개발자들은 위와 같은 문제를 해결하기 위해 노력해왔고, 발견한것이 리액트 '서버 컴포넌트' 이다.
Server Component : 서버에서 렌더링 되는 컴포넌트
RSC는 React 18버전 부터 등장한 완전히 새로운 개념이다. RSC를 통해 서버에서 실행되는 컴포넌트를 생성할 수 있다. React 18 이전 버전에서는 특별한 설정이 없으면 모든 컴포넌트가 RCC
로 구현되어 있다.
Next.js 공식문서에서 나와있듯, 위 표를 통해 RSC와 RCC는 명확하게 역할이 구분되어 있다. 그렇기 때문에 언제 어디서 RSC 또는 RCC를 사용할 것인지에 대한 결정이 중요하다.
Next.js 프로젝트에서 컴포넌트 트리는 RSC와 RCC가 결합된 형태로 개발될 수 있다. 컴포넌트 트리구조에서 RSC 컴포넌트는 서버에서 렌더링을 하게되고, RCC는 클라이언트 측에서 렌더링이 된다.
서버 컴포넌트의 특징은 다음과 같다.
RSC 에서의 렌더링은 서버측에서 React 컴포넌트의 로직을 실행하는 과정을 말한다.목적은 최종 HTML을 생성하는 것이 아닌, 컴포넌트의 로직을 처리하고 그 결과를 생성하는 것이다. 이렇게 생성된 결과는 클라이언트로 '스트림' 형태로 전송되어 클라이언트에서 최종적으로 사용자에게 보여질 HTML로 변환된다.
RSC는 서버에서 데이터를 가져오거나 다른 서버 사이드의 로직을 처리하여, 클라이언트에 전송되는 데이터와 JS 번들의 양을 최소화 하는 데 초점을 둔다.
SSR은 상태를 저장하고 refetch를 하면 html을 다시 받아옴(상태저장 불가)
RSC는 상태를 저장하고 refetch를 하면 RSC Payload라는 특별한 방법으로 다시 받아오기 때문에 상태 보존이 가능
위와 같은 이유 때문에 서버컴포넌트는 SSR의 대체자가 아니다. 서버사이드렌더링으로 초기 렌더링을 빠르게 해주고, 서버 컴포넌트로 제로번들링을 구현해주면 더 빠른 페이지를 제공 가능하다(보안관계)
Next AppRouter(13버전 사용)
Next13에선 모든 컴포넌트가 기본적으로 서버 컴포넌트이다.
모든 컴포넌트를 서버 컴포넌트로 만들면 BEST
user interaction 필요하면 클라이언트 컴포넌트로 사용한다(useState)
부모컴포넌트가 클라이언트 컴포넌트인데 자식컴포넌트가 서버컴포넌트이면 리렌더링이 발생할 떄 자식은 발생하지 않음. 이런 문제점 때문에 클라이언트 컴포넌트가 임포트 하는 컴포넌트는 클라이언트 컴포넌트여야 한다.