🤖

본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.

⚠️

본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.

이미지 로딩 중...

Phaser 게임 빌드와 배포 완벽 가이드 - 슬라이드 1/7
A

AI Generated

2025. 12. 5. · 12 Views

Phaser 게임 빌드와 배포 완벽 가이드

Phaser로 만든 게임을 프로덕션 빌드하고, 에셋을 최적화하여 GitHub Pages, itch.io에 배포하는 방법을 알아봅니다. 모바일 대응과 PWA 변환까지 게임 출시의 모든 과정을 다룹니다.


목차

  1. 프로덕션_빌드
  2. 에셋_최적화
  3. GitHub_Pages_배포
  4. itch.io_배포
  5. 모바일_대응
  6. PWA로_만들기

1. 프로덕션 빌드

김개발 씨는 드디어 자신의 첫 번째 Phaser 게임을 완성했습니다. 로컬에서 테스트할 때는 완벽하게 작동했죠.

그런데 막상 친구에게 보여주려고 하니 막막했습니다. "이걸 어떻게 인터넷에 올리지?"

프로덕션 빌드란 개발 중에 작성한 코드를 실제 배포할 수 있는 형태로 변환하는 과정입니다. 마치 원고를 책으로 인쇄하기 전에 교정과 편집을 거치는 것과 같습니다.

이 과정을 통해 파일 크기가 줄어들고, 로딩 속도가 빨라지며, 코드가 보호됩니다.

다음 코드를 살펴봅시다.

// vite.config.js - Phaser 게임용 프로덕션 빌드 설정
import { defineConfig } from 'vite';

export default defineConfig({
  base: './',  // 상대 경로로 설정 (배포 환경에 따라 조정)
  build: {
    outDir: 'dist',           // 빌드 결과물 폴더
    assetsDir: 'assets',      // 에셋 파일 폴더
    minify: 'terser',         // 코드 압축 방식
    terserOptions: {
      compress: {
        drop_console: true,   // console.log 제거
        drop_debugger: true   // debugger 문 제거
      }
    },
    rollupOptions: {
      output: {
        manualChunks: {
          phaser: ['phaser']  // Phaser를 별도 청크로 분리
        }
      }
    }
  }
});

김개발 씨는 입사 6개월 차 주니어 개발자입니다. 퇴근 후 틈틈이 만들던 Phaser 게임이 드디어 완성되었습니다.

로컬 개발 서버에서 실행하면 완벽하게 동작합니다. 점프도 잘 되고, 적도 잘 쓰러지고, 점수도 제대로 올라갑니다.

그런데 문제가 생겼습니다. 친구에게 게임을 자랑하고 싶은데, localhost:5173 주소를 공유할 수는 없는 노릇입니다.

이 주소는 김개발 씨의 컴퓨터에서만 접속할 수 있으니까요. 선배 개발자 박시니어 씨에게 조심스럽게 물어봤습니다.

"선배, 제가 만든 게임을 인터넷에 올리려면 어떻게 해야 하나요?" 박시니어 씨가 웃으며 대답했습니다. "먼저 프로덕션 빌드를 해야 해요.

지금 개발 중인 코드는 말하자면 초안이에요. 출판하기 전에 편집을 거쳐야 하듯이, 코드도 배포하기 전에 빌드 과정을 거쳐야 합니다." 그렇다면 프로덕션 빌드란 정확히 무엇일까요?

쉽게 비유하자면, 프로덕션 빌드는 마치 이사할 때 짐을 정리하는 것과 같습니다. 개발 중에는 여기저기 파일이 흩어져 있고, 디버깅용 코드도 섞여 있습니다.

이걸 그대로 배포하면 용량도 크고, 불필요한 정보가 노출될 수도 있습니다. 빌드 과정은 이런 것들을 깔끔하게 정리해서 박스에 담는 작업입니다.

프로덕션 빌드가 없던 시절에는 어땠을까요? 개발자들은 수동으로 파일을 압축하고, 불필요한 코드를 일일이 삭제해야 했습니다.

실수로 디버그 코드를 남겨두면 사용자에게 민감한 정보가 노출되기도 했습니다. 더 큰 문제는 여러 파일을 올바른 순서로 합치는 것이었습니다.

프로젝트가 커질수록 이 작업은 악몽이 되었습니다. 바로 이런 문제를 해결하기 위해 Vite, Webpack 같은 빌드 도구가 등장했습니다.

Vite를 사용하면 단 한 줄의 명령어로 최적화된 빌드를 생성할 수 있습니다. npm run build 또는 pnpm build를 실행하면 됩니다.

빌드 도구가 알아서 코드를 압축하고, 파일을 합치고, 불필요한 부분을 제거해줍니다. 위의 설정 코드를 한 줄씩 살펴보겠습니다.

