Google에서 PWA 구축, 1부

게시판 팀이 PWA를 개발하는 동안 서비스 워커에 대해 알게 된 내용

Douglas Parker
Douglas Parker
Joel Riley
Joel Riley
Dikla Cohen
Dikla Cohen

이 글은 Google 게시판 팀이 배운 교훈에 관한 블로그 게시물 시리즈 중 첫 번째입니다. 빌드하는 방법을 알아보겠습니다. 이 게시물에서는 우리가 직면한 몇 가지 문제점을 공유하고, 접근 방식, 함정을 피하기 위한 일반적인 조언을 제공합니다. 아니요로 PWA에 관한 전체 개요를 의미합니다. Google팀의 경험에서 얻은 교훈을 공유하는 것이 목적입니다.

이 첫 번째 게시물에서는 먼저 약간의 배경 정보를 다루고, 그런 다음 알게 된 내용입니다.

배경

게시판은 2017년 중반부터 2019년 중반까지 활발히 개발되었습니다.

PWA 빌드를 선택한 이유

개발 프로세스를 자세히 알아보기 전에 PWA 빌드가 매력적인 이유를 살펴보겠습니다. 다음 옵션을 사용하세요.

  • 빠르게 반복하는 능력. 게시판이 시범 운영되기 때문에 특히 유용합니다. 여러 시장에 진출할 수 있습니다
  • 단일 코드베이스. 사용자는 Android와 iOS를 대략적으로 균등하게 나누었습니다. PWA는 두 플랫폼에서 모두 작동하는 단일 웹 앱을 빌드할 수 있습니다. 이로 인해 영향을 미칠 수 있습니다
  • 사용자 행동과 관계없이 빠르게 업데이트됩니다. PWA에서는 사용하지 않는 클라이언트의 수를 줄입니다. 브레이킹 체인지를 푸시할 수 있었고 변경사항을 적용할 수 있도록 하는 데 매우 유용합니다.
  • 퍼스트 파티 및 서드 파티 앱과 쉽게 통합됩니다. 이러한 통합은 되었습니다. PWA에서는 단순히 URL을 여는 것을 의미할 때가 많았습니다.
  • 앱을 설치할 때 발생하는 불편함이 사라졌습니다.

Google의 프레임워크

게시판에는 Polymer를 사용했지만 작동합니다

서비스 워커에 대해 알게 된 내용

PWA를 사용하려면 서비스가 있어야 합니다. worker와 같은 메서드를 지원합니다. 서비스 워커 고급 캐싱 전략, 오프라인 기능, 백그라운드 동기화, 서비스 워커가 약간의 복잡성을 가중시키지만, 이들의 이점이 추가된 있습니다.

가능한 경우 생성

