Vue初心者が「Clubhouseやってるぜ」マウント取れるアプリ作った

:skull_crossbones:注意
内容はないです
何かを期待して読むと必要以上に落胆してしまうのでご注意ください

# :star: はじめに
2月に入ってから急激に流行りだしたClubhouse。
何か裏で働いているのかというくらいにユーザー数の伸びがハンパないようです。

## :wave: Clubhouseとは
完全招待制の音声SNSです。モデレーターがするお話を自由に視聴できます。また、モデレーターが指定した人は配信する側として一時的に加わることもできます。ラジオの視聴者参加みたいな感じですね。

# :no_entry: Clubhouseが引き起こす問題
**完全招待制**ということで、一部の人からは「私、Clubhouse招待されていますよ🥴」とマウントに見えてしまうようです。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">クラブハウス始めました。のストーリーや投稿が友達いますマウントに見えてしまうのは私だけですか？捻くれですか？😂</p>&mdash; ふぉか🇨🇭8m (@Neko_swiss) <a href="https://twitter.com/Neko_swiss/status/1355216271802032137?ref_src=twsrc%5Etfw">January 29, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

これは精神衛生上よくない。

そんな悔しい気持ちを抱える多くの人のために
　　　**[Clubhouseっぽいプロフィールメーカー](https://ima-ge.work/)**
を作りました。


# :information_source: 何ができるのか
こんな感じで

<img src="https://docs.google.com/drawings/d/e/2PACX-1vR4j_ooAxBj5nmo68FfmJGqZ0TXMLjCvZbFuq9DcTqsM-TZpSdaNC9AXeacRywQ3IjVKRSeaWmWr78n/pub?w=480&amp;h=247">

- 画像
- 姓名
- ID

を入力すると

<img src="https://docs.google.com/drawings/d/e/2PACX-1vSsSwTWa0xKA9NjNbZXb2YRKqSct8RS11mShZDqGxSqPbs6hznHh4B0kTg8QGo0NbEHTxYhzHEVEJd-/pub?w=759&amp;h=689">

Clubhouseそっくりのプロフィールができちゃいます！
これなら本当はClubhouse誘われていなくても「私、やってます」マウントが取れますね :mountain: 

[こちら](https://ima-ge.work/)からサクッと試せます。

# :computer: どうやって作ったか
Qiitaは技術情報共有サービスですので、クソアプリの宣伝のためだけに使うのはよくありませんね。
## :keyboard: 使用技術
- Vue.js
- AWS
    - S3
    - Lambda
    - API Gateway
    - Route53
    - CloudFront

## :baby_tone1:  筆者の技術レベル
Pythonなら問題なく書ける。Flaskを使って簡単なWebアプリを作った経験あり。
最近モダンなフレームワークに興味を持ち始めた。
JavaScriptもVue.jsも最近勉強し始めたので見よう見まねでやっているが、あまりVue.jsの良さを生かせた開発はできない。

## :earth_asia:  全体図

<img src="https://docs.google.com/drawings/d/e/2PACX-1vSaDL_2uPjOPECPSK3k69TC06nZ57T-eI3a8XPmpdD-ecHcpG3OUYfdm8VVcn-2eSdfLtkLGAG3R3JZ/pub?w=958&amp;h=630">

このように
1. ユーザーが画像・姓名・IDを入力
2. Base64でエンコードした画像・姓名・IDをAWS API Gatewayに送信
3. PillowでClubhouseっぽい画像に加工
4. API Gatewayを返して送信

という流れで処理をしています。

## :raised_hand: Canvas使えばいいんじゃない？
と、多くの人が思ったのではないでしょうか？
私も最初はCanvasで色々処理して、それをPNG変換しようかと思いましたが
**「WebフォントがCanvasに反映されない」**
という問題が発生しました。

（この辺詳しい方いらっしゃいましたらコメントください :bow: ）

そのため、Lambdaを介してプロフィール画像生成を行っています。

# :beginner: Vue.jsを使うにあたって
はじめてVue.jsを使って開発をした筆者がぶち当たった課題がいくつかありました。
## :fire:  vue-routerのhistoryモードの弊害
ルーティング制御のためにvue-routerを使ったのですが、https://example.com/example に直接アクセスすると404 Not Foundになるということを知りました。
SSR（サーバーサイドレンダリング）も可能なようですが、今回はそこまで時間をかけたくなかったため**プリレンダリング**を用いました。
Vue.jsのプリレンダリングについては[こちら](https://www.ritolab.com/entry/182)のサイトが大変参考になりました。

## :tired_face: API Gatewayから返されたデータを表示できない
事前にdataの中に`image: null`を定義しておき、`axios`でAPI GatewayのURLにPOSTする形で画像処理を行っていましたが、なぜか**処理後の画像がHTMLに反映されない問題**
デバッグコンソールには`Uncaught (in promise) TypeError: Cannot set property 'image' of undefined`の文字が…。
そのときに書いていたコードがこちら。

```javascript

axios.post('https://example.execute-api.ap-example.amazonaws.com/example', {
          name: this.mei + ' ' + this.sei,
          userId: '@' + this.id,
          image: imgData
        }, {
          headers: {
            "Content-Type": "application/json"
          }
        })
        .then(function(response) {
          this.image = 'data:image/png;base64,' + response.data.image
        })
```

Vue.js上級者ならすぐわかるのかもしれませんが、初心者の私は1時間くらい溶かしました。

### 解決策
結果、こちらの記事に助けられました。

[Vue.jsのdataオブジェクトを参照する際に Uncaught (in promise) TypeError: Cannot set property 'foo' of undefined](https://qiita.com/shanonim/items/7718556c0fab54a517c2)

**thisへのreference用変数(self)**が必要なようです。
というわけで、正解はこうですね。

```diff_javascript

+ let self = this
axios.post('https://example.execute-api.ap-example.amazonaws.com/example', {
          name: this.mei + ' ' + this.sei,
          userId: '@' + this.id,
          image: imgData
        }, {
          headers: {
            "Content-Type": "application/json"
          }
        })
        .then(function(response) {
-           this.image = 'data:image/png;base64,' + response.data.image
+           self.image = 'data:image/png;base64,' + response.data.image
        })
```

また一つ賢くなりました。

# :pencil2: 編集後記
初めてのVue.jsとLambdaということでちょいちょい詰まりながらも大体2日（徹夜含む :night_with_stars: ）で完成しました。

Vue.jsはまだまだ初心者なので、「こうしたらいいよ！」などありましたら温かいコメントをくださいますと幸いです。

以上、最後までありがとうございました！

※筆者は2月2日にはClubhouse招待されています :musical_note: