C#에서 랜덤 숫자 생성
C#에서 랜덤 숫자를 생성하는 것은 한 줄의 코드처럼 보입니다. 많은 경우 실제로 그렇습니다. 하지만 언어는 랜덤 값을 생성하는 여러 방법을 제공하며, 이들 간의 차이는 스레드 안전성, 재현성 및 사용 사례를 고려할 때 중요합니다. 잘못된 접근 방식을 선택하면 다중 스레드 코드에 미묘한 버그가 도입되거나 보고된 결함을 재현할 수 없게 만들 수 있습니다.
그의 비디오 "C#에서 난수 생성"에서 Tim Corey는 고전적인 Random 클래스에 대해 설명하고, 시드 값이 존재하는 이유를 설명하며, 현대적인 기본값으로 Random.Shared을 소개합니다. 각 접근 방식을 다루면서 그 배경에 있는 이유를 설명하여 추측하지 않고 올바른 것을 선택할 수 있도록 도와드립니다. 왜 이렇게 간단해 보이는 것에 여러 경로가 있는지 궁금하셨다면, 이 기사가 이를 설명해줍니다.
데모 설정
[0:11 - 0:59] Tim은 Visual Studio 2026 (현재 미리보기)에서 실행 중인 콘솔 애플리케이션 내부에서 작업을 합니다. 보여진 모든 내용은 .NET 9 및 Visual Studio 2022에도 동일하게 적용되므로, 이미 설치된 도구와 함께 따라갈 수 있습니다.
데모 레이아웃은 각 반복에서 두 개의 난수를 나란히 출력하는 for 루프입니다. 두 개의 출력이 병렬로 실행됨에 따라 두 생성기가 독립적으로 작동하는지 또는 동일한 결과를 산출하는지를 관찰하기가 쉬워집니다. 시드 값이 등장할 때 이 구분이 중요해집니다.
고전적 랜덤 클래스
[0:59 - 3:28] C#에서 랜덤 정수 생성에 대한 원래 접근 방식은 Random 클래스의 인스턴스를 만드는 것을 포함합니다:
Random rng1 = new Random();
Random rng2 = new Random();Random rng1 = new Random();
Random rng2 = new Random();각 인스턴스는 자체 내부 상태를 유지합니다. 어느 쪽이든 .Next(1, 101)을 호출하면 1에서 100 사이의 정수가 생성됩니다. Tim은 신입들을 혼란스럽게 하는 세부사항을 강조합니다: 최소값은 포함되지만 최대값은 포함되지 않습니다. 1부터 100까지의 값을 원하면 1와 101을 전달하고, 1와 100을 전달하지 않습니다.
int output1 = rng1.Next(1, 101);
int output2 = rng2.Next(1, 101);int output1 = rng1.Next(1, 101);
int output2 = rng2.Next(1, 101);애플리케이션을 실행하면 두 인스턴스가 서로 다른 시퀀스를 생성함을 확인합니다. 그 결과는 직관적으로 느껴지지만 두 인스턴스가 동일한 시작점을 공유할 때 무슨 일이 발생하는지에 대해서는 또 다른 이야기가 됩니다.
이 접근 방식에 대한 중요한 주의 사항: 개별 Random 인스턴스는 스레드 안전하지 않습니다. 애플리케이션이 병렬 처리를 수행하고 여러 스레드가 동일한 인스턴스에 접근하면 내부 상태가 손상되어 0 또는 중복 값을 생성할 수 있습니다. 안전한 방법은 스레드마다 하나의 인스턴스를 생성하는 것입니다. 이 제약 사항이 언어가 나중에 더 나은 대안을 도입한 이유 중 하나입니다.
시드 값과 재생 가능한 시퀀스
[3:28 - 6:00] Tim은 나중에 두 생성자에 명시적인 시드를 전달합니다:
Random rng1 = new Random(25);
Random rng2 = new Random(25);Random rng1 = new Random(25);
Random rng2 = new Random(25);출력이 크게 달라집니다. 이제 두 생성기가 동일한 시퀀스를 생성합니다: 79, 16, 25, 90, 50, 41, 등등. 시드를 모르면 개별 숫자는 여전히 예측할 수 없지만 같은 시작 값을 주면 진행은 결정적입니다.
왜 그렇게 원할까요? Tim은 실용적인 예를 제공합니다. 세션 중 랜덤 이벤트를 생성하는 게임을 상상해 보세요. 플레이어가 버그를 보고하지만, 결과가 랜덤화되어 재현할 수 없습니다. 게임이 해당 세션에 사용된 시드를 기록했다면 개발자는 동일한 값으로 새 Random 인스턴스를 초기화하여 정확한 의사결정 체인을 재현할 수 있습니다. 이 동일한 논리는 랜덤한 동작에 대해 신뢰할 수 있는 단언을 작성해야 하는 단위 테스트 시나리오에도 적용됩니다.
시드된 인스턴스는 제어된 랜덤화를 제공합니다: 예측할 수 없게 보이지만 요청 시 재생할 수 있는 시퀀스입니다. 이 기능 때문에 시드를 수락하는 고전적인 Random 생성자가 더 이상 사용되지 않음에도 불구하고 더 간단한 API가 존재합니다.
Random.Shared: 현대적 기본값
[7:36 - 9:01] .NET 6부터는 대부분의 난수 생성에 대한 권장 접근 방식은 Random.Shared입니다:
int output1 = Random.Shared.Next(1, 101);
int output2 = Random.Shared.Next(1, 101);int output1 = Random.Shared.Next(1, 101);
int output2 = Random.Shared.Next(1, 101);인스턴스화가 필요 없습니다. Random.Shared는 런타임에서 관리하는 정적, 스레드 안전한 인스턴스입니다. .Next()(또는 Random 클래스의 다른 메서드 중 하나)를 호출하고 객체의 수명 주기나 동시성을 걱정하지 않고 값을 받습니다.
Tim은 이를 증명하기 위해 두 번의 데모를 실행합니다. 첫 번째 실행은 94와 91로 시작합니다; 두 번째는 42와 70으로 시작합니다. 시드된 인스턴스와 달리, Random.Shared은 프로세스 시작 시마다 다른 시작 상태에서 시작합니다. 시드를 설정할 수 없으므로 이 API를 통해 재현 가능한 시퀀스를 생성할 수 없습니다. 그것이 트레이드 오프입니다: 단순성과 안전성을 제공하는 대신 결정론적 재생을 포기하게 됩니다.
.Next()를 넘어서, Random.Shared는 더블 생성, 바이트 배열 채우기 및 컬렉션 셔플링을 위한 메서드를 제공합니다. 의식 없이 빠르게 랜덤 값을 필요로 하는 대부분의 애플리케이션 코드에서는 이 단일 정적 속성이 자체 인스턴스를 관리하는 보일러플레이트를 대체합니다.
올바른 접근법 선택하기
[9:01 - 9:30] Tim은 간결한 의사 결정 프레임워크로 마무리합니다. 일상적인 무작위성 (값 선택, 리스트 셔플링, 무작위 요소 선택)에는 Random.Shared이 적합한 선택입니다. 설정이 필요 없고, 동시성을 처리하며 스레드 전반에서 올바르게 동작합니다.
디버깅, 테스트 또는 시뮬레이션 재연을 위해 반복 가능한 출력 시리즈가 필요할 때, 알려진 시드로 전용 Random 인스턴스를 생성하십시오. 그 인스턴스는 스레드 간에 공유하면 안전하지 않다는 점을 염두에 두세요.
그리고 보안 관련 토큰, 키, 비밀번호 소금 등을 포함하는 모든 것에 대해서는 이 접근법 중 어느 것도 적절하지 않습니다. Tim은 시청자에게 System.Security.Cryptography의 암호화 라이브러리를 소개하며, 이 라이브러리는 무작위할 뿐만 아니라 예측에 저항하는 값을 생성합니다.
총정리: 간단한 API, 의미 있는 차이점
[9:30 - 9:50] 이 주제를 기만적으로 어렵게 만드는 것은 얼마나 적은 코드가 필요한지입니다. 한 줄이면 어느 접근 방식으로든 랜덤 숫자를 생성할 수 있습니다. 복잡성은 문법에 있지 않고 각 메서드가 제공하는 보증이 무엇인지, 즉 스레드 안전성, 재현성 또는 암호 강도에 있습니다.
결론
[9:50 - 10:05] 요약하자면: Random.Shared은 사전 설정 없이 대부분의 필요를 충족시켜주며, 내장된 스레드 안전성을 제공합니다. 시드된 Random 인스턴스는 디버깅이나 테스트에서 특정 시퀀스 재현이 필요할 때 사용합니다. 암호 생성기는 예측 가능성이 취약성인 보안-민감 코드에 속합니다
다음 번 C#에서 랜덤 번호를 찾을 때, 결정은 한 가지 질문으로 좁혀집니다: 나중에 이 시퀀스를 재생할 필요가 있습니까? 만약 답이 아니오라면, Random.Shared이면 충분합니다.
예제 팁: Random.Shared.Next(min, max)을 호출할 때, max이 배타적임을 유의하십시오. 1에서 100의 범위는 1 및 101을 통과해야 합니다. 이 잘못된 1차 경계는 시드된 인스턴스에도 적용됩니다.

