PWA로 전환하여 MishiPay 비즈니스에 어떤 도움이 되었는지 알아보세요.
MishiPay를 사용하면 쇼핑객이 계산대에서 줄을 서서 시간을 낭비하지 않고 스마트폰으로 상품을 스캔하고 결제할 수 있습니다. MishiPay의 스캔 앤 고 기술을 사용하면 쇼핑객이 자신의 휴대전화를 사용하여 상품의 바코드를 스캔하고 결제한 후 매장을 바로 나갈 수 있습니다. 연구에 따르면 매장 내 대기열로 인해 전 세계 소매 부문에서 매년 약 2,000억 달러의 비용이 발생합니다.
Google의 기술은 사용자가 MishiPay 지원 매장을 찾고, 오프라인 매장 내에서 상품 바코드를 스캔한 후 원하는 디지털 결제 수단을 사용하여 결제할 수 있는 GPS 센서 및 카메라와 같은 기기 하드웨어 기능을 기반으로 합니다. 스캔 앤 고 기술의 초기 버전은 플랫폼별 iOS 및 Android 애플리케이션이었으며, 이 기술은 초기 사용자들로부터 좋은 반응을 얻었습니다. PWA로 전환하여 거래가 10배 증가하고 대기열에 2.5년이 절약된 방법을 알아보세요.
10×
거래 증가
2.5년
현재 재생목록 저장됨
도전과제
사용자는 대기열이나 계산대에서 대기할 때 이 기술을 사용하면 대기열을 건너뛰고 원활한 매장 경험을 누릴 수 있어 매우 유용하다고 생각합니다. 하지만 Android 또는 iOS 애플리케이션을 다운로드하는 번거로움으로 인해 사용자는 가치가 있음에도 불구하고 Google 기술을 선택하지 않았습니다. MishiPay에게는 점점 더 큰 도전이었고 진입 장벽을 낮춰 사용자 채택을 늘려야 했습니다.
솔루션
PWA를 빌드하고 출시하기 위한 노력을 통해 설치의 번거로움을 없애고 신규 사용자가 오프라인 매장에서 Google 기술을 사용해 보고, 대기열을 건너뛰고, 원활한 쇼핑 경험을 즐길 수 있도록 했습니다. 출시 이후 플랫폼별 애플리케이션에 비해 PWA의 사용자 채택이 급증했습니다.
기술 심층 분석
MishiPay를 사용 설정한 매장 찾기
이 기능을 사용 설정하려면 IP 기반 대체 솔루션과 함께 getCurrentPosition()
API를 사용합니다.
const geoOptions = {
timeout: 10 * 1000,
enableHighAccuracy: true,
maximumAge: 0,
};
window.navigator.geolocation.getCurrentPosition(
(position) => {
const cords = position.coords;
console.log(`Latitude : ${cords.latitude}`);
console.log(`Longitude : ${cords.longitude}`);
},
(error) => {
console.debug(`Error: ${error.code}:${error.message}`);
/**
* Invoke the IP based location services
* to fetch the latitude and longitude of the user.
*/
},
geoOptions,
);
이 접근 방식은 앱의 이전 버전에서는 잘 작동했지만 나중에 다음과 같은 이유로 MishiPay 사용자에게 큰 불편을 끼치는 것으로 확인되었습니다.
- IP 기반 대체 솔루션의 위치 부정확성
- 지역별로 MishiPay를 지원하는 매장 목록이 늘어나면서 사용자는 목록을 스크롤하여 올바른 매장을 찾아야 합니다.
- 사용자가 실수로 잘못된 스토어를 선택하여 구매가 잘못 기록되는 경우가 있습니다.
이러한 문제를 해결하기 위해 매장마다 매장 내 디스플레이에 고유한 위치 기반 QR 코드를 삽입했습니다. 이를 통해 더 빠른 온보딩 환경을 제공할 수 있었습니다. 사용자는 매장에 있는 마케팅 자료에 인쇄된 위치정보가 포함된 QR 코드를 스캔하기만 하면 스캔 앤 고 웹 애플리케이션에 액세스할 수 있습니다.
이렇게 하면 웹 주소 mishipay.shop
를 입력하지 않고도 서비스에 액세스할 수 있습니다.
제품 스캔
MishiPay 앱의 핵심 기능은 바코드 스캔입니다. 이를 통해 사용자는 계산대에 도달하기 전에 자신의 구매 내역을 스캔하고 실시간 총계를 확인할 수 있습니다.
웹에서 스캔 환경을 빌드하기 위해 Google은 세 가지 핵심 레이어를 파악했습니다.
동영상 스트림
getUserMedia()
메서드를 사용하면 아래의 제약 조건으로 사용자의 후방 카메라에 액세스할 수 있습니다. 이 메서드를 호출하면 사용자가 카메라 액세스를 수락하거나 거부하라는 메시지가 자동으로 표시됩니다. 동영상 스트림에 액세스할 수 있게 되면 아래와 같이 동영상 요소에 전달할 수 있습니다.
/**
* Video Stream Layer
* https://developer.mozilla.org/docs/Web/API/MediaDevices/getUserMedia
*/
const canvasEle = document.getElementById('canvas');
const videoEle = document.getElementById('videoElement');
const canvasCtx = canvasEle.getContext('2d');
fetchVideoStream();
function fetchVideoStream() {
let constraints = { video: { facingMode: 'environment' } };
if (navigator.mediaDevices !== undefined) {
navigator.mediaDevices
.getUserMedia(constraints)
.then((stream) => {
videoEle.srcObject = stream;
videoStream = stream;
videoEle.play();
// Initiate frame capture - Processing Layer.
})
.catch((error) => {
console.debug(error);
console.warn(`Failed to access the stream:${error.name}`);
});
} else {
console.warn(`getUserMedia API not supported!!`);
}
}
처리 레이어
지정된 동영상 스트림에서 바코드를 감지하려면 주기적으로 프레임을 캡처하여 디코더 레이어로 전송해야 합니다. 프레임을 캡처하려면 Canvas API의 drawImage()
메서드를 사용하여 VideoElement
의 스트림을 HTMLCanvasElement
에 그립니다.
/**
* Processing Layer - Frame Capture
* https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Manipulating_video_using_canvas
*/
async function captureFrames() {
if (videoEle.readyState === videoEle.HAVE_ENOUGH_DATA) {
const canvasHeight = (canvasEle.height = videoEle.videoHeight);
const canvasWidth = (canvasEle.width = videoEle.videoWidth);
canvasCtx.drawImage(videoEle, 0, 0, canvasWidth, canvasHeight);
// Transfer the `canvasEle` to the decoder for barcode detection.
const result = await decodeBarcode(canvasEle);
} else {
console.log('Video feed not available yet');
}
}
고급 사용 사례의 경우 이 레이어는 자르기, 회전, 회색조로 변환과 같은 일부 사전 처리 작업도 실행합니다. 이러한 작업은 CPU를 많이 사용할 수 있으며 바코드 스캔이 장기 실행 작업이므로 애플리케이션이 응답하지 않을 수 있습니다. OffscreenCanvas API를 사용하면 CPU 집약적인 작업을 웹 작업자에게 오프로드할 수 있습니다. 하드웨어 그래픽 가속을 지원하는 기기에서 WebGL API와 WebGL2RenderingContext
는 CPU 집약적인 사전 처리 작업의 이득을 최적화할 수 있습니다.
디코더 레이어
마지막 레이어는 처리 레이어에서 캡처한 프레임에서 바코드를 디코딩하는 디코더 레이어입니다. 아직 일부 브라우저에서만 사용할 수 있는 Shape Detection API 덕분에 브라우저 자체가 ImageBitmapSource
에서 바코드를 디코딩합니다. ImageBitmapSource
는 img
요소, SVG image
요소, video
요소, canvas
요소, Blob
객체, ImageData
객체 또는 ImageBitmap
객체일 수 있습니다.
/**
* Barcode Decoder with Shape Detection API
* https://web.dev/shape-detection/
*/
async function decodeBarcode(canvas) {
const formats = [
'aztec',
'code_128',
'code_39',
'code_93',
'codabar',
'data_matrix',
'ean_13',
'ean_8',
'itf',
'pdf417',
'qr_code',
'upc_a',
'upc_e',
];
const barcodeDetector = new window.BarcodeDetector({
formats,
});
try {
const barcodes = await barcodeDetector.detect(canvas);
console.log(barcodes);
return barcodes.length > 0 ? barcodes[0]['rawValue'] : undefined;
} catch (e) {
throw e;
}
}
아직 Shape Detection API를 지원하지 않는 기기의 경우 바코드를 디코딩하는 대체 솔루션이 필요합니다. Shape Detection API는 Shape Detection API와 대체 솔루션 간에 전환하는 데 도움이 되는 getSupportedFormats()
메서드를 노출합니다.
// Feature detection.
if (!('BarceodeDetector' in window)) {
return;
}
// Check supported barcode formats.
BarcodeDetector.getSupportedFormats()
.then((supportedFormats) => {
supportedFormats.forEach((format) => console.log(format));
});
대체 솔루션
모든 웹 애플리케이션과 쉽게 통합하여 검사를 구현할 수 있는 여러 오픈소스 및 엔터프라이즈 검사 라이브러리가 있습니다. 다음은 MishiPay에서 권장하는 라이브러리입니다.
위의 모든 라이브러리는 위에 설명된 모든 레이어를 구성하는 본격적인 SDK입니다. 또한 다양한 스캔 작업을 지원하는 인터페이스를 노출합니다. 비즈니스 사례에 필요한 바코드 형식과 감지 속도에 따라 Wasm 솔루션과 비-Wasm 솔루션 중에서 선택할 수 있습니다. 바코드를 디코딩하는 데 추가 리소스 (Wasm)가 필요한 오버헤드가 있음에도 불구하고 Wasm 솔루션은 정확성 측면에서 Wasm 이외의 솔루션보다 우수합니다.
Scandit이 주된 선택이었습니다. 비즈니스 사용 사례에 필요한 모든 바코드 형식을 지원하며, 스캔 속도에서 사용 가능한 모든 오픈소스 라이브러리를 능가합니다.
미래의 스캔
Shape Detection API가 모든 주요 브라우저에서 완전히 지원되면 바코드 스캐너에 필요한 기능이 있는 새 HTML 요소 <scanner>
가 있을 수 있습니다. MishiPay 엔지니어링팀은 스캔 앤 고와 같은 환경을 지원하는 오픈소스 및 라이선스 라이브러리의 수가 증가함에 따라 바코드 스캔 기능이 새로운 HTML 요소가 될 수 있는 확실한 사용 사례가 있다고 생각합니다.
결론
앱 피로는 제품이 시장에 출시될 때 개발자가 직면하는 문제입니다. 사용자는 애플리케이션을 다운로드하기 전에 애플리케이션이 제공하는 가치를 이해하고 싶어 하는 경우가 많습니다. MishiPay가 쇼핑객의 시간을 절약하고 경험을 개선하는 매장에서 애플리케이션을 사용하기 전에 다운로드를 기다리는 것은 직관에 어긋납니다. 이때 PWA가 도움이 됩니다. 진입 장벽을 없애면서 거래가 10배 증가했고 사용자는 대기열에서 2.5년을 기다릴 필요가 없어졌습니다.
감사의 말씀
이 도움말은 조 미들리님이 검토했습니다.