Claudeと画像圧縮アプリを作った記録 ①機能追加編 — 「1pxのずれ」が教えてくれたこと

Excel

この記事について

自分で作った画像圧縮アプリに、Claude Code を使って機能を追加していった記録です。
全3回のうちの1回目にあたります。

  • 対象読者: アプリ開発を始めたばかりの人(筆者と同じくらい)
  • この回で扱うこと: 要望の伝え方、AIが返してきた判断、そして見つかったバグ
  • 成果物: https://xs218295.xsrv.jp/image-compressor/

専門用語は都度説明します。分からない単語が出てきても、そこで止まらずに読み進められる
ように書いたつもりです。


出発点:すでに動くアプリがあった

作業を始めた時点で、アプリはすでに動いていました。JPEG・PNG・WebP を圧縮して、
容量を減らすだけのシンプルなツールです。

ひとつだけ特徴があって、画像がどこにも送信されません。 圧縮はブラウザの中だけで
完結します。だからサーバーに写真が保存される心配がありません。

項目内容
フレームワークNext.js 14
圧縮処理ブラウザの Canvas API
ファイル数実質4つ

この状態を v0.1.0 と呼ぶことにします。

まず「現状を把握して」と頼んだ

いきなり機能追加を頼むのではなく、最初にこうお願いしました。

D:\PF_Claude\image-compressor は画像圧縮アプリです。README.md を読んで現状を把握してください。

これは結果的に良かったと思います。Claude は README だけでなくソースコードも読んで、
何ができるアプリなのかを整理して返してきました。

そして、頼んでいないのに懸念点を3つ指摘してきました。

  1. 圧縮処理が全部メインスレッドで動いているので、大きい画像を多数入れるとUIが固まる
  2. ダウンロード用のURLを解放するタイミングが早すぎて、保存が始まる前に無効化される可能性がある
  3. 圧縮結果を全部メモリに持ち続けているので、大量ファイルだと積み上がる

正直、このとき私は3つとも意味が分かっていませんでした。でもあとで全部効いてきます。
1番は第2回で丸ごと扱うテーマになり、2番はこの回で直すことになりました。

初心者的な学びとして、「まず読ませる」だけで問題点のリストが手に入るのは大きいです。
自分では気づけないところを指摘してもらえます。


要望を6つまとめて投げた

次に、やりたいことをまとめて伝えました。箇条書きで、思いついた順です。

画質のプリセットボタン(軽さ優先/バランス/画質優先)や、圧縮前後の比較プレビューなども追加したいです。
あと、圧縮画像をダウンロードするにはzipファイルにしていましたが、これは必須ですか?そのままダウンロードできればいいのですが。
その他、
・圧縮加工したファイル名に「-compressed」と追記
・選択した画像のファイル容量サイズは表示されているが、サイズ(縦横)も表示してほしい
・画像回転機能
などは追加できますか?

整理された仕様書ではありません。質問と要望が混ざっています。 でも問題なく通じました。

ここで気づいたのは、きれいにまとめようとしなくていいということです。
「これは必須ですか?」のような疑問も一緒に書いておけば、答えてくれます。


「ZIPは必須ですか?」への回答が予想と違った

私は「ZIPをやめて直接ダウンロードにしてほしい」という意味で聞きました。
返ってきた答えは、どちらかを選ぶのではなく「両方置く」でした。

理由はこうです。ZIPを使わず1枚ずつダウンロードすることは技術的に可能ですが、
ブラウザは1つのサイトが連続でファイルを落とそうとすると
「複数ファイルのダウンロードを許可しますか?」と確認を出します。

1回許可すれば以降は出ませんが、10枚20枚になるとこの挙動が煩わしい。
だから「個別にすべて保存」を追加しつつ、ZIPも残して
「枚数が多いときはZIPが確実です」と案内する形になりました。

私が知らなかった制約を踏まえて、私の希望を否定せずに設計してきたわけです。
こういうのは自分ひとりだと気づけません。

勝手に直されたところ(理由付き)

ファイル名の追記について、私は「-compressed」と書きました。全角のハイフンです。
実装されたのは 半角の -compressed でした。

理由も添えられていました。全角文字はサーバーやツールでの扱いで問題が起きやすいので、
ASCII のハイフンのほうが安全だと。そして「不要なら1か所直すだけで変えられます」と、
戻し方まで示されていました。

さらに、同じファイルを二度圧縮しても -compressed-compressed にならない
処理まで入っていました。私は指示していません。

