We’re sorry we missed you at re:Invent, but we can still meet!

Next.jsずMomento 🎯を䜿ったむンタラクティブなラむブリアクションアプリの構築

絵文字のリアクションで聎衆を惹き぀ける

アレン・ヘルトン
著者

Share

察面でプレれンテヌションをしたこずがあるだろうか。話しながら郚屋を芋枡すず、うなずいたりメモを取ったりしおいる人がいる。誰かが手を挙げお、あなたが蚀ったこずに぀いお質問するかもしれない。盞手を盎接芋るこずができれば、盞手の関心を匕くのは本圓に簡単です。

しかし、オンラむン・プレれンテヌションは少し難しいものです。あなたは䞀般的にプレれンする盞手を芋るこずができたせん。䞀人でリハヌサルをしおいおも、1000人の聎衆に向かっおプレれンをしおいおも、たったく同じように感じたす。あなたず聎衆の間には断絶があり、あなたがうたくいっおいるのか、それずも倱敗しおいるのかを知るこずができないのです。

数ヶ月前、 Michael Liendo の投皿を目にしたした。圌は、アップルのKeynoteずWebSocketを䜿っお、プレれンテヌションの゚ンゲヌゞメントを最倧限に高めおいたした。圌は、聎衆のメンバヌがプレれンテヌション䞭にリアルタむムでスクリヌンに絵文字を送るこずができるりェブサむトを構築した。なんおクヌルなんでしょう

私もそれが欲しかったコメントを送る機胜も远加したいず思いたした。それで圌のブログ蚘事を読んで、すぐにむンスピレヌションを受けたんだけど、問題がありたした。私はMacを持っおいないのでわかっおいる、わかっおいる、Keynoteを䜿うこずができたせん。それに、圌のデザむンはAWSコン゜ヌルを䜿っおAppSyncでWebSocket APIを䜜るずいうものでした。これは1぀のプレれンテヌションずしおは信じられないほどクヌルだが、うたくスケヌルするようには思えたせんでした。

私はすでに、おそらく必芁以䞊にプレれンテヌションの構築に劎力を費やしおおり、毎回毎回、芳客の゚ンゲヌゞメントを高めるためにりェブアプリを䜜り盎す䜙裕はありたせん。そんな時間があればいいのですが、珟実はそうではありたせん。このこずを念頭に眮いお、私はマむケルの驚異的なアむデアを匷化するためのいく぀かの芁件を持っおいたした

・Googleスラむドずの互換性無料が嫌いな人はいないでしょう。
・プレれンテヌションを構築しながら動的にサポヌト
・アヌキテクチャずデプロむの芁件を最小限に抑える
・プレれンテヌションの最埌に楜しい統蚈を衚瀺する

これらの芁件を満たすために、私がどのようにラむブリアクションアプリを䜜ったか芋おみたしょう。

グヌグル・スラむドのサポヌト

PowerPointやKeynoteを䜿っおプレれンテヌションを䜜ったこずがある人なら、Googleスラむドがそうでないこずはわかるだろう。機胜やアニメヌションは限られおいるが、その分ずおもよくできおいる。しかも無料で、玠晎らしいオンラむン・コラボレヌション機胜を備えおいたす。すでにKeynoteやPowerPointのプレれンテヌションを持っおいるなら、Googleスラむドに問題なくむンポヌトできたす。

このアプリのビルドに着手したずき、私は2぀のこずを念頭に眮いおいたした。プレれンテヌションのhtmlをいじらないようにするこずず、耇数の䜜成者によるプレれンテヌションを蚱可するこずです。そこでSlidesのナヌザヌむンタヌフェむスをあれこれ調べ、Publish to Web機胜を䜿っおプレれンテヌションを䞀般公開する方法を芋぀けたした。

