Skip to content

Instantly share code, notes, and snippets.

@stzhyegeun
Created February 6, 2018 01:53
Show Gist options
  • Save stzhyegeun/f65d9eb4a1f45050627f02d1af9345b1 to your computer and use it in GitHub Desktop.
Save stzhyegeun/f65d9eb4a1f45050627f02d1af9345b1 to your computer and use it in GitHub Desktop.

개요

HTML5는 정보를 링크해서 보여주기 위한 태그 문법 이외에 자바스크립트로 동작하는 API들을 포함하여 웹 자체의 기능을 확장하였다. 추가된 API들을 살펴보면 마치 웹을 모바일 앱처럼 만들고자 하는 것으로 보인다.

HTML5 API는 다음과 같다.

  • Video, Audio
  • Canvas
  • Form
  • Drag & Drop
  • Geo Location
  • Web Storage
  • IndexedDB
  • File
  • Communication
  • Web Worker

현재 HTML5 게임을 만드는 데 활용하고 있는 Phaser 엔진은 내부적로 Audio와 Canvas 기능을 사용하고 있다.

Video

개요

웹 페이지에서 비디오를 재생하는 기능

사용 예시

<video src="../video/big_buck_bunny.webm"
    width="640" height="360"
    controls="controls"
    loop="loop"
    autoplay="autoplay"
    poster="../images/virus.png"
    preload="auto">
    당신의 브라우저는 HTML5의 video 태그를 지원하지 않습니다.
</video>

현재 HTML5의 video 태그는 표준화된 코덱이 없다. MP4는 모든 브라우저에서 지원되고 WebM과 Ogg는 사파리, IE에서는 지원되지 않는다. 코덱 문제를 해결하기 위해서는 video 태그 안에 다른 코덱을 사용해서 인코딩한 src를 추가하면 된다. 그러면 자동으로 브라우저에서 재생할 수 있는 src를 선택해서 재생한다.

video 태그는 HTMLMediaElement API를 사용해서 컨트롤할 수 있다. 이 API를 통해 비디오 재생을 컨트롤 하거나, 비디오 플레이어의 UI를 직접 만들 수도 있다.

게임에서의 활용

예전 웹 게임의 경우 오프닝이나 엔딩 영상을 Youtube를 이용해 재생새키는 경우가 종종 있었다. 다이노볼즈에서도 유튜브를 이용한 영상 삽입을 시도했었지만 Facebook의 정책으로 실패했다. (iframe 사용 불가) 만약 video 태그를 사용해 영상을 재생하고 video API로 컨트롤들을 보이지 않게 커스터마이징 한다면 동영상으로 인게임 연출을 할 수도 있을 것이다.

예시에서는 서버에서 파일을 전부 로드한 후 재생하는 기능만을 소개하고 있지만, MediaSource API 를 사용할 경우 비디오 스트리밍도 할 수 있다.

Drag & Drop

개요

웹 문서 상의 Element를 Drag & Drop 할 수 있도록 지정 로컬 컴퓨터의 파일을 브라우저로 드래그 & 드롭 했을 때 사용되는 API 이다.

HTML 태그에서 draggable 속성을 true로 설정하면 해당 요소는 드래그할 수 있게 된다.

이후 해당 요소에 dragstart, drag, dragenter, dragover, dragleave, drop, dragend와 같은 이벤트를 등록하여 드래그 이벤트에 따른 동작을 설정한다.

이때 DataTransfer 객체가 드래그 이벤트를 통해 전파되는데 이 객체는 드래그 소스에서 드롭 목적지로 전달될 데이터를 보관하는 역할을 한다.

사용 예시

태그 설정

<img src="../images/beer.png" draggable="true">

이벤트 등록 및 API 사용

var item = document.querySelector(".menuItem");
var target = document.querySelector(".dropTarget");

item.addEventListener("dragstart", function(e) {
    e.dataTransfer.setData("text/plain", e.target.dataset.type);
    e.dataTransfer.setData("id", e.target.id);
});

