모바일웹2011. 5. 10. 05:07

웹을 이용하여 모바일 웹 애플리케이션을 개발하는 경향이 증가하고 있다. 그러나 변동성이 심한 네트워크 사용가능성으로 인해 웹 기술을 클라우드 인프라의 일부로 사용하는 데는 커다란 문제가 있다. 기존의 웹 애플리케이션은 네트워크가 없으면 작동하지 않는다. 이러한 문제에 대한 한 가지 해결책은 HTML5 표준(참고자료 확인)의 두 가지 기능을 사용하는 것이다.

  • 오프라인 웹 애플리케이션
  • 클라이언트 측 데이터베이스 스토리지

사용자는 모바일 장치에서 클라우드 기능을 사용할 수 있기 때문에 로컬 데이터베이스에 전개된 애플리케이션을 사용하여 오프라인에서 작업할 수 있으며 다시 온라인 상태가 되면 나머지 클라우드와 데이터를 공유할 수 있다. 이 기사에서는 일반적으로 사용되는 시나리오를 활용하여 세부적인 기술을 살펴본다. 단순한 재고 관리 애플리케이션 프로토타입을 통해 HTML5의 기술을 살펴본다. 이 기사에 있는 예제 애플리케이션의 샘플 코드를 아래 다운로드 표에서 다운로드할 수 있다.

개요

그림 1에는 샘플 애플리케이션 아키텍처의 주요 컴포넌트에 대한 개요가 표시되어 있다.


그림 1. 오프라인 웹 애플리케이션의 핵심 요소

HTML 페이지
웹 애플리케이션의 핵심인 HTML 페이지는 모델 역할이라고 하는 것이 있다. 또한, HTML 페이지는 표시 데이터와 기본적인 렌더링 정보를 포함하고 있다. HTML 페이지에 있는 HTML 요소는 HTML 문서 오브젝트 모델(DOM) 트리 계층으로 체계화되어 있다. 사용자가 이벤트를 시작하면 페이지가 로드되고 연관된 Javascript 함수가 실행되면서 일반적인 요청 - 응답 주기가 시작된다.

분명히 말해서 이러한 애플리케이션은 하나의 HTML 페이지로 구성되며 요청 - 응답 주기를 통해 추가로 HTML를 로드할 필요가 없다. 전체 조치가 하나의 페이지에 존재한다.

Javascript
Javascript 요소에는 애플리케이션을 제어할 수 있는 기능이 있다. HTML 요소는 이벤트 핸들러를 통해 Javascript 함수에 바인드된다. Javascript에서는 모든 사용자 인터페이스(UI) 요소를 사용하여 웹 애플리케이션의 HTML DOM 트리를 액세스하며 HTML DOM 트리를 통해 계산에 필요한 데이터를 입력한다. 처리 결과는 HTML 페이지를 수정하는 과정을 통해 사용자에게 표시된다.
CSS(Cascading Style Sheet)
CSS(Cascading Style Sheet)를 이용하면 HTML 페이지를 렌더링하는 방법을 기술할 수 있다. 설명을 간단히 하기 위해 여기서는 확인 과정을 생략한다. 이러한 확장 단계에서는 HTML 요소의 기본 렌더링 작동만을 사용한다.

모바일 장치에는 본래의 웹 애플리케이션 사용자 경험과 유사한 체험을 할 수 있게 도움을 주는 다양한 Javascript/CSS 라이브러리와 프레임워크(예: iPhone용 iUI)가 있다. 자세한 정보는 참고자료를 확인하기 바란다. 사용자의 수용 가능성을 늘리려면 이 방식을 사용해야 하지만 이 방식에는 플랫폼에 종속된다는 단점이 있다.

데이터베이스
HTML5 표준에는 로컬 데이터베이스 스토리지가 도입되었다. 이는 Apple® Safari 브라우저 현재 버전에 도입되었다. 이 브라우저는 SQLite를 사용하는 임베디드 데이터베이스를 제공하며 이 데이터베이스는 Javascript에서 SQL 쿼리를 처리하여 액세스할 수 있다. 이 데이터베이스에는 애플리케이션 모델의 비즈니스 데이터가 저장된다.
명시 파일
명시 파일은 오프라인 웹 애플리케이션을 위한 필수 전개 디스크립터 컴포넌트이다. 명시 파일에는 로드해야 하는 모든 파일이 간단히 표시된다.

