推しアイデア
スクショ撮影・画面録画を、Rustを使って高速に処理できる!!
スクショ撮影・画面録画を、Rustを使って高速に処理できる!!
類似アプリは有料のため、オリジナルの機能を入れたい!!
「ラスト」というテーマに合わせて、Rustを使ったこと!!
Webサイトを美しく魅せるデスクトップアプリ。
このアプリは「ポートレート機能」と「動画撮影機能」を提供します。
Next.jsをベースに構築しています。
フロントエンド側は、最初にBoltを使用してサイトのプロトタイプを作成しました。一方で、Boltの出力ではコンポーネントの分割が不十分であったり、意図しないクライアントコンポーネント化が発生し、後からコードを改善・拡張する際に問題が生じる状態でした。そこで、Storybookを導入し、Atomic Designをもとにディレクトリ構成やコンポーネント設計を全面的に見直しました。
さらに、shadcn/uiを拡張したMagic UIを活用し、全体のUIデザインのブラッシュアップを行ったほか、ダークモードと多言語対応も実装しました。ただ、Next.jsのRSCで動的に言語を切り替える仕組みをStorybook側で再現するのが予想以上に複雑で、4時間ほど費やしてしまいました(実際はやる必要があったか微妙なところでしたが……)。
Chromaticにデプロイしました👇 https://67df1188f395761ad4884e5c-oqzryofyob.chromatic.com/
バックエンド側では認証処理と、Stripeの決済完了を通知するWebhookを除き、全てのAPIをHonoで構築しています。これは独立したサーバーとして立てているわけではなく、Next.jsのAPI Routesにマウントする形で利用しています。この手法により、Zodを用いたバリデーションやドキュメント生成が簡単に行えるほか、HonoのRPC機能を活用して型定義をフロントエンドとサーバー間で共有できるようになっています。 認証には、従来ハッカソン等でよく利用していたAuth.js(旧NextAuth.js)ではフレームワークや認証方法に制約があるため、より柔軟なBetter Authを採用しました。ORMについても、普段使い慣れているPrismaではなく、独自の記法が少なく型定義をリアルタイムで生成できるDrizzleを使用しています。
Tauriをベースに構築しています。
フロントエンド側は主に2つのタスクを担当しています。Tauriが提供するAPIを利用して画像・動画のダウンロード機能やメニューバーへのアイコン表示を実装すること、そしてFabric.jsを用いた画像・動画の編集画面を作成することです。これらのタスクを処理する過程で発生した辛みを3つ紹介します。
・ GPTがTauri1.0のコードしか出力してこない
最初はAIが次々とコードを生成してくれる様子を見て「意外といけるな」と思っていました。しかし、最近Tauriのバージョンが2系統にアップデートされており、GPTはこれに全く対応できていないことが判明しました。そのため、GPTが生成した1系統のコードを、ひたすら2系統に書き換える作業に追われることになりました。
・Permissonの記述がドキュメントにない
Tauriでは、OSのさまざまな機能を利用するために権限の設定をする必要があります。この設定自体はシンプルで、default.json
に必要な機能を書くだけなのですが、公式ドキュメントには具体的にどの機能にどの権限が必要なのかという情報が記載されておらず、自分でエディタの予測機能を頼りに適切な設定を探るという手探りの作業が必要でした💦
・Safariのv16.1ではTailwind CSS v4が一部機能しない
この原因は、Tailwind CSS v4から新しくサポートされた色空間OKLCHにありました。どうやら、Safariのバージョン16.1ではこのOKLCHがサポートされていないらしく、Mac OSをアップデートするか、Tailwind CSSのバージョンを下げるかの二択で悩みました。結局、バージョンをv3へダウングレードし、サイト側はv4、アプリ側はv3という状況になってしまいました。
バックエンド側はRustで実装しており、大きく2つの機能を提供しています:「画面録画」と「カーソルの座標取得」です。
・画面録画
画面録画にはFFmpegを使用しました。当初、適切な画面収録用のライブラリを見つけられなかったため、FFmpegのバイナリをプロジェクトにダウンロードしCLIツールとして実行する形で実装しました。(後になって、FFmpeg-sysのようなFFIを利用する方法があることを知りました。)録画した動画は/tmp/以下にタイムスタンプ付きのファイル名で保存し、そのファイルパスをフロントエンド側に返すことで、プレビュー機能を提供しています。
・カーソルの座標取得
カーソル座標の取得には、rdevクレートを使用しました。ただし、このクレートのlistenメソッドで座標を取得する処理はブロッキングであり、プロセスを終了させる以外に停止させる方法がありませんでした。 そこで、座標取得のコードを別の実行ファイルとして分離し、それをプロセスとして起動する形で対応しました。取得した座標データは標準出力から受け取り、整形したうえでフロントエンド側に返しています。
インフラ構築では、LPサイトのデプロイを目的としてECSを中心としたアーキテクチャを採用し、インスタンスにはFargateを使用しました。HTTPS接続を有効にするため、Route53とACMを利用しています。データベースにはRDSを採用し、DBMSとしてはPostgreSQLを用いています。RDSへはECS内で動作するアプリケーションから接続します。
Fargateのみではアクセスログやエラーログを確認できず、デプロイ失敗時の原因特定が困難でした。そのため、CloudWatchを導入し、ECS内のコンテナのログをCloudWatchへ転送することで、コンテナの状態監視とログ管理を可能にしました。
RDS、Route53、ACM以外のAWSリソースはTerraformを使って構築しています。また、複数人でインフラを管理していたため、環境の差異がインフラに影響を与えないよう、Terraformの状態管理ファイル(tfstate)をS3に格納し、共有管理できるようにしました。