HTML5 사이트 모바일화

에릭 비델만

소개

요즘은 모바일 웹용 개발이 화두입니다. 올해는 사상 처음으로 스마트폰이 PC를 판매했습니다. 점점 더 많은 사용자가 휴대기기를 사용하여 웹을 탐색하고 있으며, 이는 개발자가 모바일 브라우저에 맞게 사이트를 최적화하는 것이 중요하다는 것을 의미합니다.

'모바일' 전장은 아직도 많은 개발자에게 미지의 분야입니다. 많은 사람들이 모바일 사용자를 아예 무시하는 기존 사이트를 가지고 있습니다. 대신 이 사이트는 주로 데스크톱 브라우징에 적합하게 설계되었으며 모바일 브라우저에서는 성능이 저하됩니다. 이 사이트 (html5rocks.com)도 예외는 아닙니다. 사이트를 출시할 때 모바일 버전의 경우 큰 노력을 들이지 않아도 됩니다.

모바일 친화적인 html5rocks.com 만들기

그래서 html5rocks(기존 HTML5 사이트)를 모바일 친화적인 버전으로 보강하면 좋을 것 같다고 생각했습니다. 스마트폰을 타겟팅할 때 최소한의 업무량을 신경 써야 했습니다. 이 실습의 목표는 완전히 새로운 모바일 사이트를 만들고 코드베이스 2개를 유지하는 것이었습니다. 그것은 영원히 걸렸을 것이며 엄청난 시간 낭비였습니다. 사이트의 구조 (마크업)는 이미 정의되어 있습니다. 디자인도 살펴봤습니다 (CSS). 핵심 기능 (JS)이 거기에 있었습니다. 요점은 많은 장소가 이 보트에 있다는 것입니다.

이 도움말에서는 Google이 Android 및 iOS 기기에 최적화된 모바일 버전의 html5rocks를 어떻게 만들었는지 살펴봅니다. 이들 OS 중 하나를 지원하는 기기에 html5rocks.com을 로드하면 차이점을 확인할 수 있습니다. m.html5rocks.com 또는 기타 부적절한 사이트로 리디렉션되어서는 안 됩니다. HTML5rock은 그대로 사용할 수 있으며, 휴대기기에서는 보기 좋고 잘 작동하는 디자인의 추가 이점도 함께 누릴 수 있습니다.

데스크톱 html5rocks.com 모바일 html5rocks.com
데스크톱 (왼쪽) 및 모바일 (오른쪽)의 html5rocks.com

CSS 미디어 쿼리

한동안 HTML4 및 CSS2에서는 미디어 종속 스타일 시트를 지원했습니다. 예를 들면 다음과 같습니다.

<link rel="stylesheet" media="print" href="printer.css">

인쇄 기기를 타겟팅하고 페이지 콘텐츠가 인쇄될 때 특정 스타일을 제공합니다. CSS3는 미디어 유형이라는 개념을 한 단계 더 발전시켜 미디어 쿼리로 기능을 개선합니다. 미디어 쿼리는 스타일시트에 더 정확하게 라벨을 지정할 수 있도록 하여 미디어 유형의 유용성을 확장합니다. 이를 통해 콘텐츠 자체를 변경하지 않고도 콘텐츠의 프레젠테이션을 특정 범위의 출력 기기에 맞게 맞춤설정할 수 있습니다. 수정이 필요한 기존 레이아웃에 적합합니다.

외부 스타일시트의 media 속성에서 미디어 쿼리를 사용하여 화면 너비, 기기 너비, 방향 등을 타겟팅할 수 있습니다. 전체 목록은 W3C 미디어 쿼리 사양을 참고하세요.

화면 크기 타겟팅

다음 예에서 phone.css는 브라우저에서 '휴대기기'로 간주하는 기기 또는 화면 너비가 320px 이하인 기기에 적용됩니다.

 <link rel='stylesheet'
  media='handheld, only screen and (max-device-width: 320px)' href='phone.css'>

미디어 쿼리 앞에 'only' 키워드를 사용하면 CSS3를 준수하지 않는 브라우저가 규칙을 무시합니다.

다음은 641~800픽셀 사이의 화면 크기를 타겟팅합니다.

 <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"에 대해 논의해야 합니다. 사실, Android와 iOS는 media="handheld"를 무시합니다. 사용자는 media="screen"를 타겟팅하는 스타일시트에서 제공하는 고급형 콘텐츠를 놓치게 되고 개발자가 더 낮은 품질의 media="handheld" 버전을 유지할 가능성이 낮다고 주장합니다. 따라서 '풀 웹'이라는 모토의 일환으로 대부분의 최신 스마트폰 브라우저는 휴대기기 스타일 시트를 그냥 무시합니다.

이 기능은 휴대기기를 타겟팅하는 데 이상적이지만 여러 브라우저에서 여러 방법으로 이 기능을 구현했습니다.

  • 일부 스타일 시트는 휴대기기 스타일 시트만 읽습니다.
  • 어떤 경우에는 휴대기기 스타일 시트만 읽을 수 있지만 그 외에는 화면 스타일 시트가 기본값으로 설정됩니다.
  • 일부는 휴대기기 스타일 시트와 화면 스타일 시트를 모두 읽습니다.
  • 일부 사용자는 화면 스타일 시트를 읽기만 합니다.

Opera Mini는 media="handheld"를 무시하지 않습니다. Windows Mobile이 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에서 미디어 쿼리를 사용하는 방법

미디어 쿼리는 모바일 html5rock 전체에서 많이 사용됩니다. 덕분에 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의 기본 디자인을 정의했지만, 이제 800px 미만의 화면 너비에 새로운 스타일 (mobile.css)을 적용합니다. 미디어 쿼리는 스마트폰 (약 320픽셀)과 iPad (약 768픽셀)에 적용됩니다. 결과: 모바일에서 더 보기 좋도록 (필요한 경우에만) base.css의 스타일을 점진적으로 재정의합니다.

mobile.css가 시행하는 일부 스타일 변경사항은 다음과 같습니다.

  • 사이트 전체의 추가 공백/패딩을 줄입니다. 작은 화면은 공간이 소중하다는 뜻입니다.
  • :hover 상태를 삭제합니다. 터치 기기에는 결코 표시되지 않습니다.
  • 레이아웃을 단일 열로 조정합니다. 이건 나중에 다시 설명하죠
  • 사이트의 기본 컨테이너 div 주위의 box-shadow를 삭제합니다. 큰 상자 그림자는 페이지 성능을 떨어뜨립니다.
  • CSS Flex 상자 모델 box-ordinal-group을 사용하여 홈페이지에서 각 섹션의 순서를 변경했습니다. '주요 HTML5 기능 그룹별 알아보기'가 홈페이지의 '가이드' 섹션 앞에 있지만 모바일 버전에서는 뒤에 표시됩니다. 이러한 순서는 모바일에 더 적합하며 마크업을 변경할 필요가 없었습니다. CSS Flexbox FTW!
  • opacity 변경사항을 삭제합니다. 알파 값을 변경하면 모바일에서 실적 조회가 발생합니다.

모바일 메타 태그

모바일 WebKit는 사용자에게 특정 기기에서 더 나은 탐색 환경을 제공하는 몇 가지 기능을 지원합니다.

표시 영역 설정

첫 번째 메타 설정 (및 가장 자주 사용하게 되는 설정)은 표시 영역 속성입니다. 표시 영역을 설정하면 콘텐츠가 기기 화면에 어떻게 맞춰져야 하는지 브라우저에 알리고 사이트가 모바일에 최적화되었음을 브라우저에 알립니다. 예를 들면 다음과 같습니다.

 <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">

초기 배율이 1인 기기 너비로 표시 영역을 설정하도록 브라우저에 알립니다. 이 예에서는 확대/축소도 허용합니다. 이는 웹 사이트에는 바람직하지 않지만 웹 앱에는 바람직하지 않을 수 있습니다. user-scalable=no를 사용하여 확대/축소를 방지하거나 배율을 특정 수준으로 제한할 수 있습니다.

 <meta name=viewport
  content="width=device-width, initial-scale=1.0, minimum-scale=0.5 maximum-scale=1.0">

Android는 개발자가 사이트가 개발된 화면 해상도를 지정할 수 있도록 하여 표시 영역 메타 태그를 확장합니다.

 <meta name="viewport" content="target-densitydpi=device-dpi">

가능한 target-densitydpi 값은 device-dpi, high-dpi, medium-dpi, low-dpi입니다.

다양한 화면 밀도에 맞게 웹페이지를 수정하려면 자바스크립트에서 -webkit-device-pixel-ratio CSS 미디어 쿼리 또는 window.devicePixelRatio 속성을 사용한 다음 target-densitydpi 메타 속성을 device-dpi로 설정합니다. 이렇게 하면 Android가 웹페이지에서 크기 조정을 하지 않으며 CSS 및 자바스크립트를 통해 각 밀도에 필요한 조정을 할 수 있습니다.

