반응 스냅을 사용하여 경로 사전 렌더링

서버 측 렌더링이 아니지만 React 사이트의 속도를 높이고 싶으신가요? 사전 렌더링을 사용해 보세요.

react-snap는 사이트의 페이지를 정적 HTML 파일로 사전 렌더링하는 서드 파티 라이브러리입니다. 이렇게 하면 애플리케이션의 첫 페인트 시간이 개선될 수 있습니다.

다음은 시뮬레이션된 3G 연결 및 휴대기기에 로드된 사전 렌더링 유무에 따른 동일한 애플리케이션의 비교입니다.

로드 비교를 나란히 표시합니다. 사전 렌더링을 사용하는 버전은 4.2초 더 빠르게 로드됩니다.

이것이 왜 유용할까요?

대규모 싱글페이지 애플리케이션의 주요 성능 문제는 사용자가 실제 콘텐츠를 볼 수 있기 전에 사이트를 구성하는 JavaScript 번들의 다운로드가 완료될 때까지 기다려야 한다는 점입니다. 번들 크기가 클수록 사용자가 더 오래 기다려야 합니다.

이를 해결하기 위해 많은 개발자는 브라우저에서만 애플리케이션을 부팅하는 대신 서버에서 애플리케이션을 렌더링하는 접근 방식을 취합니다. 페이지/경로 전환이 있을 때마다 전체 HTML이 서버에서 생성되어 브라우저로 전송되므로 첫 페인트 시간이 줄어들지만 Time to First Byte가 느려집니다.

사전 렌더링은 서버 렌더링보다 덜 복잡하지만 애플리케이션의 첫 번째 페인트 시간을 개선하는 방법도 제공하는 별도의 기법입니다. 헤드리스 브라우저 또는 사용자 인터페이스가 없는 브라우저는 빌드 시간에 모든 경로의 정적 HTML 파일을 생성하는 데 사용됩니다. 그런 다음 이러한 파일을 애플리케이션에 필요한 JavaScript 번들과 함께 제공할 수 있습니다.

react-snap

react-snapPuppeteer를 사용하여 애플리케이션의 여러 경로에 대해 사전 렌더링된 HTML 파일을 만듭니다. 시작하려면 개발 종속 항목으로 설치합니다.

npm install --save-dev react-snap

그런 다음 package.jsonpostbuild 스크립트를 추가합니다.

"scripts": {
  //...
  "postbuild": "react-snap"
}

이렇게 하면 애플리케이션의 새 빌드가 생성될 때마다 react-snap 명령어가 자동으로 실행됩니다 (npm build).

마지막으로 애플리케이션 부팅 방식을 변경해야 합니다. src/index.js 파일을 다음과 같이 변경합니다.

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';

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

if (rootElement.hasChildNodes()) {
  ReactDOM.hydrate(<App />, rootElement);
} else {
  ReactDOM.render(<App />, rootElement);
}

ReactDOM.render만 사용하여 루트 React 요소를 DOM에 직접 렌더링하는 대신, 이 메서드는 하위 노드가 이미 있는지 확인하여 HTML 콘텐츠가 사전 렌더링되었는지 (또는 서버에서 렌더링되었는지) 확인합니다. 이 경우 ReactDOM.hydrate이 대신 사용되어 이벤트 리스너를 새로 만드는 대신 이미 생성된 HTML에 연결합니다.

이제 애플리케이션을 빌드하면 크롤링되는 각 경로의 페이로드로 정적 HTML 파일이 생성됩니다. HTML 요청의 URL을 클릭한 다음 Chrome DevTools 내에서 미리보기 탭을 클릭하여 HTML 페이로드가 어떻게 표시되는지 확인할 수 있습니다.

전후 비교 후반부 샷에는 콘텐츠가 렌더링된 모습이 보입니다.

스타일이 지정되지 않은 콘텐츠가 깜박임

이제 정적 HTML이 거의 즉시 렌더링되지만 기본적으로 스타일이 지정되지 않은 상태로 유지되므로 '스타일이 지정되지 않은 콘텐츠 플래시' (FOUC)가 표시되는 문제가 발생할 수 있습니다. 이는 CSS-in-JS 라이브러리를 사용하여 선택기를 생성하는 경우 특히 두드러집니다. 스타일을 적용하려면 JavaScript 번들의 실행이 완료되어야 하기 때문입니다.

이를 방지하려면 중요 CSS 또는 초기 페이지 렌더링에 필요한 최소 CSS를 HTML 문서의 <head>에 직접 인라인으로 추가할 수 있습니다. react-snap는 내부적으로 다른 서드 파티 라이브러리인 minimalcss를 사용하여 여러 경로의 중요한 CSS를 추출합니다. package.json 파일에 다음을 지정하여 사용 설정할 수 있습니다.

"reactSnap": {
  "inlineCss": true
}

이제 Chrome DevTools의 응답 미리보기를 보면 중요한 CSS가 인라인으로 추가된 스타일 지정된 페이지가 표시됩니다.

전후 비교 후 샷은 인라인화된 중요 CSS로 인해 콘텐츠가 렌더링되고 스타일이 지정되었음을 보여줍니다.

결론

애플리케이션에서 서버 측 렌더링 경로를 사용하지 않는 경우 react-snap를 사용하여 사용자에게 정적 HTML을 사전 렌더링합니다.

  1. 개발 종속 항목으로 설치하고 기본 설정으로 시작합니다.
  2. 실험용 inlineCss 옵션을 사용하여 중요한 CSS를 인라인 처리합니다(사이트에서 작동하는 경우).
  3. 경로 내 구성요소 수준에서 코드 분할을 사용하는 경우 사용자에게 로드 상태를 미리 렌더링하지 않도록 주의하세요. react-snap 리드미에서 자세히 설명합니다.