KKflow

MAKING · 웹 기술

웹게임을 앱처럼: 홈 화면 설치와 오프라인 플레이(PWA)

앱스토어 없이 웹게임을 홈 화면에 설치하고 오프라인에서도 돌아가게 만드는 PWA의 구성 요소, 캐시 전략, 설치 방법, 업데이트할 때 주의할 점을 정리했습니다.

· KKflow

스택 타워와 로또르르는 홈 화면에 설치할 수 있습니다. 앱스토어를 거치지 않았는데도 아이콘을 누르면 주소창 없는 전체 화면으로 열리고, 스택 타워는 비행기 모드에서도 플레이됩니다. 이런 웹 앱을 PWA(Progressive Web App)라고 부릅니다. 이 글은 PWA를 이루는 요소와 만들 때 주의할 점을 정리합니다.

PWA를 이루는 세 가지

  1. HTTPS — 서비스 워커는 보안 연결에서만 동작합니다.
  2. 웹 앱 매니페스트 — 앱 이름, 아이콘, 시작 주소, 화면 모드를 적은 JSON 파일입니다.
  3. 서비스 워커 — 페이지와 네트워크 사이에서 요청을 가로채 캐시로 응답할 수 있는 스크립트입니다. 오프라인 동작의 핵심입니다.

매니페스트 예시

{
  "name": "My Web Game",
  "short_name": "My Game",
  "start_url": "/",
  "display": "standalone",
  "background_color": "#0A1519",
  "theme_color": "#0A1519",
  "icons": [
    { "src": "/icon-192.png", "sizes": "192x192", "type": "image/png" },
    { "src": "/icon-512.png", "sizes": "512x512", "type": "image/png" }
  ]
}

display: standalone이면 주소창 없이 앱처럼 열립니다. HTML의 <head>에 <link rel="manifest" href="/manifest.json">로 연결합니다. 아이콘은 최소 192px와 512px 두 가지를 준비하는 것이 좋습니다.

서비스 워커와 캐시 전략

서비스 워커는 처음 방문할 때 설치되며, 이때 게임에 필요한 파일을 미리 캐시에 담아 둘 수 있습니다. 그다음부터는 요청 종류에 따라 응답 방식을 고릅니다.

전략동작어울리는 대상
캐시 우선캐시에 있으면 바로 쓰고, 없을 때만 네트워크게임 코드, 이미지, 폰트 같은 정적 파일
네트워크 우선네트워크를 먼저 시도하고 실패하면 캐시자주 바뀌는 데이터
네트워크 전용항상 네트워크, 실패하면 오류 처리랭킹 등록처럼 오프라인으로 대신할 수 없는 요청
const CACHE = "game-v12";
const FILES = ["/", "/index.html", "/icon-192.png"];

self.addEventListener("install", (e) => {
  e.waitUntil(caches.open(CACHE).then((c) => c.addAll(FILES)));
});

self.addEventListener("fetch", (e) => {
  if (e.request.method !== "GET") return;   // 점수 등록 같은 요청은 건드리지 않음
  e.respondWith(
    caches.match(e.request).then((hit) => hit || fetch(e.request))
  );
});

스택 타워처럼 단일 HTML 파일에 그래픽과 사운드를 코드로 생성하는 구조는 PWA와 특히 잘 맞습니다. 캐시할 파일이 적어서 첫 설치가 가볍고, 한 번 캐시되면 네트워크 없이 완전히 동작합니다. 온라인 랭킹처럼 네트워크가 필요한 기능만 연결될 때 동작하게 나누면 됩니다.

업데이트할 때 가장 많이 겪는 문제

PWA를 처음 만들면 "고쳐서 배포했는데 사용자 화면이 안 바뀐다"는 문제를 꼭 만납니다. 캐시 우선 전략은 옛 파일을 계속 보여 주기 때문입니다.

  • 캐시 이름에 버전을 붙입니다. 배포할 때 game-v12를 game-v13으로 바꾸고, 새 서비스 워커의 activate 단계에서 옛 캐시를 지웁니다.
  • 서비스 워커 파일 자체는 캐시가 오래 남지 않게 설정합니다. 브라우저가 새 버전을 확인할 수 있어야 합니다.
  • 새 버전은 다음 실행부터 적용됩니다. 기본 동작에서는 열려 있는 탭이 모두 닫혀야 새 서비스 워커가 활성화됩니다. 게임 도중에 갑자기 바뀌는 것보다 이 편이 안전하지만, 필요하면 "새 버전이 있어요" 안내를 띄우고 새로 고침을 유도할 수 있습니다.

기기에 저장한 기록은 영원하지 않다

오프라인 게임은 최고 기록, 코인, 업적을 브라우저 저장소에 남깁니다. 하지만 저장 공간이 부족하거나 사용자가 사이트 데이터를 지우면 함께 사라질 수 있습니다. 중요한 진행 상황이라면 navigator.storage.persist()로 영구 저장을 요청해 볼 수 있고, 온라인일 때 서버에 백업하는 기능을 두는 것도 방법입니다. 무엇보다 사용자에게 "기록은 이 기기에 저장된다"는 사실을 알려 두는 것이 좋습니다.

사용자 입장에서 설치하는 법

  • 안드로이드(크롬) — 주소창 오른쪽 메뉴(⋮)에서 홈 화면에 추가 또는 앱 설치를 누릅니다. 조건을 갖춘 사이트에서는 설치 안내가 자동으로 뜨기도 합니다.
  • 아이폰(사파리) — 아래쪽 공유 버튼을 누르고 홈 화면에 추가를 고릅니다.
  • PC(크롬·엣지) — 주소창 오른쪽의 설치 아이콘을 누르면 별도 창으로 실행되는 앱이 됩니다.

PWA가 어울리는 경우

짧게 자주 여는 게임, 매주 한 번 쓰는 도구처럼 다시 찾아오는 서비스라면 PWA의 이점이 큽니다. 앱스토어 심사와 설치 장벽 없이 주소 하나로 배포하고, 원하는 사람만 홈 화면에 설치하면 됩니다. 반대로 한 번 보고 끝나는 페이지라면 굳이 서비스 워커를 붙일 필요는 없습니다.