샘플 애플리케이션

이 섹션에서는 MyHomeStuff라고 하는 샘플 애플리케이션에 대한 개요를 살펴본다. 이 샘플 애플리케이션은 사용자가 소유하고 있는 품목을 추적할 수 있는 간단한 재고 관리 애플리케이션이다. 그림 2에는 iPhone에서 작동 중인 샘플 애플리케이션이 표시되어 있다.


그림 2. iPhone 뷰

간단히 설명하기 위해 서버와의 데이터 동기화 부분은 생략한다. 그림 3에는 Palm Pre 웹 브라우저에서 실행 중인 MyHomeStuff 재고 관리 애플리케이션이 표시되어 있다.


그림 3. Palm Pre 뷰


 

이 화면의 상단에는 입력된 모든 품목(책, 컴퓨터 등)의 개요가 표시된다.

사용자가 목록에 있는 품목을 선택하면 해당 품목의 세부사항(Id, Quantity, Name)이 이 양식의 중간에 표시된다. 선택 품목의 세부사항은 Update 단추를 사용하여 변경할 수 있다. 또한, Delete 단추를 사용하여 애플리케이션에서 선택 품목을 삭제할 수도 있다. 양식에서 품목의 수량과 이름을 입력하고 Create 단추를 선택하여 새 품목을 작성할 수도 있다.

애플리케이션의 상태는 이 화면 하단에 표시된다.

HTML 세부사항

HTML 페이지에는 애플리케이션의 기본 구조를 형성하는 필수 HTML 요소와 선언, 모바일 환경에 최적화된 디스플레이를 위한 메타 태그, 외부 파일(명시 파일, Javascript, CSS)에 대한 참조가 포함되어 있다. Listing 1에는 HTML 코드가 있다.


Listing 1. HTML 코드
                
    <!DOCTYPE HTML>
    <html manifest="MyHomeStuff.manifest">
        <head>
        <meta name="viewport" content="width=device-width; 
          initial-scale=1.0; maximum-scale=1.0; user-scalable=0;">
        <title>MyHomeStuff</title>
        <script type="text/javascript" src="MyHomeStuff.js" ></script>
        </head>
        <body onload="onInit()">
        <h3>Overview</h3>
        <ul id="itemData" ></ul>
        <h3>Details</h3>
        <form name="itemForm">
            <label for="id">Id: </label>
            <input type="text" name="id" id="id" size=2 disabled="true"/>
            <label for="amount">Amount: </label>
            <input type="text" name="amount" id="amount" size = 3/>
            <label for="name">Name: </label>
            <input type="text" name="name" id="name" size=16 /> <br>
            <br>
            <input type="button" name="create" value="create"
                onclick="onCreate()" />
            <input type="button" name="update" value="update"
                onclick="onUpdate()" />
            <input type="button" name="delete" value="delete"
        </form>
    <h4>Status</h4>
        <div id="status"></div>
   </body>
</html>

HTML 요소의 이벤트 핸들러 속성은 HTML 페이지가 처음에 로드될 때(onload) 그리고 단추 요소가 클릭 될 때(onclick) 실행할 Javascript 함수를 지정한다. 오프라인 웹 애플리케이션의 HTML 페이지는 <!DOCTYPE HTML> 태그로 시작한다. 명시 파일은 <html manifest="MyHomeStuff.manifest"> 태그에 있는 명시 파일 속성을 통해 참조되다. 언급한 바와 같이 명시 파일은 캐시로 로드해야 하는 필수 파일을 지정한다. 이 애플리케이션은 HTML 파일과 Javascript 파일로 구성된다. 명시 파일을 참조하는 HTML 파일은 애플리케이션의 캐시에 자동으로 포함된다. 명시 파일에는 다음과 같은 사항만 포함된다.


Listing 2. 명시 파일
CACHE MANIFEST

MyHomeStuff.js

Javascript 세부사항

Javascript 코드는 세 개의 기본 블록으로 구성된다.

  • 초기화 함수
  • DB(CRUD) 및 뷰 업데이트 함수
  • 일부 유틸리티 함수

Listing 3에 표시된 바와 같이 첫 번째 블록에는 애플리케이션을 초기화하는 이벤트 핸들러(onload)와 데이터베이스를 초기화하는 이벤트 핸들러가 포함된다.


Listing 3. Javascript 초기화 코드
function onInit(){
    try {
        if (!window.openDatabase) {
            updateStatus("Error: DB not supported");
        }
        else {
            initDB();
            createTables();
            queryAndUpdateOverview();
        }
    } 
    catch (e) {
        if (e == 2) {
            updateStatus("Error: Invalid database version.");
        }
        else {
            updateStatus("Error: Unknown error " + e + ".");
        }
        return;
    }
}

function initDB(){
    var shortName = 'stuffDB';
    var version = '1.0';
    var displayName = 'MyStuffDB';
    var maxSize = 65536; // in bytes
    localDB = window.openDatabase(shortName, version, displayName, maxSize);
}

상기 코드에서는

  • 먼저, onInit 함수가 필수 openDatabase 함수의 존재를 확인하며 이 함수가 없으면 브라우저에서 로컬 데이터베이스를 지원하지 않는 것이다.
  • initDB 함수는 HTML5 브라우저의 데이터베이스를 연다.
  • 데이터베이스가 정상적으로 열리면 SQL DDL이 실행되어 데이터베이스 테이블을 작성하게 된다. 마지막으로 레코드의 존재를 쿼리하여 HTML 페이지에 데이터를 채우는 함수가 호출된다.

두 번째 Javascript 블록의 각 함수에는 DB를 액세스하고 로직을 표현하는 부분이 포함되어 있다. 이와 같이 로직을 통합하는 것은 Model 1 아키텍처의 특징(참고자료 확인)이며 이렇게 하는 것이 간단한 웹 애플리케이션을 개발하기 위한 가장 쉬운 방법이다. 실제 시나리오에는 MVC(Model View Controller) 부분이 명확히 분리된 아키텍처가 적합하다.

예제의 개요 목록을 빌드하기 위해 이벤트 핸들러 함수에서 queryAndUpdate 함수를 호출한다. Listing 4에는 이러한 HTML 코드가 표시되어 있다.


Listing 4. Javascript 개요 코드
function queryAndUpdateOverview(){

    //remove old table rows
    var dataRows = document.getElementById("itemData").getElementsByClassName("data");
    while (dataRows.length > 0) {
        row = dataRows[0];
        document.getElementById("itemData").removeChild(row);
    };

    //read db data and create new table rows
    var query = "SELECT * FROM items;";
    try {
        localDB.transaction(function(transaction){
        
            transaction.executeSql(query, [], function(transaction, results){
                for (var i = 0; i < results.rows.length; i++) {
                
                    var row = results.rows.item(i);
                    var li = document.createElement("li");
                    li.setAttribute("id", row['id']);
                    li.setAttribute("class", "data");
                    li.setAttribute("onclick", "onSelect(this)");
                    
                    var liText =
                        document.createTextNode(row['amount'] + " x "+ row['name']);
                    li.appendChild(liText);
                    
                    document.getElementById("itemData").appendChild(li);
                }
            }, function(transaction, error){
                updateStatus("Error: " + error.code + "<br>Message: " + error.message);
            });
        });
    } 
    catch (e) {
        updateStatus("Error: Unable to select data from the db " + e + ".");
    }
}

상기 코드에서는

  • DOM 트리에서 이전 데이터를 제거한다.
  • 모든 데이터 세트를 선택하는 쿼리를 실행한다.
  • 결과에 있는 모든 데이터 세트를 위해 HTML 목록 요소를 작성하여 해당 목록에 추가한다.
  • 이벤트 핸들러 onSelect를 모든 목록 요소에 추가하여 클릭되었을 때 응답하도록 한다.

