26강웹으로 내보내고 공유하기
Compatibility 렌더러, COOP/COEP, 정적 호스팅.
이 강의 목차 (12)
- 웹(HTML5)으로 내보내 링크로 공유한다.
- Compatibility 렌더러와 COOP/COEP 헤더 문제를 이해한다.
- 모바일 브라우저(폴드 포함)에서 실제로 플레이한다.
왜 웹 배포인가#
만든 게임을 남에게 보여주는 가장 빠른 방법입니다.
| 방식 | 상대가 해야 할 일 |
|---|---|
| 실행 파일 전달 | 다운로드 → 압축 해제 → 보안 경고 통과 → 실행 |
| 스토어 출시 | 심사 며칠~몇 주 |
| 웹 | 링크 클릭 |
포트폴리오, 프로토타입 공유, 테스트 플레이 모집에 압도적으로 유리합니다.
Godot 웹 빌드의 구조#
game.html 진입점 HTML
game.js 엔진 로더
game.wasm 엔진 본체 (WebAssembly, 20~40MB)
game.pck 게임 데이터 (내 씬·스크립트·에셋)
game.audio.worklet.js
game.png 로딩 화면 이미지WebAssembly(WASM) 는 브라우저에서 네이티브에 가까운 속도로 도는 바이너리 형식입니다.
Godot 엔진 자체를 통째로 WASM으로 컴파일한 것이 game.wasm 입니다.
1) 렌더러 확인#
프로젝트 설정 → Rendering → Renderer → Rendering Method
forward_plus→ 웹에서 안 됩니다 (Vulkan 필요)mobile→ 웹에서 안 됩니다gl_compatibility→ ✓ 이걸 써야 합니다 (OpenGL ES 3.0 / WebGL 2)
1강에서 프로젝트를 만들 때 Compatibility를 고르라고 한 이유가 이것입니다.
지금 다른 렌더러라면 바꾸고 전체를 다시 확인해야 합니다. 셰이더, 조명, 일부 파티클 기능이 동작하지 않을 수 있습니다.
2) 익스포트 템플릿 설치#
에디터 → 익스포트 템플릿 관리 → 다운로드 후 설치
한 번만 하면 됩니다. 용량이 큽니다(1GB 내외).
3) 익스포트 설정#
프로젝트 → 내보내기(Export) → 추가 → Web
주요 옵션:
| 항목 | 설명 |
|---|---|
| Export Type | Regular (기본) / Extension |
| Variant → Thread Support | 가장 중요합니다. 아래 참고 |
| VRAM Texture Compression | 모바일 대상이면 For Mobile 체크 |
| HTML → Custom HTML Shell | 직접 만든 HTML 껍데기 |
| HTML → Canvas Resize Policy | Adaptive 권장 |
| Progressive Web App | 오프라인 실행/홈 화면 추가 |
Thread Support — 반드시 이해할 것#
웹에서 멀티스레드를 쓰려면 SharedArrayBuffer가 필요합니다.
그런데 보안 취약점(Spectre) 이후, 브라우저는 이걸 쓰려면 서버가 특별한 헤더를
보내도록 요구합니다.
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp문제 — itch.io, GitHub Pages 같은 정적 호스팅은 이 헤더를 설정할 수 없거나 번거롭습니다. 헤더가 없으면 게임이 아예 안 켜집니다.
A) Thread Support 끄기 (권장 — 처음이라면)
- 어디에나 올릴 수 있음. GitHub Pages, itch.io, 개인 서버 전부 OK
- 단일 스레드로 동작. 대부분의 2D 게임은 문제없음
- 로딩이 조금 느리고, 무거운 연산 시 화면이 멈출 수 있음
B) Thread Support 켜기
- 성능이 좋음
- 헤더를 설정할 수 있는 서버가 필요
- itch.io는 프로젝트 설정에 "SharedArrayBuffer 지원" 체크박스가 있음
nginx로 직접 호스팅한다면 이렇게 설정합니다.
location / {
add_header Cross-Origin-Opener-Policy same-origin;
add_header Cross-Origin-Embedder-Policy require-corp;
# .wasm 을 올바른 MIME 으로 (안 그러면 느린 경로로 로드됨)
types { application/wasm wasm; }
gzip_static on;
}4) 내보내기#
프로젝트 → 내보내기 → Web선택- 프로젝트 내보내기 버튼
- 빈 폴더를 만들고 파일 이름을
index.html로 지정 (대부분의 호스팅이index.html을 기본 페이지로 찾습니다) Export With Debug체크는 해제 (배포용)
5) 로컬에서 테스트#
file:// 로 열면 브라우저 보안 정책 때문에 동작하지 않습니다.
반드시 HTTP 서버로 띄워야 합니다.
Godot에 내장 기능이 있습니다.
에디터 → 원격 디버그 → 브라우저에서 실행 또는 익스포트 대화상자의 재생 버튼.
직접 띄우려면:
# Python
python3 -m http.server 8080
# Node
npx serve .그리고 http://localhost:8080 접속.
6) 배포하기#
| 서비스 | 특징 |
|---|---|
| itch.io | 게임 전용. HTML5 업로드 지원. 무료. 커뮤니티 있음 |
| GitHub Pages | 무료, 정적. 리포지토리에 올리면 끝 |
| Netlify / Vercel | 무료, 헤더 설정 가능 (_headers 파일) |
| 개인 서버 (nginx) | 완전한 제어. COOP/COEP 자유롭게 |
itch.io 배포 순서
- 파일들을 zip으로 압축 (
index.html이 zip 최상위에 있어야 함) - 새 프로젝트 → Kind of project: HTML
- zip 업로드 → "This file will be played in the browser" 체크
- 뷰포트 크기 지정, "Fullscreen button" 켜기
- 스레드를 켰다면 "SharedArrayBuffer support" 체크
웹 빌드의 제약#
| 항목 | 웹에서 |
|---|---|
| 파일 저장 | user:// 가 브라우저 IndexedDB. 사이트 데이터 삭제 시 사라짐 |
| 소리 | 사용자가 한 번 클릭/터치해야 재생 시작 (브라우저 정책) |
| 창 크기 | 캔버스 크기. 전체화면은 사용자 제스처 필요 |
| 멀티플레이어 | ENet(UDP) 안 됨. WebSocket 또는 WebRTC를 써야 함 |
| 스레드 | 헤더 없으면 단일 스레드 |
| 로딩 | 첫 방문에 20~40MB 다운로드 |
| 마우스 잠금 | 사용자 제스처 후에만 |
브라우저는 사용자 조작 없이 소리를 내는 것을 금지합니다. 게임을 열자마자 BGM이 나오게 만들면 조용합니다.
해결 — 시작 화면에 "클릭해서 시작" 버튼을 두세요. 그 클릭이 오디오 권한을 열어줍니다.
func _on_start_pressed() -> void:
# 이 시점에 오디오가 활성화된다
Audio.play_bgm("title")
SceneManager.goto("res://scenes/stage.tscn")22강에서 쓴 ENetMultiplayerPeer는 UDP 기반이라 브라우저에서 못 씁니다.
웹에서 멀티플레이어를 하려면:
WebSocketMultiplayerPeer— TCP 기반. 간단하지만 지연에 불리WebRTCMultiplayerPeer— UDP 유사. 성능은 좋지만 시그널링 서버가 필요
고수준 API(@rpc, MultiplayerSynchronizer)는 그대로 쓸 수 있습니다.
peer 클래스만 바꾸면 됩니다.
용량 줄이기#
첫 로딩이 40MB면 모바일에서 이탈합니다. 줄이는 방법들:
1) gzip / brotli 압축
.wasm은 압축하면 3~4배 줄어듭니다(40MB → 10MB).
서버에서 gzip_static on 또는 Content-Encoding: br 설정.
2) 안 쓰는 기능 빼기 익스포트 설정의 Features 에서 필요 없는 모듈을 끕니다. (3D를 안 쓰면 3D 관련 모듈 제거 — 커스텀 빌드가 필요할 수 있음)
3) 텍스처 최적화
- 큰 이미지를 적절한 크기로 줄이기
- 불필요하게 큰 오디오 파일을
.ogg로 변환
4) 폰트 서브셋 한글 폰트 전체는 5~15MB입니다. 실제 쓰는 글자만 담으면 수백 KB로 줄어듭니다.
모바일 브라우저 대응#
15강에서 정한 stretch 설정(canvas_items + expand)이 여기서 빛을 발합니다.
접었을 때와 폈을 때 화면 비율이 크게 달라져도 대응됩니다.
추가로 확인할 것
- 터치 입력 —
프로젝트 설정 → Input Devices → Pointing → Emulate Mouse From Touch또는 가상 조이스틱 (7강) - 성능 — 모바일 GPU는 약합니다. 파티클과 반투명을 줄이세요 (6강 오버드로)
- 주사율 — 폴드는 120Hz입니다. 물리 보간을 켜세요 (8강)
- 화면 회전 —
Display → Window → Handheld → Orientation - 주소창 — 스크롤로 주소창이 숨겨지며 화면 크기가 바뀝니다.
get_tree().root.size_changed로 대응하세요 (18강)
- 지금까지 만든 게임을 웹으로 내보내세요.
- 로컬 HTTP 서버로 띄워 PC 브라우저에서 확인하세요.
- 같은 와이파이에서 폰으로 PC의 IP:8080 에 접속해 플레이하세요.
- 폴드라면 접었다 펴면서 화면이 깨지지 않는지 확인하세요.
- itch.io나 GitHub Pages에 올리고 친구에게 링크를 보내세요.
웹 빌드 디버깅
게임이 안 켜지면 브라우저 개발자 도구(F12) → 콘솔을 보세요.
| 증상 | 원인 |
|---|---|
SharedArrayBuffer is not defined |
스레드를 켰는데 COOP/COEP 헤더가 없음 |
| 검은 화면, 오류 없음 | 렌더러가 Compatibility가 아님 |
Failed to fetch .pck |
파일 경로 문제, 또는 file:// 로 열었음 |
| 소리 없음 | 사용자 제스처 전에 재생 시도 |
| 매우 느림 | 디버그 빌드로 내보냄, 또는 압축 미적용 |
.wasm 로딩이 느림 |
MIME 타입이 application/wasm이 아님 |
- 개발자 — CI로 자동 배포를 걸어두면 좋습니다. 커밋할 때마다 자동으로 빌드해서 테스트 링크가 갱신되면 팀 피드백 속도가 달라집니다.
- 기획자 — 웹 빌드는 테스트 참여 장벽을 없앱니다. "링크 눌러서 5분만 해보고 의견 주세요"가 가능해집니다. 플레이 테스트 계획을 세울 때 이 점을 활용하세요.
- 디자이너 — 웹은 첫 로딩 화면이 곧 첫인상입니다. Godot 기본 로딩 화면 대신 커스텀 HTML 셸로 브랜딩된 로딩 화면을 만들 수 있습니다. 또 용량이 로딩 시간에 직결되므로 에셋 최적화가 실제 이탈률에 영향을 줍니다.
"내보내기 버튼이 회색이다" → 익스포트 템플릿이 설치되지 않았습니다.
"브라우저에서 검은 화면만 나온다" → ① 렌더러가 Compatibility가 아님, ② 콘솔에서 오류 확인.
"로컬에서는 되는데 서버에 올리면 안 된다"
→ ① 파일 경로 대소문자(리눅스 서버는 구분함), ② .wasm MIME 타입,
③ COOP/COEP 헤더.
"itch.io에서 화면이 잘린다" → 프로젝트 설정의 뷰포트 크기를 게임 해상도와 맞추고 전체화면 버튼을 켜세요.
- 렌더러가
gl_compatibility다 - 웹으로 내보내 로컬 서버에서 실행했다
- Thread Support 옵션의 의미를 안다
- 모바일 브라우저에서 플레이했다
- 공유 가능한 링크가 있다
강좌를 마치며#
26강을 지나오며 이런 것들을 익혔습니다.
P1 — 뼈대 프레임과 delta, 좌표계, 그리기 순서. 어떤 엔진으로 옮겨도 그대로 통하는 개념입니다.
P2~P3 — 규칙 조작감의 수치화, 충돌 레이어 설계, 시그널 기반 구조.
P4~P5 — 완성도 카메라와 해상도, 상태 머신, 게임필, 데이터 주도 설계.
P6 — 확장 권위, 동기화, 예측과 보정, 결정론.
작은 게임 하나를 끝까지 완성하세요. 배운 것을 다 넣으려 하지 말고, 완성을 목표로 하세요. 미완성 프로토타입 10개보다 완성작 1개가 훨씬 많이 가르쳐 줍니다.
다른 사람에게 플레이시켜 보세요. 26강에서 만든 링크를 쓰면 됩니다. 설명 없이 5분 플레이시키면, 여러분이 놓친 것이 전부 드러납니다.
캡스톤으로 넘어가세요. TBH 모작 트랙에서 이 부품들을 조립합니다.
수고하셨습니다.