任意の人は質問箱を作るべき

この記事は CAMPHOR- Advent Calendar 2019 1日目の記事です.

こんにちは,あたらんです.

先日自分専用の質問箱を作って,(Web関連の技術を学びたい人や新しい技術を試してみたい)任意の人は質問箱クローンを作ると良いと思ったのでその知見を共有します.

質問箱クローンを作った

質問箱クローンを作りました.良かったら質問してみてください!

f:id:satoarata:20191129000129p:plain

geing.ataran.me

フロントはReact, バックエンドはGoで書いています.

機能

実装した機能は,

  • 質問を投稿
  • 質問をbasic認証でログインして回答
  • 質問一覧を表示
  • 質問
  • 回答個別ページ
  • 質問画像の生成
  • 質問画像をOGPに設定

です.

Twitterの自動投稿,新しい質問の通知も実装したかったですが,一旦モチベが燃え尽きたのでまだ実装してないです.

構成

構成はこんな感じです.

geing

作った経緯

主なモチベとしてはフロントやクラウドで完結するものばかり作っていてAPIサーバーなどを1から作ったことがなかったので,簡単なWebサービスを1から作ってみたいと思っていました.

そこで,機能要件的にシンプルでAPIサーバーの設計や実装,インフラまわり (Dockerやリバースプロキシ,VPSなど) に入門するのに丁度いいということで質問箱を作ってみることにしました.

GoとReactを選んだのは

  • フロントはVueしか触ったことがなくてReactも入門してみたかった
  • Goが周りで流行っているので触ってみたかった

というのが主な理由です.

また,CAMPHOR- のOBの @genya0407 さんが以前Rustで質問箱クローンを作っていて結構参考にさせていただきました.🙏

任意の人が質問箱を作るべき理由

タイトルにもある通り,任意の人が質問箱を作るべき理由を述べたいと思います.任意の人というのは少々大げさですが,僕みたいに新しい技術に入門してみたい任意の人に質問箱は色々と丁度良いです.

理由1: 機能要件がシンプルである

質問箱は機能がシンプルなので,作ることでWebサービス開発を無理なく網羅的に学べると思います.

新しい技術を試してみたいときに丁度良いです.

理由2: まあまあ使ってもらえる

本家質問箱でまあまあ質問が来る人は,自分で作ってもまあまあ質問が来ます.Webサービスを作っている途中で「これ本当に需要あるのか…?」とか考えてモチベが消える心配がありません.

実際に学べた技術

僕の作った質問箱で学べた技術を具体的に挙げます.

フロントのフレームワーク

Reactに入門できました.質問のリストを表示するトップページと,個別の質問と回答を表示するサブページという構成なので,コンポーネント,SPAなどの概念を学ぶのに丁度いいと思います.

APIサーバーの設計

APIサーバーの設計 (ディレクトリの分け方など) に気を使ってみました.具体的には,Goで書いているのでGoらしい(?)ボトムアップでの設計を意識してみました.ここで言うボトムアップというのはDDDやクリーンアーキテクチャのように現実をモデル化して設計するのではなく,システムを構成するコードの断片があって,それらを組み合わせてシステム全体を作っていくという意味です.

この話題に関してこれ以上深入りするとマサカリが飛んできそうなのでこの辺にしておきます.

この辺はCAMPHOR- の先輩に色々教えていただきました.

DBの設計,操作

このアプリのDBは設計するというほど複雑ではないですが,質問と回答のテーブルは分けたほうが良さそうだったという知見は得られました.(後述)

Goは高機能なORMがあまりなさそうなので,SQLを書く練習にもなります.

ページネーションを実装すると,SQLのOFFSET句はid1から順番に見ていくのでO(n)で重いという知見なども得られました.

インフラ

インフラ周りを全然触ったことがなかったので,クラウドに頼らずにVPSでホストしてみました.リバースプロキシやDockerに入門できました.Dockerまじで楽.

CI/CD

CircleCIに使ってみた.デプロイがすごく楽だった.

CORS

CORSめっちゃハマった.

その他の細かい学び

動的ルーティングの動的なOGPの対応

SPAで動的ルーティングの動的なOGPに対応させるのは地味にめんどくさいです.

どういうことかと言うと,何も考えずにSPAを静的にビルドすると個別回答ページなどのAPIサーバーに依存した動的なページは生成されません.Twitterなどは基本的にOGPイメージをクロールするときJavaScriptを実行してくれないので,ビルド時に静的なページを生成しておくか,SSRにする必要があります.

今回は新しい回答が追加されるタイミングでAPIサーバーでNetlifyのビルド用Webフックを叩いて,静的なページを生成しています.

ページネーション

ページネーションを実装するのも地味にめんどくさいです.

トップページには回答済みの質問一覧が表示されるのですが,質問は質問にauto incrementなidで管理しているので,ページをクエリとして10件ずつ取ってきたい場合に何も考えず

WHERE id < page * 10 
~
LIMIT 10

とかするとうまくいきません.

nページ目を取ってくるのを素直に実装しようと思うと質問id1から ページ*10 件分の回答済みの質問をいちいち確認していく必要があります.

しかし,「n件目を表示したい」という需要はあまりないと思ったので,今回は最新の質問から順に取ってくる形式にしました.(infinite scroll的な)

フロント側で現在持っている最も過去の質問idをオフセットとして保持しておき,それをクエリとして

WHERE id < offset
~
LIMIT 10

このようなSQLを叩きます.

この点に関しては,そもそも質問と回答を1つのテーブルで管理している時点でミスった感もあります.

まとめ

  • 質問箱はシンプルなので新しい技術を学ぶのに丁度良い.
  • 需要も一定数あるのでモチベが続きやすい

みなさんも新しい技術を学ぶきっかけに質問箱を作ってみてはいかがでしょうか.