먼저 base: './' 설정을 보면, 이는 빌드된 파일들이 상대 경로를 사용하도록 합니다. GitHub Pages나 itch.io처럼 루트 경로가 아닌 곳에 배포할 때 필수적인 설정입니다.

outDir: 'dist'는 빌드 결과물이 저장될 폴더를 지정합니다. minify: 'terser'terserOptions 부분이 핵심입니다.

Terser는 JavaScript 코드를 압축하는 도구입니다. 변수명을 짧게 바꾸고, 공백을 제거하고, console.log까지 자동으로 삭제해줍니다.

덕분에 파일 크기가 크게 줄어듭니다. manualChunks 설정은 Phaser 라이브러리를 별도 파일로 분리합니다.

Phaser는 용량이 큰 라이브러리입니다. 별도로 분리하면 브라우저가 이를 캐시해두고, 다음 방문 때 다시 다운로드하지 않아도 됩니다.

실제 현업에서는 어떻게 활용할까요? 게임 회사에서 신작을 출시한다고 가정해봅시다.

개발팀은 수개월간 열심히 코드를 작성합니다. 출시일이 다가오면 QA팀의 테스트를 거치고, 최종적으로 프로덕션 빌드를 생성합니다.

이 빌드된 파일들이 실제로 서버에 올라가 전 세계 플레이어들에게 서비스됩니다. 하지만 주의할 점도 있습니다.

초보 개발자들이 흔히 하는 실수 중 하나는 빌드 후 테스트를 하지 않는 것입니다. 개발 모드에서는 잘 되던 것이 빌드 후에는 안 되는 경우가 종종 있습니다.

특히 경로 문제가 많이 발생합니다. 따라서 빌드 후에는 반드시 npx serve dist 같은 명령으로 로컬에서 테스트해보아야 합니다.

다시 김개발 씨의 이야기로 돌아가 봅시다. 박시니어 씨의 설명을 들은 김개발 씨는 바로 터미널을 열었습니다.

pnpm build를 입력하자, 순식간에 dist 폴더가 생성되었습니다. "와, 파일 크기가 엄청 줄었네요!" 김개발 씨가 감탄했습니다.

원래 수십 개였던 파일이 몇 개로 압축되었고, 전체 용량도 절반 이하로 줄어들었습니다. 이제 다음 단계인 에셋 최적화를 배울 준비가 되었습니다.

실전 팁

💡 - 빌드 전에 반드시 base 경로를 배포 환경에 맞게 설정하세요

  • 빌드 후에는 항상 로컬에서 테스트하여 경로 문제가 없는지 확인하세요
  • console.log는 프로덕션에서 자동 제거되므로 개발 중에는 자유롭게 사용해도 됩니다

2. 에셋 최적화

김개발 씨가 만든 게임의 빌드는 성공했지만, 용량이 50MB나 되었습니다. 친구가 모바일로 접속했더니 로딩만 30초가 걸렸습니다.

"게임 시작하기도 전에 나갔어..." 친구의 메시지에 김개발 씨는 한숨을 쉬었습니다.

에셋 최적화란 게임에 사용되는 이미지, 오디오, 폰트 등의 파일 크기를 줄이는 작업입니다. 마치 여행 가방을 쌀 때 옷을 압축팩에 넣어 부피를 줄이는 것과 같습니다.

품질은 최대한 유지하면서 용량을 줄여야 합니다.

다음 코드를 살펴봅시다.

// 이미지 최적화를 위한 스프라이트 시트 로딩
class BootScene extends Phaser.Scene {
  preload() {
    // 개별 이미지 대신 스프라이트 시트 사용
    this.load.spritesheet('player', 'assets/player-sheet.png', {
      frameWidth: 32,
      frameHeight: 32
    });

    // 텍스처 아틀라스 사용 (여러 이미지를 하나로)
    this.load.atlas(
      'gameSprites',
      'assets/sprites.png',      // 하나로 합친 이미지
      'assets/sprites.json'      // 좌표 정보
    );

    // 오디오는 웹 친화적 포맷 사용
    this.load.audio('bgm', ['assets/bgm.ogg', 'assets/bgm.mp3']);

    // WebP 포맷 지원 확인 후 로딩
    const format = this.sys.game.device.browser.webp ? 'webp' : 'png';
    this.load.image('background', `assets/bg.${format}`);
  }
}

김개발 씨는 빌드된 게임을 친구에게 공유했습니다. 하지만 친구의 반응은 예상과 달랐습니다.

"야, 로딩이 너무 오래 걸려서 그냥 껐어." 모바일 데이터로 50MB를 다운로드하려면 시간이 꽤 걸리니까요. 박시니어 씨가 김개발 씨의 게임 폴더를 살펴봤습니다.

"아, 이미지를 PNG로 그대로 쓰셨네요. 그것도 개별 파일로요.

에셋 최적화가 필요해요." 그렇다면 에셋 최적화란 정확히 무엇일까요? 쉽게 비유하자면, 에셋 최적화는 마치 여행 가방 싸기와 같습니다.

