全デバイス・全ブラウザで PDF を読みたい

TL;DR

  • PDF を画面に埋め込む方法は、iframe, object, embed, Viewer(3rd party library の利用)がある。
  • ブラウザネイティブの PDF 表示機能はブラウザ差異が大きいため、PDF を canvas や svg に変換して表示するライブラリやビューアーを利用した方が安定する。
  • しかし 3rd party library / service の利用はバンドルサイズやランタイムでの変換にコストがかかるため、なるべくブラウザネイティブなやり方で PDF を開きつつ、一部ブラウザ向けに対してのみ 3rd party library/service 経由で表示するように分岐させたい。
  • どのブラウザならブラウザネイティブの機能が使えるかを調べるために、サポート範囲の全端末・全ブラウザで PDF の描画結果を比較・調査した。

はじめに

業務委託エンジニアの井手(@sadnessOjisan)です。 今回は KAIZEN Sales の開発で作った "全ブラウザ・全端末対応の PDF コンポーネント" を作るにあたって、PDF の埋め込み方法について調査した結果を紹介します。

先に結果を書くと以下の通りです。

- Chrome
(PC)
Safari
(PC)
Firefox
(PC)
Safari
(SP)
Chrome
(SP)
IE
iframe △(no toolbar) ×(1 枚目のみ) × ×
object △(no toolbar) ×(1 枚目のみ) × ×
embed △(no toolbar) ×(1 枚目のみ) × ×
Google Drive △(不安定) △(不安定)
PDF.js(2.3.2~) ×
PDF.js legacy(2.3.2~) ×
PDF.js(~2.3.2)

KAIZEN Sales の説明と PDF コンポーネントの要件

KAIZEN Sales は企業が商談や営業に使う動画販促資料を管理するプラットフォームです。 その中に顧客企業がリンクを発行して、利用者にそのリンクの中にある動画や画像や PDF を見せてアンケートを取る機能があります。 今回僕が開発していたのはこのリンクで開かれるアンケートページと、そのリンク先にあるアンケートを顧客 HP にそのまま埋め込める 3rd party script です。

big buck bunny
KAIZEN sales の例

(※ Big Buck Bunney is licensed under CC-BY.)

この案件ではユーザーがコンテンツを視聴するページを開発し、その中で PDF を閲覧できるようにしました。ただこの PDF(コンテンツ)表示ページは外部リンクや 3rd party script として広く配布されるため PV も大きくなりやすく、また PC/Mobile や サポート対象の全ブラウザへの対応が必須となりました。

そこで、どの方法を使ってどの端末でアクセスすると PDF が表示されないかを調べるために、実験用の web サイトを作って、いろいろな方法や端末やブラウザを実験していました。 こちらがその実験用のサイトです。

FYI: https://github.com/ojisan-toybox/universal-pdf-component

PDF をページに埋め込むための方法

今回僕は下記の方法を実験しました。

  • iframe tag に埋め込む
  • object tag に埋め込む
  • embed tag に埋め込む
  • Viewer を呼び出す

iframe tag に埋め込む

iframe は

The <iframe> HTML element represents a nested browsing context, embedding another HTML page into the current one.

とある通り、別 webpage のコンテンツをそのまま埋め込めます。

FYI: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe

多くのブラウザではブラウザ自体が PDF の Viewer としても使えるため、pdf の src を iframe で指定すれば、iframe 越しに PDF を読めます。

iframe で PDF を表示させる例
iframe で PDF を表示させる例

object tag に埋め込む

object は

The <object> HTML element represents an external resource, which can be treated as an image, a nested browsing context, or a resource to be handled by a plugin.

FYI: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/object

とあり、外部リソースの読み込みに利用できるタグです。その気になれば img 要素のように画像を読み込むことができます。

<object data="/path/to/image.png" width="250" height="200"></object>

PDF も同じように読み込めます。

<object
  data="https://ojisan-toybox.github.io/universal-pdf-component/example.pdf"
  type="application/pdf"
  width="800px"
  height="400px"
></object>

object で PDF を開く
object で PDF を開く

embed tag に埋め込む

embed も

The <embed> HTML element embeds external content at the specified point in the document.

FYI: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/embed

とのことで、object のようにコンテンツをブラウザに埋め込めます。

