🫠

個人開発のタイピングソフト『HiggsTyping』をNext.jsで作り直した話。

2023/05/27に公開

Webサービスについて

5か月前にリリースしたHiggsTypingというタイピングソフトです。

当時中2ながらかなり頑張って作ったのですが、バグや脆弱性が半端ないので作り直した方が早いと考え、新しい技術で作り直しました。実はHiggsTypingは初リリースのWebサービスです。

https://higgs.boson.jp

紹介

ざっくりとHiggsTypingの機能を解説します。

Colosseum

CPUと戦うやつです。0.5秒毎に相手のスコアが増えるようになってます。

ティアがあってRuby、Silverのようになっています。

ある程度高いスコアを出せば1ティアスキップすることができます。

Battle

オンライン対戦です。

ロビー機能があって、挑戦を送ることができ、挑戦を承諾したら戦いが始まるような方式です。

この方式はdefly.ioというゲームから着想を得ました。

その他

タイピング時はEscを押すと戻れます。

ColosseumならC、BattleならBといった具合にショートカットキーが割り振られています。

発生した問題

  • useStateを使わずinnerHTMLで無理やり実装した事による脆弱性
  • セキュリティ等にNoSQLだと穴が開いてしまう

技術スタックの違い

Firebase中心からSupabase中心に乗り換えました。

旧技術

  • Firebase
  • React(JS)

一番初心者でもとっつきやすそうだったのでこの技術にしています。実際使いやすかったし、勉強にはなりました。

しかし、5か月たった今から考えると「どうせならもっといい技術で作ろう」と思い、技術を新調しました。

新技術

  • Supabase
  • Next(TS)
  • Vercel
  • Sass(module)
  • ChatGPT
  • GitHub

今回のサービスは間違いなくSupabaseのRDBの方が良いのでSupabase、NextとVercelはCI/CL環境が整えやすかったのと、ReactよりNextの方が良いと思いこれにしました。

TSは心配性の俺の好みです。GitHubでコミットすればVercelでデプロイされる感じです。

Tailwind CSSも好きなのですが、SASSをmodule化した方が使いやすそうだと思ったのでこれにしました。

ChatGPTは最初は全然使う気なかったんですが、コードを渡すだけですぐ原因を理解してくれるため、非常に使いやすく、「いったん詰まったらGoogleよりChatGPTに聞いてみよう」 という結論に至りました。

また、GitHub Copilotを搭載したのはつい最近なので技術スタックに入れていません。

苦労したこと・反省点

かなり苦労しました。一個ずつ紹介しますね。

コードが汚い

とりあえず動けばいいや理論でやっていたので汚いです。初めてJSやcssを触ったわけではなかったのですが、やはり5カ月たった今見てみると汚い。

cssをscssで改修するだけでかなりの時間を費やすことになりました。

コメント書いてない

これは意外でした。

コード書いた当初は簡単に理解できていたのですが、コメント書いてないと関数が何をしているのか理解するのに時間を割くことになりました。

これは俺の技術不足もあるとは思いますが、ちょっとでも関数の前にコメントがあると理解の補助になるので書いておくとよいかと思います。

成長すれば他人 だと思って他人がみて分かり易いコードを描くこと大事ですね。

仕組み自体が良くない

これは作った当時は最高だと思ってたんですが、作り直してる時に見ると最悪だったパターンですね。

良くなかった仕組みは具体的には以下の通りです。

localStorageの使い方

環境設定などならまだいいのですが、ユーザーが変更してはいけないデータを堂々と

localStorageに保存していて、改ざんし放題だったので改良しました。

また、localStorageの変更がページと同期する仕組みも作っていなかったため、ここも改良しました。

addEventListener

これはChatGPTに聞いて分かったのですが、useEffectのreturn文の中にremoveEventListenerを挟まないとaddEventListenerが2回動作するというおかしな状態になってしまうらしいです。

localStorageの変更を検知する

// EventListenerを設定
useEffect(() => {
  function handleStorage(event: StorageEvent) {
    // 処理を記述
  }
  
  window.addEventListener("storage", handleStorage);
  return () => {
    // ページを離れたら
    window.removeEventListener("storage", handleStorage);
  }
}, [])

localStorage変更したとき

localStorage.setItem("example", "example");
window.dispatchEvent(new Event("storage"))

これはよくわからないのですが、localStorageの変更が検知されないらしく、ディスパッチをしないといけないようです。

対戦機能

ここら辺は前作ったときはオンライン関係のことを超絶てけとうに作ってたのが仇となりました。

大体以下の通りです

  1. なぜかplayerとuidを別々の配列で管理している。
  2. そもそも退出したときという判定がガバガバ
  3. 全てデータベースに書き込んでる。

はい、おかしいですね。1番に関しては完璧に俺のミスで普通にすぐ修正したのですが、2番と3番に関しては苦労しました。

2番と3番についてはsupabaseのpresenceとなるものを利用しています。

解説している記事が途轍もなく少ないため大変でした。

少し解説すると、presenceにはtrackという仕組みがあり、自分のデータをtrackすると、接続が切れる(またはuntrackされる)時に自動的にtrackしたデータを消してくれるようになってます。

詳しくは公式ドキュメントから見てください。

時間の問題

流行り始めてからユーザーはそんなに待ってはくれません。

今回の場合は春休みでちょうど時間が空いててよかったのですが、毎回そういうわけにはいかないです。

流行り始めたころにはユーザーとの対応などが運営に含まれるため、時間が足りないことがあります。その間にユーザー離脱されたらたまったもんじゃありません。

作り直ししなくていい が一番良いのでサービスは適度にメンテナンスを挟むようにすることをお勧めします。

よかったこと

早めのベータテスト

多少機能は少なくともDiscordコミュニティ等でベータテストを設けるとバグの早期発見ができ非常に良かったです。(協力してくれた方々誠にありがとうございます!!)

個人開発のような責任の少ない開発ならベータテストを早めにやってみるのもいいと思います。

最後に

最後まで読んでいただきありがとうございました。
いいねしていってくれると筆者が泣いて喜びます。
Happy Hacking‼

Discussion