옷을 그냥 넣으면 금방 가방이 꽉 찹니다. 하지만 압축팩을 사용하고, 돌돌 말아서 넣으면 같은 양의 옷도 훨씬 작은 공간에 들어갑니다.

게임 에셋도 마찬가지입니다. 똑같은 품질을 유지하면서도 파일 크기를 크게 줄일 수 있습니다.

게임 에셋에서 가장 큰 비중을 차지하는 것은 이미지입니다. 특히 스프라이트 애니메이션에 사용되는 이미지들이 문제입니다.

캐릭터가 걷는 애니메이션에만 8장, 뛰는 애니메이션에 8장, 점프에 4장... 이렇게 개별 파일로 관리하면 HTTP 요청도 많아지고 총 용량도 커집니다.

바로 이런 문제를 해결하는 것이 스프라이트 시트입니다. 스프라이트 시트는 여러 개의 작은 이미지를 하나의 큰 이미지에 배치한 것입니다.

브라우저는 파일 하나만 다운로드하면 되고, Phaser가 필요한 부분만 잘라서 사용합니다. 20개의 이미지 파일이 1개로 줄어드니 로딩 속도가 확연히 빨라집니다.

텍스처 아틀라스는 스프라이트 시트의 발전된 형태입니다. 스프라이트 시트는 모든 프레임이 같은 크기여야 하지만, 텍스처 아틀라스는 다양한 크기의 이미지를 효율적으로 배치합니다.

JSON 파일에 각 이미지의 좌표와 크기 정보가 저장되어 있습니다. 위의 코드를 살펴보면, this.load.atlas()가 텍스처 아틀라스를 로딩하는 부분입니다.

하나의 PNG 파일과 하나의 JSON 파일만 로드하면, 수십 개의 스프라이트를 사용할 수 있습니다. 이미지 포맷 선택도 중요합니다.

WebP 포맷은 PNG보다 약 30% 작으면서 품질은 동일합니다. 대부분의 최신 브라우저가 WebP를 지원합니다.

코드에서 보듯이, WebP 지원 여부를 확인하고 조건부로 로딩할 수 있습니다. 오디오 파일도 최적화가 필요합니다.

WAV 파일은 무압축이라 용량이 큽니다. 대신 OGG 포맷을 사용하면 음질은 유지하면서 용량을 크게 줄일 수 있습니다.

Safari는 OGG를 지원하지 않으므로, MP3를 대체 포맷으로 제공하는 것이 좋습니다. 실제로 텍스처 아틀라스를 만들려면 도구가 필요합니다.

TexturePacker나 무료 도구인 Free Texture Packer를 사용하면 됩니다. 이미지 파일들을 끌어다 놓으면 자동으로 최적 배치해서 아틀라스를 생성해줍니다.

주의할 점이 있습니다. 너무 과도한 압축은 화질 저하로 이어집니다.

특히 JPEG 압축률을 높이면 계단 현상이 생깁니다. 게임 그래픽에서는 보통 품질 80% 정도가 적당합니다.

직접 눈으로 확인하면서 품질과 용량의 균형점을 찾아야 합니다. 김개발 씨는 박시니어 씨의 조언대로 에셋을 최적화했습니다.

개별 이미지 파일들을 텍스처 아틀라스로 합치고, PNG를 WebP로 변환하고, 오디오를 OGG로 변환했습니다. 결과는 놀라웠습니다.

50MB였던 게임이 12MB로 줄어들었습니다. 다시 친구에게 보냈더니 이번에는 3초 만에 로딩이 완료되었습니다.

"오, 이거 꽤 재밌는데?" 친구의 칭찬에 김개발 씨는 뿌듯한 미소를 지었습니다.

실전 팁

💡 - TexturePacker나 Free Texture Packer로 스프라이트 아틀라스를 생성하세요

  • 이미지는 WebP, 오디오는 OGG 포맷을 우선 사용하고 대체 포맷을 준비하세요
  • 최적화 전후 파일 크기를 비교하여 효과를 확인하세요

3. GitHub Pages 배포

에셋 최적화까지 완료한 김개발 씨. 이제 진짜로 세상에 게임을 공개할 차례입니다.

"무료로 웹 게임을 호스팅할 수 있는 곳이 있을까요?" 박시니어 씨가 대답했습니다. "GitHub Pages가 딱이에요.

무료인 데다가 설정도 간단합니다."

GitHub Pages는 GitHub에서 제공하는 무료 정적 웹 호스팅 서비스입니다. 마치 무료 전시 공간을 제공받아 자신의 작품을 전시하는 것과 같습니다.

저장소에 파일을 올리기만 하면 누구나 접속할 수 있는 웹사이트가 만들어집니다.

다음 코드를 살펴봅시다.

# 1. GitHub 저장소 생성 후 로컬에서 연결
git init
git remote add origin https://github.com/username/my-phaser-game.git

