React 18: 리액트 서버 컴포넌트 준비하기

React 18: 리액트 서버 컴포넌트 준비하기

시작하며

안녕하세요. 카카오페이 결제클라이언트파티 린입니다.

지난 2020년 12월, React Server Components가 소개된 이후 리액트 서버 컴포넌트는 가장 주목을 받는 React 18 기능 중 하나가 되었습니다. 아쉽게도 React 18의 initial release에는 포함되지 않았지만 여전히 많은 개발자들이 기대를 가지고 기다리고 있습니다. 이 포스트에서는 리액트 서버 컴포넌트는 무엇이고 어떤 문제를 해결하기 위한 컴포넌트인지 알아보겠습니다.

이 포스트는 Dan과 Lauren의 talkRFC를 참조하였습니다.
포스트를 읽기 전 또는 후 위 리소스들을 확인하는 것을 추천합니다.

리액트 컴포넌트의 Data fetching

리액트 릴리즈 이후 React 팀은 다방면으로 퍼포먼스 향상을 해왔으나 Data fetching의 경우 사용자 경험과 개발자 경험 모두를 만족시킬 수 있는 방법을 찾기는 어려웠습니다.

한 페이지에서 여러 정보를 보여주는 컴포넌트를 개발한다면 우리는 흔히 아래와 같이 컴포넌트를 구성합니다.

function KakaopayHome({ memberId }) {
  return (
    <MemberDetails memberId={memberId}>
      <MoneyBalance memberId={memberId}></MoneyBalance>
      <PaymentHistory memberId={memberId}></PaymentHistory>
    </MemberDetails>
  );
}

부모 컴포넌트인 <MemberDetails>와 자식 컴포넌트인 <MoneyBalance>, <PaymentHistory> 모두 다른 유저의 정보가 필요하고 필요한 정보는 서버 API를 호출하여 받아와야 합니다.

개발자는 필요한 정보를 받아오는 방법으로 흔히 두 가지 중 하나를 선택하게 됩니다.

  1. 모든 정보를 부모 컴포넌트에서 하나의 거대한 API로 호출하여 자식으로 내려준다.
  2. 컴포넌트에 필요한 API를 각 컴포넌트에서 호출한다.

첫 번째 방법의 경우 클라이언트에서 서버로 요청하는 API 요청수를 줄일 수 있지만 그로 인해 부모와 자식 컴포넌트가 결속되고 유지보수가 어려워지게 됩니다. 만일 컴포넌트의 구성이 바뀌거나 자식 컴포넌트가 다른 컴포넌트로 이동되는 경우, 해당 API를 다른 컴포넌트에서도 호출해줘야 하며 불필요한 정보를 over-fetching하게 됩니다.

두 번째 방법은 각 컴포넌트가 렌더링 될 때 필요한 데이터만 가져와 보여줄 수 있다는 장점이 있지만 high latency를 가진 클라이언트부터의 서버 요청은 늘어나게 됩니다. 또한 부모 컴포넌트는 렌더링 된 후 필요한 데이터를 받아오기 시작하고 이 과정이 끝나기 전까지 자식 컴포넌트의 렌더링과 API 호출 또한 지연됩니다. 결국 연속된 client-server API 요청과 중첩된 컴포넌트 내 API 호출 지연으로 인한 waterfall은 사용자 경험을 떨어트릴 수 있습니다.

페이스북팀 또한 이러한 고민 속에 Relay와 GraphQL로 waterfall 문제를 해결하고 있다고 하지만 이는 모든 리액트 애플리케이션에 적용하기에 적합한 솔루션은 아닙니다. 그리하여 오랜 시간 연구 끝에 React 팀은 Relay와 GraphQL의 도움 없이 리액트 애플리케이션의 waterfall을 줄일 수 있는 해결책으로 React Server Component를 소개하였습니다.

No Client-Server waterfall

기존 리액트 컴포넌트의 비동기적 data fetching의 가장 큰 문제점은 클라이언트와 서버 간 요청의 high latency와 연속된 Client-Server API 요청으로 발생하는 waterfall이었습니다.

아래는 클라이언트 컴포넌트에서 흔히 사용되는 data fetching 방식의 예제입니다.

