見出し画像

Kakeruの技術的な話

こんにちは、岩本カイドウです。最近豚肉がごろごろ入ったカレーを作ったら最高に美味しかったです。

前回はブラウザで動くホワイトボードのようなアプリ「Kakeru」について、簡単な紹介や作り始めたきっかけなどについてお話しました。
今日はKakeruがどんな技術を使って作られているか、というところを少しご紹介したいと思います。

先に申し上げておくと、KakeruのソースコードはすべてGitHubにて公開しています。ご興味があれば覗いてみてください。
https://github.com/odiak/draw
https://github.com/odiak/draw-server

フロントエンド

Kakeruのコードのほとんどはフロントエンドです。以前はバックエンドのコードもある程度ありましたが、途中からFirestoreを使うことにしたのでバックエンドのコードはほとんどなくなりました。

言語は、TypeScriptを使っています。僕はTypeScriptが大好きで、仕事でも趣味でも可能な限りJavaScriptではなくTypeScriptを使います。型があることでtypoや意図しないnullの出現などの細かいバグを減らすことができますし、型がドキュメントの役割も果たしてくれます。

UIの構築には、Reactを使っています。VueやAngularのようなフレームワーク(最近どこまでがフレームワークなのかよく分からないしReactはフレームワークではない気もしている)も少し使ったことがありますが、圧倒的にReactが好きです。TypeScriptフレンドリーで、設計に強い思想があり、使い手の自由を尊重している感じが好きです。

Reactというと、Reduxとセットで考えられることも多いですが、KakeruではReduxやFluxのような仕組みは使っていません。(ReduxやFluxはReactに必須なものではないのに、ちょっと勘違いされていることも多い気がしています。)
代わりに、副作用やコンポーネントをまたいだ状態管理を行うために、Angularのserviceクラスのようなものを用いています。シングルトンインスタンスを持つクラスに状態の変更や通信などを行うメソッドを生やし、RxのObservableのようなものを使って状態の変更などを通知しています。
まだコードベースが大きくないというのもあるかもしれませんが、このやり方は概ね成功している気がしています。

UIの構築にReactを使っていると書きましたが、絵を描くキャンバスの部分はどうなっているのでしょうか?
そこには、canvas要素が使われています。その中身はReactの管轄外で、独自の描画処理が行われています。というのも、マウスイベントやタッチイベントに応じて高速に線を描画するにはReactを使ってDOMを更新していては時間がかかりすぎるからです。

実は、Kakeruでも、最初はSVG要素を使うなどして線の描画を行っていました。その際、Angularを使ってみたこともあります。ですが、結果的になかなか良いパフォーマンスが出せませんでした。線を描くときのパフォーマンスは使い心地に直結するのでかなり重要な問題です。
あるときふと思い立って、canvasで線を描くシンプルなデモを別に作ってみました。すると、かなりスムーズに描くことができました。それをきっかけに、Kakeruの描画部分をcanvas要素に置き換えました。かなり大変な作業でしたが、結果的にとてもアプリが使いやすくなりました。

canvas要素を使って描画をしてはいるものの、データとしては線の情報を持っているので、画面をズームしたり、(まだ実装されていませんが)線を移動したりといった操作も可能です。もちろん高DPIのディスプレイ(devicePixelRatio > 1 の環境)にも対応しています。

描いた絵の情報(線の情報や、タイトルなど)は、Google Cloud Firestoreに保存しています。(以前はSocket.IOでNode.jsのサーバーと通信し、MongoDBに保存していたのですが、後ほど詳しくご紹介します。)
クライアント間でリアルタイムにデータを同期することができるので、共同編集機能が簡単に実装できています。
Firestoreのようなサービスは、以前はあまり興味が持てず「自分でバックエンド実装するのも簡単じゃん」などと思っているところがありました。ところが使ってみると、すごく楽なんですよね。冷凍食品とかレトルトのカレーみたいな感じです。すべてのケースで使えるわけではないですが、これで作れてしまうアプリやシステムは多いと思います。

最後に、フロントエンドのコードなどの静的なファイルをサーブする環境についてです。こちらはNetlifyを使っています。無料でも使える、主に静的なWebサイトを配信するサービスです。GitHubから自動でデプロイしてくれます(手動でも可能)し、自分のドメインを設定できたり Let's Encryptを使ってSSLの証明書を自動設定してくれたりもします。ときどきキャッシュが切れて少し読み込みが遅いことがありますが、概ね満足しています。(そのうち余裕ができたら有料のサービスにしても良いかも。)

バックエンド

以前はデータベースにMongoDBを使用し、Node.jsでSocket.IOを使用しフロントエンドと通信していました。ところがその後、スケーラビリティやメンテナンスの手間などを考えて、データとその同期をFirestoreに任せることにしました。1人でちまちま開発している都合上、フロントエンドの開発に集中したほうが良いだろうと思ったからです。

現在のバックエンドは、ユーザーが書いた絵をSVG画像として返すだけのものになっています。Node.js(+TypeScript)でExpressを使っています。キャッシュしたいなぁという気持ちもありつつ、とりあえずmax-age=600を設定して返しています。(でもScrapboxに貼ったりすることを考えると600秒は少し長いような気もしています。60くらいが適切でしょうか...?)

インフラとしては、Google Compute Engineを使用しています。今思うとApp Engineでも良かった気もしますが、当初使っていたHerokuから移行する際に、GCEから古いHerokuの環境にNginxを使ってプロキシすることでダウンタイムをなくしたという背景もありました。

開発環境

最後に、開発環境について。これはもちろんVS Codeです。VS CodeはTypeScriptの開発が非常にやりやすいです。僕は、TS以外でもほとんどのコードを書く場面でVS Codeを使っています。
以前はVimが好きで、ずっとVimを使っていたのですが、あるときにふと思い立ってVS Codeに乗り換えたところスッと手に馴染んだ感じがありました。もちろんVSCodeVimを入れています。

フロントエンド(またはJavaScript/TypeScript)の開発に欠かせないツールとしては、PrettierとESLintがあります。
Prettierは、コードを指定したスタイルにフォーマットしてくれるツールです。これが意外と便利でして、今まで整ったコードを書くことに意外と労力を使っていたんだと実感します。
ESLintは、設定したルールに基づいてコードをチェックしてくれるツールです。地味に便利です。最近だと、React hooksに関するルールに助けられる事が多いです。

まとめ

いかがでしたか? こんな感じでKakeruというWebアプリを作っています、という紹介でした。最近の規模が小さめのWebサービスだとよくありがちなスタイルなのかなとは思っています。

前回、「KakeruというWebアプリを作っている話」と題してアプリの紹介をしましたが、今回は技術的な話を長々と書いてしまいました。
よろしければ前回のお話も読んでみて、また https://kakeru.app のほうもぜひ使ってみてください。

それではまた〜👋


おまけ: 求人情報 🙋

わたくし、岩本 海童は現在、フルタイムのWebフロントエンドのお仕事を探しています。素敵なプロダクトを開発している and/or 優秀なエンジニアがいる会社でお仕事したいです。
Noteを読んで、こいつ面白そうだなと思ったらぜひTwitterなどからご連絡いただけると嬉しいです。友達の会社が人集めてるよ〜みたいな情報も大歓迎です!

この記事が気に入ったらサポートをしてみませんか?