# 学生エンジニアが語る「技術学習 x 個人開発」のススメ
## 自己紹介 ~ React と恋に落ちた理由 ~
こんにちは。まっきー(@macinjoke)です。今回は僕が作ったタイピングWebアプリ「Study Typer」をご紹介をします。3部構成で、前編は自己紹介、中編は「Study Typer」の説明、後編は技術面の話をします。
僕は現在、情報系専攻の修士2年です。初めてのプログラミングは中学生のとき、ボンバーマンのようなゲームを作りました。今では趣味やアルバイトでコードをバリバリ書いています。最初はWebのバックエンド開発が中心でしたが、Reactに出会ってフロントエンドにのめり込み、今ではフロントエンドで食っていくと心に決めています。
初めてjQueryのコードを2週間ほど扱ったとき、**フロントエンドはカオスになりそうだから関わりたくない**という感想を抱きました。
しかし、2年前にReactに出会い、見方がガラっと変わりました。フロントエンドでも安心してライフサイクル・状態管理ができると気付き、心踊らせたのです。
またReactによるコンポーネント指向の開発では機能と見た目を1つのコンポーネントに閉じ込めるので、CSSで予期せぬ副作用が出ません。
僕はUIデザインにあまり興味がないので、CSSを書くのはデザイナーさんに頑張って欲しいと考えています。コンポーネントごとにデザインを実装できると、分担作業を進めやすいので良い開発体験になりそうです。そのためにReactではStorybookというツールが用意されています。このようなことが、自分にとっては新鮮で面白いと思い、Reactを極めたいと思うようになりました。
## 「Study Typer」 とは
本題の「Study Typer」について説明をしていきたいと思います。ずばり「英語学習者のためのWebタイピングゲーム」です。
http://study-typer.macinjoke.com から遊ぶことができます。
(※タイピングアプリなのスマートフォン非対応です。)

1ページで作られたWebアプリです。左側に英単語が、右側に日本語の意味が書かれています。ユーザは意味を確認しつつタイピングをすることで英単語の勉強をすることができます。英単語の方を入力するので、英語タイピングの練習にもなります。ちなみに、英単語のタイピングの早さというのはその単語を何回打ってきたかで決まると思います。例えば「foreign」という単語、打ち慣れていないとめちゃくちゃタイポします(体験談)。
下の方にあるRankパラメータは1~30まであり、英単語の難易度を変更できます。1は簡単で、30まで進むと難易度があがります。1画面で10個の単語をタイピングができるようになっており、10問目のタイピングが終わったらまた最初の1問目に戻ります。出題される英単語はランダムです。Updateボタンを押すことで新しい10問の英単語を打てます。次の単語に進むと英単語の音声が流れ、発音の確認もできて便利です。

想定ユーザは高速タイピングでドヤりたい・英語力UPでモテたいエンジニアです。
### Study Typerをなぜ作ることにしたのか
マネタイズなどは今の所考えていません。単に自分が欲しいと思ったものを技術の勉強がてら作ってみようというノリです。ノリで作ったからといって無価値だとは思っていません。他にはない便利なアプリなのでぜひ使ってみてください。
Webのタイピングゲームは検索すると色々と出てきますが「日本人の英語勉強用」に特化したタイピングゲームは数が絞られます。さらに「英単語」に絞ったらほぼ見つかりません。なにより「見た目がクールである」「すぐにゲームが始められる」「**英語の音声が流れる**」といった自分の要望を満たすものはまだ世の中にないと判断し作ることにしました。
### 今後の改善点
今後以下のような機能、改善をStudy Typerは考えています。
- UI改善(一気に英単語10個表示している意味がなさそう)
- 英単語データの拡充
- ログイン機能: 自分の記録を残す(ログインしなくても使えるようにはする予定です)
- タイムアタック、スコア機能の追加: 各単語の打ち始めから打ち終わりまでの時間を計測して、日本語の意味をしっかり読みながら競える(学習用タイピングゲームなので)
時間のあるときにゆっくりと作っていきたいと考えています。ご意見や、機能提案、バグ報告、プルリクなどWelcomeですのでよろしくお願いします!
## 技術的なお話 〜どうやって作ったか〜
コードはGitHubで公開しています。
- フロントエンド: https://github.com/macinjoke/study-typer-frontend
- バックエンド: https://github.com/macinjoke/study-typer-backend
### 構成
Study TyperはReactを使ったSPAです。APIにはPythonのFlaskを利用しています。英単語データの保存にはRedisを使いました。システム構成図をお見せします。
<画像が挿入されます>

