[10 Years of React] 서버 컴포넌트

JISEUNG·2023년 6월 4일
29

Next.js

목록 보기
4/5
post-thumbnail

React Roundtable

React 10주년 기념 Delba de Oliveira(Vercel)가 React 코어 팀의 Andrew Clark, Sebastian Markbåge의 React, 서버 컴포넌트에 대한 탁상 토론이 있었다. 아래는 토론 내용을 정리한 것이다.

React에 대해 생각해보자

  • React를 단순한 어떤 패키지라거나 특정한 것에 대한 구현, 특정한 API에 국한된 것으로 생각하지 말자

  • React는 UI, 사용자 경험, 애플리케이션을 작성하는 방법에 관한 것이다.

  • React가 관심있는 것은 ComponentComposability이다.

    1. 컴포넌트와 컴포넌트 경계
    2. 그것을 사용할 수 있는 개념
    3. 그 개념을 사용한 UI 개발
  • Component Model

    • 추상화나 그 기술을 말하는 것이 아니다.
    • 중요한 것은 '함께 일할 수 있는가'

서버 컴포넌트의 등장 배경

  • Component Model & Data Fetching Model

  • 아래와 같은 고민들이 있었다.

    • 어떻게, 원하는 대로 API를 사용할 것인가
    • 데이터 패칭
      • 데이터는 결국 어디서 오는가?
      • 로딩 상태를 어디서 어떻게 설계할 것인가?
      • Suspense Boundary
    • 성능, 사용자 경험, 개발자 경험
      ➡ pre-load, pre-fetch, extra round trip 최소화
    • Waterfall
      ➡ 병렬화
    • Security Model Layer
    • Abstraction Layer
    • ...

서버 컴포넌트의 등장

  • GraphQL, Relay에서 영감을 받았다.

  • 지금까지의 SSR과는 다른 개념

  • 위의 문제들을 하나하나 해결하려고 서버 컴포넌트가 등장한 것은 아니고, 만들고 나니 저 모든 문제들을 싹 다 해결하더라!

  • Server Streaming

    • 클라이언트, 데이터 스트림을 차단하지 말자. 애플리케이션의 전체 span을 줄이자!
      • not waterfall
      • 병렬적 수행
    • 한번에가 아니라 조금씩 표현하자
      • load sequence
      • 첫번째 표시까지 시간을 줄이자
  • Suspense

    • 데이터로 앱을 구성하는 방법
    • Data fetch suspense == Server Component

Advices

  • Be Fresh

    • React에 이미 익숙하다면, 지금까지의 패러다임은 잊어버리고 fresh 하게 접근하자
  • To Be Simple

    • 너무 많이 생각하지 말자.
    • 서버 컴포넌트를 하나의 Extra tool일 뿐. 써도 되고 안써도 그만. 다만 쓰면 좋다는 걸 느낄 거다.
    • Hydration 비용이라던가, 이런 세부 구현 내용들을 하나하나 너무 깊게 신경쓰지 마라.
      • 그런 것은 라이브러리 개발자가 해야할 일이며, 이런 곳에 너무 많은 시간을 할애하고 있다면 그건 라이브러리 개발자가 잘못하고 있는 것

React 서버 컴포넌트

위 토론에서 알 수 있듯, React는 서버와 클라이언트를 활용하는 하이브리드 애플리케이션을 구축하기 위한 새로운 모델을 도입했다.

  • 이제 용도에 따라 컴포넌트를 렌더링할 위치(클라이언트 or 서버)를 선택한다.

    • 서버 컴포넌트 : Non-interactive UI
    • 클라이언트 컴포넌트 : Interactive UI
  • 클라이언트 사이드 앱의 풍부한 상호작용 + 기존 서버 렌더링의 향상된 성능의 결합

장점

