기술 포스트 · ml-stats
A/B 테스트에서 흔히 빠뜨리는 5가지 — Peeking부터 SRM까지
실험을 매일 돌리는 마케팅·그로스팀이 자주 빠지는 통계적 함정 5가지를 케이스와 함께 풀어봅니다. 결과를 자신 있게 보고하기 위한 체크리스트.
A/B 테스트는 그로스의 기본 도구입니다. 하지만 “통계적으로 유의했어요”라는 한 줄 뒤에는 자주 흔들리는 함정이 다섯 개쯤 숨어 있어요. 이 글은 실험을 매일 돌리는 팀이 보고서 작성 직전에 한 번 훑어볼 체크리스트입니다.
1. Peeking — 매일 p-value 보고 멈추기
가장 흔하고 가장 위험한 함정입니다.
실험을 시작하고, 둘째 날 p-value를 봅니다. 0.08. 셋째 날 0.04. “오, 유의해졌네! 끝!” 하고 실험을 종료합니다. 문제는, 이렇게 매일 보면서 멈출 수 있다고 가정하면 거짓 양성률이 5%가 아니라 30% 이상으로 뛴다는 점이에요.
왜 그런가
p-value 0.05의 의미는 “한 번의 검정에서 우연히 이런 결과가 나올 확률 5%“입니다. 매일 보면 검정을 매일 하는 것과 같아서, 5%가 누적 확률처럼 작용해요. 30일 동안 매일 p-value를 보면, 우연히 한 번이라도 0.05를 깰 확률이 사실상 보장됩니다.
어떻게 피하나
- 사전 정의된 표본 크기까지 보지 않습니다. 정 끄고 싶으면 시작 전에 종료 조건을 명시.
- 굳이 중간에 보고 싶다면 Sequential testing 방법론(예: GroupSequential, Always Valid Inference) 사용.
2. SRM — Sample Ratio Mismatch
50:50으로 분기했다고 했는데, 실제 트래픽이 49.2 : 50.8입니다. “오차겠지” 하고 넘기면 안 됩니다.
SRM은 분기 자체에 문제가 있다는 신호일 수 있어요:
- 봇 트래픽이 한쪽에 쏠림
- 리다이렉트 로직 버그로 한쪽 노출이 더 많이 됨
- 실험 그룹 정의가 디바이스마다 다름
카이제곱 한 줄로 끝
기대 분포 대비 관측 분포가 우연이라고 보기 힘든지 측정합니다.
- — 그룹 의 실제 관측 노출 수
- — 그룹 의 기대 노출 수 (50:50이면 전체의 절반)
- — 그룹 개수
이 값이 크면 그만큼 분기가 우연 범위 밖이라는 뜻이에요.
from scipy.stats import chisquare
observed = [49_200, 50_800] # 실제 노출 수expected = [50_000, 50_000] # 기대값chi2, p = chisquare(observed, expected)print(f"SRM p-value: {p:.4f}")# 0.0006 → SRM 의심, 분기 시스템 점검p-value가 0.001 미만이면 SRM을 의심하고, 실험 결과 분석을 멈추고 분기 시스템부터 봐야 합니다. SRM이 있는 실험에서 나온 결과는 통계적으로 무효예요.
3. Novelty Effect — 첫 주만 좋아 보임
UI를 바꾸거나 새 추천 로직을 깔면, 첫 주 사용자들은 일단 새로운 것에 반응합니다. 클릭률이 튑니다. 2주 차에 정착되면 효과가 사라지고 가끔은 마이너스로 돌아서요.
신호
- 효과가 첫 1~3일에 가장 강하고 점차 줄어듦
- 신규 사용자에게서만 효과가 나타남
- 같은 사용자의 두 번째 접속에서는 효과가 줄어듦
어떻게 다루나
- 실험 기간을 2~4주 이상으로 잡아 정착 효과를 봅니다.
- 분석 시 첫 주 데이터를 별도로 봅니다 (전체 평균에 가려지면 함정).
- 가능하면 같은 사용자의 2회차 이상 행동만으로 한 번 더 분석합니다.
4. Multiple Testing — 메트릭 10개 보면 1개는 우연히 유의
A/B 테스트 보고서에 메트릭이 10개 들어 있다고 합시다. CTR, CVR, ARPU, 체류시간, 재방문율, 등등. 각각의 p-value가 0.05라면, 10개 중 1개는 우연히 통과할 가능성이 매우 높아요.
수식으로 보면 직관이 더 분명해집니다. 각 검정이 독립이라 가정하면, 메트릭 개 중 적어도 하나가 우연히 유의로 잡힐 확률은:
, 이면 . 40% 확률로 아무 효과 없는데도 “유의한 메트릭이 하나는 있다”가 나오는 셈이에요. 이걸 무시하고 “ARPU가 유의하게 올랐어요”라고 보고하면, 사실은 우연일 수 있습니다.
보정 방법 — 두 가지 철학
Bonferroni — 보수적 (false positive 거부)
p-value 임계값을 으로 낮춥니다. 10개 메트릭이면 0.005 미만만 유의로 간주. 단순하지만 검정력(true positive 잡을 능력)이 크게 떨어져요.
FDR (Benjamini–Hochberg) — 균형형
“발견 중 가짜 발견의 비율”을 통제합니다. p-value를 오름차순 정렬한 후, 다음 조건을 만족하는 가장 큰 까지 유의로 인정.
Bonferroni보다 검정력이 훨씬 좋고, 메트릭이 많을수록 차이가 큽니다. 메트릭 10개 이상이면 보통 FDR이 답이에요.
from scipy.stats import false_discovery_controlimport numpy as np
p_values = np.array([0.001, 0.012, 0.034, 0.041, 0.048, 0.060, 0.072, 0.130, 0.220, 0.580])adjusted = false_discovery_control(p_values, method='bh')
for p, q in zip(p_values, adjusted): flag = '✓' if q < 0.05 else ' ' print(f" {flag} raw p={p:.3f} → q={q:.3f}") ✓ raw p=0.001 → q=0.010 ✓ raw p=0.012 → q=0.060 ← BH 보정 후 0.05 초과 raw p=0.034 → q=0.103 raw p=0.041 → q=0.103 raw p=0.048 → q=0.103 ...메트릭 위계 (가장 깔끔한 해결책)
실무에서 가장 안전한 답은 보정 공식이 아니라 메트릭 위계를 시작 전에 정해두는 것입니다.
- Primary metric — 1개. 의사결정의 기준. 보정 없이 검정.
- Secondary metric — 2~3개. 해석 보조. FDR 보정 후 보고.
- Diagnostic metric — 나머지 전부. 의사결정에 영향 안 줌. 점추정만 보고.
이 위계가 회의실 슬라이드에 박혀 있으면, “ARPU 우연히 유의” 같은 사고가 사실상 사라집니다.
5. Guard Rail 메트릭을 빠뜨리는 것
신규 추천 로직을 띄웠더니 CTR이 +12% 올랐어요. 박수 칠 일이지만, 같은 기간 체류시간은 -8%, 재방문율은 -5%입니다. CTR만 보고 배포했다면 단기 지표는 좋아졌지만 사용자 경험이 망가진 거예요.
이런 “건드리면 안 되는 메트릭”을 guard rail이라고 부릅니다. 실험 시작 전 반드시 정의해야 해요.
자주 쓰이는 guard rail
- 기술적: 페이지 로딩 시간(p95), 오류율, 크래시율
- 참여: 재방문율, 이탈률, 세션 길이
- 경제: 매출당 비용, ARPU, LTV (장기 메트릭)
- 정성: NPS, CS 문의 수, 부정 리뷰 비율
통계적 처리 — guard rail에는 비대칭 검정을 쓴다
primary는 양방향 검정()이지만, guard rail은 “나빠지지 않았는가”만 보면 됩니다. 단방향 검정이 더 검정력이 좋아요.
은 허용 가능한 악화의 최소값. 예를 들어 페이지 로딩이 5% 안에서는 OK, 5% 넘으면 안 됨, 식으로. 실험 시작 전 협의해서 명시합니다.
Equivalence Testing — “변함이 없다”는 걸 증명하기
guard rail은 통상 검정과 거꾸로, “차이가 없음을 증명”해야 합니다. 이걸 equivalence test 또는 TOST(Two One-Sided Tests)라고 해요.
from statsmodels.stats.weightstats import ttost_ind
# 페이지 로딩 시간 (ms 단위)control = [...]; treatment = [...]# 허용 범위: ±100ms (이 안이면 동등하다고 인정)p_low, p_high = ttost_ind(treatment, control, low=-100, upp=100)# 두 p-value 모두 0.05 미만이면 "동등하다" 결론print(f"동등성 통과: {max(p_low[0], p_high[0]) < 0.05}")equivalence test를 통과하면 “이 실험은 guard rail에 영향을 주지 않았다”를 통계적으로 주장할 수 있습니다.
의사결정 룰
”primary 메트릭이 유의하게 올라도, guard rail 중 하나라도 유의하게 나빠지면 일단 멈추고 검토.”
이 한 줄을 실험 회의 첫 슬라이드에 박아두면 많은 사고를 막을 수 있어요.
6. 체크리스트 한 페이지
실험 종료 직전, 보고서 작성 직전에 한 번 훑어보세요.
- 사전 정의된 표본 크기에 도달했나? (peeking 안 했나)
- SRM 검정 통과했나? (분기 시스템 정상)
- 첫 주 효과가 끝까지 유지됐나? (novelty effect)
- Primary 메트릭 1개로 결론 내렸나? (multiple testing)
- Guard rail 메트릭이 모두 정상 범위인가?
- 효과 크기(effect size)와 신뢰 구간을 함께 보고했나?
- 실험 그룹 정의가 디바이스/유저별로 일관됐나?
7. 실험 설계의 통계적 기초 — 한 단락 정리
p-value, MDE, 검정력이 어떻게 얽혀 있는지 한 그림으로 보면 다음과 같아요.
| 개념 | 의미 | 통상값 |
|---|---|---|
| 유의수준 | “효과 없는데 있다고 잘못 결론낼” 최대 허용 확률 | 0.05 |
| 검정력 | “효과 있는데 잡아낼” 확률 | 0.80 |
| MDE | 실험으로 잡고 싶은 최소 차이 | 사업적으로 의미 있는 값 |
| 표본 크기 | 한 그룹당 필요한 사용자 수 | 위 셋이 정해지면 자동 계산 |
이 네 변수는 묶여 있어서, 셋이 정해지면 나머지 하나는 결정됩니다. 비즈니스가 표본을 더 못 기다린다면, 을 줄이는 대신 다음 중 하나를 양보해야 해요.
- 를 0.10으로 (false positive 위험 ↑)
- 를 0.30으로 (false negative 위험 ↑)
- MDE를 키워서 더 큰 차이만 잡기 (작은 효과는 못 봄)
CUPED — 분산 줄여서 검정력 높이기
같은 표본 크기로 더 작은 효과를 잡고 싶다면 CUPED(Controlled Pre-Experiment Data)가 가장 강력합니다. 사용자의 실험 전 행동(전 주 매출, 전 분기 방문)을 covariate로 활용해 분산을 줄이는 기법이에요.
- — 실험 기간 메트릭값
- — 실험 전 같은 메트릭의 사용자별 평균
- — 회귀 계수
마이크로소프트가 발표한 결과로는 CUPED로 분산을 50% 가까이 줄일 수 있고, 이는 같은 검정력에 표본 크기 절반이라는 뜻이에요. 실험 플랫폼에 한 번 빌드해두면 모든 실험에 자동으로 적용됩니다.
8. 자주 받는 질문
Q. 표본 크기를 미리 어떻게 계산하나?
비율 비교(클릭률·전환율)에서 자주 쓰이는 근사식은 이렇습니다.
- — 한 그룹당 필요한 표본 수
- — 기준 전환율 (예: 0.05 = 5%)
- — 감지하고 싶은 최소 차이 (MDE, 예: 0.005 = 0.5%p)
- (유의수준 5%), (검정력 80%)
5%p 차이를 잡고 싶을 때와 0.5%p 차이를 잡고 싶을 때 필요한 표본은 100배 차이가 납니다(가 제곱으로 들어가기 때문). 작은 차이를 보려면 표본을 정직하게 많이 쌓아야 한다는 뜻이에요.
Evan Miller의 calculator나 statsmodels.stats.power.NormalIndPower()로 5분이면 계산되니, 시작 전 한 번만 하면 됩니다.
Q. 베이지안 A/B 테스트는 peeking이 괜찮은가?
베이지안은 “매일 봐도 괜찮다”가 아니라 “매일 봐도 거짓 양성률이 일정하다”가 정확합니다. 다만 prior 설정이 주관적이라 팀 합의가 필요해요. 의사결정 기준도 다릅니다 — “p < 0.05” 대신 “B가 A보다 좋을 확률 > 95%” 같은 표현. 베이지안 A/B는 peeking 자유, 결과 직관적, 표본 크기 사전 정의 불필요의 장점이 있어 그로스팀이 점점 더 많이 채택하는 추세예요.
Q. 실험 기간이 너무 길어 비즈니스가 못 기다린다
자주 있는 압박입니다. 위에서 본 trade-off 표에서 무엇을 양보할지 명시적으로 결정해야 해요. 자주 쓰는 답:
- MDE를 키우기 — 작은 효과는 못 보지만 큰 효과는 빨리 잡음
- CUPED 적용 — 같은 일정에 더 많은 검정력
- Sequential testing — 매일 보면서도 false positive 통제 (mSPRT, GroupSequential 등)
- 대리 메트릭(proxy metric)으로 단축 — 가입을 보지 말고 가입 의사 클릭을 봄
마지막은 비즈니스적으로 위험할 수 있으니 합의 필수.
Q. 신규 사용자가 적은 B2B에서 A/B를 어떻게 하나요?
표본이 부족하면 전통적 A/B는 거의 불가능합니다. 대안:
- Switchback test: 같은 사용자에게 A와 B를 시간대별로 번갈아 노출 (예: 1시간씩)
- Synthetic control: 비슷한 다른 사용자 집단을 control로 가상 매칭
- Diff-in-diff: 시점 전후 변화량을 비교 (지역·세그먼트 단위)
이 영역은 별도 글로 다룰 만큼 깊은 분야예요.
Q. 다중 변형(A/B/C/D)은 어떻게 보고하나요?
multiple testing 함정이 그대로 적용됩니다. 변형 4개 = 비교 6쌍이면 보정 안 하면 거짓 양성률이 ~30%. 보정 + primary metric 1개로 결론 + secondary는 FDR로 정리. 또는 Multi-armed bandit으로 전환 — 좋아 보이는 쪽에 트래픽을 자동 분배.
9. 마치며
A/B 테스트의 본질은 “다르게 보이는 것이 진짜 다른 것인가”를 묻는 일입니다. 위 5가지 함정은 모두 그 질문에 잘못된 답을 주게 만들어요.
가장 큰 변화는 체크리스트 한 페이지를 회의실 슬라이드에 박아두는 것입니다. 통계 박사가 없어도 운영 가능한 시스템을 만드는 일이에요. 그 위에 CUPED, 베이지안, sequential testing 같은 도구를 하나씩 얹어가면, 실험 운영의 정밀도가 분기마다 한 단계씩 올라갑니다.