얼마 전 랜덤 이벤트 기능을 개발하다 문득 이런 생각이 들었어요.
"이게 진짜 무작위일까?"
컴퓨터가 숫자를 만들어내는 방식은 결국 사람이 정의한 수식과 알고리즘인데… 정말 예측 불가능한 걸까?
이번 아티클에서는 Random의 작동 원리, 진짜 무작위(Random)와 의사 무작위(Pseudo-Random)의 차이, Kotlin/Java의 Random은 어디에 속하는지, 그리고 보안적으로 안전한 랜덤 생성 방식까지 다뤄보겠습니다.
| 구분 | 설명 | 예시 |
|---|---|---|
| 진짜 무작위 (True Random) | 자연 현상 기반, 예측 불가 | 라디오 노이즈, 원자 붕괴, 마우스 이동 패턴 |
| 의사 무작위 (Pseudo Random) | 알고리즘 기반, 예측 가능 | Kotlin/Java Random, Python random() |
True Random: 외부 물리적 현상을 기반으로 함 (하드웨어 필요)
Pseudo Random: 특정 시드(seed) 값으로 시작되는 알고리즘 → 재현 가능
의사 난수 생성기는 보통 수학 공식을 기반으로 난수를 생성합니다.
그중 가장 유명한 방식은 선형 합동 생성기(Linear Congruential Generator, LCG)입니다.
Xₙ₊₁ = (aXₙ + c) mod m
예시)
a = 5, c = 3, m = 16, X₀ = 7 → X₁ = (5×7 + 3) mod 16 = 38 mod 16 = 6
시드 값을 같게 하면, 생성되는 모든 난수의 시퀀스가 완전히 동일합니다.
많은 난수 생성기들은 위와 비슷한 수식을 기반으로 반복 계산을 수행해 난수를 생성합니다.
일반적으로는 곱셈 + 덧셈 → 모듈로 연산을 통해 0 ~ m 사이의 값을 순환시키는 방식입니다.
그래서 결국 무한하지 않고, 어느 시점에서는 패턴이 반복됩니다. (이를 주기(Period)라고 합니다)
Kotlin의 기본 Random()은 내부적으로 Java의 java.util.Random 또는 kotlin.random.Random.Default를 사용합니다.
val random = Random(42)
println(random.nextInt()) // 항상 동일한 값 출력
이 방식은 의사 무작위(Pseudo-Random) 생성기고, 고정된 시드값이 주어지면 항상 동일한 결과를 반환하죠.
시드는 랜덤 함수가 시작되는 "초기값"으로, 같은 시드 → 같은 난수열을 보장합니다.
여기서 바로 "Random이 조작 가능하다"는 의미의 본질이 되죠.
BUT.. 중요한 건…
"보안에 쓰기에는 부적절합니다."
암호, 토큰, 인증 코드 등 보안 목적의 난수는 예측이 불가능해야 합니다.
이 때는 SecureRandom 또는 OS 레벨의 무작위 API를 사용합니다.
val secureRandom = SecureRandom()
val token = ByteArray(16)
secureRandom.nextBytes(token)
println(token.joinToString())
| 항목 | Random | SecureRandom |
|---|---|---|
| 타입 | Pseudo Random | Cryptographic Secure Random |
| 시드 변경 | 가능 | 불가 / 내부에서 자동 관리됨 |
| 예측 가능성 | 있음 | 없음 |
| 보안용도 적합성 | ❌ | ✅ |
Random()은 일상적인 개발에는 충분히 훌륭한 도구입니다.
하지만 진짜 무작위처럼 완전히 예측 불가능한 값은 아니라는 점을 기억해야 해요.
게임의 주사위, UI 애니메이션, 테스트 데이터 → Random()
로그인 인증번호, 비밀번호 토큰, 세션 키 → SecureRandom() 또는 UUID.randomUUID()
이제부터는 "진짜 랜덤인가?"라는 질문이 생겼을 때,
그건 '무엇을 위한 랜덤'인지 먼저 생각해보면 좋을 것 같습니다 ㅎ