<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Indigo Works | Blog</title><description/><link>https://www.indigo165e83.com/</link><language>en</language><item><title>NotebookLMにJSTQBシラバスを読み込ませたら、最強の試験対策ツールになった話</title><link>https://www.indigo165e83.com/blog/notebooklm-jstqb-study-guide/</link><guid isPermaLink="true">https://www.indigo165e83.com/blog/notebooklm-jstqb-study-guide/</guid><pubDate>Wed, 04 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;div&gt;&lt;h2 id=&quot;はじめに&quot;&gt;はじめに&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;現在、QAエンジニアとしてのスキルアップも兼ねて、JSTQB Foundation Level（FL）の資格勉強を進めています。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://jstqb.jp/syllabus.html#syllabus_foundation&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;JSTQBの公式シラバス&lt;/a&gt;は非常に網羅的で勉強になる反面、テキストのボリュームが多く、ただ読んでいるだけではなかなか頭に入ってこないのが悩みでした。&lt;/p&gt;
&lt;p&gt;そこで、話題のGoogleのAIツール &lt;strong&gt;「NotebookLM」&lt;/strong&gt; に、公式シラバス（V4.0のPDF）を丸ごと読み込ませて試験対策の教材を作らせてみたところ……結果として、想像を絶するクオリティの学習環境が爆誕しました。&lt;/p&gt;
&lt;p&gt;今回は、NotebookLMを使ってどのような試験対策コンテンツを作成したのか、その衝撃的な体験をシェアします。&lt;/p&gt;

&lt;div&gt;&lt;h2 id=&quot;notebooklmで生成したものとその感想&quot;&gt;NotebookLMで生成したものとその感想&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;NotebookLMにシラバスをアップロードし、学習ガイド機能などを使って以下の4つのコンテンツを生成しました。&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;1-まるでラジオ番組男女2人の音声解説&quot;&gt;1. まるでラジオ番組！男女2人の「音声解説」&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;個人的に一番感動したのがこの機能です。アップロードしたシラバスの内容をもとに、&lt;strong&gt;男女2人のパーソナリティが会話形式で要約してくれるポッドキャスト&lt;/strong&gt;のような音声を自動生成してくれます。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;良かった点:&lt;/strong&gt; 堅苦しい専門用語も、パーソナリティ同士の自然な掛け合いの中で説明されるため、すんなりと頭に入ってきます。散歩中や家事をしながらの「ながら聞き」学習に最適でした。&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h3 id=&quot;2-過去問代わりに使えるクイズとフラッシュカード&quot;&gt;2. 過去問代わりに使える「クイズ」と「フラッシュカード」&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;インプットだけでなく、アウトプットの練習用としてクイズとフラッシュカードも生成しました。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;良かった点:&lt;/strong&gt; シラバスの要点を突いた問題が生成され、まさに「過去問もどき」として非常に使いやすい出題形式でした。自分で一から単語帳を作る手間が省け、すぐに知識の定着度テストに移行できるのが大きなメリットです。&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h3 id=&quot;3-要点が視覚化されたスライド&quot;&gt;3. 要点が視覚化された「スライド」&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;さらに、シラバスの全体像を把握するためのスライド資料も作成しました。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;良かった点:&lt;/strong&gt; まるで人間がPowerPointでまとめたような、要点が整理されたとてもわかりやすい資料が一瞬で完成しました。試験直前の総復習や、各章の構造を俯瞰したい時に大活躍します。&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;衝撃だったポイント&quot;&gt;衝撃だったポイント&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;今回NotebookLMを使ってみて最も衝撃だったのは、&lt;strong&gt;これら総合的にクオリティの高いアウトプットが「瞬時」に生成されたこと&lt;/strong&gt;です。&lt;/p&gt;
&lt;p&gt;これまでであれば、シラバスを読み込み、自分でノートにまとめ、単語帳を作り……と数日〜数週間かかっていた「学習の準備作業」が、PDFをドラッグ＆ドロップして数クリックするだけで終わってしまいました。&lt;/p&gt;
&lt;p&gt;AIが単なる「検索ツール」から、完全に「優秀な専属チューター」へと進化していることを肌で感じました。&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;まとめ&quot;&gt;まとめ&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;JSTQBをはじめ、分厚い公式ドキュメントやシラバスを読み込む必要のある資格試験において、NotebookLMは間違いなくゲームチェンジャーになります。&lt;/p&gt;
&lt;p&gt;まだ使ったことがない方は、ぜひ手元のPDFやドキュメントを読み込ませてみてください。学習効率が劇的に跳ね上がるはずです！&lt;/p&gt;</content:encoded><category>JSTQB</category><category>AI</category><category>NotebookLM</category><category>QA</category></item><item><title>『エッセンシャル スクラム』に学び、アジャイル現場へ応用したい話</title><link>https://www.indigo165e83.com/blog/essential-scrum-agile-development/</link><guid isPermaLink="true">https://www.indigo165e83.com/blog/essential-scrum-agile-development/</guid><pubDate>Sun, 01 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;システム開発を進める中で、「アジャイルにやっているつもりだけど、なんだかうまくいかない」「要件変更に振り回されて疲弊している」と感じる瞬間はありませんか？&lt;/p&gt;
&lt;p&gt;スクラム開発のバイブルとも言える Kenneth S. Rubin (著) &lt;a href=&quot;https://amzn.to/4rufwod&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;『エッセンシャル スクラム』&lt;/a&gt; を読み、&lt;a href=&quot;../design/essential-scrum/&quot;&gt;こちら&lt;/a&gt;に要約いたしました。&lt;/p&gt;
&lt;p&gt;その中から &lt;strong&gt;「現場のエンジニアが本当に知っておくべきスクラムの要点」&lt;/strong&gt; を整理しました。単なるフレームワークの解説にとどまらず、開発のスピードと品質を両立させるためのマインドセットについて紹介します。&lt;/p&gt;

&lt;div&gt;&lt;h2 id=&quot;1-要件は契約ではなく対話のプレースホルダー&quot;&gt;1. 要件は「契約」ではなく「対話のプレースホルダー」&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;従来のウォーターフォール開発では、要件定義書は「絶対に守るべき契約」でした。しかしスクラムでは、要件（ユーザーストーリー）を &lt;strong&gt;「詳細を議論するための予約席（プレースホルダー）」&lt;/strong&gt; として扱います。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;INVEST原則を意識する:&lt;/strong&gt; 良いストーリーは、独立しており（&lt;strong&gt;I&lt;/strong&gt;ndependent）、交渉可能で（&lt;strong&gt;N&lt;/strong&gt;egotiable）、価値があり（&lt;strong&gt;V&lt;/strong&gt;aluable）、見積り可能で（&lt;strong&gt;E&lt;/strong&gt;stimatable）、適切に小さく（&lt;strong&gt;S&lt;/strong&gt;mall）、テスト可能（&lt;strong&gt;T&lt;/strong&gt;estable）であるべきです。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;完璧な文書より対話を:&lt;/strong&gt; 見た目が立派な仕様書ほど、質問を封じ込めてしまいます。スクラムでは、ジャストインタイムで要件を分割し、チームとプロダクトオーナーの「対話」を通じて詳細を詰めていきます。&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;2-見積もりと計画の真実地図より地形を信じよ&quot;&gt;2. 見積もりと計画の真実：「地図より地形を信じよ」&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;「いつできるのか？」という問いに対し、スクラムは非常に現実的なアプローチをとります。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;相対サイズで見積もる:&lt;/strong&gt; 人間は絶対的な時間を見積もるのは苦手ですが、相対的な比較（AはBの2倍大変そう）は得意です。そのため、時間ではなく「ストーリーポイント」という相対値で見積もり（プランニングポーカー）を行います。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ベロシティの罠:&lt;/strong&gt; チームが1スプリントで消化できるポイント数（ベロシティ）は、あくまで「現状の把握（体重計）」です。これを&lt;strong&gt;チームの評価指標や目標値にしてはいけません&lt;/strong&gt;。目標にされた瞬間、エンジニアは品質を犠牲にしてポイントを水増しするようになり、システムは確実に崩壊します（グッドハートの法則）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;期日固定とスコープの調整:&lt;/strong&gt; 期日が動かせないプロジェクトでは、「無理をして全部作る」のではなく、「期日を守るためにスコープ（機能）を削る」という思考の切り替えが不可欠です。&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;3-スピードを殺す技術的負債との戦い&quot;&gt;3. スピードを殺す「技術的負債」との戦い&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;本書の中で最もエンジニアが共感し、かつ恐ろしいのが「技術的負債」についての言及です。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;ベロシティの偽装:&lt;/strong&gt; 納期へのプレッシャーからテストや設計を省き、見かけ上のベロシティを維持することは、後から莫大な「利息（バグ対応や修正困難なコード）」を支払うことになります。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;品質担保こそが最速への道:&lt;/strong&gt; 手動テストの横行は負債の筆頭です。CI/CDの構築や、Playwright等を用いたE2Eテスト自動化といった技術的プラクティスを組み込み、「厳しい完了の定義（DoD）」を死守することが、結果的に開発スピードを高く保つ唯一の道です。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ボーイスカウトのルール:&lt;/strong&gt; 「来た時よりも美しくして去る」。既存コードを触るついでに数分のリファクタリングを息をするように行うことが、システム寿命を劇的に延ばします。&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;4-フリーランスや個人開発へのスクラムの応用&quot;&gt;4. フリーランスや個人開発へのスクラムの応用&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;チーム開発を前提としたスクラムですが、その原則はフリーランスの案件や、個人プロジェクトを回す上でも強力な武器になります。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;ビジョンの策定とスコープクリープの防止:&lt;/strong&gt; 個人開発では「あれもこれも」と機能を追加したくなります（スコープクリープ）。開発前に「プロダクトビジョン」を明文化しておくことで、「本当に今必要な機能か？」を立ち止まって判断できます。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;WIP（仕掛品）の制限とスウォーミング:&lt;/strong&gt; 複数の機能やプロジェクトを同時に進めるマルチタスクは、コンテキストスイッチによる無駄を生みます。1つのタスクに集中して「完全に完了（Done）」させてから次へ進む（一人スウォーミング）を意識するだけで、デリバリー速度は格段に上がります。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;一人レトロスペクティブ（ふりかえり）の価値:&lt;/strong&gt; 開発に行き詰まった際、実装に没頭する手を止め「なぜこのエラーに時間を溶かしたのか？」を振り返る。そして「次回は実装前に技術検証（スパイク）の時間を取る」といったアクションに繋げることが、継続的な成長を生み出します。&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;おわりにスクラムに終了状態はない&quot;&gt;おわりに：スクラムに「終了状態」はない&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;『エッセンシャル スクラム』の最終章で語られている通り、スクラムを導入して「これで完璧だ」という終了状態（End State）はありません。&lt;/p&gt;
&lt;p&gt;誰かが敷いたレールを歩くのではなく、自分たちのチーム（あるいは自分自身）のコンテキストに合わせて、検査と適応のループを回し、&lt;strong&gt;自分たち自身の道を切り拓いていく&lt;/strong&gt;こと。それこそが、真のアジャイル開発の姿です。&lt;/p&gt;</content:encoded><category>Scrum</category><category>Agile</category><category>開発プロセス</category><category>個人開発</category></item><item><title>Cloudflare PagesにAWS Route 53管理のカスタムドメインを設定する</title><link>https://www.indigo165e83.com/blog/20260301-cloudflare-pages-custom-domain/</link><guid isPermaLink="true">https://www.indigo165e83.com/blog/20260301-cloudflare-pages-custom-domain/</guid><pubDate>Sun, 01 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Cloudflare Pagesで公開しているサイトに、AWS Route 53で管理しているドメインをカスタムドメインとして設定する手順を解説します。今回は &lt;code dir=&quot;auto&quot;&gt;your-project.pages.dev&lt;/code&gt; で公開しているサイトを &lt;code dir=&quot;auto&quot;&gt;www.yourdomain.com&lt;/code&gt; で表示できるようにします。&lt;/p&gt;

&lt;div&gt;&lt;h2 id=&quot;なぜ-yourdomaincom-ではなく-wwwyourdomaincom-をメインにするのか&quot;&gt;なぜ &lt;code dir=&quot;auto&quot;&gt;yourdomain.com&lt;/code&gt; ではなく &lt;code dir=&quot;auto&quot;&gt;www.yourdomain.com&lt;/code&gt; をメインにするのか&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;ベストプラクティスは &lt;strong&gt;&lt;code dir=&quot;auto&quot;&gt;www.yourdomain.com&lt;/code&gt; をメインにして、&lt;code dir=&quot;auto&quot;&gt;yourdomain.com&lt;/code&gt; からリダイレクト&lt;/strong&gt; する構成です。&lt;/p&gt;
&lt;p&gt;理由はDNSの仕様にあります。Cloudflare PagesはCNAMEレコードを使ってドメインを紐付けますが、&lt;strong&gt;ルートドメイン（&lt;code dir=&quot;auto&quot;&gt;yourdomain.com&lt;/code&gt;）にはCNAMEレコードを設定できません&lt;/strong&gt;（RFC上の制約）。Route 53ではALIASレコードという独自機能で回避できますが、Cloudflare Pages向けの直接ALIASは設定できないため、&lt;code dir=&quot;auto&quot;&gt;www&lt;/code&gt; サブドメインを使う方がシンプルです。&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;step-1-cloudflare-pagesにカスタムドメインのcnameを設定する&quot;&gt;Step 1: Cloudflare PagesにカスタムドメインのCNAMEを設定する&lt;/h2&gt;&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;Cloudflare Dashboard → 対象のPagesプロジェクト → &lt;strong&gt;「Custom domains」&lt;/strong&gt; タブを開く&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;「Set up a custom domain」&lt;/strong&gt; をクリック&lt;/li&gt;
&lt;li&gt;&lt;code dir=&quot;auto&quot;&gt;www.yourdomain.com&lt;/code&gt; を入力して進む&lt;/li&gt;
&lt;li&gt;Setup Methodの選択画面で &lt;strong&gt;「My DNS provider」→「Begin CNAME setup」&lt;/strong&gt; をクリック
&lt;ul&gt;
&lt;li&gt;ドメインがCloudflare管理の場合は「Cloudflare DNS」を選ぶと自動設定されますが、今回はRoute 53管理のため「My DNS provider」を選択します&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;表示されるCNAMEレコードの情報をメモする&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Name   : www&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Target : your-project.pages.dev&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h2 id=&quot;step-2-aws-route-53にcnameレコードを追加する&quot;&gt;Step 2: AWS Route 53にCNAMEレコードを追加する&lt;/h2&gt;&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;AWS Console → &lt;strong&gt;Route 53&lt;/strong&gt; → &lt;code dir=&quot;auto&quot;&gt;yourdomain.com&lt;/code&gt; のホストゾーンを開く&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;「レコードを作成」&lt;/strong&gt; をクリック&lt;/li&gt;
&lt;li&gt;以下を入力して保存する&lt;/li&gt;
&lt;/ol&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th align=&quot;left&quot;&gt;項目&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;値&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;レコード名&lt;/strong&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;&lt;code dir=&quot;auto&quot;&gt;www&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;タイプ&lt;/strong&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;&lt;code dir=&quot;auto&quot;&gt;CNAME&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;値&lt;/strong&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;&lt;code dir=&quot;auto&quot;&gt;your-project.pages.dev&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;TTL&lt;/strong&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;&lt;code dir=&quot;auto&quot;&gt;300&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Route 53側で「レコードが正常に作成されました」と表示されれば成功です。&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;step-3-cloudflare側でdns確認を待つ&quot;&gt;Step 3: Cloudflare側でDNS確認を待つ&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Cloudflare PagesのCustom domains画面に戻ると、&lt;code dir=&quot;auto&quot;&gt;www.yourdomain.com&lt;/code&gt; が &lt;strong&gt;「Inactive (Requires DNS setup)」&lt;/strong&gt; の状態になっています。&lt;/p&gt;
&lt;p&gt;DNS変更が世界中に伝播するまで&lt;strong&gt;最大24時間&lt;/strong&gt;かかりますが、通常は数分〜数時間で反映されます。しばらく待ってからページをリロードすると &lt;strong&gt;「Active・SSL enabled」&lt;/strong&gt; に変わります。&lt;/p&gt;
&lt;p&gt;![Cloudflare Custom domains Active状態]&lt;/p&gt;
&lt;p&gt;SSL証明書も自動で発行されるため、別途設定は不要です。&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;おまけyourdomaincomルートドメインからのリダイレクト設定&quot;&gt;おまけ：&lt;code dir=&quot;auto&quot;&gt;yourdomain.com&lt;/code&gt;（ルートドメイン）からのリダイレクト設定&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;&lt;code dir=&quot;auto&quot;&gt;yourdomain.com&lt;/code&gt; にアクセスした人を &lt;code dir=&quot;auto&quot;&gt;www.yourdomain.com&lt;/code&gt; にリダイレクトするには、&lt;strong&gt;S3の静的ウェブサイトホスティング機能&lt;/strong&gt;を使います。&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;s3バケットを作成する&quot;&gt;S3バケットを作成する&lt;/h3&gt;&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;AWS Console → &lt;strong&gt;S3&lt;/strong&gt; → &lt;strong&gt;「バケットを作成」&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;バケット名を &lt;code dir=&quot;auto&quot;&gt;yourdomain.com&lt;/code&gt;（ドメイン名と完全一致）&lt;/strong&gt; で作成
&lt;ul&gt;
&lt;li&gt;リージョン: &lt;code dir=&quot;auto&quot;&gt;us-east-1&lt;/code&gt;（バージニア北部）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;「パブリックアクセスをすべてブロック」のチェックを外す&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;&lt;h3 id=&quot;s3でリダイレクトを設定する&quot;&gt;S3でリダイレクトを設定する&lt;/h3&gt;&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;作成したバケット → &lt;strong&gt;「プロパティ」&lt;/strong&gt; タブ&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;「静的ウェブサイトホスティング」&lt;/strong&gt; → 「編集」&lt;/li&gt;
&lt;li&gt;以下を設定して保存&lt;/li&gt;
&lt;/ol&gt;





















&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th align=&quot;left&quot;&gt;項目&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;値&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;ホスティングタイプ&lt;/strong&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;オブジェクトのリクエストをリダイレクトする&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;ホスト名&lt;/strong&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;&lt;code dir=&quot;auto&quot;&gt;www.yourdomain.com&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;プロトコル&lt;/strong&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;&lt;code dir=&quot;auto&quot;&gt;https&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;div&gt;&lt;h3 id=&quot;route-53にaliasレコードを追加する&quot;&gt;Route 53にALIASレコードを追加する&lt;/h3&gt;&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;Route 53 → &lt;code dir=&quot;auto&quot;&gt;yourdomain.com&lt;/code&gt; ホストゾーン → &lt;strong&gt;「レコードを作成」&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;以下を設定して保存&lt;/li&gt;
&lt;/ol&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th align=&quot;left&quot;&gt;項目&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;値&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;レコード名&lt;/strong&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;空欄（ルートドメイン）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;タイプ&lt;/strong&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;&lt;code dir=&quot;auto&quot;&gt;A&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;エイリアス&lt;/strong&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;オン&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;トラフィックのルーティング先&lt;/strong&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;S3ウェブサイトエンドポイントへのエイリアス（&lt;code dir=&quot;auto&quot;&gt;us-east-1&lt;/code&gt;）&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;バケット名がドメイン名と一致していれば、Route 53が自動でS3エンドポイントを候補として表示します。&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;まとめ&quot;&gt;まとめ&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;今回の設定をまとめると以下のとおりです。&lt;/p&gt;

















&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th align=&quot;left&quot;&gt;アクセスURL&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;動作&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;code dir=&quot;auto&quot;&gt;www.yourdomain.com&lt;/code&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Cloudflare Pages のサイトが表示される&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;code dir=&quot;auto&quot;&gt;yourdomain.com&lt;/code&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;&lt;code dir=&quot;auto&quot;&gt;www.yourdomain.com&lt;/code&gt; へ301リダイレクト&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Cloudflare PagesとAWS Route 53を組み合わせた構成でも、CNAMEとS3リダイレクトを使えばスムーズにカスタムドメインを設定できます。&lt;/p&gt;</content:encoded><category>Cloudflare</category><category>AWS</category><category>Route53</category><category>DNS</category><category>カスタムドメイン</category></item><item><title>JavaScriptとTypeScriptの歴史の整理：バージョンとモジュールシステムの変遷</title><link>https://www.indigo165e83.com/blog/20260223-a-history-of-javascript-and-typescript/</link><guid isPermaLink="true">https://www.indigo165e83.com/blog/20260223-a-history-of-javascript-and-typescript/</guid><pubDate>Mon, 23 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;エンジニアとしてモダンなWeb開発（Next.jsやAstroなど）を行う上で避けて通れない、JavaScript（JS）とTypeScript（TS）の関係性や歴史的背景を整理します。&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;1-javascriptとtypescriptの関係&quot;&gt;1. JavaScriptとTypeScriptの関係&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;TypeScriptは、JavaScriptをベースに「型」の機能を追加した言語であり、 &lt;strong&gt;JavaScriptのスーパーセット（超集合）&lt;/strong&gt; です。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;JavaScript (JS)&lt;/strong&gt;: ブラウザやNode.jsで直接実行できる標準的な言語。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;TypeScript (TS)&lt;/strong&gt;: JSの全機能を含みつつ、静的型付けなどの拡張機能を加えたもの。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;互換性&lt;/strong&gt;: 有効なJSコードは、そのまま有効なTSコードとして扱えます。&lt;/li&gt;
&lt;/ul&gt;

&lt;div&gt;&lt;h2 id=&quot;2-javascript-ecmascript-の歴史と名称の変遷&quot;&gt;2. JavaScript (ECMAScript) の歴史と名称の変遷&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;JavaScriptの規格は &lt;strong&gt;ECMAScript (ES)&lt;/strong&gt; と呼ばれます。その名称とバージョンの付け方には大きな歴史的転換点がありました。&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;es2015-es6-以前黎明期から停滞期&quot;&gt;ES2015 (ES6) 以前：黎明期から停滞期&lt;/h3&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;1995年&lt;/strong&gt;: Netscape社で誕生。わずか10日間で開発されました。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ES3 (1999年)&lt;/strong&gt;: 長らく標準として使われたバージョン。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ES5 (2009年)&lt;/strong&gt;: 約10年の沈黙を破って登場。&lt;code dir=&quot;auto&quot;&gt;forEach&lt;/code&gt; や &lt;code dir=&quot;auto&quot;&gt;JSON&lt;/code&gt; サポートなど、現代的なJSの基礎が作られました。&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h3 id=&quot;大転換期es2015-es6-と名称の変更&quot;&gt;大転換期：ES2015 (ES6) と名称の変更&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;2015年、JS史上最大のアップデートが行われ、&lt;code dir=&quot;auto&quot;&gt;class&lt;/code&gt; 構文や &lt;code dir=&quot;auto&quot;&gt;import/export&lt;/code&gt; など、大規模開発に耐えうる機能が多数導入されました。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;名称の変更&lt;/strong&gt;: 従来は「ES6」のようにバージョン番号で呼ばれていましたが、この年から &lt;strong&gt;「毎年アップデートを行う」という方針に切り替わり、名称も「ES2015」のように年号を冠する形式&lt;/strong&gt; になりました。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;現在&lt;/strong&gt;: 以降はES2016、ES2017……と毎年小規模なアップデートが繰り返され、現在はES2024まで進化しています。&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;3-typescriptの誕生と進化&quot;&gt;3. TypeScriptの誕生と進化&lt;/h2&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;2012年&lt;/strong&gt;: Microsoftが誕生させる。JSが大規模開発に使われる際のエラーを防ぐことが目的でした。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;TS 2.0 (2016年)&lt;/strong&gt;: &lt;code dir=&quot;auto&quot;&gt;strictNullChecks&lt;/code&gt; が導入され、型安全性が飛躍的に向上しました。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;TS 5.0 (2023年〜)&lt;/strong&gt;: 現在の主流バージョン。コンパイルの高速化などが進んでいます。&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;4-commonjs-cjs-と-es-modules-esm-の詳細&quot;&gt;4. CommonJS (CJS) と ES Modules (ESM) の詳細&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;モジュールシステム（ファイルを分割して読み書きする仕組み）には、歴史的な経緯から2つの主要な形式があります。現在、これらは混在した状況にあり、違いを正しく理解することが重要です。&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;比較表&quot;&gt;比較表&lt;/h3&gt;&lt;/div&gt;



































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th align=&quot;left&quot;&gt;項目&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;CommonJS (CJS)&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;ES Modules (ESM)&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;キーワード&lt;/strong&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;&lt;code dir=&quot;auto&quot;&gt;require()&lt;/code&gt;, &lt;code dir=&quot;auto&quot;&gt;module.exports&lt;/code&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;&lt;code dir=&quot;auto&quot;&gt;import&lt;/code&gt;, &lt;code dir=&quot;auto&quot;&gt;export&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;標準の場&lt;/strong&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Node.js（歴史的・サーバーサイド）&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;ブラウザ・JS標準規格（モダン）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;読み込みタイミング&lt;/strong&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;実行時 (Runtime)&lt;/strong&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;静的解析時 (Static)&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;トップレベル await&lt;/strong&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;不可&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;可能&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;拡張子&lt;/strong&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;&lt;code dir=&quot;auto&quot;&gt;.js&lt;/code&gt; (デフォルト) / &lt;code dir=&quot;auto&quot;&gt;.cjs&lt;/code&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;&lt;code dir=&quot;auto&quot;&gt;.js&lt;/code&gt; (デフォルト) / &lt;code dir=&quot;auto&quot;&gt;.mjs&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;div&gt;&lt;h3 id=&quot;技術的な大きな違い&quot;&gt;技術的な大きな違い&lt;/h3&gt;&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;静的解析 vs 動的実行&lt;/strong&gt;:
&lt;ul&gt;
&lt;li&gt;ESMはプログラムの実行前にインポートの依存関係を解決します。これにより、使われていないコードを削ぎ落とす &lt;strong&gt;Tree-shaking&lt;/strong&gt; が可能になり、ビルド後のファイルサイズを軽量化できます。&lt;/li&gt;
&lt;li&gt;CJSはコードの実行中に &lt;code dir=&quot;auto&quot;&gt;require()&lt;/code&gt; を通じて動的に読み込むため、条件分岐（&lt;code dir=&quot;auto&quot;&gt;if&lt;/code&gt; 文）の中でインポートするような書き方が可能です。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;互換性の問題&lt;/strong&gt;:
&lt;ul&gt;
&lt;li&gt;現在のNode.js環境では両方の形式をサポートしていますが、ESMからCJSを呼ぶのは比較的容易な一方、CJSからESMを呼ぶ場合には制約（非同期的な扱いや &lt;code dir=&quot;auto&quot;&gt;import()&lt;/code&gt; 関数の利用など）が生じることがあります。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;モダン環境での立ち位置&lt;/strong&gt;:
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Next.js 16系&lt;/strong&gt; など、現代的なフロントエンドフレームワークでは &lt;strong&gt;ESMが標準&lt;/strong&gt; です。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;</content:encoded><category>JavaScript</category><category>TypeScript</category><category>Node.js</category><category>ES Modules</category><category>CommonJS</category></item><item><title>Astro + Starlightブログにおけるタグ管理のベストプラクティス（Zodを使った表記揺れの防止）</title><link>https://www.indigo165e83.com/blog/20260222-astro-starlight-tag-management-best-practice/</link><guid isPermaLink="true">https://www.indigo165e83.com/blog/20260222-astro-starlight-tag-management-best-practice/</guid><pubDate>Sun, 22 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;div&gt;&lt;h2 id=&quot;はじめに&quot;&gt;はじめに&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;AstroとStarlight（starlight-blog）を使って技術ブログを運用していると、記事が増えるにつれて気になってくるのが**「タグの表記揺れ」**です。&lt;/p&gt;
&lt;p&gt;例えば、記事のフロントマターで以下のようにタグを指定していくと…&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# 記事A&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;tags&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;span&gt;Git&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;Next.js&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;UI/UX&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# 記事B&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;tags&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;nextjs&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;ui-ux&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;サイト上では「Git」と「git」が別のタグとして扱われてしまい、タグ一覧ページが分散してしまいます。
毎回「大文字だっけ？記号は入れるんだっけ？」と過去の記事を確認しながら入力するのはミスが起きやすく、執筆体験もよくありません。&lt;/p&gt;
&lt;p&gt;そこで今回は、&lt;strong&gt;Zodの &lt;code dir=&quot;auto&quot;&gt;transform&lt;/code&gt; 機能を活用して、システム側でタグの表記を自動的に正規化・統一するベストプラクティス&lt;/strong&gt;をご紹介します。&lt;/p&gt;

&lt;div&gt;&lt;h2 id=&quot;zodとはなぜastroで使われるのか&quot;&gt;Zodとは？なぜAstroで使われるのか&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;今回の解決策の鍵となるのが &lt;strong&gt;&lt;a href=&quot;https://zod.dev/&quot;&gt;Zod&lt;/a&gt;&lt;/strong&gt; です。&lt;/p&gt;
&lt;p&gt;Zodは、TypeScriptファーストのスキーマ宣言およびバリデーション（データ検証）ライブラリです。「このデータは文字列の配列であるべき」「この項目は必須である」といったルール（スキーマ）を定義し、実際のデータがそのルールに従っているかをチェックしてくれます。&lt;/p&gt;
&lt;p&gt;Astro（およびStarlight）の &lt;strong&gt;Content Collections&lt;/strong&gt; という機能では、MarkdownやMDXのフロントマター（記事先頭のメタデータ）の型を厳格に管理するために、このZodが標準で組み込まれています。&lt;/p&gt;
&lt;p&gt;Zodが特に優れているのは、単に「データが正しいか弾く（バリデーション）」だけでなく、&lt;strong&gt;「データを別の望ましい形に変換する（トランスフォーム）」&lt;/strong&gt; 機能を持っている点です。今回はこの機能を活用します。&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;解決策zodスキーマと辞書mapを使った自動変換&quot;&gt;解決策：Zodスキーマと辞書（Map）を使った自動変換&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;アプローチは非常にシンプルです。
&lt;code dir=&quot;auto&quot;&gt;src/content.config.ts&lt;/code&gt; に「タグの辞書」を用意し、記事がビルドされる際にZodの機能を使って**「入力されたタグ（スラッグ）」を「正式名称」に自動変換**する処理を挟み込みます。&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;1-srccontentconfigts-の実装&quot;&gt;1. &lt;code dir=&quot;auto&quot;&gt;src/content.config.ts&lt;/code&gt; の実装&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;以下のコードを &lt;code dir=&quot;auto&quot;&gt;src/content.config.ts&lt;/code&gt; に記述します。&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;src/content.config.ts&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { defineCollection, z } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;astro:content&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { docsLoader } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;@astrojs/starlight/loaders&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { docsSchema } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;@astrojs/starlight/schema&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { blogSchema } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;starlight-blog/schema&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// ▼ 1. タグの正式名称を管理する辞書（キーはすべて小文字で定義します）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const &lt;/span&gt;&lt;span&gt;TAG_MAP&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Record&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;&gt; = {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Git&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;github&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;GitHub&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;nextjs&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Next.js&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;next.js&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Next.js&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;// 記号入りの表記揺れ対策&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;react&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;React&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;typescript&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;TypeScript&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;javascript&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;JavaScript&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;astro&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Astro&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;starlight&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Starlight&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;ui-ux&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;UI/UX&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;jstqb&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;JSTQB&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;claude-code&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Claude Code&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;playwright&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Playwright&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;ai&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;AI&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export const &lt;/span&gt;&lt;span&gt;collections&lt;/span&gt;&lt;span&gt; = {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;docs: &lt;/span&gt;&lt;span&gt;defineCollection&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;loader: &lt;/span&gt;&lt;span&gt;docsLoader&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;schema: &lt;/span&gt;&lt;span&gt;docsSchema&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;// docsSchema の extend を使って blogSchema を統合し、さらにタグを変換します&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;extend&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;context&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; =&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;// starlight-blog のベーススキーマを読み込む&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;const &lt;/span&gt;&lt;span&gt;baseSchema&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;blogSchema&lt;/span&gt;&lt;span&gt;(context)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;// ▼ 2. そのスキーマの `tags` フィールドの動作を上書き（拡張）する&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return &lt;/span&gt;&lt;span&gt;baseSchema&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;extend&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;tags: &lt;/span&gt;&lt;span&gt;z&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;array&lt;/span&gt;&lt;span&gt;(z&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;())&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;optional&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;transform&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;tags&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; =&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;if &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;tags)&lt;/span&gt;&lt;span&gt; return &lt;/span&gt;&lt;span&gt;undefined&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return &lt;/span&gt;&lt;span&gt;tags&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;map&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;tag&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; =&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;// 入力されたタグを小文字化してキーにする&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;const &lt;/span&gt;&lt;span&gt;key&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;tag&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;toLowerCase&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;// 辞書にあれば正式名称を、なければ入力された文字をそのまま返す&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return &lt;/span&gt;&lt;span&gt;TAG_MAP&lt;/span&gt;&lt;span&gt;[key]&lt;/span&gt;&lt;span&gt; || &lt;/span&gt;&lt;span&gt;tag&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;2-コードの解説zodの動き&quot;&gt;2. コードの解説（Zodの動き）&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;注目すべきは &lt;code dir=&quot;auto&quot;&gt;tags: z.array(z.string()).optional().transform(...)&lt;/code&gt; の部分です。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;&lt;code dir=&quot;auto&quot;&gt;z.array(z.string()).optional()&lt;/code&gt;&lt;/strong&gt;: まず、入力されたフロントマターの &lt;code dir=&quot;auto&quot;&gt;tags&lt;/code&gt; が「文字列の配列」であること（または未定義であること）を検証します。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code dir=&quot;auto&quot;&gt;.transform(...)&lt;/code&gt;&lt;/strong&gt;: 検証を通過した安全な配列データを受け取り、中のタグを一つずつループ（&lt;code dir=&quot;auto&quot;&gt;map&lt;/code&gt;）して変換します。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;フォールバック処理&lt;/strong&gt;: &lt;code dir=&quot;auto&quot;&gt;return TAG_MAP[key] || tag;&lt;/code&gt; とすることで、「辞書に登録されていないタグは、入力された文字をそのまま使う」という柔軟な仕様にしています。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;div&gt;&lt;h2 id=&quot;運用ルールタグは小文字ケバブケースで書く&quot;&gt;運用ルール：タグは「小文字・ケバブケース」で書く&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;この仕組みを導入したら、今後の記事執筆時のルールはたった1つです。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;「タグは小文字・ケバブケース（スラッグ）で入力する」&lt;/strong&gt;&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Next.jsとStarlightの連携について&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;date&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;2026-02-22&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;slug&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;blog/nextjs-starlight-integration&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;tags&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;span&gt;nextjs&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;starlight&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;ui-ux&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;このようにID感覚で機械的に入力するだけで、サイトに表示される際にはZodの変換処理が走り、&lt;strong&gt;&lt;code dir=&quot;auto&quot;&gt;Next.js&lt;/code&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;code dir=&quot;auto&quot;&gt;Starlight&lt;/code&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;code dir=&quot;auto&quot;&gt;UI/UX&lt;/code&gt;&lt;/strong&gt; という美しい正式名称になります。執筆時に大文字小文字を気にする必要はもうありません。&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;辞書にないマイナーなタグはどうなる&quot;&gt;辞書にない「マイナーなタグ」はどうなる？&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;すべてのタグを事前に辞書登録するのは面倒です。
しかし今回の実装では「辞書になければそのまま返す」仕組みにしているため、&lt;strong&gt;辞書に登録していないタグは、フロントマターに書いた通りの大文字・小文字でそのまま表示されます。&lt;/strong&gt;&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;tags&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;span&gt;Docker&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;VS Code&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;ベランダ菜園&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;（※これらは辞書にないため、そのまま &lt;code dir=&quot;auto&quot;&gt;Docker&lt;/code&gt;, &lt;code dir=&quot;auto&quot;&gt;VS Code&lt;/code&gt;, &lt;code dir=&quot;auto&quot;&gt;ベランダ菜園&lt;/code&gt; と表示されます）&lt;/p&gt;
&lt;p&gt;つまり、以下のようなハイブリッドな運用が可能です。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;基本ルール&lt;/strong&gt;: 頻繁に使う主要な技術タグは &lt;code dir=&quot;auto&quot;&gt;[git, nextjs]&lt;/code&gt; のように小文字スラッグで書き、辞書で綺麗に統一する。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;特例ルール&lt;/strong&gt;: 今回しか使わないような単語は、辞書を更新せず、表示したい文字のまま &lt;code dir=&quot;auto&quot;&gt;[Docker]&lt;/code&gt; と直接書いてしまう。&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;&lt;h2 id=&quot;まとめ&quot;&gt;まとめ&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Astro（Starlight）のContent CollectionsとZodを組み合わせることで、&lt;strong&gt;「執筆時はシンプルにID指定」「表示時は綺麗に整形された名称」&lt;/strong&gt; というモダンなタグ管理が実現できます。&lt;/p&gt;
&lt;p&gt;Zodは単なる型チェックツールではなく、データ変換のパイプラインとしても非常に優秀です。表記揺れによるタグの分散を防ぎつつ、新しいタグも気軽に追加できる柔軟性を備えたおすすめの構成ですので、ぜひ試してみてください！&lt;/p&gt;</content:encoded><category>Astro</category><category>Starlight</category><category>TypeScript</category><category>zod</category></item><item><title>WSL2環境でペット専用LoRAを訓練してStability Matrixで画像生成するまでのまとめ</title><link>https://www.indigo165e83.com/blog/20260215-wsl2-lora-stable-diffusion/</link><guid isPermaLink="true">https://www.indigo165e83.com/blog/20260215-wsl2-lora-stable-diffusion/</guid><pubDate>Sun, 15 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;div&gt;&lt;h2 id=&quot;はじめに&quot;&gt;はじめに&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;この記事では、ペット（うさぎ）のクッキーの写真から専用のLoRAモデルを訓練し、Stability Matrixで画像生成するまでの全工程を解説します。&lt;/p&gt;
&lt;p&gt;WSL2環境でのセットアップから、実際の訓練、そして画像生成まで、実際に遭遇したトラブルシューティングも含めて詳しく記録しています。&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;loraローラとは&quot;&gt;LoRA（ローラ）とは？&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;AIに追加学習させるための「軽量化技術」のことです。&lt;/p&gt;
&lt;p&gt;元の巨大なAIモデル（数GB）をまるごと再学習させるのではなく、覚えさせたい特徴（今回ならペットのクッキー）だけの「差分データ」を作ります。
これにより、短時間（数分）かつ低スペックなPCでも学習が可能になり、出来上がるファイルサイズも非常に小さくなるのが特徴です。&lt;/p&gt;

&lt;div&gt;&lt;h2 id=&quot;目次&quot;&gt;目次&lt;/h2&gt;&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;環境構築&lt;/li&gt;
&lt;li&gt;訓練データの準備&lt;/li&gt;
&lt;li&gt;LoRA訓練の実行&lt;/li&gt;
&lt;li&gt;画像生成のセットアップ&lt;/li&gt;
&lt;li&gt;トラブルシューティング&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;&lt;h2 id=&quot;プロジェクト概要&quot;&gt;プロジェクト概要&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;目標&quot;&gt;目標&lt;/h3&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;うさぎ（クッキー）専用のLoRAモデルを作成&lt;/li&gt;
&lt;li&gt;Stable Diffusionでクッキーの画像を生成できるようにする&lt;/li&gt;
&lt;li&gt;最終的にWebサイトでアニメーション化&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h3 id=&quot;使用技術スタック&quot;&gt;使用技術スタック&lt;/h3&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;OS&lt;/strong&gt;: WSL2 (Ubuntu 20.04)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Python&lt;/strong&gt;: 3.10.13 (pyenv管理)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;訓練フレームワーク&lt;/strong&gt;: Kohya SS GUI&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ベースモデル&lt;/strong&gt;: Realistic Vision V6.0 B1&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GPU&lt;/strong&gt;: NVIDIA GeForce RTX 4060 Ti (8GB VRAM)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;画像生成&lt;/strong&gt;: Stability Matrix + Stable Diffusion WebUI&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;1-環境構築&quot;&gt;1. 環境構築&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;11-python環境のセットアップ&quot;&gt;1.1 Python環境のセットアップ&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;WSL2環境では、システムのPythonではなくpyenvで管理することを推奨します。&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# pyenvのインストール&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;curl&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;https://pyenv.run&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;bash&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# .bashrcに設定を追加&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;export PYENV_ROOT=&quot;$HOME/.pyenv&quot;&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/.bashrc&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;export PATH=&quot;$PYENV_ROOT/bin:$PATH&quot;&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/.bashrc&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;eval &quot;$(pyenv init -)&quot;&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/.bashrc&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;source&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/.bashrc&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# 依存パッケージのインストール&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;apt&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;update&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;apt&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-y&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;build-essential&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;libssl-dev&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;zlib1g-dev&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;libbz2-dev &lt;/span&gt;&lt;span&gt;libreadline-dev&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;libsqlite3-dev&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;curl&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;libncursesw5-dev &lt;/span&gt;&lt;span&gt;xz-utils&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tk-dev&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;libxml2-dev&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;libxmlsec1-dev &lt;/span&gt;&lt;span&gt;libffi-dev&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;liblzma-dev&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Python 3.10.13のインストール&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;pyenv&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;3.10.13&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;重要ポイント&lt;/strong&gt;: Python 3.12ではなく3.10系を使用してください。PyTorchや依存パッケージの互換性の問題を避けるためです。&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;12-kohya-ss-guiのインストール&quot;&gt;1.2 Kohya SS GUIのインストール&lt;/h3&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# プロジェクトディレクトリ作成&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;mkdir&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-p&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/ml_projects&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;cd&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/ml_projects&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Kohya SSをクローン&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;clone&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;https://github.com/bmaltais/kohya_ss.git&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;cd&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;kohya_ss&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Python 3.10を使用&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;pyenv&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;local&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;3.10.13&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# セットアップ実行&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;./setup.sh&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# GUI起動&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;./gui.sh&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--headless&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;ブラウザで &lt;code dir=&quot;auto&quot;&gt;http://localhost:7860&lt;/code&gt; にアクセスしてGUIが表示されればOKです。&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;2-訓練データの準備&quot;&gt;2. 訓練データの準備&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;21-写真の収集&quot;&gt;2.1 写真の収集&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;必要な写真枚数&lt;/strong&gt;: 15〜30枚（推奨: 20〜25枚）&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;撮影のポイント&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;様々な角度（正面、側面、後ろ、斜め）&lt;/li&gt;
&lt;li&gt;様々なポーズ（座る、立つ、食べる、毛づくろい）&lt;/li&gt;
&lt;li&gt;明るい場所で撮影&lt;/li&gt;
&lt;li&gt;ピントをしっかり合わせる&lt;/li&gt;
&lt;li&gt;背景はシンプルに&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;今回はペットカメラの映像から53枚の画像を抽出しました。&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;22-画像の前処理&quot;&gt;2.2 画像の前処理&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;ペットカメラの画像から、背景やうさぎ以外の物体を取り除き、訓練向きの画像にするよう画像を処理します：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;#!/usr/bin/env python3&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; os&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; pathlib &lt;/span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; Path&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;PIL&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; Image&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; rembg &lt;/span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; remove&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; numpy &lt;/span&gt;&lt;span&gt;as&lt;/span&gt;&lt;span&gt; np&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# パス設定&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;input_dir &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Path&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;training_data/cookie/img/20_cookie_rabbit&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;output_dir &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Path&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;training_data/cookie/processed&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;output_dir.&lt;/span&gt;&lt;span&gt;mkdir&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;parents&lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;True&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;exist_ok&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;True&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# 画像ファイルを取得&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;image_files &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sorted&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;input_dir.&lt;/span&gt;&lt;span&gt;glob&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;*.jpg&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;print&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;f&lt;/span&gt;&lt;span&gt;&quot;Found &lt;/span&gt;&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;len&lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;image_files&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; images&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;for&lt;/span&gt;&lt;span&gt; idx, img_path &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;enumerate&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;image_files&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;):&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;print&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;f&lt;/span&gt;&lt;span&gt;&quot;Processing &lt;/span&gt;&lt;span&gt;{idx}&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;len&lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;image_files&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;{img_path.name}&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;try&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;# 画像を読み込み&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;img &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; Image.&lt;/span&gt;&lt;span&gt;open&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;img_path&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;# 背景除去&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;img_no_bg &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;remove&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;img&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;# クロッピング（被写体を中心に）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;img_array &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; np.&lt;/span&gt;&lt;span&gt;array&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;img_no_bg&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;alpha &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; img_array[:, :, &lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;coords &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; np.&lt;/span&gt;&lt;span&gt;column_stack&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;np.&lt;/span&gt;&lt;span&gt;where&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;alpha &lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;len&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;coords&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;continue&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;y_min, x_min &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; coords.&lt;/span&gt;&lt;span&gt;min&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;axis&lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;y_max, x_max &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; coords.&lt;/span&gt;&lt;span&gt;max&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;axis&lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;# 余白追加&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;height &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; y_max &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; y_min&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;width &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; x_max &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; x_min&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;margin &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;max&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;height&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; width&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0.1&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;y_min &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;max&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; y_min &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; margin&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;y_max &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;min&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;img_array.shape&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; y_max &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; margin&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;x_min &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;max&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; x_min &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; margin&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;x_max &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;min&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;img_array.shape&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; x_max &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; margin&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;cropped &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; img_no_bg.&lt;/span&gt;&lt;span&gt;crop&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;(x_min, y_min, x_max, y_max)&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;# 512x512にリサイズ&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;size &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;max&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;cropped.size&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;square_img &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; Image.&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;RGBA&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; (size, size)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;255&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;255&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;255&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;paste_x &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; (size &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; cropped.size[&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;]) &lt;/span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;paste_y &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; (size &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; cropped.size[&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;]) &lt;/span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;square_img.&lt;/span&gt;&lt;span&gt;paste&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;cropped&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; (paste_x, paste_y)&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;final_img &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; square_img.&lt;/span&gt;&lt;span&gt;resize&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;512&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;512&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; Image.Resampling.LANCZOS&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;# 白背景に変換&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;white_bg &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; Image.&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;RGB&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;512&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;512&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;255&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;255&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;255&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;white_bg.&lt;/span&gt;&lt;span&gt;paste&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;final_img&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; final_img&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;# 保存&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;output_path &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; output_dir &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;f&lt;/span&gt;&lt;span&gt;&quot;cookie_&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;idx&lt;/span&gt;&lt;span&gt;:03d&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;.jpg&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;white_bg.&lt;/span&gt;&lt;span&gt;save&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;output_path&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;JPEG&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;quality&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;95&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;except&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Exception&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;as&lt;/span&gt;&lt;span&gt; e:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;print&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;f&lt;/span&gt;&lt;span&gt;&quot;Error: &lt;/span&gt;&lt;span&gt;{e}&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;continue&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;print&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;f&lt;/span&gt;&lt;span&gt;&quot;Processing complete!&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;23-ディレクトリ構造&quot;&gt;2.3 ディレクトリ構造&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;訓練用のディレクトリ構造は以下の通り：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;~/ml_projects/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;├── kohya_ss/                    # 訓練ツール&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;├── models/                      # ベースモデル&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;│   └── realisticVisionV60B1_v51HyperVAE.safetensors&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;└── training_data/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;└── cookie_final/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;└── img/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;└── 20_cookie_rabbit/  # 20は繰り返し回数&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;├── cookie_001.jpg&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;├── cookie_002.jpg&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;└── ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;重要&lt;/strong&gt;: フォルダ名は &lt;code dir=&quot;auto&quot;&gt;{繰り返し回数}_{トリガーワード}&lt;/code&gt; の形式にします。&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;3-lora訓練の実行&quot;&gt;3. LoRA訓練の実行&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;31-kohya-ss-guiでの設定&quot;&gt;3.1 Kohya SS GUIでの設定&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;LoRAタブで以下のパラメータを設定：&lt;/p&gt;
&lt;div&gt;&lt;h4 id=&quot;model設定&quot;&gt;Model設定&lt;/h4&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Pretrained model&lt;/strong&gt;: &lt;code dir=&quot;auto&quot;&gt;/path/to/realisticVisionV60B1_v51HyperVAE.safetensors&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Image folder&lt;/strong&gt;: &lt;code dir=&quot;auto&quot;&gt;/path/to/training_data/cookie_final/img&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Output folder&lt;/strong&gt;: &lt;code dir=&quot;auto&quot;&gt;/path/to/outputs/cookie_lora&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Save as&lt;/strong&gt;: &lt;code dir=&quot;auto&quot;&gt;safetensors&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Save precision&lt;/strong&gt;: &lt;code dir=&quot;auto&quot;&gt;fp16&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h4 id=&quot;parameters---basic&quot;&gt;Parameters - Basic&lt;/h4&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Train batch size&lt;/strong&gt;: 1&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Epoch&lt;/strong&gt;: 10&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Save every N epochs&lt;/strong&gt;: 2&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Learning rate&lt;/strong&gt;: 0.0001 (1e-4)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;LR Scheduler&lt;/strong&gt;: cosine&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Optimizer&lt;/strong&gt;: AdamW8bit&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Max resolution&lt;/strong&gt;: 512,512&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h4 id=&quot;parameters---advanced&quot;&gt;Parameters - Advanced&lt;/h4&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Network Rank (Dimension)&lt;/strong&gt;: 32&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Network Alpha&lt;/strong&gt;: 16&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CrossAttention&lt;/strong&gt;: xformers&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h3 id=&quot;32-訓練の実行&quot;&gt;3.2 訓練の実行&lt;/h3&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Kohya SS GUIで&quot;Start training&quot;をクリック&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# または、コマンドラインから：&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;cd&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/ml_projects/kohya_ss&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;source&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;venv/bin/activate&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;accelerate&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;launch&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--mixed_precision&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fp16&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;sd-scripts/train_network.py&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--config_file&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;outputs/cookie_lora/config.toml&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;33-訓練結果&quot;&gt;3.3 訓練結果&lt;/h3&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Training Details:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;- Images: 53 (with 20 repeats = 1060 steps)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;- Epochs: 10&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;- Total steps: 1600&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;- GPU: RTX 4060 Ti&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;- Training time: 4分52秒&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;- Final loss: 0.0375&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;非常に高速に訓練が完了しました。RTX 4060 Tiの性能が素晴らしいです。&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;4-トラブルシューティング&quot;&gt;4. トラブルシューティング&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;41-numpy互換性エラー&quot;&gt;4.1 NumPy互換性エラー&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;エラー内容&lt;/strong&gt;:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;A module that was compiled using NumPy 1.x cannot be run in NumPy 2.2.6&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;AttributeError: _ARRAY_API not found&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;解決方法&lt;/strong&gt;:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;cd&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/ml_projects/kohya_ss&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;source&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;venv/bin/activate&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;pip&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;numpy&amp;#x3C;2&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--break-system-packages&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;NumPy 2.xとNumPy 1.xの互換性問題です。TensorFlowなどの依存パッケージがNumPy 1.xでコンパイルされているため、ダウングレードが必要です。&lt;/p&gt;
&lt;p&gt;※ このオプションは本来推奨されません。（仮想環境外への影響がある）今回は仮想環境内での実行を前提としているため使用しましたが、本来は注意が必要です&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;42-stable-diffusion-webuiのインストールエラー&quot;&gt;4.2 Stable Diffusion WebUIのインストールエラー&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;WSL2環境でStable Diffusion WebUIのインストールに失敗しました：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;エラー内容&lt;/strong&gt;:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ModuleNotFoundError: No module named &apos;pkg_resources&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ERROR: Failed to build CLIP&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;試した解決策&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;setuptoolsの再インストール&lt;/li&gt;
&lt;li&gt;仮想環境の再作成&lt;/li&gt;
&lt;li&gt;CLIPの手動インストール&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;など、様々な方法を試しました。ここでかなり時間を溶かしましたが、結局、WSL2環境で解決できませんでした。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;最終的な解決&lt;/strong&gt;: WSL2でのローカルインストールを諦め、Windows側でStability Matrixを使用することに決定しました。&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;5-画像生成のセットアップ&quot;&gt;5. 画像生成のセットアップ&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;51-stability-matrixの使用&quot;&gt;5.1 Stability Matrixの使用&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;最終的にWindows 11でStability Matrixを使用しました：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/LykosAI/StabilityMatrix&quot;&gt;Stability Matrix&lt;/a&gt;をダウンロード・インストール&lt;/li&gt;
&lt;li&gt;LoRAファイルとベースモデルを配置&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# WSL2からWindowsへファイルコピー&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;cp&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/ml_projects/outputs/cookie_lora/cookie_lora.safetensors&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;   &lt;/span&gt;&lt;span&gt;/mnt/d/[your-path-to-stability-matrix]/Data/Models/Lora/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;cp&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/ml_projects/models/realisticVisionV60B1_v51HyperVAE.safetensors&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;   &lt;/span&gt;&lt;span&gt;/mnt/d/[your-path-to-stability-matrix]/Data/Models/StableDiffusion/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;52-画像生成&quot;&gt;5.2 画像生成&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;プロンプト例&lt;/strong&gt;:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Positive:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;rabbit, cookie_lora, (white and brown patches), cute rabbit,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;fluffy fur, photorealistic, detailed fur texture,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;#x3C;lora:cookie_lora:0.8&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Negative:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;blurry, low quality, distorted, deformed, ugly, bad anatomy&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;パラメータ&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Sampling method: DPM++ 2M SDE&lt;/li&gt;
&lt;li&gt;Sampling steps: 20&lt;/li&gt;
&lt;li&gt;Width: 512&lt;/li&gt;
&lt;li&gt;Height: 512&lt;/li&gt;
&lt;li&gt;CFG Scale: 4&lt;/li&gt;
&lt;li&gt;LoRA weight: 0.8&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;技術的な学び&quot;&gt;技術的な学び&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;pyenvの重要性&quot;&gt;pyenvの重要性&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;WSL2環境では、システムのPythonではなくpyenvで管理することが非常に重要です。特に：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;複数のプロジェクトで異なるPythonバージョンを使い分けられる&lt;/li&gt;
&lt;li&gt;システムのパッケージマネージャーと競合しない&lt;/li&gt;
&lt;li&gt;プロジェクトごとに独立した環境を維持できる&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h3 id=&quot;numpyバージョン管理&quot;&gt;NumPyバージョン管理&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;機械学習プロジェクトでは、NumPyのバージョンに特に注意が必要です：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;TensorFlow、PyTorchなどはNumPy 1.xでビルドされていることが多い&lt;/li&gt;
&lt;li&gt;NumPy 2.xとの互換性問題は頻繁に発生する&lt;/li&gt;
&lt;li&gt;&lt;code dir=&quot;auto&quot;&gt;numpy&amp;#x3C;2&lt;/code&gt;を明示的に指定することを推奨&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h3 id=&quot;wsl2-vs-windows-native&quot;&gt;WSL2 vs Windows Native&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;今回の経験から：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;訓練&lt;/strong&gt;: WSL2が最適（Linuxネイティブ、GPUサポート良好）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;画像生成&lt;/strong&gt;: Windowsネイティブツール（Stability Matrix）の方が簡単&lt;/li&gt;
&lt;li&gt;ハイブリッドアプローチが実用的&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h3 id=&quot;lora訓練のベストプラクティス&quot;&gt;LoRA訓練のベストプラクティス&lt;/h3&gt;&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;データ品質&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;様々な角度・ポーズの画像を用意&lt;/li&gt;
&lt;li&gt;背景除去で訓練対象を明確化&lt;/li&gt;
&lt;li&gt;512x512への統一リサイズ&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;パラメータ調整&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Network Rank: 32（標準）&lt;/li&gt;
&lt;li&gt;Learning Rate: 1e-4（安定）&lt;/li&gt;
&lt;li&gt;Epochs: 10（過学習を避ける）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;繰り返し回数&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;画像枚数が少ない場合は繰り返しを増やす&lt;/li&gt;
&lt;li&gt;今回: 53枚 × 20回 = 1060 steps&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;&lt;h2 id=&quot;まとめ&quot;&gt;まとめ&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;WSL2環境でペット専用のLoRAモデルを訓練し、Stable Diffusionで画像生成するまでの全工程を実施しました。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;成功のポイント&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;pyenvでPython環境を適切に管理&lt;/li&gt;
&lt;li&gt;NumPyバージョンの互換性に注意&lt;/li&gt;
&lt;li&gt;Kohya SS GUIで効率的な訓練&lt;/li&gt;
&lt;li&gt;Stability Matrixで簡単な画像生成環境構築&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;訓練結果&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;訓練時間: わずか4分52秒&lt;/li&gt;
&lt;li&gt;生成画像の品質: ペットの特徴をよく捉えている&lt;/li&gt;
&lt;li&gt;LoRAファイルサイズ: 26MB&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;今後の展開&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;生成した画像をWebサイトでアニメーション化&lt;/li&gt;
&lt;li&gt;様々なポーズ・表情のバリエーション生成&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;この記事が、ペット専用AIモデルを作成したい方の参考になれば幸いです。&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;参考リンク&quot;&gt;参考リンク&lt;/h2&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/bmaltais/kohya_ss&quot;&gt;Kohya SS GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/LykosAI/StabilityMatrix&quot;&gt;Stability Matrix&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://civitai.com/&quot;&gt;Civitai&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/pyenv/pyenv&quot;&gt;pyenv GitHub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;成果物&quot;&gt;成果物&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;実際にこのプロセスを経て生成されたクッキー（のAI分身）がこちらです。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.indigo165e83.com/_astro/00001-2843936366.BWUtiSMb_Z6LRhm.webp&quot; alt=&quot;Generated Cookie Image&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;896&quot; height=&quot;1152&quot;&gt;
&lt;em&gt;クッキーが5匹！？&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;これは「1rabbit」というプロンプトに対して、解像度（896x1152）が広すぎたために「スペース余ってるし、もう何匹か置いとくか！」とAIが気を利かせた結果のようです。&lt;/p&gt;
&lt;p&gt;画像サイズを512x512にして再度生成しました。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.indigo165e83.com/_astro/00000-3162487967.kvHb445j_ZMGOlp.webp&quot; alt=&quot;Generated Cookie Image&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;512&quot; height=&quot;512&quot;&gt;
&lt;em&gt;クッキーが2匹。物理法則が怪しげです。&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.indigo165e83.com/_astro/00001-2122136207.CRU3NtCB_4unAf.webp&quot; alt=&quot;Generated Cookie Image&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;512&quot; height=&quot;512&quot;&gt;
&lt;em&gt;こちらもクッキーがまだ2匹。親子・・・？&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;クッキーの毛の質感や模様がリアルに再現されているのはGoodですが、物理法則が少し怪しげです。かわいさも足りないです。&lt;/p&gt;
&lt;p&gt;学習データ用の画像に「毛づくろい中の変なポーズ」が多く含みすぎていた可能性が大です。もう少しバリエーション豊かにする必要がありそうです。&lt;/p&gt;
&lt;p&gt;もっと調整して、クッキーのかわいさが引き立つような画像を生成しようと思います！&lt;/p&gt;</content:encoded><category>AI</category><category>Machine Learning</category><category>Stability Matrix</category><category>LoRA</category><category>WSL2</category><category>Deep Learning</category></item><item><title>Stripeアカウント閉鎖からGitHub Sponsors成功まで：AI画像サービスの決済問題解決記録</title><link>https://www.indigo165e83.com/blog/20260213-stripe-rejection-to-github-sponsors/</link><guid isPermaLink="true">https://www.indigo165e83.com/blog/20260213-stripe-rejection-to-github-sponsors/</guid><pubDate>Fri, 13 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;div&gt;&lt;h2 id=&quot;はじめに&quot;&gt;はじめに&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;AI生成画像を無料で提供する「Free Images」を運営していたある日、Stripeから一通のメールが届きました。&lt;/p&gt;
&lt;p&gt;「&lt;strong&gt;カードネットワークパートナーの制限により貴社アカウントのサポート継続が困難となりました&lt;/strong&gt;」&lt;/p&gt;
&lt;p&gt;この記事は、突然の決済手段喪失から、GitHub Sponsorsで支援を受け付けられるようになるまでの2日間の記録です。同じような問題に直面している方、特にAI関連サービスを運営している方の参考になれば幸いです。&lt;/p&gt;