서비스 워커 스크립트를 직접 작성하지 마세요. 서비스 워커를 직접 작성하려면 수동으로 필요 대부분의 서비스 워커 라이브러리에 공통으로 적용되는 캐시된 리소스 관리 및 로직 재작성(예: Workbox로 변경합니다.

하지만 내부 기술 스택으로 인해 라이브러리를 사용하여 서비스 워커를 살펴보겠습니다. 우리가 얻은 지식은 때때로 이를 반영할 것입니다. 생성되지 않은 서비스 워커를 참조하세요.

일부 라이브러리가 서비스 워커와 호환되지 않음

일부 JS 라이브러리는 서비스 워커가 실행할 때 예상대로 작동하지 않는 가정을 합니다. 대상 인스턴스(window 또는 document를 사용할 수 있다고 가정하거나 서비스에 사용할 수 없는 API를 사용한다고 가정함) 작업자 (XMLHttpRequest, 로컬 저장소 등) 필요한 모든 중요 라이브러리가 서비스 워커와 호환됩니다 이 특정 PWA에서는 gapi.js를 지원하지만 서비스 워커를 지원하지 않아 작업을 수행할 수 없습니다. 또한 라이브러리 작성자는 콘텐츠를 줄이거나 서비스 워커 사용을 지원하기 위해 가능한 경우 JavaScript 컨텍스트에 대한 불필요한 가정 서비스 워커와 호환되지 않는 API를 피하고 전역적인 작업을 피하는 방법 등 상태가 될 수 있습니다.

초기화 중 IndexedDB 액세스 방지

다음과 같은 경우 IndexedDB를 읽지 않음 서비스 워커 스크립트를 초기화하지 않으면 다음과 같이 바람직하지 않은 상황이 발생할 수 있습니다.

  1. 사용자에게 IndexedDB (IDB) 버전 N의 웹 앱이 있음
  2. 새 웹 앱이 IDB 버전 N+1로 푸시됩니다.
  3. 사용자가 PWA를 방문하여 새 서비스 워커의 다운로드가 트리거됨
  4. 새 서비스 워커는 install 이벤트 핸들러를 등록하기 전에 IDB에서 읽어서 N에서 N+1로의 IDB 업그레이드 주기
  5. 사용자에게 버전 N의 이전 클라이언트가 있으므로 서비스 워커 업그레이드 프로세스가 활성 상태로 중단됨 이전 버전의 데이터베이스에 여전히 연결이 열려 있음
  6. 서비스 워커가 중단되고 설치되지 않음

우리의 사례에서는 서비스 워커 설치 시에 캐시가 무효화되었기 때문에 서비스 워커가 사용자가 업데이트된 앱을 받지 못했습니다.

복원력 강화

서비스 워커 스크립트는 백그라운드에서 실행되지만 언제든지 종료될 수 있습니다. I/O 작업 중 (네트워크, IDB 등) 모든 장기 실행 프로세스는 언제든지 재개할 수 있습니다.

큰 파일을 서버에 업로드하고 IDB에 저장하는 동기화 프로세스의 경우, 저희의 솔루션은 부분 업로드의 경우 내부 업로드 라이브러리의 재개 가능한 업로드 전에 재개 가능한 업로드 URL을 IDB에 저장하고 해당 URL을 사용하여 전체 업로드가 완료되지 않은 경우 다시 업로드할 수 있습니다. 또한 장기 실행 I/O 작업 전에, 상태는 IDB에 저장되어 각 레코드에 대해 프로세스의 어느 단계에 있었는지 나타냅니다.

전역 상태에 의존하지 않음

서비스 워커는 다른 컨텍스트에 존재하기 때문에 존재할 것으로 예상되는 많은 기호는 있습니다. 많은 코드가 window 컨텍스트와 서비스 워커 컨텍스트 (예: 로깅, 플래그, 동기화 등)가 포함됩니다. 코드는 사용하는 서비스에 대해 방어적이어야 합니다(예: 로컬 스토리지 또는 쿠키를 사용할 수 있습니다 이때 globalThis 드림 를 사용하여 모든 컨텍스트에서 작동하는 방식으로 전역 객체를 참조합니다. 저장된 데이터도 사용 언제 스크립트가 종료되고 언제 종료되는지는 보장할 수 없으므로 압박을 받을 수 있습니다.

로컬 개발

서비스 워커의 주요 구성요소는 리소스를 로컬에서 캐싱하는 것입니다. 그러나 개발 과정에서 특히 업데이트가 지연되는 경우 원하는 것과 정확히 반대됩니다. 여전히 문제를 디버깅하거나 백그라운드 동기화, 알림 등의 기능을 사용할 수 있습니다. Chrome에서는 다음과 같이 Chrome DevTools를 통해 이 작업을 수행할 수 있습니다. Bypass for network 체크박스 (Application 패널 > Service worker 창)를 활성화 네트워크 패널에서 캐시 사용 중지 체크박스를 선택하여 캐시의 데이터를 메모리 캐시를 비활성화합니다. Google은 더 많은 브라우저를 지원하기 위해 서비스 워커에서 캐싱을 비활성화하는 플래그 포함 살펴보겠습니다 따라서 개발자는 항상 캐싱 문제 없이 최신 변경사항을 가져올 수 있습니다. 그것은 Cache-Control: no-cache 헤더를 포함하여 브라우저가 모든 애셋 캐싱을 할 수 없습니다.

등대

Lighthouse는 다양한 디버깅을 제공하여 PWA에 유용한 도구들입니다. 사이트를 스캔하여 PWA, 성능, 검색엔진 최적화와 같은 여러 권장사항이 있습니다. Lighthouse는 Google Cloud의 연속된 통합되어 있는 경우 7가지 기준을 충족해야 합니다. 이것은 실제로 한 번 우리에게 일어났는데, 그 곳에서 서비스 워커가 자동으로 프로덕션 출시 전에는 이 사실을 몰랐습니다 Lighthouse를 CI에 포함했다면 막을 수 있었습니다.

지속적 배포 수용

서비스 워커는 자동으로 업데이트될 수 있으므로 사용자는 업그레이드를 제한할 수 없습니다. 이 실제로 사용되는 오래된 클라이언트의 수를 크게 줄입니다. 사용자가 앱을 열었을 때 서비스 워커가 새 클라이언트를 느리게 다운로드하는 동안 이전 클라이언트에 서비스를 제공하는 것이었습니다. 일단 새 클라이언트가 다운로드되면 사용자에게 새 기능에 액세스하기 위해 페이지를 새로고침하라는 메시지가 표시됩니다. 심지어 사용자가 이 요청을 무시하면 다음 번에 페이지를 새로고침할 때 클라이언트 버전입니다 따라서 사용자가 같은 시기에 업데이트를 거부하기는 매우 어렵습니다. iOS/Android 앱에서 사용할 수 있습니다.

브레이킹 체인지는 백엔드의 변경사항을 푸시하는 데 매우 짧은 시간 안에 있습니다 일반적으로 신규 고객에게 업데이트를 제공하기 전에 한 달 정도 브레이킹 체인지가 없습니다. 앱이 오래된 경우에도 제공되기 때문에 실제로 이전 클라이언트에서 사용자가 오랫동안 앱을 열지 않은 경우 iOS에서 서비스 워커는 2주 만에 퇴장당했습니다 이러한 경우는 발생하지 않습니다 Android의 경우 콘텐츠가 오래되거나 몇 주 후에 수동으로 만료될 수 있습니다. 실제로는 한 번도 문제가 발생할 수 있습니다 특정 팀이 얼마나 엄격하게 응대해야 하는지는 각자의 용도에 따라 다릅니다. PWA는 iOS/Android 앱보다 훨씬 더 높은 유연성을 제공합니다.

서비스 워커에서 쿠키 값 가져오기

서비스 워커 컨텍스트에서 쿠키 값에 액세스해야 하는 경우가 있습니다. 이 경우에는 퍼스트 파티 API 요청을 인증하기 위한 토큰을 생성하기 위해 쿠키 값에 액세스해야 합니다. document.cookies 같은 동기 API는 사용할 수 없습니다. 언제든지 활성 (윈도우된) 클라이언트로 메시지를 전송하여 쿠키 값을 요청하지만, 윈도우가 설정된 클라이언트 없이 서비스 워커가 백그라운드에서 실행될 수 있습니다. 백그라운드 동기화 중에 사용 가능하지 않습니다. 이 문제를 해결하기 위해 프런트엔드 서버에 전달했습니다. 서비스 워커는 이 엔드포인트에 대한 응답을 읽고 응답을 읽고 쿠키 값을 가져옵니다.

2022년 3월 Cookie Store API 이 해결 방법은 그것을 지원하는 브라우저에 더 이상 필요하지 않습니다. 브라우저 쿠키에 대한 비동기식 액세스를 제공하며 서비스 워커에 의해 직접 사용될 수 있습니다.

생성되지 않은 서비스 워커에 대한 함정

캐시된 정적 파일이 변경되면 서비스 워커 스크립트가 변경되는지 확인하세요.

일반적인 PWA 패턴은 서비스 워커가 install 단계: 클라이언트가 모든 항목에 대해 Cache Storage API 캐시에 직접 액세스할 수 있도록 합니다. 후속 방문 에서 확인할 수 있습니다 . 서비스 워커는 브라우저에서 서비스가 작업자 스크립트가 어떤 식으로든 변경되었으므로 서비스 워커 스크립트 파일 자체를 확인해야 했습니다. 어떤 방식으로든 변경되었을 수 있습니다. 이 작업을 수동으로 하려면 정적 리소스 파일 세트를 추가했기 때문에 모든 릴리스에서 고유한 서비스 워커 자바스크립트 파일에 포함됩니다. 서비스 워커 라이브러리(예: Workbox에서는 이 과정을 자동으로 자동화합니다.

단위 테스트

서비스 워커 API는 전역 객체에 이벤트 리스너를 추가하여 작동합니다. 예를 들면 다음과 같습니다.

self.addEventListener('fetch', (evt) => evt.respondWith(fetch('/foo')));

이벤트 트리거, 이벤트 객체를 모의 처리해야 하므로 테스트가 어려울 수 있습니다. respondWith() 콜백을 호출한 다음 프로미스를 기다린 후 최종적으로 결과를 어설션합니다. 이를 구조화하는 더 쉬운 방법은 모든 구현을 다른 파일에 위임하는 것입니다. 이렇게 하는 것이 있습니다

import fetchHandler from './fetch_handler.js';
self.addEventListener('fetch', (evt) => evt.respondWith(fetchHandler(evt)));

서비스 워커 스크립트에 대한 단위 테스트를 수행하기가 어렵기 때문에, 핵심 서비스 워커를 스크립트를 최대한 사용하여 대부분의 구현을 다른 모듈로 분할합니다. 이후 이러한 파일은 표준 JS 모듈일 뿐이므로 표준 테스트로 더 쉽게 단위 테스트할 수 있었습니다. 제공합니다

2부와 3부를 기대해 주세요.

본 시리즈의 2부와 3부에서는 미디어 관리 및 iOS 관련 문제에 대해 살펴보겠습니다. 만약 Google에서의 PWA 구축에 대해 자세히 문의하고 싶다면 작성자 프로필을 방문하여 문의 방법: