Overview

A/B Testing

October 9, 2025
6 min read
ab-testing

A/B Testing 이란?

A/B 테스팅은 제품, 웹페이지, 광고 등에서 두 가지 이상의 버전(A와 B)을 만들어, 어떤 버전이 더 나은 성과를 보이는지 객관적으로 판단하기 위해 사용하는 통제된 실험 프레임워크. 성과는 클릭률(CTR), 전환율, 수익 등 사전에 정의된 특정 지표를 기준으로 측정됨.

실험의 핵심은 전체 사용자 그룹을 무작위로 두 그룹으로 나누는 것:

  1. A그룹 (대조군, Control): 기존의 버전, 즉 기준이 되는 버전을 접하게 됨.
  2. B그룹 (실험군, Variant): 변화를 준 새로운 버전을 접하게 됨.

실험군에 도입된 변경 사항이 통계적으로 유의미한 개선으로 이어지는지 분석하는 것이 최종 목표.

핵심 구성 요소

A/B 테스팅을 구성하는 네 가지 핵심 요소는 다음과 같음:

  • 측정 지표: CTR, 수익, 가입 수 등 정량화 가능한 결과로 정의됨.
  • 무작위화: 편향을 피하고 신뢰도 높은 결과를 보장하기 위해 사용자를 그룹에 무작위로 할당함.
  • 통제된 환경: 관찰된 차이가 특정 변화로 인한 것임을 보장하기 위해 단 하나의 변수만 변경됨.
  • 통계 분석: 관찰된 성과 차이가 통계적으로 유의미한지 판단하기 위해 가설 검정(p-value, 신뢰 구간 등)이 사용됨.

주요 목적

A/B 테스팅의 세 가지 주요 목적은 다음과 같음:

  • 최적화: 사용자 참여도, 전환율, 수익 등의 핵심 지표를 개선함.
  • 의사결정: 직관이 아닌 데이터에 기반한 정보에 입각한 결정을 내리게 됨.
  • 위험 완화: 변경 사항을 전체 사용자에게 적용하기 전, 일부 사용자에게 먼저 테스트하여 위험을 최소화함.

수행 단계

1. 목표 정의

테스트의 목표와 최적화할 지표를 명확하게 정의하는 단계.

  • 무엇을 개선하려 하는가? (예: CTR, 전환율)
  • 왜 테스트를 실행하는가? (예: 디자인 검증)
  • 성공을 판단할 지표는 무엇인가? (예: 클릭, 구매)
Example (A/B Testing 목표 정의)
  • 목표 - 웹페이지의 클릭률(CTRCTR) 증가
  • 측정 지표 - CTR=(Clicks/Impressions)×100CTR = (\text{Clicks}/ \text{Impressions}) \times 100

2. 테스트할 변수 식별

대조군(A)과 실험군(B) 간에 테스트할 단일 요소를 결정하는 단계. 일반적인 변수로 웹사이트 요소(헤드라인, 이미지 등), 광고 소재(텍스트, CTA 등), 이메일(제목, 발신자 이름 등) 등이 존재.

3. 고객 분할

고객을 두 개 이상의 그룹으로 무작위 분할하는 단계. 편향 방지를 위해 그룹은 무작위로 선택되어야 하며, 신뢰성 있는 결과를 위해 트래픽을 동등하게(예: 50%-50%) 분할해야 함.

Example (A/B Testing 고객 분할)

그룹 A는 “광고 1”, 그룹 B는 “광고 2”를 보게 됨.

4. 표본 크기 결정

통계적으로 유의미한 결과에 필요한 최소 참가자 수를 결정하는 단계.

n=(Z1α/2+Z1β)22CTRˉ(1CTRˉ)(ΔCTR)2n = \frac{(Z_{1-\alpha/2} + Z_{1-\beta})^2 \cdot 2\bar{CTR}(1-\bar{CTR})}{(\Delta CTR)^2}

