<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="/rss.xsl"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Sveltekit Blog Engine</title>
        <link>https://testblog-6br.pages.dev</link>
        <description>CFベースの無料サーバーレスSvelteKitブログエンジンの公式ブログ。</description>
        <language>ja</language>
        <atom:link href="https://testblog-6br.pages.dev/ja/rss.xml" rel="self" type="application/rss+xml"/>
        <lastBuildDate>Tue, 22 Sep 2026 18:16:14 GMT</lastBuildDate>
        <item>
            <title><![CDATA[SKBE v1.0.1.0 アップデート：スロット別デザイン独立プリセット、レスポンシブ配置および多言語（i18n）最適化]]></title>
            <link>https://testblog-6br.pages.dev/ja/devlog/skbe-v1010-design-preset-backup-guide</link>
            <guid isPermaLink="true">https://testblog-6br.pages.dev/ja/devlog/skbe-v1010-design-preset-backup-guide</guid>
            <pubDate>Sun, 20 Sep 2026 23:18:38 GMT</pubDate>
            <description><![CDATA[SvelteKit Blog Engine（SKBE）v1.0.1.0のアップデート内容を解説します。DB破壊のリスクを排除し、特定スロットのデザインのみを独立してバックアップ＆任意スロットへ注入できる中立プリセットシステム、サイドバーのレスポンシブ縦並びレイアウト、日本語の禁則・折り返し対応、そしてデプロイスクリプトのプロジェクト名自動同期パイプラインを共有します。]]></description>
            <content:encoded><![CDATA[<h2 id="1-はじめにv10015以降に浮き彫りとなった運用課題">1. はじめに：v1.0.0.15以降に浮き彫りとなった運用課題</h2>
<p>SvelteKit Blog Engine（SKBE）v1.0.0.15では、D1サイドバースナップショットによるO(1)静的配信やメディアURLの移行パイプラインを実装しました。その後、実際の管理画面で記事執筆やテーマ編集を多言語環境（韓国語・英語・日本語）および多様な画面解像度で運用する中で、実用面におけるいくつかの構造的課題やレイアウト崩れが確認されました。</p>
<p>今回のv1.0.1.0アップデートでは、ブログ運営の要となる<strong>デザイン編集の安全性</strong>、<strong>多言語UIにおけるレスポンシブ表示の完成度</strong>、そして<strong>デプロイ自動化の利便性</strong>を全面的に刷新しました：</p>
<ol>
<li><strong>デザインバックアップにおける破壊的な全上書きリスクの排除</strong>：データベースの全テーブルを丸ごと上書きし、他スロットのデザインまで巻き添えでリセットしていた旧方式を廃止。特定スロットのデザインのみを中立なJSONプリセット（<code>design_preset</code>）として安全にエクスポートし、指定した任意のスロットに復元できる構造へと刷新しました。</li>
<li><strong>サイドバーの狭い領域におけるUIはみ出しおよび日本語テキスト途切れの解消</strong>：横幅の狭いサイドバーでボタン右端がフレーム外へ突き抜けていたレイアウト崩れと、スペースのない日本語文章で改行が行われないCJKタイポグラフィの不具合を解消しました。</li>
<li><strong>スロット初期表示名のリアルタイム動的翻訳</strong>：DBに韓国語で保存されていたスロット基本名（「디자인 슬롯 1」など）を、管理者の選択言語に合わせてリアルタイムに動的翻訳（<code>デザインスロット 1</code>、<code>Design Slot 1</code>）して表示する仕組みを導入しました。</li>
<li><strong>デプロイスクリプト内のプロジェクト名自動同期</strong>：新規セットアップやマルチアカウント切り替えの際、<code>package.json</code>のデプロイコマンド内のプロジェクト名を手動修正する必要をなくす自動同期パイプラインを構築しました。</li>
<li><strong>サイドバースナップショットのリアルタイム同期＆依存関係のセキュリティパッチ</strong>：記事の作成・更新・削除時にD1キャッシュを即座に非同期再生成し、主要ライブラリの脆弱性対応を行いました。</li>
</ol>
<hr>
<h2 id="2-デザインエディタスロット別独立プリセットのバックアップ復元アーキテクチャ">2. デザインエディタ：スロット別独立プリセットのバックアップ＆復元アーキテクチャ</h2>
<h3 id="1-従来方式の限界とデータ破壊リスク">1) 従来方式の限界とデータ破壊リスク</h3>
<p>従来のバックアップ／復元機能は、<code>/api/restore</code>エンドポイントを介してデータベース上の<code>layouts</code>、<code>widgets</code>、<code>layout_widgets</code>テーブルのレコードを完全に削除（<code>DELETE FROM</code>）した上で、バックアップファイルの内容を丸ごと再挿入する仕組みでした。</p>
<p>この設計には重大な潜在リスクが存在していました：</p>
<ul>
<li><strong>破滅的なデータ損失リスク</strong>：1つのバックアップファイルを復元した瞬間、入念に作り込んだスロット1・2・3のすべてのレイアウトとウィジェット配置が無警告で初期化されてしまいました。</li>
<li><strong>スロット単位の選択が不可能</strong>：特定のスロット（例：スロット2のミニマル1段レイアウト）のデザインだけを個別のファイルとして保管したり、別ブログで作成したデザインを現在のブログのスロット3にだけ安全に取り込むことができませんでした。</li>
<li><strong>復元前のプレビュー確認が不可能</strong>：ファイルを選択した瞬間に本番DBが上書きされてしまうため、誤ったファイルを指定した場合にロールバックする手段がありませんでした。</li>
</ul>
<h3 id="2-刷新されたアーキテクチャ中立プリセットdesign-presetシステム">2) 刷新されたアーキテクチャ：中立プリセット（<code>design_preset</code>）システム</h3>
<p>危険なDBダンプ方式を完全に撤廃し、エディタ内部のリアクティブなメモリ状態（<code>slots</code>）と連動した<strong>独立型デザインプリセット規格</strong>を新たに設計しました。</p>
<pre><code class="language-typescript">// 特定スロットの純粋なデザインデータのみを中立規格として抽出
const presetData = {
    version: &quot;3.0&quot;,
    backupType: &quot;design_preset&quot;,
    timestamp: new Date().toISOString(),
    presetName: slotName,
    design: {
        theme: snapshot.theme,
        header: snapshot.header,
        footer: snapshot.footer,
        site_title: snapshot.site_title,
        widget_shadow_global: snapshot.widget_shadow_global,
        layout: snapshot.layout,
        widgets: snapshot.widgets
    }
};
</code></pre>
<h4 id="新プリセットシステムの主な特長">新プリセットシステムの主な特長</h4>
<ul>
<li><strong>スロット番号への依存性を排除</strong>：プリセット内部には特定のスロットID（<code>slot1</code>など）が記録されません。スロット1から書き出したデザインであっても、スロット2やスロット3など、好きなスロットへ自在に流し込むことができます。</li>
<li><strong>未保存の編集状態もそのまま反映</strong>：現在編集中のアクティブなスロットを書き出す場合、DB未反映のリアルタイム編集状態（<code>getSnapshotOfCurrentSlot()</code>）をそのままキャプチャしてダウンロードします。</li>
<li><strong>プレビュー先行型の2段階安全復元（Preview-First）</strong>：バックアップファイルを読み込み対象スロットを指定すると、まずはエディタのメモリ上だけに反映され、<strong>画面プレビューで結果を即座に確認</strong>できます。ユーザーが確認の上、右上の「ブログに適用」または「現在の設定を保存」ボタンを押して初めて本番DBへ永続化されます。</li>
<li><strong>旧形式バックアップの自動互換処理（<code>convertLegacyBackupToPreset</code>）</strong>：過去に生成されたDBダンプ形式のバックアップファイルもパーサーが構造を自動検知し、新規格のプリセットへと正規化して復元できるため、従来のバックアップ資産も無駄になりません。</li>
</ul>
<hr>
<h2 id="3-サイドバーのレスポンシブ縦並び配置と多言語i18nの徹底">3. サイドバーのレスポンシブ縦並び配置と多言語（i18n）の徹底</h2>
<h3 id="1-サイドバー要素のはみ出し・ボタン途切れを縦並びfull-widthで解決">1) サイドバー要素のはみ出し・ボタン途切れを縦並び（Full-Width）で解決</h3>
<p>管理画面サイドバーの有効コンテンツ幅は、余白（padding）を除くと約260px〜290px程度です。従来はスロット選択ドロップダウン（<code>select</code>）とダウンロードボタン、復元対象ラベルとドロップダウン、復元実行ボタンがそれぞれ横1列（<code>flex-row</code>）に並んでいました。</p>
<p>そのため、ドロップダウンが幅を占有すると右側のボタンがサイドバーの外へと押し出され、ダウンロードボタンの文字が途切れたり、復元適用ボタンにいたっては画面外へ完全に消えてしまう現象が発生していました。</p>
<p>これを**レスポンシブな縦並びスタック構造（<code>flex-col</code>、<code>w-full</code>）**へと全面的に改変しました：</p>
<pre><code class="language-html">&lt;!-- バックアップ領域：横幅100%の縦並び配置 --&gt;
&lt;div class=&quot;setting-control flex flex-col gap-2 w-full&quot;&gt;
    &lt;select class=&quot;select-field w-full&quot; bind:value={slotBackupTargetId}&gt;...&lt;/select&gt;
    &lt;button class=&quot;btn-primary w-full flex items-center justify-center gap-2&quot;&gt;
        &lt;Download size={16} /&gt;
        &lt;span&gt;スロットバックアップをダウンロード&lt;/span&gt;
    &lt;/button&gt;
&lt;/div&gt;
</code></pre>
<p>各要素が独立した100%幅のブロックとして配置されるため、サイドバーがどんなに狭くなっても要素が途切れることなく端正に整列します。</p>
<h3 id="2-日本語環境における文字途切れおよび18文字カタカナボタンのはみ出しデバッグ">2) 日本語環境における文字途切れおよび18文字カタカナボタンのはみ出しデバッグ</h3>
<p>多言語環境のテストにおいて、韓国語や英語では起こらなかった深刻なレイアウト崩れが<strong>日本語環境でのみ顕著に発生する</strong>現象に直面しました。調査の結果、2つの物理的要因が判明しました：</p>
<h4 id="要因a日本語の分かち書きなしスペースなしと-word-break-keep-all-の衝突">要因A：日本語の分かち書きなし（スペースなし）と <code>word-break: keep-all</code> の衝突</h4>
<ul>
<li><strong>現象</strong>：説明テキスト（<code>各スロットのデザインを独立したファイルとしてエ...</code>）の右端が枠外へ突き抜け、見切れてしまう。</li>
<li><strong>原因</strong>：韓国語は単語間にスペースがあるため<code>keep-all</code>で単語単位の改行が効きますが、<strong>日本語の文章には単語間のスペースが存在しません</strong>。ブラウザが文章全体を「1つの巨大な英単語」のように認識し、改行を行わずに枠を突き破ってしまったのです。</li>
<li><strong>解決策</strong>：<code>overflow-wrap: anywhere; word-break: break-word;</code>を採用し、スペースのない日本語の文字列でも枠の右端で1文字単位で自然に折り返されるよう修正しました。</li>
</ul>
<h4 id="要因bレガシーcssの-width-auto-important-と18文字のカタカナ表記">要因B：レガシーCSSの <code>width: auto !important;</code> と18文字のカタカナ表記</h4>
<ul>
<li><strong>現象</strong>：日本語のダウンロードボタン（<code>スロットバックアップをダウンロード</code>）が白いカードの枠線を50px以上突き抜けてはみ出る。</li>
<li><strong>原因</strong>：過去のCSSに定義されていた<code>.setting-control .btn-primary { width: auto !important; }</code>が、HTML上の<code>w-full</code>クラスを強制的に上書きしていました。韓国語（<code>슬롯 백업 다운로드</code>、9文字）では短いため収まっていましたが、日本語のカタカナでは18文字と2倍に長くなったため、ボタンの固有幅が320pxを超えてしまい、260pxのサイドバー幅を突き破ったのです。</li>
<li><strong>解決策</strong>：CSSセレクタを<code>.setting-control .btn-primary:not(.w-full)</code>へ修正して<code>!important</code>の束縛を解除し、インラインで<code>style=&quot;width: 100% !important; max-width: 100%; white-space: normal; word-break: break-word;&quot;</code>を付与。長いテキストであっても枠幅100%に収まり、内部で安全に複数行へ折り返されるようにしました。</li>
</ul>
<h3 id="3-スロット初期表示名のリアルタイム動的翻訳getslotdisplayname">3) スロット初期表示名のリアルタイム動的翻訳（<code>getSlotDisplayName</code>）</h3>
<p>スロット1・2・3の初期名称（「디자인 슬롯 1」、「미니멀 1열」、「다크 모던」）はDB初期構築時に韓国語文字列で保存されているため、管理画面の言語を日本語や英語に切り替えても選択肢の中には韓国語がそのまま残ってしまう問題がありました。</p>
<p>ユーザーが意図して命名したカスタム名はそのまま保持しつつ、初期スロット名については現在の選択言語（<code>adminLang.value</code>）に応じてリアルタイムに翻訳して返すヘルパー関数<code>getSlotDisplayName</code>を実装しました：</p>
<ul>
<li><strong>韓国語</strong>：<code>슬롯 1 (디자인 슬롯 1)</code></li>
<li><strong>英語</strong>：<code>Slot 1 (Design Slot 1)</code></li>
<li><strong>日本語</strong>：<code>スロット 1 (デザインスロット 1)</code></li>
</ul>
<hr>
<h2 id="4-デプロイおよびマルチアカウント自動化プロジェクト名同期パイプライン">4. デプロイおよびマルチアカウント自動化：プロジェクト名同期パイプライン</h2>
<p>SKBEは、複数のCloudflareアカウントへブログや管理画面を分散デプロイできるマルチアカウント機能（<code>scripts/deploy-multi.js</code>）を備えています。</p>
<p>従来は、新規セットアップ（<code>setup.js</code>）時にプロジェクト名を手動入力したり自動乱数で生成しても、<code>package.json</code>内の基本デプロイコマンド（<code>deploy:blog</code>、<code>deploy:admin</code>）に含まれる<code>--project-name</code>引数は手動で書き換える必要がありました。</p>
<p><code>scripts/setup.js</code>および<code>scripts/sync-accounts.js</code>に<code>updatePackageJsonDeployScripts</code>処理を追加しました：</p>
<ul>
<li>自動・手動インストールを問わず、プロジェクト名が確定した時点で<code>package.json</code>内のデプロイスクリプトを自動的に更新します。</li>
<li><code>npm run deploy:sync</code>実行時にも、<code>.deploy-accounts.json</code>の<code>main</code>アカウント設定と<code>package.json</code>の内容が常に誤差なく100%同期されます。</li>
</ul>
<hr>
<h2 id="5-エンジン最適化および依存関係のセキュリティ強化">5. エンジン最適化および依存関係のセキュリティ強化</h2>
<ol>
<li><strong>サイドバースナップショットのリアルタイムイベント同期</strong>：<ul>
<li>v1.0.0.15で導入されたO(1)サイドバースナップショットキャッシュを、記事の新規投稿（<code>new</code>）、編集（<code>[id]</code>）、削除・状態変更（<code>posts</code>）時にバックグラウンドで即座に再生成（<code>generateSidebarSnapshot</code>）するよう連携しました。</li>
</ul>
</li>
<li><strong>メディア移行パスの判定拡張</strong>：<ul>
<li>本文内プロキシURLを直接配信アドレスへ一括置換する<code>/api/media/migrate-urls</code>の正規表現およびDBクエリを改善し、<code>posts/</code>配下のサブフォルダや独自階層に保存された画像も漏れなく検知・置換できるように拡張しました。</li>
</ul>
</li>
<li><strong>ライブラリのセキュリティパッチ＆Zod v4バージョン固定</strong>：<ul>
<li><code>better-auth</code>および<code>tiptap</code>関連の脆弱性修正バージョンを適用し、pnpmの<code>overrides</code>設定によって<code>zod</code>のバージョンをv4系に固定してビルドの再現性を担保しました。</li>
</ul>
</li>
</ol>
<hr>
<h2 id="6-バージョン比較まとめbefore-vs-after">6. バージョン比較まとめ（Before vs After）</h2>
<table>
<thead>
<tr>
<th align="left">比較項目</th>
<th align="left">従来（v1.0.0.15以前）</th>
<th align="left">改善後（v1.0.1.0）</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>デザインバックアップ／復元</strong></td>
<td align="left">DB全テーブル削除後の強制上書き（他スロット全滅）</td>
<td align="left"><strong>特定スロットのみを中立プリセット（<code>design_preset</code>）で安全注入</strong></td>
</tr>
<tr>
<td align="left"><strong>復元前の確認工程</strong></td>
<td align="left">復元即DB上書き（取り消し不可）</td>
<td align="left"><strong>メモリ先行反映 ➔ 画面プレビュー確認 ➔ ユーザーによる確定保存</strong></td>
</tr>
<tr>
<td align="left"><strong>サイドバーのレイアウト</strong></td>
<td align="left">横1列配置により狭い画面でボタンが見切れ・消滅</td>
<td align="left"><strong>縦並びスタック（<code>flex-col</code>、<code>w-full</code>）による100%幅ジャスト配置</strong></td>
</tr>
<tr>
<td align="left"><strong>日本語タイポグラフィ</strong></td>
<td align="left"><code>keep-all</code>により改行不能、ボタンが50px以上はみ出す</td>
<td align="left"><strong><code>overflow-wrap: anywhere</code>、ボタン内複数行折り返しで完全解消</strong></td>
</tr>
<tr>
<td align="left"><strong>スロット名の多言語化</strong></td>
<td align="left">DB保存の韓国語初期文字列が固定表示</td>
<td align="left"><strong><code>getSlotDisplayName</code>による初期スロット名の動的ローカライズ</strong></td>
</tr>
<tr>
<td align="left"><strong>デプロイコマンドの管理</strong></td>
<td align="left">プロジェクト名変更時に<code>package.json</code>の手動修正が必要</td>
<td align="left"><strong>セットアップおよび同期時に<code>package.json</code>を全自動更新</strong></td>
</tr>
</tbody></table>
<hr>
<h2 id="7-おわりに">7. おわりに</h2>
<p>今回のv1.0.1.0は、単なる機能追加にとどまらず、実際の運用において発生し得る**データ消失リスク（デザインの巻き添え上書き）**の根本的な排除と、**多言語環境における細やかな表示品質（日本語の禁則処理やボタンの整列）**を徹底して磨き上げた安定化リリースです。</p>
<p>SKBEは今後もCloudflareのエッジアーキテクチャの強みを最大限に活かしながら、あらゆる言語環境において安全で快適なブログ運営環境を提供してまいります。</p>
]]></content:encoded>
            <category>開発ログ</category>
        </item>
        <item>
            <title><![CDATA[SKBE v1.0.0.15 アップデート：D1スナップショットエンジン、ストレージURL移行、SEO最適化]]></title>
            <link>https://testblog-6br.pages.dev/ja/devlog/skbe-engine-optimization-v15-guide</link>
            <guid isPermaLink="true">https://testblog-6br.pages.dev/ja/devlog/skbe-engine-optimization-v15-guide</guid>
            <pubDate>Sun, 20 Sep 2026 06:29:57 GMT</pubDate>
            <description><![CDATA[SvelteKit Blog Engine(SKBE) v1.0.0.15の改善内容をまとめました。D1複合クエリをO(1)に削減するサイドバースナップショットエンジン、Cloudflare Workers無料枠を守る本文画像URL同期パイプライン、再デプロイ不要のタグnoindex管理、およびSEO正規化のプロセスを紹介します。]]></description>
            <content:encoded><![CDATA[<h2 id="1-はじめに実際の運用で見えてきた構造的な課題">1. はじめに：実際の運用で見えてきた構造的な課題</h2>
<p>SvelteKit Blog Engine（SKBE）は、v1.0.0.11からv1.0.0.14にかけてマルチスロットデザインシステムの導入やレイアウトシフト問題（CLS 0.000達成）の解消を進め、基盤を安定化させてきました。しかし、実際に記事を執筆し、実環境でサイトを動かしていく中で、いくつかの構造的な非効率や運用の手間が浮き彫りになってきました。</p>
<p>本プロジェクトは、Cloudflareの無料枠（Pages、D1 SQLite、KV）を活用し、月額コスト0円での持続可能なサーバーレス運用を前提に設計されています。そのため、わずかなクエリの浪費や運用上の小さなストレスであっても、早期に排除しておくことが長期的な安定性に直結します。</p>
<p>今回のv1.0.0.15では、主に以下の点に着目して改善を実施しました：</p>
<ol>
<li><strong>サイドバーウィジェットの重複クエリ削減</strong>：ページ遷移のたびにカテゴリ、最新記事、人気記事データをD1へ都度問い合わせていた構成の刷新</li>
<li><strong>Workers呼び出し量の削減と画像配信の最適化</strong>：本文内のプロキシ経由アドレス（<code>/images/...</code>）をR2やSupabaseのダイレクト配信エンドポイントへ一括置換し、Cloudflare Workersの1日あたりの無料枠（10万リクエスト/日）の枯渇を防止</li>
<li><strong>レイアウトレンダラーのマークアップ整理</strong>：検索エンジンのクローラーがメインコンテンツと補助領域（サイドバー）を明確に判別できるよう、HTML5セマンティックタグを適用</li>
<li><strong>検索エンジン向けメタデータ補正とタグnoindexの動的制御</strong>：サイトマップやOpen Graph規格を整備し、タグ一覧ページの検索インデックス許可／拒否を再デプロイなしで管理画面から切り替えられる設定を追加</li>
</ol>
<hr>
<h2 id="2-d1読み込みクエリの削減サイドバースナップショットの導入">2. D1読み込みクエリの削減：サイドバースナップショットの導入</h2>
<h3 id="1-課題の背景">1) 課題の背景</h3>
<p>グローバルレイアウト（<code>+layout.server.ts</code>）では、どのページを表示する場合でも、サイドバーに配置するカテゴリ一覧、最新記事、人気記事のデータを準備する必要があります。</p>
<p>従来の設計では、ページリクエストが発生するたびにD1データベースに対してこれら3つのクエリを毎回発行していました。アクセス数やPVが増加するにつれてD1の読み込みクエリが無駄に消費され、エッジとデータベース間の往復レイテンシも蓄積していました。</p>
<h3 id="2-実装内容sidebar-snapshotによる静的配信">2) 実装内容：<code>sidebar_snapshot</code>による静的配信</h3>
<p><code>packages/shared/src/utils/snapshot.ts</code>を新設し、カテゴリ、最新記事、人気記事のデータをあらかじめ1つのJSONオブジェクトにまとめ、<code>blog_settings</code>テーブルの<code>sidebar_snapshot</code>フィールドにキャッシュしておく構造に変更しました。</p>
<pre><code class="language-typescript">// apps/blog/src/routes/+layout.server.ts
const sidebarSnapshot = safeParse(settings?.sidebar_snapshot);
const hasSnapshot = sidebarSnapshot &amp;&amp; typeof sidebarSnapshot === &#39;object&#39; &amp;&amp; sidebarSnapshot.recentPosts;

