はじめに
普段は普通のサラリーマンをしており、開発経験はゼロ(HTMLがなんとなく分かるレベル)です。
そんな私が、生成AI(Gemini)の力を借りて、「写真から深層心理を分析して穴場旅行先を提案するアプリ」を個人開発しました。
作ったアプリ:スキバレ

https://suki-bare.vercel.app
(写真を3枚選ぶと、AIがあなたの深層心理を暴きます。よかったら遊んでみてください!)
直面した壁:スマホの写真が送れない
ローカル環境(自分のPC)では完璧に動いていた診断機能が、本番環境(Vercel)に上げたとたん動きませんでした。
VercelのLogで確認したところ「413 error」がでており、AIにエラーログを読ませると、「VercelのHobbyプランは、一度のリクエストで4.5MBまでしかデータを受け取れない」とのこと。 最近のスマホの写真は1枚で3MB〜5MBあるので、3枚送れば合計10MB超え。完全にオーバーです。
解決策:サーバーに送る前に「圧縮」する
「プランをアップグレードするしかないのか…?」と思いましたが、AIが出した解決策は「ブラウザ(クライアント)側で画像を小さく圧縮してから、APIに送信しよう」というものでした。
確かに、今回のアプリは「写真の雰囲気」さえAIに伝わればいいので、4Kのような高画質である必要はありません。
実装したコード
AIに書いてもらった「画像圧縮用」の関数がこちらです。 Canvasを使って画像を読み込み、長辺を800pxにリサイズし、JPEG品質0.7で圧縮しています。
TypeScript
// 画像を圧縮する関数
const compressImage = (file: File): Promise<string> => {
return new Promise((resolve) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = (event) => {
const img = document.createElement("img");
img.src = event.target?.result as string;
img.onload = () => {
const canvas = document.createElement("canvas");
const MAX_WIDTH = 800; // 800pxあればAIの認識には十分
const scaleSize = MAX_WIDTH / img.width;
// 幅が800pxより大きければ縮小、小さければそのまま
if (img.width > MAX_WIDTH) {
canvas.width = MAX_WIDTH;
canvas.height = img.height * scaleSize;
} else {
canvas.width = img.width;
canvas.height = img.height;
}
const ctx = canvas.getContext("2d");
ctx?.drawImage(img, 0, 0, canvas.width, canvas.height);
// JPEG形式 品質0.7で圧縮してDataURLとして返す
const dataUrl = canvas.toDataURL("image/jpeg", 0.7);
resolve(dataUrl);
};
};
});
};
これを、画像をアップロードする処理(handleImageUpload)の中で呼び出すようにしました。
TypeScript
// 変更前:ファイルをそのままStateに入れていた
// setImages((prev) => [...prev, ...files]);
// 変更後:圧縮してからStateに入れる
const compressedImages = await Promise.all(
fileArray.map((file) => compressImage(file))
);
setImages((prev) => [...prev, ...compressedImages]);
結果どうなったか
この処理を入れたことで、合計15MB近くあったリクエストが、なんと数百KBまで軽量化されました。 もちろんVercel上でもサクサク動き、エラーも完全に消えました。
副次的なメリット:
→通信が速くなり、ユーザーの待ち時間が減った。
→トークン消費減による、OpenAIのコスト削減
さいごに
知識ゼロからスタートしましたが、エラーが出るたびにAIにログを貼り付け、返ってきたコードを試す…という繰り返しで、なんとかリリースまで辿り着けました。同じようなサービス仕様かつ「413エラー」で困っている方がいたら、ぜひ「クライアント側での圧縮」を試してみてください。
今回作ったアプリ:スキバレ