# 2. gh-pages 브랜치로 배포 (dist 폴더 내용)
# 먼저 gh-pages 패키지 설치
npm install gh-pages --save-dev

# package.json에 배포 스크립트 추가
# "scripts": {
#   "deploy": "gh-pages -d dist"
# }

# 3. 빌드 후 배포 실행
npm run build
npm run deploy

# 4. GitHub 저장소 Settings > Pages에서 확인
# 배포 URL: https://username.github.io/my-phaser-game/

김개발 씨는 최적화된 게임을 손에 쥐고 있었습니다. dist 폴더 안에 깔끔하게 정리된 파일들이 있습니다.

이제 이걸 어딘가에 올려서 URL을 만들어야 합니다. "유료 서버를 빌려야 하나요?" 김개발 씨가 걱정스럽게 물었습니다.

취미로 만든 게임인데 매달 호스팅 비용을 내기는 부담스럽습니다. 박시니어 씨가 고개를 저었습니다.

"아니요, GitHub Pages를 쓰면 완전 무료예요. 정적 파일 호스팅에는 이만한 게 없습니다." GitHub Pages란 무엇일까요?

쉽게 비유하자면, GitHub Pages는 마치 무료 갤러리와 같습니다. 화가가 그림을 그렸는데, 전시할 공간이 없다면 곤란합니다.

GitHub Pages는 "여기에 작품을 올려두세요, 사람들이 볼 수 있게 해드릴게요"라고 말하는 무료 갤러리입니다. GitHub는 원래 코드를 저장하고 관리하는 서비스입니다.

GitHub Pages는 여기에 추가로 제공되는 기능으로, 저장소에 올린 정적 파일을 웹사이트로 서비스해줍니다. HTML, CSS, JavaScript로 이루어진 Phaser 게임은 정적 파일이므로 완벽하게 맞습니다.

배포 과정을 단계별로 살펴보겠습니다. 먼저 GitHub에 새 저장소를 만들어야 합니다.

GitHub 웹사이트에서 "New repository"를 클릭하고, 저장소 이름을 입력합니다. 예를 들어 "my-phaser-game"이라고 지으면, 나중에 username.github.io/my-phaser-game 주소로 접속할 수 있게 됩니다.

로컬에서 git init으로 Git을 초기화하고, git remote add origin으로 원격 저장소를 연결합니다. 여기까지는 일반적인 Git 사용법과 동일합니다.

핵심은 gh-pages 패키지입니다. 이 패키지는 지정한 폴더의 내용을 gh-pages라는 특별한 브랜치에 자동으로 푸시해줍니다.

GitHub는 이 브랜치의 내용을 웹사이트로 서비스합니다. 복잡한 설정 없이 npm run deploy 한 줄이면 배포가 완료됩니다.

vite.config.js에서 base 설정을 확인해야 합니다. GitHub Pages는 username.github.io/저장소이름/ 형태의 URL을 사용하므로, base를 저장소 이름으로 설정해야 합니다.

예를 들어 base: '/my-phaser-game/'처럼요. 이걸 빼먹으면 에셋 경로가 맞지 않아 게임이 제대로 로딩되지 않습니다.

배포 후에는 GitHub 저장소의 Settings 탭에서 Pages 메뉴를 확인합니다. "Your site is published at..."이라는 메시지와 함께 URL이 표시됩니다.

이 URL을 친구들에게 공유하면 됩니다. 주의할 점이 있습니다.

GitHub Pages는 정적 파일만 서비스합니다. 서버 사이드 로직이 필요한 기능, 예를 들어 데이터베이스 연동이나 서버 API 호출은 별도의 백엔드 서버가 필요합니다.

하지만 대부분의 Phaser 게임은 클라이언트에서만 동작하므로 문제없습니다. 또 하나, GitHub Pages는 저장소당 1GB 용량 제한이 있습니다.

잘 최적화된 게임이라면 충분하지만, 고용량 에셋이 많다면 다른 방법을 고려해야 합니다. 김개발 씨는 설레는 마음으로 npm run deploy를 실행했습니다.

터미널에 "Published"라는 메시지가 떴습니다. 브라우저를 열고 URL을 입력하자, 자신이 만든 게임이 화면에 나타났습니다.

"세상에 내 게임이 공개됐다!" 김개발 씨는 SNS에 링크를 공유했습니다. 곧 친구들의 반응이 쏟아지기 시작했습니다.

무료로, 이렇게 간단하게 게임을 배포할 수 있다니 놀라웠습니다.

실전 팁

💡 - vite.config.js의 base 설정을 GitHub 저장소 이름과 일치시키세요

  • 배포 후 브라우저 캐시 때문에 이전 버전이 보일 수 있으니 강력 새로고침(Ctrl+Shift+R)을 하세요
  • HTTPS가 기본 제공되므로 보안 걱정 없이 공유할 수 있습니다

