AMP의 고급 상호작용

Accelerated Mobile Pages (AMP)는 빠르고 빠르고 아름다우며 실적이 우수한 웹사이트와 광고를 만들 수 있는 오픈소스 이니셔티브입니다.

AMP를 처음 사용하는 경우 다음 리소스를 통해 간단한 개요를 살펴보세요.

동기

AMP는 이미지 캐러셀라이트박스 같은 UI 구성요소로 풍부하고 역동적인 콘텐츠를 지원합니다. AMP는 AMP 작업을 통해 한 구성요소가 다른 구성요소에서 작업을 트리거하는 간단한 방법을 지원합니다.

하지만 원하는 경우에는 어떻게 해야 할까요?

  • AMP 구성요소를 맞춤설정하시겠습니까?
  • 예를 들어 이미지 캐러셀에 현재 슬라이드와 총 슬라이드 수를 표시하는 맞춤 라벨을 표시합니다.
  • 스테이트풀(Stateful) 동작을 추가하시겠어요?
  • 예를 들어 사용자가 선택한 제품의 수량이 현재 재고를 초과하는 경우 '장바구니에 추가' 버튼을 사용 중지합니다.

이전에는 UI 구성요소 간의 강력한 통신 채널이 없고 변경 가능한 공유 상태를 보유할 수 없었기 때문에 AMP에서 이러한 기능을 구현하기가 어려웠습니다. 이러한 사용 사례를 해결하기 위해 AMP에 강력한 새 구성요소를 구축했습니다.

<amp-bind>

<amp-bind>는 데이터 결합 및 JS와 유사한 표현식을 통해 맞춤 상호작용을 제공하는 새로운 AMP 구성요소입니다. 이 Codelab에서는 <amp-bind>를 사용하여 풍부한 맞춤 상호작용으로 AMP 페이지를 빌드하는 방법을 안내합니다.

빌드할 항목

이 Codelab에서는 전자상거래 제품 세부정보 페이지를 빌드합니다.

  • AMP HTML 및 AMP 구성요소를 사용하여 빠르고 풍부한 사용자 환경이 포함된 웹페이지 빌드
  • <amp-bind>를 사용하여 교차 요소 상호작용 추가
  • <amp-state>를 사용하여 주문형으로 추가 제품 데이터 가져오기

학습할 내용

  • 데이터 결합과 표현식을 사용하여 <amp-bind>로 멋진 대화형 AMP 페이지를 빌드하는 방법

필요한 항목

  • 원하는 브라우저
  • 원하는 텍스트 편집기
  • Node.js 및 NPM
  • 샘플 코드
  • HTML, CSS, 자바스크립트에 대한 기본 지식

코드 다운로드

먼저 Codelab의 시작 코드를 ZIP 파일로 다운로드합니다.

다운로드

또는 git를 통해 다음을 실행합니다.

git clone https://github.com/googlecodelabs/advanced-interactivity-in-amp.git

설치 종속 항목

필요한 경우 압축 파일을 풀고 디렉토리로 이동합니다. npm install을 실행하여 종속 항목을 설치합니다.

cd advanced-interactivity-in-amp-codelab
npm install

개발 서버 실행

다음과 같이 node.js로 개발 서버를 시작합니다.

node app.js

웹브라우저에서 http://localhost:3000으로 이동하여 실행 중인 AMP 페이지를 확인합니다.

AMP 상용구

AMP 페이지는 안정적인 성능을 제공하기 위한 몇 가지 제한사항이 있는 HTML 페이지입니다. AMP 페이지에는 Google 검색에서 AMP 페이지로 식별되는 약간의 특수 마크업이 있습니다.

기본 AMP 페이지는 다음과 같습니다.

<!doctype html>
<html amp>
 <head>
   <meta charset="utf-8">
   <link rel="canonical" href="hello-world.html">
   <meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">
   <style amp-boilerplate>body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}</style><noscript><style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript>
   <script async src="https://cdn.ampproject.org/v0.js"></script>
 </head>
 <body>Hello World!</body>
</html>

AMP 구성요소

시작 코드(static/index.html)는 몇 가지 AMP 구성요소와 함께 페이지 콘텐츠 (이미지, 텍스트 등)가 포함된 기본 AMP 페이지를 기반으로 빌드됩니다.

