Web In Play의 새로운 기능

게시일: 2020년 12월 2일

신뢰할 수 있는 웹 활동이 도입된 이후 Chrome팀은 Bubblewrap에서 더 쉽게 사용할 수 있도록 했습니다. Google Play 결제 통합과 같은 기능이 추가되었으며 ChromeOS와 같은 더 많은 플랫폼에서 작동하도록 지원되었습니다.

Bubblewrap 및 신뢰할 수 있는 웹 활동 기능

Bubblewrap을 사용하면 플랫폼별 도구에 대한 지식이 없어도 신뢰할 수 있는 웹 활동 내에서 PWA를 실행하는 앱을 만들 수 있습니다.

간소화된 설정 흐름

이전에는 Bubblewrap을 사용하려면 오류가 발생하기 쉬운 Java 개발 키트와 Android SDK를 수동으로 설정해야 했습니다. 이제 이 도구는 처음 실행할 때 외부 종속 항목을 자동으로 다운로드할 수 있는 옵션을 제공합니다.

원하는 경우 기존 종속 항목 설치를 계속 사용할 수 있으며, 새 doctor 명령어는 문제를 찾고 구성 수정사항을 추천하는 데 도움이 됩니다. 이제 updateConfig 명령어를 사용하여 명령줄에서 구성을 업데이트할 수 있습니다.

마법사 개선

init로 프로젝트를 만들 때 Bubblewrap에는 Android 앱을 생성하는 데 필요한 정보가 필요합니다. 이 도구는 웹 앱 매니페스트에서 값을 추출하고 가능한 경우 기본값을 제공합니다.

새 프로젝트를 만들 때 이러한 값을 변경할 수 있지만 이전에는 각 필드의 의미가 명확하지 않았습니다. 각 입력란에 관한 설명과 유효성 검사가 개선된 초기화 대화상자가 다시 빌드되었습니다.

전체 화면 및 방향 지원 표시

경우에 따라 애플리케이션이 화면을 최대한 많이 사용하도록 할 수 있으며, PWA를 빌드할 때는 웹 앱 매니페스트의 display 필드를 fullscreen로 설정하여 이를 구현합니다.

Bubblewrap은 웹 앱 매니페스트에서 전체 화면 옵션을 감지하면 Android용 용어로 전체 화면 또는 몰입형 모드로 실행되도록 Android 애플리케이션을 구성합니다.

웹 앱 매니페스트의 orientation 필드는 애플리케이션을 세로 모드, 가로 모드 또는 기기가 현재 사용 중인 방향으로 시작해야 하는지 정의합니다. 이제 Bubblewrap은 웹 앱 매니페스트 필드를 읽고 Android 앱을 만들 때 기본값으로 사용합니다.

bubblewrap init 흐름의 일부로 두 구성을 모두 맞춤설정할 수 있습니다.

AppBundles 출력

App Bundle은 최종 APK 생성 및 서명을 Play에 위임하는 앱의 게시 형식입니다. 실제로 이렇게 하면 스토어에서 앱을 다운로드할 때 더 작은 파일이 사용자에게 제공될 수 있습니다.

이제 Bubblewrap은 애플리케이션을 app-release-bundle.aab라는 파일의 앱 번들로 패키징합니다. 2021년부터 스토어에서 요구하므로 Play 스토어에 앱을 게시할 때는 이 형식을 사용하는 것이 좋습니다.

위치정보 위임

사용자는 기기에 설치된 애플리케이션이 기술과 관계없이 일관되게 동작하기를 기대합니다. 이제 신뢰할 수 있는 웹 활동 내에서 사용하면 GeoLocation 권한을 운영체제에 위임할 수 있으며, 사용 설정하면 사용자에게 Kotlin 또는 Java로 빌드된 앱과 동일한 대화상자가 표시되고 동일한 위치에서 권한을 관리하는 컨트롤을 찾을 수 있습니다.

이 기능은 Bubblewrap을 통해 추가할 수 있으며 Android 프로젝트에 추가 종속 항목을 추가하므로 웹 앱에서 위치 정보 액세스 권한을 사용하는 경우에만 사용 설정해야 합니다.

최적화된 바이너리

저장용량이 제한된 기기는 특정 지역에서 흔하며 이러한 기기의 소유자는 소형 애플리케이션을 선호하는 경우가 많습니다. 신뢰할 수 있는 웹 활동을 사용하는 애플리케이션은 작은 바이너리를 생성하므로 이러한 사용자의 불안을 일부 해소할 수 있습니다.

필요한 Android 라이브러리 목록을 줄여 Bubblewrap을 최적화하여 생성된 바이너리의 크기가 80만 배 줄었습니다. 실제로는 이전 버전에서 생성된 평균 크기의 절반도 되지 않습니다. 더 작은 바이너리를 활용하려면 최신 버전의 Bubblewrap을 사용하여 앱을 업데이트하기만 하면 됩니다.

