교차 기기 웹 앱 빌드에 대한 비반응형 접근 방식

보리스 스머스
보리스 스머스

미디어 쿼리는 유용하지만...

미디어 쿼리는 멋진 기능입니다. 다양한 크기의 기기 사용자에게 더 나은 환경을 제공하기 위해 스타일시트를 약간 조정하려는 웹사이트 개발자에게는 정말 유용한 기능입니다. 미디어 쿼리를 사용하면 화면 크기에 따라 사이트의 CSS를 맞춤설정할 수 있습니다. 이 도움말을 살펴보기 전에 반응형 디자인에 대해 자세히 알아보고 mediaqueri.es에서 미디어 쿼리 사용의 좋은 예를 확인하세요.

Brad Frost가 이전 기사에서 지적했듯이 디자인 변경은 모바일 웹용으로 빌드할 때 고려해야 할 많은 사항 중 하나일 뿐입니다. 모바일 웹사이트를 구축할 때 미디어 쿼리로 레이아웃을 맞춤설정하는 것뿐이라면 다음과 같은 상황이 발생합니다.

  • 모든 기기에서 동일한 자바스크립트, CSS, 애셋 (이미지, 동영상)을 가져오기 때문에 로드 시간이 필요 이상으로 길어집니다.
  • 모든 기기가 동일한 초기 DOM을 가져오기 때문에 개발자가 지나치게 복잡한 CSS를 작성해야 할 수 있습니다.
  • 각 기기에 맞는 맞춤 상호작용을 지정할 수 있는 유연성이 거의 없습니다.

웹 앱에는 미디어 쿼리 이상의 것이 필요합니다

오해하지 마세요. 저는 미디어 쿼리를 통한 반응형 디자인을 싫어하지도 않아요. 그리고 확실히 이 시대에 적합한 디자인이라고 생각해요. 또한 위에서 언급한 문제 중 일부는 반응형 이미지, 동적 스크립트 로드 등의 접근 방식으로 해결할 수 있습니다. 그러나 특정 시점에는 증분 조정을 너무 많이 실행할 수 있으며 다른 버전을 제공하는 것이 더 나을 수 있습니다.

빌드하는 UI가 복잡해지고 단일 페이지 웹 앱에 끌리게 되므로 각 기기 유형에 맞게 UI를 맞춤설정하기 위해 더 많은 작업을 해야 합니다. 이 문서에서는 최소한의 노력으로 이러한 맞춤설정을 수행하는 방법을 설명합니다. 일반적인 접근 방식은 방문자의 기기를 적절한 기기 클래스로 분류하고 해당 기기에 적절한 버전을 제공하는 동시에 버전 간 코드 재사용을 극대화하는 것입니다.

어떤 기기 클래스를 타겟팅하고 있나요?

인터넷에는 수많은 기기가 있으며 거의 모두 브라우저가 있습니다. 문제는 Mac 노트북, Windows 워크스테이션, iPhone, iPad, 터치 입력이 지원되는 Android 휴대전화, 스크롤 휠, 키보드, 음성 입력, 압력 감도가 있는 기기, 스마트시계, 토스터, 냉장고 등 다양성에 있습니다. 어떤 기기는 어디에나 있고, 다른 기기는 매우 드뭅니다.

다양한 기기.
다양한 기기 (출처).

좋은 사용자 환경을 만들기 위해서는 사용자가 누구인지, 사용자가 사용하는 기기가 무엇인지 알아야 합니다. 마우스와 키보드로 데스크톱 사용자를 위한 사용자 인터페이스를 빌드하고 이를 스마트폰 사용자에게 제공하는 경우 인터페이스는 다른 화면 크기 및 다른 입력 모달리티에 맞게 설계되었기 때문에 불편을 겪을 수 있습니다.