<script async custom-element="amp-carousel" src="https://cdn.ampproject.org/v0/amp-carousel-0.1.js"></script>
<script async custom-template="amp-mustache" src="https://cdn.ampproject.org/v0/amp-mustache-0.1.js"></script>
<script async custom-element="amp-form" src="https://cdn.ampproject.org/v0/amp-form-0.1.js"></script>
<script async custom-element="amp-selector" src="https://cdn.ampproject.org/v0/amp-selector-0.1.js"></script>

AMP 구성요소는 AMP 페이지에 풍부한 상호작용을 더해주는 추가적인 기능과 UI 구성요소를 제공합니다. 시작 코드는 다음과 같은 AMP 구성요소를 사용합니다.

  • <amp-carousel>
  • 제품의 여러 뷰를 표시하는 이미지 캐러셀
  • <amp-mustache>
  • amp-form의 서버 응답을 렌더링하기 위한 템플릿 시스템
  • <amp-form>
  • AMP 페이지에 필요한 <form> 요소의 특수 기능을 추가합니다.
  • <amp-selector>
  • 요소 그룹의 하나 이상의 요소를 선택할 수 있는 의미론적 방법을 제공합니다. amp-form의 입력 소스로 사용할 수 있습니다.

기본 상호작용

시작 코드는 몇 가지 기본적인 상호작용을 제공합니다.

  • 이미지 캐러셀 (<amp-carousel>)은 제품의 여러 뷰를 표시합니다.
  • 페이지 하단에서 '장바구니에 추가' 버튼을 탭하여 제품을 <amp-form> (장바구니를 통해)에 추가할 수 있습니다.

이미지 캐러셀을 스와이프하고 '장바구니에 추가' 버튼을 탭하세요.

환경 개선

시작 코드는 매우 기본적인 사용자 환경을 제공합니다. 몇 가지 방법을 통해 사용자 환경을 개선할 수 있습니다.

  • 현재 슬라이드와 총 슬라이드 수를 표시하는 표시기를 추가합니다.
  • 사용자가 다른 셔츠 색상을 선택하면 이미지 캐러셀을 변경하여 선택한 색상의 셔츠 이미지를 표시합니다.

<amp-bind> 구성요소를 도입하기 전에는 이러한 기능을 추가하는 것이 불가능했습니다. <amp-bind> 실습을 통해 새로운 기능을 샘플 코드에 추가해 보세요.

<amp-bind> 확장 프로그램 설치하기

<amp-bind>는 데이터 결합 및 JS와 유사한 표현식을 통해 맞춤 상호작용을 제공하는 새로운 AMP 구성요소입니다. <amp-bind> 서비스를 사용하려면 페이지에 설치해야 합니다.

static/index.html 파일을 열고 페이지의 <head> 섹션에서 AMP 구성요소 목록에 다음 스크립트를 추가합니다.

<script async custom-element="amp-bind"
    src="https://cdn.ampproject.org/v0/amp-bind-0.1.js"></script>

슬라이드 표시기 추가

<amp-bind>는 요소 속성을 맞춤 표현식에 결합하는 방식으로 작동합니다. 이러한 표현식은 'state&quot'(변경 가능한 JSON 데이터)를 참조할 수 있습니다. <amp-bind>에 포함된 <amp-state> 구성요소를 통해 이 상태를 초기화할 수 있습니다.

이제 상태 변수를 초기화하여 이미지 캐러셀에 현재 표시된 슬라이드의 색인을 추적해보겠습니다. static/index.html을 열고 페이지의 <body> 상단 (헤더 앞)에 다음을 추가합니다.

<amp-state id="selected">
  <script type="application/json">
    {
      "slide": 0
    }
  </script>
</amp-state>

<amp-state> 요소 내의 데이터는 연결된 ID로 액세스할 수 있습니다. 예를 들어 이 변수를 다음 표현식 프래그먼트로 참조할 수 있습니다.

selected.slide // Evaluates to 0.

다음으로 사용자가 기존 <amp-carousel> 요소에 '작업'을 추가하여 캐러셀의 슬라이드를 변경할 때 이 변수를 업데이트합니다.

<amp-carousel type="slides" layout="fixed-height" height=250 id="carousel"
    on="slideChange:AMP.setState({selected: {slide: event.index}})">

이제 <amp-carousel>가 표시된 슬라이드가 변경될 때마다 AMP.setState 작업이 다음 인수로 호출됩니다.

{
  selected: {
    slide: event.index
  }
}

event.index 표현식이 새 슬라이드 색인으로 평가되고 AMP.setState() 작업은 이 객체 리터럴을 현재 상태로 병합합니다. 이렇게 하면 현재 selected.slide 값이 event.index 값으로 대체됩니다.

