[React]Server Components

Yewon Jeong·2023년 5월 25일
0

react,nextjs

목록 보기
1/1

nextjs에서 서버 컴포넌트와 클라이언트 컴포넌트를 사용할 수 있다. 최상단 app 폴더에서 기본적으로 모든 컴포넌트들은 서버 컴포넌트 이다.

Server Components

server component는 서버에서 렌더되는 컴포넌트들이다.

위와 같이 페이지를 컴포넌트 단위로 나눠봤을때, 컴포넌트들의 상당수가 인터랙티브 하지 않다. 사용자와의 인터랙션이 없다면 서버 컴포넌트로 렌더링 할 것을 고려할 수 있다.

Server Components 이점

  • 서버에서 render를 수행하기 때문에 api를 통한 데이터 요청의 latency를 줄일 수 있고, 클라이언트에서의 api 호출을 제거하여 client-server waterfall를 막을 수 있다.
  • 서버에서 동작하고 렌더링 된다는 특성상 다양한 종류의 백엔드 리소스에 접근할 수 있다.
  • 초기로드 속도 향상
  • js 번들사이즈 축소 : HTML이 아닌 특별한 형태로 렌더링되어 클라이언트에 전달되기 때문에 클라이언트로 전달되는 번들 사이즈 또한 감소시킬 수 있다.

제로 번들 사이즈 컴포넌트

서버 컴포넌트 코드는 브라우저에 다운로드 되지 않고 서버에서 미리 렌더링된 static content를 전달하기 때문에 패키지를 추가해도 번들 사이즈에 영향을 끼치지 않는다. React 서버 컴포넌트를 사용하면, 컴포넌트를 정기적으로 다시 가져올 수 있다. 새 데이터가 있을 때 리렌더링 되는 컴포넌트가 서버에서 실행되므로 클라이언트에 전송되는 코드의 양을 제한 할 수 있다.

자동 코드 분할

Code splitting : 하나의 거대한 자바스크립트 번들을 여러 개의 작은 번들로 쪼개어 필요할 때마다 클라이언트로 전송하는 방법
서버 컴포넌트에서 import 되는 모든 클라이언트 컴포넌트는 code splitting 포인트로 간주됨.


Client components

Client components는 어플리케이션 내에서 클라이언트 사이드 인터랙션을 가능하게 한다.

"use client" 지시자

"use client" 는 클라이언트 컴포넌트를 선언하는 컨벤션이다. 클라이언트 컴포넌트 최상단에 위치한다. 해당 컴포넌트의 자식 컴포넌트들 또한 클라이언트 번들에 포함된다. "use client"가 선언되지 않은 컴포넌트는 디폴트로 서버 컴포넌트가 된다.

'use client';
 
import { useState } from 'react';
 
export default function Counter() {
  const [count, setCount] = useState(0);
 
  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>Click me</button>
    </div>
  );
}

각각의 사용 케이스

아래는 일반적으로 권장되는 use case이다.

권장되는 패턴들

클라이언트 컴포넌트는 최하위 계층으로

1. 가능하다면 클라이언트 컴포넌트의 계층은 낮을수록 좋다.

예를 들어, 정적 요소들(로고,링크 등)와 상태가 업데이트되는 인터랙티브한 검색바를 포함하는 레이아웃이 있다고 하자. 이 경우 전체 레이아웃을 클라이언트 컴포넌트로 정의할 것이 아니라, 서버 컴포넌트로 정의된 레이아웃 하위에 인터랙티브한 로직들을 담은 클라이언트 컴포넌트를 자식 컴포넌트로 정의하는 것이다. 즉 레이아웃 전체를 클라이언트 사이드에서 그리지 않는다.

// SearchBar is a Client Component
import SearchBar from './searchbar';
// Logo is a Server Component
import Logo from './logo';
 
// Layout is a Server Component by default
export default function Layout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <>
      <nav>
        <Logo />
        <SearchBar />
      </nav>
      <main>{children}</main>
    </>
  );
}

2. 클라이언트 컴포넌트에서 서버 컴포넌트를 임포트 하는 것은 지원되지 않는다.

3. 다만 서버 컴포넌트는 클라이언트 컴포넌트에게 또 다른 서버 컴포넌트를 자식으로 넘겨주는 건 가능

'use client';
 
import { useState } from 'react';
 
export default function ExampleClientComponent({
  children,
}: {
  children: React.ReactNode;
}) {
  const [count, setCount] = useState(0);
 
  return (
    <>
      <button onClick={() => setCount(count + 1)}>{count}</button>
 
      {children}
    </>
  );
}

"server only" ,"client-only" 컴포넌트

서버 컴포넌트(Example.server.js)

  • 서버에서만 렌더링되는 컴포넌트
  • 유저 인터랙션 제공 불가(useState,useReducer,useEffet 와 같은 state/effect 사용 불가)
  • dom 과 같은 브라우저 api 사용불가
  • 데이터베이스/내부 서비스/ 파일시스템과 같은 server-only 데이터 사용가능
  • 클라이언트 컴포넌트 prop로 serializable한 데이터 전달 가능(함수 전달 불가)

클라이언트 컴포넌트(Example.client.js)

  • 클라이언트에서 렌더링 되거나 SSR을 통해 서버에서 렌더링 되는 컴포넌트
  • 유저 인터랙션 사용 가능
  • 서버 컴포넌트 도입 전 리액트 컴포넌트
  • server-only 데이터 사용 불가
  • state / effects / 브라우저 api 사용 가능

결국 리액트 컴포넌트는 목적에 따라 서버(data fetching),클라이언트(유저 인터랙션)으로 분리 될 수 있다.

"server only" ,"client-only" 패키지

서버 코드가 의도치 않게 클라이언트에서 사용되는 것을 방지하기 위해서
server-only 패키지를 사용할 수 있다.

npm install server-only
import 'server-only';
 
export async function getData() {
  const res = await fetch('https://external-service.com/data', {
    headers: {
      authorization: process.env.API_KEY,
    },
  });
 
  return res.json();
}

위와 같이 server-only 패키지를 임포트 시키면 해당 코드가 클라이언트 컴포넌트에서 임포트되었을때 빌드에러를 실행시킬수 있다. 마찬가지로 client-only 패키지를 사용해 클라이언트사이드에서만 실행되어야 하는 코드(ex.윈도우 객체에 접근하는 경우)가 서버 컴포넌트에서 실행되는 것을 방지할 수 있다.

Data Fetching

특별한 이유가 있지 않은 한 data fetching은 서버 컴포넌트에서 이루어지도록 하는 것이 좋다. 이는 더 나은 성능과 사용자 경험을 제공한다.

Third-party packages

useState,나 useEffect 등을 사용하여 클라이언트 사이드에서 실행되어야하는 써드파트 패키지의 컴포넌트들의 경우 "use strict" 가 선언되지 않은 옛날 버전의 패키지들이 다수이다. 따라서 서버사이드에서 실행되어 에러가 발생하는 경우 아래와 같이 조치 하면 클라이언트에서 실행되게 하면 에러가 해결된다.

import { Carousel } from 'acme-carousel';
 
export default function Page() {
  return (
    <div>
      <p>View pictures</p>
 
      {/* Error: `useState` can not be used within Server Components */}
      <Carousel />
    </div>
  );
}
'use client';
 
import { Carousel } from 'acme-carousel';
 
export default Carousel;

Server Component는 SSR의 대채제가 아니다.

그러나 사용자 경험 향상을 위해 함께 사용할 수는 있다.클라이언트 사이드 렌더링을 사용하는 앱의 경우 페이지 진입 시 HTML, 자바스크립트 그리고 모든 데이터가 로드되고 컴포넌트 렌더링이 끝나기 전까지 사용자는 아무런 기능이 없는 빈 화면만 보게 된다. 자바스크립트 번들의 용량이 크거나 사용자의 네트워크 속도가 느릴수록 사용자는 오랜 시간 빈 화면을 보게 되고 이는 사용자 경험을 저하시키게 된다.
반면 Next.js와 같은 서버 사이드 렌더링을 사용하는 애플리케이션은 클라이언트 애플리케이션의 자바스크립트 파일을 서버에서 먼저 HTML로 렌더링 한다. 페이지가 정상적으로 동작하기 위해서는 자바스크립트 번들이 모두 다운로드되고 hydration이 완료되어야 하지만 빈 화면 대신 데이터가 존재하는 HTML을 제공함으로써 무거운 자바스크립트 파일이 다운로드되는 동안 사용자에게 의미 있는 콘텐츠를 제공할 수 있다.

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

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

서버 컴포넌트와 서버 사이드 렌더링은 서버에서 렌더링 된다는 유사점이 있지만 해결하고자 하는 문제점이 다르다.

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

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

  • 클라이언트 측의 상태(state)를 유지하면서 서버 컴포넌트를 다시 가져올 수 있다.이는 주요 전송 메커니즘이 HTML보다 훨씬 풍부하기 때문이다. 따라서, 내부 상태(e.g 검색 입력 텍스트, 포커스, 텍스트 선택)를 없애지 않고 서버에서 렌더링 한 부분(e.g 검색 결과 목록)을 다시 가져올 수 있게 한다.



참고
https://nextjs.org/docs/getting-started/react-essentials
https://www.patterns.dev/posts/react-server-components
https://ui.toast.com/weekly-pick/ko_20210119
https://tech.kakaopay.com/post/react-server-components/#:~:text=%EC%84%9C%EB%B2%84%20%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8%EB%8A%94%20%ED%81%B4%EB%9D%BC%EC%9D%B4%EC%96%B8%ED%8A%B8%20%EC%83%81%ED%83%9C,%EB%A0%8C%EB%8D%94%EB%A7%81%ED%95%98%EC%97%AC%20%EC%A0%84%EB%8B%AC%ED%95%A0%20%EC%88%98%20%EC%9E%88%EC%8A%B5%EB%8B%88%EB%8B%A4.

profile
일단 하는 중

0개의 댓글