A/A 테스트, 똑같은 화면을 둘로 나눠 겨루게 하는 이유

들어가며

새 실험 결과가 도착했다. 그런데 이번엔 좀 이상하다. A안과 B안을 비교했는데, 두 안이 사실은 완전히 똑같은 화면이다. 글자 하나 다르지 않다. 그런데도 전환율이 다르게 나왔다. A안 12.4%, B안 11.9%. 심지어 검정을 돌리니 p = 0.04, 유의하다고 찍힌다.

디자이너가 묻는다: “같은 화면인데 왜 이겨요? 뭐가 이긴 거예요?”

이 장면이 이 글의 주제다. 같은 화면 둘을 나란히 놓고 겨루게 하는 실험을 A/A 테스트(A/A test)라고 부른다. 이름이 이상하게 들린다. A/B 테스트는 서로 다른 두 안을 겨루니까 말이 되는데, 똑같은 A안 둘을 왜 겨루게 하나. 그런데 이 A/A 테스트가 실험 시스템 전체가 제대로 돌아가는지 점검하는 위생 점검(sanity check) 도구다. 위 장면에서 왜 차이가 났는지, 그게 정상인지 고장인지 구분하는 게 이 글이 하려는 일이다.

A/A 테스트란

A/B 테스트를 먼저 떠올리자. 지금 화면(A)과 바꾼 화면(B)을 사용자에게 반씩 보여주고, 전환율이 더 높은 쪽을 고른다. 두 화면이 다르니까 차이가 나면 그 차이가 변경 때문이라고 해석한다.

A/A 테스트는 여기서 B를 A로 바꾼다. 똑같은 화면 둘을 사용자에게 반씩 보여준다. 한쪽 그룹도 A안, 다른 쪽 그룹도 같은 A안. 처치(treatment)가 없다. 바꾼 게 아무것도 없으니, 두 그룹의 전환율은 원리상 같아야 한다. 참 효과(true effect)가 0인 실험을 일부러 돌리는 것이다.

여기서 자연스러운 질문. 결과가 뻔한데 왜 돌리나. 답은 이렇다. 결과가 뻔하기 때문에 돌린다. A/A는 정답을 이미 아는 시험지다. 두 그룹의 지표는 같아야 한다는 정답을 손에 쥐고 실험을 돌린다. 만약 결과가 정답에서 크게 벗어나면, 틀린 건 세상이 아니라 채점하는 기계, 즉 실험 시스템이다. 지표를 재는 계측 코드, 사용자를 두 그룹에 나누는 배정 로직, 마지막에 유의성을 판정하는 검정. 이 파이프라인 어딘가가 고장 났다는 신호다.

쉽게 말해, A/B 테스트가 두 디자인을 재는 저울이라면, A/A 테스트는 그 저울에 같은 추 두 개를 올려보는 일이다. 같은 추인데 눈금이 다르게 나오면, 잰 대상이 아니라 저울을 의심한다.

차이가 나는 두 종류

이제 들어가며의 장면으로 돌아가자. 같은 화면인데 전환율이 12.4% 대 11.9%로 갈렸다. 이 차이는 두 종류 중 하나다. 이 둘을 구분하는 게 A/A 테스트를 읽는 핵심이다.

첫째는 우연이다. 두 그룹에 배정된 사용자가 애초에 완전히 같은 사람들이 아니다. A그룹에 어쩌다 구매 의욕이 조금 높은 사람이 몇 명 더 들어가면, 화면이 같아도 전환율이 조금 높게 나온다. 동전을 100번씩 두 묶음 던지면 앞면 수가 딱 같지 않고 52 대 48처럼 갈리는 것과 같다. 동전이 고장 난 게 아니라, 표본이 유한해서 생기는 자연스러운 흔들림이다. 이걸 표본 편차라고 부른다. A/A에서 두 지표가 소수점 아래로 조금 다른 건 정상이다.

둘째는 시스템 고장이다. 지표를 재는 코드가 A그룹의 전환만 두 번 세고 있거나, 사용자를 나누는 로직이 특정 사용자를 한쪽으로 몰거나, 로깅이 한쪽 이벤트를 빠뜨리는 경우. 이건 표본 흔들림이 아니라 구조적인 편향이다. 저울의 한쪽 접시에 이물질이 낀 상태다. 아무리 표본을 늘려도 차이가 사라지지 않고, 오히려 표본이 커질수록 그 편향이 또렷하게 드러난다.

