新型コロナダッシュボード爆速リリースの舞台裏 〜小さく始めて大胆に変えるフロントエンドプロジェクト〜

JX通信社CDOの小笠原(@yamitzky)です。

JX通信社は「今起きていることを明らかにする報道機関」というミッションの元に、新型コロナリアルタイムダッシュボードを 2月16日 から提供し続けています。今回は、「新型コロナプロジェクト」の発足から現在に至るまでの、プロジェクトの進化についてご紹介します。

f:id:yamitzky:20200529014632p:plain

プロジェクト発足

そもそものプロジェクトの発足としては、2月14日の下記のツイートが発端でした。およそ 2 日でリリースしたことになります。

当時は、東京都のような自治体公式の特設サイトや、国内全体の動向をまとめたサイトはほぼありませんでした。国内の公共機関・報道機関のなかで、かなり速くリリースできた部類に該当するかと思います。

フェーズ1: Vue.js でのプロトタイピング

現在は React で作られているプロジェクトですが、当初のプロトタイピングフェーズでは Vue.js を使っていました。

Vue.js は Progressive(漸進的) なウェブフロントフレームワークです。最もシンプルなのは、次のような単体の HTML として配信する方法です。この形式ではトランスパイル(Webpack によるビルド等)の必要もなく、HTML 単体を配信するだけでも SPA になり、非常に手早くプロジェクトを開始できます。

// index.html
<html>
  <body>
    <div id="app">{{ message }}</div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
      new Vue({
        el: "#app",
        data: () => ({ message: "hi" })
      })
    </script>
  </body>
</html>

今回のプロジェクトでは「なるべく早くリリースする」という目的があり、ツール周りでつまずきたくなかったので、まずは Vue.js で雑にプロトタイピングしました。

フェーズ2: React × TypeScript × Emotion × Parcel への移行]

f:id:yamitzky:20200529030051p:plain

プロトタイプとして実現したものの、NewsDigest のウェブメディアで配信したいという課題がありました。NewsDigest の CMS は、Django (Python) で管理しており、必ずしも SPA との相性が良いとは言えません。

そこで次のような形で、 CMS にたった 2 行のタグを埋め込むだけで新型コロナのダッシュボードを配信できるようにしてみました*1

f:id:yamitzky:20200529020642p:plain

技術的な必要条件としては、

  • 1 つの script タグに JavaScript の依存関係を全て詰め込む
  • 1 つの script タグさえ埋め込めば、デザイン(stylesheet) も再現できる

です。これを最短で実現するため、バンドラー(Webpackの代替)として Parcel を利用しました。Parcel はほぼ設定不要で使えるバンドラーなので、今回のような急ぎのプロジェクトにピッタリです。また、CSS ファイルに依存せずデザインを指定するため、Emotion も使っています。Emotion は React と相性の良い TypeScript ベースのデザイン用ライブラリで、JS 内にデザイン指定を埋め込めます。

// index.tsx
const Wrapper = styled.div({
  padding: 8,
  color: 'red',
  display: 'flex',   // vendor prefix も自動付与
})
export const Component = () => <Wrapper>some content</Wrapper>

つまり、プロトタイピングから本番配信までの間に、Vue.js から React・TypeScript にまるっと置き換えたことになります*2

フェーズ3: gitでのデータ管理から、サーバーレスなデータ管理へ

新型コロナの感染者数字は、リアルタイムに更新されます。実は、プロジェクト初期ではデータベースを用意しておらず、TypeScript のソースコード内に直書きする形でデータ管理をしていました。数値更新するたびに GitLab CI が動いて、JS ファイル自体をデプロイしていたのです・・・!

f:id:yamitzky:20200529130138p:plain

// 実際の data.ts
export const data = {
  modified: '2020.02.19 11:30',
  japanStats: {
    infected: 616,
    infectedChange: 96,
    dead: 1,
    deadChange: 0,
    // クルーズ船(ダイヤモンド・プリンセス号)
    cruiseInfected: 542,
    cruiseInfectedChange: 0
  },
   ...
}

さすがに、機械化しづらい、型安全でなく事故が起こりやすい、git でデータ更新をするのは属人性が高い、Python 製の FASTALERT API としての提供が難しい... という事情もあり、Google Spreadsheet をデータベースとして利用し、JSON から使えるようにしました。また、データ更新の仕組みは GitLab CI ではなく、AWS Lambda でサーバーレスな形で動かすようにしています。

