rendering-on-the-web 문서서는 클라이언트 렌더링 최적화를 위해
- RTTs를 줄이거나
- 중요한 스크립트와 데이터를 <link rel=preload>를 사용하여 더 빨리 전달하거나,
- JS 용량을 줄이거나(tight JavaScript budget)
- PRPL 패턴을 사용하거나
- 코드 스플리팅을 사용하거나(aggressive code-splitting)
- Application Shell caching을 사용하라고 합니다.
이 중에서 PRRL 패턴을 알아보겠습니다.
TL;DR
P : 링크의 rel 속성을 통해 프리 로딩(페치) 이용
R : 서버 렌더링을 이용해 프리 렌더링 이용
P : 서비스 워커를 이용해 프리 캐시를 이용
L : loading 속성을 통해 지연 로딩을 이용
PRPL 패턴이란?
- Push (or preload): 가장 중요한 리소스 푸시(또는 사전 로드)
- Render가능한 한 빨리 초기 경로를 렌더링
- Pre-cache : 기존에 저장해둔 자산
- Lazy load : 다른 경로(컴포넌트) 및 중요하지 않은 자산을 지연 로드
PRPL 아키텍처는 웹을 더 빠르게 만들려는 Google Chrome 팀에서 고안했습니다.
PRPL은 더 빠른 웹 경험을 촉진하기 위해 수년에 걸쳐 개발된 개별 최적화 트릭입니다.
서비스 워커, 백그라운드 동기화, 캐시 API, 우선 순위 힌트 및 프리페치를 이용합니다.
특히 PRPL은 오프라인이거나 데이터 절약 모드여서 네트워크 지연이 높은 모바일 기기를 위한 것입니다.
P- Push (or preload)
<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">
(주 : 프리페치는 주로 다음 페이지 탐색을 위해 사용합니다. https://medium.com/reloading/preload-prefetch-and-priorities-in-chrome-776165961bbf)
<link rel="prefetch">
R - 초기 렌더링
- 렌더링 차단 리소스를 제거하고
- CSS 전달을 최적화하고
- SSR을 사용해야 합니다.
import React from "react"
import ReactDOM from "react-dom"
import App from "App"
ReactDOM.hydrate(<App />, window.root)
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>"))
})
})
...
P - 미리(사전) 캐싱하기
따라서 사용자에게 네트워크 오류가 표시되는 대신 캐시된 자산이 제공됩니다.
디바이스가 온라인 상태가 되면 에셋이 네트워크를 통해 새로고침 됩니다.
- 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()
);
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" />
<img src="./big-image.jpg" loading="lazy" />
loading 애트리뷰트가 키입니다. 이 리소스가 바로 필요하지 않음을 브라우저에 알립니다.
이 속성은 image, iframe, video 및 audio 태그에 포함되어 지연 로드할 수 있습니다.
- lazy : 브라우저의 뷰포트 내/내로 들어올 때까지 리소스 로드를 연기하도록 합니다.
- auto: 리소스를 지연 로딩하지 않음을 나타냅니다.
- eager: 리소스를 지연 로딩하지 않고 즉시 로딩합니다.
지연 로딩은 Intersection Observer API를 사용하여 프로그래밍 방식으로 수행할 수도 있습니다.
Angular, React 및 Vue의 라이브러리는 Intersection Observer를 사용하여 컴포넌트와 리소스를 지연 로드합니다.
결론
놀랍도록 빠른 최신 웹 앱을 만들기 위해서는 해당 패턴을 따라가기만 하면 됩니다.
참고 :
https://blog.logrocket.com/prpl-pattern-solutions-for-modern-web-app-optimization/
https://web.dev/apply-instant-loading-with-prpl/
'FrontEnd' 카테고리의 다른 글
리액트와 유닛 테스트를 위한 클린 아키텍처 [부제, 의존성 주입은 정말 필요한가?] (0) | 2022.10.30 |
---|---|
소프트웨어 엔지니어링 관점에서 바라본 CSS-in-js vs Tailwind CSS (0) | 2022.10.29 |
개발자 관점에서 Next13 간단히 살펴보기 (6) | 2022.10.28 |
[프론트엔드 인터뷰 퀴즈] 정적 클래스 멤버(프로퍼티 / 메서드)를 사용하는 이유는 뭘까요? (0) | 2022.10.28 |
[TS]유효성 검증(validation) 대신 구문 분석(Parsing)하세요 (0) | 2022.10.27 |