자습서: Evidently 샘플 애플리케이션으로 A/B 테스트 - Amazon CloudWatch

자습서: Evidently 샘플 애플리케이션으로 A/B 테스트

이 섹션에서는 A/B 테스트를 위해 Amazon CloudWatch Evidently를 사용하는 방법에 대한 자습서를 제공합니다. 이 자습서에서는 간단한 반응 애플리케이션인 Evidently 샘플 애플리케이션입니다. 샘플 앱은 showDiscount 기능 여부를 표시하도록 구성됩니다. 이 기능이 사용자에게 표시되면 쇼핑 웹 사이트에 표시된 가격은 20% 할인된 가격으로 표시됩니다.

이 자습서에서는 다른 사용자가 아닌 일부 사용자에게 할인을 표시하는 것 외에도 두 가지 변형에서 페이지 로드 시간 지표를 수집하도록 Evidently를 설정합니다.

주의

이 시나리오에서는 프로그래밍 방식 액세스 권한과 장기 보안 인증이 있는 IAM 사용자가 필요하며 이는 보안 위험을 내포합니다. 이 위험을 줄이려면 이러한 사용자에게 작업을 수행하는 데 필요한 권한만 제공하고 더 이상 필요하지 않을 경우 이러한 사용자를 제거하는 것이 좋습니다. 필요한 경우 액세스 키를 업데이트할 수 있습니다. 자세한 내용은 IAM 사용 설명서액세스 키 업데이트를 참조하세요.

1단계: 샘플 애플리케이션 다운로드

먼저 Evidently 샘플 애플리케이션을 다운로드합니다.

샘플 애플리케이션을 다운로드하려면
  1. 다음 Amazon S3 버킷에서 샘플 애플리케이션을 다운로드합니다.

    https://evidently-sample-application.s3.us-west-2.amazonaws.com/evidently-sample-shopping-app.zip
  2. 패키지의 압축을 풉니다.

2단계: Evidently 엔드포인트 추가 및 자격 증명 설정

그런 다음, 다음 예제와 같이 Evidenty에 대한 리전 및 엔드포인트를 샘플 앱 패키지의 src 디렉터리에 있는 config.js 파일에 추가합니다.

evidently: { REGION: "us-west-2", ENDPOINT: "https://evidently.us-west-2.amazonaws.com (https://evidently.us-west-2.amazonaws.com/)", },

또한 애플리케이션에 CloudWatch Evidenty를 호출할 수 있는 권한이 있는지 확인해야 합니다.

샘플 앱에 Evidently를 호출할 수 있는 권한을 부여하려면
  1. AWS 계정에 연동합니다.

  2. IAM 사용자를 생성하고 AmazonCloudWatchEvidentlyFullAccess 정책을 이 사용자에 연결합니다.

  3. 다음 단계에서 필요하므로 IAM 사용자의 액세스 키 ID 및 비밀 액세스 키를 기록해 둡니다.

  4. 다음 예와 같이 이 섹션의 앞부분에서 수정한 동일한 config.js 파일에 액세스 키 ID 및 비밀 액세스 키의 값을 입력합니다.

    credential: { accessKeyId: "Access key ID", secretAccessKey: "Secret key" }
    중요

    이 단계를 사용하여 샘플 앱을 최대한 간단하게 할 수 있습니다. 실제 프로덕션 애플리케이션에 IAM 사용자 자격 증명을 넣지 않는 것이 좋습니다. 대신 인증을 위해 Amazon Cognito를 사용하는 것이 좋습니다. 자세한 내용은 Amazon Cognito와 웹 및 모바일 앱 통합을 참조하세요.

3단계: 기능 평가를 위한 코드 설정

CloudWatch Evidently를 사용하여 기능을 평가할 때는 EvaluateFeature 작업을 사용하여 각 사용자 세션에 대한 기능 변형을 임의로 선택해야 합니다. 이 작업은 실험에서 지정한 비율에 따라 기능의 각 변형에 사용자 세션을 할당합니다.