f:id:yamitzky:20200529130900p:plain

フェーズ4: Next.js での SSR への移行

<script> タグでの配信は、SEO 的に弱い可能性があるのではないか、という懸念が生まれました。そこで当初の Parcel から、Next.js へ移行しました。Next.js はReact のフレームワークで、サーバーサイドレンダリングの機能などが組み込まれています。

この Next.js のサーバーは、Amazon ECS(Fargate) 上にデプロイしています。

フェーズ5: ウィジェットとしての外部提供と yarn monorepo 化

JX 通信社が提供している新型コロナダッシュボードは、メドピア社LINE社にウィジェットとしても提供しています。また、NewsDigest だけでなく FASTALERT 内でも配信しています。NewsDigest での提供、ウィジェットとしての提供、FASTALERT 内での提供、、、これらを 1 プロジェクトでやるのは現実的ではなかったため、次のような monorepo 構成を行っています*3

f:id:yamitzky:20200529164607p:plain

@corona/components ・・・ 各種グラフを提供するライブラリ。FASTALERT からも npm install している
@corona/server ・・・ @corona/components に依存する、next.js のプロジェクト。newsdigest.jp 用
@corona/widget ・・・ corona/components に依存する、webpack のプロジェクト。ウィジェット配信用

この monorepo 構成は、yarn の workspace 機能を使っています。

また、@corona/server と @corona/widget では、ビルドツールなども異なっています。これは、プロジェクトの目的や、求められる安定性*4などに応じて、意図的に使い分けたものです。

うまく monorepo 構成にすれば、コードを使いまわしつつ、最適な技術選定ができるようになります。

フェーズ6: グラフライブラリの移行

プロジェクト当初は、chart.js と d3 などを使っていましたが、 vx という React 向けのグラフライブラリに移行しました。

Chart.js は canvas ベースのグラフライブラリです。しかし、 React のような宣言的な UI の思想との相性の悪さや、サーバーサイドレンダリングできない、柔軟にカスタマイズできないなどの課題がありました。

d3.js もデータ可視化に使っていましたが、厳密には「ドキュメントをデータに基づいて操作・構築するためのライブラリ」です。雑に言えば「DOMを操作するためのライブラリ」なので、 React(React-DOM)のような DOM 操作のライブラリと、役割的にかぶっています。

そこで、 React ベースの低レイヤーなデータ可視化ライブラリである vx に移行し、React だけで SVG での可視化をしています。

  • React 的な、宣言的データ可視化の実現
  • TypeScript の型
  • 柔軟なデータ可視化の実現

などができるようになりました。一方で、パフォーマンスチューニングや、コード量の増加などは起きています。

f:id:yamitzky:20200529115825p:plain
凡例や横軸の位置など、かなり微調整しています

まとめ

新型コロナウイルスに関しては、社会的な需要や、刻々と変わる状況などを踏まえ、かなりスピード重視でプロジェクトが発足しました。当初は git でデータ管理していたほど、一般的なアプリケーション構成のセオリーからは外れた作り方です。

一方で、新型コロナダッシュボード提供開始から3ヶ月経ち、ビジネスの状況に応じて大規模なアーキテクチャ変更(式年遷宮)を行っており*5「ビジネスとスピードと品質の両立」も実現できたと思います。これらの両立のポイントは「ビジネス要件に合わせて技術を使いこなし、いかに漸進的に進めるか」です。

f:id:yamitzky:20200529132312p:plain

今回のプロジェクトでは、かなりインターン生に協力いただいています。本当にありがとうございます! 引き続き、フロントエンドのエンジニアインターンを募集中です(まだ新型コロナは収束していないのでリモート中心です)。ぜひ一緒に、インパクトのある開発をしましょう!

*1:この JS 自体は、Amazon S3 と CloudFront で配信しています

*2:同じようなことを実現する方法は他にもあります。この技術選定は、慣れの部分が大きいです

*3:データ更新用サーバーレスパッケージなども含めると、10 個ほどパッケージが含まれています

*4:ウィジェットは絶対にサーバーを落としたくないため、SSR は行っていません。また、顧客ごとにカスタマイズする関係で SSG にもしていません

*5:プロジェクト発足から 3 ヶ月ぐらいしか経っていないのに、1000行以上差分のあるプルリクが複数あります。同僚から「走りながら車輪を全交換した話」というタイトル案が出るくらいです