두 종류를 가르는 기준은 이렇다. 우연은 한 번 나타났다가 다음 실험에서 방향이 바뀐다. 이번엔 A가 높고 다음엔 B가 높다. 반면 시스템 고장은 같은 방향으로 반복되거나, 표본을 키워도 안 줄어들거나, 애초에 두 그룹 인원 자체가 안 맞는다. 한 번의 작은 차이에 놀랄 필요는 없다. 반복되고 구조적인 차이가 문제다.

우연: A/A에서도 5%는 유의하게 나온다

여기서 가장 중요한 지점이다. 들어가며에서 같은 화면인데 p = 0.04, 유의하게 나왔다. 이걸 두고 “유의가 떴으니 시스템이 고장 났다"고 결론 내리면 과장이다. 오히려 이건 통계가 원래 그렇게 작동한다는 증거에 가깝다.

유의수준(significance level) 0.05로 검정한다는 건, 귀무가설이 참일 때도 100번 중 5번은 유의하다고 잘못 판정하겠다고 미리 정해두는 것이다. A/A 테스트는 귀무가설(두 그룹에 차이 없음)이 확실히 참인 상황이다. 처치가 없으니까. 그런데도 검정을 하면 약 5%는 우연히 p < 0.05로 나온다. 이건 오류라기보다 1종 오류(type I error), 즉 거짓양성(false positive)의 정의 그 자체다. A/A 테스트는 이 거짓양성을 눈앞에서 몸으로 보여주는 장치다.

숫자로 느껴보자. 귀무가설이 참인 검정을 여러 번 반복하면, 각각 5% 확률로 거짓양성이 뜬다. 20번 반복했을 때 적어도 한 번 유의가 뜰 확률은 이렇게 계산한다.

# 한 번도 거짓양성이 안 뜰 확률의 여집합
1 - 0.95^20
[1] 0.6415141

64%다. 참 효과가 0인 실험을 스무 번 돌리면, 그중 적어도 하나가 유의하게 나올 확률이 3분의 2에 가깝다. 그러니 A/A를 여러 번 돌리다 한 번 유의가 뜬 건 놀랄 일이 아니라 예상된 일이다. 이 감각이 왜 중요하냐면, 실제 A/B 테스트에서 지표 열 개를 훑다가 유의하게 나온 하나를 붙잡고 흥분하는 실수를 막아주기 때문이다. A/A에서 우연히 뜬 유의를 직접 보고 나면, “유의 = 진짜 효과"라는 등식이 몸에서 풀린다.

정리하면, A/A에서 유의가 한 번 뜨는 것 자체는 정상이다. 문제가 되는 건 그다음이다. 같은 A/A를 반복했을 때 유의가 5%보다 훨씬 자주 뜨거나, 늘 같은 방향으로 뜬다면, 그건 우연이 아니라 앞 절의 시스템 고장을 의심할 때다.

시스템 고장: 배정 비율과 계측 점검

시스템 고장 중에 A/A로 가장 잘 잡히는 게 배정 편향이다. 사용자를 두 그룹에 50 대 50으로 나누기로 했는데 실제 인원이 안 맞는 현상을 표본 비율 불일치(SRM, sample ratio mismatch)라고 부른다. 예를 들어 10만 명을 반씩 나눴어야 하는데 A그룹 50600명, B그룹 49400명이 됐다면, 나누는 로직 어딘가가 한쪽으로 사용자를 흘리고 있다는 신호다. 봇 트래픽이 한 그룹에 몰렸거나, 리다이렉트가 특정 조건에서 배정을 덮어썼거나, 캐시가 한쪽을 더 태웠거나.

이걸 눈대중으로 판단하지 말고 검정으로 확인한다. 50 대 50이라는 기대 비율에 실제 배정 인원이 얼마나 어긋났는지는 카이제곱 검정(chi-squared test)으로 잰다. R에서 이렇게 돌린다.

# 50:50으로 나눴다고 가정한 A/A 테스트의 실제 배정 인원
assigned <- c(50600, 49400)          # A그룹, B그룹
chisq.test(assigned, p = c(0.5, 0.5))
        Chi-squared test for given probabilities

