단일 페이지 웹 애플리케이션 (SPA)의 일반적인 아키텍처 기능은 애플리케이션의 전역 기능을 구동하는 데 필요한 최소한의 HTML, CSS, JavaScript 집합입니다. 실제로는 헤더, 탐색 및 모든 페이지에서 지속되는 기타 공통 사용자 인터페이스 요소인 경우가 많습니다. 서비스 워커가 이 최소 UI의 HTML 및 종속 애셋을 사전 캐시하는 경우 이를 애플리케이션 셸이라고 합니다.
애플리케이션 셸은 웹 애플리케이션의 인지 성능에서 중요한 역할을 합니다. 이 요소는 가장 먼저 로드되므로 사용자가 콘텐츠가 사용자 인터페이스에 채워질 때까지 기다리는 동안 가장 먼저 보게 되는 항목이기도 합니다.
애플리케이션 셸은 빠르게 로드할 수 있지만(네트워크를 사용할 수 있고 어느 정도 빠른 경우), 애플리케이션 셸과 관련 자산을 사전 캐시하는 서비스 워커는 애플리케이션 셸 모델에 다음과 같은 추가적인 이점을 제공합니다.
- 재방문 시 신뢰할 수 있고 일관성 있는 실적 서비스 워커가 설치되지 않은 상태로 앱을 처음 방문할 때, 애플리케이션의 마크업과 관련 애셋을 네트워크에서 로드해야 서비스 워커가 이를 캐시에 저장할 수 있습니다. 그러나 반복 방문은 캐시에서 애플리케이션 셸을 가져오기 때문에 로드 및 렌더링이 즉각적으로 이루어집니다.
- 오프라인 시나리오에서 기능에 안정적으로 액세스할 수 있습니다. 인터넷 접속이 불안정하거나 아예 없는 경우도 있고, "해당 웹사이트를 찾을 수 없습니다"라는 두려움이 있습니다. 애플리케이션 셸 모델은 캐시의 애플리케이션 셸 마크업을 사용하여 탐색 요청에 응답함으로써 이 문제를 해결합니다. 누군가 여러분의 웹 앱에서 이전에 방문한 적이 없는 URL을 방문하더라도 애플리케이션 셸이 캐시에서 제공되고 유용한 콘텐츠로 채워질 수 있습니다.
애플리케이션 셸 모델을 사용해야 하는 경우
애플리케이션 셸은 경로마다 변경되지 않지만 콘텐츠는 변경되지 않는 공통 사용자 인터페이스 요소가 있는 경우에 가장 적합합니다. 대부분의 SPA는 이미 효과적인 애플리케이션 셸 모델을 사용하고 있을 가능성이 높습니다.
이 내용이 프로젝트에 대해 설명되고 서비스 워커를 추가하여 안정성과 성능을 향상시키려는 경우 애플리케이션 셸은 다음과 같아야 합니다.
- 빠르게 로드.
Cache
인스턴스의 정적 애셋을 사용합니다.- 헤더 및 사이드바와 같은 일반적인 인터페이스 요소를 페이지 콘텐츠와 별도로 포함합니다.
- 페이지별 콘텐츠를 가져와 표시합니다.
- 필요한 경우 오프라인 보기를 위해 동적 콘텐츠를 캐시합니다(선택사항).
애플리케이션 셸은 API 또는 JavaScript로 번들된 콘텐츠를 통해 페이지별 콘텐츠를 동적으로 로드합니다. 또한 애플리케이션 셸의 마크업이 변경되면 서비스 워커 업데이트가 새 애플리케이션 셸을 선택하고 자동으로 캐시해야 한다는 점에서 자동 업데이트되어야 합니다.
애플리케이션 셸 빌드
애플리케이션 셸은 콘텐츠와는 별개로 존재해야 하지만, 애플리케이션 셸에 콘텐츠가 채워지는 기반을 제공해야 합니다. 최대한 슬림한 디자인을 사용하는 것이 가장 좋지만 초기 다운로드에는 환경이 빠르게 로드되고 있음을 사용자가 이해할 수 있을 만큼 의미 있는 콘텐츠를 포함하는 것이 가장 좋습니다.
적절한 균형은 앱에 따라 다릅니다. Jake Archibald의 Trained To Thrill 앱용 애플리케이션 셸에는 Flickr에서 새 콘텐츠를 가져올 수 있는 새로고침 버튼이 있는 헤더가 있습니다.
애플리케이션 셸 마크업은 프로젝트마다 다르지만, 다음은 애플리케이션 상용구를 제공하는 index.html
파일의 한 가지 예입니다.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>
Application Shell Example
</title>
<link rel="manifest" href="/manifest.json">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="stylesheet" type="text/css" href="styles/global.css">
</head>
<body>
<header class="header">
<!-- Application header -->
<h1 class="header__title">Application Shell Example</h1>
</header>
<nav class="nav">
<!-- Navigation items -->
</nav>
<main id="app">
<!-- Where the application content populates -->
</main>
<div class="loader">
<!-- Spinner/content placeholders -->
</div>
<!-- Critical application shell logic -->
<script src="app.js"></script>
<!-- Service worker registration script -->
<script>
if ('serviceWorker' in navigator) {
// Register a service worker after the load event
window.addEventListener('load', () => {
navigator.serviceWorker.register('/sw.js');
});
}
</script>
</body>
</html>
프로젝트에 맞게 애플리케이션 셸을 구성하는 경우 다음과 같은 특성을 지녀야 합니다.
- HTML에는 개별 사용자 인터페이스 요소를 위한 명확하게 격리된 영역이 있어야 합니다. 위 예에서는 애플리케이션의 헤더, 탐색, 기본 콘텐츠 영역, 로딩 '스피너'를 위한 공간이 포함되어 있습니다. 콘텐츠 로드 중에만 표시됩니다.
- 애플리케이션 셸을 위해 로드된 초기 JavaScript 및 CSS는 최소한의 요소여야 하며, 콘텐츠가 아닌 애플리케이션 셸 자체의 기능과만 관련이 있어야 합니다. 이렇게 하면 애플리케이션이 셸을 최대한 빠르게 렌더링하고 콘텐츠가 표시될 때까지 기본 스레드 작업을 최소화합니다.
- 서비스 워커를 등록하는 인라인 스크립트.
애플리케이션 셸이 빌드되면 서비스 워커를 빌드하여 애플리케이션과 그 자산을 모두 캐시할 수 있습니다.
애플리케이션 셸 캐싱
애플리케이션 셸과 필수 자산은 서비스 워커가 설치 시 즉시 사전 캐시해야 하는 항목입니다. 위의 예와 같은 애플리케이션 셸을 가정하고 workbox-build
를 사용하여 기본 Workbox 예시에서 이를 실행하는 방법을 살펴보겠습니다.
// build-sw.js
import {generateSW} from 'workbox-build';
// Where the generated service worker will be written to:
const swDest = './dist/sw.js';
generateSW({
swDest,
globDirectory: './dist',
globPatterns: [
// The necessary CSS and JS for the app shell
'**/*.js',
'**/*.css',
// The app shell itself
'shell.html'
],
// All navigations for URLs not precached will use this HTML
navigateFallback: 'shell.html'
}).then(({count, size}) => {
console.log(`Generated ${swDest}, which precaches ${count} assets totaling ${size} bytes.`);
});
build-sw.js
에 저장된 이 구성은 shell.html
에 포함된 애플리케이션 셸 마크업 파일을 포함하여 앱의 CSS 및 JavaScript를 가져옵니다. 스크립트는 다음과 같이 Node로 실행됩니다.
node build-sw.js
생성된 서비스 워커는 ./dist/sw.js
에 기록되며, 완료되면 다음 메시지를 기록합니다.
Generated ./dist/sw.js, which precaches 5 assets totaling 44375 bytes.
페이지가 로드되면 서비스 워커가 애플리케이션 셸 마크업과 그 종속 항목을 사전 캐시합니다.
<ph type="x-smartling-placeholder">![네트워크에서 다운로드한 애셋 목록을 보여주는 Chrome DevTools의 네트워크 패널 스크린샷. 서비스 워커가 미리 캐시한 애셋은 행의 왼쪽에 톱니바퀴가 표시된 다른 애셋과 구분됩니다. 여러 JavaScript 및 CSS 파일은 설치 시 서비스 워커에 의해 사전 캐시됩니다.](https://developer.chrome.google.cn/static/docs/workbox/app-shell-model/image/a-screenshot-the-network-96ad6896c678b.png?authuser=19&hl=ko)
번들러를 사용하는 프로젝트를 비롯한 거의 모든 워크플로에서 애플리케이션 셸의 HTML, CSS 및 JavaScript를 사전 캐싱할 수 있습니다. 문서를 살펴보면서 Workbox를 직접 사용하여 도구 모음을 설정하여 SPA인지 여부에 관계없이 프로젝트에 가장 적합한 서비스 워커를 빌드하는 방법을 배우게 됩니다.
결론
애플리케이션 셸 모델을 서비스 워커와 결합하면 특히 마크업 또는 API 응답을 위해 사전 캐싱 기능을 네트워크 우선, 캐시 전략으로 대체하는 경우와 결합하는 경우 오프라인 캐싱에 매우 효과적입니다. 그 결과, 오프라인 상태에서도 반복 방문 시 애플리케이션 셸을 즉시 렌더링하고 안정적이고 빠른 환경을 구현할 수 있습니다.