본문으로 건너뛰기

© 2026 Molayo

Qiita헤드라인2026. 05. 30. 08:34

복권 2,000만 장에서 1등을 찾는 시뮬레이터를 만들었다

요약

2,000만 장의 복권 데이터를 효율적으로 시뮬레이션하는 Web 앱 개발 사례를 소개합니다. Canvas를 활용한 렌더링 최적화와 해시 기반의 경량 상태 관리 기법을 다룹니다.

핵심 포인트

  • Canvas를 사용하여 2,000만 개의 DOM 요소 생성 없이 렌더링 최적화
  • Map과 좌표 해시(Hash)를 이용한 대규모 데이터의 경량 상태 관리
  • 카메라 배율에 따른 LOD(Level of Detail) 방식의 그리기 전략 적용
  • 복권의 기대값과 환급률을 시각화하여 사용자 경험 제공

복권의 당첨 확률은 숫자로만 봐서는 실감이 잘 나지 않는다.

그것을 체험할 수 있는 Web 앱을 만들었다.

본 기사에서는 얼마나 당첨되지 않는지에 대한 숫자와, 앱의 기능 및 조작법을 정리한다.

실제로 존재하는 고액 복권의 '1 유닛(Unit)'은 20,000,000장(2,000만 장)으로 구성된다.

등급과 수량의 예시는 다음과 같다.

등급금액수량당첨 확률
1등7억 엔11 / 20,000,000
...

1장당 300엔이므로, 1 유닛을 모두 사면 6,000,000,000엔(60억 엔)의 투자가 필요하다.

반면, 전체 수량 × 금액의 합계 환급금은 약 26억 엔이다. **환급률은 약 43%**로, 사면 살수록 기대값(Expected Value) 베이스로는 손해를 보는 구조로 되어 있다.

체감하기 쉬운 숫자를 나열해 본다.

  • 1만 장(300만 엔 분량)을 사더라도, 1등이 당첨될 확률은 1/2,000.
  • 최하위 등급(300엔)은 약 1,000장당 약 30만 엔의 환급. 투자한 300만 엔 중 돌아오는 것은 1할.
  • 1등을 노리려면 평균적으로 2,000만 장(60억 엔)이 필요하다는 거친 계산.

이것을 화면으로 보여주는 것이 본 앱이다.

20열 × 20행 = 400 에리어로 분할하고, 각 에리어에 250 × 200 = 50,000장을 배치한다. 합계 20,000,000장. 모두 Canvas로 그리고 있으며, DOM 요소는 생성하지 않는다.

처음 표시되는 다이얼로그는 「뒤집기 / 대량 구매 / 1등 찾기」의 3가지 아이콘과 시작 버튼만 있는 심플한 구성이다.

  • 1등(1장)과 전후상(앞뒤 보너스 상금)·2등·3등·4등의 소수 상위 등급은 기동 시 좌표를 확정하여 Map에 유지.
  • **5등(약 1/100)과 최하위 등급(약 1/10)**은 좌표 해시(Hash)로 판정하여, 200만 건의 좌표를 유지하지 않음.

이로써 2,000만 장 분량의 상태 관리를 경량으로 수행하고 있다.

줌 인(Zoom-in)을 하면, 각 권은 가로가 긴 티켓 형태(오렌지 + 핑크색 번호면 + 빨간 띠)로 그려진다. 뒤집은 결과는 등급별로 색상을 구분하고, ★를 중앙에 배치한다.

헤더 우측 하단이 아니라, 메인 영역의 우측 상단에 상시 표시되는 소형 패널.

각 등급의 도안(범례)과 금액, 현재 당첨 수량을 일람 표시한다.

0개인 등급은 흐리게 표시하여, 당첨된 행이 눈에 띄게 한다.

헤더에 「매수 / 투자 / 환급 / 손익」을 상시 표시한다.

손익은 색상으로 구분(빨강 = 손실, 초록 = 이익)하며, 살 때마다 작게 확대되는 bump 애니메이션으로 변화를 보여준다.

수동 탭으로 선택한 1장만 가로 회전(scaleX로 1→0→1)하며 뒤집는다.

폭買い(대량 구매, 1만 장)는 순식간에 처리하며, 플립(Flip) 연출을 넣지 않는다(불필요한 부하를 발생시키지 않기 위함).

