Wake Lock API 우수사례: BettyCrocker.com에서 구매 의도 지표 300% 증가

레시피 단계 중간에 화면이 꺼지는 것보다 더 나쁜 일은 없습니다. 요리 사이트인 BettyCrocker.com에서 Wake Lock API를 사용하여 이러한 문제를 방지한 방법을 알아보세요.

거의 한 세기 동안 베티 크로커는 미국에서 현대 요리 강좌를 받고 신뢰할 수 있는 레시피를 개발해 왔습니다. 1997년에 시작한 BettyCrocker.com 사이트는 현재 매달 1, 200만 명 이상의 방문자를 맞이하고 있습니다. Wake Lock API를 구현한 후 wake lock 사용자의 구매 의도 지표가 모든 사용자에 비해 약 300% 높았습니다.

베티 크로커는 2014년에 엄청난 풍성한 팬 작전을 펼친 후 앱 우선순위가 낮아진 후 최근 Apple App Store와 Google Play 스토어에서 앱을 제거했습니다. 오랫동안 Betty Crocker팀은 iOS/Android 앱 대신 모바일 사이트에 새 기능을 추가하는 것을 선호했습니다. iOS/Android 앱이 제작된 기술 플랫폼이 오래되었으며, 비즈니스에는 향후 앱 업데이트 및 유지관리를 지원할 리소스가 없었습니다. 또한 웹 앱은 객관적으로 트래픽이 훨씬 더 많고, 더 현대적이며, 개선하기가 더 쉬웠습니다.

하지만 iOS/Android 앱에는 사용자가 만족스러워하는 킬러 기능이 한 가지 있었습니다.

밀레니얼 세대 요리 전문가 팁: @BettyCrocker 모바일 앱은 레시피를 따라할 때 화면이 어두워지거나 잠기지 않습니다. —@AvaBeilke

80% 의 사용자가 주방에서 기기로 요리하지만 화면 어둡게하기 및 잠금이 문제가 됩니다. @BettyCrocker는 어떻게 했나요? 사용자가 레시피에 있을 때 화면이 어두워지지 않도록 앱을 업데이트했습니다. —@KatieTweedy

Wake Lock API로 웹에 강력한 기능 제공

기기로 요리할 때 화면이 꺼져 있을 때 더러운 손이나 코로 화면을 터치해야 하는 것보다 더 불편한 일은 없습니다. Betty Crocker는 iOS/Android 앱의 핵심 기능을 웹 앱으로 이식하는 방법을 모색했습니다. 이때 Project FuguWake Lock API를 알게 되었습니다.

밀가루로 뒤덮인 식탁에서 반죽을 반죽하고 있는 사람

Wake Lock API는 기기의 화면을 어둡게 하거나 잠그지 않도록 하는 방법을 제공합니다. 이 기능은 지금까지 iOS/Android 앱이 필요했던 새로운 환경을 지원합니다. Wake Lock API는 해킹할 수 있고 전력을 많이 소모할 가능성이 있는 해결 방법의 필요성을 줄여줍니다.

wake lock 요청

wake lock을 요청하려면 WakeLockSentinel 객체를 반환하는 navigator.wakeLock.request() 메서드를 호출해야 합니다. 이 객체는 센티널 값으로 사용됩니다. 브라우저는 다양한 이유(예: 배터리가 너무 낮음)로 요청을 거부할 수 있으므로 호출을 try…catch 문에 래핑하는 것이 좋습니다.

wake lock 해제

또한 WakeLockSentinel 객체의 release() 메서드를 호출하여 웨이크락을 해제하는 방법도 필요합니다. 일정 시간이 지나면 자동으로 절전 잠금을 해제하려면 아래 예와 같이 window.setTimeout()를 사용하여 release()를 호출하면 됩니다.

// The wake lock sentinel.
let wakeLock = null;

// Function that attempts to request a wake lock.
const requestWakeLock = async () => {
  try {
    wakeLock = await navigator.wakeLock.request('screen');
    wakeLock.addEventListener('release', () => {
      console.log('Wake Lock was released');
    });
    console.log('Wake Lock is active');
  } catch (err) {
    console.error(`${err.name}, ${err.message}`);
  }
};

