長期的な運用を見据えたリッチテキストエディタの選定
この記事は、[ispec] 医療に向き合う技術者集団の戦録 Advent Calendar 2024 の17日目の記事です!
はじめに
こんにちは!
最近、リッチテキストエディタの選定と実装を行う機会がありました。そこで今回は、どのような点を考慮して選定を行ったのか、当時作成したADR (Architecture Decision Record) を元にご紹介していきます!✌️
開発環境
フロントエンド
- React
- Remix (現在の React Router)
バックエンド
- Kotlin
- PostgreSQL
(ちょっと脱線) WYSIWYGとの違いは?
リッチテキストエディタについて調べている中で、WYSIWYGという言葉をよく見かけました。WYSIWYGとリッチテキストエディタの違いは何でしょうか?調べてみると、以下のような違いがあるようです。🤔
リッチテキストエディタ
テキストの書式設定(太字、斜体、箇条書きなど)を可能にする編集ツール。
WYSIWYG
What You See Is What You Get (見たものをそのままに) の略。ウィジウィグと読む。編集中の画面で見える内容が最終的な出力と完全に一致するのを特徴とする、より高度なエディタ。
Markdownで記載して装飾できるエディタもリッチテキストエディタというらしいです。なので、WYSIWYGはリッチテキストエディタの一部と言えそうです。今回求めているのはWYSIWYGですが、この記事では多くの人が聞き馴染みのある?リッチテキストエディタの呼び方で統一します。
考慮すべき点の整理
リッチテキストエディタを選定するにあたり、以下の点を考慮しました。
プロダクトの運用期間
今回導入するプロダクトは既に稼働しており、今後も長期的に運用される可能性が高いです。そのため、以下の点を重視して選定することにしました。📅
- コミットが継続的に行われているかどうか
- 将来的な機能追加や変更がしやすいかどうか
- フレームワークへの依存が少ないかどうか
装飾や機能
初期段階で実装する必要のある装飾と、将来的に実装する可能性が高い装飾や機能を、要件定義書などを元に整理しました。そして、これらを実装できるリッチテキストエディタを選定することにしました。🎨
初期段階で実装する装飾
- H1
- H2
- H3
- 太字
- 斜体
- 下線
- リンク
- リスト
- 番号リスト
- 平行線
将来的に実装する可能性がある装飾・機能
- 文字色
- ハイライト, 背景色
- テーブル
- 切り取り
- コピー
- 貼り付け
保存形式
入力内容をフロント <> バックエンド間でやり取りする際、また、データベースに保存する際、以下2つの形式が考えられます。🔤
HTML
- メリット
- HTMLなので、コンテンツデータがライブラリに依存しない (エディタライブラリを変更しても、変換コストが低い)
- HTMLをそのまま表示すれば、表示機能が完成する (開発工数が少ない)
- デメリット
- サニタイズが適切でない、またはバグが存在した際に、XSSなどのセキュリティリスクが生じる可能性がある、また、そこに意識を向ける必要がある (Reactの仕様上
<script>
を実行される心配はないが、ボタンクリックなどイベントハンドラに仕込み実行させることは可能) - コンテンツデータにスタイルも含まれているため、スタイルを後に変更しようとした際に適用が難しい可能性がある、または、コードが汚くなる可能性がある (
!important
の使用など) - 予期せぬデータが含まれていた際に、表示が崩れる可能性がある
- ブラウザで扱いやすい形式のため、将来的に異なるプラットフォーム (Flutterアプリなど) で実装が必要になった際、コンテンツデータの表示やスタイルの加工などがJSONと比べて難しい、または、WebViewで実装するなど変化球が必要になる可能性がある
- サニタイズが適切でない、またはバグが存在した際に、XSSなどのセキュリティリスクが生じる可能性がある、また、そこに意識を向ける必要がある (Reactの仕様上
JSON
- メリット
- データが構造化されているため、解析やパースなどが容易である
- MySQLやPostgreSQLがJSON型をサポートしており、DBへの保存にも適している
- ライブラリが対応していないデータはエラーとなるため、表示崩れやバグを発見しやすい
- 独自のフォーマットや装飾に対応しやすい
- デメリット
- 標準化されたフォーマットは存在しないので、ライブラリを乗り換える際に、変換するコードを実装する必要がある
- バックエンドでコンテンツデータを扱うために、自前で型定義を行う必要がある
- バックエンドでコンテンツデータを生成する場合、JSONの構造を理解する必要があるので、HTMLと比べると学習コストがかかる
本プロダクトは長期的な運用が見込まれるため、将来的にどのような機能が追加されるのか予測することが難しいです。そのため、拡張性が高く、将来の変更に対応しやすいJSON形式を採用することにしました。
ライブラリの比較
ここまでの考慮事項を満たしている、または気になったライブラリをピックアップしました。
Tiptap
- GitHub Star: 27.1k
- フレームワークに依存しないように作られている
- ヘッドレスである
- 逆に言えば、初期開発時にコストがかかるともいえる
- HTMLまたはJSONでの入力、出力ができる
- 独自のコンテンツタイプを挿入できる
- 大規模サービスでの採用事例が複数ある
Quill
- GitHub Star: 43.5k
- シンプルで軽量
- クロスプラットフォーム対応 (Flutter用のライブラリあり)
- DeltaというJSONフォーマットで出力可能
- Reactで使用する場合、react-quillというラッパーを使うのが一般的
Slate
- GitHub Star: 29.8k
- 現在ベータ版のため、破壊的変更が不安
- カスタムブロック、装飾を作成可能
- JSONで出力可能
Editor.js
- GitHub Star: 28.5k
- JSON形式でコンテンツデータを取得できる (HTMLはなさそう)
- Notionのようなブロック形式のエディタを作成することに特化しているため、今回のユースケースとはマッチしない可能性がある
- 採用事例
Lexical
- GitHub Star: 19.5k
- Meta社が開発している
- iOS版も提供されているが、メンテナンス頻度は低い
- HTMLまたはJSONでの入力、出力ができる
Plate
- GitHub Star: 11.0k
- UIにshadcn/uiを使用している
- 本プロダクトのUIはshadcn/uiをベースにカスタマイズしているため、デザインの統一が容易
- 採用例やネットの記事が少なく、トラブル時に解決が難しい可能性がある
Tiptapに決定!
最終的に我々は、Tiptapを採用することにしました。大きな理由としては以下の通りです。🥳
- ヘッドレスでカスタマイズ性が高いため、将来的なデザインや仕様変更に柔軟に対応できる
- 大規模サービスでの採用事例が多いため、安心して使用できる (開発時に参考にできる情報が多い)
- 特定のフレームワークに依存していないため、将来的にフレームワークを乗り換えても、比較的低いコストで対応できる
まとめ
この後、実際にTiptapをフロントエンドに実装したり、コンテンツデータをバックエンド (Kotlin) で扱うためのモデリングや実装を行ったりしました。こちらの話は今回は割愛しますが、また機会があればご紹介したいと思います!
リッチテキストエディタを選定する際の参考になりましたら幸いです!😊
参考記事
Discussion