A/B 테스트를 매일 훔쳐보면 안 되는 이유
들어가며
새 결제 화면을 A/B 테스트로 올렸다. 실험 대시보드는 실시간이라, 아침마다 커피 들고 숫자를 확인한다. 1일차, 2일차는 무승부. 그런데 3일차 아침, 드디어 초록불이 들어온다. 새 화면 전환율이 높고, p = 0.04. 유의하다.
팀 채팅에 이런 대화가 오간다.
PM: “유의하게 나왔네요. 이만 실험 끝내고 새 화면으로 갈까요?” 디자이너: “3일밖에 안 됐는데 벌써 멈춰도 되나요?” PM: “이미 통계적으로 유의하잖아요. 더 볼 이유가 있어요?”
그럴듯하다. 유의하게 나왔으니 멈춘다. 빠르게 이기고 다음 실험으로 넘어간다. 실험을 많이 돌리는 팀일수록 이 습관이 몸에 밴다.
그런데 이 습관에는 함정이 있다. 매일 들여다보다가 유의해지는 순간 멈추는 방식은, 겉보기와 달리 거짓양성(false positive)을 대량으로 만들어낸다. p < 0.05로 잡았으니 거짓양성은 5%일 거라고 믿지만, 실제로는 20%, 30%를 넘긴다. 이 글에서는 왜 그런지, 그리고 그럼 어떻게 해야 하는지 정리한다. 이 함정에는 이름이 있다. 피킹(peeking), 또는 조기 종료(optional stopping) 문제다.
왜 훔쳐보면 안 되나
먼저 p-value가 무슨 약속 위에서 만들어진 숫자인지부터 짚자. p-value는 이렇게 정의된다. 귀무가설(null hypothesis)이 참이라고 가정할 때, 지금 관측된 데이터만큼 극단적이거나 더 극단적인 데이터가 나올 확률. A/B 테스트에서 귀무가설은 “두 화면의 전환율에 차이가 없다"다. p = 0.04는 두 화면이 사실 똑같은데도 지금 본 만큼의 격차가 우연히 나올 확률이 4%라는 뜻이다.
여기서 놓치기 쉬운 전제가 하나 있다. 이 정의는 검정을 한 번 한다고 가정한다. 데이터를 정해진 만큼 모으고, 그 데이터로 검정을 딱 한 번 돌린다. 그 한 번의 판정에서 귀무가설이 참인데도 유의가 뜰 확률이 5%다. 이 5%라는 숫자는 한 번의 검정이라는 약속을 지킬 때만 5%다.
그런데 매일 들여다보며 유의하면 멈추는 방식은 이 약속을 깬다. 3일차에 확인하고, 4일차에 확인하고, 5일차에 확인한다. 확인할 때마다 검정을 한 번씩 새로 돌리는 셈이다. 그리고 그중 한 번이라도 유의가 뜨면 멈춘다. 검정을 한 번 하는 게 아니라, 여러 번 하면서 그중 하나만 걸리면 되는 게임을 하는 거다.
쉽게 말해 이렇다. 정상 동전을 던져서 앞면이 20번 연속 나올 확률은 아주 낮다. 그런데 20번 연속을 딱 한 세트만 시도하는 게 아니라, 안 나오면 계속 다시 시도한다고 해보자. 시도를 무한히 반복하면 언젠가는 20번 연속이 나온다. 유의성도 똑같다. 귀무가설이 참이어서 진짜 차이가 없어도, 데이터가 쌓이는 동안 전환율 격차는 우연히 커졌다 작아졌다 출렁인다. 그 출렁임을 매일 지켜보다가 우연히 위로 튄 날 멈추면, 없는 차이를 있다고 선언하게 된다.
이건 다중비교(multiple comparisons) 문제와 뿌리가 같다. 검정을 여러 번 하면 그중 하나가 우연히 유의하게 나올 기회가 늘어난다. 다중비교에서는 지표 여러 개를 동시에 검정하는 반면, 피킹에서는 같은 지표를 시점을 바꿔가며 반복 검정한다. 방향만 다를 뿐, 검정 횟수가 늘면 거짓양성이 쌓인다는 원리는 똑같다.
한 가지 정확히 해두자. 문제는 들여다보는 행위 자체가 아니다. 실험이 잘 도는지 매일 눈으로 확인하는 건 괜찮다. 문제는 볼 때마다 멈출지 말지를 판정하는 것이다. 유의하면 멈추고 아니면 계속 두는 규칙으로 매일 데이터를 검정에 태우는 순간, 5%라는 약속이 깨진다. 구경은 죄가 아니고, 구경하다가 판정하는 게 죄다.
반복 검정이 거짓양성을 부풀리는 걸 눈으로
말로만 하면 감이 잘 안 온다. 숫자로 보자. 귀무가설이 참인 상황, 그러니까 두 화면이 정말로 똑같은 상황을 컴퓨터로 만들어놓고, 매일 들여다보며 유의하면 멈추는 방식이 거짓양성을 얼마나 만들어내는지 세어보자.
설정은 이렇다. 두 그룹을 완전히 같은 분포에서 뽑는다. 정의상 진짜 차이는 없다. 매 점검마다 그룹별로 20명씩 더 모아 t-검정(t-test)을 다시 돌리고, p < 0.05가 뜨는 순간 멈추고 유의하다고 선언한다. 점검 횟수를 1회, 2회, 5회로 늘려가며 이 방식이 얼마나 자주 없는 차이를 잡아내는지 본다. 진짜 차이가 없으니, 여기서 나오는 유의는 전부 거짓양성이다.
set.seed(2026)
# 귀무가설이 참인 상황: 두 화면의 전환 성향이 사실은 똑같다.
# 매 '들여다봄(look)'마다 20명씩 더 모아 t-검정을 다시 돌리고,
# p < 0.05가 뜨는 순간 멈추고 '유의하다'고 선언한다.
peek_until_significant <- function(n_looks, batch = 20) {
a <- numeric(0); b <- numeric(0)
for (look in seq_len(n_looks)) {
a <- c(a, rnorm(batch)) # 두 그룹 모두 같은 분포에서 뽑는다 (진짜 차이 없음)
b <- c(b, rnorm(batch))
if (t.test(a, b)$p.value < 0.05) return(TRUE) # 유의 -> 멈춤
}
FALSE # 끝까지 안 뜨면 차이 없음으로 마무리
}
n_sims <- 10000
for (L in c(1, 2, 5, 10, 20, 40)) {
fp <- mean(replicate(n_sims, peek_until_significant(L)))
cat(sprintf("들여다본 횟수 %2d회 : 거짓양성률 %.1f%%\n", L, 100 * fp))
}
이 코드를 돌리면 결과가 이렇게 나온다.
들여다본 횟수 1회 : 거짓양성률 4.6%
들여다본 횟수 2회 : 거짓양성률 8.5%
들여다본 횟수 5회 : 거짓양성률 14.3%
들여다본 횟수 10회 : 거짓양성률 19.9%
들여다본 횟수 20회 : 거짓양성률 25.0%
들여다본 횟수 40회 : 거짓양성률 30.8%
첫 줄을 보자. 딱 한 번만 검정하면 거짓양성률이 4.6%다. 우리가 p < 0.05로 기대한 5%와 거의 같다. 약속을 지키면 약속대로 나온다.
그런데 아래로 내려갈수록 숫자가 무섭게 오른다. 두 번만 들여다봐도 8.5%로 이미 5%를 훌쩍 넘는다. 다섯 번이면 14.3%, 열 번이면 19.9%, 스무 번이면 25.0%다. 진짜 차이가 하나도 없는데, 매일 들여다보다 유의하면 멈추는 규칙만으로 네 번에 한 번꼴로 없는 승자를 만들어낸다. 마흔 번까지 가면 30.8%, 세 번에 한 번이다. 들어가며의 3일차 초록불이 이 표의 한 줄이다.
여기서 자연스러운 의문이 든다. 한 번 검정에 5% 위험이니, 스무 번이면 단순히 5% 곱하기 스무 번 해서 100% 아니냐고. 그렇게까지는 안 된다. 서로 독립인 검정 스무 번이라면 적어도 한 번 유의할 확률이 1 빼기 0.95의 20제곱, 즉 64%까지 오른다. 그런데 실제로는 25%에 그친다. 왜냐하면 연속된 점검은 대부분의 데이터를 공유하기 때문이다. 어제 본 데이터에 20명이 더해진 게 오늘 데이터라, 두 검정 결과가 서로 강하게 연결돼 있다. 그래서 완전히 독립인 경우보다는 덜 오른다. 덜 오를 뿐, 여전히 5%에서 25%로 다섯 배가 된다는 사실은 바뀌지 않는다.
그럼 어떻게
원인이 분명하니 해법도 분명하다. 검정을 여러 번 하면서 그중 하나만 걸리면 멈추는 규칙이 문제였다. 그러니 검정 횟수를 통제하면 된다. 크게 두 갈래다.
첫째, 가장 기본은 고정 표본 설계다. 실험을 시작하기 전에 표본 크기와 기간을 미리 정하고, 그 지점에 도달할 때까지 판정하지 않는다. 딱 한 번, 정해둔 끝에서 검정한다. 위 표의 첫 줄이 이 방식이고, 거기서만 거짓양성률이 5%로 지켜진다.
여기서 필요한 게 검정력 분석(power analysis)이다. 잡아내고 싶은 최소한의 차이, 예를 들어 전환율 2%p를 원하는 확률로 잡으려면 그룹당 몇 명이 필요한지를 실험 전에 계산한다. 그렇게 나온 숫자가 목표 표본이고, 거기 도달하기 전까지는 대시보드를 봐도 멈추지 않는다. 쉽게 말해 실험의 결승선을 출발 전에 그어두고, 결승선에서만 승패를 판정하는 거다. 중간에 앞서 있다고 경기를 멈추지 않는다.
물론 반론이 나온다.
PM: “2주를 기다리라고요? 3일차에 이미 이기고 있는데 손해 아닌가요?”
맞는 걱정이다. 명백히 안 좋은 변경을 2주씩 방치하는 것도 비용이고, 좋은 변경을 빨리 못 내보내는 것도 비용이다. 그래서 정말로 중간에 판정하고 싶다면, 두 번째 갈래가 있다.
둘째, 순차 검정(sequential testing)이다. 이건 중간 점검 자체를 금지하는 게 아니라, 여러 번 볼 걸 처음부터 계획에 넣고 문턱을 조정하는 방식이다. 핵심 아이디어는 알파 소비(alpha spending)다. 전체 1종 오류 예산 5%를 여러 번의 점검에 나눠 쓴다고 생각하면 된다. 한 번의 점검에서 5%를 다 쓰지 않고, 조금씩 배분한다. 그러면 여러 번 봐도 전체 거짓양성률이 5%를 넘지 않는다.
쉽게 말하면 이렇다. 그냥 매일 p < 0.05를 문턱으로 쓰면 위 표처럼 위험이 새어 나간다. 그런데 중간 점검에서는 훨씬 엄격한 문턱, 이를테면 초반엔 p < 0.001 같은 걸 요구하고 실험 끝에 가서야 문턱을 느슨하게 풀면, 자주 들여다봐도 전체적으로 5%가 지켜진다. 이걸 정식화한 게 그룹 순차 설계(group sequential design)이고, 오브라이언-플레밍(O’Brien-Fleming)이나 포콕(Pocock) 경계 같은 이름으로 문턱을 어떻게 나눌지 정해두는 방법들이 있다. 요즘 실험 도구 중에는 언제 멈춰도 유의수준이 지켜지는 순차 방식을 아예 내장한 것들도 있다. 이런 도구를 쓰면 매일 봐도 되고, 봐도 안전하다.
베이지안(Bayesian) 접근도 대안이 될 수 있다. 사후확률이나 기대 손실을 기준으로 중단을 결정하는 방식인데, 이쪽은 애초에 p-value의 반복 검정 틀을 벗어나 있어서 피킹 문제를 다르게 다룬다. 다만 이것도 공짜는 아니다. 아무 규칙 없이 사후확률만 매일 들여다보다 멈추면 그것대로 편향이 생길 수 있어서, 중단 규칙을 미리 정해두는 원칙은 여전히 필요하다.
정리하면 선택지는 이렇다. 빨리 갈 필요가 없으면 고정 기간을 정해두고 끝에서 한 번 판정한다. 중간에 꼭 봐야겠으면 순차 검정처럼 여러 번 보는 걸 처음부터 설계에 넣은 방법을 쓴다. 하지 말아야 할 단 하나는, 아무 보정 없이 그냥 매일 p < 0.05를 들여다보다 유의하면 멈추는 것이다.
보고서에 옮길 때
피킹 문제는 통계 지식이 부족해서만 생기는 게 아니다. 보고서에 옮기는 과정에서 슬쩍 감춰지기도 한다.
첫째, 매일 봤다는 사실을 적지 않는다. “3일차에 유의하게 나와 실험을 종료했습니다"라고만 쓰면, 읽는 사람은 3일치 데이터로 한 번 검정했다고 이해한다. 사실은 매일 판정하다 3일차에 걸린 건데, 그 과정이 안 보인다. 몇 번 들여다봤고 어느 시점에 멈췄는지를 적어야, 읽는 사람이 거짓양성 위험을 감안해서 읽는다.
둘째, 유의해진 순간에 멈춘 걸 계획된 종료처럼 쓴다. 결과가 좋게 나온 시점을 골라 멈추고 그 p값만 보고하면, 우연히 위로 튄 날을 실력으로 포장하는 셈이 된다. 앞의 시뮬레이션이 보여준 게 정확히 이 함정이다. 진짜 차이가 없어도, 멈출 시점을 유리하게 고르면 30%까지 유의가 뜬다.
셋째, 고정 기간을 정해두고도 조기에 깨는 걸 예외로 남기지 않는다. 2주를 계획했는데 5일차에 유의해서 멈췄다면, 계획을 깬 것이다. 깰 수 있다. 다만 그 사실과 이유를 남겨야 한다. 순차 검정으로 미리 설계한 조기 종료와, 그냥 못 참고 멈춘 조기 종료는 완전히 다른 일이다.
마무리
들어가며의 3일차 아침으로 돌아가자. p = 0.04, 초록불. 멈춰도 될까.
이 글의 답은 이렇다. 그 초록불 하나만으로는 안 된다. 매일 판정하다 걸린 첫 유의는, 진짜 차이가 없어도 네 번에 한 번은 뜨는 신호다. 문제는 들여다본 게 아니라, 볼 때마다 멈출지 판정한 것이다. 구경은 괜찮았고, 구경하다 규칙 없이 멈추려 한 게 함정이었다.
그러니 둘 중 하나다. 실험 전에 표본과 기간을 정해두고 그 끝에서 한 번 판정하거나, 정말 중간에 봐야겠으면 순차 검정처럼 여러 번 보는 걸 설계에 넣은 방법을 쓰거나. 3일차의 초록불은 그 자체로 나쁜 게 아니다. 그게 미리 그어둔 결승선에서 나온 초록불인지, 아니면 참다 못해 훔쳐본 아침에 우연히 튄 초록불인지가 전부를 가른다.