// Note.js
function Note(props) {
  const [note, setNote] = useState(null);

  useEffect(() => {
    // NOTE: 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 {
    return (/* render note here... */);
  }
}

위 방식으로 데이터를 불러오는 부모와 자식 컴포넌트가 있다고 생각해 봅시다. 각 컴포넌트에서 데이터를 요청하는 경우 실제 컴포넌트가 렌더링 될 때 필요한 데이터만 가져와 보여줄 수 있다는 장점이 있지만 클라이언트와 서버 사이의 API 요청은 늘어나게 됩니다. 또한 부모 컴포넌트는 컴포넌트 렌더링 후 필요한 데이터를 받아오기 시작하고 이 과정이 끝나기 전까지 자식 컴포넌트의 렌더링과 API 호출이 지연되며 불필요한 렌더링이 발생합니다.


이와 같이 클라이언트 컴포넌트에서의 비동기 data fetching은 client-server waterfall을 야기하고 성능을 저하시키는 원인이 됩니다.

이를 위한 해결책으로 소개된 리액트 서버 컴포넌트는 용어 그대로 서버에서 동작하는 리액트 컴포넌트입니다. 서버 컴포넌트를 사용하면 컴포넌트 렌더링을 클라이언트가 아닌 서버에서 수행할 수 있습니다. 서버에서 Render를 수행하기 때문에 API를 통한 데이터 요청의 latency를 줄일 수 있고, 클라이언트에서의 연속된 API 호출을 제거하여 client-server waterfall를 막을 수 있습니다.

서버 컴포넌트 도입 후 리액트 컴포넌트에서의 data-fetching은 아래와 같이 간단해집니다. 그리고 클라이언트 컴포넌트에서 발생하던 client-server waterfall을 제거하여 컴포넌트에서 필요한 데이터만 fetching하는 방식을 유지하면서 퍼포먼스를 향상시킬 수 있습니다.

// Note.server.js - 서버 컴포넌트의 파일 네임 컨벤션
import { fetch } from 'react-fetch';

function Note(props) {
  // 아래 요청은 client-to-server가 아닌 server-to-server로 진행됩니다.
  const note = fetch(`https://api.example.com/notes/${props.id}`).json();

  if (note == null) {
    return <div>노트가 존재하지 않습니다.</div>;
  } else {
    return (/* render note here... */);
  }
}

하지만 아쉽게도 서버 컴포넌트에서 데이터 요청을 처리하여도 중첩된 컴포넌트에서의 여러 API 요청에 따른 network waterfall은 여전히 존재하고 이는 이상적이지 않습니다. 그리하여 React 팀은 이를 향상시킬 수 있는 데이터 preload api를 제공할 예정입니다.

리액트 서버 컴포넌트(RSC)의 이점

서버 컴포넌트는 위에서 설명한 클라이언트 data fetching에서 오는 waterfall 문제 해결과 더불어 개발자들의 관심을 끌 수 있을만한 다양한 이점을 가지고 있습니다. 서버 컴포넌트는 서버에서 동작하고 렌더링 된다는 특성상 다양한 종류의 백엔드 리소스에 접근할 수 있고 HTML이 아닌 '특별한' 형태로 렌더링 되어 클라이언트에 전달되기 때문에 클라이언트로 전달되는 번들 사이즈 또한 감소시킬 수 있습니다.

다음 섹션부터는 리액트 공식 예제를 참조하여 서버 컴포넌트의 특징과 장점에 대해 좀 더 자세하게 이야기해 보려 합니다.

자유로운 서버 리소스 접근

서버 컴포넌트는 서버에서 동작하기 때문에 데이터베이스, 파일 시스템 그리고 인터널 서비스 같은 서버 사이드 데이터 소스에 직접 접근할 수 있습니다.

// Note.server.js - 서버 컴포넌트
import fs from 'react-fs';
import db from 'db.server';

function Note(props) {
  // NOTE: loads *during* render, w low-latency data access on the server
  const note = db.notes.get(props.id); // 데이터베이스 접근
  const noteFromFile = JSON.parse(fs.readFile(`${id}.json`)); // 파일 접근

  if (note == null) {
    // handle missing note
  }
  return (/* render note here... */);
}

이렇게 서버에서 fetching한 데이터는 클라이언트 컴포넌트에 props로 전달 가능합니다. 한 가지 유의해야 할 점은 json으로 인코딩 가능한 serializable props만 전달 가능하며 function은 전달할 수 없습니다.

제로 번들 사이즈 컴포넌트

프론트엔드 앱을 개발하다 보면 무수한 라이브러리를 사용하게 됩니다. 직접 구현하기 까다로운 라이브러리들을 하나 둘 추가하다 보면 번들 사이즈가 늘게 되고, 퍼포먼스에 악영향을 끼치게 됩니다. 요즘은 많은 라이브러리들이 트리 셰이킹을 지원하고 필요에 따라 code splitting으로 렌더링에 필요한 번들 사이즈를 최대한 줄일 수 있지만 결국 번들 사이즈가 늘어나는 것은 막을 수 없습니다.

예를 들어 아래와 같은 클라이언트 노트 컴포넌트를 렌더링 하기 위해선 컴포넌트 코드와 marked, sanitize-html 패키지 또한 번들에 추가되어야 합니다.

// NoteWithMarkdown.client.jsx - 클라이언트 컴포넌트 = 기존의 리액트 컴포넌트

import marked from 'marked'; // 35.9K (11.2K gzipped)
import sanitizeHtml from 'sanitize-html'; // 206K (63.3K gzipped)

function NoteWithMarkdown({text}) {
  const html = sanitizeHtml(marked(text));
  return (/* render */);
}

하지만 이와 다르게 서버 컴포넌트 코드는 브라우저에 다운로드되지 않고 서버에서 미리 렌더링 된 static content를 전달하기 때문에 패키지를 추가해도 번들 사이즈에 영향을 끼치지 않습니다. 위 클라이언트 컴포넌트처럼 유저 인터랙션이 없는 컴포넌트들을 서버 컴포넌트로 마이그레이션 한다면 동일한 뷰를 제공함과 동시에 번들 사이즈와 초기 로딩 시간을 감소시킬 수 있을 것입니다.

// NoteWithMarkdown.server.jsx - 서버 컴포넌트
import marked from 'marked'; // ZERO IMPACT on bundle size
import sanitizeHtml from 'sanitize-html'; // ZERO IMPACT on bundle size

function NoteWithMarkdown({ text }) {
  // client component와 동일
}

자동 코드 분할

리액트 앱 퍼포먼스에 관심이 있는 개발자라면 code splitting 컨셉에 친근할 것입니다. Code splitting이란 하나의 거대한 자바스크립트 번들을 여러 개의 작은 번들로 쪼개어 필요할 때마다 클라이언트로 전송하는 방법입니다.

클라이언트 컴포넌트에서는 React.lazy와 dynamic import를 사용하여 렌더링에 필요한 컴포넌트를 동적으로 불러왔습니다.

// PhotoRenderer.js
// NOTE: *before* Server Components
import React from 'react';

// one of these will start loading *when rendered on the client*:
const OldPhotoRenderer = React.lazy(() => import('./OldPhotoRenderer.js'));
const NewPhotoRenderer = React.lazy(() => import('./NewPhotoRenderer.js'));

function Photo(props) {
  // Switch on feature flags, logged in/out, type of content, etc:
  if (FeatureFlags.useNewPhotoRenderer) {
    return <NewPhotoRenderer {...props} />;
  } else {
    return <OldPhotoRenderer {...props} />;
  }
}

Code splitting은 앱의 퍼포먼스를 크게 향상할 수 있으나 두 가지 단점이 존재합니다. 첫 번째로 lazy loading이 필요한 컴포넌트마다 일일이 React.lazy와 dynamic import를 적용해야 합니다. 두 번째로는 부모 컴포넌트가 렌더링 된 이후 로딩을 시작하기 때문에 화면에 보이기 전 어느 정도의 딜레이가 존재한다는 점입니다.

서버 컴포넌트는 이러한 단점을 두 가지 방식으로 해결했습니다. 서버 컴포넌트에서 import 되는 모든 클라이언트 컴포넌트를 code splitting 포인트로 간주하기 때문에 더 이상 React.lazy로 메뉴얼 하게 명시하지 않아도 됩니다. 또한 서버에서 미리 필요한 컴포넌트를 선택하기 때문에 클라이언트는 렌더링 프로세스 초기에 번들을 다운로드할 수 있습니다.

// PhotoRenderer.server.js - Server Component
import React from 'react';

// one of these will start loading *once rendered and streamed to the client*:
import NewPhotoRenderer from './NewPhotoRenderer.client.js';
import OldPhotoRenderer from './OldPhotoRenderer.client.js';

function Photo(props) {
  // Switch on feature flags, logged in/out, type of content, etc:
  if (FeatureFlags.useNewPhotoRenderer) {
    return <NewPhotoRenderer {...props} />;
  } else {
    return <OldPhotoRenderer {...props} />;
  }
}

서버, 클라이언트 그리고 공유 컴포넌트

서버 컴포넌트가 도입되면서 리액트 컴포넌트는 크게 세 가지 컴포넌트로 분류되었습니다. React 18 이전에 사용되던 리액트 컴포넌트는 클라이언트 컴포넌트로 분류되고, 서버와 공유 컴포넌트가 추가되었습니다. 컴포넌트의 타입은 파일 네임 컨벤션으로 구분되며 각 타입에 맞게 컴포넌트 네임을 변경해야 합니다. 기존 리액트 컴포넌트는 사용되는 방식에 따라 서버나 공유 컴포넌트로 변경될 수 있습니다.

타입설명주의사항파일 네임 컨벤션
서버
  • 서버에서만 렌더링되는 컴포넌트
  • 유저 인터랙티비티 제공 불가
  • useState(), useReducer(), useEffect()와 같은 state / effects 사용 불가
  • ❌ DOM과 같은 브라우저 api 사용 불가
  • ❌ state / effects / 브라우저 api 사용하는 커스텀 훅 사용 불가
  • ✅ 데이터베이스 / 내부 서비스 / 파일시스템과 같은 server-only 데이터 사용 가능
  • ✅ 서버 컴포넌트 / 클라이언트 컴포넌트 / native elements (예: div, span) 임포트 및 렌더링 가능
  • ✅ 클라이언트 컴포넌트 props로 serializable한 데이터 전달 가능
Example.server.js
클라이언트
  • 클라이언트에서 렌더링 되거나 SSR을 통해 서버에서 렌더링 되는 컴포넌트
  • 유저 인터랙션 사용 가능
  • 서버 컴포넌트 도입 전 리액트 컴포넌트
  • ❌ 서버 컴포넌트 임포트 불가
    • 다만 서버 컴포넌트는 클라이언트 컴포넌트에게 또 다른 서버 컴포넌트를 자식으로 넘겨주는 건 가능(예: <ClientTabBar><ServerTabContent/></ClientTabBar>)
  • ❌ server-only 데이터 사용 불가
  • ✅ state / effects / 브라우저 api 사용 가능
Example.client.js
공유
  • 서버와 클라이언트에서 렌더링 되는 컴포넌트
  • ❌ state / effects / 브라우저 api 사용 불가
  • ❌ 서버 컴포넌트 임포트 불가, server-only 데이터 사용 불가
  • ✅ 서버와 클라이언트 컴포넌트에서 임포트 되어 사용 가능
Example.js

서버와 클라이언트 컴포넌트는 많은 차이가 있지만 차이점을 간단하게 이야기하자면 아래와 같습니다.

  • 클라이언트 컴포넌트는 파일 시스템 접근과 같은 server-only feature를 사용할 수 없다.
  • 클라이언트 컴포넌트는 다른 클라이언트 컴포넌트만 import할 수 있다.
  • 서버 컴포넌트는 state와 같은 client-only feature를 사용할 수 없다.

리액트 서버 컴포넌트의 도입으로 기존 컴포넌트는 목적에 따라 서버(data fetching), 클라이언트(유저 인터랙션) 그리고 공유 컴포넌트로 세분화할 수 있습니다. 그리고 관심사에 따라 컴포넌트 타입을 지정함으로써 서버와 클라이언트 로직을 분리할 수 있게 되었습니다.

미래의 리액트 컴포넌트

위에 설명한 서버 컴포넌트의 특징과 장점들을 적극 활용한다면 미래의 리액트 컴포넌트는 아래의 예시와 같이 작성할 수 있습니다.

// Note.server.jsx
import { format } from 'date-fns'; // 번들사이즈에 영향 없음
import { readFile } from 'react-fs'; // 번들사이즈에 영향 없음
import path from 'path';

import NotePreview from './NotePreview'; // 공유 컴포넌트
import EditButton from './EditButton.client'; // 클라이언트 컴포넌트, 자동 코드 분할됨

export default function Note({ selectedId }) {
  const note = readFile(path.resolve(`./notes/${selectedId}.md`), 'utf8'); // 파일 시스템에서 data fetching

  if (note === null) {
    return (
      <div className="note--empty-state">
        <span className="note-text--empty-state">노트를 찾을 수 없어요 🥺</span>
      </div>
    );
  }

  // 노트가 존재하지 않을 시 아래의 코드는 클라이언트에 전달되지 않음
  let { id, title, body, updated_at } = note; // serializable한 props 클라이언트 컴포넌트로 전달 가능
  const updatedAt = new Date(updated_at);

  return (
    <div className="note">
      <div className="note-header">
        <h1 className="note-title">{title}</h1>
        <div className="note-menu" role="menubar">
          <small className="note-updated-at" role="status">
            마지막 변경 시간 {format(updatedAt, "yyyy MMM d 'at' h:mm bb")}
          </small>
          <EditButton noteId={id}>수정</EditButton>
        </div>
      </div>
      <NotePreview body={body} />
    </div>
  );
}

서버 컴포넌트가 도입됨으로써 백엔드 데이터 직접 접근, 번들 사이즈 감소, 자동 Code Splitting이 가능하게 되었고, 이로 인해 미래의 리액트 애플리케이션은 각 컴포넌트의 역할에 집중하여 개발할 수 있으며 애플리케이션의 퍼포먼스 또한 향상될 수 있을 것입니다.

리액트 서버 컴포넌트(RSC)와 서버 사이드 렌더링(SSR)

최근 들어 클라이언트 사이드 렌더링의 한계점이 논쟁이 되며 Next.jsRemix와 같은 서버 사이드 렌더링 프레임워크가 인기를 얻고 있습니다. 덕분에 서버 컴포넌트 또한 많은 관심을 받았지만 '서버'가 들어간 이름과 서버에서 렌더링 된다는 점 때문에 서버 사이드 렌더링과 혼동되고 서로를 대체할 수 있는 기술로 오해되곤 합니다.

tl;dr: 리액트 서버 컴포넌트는 서버 사이드 렌더링의 대체재가 아닙니다. 하지만 사용자 경험 향상을 위해 함께 사용할 수는 있습니다.

서버 사이드 렌더링이란?

기본적인 리액트 애플리케이션은 아래와 같은 클라이언트 사이드 렌더링(CSR)으로 동작합니다.

클라이언트 사이드 렌더링-출처:https://www.youtube.com/watch?v=pj5N-Khihgc
클라이언트 사이드 렌더링-출처:https://www.youtube.com/watch?v=pj5N-Khihgc

클라이언트 사이드 렌더링을 사용하는 앱의 경우 페이지 진입 시 HTML, 자바스크립트 그리고 모든 데이터가 로드되고 컴포넌트 렌더링이 끝나기 전까지 사용자는 아무런 기능이 없는 빈 화면만 보게 됩니다. 자바스크립트 번들의 용량이 크거나 사용자의 네트워크 속도가 느릴수록 사용자는 오랜 시간 빈 화면을 보게 되고 이는 사용자 경험을 저하시키게 됩니다.

반면 Next.js와 같은 서버 사이드 렌더링을 사용하는 애플리케이션은 클라이언트 애플리케이션의 자바스크립트 파일을 서버에서 먼저 HTML로 렌더링 합니다. 페이지가 정상적으로 동작하기 위해서는 자바스크립트 번들이 모두 다운로드되고 hydration이 완료되어야 하지만 빈 화면 대신 데이터가 존재하는 HTML을 제공함으로써 무거운 자바스크립트 파일이 다운로드되는 동안 사용자에게 의미 있는 콘텐츠를 제공할 수 있습니다.

서버 사이드 렌더링-출처:https://www.youtube.com/watch?v=pj5N-Khihgc
서버 사이드 렌더링-출처:https://www.youtube.com/watch?v=pj5N-Khihgc

서버 사이드 렌더링의 가장 큰 목적은 non-interactive한 버전의 클라이언트 컴포넌트를 최대한 빠르게 브라우저에 전달하여 초기 페이지의 First Contentful Paint 또는 Largest Contentful Paint 속도를 향상시는 것 입니다.

우리가 흔히 이용하는 넷플릭스의 메인 페이지를 생각해 봅시다. 무거운 프리뷰 비디오가 다운로드되기까지는 시간이 오래 걸리지만 빈 화면 대신 이미 콘텐츠 제목, 설명, 메인 이미지가 채워진 페이지를 제공하기 때문에 기다리는 동안 사용자의 시선을 끌 수 있고 이는 사용자 경험과 만족도에 큰 영향을 미칠 것입니다.

서버 컴포넌트와 서버 사이드 렌더링의 차이

서버 컴포넌트와 서버 사이드 렌더링은 서버에서 렌더링 된다는 유사점이 있지만 해결하고자 하는 문제점이 다릅니다. 서버 사이드 렌더링 프레임워크인 Next.js를 예시로 미리 언급한 서버 컴포넌트의 특징과 비교해보면 아래와 같은 차이점을 가지고 있습니다.

  • 서버 컴포넌트의 코드는 클라이언트로 전달되지 않습니다. 하지만 서버 사이드 렌더링의 모든 컴포넌트의 코드는 자바스크립트 번들에 포함되어 클라이언트로 전송됩니다.

  • 서버 컴포넌트는 페이지 레벨에 상관없이 모든 컴포넌트에서 서버에 접근 가능합니다. 하지만 Next.js의 경우 가장 top level의 페이지에서만 getServerProps()getInitialProps()로 서버에 접근 가능합니다.

  • 서버 컴포넌트는 클라이언트 상태를 유지하며 refetch 될 수 있습니다. 서버 컴포넌트는 HTML이 아닌 특별한 형태로 컴포넌트를 전달하기 때문에 필요한 경우 포커스, 인풋 입력값 같은 클라이언트 상태를 유지하며 여러 번 데이터를 가져오고 리렌더링하여 전달할 수 있습니다. 하지만 SSR의 경우 HTML로 전달되기 때문에 새로운 refetch가 필요한 경우 HTML 전체를 리렌더링 해야 하며 이로 인해 클라이언트 상태를 유지할 수 없습니다.

서버 컴포넌트가 전달되는 형태
서버 컴포넌트가 전달되는 형태

서버 컴포넌트는 서버 사이드 렌더링 대체가 아닌 보완의 수단으로 사용할 수 있습니다. 서버 사이드 렌더링으로 초기 HTML 페이지를 빠르게 보여주고, 서버 컴포넌트로는 클라이언트로 전송되는 자바스크립트 번들 사이즈를 감소시킨다면 사용자에게 기존보다 훨씬 빠르게 인터랙팅한 페이지를 제공할 수 있을 것입니다.

미리 경험해보는 리액트 서버 컴포넌트+서버 사이드 렌더링

현재 리액트 서버 컴포넌트는 아직 실험적인 기능이며 18.x인 마이너 버전 릴리즈에 추가될 것으로 예상되고 있습니다. React 팀은 서버 컴포넌트를 보다 쉽고 간단하게 사용자에게 선보이기 위해 Next.js, Remix와 같은 서버 사이드 리액트 프레임워크와 협업하고 있습니다.

Next.js는 버전 12 릴리즈 노트에서 React 18의 기능이 적용된 알파 버전을 발표했습니다. Next.js는 알파 버전에서 서버 컴포넌트를 미리 경험해 보고 싶은 개발자들을 위한 안내서데모 프로젝트 소스코드, 그리고 라이브 데모도 제공하고 있어 서버 컴포넌트가 Next.js에서 어떻게 동작하는지 미리 테스트해 볼 수 있습니다.

마치며

React 팀은 서버 컴포넌트의 도입으로 서버 리소스를 더 사용하고 기존 클라이언트의 풍부한 유저 인터랙티비티를 유지하며 서버 사이드 렌더링의 퍼포먼스를 향상시킬 수 있는 새로운 방향을 제시했습니다.

이 포스트에서 설명한 것과 같이 서버 컴포넌트를 사용한다면 클라이언트 data fetching의 큰 문제점인 네트워크 waterfall을 해결할 수 있습니다. 더불어 번들 사이즈 감소, 다양한 백엔드 data source 사용, 자동 코드 분할, 컴포넌트 별 관심사 분리 등 다양한 이점으로 사용자 경험뿐만이 아니라 개발자 경험 또한 향상될 수 있습니다.

서버 컴포넌트는 클라이언트와 함께 사용될 수 있고 필요에 의해 자유롭게 추가나 마이그레이션 될 수 있습니다. 또한 서버 컴포넌트는 서버 사이드 렌더링과 유사한 점이 있으나 대체제가 아니며 사용자 경험을 향상을 위해 서버 사이드 렌더링과 함께 서로 보완하여 사용될 수 있습니다.

지금까지 살펴본 것과 같이 서버 컴포넌트는 기존 컴포넌트와 다른 특징과 다양한 이점을 가지고 있습니다. 아직 실험적이고 정식 릴리즈 전이지만 꾸준한 관심과 기대를 가지고 기다려 볼 만한 흥미로운 컨셉이라고 생각합니다.

결제서비스 Frontend를 개발하고 있는 린입니다. 사용자들에게 더 편리한 결제서비스를 제공하기 위해 노력하고 있습니다.
lyn.lee

결제서비스 Frontend를 개발하고 있는 린입니다. 사용자들에게 더 편리한 결제서비스를 제공하기 위해 노력하고 있습니다.

태그
  • react
  • react server components
  • frontend
  • react18
추천 콘텐츠
© Kakao pay corp.