319
255

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Nuxt + Firebaseで読書感想文を書けるWebアプリを開発しました

Last updated at Posted at 2019-10-14

こんなサービスを作りました

『みんなの感想文』というWebアプリをリリースしました!

この記事について

今月から個人でサービスを開発・運営をしている人たちの組織「運営者ギルド」のOrganizationの一員として所属することになりました。

所属してから初めてのサービスローンチなので、忘れないうちに作った開発技術などを備忘録として残しておきます。合わせて個人開発の運用にかかっている費用感なども公開します。個人でサービスを開発をしてみようと考えている人にとって役立つような情報を書ければいいなと思っています。

どんなサービスか?

一言で説明すると縦書きの感想文がWebブラウザで作成できるサービスです。400文字 or 200文字の原稿用紙のどちらかで、感想文を書くことができ、入力した文字がリアルタイムで反映されていきます。読書感想文の場合、読んだ本の情報をAmazonAPIから取得して、出版社・著者・本の金額・Amazonへの商品リンクなどを追加することができます。

スクリーンショット 2019-10-14 0.12.10.png

使っている技術

  • Nuxt.js
    • SPAモード
  • Firebase
    • Hosting
    • Firestore
    • Authentication
    • Storage
    • FirebaseFunctions
  • Product Advertising API(Amazonの商品取得)
  • Github

このサービスはSNS等でシェアしたときにOGimageで読書感想文の一部を表示できるような仕組みを取り入れたかったので、開発当初はNuxt + Herokuで環境を構築しSSRをしてmeta情報を書き換えるという実装をしていました。開発している途中にOGimageを表示するために、わざわざSSRをする必要はあるのか?Herokuのリージョンは日本にないのでキャッシュ等を上手くつかわないとレイテンシが半端ない?という懸念点から、ホスティングはFirebaseHostingを使って、全てFirebaseのプラットフォームを使うように設計を変更しました。

※ Regionはasia-northeast1(東京)を使用
※ FirebaseFunctionsのみus-central1(米国)を使用(カスタムドメインの設定のため)

システム構成

システム構成は以下のような感じになっています。
システム構成.jpg

運用コスト

  • ドメイン - kansobun.jp - 2,340円/年 (お名前.com)
  • Firebase - Blazeプラン(従量課金のプラン) - 5円/月
スクリーンショット 2019-10-14 8.38.36.png

数ヶ月運用してみないとわかりませんが、今のとこ流入もそんなに多くないのでコストもほとんどかかっていない感じです。

開発で苦戦したところ

最初にus-central1(米国アイオワ州)リージョンを選択してしまった

  • レイテンシがすごい(光遅い問題)
  • リージョンは一度設定してしまうと後から変更できない
  • us-central1から**asia-northeast1(東京)**に移行するのは結構大変(ドメインの付け替え、Firestoreのインデックスの作り直し等)
  • 教訓:日本からのアクセスが多いサービスの場合asia-northeast1からのレンポンスが絶対的に早いためasia-northeast1を選択したほうが良い(と思います)!

FunctionsからProduct Advertising API(Amazon)が叩けなかった

  • 教訓:Blazeプラン(従量課金)でないと外部のドメインとの接続が許可されていない

Functionsのカスタムドメイン設定のリージョンはus-central1のみ

  • エンドポイントのURLをカスタムドメインにしたいですよね。デフォルトだとhttps://us-central1-example.cloudfunctions.net/kansobun ですがhttps://example.com/api/kansobun でAPIを呼びたい。そしてasia-northeast1リージョンを使いたい。しかし、それはできないのです...。
  • 教訓:Functionsはasia-northeast1で使用することも可能。ただしカスタムドメインで設定する場合はus-central1のみ
スクリーンショット 2019-10-14 10.35.03.png

Product Advertising APIの審査がかなり厳しい

本の情報を取得するためにAmazonが公開しているProduct Advertising APIを使う必要がありました。このAPIの利用には審査があり、その審査の基準がブラックボックス化されているため、なぜ審査に落ちたのかがわかりませんでした(私の場合14回落ちて、15回目で通った)。以下を修正するとことで審査に合格することができたので、振り返り返ってみました(自分調べなので、他の要因もあると思うので、ご参考までに)。

  • 独自ドメインのサイトでないと審査が通らない
  • サイトのコンテンツが5〜10つ必須
  • metaタグの情報が必須
  • 教訓:開発中のサイトでの審査は通らないので、ほぼ完成した段階で申請しないと無駄な時間を費やしてしまう

工夫した部分

SPAでOGimageを動的に変える

シングルページアプリケーションでOGimageを表示を動的に変更させるために少しだけ工夫をしました。
※ 以下の記事を参考にさせていただきました。
Nuxt.js + FirebaseでOGPの仕組みを完全に理解した 〜俳句をSVGで描画するサービスをリリースした話〜 - @mitsudaman