접근 방식 스펙트럼에는 두 가지 극단적인 종리가 있습니다.

  1. 모든 기기에서 작동하는 하나의 버전을 빌드하세요. 결과적으로 기기마다 디자인 고려사항이 다르기 때문에 UX에 문제가 발생합니다.

  2. 지원하려는 각 기기의 버전을 빌드합니다. 너무 많은 버전의 애플리케이션을 빌드하게 되므로 이 작업은 영원히 걸릴 수 있습니다. 또한 다음에 새 스마트폰이 출시되면(거의 매주 제공) 또 다른 버전을 만들어야 합니다.

여기에는 근본적인 장단점이 있습니다. 기기 카테고리가 많을수록 더 나은 사용자 환경을 제공할 수 있지만 설계, 구현, 유지 관리에 더 많은 작업이 필요합니다.

성능상의 이유로 또는 다양한 기기 클래스에 제공하려는 버전이 크게 다른 경우에는 결정하는 각 기기 클래스별로 별도의 버전을 만드는 것이 좋을 수 있습니다. 그렇지 않으면 반응형 웹 디자인이 완전히 합리적인 접근 방식입니다.

가능한 해결책

이때 절충안은 기기를 카테고리로 분류하고 각 카테고리에 가능한 최상의 환경을 설계하는 것입니다. 선택하는 카테고리는 제품 및 타겟 사용자에 따라 다릅니다. 다음은 오늘날 존재하는 인기 있는 웹 지원 기기를 포괄하는 샘플 분류입니다.

  1. 작은 화면 + 터치 (대부분 휴대전화)
  2. 대형 화면 + 터치 (대부분 태블릿)
  3. 큰 화면 + 키보드/마우스 (대부분 데스크톱/노트북)

이는 가능한 여러 분류 중 하나일 뿐이지만 이 글을 작성하는 시점에는 의미가 있습니다. 위 목록에서는 터치스크린이 없는 휴대기기 (예: 피처폰, 일부 전용 eBook 리더)가 없습니다. 그러나 이러한 앱 대부분은 키보드 탐색 또는 스크린 리더 소프트웨어가 설치되어 있으므로 접근성을 염두에 두고 사이트를 구축하면 문제가 없습니다.

폼 팩터별 웹 앱의 예

다양한 폼 팩터에 대해 완전히 다른 버전을 제공하는 웹 속성의 예가 많이 있습니다. Facebook과 마찬가지로 Google 검색도 마찬가지입니다. 이를 위해 성능 (애셋 가져오기, 페이지 렌더링)과 보다 일반적인 사용자 환경을 모두 고려합니다.

네이티브 앱의 세계에서 많은 개발자가 기기 클래스에 맞게 환경을 맞춤설정합니다. 예를 들어 iPad용 Flipboard는 iPhone의 Flipboard와 UI가 매우 다릅니다. 태블릿 버전은 두 손 사용 및 가로 뒤집기용으로 최적화되어 있는 반면 휴대전화 버전은 한 손 상호작용 및 세로 뒤집기용으로 고안되었습니다. 다른 많은 iOS 애플리케이션은 아래 표시된 Things (할 일 목록) 및 Showyou (소셜 동영상)와 같이 상당히 다른 휴대전화 및 태블릿 버전을 제공합니다.

스마트폰 및 태블릿을 위한 중요한 UI 맞춤설정
휴대전화 및 태블릿을 위한 중요한 UI 맞춤설정

접근 방식 #1: 서버 측 감지

서버에서는 처리 중인 기기에 관한 이해가 훨씬 제한적입니다. 사용 가능한 가장 유용한 단서는 아마도 모든 요청에서 User-Agent 헤더를 통해 제공되는 사용자 에이전트 문자열일 것입니다. 따라서 동일한 UA 스니핑 접근 방식이 여기에서 작동합니다. 실제로 DeviceAtlas와 WURFL 프로젝트는 이미 이 작업을 실행하며 기기에 관한 다양한 추가 정보를 제공합니다.