또한, 이 블록에 있는 함수에는 onUpdateonDelete, onCreate, onSelect를 사용하여 단추 막대와 목록을 처리하는 이벤트 핸들러가 포함되어 있다. Listing 5에는 onUpdate의 코드가 표시되어 있다. (onCreateonDelete는 구조가 유사하므로 여기에서는 다루지 않으며 예제 애플리케이션의 모든 소스 코드를 아래 다운로드 표에서 다운로드할 수 있다.


Listing 5. Javascript 업데이트 코드
                
function onUpdate(){
    var id = document.itemForm.id.value;
    var amount = document.itemForm.amount.value;
    var name = document.itemForm.name.value;
    if (amount == "" || name == "") {
        updateStatus("'Amount' and 'Name' are required fields!");
    }
    else {
        var query = "update items set amount=?, name=? where id=?;";
        try {
            localDB.transaction(function(transaction){
                transaction.executeSql(query, [amount, name, id],
                function(transaction, results){
                    if (!results.rowsAffected) {
                        updateStatus("Error: No rows affected");
                    }
                    else {
                        updateForm("", "", "");
                        updateStatus("Updated rows:"
                            + results.rowsAffected);
                        queryAndUpdateOverview();
                    }
                }, errorHandler);
            });
        } 
        catch (e) {
            updateStatus("Error: Unable to perform an UPDATE " + e + ".");
        }
    }
}
            

상기 코드에서는

  • 양식의 필드 값을 읽고 유효성을 확인한다.
  • 필드 값이 올바르면 업데이트 쿼리를 실행한다.
  • 쿼리 결과는 업데이트된 HTML 페이지에 표시한다.

onSelect 함수는 사용자가 목록 요소를 선택할 때 실행한다. Details 양식은 Listing 6에 있는 코드를 사용하여 목록 요소의 데이터로 채운다.


Listing 6. Javascript 선택 코드
function onSelect(htmlLIElement){
    var id = htmlLIElement.getAttribute("id");
    query = "SELECT * FROM items where id=?;";
    try {
        localDB.transaction(function(transaction){
        
            transaction.executeSql(query, [id], function(transaction, results){
            
                var row = results.rows.item(0);
                
                updateForm(row['id'], row['amount'], row['name']);
                
            }, function(transaction, error){
                updateStatus("Error: " + error.code + "<br>Message: " + error.message);
            });
        });
    } 
    catch (e) {
        updateStatus("Error: Unable to select data from the db " + e + ".");
    }
}
            

상기 코드에서는

  • 선택된 요소의 ID를 판별한다.
  • 선택 쿼리를 실행한다.
  • 데이터 세트를 읽어서 Details 양식을 업데이트 하는 함수를 호출한다.

유틸리티 함수가 있는 마지막 Javascript 블록은 데이터 핸들러로 시작하며 쿼리 매개변수로 이 데이터 핸들러가 필요하다.


Listing 7. Javascript 핸들러 코드
            
errorHandler = function(transaction, error){
    updateStatus("Error: " + error.message);
    return true;
}

nullDataHandler = function(transaction, results){
}

중복을 피하기 위해 유틸리티 함수는 Details 양식의 필드(updateForm)와 상태 메시지(updateStatus)를 아래와 같이 채운다.


Listing 8. Javascript 유틸리티 코드
function updateForm(id, amount, name){
    document.itemForm.id.value = id;
    document.itemForm.amount.value = amount;
    document.itemForm.name.value = name;
}

function updateStatus(status){
    document.getElementById('status').innerHTML = status;
}

전개

실제 HTML5 모바일 장치에서 예제를 실행하기 위해 iPhone 3GS와 Palm Pre를 사용했다. 또한, 이 예제는 컴퓨터에 있는 현재의 Safari 브라우저에서도 작동한다.

아래 다운로드 표에서 샘플 애플리케이션을 다운로드하여 이 애플리케이션의 파일을 HTTP 서버에 전개할 수 있다. text/cache-manifest Mime 유형을 사용하여 HTTP 서버에서 명시 파일을 제공해야 한다. iPhone에서 샘플 애플리케이션을 연 다음 책갈피를 저장하고 오프라인 에어플레인 모드로 변경한다. 그러면 책갈피가 선택될 때 샘플 애플리케이션이 열리며 네트워크가 연결되지 않아도 이 애플리케이션은 작동한다.

 

요약

이 기사에서는 오프라인 웹 애플리케이션에 필요한 기술을 집중적으로 다루었다. 간단한 재고 관리 애플리케이션의 프로토타입을 통해, 로컬에서 애플리케이션과 로컬 데이터베이스를 전개하여 HTML5 기술을 실제로 사용했다.

 

참고사이트 : https://www.ibm.com/developerworks/kr/library/wa-offlineweb/

Posted by 아이맥스