프론트엔드 개발자가 서버 인프라를 더 잘 활용할 수 있다.

  • 데이터 패칭을 데이터베이스에 더 가까운 서버로 옮길 수 있다.
  • 클라이언트 JavaScript 번들 크기에 영향을 미쳤던 대규모 종속성을 서버에 유지해 성능을 개선할 수 있다.
  • 초기 페이지 로딩이 빨라지고, 클라이언트 사이드 JavaScript 번들 크기가 줄어든다.
  • 기본 클라이언트 사이드 런타임은 캐시되고, 크기가 예측 가능해지며, 애플리케이션이 커져도 증가하지 않는다.

서버 컴포넌트와 Next.js App directory

서버 컴포넌트

  • Next.js에서 route가 로드되면 초기 HTML이 서버에서 렌더링된다.

    • 브라우저에서 HTML이 점진적으로 향상된다.
    • 클라이언트가 Next.js 및 React 클라이언트 측 런타임을 비동기적으로 로드해 애플리케이션에 상호작용을 추가한다.
  • App Router에 있는 모든 컴포넌트는 기본적으로 서버 컴포넌트이다.

    • use client 를 사용해 클라이언트 컴포넌트를 선택적으로 사용할 수 있다.

클라이언트 컴포넌트

  • 애플리케이션에 클라이언트 측 인터랙션을 추가할 수 있다.

    • 즉, 클라이언트에서 hydrated 된다.
    • 클라이언트 컴포넌트는 Pages Router가 항상 작동하는 방식
  • use client

    • 서버와 클라이언트 컴포넌트 모듈 그래프 사이의 경계를 선언하는 컨벤션
    • Server-only 코드와 클라이언트 코드 사이에 위치한다.
    • 파일 최상단에 위치해 cut-off 지점을 정의
    • 해당 컴포넌트, 하위 컴포넌트, import한 모듈 모두 클라이언트 번들의 일부로 간주된다.
      • 진입점(entry point)에서 한번만 정의하면 된다.
  • 서버 컴포넌트 모듈 그래프

    • 컴포넌트가 서버에서만 렌더링
  • 클라이언트 컴포넌트 모듈 그래프

    • 주로 클라이언트에서 렌더링
    • 서버에서 미리 렌더링한 후 클라이언트에서 hydration

서버 컴포넌트 vs. 클라이언트 컴포넌트

언제 사용할까?서버 컴포넌트클라이언트 컴포넌트
데이터 패칭
백엔드 직접 접근
서버에 민감한 정보 유지
서버에 대규모 종속성 유지 / 클라이언트 자바스크립트 줄이기
OX
상호작용 및 이벤트 리스너 추가
State나 생명주기 Effect 사용
browser-only API 사용
State, effect, brower-only API 기반의 커스텀 hook 사용
React Class component 사용
XO