<embed
  width="800"
  height="400"
  src="https://ojisan-toybox.github.io/universal-pdf-component/example.pdf"
  type="application/pdf"
>

embed で PDF を表示する
embed で PDF を表示する

iframe, object, embed の使い分け

ではこれらのタグをどのようにして使い分けるとよいでしょうか。 実際のところPDFを表示させるにあたっては機能的な差異はほとんどないため、悩みます。

MDN には外部リソースの読み込みタグの歴史について解説したページがあり、embed, object, iframe の歴史や変遷について解説されています。 それによると embed, object は Java アプレットや Flash などのプラグイン技術を想定しており、今現在において使う機会はあまりないと言及されています。

FYI: https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Other_embedding_technologies

しかし私は iframe もしくはobject を使うべきだと思っています。 なぜなら iframe, object は fallback できるからです。

<object data="data/test.pdf" type="application/pdf" width="300" height="200">
  表示されない場合はこちらからDLしてください:
  <a href="data/test.pdf">test.pdf</a>
</object>

そして、iframe は第三者のコンテンツをウェブサイトに組み込む場合に使われているイメージがあるので、自分でホスティングしたPDFの表示にはobjectを使いました。 object の利用が推奨されていない理由はプラグインに頼るのを推奨しないという意味であり、PDFの閲覧という目的においては問題がない認識です。 iframe, object はどちらを使っても良く、好みの問題だと思っています。

ブラウザネイティブな方法の弱点

このように、ブラウザには PDF を読み込む方法が用意されているので、ブラウザの機能を使えばPDFを埋め込めるコンポーネント作成の要件を満たせそうです。 しかし、いざ使ってみると次のような問題に出会うでしょう。

モバイル Safari で paging できない

モバイル Safari では、iframe, object, embed を利用した際どのタグでも表示されますが、PDF のページ数が 2 ページ以上あるときに 1 枚目の内容しか表示されず、スクロールできません。

Safari ではスクロールできない
Safari ではスクロールできない

同様の問題は stackoverflow でも紹介されており、ブラウザ側の不具合もしくは仕様とみてよさそうです。

https://stackoverflow.com/questions/15480804/problems-displaying-pdf-in-iframe-on-mobile-safari

Android Chrome では表示されない

さらに Android の Mobile Chrome では 1 枚目すらも表示されませんでした。

Android Chrome では表示されない
Android Chrome では表示されない

ちなみに PC の Chrome では正常に表示されます。

IE も表示されない

PC においても IE だと表示されません。

iframe の場合

iframe が IE で表示されない
iframe が IE で表示されない

embed の場合

embed が IE で表示されない
embed が IE で表示されない

Safari(PC) ではツールバーの仕様が異なる

ツールバーの代わりにダウンロードリンクや PDF Viewer への導線が表示されます。

Safari ではツールバーが異なる
Safari ではツールバーが異なる

ブラウザの実装依存な機能に頼るのは怖い

このようにブラウザネイティブな機能に頼るとブラウザの差異が大きく、さらに利用するブラウザによってはユーザーからすれば機能不備と感じられるものもあります。(※ 個人的には、PDF の扱い方が何かで規定されているわけではないのでブラウザベンダの機能不備とは思っていません) そのため全端末・全ブラウザでの安定運用を考えると、ブラウザに頼るのは安定しなさそうです。

Viewer を用意して対応する

そこで Viewer と呼ばれる概念を使った PDF 閲覧の方法を紹介します。

Google Drive

裏技的な方法ですが、Google Drive が持つ PDF Viewer が、クエリパラメータを使って任意の PDF を読めることを利用して、その URL を iframe に埋め込んで閲覧する方法があります。

FYI: https://stackoverflow.com/questions/15480804/problems-displaying-pdf-in-iframe-on-mobile-safari

例えば、 https://ojisan-toybox.github.io/universal-pdf-component/example.pdf という PDF を表示させたければ、

<embed
  src="https://drive.google.com/viewerng/viewer?embedded=true&url=https://ojisan-toybox.github.io/universal-pdf-component/example.pdf"
  }
/>

とするだけで良いです。

