🐬

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

2022/03/30に公開約6,400字12件のコメント

どうもみなさんこんばんは
ちょっと前に「個人開発者やスタートアップの初期から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": "tanaka@example.com",
    "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": "tanaka@example.com",
    "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とか~
面白いと思いますが、本番採用されている事例が増えてから検討したいですね
特にドキュメントの少なさや何か問題に当たったときに解決する手段の少なさ(自分でリポジトリにソースコードを読みに行くなど)を考えるとまだ早いかなーと思ってます

Discussion

SPAは開発言語が基本Javascriptしかないのも難点ですね。早くWASMが一般化してくれれば良いのに。

SPAやGrapthQLに対してずっと感じてたモヤモヤはこれだったのかも・・。
万人に効くソリューションなんて無いハズですもんね。
はっきり文章にしていただいて、とてもスッキリしました!

興味深く拝読させていただきました。私も Rails 歴は長いものの、 Next.js は触り始めたばかりなので、同じような感想を持ちました。SPAのコストは高いですが、一般的にユーザー体験は向上するとは思うので、コスパという点で考えると、ユーザー体験の向上がどれくらい必要なアプリケーションなのか?が考えるべきポイントなのでしょうね。

徐々にウェブ開発全体がSPAに移行していくとすれば、参考になる日本語での情報も増えていくのかもしれません。

MPAだとデータの流れが
DB→モデル→コントローラー→ビュー
ですが、SAPだと
DB→モデル→コントローラー→APIアクセスする層→APIからデータ取り出し変数に格納する層→JSX/テンプレート
になって、コントローラーAPIからデータ取り出し変数に格納する層の責務が被って同じことを2回書いてるやんってなるのがコストだと思うんですよね

MPAやったことなかったので考えたこともなかったですが、目から鱗でした。Remixはその辺、うまくやってる感があるので今後の広がりに期待したいと思ってます。

SPAはコストが高いから、
エンジニアに高いコストを払ってくれて
個々のエンジニアにとっては単価も高めで、重宝もされて、
とてもメリットがある、って面があるような気もします。

情報が少ないから対応できるエンジニア価値があがるという、需要供給です。

WindowsクラサバのVB開発者から、Web系フロントエンドに移ったので
このあたり、非常に体感があります。

なので、システムはコストが高くてそれでいい、かと。

個人的にはエディタ上での補完を受けられないのが辛いので、型付き言語のライブラリが少ないMPAは避けがちでした。最近は型ヒントを付けられる言語も増えてきましたが、ライブラリとしてはまだまだ対応できるものは少ないですしね……。C#やJVM系言語のMPAライブラリもありますが、Railsほど色々面倒を見てくれるものはない印象です(=個人開発向きではない)。

SPAだと、例えばフロントエンドにNext.js(TypeScript)、バックエンドにGoを用いれば全て型付きで書けるのでエディタの補完をフルに受けられます。

つまるところ、SPAのメリットの半分はリッチなViewが簡単に作れるですが、もう半分は型の恩恵が受けられるだと思っています。

それを前提とした上で具体的な本記事への反論として、まずドキュメントの少なさに関してはエディタ内での型の恩恵が大きいのである程度は解消できると思います。ドットを打てば続きの候補がリストされますし、エディタ内でドキュメントが表示できるのでむしろ(言語仕様そのものの学習の難しさを除けば)時間的なコストは小さいかと思います。

↑こんな感じです。

バグがあった時の気づきやすさに関しても、そもそも型があれば実行前にエラーを補足しやすいので、記事中で書かれていることも仰る通りですが一長一短だと思います。

ここまで書いてきましたが、MPAのWeb挙動の実現のしやすさやセキュリティ面での優位性は完全に同意です。「Railsを長期的に触っていて、メソッドをほとんど覚えているから型の補完は必要ない」みたいに言われるとぐうの音も出ないです。

面白い問題提起ありがとうございます。

ひとつ気になったのが、SPAの場合はlaravelやrailsを使って自前でフルスタックのAPIを用意するという前提なのかな?という点です。その対比であればMPAの方が諸々のコストで優位なのは確実です。

SPAをベースにMVP作るならfirestoreやprismaのようなバックエンド開発&インフラ運用コスト最小にできるデータストアを利用すると思うのでそれで初めて互角の対比ができると思います。

その上で私が思うのは、どちらが優位という万人に共通する答えはなくて「作りたいサービス・機能」「開発メンバーの経験・モチベーション」「人材採用の方針や技術トレンド」によるかなということです。
傾向としてはtoB向けはMPA、toCはSPAが選ばれやすいのではないかと思います。

私はどっちかというとSPA派ですが、サクッと作りたい時にはやはりMPAだなと思っています。
この記事には描かれていませんでしたが、SPAのステート管理にもかなりのコストがかかるのではないかと思います、MPAだとすべてサーバー側で持っているのでページ遷移で取ってくればいいですし。

反論OKとのことですので、記事を書いてみました。
SPAはコストが高いのか

反論記事という性質上どうしても否定が多くなっていますが、攻撃する意図はありません。
ご理解いただけると幸いです。

最近のものでBFFとしてもフルスタックとしても機能する、Remixというフレームワークがあります。
viewはReactでMPA & web標準で書けるかつ、TypeScriptとJavaScriptどちらでも可なので記事中の問題点は大体解消できます。

ReactとNode.jsに慣れていればおススメです。

完全に同感です。

特に戻るボタンの挙動でどなたも苦労されてるように見えます。

とある焼肉屋はスマホでオーダーできるようになっていたのですが、そのSPAは画面遷移をしていると表示領域だけレンダリングされてスクロールすると真っ白という酷いものでした。iPhoneではその現象は起きずAndroidのChromeでのみ起きてる現象でした。「表示領域だけレンダリングする」というのは今どきのSPAの技術として使ってみたくなるのはわかるのですが、フレームワーク任せでAndoridではただの一度もテストしないということがバレバレです。

本当はテスト含めて高コストなのにも関わらず、そこに手を抜いて低コスト低品質のまま世に出してしまってる例もあるのだと思います。「僕はレファレンス通りに作っただけでフレームワークのバグのことは知らない!サーバーサイドが余計なトークンを送って来るのも僕の責任じゃない!僕に限っては生産性は高いんだ!」という理屈なら生産性は高いんだと思います。

ログインするとコメントできます