Skip to content

Latest commit

 

History

History
628 lines (364 loc) · 29.6 KB

README.md

File metadata and controls

628 lines (364 loc) · 29.6 KB

🐈️作成の際に検討したこと🐈

作成の際に検討したことや苦労話を書いておきます。

昔から、人がプログラムでなにかを作っているのをみて、彼らはなにも苦労せず作成できているように感じていました(絶対そんなこと無いと思うんですけどね。皆さんそれぞれ苦労しながら作成していると思うので。誤解による所産です)。

愚かな身の感想として、「みんなぐっとガッツポーズしただけで作品ができている」様に感じていた、というわけです。

しかし、自分もいろいろ作るようになり、さらに長じて人の苦労を知ると、「裏で苦労している部分はあるが、表に出していないだけ」というのが分かってきました。

分かってはきましたが、その苦労したところ、それから作成する過程をぜひとも知りたかったんですね。

どう悩んで作ってきたのか、悩んで止まったときはどの様に解決しているのか、等々...

「作ります」 -> 「できました」の間の飛躍の中身が知りたかったというわけです。

そういった思いがあったんですが、そういった物を記載するかどうかは個人の自由です。

書かれることを期待することがナンセンスだったのだと、いまでは思っています。

しかし、人に期待していたのだから、自分はせめてなにか作成過程を残すかなーと思って書いたのが、本文書になります。

参考にするなり、読んで笑うなりしてもらえればよいかと思います。

文章稚拙なため、参考にならない場合は申し訳ない。

なお、今回作成したものがRecursionCSのバックエンドプロジェクトの課題の一つのため、RecursionCSをやっているユーザーには多少でも参考になるところがあるかもしれません。


目次




要件

今回作成したものの要件は以下になります。

機能要件

  • スニペットのアップロード
    • テキストエリアにテキストやコードを貼り付ける
    • プログラミング言語ごとに構文ハイライトする
    • ユーザーが内容を送信すると、スニペット用のURL(一意)が生成される
      • フォーマットはhttps://{domain}/{path}/{unique-string}
  • スニペットの閲覧
    • 一意のURLにアクセスしてスニペットを閲覧できる
    • コードハイライトあり
  • スニペットの有効期限設定
    • スニペットの有効期限が設定できる(例: 10分、1時間、1日、永続)
    • 期限切れになったスニペットは自動的に削除され、「Expired Snippet」とメッセージを表示する
  • データストレージ
    • ユーザーからの送信されるデータは、厳格に検証とサニタイズが行われる必要がある
    • SQLインジェクションを防ぐために、スニペットは安全に保存される
  • フロントエンドインタフェース
    • シンプルで使いやすいインタフェース
    • スニペット成功して送信されると、内容にアクセスできる一意のURLが生成され、ユーザーに表示する
  • エラーハンドリング
    • 大量のテキストやコード、または、サポートされていない文字が送信された場合でも、適切に処理してエラーメッセージを表示する

技術要件

  • ウェブインタフェース
    • HTML/CSSを利用
    • JavaScriptも使用
    • monacoエディタを使用
  • バックエンド
    • サーバーサイドは静的型付けが可能なOOPの言語。例ではPHP8.0
    • 一意のURL生成にはhash()のようなハッシュ関数を用いる
  • データベース
    • MySQLを使用
  • ミドルウェア
    • マイグレーション管理システムを使用
    • DBとの接続にMySQLWrapperを使用する

非機能要件

  • デプロイメント
    • サービスはユーザーが簡単に記憶できるドメインやサブドメインで公開する必要がある
    • サービスが常に利用可能となるようにする(使えない時間を極力少なくする)
    • Gitコマンドを実行するだけで、コードの更新と同期が自動で行える必要がある
  • パフォーマンス
    • スニペットを効率よく取得して、ユーザーが迅速に閲覧できるようにする必要がある
    • ページの読み込みが極端に遅くなることなく、速やかに構文ハイライトを表示できるようにする
  • スケーラビリティ
    • 大量のスニペットが送信されても、それらをスムーズに処理できるシステムを確立する必要がある
  • セキュリティ
    • スニペットが安全に保存され、不正アクセスを防ぐ必要がある
    • 安全な接続とデータの暗号化を保証するために、HTTPSを採用する必要がある

