경품 추첨과 게임에 진짜 무작위성이 중요한 이유

인스타그램 이벤트를 열고, 무료 온라인 도구로 5,000개 댓글 중 당첨자를 뽑은 뒤 이런 생각을 해 본 적 있으신가요. 이게 정말 공정했을까?
조금 무서운 답은, 대체로 아니라는 것입니다.
무료 온라인 도구의 절대다수, 그리고 그것을 만드는 데 쓰인 프로그래밍 언어의 기본 기능조차 **의사난수 생성기(PRNG)**에 의존합니다. 돈이나 경품, 민감한 데이터, 보안이 걸린 일을 다룬다면 PRNG의 치명적 결함을 이해해 둘 필요가 있습니다.
의사난수라는 착시
컴퓨터는 근본적으로 결정론적이고 규칙을 따르는 기계입니다. 입력을 받아 엄격한 논리를 따라 출력을 냅니다. 이 경직된 성질 때문에, 컴퓨터가 진짜로 즉흥적이거나 예측 불가능한 일을 하기란 대단히 어렵습니다.
그것을 흉내 내기 위해 브라우저에 내장된 Math.random() 같은 표준 PRNG는 수학 공식과 “시드”(보통 버튼을 누른 순간의 내부 시계 밀리초)를 씁니다. 이 공식이 사람 눈에는 완전히 무작위로 보이는 수열을 뱉어 냅니다.
하지만 그것은 착시입니다. 영리한 공격자가 사용된 알고리즘과 버튼을 누른 정확한 시각을 안다면, 그 “무작위” 값이 생성되기도 전에 완벽하게 예측할 수 있습니다.
게임 애니메이션이나 재생목록 셔플이라면 아무 문제 없습니다. 추첨이나 암호 키, 큰 상금이 걸린 이벤트라면 재앙입니다.
PRNG는 내부에서 어떻게 동작하는가
가장 흔한 PRNG 알고리즘은 **선형 합동 생성기(LCG)**입니다. 공식은 간단합니다.
next = (a × current + c) mod m
a, c, m은 고정 상수이고 current는 시드에서 시작합니다. 함수를 호출할 때마다 직전 결과를 다시 공식에 넣어 다음 수를 만듭니다.
여기서 결정적인 단어는 수열입니다. PRNG는 무작위 수를 만드는 것이 아니라, 고정되고 재현 가능한 수열을 만들되 그것이 무작위처럼 보일 뿐입니다. 같은 시드를 주면 언제나 똑같은 수열이 나옵니다. 테스트와 디버깅에는 유용하지만(“무작위” 결과를 재현할 수 있으니까요) 보안에는 최악입니다.
현대 자바스크립트 엔진은 Math.random()에 xoshiro128이나 xorshift128+ 같은 더 정교한 알고리즘을 써서 기본 LCG보다 나은 통계적 분포를 냅니다. 그러나 근본적인 약점은 그대로입니다. 결정론적이라는 것, 즉 내부 상태를 알면 이후 모든 출력이 예측된다는 것입니다.
표준이 되는 답: 암호학적으로 안전한 난수
이 문제를 풀기 위해 현대 브라우저는 Web Crypto API를 도입했습니다.
단순한 수식과 예측 가능한 시계에 기대는 대신, 이 API는 물리적 환경에서 “엔트로피”(무질서의 척도)를 모읍니다. CPU 온도의 미세한 요동, 마우스 움직임의 나노초 단위 타이밍, 디스크 입출력의 지터, 시스템 배경 잡음 같은 것들에서 진짜로 예측 불가능한 시드를 만들어 냅니다.
그 결과가 암호학적으로 안전한 의사난수 생성기(CSPRNG)입니다. 은행의 암호 키를 만들고 비밀번호를 보호하는 데 쓰이는 바로 그 기술입니다.
PRNG와 CSPRNG 비교
| 항목 | Math.random() (PRNG) | crypto.getRandomValues() (CSPRNG) |
|---|---|---|
| 상태를 알면 예측 가능? | 예 | 아니요 |
| 엔트로피 출처 | 내부 알고리즘 + 시계 | OS의 하드웨어 잡음, I/O 타이밍 |
| 속도 | 매우 빠름 | 약간 느림 |
| 게임·애니메이션에 적합 | 예 | 예 (과하지만 무방) |
| 추첨·경품에 적합 | 아니요 | 예 |
| 암호 키에 적합 | 아니요 | 예 |
| 보안 토큰에 적합 | 아니요 | 예 |
모듈로 편향을 조심하세요
까다로운 부분은 여기입니다. 내부적으로 안전한 난수를 쓰더라도, 개발자가 그 값을 원하는 범위로 옮기는 과정에서 잘못된 산술을 쓰면 여전히 무너집니다.
예를 들어 1에서 100 사이의 수를 뽑고 싶을 때 많은 개발자가 “모듈로”라는 나머지 연산을 씁니다. 그런데 이진 나눗셈의 성질 때문에 단순한 모듈로는 모듈로 편향을 만듭니다. 작은 수가 큰 수보다 수학적으로 조금 더 자주 뽑히는 현상입니다.
모듈로 편향이 생기는 이유
0에서 255까지(1바이트) 값을 내는 생성기가 있고, 0에서 99 사이의 수를 원해 randomByte % 100을 계산한다고 해 봅시다.
- 0~99는 그대로 대응합니다. 0→0, 1→1, … 99→99
- 100~199도 마찬가지입니다. 100→0, 101→1, … 199→99
- 그런데 200
255는 055에만 대응합니다. 200→0, 201→1, … 255→55
055는 각각 세 개의 원본 값에서 나올 수 있고, 5699는 두 개뿐입니다. 즉 055가 뽑힐 확률이 5699보다 50% 높습니다.
주사위에 납을 넣은 것과 같습니다.
이 예시에서 그 효과는 결코 작지 않습니다. 특정한 낮은 값은 1.17%의 확률로 나오는 반면 높은 값은 0.78%입니다. 아래쪽 절반이 50% 더 자주 나오고, 낮은 수 하나하나가 공정한 추첨이라면 놓였을 자리보다 17% 위에 있습니다. 왜곡의 크기는 원하는 범위가 생성기의 범위로 얼마나 나쁘게 나뉘느냐에 달려 있으며, 그래서 없는 셈 칠 것이 아니라 반드시 다뤄야 합니다. 5,000명이 참가한 이벤트라면 목록의 특정 위치가 측정 가능할 만큼 더 높은 당첨 확률을 갖게 됩니다.
해법: 거부 표집
올바른 접근은 거부 표집입니다. 모든 난수를 목표 범위에 억지로 밀어 넣는 대신, 편향을 만들 값은 버리고 다시 뽑는 것입니다.
- 전체 범위(0~255)에서 난수를 뽑습니다
- 편향 구간(이 예에서는 200~255)에 들어가면 버립니다
- 다시 뽑습니다
- 편향 없는 구간(0~199)에 들어올 때까지 반복합니다
- 그 값에 모듈로를 적용합니다
이렇게 하면 목표 범위 전체에 완전히 균일한 분포가 보장됩니다. 얼마나 자주 버리게 되는지는 두 범위가 얼마나 잘 맞느냐에 달려 있습니다. 위 예시에서는 256번 중 56번, 약 22%를 버리게 됩니다. 낭비처럼 들리지만 체감되는 비용은 없습니다. 바이트 하나를 더 뽑는 데 나노초가 걸릴 뿐이고, 정확성은 버려진 시도보다 훨씬 값집니다.
나쁜 난수가 부른 실제 결과
이론적인 걱정이 아닙니다. 역사에는 약한 난수 생성이 부른 사고가 즐비합니다.
- 온라인 포커 스캔들: 1990년대 후반 몇몇 온라인 포커 플랫폼이 시스템 시계를 시드로 쓴 단순 PRNG를 사용했습니다. 연구자들은 몇 장의 카드만 관찰해도 덱 순서 전체를 예측할 수 있음을, 즉 모든 참가자의 패를 알 수 있음을 보였습니다.
- 예측 가능한 세션 토큰: 약한 PRNG로 로그인 세션 ID를 만드는 사이트는, 유효한 토큰을 예측한 공격자에게 계정을 탈취당할 수 있습니다.
- 블록체인 취약점: 스마트 컨트랙트의 약한 난수는 블록체인 기반 게임과 추첨에서 “무작위” 결과가 예측되면서 수백만 달러 규모의 손실로 이어졌습니다.
- RSA 키 생성: 2012년 연구자들은 인터넷상의 RSA 공개키 중 0.2%가 부팅 시점의 부족한 엔트로피 때문에 소인수를 공유한다는 사실을 발견했고, 이를 통해 개인키를 도출할 수 있었습니다.
하드웨어 난수 생성기는 어떤가
Web Crypto API보다 더 나아가 전용 하드웨어를 쓰는 시스템도 있습니다.
- 인텔 RDRAND: 현대 인텔·AMD 프로세서에는 실리콘의 열 잡음에서 값을 얻는 하드웨어 난수 생성 명령이 내장되어 있습니다.
- 라바 램프 벽: 클라우드플레어는 카메라로 촬영한 라바 램프 벽을 암호화 인프라의 엔트로피 원천으로 쓰는 것으로 유명합니다.
- 방사성 붕괴: 일부 연구소는 방사성 동위원소의 붕괴를 가이거 계수기로 측정합니다. 양자역학이 지배하는 근본적으로 예측 불가능한 현상입니다.
대부분의 용도에는 Web Crypto API가 모으는 운영체제 수준의 엔트로피로 충분하고도 남습니다. 하드웨어 난수 생성기는 주로 은행과 인증기관의 HSM 같은 특수한 맥락에서 쓰입니다.
무엇을 보고 도구를 골라야 하는가
추첨을 돌리거나 당첨자를 뽑거나 안전한 테스트 데이터를 만들어야 한다면, 다음을 만족하는 도구가 필요합니다.
Math.random()이 아니라 Web Crypto API(crypto.getRandomValues)를 쓸 것- 거부 표집으로 모듈로 편향을 제거할 것
- 브라우저 안에서 실행되어 누구도 결과를 가로채거나 조작할 수 없을 것
- 방법론을 투명하게 밝힐 것
무료 난수 생성기가 정확히 그렇게 동작합니다. 전적으로 브라우저에서 돌아가므로 완전히 사적이고 즉각적이며 수학적으로 공정합니다. 목록에서 이름이나 항목을 고르려면 돌림판을 써 보세요. 같은 암호학적 공정성에 시각적 재미가 더해집니다. 보드게임용 동전 던지기나 주사위가 필요하다면 동전·주사위 도구 역시 매번 Web Crypto API를 사용합니다.
다음번 이벤트에서는 결함 있는 시계 알고리즘에 결과를 맡기지 마세요. 암호학적으로 확실한 근거를 갖고 당첨자를 뽑을 수 있습니다.