단측 검정 (One-Sided Test) 의 경우 Z1α/2Z_{1-\alpha/2} 대신 Z1αZ_{1-\alpha} 를 사용

표본 크기 계산 시 고려 요소:

  • 기본 성과: 현재 지표의 성과(예: CTRbaseCTR_{base}).
  • 최소 검출 효과 (MDE): 감지하고자 하는 가장 작은 개선 정도. (예: ΔCTR=0.1=CTRBCTRbase\Delta{CTR} = 0.1 = CTR_{B} - CTR_{base})
  • 신뢰 수준: 결과에 대한 확신으로, 일반적으로 95%로 설정됨. (예: Type I Error1α=0.95\text{Type I Error} \rightarrow 1 - \alpha = 0.95)
  • 통계적 검정력: 실제 효과를 놓칠 확률을 최소화하며, 보통 80%로 설정됨. (예: Type II Error1β=0.8\text{Type II Error} \rightarrow 1 - \beta = 0.8)
Definition (Type I and Type II Error)
  • Type I Error (α\alpha): false positive, 실제로는 차이가 없는데 차이가 있다고 잘못 결론 내리는 오류.
  • Type II Error (β\beta): false negative, 실제로는 차이가 있는데 차이가 없다고 잘못 결론 내리는 오류.
Definition (단측 검정(One-Sided Test) vs. 양측 검정(Two-Sided Test))

A/B 테스팅의 가설 검정에서 ‘방향성’은 매우 중요한 개념으로, 이는 단측 검정(One-Sided Test)양측 검정(Two-Sided Test) 으로 구분됨. 가설의 방향성은 대립가설을 어떻게 설정하느냐에 따라 검정의 방향성이 결정됨.

  • 귀무가설 (H₀): “A와 B의 성과는 차이가 없다 (같다).” (예: CTRB=CTRACTR_B = CTR_{A})
  • 대립가설 (H₁): “A와 B의 성과는 차이가 있다.”

단측 검정 (One-Sided Test)

변화가 특정한 한 방향으로만 나타날 것이라고 강하게 가정하는 검정.

  • 대립가설 (H₁):
    • “B의 성과가 A보다 더 좋다.” (예: CTRB>CTRACTR_B > CTR_{A}) 또는
    • “B의 성과가 A보다 더 나쁘다.” (예: CTRB<CTRACTR_B < CTR_{A})
  • 오류 확률(α\alpha)의 적용: Type I Error를 범할 확률(α\alpha, 예: 5%) 전체가 한쪽 꼬리(tail)에만 할당됩니다. 즉, ‘더 좋아졌을’ 극단적인 경우에만 귀무가설을 기각하게 됨.

양측 검정 (Two-Sided Test)

변화가 어느 방향으로든 (긍정적이든 부정적이든) 발생할 수 있다고 가정하는, 더 보편적이고 엄격한 검정.

  • 대립가설 (H₁): “A와 B의 성과는 다르다 (같지 않다).” (예: CTRBCTRACTR_B \neq CTR_{A})
  • 핵심 아이디어: 실험군(B)이 대조군(A)보다 **‘더 나을 가능성’**과 예기치 않게 **‘더 나쁠 가능성’**을 모두 동시에 검증.
  • 오류 확률(α\alpha)의 분배:
    • Type I Error를 범할 확률(α\alpha, 예: 5%)이 통계 분포의 양쪽 꼬리(tail)에 각각 절반씩(2.5%씩) 분배.
    • 이는 B의 성과가 통계적으로 유의미하게 ‘더 좋거나’ 또는 ‘더 나쁜’ 양쪽의 극단적인 결과가 관찰될 때 귀무가설을 기각하겠다는 의미.
Explanation (Two-Sided Test 에서의 표본 크기 계산)

위의 definition 들을 바탕으로 양측 검정에서 실제 표본 크기(n)를 계산하기 위한 공식은 다음과 같음:

