본문 바로가기

FrontEnd

CSR 성능 최적화를 위한 PRPL 패턴 with React

반응형

rendering-on-the-web 문서서는 클라이언트 렌더링 최적화를 위해

이 중에서 PRRL 패턴을 알아보겠습니다.


TL;DR

P : 링크의 rel 속성을 통해 프리 로딩(페치) 이용

R : 서버 렌더링을 이용해 프리 렌더링 이용

P : 서비스 워커를 이용해 프리 캐시를 이용

L : loading 속성을 통해 지연 로딩을 이용


PRPL 패턴이란?

PRPL은 다음의 약어입니다.
  • Push (or preload): 가장 중요한 리소스 푸시(또는 사전 로드)
  • Render가능한 한 빨리 초기 경로를 렌더링
  • Pre-cache : 기존에 저장해둔 자산
  • Lazy load : 다른 경로(컴포넌트) 및 중요하지 않은 자산을 지연 로드

PRPL 아키텍처는 웹을 더 빠르게 만들려는 Google Chrome 팀에서 고안했습니다.
PRPL은 더 빠른 웹 경험을 촉진하기 위해 수년에 걸쳐 개발된 개별 최적화 트릭입니다.
서비스 워커, 백그라운드 동기화, 캐시 API, 우선 순위 힌트 및 프리페치를 이용합니다.
특히 PRPL은 오프라인이거나 데이터 절약 모드여서 네트워크 지연이 높은 모바일 기기를 위한 것입니다.

P- Push (or preload)

브라우저가 리소스를 미리 가져와 브라우저에 저장하도록 지시합니다.
따라서 리소스가 필요할 때 네트워크를 통해 리소스를 가져오지 않고
브라우저에서 리소스를 빠르게 검색합니다.
 
HTML 문서의 head에 있는 <link> 태그에, rel="preload" 애트리뷰트를 추가하여 사용합니다.
 
아래와 같이 작업하면, 리소스 style.css가 스타일시트로 미리 로드됩니다.
as='style'은 브라우저에 미리 로드할 리소스가 스타일시트임을 알려줍니다.
<link rel="preload" as="style" href="style.css">​

우리는 다음과 같은 것을 프리로드 할 수 있습니다.

  • Web pages
  • JS files
  • CSS files
  • Media files (Audio, Image, Video, Documents, and Web fonts)
  • DNS lookup
<link rel="preload" href="/your/webpage/link">
<link rel="preload" href="/your/js/file/link">
<link rel="preload" href="/your/css/file/link">
<link rel="preload" href="/your/audio/file/link">
<link rel="preload" href="/your/audio/file/link">
<link rel="preload" href="/your/video/file/link">
<link rel="preload" href="/your/image/file/link">
<link rel="preload" href="/your/document/file/link">
<link rel="preload" href="/your/webfont/file/link">​
프리로딩과 프리페치는 기본적으로 동일합니다.
프리페치에는 브라우저가 <link> 태그의 리소스를 가져와 로컬 캐시에 저장하는 프로세스가 포함됩니다.
사용자가 결국 <link> 태그를 통해 페이지를 요청하면 브라우저는 사용자에게 캐시된 페이지를 제공합니다.
이렇게 하면 웹 페이지의 로드 및 렌더링 속도가 모두 빨라집니다.

