自分の結婚式に写真共有アプリを作った

本記事は [個人開発 Advent Calendar 2019](https://qiita.com/advent-calendar/2019/private-development) 13日目の記事です。

# 経緯

2年前くらいに Motoki Narita さんの「 [結婚式を支えた技術 Firebaseを活用したサーバレスiOSアプリケーション開発](https://speakerdeck.com/motokiee/jie-hun-shi-wozhi-etaji-shu-firebasewohuo-yong-sitasabaresuiosapurikesiyonkai-fa
) 」の発表を拝見しました（たしか potatotips だったような :thinking: ）
もうこのとき感動しまくりで、いつか自分もこれやりたい！って思ってから、ようやくそれが叶ったって感じです。
みんな目線の写真というのが本当にいいなと思いました :relaxed:

# アプリ仕様

当初は結婚式アプリとして、席次表の閲覧とかPUSH通知でお知らせとかやりたかったのですが、工数の関係上、写真共有アプリに機能を絞ることにしました。

`参列頂いた多くの方に使ってもらいたい`

その想いから Android/iOS の対応はマストにしました。
「一番多くの いいね がついた写真を投稿してくれた人には豪華景品！」というゲームをするために簡易ログイン、いいね機能もつけました 🥕

## 対応OS

- Android
- iOS

## 機能

  - 写真の投稿
  - いいね
  - 簡易ログイン（ゲームで活用するため）

↓ 実際の画面 ↓

|ログイン画面|メイン画面|拡大表示画面|
|:-:|:-:|:-:|
|<img width="200" alt="login" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96682/ade6dd29-ab40-325d-2b42-f4d43ea6c46f.png">|<img width="200" alt="main" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96682/c422e46e-65a9-2a35-65fb-8504e5123924.png">|<img width="200" alt="detail" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96682/df084314-8b71-72db-82e4-c4178f545eb5.png">|

エスコートカードの裏面に専用の番号を書いて、それでログインしてもらう感じです

<img width="150" alt="detail" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96682/be6c6ae1-f5d0-52b5-5b49-c32e2cb39bd3.png">

# 仕組み

仕組みはこんな感じで考えました
Firebase さんに非常にお世話になりました :pray: 

## 使った Firebase の機能

||||
|:-:|:-:|:--|
|Cloud Firestore|<img width="300" alt="firestore" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96682/8b73f125-77e9-a8f8-ad3b-dc29b7ba12ed.png">|NoSQL データベース<br>投稿された画像の情報（ URL や いいね数 など）をここに保存する<br>クライアントはここから画像情報のリストを取得して一覧表示する|
|Cloud Storage|<img width="300" alt="storage" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96682/1fd27e62-a56e-a45f-c4eb-5b5b27081908.png">|ストレージ<br>投稿された画像の保存場所|
|Cloud Functions|<img width="300" alt="functions" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96682/2e3625bf-75d9-7e02-a3ed-addc5d57881f.png">|Node.js で書いた関数をいろいろなトリガーで実行してくれる<br>今回は画像の投稿（Cloud Storage へのファイル追加）をトリガーにサムネイル画像を作成|
|Hosting|<img width="200" alt="hosting" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96682/0d4c0040-3bbb-9e12-fdd3-68b29b75fe38.png">|HtmlをホスティングしてWebページが作れる<br>今回はWebアプリにして、各スマホのブラウザからアクセスしてもらう形を採用|

## 写真共有のざっくり構成

<img width="600" alt="system" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96682/2d79abf7-401c-8db8-e819-f9a280f09d51.png">

① Cloud Firestore にドキュメントを作成
② 写真をアップロード
③ Cloud Storage に写真が追加されたのをトリガーに Cloud Function でサムネイルを作成
④ サムネイルを Cloud Storage に保存
⑤ オリジナル、サムネイルの画像を Cloud Firestore のドキュメントに追記
⑥ Cloud Storage の変更がクライアントに通知される（写真が追加される）

# 結果

多くの方に投稿いただくことができました

枚数にしてなんと **319** 枚！！

やらかしてなければもっと投稿いただけたはずなのに（後述） :innocent:

ちなみに一番いいねが多かったのは新婦の甥っ子姪っ子ちゃんでした :baby:10ｲｲﾈ

<img width="400" alt="system" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96682/d4a4eb3b-f2fb-1c53-f90f-4246cabc3551.png">


# 失敗

はいこのアプリやらかしたんです。。

<img width="400" alt="スクリーンショット 2019-12-12 21.28.02.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96682/b1093ec9-12ad-f46e-9c4a-4f71d88ece53.png">

はじめログイン画面の「GOボタンを」非表示にして、番号を入力したらボタンを表示するという仕様にしていたのですが、このボタンが表示されないという方向がちらほら。。
（開発中は良かれと思って入れた機能ですが、今となってはマジでやんなきゃ良かったと悔やみきれません）

<img width="200" alt="スクリーンショット logindialog.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96682/858de2e1-b550-2379-1de1-d75f26550fc2.png">

iPhone ユーザーに起こってたようで、 IME 依存で jQuery の `keypress` イベントが発火しなかった模様。。。

この被害に遭われた方々は結局式の間アプリを使うことができませんでした（本当にごめんなさい :bow: ）

`参列頂いた多くの方に使ってもらいたい` 

はい :innocent: 

自分の iPhone でも、iPhone ユーザーの友人にもテストはしてもらってたんですが、まさか IME 依存という落とし穴

これはホントにホントに悔しいです。。

# 式終わって機能追加した

実は画像のダウンロード機能も実装しようと思っていたのですが、当日までに間に合いませんでした（手が回らなかった）
当日のウェルカムスピーチの際に皆さんと「いつか実装します！」と約束してしまったので、頑張ることにしました。

こんな感じでいけないかな？ と構想

<img width="400" alt="スクリーンショット logindialog.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96682/315ace58-87eb-9c2c-d8d2-bab529e52e4b.png">


① 選択された画像IDを Cloud Function の HTTPトリガー の関数に投げる
② ID の画像を Cloud Storage から（ Function 上で）ダウンロード
③ ダウンロードした画像を zip にする
④ zip を Cloud Storage にアップロード
⑤ Cloud Storage 上の zip の Download URL をクライアントに返す
（⑥ クライアント側でダウンロードを行う）

実際実装してみると、なんか動いてそうに見えたんですが、写真の枚数が多くなると Function でクラッシュします :thinking:

純粋にメモリが足りてないのかとか思ったんですが、Function のログ見るとなんか違いそうだがよくわからん。。（Cloud Function のベストプラクティスな開発手法知りたい）
この辺はもう少し調査が必要そうなので、今は五月雨式に選択した画像が降ってくる鬼機能にしています :sunglasses: 

# できなかったこと

最低限の機能実装って感じになってしまい、お世辞にも使いやすいUIとは言えないものでした :frowning2:
僕だけかもしれませんが、個人開発はどうしても UI が疎かになりがち。。
もう少しカッチョよくて使いやすいUIにしたかったです。

# まとめ

個人開発は楽しいはもちろん、めちゃめちゃ勉強になりますね。
今回も、普段業務で使わない node.js やら何やらは環境構築から躓いたり、Hosting で独自ドメイン使うのに DNSレコードってどんなんだっけ？って調べたり、とにかく自分の `知らない` とたくさん出会いました！
また、その `知らない` を学ぶのに好きに時間を割いて良いのも個人開発ならではだと思います！
（もともと Android/iOS でそれぞれネイティブアプリ作るつもりだったのですが、好きに時間割きすぎて、「時間ないっ！」てなってほぼ完成品の Android アプリ捨てて Webに切り替えたとかいう裏話があります。）

結婚式は信頼の置けるクローズドな環境で多くの方にアプリを触ってもらえる最高の場でした :tada: 
この記事を見て「自分もなんかやろーっ」て感じてくれる方がいたら幸いです！！