target.addEventListener("drop", function(e) {
    var id = e.dataTransfer.getData("id");
    var type = e.dataTransfer.getData("text/plain");
    this.appendChild(document.querySelector("#" + id));
});

게임에서의 활용

Canvas 내부적으로 Drag 이벤트를 처리할 수 있으므로 게임 제작 시에는 자주 사용하진 않을 것 같다.

다만 인게임과 Canvas 외부 요소 사이의 드래그 이벤트가 필요하다거나 인게임에 컴퓨터 파일을 드래그 한다거나 하는 일도 있을 수 있으므로 알아두는 것이 좋을 것 같다.

GeoLocation

개요

웹에서 사용자의 현재 위치를 위도/경도 정보로 확인할 수 있는 API이다.

위치 정보 획득 방법은 아래 3가지가 있다. HTML5 API에서는 아래 방법들을 조합해서 사용한다.

  • IP 주소 기반 : 컴퓨터 기기에 IP를 부여하는 ISP에서 정보를 넘겨받아 확인. 정확히는 ISP의 위치가 잡힌다.
  • GPS 기반 : GPS 단말이 있어야만 사용할 수 있다. 위성으로부터 정보를 받기 때문에 시간이 오래 걸리고 배터리 소모가 크다.
  • 이동 통신 기반 : 이동 통신 기지국을 활용
  • Wi-Fi 기반 : Wi-Fi 송수신을 위한 AP를 이용해 위치를 확인

기기 환경과 유저의 설정에 따라 정확도가 떨어질 수 있으며, GPS 사용을 위해서는 별도로 유저 동의가 필요할 수 있다.

지오로케이션 API의 사용법은 일회성 위치 정보 요청반복적인 위치 업데이트 두 가지가 있다.

사용 예시

일회성 위치 정보 요청

var geolocation = navigator.geolocation;
var options = {
    enableHighAccuracy: false, 
    timeout: 30000, 
    maximumAge: 0
};
geolocation.getCurrentPosition(success, error, options);

function success(position) {
    var coords = position.coords;
    var date = new Date(position.timestamp);
    
    var latitude = coords.latitude;
    var longitude = coords.longitude;
    var accuracy = coords.accuracy;
}

function error(positionError) {
    switch (positionError) {
    case 1:
    	console.log("사용자가 권한 부여를 거부하였습니다.");
        break;
    case 2:
    	console.log("내부 오류로 위치 정보를 가져오지 못했습니다.");
        break;
    case 3:
    	console.log("Timeout 초과로 정보를 가져오지 못했습니다.");
        break;
    }
}

반복적인 위치 업데이트

한번 요청하면 지속적으로 위치 정보를 업데이트하는 기능으로 watchPosition() 함수를 사용한다.

함수의 실행결과로 id를 반환하는데, 이 id를 clearWatch() 함수에 파라미터로 전달하여 위치 정보 업데이트를 중지한다.

디바이스의 위치가 변경될 때마다 success 콜백을 동작시킨다.

var geolocation = navigator.geolocation;
var options = {
    enableHighAccuracy: false, 
    timeout: 30000, 
    maximumAge: 0
};
var watchId = geolocation.watchPosition(success, error, options);

function success(position) {
    var coords = position.coords;
    var date = new Date(position.timestamp);
    
    var latitude = coords.latitude;
    var longitude = coords.longitude;
    var accuracy = coords.accuracy;
}

function error(positionError) {
    switch (positionError) {
    case 1:
    	console.log("사용자가 권한 부여를 거부하였습니다.");
        break;
    case 2:
    	console.log("내부 오류로 위치 정보를 가져오지 못했습니다.");
        break;
    case 3:
    	console.log("Timeout 초과로 정보를 가져오지 못했습니다.");
        break;
    }
}

function stopWatching() {
    geolocation.clearWatch(watchId);
}

게임에서의 활용

HTML5 게임의 강점 중 하나는 바로 다양한 플랫폼과 기기에서 동일한 경험을 갖게 하는 것에 있다.

