なぜ僕が「SPAはコストが高い」と考えているのか


どうもみなさんこんばんは
ちょっと前に「個人開発者やスタートアップの初期からSPAで開発するのはコスト高いっすよね」みたいな事を書いたらフロントエンドエンジニアの皆様からバチバチに叩かれた僕です

彼らには彼らの考えがあるのでそれはどうでもいいのですが、どういう理由があってその発言をしたのか~と言う部分が気になっている方もいたようなので説明しておこうと思います

ちなみに今でも全く意見は変わっておらず、この発言に同意できるかできないかは単純に視点の違い、規模の違い、スキルの違いだと思ってます

追記: もちろんSPAじゃないと実現できないようなサービスを作りたい場合はSPA一択ですし(インタラクティブにHPつくるサービスとか。でも世の中の95%くらいのサービスはそうじゃないと思います)、サイトの利用はログインした人にだけ提供するような業務系ツールなどはまた話が別です

前提の話

こういう記事ではコンテキストが重要なのでしっかりめに書いておきます。僕が考えているのはこんな状況です

  • SPA、MPA = この記事ではjsのフレームワークで書く今風のviewを全部ざっくりまとめてSPAと呼びます。反対に昔ながらのサーバーサイドで吐き出すviewの方式をMPAと呼びます

  • 個人開発、スタートアップ初期 = エンジニアのスキルが比較的高くない / あまり時間をとってしっかり作り込めない / 関わる人数が少なくコードレビューなどが存在しない

追記:上記で想定しているフェーズ=PMFを確認したり、MVPで作り捨てを意識するような初期半年~長くて1.5年くらいの話で、本腰を入れて開発するよりもスピード感を持ってチャレンジを繰り返したいフェーズを意識しています。それを越えても存続してるなら超優秀なエンジニアやとってプロダクトに完全に合う形に作り直してください(そもそも上場するくらいまでならモノリシックなMPAで充分という突っ込みもあると思いますが)

  • 開発するサービス = よくサンプルとして取り上げられるホームページに毛が生えたようなものではなく、ログインやユーザー登録が存在し、ログインユーザーと非ログインユーザーでデータの出し分けがあるようなもの。ここ(zennさん)とか、twitterさんとか

追記:バックエンドが必要がないようなサービス、全てをFirebaseでどうにかする覚悟を持っているなどの場合は以下で書くSPAのコストは非常に低くなると思いますので、個人的にはその道は超ありだと思います。ただそこまで割り切れるほどまだ環境が成熟してないかなーと言う気持ちもあります(Firebaseを本番採用している事例と普通にバックエンドを置いている事例では数が桁違いだと思いますし)

  • お前は誰だ? = Railsを11年ほど。Reactは5年くらい。業務でNext, Nuxt、趣味でRiot, Svelteを触っているバックエンドエンジニア。バイタリティもスキルもそこまで高くない、よくいるタイプの人

1.そもそもコストって何?

今回の一連の流れで一番視点が違うなーと思ったのがこの点で、僕が考える コスト には以下のようなものが含まれていて

  • エンジニアの採用のしやすさ
  • サービス開発の 初速
  • サービス開発の 継続性
  • 分業のしやすさ、手伝ってもらいやすさ
  • web標準の挙動の実現のしやすさ
  • セキュアなデータを流出する可能性の高低
  • バグがあった時の気づきやすさ / 対応のしやすさ
  • ドキュメントの多さ

「viewを書く」という点だけに絞れば、MPAとSPAでどっちが良いか?と言われると以下の4項目全てでSPA側が勝利すると思います

  • エンジニアの採用のしやすさ(諸説あるけどSPAの方が人は多そう)
  • サービス開発の 初速
  • サービス開発の 継続性
  • 分業のしやすさ、手伝ってもらいやすさ

しかし逆に以下の4つの項目に関しては 「比べ物にならないくらいMPAの方が楽」 だと思っており、上の全メリットを捨ててでも選ぶ価値のあるポイントだと個人的には思っています

  • web標準の挙動の実現のしやすさ
  • セキュアなデータを流出する可能性の高低
  • バグがあった時の気づきやすさ / 対応のしやすさ
  • ドキュメントの多さ