if (hasSnapshot) {
    // スナップショットが存在すれば、DBへの複合クエリなしで即座にデータを返却
    categories = sidebarSnapshot.categories?.[currentLang] || sidebarSnapshot.categories?.[defaultLang] || [];
    recentPosts = rLimit ? rawRecent.slice(0, rLimit) : rawRecent;
    popularPosts = pLimit ? rawPopular.slice(0, pLimit) : rawPopular;
    
    // 更新頻度が高いタグやコメント等のウィジェットのみ、必要に応じて軽量に取得
} else {
    // スナップショットがない場合は1回取得したのち、バックグラウンドでスナップショットを生成
    generateSidebarSnapshot(rawD1).catch(err =&gt; console.error(&#39;[Snapshot Background]&#39;, err));
}
</code></pre>
<ul>
<li><strong>効果</strong>：一般の訪問者が記事を閲覧する際、サイドバー用のD1クエリは一切発生せず、$O(1)$で即座にレンダリングされます。</li>
<li>記事の新規作成や編集時にはバックグラウンドの非同期処理でスナップショットが更新されるため、読者のレスポンス速度を損なうことはありません。</li>
</ul>
<hr>
<h2 id="3-cloudflare-workers呼び出し量の削減本文画像url同期ツール">3. Cloudflare Workers呼び出し量の削減：本文画像URL同期ツール</h2>
<h3 id="1-背景画像プロキシ配信の限界とストレージ選定のジレンマ">1) 背景：画像プロキシ配信の限界とストレージ選定のジレンマ</h3>
<p>SKBEは、ImageKit、Cloudflare R2、Supabase Storage、Cloudflare KVなど、複数のメディアバックエンドに対応しています。</p>
<p>技術的・アーキテクチャの観点だけで見れば、同じCloudflareエコシステム内にあり、帯域幅（Egress）コストが完全に無料である<strong>Cloudflare R2が最も理想的</strong>です。しかし、R2を有効化するにはCloudflareアカウントに<strong>クレジットカードの登録が必須</strong>となります。R2は転送量こそ無料ですが、保存容量（10GB）やリクエスト回数の無料枠を超過した場合は、登録されたカードから自動的に後払い課金される仕組みになっています。</p>
<p>一方で<strong>ImageKitは、クレジットカード登録なしで無料プランを利用できます</strong>。**3GBのメディア保存領域と、毎月20GBの無料グローバルCDN帯域（2026年9月執筆時点）*<em>が提供されており、万が一上限に達した場合でも自動でカード決済されることはなく、サービスが一時停止する仕様であるため、想定外の課金リスクが一切ありません。</em>（※各クラウド・ストレージサービスの無料枠ポリシーは、提供元の規約改定により変更される場合があります）*</p>
<p>そのため個人的には、<strong>「まずはクレジットカード登録不要で安全なImageKitからスタートし、ブログが成長して無料枠（保存容量3GBまたは月間20GB帯域）に近づいた段階でCloudflare R2へ移行する」というロードマップを最も推奨します</strong>。今回実装した本文画像URL同期ツールは、まさにこのようなストレージ移行のタイミングにおいて、既存記事に含まれる画像アドレスをボタン1つでスムーズに切り替える役割を果たします。</p>
<p>ただしどのストレージを採用する場合でも、初期設計の単純化のためにすべての画像を自前エンドポイント（<code>/images/...</code>）経由のプロキシで配信していた際、深刻なボトルネックが発生しました。複数の画像を含む記事を閲覧するたび、<strong>画像の枚数分だけCloudflare Workerが毎回起動し、1日あたり10万リクエストという無料枠を急激に消費してしまっていた</strong>のです。</p>
<p>この問題を解決するため、ImageKitやR2のカスタムドメイン、Supabaseの公開URLといったダイレクト配信モードを導入し、Workerを経由させずにCDNやストレージが直接トラフィックを処理できるようにしました。しかし、ここでもう1つの現実的な問題が残りました。<strong>それ以前に投稿された既存記事の本文HTML（<code>&lt;p&gt;&lt;img src=&quot;/images/...&quot;&gt;&lt;/p&gt;</code>）には依然としてプロキシ経由のパスが書き込まれており</strong>、ダイレクト配信モードを有効にしても過去記事が読まれるたびに不要なWorkerリクエストが発生し続けていたのです。</p>
<p>また、ストレージの移行やバックアップ復元を行った際にも古いパスがそのまま残り、画像が表示されなくなったり無駄なプロキシ呼び出しが継続したりする原因になっていました。</p>
<blockquote>
<p>⚠️ <strong>重要な制約事項：デフォルトのCloudflare KV利用時はURLの書き換え不可</strong><br>Cloudflare KVはパブリックな直接リンク（Public Direct URL）を提供していません。したがって、<strong>標準のKVストレージを利用している環境では、ダイレクト配信への切り替えや本文URLの書き換えは適用できず、必ずドメインプロキシ（<code>/images/...</code>）経由での配信となります</strong>。<br>本文画像URL同期ツールによってWorkersの呼び出し回数を削減するには、ImageKit（推奨）やCloudflare R2、Supabaseといったオブジェクトストレージへの移行が必要です。</p>
</blockquote>
<h3 id="2-実装内容本文画像url一括マイグレーションツール">2) 実装内容：本文画像URL一括マイグレーションツール</h3>
<p>過去記事への画像リクエストがWorkerを迂回し、最適なダイレクト配信ルートを通るよう、URL置換パイプラインを構築しました：</p>
<ul>
<li><p><strong>マイグレーション用APIエンドポイントの新設</strong>（<code>apps/admin/src/routes/api/media/migrate-urls/+server.ts</code>）：<br>公開済みの全記事を巡回し、プロキシパス（<code>/images/...</code>）や以前のストレージURLパターンを正規表現で検出。現在アクティブなストレージ（R2カスタムドメイン、Supabaseダイレクト配信等）のエンドポイントアドレスへ一括置換して保存します。</p>
</li>
<li><p><strong>管理画面へのUI配置</strong>（<code>apps/admin/src/routes/media/+page.svelte</code>）：<br>メディアストレージ設定タブの最下部に「記事内画像URL同期ツール」を配置し、配信モードを切り替えた後にボタンを1回クリックするだけで、データベース内の全画像URLを即座にダイレクトパスへ一括更新できるようにしました。</p>
</li>
<li><p><strong>バックアップ復元との連携</strong>：<br>バックアップファイルを復元する際にも「本文画像URLの自動マイグレーション」オプションを設け、復元と同時に現在の稼働環境に合わせたダイレクト配信アドレスへ自動再構成されるよう設計しました。</p>
</li>
<li><p><strong>効果</strong>：過去記事を閲覧した際に発生していた画像配信用のWorker呼び出しが完全にゼロになり、読者はストレージやCDNから直接画像をダウンロードするため、Workersの1日無料枠を安全に保護できるようになりました。</p>
</li>
</ul>
<h3 id="3-安全なマイグレーションのための推奨手順">3) ⚠️ 安全なマイグレーションのための推奨手順</h3>
<p>本文内の画像URLを一括置換する処理は、データベース内の記事データを直接書き換える操作です。現実的にあらゆる環境や例外パターンに対して100%完璧に対応できると過信することは危険です。そのため、マイグレーションを実施する際は必ず以下の手順を踏むことを推奨します：</p>
<ol>
<li><strong>マイグレーション前の全体バックアップ必須</strong>：<br>マイグレーションを実行する前に、管理画面のバックアップ機能を利用して、<strong>D1データベースとメディアファイル全体を必ずローカル環境へバックアップ</strong>してください。</li>
<li><strong>ロールバック（原状復帰）への備え</strong>：<br>万が一URLの置換ミスやリンク切れが発生した場合でも即座に復元できるよう、<strong>旧画像サーバーのデータは事前に削除せず、そのまま残した状態で作業を進めてください</strong>。</li>
<li><strong>テスト用複製ブログでの事前検証（強く推奨）</strong>：<br>最も安全で確実な方法は、<strong>バックアップしたデータを用いて検証用の複製ブログを別途立ち上げ、そこで先にマイグレーションをテストしてみること</strong>です。テスト環境で本文の画像が欠落なく正常に表示されることを目視確認した上で、本番ブログに適用することを強くお勧めします。</li>
</ol>
<hr>
<h2 id="4-レイアウトレンダラーのセマンティックタグ整理">4. レイアウトレンダラーのセマンティックタグ整理</h2>
<p>従来の<code>LayoutRenderer.svelte</code>では、カラムや各ウィジェットのコンテナ要素がほぼすべて汎用的な<code>div</code>タグで構成されていました。画面の見た目上は問題ありませんでしたが、検索エンジンのクローラーがメインコンテンツと補助領域（サイドバー）をより的確に識別できるよう、HTML5セマンティックタグへと再編しました：</p>
<ul>
<li>本文ウィジェット（<code>post_content</code>）を含まないカラムは、補助的なコンテンツであることを明示するために<code>&lt;aside class=&quot;layout-column sidebar-column&quot;&gt;</code>で囲む構成に改めました。</li>
<li>本文を含むカラムは<code>&lt;div class=&quot;layout-column main-column&quot;&gt;</code>として明確に分離しました。</li>
<li>各ウィジェットブロックには<code>&lt;section class=&quot;widget-item ...&quot;&gt;</code>を割り当て、適切な<code>&lt;h3&gt;</code>見出しを付与しました。</li>
</ul>
<hr>
<h2 id="5-検索エンジンseo向けメタデータの標準化">5. 検索エンジン（SEO）向けメタデータの標準化</h2>
<p>本番環境における実際のクローラー収集状況を確認し、規格面で不足していた細かな点を網羅的に改修しました：</p>
<ol>
<li><strong>サイトマップのトップページ<code>&lt;lastmod&gt;</code>タグ補完</strong>（<code>sitemap.xml/+server.ts</code>）：<br>個別記事のURLには最終更新日が含まれていましたが、ルート（<code>/</code>）エントリから<code>&lt;lastmod&gt;</code>が漏れていたため、最新記事の更新日時を反映するよう修正しました。</li>
<li><strong>サイドバーのタグリンクのエンコード処理</strong>（<code>TagCloudWidget.svelte</code>）：<br>スペース等を含むタグが未加工の文字列のままリンクされていた箇所に<code>encodeURIComponent</code>を適用しました。</li>
<li><strong>トップページの<code>og:image</code>フォールバック</strong>（<code>[[lang=lang]]/+page.server.ts</code>）：<br>管理画面でカスタムロゴを設定していない場合、SNS共有時にサムネイルが表示されなかった問題を解消し、最新記事のアイキャッチ画像（<code>lcpImage</code>）が自動で適用されるようにしました。</li>
<li><strong><code>og:locale</code>の多言語標準化</strong>（<code>SeoHead.svelte</code>）：<br>言語コードが<code>en</code>や<code>ja</code>として出力されていた箇所を、Open Graphの標準仕様である<code>en_US</code>および<code>ja_JP</code>へマッピングして出力するように分岐を追加しました。</li>
</ol>
<hr>
<h2 id="6-タグ一覧ページのnoindex管理機能と多言語辞書対応">6. タグ一覧ページのnoindex管理機能と多言語辞書対応</h2>
<h3 id="1-課題の背景-1">1) 課題の背景</h3>
<p>ブログの開設初期は記事数が少なく、タグごとの一覧ページに同一または酷似した記事リストが表示されがちです。これにより検索エンジンから重複コンテンツ（低品質ページ）と判定されるリスクを避けるため、開設初期のタグ一覧ページは<code>noindex</code>にしておくのがSEO上の定石です。</p>
<p>ただし、サイトが成長してタグごとに十分な記事数が蓄積された段階でインデックスを許可したい場合、従来はソースコードを修正して再デプロイする必要がありました。</p>
<h3 id="2-実装内容">2) 実装内容</h3>
<ul>
<li><strong>管理画面のサイト設定にトグルスイッチを追加</strong>（<code>apps/admin/src/routes/settings/+page.svelte</code>）：<br>「タグページの検索エンジンインデックス遮断（noindex）」項目を新設。ONでnoindex（遮断）、OFFでindex（登録許可）となるようにしました。デフォルト値はONです。</li>
<li><strong>ブログフロントエンドのリアルタイム連動</strong>（<code>apps/blog/src/routes/[[lang=lang]]/tags/[tag]/+page.server.ts</code>）：<br>ハードコードされていた<code>noindex: true</code>を管理画面の設定値（<code>settings?.tag_page_noindex !== &#39;false&#39;</code>）に置き換え、再デプロイ不要で設定保存と同時に挙動が切り替わるようにしました。</li>
<li><strong>i18n多言語辞書への登録</strong>：<br>項目のタイトルや説明文を<code>packages/shared/src/i18n/index.ts</code>の共通辞書に登録し、日本語・英語・韓国語の各言語で適切に表示されるようにしました。</li>
</ul>
<hr>
<h2 id="7-おわりに">7. おわりに</h2>
<p>今回のv1.0.0.15におけるアップデートは、見た目の派手な新機能を追加することよりも、実際にサービスを運用する中で直面する細かな非効率や管理上のストレスを解消することに重点を置きました。</p>
<p>データベースへの不要な往復クエリを削り、ストレージ移行時の手戻りを防ぎ、検索エンジンの仕様をきれいに整えるだけでも、日々の運用負荷は格段に軽くなります。今後も実際の現場運用に根ざした改善を着実に積み重ねていく予定です。</p>
]]></content:encoded>
            <category>開発ログ</category>
        </item>
        <item>
            <title><![CDATA[SvelteKit Blog Engineの構造改善記録：マルチスロットデザイン、D1キャッシュ最適化、SEO正規化 (v1.0.0.11 〜 v1.0.0.13)]]></title>
            <link>https://testblog-6br.pages.dev/ja/devlog/skbe-engine-optimization-v11-v13-guide</link>
            <guid isPermaLink="true">https://testblog-6br.pages.dev/ja/devlog/skbe-engine-optimization-v11-v13-guide</guid>
            <pubDate>Tue, 08 Sep 2026 02:58:10 GMT</pubDate>
            <description><![CDATA[SvelteKit Blog Engine(SKBE)のv1.0.0.11からv1.0.0.13にかけて実施した設計改善を総括します。マルチスロットテーマ構造、D1読み込み98%削減キャッシュ、モバイルCLS 0.000対策、SEOカノニカル正規化、そして多言語llms.txtの導入過程を詳しく解説します。]]></description>
            <content:encoded><![CDATA[<h2 id="1-はじめに本番運用で見えてきた構造的な課題">1. はじめに：本番運用で見えてきた構造的な課題</h2>
<p>SvelteKit Blog Engine（SKBE）v1.0.0.10のリリース後、実際の運用環境で検証を進める中で、ローカル開発時には見落としていた構造的なボトルネックや改善点がいくつか浮き彫りになってきました。</p>
<p>本プロジェクトは、<strong>Cloudflareの無料枠（Pages、D1 SQLite、KV）を活用し、月額コスト0円（$0）で稼働するサーバーレスアーキテクチャ</strong>を前提に設計されています。そのため、トラフィックやデータ量が増加しても無料枠の制限内で安定してサービスを継続できるかどうかが、エンジニアリングにおける最大の焦点でした。</p>
<p>具体的に向き合うべき課題は次の3点でした：</p>
<ol>
<li><strong>D1データベースのクエリ効率化</strong>：Cloudflare D1の1日あたりの無料書き込み上限（10万行/日）と、頻繁に発生する読み込みクエリをいかに削減するか？</li>
<li><strong>モバイル表示の安定性</strong>：JavaScriptのマウント後にレイアウトを判定していたことで生じる、モバイル画面の微小な累積レイアウトシフト（CLS）をどう解消するか？</li>
<li><strong>検索エンジン（SEO）と次世代AIクローラーへの標準対応</strong>：多言語ルーティングにおけるcanonical URLとhreflangの矛盾を正し、ChatGPT SearchやPerplexityといった最新AIクローラーへ正しい規格を提供できているか？</li>
</ol>
<p>これらの課題を解決するため、v1.0.0.11からv1.0.0.13にかけて段階的な改善を実施しました。本記事では、その過程で検討した設計と具体的な実装内容をまとめました。</p>
<hr>
<h2 id="2-v10011-安定性の強化adsenseの描画崩れとランタイムエラー対応">2. [v1.0.0.11] 安定性の強化：AdSenseの描画崩れとランタイムエラー対応</h2>
<p>最初のステップとして、収益化に関わる広告枠のレイアウト崩れと、特定パラメータの流入時に発生していたHTTP 500エラーの解消に着手しました。</p>
<h3 id="デスクトップ環境におけるadsenseブランク表示の防止">デスクトップ環境におけるAdSenseブランク表示の防止</h3>
<p>モバイルでは正常に表示されていたGoogle AdSenseのレスポンシブユニットが、デスクトップの特定画面幅において高さ0pxで描画され、空白として残ってしまう事象が確認されました。</p>
<ul>
<li><strong>原因</strong>：CSS Gridトラックのレスポンシブ計算タイミングと、AdSenseスクリプト（<code>adsbygoogle.push</code>）が親コンテナの横幅を測定するタイミングの間にわずかな競合が発生していました。</li>
<li><strong>対処</strong>：広告ラッパー要素に対して<code>min-height: 280px</code>とコンテナスタイルを指定し、スクリプト実行前でも安定した描画領域が確実に確保されるよう構造を補正しました。</li>
</ul>
<h3 id="特殊文字タグおよび多言語ルーティングの安定化">特殊文字タグおよび多言語ルーティングの安定化</h3>
<p>特殊文字やスペースを含むタグページへアクセスした際、SvelteKitのルートマッチャーとD1 Drizzle ORMのクエリバインディング間でエンコードの不一致が起き、500エラーが発生していた不具合を修正しました。</p>
<hr>
<h2 id="3-v10012-構造の大幅改善柔軟なデザイン管理とd1の負荷軽減">3. [v1.0.0.12] 構造の大幅改善：柔軟なデザイン管理とD1の負荷軽減</h2>
<p>v1.0.0.12では、デザイン管理の自由度を高めつつ、Cloudflare D1の読み込み・書き込み負荷を大幅に引き下げるためのアーキテクチャ刷新を行いました。</p>
<h3 id="1-3スロット・マルチデザインシステムmulti-slot-design-architecture">1) 3スロット・マルチデザインシステム（Multi-Slot Design Architecture）</h3>
<p>一般的なブログエンジン（WordPress、Ghostなど）でデザインを刷新する際、テーマを切り替えると既存の設定が上書きされてしまうため、バックアップの手間がかかるだけでなく、<strong>「稼働中の本番サイトで訪問者に崩れたレイアウトを見せることなく、リアルタイムに新デザインを検証する」ことが極めて困難</strong>でした。</p>
<p>SKBE v1.0.0.12では、この課題を根本から解決するために**「スナップショット型3スロットデザイン設計」**を採用しました。</p>
<ul>
<li><p><strong>3つの独立したデザインスナップショットを同時保持</strong>：</p>
<ul>
<li>管理画面のデザインエディタにて、**スロット1（デフォルト・モダン）、スロット2（リデザイン検証用）、スロット3（ダーク/イベント用）**をそれぞれ完全に独立したスナップショットとして保存・管理できます。</li>
<li>スロット2で新デザインを作成しても、現在公開中のスロット1には一切影響がありません。完成後はワンクリックで公開スロットを切り替えることができ、トラブル時も即座に元のデザインへロールバックが可能です。</li>
</ul>
</li>
<li><p><strong>運用方針に応じた3つの配信モード</strong>：</p>
<ul>
<li><strong>単一スロット固定</strong>：管理者が指定した特定のスロットのみを公開</li>
<li><strong>セッション別ランダムローテーション</strong>：訪問者のセッションごとにスロットをランダムに切り替えて配信（A/Bテストや訪問体験の刷新に有効）</li>
<li><strong>訪問者によるリアルタイムテーマ切り替え</strong>：画面右下のフローティングボタンから、訪問者自身が好みのテーマを選択可能</li>
</ul>
</li>
<li><p><strong>リソースの無駄を排除した条件付きSSRペイロード設計</strong>：</p>
<ul>
<li>テーマを3つ保持しているからといって、ページ読み込みが重くなっては本末転倒です。</li>
<li>訪問者テーマ選択モードがオフの場合、<code>+layout.server.ts</code>は<strong>アクティブな1スロット分のCSS/設定データのみをHTMLに同梱</strong>し、副スロットのデータは一切送信しません。</li>
<li>訪問者切り替え機能がオンの時だけ条件付きで副スロットデータを同梱することで、マルチテーマの柔軟性を備えながらも、単一テーマ運用と全く変わらない超軽量な初期表示速度を維持しています。<br><img src="https://sveltekitblog.com/images/posts/skbe-engine-optimization-v11-v13-guide/desktop/img-devlog-skbe-engine-optimization-v11-v13-guide-ja-001.webp" alt="img-devlog-skbe-engine-optimization-v11-v13-guide-ja-001"></li>
</ul>
</li>
</ul>
<h3 id="2-cloudflare-d1インメモリttlキャッシュと閲覧数バッファリング">2) Cloudflare D1インメモリTTLキャッシュと閲覧数バッファリング</h3>
<p>Cloudflare D1の無料枠を無駄なく活用するため、Worker内部でのインメモリキャッシュとバッチ処理を導入しました。</p>
<ul>
<li><strong>インメモリTTLキャッシュ（<code>cache.ts</code>）</strong>：ブログ基本設定、レイアウト情報、タグ一覧など、更新頻度の低いデータに60秒TTLのインメモリキャッシュを適用しました。これにより、初回アクセス時に発生するD1の読み込みクエリ（<code>rows_read</code>）を<strong>98%以上削減</strong>しました。</li>
<li><strong>閲覧数バッチバッファリング（<code>viewBuffer.ts</code>）</strong>：記事が閲覧されるたびにD1へ直接<code>UPDATE</code>クエリを発行すると、1日10万行の無料書き込み枠を急速に消費してしまいます。そこで、<strong>10件蓄積されるか30秒が経過した時点</strong>でまとめてD1に反映するインメモリバッファを構築しました。ユーザーの画面にはバッファ内の保留カウントを即座に加算して返すため、表示の遅延は一切ありません。</li>
<li><strong>非正規化カウンターカラムの採用</strong>：記事一覧取得時の重い集計（COUNTやJOIN）を回避するため、カテゴリに<code>post_count</code>、記事に<code>view_count</code>カラムを新設し、記事の作成・更新・削除時に自動同期される設計へと改めました。</li>
</ul>
<h3 id="3-モバイルcore-web-vitalsの最適化cls-0000達成">3) モバイルCore Web Vitalsの最適化（CLS 0.000達成）</h3>
<ul>
<li><strong>モバイルでのレイアウトシフト完全防止（CLS 0.000）</strong>：従来はJavaScriptのマウント後に画面幅を判定してビューを切り替えていましたが、これを純粋なCSSメディアクエリ（<code>@media (max-width: 768px)</code>）による制御へ全面移行しました。ブラウザが最初のフレーム（0.001秒）からモバイルレイアウトを確定するため、<strong>CLSスコアを1.0（不良）から0.000（満点）へと改善</strong>しました。</li>
<li><strong>多言語辞書の平坦化（HTMLサイズ70%削減）</strong>：フロントエンドには不要な約900個の管理画面専用翻訳キーを排除し、アクセス言語の単一文字列へと辞書データをフラット化しました。辞書サイズが<strong>179 KBから9.19 KBへと94.87%削減</strong>され、SSR HTML全体の容量も70%以上軽量化されました。</li>
<li><strong>Webフォントの非同期Preload</strong>：<code>&lt;head&gt;</code>内のWebフォント読み込みを<code>rel=&quot;preload&quot;</code> + <code>onload</code>方式に変更し、描画ブロック（Render-blocking）を解消してFCP（First Contentful Paint）の速度を半減（4.6秒 ➔ 2.0秒）させました。</li>
</ul>
<hr>
<h2 id="4-v10013-web標準の完備seo正規化とllmstxt">4. [v1.0.0.13] Web標準の完備：SEO正規化とllms.txt</h2>
<p>第3段階では、検索エンジンによるインデックスの整合性を整え、最新のAIクローラーに向けた標準エンドポイントを実装しました。</p>
<h3 id="1-seoカノニカルcanonicalurlの正規化">1) SEOカノニカル（Canonical）URLの正規化</h3>
<p>多言語ルーティング（<code>[[lang=lang]]</code>）において、デフォルト言語でアクセスした際にcanonicalメタタグへデフォルト言語プレフィックス（例：<code>/ko</code>）が付与されてしまい、<code>sitemap.xml</code>や<code>hreflang</code>タグと矛盾する問題が生じていました。</p>
<ul>
<li><strong>デフォルト言語プレフィックスの自動除外</strong>：システムで規定されたデフォルト言語でアクセスした場合、トップページ（<code>/</code>）、カテゴリ（<code>/tech</code>）、CMSページともにcanonical URLからデフォルト言語プレフィックスを自動除外するように改修し、サイトマップと完全に一致させました。</li>
<li><strong>絶対パスフォールバックの保証</strong>：個別記事およびゲストブックにおいて、管理画面の<code>siteUrl</code>が未設定の場合に相対パスが出力されてしまう例外を点検し、常に<code>url.origin</code>を参照して有効な絶対パスを出力するフォールバックを組み込みました。</li>
</ul>
<h3 id="2-閲覧数の10分重複防止とrfc-6265準拠キー">2) 閲覧数の10分重複防止とRFC 6265準拠キー</h3>
<ul>
<li><strong>リロード乱発対策</strong>：ブラウザの<code>sessionStorage</code>に加え、サーバー側でのCookie検証（10分ウィンドウ）を追加し、連続リロードやタブ複製による意図しないカウント増加を防止しました。</li>
<li><strong>非ASCIIスラッグでの500エラー解消</strong>：日本語や韓国語などのマルチバイト文字を含むスラッグがCookie名に直接渡された際、HTTPヘッダー規格違反（<code>TypeError: argument name is invalid</code>）で500エラーを吐く現象を解決しました。スラッグをRFC 6265準拠の短い英数字ハッシュ（<code>skbe_v_${hash}</code>）に変換することで安全にキーを発行します。</li>
</ul>
<h3 id="3-多言語対応の動的llmstxtエンドポイント構築">3) 多言語対応の動的<code>llms.txt</code>エンドポイント構築</h3>
<p>ChatGPT SearchやPerplexity、Claudeなど、大規模言語モデル（LLM）を活用した検索ボットのインデックスを支援するため、<strong><a href="https://llmstxt.org">llmstxt.org</a>規格のエンドポイント</strong>を実装しました。</p>
<ul>
<li><strong>言語別ルート分岐</strong>：<code>/llms.txt</code>（デフォルト言語）、<code>/en/llms.txt</code>（英語）、<code>/ja/llms.txt</code>（日本語）を提供します。</li>
<li><strong>標準Markdown出力</strong>：サイト概要、公開カテゴリ、最新記事30件（要約付き）、固定ページ、RSS/サイトマップリンクをMarkdown形式で動的生成します。</li>
<li><strong>Cloudflare CDNエッジキャッシュ</strong>：10分間のCDNキャッシュ（<code>s-maxage=600</code>）を設定し、モバイルのGoogle PageSpeed Insights監査で発生していたタイムアウト警告（<code>Fetch of llms.txt timed out</code>）を解消しました。</li>
</ul>
<hr>
<h2 id="5-主要な改善指標まとめ">5. 主要な改善指標まとめ</h2>
<p>3つのバージョンを通じて達成された主なパフォーマンス指標の比較は以下の通りです：</p>
<table>
<thead>
<tr>
<th align="left">項目</th>
<th align="left">改善前</th>
<th align="left">改善後</th>
<th align="left">効果</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>D1読み込みクエリ</strong></td>
<td align="left">ページアクセスごとに直接DB参照</td>
<td align="left">60秒インメモリTTLキャッシュ適用</td>
<td align="left"><strong>読み込み量98%以上削減</strong></td>
</tr>
<tr>
<td align="left"><strong>D1書き込み頻度</strong></td>
<td align="left">記事閲覧ごとに即時DB Write</td>
<td align="left">10件 / 30秒バッチバッファリング</td>
<td align="left"><strong>1日の無料書き込み枠を保護</strong></td>
</tr>
<tr>
<td align="left"><strong>モバイルCLS</strong></td>
<td align="left">1.0（JSマウント後のレイアウトシフト）</td>
<td align="left"><strong>0.000（純粋なCSSメディアクエリ）</strong></td>
<td align="left"><strong>初期表示の画面揺れゼロ</strong></td>
</tr>
<tr>
<td align="left"><strong>翻訳辞書データサイズ</strong></td>
<td align="left">179 KB（全言語キー同梱）</td>
<td align="left"><strong>9.19 KB（アクセス言語のみ平坦化）</strong></td>
<td align="left"><strong>データ量94.87%削減</strong></td>
</tr>
<tr>
<td align="left"><strong>SSR HTMLサイズ</strong></td>
<td align="left">約80〜100 KB</td>
<td align="left"><strong>約20〜25 KB</strong></td>
<td align="left"><strong>70%以上の軽量化</strong></td>
</tr>
<tr>
<td align="left"><strong>FCP（初回コンテンツ描画）</strong></td>
<td align="left">4.6秒</td>
<td align="left"><strong>2.0秒</strong></td>
<td align="left"><strong>50%以上の高速化</strong></td>
</tr>
<tr>
<td align="left"><strong>SEO Canonical整合性</strong></td>
<td align="left">言語プレフィックスの不整合が存在</td>
<td align="left">サイトマップおよびhreflangと100%一致</td>
<td align="left"><strong>重複インデックスのリスク排除</strong></td>
</tr>
<tr>
<td align="left"><strong>AI検索対応</strong></td>
<td align="left">未対応（Lighthouse警告）</td>
<td align="left"><strong>多言語llms.txt標準完全対応</strong></td>
<td align="left"><strong>次世代AIボットの引用を最適化</strong></td>
</tr>
</tbody></table>
<hr>
<h2 id="6-おわりに開発の舞台裏とhub機能の運用について">6. おわりに：開発の舞台裏とHub機能の運用について</h2>
<h3 id="v10011からv10013までをまとめてお届けした理由">v1.0.0.11からv1.0.0.13までをまとめてお届けした理由</h3>
<p>実を言いますと、v1.0.0.11は細かなバグ修正が中心であったため単独の記事にするには内容が軽く、メジャーな変更を含むv1.0.0.12と併せて公開する予定で準備を進めていました。</p>
<p>しかし、12の作業完了直後に、非ASCIIスラッグでの閲覧数500エラーや多言語canonical URLの不整合という重要な問題（v1.0.0.13）を発覚しました。実際にエンジンをご利用いただいている方々やクローンしてくださる開発者の方々にとっては、記事を書くことよりも**「コードの不具合を解消し、GitHubへ最優先でパッチを届けること」が急務**であると判断しました。</p>
<p>緊急修正とリリースを優先して行い、システムの安定性をしっかり確保してから執筆にあたった結果、11から13までを総括する形での開発ログとなりました。</p>
<h3 id="hub機能の一時停止と再オープンについて">Hub機能の一時停止と再オープンについて</h3>
<p>現在、本ブログのHub（サブブログ/ハブ）機能は、<strong>Google AdSenseの審査対応に伴い一時的に非公開</strong>としております。審査プロセスにおけるサイト階層の整合性を維持し、クローラーの巡回ノイズを防ぐための対応です。</p>
<p>AdSense審査が完了次第、ハブ機能全体の点検とリファクタリングを終えたうえで再オープンいたしますので、楽しみにしてくださっている皆様には今しばらくお待ちいただけますと幸いです。</p>
<hr>
<p>v1.0.0.11からv1.0.0.13にかけての改善は、単に機能数を増やすことではなく、<strong>Cloudflare Free Tierという制約の中で実用的なパフォーマンス、安定性、そしてWeb標準への適合性を極限まで高めるためのプロセス</strong>でした。</p>
<p>SKBEのソースコードはすべてGitHubにて公開されています。SvelteKitを使ったサーバーレスブログの構築やパフォーマンスチューニングに関心のある方々にとって、本記事が少しでも参考になれば幸いです。</p>
]]></content:encoded>
            <category>開発ログ</category>
            <enclosure url="https://sveltekitblog.com/images/posts/skbe-engine-optimization-v11-v13-guide/desktop/img-devlog-skbe-engine-optimization-v11-v13-guide-ja-001.webp" length="0" type="image/webp"/>
        </item>
        <item>
            <title><![CDATA[SKBE v1.0.0.10 アップデート：記事言語別タブ、Hub連携強化、エディタ保護機能]]></title>
            <link>https://testblog-6br.pages.dev/ja/devlog/release-v1-0-0-10-post-management-tabs-and-hub-sync</link>
            <guid isPermaLink="true">https://testblog-6br.pages.dev/ja/devlog/release-v1-0-0-10-post-management-tabs-and-hub-sync</guid>
            <pubDate>Wed, 26 Aug 2026 06:12:18 GMT</pubDate>
            <description><![CDATA[多言語記事が増加しても言語ごとにまとめて管理できる動的タブフィルターの導入、公式ブログHub連携の安定化、エディタ書式消失防止ガードなどを実装したv1.0.0.10アップデート情報です。]]></description>
            <content:encoded><![CDATA[<h1 id="sveltekit-blog-engine-v10010-アップデートのお知らせ">SvelteKit Blog Engine v1.0.0.10 アップデートのお知らせ</h1>
<p><strong>SvelteKit Blog Engine v1.0.0.10</strong> バージョンのアップデート内容をご案内します。</p>
<p>多言語での記事投稿が増えるにつれて、管理画面で特定の言語の記事だけを絞り込んで確認したり、翻訳が未作成の言語状況を把握することが難しくなっていました。今回のバージョンでは、この点を解消する<strong>記事管理ページの動的言語タブ機能</strong>の追加、<strong>ブログHub（hub.sveltekitblog.com）連携プロトコルの改善</strong>、<strong>エディタ書式消失防止ガード</strong>、<strong>デプロイコマンドの簡素化</strong>を実施しました。</p>
<hr>
<h2 id="1-記事管理ページの言語別フィルタータブ機能">1. 記事管理ページの言語別フィルタータブ機能</h2>
<p>従来はすべての記事が作成日順に一括表示されていたため、韓国語・英語・日本語などが混在している場合の管理が煩雑でした。</p>
<p><img src="https://sveltekitblog.com/images/posts/release-v1-0-0-10-post-management-tabs-and-hub-sync/desktop/img-devlog-release-v1-0-0-10-post-management-tabs-and-hub-sync-ja-001.webp" alt="img-devlog-release-v1-0-0-10-post-management-tabs-and-hub-sync-ja-001"></p>
<h3 id="主な改善点">主な改善点</h3>
<ul>
<li><strong>動的な言語検出とカウントバッジ</strong>: 言語一覧をコードで固定せず、データベースに登録された言語と実際の記事データを総合してタブを動的に生成します。各タブの横にはリアルタイムで記事数が表示されます（例：<code>[全体 (24)]</code>, <code>[韓国語 (10)]</code>, <code>[English (8)]</code>, <code>[日本語 (6)]</code>）。</li>
<li><strong>記事未作成の言語選択時の案内</strong>: まだ記事が0件の言語タブ（例：<code>English (0)</code>）を選択した際、単に空のテーブルを表示するのではなく、親切な案内文とともに<code>[+ この言語で新規作成]</code>ボタンを表示し、スムーズな執筆動線を確保しました。</li>
<li><strong>単一言語環境への配慮</strong>: 多言語を使用せず1言語のみで運用しているブログでは、不要なタブバーを非表示にしてシンプルな画面を維持します。</li>
<li><strong>記事一覧の言語チップと言語切替時リセット</strong>: テーブルの各タイトル横に言語コード（<code>KR</code>, <code>EN</code>, <code>JA</code>など）を表示して一目で識別できるようにし、タブ切替時には自動で1ページ目にリセットされるよう処理しました。</li>
</ul>
<hr>
<h2 id="2-ブログhubhubsveltekitblogcom連携プロトコルの改善">2. ブログHub（hub.sveltekitblog.com）連携プロトコルの改善</h2>
<p>中央フィードプラットフォームであるSvelteKit Blog Hubへの記事送信時に発生していたエッジケースを整理・改善しました。</p>
<h3 id="主な修正内容">主な修正内容</h3>
<ul>
<li><strong>導入文1,500文字の標準HTML自動抽出</strong>: Markdown形式やHTML形式を問わず、フィードカードに適した1,000〜1,500文字の標準HTML導入文をサーバー側で安全に抽出して送信します。</li>
<li><strong>画像相対パスの絶対URL自動変換</strong>: 本文内の<code>/images/posts/...</code>などの相対パス画像を、代表ドメインを含む完全な絶対URL（<code>https://ドメイン/images/...</code>）に自動変換し、Hubフィードで画像が正常に表示されるようにしました。</li>
<li><strong>記事更新時の初回公開日保持</strong>: 既存記事を編集・更新した際、更新日時で上書きされず、初回公開日（<code>published_at</code>）を保持してHubタイムラインの時系列順序が崩れないようにしました。</li>
<li><strong>Hub自動送信のデフォルト有効化設定</strong>: 管理設定（<code>⚙️ 設定</code>）に「記事作成時のHub自動送信デフォルト有効化」トグルを追加し、新規作成時に毎回チェックボックスを押す手間を省けるようにしました。</li>
</ul>
<hr>
<h2 id="3-エディタ書式消失防止ガード">3. エディタ書式消失防止ガード</h2>
<p>エディタのモード切替やタイプ変更時に、作成中の内容が誤って失われないよう安全装置を追加しました。</p>
<ul>
<li><strong>HTML ➔ Visualモード切替警告</strong>: HTMLエディタでカスタムテーブルやインラインスタイルを記述した後にVisualモードへ戻ると、パーサーによって非標準タグが自動整形され削除される場合があります。これを防ぐため、切替前に確認ダイアログを表示するようにしました。</li>
<li><strong>エディタ種別（HTML ↔ Markdown）のロック</strong>: すでに保存された記事についてはデータ保護のためエディタ種別をロックし、変更を試みた際に警告メッセージでデータ損失を事前に認知できるようにしました。</li>
</ul>
<hr>
<h2 id="4-マルチアカウントデプロイdeploy-multicliオプションの簡素化">4. マルチアカウントデプロイ（deploy-multi）CLIオプションの簡素化</h2>
<p>複数インスタンスを管理する<code>deploy-multi</code>スクリプトの引数入力をより直感的に改善しました。</p>
<pre><code class="language-bash"># 従来の指定方法
npm run deploy:multi -- myaccount --admin-only

# 新しい簡素化フォーマット
npm run deploy:multi -- myaccount admin   # 管理画面のみ高速デプロイ
npm run deploy:multi -- myaccount blog    # ブログ側のみ高速デプロイ
npm run deploy:multi -- myaccount         # 両方（ブログ＋管理画面）を一括デプロイ
</code></pre>
<p><em>(従来の<code>--admin-only</code>や<code>--blog-only</code>フラグも100%下位互換性を維持しています。)</em></p>
<hr>
<h2 id="アップデート適用方法">アップデート適用方法</h2>
<p>最新のリポジトリコードを取得した上で、デプロイコマンドを実行してください。</p>
<pre><code class="language-bash"># 最新コードの取得
git pull origin main

# デプロイ実行（例）
npm run deploy:admin
# マルチアカウントデプロイの場合
npm run deploy:multi -- &lt;アカウント名&gt; admin
</code></pre>
<p>実際のブログ運用を通じて、今後も使いやすさと安定性の向上に努めてまいります。ご意見やご要望がございましたらお気軽にお寄せください。</p>
]]></content:encoded>
            <category>開発ログ</category>
            <enclosure url="https://sveltekitblog.com/images/posts/release-v1-0-0-10-post-management-tabs-and-hub-sync/desktop/img-devlog-release-v1-0-0-10-post-management-tabs-and-hub-sync-ja-001.webp" length="0" type="image/webp"/>
        </item>
        <item>
            <title><![CDATA[SvelteKitブログハブ（Hub）連携および自動配信パーフェクトガイド]]></title>
            <link>https://testblog-6br.pages.dev/ja/detail-manual/blog-hub-integration-manual</link>
            <guid isPermaLink="true">https://testblog-6br.pages.dev/ja/detail-manual/blog-hub-integration-manual</guid>
            <pubDate>Tue, 25 Aug 2026 22:28:23 GMT</pubDate>
            <description><![CDATA[SvelteKitブログハブ（hub.sveltekitblog.com）と独立ブログを連携し、トラフィックを拡大しながら投稿の作成・更新・削除ライフサイクルを安全に自動化する完全ガイドです。]]></description>
            <content:encoded><![CDATA[<h1 id="日本語-sveltekitブログハブhub連携および自動配信パーフェクトガイド">🌐 [日本語] SvelteKitブログハブ（Hub）連携および自動配信パーフェクトガイド</h1>
<p>スタンドアロンの個人ブログを運営する際、最大の課題は**「どのようにして新しい読者に記事を届けるか」**です。商用プラットフォームとは異なり、自作ブログはデータやデザインの完全な自由を享受できますが、初期のアクセス流入や検索エンジンへの露出には多くの時間と労力が必要となります。</p>
<p>**<a href="https://hub.sveltekitblog.com/">SvelteKitブログ統合ハブ（hub.sveltekitblog.com）</a>**は、独立ブログが抱える発見性（ディスカバリー）の限界を克服し、ブロガー同士がトラフィックと統計を共有しながら共に成長できるよう設計された分散型フェデレーション・フィードプラットフォームです。</p>
<p>本ガイドでは、<strong>API Keyの発行からブログ管理画面での設定、記事の作成・更新・削除ライフサイクル、そしてGoogle AdSenseとの相乗効果</strong>まで、ハブ連携の全手順を分かりやすく解説します。</p>
<hr>
<h2 id="1-ハブ連携のコア機能と仕組み">1. 🎯 ハブ連携のコア機能と仕組み</h2>
<p><img src="https://sveltekitblog.com/images/posts/blog-hub-integration-manual/desktop/img-detail-manual-blog-hub-integration-manual-ja-001.webp" alt="img-detail-manual-blog-hub-integration-manual-ja-001"></p>
<ol>
<li><strong>データの完全な独立性と所有権</strong>:<ul>
<li>記事の本文元データや全DBレコードは、自身のCloudflare D1に安全に保管されます。</li>
<li>ハブにはカード表示に必要な**標準HTML導入文（1,000〜1,500文字）**とメタデータ（タイトル、サムネイル、スラッグなど）のみが安全に送信されます。</li>
</ul>
</li>
<li><strong>画像の絶対URL自動変換</strong>:<ul>
<li>MarkdownやHTML内の相対パス画像（<code>/images/...</code>）は、自身のドメインを含む絶対URL（<code>https://...</code>）に自動変換され、ハブフィード上でリンク切れなく美しく表示されます。</li>
</ul>
</li>
<li><strong>非同期ノンブロッキング（Non-blocking）耐障害設計</strong>:<ul>
<li>ハブサーバー側でネットワーク遅延や一時的な障害が発生しても、<strong>自身のブログの記事保存トランザクションは100%正常に完了</strong>するよう安全に保護されています。</li>
</ul>
</li>
</ol>
<hr>
<h2 id="2-ステップ1ハブapi-keyの発行とブログ設定">2. 🔑 ステップ1：ハブAPI Keyの発行とブログ設定</h2>
<h3 id="ハブプラットフォームでapi-keyを発行">① ハブプラットフォームでAPI Keyを発行</h3>
<p><img src="https://sveltekitblog.com/images/posts/blog-hub-integration-manual/desktop/img-detail-manual-blog-hub-integration-manual-ja-002.webp" alt="img-detail-manual-blog-hub-integration-manual-ja-002"></p>
<ol>
<li>**<a href="https://hub.sveltekitblog.com/register-site">SvelteKitブログハブの「ブログ連携（API発行）」</a>**にアクセスします。</li>
<li>自身のブログ代表URL（例：<code>https://myblog.com</code>）を登録し、発行された固有の<strong>Site API Key</strong>（<code>sk_board_...</code> または <code>sk_hub_...</code>）をコピーします。</li>
</ol>
<h3 id="ブログ管理画面-設定への登録">② ブログ管理画面（<code>⚙️ 設定</code>）への登録</h3>
<ol>
<li>ブログ管理画面にログインし、<strong>[設定]</strong> メニューを開きます。</li>
<li><strong>SvelteKitブログハブ（Hub）連携設定</strong> セクションを確認します。<br><img src="https://sveltekitblog.com/images/posts/blog-hub-integration-manual/desktop/img-detail-manual-blog-hub-integration-manual-ja-003.webp" alt="img-detail-manual-blog-hub-integration-manual-ja-003"></li>
</ol>
<pre><code class="language-text">[ SvelteKitブログハブ(Hub)連携設定 ]
* ハブプラットフォームURL : https://hub.sveltekitblog.com（固定）
* ハブ発行 API Key : sk_hub_live_...（発行されたKeyを入力）
* 🌐 記事作成時のハブ同時配信デフォルト有効化 : [ON / OFF トグル]
</code></pre>
<ol start="3">
<li><code>記事作成時のハブ同時配信デフォルト有効化</code>をONにしておくと、新規記事作成時に自動で連携チェックが入るようになります。</li>
<li>画面下の <strong>[設定を保存]</strong> ボタンをクリックして完了します。</li>
</ol>
<hr>
<h2 id="3-ステップ2投稿エディタでのハブ同時配信">3. ✍️ ステップ2：投稿エディタでのハブ同時配信</h2>
<p>記事作成・編集画面（<code>新規投稿</code> または <code>投稿編集</code>）のメタデータサイドバー下部に、<strong>ハブ自動送信チェックボックス</strong>が用意されています。<br><img src="https://sveltekitblog.com/images/posts/blog-hub-integration-manual/desktop/img-detail-manual-blog-hub-integration-manual-ja-004.webp" alt="img-detail-manual-blog-hub-integration-manual-ja-004"></p>
<pre><code class="language-text">[☑️] 🌐 SvelteKitブログハブ（hub.sveltekitblog.com）へ自動送信
     チェックを入れると、記事公開時にハブフィードへ要約カードが自動配信されます。
     チェックを外すとフィード上で「非表示」となり、読者のいいね（推薦）統計は安全に保持されます。
     ハブから完全に削除するには、ブログ側で記事を削除してください。
</code></pre>
<ul>
<li><strong>公開記事の保存（<code>Status: Published</code>）</strong>:<ul>
<li>チェックが入った状態で保存すると、D1 DBへの保存と同時にハブへHTML導入文とサムネイルが送信され、ハブフィードに即時公開されます。</li>
</ul>
</li>
<li><strong>下書き保存（<code>Status: Draft</code>）</strong>:<ul>
<li>下書き状態の記事は、チェックが入っていてもハブフィードに公開されることはありません。</li>
</ul>
</li>
</ul>
<hr>
<h2 id="4-ステップ3記事更新・url変更・非公開・削除ライフサイクル">4. 🔄 ステップ3：記事更新・URL変更・非公開・削除ライフサイクル</h2>
<p>ブログ運営中に行われる記事の加筆修正、カテゴリ変更、非公開化、削除などの操作は、エンジンが自動検知してハブと完全に同期します。</p>
<table>
<thead>
<tr>
<th align="left">状況</th>
<th align="left">ブログ側の動作</th>
<th align="left">ハブ（Hub）同期動作</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>本文の単純修正</strong></td>
<td align="left">本文を修正して保存</td>
<td align="left"><strong>初回公開日（<code>published_at</code>）を維持</strong>したままカード内容とサムネイルのみを更新（フィード最上部へ不必要に浮上しません）</td>
</tr>
<tr>
<td align="left"><strong>スラッグ／カテゴリ変更</strong></td>
<td align="left">URLを変更して保存</td>
<td align="left">以前のURLカードを**自動削除（<code>DELETE</code>）**し、<strong>新しいURLで新規登録（<code>POST</code>）</strong>（リンク切れ404を防止）</td>
</tr>
<tr>
<td align="left"><strong>非公開化／チェック解除</strong></td>
<td align="left">下書き変更または解除</td>
<td align="left">ハブフィード上で**「非表示（<code>hidden</code>）」処理**（獲得した<strong>いいね・推薦統計は永久保持</strong>）</td>
</tr>
<tr>
<td align="left"><strong>記事の完全削除</strong></td>
<td align="left">管理画面から記事を削除</td>
<td align="left">ハブ上でも<strong>該当カードを完全削除（<code>DELETE</code>）</strong></td>
</tr>
</tbody></table>
<hr>
<h2 id="5-google-adsense審査対策と相乗効果">5. 💰 Google AdSense審査対策と相乗効果</h2>
<p>ハブ連携を通じて獲得できる読者トラフィックは、Google AdSenseの審査通過および収益化に極めて有利に働きます。</p>
<ol>
<li><strong>初期アクティブトラフィックの確保</strong>:<ul>
<li>Googleのクローラーは、訪問者がゼロのサイトよりも実際のアクティブユーザーが流入しているブログを優先的かつ高評価でインデックスします。</li>
</ul>
</li>
<li><strong><code>ads.txt</code> の完全自動配信</strong>:<ul>
<li>管理画面の <code>ads_txt</code> 入力欄にAdSense承認コード（<code>google.com, pub-..., DIRECT, f08c47fec0942fa0</code>）を入力しておくだけで、<code>https://自身のドメイン.com/ads.txt</code> として即時配信されます。</li>
</ul>
</li>
<li><strong>コンテンツ品質ガイドライン</strong>:<ul>
<li>AdSense初回審査通過のため、<strong>1,500文字以上の良質な技術・情報記事を15〜20本以上</strong>ハブと連携して継続的に投稿することをお勧めします。</li>
</ul>
</li>
</ol>
<hr>
<h2 id="6-おわりに">6. 🏁 おわりに</h2>
<p>SvelteKitブログハブ連携を活用し、自身の独立ブログが持つデータ主権を完全に守りながら、より多くの読者と繋がる強力なシナジーを体験してください！</p>
]]></content:encoded>
            <category>詳細マニュアル</category>
            <enclosure url="https://sveltekitblog.com/images/posts/blog-hub-integration-manual/desktop/img-detail-manual-blog-hub-integration-manual-ja-001.webp" length="0" type="image/webp"/>
        </item>
        <item>
            <title><![CDATA[新リリースを最も安全に反映する方法：Cloudflare Pages 複数アカウント自動デプロイ]]></title>
            <link>https://testblog-6br.pages.dev/ja/general-guide/cloudflare-pages-multi-account-deploy-guide</link>
            <guid isPermaLink="true">https://testblog-6br.pages.dev/ja/general-guide/cloudflare-pages-multi-account-deploy-guide</guid>
            <pubDate>Sun, 23 Aug 2026 13:12:24 GMT</pubDate>
            <description><![CDATA[新リリース時にテストアカウントで事前検証し、本番アカウントへ安全に反映させる方法！キャッシュ競合のないCloudflare Pagesワンクリック複数アカウントデプロイパイプラインを紹介します。]]></description>
            <content:encoded><![CDATA[<h1 id="新リリースを最も安全に反映する方法cloudflare-pages-複数アカウント自動デプロイ">🚀 新リリースを最も安全に反映する方法：Cloudflare Pages 複数アカウント自動デプロイ</h1>
<p>GitHub リポジトリに新しいリリースバージョンが公開された際、最新コードを取得してブログに反映する中で、次のような慎重な疑問が生じることがあります。</p>
<blockquote>
<p><strong>「新バージョンのエンジンコアやデータベースマイグレーションは、現在稼働中の本番ブログへ問題なく適用できるだろうか？」</strong></p>
</blockquote>
<p>SvelteKit Blog Engine では、レイアウト、カラー、フォントなどのデザイン設定や投稿記事がすべて <strong>D1 データベースに安全に保持</strong>されるため、コードを更新しても管理画面で設定したブログ UI が初期化されることはありません。</p>
<p>しかし、新バージョンで追加されたコア機能や D1 スキーマ変更、デプロイスクリプトが自身の Cloudflare 環境で正常に動作するかを事前に確認したい場合があります。その際、最も安全な方法は<strong>無料の Cloudflare 補助アカウント（テストインスタンス）へ先にデプロイして動作を確認し、問題がなければ本番アカウントへ反映すること</strong>です。</p>
<p>従来の Cloudflare ツール（Wrangler）では、アカウント切り替えのたびにブラウザログインのやり直しや設定ファイルの書き換えが必要で、ローカルキャッシュの競合による誤デプロイのリスクもありました。本記事では、そのような手間を排除し、**新リリースをテスト環境で事前検証して本番へ安全にデプロイする「複数アカウント対応ワンクリックデプロイパイプライン」**をまとめました。</p>
<hr>
<h2 id="1-実践的な安全デプロイワークフロー">🔄 1. 実践的な安全デプロイワークフロー</h2>
<p>複数アカウントデプロイシステムを活用することで、以下のような安全なデプロイサイクルを構築できます。<br><img src="https://sveltekitblog.com/images/posts/cloudflare-pages-multi-account-deploy-guide/desktop/img-general-guide-cloudflare-pages-multi-account-deploy-guide-ja-001.webp" alt="img-general-guide-cloudflare-pages-multi-account-deploy-guide-ja-001"></p>
<hr>
<h2 id="2-事前準備cloudflare-api-トークンの発行アカウント毎に初回1回">🔑 2. 事前準備：Cloudflare API トークンの発行（アカウント毎に初回1回）</h2>
<p>ブラウザポップアップなしでバックグラウンドから安全にデプロイするため、各 Cloudflare アカウントで <strong>API トークン</strong> を初回1回のみ発行しておきます。</p>
<h3 id="2-1-最小権限-api-トークンの作成手順">2-1. 最小権限 API トークンの作成手順</h3>
<ol>
<li><a href="https://dash.cloudflare.com">Cloudflare ダッシュボード</a> ログイン ➔ 右上 <strong>[マイプロファイル]</strong> ➔ <strong>[API トークン]</strong> メニューへ移動。</li>
<li><strong>[トークンを作成]</strong> をクリックし、<strong><code>Cloudflare Pages を編集</code></strong> テンプレートの <strong>[テンプレートを使用]</strong> を選択。</li>
<li><strong>アクセス許可（Permissions）</strong> セクション下部の <strong><code>+ さらに追加</code></strong> を押し、以下の2つの権限を追加します：<ul>
<li><strong><code>アカウント</code></strong> - <strong><code>D1</code></strong> - <strong><code>編集</code></strong> <em>(D1 データベーススキーマ自動マイグレーション用)</em></li>
<li><strong><code>アカウント</code></strong> - <strong><code>Workers KV Storage</code></strong> - <strong><code>編集</code></strong> <em>(画像保存用バインディング)</em></li>
</ul>
</li>
</ol>
<blockquote>
<p><strong>💡 セキュリティ Tip</strong>: グローバル API キーの代わりに、ビルド・デプロイに必要な <strong>Pages、D1、KV の3リソースのみ権限を付与する方式（最小権限の原則）</strong> が最も安全です。</p>
</blockquote>
<ol start="4">
<li><strong>アカウントリソース</strong> で対象アカウントを選択し、<strong>[概要へ進む] ➔ [トークンを作成]</strong> をクリック。</li>
<li>発行された <strong>API トークン文字列</strong> をコピーします。</li>
</ol>
<h3 id="2-2-32桁のアカウント-id-の確認">2-2. 32桁のアカウント ID の確認</h3>
<p>ダッシュボード接続時のブラウザアドレスバー(URL)から32桁の文字列を確認できます：</p>
<pre><code class="language-text">https://dash.cloudflare.com/1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d/workers-and-pages
                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                             (この部分が Account ID です)
</code></pre>
<hr>
<h2 id="3-バックアップファイルによる1秒アカウント登録-deploysync">📦 3. バックアップファイルによる1秒アカウント登録 (<code>deploy:sync</code>)</h2>
<p>D1 ID や KV ID を手動で探す必要なく、<strong>管理画面の基本機能である「デプロイ設定バックアップ」ファイル</strong>を利用して一括登録します。</p>
<ol>
<li>対象ブログの管理画面 ➔ <strong>[環境設定] ➔ [データ管理]</strong> から <strong>[デプロイ設定ダウンロード]</strong> をクリック。</li>
<li>ダウンロードしたファイル名を任意のアカウント識別子に変更し、プロジェクトルートへ配置：<ul>
<li>例（テストアカウント）：<code>wrangler.backup.test.json</code></li>
<li>例（本番アカウント）：<code>wrangler.backup.main.json</code></li>
</ul>
</li>
<li>ターミナルで同期コマンドを実行：</li>
</ol>
<pre><code class="language-bash">npm run deploy:sync
</code></pre>
<pre><code class="language-text">======================================================
✅ [.deploy-accounts.json] アカウント設定の同期完了！
======================================================
📋 [登録済みアカウント一覧]
  - main (Main Blog Account): 🟢 準備完了
  - test (Test Blog Account): 🟡 token/accountId の入力が必要です
</code></pre>
<ol start="4">
<li>生成された <code>.deploy-accounts.json</code> を開き、発行した <code>token</code> と <code>accountId</code> を入力します：</li>
</ol>
<pre><code class="language-json">{
  &quot;test&quot;: {
    &quot;name&quot;: &quot;Test Staging Account&quot;,
    &quot;token&quot;: &quot;実際のAPIトークン&quot;,
    &quot;accountId&quot;: &quot;実際の32桁アカウントID&quot;,
    &quot;blogProject&quot;: &quot;test-blog-web&quot;,
    &quot;adminProject&quot;: &quot;test-blog-admin&quot;,
    &quot;d1&quot;: {
      &quot;BLOG_DB&quot;: { &quot;name&quot;: &quot;test-blog-db&quot;, &quot;id&quot;: &quot;11111111-2222-3333-4444-555555555555&quot; },
      &quot;USER_DB&quot;: { &quot;name&quot;: &quot;test-user-db&quot;, &quot;id&quot;: &quot;66666666-7777-8888-9999-000000000000&quot; }
    },
    &quot;kv&quot;: {
      &quot;IMAGES_KV&quot;: &quot;aaaaaaaaaabbbbbbbbbbccccccccccdd&quot;
    }
  }
}
</code></pre>
<blockquote>
<p><strong>安全性の保証</strong>: <code>deploy:sync</code> は入力済みのトークンやアカウント ID を上書きせず安全に保持(Merge)します。</p>
</blockquote>
<hr>
<h2 id="4-実践デプロイシナリオ">🚀 4. 実践デプロイシナリオ</h2>
<h3 id="シナリオ-a-テストアカウントへの先行検証デプロイ推奨">シナリオ A. テストアカウントへの先行検証デプロイ（推奨）</h3>
<pre><code class="language-bash">npm run deploy:multi test
</code></pre>
<h3 id="シナリオ-b-検証完了後の本番アカウント安全デプロイ">シナリオ B. 検証完了後の本番アカウント安全デプロイ</h3>
<pre><code class="language-bash">npm run deploy:multi main
</code></pre>
<h3 id="シナリオ-c-複数ブログの一括バージョンアップ">シナリオ C. 複数ブログの一括バージョンアップ</h3>
<pre><code class="language-bash">npm run deploy:multi -- --all
</code></pre>
<h3 id="シナリオ-d-ブログ画面または管理画面の個別選択デプロイ">シナリオ D. ブログ画面または管理画面の個別選択デプロイ</h3>
<pre><code class="language-bash"># test アカウントの Blog(Web) のみデプロイ
npm run deploy:multi test blog

# test アカウントの Admin(管理画面) のみデプロイ
npm run deploy:multi test admin
</code></pre>
<hr>
<h2 id="5-セッション競合を遮断する3段階サンドボックス設計">🛡️ 5. セッション競合を遮断する3段階サンドボックス設計</h2>
<p><img src="https://sveltekitblog.com/images/posts/cloudflare-pages-multi-account-deploy-guide/desktop/img-general-guide-cloudflare-pages-multi-account-deploy-guide-ja-002.webp" alt="img-general-guide-cloudflare-pages-multi-account-deploy-guide-ja-002"></p>
<ol>
<li><strong>セッションサンドボックス</strong>: <code>APPDATA</code> を一時ディレクトリへ隔離し、ローカル PC の既存ブラウザログインセッションを保護します。</li>
<li><strong>アトミックな設定復元</strong>: デプロイの瞬間のみ対象の <code>wrangler.json</code> を適用し、完了後に <code>finally</code> ブロックで確実に元通り復元します。</li>
<li><strong>キャッシュ自動削除</strong>: デプロイ直後にローカルキャッシュを初期化するため、通常の単一デプロイコマンド（<code>npm run deploy:blog</code> 等）と併用してもセッションが混ざりません。</li>
</ol>
<hr>
<h2 id="6-コマンドリファレンスまとめ">📋 6. コマンドリファレンスまとめ</h2>
<table>
<thead>
<tr>
<th align="left">用途</th>
<th align="left">コマンド</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>アカウント設定の自動同期</strong></td>
<td align="left"><code>npm run deploy:sync</code></td>
</tr>
<tr>
<td align="left"><strong>特定アカウントのデプロイ (Blog + Admin)</strong></td>
<td align="left"><code>npm run deploy:multi &lt;アカウント名&gt;</code></td>
</tr>
<tr>
<td align="left"><strong>特定アカウントの Blog のみ選別デプロイ</strong></td>
<td align="left"><code>npm run deploy:multi &lt;アカウント名&gt; blog</code></td>
</tr>
<tr>
<td align="left"><strong>特定アカウントの Admin のみ選別デプロイ</strong></td>
<td align="left"><code>npm run deploy:multi &lt;アカウント名&gt; admin</code></td>
</tr>
<tr>
<td align="left"><strong>登録済み全アカウントの一括更新</strong></td>
<td align="left"><code>npm run deploy:multi -- --all</code></td>
</tr>
</tbody></table>
<hr>
<h2 id="7-おわりに">💡 7. おわりに</h2>
<p>管理画面でカスタマイズしたデザインや記事データは D1 データベースにそのまま保持された状態で、テスト環境を通じて安心して新リリースを反映できるデプロイパイプラインが整いました。</p>
<p>安全な複数アカウントデプロイを活用し、ご自身のブログをより安定的かつ快適に運用してください。</p>
]]></content:encoded>
            <category>総合ガイド</category>
            <enclosure url="https://sveltekitblog.com/images/posts/cloudflare-pages-multi-account-deploy-guide/desktop/img-general-guide-cloudflare-pages-multi-account-deploy-guide-ja-001.webp" length="0" type="image/webp"/>
        </item>
        <item>
            <title><![CDATA[【開発日誌】SvelteKit Blog Engine v1.0.0.9 リリース及び主な更新内容]]></title>
            <link>https://testblog-6br.pages.dev/ja/devlog/devlog-release-v1009</link>
            <guid isPermaLink="true">https://testblog-6br.pages.dev/ja/devlog/devlog-release-v1009</guid>
            <pubDate>Sun, 23 Aug 2026 12:48:54 GMT</pubDate>
            <description><![CDATA[SvelteKit Blog Engine v1.0.0.9 がリリース！複数アカウント対応ワンクリックCloudflare Pagesデプロイ、ブログハブ連動、Google AdSense合格に向けたSEO最適化及び多言語辞書同期の改善点を紹介します。]]></description>
            <content:encoded><![CDATA[<h1 id="sveltekit-blog-engine-v1009-リリース">🚀 SvelteKit Blog Engine v1.0.0.9 リリース</h1>
<p>v1.0.0.8に続き、今回の v1.0.0.9 リリースでは、<strong>複数アカウント対応ワンクリック Cloudflare Pages 統合デプロイ</strong>、<strong><a href="https://hub.sveltekitblog.com/">SvelteKit Blog Hub BLOG FEED</a> 連携</strong>、<strong>Google AdSense 審査承認に向けた SEO/サイトマップ全面最適化および RSS/サイトマップのデザイン統一</strong>、そして <strong>多言語(i18n)辞書の完成度向上とデプロイ同期の利便性改善</strong> が導入されました。</p>
<hr>
<h3 id="1-複数アカウントのスマート自動同期およびワンクリック-cloudflare-pages-デプロイ">1. 🌐 複数アカウントのスマート自動同期およびワンクリック Cloudflare Pages デプロイ</h3>
<ul>
<li><strong><code>wrangler.backup.json</code> によるスマート自動同期 (<code>npm run deploy:sync</code>)</strong>:<ul>
<li>管理画面からダウンロードしたバックアップファイル(<code>wrangler.backup*.json</code>)をルートフォルダに配置して同期コマンドを実行するだけで、D1 ID、KV ID、プロジェクト名を自動検出し、<code>.deploy-accounts.json</code> に完全登録します。</li>
<li>ユーザーが入力済みの API トークンとアカウント ID は 100% 安全に保持(Merge)されます。</li>
</ul>
</li>
<li><strong>ワンクリック複数アカウントデプロイランナー (<code>npm run deploy:multi</code>)</strong>:<ul>
<li><code>npm run deploy:multi -- &lt;アカウントキー&gt;</code> コマンド一発で、指定アカウントの <strong>Blog + Admin (2つの CFP)</strong> を連続自動デプロイします。</li>
<li><code>--blog-only</code>、<code>--admin-only</code> の個別選択デプロイおよび登録済み全アカウントの一括デプロイ(<code>--all</code>)に対応。</li>
</ul>
</li>
<li><strong>完全なキャッシュサンドボックス分離と自動クリーンアップ</strong>:<ul>
<li><code>APPDATA: .wrangler-multi-temp</code> 一時フォルダにより、グローバルな Wrangler セッションを 100% 隔離します。</li>
<li>デプロイの瞬間のみ <code>wrangler.json</code> をインメモリスワップし、デプロイ完了後に <code>finally</code> ブロックで元の設定ファイルへ 100% 復元します。</li>
<li>デプロイ直後にローカルの <code>.wrangler</code> およびキャッシュを自動削除し、通常の単一デプロイ(<code>deploy:admin</code>、<code>deploy:blog</code>)への影響を 0% に保ちます。</li>
</ul>
</li>
<li><strong>D1 ス키마自動マイグレーション (<code>sync-secrets.js</code>)</strong>:<ul>
<li>デプロイパイプライン内で D1 DB に <code>is_syndicated</code> カラムを安全に自動追加します。</li>
</ul>
</li>
</ul>
<hr>
<h3 id="2-sveltekit-ブログハブhub-統合フィード連携">2. 📡 SvelteKit ブログハブ(Hub) 統合フィード連携</h3>
<ul>
<li><strong>リアルタイムハブクライアントモジュール (<code>hub.ts</code>, <code>syndication.ts</code>)</strong>:<ul>
<li>記事公開時に <strong><a href="https://hub.sveltekitblog.com/">SvelteKit Blog Hub BLOG FEED</a></strong> へ記事のメタデータカードをリアルタイム送信する連携パイプラインを構築しました。</li>
</ul>
</li>
<li><strong>管理画面エディタのハブ連携トグル UI (<code>PostMetadataForm.svelte</code>)</strong>:<ul>
<li>記事の作成・編集時にハブへのフィード送信フラグ(<code>is_syndicated</code>)をワンクリックで切り替えられるトグルチェックボックスを追加しました。</li>
</ul>
</li>
<li><strong>DB ス키마拡張 (<code>schema-blog-db.sql</code>)</strong>:</li>
</ul>
]]></content:encoded>
            <category>開発ログ</category>
        </item>
        <item>
            <title><![CDATA[【開発日誌】リリース履歴のまとめ (v1.0.0.4 ~ v1.0.0.8)]]></title>
            <link>https://testblog-6br.pages.dev/ja/devlog/devlog-releases-v1004-to-v1008</link>
            <guid isPermaLink="true">https://testblog-6br.pages.dev/ja/devlog/devlog-releases-v1004-to-v1008</guid>
            <pubDate>Sun, 23 Aug 2026 12:38:32 GMT</pubDate>
            <description><![CDATA[SvelteKit Blog Engine リリース履歴のまとめ (v1.0.0.4 ~ v1.0.0.8)]]></description>
            <content:encoded><![CDATA[<h3 id="release-v1008">🚀 Release v1.0.0.8</h3>
<ul>
<li><strong>カテゴリーウィジェットの一本化</strong>: 設定の混乱を招いていた従来の <code>category_link</code> オプションを完全撤廃し、単一の**「カテゴリーメニュー」ウィジェット**に統合。</li>
<li><strong>投稿数表示オプション (<code>showPostCount</code>)</strong>: 管理画面のデザインエディタに、カテゴリー名の隣に記事数を表示/非表示できるトグルチェックボックスを追加（例: <code>開発関連 (5)</code>）。</li>
<li><strong>デザインエディタのリアルタイムプレビュー改善</strong>: デザインエディタ内で、最新記事・人気記事・カテゴリーメニューの各ウィジェットが実際のデータに近いプレースホルダーで正しく表示されるよう修正。</li>
<li><strong>公開記事基準のスマート <code>hreflang</code> フィルタリング (SEO)</strong>: 実際に公開記事が存在する言語に対してのみ <code>hreflang</code> タグを生成し、空の多言語ページが Google AdSense 等で価値の低いコンテンツ（Thin Content）と判定される問題を根本から防止。</li>
</ul>
<hr>
<h3 id="release-v1007">🚀 Release v1.0.0.7</h3>
<ul>
<li><strong>エンジンバージョン管理の正式導入 (<code>APP_VERSION</code>)</strong>: <code>@sveltekitblog/shared</code> パッケージにバージョン定数を追加し、管理画面サイドバーおよびブログフッターに現在稼働中のエンジンバージョンバッジを表示。</li>
<li><strong>全ルート多言語メタデータの完全対応</strong>: ホーム、カテゴリー、タグ、検索、ゲストブックなどすべてのフロントエンドルートに <code>hreflang</code> および正規化URL (Canonical) を完全に適用。</li>
<li><strong>フッターのモバイル視認性改善</strong>: モバイル画面におけるバージョンバッジのコントラスト比とレイアウト余白を最適化。</li>
</ul>
<hr>
<h3 id="release-v1006">🚀 Release v1.0.0.6</h3>
<ul>
<li><strong>モバイル Core Web Vitals およびパフォーマンス最適化</strong>: 動的OG画像生成、Google Fonts API キャッシュ、静的リソースのプリロードによりモバイル表示速度を改善。</li>
<li><strong>サイトマップの 500 エラー修正</strong>: <code>sitemap.xml</code> 生成ロジックから存在しない古いタグテーブルクエリを削除し、エラーのない安定したサイトマップ配信を保証。</li>
<li><strong>動的 HTML <code>lang</code> 属性の最適化</strong>: アクセス言語コンテキスト（<code>ko</code>, <code>en</code>, <code>ja</code>）に応じて、ルートの HTML タグの <code>lang</code> 属性が正確に自動切り替えされるよう修正。</li>
<li><strong>多言語 i18n キー構造の整理</strong>: 言語バッジ翻訳キーを <code>common.lang.short_*</code> グループへ統一。</li>
</ul>
<hr>
<h3 id="release-v1005">🚀 Release v1.0.0.5</h3>
<ul>
<li><strong>Svelte 5 Runes 反応性警告の解消</strong>: 管理画面コンポーネント内の <code>$state</code> 反応性ステートに関するコンパイラ警告を修正。</li>
<li><strong>管理画面デフォルトルートの整理</strong>: 管理画面ダッシュボードの導線およびフィードバックフローを改善。</li>
<li><strong>正規化 URL (Canonical) の適正化</strong>: スラッシュの欠落および多言語 URL 間での正規化アドレスの不一致バグを修正。</li>
</ul>
<hr>
<h3 id="release-v1004">🚀 Release v1.0.0.4</h3>
<ul>
<li><strong>NAVER ウェブマスターツール H1 重複エラーの解消</strong>: <code>LayoutRenderer</code> のレスポンシブ切り替えをモバイルファースト SSR 方式に改善し、クローラーが H1 タグを重複検出しないよう修正。</li>
<li><strong>フッターレイアウトの整列</strong>: ブログフッターの最大幅とボーダースタイルをヘッダーと統一し、シャドウ（影）のレン더リングを最適化。</li>
<li><strong>リポジトリのクリーンアップ</strong>: Git 追跡に誤って含まれていた一時ファイル（<code>.bak</code>）を整理。</li>
</ul>
]]></content:encoded>
            <category>開発ログ</category>
        </item>
        <item>
            <title><![CDATA[Cloudflare PagesでWebサイトをデプロイする2つの方法と基本原理]]></title>
            <link>https://testblog-6br.pages.dev/ja/general-guide/cloudflare-pages-deployment-guide</link>
            <guid isPermaLink="true">https://testblog-6br.pages.dev/ja/general-guide/cloudflare-pages-deployment-guide</guid>
            <pubDate>Fri, 21 Aug 2026 23:51:07 GMT</pubDate>
            <description><![CDATA[Cloudflare Pages(CFP)にWebサイトをデプロイする2つの認証方式（ブラウザ対話型ログイン vs APIトークン）の違いや、D1データベース、Workers KVストレージの基本概念と実際のSvelteKitブログエンジンの構成をわかりやすく解説します。]]></description>
            <content:encoded><![CDATA[<h1 id="cloudflare-pagesでwebサイトをデプロイする2つの方法と基本原理">🚀 Cloudflare PagesでWebサイトをデプロイする2つの方法と基本原理</h1>
<p>Webサイトやブログを開発した後、次に直面する課題は「どうやってインターネット上に公開して訪問者に見てもらうか」ということです。</p>
<p>従来は、月額料金を支払ってVPS（仮想専用サーバー）を契約し、Nginxの設定やSSL証明書の更新などを手動で管理する必要がありました。しかし現在では、**Cloudflare Pages(CFP)**を活用することで、サーバー管理の負担なく、グローバルな超高速配信と寛大な無料枠（Free Tier）で簡単にサイトをデプロイできます。</p>
<p>本記事では、Cloudflare Pagesの概要をはじめ、手元のPCからデプロイする際の<strong>2つの認証方式（ブラウザログイン方式 vs APIトークン方式）<strong>の違い、そして記事や画像を保存する</strong>D1データベースとWorkers KVストレージ</strong>の基本原理をわかりやすく解説します。</p>
<hr>
<h2 id="1-cloudflare-pagescfpとは">📂 1. Cloudflare Pages(CFP)とは？</h2>
<p>一言で言えば、**「制作したWebサイトのファイルを世界中数百都市にあるCloudflareのエッジサーバーに配置し、訪問者に最も近い場所から超高速で配信するホスティングサービス」**です。</p>
<p>HTML、CSS、画像などの静的ファイルはもちろん、SvelteKitやNext.jsのようにサーバー側でリアルタイムに画面を描画するSSR（サーバーサイドレンダリング）やAPIルートも快適に動作します。<br><img src="https://sveltekitblog.com/images/posts/cloudflare-pages-deployment-guide/desktop/img-general-guide-cloudflare-pages-deployment-guide-ja-001.webp" alt="img-general-guide-cloudflare-pages-deployment-guide-ja-001"></p>
<h3 id="なぜ従来のvpsではなくcloudflare-pagesを選ぶのか">なぜ従来のVPSではなくCloudflare Pagesを選ぶのか？</h3>
<ul>
<li><strong>サーバー管理が一切不要</strong>: Linuxのセキュリティパッチ適用やNginxの設定、サーバー死活監視を行う必要がありません。Cloudflareが24時間安全にインフラを管理します。</li>
<li><strong>圧倒的なグローバル配信速度</strong>: ユーザーのリクエストが中央の1台のサーバーに集中せず、物理的に最も近いエッジサーバーから即座に応答します。</li>
<li><strong>優れたコストパフォーマンス</strong>: 個人ブログや一般的なポートフォリオのトラフィックであれば、無料プラン（Free Tier）の範囲内で十分に運用可能です。</li>
</ul>
<hr>
<h2 id="2-手元のpcからcloudflareへデプロイする2つの方法">🔑 2. 手元のPCからCloudflareへデプロイする2つの方法</h2>
<p>SvelteKitアプリケーションをビルド（<code>npm run build</code>）すると、<code>@sveltejs/adapter-cloudflare</code>によって**<code>.svelte-kit/cloudflare</code>**ディレクトリに静的ファイルと実行用ワーカー（<code>_worker.js</code>）が生成されます。</p>
<p>このビルド成果物をCloudflare Pagesにデプロイする標準コマンドは**<code>npx wrangler pages deploy .svelte-kit/cloudflare</code>**であり、認証方式には大きく2種類が存在します。</p>
<h4 id="方法-1-対話型ブラウザログインによるデプロイwrangler標準方式">[方法 1] 対話型ブラウザログインによるデプロイ（Wrangler標準方式）</h4>
<p>単一アカウントで初めて始める際に最も直感的な方式です。コマンドを実行するとブラウザが開き、ログイン権限を承認します。<br><img src="https://sveltekitblog.com/images/posts/cloudflare-pages-deployment-guide/desktop/img-general-guide-cloudflare-pages-deployment-guide-ja-002.webp" alt="img-general-guide-cloudflare-pages-deployment-guide-ja-002"></p>
<pre><code class="language-bash"># SvelteKitビルド後にPagesへデプロイ（ブラウザログイン）
npm run build
npx wrangler pages deploy .svelte-kit/cloudflare --project-name=my-blog-web --branch=production
</code></pre>
<ul>
<li><strong>動作の仕組み</strong>: ターミナルでコマンドを実行すると、PCのWebブラウザが自動的に起動し、Cloudflareのログイン画面が表示されます。[Allow（承認）]をクリックするとローカル環境（<code>~/.config/.wrangler</code>）にセッションが保存され、デプロイが完了します。</li>
<li><strong>メリット</strong>: APIトークンを個別に発行する手間がなく、数クリックで手軽に連携できるため、<strong>個人ブログを1つのアカウントで始める際に最適</strong>です。</li>
<li><strong>注意点</strong>:<ul>
<li>複数のCloudflareアカウントを切り替えてデプロイする場合、都度ログアウトと再ログインが必要になります。</li>
<li>画面のないリモートサーバーやGitHub ActionsなどのCI/CD自動化環境では利用できません。</li>
</ul>
</li>
</ul>
<hr>
<h4 id="方法-2-apiトークン環境変数による非対話型自動デプロイcicd・複数アカウント標準">[方法 2] APIトークン環境変数による非対話型自動デプロイ（CI/CD・複数アカウント標準）</h4>
<p>複数アカウントの運用やデプロイ自動化で使用される標準的な方式です。発行済みのAPIトークンを環境変数で渡すことで、ブラウザのポップアップなしで即座にデプロイします。<br><img src="https://sveltekitblog.com/images/posts/cloudflare-pages-deployment-guide/desktop/img-general-guide-cloudflare-pages-deployment-guide-ja-003.webp" alt="img-general-guide-cloudflare-pages-deployment-guide-ja-003"></p>
<pre><code class="language-bash"># 環境変数でトークンとアカウントIDを指定してデプロイ（PowerShell例）
$env:CLOUDFLARE_API_TOKEN=&quot;YOUR_API_TOKEN&quot;
$env:CLOUDFLARE_ACCOUNT_ID=&quot;YOUR_ACCOUNT_ID_32_CHARS&quot;
npx wrangler pages deploy .svelte-kit/cloudflare --project-name=my-blog-web --branch=production
</code></pre>
<ul>
<li><strong>動作の仕組み</strong>: Cloudflareダッシュボードで必要な権限（Pages、D1、KVなど）を持つAPIトークンを発行し、環境変数に登録しておきます。デプロイ実行時にこのトークンが自動的に渡され、ブラウザを開かずにバックグラウンドでデプロイが完了します。</li>
<li><strong>メリット</strong>:<ul>
<li>ポップアップ画面が出ず、静かに高速にアップロードされます。</li>
<li>アカウントごとに異なるトークンを割り当てられるため、<strong>複数サイトを運用する際もセッション衝突を起こさず完全に独立デプロイ</strong>が可能です。</li>
</ul>
</li>
<li><strong>準備</strong>: 初回のみCloudflareダッシュボードでAPIトークンを発行する手順が必要です。</li>
</ul>
<hr>
<h2 id="3-記事や画像はどこに保存されるのかd1とkvの役割">💾 3. 記事や画像はどこに保存されるのか？（D1とKVの役割）</h2>
<p>Webサイトのフロントエンド画面だけでなく、執筆した記事やアップロードした画像を安全に保管するストレージが必要です。Cloudflareは2種類のサーバーレスストレージを提供しています。<br><img src="https://sveltekitblog.com/images/posts/cloudflare-pages-deployment-guide/desktop/img-general-guide-cloudflare-pages-deployment-guide-ja-004.webp" alt="img-general-guide-cloudflare-pages-deployment-guide-ja-004"></p>
<ol>
<li><strong>Cloudflare D1（リレーショナルSQLiteデータベース）</strong>:<ul>
<li>行と列で構造化されたエッジ分散型SQLデータベースです。</li>
<li>記事のタイトル、本文（Markdown/HTML）、公開日時、カテゴリー、タグ、サイト基本設定など、検索や構造化が必要なデータを安全に保存します。</li>
</ul>
</li>
<li><strong>Workers KV（超高速Key-Valueストレージ）</strong>:<ul>
<li>読み込み処理に特化したグローバル分散Key-Valueストアです。</li>
<li>記事に添付されたサムネイルや画像バイナリをエッジキャッシュに保持し、訪問者の要求に応じてミリ秒単位で素早く返します。</li>
</ul>
</li>
</ol>
<hr>
<h2 id="4-sveltekitブログエンジンの実例構成">🧩 4. SvelteKitブログエンジンの実例構成</h2>
<p>SvelteKitブログエンジン（SvelteKit Blog Engine）は、これらのCloudflareサーバーレスインフラを以下のように最適化して構成しています。<br><img src="https://sveltekitblog.com/images/posts/cloudflare-pages-deployment-guide/desktop/img-general-guide-cloudflare-pages-deployment-guide-ja-005.webp" alt="img-general-guide-cloudflare-pages-deployment-guide-ja-005"></p>
<hr>
<h3 id="なぜブログ画面と管理画面を2つのpagesに分離したのか">なぜブログ画面と管理画面を2つのPagesに分離したのか？</h3>
<p>1つのアプリケーションに統合するのではなく、<strong>一般訪問者用<code>apps/blog</code>と管理者用<code>apps/admin</code>の2つの独立したCloudflare Pagesに分けてデプロイ</strong>しています：</p>
<ol>
<li><strong>攻撃対象領域（Attack Surface）の最小化</strong>: 管理者ログイン処理、Markdown/HTMLエディタ、設定変更APIなどの管理機能が一般公開バンドルに一切含まれません。別個のサブドメインで物理的に分離されるため、セキュリティが大幅に向上します。</li>
<li><strong>訪問者用バンドルの極限までの軽量化</strong>: 閲覧用サイトには重量のあるエディタライブラリや管理スクリプトが1バイトも含まれないため、モバイル端末でも極めて高速に初回ページが表示されます。</li>
</ol>
<hr>
<h3 id="データの流れ">データの流れ</h3>
<ul>
<li>管理者が<strong>管理画面</strong>で記事を投稿・編集して画像をアップロードすると、データは共有の**D1（データベース）<strong>と</strong>Workers KV（画像ストレージ）**に書き込まれます。</li>
<li>読者が<strong>ブログWebサイト</strong>にアクセスすると、同じD1およびKVから最新のコンテンツを取得して高速に描画します。</li>
</ul>
<hr>
<h2 id="5-まとめ">🚀 5. まとめ</h2>
<p>Cloudflare Pagesは、従来の複雑なサーバー保守や高額なホスティング費用を不要にし、誰もが快適で高性能なフルスタックWebアプリケーションを運用できる優れたプラットフォームです。</p>
<p>最初は<strong>ブラウザログイン方式（<code>wrangler login</code>）<strong>で手軽にサイトを立ち上げ、サイトの規模拡大や複数アカウント運用に合わせて</strong>APIトークン自動デプロイ</strong>へと段階的に移行していくアプローチをおすすめします。</p>
]]></content:encoded>
            <category>総合ガイド</category>
            <enclosure url="https://sveltekitblog.com/images/posts/cloudflare-pages-deployment-guide/desktop/img-general-guide-cloudflare-pages-deployment-guide-ja-001.webp" length="0" type="image/webp"/>
        </item>
        <item>
            <title><![CDATA[【開発日記】2026-07-10]]></title>
            <link>https://testblog-6br.pages.dev/ja/devlog/devlog-2026-07-10</link>
            <guid isPermaLink="true">https://testblog-6br.pages.dev/ja/devlog/devlog-2026-07-10</guid>
            <pubDate>Sun, 26 Jul 2026 04:50:21 GMT</pubDate>
            <description><![CDATA[v1.0.0.2 & v1.0.0.3 リリースおよび新規テーマ適用]]></description>
            <content:encoded><![CDATA[<h2 id="v1002-アップデート詳細">v1.0.0.2 アップデート詳細</h2>
<p>今回のリリースでは、管理者ポストエディターの利便性と機能を向上させるための複数の改善事項を反映しました。</p>
<h3 id="主な変更点">🚀 主な変更点</h3>
<ol>
<li><p><strong>本文画像のリンク挿入および編集機能の追加</strong></p>
<ul>
<li>エディター内で画像を挿入・編集する際、画像にカスタムリンクURLおよび「新しいタブで開く」設定を連動できるようモーダルUIを拡張しました。</li>
<li>Tiptapエディター読み込み時に画像リンクが強制削除される問題を防止するため、HTML前/後処理（Pre/Post-processing）機能を連動しました。</li>
</ul>
</li>
<li><p><strong>投稿作成/編集プレビューにおける画像キャプションの視覚化</strong></p>
<ul>
<li>投稿プレビュータブで画像下の意味論的キャプション（<code>&lt;figcaption&gt;</code>）が正常にレンダリングされるよう、既存の本文処理ユーティリティを再利用して同期しました。</li>
</ul>
</li>
<li><p><strong>多言語対応リアルタイム文字数カウンターの搭載</strong></p>
<ul>
<li>記事作成・編集画面の下部に、純粋な文字数をリアルタイムで集計するカウンターUIを追加しました。</li>
<li>HTMLタグやMarkdown文法記号などを自動的に除去し、実際のテキスト文字数のみを精密にカウント。多言語設定にも完全対応しています。</li>
</ul>
</li>
</ol>
<hr>
<h2 id="v1003-アップデート詳細">v1.0.0.3 アップデート詳細</h2>
<p>今回のリリースでは、ウィジェットシステムの構造的な欠陥を全面的に修正し、ウィジェット一覧のUIスタイルを統一しました。</p>
<h3 id="バグ修正">🐛 バグ修正</h3>
<ol>
<li><p><strong>ウィジェット新規作成直後の配置におけるレンダリングクラッシュの修正</strong></p>
<ul>
<li>作成したばかりのウィジェットを即座にレイアウトへ配置する際、<code>master-</code> プレフィックスが漏れたまま保存され、レンダリングが崩壊していた致命的なクラッシュを修正しました。</li>
</ul>
</li>
<li><p><strong>デスクトップ/モバイルレイアウト配置時の <code>config</code> データ消失の修正</strong></p>
<ul>
<li>ウィジェットを配置する過程で設定値（<code>config</code>）が一切伝達されず、配置されたウィジェットが空の状態で保存されていたバグを修正しました。</li>
</ul>
</li>
<li><p><strong>ウィジェット編集モーダル表示時の設定値初期化バグの修正</strong></p>
<ul>
<li>配置されたウィジェットの編集ボタンを押した際、<code>config</code> がすでにオブジェクトであるにもかかわらず <code>JSON.parse</code> を二重で実行して SyntaxError が発生し、モーダルの全設定（limit, shadow 等）が初期値にリセットされていたバ그を修正しました。</li>
</ul>
</li>
<li><p><strong><code>limit</code> の不正入力に対するバリデーションの強化</strong></p>
<ul>
<li>ウィジェット作成・編集時に <code>limit</code> 値が不正な値（<code>NaN</code>、1未満）で保存される脆弱性を遮断し、該当する場合はデフォルト値 <code>5</code> に正規化するよう修正しました。</li>
</ul>
</li>
<li><p><strong>同一タイプウィジェット複数配置時のデータ取得範囲エラーの修正</strong></p>
<ul>
<li>同じタイプのウィジェット（例：人気投稿）が複数配置された場合、1つ目のウィジェットの <code>limit</code> 基準でのみDBを検索し、残りのウィジェットが切れたデータを受け取っていた構造的バグを修正しました。同一タイプのウィジェットの中で最も大きい <code>limit</code> 基準で検索したのち、各ウィジェットが自らの設定に合わせて独立してデータを表示します。</li>
</ul>
</li>
</ol>
<h3 id="スタイル">🎨 スタイル</h3>
<ol>
<li><strong>ウィジェット一覧の区切り線スタイルの統一</strong><ul>
<li><code>RecentPostsWidget</code> と <code>PopularPostsWidget</code> の項目間区切り方式が異なり、視覚的な一貫性がなかった問題を修正しました。両ウィジェットとも <code>border-bottom</code> 区切り線と <code>0.5rem</code> パディングで統一しました。</li>
</ul>
</li>
</ol>
<hr>
<h2 id="レイアウトラベルおよびデザインのマイナー調整">🎨 レイアウトラベルおよびデザインのマイナー調整</h2>
<p>ブログのレイアウトやカラーなど全体のスタイル要素をマイナー調整し、画面構成が崩れることなく自然に馴染むよう整えました。</p>
<h3 id="主なスタイル調整事項">🛠️ 主なスタイル調整事項</h3>
<ol>
<li><p><strong>テーマカラーおよびトーンの調整</strong></p>
<ul>
<li>オレンジ系のサブカラーをポイントとし、背景色全体およびカード領域の境界線・影の値を整え、視覚的な負担を軽減しました。</li>
</ul>
</li>
<li><p><strong>デスクトップ2段構成レイアウトの配置調整</strong></p>
<ul>
<li>本文領域とサイドバー（カテゴリー、最新/人気投稿など）の横幅比率を再調整し、左右の余白とコンテンツの整列がずれないよう合わせました。</li>
</ul>
</li>
<li><p><strong>ヘッダー/フッターおよびモバイルメニューの配置整列</strong></p>
<ul>
<li>上部ヘッダーの透明度調整とともに、モバイルメニューアイコンの整列状態および下部フッター領域のレイアウトがスマートに表示されるよう整理しました。</li>
</ul>
</li>
</ol>
<hr>
<h2 id="title-quot開発日記2026-07-10quotslug-quotdevlog-2026-07-10quotcategory-quotdevlogquotcategoryname-quot開発日記quottags-ブログエンジン-リリース-v1003-テーマ設定excerpt-quotv1002-amp-v1003-リリースおよび新規テーマ適用quotauthorid-quot管理者quotthumbnailfit-quotcoverquot">title: &quot;【開発日記】2026-07-10&quot;<br>slug: &quot;devlog-2026-07-10&quot;<br>category: &quot;devlog&quot;<br>categoryName: &quot;開発日記&quot;<br>tags: [ブログエンジン, リリース, v1.0.0.3, テーマ設定]<br>excerpt: &quot;v1.0.0.2 &amp; v1.0.0.3 リリースおよび新規テーマ適用&quot;<br>authorId: &quot;管理者&quot;<br>thumbnailFit: &quot;cover&quot;</h2>
<h2 id="v1002-アップデート詳細-1">v1.0.0.2 アップデート詳細</h2>
<p>今回のリリースでは、管理者ポストエディターの利便性と機能を向上させるための複数の改善事項を反映しました。</p>
<h3 id="主な変更点-1">🚀 主な変更点</h3>
<ol>
<li><p><strong>本文画像のリンク挿入および編集機能の追加</strong></p>
<ul>
<li>エディター内で画像を挿入・編集する際、画像にカスタムリンクURLおよび「新しいタブで開く」設定を連動できるようモーダルUIを拡張しました。</li>
<li>Tiptapエディター読み込み時に画像リンクが強制削除される問題を防止するため、HTML前/後処理（Pre/Post-processing）機能を連動しました。</li>
</ul>
</li>
<li><p><strong>投稿作成/編集プレビューにおける画像キャプションの視覚化</strong></p>
<ul>
<li>投稿プレビュータブで画像下の意味論的キャプション（<code>&lt;figcaption&gt;</code>）が正常にレンダリングされるよう、既存の本文処理ユーティリティを再利用して同期しました。</li>
</ul>
</li>
<li><p><strong>多言語対応リアルタイム文字数カウンターの搭載</strong></p>
<ul>
<li>記事作成・編集画面の下部に、純粋な文字数をリアルタイムで集計するカウンターUIを追加しました。</li>
<li>HTMLタグやMarkdown文法記号などを自動的に除去し、実際のテキスト文字数のみを精密にカウント。多言語設定にも完全対応しています。</li>
</ul>
</li>
</ol>
<hr>
<h2 id="v1003-アップデート詳細-1">v1.0.0.3 アップデート詳細</h2>
<p>今回のリリースでは、ウィジェットシステムの構造的な欠陥を全面的に修正し、ウィジェット一覧のUIスタイルを統一しました。</p>
<h3 id="バグ修正-1">🐛 バグ修正</h3>
<ol>
<li><p><strong>ウィジェット新規作成直後の配置におけるレンダリングクラッシュの修正</strong></p>
<ul>
<li>作成したばかりのウィジェットを即座にレイアウトへ配置する際、<code>master-</code> プレフィックスが漏れたまま保存され、レンダリングが崩壊していた致命的なクラッシュを修正しました。</li>
</ul>
</li>
<li><p><strong>デスクトップ/モバイルレイアウト配置時の <code>config</code> データ消失の修正</strong></p>
<ul>
<li>ウィジェットを配置する過程で設定値（<code>config</code>）が一切伝達されず、配置されたウィジェットが空の状態で保存されていたバグを修正しました。</li>
</ul>
</li>
<li><p><strong>ウィジェット編集モーダル表示時の設定値初期化バグの修正</strong></p>
<ul>
<li>配置されたウィジェットの編集ボタンを押した際、<code>config</code> がすでにオブジェクトであるにもかかわらず <code>JSON.parse</code> を二重で実行して SyntaxError が発生し、モーダルの全設定（limit, shadow 等）が初期値にリセットされていたバ그を修正しました。</li>
</ul>
</li>
<li><p><strong><code>limit</code> の不正入力に対するバリデーションの強化</strong></p>
<ul>
<li>ウィジェット作成・編集時に <code>limit</code> 値が不正な値（<code>NaN</code>、1未満）で保存される脆弱性を遮断し、該当する場合はデフォルト値 <code>5</code> に正規化するよう修正しました。</li>
</ul>
</li>
<li><p><strong>同一タイプウィジェット複数配置時のデータ取得範囲エラーの修正</strong></p>
<ul>
<li>同じタイプのウィジェット（例：人気投稿）が複数配置された場合、1つ目のウィジェットの <code>limit</code> 基準でのみDBを検索し、残りのウィジェットが切れたデータを受け取っていた構造的バグを修正しました。同一タイプのウィジェットの中で最も大きい <code>limit</code> 基準で検索したのち、各ウィジェットが自らの設定に合わせて独立してデータを表示します。</li>
</ul>
</li>
</ol>
<h3 id="スタイル-1">🎨 スタイル</h3>
<ol>
<li><strong>ウィジェット一覧の区切り線スタイルの統一</strong><ul>
<li><code>RecentPostsWidget</code> と <code>PopularPostsWidget</code> の項目間区切り方式が異なり、視覚的な一貫性がなかった問題を修正しました。両ウィジェットとも <code>border-bottom</code> 区切り線と <code>0.5rem</code> パディングで統一しました。</li>
</ul>
</li>
</ol>
<hr>
<h2 id="レイアウトラベルおよびデザインのマイナー調整-1">🎨 レイアウトラベルおよびデザインのマイナー調整</h2>
<p>ブログのレイアウトやカラーなど全体のスタイル要素をマイナー調整し、画面構成が崩れることなく自然に馴染むよう整えました。</p>
<h3 id="主なスタイル調整事項-1">🛠️ 主なスタイル調整事項</h3>
<ol>
<li><p><strong>テーマカラーおよびトーンの調整</strong></p>
<ul>
<li>オレンジ系のサブカラーをポイントとし、背景色全体およびカード領域の境界線・影の値を整え、視覚的な負担を軽減しました。</li>
</ul>
</li>
<li><p><strong>デスクトップ2段構成レイアウトの配置調整</strong></p>
<ul>
<li>本文領域とサイドバー（カテゴリー、最新/人気投稿など）の横幅比率を再調整し、左右の余白とコンテンツの整列がずれないよう合わせました。</li>
</ul>
</li>
<li><p><strong>ヘッダー/フッターおよびモバイルメニューの配置整列</strong></p>
<ul>
<li>上部ヘッダーの透明度調整とともに、モバイルメニューアイコンの整列状態および下部フッター領域のレイアウトがスマートに表示されるよう整理しました。</li>
</ul>
</li>
</ol>
]]></content:encoded>
            <category>開発ログ</category>
        </item>
        <item>
            <title><![CDATA[[Devlog] 軽い気持ちで作ったら大ごとになり、気づけば時代遅れのブログエンジンを公開していた話]]></title>
            <link>https://testblog-6br.pages.dev/ja/devlog/devlog-why-i-built-a-blog-engine-in-2026</link>
            <guid isPermaLink="true">https://testblog-6br.pages.dev/ja/devlog/devlog-why-i-built-a-blog-engine-in-2026</guid>
            <pubDate>Sun, 26 Jul 2026 04:50:02 GMT</pubDate>
            <description><![CDATA[コーヒー代を稼ぐつもりが、自作ブログエンジンまで作る羽目になった話]]></description>
            <content:encoded><![CDATA[<p><em>※個人の考えを整理するための独白スタイルの文章のため、常体（だ・である調／敬語混じり）で書かれている点をご了承ください。</em></p><p><strong>1.</strong><br>こんな状況になってしまったのは、正直YouTubeのせい度が大きい（？）。<br>遅まきながら個人開発者として何かやってみようと資料を探していると、とてつもない量の情報が溢れ出てくる。<br>問題は、情報の量は溢れているのに、実質的な質は散々な場合が多いということだ。</p><p><strong>2.</strong><br>ごく基本的な内容なら情報が多くても問題ないのだが、少し専門的だったり特殊な状況になると、「なんでこれが検索に引っかかって、自分は今Webでこれを見ているんだ？」と理解に苦しむほど全く無用な情報に遭遇したり、タイトルには欲しい情報が含まれているのに本文は単に文法を合わせただけの意味のない文字の羅列であるケースがかなりある。<br>これがYouTube動画だと、本当に腹が立つ。</p><p><strong>3.</strong><br>そんな中、たまに砂漠でオアシスに出会うように宝物のような情報をくれたのは、ほぼすべて開発者のブログ記事だった。<br>本当に嬉しかったし、ありがたかった。<br>そして、成人ADHDを疑うほどの私の集中力は、「あ、自分も開発者ブログを一つ立ち上げて誰かの役に立ち、コーヒー代くらい稼ぎたいな（？）」という決断を下すことになる。</p><p><strong>4.</strong><br>そうして既存のブログプラットフォームを再検討してみたが、どれも自分にとっては致命的な問題点が一つずつあった。<br>まず、デプロイや維持に費用が発生するものはすべて除外した。<br>まあコーヒー代くらい稼げれば嬉しいが、実際にそれが可能なレベルにするには本気で頑張らなければならないことを知っているし、自分は自分をよく知っている。<br>私はそんなに頑張れない。<br>だからこそ、最初から「設置・維持費用が絶対にかかってはならない」という結論を大前提として敷いた。<br>そこで最初に構想したのは、ProxmoxローカルサーバーにWordPressをインストールして使うことだった。<br>このように使うには最小限のセキュリティのためにL2スイッチとpfSenseのようなファイアウォール設置は必須だと考えた。<br>そうしてある程度構成を終えてからWordPressを改めて見直してみると、カスタムが難しすぎた。<br>これをカスタムするにはPHPを本格的に勉強しなければならないと判断した時点で、この方式は諦めることになった。</p><p><strong>5.</strong><br>そしてすぐに他の方法を探してみたが、結局どれも一つずつ引っかかった。<br>どんな方法であれ、導入すればそれについて新しく勉強しなければならないという点が非常に疲弊感として押し寄せてきた。<br>その疲弊感が「あ、もうシンプルに自分で作っちゃおう」という考えへと導いた。</p><p><strong>6.</strong><br>この時、すでに私はSvelteとSvelteKitで色々と遊び倒していたところだった。<br>ブログを作ること自体は非常に簡単な状況だった。<br>問題は、自分が作ったデザインを自分が信用できないということだった。<br>そこで、デザインを簡単に変更できる構造にしようとした結果、転がり転がってここまで来ることになった。</p><p><strong>7.</strong><br>実際にデザインエディタの高度化とデバッグに最も多くの時間を費やした。<br>最初に「シンプルなブログツール」を作ろうと決めて、一日でコードの90%ほどを完成させた。<br>そして実際にブログを作って運用してみた。<br>すると、「これも必要だな」「この機能も入れなきゃ」「これがあったら便利そう」「これはないとダメだろ」がほぼ無限に溢れ出てきた。<br>そうして昨年11月末に始まったプロジェクトらしきものが、今年3月に機能実装が一段落し、7月になってようやくGitHubオープンにこぎ着けた。</p><p><strong>8.</strong><br>気がつくと機能が増えすぎてコードも複雑になったため、途中でモジュール化やリファクタリングを行った。<br>システム自体を根底から覆すことも何度かあった。<br>最初は管理画面をProxmoxサーバーにインストールしてローカルからのみ接続可能にし、DBはSupabase、認証もSupabase Authを使っていた。<br>かなり深刻なコールドスタートと無料プランでは認証時にプロジェクト名が露出する状況を経験し、素直にCloudflareエコシステムを使うことにして、以前使ったことのあるBetter-authを搭載する計画へと変更した。<br>その後も内部的にロジックや構造を相当大きく変えることになった。<br>その上、Windows環境で作業を進めたせいで、何か一つ直すたびに実際のデプロイまで行わないと確認できず、本当に凄まじい遠回り（試行錯誤）をした。</p><p><strong>9.</strong><br>ほぼ終わりが見えてきた頃には、自分一人では到底修正もできないほどコードが肥大化していたため、自分でやった部分もあるが、大部分はGeminiにやらせて進めた。<br>さらにドキュメント化をしていなかったため、一つ一つ実際の機能と照らし合わせながらドキュメント化を進め、エラーを見つけては再び修正作業に入る…という状態だった。<br>このように3月から7月まで、<em>ドキュメント化 ➔ エラー発見 ➔ 修正</em>&nbsp;の無限ループだった。<br>その間にも「これはないとダメだろ」と「この機能あったのにどこ行った？」が頻繁に飛び出して、本当に気が狂いそうだった。</p><p><strong>10.</strong><br>泣き面に蜂で、Geminiが本当に暴走する事態が非常に頻繁に発生した。<br>コード一行直せば済むものを関係のないモジュールをごっそり書き換えたりと、とにかくGeminiが暴れさえしなければ2ヶ月は早く終わっていただろうと確信している。<br>ともかくGitHubオープン直前まで極度のストレスに苛まれたが、最終的に「これはないと」という機能をすべて削ぎ落とし、できているものだけ整理して公開することに決めた。<br>それが v1.0.0.0 だった。</p><p><strong>11.</strong><br>その直後にすぐ v1.0.0.1 が出ることになる。<br>「できているもの」だけ整理して上げたはずだったのに、整理が不十分だった部分にすぐ気づいたのだ。<br>きっとこういう部分がまだあるだろうと確信していて非常に不安でもある。<br>公開した時点から直感していたので、あらかじめ&nbsp;<code>v1.0.0.x</code>&nbsp;のバージョンを切っておいたのかもしれない。</p><p><strong>12.</strong><br>…というわけで、こうしてSvelteKitブログエンジンが完成しました。<br>締めくくりが曖昧なので、PageSpeed Insightsのスコアでも載せて終わりにしたいと思います。<br>AdSenseは申請後審査待ちの状態ですが、すでにコードが挿入されており、CDNキャッシュも2分間隔に設定されているため、キャッシュがパージされて再生成される間にテストが行われるとパフォーマンスに影響を与えるのか、正確な理由は分かりませんが現在スコアの変動がかなり大きいです。<br>平均くらいが出た時のスコアですので、参考程度にご覧ください。<br>AdSenseが承認されたら、またPageSpeed Insightsのレポートを載せます。<br>それでは、良い一日を！ :)</p><figure data-align="center"><img src="https://sveltekitblog.com/images/posts/devlog-why-i-built-a-blog-engine-in-2026/desktop/img-devlog-devlog-why-i-built-a-blog-engine-in-2026-ko-001.webp" alt="img-devlog-devlog-why-i-built-a-blog-engine-in-2026-ko-001" data-align="center" data-caption="モバイルのスコア。（AdSense［審査待ち］、GA4、Google Search Console、Naver Search Advisorを導入済みの状態）"><figcaption>モバイルのスコア。（AdSense［審査待ち］、GA4、Google Search Console、Naver Search Advisorを導入済みの状態）</figcaption></figure><figure data-align="center"><img src="https://sveltekitblog.com/images/posts/devlog-why-i-built-a-blog-engine-in-2026/desktop/img-devlog-devlog-why-i-built-a-blog-engine-in-2026-ko-002.webp" alt="img-devlog-devlog-why-i-built-a-blog-engine-in-2026-ko-002" data-align="center" data-caption="デスクトップのスコア。（AdSense［審査待ち］、GA4、Google Search Console、Naver Search Advisor導入済み）"><figcaption>デスクトップのスコア。（AdSense［審査待ち］、GA4、Google Search Console、Naver Search Advisor導入済み）</figcaption></figure><p></p>]]></content:encoded>
            <category>開発ログ</category>
            <enclosure url="https://sveltekitblog.com/images/posts/devlog-why-i-built-a-blog-engine-in-2026/desktop/img-devlog-devlog-why-i-built-a-blog-engine-in-2026-ko-001.webp" length="0" type="image/webp"/>
        </item>
        <item>
            <title><![CDATA[CMDワンクリックインストール＆Cloudflareデプロイガイド]]></title>
            <link>https://testblog-6br.pages.dev/ja/admin-guide/admin-install-and-deploy</link>
            <guid isPermaLink="true">https://testblog-6br.pages.dev/ja/admin-guide/admin-install-and-deploy</guid>
            <pubDate>Wed, 15 Jul 2026 12:19:35 GMT</pubDate>
            <description><![CDATA[ターミナル（CMD/PowerShell）環境で対話型インストールスクリプトを実行し、Cloudflareエッジインフラの構築と管理者/ブログアプリのデプロイ・同期を同時に行う最も簡単な方法を解説します。]]></description>
            <content:encoded><![CDATA[<h1 id="cmdワンクリックインストールcloudflareデプロイガイド">🚀 CMDワンクリックインストール＆Cloudflareデプロイガイド</h1>
<p>本ガイドは、ターミナル（CMDまたはPowerShell）環境で自動化コマンド（<code>npm run setup</code>）を実行してCloudflareエッジインフラを構築し、管理者アプリとブログアプリを同時にデプロイ・同期する、最も簡単かつ安全な方法を案内します。</p>
<hr>
<h2 id="1-ワンクリックデプロイ自動化の紹介">🛠️ 1. ワンクリックデプロイ自動化の紹介</h2>
<p>SvelteKitブログエンジンは、複雑なサーバーリソースの生成や設定ファイルの編集プロセスを、ターミナル上の<strong>対話型インストールツール</strong>を通じて完全に自動化しました。</p>
<p>ブログのソースコードを解凍するか、<code>git clone</code>でPCに保存した後、ターミナルを開いてプロジェクトのルートフォルダーに移動し、以下のコマンドを順番に実行するだけです。</p>
<pre><code class="language-bash"># 必要なパッケージのインストール
npm install
</code></pre>
<ul>
<li><strong>依存関係ライブラリのインストール</strong>: ターミナルで<code>npm install</code>を入力して、必要なパッケージをすべてインストールします。インストール完了後にセキュリティ警告（vulnerabilities）メッセージが表示された場合は、<code>npm audit fix</code>コマンドを実行して最新のセキュリティパッチを適用することをお勧めします。</li>
</ul>
<p>パッケージのインストールが終わったら<code>npm run setup</code>を実行します。ターミナル画面の案内に従って、以下のプロセスが自動的に処理されます。</p>
<ul>
<li><strong>Cloudflareアカウントの簡単認証</strong>: クリック1回でデプロイアカウントの連携が完了します。</li>
<li><strong>DBおよびストレージリソースの生成</strong>: ブログ専用のD1データベース2個と画像保存用のKVストレージを自動的に新規生成します。</li>
<li><strong>設定ファイルのリアルタイム更新</strong>: 生成されたデータベースの固有IDを検出し、モノレポの設定ファイル（<code>wrangler.json</code>）に自動的に注入します。</li>
<li><strong>データベースの初期データ注入</strong>: テーブルを生成し、選択したデフォルト言語に合わせてサンプル記事と基本環境設定情報を自動的に登録します。</li>
<li><strong>環境変数とアクセス許可IPのアップロード</strong>: 管理者アクセス用に、デプロイ実行者の現在のパブリックIPアドレス（<code>ALLOWED_IP</code>）と管理者パスワードをクラウドへ自動送信します。</li>
<li><strong>統合ビルドおよびWebサービスのデプロイ</strong>: ブログと管理者のSvelteKitプロジェクトを統合ビル드し、Cloudflare Pagesへ即時にアップロード・デプロイします。</li>
</ul>
<hr>
<h2 id="2-デプロイ前の確認事項">📋 2. デプロイ前の確認事項</h2>
<p>安全にインストールを開始する前に、以下の準備がすべて整っているかご確認ください。</p>
<ol>
<li><strong>Node.jsのインストール</strong>: パソコンでNode.js（バージョン22以上）が動作している必要があります。</li>
<li><strong>Cloudflareアカウント</strong>: ブログサイトとデータベースをホスティングするCloudflareアカウント（無料枠で十分対応可能です）が必要です。</li>
<li><strong>環境変数ファイル（.dev.vars）の生成（必須）</strong>:<ul>
<li>デプロイを開始する前に、<code>apps/admin/</code>フォルダと<code>apps/blog/</code>フォルダの配下に、それぞれ<code>.dev.vars</code>ファイルを<strong>手動で作成</strong>する必要があります。</li>
<li><code>apps/admin/.dev.vars</code>ファイルには、<code>ADMIN_PASSWORD=管理者ログインパスワード</code>を記述します。</li>
<li><code>apps/blog/.dev.vars</code>ファイルには、<code>BETTER_AUTH_SECRET=任意の文字列</code>を記述します。</li>
<li><blockquote>
<p>[!WARNING]<br>これらの環境変数が設定されていない場合、<code>npm run setup</code>の実行直後に、正常な動作のために<strong>インストールツールが中断</strong>されます。</p>
</blockquote>
</li>
</ul>
</li>
</ol>
<hr>
<h2 id="3-npm-run-setup-ステップ別解説">⚙️ 3. npm run setup ステップ別解説</h2>
<p>ターミナルを開き、ブログプロジェクトのルートディレクトリで<code>npm run setup</code>を入力してEnterキーを押します。</p>
<h3 id="step-0-環境変数の事前検査pre-validation"><strong>Step 0. 環境変数の事前検査（Pre-Validation）</strong></h3>
<ul>
<li>実行直後に、管理者およびブログフォルダ内の<code>.dev.vars</code>ファイルと、必須の環境変数（<code>ADMIN_PASSWORD</code>、<code>BETTER_AUTH_SECRET</code>）が入力されているか検査します。</li>
<li>未入力の項目がある場合は、ガイドログを表示して終了しますので、事前に必ず記載しておいてください。</li>
</ul>
<h3 id="step-1-デプロイプロジェクト名ドメインurlの設定"><strong>Step 1. デプロイプロジェクト名（ドメインURL）の設定</strong></h3>
<ul>
<li>読者がブログにアクセスする際に使用するアドレス（例：<code>[入力したプロジェクト名].pages.dev</code>）を決定する段階です。</li>
<li>デプロイターゲットであるブログと管理者の2つの名前を入力します。復元モード（<code>--restore</code>）で実行する場合、既存のバックアップ情報ファイル（<code>wrangler.backup.json</code>）から既存の名前を自動的に読み込んで設定します。</li>
</ul>
<h3 id="step-2-cloudflareアカウントのログイン認証"><strong>Step 2. Cloudflareアカウントのログイン認証</strong></h3>
<ul>
<li>Cloudflareの認証を確認します。すでにログイン履歴がある場合は、自動的にセッションを検出してこの手順をスキップします。</li>
<li>ログインしていない場合は、ブラウザウィンドウが開き認証要求画面が表示されます。**[Allow / 許可]**をクリックしてアカウント連携を承認してください。</li>
<li><em>セキュリティ情報：本自動化ツールは、インフラ生成およびデプロイに必要な最小限のアカウントAPI権限のみを要求するため、安心してお進みください。</em></li>
</ul>
<h3 id="step-3-リソース生成および設定の連携"><strong>Step 3. リソース生成および設定の連携</strong></h3>
<ul>
<li>実際のサービス動作に必要なデータベースとメディアストレージをクラウドに生成する段階です。ターミナルの案内に従って、適切な番号を入力してください。<ol>
<li><strong><code>1</code>（新規インストール / Fresh Install）</strong>: 以前に作成されたデータベースとストレージを<strong>完全に削除して新しく作成し直します。</strong> 既存のデータはすべて消去されるためご注意ください。</li>
<li><strong><code>2</code>（既存データの維持 / Keep Existing Data）[推奨]</strong>: すでに生成されたリソースがある場合、データを安全に保持したまま接続情報のみを紐付けます。</li>
</ol>
</li>
<li>連携完了後、新しく発行されたDBおよびKVの固有IDがモノレポの各設定ファイル（<code>wrangler.json</code>）に自動的に反映されます。</li>
</ul>
<h3 id="step-4-webプロジェクトの事前生成"><strong>Step 4. Webプロジェクトの事前生成</strong></h3>
<ul>
<li>デプロイの安定性を高めるため、Cloudflare Edge上に空のWebプロジェクトをあらかじめ登録・確保します。</li>
<li>すでに同じ名前のアドレスが占有されている場合は、<strong>既存のアドレスを再利用</strong>して次のステップへ自動的に移行します。</li>
</ul>
<h3 id="step-5-環境変数のリモート同期"><strong>Step 5. 環境変数のリモート同期</strong></h3>
<ul>
<li>ローカルで作成した<code>.dev.vars</code>内のマスターパスワードなどの秘密の値をCloudflareへ安全にアップロードして同期します。</li>
<li><strong>アクセス許可IPの自動登録</strong>: 特にこの段階で、デプロイを実行している管理者PCのパブリックIPアドレスをリアルタイムで検出し、<code>ALLOWED_IP</code>変数として自動的に注入します。これにより、デプロイ直後の初回ログイン時にIP制限でアクセスできなくなる事態を防ぎます。</li>
</ul>
<h3 id="step-6-データベーステーブルの構築デフォルトテーマの設定"><strong>Step 6. データベーステーブルの構築＆デフォルトテーマの設定</strong></h3>
<ul>
<li>ブログ機能の動作に必要なDBテーブルを構築し、初期設定値を移植します。</li>
<li>画面の案内に従って使用するデフォルトの言語（韓国語、英語、日本語）を選択すると、翻訳システムが構築され、サンプルカテゴリ、ヘッダーメニューの骨組み、およびデフォルトのウェルカム記事が自動的に設定されます。</li>
</ul>
<h3 id="step-7-webサービスのビルド最終デプロイ"><strong>Step 7. Webサービスのビルド＆最終デプロイ</strong></h3>
<ul>
<li>モノレポ全体のアプリの統合ビルドを実行し、Cloudflareサーバーへのデプロイコマンドを自動実行してインストールプロセスを完了します。デプロイが完了すると、ブログと管理者のPagesアクセス用URLが表示されます。</li>
</ul>
<hr>
<blockquote>
<p>[!TIP]</p>
<h3 id="環境変数の自動設定と即時アクセスサポート">💡 環境変数の自動設定と即時アクセスサポート</h3>
<p>本プロジェクトのインストールプロセスは、リソースの生成からWebプロジェクトの確保、環境変数の送信までを一貫して処理します。<br>デプロイ完了と同時に、ログインパスワードやIPアクセス許可設定がリモートサーバーに設定されるため、追加の手動操作なしで即座に動作します。</p>
</blockquote>
<hr>
<h2 id="4-ブログのアップデートとデータ復元デプロイnpm-run-restore">🔄 4. ブログのアップデートとデータ復元デプロイ（npm run restore）</h2>
<p>ブログの運用中に<strong>新しいパッチバージョンやバグが修正された最新リリースのコードが配布された際</strong>、これまでに作成した記事データやアドレスを失うことなく、新しいコードへ安全に画面を差し替える方法です。</p>
<h3 id="新バージョン移行の4ステップ手順">💡 新バージョン移行の4ステップ手順</h3>
<p>管理者のデータバックアップと**<code>npm run restore</code>**コマンドを使用すれば、データ消失のリスクなく安全にアップグレードを完了できます。</p>
<ol>
<li><strong>[ステップ1] 既存データおよびデプロイ設定のバックアップ</strong>:<ul>
<li>稼働中の管理者アプリの**<code>コンテンツバックアップ</code><strong>メニュー、および</strong><code>デザインエディタ</code>**メニューの下部バックアップセクションから、記事データとテーマデザイン設定をそれぞれパソコンへダウンロードします。</li>
<li>また、**<code>コンテンツバックアップ</code>**メニューの「設定バックアップダウンロード」ボタンをクリックして、[wrangler.backup.json]ファイルをPCに保存します。（これは、既存の運用データベースの固有ID連携を維持するための最も重要なファイルです。）</li>
</ul>
</li>
<li><strong>[ステップ2] 新コードの分離テスト</strong>:<ul>
<li>新しく入手した新バージョンのコードフォルダ配下で<code>npm run setup</code>を実行し、<strong>テスト用の仮DB</strong>と<strong>テスト用のPages仮デプロイ名</strong>を入力して別個にデプロイします。</li>
<li>仮デプロイされた新しい管理者にログインし、ステップ1で保存したバックアップデータファイルをインポートして、データが正常に読み込まれるか事前に確認します。</li>
</ul>
</li>
<li><strong>[ステップ3] 既存の運用サーバーへのバインディング復元（<code>npm run restore</code>）</strong>:<ul>
<li>検証が終了したら、新バージョンプロジェクトのルートディレクトリに、ステップ1で保存した<code>wrangler.backup.json</code>ファイルをコピーして貼り付けます。</li>
<li>ターミナルで**<code>npm run restore</code><strong>コマンドを実行します。スクリプトがバックアップファイルに記述された</strong>既存の運用DBおよびKVの固有ID情報**を読み込み、新しいコードの設定ファイルへ上書きの形で自動接続します。</li>
</ul>
</li>
<li><strong>[ステップ4] スキーマ同期と上書きデプロイ</strong>:<ul>
<li>復元スクリプトは既存の運用DBのテーブル構造スキーマのみを安全に最新仕様へ更新するため、<strong>すでに蓄積されている実際の記事やデータは破損または削除されず完璧に保持</strong>されます。</li>
<li>続いて、<code>npm run deploy:blog</code>と<code>npm run deploy:admin</code>をそれぞれ実行します。使用していたドメインそのままで、<strong>最新にアップグレードされたブログシステムが安全に上書きデプロイ</strong>されます。</li>
</ul>
</li>
</ol>
<hr>
<h2 id="5-バックアップファイルの種類と機能">📦 5. バックアップファイルの種類と機能</h2>
<p>管理者のバックアップメニューで安全にインポートおよびエクスポートできる各ファイルの役割は、以下の表をご参照ください。</p>
<table>
<thead>
<tr>
<th align="left">バックアップファイルの種類</th>
<th align="left">抽出経路</th>
<th align="left">フォルダ・ファイル名（推奨）</th>
<th align="left">機能・保存内容</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>1. コンテンツDBデータ</strong></td>
<td align="left"><strong><code>コンテンツバックアップ</code></strong> メニュー</td>
<td align="left"><code>blog-content-backup-[日付].json</code></td>
<td align="left">作成されたすべてのブログ記事本文、作成済みのカテゴリ情報、タグ一覧、および基本的なシステム設定値を保存</td>
</tr>
<tr>
<td align="left"><strong>2. デプロイ設定バックアップ</strong></td>
<td align="left"><strong><code>コンテンツバックアップ</code></strong> メニュー</td>
<td align="left"><code>wrangler.backup.json</code></td>
<td align="left">Cloudflare D1 DBおよびKVストレージ連携に必要なデプロイ名と固有のUUID情報を安全に保存（<strong>サーバー移行時に必須</strong>）</td>
</tr>
<tr>
<td align="left"><strong>3. メディアファイルバックアップ</strong></td>
<td align="left"><strong><code>コンテンツバックアップ</code></strong> メニューの「メディアバックアップと復元」セクション、または <strong><code>メディアライブラリ</code></strong> メニュー右上の「[バックアップ / 復元]」ボタン</td>
<td align="left"><code>[ストレージ名]-images-backup-[日付].zip</code> <br>(例: <code>r2-images-backup-[日付].zip</code>)</td>
<td align="left">有効化されている画像ストレージ（R2、Supabaseなど）にアップロードされたすべての画像ファイルをZIP形式で圧縮ダウンロードして保存</td>
</tr>
<tr>
<td align="left"><strong>4. デザイン設定バックアップ</strong></td>
<td align="left"><strong><code>デザインエディタ</code></strong> メニュー下部</td>
<td align="left"><code>blog-design-backup-[日付].json</code></td>
<td align="left">デザインエディタで設定したブログのテーマカラー情報、背景タイプ、およびデバイス別（PC/モバイル）のウィ젯配置構造を保存</td>
</tr>
<tr>
<td align="left"><strong>5. システム全体のバックアップ</strong></td>
<td align="left"><strong><code>サイト設定</code></strong> メニュー下部</td>
<td align="left"><code>full-system-backup-[日付].json</code></td>
<td align="left">2つのデータベース（BLOG_DB, USER_DB）内のすべてのテーブルの元データを1つのJSONファイルにまとめ、全体のバックアップおよび復元を実行</td>
</tr>
</tbody></table>
<figure data-align="left"><img src="https://sveltekitblog.com/images/posts/admin-install-and-deploy/desktop/img-admin-guide-admin-install-and-deploy-ja-001.webp" alt="img-admin-guide-admin-install-and-deploy-ja-001" data-align="left" data-caption="コンテンツDBデータ" /><figcaption>コンテンツDBデータ</figcaption></figure>
<figure data-align="left"><img src="https://sveltekitblog.com/images/posts/admin-install-and-deploy/desktop/img-admin-guide-admin-install-and-deploy-ja-002.webp" alt="img-admin-guide-admin-install-and-deploy-ja-002" data-align="left" data-caption="デプロイ設定バックアップ" /><figcaption>デプロイ設定バックアップ</figcaption></figure>
<figure data-align="left"><img src="https://sveltekitblog.com/images/posts/admin-install-and-deploy/desktop/img-admin-guide-admin-install-and-deploy-ja-003.webp" alt="img-admin-guide-admin-install-and-deploy-ja-003" data-align="left" data-caption="メディアファイルバックアップ" /><figcaption>メディアファイルバックアップ</figcaption></figure>
<figure data-align="left"><img src="https://sveltekitblog.com/images/posts/admin-install-and-deploy/desktop/img-admin-guide-admin-install-and-deploy-ja-004.webp" alt="img-admin-guide-admin-install-and-deploy-ja-004" data-align="left" data-caption="デザイン設定バックアップ" /><figcaption>デザイン設定バックアップ</figcaption></figure>
<figure data-align="left"><img src="https://sveltekitblog.com/images/posts/admin-install-and-deploy/desktop/img-admin-guide-admin-install-and-deploy-ja-005.webp" alt="img-admin-guide-admin-install-and-deploy-ja-005" data-align="left" data-caption="システム全体のバックアップ" /><figcaption>システム全体のバックアップ</figcaption></figure>]]></content:encoded>
            <category>管理者ガイド</category>
            <enclosure url="https://sveltekitblog.com/images/posts/admin-install-and-deploy/desktop/img-admin-guide-admin-install-and-deploy-ja-001.webp" length="0" type="image/webp"/>
        </item>
        <item>
            <title><![CDATA[管理者画面への初回アクセスと環境設定]]></title>
            <link>https://testblog-6br.pages.dev/ja/admin-guide/admin-getting-started</link>
            <guid isPermaLink="true">https://testblog-6br.pages.dev/ja/admin-guide/admin-getting-started</guid>
            <pubDate>Wed, 15 Jul 2026 12:19:26 GMT</pubDate>
            <description><![CDATA[管理者がブログシステムを制御するために管理者アプリへ安全にアクセスし、ログインセッションを維持して、多言語UI辞書を活用する方法について案内します。]]></description>
            <content:encoded><![CDATA[<h1 id="管理者画面への初回アクセスと環境設定">🔑 管理者画面への初回アクセスと環境設定</h1>
<p>本ドキュメントは、ブログシステムを制御するために管理者が管理者（アドミン）ページへ安全にアクセスし、ログインセッションを維持して、画面上の様々なテキスト（多言語辞書）を簡単に管理・編集する方法を案内します。</p>
<hr>
<h2 id="1-管理者ログイン前のipセキュリティ制限ポリシー">🔒 1. 管理者ログイン前のIPセキュリティ制限ポリシー</h2>
<p>本ブログエンジンは、外部からの不正アクセスや投稿権限の乗っ取りを完璧に防ぐため、厳格な<strong>接続IPフィルタリング</strong>セキュリティポリシーを採用しています。デプロイ時に検出された管理者のパブリックIPアドレスのみが、サーバーの許可リスト（<code>ALLOWED_IP</code>）に登録され、アクセスが許可されます。</p>
<blockquote>
<p>[!IMPORTANT]</p>
<h3 id="安全地帯中心の運用原則公共の場所での使用禁止">🛡️ 安全地帯中心の運用原則（公共の場所での使用禁止）</h3>
<p>本ブログの管理者画面での操作は、必ず<strong>自宅や信頼できるオフィスなど、物理的・ネットワーク的なセキュリティが確保された場所</strong>でのみ行ってください。<br><strong>セキュリティ漏洩を防止するため、不特定多数が利用しハッキングのリスクに直接さらされるPC房（ネットカフェ）、図書館、公共Wi-Fi環境などの公共の場所では、管理者画面への接続自体を一切推奨せず、避けていただきますよう強くお願いいたします。</strong></p>
</blockquote>
<p>接続許可された安全な場所であっても、ルーターの再起動やインターネット回線の都合によりパブリックIPアドレスが変更され、<code>403 Forbidden</code>ブロック画面が表示された場合は、以下の手順に従って許可リストを手動で更新する必要があります。</p>
<h3 id="接続許可ipアドレスの更新手順再デプロイが必要">⚙️ 接続許可IPアドレスの更新手順（再デプロイが必要）</h3>
<ol>
<li>変更されたインターネット環境下でターミナルを開き、ブログサービスの管理者アプリを再度**再デプロイ（Redeploy）**するコマンドを実行します。</li>
<li><strong>作業プロセスの案内</strong>: データベース内部を直接編集してIPを強制的に書き込む極端な動作方法よりは安全ですが、再デプロイコマンドが実行されるたびにビルドプロセスとファイルアップロードの待機時間が数分間発生するため、実質的にはかなり面倒で手間の伴う作業です。</li>
<li>デプロイスクリプトの実行中に、現在接続されているコンピュータの新しいパブリックIPアドレスを検出し、リモートサーバーの許可IPリストを最新のものに更新します。</li>
<li>デプロイ処理が完了した後に管理者URLへ再アクセスすると、正常にログイン画面が再び有効になります。</li>
<li>管理者ページのみを再デプロイしてください。</li>
</ol>
<hr>
<h2 id="2-管理者ログインおよびセッション管理">🔑 2. 管理者ログインおよびセッション管理</h2>
<p>ログインのブロックが正常に解除されたら、初期構築時に設定した管理者マスターパスワード（<code>ADMIN_PASSWORD</code>）を入力して接続します。</p>
<ul>
<li><strong>安全なセッションの維持</strong>: ブラウザの専用セキュアクッキー設定を利用して、30日間管理者のログインセッションが安全に維持されます。</li>
<li><strong>公共のコンピュータおよび危険区域へのアクセス禁止</strong>:<br>セキュリティが保証されていないネットカフェや公共の共有デバイス環境では、管理者ログイン自体を試みないことが最も確実な防衛策です。万が一、やむを得ない事情で公共の環境からログインした場合は、作業終了直後に必ず管理者画面下部の**[ログアウト]**ボタンをクリックし、さらにターミナルで<code>npx wrangler logout</code>を実行して、そのコンピュータに記録されたCloudflare管理者アカウントの連携情報まで確実に解除（ログアウト）してください。</li>
</ul>
<blockquote>
<p>[!WARNING]</p>
<h3 id="管理者ページのアクセスurlドメイン露出の最小化を推奨">🔒 管理者ページのアクセスURL（ドメイン）露出の最小化を推奨</h3>
<p>管理者画面はIP許可リストにより二重で安全に保護されていますが、攻撃対象領域（Attack Surface）そのものを最小化することが最も堅牢なセキュリティ対策です。</p>
<p>そのため、<code>admin.myblog.com</code>のような分かりやすいカスタムドメインをわざわざ紐付けるのではなく、Cloudflare Pagesからデフォルトで提供されるランダムなサブドメインアドレス（例：<code>[プロジェクト名].pages.dev</code>）をそのまま使用し、管理者用のアクセス経路を秘密にしておくことを強く推奨します。</p>
</blockquote>
<hr>
<h2 id="3-多言語辞書i18n設定ガイド">🌐 3. 多言語辞書（i18n）設定ガイド</h2>
<p>記事の本文以外の、サイト画面に固定されている共通テキスト（メニューラベル、コメント送信ボタン、ログインプロンプトなど）は、ソースコードを1行も修正することなく、管理者の辞書編集機能を通じて英語、韓国語、日本語などに即時書き換えることができます。<br><figure data-align="left"><img src="https://sveltekitblog.com/images/posts/admin-getting-started/desktop/img-admin-guide-admin-getting-started-ja-002.webp" alt="img-admin-guide-admin-getting-started-ja-002" data-align="left" /></figure></p>
<p>新しい対応言語は、<strong><code>言語を追加</code></strong> 機能を使用して登録することができます。ただし、新しく追加した言語が画面上に正しく表示されるようにするには、既存の辞書に登録されているすべての翻訳キー（Key）に対して、該当言語の翻訳テキストを漏れなく入力しておく必要があります。<br><figure data-align="left"><img src="https://sveltekitblog.com/images/posts/admin-getting-started/desktop/img-admin-guide-admin-getting-started-ja-001.webp" alt="img-admin-guide-admin-getting-started-ja-001" data-align="left" /></figure></p>
<ol>
<li>管理者画面の左側メニューから**<code>言語設定</code>**メニューに移動します。</li>
<li>現在サイト全域で使用されている各翻訳キーと保存されたテキストの一覧が、下部の**<code>UI多言語辞書エディタ</code>**セクションに表示されます。</li>
<li>編集したいキーの入力フィールドに希望のテキスト（韓国語、英語、日本語）を入力し、その行の右端にある**<code>保存</code>**アイコンをクリックします。</li>
<li>保存と同時にライブサイトへ即時に反映され、訪問者がブログの言語を切り替えた際、変更後のテキストでリアルタイムかつスムーズに翻訳表示されます。</li>
</ol>
]]></content:encoded>
            <category>管理者ガイド</category>
            <enclosure url="https://sveltekitblog.com/images/posts/admin-getting-started/desktop/img-admin-guide-admin-getting-started-ja-002.webp" length="0" type="image/webp"/>
        </item>
        <item>
            <title><![CDATA[管理者機能とデュアルエディタの概要]]></title>
            <link>https://testblog-6br.pages.dev/ja/admin-guide/admin-core-features</link>
            <guid isPermaLink="true">https://testblog-6br.pages.dev/ja/admin-guide/admin-core-features</guid>
            <pubDate>Wed, 15 Jul 2026 12:19:17 GMT</pubDate>
            <description><![CDATA[多言語記事の同時保存、ビジュアルとMarkdownのデュアルエディタ切り替え、デバイス別独立ウィジェット配置など、管理者のコア機能を簡単に紹介します。]]></description>
            <content:encoded><![CDATA[<h1 id="管理者機能とデュアルエディタの概要">🎨 管理者機能とデュアルエディタの概要</h1>
<p>本ドキュメントは、多言語記事の同時パブリッシュ、デバイス別レイアウト設定、リアルタイムデザインテーマ設定など、管理者（管理画面）が提供する主要機能の基本構成と概要を紹介します。</p>
<hr>
<h2 id="1-多言語記事の作成およびデュアルエディタ">📝 1. 多言語記事の作成およびデュアルエディタ</h2>
<p>記事作成メニューに入ると、複数の言語タブが1つの画面に並んだ多言語作成環境が提供されます。</p>
<h3 id="多言語記事の一括作成と保存">① 多言語記事の一括作成と保存</h3>
<ul>
<li><strong>作成フロー</strong>: 上部の言語タブ（KO、EN、JAなど）を切り替えながら、各言語に応じたタイトル、概要、スラッグ（URLパス）、本文を入力します。</li>
<li><strong>一括保存</strong>: 下部の <strong>[すべてのタブを同時に保存する]</strong> ボタンをクリックすると、入力したすべての言語の記事がデータベースに一括で保存されます。 <strong>「公開」</strong> 設定がされていない言語は自動的に <strong>「下書き」</strong> として保存され、入力されていない言語タブはデータが生成されずにスキップされます。</li>
</ul>
<h3 id="デュアルエディタのサポートビジュアル-vs-markdown">② デュアルエディタのサポート（ビジュアル vs Markdown）</h3>
<ul>
<li><strong>ビジュアルHTMLエディタ（Visual）</strong>: 一般的なWebエディタのように、書式を設定したりメディアを埋め込んだりして直感的に作成する方式です。画像ファイルはツールバーのアップロードボタンから簡単に挿入できます。</li>
</ul>
<figure data-align="left"><img src="https://sveltekitblog.com/images/posts/admin-core-features/desktop/img-admin-guide-admin-core-features-ja-003.webp" alt="img-admin-guide-admin-core-features-ja-003" data-align="left" /></figure><ul>
<li><strong>Markdownエディタ（Markdown）</strong>: Markdown構文を好むユーザー向けに提供されています。タイトル、概要、カテゴリ、タグなどの情報は、ヘッダー領域にYAML Front Matter（<code>---</code>）形式で自動的に管理されます。保存時には、Markdownソースと変換後のHTML本文が一緒に保存されます。</li>
</ul>
<figure data-align="left"><img src="https://sveltekitblog.com/images/posts/admin-core-features/desktop/img-admin-guide-admin-core-features-ja-004.webp" alt="img-admin-guide-admin-core-features-ja-004" data-align="left" /></figure><h3 id="代表画像サムネイルの自動指定">③ 代表画像（サムネイル）の自動指定</h3>
<ul>
<li>サムネイル画像が手動で指定されていない場合、システムが本文を分析して代表画像を自動的に設定します。<ul>
<li><strong>第1優先</strong>: 本文内で最初に使用されている画像。</li>
<li><strong>第2優先</strong>: 本文に画像がなく、YouTubeのリンクやiframe埋め込みが含まれている場合、その動画の公式高解像度サムネイルURL。</li>
</ul>
</li>
</ul>
<hr>
<h2 id="2-デバイス別デスクトップモバイルの独立ウィジェット配置">🧱 2. デバイス別（デスクトップ/モバイル）の独立ウィジェット配置</h2>
<p>レイアウトエディタでは、ドラッグ＆ドロップでサイドバーや本文領域のウィジェットカードの並び順を調整できます。また、訪問者のデバイスタイプ（デスクトップまたはモバイル）に応じて、独立したウィジェット露出設定を構成できます。<br><figure data-align="left"><img src="https://sveltekitblog.com/images/posts/admin-core-features/desktop/img-admin-guide-admin-core-features-ja-002.webp" alt="img-admin-guide-admin-core-features-ja-002" data-align="left" data-caption="デスクトップ・モバイルモード選択アイコン" /><figcaption>デスクトップ・モバイルモード選択アイコン</figcaption></figure></p>
<h3 id="デバイス別のウィジェット表示条件">⚙️ デバイス別のウィジェット表示条件</h3>
<ol>
<li><strong><code>デザインエディタ</code></strong> メニュー内の <strong><code>ブログ構造</code></strong> タブから、全体のレイアウト構成と横幅の比率を選択します。</li>
<li>各ウィジェットの追加・編集時に、露出デバイス条件（Device）を指定できます。<ul>
<li><strong>Desktop (デスクトップ専用)</strong>: PC画面にのみウィジェットを表示し、モバイル接続時には不要なリソースの読み込みを遮断します。</li>
<li><strong>Mobile (モバイル専用)</strong>: PC画面では非表示にし、スマートフォンやタブレットなどのモバイル解像度でのみ表示されるように制限します。</li>
</ul>
</li>
<li><strong>効果</strong>: モバイルユーザーに対して不要または重いウィジェットのレンダリングや読み込みをスキップすることで、ページの読み込み速度とスクロールパフォーマンスを最適化します。</li>
</ol>
<hr>
<h2 id="3-リアルタイムデザインエディタとテーマ設定">🎨 3. リアルタイムデザインエディタとテーマ設定</h2>
<p>デザインエディタで変更したテーマカラーやスタイル情報は、サーバーの再ビルドやデプロイを行わずに、保存後すぐに訪問者のブラウザのCSS変数へ反映されます。</p>
<ul>
<li><strong>4種類の背景タイプ</strong>: 単色（Solid）、グラデーション（Gradient）、背景画像（Image）、HTML5 Canvasアニメーション（Canvas）から選択できます。</li>
<li><strong>グラスモフィズム効果</strong>: 背景画像を使用する際、テキストの視認性を確保するために不透明度（Opacity）とぼかし（Blur）を調整するガラス越しのような効果の設定をサポートしています。</li>
<li><strong>インタラクティブCanvas</strong>: サンドボックスで隔離された安全なキャンバス内で、雪、波、星座などの動きのあるモーションアートワークを背景として駆動できます。</li>
</ul>
<blockquote>
<p>[!TIP]<br>背景設定オプションの種類とサンプルスクリプトは、<strong><a href="./admin-design-editor.md">デザインエディタ設定の紹介</a></strong> 文書でご確認いただけます。</p>
</blockquote>
<hr>
<h2 id="4-メディアストレージのサポート">💾 4. メディアストレージのサポート</h2>
<p>本文で使用するメディアが保存されるクラウドストレージを、必要に応じて柔軟に切り替えて運用できます。<br><figure data-align="left"><img src="https://sveltekitblog.com/images/posts/admin-core-features/desktop/img-admin-guide-admin-core-features-ja-005.webp" alt="img-admin-guide-admin-core-features-ja-005" data-align="left" /></figure></p>
<ol>
<li><strong><code>メディアライブラリ</code></strong> メニュー下部の <strong><code>ストレージ設定</code></strong> 領域へ移動します。</li>
<li>連携するストレージタイプを選択し、情報を入力します。<ul>
<li><strong>Cloudflare KV</strong>: エッジノードのKVストレージにアセットを保管し、迅速に配信します。</li>
<li><strong>Cloudflare R2</strong>: 大容量オブジェクトストレージを使用し、コスト効率よく画像を管理します。</li>
<li><strong>Supabase Storage</strong>: Supabaseのストレージバケットパスにメディアをアップロードして安全に保管します.</li>
<li><strong>ImageKit.io</strong>: グローバル画像CDNプラットフォームと連携し、アップロードした画像の解像度最適化や圧縮処理を活用します。</li>
</ul>
</li>
<li><strong>ストレージのリアルタイム切り替え</strong>: 変更を保存すると、アップロードエンジンが指定したストレージへ即座に切り替わります。</li>
</ol>
<blockquote>
<p>[!NOTE]<br><strong>標準メディアストレージ (Cloudflare KV) のエクスプローラー機能の制限</strong><br>Cloudflare KVは、Workersの使用量とコストの最適化のため、画像エクスプローラー内での一覧表示（読み取りAPIの呼び出し）をサポートしていません。一方、Cloudflare R2、Supabase Storage、ImageKit.ioなどの外部ストレージを設定して接続した場合は、アップロードされた画像の一覧表示やプレビュー機能をすべて利用できます。</p>
</blockquote>
]]></content:encoded>
            <category>管理者ガイド</category>
            <enclosure url="https://sveltekitblog.com/images/posts/admin-core-features/desktop/img-admin-guide-admin-core-features-ja-003.webp" length="0" type="image/webp"/>
        </item>
        <item>
            <title><![CDATA[リアルタイムデザインエディタと背景演出の概要]]></title>
            <link>https://testblog-6br.pages.dev/ja/admin-guide/admin-design-editor</link>
            <guid isPermaLink="true">https://testblog-6br.pages.dev/ja/admin-guide/admin-design-editor</guid>
            <pubDate>Wed, 15 Jul 2026 12:19:07 GMT</pubDate>
            <description><![CDATA[再ビルドと再デプロイ不要の1秒リアルタイムテーマ反映]]></description>
            <content:encoded><![CDATA[<h1 id="リアルタイムデザインエディタと背景演出の概要">🎨 リアルタイムデザインエディタと背景演出の概要</h1>
<p>本ドキュメントは、再ビルドや再デプロイを必要とせずにテーマ設定を即座に反映するシステムと、4つの背景設定（単一色、グラデーション、背景画像、カスタムJavaScriptキャンバス）の基本構成について紹介します。</p>
<hr>
<h2 id="1-リアルタイムデザインエディタの概要">🌌 1. リアルタイムデザインエディタの概要</h2>
<p>デザインエディタで設定値を変更して保存する際、ブログサーバーやCDNサービスを再ビルド・再デプロイする必要は一切ありません。保存するとデータベースが即座に更新され、<strong>訪問者のブラウザ画面にCSS変数と背景レンダリングモジュールが1秒で反映</strong>されます。<br>ブログに最終適用する前に、管理者ページ内のプレビュー画面でデザインの変化をあらかじめ確認することができます。</p>
<blockquote>
<p>[!WARNING]</p>
<h3 id="プレビューと実際のブログデザインの差異に関する注意点">⚠️ プレビューと実際のブログデザインの差異に関する注意点</h3>
<p>デザインエディタの<strong>プレビュー機能は、実際の訪問者画面と100%同一のレンダリング環境を保証するものではありません。</strong> ブラウザによるCSS変数の解釈やJavaScript Canvasの描画条件などにより、管理者ページのプレビューと実際のブログ表示に微細な差異が生じる場合があります。</p>
<p>それゆえ、デザイン設定を変更した後は、<strong>必ず実際のブログのメイン画面にアクセスし、再読み込み（F5）を行って最終的な表示状態を確認</strong>することを強く推奨します。</p>
</blockquote>
<hr>
<h3 id="レイアウトテーマ設定項目">🧱 レイアウト＆テーマ設定項目</h3>
<h4 id="レイアウト構成">① レイアウト構成</h4>
<ul>
<li><strong>ブログ基本構造</strong>: メインページの基本レイアウト構造（例：2カラム、3カラムなど）を選択できます。</li>
<li><strong>カラム幅の比率</strong>: メインコンテンツエリアとサイドバーカラムの横幅比率（例：1:2:1、2:1:2など）を調整します。</li>
<li><strong>最大横幅</strong>: ブログ画面全体の最大横幅解像度（例：1200px、1400pxなど）を制限します。</li>
<li><strong>レイアウト詳細設定</strong>: 左右の余白（Side Margin）、カードコンテナの角の丸み（Border Radius）、立体感を与えるシャドウ（Box Shadow）効果を細かく制御します。</li>
</ul>
<h4 id="テーマ基本配色">② テーマ基本配色</h4>
<ul>
<li>メインテーマ色（Primary）、サブ色（Secondary）、本文テキスト色（Text）、アクセント強調色（Accent）、カード背景色（Card Bg）、および枠線色（Border）をカスタマイズできます。</li>
</ul>
<h4 id="タイポグラフィフォント設定">③ タイポグラフィ（フォント）設定</h4>
<ul>
<li>Google Fontsのフォント一覧から任意のフォントファミリー名を直接入力して適用し、本文のフォント種類や基本文字サイズ（Base Font Size）を設定できます。</li>
</ul>
<h4 id="ウィジェットの配置・順序管理">④ ウィジェットの配置・順序管理</h4>
<ul>
<li>使用可能なウィジェットをドラッグ＆ドロップで各カラムに自由に配置し、表示順序を調整できます。</li>
</ul>
<hr>
<h2 id="2-デバイス別の独立ウィジェット配置">🧱 2. デバイス別の独立ウィジェット配置</h2>
<p>レイアウト設計時には、ドラッグ＆ドロップでサイドバーや本文領域のウィジェット位置と順序を調整できます。また、訪問者の画面サイズ（デバイスタイプ）に応じてレイアウトを動的に切り替えるフィルタリング機能を提供します。</p>
<ul>
<li><strong>Desktop (デスクトップ専用)</strong>: 画面幅の広いPC環境でのみ、指定されたウィジェット（例：タグクラウド、カテゴリツリーなど）を表示します。モバイル接続時には、読み込み速度を維持するため、HTML送信段階で該当要素をあらかじめ除外します。</li>
<li><strong>Mobile (モバイル専用)</strong>: デスクトップ環境では非表示にし、スマートフォンなどのモバイル解像度でのみモバイル向けに表示されるよう制限します。</li>
</ul>
<hr>
<h2 id="3-4つの背景設定の種類">🌈 3. 4つの背景設定の種類</h2>
<p>ブログ全体の雰囲気を決定する背景画面として、以下の4つのタイプがサポートされています。</p>
<h3 id="solid単一色背景">① Solid（単一色背景）</h3>
<ul>
<li>任意のHEXコード（例： <code>#3b82f6</code>）またはHSLカラーコードを指定して、最もシンプルで記事本文に集中しやすい画面を構成します。</li>
</ul>
<h3 id="gradientグラデーション背景">② Gradient（グラデーション背景）</h3>
<ul>
<li><strong>グラデーションビルダー</strong>ツールを使用して、複数のカラーがスムーズに混ざり合う線形グラデーションを作成します。</li>
<li>角度（Direction）スライダーの調整やカラーストップ（Stops）の追加・移動により、開始色、中間色、終了色を自由に配合できます。</li>
</ul>
<h3 id="image背景画像グラスモフィズム効果">③ Image（背景画像＆グラスモフィズム効果）</h3>
<ul>
<li>背景に使用する画像のURLを入力するか、アップロード（Upload）ボタンを使用してメディアファイルをライブラリに追加します。</li>
<li><strong>アップロード画像の最適化</strong>: 画像を登録すると、システムが自動的にWebP形式へ変換し、読み込みパフォーマンスの低下を防ぎます。</li>
<li><strong>グラスモフィズム効果</strong>: 背景画像がテキストの視認性を妨げないよう、以下の設定を組み合わせて半透明なガラス窓風のスタイルを構成できます。</li>
</ul>
<table>
<thead>
<tr>
<th align="left">設定項目</th>
<th align="center">推奨値の範囲</th>
<th align="left">詳細説明</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>背景のぼかし (Glass Blur)</strong></td>
<td align="center"><code>5px ~ 15px</code></td>
<td align="left">本文カードの下部レイヤーに半透明なすりガラス風のフィルターを適用します。</td>
</tr>
<tr>
<td align="left"><strong>オーバーレイ不透明度 (Overlay Opacity)</strong></td>
<td align="center"><code>10% ~ 30%</code></td>
<td align="left">カードコンテナの背後に敷く半透明マスクの不透明度を決定します。</td>
</tr>
<tr>
<td align="left"><strong>オーバーレイ色 (Overlay Color)</strong></td>
<td align="center"><code>#000000</code> または <code>#ffffff</code></td>
<td align="left">マスクの色をダーク（暗）またはライト（明）に設定し、テキストのコントラストを確保します。</td>
</tr>
</tbody></table>
<h3 id="custom-javascriptカスタムjavascriptcanvas背景">④ Custom JavaScript（カスタムJavaScript＆Canvas背景）</h3>
<ul>
<li>クライアントのブラウザ背景にインタラクティブなCanvasアニメーションを描画するため、カスタムJavaScriptの入力をサポートします。</li>
<li>背景キャンバス要素（<code>canvas id=&quot;bg-canvas&quot;</code>）に対して直接描画を制御できます。連動するセキュリティ制限や省電力ポリシーの詳細は、以下の <strong>[4. カスタムJavaScript連携仕様]</strong> で説明します。</li>
</ul>
<figure data-align="center"><img src="https://sveltekitblog.com/images/posts/admin-design-editor/desktop/img-admin-guide-admin-design-editor-ja-002.webp" alt="img-admin-guide-admin-design-editor-ja-002" data-align="center" data-caption="セキュリティサンドボックス搭載のCustom JSキャンバスアニメーション入力画面（有効化前）" /><figcaption>セキュリティサンドボックス搭載のCustom JSキャンバスアニメーション入力画面（有効化前）</figcaption></figure>
<figure data-align="center"><img src="https://sveltekitblog.com/images/posts/admin-design-editor/desktop/img-admin-guide-admin-design-editor-ja-001.webp" alt="img-admin-guide-admin-design-editor-ja-001" data-align="center" data-caption="セキュリティサンドボックス搭載のCustom JSキャンバスアニメーション入力画面（有効化後）" /><figcaption>セキュリティサンドボックス搭載のCustom JSキャンバスアニメーション入力画面（有効化後）</figcaption></figure><hr>
<h2 id="4-カスタムjavascript連携仕様">⚡ 4. カスタムJavaScript連携仕様</h2>
<p>セキュリティリスクを防止し、モバイル端末のバッテリー消費を抑えるため、以下のサンドボックス環境および省電力システムが適用されています。</p>
<h3 id="サンドボックスcspによるセキュリティ制限">🔒 サンドボックス＆CSPによるセキュリティ制限</h3>
<p>悪意あるスクリプトの混入による脆弱性を防ぐため、以下のセキュリティ対策が常時稼働しています。</p>
<ol>
<li><strong>隔離されたサンドボックス構造</strong>: キャンバスコードは、スクリプト実行のみが許可された隔離された <code>iframe</code> 内で動作します。親ページのDOMノードやログイン情報などへのアクセスは技術的に遮断されます。</li>
<li><strong>コンテンツセキュリティポリシー (CSP) 制限</strong>: 外部サーバーへのネットワーク通信や外部スクリプトの呼び出しが全面的に遮断されます。これにより、外部サーバーへデータを流出させることが不可能です。</li>
<li><strong>JS APIの制限</strong>: セキュリティ上のリスクとなる <code>fetch</code>, <code>XMLHttpRequest</code>, <code>WebSocket</code>, <code>eval</code>, <code>new Function</code>, <code>document.cookie</code>, <code>localStorage</code> などのAPIが検出された場合、検証スクリプト（<code>jsValidator.ts</code>）によって自動的にコメント処理（<code>/* f_e_t_c_h (blocked) */</code>）されます。</li>
</ol>
<h3 id="バッテリー消費の最適化省電力技術">🔋 バッテリー消費の最適化（省電力技術）</h3>
<ul>
<li><strong>画面外での自動一時停止</strong>: ユーザーがスクロールして背景アニメーションが完全に視野から外れると、ブラウザが不要にCPU/GPUを消費しないよう、レンダリングループを <strong>スリープ状態（一時停止）</strong> に切り替えます。再び画面に表示されると、即座に再開されます。</li>
<li><strong>モバイル用フレーム制限</strong>: スマートフォンやタブレットでアクセスする場合、発熱を抑えるためデフォルトで背景スクリプトの実行を停止します。<strong>「モバイル端末でもアニメーションを実行」</strong> オプションを明示的に有効にした場合は実行が可能となり、その際もパフォーマンスに負荷がかからないようパーティクル数を自動的に削減する制御パラメータが適用されます。</li>
</ul>
<hr>
<h2 id="5-サンプルスクリプト3種">📝 5. サンプルスクリプト3種</h2>
<p>背景設定メニューから <strong>[Custom JavaScript]</strong> を選択し、以下のコードのいずれかをコピーして適用できます。</p>
<blockquote>
<p>[!NOTE]<br>スクリプトを作成する際は、描画対象のキャンバス要素を取得するために必ず <code>document.getElementById(&#39;bg-canvas&#39;)</code> のID名を使用してください。</p>
</blockquote>
<h3 id="サンプルa-冬の雪snowfall">❄️ サンプルA. 冬の雪（Snowfall）</h3>
<p>画面上部から雪の結晶がゆっくりと舞い落ちる背景アニメーションです。</p>
<pre><code class="language-javascript">(function() {
  const canvas = document.getElementById(&#39;bg-canvas&#39;);
  if (!canvas) return;
  const ctx = canvas.getContext(&#39;2d&#39;);
  
  let width = canvas.width = window.innerWidth;
  let height = canvas.height = window.innerHeight;
  
  // モバイル省電力の判定
  const divisor = (window.bgConfig &amp;&amp; window.bgConfig.mobileThrottleDivisor) || 1;
  const maxSnowflakes = Math.floor(100 / divisor);
  const snowflakes = [];
  
  class Snowflake {
    constructor() {
      this.reset();
      this.y = Math.random() * height; // 初期ランダム高度
    }
    
    reset() {
      this.x = Math.random() * width;
      this.y = -10;
      this.radius = Math.random() * 3 + 1;
      this.speed = Math.random() * 1 + 0.5;
      this.opacity = Math.random() * 0.6 + 0.2;
    }
    
    update() {
      this.y += this.speed;
      // 緩やかな揺らぎ
      this.x += Math.sin(this.y / 30) * 0.5;
      
      if (this.y &gt; height || this.x &lt; 0 || this.x &gt; width) {
        this.reset();
      }
    }
    
    draw() {
      ctx.beginPath();
      ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
      ctx.fillStyle = `rgba(25, 25, 255, ${this.opacity})`;
      ctx.fill();
    }
  }
  
  // パーティクルの生成
  for (let i = 0; i &lt; maxSnowflakes; i++) {
    snowflakes.push(new Snowflake());
  }
  
  function animate() {
    ctx.clearRect(0, 0, width, height);
    
    for (let i = 0; i &lt; snowflakes.length; i++) {
      snowflakes[i].update();
      snowflakes[i].draw();
    }
    requestAnimationFrame(animate);
  }
  
  // リサイズハンドラ
  window.addEventListener(&#39;resize&#39;, () =&gt; {
    width = canvas.width = window.innerWidth;
    height = canvas.height = window.innerHeight;
  });
  
  animate();
})();
</code></pre>
<hr>
<h3 id="サンプルb-星座ネットワークconstellation-network">🕸️ サンプルB. 星座ネットワーク（Constellation Network）</h3>
<p>浮遊するノードパーティクル同士が近づいた際に、薄い半透明の線で結ばれ網状のネットワークを形成する背景アニメーションです。</p>
<pre><code class="language-javascript">(function() {
  const canvas = document.getElementById(&#39;bg-canvas&#39;);
  if (!canvas) return;
  const ctx = canvas.getContext(&#39;2d&#39;);
  
  let width = canvas.width = window.innerWidth;
  let height = canvas.height = window.innerHeight;
  
  const divisor = (window.bgConfig &amp;&amp; window.bgConfig.mobileThrottleDivisor) || 1;
  const particleCount = Math.floor(80 / divisor);
  const particles = [];
  const connectionDistance = 100;
  
  class Particle {
    constructor() {
      this.x = Math.random() * width;
      this.y = Math.random() * height;
      this.vx = (Math.random() - 0.5) * 0.8;
      this.vy = (Math.random() - 0.5) * 0.8;
      this.radius = Math.random() * 2 + 1.5;
    }
    
    update() {
      this.x += this.vx;
      this.y += this.vy;
      
      // 境界での反射
      if (this.x &lt; 0 || this.x &gt; width) this.vx *= -1;
      if (this.y &lt; 0 || this.y &gt; height) this.vy *= -1;
    }
    
    draw() {
      ctx.beginPath();
      ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
      ctx.fillStyle = &#39;rgba(99, 102, 241, 0.4)&#39;; // パステルインディゴ
      ctx.fill();
    }
  }
  
  for (let i = 0; i &lt; particleCount; i++) {
    particles.push(new Particle());
  }
  
  function drawLines() {
    for (let i = 0; i &lt; particles.length; i++) {
      for (let j = i + 1; j &lt; particles.length; j++) {
        const dx = particles[i].x - particles[j].x;
        const dy = particles[i].y - particles[j].y;
        const dist = Math.sqrt(dx * dx + dy * dy);
        
        if (dist &lt; connectionDistance) {
          const alpha = (connectionDistance - dist) / connectionDistance * 0.18;
          ctx.beginPath();
          ctx.moveTo(particles[i].x, particles[i].y);
          ctx.lineTo(particles[j].x, particles[j].y);
          ctx.strokeStyle = `rgba(99, 102, 241, ${alpha})`;
          ctx.lineWidth = 1;
          ctx.stroke();
        }
      }
    }
  }
  
  function animate() {
    ctx.clearRect(0, 0, width, height);
    for (let i = 0; i &lt; particles.length; i++) {
      particles[i].update();
      particles[i].draw();
    }
    drawLines();
    requestAnimationFrame(animate);
  }
  
  window.addEventListener(&#39;resize&#39;, () =&gt; {
    width = canvas.width = window.innerWidth;
    height = canvas.height = window.innerHeight;
  });
  
  animate();
})();
</code></pre>
<hr>
<h3 id="サンプルc-穏やかな波fluid-sine-waves">🌊 サンプルC. 穏やかな波（Fluid Sine Waves）</h3>
<p>画面下部でパステル調の半透明な複数の波が緩やかに揺れる背景アニメーションです。</p>
<pre><code class="language-javascript">(function() {
  const canvas = document.getElementById(&#39;bg-canvas&#39;);
  if (!canvas) return;
  const ctx = canvas.getContext(&#39;2d&#39;);
  
  let width = canvas.width = window.innerWidth;
  let height = canvas.height = window.innerHeight;
  
  let wave1 = {
    y: height * 0.85,
    length: 0.005,
    amplitude: 25,
    frequency: 0.012
  };
  
  let wave2 = {
    y: height * 0.88,
    length: 0.008,
    amplitude: 15,
    frequency: 0.022
  };
  
  let increment = 0;
  
  function animate() {
    ctx.clearRect(0, 0, width, height);
    
    // 背景の波の描画 (半透明ティール)
    ctx.beginPath();
    ctx.moveTo(0, height);
    for (let i = 0; i &lt; width; i++) {
      ctx.lineTo(i, wave1.y + Math.sin(i * wave1.length + increment) * wave1.amplitude);
    }
    ctx.lineTo(width, height);
    ctx.fillStyle = &#39;rgba(45, 212, 191, 0.1)&#39;;
    ctx.fill();
    
    // 手前の波の描画 (半透明スカイブルー)
    ctx.beginPath();
    ctx.moveTo(0, height);
    for (let i = 0; i &lt; width; i++) {
      ctx.lineTo(i, wave2.y + Math.sin(i * wave2.length - increment * 1.5) * wave2.amplitude);
    }
    ctx.lineTo(width, height);
    ctx.fillStyle = &#39;rgba(56, 189, 248, 0.15)&#39;;
    ctx.fill();
    
    // デバイスの設定に応じた速度の調整
    const speedFactor = (window.bgConfig &amp;&amp; window.bgConfig.mobileThrottleDivisor) ? 0.3 : 1;
    increment += wave1.frequency * speedFactor;
    
    requestAnimationFrame(animate);
  }
  
  window.addEventListener(&#39;resize&#39;, () =&gt; {
    width = canvas.width = window.innerWidth;
    height = canvas.height = window.innerHeight;
    wave1.y = height * 0.85;
    wave2.y = height * 0.88;
  });
  
  animate();
})();
</code></pre>
<hr>
<h2 id="6-モバイル個別背景設定">📱 6. モバイル個別背景設定</h2>
<p>利用環境に合わせて背景設定を変えることで、システムのパフォーマンスを最適化できます。</p>
<ol>
<li><strong>デスクトップ背景</strong>: PC環境では <strong>Custom JavaScript</strong> を選択し、任意のCanvasアニメーションを実行できます。</li>
<li><strong>モバイル個別背景チェック</strong>: <strong>「モバイルデバイスでデスクトップと異なる背景設定を使用する」</strong> トグルを有効にします。</li>
<li><strong>モバイル背景の軽量化</strong>: 独立したモバイル設定パネルから背景を <strong>Solid</strong> または <strong>Gradient</strong> に設定し、モバイル端末のプロセッサ負荷を最小限に抑えます。</li>
<li><strong>効果</strong>: スマートフォン接続時には軽量な背景を選択することでバッテリーを保護し、PC接続時には豊かなビジュアルモーション効果を最大限に表現するというイネーブル化が可能です。</li>
</ol>
]]></content:encoded>
            <category>管理者ガイド</category>
            <enclosure url="https://sveltekitblog.com/images/posts/admin-design-editor/desktop/img-admin-guide-admin-design-editor-ja-002.webp" length="0" type="image/webp"/>
        </item>
        <item>
            <title><![CDATA[管理者のよくある質問 (FAQ) およびトラブルシューティング]]></title>
            <link>https://testblog-6br.pages.dev/ja/admin-guide/admin-faq</link>
            <guid isPermaLink="true">https://testblog-6br.pages.dev/ja/admin-guide/admin-faq</guid>
            <pubDate>Wed, 15 Jul 2026 12:18:57 GMT</pubDate>
            <description><![CDATA[インストールやデプロイ中に発生するエラーの解決、バージョンアップグレード、画像ストレージの必須キー一覧、およびIPセキュリティによるアクセス制限の解除方法を確認します。]]></description>
            <content:encoded><![CDATA[<img src="https://sveltekitblog.com/images/posts/admin-faq/desktop/img-admin-guide-admin-faq-ko-001.webp" alt="img-admin-guide-admin-faq-ko-001" data-align="center"><h1 id="管理者のよくある質問-faq-およびトラブルシューティング">❓ 管理者のよくある質問 (FAQ) およびトラブルシューティング</h1>
<p>本ドキュメントは、ブログのインストール、デプロイ、および運営段階で発生する可能性のある主なトラブルシューティング解決策と、安全な最新バージョンへのアップグレードガイドを提供します。</p>
<hr>
<h2 id="q1-最新バージョンへ安全にアップグレードupgradeするかデータを復元する方法を教えてください">🗄️ Q1. 最新バージョンへ安全にアップグレード（Upgrade）するか、データを復元する方法を教えてください。</h2>
<p>データベースの整合性を維持し、予期しないビルドクラッシュを防ぐため、以下の安全なアップグレード手順を遵守することを強く推奨します。</p>
<h3 id="1-安全な最新バージョンへのアップグレード検証手順推奨">1. 安全な最新バージョンへのアップグレード検証手順（推奨）</h3>
<p>既存のデータベースとのスキーマ不一致やテンプレート変更に伴う衝突を予防するため、本番サーバーに直接適用するのではなく、まず隔離されたテスト環境で安全性を確認してください。（Gitを使用せずファイルを直接ダウンロードして使用するユーザーも同様です）</p>
<ol>
<li><strong>設定ファイルのバックアップ</strong>:<ul>
<li>現在の開発フォルダ内にある <code>wrangler.backup.json</code> と <code>.dev.vars</code> ファイルを安全な一時フォルダにコピーして保管します。</li>
<li><em>※ <code>.dev.vars</code> ファイルは重要な認証情報（パスワード、OAuthキーなど）を含みますが、セキュリティ保護のためGit管理対象外（<code>.gitignore</code>）に指定されています。そのため、コードの更新時や再取得時に紛失しやすいため、必ずバックアップを取ってください。</em></li>
</ul>
</li>
<li><strong>稼働中のデータバックアップ</strong>:<ul>
<li>現在稼働しているブログ管理画面の <code>コンテンツバックアップ</code> メニューから、記事全体および設定バックアップファイルをダウンロードします。</li>
</ul>
</li>
<li><strong>隔離されたフォルダに新バージョンをクローン/ダウンロード</strong>:<ul>
<li>既存のフォルダを上書きせず、<strong>完全に新しいディレクトリ</strong>に最新リリースのソースコードをダウンロードまたはクローンします。</li>
</ul>
</li>
<li><strong>一時テストサーバーのデプロイ</strong>:<ul>
<li>新しいフォルダに移動し、依存関係のインストール（<code>npm install</code>）を行い、一時的なテスト用データベースを構築してテストデプロイを実行します。</li>
</ul>
</li>
<li><strong>バックアップ復元テスト</strong>:<ul>
<li>新しくデプロイされたテスト用管理画面にアクセスし、ステップ2で取得したバックアップファイルをロードして、データが漏れなく正常に表示され、エラーが発生しないかを徹底的に検証します。</li>
</ul>
</li>
<li><strong>本番サーバーへの正式アップデート適用</strong>:<ul>
<li>テストサーバーですべての機能が正常に動作することを確認した後でのみ、元の開発フォルダに戻り、コードを更新（<code>git pull</code> またはソースコードの上書き）して、各アプリの個別デプロイコマンド（<code>npm run deploy:blog</code>、<code>npm run deploy:admin</code>）を実行し、正式なアップグレードを完了させます。</li>
</ul>
</li>
</ol>
<h3 id="2-データ紛失時の最終手段">2. データ紛失時の最終手段</h3>
<ul>
<li><strong><code>npm run restore</code> コマンドの使用</strong>:<ul>
<li>インフラ設定やデータベースが深刻に破損し、根本的な復旧が必要な場合にのみ、バックアップしておいた <code>wrangler.backup.json</code> ファイルを基にインフラ全体を再構築する**「最後のデータ復旧手段」**として実行してください。</li>
</ul>
</li>
</ul>
<hr>
<h2 id="q2-r2supabaseimagekitへの画像ストレージ変更時に入力すべき必須キーは何ですか">📂 Q2. R2、Supabase、ImageKitへの画像ストレージ変更時に入力すべき必須キーは何ですか？</h2>
<p>各外部画像ストレージ使用時に入力漏れのないよう確認すべき必須環境変数および設定のチェックリストです。</p>
<table>
<thead>
<tr>
<th align="left">ストレージタイプ</th>
<th align="left">必須入力項目 / 設定</th>
<th align="left">説明</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>Cloudflare R2</strong></td>
<td align="left"><code>IMAGES</code> R2バケットバインディング</td>
<td align="left"><code>wrangler.json</code> 内のR2バケット情報とのマッピングが必要です。</td>
</tr>
<tr>
<td align="left"><strong>Supabase Storage</strong></td>
<td align="left"><code>supabase_storage_url</code><br><code>supabase_storage_key</code><br><code>supabase_storage_bucket</code></td>
<td align="left">API URLおよびService Role Keyが正確である必要があり、Supabase Storage内のバケット権限ポリシーが <strong>Public（公開）</strong> に設定されていなければ画像が表示されません。</td>
</tr>
<tr>
<td align="left"><strong>ImageKit.io</strong></td>
<td align="left"><code>imagekit_url_endpoint</code><br><code>imagekit_public_key</code><br><code>imagekit_private_key</code></td>
<td align="left">エンドポイントURLの形式を確認し、クロスオリジンリソース共有（CORS）の許可を設定する必要があります。</td>
</tr>
</tbody></table>
<hr>
<h2 id="q3-ダッシュボードの統計グラフがデモデータのみで表示されます">📊 Q3. ダッシュボードの統計グラフがデモデータのみで表示されます。</h2>
<ul>
<li><strong>原因</strong>: Google Analytics 4 (GA4) APIの環境変数が入力されていない、または無効な場合、ダッシュボードのクラッシュを防ぐためにデモ用のダミーデータが代替表示されます。</li>
<li><strong>設定手順</strong>: Cloudflare Pages設定に以下の環境変数を入力し、再デプロイを行ってください。<ul>
<li><code>GA4_PROPERTY_ID</code>: Google Analytics プロパティ ID</li>
<li><code>GA4_CLIENT_EMAIL</code>: Google Cloud サービスアカウントメールアドレス</li>
<li><code>GA4_PRIVATE_KEY</code>: Google サービスアカウント秘密鍵</li>
</ul>
</li>
<li><strong>注意点</strong>: <code>.dev.vars</code> ファイルに <code>GA4_PRIVATE_KEY</code> を記述する際、改行コード（<code>\n</code>）の形式が崩れないよう、必ず鍵文字列全体を二重引用符（<code>&quot;</code>）で囲って登録してください。</li>
</ul>
<hr>
<h2 id="q4-初回setupデプロイ後管理画面にアクセスできなかったり一部のデータ同期が不足しているようです">⚡ Q4. 初回setupデプロイ後、管理画面にアクセスできなかったり、一部のデータ同期が不足しているようです。</h2>
<ul>
<li><strong>原因</strong>: ワンクリックセットアップ時に環境変数の設定が正常に完了していたとしても、一時的なネットワーク接続エラーやシステム上の不具合により、暗号化キーや秘密環境変数（Secrets）の同期が一部抜けてしまったり、データが破損した状態でデプロイが進行することがあります。</li>
<li><strong>解決策</strong>: 各アプリの <code>.dev.vars</code> 設定値の状態を再度確認した上で、各フォルダに移動し、手動で個別のデプロイコマンド <code>npm run deploy:blog</code> と <code>npm run deploy:admin</code> をそれぞれ <strong>1回実行</strong> してください。ローカルの <code>.dev.vars</code> ファイルに入力された秘密環境変数群が正常に上書きされることで、問題が解決します。</li>
</ul>
<hr>
<h2 id="q5-管理画面へのアクセス時にforbidden-ip-not-allowedまたは403エラーが表示されます">🔒 Q5. 管理画面へのアクセス時に「Forbidden (IP Not Allowed)」または403エラーが表示されます。</h2>
<p>本ブログのセキュリティ仕様上、デプロイスクリプトの実行時にデプロイを行っているPCのグローバルIPアドレスを自動的に検知し、アクセス許可IP（<code>ALLOWED_IP</code>）としてPages Secretに自動挿入します。</p>
<h3 id="管理者セキュリティ推奨事項">⚠️ 管理者セキュリティ推奨事項</h3>
<ul>
<li>アカウントの乗っ取りや不正アクセスを防ぐため、外部の公共エリア（カフェ、図書館など）や、信頼性の保証されない <strong>共有PC</strong> からの管理画面へのアクセス・投稿作業は、<strong>セキュリティ保護のため強く制限することを推奨</strong>します。</li>
</ul>
<h3 id="状況別の対処法">💡 状況別の対処法</h3>
<ul>
<li><strong>状況A. 自宅やオフィスのIPアドレスが変わった場合（ルーターの再起動など）</strong>:<ul>
<li>既存の作業を行っていた自宅やオフィスのメイン開発PCのターミナルから <code>npm run deploy:admin</code> を1回実行してください。変更後のグローバルIPを自動的に検知して再デプロイが行われ、即座にアクセスが再開されます。</li>
</ul>
</li>
<li><strong>状況B. やむを得ず外部の場所から投稿作業を行うため、一時的に開発環境を移動させる場合</strong>:<ul>
<li>以前取得したバックアップ設定ファイル（<code>wrangler.backup.json</code> など）を持参し、新しいPC環境でワンクリックセットアップを実行して、新規にビルド・デプロイすることでアクセス権限を取得する方法が最も簡単です。</li>
<li><strong>※ 注意:</strong> 外部での作業を終え、再び本来の自宅やオフィスに戻ってブログを管理する際には、<strong>必ず元のメイン開発PCから <code>npm run deploy:admin</code> を再実行し、アクセス許可IPを本来の固定IPに復元デプロイ</strong>してください。</li>
</ul>
</li>
</ul>
<hr>
<h2 id="q6-better-authソーシャルログイン・会員登録に関するエラーやログインの無限ループが発生します">🔐 Q6. Better Auth（ソーシャルログイン・会員登録）に関するエラーや、ログインの無限ループが発生します。</h2>
<ul>
<li><strong>原因</strong>: 認証セキュリティに使用される環境変数が欠落しているか無効な場合、セッション検証に失敗してログイン画面がループし続けます。</li>
<li><strong>解決策</strong>: <ol>
<li><code>apps/blog/.dev.vars</code> を開き、<code>BETTER_AUTH_SECRET</code> に <strong>最小32文字以上の安全なランダム暗号化文字列</strong> が入力されているか確認します。</li>
<li>正確に値を設定した後、<code>npm run deploy:blog</code> を実行して秘密キーを同期上書きすれば、認証機能が正常に復旧します。</li>
</ol>
</li>
</ul>
]]></content:encoded>
            <category>管理者ガイド</category>
            <enclosure url="https://sveltekitblog.com/images/posts/admin-faq/desktop/img-admin-guide-admin-faq-ko-001.webp" length="0" type="image/webp"/>
        </item>
        <item>
            <title><![CDATA[ブログの初期画面構成とログインについて]]></title>
            <link>https://testblog-6br.pages.dev/ja/blog-guide/blog-getting-started</link>
            <guid isPermaLink="true">https://testblog-6br.pages.dev/ja/blog-guide/blog-getting-started</guid>
            <pubDate>Wed, 15 Jul 2026 12:18:46 GMT</pubDate>
            <description><![CDATA[ブログアプリのトップ画面レイアウト構成と、基本的な会員登録およびログイン手順の概要を紹介します。]]></description>
            <content:encoded><![CDATA[<h1 id="ブログの初期画面構成とログインについて">🌐 ブログの初期画面構成とログインについて</h1>
<p>本ドキュメントは、ブログ（<code>apps/blog</code>）トップ画面のレイアウト構成と、基本的な会員登録およびログイン方式の概要を紹介します。</p>
<hr>
<h2 id="1-ブログの画面構成とレイアウト">🎨 1. ブログの画面構成とレイアウト</h2>
<p>ブログの画面は、訪問者が情報を迅速に見つけられるよう、直感的なレイアウト（ヘッダー、本文、サイドバー、フッター）で設計されています。</p>
<ol>
<li><strong>ナビゲーションヘッダー (Header)</strong>:<ul>
<li><strong>サイトロゴ</strong>: クリックするといつでもトップ画面に戻ります。</li>
<li><strong>ショートカットメニュー</strong>: カテゴリへのショートカットや外部リンクなど、主要なリンクが表示されます。</li>
<li><strong>多言語切り替え</strong>: 地球儀アイコンをクリックすると、メニュー言語や記事の翻訳版をリアルタイムで切り替えて表示できます。</li>
</ul>
</li>
<li><strong>サイドバー (Sidebar)</strong>:<ul>
<li>PC画面では、カテゴリ一覧、運営者プロフィールカード、人気タグなどが表示され、自由な探索をサポートします。</li>
<li>モバイル端末からのアクセス時には、本文の視認性向上のため自動的に非表示になります。</li>
</ul>
</li>
<li><strong>メインコンテンツ領域 (Main Content)</strong>:<ul>
<li>最新の記事一覧とカテゴリフィルターが、カードデザイン形式で配置されています。</li>
</ul>
</li>
</ol>
<hr>
<h2 id="2-会員登録とログイン">🔑 2. 会員登録とログイン</h2>
<p>コメントの入力やゲストブックへのメッセージ作成に必要な、アカウント作成およびログイン機能をサポートしています。</p>
<h3 id="ログインおよび登録サポート方式">⚙️ ログインおよび登録サポート方式</h3>
<ul>
<li><strong>標準メールログイン/登録 (Default)</strong>:<ul>
<li>インストール直後にデフォルトで有効化されているログイン方式です。メールアドレス、表示名（ニックネーム）、パスワードを登録して新規登録でき、登録後は即座に自動ログインが適用されます。</li>
</ul>
</li>
</ul>
<figure data-align="center"><img src="https://sveltekitblog.com/images/posts/blog-getting-started/desktop/img-blog-guide-blog-getting-started-ja-001.webp" alt="img-blog-guide-blog-getting-started-ja-001" data-align="center" data-caption="デフォルトのメールログイン" /><figcaption>デフォルトのメールログイン</figcaption></figure>
* **ソーシャルログイン (Better-Auth)**:
  * 内部的にBetter-Authエンジンを搭載しており、Google、GitHub、カカオ、ネイバーなどを含む **計21種のソーシャルプロバイダーログイン** をサポートしています。
  * ただし、ソーシャルログインは、サイト構築後に管理者が各ソーシャルプラットフォームから取得したクライアントIDおよびシークレットキーを設定用の環境変数に手動で挿入する必要があります。入力されていない場合、ログイン画面にソーシャルボタンは表示されず動作しません。（プロバイダーごとの具体的な連携手順は、後日別のドキュメントで案内予定です。）
<figure data-align="center"><img src="https://sveltekitblog.com/images/posts/blog-getting-started/desktop/img-blog-guide-blog-getting-started-ja-002.webp" alt="img-blog-guide-blog-getting-started-ja-002" data-align="center" data-caption="ソーシャル＋メールログイン" /><figcaption>ソーシャル＋メールログイン</figcaption></figure>
* **ログイン状態の表示**:
  * ログインが完了すると、右上ボタンがユーザーのプロフィール画像アイコンとマイページへのショートカットに切り替わります。
  * *参考：ユーザーのプロフィール写真を実際に連携して表示する機能は現在開発中のため、デフォルトの画像アイコンで代替表示される場合があります。*]]></content:encoded>
            <category>ユーザーガイド</category>
            <enclosure url="https://sveltekitblog.com/images/posts/blog-getting-started/desktop/img-blog-guide-blog-getting-started-ja-001.webp" length="0" type="image/webp"/>
        </item>
        <item>
            <title><![CDATA[ブログの主要機能と多言語サービスの概要]]></title>
            <link>https://testblog-6br.pages.dev/ja/blog-guide/blog-core-features</link>
            <guid isPermaLink="true">https://testblog-6br.pages.dev/ja/blog-guide/blog-core-features</guid>
            <pubDate>Wed, 15 Jul 2026 12:18:37 GMT</pubDate>
            <description><![CDATA[記事本文のリアルタイム言語切り替えと手動翻訳データの代替表示ロジック、階層型コメント構造、非公開ゲストブックなど、ブログサービスの主要機能を紹介します。]]></description>
            <content:encoded><![CDATA[<h1 id="ブログの主要機能と多言語サービスの概要">🌐 ブログの主要機能と多言語サービスの概要</h1>
<p>本ドキュメントは、訪問者がブログの多言語翻訳記事を閲覧し、コメントやゲストブックを通じて他のユーザーと対話するための主要機能を紹介します。</p>
<hr>
<h2 id="1-記事本文のリアルタイム言語切り替え-i18n">🌐 1. 記事本文のリアルタイム言語切り替え (i18n)</h2>
<p>このブログは、メニューやボタンなどの基本的なUIだけでなく、<strong>作成された記事本文データ自体</strong>も選択した言語バージョンに切り替えて閲覧できる、リアルタイム多言語翻訳ビューアを提供しています。</p>
<h3 id="動作方式と特徴">⚙️ 動作方式と特徴</h3>
<ul>
<li><strong>言語別URLルーティング</strong>: 上部の地球儀アイコンまたは記事タイトル横の言語選択ボタンをクリックすると、URLアドレスに言語コードのプレフィックス（例：<code>/en</code>, <code>/ja</code>）が付与され、該当言語の専用ページへ即座にルーティングされます。デフォルトで提供される言語（韓国語、英語、日本語）だけでなく、管理者が多言語翻訳辞書（ディクショナリ）を追加定義することで、新たな言語バージョンを制限なく追加発行できます。</li>
</ul>
<img src="https://sveltekitblog.com/images/posts/blog-core-features/desktop/img-blog-guide-blog-core-features-ko-001.webp" alt="img-blog-guide-blog-core-features-ko-001" data-align="center" />
<img src="https://sveltekitblog.com/images/posts/blog-core-features/desktop/img-blog-guide-blog-core-features-ko-002.webp" alt="img-blog-guide-blog-core-features-ko-002" data-align="center" />
<figure data-align="center"><img src="https://sveltekitblog.com/images/posts/blog-core-features/desktop/img-blog-guide-blog-core-features-ko-003.webp" alt="img-blog-guide-blog-core-features-ko-003" data-align="center" data-caption="多言語で公開された記事は、対象の言語ボタンを押すことで、その言語の翻訳版ページに即時に切り替わります。" /><figcaption>多言語で公開された記事は、対象の言語ボタンを押すことで、その言語の翻訳版ページに即時に切り替わります。</figcaption></figure><ul>
<li><strong>手動作成データの読み込み（自動翻訳ではありません）</strong>: システムが自動的にリアルタイムで機械翻訳を行うわけではありません。作成者が各言語タブの翻訳記事本文データを直接執筆して個別に保存しておくと（外部AIや翻訳機で加工したものをデータベースにそれぞれ登録）、訪問者が選択した言語に対応する記事データをデータベースから正確に呼び出し、画面にレンダリングする方式です。</li>
<li><strong>本文およびメタデータの同時読み込み</strong>: 単純なテキストの置き換えにとどまらず、データベースに各言語向けに登録されているタイトル、概要、タグ、本文HTMLが、完全に選択した言語の個別データに切り替わって出力されます。</li>
<li><strong>未翻訳記事の代替（フォールバック）表示</strong>: 作成者が特定の言語の翻訳記事を登録していない場合、訪問者がコンテンツを閲覧できなくなるのを防ぐため、「翻訳準備中」の案内メッセージとともに、デフォルトの作成言語（例：韓国語本文）で記事内容を代替表示し、スムーズな読書をサポートします。</li>
</ul>
<hr>
<h2 id="2-コメントおよび返信スレッド機能">💬 2. コメントおよび返信（スレッド）機能</h2>
<p>各記事の下部には、ゲスト訪問者や会員が意見を交換できるコメント領域が配置されています。</p>
<ul>
<li><strong>意見の共有</strong>: ログインユーザーは、コメント入力欄に内容を記入して登録することで、即座に意見を共有できます。</li>
<li><strong>階層型の返信（スレッド表示）</strong>: 特定のコメントに対して返信を行うことができ、スレッドの対話の流れをツリー構造でわかりやすく整理します。</li>
<li><strong>セキュリティと完全性の維持</strong>: 自分が作成したコメントは削除が可能です。ただし、すでに返信がついているコメントを削除する場合、会話の階層構造が崩れるのを防ぐため、「削除されたコメントです」というメッセージにマスク処理され、構造自体は保持されます。</li>
</ul>
<hr>
<h2 id="3-ゲストブックと非公開メッセージの作成">📖 3. ゲストブックと非公開メッセージの作成</h2>
<p>ゲストブック（Guestbook）を通じて、ブログの開設者とプライベートにコミュニケーションをとる機能をサポートしています。</p>
<h3 id="非公開ゲストブック機能">⚙️ 非公開ゲストブック機能</h3>
<ul>
<li><strong>非公開メッセージの作成</strong>: メッセージ入力時に <strong>[🔒 非公開で作成]</strong> チェックボックスを有効にすることで、非公開メッセージの送信が可能です。</li>
<li><strong>表示権限の制限</strong>: 非公開で保存されたメッセージは、第三者（他の訪問者や未ログインの他ユーザー）のリストからは完全に除外されます。</li>
<li><strong>安全なメッセージ送信</strong>: メッセージを作成した本人（ログイン状態）とサイト管理者のみに本文が表示され、安全に対話を行うことができます。</li>
</ul>
]]></content:encoded>
            <category>ユーザーガイド</category>
            <enclosure url="https://sveltekitblog.com/images/posts/blog-core-features/desktop/img-blog-guide-blog-core-features-ko-001.webp" length="0" type="image/webp"/>
        </item>
        <item>
            <title><![CDATA[読者ポリシーとよくある質問 (FAQ)]]></title>
            <link>https://testblog-6br.pages.dev/ja/blog-guide/blog-faq</link>
            <guid isPermaLink="true">https://testblog-6br.pages.dev/ja/blog-guide/blog-faq</guid>
            <pubDate>Wed, 15 Jul 2026 12:18:27 GMT</pubDate>
            <description><![CDATA[退会時の作成データ（コメント、ゲストブック）の取り扱い方針や、利用制限に関する基本原則を紹介します。]]></description>
            <content:encoded><![CDATA[<p><img src="https://sveltekitblog.com/images/posts/blog-faq/desktop/img-blog-guide-blog-faq-ko-001.webp" alt="img-blog-guide-blog-faq-ko-001"></p>
<h1 id="読者ポリシーとよくある質問-faq">❓ 読者ポリシーとよくある質問 (FAQ)</h1>
<p>本ドキュメントは、ブログサービス利用時に発生する可能性のある退会に伴う個人情報および作成データの処理方法、利用制限ポリシーなどの基本原則を紹介します。</p>
<hr>
<h2 id="q1-退会した場合これまで作成したコメントやゲストブックはどうなりますか">🚪 Q1. 退会した場合、これまで作成したコメントやゲストブックはどうなりますか？</h2>
<p>ブログシステムは、読者の個人情報保護とサイト内における対話の流れの整合性を同時に維持するため、**「作成者の匿名化処理ポリシー」**を適用しています。</p>
<h3 id="退会時の個人情報処理原則">💡 退会時の個人情報処理原則</h3>
<ol>
<li><strong>個人識別情報の削除</strong>:<ul>
<li>退会処理が行われると、ログインメールアドレス、ソーシャルプロファイル画像、ニックネームなど、会員を識別できる固有データはデータベースから即座に削除されます。ただし、不正加入および不正利用の防止、セキュリティ監査(Audit)を目的として、退会時のメールアドレスやソーシャルIDなどの最小限の情報が、隔離された内部脱退ログテーブル(<code>deleted_users</code>)にアーカイブ(バックアップ)されます。このログ情報は内部管理用であり、公開画面上からは作成者の特定や追跡が不可能なように完全に匿名化（作成者不明）処理されます。</li>
</ul>
</li>
<li><strong>作成データの保持</strong>:<ul>
<li>作成されたコメントやゲストブックの投稿は、これまでの会話の流れや文脈を保持するため、テキスト内容は画面上にそのまま維持されます。</li>
</ul>
</li>
<li><strong>ニックネームの匿名化処理</strong>:<ul>
<li>作成者ニックネームとアカウント間の連携情報が解除され、作成者表示名は自動的に <strong>「作成者不明」</strong> に一括で切り替わります。これにより、以前の作成者が実際に誰であったかをシステム的に逆追跡することが防がれます。</li>
</ul>
</li>
</ol>
<ul>
<li><em>参考：管理者が会員情報を直接完全に削除した場合には、作成されたコメントやメッセージも一緒に削除されることがあります。</em></li>
</ul>
<hr>
<h2 id="q2-退会後すぐに再登録再加入は可能ですか">🚪 Q2. 退会後、すぐに再登録（再加入）は可能ですか？</h2>
<ul>
<li><strong>自主退会の場合</strong>:<ul>
<li>本人の意思で退会手続きを完了した場合、特別な憂慮期間なく、同一のメールアドレスやソーシャルアカウントを使用して即座に再登録およびサービス利用が可能です。</li>
</ul>
</li>
<li><strong>利用制限（Ban）状態の場合</strong>:<ul>
<li>運営ポリシー違反によってアカウントが利用制限（Ban）されている間は、該当のメールアドレス情報がすでにデータベース上に存在するため、同一のメールアドレスで新たに再登録することは原則として不可能です。</li>
</ul>
</li>
<li><strong>強制退会（Kick）処理された場合</strong>:<ul>
<li>管理者によってアカウントが強制削除（Hard Delete）された場合、認証テーブルから既存の情報が完全に削除されるため、同一のメールアドレスでの即時再登録が技術的に可能になります。</li>
<li>内部脱退ログ（<code>deleted_users</code>）に強制退会時のメールアドレスおよびソーシャルID情報は保管されますが、現時点では登録時にこのログを照合して自動的に再入会を制限するシステム上のフィルタリング（ブロック）ロジックは実装されておりません。</li>
<li>したがって、同一人物による悪意ある活動の再発を物理的かつ完全に防ぐためには、アカウントを強制削除（Kick）する代わりに、<strong>「永久利用制限（Ban - ブロック済み）」</strong> 状態を維持することを推奨します。（Ban状態ではメールアドレスがデータベースに残るため、一意の制約によって同一メールアドレスでの再登録が遮断されます。）</li>
</ul>
</li>
</ul>
<hr>
<h2 id="q3-アカウント利用制限banまたは強制退会kick処理されました">🚫 Q3. アカウント利用制限（Ban）または強制退会（Kick）処理されました。</h2>
<p>スパム広告や誹謗中傷などの運営ポリシー違反により、サービスの一部利用制限（Ban）措置が適用された、または強制退会（Kick）された場合の確認事項です。</p>
<h3 id="利用制限適用時の確認事項">💡 利用制限適用時の確認事項</h3>
<ol>
<li><strong>利用制限 (Ban)</strong>:<ul>
<li><strong>状態</strong>: ログイン状態は維持されますが、コメント作成、返信、ゲストブック登録など、サイト内における直接的な活動権限が一時的または永久に制限されます。</li>
<li><strong>理由の確認</strong>: 制限が適用されると、コメント作成エリアに <strong>「ブロックされたユーザーはコメントを書き込めません。」</strong> という案内メッセージが表示され、具体的な制限事由や制限満了日（解除予定日時）を確認できます。</li>
</ul>
</li>
<li><strong>強制退会 (Kick/Delete)</strong>:<ul>
<li><strong>状態</strong>: ログインアカウント自体がデータベースから完全に削除（Hard Delete）された状態です。</li>
<li><strong>動作</strong>: 強制退会処理が完了すると、アクティブなログインセッションが即座に強制終了されます。その後ログインを試みた場合は、登録履歴のない新規会員（存在しないアカウント）として扱われます。管理者が設定した削除動作（deleteBehavior）によって、書き込まれたコメントがすべて一括削除されるか、作成者名が「作成者不明」として匿名化されて残るかが異なります。</li>
</ul>
</li>
</ol>
]]></content:encoded>
            <category>ユーザーガイド</category>
            <enclosure url="https://sveltekitblog.com/images/posts/blog-faq/desktop/img-blog-guide-blog-faq-ko-001.webp" length="0" type="image/webp"/>
        </item>
        <item>
            <title><![CDATA[ブログと管理者機能の統合紹介]]></title>
            <link>https://testblog-6br.pages.dev/ja/general-guide/general-integration-guide</link>
            <guid isPermaLink="true">https://testblog-6br.pages.dev/ja/general-guide/general-integration-guide</guid>
            <pubDate>Wed, 15 Jul 2026 12:18:01 GMT</pubDate>
            <description><![CDATA[ブログアプリのレイアウトやコミュニケーション機能、管理者モードの多言語執筆、リアルタイムデザイン設定など、主要な機能紹介ドキュメントを統合して紹介します。]]></description>
            <content:encoded><![CDATA[<p><img src="https://sveltekitblog.com/images/posts/general-integration-guide/desktop/img-general-guide-general-integration-guide-ko-001.webp" alt="img-general-guide-general-integration-guide-ko-001"></p>
<h1 id="ブログと管理者機能の統合紹介">📖 ブログと管理者機能の統合紹介</h1>
<p>本ドキュメントは、ブログサービスおよび管理者機能（管理画面）の主要構成を一覧できるよう、個別の機能紹介ドキュメントを整理して案内します。</p>
<p><em>参考：各機能の具体的な連携手順や詳細なマニュアルは、後日個別の投稿を通じて順次案内される予定です。</em></p>
<hr>
<h2 id="主要機能の紹介一覧">📂 主要機能の紹介一覧</h2>
<ul>
<li><strong>管理者（管理画面）機能紹介</strong><ul>
<li><strong><a href="../admin-guide/admin-install-and-deploy">CMDワンクリックインストールとCloudflareデプロイガイド</a></strong>: ターミナルコマンド一行でCloudflareエッジインフラを構築し、ブログをデプロイする方法を案内します。</li>
<li><strong><a href="../admin-guide/admin-getting-started">管理画面の初回アクセスと環境設定の開始</a></strong>: マスターパスワードログイン、接続許可IP（ホワイトリスト）管理および多言語UI辞書環境設定の概要を紹介します。</li>
<li><strong><a href="../admin-guide/admin-core-features">管理者主要機能とデュアルエディタ紹介</a></strong>: ビジュアルHTMLエディタとMarkdownエディタを切り替えながら執筆し、デバイス別レイアウトウィジェットを配置する方法を紹介します。</li>
<li><strong><a href="../admin-guide/admin-design-editor">リアルタイムデザインエディタと背景演出紹介</a></strong>: テーマカラー、単色/グラデーション/背景画像/キャンバススクリプトの4種類の背景設定を、再ビルドなしでリアルタイムに反映する機能を紹介します。</li>
<li><strong><a href="../admin-guide/admin-faq">管理者よくある質問 (FAQ) およびトラブルシューティング</a></strong>: バージョンアップグレード、画像ストレージキーの点検、デプロイ後の不具合解消およびIPセキュリティブロック解除方法などを確認します。</li>
</ul>
</li>
<li><strong>ブログ機能紹介</strong><ul>
<li><strong><a href="../blog-guide/blog-getting-started">ブログ初期画面構成とログイン</a></strong>: ブログの画面レイアウト構成とメール・ソーシャルログイン方式を紹介します。</li>
<li><strong><a href="../blog-guide/blog-core-features">ブログ主要機能と多言語サービス紹介</a></strong>: 多言語本文のリアルタイム切り替えおよび未翻訳時の代替処理ロジック、階層型コメントと非公開ゲストブックを通じたコミュニケーション構造を扱います。</li>
<li><strong><a href="../blog-guide/blog-faq">読者ポリシーとよくある質問 (FAQ)</a></strong>: 退会時の作成データ処理原則、再登録の規定および利用制限（Ban）/強制退会（Kick）措置に関して確認します。</li>
</ul>
</li>
</ul>
<hr>
<h2 id="ブログ管理の基本プロセス">🚀 ブログ管理の基本プロセス</h2>
<p>管理者の権限を設定し、執筆を開始する基本的な流れの概要です。</p>
<h3 id="1-ip許可リストの適用とログイン">1. IP許可リストの適用とログイン</h3>
<ul>
<li>初めて管理画面にアクセスする際、セキュリティのため訪問者のグローバルIPアドレスが許可リスト（<code>ALLOWED_IP</code>）に登録されている必要があります。</li>
<li>ログイン画面で設定された管理者マスターパスワードを入力して接続を確立します。</li>
</ul>
<h3 id="2-多言語記事の一括保存">2. 多言語記事の一括保存</h3>
<ul>
<li>記事作成メニューに用意された多言語タブを切り替えながら本文をそれぞれ入力し、下部の保存ボタンをクリックしてデータベースへ多言語の記事データを一括で保存します。</li>
</ul>
<h3 id="3-リアルタイムテーマの適用">3. リアルタイムテーマの適用</h3>
<ul>
<li>デザインエディタでテーマカラー、フォントの種類、背景効果（Canvas、画像など）を保存すると、再ビルドなしに訪問者のブラウザ画面へリアルタイムで即座に反映されます。</li>
</ul>
<hr>
<h2 id="おわりに">💬 おわりに</h2>
<p>本ドキュメントで紹介した内容は、ブログと管理画面が提供する機能の<strong>簡単な概要</strong>です。実際には、ここでは取り上げきれなかった様々な詳細機能や便利なオプションがさらに多く用意されており、実際にお使いいただきながらひとつずつ発見していくのも、また一つの楽しみになるかと思います。</p>
<p>今後、各機能の詳細な使い方や活用のコツを盛り込んだ詳細ガイドも順次投稿していく予定ですので、必要な際にまたお越しいただければ幸いです。ご利用中にご不明な点やお問い合わせがございましたら、ゲストブックやコメントを通じていつでもお気軽にお寄せください。</p>
]]></content:encoded>
            <category>総合ガイド</category>
            <enclosure url="https://sveltekitblog.com/images/posts/general-integration-guide/desktop/img-general-guide-general-integration-guide-ko-001.webp" length="0" type="image/webp"/>
        </item>
    </channel>
</rss>