클라이언트까지 꼭 데이터가 와야할까? RCS(React Server Components)에 대해 알아보자

2023. 9. 28. 00:15·Development Study/Frontend
728x90

React 18에 등장한 React Server Components

React 18에서 새롭게 도입된 개념인 React Server Component(RSC) 에 대해 살펴봅시다.


React Server Components(RSC)란?

RSC는 서버에서만 렌더링되어 전달되는 컴포넌트를 의미합니다.
이는 HTML이 아닌 특별한 형태로 서버에서 렌더링되어 클라이언트에게 전달됩니다.

  • 특히, NextJS App Router는 이를 기본적으로 지원합니다.

기존 방식의 문제점

컴포넌트 구조는 다음과 같이 설정됩니다

const App = () => {
    return (
        <Wrapper>
            <ComponentA />
            <ComponentB />
        </Wrapper>
    )
}

기존의 방식은 각 컴포넌트에서 API 호출을 통해 데이터를 렌더링합니다.
이렇게 하면 각 컴포넌트는 자신에게 필요한 데이터만을 받게 됩니다.

각각의 컴포넌트는 아래와 같은 형식으로 구성되어있습니다.

const Component = () => {
    const [data, setData] = useState(null);

    useEffect(() => {
        // API 호출 등의 로직으로 dataB를 설정
        // setData(responseData);
    }, []);

    return (
        <div className="component">
            {data ? <h1>{data.title}</h1> : "데이터를 불러오는 중..."}
        </div>
    );
}

이럴 경우 아래와 같은 문제점이 발생합니다.

  • 여러 컴포넌트가 동시에 API를 호출하면 불필요한 네트워크 요청이 발생
  • 일부 컴포넌트에서 데이터를 가져오는 데 시간이 오래 걸릴 경우 전체 렌더링 성능의 저하 발생

서버 컴포넌트로 어떻게 해결할 수 있을까?

RSC는 서버에서 렌더링되어 전체 페이지나 특정 부분에 필요한 데이터를 한 번에 가져올 수 있습니다.
조금 더 쉽게 말해보자면 페이지의 가장 상단에서 데이터들을 호출하고 하위 컴포넌트들에 props로 전달합니다.

효과

  • 클라이언트에서의 불필요한 네트워크 요청 감소
    -> API 호출 시 발생하는 네트워크비용, 렌더링 지연 감소
  • 전체 애플리케이션의 퍼포먼스 향상
// ServerComponent.server.js
import { db } from './database';  // 예시로 데이터베이스 연결을 가정합니다.
function ServerComponent() {
    // 서버에서 필요한 데이터를 직접 요청합니다.
    const dataA = db.getDataForComponentA();
    const dataB = db.getDataForComponentB();

    return (
        <Wrapper>
            <ComponentA data={dataA} />
            <ComponentB data={dataB} />
        </Wrapper>
    );
}
// Wrapper 컴포넌트는 동일하게 유지됩니다.
const Wrapper = ({ children }) => {
    return (
        <div className="wrapper">
            {children}
        </div>
    );
}
// ComponentA
const ComponentA = ({ data }) => {
    return (
        <div className="componentA">
            <h1>{data.title}</h1>
        </div>
    );
}

// ComponentB
const ComponentB = ({ data }) => {
    return (
        <div className="componentB">
            <h1>{data.title}</h1>
        </div>
    );
}

서버 컴포넌트 사용 시 주의사항

서버 컴포넌트에도 주의사항이 있습니다. 어떤 점들을 유의해야 할까요?

  1. 상태 관리를 위한 로직 분리
    • 서버 컴포넌트는 클라이언트의 상태와 직접적인 상호작용을 할 수 없기 때문에, 상태를 필요로 하는 컴포넌트는 클라이언트 컴포넌트로 처리해야 합니다.
// Example.server.js (서버 컴포넌트)
import fetch from 'node-fetch';