다음으로 현재 표시된 슬라이드를 추적하는 이 상태 변수를 활용하여 슬라이드 표시기를 만들어 보겠습니다. 슬라이드 표시기 요소 (<!-- TODO: "Add a slide indicator" -->이라고 함)를 찾아 다음 바인딩을 하위 요소에 추가합니다.

<!-- TODO: "Add a slide indicator" -->
<p class="dots">
  <!-- The <span> element corresponding to the current displayed slide
       will have the 'current' CSS class. -->
  <span [class]="selected.slide == 0 ? 'current' : ''" class="current"></span>
  <span [class]="selected.slide == 1 ? 'current' : ''"></span>
  <span [class]="selected.slide == 2 ? 'current' : ''"></span>
</p>

[class]class 속성을 변경하는 결합이며, 이를 사용해 모든 요소에서 CSS 클래스를 추가하거나 삭제할 수 있습니다.

이제 페이지를 새로고침해 보세요. 캐러셀의 슬라이드를 변경하면 다음과 같은 작업이 실행됩니다.

  1. slideChange 이벤트 트리거...
  2. AMP.setState 작업을 호출하는 것은...
  3. 상태 변수 selected.slide를 업데이트합니다.
  4. 그러면 표시기 <span> 요소에서 [class] 결합이 업데이트됩니다.

좋아요 이제 슬라이드 표시기가 작동합니다.

선택된 색상을 변경할 때 다른 셔츠 색상의 이미지가 표시되면 좋습니다. amp-bind를 사용하면 <amp-carousel> 내의 <amp-img> 요소에 [src]를 바인딩할 수 있습니다.

그러나 먼저 각 색상 셔츠의 이미지 소스 URL로 상태 데이터를 초기화해야 합니다. 새로운 <amp-state> 요소로 그렇게 해 보겠습니다.

<!-- Available shirts. Maps unique string identifier to color and image URL string. -->
<amp-state id="shirts">
  <script type="application/json">
    {
      "1001": {
        "color": "black",
        "image": "./shirts/black.jpg"
      },
      "1002": {
        "color": "blue",
        "image": "./shirts/blue.jpg"
      },
      "1010": {
        "color": "brown",
        "image": "./shirts/brown.jpg"
      },
      "1014": {
        "color": "dark green",
        "image": "./shirts/dark-green.jpg"
      },
      "1015": {
        "color": "gray",
        "image": "./shirts/gray.jpg"
      },
      "1016": {
        "color": "light gray",
        "image": "./shirts/light-gray.jpg"
      },
      "1021": {
        "color": "navy",
        "image": "./shirts/navy.jpg"
      },
      "1030": {
        "color": "wine",
        "image": "./shirts/wine.jpg"
      }
    }
  </script>
</amp-state>

<amp-state> 요소에는 셔츠 식별자 문자열 (예: SKU)을 해당 셔츠의 색상 및 이미지 URL에 매핑하는 JSON 객체가 포함되어 있습니다. JSON 배열은 여기에서도 작동하지만 객체를 사용하면 다른 멋진 작업을 할 수 있습니다. 이러한 작업은 곧 제공될 예정입니다.

이제 셔츠 식별자를 통해 이미지 URL에 액세스할 수 있습니다. 예를 들어 shirts['10014'].color"dark green"로 평가되고 shirts['10030'].image는 "wine" 셔츠 색상의 이미지 URL을 반환합니다.

선택한 SKU를 추적하는 또 다른 상태 변수를 추가하면 선택한 SKU가 변경될 때 표현식을 <amp-img> 요소에 바인딩하여 src 속성을 업데이트할 수 있습니다. 새 sku 키를 기존 amp-state#selected 요소 JSON에 추가합니다.

<amp-state id="selected">
  <script type="application/json">
    {
      "slide": 0,
      "sku": "1001"
    }
  </script>
</amp-state>

새로운 색상이 선택될 때마다 selected.sku 변수를 업데이트하는 <amp-selector>에 'quot;' 작업을 추가합니다.

<amp-selector name="color" 
    on="select:AMP.setState({selected: {sku: event.targetOption}})">

그런 다음 <amp-carousel> 내의 <amp-img> 요소에 바인딩을 추가합니다(<!-- TODO: "Changing images in amp-carousel-->" 참고).