안타깝게도 각각에는 나름의 어려움이 있습니다. WURFL은 매우 크고 20MB의 XML을 포함하므로 각 요청에 상당한 서버 측 오버헤드가 발생할 수 있습니다. 성능상의 이유로 XML을 분할하는 프로젝트도 있습니다. DeviceAtlas는 오픈소스가 아니며 사용하려면 유료 라이선스가 필요합니다.

Detect Mobile Browsers 프로젝트와 같이 더 간단한 무료 대안도 있습니다. 물론 단점은 기기 감지의 포괄성이 떨어지게 된다는 점입니다. 또한 휴대기기와 모바일이 아닌 기기만 구분하며 임시 조정을 통해서만 제한된 태블릿 지원을 제공합니다.

접근 방식 #2: 클라이언트 측 감지

기능 감지를 사용하여 사용자의 브라우저와 기기에 관한 많은 정보를 얻을 수 있습니다. 기기에 터치 기능이 있는지, 화면이 큰지 작은지 확인해야 합니다.

소형 터치 기기와 대형 터치 기기를 구별하기 위해 어딘가에 선을 그려야 합니다. 5인치 Galaxy Note와 같은 특이한 케이스는 어떨까요? 다음 그래픽은 많이 사용되는 여러 Android 및 iOS 기기가 오버레이된 (상응하는 화면 해상도)을 보여줍니다. 별표는 기기가 2배 밀도로 제공될 수 있거나 제공될 수 있음을 나타냅니다. 픽셀 밀도가 두 배가 될 수는 있지만 CSS는 여전히 동일한 크기를 보고합니다.

CSS의 픽셀 관련 참고 사항: 모바일 웹의 CSS 픽셀은 화면 픽셀과 동일하지 않습니다. iOS 레티나 기기에서는 픽셀 밀도를 두 배로 높이는 방법 (예: iPhone 3GS 대 4, iPad 2 대 3)이 도입되었습니다. Retina Mobile Safari UA는 웹의 중단을 방지하기 위해 여전히 동일한 기기 너비를 보고합니다. 다른 기기 (예: Android)가 더 높은 해상도 디스플레이를 받으며 동일한 기기 너비 트릭을 실행하는 것입니다.

기기 해상도 (픽셀)
기기 해상도 (픽셀)

하지만 세로 모드와 가로 모드를 모두 고려하는 것이 중요하기 때문에 이 결정을 복잡하게 할 수 있습니다. 기기의 방향을 변경할 때마다 페이지를 새로고침하거나 추가 스크립트를 로드하지는 않으려고 하지만 페이지를 다르게 렌더링하고 싶을 수 있습니다.

다음 다이어그램에서 정사각형은 세로 모드 및 가로 모드 윤곽선을 오버레이하고 정사각형을 완성한 결과로 각 기기의 최대 크기를 나타냅니다.

세로 및 가로 해상도 (픽셀)
세로 및 가로 해상도 (픽셀)

기준점을 650px로 설정하면 iPhone, Galaxy Nexus는 Smalltouch로, iPad는 Galaxy Tab은 '태블릿'으로 분류됩니다. 이 경우 중성 갤럭시 노트는 '휴대전화'로 분류되며 휴대전화 레이아웃을 사용합니다.

따라서 합리적인 전략은 다음과 같습니다.

if (hasTouch) {
  if (isSmall) {
    device = PHONE;
  } else {
    device = TABLET;
  }
} else {
  device = DESKTOP;
}

실제 특성 감지 접근 방식의 최소 샘플을 확인하세요.

여기서 다른 접근 방식은 UA 스니핑을 사용하여 기기 유형을 감지하는 것입니다. 기본적으로 휴리스틱 세트를 만들어 사용자의 navigator.userAgent과 일치시킵니다. 의사 코드는 다음과 같습니다.

var ua = navigator.userAgent;
for (var re in RULES) {
  if (ua.match(re)) {
    device = RULES[re];
    return;
  }
}

실제 UA 감지 방식 샘플을 확인해 보세요.

클라이언트 측 로드 관련 참고사항