n=(Z1α/2+Z1β)22CTRˉ(1CTRˉ)(ΔCTR)2n = \frac{(Z_{1-\alpha/2} + Z_{1-\beta})^2 \cdot 2\bar{CTR}(1-\bar{CTR})}{(\Delta CTR)^2}

먼저 핵심 아이디어를 이해하기 위해 다음 등식을 살펴봄:

Z1α/2+Z1β=ΔCTRStandard Error (S.E.)Z_{1-\alpha/2} + Z_{1-\beta} = \frac{\Delta CTR}{\text{Standard Error (S.E.)}}

위 식은 (필요한 통계적 증거의 총량) = (우리가 탐지하려는 신호) / (데이터의 잡음) 을 의미하며 각 구성 요소는 다음과 같이 해석됨:

  • 좌변 (Z1α/2+Z1βZ_{1-\alpha/2} + Z_{1-\beta}): 실험의 신뢰도와 검정력을 만족시키기 위해 필요한 통계적 증거의 총량 으로 정의됨.

    • Z1α/2Z_{1-\alpha/2}: 신뢰 수준에 해당하는 Z-점수. α/2는 오류 확률을 양쪽으로 나눈 양측 검정을 의미함. (예: α=0.05일 때, Z0.975Z_{0.975} = 1.96)
    • Z1βZ_{1-\beta}: 통계적 검정력에 해당하는 Z-점수. (예: β=0.2일 때, Z0.80Z_{0.80} = 0.84)
  • 우변 (ΔCTRS.E.\frac{\Delta CTR}{\text{S.E.}}): 실험을 통해 얻을 수 있는 신호 대 잡음비로 정의됨.

    • ΔCTR\Delta CTR: 최소 검출 효과(MDE) 에 해당. 우리가 탐지하려는 신호(Signal)의 크기.
    • Standard Error (S.E.): 차이의 표준 오차. 데이터의 무작위 변동성, 즉 잡음(Noise)의 크기.

표준 오차(S.E.) 공식은 다음과 같이 정의되며, 이를 통해 표본 크기(n)에 대한 식을 유도할 수 있음:

S.E.=pˉ(1pˉ)n1+pˉ(1pˉ)n2\text{S.E.} = \sqrt{\frac{\bar{p}(1-\bar{p})}{n_1} + \frac{\bar{p}(1-\bar{p})}{n_2}}
  • pˉ\bar{p}: 두 그룹의 통합 비율 (CTR\overline{CTR})
  • n1,n2n_1, n_2: 각 그룹의 표본 크기

두 그룹의 표본 크기를 동일하게(n1=n2=nn_1=n_2=n) 설정하면 공식은 다음과 같이 단순화됨.

S.E.=2pˉ(1pˉ)n\text{S.E.} = \sqrt{\frac{2\bar{p}(1-\bar{p})}{n}}

5. 실험 실행

결정된 표본 크기가 충족될 때까지 실험을 실행하는 단계. A그룹은 대조군 버전에, B그룹은 실험군 버전에 노출됨.

6. 결과 분석

수집된 데이터를 분석하여 결론을 도출하는 단계.

  • 지표 계산: 두 버전에 대한 지표(CTR, 전환율 등)가 계산됨.
  • 가설 검정 수행: 귀무가설(H₀: 차이 없음)과 대립가설(H₁: 유의미한 차이 있음) 설정 후, z-test나 t-test로 통계적 유의성이 평가됨.
  • P-Value 계산: 가설 검정 결과로 p-value가 계산됨.
  • 결론 도출: p-value가 0.05 미만이면 ‘통계적으로 유의미한 차이 있음’, 0.05 이상이면 ‘유의미한 차이 없음’ 으로 판단됨.
  • 최종 조치: 승리한 버전이 최종적으로 구현됨.