(주 : 프리페치는 주로 다음 페이지 탐색을 위해 사용합니다. https://medium.com/reloading/preload-prefetch-and-priorities-in-chrome-776165961bbf)

리소스를 미리 가져오기 위해 rel="prefetch" 애트리뷰트를 사용합니다.
<link rel="prefetch">

R - 초기 렌더링

웹 앱의 초기 경로는 최대한 빨리 렌더링해야 하며 초기 경로는 지연 로드(lazy-load)하지 않아야 한다는 규칙입니다.
이를  Initial Contentful Paint라고 합니다.
앱의 목적이 무엇이든 간에, 브라우저에서 First Paint를 빠르게 생성해야 합니다.
 
First Contentful Paint를 최적화하려면
  • 렌더링 차단 리소스를 제거하고
  • CSS 전달을 최적화하고
  • SSR을 사용해야 합니다.
위 설명은 브라우저/클라이언트 측에서 페이로드를 렌더링하기 때문에 JS 프레임워크에 대부분 적용 가능합니다.
따라서 앱에 페이로드가 많은 경우 콘텐츠를 렌더링하기 전에 JS/CSS 자산을 로드해야 합니다.
 
따라서 웹 앱의 SSR은 서버에서 웹 앱을 렌더링하고 나머지 JS/CSS가 도착하기 전에 첫 번째 페인트를 생성하는 데 도움이 됩니다.
 
예를 들어 React에서 ReactDOMServer를 사용하여 서버 렌더링 앱을 빌드할 수 있습니다.
import React from "react" 
import ReactDOM from "react-dom" 
import App from "App"

ReactDOM.hydrate(<App />, window.root)
React DOM 렌더러가 서버 측 렌더링 후 앱을 다시 수화하고 있다는 것을 알기를 원하기 때문에
render 메서드 대신 ReactDOM.hydrate를 사용하고 있습니다.
이것은 React DOM 렌더러가 서버 렌더링을 예상한다는 것을 의미합니다.
서버에 의해 렌더링 된 결과를 먼저 표시하고,
나머지는 브라우저의 React에 의해 수행됩니다.
 
Express 서버의 코드는 다음과 같습니다.
import fs from "fs" 
import React from "react" 
import App from "./App" 
import ReactDOMServer from "react-dom/server"

...

app.get("/", (req, res, next) => { // This would render the <App /> and return it as string. 
	const app = ReactDOMServer.renderToString(<App />);
	fs.readFile("./build/index.html", (err, data) => {
    	// We read the index.html file, replace the `div#root` with the rendered App component and send it to the browser.
        return res.send(data.replace("<div id='root'></div>", "<div id='root'>" + app + "</div>"))
	})
})

...
 
위의 코드 덕택에 React 앱은 이제 SSR을 사용할 수 있습니다.
전체 로드가 렌더링되기 전에 첫 번째 페인트를 렌더링합니다.
 
중요한 CSS를 HTML에 인라인하는 작업이 포함되므로 CSS가 빠르게 전달됩니다.
 
이 모든 것은 웹 앱이 초기 경로를 빠르게 렌더링하고
나머지 페이로드가 도착하기 전에 사용자와 상호 작용하는 데 도움이 됩니다.
성능과 사용자 경험을 향상시킵니다.

P - 미리(사전) 캐싱하기

에셋이 브라우저에 캐시되어 에셋에 대한 요청이 있을 때 사용자 경험을 방해하지 않고 캐시에서 제공되는 기술입니다.
모바일 기기가 오프라인일 때 매우 유용합니다.

따라서 사용자에게 네트워크 오류가 표시되는 대신 캐시된 자산이 제공됩니다.
디바이스가 온라인 상태가 되면 에셋이 네트워크를 통해 새로고침 됩니다.

Pre-caching은 서비스 워커를 사용하여 수행되며 대부분 PWA에서 사용됩니다.
서비스 워커의 Pre-caching에는 다양한 전략이 있습니다.
  • Stale-while-revalidate:
    • 먼저, 캐시에서 응답을 확인합니다.
    • 캐시에서 응답이 사용 가능한 경우 이를 전달하고, 캐시의 유효성을 다시 확인합니다.
    • 사용할 수 없는 경우 서비스 작업자는 네트워크에서 응답을 가져와 캐시합니다.
  • Cache first:
    • 먼저 캐시에서 응답을 찾습니다.
    • 이전에 캐시된 응답이 발견되면 캐시를 반환하고 제공합니다.
    • 그렇지 않은 경우 네트워크에서 응답을 가져와서 제공하고 다음 번에 캐시합니다.
  • Network first:
    • 이 전략은 네트워크에서 응답을 가져오려고 합니다.
      • 성공하면 응답을 캐시하고 응답을 반환합니다.
      • 네트워크가 실패하면 캐시에서 응답을 제공합니다.
  • Cache only이 전략은 캐시에서만 응답합니다. 
  • Network only: 이 전략은 네트워크만 사용하여 응답을 가져오고 제공합니다. 
주 : 보통 SWR 전략을 사용하고 예외 사항을 서비스 워커 로직을 통해 구현하는 것 같네요

Google의 Workbox라는 라이브러리가 있습니다.
서비스 워커의 캐시를 유지 관리하기 위한 도구를 제공하는 것을 목표로 하며
위에서 방금 본 캐싱 전략 중에서 선택할 수 있습니다.

 

사전 캐싱에 Workbox를 사용하는 방법의 예를 살펴보겠습니다.
Workbox를 사용하여 사용하려는 경로와 캐싱 전략을 설정합니다.
Workbox는 경로 요청을 수신하고 요청이 캐시되고 응답되는 방법을 결정합니다.

import { registerRoute } from 'workbox-routing'; 
import { NetworkFirst } from 'workbox-strategies';

registerRoute(
    // We are caching the style resources.
    ({request}) => request.destination === 'style',

    // We are setting the style cahcing to use the
    // StaleWhileRevalidate strategy.
    // Use cache but update in the background.
	 new StaleWhileRevalidate()
);
위는 StaleWhileRevalidate 전략을 사용하기 위해 Workbox에서 스타일 리소스를 캐시하는 방법의 간단한 예입니다.
이렇게 하면 다시 로드할 때 더 빠르게 가져올 수 있도록 모든 스타일 파일을 캐시합니다.
네트워크가 있으면 백그라운드에서 조용히 업데이트됩니다.
registerRoute( ({request}) => request.destination === 'script', new NetworkFirst() );

여기에서 모든 JavaScript 파일은 NetworkFirst 전략을 사용하여 캐시됩니다.
네트워크를 통한 가져오기가 실패하면 캐시에서 가져옵니다.


L - Lazy load

이것은 웹 앱의 경로(컴포넌트) 및 자산 로드를 연기합니다.
애플리케이션 성능의 가장 중요한 개념은 응답 시간과 리소스 소비입니다. 이가 발생하는 것은 불가피합니다.
문제는 어디에서나 발생할 수 있으며 문제가 발생하기 전에 찾아서 해결하는 것이 중요합니다.

지연 로딩은 많은 웹 앱 성능 문제의 위험을 최소화하는 데 도움이 됩니다.

지연 로딩은 아래를 최적화하여 성능 개선에 도움이 됩니다.

  • Response time:
    • 웹 애플리케이션이 로드되고 UI 인터페이스가 사용자에게 응답하는 데 걸리는 시간입니다.
    • 지연 로딩은 코드를 분할하고 필요한 번들만 로드하여 응답 시간을 최적화합니다.
  • Resources consumption:
    • 인간은 참을성이 없는 동물입니다. 우리는 웹사이트가 로드되는 데 3초 이상 걸리는 것을 좋아하지 않습니다.
    • 따라서 지연 로딩은 리소스 로딩량을 줄이기 위해 당시 필요한 코드 번들만 로딩합니다.

지연 로딩은 외부 라이브러리의 도움 없이 Chrome에서 기본적으로 수행할 수 있습니다.
즉, 지연 로딩은 브라우저에서 기본적으로 지원됩니다.

지연 로드하려는 리소스에 loading 속성만 추가하면 됩니다.
예를 들어 다음과 같은 이미지가 있습니다.

<img src="./big-image.jpg" />
기본적으로 위의 이미지를 지연 로드하려면 "lazy" 값을 가진 loading 속성을 추가하기만 하면 됩니다.
<img src="./big-image.jpg" loading="lazy" />

loading 애트리뷰트가 키입니다. 이 리소스가 바로 필요하지 않음을 브라우저에 알립니다.
이 속성은 image, iframe, videoaudio 태그에 포함되어 지연 로드할 수 있습니다.

loading 애트리뷰트에는 선택할 수 있는 다른 값이 있습니다.
  • lazy : 브라우저의 뷰포트 내/내로 들어올 때까지 리소스 로드를 연기하도록 합니다.
  • auto: 리소스를 지연 로딩하지 않음을 나타냅니다.
  • eager: 리소스를 지연 로딩하지 않고 즉시 로딩합니다.

지연 로딩은 Intersection Observer API를 사용하여 프로그래밍 방식으로 수행할 수도 있습니다.
Angular, React 및 Vue의 라이브러리는 Intersection Observer를 사용하여 컴포넌트와 리소스를 지연 로드합니다.

 

결론

PRPL은 효율적으로 웹 앱을 최적화하는 방법을 의미하는 최신 웹 표준에 의해 설정된 패턴 또는 기술의 컬랙션 입니다.
사전 로드 및 사전 가져오기에서 지연 로드에 이르기까지 다양한 기술을 살펴보았습니다.

놀랍도록 빠른 최신 웹 앱을 만들기 위해서는 해당 패턴을 따라가기만 하면 됩니다.

참고 :

https://blog.logrocket.com/prpl-pattern-solutions-for-modern-web-app-optimization/

 

PRPL pattern: Solutions for modern web app optimization - LogRocket Blog

Learn how to make your web apps fast, responsive, and efficient with the Push, Render, Pre-cache, and Lazy load (PRPL) pattern.

blog.logrocket.com

https://web.dev/apply-instant-loading-with-prpl/

 

PRPL 패턴으로 즉시 로딩 적용

PRPL은 웹 페이지를 더 빠르게 로드하고 상호작용할 수 있도록 만드는 데 사용되는 패턴을 설명하는 약어입니다. 이 가이드에서는 이러한 각 기술을 함께 사용하거나 독립적으로 사용하여 성능

web.dev

 

반응형