初Reactでも能力Amplifyしてスマートな(?)チーム分けアプリを作ってみる


突然ですが現withコロナ時代においてチーム分け時が必要な時はないでしょうか。

例えば最近増えましたリモート会議ですと一人しか同時に話せないのでzoomのブレイクアウトルーム使うなどチームを分割することは良くあるのかなと思ってます :person_frowning: :person_frowning_tone1: 
（ちなみに自分の所属するチームでは毎朝、朝会でランダムに２、３人のチームを組んで開発しております。Be Full Stackです。）

普通のチームわけであればrandomに割り振ればいいかと思いますが、下記のような状況で困ることがありました

- なるべく均等に分けたいグループがある（たとえば下記）
    - 研究室MTGで各学年が均等に分かれて欲しい
    - 飲み会で１つのチームに各世代の人がいて欲しい
    - マルチファンクショナルなスクラムチームを結成する時に各分野（インフラ、バックエンド、フロントエンド、デザイナー、QA）の人がそれぞれれのチームに入って欲しい）
- ランダムだと前回と同じ人と同じチームになってなかなか同じチームになれない人がいる

このようなことが何回かあって毎回手動で調整するのが面倒だなと思い、楽するために苦労するエンジニア精神でこの２つの問題を解消するwebアプリ作ってみました。

↓作ったもの
https://www.memo-team-gen.com/

↓~~ク~~ソースコード (拙いですが公開しました。`aws-exports.js` などセキュリティ的にまずそうなのは除いております。)
https://github.com/LittleWat/memorizing-team-generator-public

普段の業務はモバイルアプリなのでiOSやAndroid,バックエンドをいじることはあってもフロントをいじることはほとんどなく、フロントエンドは :thinking::thinking::thinking:状態だったのですがこれを良い機会にということでググり力 :up: :up: で開発しました。

時代はjsよりtsということで、初めて `typescript` を使ったので設定周りでつまづきまくりました。が、何とかバックもフロントも開発できバックもフロントもイケるts(js)の旨味を享受しました。`typescript`初めてではありましたが、null安全で型推論が多少ありmapはじめコレクションの処理がサポートされていたりして`swift`, `kotlin`と似ており個人的に書きやすかったです。

いろいろググると、`React`, `Vue`, `Angular`がフロントエンド の三大レームワークとしてあり、`Angular`は後方互換性を消しまくってて開発辛いみたいな記事を散見したので選択肢から外し、`React`と `Vue`で迷い :thinking: 世界的にメジャーなのは `React` ということで `React` の学習から入りました。（のちのち `Vue` のチュートリアルも行ったのですが、今回のようにRedux使わなくても良さそうな小さいアプリだったら`Vue`のほうが楽かもと感じました。）

では開発の流れや内容についてご紹介したいと思います。

## 開発の流れ

### Reactの学習

適当にググって出るチュートリアルをやっているとReactは書き方が`class`のものと`function`(`Hooks`使うもの)のものの大きく二種類があるのを知り、`Hooks`がナウいことをしったので何かまとまっている良さげな記事ないかなと調べておりますと下記の良記事に遭遇できました。

