はじめに
こんにちは!jack-blog運営委員会のゆってぃーです。
最近jack-blogをリプレイスしたので、使用している技術や構成を紹介します。
リプレイス後のリポジトリはこちら
https://github.com/jack-app/jack-blog-tsjack-blogリプレイスの背景
もともとjack-blogはnotion-blog-nextjsをフォークして、スタイルを調整することで構成されていました。しかし、機能を追加しようとなると以下の様な問題点がありました。
- TypeScriptやLinterが導入されていないので未然にバグを防げない
- コンポーネントの粒度が低く、どこに何の処理があるか分かりづらい
コードはとても参考になりましたが、あまり開発体験が良くなく、今後保守していくには少ししんどいなと思いました。
Next.jsのバージョンも上げたかったので作り直そう!という方針になりました。
主な使用技術
Next.js
選定理由としては、以下があります。
- OGP, SEOに強い
- SSG(Static Site Generator)がサポートされている
jack-blogは基本的に静的なサイトです。SSGを利用することでビルド時に生成されたHTMLを返すだけでよく、クライアント側で処理を実行しなくて良いのでパフォーマンスが向上します。
Vercel上でパフォーマンスを確認できますが、爆速ですね。
Astroを使用しても良いかなとも考えましたが、今後クライアント側で制御したい機能が追加されるかもしれないということでNext.jsを採用しました。
Notion API
jackでは情報の管理にNotionを使用しています。jackerが気軽にブログを書ける仕組みを提供したいということで、NotionをCMSとしたブログを開発することにしました。
TypeScript
もともとJavaScriptでNext.jsを書いていましたが、事前にバグを防ぎたくTypeScriptを導入しました。
ただNotion APIの型定義がしんどく、信頼境界がReactコンポーネントのPropsまでとなっており、TypeScriptの恩恵を十分に受けていません。今後Notion APIでフェッチしたデータに型を追加して信頼境界を上げていきたいです。
Storybook
コンポーネントの管理のために導入しました。
Tailwind
カラー、余白の設定などが簡単に設定でき、秩序を持った開発がしやすいため導入しました。
また、Next.jsのでcss-in-jsを使用したくないという思いもありました。styled-componentsなどのcss-in-jsはブラウザでjsを実行するため、Next.jsのサーバーコンポーネントで使用できずパフォーマンスに影響が出るためです。(現状はSSGを使用しているのでcss-in-jsを使用しても何の問題もないですが…)
ただ、学習コストがかかるので保守性という観点であまり良い選択ではなかったかもと思ってます。
PLOP
コンポーネントの雛形を作成できる様にしています。
- ディレクトリ構成の秩序を保てる
- いちいちファイルを生成しなくていい
など、良い恩恵を受けています。
ESLint
以下のプラグインを使用しています。
- eslint-plugin-import
- importの順番をいい感じに整えてくれます。
- eslint-plugin-unused-imports
- importしているが使用されていないものを削除してくれます。
ディレクトリ構成
主なディレクトリ構成は以下の様になっています。
┣ src/
┃ ┣ app/ *Next.jsのルーティングなど
┃ ┣ features/ *特定の機能を持つコンポーネント
┃ ┣ screens/ *ページ全体の見た目を構築する
┃ ┣ ui/ *汎用コンポーネント
┃ ┗ utils/ *汎用関数
app
Next.jsの諸々の設定を担うディレクトリです。具体的には
- ルーティング定義
- global css, 共通レイアウト
- metadataの設定
Next.jsのAppRouterを使用しているわけですが、appディレクトリの債務が多いので改善の余地ありだなと思います。
features
特定の機能を実現するためのコンポーネントを集めています。データフェッチと密接なコンポーネントや、一つのページでしか使わないようなコンポーネントが該当します。
featuresに存在するコンポーネントは以下の様な構成をしています。
┣ hooks/ *特定の機能を呼び出すためのhooks
┣ presentations/
┃ ┗ index.tsx
┣ tests/
┣ index.stories.tsx *presentationをstorybookで表示
┗ index.tsx *hooksとpresentationを結合。最終的に利用されるコンポーネント
フロントエンドのデザインパターンとして知られているコンテナ・プレゼンテーションパターンに基づいて設計しています。コンポーネントを以下の2つに分けることで関心の分離を実現しています。
- プレゼンテーションコンポーネント
- データがユーザーにどの様に表示されるかを管理するコンポーネント
- コンテナコンポーネント
- 何のデータがユーザーに表示されるかを管理するコンポーネント
- フェッチしたデータをプレゼンテーションコンポーネントに流し込む
以下の様に多くの恩恵を受けれます。
- どこに何の処理があるか明確
- 1ファイルのコード量が減る
- storybookで参照するコンポーネントがロジックを持たない
ui
汎用的なコンポーネントをまとめます。基本的に見た目の債務のみを持ちます。
screens
ページ単位での見た目を管理します。コンポーネントを組み合わせ、見た目を構築する債務のみを持ちます。
utils
汎用的な関数をまとめます。
デザイン
jack-blogのデザインはfigmaを使って行いました。
デザインシステムの導入
簡素なものではありますが、デザインシステムを作成しました。
デザインシステムを導入したことで、
- 今後開発していく上でデザイン的な統一感を保つ
- フォントサイズやカラーコードのタイポを防ぐ
など、秩序のある開発を進めることができます。
tailwindではこれらの設定を簡単に行うことができて開発体験が良かったです。
リプレイスして良かった点と悪かった点
良かった点
良かった点は以下になります。
- TypeScriptとESLintによって安全な開発ができる
これで今後も保守しやすくなりました。
- 債務が分離されて管理しやすい
featuresの導入はとても良かったなと思います。一番上のコンポーネントでデータフェッチをして子コンポーネントにデータを流す方法が多いと思いますが、propsのバケツリレーや構成が複雑になるなどの問題があります。feature単位でhooksを作ることでこれらの問題を解決できました。
悪かった点
一方で悪かった点もあります。
- AppRouterやTailwindなど学習コストが少しかかる
今後引き継ぎをするとなった時に大変になりそうかも…
- ビルドに時間がかかる
featuresやSSGの設定で同じAPIに複数回リクエストをしてしまっています。データが大量になったらこの構成は見直す必要があるかもしれません。
ユーザー側でパフォーマンスが落ちるわけではないので今はよしとしています。
まとめ
この構成が参考になれば幸いです。
今後もjackの活動がより広まる様に、jack-blogを改善していきます!