年末なのでコブラの名言をランダムに表示するクソアプリ作った

# 年末なのでコブラの名言をランダムに表示するクソアプリ作った

どうもこんにちは。
普段は会社員とかプログラムスクールのメンターとかやらせて頂いてますその辺のエンジニアです。  

2019年もあとわずかですが、みなさま年末は如何お過ごしでしょうか。
せっかくの年末なので、クソアプリ作ってみました。  

**本当はクソアプリアドベントカレンダーに乗っかってクリスマスに間に合わせたかったよ！！**  
納期駆動開発はつらい。

## その前に
皆さまあの有名な漫画、`コブラ`はご存知でしょうか？
宇宙海賊であるコブラが相棒であるレディーと一緒に宇宙を旅する少年漫画です。  

1978年から1984年にかけて連載していた漫画なので、お若い方はあまり知らないかもしれませんね。
色々突っ込むポイントがありすぎるところが逆に面白くて、僕はハマってしまいました。  

ネタバレしない程度にどんな話かというと、  
 - コブラがめちゃくちゃ強い
 - セリフがいちいちクサい
 - セリフがいちいちダサい
 - でもそこがかっこいい
 - すぐ美女と良い感じになるから羨ましい
 - 作者は絶対尻フェチ
 - なんかコブラが来ればもう安心する
 - っていうか結局コブラがなんとかしてくれる
 - 歩く名言(迷言)製造機
 - ヒューーッ！！

っていう感じのお話です。

※最近コブラにハマったにわかなのでご容赦を。。。  

そんな漫画 `コブラ`ですが数多くの名言が生み出されており、
Googleで[コブラ 名言]などで検索すると数多くの記事がヒットします。  

そんなわけで、どうにかコブラの名言を交えてクソアプリを作れないか、と検討し、
今回このアプリの作成に至った、という経緯です。  

あと、純粋にクソアプリアドベントカレンダー読み漁ってたら作りたくなったっていうのが一番大きいです。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">クソアプリアドベントカレンダー漁ってたらクソアプリ作りたくなってきた</p>&mdash; yoshi.@code-plum (@codeplumdev) <a href="https://twitter.com/codeplumdev/status/1208585368653549569?ref_src=twsrc%5Etfw">December 22, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

## 作ったもの
こちらです。
実際に動くのでよければ「ヒューッ！」していってください。

