년도별 글 목록: 2010

HTML5 API 강좌 #3 – 아이폰 Web App 만들기

이 글은 HTML5 로 아이폰 앱 만들기 라는 제목으로 월간 W.e.b. 에 연재하는 글에서 발췌한 것입니다. ( 잡지에 기고한 글이라 원래 제 블로그의 글과 어투가 다릅니다. )

이 글 전에 먼저 “HTML5 API 강좌 #1 – Web Storage 와 Application Cache” , “HTML5 API 강좌 #2 – Web SQL Database 와 GeoLocation” 글을 읽어보시기 바랍니다.

지난글 까지 HTML5 API 중 Mobile Web App 을 만드는데 중요한 API 들을 살펴보았다. 이번 회에서는 아이폰에서 Web App 을 만드는 방법을 배워보고, 이전 회에서 만든 CheckList App 을 아이폰 웹 앱으로 바꿔보자.

아이폰 Web App 개발하기

현재 Mobile Web App 을 가장 잘 지원하는 것은 아이폰이다. 국내에 출시되지 않았던 아이폰 OS의 1.0 버전 때에는 App Store 가 없었기에 애플 측에서 Web App 의 사용을 권장했었기 때문이고, 아이폰 OS 2.0 버전부터 App Store 가 런칭 되면서 사용용도가 많이 줄기는 했지만, 아직도 애플 웹사이트의 Web App Directory ( http://www.apple.com/webapps )에는 약 5천개 정도의 Web App 들이 등록되어 있다. 현재도 계속 새로운 WebApp 들이 개발되고 추가되고 있으며, iPad의 출시와 함께 큰 화면에서 사용 가능한 웹 앱들도 개발되고 있다. 아이폰 상에서 어떻게 HTML5 Mobile Web App 을 만드는지를 알아보자.

iPhone 환경에서의 Web App 지원

먼저, iPhone 환경에서는 데스크탑용 웹 브라우저와 달리 사이트에 접근하는 방식이 차이가 있기 때문에 특이한 사항이 몇 개 있다. HTML5 지원과는 조금 다른 얘기이지만, HTML5 를 이용한 Mobile Web Application 을 만든다면 꼭 알아둬야 것 중의 하나다.

아이폰 사파리는 웹 사이트에 대해 “홈 화면에 추가 ( Add to Home Screen )” 이라는 기능을 제공한다. 사파리 브라우저에서 맨 아래 + 버튼을 누르면 “책갈피추가 / 홈 화면에 추가” 선택팝업이 뜨고, 이를 통해 아이폰 메인화면에 웹 사이트에 대한 바로가기 기능을 추가 할 수 있다. ( iOS 4.2 에서는 + 버튼에 Print 기능이 추가되면서 화살표 같은 형태로 아이콘이 바뀌었다. )

iPhone WebAppiphone WebApp 2

iPhone WebApp 3iphone webapp 4

추가된 아이콘은 일반 앱과 같은 형태이며, 삭제하는 방식도 똑같다. 클릭하면 바로 모바일 사파리가 실행되면서 해당 웹사이트로 이동하는 URL 바로가기 형태로 보면 된다. 이것은 데스크탑 크롬 웹브라우저의 “웹 애플리케이션 바로가기 만들기” , Mozilla 의 Prism ( Firefox 에선 확장기능으로 제공 ) 과 비슷한 기능이다.

안드로이드 에서도 비슷한 기능을 제공하긴 하지만 단계가 조금 복잡하다.

  1. 홈스크린에 추가하고자 하는 웹 페이지를 Bookmark 한다.
  2. 메뉴 버튼을 눌러서 Bookmarks 페이지로 간다.
  3. 홈스크린에 추가할 북마크를 길게 누른다.
  4. “Add to Home Screen” 을 선택하면 현재 페이지에 추가된다. (빈 공간이 있어야만 가능)

원하는 페이지에 직접 추가하려면 일단 웹페이지를 먼저 북마크 한후 홈스크린으로 간다.

  1. 추가할 홈스크린으로 가서 빈 공간을 길게 누른다.
  2. 나온 “Add to Home Screen” 메뉴에서 Shortcuts > Bookmark 선택
  3. 원하는 북마크 선택

안드로이드의 Add to Home Screen 기능은 단지 북마크를 화면에 추가하는 것일 뿐 추가적인 기능은 제공하지 않는다. 하지만 아이폰 사파리에서 웹 페이지에 대해 이렇게 + 를 눌러 “홈 화면에 추가”후 실행하면 iPhone 에서는 Mobile Web App 으로 동작하게 된다. 이때 Web App 의 지원을 위해 모바일 사파리는 몇 가지 태그를 지원한다.

모바일 웹 앱 개발자는 head 섹션에 아래의 4가지 태그를 추가할 수 있다.

[html]
<link rel="apple-touch-icon" href="/apple-touch-icon.png"/>
<link rel="apple-touch-startup-image" href="/startup.png">
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" />[/html]

각각은 다음과 같은 기능을 지원한다.

  1. <link rel=”apple-touch-icon” href=”/apple-touch-icon.png”/ >

    등록되는 웹 사이트의 아이콘을 지정할 수 있다. apple-touch-icon.png 가 기본 이름

    일반적으로 아이폰에서 웹사이트 아이콘을 추가하게 되면 웹사이트 화면을 캡쳐한 내용을 아이콘으로 사용하는데 apple-touch-icon 이라는 링크를 추가하여 아이콘을 내가 지정한 것으로 사용할 수 있다. favicon 의 아이폰 버전이라고 생각하면 된다.

    아이폰은 57×57 , 아이패드는 72×72, 아이폰4는 114×114 사이즈의 png 이미지를 사용한다. 가능하면 114×114 이미지로 만들어두면 아이폰에서 자동으로 크기 리사이즈를 한다.

    이 아이콘은 기본적으로 아이폰이 제공하는 UI 처리 ( 모서리를 둥글게 하고 반원형의 밝은 부분을 추가해 주는 것) 가 된다. 원하지 않을 때는 rel 속성의 값을 apple-touch-icon-precomposed 라는 이름으로 지정하여 사용하면 된다.

    <link rel=”apple-touch-icon-precomposed” href=”/apple-touch-icon-precomposed.png”/>

    → 이렇게 지정한 precomposed 아이콘 이미지는 안드로이드의 Add to Home Screen 기능에서도 지원된다. 사이즈는 48×48 이다.

  2. <link rel=”apple-touch-startup-image” href=”/startup.png”>

    화면이 로딩될 때 스타트업 이미지를 지정할 수 있다. Web App 이지만 앱 처음 로딩시 로고화면 같은걸 보여줄 수 있다. 아이폰 기본 앱에 들어있는 Default.png 와 비슷한 역할이다.

    단, 이미지의 크기가 정확히 맞아야 한다. 아이폰은 320×460 , 아이폰4는 640×920 , 아이패드는 768×1004 로 정확히 맞춰야만 제대로 화면에 표시된다.

  3. <meta name=”apple-mobile-web-app-capable” content=”yes” />

    Web App으로 선언하여 브라우저의 UI ( URL 바 ) 를 안 보이도록 할 수 있다.

    즉, Web App 이 마치 일반 Native App 처럼 화면 전체 ( 최상단 상태바 20px 제외) 를 활용할 수 있도록 한다.

  4. <meta name=”apple-mobile-web-app-status-bar-style” content=”black” />

    상태바의 색상을 지정할수 있다. 바탕화면이 검정색인 어플리케이션의 경우 상태바만 회색인 이질감을 줄이기 위해 사용한다.

    3가지 스타일 : default (회색) , black , black-translucent ( 반투명 )

이렇게 4가지 기능을 지원함으로써 HTML5 기반의 Local 또는 Online Web Application 들이 마치 Native App 처럼 실행하는 효과를 지원할 수 있다.

아이폰 개발 환경 꾸미기

Web App 은 아이폰 Native App 과 달리 개발환경으로 꼭 Mac을 필요로 하지 않는다. 기존의 웹 개발 할때와 마찬가지로 손에 익은 툴을 이용하여 개발할 수 있다. 아이폰 상에서의 동작 화면을 테스트하기 위해서는 맥의 개발환경인 XCode 에 포함된 iPhone Simulator 가 테스트용으로 아주 적절하지만, Mac 사용자가 아니라면 사용이 어렵다. 또한 Web App 개발시 iPhone Simulator는 디버깅을 하는 용도로는 적합하지 않다.

Safari 의 개발자 도구 활용하기

Webkit 기반의 브라우저 최신버전들에는 개발자 도구가 들어있다. 이를 활용해 보자

Safari 의 메뉴에서 편집->기본설정 또는 “ Ctrl + , “ 를 눌러서 설정창을 연다. 우측 끝 고급탭을 선택하여 맨 밑에 “메뉴 막대에서 개발자용 메뉴 보기” 를 활성화 한다.

사파리 개발자 메뉴 활성화 시키기

활성화 하면 아래와 같이 개발자용 메뉴가 추가된다. 이 개발자 도구는 같은 WebKit 기반인 Chrome 브라우저에도 비슷한 기능이 있지만, Safari 가 더 많은 기능을 가지고 있다.

사파리 개발자 메뉴

개발자용 메뉴중 주요 기능을 몇 개 살펴보자.

  1. 사용자 에이전트

    만들고 있는 웹 사이트를 iPhone / iPad 및 각 브라우저별로 어떻게 표시되는지를 테스트해보기 위해 UserAgent 를 쉽게 바꾸는 기능을 제공한다

    Safari User Agent

    Chrome 이나 Firefox 에서는 확장기능을 설치해야 가능했던 동작인데 기본으로 포함하고 있다. Mobile Safari 로 설정시 거의 아이폰 화면과 비슷한 화면을 보여주기 때문에 아이폰용 Web App 개발후 테스트시 아주 유용하다.

    실제로 아이폰/아이패드의 UserAgent 앞부분은 아래와 같다.

    Mozilla/5.0 (iPhone; U; CPU like Mac OS X; en) AppleWebKit/420+ (KHTML, like Gecko) Version/3.0 Mobile/1A543 Safari/419.3
    Mozilla/5.0 (iPad; U; CPU OS 3_2 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML,like Gecko) Version/4.0.4…
    

    즉, 각 웹페이지 개발시 아래와 같은 방법으로 현재 접속한 브라우저에 맞는 화면을 보여줄 수 있다.

    [javascript light=”true”]Javascript : if((navigator.userAgent.match(/iPhone/i)) { }[/javascript]
    [php light=”true”]PHP : if (strpos($_SERVER[‘HTTP_USER_AGENT’],’iPad’) !== false) { }[/php]

    아래는 사파리 브라우저 사이즈를 아이폰과 비슷하게 맞춘후, UserAgent 를 iPhone 으로 설정하고, 아이폰용 UI 라이브러리인 jQTouch 데모 사이트를 불러온 화면 이다. 거의 비슷한 형태의 화면을 볼 수 있으며, Webkit 애니메이션들도 그대로 볼 수 있다. 아이폰용 Web App 을 만든다면 꼭 알고 있어야 할 필수 테스트 방법이다.

    jQtouch on Safari

  2. 웹 속성보기 – Web Inspector ( Ctrl – Alt – I , ⌥ – ⌘ – I )

    현재 웹 페이지의 상세 속성을 보는 창이다. 앞서 HTML5 API 를 설명할 때 잠깐 설명한 기능으로 조금 자세히 살펴보자.

    Safari Web Inspector

    • 요소 : 현재 페이지의 HTML 소스를 보여준다.
    • 리소스 : 현재 페이지에서 로딩한 리소스 파일들 ( CSS, JS , 이미지 등 ) 및 각 리소스의 로딩에 걸린시간을 보여준다.
    • 스크립트 : 현재 페이지내의 Javascript 소스를 확인하고 디버깅 할 수 있다.
    • 타임라인 : 리소스 로드/스크립트수행/렌더링 시간들의 타임라인을 볼 수 있다.
    • 프로파일 : 현재 페이지내의 스크립트가 CPU 자원을 얼마나 소모하는지 프로파일링
    • 스토리지 : Database , Local/Session Storage , Cookie 들을 일목요연하게 볼 수 있다.
    • 콘솔 : 에러 확인 및 자바스크립트 실행이 가능한 커맨드 라인 창

    마치 Firefox 에서의 Firebug 플러그인을 보는 것처럼 잘 만들어져 있다.

아이폰 브라우저 화면 & Viewport 알기

모바일 웹 개발시 브라우저의 화면에 대해 먼저 알아둘 필요가 있다. 아이폰/아이패드의 브라우저 화면에 대해 알아보자.

iPhone Viewport

아이폰/아이패드 공히 상단 20px 의 상태바와, 60px 의 URL 바 를 가지고 있다. 그리고, 실제 보이는 View Port는 아이폰의 경우 아무것도 지정안할 경우 980px 로 세팅되어있다. 즉 모바일웹에 대응안한 웹 사이트 접속시 980px 이내라면 축소되어서 한 화면에 보이도록 되어있다. 아이패드는 일반 웹브라우저와 같다.

만드는 모바일 웹앱이 화면에 정확하게 맞도록 하려면 위에 보이는 것처럼 태그를 이용하여 viewport 의 width 를 device-width로 맞추면 된다. 또한 사용자가 확대를 할 수 있도록 할지 말지를 user-scalable 로 조정이 가능하다. 일반적으로 사진확대가 필요한 페이지가 아니라면 no로 지정하면 웹 앱 사용시 터치하여 이동이 편하게 된다.

이 device-width viewport 옵션은 안드로이드에서도 동일하게 동작된다.

앞에서 태그에 대해 설명했다. 이 태그를 추가한 후에 Add to Home Screen 을 한 Web App 은 다시 실행했을 때 앞에서 말한 URL Bar 와 Button Bar 를 모두 제거하고 아래처럼 전체 화면인 320x460px 를 웹 앱에서 쓸 수 있다. ( 아이폰 4에서는 640x920px 로 정확히 가로세로 2배로 확대 되었다고 보면 된다. )

iphone web app viewport

이렇게 지정 했을 경우 URL Bar 가 아예 없어지기 때문에, 사용자는 현재 웹페이지만 사용가능하고 다른 페이지로 브라우징이 불가능하게 된다. 즉 독립된 앱 처럼 사용이 가능하다. 작성중인 Mobile Web App 이 다른 페이지로 갈 이유가 없는 앱이라면 이 방식으로 해주면 된다.

만약 개발중인 Web App 이 URL Bar 가 필요는 하지만, 화면에 꼭 보일 필요가 없다면 앞에 Meta 태그 대신 아래의 자바스크립트를 사용하여 자동으로 URL Bar 를 스크롤 시킬 수 있다.

[javascript light=”true”]
window.addEventListener(‘load’, function(){
setTimeout(scrollTo, 0, 0, 1);
}, false);
[/javascript]

CheckList 를 아이폰 앱으로 바꾸기

이전 회에서 만들었던 Checklist App 를 좀더 아이폰 앱 처럼 변경해보자. HTML 파일을 다음과 같이 변경한다.

[html]
<!DOCTYPE html>
<html manifest="webapp.manifest">
<head>
<meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1.0"/>
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
<link rel="apple-touch-icon" href="iphone_icon.png"/>
<link rel="apple-touch-startup-image" href="startup.png" />
<script src="app.js"></script>
<script>
window.addEventListener(‘load’, function(){
setTimeout(scrollTo, 0, 0, 1);
}, false);
</script>
<title>Checklist WebApp ( Offline )</title>
</head>
<body>
<div id="checklist">
<div id="add">
New Check List <input type="text" id="description"/>
<input type="button" value="Add" id="addbutton" onclick="newToDo();"/>
</div>
<div id="items">
</div>
[/html]

앞에서 본 viewport 및 meta 태그들을 모두 추가한다.

[html light=”true”]<meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1.0"/>[/html]
Viewport 를 Device 크기에 맞게 고정하고, 스크롤이 불가능하게 설정한다.
[html light=”true”]<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" />[/html]
Add To Home Screen 했을 경우, URL Bar 를 없애고, 상태바를 검은색으로 보이게 한다.
[html light=”true”]<link rel="apple-touch-icon" href="iphone_icon.png"/>[/html]
Add To Home Screen 할 때 보일 아이콘을 지정한다.
[html light=”true”]<link rel="apple-touch-startup-image" href="startup.png" />[/html]
Add To Home Screen 한 후에 실행시 보일 Start Up 이미지를 지정한다.
[javascript light=”true”]<script>
window.addEventListener(‘load’,
function(){ setTimeout(scrollTo, 0, 0, 1);}
, false);
</script>[/javascript]
기본으로 웹페이지가 보일시에도 자동으로 URL Bar 가 가려지도록 한다. 단, 이 코드는 아이템리스트가 화면크기보다 많을 경우, 즉 페이지가 아래로 스크롤이 가능할 때에만 자동으로 실행된다.

이렇게 모두 수정하고 iphone_icon.png 와 startup.png 를 같은 폴더에 업로드 한다. 실행화면은 다음과 같다.

iPhone Checklist WebApp 1

Add to Home Screen 할 때 위와같이 지정한 아이콘 이미지가 보인다.

iPhone Checklist WebApp 2

이렇게 추가된 아이콘으로 실행시 지정한 startup.png 이미지가 보이게 된다.

iPhone Checklist WebApp 3

실행하고 나면 위와 같이 URL 바가 모두 사라지고, 상태바만 남은 형태로 사용할 수 있게 된다.

이렇게 해서 모바일 Web App 를 아이폰에 맞는 형태로 만들어보았다. 다음 회에서는 터치기반의 모바일 UI 를 만들어 볼 것이다.

.

HTML5 API 강좌 #2 – Web SQL Database 와 GeoLocation

이 글은 HTML5 로 아이폰 앱 만들기 라는 제목으로 월간 W.e.b. 에 연재하는 글에서 발췌한 것입니다.
( 잡지에 기고한 글이라 원래 제 블로그의 글과 어투가 다릅니다. )

이 글 전에 먼저 “HTML5 API 강좌 #1 – Web Storage 와 Application Cache” 글을 읽어보시기 바랍니다.

지난 글에서는 모바일에서 HTML, CSS, JS 와 이미지파일을 캐슁할 수 있는 Application Cache 와 브라우저에 Key Value 값을 저장할 수 있는 Web Storage ( LocalStorage , Session Storage ) 를 사용하는 법을 배웠다. 이번 회에서는 데이터베이스 형식을 사용하여 좀 더 다양한 데이터를 저장할 수 있는 Web SQL Database 와 GeoLocation API 의 사용방법을 알아보자

Web SQL Database – http://dev.w3.org/html5/webdatabase/

이름에서 의미하듯이 Web SQL Database 는 클라이언트인 웹 브라우저에 Database 엔진을 심어서, 로컬에서 자바스크립트로 사용할 수 있도록 하는 것이다. 즉 예전에는 Database는 웹 서버 뒷 단에서 구조화된 데이터를 저장하고 웹 서버에서 이를 조회하여 페이지 구성 후 브라우저에서 불러다 보여주는 용도로만 사용했는데, 이제는 클라이언트(브라우저) 에도 이런 데이터를 저장할 수 있게 된 것이다. 이는 Offline 기능 지원 및 속도향상을 위해 다양하게 사용될 수 있다.

Web SQL Database의 실제 구현에는 SQLite 데이터베이스를 사용하고 있다. SQLite는 소스가 완전 공개된 Public Domain 오픈 소스 ( 소스코드를 가져다 마음대로 수정해서 사용해도 되고 상용으로 써도 아무런 문제가 없다 ) 로 iPhone OS 및 Android OS 에서도 기본 데이터베이스 형식으로 사용되고 있으며 간단한 Embedded Database를 구현할 때 많이 사용되는 오픈소스이다.

이를 이용해서 개발자는 브라우저 내에 테이블을 구성하고 데이터를 추가/수정/삭제할 수 있으며, 안정성을 보장하기 위해 일련의 작업을 하나의 논리적 작업으로 그룹화 해주는 트랜잭션 메커니즘까지도 지원된다.

현재 Webkit 기반 브라우저 (Safari, Chrome 및 iPhone, Android 의 브라우저) 에서는 이 Web SQL Database 를 지원하고 있지만, Firefox 및 IE 에서는 지원이 안되고 있다. 이것은 현재 HTML5 스펙을 작성하고 있는 W3C 에서 SQLite에 종속적인 SQL 언어기반의 Database 기술을 사용하는것이 웹 개발 형식과는 잘 맞지 않아서, 이 대신 B-Tree 기반의 key/value 저장소인 IndexedDB를 지원하는 것으로 가닥을 잡고 있기 때문이다. 즉 현재 모바일 브라우저들은 다 지원을 하기는 하지만, 실제 HTML5 표준스펙에서는 빠질 것으로 보인다. 하지만 현재로선 Firefox 4 베타버전을 제외한 모든 데스크탑/모바일 브라우저에서는 IndexedDB 지원이 불가능한 상태 이기 때문에, 지금은 모바일 웹 앱 개발시 Web SQL Database를 지원하도록 작성하고, 차후에 IndexedDB 지원이 완벽해지면 그때 다시 수정을 하는 방식으로 개발을 진행하는 방법이 좋을 것이다.

( 참고로 IndexedDB 의 오픈소스 구현체는 http://code.google.com/p/indexeddb/ 에서 받아서 사용해 볼수 있다. )

Web SQL Database 의 가장 중요한 메소드는 아래 3가지 이다.

  • openDatabase : 데이터 베이스 열기
  • transaction : 트랜잭션 시작
  • executeSql: SQL 문장 실행

각 함수를 좀더 자세히 살펴보도록 하자. 각 함수의 선언부에서 { } 는 생략이 가능한 인자를 의미한다.

Web SQL Database 는 스펙상 Asynchronous/Synchronous ( 비동기/동기 ) 두 가지 방식을 다 지원하지만, 현재 브라우저의 구현체들은 주로 Async 모델만을 지원한다. 즉 메소드의 리턴값으로는 적절한 객체나 결과를 얻을 수 없고, 콜백 함수를 메소드 인자로 전달하고 작업이 끝난 후 이 콜백 함수가 호출되는 형식으로 처리되어야 한다.

openDatabase

Database openDatabase(name, version, displayName, estimatedSize {, creationCallback } );

openDatabase 는 데이터베이스를 열거나 생성하는데 사용된다. 데이터베이스가 없다면 자동으로 생성된다. 이 메소드는 4개의 필수 인자와 1개의 옵션 인자를 가지고 있다.

  1. name : 데이터베이스 이름
  2. version : 버전 번호
  3. displayName : 데이터베이스 설명
  4. estimatedSize : 예상 크기
  5. { creationCallback : 생성후 호출될 콜백함수 }

버전번호는 약간 설명이 필요하다. 각 데이터 베이스는 버전번호를 가질 수 있는데, 개발자는 이 버전번호를 통해 데이터베이스의 스키마 변경 등의 작업을 지정할 수 있다. 단, 각 버전이 따로 존재 하는 것이 아니라, 항상 DB는 한 개의 버전만을 가질 수 있다. ( 실제로는 openDatabase 를 호출할 때 이 version 이 필수인자이기 때문에, DB 버전 업데이트를 자동으로 처리하기가 좀 곤란하다. 보통은 한 개의 버전번호를 고정적으로 붙여서 데이터베이스 오픈은 항상 가능하게 한 뒤, 내부에 관리자용 테이블을 만들어 데이터베이스의 스키마 버전번호를 저장하거나 하는 방식을 사용하기도 한다. )

예상크기는 데이터베이스의 크기를 결정하는 것으로, 일반적으로 브라우저 구현에 따르지만 기본값은 5MB로 되어있다. 예상크기가 5MB 보다 작다면 아무것도 묻지 않고 그냥 생성된다. 처음부터 5MB 보다 큰 예상크기를 요청하면 아래처럼 브라우저가 displayName 을 이용하여 이 크기의 디스크 공간을 사용해도 되는지를 사용자에게 묻는다. ( 사파리 브라우저만 이 확인창이 보이며 크롬 브라우저에서는 묻지 않고 그냥 생성한다. )

201011171529.jpg

아이폰 ( 모바일 사파리 ) 에서도 똑같이 확인창이 뜨게 된다.

201011171530.jpg

위의 DB 생성을 시도한 코드는 동일한 것으로 다음과 같다.

var db = openDatabase(‘mydb’, ‘1.0’, ‘guru test db’, 10 * 1024 * 1024);

데스크탑용 사파리와 모바일 사파리 둘 다, 정확히 10MB 의 크기를 요청하였지만 실제 생성되는 디스크공간이 차이가 있다. 이것은 브라우저에 따라 구현상의 차이가 있기 때문이다. 주의할 점은 데스크탑용 사파리에선 500MB, 1GB 까지의 데이터베이스도 생성이 가능하지만, 아이폰의 경우는 50MB 가 넘어가는 크기를 요청할 시 에러가 나면서 openDatabase 가 null 을 리턴한다. 안드로이드의 경우는 확인창이 뜨지않아서 정확히 체크는 불가능하지만 아이폰과 비슷할 것이므로, 모바일 웹 앱 개발시에는 무조건 DB 사이즈를 50MB 이하로 활용하도록 한다. ( 안정적으로 하려면 10메가 이상은 안 쓰는 것이 좋다. 그리고, 이것은 모바일 브라우저의 스펙에 따라 언제라도 바뀔 수 있다. )

마지막의 옵션인자인 creationCallback 은 데이터베이스가 생성될 때 호출되는 함수이다. 처음 데이터베이스가 생성 되었을 때 꼭 실행되어야 할 테이블 생성 스크립트 같은걸 처리하면 된다.

transaction / readTransaction

void transaction(callback {, errorCallback, successCallback} );

void readTransaction(callback {, errorCallback, successCallback} );

Web SQL Database 는 트랜잭션 메커니즘을 제공한다. 하나의 트랜잭션 안에서 여러 개의 SQL 문장을 실행하다가 에러가 발생했을 시에 앞에 했던 동작들을 모두 없었던 것으로 처리함으로써, 복잡한 데이터베이스 작업 도중에 발생한 에러로 데이터베이스에 문제가 생기는걸 방지해 준다. 또한 errorCallback 과 successCallback 인자를 통해 트랜잭션 전체의 에러/성공 시에 호출될 콜백함수도 지정가능하다.

transaction 함수는 보통 아래 형태로 호출된다.

var db = openDatabase(‘mydb’, ‘1.0’, ‘guru test db’, 10 * 1024 * 1024);
db.transaction(function (tx) {
   // 트랜잭션 내부
   // 롤백을 위해 SQL 함수는 tx 객체를 이용한다.
})

그리고 readTransaction 함수는 transaction 과 호출방식은 똑같지만, 트랜잭션내부에서 데이터베이스 쓰기 동작은 불가능하고 오로지 읽기 동작만 가능한 트랜잭션을 만들어준다.

executeSql

void executeSql(sqlStatement {, arguments, callback, errorCallback } );

executeSql 은 실제로 SQL 문장을 실행하는 함수로 일반적으로 Transaction 을 사용할경우 아래와 같은 형태로 호출된다.

var db = openDatabase(‘mydb’, ‘1.0’, ‘guru test db’, 10 * 1024 * 1024);
db.transaction(function (tx) {
    tx.executeSql(‘CREATE TABLE test (id INTEGER PRIMARY KEY, content TEXT)’);
})

4개의 인자가 있는데, 뒤의 2개는 성공/실패시 콜백함수를 지정하는 인자이며, 2번째 인자인arguments 는 SQL 문장에 있는 ? 문자들을 치환한 변수들의 배열이다. 즉 일반적인 INSERT 문의 경우 다음처럼 사용된다.

tx.executeSql( ‘INSERT INTO test (id,content) VALUES( ?, ? )’, [ id , contentStr ] );

SQL 문장을 + 와 같은 연산자를 이용한 스트링 연산으로 동적으로 만들 수도 있지만, SQL Injection 이라고 불리는 기법에 의해 해커의 표적이 되기 쉽다. 위와 같이 파라미터 치환자를 이용한 방식으로 하는 것이 훨씬 안전하다.

executeSql 의 3번째 인자인 Callback 에는 SQL 문장 실행후 transaction 객체와 Resultset 이 리턴된다.

tx.executeSql( ‘SELECT * FROM test’, [ ] , function ( tx, results) {
   for ( var i = 0 ; I < results.rows.length ; i++) {
      document.write(results.rows.item(i).id + ‘ - ‘ + results.rows.item(i).content);
   }
} );

Resultset 은 insertId , rowsAffected, rows 3개의 속성을 가지고 있다. insertID에는 실행한 SQL 문장이 INSERT 문 일 경우에 추가된 데이터의 Row ID 가 리턴되며, 만약 여러 개의 행이 추가되었을경우엔 마지막 ID가 리턴된다. rowsAffected 는 실행한 SQL 문장이 UPDATE 문 일경우에 변경된 Row 수가 리턴된다. rows 는 SELECT 문장일 경우 선택된 데이터들이 item 배열로 리턴 ( 실제로는 배열이 아니다. item 은 getter 함수일 뿐이다 ) 되며 length 를 통해 데이터의 개수를 알 수 있다. 위의 예제에서 보는 것처럼, test 테이블의 id 와 content 필드를 item(i).id , item(i).content 와 같이 각 테이블의 컬럼 이름을 이용해서 직접 item 객체의 속성인 것처럼 꺼내서 사용할 수 있다.

Web SQL Database 는 openDatabase, transaction , executeSql 세개의 함수만으로 로컬 브라우저상에서 유용하게 사용할 수 있는 데이터베이스를 제공한다. 실제로 Gmail 의 모바일버전 사이트는 이 Web SQL Database 를 사용하여, Offline 시에도 Inbox 의 최근 메일들을 보거나, 새로운 메일 작성 등을 가능하게 하고 있다.

Web SQL Database 역시 이전회에서 살펴본 LocalStorage 처럼 Safari Browser 의 Web Inspector 나 크롬 브라우저의 Developer Tools 를 통해 테이블과 그 내용들을 확인할수 있다.

201011171532.jpg

Chrome : Ctrl+Shift+J (윈) 또는 Command+Option+J (맥) 을 눌러 Developer Tools 실행

Safari : Ctrl+Alt+I (윈) 또는 Command+Option+I (맥) 을 눌러 Web Inspector 실행

GeoLocation API

HTML5 의 API 중 모바일환경에서 가장 많이 사용되고 있는 API 는 GeoLocation API 이다. 최근의 스마트폰들이 대부분 GPS를 내장하고 있어서, 모바일 웹에서도 위치정보에 기반한 구글맵과 같은 다양한 웹 서비스들이 이용되고 있다. 실제로 GeoLocation API 는 구글에서 제안한 것으로 HTML5 표준이 아니라 Geolocation API 라는 다른 스펙에서 정의되고 있다.

GeoLocation API 는 3개의 메소드로 이루어져 있다.

void navigator.geolocation.getCurrentPosition ( successCallback , errorCallback, options );
long navigator.geolocation.watchPosition ( successCallback , errorCallback, options );
void clearWatch ( watchID )

브라우저상에서 위치정보를 얻는 방법은 2가지인데, 첫번째는 getCurrentPoisition 함수를 이용하여 한번만 얻어오는 것이고, watchPosition 은 브라우저(디바이스)가 판단하여 위치가 바뀌었을 때마다 계속적으로 콜백 함수를 불러주는 방법이다. 전자의 방법은 주로 현재 위치의 지도를 보여주거나 근처의 POI ( Places of Interest ) 들을 불러올 때 사용하며, 후자는 사용자가 움직인 거리들을 기록하거나 지도에서 계속적으로 위치를 바꾸면서 보여주고 싶을 때 사용한다.

보통은 다음과 같은 형태로 호출한다.

navigator.geolocation.getCurrentPosition( show_position , show_error ) ;

성공시에 호출되는 successCallback 함수는 다음과 같은 위치정보를 리턴한다.

function show_position ( position ) {
var lat = position.coords.latitude;
var lon = position.coords.longitude;
}

이 위도경도 값으로 지도 API 를 호출하면 된다. 실제로 coords 는 altitude ( 고도 ) , heading ( 방향 , 360도 ) , speed ( 속도 m/s ) 와 같은 몇 개의 속성을 더 가지고 있지만, 이것은 모바일 브라우저에 따라 지원될 수 도 있고 안될 수도 있다. ( 지원 된다고 해도 부정확할 수 있다 ) 위도 경도값은 꼭 GPS 정보 뿐만이 아니라 모바일 브라우저 구현에 따라 IP 주소, WiFi/블루투스의 MAC 주소, GSM/CDMA 의 셀타워 정보등으로부터 위치정보를 얻게 된다.

Web SQL Database 를 이용한 Offline 실행지원 어플리케이션 - CheckList

이제 앞에서 배운 Application Cache 와 Web SQL Database 를 활용해서 Offline 에서도 실행이 가능한 Application 을 만들어 보자. 만들어볼 CheckList Application 은 모바일에서 활용가능한 간단한 체크리스트이다.

  • Application Cache 적용을 위해 Manifest 를 지정한다.
  • Web SQL Database 를 이용하여 아이템을 저장한다.

* 이 예제는 http://www.berttimmermans.com/2009/02/checklist/ 의 CheckList Web Application을 좀 더 간략하게 만든 것이다.

먼저 Offline 에서도 실행 가능한 Web App 의 HTML 내용은 다음과 같다.





Checklist WebApp ( Offline )


New Check List

앞서 설명한대로 간단한 체크리스트를 Offline 에서 등록/관리 하는 Application 이다. 추가버튼만 있고 모든 아이템은 items 라는

div 에 표시되도록 되어있다. 주요 기능은 app.js 에 있다. app.js 를 살펴보자.

// BUILD DATABASE ———————————————————————
var db;
// 데이터베이스를 오픈한다. 사이즈는 넉넉히 20만바이트로 지정한다.
try {
    if (window.openDatabase) {
        db = openDatabase(“Checklist”, “1.0”, “HTML5 Database API”, 200000);
        if (!db)
        alert(“DB를여는데실패했습니다.버전이틀리거나이도메인에할당된공간이꽉찻기때문입니다”);
        else
        var highestId = 0;
    } else
    alert(“Web SQL Database가지원되지않는브라우저입니다.”);
} catch (err) {
}
function loaded() {
    // 페이지가 로드되면 테이블이 있는지 확인하여 없으면 새로 테이블을 생성한다.
    // 이미 생성한 경우라면 BuildList() 함수를 호출하여 리스트에 아이템을 채운다.
    db.transaction(function (tx) {
        tx.executeSql(“SELECT COUNT( * ) FROM ToDos”, [], function (result) {
            BuildList();
        }, function (tx, error) {
            tx.executeSql(“CREATE TABLE ToDos(id REAL UNIQUE, description TEXT, status REAL)”, [], function (result) {
                BuildList();
            });
        });
    });
}
// 페이지 로드후 이벤트에 등록한다.
addEventListener(‘load’, loaded, false);
// 새 아이템 입력 —————————————————————————————
function newToDo() {
    if (document.getElementById(‘description’).value != “”) {
        highestId++;
        var description = document.getElementById(‘description’).value;
        var status = 0;
        // INSERT 문으로 테이블에 새 아이템 추가
        db.transaction(function (tx) {
            tx.executeSql(“INSERT INTO ToDos(id, description, status) VALUES(“ + highestId + ”, ‘” + description + ”’, ” + status + ”)”, [],
            function (result) {
                document.getElementById(‘description’).value = “”;
                BuildList();
            }, function (tx, error) {
                alert(error);
            }
            );
        });
    }
}
// 아이템 삭제 —————————————————————————————
function deleteToDo(id) {
    // DELETE 문으로 테이블에서 아이템 삭제
    db.transaction(function (tx) {
        tx.executeSql(“DELETE FROM ToDos WHERE id = ” + id + “;”, [],
        function (result) {
            document.getElementById(id).style.display = “none”;
        }, function (tx, error) {
            alert(error);
        }
        );
    });
}
// CHANGE STATUS —————————————————————————————
function updateToDo(id, status) {
    if (status == ‘1’) {
        status = ‘0’;
    } else {
        status = ‘1’;
    }
    // 해당 체크리스트 아이템을 수행했는지 안했는지를 체크한다. 체크한 아이템은 흐리게 만든다.
    db.transaction(function (tx) {
        tx.executeSql(“UPDATE ToDos SET status = ” + status + ”WHERE id = ” + id + ”;”, [],
        function (result) {
            if (status == ‘1’) {
                document.getElementById(id + ”box”).removeAttribute(“onclick”);
                var newfunction = document.createAttribute(“onclick”);
                newfunction.nodeValue = “updateToDo(” + id + “, ” + status + “)”;
                document.getElementById(id + ”box”).setAttributeNode(newfunction);
                document.getElementById(id).style.opacity = ‘0.2’;
            }
            if (status == ‘0’) {
                document.getElementById(id + ”box”).removeAttribute(“onclick”);
                var newfunction = document.createAttribute(“onclick”);
                newfunction.nodeValue = “updateToDo(” + id + “, ” + status + “)”;
                document.getElementById(id + ”box”).setAttributeNode(newfunction);
                document.getElementById(id).style.opacity = ‘1’;
            }
        }, function (tx, error) {
            alert(error);
        }
        );
    });
}
// BUILD LIST —————————————————————————-
function BuildList() {
    // DB 에서 전체 체크리스트 아이템을 읽어온다.
    document.getElementById(‘items’).innerHTML = “”;
    db.transaction(function (tx) {
        tx.executeSql(“SELECT id, description, status FROM ToDos”, [], function (tx, result) {
            for (var i = 0; i < result.rows.length; ++i) {
                var row = result.rows.item(i);
                ToDo(row[‘id’], row[‘description’], row[‘status’]);
                if (row[‘id’] > highestId)
                highestId = row[‘id’];
            }
        }, function (tx, error) {
            alert(‘DB에서아이템을읽어오는데실패했습니다 - ’ + error.message);
            return;
        });
    });
}
// TODO ————————————————————————————-
function ToDo(id, description, status) {
    // 각 아이템 등록
    var ToDoItem = “ < div id = ’” + id + “’class = ’part“;
    if (status == 1) {
        ToDoItem += ”done”;
    }
    ToDoItem += “’ > < input type = ’checkbox’”;
    if (status == 1) {
        ToDoItem += “checked = ’checked’”;
    }
    ToDoItem += ”onclick = ’updateToDo(” + id + “, ” + status + “)’id = ’” + id + “box’class = ’checked’ / > ”;
    ToDoItem += ” < span > ” + description + “ < /span>”;
ToDoItem +=”
 < /div>
 “;
document.getElementById(‘items’).innerHTML = document.getElementById(‘items’).innerHTML + ToDoItem;
}

그리고, 오프라인 실행을 위해 webapp.manifest 를 만든다.

CACHE MANIFEST app.js # 캐시를 선언한 index.html 은 자동으로 추가되므로 적지 않아도 된다. # Version 20100905-054

실행한 화면은 다음과 같다. 한번 온라인상태에서 로딩만 되면 오프라인시에도 동작된다.

201011171533.jpg 201011171533.jpg

우측의 화면은 아이폰을 Airplane 모드로 바꾸어서 오프라인에서도 동작하는지를 테스트해본 것이다.

현재 실행에 필요한 파일은 index.html 파일과 app.js 파일 2가지 밖에 없다. webapp.manifest 파일에 의해 index.html , app.js 두 개의 파일이 캐쉬되므로 webapp.manifest 파일이 수정되지 않는한 index.html 이나 app.js 의 수정내용이 변경되지 않는다는 것을 명심하자. 코드를 업데이트 하였다면 webapp.manifest 파일의 마지막 #주석문에 버전번호를 바꾸어주도록 하자.

다음 회에서는 아이폰용 Web App 을 만드는 방법에 대해 알아보자.

.

아이폰이 내 삶을 바꿨다 : 시각장애인 Austin 의 아이폰 사용기

이 글은 시각장애인인 Austin Seraphin 의 블로그 Behind the Curtain 에 2010년 6월 12일자로 올라온 My First Week with the iPhone 을 번역한 글입니다. 읽으면서 저도 느낀게 많고, 국내의 아이폰 앱 개발자님들, 휴대폰 및 기타 전자기기 플랫폼을 개발하는 분들, 나아가 모든 소프트웨어 개발자분들이 한번 꼭 읽으셨으면 해서 추석연휴동안 영문글을 하나 번역해 봤습니다. 물론 오역도 있을수 있으니 감안해서 읽어주세요 😉

My First Week with the iPhone : 아이폰과 함께한 나의 첫 일주일

지난 수요일, 내 삶은 완전히 바뀌었다. 내가 아이폰을 구입한 것이다. 난 아이폰이 매우 오랫동안, 아마도 영원히, 시각장애인들에게 일어날수 있는 가장 위대한 것이라고 생각한다. 아이폰은 적절하게 만들어진 어플리케이션들에 다른것과 견줄수 없는 접근 방법을 제공하였고, 내 삶은 24시간만에 바뀌어 버렸다. 아이폰은 단 한가지의 문제점을 가지고 있다: iTunes. 하지만 그럼에도 불구하고, 난 사랑에 빠져 버렸다.

처음에 애플이 VoiceOver 기능(Mac 에서 사용하던 화면을 읽어주는 소프트웨어) 을 탑재한 터치패드 휴대폰을 발표한다고 들었을때, 난 비웃었다. 지금까지 시각장애인들은 값비싼 하드웨어위에서  최소한의 기능만 수행하는 스크린 리더와 대충만든 소프트웨어들때문에 절대 망할수 밖에 없었던 꿈의 플랫폼들의 오만한 약속에 익숙해져 있었다. 내가 생각하기에 Apple 은 단지 좋은 PR을 원했던 것이다. 어떻게 시각장애인이 터치패드를 쓸수있단 말인가 ? 난 시각장애가 없는 사람도 있는 사람도 아이폰을 구입하고, 그에 대해 열광하는 트렌드를 비웃었다.  그러다가, 나와 같은 의견을 가지고 있던 시각장애인 친구가 아이폰을 하나 구입하고, 그녀가 아이폰 그중에서도 특히 터치패드 인터페이스에 대해  미칠정도로(go nut) 그걸 사랑하게 되었는 지를 알게된 후 생각이 바뀌었다. 난 그걸 도저히 믿을수 없었고, 재평가 해봐야 겠다고 생각했다.

난 엄마와 함께 AT&T 스토어로 갔다. 이건 마치 오래전 애플스토어에 Apple II/E 를 구입하러 갔을때 이후로 원점으로 돌아온듯한 기분이었다. 기쁘게도, 점원은 VoiceOver 에 대해 알고있었고, 어떻게 활성화시키는지를 알고있었다. 물론 어떻게 사용하는지는 몰랐지만.. 다행히 내가 그곳에 가기전 사용법을 읽어두었다. 아이템을 탭 하면 들을수 있고, 더블 탭 하면 실행하는 것이고, 세 손가락을 Swipe 하면 스크롤하는것이다.  또한 Split-Tap 도 가능한데, 화면의 한점을 누르고 다른 한점을 누르는것이다. 이것은 이해만 한다면 훨씬 빠른 입력이 가능하게 한다. ( 역자 주: Split-Tap 은 더블탭의 대용으로 한번탭해서 소리로 아이템을 들은후, 그걸 탭한 상태에서 다른 한점을 더 탭하는것으로 입력이 되게 하는것이다. 탭한후 입력을 위해 더블탭할때 위치오차가 날 수 있으므로 훨씬 편하면서 정확한 입력방식이다. ) 또한 손가락들을 다이얼처럼 돌림으로써 Rotor 라는 기능을 활성화 할수 있다. ( 역자 주: Rotor 는 컨텐츠 형식에 맞게 아이템의 내비게이션 방식을 선택하게 하는 동작으로 웹페이지의 경우 헤더,이미지,방문한링크,방문안한링크 등을 선택가능하게 한다. ) 또한 두번의 트리플-핑거 탭을 함으로써 음성을 켜고 끌수 있고, 세번의 트리플-핑거 탭을 하면 화면과 카메라를 비활성화 하는 기막히게 멋진 Screen Curtain 기능을 켜게된다. ( 역자 주 : Screen Curtain 은 보안을 위해 다른사람은 보지못하게 화면을 끄고, VoiceOver 및 터치와 음성으로만 제어할 수 있는 옵션이다. )

많은 리뷰와 사람들이 말하길 터치패드 인터페이스를 음성으로 사용하는것에 대해 평가하는것은 적어도 30분에서 한시간은 써봐야 한다고 얘기했다. 난 기이하고 약간은 힘든 여행을 예상했다. 특히나 키보드를 사용하는 부분에 있어서.. 놀랍게도 난 즉시 그것을 들었다. 30초안에 난 날씨를 체크했다. 그리고, 난 어떤 주식의 가격을 읽었다. 놀라운건, 아이폰이 주식 차트도 표현해 주었다. 시각장애인은 한번도 접해보지 못했었던.. 구입하자. ( 원문: “Sold.” 역자주: Sold 는 그냥 I’m convinced. I’ll buy it 의 의미로 보면 됩니다.)

그 다음 필요한 몇개의 준비를 진행했다. 데이터 플랜을 사야했다. 운좋게도 6/7 일에 종료되는 $30/무제한 플랜을 받을수 있었다. ( 역자 주 : AT&T 의 무제한 데이터 플랜이 올해 6/7 일 이후 가입자들에게는 제공되지 않습니다. ) 몇몇 작업후 모든게 완료되었다. 난 신나서 계속 질문을 해나갔고, 우리 엄마도 그랬다. “이 아이가 이걸로 문자 메시지를 받을수 있나요?” 그녀가 물었다. “음, 네. 하지만 메시지를 읽어주진 않습니다.” 점원이 말했다. 엄마의 희망이 가라앉았지만, 난 그렇지 않았다. 내가 소프트웨어를 잘 이해하고 있으므로.. “음, 한번 해보죠, 시도해봐요” 내가 제안했다. 엄마는 당신의 폰을 꺼내들고, 나에게 문자 메시지를 보냈다. 몇초안에 내 폰이 나에게 알림을 줬고, 그녀의 이름을 말해줬다. 난 간단히 내 손가락을 Swipe 했고, 아이폰이 엄마의 메시지를 읽어주었다: Hi Austin. 엄마는 거의 울뻔했다. “애플에 전해주세요” 내가 말했다. “이건 마치 우리가 처음으로 애플스토어에 갔을때와 마찬가지로 놀랍습니다.” 그렇다, 80년대에는 컴퓨터는 진기한것이었다. 난 내 부모님이 Apple II/E 를 가지고 주식을 확인하고 업무관련 메시지를 받던것을 기억한다. 이젠 우리 주머니에 들어가는 애플 디바이스를 가지고 그 일을 할수 있다.

난 시각장애인용 많은 기술을 봐왔지만, 난 아이폰이 시각장애인들에게 지난 10년간 일어난 일중 가장 혁명적인것이라고 틀림없이 얘기할수 있다. 15년 또는 20년전이라면 같은 방식으로 좋아했던 ‘Braille ‘n Speak’ 를 기억나게 해서 어떤것이 더 뛰어난지를 고민하게 한다. (역자 주 : Braille ‘n Speak 는 점자로 입력하면 소리로 읽어주고 편집이 가능한 휴대용 점자 타자기 라고 백남중님이 알려주셨습니다. ) 나의 많은 기쁜 순간들중에서 아이폰이 시각장애인에게 일어날수 있는 가장 큰일이라고 평가하고, 그렇게 입증될것이라고 믿는다. 시간이 말해줄것이다. 음성은 1차원적인 결과만을 제공하지만, 터치패드는 시각장애인에게 필요했던 친숙한 이전/다음 모션을 제공한다. 스크린의 어디나 터치하고, 들을수 있는 기능을 추가하는것은 완전히 새로운 차원이다. 처음으로 시각장애인들이 어떤것에 대한 공간적인 정보를 얻을수 있었다. 스토어에서 엄마는 “저 버튼도 해봐” 라고 말할수 있고, 난 그걸 할수 있었다. 시각장애인들은 내가 무엇을 말하는지 잘 알것이다. 시각을 가진 사람이 “화면 맨 위의 아이콘이 보여요?” 라고 말하는걸 얼마나 많이 들었는가. 이젠 그것이 정말 어떤걸 의미하게 되었다. 난 내 컴퓨터의 터치패드를 가지고 웹을 볼수 있는 방법을 찾길 원했다. 이건 정말 미래의 물결을 보여준다.

어플리케이션들은 어떤 그래픽 환경에서도 접근성에 문제를 가지고 있다. 애플은 앱 개발자들에게 가이드라인 ( Accessibility Programming Guide for iOS )을 제공함으로써 좋은 작업을 했고, 난 열렬히 개발자들에게 그걸 따르라고 권고한다. 윈도우즈,맥,Gnome 등 어떤 시각장애인 컴퓨터 사용자도 이 문제를 격어왔다. 라벨링 되지 않은 버튼과 필드, 화나는 방법을 제외하고는 접근 불가능한 컨트롤 또는 완벽히 접근히 불가능한 케이스들. Accessible AppsAppleVis 페이지도 도와줄수 있다. 적절하게 코딩된 앱들은 시각장애인이 전혀 접해보지 않는 굉장히 아름다운 방법으로 접근성을 제공한다. 난  지금 당장 리눅스 머신에서도 터치 제스쳐를 쓰고 싶다!

그리고, 이제 황금 사과(golden Apple)안의 유명한 벌레인 iTunes 가 남았다. 난 시장의 힘의 강력함을 이해하지만, 그 아름다운 하드웨어가 이상하고 접근성이 좋지 않은 소프트웨어하고 연결되는 것을 도저히 참을수가 없다. 애플은 iPhone 의 접근성을 좋게하는데 정말 놀라운일을 했지만, iTunes 는 시각장애인에게는 거의 쓸수가 없게 남아있다. 물론 시각장애인 Mac 사용자들은 아주 약간의 문제만 있지만, 그들은 시각장애인 커뮤니티에서는 매우 소수이다. 시각장애인 Windows 사용자는 굳센 의지가 있다면 사용할수는 있겠지만, 즐기지는 못할 것이다. 우리같은 시각장애인 Linux 사용자는 어둠속에 남아있다. Linux 사용자는 WINE 또는 가상 머신을 통하지 않고는 iTunes 에 접근할수 없다.

애플은 접근성에 대한 노력에 대해 홍보할 권리가 있다. 하지만, 사용자가 모든것을 할때  iTunes 를 사용해야 하는한 그들은 진짜 주장을 할수 없다는것을 알아야 한다. 나는 Linux 사용자로서 많이 예상을 했었고, 그 도전을 이겨낼수 있지만, 아직 시각장애인의 도전은 남아있다. 난 아이튠스와 싸우기가 싫어서 아이폰을 구입하지 않은 시각장애인들을 알고 있다. 영구적인 건강문제를 다룰때, 아무것도 하지 않으면서 단순히 기대하고 희망하는것만으로 좋아지길 바랄수는 없다. 스티브 잡스도 이해할수 있으리라고 본다.

애플은 Apple II/E 와 함께 시작한이후로 언제나 내마음속에 특별한 위치였다. 그 기계는 ROM 안에 내장된 두개의 언어 BASIC 과 어셈블러를 가지고 있었고, 전체 회로도(Schematic)가 안에 들어있었다. (역자 주: Apple II 는 발매시 Apple II Reference Manual 에 전체 컴퓨터의 회로도를 포함하였으며, BIOS 역할을 하는 “Monitor” ROM 펌웨어의 전체 소스코드까지 다 포함되어 있었다. 이것은 Apple II 가 성공하면서, 컴퓨터 플랫폼의 성공에 있어서 오픈 아키텍쳐의 중요성을 배울수 있었던 교훈적인 일이었다. from Wikipedia ) 이 제한적이지 않은 환경이 많은 발명에 영감을 주었고 그것은 10년간 계속되었다. 나는 PC 플랫폼이 우세하게 되면서 어쩔수 없이 옮기게 되었다. 난 DOS를 극한까지 사용했고, Windows를 싫어했고, Linux 에 편안히 정착했다.  우리는 두명의 해커가 그들의 차고에서 Blue Box를 팔기 시작한 이후로 많은 발전을 하는것을 보아왔다. 내가 앱스토어에서 “Red Box Pro” 가 사라지는것을 보는것은 특별히 아이러닉했다. ( 역자주: 스티브 워즈니악이 1971년에 Blue Box 를 만들었는데, 이것은 전화회선에 가짜 시그널을 보내서 무료로 통화를 하게 해주는 불법장비였다. 친구인 스티브 잡스가 이 기계의 상품성을 알게되고 $40 어치 재료를 가지고 제작한후, UC 버클리에 다니는 친구들에게 $150불에게 판매하게 된다. Red Box Pro 는 미국의 자동 DVD 대여기인 Redbox 시스템용 아이폰 앱으로 공식앱이 아닌 Inside Red Box 라는 커뮤니티에서 만든 비공식 앱이다. RedBox 가 공식앱을 출시하면서 이 앱은 앱스토어에서 내려지게 된다.)

iTunes 의 제한을 극복해야 함에도 불구하고, 난 아직 아이폰을 좋아한다. 난 아이폰의 능력에 계속적으로 놀라게 되었다. 난 이메일, 트위터 멘션이나 DM 도 언제나 받을수 있다. 난 이세상 어디에서라도 Good Vibes Radio 를 들을수 있다. (역자주 : Good Vibes Radio 는 필자인 Austin 이 2000년부터 운영하는 인터넷 라디오 스테이션으로 주로 일렉/트랜스 등의 음악을 주로 방송한다. ) 화장실에서 Liberty Pulse 를 읽을수 있다. WebMD 앱은 내 화상(Burn)에 쓸모가 있을 것이다. 난 아이폰과 VoiceOver 가 제공하는 능률적인 접근성 인터페이스 덕분에 일반 웹브라우저에서는 화가 날법한 일들을 할수 있게 될것이다. Coast to Coast AM 을 듣는것이 갑자기 생각났다. (역자 주: Austin 이 꽤 오랬동안 Coast to Coast 라디오방송을 계속 듣고 있었는데, 이 웹사이트가 날이 갈수록 플래시/자바스크립트 범벅이 되며 접근성이 떨어지자 제발 표준 HTML 을 지켜서 시각장애인들도 좀 더 편하게 사용할수 있게 해달라고 편지를 보냈었다. )

다른날 밤에, 매우 놀라운 일이 일어났다. 난 Color Identifier 라는 앱을 다운로드 받았다. 이 앱은 아이폰의 카메라를 이용해서 색상의 이름을 읽어주었다. 각 색상들은 6개의 헥사값으로 되어있기때문에 매핑하기 위한 테이블을 사용했을것이다. 이것은 총 16777216개의 색상이고 난 그렇게 믿는다. 몇가지 색상은 Atomic Orange , Cosmic ( 우주의 ) ,  Hippie Green , Opium ( 아편 ) 그리고 Black-White 처럼 매우 비현실적인 이름을 가지고 있다. 이 이름들과 세로토닌의 조합이 마치 환각제와 같은 경험(Psychedelic)을 가져다 주었다. ( 역자주 : 세로토닌/ Serotonin – 뇌 신경전달물질 중 하나. 놀-아드레날린, 엔도르핀 등과 같이 활동을 조절한다. 놀-아드레날린 등이 공격성, 환희 등을 담당한다면 세로토닌은 주의력과 기억력을 향상시키고 생기를 불러 일으킨다. 행복물질, 공부물질로 불린다. )

내 생전 한번도 이런경험을 해보지 못했다. 난 몇개의 빛과 색을 볼수 있다. 하지만 단지 흐릿하게 보일뿐이고, 각 사물들은 색을 가지고 있는것이 아니라 광원일 뿐이다. 내가 처음 이걸 새벽 3시에 시도했을때, 난 왜 앱이 계속 검은색이라고 얘기하는지 알수가 없었다. 그러다 Screen Curtain 기능이 카메라도 차단한다는것을 알게되었고, 그것을 꺼버렸지만, 여전히 계속 매우 어두운색만을 가지고 있었다. 그리고 나서 뭔가를 보기위해서는 빛이 필요하다는것을 기억해냈고, 밤에는 많이 볼수 없을거라는것을 알았다. 난 Get Lamp 와 했던 인터뷰와 광원에 대해 생각했다. ( 역자 주 : Get Lamp 는 1980년대 초반에 텍스트기반의 컴퓨터 어드벤쳐 게임을 만드는 것에 대한것을 다룬 다큐멘터리이다. ) 첫번째로 난 내 소금램프 들이 하나는 오렌지, 또 하나는 핑크와 장미색상, 세번째는 밝게 핑크와 붉은색의 색조들인 것을 보았고, 난 큰 감동을 받았다.

다음날, 난 밖으로 나갔다. 난 하늘을 보았다. 난 “Horizon” (수평선) , “Outer Space” ( 우주 공간 )와 같은 색상들과 다양한 파란색과 회색의 색조를 들었다. 난 갈색과 돌 색상사이의 녹색을 찾아내는 방식으로, 내 호박나무를 찾는데 여러가지의 색상을 차례차례 큐(Queue)로 사용했다. 난 내 호박나무의 녹색과 Lemon-Ginger ( 레몬-생강색 ) 잎을 둘러보는데 약 10분을 소요했다. 그리고 난 내 마당을 돌아다니면서 파란 꽃을 보았다. 갈색 헛간을 발견했고, 회색 집으로 돌아왔다. 내 마음은 마약에 취한듯 몽롱해 졌다. 난 일몰을 봤고, 하늘이 어두워지면서 색상이 변화하는것을 듣고 있었다. 다음날 밤 난 엄마와 오늘밤엔 하늘이 얼마나 더 파랬는지에 대해 대화를 했다. 내가 약간의 빛과 색상을 볼수 있기에, 색상의 이름을 듣는 것은 내 시각적 지각을 도와주고 시각적 경험을 향상시켰다. 놀랍다!

난 내 아이폰을 좋아한다. 내 우주에 들어온 그 순간부터 내 우주를 변화시켜버렸다. 하지만 모든 디스코디안(Discordian)들이 알고있듯이, 모든 황금의 사과는 중심에 황금벌레를 가지고 있다. ( 역자 주 : Discordian 은 프리메이슨에 관련된 용어로, 그들은 그리스 신화에 나오는 Eris 여신의 눈과 황금의 사과를 숭배한다. 앞에서 얘기한 아이튠스가 황금사과 내부의 벌레라는 것을 블랙코디미 형식으로 적은듯 하다. )

번역 후기

이후 Austin 은 아이패드를 구입했고, 최근인 9월초에는 iMac을 구입해 애플 패밀리로 되돌아갔습니다. ( 글의 원제목이 Rejoining the Apple Family 입니다. 아이폰 구입과 비슷한 얘기들이 있으니, 이것도 한번 읽어보세요. ) 위와 같은 아이폰의 경험들이 그를 DOS 와 Linux 를 거쳐 다시 Apple 제품을 사용하게 되는 계기가 되었을듯 합니다. 이 글을 번역하기위해 Austin 에게 허락을 요청하는 메일도 보냈는데, 몇일후 번역해도 좋다는 답변이 오더군요. 메일확인에도 아이폰이 도움을 줬을꺼라 생각됩니다.

제가 트윗에 잠깐 얘기한적이 있는데,  iOS 는 현존하는 운영체계중 장애인들을 위한 배려가 가장 뛰어난 OS 입니다. 가장 훌륭한 VoiceOver 기능이 탑재되어있고, 멀티터치 기능을 잘 활용하여 투핑거 탭, 투핑거 더블탭, 쓰리핑거 더블탭, 쓰리핑거 쓰리플탭, 스프릿탭 등 다양한 제스쳐와 , Rotor 라고 불리우는 시각장애인을 위한 멋진 네비게이션 조절 스위치 기능까지 제공합니다. 이 Rotor 를 활용하면 아이폰 기본 메모 어플리케이션에서 Cut/Copy/Paste 기능까지 시각장애인들이 쓸수가 있습니다.  AFB ( American Foundation for the Blind ) 에 의해 발표된 iPhone 3Gs의 평가 리포트를 참고해보세요. 정말 많은 부분에 있어서 장애인들을 위해 접근성부분에서 애플이 신경을 썼구나 하는것을 알 수 있습니다. 또한 iOS 4.1 부터는 이 VoiceOver 기능을 더욱 향상시켜서 테이블도 제대로 읽어줄수 있게 되었으며, 블루투스 키보드를 장착하면 VoiceOver 시에 모든 메뉴를 손쉽게 선택할수 있는 강력한 키보드 네비게이션을 제공합니다. 실제로 Apple 의 iOS 4.1 발표시에는 이런부분은 부각되지도 않았지만, 아주 많은 향상을 했다고 합니다. ( 전 다른분들에 비하면 초보 애플빠? 이긴 하지만 이런 애플이 너무 좋습니다. )

Forbes 의 4월 기사 ( Apple’s iPad Brings Easy Reading to the Blind ) 를 인용해보면, 1월에 아이패드 출시가 발표되었을때 미국 NFB ( National Federation of the Blind ) 는 아이패드를 칭찬하는 발표를 합니다. 시각장애인에 대한 지원이 완벽한 eBook 리더이기 때문인데요. 킨들의 경우 NFB 와 ACB ( American Council of the Blind ) 로 부터 대학교의 교과서를 킨들로 지원하는 파일롯 프로그램에서 빼라는 맹공격을 받게 됩니다. 시각장애인의 경우 킨들 사용시 모든 메뉴를 접근하기가 불가능했기 때문이죠. (실제로 책을 선택할수가 없었다고 합니다. ) 아마존이 올 여름까지 수정한다고 했지만, 기능상 많이 부족했고 늦었습니다. 아이패드는 출시 처음부터 아이폰에서 인정받은 VoiceOver 기능을 내장했기 때문에 더욱 환영받게 됩니다.

한국정보화진흥원 현준호님의 글에서 통계를 발췌해보면, UN 통계로 봤을때 전세계적으로 장애인 인구는 전체 인구의 10% 정도인 6억5천명이라고 합니다. 이중 80%가 개발도상국에 있구요. ( 또 다른글에 보니 이 장애인들중 거의 대다수인 6억명 가량이 시각장애인이라고 합니다. 굶주림과 비타민A 부족때문에 후천적으로 장애를 가지게 되는 사람이 70% 라고 하는군요. ) 한국의 경우 보건복지부 통계자료에서 2009년 12월기준 전체 장애인 242만명중 시각장애인 인구는 24만명입니다. 미국은 3억인구중 19%인 5천 4백만명이 장애를 가지고 있으며, 출판물을 보기 어려운 사람이 15세 이상인구중 180만명 이라고 하는군요. 상업적으로만 판단하면 아주 작은 시장이라고 생각이 들법도 합니다. 그러니 다른 업체들이 아주 잘 지원하지 못하고 있는거겠죠. 하지만 애플은 계속적으로 지원기능을 추가하고 있습니다.

사실 저는 접근성(Accessibility) 에 관해서 많은 지식을 가지고 있지는 않습니다. 다만 이런 사례들을 보면서, 저도 소프트웨어 개발자로서 제가 하고 있는일 가운데서 무엇인가 해봐야 겠다고 느끼고 있을뿐입니다. 그러던 와중에 Austin 의 블로그 글들을 보고나서, 더욱 더 그런 생각을 확고하게 가지게 되었구요. 이 글을 읽고 계신 IT 관련직종 종사자 분들, 한번 고려해 보지 않으시겠습니까 ?

추가적으로 읽어보실만한 글들을 링크해 드립니다.


.


GuruLinks: 아이폰 앱 76개 소스코드, 강좌, 개발 팁 링크모음

아이폰 개발시 도움이 되는 각종 팁 , 튜토리얼, 소스코드 링크 모음입니다. 제가 주로 트위터를 통해서 공개한 것들입니다만, 워낙 간헐적으로 트위팅 한듯해서 좀더 링크를 모아서 포스팅 합니다. 한글판 앱스토어 리뷰 가이드라인은 아이폰 앱 개발자분들은 꼭 한번 읽어보셔야 합니다.


.

HTML5 API 강좌 #1 – Web Storage 와 Application Cache

이 글은 HTML5 로 아이폰 앱 만들기 라는 제목으로 월간 W.e.b. 에 연재하는 글에서 발췌한 것입니다.
( 잡지에 기고한 글이라 원래 제 블로그의 글과 어투가 다릅니다. ^^; 총 5회 분량으로 진행됩니다. )

이 글 전에 먼저 “HTML5 on Mobile : 왜 HTML5 가 모바일에서 중요한가” 글을 읽어보시기 바랍니다.

1.1.      모바일에서 중요한 HTML5 요소들

모바일에서 HTML5 가 특별히 다른 태그를 활용하는 것은 아니다. 다만 API 중 몇 개가 모바일에 더욱 쓰기 좋은 형태일 뿐이다.

  • Offline 지원 : Web Storage ( Local & Session ) , Web Database , App Cache
  • 미디어 처리 : Video , Audio , Canvas
  • 입력 지원 : Advanced Forms
  • 위치 정보 : GeoLocation ( 연계표준 )

Offline 지원의 경우 항상 인터넷에 연결되어 있는 데스크탑과 달리 모바일 환경은 꼭 3G 와 같은 네트웍에 항시 연결되어있지 않은 WIFI 전용 기기 ( iPod Touch , iPad ) 들도 있으며, 3G 환경이라 할지라도 네트웍 트래픽을 최소화 하는 것이 아주 중요하다. 또한 HTML5 의 중요 스펙중 몇 가지는 아직 모바일용 주요 브라우저에서도 지원되지 않는다 ( iOS 4 , Android  )

  • WebSocket
  • FileReader
  • IndexedDB
  • Web Workers

현재로선 모바일에서의 HTML5 사용은 주로 Offline 지원을 통한 Local App 으로서의 동작 및 트래픽 최적화, Geo Location 을 통한 위치정보 연동이 가장 많이 쓰이고 있다. 이 연재에서는 모바일에서의 Offline 지원 / GeoLocation API 의 사용방법을 알아보고, 모바일 HTML5 기반 웹앱을 만드는 방법을 알아본다.

1.2.      Application Cache

앞서 말했듯이 HTML5 어플리케이션은 Offline 환경을 고려하여 Local App 으로 동작할 때, 또는 모바일 환경에서 사용될 때 트래픽의 최소화를 위하여 Application Cache (어플리케이션 캐쉬) 기능을 제공한다. 사실 우리가 사용하는 많은 브라우저들은 자체 캐쉬 기능을 가지고 있다. 하지만 이 캐쉬는 브라우저가 임시폴더에 저장하고 브라우저 자체에 의해 관리되므로, 웹 어플리케이션은 실제로 자신의 데이타가 캐쉬되어 있는지 확인하거나 관리할 수 없다. 이 HTML5 의 Application Cache 는 브라우저 캐쉬 와는 다르게 전적으로 웹 어플리케이션에 의해 관리되어 어떤 파일을 캐쉬할지부터 캐쉬된 파일을 업데이트 할지 말지를 웹 어플리케이션이 결정하게 된다.

이 Application Cache는 우리가 일반적으로 웹사이트를 구성할 때 사용하는 주요 요소인 HTML, JS, CSS 등의 문서파일들과 각종 이미지 파일들을 브라우저가 제공하는 임시영역에 캐쉬로 저장하도록 지원하여, 오프라인상일 때 브라우저가 웹페이지를 Refresh 하더라도 페이지가 제대로 로딩되도록 한다. 이 캐쉬는 다음과 같은 장점을 가지고 있다.

  • Offline 지원 : 한번 사이트에 접속하여 파일을 다운로드 해 놓으면, 사용자가 오프라인일때에도 페이지/사이트를 접근할 수 있다.
  • 속도 향상 : 지정한 모든 파일을 로컬에 저장해두므로, 훨씬 빠르게 페이지를 표시할 수 있다. 일반적으로 CSS, JS 같은 파일들은 전체 사이트에서 공유되므로 초기에 한번만 다운로드해 놓으면 그 후에는 매우 빠르게 재사용된다.
  • 서버 부하 감소 : 캐쉬된 파일들이 있을 경우에 브라우저가 그 페이지를 Reload 할 경우, 브라우저는 혹시 캐쉬된 파일이 업데이트 되었는지만 체크하고 변경 안되었을 경우 다운로드 하지 않으므로 서버측에서는 파일전송에 대한 부하가 줄어들게 된다.

현재 모바일/데스크탑 웹 브라우저의 경우 일반적으로 각 도메인당 5M로 캐쉬 크기가 설정되어 있는것으로 알려져 있으며, 데스크탑 브라우저의 경우 5MB 가 넘어갈 때 사용자에게 확장할 것인지를 묻기도 한다. ( 브라우저 구현에 따라 다르고, 현재로서는 스펙상 크기가 제한되어 있지 않다. )

1.2.1.      Application Cache 동작방식 알아보기

사이트에 Application Cache 를 추가하는 건 매우 간단하다. 각 페이지의 html 태그에 다음과 같이 manifest 속성을 지정한다.

* 주의 : 몇몇 브라우저에서는 와 같은 HTML5 Doctype 선언이 없으면 Application Cache 가 잘 동작하지 않을 수도 있다.

이 manifest 속성에는 URI 형식으로 Manifest 파일을 지정하면 된다. 이 appcache.manifest 파일 ( 이름이나 확장자는 상관없다 ) 은 텍스트 파일이며 다음과 같은 내용으로 구성된다.

CACHE MANIFEST
# this is comment
# manifest version : v1.1 20100808
CACHE:
stylesheet.css
js/jquery.js
js/system.js
img/logo.png
img/icons.png
NETWORK:
login.php
FALLBACK:
img/main_image.png   img/backup_image.png

Manifest 파일은 단순한 텍스트 파일이지만 서버에서 이 파일을 전송할 때 MIME 타입은 꼭 text/cache-manifest 로 되어있어야 한다. 웹 서버에 .manifest 의 MIME 타입을 등록 ( 확장자는 상관없기 때문에 원하는 걸로 해도 된다. ) 해주거나, 동적으로 manifest 를 생성할 때는 헤더를 지정해주면 된다. 예를 들어 php 에서는 아래와 같이 header 함수를 이용해 Content-Type 을 text/cache-manifest 로 지정한다.

이 Manifest 파일의 맨 첫 줄은 꼭 CACHE MANIFEST 라는 내용으로 시작해야 한다. 실제로는 CACHE: , NETWORK: , FALLBACK: 이라는 3개의 헤더정보가 있지만 맨 앞의 CACHE: 는 생략되어도 된다. 즉 앞의 Manifest 파일 앞 부분은 아래와 같이 CACHE: 를 제거해도 상관없다.

CACHE MANIFEST
# this is comment
# manifest version : v1.1 20100808
stylesheet.css
js/jquery.js
js/system.js
img/logo.png
img/icons.png

섹션아래에 리소스들은 Manifest 파일이 위치한 곳으로부터의 상대경로 또는 절대경로로 지정될 수 있다. html 파일이 위치한 경로가 아님에 주의하자. Manifest 파일의 경로가 다른 곳에 있으면 이미지등 URL 이 실제 html 페이지와 다를 수 있으므로, 일반적으로는 html 파일과 Manifest 를 같은 위치에 두는 것이 좋다. 그외에 # 으로 시작되는 주석문 또는 빈 줄도 추가될 수 있다.

각 섹션에 대해 알아보자

  • CACHE:
    • 이 섹션에 명시되어 있는 파일들은 맨 처음 로드되고 나서 캐쉬에 저장된다.
    • 현재의 Manifest 파일을 지정한 html 파일은 이 섹션에 없더라도 자동으로 캐쉬에 저장된다.
  • NETWORK:
    • 이 섹션에 명시되어 있는 파일들은 무조건 네트웍에서만 불러오게 된다. 즉, 만약 브라우저가 오프라인 상태라면, 이 파일들은 캐쉬된 파일에서 사용되면 안 된다.
  • FALLBACK:
    • 이 섹션은 로딩에 실패했을 경우에 대신 사용할 파일들을 지정하는 것이다. A B 라는 형형태 지정했을 때 A 의 로딩에 실패하면 B 를 자동으로 로딩한다. 즉 이 경우 B 의 파일은 캐쉬에 저장되어, 오프라인 모드에서 A 를 못불러올 경우 캐쉬에서 읽히게 된다.
    • Fallback 은 폴더 전체에도 적용이 가능하다. 즉
      FALLBACK:
       /photo  notavailable.png

      위와 같이 지정했을 경우 오프라인 모드에서는 /photo 폴더 밑의 모든 이미지를 못 가져 오게 되므로 notavailable.png 파일로 대체되게 된다.

1.2.2.      Application Cache 동작순서

브라우저에서 Application Cache가 동작하는 순서를 간략히 알아보자.

  1. 사이트에 최초 접속시 브라우저는 html 태그의 manifest 속성을 보고 manifest 파일을 로드한다.
  2. Manifest 파일 자체와 내부의 CACHE: 섹션의 모든 파일, 그리고 FALLBACK: 섹션에 있는 대체 파일들을 캐쉬에 저장한다.
  3. 두번째 접속시 브라우저는 manifest 파일을 불러서 이 파일이 변경되었는지를 비교한다. 수정이 되었다면 2번 프로세스를 다시 시작한다.

    * 중요 체크포인트 1 : 리소스 파일이 변경되었는지가 아니라 manifest 파일내용 자체가 변경되었는지를 체크하는 것이다. 만약 캐쉬된 로고이미지가 바뀌거나 CSS 파일의 내용이 바뀌었다면 manifest 파일 자체를 수정해서 다시 로드 되도록 해야 한다. 이 때문에 손쉽게는 동적으로 생성되는 Manifest 파일의 코멘트부분에 수정한 날짜 또는 버전정보를 적어놓는 등의 방법을 이용하거나, Javascript 를 이용하여 수동으로 갱신하도록 한다.

    * 중요 체크포인트 2 : 2번째 접속시에 만약 리소스가 변경되었더라도 2번째 접속 자체는 처음의 캐쉬를 이용하여 화면이 구성되게 된다. 즉 처음 캐쉬로 화면을 먼저 표시하고 , 백그라운드에서 캐쉬 업데이트 프로세스가 진행된다. 업데이트가 완료되면 Javascript 로 이벤트가 발생하게 되며, 이때 강제로 캐쉬를 교체해야만 수정된 캐쉬가 화면에 보여지게 된다.

  4. 오프라인 상태에서 해당 페이지로 접속시도시 실패한다면 Application Cache 에서 html 파일과 캐쉬된 파일을 로드한다. 즉, Offline 모드에서의 페이지 보기로 동작한다.
  5. 사이트의 html 태그에서 manifest 가 제거되었다면, 브라우저는 해당캐쉬를 삭제한다.

즉, 처음 접속시 manifest 가 지정되어 있다면 이 내용에 따라 캐쉬해 두었다가, 차후 접속 시에 사용하는 방식으로 동작한다.

1.2.3.      Javascript 에서 Application Cache 관리하기

Application Cache는 Manifest 파일에 의해서 정적으로 관리되는 방식 외에 Javascript 를 통해서 좀더 세밀하게 관리가 가능하다. 브라우저가 Application Cache 를 지원하는지 여부, 캐쉬에 대한 상태를 읽어들이거나, 비동기적으로 업데이트 하는등의 동작을 자바스크립트 코드상에서 할수있도록 지원한다.

  • Application Cache 지원여부 확인
    • 코드상에서 window.applicationCache 를 객체를 검사하면 브라우저가 Application Cache를 지원하는지 여부를 확인할수 있다.
    • If (!!window.applicationCache) { // 지원함
      } else { }
  • Application Cache 상태

    Window.applicationCache.status 를 확인하면 현재 캐쉬의 상태를 알 수 있다. 다음과 같이 6개의 상태값을 가질 수 있다. 각 상태값은 applicationCache 에 대문자로 된 상수로 선언되어 있다.

    if ( window.applicationCache.status == window.applicationCache.UPDATEREADY) {
     // 작업진행
     }
    • 0 – UNCACHED
      페이지가 캐쉬를 사용하지 않거나, 맨 처음 접속시에 캐쉬가 다운로드 되기 전까지는 UNCACHED 상태이다.
    • 1 – IDLE
      브라우저가 최신버전의 Application Cache 로 업데이트되었고, 더 이상 다운로드 할 업데이트 버전이 없는 상태
    • 2 – CHECKING
      Manifest 파일이 업데이트 되었는지를 체크하는 상태 ( 현재 캐쉬된 Manifest 파일과 서버의 Manifest 파일을 바이트단위로 비교한다 )
    • 3 – DOWNLOADING
      캐쉬할 파일들을 다운로드 하는 상태 ( 2번 단계에서 Manifest 파일이 업데이트되었다고 알게 되었을 때 )
    • 4 – UPDATEREADY
      새로운 캐쉬의 다운로드가 끝나고 사용할 준비가 되었을 때 ( 아직 이 캐쉬가 사용된건 아님 )
    • 5 – OBSOLETE
      Manifest 파일 자체를 찾을 수 없을 때, 상태는 OBSOLETE로 지정되며 캐쉬는 삭제된다.
  • Application Cache 에 의해 발생하는 Event 들
    캐쉬의 상태에 따라 아래와 같은 이벤트 들이 발생한다. 아래와 같이 이벤트 리스너를 등록해서 각각의 이벤트에 대응할수 있다.

    window.applicationCache.addEventListener(‘updateready’, function() {
     // 이벤트 처리하기
     }, false);
    • checking
      manifest 파일을 처음으로 다운받을때나, 업데이트된 manifest 가 있는지 확인할 때 발생하는 이벤트
    • error
      파일을 다운로드 할 수 없거나 하는 다양한 에러시 발생. 만약 manifest 에 있는 파일들중 하나라도 다운로드가 실패하면 캐쉬 업데이트 전체가 실패한다. 물론 실패하더라도 기존에 있는 캐쉬 자체가 깨지지는 않으며 브라우저는 계속해서 예전버전의 캐쉬를 이용하게 된다.
    • noupdate
      업데이트할 파일이 없을 때 발생하는 이벤트
    • downloading
      처음으로 캐쉬할 파일들을 다운받거나, 업데이트된 캐쉬가 있을 때 발생
      화면에 변경된 내용을 업데이트 중이라는 것을 표현해줄 수 있는 이벤트

      window.applicationCache.addEventListener(‘downloading, function() {
       showMessage(‘리소스 다운로드중입니다.’)
       }, false);
    • progress
      캐쉬할 각각의 파일이 다운로드 될 때마다 발생하는 이벤트
    • updateready
      새로 업데이트된 캐쉬의 모든 리소스가 다운로드되었을 때 발생하는 이벤트. 이 이벤트가 발생하면 swapCache() 함수를 호출하여 브라우저가 새로 업데이트된 캐쉬를 사용하도록 할 수 있다.

      window.applicationCache.addEventListener(‘updateready’, function() {
       window.applicationCache.swapCache();
       }, false);
    • cached
      모든 리소스가 다운로드 되어, 어플리케이션이 캐쉬되었을 때 발생
    • obsolete
      manifest 파일을 찾을 수 없을 때 ( 404 또는 410 에러 )
  • Application Cache 의 메소드들
    캐쉬에 대해 사용할수 있는 메소드는 2가지이다.

    • window.applicationCache.swapCache()

      앞에서 나왔듯이 새로 다운로드 받은 캐쉬와 현재 사용중인 캐쉬를 강제로 교체하는 함수이다.

    • window.applicationCache.update()
      Application Cache 다운로드를 다시 호출한다. 즉 Manifest 파일을 다운받아서, 현재 캐쉬 되어있는 Manifest 와 비교하여 변경된 부분이 있다면 새 캐쉬를 다운로드 한다. 이걸 호출하더라도, 현재 사용중인 캐쉬가 변경되지는 않으며, 새 캐쉬고 업데이트되고 updateready 이벤트 발생시에 swapCache() 함수를 호출해 줘야 한다.

실제로 자신의 웹 어플리케이션에 Application Cache 를 적용하게 되면 항상 2가지 작업을 하게 된다.

  1. html 태그 에 manifest 속성추가
  2. 자바스크립트에 updateready 이벤트 받아서 swapCache() 호출

이를 통해 여러분의 웹 어플리케이션이 Offline 상에서도 동작 가능하게 하고, 또한 좀더 빠르게 로딩할 수 있도록 만들 수 있다.

1.3.      Web Storage

1.3.1.      Web Storage 와 Cookie

HTML5 의 Offline 지원 API 중에서 2번째는 Web Storage 라고 불리는 Key/Value 쌍으로 된 형식의 저장소이다. 우리는 이미 많은 웹 어플리케이션에서 Cookie 를 이용해서 이 Key/Value 값을 저장해왔다. 하지만 Cookie 는 다음과 같은 단점이 있다.

  • 보통 브라우저에서 최대 크기가 4KB 정도로 제한이 있다.
  • 매번 HTTP Request 호출 시마다 HTTP 헤더에 전송을 필요로 하기 때문에 많은 양의 값들을 저장하기 에는 무리가 있어서, Cookie 양이 커질 때마다 웹서버의 응답시간이 늘어나는 결과를 보인다. ( Cookie 에 대한 퍼포먼스 측정은 http://j.mp/nomore_cookies 를 참고 )
  • 헤더에 전송되므로 악의적인 용도로 쓰일 수 있다.

HTML5의 Web Storage 는 이런 쿠키의 단점을 보완하여 클라이언트(브라우저) 상에서만 저장되고 관리되므로 더욱 효율적이다. Web Storage 는 2가지의 저장공간을 제공한다. 일반적으로 이 데이터들은 일반적으로 각 도메인 별로 약 5Mb 까지 저장이 가능하다. ( 브라우저 별로 차이가 있을수 있다 )

  • LocalStorage
    장기적으로 저장할 수 있는 공간이다. 브라우저 창을 닫아도 계속 유지되며 모든 브라우저 창 간에 공유된다. 유효기간이 없는 Cookie 와 같다고 보면 된다.
  • SessionStorage
    세션이라는 이름에서 볼 수 있듯이 현재 브라우저 창에만 유효한 저장공간이다. 즉 브라우저창을 닫으면 모두 지워진다.

주의! : 만약 현재 브라우저 창 내에서 a 태그에 target=_blank 같은 걸 지정했을 경우 이 링크를 눌러서 새로운 창을 열게 되면, 지금까지 기록된 SessionStorage 내부의 값을 모두 복사해서 넘기게 된다. 그러면 두 개의 창은 처음에는 똑 같은 SessionStorage 내용으로 만들어지지만, 그 다음부터 수정된 SessionStorage 내용은 서로 다르게 된다. 즉 세션 데이터가 복사되지만 공유되는 것은 아니다.

1.3.2.      Web Storage 사용하기

LocalStroage 와 Sesseion Storage 가 브라우저상에서 처리되는 것은 차이가 있지만, 코드상에서는 똑같이 동작한다. 제공되는 메소드는 다음과 같다. ( localStorage 를 sessionStorage 로 바꿔도 똑같이 동작한다 )

localStorage.setItem ( key , value )  // 키/밸류 아이템을 저장
localStorage.getItem ( key ) // 해당키의 아이템을 가져온다.
localStorage.removeItem ( key ) // 해당키의 아이템을 삭제한다
localStorage.clear ( )  // 전체 아이템을 삭제한다
localStorage.length  // 스토리지에 들어있는 아이템의 개수

예제를 통해 조금 더 자세히 알아보자.

  • setItem ( key , value )

    스토리지에 아이템을 저장하는 setItem 함수는 다음과 같이 호출할 수 있다.

    localStorage.setItem( ‘username’ , ‘철수’);

    일반적으로 에러가 발생할 일은 없지만 단 하나의 에러가 발생할 수 있다. 앞에서 말한 5MB 크기제한을 넘겼을 경우다.

    try {
    localStorage.setItem( ‘username’ , ‘철수’ );
    } catch (e) {
      if (e == QUOTA_EXCEEDED_ERR) {
        alert('저장공간이 초과되었습니다!');
      }
    }
  • getItem ( key )

    저장된 아이템은 키를 이용해서 가져올수 있다.

    alert( ‘현재 사용자는 ‘ + localStorage.getItem(‘username’) + ‘입니다.’ );

    만약 지정한 key 이름이 유효한 javascript 토큰 ( 빈칸없고, 언더바만 사용되는 일반 적인 변수명과 같은 ) 이라면 아래와 같이 축약해서 쓸 수도 있다.

    alert( ‘현재 사용자는 ‘ + localStorage.username + ‘입니다.’ );
  • removeItem( key ) , clear()
    removeItem() 을 이용하여 key 별로 지우거나, clear() 를 이용해서 전체 아이템을 삭제할 수 있다. 주의할 것은 localStorage.clear() 의 경우 현재 해당된 도메인에 할당된 전체 LocalStorage 를 지운다는 것이다. 즉 http://foo.org/abc/ , http://foo.org/xyz/ 두 개의 폴더에서 만들었더라도 상관없이 모두 지워진다. 단, http://bar.foo.org 와 같은 서브 도메인은 상관없다. 폴더별이 아닌 도메인별로 관리된다고 보면 된다.

1.3.3.      브라우저에서 Web Storage 관리하기

HTML5 를 잘 지원하는 WebKit 기반 브라우저 ( 크롬 / 사파리 )에서는 LocalStorage / SessionStorage 를 UI 상에서 보고 관리할 수 있는 방법을 제공한다. 개발시에 유용하게 사용할 수 있으므로 꼭 알아두자. ( Firefox 는 Firebug 를 깔면 볼 수는 있지만, WebKit 에 비해 불편하다 )

Chrome : Ctrl+Shift+J (윈) 또는 Command+Option+J (맥) 을 눌러 Developer Tools 실행

Safari : Ctrl+Alt+I (윈) 또는 Command+Option+I (맥) 을 눌러 Web Inspector 실행

양쪽 브라우저에서 창의 이름은 다르지만 기능은 거의 동일하다. 아래와 같이 Storage 탭에서 Local / Session 데이터를 확인 가능하며, Key/Value 아이템의 추가/수정/삭제등이 모두 가능하다.

Chrome Developer Tools Window

지금까지 HTML5 의 Offline 지원기능 중에 두 가지인 Application Cache 와 Web Storage 에 대해 알아보았다. 다음 회에선 브라우저에서 사용 가능한 데이터베이스인 Web SQL Database 와 위치정보 지원을 위한 GeoLocation API 를 알아보고, 이를 이용한 샘플 어플리케이션을 제작해볼 것이다.