FirebaseとReactを使って学祭の模擬店用にカウンターアプリを作った話

去年の秋に大学の学祭で、クラスで模擬店を出したのですが、その売上をリアルタイムで把握できるような簡易Webアプリを作りました。アウトプットの練習も兼ねて、それについて書こうかと。

# 自己紹介
もうすぐB2になろうとしている、横浜在住の大学生です。
中2の頃Javaを学び始めて、それ以降Android・iOSのアプリやPythonでのDNNに手を出しつつ、コードをいじったりいじらなかったり。
今の所一番好きな言語はKotlinです。これ作るまでWeb全然触っていなかった。あとC言語系も全然なのでそのうち触りたい。

# 目的
#### アプリの目的
**模擬店での売上をリアルタイムで把握すること**がメインの目的。何がどれくらいの個数売れて、売上が何円なのかの把握。
最終的な売上の計上には使わないが(入力し忘れもあるだろうし)、その日の売れ行きの目安になる。
ついでに、どうせだったら似たような状況で使い回せるものをと、未受け渡しの注文の一覧や、会計時に合計金額を表示する機能などをつけたが、案の定その機能はあまり使わず。
#### その他個人的な目標
実は似たようなものを同年春の学祭でもAndroid・iOSネイティブアプリとして作成(当時Webの知識は皆無)。ただ、そのときはあまり使い物にならず。というのも…

- レジ機能がメインだったものの模擬店程度なら暗算で事足りる
- 一応データは溜めるもののローカルオンリーで皆で共有できない

そのリベンジも兼ねてメイン機能を売上把握に変えて作ろうとした。ただ、またもiOS・Androidそれぞれに作るのがとっっっても面倒くさかったので、Webの勉強のためにということでWebで作ることに。
HTMLタグくらいしか知らなかったところから数ヶ月で動的なものを作るの、背伸び感めっちゃあったけど気にしないことにした。

# 主に使ったもの
- Firebase
    - Firestore
    - Firebase Hosting
    - Firebase functions
- Webpack
- React
- Basic認証

サーバー持ってないし、そもそもそこらへんの知識もほぼないので、Firebaseの力を借りることに。Firestoreのデータはスマホからも弄れるし。
functionsを使っているのは、クラスの人の認証に簡易的にBasic認証を使ったため。
Webpackは色んなライブラリをFirebaseでも使えるようにするために使用。
内部の状態変化を画面と連動させるのにReactを使用。Angularとかもあるらしいけど最初に知ったのがReactだった。SPAにはしていない。

# できたもの
[GitHub](https://github.com/PaulHowell/stall-counters)
[デモページ](https://stall-counter-dev.web.app/) (オレンジの方(ICE AGE MAN)に ユーザー名:demo パス:demopass で入れます)
(悪質なサイトだと警告が出る可能性アリ。なにか脆弱性を含んでいるのかなあ…？)

## 見た目
デザインセンスはないのでご了承を。。
### 店舗トップ
<img width="300" alt="店舗トップ(iPhone 6_7_8 Plus).png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156657/9e9a5f2f-f9cc-b11f-2f83-817b07c150ec.png">
今日と今まででそれぞれどれくらい売れたのかを表示。その下にメニューを配置。
メニューは上においても良かったかもだけど、メイン機能が売上把握なのでこの配置にした。

### 会計担当用ページ
<img width="300" alt="注文画面(iPhone 6_7_8 Plus).png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156657/abab79e2-dcb9-bcad-dcaf-a6f847effb48.png">
何故かここだけ背景白だが特に意味はない。
ページ読み込み時にメニューをFirestoreから取得。注文を入力して注文確定したらそれがFirestoreへ送られる。「キャンセル」ボタンは「クリア」にしたほうが直感的だったかも。

### 注文一覧
<img width="300" alt="注文一覧(iPhone 6_7_8 Plus).png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156657/0dbdb825-c741-6e4f-3348-e39b253cffc7.png">
受け渡しが完了していない注文の一覧が表示される場所。実際の運用では使われず、ただただ完了ボタンを連打してストレス発散(?)する場所となった。


# こだわりというか諸々
### 柔軟性
メニューのデータをハードコードせずFirestoreに預けることで、いつでもメニュー変更や値段変更に対応できるように。Firestoreにアクセスして手動で変えないといけないものの、手元のスマホでできるので、何度もあった値下げなどには割と簡単に対応できた。
また、データさえ作ってhtmlをコピペしてちょこっと改変すれば、簡単に他店舗にも流用できる。足りない機能さえなければ。
### 前売り券・セット割機能
前売り券はデータさえ変えれば単なる割引券にもできる(むしろ内部的には値段付きの割引券みたいな感じで処理している)。前売り券の値段のデータは一応設定してあるものの、払い戻し機能を結局つけなかったので何にも使われていない。
セット割は学祭の途中で急遽付け加えた機能。3個で500円とかそんな感じのやつ。いくつかセットがあっても最も安い値段を表示する…はず。
### 認証
店舗の人(クラスメイト)だという認証をどうしようか迷った。
できればFirebase Authenticationで完結させたかった。(Firestoreのアクセスルール設定でFirebase Authは使う。)
FirebaseAuthでは、会員登録ページとかは楽に用意できるらしい。
でも各々に登録してもらってそれを承認するのは手間がかかるし避けたい(誰か把握するのも大変だし)ので、楽そうなBasic認証にすることに(セキュリティもそんなに必要じゃないし)。Basic認証を通った後、Firestoreのルール用には匿名ログインで対応した。
もう少しセキュリティ必要なら手間をかける必要がありそう。
FirebaseAuthにメルアド-パスワードじゃなくてユーザー名-パスワード方式があればそれを使ったのだけれど。
### firebase.initializeApp() まわり
このメソッドを複数回呼び出すと怒られる。けどSPAじゃないしどのページから入ってくるかわからないので、トップページに限らずどのページでも初期化の態勢は整えないといけない。
いちいち初期化済みかチェックしたあと初期化する処理を書くくらいならまとめてしまえということで、fbinitAnd(func)というメソッドを定義した。既に呼び出されていたらそのままfunc()を実行し、それ以外はinitializeしてからfunc()を実行。
非同期処理が関わってくるので少しめんどくさかった。もう少しスマートに書けないものかなあとも思うがこれ以上はわからない。勉強不足。


```javascript:fbinit.js
import * as firebase from "firebase/app";

function fbinitAnd(func){
	if (!firebase.apps.length) {    // まだ初期化されてない
		fetch("/__/firebase/init.json").then(async response => {
			firebase.initializeApp(await response.json());
			func();
		}).catch( error => {
			throw error;
		});
	} else {    //初期化済
		func();
	}
}
export default fbinitAnd;
```
あとはtry-catchでfbinitAndの呼び出しを囲めば例外処理も上手くいくはず…はず…
今回例外処理には苦戦したので自信がない。

# 課題

### Reactの描画更新処理
これを作った後、[「React に優しい僕でありたい」](https://qiita.com/nabeliwo/items/de0bc076ed7105dda2ca)の記事を読んでレンダリングを可視化してみたら、やっぱり無駄にレンダリングしていた。
レンダリングするかしないかを勝手に判断してくれてると勘違いしていたけどそうではないらしい。今回はそんなに重い処理をしているわけではないからいいけど、Reactとか使うとき留意しておきたいポイントだなと思う。

### コードのコメントの量
できるだけわかりやすいように書くことを意識しているも、少し時間が経った今見ると、コメントはあまり書いていない。それでも見ればだいたいなんとなくわかる気もするけど、記憶が完全に抜けきってから見て理解できるかは怪しい。
セット割関連の処理は突貫工事だったのでそこらへんは別としても、もう少し意識していきたい。

### Git関連
Gitflowを試しに使ってみたけれど、どの段階で新たにfeatureブランチ切るかとか、Gitそのものも含め慣れていかないとなあと。途中、面倒になってdevelopブランチで作業していたり…。

### 整理券番号機能
注文番号を自動で割り振る機能はあった。が、紙の整理券を使いまわしたりしていると、そっちに番号を合わせないといけないので注文時に整理券番号を入力する機能をつけておけばよかった。
あとそもそも注文番号が会計画面に表示されていないので使い物にならないし。
ここらへんをちゃんと整備さえしていれば、サークルの方の模擬店にもきちんと流用できたのになあと後悔。おかげで注文一覧画面が日の目を見ることはなかった。

# その他感想
作ったものを実際に使って機能しているのを見るのは面白い。
ただ、どんどん増えていく売上を楽しむための可視化のつもりだったのに、肝心の売れ行きが不振だったのは残念。
あと、新しい分野を学ぶときはじっくり体系的に学んでいきたい。Web系は特に変化が速いし、そうも言ってられない気もするけど。
今更だけどこれじゃあブログでは…？Qiitaに投稿して大丈夫なのか？

# 参考にさせていただいたもの

[Firebase チュートリアル　FriendlyEats-React](https://qiita.com/isamua/items/c50d3a09615285bceea0)
 最初見様見真似で始めるとき、ソースをめっちゃ参考にしていた。

[Babelとwebpackを使ってES6でReactを動かすまでのチュートリアル](https://qiita.com/akirakudo/items/77c3cd49e2bf39da79dd)
 初期の参考第2弾

[Webpackで複数のHTMLを出力する。](https://gist.github.com/giisyu/12dfe2a4bde329b8ea99747fe87f63a9)
 求めているのはSPAじゃないのでどうするかってときに参考になった。chunkの存在とか。

[webpackのcss-loaderでCSS Modulesをやる](https://qiita.com/_likr/items/c335dec5221024ad56bc)
 ReactでCSSを扱うときにどうするかってときこれを参考に。

[[フロントエンド] React.jsのJSXで条件分岐を表現する方法（5つ）](https://www.yoheim.net/blog.php?q=20180409)
 レンダリング時に条件分岐がほしいとき。最初書き方に混乱しては見ていたのを覚えている。

[「FirebaseでBasic認証をかけてサイトを作る(Hostingする)」](https://qiita.com/567000/items/65f55eda8d7c6df09138)
 Basic認証簡単そうじゃんと思ったらFirebaseでやるにはそのままじゃだめだった。この記事を参考に導入。

[「React に優しい僕でありたい」](https://qiita.com/nabeliwo/items/de0bc076ed7105dda2ca)
 これは本文中に出した記事。優しくありたい。

他にも沢山参考にしたけど、作るために漁っていたのがだいぶ前で、どれを参考にしたかいまいち覚えていない…。覚えている限りで。