그러나 GeoLocation API를 서로 다른 기기에서 동일한 결과를 보장하지 않는다.

당장 데스크탑에서는 ISP의 정보가 나타날 뿐이다.

만약 포켓몬 GO 처럼 GPS가 있어야만 실행할 수 있다면 활용할 수 있는 여지가 있겠지만

어뷰징에 취약한 HTML5 환경의 특성 상, 위치 정보를 키 포인트으로 활용하기는 힘들 것으로 보인다.

WebStorage

개요

웹 스토리지는 쿠키를 개선한 기술이다.

도메인 별로 저장되므로 서로 다른 도메인의 스토리지를 직접 참조할 수 없다.

서로 다른 페이지라고 해도 동일 도메인이라면 동일 저장소를 참조한다. 따라서 도메인 내에 여러 페이지가 있고 페이지 별로 다른 값을 저장하고자 한다면 키를 길게 하자.

쿠키의 문제점

  • 네트워크 부하 유발
    • 서버 요청 시, 클라이언트의 쿠키가 함께 전송된다.
  • 보안상의 문제
    • 쿠키는 전송 과정에서 쉽게 탈취 및 조작될 수 있다.
  • 쿠키의 차단
    • 위의 문제로 인해 대부분의 브라우저에서 쿠키를 차단하고 있다.
  • 제한적인 용량
    • 쿠키의 용량은 기껏해야 4kb 정도이다.

웹 스토리지와 쿠키의 유사점

  • 이름과 값의 단순한 형태로 데이터를 저장한다.
  • 클라이언트의 공간에 데이터를 저장한다.

웹 스토리지와 쿠키의 차이점

  • 웹 스토리지 데이터는 서버로 전송되지 않는다.
  • 평균적으로 5Mb 이상의 용량을 제공한다.

웹 스토리지의 종류

  • 세션 스토리지
    • 브라우저의 세션 기간만 데이터를 유지
    • 동일한 브라우저(탭)에서 리프레시 해도 데이터 유지
    • location.href로 리다이렉트 후, 뒤로가기로 돌아가도 데이터 유지
    • 다른 브라우저(탭)을 열어 게임을 실행할 경우 데이터 유실
  • 로컬 스토리지
    • 로컬 스토리지는 브라우저를 닫아도 데이터를 삭제하지 않는다.
    • 파일로 저장되며 크롬의 경우 /Local Settings/Application Data/Google/Chrome/UserData/Default/Local Storage에 저장

로컬 스토리지의 데이터 공유

  • 서로 다른 브라우저는 로컬 스토리지 저장 파일이 다르기 때문에 공유할 수 없다.
  • 동일한 브라우저 여러 개에서 동일한 도메인에 접속한 경우, 로컬 스토리지를 공유해서 사용할 수 있다.
    • 로컬 스토리지가 변경될 경우 window에 storage 이벤트가 발생

사용법

기본적인 사용법은 다음과 같다.

try {
    if (window.localStorage) {
        localStorage.setItem("test", "test-value");
        localStorage.getItem("test");
        localStorage.removeItem("test");
    }
    
    if (window.sessionStorage) {
        sessionStorage.setItem("test2", "test-value");
        sessionStorage.getItem("test2");
        sessionStorage.removeItem("test2");
    }
} catch (exception) {
    // 용량 초과 시, QuotaExceededError 발생 
    return exception;
} finally {
    localStorage.clear();
    sessionStorage.clear();
}

로컬 스토리지 공유

window.addEventListener("storage", function(e) {
    console.log(e.key);
});

게임에서의 활용

다이노볼즈에서는 공지사항 확인 여부를 로컬 스토리지에 저장하고 있다. 링크

또한 Facebook Instant SDK에서는 유저의 이름과 프로필 이미지 URL을 로컬 스토리지에 저장하는 것을 권장하고 있다.

IndexedDB

클라이언트 브라우저에 구조적 데이터를 저장할 수 있어서 오프라인에서도 작업할 수 있다.

MDN 웹 도큐먼트를 보면 브라우저 및 설정에 따라 DB 용량 제한이 다를 수 있다.

  • 파이어폭스는 50MB 이상의 용량이 필요할 경우 권한 승인 요청 인터페이스가 노출된다.
  • 크롬은 브라우저 사용 가능 용량에 따라 다르다.

키-값 쌍으로 데이터를 저장하며, 이 키들을 이용해 인덱스를 만들 수 있고 이를 객체 조회에 활용한다.

트랜잭션 데이터베이스 모델 기반으로 동작한다. 인덱스, 테이블, 커서 등의 작업은 모두 트랜잭션 내에서 수행되어야 한다. indexedDB는 자동 커밋으로 동작하며 수동 커밋은 지원하지 않는다.

비동기 방식으로 동작하며 동작이 완료될 때 콜백 이벤트를 받아 처리해야 한다. 따라서 동기적으로 관련 작업을 처리하고 싶다면 Promise를 이용해 래핑해야 한다.

SQL을 사용하지 않고 함수 기반으로 데이터 저장, 수정, 검색, 삭제를 수행한다.

특히 검색을 위해선 인덱스를 통해 생성한 커서를 사용해야 한다.

동일 근원 정책을 따르므로 프로토콜, 도메인, 포트가 같은 경우 indexedDB 를 공유하고 다를 때는 공유하지 않는다.

KeyRange API를 사용해서 인덱스 값에 대해 범위 검색으로 데이터를 추출할 수 있다.

사용법

DB를 여는 사용 예 (다이노볼즈)

this.openStaticDB = function() {
    var promise = new Promise(function(resolve, reject) {
        if (!window.indexDB) {
            StaticManager.dbConnect = null;
            reject("indexedDB not allowed.");
        }
        
        var staticDB = indexedDB.open("dino_static");
        staticDB.addEventListener("upgradeneeded", function() {
            StaticManager.dbConnect = staticDB.result;
            if (StaticManager.dbConnect.objectStoreNames.contains("dino_static") === false) {
                StaticManager.dbConnect.createObjetStore("dino_static", {keyPath: "name"});
            }
        });
        
        staticDB.addEventListener("success", function() {
            StaticManager.dbConnect = staticDB.result;
            resolve(StaticManager.dbConnect);
        });
        
        staticDB.addEventListener("error", function(err) {
           StaticManager.dbConnect = null;
           reject(JSON.stringify(err));
        });
    });
    return promise;
};

DB에서 데이터 조회 예 (다이노볼즈)

this.getIndexedData = function() {
    var promise = new Promise(function(resolve, reject) {
        if (!window.indexedDB || !StaticManager.dbConnect) {
            StaticManager.dbConnect = null;
            reject("indexedDB not allowed.");
        }
        
        var tx = StaticManager.dbConnect.transaction(["dino_static"], "readonly");
        var objectStore = tx.objectStore("dino_static");
        var request = objectStorage.getAll();
        request.addEventListener("success", function() {
            var result = {};
            for (var i = 0; i < request.result.length; i++) {
                this[request.result[i].name] = new StaticModel(request.result[i].name, request.result[i].version, JSON.parse(request.result[i].data))
            }
            resolve();
        });
        request.addEventListener("error", function() {
            reject("Transaction Error.");
        });
    });
    return promise;
};

DB 데이터 수정(다이노볼즈)

this.updateIndexedData = function(inName, inVersion, inData) {
    var promise = new Promise(function(resolve, reject) {
        if (!window.indexedDB || !StaticManager.dbConnect) {
            StaticManager.dbConnect = null;
            reject("indexedDB not allowed.");
        }
        
        var data = {
            name: inName, 
            version: inVersion, 
            data: JSON.stringify(inData)
        };
        
        var tx = StaticManager.dbConnect.transaction(["dino_static"], "readwrite");
        var objectStore = tx.objectStore("dino_static");
        var request = objectStore.put(data);
        request.addEventListener("success", function() {
            resolve(data);
        });
        request.addEventListener("error", function() {
            reject("db update failed: " + data.name);
        });
    });
    return promise;
};