// Request a wake lock…
await requestWakeLock();
// …and release it again after 5s.
window.setTimeout(() => {
  wakeLock.release();
  wakeLock = null;
}, 5000);

구현

새로운 웹 앱을 사용하면 사용자가 레시피를 쉽게 탐색하고, 단계를 완료하고, 화면이 잠기지 않고도 자리를 뜰 수 있습니다. 이 목표를 달성하기 위해 팀은 먼저 개념 증명으로 사용하고 UX 입력을 수집하기 위한 빠른 프런트엔드 프로토타입을 제작했습니다.

프로토타입이 유용하다는 것이 입증되자 모든 브랜드 (BettyCrocker, Pillsbury, Tablespoon)에서 공유할 수 있는 Vue.js 구성요소를 설계했습니다. Betty Crocker에만 iOS 및 Android 앱이 있었지만 세 사이트에는 공유 코드베이스가 있으므로 아래 스크린샷과 같이 구성요소를 한 번 구현하고 모든 위치에 배포할 수 있었습니다.

BettyCrocker.com wake lock 전환
BettyCrocker.com의 wake lock 전환
Pillsbury.com wake lock 전환
Pillsbury.com wake lock 전환 버튼
Tablespoon.com wake lock 전환
Tablespoon.com wake lock 전환 버튼

새 사이트의 현대화된 프레임워크를 기반으로 구성요소를 개발할 때는 MVVM 패턴의 ViewModel 레이어에 중점을 두었습니다. 또한 팀은 사이트의 기존 프레임워크와 새 프레임워크에서 기능을 사용 설정할 수 있도록 상호 운용성을 염두에 두고 프로그래밍했습니다.

조회가능성과 사용성을 추적하기 위해 Betty Crocker는 절전 모드 잠금 수명 주기의 핵심 이벤트에 대한 분석 추적을 통합했습니다. 팀은 기능 관리를 활용하여 wake lock 구성요소를 단일 사이트에 배포한 다음 초기 프로덕션 출시를 위해 사용 및 페이지 상태를 모니터링한 후 나머지 사이트에 이 기능을 배포했습니다. 계속해서 이 구성요소의 사용을 기반으로 분석 데이터를 모니터링합니다.

사용자를 위한 안전 장치로 팀은 1시간 동안 활동이 없으면 wake lock을 사용 중지하는 강제 제한 시간을 만들었습니다. 최종적으로 결정한 구현은 사이트의 모든 레시피 페이지에 단기적으로 전환 스위치를 추가하는 것이었습니다. 장기적으로는 레시피 페이지 뷰를 개선할 계획입니다.

wake lock 컨테이너

var wakeLockControl = () => {
  return import(/* webpackChunkName: 'wakeLock' */ './wakeLock');
};

export default {
  components: {
    wakeLockControl: wakeLockControl,
  },
  data() {
    return {
      config: {},
      wakeLockComponent: '',
    };
  },
  methods: {
    init: function(config) {
      this.config = config || {};
      if ('wakeLock' in navigator && 'request' in navigator.wakeLock) {
        this.wakeLockComponent = 'wakeLockControl';
      } else {
        console.log('Browser not supported');
      }
    },
  },
};

wake lock 구성요소

<template>
  <div class="wakeLock">
    <div class="textAbove"></div>
    <label class="switch" :aria-label="settingsInternal.textAbove">
      <input type="checkbox" @change="onChange()" v-model="isChecked">
      <span class="slider round"></span>
    </label>
  </div>
</template>

