ふりかえりに関するアプリを個人開発でやってみて、その失敗と成長のふりかえり
はじめに
こんにちは!突然ですが、みなさん「ふりかえり」は好きですか?僕は大好きです!
今回、ふりかえりに関する自作アプリを作ってみました。
この記事では自作アプリを作る過程での工夫や失敗、そこから得た学びをふりかえっていきます!
リンクはこちらなので、良かったらアクセスしてみてください〜!
👉 アプリはこちら
👉 GitHubリポジトリはこちら
なぜ作ったのか?
私がこのアプリを作った理由には、以下のようなモチベーションがありました。
- 技術力向上の1つの手段として、何かを作ってみたかった
- フロント、バックエンド、インフラを通したアプリケーションを作って、一通り理解したかった
- 自分自身のポートフォリオとして、公開できる何かを作りたかった
次に「何をテーマにしたアプリを作るか」を考えたとき、以下のような理由から「ふりかえり」を選びました。
- ふりかえりがすきという点
- 過去のふりかえりアプリを拡張できる何かを作りたかった
- 過去のアプリとは「ふりかえり手法をランダムに選択する」アプリです
- 詳細については、こちらの過去記事をご覧ください
- ふりかえりのファシリテーターのとき、どのふりかえり手法を選ぶか悩むときがあった
- なのでいろいろなふりかえり手法のコメントを登録できると、ファシリテーターが選ぶことを支援できて、面白そうかもと思った
というわけで、「ふりかえり手法をランダムに選択する」「ふりかえり手法ごとにコメントを登録できる」アプリを作りました!
技術選定
アーキテクチャー構成図
最終的なアーキテクチャー構成図はこちらです。
バックエンドとインフラ
バックエンドとインフラには、これまで使ったことのあるPython、Serverless Framework、Lambda、API Gatewayを採用しました。
フレームワークは、興味があったFastAPIを選びました。
データベース
正直かなり悩みました。「どのデータベースを使うか」だけでなく、「どこでデータベースサーバーを管理するか」も重要なポイントでした。
そのため、いくつかのサービスを調べて比較表を作成して検討しました。
- Lambdaとの接続のしやすさ
- コストの安さ
- セキュリティの確保が容易であること
- 情報量の豊富さ
これらの情報を重視し、結果としてVPC内のLightsailインスタンスにPostgresをインストールし、VPC内部のLambdaからアクセスする構成に決定しました。
当時作成したデータベースの比較表
余談ですが、当時ここまでしっかり比較表を作成して検討した自分自信を「えらいなあ!」としみじみ感じています 😂
フロントエンド
私はフロントエンドの経験がほとんどなく、どうしたものかと悩みました。
ただ、ReactとTypeScriptに興味があって学びたいという気持ちがあったこと、そして私の現場でもそれらを使っているため、ReactとTypeScriptを採用しました。
一方、ReactのフレームワークであるNext.jsは採用を見送りました。そのため、今回は基本的なReactとTypeScriptにフォーカスしました。
- Reactですら自信がない段階で、さらに学習コストを上げすぎるのは厳しそう
- SSRやSSGを使う予定もなくて、SPAで十分そう
認証認可
認証認可のライブラリ選定も、比較表を作って選定しました。この分野は、序盤の時点で不確実性が高いと感じたためです。
当時作成した認証認可ライブラリの比較表
「当時比較表まで作ってしっかり検討している自分えらいなあ!」(2回目)
はじめて使う技術の学び方
FastAPI、TypeScript、Reactはほとんど初挑戦だったため、いきなり実装に取りかかるのではなく、まずは学習用コンテンツを活用して基礎を学びました。
- FastAPIの公式ドキュメントを一通り読む
- サバイバルTypeScriptとりあクト!で手を動かしながら学ぶ
また、それ以外にも、ChatGPTやGithub Copilotのといった生成系AIを活用して、コードを生成しながら学びました。
工夫した点
アプリ名を付けた点
私はネーミングが得意ではないのですが、綺麗なデザインになっているのでデザインに合う名前を付けたかったので「グリーンレンズ」というアプリ名を付けました。
由来としては「ふりかえり → ふりかえる → カエルの目を通して世界を見る → 緑色のレンズを通して世界を見ている」という連想からきています。(若干無理やりかもしれませんが😂)
開発環境の作成
個人開発では、ローカル環境以外の開発環境は不要なケースが多いかもしれません。
しかし、ローカル環境だけでテストして、そのまま本番に出すのはちょっと抵抗がありました。これには、私自身がQAエンジニアとして働いていることも影響していますし、性格的に慎重な部分も関係していると思います。
そして、「万が一、インフラ環境(今回はAWS)との統合時に問題が起きたときに、いちいちrevertするのがめんどくさそうだな~」とか、「テストデータが本番環境に残さないために毎回消す作業がめんどくさそう」と思いました。
その為、今回は本番環境とローカル環境だけでなく、開発環境を作成しました。実現方法としては以下の通りです。
- 開発環境と本番環境に異なるドメインを適用
- CloudflareのZero Trustを使用して、開発環境へのアクセスを私だけに制限
バックエンドの工夫
各レイヤーの役割決め
以下のディレクトリ(レイヤー)を設計し、役割を決めて実装しました。 functions/とmodels/とrepository/とservices/を特に考えて決めました。
詳細はこちらのGithubへのリンクをご覧ください。
テストコードの作成
個人開発や、プロダクトの初期では要らない、という考え方もできるのですが、テストコードについてもっと慣れたいこと、テスト観点を網羅しつつ手動テストを減らすことを目指して、なるべくテストコードを書きました。
結果として125の数のテストを実装して、C0カバレッジ: 94%、C1カバレッジ: 92%と高い率にできました!
データベースのマイグレーションをCI/CDパイプラインで実現
マイグレーションを実行するには、Lightsail上のインスタンスにアクセスして、特定のコマンドを実行する必要があります。
あまりマイグレーションはやらないのですが、必要なときにその分どのタイミングでどのコマンドを実行するかを忘れてしまいがちでした。
そのため、ボタン1つでマイグレーションを実行できるように、このようなGithub Actionsでワークフローを作成しました!
フロントエンドの工夫
デザインの作成
私はデザインはほとんどわかりません!
知人にデザインが詳しい人がいて、その方にデザインをお願いしました。
そのデザインをもとに私がフロントエンドを実装しました。おかげで、すごくきれいなユーザーインターフェースになりました!
container/presentational パターンを適用
フロントエンドはほぼ初心者なのですが、初心者なりに学んだ知識を活かしたい気持ちがありました。
りあクト!を読んで、container/presentationalパターンは理解できたので、適用してみました。責務を分割することで、コードの可読性が上がったと感じることができました。
- containerコンポーネント:ロジックを管理
- presenterコンポーネント:見た目の部分を管理
学んだ内容を、少しでもすぐ活かせたのが良かったと捉えています!
キャッシュを使用
ふりかえり手法の詳細モーダルを開いたタイミングで、SWRを使ってふりかえり手法ごとコメント取得のGET APIをキャッシュしました。
そして、ふりかえり手法ごとにコメントの登録または削除しない限り、再度GET APIをコールしないようにしました。
これによって、不要なAPIコールを減らせることができました!
その他の工夫
他にもたくさん工夫した点はあるのですが、長くなって読みづらくなるのを避けるため、折り畳みのなかで簡単に紹介しています。
惜しくも折り畳みの中で紹介する「その他の工夫した点」
- 【バックエンド】テストの可読性を向上するために、RSpecのようにテストコードを書いた点
- 【バックエンド】テストコードでテスト対象をsutという変数名にしてテスト対象をわかりやすくした点。また、途中からなるべく上位クラスのfixutreでsutを定義することで、DRYなコードとした
- 【バックエンド】GithubActionsを使ってCI/CDパイプラインで自動テスト実行と自動デプロイを実現して、ビルドの安定性と効率化を図った点
- 【バックエンド】コメント登録時に管理者にLineメッセージを送ることで、金銭的コストを少なくしながら公序良俗に反するコメントを素早く検知できる点。AIサービスでも防ぐことはできるが金銭的コストがかかる想定
- 【バックエンド】大量リクエストが来ても課金されにくくするように、AWS API Gatewayのスロットリング設定に小さい数値を設定した点。WAFだけでも大量リクエストが来たときにブロックしてくれるので十分かもしれないが、一応念のため設定した
- 【フロントエンド】スクロールやボタンを押しただけで、不要な再レンダリングが走らないように多く適切にmemorizeした点
頑張らなかった点(諦めてスコープ外にした点)
アプリのゴールを個人の学習やポートフォリオ用に割り切った
このアプリを開発し始めたときは、pdf版のふりかえりカタログがあり、 コメントを登録する`というのはpdfではできないことなので、差別化や補完できました。この時点では、多くの人に使ってもらえる可能性があると、どこかで暗黙的に思っていました。
しかし、開発中にmiro版のふりかえりカタログがでて、miroでコメント登録ができるのでユーザーはこちらを使うべき と気付きました。
気付いたときには開発を続けるかどうか結構悩んだのですが、すごく成長させるアプリケーションを目指しているわけでもないので、個人の学習用とポートフォリオ用と割り切って開発を進めていきました。
認証の脆さ
このアプリでは、usernameとpasswordで認証しています。
もともと、メールアドレスとパスワードで認証する予定で、usernameを使用せずに認証する予定だったのですが、個人情報としてメールアドレスを保持したくなくて、usernameとpasswordで認証するように仕様変更しました。
そして、コメントを登録したとき、コメントのデータとして登録したusernameを表示する仕様です。
お気づきだと思いますが、認証の片方の情報を取得できる状態になってしまっています。セキュリティとしては良くない状況です。
仕様変更の考慮漏れに気付いたのが結構終盤で、よくはないけども、一方で個人情報なども管理してないためセキュリティの懸念は妥協範囲かな、と思い修正はスコープ外としました。
その他
まだ他にも頑張らなかった点(諦めてスコープ外にした点)はあるのですが、折り畳みのなかで簡単に紹介していきます。
ブログにすべて書くことを頑張らなかった、「頑張らなかった点(諦めてスコープ外にした点)」
- 【バックエンド以外の自動テスト】フロントエンドのテストコード、AWS環境を使用した状態のAPIテストとE2Eテストの自動化。最初はやる気があって、「すべてテスト自動化してやるぞ!」という気持ちだったのですが、だんだんフロントエンドの実装だけで大変に思えてきたので諦めた
- 【フロントエンド】MUIの依存度が高いこと。抽象化してないため他のライブラリへの置換えは作業が多くなりそう。他に移行する機会もないと思うので大丈夫のはず
- 【バックエンド】テストコードの設計の方針。共通化など、都度場当たり的に考えてしまっていた
- 【フロントエンド】詳しい設計。例えば、propsが12以上など以上に多いコンポーネントがあったり、特に見た目の部分(cssのプロパティの様な部分)はGithub copilotに聞いて解決したので、個別最適で統一された設計にはなっていない
- 【フロントエンド】適切なmemorize。途中まで頑張っていたが、終盤めんどくさくなって、ボタン操作時に不要なレンダリングが走る部分がある
- 【フロントエンド/バックエンド】ユーザー情報の変更とユーザー削除
- 【バックエンド】ログ出力。あとでやろうと思いつつ、めんどくさくなってやってない
- 【インフラ】AWS WAFの必要性。CloudflareのWAFだけで良いとも思っているが、とりあえず「えいや」で適用している
学びになった点
VPC内にLambdaを設置したことによる弊害
VPC内にLambdaを設置すると、外部ネットワークへの接続が制限されるため、外部APIやAWSのサービスに直接アクセスできなくなります。具体的には、以下のような問題に直面しました。
- AWS Systems Manager(SSM)にアクセスできない
LambdaをVPC内に配置すると、AWSのサービスへのアクセスも制限されます。
当初、SSMに保存されたSecure stringを利用してデータベース接続情報を参照・取得する予定でしたが、VPC内LambdaではSSMにアクセスできないため、この方法は断念しました。 - LINE APIに接続できない
VPC内のLambdaから外部ネットワークへの接続ができないため、外部サービスであるLINE APIにアクセスできませんでした。この制限により、API連携の実装が複雑になりました。
NATゲートウェイやVPCエンドポイントを使用すれば外部ネットワークと接続できるのですが、個人開発では費用が高いので使用することは困難です。それ以外の方法として、以下を実施しました。
- 「AWS Systems Manager(SSM)にアクセスできない」問題の解決策
Serverless Frameworkを使用して、デプロイ時にSSMから情報取得し、その体をLambdaの環境変数に設定する方法に変更しました。
ただし、Lambdaの環境変数は平文で保存されるため、アクセス権を持っていればだれでも閲覧可能です。最善策ではないことは理解しつつ、許容範囲としました。 - 「LINE APIに接続できない」問題の解決策
どのようにLINE APIをコールするか、すごく悩みました。
まず思い付いたのは、VPC外LambdaでPOSTデータを受け取る → StepfucntionでVPC内Lambda呼び出し、VPC内Lambdaでデータベース処理を完了し、VPC内Lambdaが終わるまでポーリングする → ポーリング後にLINE APIをコールする、といったものでした。
しかし、実装してみると、VPC外LambdaでPOSTデータを受け取る際に自前の認証処理が必要であることに気付き、認証処理はデータベースにアクセスする必要があります。そのため、この方法は頓挫[1]しました。
次に思い付いた方法として、Cloudwatchのサブスクリプションフィルタを使用して、POSTデータがログに書かれたときに、VPC外Lambdaがトリガーされ、LINE APIを呼び出すものです。最終的にはこちらの方法を採用しました。
具体的なコード:serverless.yml、line_message.py
VPC内にLambdaを配置するだけでかなり苦戦したので、今後VPC内にするかどうかは今後よく考えた方が良いな、と学びになりました。
Oauth2を使った認証周り
このような流れでトークンを使用して認証しています。
このような流れ
ログイン時のシーケンス図
リフレッシュトークン発行時のシーケンス図
AccessTokenが必要なAPIでの処理の時のシーケンス図
実務では認証を作成することは少なく、すでに認証機能が出来上がった状態から携わることが多かったため、今回特にトークンの管理方法について学びになりました!
オブジェクト指向プログラミング
よりオブジェクト指向を理解できたのかな、と捉えています。ここはcompositパターン、ここはtemplate methodパターン、ここはobserverパターンが良いかも、と考えられたと思います。
例えば、ここでObserverパターンを使用しました。
何をしたかったか、というと「ログインしているかどうかのbooleanの状態でどこでも取得できるようにしつつ、ログイン状態が変わったときに、各コンポーネントで即座にbooleanが変わるようにしたかった」です。
状態が変わったことをObserverが観察する、ということで実現できそうだと思い、適用しました[2]。
Reactが関数型言語を推奨しているにもかかわらず、Reactを使用している部分でオブジェクト指向ぽく書いたのはどうなのかという気持ちもあるのですが...学びになったのでヨシ!としてます。
フロントエンド
ふんわ~りとですが、Reactの知識を得ることができました。それによって、現場でReactのコードが以前よりも読めるようになったことに気付きました!
presenterやcontainer、memorize、propsといった単語の意味を理解したり、loopのときはコンポーネントにkeyをつける必要があることや、状態の変数がどれなのかを理解できたからかな~と捉えています。
ドメイン
今までドメインに携わることが無かったのでふんわりとしか理解していませんでした。
今回ドメインを買うという実績を解放したことで、TLDの種類の多さ、TLDごとのDNS浸透などの違い、DNSのレコードの意味合い(Aレコード、CNAMEレコード)、ドメイン買っただけですぐ海外からたくさんの脆弱性への攻撃が来ること、などを学びました!
作成に時間が掛かる
とにかく時間がかかりました...
3か月くらいでササッと終わると思ってた。いろいろあって1年半~2年くらいかかりました。超概算で合計600ー700時間くらいかかってそうでした。見積もりが甘かったです...
時間が掛かった理由として思い当たる節としては、このようなことだとと思っています。
- バックエンドのテストコードをしっかり作成したから
- バックエンドの設計をしっかり考えたから
- 認証認可のトークンの流れに沿ってバックエンドとフロントエンドを実装したから
- FastAPI、Typescript、Reactなどの書き方がわからずによく調べてたから
学びとしては、改めて「たったこれだけの機能/画面だからすぐ作れるでしょ」と思わないことです。もちろん実務でも。見た目は少なくても、裏側ではかなり考え込んでいるかもしれないですね。
おわりに
長い時間をかけて取り組んだ分、色々な出来事や思い出が積み重なり、それに比例してこのブログも少し長めになってしまいました。
苦戦する場面も多々あり、ときには悩みながら進める中で、「学ぶことの楽しさ」や「自分で作り上げる達成感」を再確認する良い機会にもなりました。ひとまず無事に完成させることができ、今は清々しい気分です!
そして、私1人だけで作りきれたものではありません、アイデア出しを手伝ってくれた方、デザインを手伝ってくれた方、開発を手伝ってくれた方がいてくれたおかげで、何とか形にできました。この場を借りて感謝を伝えたいです。
私自身は「"品質"と"技術"を掛け合わせた領域でプロダクト価値を向上する」を目指しています。その姿を目指すための1つとしてこの経験を糧にして、さらに成長していけるように頑張っていきます!
Discussion