← 전체 히스토리
PHASE 2

Three.js 3D 웹 게임

서버 없이 순수 JavaScript(Three.js)만으로 동작하는 3D 웹 게임을 만들어 Cloudflare Pages로 배포한다.

마지막 업데이트: 2026-10-05 00:24 KST

이 단계의 결과물

목차
    #14 새 단계 시작: Three.js 3D 웹 게임 + 히스토리 구조 개편 완료 지시 2026-10-04 23:47 KST → 완료 2026-10-05 00:05 KST

    지시 (원문)

    이제 다른 기능을 개발해보고 싶은데,
    Three.js 활용한 웹 기반 3D 게임 제작해서 cloudflare 배포. 서버 없이 순수 js만으로 구동 가능한지? 필요하다면 cdn을 사용하는 것도 방법일 듯 하다. 한가지 더 질문이 있는데, 이제 새로운 기능 개발 국면인데, 기존 history.html 에서 계속 나열식으로 하는 것보다 더 좋은 방법이 있을까? 너가 알아서 이부분도 구현해봐.

    분석 (Claude가 이해한 내용)

    • 질문 1 — 서버 없이 순수 JS로 가능한가? → 가능. Three.js는 브라우저에서 WebGL로 그리는 라이브러리라 HTML·JS 파일만 있으면 동작함. 게임 로직·저장(최고 점수 등)도 브라우저 안에서 처리 → Cloudflare Pages 같은 정적 호스팅으로 충분.
    • CDN 사용 여부 → 외부 CDN(jsDelivr 등) 대신 Three.js 파일을 게임과 함께 배포하기로 결정. 이유: Cloudflare Pages 자체가 전 세계 CDN이라 속도 차이가 없고, 외부 서비스 장애·버전 변경 영향이 없으며, 방문자 정보가 제3자에게 가지 않음. 공식 npm 패키지(0.186.1)에서 가져와 버전 고정.
    • 질문 2 — 히스토리를 계속 나열하는 것보다 좋은 방법 → 프로젝트가 "기록·배포 단계"에서 "새 기능 개발 단계"로 넘어가므로 단계(Phase)별로 나눔:
      • history.html = 전체 개요(랜딩): 단계별 카드(기간·기록 수·상태·결과물·대표 이미지) + 최근 기록
      • 단계마다 별도 페이지(history-p1.html, history-p2.html) + 맨 위에 "이 단계의 결과물" 요약
      • 공통 스타일·스크립트는 history_assets/로 분리, 랜딩은 스크립트로 자동 생성
    • 게임 기획 (내용 지정이 없어 직접 결정): 모바일·PC 모두 쉽게 할 수 있는 3D 레인 러너 "Raspberry Run" — 라즈베리 캐릭터가 3개 레인을 굴러가며 높은 블록은 피하고, 낮은 바는 점프로 넘고, 코인을 모은다. 점점 빨라짐. 최고 점수는 브라우저에 저장.
    • 구조 설계: 게임 규칙(logic.js)을 Three.js 렌더링(main.js)과 분리 → 화면 없이 Node.js로 게임을 수천 번 시뮬레이션해 "모든 맵이 실제로 클리어 가능한지"를 자동 테스트할 수 있게 함.

    작업 과정

    1. 23:48Three.js 최신 버전 확인(0.186.1), 공식 npm 패키지 내려받음. 이 버전부터 압축(min) 파일이 없어 three.module.js + three.core.js를 사용.
    2. 23:50[실패 1] 헤드리스 Chromium에서 WebGL 테스트 → 빈 화면. file://로 열면 브라우저 보안 규칙상 ES 모듈이 로드되지 않음 → 로컬 HTTP 서버로 테스트하도록 변경.
    3. 23:51원인을 바로 볼 수 있게 tools/screenshot.js에 페이지 오류·콘솔 에러 출력 기능 추가.
    4. 23:51[실패 2] 새 기능으로 원인 확인 — 스크린샷 도구의 --disable-gpu 때문에 WebGL 컨텍스트 생성 실패.
    5. 23:52WEBGL=swiftshader|gpu 옵션 추가 후 비교 → 소프트웨어 렌더러(SwiftShader)는 WebGL2로 정상 렌더링, Pi의 실제 GPU는 헤드리스에서 사용 불가. 게임 테스트는 SwiftShader로 진행.
    6. 23:52히스토리 구조 개편 — 공통 CSS/JS를 history_assets/로 분리, 기존 #1~#13을 history-p1.html로 그대로 이동, history-p2.html 생성, 랜딩 생성 스크립트 tools/build_history.py 작성. 예전 링크(history.html#entry-5)는 해당 단계 페이지로 자동 이동.
    7. 23:53공유 서버(8000번) 허용 목록을 새 구조(단계 페이지·공통 자산)에 맞게 확장. [실패 3] 서버 재시작 때 pgrep -f 패턴이 명령을 실행하는 셸 자신과도 일치해 셸이 종료됨 (#3과 같은 실수) → 명령 시작에 고정한 패턴(^python3)으로 재시작, CLAUDE.md의 경고를 pgrep까지 확대.
    8. 23:54랜딩 확인 — 단계 카드·통계·최근 기록 정상, 예전 링크 history.html#entry-5 → history-p1.html#entry-5 자동 이동 확인. 단계 카드 대표 이미지를 직접 지정할 수 있게 개선.
    9. 23:55게임 규칙 game/src/logic.js 작성 (고정 시드 난수, 장애물 행 생성, 충돌, 점프 물리, 자동 플레이 AI) + Node 테스트 7개 작성.
    10. 23:55[실패 4] AI가 30개 시드 모두에서 충돌. 진단 스크립트로 원인 분석: ① 같은 거리의 블록 2개를 "피해도 소용없음"으로 판단 ② 레인 변경 직후 바를 늦게 인식 ③ 최고 속도에서 장애물 간격 0.29초로 사람도 어려움 → AI를 "충돌까지 남은 시간" 기반으로 재작성, 간격을 약 0.5초로 조정.
    11. 23:56[실패 5] 남은 원인은 모두 바: 같은 레인에 바가 연속(점프 한 번으로 둘 다 못 넘음 — 사람에게도 불공정) → 생성 규칙에서 금지. 공중에서 레인을 바꿔 바 앞에 착지 → AI가 착지 높이를 예측하도록 수정.
    12. 23:56테스트 7개 모두 통과, AI가 30개 시드 중 29개에서 3분 생존 (예: 점수 8,748, 코인 393개). Three.js 파일과 라이선스를 game/vendor/three/에 복사.
    13. 23:58렌더링·입력·사운드·UI game/src/main.js + game/index.html 작성: 로우폴리 라즈베리, 블록/바/코인, 나무·언덕 배경, 그림자, 코인 반짝임, 카메라 흔들림, 효과음(WebAudio), 키보드·스와이프·탭, 일시정지, 최고 점수, WebGL 미지원 안내, 타이틀 화면 자동 플레이 데모.
    14. 23:59첫 실행 — 정상 렌더링. Three.js r186 경고 2건(Clock → Timer, PCFSoftShadowMap 제거) 수정, 타이틀 패널이 데모를 가려 위로 이동.
    15. 00:00[문제 6] 소프트웨어 렌더링(SwiftShader)은 약 4.5 FPS라 15초 동안 게임 시간이 1.7초만 흐름 (실제 기기에서는 GPU 사용). → 시뮬레이션을 1/60초 고정 스텝으로 바꿔 테스트와 동일하게 만들고, 테스트용 ?catchup=1 추가. EVAL 결과를 출력하도록 스크린샷 도구 개선.
    16. 00:01게임 오버 화면, 모바일 세로 화면 확인 → 세로 화면에서 양쪽 레인이 잘려 카메라를 뒤로·위로 조정.
    17. 00:02배포 스크립트를 공통부(tools/pages_deploy.sh: 민감 정보 검사·인증·생성·업로드)와 사이트별(deploy_pages.sh, deploy_game.sh)로 분리. 게임 배포는 테스트 통과 시에만 진행, Three.js에 1년 캐시 헤더.
    18. 00:02게임 배포 → https://pi-raspberry-run.pages.dev/. Three.js는 Brotli 압축으로 2.1MB → 약 400KB 전송 확인.
    19. 00:03실제 배포된 사이트에서 타이틀·플레이·게임 오버·모바일 화면 캡처, 모두 콘솔 에러 없음.
    20. 00:05CLAUDE.md를 새 구조(단계별 기록, 랜딩 자동 생성, 게임, 배포 스크립트, WebGL 테스트 방법)에 맞게 갱신.

    결과 / 증거

    • 답변: 서버 없이 순수 JS만으로 가능 — 정적 파일만 Cloudflare Pages에 올려 동작 중. Three.js는 외부 CDN 대신 함께 배포(Cloudflare가 CDN 역할).
    • 게임: https://pi-raspberry-run.pages.dev/ — PC(방향키/A·D/Space), 모바일(스와이프/탭)
    • 히스토리: 전체 개요 랜딩 + 단계별 페이지로 개편 — https://pi-history.pages.dev/
    새 히스토리 랜딩
    20261005_002248_history_landing.png — 새 히스토리 랜딩: 단계별 카드(기간·기록 수·결과물·대표 이미지) + 최근 기록
    Phase 2 페이지
    20261005_000619_history_phase2.png — 단계 페이지: "이 단계의 결과물" 요약 + 목차 + 기록
    게임 플레이 화면
    20261005_000320_game_play.png — 배포된 게임 플레이 중 (블록·바·코인, 코인 획득 반짝임)
    타이틀 화면
    20261005_000305_game_title.png — 타이틀 화면 (뒤에서 자동 플레이 데모)
    게임 오버 화면
    20261005_000347_game_over.png — 게임 오버 화면 (최고 기록 표시)
    모바일 세로 화면
    20261005_000415_game_mobile.png — 모바일 세로 화면 (390×844)