Closed6

個人開発で静的画像を配信するためにCDNを使うか悩む

eaieai

要件

  • 個人開発でウェブサイトを作っている
    • フロントエンドはReactとSSG(非next.js) でビルド時にすべてのデータが吐き出される
    • バックエンドはない
      • ビルド時にデータソースからフロントエンドが参照するためのjsやjsonが吐かれ、それに必要なデータがすべて含まれている
    • フロントエンドのホスティングコストはCloudflare PagesやVercelを使えば無料
    • 静的なので無料で置く手段は無限にあるはず
  • サイトで使うアセットは別ドメインで管理している
    • 画像が主。現在2000ファイル、500MBほど
      • これから数MB級の動画も追加されるかも
    • 運営してくと増える見込み。たぶん10GB未満くらいで収まるはず
    • UGCではない。動的に増えることはない。手動の更新タイミングで増えたり変更されたりする
    • これらをどのようにホストするか
  • このサイトでマネタイズする気は無い
  • できる限り維持費は抑えたい
    • 現状: 無料
    • 「個人開発だと時間が取れず放置してしまうことがあるのはしょうがないが、お金がかかると閉じるという選択肢が出てくるのでよくない。運営に『少しお金がかかる』と『完全無料』は大きく差がある」という主張をどこかで見た
eaieai

現在の運用

  • アセットを管理する専用のGitリポジトリを作成
  • png画像をcommit
  • Romeolight WebPconvでpng画像のwebp版を作成し、同じ場所にcommit
  • GitHubにpush
  • Cloudflare Pagesで展開
  • サイトから直リン
    • pngを参照するかwebpを参照するかはフロントエンドのコンポーネントで切り替えている

いいところ

  • 完全無料
    • データの保存も、CDNで世界各地へのデプロイも無料
  • まあまあ楽
    • 慣れてるツールでファイル追加しcommit, pushするだけ

わるいところ

  • Gitの使い方を間違えている
    • Gitは本来ソースコードなどのテキストファイルを管理するもので、バイナリを管理するものではない
      • こんな使い方をしているとcommitやpushなどの操作にやたら時間がかかる
      • Gitでバイナリを扱う手段としてGit LFSを使う手があるが、GitHubのPrivateリポジトリでGit LFSを使うと入れた分だけお金がかかる
    • .gitが膨らんでいく
      • 上記と同じ
          - これは一応コミット履歴を捨てることで解消可能
  • GitHubの使い方を間違えている

現在の悩み

  • webpを作るのがめんどくさい
    • CLIで良い感じにできるツールが見つからなかったのでGUIで手動で作っている
      • せめてなんらかのワークフローで自動化したい
      • リポジトリが膨らむ
      • フォーマット変換以外にも、「そんなに大事じゃないところで使うので画質を落としたやつ」とかも作ったり管理したりするのがめんどい
      • 後述の様なCDNでの動的変換を使えばこれはいらなくなる
  • 小さい表示枠(40p)に大きい画像(256p)を入れてたらLighthouseに怒られた
    • 確かに無駄にでかい画像を入れるのは気持ち悪いし、ピクセルパーフェクトな画像を入れたい
    • しかし表示枠に合わせて40p版、256p版などの画像を作ってコミットしておくのはめんどくさいしリポジトリも膨らんでしまう
eaieai

CDNを使って動的変換すると表示が早くなったり、Lighthouseスコアが上がるのか?

14日間無料で試せるBunny.netを使って動的にリサイズや変換してみる

取得元は先の通りCloudflare Pagesで展開していたURL

結果

表示速度やLighthouseスコアはほぼかわらなかった

左がbeforeで、右がafter

適切なサイズの画像~の警告が消えているが、そもそもこの警告がある時点でも表示にそんな時間はかかってない

いいところ

  • 転送量は減った
    • 40pxの枠で画像が200件ほどの表示されるページだと
    • before (予め変換した256pのwebp): 3.1MB
    • after (動的にリサイズした40pのwebp): 720kB
    • かなり小さくなるのは良い
  • png, webpの判別をしなくて良くなった
    • .pngのURLにアクセスしてもブラウザのacceptヘッダーを見てwebpで返す機能がある
    • つまり全部.pngを渡せばいい

わるいところ

  • 使い続けるにはお金がかかる
    • CDN Pricing | Affordable Pay As You Go CDN | bunny.net
    • $0.03 /GB (Asia)
    • 加えて webpの自動変換など(optimizer)が $9.5 /month
    • お金を払うこと自体は別にいいんだが、前述の通りマネタイズしない個人開発という観点だとマイナス点
  • srcsetを書くのはちょっとめんどくさい
    • こんな感じになる
    • <img
        src={url + "?class=icon"}
        srcSet={[`${url}?class=icon 1x`, `${url}?class=icon2x 2x`].join(",")}
      />
      
    • classはBunny側で設定したプリセットである
eaieai

お気もちのまとめ

  • 手動でwebpとか作るのめんどくさい。良いツールも見つからない
  • でもそれだけのために維持費かかるCDN使うのもだるい
    • しかもそれで劇的に早くなったりするわけでもない

で悩んでいる
ご意見お待ちしています

とりあえずバイナリ置き場はGitHubではなくS3を使うべきな気がする

eaieai

sharpでpngからwebpを作成する

import sharp from "sharp";
import globOriginal from "glob";
import { promisify } from "util";
const glob = promisify(globOriginal);

const convertWebp = async (file: string) => {
    console.log(`Converting ${file}`);
    return await sharp(file)
        .webp({
            lossless: true,
        })
        .toFile(file.replace(".png", ".webp"));
};

const listFiles = async (pattern: string) => {
    const files = await glob(pattern);
    return files;
};

(async () => {
    const dirBase = `${__dirname}/../`;
    const pattern = `${dirBase}**/*.png`;
    const files = await listFiles(pattern);
    await Promise.all(files.map(convertWebp));
})();
eaieai

コストが掛からない方がいいと思ったので使わないようにした
sharpでのwebp変換は結構早くてよかった

このスクラップは2022/08/25にクローズされました