data:  assigned
X-squared = 14.4, df = 1, p-value = 0.0001481

p-value가 0.00015다. 50 대 50이 맞다면 이 정도로 치우친 배정은 거의 안 나온다는 뜻이다. 배정 로직을 뜯어봐야 한다. 반대로 A그룹 50110명, B그룹 49890명 정도였다면 같은 검정에서 p = 0.49가 나온다. 이 정도 어긋남은 앞 절의 표본 우연으로 충분히 설명된다. 즉 배정은 정상이다. 같은 카이제곱 검정 하나가, 손봐야 할 고장과 그냥 넘어갈 흔들림을 갈라준다.

배정 비율 말고도 A/A가 잡아내는 고장이 더 있다. 계측이나 로깅 버그가 대표적이다. 한 그룹의 전환 이벤트가 중복으로 집계되거나, 특정 브라우저에서 로그가 누락되면 A/A에서 두 지표가 구조적으로 벌어진다. 분산 과소추정(variance underestimation)도 있다. 사용자별 데이터가 서로 독립이 아닌데 독립인 척 분산을 계산하면, 신뢰구간이 실제보다 좁게 나오고 거짓양성이 5%보다 훨씬 자주 뜬다. 이 경우 A/A를 여러 번 돌리면 유의가 뜨는 비율이 5%를 크게 넘어서, 검정 자체가 헐겁다는 걸 알려준다.

언제 A/A를 돌리나

A/A 테스트를 매번 돌릴 필요는 없다. 손이 가고 트래픽도 쓰니까. 대신 값어치가 분명한 시점이 있다.

첫째, 실험 플랫폼을 새로 깔거나 크게 고쳤을 때다. 배정 로직, 지표 집계 코드, 검정 파이프라인을 바꿨으면, 진짜 A/B를 돌리기 전에 A/A로 저울부터 검증한다. 정답을 아는 시험지를 먼저 풀어보는 것이다.

둘째, 어떤 A/B 결과가 너무 좋아서 믿기 어려울 때다. 전환율이 갑자기 두 배로 뛰었다면, 진짜 대박일 수도 있지만 계측 버그일 수도 있다. 같은 조건에서 A/A를 돌려 시스템이 멀쩡한지 먼저 확인하면, 그 대박이 진짜인지 저울 고장인지 갈린다.

셋째, A/B와 A/A를 같이 돌리는 방법도 있다. 트래픽 일부를 A/A에 상시로 떼어두면, 실험 시스템이 지금도 정상인지 계속 감시할 수 있다. 배경에서 돌아가는 위생 점검인 셈이다.

다만 한 가지. A/A를 한 번 돌려 유의가 떴다고 곧장 시스템을 뜯지는 말자. 앞에서 봤듯 한 번의 5%는 정상이다. 판단하려면 여러 번 돌려서 유의가 뜨는 비율이 5% 근처인지 보거나, 배정 인원 자체가 안 맞는지 카이제곱으로 확인하는 식으로, 반복적이고 구조적인 신호를 찾아야 한다.

마무리

들어가며의 질문으로 돌아가자. “같은 화면인데 왜 이겨요?” 이 글의 답은 이렇다. 이긴 게 아니다. 참 효과가 0인 실험에서도 표본은 늘 조금씩 흔들리고, 유의수준 0.05로 검정하면 그중 5%는 우연히 유의하게 찍힌다. 한 번의 그 유의는 시스템 고장이 아니라 통계가 원래 그렇게 작동한다는 증거다.

진짜 고장은 다른 얼굴을 하고 온다. 배정 인원이 50 대 50에서 반복적으로 벗어나거나, 유의가 늘 같은 방향으로 뜨거나, 표본을 키워도 차이가 안 줄어든다. A/A 테스트는 이 둘을 갈라주는 위생 점검이다. 저울에 같은 추 두 개를 올려, 눈금이 흔들리는 정도가 정상인지 아니면 접시에 이물질이 꼈는지 확인하는 일. A/B로 두 디자인을 재기 전에, 저울부터 믿을 수 있는지 물어보는 습관. 그게 A/A 테스트가 실험 시스템에 해주는 몫이다.