헬로우봇 서버 A/B 테스트 도입기 (with. Hackle)

손용기

업데이트:

배경

image

최근 헬로우봇 개발팀의 개발 프로세스가 피쳐 기반에서 A/B 테스트 기반으로 변경되었습니다. 조금 달라진 점이 있다면 프로세스 자체가 “테스트”에 초점을 맞추고 있기 때문에, 최소한의 리소스로 빠르게 테스트를 준비하고 결과를 확인하는 것이 핵심이 되었습니다 (단기간에 서비스 전체를 구석구석 건드려볼 수 있어, 주니어로서 꽤 마음에 드는 프로세스입니다) 그렇다면 A/B 테스트를 진행하기 위해서는 어떤 준비를 해야 할까요?

A/B 테스트는 기존 서비스(A)새로 적용하고 싶은 서비스(B)를 나누고 각각 사용자들의 반응을 테스트해 B가 효과가 있는지를 알아보는 방법입니다 (엄청 간단한 예시로 버튼의 배경색을 바꿔보는 것도 A/B 테스트가 될 수 있습니다)

위 정의를 살펴보면, A/B 테스트는 아래와 같은 흐름으로 진행될 것 같습니다.

image

1~2단계는 사내에서 직접 진행하는데 어려움이 없겠지만, 3~6단계를 진행하기 위해서는 사용자를 구분할 수 있도록 기반을 다져야 하고, 데이터를 수집 및 분석하는 파이프라인도 구축해야 하며, 결과를 도출하는 업무도 수행해야만 합니다. 새로 적용하고 싶은 서비스가 효과가 있을지 알아보려고 간단하고 빠르게 테스트를 해보려는 건데.. 배보다 배꼽이 더 큰 셈이죠. 그래서 헬로우봇팀에서는, 앞선 내용처럼 복잡한 단계의 작업들을 대신 수행해 줄 수 있는 Hackle 서비스를 도입하기로 결정하였습니다

유사 서비스인 Google의 FirebaseOptimize도 도입을 함께 검토하였으나 Firebase의 경우 웹 프로젝트의 A/B테스트를 지원하지 않고, Optimize에서는 기술적인 이슈가 발견되어 도입 대상에서 제외하였습니다.

Hackle이란?

image

Hackle은 쿠팡 출신의 시니어 개발자와 PO들이 공동 창업한 스타트업이자 서비스의 명칭입니다. 스타트업이 빠르고 안전하게 데이터 기반의 의사결정으로 제품을 개발해나갈 수 있도록 지원하고 있습니다 (Hackle 소개 문구를 인용하였습니다)

Hackle에는 대표적으로 아래 두 가지 기능이 있습니다.

  1. A/B 테스트

    Hackle의 대표적인 기능입니다. 위에서 설명했던 것처럼, A/B 테스트를 쉽고 빠르게 진행할 수 있도록 도와줍니다. 여러 실험(A/B 테스트)들을 생성하고 관리할 수 있으며, 실험에 참여할 사용자들의 비율을 설정할 수 있고 플랫폼, OS, 버전 등의 조건으로 보다 디테일하게 집단을 구성할 수 있습니다. 또한 이벤트(사용자 행동 데이터)를 수집하고 실험의 진행 상황을 실시간으로 확인할 수 있으며, 분석된 결과를 쉽게 확인할 수 있습니다.

  2. Feature Flag

    배포된 코드를 변경하거나 롤백하지 않아도 기능을 손쉽게 켜고 끌 수 있도록 도와주는 기능입니다. 자세한 내용은 Hackle Feature Flag를 참고해 주세요.

배경에서 언급했던 것처럼, 이 글에서는 A/B테스트 기능을 사용해 본 경험을 공유드릴 것입니다.

Getting Started

실제 헬로우봇팀에서의 적용 사례를 보기 전에, 이해를 돕기 위해 간단하게 아래 웹사이트 내의 버튼 텍스트를 바꿔보는 A/B 테스트를 진행해 보겠습니다. 현재 “버튼을 눌러주세요” 텍스트는 서버에서 받아오는 데이터입니다.

image

1. 실험 생성

먼저, Hackle 대시보드의 [A/B 테스트] 탭에서 실험을 생성합니다(Hackle에서의 실험은 A/B 테스트를 의미합니다) 기획한 대로, 아래처럼 실험을 생성해 주도록 하겠습니다. 실험을 시작하기 전까지는 “준비”상태로 표시됩니다.

image

image

2. 이벤트 생성

이벤트란 버튼 클릭, 상품 구매 등 사용자가 서비스 내에서 하는 모든 행동 데이터를 의미하며, Hackle 대시보드의 [이벤트] 탭에서 생성할 수 있습니다. 이번 테스트에서는 버튼을 클릭했을 때의 데이터가 필요하니, button_click 이라는 이름의 이벤트를 생성해 주도록 하겠습니다. 생성한 이벤트는 실험의 결과를 확인하기 위한 목표 계산에 사용됩니다.