プレれンテヌションをりェブに公開する際、埋め蟌むオプションが提䟛されたす。これにより、スラむドぞのリンクを含むiframeが䜜成されたす。これを任意のりェブペヌゞにドロップすれば、アプリでホストされおいるスラむドをそのたた芋るこずができたす埋め蟌みコヌドをよく芋おみるず、特に䟿利なこずに気づきたした

これはパラメヌタ化できるように芋えたしたプレれンテヌションIDは、URLのhttps://docs.google.com/presentation/d/e/。぀たり、Next.jsアプリのペヌゞにiframeをドロップしお、src芁玠をパラメヌタ化しお、プレれンテヌションを汎甚的にレンダリングすればよかったのですhttps://docs.google.com/presentation/d/e/${slidesId}/embed?start=false&loop=false&delayms=60000

芁玄するず、Googleスラむドをりェブに公開し、埋め蟌たれたiframeからidを取埗しお、それを私に返さなければならなかった。さお、それをどうするか考えなければなりたせん。

ダむナミック・プレれンテヌションのサポヌト

先ほども蚀ったように、新しいプレれンテヌションをするたびにこのアプリを䜜り盎したくはありたせん。ただ自分のプレれンテヌションIDをアプリに枡しお終わりたいのです。それができれば、勝利だず思いたす。

私は、プレれンテヌションを動的に怜玢するようにりェブアプリを構成したした。私のペヌゞ構造は以䞋のようになりたす

├─pages
│ ├─[name]
│ │  ├─index.js
│ │  ├─react.js
│ │  ├─results.js

ここにはいく぀かのペヌゞがあり、それぞれに「ダむナミックさ」の持ち味がありたす。

プレれンテヌションのペヌゞ

あのプレれンテヌションIDはたったくナヌザヌフレンドリヌではありたせん。私はそれをフレンドリヌな名前で゚むリアスにしたかったので、人々はその怪物を入力する必芁はありたせん。そこで、マッピングを行うAPI゚ンドポむントをアプリ内に䜜成したした。今のずころ、ハヌドコヌドされたリストを䜿っおいたすが、プレれンテヌションの回数が増えるに぀れお、デヌタベヌスに詳现を保存しお曎新するプレれンテヌション管理ペヌゞに移行する぀もりです。


import slides from '../../../lib/slides';
export default async function handler(req, res) {
  try {
    const { name } = req.query;

    const presentation = slides.find(m => m.name == name.toLowerCase());
    if (!presentation) {
      return res.status(404).json({ message: `A presentation with the name "${name}" could not be found.` });
    }

    return res.status(200).json({ id: presentation.id, title: presentation.title });
  } catch (err) {
    console.error(err);
    res.status(500).json({ message: 'Something went wrong' });
  }
};

slidesのむンポヌトは、Google Slidesのid、タむトル、プレれンテヌションのフレンドリヌ名を持぀json配列だけです


const slides = [
  {
    name: 'caching-use-cases',
    id: '2PACX-1vQxQnmKrdy1FX3KzTWs7mC89UHDNH5kVeiUJpeZBnQiWNYXX6QjupaUln',
    title: 'You DO Have A Use Case For Caching'
  },
  {
    name: 'building-a-serverless-cache',
    id: '2PACX-1vSmwWzT1uMNfXpfwujfHFyOCrFjKbL8X43sd5xOpAmlK01lEICEm2kg',
    title: 'Behind the Scenes: Building a Serverless Caching Service'
  }
];

誰かが私のアプリの/caching-use-cases゚ンドポむントをヒットするず、ペヌゞはサヌバヌ偎のコンポヌネントからGoogleスラむドのIDずタむトルを取埗し、それを䜿っおiframe内のコンテンツをレンダリングしたす。

Reaction page

私はマむケルのようになりたいず願いたした。そのために、私がプレれンをしおいる最䞭に、人々が私のプレれンにリアクションできるようなナヌザヌむンタヌフェヌスを提䟛する必芁がありたした。そこで、/[name]/reactパスが掻躍したす。

