웹 앱 빌드 (Dialogflow)

웹 앱은 Interactive Canvas를 사용하는 작업의 UI입니다. 기존 웹 기술 (HTML, CSS, 자바스크립트)을 사용하여 웹 앱을 디자인하고 개발할 수 있습니다. 대부분의 경우 Interactive Canvas는 브라우저와 같은 웹 콘텐츠를 렌더링할 수 있지만 사용자 개인 정보 보호 및 보안을 위해 몇 가지 제한사항이 적용됩니다. UI 디자인을 시작하기 전에 Design guidelines 섹션에 설명된 디자인 원칙을 고려하세요.

웹 앱의 HTML 및 JavaScript는 다음을 수행합니다.

  • Interactive Canvas 이벤트 callbacks을 등록합니다.
  • Interactive Canvas JavaScript 라이브러리를 초기화합니다.
  • 상태에 따라 웹 앱을 업데이트하는 커스텀 로직을 제공합니다.

이 페이지에서는 웹 앱을 빌드하는 권장 방법, 웹 앱과 처리 간의 통신을 사용 설정하는 방법, 일반적인 가이드라인과 제한사항을 설명합니다.

모든 메서드를 사용하여 UI를 빌드할 수 있지만 다음 라이브러리를 사용하는 것이 좋습니다.

  • Greensock: 복잡한 애니메이션을 빌드하는 데 적합합니다.
  • Pixi.js: WebGL에서 2D 그래픽을 그리는 데 사용됩니다.
  • Three.js: WebGL에서 3D 그래픽을 그리는 데 사용됩니다.
  • HTML5 캔버스 그리기: 간단한 그림에 적합합니다.
  • DOM 요소: 정적 콘텐츠용입니다.

아키텍처

단일 페이지 애플리케이션 아키텍처를 사용하는 것이 좋습니다. 이 접근 방식을 사용하면 최적의 성능을 얻을 수 있으며 지속적인 대화 사용자 환경을 지원합니다. Interactive Canvas는 상태 관리에 도움이 되는 Vue, Angular, React 같은 프런트엔드 프레임워크와 함께 사용할 수 있습니다.

HTML 파일

HTML 파일은 UI의 모양을 정의합니다. 이 파일은 웹 앱과 대화형 작업 간의 커뮤니케이션을 지원하는 Interactive Canvas JavaScript 라이브러리도 로드합니다.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>Immersive Canvas Sample</title>
    <!-- Disable favicon requests -->
    <link rel="shortcut icon" type="image/x-icon" href="data:image/x-icon;,">
    <!-- Load Interactive Canvas JavaScript -->
    <script src="https://www.gstatic.com/assistant/df-asdk/interactivecanvas/api/interactive_canvas.min.js"></script>
    <!-- Load PixiJS for graphics rendering -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/pixi.js/4.8.7/pixi.min.js"></script>
    <!-- Load Stats.js for fps monitoring -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/stats.js/r16/Stats.min.js"></script>
    <!-- Load custom CSS -->
    <link rel="stylesheet" href="css/main.css">
  </head>
  <body>
    <div id="view" class="view">
      <div class="debug">
        <div class="stats"></div>
        <div class="logs"></div>
      </div>
    </div>
    <!-- Load custom JavaScript after elements are on page -->
    <script src="js/main.js"></script>
    <script src="js/log.js"></script>
  </body>
</html>

처리와 웹 앱 간 통신

웹 앱과 처리를 빌드하고 웹 앱 파일의 Interactive Canvas 라이브러리에 로드했으므로 이제 웹 앱과 처리가 상호작용하는 방식을 정의해야 합니다. 이렇게 하려면 웹 앱 로직이 포함된 파일을 수정합니다.

action.js

이 파일에는 callbacks을 정의하고 interactiveCanvas를 통해 메서드를 호출하는 코드가 포함되어 있습니다. 콜백을 사용하면 웹 앱이 대화 작업에서 정보나 요청에 응답할 수 있고, 메서드는 대화 작업으로 정보나 요청을 보내는 방법을 제공합니다.

HTML 파일에 interactiveCanvas.ready(callbacks);를 추가하여 callbacks을 초기화하고 등록합니다.

