RSC(React Server Component)

miyaongg·2023년 8월 2일
0

클라이언트 컴포넌트의 data fetching

function Note(props) {
	const [note, setNote] = useState(null);
  
  	useEffect(() => {
    	//loads after rendering, triggering waterfalls in children
      	fetch(`https://api.example.com/notes/${props.id}`)
      	.then(res => res.json())
      	.then(
        	(result) => {
            	setNote(result);
            }
        )
    }, [props.id])
  
  	if (note === null) {
    	return "Loading";
    } else {
    	//render note...
    }
}

장점

  • 각 컴포넌트에서 데이터를 요청하는 경우, 실제 컴포넌트가 렌더링 될 때 필요한 데이터만 가져와 보여줄 수 있음

단점

  • 클라이언트와 서버 사이의 API 요청 증가
  • 부모 컴포넌트는 컴포넌트 렌더링 후 필요 데이터를 받아오기 시작하고, 이 과정이 끝나기 전까지 자식 컴포넌트의 렌더링과 API 호출이 지연되며 불필요한 렌더링 발생

리액트 서버 컴포넌트의 data fetching

React Server Component
서버에서 동작하는 리액트 컴포넌트.
컴포넌트 렌더링을 클라이언트가 아닌 서버에서 수행할 수 있음
서버에서 render를 수행하기 때문에 API를 통한 데이터 요청의 latency를 줄일 수 있고, 클라이언트에서의 연속된 API 호출을 제거해 client-server waterfall을 막을 수 있음

import { fetch } from 'react-fetch';

function Note(props) {
	//server-to-server
  	const note = fetch(`https://api.example.com/notes/${props.id}`).json();
  
  if (note === null) {
  	return <div>노트가 존재하지 않습니다.</div>;
  } else {
  	//render note...
  }
}

장점

  • 클라이언트 컴포넌트에서 발생하던 client-server waterfall을 제거하여 컴포넌트에서 필요한 데이터만 fetching하는 방식을 유지하며, 퍼포먼스 향상 가능

서버 컴포넌트 장점

1. 자유로운 서버 리소스 접근

  • 서버에서 동작하기 때문에 데이터베이스, 파일 시스템 등 서버 사이드 데이터 소스에 직접 접근 가능
  • 서버에서 fetching한 데이터는 클라이언트 컴포넌트에 props로 전달 가능
    (json으로 인코딩 가능한 serializable props만 전달 가능, function은 전달 불가능)
import fs from 'react-fs';
import db from 'db.server';

function Note(props) {
	const note = db.notes.get(props.id); //db 접근
  	const noteFromFile = JSON.parse(fs.readFile(`${id}.json`)); //파일접근
  
  	if(note === null) {
    	//handle missing note
    }
  
  	return {  
    	//render note...
    }
}

제로 번들 사이즈 컴포넌트

  • 라이브러리를 많이 사용하게 되면 번들 사이즈가 늘게 되고, 퍼포먼스에 악영향을 끼침
    서버 컴포넌트 코드는 브라우저에 다운로드 되지 않고 서버에서 미리 렌더링 된 static content를 전달하기 때문에 패키지를 추가해도 번들 사이즈에 영향을 끼치지 않음

자동 코드 분할

[client component]

  • React.lazy와 dynamic import를 사용해 렌더링에 필요한 컴포넌트를 동적으로 불러옴
  • lazy loading이 필요한 컴포넌트마다 일일이 React.lazy와 dynamic import를 적용해야 함
  • 부모 컴포넌트가 렌더링 된 이후 로딩을 시작하기 때문에 화면에 보이기 전 딜레이 존재
import React from 'react';

const OldPhotoRenderer = React.lazy(() => import('./OldPhotoRender.js'));
cosnt NewPhotoRenderer = React.lazy(() => import('./NewPhotoRenderer.js'));

function Photo(props) {
	if(FeatureFlags.useNewPhotoRenderer) {
    	return <NewPhotoRenderer {...props} />;
    } else {
    	return <OldPhotoRenderer {...props} />;
    }
}

[server component]

  • import 되는 모든 클라이언트 컴포넌트를 code splitting 포인트로 간주하기 때문에 더 이상 React.lazy로 메뉴얼 하게 명시하지 않아도 됨
    = 서버에서 미리 필요한 컴포넌트를 선택하기 때문에 클라이언트는 렌더링 프로세스 초기에 번들 다운로드 가능