要件は以上ですが、すべての要件は満たしていません。

言語を変えたり、意図的によりよいと思ったものに変更したものがあるためです。

故に、課題としては百点満点で実装できたわけではないことを、ここに明言しておきます。

今見返すと取りこぼしたところもありそうだしね...(目逸らし)




全体的なこと

さて、ここから苦労した点や作成時の考え方などについて記載します。


言語の選定

本来、バックエンドプロジェクトで想定されているのは以下のものでした。

  • php
  • JavaScript

ただ、プロジェクト側で静的型付け言語であればいいといった記載があったため(あったかな? ...あったと思う)、趣味で以下の言語を選択しました。

バックエンドはRust。 趣味で選定。

本来はバックエンドプロジェクトおわるまではRustの学習は我慢する予定でしたが、バックエンドプロジェクト自体が時間がかかる内容であることもあり、我慢できずRustを使用することにしました。 というよりも、「やりたい言語があるのに自分を縛り続けるのも意味がわからないな」といったところが実情でしょうか。 どうせ苦労するなら好きな言語使っちまえ、ということですね。

やっぱり好きなものやるほうが健康に良いですね。

しかし、Rustを選定することで苦労が生じました。それについては後述します。

フロントエンドはTypeScript + Reactを使っています。

こちらはもともとReactプロジェクト等で勉強していたこと、それからjsx記法、コンポーネント化の機能を気に入っていたため使用しています。 一つ前の静的サイトを作成する課題で使用していたので、そのまま続投した形です。

なお、その時作ったサイトはこちらになります。

言語を変更したことによって生じた作業

Rustを選定したことによって生じた苦労について。

もともとはphpで進めるプロジェクトであり、学習中のサンプルコードももちろんphpで書かれていました。 しかし、Rustを選んだことで、そのサンプルは使えません。 そのため、サンプルコードもRustで自力実装する必要が生じました。

しかも、Rustとphpの言語の特徴の差異のため、この自力実装にかなり悩まされました。 何に悩まされたかというと、

  • サンプルコードはphpによりOOPを使用したコードでした。RustはOOPも実装は可能ですが、せっかくならよりRustっぽく実装したいと思っていたので、Rustのパラダイムに変換しながら実装する必要が生じました。これは自分で背負った苦労です。
  • サンプルコードはphpのスクリプト言語の側面を最大限に利用していました。Rustではコンパイルする必要があるため、そこの違いをどの様に実装するべきか、といったところをかなり悩み、なかなか骨が折れました。苦しいです。評価してください。

上記の点、それからRust自体のキャッチアップを含めて、結局作成志してから作り上げるまで9ヶ月かかってしまいました。

もちろん間でwebsiteを作ったり他のプロダクト作成をしていたため、そのあたりの時間を差っ引く必要はありますが...

さすがに時間かけすぎでしょ...

わりぃ...やっぱ...つれぇわ......

まあ無事に完成したので無問題です。 時間かかったことは特に気にしていません。後悔もしていません。本当です。信じてください(曇りなき眼)。

冗談はさておき、苦労はしましたが、結果的には良かったと考えているのが以下の点です。

  • RustでCLIツールを作るところから始めたので、Rustの学習がスムーズだった。
  • また、たくさんのツールを作る必要が生じたため、経験値も積めた。
  • 最終的にAPIサーバをRustで作成できたので、自信につながった。
  • Rust触るの楽しい。
Rustで作成した自作ツール郡

さて、phpからRustに変えることで自作したツールは以下のものです。

個別にリポジトリを切っているので、興味があればご覧ください。

consowrap

GitHub - kip2/consowrap: It is a simple console wrapper application made in Rust.

コマンドツールをまとめて一括で扱えるようにするためのCLIツール。

sqcr

GitHub - kip2/sqcr: This is a simple application for executing SQL from the console.