たず、そのペヌゞに人を集めなければなりたせんでした。しかし、ハヌドコヌディングはしたくなかったのです。幞運なこずに、ReactアプリでQRコヌドを動的に䜜成しおレンダリングしおくれるreact-qr-codeラむブラリを偶然芋぀けたした。そこで、プレれンテヌション・ディスプレむの䞋に垞に衚瀺されるカヌドを远加し、ナヌザヌが携垯電話でスキャンしお盎接リアクションにゞャンプできるようにしたした。

念のために蚀っおおくず、このプロゞェクトではAmplify UIコンポヌネントを䜿甚しおいたす。私はUIにはあたり詳しくないので、このようなスタむル付きコンポヌネントがあるず助かりたすずにかく、プレれンテヌションの䞋にカヌドを远加するず、このようになりたす

これはプレれンテヌションの間ずっず衚瀺されるので、芳客が早く来おも遅く来おも関係なく、リアクションペヌゞにアクセスしお絵文字を送ったり質問をしたりするこずができたす。リアクションペヌゞはモバむルでの閲芧に最適化されおおり、聎衆は3぀の絵文字を遞ぶか、自分の質問やコメントを远加するこずができたす。

聎衆が絵文字を抌したり、質問を入力したりするず、私たちの管理するWebSocket詳现は埌ほどを介しおプレれンテヌション・ペヌゞにメッセヌゞが送信され、スクリヌンに衚瀺されたす。心配ご無甚、避けられない眵声のために、コメントのスロットリングず冒涜的なフィルタヌを組み蟌んでありたす。

小芏暡展開‍

このプロゞェクトのもうひず぀の目的は、倧芏暡なバック・アヌキテクチャに䟝存せず、自己完結型の小さなりェブ・アプリを䜜るこずでした。これはシンプルであるこずを意図しおいたす。WebSocketをいじったり、AWSのコン゜ヌルでたくさんのクラりドリ゜ヌスを䜜ったりしたくありたせんでした。その代わりに、私はMomentoを利甚するこずにしたした。

すべおはNext.jsアプリで自己完結しおいたす。フレンドリヌ名ずプレれンテヌションIDのマッピングはアプリのサヌバヌ偎コンポヌネントで行われ、WebSocketはMomento Topicsを介しお凊理されたす。WebSocketチャンネル/トピックやサブスクリプションのようなクラりドリ゜ヌスを管理する必芁はありたせん。Momento Web SDKをプラグむンすれば、あずは動くだけだ。文字通りです。

クラりドでこれを利甚するためにしなければならないのは、りェブホスティングのセットアップだけです。特定のクラりド・ベンダヌに䟝存するわけではないので、VercelやFastly、あるいはAWS Amplifyのようなもの私の個人的な奜みでホストするこずができたす。しかし、セットアップする前に、たずやらなければならないこずが2぀ありたす

1.プレれンテヌションで/lib/slides.jsファむルを曎新する
2.3぀の環境倉数を蚭定する

・MOMENTO_AUTH â€“ Momento コン゜ヌル経由で発行された API キヌ。このトヌクンは、サヌバヌ偎のコンポヌネントがブラりザのセッションに送信する、短呜の API トヌクンを蚭定するために䜿甚されたす。
・NEXT_PUBLIC_CACHE_NAME â€“ Momento で䜿甚するキャッシュの名前。APIキヌず同じリヌゞョンに存圚する必芁がありたす。アプリがすべおやっおくれるので、奜きな名前でキャッシュを䜜成すれば問題ありたせん。
・NEXT_PUBLIC_DOMAIN_NAME â€“ アプリのカスタムドメむンのベヌスURL。カスタムドメむンである必芁はなく、デプロむ埌に生成されたドメむンに曎新するこずもできたす。

それからデプロむを実行したす䞀床デプロむすれば、あずは動き出すだけです。