4. itch.io 배포

GitHub Pages 배포에 성공한 김개발 씨. 하지만 뭔가 아쉬웠습니다.

"게임 개발자들이 모인 커뮤니티에 올리고 싶어요. 피드백도 받고, 다른 개발자들과 교류도 하고 싶거든요." 박시니어 씨가 추천했습니다.

"그럼 itch.io에 올려보세요. 인디 게임 개발자들의 성지예요."

itch.io는 인디 게임 개발자들을 위한 배포 플랫폼입니다. 마치 핸드메이드 작품을 판매하는 플리마켓과 같습니다.

무료 게임도, 유료 게임도 올릴 수 있고, 게임을 좋아하는 사람들이 모여 있어 피드백을 받기 좋습니다.

다음 코드를 살펴봅시다.

// itch.io 배포를 위한 index.html 설정
<!DOCTYPE html>
<html lang="ko">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>My Phaser Game</title>
  <style>
    /* itch.io iframe에 맞게 크기 조절 */
    * { margin: 0; padding: 0; }
    html, body { width: 100%; height: 100%; overflow: hidden; }
    canvas { display: block; margin: 0 auto; }
  </style>
</head>
<body>
  <script type="module" src="./main.js"></script>
</body>
</html>

// Phaser config에서 크기 설정 (itch.io 권장)
const config = {
  type: Phaser.AUTO,
  width: 800,          // itch.io 기본 프레임 크기에 맞춤
  height: 600,
  scale: {
    mode: Phaser.Scale.FIT,      // 프레임에 맞게 자동 조절
    autoCenter: Phaser.Scale.CENTER_BOTH
  }
};

GitHub Pages에 배포한 게임에 몇몇 친구들이 플레이해주었습니다. 하지만 김개발 씨는 더 많은 사람들의 피드백을 원했습니다.

특히 게임을 만드는 다른 개발자들의 의견이 궁금했습니다. 박시니어 씨가 조언했습니다.

"itch.io에 올려보세요. 인디 게임 개발자들의 놀이터예요.

매주 수천 개의 게임이 올라오고, 게임잼 문화도 활발합니다." itch.io란 무엇일까요? 쉽게 비유하자면, itch.io는 마치 인디 음악가들을 위한 음원 플랫폼 같습니다.

메이저 음반사 없이도 자신의 음악을 공개하고, 팬을 모으고, 수익을 낼 수 있는 곳이죠. itch.io도 마찬가지입니다.

대형 퍼블리셔 없이도 자신의 게임을 세상에 공개할 수 있습니다. itch.io의 가장 큰 장점은 게임에 특화된 플랫폼이라는 점입니다.

GitHub Pages는 범용 웹 호스팅이지만, itch.io는 게임만을 위한 곳입니다. 게임 페이지에 스크린샷, 설명, 태그를 달 수 있고, 사용자들이 댓글로 피드백을 남길 수 있습니다.

배포 방법은 간단합니다. 먼저 itch.io에 계정을 만듭니다.

"Create new project"를 클릭하고, 게임 정보를 입력합니다. 제목, 설명, 태그, 스크린샷 등을 채웁니다.

이 정보가 게임 페이지에 표시되므로 정성껏 작성하는 것이 좋습니다. 핵심은 Kind of project를 "HTML"로 선택하는 것입니다.

Phaser 게임은 웹 브라우저에서 실행되는 HTML5 게임이기 때문입니다. 그러면 itch.io가 게임을 iframe 안에서 실행해줍니다.

빌드된 dist 폴더를 ZIP 파일로 압축해서 업로드합니다. 업로드한 후 "This file will be played in the browser" 체크박스를 선택합니다.

그러면 사용자가 다운로드 없이 바로 브라우저에서 플레이할 수 있습니다. 위의 코드에서 중요한 부분을 살펴보겠습니다.

CSS에서 margin: 0; padding: 0; overflow: hidden;은 필수입니다. itch.io는 게임을 iframe 안에 띄우는데, 기본 여백이 있으면 스크롤바가 생기거나 게임이 잘릴 수 있습니다.

Phaser 설정에서 Phaser.Scale.FIT은 게임 화면을 iframe 크기에 맞게 자동 조절합니다. itch.io에서 플레이어가 전체화면 버튼을 누르면 게임이 화면 전체로 확대되는데, 이 설정이 있어야 깨지지 않고 잘 확대됩니다.

itch.io만의 특별한 기능도 있습니다. 게임잼에 참여할 수 있습니다.

게임잼은 정해진 기간(보통 48시간~1주일) 동안 주제에 맞는 게임을 만드는 행사입니다. 전 세계 개발자들과 경쟁하고, 피드백을 주고받으며 실력을 키울 수 있습니다.

주의할 점도 있습니다. itch.io는 파일 크기 제한이 1GB입니다.