패턴

  • 클라이언트 컴포넌트를 잎(Leaves)으로 이동하기

    • 정적 요소는 서버에서 렌더링해서 성능을 향상시킬 수 있도록
  • 클라이언트와 서버 컴포넌트를 동일한 컴포넌트 트리에서 결합하기

    • 서버 컴포넌트와 클라이언트 컴포넌트는 동일한 컴포넌트 트리에서 결합될 수 있다.
    • 서버에서 React는 결과를 클라이언트로 보내기 전에 모든 서버 컴포넌트를 렌더링하며, 클라이언트 컴포넌트 안에 중첩된 서버 컴포넌트가 포함된다.
      • 클라이언트는 클라이언트 컴포넌트를 렌더링하고, 서버 컴포넌트 결과를 슬롯에 삽입해 병합한다.
    • Next.js에서는 초기 페이지를 로드하는 동안 위 단계의 서버 컴포넌트와 클라이언트 컴포넌트의 렌더링 결과를 서버에서 HTML로 미리 렌더링해, 초기 페이지 로딩 속도가 빨라진다.
  • 클라이언트 컴포넌트 내에 중첩된 서버 컴포넌트

    • 서버 컴포넌트를 클라이언트 컴포넌트로 import 할 수는 없다.
      • 추가적인 서버 왕복이 필요
    • 서버 컴포넌트를 클라이언트 컴포넌트에 props로 전달해야 한다.
      • 서버 컴포넌트에 children props을 사용하기
      • 각 컴포넌트가 독립적으로 렌더링되고, 조정될 수 있다.
      • 배치에 대해서만 책임이 있다.
      • lifting content up
      • children prop이 아니라 어떤 JSX든 상관 없다.
    // app/client-component.tsx
    'use client';
    
    import { useState } from 'react';
    
    export default function ClientComponent({ children } 
    	: { children: React.ReactNode; }) {
    			const [count, setCount] = useState(0);
    
      	return (
          <>
              <button onClick={() => setCount(count + 1)}>{count}</button>
              {children}
          </>
      );
    }
    
    // app/page.tsx
    import ClientComponent from './client-component';
    import ServerComponent from './server-component';
    
    export default function Page() {
      return (
        <ClientComponent>
        <ServerComponent />
        </ClientComponent>
      );
    }
  • 서버에서 클라이언트로 props 전달하기 (Serialization)

    • 클라이언트 컴포넌트에 전달하는 props는 Serialization이 가능해야 한다. 즉, 함수나 Dates 같은 건 안된다.
    • Network Boundary
      • 서버 컴포넌트와 클라이언트 컴포넌트 사이
  • 서버 전용 코드를 클라이언트 컴포넌트에서 제외하기 (Poisoning)

    • API_KEY처럼 보안에 민감한 정보는 서버에서만 접근할 수 있도록 의도적으로 작성된다.
  • server-only 패키지

    • Server-only 모듈을 클라이언트 컴포넌트로 import 할 때 빌드 타임 에러 발생
    • 비슷하게 client-only 패키지도 있다.
  • 데이터 패칭

    • 특별한 이유가 없으면 서버 컴포넌트에서 데이터를 가져오자
  • 서드파티 패키지

    • 많은 client-only 패키지에 아직 use client 지시어가 없다.
    • 타사 컴포넌트를 자체 클라이언트 컴포넌트에서 래핑해야 한다.
    • Provider 컴포넌트는 일반적으로 애플리케이션의 root에 필요한 경우가 많다. 아래서 더 살펴보자.

Context

  • 대부분의 React 애플리케이션을 직간접적으로 context에 의존해 컴포넌트 간에 데이터를 공유한다.

    • 이 context는 서버 컴포넌트에서 직접 사용할 수 없다. 클라이언트 컴포넌트 안에서 context를 사용해야 한다.
    • Next.js가 서버 컴포넌트의 정적 부분을 쉽게 최적화할 수 있도록 최대한 트리 깊숙이 클라이언트 컴포넌트를 렌더링 할 수 있도록 하자
  • 서드 파티 context provider를 서버 컴포넌트에서 렌더링하기

    • 애플리케이션 root 근처에서 렌더링해야 하는 provider를 포함하는 경우
      • use client를 포함해 서버 컴포넌트 내부에서 직접 렌더링할 수 있도록 하자
  • 서버 컴포넌트 간에 데이터 공유하기

    • context가 필요하지 않다.
    • native JavaScript 패턴을 사용할 수 있다.
      • 예를 들어 모듈을 사용해 여러 컴포넌트에서 DB 연결 공유
      • global singleton 패턴
  • 서버 컴포넌트 간에 fetch request 공유하기

    • 페이지나 일부 하위 컴포넌트 간에 fetch 결과를 공유할 수 있다.
    • 데이터를 소비하는 컴포넌트와 함께 데이터 fetch를 배치하기
    • Next.js는 중복 fetch 요청을 제거하고, fetch 캐시에서 동일한 값을 읽는다.

Reference

Getting Started: React Essentials
React Roundtable: Server Components, Suspense, and Actions

profile
Frontend Web Developer

4개의 댓글

comment-user-thumbnail
2023년 6월 7일

굳!

1개의 답글
comment-user-thumbnail
2023년 6월 10일

넘 멋있어요

1개의 답글