권에 마우스를 올리면, 해당 권이 당첨권인 경우에만 등급과 금액을 툴팁(Tooltip)으로 표시한다.

꽝이거나 권 사이의 틈새에서는 나타나지 않는다.

버튼을 누르면 현재 카메라 위치에서 1등의 좌표로 부드럽게 애니메이션 이동한다(위치는 ease-in-out, 배율은 로그 보간).

도착하면 금색 권과 집중선이 표시된다.

랜덤한 좌표에서 1만 장을 일괄 구매한다.

3,000,000엔 분량. 실행 후 푸터(Footer)에 「당첨 수량」과 「환급 금액」이 일시적으로 표시된다.

줌 아웃(Zoom-out) 시에는 에리어 단위의 채우기만 수행하고, 줌 인(Zoom-in) 시에만 개별 권을 그린다.

광역에서도 개별 권을 전부 그리려고 하면 수백만 번의 그리기 명령이 발생하므로, 카메라 배율에 따라 전환한다.

블록의 색이 검은색에 가까워질수록, 해당 에리어 내의 뒤집은 매수가 많음을 나타낸다.

조작방법
이동드래그 / 스와이프

탭 판정은 세계 좌표에서 권의 그리드(Grid)로 사상(Mapping)하여 수행한다.

너무 줌 아웃되어 뒤집을 수 없는 경우, 경고를 띄우는 대신 탭 지점을 향해 자동으로 줌 인한다(조작의 헛스윙을 방지하기 위함).

최초 기동 시에는 중앙 부근의 권에 스케일 1로 밀착된 상태로 시작하며, 손가락 아이콘이 점멸하며 표시된다.

약 1초간 액션이 없으면, 고스트가 손가락을 움직이며 3장을 뒤집어 보여준다.

직접 탭하면 손가락 아이콘은 사라지고 일반 조작으로 돌아간다.

  • 프론트엔드는 단일 index.html.
  • 프레임워크 없이 Canvas 2D만 사용.
  • 그리기(Rendering)는 카메라 배율에 따라 LOD(Level of Detail)를 전환. 가시 사각형 컬링(Culling)을 통해 화면 밖의 권은 그리지 않음.
  • 5등·최하위 등급 판정은 좌표 해시 사용. 고정 좌표를 Map으로 가지는 것은 상위 등급뿐(합계 5,000건 이상).
  • 상태는 Set(구매 완료)과 Map으로 관리.

(당첨)으로 유지. 블록별로 이미 넘긴 매수도 별도로 캐싱하여, 광역 렌더링 시의 어두운 정도를 결정. - 아이콘은 원본 이미지로부터 System.Drawing을 사용하여 여러 크기로 생성. 작은 사이즈는 중앙의 고래 쪽으로 크롭하여 가독성을 확보. - 호스팅은 Cloudflare Pages, 배포는 Wrangler CLI (npm run deploy) 사용.

소스가 단일 파일이며 프레임워크를 사용하지 않으므로, 개조나 이식도 용이함.

1 유닛의 이론적인 환급률은 약 43%.

100만 엔을 사용하면 평균적으로 약 43만 엔이 돌아오고, 57만 엔이 손실이 됨. 이것이 복권을 "자산 형성 수단"이라 부를 수 없는 이유.

다만, 1등 7억 엔이라는 "분산이 큰 이상치 (Outlier)"에 거는 오락으로서 선을 긋는다면, 300엔으로 7억 엔의 보물을 하루 동안 맡겨두고 꿈을 꾸는 것이라는 가성비는 나쁘지 않다는 관점도 가능함.

본 앱은 전자(숫자의 현실)를 시각적으로 강요하지 않으면서 확인할 수 있는 도구.

  • 고액 복권 1 유닛 = 2,000만 매를 화면상에서 재현하여, 넘겨보는 경험을 제공.
  • 1등은 1매 (1/2,000만). 5등·최하위 등수는 좌표 해시 (Coordinate Hash)로 판정하여 가볍게 동작.
  • 투자·환급·손익, 당첨 내역을 상시 표시하여 확률의 편차를 숫자로도 보여줌.
  • 단일 HTML, Canvas 2D만 사용. Cloudflare Pages로 배포.

AI 자동 생성 콘텐츠

본 콘텐츠는 Qiita AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0