게임에서의 활용

웹 스토리지와 달리 용량 제한에서 자유롭기 때문에 스태틱 데이터를 캐싱하는 데 활용할 수 있다.

현재 다이노볼즈에 적용 예정이다.

File

개요

File API를 통해 파일을 읽거나 쓸 수 있다.

쓰기 관련 API는 크롬과 오페라 계열에서만 사용가능하다.

읽기 관련 API는 읽기 전용으로 파일에 접근하기 때문에 파일의 수정, 삭제는 불가능하다.

파일의 정보를 확인하기 위해서는 FileReader 객체를 사용한다.

FileReader 객체는 비동기적으로 동작하며 사용자의 파일을 읽는 역할을 한다.

FileReader 객체의 loadstart, progress, loadend와 같은 이벤트를 사용해서 파일 읽기 진행상황을 확인할 수 있다.

File API 중에는 Blob API가 있는데, Blob를 통해 대용량의 원시 데이터를 처리할 수 있다.

Blob API 중 slice 함수를 사용해서 파일 전체 중 일부만 메모리에 로드해서 사용할 수 있다.

사용법

FileReader 사용법

var file = inFile;
var fReader = new FileReader();

if (getType(file.type) === "image") {
    // 이미지 파일 읽기
    fReader.readAsDataURL(file);
} else if (getType(file.type) === "text") {
    // 문자열 파일 읽기
    fReader.readAsText(file);
    // 혹은 Blob를 이용해 파일의 일부만 불러오기
    var blob = file.slice(0, 100);
    fReader.readAsText(blob);
}

fReader.addEventListener("load", function(e) {
    console.log("파일 읽기 완료.");
    // 파일 정보를 세션 스토리지에 저장
    sessionStorage.setItem(file.name, JSON.strigify(e.result));
});

fReader.addEventListener("error", function(e) {});
fReader.addEventListener("loadstart", function(e) {});
fReader.addEventListener("progress", function(e) {});
fReader.addEventListener("loadend", function(e) {});

게임에서의 활용

메모리 캐시 보안에 Blob를 활용할 수 있을지 고민해봤으나, 그냥 ArrayBuffer를 바로 사용하는게 나을 듯 하다.

게다가 Blob는 readonly이기 때문에 값의 변경이 있을 시, 메모리 낭비만 많아질 수 있다.

Communication

개요

일반적으로 XMLHttpRequest Level2를 이용하는 Ajax 기술,

다른 근원의 문서 메시징을 지원하는 Cross Document Messaging (또는 Web Messaging),

그리고 서버와의 실시간 통신을 위한 Web Socket을 지칭한다.

XMLHttpRequest Level 2

현재 다이노볼즈에서는 jQuery를 통해 사용 중이다.

Level 2 에서는 서버가 허락만 한다면 다른 근원에서 제공되는 서비스를 마음껏 사용할 수 있다.

서버의 응답 헤더에 Access-Control-Allow-Origin 속성으로 허용할 서버를 등록하면 된다.

페이스북 인스턴트 게임은 페이스북 호스팅 서버를 사용하기 때문에 게임 서버와는 근원이 다르다.

따라서 게임 서버의 응답 헤더에 페이스북 호스팅 서버를 등록해주어야 한다.

Cross Document Messaging

한 문서에서 메시지를 생성해 다른 페이지 (iframe, 탭, 윈도우)로 전송할 수 있는 기능이다.

window.postMessage 함수를 사용해 메시지를 전송하고, 메시지를 받는 측에서는 message 이벤트를 통해 전달받은 메시지를 처리할 수 있다.

웹 소켓

웹 상에서 하나의 소켓으로 작동하는 양방향 통신 기능이다.

