Storybookをデプロイした話
そもそもなんでStorybook入れたのか?? Storybookの目的とは
- 社内でのUI仕様の確認
- 色んな人が、UI仕様を簡単に確認できる環境があったら便利。
- 例えば、ユーザがこのステータスのとき、配送日変更できるのかとか、すぐ知りたいときに、ユーザの状態を再現しないと確認できないのは避けたい
- 開発効率改善
- UIの再現が楽なので、UIの開発が効率が上がる。具体的には、修正とレビューとテストが楽になる。
- デプロイに関係するところで言うと、PRを楽にしたかった。
- PR作成時に、完成形のスクリーンショット貼るのがしんどい。
- PRレビュー時に、ローカルにクローンして、レビュイーと同じUI環境を再現するのがしんどい。
デプロイ環境の選定
要件を満たすためしたいデプロイ環境は2つ
- 色んな人が見れる、mainブランチのStorybookのデプロイ
- 社外の人から見れない
- PRごとに作成される一時的なStorybookのデプロイ
- 複数のPRがあっても、かぶらないこと
Storybook デプロイ環境の選定をしていく
ちなみに、Storybookは、サーバサイドを伴わない、ただのReact(html + js + css + assets)
先に結論! Storybookのデプロイ環境は、chromaticと、Github Pagesと、ホスティングサービス(AWS S3, Firebase Hosting )などが考えられ、今回はFirebase Hostingを選定した
- chromatic https://www.chromatic.com/
- 特徴
- 公式で案内しているStorybookデプロイ環境
- チームのみがアクセスできるStorybook環境
- 簡単デプロイ & 簡単テスト: デプロイ時、毎回スナップショットをとって、自動差分テストしてくれる (スナップショット月5000枚まで無料)
- デザイナーさんとコミュニケーション取りやすい。 デザイナーさんとコミュニケーション取るため、UIにポストイットようなコメントを貼れる
- 選定結果→不採用
- 個人的に好みではないコンポーネントの選択のUI。Storybookのサイドバーのアコーディオン形式のディレクトリ表示が好きだったのに、ディレクトリごとにページが有るのは触りづらい
- chromaticは、リッチすぎる。デザイナーさんとのコミュニケーションは課題ではない
- フリープランは容易に使い切って課金になりそう。
- → リッチすぎるので、使わない
- 特徴
- Github Pages
- 特徴
- Githubのサブな機能で、ホスティングできる
- リポジトリとまとめて管理できるので、リソースが散らばらないのはかなり良い
- ただ、現状のプランだと社外から丸見え。見えなくするには、エンタープライズのプラン変更でお金かかる
- 選定結果 →不採用
- Storybookのためにエンタープライズのプラン変更するのはきつい
- 特徴
- その他ホスティングサービス (AWS, Firebase)
- AWS
- AWSは、ぐぐるとよく出てくる
- ampfliy とか S3 とか https://qiita.com/amagurix/items/492affc43dc8b1ac2588 https://zenn.dev/cumet04/articles/private-storybook-on-pullreq
- Firebase
- Firebase Hostingとか
- awsデプロイ記事よりは記事が多くないが、問題なくできそう
- Github ActionでPRごとにhostingする機能もある&
- 選定結果 → Firebase Hostingを採用
- 使い慣れているので、Firebase Hostingを採用する。
- ホスティングサービスはどこも正直似たりよったりだと思っているので、慣れているのを使うのが一番良い
- AWS
mainブランチのデプロイ
構成
社内の色んな人が見れて、社外の人から見れないを実現するためには、認証機能が必要 Basic認証に成功したら、storybookを返す関数をfirebase functionに実装した
flowchart LR
subgraph GST[Firebase]
FCF[Firebase Cloud Functions]
Auth["認証(express)"]
SB[Story Book]
end
User --> FCF
FCF --> Auth
Auth --> SB
やり方
実装
以下のライブラリを使って、ベーシック認証と、静的ファイルの配信を行っている
express
basic-auth-connect
expressなくてもベーシック認証ができるが、静的ファイルの配信をするのが簡単にできるので採用している
また、Cloud Functionsでは、expressをそのまま返すことができるので、簡単に使えて便利
import * as functions from 'firebase-functions'
import * as express from 'express'
import * as basicAuth from 'basic-auth-connect'
// 認証情報
const userName = '****'
const password = '****'
const app = express()
// 認証を付ける
app.all(
'/*',
basicAuth(function (user: string, pass: string) {
return user === userName && pass === password
}),
)
// storybookを、静的ファイルとして返す
app.use(express.static(__dirname))
// 関数を公開する
export const storybookWithAuth = functions.https.onRequest(app)
参考:
ビルド
- storybookを、functions/libにビルド結果を出力(ここがポイント。)
- cloud functionsをビルド
- functions/libをデプロイ
↑をGithub Actionでmainにマージタイミングで走るようにする
詰まった箇所
storybookのビルド出力先が味噌。
- 最初は、functions/lib/publicやfunctions/lib/staticにビルド結果を出力してた。Storybookのアセットのパスが解決できず no imageになっていた。
- storybook内で使っている画像などアセットパスが、expressの静的ファイル配信場所に合わせて変更できない
- 以下の方法でかいけつ
- ファイル直下を配信する
app.use(express.static(__dirname))
- storybookのビルドの出力先を、
functions/lib
にする
- ファイル直下を配信する
Firebaseの設定ファイル書き方に気をつける
Firebaseの設定ファイルの書き方が悪くて、Github Actionで、デプロイされないことがあった。何度書いてもミスする不思議。
2 PRごとのデプロイ
公式Firebase Hostingで紹介されているGithubアクションをそのまんま利用
- https://github.com/marketplace/actions/deploy-to-firebase-hosting
- PRごとに期限付きの独自URLが作られる。
構成
flowchart LR
subgraph GST[Firebase]
FH[Firebase Hosting]
SB[Story Book]
end
User --> FH
FH --> SB
まとめ&今後
- Firebaseの機能を使うことで、高いお金を払うこともなく&セキュリティを損なうこともなく、Storybookでデプロイしたい要件を満たすことができた。
- Firebase Cloud Functionsはやっぱり便利。Firebase Cloud Functionsとexpressの相性がいいのを知らなかったので、知れてよかった
- 現状、Storybookを使い始めたばかり&エンジニア内でしか使っていない。Storybookがもっと全社的に使われるorデザイナーさんとコミュニケーションとして使うなど、文脈が変わってくれば、もしかしたらデプロイ環境を考え直す必要があるかもしれない