윈도우 XP는 초기 사용자 프로필 사진을 어떤 알고리즘으로 골랐을까요?

과거 널리 쓰였던 마이크로소프트의 운영체제 윈도우 XP에서 새로운 사용자 계정을 처음 만들 때, 초기 프로필 사진이 어떤 기술적 원리로 무작위 배정되었는지를 설명하는 글입니다. 운영체제는 컴퓨터의 하드웨어와 프로그램을 전반적으로 관리해 주는 기본 소프트웨어를 말합니다. 윈도우 XP는 기본 사진들이 저장된 특정 폴더에서 무작위로 하나를 골라 초기 사진으로 지정했습니다. 글쓴이는 여기에 'RtlRandomEx'라는 난수(무작위 숫자) 생성 도구가 사용되었고, 컴퓨터가 시작된 뒤 흐른 시간을 밀리초 단위로 재는 'GetTickCount()'의 현재 값이 난수를 만들기 위한 시작값(초기 시드)으로 활용되었다고 밝힙니다. 아울러 전체 사진 목록을 단 한 번만 훑으면서도 모든 사진이 균등한 확률로 뽑히도록 설계된 무작위 선택 방식이 사용되었습니다.

보통 목록에서 무작위로 하나를 고를 때는 먼저 폴더 안에 사진이 총 몇 장 있는지 처음부터 끝까지 세어본 뒤, 1부터 전체 개수 사이의 번호를 무작위로 하나 뽑아 다시 그 번호의 사진을 찾아가는 두 번 훑기 방식을 떠올리기 쉽습니다. 하지만 이 글은 윈도우 XP가 사진 목록을 두 번 읽지 않고 단 한 번만 차례대로 훑으며 최종 사진을 결정하는 알고리즘을 사용했다는 점을 짚어줍니다. 컴퓨터에서 속도가 상대적으로 느린 파일 시스템을 반복해서 호출하는 작업은 전체 성능을 떨어뜨리는 병목 지점이 되는데, 한 번만 훑는 방식을 쓰면 파일 시스템 호출 횟수를 줄여 훨씬 효율적입니다. 아울러 코드가 실행되는 도중에 해당 폴더의 파일 수가 바뀌더라도 생길 수 있는 복잡한 오류를 방지할 수 있습니다. 또한 누군가 기본 사진 폴더에 백만 개의 파일을 넣어두는 것과 같은 비정상적인 상황에 대비해, 최대 100장의 사진까지만 확인하고 표본 추출을 멈추도록 안전장치를 두었다는 점도 설명합니다.

얼마 전 저는 윈도우 XP가 사용자의 기본 프로필 사진을 고를 때, 모든 사용자가 공유하는 기본 폴더 경로인 %ALLUSERSPROFILE%\Application Data\Microsoft\User Account Pictures\Default Pictures 디렉터리에 들어 있는 사진 중에서 무작위로 하나를 선택한다고 언급한 적이 있습니다. 여기서 디렉터리란 컴퓨터에서 파일들을 모아두는 폴더를 뜻합니다. 그런데 많은 분이 그보다 더 구체적인 원리를 알고 싶어 하시는 것 같습니다.

"윈도우 XP에서 계정을 처음 만들 때 어떤 프로필 사진을 넣을지 결정하는 무작위 난수 생성기 원리를 파헤쳐 본 사람이 있나요?"

— Xeno (@XenoPanther) 2025년 12월 11일

이때 무작위 숫자를 뽑아내는 난수 생성 기능은 윈도우 내부 함수인 RtlRandomEx를 사용했습니다. 그리고 컴퓨터가 부팅된 후 흘러간 시간을 밀리초 단위로 알려주는 GetTickCount() 함수의 현재 측정값을 난수의 시작 기준점인 '초기 시드(seed)'로 사용합니다. 시드는 주사위를 굴리기 전 손을 흔드는 첫 동작처럼, 컴퓨터가 예측하기 힘든 무작위 수를 만들기 위해 넣어주는 시작 번호입니다.

