본문 바로가기

블로그 / 기술

Godot 4 웹 export로 브라우저 게임을 낼 때 부딪힌 문제들

Salmonholic을 Godot 4.7.2 웹 빌드로 내보내며 겪은 세로 화면 10% 렌더링, 39.5MB wasm, 카카오톡 인앱 브라우저의 보안 컨텍스트, 오디오 재개 오류를 실제 측정값과 함께 정리했다.

Salmonholic ·· 7분 읽기

데스크톱 브라우저의 Salmonholic 경기 화면 위에, 세로 폰에서 게임이 화면 가운데 작은 상자로만 보이던 점검 스크린샷을 겹쳐 놓은 이미지

Salmonholic은 Godot 4.7.2로 만들고 웹으로 내보낸 브라우저 게임이다. 에디터에서 잘 돌던 게임이 브라우저에 올라가면 문제가 생긴다. 엔진이 해 주는 일과 브라우저가 막는 일 사이에서 생기는 문제들이다. 출시 전후 일주일 동안 그런 문제를 하나씩 만났고, 매번 점검 문서를 남겼다. 이 글은 그 문서들을 다시 읽으며 정리한 기록이다. 당시에는 작업명이 “Survivor Royale”였어서 이 글의 일부 스크린샷에는 그 이름이 남아 있다.

세 브라우저 매트릭스로 처음 점검했다

9월 21일, 라이브 빌드(v0.7.0)를 Playwright로 Chromium, WebKit, Firefox 세 엔진에서 돌렸다. 데스크톱 한 가지와 폰 세로·가로 두 가지씩, 모두 9개 구성이었다. 흐름은 로드, 타이틀, 로비, 실제 온라인 경기, 20초 동안 누르고 이동하기까지였다.

데스크톱은 세 브라우저 모두 통과했다. WebGL 2 컨텍스트도 정상이었고 콘솔 오류는 0건이었다. 그런데 두 가지가 눈에 띄었다.

구성 엔진 시작까지 게임이 차지한 화면 비율
Chromium 데스크톱 2.81초 100%
WebKit iPhone 14 세로 2.34초 10%
Firefox 데스크톱 5.82초 100%
Chromium Pixel 7 가로 2.70초 43%

첫째, Firefox만 엔진 시작이 2.5초 정도 느렸다. 콘솔을 보면 엔진 배너가 찍히고 렌더러가 올라오기까지 3.3초가 걸렸다. 압축을 풀면 39.5MB인 wasm을 컴파일하는 시간이었다. 둘째가 더 심각했다. 세로로 든 폰에서는 게임이 화면의 10%만 차지했다.

세로 폰에서 게임 화면이 가운데 작은 상자로만 그려진 점검 스크린샷 Pixel 7 세로 구성의 경기 화면. 게임이 243×137 CSS px 상자에 갇혔다.

원인은 프로젝트 설정이었다. 픽셀아트가 뭉개지지 않게 640×360 화면을 정수 배율로만 키우도록 해 두었다(scale_mode="integer"). 세로 폰의 캔버스 폭은 1081 device px라서 640으로 나누면 1.69가 나오고, 정수 배율은 1로 떨어진다. PLAY 버튼은 76×17 px, 설정 톱니는 40×10 px가 됐다. 탭은 들어가지만 읽을 수는 없는 크기였다. 데스크톱에서 픽셀을 지키려고 한 설정이 폰에서는 화면을 버리고 있었다. 이 문제는 나중에 세로 플레이를 따로 지원하면서 풀었고, 그 과정은 모바일 세로 화면과 터치 대응기에 따로 적었다.

wasm 39.5MB를 20.9MB로 줄인 slim 템플릿

Firefox가 느린 것도, 포털에 올릴 zip이 큰 것도 결국 wasm 크기 문제였다. Godot 공식 웹 템플릿에는 이 게임이 쓰지 않는 3D, 물리, 내비게이션, XR, 고급 텍스트 서버가 다 들어 있다. 9월 22일에 Godot 4.7.2 소스를 받아 기능을 끈 커스텀 템플릿을 빌드했다. 남긴 모듈은 gdscript, websocket, text_server_fb, freetype, webp, svg 여섯 개다.

파일 기본 템플릿 slim 템플릿 변화
index.wasm 원본 39.5MB 20.9MB -47%
index.wasm brotli 6.90MB 3.51MB -49%
포털 zip 11.87MB 6.91MB -41.7%
첫 프레임(10Mbps, 비압축) 37.1초 22.1초 -15.0초

삽질도 있었다. 빌드 설정 파일에 target을 적었는데 무시되고 디버그 템플릿이 나왔다. Godot의 웹 빌드 스크립트가 이 값을 강제로 덮어쓰고, 명령줄 인자로만 바꿀 수 있었다. 문서에 없는 함정이었다. svg 모듈은 원래 끄려고 했다. 그런데 기본 테마의 체크박스 토글 아이콘이 실행 중에 SVG로 그려진다는 걸 알고 남겼다. webp도 가져온 텍스처가 내부적으로 WebP를 담고 있어서 남겼다.

