1 정의
A/B 테스트는 사용자를 무작위로 두 개 이상의 그룹(변형, variant)에 배정하고, 각 그룹에 서로 다른 경험(처치, treatment)을 제공한 뒤, 사전 정의한 지표(metric)의 차이를 통계적으로 검정하여 인과적 효과를 추정하는 온라인 대조 실험이다.
- 역학 용어: Randomized Controlled Trial (RCT)의 IT 버전
- 다른 이름: Online Controlled Experiment, Split Test, Bucket Test
- 핵심 원리: 무작위 배정(randomization) → 교란 변수 통제 → 인과 추론 가능
A/B 테스트는 “A가 좋은가, B가 좋은가?”라는 단순한 질문처럼 보이지만, 그 뒤에는 역학(Epidemiology)의 실험 설계, 통계학의 가설 검정, 인과추론의 수학적 프레임워크가 결합된 정교한 방법론이 있다.
2 개념 및 원리
2.1 역학적 원류: RCT에서 A/B 테스트로
A/B 테스트는 갑자기 등장한 것이 아니다. 수백 년에 걸친 실험 설계의 진화 위에 서 있다.
| 시기 | 사건 | 의의 |
|---|---|---|
| 1747 | James Lind의 괴혈병 실험 | 최초의 대조 실험 |
| 1920s | R.A. Fisher의 실험 설계 이론 | 무작위화, 분산 분석(ANOVA) 정립 |
| 1948 | Streptomycin Trial (MRC) | 최초의 현대적 RCT |
| 1990s | Amazon, Google의 온라인 실험 | A/B 테스트의 대규모 산업 적용 |
| 2000s~ | Experimentation Platform 시대 | Optimizely, Google Optimize, 자체 플랫폼 |
이 계보를 이해하면, A/B 테스트의 각 구성 요소가 왜 그렇게 설계되었는지 자연스럽게 납득된다.
2.2 인과추론 프레임워크
A/B 테스트의 수학적 기반은 Rubin Causal Model (잠재적 결과 프레임워크)이다.
각 사용자 \(i\) 에 대해 두 가지 잠재적 결과가 존재한다:
- \(Y_i(1)\): 처치(새 디자인)를 받았을 때의 결과
- \(Y_i(0)\): 대조(기존 디자인)를 받았을 때의 결과
개인 처치 효과(Individual Treatment Effect):
\[ \tau_i = Y_i(1) - Y_i(0) \]
문제: 한 사용자는 처치 또는 대조 중 하나만 경험한다. \(Y_i(1)\) 과 \(Y_i(0)\) 을 동시에 관측할 수 없다 — 이것이 인과추론의 근본 문제(Fundamental Problem of Causal Inference)이다.
개인 효과를 알 수 없으므로, 집단 수준의 평균 처치 효과(Average Treatment Effect, ATE)를 추정한다:
\[ \text{ATE} = E[Y(1) - Y(0)] = E[Y(1)] - E[Y(0)] \]
무작위 배정이 이 추정을 가능하게 한다. 처치군과 대조군이 평균적으로 동일한 특성을 가지므로:
\[ \underbrace{E[Y \mid T=1] - E[Y \mid T=0]}_{\text{관측 가능한 차이}} = \underbrace{E[Y(1)] - E[Y(0)]}_{\text{인과적 효과 (ATE)}} \]
무작위 배정이 없으면 이 등식이 성립하지 않는다 — 관측된 차이에 선택 편향(selection bias)이 섞이기 때문이다.
2.3 핵심 가정: SUTVA
- 간섭 없음(No Interference): 한 사용자의 처치 배정이 다른 사용자의 결과에 영향을 주지 않는다
- 처치의 단일 버전(No Hidden Variations): 같은 처치를 받은 사용자는 동일한 경험을 한다
위반 사례: - 소셜 네트워크에서 친구가 새 기능을 쓰면 대조군 사용자의 행동도 변한다 (간섭) - 서버 부하로 인해 처치군 내에서도 경험이 다르다 (처치 변이)
2.4 A/B 테스트의 통계적 프레임워크
A/B 테스트는 가설 검정(hypothesis testing)으로 형식화된다.
2.4.1 가설 설정
\[ H_0: \mu_T - \mu_C = 0 \quad \text{(처치 효과 없음)} \] \[ H_1: \mu_T - \mu_C \neq 0 \quad \text{(처치 효과 있음)} \]
2.4.2 네 가지 핵심 파라미터
이 네 파라미터는 서로 연결되어 있으며, 세 개를 정하면 나머지 하나가 결정된다:
| 파라미터 | 기호 | 의미 | 일반적 설정 |
|---|---|---|---|
| 유의수준 | \(\alpha\) | 효과가 없는데 “있다”고 판단할 확률 (제1종 오류) | 0.05 |
| 검정력 | \(1 - \beta\) | 효과가 있을 때 “있다”고 판단할 확률 | 0.80 |
| 최소감지효과 | MDE | 감지하고 싶은 최소한의 효과 크기 | 비즈니스 맥락에서 결정 |
| 표본 크기 | \(n\) | 각 그룹에 필요한 대상 수 | 위 세 파라미터로 계산 |
2.4.3 표본 크기 공식
두 비율의 차이를 검정하는 경우 (이항 지표):
\[ n = \frac{(Z_{1-\alpha/2} + Z_{1-\beta})^2 \cdot (\hat{p}_C(1-\hat{p}_C) + \hat{p}_T(1-\hat{p}_T))}{(\hat{p}_T - \hat{p}_C)^2} \]
두 평균의 차이를 검정하는 경우 (연속 지표):
\[ n = \frac{(Z_{1-\alpha/2} + Z_{1-\beta})^2 \cdot 2\sigma^2}{\delta^2} \]
여기서 \(\delta = \mu_T - \mu_C\) 는 MDE이다.
- 전환율 5%에서 상대적 5% 개선(5.25%)을 감지하려면 그룹당 약 120,000명이 필요하다
- MDE를 절반으로 줄이면 필요 표본 크기는 4배로 늘어난다
- 분산이 큰 지표(매출)는 분산이 작은 지표(클릭률)보다 훨씬 많은 표본이 필요하다
3 직관적 설명
직관: A/B 테스트는 “동시에 두 가지를 시도해보고 어느 쪽이 나은지 과학적으로 판단하는 것”이다.
새 랜딩 페이지가 더 좋은지 궁금하다면, 모든 사용자를 한꺼번에 바꾸지 말고 반은 기존(A), 반은 새 것(B)을 보여준 뒤 전환율을 비교한다. 동시에 비교하므로 계절성이나 외부 이벤트의 영향을 배제할 수 있고, 무작위로 나누므로 두 그룹의 특성이 비슷해져 공정한 비교가 된다.
“어제 vs 오늘”이 아니라 “같은 시간에 A vs B” — 이것이 핵심이다.
3.1 왜 전후 비교(Before-After)로는 안 되는가
전후 비교의 문제:
시간 → [기존 디자인] [새 디자인 적용]
↓ ↓
전환율 3% 전환율 3.5% → "0.5%p 효과!"
하지만...
- 계절성: 원래 이 시기에 전환율이 오른다면?
- 외부 이벤트: 경쟁사가 서비스를 중단했다면?
- 자연적 추세: 사용자 기반이 성장하고 있었다면?
A/B 테스트의 해결:
같은 시간 ─── 대조군(A): 기존 디자인 → 전환율 3.0%
─── 처치군(B): 새 디자인 → 전환율 3.5%
두 그룹이 동시에 같은 외부 조건을 경험하므로,
차이 0.5%p는 디자인 변경의 인과적 효과로 귀속할 수 있다.
4 왜 필요한가
4.1 의사결정의 세 가지 방식 비교
| 방식 | 장점 | 단점 | 예시 |
|---|---|---|---|
| HiPPO (Highest Paid Person’s Opinion) | 빠르다 | 편향, 검증 불가 | “대표님이 파란색 좋다고 하셨다” |
| 데이터 분석 (관찰) | 패턴 발견 가능 | 인과 판단 불가 (상관 ≠ 인과) | “파란색 버튼 페이지에서 전환율이 높다” |
| A/B 테스트 (실험) | 인과적 효과 추정 | 시간, 트래픽 필요 | “파란색으로 바꾸면 전환율이 0.5%p 오른다” |
4.2 A/B 테스트가 필요한 순간
- 변경의 영향을 정량화해야 할 때: “이 기능이 실제로 KPI를 개선하는가?”
- 의사결정의 리스크를 줄이고 싶을 때: “출시하면 역효과가 나지는 않는가?”
- 반복적 개선(iteration)을 추구할 때: “어느 버전이 더 나은가?”
4.3 A/B 테스트가 어려운 / 불가능한 상황
| 상황 | 이유 | 대안 |
|---|---|---|
| 트래픽이 적다 | 표본 크기 부족으로 검정력 부족 | 베이지안 방법, MAB |
| 네트워크 효과가 강하다 | SUTVA 위반 (간섭) | 클러스터 무작위화, Geo-experiment |
| 장기 효과를 봐야 한다 | 실험 기간 제한 | 장기 홀드아웃, 서로게이트 지표 |
| 윤리적 문제가 있다 | 처치가 해로울 수 있다 | 관찰 연구, 준실험 설계 |
| 가역 불가능한 변경이다 | 되돌릴 수 없다 | 단계적 롤아웃, Stepped Wedge |
5 응용 분야
| 분야 | 실험 대상 | 핵심 지표 | 구체적 예시 |
|---|---|---|---|
| 제품 | UI/UX 변경 | 전환율, DAU, 세션 시간 | 버튼 색상, 레이아웃, 온보딩 흐름 |
| 성장 | 퍼널 최적화 | 가입률, 활성화율 | 회원가입 단계 수 축소 |
| 마케팅 | 메시지/크리에이티브 | CTR, CPA, ROAS | 이메일 제목, 광고 이미지 |
| 검색/추천 | 알고리즘 변경 | CTR, 체류 시간, 만족도 | 추천 알고리즘 업데이트 |
| 가격 | 가격/요금제 | 전환율, ARPU, LTV | 구독 가격 변경 |
| 인프라 | 성능 최적화 | 로딩 시간, 에러율 | CDN 변경, 캐시 전략 |
| 임상의학 | 치료법 비교 | 생존율, 재발율, 부작용 | 신약 vs 위약 RCT |
6 예시
6.1 예시: 결제 버튼 색상 A/B 테스트
상황: 이커머스 사이트에서 결제 버튼을 초록색(A)에서 파란색(B)으로 바꾸면 전환율이 오를지 검증한다.
Step 1. 가설 설정
- \(H_0\): 버튼 색상은 전환율에 영향을 주지 않는다 (\(p_B = p_A\))
- \(H_1\): 버튼 색상이 전환율에 영향을 준다 (\(p_B \neq p_A\))
Step 2. 실험 설계
- 현재 전환율: \(p_A = 3.0\%\)
- 최소감지효과 (MDE): 상대적 10% 개선 → \(p_B = 3.3\%\)
- \(\alpha = 0.05\), Power = \(0.80\)
표본 크기 계산:
\[ n = \frac{(1.96 + 0.84)^2 \times (0.03 \times 0.97 + 0.033 \times 0.967)}{(0.033 - 0.03)^2} = \frac{7.84 \times 0.0611}{0.000009} \approx 53{,}248 \]
그룹당 약 53,000명, 총 106,000명 필요.
Step 3. 결과 분석
| 그룹 | 사용자 수 | 전환 수 | 전환율 |
|---|---|---|---|
| A (초록) | 55,000 | 1,650 | 3.00% |
| B (파란) | 55,000 | 1,848 | 3.36% |
- 차이: \(3.36\% - 3.00\% = 0.36\%p\) (상대적 12% 개선)
- 95% 신뢰구간: \([0.06\%p, 0.66\%p]\)
- p-value: \(0.019\)
Step 4. 의사결정
\(p = 0.019 < 0.05\) 이므로 \(H_0\) 를 기각한다. 파란색 버튼이 전환율을 통계적으로 유의하게 개선한다. 다만, 실무적으로 0.36%p가 비즈니스적으로 의미 있는 크기인지도 함께 판단해야 한다.
7 코드 예시
7.1 Step 1: 순수 Python 구현 (원리 이해)
import math
def sample_size_two_proportions(p_c, p_t, alpha=0.05, power=0.80):
"""
두 비율 비교를 위한 표본 크기를 순수 Python으로 계산한다.
Lachin (1981) 공식 기반.
"""
# 표준 정규 분포의 임계값 (근사)
z_alpha = {0.01: 2.576, 0.05: 1.960, 0.10: 1.645}
z_beta = {0.80: 0.842, 0.85: 1.036, 0.90: 1.282, 0.95: 1.645}
z_a = z_alpha[alpha]
z_b = z_beta[power]
numerator = (z_a + z_b) ** 2 * (p_c * (1 - p_c) + p_t * (1 - p_t))
denominator = (p_t - p_c) ** 2
n = math.ceil(numerator / denominator)
return n
def z_test_two_proportions(n_c, x_c, n_t, x_t):
"""
두 비율의 차이에 대한 z-검정을 순수 Python으로 구현한다.
"""
p_c = x_c / n_c
p_t = x_t / n_t
p_pool = (x_c + x_t) / (n_c + n_t)
se = math.sqrt(p_pool * (1 - p_pool) * (1/n_c + 1/n_t))
z_stat = (p_t - p_c) / se
# 양측 검정 p-value (표준 정규 분포 근사)
# erfc를 이용한 p-value 계산
p_value = math.erfc(abs(z_stat) / math.sqrt(2))
# 95% 신뢰구간 (차이)
se_diff = math.sqrt(p_c * (1 - p_c) / n_c + p_t * (1 - p_t) / n_t)
ci_lower = (p_t - p_c) - 1.96 * se_diff
ci_upper = (p_t - p_c) + 1.96 * se_diff
return {
"p_control": p_c,
"p_treatment": p_t,
"difference": p_t - p_c,
"relative_lift": (p_t - p_c) / p_c,
"z_statistic": z_stat,
"p_value": p_value,
"ci_95": (ci_lower, ci_upper),
"significant": p_value < 0.05
}
# --- 실험 설계 ---
print("=== 표본 크기 계산 ===")
n = sample_size_two_proportions(p_c=0.03, p_t=0.033)
print(f"그룹당 필요 표본 크기: {n:,}")
print(f"총 필요 표본 크기: {2 * n:,}")
# --- 결과 분석 ---
print("\n=== 실험 결과 분석 ===")
result = z_test_two_proportions(
n_c=55000, x_c=1650, # 대조군
n_t=55000, x_t=1848 # 처치군
)
print(f"대조군 전환율: {result['p_control']:.2%}")
print(f"처치군 전환율: {result['p_treatment']:.2%}")
print(f"절대 차이: {result['difference']:.2%}p")
print(f"상대 개선: {result['relative_lift']:.1%}")
print(f"z 통계량: {result['z_statistic']:.3f}")
print(f"p-value: {result['p_value']:.4f}")
print(f"95% 신뢰구간: [{result['ci_95'][0]:.4f}, {result['ci_95'][1]:.4f}]")
print(f"유의 여부 (α=0.05): {result['significant']}")7.2 Step 2: statsmodels 구현 (실무 활용)
import numpy as np
from statsmodels.stats.power import NormalIndPower
from statsmodels.stats.proportion import proportions_ztest, proportion_confint
# --- 표본 크기 계산 ---
# Cohen's h: 효과 크기 계산
p_c, p_t = 0.03, 0.033
cohens_h = 2 * (np.arcsin(np.sqrt(p_t)) - np.arcsin(np.sqrt(p_c)))
power_analysis = NormalIndPower()
n = power_analysis.solve_power(
effect_size=cohens_h,
alpha=0.05,
power=0.80,
alternative='two-sided'
)
print(f"그룹당 필요 표본 크기: {int(np.ceil(n)):,}")
# --- 결과 분석 ---
# 관측 데이터
successes = np.array([1848, 1650]) # [처치군, 대조군]
trials = np.array([55000, 55000])
# z-검정
z_stat, p_value = proportions_ztest(successes, trials, alternative='two-sided')
print(f"\nz 통계량: {z_stat:.3f}")
print(f"p-value: {p_value:.4f}")
# 각 그룹의 신뢰구간
for label, s, n_i in [("대조군", 1650, 55000), ("처치군", 1848, 55000)]:
ci_low, ci_high = proportion_confint(s, n_i, alpha=0.05, method='normal')
print(f"{label} 전환율 95% CI: [{ci_low:.4f}, {ci_high:.4f}]")
# 차이의 신뢰구간
p_t_obs = 1848 / 55000
p_c_obs = 1650 / 55000
se_diff = np.sqrt(p_t_obs * (1 - p_t_obs) / 55000 + p_c_obs * (1 - p_c_obs) / 55000)
diff = p_t_obs - p_c_obs
print(f"\n차이: {diff:.4f} ({diff/p_c_obs:.1%} relative)")
print(f"차이 95% CI: [{diff - 1.96*se_diff:.4f}, {diff + 1.96*se_diff:.4f}]")8 A/B 테스트의 전체 파이프라인
실무에서 A/B 테스트는 다음 단계를 따른다:
1. 가설 수립
├── 비즈니스 문제 → 측정 가능한 가설로 변환
├── 핵심 지표(Primary Metric) 선정
└── 가드레일 지표(Guardrail Metric) 선정
2. 실험 설계
├── MDE 결정 (비즈니스적으로 의미 있는 최소 효과)
├── 표본 크기 계산 (α, power, MDE, 현재 분산)
├── 실험 기간 결정 (주간 주기 고려, 최소 1-2주)
├── 무작위 배정 단위 결정 (사용자, 세션, 디바이스)
└── 트래픽 비율 결정 (50/50 또는 비대칭)
3. 실험 실행
├── A/A 테스트로 플랫폼 검증
├── 점진적 롤아웃 (1% → 10% → 50%)
├── SRM(Sample Ratio Mismatch) 모니터링
└── 가드레일 지표 모니터링
4. 결과 분석
├── SRM 체크 (배정 비율 검증)
├── 가설 검정 (z-test / t-test)
├── 신뢰구간 계산
├── 세그먼트 분석 (HTE)
└── 실무적 유의성 판단 (통계적 유의성 ≠ 실무적 유의성)
5. 의사결정
├── 출시(Ship) / 반복(Iterate) / 중단(Kill) 결정
├── 장기 효과 고려 (novelty effect, learning effect)
└── 문서화 및 지식 축적
9 A/B 테스트 방법론 체계
이 시리즈에서 다룰 방법론을 개관한다.
A/B 테스트
├── 기초 (Classical)
│ ├── 가설 검정 프레임워크 ← 제1종/2종 오류, p-value, 검정력
│ ├── 표본 크기 계산 ← MDE, 비율/평균 검정
│ ├── 무작위 배정 ← 단순, 층화, 해싱
│ ├── 분석 방법 ← z-test, t-test, 카이제곱
│ └── 실험 기간 결정 ← 주기성, peeking 문제
│
├── 고급 (Advanced)
│ ├── Sequential Testing ← Group Sequential, SPRT, Alpha Spending
│ ├── Bayesian A/B Testing ← 사전/사후 분포, 확률적 우위
│ ├── Multivariate Testing ← 요인 설계, 교호작용
│ └── A/A Testing ← 플랫폼 검증, SRM 탐지
│
├── 분산 감소 (Variance Reduction)
│ ├── 사전: 층화, 매칭, 블로킹
│ └── 사후: CUPED, CUPAC, 회귀 보정, DiD
│
├── 인과추론 확장 (Causal Inference)
│ ├── Potential Outcomes ← Rubin Causal Model
│ ├── DAG ← Pearl's Framework
│ ├── 간섭(Interference) 처리 ← 클러스터 무작위화, Switchback
│ └── 이질적 처치효과 (HTE) ← Causal Forest, Meta-learner
│
├── Multi-Armed Bandit (MAB)
│ ├── Classical: Epsilon-Greedy, UCB, Thompson Sampling
│ ├── Contextual Bandit
│ └── A/B vs MAB 비교 ← 탐색-활용 트레이드오프
│
├── 실무 도전 (Practical Challenges)
│ ├── 지표 선정 ← North Star, Proxy, Guardrail
│ ├── Novelty/Primacy Effect
│ ├── Network Effect / Interference
│ └── SRM (Sample Ratio Mismatch)
│
└── 플랫폼 (Platform)
├── 배정 서비스 (Assignment)
├── 로깅 파이프라인
├── 분석 엔진
└── 대시보드 / 자동화
9.1 학습 경로 제안
Phase 1: 기초 (2-3주)
├── 역학 기초: RCT의 원리, 편향, 교란
├── 가설 검정: α, β, power, p-value
├── 표본 크기: MDE, 비율/평균 공식
└── 기본 분석: z-test, 신뢰구간
Phase 2: 설계와 실행 (2-3주)
├── 무작위 배정: 해싱, 층화
├── 지표 선정: Primary, Guardrail, Proxy
├── 실험 기간: 주기성, 최소 기간
└── A/A 테스트: 플랫폼 검증
Phase 3: 고급 분석 (3-4주)
├── Sequential Testing: 조기 종료
├── 분산 감소: CUPED, 회귀 보정
├── HTE: 세그먼트별 효과 분석
└── 간섭 처리: 네트워크 효과
Phase 4: 확장 (2-3주)
├── Bayesian A/B Testing
├── Multi-Armed Bandit
├── 인과추론: DiD, IV, RDD
└── 플랫폼 설계
10 역학-통계-IT 용어 매핑
A/B 테스트는 multi-disciplinary 영역이다. 같은 개념이 분야마다 다른 이름으로 불린다:
| 개념 | 역학 (Epidemiology) | 통계학 (Statistics) | IT/비즈니스 |
|---|---|---|---|
| 실험 대상 | Subject/Participant | Observation/Unit | User/Visitor |
| 처치 | Treatment/Exposure | Treatment | Variant/Feature Flag |
| 대조 | Control/Placebo | Control | Baseline/Default |
| 결과 변수 | Outcome/Endpoint | Response Variable | Metric/KPI |
| 무작위 배정 | Randomization | Random Assignment | Bucketing/Hashing |
| 처치 효과 | Treatment Effect | Parameter of Interest | Lift/Uplift |
| 교란 변수 | Confounder | Lurking Variable | Confound/Bias |
| 배정 분석 | Intention-to-Treat (ITT) | - | Intent-to-Treat |
| 처치 순응 분석 | Per-Protocol | - | As-Treated |
| 외적 타당성 | External Validity | Generalizability | Generalizability |
| 효과 수정 | Effect Modification | Interaction | HTE (Heterogeneous TE) |
11 관련 주제
카테고리 내 기존 포스트
시리즈 내 후속 포스트 (예정)
- 표본 크기 계산
- 무작위 배정 방법
- 실험 분석 방법
- 실험 기간 결정과 Peeking 문제
- Sequential Testing
- Bayesian A/B Testing
- CUPED 분산 감소
- MAB 개요
다른 카테고리 연결 (Multi-disciplinary)
- 통계학 개요 — 가설 검정, 추정의 이론적 기반
- GLM 프레임워크 — 실험 분석에서의 회귀 모형
- 생존 분석 — 시간-사건 지표의 실험 분석
- 역학 연구 설계 — A/B 테스트의 역학적 원류
- Random Survival Forest — ML 기반 HTE 추정
- 실험 플랫폼 아키텍처 — 엔지니어링 관점
참고 문헌
- Kohavi, R., Tang, D. & Xu, Y. (2020). Trustworthy Online Controlled Experiments. Cambridge University Press.
- Imbens, G.W. & Rubin, D.B. (2015). Causal Inference for Statistics, Social, and Biomedical Sciences. Cambridge University Press.
- Lattimore, T. & Szepesvari, C. (2020). Bandit Algorithms. Cambridge University Press.
- Hernan, M.A. & Robins, J.M. (2020). Causal Inference: What If. Chapman & Hall/CRC.
- Deng, A. et al. (2013). “Improving the Sensitivity of Online Controlled Experiments by Utilizing Pre-Experiment Data” (CUPED). WSDM.