&lt;hr&gt;
&lt;div&gt;&lt;h2 id=&quot;1突然のアカウント閉鎖通知&quot;&gt;1：突然のアカウント閉鎖通知&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;運営していたサービス&quot;&gt;運営していたサービス&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href=&quot;https://free-images.indigo165e83.com&quot;&gt;Free Images&lt;/a&gt; は、AI生成画像を無料で提供するWebサービスです。誰でも自由にダウンロードして使える画像を提供し、運営費用をまかなうためにStripeの支援リンクを設置していました。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ビジネスモデル：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;画像配布：完全無料&lt;/li&gt;
&lt;li&gt;運営費：任意の支援（Stripe Payment Links）&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h3 id=&quot;届いた通知メール&quot;&gt;届いた通知メール&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;2026年2月13日、Stripeから以下のメールが届きました：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;お客様のビジネス「Free Images」(Stripe アカウント ID: xxx) に関する追加情報をご提供いただきました。&lt;/p&gt;
&lt;p&gt;審査が無事に完了しましたので、Stripe で決済処理を継続していただけます。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;（数時間後）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;誠に心苦しいのですが、貴社のビジネスについて再度確認させていただきました結果、&lt;strong&gt;カードネットワークパートナーの制限により貴社アカウントのサポート継続が困難&lt;/strong&gt;となりました。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;本決定は最終的なものであり、詳細情報の提供や再審査も叶いません&lt;/strong&gt;ことをご理解賜りたく存じます。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;一度は承認されたものの、数時間後に覆される。しかも「最終的な決定」「再審査不可」という強い表現。これは想定外の事態でした。&lt;/p&gt;
&lt;hr&gt;
&lt;div&gt;&lt;h2 id=&quot;2カードネットワーク制限という壁&quot;&gt;2：「カードネットワーク制限」という壁&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;stripeレベルではなかった&quot;&gt;Stripeレベルではなかった&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;最初は「Stripeに何か説明すれば解決するだろう」と考えていました。しかし、問題はもっと深刻でした。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;決済の階層構造：&lt;/strong&gt;&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;お客様&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;↓&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Stripe（決済代行業者）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;↓&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Visa/Mastercard（カードネットワーク）← ★ここが拒否した&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;↓&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;カード発行会社&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;↓&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;銀行口座&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Stripeではなく、その上のレイヤーである&lt;strong&gt;Visa/Mastercardなどのカードネットワーク&lt;/strong&gt;が制限をかけていたのです。&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;なぜai画像生成が拒否されたのか&quot;&gt;なぜAI画像生成が拒否されたのか&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;推測される理由：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;新しい技術への警戒&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;AI画像生成は2022年頃から急速に普及した新しい分野&lt;/li&gt;
&lt;li&gt;カード会社のリスク評価基準が未整備&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;「画像サイト」への警戒&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;過去に成人向けコンテンツなどの問題サイトが多数存在&lt;/li&gt;
&lt;li&gt;「画像」というキーワードだけで自動的に高リスクカテゴリに分類された可能性&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;著作権リスク&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;AI生成コンテンツの著作権は法的にグレーゾーン&lt;/li&gt;
&lt;li&gt;訴訟リスクを避けたいカード会社の判断&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;ビジネスモデルの不透明性&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;「無料配布 + 任意の支援」は対価が不明確&lt;/li&gt;
&lt;li&gt;マネーロンダリングリスクと判断された可能性&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;&lt;h3 id=&quot;連鎖的な影響&quot;&gt;連鎖的な影響&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;さらに悪いことに、同じA銀行口座に紐付けていた別のアカウント「Indigo Works」も審査保留状態になりました。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;影響範囲：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;❌ Free Images（閉鎖）&lt;/li&gt;
&lt;li&gt;⚠️ Indigo Works（審査中・一時停止）&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;div&gt;&lt;h2 id=&quot;3代替手段の模索&quot;&gt;3：代替手段の模索&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;試した方法github-sponsorsに直接申請&quot;&gt;試した方法：GitHub Sponsorsに直接申請&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/sponsors&quot;&gt;GitHub Sponsors&lt;/a&gt;なら、「オープンソース開発者への支援」という明確な文脈があるので通るかもしれないと考えました。&lt;/p&gt;
&lt;p&gt;「変えられない情報（名前、住所、生年月日）はそのままに、変えられる情報を変える」という戦略に切り替えました。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;変更した要素：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;✅ 銀行口座：A銀行 → &lt;strong&gt;B銀行&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;⚠️ 電話番号：変更なし&lt;/li&gt;
&lt;li&gt;⚠️ メールアドレス：変更なし（&lt;a href=&quot;mailto:indigo165e83@gmail.com&quot;&gt;indigo165e83@gmail.com&lt;/a&gt;）&lt;/li&gt;
&lt;li&gt;⚠️ 名前、住所、生年月日：変更不可（虚偽申告になる）&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h3 id=&quot;成功&quot;&gt;成功&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;すべてのStripe Connectアカウントを削除&lt;/strong&gt;してから24時間待ち、新しい銀行口座で再申請しました。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;結果：成功！&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;GitHub Sponsorsのダッシュボードに緑色のメッセージが表示されました：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;🎉 &lt;strong&gt;Your GitHub Sponsors profile was approved and is now public!&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;なんとか成功しました。&lt;/p&gt;
&lt;hr&gt;
&lt;div&gt;&lt;h2 id=&quot;4成功の要因分析&quot;&gt;4：成功の要因分析&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;なぜ通ったのか&quot;&gt;なぜ通ったのか？&lt;/h3&gt;&lt;/div&gt;
&lt;div&gt;&lt;h4 id=&quot;仮説1銀行口座が重要な照合要素&quot;&gt;仮説1：銀行口座が重要な照合要素&lt;/h4&gt;&lt;/div&gt;
&lt;p&gt;B銀行は過去の問題アカウントと紐付いていない「クリーンな」口座でした。Stripeのリスク評価において、銀行口座情報の重要度が想像以上に高かった可能性があります。&lt;/p&gt;
&lt;div&gt;&lt;h4 id=&quot;仮説2github経由の信頼性&quot;&gt;仮説2：GitHub経由の信頼性&lt;/h4&gt;&lt;/div&gt;
&lt;p&gt;通常のStripe申請とGitHub Sponsorsでは、審査フローが異なります：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;通常のStripe：&lt;/strong&gt;&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;個人/企業 → Stripe → カードネットワーク&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;GitHub Sponsors：&lt;/strong&gt;&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;個人 → GitHub → Stripe Connect → カードネットワーク&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;       &lt;/span&gt;&lt;/span&gt;&lt;span&gt;★GitHubの信頼性が加味される&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;GitHubというプラットフォームの信頼性が、審査にプラスに働いた可能性があります。&lt;/p&gt;
&lt;div&gt;&lt;h4 id=&quot;仮説3ビジネスモデルの違い&quot;&gt;仮説3：ビジネスモデルの違い&lt;/h4&gt;&lt;/div&gt;






























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;要素&lt;/th&gt;&lt;th&gt;Free Images（拒否）&lt;/th&gt;&lt;th&gt;GitHub Sponsors（承認）&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;プラットフォーム&lt;/td&gt;&lt;td&gt;独自サイト&lt;/td&gt;&lt;td&gt;GitHub&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;カテゴリ&lt;/td&gt;&lt;td&gt;「画像サイト」&lt;/td&gt;&lt;td&gt;「開発者支援」&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ビジネスモデル&lt;/td&gt;&lt;td&gt;無料配布 + 支援&lt;/td&gt;&lt;td&gt;オープンソース支援&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;信頼性&lt;/td&gt;&lt;td&gt;不明&lt;/td&gt;&lt;td&gt;GitHubが保証&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;「AI画像サイト」より「オープンソース開発者への支援」の方が、カードネットワークにとって受け入れやすいビジネスモデルだったと推測されます。&lt;/p&gt;
&lt;hr&gt;
&lt;div&gt;&lt;h2 id=&quot;5github-sponsorsの設定&quot;&gt;5：GitHub Sponsorsの設定&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;プロフィール作成&quot;&gt;プロフィール作成&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;承認後、以下の設定を行いました：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;自己紹介文：&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I’m creating and maintaining &lt;strong&gt;Free Images&lt;/strong&gt;, a project that provides &lt;strong&gt;free AI-generated images&lt;/strong&gt; for designers, developers, and creators worldwide.&lt;/p&gt;
&lt;p&gt;Your support helps me cover server costs, create more free content, and develop new features.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;支援ティア設定：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;$1/月 - コーヒー1杯分の支援 ☕&lt;/li&gt;
&lt;li&gt;$5/月 - コンテンツ作成の支援 🎨&lt;/li&gt;
&lt;li&gt;$3 - 1回限りの支援 🙏&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h3 id=&quot;リポジトリへの統合&quot;&gt;リポジトリへの統合&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;GitHub Sponsorsボタンをリポジトリに表示するため、&lt;code dir=&quot;auto&quot;&gt;.github/FUNDING.yml&lt;/code&gt;ファイルを作成：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;github&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;indigo165e83&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;これだけで、リポジトリに「Sponsor」ボタンが自動的に表示されます。&lt;/p&gt;
&lt;p&gt;詳細は&lt;a href=&quot;https://docs.github.com/sponsors&quot;&gt;GitHub Sponsors公式ドキュメント&lt;/a&gt;を参照してください。&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;サイトへのリンク追加&quot;&gt;サイトへのリンク追加&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;Free ImagesとIndigo Worksの両サイトに、GitHub Sponsorsへのリンクを追加しました。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;実装例は&lt;a href=&quot;https://github.com/indigo165e83/free-images&quot;&gt;こちらのリポジトリ&lt;/a&gt;で公開しています。&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;div&gt;&lt;h2 id=&quot;学んだ教訓&quot;&gt;学んだ教訓&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;1-単一の決済手段に依存しない&quot;&gt;1. 単一の決済手段に依存しない&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;今回の経験から、&lt;strong&gt;複数の決済手段を用意することの重要性&lt;/strong&gt;を痛感しました。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;推奨されるアーキテクチャ：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;メイン：GitHub Sponsors&lt;/li&gt;
&lt;li&gt;サブ：Ko-fi（PayPal専用モード）&lt;/li&gt;
&lt;li&gt;バックアップ：PayPal直接、銀行振込&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h3 id=&quot;2-新しい技術はリスク評価が厳しい&quot;&gt;2. 新しい技術はリスク評価が厳しい&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;AI関連サービスは、カード会社の既存リスク評価基準に該当しません。そのため、&lt;strong&gt;より慎重に扱われる&lt;/strong&gt;傾向があります。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;対策：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;利用規約で著作権侵害を禁止&lt;/li&gt;
&lt;li&gt;コンテンツモデレーションを実装&lt;/li&gt;
&lt;li&gt;特定商取引法に基づく表記を整備&lt;/li&gt;
&lt;li&gt;プラットフォームの信頼性を借りる（GitHubなど）&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h3 id=&quot;3-ブラックリストではなく制限対象&quot;&gt;3. 「ブラックリスト」ではなく「制限対象」&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;Stripeからの拒否は、「犯罪者」や「詐欺師」のようなブラックリストではありません。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;正確には：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;❌ ブラックリスト（永久的・全サービスで拒否）&lt;/li&gt;
&lt;li&gt;✅ 制限対象（特定のビジネスモデルが基準に合わない）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;時間と条件次第で、将来的にStripeを使える可能性もあります。&lt;/p&gt;
&lt;hr&gt;
&lt;div&gt;&lt;h2 id=&quot;最終的なアーキテクチャ&quot;&gt;最終的なアーキテクチャ&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;現在のFree Imagesの支援受付体制：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;メイン：GitHub Sponsors&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;プラットフォーム：GitHub&lt;/li&gt;
&lt;li&gt;決済処理：Stripe Connect&lt;/li&gt;
&lt;li&gt;銀行口座：B銀行&lt;/li&gt;
&lt;li&gt;手数料：0%（GitHubが負担）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;将来の拡張：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ko-fi（PayPal専用モード）&lt;/li&gt;
&lt;li&gt;PayPal直接寄付ボタン&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;div&gt;&lt;h2 id=&quot;プライバシーについて&quot;&gt;プライバシーについて&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;GitHub Sponsorsで支援を受ける際、&lt;strong&gt;個人情報は保護されます&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;支援者に見える情報：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;✅ GitHubユーザー名（indigo165e83）&lt;/li&gt;
&lt;li&gt;✅ プロフィール画像&lt;/li&gt;
&lt;li&gt;✅ 自己紹介文&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;支援者に見えない情報：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;❌ 本名&lt;/li&gt;
&lt;li&gt;❌ 住所&lt;/li&gt;
&lt;li&gt;❌ 電話番号&lt;/li&gt;
&lt;li&gt;❌ 銀行口座&lt;/li&gt;
&lt;li&gt;❌ メールアドレス（デフォルト設定）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;詳細は&lt;a href=&quot;https://docs.github.com/sponsors/receiving-sponsorships-through-github-sponsors/managing-your-payouts-from-github-sponsors&quot;&gt;GitHub Sponsorsのプライバシー設定&lt;/a&gt;を確認してください。&lt;/p&gt;
&lt;hr&gt;
&lt;div&gt;&lt;h2 id=&quot;まとめ&quot;&gt;まとめ&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;重要なポイント&quot;&gt;重要なポイント&lt;/h3&gt;&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;カードネットワーク制限はStripeレベルより深刻&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Visa/Mastercardが直接拒否している&lt;/li&gt;
&lt;li&gt;再審査はほぼ不可能&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;AI関連サービスはリスク評価が厳しい&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;新しい技術への警戒&lt;/li&gt;
&lt;li&gt;著作権の不透明性&lt;/li&gt;
&lt;li&gt;既存カテゴリに該当しない&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;複数の決済手段を用意すべき&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;単一障害点を避ける&lt;/li&gt;
&lt;li&gt;フォールバック戦略が重要&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;GitHub Sponsorsは有力な選択肢&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;オープンソース開発者への支援という明確な文脈&lt;/li&gt;
&lt;li&gt;GitHubの信頼性が審査にプラスに働く&lt;/li&gt;
&lt;li&gt;手数料0%&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;諦めずに試行錯誤する価値がある&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;同じサービスでもアプローチを変えれば成功する可能性&lt;/li&gt;
&lt;li&gt;銀行口座を変える、プラットフォームを変えるなど&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;&lt;h3 id=&quot;現在の状況&quot;&gt;現在の状況&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;GitHub Sponsorsで無事に支援を受け付けられるようになり、Free Imagesプロジェクトを継続できています。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;GitHub Sponsorsプロフィール：&lt;/strong&gt;
&lt;a href=&quot;https://github.com/sponsors/indigo165e83&quot;&gt;https://github.com/sponsors/indigo165e83&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;プロジェクト：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://free-images.indigo165e83.com&quot;&gt;Free Images&lt;/a&gt; - AI生成画像の無料配布&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.indigo165e83.com&quot;&gt;Indigo Works&lt;/a&gt; - ポートフォリオ、技術ブログなどのサイト&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;div&gt;&lt;h2 id=&quot;さいごに&quot;&gt;さいごに&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;この記事が、同じような問題に直面している方の助けになれば幸いです。&lt;/p&gt;
&lt;p&gt;AI関連サービスを運営している方、決済手段の選択に悩んでいる方、突然アカウントを閉鎖された方。&lt;strong&gt;諦めずに、別のアプローチを試してみてください。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;質問やフィードバックは、&lt;a href=&quot;https://github.com/indigo165e83/indigo-works/issues&quot;&gt;GitHub Issues&lt;/a&gt;または&lt;a href=&quot;https://github.com/sponsors/indigo165e83&quot;&gt;GitHub Sponsors経由&lt;/a&gt;でお待ちしています。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;関連リンク：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.github.com/sponsors&quot;&gt;GitHub Sponsors Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stripe.com/legal/restricted-businesses&quot;&gt;Stripe Restricted Businesses&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/indigo165e83/free-images&quot;&gt;Free Images - GitHub Repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/indigo165e83/indigo-works&quot;&gt;Indigo Works - GitHub Repository&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>Stripe</category><category>GitHub Sponsors</category><category>AI</category><category>決済</category><category>トラブルシューティング</category></item><item><title>BigQueryでテーブルを結合する：JOIN構文の完全ガイド</title><link>https://www.indigo165e83.com/blog/20260212-bigquery-join-tables/</link><guid isPermaLink="true">https://www.indigo165e83.com/blog/20260212-bigquery-join-tables/</guid><pubDate>Thu, 12 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;BigQueryでデータ分析を行う際、複数のテーブルを結合して情報を統合することは日常的な作業です。今回は、JOIN構文の基本から実践的な使い分けまで、わかりやすく解説します。&lt;/p&gt;

