A/B 테스트 마스터 노트 시리즈 5편. 카이제곱 검정의 원리부터 chi-square-ab-testing 라이브러리 사용, p-value와 신뢰 수준의 정확한 의미, 빈도주의 vs 베이지안 접근, MDE와 검정력, 다중 비교 문제와 Bonferroni 보정까지 — 의사결정에 필요한 통계만 골라 풀어 갑니다.
이 글은 A/B 테스트 마스터 노트 시리즈의 다섯 번째 편입니다. 통계는 복잡한 수식이 아니에요. "우리가 얼마나 확신할 수 있는가" 를 숫자로 표현하는 것입니다.
이번 편의 목표는 단순해요. 언제 테스트를 종료하고 어떤 버전을 채택할지 의사결정에 필요한 통계만 골라 손에 잡히게 만드는 것. 카이제곱·베이지안·다중 비교 — 이름은 무거워 보여도 의사결정 관점에서 보면 한 흐름으로 정리됩니다.
처음 통계가 어렵게 느껴지는 이유
이유는 두 가지예요.
첫째, p-value의 정의가 직관과 어긋납니다. "p < 0.05면 유의미"는 외워도, p-value가 정확히 뭐냐고 물으면 답이 막혀요. 그리고 95% 신뢰 수준 — 도대체 뭘 95% 확신한다는 건지 헷갈립니다.
둘째, 검정 방법이 너무 많습니다. t-검정, 카이제곱, 피셔 정확검정, z-검정 — 어떤 자리에 어떤 걸 써야 하는지가 첫 단계에서 막혀요.
해결법은 두 가지. 첫째, p-value를 "귀무가설이 참일 때 이만큼 극단적인 결과가 나올 확률"로 정확히 외우세요. "귀무가설이 참일 확률"이 절대 아닙니다. 둘째, A/B 테스트의 표준 검정 = 카이제곱 한 줄로 정리하세요. 이진 전환율 비교에는 카이제곱이 사실상 표준이에요. 다른 검정들은 특수 상황에서만.
통계적 유의성 — 우연 아닌 진짜
동전 비유로 직관 잡기
동전을 3번 던져서 앞면이 3번 나왔다고 해서 그 동전이 앞면만 나오는 동전이라고 할 수 없어요. 우연일 수 있죠. 1000번 던져서 700번 앞이 나왔다면 그제서야 편향된 동전임을 의심합니다.
사례 비교:
- 10명 테스트: 변형 7명, 대조군 3명 구매 → 신뢰 어려움
- 1000명 테스트: 변형 700명, 대조군 300명 구매 → 신뢰 가능
같은 70% vs 30% 비율이지만 표본 크기가 다르면 신뢰도도 다름
통계적 유의성(Statistical Significance) 은 "관찰된 차이가 우연이 아닐 확률"이에요. 이걸 신뢰 수준(Confidence Level)으로 표현합니다.
신뢰 수준 — 우연일 확률을 뒤집은 숫자
| 신뢰 수준 | 우연일 확률 | 해석 |
|---|---|---|
| 90% | 10% | 10번 중 1번 틀림 |
| 95% | 5% | 표준. 20번 중 1번 틀림 |
| 99% | 1% | 강한 증거. 100번 중 1번 |
100% 신뢰는 통계적으로 불가능입니다. 항상 우연 가능성은 남아 있어요.
p-value — 정확한 정의와 흔한 오해
정확한 정의
p-value = 귀무가설(H₀)이 참이라고 가정할 때, 관찰된 결과(또는 더 극단적 결과)가 나올 확률
예시. p = 0.03이면 "귀무가설이 참인데도 이만큼 극단적인 결과가 나올 확률 3%"라는 뜻입니다.
해석 룰:
- p < α → H₀ 기각 (대립가설 채택)
- p ≥ α → H₀ 기각 못함 (귀무가설 유지)
가장 흔한 오해
여기서 정말 중요한 시험 함정 — p-value는 "H₀가 참일 확률"이 아닙니다. 자주 헷갈리는데, "H₀가 참이라고 가정했을 때 이런 결과가 나올 확률" 입니다. 비슷해 보이지만 완전히 다른 개념이에요.
잘못된 해석: p = 0.03 → 귀무가설이 참일 확률 3%
올바른 해석: p = 0.03 → 귀무가설 가정 하에 이런 결과가 나올 확률 3%
이 차이를 무시하면 베이지안 통계와 빈도주의 통계의 본질적 차이가 묻혀요.
p-value와 신뢰 수준 관계
p-value = 0.05 → 신뢰 수준 95%
p-value = 0.01 → 신뢰 수준 99%
p-value = 0.10 → 신뢰 수준 90%
신뢰 수준 = 1 - p-value
chi-square-ab-testing 라이브러리는 신뢰 수준을 직접 반환합니다 (0~1 사이 값). 0.95면 95% 신뢰.
카이제곱 검정 — 이진 비교의 표준
왜 카이제곱?
A/B 테스트에서 사용 가능한 통계 검정은 여러 가지예요.
| 검정 | 사용 자리 | 장단점 |
|---|---|---|
| 카이제곱 검정 | 이진 전환율 비교 | 간단·라이브러리 풍부 / 연속형 부적합 |
| z-검정 | 대표본 비율 비교 | 정확·표준 / 표본 충분해야 |
| t-검정 | 두 그룹 평균 비교 | 소표본 가능 / 정규분포 가정 |
| 피셔의 정확검정 | 소표본 이진 비교 | 소표본 정확 / 계산 복잡 |
A/B 테스트의 핵심 질문은 "사용자가 어떤 그룹에 있는지와 구매 여부가 독립적인가?"예요. 두 범주형 변수의 독립성 검정 — 이게 카이제곱이 답하는 질문입니다.
카이제곱 통계량
$$\chi^2 = \sum \frac{(O - E)^2}{E}$$
- O: 관측값 (Observed)
- E: 기대값 (Expected)
직관 — 관찰값이 기대값에서 얼마나 멀리 떨어졌는가를 모든 셀에서 더한 값. 멀어질수록 χ²가 커지고 p-value가 낮아집니다.
손으로 풀어 보기
대조군 50명, 변형 50명. 총 구매 15명. 그룹이 구매에 영향이 없다면(귀무가설) 각 그룹 기대 구매자는 7.5명.
실제 관찰:
- 대조군: 3명 구매
- 변형: 12명 구매
대조군 χ² 기여 = (3 - 7.5)² / 7.5 = 20.25 / 7.5 ≈ 2.7 변형 χ² 기여 = (12 - 7.5)² / 7.5 = 20.25 / 7.5 ≈ 2.7
미구매자도 비슷하게 계산해 모두 합치면 약 9.0의 χ². 자유도 1의 임계값(α=0.05)이 약 3.84이니 H₀ 기각 — 두 그룹의 구매 패턴이 다르다.
라이브러리로 자동 계산
// npm install chi-square-ab-testing
const chiSquare = require('chi-square-ab-testing');
// 입력 형식: [[users1, conversions1], [users2, conversions2]]
const table = [
[50, 3], // 대조군: 50명 중 3명 구매
[50, 12], // 변형: 50명 중 12명 구매
];
const confidence = chiSquare(table);
// 반환: 0~1 사이 신뢰 수준
console.log(confidence); // 예: 0.9234
3편(시스템)에서 본 /results 엔드포인트의 마지막 단계가 이거예요.
const table = Object.keys(results).map(variation => [
results[variation].users,
results[variation][metric] // 동적 변수 — 괄호 표기법!
]);
const statSig = chiSquare(table);
res.json({ statSig, results });
여기서 시험 함정이 하나 있어요. results[variation].metric 은 'metric'이라는 글자 속성을 찾고, results[variation][metric] 은 metric 변수의 값(예: 'purchase')을 속성명으로 사용해요. 동적 변수에는 괄호 표기법 필수.
Peeking Problem — 빈도주의의 약점
발생 메커니즘
테스트 진행 중 결과를 자주 들여다보다가 통계적 유의성이 잠깐 나타났을 때 종료하면 거짓 양성이 폭증합니다.
시뮬레이션:
- 1시간 후: 신뢰 수준 92% (변형이 대조군 2배 구매율)
→ 섣불리 종료하면? 10시간 후 다시 측정 시 55%로 줄어 있을 수 있음
원인: 초기 소수 사용자가 우연히 한쪽에 쏠림
시간 지나면서 표본 커지고 실제 전환율로 수렴
올바른 종료 기준
테스트 시작 전에 종료 기준을 미리 정합니다.
const MINIMUM_CONVERSIONS_PER_VARIATION = 100;
const TARGET_CONFIDENCE_LEVEL = 0.95;
function shouldStopTest(results, statSig) {
// 조건 1: 최소 전환 수
const enoughData = Object.values(results).every(
r => r.conversions >= MINIMUM_CONVERSIONS_PER_VARIATION
);
if (!enoughData) return { stop: false, reason: '최소 전환 수 미달' };
// 조건 2: 신뢰 수준
if (statSig < TARGET_CONFIDENCE_LEVEL) {
return { stop: false, reason: '신뢰 수준 미달' };
}
return { stop: true, reason: '테스트 완료' };
}
여기서 정말 중요한 시험 함정 — 결과를 보고 종료 시점을 결정하는 게 가장 흔한 실수입니다. 미리 정한 조건이 충족되기 전까지 결과 자체를 보지 않는 게 이상적이지만, 현실적으로 어렵다면 최소 100건 전환 + 1주일 이상 + 주중/주말 모두 포함 룰을 지키세요.
샘플 크기와 MDE
MDE (Minimum Detectable Effect)
검출하고자 하는 최소 효과 크기에 따라 필요한 샘플 크기가 달라져요.
효과 크기별 필요 샘플 (3% → X%로 향상 감지):
3% → 6% (100% 상대 향상): 작은 표본으로도 감지 가능
3% → 4.5% (50%): 중간 표본 필요
3% → 3.3% (10%): 큰 표본 필요
3% → 3.1% (3.3%): 매우 큰 표본 필요
검출하려는 효과가 작을수록 — 잡음 속에서 작은 신호를 구별해야 하므로 — 더 큰 표본이 필요해요.
트래픽 수준별 권장 신뢰 수준
작은 사이트에서 95% 도달은 수개월 걸려요. 현실적 조정:
| 일 방문자 | 권장 신뢰 수준 | 비고 |
|---|---|---|
| 100명 이하 | 75~80% | 결제 핵심 X — 저위험 테스트만 |
| 1,000~10,000 | 85~90% | 일반 |
| 100,000+ | 95%+ | 표준, 다중 테스트 가능 |
여기서 시험 함정이 하나 있어요. 75% 신뢰 수준은 4번 중 1번 틀린다는 뜻이에요. 상품 배치·색상 같은 저위험 결정엔 충분하지만, 결제 흐름 변경처럼 매출 직접 타격 가능한 자리엔 너무 낮습니다.
빈도주의 vs 베이지안
빈도주의 (Frequentist)
지금까지 본 카이제곱·p-value 모두 빈도주의 접근.
특징:
- "충분한 반복 실험으로 true frequency에 수렴" 가정
- p-value로 H₀ 기각 또는 비기각
- 샘플 크기·종료 기준을 사전에 정해야
장점: 이해 쉬움, 표준, 라이브러리 풍부
단점: Peeking에 취약, 결과 이진(기각/비기각), 사전 지식 반영 X
베이지안 (Bayesian)
산업계에서 점점 많이 쓰이는 접근.
특징:
- 사전 확률(Prior)을 반영하고 데이터로 업데이트
- "변형이 대조군보다 좋을 확률 X%"로 직접 표현
- 실시간 업데이트 가능 (Peeking이 덜 문제됨)
장점: 직관적 해석 ("변형이 더 나을 확률 95%")
Peeking에 강건, 비즈니스 의사결정 직접 활용
단점: 사전 확률의 주관성, 계산 복잡, 학습 곡선 가팔
실무 선택
작은 팀이라면 빈도주의로 시작하고 ($\chi^2$ 라이브러리 한 줄로 끝), 트래픽이 커지고 동시 실험이 많아지면 베이지안 도구(Optimizely Stats Engine, ABTesty)를 도입하는 흐름이 일반적이에요.
다중 비교 문제 — FWER 폭증
개념
여러 지표를 동시에 테스트하거나 여러 변형을 동시에 비교하면 우연히 유의미한 결과가 나올 확률이 폭증합니다.
예시: α = 0.05 (5% 우연 오류)
- 1개 지표 검정: 우연 오류 5%
- 20개 지표 동시 검정:
적어도 1개가 유의미하게 나올 확률 = 1 - (0.95)²⁰ ≈ 64%
20개 중 하나는 우연히 유의미하게 나올 가능성이 매우 높아져요. 이걸 다중 비교 문제(Multiple Comparisons Problem) 또는 FWER (Family-Wise Error Rate) 라고 부릅니다.
Bonferroni 보정
가장 단순한 보정. n개의 지표를 동시 검정할 때 각 지표의 유의 수준을 α/n로 줄입니다.
예: 5개 지표, 전체 α = 0.05
→ 각 지표의 α = 0.05 / 5 = 0.01
→ 각 지표는 신뢰 수준 99% 이상이어야 유의미 판정
너무 보수적이라는 단점이 있어요. False Discovery Rate (FDR) 같은 더 정교한 방법(Benjamini-Hochberg 절차)이 게놈 분석 등에서 표준입니다.
실무 접근
다중 지표를 다룰 때 권장하는 방식 — 주요 목표 지표 1~2개를 미리 정하고, 나머지는 보조/감시 지표로 취급.
예시:
주 지표: "2개 이상 구매 비율" (A/B 결정 근거)
감시 지표: 전체 구매율, 평균 체류 시간, 모달 닫기 비율
(주 지표 결과를 검증·반박하는 용도)
핵심: 합/불 결정은 주 지표만, 감시 지표는 트레이드오프 검토용
통계적 유의성 vs 실제적 유의성
통계적으로 유의미하지만 실무 의미 없음
함정 사례:
- 표본 100만 명
- 변형 전환율: 10.01%
- 대조군 전환율: 10.00%
- p-value: < 0.001 (통계적으로 매우 유의미!)
문제: 0.01%p 차이로 비즈니스 영향 미미
교훈: 통계 유의성과 효과 크기(Effect Size) 둘 다 봐야
큰 표본은 어떤 작은 차이도 p < 0.05로 만들 수 있어요. "통계적으로 유의미한가" 와 "실무적으로 의미 있는가"는 다른 질문입니다.
효과 크기 함께 보고
A/B 테스트 결과 보고 시 다음을 모두 포함하세요.
- 통계적 유의성 (신뢰 수준)
- 효과 크기 (전환율 차이의 절댓값·상대값)
- 표본 크기
- 핵심 KPI 변화
검정 방향 — 단측 vs 양측
| 검정 | 질문 | 사용 시점 |
|---|---|---|
| 단측 (One-tailed) | "B가 A보다 좋은가?" | 더 나빠지는 것에 관심 없을 때 |
| 양측 (Two-tailed) | "A와 B가 다른가?" | 부작용도 감지하려 할 때 |
A/B 테스트는 보통 "더 좋아질 것"을 기대하므로 단측 검정이 더 자주 등장해요. 단, 부작용까지 감지해야 하는 핵심 결제 흐름 변경에는 양측 검정이 안전합니다.
흔한 실수
(1) 적은 데이터에서 성급한 결론
나쁜 사례:
- 5시간 후: 변형 20%, 대조군 10%, 신뢰 85%
- 팀장: "충분해! 바로 적용!"
2주 후 재확인:
- 변형 11%, 대조군 10%, 신뢰 52% (의미 없음)
교훈: 초기 데이터는 노이즈 큼. 충분한 시간 필요
(2) CSV 데이터 분석 시 빈 줄
// 흔한 버그: 빈 줄이 undefined를 만듦
bucketData.split('\n').forEach(row => {
// 반드시 빈 줄 체크!
if (!row) return;
const fields = row.split(',');
const variation = fields[4]; // 빈 줄이면 fields = [""], variation = undefined
});
(3) Peeking 후 결정
이미 살펴봤듯, 결과 보고 종료 시점 결정하는 게 가장 큰 함정.
시험 직전 한 번 더 — 자주 헷갈리는 함정 모음
여기까지가 5편의 핵심입니다. 시험 직전 또는 실무에서 헷갈릴 때 다시 펼쳐 볼 수 있게 압축 노트로 마무리할게요.
- 통계적 유의성 = 결과가 우연이 아닐 확률
- 신뢰 수준 = 1 - p-value
- 95% 신뢰 = 우연일 확률 5% (표준)
- 100% 신뢰는 통계적 불가능
- p-value 정확한 정의 — H₀ 가정 시 이만큼 극단적 결과가 나올 확률
- p-value ≠ H₀가 참일 확률 (가장 흔한 오해)
- p < α → H₀ 기각 / p ≥ α → 기각 못함 (참 증명 X)
- A/B 테스트 표준 검정 = 카이제곱
- χ² = Σ (O - E)² / E
chi-square-ab-testing입력 =[[users, conv], [users, conv]]- 반환값 = 0~1 신뢰 수준 (0.95 = 95%)
- 동적 변수는 괄호 표기법 (
r[metric], notr.metric) - Peeking Problem = 결과 보고 종료 시점 결정 → 거짓 양성 폭증
- 종료 기준은 시작 전에 정함 (최소 100건 + 1주 + 주중/주말)
- MDE = 검출하려는 최소 효과 크기, 작을수록 큰 표본 필요
- 트래픽별 신뢰 수준 — 작으면 75~80%, 크면 95%+
- 75% 신뢰 = 4번 중 1번 틀림 — 결제 흐름엔 너무 낮음
- 빈도주의 = p-value, Peeking 약함, 사전 지식 X
- 베이지안 = "B가 좋을 확률 X%", Peeking 강건
- 작은 팀은 빈도주의로 시작, 커지면 베이지안 도입
- 다중 비교 문제 — 20개 동시 검정 시 FWER 64%
- Bonferroni 보정 = α/n
- 주 지표 1~2개 + 감시 지표 (트레이드오프 검토용)
- 통계 유의 ≠ 실무 유의 — 효과 크기 함께 보고
- 큰 표본은 작은 차이도 p < 0.05 만들 수 있음
- 단측 검정 = "B가 A보다 좋은가" / 양측 = "다른가"
- 결제 흐름 같은 핵심은 양측 검정으로 부작용 감지
- CSV 분석 시 빈 줄 방어 (
if (!row) return)
시리즈 다른 편
같은 시리즈의 다른 글들도 같은 톤으로 묶어 정리되어 있어요. 5편 통계 위에 6편 구현 패턴이 올라갑니다.
- 1편 — A/B 테스트 입문 (대조군·전환율·유의성)
- 2편 — 테스트 설계 (가설·지표·편향 방지)
- 3편 — A/B 테스트 시스템 (Feature Flag·실험 플랫폼)
- 4편 — 실제 사례 분석 (5가지 패턴)
- 5편 — 통계 심화 (현재 글)
- 6편 — 구현 패턴 (React·Node·Pandas)
- 7편 — 베스트 프랙티스 (실험 문화·흔한 실수)
공식 문서: Bayesian A/B Testing 입문와 통계 검정 가이드 (Wikipedia)에서 더 깊이 갈 수 있어요. 더 본격적인 통계 학습은 시리즈 외 확률과 통계 마스터 노트 시리즈 1~6편을 참고하세요 — 카이제곱과 ANOVA를 더 본격적으로 다룹니다.
다음 글(6편)에서는 React 컴포넌트 패턴·Node.js 백엔드 패턴·Pandas 데이터 분석 패턴을 한 흐름으로 묶어 풀어 갑니다. 흔한 버그와 해결책도 자세히.