기존 앱을 업데이트하는 방법

Bubblewrap에서 생성된 애플리케이션은 웹 애플리케이션과 PWA를 여는 경량 Android 래퍼로 구성됩니다. 신뢰할 수 있는 웹 활동 내에서 열린 PWA는 다른 웹 앱과 동일한 업데이트 주기를 따르지만 네이티브 래퍼는 업데이트할 수 있고 업데이트해야 합니다.

최신 버그 수정 및 기능이 포함된 최신 버전의 래퍼를 사용하도록 앱을 업데이트해야 합니다. 최신 버전의 Bubblewrap이 설치된 경우 update 명령어는 기존 프로젝트에 최신 버전의 래퍼를 적용합니다.

npm update -g @bubblewrap/cli
bubblewrap update
bubblewrap build

이러한 애플리케이션을 업데이트하는 또 다른 이유는 웹 매니페스트의 변경사항이 애플리케이션에 적용되도록 하기 위해서입니다. 새 merge 명령어를 사용합니다.

bubblewrap merge
bubblewrap update
bubblewrap build

품질 기준 업데이트

Chrome 86에서는 신뢰할 수 있는 웹 활동 품질 기준에 변경사항이 도입되었습니다. 자세한 내용은 신뢰할 수 있는 웹 활동을 사용하는 PWA의 품질 기준 변경사항을 참고하세요.

간단히 요약하면 애플리케이션이 비정상 종료를 방지하려면 다음 시나리오를 처리해야 합니다.

  • 애플리케이션 실행 시 디지털 애셋 링크 확인 실패
  • 오프라인 네트워크 리소스 요청에 HTTP 200을 반환하지 않음
  • 애플리케이션에서 HTTP 404 또는 5xx 오류가 반환됩니다.

애플리케이션이 디지털 애셋 링크 유효성 검사를 통과하는지 확인하는 것 외에도 나머지 시나리오는 서비스 워커에서 처리할 수 있습니다.

self.addEventListener('fetch', event => {
  event.respondWith((async () => {
    try {
      return await fetchAndHandleError(event.request);
    } catch {
      // Failed to load from the network. User is offline or the response
      // has a status code that triggers the Quality Criteria.
      // Try loading from cache.
      const cachedResponse = await caches.match(event.request);
      if (cachedResponse) {
        return cachedResponse;
      }
      // Response was not found on the cache. Send the error / offline
      // page. OFFLINE_PAGE should be pre-cached when the service worker
      // is activated.
      return await caches.match(OFFLINE_PAGE);
    }
  })());
});

async function fetchAndHandleError(request) {
  const cache = await caches.open(RUNTIME_CACHE);
  const response = await fetch(request);

  // Throw an error if the response returns one of the status
  // that trigger the Quality Criteria.
  if (response.status === 404 ||
      response.status >= 500 && response.status < 600) {
    throw new Error(`Server responded with status: ${response.status}`);
  }

  // Cache the response if the request is successful.
  cache.put(request, response.clone());
  return response;
}

Workbox는 권장사항을 베이킹하고 서비스 워커를 사용할 때 불필요한 코드를 삭제합니다. 또는 Workbox 플러그인을 사용하여 이러한 시나리오를 처리하는 것이 좋습니다.

export class FallbackOnErrorPlugin {
  constructor(offlineFallbackUrl, notFoundFallbackUrl, serverErrorFallbackUrl) {
    this.notFoundFallbackUrl = notFoundFallbackUrl;
    this.offlineFallbackUrl = offlineFallbackUrl;
    this.serverErrorFallbackUrl = serverErrorFallbackUrl;
  }

  checkTrustedWebActivityCrash(response) {
    if (response.status === 404 || response.status >= 500 && response.status <= 600) {
      const type = response.status === 404 ? 'E_NOT_FOUND' : 'E_SERVER_ERROR';
      const error = new Error(`Invalid response status (${response.status})`);
      error.type = type;
      throw error;
    }
  }

  // This is called whenever there's a network response,
  // but we want special behavior for 404 and 5**.
  fetchDidSucceed({response}) {
    // Cause a crash if this is a Trusted Web Activity crash.
    this.checkTrustedWebActivityCrash(response);

    // If it's a good response, it can be used as-is.
    return response;
  }

  // This callback is new in Workbox v6, and is triggered whenever
  // an error (including a NetworkError) is thrown when a handler runs.
  handlerDidError(details) {
    let fallbackURL;
    switch (details.error.details.error.type) {
      case 'E_NOT_FOUND': fallbackURL = this.notFoundFallbackUrl; break;
      case 'E_SERVER_ERROR': fallbackURL = this.serverErrorFallbackUrl; break;
      default: fallbackURL = this.offlineFallbackUrl;
    }

    return caches.match(fallbackURL, {
      // Use ignoreSearch as a shortcut to work with precached URLs
      // that have _WB_REVISION parameters.
      ignoreSearch: true,
    });
  }
}