번역 : bellamy.chang([email protected] , izect.kr)
들어가며
요즘 모바일 웹 개발은 화재입니다. 스마트폰이 PC보다 더 많이 팔리게 되면서 더 많은 사용자들이 모바일 장치를 통해 웹서핑을 하고 있고 이것은 개발자들에게 그들의 사이트를 모바일 웹 브라우저에 맞추게 되는 중요한 원인을 제공할 것입니다.
“모바일” 전장은 아직 수 많은 개발자들의 미지의 물과도 같습니다. 대부분의 사이트들은 모바일 유저를 배제하고 있습니다. 대신, 웹 사이트들은 기본적인 데스크톱 브라우저나 형편없는 모바일 브라우저 사이트로 디자인 되었습니다. html5rocks.com 같은 사이트도 예외는 없으므로 이를 간단한 노력만으로 모바일 버전의 사이트를 만들어 봅시다.
모바일 친화적인 html5rock.com 사이트 만들기
이 강의에서는 html5rocks 사이트를 모바일 친화적 버전으로 만들면서 흥미로운 사이트로 만들 것입니다. 그리고 주로 사용되는 스마트 폰에 타겟을 맞춰보겠습니다. 이 강의의 목표는 새로운 모바일 사이트를 만드는 것이 아닙니다. 그렇게 되면 쓸때없는 시간이 많이 낭비됩니다. 우리는 이미 사이트 구조(마크업)를 정의해 두었습니다. CSS로 룩앤필도 설계해 두었고, 기능적인 JS 함수도 있습니다. 대부분의 사이트들이 이정도는 기본으로 갖추고 있습니다.
이 글에서는 어떻게 html5rocks를 안드로이드와 아이폰에 최적화된 모바일 사이트를 만드는 방법을 보여줍니다. 스마트폰에서 단순히 html5rocks.com을 로드하면 각 OS에 따라 다른 사이트르 보여줍니다. m.html5rocks.com 으로 리다이렉트 된다든가의 잡다한 기능은 기본적으로 없습니다. 이처럼 모바일 버전에서는 최적화된 사이트의 모습과 기능적인 동작 등을 모바일 장치에서 잘 보이고 동작하게 하는 것입니다.
html5rocks.com의 데스크탑버전(왼쪽)과 모바일 버전(오른쪽)
CSS 미디어 쿼리
HTML4와 CSS2에서는 미디어 독립적인 스타일 시트를 아래와 같이 제공합니다.
<link rel="stylesheet" media="print" href="printer.css">
이 테그는 목표가 되는 장비와 화면에 출력될 떄의 정의된 컨텐츠 스타일을 가져옵니다. CSS3은 미디어 타입보다 한단계 나아가, 미디어 쿼리와 함께 강화되었습니다. 미디어 쿼리는 허용된 스타일 시트를 더 세심하게 확장합니다. 미디어 쿼리를 사용함으로써 굳이 컨텐츠 자체를 바꾸지 않아도 디바이스에 맞게 컨텐츠들이 조절됩니다. 기존 레이아웃을 바꾸는데는 꼭 필요해 보이지 않습니까!
미디어 쿼리를 사용하면 디바이스 스크린의 가로세로 크기, 오리엔테이션(디바이스의 가로/세로 상태) 등등의 미디어 속성을 사용할 수 있습니다. W3C 미디어 쿼리 스펙 문서에서 모든 리스트를 볼 수 있습니다.
스크린 사이즈에 맞게 타겟팅
이 예제에서는 phone.css가 손바닥 크기나 320px보다 작은 디바이스의 가로 사이즈에 맞게 적용시켜 줄 것입니다.
<link rel="stylesheet"
media="handheld, only screen and (max-device-width: 320px)" href="phone.css">
미디어 쿼리에 only라는 키워드는 CSS3이 호환되지 않는 브라우저는 이를 무시하게 됩니다.
아래는 스크린 사이즈를 641px x 800px 로 맞춘 것입니다.
<link rel="stylesheet"
media="only screen and (min-width: 641px) and (max-width: 800px)" href="ipad.css">
미디어 쿼리는 <style>테그 내에서 인라인으로도 사용할 수 있습니다.아래는 세로방향일 경우 all이라는 미디어 타입을 준 것입니다.
<style>
@media only all and (orientation: portrait) { ... }
</style>
media=”handheld”
잠시 media=”handheld”에 관해 얘기해 보겠습니다. 사실 안드로이드랑 iOS에서는 이 속성이 무시됩니다. 이것은 media=”screen” 스타일 시트로 공급된 높은 퀄리티의 컨텐츠(데스크탑에 맞춰진 컨텐츠)를 유저에게 공급하는 것과 개발자들이 media=”handheld”로 된 낮은 품질의 컨텐츠를 유지보수하는 것을 최소하 하려는데 의의가 있습니다. 전체적인 웹의 모토에 따라 대부분의 최신 스마트폰 브라우저 들은 handheld 스타일 시트를 가볍게 무시하게 됩니다.
이는 모바일 장치에게는 이상적이겠지만, 다른 브라우저들은 아래처럼 다른 방법을 사용하고 있습니다. :
– 몇몇의 브라우저들에게 handheld 스타일 시트만 읽는다.
– 몇몇의 브라우저들에게 handheld 스타일 시트가 한개일 경우만 읽고. 아니면 다른 방법을 사용한다.
– 몇몇의 브라우저들은 두개의 handheld 스타일 시트를 읽고 screen 스타일 시트도 사용한다.
– 몇몇은 screen 스타일 시트만 읽는다.
오페라 미니 브라우저는 media=”handheld”를 무시하지 않으며, 윈도우 모바일 브라우저는 주석을 달아 미디어 속성값과 구분하는 트릭을 사용합니다.
<!-- media="handheld" trick for Windows Mobile -->
<link rel="stylesheet" href="screen.css" media="Screen">
<link rel="stylesheet" href="mobile.css" media="handheld">
html5rocks에서 어떻게 미디어 쿼리를 사용할까.
미디어 쿼리는 html5rocks 모바일 버전에서 내내 무겁게 사용되었습니다. 우리는 우리의 정말 중요한 Django 템플릿을 사용하지 않고 모바일 사이트를 트윅하는 것을 허락 받았습니다. 덛붙혀 그들의 지원으로 다양한 브라우저에서 꽤 잘 돌아가게 되었습니다.
각 페이지들의 <head>내에서 여러분은 다음의 스타일 시트를 보게 될 것입니다.
<link rel="stylesheet"
media="all" href="/static/css/base.min.css" />
<link rel="stylesheet"
media="only screen and (max-width: 800px)" href="/static/css/mobile.min.css" />
항상 base.css는 html5rocks.com의 룩앤필을 결정합니다. 그러나 우리는 mobile.css를 사용하여 가로가 800px 미만인 새로운 스타일을 적용할 것입니다. 이 미디어 쿼리는 스마트폰(~320px)과 아이패드(~768px)를 커버합니다.
mobile.css는 또한 아래와 같은 몇개의 스타일을 강제로 바꿉니다 :
– 여분의 여백(Whitespace)/패딩(padding)을 줄인다. 작은 화면에서 여백은 낭비다!
– hover 상태를 제거한다. 터치스크린에서 이는 전혀 의미가 없다.
– 레이아웃을 단일 컬럼으로 맞춘다. 자세한건 나중에..
– 메인 컨테이너 div의 box-shadow를 삭제한다. 큰 박스 shadow는 페이지의 퍼포먼스를 떨어뜨린다.
– CSS를 사용해서 박스를 구부리는 box-ordinal-group 를 사용해서 홈페이지의 각각의 섹션을 정렬한다. 이 강의의 모바일 버전 강의부터 진행한 이후에..본 사이트의 튜토리얼에서 “html5의 5가지 주요 기능 배우기” 강의로 다뤄질 것이다.
– opacity 의 변화를 제거한다. 알파값을 주는 것은 모바일 웹의 퍼포먼스에 치명적이다.
모바일 메타 테그
모바일 웹킷(webkit)은 유저의 디바이스로 보다 나은 브라우징을 할 수 있게 합니다.
뷰포트 세팅
첫번째 메타의 세팅은 뷰포트의 세팅입니다. 뷰포트를 세팅하는 것은 브라우저에 컨텐츠를 어떻게 화면에 맞출 것인지, 그리고 모바일에 최적화된 사이트는 어떤 것인지 알려줍니다. 예를 들면
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
위처럼 뷰포트를 세팅하면 디바이스에 맞게 가로값을 조절하려면 초기 스케일(확대 비율)은 1이라고 알려줍니다. 또한 user-scalable=yes 를 통해 사용자가 확대/축소를 할 수 있게 하는데, 이는 모바일 웹에는 적합할 수 있지만 웹앱은 아닙니다. 웹앱에서는 user-scalable=no 를 통해 현재의 확대 비율을 유지하도록 예방해야 합니다.
<meta name=viewport
content="width=device-width, initial-scale=1.0, minimum-scale=0.5 maximum-scale=1.0">
주 : 가로(width)는 픽셀로 정의됩니다. 아이폰이나 몇몇 스마트폰들은 width=320이나 width=device-width나 동일한 결과를 보여줍니다. 모든 폰들이 같은 가로 사이즈를 가지고 있지 않다는 것을 안다면, device-width를 쓰는 것이 가장 나을 것입니다.
안드로이드는 아래처럼 화면의 해상도를 조절할 수 있는 확장된 뷰포트 정의를 개발자들에게 제공합니다.
<meta name="viewport" content="target-densitydpi=device-dpi">
target-densitydpi는 device-api, high-dpi, medium-dpi, low-dpi를 정의할 수 있습니다.
만약 CSS 미디어 쿼리인 -webkit-device-pixel-ratio 혹은 자바스크립트의 window.devicePixelRatio 를 사용하여 각기 다른 화면의 해상도에 따라 모바일웹을 조절하려면 target-densitydpi 메타테그를 device-api로 정의해야 합니다. 이는 모바일 웹에서 안드로이드에서 스케일을 제한하고 각각의 해상도에 따라 자바스크립트나 CSS에서 허용해 놓은 필요 조건을 적용시킬 것입니다.
안드로이드의 웹뷰 문서 를 보시면 타겟팅과 디바이스 해상도에 대해 더 많은 정보를 알 수 있습니다.
풀스크린 브라우징
아래 두개의 다른 메타 값은 iOS의 스펙입니다. apple-mobile-web-app-capable 과 apple-mobile-web-app-status-bar-style은 웹 컨텐츠들을 마치 앱(app)처럼 풀스크린으로 보여주고 상태바를 보여줄 것입니다.
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
iOS에서 사용 가능한 더 많은 메타 값을 보려면 사파리 레퍼런스 문서를 보시기 바랍니다.
홈스크린 아이콘
iOS와 안드로이드 장비는 rel=”apple-touch-icon” (iOS) 와 rel=”apple-touch-icon-precomposed” (Android)를 통한 링크를 허용합니다. 이는 유저들이 우리의 사이트를 자신의 홈스크린에 앱(app)처럼 화려한 아이콘을 사용해 북마크 할 수 있도록 합니다.
<link rel="apple-touch-icon"
href="/static/images/identity/HTML5_Badge_64.png" />
<link rel="apple-touch-icon-precomposed"
href="/static/images/identity/HTML5_Badge_64.png" />
html5rocks는 어떤 모바일 메타 테그를 사용하는가?
위의 것들을 전부 모아서 html5rocks의 <head> 부분을 만듭니다.
<head>
...
<meta name="viewport"
content="width=device-width, initial-scale=1.0, minimum-scale=1.0" />
<link rel="apple-touch-icon"
href="/static/images/identity/HTML5_Badge_64.png" />
<link rel="apple-touch-icon-precomposed"
href="/static/images/identity/HTML5_Badge_64.png" />
...
</head>
수직 레이아웃
보다 작은 화면일수록 세로 스크롤이 가로 스크롤보다 편리하게 됩니다. 컨텐츠들을 단일 컬럼으로 유지하여 모바일에서 수직 레이아웃이 되도록 합니다.
html5rocks에서는 CSS3 미디어 쿼리를 사용하여 이러한 레이아웃을 만들었습니다. 마크업을 다시 변경할 필요가 없었습니다.
html5rocks의 단일 칼럼을 사용한 수직 레이아웃
모바일 최적화
우리가 만드는 것이 처음보다는 꽤나 최적화 되었습니다. 그리고 보다 더 최적화 시켜야 합니다. 네트웍 요청을 줄이고 JS/CSS를 압축하고, gzip(앱엔진에서의), 그리고 DOM을 조작해서 최소화 해야 합니다. 이런 기법을 기본으로 적용해야 겠지만, 가끔 급하게 작업을 하다가 놓칠 수도 있습니다.
자동 주소 바(bar)
모바일 브라우저는 데스크탑보다 상태를 표현하는 부분이 부족합니다. 심지어 다른 플렛폼에서는 커다란 주소 입력바가 상단에 위치하며.. 페이지가 다 로딩되기 전까지 존재합니다.
이를 다루는 가장 쉬운 방법은 자바스크립트를 이용하여 스크롤 하는 것입니다. 하나의 작은 픽셀이 성가신 주소 바를 다뤄줄 것입니다. html5rocks에서는 이를 강제로 숨기기 위해 onload이벤트에 window를 한픽셀 내리도록 처리하였습니다.
못생긴 주소 바가 스크린의 위쪽에 있는 경우
{% if is_mobile % }
// Hides mobile browser's address bar when page is done loading.
window.addEventListener('load', function(e) {
setTimeout(function() { window.scrollTo(0, 1); }, 1);
}, false);
{% endif % }
또한 데스크탑에서는 이를 사용하지 않도록 is_mobile로 감쌌습니다.
네트워크 요청을 줄여 대역폭 확보하기
HTTP의 요청을 줄이는 것은 가장 큰 퍼포먼스 변화를 가져온다고 알려져 있습니다. 모바일 장비는 더더욱이나 동시 접속을 제한해야 합니다.(3G등의 네트워크가 한계가 있기 때문에) 그래야 쓸때없는 요청을 줄일 수 있습니다. 게다가 각각의 바이트(byte)를 줄이지 않으면 사이트가 유저들의 돈을 깎아먹을 수도 있습니다.
아래는 우리가 어떻게 네트워크 요청을 최소화하고 html5rocks의 대역폭을 줄였는지를 보여줍니다.
- iframe의 삭제 – 아이프레임은 자체로도 느립니다. 거대한 양의 보이지 않는 서드파티 공유 위젯들(버즈,구글 프랜드 커넥트, 트위터, 페이스북등)이 이 튜토리얼 페이지에 있습니다. 이러한 API들은 <script>테그를 통해 포함되고 iframe을 페이지에 생성시켜 느리게 만듭니다. 모바일 버전에서 이러한 위젯들은 제거되었습니다.
- display:none – 가끔 모바일의 윤곽에 맞지 않는 마크업은 숨겨야 합니다. 가장 좋은 예로 우리는 4개의 라운딩된 박스를 데스크탑 홈페이지 위에 위치하였습니다.
(html5rocks.com 상단의 버튼들)
이것들은 모바일 사이트에서는 안보입니다. 중요한 것은 이 아이콘들이 display:none으로 안보임에도 불구하고 아직 브라우저에서는 각각의 아이콘을 만든다는 것입니다. 그러므로 모바일에서는 숨기는 것으로만 만족하면 안됩니다. 이는 대역폭의 낭비를 제공하면서도 유저는 심지어 이를 감지해 낼 수도 없습니다. 우리의 해결책은 Django 템플릿 중 “is_mobile” 이진 변수를 만들어 HTML에서 생략해야 할 섹션을 골라냈다는 것입니다. 만약 사용자가 스마트폰으로 보면 이 버튼들은 없어집니다.
- 어플리케이션 캐시 – 어플리케이션 캐시는 오프라인 지원 뿐만 아니라 빠르게 웹앱을 시작하게 합니다.
- CSS/JS compression – 우리는 Closure 컴파일러 대신에 YUI 압축기를 사용했습니다. 주된 이유로는 YUI compressor는 CSS와 JS를 다루기 쉽습니다. 참고로 우리는 인라인 미디어 쿼리를(CSS안에 미디어 쿼리를 사용하는 것) 사용하는데 YUI compressor 2.4.2버전(이 이슈를 참조하세요)에서는 계속 에러를 뱉었습니다. YUI Compressor 2.4.4이상에서는 이 문제가 수정되었습니다.
- 가능하면 CSS 이미지 스프라이트를 사용합니다.
- pngcrush를 이미지 압축에 사용합니다.
- dataURI들을 작은 이미지에 사용합니다. Base64 인코딩은 30% 이상 이미지를 줄이면서 네트워크를 절약할 수 있습니다.
- 자동으로 로딩되는 단일 스크립트 테그의 구글 커스텀 서치 가 google.load()를 통한 동적 로딩보다 낫습니다. 이 구문(google.load())은 엄청난 네트웍 요청을 만듭니다.
-
<script src="//www.google.com/jsapi?autoload={"modules":[{"name":"search","version":"1"}]}"></script>
- 사용되지 않더라도 모든 페이지에 작은 프린트와 Modernizr를 추가하였었습니다. Modernizr는 꽤 괜찮습니다. 허나 Moernizr는 모든 로드되는 페이지에서 테스트되어야 합니다.가끔 DOM을 수정하고 페이지를 느리게 로드하게 만듭니다. 최근에 우리는 이 라이브러리를 실제로 필요한 곳에 로딩하였습니다. 겨우 2개의 요청만 나왔구요. 🙂
성능 개선에 대해 더 언급해보면..
- 가능하면 모든 JS를 페이지 아래에다 옮깁니다.
- 인라인 <style> 테그를 삭제합니다.
- 캐시된 DOM을 찾고 DOM을 조작하여 최적화합니다. – 매시간 브라우저에서 DOM을 터치하는 것은 다시 그려집니다. 다시 그려진다는 것(reflew)은 그만큼 값비싼 결과를 초래합니다.
- 사용하지도 않는 코드를 미리 클라이언트쪽에 로드하는 것은 낭비입니다. 특히 아래처럼 현재 페이지의 네비게이션을 체크하는 구문에서 볼 수 있습니다.
var lis = document.querySelectorAll('header nav li');
var i = lis.length;
while (i--) {
var a = lis[i].querySelector('a');
var section = a.getAttribute("data-section");
if (new RegExp(section).test(document.location.href)) {
a.className = 'current';
}
}
- 고정된 폭을 가지는 요소들은 width:100%나 width:auto로 교체되는 것이 좋습니다.
애플리케이션 캐시
html5rocks모바일 버전은 애플리케이션 캐시를 사용해서 초기 로딩을 빠르게 하고 유저들에게 오프라인에서 컨텐츠를 읽을 수 있도록 해줍니다.
당신의 사이트에 앱케시가 가능할 때, manifest파일을 캐시하지 않는 것이 매우 중요합니다.(명확하게 manifest파일 그 자체나 맹목적인 큰 캐시 컨트롤 헤더를 가지고 있다면 말입니다.) 만약 manifest파일이 캐시되었다면 그것은 디버깅의 함정이 될 것입니다. iOS와 안드로이드는 특히 이 파일을 잘 캐시하고 있습니다. 하지만 데스크톱처럼 손쉽게 캐시를 초기화 하는 것은 공급하지 않습니다.
이러한 예방은 우리의 사이트의 캐시를 앱 엔진이 manifest파일을 캐시하지 못하도록 먼저 추가합니다.
- url: /(.*\.(appcache|manifest))
static_files: \1
mime_type: text/cache-manifest
upload: (.*\.(appcache|manifest))
expiration: "0s"
그리고 JS API를 통해 새로운 manifest가 다운되었을 떄 유저에게 알립니다. 이 API는 페이지를 새로 고칠겁니다.
window.applicationCache.addEventListener('updateready', function(e) {
if (window.applicationCache.status == window.applicationCache.UPDATEREADY) {
window.applicationCache.swapCache();
if (confirm('A new version of this site is available. Load it?')) {
window.location.reload();
}
}
}, false);
네트워크 트래픽을 아끼기 위해서는 manifest가 간결해야 합니다. 이는 모든 페이지마다 호출하라는 말이 아니라 꼭 필요한 사이트에 이미지, CSS, 그리고 자바스크립트 파일을 호출하라는 것입니다 .마지막으로 당신은 모든 브라우저에서 앱캐시가 업데이트 되는 유리한 점을 강제화 할 수 있어야 합니다. 그렇지 않으면 브라우저가 맹목적으로 html페이지를 방문할 때마다 캐시할 것입니다.(그리고 <html manifest=”…”> 요소를 추가해 주세요.)
(이 글은 한국 HTML5 사용자 모임에 기고된 글입니다.)