A/B 테스트 통계 — 카이제곱·베이지안·다중 비교

2026-05-03확률과 통계 마스터 노트

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], not r.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편 구현 패턴이 올라갑니다.

공식 문서: Bayesian A/B Testing 입문통계 검정 가이드 (Wikipedia)에서 더 깊이 갈 수 있어요. 더 본격적인 통계 학습은 시리즈 외 확률과 통계 마스터 노트 시리즈 1~6편을 참고하세요 — 카이제곱과 ANOVA를 더 본격적으로 다룹니다.

다음 글(6편)에서는 React 컴포넌트 패턴·Node.js 백엔드 패턴·Pandas 데이터 분석 패턴을 한 흐름으로 묶어 풀어 갑니다. 흔한 버그와 해결책도 자세히.

※ 이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.

답글 남기기

error: Content is protected !!