📝

Yjsによるリアルタイム共同編集

2022/07/20に公開

株式会社 ZENKIGEN の revii 事業部でエンジニアを務めている守谷と申します。
普段はrevii(リービー)という 1on1 改善サポート AI のプロダクト開発を行っています。

今回はプロダクトの新規機能として実装したリアルタイムに共同編集ができるメモ機能について書きたいと思います。

revii(リービー) とは

revii(リービー)はオンラインで 1on1 を行い、録画したファイルを解析して客観的な数値をレポーティングするツールです。revii(リービー) チームとして Note も公開しているのでぜひご覧ください。

https://note.com/brainy_hebe799/n/nae70a2ab835b

なぜリアルタイム共同編集?

1on1 ではメンターとメンティーがそれぞれ共通認識を持って話を進める必要があります。片方でメモを取り、1on1 終了後にテキストを共有するやり方だと、手直しするコミュニケーションが発生したり認識齟齬が生まれてしまう可能性があります。
スムーズに相互理解を行い、スピーディーに 1on1 を進めるためにはリアルタイム性が必要だと考え、今回の機能を実装しました。

メモ機能イメージ
メモ機能イメージ

利用したライブラリ

  • Yjs
  • tiptap

yjs とは

yjs とは CRDT という仕組みを使ったフレームワークです。CRDT とは(conflict-free replicated data type)の略で、競合しないデータ構造を扱うアルゴリズムだそうです。詳しくは Yjs の Readme に解説が記載されておりますので興味がある方はご一読ください。

https://github.com/yjs/yjs#Yjs-CRDT-Algorithm

tiptap とは

ProseMirror がベースとなっているテキストエディタライブラリです。yjs でも ProseMirror を wrap した y-prosemirrorを提供していますが、拡張性を考え tiptap を採用しました。ベータリリースになっていますが動作は特に問題ないです。React と Vue.js に対応しています。

https://tiptap.dev/

Yjs + tiptap + Next.js でハンズオン

とりあえず動かしてみたいということであれば、以下のような手順でローカル環境を構築することができます。是非手元で実装してみてください。

  1. Next.js の環境をインストールする
  2. Yjs、y-websocket、tiptap をインストールする
  3. Next.js の page を作成し、tiptap でテキストエディタの UI を構築する
  4. y-websocket に同梱されている WebSocket サーバーを起動する
  5. Next.js を起動してページを開く

Next.js の環境をインストールする

Next.js の create-next-app を用いて初期設定を行います。

$ npx create-next-app@latest --ts

Yjs、y-websocket、tiptap をインストールする

必要なライブラリをインストールします。

$ yarn add yjs y-websocket @tiptap/core @tiptap/starter-kit @tiptap/react @tiptap/extension-collaboration @tiptap/extension-collaboration-cursor

Next.js の page を作成し、tiptap でテキストエディタの UI を構築する

Yjs と y-websocket を初期化して、tiptap でテキストエディタの UI を構築します。

import type { NextPage } from 'next'
import * as Y from 'yjs'
import { WebsocketProvider } from 'y-websocket'
import StarterKit from '@tiptap/starter-kit'
import Collaboration from '@tiptap/extension-collaboration'
import CollaborationCursor from '@tiptap/extension-collaboration-cursor'
import { EditorContent, Editor } from '@tiptap/react'
import { useEffect, useRef, useState } from 'react'

const Home: NextPage = () => {
  const YdocRef = useRef<Y.Doc | null>(null)
  const YWebSocketProviderRef = useRef<WebsocketProvider | null>(null)
  const [editor, setEditor] = useState<Editor | null>(null)

  useEffect(() => {
    YdocRef.current = new Y.Doc()

    YWebSocketProviderRef.current = new WebsocketProvider(
      'ws://localhost:1234',
      'roomname',
      YdocRef.current
    )

    setEditor(
      new Editor({
        extensions: [
          StarterKit,
          Collaboration.configure({
            document: YdocRef.current,
          }),
          CollaborationCursor.configure({
            provider: YWebSocketProviderRef.current,
          }),
        ],
      })
    )

    return () => {
      YdocRef.current?.destroy()
      YWebSocketProviderRef.current?.destroy()
    }
  }, [])

  return (
    <div>
      <EditorContent editor={editor} />
    </div>
  )
}

export default Home

y-websocket に同梱されている WebSocket サーバーを起動する

y-websocket にはNode で動く WebSocket サーバーが同梱されているのでこちらを以下のコマンドで起動します。

$ npx y-websocket start

Next.js を起動してページを開く

最後に Next.js を起動して localhost:3000 でページを開きます。

$ yarn dev

demo

表示される UI は簡素ですが、スタイルを反映したりテキストエディタの UI を配置すればちゃんとしたリアルタイム同時編集の UI を作ることができます。

まとめ

今回はテキストエディタという形で Yjs を利用しましたが、それ以外のコンテンツでも Yjs を用いた同時データ編集の仕組みは活用できそうに思いました。とりあえず触ってみたいということであれば上記のサンプルを参考にしてみてください。

お知らせ

少しでも弊社や harutaka 、revii に興味を持っていただいたという方は、お気軽にご連絡頂けると幸いです!カジュアルにお話という形でも、副業したいという形でも大歓迎です。

https://recruit.zenkigen.co.jp/career

Discussion