<!-- Update the `src` of each <amp-img> when the `selected.sku` variable changes. -->
<amp-img width=200 height=250 src="./shirts/black.jpg"
    [src]="shirts[selected.sku].image"></amp-img>
<amp-img width=300 height=375 src="./shirts/black.jpg"
    [src]="shirts[selected.sku].image"></amp-img>
<amp-img width=400 height=500 src="./shirts/black.jpg"
    [src]="shirts[selected.sku].image"></amp-img>

참고: 실제로 캐러셀의 각 이미지에는 다른 src가 있을 수 있습니다. 이는 하나의 이미지를 이미지 배열로 대체함으로써 실행할 수 있습니다. 편의를 위해 이 Codelab에서는 다양한 확대 시 단일 이미지를 사용합니다.

이제 페이지를 새로고침하고 셔츠에 다른 색상을 선택하세요. 다른 색상을 선택하면 캐러셀의 이미지가 업데이트되어 선택한 색상의 셔츠가 표시됩니다.

페이지 로드 시 바인딩할 수 있는 데이터가 가져오기에 너무 크거나 복잡한 경우 어떻게 해야 할까요? 또는 각 SKU에 찾는 데 시간이 오래 걸리는 가격이 지정된 경우에는 어떻게 할까요? 표시되지 않는 항목의 SKU 가격을 찾는 것은 헛수고입니다.

셔츠에 사용할 수 있는 사이즈 가져오는 중

Codelab 데이터를 통해 원격 데이터를 가져와 SKU 가격을 확인할 수 있는 기능을 활용해 보겠습니다. app.js의 Express.js 개발 서버에는 이미 셔츠 SKU가 지정된 각 크기의 사이즈 및 가격을 반환하는 엔드포인트 /shirts/sizes?shirt=<sku>이 있습니다. 네트워크 지연 시간을 시뮬레이션하기 위해 1초의 인위적인 지연이 있는 응답을 전송합니다.

요청

응답

GET /shirts/sizesAndPrices?sku=1001