Example (A/B Testing Implementation)
import numpy as np
import pandas as pd
from scipy.stats import norm
from statsmodels.stats.proportion import proportions_ztest
np.random.seed(42)
# base line performance
ctr_base = 0.05
# minimum detectable effect (MDE)
mde = 0.01
ctr_b = ctr_base + mde
# define type I and type II error rates
alpha = 0.05
beta = 0.2
# test z-scores
z_alpha_one_sided = norm.ppf(1 - alpha)
z_alpha_two_sided = norm.ppf(1 - alpha / 2)
z_beta = norm.ppf(1 - beta)
# pooled ctr and delta ctr
ctr_bar = (ctr_base + ctr_b) / 2
delta_ctr = mde
# require samples size
n_one_sided = int(((z_alpha_one_sided + z_beta) ** 2 * 2 * ctr_bar * (1 - ctr_bar)) / (delta_ctr ** 2))
n_two_sided = int(((z_alpha_two_sided + z_beta) ** 2 * 2 * ctr_bar * (1 - ctr_bar)) / (delta_ctr ** 2))
# generate data
clicks_A_one_sided = np.random.binomial(n=n_one_sided, p=ctr_base)
clicks_B_one_sided = np.random.binomial(n=n_one_sided, p=ctr_b)
clicks_A_two_sided = np.random.binomial(n=n_two_sided, p=ctr_base)
clicks_B_two_sided = np.random.binomial(n=n_two_sided, p=ctr_b)
data_one_sided = pd.DataFrame({
'Ads': ['A', 'B'],
'Impressions': [n_one_sided, n_one_sided],
'Clicks': [clicks_A_one_sided, clicks_B_one_sided]
})
data_two_sided = pd.DataFrame({
'Ads': ['A', 'B'],
'Impressions': [n_two_sided, n_two_sided],
'Clicks': [clicks_A_two_sided, clicks_B_two_sided]
})
# do hypothesis testing
z_stat_one_sided, p_value_one_sided = proportions_ztest(data_one_sided['Clicks'], data_one_sided['Impressions'], alternative='smaller')
z_stat_two_sided, p_value_two_sided = proportions_ztest(data_two_sided['Clicks'], data_two_sided['Impressions'], alternative='two-sided')
# print results
print("One-sided test results:")
print(data_one_sided)
print(f"Z-statistic: {z_stat_one_sided}, P-value: {p_value_one_sided}")
if p_value_one_sided < 0.05:
print("Reject the null hypothesis in one-sided test.")
else:
print("Fail to reject the null hypothesis in one-sided test.")
print("\n---\n")
print("Two-sided test results:")
print(data_two_sided)
print(f"Z-statistic: {z_stat_two_sided}, P-value: {p_value_two_sided}")
if p_value_two_sided < 0.05:
print("Reject the null hypothesis in two-sided test.")
else:
print("Fail to reject the null hypothesis in two-sided test.")
One-sided test results:
Ads Impressions Clicks
0 A 6426 308
1 B 6426 388
Z-statistic: -3.117994752318808, P-value: 0.0009104302281918874
Reject the null hypothesis in one-sided test.
---
Two-sided test results:
Ads Impressions Clicks
0 A 8158 377
1 B 8158 496
Z-statistic: -4.139814221510001, P-value: 3.4758719657070286e-05
Reject the null hypothesis in two-sided test.

장단점 및 대안

good:

  • 단순성 (Simplicity)
  • 실행 가능한 결과 (Actionable Results)
  • 변수 격리 (Isolated Variables)
  • 넓은 적용성 (Broad Applicability)
  • 위험 완화 (Risk Mitigation)

bad:

  • 정적 트래픽 할당: 테스트 기간 내내 트래픽을 50:50과 같이 고정적으로 분배함.
  • 자원 낭비: 성과가 낮은 버전을 상당수 사용자에게 계속 노출시켜 자원을 낭비하게 됨.

alternative: