본문 바로가기

Next.js

CSR부터 RSC까지 렌더링 방식의 변화

Next.js의 서버 컴포넌트와 서버 액션을 보면서 관련된 개념들이 헷갈렸습니다.

그래서 그 흐름을 정리하고, 각각의 렌더링을 비교해 보겠습니다.

 

 

CSR(SPA) - 2013

CSR은 브라우저에서 코드를 사용자 인터페이스로 변환하는 렌더링 방법입니다

CSR 렌더링

렌더링 순서

  1. 클라이언트가 요청합니다.
  2. 서버가 빈 HTML을 응답으로 보냅니다.
  3. 클라이언트가 JS 코드를 로드합니다.
  4. 클라이언트는 HTML을 생성하고, 하이드레이션 하면서 상호작용이 가능한 UI를 만듭니다.
💡 하이드레이션 (hydration)
비유하자면, 말라비틀어진 HTML에 상호작용이 가능하게 물을 주는 것과 같습니다.
구성 요소를 렌더링 하고 이벤트 핸들러를 연결합니다.

약점

  • SEO에 적합하지 않습니다. 크롤러가 인덱싱 하기에 번들 사이즈가 크고 다운로드 속도가 느립니다.
  • 성능문제도 있습니다. 브라우저의 과도한 작업으로 로딩 속도가 느립니다.

 

 

SSR - 2015

Next.js가 CSR의 단점을 보완했습니다. 서버가 HTML 렌더링을 담당하면서, 빈 HTML이 아닌 전체 HTML으로 응답합니다.

브라우저의 작업이 줄어들어 초기 페이지 로드 시간이 개선됩니다. 다만, 성능 개선은 측면은 미미한 부분이 있습니다.

렌더링 순서

  1. 클라이언트가 요청합니다.
  2. 서버가 전체 HTML을 렌더링(아직 상호작용 불가)하고 응답으로 돌려줍니다.
  3. 클라이언트가 JS 코드를 로드합니다.
  4. 클라이언트는 HTML에 하이드레이션하면서 상호작용이 가능한 UI를 만듭니다.

결과적으로 SSR은 SEO를 개선하고 브라우저는 사용자에게 빈 화면 대신 UI를 보여줄 수 있습니다.

약점

  • 각 단계마다 전체 앱에 대한 작업이 완료되어야, 다음 단계가 시작되는 waterfall 문제가 있습니다.
    waterfall: fetch data (server) → render to HTML (server) → load JS code (client) → hydrate (client)
    • 서버는 HTML을 클라이언트로 전송하기 전, 모든 데이터가 준비될 때까지 기다려야 합니다.
    • 클라이언트는 모든 구성 요소에 대해 JS를 로드해야, 하이드레이션을 시작할 수 있습니다.
    • 이와 비슷하게, 모든 구성 요소가 하이드레이션 되어야, 사용자는 상호작용이 가능합니다.

 

 

Suspense for SSR - 2022

기존 SSR 문제를 보완하기 위해 React 18은 Suspense와 React.lazy를 활용합니다. 

Suspense 컴포넌트는 앱을 더 작은 독립 단위로 나누고, React.lazy는 필요할 때만 컴포넌트를 로드합니다.

다음으로 SSR의 약점을 어떻게 보완했는지 설명하겠습니다.

 

HTML streaming on the server

먼저 표시된 전체 페이지

 

 

  • Suspense 컴포넌트로 복잡한 컴포넌트를 래핑(wrapping)하면, 나머지 페이지는 먼저 로딩될 수 있습니다.
  • 서버는 래핑한 컴포넌트가 준비되면, 추가 HTML 스트리밍과  JS로 그 위치 정보를 전송합니다.
  • 결론적으로 서버는 HTML 전체가 준비되기까지 기다리지 않고, 초기 HTML을 따로 더 일찍 보냅니다.

 

Selective hydration on the client

먼저 hydration된 페이지 일부

 

 

Hydrating the page before all the code has loaded

  • 여전히 있는 문제는 JS도 마찬가지로 로드되기 전까지 하이드레이션이 시작될 수 없다는 것입니다.
  • 이는 React.lazy 코드 분할을 통해 해결할 수 있습니다. 복잡한 컴포넌트가 JS를 로드하기 전에 준비된 앱의 일부를 하이드레이션 할 수 있습니다.

 

상호작용이 가능한 먼저 hydration된 페이지 일부

 

 

