Nuxt.js+Herokuを使って布団の温度をLINE LIFFで見る #SleepTech

## はじめに

前回、[寝床内気象（布団内の温度・湿度）を測定する装置](https://qiita.com/tmisuo0423/items/8e3bba55c3242ba02cc7)を作り、データを[Ambient](https://ambidata.io/)というサービスを使って可視化していました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90087/2edff305-411c-0e38-827c-b440948b5ffa.png)

しかし、毎回 Ambient にログインしてグラフを見るのが面倒になってきたので、
以前に作成した[気温に応じておすすめのおふとんを教えてくれるBOT](https://qiita.com/tmisuo0423/items/a00223ebd603a911ddd9)で見れるようにしていきます。


## 作ったもの

リッチメニューの右側をタップすると、LINE LIFF 上にグラフが表示されます。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">これで手軽に布団の温度が確認できるようになった！ <a href="https://twitter.com/hashtag/protoout?src=hash&amp;ref_src=twsrc%5Etfw">#protoout</a> <a href="https://twitter.com/hashtag/sleeptech?src=hash&amp;ref_src=twsrc%5Etfw">#sleeptech</a> <a href="https://t.co/KBNQdb1dNd">pic.twitter.com/KBNQdb1dNd</a></p>&mdash; Takahiro Mitsuoka (@t_mitsuoka) <a href="https://twitter.com/t_mitsuoka/status/1199696314897858562?ref_src=twsrc%5Etfw">November 27, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

## アプリ構成

前の記事で温度を測定する部分を作成したので、今回は LINE で表示する部分を作っていきます。

![グループ 2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90087/8e447d86-4d22-4a34-8dee-916b0d18d7d7.png)

## システム構成

バックエンドは前回作成した AWS サーバーレスアーキテクチャをそのまま使用します。
フロントエンドは Nuxt.js で作成した静的ページを Heroku でホストしました。
グラフ表示部分には、[vue-chartjs](https://vue-chartjs.org/ja/)を使用しています。
![図2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90087/3b5da1bb-3cbe-f2f9-020d-4f707490dee8.png)


Heroku は初めて使ってみたのですが、作成した資源を Heroku 内の Git リポジトリに Push すると自動でデプロイしてくれるので、CICD パイプラインの作成が不要でとても楽でした。

## フロントエンドの作成

### Nuxt.js

Nuxt.js は、Vue.js でサーバーサイドレンダリングをしたい際によく用いられる印象ですが、

>本質的に汎用性があり、さまざまなターゲット（サーバー、サーバーレス、または`静的`）をサポートし、サーバーサイドのレンダリングは切り替えることができます。
[引用：https://ja.nuxtjs.org/guide/](https://ja.nuxtjs.org/guide/)

とのことで、実は静的ページを作成するのにも使えます。
一通りの資源を作成した後、`$ nuxt generate`で静的ページを生成します。

### 画面を作成する

公式ドキュメント([https://ja.nuxtjs.org/guide/installation](https://ja.nuxtjs.org/guide/installation))に従ってサンプルアプリを作成します。

#### ステップ1. Nuxtプロジェクトを作成する

以下のコマンドを実行します。

```
$ npx create-nuxt-app <project-name>
```

コマンドを実行するとプロジェクト名などを聞かれるので適当に答えます。
Axios は使用するので ON にしておきましょう。

```
$ npx create-nuxt-app liff-heroku

create-nuxt-app v2.12.0
✨  Generating Nuxt.js project in liff-heroku
? Project name liff-heroku
? Project description My impressive Nuxt.js project
? Author name T Mitsu
? Choose the package manager Npm
? Choose UI framework None
? Choose custom server framework None (Recommended)
? Choose Nuxt.js modules Axios
? Choose linting tools ESLint, Prettier
? Choose test framework None
? Choose rendering mode Universal (SSR)
? Choose development tools jsconfig.json (Recommended for VS Code)
～～～～～～～～～～～～～～～～(略)～～～～～～～～～～～～～～～～～～～
> liff-heroku@1.0.0 lint /mnt/c/Users/taka/liff-heroku
> eslint --ext .js,.vue --ignore-path .gitignore . "--fix"

🎉  Successfully created project liff-heroku
  To get started:
        cd liff-heroku
        npm run dev

  To build & start for production:
        cd liff-heroku
        npm run build
        npm run start
```

#### ステップ2. サンプルアプリを起動してみる

```
$ npm run dev
```

でサンプルアプリが立ち上がるので、[http://localhost:3000](http://localhost:3000)にクセス。
するとこんな画面になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90087/d0d61ba3-c542-dc58-47c9-4524d8dfe87e.png)

これで準備完了です。

#### ステップ3. グラフを表示する

今回は、[vue-chart.js](https://vue-chartjs.org/ja/guide/#%E5%88%9D%E3%82%81%E3%81%AB)を使用します。
これは[Chart.js](https://www.chartjs.org/)というグラフ描画ライブラリを Vue.js 用に使いやすくしたものなのですが、
比較的簡単に扱えるのでオススメです。

まずはインストール。

```
$ npm install vue-chartjs chart.js --save
```

できたら、`components`フォルダに`ScatterChart.vue`を作成し、以下の内容をコピー&ペーストします。

```html:components/ScatterChart.vue
<script>
import { Scatter, mixins } from 'vue-chartjs'
const { reactiveProp } = mixins

export default {
  extends: Scatter,
  mixins: [reactiveProp],
  props: ['options'],
  mounted() {
    // this.chartData is created in the mixin.
    // If you want to pass options please create a local options object
    this.renderChart(this.chartData, this.options)
  }
}
</script>
```

続いて、`pages/index.vue`の内容を以下のように変更します。

```html:pages/index.vue
<template>
  <div class="container">
    <scatter-chart :chart-data="datacollection"></scatter-chart>
  </div>
</template>

<script>
import ScatterChart from '~/components/ScatterChart.vue'

export default {
  components: {
    ScatterChart
  },
  data() {
    return {
      datacollection: null
    }
  },
  mounted() {
    this.datacollection = {
      datasets: [
        {
          label: 'sample',
          backgroundColor: '#f87979',
          data: [
            { x: 1, y: 1 },
            { x: 2, y: 2 },
            { x: 3, y: 3 }
          ]
        }
      ]
    }
  }
}
</script>

<style>
.container {
  margin: 0 auto;
  min-heighx: 100vh;
  display: flex;
  justify-contenx: center;
  align-items: center;
  text-align: center;
}
</style>
```

そうすると、このようなグラフが表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90087/20c8a80e-1edb-c628-93ac-9c10111861bb.png)

vue-chart.js を使うと、こんな簡単にグラフが作れてしまいます。

#### ステップ4. データを取得する

`pages/index.vue`の`mounted関数`内容を以下のように変更。

```javascript:pages/index.vue
async mounted() {
  // this.$axios.$getでGET通信できる
  const response = await this.$axios.$get(`/data`) 
  // ※1 今回は"/data"でデータが取れるようなバックエンドを作成しています
  // ※2 クロスオリジンでデータを取得するような場合には、サーバサイドで設定が必要です。

  this.datacollection = {
    datasets: [
      {
        label: 'sample',
        backgroundColor: '#f87979',
        data: response.data
      }
    ]
  }
}
```

そうすると、グラフに実際のデータが表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90087/85d459cf-e45d-44d0-0b86-7829338f81e5.png)

これで画面は完成です。

#### ステップ5. 静的ページを生成する

以下のコマンドを実行します。

```
$ npm run generate
```

すると、`dist`フォルダが生成され、中に HTML が出力されます。
これらを Heroku にデプロイしていきます。

## Herokuにデプロイする

Heroku 関連の操作は以下の記事を参考にさせていただきました。
ほかにも良記事をがたくさんあるので、参考に進めてみてください。

  - 【Windows10】Heroku の CLI ダウンロードからデプロイまで (https://note.com/on_bass/n/n0495484a2b2b)
  - Node.js での Heroku チュートリアル前編 (https://qiita.com/shti_f/items/b4b5d830672d908eff4e)
  - Node.js アプリを Heroku へデプロイ (https://qiita.com/daiki7nohe/items/035c39c1e538551b1f6c)

Heroku にデプロイすると、URL が発行されます。(https://〇〇〇.herokuapp.comみたいなやつ)
LIFF の登録に使用するので、コピーしておいてください。

## LINE LIFFを作成する

こちらも先人の良記事がたくさんあるので、作成方法などの以下をご覧ください。

  - LIFF アプリを開発する(公式) (https://developers.line.biz/ja/docs/liff/developing-liff-apps/)
  - はじめての LIFF #LINE_API (https://dev.classmethod.jp/etc/liff-tutorial/)

### リッチメニューからLIFFを起動する

リッチメニューの作成もいろんな記事で紹介されているので、そちらをご覧ください。

  - https://developers.line.biz/ja/docs/messaging-api/using-rich-menus/
  - https://qiita.com/ufoo68/items/94510bafa0a9f53f8099

LIFF との紐づけ方が少しわかりにくかったので説明します。
LIFF を作成すると、`liff://app/xxxxxxxxxxxx`のような LIFF URL が発行されるので、コピーしてください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90087/f713e421-7cb3-d3c8-74c8-d5c4db5d7092.png)

リッチメニュー作成画面(LINE Official Account Manager)のコンテンツ設定画面で、以下の通り入力します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90087/bd97237c-0eba-bdad-c88b-5b552ae55c78.png)

これで、リッチメニューをタップすると、LIFF が起動するようになります。

## できたもの

これでいつでも布団の温度データが見れるようになりました。
Ambient の方が機能的にはリッチですが、手軽に見る分には十分です。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">これで手軽に布団の温度が確認できるようになった！ <a href="https://twitter.com/hashtag/protoout?src=hash&amp;ref_src=twsrc%5Etfw">#protoout</a> <a href="https://twitter.com/hashtag/sleeptech?src=hash&amp;ref_src=twsrc%5Etfw">#sleeptech</a> <a href="https://t.co/KBNQdb1dNd">pic.twitter.com/KBNQdb1dNd</a></p>&mdash; Takahiro Mitsuoka (@t_mitsuoka) <a href="https://twitter.com/t_mitsuoka/status/1199696314897858562?ref_src=twsrc%5Etfw">November 27, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

## 振り返り

Nuxt.js は初めて触りましたが、このレベルのアプリだと、あまり困ることなく開発を進められました。
Vue.js を使ったことがある人であれば、一度使ってみても損はないと思います。

グラフの実装では、調査・考えることが多く、かなり面倒でした。

  - どのグラフ描画フレームワークを使う？
  - Vue.js との相性は？
  - 横軸を日付にするには？
  - 点を線でつなぐには？
  - 見やすい色は？
  - いつからいつまでのデータを表示する？
  - 前後のデータを見るためのボタン

データを可視化するだけなら、外部サービスを使うのがコスパの良い選択肢だなと思いました。

## 改善点

まずはグラフ。
上で挙げたポイントは時間の都合でほとんど実現できず、なんともわかりにくいグラフになってしまいました。
まずは、Ambient の見た目に近づけることを目標にします。

LIFF の機能を活かせていない点も心残り（そもそもちゃんと liff.init してない）。
LIFF を使うと LINE ID が簡単に取れるので、グラフのデータは個人に紐づくものを取得するように改善したい。