image

image

3. 목표 설정

목표는 새로 적용하고 싶은 서비스가 효과가 있는지 판단하기 위한 기준을 의미합니다. [1]에서 생성한 실험을 클릭하여 설정할 수 있습니다. 목표는 위에서 생성한 이벤트를 기반으로 설정할 수 있습니다. 이번 테스트의 결과는 버튼의 클릭률이 기준이 되기 때문에, 목표를 버튼 클릭률 = 버튼 클릭 횟수 / 전체 노출 횟수 로 설정하겠습니다.

image

4. Hackle 연동

지금까지는 실험을 진행하기 위해 Hackle 플랫폼에서 해야 할 일들을 알아보았습니다. 여기서는 서버에서 A/B 테스트를 코드로 구현하고 목표를 측정하기 위해 Hackle 플랫폼과 연동하는 작업을 진행해 보겠습니다 (Node.js 환경에서의 연동 과정입니다)

a. 의존성 추가

아래 명령어를 통해 Hackle SDK 모듈을 설치해줍니다.

npm install --save @hackler/hackle-sdk

b. SDK 연동

Hackle 플랫폼에서 제공하는 기능을 사용하기 위해서는 SDK 연동이 필요합니다. SDK 키는 아래 화면처럼 대시보드의 [SDK 연동 정보] 탭에서 확인할 수 있으며 Server, App, Browser 중 필요한 키를 사용하면 됩니다 (운영 환경과 개발 환경의 키가 분리되어 있습니다)

image

플랫폼과 환경에 맞는 키를 찾았으면, 아래 코드처럼 아주 간단하게 연동을 할 수 있습니다.

import Hackle from "@hackler/hackle-sdk";

// HackleClient를 인스턴스화 합니다.
const hackleClient = Hackle.createInstance("SDK_KEY");

// Hackle 서버와 통신할 준비를 합니다.
hackleClient.onReady(() => {    
    http.createServer((req, res) => {       
        
    }).listen(3000)
});

실제 헬로우봇 서버에서는 위 코드를 조금 변형하고, Hackle SDK에서 제공하는 함수를 사용하여 아래처럼 클래스로 사용하고 있습니다.

class HackleClient {
  private hackleClient: HackleClientInstance;

  constructor() {
    // HackleClient를 인스턴스화 합니다.
    this.hackleClient = Hackle.createInstance("SDK_KEY");
    // Hackle 서버와 통신할 준비를 합니다.
    this.hackleClient.onReady(() => {
      winston.info("hackle is ready");
    });
  }

  // 1.배정된 그룹을 받아오거나 그룹을 배정하는 메소드입니다.
  getExperimentGroup(experimentKey: number, userId: number): string {
    return this.hackleClient.variation(
      experimentKey,
      { id: userId.toString() }
    );
  }

  // 2.이벤트를 전송하는 메소드입니다.
  sendEvent(key: string, userId: number, value?: number, properties?: Properties): void {
    this.hackleClient.track(
      { key, value, properties },
      { id: userId.toString() }
    );
  }
}

[1 ~ 2] 부분을 보시면 A/B 테스트 진행에 꼭 필요한 Hackle SDK의 주요 함수들을 확인할 수 있습니다. 각 함수들은 아래와 같은 역할을 합니다.

  • variation (key, { id: user_id })

    특정 사용자가 A/B 어느 그룹에 속해 있는지 받아오는 함수입니다. “A”, “B” 등 소속 그룹을 문자열로 반환합니다. 만약 사용자가 어떤 그룹에도 속해있지 않을 경우, 배정시킨 후에 그룹을 반환해 줍니다.

  • track ({key, value, properties}, { id: user_id })

    이벤트를 Hackle 플랫폼으로 전송하는 함수입니다. 이벤트의 발생 여부만 필요할 때는 key(이벤트명)만 입력하고, 숫자나 기타 데이터가 함께 필요한 경우에는 value와 properties를 활용할 수 있습니다.

c. 테스트 코드

Hackle SDK 연동이 완료되었으니, 아래처럼 그룹별로 다른 텍스트가 내려가도록 해주면 아주 간단하게 실험 준비를 마칠 수 있습니다.

// 버튼 텍스트를 내려주는 메소드
function getButtonText(userId: number) {
  // 배정된 그룹을 받아옵니다.
  const experimentGroup = getExperimentGroup(39, userId);

  let text;

  // 그룹에 해당하는 값을 넣어줍니다.
  if (experimentGroup === "A") {
      text = "버튼을 눌러주세요";
  }
  else if (experimentGroup === "B") {
      text = "버튼 누르면 하트 100개!";
  }
  
  return text;
}