<script type="text/javascript">
  import debounce from 'lodash.debounce';

  const scrollDebounceMs = 1000;

  export default {
    props: {
      settings: { type: Object },
    },
    data() {
      return {
        settingsInternal: this.settings || {},
        isChecked: false,
        wakeLock: null,
        timerId: 0,
      };
    },
    created() {
      this.$_raiseAnalyticsEvent('Wake Lock Toggle Available');
    },
    methods: {
      onChange: function() {
        if (this.isChecked) {
          this.$_requestWakeLock();
        } else {
          this.$_releaseWakeLock();
        }
      },
      $_requestWakeLock: async function() {
        try {
          this.wakeLock = await navigator.wakeLock.request('screen');
          //Start new timer
          this.$_handleAbortTimer();
          //Only add event listeners after wake lock is successfully enabled
          document.addEventListener(
            'visibilitychange',
            this.$_handleVisibilityChange,
          );
          window.addEventListener(
            'scroll',
            debounce(this.$_handleAbortTimer, scrollDebounceMs),
          );
          this.$_raiseAnalyticsEvent('Wake Lock Toggle Enabled');
        } catch (e) {
          this.isChecked = false;
        }
      },
      $_releaseWakeLock: function() {
        try {
          this.wakeLock.release();
          this.wakeLock = null;
          //Clear timer
          this.$_handleAbortTimer();
          //Clean up event listeners
          document.removeEventListener(
            'visibilitychange',
            this.$_handleVisibilityChange,
          );
          window.removeEventListener(
            'scroll',
            debounce(this.$_handleAbortTimer, scrollDebounceMs),
          );
        } catch (e) {
          console.log(`Wake Lock Release Error: ${e.name}, ${e.message}`);
        }
      },
      $_handleAbortTimer: function() {
        //If there is an existing timer then clear it and set to zero
        if (this.timerId !== 0) {
          clearTimeout(this.timerId);
          this.timerId = 0;
        }
        //Start new timer; Will be triggered from toggle enabled or scroll event
        if (this.isChecked) {
          this.timerId = setTimeout(
            this.$_releaseWakeLock,
            this.settingsInternal.timeoutDurationMs,
          );
        }
      },
      $_handleVisibilityChange: function() {
        //Handle navigating away from page/tab
        if (this.isChecked) {
          this.$_releaseWakeLock();
          this.isChecked = false;
        }
      },
      $_raiseAnalyticsEvent: function(eventType) {
        let eventParams = {
          EventType: eventType,
          Position: window.location.pathname || '',
        };
        Analytics.raiseEvent(eventParams);
      },
    },
  };
</script>

결과

Vue.js 구성요소가 세 사이트 모두에 배포되어 우수한 결과를 얻었습니다. 2019년 12월 10일부터 2020년 1월 10일까지 BettyCrocker.com은 다음과 같은 측정항목을 보고했습니다.

  • Wake Lock API와 호환되는 브라우저를 사용하는 모든 Betty Crocker 사용자 중 3.5% 가 이 기능을 즉시 사용 설정하여 상위 5개 작업 중 하나가 되었습니다.
  • 절전 모드를 사용 설정한 사용자의 세션 지속 시간은 사용 설정하지 않은 사용자의 세션 지속 시간보다 3.1배 더 길었습니다.
  • 절전 모드 잠금을 사용 설정한 사용자의 이탈률은 절전 모드 잠금 기능을 사용하지 않는 사용자보다 50% 낮았습니다.
  • 구매 의도 지표는 전체 사용자에 비해 Wake Lock 사용자의 경우 약 300% 더 높았습니다.

3.1

세션 시간 연장

50%

이탈률 감소

300%

구매 의도 지표가 더 높음

결론

Betty Crocker는 Wake Lock API를 사용하여 놀라운 결과를 얻었습니다. 사이트(BettyCrocker, Pillsbury 또는 Tablespoon)에서 좋아하는 레시피를 검색하고 요리 중 화면이 어두워지지 않도록 하기 전환 버튼을 사용 설정하여 이 기능을 직접 테스트해 볼 수 있습니다.

웨이크 락의 사용 사례는 레시피 사이트에만 국한되지 않습니다. 다른 예로는 바코드를 스캔할 때까지 화면을 켜진 상태로 유지해야 하는 탑승권 또는 티켓 앱, 화면을 계속 켜진 상태로 유지하는 키오스크 스타일 앱 또는 프레젠테이션 중에 화면이 절전 모드로 전환되지 않도록 하는 웹 기반 프레젠테이션 앱이 있습니다.

이 사이트의 종합적인 도움말에서 Wake Lock API에 관해 알아야 할 모든 것을 확인할 수 있습니다. 즐겁게 읽고 맛있는 요리 만들어 보세요.

감사의 말씀

반죽을 반죽하는 사람 사진은 UnsplashJulian Hochgesang님이 제공해 주셨습니다.