単純にsqlファイルのSQLを実行できるようにしたコマンド。当初必要かなと考えて作ったが、結局不要になったため使っていない。

db-wipe

GitHub - kip2/db-wipe: A simple command-line tool for deleting databases in MySQL.

MySQLのバックアップを行うためのツール。MySQLは使用しないことになったため、作ったが使っていないツール。

migrate

GitHub - kip2/migrate: This is a simple migration application that can be executed in the console.

マイグレーション用のツール。PostgreSQLとMySQLに対応したマイグレーションツール。 MySQLが要件にあったが、マイグレーションの仕様を誤解していたため、DDL文によるトランザクションを回避する必要があると誤解し、PostgreSQLでも使用できるようにしている。

migrate-PostgreSQL

GitHub - kip2/migrate-PostgreSQL: This is a simple migration application that can be executed in the console, using PostgreSQL as the database.

マイグレーション用のツールで、途中PostgreSQLに変更しようか検討したときに作成。

上のmigrateに機能が統合されたため、こちらは作ったが使っていない。

seeder

GitHub - kip2/seeder

シーダー用のツール。一番最後に作成。

このころには使用するDBが決定していたため、PostgreSQLのみに対応している。


サーバ

今回利用しているサーバは以下になります

  • バックエンドサーバ:APIを配置するサーバ。
  • フロントエンドサーバ:クライアントに配布する画面機能を配置するサーバ。
  • DBサーバ:DBを配置するサーバ。

プロジェクトの流れに従うと、1つのサーバ上に上記3つを乗せることになると思います。

とはいえ、冒険してはいけないわけではないと思うので、冒険して個別のサーバにわけました。

また、冒険したのは以下の理由もあったからです。

  • 一つのインスタンス(AWS)に乗せるとスペックを考慮する必要があります。特に、今後バックエンドプロジェクトを進めると他のプロダクトも乗せる必要があるため、それによる懸念がありました。
  • お金をあまりかけたくなかったことがあります。一点目と関連する内容ですが、インスタンスの数を増やしたりスペックを増強するとお金がかかります。公開するサービスの永続性のためにもできるだけお金をかけずに継続したかったのです。
  • 単純に冒険したかったことがあります。いろんな技術触りたい。技術触るの楽しい。

バックエンド

Rustをバックエンドに選定したことによる苦労等は言語の欄で語っているため、こちらでは省略します。

その他の覚えてる範囲のことを書きます。

ハッシュ関数について

今回のプロダクトでは、要件上、API側で一意のハッシュを生成する必要がありました。

いろいろ実現手段はあるかと思いますが、最初、一意性があり衝突性を気にしなくて良いと聞いたため、Blake3を使おうと選定しました。

しかし実際に使ってみると、今回のプロダクトには向かない点がみつかりました。

生成されるハッシュが64文字ありURLにするには長すぎる、という点です。

さすがにURLにするのに64文字は長すぎのかなと思い、検討した結果、UUID + sqidsクレートを採用しました。

Short Unique IDs in Rust · Sqids

sqidsを使うと一意性を失わずにハッシュを短くできるそうです。

難しいことはわかりませんが、どうやらそうらしいです。

さて、実現方式としては、UUIDを生成しsqidsクレートを使用して62進数エンコード、という形で実現しています。

これで、64 -> 24文字までハッシュを短くできました。

API

バックエンド用のサーバにAPIを配置するにあたり行ったことは以下になります。

  • ビルド用にdockerfileを用意
  • デプロイごとにビルドとディレクトリの配置変更してくれるように、シェルスクリプトを用意
  • APIサーバをデーモンとして設定。systemdを用いて管理。

なぜDockerfileか、というと、単純にサーバの環境を汚したくなかったからです。

ビルド用の環境を、Dockerfileを用いることで、ポータビリティ性を持たせています。

もしサーバを移管する必要があっても安心。


フロントエンド

フロントエンドで覚えてる範囲のことを書きます。

YamadaUI