//action.js
class Action {
  constructor(scene) {
    this.canvas = window.interactiveCanvas;
    this.scene = scene;
    const that = this;
    this.commands = {
      TINT: function(data) {
        that.scene.sprite.tint = data.tint;
      },
      SPIN: function(data) {
        that.scene.sprite.spin = data.spin;
      },
      RESTART_GAME: function(data) {
        that.scene.button.texture = that.scene.button.textureButton;
        that.scene.sprite.spin = true;
        that.scene.sprite.tint = 0x0000FF; // blue
        that.scene.sprite.rotation = 0;
      },
    };
  }

  /**
   * Register all callbacks used by Interactive Canvas
   * executed during scene creation time.
   *
   */
  setCallbacks() {
    const that = this;
    // declare interactive canvas callbacks
    const callbacks = {
      onUpdate(data) {
        try {
          that.commands[data.command.toUpperCase()](data);
        } catch (e) {
          // do nothing, when no command is sent or found
        }
      },
    };
    // called by the Interactive Canvas web app once web app has loaded to
    // register callbacks
    this.canvas.ready(callbacks);
  }
}

main.js

이 파일은 웹 앱의 장면을 구성합니다. 이 예에서는 sendTextQuery()로 반환된 프로미스의 성공 사례와 실패 사례도 처리합니다. 다음은 main.js에서 발췌한 것입니다.

// main.js
const view = document.getElementById('view');
// initialize rendering and set correct sizing
this.renderer = PIXI.autoDetectRenderer({
  transparent: true,
  antialias: true,
  resolution: this.radio,
  width: view.clientWidth,
  height: view.clientHeight,
});
view.appendChild(this.element);

// center stage and normalize scaling for all resolutions
this.stage = new PIXI.Container();
this.stage.position.set(view.clientWidth / 2, view.clientHeight / 2);
this.stage.scale.set(Math.max(this.renderer.width,
    this.renderer.height) / 1024);

// load a sprite from a svg file
this.sprite = PIXI.Sprite.from('triangle.svg');
this.sprite.anchor.set(0.5);
this.sprite.tint = 0x00FF00; // green
this.sprite.spin = true;
this.stage.addChild(this.sprite);

// toggle spin on touch events of the triangle
this.sprite.interactive = true;
this.sprite.buttonMode = true;
this.sprite.on('pointerdown', () => {
  this.sprite.spin = !this.sprite.spin;
});

터치 상호작용 지원

Interactive Canvas 작업은 사용자의 터치와 음성 입력에 응답할 수 있습니다. Interactive Canvas 디자인 가이드라인에 따라 작업을 '음성 우선'으로 개발해야 합니다. 하지만 일부 스마트 디스플레이는 터치 상호작용을 지원합니다.

터치 지원은 대화형 응답을 지원하는 것과 유사합니다. 하지만 클라이언트 측 JavaScript는 사용자의 음성 응답 대신 터치 상호작용을 찾고 이를 사용하여 웹 앱의 요소를 변경합니다.

Pixi.js 라이브러리를 사용하는 샘플에서 이에 관한 예를 확인할 수 있습니다.

...
this.sprite = PIXI.Sprite.from('triangle.svg');
...
this.sprite.interactive = true; // Enables interaction events
this.sprite.buttonMode = true; // Changes `cursor` property to `pointer` for PointerEvent
this.sprite.on('pointerdown', () => {
  this.sprite.spin = !this.sprite.spin;
});
...

이 경우 spin 변수의 값은 interactiveCanvas API를 통해 update 콜백으로 전송됩니다. 처리에는 spin 값에 따라 인텐트를 트리거하는 로직이 있습니다.

...
app.intent('pause', (conv) => {
  conv.ask(`Ok, I paused spinning. What else?`);
  conv.ask(new HtmlResponse({
    data: {
      spin: false,
    },
  }));
});
...

다른 기능 추가

기본사항을 배웠으므로 이제 Canvas 관련 API를 사용하여 작업을 개선하고 맞춤설정할 수 있습니다. 이 섹션에서는 Interactive Canvas 작업에서 이러한 API를 구현하는 방법을 설명합니다.

sendTextQuery()