충분히 넉넉하지만, 고용량 에셋이 많다면 확인이 필요합니다. 또한 무료 게임이라도 "Name your own price"를 설정하면 후원을 받을 수 있습니다.

김개발 씨는 열심히 게임 페이지를 꾸몄습니다. 매력적인 스크린샷을 찍고, 게임 방법을 상세히 설명하고, 관련 태그를 달았습니다.

업로드 후 "Publish"를 누르는 순간, 가슴이 두근거렸습니다. 며칠 후, 알림이 왔습니다.

누군가 게임에 댓글을 남겼습니다. "Simple but fun!

I enjoyed the art style." 영어로 된 칭찬이었습니다. 전 세계 누군가가 자신의 게임을 즐기고 있다는 사실에 김개발 씨는 감격했습니다.

실전 팁

💡 - 매력적인 커버 이미지와 스크린샷이 클릭률을 크게 높입니다

  • 게임 설명에 조작법을 명확히 적어주세요 (WASD로 이동, Space로 점프 등)
  • 게임잼에 참여하면 단기간에 많은 피드백을 받을 수 있습니다

5. 모바일 대응

itch.io에 올린 게임이 좋은 반응을 얻고 있습니다. 그런데 한 댓글이 눈에 들어왔습니다.

"I tried to play on my phone but couldn't control the character." 김개발 씨는 깜짝 놀랐습니다. 모바일에서 플레이하는 사람이 있을 줄은 생각도 못했거든요.

모바일 대응이란 스마트폰이나 태블릿에서도 게임이 원활하게 작동하도록 만드는 것입니다. 마치 같은 옷을 다양한 체형에 맞게 조절하는 것과 같습니다.

화면 크기 대응과 터치 컨트롤 구현이 핵심입니다.

다음 코드를 살펴봅시다.

// 터치 컨트롤을 위한 가상 조이스틱 구현
class GameScene extends Phaser.Scene {
  create() {
    // 모바일 여부 확인
    this.isMobile = this.sys.game.device.input.touch;

    if (this.isMobile) {
      this.createTouchControls();
    }

    // 키보드 입력 (PC용)
    this.cursors = this.input.keyboard.createCursorKeys();
  }

  createTouchControls() {
    // 왼쪽 하단에 이동 버튼 영역
    this.joystickBase = this.add.circle(100, 500, 60, 0x888888, 0.5);
    this.joystickThumb = this.add.circle(100, 500, 30, 0xffffff, 0.8);
    this.joystickBase.setScrollFactor(0).setDepth(100);
    this.joystickThumb.setScrollFactor(0).setDepth(101);

    // 터치 입력 처리
    this.input.on('pointermove', (pointer) => {
      if (pointer.isDown && pointer.x < 200) {
        // 조이스틱 범위 내에서 움직임 계산
        const dx = pointer.x - this.joystickBase.x;
        const dy = pointer.y - this.joystickBase.y;
        this.touchVector = { x: dx / 60, y: dy / 60 };
      }
    });

    // 오른쪽에 점프 버튼
    this.jumpButton = this.add.circle(700, 500, 50, 0xff0000, 0.6)
      .setScrollFactor(0).setDepth(100).setInteractive();
    this.jumpButton.on('pointerdown', () => this.player.jump());
  }
}

김개발 씨는 당황했습니다. 키보드로 WASD를 누르면 캐릭터가 움직이도록 만들었는데, 스마트폰에는 키보드가 없습니다.

터치 화면에서는 아무리 화면을 눌러도 캐릭터가 꿈쩍하지 않습니다. 박시니어 씨가 설명했습니다.

"요즘 웹 게임 플레이어 중 절반 이상이 모바일 사용자예요. 모바일 대응은 선택이 아니라 필수입니다." 모바일 대응이란 정확히 무엇일까요?

쉽게 비유하자면, 모바일 대응은 마치 유니버설 디자인과 같습니다. 건물에 계단만 있으면 휠체어 사용자는 들어갈 수 없습니다.

경사로를 만들면 누구나 접근할 수 있죠. 게임도 마찬가지입니다.

키보드 없이도 플레이할 수 있는 방법을 제공해야 합니다. 모바일 대응의 첫 번째 과제는 화면 크기입니다.

PC 모니터는 보통 1920x1080 이상이지만, 스마트폰 화면은 그보다 훨씬 작습니다. 게다가 세로 모드로 사용하는 경우도 많습니다.

Phaser의 Scale Manager를 활용하면 화면 크기에 자동으로 맞출 수 있습니다. Phaser.Scale.FIT이나 Phaser.Scale.RESIZE 모드를 사용하면 됩니다.

두 번째 과제는 터치 컨트롤입니다. 이것이 핵심입니다.

PC에서는 키보드와 마우스로 조작하지만, 모바일에서는 화면을 터치해야 합니다. 가장 일반적인 해결책은 가상 조이스틱입니다.