他にもSPAに対して「SEO対策が万全ではない(Googleは対応していると言いつつ他は?)」とか「サードパーティのヒートマップツール等、ビジネスツールが対応していないものが多い」とか人によっては致命的な場合もあると思うのですが、そこら辺はケースバイケースなので減点要素としてはカウントしません

2. 「viewの書きやすさ」は圧倒的にSPAが上

僕がRailsユーザーなのでRailsの話をしますが、Railsでviewを書く辛さと言うのはすごく感じていて、例えばcontrollerと1:1で結びつく形の設計なので無駄にファイルが増える点とか、scoped cssが存在しない事によってcssに何かしらのルールを持ち込まないと一瞬で負債化するとか(今はtailwindがあるけど)、DRYを推奨してはいるけどそもそもcomponentという概念で作られていないので(render, view_componentなどはありつつ)viewの組み立てがしんどいとか、テーブルのソート程度の事でもjqueryプラグインを探してくるか自分で書くか、ページ遷移が必要になる~とか本当に色んなしんどさがあります

従ってviewをサクサク書きたいとか、継続的にいい感じにしたいとか、破綻しにくいとか、そういう面で言えばSPAの方が圧倒的に体験がよく実際に書いていて楽しいのもそっちです。この点は一切否定する気は無いですし、Railsもこれくらいサクサク書けたらなーと毎日思ってます

3. web標準の挙動の実現のしやすさ

この点は賛否あると思いますが僕は個人的に非常に重視しています。SPAが嫌いな人になぜ嫌いか聞くと大抵の人がこう答えるのではないでしょうか?

「なんかスクロールが変になったり、戻るボタンを押したらログインしてない事になったり、バグが多い」

実際に例として上げてしまってめっちゃ申し訳ないのですが、SPAとして非常にクオリティが高いここzenn.devさんでも「Popular Topics」→「○○をもっと見る→」を押した後に戻るボタンを押すとスクロールバーの位置がおかしくなる~と言ったバグがあります

ちょっと前ですがpixiv sketchさんでもログインして戻るボタンを押したらログインしてないことになってバグる~とかありました

正直些細な点ではあるのですが 「思った挙動と違う動きをするストレス」というのは「無駄にページ遷移するオールドなMPA」よりも遥かにコストが高い と個人的には思っています

更に今回の想定のように、そこまでスキルが高くない人が作ったSPAではガンガン非同期で読み込む事によりうまくスクロール位置が保存できずにずれまくる~とかそういうミスが多発しがちです

MPAなら?当然ですがそんな事は起きません。昔ながらの古臭い遷移で動いてくれるので初心者が作ろうとプロが作ろうと「web標準の挙動」を当たり前に0コストで実現できます

勿論SPAでもできますが +αではなく標準 を実現するだけでもテクニックやある程度の能力が必要というのはコストが高いと思っています

4. セキュアなデータを流出する可能性の高低

ここらへんはどっちかというとサーバーサイドさんの問題なんですがSPAを採用しなければ発生しない問題なので含ませて頂きます

SPAと言うのは基本的にサーバーサイドのAPIを叩いてデータをもらってきてそれを組み合わせてviewを形成するという仕組みなんですがその際にミスが起こりやすいです

例えば以下のデータがあった場合に……

{
  "user": {
    "id": 1,
    "name": "tanaka",
    "email": "[email protected]",
    "last_access_ip": "123.456.789.0"
  }
}

MPAであればこんな感じで表示できます

<%- @user = User.find(1) %>
<div>#{@user.name}</div>

しかしそのイメージのままSPAでも同じようにやってしまうと……

user = axios.get('/user/1')
// 略
<div>{user.name}</div>

フロント側には以下のようなデータが丸々渡されてしまい、メールアドレスやIPアドレスが流出する事態が発生します

{
  "user": {
    "id": 1,
    "name": "tanaka",
    "email": "[email protected]",
    "last_access_ip": "123.456.789.0"
  }
}

そんなミスしないでしょ?と思うかもしれませんが事例はゴロゴロ転がっています

note.com

https://note.jp/n/n2115642a4e45

peing.net

https://cybersecurity-jp.com/news/29690

Twitter

https://news.yahoo.co.jp/byline/shinoharashuji/20200206-00162033

というより、発覚していないだけで同じミスしてるサービスめちゃくちゃ沢山あると思います

ちなみにこのミスを防ぐためには人力では確実にいつかミスが起こるため、openapiでファイルを作成して、additionalProperties:falseを付与して、更に各schemaにこのプロパティが設定されているか事前に確認する処理を挟み込み、更にschemaにあったレスポンスを返すかテストする~という一連の流れを自動化するのが一般的な方法かと思います。ただ、そもそもopenapiの時点でschemaの妥当性の確認やセキュリティ上の問題点の洗い出しなどが必要となります。当然ながら今回の想定にあるようなスキルがあまり高くない人が開発の片手間でやれるようなレベルのものではありません

これもまたMPAであれば上に書いたように何も考えずに書いても流出することはありませんのでリスクが段違いとなりこれもまたSPAの方がコストが格段に高いと思っています

5.バグがあった時の気づきやすさ / 対応のしやすさ

SPAはその性質上エラーがクライアントサイドで発生するのでエラーハンドリングをするためにも知識が必要となります

いや、わかりますよ?sentryでも入れれば良いだろ?って話だと思います。でもそれもまたスキルが必要です。今回想定しているような層はそもそもエラーハンドリングなんて概念がないと思いますし、何もしなくてもエラーが起こったらサーバーサイドにエラーログが残るというシンプルな仕組みのほうがコストが低いと思うわけです

そもそもSPAのエラー分かりづらすぎ問題(個人差あり)もありますし、サーバー→フロントに分離していることでエラーがどこで起こっているのか分かりづらかったりなどの問題もあります

既に開発に慣れているみなさんにとっては気にもしないような事でしょうけどこういう点は意外と重要です

6.ドキュメントの多さ

例えば実例としてQiitaさんでタグ検索をしてみます




react / vueだけで調べるともっと多いのですが、大抵の人がフレームワークを利用して開発するだろう現在においてこの情報量の少なさはかなりしんどいと思います


これは体感でしか無いので正確なデータには基づかなくて申し訳ないのですが、何かやりたいことがあったときにMPAではほぼほぼ99%やりたい事を実現する記事が3記事くらいは引っかかることが多いのに対して、SPAではやりたいことに80%くらいマッチした記事が2記事くらいあったら嬉しいくらいの期待値で検索しています(個人の感想です)

特に今回想定しているようなユーザーはドキュメント量の多い少ないの影響をもろに受ける層だと思うのでこの点からもSPAはコストが高いと感じます

というわけでSPAはコストが高いと思っています

色々書きましたが以上のような事を一切気にしないようなハイスキルなエンジニアにとっては関係のない話かと思います。ですがそんなエンジニアは希少中の希少ですしスタートアップの初期にそんなメンバーを獲得できるのは1000社に1社あれば良いほうでしょう

そんなエンジニアの皆さんは、自分がエンジニアガチャのSSRだと言うことを自覚しつつそこに至らない平凡な僕みたいなNRにとっての話だと納得して頂ければ嬉しいです

じゃあどういう状況ならSPAを使うの?

個人的な話で恐縮ですが以下のような状況であれば採用します

  • チーム内にSPAのエキスパートが居る
  • 社内にセキュリティ対策チームが存在する
  • チームメンバーが複数存在し、サーバーとフロントを分けたほうが良い
  • 一定以上の規模のサービスである

反論お待ちしています

いや、そうじゃないだろ!みたいな話は勉強になるのでめちゃくちゃお待ちしていますが、誹謗中傷や過激な発言は普通に傷つくのでやめてもらえると嬉しいです

一応書いておきますが「SPAのコストが高い」=「SPAで開発しているお前は無能」という話では全くないので個人とは切り離して純粋にSPAでの開発にはこういう面もあるよ~って話だと理解してもらえるとありがたいですm(_ _)m

書き忘れていた事

blitzとか、remixとか~
面白いと思いますが、本番採用されている事例が増えてから検討したいですね
特にドキュメントの少なさや何か問題に当たったときに解決する手段の少なさ(自分でリポジトリにソースコードを読みに行くなど)を考えるとまだ早いかなーと思ってます