sendTextQuery() 메서드는 대화형 작업에 텍스트 쿼리를 전송하여 프로그래매틱 방식으로 인텐트를 호출합니다. 이 샘플은 sendTextQuery()를 사용하여 사용자가 버튼을 클릭할 때 삼각형 회전 게임을 다시 시작합니다. 사용자가 '게임 다시 시작' 버튼을 클릭하면 sendTextQuery()에서 Restart game 인텐트를 호출하고 프로미스를 반환합니다. 이 프로미스로 인해 인텐트가 트리거되면 SUCCESS, 트리거되지 않으면 BLOCKED이 발생합니다. 다음 스니펫은 인텐트를 트리거하고 프로미스의 성공 및 실패 사례를 처리합니다.

//main.js
...
that.action.canvas.sendTextQuery('Restart game')
    .then((res) => {
      if (res.toUpperCase() === 'SUCCESS') {
        console.log(`Request in flight: ${res}`);
        that.button.texture = that.button.textureButtonDisabled;
        that.sprite.spin = false;
      } else {
        console.log(`Request in flight: ${res}`);
      }
    });
...

프로미스로 SUCCESS가 반환되면 Restart game 인텐트가 웹 앱에 HtmlResponse를 전송합니다.

//index.js
...
app.intent('restart game', (conv) => {
  conv.ask(new HtmlResponse({
    data: {
      command: 'RESTART_GAME',
    },
...

HtmlResponse는 아래 RESTART_GAME 코드 스니펫의 코드를 실행하는 onUpdate() 콜백을 트리거합니다.

//action.js
...
RESTART_GAME: function(data) {
  that.scene.button.texture = that.scene.button.textureButton;
  that.scene.sprite.spin = true;
  that.scene.sprite.tint = 0x0000FF; // blue
  that.scene.sprite.rotation = 0;
},
...

OnTtsMark()

OnTtsMark() 콜백은 사용자에 대한 SSML 응답에 고유한 이름이 있는 <mark> 태그를 포함할 때 호출됩니다. Snowman 샘플의 다음 발췌 부분에서 OnTtsMark()는 웹 앱의 애니메이션을 상응하는 TTS 출력과 동기화합니다. 작업이 사용자에게 죄송합니다. 분실하셨어요라고 말하면 웹 앱이 올바른 단어의 철자를 입력하고 사용자에게 문자를 표시합니다.

Game Over Reveal Word 인텐트는 게임에서 졌을 때 사용자에 대한 응답에 맞춤 마크를 포함합니다.

//index.js
...
app.intent('Game Over Reveal Word', (conv, {word}) => {
  conv.ask(`<speak>Sorry, you lost.<mark name="REVEAL_WORD"/> The word is ${word}.` +
    `${PLAY_AGAIN_INSTRUCTIONS}</speak>`);
  conv.ask(new HtmlResponse());
});
...

그런 다음, 다음 코드 스니펫은 OnTtsMark() 콜백을 등록하고, 표시 이름을 확인하고, revealCorrectWord() 함수를 실행하여 웹 앱을 업데이트합니다.

//action.js
...
setCallbacks() {
  const that = this;
  // declare assistant canvas action callbacks
  const callbacks = {
    onTtsMark(markName) {
      if (markName === 'REVEAL_WORD') {
        // display the correct word to the user
        that.revealCorrectWord();
      }
    },
...

제한사항

웹 앱을 개발할 때 다음 제한사항을 고려하세요.

  • 쿠키 없음
  • 로컬 저장소 없음
  • 위치정보 없음
  • 카메라 사용 안함
  • 팝업 없음
  • 메모리 한도 200MB 미만 유지
  • 서드 파티 헤더가 화면 상단을 차지합니다.
  • 동영상에 스타일을 적용할 수 없습니다.
  • 한 번에 하나의 미디어 요소만 사용할 수 있습니다.
  • HLS 동영상 없음
  • 웹 SQL 데이터베이스 없음
  • Web Speech APISpeechRecognition 인터페이스는 지원되지 않습니다.
  • 오디오 또는 동영상 녹화가 되지 않음
  • 어두운 모드 설정을 적용할 수 없음

교차 출처 리소스 공유

Interactive Canvas 웹 앱은 iframe에서 호스팅되고 출처가 null로 설정되므로 웹 서버 및 스토리지 리소스에 교차 출처 리소스 공유 (CORS)를 사용 설정해야 합니다. 이렇게 하면 애셋이 null 출처의 요청을 수락할 수 있습니다.