화면 한쪽에 원형 조이스틱을 그려두고, 사용자가 드래그하면 그 방향으로 캐릭터가 이동합니다. 위의 코드를 살펴보겠습니다.

this.sys.game.device.input.touch로 현재 디바이스가 터치를 지원하는지 확인합니다. PC에서는 이 값이 false이므로 가상 컨트롤이 나타나지 않습니다.

모바일에서만 터치 컨트롤이 표시됩니다. joystickBasejoystickThumb은 조이스틱의 배경과 조작부입니다.

setScrollFactor(0)은 카메라가 움직여도 조이스틱이 화면에 고정되도록 합니다. setDepth(100)은 다른 게임 요소 위에 표시되도록 합니다.

pointermove 이벤트에서 터치 위치를 계산합니다. 조이스틱 중심에서 터치 위치까지의 방향과 거리를 계산해서 touchVector에 저장합니다.

이 값을 update()에서 읽어 캐릭터를 이동시키면 됩니다. 점프 버튼도 비슷합니다.

화면 오른쪽에 원형 버튼을 배치하고, pointerdown 이벤트로 터치를 감지합니다. 터치하면 점프 함수가 호출됩니다.

더 정교한 조이스틱이 필요하다면 rexUI 플러그인을 사용하는 것도 방법입니다. 이 플러그인은 가상 조이스틱, 버튼, 슬라이더 등 다양한 UI 컴포넌트를 제공합니다.

주의할 점이 있습니다. 모바일 화면은 작기 때문에 버튼이 너무 작으면 누르기 어렵습니다.

터치 영역은 최소 44x44 픽셀 이상을 권장합니다. 또한 손가락으로 화면을 가리게 되므로, 중요한 게임 요소가 컨트롤 영역과 겹치지 않도록 배치해야 합니다.

김개발 씨는 가상 조이스틱과 점프 버튼을 추가했습니다. 스마트폰으로 테스트해보니, 엄지손가락으로 조이스틱을 드래그하면 캐릭터가 부드럽게 움직였습니다.

오른쪽 버튼을 탭하면 점프도 잘 됩니다. 게임을 다시 배포하고 댓글로 알렸습니다.

"Mobile controls added!" 얼마 지나지 않아 답글이 달렸습니다. "Now I can play on the bus.

Thanks!" 김개발 씨는 뿌듯했습니다. 이제 언제 어디서나 자신의 게임을 즐길 수 있게 되었습니다.

실전 팁

💡 - 가상 조이스틱은 반투명하게 만들어 게임 화면을 가리지 않도록 하세요

  • 터치 영역은 실제 표시되는 버튼보다 약간 크게 설정하면 조작감이 좋아집니다
  • 가로/세로 모드 전환 시 컨트롤 위치가 올바른지 테스트하세요

6. PWA로 만들기

모바일 대응까지 완료한 김개발 씨. 친구가 물었습니다.

"이거 앱스토어에는 안 올려?" 김개발 씨는 고개를 저었습니다. "앱 개발은 또 다른 세계야.

Swift도 배워야 하고, 개발자 등록비도 내야 하고..." 그때 박시니어 씨가 말했습니다. "PWA로 만들면 돼요.

앱처럼 설치할 수 있어요."

**PWA(Progressive Web App)**는 웹사이트를 앱처럼 설치하고 사용할 수 있게 만드는 기술입니다. 마치 책에 예쁜 표지를 씌워서 책장에 꽂아두는 것과 같습니다.

브라우저 없이도 바로 실행할 수 있고, 오프라인에서도 동작합니다.

다음 코드를 살펴봅시다.

// manifest.json - PWA 설정 파일
{
  "name": "My Phaser Game",
  "short_name": "PhaserGame",
  "description": "An awesome game built with Phaser",
  "start_url": "./index.html",
  "display": "fullscreen",
  "orientation": "landscape",
  "background_color": "#000000",
  "theme_color": "#1a1a2e",
  "icons": [
    { "src": "icons/icon-192.png", "sizes": "192x192", "type": "image/png" },
    { "src": "icons/icon-512.png", "sizes": "512x512", "type": "image/png" }
  ]
}

// service-worker.js - 오프라인 지원
const CACHE_NAME = 'phaser-game-v1';
const ASSETS = ['/', '/index.html', '/main.js', '/assets/'];

self.addEventListener('install', (event) => {
  event.waitUntil(
    caches.open(CACHE_NAME)
      .then((cache) => cache.addAll(ASSETS))
  );
});

self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.match(event.request)
      .then((response) => response || fetch(event.request))
  );
});

김개발 씨는 고민에 빠졌습니다. 친구들이 "앱으로 만들어줘"라고 자꾸 요청합니다.

매번 브라우저를 열고 URL을 입력하는 것이 귀찮다는 거죠. 하지만 iOS 앱을 만들려면 Swift를 배워야 하고, 안드로이드 앱을 만들려면 Kotlin을 배워야 합니다.

