웹 앱 빌드 (Dialogflow)

웹 앱은 Interactive Canvas를 사용하는 작업의 UI입니다. 이때 기존의 웹 기술 (HTML, CSS, JavaScript)을 이용하여 웹 앱을 만드는 것입니다. 대부분의 경우 Interactive Canvas는 다음과 같은 웹 콘텐츠를 렌더링할 수 있습니다. 일부 제한사항이 있습니다. 사용자 개인 정보 보호 및 보안 UI 디자인을 시작하기 전에 먼저 Design guidelines에 설명된 디자인 원칙 섹션으로 이동합니다.

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

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

이 페이지에서는 웹 앱을 빌드하는 데 권장되는 방법, 웹 앱을 일반적인 가이드라인과 제한되기 때문입니다.

어떤 방법이든 사용하여 UI를 빌드할 수 있지만 Google에서는 다음 방법을 사용하는 것이 좋습니다. 라이브러리:

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

아키텍처

단일 페이지 애플리케이션 아키텍처를 사용하는 것이 좋습니다. 이 접근 방식은 최적의 성능을 제공하고 지속적인 대화형 사용자 환경입니다. Interactive Canvas는 프런트엔드 프레임워크(예: Vue), Angular, React 상태 관리에 도움이 됩니다

HTML 파일

HTML 파일은 UI의 모양을 정의합니다. 이 파일은 커뮤니케이션을 지원하는 캔버스 JavaScript 라이브러리 대화식 Action을 연결하는 것입니다.

<!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 캔버스 라이브러리를 사용하여 웹 앱 파일에서 웹 앱과 처리가 상호작용하는 방식을 정의해야 합니다. 받는사람 웹 앱 로직이 포함된 파일을 수정해야 합니다.

action.js

이 파일에는 콜백 메서드를 호출합니다. (interactiveCanvas까지) 콜백을 통해 웹 앱은 대화 작업 에서의 정보 또는 요청을 확인할 수 있으며, 대화 작업 정보나 요청을 보내는 방법을 제공합니다.

HTML 파일에 interactiveCanvas.ready(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 디자인 가이드라인 액션을 '음성 중심'으로 개발해야 합니다. 그렇긴 하지만 일부 스마트한 디스플레이는 터치 상호작용을 지원합니다.

지원 터치는 대화 응답을 지원하는 것과 비슷합니다. 하지만 클라이언트 자바스크립트는 사용자의 음성 응답 대신 을 사용하고 이를 사용하여 웹 앱의 요소를 변경합니다.

이 예시는 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 콜백으로 사용합니다. fulfillment에 로직이 있음 spin의 값을 기반으로 인텐트를 트리거하는 내부 클래스입니다.

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

다른 기능 추가

이제 기본사항을 배웠으므로 작업을 개선하고 맞춤설정할 수 있습니다. 캔버스 전용 API를 사용합니다. 이 섹션에서는 이러한 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는 코드를 실행하는 onUpdate() 콜백을 트리거합니다. 아래 RESTART_GAME 코드 스니펫에서 찾을 수 있습니다.

//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() 콜백은 <mark> 태그를 SSML 응답을 사용자에게 제공합니다. 다음은 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 데이터베이스 없음
  • SpeechRecognition 인터페이스를 지원하지 않음: Web Speech API.
  • 오디오 또는 동영상 녹화 안 됨
  • 어두운 모드 설정을 적용할 수 없음
를 통해 개인정보처리방침을 정의할 수 있습니다.

교차 출처 리소스 공유

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