소켓 통신은 기존의 HTTP 헤더를 통한 통신 대신 TCP 소켓을 통해 이뤄진다.

헤더에 대한 정보가 줄어들기 때문에 전송된느 데이터의 크기가 줄어들고, 소켓을 닫기 전까진 연결이 지속되므로 응답 속도도 빠르다.

사용법

postMesage 사용법

// iframe 엘리먼트 
var widget = document.getElementById("widget");
var trust = "http://test.com:8080";
widget.src = trust + "/a.html";
widget.contentWindow.postMessage("test", trust);

window.addEventListener("message", function(e) {
    if (e.origin === trust) {
        console.log(e.data);
    }
});

웹 소켓 사용법

var wsUri = "ws://localhost:8080/echo";
socket = new WebSocket(wsUri);
socket.addEventListener("open", function() {
    console.log("소켓 연결 성공.");
});
socket.addEventListener("message", function(e) {
    console.log("메시지 수신: " + e.data);
});
socket.addEventListener("error", function(e) {
    console.log("에러 발생: " + JSON.stringify(e));
});

게임에서의 활용

Cross Document Messaging

대부분 게임에서는 새로운 탭이나 브라우저를 여는 경우가 없다.

현재 facebook 에서는 iframe도 허용하지 않고 있다. 하지만 다른 플랫폼에서는 활용할 여지가 있다.

Web Worker

자바 스크립트는 기본적으로 단일 스레드 언어이다.

개발하기 편리하다는 장점이 있지만, 연산이 길어질 경우 메인 스레드에 큰 부담을 주면서 UI 동작 등을 수행할 수 없게 된다.

웹 워커를 통해 자바스크립트를 멀티 스레드 형태로 사용할 수 있다.

스크립트의 동작을 메인 스레드와 분리된 작업 스레드로 만들어 스레드가 멈추거나 늦게 동작하는 것을 방지할 수 있다.

웹 워커에는 워커를 생성한 메인 코드에서만 사용할 수 있는 전용 워커(Dedicated Worker)와 여러 문서에서의 요청에 응답할 수 있는 공유 워커(Shared Worker)의 두가지가 있다.

워커를 사용하기 위해서는 먼저 워커에서 실행할 코드를 별도의 자바스크립트로 만들어야 한다.

이후 메인 코드에서 new Worker() 생성자를 호출하면서 파라미터로 파일명을 넘겨주면 된다.

워커와 메인 코드는 MessageEvent 기반으로 데이터를 주고 받는다.

또한 워커는 메인 코드와는 별개의 전역 컨텍스트에서 실행되므로 워커에서 DOM에 직접 접근할 수 없고, 일부 window 속성도 사용할 수 없다.

메인 코드에서도 워커 내부의 코드에 접근할 수 없다.

워커에서 try catch로 처리하지 않은 오류는 자동으로 메인 코드에 이벤트로 전달된다.

이 오류 이벤트를 처리하기 위해 메인 코드에서는 워커 객체에 error 이벤트 리스너를 등록해줘야 한다.

한번 생성한 워커는 자동으로 멈추지 않고 계속해서 message 이벤트를 수신 대기한다.

따라서 더 이상 워커가 필요하지 않은 경우에는 명시적으로 처리를 중지하거나 종료해야 한다.

워커를 종료하는 데에는 terminate() 함수와 close() 함수를 사용할 수 있다.

terminate는 메인 코드에서 워커를 종료시키는 함수로 워커에서 자원 반납의 기회를 주지 않고 강제로 종료시킨다.

close는 워커 내부에서 사용하는 함수로 워커 내부 자원을 반납한 후 close를 호출하여 워커를 종료하면 된다.

사용법

Dedicated Worker

메인 코드

var worker = new Worker("a.js");
worker.addEventListener("message", function(e) {
    console.log(e.data);
});
worker.addEventListener("error", function(e) {
    console.log("error: " + JSON.stringify(e));
});
worker.postMessage("test");
worker.postMessage("close");

a.js