두 플랫폼을 다 지원하려면 코드를 두 벌 작성해야 합니다. 박시니어 씨가 해결책을 제시했습니다.

"PWA로 만들면 웹 게임을 앱처럼 쓸 수 있어요. 코드 한 벌로요." PWA란 정확히 무엇일까요?

쉽게 비유하자면, PWA는 마치 변신 로봇 같습니다. 평소에는 웹사이트로 작동하다가, 설치하면 앱처럼 변신합니다.

브라우저 주소창 없이 전체화면으로 실행되고, 홈 화면에 아이콘도 생깁니다. 사용자 입장에서는 앱과 구분이 안 됩니다.

PWA의 핵심은 두 가지 파일입니다. 첫 번째는 manifest.json입니다.

이 파일은 앱의 정체성을 정의합니다. 이름이 뭔지, 아이콘은 뭔지, 어떤 화면 방향을 사용할지 등의 정보가 담겨 있습니다.

브라우저는 이 파일을 읽고 "이건 설치 가능한 앱이구나"라고 판단합니다. 위의 manifest.json을 살펴보겠습니다.

display: "fullscreen"은 브라우저 UI 없이 전체화면으로 실행하겠다는 의미입니다. 게임에 딱 맞는 설정이죠.

orientation: "landscape"는 가로 모드를 선호한다는 뜻입니다. 가로 화면 게임이라면 이렇게 설정합니다.

icons 배열에는 앱 아이콘을 지정합니다. 최소 192x192와 512x512 두 가지 크기가 필요합니다.

이 아이콘이 홈 화면에 표시됩니다. 두 번째 핵심 파일은 Service Worker입니다.

Service Worker는 브라우저와 네트워크 사이에서 중개 역할을 합니다. 사용자가 처음 게임에 접속하면, Service Worker가 필요한 파일들을 캐시에 저장합니다.

이후에는 네트워크 연결이 없어도 캐시에서 파일을 불러옵니다. 지하철에서 인터넷이 끊겨도 게임을 계속할 수 있는 이유입니다.

코드에서 install 이벤트는 Service Worker가 처음 설치될 때 발생합니다. 이때 ASSETS 배열에 있는 파일들을 캐시에 저장합니다.

fetch 이벤트는 파일을 요청할 때마다 발생합니다. 캐시에 있으면 캐시에서, 없으면 네트워크에서 가져옵니다.

index.html에서 Service Worker를 등록해야 합니다. javascript if ('serviceWorker' in navigator) { navigator.serviceWorker.register('./service-worker.js'); } 이 코드를 추가하면 PWA 설정이 완료됩니다.

PWA로 만들면 어떤 장점이 있을까요? 첫째, 앱 스토어 심사가 필요 없습니다.

웹사이트를 업데이트하면 바로 반영됩니다. 둘째, 설치 용량이 작습니다.

네이티브 앱은 수십 MB지만, PWA는 필요한 에셋만 캐시합니다. 셋째, URL 공유가 가능합니다.

친구에게 링크를 보내면 바로 플레이할 수 있습니다. 주의할 점도 있습니다.

PWA는 HTTPS에서만 작동합니다. 다행히 GitHub Pages와 itch.io는 기본적으로 HTTPS를 제공합니다.

또한 iOS Safari는 PWA 지원이 제한적입니다. 푸시 알림 같은 일부 기능은 작동하지 않습니다.

하지만 게임 플레이에는 문제없습니다. 김개발 씨는 manifest.json과 service-worker.js를 추가하고 다시 배포했습니다.

스마트폰으로 접속해보니, 주소창에 "설치" 버튼이 나타났습니다. 버튼을 누르자, 홈 화면에 게임 아이콘이 추가되었습니다.

아이콘을 탭하니, 앱처럼 전체화면으로 게임이 실행되었습니다. 브라우저 흔적은 전혀 보이지 않습니다.

비행기 모드로 바꿔봐도 게임은 문제없이 돌아갑니다. "이제 진짜 내 게임이 앱이 됐다!" 김개발 씨는 감격했습니다.

앱 개발을 따로 배우지 않고도, 웹 기술만으로 앱 같은 경험을 만들어냈습니다. 처음 Phaser를 배우던 날부터 여기까지, 긴 여정이었지만 보람찬 결과물이었습니다.

실전 팁

💡 - Lighthouse 도구로 PWA 요구사항을 충족하는지 검사할 수 있습니다

  • 캐시 버전(CACHE_NAME)을 업데이트할 때마다 바꿔서 사용자가 최신 버전을 받도록 하세요
  • 아이콘은 배경이 투명한 PNG보다 단색 배경 PNG가 더 잘 표시됩니다

이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!

#Phaser#GameDev#Build#Deploy#PWA#Game

댓글 (0)

댓글을 작성하려면 로그인이 필요합니다.