모던 리액트 Deep Dive 스터디

4장 - 서버사이드 렌더링

려낭 2024. 11. 28. 02:07
4.1.1 싱글 페이지 애프리케이션의 세상

 

싱글 페이지 애플리케이션이란?

 

렌더링과 라우팅에 필요한 대부분의 기능을 서버가 아닌 브라우저의 자바스크립트에 의존하는 방식.

 

페이지를 불러온 이후에는 서버에서 HTML을 내려받지 않고 하나의 페이지에서 모든 작업을 처리한다.

 

 

페이지 전환 시 새로운 HTML페이지를 요청하는 게 X

자바스크립트에서 다음 페이지의 렌더링에 필요한 정보만 HTTP요청으로 가져온 다음,

그 결과를 바탕으로 <body/> 내부에 DOM을 추가, 수정, 삭제하는 방법으로 페이지가 전환된다.

 

전통적인 방식의 애플리케이션과 싱글 페이지 애플리케이션의 작동 비교

 

과거에는 페이지 전환이 발생할 때마다 새롭게 페이지를 요청, HTML 페이지를 다운로드해 파싱하는 작업을 거쳤다.

>> 페이지를 처음부터 새로 그려야 해서 부자연스러움

 

 

그러나 이러한 페이지 전환을 모두 자바스크립트로 한다면 최초 한번 모든 리소스를 다운받은 후, 페이지를 전환할 때 추가로 리소스를 다운할 필요가 없다.

 

경우에 따라 페이지 전체를 새로 렌더링하는 것 X >> 페이지 전환에 필요한 일부 영역만 다시 그리기 때문에 매끄러운 UI

 

 

4.1.2 서버 사이드 렌더링이란?

 

최초에 사용자에게 보여줄 페이지를 서버에서 렌더링해 빠르게 사용자에게 화면을 제공하는 방식.

 

싱글 페이지 애플리케이션(SPA) VS 서버 사이드 렌더링(SSR)

 

싱글 페이지 애플리케이션

  • 동작 방법
    • 사용자가 페이지에 들어오면 빈 HTML에 필요한 데이터와 컴포넌트를 클라이언트(브라우저)에서 렌더링
    • 서버는 필요한 데이터(API)를 제공하고, 렌더링은 브라우저가 처리한다.
  • 장점
    • 빠른 페이지 전환 > 새로고침 없이 부드럽게 작동(앱처럼 동작함)
    • 트래픽 감소 > 서버로부터 전체 HTML을 계속 받지 않아도 됨.
  • 단점
    • 처음에 모든 자바스크립트를 다운해야하기 때문에 초기 로딩 속도가 느리다.
    • 검색 엔진이 자바스크립트를 실행하지 않으면 콘텐츠를 못 읽기 때문에 검색 엔진 최적화가 어렵다.

 

서버 사이드 렌더링

  • 동작 방법 
    • 사용자가 페이지 요청 > 서버에서 필요한 데이터를 가져와 완성된 HTML을 생성 > 브라우저로 전달 > 화면에 표시
    • 이후에 자바스크립트가 실행되어 페이지에 동작을 추가한다.
  • 장점
    • 서버에서 만들어진 HTML을 바로 보여줄 수 있어 초기 로딩이 빠르다.
    • 검색 엔진과 SNS공유 등 메타데이터 제공이 쉽다. (검색 엔진 최적화에 유용하기 때문)
    • 모든 요청이 완전히 완료된 이후에 완성된 페이지를 제공하기 때문에 누적 레이아웃 이동이 적다.
    • 사용자의 디바이스 성능에 비교적 자유롭다.
    • 인증 혹은 민감한 작업을 서버에서 수행하고 그 결과만 브라우저에 제공하기 때문에 보안에 좀 더 안전하다.
  • 단점
    • 모든 요청마다 서버가 HTML을 생성해야 해서 부담이 크다.
    • 페이지 이동마다 서버 요청이 발생해 페이지 전환이 느리다.

 

 

4.1.3 SPA와 SSR을 알아야 하는 이유

 

서버 사이드 렌더링 역시 만능이 아니다

 

웹페이지의 설계와 목적, 우선순위에 따라 싱글 페이지 애플리케이션이 더 효율적일 수도 있다.

 

 

싱글 페이지 애플리케이션과 서버 사이드 렌더링 애플리케이션

  • 가장 뛰어난 싱글 페이지 애플리케이션은 가장 뛰어난 멀티 페이지 애플리케이션보다 낫다.
  • 평균적인 싱글 페이지 애플리케이션은 평균적인 멀티 페이지 애플리케이션보다 느리다.

 

4.2 서버 사이드 렌더링을 위한 리액트 API 살펴보기

리액트는 프런트엔드 라이브러리로 브라우저 자바스크립트 환경에서 렌더링할 수 있는 방법을 제공하지만 서버에서 렌더링 하는 API 또한 제공한다. 해당 API는 당연히 Node.js와 같은 서버 환경에서만 실행할 수 있으며 window 환경에서 실행 시 에러가 발생할 수 있다.
API를 확인하기 위해서는 react-dom/server.js를 확인하면 된다.

 

4.2.1 renderToString

 

인수로 넘겨받은 리액트 컴포넌트를 렌더링해 HTML문자열로 반환하는 함수

 

서버 사이드 렌더링을 구현하는 데 가장 기초적인 API

최초의 페이지를 HTML로 먼저 렌더링하는 역할을 하는 함수

 

예시 코드

const result = ReactDOMServer.renderToString(
 React.createElement('div', { id: 'root' }, <SampleComponent />),
)

 

동작 방법

  1. 서버에서 리액트 컴포넌트를 준비한다.
  2. renderToString 함수를 사용해 컴포넌트를 HTML 문자열로 변환한다.
  3. 변환된 HTML 문자열을 브라우저로 보내면, 브라우저는 이를 화면에 렌더링한다.
  4. 이후 리액트가 클라이언트에서 동작을 재연결 하여 인터랙티브한 동작을 추가한다.

 

4.2.2 renderToStaticMarkup

 

renderToString과 매우 유사한 함수로 두 함수 모두 리액트 컴포넌트를 기준으로 HTML 문자열을 만든다.

차이점은 앞서 루트 요소에 추가한 data-reactroot와 같은 리액트에서만 사용하는 추가적인 DOM 속성을 만들지 않는다는 점이다.
해당 함수를 통해 렌더링을 수행하면 리액트에서 제공하는 useEffect와 같은 브라우저 API를 절대로 실행할 수 없다.

 

4.2.3 renderToNodeStream

 

React에서 서버 사이드 렌더링(SSR)을 할 때, HTML을 스트리밍 방식으로 브라우저에 전송하는 기능을 제공한다.

기존의 renderToString과는 달리, HTML을 한 번에 모두 생성하지 않고, 조각(chunk) 단위로 나눠서 전송한다.

 

 

  • 차이점:
    • renderToString: 전체 HTML을 한 번에 생성하고 브라우저로 보낸다. HTML 생성이 끝날 때까지 기다려야 브라우저에 표시된다.
    • renderToNodeStream: HTML을 한 번에 모두 생성하지 않고, 스트림 형태로 조금씩 브라우저에 전송한다. 브라우저는 전송받은 부분부터 바로 렌더링할 수 있다.
  • 장점:
    • 빠른 초기 렌더링: 데이터를 모두 처리하지 않아도, 사용자가 부분적으로라도 페이지를 볼 수 있다.
    • 큰 데이터 처리 최적화: 메모리를 효율적으로 사용하고, 서버 부하를 줄일 수 있다.

 

  • 사용 사례:
    • 대규모 HTML이나 복잡한 UI를 렌더링할 때 초기 표시 시간을 줄이고 싶을 때.
    • 블로그, 쇼핑몰 같은 곳에서 빠른 사용자 경험이 중요한 경우.

 

4.2.4 renderToStaticNodeStream

 

renderToNodeStream과 제공하는 결과물은 동일하나, renderToStaticMarkup과 마찬가지로 리액트 자바스크립트에 필요한 리액트 속성이 제공되지 않는다.
hydrate를 할 필요가 없는 순수 HTML 결과물이 필요할 때 사용하는 메서드다.

 

4.2.5 hydrate

 

React에서 서버 사이드 렌더링(SSR)으로 미리 생성된 HTML에 이벤트와 React 상태를 연결하는 과정(즉, 클라이언트 쪽에서의 초기화 작업)을 수행한다.

 

역할

  • 서버에서 이미 렌더링된 HTML을 재사용하면서, 클라이언트 측에서 React 컴포넌트를 "활성화"시킨다.
  • HTML만으로는 동적인 동작(클릭, 입력 등)이 없기 때문에, 브라우저에서 이를 연결해주는 작업이 필요하다.

 

사용 방법

import { hydrate } from 'react-dom';
import App from './App';

const rootElement = document.getElementById('root');
hydrate(<App />, rootElement);

 

HTML에 미리 렌더링된 <div id="root"> 내부 내용을 기준으로 React 컴포넌트를 연결.

 

 

차이점:

  • render: HTML이 없는 상태에서 React 컴포넌트를 처음부터 렌더링한다.
  • hydrate: 이미 서버에서 렌더링된 HTML이 있는 상태에서 React 컴포넌트를 연결하여, DOM 업데이트를 최소화한다.

주의할 점:

  • 서버와 클라이언트의 렌더링 결과가 다르면 경고 메시지가 나타날 수 있다.
  • 이런 문제를 방지하려면 서버와 클라이언트의 렌더링 결과를 정확히 맞추는 것이 중요하다.