기기 해상도 타겟팅에 관한 자세한 내용은 Android의 WebView 문서를 참고하세요.

전체 화면 탐색

iOS에 해당하는 다른 메타 값이 두 개 있습니다. apple-mobile-web-app-capableapple-mobile-web-app-status-bar-style는 페이지 콘텐츠를 앱과 같은 전체 화면 모드로 렌더링하고 상태 표시줄을 반투명하게 만듭니다.

 <meta name="apple-mobile-web-app-capable" content="yes">
 <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">

사용 가능한 모든 메타 옵션에 대한 자세한 내용은 Safari 참조 문서를 확인하세요.

홈 화면 아이콘

iOS 및 Android 기기도 링크에 rel="apple-touch-icon" (iOS) 및 rel="apple-touch-icon-precomposed" (Android)를 허용합니다. 다음과 같이 사용자가 사이트를 북마크할 때 사용자의 홈 화면에 앱 모양의 화려한 아이콘이 만들어집니다.

 <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 미디어 쿼리를 사용하여 이러한 레이아웃을 만들었습니다. 마크업 변경도 하지 않습니다.

가이드 색인입니다. 튜토리얼 HTML5 기능 페이지입니다. 작성자 프로필 페이지.
사이트 전체에 걸쳐 단일 열 세로 레이아웃

모바일 최적화

우리가 수행한 대부분의 최적화는 애초에 실행되어야 하는 작업입니다. 네트워크 요청 횟수, JS/CSS 압축, gzip (App Engine에서 무료로 제공), DOM 조작 최소화와 같은 작업. 이러한 기법은 일반적인 권장사항이지만 사이트를 급하게 출입할 때 간과되는 경우가 있습니다.

주소 표시줄 자동 숨기기

모바일 브라우저는 데스크톱 브라우저에 비해 화면 공간이 부족합니다. 최악의 경우 다른 플랫폼에서는 페이지 로드가 완료된 후에도 화면 상단에 큰 주소 표시줄이 표시되는 경우가 있습니다.

이 문제를 처리하는 한 가지 쉬운 방법은 자바스크립트를 사용하여 페이지를 스크롤하는 것입니다. 한 픽셀로 그렇게 해도 성가신 주소 표시줄을 관리할 수 있습니다. html5rocks에서 주소 표시줄을 강제로 숨기기 위해 onload 이벤트 핸들러를 window 객체에 연결하고 페이지를 세로로 1픽셀 스크롤했습니다.

주소 표시줄
주소 표시줄이 엉뚱한 화면 공간을 차지합니다.
  // Hides mobile browser's address bar when page is done loading.
  window.addEventListener('load', function(e) {
    setTimeout(function() { window.scrollTo(0, 1); }, 1);
  }, false);

또한 이 리스너는 데스크톱에서 필요하지 않으므로 is_mobile 템플릿 변수로 래핑했습니다.

네트워크 요청 감소, 대역폭 절약

HTTP 요청 수를 줄이면 성능이 크게 향상될 수 있다는 사실이 알려져 있습니다. 휴대기기에서는 브라우저에서 실행할 수 있는 동시 연결 수를 추가로 제한하므로 모바일 사이트에서는 이러한 불필요한 요청을 줄이면 훨씬 더 많은 이점을 얻을 수 있습니다. 또한 휴대전화에서는 대역폭이 제한되는 경우가 많기 때문에 모든 바이트를 잘라내는 것이 중요합니다. 사용자에게 비용이 발생할 수 있습니다.

다음은 네트워크 요청을 최소화하고 html5rock에서 대역폭을 줄이기 위해 Google에서 취한 접근 방식 중 일부입니다.

  • iframe 삭제 - iframe이 느립니다. 튜토리얼 페이지의 타사 공유 위젯 (버즈, Google 프렌즈, Twitter, Facebook)에서 지연 시간이 상당히 늘었습니다. 이러한 API는 <script> 태그를 통해 포함되었으며 페이지 속도를 저하시키는 iframe을 만듭니다. 휴대기기에서 위젯이 삭제되었습니다.

  • display:none: 경우에 따라 마크업이 모바일 프로필에 맞지 않으면 숨겨졌습니다. 홈페이지 상단에 있는 둥근 상자 4개가 좋은 예입니다.

홈페이지의 상자 버튼
홈페이지의 상자 버튼