이 기능은 목록을 딱 한 번만 훑으면서 무작위로 항목을 뽑는 '원패스(one-pass)' 방식을 씁니다. 이런 방식을 선택해서 얻는 이점은 두 가지가 바로 떠오릅니다. 먼저, 단순히 생각해서 전체 파일 개수를 한 번 다 센 다음 1부터 전체 개수 사이의 숫자를 무작위로 뽑고, 다시 목록을 처음부터 훑어가며 해당 위치의 파일을 찾는 '투패스(two-pass)' 방식과 비교했을 때 훨씬 효율적입니다. 하드디스크 같은 저장 공간에서 파일 정보를 읽어오는 작업은 속도가 느려서 전체 작업의 발목을 잡는 병목 구간이 되기 쉬운데, 원패스 방식은 파일 시스템을 조회하는 횟수 자체를 줄여주기 때문입니다. 게다가 컴퓨터가 코드를 실행하는 도중에 폴더 안의 파일 개수가 갑자기 바뀌더라도, 한 번만 훑는 방식은 복잡한 오류나 꼬임 현상을 피할 수 있습니다.

이 원패스 알고리즘은 전체 데이터 중에서 몇 개를 무작위로 골라내는 '저수지 표본 추출(reservoir sampling)' 기법에서 뽑는 개수인 k가 1인 특수한 경우입니다. 골라야 하는 개수가 딱 1개뿐인 특수한 상황이므로, 훨씬 더 단순하고 맞춤화된 방식을 적용할 수 있습니다.

selectRandomFromIterator(iterator) { var count = 0; var winner = null; 이 코드는 파일 목록을 순서대로 하나씩 넘겨주는 도구(iterator)를 받아 무작위 항목 하나를 고르는 함수입니다. 먼저 지금까지 확인한 파일 개수를 셀 변수인 count를 0으로 맞추고, 최종 선택된 사진을 담을 변수인 winner를 비어 있는 상태(null)로 준비합니다.

while (iterator.moveNext()) { ++count; if (uniform_random(min: 1, max: count) == count) { winner = iterator.current(); } } 이어서 폴더 안의 다음 파일이 있을 때까지 반복해서 이동합니다. 파일을 하나씩 확인할 때마다 개수(count)를 1씩 올립니다. 그런 다음 1부터 현재 센 개수 사이에서 균등한 확률로 숫자를 하나 뽑았을 때, 그 숫자가 마침 방금 확인한 번호(count)와 일치하면 지금 보고 있는 파일(iterator.current())을 당첨 항목(winner)으로 교체합니다.

return winner; } 목록을 끝까지 확인하고 나면 마지막으로 정해진 당첨 파일(winner)을 최종 결과로 돌려줍니다.

이 알고리즘이 작동하는 핵심 원리는 전체 n개의 항목이 모여 있을 때, 맨 마지막 항목이 무작위로 최종 선택될 확률이 정확히 n분의 1(1/n)이라는 점에서 출발합니다. 만약 마지막 항목이 선택되지 않는다면, 그 앞의 n - 1개 항목 중에서 무작위로 하나를 뽑아야 하는 것과 같으며, 이는 이전 단계의 선택 방식을 그대로 되짚어 푸는 재귀적인 논리로 해결할 수 있습니다.

이 재귀 과정을 처음부터 순서대로 적용해 보면 이해하기 쉽습니다. 가장 기초 단계로 목록에 항목이 딱 1개만 있다면, 선택할 수 있는 대상은 그 항목 하나뿐입니다. 만약 전체 항목이 n개라면, 먼저 앞선 n - 1개 항목 중에서 무작위로 하나를 골라 둔 다음, 마지막 n번째 항목으로 바꿀지 말지를 n분의 1 확률로 결정하면 됩니다.

마지막 안전장치로, 이 코드는 최대 100장의 사진까지만 확인하고 탐색을 중단합니다. 이렇게 제한을 두면 누군가 기본 사진 폴더에 수백만 개의 파일을 집어넣더라도, 컴퓨터가 사진을 고르느라 지나치게 오래 멈춰 서 있는 비정상적인 작동을 막을 수 있습니다.

현재 윈도우 XP를 주력으로 사용하는 일반 사용자는 거의 없기 때문에 일상적인 컴퓨터 사용에 당장 미치는 직접적인 영향은 없습니다. 다만 컴퓨터 프로그램을 만들거나 데이터 처리를 공부하는 사람에게는 데이터의 전체 개수를 미리 알기 어렵거나 파일 시스템처럼 접근 비용이 큰 환경에서, '저수지 표본추출(reservoir sampling)'이라는 기법(이 경우 1개를 뽑는 특수한 형태)을 어떻게 실무에 적용해 성능과 안정성을 확보했는지 배울 수 있는 유용한 기술적 참고 사례가 됩니다.