function ServerComponent() {
    const data = fetch('https://api.example.com/data').then(res => res.json());
    return <div>Data: {data}</div>;
}

// Example.client.js (클라이언트 컴포넌트)
import React, { useState } from 'react';

function ClientComponent() {
    const [count, setCount] = useState(0);

    return (
        <div>
            <button onClick={() => setCount(count + 1)}>Click me!</button>
            <p>You clicked {count} times</p>
        </div>
    );
}
  1. 서버 컴포넌트와 클라이언트 컴포넌트 사이의 데이터 전달
    • 서버 컴포넌트로부터 데이터를 가져와 클라이언트 컴포넌트에게 props로 전달해야 합니다.
// App.server.js (서버 컴포넌트에서 데이터 가져오기)
import fetch from 'node-fetch';
import ClientComponent from './Example.client.js';

function App() {
    const data = fetch('https://api.example.com/data').then(res => res.json());
    return <ClientComponent serverData={data} />;
}

// Example.client.js (클라이언트 컴포넌트에서 데이터 사용하기)
import React from 'react';

function ClientComponent({ serverData }) {
    return (
        <div>
            <h1>Data from Server: {serverData}</h1>
        </div>
    );
}

위의 예시는 간단한 형태로 RSC를 통한 데이터 전달 및 클라이언트에서의 상태 관리를 보여주는 예시 코드입니다.
실제 구현 시에는 데이터 처리, 에러 핸들링 등의 추가적인 작업을 유동적으로 수행해주세요.


+1 서버 컴포넌트의 장점: 번들 사이즈 감소

  • 번들 사이즈 감소(zero-bundle-size)
    • 서버에서 렌더링되는 컴포넌트의 코드는 클라이언트로 전송되지 않으므로 최종 번들 사이즈를 줄일 수 있습니다.
    • 서버 컴포넌트가 클라이언트 번들에 포함되지 않는다는 것으로 사실상 번들의 크기가 0이라고 볼 수 있습니다. 따라서 RSC는 zero-bundle-size React Server Components 라고도 불립니다.

+2 서버 컴포넌트의 장점: 자동 코드 분할

서버 컴포넌트와 자동 코드 분할을 함께 사용하면 더욱 효과적입니다.
React Server Components는 필요한 컴포넌트만을 동적으로 불러와 퍼포먼스를 향상시킬 수 있습니다.
React에서 전통적으로 사용되는 클라이언트 컴포넌트의 코드 분할과 서버 컴포넌트를 이용한 코드 분할의 차이점을 보면서 확인해보겠습니다.

1. 전통적인 클라이언트 컴포넌트의 코드 분할

  • React의 클라이언트 사이드에서는 React.lazy()와 함께 import() 문법을 사용하여 컴포넌트를 동적으로 불러올 수 있습니다.
    -> 즉, 레이지 로딩(lazy loading)을 사용하는 방법입니다.
import React, { Suspense } from 'react';

const LazyComponent = React.lazy(() => import('./LazyComponent'));

function App() {
  return (
    <div>
      <Suspense fallback={<div>Loading...</div>}>
        <LazyComponent />
      </Suspense>
    </div>
  );
}

export default App;

위의 코드에서 LazyComponent는 실제로 필요할 때만(렌더링 시점에) 동적으로 불러와지며, 그 전까지는 불러오지 않습니다.

2. React Server Components를 사용한 자동 코드 분할
반면에 서버 컴포넌트를 사용하면, 클라이언트에서 불러오는 번들에 포함되지 않는 컴포넌트나 라이브러리를 사용할 수 있습니다.

// MyServerComponent.server.js

import ExpensiveLibrary from 'expensive-library';  // ZERO IMPACT on client bundle size

function MyServerComponent() {
  const result = ExpensiveLibrary.someFunction();
  return <div>{result}</div>;
}

export default MyServerComponent;

위의 코드에서 ExpensiveLibrary는 클라이언트의 번들에 포함되지 않습니다.
따라서 클라이언트 측에서의 번들 사이즈는 이 라이브러리의 크기만큼 감소합니다.