slim 템플릿으로 내보낸 초기 타이틀 화면 slim 템플릿 검증 때 찍은 타이틀 화면. 버튼과 글자가 기본 템플릿과 똑같이 그려지는지 비교했다.

대가도 있었다. 대체 텍스트 서버(text_server_fb)는 라틴·그리스·키릴 문자만 제대로 다룬다. 나중에 한국어를 넣을 때 이 제약이 다시 문제가 됐고, 그 이야기는 한국어 포함 9개 언어 현지화 기록에 있다. 이 작업은 9월 23일 커밋 86c8d83(“slim Godot web template build”)으로 저장소에 들어갔다.

카카오톡 인앱 브라우저에서 “WebGL 2가 없다”는 오류

9월 24일, 아이폰의 카카오톡 인앱 브라우저로 링크를 열었더니 “브라우저에 WebGL 2 / WebAssembly가 없다”는 오류가 떴다. 아이폰 Safari 엔진이 WebGL 2를 못 할 리는 없었다. 원인은 페이지가 https가 아니라 http로 열렸다는 데 있었다. 서버가 http 요청에도 리다이렉트 없이 200을 돌려주고 있었다. Godot 웹 로더는 시작 전에 필요한 기능을 검사하는데, 이 검사가 “Secure Context 없음”을 보고한 것이다. 오류 문구만 보면 그래픽 문제 같지만 실제로는 보안 컨텍스트 문제였다.

텔레메트리를 찾아보니 같은 부팅 오류가 이미 7건 있었다. 그날 커밋 6fe0575의 메시지는 “redirect http to https (Godot needs a secure context)“다. 서버가 http를 https로 301 리다이렉트하도록 여섯 줄을 더해 해결했다. 게임 코드는 한 줄도 바꾸지 않았다.

다운로드와 SDK 초기화를 겹치고, 오디오 거부를 처리했다

CrazyGames 버전에서는 포털 SDK가 준비될 때까지 기다린 뒤에야 엔진을 받기 시작했다. 느린 모바일 회선에서는 이 대기 시간이 그대로 로딩 시간에 더해졌다. 9월 26일의 0.15.2(커밋 488a48a)에서 셸을 고쳤다. SDK 준비를 기다리는 동안 Engine.load로 wasm을 먼저 받아 두고, 게임 실행과 저장 접근만 SDK 준비 뒤로 미뤘다. SDK가 2.5초 늦게 준비되는 조건에서 느린 모바일의 첫 입력 시각이 42.64초에서 40.08초로 당겨졌다. 20초 목표에는 한참 못 미쳤다. 다운로드 자체가 병목이었기 때문이다. 그래서 다음 버전에서는 추가 음악을 별도 music.pck로 떼어 경기 시작 뒤에 받게 했다. 초기 전송량이 5.55MB에서 5.04MB로 줄었다(커밋 996ff43).

같은 날 저녁에는 오디오 문제를 고쳤다. 브라우저는 사용자 입력 전에 소리가 나는 것을 막는다. Godot 템플릿은 AudioContext.resume()을 호출하고 반환된 Promise를 처리하지 않았다. 정책 때문에 재개가 거부되면 NotAllowedError가 처리되지 않은 오류로 남았다. 테스트에서 이런 오류가 한 세션에 2건씩 재현됐다. 커밋 dd0d15e(0.16.2)에서 셸의 resume 래퍼가 NotAllowedError, AbortError, 닫힌 컨텍스트의 InvalidStateError만 삼키고 다음 입력 때 다시 시도하게 했다. 모르는 오류까지 숨기면 진짜 버그를 놓치게 된다. 그래서 전역 오류 처리는 건드리지 않았다. 수정 후 예상 오디오 오류는 데스크톱과 모바일 모두 2건에서 0건이 됐다.

정리하면

  • 픽셀아트용 정수 배율은 데스크톱에는 맞고 폰에는 맞지 않는다. 기기별로 배율 정책을 나눠야 한다.
  • 웹 빌드의 크기는 엔진 템플릿이 거의 다 차지한다. 쓰지 않는 모듈을 끈 커스텀 템플릿 하나로 전송량이 절반이 됐다.
  • “WebGL이 없다”는 오류가 떠도 원인이 WebGL이 아닐 수 있다. https인지부터 확인한다.
  • 브라우저의 오디오 정책 거부는 정상 동작이다. 거부된 Promise를 처리할 곳은 오디오를 재개하는 코드 바로 옆이다.

이 문제들을 거친 빌드가 지금 Salmonholic 게임 페이지에서 돌아가는 버전이다. 포털마다 다른 SDK 요구 사항은 웹 게임 포털 대응기에 이어서 정리했다.

#Godot#웹 export#WebAssembly#브라우저 호환성#최적화

함께 읽으면 좋은 글

전체 글 →