{"1001: {"sizes": {"XS": 8.99, "S" 9.99}}}

<amp-state> 요소 내의 JSON 데이터와 마찬가지로 이러한 가져오기에서 반환된 원격 데이터는 요소의 id 속성에 병합되고 사용할 수 있습니다. 예를 들어 위의 예제 응답에서 반환되는 데이터는 다음과 같은 표현식에서 액세스할 수 있습니다.

표현식

결과

shirts['1001'].sizes['XS']

8.99

이제 전자상거래 예제에 적용해 보겠습니다. 먼저 새 SKU가 선택되면 이 셔츠 데이터를 가져오게 하겠습니다. amp-state#shirts 요소에 [src] 바인딩을 추가합니다.

<!-- When `selected.sku` changes, update the `src` attribute and fetch
     JSON at the new URL. Then, merge that data under `id` ("shirts"). -->
<amp-state id="shirts" [src]="'/shirts/sizesAndPrices?sku=' + selected.sku">

다음으로, 특정 SKU의 사용 불가 크기를 명확하게 표시합니다. "unavailable" CSS 클래스는 요소를 통해 대각선을 추가합니다. amp-selector[name="size"]에 없는 요소 중 사용할 수 없는 크기에 추가할 수 있습니다.

<amp-selector name="size">
  <table>
    <tr>
      <!-- If 'XS' size is available for selected SKU, return empty string.
           Otherwise, return 'unavailable'. -->
      <td [class]="shirts[selected.sku].sizes['XS'] ? '' : 'unavailable'">
        <div option="XS">XS</div>
      </td>
      <td [class]="shirts[selected.sku].sizes['S'] ? '' : 'unavailable'">
        <div option="S">S</div>
      </td>
      <td [class]="shirts[selected.sku].sizes['M'] ? '' : 'unavailable'">
        <div option="M">M</div>
      </td>
      <td [class]="shirts[selected.sku].sizes['L'] ? '' : 'unavailable'">
        <div option="L">L</div>
      </td>
      <td [class]="shirts[selected.sku].sizes['XL'] ? '' : 'unavailable'">
        <div option="XL">XL</div>
      </td>
    </tr>
  </table>
</amp-selector>

이제 페이지를 새로고침해 봅니다. 새 SKU(셔츠 색상)를 선택하면 약간의 지연 후 주문할 수 없는 사이즈에 선이 그어집니다.

여기서 사소한 문제가 생깁니다. 기본으로 선택되는 색상인 검은색 셔츠는 어떻게 해야 할까요? amp-state#shirts에 검은색 셔츠의 사이즈와 가격 데이터를 추가해야 합니다.

<amp-state id="shirts" [src]="'/shirts/sizesAndPrices?sku=' + selected.sku">
  <script type="application/json">
    {
      "1001": {
        "color": "black",
        "image": "./shirts/black.jpg",
        "sizes": {
          "XS": 8.99,
          "S": 9.99
        }
      },
<!-- ... -->

관련 요소의 기본 상태도 업데이트합니다.

<amp-selector name="size">
  <table>
    <tr>
      <!-- If 'XS' size is available for selected SKU, return empty string.
           Otherwise, return 'unavailable'. -->
      <td [class]="shirts[selected.sku].sizes['XS'] ? '' : 'unavailable'">
        <div option="XS">XS</div>
      </td>
      <td [class]="shirts[selected.sku].sizes['S'] ? '' : 'unavailable'">
        <div option="S">S</div>
      </td>
      <!-- Add the ‘unavailable' class to the next three <td> elements
           to be consistent with the available sizes of the default SKU. -->
      <td class="unavailable" 
          [class]="shirts[selected.sku].sizes['M'] ? '' : 'unavailable'">
        <div option="M">M</div>
      </td>
      <td class="unavailable" 
          [class]="shirts[selected.sku].sizes['L'] ? '' : 'unavailable'">
        <div option="L">L</div>
      </td>
      <td class="unavailable" 
          [class]="shirts[selected.sku].sizes['XL'] ? '' : 'unavailable'">
        <div option="XL">XL</div>
      </td>
    </tr>
  </table>
</amp-selector>

가변 셔츠 가격

이제 사용 가능한 크기를 올바르게 표시하므로 올바른 가격도 표시되도록 합니다.

AMPPAREL 매장은 특이하게도 색상과 사이즈에 따라 셔츠 가격이 달라집니다. 따라서 사용자가 선택한 사이즈를 추적하기 위해서는 새 변수가 필요합니다. 크기 <amp-selector> 요소에 새 작업을 추가합니다.

<!-- When an element is selected, set the `selectedSize` variable to the
     value of the "option" attribute of the selected element.  -->
<amp-selector name="size" 
    on="select:AMP.setState({selectedSize: event.targetOption})">

amp-state#selected 요소를 통해 selectedSize의 값을 초기화하지는 않습니다. 의도적으로 기본 선택된 사이즈를 제공하지 않고 대신 사용자가 사이즈를 선택하도록 하기 때문입니다.

가격 라벨을 래핑하는 새 <span> 요소를 추가하고 기본 텍스트를 선택하지 않도록 기본 텍스트를 변경합니다.

<h6>PRICE :
  <!-- Display the price of the selected shirt in the selected size if available.
       Otherwise, display the placeholder text '---'. -->
  <span [text]="shirts[selected.sku].sizes[selectedSize] || '---'">---</span>
</h6>

이제 올바른 가격이 표시됩니다. 직접 사용해 보세요.

조건부 사용 설정 버튼

이제 거의 다 끝나갑니다. 이제 선택한 크기를 사용할 수 없을 때 '장바구니에 추가' 버튼을 사용 중지합니다.

<!-- Disable the "ADD TO CART" button when:
     1. There is no selected size, OR
     2. The available sizes for the selected SKU haven't been fetched yet
-->
<input type="submit" value="ADD TO CART" disabled
    class="mdl-button mdl-button--raised mdl-button--accent"
    [disabled]="!selectedSize || !shirts[selected.sku].sizes[selectedSize]">

Google은 대화형 JSON 엔드포인트에서 주문형으로 가져오는 각 SKU의 크기와 가격이 다양한 양방향 전자상거래 제품 세부정보 페이지를 제공합니다.

문제가 발생하면 static/final.html의 전체 솔루션을 확인하세요.

이 Codelab을 통해 <amp-bind>로 대화형 AMP 페이지를 빌드할 수 있는 강력한 기능과 유연성을 갖추기를 바랍니다. 자세한 내용은 <amp-bind> 문서를 참고하세요.

의견을 보내주시기 바랍니다. 기능 요청, 제안 또는 버그 신고를 보내주세요. 실제 사용자를 대상으로 <amp-bind>를 테스트해 보려면 원본 체험판을 신청해 보세요.

<amp-bind>를 출시하고 이 빌드로 무엇을 빌드할 수 있는지 기대하게 되었습니다.