はじめに
大学4回の情報系の学部生です!研究と並行しながらタイトル通りのものを作ったので紹介させてください!なおクオリティに関しては、ツッコミどころ満載のサンドバッグのようなものになるかもしれませんが、ご了承ください。
できるだけ簡潔に書いていきます!
どんなサイト?
アプリ名 「DevSearch」
Dev(開発物)をSearch(探す)という意味合いで、思いついて10秒ほどで名づけました。
技術記事(QiitaやZennなど)から個人開発物を紹介している記事のみをかき集め、まとめて閲覧できるようなサービスです。どのような機能があるかに関してはアプリの概要にて、紹介します!
アプリのURL
Githubリポジトリ
開発の目的と背景
目的
目的は以下の2点です。
①バック・フロント・インフラを1人でまるっと作る経験をしたかった(気持ち65%)
②単純にこのような物があれば良いなという願望があった(気持ち35%)
基本的には①の勉強目的が主な理由です。今に加えてフロントエンドのフレームワーク・インフラを新たに学び、全て1人で作ってみてデプロイまですることで、実際にどのような仕組みでアプリそのものが動いているかを体験することができると思いました。②に関しては、毎度何かをつくろうとするとき「何か良いアイデアないかな?」と様々な技術記事をあさっていたので、「一つにまとまってたら楽かも」という思いから作成しました。
背景(制作期間) 2023年 10月~2024年 1月(4か月)
「完璧を目指すより、まず終わらせる」を目標に、たとえ実装の内容がちんけなものになってしまいそうでもデプロイまでを絶対に「4か月」という期間以内に終わらせることにしました。Vue.jsとAWSの学習経験がなく、学びながら実装だったため、本当に終わらせれるかびくびくしました。
アプリの概要(使用技術やこだわった点など)
使用技術
- Vue.js・TailwindCSS(フロント)
- Django Rest Framework(バックエンドAPI)
- AWS(インフラ)
- qiita API (その他)
使用技術の理由
開発の目的に立ち返り、以下が理由になります。
フロント:「学習コストが低い」かつ「モダンな技術」を目的に選びました。
バックエンド:率直ですが、使えるフレームワークがDjangoだったのが理由です。
インフラ:「自分で1から構築」かつ「モダンな技術」を目的に選びました。
インフラ構成図
ec2内のwebサーバやDBなどは冗長化させた方がよいと思うのですが、無料枠の範囲で行うつもりでしたので以下のように行っています。
ER図
現在はZennとQiitaのみで外部apiとスクレイピングで最新の記事を取得し、cronでデータベースの入れ替えのジョブをスケジュールしています。
アプリ機能
-
ユーザ認証機能
- 新規登録
- ログイン
- ログアウト
-
メイン機能
- 記事一覧 (各サイトでいいねが多い順)
- 記事・タグ検索(あいまい検索)
- タグごとの記事表示
- いいね・ブックマーク機能 (認証ユーザのみ)
こだわった点
①無駄な機能を一切省いた
まず、記事を探す際にみんなはどのようなことを考えて探しているかを考え、それを前述したような機能で、解決しました。
-
チャットアプリを作りたい!他の人はどんな感じで作ってるんや
⇒ 記事のあいまい検索 -
そのフレームワークでどんなものが作られているか
⇒ タグ検索・タグごとの記事表示 -
この記事いいな、保存しときたい
⇒ いいね、ブックマーク
基本的には、上記の3点の機能のみがあればこのサイトを訪れる目的は達成できると思いました。よくあるのはこれらに加え、コメント機能やSNS共有機能です。これらの機能に関して「コメントをテキストで書く」と「共有をしに行く」ことをわざわざしにいくユーザは少ないんじゃないかと思ってしまったので省きました。正直いいね機能も省くか迷ったのですが、ボタンを押すだけで労力はそんなにかからないと思ったので取り入れました。
②最新かつ人気順に表示された記事
cronで記事のデータを一週間ごとに以下のようにスケジュールしているため、常に最新の記事をDevSearchでは扱っています!(皆さんが寝ている時間にしています。。)
# スケジューリング アップデート状況のログも出力
22 2 * * 3 cd /home/ubuntu/DevSearch/backend && /home/ubuntu/DevSearch/backend/venv/bin/python manage.py dbUpdate >> /home/ubuntu/DevSearch/backend/dbUpdate.log
また、記事の一覧表示は基本的に外部サイトでのいいね順に並んでいるため、評価が高い記事を常に見やすい位置に配置しています!
③RESTを意識した
勉強したものをすぐに取り入れたがる現象です。具体的に以下を考えました。
エンドポイントのurlのわかりやすさ
エンドポイントは以下になります。
-
User
/users ユーザ一覧
/users/pk/ ユーザー詳細
/users/pk/like/ ユーザがいいねした記事
/users/pk/bookmark/ ユーザがブックマークした記事 -
Post
/posts/ 投稿一覧
/posts/pk/ 投稿詳細
/posts/pk/likes/ 特定の投稿にいいねした人の一覧 -
Tag
/tags/ タグ一覧
/tags/pk/ タグ詳細
/tags/pk/posts/ 特定のタグの投稿一覧
外部のサイトの記事やタグを使うため、エンドポイントへのpost, put, patchのメソッドによる書き込み操作は不可にして読み取り専用にしています。
トークン認証
サーバーに状態保持をしないように、ユーザの認証はjwtによるトークン認証を採用しました。また、以下のサイトを参考にアクセストークンを自動で更新する処理も実装しました。
トークンの保存場所には、主にローカルストレージとクッキーがあります。
今回、アクセストークンとリフレッシュトークンはローカルストレージに保存しています。調べる限り、jwtの保存はjavascriptからのアクセスを拒否する設定をしてクッキーで管理するのがよいというのが基本みたいです。理由は、ローカルストレージへの保存は、XSSによる情報の盗難があるからです。ですが、別にローカルストレージでもいいんじゃね?というような記事や情報もあります。
そこで、本アプリでの状況を考えました。アプリ内では、
① ユーザが入力したものをユーザに共有されている状況がない
② 認証が必要なのは、いいねとブックマーク機能のみ
①に関して、そもそも本アプリでユーザ入力が必要な部分は新規登録とログインと検索ボックス部分です。まずこれら全ての入力欄はvue.js側でデータバインディングによるエスケープ処理が自動でなされているらしいです。また、コメント機能を省いていて、特定のユーザが入力したものが他のユーザに共有されている状況がないのでXSSの危険性は大幅に低くなっているかなと考えました。
②に関して、超万が一XSS起きてローカルストレージのトークンが盗まれたとしても、それを使って攻撃者ができることは被害者のいいねとブックマークを荒らすぐらいしかできず、えげつない被害になることはあまりないかなと考えました。
以上の点から、今回はローカルストレージにトークンを保存しています。(やり方が簡単だったというのもある)
苦労した点
Vue.jsとAWSは、ほぼ触ったことがなかったので学びながら実装していくのがしんどかったです。また、Djangoの細かいケースの対処法の記事があまりなく、ドキュメントも英語だったので問題にぶち当たった時の対処が難しかったです...。
おわりに
今後の課題
現段階で実装しきれていない機能は、
- タグのフォロー機能
- 認証周りの細かい機能 (パスワードリセットとか)
- adminユーザの管理機能
-
ログ管理
等々
フレームワークに依存しない知識やコード実装力やデータベースに関する知識が疎いと自覚しているのでそれらを勉強し、自分が今後勉強して広がった知見でこのアプリを改善し、育てていこうかなと考えています!
見にくかったかもしれませんが、最後まで見ていただきありがとうございました!!
参考記事・書籍・教材
- マスタリングTCP/IP
- サーバインフラエンジニアの基本がこれ一冊でしっかり身につく本