いままではフロントのUIも一から作ってきたが、自サイトを作る際に一から作る苦労を嫌と言うほど知ってしまったため(作成に160時間くらいかけてしまった)、UIライブラリを使おうと思いました。

また、これは個人的な思いですが、OSS活動もいつか参加したかったということがあります。

YamadaUIは定期的に参加の呼びかけを行っており、更に、参加用のドキュメントもあり、OSS参加に最適に思えました。

しかし、YamadaUIを使ったこともないのに参加するのもおかしい話なので、一度ちゃんと使ってみたかったのです。

そこで、OSS活動の参加まで見据えて、YamadaUIを使うことに決定。

あれだけ苦労していたUIがするすると簡単に実装できるのはかなり楽しかったです。

しかもドキュメントが完全日本語対応しているので、困った場合も公式ドキュメントがある、という安心感があります。

みんなもぜひ使ってみよう。

CodeMirror

コードエディターのサイトのため、エディターの部分を担うライブラリが必要です。

もともとの要件ではmonacoが推奨されていました。

しかし、いろいろ探したところ、CodeMirrorという便利なライブラリがあることを知ったため、こちらを使用するようにしました。

コードハイライトやテーマが充実しているため、おかげてかなりの言語をサポートしたり、いろんなバリエーションのテーマが使えるようになりました。

しかし、公式ドキュメントが若干読みにくいと思う...自分だけかな?

UIVerse

一部のデザインについて、UIVerseからお借りました。

Switch by aymenthedeveloper made with CSS | Uiverse.io

Button by McHaXYT made with CSS | Uiverse.io

Button by xopc333 made with CSS | Uiverse.io

Loader by jeremyssocial made with CSS | Uiverse.io

Button by gharsh11032000 made with CSS | Uiverse.io

Google Fonts

フォントに関してはGoogle Fontsからいくつかお借りしています。

Nerko One - Google Fonts

Oswald - Google Fonts

Dancing Script - Google Fonts

favion

サイトのアイコンはこちらからお借りしています。

Phosphor Icons


セキュリティ

セキュリティ関係で覚えてる範囲のことを書きます。

クロスオリジン対策

バックエンドとしてAPIサーバを立てています。

何も設定しないままではあらゆる場所からのアクセスが可能となってしまうため(対策をしないと画面機能を通さずにAPIにアクセスできる可能性があります。API機能は公開していないので防ぐ必要がある、ということです。)、アクセスするオリジンを制限することでクロスオリジンを対策しています。

同じサーバ上に配置していれば気にしなくていい部分かと思いますが、今回は冒険でフロントエンドとバックエンドを別のサーバに配置したため、このような措置の必要性が発生しています。

各種設定値

.envに設定値を設定することで、セキュリティに配慮しています。

これしか知らないからこれでやってるだけなので、他にいい方法あったら教えて下さい(他力本願)。


その他

Domainの移管

もともと、ドメインはお名前.comで取得していましたが、今回、Cloudflareを使用するに当たり、Cloudflareに移管しています。

DNSの移管は移管で面倒も多い作業でしたが、なかなか楽しい作業でした。

Cloudflareは初めて触りましたが、なかなかいいもんですね。

管理画面は使いやすいし、DNS、静的サイトのデプロイサーバと、一気通貫で一つのサービスで管理できるのが魅力です。

今回使った以外のサービスや機能もあるため、今後使い込んでいきたいなーと思っています。




参考資料

作成の際に参考にさせていただいた資料等を記載しておきます。

世の中に技術記事を書いてくださる方がいるからこそ、私のような愚鈍の性質でも技術にキャッチアップすることができます。

技術記事を書いている方に最大限の感謝と敬意を評して、この場を借りてお礼を述べさせていただきます。

いつもありがとうございます。

インフラ

Cloudflare

Cloudflare Registrarでdevドメインを取得した

お名前.com から Cloudflare Registrar にドメイン移管した話

Cloudflareにドメインを移管してみた | DevelopersIO

Cloudflare Registrarでdevドメインを取得した

