랜덤 생성기: 작동 원리와 사용 시기
· 13분 읽기
랜덤 생성기는 어디에나 있습니다. 재생 목록을 섞거나, 비밀번호를 생성하거나, 시뮬레이션을 실행하거나, 비디오 게임을 할 때마다 난수 생성기가 뒤에서 작동하고 있습니다. 하지만 정확한 명령을 따르는 결정론적 기계인 컴퓨터가 어떻게 무작위성을 생성하는지 궁금해 본 적이 있나요? 그 답은 여러분이 예상하는 것보다 더 흥미롭고 (그리고 더 미묘합니다).
이 가이드는 랜덤 생성기의 과학과 공학을 탐구합니다. 진정한 난수와 의사 난수의 차이, 인기 있는 알고리즘의 작동 방식, 암호학적 등급의 무작위성이 필요한 시기, 그리고 일상 생활에 영향을 미치는 실용적인 응용 프로그램을 다룰 것입니다. 올바른 랜덤 함수를 선택하는 개발자이든, 확률에 대해 배우는 학생이든, 아니면 단순히 사물이 어떻게 작동하는지 궁금한 사람이든, 이 가이드가 도움이 될 것입니다.
무작위성이란 무엇인가? 심층 분석
무작위성은 직관적으로 보입니다 — 예측 불가능성, 혼돈, 패턴의 부재입니다. 하지만 무작위성을 정확하게 정의하는 것은 놀랍도록 어렵고, 수학자들은 수세기 동안 이에 대해 논쟁해 왔습니다.
가장 엄격한 의미에서, 시퀀스는 이전의 모든 값에 대한 완전한 지식이 있어도 어떤 알고리즘도 추측보다 더 나은 다음 값을 예측할 수 없다면 진정으로 무작위입니다. 이것을 콜모고로프 무작위성이라고 합니다 — 시퀀스가 더 짧은 설명으로 압축될 수 없다면 무작위입니다. 예를 들어, 파이의 숫자는 무작위성에 대한 모든 통계적 테스트를 통과하지만, 완전히 결정론적입니다 (충분한 계산으로 어떤 숫자든 계산할 수 있습니다).
실용적인 목적을 위해, 우리는 무작위성의 세 가지 속성에 관심을 갖습니다:
- 균등 분포: 모든 가능한 결과가 동일한 확률로 발생합니다.
- 독립성: 각 값은 이전 또는 미래 값과 관련이 없습니다.
- 예측 불가능성: 관찰자는 이력으로부터 다음 값을 예측할 수 없습니다.
다른 응용 프로그램은 이러한 속성의 다른 수준을 요구합니다. 주사위 굴리기 게임은 균등성과 독립성이 필요하지만, 반드시 예측 불가능성이 필요한 것은 아닙니다. 비밀번호 생성기는 세 가지 모두가 필요합니다. 응용 프로그램에 필요한 무작위성의 종류를 이해하는 것이 올바른 생성기를 선택하는 첫 번째 단계입니다.
진정한 무작위 vs. 의사 무작위: 근본적인 차이
모든 랜덤 생성기는 두 가지 범주로 나뉘며, 이 구분은 정확성과 보안 모두에 매우 중요합니다.
진정한 난수 생성기(TRNG)
진정한 난수 생성기는 근본적으로 예측할 수 없는 물리적 현상에서 무작위성을 도출합니다:
- 방사성 붕괴: 원자가 입자를 방출하는 정확한 순간은 양자역학에 따라 진정으로 예측할 수 없습니다.
- 대기 잡음: Random.org는 무선 수신기를 사용하여 대기 전자기 잡음을 포착하는데, 이는 혼란스럽고 재현할 수 없습니다.
- 열 잡음: 전자 부품은 전자의 열 운동으로 인해 무작위 전압 변동을 생성합니다.
- 광자 감지: 빔 스플리터를 통과하는 개별 광자의 정확한 도착 시간과 경로는 양자역학적으로 무작위입니다.
- 하드웨어 RNG: 최신 CPU(Intel의 RDRAND, AMD의 동등 제품)는 전자 잡음을 수집하여 무작위 비트를 생성하는 내장 회로를 포함합니다.
TRNG의 핵심 속성은 출력이 재현 불가능하다는 것입니다. 동일한 장비와 조건에서도 매번 다른 시퀀스를 얻게 됩니다. 이것은 재현성이 치명적인 결함이 될 암호학적 응용 프로그램에 필수적입니다.
의사 난수 생성기(PRNG)
PRNG는 알고리즘입니다 — 무작위로 보이지만 초기 상태(시드라고 함)가 주어지면 완전히 예측 가능한 숫자 시퀀스를 생성하는 결정론적 수학 공식입니다. PRNG에 동일한 시드를 제공하면 매번 정확히 동일한 시퀀스를 생성합니다.
이것은 약점처럼 들릴 수 있지만, 실제로는 많은 응용 프로그램의 기능입니다:
- 재현성: 과학자들은 시드를 기록하여 시뮬레이션을 정확히 재현할 수 있습니다.
- 속도: PRNG는 TRNG보다 몇 배나 빠릅니다.
- 특수 하드웨어 불필요: PRNG는 모든 컴퓨터에서 실행됩니다.
- 테스트 가능성: 개발자는 고정된 시드로 프로그램을 디버그하여 일관된 동작을 보장할 수 있습니다.
과제는 출력이 진정한 무작위성과 통계적으로 구별할 수 없는 PRNG를 설계하는 것입니다 — 균등성, 독립성 및 패턴 부재에 대한 모든 알려진 통계적 테스트를 통과하는 것입니다.
🎲 생성기를 사용해 보세요
의사 난수 생성기(PRNG)의 작동 원리
핵심적으로 PRNG는 각 호출마다 변환하는 내부 상태를 유지하여 출력을 생성하고 다음을 위해 상태를 업데이트합니다. PRNG의 품질은 변환이 패턴과 상관관계를 얼마나 잘 피하는지에 달려 있습니다.
선형 합동 생성기(LCG)
가장 간단하고 오래된 PRNG 계열인 LCG는 다음 공식을 사용합니다:
state = (a × state + c) mod m
output = state
여기서 a(승수), c(증분), m(모듈러스)은 신중하게 선택된 상수입니다. 예를 들어, 고전적인 MINSTD 생성기는 a = 16807, c = 0, m = 2³¹ - 1을 사용합니다. LCG는 매우 빠르지만(곱셈과 모듈로) 잘 알려진 약점이 있습니다: 순차적 값은 고차원으로 플롯할 때 패턴을 보이고, 하위 비트는 짧은 주기로 순환합니다. 대부분의 최신 응용 프로그램은 LCG를 넘어섰습니다.
메르센 트위스터(MT19937)
메르센 트위스터는 1997년 도입 이후 많은 언어와 시스템에서 기본 PRNG였습니다. 624개의 32비트 정수 상태를 유지하며 2¹⁹⁹³⁷ - 1(메르센 소수, 따라서 이름)의 주기를 갖습니다. 그 주기는 천문학적으로 길며 — 관측 가능한 우주의 원자 수보다 훨씬 많습니다.
알고리즘은 다음과 같이 작동합니다:
- 시드에서 624개의 상태 값 초기화
- 각 난수에 대해 비트 시프트와 XOR 연산을 통해 상태 값을 추출하고 "템퍼링"
- 트위스트 변환을 사용하여 주기적으로 전체 상태 배열 재생성
메르센 트위스터는 대부분의 통계적 테스트를 통과하며 시뮬레이션, 게임 및 범용 사용에 탁월합니다. 그러나 암호학적으로 안전하지 않습니다 — 624개의 출력을 관찰하면 전체 내부 상태를 재구성하고 모든 미래 값을 예측할 수 있습니다.
Xoshiro256** 및 최신 PRNG
xoshiro256**(많은 최신 언어에서 사용) 및 PCG(순열 합동 생성기)와 같은 최신 PRNG는 이전 생성기의 단점을 해결합니다. 다음을 제공합니다:
- 더 작은 상태(메르센 트위스터의 20KB 대비 256비트)
- 엣지 케이스에서 더 나은 통계적 속성
- 단일 명령 연산으로 더 빠른 실행
- 점프 가능한 상태(중간 값을 생성하지 않고 시퀀스에서 앞으로 건너뛰기)
JavaScript의 Math.random()
JavaScript에서 Math.random()을 호출하면 최신 엔진(V8, SpiderMonkey, JavaScriptCore)은 좋은 통계적 속성을 가진 빠른 PRNG인 xorshift128+를 사용합니다. 0(포함)과 1(제외) 사이의 부동 소수점 숫자를 생성합니다. 게임, 셔플 및 무작위 선택에는 완벽하지만, 보안에 민감한 목적으로는 절대 사용해서는 안 됩니다.
// min과 max 사이의 기본 난수(포함)
function randomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
// 배열에서 무작위 요소
function randomChoice(array) {
return array[Math.floor(Math.random() * array.length)];
}
// 배열 셔플(Fisher-Yates 알고리즘)
function shuffle(array) {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
return array;
}
암호학적 무작위성: 보안이 중요할 때
보안 응용 프로그램 — 비밀번호, 암호화 키, 세션 토큰, 논스 — 의 경우 일반 PRNG는 위험할 정도로 부적절합니다. 난수를 예측할 수 있는 공격자는 전체 보안 모델을 손상시킬 수 있습니다.
CSPRNG를 다르게 만드는 것은 무엇인가?
암호학적으로 안전한 의사 난수 생성기(CSPRNG)는 중요한 속성을 추가합니다: 전방 및 후방 비밀성. 공격자가 현재 내부 상태를 발견하더라도 이전 출력을 결정할 수 없습니다(후방 비밀성). 그리고 상태를 모르고 과거 출력에서 미래 출력을 예측하는 것은 계산적으로 불가능합니다.
CSPRNG는 다음을 통해 이를 달성합니다:
- 큰 내부 상태: 256+ 비트로 무차별 대입 상태 복구를 비실용적으로 만듭니다
- 일방향 함수: 출력 변환은 계산적으로 되돌릴 수 없습니다
- 지속적인 재시딩: 하드웨어 소스에서 신선한 엔트로피를 정기적으로 혼합합니다
- 상태 파괴: 사용 후 이전 상태를 덮어씁니다
일반적인 CSPRNG
- ChaCha20: Linux의
/dev/urandom(커널 4.8 이후) 및 많은 최신 시스템에서 사용됩니다. 빠르고 안전하며 잘 분석되었습니다. - AES-CTR-DRBG: 카운터 모드의 AES 암호화를 기반으로 합니다. NIST 권장 및 최신 CPU에서 하드웨어 가속됩니다.
- Fortuna: Bruce Schneier가 설계했습니다. 부분 손상에 대한 복원력을 위해 여러 엔트로피 풀을 사용합니다.
실제로 암호학적 무작위성 사용하기
브라우저에서 Web Crypto API는 암호학적 등급의 무작위성을 제공합니다:
// 암호학적으로 안전한 난수 정수 생성
function secureRandomInt(min, max) {
const range = max - min + 1;
const bytesNeeded = Math.ceil(Math.log2(range) / 8);
const maxValid = Math.floor(256 ** bytesNeeded / range) * range - 1;
let value;
do {
const array = new Uint8Array(bytesNeeded);
crypto.getRandomValues(array);
value = array.reduce((acc, byte, i) => acc + byte * (256 ** i), 0);
} while (value > maxValid); // 편향된 값 거부
return min + (value % range);
}
// 안전한 무작위 비밀번호 생성
function generatePassword(length = 16) {
const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*';
const array = new Uint32Array(length);
crypto.getRandomValues(array);
return Array.from(array, x => chars[x % chars.length]).join('');
}
암호학적 등급의 무작위성을 위해 Web Crypto API를 사용하는 비밀번호 생성기로 안전한 비밀번호를 생성해 보세요.
엔트로피: 무작위성의 연료
무작위성의 맥락에서 엔트로피는 시스템의 진정한 예측 불가능성의 양을 측정합니다. 비트로 측정됩니다 — 1비트의 엔트로피는 공격자가 예측할 수 없는 하나의 이진 선택을 의미합니다.
엔트로피는 어디에서 오는가?
운영 체제는 여러 소스에서 지속적으로 엔트로피를 수집합니다:
- 키보드 및 마우스 타이밍: 키 입력과 마우스 움직임 사이의 정확한 마이크로초 수준 간격은 예측할 수 없습니다.
- 디스크 I/O 타이밍: 탐색 시간과 인터럽트 타이밍은 기계적 및 전자적 요인으로 인한 고유한 지터를 갖습니다.
- 네트워크 패킷 타이밍: 네트워크 패킷의 도착 시간은 네트워크 조건으로 인한 서브 마이크로초 변동을 포함합니다.
- 하드웨어 RNG: 최신 CPU는 전용 엔트로피 소스를 제공합니다(Intel RDRAND/RDSEED, ARM RNDR).
- 열 센서 잡음: 최대 정밀도의 온도 판독값은