前日から始める！ LT発表を乗り切るデモアプリの作り方 【GraphQL】

#この記事を読むと得られること
・後先考えずにLTに申し込んだ時の乗り切り方

実際にLT発表をした時の話を体験談風にまとめながら書いていくので、長ったらしい文を読みたくないという人は最後のまとめだけ読んでいただければ大丈夫！

##事の始まり
夏も明け、新卒で入社した仕事も慣れてきた~~(気がする)~~という事で、外部での実績を積みたいと思いLT発表を考えていました。
connpassさん経由で初心者でも参加できそうなイベントを探していると、ちょうど初心者でも参加できそうなLT会をいくつか見つけたため、まずは応募してみようと申し込む事に。

そのいくつか申し込んだLT会のうち、弁護士ドットコムさん主催のタピオカLT( https://bengo4.connpass.com/event/143609/ )に参加させてもらった時の話を書き記しておこうと思います。

タピオカLTはざっくりWEBに関するテーマで喋って良いという事で、何を話すかは特に決めずにとりあえず申し込んでみました。
申し込んでからイベント開催までかなり時間があったので、何かしらネタが思いつくだろうと甘い考えを持っていたのですが、結局発表前日まで何も思い浮かばず。
流石にまずいという事で大慌てで前日発表資料の作成に取り掛かりました。

##テーマ決め
Web系の仕事についていれば仕事に関係する事で何か発表するなどの手があるのですが、普段の仕事では機械学習寄りの技術ばかり触っているため、Web系の技術はあまり触ったことがありませんでした。
ただ、個人の趣味でWebアプリを開発しており、React周りを色々触っていたので、最初はその事について喋ろうかなと思っていました。

候補としてはredux-starter-kitを使ったアプリ開発、個人開発の技術選定のお話などを考えましたが、描き始めてみるとどれもイマイチ。
どうしようかと悩んでいたところ、目に入ってきたのが今回のLT会の名前であるタピオカLTという単語でした。
どうせならタピオカに関係のある発表をしたいよな……と悩んだ末、

「黒っぽいバブルチャートを容器のイラストの中に表示したらタピオカに見えるんじゃないか？」

という発想の元発表テーマを決定。

普段機械学習やデータ分析の仕事をしている関係上、グラフやチャートなどを見慣れていた事、個人で作っているWebアプリが口コミを解析してグラフ化するものだったという事もあり、これしかないと覚悟を決めました。（この時点で発表前日お昼過ぎ）

ここまでGraphQLのGの字も出ていませんが、最終的に運営さんにお伝えした発表テーマは「タピオカで始めるGraphQL」
流石にバブルチャート作っただけで発表するのは厳しいという事で、しばらく前からそのうち触って見たいなと思っていたGraphQLを絡める事に。
~~まさにLT駆動開発ですね！~~

##デモアプリ作成
発表テーマと作るデモが決まったので、資料とデモの作成に着手し始めました。（この時点で18時くらい）
時間もないので必要最低限の機能を持たせたデモアプリを作成します。

要件として決めたのは、

・バブルチャートでタピオカっぽい物を表現する。
・APIのデモを行うため、簡単なデータベースを作り、RESTとGraphQLどちらでも呼び出せるようにする。

の二つだけです、時間もなかったので。

バブルチャートとして表示する情報は、今回はタピオカ販売店のホームページからテキストを収集、形態素解析を施し、単語と単語の出現回数をデータとして取得して利用しました。
単語はtf-idfを用いてあまり重要そうでない単語はデータから削除し、タピオカ店の特徴を表すデータだけに絞って表示するようにしました。

……が、結局ちゃんとデータを作ったものの、ちゃんとデータベース作ってフロントと繋ぎこむ時間がなかったため、上記データを元に手動でダミーデータを作り、スプリプトの内部に直接書き込むという強硬手段に移行。
今後使う予定もなく、5分間のLT発表のためだけと割り切ってしまうのも時と場合によってはありなのかなと思います。（言い訳）

そうしてできたAPI側がこちら

```rest.js
const express = require('express');
const app = express();

const port = process.env.PORT || 3030;

const ctSum =
    [[ { label: "表参道",        value: 3 ,color :"#663333"},
      { label: "クリーム",       value: 2, color :"#663333"},
      { label: "Tea",         value: 3,color :"#333300" },
      { label: "インスタ",       value: 2 ,color :"#333300"},
      { label: "持ち帰り",       value: 2 , color :"#333333"},
      { label: "スイカ",         value: 2 , color :"#333333"},
      { label: "チーズ",         value: 1 , color :"#330000"},
      { label: "カップ",         value: 2 , color :"#330000"},
      { label: "恋人",           value: 2  , color :"#000033"},
      { label: "ドリンク",       value: 2  , color :"#000033"},
      { label: "女子",           value: 3 , color :"#000000"},
      { label: "大人気",         value: 2 , color :"#000000"},
      { label: "感動",         value: 3 , color :"#440000"},
      { label: "テーブル",       value: 2 , color :"#440000"},
      { label: "映え",           value: 2 ,color :"#330000"},
      { label: "シェア",         value: 2 ,color :"#330000"},
      { label: "飲み物",         value: 3 ,color :"#220000" },
      { label: "職人",           value: 2 ,color :"#220000" },
      { label: "ミルク",         value: 2 ,color :"#663300"},
      { label: "自然",            value: 2,color :"#663300" },
       ]


];

app.listen(port, () => console.log(`Listening on port ${port}...`));
app.get('/api/tapiokaInfo', (req, res) => {
        res.send(ctSum);
});
```

REST側は、Node.jsとExpress.jsを使って必要最低限の機能だけ持たせたAPIを作成。
本当はエンドポイントを複数作ってエンドポイントを一つしか持たないGraphQLとの対比なんかもしたかったけど時間の問題で断念。
これをREST APIと言い張るのは心苦しいものがあるけれど、一応RESTの四つの設計原則は満たしているからいいか……と諦めました。

続いてGraphQL側です。
こちらは、フロント側との繋ぎこみはせず、GraphiQLを使って実際にデータを取ってくる部分のデモを行う用に作成しました。
ソースコードはこちら。

```graphQL.js
var express = require('express');
var express_graphql = require('express-graphql');
var { buildSchema } = require('graphql');
// GraphQL schema                                                               
var schema = buildSchema(`
                         type Query {
                             tapioka(name: String!):Tapioka
                             tapiokas(name: String!):[Tapioka]
                         },
                         type Tapioka{
                             name : String
                             label :String
                             value :Int
                             color :String
                         }
                         `
);

var tapiokaData = [
                   {
                       name:"LTタピオカ 渋谷店",
                       label: "表参道",
                       value: 3 ,
                       color :"#663333"

                   },
                   {
                       name:"LTタピオカ 渋谷店",
                       label: "クリーム",
                       value: 2,
                       color :"#663333"
                   },
                   {
                       name:"LTタピオカ 表参道店",
                       label: "Tea",
                       value: 3,
                       color :"#333300"
                   },
                   {
                       name:"LTタピオカ 表参道店",
                       label: "インスタ",
                       value: 2 ,
                       color :"#333300"
                   },
                   {
                       name:"LTタピオカ 表参道店",
                       label: "持ち帰り",
                       value: 2 ,
                       color :"#333333"
                   },
                   {
                       name:"LTタピオカ 表参道店",
                       label: "スイカ",
                       value: 2 ,
                       color :"#333333"
                   },
                   {
                       name:"LTタピオカ 表参道店",
                       label: "恋人",
                       value: 2  ,
                       color :"#000033"
                   },
                   {
                       name:"LTタピオカ 表参道店",
                       label: "チーズ",
                       value: 1 ,
                       color :"#330000"
                   },
                   {
                       name:"LTタピオカ 表参道店",
                       label: "カップ",
                       value: 2 ,
                       color :"#330000"
                   },
]
var getTapioka = function(args) {
    var name = args.name;
    return tapiokaData.filter(tapioka => {
            return tapioka.name == name;
        })[0];
}

var getTapiokas = function(args) {
    var name = args.name;
    return tapiokaData.filter(tapioka => {
            return tapioka.name == name;});
}

// Root resolver                                                                
var root = {
    tapioka:getTapioka,
    tapiokas: getTapiokas
};


// Create an express server and a GraphQL endpoint                              
var app = express();
app.use('/graphql', express_graphql({
            schema: schema,
                rootValue: root,
                graphiql: true
                }));
app.listen(4000, () => console.log('Express GraphQL Server Now Running On local\
host:4000/graphql'));

```

こちらも最低限の機能だけ持たせて実装をしました。
5分というLTの発表時間を考えると、これくらいの機能でも十分すぎるほどです。（実際、発表時は5分超えてしまいました。反省）
さて、このGraphQLにはGraphiQLというブラウザ上で実行できるクエリエディタのようなものがあります。
実際の発表では、このエディタを使って、APIを叩いてデータを取ってくる様子を実演しました。
<img width="919" alt="スクリーンショット 2019-10-14 16.51.45.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/392517/c57333b8-19ec-1342-3b40-c975e3989cab.png">

こんな感じ

一目でどんなクエリを投げていて、どんな結果が返ってきているのかがわかるので、デモをする際にも非常に役に立ちました。
GraphQLを知っている人からすると当たり前のことしかしていない気もしますが、初心者向けにはちょうど良いのかなと。

続いてはフロントエンド。
構成はreact,redux,nodeを使ってサクッと作ります。
肝心要のタピオカ部分はD3.jsを使って作ろうと思ったものの、調べたやり方がバージョン違いなどで上手く動作せず断念。
代わりに[react-bubble-chart-d3](https://github.com/weknowinc/react-bubble-chart-d3)を使って実装しました。

ソースコードはこちら

```tapioka_bubble.jsx
import React, {Component} from 'react';
import styles from './css/box.css'
import { connect } from 'react-redux';
import BubbleChart from '@weknow/react-bubble-chart-d3';
const Content = ({data,onIncrementClick}) =>{
      return(
        <div className={styles.box}>
             <p>店名 : LTタピオカ 表参道店</p>
　　　　<BubbleChart
          graph= {{
               zoom: 0.9,
                offsetX: 0.12,
             offsetY: 0.9,
        }}
          width={320}
          height={850}
          fontFamily="Arial"
          showLegend={false}
          data={data}
  />
        </div>
      );
};
export default connect()(Content);


```
全部書くとかなりの量になってしまうため、タピオカ作成部分だけ掲載します。
実際に実装した画面はこんな感じ。


<img width="496" alt="スクリーンショット 2019-10-06 20.52.12.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/392517/f79543ad-0150-967d-4ea4-2dd86fbcec98.png">


なんか左に寄っている上にちょっと気持ち悪い・・・
ただまぁなんとかタピオカに見えなくも、ない、ような・・・？
時間もないのでもうこれでいいやと思って持って行きました。

##実際の発表
当日、まさかのトップバッターかつ仕事が長引いたこともあって遅刻寸前での参加とかなりバタバタしましたが、なんとか発表しきることができました。
デモで見せる内容がちゃんと整理できていなかったため5分を少しオーバーしてしまったのが反省点ですが、なんとか乗り切れたと思います。~~（思っているだけ）~~
LTの発表経験はまだまだ浅く、デモアプリを用意したのは今回初めてでしたが、実際にやってみると意外となんとかなるものだなぁと思うので、思い切って参加してみることが一番大切なのかもしれませんね。

#まとめ
LTでデモを用意する際に大事なこと！
・目で見て印象に残るものを作る
・細かい機能の実装は気にしない
・低クオリティでもとりあえず見せられるものを作り上げる
・五分は思っているより短いので機能を詰め込みすぎない
~~・無理だと思ったら潔く諦める~~

破線にしましたが、諦めるということは結構大事かなと思っていて、今回の発表でも当初実装する予定だった機能の半分以上は時間の都合で諦めていて、それでもなんとかなったので、できる範囲で完成させることの方が大事なのかなと思います。

#最後に
今後もLT発表や登壇の経験を積んで行きたいと思っているため、そういった機会があるよー！というお方がいらっしゃれば、@mattya_monaca までDMかリプをくだされば喜んで参加させていただきます！
機械学習系やWeb周辺を中心にネタを蓄えていますので、よろしくお願いします！