ここも学びでした。指示を字面どおりに実行するのではなく、意図を汲んで調整されることがある。
そして納得できない場合に戻せるよう、判断の理由と変更箇所が示される。


実装されたもの

結果として v0.2.0 で入った機能です。

  • 画質プリセット — 軽さ優先(60) / バランス(80) / 画質優先(92) のボタン。
    細かく調整したいときは従来のスライダーも使える
  • 比較プレビュー — 境界線を左右に動かして、元画像と圧縮後を重ねて見比べる
  • 回転 — 画像ごとに90°ずつ回せる
  • 寸法表示 — 画像を選んだ直後に「3000×2000」と出る(圧縮完了を待たない)
  • ファイル名写真.jpg写真-compressed.jpg
  • 個別にすべて保存 — ZIPを使わないダウンロード

あわせて、最初に指摘された懸念2(ダウンロードURLの解放タイミング)も直りました。
それと、私が頼んでいない改善がもうひとつ入っていました。

従来は設定を1つ変えるたびに全画像を圧縮し直していたのですが、
変わったものだけを処理するようになりました。
回転ボタンを1枚に押しても、再圧縮されるのはその1枚だけです。


ここからが本題:1pxのずれ

実装が終わったあと、Claude は自分でブラウザを起動して動作確認を始めました。
ここで問題が見つかります。

何が起きたか

比較プレビューは、元画像と圧縮後の画像をぴったり重ねて
境界線から左右に見せ分ける仕組みです。ずれていたら比較になりません。

回転した画像で確認したところ、2枚の表示領域が
277×277 と 277×416 になっていました。縦のサイズが全然違います。

原因は「親切な初期スタイル」だった

犯人は Tailwind CSS(見た目を整えるための道具)が最初から入れている、
たった1行の指定でした。

img { max-width: 100% }

これは「画像が枠より大きくなって、はみ出さないようにする」ための、
普通ならありがたい設定です。スマホで写真がはみ出さないのは、こういう指定のおかげです。

ところが回転と組み合わせると邪魔になります。

90°回転させる場合、回転するの画像は横に長く広げておく必要があります。
回して初めて縦長の枠にぴったり収まるからです。
つまり一時的に枠の幅を超えていないといけないのに、
max-width: 100% がそれを「はみ出し」と判断して切り詰めてしまっていた。

解決策は、この画像だけ制限を外すことでした。

max-width: none;

初心者として何を学んだか

「便利な初期設定」が、想定外の使い方をすると足を引っ張る。

Tailwind は何も悪いことをしていません。99%の場面で正しい設定です。
ただ「回転させた画像を枠に合わせる」という使い方が想定外だっただけ。

こういうバグは、エラーメッセージが出ません。ただ静かに1px〜100pxずれるだけです。
だから「見た目が合っているか実際に測る」という作業が必要になります。

修正後、2枚の表示領域は完全に一致しました。


回転の向きをどう確かめたか

もうひとつ面白かったのが、回転方向の検証方法です。

このアプリでは、CSSで画像を回す処理と、Canvas(画像加工の仕組み)で回す処理の
2種類の回転が使われています。この2つが同じ向きに回っていないと、
プレビューと実際の保存結果が食い違ってしまいます。

Claude がやった確認方法はこうでした。

  1. テスト画像の上辺の中央に赤い三角を描く
  2. 右に90°回転させて圧縮する
  3. 出力画像の右辺の中央の色を調べる
  4. そこが赤なら、時計回りに回っている

実際に rgb(253,0,3) という値が返ってきて、一致が確認できました。

「たぶん合ってる」で済ませず、判定できる目印を仕込んで数値で確認する。
自分でテストを書くときの参考になりました。


この回のまとめ

  • まず読ませる。 現状把握を頼むだけで、自分では気づけない問題点のリストが出てくる
  • 要望はきれいにまとめなくていい。 質問混じりの箇条書きでも通じる
  • 「必須ですか?」と聞くと、知らなかった制約が出てくる。 今回はブラウザの
    ダウンロード確認ダイアログ。それを踏まえた第三の案が返ってきた
  • 勝手に調整されることがあるが、理由と戻し方が示される。 全角→半角のハイフンなど
  • エラーが出ないバグがある。 1pxのずれは実際に測らないと分からない
  • 検証は「判定できる形」にする。 赤い三角を目印にする、寸法を数値で比べる

次回は、最初に指摘された懸念1「圧縮中にUIが固まる」を解決する話です。
Web Worker という仕組みを使うのですが、ここで私は2回、数字にだまされます。

→ ②高速化編へ

コメント

タイトルとURLをコピーしました