個人開発のWebサービスをCloudflareに載せてみた【無料でここまでできる】

NGINX

nginxでリバースプロキシ


フロントエンド

全体的

AbortController - Web API | MDN

React

React で初期化時に 1 回だけ処理を実行したいときの書き方

Reactが初回マウントされるまでの仕組みを理解する

なぜ非同期処理? Node.jsの実装から読み解く Fetch API の response.json()

TypeScript

バグを減らす第一歩は、ちゃんと名前を呼んであげること(名前付き引数を使おう)

vite

【Vite】デプロイ環境ごとに参照する環境変数の値を変える

CSS

【初心者でもわかる】cssで使われる透明3種類の使い方 #CSS - Qiita

cubic-bezier を知る。 #CSS - Qiita

CodeMirror

ReactでCodemirror6を使ってみた - Qiita

React CodeMirror - CodeMirror component for React.

ディレクトリツリーを2秒で書けるアプリを作りました

CodeMirror v6によるZennのMarkdownエディタの作り方

新しくなったCodeMirror v6で遊ぼう! - TECHSCORE BLOG

How to use legacy-modes in CodeMirror6 - v6 - discuss.CodeMirror

CodeMirror: Language Modes

使わなかったが、勉強になったもの

一瞬で理解するHydration

React のハイドレーションとは?

[Next.js] SSR と React Server Components の相違点を理解する

SSRとReact Server Components の違いをレスポンスから考える - Qiita

htmxとは何なのか? その背景にある思想について - Qiita

Svelte をはじめる - ウェブ開発を学ぶ | MDN

Svelteのすすめ - Qiita

【2024年】React Router & TanStack Router比較

TanStack Router(& Query)はSPA開発で求めていたものだった✨【Reactのルーティングとデータ取得】


バックエンド

API作成時の資料

API設計まとめ - Qiita

RustでAPIを開発してみたら結構辛かった話

RustでAPIサーバーを書くのが思ったより良い

概要 - Rust APIガイドライン

Go、Rust、Pythonで実装したAPIサーバーの負荷試験比較 #Postman - Qiita

RustでWeb APIを作る際のエラーハンドリング - CADDi Tech Blog

Pavex – Rust API構築のための新しいWebフレームワーク | DevelopersIO

E2E テストの実装方法|Rust で MVP な Web API サーバを開発する方法

systemdでユニットファイルを作ってサービス化してみる #初心者 - Qiita

hash関数作成時の資料

hashアルゴリズムとハッシュ値の長さ一覧(+ハッシュ関数の基本と応用) #ブロックチェーン - Qiita

【Hash関数】できるだけ衝突率を上げずにIDを短くしたい #JavaScript - Qiita

Hashidsを使って短いハッシュ値を生成する - Qiita


DB

DB検討時の資料

永久無料のサーバーレスSQLデータベースを構築した方法

【クラウドDB比較】無料枠で提供されるサービスレベル - Qiita

無料から使えるデータベース比較 - Qiita

フルマネージドサーバーレスPostgres「Neon」を触ってみた

PlanetScaleの無料プランがなくなるので、NeonとTiDBを試してみた - wheatandcatの開発ブログ

安価かつスケーラブルなサーバレスバックエンドプラットフォーム ~KoyebとNeonのススメ~

Bun & TypeScriptでバックエンド開発:サーバーレスDB「Neon」の基本的な使い方 | Go-Tech Blog

授業内開発で部室予約システムを開発した話

DB設計時の資料

SQL Training 2021 - Speaker Deck

CockroachDB Serverless

CockroachDB Serverlessから学んだこと|昭和のオッサン

CockroachDB はどのくらい「しぶとい」のか? / How tough is CockroachDB? - Speaker Deck

PostgreSQLでしない方がいいことリスト

DBの検証環境構築時の資料

Cockroach DBをsingle-node clusterで動かすdocker-compose最小構成 - Qiita

その他

Font

フォントのライセンスはわかりづらい? モリサワnote編集部が法務担当を突撃してみた|モリサワ note編集部

SIL Open Font License - Wikipedia