[アクセスの度にコブラの名セリフをランダムに表示するだけのサイト](https://randomcobrabucket.s3-ap-northeast-1.amazonaws.com/index.html)


**（うーんこのフロントセンスの無さよ…）**

機能として、ざっくりこんな感じです。
- TOPにアクセスするとDynamoDBをスキャンしてランダムに名言を表示
- ヒューッ！機能（いいね機能）
- ヒューッ！ランキング（いいね数ランキング）

## 構成図

~~こんなクソみたいなアプリにサーバを構築するなんて絶対に嫌なので、~~
フルサーバレスアーキテクチャを組みました。  


![Untitled Diagram.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535324/7fdc2088-5af1-ab4f-61bf-fbeaf4f279bd.jpeg)


簡単に環境構成図作れるdraw.ioくんすき  

## APIGateway + Lambda
ここは今まで結構使ったことがあった部分だったので、簡単なAPI程度なら楽勝やろ〜〜って感じでした。
ただ絶対にやりたくなかったのが、**API別にLambdaを別々に構築する**のはやりたくなかった。
普通にめんどくさかったし拡張性ないし。  

なんか良い方法ないかなぁと色々探していたところ、  
Expressをそのままサーバレスで動かすことができるライブラリを発見。神か・・・  
[https://github.com/awslabs/aws-serverless-express](https://github.com/awslabs/aws-serverless-express)

導入方法に関しては[こちら](https://utano.jp/entry/2019/05/aws-serverless-express/)の記事をめちゃめちゃ参考にさせて頂きました。本当にありがとうございます。  

読んでみると、なになに〜

 - 初期設定だけしてしまえばnpm scriptでビルドからデプロイまで可能
 - S3バケットを作成しそこにデプロイ
 - LambdaだけじゃなくてAPIGatewayの設定もしてくれる

**神か・・・**

やったことある人ならわかると思うんですけど、APIGatewayって設定結構めんどくさいんですよね。 
普通に使うだけだとクエリパラメータとかBodyとか一個一個設定しなきゃいけないので、
コマンド一発でできるのは非常に魅力的。  

学習コストどうなのー？とか思って試しましたけど、本当に簡単にできてびっくりでした。  

・・・  
**ここまではよかったんだ、ここまでは。**

## Lambda + DynamoDB
APIできたのでLambda上にソースコード書いていきますよーっと意気込みます。  
ここでDynamoDBの性質に非常に苦しむことになります。  

今回やりたかったのが、
1. データのランダム抽出
2. ヒューッ！付与機能の実装
3.  **ヒューッ！ランキング**

一番苦労したのがランキング機能でした。まあ仕組みをわかってしまえばよかったのですが、
そこになるまでに時間がかかった。  

DB構造としては、~~クソアプリだし適当にテーブル一つでいいやって感じで~~下記のようなデータの持たせ方をイメージしてました。  

|id|serif|hyu|
|---|---|---|
|1|名言1|1|
|2|名言2|3|
|3|名言3|2|
|4|名言4|1|

**(hyuってカラム名付けたの人生で初めてだぞ良いのかこの命名)**
はい、完全にRDB脳で設計してますね、これだと不都合がありすぎるのです。  

### パーティションキーとソートキーについて
DynamoDBの特性上、パーティションキー、必要であればソートキーをつける必要があります。  
パーティションキーのみの設定する場合、テーブル内でパーティションキーのデータは**ユニーク**である必要があります。  
パーティションキー+ソートキーを設定する場合、**パーティションキー+ソートキー**の組み合わせが、テーブル内でユニークである必要があります。

はて？？なんのことやら？？
とりあえず軽量なKVS使うんだし全件データ取得してきてアプリでガリガリロジック書けば良いんだろ？？

とか思ってました。

それでは今回のやりたいことに照らし合わせて考えてみます。  

1.データのランダム抽出

これは現在のテーブル構造でも可能です。  
dynamoDBには`scan`という機能が用意されており、テーブル名だけ指定すれば全件データを取得することができます。  
データさえ取得してしまえば、件数を最大数に設定して乱数だけパパッと作っちゃえばランダム表示は簡単に実装できそうですね。  

2.ヒューッ！付与機能の実装

これも現在のテーブル構造でも可能です。  
dynamoDBに用意されている`update`を利用しますが、テーブル名、あとはユニークなキー情報と更新データを渡してあげれば
何も問題ありません。

3.**ヒューッ！ランキング**

このテーブル構造だとこれが問題だった。
ランキングで重要になってくるのがデータのソート。
DynamoDBの仕様上、ソートを行うためにはソートキーが必要になります。  
また、ソートキーでのソートを行うために、パーティションキーでユニークな値を指定しなければなりません。  

ここでめちゃくちゃ悩みました。~~単に頭が悪いだけです~~

さっきのテーブル設計で考えるのであれば、

|id|serif|hyu|
|---|---|---|
|1|名言1|1|
|2|名言2|3|
|3|名言3|2|
|4|名言4|1|

RDB的に`hyu`が多い順に取り出すのであれば、こんな感じのSQLにきっとなるでしょう。  

```
SELECT hyu from TBL ORDER BY hyu DESC;
// [3,2,1,1]
```

これをDynamoDBでやろうとすると、`query`という機能を利用します。
が、`query`を使う場合パーティションキーにユニークなものを指定する必要があるので、多い順に**全件を**取り出すことができません。
`query`機能の使い方上、id = 1, id = 2など、パーティションキーを指定する必要があるので、そもそも全件というのがどう頑張っても取れないんですよね。

### 結論
GSI(Global Secondary Index)を利用したのと、キーの付け方を少し工夫しました。  
最終的なテーブル設計としてはこんな感じ。  
※正直このやり方で良いのか微妙・・・ってか今回LSIでもよかった気がする

|type|id|serif|hyu|
|---|---|---|---|
|post|1|名言1|1|
|post|2|名言2|3|
|post|3|名言3|2|
|post|4|名言4|1|

`type`がパーティションキーになっており、`id`がソートキーになっています。
また、`type`がパーティションキー、 `hyu` がソートキーとなるGSIを設定することで
ヒューッ！ランキングのデータが取得できるようになりました。

いや、めちゃくちゃ悩んだわ。DynamoDBの設計力をあげたい。
解説記事はたくさんあるのでそれをちゃんと読もうね。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">グローバルセカンダリインデックスの使い方やっとわかってきたかもしれない。。サーバレスでもRDBの恩恵を受けたい</p>&mdash; yoshi.@code-plum (@codeplumdev) <a href="https://twitter.com/codeplumdev/status/1210341852051722241?ref_src=twsrc%5Etfw">December 26, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

## Vue.js

フロントは大したことやってません。
あえて言うならVuetify導入によるデザイン工数削減ぐらいでしょうか。  
CSSフレームワーク使ったならもうちょいセンス良く仕上げてくれとか言わないでください・・・

## デプロイ

Lambdaのデプロイは先述のnpm scriptでワンコマンドデプロイ可能。
Vueに関しても同様にnpm scriptでビルドしてS3にアップロードしてくれるようなものを簡単にpackage.jsonに書きました。

```
  "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "lint": "vue-cli-service lint",
    "deploy": "npm run build && aws s3 cp dist/ 's3://{YOUR_BACKET_NAME}/' --recursive"
  }
```

すごくいまさら感満載なのですが、最近npm scirptよく使うんですけど、便利ですよねこれ

## 完成
そんな感じで完成。  
実際の稼働時間としては丸々二日ぐらいでしょうか。  

コーディングはそこまでハマったポイントはなかったけど、DynamoDBの実装に大きく時間を食われてしまった。  

仕事納め翌日に鬼の形相でコーディングしてました。楽しかった。

## まとめ
### 工夫した点
 - サーバレスアーキテクチャ
 - aws-serverless-expressを利用したExpress丸ごとのAPI化

### 大変だった点
 - サーバレスアーキテクチャ（結局調査時間めっちゃかかってしまったやん）
 - DynamoDBの特性（相変わらずDBは苦手）
 - **デザイン** (学生時代美術の評価が10段階中2という快挙)
 - Vue.js(なんだかんだまだjQueryに慣れちゃってる)

### 楽だった点
 - デザイン（Vuetify偉大）
 - **最悪間に合わなくても良いという心の余裕**

### 妥協した点
 - ドメイン（いや、、クソアプリだし多少はね？）
 - ソースコード可読性（リファクタリングどうこうじゃなくてロジックがゴミ実装の部分が多い）


## 最後に
***12/25に間に合わせてリリースしたかったよ！***

## おまけ
いくつかコブラの名言を紹介したいと思います。  

### Q.コブラ…あんたケガは…
![スクリーンショット 2019-12-30 4.26.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535324/346aadc3-845a-7c14-7571-b007eba63286.png)

個人的にこれめっちゃ好き。切り返しの凄さよ。  

### Q.(大怪我を医者に見てもらい) 驚くべきはやさで治りかけている…
![スクリーンショット 2019-12-30 4.27.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535324/6e515b6a-38fb-000d-f3de-a1c2ebe3a3b1.png)

コーンフレーク最強かよ。

### Q.剣は使ったことがあるのか？
![スクリーンショット 2019-12-30 4.25.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535324/30fad567-9706-fff7-68cd-4a28dfb60dcf.png)

もはやギャグでしか無い。

### オリンピックの話になり
![スクリーンショット 2019-12-30 4.28.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535324/daca1ea6-d315-6d8a-3491-901b62663468.png)

これもめっちゃ好き。コブラっぽい。



---

最後まで見てくださってありがとうございました。
再掲ですがサイトこちらになります。  
[アクセスの度にコブラの名セリフをランダムに表示するだけのサイト](https://randomcobrabucket.s3-ap-northeast-1.amazonaws.com/index.html)

それではみなさま良いお年を。