この方法では、アプリ開発者側では実装が不要なので、作業工数やバンドルサイズの節約ができます。 しかし、アプリ開発者側で挙動の制御はできないので、細かい要件に対応するには向きません。 またこの方法は IE やモバイルでも対応できる方法ですが、なぜかたまに表示されないという問題があるので、少し安定さには欠けます。

FYI: https://stackoverflow.com/questions/26934520/google-drive-pdf-viewer-does-not-work-anymore-on-android

PDF.js

PDF.jsは Mozilla が開発している、Web 上で PDF を閲覧可能にするツールです。

PDF.js
PDF.js

公式の説明では、

A general-purpose, web standards-based platform for parsing and rendering PDFs.

とあり、PDF の parse と rendering を行ってくれます。 解釈された PDF は canvas もしくは svg に変換されます。 オプション次第では、テキストやリンク情報も保持できるので、テキストコピーやリンクを使った遷移も可能です。

また公式がクエリパラメータを使って任意の PDF を読めるビルド済みサイトを配布しているため、それを自前でホスティングすれば先ほどの Google Drive のような使い方が可能となります。

<embed
  src={`https://ojisan-toybox.github.io/pdfjs-file-viewer/?file=https://ojisan-toybox.github.io/universal-pdf-component/example.pdf`}
  width="500"
  height="375"
>

PDF.js の例
PDF.js の例

IE サポートはあるのか

ただし、IE でのサポートは注意が必要です。

#12602Frequently-Asked-Questions#faq-support を見るに、なんと今の PDF.js は IE サポートがありません。

IEがサポートされていないという比較表
IEがサポートされていないという比較表

PDF.js 公式が配布している Prebuilt (for older browsers) という legacy バージョンも IE サポートがされていません。ためしにその zip の中にある PDF.js で => と検索すると arrow function が見つかります。

legacy 版に arrow が含まれる
legacy 版に arrow が含まれる

ちなみに fgrep した箇所が webpack の変換が効いていそうな箇所であることと最新バージョンでは webpack5 でビルドされていることから、webpack5 がデフォルトで arrow function を吐くようになった ことによる影響かと思い、webpack のビルドオプションを切り替えてビルドしなおせば IE 対応できるのではと思ったのですが、IE サポートが切れる v2.4.456 は webpack4 でビルドされていたのでそれが理由ではなさそうでした。そもそも公式が IE 対応しないと言っているので、最新バージョンを使う以上は IE 対応を諦めるのが吉かと思います。

この arrow は v2.3.200 には入っていないので、もし IE 対応をしたいのであればこのバージョンを使いましょう。 このバージョンで IE 対応した Viewer を用意したので、もし興味がある方はこちらをお使いください。

FYI: https://github.com/ojisan-toybox/pdfjs-file-ie11-viewer

ツールバー

ちなみに PDF.js 公式が配布している Viewer のツールバーは、 Firefox が備えている Viewer のそれと同じデザイン・機能です。 Firefox が PDF.js を利用しているからなのか、PDF.js が Firefox の技術を利用しているのかはわかりませんが、同じ開発元だからと考えられます。

FireFox で開くPDF
FireFox で開くPDF

KAIZEN Sales での選択

さて、これらの実験結果を踏まえて KAIZEN Sales での技術選択を考えます。

要件

KAIZEN Sales では、PDF 閲覧機能を 3rd party script としても配布しなければいけなく、Mobile, IE でも動作させる必要があるため、

  • バンドルサイズを落とすためにブラウザネイティブな機能を使いたい
  • ランタイムでのパフォーマンス向上のためにブラウザネイティブな機能を使いたい
  • そのうえで IE, Mobile でも動作させたい

という要件があります。

実装

そこで、基本的には object タグを用いて PDF を埋め込み、UA が mobile, IE なら Google Viewer 越しに iframe で PDF を埋め込むといった実装をしました。そして表示されなかった場合の保険として、PDF のダウンロードリンクを用意しました。

KAIZEN Sales で見るPDF
KAIZEN Sales で見るPDF

万能ツールである PDF.js に寄せなかったのは、PDF の解釈・canvas/svg への変換をライブラリ越しで行われると、パフォーマンスが落ちるためです。また 3rd party script としても配布するため、PDF.js をバンドルに含めたくないという理由もあります。(※ これは iframe 超しに PDF.js の viewer を呼べば防げます。ただし別のサーバーにホスティングする必要があり、当時の開発リソース的な問題でこの選択をしませんでした。そのため開発リソースに余裕が出たフェーズで Google Drive から PDF.js への移行する予定です)

ツールバーの制御

Chrome では一定以上の width になるとツールバーが表示されます。 Chrome に備わっているツールバーは PDF の minimap のようなものが左側に表示されるため、PDF の閲覧領域が狭くなり、これを無くして欲しいと言う要望がありました。 これはブラウザネイティブな機能であるため難しそうに思えますが、実は PDF の URL へのクエリパラメータで制御できます。

たとえば URL に #view=FitV&pagemode=none&toolbar=0 をつけるだけでヘッダや minimap を消せます。 このような機能はブラウザの実装依存な機能であるため、この方法に気づくには苦労しました。

ちなみに PDF.js で同様のことをしたい場合は、PDF.js の Viewer の UI(PDF 除く)が DOM であることを利用して、PDF.js の Viewr それ自体の HTML・CSS を直接いじることで制御できます。 もっとも、PDF.js のツールバーは表示領域を圧迫しないのでこの対応は不要かと思います。

おわりに

iframe, object, embed を使った PDF の表示については各ブラウザの実装依存であるため、各ブラウザを使った時の差異の把握に苦労しました。 しかし、3rd party の code を頼らずに PDF を表示できるのがこの方法の強みではあるので、ブラウザの各挙動の勉強と追従を頑張り、このやり方を続けたいです。 もし私たちと同じようにブラウザネイティブなやり方で PDF を表示させたい方にとって本記事が役に立ってくれれば幸いです。

とはいえバンドルサイズ・ランタイムでの変換コストを考えずに全環境での PDF 表示をしたいのであれば、古い PDF.js を使って PDF を表示させるのが一番良いと思います。

We're hiring!

PDF や 動画の最高の視聴体験を一緒に作りませんか?

エンジニアインターンで開発を通して学んだこと

Kaizen Platformでエンジニアインターンをしているyabanaです。

インターン生活全般に関する記事は

インターン生活を振り返って - Kaizen Platform 開発者ブログ

でfunakoshiさんが書いているので、今回は私が実際に行ったKaizen Adというプロダクトの開発についてご紹介します。

Kaizenでエンジニアインターンするとこんなことができるんだ!という参考にしていただけると幸いです。

概要

Kaizen Adは、動画広告を作りたい人(広告主や代理店)が、制作指示書(動画の仕様書に相当するもの)を作成して素材データと共にアップロードすると、GH(Growth Hacker)ネットワークに参加するクリエイターが原則5営業日で動画を制作・納品してくれるサービスです。

Kaizen Ad のサービスサイトはこちら: https://kaizenplatform.com/video

制作指示書の部分には、基本情報、納品期日、制作内容、追加オプション、発注内容の確認、という5つのページがあり、これらの項目名と、現在自分がどの画面にいるのかをステッパーと呼ばれるコンポーネントで表しています。

f:id:kaizenplatform:20210623154658p:plain
赤枠で囲った部分がステッパーです。改修前は制作指示書の各ページの左上に固定されていました。

今回は、このステッパーの改修を行いました。

改修のきっかけ

ステッパーの改修を行ったきっかけは、CS(カスタマーサクセス)の方から

「制作指示書を書いている時に、ページ下部から2つ前、3つ前のページに戻りたいことがある。画面下部に「2つ前に戻る」「3つ前に戻る」ボタンを追加してほしい」

という要望をいただいたことでした。

提案いただいたボタンを増やす方法だと、今後スマートフォン対応をしていく上で画面の多くをボタンが占めてしまう、という問題がありました。 一方で、これまでのステッパーには、現在地より前のページタイトルを押すとそのページに戻れるという機能がすでに存在していました。

そこで、今回の問題の解決策として、

  • ステッパーを画面下部で使えるようにする
  • 現在地より前の各ページタイトルに押せそう感を出す

という改修を行いました。

実装とフィードバック

上記の解決策のうち、1つ目の「ステッパーを画面下部で使えるようにする」については、

  • 左上のステッパーをスクロールに追従するようにする
  • 画面下部に横向きのステッパーを追加する