#### Dokku
AWS EC2のUbuntu上に「Dokku」(http://dokku.viewdocs.io/dokku/)と呼ばれるHerokuライクなPaaSを簡単に立ち上げるツールを入れています。Dokku上のメインアプリとして、Python, Flaskを使ったAPIサーバを立ち上げています。
Dokkuの「Redis Plugin」(https://github.com/dokku/dokku-redis)でRedisを立ち上げ、Flaskアプリと繋げています(アプリ側のREDIS_URLという環境変数に値が入るのでそれを使い接続します)。これらはgit pushコマンドで楽チンデプロイできます。
Dokkuを使った構成はコマンドを何回か叩けばポンとすぐできるので簡単でした。
#### なぜHeroku でやらなかったか
Dokkuなんて難しいことしなくてもHerokuとか使えば良いと思われるかも知れません。ですが、Herokuだとまあまあお金を払わないとRedisのデータを永続化することができません。今回Redisのデータは永続化したいと考えていたので、使い慣れたHerokuを手放しました。
#### Redis
Redisにはランクをつけられた英単語とそれに対応する日本語意味のデータが格納されています。ブラウザからのリクエストによってFlaskサーバがRedisからデータを取得し、レスポンスを返します。データの更新は管理者(僕)しかしません。なぜ数あるDBの中でRedisを選んだかという話ですが、単純に使ってみたかったからです。とはいえ、後から振り返るとRedisならではの利点がありました。
リレーショナルデータベースや、MongoDBのようなドキュメント型のデータベースと比べるとRedisはレスポンスが早いです。Redisはインメモリのデータベースで、永続化機能を弱める代わりに速度を上げています。今回のアプリではデータの更新を行うのは管理者(僕)だけなので、更新が頻繁に起きず速度が落ちるような心配もありません。
RedisのデータモデルはKey・Value形式で、複雑なデータ構造を作るのは大変です。[^1]Study Typerでは以下のように設定しています。
[^1]: RedisでRDBのように複雑なデータを扱う例として https://redis.io/topics/twitter-clone という公式チュートリアルがわかりやすかったです。
```
# 英単語の集合
words:1, words:2, words:3 # words:[rank] キーをSorted Sets型のデータで定義
words:1 = [ attack, cook, ask, come, ... ] # 例: ランク1の英単語のリスト
# 単語の個別情報
word:attack, word:cook, word:boondoggle # word:[word] キーをHash型のデータで定義
word:attack = { rank: 1, ja: 攻撃する } # 例: attackという英単語の情報
```
Redisは検索向きではなく、キーを指定して特定のデータを取得することしかできません。必要なデータ形式はあらかじめデータに持たせなければならないのです。このような工夫は必要ですが、Redisを使ってみて、結果的に良かったんじゃないかと思ってます。
実際に英単語を取ってくるのも早いですし。
## 英単語データ
英単語データはejdic-handというフリーの辞書を使っています。英単語音声データはproject shtookaから無料データを探して使いました。英単語の難易度を設定するためのデータは某辞書サイトをスクレイピングしています。
これらの別々の情報を結びつけてRedisに保存するバッチスクリプトはPythonで書いています。コーディングは簡単でしたが、フリーのデータを探すのが大変でした。
#### S3 とCloudFront
静的コンテンツの配信はAmazonのS3とCloudFront(CDNサービス)を使いました。最初はS3だけで良いかなと思ってたのですが、公開した結果、英単語の音声が鳴るのが遅い(音声の読み込みが遅い)という問題が起きたため、CloudFrontも使うようにしています。その結果ちゃんと一瞬で英語音声が鳴るようになりました。CloudFrontの設定はそんなに難しくなかったのは良かったです。転送するデータ量が多いときはCloudFrontを使ったほうが結果的に料金が安くなるらしいです。
### Reactの実装
ここからが本番です。僕が大好きなフロントエンドの実装のお話です。楽しみですね。
#### Flow
今回は「Flow」(https://flow.org/)を初めて使いました。TypeScriptのように漸進的型付けを可能にします。最初は戸惑いましたが、Reduxリポジトリにあるtodos-flowというTODOアプリのFlow版サンプルを参考にしたら良い感じになりました。
漸進的型付けを初めてやりましたが、かなり良さそうです。慣れるまではどう型を付けたら良いか分かりませんが、やるうちにコツが掴めます。コードが肥大化して自分が書いたコードを忘れるくらいになってから見ると改めて型の偉大さがわかりました。エディタの補完もバリバリ効くのがGoodです。
最近は流行としてFlowをTypeScriptに移行する流れが強く、自分も検討をしています。
#### Redux
Reduxという状態管理ライブラリを使っています。React界のデファクトです。ユーザが打ち込んだ文字や英単語のデータを管理しています。
正直この規模のアプリでは過剰まmpですが、これからスケールアップする可能性と、Reduxを使い慣れているので使っちゃえという感じでした。Reduxは慣れてきたのでちゃんとした設計になっていると思います。非同期処理はthunkでやっています。
#### CSS
まだ慣れていないstyled-componentsを使わず、less, bootstrapで手軽に実装しました。UIに時間かけたくなかったので。次のプロダクトはCSS in JSに挑戦したいです。
#### Prettier
Lint系はPrettierを使いつつairbnbのコンフィグをextendsして一部修正しています。Prettierは最高です。1行に入る文字数を考えて整形してくれます。コードフォーマットについて考えたり議論する時間は減りそうです。迷える子羊たちよ、Prettierに従え。
#### Jest
Jestというテストツールを今回初めて使いました。デフォルトでカバレッジを取得できるため使いやすいです。今回は純粋関数でテストがしやすいReducerのみテストしました。UIは頻繁に変更する予定なので、Enzymeを使ったUIテストはやりませんでした。
## まとめ
このように今回のアプリ開発では「最近のフロントエンド開発」を意識して、ナウい構成をキャッチアップしたつもりです(CSS周り以外)。新しい技術に挑戦しながら開発をしていたので、1年くらいかけてダラダラと開発をしていました。ですがそれを苦痛とは思わないほどフロントエンドの新しい技術は面白いものばかりでした。
またタイピングゲームという題材自体がはシンプルで良かったのだと思っています。題材選びが良いと続けやすいですね。運用環境も、初めて使うCloudFrontなど挑戦ばかりでした。Webアプリの個人開発でデプロイまで行ったのはこれが初めてです。
2年前はフロントエンドのフの字も知りませんでしたが、ここまでできるようになりました。独学中心ですが、研究室の先輩からの教えや、バイトの経験が役に立ちました。個人的な感想として、JavaScriptのライブラリの入れ替わりスピードは尋常ではない気がします。次々と新しいものが登場しては、次々と廃れていきます。
そんな中で僕たち人間が開発をするために必要なことは表面的な使い方だけを覚えるのではなく、その技術が何をしているかをしっかり理解することだと思いました。そのことを意識して公式ドキュメントのGetting StartedやCore Conceptに目を通しています。
自分の性格的に、コードがただ動けば良いというよりも「コードがどうなっているのか」「設計がしっかりしているか」が気になり、突き詰めようとしてしまいます。そのためインプットばかりを行っているので、もっとアウトプット速度を早めたいなと思いますね。いや、フロントエンドのインプット面白いんですよ。
何か感想や意見がありましたら Twitter「@macinjoke」やGitHubのIssueにドシドシお願いします。こういう構成の方が良いよーなどのアドバイスも欲しいです。