note・グッドパッチ・STUDIO・オープンエイトの開発事例に学ぶSPA/PWA活用と最新トレンド
ダイジェスト動画
■登壇者プロフィール
note株式会社 山形 孝造氏
受託開発会社、フリーランスを経てフロントエンドエンジニアとして2020年3月にnoteへ入社。コーヒーを自宅で焙煎したり、農園に行ったりするくらいに大好き。
株式会社グッドパッチ エンジニア 古家 佳武氏
Androidエンジニアとして2016年にグッドパッチ入社。自社サービスのアプリ開発を経て、2019年よりStrapのフロントエンドを中心に担当中。
STUDIO株式会社 CPO / Founder 甲斐 啓真氏
2016年オハコプロダクツ(現STUDIO)を創業。2017年よりCPOとして、フロントエンドを中心にプロダクトを担当。
株式会社オープンエイト フロントエンドエンジニア 田村 崚氏
2019年オープンエイトに新卒入社。フロントエンドは未経験だったが、入社後からReactの基礎から勉強し始め、現在もフロントエンドを中心に担当中。
【note】開発環境の刷新からApp分割で得た技術や気づき
最初に登壇したのは、noteの山形孝造氏。従来のブログやメディアとは一線を画し、文章や写真に限らず音楽、映像などのクリエイティブなコンテンツを表現できるプラットフォームを展開している。
山形氏は、これまでの開発環境ならびに生じていた課題。課題に対する取り組み。そこから生じた新たな課題、そして解決まで。時系列で詳しく紹介していった。
「初期の開発環境は、Ruby on RailsとAngularJS上に、CoffeeScriptで書かれたSPA(Single Page Application)でした。ただこの環境では、『動作が遅い』『SSR(Server Side Rendering)ができない』『モダンなJavaScriptでの開発についていけない』という課題がありました。そこで2018年頃から、Nuxt.jsに移行する取り組みを始めました」
移行プロジェクトチームも発足。プロジェクトの取り組みから2年弱、2020年の3月には移行作業はほぼ終了し、チームも解散。現在はAngularJSの一部がわずかに残っている状況までに改善した。
「Nuxt.js上にAngularJS時代の全機能と新機能の全てがあることでコード量が膨大となり、ビルドやデプロイに時間がかかるという課題が生じました。少しのミスが多大な影響を及ぼす、バタフライエフェクトのような障害も発生。SEOにも影響が出ていました。マシンの性能によっては作業の途中で落ちることがあったり、実際のサービスがダウンする一歩手前という、かなり危険な状況を引き起こしていました。そこで、パフォーマンスの向上においては別軸で対策チームを発足すると同時に、Appを分割しようとの取り組みを始めます」
具体的には、Next.jsで別実装することを決めた。ただし、Nuxt.jsをすべて破棄するわけではない。また同取り組みにおいても、共通コンポーネントをどう扱うかとの問題が生じた。Web Componentsを検討し、まずはStencil.jsを試した。しかし、Web Componentsはブラウザ内で動く機能のため、SSRができなかった。そこで後述でも触れるが仲間に相談し、すすめられたのがSvelteである。
「TypeScriptが気持ちよく書け、Webpackの設定ファイルを書けば、Nuxt.js、Next.jsどちらでも動く。SSRも簡単にできました。Stencil.jsと比べサイズが小さいのも魅力でした。実際、Buttonを実装してみると、バンドルサイズは約7分の1でした」
山形氏はサンプルコードを紹介。また、今回の取り組みを通じて得た気づきを次のように紹介した。
「技術的な面においては、これまで身につけた技術の習熟度を上げておくと共に、常に新しい言語、ライブラリ、フレームワークの知識を身につけておく必要があると改めて感じました。ベースとなる技術があれば、新しい技術も使えるからです。
実際、Svelteに触れたのは今回初めてでしたが、Vue.jsのスキルがあったため、比較的楽に利用できました。またどういったタイミングでコンポーネントがコンパイルされ、最終的にブラウザに届くのか。ビルドシステムの理解や、バンドルされたソースコードを見て問題解決がなされているか理解できる、分析力も必要だと感じました」
加えて山形氏は、一人で課題解決に臨むのではなく、仲間や組織を活用することも重要だと強調。Slackに課題を投げる程度でも十分だと補足した。実際、Svelteはメンバーに相談したことで得た情報である。プロジェクトリーダーやマネージャーに相談することも重要だ。課題解決に充てる時間を配慮してくれる可能性があるからだ。実際、noteではそのような環境をとっているという。最後に次のように述べ、セッションを締めた。
「当社のミッション・ビジョン・バリューである、『すばやく試そう』『常にリーダーシップを』『大きな視点で考えよう』といった思考も意識しています。実際、小さく始めて、徐々に大きくしていき、まわりを巻き込む。このような開発フローが重要だと思います」
【グッドパッチ】「Strap/レイヤー機能」の開発で得た技術・価値・気づき
続いては、デザイン視点でアプリやWebなど、クライアントのサービスや取り組みをサポートするグッドパッチの古家氏が登場。同社のUI/UXデザイン、ビジネスモデルデザイン、ブランド体験デザイン、組織デザイン、ソフトウェア開発など、同社のサービス領域は多岐にわたる。
今回のセッションでは、自社開発のSaaSプロダクト「Strap」について、同プロダクトのフロントエンドエンジニアである古家氏が、特にLayer機能の開発の変遷について紹介した。
「Strapはリモートコラボレーションの可能性を広げる、オンラインホワイトボードサービスです。クラウド型のワークスペースとも言えます。アーキテクチャはスライドのとおり。React、Reduxといったフレームワーク・ライブラリ上に、TypeScriptで書いて実装しています」
ブラウザ上でさまざまな2Dグラフィックス処理を可能にする、JavaScriptのグラフィックエンジン(ライブラリ)、Pixi.jsも採用。一方、バックエンドはGoogleが提供する各種サービスを利用している。GCP(Google Cloud Platform)、Firebaseならびに同プラットフォームが提供する各種機能群だ。
Strapでは、まさにリアルのホワイトボードのように、オンラインのボード上に図形やテキストなどを作成し、大きく引き伸ばしたり、削除したりできる。そしてその様子を、メンバー全員で共有できる。
当然、図形は重なることもある。その際にポイントとなってくるのが、どの図形が一番上なのか、その後の重なり順はどうなのか、いわゆるレイヤーであり、StrapでもLayer機能として明確に定義し、開発を進めている。
「初期のバージョンでは、データの不整合が起こらない。データ更新量が一定量に抑えることができるとの目標を掲げ、開発に取り組みました。まず挙がったのは、エレメントごとにindexを振り分けるアイデアでした」
エレメントとは、ホワイトボード上に示される、テキストや図といったオブジェクトである。実際、同アイデアを試してみると、indexの更新時にデータ更新量が大きくなる。Firestoreの機能のSecurity Rulesを利用しても、index値の重複が防げないという課題が見えきた。そこで次のプランを考える。
「エレメントとは別に、Layerというドキュメント(要素)を用意。indexではなく、配列で振り分けるとのアイデアが出ました。同アイデアを試すと、FirestoreのAPIが利用できるため、機能の追加や削除が効率的に行えそうであること、Firestoreのトランザクション、バッチ機能を使うことで安全にデータが更新できそうだと感じ、こちらの手法で開発を進めることになりました」
ところがβ版を開発し社内で試すと、問題が生じた。複数人が同時にレイヤーを編集する機会が想定より多くエラーが頻発したのだ。ユーザーごとの表示差分が発生する問題や、Firestoreのトランザクションに頼っていたため更新速度が十分に出ない、といった課題も生じた。そこで新たなバージョン、Layer2.0の開発に取り組む。
「後からわかったことですが、配列を利用した設計はアンチパターンだと、Firebaseのブログで紹介されていました(苦笑)。そこで、配列の代わりにObjectを使う方法を思いつきます。フロントエンドでデータ更新が簡単になるなど、一見すると良さそうなアイデアでしたが、シミュレーションを行うと、同時編集でバグが出ることがわかりました」
そこで古家氏たちは、エレメントに対して1:1でレイヤー機能を持たせる。indexの値の間をあける、といった工夫を施した。シミュレーションすると先のバグはなくなり、同時編集も可能になった。
しかしその後、ユーザーが増えていったり、複雑な機能が追加されていくと、新たな問題が顕在化してきた。多数のユーザーが多くのエレメントを追加したり、Redo、Undoといった操作を繰り返すことで、エレメントにレイヤーが存在しないなどのバグが生じるようになっていたからだ。
「ただ実は、このようなバグが生じる理由を、多くのエンジニアは気づいていました。バージョン1.0の際に、エレメントとレイヤーのデータを分けたことが理由だろうと。そこで改めてレイヤーとエレメントのindexを統合しようとの結論に至りました」
こうして開発されたのが、最新バージョンの3.0である。基本的には、初期に考えていたデータ構造ではあるが、先に紹介したようにエレメントとレイヤーを1対1で持たせるなどの工夫が施されているため、構造自体は大きく変化。端的に言えば、データ量が大幅に削減した。
マイグレーションの必要はあったが、同作業も無事終了。コードの見通しよくなり、エレメント作成時にindexも一緒に付与される仕様となり、ボードの読み込みスピードは30%も高速化した。
古家氏は同アプリケーションを開発する価値を次のように話し、セッションを締めた。
「Strapの開発においては、次の3つの価値を意識しながら進めています。個人の思考の整理、リアルタイムコラボレーション、チームコラボレーションです。Layer1.0では、ビジュアルコラボレーションサービスとしての基礎的な機能を実装したことで、個人の作業の生産性アップを実現しました。
バージョン2.0では、同時編集の安定化を実装することで、チームコラボレーションならびに、共同作業のコスト削減を。そしてバージョン3.0では、操作の高速化により、個人・共同作業どちらの価値も向上したと手応えを感じています。またLayerの開発を経て、エンジニアがビジュアルコラボレーションの価値の重要性を改めて実感することができました」
【STUDIO】デザインエディタの設計思想ならびに実装ノウハウ
続いては、STUDIO甲斐氏が登場。同社が開発・運営する「STUDIO」は、ノーコードでWebサイトを作成できるWebデザインプラットフォームだ。Webサイトの幅やテキストといった各種要素をマウス操作、ドラッグアンドドロップだけ、つまりデザイン感覚で配置、作成できる。
同社のファウンダーでありCPOでもある甲斐氏は、まずSTUDIOの特徴を紹介した。
「デザインエディタ側で作成した内容を、ライブプレビューし、確認。問題がなければそのままドメインを取得し、公開。その後の運用やサイトの変更まで、一気通貫でWebサイトの作成から公開、運用を行えるのがSTUDIOの特徴です。技術的には、Vue.jsやFirebaseを利用しプレビュー機能に反映。巨大なJSONを編集しているエディタと言えます」
デザインエディタの構造は主に4つからなる。「レンダラー」「スクロール」「ズーム」「ハンドル」だ。甲斐氏はそれぞれの構造(機能)において、どのよう点に重きを置き、技術や要素を決めているのか。それぞれ解説していった。
●レンダラー
レンダラーでは、ボックスという単位(ブロック)をキャンバス上に追加・描写していく。
同様のデザインツールではDomを使うことはあまりないが、STUDIOの場合は最終成果物がHTMLのため、Domツリー、レイヤー構造をJSONのかたちで保存して、そのままコンポーネントツリーに変換・表示している。
「ボックス一つひとつがコンポーネントであり、HTMLのレンダリングをデザインツールのレンダリングとして使っています」
●スクロール
スクロール機能では、いろいろと試した結果、ブラウザに標準で備わる同機能を採用した。scriptが走らないため軽い。無限のように見え、実は巨大なスクロール面になっていること。採用を決めた一番理由は、ブラウザバックが回避できるからだ。
●ズーム
「ズームイン・アウトしたときに画面がカクカクすると、他の機能が高性能でもユーザーは悪い印象を持ちます。そこで同機能については、特に重きを置いて開発しました」
アプリ内で唯一、パフォーマンスの為にVue.jsを経由せずに直接Domを書き換えている。ピンチ中はstateを書き換えず、直接styleのtransformを編集することで、快適なズームイン・アウトを実現している。そして連続したズームイン・アウトが終わったときにはじめて、Vue.jsのstateに更新関数を読み込む。
またズーム率が800%を超えると出てくるグリッド表示では、Canvasを利用。基本的にCanvasを使わないが、SVGやDomだとグリッドの様な連続する要素は重くなるため採用している。
●ハンドル
「スクリーンとレンダラーの部分は、CSSのtransformを用いて、滑らかズームを実現していますが、ハンドルはあえて使っていません。transformをすると、基本的にぼやける傾向にあるからです。レンダラーのrectを計算して表示しています」
但しテキスト編集については例外的に、レンダラーを利用している。
甲斐氏は最後に、現在開発中でアーリーアクセス間近である、API連携により取得したデータを追加できる新機能「Blueprint」のデモ動画を紹介し、セッションを締めた。
【オープンエイト】「Video BRAIN」を実現する技術ならびにマインドセット
最後に登壇したのは、自然言語処理とコンピュータービジョンを中心に独自のAI技術を開発しているオープンエイトの田村氏だ。同社は、同技術をベースに動画編集クラウドサービス「Video BRAIN(ビデオブレイン)」、SNS投稿・分析サービス「Insight BRAIN(インサイトブレイン)」などの自社サービスを提供している。
今回のセッションではオープンエイトの主力サービスであるビデオブレインについて紹介した。田村氏は冒頭で、ビデオブレインで作成した自己紹介動画を見せた後、動画編集ソフトに必要な機能について、次のように説明した。
「動画編集ソフトには、編集と書き出し大きく2つの必要な機能があります。クラウドでの編集では、動画、静止画、図形、テキストなどのオブジェクトと呼ばれる各種素材をアップロードし、動画を編集・構築していきます。そうして編集された動画をプレビューで確認し、再度編集を行いプレビューすることを繰り返し、最終的に書き出す流れとなります」
「誰でも簡単に動画が作成できる」が、ビデオブレインが目指している世界観だ。言い方を変えると、プロユースだけれど使い方が難しかったり、スキルを必要とするソフトではない。実際、マニュアルを見ることなく、直感で先の機能を実現することに重きが置かれた開発が進められている。
「編集行程であれば、すべてのオブジェクトの編集がドラッグアンドドロップで簡便に行えること。プレビューに時間がかからないこと。OPEN8のエンジニアはこの2つを意識しながら開発を行っています」
ただし、言うは易く行うは難し。実装の難易度は高いと言う田村氏。種類の異なるすべてのオブジェクトの座標を把握する必要があり、画面の端にいくと、不自然な動きを見せるオブジェクトなどの課題があるからだ。
このような課題に対し、React、Redux(一部Context)、TypeScriptの良さを活かしながら、オブジェクトにたいして統一のRectという共通の型を定義するアプローチをとっている。
プレビューの即時反映においては、Canvas上で即時でレンダリングし、動画ファイルのように見せる。プレビュー専用のレンダリングモジュールを作成することで、サーバーサイドでMP4動画などを生成するよりも格段にはやく即時で編集プレビューが実行できるような仕様を実現できた。書き出しにおいても、プレビューと同じのレンダリングモジュールを同じく使うことで、開発効率のアップを実現している。
ただ繰り返しになるが、このような開発を実現した裏側では苦労も多かったと、田村氏は明かしている。開発の舞台裏を次のように紹介し、セッションを締めた。
「私がオープンエイトにジョインした2年前、コード・仕様が複雑で誰に聞けばいいかわからない状況でした。。過去の負債に加え、動画のフレームがズレるなど、課題は山積み。ズレに関してはテスト工程で発覚することも多く、現在では人力が中心になっているQA工程を自動化するためのプロジェクトが走っています。コードの問題に関しては、SE5からES6に規格をアップすると共に、TypeScriptで統一するなど、リファクタを進めました。最近ではUIにAtomicDesignを導入したり、保守体制が整うなど、より良い環境となっています」
【Q&A】参加者から寄せられた質問を紹介
セッション後、参加者からの質問に答えるQ&Aタイムが設けられた。
Q:Nuxt.jsを軽くする対策は考えなかったのかなど、Next.jsに移行した理由を知りたい
山形:もちろん考えましたし、コードボリュームを小さくするとの取り組みは、別軸で行っています。Next.jsに移行したのは、React.jsなので融通がきくこと。進化が早い、最新の技術を使いたいとの想いがあったからです。
Q:Strapは機能が増えるにつれ、PixiJSだけでは解決できないトラブルは発生していないか
古家:PixiJSだけでは難しい課題もあり、いろいろと細かなチューニングを施しています。たとえば、画面に見えている箇所だけを描画し、見えていないところは描画しない。ユーザーの初期位置を中心にエレメントを読み込むといった処理などです。
Q:他のライブラリを補完した、forkしたなど。別の解決策があれば知りたい
古家:日本語の改行がうまくいかないという課題がありました。課題を解決するプラグイン、正確にはCJK(中国語、日本語、韓国語)を扱いやすくするプラグインを自分たちで開発し、組み込むと共にOSSとして公開しています。
※プラグイン:https://www.npmjs.com/package/pixi-cjk
Q:STUDIOのズーム機能、ピンチ中のパフォーマンスの計測はどのように行っているのか
甲斐:Chromeに備わっているデベロッパーツールを使っています。パフォーマンスタグをクリックし、実際にズームイン・アウトを行い、そのパフォーマンス状況を録画。フレームレートが表示されるので、基本的には16ミリ秒以下に設定するようにしています。
Q:オープンエイトがReact.jsやTypeScriptを使用した理由について聞きたい
田村:React.jsについては、当時は参考となる情報が一番多かったからです。TypeScriptに関しては、ビデオブレインはデータ量が多いなど処理が複雑なため、ある程度型を用意しておくことで、新しくジョインしてきたメンバーがスムーズに業務を行えることを考え、半年ほど前より利用しています。
Q:新言語に移行する際、社内研修などは行っているのか
甲斐:言語の移行に限らず、新しいテストの方法やその他ノウハウのシェアをしたいときには、ペアプログラミングをよく行っています。実際、有効だと捉えています。
Q:経験も含め、求めるフロントエンドエンジニア像について
山形:noteそのものに興味を持つ人ですね。実際、働いているフロントエンドエンジニアの多くも、小説、アニメといったコンテンツに興味を持つ人が多いです。経験に関しては、AngularJSやVue.jsなど、モダンフレームワークの経験が2年ほどあるエンジニアを求めています。
古家:TypeScript、React.jsを使ってのSPA開発経験を求めています。Firebaseの経験があれば、なお歓迎です。また、自分の得意じゃない領域に対して、抵抗なく学び、吸収していける姿勢をお持ちの方。チームで働くことに喜びを感じるタイプがフィットする環境です。
甲斐:デザインが好きで、こだわりを持って業務に臨める方を求めています。経験年数は不問ですが、当社のフロントエンドエンジニアを採用する際には、全員に同じテストを受けてもらっていますので、その結果を見て判断しています。
田村:無駄なレンダリングを避けるなど、基本に忠実なエンジニアを求めています。またチームは複数に分かれて開発を進めていくため、チーム間の連携も重要です。モダンな開発経験2年をイメージしていますが、リーダースキルも兼ね備えている方はより歓迎します。