북스토어 데모 앱에 대한 기능 평가 코드 설정
  1. 샘플 앱이 Evidenty를 호출할 수 있도록 클라이언트 빌더를 src/App.jsx파일에 추가합니다.

    import Evidently from 'aws-sdk/clients/evidently'; import config from './config'; const defaultClientBuilder = ( endpoint, region, ) => { const credentials = { accessKeyId: config.credential.accessKeyId, secretAccessKey: config.credential.secretAccessKey } return new Evidently({ endpoint, region, credentials, }); };
  2. 다음을 const App 코드 섹션에 추가하여 클라이언트를 시작합니다.

    if (client == null) { client = defaultClientBuilder( config.evidently.ENDPOINT, config.evidently.REGION, );
  3. 다음 코드를 추가하여 evaluateFeatureRequest를 구성합니다. 이 코드는 이 자습서의 뒷부분에서 권장하는 프로젝트 이름과 기능 이름을 미리 채웁니다. Evidently 콘솔에서 해당 프로젝트와 기능 이름도 지정하면 고유한 프로젝트와 기능 이름을 대체할 수 있습니다.

    const evaluateFeatureRequest = { entityId: id, // Input Your feature name feature: 'showDiscount', // Input Your project name' project: 'EvidentlySampleApp', };
  4. 기능 평가를 위해 Evidently를 호출할 코드를 추가합니다. 요청이 전송되면 Evidently가 사용자 세션을 무작위로 할당하여 showDiscount 기능 여부를 확인합니다.

    client.evaluateFeature(evaluateFeatureRequest).promise().then(res => { if(res.value?.boolValue !== undefined) { setShowDiscount(res.value.boolValue); } getPageLoadTime() })

4단계: 실험 지표에 대한 코드 설정

사용자 지정 지표의 경우 Evidently의 PutProjectEvents API를 사용하여 Evidently로 지표 결과를 전송합니다. 다음 예제에서는 사용자 지정 지표를 설정하고 실험 데이터를 Evidently로 전송하는 방법을 보여줍니다.

다음 함수를 추가해 페이지 로드 시간을 계산하고 PutProjectEvents를 사용하여 지표 값을 Evidently로 전송합니다. 다음 기능을 Home.tsx에 추가하고 EvaluateFeature API에서 이 함수를 호출합니다.

const getPageLoadTime = () => { const timeSpent = (new Date().getTime() - startTime.getTime()) * 1.000001; const pageLoadTimeData = `{ "details": { "pageLoadTime": ${timeSpent} }, "UserDetails": { "userId": "${id}", "sessionId": "${id}"} }`; const putProjectEventsRequest = { project: 'EvidentlySampleApp', events: [ { timestamp: new Date(), type: 'aws.evidently.custom', data: JSON.parse(pageLoadTimeData) }, ], }; client.putProjectEvents(putProjectEventsRequest).promise(); }

다음은 App.js 파일을 다운로드 한 후 수행한 모든 편집 후의 파일 형식입니다.

import React, { useEffect, useState } from "react"; import { BrowserRouter as Router, Switch } from "react-router-dom"; import AuthProvider from "contexts/auth"; import CommonProvider from "contexts/common"; import ProductsProvider from "contexts/products"; import CartProvider from "contexts/cart"; import CheckoutProvider from "contexts/checkout"; import RouteWrapper from "layouts/RouteWrapper"; import AuthLayout from "layouts/AuthLayout"; import CommonLayout from "layouts/CommonLayout"; import AuthPage from "pages/auth"; import HomePage from "pages/home"; import CheckoutPage from "pages/checkout"; import "assets/scss/style.scss"; import { Spinner } from 'react-bootstrap'; import Evidently from 'aws-sdk/clients/evidently'; import config from './config'; const defaultClientBuilder = ( endpoint, region, ) => { const credentials = { accessKeyId: config.credential.accessKeyId, secretAccessKey: config.credential.secretAccessKey } return new Evidently({ endpoint, region, credentials, }); }; const App = () => { const [isLoading, setIsLoading] = useState(true); const [startTime, setStartTime] = useState(new Date()); const [showDiscount, setShowDiscount] = useState(false); let client = null; let id = null; useEffect(() => { id = new Date().getTime().toString(); setStartTime(new Date()); if (client == null) { client = defaultClientBuilder( config.evidently.ENDPOINT, config.evidently.REGION, ); } const evaluateFeatureRequest = { entityId: id, // Input Your feature name feature: 'showDiscount', // Input Your project name' project: 'EvidentlySampleApp', }; // Launch client.evaluateFeature(evaluateFeatureRequest).promise().then(res => { if(res.value?.boolValue !== undefined) { setShowDiscount(res.value.boolValue); } }); // Experiment client.evaluateFeature(evaluateFeatureRequest).promise().then(res => { if(res.value?.boolValue !== undefined) { setShowDiscount(res.value.boolValue); } getPageLoadTime() }) setIsLoading(false); },[]); const getPageLoadTime = () => { const timeSpent = (new Date().getTime() - startTime.getTime()) * 1.000001; const pageLoadTimeData = `{ "details": { "pageLoadTime": ${timeSpent} }, "UserDetails": { "userId": "${id}", "sessionId": "${id}"} }`; const putProjectEventsRequest = { project: 'EvidentlySampleApp', events: [ { timestamp: new Date(), type: 'aws.evidently.custom', data: JSON.parse(pageLoadTimeData) }, ], }; client.putProjectEvents(putProjectEventsRequest).promise(); } return ( !isLoading? ( <AuthProvider> <CommonProvider> <ProductsProvider> <CartProvider> <CheckoutProvider> <Router> <Switch> <RouteWrapper path="/" exact component={() => <HomePage showDiscount={showDiscount}/>} layout={CommonLayout} /> <RouteWrapper path="/checkout" component={CheckoutPage} layout={CommonLayout} /> <RouteWrapper path="/auth" component={AuthPage} layout={AuthLayout} /> </Switch> </Router> </CheckoutProvider> </CartProvider> </ProductsProvider> </CommonProvider> </AuthProvider> ) : ( <Spinner animation="border" /> ) ); }; export default App;

사용자가 샘플 앱을 방문할 때마다 분석을 위해 사용자 지정 지표가 Evidently로 전송됩니다. Evidently는 각 지표를 분명히 분석하고 Evidently 대시보드에 결과를 실시간으로 표시합니다. 다음 예제에서는 지표 페이로드를 보여줍니다.

[ {"timestamp": 1637368646.468, "type": "aws.evidently.custom", "data": "{\"details\":{\"pageLoadTime\":2058.002058},\"userDetails\":{\"userId\":\"1637368644430\",\"sessionId\":\"1637368644430\"}}" } ]

5단계: 프로젝트, 기능 및 실험 생성

다음으로 CloudWatch Evidently 콘솔에서 프로젝트, 기능 및 실험을 생성합니다.

이 튜토리얼에서 프로젝트, 기능 및 실험 만들기
  1. https://console.aws.amazon.com/cloudwatch/에서 CloudWatch 콘솔을 엽니다.

  2. 탐색 창에서 Application Signals, Evidently를 선택합니다.

  3. 프로젝트 생성(Create project)을 선택하고 필드를 입력합니다. 샘플의 프로젝트 이름에 EvidentlySampleApp을 사용하여 올바르게 작동하도록 합니다. 평가 이벤트 스토리지(Evaluation event storage)평가 이벤트 저장 안 함(Don't store Evaluation events)을 선택합니다.

    필드에 내용을 입력한 후 프로젝트 생성(Create profile)을 선택합니다.

    자세한 내용은 새 프로젝트 만들기를 참조하세요.

  4. 프로젝트를 만든 후 해당 프로젝트에 기능을 생성합니다. showDiscount 기능에 이름을 지정합니다. 이 기능에서는 Boolean 유형의 2가지 변형을 만듭니다. False 값을 가진 첫 번째 변형인 disable의 이름을 지정하고 True 값을 가진 두 번째 변형인 enable의 이름을 지정합니다.

    기능 생성에 대한 자세한 내용은 프로젝트에 기능 추가 섹션을 참조하세요.

  5. 기능 생성을 마친 후 프로젝트에서 실험을 생성합니다. pageLoadTime 실험 이름을 지정합니다.

    이 실험에서는 테스트 중인 페이지의 페이지 로드 시간을 측정하는 pageLoadTime이라는 사용자 지정 지표를 사용합니다. 실험용 사용자 지정 지표는 Amazon EventBridge를 사용해서 생성됩니다. EventBridge에 대한 자세한 내용은 “ Amazon EventBridge란 무엇인가요?”를 참조하세요.

    사용자 지정 지표를 만들려면 실험을 생성할 때 다음을 수행합니다.

    • 지표(Metrics)지표 소스(Metric source)에서 사용자 지정 지표(Custom metrics)를 선택합니다.

    • 지표 이름(Metric name)에서 pageLoadTime을 입력합니다.

    • 목표(Goal)에서 감소(Decrease)를 선택합니다. 이는 기능의 최적 변형을 나타내기 위해 이 지표 값이 낮아야 한다는 것을 나타냅니다.

    • 지표 규칙(Metric rule)에서 다음을 입력합니다.

      • Entity ID(개체 ID)UserDetails.userId를 입력합니다.

      • 값 키(Value key)에서 details.pageLoadTime을 입력합니다.

      • 단위(Units)에서 ms를 입력합니다.

    • 지표 추가(Add metric)를 입력합니다.

    대상(Audiences)에서 모든 사용자가 실험에 입력되도록 하려면 100%를 선택합니다. 변형 간 트래픽 분할을 각각 50%로 설정합니다.

    그런 다음, 실험을 생성하려면 실험 생성(Create Experiment)을 선택합니다. 실험을 생성한 후, Evidently에 시작하도록 알리기 전까지는 시작하지 않습니다.

6단계: 실험 시작 및 CloudWatch Evidently 테스트

마지막 단계에서는 실험을 시작하고 샘플 앱을 시작합니다.

자습서 실험을 시작하려면
  1. https://console.aws.amazon.com/cloudwatch/에서 CloudWatch 콘솔을 엽니다.

  2. 탐색 창에서 Application Signals, Evidently를 선택합니다.

  3. EvidentlySampleApp 프로젝트를 선택합니다.

  4. 실험(Experiments) 탭을 선택합니다.

  5. pageLoadTime 옆에 있는 버튼을 선택하고 작업(Actions)에서 실험 시작(Start experiment)을 선택합니다.

  6. 실험을 종료할 시간을 선택합니다.

  7. 실험 시작(Start experiment)을 선택합니다.

    실험이 즉시 시작됩니다.

그리고 다음 명령을 사용하여 Evidently 샘플 앱을 시작합니다.

npm install -f && npm start

앱이 시작되면 테스트 중인 두 가지 기능 변형 중 하나에 할당됩니다. 한 변형에는 "20% 할인”이 표시되고 다른 변형에는 표시되지 않습니다. 다양한 변형을 보려면 페이지를 계속 새로 고칩니다.

참고

Evidently에는 고정 평가가 있습니다. 기능 평가는 결정적입니다. 즉 동일한 entityId 및 기능을 사용하면 사용자가 항상 동일한 변형 할당을 받게 됩니다. 변형 할당이 변경되는 유일한 시간은 엔터티가 재정의에 추가되거나 실험 트래픽이 전화 접속 연결이 될 때입니다.

그러나 샘플 앱 자습서를 쉽게 사용할 수 있도록 페이지를 새로 고칠 때마다 Evidently가 샘플 앱 기능 평가를 다시 할당하므로 재정의를 추가하지 않고도 두 변형을 모두 경험할 수 있습니다.

문제 해결

npm 버전 6.14.14를 사용하는 것이 좋습니다. 샘플 앱을 구축하거나 시작하는 데 오류가 있고 다른 버전의 npm을 사용하는 경우 다음을 수행합니다.

npm 버전 6.14.14를 설치하려면
  1. 브라우저를 사용하여 https://nodejs.org/download/release/v14.17.5/에 연결합니다.

  2. node-v14.17.5.pkg를 다운로드하고 이 pkg를 실행하여 npm을 설치합니다.

    webpack not found 오류가 나타나는 경우 evidently-sample-shopping-app 폴더로 이동하여 다음을 시도합니다.

    1. package-lock.json 삭제

    2. yarn-lock.json 삭제

    3. node_modules 삭제

    4. package.json에서 webpack 종속성 삭제

    5. 다음을 실행합니다.

      npm install -f && npm