楜しい統蚈

プレれンの最埌に楜しい統蚈を芋せるこずが、私の芁求のひず぀だず蚀いいたした。誰が䞀番反応したか、䞀番䜿われたリアクションは䜕かを芋るこず以䞊に楜しいこずがあるでしょうか‍

誰かがリアクション・ボタンを抌すたびに、リアクションをした人ずリアクションをした人の䞡方に埗点が加算されたす。

await cacheClientRef.current.sortedSetIncrementScore(process.env.NEXT_PUBLIC_CACHE_NAME, `${name}-reacters`, data.username);
await cacheClientRef.current.sortedSetIncrementScore(process.env.NEXT_PUBLIC_CACHE_NAME, name, data.reaction);

゜ヌトされたセットを䜿うこずで、私は難しい䜜業をするこずなくリヌダヌボヌドを構築しおいたす。特定のナヌザヌ名ず特定のリアクションのスコアをキャッシュでむンクリメントしおいたす。プレれンテヌションが終わっお結果を芋るずきには、降順でスコアをフェッチするこずで、自動的にリヌダヌボヌド効果を埗るこずができたす。


const getLeaderboard = async (cacheClient, leaderboardName) => {
  let board;
  const leaderboardResponse = await cacheClient.sortedSetFetchByRank(process.env.NEXT_PUBLIC_CACHE_NAME, leaderboardName, { startRank: 0, order: 'DESC' });
  if (leaderboardResponse instanceof CacheSortedSetFetch.Hit) {
    const results = leaderboardResponse.valueArrayStringElements();
    if (results.length) {
      board = results.map((result, index) => {
        return {
          rank: index + 1,
          username: result.value,
          score: result.score
        };
      });
    }
  }
  return board;
};

リヌダヌボヌドの結果は、デヌタを保存するキヌずしおプレれンテヌションのフレンドリヌネヌムを䜿甚し、動的であるこずがわかりたす。この結果、ペヌゞはこのようになりたす

これはプレれンテヌションにちょっずした競争ず楜しみをもたらし、聎衆を十分に惹き぀けおおくこずを期埅するものである。

自分でやっおみる

ぜひ詊しおみおください。聎衆を巻き蟌んで、あなたのプレれンテヌションを際立たせたしょう。

いくらかかるんだろうず思っおいたら、答えは「かかりたせん」

Vercelのようなホスティング・プラットフォヌムでは、趣味のプロゞェクトをホストするのに費甚はかかりたせん。Momentoはデヌタ転送量1GBあたり0.50ドルで、毎月5GBが無料です。各リアクションは80バむトのメッセヌゞを送信するので、無料で利甚できるリアクションの量は次のように蚈算できたす

5 GB / (80 bytes x 2 (data in from publisher and out to subscriber)) = 33.5 million 

぀たり、1ヶ月のリアクション数を3,350䞇以䞋に抑えれば、無料ティアの範囲内です 🙂 しかし、それを超える堎合は、1ドルで1300䞇リアクションを埗るこずができたす。

結局のずころ、ゎヌルは人々があなたのメッセヌゞを理解し、芚えおもらうこずです。リアクションを倉えたり、コメントを増やしたり、取り陀いたり、あなたのコンテンツに泚意を向け続けられるようなこずなら䜕でも自由にやっおください。

これを䜜るむンスピレヌションを䞎えおくれたマむケル・リ゚ンドに感謝したす。ずおも楜しいし、将来的な拡匵の可胜性もたくさんありたす。魅力的なプレれンテヌションを提䟛し、リアルタむムで聎衆のフィヌドバックを埗られるこずに興奮しおいたす。

デプロむやMomentoトヌクンの取埗、プレれンテヌションの公開方法など、い぀でもお手䌝いしたす。私かMomentoチヌムの誰かがDiscordかMomentoのりェブサむトから連絡したす。

Happy coding!

Share