という2種類を実装しました。また、2つ目の「現在地より前の各ページタイトルに押せそう感を出す」については、色の変更やマウスホバー時に太字にする、アンダーラインをつける等を行いました。 これらを一通り実装完了後にPMやCTOと開発環境でデモを行い、実際の使用感を確認しました。

結論としては、縦横のステッパーが一画面に収まると混乱してしまう…ということで、下部ステッパーについては今回は見送りとなり、最終的に以下のような改修に落ち着きました。

f:id:kaizenplatform:20210625101726p:plain
ステッパーをスクロールに追従させることで、画面下部でも利用できるようになりました!

リリース前最後に、最初に要望をいただいたCSの方に実際にステッパーを触っていただくデモ会を行いました。今回初めてユーザーの方から反応をいただく体験をしたのですが、自分の行った改修に「使いやすくなった!」「ありがとうございます!」という声を直接いただけて、めちゃくちゃ嬉しかったです!

その後、下部ステッパーの削除と微調整を行い、無事に本番環境にリリースしました🎉

開発を通して

今回の改修では、まず「要望をどう解決するのが最善か」という視点の大切さに気づかされました。 ボタンを増やすのではなくステッパー改修をする、という方向性はPMが決めてくださったのですが、今後自分がエンジニアになる上でこのような判断ができるようになりたいなあ、と思っています。 私自身はKaizenでしかインターンをしたことがなく他と比べられないですが、「〇〇を実装して欲しい」ではなく、こういうバックグラウンドから共有してもらえるのはとてもありがたいですし、モチベーションにも繋がります!

また、フィードバックやデモ会などを通して、インターン生である私も対等に意見を言い合えるので、一緒にプロダクトを作ることができるのも魅力的です。実際に、色の変更等で私の意見を採用していただいた部分もあります。

おわりに

個人的な話になりますが、当時就活中で今後エンジニアになるのか、他の道を行くのか悩んでいた私にとって、今回のCSの方から実際に使用感を伝えていただいたことはとても大事な経験となりました。この経験と、毎月のメンターとの1on1で話をする中で、

「自分は誰かに喜んでもらうためのものづくりがしたい」

という気持ちに気づくことができました。Kaizenで実際にプロダクト開発に関われたからこそだなあと思っています。

私と同じように「エンジニアって気になるけど自分がなれるのかな、合ってるのかな…」と思っている方、絶対に良い経験になると思うのでおすすめです!

インターン生絶賛募集中ですので、とりあえず話だけきいてみたい、という方もぜひこちらをご覧ください!

hrmos.co

Kaizen Adのフロントエンドアーキテクチャの遷移について

Kaizen Platformで主にフロントエンドを開発しているyuki-yanoです。
TypeScriptが好きで、最近はZennにDenoでzshのプラグインを作った記事を投稿しました。

今回はKaizen Adというプロダクトにおけるフロントエンドのアーキテクチャの遷移について紹介します。
Kaizen Platformでは2019年に React + GraphQL から成る Kaizen Ad のフロントエンド - Kaizen Platform 開発者ブログ という記事を書いています。
その後、プロダクトが成長するにつれて課題なども出てきており、現在の実装方針は変わってきています。

この記事では現在のアーキテクチャと、どういう経緯があって変遷してきたかについて紹介します。

これまでのKaizen Adでのフロント開発

これまでの実装は 前回の記事 に書いている方針で進めてきました。
基本的にレイヤードアーキテクチャを前提としており、Viewの世界の中だけではなくアプリケーション全体としてView, Usecase, Entityというレイヤを切る規約で強めに縛った設計となっていました。

レイヤードアーキテクチャによる開発は当然様々なメリットもありました。例えば

  • アーキテクチャが共有されていれば誰が書いても近いコードになる
  • ドメインに関わるStateを扱う処理をどこで実装するかが明確(Viewは基本的に関与しなくていい)
  • Serviceのレイヤが抽象化されているのでViewではEntityを取り回すだけで、内部実装などは一切知らなくてよい

などといったものです。

以下が前回の記事で概要の説明に使っていた画像です。
それぞれについての詳細は 前回の記事 を参考にしてください。

20210609115405 20210609115420

現状の課題

プロダクトが成長するにつれて、当時の設計では解決の難しい問題がいくつかでてきました。
具体的に書くと以下になります。

  • レイヤードアーキテクチャの実装手法によるGlobal Stateの肥大化
  • コンポーネントのレイヤリングによる巨大なProps Drilling
  • エンティティの肥大化に伴うGraphQLクエリのサイズ増加

