Godot로 만든 게임을 웹으로 내보내면 wasm 파일 하나가 40MB 가까이 나옵니다. 그런데 Cloudflare에 정적 파일로 올릴 때는 파일 하나가 25MiB를 넘을 수 없습니다. 배포 명령을 실행하고 한참 기다린 끝에 거절당합니다.

이 글은 그 상황에서 쓴 방법입니다. 게임 다섯 개를 같은 구조로 올려 두었습니다.

방법: 잘라 올리고 워커가 이어 붙인다

원리는 단순합니다.

  1. 큰 파일을 20MB씩 조각으로 자른다 (index.wasm.part0, part1, …)
  2. 조각들을 정적 자산으로 올린다 (각각 25MiB 미만이라 통과)
  3. 브라우저가 index.wasm을 요청하면 워커가 조각을 순서대로 읽어 하나의 응답으로 이어 붙여 내보낸다

브라우저 입장에서는 원본 파일 하나를 받은 것과 같습니다. Godot의 로더도 그대로 씁니다.

자를 때 확인할 것

자르는 코드보다 자른 뒤 확인이 중요합니다.

  • 조각이 전부 한도 아래인지 스크립트가 직접 확인하게 하세요. 안 그러면 배포 단계에서야 거절당하는데, 그때는 이미 몇 분을 기다린 뒤입니다
  • 조각 개수와 각 크기를 기록해 두세요. 워커가 몇 개를 이어 붙여야 하는지 알아야 합니다
  • pck 파일도 같은 처리가 필요할 수 있습니다. 게임 자산이 커지면 pck가 먼저 한도를 넘습니다

반드시 지켜야 하는 헤더

이어 붙이는 것만으로는 안 됩니다. wasm은 헤더가 틀리면 조용히 느려지거나 아예 안 뜹니다.

  • Content-Type: application/wasm — 이게 아니면 브라우저가 스트리밍 컴파일을 포기하고 전부 받은 뒤에 컴파일합니다. 로딩이 눈에 띄게 느려집니다
  • 미리 압축해서 올렸다면 Content-Encoding: gzip을 붙이고, 길이 계산을 압축된 크기 기준으로 하세요. 여기를 원본 크기로 두면 응답이 중간에 끊깁니다
  • 캐시는 파일 이름에 내용 해시가 들어가는 경우에만 길게 잡으세요. 이름이 고정인데 캐시를 1년으로 걸면 다음 배포가 사용자에게 도달하지 않습니다

마지막 항목으로 한 번 데었습니다. 법적 문서를 정적 자산으로 올렸더니 1년 immutable로 캐시돼서 문서를 고쳐도 안 바뀌었습니다. 자주 고칠 것은 워커가 직접 응답하게 두는 편이 낫습니다.

스레드를 쓸지 정하기

Godot 웹 빌드는 스레드를 쓰는 쪽이 빠르지만, 그러려면 서버가 두 헤더를 보내야 합니다.

Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp

이 헤더가 붙으면 그 페이지 안에서 외부 자원을 불러오는 것이 까다로워집니다. 호스팅이 이 헤더를 못 넣는 곳(예: 일부 정적 호스팅)이라면 단일 스레드로 내보내야 합니다. 어느 쪽이든 내보내기 설정과 서버 헤더가 짝이 맞아야 하고, 안 맞으면 로딩 도중에 멈춥니다.

배포 뒤 확인은 눈이 아니라 해시로

"게임이 뜬다"는 새 빌드가 올라갔다는 증거가 아닙니다. 캐시된 옛 빌드일 수 있습니다. 저는 이렇게 확인합니다.

curl -sS https://example.com/game/ | md5sum
md5sum build/deploy/static/game/index.html

로컬 산출물과 라이브 응답의 해시가 같은지 봅니다. 같으면 확실히 올라간 것이고, 다르면 아직 옛 버전이 나가고 있는 것입니다. 실제로 이 확인 덕분에 "배포했다"고 잘못 보고할 뻔한 것을 여러 번 잡았습니다.

회사망에서 배포한다면

Cloudflare 배포 도구는 Node로 돌기 때문에 사내 TLS 검사 프록시에 막힙니다. 그 명령에만 검증을 끄는 방법은 회사망 TLS 프록시 글에 적어 두었습니다.