waterfall

  • 렌더링과 로딩이 한 번에 진행되는 것이 아니라, 렌더링 이후 데이터가 로딩되는 방식으로 인해 불필요하게 시간이 소요되는 것

서버/클라이언트/공유 컴포넌트

서버 컴포넌트

  • 서버에서만 렌더링되는 컴포넌트
  • 유저 인터렉티비티 제공 불가

[주의사항]

  • useState(), useReducer(), useEffect()와 같은 state/effects 사용 불가
  • DOM과 같은 브라우저 api 사용 불가
  • state/effects/브라우저 api 사용하는 커스텀 훅 사용 불가
  • 데이터베이스 / 내부 서비스 / 파일 시스템과 같은 server-only 데이터 사용 가능
  • 서버 컴포넌트 / 클라이언트 컴포넌트 / native elements(ex. div, span..) import 및 렌더링 가능
  • 클라이언트 컴포넌트 props로 serializable한 데이터 전달 가능

[파일 네임 컨벤션]

  • Example.server.js

클라이언트 컴포넌트

  • 클라이언트에서 렌더링 되거나 SSR을 통해 서버에서 렌더링 되는 컴포넌트
  • 유저 인터렉션 사용 가능
  • 서버 컴포넌트 도입 전 리액트 컴포넌트

[주의사항]

  • 서버 컴포넌트 import 불가
    (서버 컴포넌트 -> 클라이언트 컴포넌트로 또 다른 서버 컴포넌트를 자식으로 넘겨주는 건 가능)
  • server-only 데이터 사용 불가
  • state / effects / 브라우저 api 사용 가능

[파일 네임 컨벤션]

  • Example.client.js

공유 컴포넌트

  • 서버와 클라이언트에서 렌더링 되는 컴포넌트

[주의사항]

  • state / effects / 브라우저 api 사용 불가
  • 서버 컴포넌트 import 불가, server-only 데이터 사용 불가
  • 서버와 클라이언트 컴포넌트에서 import 되어 사용 가능

[파일 네임 컨벤션]

  • Example.js

RSC / SSR

*CSR

  • 페이지 진입 -> HTML, js, data 로드 후 컴포넌트 렌더링이 끝나기 전까지 사용자는 빈 화면만 보게 됨

SSR

  • js 파일을 서버에서 먼저 HTML로 렌더링
  • 페이지 동작을 위해서는 자바스크립트 번들이 모두 다운로드 되고 hydration이 되어야 하지만, 빈 화면 대신 데이터가 존재하는 HTML을 제공함으로써 무거운 js 파일이 다운로드되는 동안 사용자에게 의미있는 콘텐츠 제공
  • non-interactive한 버전의 클라이언트 컴포넌트를 최대한 빠르게 브라우저에 전달해 초기 페이지의 first contentful paint 속도를 향상시키는 것이 목적

RSC와 SSR의 차이

  • RSC의 코드는 클라이언트로 전달되지 않음
  • SSR의 모든 컴포넌트의 코드는 js 번들에 포함되어 클라이언트로 전송
  • RSC는 페이지 레벨에 상관없이 모든 컴포넌트에서 서버에 접근 가능
  • next.js는 가장 top level의 페이지에서만 getServerProps(), getInitialProps()로 서버에 접근 가능
  • RSC는 클라이언트 상태를 유지하며 refetch 될 수 있음, 서버 컴포넌트는 HTML이 아닌 특별한 형태로 컴포넌트를 전달하기 떄문에 필요한 경우 포커스, 인풋 입력값 같은 클라이언트 상태를 유지하며 여러 번 데이터를 가져오고 리렌더링해 전달 가능
  • SSR은 HTML로 전달되기 때문에 새로운 refetch가 필요한 경우 HTML 전체를 리렌더링 해야 하고, 이로 인해 클라이언트 상태를 유지할 수 없음

즉, RSC는 SSR의 대체가 아닌 보완의 수단으로 사용할 수 있음
SSR으로 초기 HTML 페이지를 빠르게 보여주고, 서버 컴포넌트로는 클라이언트로 전송되는 js 번들 사이즈를 감소시킨다면 사용자에게 기존보다 훨씬 빠르게 인터렉팅한 페이지를 제공할 수 있음

출처: https://tech.kakaopay.com/post/react-server-components/

profile
Front-End Developer

0개의 댓글