それでは、それぞれの課題の詳細と現在の方針について説明します。

レイヤードアーキテクチャの実装手法によるGlobal Stateの肥大化

これまでの課題

前述の通り、Kaizen Adではレイヤードアーキテクチャを用いて開発しています。

これは大雑把に言うとRedux Thunkを用いた状態管理に近いものとなっています。
UsecaseはThunk Actionと対応しており、Usecase内のビジネスロジックから各種ActionがdispatchされてGlobal Stateが更新されます。

これにより要件が複雑なUIによる入力・影響範囲がUsecaseで処理されることによるGlobal Stateの複雑化が問題になりつつあります。
UsecaseはGlobal Stateと密結合しており、内部ではState全体へアクセスできるインターフェイスが多用されています。
Viewの1つの操作が大量のActionをdispatchしてしまい、Global Stateのどこに影響を与えるか分かりづらくなりがちです。
そのため、再レンダリングの制御も難しくなってしまっています。

また、本来であれば特定のフォームに閉じるようなStateであっても、エンティティに関わる状態管理には基本的に全てUsecaseを通すという制約の影響で実装には多くのファイルに変更を加える必要があります。

現在の方針

これは最近のReactのプラクティスにも繋がりますが、Global Stateだけではなく適切な粒度でLocal Stateを作成し、その中でStateを管理していきたいと思っています。
具体的には大きな方針としてはGlobal Stateが必要ない場面ではUsecaseを使わないようにしていく、ということを考えています。
今までUsecaseに記述していたビジネスロジックについてはCustom Hooksへと移行を進めています。

また、Global Stateの肥大化については「コンポーネントのレイヤリングによる巨大なProps Drilling」にも関わってきます。

コンポーネントのレイヤリングによる巨大なProps Drilling

これまでの課題

useContextで途中からStateを使うのはよくないパターンで、極力Propsを使うべきという意見も見かけますが、Kaizen AdではProps Drillingが問題となっています。

前回の記事には書かれていない内容なのですが、Global StateとUsecaseの扱い方としてRouter直下のURLを管理するレイヤ(pages)でコンポーネントに注入する必要がある、という規約がありました。
例を挙げると pages -> organisms -> atom, molecules というレイヤリングでViewが構築されています。

どれだけ複雑なViewだったとしても、pagesという最上位のレイヤでしかStateとUsecaseを注入していませんでした。
それによって、例えばページ全体の末端で制御するモーダルについてもStateとHandlerを最上位のコンポーネントから全てバケツリレーするという構造になっています。

多いところでは50個前後のPropsが何重にも渡されており、そのうち30個がHandler、20個がState、のような状態となっています。
このような状態では処理を追加する際に大量のファイルに変更を加える必要があります。また、Handlerがどのような処理と対応しているかについて何重もコンポーネントを遡る必要もあります。

現在の方針

解決法としては大きく分けて2つ考えています。

1つは前述したGlobal Stateからの脱却と並行してLocal Stateを適切に用いる事で、そもそもある程度任意の単位でStateを管理するようにする、というものです。
もう1つは単純にpagesレイヤ以外からの注入を許容します。

後者についてですが、何も考えず無秩序にGlobal Stateへのアクセスを濫用すると構造が破綻するという問題があると思います。
なので、全てのコンポーネントでStateへアクセスするのは許可しません。

しかし、Viewの末端のモーダルで使うStateやUsecaseを最上位からPropsとして渡すのは明らかに無駄が多いです。
そういった部分ではモーダル全体を囲うコンポーネントのレイヤでのState, Usecaseの注入を許容するように方針の変更を進めています。

エンティティの肥大化に伴うGraphQLクエリのサイズ増加

これまでの課題

既存のアーキテクチャではどのViewでも使えるように全てのプロパティを持っていることが前提のエンティティという巨大なオブジェクトをStateで取り回しています。
Kaizen AdではバックエンドのAPIにGraphQLを使っているのですが、その際にこのエンティティを生成するコストが問題になります。