// 버튼 클릭시 호출되는 메소드
function clickButton(userId: number) {
  // Hackle로 이벤트를 전송합니다.
  sendEvent("button_click", userId);
}

5. 실험 시작

실험을 시작하려면, 얼마만큼의 해당하는 사용자에게 실험을 적용할지 트래픽 할당 비중을 설정해 주어야 합니다. 이번 테스트에서는 100%로 설정해 주겠습니다(새로 적용하고 싶은 서비스의 효과가 좋지 않아 운영에 치명적인 영향을 줄 가능성이 있는 경우에는, 실험의 진행 상황을 살펴보며 점진적으로 높여나가는 것이 좋습니다) “시작하기” 버튼을 누르면 실험이 시작되고, “진행 중” 상태로 변경되는 것을 확인할 수 있습니다.

image

image

6. 결과

image

새로운 브라우저로 다시 접속해 보니, B그룹으로 배정되어 위처럼 변경된 버튼 텍스트가 보이는 것을 확인할 수 있었습니다. 이렇게 테스트를 할 때, 기존과 동일한 A그룹으로 배정이 될 수도 있는데, 그럴 때는 Hackle에서 아래처럼 원하는 그룹에 테스트 전용 기기(아이디)를 등록하시면 고정된 그룹으로 테스트를 진행할 수 있습니다.

image

7. 데이터 확인

image

이벤트 전송을 위해 아래 조건으로 간단한 테스트를 진행해 보았습니다.

  • 그룹 A / B 각각 20번씩 웹사이트에 접근한다.
  • 그룹 A는 5번, 그룹 B는 20번 버튼을 클릭한다.

결과를 보면, “방문횟수당 전환율”을 통해 버튼 텍스트가 노출되었을 때, 몇 번이나 클릭이 되었는지를 알 수 있고, 이를 기준으로 새로 적용하고 싶은 서비스가 기존 안 대비 어느 정도의 개선율을 보이는지 확인할 수 있습니다.

예제를 모두 마쳤으니, 다음 단계에서는 헬로우봇팀에서 Hackle A/B 테스트 기능을 실제로 어떻게 활용하고 있는지 알아보겠습니다.

활용

1. 배경

헬로우봇에는 스킬이라고 불리는 연애, 사주, 타로 등 다양한 장르의 콘텐츠들이 존재합니다. 저희는 각 스킬의 타이틀과 이미지 등을 유동적으로 바꾸어가며 사용자들의 반응을 테스트하기를 원했고, 이러한 작업을 개발자가 아닌 마케팅/콘텐츠 매니저분들이 직접 쉽게 할 수 있도록 시스템을 구축하고 싶었습니다.

여기에 Hackle A/B 테스트 기능을 사용하려고 하는데.. 위 예제처럼 새로운 실험을 할 때마다 서버를 배포하는 것은 너무 비효율적이었고 개발자의 리소스도 낭비되는 느낌이었습니다. 그래서 배포 없이 스킬에 대한 유동적인 실험이 가능하도록 프로세스를 설계하고, 쉽게 운영할 수 있는 어드민 페이지를 개발하였습니다.

image

최근에 오픈했던 손금 분석 스킬을 예시로, 어떻게 쉽게 스킬에 대한 A/B 테스트를 진행하고 있는지 알아보겠습니다 (예제에서 디테일하게 설명된 부분은 중복 작성하지 않았습니다)

2. 과정

a. 실험 생성

먼저, 실험을 생성해 줍니다.

image

image

b. 이벤트 생성

image

이번 실험의 목적은 새로 적용하고 싶은 안건들이 스킬 사용에 긍정적인 영향을 줄 수 있는지를 알아보는 것입니다. 긍정적인 영향이란, 더 많은 사용자가 스킬을 클릭하여 진입하고, 소비(사용)하는 것을 의미합니다. 따라서 스킬에 대한 진입/소비 데이터를 수집하기 위해 해당 이벤트를 생성해 주도록 하겠습니다 (손금 분석 스킬뿐만 아니라 모든 스킬의 A/B 테스트에서 진입/소비 데이터를 기본 이벤트로 사용하고 있기 때문에, 구분할 수 있도록 스킬의 DB 고유번호(12345)를 이벤트명에 포함시켰습니다)

image

c. 목표 설정

위에서 생성한 이벤트를 기반으로 스킬의 진입률과 소비율을 실험 목표로 설정해 주겠습니다.

image

d. 스킬 실험 생성

배경에서 언급했던 것처럼, 어드민 페이지에서 실험 정보를 등록해 주도록 하겠습니다. 대상 스킬을 선정하고, Hackle에서 생성한 실험의 실험키와 각 그룹에게 보여질 제목/배너이미지를 기획대로 세팅해 줍니다 (클릭 몇 번으로 쉽게 세팅할 수 있으며 자세한 화면은 보안상 제외하였습니다)

