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

휴대기기로 요리할 때 레시피 단계 중간에 화면이 꺼지는 것은 가장 끔찍한 일입니다. 요리 사이트 BettyCrocker.com에서 Wake Lock API를 사용하여 이러한 상황을 방지하는 방법을 알아보세요.

베티 크로커는 거의 100년 동안 미국에서 현대식 요리 교육 및 신뢰할 수 있는 레시피 개발의 원천이 되었습니다. 1997년에 설립된 이 사이트의 BettyCrocker.com은 오늘날 월간 방문자가 1, 200만 명 이상입니다. Wake Lock API를 구현한 후 wake lock 사용자의 구매 의도 지표는 전체 사용자에 비해 약 300% 더 높았습니다.

지원 중단된 iOS 및 Android 앱

베티 크로커는 2014년에 큰 판파로 출시된 앱의 우선순위가 내려진 후 최근 Apple App Store와 Google Play 스토어에서 앱을 삭제했습니다. Betty Crocker팀은 오랫동안 iOS/Android 앱 대신 모바일 사이트에 새로운 기능을 추가하는 것을 선호했습니다. iOS/Android 앱이 만들어진 기술 플랫폼은 오래되었고, 회사에는 향후 앱 업데이트와 유지관리를 지원할 리소스가 없었습니다. 또한 웹 앱은 객관적으로 트래픽 측면에서 더 크고 현대적이며 향상하기 쉬웠습니다.

하지만 iOS/Android 앱에는 사용자들이 좋아하는 한 가지 핵심 기능이 있었습니다.

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

80% 의 사람들이 주방에서 기기로 요리하지만 화면이 어두워지거나 잠기는 것이 문제가 됩니다. @BettyCrocker가 무슨 일을 했나요? 사용자가 레시피에 있을 때 어두워지지 않도록 앱을 업데이트했습니다. —@케이티트위디

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() 메서드를 호출하여 wake lock을 해제할 방법이 필요합니다. 일정 시간이 지난 후 wake lock을 자동으로 해제하려면 아래 예와 같이 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 구성요소를 설계했습니다. 베티 크로커만 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 수명 주기의 핵심 이벤트에 관한 애널리틱스 추적을 통합했습니다. 팀은 기능 관리를 활용하여 초기 프로덕션 출시를 위해 단일 사이트에 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위에 올랐습니다.
  • wake lock을 사용 설정한 사용자의 세션 시간은 그렇지 않은 사용자보다 3.1배 더 길었습니다.
  • wake lock을 사용 설정한 사용자의 이탈률은 wake lock 기능을 사용하지 않는 사용자의 이탈률보다 50% 낮았습니다.
  • wake lock 사용자의 구매 의도 지표는 전체 사용자에 비해 약 300% 높았습니다.

3.1×

더 긴 세션 시간

50%

이탈률 감소

300%

더 높은 구매 의도 지표

결론

베티 크로커는 Wake Lock API를 사용하여 놀라운 결과를 얻었습니다. BettyCrocker, Pillsbury, Tablespoon 등의 사이트에서 좋아하는 레시피를 검색하고 요리하는 동안 화면이 어두워지지 않도록 방지 전환 버튼을 사용 설정하여 이 기능을 직접 테스트할 수 있습니다.

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

Wake Lock API에 관해 알아야 할 모든 정보를 이 사이트에 관한 포괄적인 도움말에 정리해 두었습니다. 즐겁게 읽고 즐겁게 요리하세요!

감사의 말

반죽을 반죽하는 사람(사진 제공: 줄리안 호크상상)(출처: Unsplash)