このOGimageを表示するために以下のFunctionsを実行しています。

share.js
const functions = require('firebase-functions');

module.exports = functions.https
  .onRequest((req, res) => {
    let SITEURL = 'og:url';
    let TITLE = 'og:title';
    let DESCRIPTION = 'og:description';
    let IMAGE = `https://kansobun.jp/main_ogp.jpg`;

    let itemParams = '';
    let docUserItems = fireStore
      .collection('kansobun')
      .doc('items')
      .collection(itemId)
      .get()
      .then(doc => {
        if (doc.exists) {
          itemParams = doc.data();

          TITLE = 'みんなの感想文 | ' + itemParams.title;
          SITEURL = 'https://kansobun.jp/post/?id=' + itemId;
          DESCRIPTION = itemParams.text;
          IMAGE = itemParams.image_url;
          res.set('Cache-Control', 'public, max-age=300, s-maxage=600');
          res.status(200).send(`<!doctype html>
          <script>window.location.href="${SITEURL}"</script> // ** ここがポイント!
          <head>
            <title>${TITLE}</title>
            <meta property="og:title" content="${TITLE}">
            <meta property="og:image" content="${IMAGE}">
            <meta property="og:description" content="${DESCRIPTION}">
            <meta property="og:url" content="${SITEURL}">
            <meta property="og:type" content="website">
            <meta property="og:site_name" content="${TITLE}">
            <meta property="fb:app_id" content="123456789012345" />
            <meta name="twitter:site" content="@kansobun20">
            <meta name="twitter:card" content="summary_large_image">
            <meta name="twitter:title" content="${TITLE}">
            <meta name="twitter:image" content="${IMAGE}">
            <meta name="twitter:description" content="${DESCRIPTION}">
            <meta name="twitter:url" content="${SITEURL}" />
            <link rel="canonical" href="${SITEURL}">
          </head>
          <body>
          </body>
        </html>`);
        } else {
          itemParams = 'NOTHING';
          res.status(404).send(itemParams + '' + itemId);
        }
      })
      .catch(error => {
        res.status(400).send(error + '' + itemId);
        console.error('Error getting document:', error);
      });
  });

詳細ページのURL /post/hogehugapiyo と、OGimageを表示させるシェア用のURL/share/hogehugapiyoを用意します。/share/hogehugapiyoはFunctionsでmata情報だけをレンダリングしたhtmlを返すAPIになっており、Twitterの場合はJavaScriptが実行されないためmeta情報だけがレンダリングされたページがクローリングされ動的OGimageが表示されます。人間の場合は/post/hogehugapiyoにリダイレクトされ、詳細ページの情報が表示されるような仕組みなっています。

システム構成シェア.jpg

所感

もうSPAでいいんじゃないか。

今回はSSRモードではなく、SPAモードを使用しました。色々なところで話題に上がる「SPAってSEO的にどうなの?」という問題がありますが、Googleの最新のクローラーのレンダリングエンジンは最新版Chromium相当の機能を持ち合わせており、JavaScriptも実行できると言われています。実際にGoogleBotがどのようにページを表示していつかをサーチコンソールで確認することができますが、SPAのアプリケーションでも問題なくコンテンツが表示されていることを確認しました。ただ世界にはGoogle以外にも多くのクローラーが巡回しているので「全部SPAだけでいいんじゃないか?」というわけいはいかなそうです。そういったクローラーに対してはサーバーサイドでレンダリングしたhtmlを返す必要があるのでSSRをする必要があります。(が、全てを網羅することは難しいので個人的にはSPAでいいんじゃないかという結論に達しています...)

スクリーンショット 2019-10-14 21.03.09.png

Googlebotのレンダリングが最新版Chrome相当になった! これは嬉しい!【SEO記事13本まとめ】
アップデートでレンダリングエンジンが改良 ES6などがサポート対象に

Firebaseはやっぱり最高!

やっぱりFirebaseはすごいですね。本当に便利です。FirebaseHostingを使えばSSLやDNSを一瞬で設定することができます。初めて使ったとき「こんな簡単にアプリケーションが動くのか!」と感動しました。

アプリケーションはVPSに乗せて、データストアだけFirebaseを使うなど部分的に使うこともできたりするので、色々な使い方を試してみてください。

まとめ

みんなの感想文は読書感想文だけではなく、日常のポエムや、映画や音楽の感想文など色々なジャンルの投稿をすることができます。ぜひ、ささいなことでもいいので400文字or200文字でみんなの感想文を投稿してみてください!

main_ogp.jpg

みんなの感想文 - kansobun.jp

319
255
6

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
319
255

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?