&lt;div&gt;&lt;h2 id=&quot;bigqueryのテーブル参照の基本&quot;&gt;BigQueryのテーブル参照の基本&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;まず、BigQueryでテーブルを参照する基本的な構文を確認しておきましょう。&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;SELECT&lt;/span&gt;&lt;span&gt; カラム名&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;FROM&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;プロジェクトID&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;データセット名&lt;/span&gt;&lt;span&gt;.テーブル名&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;通常、テーブル名全体をバッククォート（`）で囲む必要はありません。カラム名やテーブル名に予約語や特殊文字が含まれる場合のみ、個別の要素をバッククォートで囲みます。&lt;/p&gt;
&lt;aside aria-label=&quot;バッククォートの注意点&quot;&gt;&lt;p aria-hidden=&quot;true&quot;&gt;バッククォートの注意点&lt;/p&gt;&lt;div&gt;&lt;p&gt;テーブル名全体を &lt;code dir=&quot;auto&quot;&gt;`project-id.dataset.table`&lt;/code&gt; のように囲むと、区切り文字（&lt;code dir=&quot;auto&quot;&gt;.&lt;/code&gt;）が認識されずエラーになることがあります。基本的には素のテーブル名で記述しましょう。&lt;/p&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;div&gt;&lt;h2 id=&quot;実践2つのテーブルを結合する&quot;&gt;実践：2つのテーブルを結合する&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;以下のような2つのテーブルがあるとします。&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;-- テーブル1: publishers（出版社情報）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;SELECT&lt;/span&gt;&lt;span&gt; id, &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;, country&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;FROM&lt;/span&gt;&lt;span&gt; my&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;project&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;analytics&lt;/span&gt;&lt;span&gt;.publishers&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;-- テーブル2: contracts（契約情報）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;SELECT&lt;/span&gt;&lt;span&gt; id, fee_rate, publisher_id&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;FROM&lt;/span&gt;&lt;span&gt; my&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;project&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;analytics&lt;/span&gt;&lt;span&gt;.contracts&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;これらを &lt;code dir=&quot;auto&quot;&gt;publisher_id&lt;/code&gt; で結合して、「どの出版社がどの手数料率で契約しているか」を一覧化したいとします。&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;inner-join両方に存在するデータのみ取得&quot;&gt;INNER JOIN：両方に存在するデータのみ取得&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;最も基本的な結合方法です。両方のテーブルに存在するレコードのみを取得します。&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;SELECT&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AS&lt;/span&gt;&lt;span&gt; publisher_id,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AS&lt;/span&gt;&lt;span&gt; publisher_name,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;country&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;c&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AS&lt;/span&gt;&lt;span&gt; contract_id,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;c&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;fee_rate&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;FROM&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;my&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;project&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;analytics&lt;/span&gt;&lt;span&gt;.publishers &lt;/span&gt;&lt;span&gt;AS&lt;/span&gt;&lt;span&gt; p&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;INNER JOIN&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;my&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;project&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;analytics&lt;/span&gt;&lt;span&gt;.contracts &lt;/span&gt;&lt;span&gt;AS&lt;/span&gt;&lt;span&gt; c&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ON&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;c&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;publisher_id&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;LIMIT&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1000&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h4 id=&quot;構文の解説&quot;&gt;構文の解説&lt;/h4&gt;&lt;/div&gt;





















&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th align=&quot;left&quot;&gt;要素&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;説明&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;code dir=&quot;auto&quot;&gt;AS p&lt;/code&gt;, &lt;code dir=&quot;auto&quot;&gt;AS c&lt;/code&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;テーブルにエイリアス（別名）を付けることで、クエリを読みやすくする&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;code dir=&quot;auto&quot;&gt;INNER JOIN&lt;/code&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;両方のテーブルに存在するレコードのみを取得&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;code dir=&quot;auto&quot;&gt;ON p.id = c.publisher_id&lt;/code&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;結合条件。publishers の id と contracts の publisher_id が一致するレコードを紐付ける&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;div&gt;&lt;h4 id=&quot;結果イメージ&quot;&gt;結果イメージ&lt;/h4&gt;&lt;/div&gt;


























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th align=&quot;left&quot;&gt;publisher_id&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;publisher_name&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;country&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;contract_id&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;fee_rate&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;1&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Publisher A&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Japan&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;101&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;0.15&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;2&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Publisher B&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;USA&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;102&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;0.20&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;この例では、契約情報（contracts）が存在する出版社のみが結果に含まれます。&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;結合方法の使い分け&quot;&gt;結合方法の使い分け&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;データの要件に応じて、異なる結合方法を選択できます。&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;left-join左側のテーブルを全て保持&quot;&gt;LEFT JOIN：左側のテーブルを全て保持&lt;/h3&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;SELECT&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AS&lt;/span&gt;&lt;span&gt; publisher_id,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AS&lt;/span&gt;&lt;span&gt; publisher_name,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;country&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;c&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AS&lt;/span&gt;&lt;span&gt; contract_id,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;c&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;fee_rate&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;FROM&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;my&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;project&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;analytics&lt;/span&gt;&lt;span&gt;.publishers &lt;/span&gt;&lt;span&gt;AS&lt;/span&gt;&lt;span&gt; p&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;LEFT JOIN&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;my&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;project&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;analytics&lt;/span&gt;&lt;span&gt;.contracts &lt;/span&gt;&lt;span&gt;AS&lt;/span&gt;&lt;span&gt; c&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ON&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;c&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;publisher_id&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;LIMIT&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1000&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;用途&lt;/strong&gt;: 「全ての出版社と、契約がある場合はその情報も表示したい」場合に使用。契約がない出版社も結果に含まれ、contract_id と fee_rate は NULL になります。&lt;/p&gt;
&lt;div&gt;&lt;h4 id=&quot;結果イメージ-1&quot;&gt;結果イメージ&lt;/h4&gt;&lt;/div&gt;

































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th align=&quot;left&quot;&gt;publisher_id&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;publisher_name&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;country&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;contract_id&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;fee_rate&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;1&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Publisher A&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Japan&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;101&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;0.15&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;2&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Publisher B&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;USA&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;102&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;0.20&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;3&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Publisher C&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;UK&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;NULL&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;NULL&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;div&gt;&lt;h3 id=&quot;right-join右側のテーブルを全て保持&quot;&gt;RIGHT JOIN：右側のテーブルを全て保持&lt;/h3&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;SELECT&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AS&lt;/span&gt;&lt;span&gt; publisher_id,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AS&lt;/span&gt;&lt;span&gt; publisher_name,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;country&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;c&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AS&lt;/span&gt;&lt;span&gt; contract_id,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;c&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;fee_rate&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;FROM&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;my&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;project&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;analytics&lt;/span&gt;&lt;span&gt;.publishers &lt;/span&gt;&lt;span&gt;AS&lt;/span&gt;&lt;span&gt; p&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;RIGHT JOIN&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;my&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;project&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;analytics&lt;/span&gt;&lt;span&gt;.contracts &lt;/span&gt;&lt;span&gt;AS&lt;/span&gt;&lt;span&gt; c&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ON&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;c&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;publisher_id&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;LIMIT&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1000&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;用途&lt;/strong&gt;: 「全ての契約と、対応する出版社情報を表示したい」場合に使用。出版社情報が見つからない契約も結果に含まれます（データ不整合の検出に便利）。&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;full-outer-join両方のテーブルを全て保持&quot;&gt;FULL OUTER JOIN：両方のテーブルを全て保持&lt;/h3&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;SELECT&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AS&lt;/span&gt;&lt;span&gt; publisher_id,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AS&lt;/span&gt;&lt;span&gt; publisher_name,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;country&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;c&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AS&lt;/span&gt;&lt;span&gt; contract_id,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;c&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;fee_rate&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;FROM&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;my&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;project&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;analytics&lt;/span&gt;&lt;span&gt;.publishers &lt;/span&gt;&lt;span&gt;AS&lt;/span&gt;&lt;span&gt; p&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;FULL OUTER JOIN&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;my&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;project&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;analytics&lt;/span&gt;&lt;span&gt;.contracts &lt;/span&gt;&lt;span&gt;AS&lt;/span&gt;&lt;span&gt; c&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ON&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;c&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;publisher_id&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;LIMIT&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1000&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;用途&lt;/strong&gt;: 「出版社と契約の全データを取得し、対応関係がないものも確認したい」場合に使用。マスタデータとトランザクションデータの整合性チェックに有効です。&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;実務でのtips&quot;&gt;実務でのTips&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;1-エイリアスは必ず使う&quot;&gt;1. エイリアスは必ず使う&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;テーブル名が長い場合、エイリアスを使わないとクエリが読みにくくなります。&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;-- ❌ 読みにくい&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;SELECT&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;my&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;project&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;analytics&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;publishers&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;my&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;project&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;analytics&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;contracts&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;fee_rate&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;FROM&lt;/span&gt;&lt;span&gt; my&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;project&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;analytics&lt;/span&gt;&lt;span&gt;.publishers&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;INNER JOIN&lt;/span&gt;&lt;span&gt; my&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;project&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;analytics&lt;/span&gt;&lt;span&gt;.contracts&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ON&lt;/span&gt;&lt;span&gt; my&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;project&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;analytics&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;publishers&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; my&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;project&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;analytics&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;contracts&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;publisher_id&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;-- ✅ 読みやすい&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;SELECT&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;c&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;fee_rate&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;FROM&lt;/span&gt;&lt;span&gt; my&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;project&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;analytics&lt;/span&gt;&lt;span&gt;.publishers &lt;/span&gt;&lt;span&gt;AS&lt;/span&gt;&lt;span&gt; p&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;INNER JOIN&lt;/span&gt;&lt;span&gt; my&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;project&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;analytics&lt;/span&gt;&lt;span&gt;.contracts &lt;/span&gt;&lt;span&gt;AS&lt;/span&gt;&lt;span&gt; c&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ON&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;c&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;publisher_id&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;2-結合キーにインデックスがあるか確認&quot;&gt;2. 結合キーにインデックスがあるか確認&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;BigQueryでは、結合キーに使用するカラムが適切にパーティション分割されているか、クラスタ化されているかで、クエリのパフォーマンスが大きく変わります。&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;3-null値の扱いに注意&quot;&gt;3. NULL値の扱いに注意&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;LEFT JOINやFULL OUTER JOINを使う場合、NULL値が発生します。集計関数（COUNT、SUMなど）を使う際は、NULL値の扱いに注意しましょう。&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;-- NULL を 0 として扱う例&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;SELECT&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;IFNULL&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;c&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;fee_rate&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;AS&lt;/span&gt;&lt;span&gt; fee_rate&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;FROM&lt;/span&gt;&lt;span&gt; my&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;project&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;analytics&lt;/span&gt;&lt;span&gt;.publishers &lt;/span&gt;&lt;span&gt;AS&lt;/span&gt;&lt;span&gt; p&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;LEFT JOIN&lt;/span&gt;&lt;span&gt; my&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;project&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;analytics&lt;/span&gt;&lt;span&gt;.contracts &lt;/span&gt;&lt;span&gt;AS&lt;/span&gt;&lt;span&gt; c&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ON&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;c&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;publisher_id&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h2 id=&quot;まとめ&quot;&gt;まとめ&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;BigQueryでのテーブル結合において、以下の点を押さえておきましょう。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;JOIN の種類を理解する&lt;/strong&gt;: INNER、LEFT、RIGHT、FULL OUTER の違いと使い分け&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;エイリアスを活用する&lt;/strong&gt;: クエリの可読性を高める&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;結合条件を明確にする&lt;/strong&gt;: ON句で正確な結合キーを指定&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;NULL値の扱いに注意&lt;/strong&gt;: 特にOUTER JOINを使う場合&lt;/li&gt;
&lt;/ol&gt;






























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th align=&quot;left&quot;&gt;結合タイプ&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;取得されるレコード&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;よくある用途&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;INNER JOIN&lt;/strong&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;両方のテーブルに存在するレコードのみ&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;確実に紐付くデータのみ取得&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;LEFT JOIN&lt;/strong&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;左側の全レコード + 右側の一致するレコード&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;マスタデータを全て表示&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;RIGHT JOIN&lt;/strong&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;右側の全レコード + 左側の一致するレコード&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;トランザクションデータを全て表示&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;FULL OUTER JOIN&lt;/strong&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;両方のテーブルの全レコード&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;データの整合性チェック&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;適切なJOINを選択することで、効率的で正確なデータ分析が可能になります。実務でのデータ要件に応じて、最適な結合方法を選びましょう。&lt;/p&gt;</content:encoded><category>BigQuery</category><category>SQL</category><category>Google Cloud</category><category>データ分析</category></item><item><title>Claude Codeで「UIがダサい」と伝えたら、驚くほどオシャレになった話</title><link>https://www.indigo165e83.com/blog/20260212-claude-code-ui-improvement/</link><guid isPermaLink="true">https://www.indigo165e83.com/blog/20260212-claude-code-ui-improvement/</guid><pubDate>Thu, 12 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;div&gt;&lt;h2 id=&quot;はじめに&quot;&gt;はじめに&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;バイブコーディング（Vibe Coding）&lt;/strong&gt; という言葉をご存知でしょうか。これは、具体的な技術仕様ではなく「雰囲気」や「感覚」といった抽象的な指示でコードを生成・修正するAI開発の新しいスタイルです。&lt;/p&gt;
&lt;p&gt;「AIに開発を任せる」ってどんな感じなのか興味があったのですが、Claude Codeを使って実際に体験したことは、想像を遥かに超えるものでした。&lt;/p&gt;
&lt;p&gt;今回は、簡単なホワイトボードアプリのUIを改善してもらった体験を通じて、&lt;strong&gt;たった一言の「バイブ」でどこまでできるのか&lt;/strong&gt;を検証してみました。&lt;/p&gt;

&lt;div&gt;&lt;h2 id=&quot;試したことバイブコーディングの実践&quot;&gt;試したこと：バイブコーディングの実践&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;私がClaude Codeに伝えたのは、たったこれだけです。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;「UIがダサいので、かっこよくしてください」&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;具体的なデザイン指示も、色の指定も、レイアウトの要望も何もありません。&lt;strong&gt;ただ「ダサい」から「かっこよく」してほしい、&lt;/strong&gt; それだけです。&lt;/p&gt;
&lt;p&gt;これこそが、まさに&lt;strong&gt;バイブコーディング&lt;/strong&gt;です。技術的な詳細ではなく、「こんな雰囲気にしたい」という感覚的な要求だけを伝える。従来の開発では考えられなかったアプローチです。&lt;/p&gt;
&lt;p&gt;正直、こんな曖昧な指示で何かが変わるとは思っていませんでした。せいぜい色が少し変わる程度だろう、と。&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;before機能はあるけど&quot;&gt;Before：機能はあるけど…&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;修正前のホワイトボードアプリは、こんな感じでした。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.indigo165e83.com/_astro/before-01.DWe5SXqM_Z1SN0F6.webp&quot; alt=&quot;修正前のホワイトボード（起動画面）&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;1247&quot; height=&quot;759&quot;&gt;
&lt;em&gt;修正前：シンプルだけど… あまりにもシンプルすぎる起動画面&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.indigo165e83.com/_astro/before-02.tQ-q6aE7_2lVy24.webp&quot; alt=&quot;修正前のホワイトボード（描画画面）&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;1234&quot; height=&quot;744&quot;&gt;
&lt;em&gt;修正前：グレー背景に黒ボタン。機能的だけど、確かに「ダサい」かも&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;機能としては問題ありません。描画もできるし、色も選べます。でも、デザインという観点では…確かに「ダサい」と言わざるを得ません。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;背景はただのグレー&lt;/li&gt;
&lt;li&gt;UIパーツは最小限&lt;/li&gt;
&lt;li&gt;「Welcome my site」というテキストが寂しげ&lt;/li&gt;
&lt;li&gt;全体的に無機質で冷たい印象&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;after驚きの変貌&quot;&gt;After：驚きの変貌&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;そして、Claude Codeが修正した結果がこちらです。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.indigo165e83.com/_astro/after-01.Bt2UWUu2_o9T28.webp&quot; alt=&quot;修正後のホワイトボード（起動画面）&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;1459&quot; height=&quot;841&quot;&gt;
&lt;em&gt;修正後：グラデーション背景にパープルのアクセントカラーが映える&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.indigo165e83.com/_astro/after-02.D09tgu1W_Z20Gej3.webp&quot; alt=&quot;修正後のホワイトボード（描画画面）&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;1459&quot; height=&quot;830&quot;&gt;
&lt;em&gt;修正後：ツールバーが洗練され、全体的にモダンな印象に&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;正直、驚きました。&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;背景が美しいグラデーションに&lt;/li&gt;
&lt;li&gt;アイコンがモダンなデザインに刷新&lt;/li&gt;
&lt;li&gt;パープル系の統一されたカラースキーム&lt;/li&gt;
&lt;li&gt;適切な余白とバランス&lt;/li&gt;
&lt;li&gt;「Draw freely」という洗練されたコピー&lt;/li&gt;
&lt;li&gt;ボタンに矢印アイコンが追加され、UIとしての誘導性が向上&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;何が起きたのかclaude-codeの思考プロセス&quot;&gt;何が起きたのか：Claude Codeの思考プロセス&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;たった一言「かっこよくして」という指示から、Claude Codeは以下のような判断をしたと考えられます。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;現状分析&lt;/strong&gt;：既存のUIを解析し、改善点を特定&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;デザイントレンド理解&lt;/strong&gt;：モダンなUIデザインの原則を適用&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;配色の選択&lt;/strong&gt;：パープル系という現代的で親しみやすい色を選定&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;視覚的階層の構築&lt;/strong&gt;：グラデーション、シャドウ、適切なコントラストで視認性向上&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ユーザー体験の最適化&lt;/strong&gt;：コピーの改善、アイコンの追加など細部にも配慮&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;これらすべてを、&lt;strong&gt;具体的な指示なしに&lt;/strong&gt;自律的に実行したのです。&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;なぜこれが凄いのか&quot;&gt;なぜこれが凄いのか&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;1-抽象的な要求の理解&quot;&gt;1. 抽象的な要求の理解&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;「ダサい」「かっこいい」という極めて主観的で曖昧な表現を、Claude Codeは適切に解釈しました。これは単なるキーワードマッチングではなく、&lt;strong&gt;デザインの良し悪しを理解している&lt;/strong&gt;証拠です。&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;2-総合的なuiセンス&quot;&gt;2. 総合的なUIセンス&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;色を変えるだけではなく、以下の要素を総合的に改善しています。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;タイポグラフィ&lt;/li&gt;
&lt;li&gt;レイアウト&lt;/li&gt;
&lt;li&gt;配色&lt;/li&gt;
&lt;li&gt;アイコンデザイン&lt;/li&gt;
&lt;li&gt;ユーザーガイダンス（コピーライティング）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;これは、&lt;strong&gt;デザインシステム全体&lt;/strong&gt;を理解していないとできません。&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;3-コンテキストの把握&quot;&gt;3. コンテキストの把握&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;「ホワイトボードアプリ」というコンテキストを理解し、そのツールに適したデザインを提案しています。例えば、クリエイティブなツールにふさわしい柔らかさと親しみやすさを表現しています。&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;バイブコーディングが開く新しい開発体験&quot;&gt;バイブコーディングが開く新しい開発体験&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;この体験を通じて感じたのは、&lt;strong&gt;AI開発ツールが「指示の実行者」から「デザインパートナー」へと進化している&lt;/strong&gt;ということです。&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;バイブコーディングの革新性&quot;&gt;バイブコーディングの革新性&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;従来の開発では、デザイナーが詳細なモックアップを作り、それを開発者が実装するという分業が一般的でした。しかしバイブコーディングでは、以下のような新しいワークフローが可能になります。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;感覚的な要望を伝える&lt;/strong&gt;：「ダサい」「かっこいい」「モダンに」など&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AIが解釈・提案する&lt;/strong&gt;：デザインの方向性を自律的に決定&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;即座に実装される&lt;/strong&gt;：コードレベルでの実現まで一気通貫&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;この「曖昧さを許容する開発」は、特に以下のような場面で威力を発揮するでしょう。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;プロトタイピング段階&lt;/strong&gt;：アイデアを素早く形にしたい時&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;個人開発&lt;/strong&gt;：デザインリソースが限られている時&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;UI改善の試行錯誤&lt;/strong&gt;：複数のデザイン案を短時間で比較したい時&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;非デザイナーの開発者&lt;/strong&gt;：デザインスキルがなくても良質なUIを実現したい時&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h3 id=&quot;開発の民主化&quot;&gt;開発の民主化&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;バイブコーディングは、開発における**「専門知識の壁」を下げる**重要な役割を果たします。CSSの詳細なプロパティを知らなくても、色彩理論を学んでいなくても、「こんな感じにしたい」という思いさえあれば、AIが形にしてくれるのです。&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;まとめバイブコーディングが示す開発の未来&quot;&gt;まとめ：バイブコーディングが示す開発の未来&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;「UIがダサいので、かっこよくしてください」&lt;/p&gt;
&lt;p&gt;このたった一言から、予想を遥かに超える改善が実現しました。&lt;/p&gt;
&lt;p&gt;Claude Codeとバイブコーディングは、開発における新しい可能性を示しています。それは単なる「効率化」ではなく、&lt;strong&gt;開発そのものの在り方を変える&lt;/strong&gt;イノベーションです。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;技術仕様書ではなく「雰囲気」で開発できる&lt;/li&gt;
&lt;li&gt;専門知識がなくても、思い描いたものを形にできる&lt;/li&gt;
&lt;li&gt;AIは手伝うのではなく、&lt;strong&gt;共に創り上げる&lt;/strong&gt;パートナーになる&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;もちろん、すべてのケースでこのように完璧な結果が得られるわけではないでしょう。しかし、少なくとも「AIには創造的な仕事は無理」という思い込みは、もう過去のものになりつつあるのかもしれません。&lt;/p&gt;
&lt;p&gt;バイブコーディングは、まだ始まったばかりの新しい開発手法です。あなたも、ぜひ一度試してみてください。きっと、新しい開発体験が待っているはずです。&lt;/p&gt;
&lt;p&gt;同時に、エンジニアの役割も変わっていくだろうとなと感じました。AIがコードを書き、デザインを提案する時代において、私たち人間は &lt;strong&gt;「ビジョンを伝える力」&lt;/strong&gt; や &lt;strong&gt;「AIと協働するスキル」&lt;/strong&gt; が求められるようになるでしょう。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;使用したツール&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.anthropic.com/en/docs/build-with-claude/claude-code&quot;&gt;Claude Code&lt;/a&gt; by Anthropic&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;プロジェクト&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://whiteboard.indigo165e83.com/&quot;&gt;ホワイトボードアプリ&lt;/a&gt;（個人開発のシンプルなWebアプリ）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;所要時間&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;指示から完成まで：数分&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>Claude Code</category><category>AI開発</category><category>UI改善</category><category>体験レポート</category><category>バイブコーディング</category></item><item><title>Next.js 16 アップデート：脆弱性 0 への道とビルドエラーの回避</title><link>https://www.indigo165e83.com/blog/20260212-nextjs16-fix-build-errors/</link><guid isPermaLink="true">https://www.indigo165e83.com/blog/20260212-nextjs16-fix-build-errors/</guid><pubDate>Thu, 12 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Next.js の公式チュートリアルをベースにしたダッシュボードアプリにおいて、セキュリティ脆弱性の解消をきっかけに Next.js 16 へのアップデートを敢行しました。&lt;/p&gt;
&lt;p&gt;最新の React 19 や Next.js 16 では、これまでの書き方が通用しない「破壊的変更」や、ビルド時の挙動の変化がいくつかあります。フリーランスエンジニアとして、実務でも遭遇しそうなこれらのトラブルをどう解決したか、その全記録を共有します。&lt;/p&gt;

&lt;div&gt;&lt;h2 id=&quot;1-脆弱性-0-件へパッケージの強制アップデート&quot;&gt;1. 脆弱性 0 件へ：パッケージの強制アップデート&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;まず着手したのは、&lt;code dir=&quot;auto&quot;&gt;pnpm audit&lt;/code&gt; で指摘された 16 件の脆弱性対応です。&lt;/p&gt;
&lt;p&gt;古いチュートリアルコードでは依存ライブラリが固定されていることが多く、単純な &lt;code dir=&quot;auto&quot;&gt;pnpm update&lt;/code&gt; では解消しきれない場合があります。今回は &lt;code dir=&quot;auto&quot;&gt;package.json&lt;/code&gt; の &lt;code dir=&quot;auto&quot;&gt;pnpm.overrides&lt;/code&gt; を活用し、安全なバージョンを強制的に指定することで、脆弱性を 0 件まで抑え込みました。&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;2-react-19-の洗礼server-actions-の型定義&quot;&gt;2. React 19 の洗礼：Server Actions の型定義&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Next.js 16（React 19）に上げた直後、TypeScript のビルドエラーが発生しました。&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;発生したエラー&quot;&gt;発生したエラー&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;&lt;code dir=&quot;auto&quot;&gt;./app/ui/invoices/edit-form.tsx&lt;/code&gt; などの &lt;code dir=&quot;auto&quot;&gt;form action&lt;/code&gt; 属性において、以下の型不一致が起きました。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Type &lt;code dir=&quot;auto&quot;&gt;Promise&amp;#x3C;{ message: string; }&gt;&lt;/code&gt; is not assignable to type &lt;code dir=&quot;auto&quot;&gt;void | Promise&amp;#x3C;void&gt;&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div&gt;&lt;h3 id=&quot;原因と対策&quot;&gt;原因と対策&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;React 19 から、&lt;code dir=&quot;auto&quot;&gt;form action&lt;/code&gt; に渡す関数は戻り値が &lt;code dir=&quot;auto&quot;&gt;void&lt;/code&gt; または &lt;code dir=&quot;auto&quot;&gt;Promise&amp;#x3C;void&gt;&lt;/code&gt; であることが厳格に求められるようになりました。従来のコードでオブジェクト（メッセージなど）を返している場合、エラーになります。&lt;/p&gt;
&lt;p&gt;これを解決するために、以下のようにラッパー関数で囲む修正を行いました。&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// 修正前&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;form&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;action&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;updateInvoiceWithId&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// 修正後（ラッパーで囲んで戻り値を無視させる）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;form&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;action&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;formData&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;updateInvoiceWithId&lt;/span&gt;&lt;span&gt;(formData)&lt;/span&gt;&lt;span&gt;; }&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h2 id=&quot;3-ビルドの壁neon-db-接続エラーと-ppr&quot;&gt;3. ビルドの壁：Neon DB 接続エラーと PPR&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;一番苦労したのが、&lt;code dir=&quot;auto&quot;&gt;pnpm run build&lt;/code&gt; 時のデータベース接続エラーです。&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;症状&quot;&gt;症状&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;ビルド中に Neon DB へ接続しようとして &lt;code dir=&quot;auto&quot;&gt;HANGING_PROMISE_REJECTION&lt;/code&gt; が発生。WSL2（GALLERIA）のローカル環境から外部 DB への接続が、プリレンダリング工程でタイムアウトしてしまうのが原因でした。&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;解決策force-dynamic-の活用&quot;&gt;解決策：force-dynamic の活用&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;Next.js 16 の &lt;code dir=&quot;auto&quot;&gt;cacheComponents (Partial Prerendering)&lt;/code&gt; を有効にしていると、ビルド時に DB を叩きに行こうとします。今回はビルドを確実に成功させるため、一旦 &lt;code dir=&quot;auto&quot;&gt;app/layout.tsx&lt;/code&gt; に以下の設定を投入しました。&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export const dynamic = &apos;force-dynamic&apos;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;これにより、「ビルド時に静的にページを作る」のをやめ、「リクエスト時に動的に生成する」挙動に強制変更。ビルド時のネットワーク起因によるエラーを完全に回避できました。&lt;/p&gt;
&lt;p&gt;※注意：&lt;code dir=&quot;auto&quot;&gt;cacheComponents&lt;/code&gt; が有効だと &lt;code dir=&quot;auto&quot;&gt;dynamic = &apos;force-dynamic&apos;&lt;/code&gt; はエラーになるため、ビルドを優先する場合は &lt;code dir=&quot;auto&quot;&gt;next.config.mjs&lt;/code&gt; で一旦 PPR をオフにする調整も必要です。&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;まとめ&quot;&gt;まとめ&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;今回のアップデートを通じて、以下の 3 点が重要だと再認識しました。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;脆弱性対応は定期的に: &lt;code dir=&quot;auto&quot;&gt;overrides&lt;/code&gt; を駆使してでも環境を清潔に保つ。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;型定義の変化に敏感に: React 19 の &lt;code dir=&quot;auto&quot;&gt;form action&lt;/code&gt; などの仕様変更を把握する。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;環境差分を考慮する: ローカル（WSL2）とデプロイ先（Vercel）でビルド時の挙動が異なる場合、&lt;code dir=&quot;auto&quot;&gt;force-dynamic&lt;/code&gt; などのフラグを戦略的に使う。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;次は、Vercel 上で本来のパフォーマンスを発揮させるための「PPR 戻し」に挑戦する予定です。&lt;/p&gt;</content:encoded><category>Next.js</category><category>TypeScript</category><category>React19</category><category>WSL2</category><category>Web開発</category></item><item><title>個人開発アプリにStripeで寄付機能を導入する方法と注意点</title><link>https://www.indigo165e83.com/blog/20260210-stripe-introduction-for-beginners/</link><guid isPermaLink="true">https://www.indigo165e83.com/blog/20260210-stripe-introduction-for-beginners/</guid><pubDate>Tue, 10 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;現在開発中のフリー素材サイト「Free Images」に、「寄付（Donate）」機能を導入することにしました。&lt;/p&gt;
&lt;p&gt;「決済機能なんて実装が大変そう…」と身構えていたのですが、&lt;strong&gt;Stripe&lt;/strong&gt; を使えばコードをほとんど書かずに導入できることが判明。&lt;/p&gt;
&lt;p&gt;しかし、いざ設定を始めると &lt;strong&gt;「管理画面のどこで名前を変えるの？」「複数のアプリはどう管理する？」&lt;/strong&gt; といった、コード以外の部分で意外と躓きました。&lt;/p&gt;
&lt;p&gt;今回は、初めてStripeを触るエンジニアに向けて、最短で安全に寄付機能をリリースするための手順と、私がハマった落とし穴をシェアします。&lt;/p&gt;

&lt;div&gt;&lt;h2 id=&quot;1-実装手段最初はpayment-links一択&quot;&gt;1. 実装手段：最初は「Payment Links」一択&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Next.jsなどのフレームワークを使っていると、つい「APIを叩いて…」と考えがちですが、単発の寄付やシンプルな決済なら、Stripeが提供する &lt;strong&gt;「Payment Links（支払いリンク）」&lt;/strong&gt; 機能を使うのが最も手軽です。&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;なぜpayment-linksなのか&quot;&gt;なぜPayment Linksなのか？&lt;/h3&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;実装工数がほぼゼロ:&lt;/strong&gt; 管理画面でリンクを作るだけ。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;セキュリティ:&lt;/strong&gt; カード情報入力はStripeのドメイン上で行われるため、自サイトで機密情報を扱うリスクがない。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ノーコード:&lt;/strong&gt; サイトのフッターなどに &lt;code dir=&quot;auto&quot;&gt;&amp;#x3C;a&gt;&lt;/code&gt; タグでリンクを貼るだけで完了。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;今回は、まずこの機能を使って「寄付ページ」のURLを発行しました。&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;2-最大の罠名前が変わらない問題&quot;&gt;2. 【最大の罠】名前が変わらない問題&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;意気揚々とリンクを作成し、プレビュー画面を見たときに違和感を覚えました。
&lt;strong&gt;「左上の店名が、変えたい名前に変わっていない…」&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Stripeのダッシュボードには「名前」を設定する場所が複数あり、それぞれ役割が全く異なります。ここを混同すると、「自分には正しい名前が見えているのに、お客様には変な名前が表示される」という事故が起きます。&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;混乱しやすい2つの名前&quot;&gt;混乱しやすい2つの名前&lt;/h3&gt;&lt;/div&gt;























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th align=&quot;left&quot;&gt;項目名&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;役割&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;誰に見える？&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;設定場所の探し方&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;アカウント名&lt;/strong&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;管理用ラベル&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;自分だけ&lt;/strong&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;設定 &gt; アカウントの詳細&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;公開ビジネス名称&lt;/strong&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;正式名称&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;お客様&lt;/strong&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;設定 &gt; 公開情報&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;私は最初、「アカウント名」だけを一生懸命変更して「プレビューが変わらない！」と悩んでいました。
&lt;strong&gt;お客様に見える名前（決済ページや領収書メール）を変えたい場合は、「公開ビジネス名称」を変更する必要があります。&lt;/strong&gt;&lt;/p&gt;
&lt;aside aria-label=&quot;迷子にならないための検索技&quot;&gt;&lt;p aria-hidden=&quot;true&quot;&gt;迷子にならないための検索技&lt;/p&gt;&lt;div&gt;&lt;p&gt;Stripeの管理画面は項目が多いため、メニューから探すのは大変です。
画面上部の検索バー（ショートカット：&lt;code dir=&quot;auto&quot;&gt;Cmd + K&lt;/code&gt;）に**「公開情報」**と入力して直接移動するのが一番の近道です。&lt;/p&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;div&gt;&lt;h2 id=&quot;3-複数サービス運用時のアカウント戦略&quot;&gt;3. 複数サービス運用時の「アカウント」戦略&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;私は「Free Images」以外にも、「4Testing（ツール系）」や「Indigo Works（ポートフォリオ）」など、性質の異なる複数のサイトを持っています。&lt;/p&gt;
&lt;p&gt;ふと、「これら全ての決済を1つのStripeアカウントで管理していいのか？」という疑問が湧きました。
結論から言うと、&lt;strong&gt;サービスごとにStripeアカウント（プロファイル）を分けるのが鉄則&lt;/strong&gt;です。&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;理由クレジットカードの利用明細重要&quot;&gt;理由：クレジットカードの利用明細（重要！）&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;もし全サービスを「Indigo Works」という1つのアカウントでまとめてしまうと、お客様のカード明細には常に「INDIGO WORKS」と記載されます。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;「画像サイト(Free Images)」で寄付した人 → 明細：&lt;code dir=&quot;auto&quot;&gt;INDIGO WORKS&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ユーザーの心理:&lt;/strong&gt; 「えっ、INDIGO WORKSって何？不正利用かも？」&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;これでは、覚えのない請求として**チャージバック（返金要求）**されるリスクが高まります。&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;解決策アカウントの新規作成を活用する&quot;&gt;解決策：アカウントの「新規作成」を活用する&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;Stripeは、1つのメールアドレス（ログインID）で、**複数のアカウント（プロファイル）**を簡単に作成・切り替えできます。追加料金もかかりません。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;【おすすめの運用構成】&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;ダッシュボード左上の名前をクリック&lt;/li&gt;
&lt;li&gt;「＋ 新規アカウント」を選択&lt;/li&gt;
&lt;li&gt;サービス名（例：Free Images）を入力して作成&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;これだけで、**「Free Images専用の環境」**が手に入ります。
あとは、それぞれの「公開ビジネス名称（明細書表記）」を正しく設定すれば完璧です。&lt;/p&gt;




















&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th align=&quot;left&quot;&gt;アカウント名（管理用）&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;公開ビジネス名称（明細表記）&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;対象サービス&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;Free Images&lt;/strong&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;FREE IMAGES&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;画像素材サイト&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;4Testing&lt;/strong&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;4TESTING&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;テストツール&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;div&gt;&lt;h2 id=&quot;まとめ&quot;&gt;まとめ&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Stripeの導入は技術的には簡単ですが、管理画面の設定には少しコツが必要です。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;実装:&lt;/strong&gt; まずは「Payment Links」で小さく始める。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;設定:&lt;/strong&gt; お客様に見せたい名前は「公開情報」で設定する。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;運用:&lt;/strong&gt; サービスごとに「新規アカウント」を作成し、明細表記を分ける。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;これで、ユーザーにとっても怪しくない、クリーンな寄付・決済環境が整いました。
個人開発アプリの収益化を考えている方は、ぜひ「アカウントの分け方」から設計してみてください。&lt;/p&gt;</content:encoded><category>Stripe</category><category>Next.js</category><category>個人開発</category><category>寄付</category></item><item><title>JSTQB Foundation Level シラバスの概要とマインドマップ化</title><link>https://www.indigo165e83.com/blog/20260208-jstqb-foundation-level-syllabus-mindmap/</link><guid isPermaLink="true">https://www.indigo165e83.com/blog/20260208-jstqb-foundation-level-syllabus-mindmap/</guid><pubDate>Sun, 08 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;最近、初心に帰って、&lt;strong&gt;JSTQB Foundation Level&lt;/strong&gt; の資格を取得しようと思っています。&lt;/p&gt;
&lt;p&gt;とりあえず、 シラバス（V4.0）を読み込んで、マインドマップ化しました。&lt;/p&gt;
&lt;p&gt;JSTQB Foundation Level (Syllabus V4.0)：
&lt;a href=&quot;https://jstqb.jp/syllabus.html#syllabus_corefoundation&quot;&gt;https://jstqb.jp/syllabus.html#syllabus_corefoundation&lt;/a&gt;&lt;/p&gt;

&lt;div&gt;&lt;h2 id=&quot;jstqb-foundation-level-syllabus-v40-概要&quot;&gt;JSTQB Foundation Level (Syllabus V4.0) 概要&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;1-テストの基礎&quot;&gt;1. テストの基礎&lt;/h3&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;テストとは何か？&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;典型的なテスト目的&lt;/li&gt;
&lt;li&gt;テストとデバッグの違い&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;なぜテストが必要か？&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;品質保証 (QA) と品質管理 (QC)&lt;/li&gt;
&lt;li&gt;エラー・欠陥・故障・根本原因&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;テストの7原則&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;テストプロセスと役割&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;テスト活動とタスク&lt;/li&gt;
&lt;li&gt;テストウェア&lt;/li&gt;
&lt;li&gt;トレーサビリティ&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;必要不可欠なスキルとよい実践例&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;チーム全体アプローチ&lt;/li&gt;
&lt;li&gt;テストの独立性&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h3 id=&quot;2-sdlc全体を通してのテスト&quot;&gt;2. SDLC全体を通してのテスト&lt;/h3&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;開発モデルの影響&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;DevOpsとテスト&lt;/li&gt;
&lt;li&gt;シフトレフトアプローチ&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;テストレベル&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;コンポーネントテスト&lt;/li&gt;
&lt;li&gt;統合テスト (コンポーネント統合 / システム統合)&lt;/li&gt;
&lt;li&gt;システムテスト&lt;/li&gt;
&lt;li&gt;受け入れテスト&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;テストタイプ&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;機能テスト&lt;/li&gt;
&lt;li&gt;非機能テスト&lt;/li&gt;
&lt;li&gt;ホワイトボックステスト&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;メンテナンステスト&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;確認テストとリグレッションテスト&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h3 id=&quot;3-静的テスト&quot;&gt;3. 静的テスト&lt;/h3&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;静的テストの基本&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;静的テストと動的テストの違い&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;レビュープロセス&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;レビュー種別 (非形式的 / ウォークスルー / テクニカルレビュー / インスペクション)&lt;/li&gt;
&lt;li&gt;レビューの成功要因&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h3 id=&quot;4-テスト分析と設計&quot;&gt;4. テスト分析と設計&lt;/h3&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;ブラックボックステスト技法&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;同値分割法&lt;/li&gt;
&lt;li&gt;境界値分析 (2値 / 3値)&lt;/li&gt;
&lt;li&gt;デシジョンテーブルテスト&lt;/li&gt;
&lt;li&gt;状態遷移テスト&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ホワイトボックステスト技法&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;ステートメントテスト&lt;/li&gt;
&lt;li&gt;ブランチテスト&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;経験ベースのテスト技法&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;エラー推測&lt;/li&gt;
&lt;li&gt;探索的テスト&lt;/li&gt;
&lt;li&gt;チェックリストベースドテスト&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;コラボレーションベースのアプローチ&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;ATDD (受け入れテスト駆動開発)&lt;/li&gt;
&lt;li&gt;ユーザーストーリーと受け入れ基準&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h3 id=&quot;5-テストマネジメント&quot;&gt;5. テストマネジメント&lt;/h3&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;計画と見積り&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;開始基準と終了基準&lt;/li&gt;
&lt;li&gt;見積り技法 (3点見積り / プランニングポーカー等)&lt;/li&gt;
&lt;li&gt;テストピラミッド&lt;/li&gt;
&lt;li&gt;テストの四象限&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;リスクマネジメント&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;プロジェクトリスクとプロダクトリスク&lt;/li&gt;
&lt;li&gt;リスク分析とコントロール&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;モニタリングとコントロール&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;構成管理と欠陥マネジメント&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h3 id=&quot;6-テストツール&quot;&gt;6. テストツール&lt;/h3&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;ツールの分類と支援&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;テスト自動化の利点とリスク&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;マインドマップ&quot;&gt;マインドマップ&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;mindmap&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;root((JSTQB FL&amp;#x3C;br/&gt;Syllabus V4.0))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;1. テストの基礎&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;テストとは何か&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;テストとデバッグの違い&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;なぜテストが必要か&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;QAとQC&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;エラー・欠陥・故障&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;テストの7原則&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;プロセスと役割&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;テストウェア&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;トレーサビリティ&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;スキルと独立性&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;チーム全体アプローチ&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;2. SDLCにおけるテスト&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;開発モデルの影響&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;DevOps&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;シフトレフト&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;テストレベル&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;コンポーネントテスト&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;統合テスト&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;システムテスト&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;受け入れテスト&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;テストタイプ&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;機能 / 非機能&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;ホワイトボックス&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;メンテナンステスト&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;確認 / リグレッション&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;3. 静的テスト&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;静的テストの基本&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;動的テストとの違い&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;レビュープロセス&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;レビュー種別&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;成功要因&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;4. テスト分析と設計&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;ブラックボックス技法&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;同値分割法&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;境界値分析&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;デシジョンテーブル&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;状態遷移テスト&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;ホワイトボックス技法&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;ステートメント&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;ブランチ&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;経験ベース技法&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;エラー推測&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;探索的テスト&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;コラボレーション&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;ATDD&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;ユーザーストーリー&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;5. テストマネジメント&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;計画と見積り&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;開始・終了基準&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;テストピラミッド&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;テストの四象限&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;リスクマネジメント&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;プロジェクトリスク&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;プロダクトリスク&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;モニタリングとコントロール&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;構成管理・欠陥管理&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;6. テストツール&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;ツールの分類&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;自動化の利益とリスク&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;</content:encoded><category>JSTQB</category><category>JSTQB FL</category><category>シラバス</category><category>マインドマップ</category></item><item><title>Next.js認証の深堀り：JWTという『透明な証明書』をどう扱うか？</title><link>https://www.indigo165e83.com/blog/20260207-nextjs-auth-jwt-deep-dive/</link><guid isPermaLink="true">https://www.indigo165e83.com/blog/20260207-nextjs-auth-jwt-deep-dive/</guid><pubDate>Sat, 07 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Next.js（App Router）での認証実装。ライブラリが優秀すぎて「なぜか動く」状態になりがちですが、一歩踏み込んで &lt;strong&gt;JWT（JSON Web Token ジョット）&lt;/strong&gt; の仕組みを理解すると、デバッグの質が劇的に変わります。&lt;/p&gt;
&lt;p&gt;今回は、Auth.js（NextAuth）を例に、JWTの正体と現場で使えるTipsをまとめました。&lt;/p&gt;

&lt;div&gt;&lt;h2 id=&quot;認証はパスポートと搭乗券のリレー&quot;&gt;認証は「パスポート」と「搭乗券」のリレー&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Webサイトの認証フローは、空港のプロセスに例えると非常にクリアになります。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;ログイン（認証）&lt;/strong&gt;: ID/PWを提示して本人確認をする（パスポート提示）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;トークン発行&lt;/strong&gt;: サーバーが「この人は本人です」という証明書を発行する（搭乗券の発行）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;アクセス（認可）&lt;/strong&gt;: 以降、ブラウザがその証明書を自動で提示する。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;この「搭乗券」の役割を果たすのが &lt;strong&gt;JWT&lt;/strong&gt; です。&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;jwtjson-web-tokenとは&quot;&gt;JWT（JSON Web Token）とは？&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;現在、Next.jsなどのモダンな開発で最も使われているトークンの形式が &lt;strong&gt;JWT&lt;/strong&gt; です。&lt;/p&gt;
&lt;p&gt;最大の特徴は、 &lt;strong&gt;「サーバーがいちいちデータベースを見に行かなくても、その中身を見ただけで正当性がわかる」&lt;/strong&gt; という点にあります。&lt;/p&gt;
&lt;p&gt;JWTを分解すると、ドット（.）で区切られた3つのパーツでできています。&lt;/p&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th align=&quot;left&quot;&gt;パーツ名&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;例え&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;内容&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;ヘッダー&lt;/strong&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;封筒の種類&lt;/strong&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;「これはJWTです」「暗号化の種類はこれです」という情報。&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;ペイロード&lt;/strong&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;証明書の本文&lt;/strong&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;「ユーザーID：123」「有効期限：2026/02/07」などの実データ。&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;署名 (Signature)&lt;/strong&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;偽造防止のハンコ&lt;/strong&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;サーバーだけが知る「秘密の鍵」で押された電子印鑑。&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;追記：
&lt;a href=&quot;https://www.jwt.io/ja/&quot;&gt;JSON Web Token（JWT）デバッガー&lt;/a&gt; で実際のJWTを貼り付けると、各パーツの中身を簡単に確認できます。&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;jwtは透明なガラスケース&quot;&gt;JWTは「透明なガラスケース」&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;JWTについて最も誤解されやすいのは、 &lt;strong&gt;「中身は暗号化されていない」&lt;/strong&gt; という点です。&lt;/p&gt;
&lt;p&gt;JWTはドット（&lt;code dir=&quot;auto&quot;&gt;.&lt;/code&gt;）で区切られた3つのパートで構成されていますが、本文（ペイロード）は単にBase64で変換されているだけ。誰でも中身を読めてしまいます。&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;-ng例機密情報を入れてしまう&quot;&gt;❌ NG例：機密情報を入れてしまう&lt;/h3&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;userId&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;123&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;password&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;hashed_password_abc&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;// 絶対にダメ！&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;address&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;東京都...&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;// 個人情報も避けるべき&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;-ok例識別子と期限に留める&quot;&gt;✅ OK例：識別子と期限に留める&lt;/h3&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;sub&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;user_8k92jL0s&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;// ユーザーID&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;role&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;ADMIN&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,       &lt;/span&gt;&lt;span&gt;// 権限&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;exp&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;1709859015&lt;/span&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;// 有効期限&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;「中身は見えてもいいが、署名（ハンコ）があるから書き換えはできない」。これがJWTの鉄則です。&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;現場で使えるデバッグとテストの実践&quot;&gt;現場で使える「デバッグとテスト」の実践&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;1-ターミナルでjwtを覗き見る&quot;&gt;1. ターミナルでJWTを覗き見る&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;Auth.jsのデフォルトではCookieが暗号化されているため、ブラウザのデベロッパーツールから直接中身を読むのは困難です。&lt;/p&gt;
&lt;p&gt;そんな時は auth.ts の jwt コールバックにログを仕込むのが最速です。&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;auth.ts&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;callbacks: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;async &lt;/span&gt;&lt;span&gt;jwt&lt;/span&gt;&lt;span&gt;({ token, user }) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (user) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;token&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;role&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; user&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;role&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;console&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;DEBUG [JWT]:&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, token); &lt;/span&gt;&lt;span&gt;// サーバー側のログで規約通りの sub や exp を確認&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; token;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;2-安全なテストログインの実装&quot;&gt;2. 安全なテストログインの実装&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;開発中、いちいちGoogleログインをするのは手間ですよね。かといってコードにパスワードを直書きするのは論外です。&lt;/p&gt;
&lt;p&gt;以下のように環境変数と NODE_ENV を組み合わせるのが「エンジニアの作法」です。&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Credentials&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;authorize&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;credentials&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (process&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;env&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;NODE_ENV&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;production&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;; &lt;/span&gt;&lt;span&gt;// 本番では絶対動作させない&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;credentials&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;email&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; process&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;env&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;TEST_ADMIN_EMAIL&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;#x26;&amp;#x26;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;credentials&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;password&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; process&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;env&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;TEST_ADMIN_PASSWORD&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; { id: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;test-id&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, role: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;ADMIN&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; };&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;})&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h2 id=&quot;補足規約を知る&quot;&gt;補足：規約を知る&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;JWTには &lt;a href=&quot;https://tex2e.github.io/rfc-translater/html/rfc7519.html&quot;&gt;RFC 7519&lt;/a&gt; という国際規格があります。&lt;/p&gt;
&lt;p&gt;特に、JWTの中身（ペイロード）に入れる項目には、規約で決められた &lt;strong&gt;「予約済みクレーム（Reserved Claims）」&lt;/strong&gt; という特別な名前があります。&lt;/p&gt;
&lt;p&gt;規約では、項目の名前を3文字に省略することが推奨されています。これは、データ量を少しでも小さくして通信速度を上げるためです。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;よく使われる予約済みクレーム&lt;/strong&gt;&lt;/p&gt;








































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th align=&quot;left&quot;&gt;項目名&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;正称&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;意味&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;&lt;code dir=&quot;auto&quot;&gt;iss&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Issuer&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;発行者&lt;/strong&gt;（誰がこのトークンを作ったか。例：Googleなど）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;&lt;code dir=&quot;auto&quot;&gt;sub&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Subject&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;一意識別子&lt;/strong&gt;（誰のためのトークンか。通常はユーザーID）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;&lt;code dir=&quot;auto&quot;&gt;aud&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Audience&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;対象者&lt;/strong&gt;（誰がこのトークンを使う予定か。例：自分のアプリのURL）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;&lt;code dir=&quot;auto&quot;&gt;exp&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Expiration Time&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;有効期限&lt;/strong&gt;（これ以降は無効。UNIXタイムスタンプで記述）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;&lt;code dir=&quot;auto&quot;&gt;iat&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Issued At&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;発行時刻&lt;/strong&gt;（いつ作られたか）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;&lt;code dir=&quot;auto&quot;&gt;jti&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;JWT ID&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;トークン独自のID&lt;/strong&gt;（再利用攻撃を防ぐためのシリアル番号）&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;なぜユーザーIDが id ではなく sub なのか、なぜ期限が exp なのか。これらの「3文字の規約」を知っておくと、Auth.jsから別のライブラリ（Better Authなど）へ移行した際や、Supabase等のBaaSを使う際も、共通の知識として活用できます。&lt;/p&gt;
&lt;p&gt;「なんとなく動く認証」から「仕組みを理解した堅牢な認証」へ。
皆さんのNext.js開発の一助になれば幸いです。&lt;/p&gt;</content:encoded><category>Next.js</category><category>Auth.js</category><category>セキュリティ</category><category>JWT</category></item><item><title>Playwright × NextAuth v5：ログインのテストを自動化する方法</title><link>https://www.indigo165e83.com/blog/20260206-playwright-auth-complete/</link><guid isPermaLink="true">https://www.indigo165e83.com/blog/20260206-playwright-auth-complete/</guid><pubDate>Fri, 06 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Next.js + NextAuth (v5) で構築された、AI画像ストックサイト「&lt;a href=&quot;https://free-images.indigo165e83.com&quot;&gt;free-images&lt;/a&gt;」というサービスに E2E テスト（Playwright）を導入しました。&lt;/p&gt;
&lt;p&gt;初期段階では「Googleログインしかないから、テストが難しいな」くらいに考えていたのですが、実際は&lt;strong&gt;認証基盤のアーキテクチャ変更&lt;/strong&gt;にまで発展する大工事となりました。&lt;/p&gt;
&lt;p&gt;この記事では、直面したエラーとその解決策、そして最終的に完成した「テスト用裏口実装」のコードを共有します。&lt;/p&gt;

&lt;div&gt;&lt;h2 id=&quot;直面した2つの壁&quot;&gt;直面した2つの壁&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;1-google認証の壁&quot;&gt;1. Google認証の壁&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;Playwright で Google ログイン画面を操作しようとすると、Bot検知や二要素認証（2FA）に阻まれます。
CI/CD で安定して回すためには、外部プロバイダ（Google）に依存しないログイン方法が必要でした。&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;2-nextauth-のdbセッションの制約&quot;&gt;2. NextAuth の「DBセッション」の制約&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;「じゃあ、テスト用にメール/パスワード認証（Credentials）を追加しよう」と考えましたが、NextAuth には以下の制約がありました。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;“Credentials 認証を使うなら、DBセッション（Adapter）は使えない。JWT を使いなさい。”&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;これにより、既存のデータベースセッション方式を捨て、JWT（JSON Web Token）方式へ移行する決断を迫られました。&lt;/p&gt;
&lt;hr&gt;
&lt;div&gt;&lt;h2 id=&quot;解決策テスト用裏口の実装&quot;&gt;解決策：テスト用「裏口」の実装&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;最終的に実装した &lt;code dir=&quot;auto&quot;&gt;auth.ts&lt;/code&gt; がこちらです。
ポイントは、&lt;strong&gt;本番環境以外（開発・テスト）でのみ有効になる Credentials プロバイダー&lt;/strong&gt; を追加したことです。&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// auth.ts (完成版)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; NextAuth &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;next-auth&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; Google &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;next-auth/providers/google&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; Credentials &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;next-auth/providers/credentials&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { PrismaAdapter } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;@auth/prisma-adapter&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { prisma } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;@/lib/prisma&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export const { &lt;/span&gt;&lt;span&gt;handlers&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;auth&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;signIn&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;signOut&lt;/span&gt;&lt;span&gt; } = &lt;/span&gt;&lt;span&gt;NextAuth&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;adapter: &lt;/span&gt;&lt;span&gt;PrismaAdapter&lt;/span&gt;&lt;span&gt;(prisma)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;// Credentialsを使うために必須の設定&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;session: { strategy: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;jwt&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;providers:&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;Google&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;clientId: process&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;env&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;AUTH_GOOGLE_ID&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;clientSecret: process&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;env&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;AUTH_GOOGLE_SECRET&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;// ▼ テスト用裏口プロバイダー&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;Credentials&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;id: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;test-login&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;name: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Test Login&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;credentials: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;email: { label: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Email&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, type: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;email&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;password: { label: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Password&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, type: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;password&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;authorize&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;credentials&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;// 【重要】本番環境(production)でのみ無効化する&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;// ※ ここを &quot;if (NODE_ENV !== &apos;test&apos;)&quot; にすると、&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;// npm run dev (development) で動かした時にログインできずハマります！&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (process&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;env&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;NODE_ENV&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;production&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;// 特定のアカウントのみ管理者として許可&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;credentials&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;email&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;admin@example.com&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;#x26;&amp;#x26;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;credentials&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;password&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;test-password&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;id: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;test-admin-id&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;role: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;ADMIN&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;// ここで権限付与&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;email: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;admin@example.com&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;// ... callbacks省略&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h2 id=&quot;ハマりポイントnode_env-の落とし穴&quot;&gt;ハマりポイント：NODE_ENV の落とし穴&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;実装中に一番時間を溶かしたのが、環境変数の扱いです。&lt;/p&gt;
&lt;p&gt;当初、ガード条件を以下のように書いていました。&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// ❌ 失敗したコード&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (process&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;env&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;NODE_ENV&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;!==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;test&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Playwright は NODE_ENV=test で実行されるのでこれで良さそうに見えます。
しかし、Next.js のサーバー自体 は &lt;code dir=&quot;auto&quot;&gt;pnpm dev&lt;/code&gt; で起動しており、この時の環境変数は &lt;code dir=&quot;auto&quot;&gt;development&lt;/code&gt; です。&lt;/p&gt;
&lt;p&gt;結果、&lt;strong&gt;テストランナー(test)はログインしようとするが、サーバー(development)がそれを拒否する&lt;/strong&gt; という状況になり、永遠に Sign in failed エラーが出続けました。&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;教訓&quot;&gt;教訓&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;ガード条件は &lt;strong&gt;「テスト環境のみ許可」&lt;/strong&gt; ではなく、 &lt;strong&gt;「本番環境のみ拒否 (&lt;code dir=&quot;auto&quot;&gt;process.env.NODE_ENV === &quot;production&quot;&lt;/code&gt;)」&lt;/strong&gt; とするのが安全かつ確実です。&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;まとめ&quot;&gt;まとめ&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;今回の対応で、以下の成果が得られました。&lt;/p&gt;
&lt;p&gt;1.Google依存の排除: 外部要因でテストが落ちることがなくなった。&lt;/p&gt;
&lt;p&gt;2.高速化: ログイン処理が一瞬で終わるようになった。&lt;/p&gt;
&lt;p&gt;3.JWTへの移行: DBアクセスが減り、本番のパフォーマンスも向上した（副次的効果）。&lt;/p&gt;
&lt;p&gt;「テストのために本番コードを弄るのは負け」という考え方もありますが、「テスト容易性（Testability）の高いアーキテクチャを選ぶ」 ことも、必要だと実感しました。&lt;/p&gt;</content:encoded><category>Playwright</category><category>NextAuth</category><category>Testing</category><category>Troubleshooting</category><category>login</category></item><item><title>Vercel × Route53 × Astro で「wwwあり」を正解にする理由</title><link>https://www.indigo165e83.com/blog/20260206-why-use-www-on-vercel/</link><guid isPermaLink="true">https://www.indigo165e83.com/blog/20260206-why-use-www-on-vercel/</guid><pubDate>Fri, 06 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;「ドメインは短い方がクールだ」&lt;/p&gt;
&lt;p&gt;そう思って、個人開発のサイトではこれまで &lt;code dir=&quot;auto&quot;&gt;https://example.com&lt;/code&gt; のような「wwwなし（ネイキッドドメイン）」を好んで使っていました。しかし、このブログ（Astro Starlight + Vercel）を構築し、Google AdSenseやSearch Consoleの設定を進める中で、その認識を改めることになりました。&lt;/p&gt;
&lt;p&gt;今回は、&lt;strong&gt;Vercel環境においてなぜ「wwwあり」が推奨されるのか&lt;/strong&gt;、そして&lt;strong&gt;Astroの設定でハマりやすい サイトマップの罠&lt;/strong&gt;について共有します。&lt;/p&gt;

&lt;div&gt;&lt;h2 id=&quot;vercelからの提案&quot;&gt;Vercelからの「提案」&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;AWS Route53 で取得したドメインを Vercel に接続したときのことです。
Vercelのダッシュボードには、親切にも次のような推奨設定が表示されていました。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;http://www.indigo165e83.com&quot;&gt;www.indigo165e83.com&lt;/a&gt;&lt;/strong&gt; -&gt; &lt;code dir=&quot;auto&quot;&gt;Primary&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;indigo165e83.com&lt;/strong&gt; -&gt; &lt;code dir=&quot;auto&quot;&gt;Redirect to www...&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;「え、逆じゃない？ 短い方をメインにしたいんだけど」と最初は思いました。しかし、Vercelの公式ドキュメントやアーキテクチャを調べていくと、これには明確な&lt;strong&gt;技術的理由&lt;/strong&gt;があることがわかったのです。&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;anycast-ip-と-cname-の違い&quot;&gt;Anycast IP と CNAME の違い&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;結論から言うと、Vercel（というか多くのCDN）においては、&lt;strong&gt;サブドメイン（www）の方がパフォーマンスと耐障害性に優れています。&lt;/strong&gt;&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;wwwありcnameレコード&quot;&gt;wwwあり（CNAMEレコード）&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;&lt;code dir=&quot;auto&quot;&gt;www&lt;/code&gt; を使う場合、DNSレコードは &lt;strong&gt;CNAME&lt;/strong&gt; で設定し、値は &lt;code dir=&quot;auto&quot;&gt;cname.vercel-dns.com&lt;/code&gt; になります。
これにより、Vercel側がトラフィックを動的に制御し、ユーザーから最も近いエッジサーバーへ最適にルーティングしてくれます。&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;wwwなしaレコード&quot;&gt;wwwなし（Aレコード）&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;一方、ネイキッドドメインを使う場合、DNSの仕様上 CNAME が使えない（ことが多い）ため、&lt;strong&gt;Aレコード&lt;/strong&gt; で固定IPを指定する必要があります。
Vercelの場合、ここで指定するのが &lt;strong&gt;&lt;code dir=&quot;auto&quot;&gt;76.76.21.21&lt;/code&gt;&lt;/strong&gt; という &lt;strong&gt;Anycast IP&lt;/strong&gt; です。&lt;/p&gt;
&lt;p&gt;これも十分に高速なのですが、固定IPである以上、CNAMEを用いた柔軟なトラフィック制御に比べると、Vercelの持てるポテンシャルを100%引き出しにくいという側面があります。&lt;/p&gt;
&lt;p&gt;「見た目のシンプルさ」を取るか、「インフラ的な最適解」を取るかのトレードオフですが、私は後者（wwwあり）を選びました。&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;astro設定ファイルの落とし穴&quot;&gt;Astro設定ファイルの落とし穴&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;DNSの設定（AWS Route53）とVercelのリダイレクト設定を完了し、「これで完璧だ」と思っていました。
しかし、Google Search Console にサイトマップを送信したところ、**「インデックス登録されない」「AdSenseの審査が進まない」**という問題が発生しました。&lt;/p&gt;
&lt;p&gt;原因は、私のローカルのコード、&lt;code dir=&quot;auto&quot;&gt;astro.config.mjs&lt;/code&gt; にありました。&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// 修正前の astro.config.mjs（NG）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;defineConfig&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;site: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;https://indigo165e83.com&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;// wwwがない！&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;// ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Webサーバー（Vercel）側では「wwwありが正義（Primary）」として動いているのに、Astroが自動生成する sitemap-index.xml の中身は、設定ファイルに従って &lt;code dir=&quot;auto&quot;&gt;https://indigo165e83.com/&lt;/code&gt;… （wwwなし）のURLをリストアップしていたのです。&lt;/p&gt;
&lt;p&gt;Googleロボットがサイトマップを見る（wwwなしのURLが書いてある）そのURLにアクセスするVercelが「wwwあり」にリダイレクトするGoogleロボット「おっと、リダイレクトされた。正規URLがどっちかわからないぞ」これが評価を下げる要因になっていました。&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;正しい設定のまとめ&quot;&gt;正しい設定のまとめ&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;この問題を解決するために行った設定は以下の3点です。これでサイトマップ、実アクセス、DNSの全てが「wwwあり」で一本化されました。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;AWS Route 53 の設定ルートドメインへのアクセスも取りこぼさないよう、Aレコードも正しく設定します。レコード名タイプ値備考wwwCNAMEcname.vercel-dns.comメインの経路(空欄)A76.76.21.21Anycast IP (リダイレクト用)&lt;/li&gt;
&lt;li&gt;Vercel の設定Domains設定画面で &lt;code dir=&quot;auto&quot;&gt;www.indigo165e83.com&lt;/code&gt; を Primary に設定。&lt;/li&gt;
&lt;li&gt;Astro の設定（忘れがち！）astro.config.mjs の site プロパティを、実際に運用するURLと完全に一致させます。&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// 修正後の astro.config.mjs（OK）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;defineConfig&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;site: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;https://www.indigo165e83.com&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;// wwwをつける&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;integrations: [&lt;/span&gt;&lt;span&gt;sitemap&lt;/span&gt;&lt;span&gt;(), &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h2 id=&quot;まとめ&quot;&gt;まとめ&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;「たかがwww、されどwww」。&lt;/p&gt;
&lt;p&gt;モダンなホスティング環境では、DNSの設定ひとつでパフォーマンスが変わりますし、SSGフレームワーク（Astro）側との整合性もSEOに直結します。&lt;/p&gt;
&lt;p&gt;もしVercel × Astroで構築していて「インデックス登録が遅いな？」と感じたら、一度 astro.config.mjs と DNS設定の整合性を確認してみることをおすすめします。&lt;/p&gt;</content:encoded><category>Vercel</category><category>AWS</category><category>route53</category><category>Astro</category><category>DNS</category></item><item><title>認証の基礎知識：Cookie、セッション、JWTなど</title><link>https://www.indigo165e83.com/blog/20260205-fundamentals-of-authentication/</link><guid isPermaLink="true">https://www.indigo165e83.com/blog/20260205-fundamentals-of-authentication/</guid><pubDate>Thu, 05 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Webアプリケーションを開発する上で、避けて通れないのが**「認証 (Authentication)」**です。&lt;/p&gt;
&lt;p&gt;特に Next.js + NextAuth (Auth.js) のようなモダンな構成を採用する場合、裏側で何が起きているのかを理解していないと、「ログインできない」「テストが通らない」「意図しないログアウトが起きる」といったトラブルに対処できません。&lt;/p&gt;
&lt;p&gt;この記事では、認証周りの重要キーワードを、イメージしやすいように整理しました。&lt;/p&gt;

&lt;div&gt;&lt;h2 id=&quot;1-認証と認可の基本コンポーネント&quot;&gt;1. 認証と認可の基本コンポーネント&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;まずは、認証情報の「入れ物」と「中身」についてです。&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;cookie-クッキー&quot;&gt;Cookie (クッキー)&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;役割：ブラウザ側の「保存容器」&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;サーバーから送られてくる小さなテキストデータです。ブラウザの中に保存されます。
認証においては、ログイン後にサーバーから発行された「会員証（セッションIDやトークン）」を保存しておく場所として使われます。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;HttpOnly属性&lt;/strong&gt;: JavaScriptからのアクセスを禁止する（XSS対策）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Secure属性&lt;/strong&gt;: HTTPS通信時のみ送信する（盗聴対策）。&lt;/li&gt;
&lt;/ul&gt;
&lt;aside aria-label=&quot;Tip&quot;&gt;&lt;p aria-hidden=&quot;true&quot;&gt;Tip&lt;/p&gt;&lt;div&gt;&lt;p&gt;認証トラブルの8割はCookieにあります。「ログインできない」ときは、DevToolsの Application &gt; Cookies を見て、そもそもCookieが保存されているか、ドメインやPathが合っているかを確認しましょう。&lt;/p&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;div&gt;&lt;h3 id=&quot;session-セッション&quot;&gt;Session (セッション)&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;役割：サーバー側の「滞在状態」&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;「このユーザーは現在ログイン中である」という&lt;strong&gt;状態そのもの&lt;/strong&gt;を指します。
Webサーバー（HTTP）は本来ステートレス（記憶喪失）なため、リクエストごとに「これはさっきのユーザーだ」と識別する仕組みが必要です。これを実現するのがセッションです。&lt;/p&gt;
&lt;hr&gt;
&lt;div&gt;&lt;h2 id=&quot;2-セッション管理の2つの方式&quot;&gt;2. セッション管理の2つの方式&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;NextAuthなどのライブラリを使う際、最も重要なアーキテクチャの選択がここにあります。&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;データベースセッション-adapter利用&quot;&gt;データベースセッション (Adapter利用)&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;＝ サーバーサイドセッション&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;仕組み&lt;/strong&gt;: セッションIDだけをCookie（ユーザー）に渡し、実データ（誰が、いつまで有効か）はデータベースの &lt;code dir=&quot;auto&quot;&gt;Session&lt;/code&gt; テーブルに保存します。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;メリット&lt;/strong&gt;: サーバー側から特定のユーザーを強制ログアウト（DBレコード削除）させることができます。セキュリティが高いです。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;デメリット&lt;/strong&gt;: ページ遷移のたびにDBアクセスが発生するため、パフォーマンスに影響が出る場合があります。&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h3 id=&quot;jwt-json-web-token&quot;&gt;JWT (JSON Web Token)&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;＝ クライアントサイドセッション&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;仕組み&lt;/strong&gt;: ユーザー情報（ID、権限、有効期限など）をJSON形式で記述し、改ざん防止の署名を付けて暗号化した文字列（トークン）をCookieに保存します。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;メリット&lt;/strong&gt;: サーバー（DB）を見に行かなくても、トークンの中身だけで認証検証ができるため高速です。スケーラビリティに優れます。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;デメリット&lt;/strong&gt;: 一度発行したトークンは有効期限まで使い続けられるため、サーバー側からの即時無効化（強制ログアウト）が困難です。&lt;/li&gt;
&lt;/ul&gt;
&lt;aside aria-label=&quot;NextAuthの制約&quot;&gt;&lt;p aria-hidden=&quot;true&quot;&gt;NextAuthの制約&lt;/p&gt;&lt;div&gt;&lt;p&gt;NextAuthでは、後述する「Credentials認証」を使用する場合、セキュリティ上の理由から&lt;strong&gt;強制的にJWTモード&lt;/strong&gt;になります。&lt;/p&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;hr&gt;
&lt;div&gt;&lt;h2 id=&quot;3-認証の入り口プロバイダー&quot;&gt;3. 認証の入り口（プロバイダー）&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;ユーザーがどうやって本人確認を行うかの方式です。&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;credentials-認証&quot;&gt;Credentials 認証&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;＝ メールアドレス / パスワード認証&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;昔ながらの ID/Password を入力する方式です。
アプリ開発者が独自に認証ロジック（DBと照合するなど）を実装する必要があります。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;自動テスト（Playwright等）での利用&lt;/strong&gt;:
Google等の外部認証はBot検知が厳しいため、E2Eテストのためだけにこの「Credentials認証」を裏口として実装し、テスト環境でのみ有効化する手法がよく取られます。&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h3 id=&quot;外部idプロバイダ認証-oidc--oauth-20&quot;&gt;外部IDプロバイダ認証 (OIDC / OAuth 2.0)&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;＝ ソーシャルログイン (Google, GitHubなど)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;「認証（本人確認）」をGoogleなどの信頼できる第三者に任せる方式です。アプリ側はパスワードを管理しません。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;OAuth 2.0&lt;/strong&gt;: 「認可（データへのアクセス権）」のための仕様。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;OIDC (OpenID Connect)&lt;/strong&gt;: OAuth 2.0を拡張して「認証（ID確認）」を行えるようにした仕様。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;ユーザーは「Googleのパスポート」を見せて入国するイメージです。セキュリティリスクを外部にオフロードできるため、現代の主流となっています。&lt;/p&gt;
&lt;hr&gt;
&lt;div&gt;&lt;h2 id=&quot;4-nextauth内部のデータフロー&quot;&gt;4. NextAuth内部のデータフロー&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;実装時によく混乱するキーワードです。&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;token-トークン&quot;&gt;Token (トークン)&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;役割：データの運び屋&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;JWT方式において、暗号化されたデータの塊そのものです。
ログイン時にDBから取得した情報（User IDやRole）は、一度この「トークン」の中に書き込まれます。&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;callbacks-コールバック&quot;&gt;Callbacks (コールバック)&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;役割：データ加工のフックポイント&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;NextAuthが認証処理を行う途中で、開発者がデータを操作できる関数です。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;&lt;code dir=&quot;auto&quot;&gt;jwt&lt;/code&gt; コールバック&lt;/strong&gt;:
ログイン成功直後や、トークン更新時に呼ばれます。「DBから取得した &lt;code dir=&quot;auto&quot;&gt;role&lt;/code&gt; 情報を、JWTトークンの中に書き込む」といった処理はここに書きます。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code dir=&quot;auto&quot;&gt;session&lt;/code&gt; コールバック&lt;/strong&gt;:
フロントエンドで &lt;code dir=&quot;auto&quot;&gt;useSession()&lt;/code&gt; 等を使った時に呼ばれます。「JWTトークンの中から &lt;code dir=&quot;auto&quot;&gt;role&lt;/code&gt; 情報を取り出して、ブラウザで使えるようにする」処理を書きます。&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// NextAuthの設定例（イメージ）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;callbacks: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;// 1. DBの情報をトークン(荷台)に載せる&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;async &lt;/span&gt;&lt;span&gt;jwt&lt;/span&gt;&lt;span&gt;({ token, user }) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (user) token&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;role&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; user&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;role&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; token;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;// 2. トークン(荷台)から取り出してアプリで使う&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;async &lt;/span&gt;&lt;span&gt;session&lt;/span&gt;&lt;span&gt;({ session, token }) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;session&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;user&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;role&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; token&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;role&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; session;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h2 id=&quot;まとめ&quot;&gt;まとめ&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;認証基盤を選定・実装する際は、以下のトレードオフを理解しておくことが重要です。&lt;/p&gt;






























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th align=&quot;left&quot;&gt;特徴&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;データベースセッション&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;JWT (JSON Web Token)&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;状態の場所&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;サーバー (DB)&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;クライアント (Cookie)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;パフォーマンス&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;DBアクセスが必要&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;高速 (計算のみ)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;強制ログアウト&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;容易 (レコード削除)&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;困難 (期限切れ待ち)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;Credentials利用&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;原則不可 (NextAuth)&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;必須&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;</content:encoded><category>認証</category><category>Authentication</category><category>NextAuth</category><category>Security</category><category>JWT</category></item><item><title>Playwright トラブルシューティング - Firefoxだけが動かない</title><link>https://www.indigo165e83.com/blog/20260204-playwright-trouble-shooting/</link><guid isPermaLink="true">https://www.indigo165e83.com/blog/20260204-playwright-trouble-shooting/</guid><pubDate>Wed, 04 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;現在開発中のAI画像ストックサイト『&lt;strong&gt;&lt;a href=&quot;https://free-images.indigo165e83.com&quot;&gt;free-images&lt;/a&gt;&lt;/strong&gt;』に、品質担保のためE2Eテストツールである &lt;strong&gt;Playwright&lt;/strong&gt; を導入することにしました。&lt;/p&gt;
&lt;p&gt;「モダンなツールだし、サクッと導入できるだろう」と高を括っていたのですが、&lt;strong&gt;Firefox環境でのみテストが落ちる&lt;/strong&gt; という泥沼にハマりました。
今回はそのトラブルシューティングの記録を残しておきます。WSL環境で開発している方の参考になれば幸いです。&lt;/p&gt;

&lt;div&gt;&lt;h2 id=&quot;最初のつまずき謎の-syntax-error&quot;&gt;最初のつまずき：謎の Syntax Error&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;まずは公式ドキュメント通りにテストコード (&lt;code dir=&quot;auto&quot;&gt;tests/home.spec.ts&lt;/code&gt;) を書き、実行してみました。&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { test, expect } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;@playwright/test&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;test&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;describe&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;トップページ（ゲスト表示）のテスト&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;test&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;beforeEach&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&lt;span&gt;{ &lt;/span&gt;&lt;span&gt;page&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; page&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;goto&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;/ja&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;// ...各テストケース&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;しかし、実行すると即座にエラー。&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Error:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Playwright&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Test&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;did&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;not&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;expect&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;test.describe&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;to&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;be&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;called&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;here.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;構文は合っているはずなのに、「ここで describe を呼ぶな」と怒られます。&lt;/p&gt;
&lt;p&gt;よくよくエラーログを見ると、「You have two different versions of @playwright/test」 という不穏なメッセージが。&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;原因npmとpnpmの混ぜるな危険&quot;&gt;原因：npmとpnpmの混ぜるな危険&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;pnpm-lock.yaml があるのに、手癖で npm install などを実行してしまったため、package-lock.json も生成されていたのです。&lt;/p&gt;
&lt;p&gt;これにより、node_modules 内でバージョンの競合が起き、Playwrightが正しく動作していませんでした。&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;解決策&quot;&gt;解決策：&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;環境をクリーンにして、パッケージマネージャーを pnpm に統一しました。&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;rm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;package-lock.json&lt;/span&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;# 不要なロックファイルを削除&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;rm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-rf&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;node_modules&lt;/span&gt;&lt;span&gt;   &lt;/span&gt;&lt;span&gt;# 既存のモジュールを全削除&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;pnpm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;store&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;prune&lt;/span&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;# ストアの整合性を確認&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;pnpm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;# 再インストール&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;これで構文エラーは解消。Chrome (Chromium) でのテストはパスするようになりました。
「よし、これで完了！」と思った矢先、本当の敵が現れます。&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;firefoxだけが起動しない&quot;&gt;Firefoxだけが起動しない&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;クロスブラウザテストのため、Firefoxを含めて実行すると…&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;npx&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;playwright&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;test&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tests/home.spec.ts&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--project=firefox&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--headed&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Chromeは通るのに、Firefoxだけが全滅です。&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;エラー内容&quot;&gt;エラー内容&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Error: browserType.launch: ENOENT: no such file or directory, stat &apos;/home/indigo/.cache/ms-playwright/firefox-1495/firefox/lock&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;ENOENT (Error NO ENTry)、つまり「ファイルがない」と言っています。
パスを見ると ~/.cache/ms-playwright/… という、Playwrightがブラウザのバイナリを管理しているディレクトリを指していました。&lt;/p&gt;
&lt;p&gt;なぜ消えた？
node_modules を再インストールした際、テストランナー自体は入りましたが、ブラウザ本体（Firefoxバイナリ）のリンクや実体がおかしくなっていたようです。
また、WSL (Ubuntu) 環境特有のライブラリ不足も疑われました。&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;解決策キャッシュの完全削除と再インストール&quot;&gt;解決策：キャッシュの完全削除と再インストール&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;中途半端に修復しようとせず、Playwrightのブラウザキャッシュを一度「無」にしてから入れ直すのが最短ルートでした。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;キャッシュフォルダを爆破する
壊れている可能性のある既存のブラウザデータを手動で削除します。&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;rm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-rf&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/.cache/ms-playwright&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;ブラウザと依存関係の再インストール
ただ install するだけでなく、install-deps も実行するのがポイントです。特にWSL環境では、Firefoxを動かすためのシステムライブラリ（GTKやdbus関連など）が不足していることがよくあります。&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# ブラウザバイナリのダウンロード&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;npx&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;playwright&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# システム依存ライブラリのインストール（sudo権限が必要になる場合あり）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;npx&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;playwright&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install-deps&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;これを実行すると、Ubuntu側で必要なパッケージ（libgtk-3-0 や libdbus-glib-1-2 など）も自動でチェックしてインストールしてくれます。&lt;/p&gt;
&lt;p&gt;テスト成功
再度Firefoxでテストを実行。&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;npx&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;playwright&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;test&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tests/home.spec.ts&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--project=firefox&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;無事、キツネのアイコンとともにブラウザが立ち上がり、テストがオールグリーンになりました！&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;まとめ&quot;&gt;まとめ&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;今回の教訓は以下の3点です。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ロックファイルは一つに絞る: npm と pnpm を混在させると、特にバイナリを扱うライブラリ（PlaywrightやPrismaなど）で整合性が壊れやすい。&lt;/li&gt;
&lt;li&gt;Playwrightのブラウザは node_modules 外にある: npm install し直しただけではブラウザ本体は直らない。~/.cache/ms-playwright を疑うべし。&lt;/li&gt;
&lt;li&gt;WSLユーザーは install-deps を忘れずに: Linux環境ではブラウザを動かすためのOSレベルの依存関係が必須。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;E2Eテストは環境構築でハマりがちですが、一度整えば「寝ている間にバグを見つけてくれる」頼もしい相棒になります。めげずに整備していきましょう。&lt;/p&gt;</content:encoded><category>Playwright</category><category>Testing</category><category>Firefox</category><category>WSL</category><category>Troubleshooting</category></item><item><title>生成画像プロンプトを洗練</title><link>https://www.indigo165e83.com/blog/20260203-prompt-engineering-textures/</link><guid isPermaLink="true">https://www.indigo165e83.com/blog/20260203-prompt-engineering-textures/</guid><pubDate>Tue, 03 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;「赤い布の画像を作って」
AIにそう頼めば、確かに赤い布の画像は出てきます。しかし、それは果たしてストックフォトサイトで「売れる素材」と言えるでしょうか？&lt;/p&gt;
&lt;p&gt;現在開発中の画像ストックサイト『&lt;strong&gt;&lt;a href=&quot;https://free-images.indigo165e83.com&quot;&gt;free-images&lt;/a&gt;&lt;/strong&gt;』では、単なる自動生成を超え、デザイナーが「これこそ探していた質感だ」と唸るクオリティを目指しています。今回は、Google Gemini APIを駆使した&lt;strong&gt;プロンプト改善の旅&lt;/strong&gt;についてお話しします。&lt;/p&gt;

&lt;div&gt;&lt;h2 id=&quot;名もなき色に命を吹き込む&quot;&gt;「名もなき色」に命を吹き込む&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;ストックフォトの世界では、「赤」や「青」といった単純な色名だけでは不十分です。例えば、Webデザインの背景として求められるのは、その色が持つ「空気感」や「深み」です。&lt;/p&gt;
&lt;p&gt;そこで、Geminiに与える色指定を&lt;strong&gt;40種類以上の詳細なフレーズ&lt;/strong&gt;へとアップデートしました。&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;改善のビフォーアフター&quot;&gt;改善のビフォー・アフター&lt;/h3&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Before:&lt;/strong&gt; &lt;code dir=&quot;auto&quot;&gt;Vivid Crimson Red&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;After:&lt;/strong&gt; &lt;code dir=&quot;auto&quot;&gt;Vivid Crimson Red – with intense saturation and a smooth, velvety light falloff&lt;/code&gt;
（鮮烈なクリムゾンレッド － 強い彩度と、滑らかでベルベットのような光の減衰）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;この「ベルベットのような光の減衰」という一言があるだけで、AIは布の表面で光がどのように散らばり、影がどう落ちるかを計算し始めます。ただの赤い平面が、触れられそうなほどの &lt;strong&gt;質感（テクスチャ）&lt;/strong&gt; へと昇華されます。&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;ニッチを攻めるメタリックの魔力&quot;&gt;ニッチを攻める：メタリックの魔力&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;サイトの差別化として注力したのが、ゴールド、シルバー、ブロンズといった&lt;strong&gt;メタリック素材&lt;/strong&gt;です。金属の質感は、ライティングの指示一つで「安っぽいCG」にも「重厚な写真」にもなります。&lt;/p&gt;
&lt;p&gt;今回、プロンプトに組み込んだのは以下のような「物理現象」への言及です。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Hammered Raw Silver&lt;/strong&gt;: 多方向からの光を捉える「打ち出し加工」の凹凸。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Molten Liquid Bronze&lt;/strong&gt;: 深い琥珀色の反射を伴う、流動的で粘り気のある表面。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;これらに &lt;code dir=&quot;auto&quot;&gt;Specular highlights（鏡面反射）&lt;/code&gt; や &lt;code dir=&quot;auto&quot;&gt;Micro-crystalline detail（微結晶のディテール）&lt;/code&gt; といった専門的なキーワードを掛け合わせることで、PCの計算リソースをフルに活かした、圧倒的なリアリティを実現しました。&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;デザイナーの視点をaiに教え込む&quot;&gt;デザイナーの視点をAIに教え込む&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;プロンプトの改善は、AIを「画材」から「熟練のフォトグラファー」へ育てる作業に似ています。&lt;/p&gt;
&lt;p&gt;単に綺麗なものを作らせるのではなく、「&lt;strong&gt;Minimalist composition（ミニマルな構図）&lt;/strong&gt;」や「&lt;strong&gt;Top-down flat lay（真上からの俯瞰）&lt;/strong&gt;」といった、商用利用で需要の高いアングルをランダムに選択させるロジックを組み込みました。&lt;/p&gt;
&lt;p&gt;これにより、寝ている間に実行される「仮想カメラマン」たちは、常にストックフォトとして使い勝手の良い画像を量産してくれるようになったのです。&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;終わりに言葉が画像を作る&quot;&gt;終わりに：言葉が画像を作る&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;今回の改善を通じて感じたのは、「&lt;strong&gt;AI画像生成の限界は、人間の想像力と語彙力の限界である&lt;/strong&gt;」ということです。&lt;/p&gt;
&lt;p&gt;プロンプトエンジニアリングは、単なる技術的な作業ではなく、言葉の力で視覚的なリアリティを創り出すクリエイティブな挑戦です。&lt;/p&gt;</content:encoded><category>Gemini</category><category>プロンプトエンジニアリング</category><category>AI画像生成</category><category>ストックフォト</category><category>個人開発</category></item><item><title>povo2.0で構築する安価なSMS認証基盤</title><link>https://www.indigo165e83.com/blog/20260202-povo-multi-phone-number/</link><guid isPermaLink="true">https://www.indigo165e83.com/blog/20260202-povo-multi-phone-number/</guid><pubDate>Mon, 02 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;開発やサービス検証を行っていると、「商用環境とテスト環境でアカウントを分けたい」「検証用にクリーンな電話番号がもう一つ欲しい」という場面に多々遭遇します。&lt;/p&gt;
&lt;p&gt;しかし、そのためだけに月額数千円のキャリア契約を追加するのはコストが見合いません。今回は、Androidスマホとpovo2.0を組み合わせ、&lt;strong&gt;年間数百円で「死なない認証用番号」を維持する方法&lt;/strong&gt;をまとめました。&lt;/p&gt;

&lt;div&gt;&lt;h2 id=&quot;1-なぜ物理的な電話番号が必要なのか&quot;&gt;1. なぜ「物理的な電話番号」が必要なのか&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;最近のWebサービス（Amazon、Google、SNS各社）は、Bot対策として050番号（IP電話）によるSMS認証をブロックする傾向が強まっています。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;IP電話の限界&lt;/strong&gt;: 多くの主要サービスで「この番号は登録できません」と弾かれる。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;アカウントの独立性&lt;/strong&gt;: メインの番号を使い回すと、テストアカウントで問題が起きた際にメインアカウントまで芋づる式に制限を受けるリスクがある。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;確実な検証環境を構築するには、070/080/090から始まる「本物の回線」が不可欠です。&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;2-最適解としてのpovo20--esim&quot;&gt;2. 最適解としての「povo2.0 × eSIM」&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;エンジニアにとって、povo2.0は「予備回線」としてこれ以上ないスペックを持っています。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;基本料0円&lt;/strong&gt;: 使わない月は維持費が発生しません。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;eSIMによる即時発行&lt;/strong&gt;: 物理カードの到着を待たず、数時間で検証を開始できます。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Androidとの相性&lt;/strong&gt;: 物理SIM（メイン）とeSIM（povo）を同時に有効化し、1台の端末で2つのSMSをリアルタイムに待ち受け可能です。&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;3-実践テストアカウント運用のためのtips&quot;&gt;3. 実践：テストアカウント運用のためのTips&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;招待連携のエラーを回避する&quot;&gt;招待・連携のエラーを回避する&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;複数のアカウントを1つのブラウザで管理すると、セッションやキャッシュが干渉し、認証エラーや招待リンクの無効化を招きます。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;対策&lt;/strong&gt;: 必ずシークレットウィンドウを使用するか、ブラウザの「プロファイル」を完全に分けて運用することを推奨します。&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h3 id=&quot;番号を年間440円で延命するスキーム&quot;&gt;番号を「年間440円」で延命するスキーム&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;povo2.0は180日間トッピング未購入だと利用停止になりますが、以下の運用でコストを最小化できます。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;smash.使い放題パック（220円/24時間）&lt;/strong&gt; を半年に1回購入。&lt;/li&gt;
&lt;li&gt;もしくは、外出先でデバッグ作業が必要な日に &lt;strong&gt;データ使い放題（330円/24時間）&lt;/strong&gt; を購入。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;これにより、月額換算&lt;strong&gt;約37円&lt;/strong&gt;という圧倒的低コストで、いつでもSMS認証が可能な状態をキープできます。&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;4-まとめ&quot;&gt;4. まとめ&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;「とりあえず番号が必要」という時のために、eSIMスロットを一つpovoに割り当てておくのは、非常に有効な手段です。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;環境分離&lt;/strong&gt;: プライベートとテスト環境の完全な隔離。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;リスクヘッジ&lt;/strong&gt;: メイン番号のクリーンな状態を維持。&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>povo2.0</category><category>デュアルSIM</category><category>テスト環境</category><category>開発Tips</category></item><item><title>30秒周期で画面が収縮する謎の挙動を追う。</title><link>https://www.indigo165e83.com/blog/20260201-periodic-screen-shrinkage/</link><guid isPermaLink="true">https://www.indigo165e83.com/blog/20260201-periodic-screen-shrinkage/</guid><pubDate>Sun, 01 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;作業中に突然発生し始めた「約30秒周期で画面が一瞬収縮する」という謎の挙動。
作業に集中できないだけでなく、ハードの故障も疑われるこの問題に対し、イベントログの解析から物理的な環境要因の特定までの記録をまとめました。&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;予兆30秒ごとの異変&quot;&gt;予兆：30秒ごとの「異変」&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;ある日、私のデスクトップ環境（GALLERIA / RTX 4060 Ti）で、画面が約30秒おきに数ミリ収縮し、また戻るという現象が起き始めました。&lt;/p&gt;
&lt;p&gt;「30秒」というあまりに正確な周期は、ハードウェアの寿命というより、何らかのソフトウェア的な「タイムアウトとリトライ」を強く示唆していました。&lt;/p&gt;

&lt;div&gt;&lt;h2 id=&quot;迷走intel系サービスの負の連鎖&quot;&gt;迷走：Intel系サービスの負の連鎖&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;まず真っ先に確認したのは**イベントビューアー（システムログ）**です。案の定、そこには犯人らしきログが並んでいました。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Intel(R) SUR QC Software Asset Manager&lt;/strong&gt;: 「接続を待機中にタイムアウト (30000 ミリ秒) になりました」&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Energy Server Service queencreek&lt;/strong&gt;: クラッシュの繰り返し&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;これらはIntelの利用状況レポート用プログラムですが、これが30秒（30,000ms）ごとに起動に失敗し、GPUの描写に干渉していたのです。&lt;/p&gt;
&lt;p&gt;以下の対策を講じました。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code dir=&quot;auto&quot;&gt;Intel(R) Computing Improvement Program&lt;/code&gt; のアンインストール&lt;/li&gt;
&lt;li&gt;関連するIntelサービスの無効化&lt;/li&gt;
&lt;li&gt;デバイスマネージャーでの「HID カスタム センサー」の無効化&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;これで解決かと思いきや……&lt;strong&gt;症状は再発しました。&lt;/strong&gt;&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;突破口マルチモニターの死角&quot;&gt;突破口：マルチモニターの死角&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;ソフトウェア的な要因をほぼ潰しても治らない。ここで、物理環境に目を向けました。
私の作業環境はマルチモニターですが、そのうち1枚は、&lt;strong&gt;TCL製のGoogle TV&lt;/strong&gt;です。&lt;/p&gt;
&lt;p&gt;ふと気づき、ある実験をしました。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;TVがONの時&lt;/strong&gt;: 症状は出ない。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;TVがOFFの時&lt;/strong&gt;: 30秒周期で画面が収縮する。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;真犯人は「TVの待機信号」でした。&lt;/strong&gt;
Google TVがOFF（待機状態）の際、約30秒周期で「生存確認（ハンドシェイク）」をPCに送っており、それを受けるたびにWindowsが「モニター構成が変わった」と誤認して画面全体を再計算（リセット）していたのです。&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;解決hdmi-cecの封印と最適化&quot;&gt;解決：HDMI CECの封印と最適化&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;最終的に、以下の設定を行うことで、画面は完全に安定しました。&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;1-tv側tclの設定&quot;&gt;1. TV側（TCL）の設定&lt;/h3&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;T-Link（HDMI CEC）をオフ&lt;/strong&gt;: HDMI経由の制御信号を遮断。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;クイックスタートをオフ&lt;/strong&gt;: 待機中の余計なスキャンを防止。&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h3 id=&quot;2-windows-11の設定&quot;&gt;2. Windows 11の設定&lt;/h3&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;「モニターの接続に基づいてウィンドウの位置を記憶する」をオフ&lt;/strong&gt;: 切断検知時の描写リセットを抑制。&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;まとめ&quot;&gt;まとめ&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;「30秒周期」という規則正しいエラーは、ソフトウェアの再試行、あるいは外部機器のポーリングを疑うのが鉄則です。
今回は「Intelのテレメトリサービス」と「スマートTVの待機信号」という二重の罠でしたが、ログと物理現象を紐付けることで解決できました。&lt;/p&gt;
&lt;p&gt;同じようなマルチモニター環境で「画面が一瞬ガタつく」方は、一度モニター（特にTV）の省電力設定を疑ってみることをお勧めします。&lt;/p&gt;</content:encoded><category>Windows11</category><category>トラブルシューティング</category><category>NVIDIA</category><category>GoogleTV</category><category>TCL</category></item></channel></rss>