複雑なドメインを持つオブジェクトの全てのプロパティを取得するという設計上、GraphQLのクエリ及びレスポンスが肥大化し、バックエンドの処理も時間がかかっています。
この使い方だと毎回同じFieldを送るという実装のため、本来のGraphQLの目的からも乖離してしまっています。

具体的に一部の数字を出すと

  • GraphQLのField: 700行前後
  • レスポンスが返ってくるまでの時間: 1秒強
  • レスポンスのサイズ: 40kb前後
  • prettierをかけたレスポンスのJSONの行数: 1万行前後

となっていました。

これがページ遷移の度に発生しているのでUXについても問題が出てきています。

現在の方針

一部については自分が改修を進めており、予想通り単純に不要なFieldの削除が最も効果的でした。
しかし、既存の実装については全てのプロパティが存在する前提となっており、一部を削除した際どこに影響が波及するか調査する必要があるの慎重に進める必要があります。

明らかに不要な部分を削除することで、画面遷移を1-2秒高速化できた部分も既に見つかっています。

また、自分が試した改善案として

  • ファーストビューに必要な要素だけを取得するFieldを作成して最初に取得
  • Viewのレンダリングをブロックせずに裏で別のFieldから取得したデータをエンティティに注入する

というものがあります。

これについても影響範囲の調査は必要なのですが、エンティティの構造自体には大きな影響を与えずに実装できる可能性があり、引き続き検討していきたいと思っています。

フロントエンド勉強会について

Kaizen Platformでは去年の8-9月から2週間に1度フロントエンド勉強会を開催し、知見を共有しています。
当初は読書会から始まり、現在は得た知見を持ち寄って話し合う時間になっています。
開催前はバックエンドエンジニアの方などがReactについてあまり知見がないという状況だったのですが、現在はフロントエンドのBetterな書き方についてお互いに議論できるような状態となってきています。

この記事のアーキテクチャの改善についてもこの勉強会から生まれたものであり、多くのエンジニアとReactの知見を共有することで問題点の共有・発見へと繋がりました。
この取り組みについては今後も続けていきたいと考えています。

最後に

当時のアーキテクチャから生まれたいくつかの課題はありますが、Kaizen Platformでは積極的にそれらの課題に対応しつつフロントエンドの開発を進めています。
プロダクトの成長につれて新たな課題が生まれていくかもしれませんが、それらについて議論しつつ改善していく環境は整いつつあり、今後も積極的にそれらの知見について発信していければなと考えています。

We're hiring!

Kaizen Platformで一緒に働いてくれる方を絶賛募集中です!

話を聞いてみるだけでもいいので、ご興味ある方はぜひ!

hrmos.co hrmos.co hrmos.co

Kaizen Platformのデータ処理基盤「Data Platform」のご紹介

はじめまして。Kaizen Platformのアプリエーションエンジニアの眞井(@yasu01)です。
今回は、私が2020年を通して開発してきたData Platformというデータ処理基盤についてご紹介します。

背景

Kaizen Platformの創業当時から提供しているサービスとしてKAIZEN ENGINEというものがあり、お客様のサイトにJavaScriptのコードを設置することで、サイトへアクセスしたエンドユーザーのサイト行動ログをデータレイクに蓄積される仕組みを提供しております。
そのデータは膨大で、そこで蓄積されたデータを分析することで、Site AnalyticsLift Reportなど様々な集計を施し、これまで様々なレポートを生成・提供してきました。

続きを読む

共同研究体験記: 広告再生回数と効果低下の関係

こんにちは❗️筑波大学システム情報工学研究科社会工学専攻2年の渡邊と申します。2020年4月から現在に至るまでKaizen さんと共同研究をしていました。

2月に修士論文として提出し、 3月には卒業予定である為、 最後に携わらせて頂いた研究についてせっかくですので振り返らせて頂きます🙆🏻‍♂️

きっかけ

昨年3月までは最適化系の研究をしており、4月から新しい研究テーマを探していた中同じ研究室の先輩から「技術面に明るい人が多い+仕事のような姿勢で研究できる」とお勧めされました。  

当時データ分析関連の研究に興味はあったもののロクにSQLも触ったことがなく、Pythonを動かせる程度の自分にとって「何でもすぐに質問できる」という環境もありがたかった為、指導教員にお願いして4月からチームに加入させて頂けることになりました❗️

続きを読む