서버에서 UA 감지를 실행하는 경우 새 요청 시 제공할 CSS, 자바스크립트, DOM을 결정할 수 있습니다. 그러나 클라이언트 측 감지를 실행하는 경우 상황이 더 복잡합니다. 다음과 같은 몇 가지 옵션이 있습니다.

  1. 이 기기 유형의 버전이 포함된 기기별 URL로 리디렉션합니다.
  2. 기기 유형별 애셋을 동적으로 로드합니다.

첫 번째 방법은 간단합니다. window.location.href = '/tablet'과 같은 리디렉션이 필요합니다. 그러나 이제 위치에 이러한 기기 유형 정보가 추가되므로 History API를 사용하여 URL을 정리할 수 있습니다. 안타깝게도 이 접근 방식은 리디렉션이 수반되므로, 특히 휴대기기에서는 속도가 느릴 수 있습니다.

두 번째 접근 방식은 구현하기가 조금 더 복잡합니다. CSS 및 JS를 동적으로 로드하는 메커니즘이 필요하며 브라우저에 따라 <meta viewport> 맞춤설정과 같은 작업을 하지 못할 수도 있습니다. 또한 리디렉션이 없으므로 게재되었던 원본 HTML이 유지됩니다. 물론 JavaScript로 조작할 수 있지만 애플리케이션에 따라 느리거나 부적절할 수 있습니다.

클라이언트 또는 서버 결정

이러한 접근 방식 간의 장단점은 다음과 같습니다.

전문가 고객:

  • UA가 아닌 화면 크기/기능을 기반으로 하기 때문에 향후에도 더 대비할 수 있습니다.
  • UA 목록을 지속적으로 업데이트할 필요가 없습니다.

프로 서버:

  • 기기에 제공할 버전을 완전히 제어합니다.
  • 성능 향상: 클라이언트 리디렉션이나 동적 로드가 필요하지 않습니다.

저는 개인적으로 device.js와 클라이언트 측 감지로 시작하는 것을 선호합니다. 애플리케이션이 발전함에 따라 클라이언트 측 리디렉션이 성능에 상당한 단점이 있다는 사실을 발견할 경우 쉽게 device.js 스크립트를 삭제하고 서버에서 UA 감지를 구현할 수 있습니다.

device.js 소개

Device.js는 특별한 서버 측 구성 없이 시맨틱 미디어 쿼리 기반의 기기 감지를 실행할 수 있는 시작점으로, 사용자 에이전트 문자열을 파싱하는 데 필요한 시간과 노력을 절약합니다.

<head> 상단에 제공하려는 사이트 버전을 나타내는 검색엔진에 적합한 마크업 (link rel=alternate)을 제공하는 것이 좋습니다.

<link rel="alternate" href="http://foo.com" id="desktop"
    media="only screen and (touch-enabled: 0)">

그런 다음 서버 측 UA 감지를 직접 실행하고 버전 리디렉션을 처리하거나 device.js 스크립트를 사용하여 기능 기반 클라이언트 측 리디렉션을 실행할 수 있습니다.

자세한 내용은 device.js 프로젝트 페이지를 참고하고 클라이언트 측 리디렉션에 device.js를 사용하는 가짜 애플리케이션도 참고하세요.

권장사항: 폼 팩터별 뷰가 있는 MVC

각 기기 유형에 하나씩, 완전히 별개의 세 개의 앱을 빌드해야 한다고 생각하실 것입니다. 아니요 코드 공유가 핵심입니다.

백본, Ember 등 MVC와 유사한 프레임워크를 사용해 보셨을 것입니다. 지금까지 문제 분리 원칙, 특히 UI (뷰 레이어)가 로직 (모델 레이어)에서 분리되어야 한다는 점을 잘 알고 계실 것입니다. 처음이라면 MVC의 리소스JavaScript의 MVC 중 일부를 시작해 보세요.