image

위처럼 실험 정보를 등록하게 되면, DB에 아래와 같은 형식으로 데이터가 저장됩니다.

image

e. 코드

이제 클라이언트에서 서버로 손금 분석 스킬(12345)에 대한 정보를 요청했을 때 사용자가 소속된 그룹에 해당하는 데이터(제목/배너이미지)가 내려갈 수 있도록 아래처럼 코드를 작성해 주겠습니다.

// 스킬 정보를 내려주는 메소드
async function getSkillInfo(userId: number, skillId: number) {
  // 1.스킬에 등록되어 있는 실험정보를 DB에서 가져옵니다.
  const experimentData = await SkillExperiment.findOne({ where: { skillId } });
  // 2.Hackle실험키와 그룹별 데이터를 추출합니다.
  const { experimentKey, groupData } = experimentData;
  // 3.배정된 그룹을 받아옵니다.
  const experimentGroup = getExperimentGroup(experimentKey, userId);
  // 4.배정된 그룹에 해당하는 데이터를 내려줍니다.
  return groupData[experimentGroup];
}

위 코드의 동작 과정을 포함한 전체적인 흐름은 아래와 같습니다.

image

다음으로, 이벤트 전송에 필요한 코드를 작성해 주겠습니다.

// 스킬 진입시 호출되는 메소드
function enterSkill(skillId: number) {
  // ...
  sendEvent(`request_enter_skill_${skillId}`, userId);
}

// 스킬 소비시 호출되는 메소드
function consumeSkill(skillId: number) {
  // ...
  sendEvent(`request_consume_skill_${skillId}`, userId);
}

[b]에서 언급했던 것처럼 모든 스킬의 A/B 테스트에서 진입/소비 데이터를 기본 이벤트로 사용하고 있고, 스킬의 고유번호를 포함시키는 것으로 컨벤션을 정했기 때문에, 스킬에 대한 실험 내에서는 enterSkill, consumeSkill 메소드만 있으면 이벤트명을 추가하기 위한 별도의 서버 배포 작업을 수행하지 않아도 됩니다.

e. 결과

image

이제 Hackle에서 실험을 시작하고 손금 분석 스킬에 접근해 보면, 위처럼 사용자가 소속된 그룹에 해당하는 데이터(제목/배너이미지)가 내려오는 것을 확인할 수 있습니다. 또한, 스킬 진입/소비 시 해당되는 이벤트를 Hackle로 전송하게 됩니다.

f. 데이터 확인

image

실제로 위 실험을 진행했을 때 수집된 데이터입니다. “사용자 전환율”을 통해 손금 스킬이 노출되었을 때, 얼마만큼의 사용자가 스킬 진입/소비를 했는지를 알 수 있고 이를 기준으로 새로 적용하고 싶은 서비스가 기존 안 대비 어느 정도의 개선율을 보이는지 확인할 수 있습니다. 위 데이터에서는 기존안과 비슷한 효과를 보이고 있네요😅

3. 마무리

image

현재 위 과정을 통해 실제로 마케팅/콘텐츠 매니저분들이 많은 A/B 테스트를 계획하여 진행하고 있습니다. 누구나 쉽게 테스트를 할 수 있다 보니, 다양한 아이디어들을 빠르게 실험해 보면서도 투여되는 리소스를 줄일 수 있는 것이 큰 장점인 것 같습니다. 효과가 좋던 나쁘던, 결과 확인이 빠른 만큼 다음에 어떤 액션을 취할지도 빠르게 정할 수 있으니까요!

마치며

처음 Hackle을 접했을 때, 사용방법이 너무 간단해서 놀랐었습니다. 그래서 수월하게 업무를 진행할 수 있었는데, 글을 쓰려고 보니.. 그 만큼 생각보다 쓸 내용이 많이 없더군요.. 역시 모든 일에는 트레이드오프가 존재하는 것 같습니다😇 제가 느낀 것들을 철학적으로 풀어써보려고 했는데, 잘되지 않은 듯하네요. 그래도 어쩌면, 글의 난도가 낮아 Hackle을 처음 접하시는 분들에게는 도움이 될 것 같기도 합니다. 긴 글 읽어주셔서 감사드리며, 다음에 더 유익한 내용으로 찾아뵙도록 하겠습니다.

띵스플로우 팀은 자기의 일을 좋아하고 잘하는 사람들 입니다. 사용자와 서비스를 중심으로 빠르게 실행하고 학습하며, 다양한 직무의 사람들이 협업을 통해 시너지를 내고 있습니다. 다양한 콘텐츠 혁신을 이루고 있는 띵스플로우 팀에 함께할 분을 찾습니다! 언제든 people@thingsflow.com로 이메일을 주시기 바랍니다!

태그:

카테고리:

업데이트:

댓글남기기