// Worker 객체의 메서드인 importScripts를 이용해 워커 내부에서 다른 스크립트를 재사용할 수 있다.
importScripts("b.js");

addEventListener("message", function(e) {
    postMessage(e.data + "!");

    if (e.data === "close") {
        close();
    }
});

Shared Worker

메인 코드

var worker = new SharedWorker("a.js");
worker.addEventListener("error", function(e) {
    console.log("error: " + e.toString());
});
worker.addEventListener("message", function(e) {
    console.log(e.data);
});

// 하나의 공유 워커에는 연결된 메인 코드별로 port가 할당된다. port를 통해 메시지를 전송한다.
worker.port.postMessage("test");

// 메시지 큐에 쌓여있는 메시지들을 보내기 시작하는 함수이다.
// postMessage를 통해 메시지를 보내더라도 start가 호출되지 않으면 메시지는 전송되지 않는다.
worker.port.start();

a.js

var ports = new Array();
var prev = "";
addEventListener("connect", function(e) {
    var clientPort = e.ports[0];
    ports.push(clinetPort);

    clinetPort.addEventListener("message", function(e) {
        prev += e.data;
        for (var i = 0; i < ports.length; i++) {
            ports[i].postMessage(e.data);
        }
    });
    clientPort.start();
    clientPort.postMessage(prev);
});

게임에서의 활용

  • 단순히 오래 걸리는 연산을 별도 스레드로 돌리는 역할
  • 서버와의 통신을 전담하는 백그라운드 프로세스

참고자료

어플리케이션 캐시

웹기반 어플리케이션이 오프라인에서도 실행되도록 어플리케이션 캐시를 제공한다.

어플리케이션 캐시를 하면 서버의 부하가 줄어들고 리소스 로딩이 빨라진다.

어플리케이션 캐시를 사용하기 위해서 html 태그에 manifest 어트리뷰트를 설정한다.

manifest 어트리뷰트는 캐시 매니페스트 텍스트 파일을 참조한다.

어플리케이션 내의 모든 페이지에 manifest 어트리뷰트를 추가해야 한다.

만약 manifest 어트리뷰트를 설정하지 않은 페이지가 있다면 해당 페이지는 캐싱되지 않는다.

로딩 과정은 다음과 같다.

  1. 어플리케이션 캐시가 있다면 브라우저는 네트워크에 연결하지 않고 먼저 캐시로부터 페이지 렌더링에 필요한 리소스를 로드한다.
  2. 그 다음 서버의 캐시 매니페이스트 파일을 확인하여 업데이트된 리소스가 있는지 체크한다.
  3. 캐시 매니페스트 파일이 업데이트 되었다면 새로운 버전의 매니페스트 파일과 리소스들을 다운로드 한다.

캐시 매니페스트 파일은 3가지 섹션으로 나뉘어 있다.

  • CACHE
    • 기본적인 섹션으로 여기에 리스트업된 리소스는 캐싱된다.
  • NETWORK
    • 캐싱과 상관없이 매번 네트워크 요청을 해야하는 URL 규칙
  • FALLBACK
    • 리소스 요청이 실패했을 때 대신 요청할 URL

주의할 점

매니페스트 파일을 요청할 때 header 정보에 Content-Type을 text/cache-manifest로 설정해야 한다.

HTML 파일은 자동으로 캐싱되어 버린다. 그래서 HTML 문서를 수정해도 캐싱된 문서를 로드하기 때문에 적용되지 않는다.

이 경우, 매니페스트 파일을 변경해서 페이지가 업데이트 되도록 해야 한다.

자주 변경되는 파일이 있다면 NETWORK 섹션에 추가해서 지속적으로 업데이트 되도록 하면 된다.

참고할 만한 링크

@changmlee
Copy link

changmlee commented Feb 6, 2018

네 정리 잘해주셨네요.
슬롯메이트 애니팡 플러스에서 웹소켓 사용할 예정인데 애니팡 플러스 개발자들도 같이 참여할 수 있게 공지할게요

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment