Google에서 PWA 구축, 1부

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

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

이 글은 Google 게시판팀이 외부용 PWA를 빌드하면서 배운 교훈에 관한 블로그 게시물 시리즈 중 첫 번째입니다. 이 게시물에서는 우리가 직면한 몇 가지 도전과제, 이를 극복하기 위해 취한 접근 방식, 함정을 피하기 위한 일반적인 조언을 공유합니다. 이것이 PWA에 관한 전체 개요는 아닙니다. Google팀의 경험에서 얻은 교훈을 공유하는 것이 목적입니다.

이 첫 번째 게시물에서는 먼저 약간의 배경 정보를 다룬 다음, 서비스 워커에 대해 배운 모든 내용을 자세히 살펴보겠습니다.

배경

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

PWA 빌드를 선택한 이유

개발 프로세스를 자세히 알아보기 전에 PWA 빌드가 이 프로젝트에서 매력적인 옵션인 이유를 살펴보겠습니다.

  • 빠르게 반복하는 능력. 게시판은 여러 시장에서 시범적으로 운영되기 때문에 특히 유용합니다.
  • 단일 코드베이스. 사용자는 Android와 iOS를 대략적으로 균등하게 나누었습니다. PWA를 통해 두 플랫폼에서 모두 작동하는 단일 웹 앱을 빌드할 수 있었습니다. 그 결과 팀의 업무 속도와 영향력이 늘어났습니다
  • 사용자 행동과 관계없이 빠르게 업데이트됩니다. PWA는 자동으로 업데이트되어 실제로 오래된 클라이언트의 양을 줄일 수 있습니다. 클라이언트의 마이그레이션 시간을 매우 짧게 하여 백엔드 변경사항을 푸시할 수 있었습니다.
  • 퍼스트 파티 및 서드 파티 앱과 쉽게 통합됩니다. 이러한 통합은 앱의 요구사항이었습니다. PWA를 사용하면 단순히 URL을 여는 것을 의미할 때가 많습니다.
  • 앱을 설치할 때 발생하는 불편함이 사라졌습니다.

Google의 프레임워크

게시판에는 Polymer를 사용했지만, 잘 지원되는 최신 프레임워크라면 무엇이든 사용할 수 있습니다.

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

서비스 워커가 없으면 PWA를 보유할 수 없습니다. 서비스 워커는 고급 캐싱 전략, 오프라인 기능, 백그라운드 동기화 등과 같은 많은 기능을 제공합니다. 서비스 워커가 약간의 복잡성을 더하지만 그 이점이 복잡성을 능가하는 것으로 나타났습니다.

가능한 경우 생성

서비스 워커 스크립트를 직접 작성하지 마세요. 서비스 워커를 직접 작성하려면 캐시된 리소스를 수동으로 관리하고 Workbox와 같은 대부분의 서비스 워커 라이브러리에 공통된 로직을 다시 작성해야 합니다.

하지만 내부 기술 스택으로 인해 라이브러리를 사용하여 서비스 워커를 생성하고 관리할 수 없었습니다. 우리가 얻은 지식은 때때로 이를 반영할 것입니다. 자세한 내용은 생성되지 않은 서비스 워커의 문제를 참조하세요.

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

일부 JS 라이브러리는 서비스 워커가 실행할 때 예상대로 작동하지 않는 가정을 합니다. 예를 들어 window 또는 document를 사용할 수 있다고 가정하거나 서비스 워커 (XMLHttpRequest, 로컬 스토리지 등)가 사용할 수 없는 API를 사용하는 경우입니다. 애플리케이션에 필요한 모든 중요 라이브러리가 서비스 워커와 호환되는지 확인하세요. 이 특정 PWA에서는 인증에 gapi.js를 사용하고 싶었지만 서비스 워커를 지원하지 않아 사용할 수 없었습니다. 또한 라이브러리 작성자는 서비스 워커와 호환되지 않는 API를 피하고 전역 상태를 회피하는 등 서비스 워커 사용 사례를 지원하기 위해 JavaScript 컨텍스트에 관한 불필요한 가정을 줄이거나 없애야 합니다.

초기화 중 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를 사용하여 모든 컨텍스트에서 작동하는 방식으로 전역 객체를 참조할 수 있습니다. 또한 전역 변수에 저장된 데이터는 드물게 사용합니다. 스크립트가 종료되고 상태가 제거되는 시점을 보장할 수 없기 때문입니다.

로컬 개발

서비스 워커의 주요 구성요소는 리소스를 로컬에서 캐싱하는 것입니다. 그러나 개발 중에는 특히 업데이트가 지연되는 경우 원하는 것과 정반대입니다. 문제를 디버그하거나 백그라운드 동기화 또는 알림과 같은 다른 API를 사용할 수 있도록 서버 워커를 계속 설치해야 합니다. Chrome에서는 메모리 캐시를 사용 중지하기 위해 네트워크 패널에서 캐시 사용 중지 체크박스를 사용 설정하는 것 외에도 네트워크 우회 체크박스 (애플리케이션 패널 > 서비스 워커 창)를 사용 설정하여 Chrome DevTools를 통해 이 작업을 수행할 수 있습니다. Google은 더 많은 브라우저를 지원하기 위해 서비스 워커에서 캐싱을 사용 중지하는 플래그를 포함하여 다른 솔루션을 선택했습니다. 이 플래그는 개발자 빌드에서 기본적으로 사용 설정됩니다. 따라서 개발자는 항상 캐싱 문제 없이 최신 변경사항을 가져올 수 있습니다. 브라우저가 애셋을 캐시하지 못하도록 Cache-Control: no-cache 헤더를 포함하는 것이 중요합니다.

등대

Lighthouse는 PWA에 유용한 여러 디버깅 도구를 제공합니다. 사이트를 스캔하고 PWA, 성능, 접근성, 검색엔진 최적화, 기타 권장사항에 관한 보고서를 생성합니다. 지속적 통합에서 Lighthouse를 실행하여 기준 중 하나가 PWA가 되는 경우 알림을 받는 것이 좋습니다. 실제로 이런 일이 한 번 발생했는데, 서비스 워커가 설치되지 않고 프로덕션 푸시 전에는 이러한 사실을 몰랐습니다. Lighthouse를 CI에 포함했다면 그렇지 못했을 겁니다

지속적 배포 수용

서비스 워커는 자동으로 업데이트될 수 있으므로 사용자는 업그레이드를 제한할 수 없습니다. 이렇게 하면 실제로 사용 중인 오래된 클라이언트의 양이 크게 줄어듭니다. 사용자가 앱을 열면 서비스 워커는 이전 클라이언트가 늦게 다운로드되는 동안 이전 클라이언트를 지원했습니다. 새 클라이언트가 다운로드되면 사용자에게 새 기능에 액세스하기 위해 페이지를 새로고침하라는 메시지가 표시됩니다. 사용자가 이 요청을 무시해도 다음에 페이지를 새로고침하면 새 버전의 클라이언트를 수신하게 됩니다. 따라서 사용자가 iOS/Android 앱에서와 같은 방식으로 업데이트를 거부하기가 매우 어렵습니다.

클라이언트의 경우 매우 짧은 마이그레이션 시간으로 브레이킹 체인지를 푸시할 수 있었습니다. 일반적으로 브레이킹 체인지를 수행하기 전에 사용자가 최신 클라이언트로 업데이트할 수 있도록 한 달을 제공합니다. 앱이 오래된 경우에도 제공되기 때문에 사용자가 오랫동안 앱을 열지 않은 경우 실제로 이전 클라이언트가 통제되지 않은 상태로 존재할 수 있었습니다. iOS에서는 서비스 워커가 2주 후에 제거되므로 이러한 상황이 발생하지 않습니다. Android의 경우 오래된 동안 게재하지 않거나 몇 주 후에 수동으로 콘텐츠를 만료하여 이 문제를 완화할 수 있습니다. 실제로 비활성 클라이언트의 문제는 발생하지 않았습니다 특정 팀이 얼마나 엄격하기를 원하는지는 구체적인 사용 사례에 따라 달라지지만, PWA는 iOS/Android 앱보다 훨씬 더 높은 유연성을 제공합니다.

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

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

Cookie Store API가 출시됨에 따라 브라우저 쿠키에 대한 비동기식 액세스를 제공하고 서비스 워커가 직접 사용할 수 있으므로 이 해결 방법을 지원하는 브라우저에서 더 이상 이 해결 방법이 필요하지 않습니다.

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

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

일반적인 PWA 패턴은 서비스 워커가 install 단계 중에 모든 정적 애플리케이션 파일을 설치하는 것으로, 이를 통해 클라이언트는 이후의 모든 방문에서 Cache Storage API 캐시에 직접 액세스할 수 있습니다 . 서비스 워커는 브라우저에서 서비스 워커 스크립트가 어떤 식으로든 변경되었음을 감지할 때만 설치되므로 캐시된 파일이 변경될 때 서비스 워커 스크립트 파일 자체가 어떤 식으로든 변경되었는지 확인해야 했습니다. 서비스 워커 스크립트 내에 정적 리소스 파일 세트의 해시를 삽입하여 이 작업을 수동으로 수행했으므로 모든 출시에서 고유한 서비스 워커 JavaScript 파일이 생성되었습니다. 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)));

서비스 워커 스크립트의 단위 테스트가 어렵기 때문에 Google은 핵심 서비스 워커 스크립트를 최대한 기본형으로 유지하여 대부분의 구현을 다른 모듈로 분할했습니다. 이러한 파일은 표준 JS 모듈일 뿐이므로 표준 테스트 라이브러리로 더 쉽게 단위 테스트할 수 있습니다.

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

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