Nextjs client, server component에 대해

이희제·2024년 6월 24일

Nextjs에서 client 컴포넌트란 무엇이고 Server component와의 관계가 어떻게 되는 것인이 확인을 위한 문서 정리

Server Components

기본으로 다 Server Component이다.

렌더링 전략이 3가지가 있다.

  1. static rendering (디폴트)
  • 빌드 타임에 렌더링된다. 또는 data revalidation 후
  • 렌더링 결과가 캐시된다.
  1. 동적 렌더링 (Dynamic Rendering)
  • 요청 시점에 렌더링(각 유저의)
  • dynamic function이나 캐시되지 않은 데이터 요청이 발견되었을 때 동적 렌더링으로 전환
    • dynamic function - cookies(), headers(), searchParams()
  1. 스트리밍 (Streaming)
  • React Suspense랑 사용함
  • 작업이 청크로 분할되어 준비되는 대로 클라이언트로 전송
  • 클라이언트로 스트리밍 가능 -> 준비되는대로, 이로 인해 페이지의 일부를 빨리 볼 수 있음

Client Components

클라이언트 사이드에서 hydration(수화)가 발생하는 컴포넌트이다.
-> 일반적으로 알고있는 리액트 컴포넌트 개념으로 생각하자. (참고)

서버사이드에서 prerednered 된다

"use client" 디렉티브를 사용해서 클라이언트 컴포넌트라고 명시한다.
해당 컴포넌트 내, 자식 모두 클라이언트 번들에 포함된다.

여러 컴포넌트에 "use client"를 정의할 수 있는데 이는 여러 개의 클라이언트 번들로 쪼갤 수 있게 해준다.

그러나 클라이언트 컴포넌트의 자식까지 모두 "use client"를 명시해줄 필요는 없다. (자식, import된 모듈 등 모드 클라이언트 번들의 일부로 간주된다.)

렌더링 방식은?

풀 페이지 로드를 할 때랑 후속 탐색인지에 따라 달라진다.

Full page load

  • 풀 페이지 로드(최초 방문, 새로고침) - 클라, 서버 컴포넌트 모두에 대해 서버에서 정적 HTML 미리보기 렌더링 (React's APIs 사용)
  • 모두 서버 사이드에서 렌더링되는 것
  1. 리액트는 Server component는 React Server Component Payload (RSC Payload) 형식으로 렌더링 (클라이언트 컴포넌트의 참조가 포함되어 있다.)

    → 클라, 서버 관계 없이 모두 RSC Payload 기반으로 렌더링

  2. Next.js는 RSC Payload Client Component JavaScript 명령을 사용하여 서버에서 HTML을 렌더링

    → JavaScript 명령은 클라 사이드에서 어떤 동작을 해야되는지 명시해둔 것일까? (수화 시에 필요한 js 명령)

이제 클라이언트 사이드에서는

  1. HTML은 경로의 빠르고 비상호작용적인 초기 미리보기를 즉시 보여주는 데 사용
  2. React 서버 컴포넌트 페이로드는 클라이언트 및 서버 컴포넌트 트리를 조정하고 DOM을 업데이트하는 데 사용
  3. JavaScript 명령은 클라이언트 컴포넌트를 hydrate하여 UI를 상호작용 가능하게 만든다.

hydration은 이벤트 리스너를 DOM에 붙이는 과정이다. 즉, 상호작용이 가능해진다. hydrateRoot라는 React API를 사용한다.

Subsequent Navigations

클라이언트 컴포넌트는 클라이언트 사이드에서 렌더링된다. (without the server-rendered HTML.)

서버 컴포넌트와 클라이언트 컴포넌트의 조합 패턴

공식문서에서 추천하는 조합 방식에 대해

서버 컴포넌트 사용 패턴

  1. 컴포넌트 사이 데이터 공유
  • React Context를 사용하는 것 대신에 fetch를 사용할 수 있다.
    → 같은 데이터에 대해서 중복 호출이 되지 않기 때문에 걱정하지 않아도 된다. (알아서 메모이제이션을 한다.)
  1. 서버 전용 코드를 클라이언트 코드에서 제외
  • 자바스크립트 모듈은 클라이언트, 서버 컴포넌트 모두에 사용될 수가 있다. (재사용을 위한 api 요청함수를 생각하자)

  • 서버에서만 동작하도록 하기 위해 server-only 패키지를 사용해서 서버 사이드에서만 동작한다는 것을 보장할 수 있다. (클라이언트 컴포넌트에서 import 시 오류가 발생한다.)

  1. 서드 파티 라이브러리 사용
  • 서버 컴포넌트에서 바로 import 시에 동작하지 않는 경우가 있다.
  • 서드 파티 컴포넌트를 클라이언트 컴포넌트로 명시적으로 감싸주면 된다.
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


import Carousel from './carousel'
 
export default function Page() {
  return (
    <div>
      <p>View pictures</p>
 
      {/*  Works, since Carousel is a Client Component */}
      <Carousel />
    </div>
  )
}

클라이언트 컴포넌트 사용 패턴

  1. 트리의 하단으로 클라이언트 컴포넌트 옮기기
  • js 번들 사이즈를 줄이기 위해 클라이언트 컴포넌트를 컴포넌트 트리 하단으로 옮기는 것을 권장한다.

    → 클라이언트 컴포넌트를 쪼개서 각각 분리되어 번들에 포함되도록 하는 것

  1. 서버 컴포넌트에서 클라이언트 컴포넌트로 props 넘기기(Serialization)

서버와 클라이언트 컴포넌트의 인터리빙

  • 요청-응답 라이프사이클 동안 코드가 서버에서 클라이언트로 이동한다. 클라이언트에서 서버의 데이터나 리소스에 접근해야 하는 경우, 서버로 새로운 요청을 보내야 하며, 서버와 클라이언트 간을 계속해서 전환할 수는 없다.

  • 서버로 새로운 요청이 있을 때, 모든 서버 컴포넌트는 클라이언트 컴포넌트 내에 중첩된 경우도 포함하여 먼저 렌더링. 렌더링된 결과(RSC 페이로드)는 클라이언트 컴포넌트의 위치에 대한 참조를 포함한다. 그런 다음, 클라이언트에서 React는 RSC 페이로드를 사용하여 서버와 클라이언트 컴포넌트를 단일 트리로 결합한다.

  • 클라이언트 컴포넌트에서 서버 컴포넌트를 import하는건 지원하지 않는다.

  • 다만 서버 컴포넌트를 클라이언트 컴포넌트의 props로 넘겨주는 것은 가능하다.
    ex) children으로 넘겨주는 방식이 일반적이다.
    -> 클라이언트 컴포넌트에 "slot"을 만든다.

참고
https://saengmotmi.netlify.app/react/what-is-rsc/

profile
그냥 하자

0개의 댓글