모바일 사이트에 없습니다. display:none로 컨테이너가 숨겨진 경우에도 브라우저는 각 아이콘에 관한 요청을 계속합니다. 따라서 단순히 이러한 버튼을 숨기는 것만으로는 충분하지 않았습니다. 이렇게 하면 대역폭이 낭비될 뿐 아니라 사용자는 낭비된 대역폭의 열매를 볼 수 없게 됩니다. 해결책은 Django 템플릿에서 'is_mobile' 부울을 만들어 조건부로 HTML 섹션을 생략하는 것이었습니다. 사용자가 스마트 기기에서 사이트를 볼 때 버튼이 표시되지 않습니다.

  • 애플리케이션 캐시 - 오프라인 지원을 제공할 뿐만 아니라 시작 속도가 더 빨라집니다.

  • CSS/JS 압축 - 주로 CSS와 JS를 모두 처리하므로 클로저 컴파일러 대신 YUI 압축 프로그램을 사용합니다. 발생한 한 가지 문제는 YUI 압축 프로그램 2.4.2에서 인라인 미디어 쿼리(스타일시트 내에 표시되는 미디어 쿼리)가 인라인 미디어 쿼리로 변경되었다는 것입니다 (이 문제 참고). YUI Compressor 2.4.4 이상 사용으로 문제가 해결되었습니다.

  • 가능한 경우 CSS 이미지 스프라이트를 사용합니다.

  • 이미지 압축에 pngcrush를 사용했습니다.

  • 작은 이미지에 dataURI를 사용합니다. Base64 인코딩을 사용하면 이미지의 크기가 최대 30%이상 추가되지만 네트워크 요청이 절약됩니다.

  • google.load()를 사용하여 동적으로 로드하는 대신 단일 스크립트 태그를 사용하여 Google 맞춤검색을 자동 로드했습니다. 후자는 추가 요청을 합니다.

<script src="//www.google.com/jsapi?autoload={"modules":[{"name":"search","version":"1"}]}"> </script>
  • 우리의 코드 프리 프린터와 Modernizr은 사용되지 않은 경우에도 모든 페이지에 포함되었습니다. Modernizr는 훌륭하지만, 모든 부하에서 여러 가지 테스트를 실행합니다. 이러한 테스트 중 일부는 DOM을 수정하고 페이지 로드 속도를 저하시킵니다. 이제 이러한 라이브러리가 실제로 필요한 페이지에만 포함됩니다. 요청 2건 :)

추가적인 성능 조정:

  • 모든 JS를 페이지 하단으로 이동했습니다 (가능한 경우).
  • 인라인 <style> 태그를 삭제했습니다.
  • 캐시된 DOM 조회 및 최소화된 DOM 조작 - DOM을 터치할 때마다 브라우저가 리플로우를 수행합니다. 리플로우는 휴대기기에서 훨씬 더 비용이 많이 듭니다.
  • 불필요한 클라이언트 측 코드를 서버로 오프로드했습니다. 특히 현재 페이지의 탐색 스타일 지정 설정을 확인하는 검사는 다음과 같습니다. js 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의 모바일 버전은 애플리케이션 캐시를 사용하여 초기 로드 속도를 높이고 사용자가 오프라인에서 콘텐츠를 읽을 수 있도록 합니다.

사이트에서 AppCache를 구현할 때는 매니페스트 파일을 매니페스트 파일 자체에 명시적으로 또는 과도한 캐시 제어 헤더를 사용하여 암시적으로 캐시하지 않는 것이 매우 중요합니다. 브라우저에 의해 매니페스트가 캐시되면 디버그하기에 악몽입니다. iOS와 Android는 이 파일을 캐싱하는 데 특히 성능이 뛰어나지만 데스크톱 브라우저처럼 캐시를 플러시하는 편리한 방법을 제공하지는 않습니다.

이러한 사이트의 캐싱을 방지하기 위해 먼저 App Engine에서 매니페스트 파일을 캐시하지 않도록 설정했습니다.

- url: /(.*\.(appcache|manifest))
  static_files: \1
  mime_type: text/cache-manifest
  upload: (.*\.(appcache|manifest))
  expiration: "0s"

둘째, JS 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);

네트워크 트래픽을 절약하려면 매니페스트를 단순하게 유지하세요. 즉, 사이트의 모든 페이지를 소개하지 마세요 중요한 이미지, CSS, JavaScript 파일만 표시합니다. 마지막으로 할 일은 앱 캐시 업데이트가 있을 때마다 모바일 브라우저가 대량의 애셋을 다운로드하도록 강제하는 것입니다. 대신 사용자가 방문할 때 브라우저가 html 페이지를 암시적으로 캐시합니다(<html manifest="..."> 속성 포함).