Interacting with the page before all the components have hydrated

  • 이제 하이드레이션 된 나머지 페이지는 상호작용이 가능합니다. 아직 복잡한 컴포넌트는 할 수 없지만요.
  • 그리고 리액트는 하이드레이션 대기 중인 컴포넌트가 있더라도 사용자의 상호작용에 따라 우선순위를 결정합니다. 컴포넌트를 하이드레이션 하던 중이더라도, 다른 컴포넌트를 클릭하면 먼저 동기적으로 하이드레이션 합니다.

약점

  • 그럼에도 결국에는 전체 JS 코드를 전부 로드해야 합니다.
  • 상호작용이 필요 없는 페이지들 또한, 결국엔 하이드레이션 됩니다.
  • 여전히 클라이언트 측에서 JS를 로드하고 실행합니다. 이는 사용자의 디바이스 성능에 영향을 크게 받습니다.

 

 

React Server Components 2024

서버와 클라이언트 컴포넌트의 강점을 살리고, 로드 시간 등을 최적화합니다.

새로운 패러다임의 시작으로, 기본 컴포넌트는 상태가 변하지 않는 서버 컴포넌트가 되었습니다.

 

Server Components

  • 서버에서만 실행되며, JS 번들 크기에 영향을 주지 않습니다. 렌더링에 필요한 코드를 최소한으로 받습니다.
  • 브라우저에서 처리되는 작업이 줄고, 성능 좋은 서버가 그 작업을 하면서 클라이언트-서버 waterfall이 제거됩니다.
  • 서버 컴포넌트는 점진적으로 렌더링 되며, UI 단위를 클라이언트에 점진적으로 스트리밍합니다.

렌더링

  1. 브라우저가 페이지를 요청하고, Next.js는 React에게 해당 서버 컴포넌트를 렌더링 하도록 지시합니다.
  2. React는 '서버 컴포넌트'와 '서버 컴포넌트이기도 한 컴포넌트'를 렌더링하여 RSC payload라고 하는 특수 포맷으로 변환합니다.
  3. 한편으로는 클라이언트 컴포넌트 인스트럭션도 함께 준비됩니다.
  4. Next.js는 RSC payload와 클라이언트 컴포넌트 자바스크립트 명령어를 사용하여 서버에서 HTML을 생성합니다. 이 HTML은 브라우저로 스트리밍 되어 사용자에게 보입니다.
  5. Next.js는 React가 각 UI 단위를 렌더링 할RSC payload를 스트리밍합니다.
  6. React는 RSC payload와 클라이언트 컴포넌트 인스트럭션을 사용해 UI를 점진적으로 렌더링합니다.
  7. 완료되면 최종 UI 상태가 사용자에게 표시됩니다.
  8. 클라이언트 컴포넌트는 하이드레이션을 거쳐 상호작용이 가능해집니다.
💡 RSC Payload
리액트 서버 컴포넌트 트리의 압축된 바이너리로, React가 DOM을 업데이트하는 데 사용됩니다.

포함되는 내용:
- 서버 컴포넌트 렌더링 결과
- 클라이언트 컴포넌트가 렌더링 될 위치, JS 파일에 대한 참조를 위한 플레이스 홀더
- 서버 컴포넌트에서 클라이언트 컴포넌트로 전달되는 모든 Props

 

Client Components

CSR, SSR이 모두 가능한 기존 리액트 컴포넌트입니다.

상태 관리, 브라우저 API 접근, 상호작용 등이 가능한 클라이언트 환경에 접근할 수 있습니다.

 

렌더링

  • 페이지 내 이동일 경우: 전적으로 클라이언트에서 렌더링 됩니다.
    1. 클라이언트 컴포넌트 JS 번들이 다운로드되고 파싱 됩니다.
    2. 리액트는 RSC Payload로 클라이언트와 서버 컴포넌트 트리를 조정하고, DOM을 업데이트합니다.
  • 전체 페이지 로드일 경우: 서버 컴포넌트 렌더링 과정과 동일합니다.

 

 

 

 

 

출처

What are React Server Components? Understanding the Future of React Apps
Making Sense of React Server Components

New Suspense SSR Architecture in React 18

 

 

'Next.js' 카테고리의 다른 글

[Next.js] 라우팅과 네비게이션의 동작  (0) 2024.06.14
SSR(Server-side Rendering)  (0) 2023.06.30
CSR(Client-side Rendering)  (0) 2023.06.30