교차 기기 스토리가 기존 MVC 프레임워크에 잘 맞습니다. 뷰를 별도의 파일로 쉽게 이동하여 각 기기 유형에 맞는 맞춤 뷰를 만들 수 있습니다. 그러면 개발자는 뷰 레이어를 제외한 모든 기기에 동일한 코드를 제공할 수 있습니다.

교차 기기 MVC입니다.
교차 기기 MVC.

프로젝트의 구조는 다음과 같을 수 있습니다. 물론 애플리케이션에 따라 가장 적합한 구조를 자유롭게 선택할 수 있습니다.

model/ (공유 모델) item.js item-collection.js

컨트롤러s/ (공유 컨트롤러) item-controller.js

버전/ (기기별 항목) 태블릿/ 데스크톱/ 휴대전화/ (휴대전화 전용 코드) style.css index.html views/ item.js item-list.js

이러한 종류의 구조를 사용하면 기기마다 맞춤 HTML, CSS, 자바스크립트가 있으므로 각 버전이 로드하는 애셋을 완전히 제어할 수 있습니다. 이는 매우 강력하며, 적응형 이미지와 같은 트릭에 의존하지 않고도 교차 기기 웹을 위한 가장 가볍고 효율적인 개발 방법으로 이어질 수 있습니다.

즐겨 사용하는 빌드 도구를 실행한 후에는 빠르게 로드할 수 있도록 모든 자바스크립트 및 CSS를 단일 파일로 연결하고 축소하며 프로덕션 HTML의 형식은 다음과 같습니다 (휴대전화의 경우 device.js 사용).

<!doctype html>
<head>
  <title>Mobile Web Rocks! (Phone Edition)</title>

  <!-- Every version of your webapp should include a list of all
        versions. -->
  <link rel="alternate" href="http://foo.com" id="desktop"
      media="only screen and (touch-enabled: 0)">
  <link rel="alternate" href="http://m.foo.com" id="phone"
      media="only screen and (max-device-width: 650px)">
  <link rel="alternate" href="http://tablet.foo.com" id="tablet"
      media="only screen and (min-device-width: 650px)">

  <!-- Viewport is very important, since it affects results of media
        query matching. -->
  <meta name="viewport" content="width=device-width">

  <!-- Include device.js in each version for redirection. -->
  <script src="device.js"></script>

  <link rel="style" href="phone.min.css">
</head>
<body>
  <script src="phone.min.js"></script>
</body>

(touch-enabled: 0) 미디어 쿼리는 비표준 쿼리이지만(Firefox에서만 moz 공급업체 접두사 뒤에 구현됨) device.js에 의해 Modernizr.touch 덕분에 올바르게 처리됩니다.

버전 재정의

기기 감지가 때때로 잘못될 수 있으며 경우에 따라 사용자는 휴대전화에서 태블릿 레이아웃을 선호 (예: Galaxy Note 사용)할 수 있습니다. 따라서 수동으로 재정의하려는 경우 사용할 사이트 버전을 사용자가 선택할 수 있도록 하는 것이 중요합니다.

일반적인 접근 방식은 모바일 버전에서 데스크톱 버전의 링크를 제공하는 것입니다. 이는 구현하기에 쉽지만 device.js는 device GET 매개변수를 사용하여 이 기능을 지원합니다.

마무리

요약하자면, 반응형 디자인에 어울리지 않는 교차 기기 단일 페이지 UI를 빌드하는 경우 다음과 같이 해야 합니다.

  1. 지원할 기기 클래스 집합과 기기를 클래스로 분류할 기준을 선택합니다.
  2. 관심사를 강력하게 분리하여 나머지 코드베이스와 뷰를 분할하여 MVC 앱을 빌드합니다.
  3. 클라이언트 측 기기 클래스 감지를 수행하려면 device.js를 사용합니다.
  4. 준비가 되면 기기 클래스마다 스크립트와 스타일시트를 각각 하나씩 패키징합니다.
  5. 클라이언트 측 리디렉션 성능이 문제라면 device.js를 중단하고 서버 측 UA 감지로 전환하세요.