- [Reactの学習、今からやるならこうする](https://qiita.com/d0ne1s/items/971ffc8db6815e9aaad0)

こちらで紹介されていた本（pdf）を買って勉強しました。

- [りあクト！ TypeScriptで始めるつらくないReact開発](https://oukayuka.booth.pm/items/1312652)

こちらの本、体系的にまとまっててお世話になりました :bow: 
Boothのpdfの本ですが、紙の本は時代の流れについていけてない(hooks以前のものが多い)ので流行りすたり（競争）の激しいDeepLearing研究時代を彷彿とさせるものを感じました。

またUIのライブラリはbootstrapか何か使おうか迷っていましたが、[material-ui](https://material-ui.com/getting-started/templates/) がぱっと見充実しており、[GoTのキャラ](https://www.creative-tim.com/product/material-kit-react?partner=104080)が使われていたためmaterial-uiを使うことにしました。話それますが、GoTのキャラはDeepLearning研究でも[こちら](https://github.com/albertpumarola/GANimation)などちらほら題材として使われていましたね。


### 開発

ナウい（お財布に優しい）構成で行きたいなと思い、firebaseは以前iOSアプリで一通り触ってみたので、今回は別のものがいいなということでawsのamplifyでサーバレスにいくことにしました。

#### フロントエンド
`amplify hosting` で行いました。masterにpushするとデプロイされる仕組みも簡単に構築でき、ドメイン名の取得も簡単に行えました。amplify楽で良いですね。

#### バックエンド
`amplify api` は `GraphQL` と `RestfulAPI`の二種類選べます。

ナウさを求める私はGraphQLで行おうと思い、下記のAWS公式のチュートリアルを行いました。

- [Amplify SNS Workshop ](https://amplify-sns.workshop.aws/ja)

このチュートリアル、実装物も具体的で図解もあり、分かりやすく勉強になりました（おすすめです！！）
AppSync使えば簡単にElastic Seachと連携できるというのも知りました。強いですね。

がしかし、実際のチームわけのモデルをしたところ、お金のかかる（CapacityUnitの料金が倍になる）DynamoDBのGlobasSecondaryIndexが自動で貼られてしまったり、思ったように動かなかったので断念しました。（さらなるGraphQLの勉強の必要性を感じました。。）

仕方がないので、ここで方向転換して普通の `RestfulAPI`でいくことにしました。

こちらもいろいろ選べたのですが、`express`で開発することにしました。
フロントは `create-react-app` でtypescirptが選べたのですが、`express`はデフォルトが `js`でバックエンドもどうせなら統一して `ts`でいきたいなと思ったのですが、割と設定でハマったので後述してます。結論として何とかjsに変換してデプロイはできました。
（amplify、デフォルトを `js`か `ts`か選べるようになることを期待してます。（自分でコミットせぇという話かもですが汗））

##### DynamoDBのテーブル設計

Dynamo(NoSQL)なのでRDSではアンチパターンとされる配列を格納しても良いかと思い、配列を格納する設計にしました。

設計には[NoSQL Workbench](https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/workbench.settingup.html) という下記のようなツールを用いました。

![スクリーンショット 2020-09-22 21.41.18（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104256/471119b2-7d9a-dbe8-ab8d-ce1b25307acc.png)

このツール、dynamoのlocalやawsに簡単に設計したものを反映でき、テーブル定義のjsonも保存できて使いやすかったです^^


##### チームわけのアルゴリズム

`random` か `smart` か選べるようにしました。

`スマートな`と言ってますが、実は100回ランダムにチーム生成してその中から スコア(frequency)が低いものを選ぶというシンプルな方法です。

スコア(frequency, `freq.`)の計算ですが、これまでのチームわけの履歴を見て最近チームになった人がいればいるほど高いみたいな計算してます。

簡単に数式で示しますと

```math
　w_{ij} = 
\begin{eqnarray}
\left\{
\begin{array}{l}
1　(member_i とmember_j　が同じチームの時) \\
0　(member_i とmember_j　が異なるチームの時)
\end{array}
\right.
\end{eqnarray} \\
N: 過去の何回分まで見るか(max10回分見るようにしてます) \\
M: メンバーの総数
```
と置くと下記のような計算をしてます。

```math
  freq. = \sum_{k=1}^N \sum_{i=1}^M \sum_{j=1}^M \frac{1}{k}w_{ij} \\
```

ソースコード的には下記のあたりです。
https://github.com/LittleWat/memorizing-team-generator-public/blob/939748cd58a5f4be415b9a65b1b417407d92f946/amplify/backend/function/memoteamgenec8c5c26/src/ts/service/adjacent-matrix-computer.ts#L58

具体例で説明しますと下記のように均等に分けたいグループがあるときに
![スクリーンショット 2020-09-24 0.08.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104256/6fb730f8-c183-2bc0-74e3-160ba8b708c9.png)

単純に直近10回で何回一緒のチームになったかが下記の隣接行列で、5回一緒になった人が数組ありますが、

![nmat.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104256/7d23b45d-80bb-91f6-3c14-c3e52c192ebd.png)

下記の重み付けされた隣接行列で100(最大値を100にスケールしました)の組みは[麻生太郎、松本さん]ペアなので

![wmat.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104256/40119c28-7487-6aca-dee8-1368bb29e451.png)

次回は[麻生太郎、松本さん]が同じチームには絶対ならないだろうということでgenerationすると下記のようになり

![スクリーンショット 2020-09-24 0.11.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104256/24670421-34a2-9c1d-1c7c-8a0a472f1ef7.png)

たしかに[麻生太郎、松本さん]は同じチームにならず、いい感じでチーム分けできていることが伝わるかと思います。

ぜひ使っていただいて何かフィードバックいただけると幸いです。 :bow: :bow:

##### Analytics
`amplify add analytics` で一瞬でAmazon Pinpointが使えるようになります。簡単ですね。。
GoogleAnalyticsやFirebaseAnalyticsの置き換えになりそうでしょうか。

## まとめ

- Amplifyは現在進行形で発展中（awsのUIもupdateされてますね。）
- 個人開発は全部自分でやらないといけないので良い勉強になる（慣れない技術を使うと苦労も多いですが発見が多く楽しい）
- 隣接行列の可視化のライブラリ探してて偶然見つけた`apexcharts`楽しい

## 将来的にやりたいこと

- ランダム生成はログイン不要にしたい
- 生成結果をslack通知

などありますがやるかは不明です。。

## ハマったこと

いろいろあるのですが、誰かの参考になればと思い念のため残しておきます。

### backendのfunctionを勝手にいじってうまくdeployできない

同じく困った人がいた模様

https://github.com/yayun-12/amplify-typescript

下記、私がミスしていたファイル構成(`src` に `ts`ファイルを置いた。)

```
➜ memoteamgenec8c5c26 git:(add-lambda-team-gen-api) ✗ tree . -L 2
.
├── amplify.state
├── dist
│   ├── app.js
│   ├── app.js.map
│   ├── config
│   ├── index.js
│   ├── index.js.map
│   ├── latest-build
│   ├── latest-build.zip
│   ├── models
│   ├── repository
│   ├── service
│   └── util
├── function-parameters.json
├── memoteamgenec8c5c26-cloudformation-template.json
├── package-lock.json
├── package.json
├── parameters.json
├── src
│   ├── app.ts
│   ├── config
│   ├── event.json
│   ├── index.ts
│   ├── models
│   ├── repository
│   ├── service
│   ├── swagger.yml
│   └── util
└── tsconfig.json

```

あるべき姿（デフォルトの姿): (`src` に `js`ファイル, `package.json`を置く。)

```
➜ memorizing-team-generator git:(add-lambda-team-gen-api) ✗ tree amplify/backend/function/tmp             
amplify/backend/function/tmp
├── amplify.state
├── function-parameters.json
├── parameters.json
├── src
│   ├── app.js
│   ├── event.json
│   ├── index.js
│   └── package.json
└── tmp-cloudformation-template.json
```

結局 `src`の下に `ts`ディレクトリを切って下記のようにtsconfig.jsonを編集すると動いたが、ディレクトリ構造が気持ち悪いことになりました...
何か良い方法ないのですかね :thinking: 

```tsconfig.json
{
  "compilerOptions": {
    "outDir": ".",
    "sourceMap": true,
    "module": "commonjs",
    "target": "es6",
    "lib": ["es2017", "dom"],
    "moduleResolution": "node",
    "removeComments": true,
    "allowJs": true,
    "checkJs": true,
    "allowSyntheticDefaultImports": true,
    "baseUrl": "./ts",
    "paths": {
      "*": ["node_modules/*", "ts/types/*"]
    }
  },
  "include": [
    "ts/**/*"
  ],
  "exclude": [
    "node_modules",
    "**/*.spec.ts"
  ]
}

```

### responsive designにできない

こちらは今もハマってます。ただのフロントエンドスキル不足故の問題です。。cssの勉強などします。。