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

Betty Crocker는 거의 한 세기 동안 현대적인 요리 교육과 신뢰할 수 있는 레시피 개발을 위한 미국의 소스였습니다. 1997년에 출시된 BettyCrocker.com은 현재 월간 방문자 수가 1, 200만 명을 넘습니다. Wake Lock API를 구현한 후 Wake Lock 사용자의 구매 의도 표시기가 모든 사용자에 비해 약 300% 더 높았습니다.

지원 중단된 iOS 및 Android 앱

2014년에 큰 화제를 모으며 출시된 Betty Crocker 앱은 최근 우선순위가 낮아진 후 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 앱이 필요했던 새로운 환경을 구현할 수 있습니다. 절전 모드 해제 잠금 API를 사용하면 해킹이 필요하고 전력 소모가 클 수 있는 해결 방법의 필요성이 줄어듭니다.

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 절전 모드 해제 전환 버튼
Pillsbury.com wake lock 전환
Pillsbury.com 절전 모드 해제 전환 버튼
Tablespoon.com wake lock 전환
Tablespoon.com 절전 모드 해제 토글

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

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

사용자를 위한 안전 장치로 팀에서는 1시간 동안 비활성 상태가 되면 절전 모드를 사용 중지하는 강제 제한 시간을 만들었습니다. 최종적으로 결정된 구현은 사이트의 모든 레시피 페이지에 단기적으로 전환 스위치를 추가하는 것이었습니다. 장기적으로는 레시피 페이지 조회 개선을 목표로 하고 있습니다.

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% 낮았습니다.
  • 절전 모드 해제 사용자의 구매 의도 지표는 전체 사용자보다 약 300% 높았습니다.

3.1×

세션 시간 연장

50%

이탈률 감소

300%

높은 구매 의도 지표

결론

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

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

이 사이트의 포괄적인 도움말에서 절전 모드 해제 API에 관해 알아야 할 모든 것을 확인하세요. 즐거운 독서와 요리 되세요!