PWA를 구현한 후 전환율이 55% 증가한 Mainline Menswear

메인라인은 패션계 최고의 디자이너 브랜드 이름을 제공하는 온라인 의류 소매업체입니다. 영국에 본사를 둔 이 회사는 주요 파트너와 전략적으로 결합된 사내 전문가로 구성된 팀을 통해 모든 사용자에게 원활한 쇼핑 경험을 제공한다는 확신을 가지고 있습니다. 7개의 맞춤 제작된 지역 웹사이트와 앱을 통해 100개 이상의 국가에서 시장을 공략하는 메인라인은 계속해서 전자상거래 제품이 경쟁 제품에 맞설 수 있도록 지원할 것입니다.

당면 과제

메인라인 남성복의 목표는 성장하는 스마트폰 시장을 염두에 두고 모바일 친화적인 디자인과 기능에 중점을 두고 '모바일 우선' 비전을 준수하는 점진적인 기능으로 현재의 모바일에 최적화된 웹사이트를 보완하는 것이었습니다.

솔루션

목표는 메인라인 남성복 웹사이트의 원래 모바일 친화적인 버전을 보완하는 PWA를 빌드하고 출시한 다음, 현재 Android와 iOS에서 사용 가능한 하이브리드 모바일 앱과 통계를 비교하는 것이었습니다.

앱이 출시되어 소수의 메인라인 남성용 사용자가 사용하자 PWA, 앱, 웹 간의 주요 통계 차이를 확인할 수 있었습니다.

Mainline이 웹사이트를 PWA로 전환할 때 사용한 접근 방식은 웹사이트를 위해 선택한 프레임워크 (Vue.js를 활용하는 Nuxt.js)가 미래 경쟁력을 확보하고 빠르게 움직이는 웹 기술을 활용할 수 있도록 하는 것이었습니다.

결과

139%

웹 대비 PWA의 세션당 페이지 수 증가

161%

웹 대비 PWA에서 세션 시간이 더 깁니다.

10%

웹 대비 PWA의 이탈률 감소

12.5%

웹 대비 PWA에서 평균 주문 금액이 더 높음

55%

웹 대비 PWA에서 더 높은 전환율

243%

웹 대비 PWA에서 더 높은 세션당 수익

기술 심층 정보

Mainline MenswearNuxt.js 프레임워크를 사용하여 단일 페이지 애플리케이션 (SPA)인 사이트를 번들로 묶고 렌더링합니다.

서비스 워커 파일 생성

Mainline Menswear는 서비스 워커를 생성하기 위해 nuxt/pwa Workbox 모듈의 맞춤 구현을 통해 구성을 추가했습니다.

nuxt/pwa 모듈을 포크한 이유는 팀이 표준 버전을 사용할 때 할 수 없었거나 문제가 발생한 맞춤설정을 서비스 워커 파일에 추가할 수 있도록 하기 위해서입니다. 이러한 최적화 중 하나는 사이트의 오프라인 기능을 기반으로 하는 것이었습니다. 예를 들어 오프라인 상태에서 기본 오프라인 페이지를 제공하고 분석을 수집하는 것이 그 예입니다.

웹 앱 매니페스트 분석

팀은 다양한 모바일 앱 아이콘 크기 및 기타 웹 앱 세부정보(예: name, description, theme_color)에 관한 아이콘이 포함된 매니페스트를 생성했습니다.

{
  "name": "Mainline Menswear",
  "short_name": "MMW",
  "description": "Shop mens designer clothes with Mainline Menswear. Famous brands including Hugo Boss, Adidas, and Emporio Armani.",
  "icons": [
    {
      "src": "/_nuxt/icons/icon_512.c2336e.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ],
  "theme_color": "#107cbb"
}

설치된 웹 앱은 브라우저를 방해하지 않고 홈 화면에서 실행할 수 있습니다. 웹 앱 매니페스트 파일에 display 매개변수를 추가하면 됩니다.

{
  "display": "standalone"
}

마지막으로, 회사는 이제 매니페스트의 start_url 필드에 utm_source 매개변수를 추가하기만 하면 홈 화면에서 웹 앱을 방문하는 사용자 수를 쉽게 추적할 수 있습니다.

{
  "start_url": "/?utm_source=pwa"
}

더 빠른 탐색을 위한 런타임 캐싱

웹 앱 캐싱은 페이지 속도를 최적화하고 재사용자에게 더 나은 사용자 환경을 제공하기 위해 반드시 필요합니다.

웹에서 캐싱의 경우 여러 가지 접근 방식이 있습니다. 팀은 클라이언트 측에서 애셋을 캐싱하기 위해 HTTP 캐시Cache API를 함께 사용하고 있습니다.

Cache API를 사용하면 메인라인 남성복이 캐시된 애셋을 더 세밀하게 제어할 수 있으므로 각 파일 형식에 복잡한 전략을 적용할 수 있습니다. 이 모든 것이 복잡하고 설정 및 유지보수가 어렵게 들리지만 Workbox는 이러한 복잡한 전략을 선언할 수 있는 간편한 방법을 제공하고 유지보수의 부담을 덜어줍니다.

CSS 및 JS 캐싱

CSS 및 JS 파일의 경우 팀은 StaleWhileRevalidate Workbox 전략을 사용하여 파일을 캐시하고 캐시 대신 제공하기로 했습니다. 이 전략을 사용하면 모든 Nuxt CSS 및 JS 파일을 빠르게 제공하여 사이트의 성능을 크게 높일 수 있습니다. 동시에 파일은 다음 방문을 위해 백그라운드에서 최신 버전으로 업데이트됩니다.

/* sw.js */
workbox.routing.registerRoute(
  /\/_nuxt\/.*(?:js|css)$/,
  new workbox.strategies.StaleWhileRevalidate({
    cacheName: 'css_js',
  }),
  'GET',
);

Google 글꼴 캐싱

Google Fonts 캐싱 전략은 두 가지 파일 형식에 따라 달라집니다.

  • @font-face 선언이 포함된 스타일시트입니다.
  • 기본 글꼴 파일 (위에 언급된 스타일시트에서 요청됨)
// Cache the Google Fonts stylesheets with a stale-while-revalidate strategy.
workbox.routing.registerRoute(
  /https:\/\/fonts\.googleapis\.com\/*/,
  new workbox.strategies.StaleWhileRevalidate({
    cacheName: 'google_fonts_stylesheets',
  }),
  'GET',
);

// Cache the underlying font files with a cache-first strategy for 1 year.
workbox.routing.registerRoute(
  /https:\/\/fonts\.gstatic\.com\/*/,
  new workbox.strategies.CacheFirst({
    cacheName: 'google_fonts_webfonts',
    plugins: [
      new workbox.cacheableResponse.CacheableResponsePlugin({
        statuses: [0, 200],
      }),
      new workbox.expiration.ExpirationPlugin({
        maxAgeSeconds: 60 * 60 * 24 * 365, // 1 year
        maxEntries: 30,
      }),
    ],
  }),
  'GET',
);

이미지 캐싱

이미지의 경우 Mainline Menswear는 두 가지 전략을 사용하기로 결정했습니다. 첫 번째 전략은 일반적으로 제품 이미지인 CDN에서 오는 모든 이미지에 적용됩니다. 이러한 페이지는 이미지가 무거워서 사용자의 기기 저장용량을 많이 차지하지 않도록 의식합니다. 따라서 Workbox는 ExpirationPlugin을 사용하여 최대 60개의 이미지CDN에서 오는 이미지만 캐싱하는 전략을 추가했습니다.

요청된 61번째 (최신) 이미지가 첫 번째 (가장 오래된) 이미지를 대체하여 어느 시점에서든 60개 이하의 제품 이미지가 캐시되지 않도록 합니다.

workbox.routing.registerRoute(
  ({ url, request }) =>
    url.origin === 'https://mainline-menswear-res.cloudinary.com' &&
    request.destination === 'image',
  new workbox.strategies.StaleWhileRevalidate({
    cacheName: 'product_images',
    plugins: [
      new workbox.expiration.ExpirationPlugin({
        // Only cache 60 images.
        maxEntries: 60,
        purgeOnQuotaError: true,
      }),
    ],
  }),
);

두 번째 이미지 전략은 출처에서 요청하는 나머지 이미지를 처리합니다. 이러한 이미지는 전체 출처에서 매우 적고 작은 경향이 있지만 안전을 위해 캐시된 이미지 수도 60개로 제한됩니다.

workbox.routing.registerRoute(
  /\.(?:png|gif|jpg|jpeg|svg|webp)$/,
  new workbox.strategies.StaleWhileRevalidate({
    cacheName: 'images',
    plugins: [
      new workbox.expiration.ExpirationPlugin({
        // Only cache 60 images.
        maxEntries: 60,
        purgeOnQuotaError: true,
      }),
    ],
  }),
);

오프라인 기능 제공

오프라인 페이지는 서비스 워커가 설치되고 활성화된 직후에 사전 캐시됩니다. 이렇게 하려면 모든 오프라인 종속 항목(오프라인 HTML 파일 및 오프라인 SVG 아이콘)의 목록을 생성합니다.

const OFFLINE_HTML = '/offline/offline.html';
const PRECACHE = [
  { url: OFFLINE_HTML, revision: '70f044fda3e9647a98f084763ae2c32a' },
  { url: '/offline/offline.svg', revision: 'efe016c546d7ba9f20aefc0afa9fc74a' },
];

그러면 사전 캐시 목록이 Workbox에 입력되며, 캐시에 URL을 추가하고 버전 불일치를 확인하며 사전 캐시된 파일을 업데이트 및 제공하는 것과 같은 모든 힘든 작업을 CacheFirst 전략을 사용하여 처리합니다.

workbox.precaching.precacheAndRoute(PRECACHE);

오프라인 탐색 처리

서비스 워커가 활성화되고 오프라인 페이지가 사전 캐시되면 이 페이지가 사용자의 오프라인 탐색 요청에 응답하는 데 사용됩니다. 메인라인 남성복의 웹 앱은 SPA이지만 오프라인 페이지는 페이지가 새로고침되거나, 사용자가 브라우저 탭을 닫았다가 다시 열거나, 오프라인 상태에서 홈 화면에서 웹 앱이 실행될 때만 표시됩니다.

이를 위해 메인라인 남성복은 사전 캐시된 오프라인 페이지를 사용하여 실패한 NavigationRoute 요청에 대체를 제공했습니다.

const htmlHandler = new workbox.strategies.NetworkOnly();
const navigationRoute = new workbox.routing.NavigationRoute(({ event }) => {
    const request = event.request;
    // A NavigationRoute matches navigation requests in the browser, i.e. requests for HTML
    return htmlHandler.handle({ event, request }).catch(() => caches.match(OFFLINE_HTML, {
        ignoreSearch: true
    }));
});
workbox.routing.registerRoute(navigationRoute);

데모

www.mainlinemenswear.co.uk에 표시된 오프라인 페이지 예

성공적인 설치 보고

홈 화면 실행 추적 (웹 애플리케이션 매니페스트에 "start_url": "/?utm_source=pwa" 사용) 외에도 웹 앱은 windowappinstalled 이벤트를 수신 대기하여 성공적인 앱 설치를 보고합니다.

window.addEventListener('appinstalled', (evt) => {
  ga('send', 'event', 'Install', 'Success');
});

웹사이트에 PWA 기능을 추가하면 고객의 쇼핑 환경이 더욱 개선되고 [플랫폼별] 앱보다 마케팅 속도가 더 빠릅니다.

앤디 호일, 개발 책임자

결론

프로그레시브 웹 앱과 이를 빌드하는 방법을 자세히 알아보려면 web.dev의 프로그레시브 웹 앱 섹션을 참고하세요.

프로그레시브 웹 앱 우수사례를 더 보려면 우수사례 섹션으로 이동하세요.