이처럼 서버 컴포넌트는 무거운 라이브러리나 코드를 클라이언트 번들에서 제외할 수 있어 초기 로딩 시간을 크게 줄일 수 있습니다.


서버 컴포넌트와 클라이언트 컴포넌트의 적절한 조합은 React 애플리케이션의 성능과 사용자 경험을 크게 향상시킬 수 있습니다.
NextJS의 Server Components와 함께 사용하여 그 효과를 극대화시켜보아요!

References


reference1
reference2
reference3
reference4
reference5
reference6
reference7

728x90
저작자표시 비영리 변경금지 (새창열림)

'Development Study > Frontend' 카테고리의 다른 글

UI 버벅임은 이제 그만! Web Worker 를 적절히 이용하여 UX 증진시키기  (0) 2023.10.06
[NextJS AppRouter] 초간단 API Mocking 하기(with Route Handlers)  (0) 2023.09.28
[Next.js App Router] Next.js 13 주요 변경사항 알아보기  (0) 2023.09.18
[CSS] BEM 방법론에 대해 알아보자(Block, Element, Modifier)  (0) 2023.06.01
[JavaScript, React] const = () => {}? function () {}? 함수 표현식, 함수 선언문 중 무엇을 쓸까?  (0) 2023.05.30
'Development Study/Frontend' 카테고리의 다른 글
  • UI 버벅임은 이제 그만! Web Worker 를 적절히 이용하여 UX 증진시키기
  • [NextJS AppRouter] 초간단 API Mocking 하기(with Route Handlers)
  • [Next.js App Router] Next.js 13 주요 변경사항 알아보기
  • [CSS] BEM 방법론에 대해 알아보자(Block, Element, Modifier)
ThreeLight
ThreeLight
ThreeLight Studio의 블로그, TimeMap.exe에 오신 것을 환영합니다.
  • ThreeLight
    TimeMap.exe
    ThreeLight
  • 전체
    오늘
    어제
    • 분류 전체보기 (245)
      • Checkpoint (1)
      • (3D)Dev Deep Dive (0)
        • Templates & Guides (9)
        • Frontend origin (9)
        • Backend origin (1)
        • TroubleShootings (4)
      • Development Study (95)
        • Frontend (36)
        • Backend (21)
        • CS(Computer Science) (2)
        • Background Knowledges (11)
        • Algorithm (2)
        • Mobile (3)
        • AWS (6)
        • Python (6)
        • MSW(MapleStoryWorlds) (8)
      • Coding Test (59)
        • 문제.zip (1)
        • BaekJoon_JavaScript (0)
        • Programmers_JavaScript (9)
        • BaekJoon_Python (23)
        • Programmers_Python (10)
        • Undefined_Python (3)
        • Programmers_SQL (13)
      • 활동내역.zip (43)
        • 개인 (21)
        • Techeer (12)
        • Bootcamp (7)
        • Hackathon (1)
        • TeamProjects (2)
      • 여기 괜찮네??(사이트 | App) (5)
      • 재미있는 주제들 (8)
      • 개발 외 공부 저장소 (11)
        • 생산운영관리 (3)
        • 생활속의금융 (6)
        • 경영정보시스템 (2)
  • 링크

    • TimeMap.dmg (Portfolio)
    • GitHub 바로가기
    • 오픈프로필(카카오톡)
    • Medium 바로가기
    • Disquiet 바로가기
    • LinkedIn 바로가기
  • 인기 글

  • 태그

    SQL
    Python
    HTML
    Baek Joon
    프로그래머스
    CSS
    JavaScript
    TypeScript
    react
    programmers
  • 최근 글

  • 최근 댓글

  • hELLO· Designed By정상우.v4.10.1
ThreeLight
클라이언트까지 꼭 데이터가 와야할까? RCS(React Server Components)에 대해 알아보자
상단으로

티스토리툴바