<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="zh-Hant">
  <generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator>
  <link href="https://zhgchg.li/feed.xml" rel="self" type="application/atom+xml" />
  <link href="https://zhgchg.li/" rel="alternate" type="text/html" hreflang="zh-Hant" />
  <updated>2026-05-16T13:30:08+08:00</updated>
  <id>https://zhgchg.li/feed.xml</id>
  <title type="html">ZhgChgLi Tech &amp;amp; Travel</title>
  <subtitle>記錄程式開發、科技觀察與旅途生活。 An iOS, web, and automation developer from Taiwan who also loves sharing, traveling, and writing.</subtitle><author>
    <name>ZhgChgLi</name><email>zhgchgli@gmail.com</email>
  </author><entry>
    <title type="html">AI 協作設計 Rangeable RFC｜資料結構演算法與多語言實作詳解</title>
    <link href="https://zhgchg.li/posts/zrealm-dev/ai-%E5%8D%94%E4%BD%9C%E8%A8%AD%E8%A8%88-rangeable-rfc-%E8%B3%87%E6%96%99%E7%B5%90%E6%A7%8B%E6%BC%94%E7%AE%97%E6%B3%95%E8%88%87%E5%A4%9A%E8%AA%9E%E8%A8%80%E5%AF%A6%E4%BD%9C%E8%A9%B3%E8%A7%A3-f7bec8f79c08/" rel="alternate" type="text/html" title="AI 協作設計 Rangeable RFC｜資料結構演算法與多語言實作詳解" />
    <published>2026-05-10T15:01:01+08:00</published>
    <updated>2026-05-10T15:28:40+08:00</updated>
    <id>https://zhgchg.li/posts/zrealm-dev/f7bec8f79c08</id><summary type="html">針對 Medium Markup 與 AVPlayer Cache 區間問題，揭露 AI 如何從抽象問題、演算法選型到 Ruby、Swift 等多語言實作 Rangeable RFC，帶來最高效區間合併與查詢解決方案，令 Markdown 渲染效能提升超過 2 倍。</summary><author>
      <name>ZhgChgLi</name>
    </author><category term="ZRealm Dev." /><category term="ios-app-development" /><category term="leetcode" /><category term="claude-code" /><category term="ai-agent" /><category term="algorithms" /><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://zhgchg.li/assets/f7bec8f79c08/1*H5MBf1d1kEqBQnqtG9-zgg.webp" /><content type="html" xml:base="https://zhgchg.li/posts/zrealm-dev/ai-%E5%8D%94%E4%BD%9C%E8%A8%AD%E8%A8%88-rangeable-rfc-%E8%B3%87%E6%96%99%E7%B5%90%E6%A7%8B%E6%BC%94%E7%AE%97%E6%B3%95%E8%88%87%E5%A4%9A%E8%AA%9E%E8%A8%80%E5%AF%A6%E4%BD%9C%E8%A9%B3%E8%A7%A3-f7bec8f79c08/"><![CDATA[<h3 id="ai-不只會做應用用-ai-協作完成一份資料結構-rfc-與多語言實作"><strong>AI 不只會做應用：用 AI 協作完成一份資料結構 RFC 與多語言實作</strong></h3>

<p>以 Rangeable RFC 為例，從問題抽象、演算法選型、語意定義到 Ruby / Swift 參考實作，記錄 AI 如何參與更深入的技術研究與設計。</p>

<p><img src="/assets/f7bec8f79c08/1*H5MBf1d1kEqBQnqtG9-zgg.webp" alt="" loading="lazy" decoding="async" width="1200" height="800" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjgwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<h3 id="背景">背景</h3>

<p>前幾週為了 Re-Design 我的 <a href="https://zhgchg.li/" target="_blank">個人網站</a> ，直上了 Claude Code Max 後來又用 AI x Google Apps Script 做了一個個人桌上 Dashboard Deck 把舊的 iPhone 變廢為寶；這週開始把目標轉向之前做的開源專案，用 AI 重構原本手搓的程式碼，優化效率、補充文件、補足測試。</p>

<blockquote>
  <p><strong><em>TL;DR</em></strong></p>
</blockquote>

<blockquote>
  <p><em>先用 AI 重構了幾個開源專案的應用層面，因而得到啟發也讓他做做深入的 Foundation 資料結構設計。</em></p>
</blockquote>

<h4 id="linkyee-your-own-link-page"><a href="https://github.com/ZhgChgLi/linkyee" target="_blank">Linkyee— Your Own Link Page</a></h4>

<p>A fully customized, 100% free, open-source LinkTree alternative — deployed straight to GitHub Pages.</p>

<p><a href="https://github.com/ZhgChgLi/linkyee" target="_blank"><img src="https://repository-images.githubusercontent.com/877945203/333b5db1-e5e7-4d71-8b53-986a60e75033" alt="" /></a></p>

<p>第一個優化的是之前做的 類 — Linktree 免費開源自架版，用 AI 重整了一下架構、多設計幾個主題、多做幾個內建 Plugin、新增 Design AI Skill 讓使用者方便客製化、補充本地測試環境。</p>
<h4 id="zmediumtomarkdown"><a href="https://github.com/ZhgChgLi/ZMediumToMarkdown" target="_blank">ZMediumToMarkdown</a></h4>

<p>Download Medium posts as clean Markdown, preserving structure, images, links, code blocks, and common embeds for plain Markdown or Jekyll workflows.</p>

<p>幫你把文章的所有內容包含圖片、內嵌程式碼、Youtube 連結…全部下載轉換成 Markdown、圖片也會一起下載放到 <code class="language-plaintext highlighter-rouge">./assets</code> 。</p>

<p><a href="https://github.com/ZhgChgLi/ZMediumToMarkdown" target="_blank"><img src="https://repository-images.githubusercontent.com/493527574/9b5b7025-cc95-4e81-84a9-b38706093c27" alt="" /></a></p>

<p>第二個是我一直在使用、維護了四年多的專案 — 「下載並轉換 Medium 文章成 Markdown 格式」的工具，因為後期我幾乎只把 Medium 當成文章編輯器後台，主力是透過這工具把原文下載轉換後傳到我的 <a href="https://zhgchg.li/" target="_blank">自架網站</a> 。</p>

<p>這個專案是我的網站跟 Medium 之間的重要橋樑，不能斷；當初第一版開發的時候花了我大量的時間跟精力，這幾年也都在修修補補一些小問題，但是很久沒有整個重新檢視設計與優化了。</p>

<p>一樣是先從 AI 優化應用開始，除了請他重構＋補測試原本手搓的渲染邏輯外；還有補強 Medium Cloudflare Anti-bot 流程，讓使用者可以在遇到 Medium 阻擋爬蟲時能優雅地從 Chrome 登入後自動獲取 Cookies 然後自動執行。</p>
<h4 id="mcp-medium-reader"><a href="https://github.com/ZhgChgLi/mcp-medium-reader" target="_blank">mcp-medium-reader</a></h4>

<p>最後還請 AI 建了 Medium.com 文章 Reader MCP 服務。</p>

<p><a href="https://github.com/ZhgChgLi/mcp-medium-reader" target="_blank"><img src="https://opengraph.githubassets.com/be5c8e0489d9add1b623ab52d36b56df26fe36b6d911ba3df2be6bf6c03fe966/ZhgChgLi/mcp-medium-reader" alt="" /></a></p>

<p>主要是基礎的轉換服務 <a href="https://github.com/ZhgChgLi/ZMediumToMarkdown" target="_blank">ZMediumToMarkdown</a> 都做好了，MCP 只是一層 Wrapper 呼叫它來做就能達成。</p>

<p>解決的問題是 AI 如 ChatGPT, Codex, Claude, Claude Code 貼給他 Medium 文章時很容易被 Medium 的 Cloudflare Anti-bot 阻擋爬取內容，以至於 AI 無法直接讀取文章，就算能也是讀取 HTML，不是 AI 友善的 Markdown。</p>

<blockquote>
  <p><a href="https://github.com/ZhgChgLi/mcp-medium-reader" target="_blank"><em>mcp-medium-reader</em></a> <em>能讓你的 AI 穿透封鎖並且讀取 Markdown 版本的 Medium 文章，節省 Token 的同時又能提升 AI 理解性。</em></p>
</blockquote>

<h3 id="帶標籤區間集合問題"><strong>帶標籤區間集合問題</strong></h3>

<blockquote>
  <p><em>當年在做 <a href="https://github.com/ZhgChgLi/ZMediumToMarkdown" target="_blank">ZMediumToMarkdown</a> 的時候有遇到 <strong>這個 Foundation 資料結構設計(算法)問題</strong> ，那時候沒有多餘的精力也沒有能力解決，但現在有 AI 了，想想可以嘗試用 AI 解決這個問題。</em></p>
</blockquote>

<h4 id="案例一--medium-渲染問題">案例一 — Medium 渲染問題</h4>

<p><strong>Medium.com 的 GraphQL API 回傳的文章內容資料如下格式：</strong></p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"text"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."</span><span class="p">,</span><span class="w">
  </span><span class="nl">"markups"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"A"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"start"</span><span class="p">:</span><span class="w"> </span><span class="mi">169</span><span class="p">,</span><span class="w">
      </span><span class="nl">"end"</span><span class="p">:</span><span class="w"> </span><span class="mi">207</span><span class="p">,</span><span class="w">
      </span><span class="nl">"href"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://zhgchg.li/posts/f6713ba3fee3/"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"anchorType"</span><span class="p">:</span><span class="w"> </span><span class="s2">"LINK"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"userId"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w">
      </span><span class="nl">"linkMetadata"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w">
      </span><span class="nl">"__typename"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Markup"</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"STRONG"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"start"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
      </span><span class="nl">"end"</span><span class="p">:</span><span class="w"> </span><span class="mi">29</span><span class="p">,</span><span class="w">
      </span><span class="nl">"href"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w">
      </span><span class="nl">"anchorType"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w">
      </span><span class="nl">"userId"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w">
      </span><span class="nl">"linkMetadata"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w">
      </span><span class="nl">"__typename"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Markup"</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"EM"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"start"</span><span class="p">:</span><span class="w"> </span><span class="mi">15</span><span class="p">,</span><span class="w">
      </span><span class="nl">"end"</span><span class="p">:</span><span class="w"> </span><span class="mi">55</span><span class="p">,</span><span class="w">
      </span><span class="nl">"href"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w">
      </span><span class="nl">"anchorType"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w">
      </span><span class="nl">"userId"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w">
      </span><span class="nl">"linkMetadata"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w">
      </span><span class="nl">"__typename"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Markup"</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"STRONG"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"start"</span><span class="p">:</span><span class="w"> </span><span class="mi">69</span><span class="p">,</span><span class="w">
      </span><span class="nl">"end"</span><span class="p">:</span><span class="w"> </span><span class="mi">88</span><span class="p">,</span><span class="w">
      </span><span class="nl">"href"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w">
      </span><span class="nl">"anchorType"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w">
      </span><span class="nl">"userId"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w">
      </span><span class="nl">"linkMetadata"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w">
      </span><span class="nl">"__typename"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Markup"</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p><strong>翻譯成白話文：</strong></p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">[</span><span class="m">0</span><span class="p">,</span><span class="m">14</span><span class="p">]</span>    <span class="n">STRONG</span>       <span class="p">=</span> <span class="n">Lorem</span> <span class="n">ipsum</span> <span class="n">dol</span>
<span class="p">[</span><span class="m">15</span><span class="p">,</span><span class="m">29</span><span class="p">]</span>   <span class="n">STRONG</span> <span class="p">+</span> <span class="n">EM</span>  <span class="p">=</span> <span class="n">or</span> <span class="n">sit</span> <span class="n">amet</span><span class="p">,</span> <span class="n">co</span>
<span class="p">[</span><span class="m">30</span><span class="p">,</span><span class="m">55</span><span class="p">]</span>   <span class="n">EM</span>           <span class="p">=</span> <span class="n">nsectetur</span> <span class="n">adipiscing</span> <span class="n">elit</span><span class="p">,</span>
<span class="p">[</span><span class="m">56</span><span class="p">,</span><span class="m">68</span><span class="p">]</span>   <span class="n">normal</span>       <span class="p">=</span>  <span class="n">sed</span> <span class="k">do</span> <span class="n">eiusm</span>
<span class="p">[</span><span class="m">69</span><span class="p">,</span><span class="m">88</span><span class="p">]</span>   <span class="n">STRONG</span>       <span class="p">=</span> <span class="n">od</span> <span class="n">tempor</span> <span class="n">incididunt</span>
<span class="p">[</span><span class="m">89</span><span class="p">,</span><span class="m">168</span><span class="p">]</span>  <span class="n">normal</span>       <span class="p">=</span>  <span class="n">ut</span> <span class="n">labore</span> <span class="n">et</span> <span class="n">dolore</span> <span class="n">magna</span> <span class="n">aliqua</span><span class="p">.</span> <span class="n">Ut</span> <span class="n">enim</span> <span class="n">ad</span> <span class="n">minim</span> <span class="n">veniam</span><span class="p">,</span> <span class="n">quis</span> <span class="n">nostrud</span> <span class="n">exercit</span>
<span class="p">[</span><span class="m">169</span><span class="p">,</span><span class="m">207</span><span class="p">]</span> <span class="n">A</span>            <span class="p">=</span> <span class="n">ation</span> <span class="n">ullamco</span> <span class="n">laboris</span> <span class="n">nisi</span> <span class="n">ut</span> <span class="n">aliquip</span> <span class="n">e</span>
</code></pre></div></div>

<p><strong>預期渲染結果：</strong></p>

<p><img src="/assets/f7bec8f79c08/1*0cOIQ8YgvcrBZKUdCEaxGw.webp" alt="" loading="lazy" decoding="async" width="652" height="99" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2NTIiIGhlaWdodD0iOTkiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<blockquote>
  <p><em>注意：Medium 原始資料的 end 語意需依實際 API 定義轉換；本文後續以 Rangeable RFC 的 closed interval 語意說明。</em></p>
</blockquote>

<h4 id="問題">問題</h4>
<ul>
  <li><strong>區間疊加</strong> 
同一區間可以是多個狀態疊加，例如 [15,29] 是 STRONG + EM</li>
  <li><strong>區間交錯</strong> 
極端狀況下使用者可能設定交錯的樣式，[0,29] 是 STRONG / [15,55] 是 EM</li>
</ul>

<p><img src="/assets/f7bec8f79c08/1*nagAmJcFkHpMSdbELnT7Mg.webp" alt="" loading="lazy" decoding="async" width="386" height="77" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzODYiIGhlaWdodD0iNzciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<ul>
  <li><strong>區間連續合併</strong> 
資料可能給 [0,12] 是 STRONG / [12,29] 是 STRONG 需要合併成 [0,29] 是 STRONG</li>
</ul>

<p>區間疊加、區間連續合併還算好處理，最麻煩的是區間交錯閉合處理，不能直接照 index 渲染，會變成 <code class="language-plaintext highlighter-rouge">**AA_AA**BBB_</code> ，需要自己處理開閉變成 <code class="language-plaintext highlighter-rouge">**AA_AA_**_BBB_</code> 才會是正確的 Markdown。</p>

<p>iOS 開發者如果有用過 <code class="language-plaintext highlighter-rouge">NSAttributedString attributes</code> 也是一樣的問題，只是 Apple Foundation 幫我們做好區間合併跟疊加處理了。</p>

<blockquote>
  <p><em>當時也是搞了很久想了老半天，書到用時方恨少，平時刷題太少遇到實際要用的時候沒武器可用；那時候是用最暴力的 walkthrough 解決，結果正確只是效能跟程式碼很可怕。</em></p>
</blockquote>

<h4 id="關聯的-leetcode-題型">關聯的 LeetCode 題型</h4>
<ul>
  <li><strong>Merge Intervals</strong></li>
</ul>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mi">56</span><span class="p">.</span> <span class="n">Merge</span> <span class="n">Intervals</span>
<span class="mi">57</span><span class="p">.</span> <span class="k">Insert</span> <span class="n">Interval</span>
</code></pre></div></div>

<p><strong>用途：</strong> 合併 STRONG [0,12], STRONG [13,29] -&gt; STRONG [0,29]。</p>
<ul>
  <li><strong>Sweep Line</strong></li>
</ul>

<div class="language-markdown highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">253.</span> Meeting Rooms II
<span class="p">731.</span> My Calendar II
<span class="p">732.</span> My Calendar III
<span class="p">1094.</span> Car Pooling
<span class="p">1851.</span> Minimum Interval to Include Each Query
</code></pre></div></div>

<p><strong>用途：</strong></p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">STRONG</span> <span class="o">[</span><span class="err">0</span><span class="o">,</span><span class="err">29</span><span class="o">]</span>
<span class="nt">EM</span>     <span class="o">[</span><span class="err">15</span><span class="o">,</span><span class="err">55</span><span class="o">]</span>
<span class="nt">A</span>      <span class="o">[</span><span class="err">169</span><span class="o">,</span><span class="err">207</span><span class="o">]</span>
</code></pre></div></div>

<p>to</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">0</span>    <span class="nt">open</span> <span class="nt">STRONG</span>
<span class="err">15</span>   <span class="nt">open</span> <span class="nt">EM</span>
<span class="err">30</span>   <span class="nt">close</span> <span class="nt">STRONG</span>
<span class="err">56</span>   <span class="nt">close</span> <span class="nt">EM</span>
<span class="err">169</span>  <span class="nt">open</span> <span class="nt">A</span>
<span class="err">208</span>  <span class="nt">close</span> <span class="nt">A</span>
</code></pre></div></div>
<ul>
  <li><strong>Difference Array，但不是純數字差分</strong></li>
</ul>

<div class="language-markdown highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">370.</span> Range Addition
<span class="p">1109.</span> Corporate Flight Bookings
<span class="p">1094.</span> Car Pooling
</code></pre></div></div>

<p><strong>用途：</strong> 維護 active markup set</p>
<ul>
  <li><strong>Interval Split / Segment Construction</strong></li>
</ul>

<p><strong>用途：</strong></p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">STRONG</span> <span class="o">[</span><span class="err">0</span><span class="o">,</span><span class="err">29</span><span class="o">]</span>
<span class="nt">EM</span>     <span class="o">[</span><span class="err">15</span><span class="o">,</span><span class="err">55</span><span class="o">]</span>
</code></pre></div></div>

<p>to</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span><span class="err">0</span><span class="o">,</span><span class="err">14</span><span class="o">]</span>   <span class="nt">STRONG</span>
<span class="o">[</span><span class="err">15</span><span class="o">,</span><span class="err">29</span><span class="o">]</span>  <span class="nt">STRONG</span> <span class="o">+</span> <span class="nt">EM</span>
<span class="o">[</span><span class="err">30</span><span class="o">,</span><span class="err">55</span><span class="o">]</span>  <span class="nt">EM</span>
</code></pre></div></div>

<p><strong>其他用到的題型、資料結構：</strong></p>
<ul>
  <li>Event Sorting</li>
  <li>Interval Partition / Segment Split</li>
  <li>Stack / Parentheses Matching</li>
  <li>Binary Search</li>
  <li>Ordered Set / LinkedHashSet</li>
  <li>Canonicalization</li>
</ul>

<h4 id="案例二--avplayer-cache-區段問題">案例二 — AVPlayer Cache 區段問題</h4>

<p>上面 Medium 遇到的區間問題其實我之前也遇過類似的，在開發「 <a href="/posts/zrealm-dev/avplayer-本地-cache-實作攻略-使用-avassetresourceloaderdelegate-節省-ios-音樂串流流量-6ce488898003/">AVPlayer 本地邊播邊 Cache 時</a> 」也有遇到。</p>

<p>因為串流資料是不連續的，一份 Size: 1000 的 Data，AVPlayer 可能會要求 <code class="language-plaintext highlighter-rouge">[0,100] [300–500] [150, 200]…</code> 之間的不連續或是交錯資料。</p>

<p><img src="/assets/f7bec8f79c08/1*9cNp02AnTbilWCpadldfBw.webp" alt="" loading="lazy" decoding="async" width="1189" height="801" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMTg5IiBoZWlnaHQ9IjgwMSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>以上圖為例，假設 <strong>目前已有資料區段：</strong> <code class="language-plaintext highlighter-rouge">[250,566] [850,959]</code> ，理想上：</p>
<ul>
  <li><strong>AVPlayer 詢問 [0,300] 時：</strong> [0,249] 從遠端拿、[250,300] 從本地拿</li>
  <li><strong>AVPlayer 詢問 [350, 920] 時：</strong> [350,566] 從本地拿、[567,849] 從遠端拿、[850,920] 從本地拿</li>
</ul>

<p>當時一樣遇到區間計算問題 — <strong>Covered / Uncovered Interval Query</strong> ，不過比 Medium 問題簡單因為這邊是 Binary Data 0 跟 1 的區別而已，只需要計算結果。</p>

<blockquote>
  <p><em>當時也是暫時沒解，因為光開發 AVPlayer 邊播放邊緩存的功能就花大半時間了；加上當時場景是音訊，檔案本身就不大； <strong>直接先走區間沒資料就全拿覆蓋的方式 — 細節請參考原文章「 <a href="/posts/zrealm-dev/avplayer-本地-cache-實作攻略-使用-avassetresourceloaderdelegate-節省-ios-音樂串流流量-6ce488898003/">AVPlayer 本地 Cache 實作攻略｜使用 AVAssetResourceLoaderDelegate 節省 iOS 音樂串流流量</a> 」</strong> 。</em></p>
</blockquote>

<h3 id="整體協作流程">整體協作流程</h3>

<p><img src="/assets/f7bec8f79c08/1*t1oXXy1czhI_yV0LKBVf0Q.webp" alt="" loading="lazy" decoding="async" width="1672" height="941" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNjcyIiBoZWlnaHQ9Ijk0MSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<h3 id="用-ai-實現-foundation-資料結構設計">用 AI 實現 Foundation 資料結構設計</h3>
<h4 id="工具">工具</h4>
<ul>
  <li>Claude Code Max</li>
  <li>Effort: Max</li>
  <li>Model: Opus 4.7</li>
</ul>

<h4 id="流程">流程</h4>
<ul>
  <li>先請 AI Plan Mode 起草研究 RFC 撰寫方案</li>
  <li>請 AI 開始研究並且有兩個 Agent 身份：一位是研究生專門負責研究與撰寫 RFC、另一位是教授只負責專注在 Review 算法效率與合理性。(Review 直到 Approve)</li>
  <li>研究生可以先以 Medium 案例為實驗、用 Ruby 開發看看</li>
  <li>最終產出 RFC.md 文件</li>
  <li>交由其他 Agent 實現到各種語言 (我 ZMediumToMarkdown 是 Ruby / AVPlayer 是 iOS Swift 問題)</li>
</ul>

<h4 id="1-ai-研擬資料結構-rfc--rangeable-rfc">1. AI 研擬資料結構 RFC — Rangeable RFC</h4>

<p><strong>Prompt (Plan Mode):</strong></p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/plan

請使用英文，參考英文論文研究，研擬一份 RFC 實作文件，之後會交付讓其他語言可以遵循這份文件實現，研擬期間可以使用 Ruby 作為實驗語言。
你需要研究所有資料結構與演算法，找出一套時間與空間效率最平衡的算法達成需求。
工具名稱：Rangeable
功能：計算泛型物件組合的連續與不連續集合。
範例：
var strings:Rangeable&lt;String&gt; <span class="o">=</span> <span class="o">[]</span>
strings.insert<span class="o">(</span>Strong<span class="o">()</span>, start: 2, end: 5<span class="o">)</span> // 2-5 Strong
strings.insert<span class="o">(</span>Strong<span class="o">()</span>, start: 3, end: 7<span class="o">)</span> // 3-7 Strong
strings.insert<span class="o">(</span>Strong<span class="o">()</span>, start: 9, end: 11<span class="o">)</span> // 9-11 Strong
strings.insert<span class="o">(</span>Italic<span class="o">()</span>, start: 3, end: 8 <span class="o">)</span> // 3-8 Italic

// Usage:
Strong<span class="o">()</span>.getRange<span class="o">(</span>from: strings<span class="o">)</span> -&gt; <span class="o">[{</span>2,7<span class="o">}</span>,<span class="o">{</span>9-11<span class="o">}]</span>
Italic<span class="o">()</span>.getRange<span class="o">(</span>from strings<span class="o">)</span> -&gt; <span class="o">[{</span>3,8<span class="o">}]</span>
//
strings[4].objs -&gt; <span class="o">[</span>Strong<span class="o">()</span>, Italic<span class="o">()]</span>
strings[8].objs -&gt; <span class="o">[</span>Italic<span class="o">()]</span>
strings[10].objs -&gt; <span class="o">[</span>Strong<span class="o">()]</span>
//
請注意 Strong, Italic 本身也是泛型，這裡只是舉例
//

實際研擬 RFC 時請用 sub-agent 一個是研究生負責研究人實現方式與撰寫 RFC、另一個是教授以學術或更深入的演算法為主回頭 Review 研究生 Agent 的產出；如果被教授 Agent 駁回就重新研究撰寫 RFC。
可以先建立 ./RubyRangeable Ruby 的實現 和 ./SwiftRangeable Swift 的實現
你可以盡情消耗 Token 與花費資源做研究。
//
提供一個實際應用場景：../ZMediumToMarkdown 中的 MarkupStyleRender.rb
目前會解析 Medium GraphQL 回傳的 Paragraphs
<span class="o">{</span><span class="s2">"id"</span>: <span class="s2">"f30dc1c4fe6c_19"</span>, <span class="s2">"name"</span>: <span class="s2">"b2ba"</span>, <span class="s2">"type"</span>: <span class="s2">"BQ"</span>, <span class="s2">"href"</span>: null, <span class="s2">"layout"</span>: null, <span class="s2">"metadata"</span>: null, <span class="s2">"text"</span>: <span class="s2">"A notification webhook is an endpoint you create on your server.</span><span class="se">\n</span><span class="s2">通知型 Webhook 是你在自己伺服器上建立的一個端點(endpoint)。"</span>, <span class="s2">"hasDropCap"</span>: null, <span class="s2">"dropCapImage"</span>: null, <span class="s2">"markups"</span>: <span class="o">[{</span><span class="s2">"type"</span>: <span class="s2">"EM"</span>, <span class="s2">"start"</span>: 0, <span class="s2">"end"</span>: 104, <span class="s2">"href"</span>: null, <span class="s2">"anchorType"</span>: null, <span class="s2">"userId"</span>: null, <span class="s2">"linkMetadata"</span>: null, <span class="s2">"__typename"</span>: <span class="s2">"Markup"</span><span class="o">}]</span>, <span class="s2">"__typename"</span>: <span class="s2">"Paragraph"</span>, <span class="s2">"codeBlockMetadata"</span>: null, <span class="s2">"iframe"</span>: null, <span class="s2">"mixtapeMetadata"</span>: null<span class="o">}</span>,
<span class="o">{</span><span class="s2">"id"</span>: <span class="s2">"f30dc1c4fe6c_20"</span>, <span class="s2">"name"</span>: <span class="s2">"f2e8"</span>, <span class="s2">"type"</span>: <span class="s2">"BQ"</span>, <span class="s2">"href"</span>: null, <span class="s2">"layout"</span>: null, <span class="s2">"metadata"</span>: null, <span class="s2">"text"</span>: <span class="s2">"This webhook endpoint receives HTTP POST requests from App Store Connect.</span><span class="se">\n</span><span class="s2">這個 Webhook 端點會接收來自 App Store Connect 的 HTTP POST 請求。"</span>, <span class="s2">"hasDropCap"</span>: null, <span class="s2">"dropCapImage"</span>: null, <span class="s2">"markups"</span>: <span class="o">[{</span><span class="s2">"type"</span>: <span class="s2">"EM"</span>, <span class="s2">"start"</span>: 0, <span class="s2">"end"</span>: 126, <span class="s2">"href"</span>: null, <span class="s2">"anchorType"</span>: null, <span class="s2">"userId"</span>: null, <span class="s2">"linkMetadata"</span>: null, <span class="s2">"__typename"</span>: <span class="s2">"Markup"</span><span class="o">}]</span>, <span class="s2">"__typename"</span>: <span class="s2">"Paragraph"</span>, <span class="s2">"codeBlockMetadata"</span>: null, <span class="s2">"iframe"</span>: null, <span class="s2">"mixtapeMetadata"</span>: null<span class="o">}</span>,
<span class="o">{</span><span class="s2">"id"</span>: <span class="s2">"f30dc1c4fe6c_21"</span>, <span class="s2">"name"</span>: <span class="s2">"1725"</span>, <span class="s2">"type"</span>: <span class="s2">"BQ"</span>, <span class="s2">"href"</span>: null, <span class="s2">"layout"</span>: null, <span class="s2">"metadata"</span>: null, <span class="s2">"text"</span>: <span class="s2">"The POST requests describe important events about your app.</span><span class="se">\n</span><span class="s2">這些 POST 請求會描述與你的 App 相關的重要事件。"</span>, <span class="s2">"hasDropCap"</span>: null, <span class="s2">"dropCapImage"</span>: null, <span class="s2">"markups"</span>: <span class="o">[{</span><span class="s2">"type"</span>: <span class="s2">"EM"</span>, <span class="s2">"start"</span>: 0, <span class="s2">"end"</span>: 89, <span class="s2">"href"</span>: null, <span class="s2">"anchorType"</span>: null, <span class="s2">"userId"</span>: null, <span class="s2">"linkMetadata"</span>: null, <span class="s2">"__typename"</span>: <span class="s2">"Markup"</span><span class="o">}]</span>, <span class="s2">"__typename"</span>: <span class="s2">"Paragraph"</span>, <span class="s2">"codeBlockMetadata"</span>: null, <span class="s2">"iframe"</span>: null, <span class="s2">"mixtapeMetadata"</span>: null<span class="o">}</span>,
<span class="o">{</span><span class="s2">"id"</span>: <span class="s2">"f30dc1c4fe6c_22"</span>, <span class="s2">"name"</span>: <span class="s2">"3e9d"</span>, <span class="s2">"type"</span>: <span class="s2">"BQ"</span>, <span class="s2">"href"</span>: null, <span class="s2">"layout"</span>: null, <span class="s2">"metadata"</span>: null, <span class="s2">"text"</span>: <span class="s2">"Use the webhooks notifications endpoint to configure the notifications for events happening to your apps.</span><span class="se">\n</span><span class="s2">你可以使用 Webhook 通知端點，來設定當你的 App 發生各種事件時所要接收的通知。"</span>, <span class="s2">"hasDropCap"</span>: null, <span class="s2">"dropCapImage"</span>: null, <span class="s2">"markups"</span>: <span class="o">[{</span><span class="s2">"type"</span>: <span class="s2">"EM"</span>, <span class="s2">"start"</span>: 0, <span class="s2">"end"</span>: 151, <span class="s2">"href"</span>: null, <span class="s2">"anchorType"</span>: null, <span class="s2">"userId"</span>: null, <span class="s2">"linkMetadata"</span>: null, <span class="s2">"__typename"</span>: <span class="s2">"Markup"</span><span class="o">}]</span>, <span class="s2">"__typename"</span>: <span class="s2">"Paragraph"</span>, <span class="s2">"codeBlockMetadata"</span>: null, <span class="s2">"iframe"</span>: null, <span class="s2">"mixtapeMetadata"</span>: null<span class="o">}</span>,
然後依照 start, end 渲染出 Markdown，可是目前沒有算法所以是用巡迴填補的方法。
</code></pre></div></div>
<h4 id="2-方案確定後研究生-agent-開始研究---產出第一版-rfc">2. 方案確定後研究生 Agent 開始研究 -&gt; 產出第一版 RFC</h4>
<h4 id="3-教授-agent-開始-review---rejected">3. 教授 Agent 開始 Review -&gt; REJECTED</h4>

<p>主因是教授認為 RFC 還有 <strong>6 個 MUST-FIX</strong> ，也就是不修不能通過的規格/演算法正確性問題。</p>

<p><img src="/assets/f7bec8f79c08/1*wJuWgRv0aQqpDWkjORt1nw.webp" alt="" loading="lazy" decoding="async" width="967" height="469" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NjciIGhlaWdodD0iNDY5Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<h4 id="4-研究生-agent-回頭研究修改---產出第二版-rfc">4. 研究生 Agent 回頭研究修改 -&gt; 產出第二版 RFC</h4>
<h4 id="5-教授-agent-重新-review---approved">5. 教授 Agent 重新 Review -&gt; APPROVED</h4>
<h4 id="6-done">6. Done</h4>
<ul>
  <li>花費時間：約 1 hr 30 mins</li>
  <li>花費 Token：178K (約佔 Claude Code Max 5 hr 可用額度的 30%)</li>
</ul>

<h3 id="rangeable-rfc">Rangeable RFC</h3>

<p><a href="https://github.com/ZhgChgLi/RangeableRFC/blob/main/RFC.md" target="_blank"><img src="https://opengraph.githubassets.com/c1bbe414aeed52b95e22e3420b934b9cc8d1b4fc8feb04da559f7d9b381f7f0f/ZhgChgLi/RangeableRFC" alt="" /></a></p>

<h4 id="tldr">TL;DR</h4>

<blockquote>
  <p><code class="language-plaintext highlighter-rouge">Rangeable&lt;Element&gt;</code> <em>是一個用來管理「元素在哪些整數閉區間內生效」的通用資料結構。它可以把同一元素重疊或相鄰的區間自動合併，並支援查詢某個 index 上有哪些元素 active，以及輸出某段範圍內的 open / close transition events。它原本是為了解決 Medium Markdown markup render 問題，例如判斷某個字元同時套用了 <code class="language-plaintext highlighter-rouge">STRONG</code> 、 <code class="language-plaintext highlighter-rouge">EM</code> 、 <code class="language-plaintext highlighter-rouge">LINK</code> 哪些樣式；但同樣 <strong>也能套用在 Calendar、遊戲狀態、Genome annotation、AVPlayer byte-range cache 等場景</strong> 。核心價值是把「區間合併、active set 查詢、邊界事件產生」抽象成一個 deterministic、跨語言一致、適合 Ruby / Swift 實作的規格。</em></p>
</blockquote>

<blockquote>
  <p>以下 RFC 細節除非很有興趣不然可以直接略過。這邊也是直接用 AI 翻譯總結的。</p>
</blockquote>

<p><code class="language-plaintext highlighter-rouge">Rangeable&lt;Element&gt;</code> 是一個「 <strong>把元素對應到多段整數閉區間，並能快速查詢某個位置有哪些元素生效</strong> 」的通用資料結構。</p>

<p>它解的不是單純的 <code class="language-plaintext highlighter-rouge">Range</code> 問題，而是給我很多個 <code class="language-plaintext highlighter-rouge">(元素, start, end)</code> ，我要能：</p>
<ol>
  <li>查某個元素有哪些合併後的區間</li>
  <li>查某個 index 上有哪些元素 active</li>
  <li>查某段範圍內有哪些 open / close 邊界事件</li>
</ol>

<p>RFC 明確定義 <code class="language-plaintext highlighter-rouge">Rangeable</code> 是一個 language-neutral、generic、integer-coordinate、closed-interval set container，元素必須可 Hash，比對以 value equality 為準。它支援 <code class="language-plaintext highlighter-rouge">getRange</code> 、 <code class="language-plaintext highlighter-rouge">r[i].objs</code> 、 <code class="language-plaintext highlighter-rouge">transitions</code> 三種查詢。</p>
<h4 id="主要動機">主要動機</h4>

<p>這份 RFC 起源於 <code class="language-plaintext highlighter-rouge">ZMediumToMarkdown</code> 的 Medium markup render 問題。</p>

<p>Medium paragraph 內會有像這樣的 markups：</p>
<div class="language-makefile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nl">STRONG</span><span class="o">:</span> <span class="nf">[2</span><span class="p">,</span><span class="nf"> 5]</span>
<span class="nl">STRONG</span><span class="o">:</span> <span class="nf">[3</span><span class="p">,</span><span class="nf"> 7]</span>
<span class="nl">EM</span><span class="o">:</span>     <span class="nf">[4</span><span class="p">,</span><span class="nf"> 10]</span>
</code></pre></div></div>

<p>目前 render 時需要逐字掃描，判斷每個字元上有哪些 tag active，例如 bold、italic、link、code 等。RFC 裡提到原本做法會把 markups 轉成 <code class="language-plaintext highlighter-rouge">TagChar</code> ，依照 <code class="language-plaintext highlighter-rouge">startIndex</code> 排序，然後每個字元線性掃過所有 tags，最壞會到 <code class="language-plaintext highlighter-rouge">O(L · m)</code> 。 <code class="language-plaintext highlighter-rouge">Rangeable</code> 想把這件事抽象化成通用容器，讓 renderer 只要 insert markups，再用 <code class="language-plaintext highlighter-rouge">r[i].objs</code> 或 <code class="language-plaintext highlighter-rouge">transitions</code> 查詢即可。</p>

<p>RFC 也把這個問題擴展到其他場景，例如：</p>

<p><img src="/assets/f7bec8f79c08/1*AWs90nwJGkKee9iEzLwexQ.webp" alt="" loading="lazy" decoding="async" width="611" height="275" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2MTEiIGhlaWdodD0iMjc1Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>RFC 明確指出共通問題是：給定很多 <code class="language-plaintext highlighter-rouge">(eᵢ, lᵢ, hᵢ)</code> ，查某個位置 <code class="language-plaintext highlighter-rouge">i</code> 時，要回傳所有滿足 <code class="language-plaintext highlighter-rouge">l ≤ i ≤ h</code> 的元素。</p>
<h4 id="核心概念翻譯">核心概念翻譯</h4>
<h4 id="1-rangeableelement">1. <code class="language-plaintext highlighter-rouge">Rangeable&lt;Element&gt;</code></h4>

<p>可以想成：</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">Element</span> <span class="nt">-</span><span class="o">&gt;</span> <span class="o">[</span><span class="nt">ClosedRange</span><span class="o">&lt;</span><span class="nt">Int</span><span class="o">&gt;]</span>
</code></pre></div></div>

<p>但它不是普通 Dictionary，因為它會：</p>
<ol>
  <li>自動合併同一元素的重疊區間</li>
  <li>自動合併相鄰區間</li>
  <li>保留元素第一次插入順序</li>
  <li>支援快速查詢某個 index 上 active 的 elements</li>
  <li>支援輸出 open / close transition events</li>
</ol>

<p>例如：</p>
<div class="language-scss highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">insert</span><span class="o">(</span><span class="nt">STRONG</span><span class="o">,</span> <span class="nt">2</span><span class="o">,</span> <span class="nt">5</span><span class="o">)</span>
<span class="nt">insert</span><span class="o">(</span><span class="nt">STRONG</span><span class="o">,</span> <span class="nt">3</span><span class="o">,</span> <span class="nt">7</span><span class="o">)</span>
</code></pre></div></div>

<p>最後會變成：</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">STRONG</span> <span class="nt">-</span><span class="o">&gt;</span> <span class="o">[(</span><span class="err">2</span><span class="o">,</span> <span class="err">7</span><span class="o">)]</span>
</code></pre></div></div>

<p>因為 <code class="language-plaintext highlighter-rouge">[2,5]</code> 和 <code class="language-plaintext highlighter-rouge">[3,7]</code> 重疊。</p>
<h4 id="api-重點">API 重點</h4>

<p>RFC 定義的主要 API 如下。</p>
<h4 id="inserte-start-end"><code class="language-plaintext highlighter-rouge">insert(e, start, end)</code></h4>
<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">insert</span><span class="p">(</span><span class="n">e</span><span class="p">:</span> <span class="n">Element</span><span class="p">,</span> <span class="k">start</span><span class="p">:</span> <span class="nb">Int</span><span class="p">,</span> <span class="k">end</span><span class="p">:</span> <span class="nb">Int</span><span class="p">)</span>
</code></pre></div></div>

<p>效果是：</p>
<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">R</span><span class="p">(</span><span class="n">e</span><span class="p">)</span> <span class="o">=</span> <span class="n">canonicalize</span><span class="p">(</span><span class="n">R</span><span class="p">(</span><span class="n">e</span><span class="p">)</span> <span class="err">∪</span> <span class="p">[</span><span class="k">start</span><span class="p">,</span> <span class="k">end</span><span class="p">])</span>
</code></pre></div></div>

<p>也就是把新區間加入元素 <code class="language-plaintext highlighter-rouge">e</code> 的既有區間集合，然後做 canonicalize：合併所有重疊或相鄰的區間並排序。 <code class="language-plaintext highlighter-rouge">start &gt; end</code> 必須丟出 <code class="language-plaintext highlighter-rouge">InvalidIntervalError</code> ；重複 insert 相同內容是 idempotent，不應改變結果，也不應增加 version。</p>
<h4 id="riobjs--activeatindex"><code class="language-plaintext highlighter-rouge">r[i].objs</code> / <code class="language-plaintext highlighter-rouge">activeAt(index:)</code></h4>
<pre><code class="language-less">r[i] -&gt; Slot
r.activeAt(index: i) -&gt; Slot
</code></pre>

<p>回傳某個 index 上 active 的 elements。</p>

<p>例如：</p>
<div class="language-scss highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">insert</span><span class="o">(</span><span class="nt">Strong</span><span class="o">,</span> <span class="nt">2</span><span class="o">,</span> <span class="nt">5</span><span class="o">)</span>
<span class="nt">insert</span><span class="o">(</span><span class="nt">Italic</span><span class="o">,</span> <span class="nt">3</span><span class="o">,</span> <span class="nt">7</span><span class="o">)</span>
</code></pre></div></div>

<p>查詢：</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">r</span><span class="o">[</span><span class="err">3</span><span class="o">]</span><span class="nc">.objs</span>
</code></pre></div></div>

<p>結果是：</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">[</span><span class="n">Strong</span><span class="p">,</span> <span class="n">Italic</span><span class="p">]</span>
</code></pre></div></div>

<p>查詢複雜度目標是：</p>
<div class="language-scss highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">O</span><span class="o">(</span><span class="nt">log</span> <span class="nt">M</span> <span class="o">+</span> <span class="nt">r</span><span class="o">)</span>
</code></pre></div></div>

<p>其中 <code class="language-plaintext highlighter-rouge">M</code> 是所有元素合併後的 interval 數量總和， <code class="language-plaintext highlighter-rouge">r</code> 是該位置實際回傳的元素數。</p>
<h4 id="getrangeof"><code class="language-plaintext highlighter-rouge">getRange(of:)</code></h4>
<div class="language-scss highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">getRange</span><span class="o">(</span><span class="nt">of</span> <span class="nt">e</span><span class="o">)</span> <span class="nt">-</span><span class="o">&gt;</span> <span class="o">[(</span><span class="nt">Int</span><span class="o">,</span> <span class="nt">Int</span><span class="o">)]</span>
</code></pre></div></div>

<p>回傳某個元素目前合併後的 canonical ranges。</p>

<p>例如：</p>
<div class="language-scss highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">insert</span><span class="o">(</span><span class="nt">Strong</span><span class="o">,</span> <span class="nt">2</span><span class="o">,</span> <span class="nt">4</span><span class="o">)</span>
<span class="nt">insert</span><span class="o">(</span><span class="nt">Strong</span><span class="o">,</span> <span class="nt">5</span><span class="o">,</span> <span class="nt">7</span><span class="o">)</span>
</code></pre></div></div>

<p>因為 <code class="language-plaintext highlighter-rouge">[2,4]</code> 和 <code class="language-plaintext highlighter-rouge">[5,7]</code> 在整數座標上相鄰，所以結果是：</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[(</span><span class="err">2</span><span class="o">,</span> <span class="err">7</span><span class="o">)]</span>
</code></pre></div></div>

<p>RFC 明確要求回傳的 intervals 必須排序、互不重疊、也不相鄰。</p>
<h4 id="transitionsover"><code class="language-plaintext highlighter-rouge">transitions(over:)</code></h4>
<div class="language-scss highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">transitions</span><span class="err">(</span><span class="na">over</span><span class="p">:</span> <span class="n">ClosedRange</span><span class="o">&lt;</span><span class="n">Int</span><span class="o">&gt;</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="p">[</span><span class="n">TransitionEvent</span><span class="p">]</span>
</code></pre></div></div>

<p>回傳一段範圍內的 open / close 邊界事件。</p>

<p>例如：</p>
<div class="language-scss highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">insert</span><span class="o">(</span><span class="nt">Strong</span><span class="o">,</span> <span class="nt">2</span><span class="o">,</span> <span class="nt">5</span><span class="o">)</span>
<span class="nt">insert</span><span class="o">(</span><span class="nt">Italic</span><span class="o">,</span> <span class="nt">3</span><span class="o">,</span> <span class="nt">7</span><span class="o">)</span>
</code></pre></div></div>

<p>則 transitions 會是：</p>
<div class="language-lua highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">[</span>
  <span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="n">open</span><span class="p">,</span>  <span class="n">Strong</span><span class="p">),</span>
  <span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="n">open</span><span class="p">,</span>  <span class="n">Italic</span><span class="p">),</span>
  <span class="p">(</span><span class="mi">6</span><span class="p">,</span> <span class="n">close</span><span class="p">,</span> <span class="n">Strong</span><span class="p">),</span>
  <span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="n">close</span><span class="p">,</span> <span class="n">Italic</span><span class="p">)</span>
<span class="p">]</span>
</code></pre></div></div>

<p>注意 close event 的位置是 <code class="language-plaintext highlighter-rouge">hi + 1</code> ，因為外部語意是閉區間 <code class="language-plaintext highlighter-rouge">[lo, hi]</code> ，但 sweep-line 內部用「第一個不再 active 的位置」當 close coordinate。RFC 明確說明 <code class="language-plaintext highlighter-rouge">transitions(over: lo..hi)</code> 會包含 <code class="language-plaintext highlighter-rouge">hi + 1</code> 的 close event，方便處理右邊界。</p>
<h4 id="區間語意">區間語意</h4>
<h4 id="1-end-是-inclusive">1. <code class="language-plaintext highlighter-rouge">end</code> 是 inclusive</h4>

<p>這份 RFC 非常明確： <code class="language-plaintext highlighter-rouge">insert(e, start: a, end: b)</code> 表示閉區間 <code class="language-plaintext highlighter-rouge">[a, b]</code> ， <code class="language-plaintext highlighter-rouge">b</code> 本身也包含在 active range 裡。</p>

<p>也就是：</p>
<div class="language-scss highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">insert</span><span class="o">(</span><span class="nt">Strong</span><span class="o">,</span> <span class="nt">2</span><span class="o">,</span> <span class="nt">5</span><span class="o">)</span>
</code></pre></div></div>

<p>代表：</p>
<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span> <span class="nx">都</span> <span class="nx">active</span>
</code></pre></div></div>

<p>不是 <code class="language-plaintext highlighter-rouge">[2, 5)</code> 。</p>

<p>RFC 選擇 inclusive 的原因包括：</p>
<ol>
  <li>符合 Medium markup / ZMediumToMarkdown 的歷史資料模型</li>
  <li>比較符合「這個字元是否 active」的人類直覺</li>
  <li>整數相鄰合併可以寫成 <code class="language-plaintext highlighter-rouge">hi + 1 == lo</code></li>
  <li>單點區間 <code class="language-plaintext highlighter-rouge">[k, k]</code> 可以自然表示一個有效位置</li>
</ol>

<h4 id="2-相鄰區間要合併">2. 相鄰區間要合併</h4>

<p>在整數座標上：</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">[</span><span class="m">2</span><span class="p">,</span> <span class="m">4</span><span class="p">]</span> <span class="p">+</span> <span class="p">[</span><span class="m">5</span><span class="p">,</span> <span class="m">7</span><span class="p">]</span> <span class="p">=&gt;</span> <span class="p">[</span><span class="m">2</span><span class="p">,</span> <span class="m">7</span><span class="p">]</span>
</code></pre></div></div>

<p>因為 4 和 5 之間沒有任何整數位置可以表示「不 active」。RFC 要求同一個元素的整數相鄰區間必須合併。</p>

<p>但：</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">[</span><span class="m">2</span><span class="p">,</span> <span class="m">4</span><span class="p">]</span> <span class="p">+</span> <span class="p">[</span><span class="m">6</span><span class="p">,</span> <span class="m">7</span><span class="p">]</span> <span class="p">=&gt;</span> <span class="p">[(</span><span class="m">2</span><span class="p">,</span> <span class="m">4</span><span class="p">),</span> <span class="p">(</span><span class="m">6</span><span class="p">,</span> <span class="m">7</span><span class="p">)]</span>
</code></pre></div></div>

<p>因為中間有 <code class="language-plaintext highlighter-rouge">5</code> 這個 gap。</p>
<h4 id="3-start--end-是合法-singleton-interval">3. <code class="language-plaintext highlighter-rouge">start == end</code> 是合法 singleton interval</h4>
<div class="language-scss highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">insert</span><span class="o">(</span><span class="nt">e</span><span class="o">,</span> <span class="nt">5</span><span class="o">,</span> <span class="nt">5</span><span class="o">)</span>
</code></pre></div></div>

<p>代表只有 index <code class="language-plaintext highlighter-rouge">5</code> active。RFC 要求這是合法且非空的區間。</p>
<h4 id="4-start--end-必須丟錯">4. <code class="language-plaintext highlighter-rouge">start &gt; end</code> 必須丟錯</h4>
<div class="language-scss highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">insert</span><span class="o">(</span><span class="nt">e</span><span class="o">,</span> <span class="nt">10</span><span class="o">,</span> <span class="nt">5</span><span class="o">)</span>
</code></pre></div></div>

<p>不能自動反轉成 <code class="language-plaintext highlighter-rouge">[5,10]</code> ，也不能 silent normalize。RFC 要求必須丟出 <code class="language-plaintext highlighter-rouge">InvalidIntervalError</code> ，而且 container 狀態不能改變。</p>
<h4 id="element-equality-語意">Element equality 語意</h4>

<p><code class="language-plaintext highlighter-rouge">Rangeable</code> 判斷兩個 element 是否同一個元素，是看語言原生的 value equality。</p>

<p>Ruby：</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>eql? + <span class="nb">hash</span>
</code></pre></div></div>

<p>Swift：</p>
<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">Hashable</span> <span class="o">/</span> <span class="nx">Equatable</span>
</code></pre></div></div>

<p>所以：</p>
<div class="language-scss highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">Link</span><span class="o">(</span><span class="s2">"a"</span><span class="o">)</span> <span class="err">和</span> <span class="nt">Link</span><span class="o">(</span><span class="s2">"a"</span><span class="o">)</span> <span class="err">會被視為同一個元素，區間會合併</span>
<span class="nt">Link</span><span class="o">(</span><span class="s2">"a"</span><span class="o">)</span> <span class="err">和</span> <span class="nt">Link</span><span class="o">(</span><span class="s2">"b"</span><span class="o">)</span> <span class="err">是不同元素，不會合併</span>
<span class="nt">Strong</span><span class="o">()</span> <span class="err">和</span> <span class="nt">Strong</span><span class="o">()</span> <span class="err">如果</span> <span class="nt">equality</span> <span class="err">相等，也會合併</span>
</code></pre></div></div>

<p>RFC 要求 equality 必須滿足 reflexive、symmetric、transitive、hash consistency，而且元素插入後不應再被外部 mutation 破壞 hash/equality。</p>
<h4 id="排序規則">排序規則</h4>

<p>這是 RFC 很重要的部分。</p>
<h4 id="active-set-排序">Active set 排序</h4>

<p><code class="language-plaintext highlighter-rouge">r[i].objs</code> 的順序不是依 hash、不是依字母、也不是依 range 長度，而是：</p>
<div class="language-lua highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">元素第一次被</span> <span class="n">insert</span> <span class="err">的順序</span>
</code></pre></div></div>

<p>例如：</p>
<div class="language-scss highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">insert</span><span class="o">(</span><span class="nt">Strong</span><span class="o">,</span> <span class="nt">1</span><span class="o">,</span> <span class="nt">10</span><span class="o">)</span>
<span class="nt">insert</span><span class="o">(</span><span class="nt">Italic</span><span class="o">,</span> <span class="nt">1</span><span class="o">,</span> <span class="nt">10</span><span class="o">)</span>
<span class="nt">insert</span><span class="o">(</span><span class="nt">Code</span><span class="o">,</span> <span class="nt">1</span><span class="o">,</span> <span class="nt">10</span><span class="o">)</span>
</code></pre></div></div>

<p>則：</p>
<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">r</span><span class="p">[</span><span class="mi">5</span><span class="p">].</span><span class="nx">objs</span> <span class="o">==</span> <span class="p">[</span><span class="nx">Strong</span><span class="p">,</span> <span class="nx">Italic</span><span class="p">,</span> <span class="nx">Code</span><span class="p">]</span>
</code></pre></div></div>

<p>RFC 指出這樣做是為了 deterministic、跨語言一致，而且符合 Markdown nesting：越早出現的 style 通常是外層。</p>
<h4 id="merge-不會改變-insertion-order">Merge 不會改變 insertion order</h4>

<p>例如：</p>
<div class="language-scss highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">insert</span><span class="o">(</span><span class="nt">Strong</span><span class="o">,</span> <span class="nt">1</span><span class="o">,</span> <span class="nt">5</span><span class="o">)</span>   <span class="o">//</span> <span class="nt">Strong</span> <span class="nt">ord</span> <span class="o">=</span> <span class="nt">1</span>
<span class="nt">insert</span><span class="o">(</span><span class="nt">Italic</span><span class="o">,</span> <span class="nt">3</span><span class="o">,</span> <span class="nt">7</span><span class="o">)</span>   <span class="o">//</span> <span class="nt">Italic</span> <span class="nt">ord</span> <span class="o">=</span> <span class="nt">2</span>
<span class="nt">insert</span><span class="o">(</span><span class="nt">Strong</span><span class="o">,</span> <span class="nt">4</span><span class="o">,</span> <span class="nt">8</span><span class="o">)</span>   <span class="o">//</span> <span class="nt">Strong</span> <span class="nt">range</span> <span class="err">合併成</span> <span class="o">[</span><span class="nt">1</span><span class="o">,</span><span class="nt">8</span><span class="o">]</span><span class="err">，但</span> <span class="nt">ord</span> <span class="err">還是</span> <span class="nt">1</span>
</code></pre></div></div>

<p>查詢：</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">r</span><span class="o">[</span><span class="err">6</span><span class="o">]</span><span class="nc">.objs</span>
</code></pre></div></div>

<p>結果是：</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">[</span><span class="n">Strong</span><span class="p">,</span> <span class="n">Italic</span><span class="p">]</span>
</code></pre></div></div>

<p>雖然 Strong 是第三步才延伸到 index 6，但它的 first-insert order 仍然比 Italic 早。RFC 用這個 case 釘死「元素第一次出現的順序」才是排序基準。</p>
<h4 id="transitions-排序">Transitions 排序</h4>

<p>同一個 coordinate 上：</p>
<ol>
  <li><code class="language-plaintext highlighter-rouge">.open</code> 比 <code class="language-plaintext highlighter-rouge">.close</code> 早</li>
  <li>多個 <code class="language-plaintext highlighter-rouge">.open</code> ：依 insertion order 升序</li>
  <li>多個 <code class="language-plaintext highlighter-rouge">.close</code> ：依 insertion order 降序，也就是 LIFO</li>
</ol>

<p>這樣可以符合 Markdown stack discipline：</p>
<div class="language-lua highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">**</span> <span class="n">open</span>
<span class="n">_</span> <span class="n">open</span>
<span class="n">_</span> <span class="n">close</span>
<span class="o">**</span> <span class="n">close</span>
</code></pre></div></div>

<p>RFC 明確定義這套 tie-breaking，以避免 Ruby / Swift hash order 不一致造成跨語言輸出不同。</p>
<h4 id="內部資料結構">內部資料結構</h4>

<p>RFC 選擇的核心設計是：</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Map&lt;Element, SortedList&lt;Interval&gt;&gt;
+ insertion_order
+ lazy boundary-event index
+ version counter
</code></pre></div></div>
<h4 id="1-per-element-sorted-disjoint-list">1. Per-element sorted disjoint list</h4>

<p>每個元素有自己的 interval list：</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">intervals</span><span class="p">:</span> <span class="kt">Map</span><span class="o">&lt;</span><span class="kt">Element</span><span class="p">,</span> <span class="kt">SortedList</span><span class="o">&lt;</span><span class="kt">Interval</span><span class="o">&gt;&gt;</span>
</code></pre></div></div>

<p>而且必須維持 canonical form：</p>
<ol>
  <li>依 <code class="language-plaintext highlighter-rouge">lo</code> 遞增排序</li>
  <li>彼此不重疊</li>
  <li>彼此不相鄰</li>
  <li>每個 interval 都滿足 <code class="language-plaintext highlighter-rouge">lo &lt;= hi</code></li>
</ol>

<p>RFC 稱這是 I1 invariant。</p>
<h4 id="2-lazy-boundary-event-index">2. Lazy boundary-event index</h4>

<p><code class="language-plaintext highlighter-rouge">Rangeable</code> 不會每次 insert 都重建查詢 index，而是等第一次 query 時才 build。</p>
<div class="language-makefile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nl">version</span><span class="o">:</span> <span class="nf">Int</span>
<span class="nl">event_index</span><span class="o">:</span> <span class="nf">EventIndex?</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">event_index</code> 包含：</p>
<pre><code class="language-vbnet">events: SortedArray&lt;Event&gt;
segments: SortedArray&lt;Segment&gt;
version: Int
</code></pre>

<p>這個設計是為了符合「build-once-then-query-densely」的 workload：大量 insert 完後，再密集查詢每個位置。RFC 明確說明 lazy index 適合這種模式，因為 build phase 不需要一直 rebuild index，query phase 只付一次 <code class="language-plaintext highlighter-rouge">O(M log M)</code> 成本。</p>
<h4 id="演算法重點">演算法重點</h4>
<h4 id="insert-的核心流程"><code class="language-plaintext highlighter-rouge">insert</code> 的核心流程</h4>

<p>RFC 的 pseudocode 大致是：</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">function </span>insert<span class="o">(</span>r, e, start, end<span class="o">)</span>:
  <span class="k">if </span>start <span class="o">&gt;</span> end:
    raise InvalidIntervalError

  e_frozen <span class="o">=</span> freeze_for_insert<span class="o">(</span>e<span class="o">)</span>

  <span class="k">if </span>e not <span class="k">in </span>intervals:
    intervals[e] <span class="o">=</span> <span class="o">[]</span>
    insertion_order.append<span class="o">(</span>e<span class="o">)</span>
    ord[e] <span class="o">=</span> insertion_order.length

  list <span class="o">=</span> intervals[e]
  lo <span class="o">=</span> start
  hi <span class="o">=</span> end

  找到第一個可能與 <span class="o">[</span>lo, hi] 重疊或相鄰的 interval

  <span class="k">while </span>interval 跟 <span class="o">[</span>lo, hi] 重疊或相鄰:
    lo <span class="o">=</span> min<span class="o">(</span>lo, interval.lo<span class="o">)</span>
    hi <span class="o">=</span> max<span class="o">(</span>hi, interval.hi<span class="o">)</span>
    移除舊 interval

  插入合併後的新 <span class="o">[</span>lo, hi]

  如果真的有改變:
    version +<span class="o">=</span> 1
    event_index <span class="o">=</span> nil
</code></pre></div></div>

<p>特別要注意 RFC 提醒不能用 <code class="language-plaintext highlighter-rouge">lo - 1</code> 來判斷，因為 <code class="language-plaintext highlighter-rouge">lo == Int.min</code> 時會 underflow；應該用 <code class="language-plaintext highlighter-rouge">hi + 1 &gt;= lo</code> 或等價的 successor model。</p>
<h4 id="複雜度">複雜度</h4>

<p>RFC 選擇的 reference structure 是：</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">per</span><span class="p">-</span><span class="n">element</span> <span class="n">sorted</span> <span class="n">disjoint</span> <span class="n">list</span> <span class="p">+</span> <span class="n">lazy</span> <span class="k">event</span> <span class="n">index</span>
</code></pre></div></div>

<p>複雜度大致如下：</p>

<p><img src="/assets/f7bec8f79c08/1*5HKeVhHLMRIWnuJJmmGTbQ.webp" alt="" loading="lazy" decoding="async" width="396" height="197" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzOTYiIGhlaWdodD0iMTk3Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>其中：</p>
<div class="language-ini highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="py">m_e</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s">某個 element 自己的 merged interval 數量</span>
<span class="py">k</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s">本次 insert 吸收/合併掉的舊 intervals 數量</span>
<span class="py">M</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s">所有 elements 的 merged intervals 總數</span>
<span class="py">r</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s">查詢位置實際 active 的 elements 數量</span>
</code></pre></div></div>

<p>RFC 也指出，如果同一個位置真的有 <code class="language-plaintext highlighter-rouge">m</code> 個元素 active，那輸出本身就需要 <code class="language-plaintext highlighter-rouge">Ω(m)</code> ，所以 <code class="language-plaintext highlighter-rouge">O(log M + m)</code> 已經是 output-sensitive 的合理界線。</p>
<h4 id="為什麼不用-interval-tree--segment-tree--roaring-bitmap">為什麼不用 Interval Tree / Segment Tree / Roaring Bitmap？</h4>

<p>RFC 有一整節比較替代方案，最後選擇 <code class="language-plaintext highlighter-rouge">(a) Per-element sorted disjoint list + lazy event index</code> 。</p>

<p>重點如下：</p>

<p><img src="/assets/f7bec8f79c08/1*psrNFxHTKrwba92dA0rOLw.webp" alt="" loading="lazy" decoding="async" width="891" height="275" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4OTEiIGhlaWdodD0iMjc1Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>RFC 總結選擇主結構的三個原因：</p>
<ol>
  <li>per-element merge 語意自然， <code class="language-plaintext highlighter-rouge">getRange</code> 可以直接回傳 <code class="language-plaintext highlighter-rouge">R(e)</code></li>
  <li>deterministic、跨語言可重現</li>
  <li>lazy index 很適合 build-once-then-query workload</li>
</ol>

<h4 id="v1-不包含的功能">v1 不包含的功能</h4>

<p>RFC 明確列出 v1 不做：</p>
<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">remove</span><span class="p">(</span><span class="n">e</span><span class="p">,</span> <span class="k">start</span><span class="p">,</span> <span class="k">end</span><span class="p">)</span>
<span class="n">remove</span><span class="p">(</span><span class="n">e</span><span class="p">)</span>
<span class="n">clear</span>
<span class="k">union</span> <span class="o">/</span> <span class="k">intersect</span> <span class="o">/</span> <span class="n">difference</span>
<span class="n">persistent</span> <span class="k">immutable</span> <span class="n">snapshot</span>
<span class="n">floating</span><span class="o">-</span><span class="n">point</span> <span class="n">coordinates</span>
<span class="n">multi</span><span class="o">-</span><span class="n">dimensional</span> <span class="n">rectangle</span> <span class="n">stabbing</span>
<span class="n">r</span><span class="p">[</span><span class="n">lo</span><span class="p">...</span><span class="n">hi</span><span class="p">].</span><span class="n">objs</span> <span class="err">這種</span> <span class="k">range</span> <span class="n">slot</span> <span class="n">query</span>
</code></pre></div></div>

<p>刪除、交集這塊也請 AI 著手在 RFC V2 實現。</p>
<h3 id="請-ai-基於-rangeable-rfc-實現語言實作">請 AI 基於 <a href="https://github.com/ZhgChgLi/RangeableRFC" target="_blank">Rangeable RFC</a> 實現語言實作</h3>
<h4 id="流程-1">流程</h4>
<ol>
  <li>一樣是先走 <code class="language-plaintext highlighter-rouge">/plan</code> Plan Mode 請 AI 詳細閱讀 RFC 並規劃實作</li>
  <li>一樣拆分 developer / reviewer 一個做一個審核，直到沒問題</li>
  <li>撰寫 Readme 使用文件、Push 到 GitHub</li>
</ol>

<blockquote>
  <p><em>最困難的 RFC 規格研究已經完成後面的實作只要 AI 遵照這個規格實現，幾乎都不會有問題。</em></p>
</blockquote>

<h4 id="目前已實現的語言如下">目前已實現的語言如下</h4>
<ul>
  <li>Ruby 3.2+ <a href="https://github.com/ZhgChgLi/RubyRangeable" target="_blank">github.com/ZhgChgLi/RubyRangeable</a></li>
  <li>Swift 5.7+ <a href="https://github.com/ZhgChgLi/SwiftRangeable" target="_blank">github.com/ZhgChgLi/SwiftRangeable</a></li>
  <li>Python 3.10+ <a href="https://github.com/ZhgChgLi/PythonRangeable" target="_blank">github.com/ZhgChgLi/PythonRangeable</a></li>
  <li>TypeScript / JS (Node 18+) <a href="https://github.com/ZhgChgLi/JSRangeable" target="_blank">github.com/ZhgChgLi/JSRangeable</a></li>
  <li>Kotlin / JVM 11+ <a href="https://github.com/ZhgChgLi/KotlinRangeable" target="_blank">github.com/ZhgChgLi/KotlinRangeable</a></li>
  <li>Go 1.22+ <a href="https://github.com/ZhgChgLi/GoRangeable" target="_blank">github.com/ZhgChgLi/GoRangeable</a></li>
</ul>

<h3 id="實際應用-ai-設計開發的--rangeable-foundation">實際應用 AI 設計開發的 — Rangeable Foundation</h3>

<p>東西都準備好之後，回到最一開始的開源專案 ZMediumToMarkdown 的算法問題本身，請 AI 套用這個 Lib 回到、移除原本複雜的 walkthrough 做法優化架構＋ <strong>補上驗證套用前後測試</strong> 。</p>
<h4 id="zmediumtomarkdown-360"><a href="https://github.com/ZhgChgLi/ZMediumToMarkdown/releases/tag/v3.6.0" target="_blank">ZMediumToMarkdown 3.6.0</a></h4>

<p><strong>Performance</strong></p>
<ul>
  <li>Micro-benchmark: 5.5× faster on the markup render hot path.</li>
  <li>End-to-end on a real Medium article: 2.23× faster
(2073 µs → 930 µs per paragraph on average).</li>
</ul>

<h4 id="avplayer-本地-cache-實作攻略"><a href="https://zhgchg.li/posts/zrealm-dev/avplayer-%E6%9C%AC%E5%9C%B0-cache-%E5%AF%A6%E4%BD%9C%E6%94%BB%E7%95%A5-%E4%BD%BF%E7%94%A8-avassetresourceloaderdelegate-%E7%AF%80%E7%9C%81-ios-%E9%9F%B3%E6%A8%82%E4%B8%B2%E6%B5%81%E6%B5%81%E9%87%8F-6ce488898003/#20260510-updated" target="_blank">AVPlayer 本地 Cache 實作攻略</a></h4>

<p>文章內容也多補充了套用 SwiftRangeable 的案例。</p>
<h3 id="讚嘆-ai">讚嘆 AI</h3>

<p>原本都只拿 AI 做一些應用問題，例如 <a href="/posts/zrealm-dev/jekyll-blog-自訂主題設計-用-claude-design-claude-code-快速打造專屬風格-6bf79c5b4dab/">重新設計網站</a> 、 <a href="/posts/zrealm-dev/別再從零開始-ai-寫程式-讓-ai-agent-直接幫你搞定-google-apps-script-串接與開發-35cc65327d28/">個人 Dashboard</a> 亦或是工作上的產品問題修正；首次嘗試請他深入研究解決算法、底層資料設計的問題。</p>

<p>效果超乎我的想像，大略閱讀了他寫的 RFC，不管是格式還是內容都不比真實的人去研究寫出來的東西還差(至少一定比我寫的好)，中間有觀察他在做什麼，他會用 Web Search 去搜尋公開的相關論文或是技術文獻然後整合評估實作合理性；拆分出 doer / reviewer 的效果也很顯著，doer 會太著重在實作而 reviewer 能以更廣更高的視角來縱觀看 doer 做的東西有沒有 side effect；最後 RFC 定下後再請 AI 基於這個文件實作，準確度幾乎是 100%。</p>
<h4 id="ai-使用的反思">AI 使用的反思</h4>

<p>不過大家也不用怕被 AI 取代，主要還是解決問題的思維，這個 AI 就很弱了；例如如果直接叫他去優化 ZMediumToMarkdown 他可能只能專注在 Medium 的場景還有原本程式的寫法； <strong>但是人知道可以抽成 Foundation，可以先研究 RFC，最後再實踐，這樣效果更好；當然我們也可以自己做，只是需要時間，AI 是幫我們加速這個過程，不是取代我們。</strong></p>]]></content>
  </entry><entry>
    <title type="html">IQUNIX MG75 Pro 三模鋁合金機械鍵盤｜輕羽軸低矮軸設計，MacOS 完美支援</title>
    <link href="https://zhgchg.li/posts/zrealm-life/iqunix-mg75-pro-%E4%B8%89%E6%A8%A1%E9%8B%81%E5%90%88%E9%87%91%E6%A9%9F%E6%A2%B0%E9%8D%B5%E7%9B%A4-%E8%BC%95%E7%BE%BD%E8%BB%B8%E4%BD%8E%E7%9F%AE%E8%BB%B8%E8%A8%AD%E8%A8%88-macos-%E5%AE%8C%E7%BE%8E%E6%94%AF%E6%8F%B4-0d4a5fd2929b/" rel="alternate" type="text/html" title="IQUNIX MG75 Pro 三模鋁合金機械鍵盤｜輕羽軸低矮軸設計，MacOS 完美支援" />
    <published>2026-05-09T00:33:11+08:00</published>
    <updated>2026-05-09T14:07:27+08:00</updated>
    <id>https://zhgchg.li/posts/zrealm-life/0d4a5fd2929b</id><summary type="html">Mac用戶首選IQUNIX MG75 Pro，搭載輕羽軸與矮軸設計，結合鋁合金外殼與三模連接，提供安靜且有手感的打字體驗，解決巧控鍵盤手感不足問題，輕鬆提升工作效率與舒適度。</summary><author>
      <name>ZhgChgLi</name>
    </author><category term="ZRealm Life." /><category term="iqunix" /><category term="淘寶" /><category term="生活" /><category term="開箱" /><category term="機械鍵盤" /><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://zhgchg.li/assets/0d4a5fd2929b/1*UYsoPN7LJA8eLwCW3neySg.webp" /><content type="html" xml:base="https://zhgchg.li/posts/zrealm-life/iqunix-mg75-pro-%E4%B8%89%E6%A8%A1%E9%8B%81%E5%90%88%E9%87%91%E6%A9%9F%E6%A2%B0%E9%8D%B5%E7%9B%A4-%E8%BC%95%E7%BE%BD%E8%BB%B8%E4%BD%8E%E7%9F%AE%E8%BB%B8%E8%A8%AD%E8%A8%88-macos-%E5%AE%8C%E7%BE%8E%E6%94%AF%E6%8F%B4-0d4a5fd2929b/"><![CDATA[<h3 id="iqunix-mg-657596-pro-矮軸輕羽軸三模鋁合金外殼機械鍵盤夜遊黑開箱">IQUNIX MG 65/75/96 Pro 矮軸輕羽軸三模鋁合金外殼機械鍵盤夜遊黑開箱</h3>

<p>MacOS 可用的鋁合金質感與輕快手感的 IQUNIX MG75 Pro 開箱</p>

<p><img src="/assets/0d4a5fd2929b/1*UYsoPN7LJA8eLwCW3neySg.webp" alt="MG75 Pro 開箱體驗" loading="lazy" decoding="async" width="1200" height="902" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><a href="https://iqunix.com/products/iqunix-magi75-96-aluminum-low-profile-mechanical-keyboard?variant=50355797393714" target="_blank">MG75 Pro 開箱體驗</a></p>
<h4 id="前言">前言</h4>

<p>好久沒寫開箱文，也沒什麼特別的東西可以開箱；最近剛好用了兩年的二手機械鍵盤壽終正寢(其實是潑到水)，是同事自己組裝的高軸鍵盤(基底是 DUKHARO — VN66) 軸體應該是黃軸/白軸。</p>

<p><img src="/assets/0d4a5fd2929b/1*tBmCni-mJ0ZPat8Uf7dqfQ.webp" alt="舊的鍵盤" loading="lazy" decoding="async" width="1200" height="491" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjQ5MSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>舊的鍵盤</p>

<p>入坑機械鍵盤之前最喜歡用的是 Apple 巧控鍵盤，我比較喜歡低矮軸的鍵盤，又是蘋果用戶，那就飛這款莫屬。</p>

<p><img src="/assets/0d4a5fd2929b/1*vMEz6OFmApz2ZXj2aeQq5A.webp" alt="Apple 巧控鍵盤" loading="lazy" decoding="async" width="1200" height="331" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjMzMSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>Apple 巧控鍵盤</p>

<p>但是手感上就是純巧克力鍵盤，乾扁沒有任何反饋手感。</p>
<h3 id="iqunix-mg657596-pro-矮軸輕羽軸三模鋁合金外殼機械鍵盤">IQUNIX MG65/75/96 Pro 矮軸輕羽軸三模鋁合金外殼機械鍵盤</h3>

<p><img src="/assets/0d4a5fd2929b/1*5tbfNVtQ2DxnxZNDApzf_A.webp" alt="https://iqunix.com/products/iqunix-magi75-96-aluminum-low-profile-mechanical-keyboard?variant=50355797590322" loading="lazy" decoding="async" width="2908" height="1042" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyOTA4IiBoZWlnaHQ9IjEwNDIiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><a href="https://iqunix.com/products/iqunix-magi75-96-aluminum-low-profile-mechanical-keyboard?variant=50355797590322" target="_blank">https://iqunix.com/products/iqunix-magi75-96-aluminum-low-profile-mechanical-keyboard?variant=50355797590322</a></p>

<p>這塊也算是被同事燒到的，就在我入手上一個鍵盤沒多久辦公室就有人買這款；親自去按了幾次，確認是我的夢中情「盤」矮軸符合我喜歡用巧克力鍵盤加上「輕羽軸」有手感但又不會太吵的特性，還有亮麗的外觀跟多功能控制台，最重要的事是原廠就 MacOS 鍵位；現在終於有機會能入手了。</p>

<p><img src="/assets/0d4a5fd2929b/1*qKmRbmaj27xzB1Fdb4elhQ.webp" alt="圖片取自 官方商品圖" loading="lazy" decoding="async" width="1200" height="509" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjUwOSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>圖片取自 <a href="https://e.tb.cn/h.iBDV6UGnYYloGER?tk=m11t5opK0Zg" target="_blank">官方商品圖</a></p>
<h4 id="軸體--訂製款-輕羽軸-官方行銷文案gold-red-switch-規格書"><strong>軸體 — 訂製款</strong> 輕羽軸 (官方行銷文案)、Gold Red Switch (規格書)</h4>
<ul>
  <li>壓力克數：40 ± 10 Ggf</li>
  <li>全行程 (全距離)：2.8 mm ± 0.25</li>
  <li>導通行程 (按鍵從最上方開始往下壓，到「觸發訊號」那一刻所需要的距離)：1.2 mm ± 0.30</li>
  <li>壽命：5 千萬次</li>
</ul>

<h4 id="鍵帽">鍵帽</h4>
<ul>
  <li>高含量 PBT + 抗污塗層</li>
  <li>類膚質質感</li>
</ul>

<h4 id="外殼">外殼</h4>
<ul>
  <li>CNC 加工全鋁合金</li>
  <li>高度 11 mm(低處) — 25.5 mm(高處)</li>
  <li>LE-Tray 結構、Poron 夾心棉、PET 墊底、IXPE 墊下軸、Poron 底棉、IXPE 底墊</li>
</ul>

<h4 id="使用參數">使用參數</h4>
<ul>
  <li>Type-C</li>
  <li>支援熱插拔、VIA</li>
  <li>支援連接：有線、藍芽 5.1、2.4 GHz 無線</li>
  <li>支援多彩 RGB 控制背光</li>
  <li>支援：Windows / macOS / iOS / Android
附可替換 Windows / macOS 鍵帽</li>
</ul>

<h4 id="型號-mg65--mg75--mg96--mg65-pro--mg75-pro--mg96-pro">型號 MG65 / MG75 / MG96 / MG65 Pro / MG75 Pro / MG96 Pro</h4>

<p><img src="/assets/0d4a5fd2929b/1*XHoih2u5-Azazt5Gkd1IKA.webp" alt="" loading="lazy" decoding="async" width="1200" height="1071" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjEwNzEiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>主要差別是 鍵盤配置(65/75/96) 跟 Pro 有右方多媒體控制板(+5 鍵)。</p>
<ul>
  <li>MG65: 68鍵 電池 3,000mAh
900g / MG65 Pro 1030g</li>
  <li>MG75: 84鍵 電池 4,000mAh
947g / MG75 Pro 1110g</li>
  <li>MG96: 100鍵 電池 4,000mAh
1144g / MG96 Pro 1296g</li>
</ul>

<p><strong>多媒體控制板功能：</strong></p>

<p><img src="/assets/0d4a5fd2929b/1*zIGfXexH_24_Pt1sTnVjBA.webp" alt="" loading="lazy" decoding="async" width="1172" height="1042" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMTcyIiBoZWlnaHQ9IjEwNDIiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>最下方是 2.4G USB 接收器收納倉，無功能。</p>

<p><strong>外觀：</strong></p>

<p><img src="/assets/0d4a5fd2929b/1*D69-323N4fUoRribRtXGnQ.webp" alt="" loading="lazy" decoding="async" width="1200" height="870" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg3MCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<ul>
  <li>霧蘇白</li>
  <li>夜遊黑</li>
</ul>

<h4 id="價格方面">價格方面</h4>

<p>照淘寶 上面 <a href="https://e.tb.cn/h.iBDV6UGnYYloGER?tk=m11t5opK0Zg" target="_blank"><strong>(天貓 — IQUNIX 旗艦店)</strong></a> 2026/05 售價:</p>
<ul>
  <li>MG65: 人民幣 $699</li>
  <li>MG75: 人民幣 $899</li>
  <li>MG96: 人民幣 $899</li>
  <li>MG65 Pro: 人民幣 $899</li>
  <li>MG75 Pro: 人民幣 $999</li>
  <li>MG96 Pro: 人民幣 $1,099</li>
</ul>

<h4 id="選擇">選擇</h4>

<p>MG65 沒有 F 系列按鍵所以不考慮，基礎從 MG75 開始，MG96 感覺太大；Pro 功能可有可無但是因為價差不大所以就升級了， <strong>最終選擇購買 — MG75 Pro。</strong></p>
<h4 id="購買渠道">購買渠道</h4>
<ul>
  <li>台灣無代理或是代理商已無進貨</li>
  <li>海外官網 <a href="https://iqunix.com/products/iqunix-magi75-96-aluminum-low-profile-mechanical-keyboard?variant=50355797590322" target="_blank">https://iqunix.com/</a> 但是價格(MG75Pro)加運費逼近 <code class="language-plaintext highlighter-rouge">NT$6,200</code></li>
  <li><strong>淘寶官方 <a href="https://e.tb.cn/h.iBDV6UGnYYloGER?tk=m11t5opK0Zg" target="_blank">IQUNIX 旗艦店</a></strong> MG75PRO 運費加包稅總共支付 <code class="language-plaintext highlighter-rouge">NT$4,798</code></li>
</ul>

<blockquote>
  <p><em>白色比黑色還熱門，我看每週一上完架之後到週末就都售完了；我是買 <strong>黑色 MG75 PRO。</strong></em></p>
</blockquote>

<h4 id="淘寶官方-iqunix-旗艦店"><strong>淘寶官方 <a href="https://e.tb.cn/h.iBDV6UGnYYloGER?tk=m11t5opK0Zg" target="_blank">IQUNIX 旗艦店</a></strong></h4>

<p>不得不說現在買淘寶超方便，不需要自己研究集運也不用中國帳戶、信用卡；可以直接用街口、Apple Pay、信用卡線上支付；然後官方集/直運寄到你家甚至可以超商取貨。</p>
<ul>
  <li>05/03 凌晨下單、早上出貨</li>
  <li>05/05 船舶出港</li>
  <li>05/06 抵達台灣</li>
  <li>05/08 中午收到貨</li>
</ul>

<p>如果你沒有從海外買東西回來的經驗， <a href="https://www.rakuten.com.tw/magazine/life/2022/012802/#D" target="_blank">務必要完成 EZWay 實名驗證，下載 EZWay App、完成註冊、完成實名認證即可</a> ； <strong>大約在出貨後幾天會通知有一筆報關紀錄，進入 App 按確認就可以了。</strong></p>
<h3 id="iqunix-mg75-pro-矮軸輕羽軸三模鋁合金外殼機械鍵盤夜遊黑開箱">IQUNIX MG75 Pro 矮軸輕羽軸三模鋁合金外殼機械鍵盤夜遊黑開箱</h3>

<p><img src="/assets/0d4a5fd2929b/1*8yLYKhBLcd0WbWRXtVteCw.webp" alt="" loading="lazy" decoding="async" width="1200" height="844" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg0NCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>包裝非常簡單，就是一個跟鍵盤一樣大的紙盒子…其實有點怕會撞壞，但還好鍵盤收納包是硬的。</p>

<p><img src="/assets/0d4a5fd2929b/1*PL9Fbr5_nRpgLEgKtrcVJA.webp" alt="" loading="lazy" decoding="async" width="1200" height="849" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg0OSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>打開就是鍵盤收納包，包裝就只是直接放進紙箱子。</p>

<p><img src="/assets/0d4a5fd2929b/1*-hTJy3Cz8YSTFHX60HRzlg.webp" alt="" loading="lazy" decoding="async" width="569" height="178" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1NjkiIGhlaWdodD0iMTc4Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>看官方商品頁說明好像是新包裝，直接替換成鍵盤收納包(等於多送你一個盒子)。</p>

<p><img src="/assets/0d4a5fd2929b/1*o4caqnDEgtJQBynfPEEKAg.webp" alt="" loading="lazy" decoding="async" width="1690" height="1240" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNjkwIiBoZWlnaHQ9IjEyNDAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>鍵盤包打開就是鍵盤本體、說明書、配件。</p>

<p><img src="/assets/0d4a5fd2929b/1*aRdH-fuap85bju8r5WHMdg.webp" alt="" loading="lazy" decoding="async" width="1602" height="1205" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNjAyIiBoZWlnaHQ9IjEyMDUiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>配件有：2.4Ghz 接收器、USB-A to USB-C 傳輸線、送的替換鍵帽、拔鍵器。</p>

<p><img src="/assets/0d4a5fd2929b/1*lqklyMPAqRFuNskhqcJwrg.webp" alt="" loading="lazy" decoding="async" width="1200" height="887" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg4NyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>原廠預設就是 macOS 鍵帽，就看自己想不想換上多送的 X 鋁合金鍵帽增加質感；送的數據線是彈簧線可以伸比較長也好收納，這點不錯。</p>

<p><img src="/assets/0d4a5fd2929b/1*DZJLlUoscPXZsDWKq7sVpw.webp" alt="" loading="lazy" decoding="async" width="1594" height="621" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNTk0IiBoZWlnaHQ9IjYyMSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>鍵盤背面，也很有金屬質感；唯一美中不足是沒有內建的支架可以調仰角角度。</p>

<p><img src="/assets/0d4a5fd2929b/1*IbjacAUuMmG36KG1yAA11w.webp" alt="" loading="lazy" decoding="async" width="1724" height="1038" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNzI0IiBoZWlnaHQ9IjEwMzgiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>鍵高跟原本的鍵盤相比。</p>

<p><img src="/assets/0d4a5fd2929b/1*8Q3XPHzX7coQBMmshcxLlQ.webp" alt="" loading="lazy" decoding="async" width="1536" height="2048" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNTM2IiBoZWlnaHQ9IjIwNDgiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>直接先把 X 換上去了。</p>
<h4 id="本體細節">本體細節</h4>

<p><img src="/assets/0d4a5fd2929b/1*CsWUoUK0HSqcgPJ7rWFMBw.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>買的時候沒注意到，原來這塊面板上還有一條光條會發光。</p>

<p><img src="/assets/0d4a5fd2929b/1*_-epZwLtuUlUBwUolthLgw.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/0d4a5fd2929b/1*eCMkEokNFYEDkdmPkqGpeg.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/0d4a5fd2929b/1*aJ3Q02uvkViRzL2eoQYXYQ.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/0d4a5fd2929b/1*bYC27o6XOVINVrab6Nq7rQ.webp" alt="" loading="lazy" decoding="async" width="1200" height="901" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/0d4a5fd2929b/1*nAz4ZUL677wsJsYBf7Zz-g.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>按鍵本體是沈穩的黑色磨砂質感，鍵盤本身是磨砂鋁合金非常有質感，拿起來穩重務實。</p>
<h4 id="說明書">說明書</h4>

<p><img src="/assets/0d4a5fd2929b/1*EE1mOGy6Qm8NYhN0uBnSGw.webp" alt="" loading="lazy" decoding="async" width="2048" height="721" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDQ4IiBoZWlnaHQ9IjcyMSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/0d4a5fd2929b/1*NCGdcSIYG1S6qHzJLeu5bA.webp" alt="" loading="lazy" decoding="async" width="821" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4MjEiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>基本上跟所有機械鍵盤差不多，反正就燈光調整跟連線方式設定。</p>
<h4 id="啟動流程">啟動流程</h4>
<ul>
  <li>接線插到電腦上</li>
  <li>長按 <code class="language-plaintext highlighter-rouge">FN + ESC</code> 3 秒以上鍵盤開機 (Tab 底光會閃)</li>
  <li>切換成 macOS 鍵位配置： <code class="language-plaintext highlighter-rouge">FN + Tab</code> <strong>(macOS 務必設定，不然 CMD 會變要按 CTRL)</strong></li>
  <li>設定背光： <code class="language-plaintext highlighter-rouge">FN + [</code> / <code class="language-plaintext highlighter-rouge">FN + ]</code> / <code class="language-plaintext highlighter-rouge">FN + Shift</code> / <code class="language-plaintext highlighter-rouge">FN + Enter</code></li>
</ul>

<p><img src="/assets/0d4a5fd2929b/1*2XHm8O4TUpCVmHiF0GrIrQ.webp" alt="" loading="lazy" decoding="async" width="2048" height="1536" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDQ4IiBoZWlnaHQ9IjE1MzYiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<h4 id="實際按鍵聲音">實際按鍵聲音</h4>
<iframe class="embed-video" loading="lazy" src="https://www.youtube.com/embed/Md2yzNBa1K4" title="IQUNIX MG 65/75/96 Pro 矮軸輕羽軸三模鋁合金外殼機械鍵盤夜遊黑開箱、按鍵聲音" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>

<blockquote>
  <p><em>請以對照大小為主，因為 iPhone 收音關係會放大實際音量。</em></p>
</blockquote>

<h4 id="使用手感">使用手感</h4>

<p>聲音比原本的沉穩安靜許多但又不失機械鍵盤的反饋手感，目前用起來挺舒服的；缺點可能是右邊的按鍵太密集還不習慣很容易按錯，例如想按刪除按到 Home 鍵。</p>]]></content>
  </entry><entry>
    <title type="html">別再從零開始 AI 寫程式：讓 AI Agent 直接幫你搞定 Google Apps Script 串接與開發</title>
    <link href="https://zhgchg.li/posts/zrealm-dev/%E5%88%A5%E5%86%8D%E5%BE%9E%E9%9B%B6%E9%96%8B%E5%A7%8B-ai-%E5%AF%AB%E7%A8%8B%E5%BC%8F-%E8%AE%93-ai-agent-%E7%9B%B4%E6%8E%A5%E5%B9%AB%E4%BD%A0%E6%90%9E%E5%AE%9A-google-apps-script-%E4%B8%B2%E6%8E%A5%E8%88%87%E9%96%8B%E7%99%BC-35cc65327d28/" rel="alternate" type="text/html" title="別再從零開始 AI 寫程式：讓 AI Agent 直接幫你搞定 Google Apps Script 串接與開發" />
    <published>2026-05-03T21:06:17+08:00</published>
    <updated>2026-05-09T00:56:44+08:00</updated>
    <id>https://zhgchg.li/posts/zrealm-dev/35cc65327d28</id><summary type="html">個人或團隊想用 Google Apps Script 自動化流程卻苦於串接複雜？透過 AI Agent 直接從零開發，3 小時內完成高準確度專案，實現個人桌面 Dashboard 串接天氣、行事曆與倒數日資料，提升開發效率與穩定性。</summary><author>
      <name>ZhgChgLi</name>
    </author><category term="ZRealm Dev." /><category term="claude-code" /><category term="google-apps-script" /><category term="dashboard" /><category term="ai" /><category term="claude" /><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://zhgchg.li/assets/35cc65327d28/1*QFRr50u8zbS5WtVtvprG9w.webp" /><content type="html" xml:base="https://zhgchg.li/posts/zrealm-dev/%E5%88%A5%E5%86%8D%E5%BE%9E%E9%9B%B6%E9%96%8B%E5%A7%8B-ai-%E5%AF%AB%E7%A8%8B%E5%BC%8F-%E8%AE%93-ai-agent-%E7%9B%B4%E6%8E%A5%E5%B9%AB%E4%BD%A0%E6%90%9E%E5%AE%9A-google-apps-script-%E4%B8%B2%E6%8E%A5%E8%88%87%E9%96%8B%E7%99%BC-35cc65327d28/"><![CDATA[<h3 id="別再從零開始-ai-寫程式讓-ai-agent-直接幫你搞定-google-apps-script-串接與開發">別再從零開始 AI 寫程式：讓 AI Agent 直接幫你搞定 Google Apps Script 串接與開發</h3>

<p>以 AI 變廢為寶 —用 Claude Design &amp; Claude Code 打造屬於你的個人桌面 Dashboard 為例。</p>

<p><img src="/assets/35cc65327d28/1*QFRr50u8zbS5WtVtvprG9w.webp" alt="" loading="lazy" decoding="async" width="1448" height="1086" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDQ4IiBoZWlnaHQ9IjEwODYiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<h4 id="完成品-demo">完成品 Demo</h4>
<iframe class="embed-video" loading="lazy" src="https://www.youtube.com/embed/Zu0rbnu_iy8" title="AI 變廢為寶 - 用 Claude Design &amp; Claude Code 打造屬於你的個人桌面 Dashboard" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>

<blockquote>
  <p><em>把塵封在抽屜許多年的 iPhone 8 Plus 搖身一變成個人專屬的桌上 Dashboard Deck，內容完全客製化、製作完全免費。</em></p>
</blockquote>

<h3 id="前言">前言</h3>

<blockquote>
  <p><strong><em>TL;DR 如果你只在意怎麼用 AI 打造屬於你的個人桌面 Dashboard 請忽略此章節。</em></strong></p>
</blockquote>

<p>大約是從 2021 年開始用 Google Apps Script 開發小工具串接自動化優化工作流程；那個年代沒有 AI、RPA 流程自動化也很少人在討論；很單純只是希望提升團隊協作效率，或提高個人生活舒適度，所有腳本都是我一行一行手刻出來的(用現在來看就是古法寫程式)， <strong>但最難的其實不是寫程式本身，而是能不能串接？要怎麼串接？思維要怎麼跳脫框架？</strong></p>
<h4 id="之前做過的案例">之前做過的案例</h4>
<ol>
  <li><strong>目標 —對外公開的 Email 窗口收到信時希望能轉發到 Slack 工作 Channel。</strong> 
<strong>技術上 Google Apps Script 沒有當 Gmail 收到信件的事件</strong> ，因此我們換一條思路用排程功能每分鐘(或更長)執行一次：檢查未讀信件 ➡️ 讀取內容️️ ➡️ Strip HTML 轉發到 Slack ➡️ 標記已讀。
如果只想針對特定信件(寄件人、標題內容)那就在 Gmail 上設定 Label Filter、檢查未讀信件改成 檢查未讀信件+Label 就能達成。
ref: 「 <a href="/posts/zrealm-robotic-process-automation/google-apps-script-教學-自動轉發-gmail-郵件到-slack-channel-客製化通知-d414bdbdb8c9/">運用 Google Apps Script 轉發 Gmail 信件到 Slack</a> 」</li>
  <li><strong>目標 — 自動查詢 GA 流量/Crash-free rate 發到 Slack 工作 Channel。</strong> 
Google Apps Script 本身就有現成的 AnalyticsData / AdSense Library，只要引入並設定好參數就能直接使用，幾乎是無痛整合。
如果 App 想查詢閃退 Issue 細節、Top 10，只要再多做一步 <strong>Firebase to BigQuery</strong> 再從 Google Apps Script 引入 BigQuery Library 然後下 SQL 查詢就能做到。
ref: 「 <a href="/posts/zrealm-robotic-process-automation/crashlytics與google-analytics自動查詢app-crash-free-users-rate-google-apps-script整合實作-793cb8f89b72/">Crashlytics + Google Analytics 自動查詢 App Crash-Free Users Rate</a> 」、「 <a href="/posts/zrealm-robotic-process-automation/crashlytics-big-query-串接教學-自動查詢並轉發-ios-app-閃退報告到-slack-e77b80cc6f89/">Crashlytics + Big Query 打造更即時便利的 Crash 追蹤工具</a> 」</li>
  <li><strong>目標 — 自動彙整營運數據到 Google Sheet。</strong> 
承上做法，當時多做個 Web App 讓整個營運數據能在團隊的電視牆上一目瞭然； <strong>那時候遇到的技術問題是部分數據是在內部</strong> ，外網無法穿透，所幸反過來改內網排程，每日發送數據到 Google Apps Script Web App，接收後處理填回 Google Sheet。
ref:「 <a href="/posts/zrealm-robotic-process-automation/google-apps-script-打造每日數據報表自動化-rpa-快速串接-google-analytics-與-google-sheet-f6713ba3fee3/">使用 Google Apps Script 實現每日數據報表 RPA 自動化</a> 」</li>
  <li><strong>目標 — 用現有資源建置一個簡易的 App 打包平台</strong> 
當時的背景是 App 打包服務遷移至 GitHub Actions，但是非工程團隊也會有 App 打包需求，以往都是透過人工處理；需要一個公司團隊內都可以用的服務來串接 GitHub Actions 來執行打包。
用 Google Apps Script Web App 當平台、鎖住只有組織內帳號可使用，在平台表單填好打包資訊轉手觸發 GitHub Actions (GitHub API) 並發送打包 Slack 通知給對方。
App 打包好是上傳到 Firebase App Distribution， <strong>可是 Google Apps Script 沒有內建的 Library，不過 <a href="/posts/zrealm-robotic-process-automation/google-apps-script-快速串接-firebase-app-distribution-api-教學與進階設定-71400d408dc8/">研究了一下可以直接用 googleapis 達成</a> ，最後也是無痛的串起來。</strong> 
ref:「 <a href="/posts/zrealm-dev/ci-cd-打包工具平台-使用-google-apps-script-web-app-串接-github-actions-提升跨團隊效率-4273e57e7148/">使用 Google Apps Script Web App 串接 GitHub Actions 建置免費易用的打包工具平台</a> 」</li>
</ol>

<p>還有一些太小的案例就不列舉出來了， <strong>總之想傳達的是「辦法總比困難多」實際寫程式反而是最簡單的，要如何突破重重障礙串接起來才是困難的</strong> 。</p>
<h4 id="技術限制">技術限制</h4>

<p>「辦法總比困難多」的前提，是 <strong>技術上可行且合理</strong> ；換句話說「方向錯了，努力也是徒勞」，我們可以不用實際寫程式但一定要知道何時可以「選擇」使用 Google Apps Script 開發小工具。</p>
<ul>
  <li><strong>User-Agent 無法自訂：</strong> 基於安全與防止濫用原因，Google Apps Script User-Agent 是無法自己指定的。(但其他 Header 欄位可以指定)
<strong>影響：</strong> 有些罕見的 API 服務需要帶資料在 User-Agent、目標網站 / API 封鎖 Google Apps Script User-Agent，這兩種情況都是硬限制、無法使用。
<strong>Workaround：</strong> <a href="https://zhgchg.li/posts/88f0fb935120/" target="_blank">如果你硬要用的話可以透過 Cloudflare Worker 進行 Proxy 橋接。</a></li>
  <li><strong>最長執行 6 分鐘：</strong> 每次執行最多 6 分鐘，任務太大太多建議分段處理。
另外總額度也有上限例如 Trigger 一天最多執行 90 分鐘。 <strong>(但這條似乎是軟限制，我的腳本很多應該超過上限了卻沒有被阻擋)</strong></li>
  <li><strong>單次執行以同步阻塞為主</strong> ：你無法在單次 execution 內派發多個執行緒並行處理任務；若任務耗時或需要大量並行處理，建議拆成多次執行、排程觸發。</li>
  <li><strong>冷啟動：</strong> Faas 服務的通病，當任務太久沒觸發會進入睡眠、再次觸發時需要等待較長的時間。
這問題加上任務需同步(阻塞)執行的限制，如果是部署成 Webhook 讓其他服務來呼叫很容易會被判斷成請求失敗，可能會重複請求、重複收到、重複執行。
例如：Slack 硬要求 Webhook 服務端需在 3 秒內回應，以往經驗很常踩到這個問題。</li>
  <li><strong>Web App API 會跳轉址：</strong> 基於安全性，Content service 回傳的內容會 redirect 到一次性 <code class="language-plaintext highlighter-rouge">script.googleusercontent.com</code> URL；HTTP client 需要支援 follow redirects。
有的 Webhook 服務不會自動 302 跳轉就會失敗(例如：Jira Webhook)。</li>
  <li><strong>Web App HTML</strong> ：網頁頂部會有防濫用聲明、無法達成真 RWD (頁面實際是 iframe 在 google 框架內)、網址很醜無法自訂、無法做到 PWA 滿版頁面。</li>
</ul>

<p><img src="/assets/35cc65327d28/1*NI80VyLt-hHAKJskJLdTmg.webp" alt="" loading="lazy" decoding="async" width="693" height="196" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2OTMiIGhlaWdodD0iMTk2Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<ul>
  <li><strong>服務限制：</strong> 還有一些服務取用的限制，不過正常使用下不太會踩到上限，除非你是需要高頻檢查即時響應，那就很容易摸到限制。</li>
</ul>

<p><img src="/assets/35cc65327d28/1*bg8nLfiGaVJ7xqhIQewp1g.webp" alt="https://developers.google.com/apps-script/guides/services/quotas?hl=zh-tw" loading="lazy" decoding="async" width="1200" height="954" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijk1NCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><a href="https://developers.google.com/apps-script/guides/services/quotas?hl=zh-tw" target="_blank">https://developers.google.com/apps-script/guides/services/quotas?hl=zh-tw</a></p>
<h4 id="優勢與功能">優勢與功能</h4>

<p>了解完限制再來看看有哪些優勢、功能。</p>

<p>最大的優勢是直接無痛串接 Google 本家相關服務，Web App 存取方式可以選擇腳本擁有者本人身份、組織內人員、已登入 Google 的帳號、所有人， <strong>不需要自己做複雜的 OAuth 流程，一鍵設定一鍵完成一鍵開始執行</strong> ：</p>

<p><strong>內建整合服務 Built-in Services:</strong> 
<strong>不需在 GAS 服務中引用就能使用。</strong></p>
<ul>
  <li>DocumentApp → Google Docs</li>
  <li>SpreadsheetApp → Google Sheets</li>
  <li>SlidesApp → Google Slides</li>
  <li>FormApp → Google Forms</li>
  <li>GmailApp → Gmail</li>
  <li>CalendarApp → Google Calendar</li>
  <li>DriveApp → Google Drive</li>
  <li>SitesApp → Google Sites</li>
  <li>Maps → 地圖 / 距離 / 路線</li>
  <li>Translate → 翻譯</li>
</ul>

<p><strong>進階服務 Advanced Services:</strong> 
<strong>需在 GAS 服務中引用才能使用。</strong></p>
<ul>
  <li>Sheets API</li>
  <li>Drive API</li>
  <li>Calendar API</li>
  <li>Gmail API</li>
  <li>Analytics API / Analytics Data API</li>
  <li>BigQuery API</li>
  <li>Adsense API</li>
  <li>YouTube Data API</li>
  <li>Tasks API</li>
  <li>Googleapis (Other/GCP)</li>
</ul>

<p><strong>工具 / 系統服務:</strong></p>
<ul>
  <li>UrlFetchApp → 呼叫外部 API</li>
  <li>PropertiesService → Key-Value 儲存</li>
  <li>CacheService → 快取</li>
  <li>LockService → 防併發控制</li>
  <li>Utilities → 日期 / Hash / Base64</li>
  <li>Logger → Log 輸出</li>
  <li>HtmlService → 建立 Web UI、支援 Ajax 非同步更新內容</li>
  <li>ContentService → 建立 API endpoint</li>
  <li>Trigger → 排程自動執行</li>
</ul>

<p>基本上開發上需要的東西該有的都有了，實務上就是把這些服務組合串接起來，就能達成流程自動化，例如：</p>
<ul>
  <li>自動查詢 GA 流量/Crash-free rate 發到 Slack 工作 Channel：
Analytics Data API 查資料、PropertiesService 儲存 Slack Bot Token、UrlFetchApp 打 Slack Send Message API</li>
  <li>自動彙整營運數據到 Google Sheet、數據 Dashboard Web：
Trigger 排程執行 Function、LockService 確保只有我在執行、Analytics Data API 查資料、UrlFetchApp 取得外部服務資料、SpreadsheetApp 寫入 Google Sheet、HtmlService 輸出 Web App 介面、ContentService 輸出 JSON 資料＋CacheService 資料緩存。</li>
</ul>

<h4 id="與-cloud-function--lambda-之類的-faas-差異">與 Cloud Function / Lambda 之類的 FAAS 差異</h4>

<p>Google Apps Script 可以視為是環繞 Google 服務場景的 FAAS 腳本工具，限制較多但是 <strong>目前完全免費、無痛串接 Google 服務；</strong> 其他 FAAS 服務通常需要付費，但有部分免費額度、串接 Google 服務需要走正規 IAM or OAuth 流程比較複雜繁瑣。</p>
<h4 id="不適合的場景">不適合的場景</h4>
<ul>
  <li>本身非 Google 家族，例如想串接 Microsoft Office or OneNote…</li>
  <li>地端雲端混合，這比較適合 n8n or AI Agent 來做</li>
  <li>複雜計算或是大量資料處理，每個執行階段最多 6 分鐘會跑不完</li>
  <li>大型網站爬蟲、搶票程式，這也不用想了，Google Apps Script 很容易封鎖或是被 Cloudflare 反爬蟲阻擋</li>
</ul>

<h4 id="適合的場景">適合的場景</h4>

<p>個人或是團隊內工作流程串接，以我個人來說我有很多腳本在幫我管理日常例行公事，例如每日通知 <a href="https://zhgchg.li/" target="_blank">zhgchg.li</a> 網站流量概況、GitHub Repo Issues 甚至是更新持股 Google Sheet 上的現價；團隊來說，App 發版流程可以結合 Google Calendar，檢查行事曆事件然後觸發(呼叫 GitHub Actions)對應的 CI/CD 流程，然後發送訊息到團隊 Slack Channel、轉發 App 送審失敗信件到 Slack。</p>

<blockquote>
  <p><em>以上是以人的視角重新回顧一次這幾年開發 Google Apps Script 的心得感受， <strong>再來是最近開始使喚 AI Agent 從 0 直接幫我開發 GAS 的實作感想與實際案例 —個人桌面 Dashboard Deck</strong> 。</em></p>
</blockquote>

<h4 id="讓-ai-agent-直接幫你搞定-google-apps-script-串接與開發">讓 AI Agent 直接幫你搞定 Google Apps Script 串接與開發</h4>

<p>最近開始嘗試從 0 就使用 AI 直接開發 Google Apps Script，結合上方的基礎知識( <strong>即使沒有，AI 掌握度也很高</strong> )； <strong>完成度與準確性幾乎是 100%，</strong> 可以說「從零開始 AI 寫程式」的時代也過去了。</p>
<h3 id="實戰--ai-變廢為寶--用-claude-design--claude-code-打造屬於你的個人桌面-dashboard">實戰 — AI 變廢為寶 — 用 Claude Design &amp; Claude Code 打造屬於你的個人桌面 Dashboard</h3>
<h4 id="問題">問題</h4>

<p>一直有一台汰換下來的 iPhone 8 Plus (iOS 16.7) 放在抽屜裡毫無用處，想說可以把它當成桌面小螢幕 Dashboard 呈現我想看到的即時訊息。</p>
<h4 id="基礎建設">基礎建設</h4>

<p>要讓 AI Agent 可以幫你開發 Google Apps Script，我們需要完成一些基本的基礎建設。</p>

<blockquote>
  <p><em>就算不是要給 AI 開發，也強烈建議開發中大型腳本時務必要使用，Web Editor 有時候會當機或是不小心多開 Tab 舊的覆蓋掉新的 Code，用 Clasp 可以更方便、安全的撰寫管理程式源碼。</em></p>
</blockquote>

<blockquote>
  <p><strong><em>其他使用案例：</em></strong> <em>Google Apps Script Project 同時上傳到 Git Repo 做版控 (排除 <code class="language-plaintext highlighter-rouge">.clasprc.json</code> 和 <code class="language-plaintext highlighter-rouge">.clasp.json</code> )、加上 CI 自動跑 Jest JS 單元測試、CD 用 Clasp Token 拉取最新程式碼、可以用 .claspignore 排除沒有要上傳到 Google Apps Script Project 的檔案。</em></p>
</blockquote>

<p>1.安裝 <a href="https://codelabs.developers.google.com/codelabs/clasp?hl=zh-tw#1" target="_blank"><strong>clasp Google Apps Script CLI</strong> ( 官方 本地開發套件)</a></p>
<ul>
  <li>Node.js &gt;= 20.0.0 ( <a href="https://docs.npmjs.com/downloading-and-installing-node-js-and-npm" target="_blank">如果沒有安裝 nodejs 環境請先安裝</a> )</li>
  <li><code class="language-plaintext highlighter-rouge">npm i @google/clasp -g</code></li>
</ul>

<p>2. 執行 <code class="language-plaintext highlighter-rouge">clasp login</code> 登入、產生授權 Token ( <a href="https://script.google.com/home" target="_blank">選擇 Google Apps Script 對應的帳號</a> )</p>

<p>3. 將 clasprc (Clasp Token) 儲存到 Keychain 讓 AI Agent 能安全的調用</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>security add-generic-password <span class="se">\</span>
  <span class="nt">-U</span> <span class="se">\</span>
  <span class="nt">-s</span> <span class="s2">"com.google.clasp"</span> <span class="se">\</span>
  <span class="nt">-a</span> <span class="s2">"</span><span class="nv">$USER</span><span class="s2">"</span> <span class="se">\</span>
  <span class="nt">-w</span> <span class="s2">"</span><span class="si">$(</span><span class="nb">cat</span> ~/.clasprc.json<span class="si">)</span><span class="s2">"</span>
</code></pre></div></div>

<p>如果你想要複製出 Clasp Token 放到 CI/CD Secret 上執行任務可以用 <code class="language-plaintext highlighter-rouge">cat ~/.clasprc.json | base64 | pbcopy</code> (記得加上 base64 encode)，使用時再 <code class="language-plaintext highlighter-rouge">echo "$CLASPRC" | base64 — decode &gt; ~/.clasprc.json &amp;&amp; chmod 600 ~/.clasprc.json</code></p>

<p>4. 建立專案目錄</p>

<p><img src="/assets/35cc65327d28/1*W5Y_173DcbwNeNZ8aeSGsA.webp" alt="" loading="lazy" decoding="async" width="219" height="36" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMTkiIGhlaWdodD0iMzYiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>必須建立一個專案目錄然後進入</p>

<p>5. 在目錄內建立 Google Apps Script Project (透過 clasp)</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>clasp create
</code></pre></div></div>

<blockquote>
  <p><em>會直接用目錄名稱當專案名稱。</em></p>
</blockquote>

<p><img src="/assets/35cc65327d28/1*4OjgluUubwvz0cOrs3DZ9g.webp" alt="" loading="lazy" decoding="async" width="888" height="69" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4ODgiIGhlaWdodD0iNjkiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>目前專案是空的，只有 <code class="language-plaintext highlighter-rouge">appsscript.json</code> 一個設定檔。</p>
<h4 id="我的設計">我的設計</h4>

<p><a href="https://github.com/zhgchgli0718/GASDashboardExample" target="_blank"><img src="https://opengraph.githubassets.com/7f1fca54b1504ffaae1102433fb1baff9a4165f507554bb5b745851eefd4eb27/zhgchgli0718/GASDashboardExample" alt="" /></a></p>

<blockquote>
  <p><em>如果你想節省 Token，可以直接 git clone 我的 GAS Web App 復用我的設計跟已開發好的 GAS 程式碼。</em></p>
</blockquote>

<h4 id="使用-claude-design-or-design-agent-skill-設計-dashboard">使用 Claude Design or Design Agent Skill 設計 Dashboard</h4>

<p><strong>方式 1 — Claude Design</strong> 
上一篇文章「 <a href="https://zhgchg.li/posts/zrealm-dev/jekyll-blog-%E8%87%AA%E8%A8%82%E4%B8%BB%E9%A1%8C%E8%A8%AD%E8%A8%88-%E7%94%A8-claude-design-claude-code-%E5%BF%AB%E9%80%9F%E6%89%93%E9%80%A0%E5%B0%88%E5%B1%AC%E9%A2%A8%E6%A0%BC-6bf79c5b4dab/#%E5%AF%A6%E7%8F%BE%E6%AD%A5%E9%A9%9F" target="_blank">Jekyll Blog 自訂主題設計｜用 Claude Design + Claude Code 快速打造專屬風格</a> 」中有介紹過，照相同的步驟調整到滿意的設計然後匯出 Project 解壓縮放到專案的 <code class="language-plaintext highlighter-rouge">./Design</code> 目錄下。</p>

<p><strong>方式 2 — Design Agent Skill</strong> 
使用第三方 <a href="https://github.com/alchaincyf/huashu-skills" target="_blank">設計 Skill</a> 完成設計，我的 Prompt 大致如下:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/huashu-design
設計一個可以在 iPhone 8 Plus 上橫放的網頁 Dashboard，把設計結果放到 ./Design 目錄下
- 之後會部署在 Google Apps Script Web App
- 上方有固定顯示日期星期還有翻頁時鐘
- 下方 tab 會定時切換，會顯示定時進度條
- 下方 tab 有:
-- 天氣狀況
-- 行事曆：當前行程 or 當日即將到來的行程、顯示剩餘事件
-- 倒數/計數日
</code></pre></div></div>

<blockquote>
  <p><em>我使用 Model: Opus 4.7 (1M context) / Effort: Medium</em></p>
</blockquote>

<div class="language-markdown highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
⏺ 我先掌握了脈絡：iPhone 8 Plus 橫放 = 736×414 CSS px，內容資料結構（天氣 / 行事曆 / 倒數）已在 CLAUDE.md 定義清楚。
  樣式你還沒指定，我按「設計方向顧問」給你 3 個明顯不同流派 的方向，請選一個（或混搭，例如 "A 的字型 + C 的配色"），我再做完整 hi-fi。
<span class="p">  ---</span>
  🅐 方向一 · Massimo Vignelli 式瑞士網格（信息建筑派）
  ▎ Vignelli 替紐約地鐵設計的網格美學——Helvetica + 嚴格欄位 + 紅黑白三色。資訊本身就是裝飾，不靠插圖、icon、漸變。
<span class="p">  -</span> 字型：Neue Haas Grotesk / Inter Tight（display）+ IBM Plex Mono（時鐘/數字）
<span class="p">  -</span> 配色：純白底 #FAFAFA · 純黑墨 #0A0A0A · 單一朱紅 accent #E63946（用在進度條與當前 tab）
<span class="p">  -</span> 版面：12 欄 baseline grid，所有元素對齊；翻頁時鐘是黑底白字大字 + 細水平分隔線；tab 是無圓角的色塊
<span class="p">  -</span> 氣質關鍵字：理性 / 克制 / 印刷品 / 資訊密度 / 永恆
<span class="p">  -</span> 適合：你想要不會過時、像一張高品質報紙頭版的儀表板
<span class="p">  ---</span>
  🅑 方向二 · Dieter Rams × Braun 工業儀表（極簡產業派）
  ▎ Rams 為 Braun 設計的 ET66 計算機、Audio 系統的美學——淺米灰底 + 圓角硬體面板 + 單一橙色 accent +
  ▎ 物理按鍵質感。每個區塊像一個獨立的儀器面板。
<span class="p">  -</span> 字型：Söhne / Inter（標籤）+ JetBrains Mono（時鐘走 LCD 七段碼風）
<span class="p">  -</span> 配色：暖米灰 #E5E1DA · 深炭 #2B2B2B · Braun 橙 #E85D2C accent · 翻頁時鐘走 LCD 黃綠 #A8B547 仿舊液晶
<span class="p">  -</span> 版面：每個 card 有微弱的內陰影與 1px hairline 邊框，像實體儀表的凹陷感；tab 切換像按鍵下沉
<span class="p">  -</span> 氣質關鍵字：產業設計 / 觸感 / 1970s Braun / 沉穩 / 工程師審美
<span class="p">  -</span> 適合：你喜歡硬體感、想要長時間注視也舒服
<span class="p">  ---</span>
  🅒 方向三 · Teenage Engineering OP-1 / 終端機（運動詩學派）
  ▎ TE 的 OP-1 合成器面板與老 OLED 終端機美學——深炭灰底 + OLED 青綠光暈 + mono 字 + 細網格背景。翻頁時鐘做成大型 OLED
  ▎ 翻牌，進度條像音序器步進燈。
<span class="p">  -</span> 字型：JetBrains Mono / IBM Plex Mono（全場）+ Berkeley Mono（display 大字）
<span class="p">  -</span> 配色：底 #0E0F0D · 主文字 #E8E5DA 米白 · OLED 青 #9DEF8F accent · 警示橙 #FFB454
<span class="p">  -</span> 版面：背景有極淡 8px dot grid；翻頁時鐘大字佔 1/3 寬，帶微弱掃描線；tab 進度條走 LED 步進；天氣用 ASCII 風小圖
<span class="p">  -</span> 氣質關鍵字：OLED / cyberdeck / 模擬合成器 / hacker chic / 駭客文化
<span class="p">  -</span> 適合：你想要它在書桌上像一台「設備」而不是一張「網頁」
<span class="p">  ---</span>
  請告訴我選哪個（或哪幾個元素混搭），我接著做完整 hi-fi 進 ./Design/。 也可以說「都不對，再來幾個」我換流派重推。
</code></pre></div></div>

<p>可以選擇喜歡的風格或是跟 AI 說自己想要的風格，例如 <code class="language-plaintext highlighter-rouge">紙感風格，米白宣紙 + 朱印章美學</code> 。</p>

<p>等待 AI 完成設計… <strong>AI 可能會在完成後用 Playwright 截圖驗證，如果想省 Token、自己驗證結果可以加在 Prompt 跟他說。</strong></p>

<p><strong>在目錄裡直接點擊 .html 檔案查看設計結果：</strong></p>

<p><img src="/assets/35cc65327d28/1*dTXDvk5dttT_RrCTNaSR5w.webp" alt="" loading="lazy" decoding="async" width="1200" height="681" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjY4MSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<blockquote>
  <p><em>調整到滿意後再進下一步開發。</em></p>
</blockquote>

<p><strong>建立 <a href="https://docs.google.com/spreadsheets/d/1R98OcfLMB3Dx9MSUsH849ZxLv0Hveh8Py_YxtJ1JljE/edit?usp=sharing" target="_blank">倒數日資料來源 Google Sheet</a> ：</strong></p>

<p><img src="/assets/35cc65327d28/1*Rb4yLjOhg9XwgICKCM7aww.webp" alt="" loading="lazy" decoding="async" width="1056" height="465" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDU2IiBoZWlnaHQ9IjQ2NSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>最後順手建立一個倒數日資料來源 Google Sheet，以後要加資料只要增加到這個 Sheet。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>title,date,repeat
生日,1994-07-18,yes
日本行,2026-12-12,
</code></pre></div></div>

<blockquote>
  <p><em>repeat=yes: 每年循環的事件</em></p>
</blockquote>

<blockquote>
  <p><em><strong>記下 Sheet Id: 上圖網址紅底部分 / Gid: 0(第一張 Sheet)。</strong></em></p>
</blockquote>

<p><strong>註冊交通部中央氣象署 API Key:</strong></p>
<ul>
  <li>註冊登入: <a href="https://opendata.cwa.gov.tw/userLogin" target="_blank">https://opendata.cwa.gov.tw/userLogin</a></li>
  <li>取得授權碼</li>
</ul>

<h4 id="使用-claude-code-開發實現-dashboard">使用 Claude Code 開發實現 Dashboard</h4>

<p>先 Init Claude Code Memory Markdown:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/init
⏺ 建立了 CLAUDE.md，內容聚焦本 repo 實際狀態：
  - 專案階段：早期 GAS 專案，目前只有 appsscript.json / .clasp.json / Design/Paper Dashboard.html，還沒任何 .gs 程式碼。
  - 檔案用途：manifest 設定（V8、America/New_York、Stackdriver）、clasp 綁定的 scriptId、以及 React+CDN 的純視覺原型（不含
  google.script.run）。
  - 常用指令：以 clasp 為主（push / pull / open / logs / deploy），目前沒有 lint 或 test runner。
  - push 前要注意：skipSubdirectories: false 會把 Design/Paper Dashboard.html 一起上傳；manifest 還沒宣告 oauthScopes；時區設定的位置。
  - 工作範圍：明確要求只用本 repo 內容，不參考其他專案（呼應你的 memory 規則）
</code></pre></div></div>

<p>執行 Plan 規劃開發工作:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/plan
實現 ./Design 的 Dashboard 網頁功能與設計，部署到 Google Apps Script Web App。
- 天氣使用交通部中央氣象署 API https://opendata.cwa.gov.tw/api/v1/rest/datastore/F-C0032-00
- 顯示 臺北市 天氣，有什麼欄位就顯示什麼
- 在開發階段你可以自己產一個 Token 然後部署 Web App 自主觸發必要方法與測試驗證，開發完畢務必移除這個開口
- Web App 部署成所有人都可存取，但是透過自訂的 Token 保護，初次使用使用者可輸入 Token 或是從網址帶入
- 行事曆默認讀取：primary
- 倒數日從我給你的 Google Sheet 裡讀取，title,date,repeat，如果 repeat 是 yes 代表是年循環倒數，多顯示已經過了 x 年 y 月 (無條件捨去)
- API Key, Google Sheet ID/GID... 統一都放在 PropertiesService 設定
- 直接幫我使用 clasp push &amp; deploy，clasprc login token 在 keychain security add-generic-password \ -U \ -s "com.google.clasp" \ -a "$USER"
- 如果需要我手動處理的地方請用繁體中文告訴我每個步驟
- oauthScopes": [ "https://www.googleapis.com/auth/script.external_request", "https://www.googleapis.com/auth/calendar", "https://www.googleapis.com/auth/spreadsheets", "https://www.googleapis.com/auth/script.scriptapp" ]
- 特別確認 google.script.run 串接取用格式是否確
- The live GAS Web App HTML is rendered inside an iframe wrapper.
- ./Design 等等不用上傳到 Google Apps Script 的檔案加入到 .claspignore
</code></pre></div></div>
<ul>
  <li>Web App 有四種存取權限：所有人、只有自己、只有已登入的人、已登入&amp;組織內的帳號</li>
  <li>上面範例的設計是簡單做：所有人都可以存取但需要輸入正確的亂數 Token 才能看到資料、天氣可以改成自己所在的地區，也能多個地區。</li>
  <li>行事曆默認只處理主行事曆，如果你有其他行事曆可以從行事曆 設定與共用 -&gt; 整合日曆 -&gt; 日曆 ID 取得</li>
  <li><strong>這邊加了一段懶人開發，直接請 AI 部署測試用 Web App 入口讓他邊開發邊測試邊修正，都好了再叫我</strong></li>
</ul>

<blockquote>
  <p><em>Plan 完如果有問題再回答 AI，沒有就可以開始實作了！</em></p>
</blockquote>

<h4 id="泡咖啡等-ai-開發">泡咖啡等 AI 開發…</h4>

<p><img src="/assets/35cc65327d28/1*TEYsNQzxxveclbMvaeAwAw.webp" alt="" loading="lazy" decoding="async" width="1246" height="1318" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjQ2IiBoZWlnaHQ9IjEzMTgiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><strong>AI Agent Question:</strong></p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="n">Action</span> <span class="n">needed</span> <span class="n">from</span> <span class="nf">you</span> <span class="p">(</span><span class="n">one</span><span class="o">-</span><span class="n">time</span><span class="p">):</span>

  <span class="mf">1.</span> <span class="n">Open</span> <span class="n">the</span> <span class="n">editor</span><span class="p">:</span> <span class="n">https</span><span class="p">:</span><span class="c1">//script.google.com/d/1KEWgIql-gqTNI_Tv_w2-5t7VE_R0eBtjAtd3iqLqabYLOnzJZ8Ga8N4s/edit</span>
  <span class="mf">2.</span> <span class="n">Top</span><span class="o">-</span><span class="n">right</span> <span class="s">"部署 / Deploy"</span> <span class="err">→</span> <span class="s">"管理部署作業 / Manage deployments"</span>
  <span class="mf">3.</span> <span class="n">Delete</span> <span class="n">both</span> <span class="n">existing</span> <span class="nf">deployments</span> <span class="p">(</span><span class="n">the</span> <span class="n">bare</span> <span class="o">@</span><span class="n">HEAD</span> <span class="n">and</span> <span class="n">dev</span> <span class="n">v1</span> <span class="err">—</span> <span class="n">the</span> <span class="n">pencil</span><span class="o">-</span><span class="n">icon</span> <span class="n">edit</span> <span class="n">doesn</span><span class="nv">'t</span> <span class="k">let</span> <span class="n">you</span> <span class="n">change</span> <span class="n">access</span> <span class="k">type</span><span class="p">)</span>
  <span class="mf">4.</span> <span class="n">Then</span> <span class="s">"新增部署 / New deployment"</span> <span class="err">→</span> <span class="n">類型</span><span class="p">:</span> <span class="n">網路應用程式</span> <span class="err">→</span> <span class="n">執行身分</span><span class="p">:</span> <span class="n">我</span><span class="err">（</span><span class="n">your</span> <span class="n">account</span><span class="err">）→</span> <span class="n">誰可以存取</span><span class="p">:</span> <span class="n">任何人</span> <span class="o">/</span> <span class="n">Anyone</span> <span class="err">→</span> <span class="n">部署</span>
  <span class="mf">5.</span> <span class="n">Grant</span> <span class="n">OAuth</span> <span class="n">on</span> <span class="n">the</span> <span class="n">consent</span> <span class="nf">screen</span> <span class="p">(</span><span class="n">calendar</span><span class="py">.readonly</span><span class="p">,</span> <span class="n">spreadsheets</span><span class="py">.readonly</span><span class="p">,</span> <span class="n">external_request</span><span class="p">)</span>
  <span class="mf">6.</span> <span class="nb">Copy</span> <span class="n">the</span> <span class="n">new</span> <span class="n">web</span> <span class="n">app</span> <span class="nf">URL</span> <span class="p">(</span><span class="n">looks</span> <span class="n">like</span> <span class="n">https</span><span class="p">:</span><span class="c1">//script.google.com/macros/s/AKfycb.../exec) and paste it here</span>

  <span class="n">Also</span> <span class="n">paste</span><span class="p">:</span>
  <span class="o">-</span> <span class="n">CWA</span> <span class="n">API</span> <span class="nf">Key</span> <span class="p">(</span><span class="n">交通部中央氣象署</span> <span class="n">Open</span> <span class="n">Data</span> <span class="n">Authorization</span><span class="p">)</span>
  <span class="o">-</span> <span class="n">倒數日</span> <span class="n">Sheet</span> <span class="nf">ID</span> <span class="p">(</span><span class="n">the</span> <span class="n">long</span> <span class="n">part</span> <span class="k">in</span> <span class="n">the</span> <span class="n">Sheet</span> <span class="n">URL</span> <span class="n">between</span> <span class="o">/</span><span class="n">d</span><span class="o">/</span> <span class="n">and</span> <span class="o">/</span><span class="n">edit</span><span class="p">)</span>
  <span class="o">-</span> <span class="n">Sheet</span> <span class="nf">GID</span> <span class="p">(</span><span class="n">the</span> <span class="n">number</span> <span class="n">after</span> <span class="n">gid</span><span class="o">=</span> <span class="k">in</span> <span class="n">the</span> <span class="n">URL</span> <span class="err">—</span> <span class="n">defaults</span> <span class="n">to</span> <span class="mi">0</span> <span class="k">for</span> <span class="n">the</span> <span class="n">first</span> <span class="n">tab</span><span class="p">)</span>

  <span class="n">Once</span> <span class="n">I</span> <span class="n">have</span> <span class="n">the</span> <span class="n">URL</span> <span class="o">+</span> <span class="n">secrets</span><span class="p">,</span> <span class="n">I</span><span class="nv">'ll</span> <span class="n">seed</span> <span class="n">Properties</span> <span class="n">via</span> <span class="n">the</span> <span class="n">dev</span> <span class="n">endpoint</span><span class="p">,</span> <span class="n">mint</span> <span class="n">the</span> <span class="n">access</span> <span class="n">token</span><span class="p">,</span> <span class="n">verify</span> <span class="n">each</span> <span class="n">handler</span><span class="p">,</span> <span class="n">and</span> <span class="n">wire</span> <span class="n">you</span> <span class="k">in</span><span class="err">.</span>
</code></pre></div></div>

<p>(1.) 第一次執行還是需要我們授權，AI Agent 之後才能自己跑，前往 Script Editor 網址：</p>

<p><img src="/assets/35cc65327d28/1*qif-nDDBfttn-y6kR2I2VA.webp" alt="" loading="lazy" decoding="async" width="863" height="404" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4NjMiIGhlaWdodD0iNDA0Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>隨便選擇一個有函式的檔案，選擇一個函式點偵錯：</p>

<p><img src="/assets/35cc65327d28/1*BgTkreP26mNx7nu9gkhedA.webp" alt="" loading="lazy" decoding="async" width="1412" height="769" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDEyIiBoZWlnaHQ9Ijc2OSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<ul>
  <li>只有第一次執行或是有新增權限時會需要授權</li>
</ul>

<blockquote>
  <p><em>如果 AI 一直說有權限問題，但是實際執行又沒跳授權視窗可以先去專案設定勾選「在編輯器中顯示「appsscript.json」資訊清單檔案」然後回到編輯器編輯 <code class="language-plaintext highlighter-rouge">appsscript.json</code> 改變一下 oauthScopes 的內容執行一次再改回來執行應該就會跳出授權流程了。</em></p>
</blockquote>

<p><strong>只有 1. 需要自己處理其他可以叫 AI 自己做:</strong></p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">1. OK，你直接幫我部署</span>
  <span class="s">CWA API Key</span><span class="err">:</span> <span class="s">CWA-XXXX-XXXX-XXXX-XXXX-XXXX</span>
  <span class="s">倒數日 Sheet ID</span><span class="na">: XXXX / GID</span><span class="pi">:</span> <span class="m">0</span>
</code></pre></div></div>

<p><img src="/assets/35cc65327d28/1*E1UAPVqKXjYtmAsJRRABdw.webp" alt="" loading="lazy" decoding="async" width="1266" height="577" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjY2IiBoZWlnaHQ9IjU3NyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><strong>協助 AI 設定 Google Apps Script..</strong></p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">⏺</span> <span class="n">Scope</span> <span class="ow">is</span> <span class="n">updated</span> <span class="ow">and</span> <span class="n">pushed</span> <span class="n">but</span> <span class="n">the</span> <span class="n">deployer</span><span class="sh">'</span><span class="s">s OAuth grant needs to be re-authorized for the new scope. The web app runs under your account — newly added
  scopes require you to re-grant.

  Please do this once:

  1. Open the editor: https://script.google.com/d/1KEWgIql-gqTNI_Tv_w2-5t7VE_R0eBtjAtd3iqLqabYLOnzJZ8Ga8N4s/edit
  2. From the function dropdown at the top, pick setupConfig (or any function)
  3. Click 執行 / Run — Google will pop a </span><span class="sh">"</span><span class="s">需要授權 / Authorization required</span><span class="sh">"</span><span class="s"> dialog
  4. Allow → grants the upgraded auth/spreadsheets scope
  5. Tell me when done and I</span><span class="sh">'</span><span class="n">ll</span> <span class="n">re</span><span class="o">-</span><span class="n">test</span> <span class="n">the</span> <span class="n">countdown</span> <span class="n">handler</span>
</code></pre></div></div>

<p>雖然我們都跟 AI 說好了但他還是有可能卡在一些地方需要人工幫忙，例如以上訊息； <strong>我們也很簡單就是照他步驟點擊執行一次函式再跟他說 OK 就好，就當幫個忙。(如果沒出現他說的方法就重整一下網頁)</strong></p>

<p><img src="/assets/35cc65327d28/1*oLIgKwY7YYbDZszAEMiSKQ.webp" alt="" loading="lazy" decoding="async" width="1200" height="896" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg5NiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<h4 id="ai-開發完成驗收">AI 開發完成，驗收！</h4>

<p><img src="/assets/35cc65327d28/1*_lM4Az2SosWIYlp26KG8zQ.webp" alt="" loading="lazy" decoding="async" width="1200" height="257" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjI1NyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>如果發現問題，可以再請 AI 修正。</p>
<h3 id="用-ai-打造屬於你的個人桌面-dashboard--成果">用 AI 打造屬於你的個人桌面 Dashboard — 成果</h3>
<h4 id="桌面版">桌面版</h4>

<p><img src="/assets/35cc65327d28/1*a70ymWexht6E2RLr7v0PWQ.webp" alt="" loading="lazy" decoding="async" width="1200" height="716" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjcxNiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<h4 id="iphone-手機版">iPhone 手機版</h4>

<p><img src="/assets/35cc65327d28/1*o6rsj5YDo5x47eo4JpvbkQ.webp" alt="" loading="lazy" decoding="async" width="1200" height="675" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjY3NSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<blockquote>
  <p><em>GAS Web App 無法做到滿版 PWA 隱藏網址列，如果想做到 100% 體驗請參考進階內容。</em></p>
</blockquote>

<h4 id="最終程式碼">最終程式碼</h4>

<p><a href="https://github.com/zhgchgli0718/GASDashboardExample" target="_blank"><img src="https://opengraph.githubassets.com/7f1fca54b1504ffaae1102433fb1baff9a4165f507554bb5b745851eefd4eb27/zhgchgli0718/GASDashboardExample" alt="" /></a></p>

<h4 id="後續任務">後續任務</h4>
<ul>
  <li>[required] 跟 AI 說：開發完畢請移除所有開發階段的介面／端點</li>
  <li>[optional] Google Apps Script Project -&gt; 專案設定 -&gt; 指令碼屬性 -&gt; <code class="language-plaintext highlighter-rouge">ACCESS_TOKEN</code> -&gt; 重新產生一個亂數字串，網址上的 <code class="language-plaintext highlighter-rouge">?token=</code> 也換成這個字串
macOS 可以用這個指令產生亂數字串: <code class="language-plaintext highlighter-rouge">openssl rand -hex 32</code></li>
  <li>[optional] <a href="https://opendata.cwa.gov.tw/userLogin" target="_blank">重新產生交通部中央氣象署API Key 授權碼</a> 並填入 Google Apps Script Project -&gt; 專案設定 -&gt; 指令碼屬性 -&gt; <code class="language-plaintext highlighter-rouge">CWA_API_KEY</code></li>
</ul>

<blockquote>
  <p><strong><em>⚠️根據 Claude Code 警告：</em></strong> <em>所有暴露在對話內容的 Token 都有機會洩漏，需當成已洩漏，但是為了驗證開發方便所以先直接輸入，驗證完沒問題後需要重新產生填入。</em></p>
</blockquote>

<blockquote>
  <p><em><strong>如果你熟悉 GAS 也可以在一開始就自己先設好 指令碼屬性 然後跟 AI 說直接使用。</strong></em></p>
</blockquote>

<h4 id="延伸">延伸</h4>

<p>如果這篇文章有激起你的創意，不妨想想還能串接顯示什麼，例如我多串接了 Yahoo Finance 取得我關注的股票即時股價、串接 AnalyticsData / AdSense 取得網站的流量與廣告狀況。</p>
<h4 id="進階">進階</h4>

<p>因為 GAS Web App 無法達成真 RWD、滿版 PWA (隱藏網址列)，所以我自己是把 GAS Web App 當 API 服務只回傳 JSON 資料，然後另外用 GitHub Pages 實現前端功能，部署成正式網頁；這樣就能達到完整使用的體驗。</p>

<p><img src="/assets/35cc65327d28/1*Cx-dFPrSRjj8g2tL5Z_kJw.webp" alt="" loading="lazy" decoding="async" width="1160" height="695" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMTYwIiBoZWlnaHQ9IjY5NSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/35cc65327d28/1*vtT3UhsGwmx15v4vNYS4vw.webp" alt="iOS 滿版 PWA 網頁設定 -&gt; Safari 打開網址 -&gt; 分享 -&gt; 加入主畫面" loading="lazy" decoding="async" width="1200" height="675" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjY3NSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>iOS 滿版 PWA 網頁設定 -&gt; Safari 打開網址 -&gt; 分享 -&gt; 加入主畫面</p>

<p><strong>這條路會比較複雜一點，可以在 /plan 階段跟 AI 說:</strong></p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>- GAS Web App 只做後端服務，輸出 JSON 給前端使用
- 前端使用 GitHub Pages 進行設計呈現和部署，幫我完成所有程式開發和部署任務開發
- 前端與後端之間用自訂亂數字串 Token 保護
</code></pre></div></div>
<h3 id="總結">總結</h3>

<p>以上就是這次使用 AI Agent 直接進行 Google Apps Script 的開發體驗介紹，最近也有在用 AI 重整之前開發過的中大型 GAS 專案，效果都不錯， <strong>還能請他在本地端加上函式單元測試(用 Jest、MOCK Google APIs) 提升穩定性</strong> (跑在 CI/CD 端、結合 clasp pull &amp; backup GAS Project)；AI 對 GAS 的掌握度與產出正確性都接近 100%。</p>
<h4 id="如果是-2021-年">如果是 2021 年…</h4>

<p>如果是那個沒有 AI 的年代，手搓從 0 包含設計、切版、程式邏輯開發到上線，我預估大約需要 30 小時左右；如今用 AI 3 小時內就能搞定。</p>

<blockquote>
  <p>基本上我以後應該不會再從頭開發 GAS，也不會從零開始 AI 寫程式，而是從零開始叫 AI 做好產品給我 (程式? IDGAF)。</p>
</blockquote>

<h4 id="案例2--個人持股清單管理">案例(2) — 個人持股清單管理</h4>

<p><img src="/assets/35cc65327d28/1*ouM-S6-JDJ8ddoUDVh8TVg.webp" alt="" loading="lazy" decoding="async" width="1147" height="1295" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMTQ3IiBoZWlnaHQ9IjEyOTUiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>最近嘗試的另一個 AI 直接搞定 Google Apps Script 的案例，請他把我的 Google Sheet 持股表格轉成可視化 Web App，結合 Yahoo Finance 取得最新股價做成表格顯示給我看，下方也可以快速地輸入買入賣出紀錄，會再串接寫入回 Google Sheet。</p>
<h4 id="案例-3--jekyll-blog-自訂主題設計用-claude-design--claude-code-快速打造專屬風格-️">案例 (3) — <a href="/posts/zrealm-dev/jekyll-blog-自訂主題設計-用-claude-design-claude-code-快速打造專屬風格-6bf79c5b4dab/">Jekyll Blog 自訂主題設計｜用 Claude Design + Claude Code 快速打造專屬風格</a> ⬅️</h4>

<blockquote>
  <p><em>花了一個週末的午後時光用 Claude Design 設計喜歡的 Blog 樣式與功能，再透過 Claude Code 實作並套用回 Jekyll Blog，把用了 5 年的 Chirpy Theme 介面完美替換成我心目中理想的個人 Blog 樣式。</em></p>
</blockquote>

<h4 id="延伸閱讀">延伸閱讀</h4>

<blockquote>
  <p><em>TL;DR 這些都是之前手搓的 GAS 服務、工具</em></p>
</blockquote>

<ul>
  <li><a href="https://medium.com/zrealm-robotic-process-automation/%E4%BD%BF%E7%94%A8-google-apps-script-%E5%AF%A6%E7%8F%BE-google-%E6%9C%8D%E5%8B%99-rpa-%E8%87%AA%E5%8B%95%E5%8C%96-f6713ba3fee3?source=collection_home---6------3-----------------------" target="_blank"><strong>使用 Google Apps Script 實現每日數據報表 RPA 自動化</strong></a></li>
  <li><a href="/posts/zrealm-robotic-process-automation/google-apps-script-快速串接-firebase-app-distribution-api-教學與進階設定-71400d408dc8/"><strong>Google Apps Script x Google APIs 快速串接整合方式</strong></a></li>
  <li><a href="/posts/zrealm-dev/ci-cd-打包工具平台-使用-google-apps-script-web-app-串接-github-actions-提升跨團隊效率-4273e57e7148/"><strong>使用 Google Apps Script Web App 串接 GitHub Actions 建置免費易用的打包工具平台</strong></a></li>
  <li><a href="/posts/zrealm-robotic-process-automation/ga4-自動數據通知機器人-3-步驟用-google-apps-script-串接-telegram-bot-1e85b8df2348/">簡單 3 步驟 — 打造免費 GA4 自動數據通知機器人</a></li>
  <li><a href="/posts/zrealm-robotic-process-automation/google-apps-script-教學-自動轉發-gmail-郵件到-slack-channel-客製化通知-d414bdbdb8c9/">運用 Google Apps Script 轉發 Gmail 信件到 Slack</a></li>
  <li><a href="https://medium.com/zrealm-robotic-process-automation/slack-%E6%89%93%E9%80%A0%E5%85%A8%E8%87%AA%E5%8B%95-wfh-%E5%93%A1%E5%B7%A5%E5%81%A5%E5%BA%B7%E7%8B%80%E6%B3%81%E5%9B%9E%E5%A0%B1%E7%B3%BB%E7%B5%B1-d61062833c1a?source=collection_home---6------9-----------------------" target="_blank">Slack 打造全自動 WFH 員工健康狀況回報系統</a></li>
  <li><a href="https://medium.com/zrealm-robotic-process-automation/crashlytics-big-query-%E6%89%93%E9%80%A0%E6%9B%B4%E5%8D%B3%E6%99%82%E4%BE%BF%E5%88%A9%E7%9A%84-crash-%E8%BF%BD%E8%B9%A4%E5%B7%A5%E5%85%B7-e77b80cc6f89?source=collection_home---6------7-----------------------" target="_blank">Crashlytics + Big Query 打造更即時便利的 Crash 追蹤工具</a></li>
  <li><a href="https://medium.com/zrealm-robotic-process-automation/crashlytics-google-analytics-%E8%87%AA%E5%8B%95%E6%9F%A5%E8%A9%A2-app-crash-free-users-rate-793cb8f89b72?source=collection_home---6------6-----------------------" target="_blank">Crashlytics + Google Analytics 自動查詢 App Crash-Free Users Rate</a></li>
  <li><a href="https://medium.com/zrealm-robotic-process-automation/%E4%BD%BF%E7%94%A8-google-apps-script-%E4%B8%89%E6%AD%A5%E9%A9%9F%E5%85%8D%E8%B2%BB%E5%BB%BA%E7%AB%8B-github-repo-star-notifier-382218e15697?source=collection_home---6------5-----------------------" target="_blank">使用 Google Apps Script 三步驟免費建立 GitHub Repo Star Notifier</a></li>
  <li><a href="https://medium.com/zrealm-robotic-process-automation/10-%E5%88%86%E9%90%98%E5%BF%AB%E9%80%9F%E7%A7%BB%E8%BD%89-line-notify-%E5%88%B0-telegram-bot-%E9%80%9A%E7%9F%A5-6922e90ba90c?source=collection_home---6------0-----------------------" target="_blank">10 分鐘快速移轉 Line Notify 到 Telegram Bot 通知</a></li>
</ul>]]></content>
  </entry><entry>
    <title type="html">Jekyll Blog 自訂主題設計｜用 Claude Design + Claude Code 快速打造專屬風格</title>
    <link href="https://zhgchg.li/posts/zrealm-dev/jekyll-blog-%E8%87%AA%E8%A8%82%E4%B8%BB%E9%A1%8C%E8%A8%AD%E8%A8%88-%E7%94%A8-claude-design-claude-code-%E5%BF%AB%E9%80%9F%E6%89%93%E9%80%A0%E5%B0%88%E5%B1%AC%E9%A2%A8%E6%A0%BC-6bf79c5b4dab/" rel="alternate" type="text/html" title="Jekyll Blog 自訂主題設計｜用 Claude Design + Claude Code 快速打造專屬風格" />
    <published>2026-04-27T23:00:32+08:00</published>
    <updated>2026-05-09T00:55:11+08:00</updated>
    <id>https://zhgchg.li/posts/zrealm-dev/6bf79c5b4dab</id><summary type="html">程式設計師用 Claude Design 與 Claude Code 於一個週末完成 Jekyll Blog 主題重設，解決找不到理想主題的痛點，成功打造專屬風格且支援 RWD 與 SEO，部署於免費可靠的 GitHub Pages，提升網站專業度與使用體驗。</summary><author>
      <name>ZhgChgLi</name>
    </author><category term="ZRealm Dev." /><category term="claude-code" /><category term="github-pages" /><category term="jekyll" /><category term="ai" /><category term="blog" /><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://zhgchg.li/assets/6bf79c5b4dab/1*cQbgChJysDv6MjvNic7Hlw.webp" /><content type="html" xml:base="https://zhgchg.li/posts/zrealm-dev/jekyll-blog-%E8%87%AA%E8%A8%82%E4%B8%BB%E9%A1%8C%E8%A8%AD%E8%A8%88-%E7%94%A8-claude-design-claude-code-%E5%BF%AB%E9%80%9F%E6%89%93%E9%80%A0%E5%B0%88%E5%B1%AC%E9%A2%A8%E6%A0%BC-6bf79c5b4dab/"><![CDATA[<h3 id="一個週末午後--claude-design--claude-code--打造屬於你自己風格的-blog">一個週末午後 + Claude Design + Claude Code = 打造屬於你自己風格的 Blog</h3>

<p>使用 Claude Design + Claude Code 打造你專屬的 Jekyll Theme Blog 主題</p>

<h4 id="httpszhgchgli-20"><a href="https://zhgchg.li/" target="_blank">https://zhgchg.li/</a> 2.0!</h4>

<p><img src="/assets/6bf79c5b4dab/1*cQbgChJysDv6MjvNic7Hlw.webp" alt="https://zhgchg.li/" loading="lazy" decoding="async" width="1200" height="1057" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjEwNTciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><a href="https://zhgchg.li/" target="_blank">https://zhgchg.li/</a></p>

<blockquote>
  <p><em>花了一個週末午後，用 Claude Design 設計喜歡的 Blog 樣式與功能，再透過 Claude Code 實作並套用回 Jekyll Blog，把用了 5 年的 Chirpy Theme 介面完美替換成我心目中理想的個人 Blog 樣式。</em></p>
</blockquote>

<h4 id="技術">技術</h4>
<ul>
  <li><strong>Blog 架構:</strong> Jekyll 靜態網站</li>
  <li><strong>文章原始檔:</strong> Markdown 檔案</li>
  <li><strong>伺服器/網頁寄存:</strong> GitHub Pages (免費可靠)</li>
  <li><strong>Engineering:</strong> Claude Code Max ($100 USD，5 小時額度 <strong>還沒用完就做好了</strong> )</li>
  <li><strong>Design:</strong> Claude Design (Blog 頁面不多，2 小時額度內就夠用)</li>
</ul>

<h4 id="花費">花費</h4>
<ul>
  <li>Claude Code Max $100 USD</li>
  <li>伺服器/網頁寄存: $0 USD</li>
  <li>Jekyll Theme: $0 USD</li>
  <li>時間: 一個週末的下午</li>
</ul>

<h3 id="實現步驟">實現步驟</h3>
<h4 id="1-在本地建立一個乾淨的-jekyll-blog">1. 在本地建立一個乾淨的 Jekyll Blog</h4>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>jekyll new blog
</code></pre></div></div>
<h4 id="2-搜集-theme-頁面案例並上傳到-claude-design">2. 搜集 Theme 頁面案例並上傳到 Claude Design</h4>
<h4 id="20260506-upadte-21-design-agent-skill-使用第三方-設計-skill-完成設計">[2026/05/06 Upadte] <a href="https://zhgchg.li/posts/zrealm-dev/%E5%88%A5%E5%86%8D%E5%BE%9E%E9%9B%B6%E9%96%8B%E5%A7%8B-ai-%E5%AF%AB%E7%A8%8B%E5%BC%8F-%E8%AE%93-ai-agent-%E7%9B%B4%E6%8E%A5%E5%B9%AB%E4%BD%A0%E6%90%9E%E5%AE%9A-google-apps-script-%E4%B8%B2%E6%8E%A5%E8%88%87%E9%96%8B%E7%99%BC-35cc65327d28/#%E4%BD%BF%E7%94%A8-claude-design-or-design-agent-skill-%E8%A8%AD%E8%A8%88-dashboard" target="_blank">2–1 Design Agent Skill 使用第三方 設計 Skill 完成設計</a></h4>

<p>細節可參考此篇文章「 <a href="https://zhgchg.li/posts/zrealm-dev/%E5%88%A5%E5%86%8D%E5%BE%9E%E9%9B%B6%E9%96%8B%E5%A7%8B-ai-%E5%AF%AB%E7%A8%8B%E5%BC%8F-%E8%AE%93-ai-agent-%E7%9B%B4%E6%8E%A5%E5%B9%AB%E4%BD%A0%E6%90%9E%E5%AE%9A-google-apps-script-%E4%B8%B2%E6%8E%A5%E8%88%87%E9%96%8B%E7%99%BC-35cc65327d28/#%E4%BD%BF%E7%94%A8-claude-design-or-design-agent-skill-%E8%A8%AD%E8%A8%88-dashboard" target="_blank">別再從零開始 AI 寫程式：讓 AI Agent 直接幫你搞定 Google Apps Script 串接與開發</a> 」</p>
<h4 id="22-claude-design">2–2 Claude Design</h4>

<p>我直接到目前的 Blog，在各頁面按右鍵「Save as」例如 Home、Post、Page、PostList、Archives、Tags…</p>

<p><img src="/assets/6bf79c5b4dab/1*EWBgWOobhy5kFeNPq9-uqQ.webp" alt="" loading="lazy" decoding="async" width="482" height="359" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0ODIiIGhlaWdodD0iMzU5Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><strong>建一個 Theme 目錄，把所有頁面跟附加檔案都放進去:</strong></p>

<p><img src="/assets/6bf79c5b4dab/1*eo3d-J4ehZpBIZfx4bV4Wg.webp" alt="" loading="lazy" decoding="async" width="253" height="231" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNTMiIGhlaWdodD0iMjMxIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><strong>到 <a href="https://claude.ai/design" target="_blank">Claude Design</a> 建立專案:</strong></p>

<p><img src="/assets/6bf79c5b4dab/1*AYCu31HSzfOHST8nberNWA.webp" alt="https://claude.ai/design" loading="lazy" decoding="async" width="377" height="441" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzNzciIGhlaWdodD0iNDQxIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><a href="https://claude.ai/design" target="_blank">https://claude.ai/design</a></p>

<p><strong>將 Theme 頁面案例上傳到 Claude Design，選擇「Attach codebase」:</strong></p>

<p><img src="/assets/6bf79c5b4dab/1*1A08G_oNaieGA_8t17xPow.webp" alt="" loading="lazy" decoding="async" width="323" height="340" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzMjMiIGhlaWdodD0iMzQwIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><strong>選擇剛剛整理好的 Theme 目錄:</strong></p>

<p><img src="/assets/6bf79c5b4dab/1*KbXyuIRuWnisFYbtnIUhzg.webp" alt="" loading="lazy" decoding="async" width="998" height="560" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5OTgiIGhlaWdodD0iNTYwIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><strong>選擇完成後，輸入 Prompt 請 Claude Design 幫你設計頁面:</strong></p>

<p><img src="/assets/6bf79c5b4dab/1*bvO5_7XHaQSRdjbIcj_QsA.webp" alt="" loading="lazy" decoding="async" width="391" height="153" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzOTEiIGhlaWdodD0iMTUzIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<blockquote>
  <p><em>重新設計我的 Blog，用繁體中文跟我交流。</em></p>
</blockquote>

<h4 id="3-使用-claude-design-重新設計">3. 使用 Claude Design 重新設計</h4>

<p>送出對話後，Claude Design 會問你關於你想要的設計問題:</p>

<p><img src="/assets/6bf79c5b4dab/1*EvWpwlNUXDcLI5MHUz9Jug.webp" alt="" loading="lazy" decoding="async" width="646" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2NDYiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<ul>
  <li>想看幾個視覺方向的變體？ AI 會產出多個方案供你選擇</li>
  <li>希望可即時調整哪些 Tweaks？右下角 Debug 小工具需要哪些切換功能</li>
  <li>其他就照喜好自己填寫囉。</li>
</ul>

<h4 id="4-claude-design-cooking">4. Claude Design Cooking…</h4>

<p><img src="/assets/6bf79c5b4dab/1*pBQcJF9Bim581CsPHGdlxQ.webp" alt="" loading="lazy" decoding="async" width="1109" height="1302" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMTA5IiBoZWlnaHQ9IjEzMDIiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>大約需要 3–5 分鐘，AI 會建立完整的 Design System（.css），並用 React.js／HTML 展示結果。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>已完成 Blog 的重新設計，放在 Blog Redesign.html。

設計系統

字體：Noto Serif TC（宋體）+ Inter + JetBrains Mono
色調：暖紙張米白 + 赤陶主色（oklch warm 系）
三條分類軸線：科技 / 旅行 / 分享，全部使用真實的中文範例內容
兩個方向，共 7 個畫板（雙擊可全螢幕預覽）

方向 A · 雜誌風（Magazine）

首頁 — 報頭刊名 + 主打報導 + 三欄次要文章 + 編號式條列 + Newsletter
文章頁 — 完整單篇含目錄、首字下沉、引言、程式碼區塊、留言、相關文章
分類頁 — 2×2 大圖式索引
標籤頁 — 視覺化條狀統計 + 標籤雲
歸檔頁 — 年份 + 月份的時光軸
關於頁 — 個人介紹 + Now 區塊 + 訂閱
方向 B · 現代刊物（Substack 風） 7. 首頁 — 大圖 Hero + 卡片式網格 + 側欄訂閱牆

Tweaks 面板（右下角開啟）可即時切換：

標題字體（Noto Serif / 思源宋體）
寬版面 / 標準版面
主色與紙張色（含 4 組預設配色：赤陶、深藍、墨綠、酒紅）
</code></pre></div></div>
<h4 id="5-claude-design-結果調整">5. Claude Design 結果調整</h4>

<p><img src="/assets/6bf79c5b4dab/1*fRHChM-U_a_dbtde2NcO3w.webp" alt="" loading="lazy" decoding="async" width="1200" height="769" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijc2OSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<ul>
  <li>可以點擊右下角的 Tweak，快速調整參數並查看結果</li>
  <li>可以點擊頁面右上角放大查看完整網頁；若有互動功能，也能直接操作 Mockup</li>
  <li>如果點擊無效、無法滾動、頁面顯示不完整或內容被裁切，都可以請 AI 調整</li>
</ul>

<p>可以繼續跟 AI 對話請他加上你要的功能、調整現有功能，例如我希望文章末可以加入 buy me a coffee donate 連結、留言， <strong>我就可以再跟他說:</strong></p>

<p><img src="/assets/6bf79c5b4dab/1*5IdYAz7JCerimasdQQl8lA.webp" alt="" loading="lazy" decoding="async" width="389" height="684" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzODkiIGhlaWdodD0iNjg0Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><strong>結果:</strong></p>

<p><img src="/assets/6bf79c5b4dab/1*UdAS4J2B494L3RYk_Dpgcg.webp" alt="" loading="lazy" decoding="async" width="846" height="1184" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4NDYiIGhlaWdodD0iMTE4NCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<blockquote>
  <p>持續跟 AI 對話把功能跟頁面設計成自己想要。</p>
</blockquote>

<h4 id="6-claude-design-完成">6. Claude Design 完成</h4>

<p>設計到滿意後，點右上角「Share」→「Download project as .zip」，將原始設計檔下載回來:</p>

<p><img src="/assets/6bf79c5b4dab/1*HD6TIUrAQXeduhkUoRdC7g.webp" alt="" loading="lazy" decoding="async" width="477" height="450" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0NzciIGhlaWdodD0iNDUwIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>下載並解壓縮後，把資料夾放到 Jekyll Blog 目錄中:</p>

<p><img src="/assets/6bf79c5b4dab/1*ZUZcFtumkmeo9UuTACQakw.webp" alt="" loading="lazy" decoding="async" width="326" height="476" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzMjYiIGhlaWdodD0iNDc2Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<h4 id="7-請-claude-code-實現-claude-design-設計稿">7. 請 Claude Code 實現 Claude Design 設計稿</h4>

<p><img src="/assets/6bf79c5b4dab/1*npQOf0QkzG6qDJKwT1-f9A.webp" alt="" loading="lazy" decoding="async" width="1078" height="725" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDc4IiBoZWlnaHQ9IjcyNSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>記得先在 Jekyll Blog 目錄下執行一次 <code class="language-plaintext highlighter-rouge">/init</code> ，讓 AI 知道這是 Jekyll Blog 架構。</p>

<p><img src="/assets/6bf79c5b4dab/1*sEtu11Zbw5ZkP5n69uyVmA.webp" alt="" loading="lazy" decoding="async" width="668" height="217" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2NjgiIGhlaWdodD0iMjE3Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<blockquote>
  <p><em>使用 ./MyBlogTheme 的主題設計套用到我的 blog</em></p>
</blockquote>

<h4 id="8-claude-code-baking">8. Claude Code baking…</h4>

<p><img src="/assets/6bf79c5b4dab/1*_iSl56RBPuHX9OqAjR4q-g.webp" alt="" loading="lazy" decoding="async" width="1192" height="1174" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMTkyIiBoZWlnaHQ9IjExNzQiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/6bf79c5b4dab/1*D8RjjpPikNWEqe2G55PkhA.webp" alt="" loading="lazy" decoding="async" width="1192" height="1174" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMTkyIiBoZWlnaHQ9IjExNzQiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<ul>
  <li>首次建置大約花費 20K Tokens (Opus 4.7 / effort: medium)</li>
</ul>

<h4 id="成果">成果：</h4>

<p>執行 <code class="language-plaintext highlighter-rouge">bundle exec jekyll serve</code> 打開 http://127.0.0.1:4000/ 查看結果:</p>

<p><img src="/assets/6bf79c5b4dab/1*nvsTfFy1S2siBrAlHcbguQ.webp" alt="" loading="lazy" decoding="async" width="1200" height="638" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjYzOCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<blockquote>
  <p>接著持續請 AI 優化與修正問題，但基本完成度已經很高了！</p>
</blockquote>

<h3 id="實現小-tips">實現小 Tips</h3>
<ul>
  <li>可以跟 AI 說： <code class="language-plaintext highlighter-rouge">你的 Blog 會部署到 GitHub Pages</code> 或其他平台，因為每個平台可能機能不一樣，例如 GitHub Pages 只允許部分的 <a href="https://github.com/github/pages-gem/blob/master/lib/github-pages/plugins.rb#L21-L42" target="_blank">Jekyll Plugins</a> ，不在允許清單中的套件，上線後也無法使用。</li>
  <li>可以跟 AI 說： <code class="language-plaintext highlighter-rouge">優先使用 Jekyll 原生、Jekyll Plugin、免費開源的專案實現功能。</code></li>
  <li>可以跟 AI 說： <code class="language-plaintext highlighter-rouge">網站設計務必要重視 SEO 結構與 RWD 操作體驗。</code></li>
  <li>可以跟 AI 說： <code class="language-plaintext highlighter-rouge">設計新的 Theme 替代掉現有的</code> 。</li>
  <li>部署到 GitHub Pages 的 CI/CD YAML 也可以請 AI 寫，步驟他也會教你。</li>
  <li><strong>如果想調整新的頁面設計，建議回到 Claude Design 修改設計稿，調整完再重新下載並放回目錄；不建議直接請 Claude Code 做視覺設計，它不太擅長美感。</strong></li>
</ul>

<h4 id="我用到的套件或功能"><strong>我用到的套件或功能</strong></h4>

<p><strong>Jekyll 套件:</strong></p>
<ul>
  <li>jekyll 4.3 + kramdown (GFM) + kramdown-parser-gfm</li>
  <li>rouge — code highlight</li>
  <li>jekyll-feed — Atom feed (/feed.xml)</li>
  <li>jekyll-sitemap — /sitemap.xml</li>
  <li>jekyll-paginate-v2 — 文章分頁功能</li>
  <li>jekyll-archives — 歸檔頁面</li>
  <li>jekyll-seo-tag — SEO Meta Tag / OG / Twitter Card meta</li>
  <li>jekyll-redirect-from — 短網址 redirect</li>
</ul>

<p><strong>CSS / 前端資源:</strong></p>
<ul>
  <li>GLightbox v3（CDN, MIT） — 圖片點擊放大 lightbox</li>
  <li>SCSS：assets/css/main.scss</li>
  <li><a href="https://fontawesome.com/" target="_blank">Font Awesome</a> icon svg</li>
</ul>

<p><strong>JavaScript 功能:</strong></p>
<ul>
  <li>抽屜選單（topbar menu / overlay / ESC 關閉）</li>
  <li>LQIP hydrator &amp; Lazy Load — 文章圖片 Placeholder &amp; 延遲載入</li>
  <li>Reading progress bar</li>
  <li>Client-side TOC</li>
  <li>Search：/search.json build-time + 純 JS substring filter（無 lunr）</li>
</ul>

<p><strong>第三方服務:</strong></p>
<ul>
  <li>giscus — 留言（GitHub Discussions）</li>
</ul>

<h3 id="延伸閱讀">延伸閱讀</h3>
<h4 id="️-別再從零開始-ai-寫程式讓-ai-agent-直接幫你搞定-google-apps-script-串接與開發">➡️ <a href="/posts/zrealm-dev/別再從零開始-ai-寫程式-讓-ai-agent-直接幫你搞定-google-apps-script-串接與開發-35cc65327d28/">別再從零開始 AI 寫程式：讓 AI Agent 直接幫你搞定 Google Apps Script 串接與開發</a></h4>

<blockquote>
  <p><em>用 Claude Code 重構現有的中大型 Google Apps Script 專案、補上測試，並開發全新的應用。</em></p>
</blockquote>

<blockquote>
  <p><em>個人感覺，在 GAS 串接與小工具這類開發場景中，AI Agent 的準確性與完整度已經非常高。以後不一定要從頭手刻，也不一定要從零開始 AI 寫程式，而是可以直接請 AI 從 0 到 1 產出可用成品。</em></p>
</blockquote>

<blockquote>
  <p><em>這個案例我從塵封已久的抽屜裡翻出舊 iPhone，希望讓 AI 變廢為寶：</em></p>
</blockquote>

<blockquote>
  <p><strong><em>用 Claude Design + Claude Code 打造屬於自己的個人桌面 Dashboard，並透過 Google Apps Script 完成串接、開發與部署。</em></strong></p>
</blockquote>

<h3 id="背景">背景</h3>

<p>2018 年起在 Medium 撰寫文章紀錄程式與生活，後來自己開發了一個小工具 <a href="https://github.com/ZhgChgLi/ZMediumToMarkdown" target="_blank">ZMediumToMarkdown</a> 可以把 Medium 文章下載轉換成 Markdown 檔案備份，並順帶用 Jekyll + Chirpy theme 建立了獨立的 Blog 網站，部署在免費的 GitHub Pages 之上。</p>

<p><a href="https://github.com/jekyll/jekyll" target="_blank"><img src="https://repository-images.githubusercontent.com/65252/f2b7c780-70b6-11e9-85d2-f4bda8708a2d" alt="" /></a></p>

<p>Jekyll 是一套很方便的靜態網站產生工具，可以把 Markdown 和模板轉換成 HTML，再將 HTML 檔案放到託管服務上，內容就能上線。</p>
<ul>
  <li>我也用 Jekyll 做了一個自己的 Linktree: <a href="https://link.zhgchg.li" target="_blank">https://link.zhgchg.li</a></li>
  <li><code class="language-plaintext highlighter-rouge">jekyll new blog</code> 就能快速建立一個預設的 Blog 專案</li>
</ul>

<p><strong>Jekyll 預設的 Blog 專案樣式非常陽春:</strong></p>

<p><img src="/assets/6bf79c5b4dab/1*SB1pZSaMsMvUG8AiUla62w.webp" alt="" loading="lazy" decoding="async" width="792" height="937" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3OTIiIGhlaWdodD0iOTM3Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>但是要找到滿意的 Jekyll Blog Theme 不太容易，有的是首頁漂亮、有的是文章內容頁體驗好、有的是列表頁不錯，就算是付費主題也很難找到完全滿意的；也有可能裝了都設定好了才發現少了什麼功能或是頁面，又只能捨棄再找，很痛苦。</p>

<p><a href="https://github.com/cotes2020/jekyll-theme-chirpy" target="_blank"><img src="https://repository-images.githubusercontent.com/165360641/257ebaa6-61dd-40a4-b075-d21b546026d6" alt="" /></a></p>

<p><strong>來來回回找了好幾輪，最終找到一個佈局不差、支援 RWD、Blog 功能完善的主題 — <a href="https://github.com/cotes2020/jekyll-theme-chirpy" target="_blank">Theme Chirpy</a> :</strong></p>

<p><img src="/assets/6bf79c5b4dab/1*Ayc3pF9qbahE5U2BnzixaQ.webp" alt="Live Demo" loading="lazy" decoding="async" width="1576" height="1143" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNTc2IiBoZWlnaHQ9IjExNDMiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><a href="https://chirpy.cotes.page/posts/text-and-typography/" target="_blank">Live Demo</a></p>

<p>一用就是 5 年，當中也有看得很膩想要換的時候；但每次鬼打牆找了一輪後，最後還是回來用 <a href="https://github.com/cotes2020/jekyll-theme-chirpy" target="_blank"><strong>Theme Chirpy</strong></a> ，如同前述找不到比它更好看又實用的主題了。</p>

<blockquote>
  <p>直到 Claude Design + Claude Code 出現，我才一鼓作氣請 AI 幫我設計想要的 Blog 風格，並實作功能頁。</p>
</blockquote>]]></content>
  </entry><entry>
    <title type="html">北海道札幌6日遊攻略｜定山溪溫泉泡湯＋富良野美瑛點燈一日遊全記錄</title>
    <link href="https://zhgchg.li/posts/z-%E5%BA%A6%E6%97%85%E8%A1%8C%E9%81%8A%E8%A8%98/%E5%8C%97%E6%B5%B7%E9%81%93%E6%9C%AD%E5%B9%8C6%E6%97%A5%E9%81%8A%E6%94%BB%E7%95%A5-%E5%AE%9A%E5%B1%B1%E6%BA%AA%E6%BA%AB%E6%B3%89%E6%B3%A1%E6%B9%AF-%E5%AF%8C%E8%89%AF%E9%87%8E%E7%BE%8E%E7%91%9B%E9%BB%9E%E7%87%88%E4%B8%80%E6%97%A5%E9%81%8A%E5%85%A8%E8%A8%98%E9%8C%84-055527a739dd/" rel="alternate" type="text/html" title="北海道札幌6日遊攻略｜定山溪溫泉泡湯＋富良野美瑛點燈一日遊全記錄" />
    <published>2026-04-19T23:51:51+08:00</published>
    <updated>2026-04-20T23:33:43+08:00</updated>
    <id>https://zhgchg.li/posts/z-%E5%BA%A6%E6%97%85%E8%A1%8C%E9%81%8A%E8%A8%98/055527a739dd</id><summary type="html">規劃北海道札幌6日遊，詳細分享定山溪溫泉交通預約技巧、札幌市區美食與景點、小樽水族館體驗，及KKday富良野精靈台與美瑛青池點燈行程，助你輕鬆掌握交通、住宿與行程安排，打造無憂冬季北海道旅遊。</summary><author>
      <name>ZhgChgLi</name>
    </author><category term="Z 度旅行遊記" /><category term="japan" /><category term="生活" /><category term="travel" /><category term="travel-writing" /><category term="hokkaido" /><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://zhgchg.li/assets/055527a739dd/1*4N6zFGkzcM6QsIulPOwslQ.webp" /><content type="html" xml:base="https://zhgchg.li/posts/z-%E5%BA%A6%E6%97%85%E8%A1%8C%E9%81%8A%E8%A8%98/%E5%8C%97%E6%B5%B7%E9%81%93%E6%9C%AD%E5%B9%8C6%E6%97%A5%E9%81%8A%E6%94%BB%E7%95%A5-%E5%AE%9A%E5%B1%B1%E6%BA%AA%E6%BA%AB%E6%B3%89%E6%B3%A1%E6%B9%AF-%E5%AF%8C%E8%89%AF%E9%87%8E%E7%BE%8E%E7%91%9B%E9%BB%9E%E7%87%88%E4%B8%80%E6%97%A5%E9%81%8A%E5%85%A8%E8%A8%98%E9%8C%84-055527a739dd/"><![CDATA[<h3 id="遊記-2026-北海道札幌-6-日遊--札幌市區定山溪溫泉北海道點燈富良野精靈台--美瑛青池一日遊">[遊記] 2026 北海道札幌 6 日遊 — 札幌市區、定山溪溫泉、北海道點燈(富良野精靈台 &amp; 美瑛青池)一日遊</h3>

<p>北海道札幌市區散策、定山溪泡湯、富良野精靈台與美瑛青池點燈行程全記錄</p>

<p><img src="/assets/055527a739dd/1*4N6zFGkzcM6QsIulPOwslQ.webp" alt="" loading="lazy" decoding="async" width="1200" height="760" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijc2MCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>新工作做了快半年加上手上專案剛結束，也從沒去過下雪的日本，於是就在 3 月中來場日本北海道之旅。</p>
<h3 id="出發前準備">出發前準備</h3>
<h4 id="計畫">計畫</h4>
<ul>
  <li>第一天：到札幌後直接去定山溪溫泉住一晚</li>
  <li>第二天：回札幌市區逛景點</li>
  <li>第三天：小樽、小樽水族館</li>
  <li>第四天： <a href="https://www.kkday.com/zh-tw/product/157133-hokkaido-sapporo-biei-blue-pond-illumination-tour-japan?cid=19365" target="_blank"><strong>KKday 北海道點燈之旅 ｜富良野精靈台＆美瑛青池＆白鬚瀑布一日遊｜札幌出發</strong></a></li>
  <li>第五天：逛街購物、北海道神宮</li>
  <li>第六天：前往機場返程</li>
</ul>

<h4 id="行">行</h4>
<h4 id="機票--中華航空"><strong>機票 — 中華航空</strong></h4>
<ul>
  <li>🛫 03/15 去程 CI130 : TPE 桃園國際機場 T2 <strong>08:35</strong> (實際延誤 3 分鐘)-&gt; CTS 北海道新千歲機場 <strong>13:15</strong> (實際延誤 7 分鐘)</li>
  <li>🛬 03/20 回程 CI131 : CTS 北海道新千歲機場 <strong>14:20</strong> (實際延誤 8 分鐘) <strong>-&gt;</strong> TPE 桃園國際機場 <strong>18:15</strong> (實際提早 3 分鐘)</li>
</ul>

<blockquote>
  <p><em>價格：NT $24,356/人</em></p>
</blockquote>

<blockquote>
  <p>*因為雪季還沒過還是貴，週五回因為六日更貴…</p>
</blockquote>

<h4 id="札幌車站---定山溪-往返巴士車票"><strong>札幌車站 &lt;-&gt; 定山溪 往返巴士車票</strong></h4>

<p><img src="/assets/055527a739dd/1*WwGk0L6ccnJi3FweO4qr_g.webp" alt="" loading="lazy" decoding="async" width="848" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4NDgiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/055527a739dd/1*i7zUMAOkPm7RwhcYbs3xVA.webp" alt="https://jozankei.jp/tw/news/newaccess/" loading="lazy" decoding="async" width="1200" height="848" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg0OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>https://jozankei.jp/tw/news/newaccess/</p>

<p>新千歲機場到札幌站的 JR 我看班次很多加上飛機落地時間抓不太準就沒先買票；前往定山溪溫泉只能搭乘巴士，根據 <a href="https://jozankei.jp/tw/news/newaccess/" target="_blank">2025/12 最新交通資訊</a> ，有 3 種路線可以選擇：</p>
<ol>
  <li><strong>[需預約][約 60 分鐘] 札幌車站 → 搭乘 Kappa Liner 巴士 → 定山溪溫泉。</strong> 
<strong>點此查看時刻表</strong></li>
  <li>[不用預約][約 70 分鐘] 札幌車站 → 真駒內站 → 巴士 → 定山溪溫泉。</li>
  <li>[不用預約][約 120 分鐘] 新千歲機場 → <a href="https://www.hokto.co.jp/makomanai-chitose/" target="_blank">機場巴士（北斗交通）</a> → 真駒內站→ 定山溪溫泉。</li>
</ol>

<p><strong>路線(1) — <a href="https://www.jotetsu.co.jp/bus/kappa_liner/index.html" target="_blank">かっぱライナー号 / 卡帕​內​衣號 / Kappa Liner</a> 是最順最方便的</strong> ，查了一下班次飛機落地後入關再搭 JR 到札幌站走出來，下午 4:00 發車(秋冬限定班次)的時間上綽綽有餘，就先在網路上預約了(外國人也可以！)。</p>

<p><strong>去程 札幌站 -&gt; 定山溪：</strong></p>

<p><img src="/assets/055527a739dd/1*cAKpufRPO7_kJuG-f3l74w.webp" alt="https://www.jotetsu.co.jp/bus/kappa_liner/index.html" loading="lazy" decoding="async" width="868" height="619" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4NjgiIGhlaWdodD0iNjE5Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><a href="https://www.jotetsu.co.jp/bus/kappa_liner/index.html" target="_blank">https://www.jotetsu.co.jp/bus/kappa_liner/index.html</a></p>
<ul>
  <li>要注意 <strong>紅色字的班次只有秋冬季有行駛</strong> 、札幌站 16:00/17:00 發車的班次只開到定山溪車庫前。</li>
</ul>

<p><strong>回程 定山溪 -&gt; 札幌站：</strong></p>

<p><img src="/assets/055527a739dd/1*qVXeuuYsNv__vp_ye6jnvw.webp" alt="https://www.jotetsu.co.jp/bus/kappa_liner/index.html" loading="lazy" decoding="async" width="923" height="535" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MjMiIGhlaWdodD0iNTM1Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><a href="https://www.jotetsu.co.jp/bus/kappa_liner/index.html" target="_blank">https://www.jotetsu.co.jp/bus/kappa_liner/index.html</a></p>
<ul>
  <li>回程想說 11:00 Checkout 後在溫泉街走一走再回去，所以訂 13:15 發車 (白絲瀑布 13:31 上車)的班次</li>
</ul>

<p><strong>預約方式：</strong></p>

<p>1.到 <a href="https://jotetsu.quicktrip.jp/buy" target="_blank">官方預約網站</a> 註冊會員</p>

<p><img src="/assets/055527a739dd/1*CzG4MgskigUp8NXhe3xExw.webp" alt="" loading="lazy" decoding="async" width="767" height="484" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjciIGhlaWdodD0iNDg0Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<ul>
  <li>Email / 台灣手機號碼 OK。</li>
  <li>務必記得帳號密碼，因為是電子車票直接線上啟用給司機確認。</li>
</ul>

<p>2.選擇購買 <a href="https://jotetsu.quicktrip.jp/buy/reservation?salesTicketId=1943" target="_blank">かっぱライナー号 / 卡帕​內​衣號 / Kappa Liner</a> 來回 or 單程</p>

<p><img src="/assets/055527a739dd/1*HjF2Kh18Rm6CxchhV8rP7g.webp" alt="" loading="lazy" decoding="async" width="721" height="187" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3MjEiIGhlaWdodD0iMTg3Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>3.選擇人數</p>

<p><img src="/assets/055527a739dd/1*BuuwoHzpfc4R2XeDb-0Luw.webp" alt="" loading="lazy" decoding="async" width="745" height="279" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NDUiIGhlaWdodD0iMjc5Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>4.選擇 去程 的日期與班次(班次時間以札幌出發表示)</p>

<p><img src="/assets/055527a739dd/1*6JVbtozEiw37fj0EqFDADw.webp" alt="" loading="lazy" decoding="async" width="779" height="1209" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NzkiIGhlaWdodD0iMTIwOSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>5.選擇去程上下車位置</p>

<p><img src="/assets/055527a739dd/1*apTSepE2RXgoJmbK_IoFjA.webp" alt="" loading="lazy" decoding="async" width="762" height="486" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjIiIGhlaWdodD0iNDg2Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>上下車站點可參考下方圖表：</p>

<p><img src="/assets/055527a739dd/1*NyUAFnQb4KqMSoXiRiT6iw.webp" alt="https://www.jotetsu.co.jp/bus/kappa_liner/index.html" loading="lazy" decoding="async" width="1200" height="736" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjczNiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><a href="https://www.jotetsu.co.jp/bus/kappa_liner/index.html" target="_blank">https://www.jotetsu.co.jp/bus/kappa_liner/index.html</a></p>

<p>我們的飯店離 <strong>(2) <a href="https://jozankei.jp/tw/spot/1449/" target="_blank">白絲瀑布/白糸の滝/Shiraitonotaki</a></strong> 比較近所以預約在這站下車。</p>

<blockquote>
  <p><em>後來才發現也可以繼續坐繞一圈坐到 <strong>(7) Jozankei onsen higashi 2 chome / 定山渓溫泉東2丁目</strong> 站下車，就是飯店門口。</em></p>
</blockquote>

<p>5.選擇回程日期與班次(班次時間以出發站點表示)、上下車位置</p>

<p>6.線上刷卡完成付款</p>

<blockquote>
  <p><em>價格： ¥2,920 日圓/大人。</em></p>
</blockquote>

<p><img src="/assets/055527a739dd/1*5flfur4c5dHTJsugsXkN0A.webp" alt="" loading="lazy" decoding="async" width="569" height="1013" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1NjkiIGhlaWdodD0iMTAxMyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>預約訂位成功會收到 Email 通知， <strong>不需要再去兌換直接在 <a href="https://jotetsu.quicktrip.jp/bought" target="_blank">車票網站線上啟用</a> 給司機看即可</strong> 。</p>
<h4 id="esim">eSIM</h4>

<p>同樣在 KKday 購買 5G 吃到飽 eSIM，這次在北海道使用只有在美瑛深山一度收不到訊號之外其他時候都沒問題；這次特別選支援 ChatGPT 的方案，方便查資料。</p>
<ul>
  <li>KKday 購買： <a href="https://www.kkday.com/zh-tw/product/137689-japan-unlimited-data-esim-card?cid=19365" target="_blank">日本 eSIM｜Docomo / KDDI / SoftBank / Rakuten｜ <strong>支援ChatGPT 及 Gemini</strong></a></li>
  <li><strong>eSIM 要在有網路環境下啟用，最好是在機場上飛機前就先在台灣啟用</strong></li>
</ul>

<blockquote>
  <p><em>價格：NT $343/6 日，無限流量（須遵守公平原則）</em></p>
</blockquote>

<h4 id="visit-japan">Visit Japan</h4>

<p><a href="https://zhgchg.li/posts/z-%E5%BA%A6%E6%97%85%E8%A1%8C%E9%81%8A%E8%A8%98/%E4%BA%AC%E9%98%AA%E7%A5%9E%E8%87%AA%E7%94%B1%E8%A1%8C%E6%94%BB%E7%95%A5-%E4%BA%AC%E9%83%BD%E5%A4%A7%E9%98%AA%E7%A5%9E%E6%88%B68%E6%97%A5%E9%81%8A%E5%85%A8%E7%B4%80%E9%8C%84%E8%88%87%E5%AF%A6%E7%94%A8%E4%BA%A4%E9%80%9A%E4%BD%8F%E5%AE%BF%E6%8C%87%E5%8D%97-76d66c2e34af/#20241125-%E6%9B%B4%E6%96%B0%E5%85%A5%E5%A2%83%E5%AF%A9%E6%9F%A5%E8%88%87%E6%B5%B7%E9%97%9C%E7%94%B3%E5%A0%B1qr-code-%E5%B7%B2%E5%90%88%E4%BD%B5%E6%88%90%E5%90%8C%E4%B8%80%E5%80%8B%E5%85%A5%E5%A2%83%E5%AE%A1%E6%9F%A5%E5%8F%8A%E6%B5%B7%E5%85%B3%E7%94%B3%E6%8A%A5%E7%9A%84qr%E7%A0%81%E6%B2%92%E6%9C%89%E8%97%8D%E7%A2%BC%E9%BB%83%E7%A2%BC%E5%8D%80%E5%88%A5%E4%BB%A5%E4%B8%8B%E5%85%A7%E5%AE%B9%E5%8F%AA%E7%95%B6%E7%B4%80%E9%8C%84%E5%8F%AF%E4%BB%A5%E5%BF%BD%E7%95%A5" target="_blank">請參考之前的文章，事前完成登錄就好，現在入境審查與海關審查都用同一個 QRCode。</a></p>
<h3 id="住">住</h3>
<h4 id="031516-1-晚--jozankei-yurakusoan-ゆらく草庵-日式溫泉飯店"><strong>03/15–16 (1 晚) — <a href="https://maps.app.goo.gl/mi1LRdo79NghFL3n7" target="_blank">Jozankei Yurakusoan ゆらく草庵 日式溫泉飯店</a></strong></h4>

<p><img src="/assets/055527a739dd/1*NeSAUcKO7d4QbVw6OGaF6A.webp" alt="_3 Chome-228–1 Jozankeionsenhigashi, Minami Ward, Sapporo, Hokkaido 061–2301日本_" loading="lazy" decoding="async" width="512" height="362" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1MTIiIGhlaWdodD0iMzYyIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><a href="https://maps.app.goo.gl/H3xB8zhQ7vNSeqtaA" target="_blank"><em>3 Chome-228–1 Jozankeionsenhigashi, Minami Ward, Sapporo, Hokkaido 061–2301日本</em></a></p>

<p>位置在定山溪溫泉街的入口。</p>

<blockquote>
  <p><em>價格： ¥ 52,150 日圓 — 1晚雙人房/含豐盛日式早餐/有私人湯屋/禁菸 (Hollywood Twin Room with Tatami — Non-Smoking)</em></p>
</blockquote>

<h4 id="031620-4-晚--keio-prelia-hotel-sapporo--京王プレリアホテル札幌--札幌京王普雷利亞飯店"><strong>03/16–20 (4 晚) —</strong> <a href="https://maps.app.goo.gl/agffZjxdVngA5p5B9" target="_blank">Keio Prelia Hotel Sapporo / 京王プレリアホテル札幌 / 札幌京王普雷利亞飯店</a></h4>

<p><img src="/assets/055527a739dd/1*3oY7bhsG8XdTjJQprMIvhQ.webp" alt="4 Chome-11–1 Kita 8 Jonishi, Kita Ward, Sapporo, Hokkaido 060–0808日本" loading="lazy" decoding="async" width="1144" height="832" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMTQ0IiBoZWlnaHQ9IjgzMiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><a href="https://maps.app.goo.gl/yko9ThsVPsdDj4WP6" target="_blank">4 Chome-11–1 Kita 8 Jonishi, Kita Ward, Sapporo, Hokkaido 060–0808日本</a></p>

<p>位置在札幌車站後站步行約 5 分鐘。</p>

<blockquote>
  <p><em>價格： ¥58,389 日圓 — 4晚舒適加大雙人床房/禁菸。</em></p>
</blockquote>

<h3 id="樂">樂</h3>
<h4 id="kkday-一日遊北海道點燈之旅-富良野精靈台美瑛青池白鬚瀑布一日遊札幌出發"><a href="https://www.kkday.com/zh-tw/product/157133-hokkaido-sapporo-biei-blue-pond-illumination-tour-japan?cid=19365" target="_blank">KKday 一日遊：北海道點燈之旅 ｜富良野精靈台＆美瑛青池＆白鬚瀑布一日遊｜札幌出發</a></h4>

<p><img src="/assets/055527a739dd/1*5vq3ePeRaN3GcAHfFINuvA.webp" alt="北海道點燈之旅 ｜富良野精靈台＆美瑛青池＆白鬚瀑布一日遊｜札幌出發" loading="lazy" decoding="async" width="1175" height="1075" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMTc1IiBoZWlnaHQ9IjEwNzUiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><a href="https://www.kkday.com/zh-tw/product/157133-hokkaido-sapporo-biei-blue-pond-illumination-tour-japan?cid=19365" target="_blank">北海道點燈之旅 ｜富良野精靈台＆美瑛青池＆白鬚瀑布一日遊｜札幌出發</a></p>
<ul>
  <li>
    <table>
      <tbody>
        <tr>
          <td><strong>[悠閒11:50出發 B方案]</strong> 2026/3月&amp;4月</td>
          <td>白天富良野精靈台＆聖誕樹＆白鬚瀑布＆夜晚青池點燈（無四季彩之丘）</td>
        </tr>
      </tbody>
    </table>
  </li>
</ul>

<blockquote>
  <p><em>價格：NT $1,807 台幣/人。</em></p>
</blockquote>

<h4 id="kkday---官方銷售北海道小樽水族館門票-即買即用購票後-30-天內都可使用"><a href="https://www.kkday.com/zh-tw/product/139823-otaru-aquarium-admission-ticket-hokkaido?cid=19365" target="_blank">KKday — 【 官方銷售】北海道小樽水族館門票 (即買即用/購票後 30 天內都可使用)</a></h4>

<p><img src="/assets/055527a739dd/1*LV2OUgLymbyCMmfLjgsfpg.webp" alt="【官方銷售】北海道小樽水族館門票 (即買即用/購票後 30 天內都可使用)" loading="lazy" decoding="async" width="1158" height="939" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMTU4IiBoZWlnaHQ9IjkzOSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><a href="https://www.kkday.com/zh-tw/product/139823-otaru-aquarium-admission-ticket-hokkaido?cid=19365" target="_blank">【官方銷售】北海道小樽水族館門票 (即買即用/購票後 30 天內都可使用)</a></p>
<ul>
  <li>特色是戶外港口圈養、有很多展演跟冬季會有企鵝走路巡迴</li>
</ul>

<blockquote>
  <p><em>準備！出發！</em></p>
</blockquote>

<blockquote>
  <p><a href="https://calec.china-airlines.com/eCheckin/eCheckin_home.aspx" target="_blank"><em>可在出發前三天完成選位與網路報到，加速流程。</em></a></p>
</blockquote>

<h3 id="day-1-0315-日--抵達-北海道札幌定山溪溫泉飯店">Day 1 (03/15 日) — 抵達 北海道札幌、定山溪溫泉飯店</h3>
<h4 id="0500-機場接送-出發"><a href="https://www.kkday.com/zh-tw/trans/airport_transfer?cid=19365" target="_blank">05:00 機場接送</a> 出發</h4>
<h4 id="0535-機場接送-抵達-tpe-桃園國際機場-t2-第二航廈"><a href="https://www.kkday.com/zh-tw/trans/airport_transfer?cid=19365" target="_blank">05:35 機場接送</a> 抵達 TPE 桃園國際機場 T2 第二航廈</h4>

<p><img src="/assets/055527a739dd/1*6_vxPCIXLx06F53yP4zS3g.webp" alt="" loading="lazy" decoding="async" width="1066" height="512" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDY2IiBoZWlnaHQ9IjUxMiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>分配到邊邊角角，06:10 才開櫃。</p>

<p><img src="/assets/055527a739dd/1*2LMJVQ9PSoJkWNLER6nBfA.webp" alt="" loading="lazy" decoding="async" width="602" height="1306" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2MDIiIGhlaWdodD0iMTMwNiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>趁空檔先 <a href="https://www.kkday.com/zh-tw/product/137689-japan-unlimited-data-esim-card?cid=19365" target="_blank">啟用日本 eSIM</a> 。</p>
<h4 id="0640-完成-報到行李托運出境安檢">06:40 完成 報到＋行李托運＋出境安檢</h4>

<p>報到托運可能是因為提早來所以沒花多久時間，出境人蠻多的但也消化的很快，大約 06:40 就已經出境了。</p>

<p><img src="/assets/055527a739dd/1*oOO3XXdFpobNN_KqNEtOyw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>悠閒吃個早餐 Zzzzz。</p>
<h4 id="0730-候機逛逛--d6-登機門">07:30 候機逛逛 — D6 登機門</h4>

<p><img src="/assets/055527a739dd/1*G4-ZNKS-LpmIN5tRNEHe6g.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>離登機還有一點時間先去 <a href="https://zhgchg.li/posts/z-%E5%BA%A6%E6%97%85%E8%A1%8C%E9%81%8A%E8%A8%98/%E6%9D%B1%E4%BA%AC%E8%87%AA%E7%94%B1%E8%A1%8C%E6%94%BB%E7%95%A5-%E5%B7%9D%E8%B6%8A%E5%B0%8F%E6%B1%9F%E6%88%B6%E4%B8%80%E6%97%A5%E9%81%8A%E8%88%87%E7%86%B1%E6%B5%B7%E6%B5%B7%E4%B8%8A%E8%8A%B1%E7%81%AB%E5%A4%A7%E6%9C%83%E5%85%A8%E7%B4%80%E9%8C%84-958599363857/#%E4%BA%8C%E8%88%AA%E5%87%BA%E5%A2%83%E5%85%8D%E7%A8%85%E5%BA%97-le-labo" target="_blank">D1 坐個免費按摩椅(代幣可以跟附近商家索取)</a> 。</p>
<h4 id="0750-準備登機">07:50 準備登機</h4>

<p><img src="/assets/055527a739dd/1*TqzxK3cXR-j2tfGBVLspow.webp" alt="" loading="lazy" decoding="async" width="2048" height="1536" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDQ4IiBoZWlnaHQ9IjE1MzYiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<h4 id="0838-飛機起飛-tpe---cts">08:38 飛機起飛 TPE -&gt; CTS</h4>

<p><img src="/assets/055527a739dd/1*kGuZuIVYW2fYPb3clzIfXg.webp" alt="" loading="lazy" decoding="async" width="788" height="512" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3ODgiIGhlaWdodD0iNTEyIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>搭到的是華航比較新的飛機 Airbus A330–300，空間蠻大的，電視機上娛樂設備也很齊全。</p>

<p><img src="/assets/055527a739dd/1*jTKu8WMuRVfU9IZTxQvfpg.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>吃的就…普普吧。</p>

<blockquote>
  <p><em>飛行時間約：3 小時 50 分鐘，還會再給小點心。</em></p>
</blockquote>

<h4 id="1322-飛機降落-tpe---cts">13:22 飛機降落 TPE -&gt; CTS</h4>

<p><img src="/assets/055527a739dd/1*TAHPtMwcNQIVPH7AohEhOg.webp" alt="" loading="lazy" decoding="async" width="786" height="512" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3ODYiIGhlaWdodD0iNTEyIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<h4 id="1410-完成入境行李提領">14:10 完成入境＋行李提領</h4>

<p>入境蠻快的但是等了很久的行李，不知道是不是人太多(所有機位都賣完)。</p>

<p><img src="/assets/055527a739dd/1*b35AwJ_fwCxrOJqJGcwOTg.webp" alt="" loading="lazy" decoding="async" width="2112" height="878" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMTEyIiBoZWlnaHQ9Ijg3OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>出機場一路跟著「國內線、JR 線」指標一路走。</p>

<p><img src="/assets/055527a739dd/1*UFM6K_85XwH7g19kKd6vRA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>到另一棟大廳後下去 B1F 就是 JR 站了。(手扶梯的話還要再繞一下)</p>

<p><img src="/assets/055527a739dd/1*yTccLA73k_OJ6E5OKFMWQw.webp" alt="時刻表" loading="lazy" decoding="async" width="931" height="862" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MzEiIGhlaWdodD0iODYyIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><a href="https://jrhokkaidonorikae.com/vtime/vtime.php?s=9650&amp;d=20260419" target="_blank">時刻表</a></p>
<h4 id="1429-搭乘-區間快速-從-新千歲機場站-前往-札幌站">14:29 搭乘 區間快速 從 新千歲機場站 前往 札幌站</h4>

<p><img src="/assets/055527a739dd/1*J47APGl3TjqzWKm7XQQJrA.webp" alt="https://www.jrhokkaido.co.jp/global/chinese/travel/airport.html" loading="lazy" decoding="async" width="1101" height="564" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMTAxIiBoZWlnaHQ9IjU2NCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><a href="https://www.jrhokkaido.co.jp/global/chinese/travel/airport.html" target="_blank">https://www.jrhokkaido.co.jp/global/chinese/travel/airport.html</a></p>
<ul>
  <li><a href="https://www.jrhokkaido.co.jp/global/chinese/travel/airport.html" target="_blank">札幌 &lt;-&gt; 新千歲機場所需時間約 33 - 41 分鐘； <strong>可使用 Kitaca、Suica、PASMO等IC卡、不需要在自動售票機排隊</strong> 、1天行駛163班次，非常密集。</a></li>
  <li><a href="https://www.jrhokkaido.co.jp/global/chinese/travel/airport.html" target="_blank">5 節自由席、 1 節指定席</a></li>
</ul>

<p><img src="/assets/055527a739dd/1*6UaZZp6c43PLTj9EhJT36w.webp" alt="" loading="lazy" decoding="async" width="965" height="781" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NjUiIGhlaWdodD0iNzgxIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<blockquote>
  <p><strong><em>可以用西瓜卡直接進站，</em></strong> <em>不過想說把錢找開所以還是去投幣買了車票。</em></p>
</blockquote>

<h4 id="1506-抵達-札幌站">15:06 抵達 札幌站</h4>

<p><img src="/assets/055527a739dd/1*UgPpbSYwAcb02Kb74ML8lw.webp" alt="" loading="lazy" decoding="async" width="1536" height="2048" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNTM2IiBoZWlnaHQ9IjIwNDgiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<ul>
  <li>上個廁所、買個吃的就繼續走去 27 巴士站</li>
</ul>

<h4 id="1515-前往-札幌駅-27番のりば-kappa-line-上車處">15:15 前往 <a href="https://maps.app.goo.gl/MScVjZQHnW84qn626" target="_blank">札幌駅 27番のりば (Kappa Line 上車處)</a></h4>

<p><img src="/assets/055527a739dd/1*8ARDa4cvMa0NvmAMXENJkQ.webp" alt="" loading="lazy" decoding="async" width="1200" height="929" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkyOSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<h4 id="1525-抵達-札幌駅-27番のりば-kappa-line-上車處-候車">15:25 抵達 <a href="https://maps.app.goo.gl/MScVjZQHnW84qn626" target="_blank">札幌駅 27番のりば (Kappa Line 上車處)</a> 候車</h4>

<p><img src="/assets/055527a739dd/1*XQcnNbnu6N_IBLvKtW_5Fg.webp" alt="" loading="lazy" decoding="async" width="4696" height="2060" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0Njk2IiBoZWlnaHQ9IjIwNjAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<blockquote>
  <p><em>札幌站前站走出來左轉下一個路口右轉就看到了，後面有一棟紅色大樓。</em></p>
</blockquote>

<ul>
  <li>日本的服務就是安心，這裡也會有工作人員確認你是要去定山溪的並且有預約的。</li>
</ul>

<h4 id="1600-發車前往定山溪溫泉">16:00 發車前往定山溪溫泉</h4>

<p><img src="/assets/055527a739dd/1*mX62B7-0IYGOOkiCFard2g.webp" alt="" loading="lazy" decoding="async" width="2504" height="2036" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNTA0IiBoZWlnaHQ9IjIwMzYiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<ul>
  <li><a href="https://jotetsu.quicktrip.jp/bought" target="_blank">手機登入預約購票網站點啟用給工作人員看就可以直接上車。</a></li>
  <li>因為是觀光巴士所以直接把行李箱交給工作人員放車廂就可以。</li>
</ul>

<p><img src="/assets/055527a739dd/1*zDFW68P2S_0QYBuo7WJN8g.webp" alt="" loading="lazy" decoding="async" width="1200" height="794" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijc5NCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<ul>
  <li>車程時間約 50 分鐘，3 月中市區已經沒什麼雪了，郊外還有些許積雪</li>
</ul>

<h4 id="1650-抵達定山溪溫泉-2白絲瀑布白糸の滝-站下車">16:50 抵達定山溪溫泉 (2)白絲瀑布/白糸の滝 站下車</h4>

<p><img src="/assets/055527a739dd/1*U1I_hwru8pgom6R3qG_9jA.webp" alt="" loading="lazy" decoding="async" width="1200" height="795" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijc5NSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<ul>
  <li>Kappa Line 巴士塗裝就是定山溪溫泉吉祥物 — 河童</li>
  <li><strong>下車時候再給司機看一下電子車票然後告知要拿行李</strong></li>
  <li>下車往前走過個馬路就到今天要下榻的溫泉飯店了！</li>
</ul>

<h4 id="1700-抵達-jozankei-yurakusoan-ゆらく草庵-日式溫泉飯店">17:00 抵達 <a href="https://maps.app.goo.gl/mi1LRdo79NghFL3n7" target="_blank">Jozankei Yurakusoan ゆらく草庵 日式溫泉飯店</a></h4>

<p><img src="/assets/055527a739dd/1*jXijzCdM080XooCnRKifIQ.webp" alt="" loading="lazy" decoding="async" width="1200" height="797" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijc5NyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>非常傳統日式，整個飯店都是木造建築，入廳就是榻榻米要換鞋子放鞋櫃。</p>
<iframe class="embed-video" loading="lazy" src="https://www.youtube.com/embed/aQBG5TI_Zzg" title="Jozankei Yurakusoan ゆらく草庵 日式溫泉飯店" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
<ul>
  <li>房間很大有獨立的廁所、私人衛浴/湯屋、客廳跟床；厲害的是雖然是傳統木造榻榻米但是整體維護的非常好， <strong>沒有任何髒髒舊舊或是霉味的感覺</strong> 。</li>
  <li>也有公共的私人湯屋可以使用 (有好幾間，沒人就可以進去)</li>
</ul>

<h4 id="1800-出門覓食">18:00 出門覓食</h4>

<p>飯店有一泊二食，我們訂一泊一食(早餐)；出去找找吃的。</p>
<h4 id="1810-北の国定山渓万世閣ホテルミリオーネ3階">18:10 <a href="https://maps.app.goo.gl/x1Wu1bSFXkk8PDbm7" target="_blank">北の国｜定山渓万世閣・ホテルミリオーネ3階</a></h4>

<p>溫泉街幾乎都是泊+食，所以外面的餐廳不多；最後走去另一家飯店 <a href="https://maps.app.goo.gl/x1Wu1bSFXkk8PDbm7" target="_blank">万世閣</a> 的 3 樓居酒屋吃晚餐。</p>

<p><img src="/assets/055527a739dd/1*iCfHKvgNp00NMpSeW_MnXw.webp" alt="" loading="lazy" decoding="async" width="1200" height="424" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjQyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<ul>
  <li>第一杯: 正 — Sapporo 生啤酒</li>
  <li>吃了豬排飯 跟 名物炸鯽魚 (蠻好吃的)</li>
</ul>

<h4 id="1930-回飯店泡湯休息">19:30 回飯店泡湯休息</h4>

<p><img src="/assets/055527a739dd/1*IqDyTM7yqweHmbUSLNZSRQ.webp" alt="" loading="lazy" decoding="async" width="1200" height="576" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjU3NiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<ul>
  <li>幸好路上超商蠻多家的，去全家順路買了宵夜晚上吃。</li>
</ul>

<blockquote>
  <p><em>雖然沒下雪了，但是晚上太陽下山後還是很冷，奔波一天，早早回飯店泡湯休息。</em></p>
</blockquote>

<h3 id="day-2-0316-一--定山溪溫泉街札幌市區景點">Day 2 (03/16 一) — 定山溪溫泉街、札幌市區景點</h3>
<h4 id="0900-吃早餐">09:00 吃早餐</h4>

<p><img src="/assets/055527a739dd/1*ypWZE7fthJG5vBU8uC_F2A.webp" alt="" loading="lazy" decoding="async" width="1200" height="452" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjQ1MiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>剛好分配到靠窗的位子，窗外景色非常幽美。</p>

<p><img src="/assets/055527a739dd/1*zIdJFU-HuUUqDTW20dTX2Q.webp" alt="" loading="lazy" decoding="async" width="1200" height="574" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjU3NCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<blockquote>
  <p><em>早餐很豐盛：主要有飯/野菜飯、味增湯、(1) 中間蒸豬肉＋野菜、(2)冷鮭魚 — 可用中間酒精燈鋁箔加熱、(3)玉子燒。</em></p>
</blockquote>

<h4 id="1100-checkout-寄放行李定山溪溫泉街附近走走">11:00 Checkout 寄放行李、定山溪溫泉街附近走走</h4>

<p><img src="/assets/055527a739dd/1*RIvrEU6Nizz0EnOj-extDw.webp" alt="" loading="lazy" decoding="async" width="1456" height="1151" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDU2IiBoZWlnaHQ9IjExNTEiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<blockquote>
  <p><em>離回程預約的巴士 13:31 上車還有一段時間，就把整個定山溪溫泉走了一圈。</em></p>
</blockquote>

<h4 id="1110-定山溪神社">11:10 定山溪神社</h4>

<p><img src="/assets/055527a739dd/1*otr7MA3QH-m__iTJI6AxWg.webp" alt="" loading="lazy" decoding="async" width="960" height="1280" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NjAiIGhlaWdodD0iMTI4MCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/055527a739dd/1*gOkeBho1WpoY-Bowj3e5qA.webp" alt="" loading="lazy" decoding="async" width="902" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDIiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>整個神社都被大雪冰封了。</p>
<h4 id="1130-河童家族的祈願手湯"><a href="https://maps.app.goo.gl/q9rNER1WAPXw9DPH7" target="_blank">11:30 河童家族的祈願手湯</a></h4>

<p><img src="/assets/055527a739dd/1*dBB1541WoB5lOVGSfaG44g.webp" alt="" loading="lazy" decoding="async" width="1536" height="2048" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNTM2IiBoZWlnaHQ9IjIwNDgiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>簡單洗個手。</p>

<p>旁邊斜坡往下走就是溫泉公園。</p>
<h4 id="1140-定山源泉公園"><a href="https://maps.app.goo.gl/j7K1pCCkWTsPX2uKA" target="_blank">11:40 定山源泉公園</a></h4>

<p><img src="/assets/055527a739dd/1*e4RVh1Mg-cxup8svIuUpPw.webp" alt="" loading="lazy" decoding="async" width="1200" height="792" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijc5MiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>公園有公共泡腳的浴池跟可以煮水煮蛋的湧口。</p>

<p><img src="/assets/055527a739dd/1*FVNqZAQ-8GVQsEcLza7uMg.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<blockquote>
  <p><em>如果想要體驗煮溫泉蛋可以走過橋到對面的 <a href="https://maps.app.goo.gl/JQ8MedX36trBSw7EA" target="_blank">㈱定山渓物産館 中央店</a> 買溫泉蛋來煮然後帶回店裡吃。</em></p>
</blockquote>

<p><img src="/assets/055527a739dd/1*JXKfDrv0HzbuwK7HENsOcA.webp" alt="" loading="lazy" decoding="async" width="3004" height="1996" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzMDA0IiBoZWlnaHQ9IjE5OTYiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<h4 id="1200-路過-ふる川果日-jglacée"><a href="https://maps.app.goo.gl/iUFZh6uSjqmqffw88" target="_blank">12:00 路過 ふる川果日 J.glacée</a></h4>

<p><img src="/assets/055527a739dd/1*Ap1oNMTiOhNzn6JBFs6mcg.webp" alt="" loading="lazy" decoding="async" width="1200" height="798" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijc5OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>繼續往上走路過一家麵包店，有剛出爐的蘋果可頌，買了一個來嚐鮮，味道不錯。</p>
<h4 id="1215-經過-岩戶觀音堂"><a href="https://maps.app.goo.gl/6Ymw95Ptk4BNpX1z6" target="_blank">12:15 經過 岩戶觀音堂</a></h4>

<p><img src="/assets/055527a739dd/1*QxUH_qMGIGBPCNJNG16zBw.webp" alt="" loading="lazy" decoding="async" width="1536" height="2048" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNTM2IiBoZWlnaHQ9IjIwNDgiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>經過這裡之後往前再往下走就會回到起點飯店了。</p>

<p><img src="/assets/055527a739dd/1*BhscZ015lnl7cK7NxMlAPA.webp" alt="" loading="lazy" decoding="async" width="1536" height="2048" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNTM2IiBoZWlnaHQ9IjIwNDgiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/055527a739dd/1*qYBqoqgOiP2n19HP1pwXEg.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>可以想像之前的雪有多大，溪上的吊橋也都被雪冰封了！</p>
<h4 id="1230-走回到飯店">12:30 走回到飯店</h4>

<p>還有點時間就繼續去附近的白絲瀑布看看。</p>

<p><img src="/assets/055527a739dd/1*wOsNH9LlgSPzBv2LHxB3aA.webp" alt="" loading="lazy" decoding="async" width="3312" height="2210" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzMzEyIiBoZWlnaHQ9IjIyMTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>要拐個彎走下坡；結果悲報，也是被雪冰封了沒有開放。</p>
<h4 id="1310-回到飯店拿行李">13:10 回到飯店拿行李</h4>
<h4 id="1315-到-2-白絲瀑布-候車-來的時候的對面">13:15 到 <a href="https://maps.app.goo.gl/XE4wcHVAofeew1DQ8" target="_blank">(2) 白絲瀑布 候車 (來的時候的對面)</a></h4>

<p><img src="/assets/055527a739dd/1*6fAEfLztaLDeyirDO8CTmg.webp" alt="" loading="lazy" decoding="async" width="2956" height="1964" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyOTU2IiBoZWlnaHQ9IjE5NjQiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/055527a739dd/1*m8v0h-nb67YzZWgkldbdBQ.webp" alt="" loading="lazy" decoding="async" width="960" height="1280" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NjAiIGhlaWdodD0iMTI4MCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>我們的車是 13:15 從起始站發車，預計是 13:31 才會到白絲瀑布站，等待時間很無聊，踩踩雪。</p>
<h4 id="1340-上車回札幌站">13:40 上車回札幌站</h4>
<ul>
  <li>上下車一樣出示一次電子車票給司機看</li>
</ul>

<h4 id="1450-回到札幌入住-keio-prelia-hotel-sapporo--京王プレリアホテル札幌--札幌京王普雷利亞飯店">14:50 回到札幌，入住 <a href="https://maps.app.goo.gl/JP8qZhQiLpvaFaVq9" target="_blank">Keio Prelia Hotel Sapporo : 京王プレリアホテル札幌 : 札幌京王普雷利亞飯店</a></h4>

<p><img src="/assets/055527a739dd/1*ShHQszkicgDZYCPb-3VaHw.webp" alt="" loading="lazy" decoding="async" width="2912" height="1936" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyOTEyIiBoZWlnaHQ9IjE5MzYiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<iframe class="embed-video" loading="lazy" src="https://www.youtube.com/embed/6ENDDDNTK8g" title="Keio Prelia Hotel Sapporo : 京王プレリアホテル札幌 : 札幌京王普雷利亞飯店" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
<ul>
  <li>飯店很新、設備齊全，房間算大。</li>
  <li>地點在後站比較不熱鬧，要走一小段、札幌站也沒藥妝店； <strong>相較之下大通站周邊比較熱鬧。</strong></li>
</ul>

<blockquote>
  <p><em>15:20 左右整裝重新出發，沒有吃午餐呢，計劃早點去大通公園附近吃晚餐。</em></p>
</blockquote>

<h4 id="1530-yodobashi">15:30 <a href="https://maps.app.goo.gl/AGEuLosKQh1iSNE4A" target="_blank">Yodobashi</a></h4>

<p><img src="/assets/055527a739dd/1*6RC-qsV-DaGBnN-Yd4OSnw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>飯店走到 JR/地鐵站的路上就有一家 Yodobashi (後面有 <a href="https://maps.app.goo.gl/3xn9SZskRd8Yzr2t9" target="_blank">麥當勞</a> )，去看了看扭蛋。</p>
<h4 id="1610-抵達-薄野-すすきの駅">16:10 抵達 薄野 すすきの駅</h4>

<p><img src="/assets/055527a739dd/1*jdoeeUN6g1laKeVunxxbaQ.webp" alt="" loading="lazy" decoding="async" width="1536" height="2048" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNTM2IiBoZWlnaHQ9IjIwNDgiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>搭車到薄野站準備去吃札幌有名的「羊肉」烤肉。</p>
<h4 id="1615-成吉思汗-達摩-本店-開吃">16:15 <a href="https://maps.app.goo.gl/3TvfU2SAzF8beSFm6" target="_blank">成吉思汗 達摩 本店</a> 開吃</h4>

<p><img src="/assets/055527a739dd/1*VKpLNNn6ryHdjU0c80J2Cw.webp" alt="" loading="lazy" decoding="async" width="1200" height="800" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjgwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<ul>
  <li>點了兩份招牌跟一份一般的肉還有白飯、生啤(大杯…有點太大杯)。</li>
  <li>肉非常好吃，完全沒有羊騷味，肉質很嫩。</li>
  <li>這樣狂吃一人大約才 $700 台幣。</li>
</ul>

<blockquote>
  <p><em>店內不大，附近還有分店，吃飽接近 17:00 外面已經很多人在排隊了。</em></p>
</blockquote>

<h4 id="1700-薄野站十字路口"><a href="https://maps.app.goo.gl/kN9qw85rbk16KAWh6" target="_blank">17:00 薄野站十字路口</a></h4>

<p><img src="/assets/055527a739dd/1*0_Uo2gxSY01IJ7PS2jgWCg.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>吃飽慢慢走回大通站，想去 <a href="https://maps.app.goo.gl/HdrBr15BZhTY9xoy5" target="_blank">札幌電視塔</a> ，路過札幌最有名的十字路口。</p>
<h4 id="1715-經過-狸小路順便逛逛扭蛋唐吉訶德">17:15 經過 <a href="https://maps.app.goo.gl/t9UREfvkAkyNrCim6" target="_blank">狸小路順便逛逛扭蛋、唐吉訶德</a></h4>

<p><img src="/assets/055527a739dd/1*Z0HU0h6iFvmVKFFxlFcrTA.webp" alt="" loading="lazy" decoding="async" width="2048" height="911" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDQ4IiBoZWlnaHQ9IjkxMSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>這裡有一家 MEGA 整棟的 <a href="https://maps.app.goo.gl/t9UREfvkAkyNrCim6" target="_blank">唐吉訶德</a> 。</p>
<h4 id="1741-札幌電視塔">17:41 <a href="https://maps.app.goo.gl/HdrBr15BZhTY9xoy5" target="_blank">札幌電視塔</a></h4>

<p><img src="/assets/055527a739dd/1*uW87zB98sSmv7KTGQBwWRw.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<h4 id="1830-札幌電視塔-夜景">18:30 <a href="https://maps.app.goo.gl/HdrBr15BZhTY9xoy5" target="_blank">札幌電視塔</a> 夜景</h4>

<p><img src="/assets/055527a739dd/1*66Xgue8l-kfCCv_N8iGa3Q.webp" alt="" loading="lazy" decoding="async" width="1200" height="798" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijc5OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>在札幌塔待到大約 18:30 看完夜景就下來了。</p>

<p><img src="/assets/055527a739dd/1*XgVlX-8dfSDPQzb3ajJzug.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>可以順便蓋印章搜集明信片。</p>

<p><img src="/assets/055527a739dd/1*D5X9adov6oTZWgf4qOMZUQ.webp" alt="" loading="lazy" decoding="async" width="1536" height="2048" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNTM2IiBoZWlnaHQ9IjIwNDgiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<h4 id="1840-parco-札幌-逛街"><a href="https://maps.app.goo.gl/pexoh92RLGVdG9N36" target="_blank">18:40 PARCO 札幌</a> 逛街</h4>

<p><img src="/assets/055527a739dd/1*f44BK8MmMCPBRjyRgD9N4g.webp" alt="" loading="lazy" decoding="async" width="3540" height="1560" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzNTQwIiBoZWlnaHQ9IjE1NjAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>電視塔出來下一個路口就是 Parco 札幌店，來逛逛街。</p>
<h4 id="2030-買超商宵夜回飯店休息">20:30 買超商宵夜回飯店休息</h4>

<p><img src="/assets/055527a739dd/1*x52OxjlfB-Udv1DWwxrSVA.webp" alt="" loading="lazy" decoding="async" width="1200" height="449" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjQ0OSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>晚餐很早就吃，所以宵夜要多吃一點；買了超商泡麵＋串燒＋炸雞還有草莓水果酒(好喝)跟 <a href="https://the-sapporo-bar.susukino-brewing.com/" target="_blank"><strong>北海道 The Sapporo Bar 物語聯名的威士忌巧克力冰淇淋</strong></a> (真的有酒味，蠻好吃的)。</p>
<h3 id="day-3-0317-二--小樽小樽水族館">Day 3 (03/17 二) — 小樽、小樽水族館</h3>
<h4 id="0930-出門">09:30 出門</h4>

<p><img src="/assets/055527a739dd/1*NCc4ExDsn14kx-uIpTxBWw.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>買個麥當勞早餐在車上吃。</p>
<h4 id="1000-札幌站上車前往小樽站">10:00 札幌站上車前往小樽站</h4>

<p><img src="/assets/055527a739dd/1*sVzzX65_gTfTTnwA5kG-XA.webp" alt="" loading="lazy" decoding="async" width="379" height="153" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzNzkiIGhlaWdodD0iMTUzIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<blockquote>
  <p><strong><em>結果是區間車，人很多只能站著，</em></strong> <em>根本沒辦法在車上吃。</em></p>
</blockquote>

<h4 id="1042-抵達小樽站">10:42 抵達小樽站</h4>

<p><img src="/assets/055527a739dd/1*pH7YPEhWohZ5L9mVmdhX7A.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>看這人潮就知道很多人來小樽了。</p>
<h4 id="1045-小樽站">10:45 小樽站</h4>

<p><img src="/assets/055527a739dd/1*0oQhRkSceYFsf3jHZwV9Bg.webp" alt="" loading="lazy" decoding="async" width="1074" height="810" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDc0IiBoZWlnaHQ9IjgxMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>小樽站正面出來過馬路右邊就是巴士站，直接在 3月台 等候前往 <strong>１０小樽水族館線：</strong></p>

<p><img src="/assets/055527a739dd/1*1iMFSEhrHNtnxkYzh8O30Q.webp" alt="圖片來源：Google Map - K T (torute)" loading="lazy" decoding="async" width="935" height="907" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MzUiIGhlaWdodD0iOTA3Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><a href="https://maps.app.goo.gl/p7mnP8KpppyUdtwu7" target="_blank">圖片來源：Google Map</a> - <a href="https://maps.google.com/maps/contrib/117333353874320617109" target="_blank">K T (torute)</a></p>

<p><img src="/assets/055527a739dd/1*irY-RRWlc8mKIiM6s6bdXw.webp" alt="" loading="lazy" decoding="async" width="734" height="826" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3MzQiIGhlaWdodD0iODI2Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<ul>
  <li><strong>１０小樽水族館線</strong> ：幾乎是直達，只需約 20 分鐘就能抵達小樽水族館。</li>
  <li><strong>１１祝津線</strong> ：不是直達、也是會到但是要約 30 分鐘。</li>
</ul>

<h4 id="1110-抵達小樽水族館">11:10 抵達小樽水族館</h4>

<p><img src="/assets/055527a739dd/1*f3ZL6ho5OMt3wYHfVRT-TQ.webp" alt="" loading="lazy" decoding="async" width="1200" height="452" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjQ1MiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>直接在櫃檯出示線上買的 <a href="https://www.kkday.com/zh-tw/product/139823-otaru-aquarium-admission-ticket-hokkaido?cid=19365" target="_blank">【官方銷售】北海道小樽水族館門票 (即買即用/購票後 30 天內都可使用)</a> QR Code，就能直接進場。</p>
<h4 id="小樽水族館自製地圖">小樽水族館自製地圖</h4>

<p><img src="/assets/055527a739dd/1*H0BTCoeqStQ7n4POyH6ymw.webp" alt="" loading="lazy" decoding="async" width="2522" height="1918" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNTIyIiBoZWlnaHQ9IjE5MTgiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<ul>
  <li>冬天摩天輪封閉中，摩天輪不在水族館園區內要另外買票</li>
  <li>小樽水族館雖然不大但是物種豐富、照顧的也不錯；展演也很多整體體驗不錯。</li>
  <li>紅色是企鵝散步路線</li>
</ul>

<p><img src="/assets/055527a739dd/1*VpRX9psi2InntONIV365jA.webp" alt="時間會因為季節而有所不同請 參考官網資訊 。" loading="lazy" decoding="async" width="3116" height="1400" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzMTE2IiBoZWlnaHQ9IjE0MDAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>時間會因為季節而有所不同請 <a href="https://uu-beihaidao.tw/corporate/otaru-aq.shtml" target="_blank">參考官網資訊</a> 。</p>
<h4 id="進場後右轉出來往下走去--海獸公園">進場後右轉出來往下走去 — 海獸公園</h4>

<p><img src="/assets/055527a739dd/1*HjDF6BvQMKbWcx1HpFS4yA.webp" alt="" loading="lazy" decoding="async" width="1200" height="913" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkxMyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/055527a739dd/1*frqiW_5h5tX81n9DqKUXlw.webp" alt="" loading="lazy" decoding="async" width="1200" height="685" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjY4NSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>這邊只有上坡有手扶梯，下坡蠻抖的。</p>

<p><strong>所有戶外的展演活動都在這邊：</strong></p>
<ul>
  <li>海豹活動：來的時候 11:20 正在表演</li>
  <li>北海獅活動 (超大海獅)：11:40</li>
  <li>企鵝野餐：12:00</li>
  <li>企鵝活動：沒看到</li>
  <li>海象的覓食解說：沒看到</li>
</ul>

<p><img src="/assets/055527a739dd/1*DB-wHVOUcIjP73sMVSXeCA.webp" alt="" loading="lazy" decoding="async" width="2804" height="2350" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyODA0IiBoZWlnaHQ9IjIzNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<h4 id="1140-北海獅表演">11:40 北海獅表演</h4>

<p><img src="/assets/055527a739dd/1*0cK0DaJLgqU8v2aqloSntg.webp" alt="" loading="lazy" decoding="async" width="3966" height="1756" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzOTY2IiBoZWlnaHQ9IjE3NTYiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>來的時間早站在第一排，北海獅真的好巨大！</p>
<h4 id="海豹">海豹</h4>

<p><img src="/assets/055527a739dd/1*VlGMYBS8lHbzv--Fnoccxg.webp" alt="" loading="lazy" decoding="async" width="4086" height="1964" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0MDg2IiBoZWlnaHQ9IjE5NjQiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>看完表演隔壁是海豹池，每一隻都很愜意。</p>

<blockquote>
  <p><em>海豹海獅可以買魚投餵互動， <strong>有限量、有的池子不能餵要自己看標示。</strong></em></p>
</blockquote>

<h4 id="1200-企鵝散步">12:00 企鵝散步</h4>

<p><img src="/assets/055527a739dd/1*OI0aGYtaim4YBadFYp91nQ.webp" alt="" loading="lazy" decoding="async" width="1200" height="602" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjYwMiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<blockquote>
  <p><em>完整路線如上圖紅色線，最後會去企鵝池泡泡水。</em></p>
</blockquote>

<p>看完海豹之後就來企鵝散步路線等，地板有一條白線，站在線外等企鵝經過；我站在轉角這(如上圖人的位置)。</p>

<p><img src="/assets/055527a739dd/1*FxieyG5_jW2rSbBjlz4jPg.webp" alt="" loading="lazy" decoding="async" width="1928" height="1772" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxOTI4IiBoZWlnaHQ9IjE3NzIiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>是小企鵝，有點像南崁企鵝。</p>
<iframe class="embed-video" loading="lazy" src="https://www.youtube.com/embed/iGPgTEcVJWc" title="小樽水族館 企鵝散步 (海獸公園) / おたる水族館 ペンギンの海まで遠足（海獣公園） / Otaru Aquarium Penguin Picnic (Marine mammal park)" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>

<p><strong>最終會走到企鵝池泡泡水：</strong></p>

<p><img src="/assets/055527a739dd/1*AC942rnUoH5eUTxG8hpdfQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<h4 id="也去看其他在室內的動物們海豹海象">也去看其他在室內的動物們：(海豹、海象)</h4>

<p><img src="/assets/055527a739dd/1*L4Ow3BnB1I2VaSfm-HraCw.webp" alt="" loading="lazy" decoding="async" width="1200" height="794" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijc5NCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>看完後往海豚館方向移動：</p>

<p><img src="/assets/055527a739dd/1*YJq8kpXFK_b7gwOu7fw9tQ.webp" alt="" loading="lazy" decoding="async" width="2808" height="1638" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyODA4IiBoZWlnaHQ9IjE2MzgiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>海豚館外也有一個小的企鵝池：</p>

<p><img src="/assets/055527a739dd/1*eSjHmjQ2Xb6J1mQjH93i1Q.webp" alt="" loading="lazy" decoding="async" width="2048" height="1536" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDQ4IiBoZWlnaHQ9IjE1MzYiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<h4 id="1240-南海獅海豚表演">12:40 南海獅＋海豚表演</h4>

<p><img src="/assets/055527a739dd/1*zApgCBreekd_oHyGNhuGmA.webp" alt="" loading="lazy" decoding="async" width="1200" height="798" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijc5OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>看完回到本館參觀市內的海洋生物：</p>

<p><img src="/assets/055527a739dd/1*7JOgPNrhoEEWZ9fvaDk7dw.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>物種也蠻豐富的。</p>

<p><strong>日本最高齡的小爪水獺：</strong></p>

<p><img src="/assets/055527a739dd/1*2TlrnSrYe31PT_qlk9RrMQ.webp" alt="" loading="lazy" decoding="async" width="1200" height="577" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjU3NyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<blockquote>
  <p><em>這隻水獺年事已高，動作踉蹌緩慢，大家就不要再去打擾他享受晚年了。</em></p>
</blockquote>

<p>逛完紀念品店後，我們就回到來時的巴士站等車。</p>
<h4 id="1400-搭乘公車回小樽站">14:00 搭乘公車回小樽站</h4>
<h4 id="1420-回到小樽站">14:20 回到小樽站</h4>

<p>走站前大道往小樽運河方向走。</p>

<p><img src="/assets/055527a739dd/1*eCDjh5Dp6zq7Lra8dZyQzg.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>接近路底就能看到 LeTAO 運河店：</p>

<p><img src="/assets/055527a739dd/1*j9S4vpv8COBT_jiAu_95wA.webp" alt="" loading="lazy" decoding="async" width="3360" height="1916" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzMzYwIiBoZWlnaHQ9IjE5MTYiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/055527a739dd/1*sPnweRj4fwtR97NXNLchTw.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>這邊除了有賣伴手禮也有賣冰淇淋甜點還有熟食咖啡廳 <a href="https://maps.app.goo.gl/K9PY2rFURawPitX38" target="_blank">ルタオ小樽運河店 三番庫カフェ</a> 。</p>

<p><img src="/assets/055527a739dd/1*u-odR1T48phugsDA844MHQ.webp" alt="" loading="lazy" decoding="async" width="1200" height="902" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<blockquote>
  <p><em>餅乾蠻好吃的，另外一盒鐵盒是草莓乾巧克力，有點太甜。</em></p>
</blockquote>

<h4 id="1440-ルタオ小樽運河店-三番庫カフェ-吃午餐"><a href="https://maps.app.goo.gl/K9PY2rFURawPitX38" target="_blank">14:40 ルタオ小樽運河店 三番庫カフェ</a> 吃午餐</h4>

<p><img src="/assets/055527a739dd/1*Pm2hpXp23qw2J1hKJ0oixg.webp" alt="" loading="lazy" decoding="async" width="3998" height="1502" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzOTk4IiBoZWlnaHQ9IjE1MDIiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>隨便點了個義大利麵熟食果腹，還有甜點套餐(蛋糕好吃)。</p>
<h4 id="1600-小樽運河漫步">16:00 小樽運河漫步</h4>

<p><img src="/assets/055527a739dd/1*64MslNJEByUEGfoSK0YTGA.webp" alt="" loading="lazy" decoding="async" width="2048" height="1536" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDQ4IiBoZWlnaHQ9IjE1MzYiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/055527a739dd/1*6XxJGWGJCVSZGo-sualLdg.webp" alt="" loading="lazy" decoding="async" width="612" height="711" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2MTIiIGhlaWdodD0iNzExIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>吃飽繼續前進到小樽運河漫步，一路走往 <a href="https://maps.app.goo.gl/fX5yyT2tVRV8Lo98A" target="_blank">小樽蒸汽鐘</a> 。</p>

<p><img src="/assets/055527a739dd/1*xCKpu7uLjK8VgfkbRuCSAQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>往 <a href="https://maps.app.goo.gl/fX5yyT2tVRV8Lo98A" target="_blank">小樽蒸汽鐘</a> 的商店家有非常多小店、伴手禮店、琉璃製品店可以逛。</p>

<p><img src="/assets/055527a739dd/1*kzVBtUi0gLMICty86NrsSA.webp" alt="" loading="lazy" decoding="async" width="3988" height="1746" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzOTg4IiBoZWlnaHQ9IjE3NDYiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>在這邊買了很多餅乾伴手禮，尤其是北海道才買得到的 — 六花亭。</p>

<p><img src="/assets/055527a739dd/1*skVsoy14UQ1TzJ26bBOShA.webp" alt="" loading="lazy" decoding="async" width="2920" height="1400" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyOTIwIiBoZWlnaHQ9IjE0MDAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<blockquote>
  <p><em>六花亭必買的 — 萊姆酒餅乾(保存期限短、冰過比較好吃)、酒心糖(是真的酒)</em></p>
</blockquote>

<p><img src="/assets/055527a739dd/1*6TcrcwJUKKGXrSHL1_vJSg.webp" alt="" loading="lazy" decoding="async" width="2724" height="1818" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNzI0IiBoZWlnaHQ9IjE4MTgiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>路上有一家 <a href="https://maps.app.goo.gl/eq7F2zZ5P6RuUJ1H6" target="_blank">北一硝子 Outlet</a> 有賣北海道地酒而且可以免費試喝。</p>

<p><img src="/assets/055527a739dd/1*9KidHCaTaB9XSzch8ul8vA.webp" alt="" loading="lazy" decoding="async" width="4123" height="3100" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0MTIzIiBoZWlnaHQ9IjMxMDAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<blockquote>
  <p><em>買了一瓶哈密瓜酒回去喝。</em></p>
</blockquote>

<blockquote>
  <p><em><strong>有哈密瓜果肉的濃稠感，哈密瓜味也很重，套冰塊、氣泡水喝比較好喝。</strong></em></p>
</blockquote>

<h4 id="1710-走到路底-小樽蒸汽鐘">17:10 走到路底 <a href="https://maps.app.goo.gl/fX5yyT2tVRV8Lo98A" target="_blank">小樽蒸汽鐘</a></h4>

<p><img src="/assets/055527a739dd/1*F2amQ8v_lM3yYvDnzXta3A.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>時間接近傍晚了，開始往回走。</p>

<p><img src="/assets/055527a739dd/1*4FRDy264prK1bVwCm2BpRw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<h4 id="1810-回到小樽站">18:10 回到小樽站</h4>

<p><img src="/assets/055527a739dd/1*WI719YuzTLNf4fTMVWBOOg.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/055527a739dd/1*hcCX6rn7zFpfD38KbgP-Jw.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>沒搭到 18:00 快速機場回札幌的，隨便搭了一班回去(比較慢)。</p>
<h4 id="1900-回到-札幌站">19:00 回到 札幌站</h4>
<h4 id="1930-savoy-sapporo-station-kitaguchi-北海道湯咖喱">19:30 <a href="https://maps.app.goo.gl/1tAe4EyEFP2r5wC98" target="_blank">Savoy Sapporo Station Kitaguchi</a> 北海道湯咖喱</h4>

<p><img src="/assets/055527a739dd/1*uWJwZJH4ZnZq92cIDGUabg.webp" alt="" loading="lazy" decoding="async" width="2612" height="1482" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNjEyIiBoZWlnaHQ9IjE0ODIiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>飯店附近有一家評價蠻高＋可以外帶(太累)的湯咖喱，買回飯店吃。</p>

<blockquote>
  <p><em>食材都很新鮮，咖喱味不會搶過食材的鋒頭，清爽好吃。</em></p>
</blockquote>

<p><img src="/assets/055527a739dd/1*8MK6Ra5FKK9jKxpKwWrIvg.webp" alt="" loading="lazy" decoding="async" width="2356" height="1118" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMzU2IiBoZWlnaHQ9IjExMTgiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>宵夜是超商熱狗＋哈密瓜酒(也不錯)＋養樂多 1000 減糖版，補充乳酸菌(其實每天都有喝，只是懶得拍了)。</p>
<h3 id="day-4-0318-三--kkday-北海道點燈之旅-富良野精靈台美瑛青池白鬚瀑布一日遊札幌出發">Day 4 (03/18 三) — <a href="https://www.kkday.com/zh-tw/product/157133-hokkaido-sapporo-biei-blue-pond-illumination-tour-japan?cid=19365" target="_blank"><strong>KKday 北海道點燈之旅 ｜富良野精靈台＆美瑛青池＆白鬚瀑布一日遊｜札幌出發</strong></a></h3>

<blockquote>
  <p><em>我們選的是 <strong>悠閒11:50 出發 B方案</strong> ，但是晚上就會到很晚，預計 21:40 才會回到札幌大通站。</em></p>
</blockquote>

<p><img src="/assets/055527a739dd/1*r4yUA95KAdrxtt6SBKWZfA.webp" alt="" loading="lazy" decoding="async" width="872" height="783" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4NzIiIGhlaWdodD0iNzgzIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>集合地點： <a href="https://maps.app.goo.gl/dgPQLs75bVNTbVPR9" target="_blank">大通地鐵站 31 號出口</a> ，前一天有收到行前通知信可以加導遊/車導的聯絡方式。</p>
<ul>
  <li><strong>11:50 是發車時間，務必在 11:40 抵達</strong></li>
  <li><strong>11:50 是發車時間，務必在 11:40 抵達</strong></li>
  <li><strong>11:50 是發車時間，務必在 11:40 抵達</strong></li>
</ul>

<h4 id="1000-出門前往大通站">10:00 出門前往大通站</h4>
<h4 id="1030-先去附近的-客美多咖啡-大通西二丁目店-吃早餐">10:30 先去附近的 <a href="https://maps.app.goo.gl/3m7TRSjJWUJAWDfi9" target="_blank">客美多咖啡 大通西二丁目店</a> 吃早餐</h4>

<p><img src="/assets/055527a739dd/1*HpkfNpNAwG8fjJ-Cm9BsUg.webp" alt="" loading="lazy" decoding="async" width="1200" height="451" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjQ1MSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<ul>
  <li>點飲料送餐包＋單點炸蝦漢堡</li>
</ul>

<blockquote>
  <p><em>因為路程遙遠想說多儲備一些體力。</em></p>
</blockquote>

<h4 id="1130-左右抵達-大通地鐵站-31-號出口-公園這等車">11:30 左右抵達 <a href="https://maps.app.goo.gl/dgPQLs75bVNTbVPR9" target="_blank">大通地鐵站 31 號出口</a> 公園這等車</h4>

<p><img src="/assets/055527a739dd/1*v4Yooh8ijGwkJw00MO1gMg.webp" alt="" loading="lazy" decoding="async" width="1200" height="452" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjQ1MiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/055527a739dd/1*mc74gZLhUIfqbW49JYgUXw.webp" alt="" loading="lazy" decoding="async" width="960" height="1280" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NjAiIGhlaWdodD0iMTI4MCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<blockquote>
  <p><em>一日遊幾乎都在這個公園四周集合，要跟導遊確認一下是不是自己的團，不然會上錯車。</em></p>
</blockquote>

<h4 id="1140-車導出現上車">11:40 車導出現，上車</h4>

<p><img src="/assets/055527a739dd/1*AFAqnskVajf2pY0py1Bn7w.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>上次去「 <a href="https://zhgchg.li/posts/z-%E5%BA%A6%E6%97%85%E8%A1%8C%E9%81%8A%E8%A8%98/%E4%B9%9D%E5%B7%9E%E8%87%AA%E7%94%B1%E8%A1%8C%E6%94%BB%E7%95%A5-%E9%87%9C%E5%B1%B1%E6%96%B0%E5%B1%B1%E8%8C%B6%E8%8A%B1%E8%99%9F%E9%83%B5%E8%BC%AA%E5%85%A5%E5%A2%83%E6%97%A5%E6%9C%AC%E5%8D%9A%E5%A4%9A-%E6%B7%B1%E5%85%A5%E7%94%B1%E5%B8%83%E9%99%A2-%E5%A4%A7%E5%88%86-%E7%A6%8F%E5%B2%A1%E7%AD%89%E5%9C%B0-cb65fd5ab770/#day-9-kkday-%E9%AB%98%E5%8D%83%E7%A9%97%E4%B8%80%E6%97%A5%E9%81%8A%E9%AB%98%E5%8D%83%E7%A9%97%E7%A5%9E%E7%A4%BE%E9%AB%98%E5%8D%83%E7%A9%97%E5%B3%BD%E8%B0%B7%E5%A4%A9%E5%B2%A9%E6%88%B8%E7%A5%9E%E7%A4%BE%E5%A4%A9%E5%AE%89%E6%B2%B3%E5%8E%9F" target="_blank">KKDAY 高千穗一日遊，高千穗神社、高千穗峽谷、天岩戸神社、天安河原</a> 」是大團遊覽車司機＋導遊；這次第一次遇到 6 人小團，就只有車導；不過好處是人少移動比較快速。</p>

<p><img src="/assets/055527a739dd/1*GGf6O-gDlFpehOsdYEx-vw.webp" alt="" loading="lazy" decoding="async" width="1536" height="2048" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNTM2IiBoZWlnaHQ9IjIwNDgiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>先高速公路＋進入深山的山路，真的蠻遠的。</p>

<blockquote>
  <p><strong><em>到第一個景點車程約：1 小時 50 分鐘。</em></strong></p>
</blockquote>

<h4 id="1330-富良野森林精靈露台-白天"><a href="https://www.google.com/maps/search/?api=1&amp;query=43.3232668%2C142.3563319&amp;query_place_id=ChIJPbaM-V5Sc18RLOjzIysMtac" target="_blank">13:30 富良野森林精靈露台-白天</a></h4>

<p><img src="/assets/055527a739dd/1*2ur7G9_kzOM1MD8eauR0fg.webp" alt="" loading="lazy" decoding="async" width="3122" height="1784" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzMTIyIiBoZWlnaHQ9IjE3ODQiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>這裡有遊客中心可以上廁所，後面就是富良野滑雪場。</p>

<p><img src="/assets/055527a739dd/1*7M8YnUnPp4Srks7MyeFIHw.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/055527a739dd/1*aJdiFhYD9bY2-DjAjpJxog.webp" alt="" loading="lazy" decoding="async" width="3052" height="2036" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzMDUyIiBoZWlnaHQ9IjIwMzYiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/055527a739dd/1*vBRe8LKhvuxUAPJ1YMuVPw.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<blockquote>
  <p><em>富良野森林精靈露台就是在森林中的木棧道，有很多小木屋賣手作商品，整體不大，走一圈不到 10 分鐘；冬天白天可以看雪景，晚上有夜景點燈。</em></p>
</blockquote>

<blockquote>
  <p><em><strong>店家門口、店內都禁止攝影。</strong></em></p>
</blockquote>

<p><img src="/assets/055527a739dd/1*Bh5lCi-uhSPsvKmRHxITag.webp" alt="買了一個木頭月對手做紀念品" loading="lazy" decoding="async" width="903" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDMiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>買了一個木頭月對手做紀念品</p>

<blockquote>
  <p><em>因為我們比較早到，所以在此停留 50 分鐘。</em></p>
</blockquote>

<h4 id="1420-出發前往-ハーブガーデン富良野-herb-garden-furano">14:20 出發前往 <a href="https://maps.app.goo.gl/kYdVBB1XYsgGo45x7" target="_blank">ハーブガーデン富良野 (HERB GARDEN FURANO)</a></h4>

<blockquote>
  <p><strong><em>車程約：30 分鐘。</em></strong></p>
</blockquote>

<blockquote>
  <p><em>休息站(購物站) 行程， <strong>無強迫推銷，裡面的冰淇淋好吃！</strong></em></p>
</blockquote>

<blockquote>
  <p><em>停留約 30 分鐘。</em></p>
</blockquote>

<h4 id="1540-抵達-美瑛車站">15:40 抵達 <a href="https://www.google.com/maps/search/?api=1&amp;query=43.5911991%2C142.4633615&amp;query_place_id=ChIJVzdU8tzFDF8RTXvFSGX1S8Y" target="_blank">美瑛車站</a></h4>

<p><img src="/assets/055527a739dd/1*fEsxWXDi675dMyBw-cZnag.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/055527a739dd/1*kquYhK10b7_hz5ASDgp5gQ.webp" alt="" loading="lazy" decoding="async" width="1200" height="797" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijc5NyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<blockquote>
  <p><a href="https://www.google.com/maps/search/?api=1&amp;query=43.5911991%2C142.4633615&amp;query_place_id=ChIJVzdU8tzFDF8RTXvFSGX1S8Y" target="_blank"><em>美瑛車站</em></a> <em>很小附近很荒涼，主要是拍拍照上個廁所跟 <strong>去附近買些東西當晚餐帶在路上吃</strong> 。</em></p>
</blockquote>

<blockquote>
  <p><em>附近有一家 7–11 跟麵包店可以選擇。</em></p>
</blockquote>

<p><img src="/assets/055527a739dd/1*e2sqcvDP_rl7hJpTK8FlWQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>買了一個麵包跟超商熟食當路上的晚餐。</p>

<blockquote>
  <p><em>停留約 50 分鐘。</em></p>
</blockquote>

<h4 id="1630-出發前往-美瑛聖誕樹">16:30 出發前往 <a href="https://maps.app.goo.gl/5MbFa5QCD46tfjH67" target="_blank">美瑛聖誕樹</a></h4>

<blockquote>
  <p><em>車程約 10 分鐘，路邊景點。</em></p>
</blockquote>

<h4 id="1640-抵達-美瑛聖誕樹">16:40 抵達 <a href="https://maps.app.goo.gl/5MbFa5QCD46tfjH67" target="_blank">美瑛聖誕樹</a></h4>

<p><img src="/assets/055527a739dd/1*VNDn9fdGddz9VJpvUunO_g.webp" alt="" loading="lazy" decoding="async" width="3198" height="2130" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzMTk4IiBoZWlnaHQ9IjIxMzAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/055527a739dd/1*bmTIWwQmcsnzZoqe0ciDlg.webp" alt="" loading="lazy" decoding="async" width="1477" height="1108" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDc3IiBoZWlnaHQ9IjExMDgiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>四季來看都有不同風景，今天是夕陽若隱若現的雪景。</p>

<blockquote>
  <p><em>停留約 20 分鐘。</em></p>
</blockquote>

<h4 id="1700-出發前往-白鬚瀑布">17:00 出發前往 <a href="https://www.google.com/maps/search/?api=1&amp;query=43.474803813168%2C142.63934225235&amp;query_place_id=none" target="_blank">白鬚瀑布</a></h4>

<blockquote>
  <p><em>車程約 30 分鐘，橋景點。</em></p>
</blockquote>

<h4 id="1730-抵達-白鬚瀑布">17:30 抵達 <a href="https://www.google.com/maps/search/?api=1&amp;query=43.474803813168%2C142.63934225235&amp;query_place_id=none" target="_blank">白鬚瀑布</a></h4>

<p><img src="/assets/055527a739dd/1*dDpDKI44Mek6JN6zFChEhA.webp" alt="" loading="lazy" decoding="async" width="1200" height="683" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjY4MyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>從這座人行鐵橋往下看就是結冰的白鬚瀑布。</p>

<p><img src="/assets/055527a739dd/1*tiviDULpwjoqXcl4kcncfw.webp" alt="" loading="lazy" decoding="async" width="1536" height="2048" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNTM2IiBoZWlnaHQ9IjIwNDgiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<blockquote>
  <p><em>停留約 20 分鐘。</em></p>
</blockquote>

<blockquote>
  <p><em>時間接近傍晚，天氣寒冷； <strong>橋面有結冰走路要小心</strong> 。</em></p>
</blockquote>

<h4 id="1750-出發前往-白金青池">17:50 出發前往 <a href="https://www.google.com/maps/search/?api=1&amp;query=43.493606086454%2C142.61433241373&amp;query_place_id=none" target="_blank">白金青池</a></h4>

<blockquote>
  <p><em>車程約 5 分鐘。</em></p>
</blockquote>

<h4 id="1755-抵達最後一個景點-白金青池">17:55 抵達最後一個景點 <a href="https://www.google.com/maps/search/?api=1&amp;query=43.493606086454%2C142.61433241373&amp;query_place_id=none" target="_blank">白金青池</a></h4>

<p><img src="/assets/055527a739dd/1*E9VIGAECznfFqG9QKyRqBA.webp" alt="" loading="lazy" decoding="async" width="2160" height="2172" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMTYwIiBoZWlnaHQ9IjIxNzIiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<blockquote>
  <p><em>停留約 30 分鐘，有遊客中心廁所。</em></p>
</blockquote>

<p><img src="/assets/055527a739dd/1*l61TfdQcTz-adTlROZsmnA.webp" alt="" loading="lazy" decoding="async" width="1536" height="2048" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNTM2IiBoZWlnaHQ9IjIwNDgiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>停車場往上走就能看到青池。</p>

<p><img src="/assets/055527a739dd/1*1g4Qki4CDbE4WWOoNig2Rg.webp" alt="" loading="lazy" decoding="async" width="2048" height="1536" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDQ4IiBoZWlnaHQ9IjE1MzYiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/055527a739dd/1*68bEAmSijcpqQbGn8z58lA.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/055527a739dd/1*b92j-p7ghCGyP3peml3M3A.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<blockquote>
  <p><em>是一個靜謐景點，死寂冰凍的潭面透過光影的變換展現出一絲生機。</em></p>
</blockquote>

<h4 id="1830-回程-大通地鐵站-31-號出口">18:30 回程 <a href="https://maps.app.goo.gl/dgPQLs75bVNTbVPR9" target="_blank">大通地鐵站 31 號出口</a></h4>

<p><img src="/assets/055527a739dd/1*VwEV_rxTwap-JzHlqs4kMw.webp" alt="" loading="lazy" decoding="async" width="2048" height="1536" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDQ4IiBoZWlnaHQ9IjE1MzYiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<blockquote>
  <p><em>車程：約 2 小時 30 分鐘，中間大約 20:00 左右有經過休息站讓大家去上廁所。</em></p>
</blockquote>

<h4 id="2055-抵達-大通地鐵站-31-號出口">20:55 抵達 <a href="https://maps.app.goo.gl/dgPQLs75bVNTbVPR9" target="_blank">大通地鐵站 31 號出口</a></h4>

<p>整體來說跟之前「 <a href="https://zhgchg.li/posts/z-%E5%BA%A6%E6%97%85%E8%A1%8C%E9%81%8A%E8%A8%98/%E4%B9%9D%E5%B7%9E%E8%87%AA%E7%94%B1%E8%A1%8C%E6%94%BB%E7%95%A5-%E9%87%9C%E5%B1%B1%E6%96%B0%E5%B1%B1%E8%8C%B6%E8%8A%B1%E8%99%9F%E9%83%B5%E8%BC%AA%E5%85%A5%E5%A2%83%E6%97%A5%E6%9C%AC%E5%8D%9A%E5%A4%9A-%E6%B7%B1%E5%85%A5%E7%94%B1%E5%B8%83%E9%99%A2-%E5%A4%A7%E5%88%86-%E7%A6%8F%E5%B2%A1%E7%AD%89%E5%9C%B0-cb65fd5ab770/#day-9-kkday-%E9%AB%98%E5%8D%83%E7%A9%97%E4%B8%80%E6%97%A5%E9%81%8A%E9%AB%98%E5%8D%83%E7%A9%97%E7%A5%9E%E7%A4%BE%E9%AB%98%E5%8D%83%E7%A9%97%E5%B3%BD%E8%B0%B7%E5%A4%A9%E5%B2%A9%E6%88%B8%E7%A5%9E%E7%A4%BE%E5%A4%A9%E5%AE%89%E6%B2%B3%E5%8E%9F" target="_blank">KKDAY 高千穗一日遊，高千穗神社、高千穗峽谷、天岩戸神社、天安河原</a> 」心得差不多，就是坐車走馬看花；自己要去這些景點非常麻煩，路程一半是高速一半是山路無人區(感覺就會有很多熊)；這次遇到小團體行動加上司機狂飆所以比預期時間早回到市區。</p>
<h4 id="2130-回到札幌站">21:30 回到札幌站</h4>

<p>大通站附近走一走後就回札幌站了，大部分吃的也都關門了。</p>
<h4 id="札幌-つなぐ-橫町"><a href="https://maps.app.goo.gl/kWrKsjCDcQXqZ7MW6" target="_blank"><strong>札幌 つなぐ 橫町</strong></a></h4>

<p>查了一下車站附近還有一個夜市還有營業就走去看看，隨便都好，吃個什麼。</p>

<p><img src="/assets/055527a739dd/1*PthAxfmCvDvVAb1A96Xxcw.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/055527a739dd/1*chm5p8whKi2G8nLTCbNu1Q.webp" alt="" loading="lazy" decoding="async" width="3516" height="1996" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzNTE2IiBoZWlnaHQ9IjE5OTYiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>隨便選了一家有烤串的 (主要是烤鰻魚)，先吃一點。</p>

<blockquote>
  <p><em>這家也只營業到 3 月，本來是想吃手羽先的，結果選錯家。</em></p>
</blockquote>

<h4 id="2230-回飯店">22:30 回飯店</h4>

<p><img src="/assets/055527a739dd/1*iVNNjGw0DokpzTnl6XKUyg.webp" alt="" loading="lazy" decoding="async" width="3038" height="1730" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzMDM4IiBoZWlnaHQ9IjE3MzAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>吃完還有點餓，去附近超商買點宵夜回去吃。</p>

<blockquote>
  <p>S <em>eicomart 有賣一些 Lawsone/711/全家沒有的東西。</em></p>
</blockquote>

<h3 id="day-5-0319-四-逛街採購北海道神宮">Day 5 (03/19 四) —逛街採購、北海道神宮</h3>
<h4 id="1000-出門吃早餐-麥當勞">10:00 出門吃早餐 <a href="https://maps.app.goo.gl/3xn9SZskRd8Yzr2t9" target="_blank">麥當勞</a></h4>

<p><img src="/assets/055527a739dd/1*38MT4SC9vjfjZBJua2NE1A.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/055527a739dd/1*kODLJ4LXRV_ZXVa20qwZNg.webp" alt="" loading="lazy" decoding="async" width="1200" height="799" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijc5OSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<ul>
  <li>McGriddles 厚鬆餅堡(台灣沒賣) — 培根起司蛋，有蜂蜜甜甜鹹鹹的</li>
  <li>草莓起司派(台灣後來也開賣) — 草莓果醬＋起司</li>
</ul>

<h4 id="1000-大通-狸小路繼續逛扭蛋-唐吉訶德-採購">10:00 大通 狸小路繼續逛扭蛋 <a href="https://maps.app.goo.gl/t9UREfvkAkyNrCim6" target="_blank">、唐吉訶德</a> 採購</h4>

<p><img src="/assets/055527a739dd/1*7TSlQ_cHD2h9sBSh_w1XVQ.webp" alt="" loading="lazy" decoding="async" width="3654" height="2084" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzNjU0IiBoZWlnaHQ9IjIwODQiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/055527a739dd/1*B9w_5kkFC02FeDDpl49I_Q.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<blockquote>
  <p><em>札幌站沒什麼好逛，這裡比較多東西可以逛。</em></p>
</blockquote>

<p><img src="/assets/055527a739dd/1*ATM-ffFeXSn1OU2MsF1VYw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>扭了一個火災報警器跟一些有趣的扭蛋。</p>
<h4 id="1330-回札幌站飯店放東西百貨公司繼續逛吃">13:30 回札幌站飯店放東西、百貨公司繼續逛吃</h4>

<p><img src="/assets/055527a739dd/1*WlIAL89mRGqmZUHBO6zqTg.webp" alt="午餐隨意吃了百貨公司的散壽司＋蕎麥麵" loading="lazy" decoding="async" width="4117" height="3093" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0MTE3IiBoZWlnaHQ9IjMwOTMiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>午餐隨意吃了百貨公司的散壽司＋蕎麥麵</p>
<h4 id="le-labo-ル-ラボ-札幌大丸店"><a href="https://maps.app.goo.gl/fdeQ5DFzfkGPGgaQ8" target="_blank">LE LABO ル ラボ 札幌大丸店</a></h4>

<p>順便瞄一眼 Le Labo 2026 / 札幌 買的價格：</p>

<p><img src="/assets/055527a739dd/1*Q039ga_Vw3MQgF0g0BRqwA.webp" alt="" loading="lazy" decoding="async" width="1086" height="1013" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDg2IiBoZWlnaHQ9IjEwMTMiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<ul>
  <li>50ml ¥31,350 跟 <a href="/posts/z-度旅行遊記/東京自由行攻略-川越小江戶一日遊與熱海海上花火大會全紀錄-958599363857/">2025 東京價格一樣</a></li>
</ul>

<blockquote>
  <p><em>逛逛買買大約 15:00 左右出發去 <a href="https://maps.app.goo.gl/sZDHVC7X3ctkwQzX9" target="_blank">北海道神宮</a> 。</em></p>
</blockquote>

<blockquote>
  <p><em>札幌出發先坐南北線到大通站轉東西線 <a href="https://maps.app.goo.gl/rhiPPKBaugwZBxid6" target="_blank">圓山公園站</a> ，車埕約 30 分鐘。</em></p>
</blockquote>

<h4 id="1540-抵達-圓山公園站">15:40 抵達 <a href="https://maps.app.goo.gl/rhiPPKBaugwZBxid6" target="_blank">圓山公園站</a></h4>

<p><img src="/assets/055527a739dd/1*UVfaLNsp7YdujXBEEzQHCQ.webp" alt="" loading="lazy" decoding="async" width="1858" height="2490" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxODU4IiBoZWlnaHQ9IjI0OTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<blockquote>
  <p><em>如果早點去可以先坐去附近 <a href="https://maps.app.goo.gl/jZe1NSebYoACZfEj8" target="_blank"><strong>有名的北海道海產迴轉壽司 Toriton</strong></a></em> 。</p>
</blockquote>

<blockquote>
  <p><em>圓山公園站 3 號出口出來直接朝面公園的方向往裡面走就是北海道神宮了。</em></p>
</blockquote>

<blockquote>
  <p><em>路程約：850公尺 / 15 分鐘</em></p>
</blockquote>

<p><img src="/assets/055527a739dd/1*S002OiQvL92tuOKH1IdGkg.webp" alt="" loading="lazy" decoding="async" width="1200" height="801" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjgwMSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>過個馬路到底就是公園入口了：</p>

<p><img src="/assets/055527a739dd/1*D8BbsmFeID8pc06a0JjeTw.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<blockquote>
  <p><em>當你跟朋友約在圓山時…</em></p>
</blockquote>

<blockquote>
  <p><em>我：圓山公園 💁</em></p>
</blockquote>

<p><img src="/assets/055527a739dd/1*xqB_Gf70oekdjB_7kAlyUw.webp" alt="" loading="lazy" decoding="async" width="1200" height="800" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjgwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>先穿過圓山公園(這裡還有很多積雪)，就能看到神宮鳥居入口了：</p>

<p><img src="/assets/055527a739dd/1*cBmTSlECefOGTo6xrR-uLA.webp" alt="" loading="lazy" decoding="async" width="3092" height="4117" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzMDkyIiBoZWlnaHQ9IjQxMTciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<h4 id="1550-神宮茶屋"><a href="https://maps.app.goo.gl/A4X2vpZic2F3onHD9" target="_blank">15:50 神宮茶屋</a></h4>

<p><img src="/assets/055527a739dd/1*cDFq2pN_R-mdNkjb9J6M2Q.webp" alt="" loading="lazy" decoding="async" width="1128" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMTI4IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>繼續走路上會經過神宮茶屋，先去吃個北海道牛奶冰淇淋。</p>

<p><img src="/assets/055527a739dd/1*ak0-XC8XNs7U80Jv7w0SHw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>很濃郁好吃。</p>

<blockquote>
  <p><strong><em>神宮內不能吃東西，在茶屋這吃完再走。</em></strong></p>
</blockquote>

<h4 id="1615-北海道神宮"><a href="https://maps.app.goo.gl/sZDHVC7X3ctkwQzX9" target="_blank">16:15 北海道神宮</a></h4>

<p><img src="/assets/055527a739dd/1*geeJcHKwKdulE3ZAKdrdXw.webp" alt="" loading="lazy" decoding="async" width="1698" height="1236" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNjk4IiBoZWlnaHQ9IjEyMzYiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<blockquote>
  <p><em>參拜： <strong>投五円(意結緣) -&gt; 鞠躬兩次 -&gt; 拍手兩次 -&gt; 許願 -&gt; 結束鞠躬一次</strong></em></p>
</blockquote>

<p><img src="/assets/055527a739dd/1*vIk2569WTqkQU_EOFagzkA.webp" alt="" loading="lazy" decoding="async" width="2826" height="1484" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyODI2IiBoZWlnaHQ9IjE0ODQiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/055527a739dd/1*Mvgp7m0K3DkGJTemBXAIEw.webp" alt="" loading="lazy" decoding="async" width="1974" height="1484" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxOTc0IiBoZWlnaHQ9IjE0ODQiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>買了兩個御守，比較特別的是交通御守，可以掛在車內跟有一個貼紙可以貼在機車、腳踏車、安全帽上。</p>
<h4 id="1630-離開北海道神宮">16:30 離開北海道神宮</h4>

<p><img src="/assets/055527a739dd/1*uf7nqGUX9f_Sx-RWSieeSQ.webp" alt="" loading="lazy" decoding="async" width="3260" height="2170" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzMjYwIiBoZWlnaHQ9IjIxNzAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>從神宮跟公園慢慢走回地鐵站準備回札幌站附近吃晚餐了。</p>
<h3 id="1720-經過札幌北海道廳紅磚廳舍">17:20 <a href="https://maps.app.goo.gl/TPopSdeEizc4L3pt9" target="_blank">經過札幌北海道廳紅磚廳舍</a></h3>

<p><img src="/assets/055527a739dd/1*HepNYeLKdeZnUDG-5z9OjA.webp" alt="" loading="lazy" decoding="async" width="1536" height="2048" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNTM2IiBoZWlnaHQ9IjIwNDgiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>只是路過，主要是去隔壁的札幌三井吃燒肉：</p>

<p><img src="/assets/055527a739dd/1*0q_FfJh3NGkGSK2Qy3R0Yg.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<h3 id="1730-yakiniku-bar-tamura-燒肉"><a href="https://maps.app.goo.gl/G8HjR4dNjvcoLA246" target="_blank">17:30 Yakiniku Bar Tamura</a> 燒肉</h3>

<p><img src="/assets/055527a739dd/1*VNc3gye9wK6y56eHPc2deA.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>來試試北海道和牛。</p>

<p><img src="/assets/055527a739dd/1*AYwFzwBblluHK6-hmf_Cfw.webp" alt="" loading="lazy" decoding="async" width="3458" height="1640" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzNDU4IiBoZWlnaHQ9IjE2NDAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<blockquote>
  <p><em>點了北海道牛套餐＋生菜＋啤酒＋飲料＋冷麵，總共約 NT$3,100；和牛油脂豐富入口即化，牛排也很好吃，最驚豔的是牛舌，很 Q 很好吃。</em></p>
</blockquote>

<h4 id="1900-札幌東急百貨"><a href="https://maps.app.goo.gl/rR4a14JofuNjSBXT8" target="_blank">19:00 札幌東急百貨</a></h4>

<p><img src="/assets/055527a739dd/1*M1Jvj4MqsexEZInEN2CYiw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>吃飽走來最後一逛，東急百貨。</p>

<p><img src="/assets/055527a739dd/1*ruyLfADnDmXwM1iObk1rig.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>這邊樓上也有玩具、扭蛋。</p>
<h4 id="2000-回到札幌站">20:00 回到札幌站</h4>

<p><img src="/assets/055527a739dd/1*Qm2l3HH2jAG788N4lvMRDA.webp" alt="" loading="lazy" decoding="async" width="901" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDEiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>百貨公司快關門了，最後再逛逛。</p>
<h4 id="札幌車站--サンドイッチ工房-sandria"><a href="https://www.s-sandwich.com/" target="_blank">札幌車站 — サンドイッチ工房 Sandria</a></h4>

<p><img src="/assets/055527a739dd/1*6d-W6JOQaseQid4b7YrKow.webp" alt="" loading="lazy" decoding="async" width="1200" height="686" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjY4NiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>回飯店的路上買了每天經過 JR 車站都很多人都在排的三明治販賣機。</p>

<blockquote>
  <p><em>一人限購兩個，我買了一個豬排當宵夜、馬鈴薯蛋沙拉當明天早餐。</em></p>
</blockquote>

<h4 id="2230-札幌最後一晚的宵夜">22:30 札幌最後一晚的宵夜</h4>

<p><img src="/assets/055527a739dd/1*RUURJltr-9QnEkSuGXEsUA.webp" alt="" loading="lazy" decoding="async" width="3552" height="2020" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzNTUyIiBoZWlnaHQ9IjIwMjAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<blockquote>
  <p>配了第 <em>一天有喝的草莓酒跟北海道牛奶冰淇淋。</em></p>
</blockquote>

<h3 id="day-6-0320-五--新千歲機場回程">Day 6 (03/20 五) — 新千歲機場、回程</h3>
<h4 id="1000-起床房間吃早餐">10:00 起床房間吃早餐</h4>

<p><img src="/assets/055527a739dd/1*O6AlKj2q5BGnpifN8xfb1Q.webp" alt="" loading="lazy" decoding="async" width="2048" height="1536" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDQ4IiBoZWlnaHQ9IjE1MzYiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>吃完收一收 Checkout 後就直奔機場了。</p>
<h4 id="1105-札幌站等車">11:05 札幌站等車</h4>

<p><img src="/assets/055527a739dd/1*NZDX273R7I57ua8K7dgQZg.webp" alt="" loading="lazy" decoding="async" width="1200" height="728" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjcyOCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<h4 id="1119-札幌站搭車前往新千歲機場">11:19 札幌站搭車前往新千歲機場</h4>

<blockquote>
  <p><em>快速機場 68快速新千歲機場、38 分鐘</em></p>
</blockquote>

<h4 id="1200-抵達新千歲機場">12:00 抵達新千歲機場</h4>

<p><img src="/assets/055527a739dd/1*GY04A3vbPbQHK3yCnjUSyw.webp" alt="" loading="lazy" decoding="async" width="958" height="1115" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NTgiIGhlaWdodD0iMTExNSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<h4 id="jr-新千歲空港國內線大廳">JR 新千歲空港、國內線大廳</h4>

<p><img src="/assets/055527a739dd/1*gh59koOOU8_OF3ONpsXV-g.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<blockquote>
  <p><em>地鐵往上走到 1 樓這邊大廳有很多吃的跟伴手禮，如果買不夠這裡還有的逛。</em></p>
</blockquote>

<h4 id="要回去了一路從來時的方向往回走">要回去了…一路從來時的方向往回走</h4>

<p><img src="/assets/055527a739dd/1*6W0gH8pkfaj0OJyXMEdLbQ.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<h4 id="1217-抵達國際線大廳">12:17 抵達國際線大廳</h4>

<p><img src="/assets/055527a739dd/1*7WibQith1SFr80NzDbpVpw.webp" alt="" loading="lazy" decoding="async" width="1704" height="1179" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNzA0IiBoZWlnaHQ9IjExNzkiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>分配到最邊邊的報到櫃檯，還有走 165 公尺才會到：</p>

<p><img src="/assets/055527a739dd/1*s5JrKPKZCS7aalLWzJzQAQ.webp" alt="" loading="lazy" decoding="async" width="961" height="772" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NjEiIGhlaWdodD0iNzcyIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>北海道鳥銀喉長尾山雀：</p>

<p><img src="/assets/055527a739dd/1*vfgtjZcmirVzk2On2jCiaA.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<h4 id="1222-抵達報到櫃檯">12:22 抵達報到櫃檯</h4>

<blockquote>
  <p><em>現場人很多，網路報到少一點，因此務必在回程 <a href="https://calec.china-airlines.com/eCheckin/eCheckin_home.aspx" target="_blank">前三天完成選位與網路報到，節省排隊時間。</a></em></p>
</blockquote>

<p><img src="/assets/055527a739dd/1*Kf8mo1c0_Q_dCgUF3cYV-g.webp" alt="" loading="lazy" decoding="async" width="1200" height="835" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjgzNSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>這邊後面還有一家唐吉訶德可以在托運前最後採購：</p>

<p><img src="/assets/055527a739dd/1*Nh4v3mOEt0UkPi9ERBJ8yw.webp" alt="" loading="lazy" decoding="async" width="1200" height="833" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjgzMyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<h4 id="1303-完成報到行李托運">13:03 完成報到＋行李托運</h4>
<h4 id="1317-完成安檢出境審查">13:17 完成安檢、出境審查</h4>

<blockquote>
  <p><em>日本機場安檢比較嚴格，如果穿靴子都要脫下來過 X 光。</em></p>
</blockquote>

<p><img src="/assets/055527a739dd/1*KEoEKjL5F66qscCtVKyLcw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<h4 id="1320-候機逛逛">13:20 候機逛逛</h4>

<p><img src="/assets/055527a739dd/1*jYqkVX8z5aFrCMr9LD7vlQ.webp" alt="" loading="lazy" decoding="async" width="3322" height="1064" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzMzIyIiBoZWlnaHQ9IjEwNjQiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/055527a739dd/1*vwC7ieofFNIOVl2L_0YjKA.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<blockquote>
  <p><em>有一條美食街可以吃東西，我只買了麵包、炸雞、哈密瓜冰淇淋加減吃一點。</em></p>
</blockquote>

<p><img src="/assets/055527a739dd/1*_83IZv2r6TiRQW-isqq3iw.webp" alt="" loading="lazy" decoding="async" width="1200" height="833" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjgzMyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>一樓有整排伴手禮店還可以買，幾乎什麼都有；但是精品店比較少。</p>

<p><img src="/assets/055527a739dd/1*5UHmAXCqA7_aR5EeHTvWzg.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>二樓也有一些伴手禮。</p>
<h4 id="1430-完成登機">14:30 完成登機</h4>

<p><img src="/assets/055527a739dd/1*ggFHkcOkMXJO-u8Opfn1lw.webp" alt="" loading="lazy" decoding="async" width="1200" height="802" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjgwMiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>跟來時候一樣是新的空客飛機，位置大、娛樂設施新。</p>
<h4 id="1500-起飛再會--札幌">15:00 起飛，再會 — 札幌</h4>

<p><img src="/assets/055527a739dd/1*NsbLIJ-RuRa4YOkVICnPGA.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>飛機餐一樣普普：</p>

<p><img src="/assets/055527a739dd/1*a20WxDinpEcoDIUd6Bi3xw.webp" alt="" loading="lazy" decoding="async" width="2048" height="1536" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDQ4IiBoZWlnaHQ9IjE1MzYiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<h4 id="1815-落地台灣桃園國際機場">18:15 落地台灣桃園國際機場</h4>
<h4 id="1915-入境提領行李回家休息">19:15 入境＋提領行李，回家休息</h4>

<p>這次行李等了超久…</p>
<h3 id="感謝閱讀">感謝閱讀</h3>

<p>感謝您看到這裡，一起體驗北海道的各種風景，下個地方再見！</p>
<h3 id="機上免費-wifi">機上免費 WiFi</h3>

<p><img src="/assets/055527a739dd/1*lK1NDVwT4V1iu0XiZhUtsw.webp" alt="" loading="lazy" decoding="async" width="1120" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMTIwIiBoZWlnaHQ9IjEyMDAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>這航次華航提供全程的訊息 WiFi 功能，可以跟朋友傳訊息聊天。</p>

<blockquote>
  <p><strong><em>如果你手機有裝 AdGuard DNS 或是類似的 DNS 廣告阻擋服務，需要先去設定 -&gt; 一般 -&gt; VPN與裝置管理 -&gt; 限制和代理伺服器 -&gt; DNS -&gt; 改回自動。</em></strong></p>
</blockquote>

<blockquote>
  <p><em>不然會無法連上機上 WiFi。</em></p>
</blockquote>

<h3 id="戰利品">戰利品</h3>

<p><img src="/assets/055527a739dd/1*zSYwjAvHNp4zYYXixRVExA.webp" alt="" loading="lazy" decoding="async" width="3238" height="2432" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzMjM4IiBoZWlnaHQ9IjI0MzIiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<ul>
  <li>札幌農學校(牛奶餅乾)、烤玉米餅乾(膨化) 也不錯</li>
</ul>

<h4 id="玩具">玩具</h4>

<p><img src="/assets/055527a739dd/1*Q4kKEs40fKLCm6dfF0LG7Q.webp" alt="" loading="lazy" decoding="async" width="1200" height="901" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<ul>
  <li>扭了平交道、招牌燈、車鑰匙、火災報警器、唐吉訶德公仔、日本隊球衣</li>
  <li>富良野精靈台買的木製手作樂手</li>
</ul>]]></content>
  </entry><entry>
    <title type="html">iOS Certificates、Identifiers與Profiles詳解｜Fastlane Match統一管理憑證與CI/CD整合實戰</title>
    <link href="https://zhgchg.li/posts/zrealm-dev/ios-certificates-identifiers%E8%88%87profiles%E8%A9%B3%E8%A7%A3-fastlane-match%E7%B5%B1%E4%B8%80%E7%AE%A1%E7%90%86%E6%86%91%E8%AD%89%E8%88%87ci-cd%E6%95%B4%E5%90%88%E5%AF%A6%E6%88%B0-823ac523ccc8/" rel="alternate" type="text/html" title="iOS Certificates、Identifiers與Profiles詳解｜Fastlane Match統一管理憑證與CI/CD整合實戰" />
    <published>2026-01-04T01:03:46+08:00</published>
    <updated>2026-01-05T08:55:47+08:00</updated>
    <id>https://zhgchg.li/posts/zrealm-dev/823ac523ccc8</id><summary type="html">針對iOS開發者與團隊，解決憑證混亂與管理困難問題，透過Fastlane Match統一管理Certificates、Profiles並安全同步至Git私有庫，搭配GitHub Actions實現CI/CD自動化，提升開發效率與憑證安全性。</summary><author>
      <name>ZhgChgLi</name>
    </author><category term="ZRealm Dev." /><category term="ios-app-development" /><category term="fastlane" /><category term="keychain" /><category term="github-actions" /><category term="macos" /><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://zhgchg.li/assets/823ac523ccc8/1*-OS8G9xu0CFpnlAQRgnGAg.webp" /><content type="html" xml:base="https://zhgchg.li/posts/zrealm-dev/ios-certificates-identifiers%E8%88%87profiles%E8%A9%B3%E8%A7%A3-fastlane-match%E7%B5%B1%E4%B8%80%E7%AE%A1%E7%90%86%E6%86%91%E8%AD%89%E8%88%87ci-cd%E6%95%B4%E5%90%88%E5%AF%A6%E6%88%B0-823ac523ccc8/"><![CDATA[<h3 id="ios-certificates-identifiers--profiles-是什麼及-fastlane-match-統一-管理-憑證與-cicd-的一些筆記">iOS Certificates, Identifiers &amp; Profiles 是什麼及 <strong>Fastlane Match 統一</strong> 管理 <strong>憑證與 CI/CD 的一些筆記</strong></h3>

<p>介紹 Certificates, Identifiers &amp; Profiles 之間的關係與使用 Fastlane Match 統一管理簽發憑證並整合進 CI/CD 工作流程的紀錄。</p>

<p><img src="/assets/823ac523ccc8/1*-OS8G9xu0CFpnlAQRgnGAg.webp" alt="Photo by marcos mayer" loading="lazy" decoding="async" width="1200" height="800" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjgwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>Photo by <a href="https://unsplash.com/@mmayyer?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" target="_blank">marcos mayer</a></p>
<h4 id="前言">前言</h4>

<p>2025 年中時寫過一系列從 0 到 1 使用 GitHub Actions 建置 App CI/CD 完整流程的文章：</p>
<ul>
  <li><a href="/posts/zrealm-dev/ci-cd-是什麼-打造高效穩定開發團隊的關鍵流程與工具選擇-c008a9e8ceca/">CI/CD 實戰指南（一）：CI/CD 是什麼？如何透過 CI/CD 打造穩定高效的開發團隊？工具選擇？</a></li>
  <li><a href="/posts/zrealm-dev/github-actions-self-hosted-runner-詳解與實戰案例-ci-cd-自動化工作流程建置-404bd5c70040/">CI/CD 實戰指南（二）：GitHub Actions 與 self-hosted Runner 使用與建置大全</a></li>
  <li><a href="/posts/zrealm-dev/github-actions-ios-ci-cd-完整實作自動化建置-測試與部署流程教學-4b001d2e8440/">CI/CD 實戰指南（三）：使用 GitHub Actions 實作 App 專案的 CI 與 CD 工作流程</a></li>
  <li><a href="/posts/zrealm-dev/ci-cd-打包工具平台-使用-google-apps-script-web-app-串接-github-actions-提升跨團隊效率-4273e57e7148/">CI/CD 實戰指南（四）：使用 Google Apps Script Web App 串接 GitHub Actions 建置免費易用的打包工具平台</a></li>
</ul>

<p>最近到新環境又重新跑了一次，每次都有學習到新的東西，這次聚焦在 iOS Codesigning：Certificates / Profiles / Devices 的關係，與我如何用 Fastlane Match 把憑證管理與 CI/CD 串起來。</p>
<h3 id="certificates-identifiers--profiles-devices-是什麼"><a href="https://developer.apple.com/account/resources/certificates/list" target="_blank">Certificates, Identifiers &amp; Profiles, Devices 是什麼？</a></h3>

<p>在 Apple 生態系中，App 開發是基於憑證與描述檔控管的，跟 Android 只要有 .apk 就能安裝使用不同；Apple 有嚴格的憑證對應管制，不符合規則就不能安裝、使用。</p>
<h4 id="主要項目組成及功能"><strong>主要項目組成及功能：</strong></h4>

<p><img src="/assets/823ac523ccc8/1*PyM0l-jXXcVBKubOwCiTsw.webp" alt="" loading="lazy" decoding="async" width="1200" height="439" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjQzOSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<h4 id="certificates-簽署-app-的身分-signing-identity"><strong>Certificates:</strong> 簽署 App 的身分 (Signing Identity)</h4>
<ul>
  <li><strong>Development</strong> — 開發階段跑在實體機上使用(只跑模擬器不用憑證)，所屬是人或 API Key (有數量限制)。</li>
  <li><strong>Distribution</strong> — 打包上 AppStore, TestFlight 或是 Ad Hoc 內測(限定已註冊裝置)使用，有數量限制，所屬是 Team。</li>
  <li><strong>Enterprise</strong> — 企業內部 App 使用。</li>
</ul>

<blockquote>
  <p><strong><em>格式</em></strong> <em>：需要有 <code class="language-plaintext highlighter-rouge">Private Key (私鑰)</code> 加 <code class="language-plaintext highlighter-rouge">Certificate .cer (公鑰憑證)</code> 才能使用，或是在原創建電腦上的 Keychain 匯出成 <code class="language-plaintext highlighter-rouge">.p12</code> 檔案就會同時包含。</em></p>
</blockquote>

<p><img src="/assets/823ac523ccc8/1*oP_p2kJ1Prqku4D8LIZ7yg.webp" alt="" loading="lazy" decoding="async" width="371" height="115" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzNzEiIGhlaWdodD0iMTE1Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<h4 id="identifiers-哪一個-appextension-bundle-id"><strong>Identifiers: 哪一個 App/Extension (Bundle ID)</strong></h4>

<p>App 的 Bundle ID 註冊跟需要啟用的 Capabilities(例如啟用 Push Notifications, App Groups…)、App Services。</p>

<p>Extension 也會有自己的 Identifier。</p>
<h4 id="devices-已註冊裝置-iphoneipad"><strong>Devices: 已註冊裝置 (iPhone/iPad..)</strong></h4>

<p>Development 跑在實體機上跟 Distribution Ad Hoc 內測只有註冊的裝置才能使用。</p>

<blockquote>
  <p><strong><em>上限：100 個；包含取消，要等到每年付費週期才會重新刷新釋出額度。</em></strong></p>
</blockquote>

<h4 id="provisioning-profile-以下簡稱-profile-描述檔"><strong>Provisioning Profile (以下簡稱 Profile): 描述檔</strong></h4>

<p>組合 Certificates+Identifiers+Devices 關係。</p>
<ul>
  <li><strong>Development</strong> — 開發階段使用的描述檔，如果要跑實體機測試就必須使用，描述檔包含 Certificates+Identifiers+Devices 之間的關係。</li>
  <li><strong>Ad Hoc</strong> —打包給內部測試使用(e.g. Deploy to Firebase App Distribution)的描述檔，描述檔包含 Certificates+Identifiers+Devices 之間的關係。</li>
  <li><strong>App Store</strong> — 打包上傳到 App Store / TestFlight 使用的描述檔，描述檔包含 Certificates+Identifiers 之間的關係。</li>
</ul>

<blockquote>
  <p><em>格式： <code class="language-plaintext highlighter-rouge">.mobileprovision</code></em></p>
</blockquote>

<blockquote>
  <p><em><strong>要注意：Profile 只是描述檔，描述關係，不會包含憑證本體。</strong></em></p>
</blockquote>

<h4 id="小結">小結</h4>

<p>綜合以上，如果要在一台乾淨的機器、不登入 Xcode Apple Account 的情況下要能 Build 實體機 (Development Certificate) 或是執行 Archive 打包 (Distribution Certificate)， <strong>必須要有兩個檔案</strong> ：</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">.mobileprovision</code> Provisioning Profile 描述檔: 描述Certificates+Identifiers+Devices 關係。</li>
  <li><code class="language-plaintext highlighter-rouge">.p12</code> Certificate: 憑證的實體 (從原創建 Certificate 的電腦匯出)。</li>
</ul>

<p><strong>另外 macOS ≥ 15 後的 Keychain 移動到：</strong></p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>open /System/Library/CoreServices/Applications/Keychain<span class="se">\ </span>Access.app
</code></pre></div></div>

<p><em>找了超久..</em></p>
<h4 id="常見錯誤">常見錯誤</h4>

<p><strong>⚠️⚠️⚠️ 確定憑證、Profile 都正確但怎麼樣都報錯:</strong></p>

<blockquote>
  <p><em>多半是你還殘存有之前的憑證、多張憑證，Xcode 會錯亂。</em></p>
</blockquote>

<blockquote>
  <p><em><strong>這問題很常會遇到！</strong></em></p>
</blockquote>

<ol>
  <li>關閉 Xcode</li>
  <li>打開 macOS keychain: <code class="language-plaintext highlighter-rouge">open /System/Library/CoreServices/Applications/Keychain\ Access.app</code></li>
  <li>login keychain -&gt; All items -&gt; 搜尋 <code class="language-plaintext highlighter-rouge">apple development</code> -&gt; 刪除所有 Certificates 憑證</li>
  <li>Finder -&gt; Go -&gt; Go to -&gt; <code class="language-plaintext highlighter-rouge">~/Library/MobileDevice/Provisioning\ Profiles</code> -&gt; 刪除所有 Profiles 描述檔</li>
  <li>重新拉取憑證</li>
  <li>重開 Xcode 應該就正常了</li>
</ol>

<p><strong>No signing certificate “iOS Distribution” found / No signing certificate “iOS Development” found.:</strong></p>
<pre><code class="language-vbnet">No "iOS Distribution" signing certificate matching team ID "" with a private key was found.
No "iOS Development" signing certificate matching team ID "" with a private key was found.
</code></pre>

<p><strong>原因:</strong></p>
<ul>
  <li>缺少 iOS Distribution/iOS Development Certificate 憑證</li>
  <li>有 iOS Distribution/iOS Development Certificate 憑證 <strong>但是 沒有對應的 Private Key 私鑰</strong></li>
</ul>

<p><strong>解決辦法:</strong></p>
<ul>
  <li>刪除 Keychain 裡的所有舊 Certificates 憑證、Profiles 描述檔</li>
  <li>在當初創建 Certificate 的電腦 Keychain 上找到該憑證，匯出成 <code class="language-plaintext highlighter-rouge">.p12</code> 格式，在有問題電腦上安裝。</li>
  <li>到 <a href="https://developer.apple.com/account/resources/certificates/list" target="_blank">Certificates, Identifiers &amp; Profiles</a> Revoke 舊的 Certificate，重新產生
(放心，不影響線上版 App；只影響開發與打包階段)</li>
</ul>

<p><strong>Provisioning profile “” doesn’t include signing certificate “Apple Development: XXX”. / Provisioning profile “” doesn’t include signing certificate “Apple Distribution: XXX”.:</strong></p>

<p><strong>原因:</strong></p>
<ul>
  <li>目前選擇的 Provisioning profile 描述檔跟當前的 Certificate 沒有對應關係</li>
</ul>

<p><strong>解決辦法:</strong></p>
<ul>
  <li>刪除 Keychain 裡的所有舊 Certificates 憑證、Profiles 描述檔</li>
  <li>到 <a href="https://developer.apple.com/account/resources/certificates/list" target="_blank">Certificates, Identifiers &amp; Profiles</a> Profiles 確認描述檔有勾選對應憑證或重產 Profile 使用</li>
</ul>

<p><strong>No profile for team ‘’ matching ‘’ found: Xcode couldn’t find any provisioning profiles matching ‘’.:</strong></p>

<p><strong>原因:</strong></p>
<ul>
  <li>找不到指定的 Provisioning profile 描述檔</li>
</ul>

<p><strong>解決辦法:</strong></p>
<ul>
  <li>刪除 Keychain 裡的所有舊 Certificates 憑證、Profiles 描述檔</li>
  <li>到 <a href="https://developer.apple.com/account/resources/certificates/list" target="_blank">Certificates, Identifiers &amp; Profiles</a> Profiles 下載對應的 Profile 使用</li>
</ul>

<p><strong>Provisioning profile “” has app ID “”, which does not match the bundle ID “”. / Provisioning profile doesn’t match the bundle identifier:</strong></p>

<p><strong>原因:</strong></p>
<ul>
  <li>Provisioning profile 不包含目前的 Bundle Identifier</li>
</ul>

<p><strong>解決辦法:</strong></p>
<ul>
  <li>刪除 Keychain 裡的所有舊 Certificates 憑證、Profiles 描述檔</li>
  <li>到 <a href="https://developer.apple.com/account/resources/certificates/list" target="_blank">Certificates, Identifiers &amp; Profiles</a> Identifiers 確認 Bundle Identifier 已註冊</li>
  <li>到 <a href="https://developer.apple.com/account/resources/certificates/list" target="_blank">Certificates, Identifiers &amp; Profiles</a> Profiles 下載對應的 Profile 使用</li>
</ul>

<p><strong>Provisioning profile “” doesn’t include the currently selected device “” (identifier ).:</strong></p>

<p><strong>原因:</strong></p>
<ul>
  <li>Provisioning profile 不包含選擇的實體機 Device Identifier</li>
</ul>

<p><strong>解決辦法:</strong></p>
<ul>
  <li>到 <a href="https://developer.apple.com/account/resources/certificates/list" target="_blank">Certificates, Identifiers &amp; Profiles</a> Devices 確認實體機的 Identifier 已註冊</li>
  <li>到 <a href="https://developer.apple.com/account/resources/certificates/list" target="_blank">Certificates, Identifiers &amp; Profiles</a> Profiles 對應的 Profile 該 實體機 有勾選啟用。</li>
  <li>重新下載 Profile 使用</li>
</ul>

<p><strong>Could not create another Development/Distribution certificate, reached the maximum number of available Development/Distribution certificates. :</strong></p>

<p><strong>原因:</strong></p>

<p>代表建立的 Development/Distribution Certificate 已達上限數量。</p>

<p><strong>解決辦法:</strong></p>
<ul>
  <li>到 <a href="https://developer.apple.com/account/resources/certificates/list" target="_blank">Certificates, Identifiers &amp; Profiles</a> 刪除不使用的憑證。</li>
</ul>

<p><strong>Xcode 憑證正確且可以正常打包但是用 CLI (Fastlane) 執行打包命令會出現簽名錯誤:</strong></p>

<p><strong>原因:</strong></p>

<p>這邊還有踩到另一個雷，就是我手賤把專案放在 iCloud 同步目錄下，不知道為何Fastlane 會一直出現憑證問題 (疑似 keychain 存取有問題)。</p>

<p><strong>解決辦法:</strong></p>

<p>移出 iCloud 同步目錄即可。</p>
<h3 id="日常使用的問題場景">日常使用的問題場景</h3>
<h4 id="development-certificate">Development Certificate</h4>

<p><img src="/assets/823ac523ccc8/1*uNICvLN0a9gdGAzi4EZ24g.webp" alt="" loading="lazy" decoding="async" width="1693" height="1029" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNjkzIiBoZWlnaHQ9IjEwMjkiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<ul>
  <li>在導入 Match 統一管理憑證之前，每位開發者都會建立自己的 <code class="language-plaintext highlighter-rouge">Development Certificate</code> 和 <code class="language-plaintext highlighter-rouge">Development Profile</code> ；假設組織內有 1,000+ 位開發者，在 <a href="https://developer.apple.com/account/resources/certificates/list" target="_blank">Certificates, Identifiers &amp; Profiles, Devices</a> 後台會非常的混亂可怕。</li>
  <li>如果有外包團隊，只負責開發工作，還是需要把他加入到 Apple Developer Program 後台，讓他產自己的開發憑證跟 Profile。</li>
</ul>

<h4 id="distribution-certificate">Distribution Certificate</h4>

<p><img src="/assets/823ac523ccc8/1*1JKlvoO8d89dlcpz2mJvjg.webp" alt="" loading="lazy" decoding="async" width="1200" height="1105" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjExMDUiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<ul>
  <li>Distribution Certificate 是 by Team 建立，因此每個開發者計劃的 Team 只能建立有限數量的發行憑證。</li>
  <li>常見做法是由一位工程師建立產生 Distribution Certificate 再匯出 .p12 檔案給其他需要上架的開發者或是放到 CI/CD 機器上使用。</li>
  <li>團隊大、App 數量多時要寄來寄去的會很麻煩，而且 <strong>每年都要更新一次</strong></li>
  <li><strong>打包 Ad Hoc 時，當有新裝置註冊；所有人、CI/CD 都需要重新下載 Profile 才能讓新裝置生效。</strong></li>
</ul>

<h3 id="fastlane-match"><a href="https://docs.fastlane.tools/actions/match/" target="_blank">Fastlane Match</a></h3>

<p>基於以上問題我們希望能有一個平台幫我們代為管理所有跟憑證有關的事物，所有開發者跟 CI/CD 服務都是統一對這個平台拉取、更新資料，這個平台的儲存必須安全，這就是 — <a href="https://docs.fastlane.tools/actions/match/" target="_blank">Fastlane Match</a> 。</p>

<blockquote>
  <p><em>Easily sync your certificates and profiles across your team</em></p>
</blockquote>

<blockquote>
  <p><em>A new approach to iOS and macOS code signing: Share one code signing identity across your development team to simplify your codesigning setup and prevent code signing issues.</em></p>
</blockquote>

<blockquote>
  <p><em>match is the implementation of the <a href="https://codesigning.guide/" target="_blank">codesigning.guide concept</a> . match creates all required certificates &amp; provisioning profiles and stores them in a separate git repository, Google Cloud, or Amazon S3. Every team member with access to the selected storage can use those credentials for code signing. match also automatically repairs broken and expired credentials. It’s the easiest way to share signing credentials across teams</em></p>
</blockquote>

<p><img src="/assets/823ac523ccc8/1*UEL7Abx3PFrYbAc2yVlq8A.webp" alt="" loading="lazy" decoding="async" width="1883" height="777" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxODgzIiBoZWlnaHQ9Ijc3NyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><strong>Fastlane Match：</strong></p>
<ul>
  <li>跟 App Store Connect 做交互 (透過 App Store Connect API or Apple Developer Login Session)，產生或是更新憑證</li>
  <li>把憑證結果 ( <code class="language-plaintext highlighter-rouge">.mobileprovision</code> Profiles, <code class="language-plaintext highlighter-rouge">.p12</code> Certificate, <code class="language-plaintext highlighter-rouge">.cer</code> Certificate) 三個檔案加密上傳到 Git Repo (也可以用其他 Storage)
<code class="language-plaintext highlighter-rouge">.cer</code> Certificate 也會獨立存，因為這樣才能知道憑證是否有效</li>
  <li>如果要細分權限可以分兩個 Repos，一個管理 Development 憑證、一個管理 Distribution 憑證。</li>
</ul>

<p><a href="https://docs.fastlane.tools/actions/match/" target="_blank"><strong>Folder structure:</strong></a></p>
<ul>
  <li>The <code class="language-plaintext highlighter-rouge">certs</code> folder contains all certificates with their private keys</li>
  <li>The <code class="language-plaintext highlighter-rouge">profiles</code> folder contains all provisioning profiles</li>
</ul>

<p><img src="/assets/823ac523ccc8/1*mStp0t3Ty3xGTKvbiBGtqw.webp" alt="https://docs.fastlane.tools/actions/match/" loading="lazy" decoding="async" width="683" height="508" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2ODMiIGhlaWdodD0iNTA4Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><a href="https://docs.fastlane.tools/actions/match/" target="_blank">https://docs.fastlane.tools/actions/match/</a></p>

<p>加密算法： <a href="https://github.com/fastlane/fastlane/blob/master/match/lib/match/encryption/encryption.rb" target="_blank">AES-256-GCM</a></p>

<p><strong>開發者、CI/CD 服務：</strong></p>
<ul>
  <li>統一使用 Fastlane match 指令操作憑證。</li>
  <li>Fastlane Match 會優先拉回 Git Repo 的憑證檔案、解密回來使用，如果發現過期或是無法使用，有權限(Create/Write)的情況下會自動重新產生並 Push 回去 Repo；若只有 Read 權限則報錯。</li>
</ul>

<p><strong>Create/Write：</strong></p>
<ul>
  <li>只有負責管理憑證的人可以產生/更新、Push 上傳憑證</li>
  <li><strong>重要的 Distribution Certificate 最好放在獨立 Repo 且只有 CI/CD 服務或管理者可以使用</strong></li>
</ul>

<p><strong>Read:</strong></p>
<ul>
  <li>其他開發者、CI/CD 服務都 <strong>只有讀取</strong> Pull 憑證權限</li>
  <li>CI/CD 服務會在每次執行任務前拉取最新憑證</li>
  <li>所有人共用同一張 Development &amp; Distribution Certificate</li>
  <li>人員異動後會失去 Match Repo 權限，可以 Revoke 舊的憑證重新產生，其他人在重新 Pull 就好(如果有整合到 make project script 那更無痛)</li>
</ul>

<h3 id="fastlane-match-設定與使用">Fastlane Match 設定與使用</h3>

<p>我們以 Git Storage 為例，所有憑證。</p>

<p><strong>1.建立一個空的儲存憑證的 Git Private Match Repo</strong> 
雖然所有 Certificates, Profiles 都會加密儲存，但還是要設定好該 Repo 的存取權限。</p>

<p>2.確認本地有設定好 Git SSH 存取權限，可以使用 <code class="language-plaintext highlighter-rouge">git clone git@github.com:xxx/certificates.git</code></p>

<blockquote>
  <p><em>這邊建議 <strong>統一都使用 SSH Git Clone Repo</strong> ，因為 CI/CD 也會使用相同的方式。</em></p>
</blockquote>

<blockquote>
  <p><em>如果執行 fastlane match 一直卡在 <code class="language-plaintext highlighter-rouge">If cloning the repo takes too long, you can use the </code>clone_branch_directly` option in match.` 多半就是遇到 SSH 權限問題。</em></p>
</blockquote>

<p>3. 在專案目錄執行 <code class="language-plaintext highlighter-rouge">bundle exec fastlane match init</code> 完成設定</p>
<div class="language-lua highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">[</span><span class="mi">21</span><span class="p">:</span><span class="mi">54</span><span class="p">:</span><span class="mi">32</span><span class="p">]:</span> <span class="n">fastlane</span> <span class="n">match</span> <span class="n">supports</span> <span class="n">multiple</span> <span class="n">storage</span> <span class="n">modes</span><span class="p">,</span> <span class="n">please</span> <span class="nb">select</span> <span class="n">the</span> <span class="n">one</span> <span class="n">you</span> <span class="n">want</span> <span class="n">to</span> <span class="n">use</span><span class="p">:</span>
<span class="mi">1</span><span class="p">.</span> <span class="n">git</span>
<span class="mi">2</span><span class="p">.</span> <span class="n">google_cloud</span>
<span class="mi">3</span><span class="p">.</span> <span class="n">s3</span>
<span class="mi">4</span><span class="p">.</span> <span class="n">gitlab_secure_files</span>
<span class="o">#</span> <span class="err">輸入</span> <span class="mi">1</span> <span class="err">用</span> <span class="n">git</span>
<span class="err">?</span>   <span class="mi">1</span>

<span class="p">[</span><span class="mi">22</span><span class="p">:</span><span class="mi">04</span><span class="p">:</span><span class="mi">40</span><span class="p">]:</span> <span class="n">Please</span> <span class="n">create</span> <span class="n">a</span> <span class="n">new</span><span class="p">,</span> <span class="n">private</span> <span class="n">git</span> <span class="n">repository</span> <span class="n">to</span> <span class="n">store</span> <span class="n">the</span> <span class="n">certificates</span> <span class="ow">and</span> <span class="n">profiles</span> <span class="n">there</span>
<span class="p">[</span><span class="mi">22</span><span class="p">:</span><span class="mi">04</span><span class="p">:</span><span class="mi">40</span><span class="p">]:</span> <span class="n">URL</span> <span class="n">of</span> <span class="n">the</span> <span class="n">Git</span> <span class="n">Repo</span><span class="p">:</span> <span class="n">git</span><span class="err">@</span><span class="n">github</span><span class="p">.</span><span class="n">com</span><span class="p">:</span><span class="n">xxx</span><span class="o">/</span><span class="n">certificates</span><span class="p">.</span><span class="n">git</span>
<span class="o">#</span> <span class="err">輸入你建立的</span> <span class="n">Git</span> <span class="n">Private</span> <span class="n">Match</span> <span class="n">Repo</span> <span class="n">SSH</span> <span class="n">URL</span>

<span class="p">[</span><span class="mi">22</span><span class="p">:</span><span class="mi">04</span><span class="p">:</span><span class="mi">47</span><span class="p">]:</span> <span class="n">Successfully</span> <span class="n">created</span> <span class="s1">'./fastlane/Matchfile'</span><span class="p">.</span> <span class="n">You</span> <span class="n">can</span> <span class="n">open</span> <span class="n">the</span> <span class="n">file</span> <span class="n">using</span> <span class="n">a</span> <span class="n">code</span> <span class="n">editor</span><span class="p">.</span>
<span class="p">[</span><span class="mi">22</span><span class="p">:</span><span class="mi">04</span><span class="p">:</span><span class="mi">47</span><span class="p">]:</span> <span class="n">You</span> <span class="n">can</span> <span class="n">now</span> <span class="n">run</span> <span class="err">`</span><span class="n">fastlane</span> <span class="n">match</span> <span class="n">development</span><span class="err">`</span><span class="p">,</span> <span class="err">`</span><span class="n">fastlane</span> <span class="n">match</span> <span class="n">adhoc</span><span class="err">`</span><span class="p">,</span> <span class="err">`</span><span class="n">fastlane</span> <span class="n">match</span> <span class="n">enterprise</span><span class="err">`</span> <span class="ow">and</span> <span class="err">`</span><span class="n">fastlane</span> <span class="n">match</span> <span class="n">appstore</span><span class="err">`</span>
<span class="p">[</span><span class="mi">22</span><span class="p">:</span><span class="mi">04</span><span class="p">:</span><span class="mi">47</span><span class="p">]:</span> <span class="n">On</span> <span class="n">the</span> <span class="n">first</span> <span class="n">run</span> <span class="k">for</span> <span class="n">each</span> <span class="n">environment</span> <span class="n">it</span> <span class="n">will</span> <span class="n">create</span> <span class="n">the</span> <span class="n">provisioning</span> <span class="n">profiles</span> <span class="ow">and</span>
<span class="p">[</span><span class="mi">22</span><span class="p">:</span><span class="mi">04</span><span class="p">:</span><span class="mi">47</span><span class="p">]:</span> <span class="n">certificates</span> <span class="k">for</span> <span class="n">you</span><span class="p">.</span> <span class="n">From</span> <span class="k">then</span> <span class="n">on</span><span class="p">,</span> <span class="n">it</span> <span class="n">will</span> <span class="n">automatically</span> <span class="n">import</span> <span class="n">the</span> <span class="n">existing</span> <span class="n">profiles</span><span class="p">.</span>
<span class="p">[</span><span class="mi">22</span><span class="p">:</span><span class="mi">04</span><span class="p">:</span><span class="mi">47</span><span class="p">]:</span> <span class="n">For</span> <span class="n">more</span> <span class="n">information</span> <span class="n">visit</span> <span class="n">https</span><span class="p">:</span><span class="o">//</span><span class="n">docs</span><span class="p">.</span><span class="n">fastlane</span><span class="p">.</span><span class="n">tools</span><span class="o">/</span><span class="n">actions</span><span class="o">/</span><span class="n">match</span><span class="o">/</span>
</code></pre></div></div>

<p>4.完成後會產生一個 <strong><code class="language-plaintext highlighter-rouge">fastlane/Matchfile</code> :</strong></p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 你的 Git Private Match Repo SSH URL</span>
git_url<span class="o">(</span><span class="s2">"git@github.com:xxxx/certificates.git"</span><span class="o">)</span>

storage_mode<span class="o">(</span><span class="s2">"git"</span><span class="o">)</span>

<span class="nb">type</span><span class="o">(</span><span class="s2">"development"</span><span class="o">)</span> <span class="c"># The default type, can be: appstore, adhoc, enterprise or development</span>

<span class="c"># app_identifier(["tools.fastlane.app", "tools.fastlane.app2"])</span>
<span class="c"># username("user@fastlane.tools") # Your Apple Developer Portal username</span>

<span class="c"># For all available options run `fastlane match --help`</span>
<span class="c"># Remove the # in the beginning of the line to enable the other options</span>

<span class="c"># The docs are available on https://docs.fastlane.tools/actions/match</span>
</code></pre></div></div>

<p><strong>5. 產生 <a href="https://developer.apple.com/documentation/appstoreconnectapi/creating-api-keys-for-app-store-connect-api" target="_blank">App Store Connect API .p8 Key</a></strong> ， <strong>統一使用 API Key 產生憑證:</strong></p>

<p><img src="/assets/823ac523ccc8/1*RFhS_XtZ3TfIJBAeoE-laA.webp" alt="" loading="lazy" decoding="async" width="1181" height="349" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMTgxIiBoZWlnaHQ9IjM0OSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<blockquote>
  <p><em>⚠️️️️ <a href="https://developer.apple.com/documentation/appstoreconnectapi/creating-api-keys-for-app-store-connect-api" target="_blank"><strong>App Store Connect API .p8 Key</strong></a> <strong>的權限很大</strong> ，除了可以管理憑證、也可以管理 App 的上架、送審，還有後台使用者跟評論、財務數據報表。</em></p>
</blockquote>

<blockquote>
  <p><em>要不要進到團隊的 .git 讓大家都能存取，可依照團隊情境自行決定。</em></p>
</blockquote>

<blockquote>
  <p><em><strong>比較高風控的方式是只有負責管理的人跟加密儲存在 CI/CD 上使用(文後會介紹)。</strong></em></p>
</blockquote>

<blockquote>
  <p><strong><em>這邊為了 Demo 方便直接放進 fastlane 目錄下存取。</em></strong></p>
</blockquote>

<blockquote>
  <p><strong><em>另外本篇的 Fastlane Script 為了方便展示，不考慮重複程式碼與程式結構。</em></strong></p>
</blockquote>

<h4 id="憑證管理">憑證管理</h4>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>platform :ios <span class="k">do
  </span>lane :match_development <span class="k">do</span> |options|
    <span class="c"># 換成你的 App Identifier ID</span>
    app_identifier <span class="o">=</span> <span class="s2">"li.zhgchg.myApp"</span>

    <span class="nb">type</span> <span class="o">=</span> options.fetch<span class="o">(</span>:type, <span class="s2">"development"</span><span class="o">)</span>
    isRead <span class="o">=</span> options.fetch<span class="o">(</span>:isRead, <span class="nb">true</span><span class="o">)</span>
    <span class="k">if </span>isRead 
      <span class="nb">readonly</span> <span class="o">=</span> <span class="nb">true
      </span>force <span class="o">=</span> <span class="nb">false
    </span><span class="k">else
      </span><span class="nb">readonly</span> <span class="o">=</span> <span class="nb">false
      </span>force <span class="o">=</span> <span class="nb">true</span>

      <span class="c"># 需要 App Store Connect API Key 才有權限去 Apple 後台管理憑證</span>
      <span class="c"># 假設 App Store Connect API .p8 Key 在 ./fastlane/ 目錄內</span>
      app_store_connect_api_key<span class="o">(</span>
        key_id: <span class="s2">"XXXXXX"</span>,
        issuer_id: <span class="s2">"XXXXXX-XXXXXX-XXXXXX-XXXXXX-XXXXXX"</span>,
        key_filepath: <span class="s2">"./fastlane/AuthKey_XXXXXX.p8"</span>,
      <span class="o">)</span>
    end

    <span class="c"># 拉取 app_identifier 的 Development 憑證</span>
    match<span class="o">(</span>
      <span class="nb">type</span>: <span class="nb">type</span>,
      app_identifier: app_identifier, 
      <span class="nb">readonly</span>: <span class="nb">readonly</span>, <span class="c"># 能否從有需要時，更新、上傳 cert / profile</span>
      force: force <span class="c"># 要不要無條件重建 provisioning profile</span>
    <span class="o">)</span>
  end
end
</code></pre></div></div>

<p><strong>建立 Development Certificate &amp; Profile 並上傳到 Match Repo:</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bundle exec fastlane match_development type:development isRead:false
[22:22:38]: Creating new provisioning profile for 'li.zhgchg.myApp' with name 'match Development li.zhgchg.myApp' for 'ios' platform
[22:22:39]: Downloading provisioning profile...
[22:22:39]: Successfully downloaded provisioning profile...
[22:22:39]: Installing provisioning profile...
/var/folders/pk/978f3gws7ml0bkmrw245cg_c0000gn/T/d20260103-5010-v6r6w2/profiles/development/Development_li.zhgchg.myApp.mobileprovision
[22:22:39]: Installing provisioning profile...
[22:22:39]: 🔒  Successfully encrypted certificates repo
[22:22:39]: Pushing changes to remote git repo...
[22:22:42]: Finished uploading files to Git Repo [git@github.com:xxxx/certificates.git]
</code></pre></div></div>
<ul>
  <li>沒有錯誤就代表 Development Certificate &amp; Profile 產生、安裝成功並同步 Push 上傳到 Match Repo 成功。</li>
</ul>

<p><strong>首次建立會需要你設定 <code class="language-plaintext highlighter-rouge">Passphrase</code> ：</strong></p>
<div class="language-markdown highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">[</span><span class="ss">23:29:12</span><span class="p">]:</span> <span class="sx">Enter</span> the passphrase that should be used to encrypt/decrypt your certificates
<span class="p">[</span><span class="ss">23:29:12</span><span class="p">]:</span> <span class="sx">This</span> passphrase is specific per repository and will be stored in your local keychain
<span class="p">[</span><span class="ss">23:29:12</span><span class="p">]:</span> <span class="sx">Make</span> sure to remember the password, as you'll need it when you run match on a different machine
<span class="p">[</span><span class="ss">23:29:12</span><span class="p">]:</span> <span class="sx">Passphrase</span> for Match storage:
</code></pre></div></div>
<ul>
  <li>這個值就是用來加密你的所有 Match Repo 上的檔案的會需要用到的參考 (passphrase + random salt)</li>
  <li>建議產生一個 <a href="https://www.random.org/strings/?num=1&amp;len=32&amp;digits=on&amp;upperalpha=on&amp;loweralpha=on&amp;unique=on&amp;format=html&amp;rnd=new" target="_blank">隨機字串</a> 設定並記錄下來</li>
  <li>日後更新或是其他人拉取 Match Repo 憑證都需要輸入這個 字串 解密回原始檔案。</li>
</ul>

<p><strong>團隊其他成員統一從 Match Repo Pull 拉取 Development Certificate &amp; Profile:</strong></p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bundle <span class="nb">exec </span>fastlane match_development <span class="nb">type</span>:development
</code></pre></div></div>

<p><strong>首次使用會詢問你的登入密碼 (login keychain)，因為要把憑證安裝到 keychain 中，輸入兩次確認即可:</strong></p>
<div class="language-markdown highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">[</span><span class="ss">16:52:59</span><span class="p">]:</span> <span class="sx">Installing</span> certificate...
<span class="p">[</span><span class="ss">16:53:00</span><span class="p">]:</span> <span class="sx">There</span> are no local code signing identities found.
You can run security find-identity -v -p codesigning to get this output.
This Stack Overflow thread has more information: https://stackoverflow.com/q/35390072/774.
(Check in Keychain Access for an expired WWDR certificate: https://stackoverflow.com/a/35409835/774 has more info.)
<span class="p">[</span><span class="ss">16:53:00</span><span class="p">]:</span> <span class="sx">Enter</span> the password for /Users/zhgchgli/Library/Keychains/login.keychain-db
<span class="p">[</span><span class="ss">16:53:00</span><span class="p">]:</span> <span class="sx">This</span> passphrase will be stored in your local keychain with the name fastlane_keychain_login and used in future runs
<span class="p">[</span><span class="ss">16:53:00</span><span class="p">]:</span> <span class="sx">This</span> prompt can be avoided by specifying the 'keychain_password' option or 'MATCH_KEYCHAIN_PASSWORD' environment variable
<span class="p">[</span><span class="ss">16:53:00</span><span class="p">]:</span> <span class="sx">Password</span> for login keychain: <span class="ge">********</span>
<span class="p">[</span><span class="ss">16:53:24</span><span class="p">]:</span> <span class="sx">Type</span> password for login keychain again: <span class="ge">********</span>
</code></pre></div></div>

<p><strong>如果 Match Development 拉完憑證，但 Xcode 一直顯示有錯誤或無效:</strong></p>

<p>可參考前文提到的常見錯誤，多半是有舊的髒憑證，清除所有憑證再重新拉取，應該就可以了。</p>

<p>—</p>

<p><strong>建立 AdHoc Distribution Certificate &amp; Profile 並上傳到 Match Repo:</strong></p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bundle <span class="nb">exec </span>fastlane match_development <span class="nb">type</span>:adhoc isRead:false
</code></pre></div></div>

<p><strong>建立 AppStore Distribution Certificate &amp; Profile 並上傳到 Match Repo:</strong></p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bundle <span class="nb">exec </span>fastlane match_development <span class="nb">type</span>:appstore isRead:false
</code></pre></div></div>

<p><strong>CI/CD 服務統一從 Match Repo Pull 拉取 Distribution Certificate &amp; Profile ( <code class="language-plaintext highlighter-rouge">isRead:true</code> )，然後再執行打包發佈任務。</strong></p>
<h4 id="註冊新裝置">註冊新裝置</h4>
<div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">platform</span> <span class="o">:</span><span class="n">ios</span> <span class="k">do</span>  
  <span class="n">desc</span> <span class="s2">"Register a new device and refresh profiles"</span>
  <span class="n">lane</span> <span class="o">:</span><span class="n">registerDevice</span> <span class="k">do</span> <span class="o">|</span><span class="n">options</span><span class="o">|</span>
    <span class="c1"># 換成你的 App Identifier ID</span>
    <span class="n">app_identifier</span> <span class="o">=</span> <span class="s2">"li.zhgchg.myApp"</span>

    <span class="c1"># 需要 App Store Connect API Key 才有權限去 Apple 後台管理憑證</span>
    <span class="c1"># 假設 App Store Connect API .p8 Key 在 ./fastlane/ 目錄內</span>
    <span class="nf">app_store_connect_api_key</span><span class="p">(</span>
      <span class="n">key_id</span><span class="o">:</span> <span class="s2">"XXXXXX"</span><span class="p">,</span>
      <span class="n">issuer_id</span><span class="o">:</span> <span class="s2">"XXXXXX-XXXXXX-XXXXXX-XXXXXX-XXXXXX"</span><span class="p">,</span>
      <span class="n">key_filepath</span><span class="o">:</span> <span class="s2">"./fastlane/AuthKey_XXXXXX.p8"</span><span class="p">,</span>
    <span class="p">)</span>
    <span class="c1"># Input: UDID and device name</span>
    <span class="n">udid</span> <span class="o">=</span> <span class="n">options</span><span class="p">[</span><span class="o">:</span><span class="n">udid</span><span class="p">]</span> <span class="o">||</span> <span class="no">UI</span><span class="mf">.</span><span class="nf">input</span><span class="p">(</span><span class="s2">"Enter device UDID:"</span><span class="p">)</span>
    <span class="n">device_name</span> <span class="o">=</span> <span class="n">options</span><span class="p">[</span><span class="o">:</span><span class="n">name</span><span class="p">]</span> <span class="o">||</span> <span class="no">UI</span><span class="mf">.</span><span class="nf">input</span><span class="p">(</span><span class="s2">"Enter device name:"</span><span class="p">)</span>
    <span class="no">UI</span><span class="mf">.</span><span class="nf">message</span><span class="p">(</span><span class="s2">"📱 Registering device #</span><span class="si">{</span><span class="nv">device_name</span><span class="si">}</span><span class="s2"> (#</span><span class="si">{</span><span class="nv">udid</span><span class="si">}</span><span class="s2">)"</span><span class="p">)</span>
    <span class="nf">register_device</span><span class="p">(</span>
      <span class="n">name</span><span class="o">:</span> <span class="n">device_name</span><span class="p">,</span>
      <span class="n">udid</span><span class="o">:</span> <span class="n">udid</span><span class="p">,</span>
      <span class="n">platform</span><span class="o">:</span> <span class="s1">'ios'</span>
    <span class="p">)</span>

    <span class="c1"># 更新 app_identifier 的 Development 憑證</span>
    <span class="k">match</span><span class="p">(</span>
      <span class="n">type</span><span class="o">:</span> <span class="s2">"development"</span><span class="p">,</span>
      <span class="n">app_identifier</span><span class="o">:</span> <span class="n">app_identifier</span><span class="p">,</span> 
      <span class="k">readonly</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span> <span class="c1"># 如果有需要，更新、上傳 cert / profile</span>
      <span class="n">force_for_new_devices</span><span class="o">:</span> <span class="kc">true</span> <span class="c1"># 如果為新裝置則重建 provisioning profile</span>
    <span class="p">)</span>

    <span class="c1"># 更新 app_identifier 的 AdHoc 憑證</span>
    <span class="k">match</span><span class="p">(</span>
      <span class="n">type</span><span class="o">:</span> <span class="s2">"adhoc"</span><span class="p">,</span>
      <span class="n">app_identifier</span><span class="o">:</span> <span class="n">app_identifier</span><span class="p">,</span> 
      <span class="k">readonly</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span> <span class="c1"># 如果有需要，更新、上傳 cert / profile</span>
      <span class="n">force_for_new_devices</span><span class="o">:</span> <span class="kc">true</span> <span class="c1"># 如果為新裝置則重建 provisioning profile</span>
    <span class="p">)</span>
  <span class="n">end</span>
<span class="n">end</span>
</code></pre></div></div>

<p>註冊完之後其他開發者或 CI/CD 服務再從 Match Repo 拉 Profile (Development or AdHoc) 就會包含新的裝置了。</p>
<h3 id="fastlane-match-x-cicd-工作流程整合">Fastlane Match x CI/CD 工作流程整合</h3>

<p>大致介紹完 Fastlane Match 怎麼產生 Push / 拉取 Pull 憑證後，再來要講怎麼整合進 CI/CD 流程。</p>
<h4 id="問題1--怎麼-clone-private-match-repo">問題1 — 怎麼 Clone Private Match Repo</h4>

<p>第一個最常見的問題就是在 CI/CD 上怎麼 Clone Private Match Repo 專案，在本機開發上因為我們統一都使用 ssh git clone 並且是用我們自己帳號的 ssh key 所以不會遇到問題；但是 CI/CD 上沒有這個 key，當然也可以用個人的 key 但是很不保險。</p>

<p><strong>GitHub — Repo Deploy Key:</strong></p>

<p>1.我們先在本地產生 private/public key: <code class="language-plaintext highlighter-rouge">ssh-keygen -t rsa -b 4096 -f ./id_rsa</code> ( <strong>不要輸入 <code class="language-plaintext highlighter-rouge">passphrase</code></strong> )</p>

<p>2.到 <strong>Match Private Repo</strong> -&gt; Settings -&gt; Security -&gt; Deploy keys -&gt; Add deploy key:</p>

<p><img src="/assets/823ac523ccc8/1*nzo-oPRi7DDxPvtJIWBspw.webp" alt="" loading="lazy" decoding="async" width="1149" height="774" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMTQ5IiBoZWlnaHQ9Ijc3NCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>3. 用文字編輯器打開「 <code class="language-plaintext highlighter-rouge">id_rsa.pub</code> 」複製內容貼上到 Key -&gt;「Add key」</p>

<p><img src="/assets/823ac523ccc8/1*mbwtijzM-2iAzfgFiT32rw.webp" alt="" loading="lazy" decoding="async" width="1496" height="780" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDk2IiBoZWlnaHQ9Ijc4MCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>5. 回到主要 Repo -&gt; Settings -&gt; Security -&gt; Secrets and variables</p>

<p><img src="/assets/823ac523ccc8/1*jvY8Yg6tI85LNKHgIaHVVQ.webp" alt="" loading="lazy" decoding="async" width="1200" height="869" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg2OSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>5. 新增 SSH Private Key Content 到 Secret:</p>

<p><img src="/assets/823ac523ccc8/1*8WiPfOz21rJWD6yiMwP3aw.webp" alt="" loading="lazy" decoding="async" width="1200" height="689" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjY4OSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<ul>
  <li>Name: <code class="language-plaintext highlighter-rouge">MATCH_REPO_DEPLOY_PRIVATE_KEY</code></li>
</ul>

<p>6. 回到主要 Repo 的 GitHub Actions 設定 SSH Key:</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">name</span><span class="pi">:</span> <span class="s">CI - Deploy</span>

<span class="na">on</span><span class="pi">:</span>
  <span class="na">push</span><span class="pi">:</span>
    <span class="na">branches</span><span class="pi">:</span> <span class="pi">[</span> <span class="nv">main</span> <span class="pi">]</span>
  <span class="na">pull_request</span><span class="pi">:</span>

<span class="na">jobs</span><span class="pi">:</span>
  <span class="na">build</span><span class="pi">:</span>
    <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
    <span class="na">steps</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Checkout current repo (Repo A)</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/checkout@v4</span>

      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Setup SSH for Deploy Key</span>
        <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
          <span class="s">mkdir -p ~/.ssh</span>
          <span class="s">echo "${{ secrets.MATCH_REPO_DEPLOY_PRIVATE_KEY }}" &gt; ~/.ssh/id_ed25519</span>
          <span class="s">chmod 600 ~/.ssh/id_ed25519</span>
          <span class="s">ssh-keyscan github.com &gt;&gt; ~/.ssh/known_hosts</span>

      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Test Clone</span>
        <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
          <span class="s">git clone git@github.com:xxxx/match-certificates.git</span>
<span class="c1"># Success!</span>
<span class="c1"># .. do deploy job...</span>
</code></pre></div></div>

<p>7. 設定成功!</p>
<h4 id="問題2-app-store-connect-api-p8-key-安全存放使用">問題2— App Store Connect API .p8 Key 安全存放使用</h4>

<p>因為 GitHub Actions 不能存檔案，所以只能先存成字串，然後寫入檔案。</p>
<ol>
  <li>同問題 1 步驟，在主要 Repo 新增一個 <code class="language-plaintext highlighter-rouge">APP_STORE_CONNECT_API_KEY_CONTENT</code> Secret</li>
  <li>用文字編輯器打開 <code class="language-plaintext highlighter-rouge">AuthKey_XXXXXX.p8</code> 複製並貼上內容</li>
  <li><strong>主要 Repo 的 GitHub Actions 加上一個步驟讀出內容寫入檔案:</strong></li>
</ol>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">name</span><span class="pi">:</span> <span class="s">CI - Deploy</span>
<span class="na">on</span><span class="pi">:</span>
  <span class="na">push</span><span class="pi">:</span>
    <span class="na">branches</span><span class="pi">:</span> <span class="pi">[</span> <span class="nv">main</span> <span class="pi">]</span>
  <span class="na">pull_request</span><span class="pi">:</span>
<span class="na">jobs</span><span class="pi">:</span>
  <span class="na">build</span><span class="pi">:</span>
    <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
    <span class="na">steps</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Checkout current repo (Repo A)</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/checkout@v4</span>

      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Setup SSH for Deploy Key</span>
        <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
          <span class="s">mkdir -p ~/.ssh</span>
          <span class="s">echo "${{ secrets.MATCH_REPO_DEPLOY_PRIVATE_KEY }}" &gt; ~/.ssh/id_ed25519</span>
          <span class="s">chmod 600 ~/.ssh/id_ed25519</span>
          <span class="s">ssh-keyscan github.com &gt;&gt; ~/.ssh/known_hosts  </span>
      
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Write Secret Key to File</span>
        <span class="na">env</span><span class="pi">:</span>
          <span class="na">APP_STORE_CONNECT_API_KEY_CONTENT</span><span class="pi">:</span> <span class="s">${{ secrets.APP_STORE_CONNECT_API_KEY_CONTENT }}</span>
        <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
          <span class="s"># ensure fastlane directory exists</span>
          <span class="s">mkdir -p ./fastlane</span>
      
          <span class="s"># create file path</span>
          <span class="s">APP_STORE_CONNECT_API_KEY_PATH=./fastlane/AuthKey_XXXXXX.p8</span>
      
          <span class="s"># write content to file (keep newline)</span>
          <span class="s">echo "$APP_STORE_CONNECT_API_KEY_CONTENT" &gt; "$APP_STORE_CONNECT_API_KEY_PATH"</span>
      
          <span class="s"># (optional) restrict permissions</span>
          <span class="s">chmod 600 "$APP_STORE_CONNECT_API_KEY_PATH"</span>

      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Deploy to Firebase</span>
        <span class="na">env</span><span class="pi">:</span>
          <span class="na">MATCH_PASSWORD</span><span class="pi">:</span> <span class="s2">"</span><span class="s">${{</span><span class="nv"> </span><span class="s">secrets.MATCH_PASSWORD</span><span class="nv"> </span><span class="s">}}"</span>
        <span class="na">run</span><span class="pi">:</span> <span class="s">bundle exec fastlane deploy_to_firebase</span>
</code></pre></div></div>
<h4 id="問題3--設定-private-match-repo-的-passphrase防止-match-時跳-prompt-中斷">問題3 — 設定 Private Match Repo 的 Passphrase，防止 Match 時跳 Prompt 中斷</h4>
<ol>
  <li>同問題 1 步驟，在主要 Repo 新增 <code class="language-plaintext highlighter-rouge">MATCH_PASSWORD</code> 到 Secret</li>
  <li>內容輸入你設定的 Fastlane Match Repo <code class="language-plaintext highlighter-rouge">Passphrase</code></li>
  <li><strong>主要 Repo 的 GitHub Actions 加上</strong> <code class="language-plaintext highlighter-rouge">env: secret.MATCH_PASSWORD</code> <strong>:</strong></li>
</ol>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">name</span><span class="pi">:</span> <span class="s">CI - Deploy</span>
<span class="na">on</span><span class="pi">:</span>
  <span class="na">push</span><span class="pi">:</span>
    <span class="na">branches</span><span class="pi">:</span> <span class="pi">[</span> <span class="nv">main</span> <span class="pi">]</span>
  <span class="na">pull_request</span><span class="pi">:</span>
<span class="na">jobs</span><span class="pi">:</span>
  <span class="na">build</span><span class="pi">:</span>
    <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
    <span class="na">steps</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Checkout current repo (Repo A)</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/checkout@v4</span>

      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Setup SSH for Deploy Key</span>
        <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
          <span class="s">mkdir -p ~/.ssh</span>
          <span class="s">echo "${{ secrets.MATCH_REPO_DEPLOY_PRIVATE_KEY }}" &gt; ~/.ssh/id_ed25519</span>
          <span class="s">chmod 600 ~/.ssh/id_ed25519</span>
          <span class="s">ssh-keyscan github.com &gt;&gt; ~/.ssh/known_hosts  </span>
      
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Write Secret Key to File</span>
        <span class="na">env</span><span class="pi">:</span>
          <span class="na">APP_STORE_CONNECT_API_KEY_CONTENT</span><span class="pi">:</span> <span class="s">${{ secrets.APP_STORE_CONNECT_API_KEY_CONTENT }}</span>
        <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
          <span class="s"># ensure fastlane directory exists</span>
          <span class="s">mkdir -p ./fastlane</span>
      
          <span class="s"># create file path</span>
          <span class="s">APP_STORE_CONNECT_API_KEY_PATH=./fastlane/AuthKey_XXXXXX.p8</span>
      
          <span class="s"># write content to file (keep newline)</span>
          <span class="s">echo "$APP_STORE_CONNECT_API_KEY_CONTENT" &gt; "$APP_STORE_CONNECT_API_KEY_PATH"</span>
      
          <span class="s"># (optional) restrict permissions</span>
          <span class="s">chmod 600 "$APP_STORE_CONNECT_API_KEY_PATH"</span>

      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Deploy to Firebase</span>
        <span class="na">env</span><span class="pi">:</span>
          <span class="na">MATCH_PASSWORD</span><span class="pi">:</span> <span class="s2">"</span><span class="s">${{</span><span class="nv"> </span><span class="s">secrets.MATCH_PASSWORD</span><span class="nv"> </span><span class="s">}}"</span>
        <span class="na">run</span><span class="pi">:</span> <span class="s">bundle exec fastlane deploy_to_firebase</span>
</code></pre></div></div>
<h4 id="問題4--self-hosted-runner-上的-keychain-處理">問題4 — Self-hosted Runner 上的 Keychain 處理</h4>

<p>跟雲端機器每次都是乾淨全新的不同，如果是用 Self-hosted Runner 我們可以在 Fastlane 指定 <code class="language-plaintext highlighter-rouge">derived_data_path</code> 、 <code class="language-plaintext highlighter-rouge">output_directory</code> 、 <code class="language-plaintext highlighter-rouge">buildlog_path</code> 、 <code class="language-plaintext highlighter-rouge">reinstall_app</code> 讓每次執行的環境都是乾淨的；但是 Certificates, Profiles 是安裝到系統的 Keychain 應用中，該如何處理？</p>

<p>其實 Fastlane 也幫我們考慮到了，可以在 Match 之前先建立一個乾淨的 Keychain:</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">name</span><span class="pi">:</span> <span class="s">CI - Deploy</span>
<span class="na">on</span><span class="pi">:</span>
  <span class="na">push</span><span class="pi">:</span>
    <span class="na">branches</span><span class="pi">:</span> <span class="pi">[</span> <span class="nv">main</span> <span class="pi">]</span>
  <span class="na">pull_request</span><span class="pi">:</span>
<span class="na">jobs</span><span class="pi">:</span>
  <span class="na">build</span><span class="pi">:</span>
    <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
    <span class="na">steps</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Checkout current repo (Repo A)</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/checkout@v4</span>

      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Setup SSH for Deploy Key</span>
        <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
          <span class="s">mkdir -p ~/.ssh</span>
          <span class="s">echo "${{ secrets.MATCH_REPO_DEPLOY_PRIVATE_KEY }}" &gt; ~/.ssh/id_ed25519</span>
          <span class="s">chmod 600 ~/.ssh/id_ed25519</span>
          <span class="s">ssh-keyscan github.com &gt;&gt; ~/.ssh/known_hosts  </span>
      
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Write Secret Key to File</span>
        <span class="na">env</span><span class="pi">:</span>
          <span class="na">APP_STORE_CONNECT_API_KEY_CONTENT</span><span class="pi">:</span> <span class="s">${{ secrets.APP_STORE_CONNECT_API_KEY_CONTENT }}</span>
        <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
          <span class="s"># ensure fastlane directory exists</span>
          <span class="s">mkdir -p ./fastlane</span>
      
          <span class="s"># create file path</span>
          <span class="s">APP_STORE_CONNECT_API_KEY_PATH=./fastlane/AuthKey_XXXXXX.p8</span>
      
          <span class="s"># write content to file (keep newline)</span>
          <span class="s">echo "$APP_STORE_CONNECT_API_KEY_CONTENT" &gt; "$APP_STORE_CONNECT_API_KEY_PATH"</span>
      
          <span class="s"># (optional) restrict permissions</span>
          <span class="s">chmod 600 "$APP_STORE_CONNECT_API_KEY_PATH"</span>

      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Create fastlane keychain</span>
        <span class="na">env</span><span class="pi">:</span>
          <span class="na">KEYCHAIN_NAME</span><span class="pi">:</span> <span class="s2">"</span><span class="s">${{</span><span class="nv"> </span><span class="s">runner.name</span><span class="nv"> </span><span class="s">}}"</span>
          <span class="na">MATCH_PASSWORD</span><span class="pi">:</span> <span class="s2">"</span><span class="s">${{</span><span class="nv"> </span><span class="s">secrets.MATCH_PASSWORD</span><span class="nv"> </span><span class="s">}}"</span>
        <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
          <span class="s">bundle exec fastlane run create_keychain \</span>
            <span class="s">name:"$KEYCHAIN_NAME" \</span>
            <span class="s">password:"$MATCH_PASSWORD" \</span>
            <span class="s">unlock:true \</span>
            <span class="s">timeout:0 \</span>
            <span class="s">lock_when_sleeps:false</span>
      
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Deploy to Firebase</span>
        <span class="na">env</span><span class="pi">:</span>
          <span class="na">MATCH_PASSWORD</span><span class="pi">:</span> <span class="s2">"</span><span class="s">${{</span><span class="nv"> </span><span class="s">secrets.MATCH_PASSWORD</span><span class="nv"> </span><span class="s">}}"</span>
          <span class="na">KEYCHAIN_NAME</span><span class="pi">:</span> <span class="s2">"</span><span class="s">${{</span><span class="nv"> </span><span class="s">runner.name</span><span class="nv"> </span><span class="s">}}"</span>
        <span class="na">run</span><span class="pi">:</span> <span class="s">bundle exec fastlane deploy_to_firebase</span>

      <span class="c1"># 🔥 不管前面成功或失敗，一定會執行</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Delete fastlane keychain</span>
        <span class="na">if</span><span class="pi">:</span> <span class="s">always()</span>
        <span class="na">env</span><span class="pi">:</span>
          <span class="na">KEYCHAIN_NAME</span><span class="pi">:</span> <span class="s">${{ runner.name }}</span>
        <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
          <span class="s">bundle exec fastlane run delete_keychain \</span>
            <span class="s">name:"$KEYCHAIN_NAME"</span>
</code></pre></div></div>
<ul>
  <li>每個 Runner 同時只會執行一個，因此我們用 Runner Name 當成 Keychain Name，每個 Runner 都會有自己的 Keychain</li>
  <li>執行完畢，不管成功或失敗都會刪除</li>
  <li><code class="language-plaintext highlighter-rouge">keychain_password</code> 我就沒特別另外設定了，統一用 <code class="language-plaintext highlighter-rouge">MATCH_PASSWORD</code></li>
</ul>

<p><code class="language-plaintext highlighter-rouge">Fastlane/Fastfile</code> 裡的 match 方法多加上 keychain parameters:</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#...</span>
    <span class="n">match</span><span class="p">(</span>
      <span class="ss">type: </span><span class="s2">"adhoc"</span><span class="p">,</span>
      <span class="ss">app_identifier: </span><span class="n">app_identifier</span><span class="p">,</span> 
      <span class="ss">readonly: </span><span class="kp">false</span><span class="p">,</span> <span class="c1"># 如果有需要，更新、上傳 cert / profile</span>
      <span class="ss">force_for_new_devices: </span><span class="kp">true</span><span class="p">,</span> <span class="c1"># 如果為新裝置則重建 provisioning profile</span>
      <span class="ss">keychain_name: </span><span class="no">ENV</span><span class="p">[</span><span class="s1">'KEYCHAIN_NAME'</span><span class="p">],</span> <span class="c1"># default value: nil</span>
      <span class="ss">keychain_password: </span><span class="no">ENV</span><span class="p">[</span><span class="s1">'MATCH_PASSWORD'</span><span class="p">]</span> <span class="c1"># default value: nil</span>
    <span class="p">)</span>
<span class="c1">#...</span>
</code></pre></div></div>

<p>這樣 Match 在拉取憑證的時候就會改存到指定的 Keychain，而非共用的 login keychain。</p>
<h3 id="結語">結語</h3>

<p>Fastlane 的文章範圍太大了，這邊就只記錄使用 Fastlane Match 的過程；其他關於 Fastlane 跑測試、打包發佈…等等 Lane 有機會再寫一篇新的補充；其他 Match 相關的問題，有想到什麼案例會再來補充！也歡迎留言提問！</p>
<h3 id="延伸閱讀">延伸閱讀</h3>
<ul>
  <li><a href="/posts/zrealm-dev/ci-cd-是什麼-打造高效穩定開發團隊的關鍵流程與工具選擇-c008a9e8ceca/">CI/CD 實戰指南（一）：CI/CD 是什麼？如何透過 CI/CD 打造穩定高效的開發團隊？工具選擇？</a></li>
  <li><a href="/posts/zrealm-dev/github-actions-self-hosted-runner-詳解與實戰案例-ci-cd-自動化工作流程建置-404bd5c70040/">CI/CD 實戰指南（二）：GitHub Actions 與 self-hosted Runner 使用與建置大全</a></li>
  <li><a href="/posts/zrealm-dev/github-actions-ios-ci-cd-完整實作自動化建置-測試與部署流程教學-4b001d2e8440/">CI/CD 實戰指南（三）：使用 GitHub Actions 實作 App 專案的 CI 與 CD 工作流程</a></li>
  <li><a href="/posts/zrealm-dev/ci-cd-打包工具平台-使用-google-apps-script-web-app-串接-github-actions-提升跨團隊效率-4273e57e7148/">CI/CD 實戰指南（四）：使用 Google Apps Script Web App 串接 GitHub Actions 建置免費易用的打包工具平台</a></li>
</ul>

<p><strong>敬請移步閱覽。🤞🏻</strong></p>]]></content>
  </entry><entry>
    <title type="html">Medium API 爬取資料與突破 Cloudflare 防護｜完整 GraphQL 操作教學</title>
    <link href="https://zhgchg.li/posts/zrealm-dev/medium-api-%E7%88%AC%E5%8F%96%E8%B3%87%E6%96%99%E8%88%87%E7%AA%81%E7%A0%B4-cloudflare-%E9%98%B2%E8%AD%B7-%E5%AE%8C%E6%95%B4-graphql-%E6%93%8D%E4%BD%9C%E6%95%99%E5%AD%B8-88f0fb935120/" rel="alternate" type="text/html" title="Medium API 爬取資料與突破 Cloudflare 防護｜完整 GraphQL 操作教學" />
    <published>2025-12-31T12:36:53+08:00</published>
    <updated>2026-01-02T22:43:48+08:00</updated>
    <id>https://zhgchg.li/posts/zrealm-dev/88f0fb935120</id><summary type="html">針對 Medium 使用者擔心文章無備份、Cloudflare 阻擋爬蟲的痛點，詳解如何透過 Medium API 與 GraphQL 取得追蹤人數、文章列表及內容，並示範用 Cloudflare Worker 成功繞過防護，實現自動備份與資料同步。</summary><author>
      <name>ZhgChgLi</name>
    </author><category term="ZRealm Dev." /><category term="cloudflare" /><category term="medium" /><category term="api" /><category term="graphql" /><category term="medium-api" /><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://zhgchg.li/assets/88f0fb935120/1*Lh9a_XR4zvvsaP3y58x9Aw.webp" /><content type="html" xml:base="https://zhgchg.li/posts/zrealm-dev/medium-api-%E7%88%AC%E5%8F%96%E8%B3%87%E6%96%99%E8%88%87%E7%AA%81%E7%A0%B4-cloudflare-%E9%98%B2%E8%AD%B7-%E5%AE%8C%E6%95%B4-graphql-%E6%93%8D%E4%BD%9C%E6%95%99%E5%AD%B8-88f0fb935120/"><![CDATA[<h3 id="medium-api-資料爬蟲與-cloudflare-攻防的心路歷程">Medium API 資料爬蟲與 Cloudflare 攻防的心路歷程</h3>

<p>使用 Medium Graphql Private API 爬取資料、備份文章及 Cloudflare 驗證阻擋突破方式。</p>

<h4 id="背景">背景</h4>

<p>自 2018 年開始經營 Medium 撰寫文章，大約在 2022 年時累積的文章數量已超過 50+ 篇，接近 6+ 萬字加上約 3 GB 的文章圖片 (直至 2025 已破 120+ 篇/10+萬字/6 GB+圖片)； <strong>沒有備份! 沒有備份! 沒有備份!</strong> ，因為我一直以來都直接用 Medium 寫文章(編輯器真的好用)， <strong>寫完就直接儲存發佈，本地沒有任何備份。</strong></p>

<p>那時候有一股不安感油然而生，如果哪一天 Medium 倒了我的文章不就不見了？(而且那幾年 Medium 狀況一直都很不好，沒有賺錢) 或是我的帳號突然被停權心血不就都付之一炬了？</p>

<p>於是在那年開始研究如何爬取 Medium 資料、尤其是文章內容跟如何下載備份。</p>
<h4 id="medium-html-">Medium HTML ❌</h4>

<p>結構非常複雜、易改變、爬取緩慢，直接起瀏覽器爬內容是下下策。</p>
<h4 id="medium-official-api-">Medium Official API ❌</h4>

<p><a href="https://github.com/Medium/medium-api-docs" target="_blank"><img src="https://opengraph.githubassets.com/e5dab4a39df5809789f4fa59924d1eda04c658aa27cd430b543a80ff7f74a935/Medium/medium-api-docs" alt="" /></a></p>

<blockquote>
  <p><strong><em>Warning The Medium API is no longer supported. We do not recommend using it.</em></strong></p>
</blockquote>

<p>Medium 曾經提供過官方的 API 接口，但是應該在推出沒多久就停用了；推測應該是跟產品目標不同，Medium 是私域流量應該要盡量把內容留在站內，而不是開 API 讓外部有機會串接拿到資料。</p>
<h4 id="medium-unofficial-api-">Medium Unofficial API 🤔</h4>

<p><a href="https://mediumapi.com/" target="_blank"><img src="https://mediumapi.com/imgs/mediumapi-website-image.jpg" alt="" /></a></p>

<p>也因為官方不提供 API，有民間大神自行開發對外的 API 接口讓有需求的使用者串接；但其中原理我推測也是打 Medium Private API，只是過了它那一層的封裝橋接，幫你把請求轉換成 Private API 取得原始資料。</p>

<blockquote>
  <p><strong><em>收費也不便宜，約每 2,500次請求 $USD 5 美元</em></strong> <em>；這個服務做蠻久的，但既然最終都是打 Medium Private API，那我自己嗅探自己打就好了。</em></p>
</blockquote>

<h3 id="medium-private-api-">Medium Private API ✅</h3>

<p>何謂 Private API? 就是這個 API 接口 <strong>並非給外部使用、沒有文件、可能隨時停用、可能有法律風險</strong> ；等於 <strong>直接去看他網站 / App 的行為找到呼叫的對應 API</strong> ，自己寫程式重用這個 API 撈資料到自己的服務上。</p>
<h4 id="httpsmediumcom_graphql"><a href="https://medium.com/_/graphql" target="_blank">https://medium.com/_/graphql</a></h4>

<p>經過網路請求嗅探，我們可以發現所有的 Medium API 都是使用 <a href="https://medium.com/_/graphql" target="_blank">https://medium.com/_/graphql</a> 這個接口，只要我們能在自己的程式上呼叫並取得相同的資料，這條路就能達成。</p>
<h4 id="medium-private-api--嗅探-medium-graphql-取得追蹤人數為例">Medium Private API — 嗅探 Medium Graphql (取得追蹤人數為例)</h4>

<p>我的第一個應用是想爬取我的追蹤人數顯示在我的入口網站。</p>

<p><a href="https://link.zhgchg.li/" target="_blank"><strong>link.zhgchg.li</strong></a> <strong>:</strong></p>

<p><img src="/assets/88f0fb935120/1*Lh9a_XR4zvvsaP3y58x9Aw.webp" alt="" loading="lazy" decoding="async" width="963" height="526" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NjMiIGhlaWdodD0iNTI2Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>因此我必須爬取 Medium 上我的帳號的追蹤人數。</p>
<ul>
  <li>我們先打開瀏覽器的「開發者模式」(Chrome -&gt; 右鍵 -&gt; 檢查) 切換到「Network」頁籤，勾選「Preserve log」、搜尋匡輸入「 <code class="language-plaintext highlighter-rouge">graphql</code> 」</li>
  <li>進入 Medium 帳號追蹤者頁面:「 <code class="language-plaintext highlighter-rouge">https://medium.com/@YOUR_USER_NAME/followers</code> 」
只能用這個格式的 Follower 網址嗅探， <code class="language-plaintext highlighter-rouge">https://YOUR_USER_NAME.medium.com/followers</code> 這種格式的沒有呼叫取得 Followers API。</li>
</ul>

<p><img src="/assets/88f0fb935120/1*jmGbo-BQK9amNmoRneJRPw.webp" alt="" loading="lazy" decoding="async" width="1200" height="848" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg0OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<ul>
  <li>一個一個查看左方「graphql」請求，在「Response」資料中搜尋「 <code class="language-plaintext highlighter-rouge">followerCount</code> 」關鍵字，找到有這個關鍵字且 <code class="language-plaintext highlighter-rouge">username</code> 同網頁的使用者名稱 &amp; 追蹤數字跟網頁顯示相同，找到這個目標請求。</li>
  <li>找到目標的 API 後，切換到 Payload -&gt; View Source -&gt; 複製以下全部內容</li>
</ul>

<p><img src="/assets/88f0fb935120/1*CGyScjCEe9Cf_3VIJrUJzQ.webp" alt="" loading="lazy" decoding="async" width="1437" height="633" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDM3IiBoZWlnaHQ9IjYzMyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>現在我們已經有取得 Medium 追蹤者的 API &amp; Payload 了，用 <a href="https://www.postman.com/" target="_blank">Postman</a> 測試看看！</p>
<h4 id="medium-取得追蹤人數-endpoint--request--response">Medium 取得追蹤人數 Endpoint — Request &amp; Response</h4>

<p><strong>Endpoint:</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>POST https://medium.com/_/graphql
</code></pre></div></div>

<p>Authorization: No authorization required (不用帶 Token or set cookies)</p>

<p><strong>Payload:</strong></p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">[</span><span class="w">
    </span><span class="p">{</span><span class="w">
        </span><span class="nl">"operationName"</span><span class="p">:</span><span class="w"> </span><span class="s2">"UserFollowers"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"variables"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
            </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w">
            </span><span class="nl">"username"</span><span class="p">:</span><span class="w"> </span><span class="s2">"zhgchgli"</span><span class="w">
        </span><span class="p">},</span><span class="w">
        </span><span class="nl">"query"</span><span class="p">:</span><span class="w"> </span><span class="s2">"query UserFollowers($username: ID, $id: ID, $paging: PagingOptions) {</span><span class="se">\n</span><span class="s2">  userResult(username: $username, id: $id) {</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    ... on User {</span><span class="se">\n</span><span class="s2">      id</span><span class="se">\n</span><span class="s2">      followersUserConnection(paging: $paging) {</span><span class="se">\n</span><span class="s2">        pagingInfo {</span><span class="se">\n</span><span class="s2">          next {</span><span class="se">\n</span><span class="s2">            from</span><span class="se">\n</span><span class="s2">            limit</span><span class="se">\n</span><span class="s2">            __typename</span><span class="se">\n</span><span class="s2">          }</span><span class="se">\n</span><span class="s2">          __typename</span><span class="se">\n</span><span class="s2">        }</span><span class="se">\n</span><span class="s2">        users {</span><span class="se">\n</span><span class="s2">          ...FollowList_publisher</span><span class="se">\n</span><span class="s2">          __typename</span><span class="se">\n</span><span class="s2">        }</span><span class="se">\n</span><span class="s2">        __typename</span><span class="se">\n</span><span class="s2">      }</span><span class="se">\n</span><span class="s2">      ...UserCanonicalizer_user</span><span class="se">\n</span><span class="s2">      ...FollowersHeader_publisher</span><span class="se">\n</span><span class="s2">      ...NoFollows_publisher</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment collectionUrl_collection on Collection {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  domain</span><span class="se">\n</span><span class="s2">  slug</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment CollectionAvatar_collection on Collection {</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  avatar {</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  ...collectionUrl_collection</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment SignInOptions_collection on Collection {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment SignUpOptions_collection on Collection {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment SusiModal_collection on Collection {</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  ...SignInOptions_collection</span><span class="se">\n</span><span class="s2">  ...SignUpOptions_collection</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment PublicationFollowButton_collection on Collection {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  slug</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  ...SusiModal_collection</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment PublicationFollowRow_collection on Collection {</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  description</span><span class="se">\n</span><span class="s2">  ...CollectionAvatar_collection</span><span class="se">\n</span><span class="s2">  ...PublicationFollowButton_collection</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment userUrl_user on User {</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  customDomainState {</span><span class="se">\n</span><span class="s2">    live {</span><span class="se">\n</span><span class="s2">      domain</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  hasSubdomain</span><span class="se">\n</span><span class="s2">  username</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment UserAvatar_user on User {</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  imageId</span><span class="se">\n</span><span class="s2">  membership {</span><span class="se">\n</span><span class="s2">    tier</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  username</span><span class="se">\n</span><span class="s2">  ...userUrl_user</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment isUserVerifiedBookAuthor_user on User {</span><span class="se">\n</span><span class="s2">  verifications {</span><span class="se">\n</span><span class="s2">    isBookAuthor</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment SignInOptions_user on User {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  imageId</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment SignUpOptions_user on User {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  imageId</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment SusiModal_user on User {</span><span class="se">\n</span><span class="s2">  ...SignInOptions_user</span><span class="se">\n</span><span class="s2">  ...SignUpOptions_user</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment useNewsletterV3Subscription_newsletterV3 on NewsletterV3 {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  type</span><span class="se">\n</span><span class="s2">  slug</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  collection {</span><span class="se">\n</span><span class="s2">    slug</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  user {</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">    name</span><span class="se">\n</span><span class="s2">    username</span><span class="se">\n</span><span class="s2">    newsletterV3 {</span><span class="se">\n</span><span class="s2">      id</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment useNewsletterV3Subscription_user on User {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  username</span><span class="se">\n</span><span class="s2">  newsletterV3 {</span><span class="se">\n</span><span class="s2">    ...useNewsletterV3Subscription_newsletterV3</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment useAuthorFollowSubscribeButton_user on User {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  ...useNewsletterV3Subscription_user</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment useAuthorFollowSubscribeButton_newsletterV3 on NewsletterV3 {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  ...useNewsletterV3Subscription_newsletterV3</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment AuthorFollowSubscribeButton_user on User {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  imageId</span><span class="se">\n</span><span class="s2">  ...SusiModal_user</span><span class="se">\n</span><span class="s2">  ...useAuthorFollowSubscribeButton_user</span><span class="se">\n</span><span class="s2">  newsletterV3 {</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">    ...useAuthorFollowSubscribeButton_newsletterV3</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment UserFollowRow_user on User {</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  bio</span><span class="se">\n</span><span class="s2">  ...UserAvatar_user</span><span class="se">\n</span><span class="s2">  ...isUserVerifiedBookAuthor_user</span><span class="se">\n</span><span class="s2">  ...AuthorFollowSubscribeButton_user</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment FollowsHeader_publisher on Publisher {</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  ... on Collection {</span><span class="se">\n</span><span class="s2">    ...collectionUrl_collection</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  ... on User {</span><span class="se">\n</span><span class="s2">    ...userUrl_user</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment FollowList_publisher on Publisher {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  ... on Collection {</span><span class="se">\n</span><span class="s2">    ...PublicationFollowRow_collection</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  ... on User {</span><span class="se">\n</span><span class="s2">    ...UserFollowRow_user</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment UserCanonicalizer_user on User {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  username</span><span class="se">\n</span><span class="s2">  hasSubdomain</span><span class="se">\n</span><span class="s2">  customDomainState {</span><span class="se">\n</span><span class="s2">    live {</span><span class="se">\n</span><span class="s2">      domain</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment FollowersHeader_publisher on Publisher {</span><span class="se">\n</span><span class="s2">  ...FollowsHeader_publisher</span><span class="se">\n</span><span class="s2">  ... on Collection {</span><span class="se">\n</span><span class="s2">    subscriberCount</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  ... on User {</span><span class="se">\n</span><span class="s2">    socialStats {</span><span class="se">\n</span><span class="s2">      followerCount</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment NoFollows_publisher on Publisher {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n</span><span class="s2">"</span><span class="w">
    </span><span class="p">}</span><span class="w">
</span><span class="p">]</span><span class="w">
</span></code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">username</code> : 換成你要查詢的使用者名稱。</p>

<p><a href="https://www.postman.com/" target="_blank"><strong>Postman</strong></a> <strong>:</strong></p>

<p><img src="/assets/88f0fb935120/1*MuntmkeqjFqqD_ma_td3VA.webp" alt="" loading="lazy" decoding="async" width="1200" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjEwMjQiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">POST https://medium.com/_/graphql</code></li>
  <li>Body -&gt; raw -&gt; JSON</li>
</ul>

<p><strong>Response:</strong></p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">[</span><span class="w">
    </span><span class="p">{</span><span class="w">
        </span><span class="nl">"data"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
            </span><span class="nl">"userResult"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
                </span><span class="nl">"__typename"</span><span class="p">:</span><span class="w"> </span><span class="s2">"User"</span><span class="p">,</span><span class="w">
                </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"8854784154b8"</span><span class="p">,</span><span class="w">
                </span><span class="nl">"followersUserConnection"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
                    </span><span class="nl">"pagingInfo"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
                        </span><span class="nl">"next"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
                            </span><span class="nl">"from"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1140babdfef7"</span><span class="p">,</span><span class="w">
                            </span><span class="nl">"limit"</span><span class="p">:</span><span class="w"> </span><span class="mi">8</span><span class="p">,</span><span class="w">
                            </span><span class="nl">"__typename"</span><span class="p">:</span><span class="w"> </span><span class="s2">"PageParams"</span><span class="w">
                        </span><span class="p">},</span><span class="w">
                        </span><span class="nl">"__typename"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Paging"</span><span class="w">
                    </span><span class="p">},</span><span class="w">
                    </span><span class="nl">"users"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
                        </span><span class="err">///..</span><span class="w"> </span><span class="err">略</span><span class="w">
                    </span><span class="p">],</span><span class="w">
                    </span><span class="nl">"__typename"</span><span class="p">:</span><span class="w"> </span><span class="s2">"UserConnection"</span><span class="w">
                </span><span class="p">},</span><span class="w">
                </span><span class="nl">"username"</span><span class="p">:</span><span class="w"> </span><span class="s2">"zhgchgli"</span><span class="p">,</span><span class="w">
                </span><span class="nl">"hasSubdomain"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
                </span><span class="nl">"customDomainState"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
                    </span><span class="nl">"live"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
                        </span><span class="nl">"domain"</span><span class="p">:</span><span class="w"> </span><span class="s2">"zhgchgli.medium.com"</span><span class="p">,</span><span class="w">
                        </span><span class="nl">"__typename"</span><span class="p">:</span><span class="w"> </span><span class="s2">"CustomDomain"</span><span class="w">
                    </span><span class="p">},</span><span class="w">
                    </span><span class="nl">"__typename"</span><span class="p">:</span><span class="w"> </span><span class="s2">"CustomDomainState"</span><span class="w">
                </span><span class="p">},</span><span class="w">
                </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"ZhgChgLi"</span><span class="p">,</span><span class="w">
                </span><span class="nl">"socialStats"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
                    </span><span class="nl">"followerCount"</span><span class="p">:</span><span class="w"> </span><span class="mi">1050</span><span class="p">,</span><span class="w">
                    </span><span class="nl">"__typename"</span><span class="p">:</span><span class="w"> </span><span class="s2">"SocialStats"</span><span class="w">
                </span><span class="p">}</span><span class="w">
            </span><span class="p">}</span><span class="w">
        </span><span class="p">}</span><span class="w">
    </span><span class="p">}</span><span class="w">
</span><span class="p">]</span><span class="w">
</span></code></pre></div></div>

<p>關鍵資訊： <code class="language-plaintext highlighter-rouge">response[0]["data"]["userResult"]["socialStats"]["followerCount"]</code> 就是目標追蹤人數了！</p>

<p><strong>Ruby Demo Code:</strong></p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">load_medium_followers</span><span class="p">(</span><span class="n">username</span><span class="p">)</span>
    <span class="k">begin</span>
        <span class="n">url</span> <span class="o">=</span> <span class="s2">"https://medium.com/_/graphql"</span>
        <span class="n">uri</span> <span class="o">=</span> <span class="no">URI</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="n">url</span><span class="p">)</span>

        <span class="n">payload</span> <span class="o">=</span> <span class="p">[</span>
            <span class="p">{</span>
            <span class="s2">"operationName"</span><span class="p">:</span> <span class="s2">"UserFollowers"</span><span class="p">,</span>
            <span class="s2">"variables"</span><span class="p">:</span> <span class="p">{</span>
                <span class="s2">"id"</span><span class="p">:</span> <span class="kp">nil</span><span class="p">,</span>
                <span class="s2">"username"</span><span class="p">:</span> <span class="n">username</span><span class="p">,</span>
                <span class="s2">"paging"</span><span class="p">:</span> <span class="kp">nil</span>
            <span class="p">},</span>
            <span class="s2">"query"</span><span class="p">:</span> <span class="s2">"query UserFollowers($username: ID, $id: ID, $paging: PagingOptions) {</span><span class="se">\n</span><span class="s2">  userResult(username: $username, id: $id) {</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    ... on User {</span><span class="se">\n</span><span class="s2">      id</span><span class="se">\n</span><span class="s2">      followersUserConnection(paging: $paging) {</span><span class="se">\n</span><span class="s2">        pagingInfo {</span><span class="se">\n</span><span class="s2">          next {</span><span class="se">\n</span><span class="s2">            from</span><span class="se">\n</span><span class="s2">            limit</span><span class="se">\n</span><span class="s2">            __typename</span><span class="se">\n</span><span class="s2">          }</span><span class="se">\n</span><span class="s2">          __typename</span><span class="se">\n</span><span class="s2">        }</span><span class="se">\n</span><span class="s2">        users {</span><span class="se">\n</span><span class="s2">          ...FollowList_publisher</span><span class="se">\n</span><span class="s2">          __typename</span><span class="se">\n</span><span class="s2">        }</span><span class="se">\n</span><span class="s2">        __typename</span><span class="se">\n</span><span class="s2">      }</span><span class="se">\n</span><span class="s2">      ...UserCanonicalizer_user</span><span class="se">\n</span><span class="s2">      ...FollowersHeader_publisher</span><span class="se">\n</span><span class="s2">      ...NoFollows_publisher</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment collectionUrl_collection on Collection {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  domain</span><span class="se">\n</span><span class="s2">  slug</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment CollectionAvatar_collection on Collection {</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  avatar {</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  ...collectionUrl_collection</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment SignInOptions_collection on Collection {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment SignUpOptions_collection on Collection {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment SusiModal_collection on Collection {</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  ...SignInOptions_collection</span><span class="se">\n</span><span class="s2">  ...SignUpOptions_collection</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment PublicationFollowButton_collection on Collection {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  slug</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  ...SusiModal_collection</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment PublicationFollowRow_collection on Collection {</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  description</span><span class="se">\n</span><span class="s2">  ...CollectionAvatar_collection</span><span class="se">\n</span><span class="s2">  ...PublicationFollowButton_collection</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment userUrl_user on User {</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  customDomainState {</span><span class="se">\n</span><span class="s2">    live {</span><span class="se">\n</span><span class="s2">      domain</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  hasSubdomain</span><span class="se">\n</span><span class="s2">  username</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment UserAvatar_user on User {</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  imageId</span><span class="se">\n</span><span class="s2">  membership {</span><span class="se">\n</span><span class="s2">    tier</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  username</span><span class="se">\n</span><span class="s2">  ...userUrl_user</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment isUserVerifiedBookAuthor_user on User {</span><span class="se">\n</span><span class="s2">  verifications {</span><span class="se">\n</span><span class="s2">    isBookAuthor</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment SignInOptions_user on User {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  imageId</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment SignUpOptions_user on User {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  imageId</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment SusiModal_user on User {</span><span class="se">\n</span><span class="s2">  ...SignInOptions_user</span><span class="se">\n</span><span class="s2">  ...SignUpOptions_user</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment useNewsletterV3Subscription_newsletterV3 on NewsletterV3 {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  type</span><span class="se">\n</span><span class="s2">  slug</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  collection {</span><span class="se">\n</span><span class="s2">    slug</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  user {</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">    name</span><span class="se">\n</span><span class="s2">    username</span><span class="se">\n</span><span class="s2">    newsletterV3 {</span><span class="se">\n</span><span class="s2">      id</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment useNewsletterV3Subscription_user on User {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  username</span><span class="se">\n</span><span class="s2">  newsletterV3 {</span><span class="se">\n</span><span class="s2">    ...useNewsletterV3Subscription_newsletterV3</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment useAuthorFollowSubscribeButton_user on User {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  ...useNewsletterV3Subscription_user</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment useAuthorFollowSubscribeButton_newsletterV3 on NewsletterV3 {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  ...useNewsletterV3Subscription_newsletterV3</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment AuthorFollowSubscribeButton_user on User {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  imageId</span><span class="se">\n</span><span class="s2">  ...SusiModal_user</span><span class="se">\n</span><span class="s2">  ...useAuthorFollowSubscribeButton_user</span><span class="se">\n</span><span class="s2">  newsletterV3 {</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">    ...useAuthorFollowSubscribeButton_newsletterV3</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment UserFollowRow_user on User {</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  bio</span><span class="se">\n</span><span class="s2">  ...UserAvatar_user</span><span class="se">\n</span><span class="s2">  ...isUserVerifiedBookAuthor_user</span><span class="se">\n</span><span class="s2">  ...AuthorFollowSubscribeButton_user</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment FollowsHeader_publisher on Publisher {</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  ... on Collection {</span><span class="se">\n</span><span class="s2">    ...collectionUrl_collection</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  ... on User {</span><span class="se">\n</span><span class="s2">    ...userUrl_user</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment FollowList_publisher on Publisher {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  ... on Collection {</span><span class="se">\n</span><span class="s2">    ...PublicationFollowRow_collection</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  ... on User {</span><span class="se">\n</span><span class="s2">    ...UserFollowRow_user</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment UserCanonicalizer_user on User {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  username</span><span class="se">\n</span><span class="s2">  hasSubdomain</span><span class="se">\n</span><span class="s2">  customDomainState {</span><span class="se">\n</span><span class="s2">    live {</span><span class="se">\n</span><span class="s2">      domain</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment FollowersHeader_publisher on Publisher {</span><span class="se">\n</span><span class="s2">  ...FollowsHeader_publisher</span><span class="se">\n</span><span class="s2">  ... on Collection {</span><span class="se">\n</span><span class="s2">    subscriberCount</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  ... on User {</span><span class="se">\n</span><span class="s2">    socialStats {</span><span class="se">\n</span><span class="s2">      followerCount</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment NoFollows_publisher on Publisher {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n</span><span class="s2">"</span>
            <span class="p">}</span>
        <span class="p">];</span>

        <span class="n">headers</span> <span class="o">=</span> <span class="p">{</span>
            <span class="s2">"User-Agent"</span> <span class="o">=&gt;</span> <span class="s2">"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36 Edg/142.0.0.0"</span><span class="p">,</span>
            <span class="s2">"Content-Type"</span> <span class="o">=&gt;</span> <span class="s2">"application/json"</span>
        <span class="p">}</span>

        <span class="n">https</span> <span class="o">=</span> <span class="no">Net</span><span class="o">::</span><span class="no">HTTP</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">uri</span><span class="p">.</span><span class="nf">host</span><span class="p">,</span> <span class="n">uri</span><span class="p">.</span><span class="nf">port</span><span class="p">)</span>
        <span class="n">https</span><span class="p">.</span><span class="nf">read_timeout</span> <span class="o">=</span> <span class="mi">30</span>
        <span class="n">https</span><span class="p">.</span><span class="nf">open_timeout</span> <span class="o">=</span> <span class="mi">10</span>
        <span class="n">https</span><span class="p">.</span><span class="nf">use_ssl</span> <span class="o">=</span> <span class="kp">true</span>

        <span class="c1"># --- TLS / Certificate verification setup ---</span>
        <span class="c1"># Some OpenSSL builds/configs enable CRL checking, which can fail with:</span>
        <span class="c1"># "certificate verify failed (unable to get certificate CRL)".</span>
        <span class="c1"># Net::HTTP/OpenSSL does not automatically fetch CRLs, so we use a default</span>
        <span class="c1"># cert store and clear CRL-related flags to avoid hard failures while still</span>
        <span class="c1"># verifying the peer certificate.</span>
        <span class="n">https</span><span class="p">.</span><span class="nf">verify_mode</span> <span class="o">=</span> <span class="no">OpenSSL</span><span class="o">::</span><span class="no">SSL</span><span class="o">::</span><span class="no">VERIFY_PEER</span>

        <span class="n">store</span> <span class="o">=</span> <span class="no">OpenSSL</span><span class="o">::</span><span class="no">X509</span><span class="o">::</span><span class="no">Store</span><span class="p">.</span><span class="nf">new</span>
        <span class="n">store</span><span class="p">.</span><span class="nf">set_default_paths</span>
        <span class="c1"># Ensure no CRL-check flags are enabled by default</span>
        <span class="n">store</span><span class="p">.</span><span class="nf">flags</span> <span class="o">=</span> <span class="mi">0</span>
        <span class="n">https</span><span class="p">.</span><span class="nf">cert_store</span> <span class="o">=</span> <span class="n">store</span>

        <span class="c1"># Allow overriding CA bundle paths via environment variables if needed.</span>
        <span class="k">if</span> <span class="no">ENV</span><span class="p">[</span><span class="s1">'SSL_CERT_FILE'</span><span class="p">]</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="no">ENV</span><span class="p">[</span><span class="s1">'SSL_CERT_FILE'</span><span class="p">].</span><span class="nf">empty?</span>
        <span class="n">https</span><span class="p">.</span><span class="nf">ca_file</span> <span class="o">=</span> <span class="no">ENV</span><span class="p">[</span><span class="s1">'SSL_CERT_FILE'</span><span class="p">]</span>
        <span class="k">end</span>
        <span class="k">if</span> <span class="no">ENV</span><span class="p">[</span><span class="s1">'SSL_CERT_DIR'</span><span class="p">]</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="no">ENV</span><span class="p">[</span><span class="s1">'SSL_CERT_DIR'</span><span class="p">].</span><span class="nf">empty?</span>
        <span class="n">https</span><span class="p">.</span><span class="nf">ca_path</span> <span class="o">=</span> <span class="no">ENV</span><span class="p">[</span><span class="s1">'SSL_CERT_DIR'</span><span class="p">]</span>
        <span class="k">end</span>

        <span class="c1"># (Optional) timeouts to avoid hanging on network issues</span>
        <span class="n">https</span><span class="p">.</span><span class="nf">open_timeout</span> <span class="o">=</span> <span class="mi">10</span>
        <span class="n">https</span><span class="p">.</span><span class="nf">read_timeout</span> <span class="o">=</span> <span class="mi">30</span>
        <span class="c1"># --- end TLS setup ---</span>


        <span class="n">req</span> <span class="o">=</span> <span class="no">Net</span><span class="o">::</span><span class="no">HTTP</span><span class="o">::</span><span class="no">Post</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">uri</span><span class="p">.</span><span class="nf">request_uri</span><span class="p">,</span> <span class="n">headers</span><span class="p">)</span>
        <span class="n">req</span><span class="p">.</span><span class="nf">body</span> <span class="o">=</span> <span class="no">JSON</span><span class="p">.</span><span class="nf">dump</span><span class="p">(</span><span class="n">payload</span><span class="p">)</span>

        <span class="n">res</span> <span class="o">=</span> <span class="n">https</span><span class="p">.</span><span class="nf">request</span><span class="p">(</span><span class="n">req</span><span class="p">)</span>

        <span class="n">json</span> <span class="o">=</span> <span class="no">JSON</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="n">res</span><span class="p">.</span><span class="nf">body</span><span class="p">)</span>
        <span class="n">count</span> <span class="o">=</span> <span class="n">json</span><span class="o">&amp;</span><span class="p">.</span><span class="nf">dig</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="s2">"data"</span><span class="p">,</span> <span class="s2">"userResult"</span><span class="p">,</span> <span class="s2">"socialStats"</span><span class="p">,</span> <span class="s2">"followerCount"</span><span class="p">)</span>
        <span class="n">count</span> <span class="p">?</span> <span class="n">count</span><span class="p">.</span><span class="nf">to_i</span> <span class="p">:</span> <span class="mi">0</span>
        <span class="k">return</span> <span class="s2">"1K+"</span> <span class="k">unless</span> <span class="n">count</span><span class="p">.</span><span class="nf">to_i</span> <span class="o">&gt;</span> <span class="mi">0</span>
        <span class="k">return</span> <span class="s2">"</span><span class="si">#{</span><span class="n">count</span><span class="p">.</span><span class="nf">to_s</span><span class="p">.</span><span class="nf">reverse</span><span class="p">.</span><span class="nf">gsub</span><span class="p">(</span><span class="sr">/(\d{3})(?=\d)/</span><span class="p">,</span> <span class="s1">'\\1,'</span><span class="p">).</span><span class="nf">reverse</span><span class="si">}</span><span class="s2">+"</span>
    <span class="k">rescue</span> <span class="o">=&gt;</span> <span class="n">e</span>
        <span class="s2">"1K+"</span>
    <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>
<h4 id="medium-private-api--取得帳號的所有文章">Medium Private API — 取得帳號的所有文章</h4>

<p>用相同的方式我們也可以取得目標帳號的文章列表。</p>

<p>Graphql Query Body:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">[</span><span class="w">
      </span><span class="p">{</span><span class="w">
        </span><span class="nl">"operationName"</span><span class="p">:</span><span class="w"> </span><span class="s2">"UserProfileQuery"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"variables"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"homepagePostsFrom"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w">
          </span><span class="nl">"includeDistributedResponses"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
          </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"8854784154b8"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"homepagePostsLimit"</span><span class="p">:</span><span class="w"> </span><span class="mi">10</span><span class="w">
        </span><span class="p">},</span><span class="w">
        </span><span class="nl">"query"</span><span class="p">:</span><span class="w"> </span><span class="s2">"query UserProfileQuery($id: ID, $username: ID, $homepagePostsLimit: PaginationLimit, $homepagePostsFrom: String = null, $includeDistributedResponses: Boolean = true) {</span><span class="se">\n</span><span class="s2">  userResult(id: $id, username: $username) {</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    ... on User {</span><span class="se">\n</span><span class="s2">      id</span><span class="se">\n</span><span class="s2">      name</span><span class="se">\n</span><span class="s2">      viewerIsUser</span><span class="se">\n</span><span class="s2">      viewerEdge {</span><span class="se">\n</span><span class="s2">        id</span><span class="se">\n</span><span class="s2">        isFollowing</span><span class="se">\n</span><span class="s2">        __typename</span><span class="se">\n</span><span class="s2">      }</span><span class="se">\n</span><span class="s2">      homePostsPublished: homepagePostsConnection(paging: {limit: 1}) {</span><span class="se">\n</span><span class="s2">        posts {</span><span class="se">\n</span><span class="s2">          id</span><span class="se">\n</span><span class="s2">          __typename</span><span class="se">\n</span><span class="s2">        }</span><span class="se">\n</span><span class="s2">        __typename</span><span class="se">\n</span><span class="s2">      }</span><span class="se">\n</span><span class="s2">      ...UserCanonicalizer_user</span><span class="se">\n</span><span class="s2">      ...UserProfileScreen_user</span><span class="se">\n</span><span class="s2">      ...EntityDrivenSubscriptionLandingPageScreen_writer</span><span class="se">\n</span><span class="s2">      ...useShouldShowEntityDrivenSubscription_creator</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment UserCanonicalizer_user on User {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  username</span><span class="se">\n</span><span class="s2">  hasSubdomain</span><span class="se">\n</span><span class="s2">  customDomainState {</span><span class="se">\n</span><span class="s2">    live {</span><span class="se">\n</span><span class="s2">      domain</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment UserProfileScreen_user on User {</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  viewerIsUser</span><span class="se">\n</span><span class="s2">  ...PublisherHeader_publisher</span><span class="se">\n</span><span class="s2">  ...PublisherHomePosts_publisher</span><span class="se">\n</span><span class="s2">  ...UserSubdomainFlow_user</span><span class="se">\n</span><span class="s2">  ...UserProfileMetadata_user</span><span class="se">\n</span><span class="s2">  ...SuspendedBannerLoader_user</span><span class="se">\n</span><span class="s2">  ...ExpandablePost_user</span><span class="se">\n</span><span class="s2">  ...useAnalytics_user</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment PublisherHeader_publisher on Publisher {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  ...PublisherHeaderBackground_publisher</span><span class="se">\n</span><span class="s2">  ...PublisherHeaderNameplate_publisher</span><span class="se">\n</span><span class="s2">  ...PublisherHeaderActions_publisher</span><span class="se">\n</span><span class="s2">  ...PublisherHeaderNav_publisher</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment PublisherHeaderBackground_publisher on Publisher {</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  customStyleSheet {</span><span class="se">\n</span><span class="s2">    ...PublisherHeaderBackground_customStyleSheet</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  ... on Collection {</span><span class="se">\n</span><span class="s2">    colorPalette {</span><span class="se">\n</span><span class="s2">      tintBackgroundSpectrum {</span><span class="se">\n</span><span class="s2">        backgroundColor</span><span class="se">\n</span><span class="s2">        __typename</span><span class="se">\n</span><span class="s2">      }</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    isAuroraVisible</span><span class="se">\n</span><span class="s2">    legacyHeaderBackgroundImage {</span><span class="se">\n</span><span class="s2">      id</span><span class="se">\n</span><span class="s2">      originalWidth</span><span class="se">\n</span><span class="s2">      focusPercentX</span><span class="se">\n</span><span class="s2">      focusPercentY</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    ...collectionTintBackgroundTheme_collection</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  ...publisherUrl_publisher</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment PublisherHeaderBackground_customStyleSheet on CustomStyleSheet {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  global {</span><span class="se">\n</span><span class="s2">    colorPalette {</span><span class="se">\n</span><span class="s2">      background {</span><span class="se">\n</span><span class="s2">        rgb</span><span class="se">\n</span><span class="s2">        __typename</span><span class="se">\n</span><span class="s2">      }</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  header {</span><span class="se">\n</span><span class="s2">    headerScale</span><span class="se">\n</span><span class="s2">    backgroundImageDisplayMode</span><span class="se">\n</span><span class="s2">    backgroundImageVerticalAlignment</span><span class="se">\n</span><span class="s2">    backgroundColorDisplayMode</span><span class="se">\n</span><span class="s2">    backgroundColor {</span><span class="se">\n</span><span class="s2">      alpha</span><span class="se">\n</span><span class="s2">      rgb</span><span class="se">\n</span><span class="s2">      ...getHexFromColorValue_colorValue</span><span class="se">\n</span><span class="s2">      ...getOpaqueHexFromColorValue_colorValue</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    secondaryBackgroundColor {</span><span class="se">\n</span><span class="s2">      ...getHexFromColorValue_colorValue</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    postBackgroundColor {</span><span class="se">\n</span><span class="s2">      ...getHexFromColorValue_colorValue</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    backgroundImage {</span><span class="se">\n</span><span class="s2">      ...MetaHeaderBackground_imageMetadata</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment getHexFromColorValue_colorValue on ColorValue {</span><span class="se">\n</span><span class="s2">  rgb</span><span class="se">\n</span><span class="s2">  alpha</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment getOpaqueHexFromColorValue_colorValue on ColorValue {</span><span class="se">\n</span><span class="s2">  rgb</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment MetaHeaderBackground_imageMetadata on ImageMetadata {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  originalWidth</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment collectionTintBackgroundTheme_collection on Collection {</span><span class="se">\n</span><span class="s2">  colorPalette {</span><span class="se">\n</span><span class="s2">    ...collectionTintBackgroundTheme_colorPalette</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  customStyleSheet {</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">    ...collectionTintBackgroundTheme_customStyleSheet</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment collectionTintBackgroundTheme_colorPalette on ColorPalette {</span><span class="se">\n</span><span class="s2">  ...customTintBackgroundTheme_colorPalette</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment customTintBackgroundTheme_colorPalette on ColorPalette {</span><span class="se">\n</span><span class="s2">  tintBackgroundSpectrum {</span><span class="se">\n</span><span class="s2">    ...ThemeUtil_colorSpectrum</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment ThemeUtil_colorSpectrum on ColorSpectrum {</span><span class="se">\n</span><span class="s2">  backgroundColor</span><span class="se">\n</span><span class="s2">  ...ThemeUtilInterpolateHelpers_colorSpectrum</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment ThemeUtilInterpolateHelpers_colorSpectrum on ColorSpectrum {</span><span class="se">\n</span><span class="s2">  colorPoints {</span><span class="se">\n</span><span class="s2">    ...ThemeUtil_colorPoint</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment ThemeUtil_colorPoint on ColorPoint {</span><span class="se">\n</span><span class="s2">  color</span><span class="se">\n</span><span class="s2">  point</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment collectionTintBackgroundTheme_customStyleSheet on CustomStyleSheet {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  ...customTintBackgroundTheme_customStyleSheet</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment customTintBackgroundTheme_customStyleSheet on CustomStyleSheet {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  global {</span><span class="se">\n</span><span class="s2">    colorPalette {</span><span class="se">\n</span><span class="s2">      primary {</span><span class="se">\n</span><span class="s2">        colorPalette {</span><span class="se">\n</span><span class="s2">          ...customTintBackgroundTheme_colorPalette</span><span class="se">\n</span><span class="s2">          __typename</span><span class="se">\n</span><span class="s2">        }</span><span class="se">\n</span><span class="s2">        __typename</span><span class="se">\n</span><span class="s2">      }</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment publisherUrl_publisher on Publisher {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  ... on Collection {</span><span class="se">\n</span><span class="s2">    ...collectionUrl_collection</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  ... on User {</span><span class="se">\n</span><span class="s2">    ...userUrl_user</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment collectionUrl_collection on Collection {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  domain</span><span class="se">\n</span><span class="s2">  slug</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment userUrl_user on User {</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  customDomainState {</span><span class="se">\n</span><span class="s2">    live {</span><span class="se">\n</span><span class="s2">      domain</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  hasSubdomain</span><span class="se">\n</span><span class="s2">  username</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment PublisherHeaderNameplate_publisher on Publisher {</span><span class="se">\n</span><span class="s2">  ...PublisherAvatar_publisher</span><span class="se">\n</span><span class="s2">  ...PublisherHeaderLogo_publisher</span><span class="se">\n</span><span class="s2">  ...PublisherFollowerCount_publisher</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment PublisherAvatar_publisher on Publisher {</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  ... on Collection {</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">    ...CollectionAvatar_collection</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  ... on User {</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">    ...UserAvatar_user</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment CollectionAvatar_collection on Collection {</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  avatar {</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  ...collectionUrl_collection</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment UserAvatar_user on User {</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  imageId</span><span class="se">\n</span><span class="s2">  mediumMemberAt</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  username</span><span class="se">\n</span><span class="s2">  ...userUrl_user</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment PublisherHeaderLogo_publisher on Publisher {</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  customStyleSheet {</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">    header {</span><span class="se">\n</span><span class="s2">      logoImage {</span><span class="se">\n</span><span class="s2">        id</span><span class="se">\n</span><span class="s2">        originalHeight</span><span class="se">\n</span><span class="s2">        originalWidth</span><span class="se">\n</span><span class="s2">        __typename</span><span class="se">\n</span><span class="s2">      }</span><span class="se">\n</span><span class="s2">      appNameColor {</span><span class="se">\n</span><span class="s2">        ...getHexFromColorValue_colorValue</span><span class="se">\n</span><span class="s2">        __typename</span><span class="se">\n</span><span class="s2">      }</span><span class="se">\n</span><span class="s2">      appNameTreatment</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  ... on Collection {</span><span class="se">\n</span><span class="s2">    isAuroraVisible</span><span class="se">\n</span><span class="s2">    logo {</span><span class="se">\n</span><span class="s2">      id</span><span class="se">\n</span><span class="s2">      originalHeight</span><span class="se">\n</span><span class="s2">      originalWidth</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  ...CustomHeaderTooltip_publisher</span><span class="se">\n</span><span class="s2">  ...publisherUrl_publisher</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment CustomHeaderTooltip_publisher on Publisher {</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  customStyleSheet {</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">    header {</span><span class="se">\n</span><span class="s2">      appNameTreatment</span><span class="se">\n</span><span class="s2">      nameTreatment</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  ... on Collection {</span><span class="se">\n</span><span class="s2">    isAuroraVisible</span><span class="se">\n</span><span class="s2">    slug</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment PublisherFollowerCount_publisher on Publisher {</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  ... on Collection {</span><span class="se">\n</span><span class="s2">    slug</span><span class="se">\n</span><span class="s2">    subscriberCount</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  ... on User {</span><span class="se">\n</span><span class="s2">    socialStats {</span><span class="se">\n</span><span class="s2">      followerCount</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    username</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment PublisherHeaderActions_publisher on Publisher {</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  ...MetaHeaderPubMenu_publisher</span><span class="se">\n</span><span class="s2">  ... on Collection {</span><span class="se">\n</span><span class="s2">    ...CollectionFollowButton_collection</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  ... on User {</span><span class="se">\n</span><span class="s2">    ...FollowAndSubscribeButtons_user</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment MetaHeaderPubMenu_publisher on Publisher {</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  ... on Collection {</span><span class="se">\n</span><span class="s2">    ...MetaHeaderPubMenu_publisher_collection</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  ... on User {</span><span class="se">\n</span><span class="s2">    ...MetaHeaderPubMenu_publisher_user</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment MetaHeaderPubMenu_publisher_collection on Collection {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  slug</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  domain</span><span class="se">\n</span><span class="s2">  newsletterV3 {</span><span class="se">\n</span><span class="s2">    slug</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  ...MutePopoverOptions_collection</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment MutePopoverOptions_collection on Collection {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment MetaHeaderPubMenu_publisher_user on User {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  username</span><span class="se">\n</span><span class="s2">  ...MutePopoverOptions_creator</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment MutePopoverOptions_creator on User {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment CollectionFollowButton_collection on Collection {</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  slug</span><span class="se">\n</span><span class="s2">  ...collectionUrl_collection</span><span class="se">\n</span><span class="s2">  ...SusiClickable_collection</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment SusiClickable_collection on Collection {</span><span class="se">\n</span><span class="s2">  ...SusiContainer_collection</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment SusiContainer_collection on Collection {</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  ...SignInOptions_collection</span><span class="se">\n</span><span class="s2">  ...SignUpOptions_collection</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment SignInOptions_collection on Collection {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment SignUpOptions_collection on Collection {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment FollowAndSubscribeButtons_user on User {</span><span class="se">\n</span><span class="s2">  ...UserFollowButton_user</span><span class="se">\n</span><span class="s2">  ...UserSubscribeButton_user</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment UserFollowButton_user on User {</span><span class="se">\n</span><span class="s2">  ...UserFollowButtonSignedIn_user</span><span class="se">\n</span><span class="s2">  ...UserFollowButtonSignedOut_user</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment UserFollowButtonSignedIn_user on User {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment UserFollowButtonSignedOut_user on User {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  ...SusiClickable_user</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment SusiClickable_user on User {</span><span class="se">\n</span><span class="s2">  ...SusiContainer_user</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment SusiContainer_user on User {</span><span class="se">\n</span><span class="s2">  ...SignInOptions_user</span><span class="se">\n</span><span class="s2">  ...SignUpOptions_user</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment SignInOptions_user on User {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment SignUpOptions_user on User {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment UserSubscribeButton_user on User {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  isPartnerProgramEnrolled</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  viewerEdge {</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">    isFollowing</span><span class="se">\n</span><span class="s2">    isUser</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  viewerIsUser</span><span class="se">\n</span><span class="s2">  newsletterV3 {</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">    ...useNewsletterV3Subscription_newsletterV3</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  ...useNewsletterV3Subscription_user</span><span class="se">\n</span><span class="s2">  ...MembershipUpsellModal_user</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment useNewsletterV3Subscription_newsletterV3 on NewsletterV3 {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  type</span><span class="se">\n</span><span class="s2">  slug</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  collection {</span><span class="se">\n</span><span class="s2">    slug</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  user {</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">    name</span><span class="se">\n</span><span class="s2">    username</span><span class="se">\n</span><span class="s2">    newsletterV3 {</span><span class="se">\n</span><span class="s2">      id</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment useNewsletterV3Subscription_user on User {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  username</span><span class="se">\n</span><span class="s2">  newsletterV3 {</span><span class="se">\n</span><span class="s2">    ...useNewsletterV3Subscription_newsletterV3</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment MembershipUpsellModal_user on User {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  imageId</span><span class="se">\n</span><span class="s2">  postSubscribeMembershipUpsellShownAt</span><span class="se">\n</span><span class="s2">  newsletterV3 {</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment PublisherHeaderNav_publisher on Publisher {</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  customStyleSheet {</span><span class="se">\n</span><span class="s2">    navigation {</span><span class="se">\n</span><span class="s2">      navItems {</span><span class="se">\n</span><span class="s2">        name</span><span class="se">\n</span><span class="s2">        ...PublisherHeaderNavLink_headerNavigationItem</span><span class="se">\n</span><span class="s2">        __typename</span><span class="se">\n</span><span class="s2">      }</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  ...PublisherHeaderNavLink_publisher</span><span class="se">\n</span><span class="s2">  ... on Collection {</span><span class="se">\n</span><span class="s2">    domain</span><span class="se">\n</span><span class="s2">    isAuroraVisible</span><span class="se">\n</span><span class="s2">    slug</span><span class="se">\n</span><span class="s2">    navItems {</span><span class="se">\n</span><span class="s2">      tagSlug</span><span class="se">\n</span><span class="s2">      title</span><span class="se">\n</span><span class="s2">      url</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  ... on User {</span><span class="se">\n</span><span class="s2">    customDomainState {</span><span class="se">\n</span><span class="s2">      live {</span><span class="se">\n</span><span class="s2">        domain</span><span class="se">\n</span><span class="s2">        __typename</span><span class="se">\n</span><span class="s2">      }</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    hasSubdomain</span><span class="se">\n</span><span class="s2">    username</span><span class="se">\n</span><span class="s2">    about</span><span class="se">\n</span><span class="s2">    homePostsPublished: homepagePostsConnection(paging: {limit: 1}) {</span><span class="se">\n</span><span class="s2">      posts {</span><span class="se">\n</span><span class="s2">        id</span><span class="se">\n</span><span class="s2">        __typename</span><span class="se">\n</span><span class="s2">      }</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment PublisherHeaderNavLink_headerNavigationItem on HeaderNavigationItem {</span><span class="se">\n</span><span class="s2">  href</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  tags {</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">    normalizedTagSlug</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  type</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment PublisherHeaderNavLink_publisher on Publisher {</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  ... on Collection {</span><span class="se">\n</span><span class="s2">    slug</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment PublisherHomePosts_publisher on Publisher {</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  homepagePostsConnection(</span><span class="se">\n</span><span class="s2">    paging: {limit: $homepagePostsLimit, from: $homepagePostsFrom}</span><span class="se">\n</span><span class="s2">    includeDistributedResponses: $includeDistributedResponses</span><span class="se">\n</span><span class="s2">  ) {</span><span class="se">\n</span><span class="s2">    posts {</span><span class="se">\n</span><span class="s2">      ...PublisherHomePosts_post</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    pagingInfo {</span><span class="se">\n</span><span class="s2">      next {</span><span class="se">\n</span><span class="s2">        from</span><span class="se">\n</span><span class="s2">        limit</span><span class="se">\n</span><span class="s2">        __typename</span><span class="se">\n</span><span class="s2">      }</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  ...CardByline_publisher</span><span class="se">\n</span><span class="s2">  ...NewsletterV3Promo_publisher</span><span class="se">\n</span><span class="s2">  ...PublisherHomePosts_user</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment PublisherHomePosts_post on Post {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  collection {</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">    name</span><span class="se">\n</span><span class="s2">    ...collectionUrl_collection</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  ...ExpandablePost_post</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment ExpandablePost_post on Post {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  creator {</span><span class="se">\n</span><span class="s2">    ...ExpandablePost_user</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  collection {</span><span class="se">\n</span><span class="s2">    ...CardByline_collection</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  ...InteractivePostBody_postPreview</span><span class="se">\n</span><span class="s2">  firstPublishedAt</span><span class="se">\n</span><span class="s2">  isLocked</span><span class="se">\n</span><span class="s2">  isSeries</span><span class="se">\n</span><span class="s2">  isShortform</span><span class="se">\n</span><span class="s2">  latestPublishedAt</span><span class="se">\n</span><span class="s2">  inResponseToCatalogResult {</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  mediumUrl</span><span class="se">\n</span><span class="s2">  postResponses {</span><span class="se">\n</span><span class="s2">    count</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  previewImage {</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">    focusPercentX</span><span class="se">\n</span><span class="s2">    focusPercentY</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  readingTime</span><span class="se">\n</span><span class="s2">  sequence {</span><span class="se">\n</span><span class="s2">    slug</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  title</span><span class="se">\n</span><span class="s2">  uniqueSlug</span><span class="se">\n</span><span class="s2">  visibility</span><span class="se">\n</span><span class="s2">  ...CardByline_post</span><span class="se">\n</span><span class="s2">  ...ExpandablePostFooter_post</span><span class="se">\n</span><span class="s2">  ...InResponseToEntityPreview_post</span><span class="se">\n</span><span class="s2">  ...PostScrollTracker_post</span><span class="se">\n</span><span class="s2">  ...ReadMore_post</span><span class="se">\n</span><span class="s2">  ...HighDensityPreview_post</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment ExpandablePost_user on User {</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  username</span><span class="se">\n</span><span class="s2">  ...CardByline_user</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment CardByline_user on User {</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  username</span><span class="se">\n</span><span class="s2">  mediumMemberAt</span><span class="se">\n</span><span class="s2">  socialStats {</span><span class="se">\n</span><span class="s2">    followerCount</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  ...userUrl_user</span><span class="se">\n</span><span class="s2">  ...UserMentionTooltip_user</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment UserMentionTooltip_user on User {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  username</span><span class="se">\n</span><span class="s2">  bio</span><span class="se">\n</span><span class="s2">  imageId</span><span class="se">\n</span><span class="s2">  mediumMemberAt</span><span class="se">\n</span><span class="s2">  ...UserAvatar_user</span><span class="se">\n</span><span class="s2">  ...UserFollowButton_user</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment CardByline_collection on Collection {</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  ...collectionUrl_collection</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment InteractivePostBody_postPreview on Post {</span><span class="se">\n</span><span class="s2">  extendedPreviewContent(</span><span class="se">\n</span><span class="s2">    truncationConfig: {previewParagraphsWordCountThreshold: 400, minimumWordLengthForTruncation: 150, truncateAtEndOfSentence: true, showFullImageCaptions: true, shortformPreviewParagraphsWordCountThreshold: 30, shortformMinimumWordLengthForTruncation: 30}</span><span class="se">\n</span><span class="s2">  ) {</span><span class="se">\n</span><span class="s2">    bodyModel {</span><span class="se">\n</span><span class="s2">      ...PostBody_bodyModel</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    isFullContent</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment PostBody_bodyModel on RichText {</span><span class="se">\n</span><span class="s2">  sections {</span><span class="se">\n</span><span class="s2">    name</span><span class="se">\n</span><span class="s2">    startIndex</span><span class="se">\n</span><span class="s2">    textLayout</span><span class="se">\n</span><span class="s2">    imageLayout</span><span class="se">\n</span><span class="s2">    backgroundImage {</span><span class="se">\n</span><span class="s2">      id</span><span class="se">\n</span><span class="s2">      originalHeight</span><span class="se">\n</span><span class="s2">      originalWidth</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    videoLayout</span><span class="se">\n</span><span class="s2">    backgroundVideo {</span><span class="se">\n</span><span class="s2">      videoId</span><span class="se">\n</span><span class="s2">      originalHeight</span><span class="se">\n</span><span class="s2">      originalWidth</span><span class="se">\n</span><span class="s2">      previewImageId</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  paragraphs {</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">    ...PostBodySection_paragraph</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  ...normalizedBodyModel_richText</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment PostBodySection_paragraph on Paragraph {</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  ...PostBodyParagraph_paragraph</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment PostBodyParagraph_paragraph on Paragraph {</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  type</span><span class="se">\n</span><span class="s2">  ...ImageParagraph_paragraph</span><span class="se">\n</span><span class="s2">  ...TextParagraph_paragraph</span><span class="se">\n</span><span class="s2">  ...IframeParagraph_paragraph</span><span class="se">\n</span><span class="s2">  ...MixtapeParagraph_paragraph</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment ImageParagraph_paragraph on Paragraph {</span><span class="se">\n</span><span class="s2">  href</span><span class="se">\n</span><span class="s2">  layout</span><span class="se">\n</span><span class="s2">  metadata {</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">    originalHeight</span><span class="se">\n</span><span class="s2">    originalWidth</span><span class="se">\n</span><span class="s2">    focusPercentX</span><span class="se">\n</span><span class="s2">    focusPercentY</span><span class="se">\n</span><span class="s2">    alt</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  ...Markups_paragraph</span><span class="se">\n</span><span class="s2">  ...ParagraphRefsMapContext_paragraph</span><span class="se">\n</span><span class="s2">  ...PostAnnotationsMarker_paragraph</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment Markups_paragraph on Paragraph {</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  text</span><span class="se">\n</span><span class="s2">  hasDropCap</span><span class="se">\n</span><span class="s2">  dropCapImage {</span><span class="se">\n</span><span class="s2">    ...MarkupNode_data_dropCapImage</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  markups {</span><span class="se">\n</span><span class="s2">    type</span><span class="se">\n</span><span class="s2">    start</span><span class="se">\n</span><span class="s2">    end</span><span class="se">\n</span><span class="s2">    href</span><span class="se">\n</span><span class="s2">    anchorType</span><span class="se">\n</span><span class="s2">    userId</span><span class="se">\n</span><span class="s2">    linkMetadata {</span><span class="se">\n</span><span class="s2">      httpStatus</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment MarkupNode_data_dropCapImage on ImageMetadata {</span><span class="se">\n</span><span class="s2">  ...DropCap_image</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment DropCap_image on ImageMetadata {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  originalHeight</span><span class="se">\n</span><span class="s2">  originalWidth</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment ParagraphRefsMapContext_paragraph on Paragraph {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  text</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment PostAnnotationsMarker_paragraph on Paragraph {</span><span class="se">\n</span><span class="s2">  ...PostViewNoteCard_paragraph</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment PostViewNoteCard_paragraph on Paragraph {</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment TextParagraph_paragraph on Paragraph {</span><span class="se">\n</span><span class="s2">  type</span><span class="se">\n</span><span class="s2">  hasDropCap</span><span class="se">\n</span><span class="s2">  ...Markups_paragraph</span><span class="se">\n</span><span class="s2">  ...ParagraphRefsMapContext_paragraph</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment IframeParagraph_paragraph on Paragraph {</span><span class="se">\n</span><span class="s2">  iframe {</span><span class="se">\n</span><span class="s2">    mediaResource {</span><span class="se">\n</span><span class="s2">      id</span><span class="se">\n</span><span class="s2">      iframeSrc</span><span class="se">\n</span><span class="s2">      iframeHeight</span><span class="se">\n</span><span class="s2">      iframeWidth</span><span class="se">\n</span><span class="s2">      title</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  layout</span><span class="se">\n</span><span class="s2">  ...getEmbedlyCardUrlParams_paragraph</span><span class="se">\n</span><span class="s2">  ...Markups_paragraph</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment getEmbedlyCardUrlParams_paragraph on Paragraph {</span><span class="se">\n</span><span class="s2">  type</span><span class="se">\n</span><span class="s2">  iframe {</span><span class="se">\n</span><span class="s2">    mediaResource {</span><span class="se">\n</span><span class="s2">      iframeSrc</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment MixtapeParagraph_paragraph on Paragraph {</span><span class="se">\n</span><span class="s2">  type</span><span class="se">\n</span><span class="s2">  mixtapeMetadata {</span><span class="se">\n</span><span class="s2">    href</span><span class="se">\n</span><span class="s2">    mediaResource {</span><span class="se">\n</span><span class="s2">      mediumCatalog {</span><span class="se">\n</span><span class="s2">        id</span><span class="se">\n</span><span class="s2">        __typename</span><span class="se">\n</span><span class="s2">      }</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  ...GenericMixtapeParagraph_paragraph</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment GenericMixtapeParagraph_paragraph on Paragraph {</span><span class="se">\n</span><span class="s2">  text</span><span class="se">\n</span><span class="s2">  mixtapeMetadata {</span><span class="se">\n</span><span class="s2">    href</span><span class="se">\n</span><span class="s2">    thumbnailImageId</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  markups {</span><span class="se">\n</span><span class="s2">    start</span><span class="se">\n</span><span class="s2">    end</span><span class="se">\n</span><span class="s2">    type</span><span class="se">\n</span><span class="s2">    href</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment normalizedBodyModel_richText on RichText {</span><span class="se">\n</span><span class="s2">  paragraphs {</span><span class="se">\n</span><span class="s2">    markups {</span><span class="se">\n</span><span class="s2">      type</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    ...getParagraphHighlights_paragraph</span><span class="se">\n</span><span class="s2">    ...getParagraphPrivateNotes_paragraph</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  sections {</span><span class="se">\n</span><span class="s2">    startIndex</span><span class="se">\n</span><span class="s2">    ...getSectionEndIndex_section</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  ...getParagraphStyles_richText</span><span class="se">\n</span><span class="s2">  ...getParagraphSpaces_richText</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment getParagraphHighlights_paragraph on Paragraph {</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment getParagraphPrivateNotes_paragraph on Paragraph {</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment getSectionEndIndex_section on Section {</span><span class="se">\n</span><span class="s2">  startIndex</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment getParagraphStyles_richText on RichText {</span><span class="se">\n</span><span class="s2">  paragraphs {</span><span class="se">\n</span><span class="s2">    text</span><span class="se">\n</span><span class="s2">    type</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  sections {</span><span class="se">\n</span><span class="s2">    ...getSectionEndIndex_section</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment getParagraphSpaces_richText on RichText {</span><span class="se">\n</span><span class="s2">  paragraphs {</span><span class="se">\n</span><span class="s2">    layout</span><span class="se">\n</span><span class="s2">    metadata {</span><span class="se">\n</span><span class="s2">      originalHeight</span><span class="se">\n</span><span class="s2">      originalWidth</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    type</span><span class="se">\n</span><span class="s2">    ...paragraphExtendsImageGrid_paragraph</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  ...getSeriesParagraphTopSpacings_richText</span><span class="se">\n</span><span class="s2">  ...getPostParagraphTopSpacings_richText</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment paragraphExtendsImageGrid_paragraph on Paragraph {</span><span class="se">\n</span><span class="s2">  layout</span><span class="se">\n</span><span class="s2">  type</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment getSeriesParagraphTopSpacings_richText on RichText {</span><span class="se">\n</span><span class="s2">  paragraphs {</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  sections {</span><span class="se">\n</span><span class="s2">    startIndex</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment getPostParagraphTopSpacings_richText on RichText {</span><span class="se">\n</span><span class="s2">  paragraphs {</span><span class="se">\n</span><span class="s2">    layout</span><span class="se">\n</span><span class="s2">    text</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  sections {</span><span class="se">\n</span><span class="s2">    startIndex</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment CardByline_post on Post {</span><span class="se">\n</span><span class="s2">  ...DraftStatus_post</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment DraftStatus_post on Post {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  pendingCollection {</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">    creator {</span><span class="se">\n</span><span class="s2">      id</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    ...BoldCollectionName_collection</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  statusForCollection</span><span class="se">\n</span><span class="s2">  creator {</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  isPublished</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment BoldCollectionName_collection on Collection {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment ExpandablePostFooter_post on Post {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  allowResponses</span><span class="se">\n</span><span class="s2">  postResponses {</span><span class="se">\n</span><span class="s2">    count</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  isLimitedState</span><span class="se">\n</span><span class="s2">  ...ExpandablePostCardOverflowButton_post</span><span class="se">\n</span><span class="s2">  ...BookmarkButton_post</span><span class="se">\n</span><span class="s2">  ...PostFooterSocialPopover_post</span><span class="se">\n</span><span class="s2">  ...MultiVote_post</span><span class="se">\n</span><span class="s2">  ...OverflowMenuButtonWithNegativeSignal_post</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment ExpandablePostCardOverflowButton_post on Post {</span><span class="se">\n</span><span class="s2">  creator {</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  ...ExpandablePostCardEditorWriterButton_post</span><span class="se">\n</span><span class="s2">  ...ExpandablePostCardReaderButton_post</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment ExpandablePostCardEditorWriterButton_post on Post {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  collection {</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">    name</span><span class="se">\n</span><span class="s2">    slug</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  allowResponses</span><span class="se">\n</span><span class="s2">  clapCount</span><span class="se">\n</span><span class="s2">  visibility</span><span class="se">\n</span><span class="s2">  mediumUrl</span><span class="se">\n</span><span class="s2">  responseDistribution</span><span class="se">\n</span><span class="s2">  ...useIsPinnedInContext_post</span><span class="se">\n</span><span class="s2">  ...CopyFriendLinkMenuItem_post</span><span class="se">\n</span><span class="s2">  ...NewsletterV3EmailToSubscribersMenuItem_post</span><span class="se">\n</span><span class="s2">  ...OverflowMenuItemUndoClaps_post</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment useIsPinnedInContext_post on Post {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  collection {</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  pendingCollection {</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  pinnedAt</span><span class="se">\n</span><span class="s2">  pinnedByCreatorAt</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment CopyFriendLinkMenuItem_post on Post {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment NewsletterV3EmailToSubscribersMenuItem_post on Post {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  creator {</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">    newsletterV3 {</span><span class="se">\n</span><span class="s2">      id</span><span class="se">\n</span><span class="s2">      subscribersCount</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  isNewsletter</span><span class="se">\n</span><span class="s2">  isAuthorNewsletter</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment OverflowMenuItemUndoClaps_post on Post {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  clapCount</span><span class="se">\n</span><span class="s2">  ...ClapMutation_post</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment ClapMutation_post on Post {</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  clapCount</span><span class="se">\n</span><span class="s2">  ...MultiVoteCount_post</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment MultiVoteCount_post on Post {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  ...PostVotersNetwork_post</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment PostVotersNetwork_post on Post {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  voterCount</span><span class="se">\n</span><span class="s2">  recommenders {</span><span class="se">\n</span><span class="s2">    name</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment ExpandablePostCardReaderButton_post on Post {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  collection {</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  creator {</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  clapCount</span><span class="se">\n</span><span class="s2">  ...ClapMutation_post</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment BookmarkButton_post on Post {</span><span class="se">\n</span><span class="s2">  visibility</span><span class="se">\n</span><span class="s2">  ...SusiClickable_post</span><span class="se">\n</span><span class="s2">  ...AddToCatalogBookmarkButton_post</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment SusiClickable_post on Post {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  mediumUrl</span><span class="se">\n</span><span class="s2">  ...SusiContainer_post</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment SusiContainer_post on Post {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment AddToCatalogBookmarkButton_post on Post {</span><span class="se">\n</span><span class="s2">  ...AddToCatalogBase_post</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment AddToCatalogBase_post on Post {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment PostFooterSocialPopover_post on Post {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  mediumUrl</span><span class="se">\n</span><span class="s2">  title</span><span class="se">\n</span><span class="s2">  ...SharePostButton_post</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment SharePostButton_post on Post {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment MultiVote_post on Post {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  clapCount</span><span class="se">\n</span><span class="s2">  creator {</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">    ...SusiClickable_user</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  isPublished</span><span class="se">\n</span><span class="s2">  ...SusiClickable_post</span><span class="se">\n</span><span class="s2">  collection {</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">    slug</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  isLimitedState</span><span class="se">\n</span><span class="s2">  ...MultiVoteCount_post</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment OverflowMenuButtonWithNegativeSignal_post on Post {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  ...OverflowMenuWithNegativeSignal_post</span><span class="se">\n</span><span class="s2">  ...CreatorActionOverflowPopover_post</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment OverflowMenuWithNegativeSignal_post on Post {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  creator {</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  collection {</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  ...OverflowMenuItemUndoClaps_post</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment CreatorActionOverflowPopover_post on Post {</span><span class="se">\n</span><span class="s2">  allowResponses</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  statusForCollection</span><span class="se">\n</span><span class="s2">  isLocked</span><span class="se">\n</span><span class="s2">  isPublished</span><span class="se">\n</span><span class="s2">  clapCount</span><span class="se">\n</span><span class="s2">  mediumUrl</span><span class="se">\n</span><span class="s2">  pinnedAt</span><span class="se">\n</span><span class="s2">  pinnedByCreatorAt</span><span class="se">\n</span><span class="s2">  curationEligibleAt</span><span class="se">\n</span><span class="s2">  mediumUrl</span><span class="se">\n</span><span class="s2">  responseDistribution</span><span class="se">\n</span><span class="s2">  visibility</span><span class="se">\n</span><span class="s2">  inResponseToPostResult {</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  inResponseToCatalogResult {</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  pendingCollection {</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">    name</span><span class="se">\n</span><span class="s2">    creator {</span><span class="se">\n</span><span class="s2">      id</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    avatar {</span><span class="se">\n</span><span class="s2">      id</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    domain</span><span class="se">\n</span><span class="s2">    slug</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  creator {</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">    ...MutePopoverOptions_creator</span><span class="se">\n</span><span class="s2">    ...auroraHooks_publisher</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  collection {</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">    name</span><span class="se">\n</span><span class="s2">    creator {</span><span class="se">\n</span><span class="s2">      id</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    avatar {</span><span class="se">\n</span><span class="s2">      id</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    domain</span><span class="se">\n</span><span class="s2">    slug</span><span class="se">\n</span><span class="s2">    ...MutePopoverOptions_collection</span><span class="se">\n</span><span class="s2">    ...auroraHooks_publisher</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  ...useIsPinnedInContext_post</span><span class="se">\n</span><span class="s2">  ...NewsletterV3EmailToSubscribersMenuItem_post</span><span class="se">\n</span><span class="s2">  ...OverflowMenuItemUndoClaps_post</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment auroraHooks_publisher on Publisher {</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  ... on Collection {</span><span class="se">\n</span><span class="s2">    isAuroraEligible</span><span class="se">\n</span><span class="s2">    isAuroraVisible</span><span class="se">\n</span><span class="s2">    viewerEdge {</span><span class="se">\n</span><span class="s2">      id</span><span class="se">\n</span><span class="s2">      isEditor</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  ... on User {</span><span class="se">\n</span><span class="s2">    isAuroraVisible</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment InResponseToEntityPreview_post on Post {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  inResponseToEntityType</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment PostScrollTracker_post on Post {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  collection {</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  sequence {</span><span class="se">\n</span><span class="s2">    sequenceId</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment ReadMore_post on Post {</span><span class="se">\n</span><span class="s2">  mediumUrl</span><span class="se">\n</span><span class="s2">  readingTime</span><span class="se">\n</span><span class="s2">  ...usePostUrl_post</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment usePostUrl_post on Post {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  creator {</span><span class="se">\n</span><span class="s2">    ...userUrl_user</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  collection {</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">    domain</span><span class="se">\n</span><span class="s2">    slug</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  isSeries</span><span class="se">\n</span><span class="s2">  mediumUrl</span><span class="se">\n</span><span class="s2">  sequence {</span><span class="se">\n</span><span class="s2">    slug</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  uniqueSlug</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment HighDensityPreview_post on Post {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  title</span><span class="se">\n</span><span class="s2">  previewImage {</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">    focusPercentX</span><span class="se">\n</span><span class="s2">    focusPercentY</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  extendedPreviewContent(</span><span class="se">\n</span><span class="s2">    truncationConfig: {previewParagraphsWordCountThreshold: 400, minimumWordLengthForTruncation: 150, truncateAtEndOfSentence: true, showFullImageCaptions: true, shortformPreviewParagraphsWordCountThreshold: 30, shortformMinimumWordLengthForTruncation: 30}</span><span class="se">\n</span><span class="s2">  ) {</span><span class="se">\n</span><span class="s2">    subtitle</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  ...HighDensityFooter_post</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment HighDensityFooter_post on Post {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  readingTime</span><span class="se">\n</span><span class="s2">  tags {</span><span class="se">\n</span><span class="s2">    ...TopicPill_tag</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  ...BookmarkButton_post</span><span class="se">\n</span><span class="s2">  ...ExpandablePostCardOverflowButton_post</span><span class="se">\n</span><span class="s2">  ...OverflowMenuButtonWithNegativeSignal_post</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment TopicPill_tag on Tag {</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  displayTitle</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment CardByline_publisher on Publisher {</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  ... on User {</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">    ...CardByline_user</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  ... on Collection {</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">    ...CardByline_collection</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment NewsletterV3Promo_publisher on Publisher {</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  ... on User {</span><span class="se">\n</span><span class="s2">    ...NewsletterV3Promo_publisher_User</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  ... on Collection {</span><span class="se">\n</span><span class="s2">    ...NewsletterV3Promo_publisher_Collection</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment NewsletterV3Promo_publisher_User on User {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  username</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  viewerIsUser</span><span class="se">\n</span><span class="s2">  newsletterV3 {</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">    ...NewsletterV3Promo_newsletterV3</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment NewsletterV3Promo_newsletterV3 on NewsletterV3 {</span><span class="se">\n</span><span class="s2">  slug</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  description</span><span class="se">\n</span><span class="s2">  promoHeadline</span><span class="se">\n</span><span class="s2">  promoBody</span><span class="se">\n</span><span class="s2">  ...NewsletterV3AmpButton_newsletterV3</span><span class="se">\n</span><span class="s2">  ...NewsletterV3SubscribeButton_newsletterV3</span><span class="se">\n</span><span class="s2">  ...NewsletterV3SubscribeByEmail_newsletterV3</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment NewsletterV3AmpButton_newsletterV3 on NewsletterV3 {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  collection {</span><span class="se">\n</span><span class="s2">    ...collectionDefaultBackgroundTheme_collection</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment collectionDefaultBackgroundTheme_collection on Collection {</span><span class="se">\n</span><span class="s2">  colorPalette {</span><span class="se">\n</span><span class="s2">    ...collectionDefaultBackgroundTheme_colorPalette</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  customStyleSheet {</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">    ...collectionDefaultBackgroundTheme_customStyleSheet</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment collectionDefaultBackgroundTheme_colorPalette on ColorPalette {</span><span class="se">\n</span><span class="s2">  ...customDefaultBackgroundTheme_colorPalette</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment customDefaultBackgroundTheme_colorPalette on ColorPalette {</span><span class="se">\n</span><span class="s2">  highlightSpectrum {</span><span class="se">\n</span><span class="s2">    ...ThemeUtil_colorSpectrum</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  defaultBackgroundSpectrum {</span><span class="se">\n</span><span class="s2">    ...ThemeUtil_colorSpectrum</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  tintBackgroundSpectrum {</span><span class="se">\n</span><span class="s2">    ...ThemeUtil_colorSpectrum</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment collectionDefaultBackgroundTheme_customStyleSheet on CustomStyleSheet {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  ...customDefaultBackgroundTheme_customStyleSheet</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment customDefaultBackgroundTheme_customStyleSheet on CustomStyleSheet {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  global {</span><span class="se">\n</span><span class="s2">    colorPalette {</span><span class="se">\n</span><span class="s2">      primary {</span><span class="se">\n</span><span class="s2">        colorPalette {</span><span class="se">\n</span><span class="s2">          ...customDefaultBackgroundTheme_colorPalette</span><span class="se">\n</span><span class="s2">          __typename</span><span class="se">\n</span><span class="s2">        }</span><span class="se">\n</span><span class="s2">        __typename</span><span class="se">\n</span><span class="s2">      }</span><span class="se">\n</span><span class="s2">      background {</span><span class="se">\n</span><span class="s2">        colorPalette {</span><span class="se">\n</span><span class="s2">          ...customDefaultBackgroundTheme_colorPalette</span><span class="se">\n</span><span class="s2">          __typename</span><span class="se">\n</span><span class="s2">        }</span><span class="se">\n</span><span class="s2">        __typename</span><span class="se">\n</span><span class="s2">      }</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment NewsletterV3SubscribeButton_newsletterV3 on NewsletterV3 {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  slug</span><span class="se">\n</span><span class="s2">  type</span><span class="se">\n</span><span class="s2">  user {</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">    name</span><span class="se">\n</span><span class="s2">    username</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  collection {</span><span class="se">\n</span><span class="s2">    slug</span><span class="se">\n</span><span class="s2">    ...SusiClickable_collection</span><span class="se">\n</span><span class="s2">    ...collectionDefaultBackgroundTheme_collection</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  ...SusiClickable_newsletterV3</span><span class="se">\n</span><span class="s2">  ...useNewsletterV3Subscription_newsletterV3</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment SusiClickable_newsletterV3 on NewsletterV3 {</span><span class="se">\n</span><span class="s2">  ...SusiContainer_newsletterV3</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment SusiContainer_newsletterV3 on NewsletterV3 {</span><span class="se">\n</span><span class="s2">  ...SignInOptions_newsletterV3</span><span class="se">\n</span><span class="s2">  ...SignUpOptions_newsletterV3</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment SignInOptions_newsletterV3 on NewsletterV3 {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment SignUpOptions_newsletterV3 on NewsletterV3 {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment NewsletterV3SubscribeByEmail_newsletterV3 on NewsletterV3 {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  slug</span><span class="se">\n</span><span class="s2">  type</span><span class="se">\n</span><span class="s2">  user {</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">    name</span><span class="se">\n</span><span class="s2">    username</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  collection {</span><span class="se">\n</span><span class="s2">    ...collectionDefaultBackgroundTheme_collection</span><span class="se">\n</span><span class="s2">    ...collectionUrl_collection</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment NewsletterV3Promo_publisher_Collection on Collection {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  slug</span><span class="se">\n</span><span class="s2">  domain</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  newsletterV3 {</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">    ...NewsletterV3Promo_newsletterV3</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment PublisherHomePosts_user on User {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  ...useShowAuthorNewsletterV3Promo_user</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment useShowAuthorNewsletterV3Promo_user on User {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  username</span><span class="se">\n</span><span class="s2">  newsletterV3 {</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">    showPromo</span><span class="se">\n</span><span class="s2">    slug</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment UserSubdomainFlow_user on User {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  hasCompletedProfile</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  bio</span><span class="se">\n</span><span class="s2">  imageId</span><span class="se">\n</span><span class="s2">  ...UserCompleteProfileDialog_user</span><span class="se">\n</span><span class="s2">  ...UserSubdomainOnboardingDialog_user</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment UserCompleteProfileDialog_user on User {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  bio</span><span class="se">\n</span><span class="s2">  imageId</span><span class="se">\n</span><span class="s2">  hasCompletedProfile</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment UserSubdomainOnboardingDialog_user on User {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  customDomainState {</span><span class="se">\n</span><span class="s2">    pending {</span><span class="se">\n</span><span class="s2">      status</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    live {</span><span class="se">\n</span><span class="s2">      status</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  username</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment UserProfileMetadata_user on User {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  username</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  bio</span><span class="se">\n</span><span class="s2">  socialStats {</span><span class="se">\n</span><span class="s2">    followerCount</span><span class="se">\n</span><span class="s2">    followingCount</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  ...userUrl_user</span><span class="se">\n</span><span class="s2">  ...UserProfileMetadataHelmet_user</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment UserProfileMetadataHelmet_user on User {</span><span class="se">\n</span><span class="s2">  username</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  imageId</span><span class="se">\n</span><span class="s2">  twitterScreenName</span><span class="se">\n</span><span class="s2">  navItems {</span><span class="se">\n</span><span class="s2">    title</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment SuspendedBannerLoader_user on User {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  isSuspended</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment useAnalytics_user on User {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  imageId</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  username</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment EntityDrivenSubscriptionLandingPageScreen_writer on User {</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  imageId</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  username</span><span class="se">\n</span><span class="s2">  isPartnerProgramEnrolled</span><span class="se">\n</span><span class="s2">  customStyleSheet {</span><span class="se">\n</span><span class="s2">    ...CustomThemeProvider_customStyleSheet</span><span class="se">\n</span><span class="s2">    ...CustomBackgroundWrapper_customStyleSheet</span><span class="se">\n</span><span class="s2">    ...MetaHeader_customStyleSheet</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  ...MetaHeader_publisher</span><span class="se">\n</span><span class="s2">  ...userUrl_user</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment CustomThemeProvider_customStyleSheet on CustomStyleSheet {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  ...customDefaultBackgroundTheme_customStyleSheet</span><span class="se">\n</span><span class="s2">  ...customStyleSheetFontTheme_customStyleSheet</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment customStyleSheetFontTheme_customStyleSheet on CustomStyleSheet {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  global {</span><span class="se">\n</span><span class="s2">    fonts {</span><span class="se">\n</span><span class="s2">      font1 {</span><span class="se">\n</span><span class="s2">        name</span><span class="se">\n</span><span class="s2">        __typename</span><span class="se">\n</span><span class="s2">      }</span><span class="se">\n</span><span class="s2">      font2 {</span><span class="se">\n</span><span class="s2">        name</span><span class="se">\n</span><span class="s2">        __typename</span><span class="se">\n</span><span class="s2">      }</span><span class="se">\n</span><span class="s2">      font3 {</span><span class="se">\n</span><span class="s2">        name</span><span class="se">\n</span><span class="s2">        __typename</span><span class="se">\n</span><span class="s2">      }</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment CustomBackgroundWrapper_customStyleSheet on CustomStyleSheet {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  global {</span><span class="se">\n</span><span class="s2">    colorPalette {</span><span class="se">\n</span><span class="s2">      background {</span><span class="se">\n</span><span class="s2">        ...getHexFromColorValue_colorValue</span><span class="se">\n</span><span class="s2">        __typename</span><span class="se">\n</span><span class="s2">      }</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment MetaHeader_customStyleSheet on CustomStyleSheet {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  header {</span><span class="se">\n</span><span class="s2">    headerScale</span><span class="se">\n</span><span class="s2">    horizontalAlignment</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  ...MetaHeaderBackground_customStyleSheet</span><span class="se">\n</span><span class="s2">  ...MetaHeaderEngagement_customStyleSheet</span><span class="se">\n</span><span class="s2">  ...MetaHeaderLogo_customStyleSheet</span><span class="se">\n</span><span class="s2">  ...MetaHeaderNavVertical_customStyleSheet</span><span class="se">\n</span><span class="s2">  ...MetaHeaderTagline_customStyleSheet</span><span class="se">\n</span><span class="s2">  ...MetaHeaderThemeProvider_customStyleSheet</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment MetaHeaderBackground_customStyleSheet on CustomStyleSheet {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  header {</span><span class="se">\n</span><span class="s2">    headerScale</span><span class="se">\n</span><span class="s2">    backgroundImageDisplayMode</span><span class="se">\n</span><span class="s2">    backgroundImageVerticalAlignment</span><span class="se">\n</span><span class="s2">    backgroundColorDisplayMode</span><span class="se">\n</span><span class="s2">    backgroundColor {</span><span class="se">\n</span><span class="s2">      ...getHexFromColorValue_colorValue</span><span class="se">\n</span><span class="s2">      ...getOpaqueHexFromColorValue_colorValue</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    secondaryBackgroundColor {</span><span class="se">\n</span><span class="s2">      ...getHexFromColorValue_colorValue</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    postBackgroundColor {</span><span class="se">\n</span><span class="s2">      ...getHexFromColorValue_colorValue</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    backgroundImage {</span><span class="se">\n</span><span class="s2">      ...MetaHeaderBackground_imageMetadata</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment MetaHeaderEngagement_customStyleSheet on CustomStyleSheet {</span><span class="se">\n</span><span class="s2">  ...MetaHeaderNav_customStyleSheet</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment MetaHeaderNav_customStyleSheet on CustomStyleSheet {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  navigation {</span><span class="se">\n</span><span class="s2">    navItems {</span><span class="se">\n</span><span class="s2">      ...MetaHeaderNav_headerNavigationItem</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment MetaHeaderNav_headerNavigationItem on HeaderNavigationItem {</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  tagSlugs</span><span class="se">\n</span><span class="s2">  ...MetaHeaderNavLink_headerNavigationItem</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment MetaHeaderNavLink_headerNavigationItem on HeaderNavigationItem {</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  ...getNavItemHref_headerNavigationItem</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment getNavItemHref_headerNavigationItem on HeaderNavigationItem {</span><span class="se">\n</span><span class="s2">  href</span><span class="se">\n</span><span class="s2">  type</span><span class="se">\n</span><span class="s2">  tags {</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">    normalizedTagSlug</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment MetaHeaderLogo_customStyleSheet on CustomStyleSheet {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  header {</span><span class="se">\n</span><span class="s2">    nameColor {</span><span class="se">\n</span><span class="s2">      ...getHexFromColorValue_colorValue</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    nameTreatment</span><span class="se">\n</span><span class="s2">    postNameTreatment</span><span class="se">\n</span><span class="s2">    logoImage {</span><span class="se">\n</span><span class="s2">      ...MetaHeaderLogo_imageMetadata</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    logoScale</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment MetaHeaderLogo_imageMetadata on ImageMetadata {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  originalWidth</span><span class="se">\n</span><span class="s2">  originalHeight</span><span class="se">\n</span><span class="s2">  ...PublisherLogo_image</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment PublisherLogo_image on ImageMetadata {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  originalHeight</span><span class="se">\n</span><span class="s2">  originalWidth</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment MetaHeaderNavVertical_customStyleSheet on CustomStyleSheet {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  navigation {</span><span class="se">\n</span><span class="s2">    navItems {</span><span class="se">\n</span><span class="s2">      ...MetaHeaderNavLink_headerNavigationItem</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  ...MetaHeaderNav_customStyleSheet</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment MetaHeaderTagline_customStyleSheet on CustomStyleSheet {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  header {</span><span class="se">\n</span><span class="s2">    taglineColor {</span><span class="se">\n</span><span class="s2">      ...getHexFromColorValue_colorValue</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    taglineTreatment</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment MetaHeaderThemeProvider_customStyleSheet on CustomStyleSheet {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  ...useMetaHeaderTheme_customStyleSheet</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment useMetaHeaderTheme_customStyleSheet on CustomStyleSheet {</span><span class="se">\n</span><span class="s2">  ...customDefaultBackgroundTheme_customStyleSheet</span><span class="se">\n</span><span class="s2">  global {</span><span class="se">\n</span><span class="s2">    colorPalette {</span><span class="se">\n</span><span class="s2">      primary {</span><span class="se">\n</span><span class="s2">        colorPalette {</span><span class="se">\n</span><span class="s2">          tintBackgroundSpectrum {</span><span class="se">\n</span><span class="s2">            ...ThemeUtil_colorSpectrum</span><span class="se">\n</span><span class="s2">            __typename</span><span class="se">\n</span><span class="s2">          }</span><span class="se">\n</span><span class="s2">          __typename</span><span class="se">\n</span><span class="s2">        }</span><span class="se">\n</span><span class="s2">        __typename</span><span class="se">\n</span><span class="s2">      }</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  header {</span><span class="se">\n</span><span class="s2">    backgroundColor {</span><span class="se">\n</span><span class="s2">      colorPalette {</span><span class="se">\n</span><span class="s2">        tintBackgroundSpectrum {</span><span class="se">\n</span><span class="s2">          ...ThemeUtil_colorSpectrum</span><span class="se">\n</span><span class="s2">          __typename</span><span class="se">\n</span><span class="s2">        }</span><span class="se">\n</span><span class="s2">        __typename</span><span class="se">\n</span><span class="s2">      }</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    postBackgroundColor {</span><span class="se">\n</span><span class="s2">      colorPalette {</span><span class="se">\n</span><span class="s2">        tintBackgroundSpectrum {</span><span class="se">\n</span><span class="s2">          ...ThemeUtil_colorSpectrum</span><span class="se">\n</span><span class="s2">          __typename</span><span class="se">\n</span><span class="s2">        }</span><span class="se">\n</span><span class="s2">        __typename</span><span class="se">\n</span><span class="s2">      }</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    backgroundImage {</span><span class="se">\n</span><span class="s2">      id</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment MetaHeader_publisher on Publisher {</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  ...MetaHeaderEngagement_publisher</span><span class="se">\n</span><span class="s2">  ...MetaHeaderLogo_publisher</span><span class="se">\n</span><span class="s2">  ...MetaHeaderNavVertical_publisher</span><span class="se">\n</span><span class="s2">  ...MetaHeaderTagline_publisher</span><span class="se">\n</span><span class="s2">  ...MetaHeaderThemeProvider_publisher</span><span class="se">\n</span><span class="s2">  ...MetaHeaderActions_publisher</span><span class="se">\n</span><span class="s2">  ...MetaHeaderTop_publisher</span><span class="se">\n</span><span class="s2">  ...MetaHeaderNavLink_publisher</span><span class="se">\n</span><span class="s2">  ... on Collection {</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">    favicon {</span><span class="se">\n</span><span class="s2">      id</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    tagline</span><span class="se">\n</span><span class="s2">    ...CollectionNavigationContextProvider_collection</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  ... on User {</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">    bio</span><span class="se">\n</span><span class="s2">    ...UserProfileCatalogsLink_publisher</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment MetaHeaderEngagement_publisher on Publisher {</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  ...MetaHeaderNav_publisher</span><span class="se">\n</span><span class="s2">  ...PublisherAboutLink_publisher</span><span class="se">\n</span><span class="s2">  ...PublisherFollowButton_publisher</span><span class="se">\n</span><span class="s2">  ...PublisherFollowerCount_publisher</span><span class="se">\n</span><span class="s2">  ... on Collection {</span><span class="se">\n</span><span class="s2">    creator {</span><span class="se">\n</span><span class="s2">      id</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    customStyleSheet {</span><span class="se">\n</span><span class="s2">      id</span><span class="se">\n</span><span class="s2">      ...CustomThemeProvider_customStyleSheet</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  ... on User {</span><span class="se">\n</span><span class="s2">    ...UserProfileCatalogsLink_publisher</span><span class="se">\n</span><span class="s2">    ...UserSubscribeButton_user</span><span class="se">\n</span><span class="s2">    customStyleSheet {</span><span class="se">\n</span><span class="s2">      id</span><span class="se">\n</span><span class="s2">      ...CustomThemeProvider_customStyleSheet</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment MetaHeaderNav_publisher on Publisher {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  ...MetaHeaderNavLink_publisher</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment MetaHeaderNavLink_publisher on Publisher {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  ...getNavItemHref_publisher</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment getNavItemHref_publisher on Publisher {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  ...publisherUrl_publisher</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment PublisherAboutLink_publisher on Publisher {</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  ... on Collection {</span><span class="se">\n</span><span class="s2">    slug</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  ... on User {</span><span class="se">\n</span><span class="s2">    ...userUrl_user</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment PublisherFollowButton_publisher on Publisher {</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  ... on Collection {</span><span class="se">\n</span><span class="s2">    ...CollectionFollowButton_collection</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  ... on User {</span><span class="se">\n</span><span class="s2">    ...UserFollowButton_user</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment UserProfileCatalogsLink_publisher on Publisher {</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  ... on User {</span><span class="se">\n</span><span class="s2">    ...userUrl_user</span><span class="se">\n</span><span class="s2">    homePostsPublished: homepagePostsConnection(paging: {limit: 1}) {</span><span class="se">\n</span><span class="s2">      posts {</span><span class="se">\n</span><span class="s2">        id</span><span class="se">\n</span><span class="s2">        __typename</span><span class="se">\n</span><span class="s2">      }</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment MetaHeaderLogo_publisher on Publisher {</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  ... on Collection {</span><span class="se">\n</span><span class="s2">    logo {</span><span class="se">\n</span><span class="s2">      ...MetaHeaderLogo_imageMetadata</span><span class="se">\n</span><span class="s2">      ...PublisherLogo_image</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">      id</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  ...auroraHooks_publisher</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment MetaHeaderNavVertical_publisher on Publisher {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  ...PublisherAboutLink_publisher</span><span class="se">\n</span><span class="s2">  ...MetaHeaderNav_publisher</span><span class="se">\n</span><span class="s2">  ...MetaHeaderNavLink_publisher</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment MetaHeaderTagline_publisher on Publisher {</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  ... on Collection {</span><span class="se">\n</span><span class="s2">    tagline</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  ... on User {</span><span class="se">\n</span><span class="s2">    bio</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment MetaHeaderThemeProvider_publisher on Publisher {</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  customStyleSheet {</span><span class="se">\n</span><span class="s2">    ...MetaHeaderThemeProvider_customStyleSheet</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  ... on Collection {</span><span class="se">\n</span><span class="s2">    colorPalette {</span><span class="se">\n</span><span class="s2">      ...customDefaultBackgroundTheme_colorPalette</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment MetaHeaderActions_publisher on Publisher {</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  ...MetaHeaderPubMenu_publisher</span><span class="se">\n</span><span class="s2">  ...SearchWidget_publisher</span><span class="se">\n</span><span class="s2">  ... on Collection {</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">    creator {</span><span class="se">\n</span><span class="s2">      id</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    customStyleSheet {</span><span class="se">\n</span><span class="s2">      navigation {</span><span class="se">\n</span><span class="s2">        navItems {</span><span class="se">\n</span><span class="s2">          name</span><span class="se">\n</span><span class="s2">          __typename</span><span class="se">\n</span><span class="s2">        }</span><span class="se">\n</span><span class="s2">        __typename</span><span class="se">\n</span><span class="s2">      }</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">      id</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    ...CollectionAvatar_collection</span><span class="se">\n</span><span class="s2">    ...CollectionMetabarActionsPopover_collection</span><span class="se">\n</span><span class="s2">    ...MetaHeaderActions_collection_common</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  ... on User {</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">    ...UserAvatar_user</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment SearchWidget_publisher on Publisher {</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  ... on Collection {</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">    slug</span><span class="se">\n</span><span class="s2">    name</span><span class="se">\n</span><span class="s2">    domain</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  ... on User {</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">    name</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  ...algoliaSearch_publisher</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment algoliaSearch_publisher on Publisher {</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment CollectionMetabarActionsPopover_collection on Collection {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  slug</span><span class="se">\n</span><span class="s2">  isAuroraEligible</span><span class="se">\n</span><span class="s2">  isAuroraVisible</span><span class="se">\n</span><span class="s2">  newsletterV3 {</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">    slug</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  ...collectionUrl_collection</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment MetaHeaderActions_collection_common on Collection {</span><span class="se">\n</span><span class="s2">  creator {</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment MetaHeaderTop_publisher on Publisher {</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  ... on Collection {</span><span class="se">\n</span><span class="s2">    slug</span><span class="se">\n</span><span class="s2">    ...CollectionMetabarActionsPopover_collection</span><span class="se">\n</span><span class="s2">    ...CollectionAvatar_collection</span><span class="se">\n</span><span class="s2">    ...MetaHeaderTop_collection</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  ... on User {</span><span class="se">\n</span><span class="s2">    username</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment MetaHeaderTop_collection on Collection {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  creator {</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment CollectionNavigationContextProvider_collection on Collection {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  domain</span><span class="se">\n</span><span class="s2">  slug</span><span class="se">\n</span><span class="s2">  isAuroraVisible</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment useShouldShowEntityDrivenSubscription_creator on User {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n</span><span class="s2">"</span><span class="w">
      </span><span class="p">}</span><span class="w">
</span><span class="p">]</span><span class="w">
</span></code></pre></div></div>
<ul>
  <li><code class="language-plaintext highlighter-rouge">id</code> : 帳號對應的 User Id，上面那個取得追蹤者的 Endpoint 有給</li>
  <li><code class="language-plaintext highlighter-rouge">homepagePostsFrom</code> : 分頁參數，可參考回應中的 <code class="language-plaintext highlighter-rouge">from</code> 帶入取得所有分頁資料</li>
</ul>

<p><a href="https://www.postman.com/" target="_blank"><strong>Postman</strong></a> <strong>:</strong></p>

<p><img src="/assets/88f0fb935120/1*dw61NwISVGRbFLlie4Na2w.webp" alt="" loading="lazy" decoding="async" width="1200" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjEwMjQiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><strong>Response:</strong></p>

<p><img src="/assets/88f0fb935120/1*FEt2xWEjJMqpWjxYVUgVWg.webp" alt="" loading="lazy" decoding="async" width="1070" height="998" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDcwIiBoZWlnaHQ9Ijk5OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>在 <code class="language-plaintext highlighter-rouge">response[0]["data"]["userResult"]["homepagePostsConnection"]["posts"]</code> 可以取得文章列表資訊。</p>
<h4 id="medium-private-api--取得文章內容">Medium Private API — 取得文章內容</h4>

<p>再來就是最關鍵的，爬取文章原始內容。</p>

<p>Graphql Query Body:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">[</span><span class="w">
      </span><span class="p">{</span><span class="w">
        </span><span class="nl">"operationName"</span><span class="p">:</span><span class="w"> </span><span class="s2">"PostViewerEdgeContentQuery"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"variables"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"postId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"c008a9e8ceca"</span><span class="w">
        </span><span class="p">},</span><span class="w">
        </span><span class="nl">"query"</span><span class="p">:</span><span class="w"> </span><span class="s2">"query PostViewerEdgeContentQuery($postId: ID!, $postMeteringOptions: PostMeteringOptions) {</span><span class="se">\n</span><span class="s2">  post(id: $postId) {</span><span class="se">\n</span><span class="s2">    ... on Post {</span><span class="se">\n</span><span class="s2">      id</span><span class="se">\n</span><span class="s2">      viewerEdge {</span><span class="se">\n</span><span class="s2">        id</span><span class="se">\n</span><span class="s2">        fullContent(postMeteringOptions: $postMeteringOptions) {</span><span class="se">\n</span><span class="s2">          isLockedPreviewOnly</span><span class="se">\n</span><span class="s2">          validatedShareKey</span><span class="se">\n</span><span class="s2">          bodyModel {</span><span class="se">\n</span><span class="s2">            ...PostBody_bodyModel</span><span class="se">\n</span><span class="s2">            __typename</span><span class="se">\n</span><span class="s2">          }</span><span class="se">\n</span><span class="s2">          __typename</span><span class="se">\n</span><span class="s2">        }</span><span class="se">\n</span><span class="s2">        __typename</span><span class="se">\n</span><span class="s2">      }</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment PostBody_bodyModel on RichText {</span><span class="se">\n</span><span class="s2">  sections {</span><span class="se">\n</span><span class="s2">    name</span><span class="se">\n</span><span class="s2">    startIndex</span><span class="se">\n</span><span class="s2">    textLayout</span><span class="se">\n</span><span class="s2">    imageLayout</span><span class="se">\n</span><span class="s2">    backgroundImage {</span><span class="se">\n</span><span class="s2">      id</span><span class="se">\n</span><span class="s2">      originalHeight</span><span class="se">\n</span><span class="s2">      originalWidth</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    videoLayout</span><span class="se">\n</span><span class="s2">    backgroundVideo {</span><span class="se">\n</span><span class="s2">      videoId</span><span class="se">\n</span><span class="s2">      originalHeight</span><span class="se">\n</span><span class="s2">      originalWidth</span><span class="se">\n</span><span class="s2">      previewImageId</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  paragraphs {</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">    ...PostBodySection_paragraph</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  ...normalizedBodyModel_richText</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment PostBodySection_paragraph on Paragraph {</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  ...PostBodyParagraph_paragraph</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment PostBodyParagraph_paragraph on Paragraph {</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  type</span><span class="se">\n</span><span class="s2">  ...ImageParagraph_paragraph</span><span class="se">\n</span><span class="s2">  ...TextParagraph_paragraph</span><span class="se">\n</span><span class="s2">  ...IframeParagraph_paragraph</span><span class="se">\n</span><span class="s2">  ...MixtapeParagraph_paragraph</span><span class="se">\n</span><span class="s2">  ...CodeBlockParagraph_paragraph</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment ImageParagraph_paragraph on Paragraph {</span><span class="se">\n</span><span class="s2">  href</span><span class="se">\n</span><span class="s2">  layout</span><span class="se">\n</span><span class="s2">  metadata {</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">    originalHeight</span><span class="se">\n</span><span class="s2">    originalWidth</span><span class="se">\n</span><span class="s2">    focusPercentX</span><span class="se">\n</span><span class="s2">    focusPercentY</span><span class="se">\n</span><span class="s2">    alt</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  ...Markups_paragraph</span><span class="se">\n</span><span class="s2">  ...ParagraphRefsMapContext_paragraph</span><span class="se">\n</span><span class="s2">  ...PostAnnotationsMarker_paragraph</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment Markups_paragraph on Paragraph {</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  text</span><span class="se">\n</span><span class="s2">  hasDropCap</span><span class="se">\n</span><span class="s2">  dropCapImage {</span><span class="se">\n</span><span class="s2">    ...MarkupNode_data_dropCapImage</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  markups {</span><span class="se">\n</span><span class="s2">    type</span><span class="se">\n</span><span class="s2">    start</span><span class="se">\n</span><span class="s2">    end</span><span class="se">\n</span><span class="s2">    href</span><span class="se">\n</span><span class="s2">    anchorType</span><span class="se">\n</span><span class="s2">    userId</span><span class="se">\n</span><span class="s2">    linkMetadata {</span><span class="se">\n</span><span class="s2">      httpStatus</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment MarkupNode_data_dropCapImage on ImageMetadata {</span><span class="se">\n</span><span class="s2">  ...DropCap_image</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment DropCap_image on ImageMetadata {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  originalHeight</span><span class="se">\n</span><span class="s2">  originalWidth</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment ParagraphRefsMapContext_paragraph on Paragraph {</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  text</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment PostAnnotationsMarker_paragraph on Paragraph {</span><span class="se">\n</span><span class="s2">  ...PostViewNoteCard_paragraph</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment PostViewNoteCard_paragraph on Paragraph {</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment TextParagraph_paragraph on Paragraph {</span><span class="se">\n</span><span class="s2">  type</span><span class="se">\n</span><span class="s2">  hasDropCap</span><span class="se">\n</span><span class="s2">  codeBlockMetadata {</span><span class="se">\n</span><span class="s2">    mode</span><span class="se">\n</span><span class="s2">    lang</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  ...Markups_paragraph</span><span class="se">\n</span><span class="s2">  ...ParagraphRefsMapContext_paragraph</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment IframeParagraph_paragraph on Paragraph {</span><span class="se">\n</span><span class="s2">  iframe {</span><span class="se">\n</span><span class="s2">    mediaResource {</span><span class="se">\n</span><span class="s2">      id</span><span class="se">\n</span><span class="s2">      iframeSrc</span><span class="se">\n</span><span class="s2">      iframeHeight</span><span class="se">\n</span><span class="s2">      iframeWidth</span><span class="se">\n</span><span class="s2">      title</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  layout</span><span class="se">\n</span><span class="s2">  ...getEmbedlyCardUrlParams_paragraph</span><span class="se">\n</span><span class="s2">  ...Markups_paragraph</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment getEmbedlyCardUrlParams_paragraph on Paragraph {</span><span class="se">\n</span><span class="s2">  type</span><span class="se">\n</span><span class="s2">  iframe {</span><span class="se">\n</span><span class="s2">    mediaResource {</span><span class="se">\n</span><span class="s2">      iframeSrc</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment MixtapeParagraph_paragraph on Paragraph {</span><span class="se">\n</span><span class="s2">  type</span><span class="se">\n</span><span class="s2">  mixtapeMetadata {</span><span class="se">\n</span><span class="s2">    href</span><span class="se">\n</span><span class="s2">    mediaResource {</span><span class="se">\n</span><span class="s2">      mediumCatalog {</span><span class="se">\n</span><span class="s2">        id</span><span class="se">\n</span><span class="s2">        __typename</span><span class="se">\n</span><span class="s2">      }</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  ...GenericMixtapeParagraph_paragraph</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment GenericMixtapeParagraph_paragraph on Paragraph {</span><span class="se">\n</span><span class="s2">  text</span><span class="se">\n</span><span class="s2">  mixtapeMetadata {</span><span class="se">\n</span><span class="s2">    href</span><span class="se">\n</span><span class="s2">    thumbnailImageId</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  markups {</span><span class="se">\n</span><span class="s2">    start</span><span class="se">\n</span><span class="s2">    end</span><span class="se">\n</span><span class="s2">    type</span><span class="se">\n</span><span class="s2">    href</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment CodeBlockParagraph_paragraph on Paragraph {</span><span class="se">\n</span><span class="s2">  codeBlockMetadata {</span><span class="se">\n</span><span class="s2">    lang</span><span class="se">\n</span><span class="s2">    mode</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment normalizedBodyModel_richText on RichText {</span><span class="se">\n</span><span class="s2">  paragraphs {</span><span class="se">\n</span><span class="s2">    markups {</span><span class="se">\n</span><span class="s2">      type</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    codeBlockMetadata {</span><span class="se">\n</span><span class="s2">      lang</span><span class="se">\n</span><span class="s2">      mode</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    ...getParagraphHighlights_paragraph</span><span class="se">\n</span><span class="s2">    ...getParagraphPrivateNotes_paragraph</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  sections {</span><span class="se">\n</span><span class="s2">    startIndex</span><span class="se">\n</span><span class="s2">    ...getSectionEndIndex_section</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  ...getParagraphStyles_richText</span><span class="se">\n</span><span class="s2">  ...getParagraphSpaces_richText</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment getParagraphHighlights_paragraph on Paragraph {</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment getParagraphPrivateNotes_paragraph on Paragraph {</span><span class="se">\n</span><span class="s2">  name</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment getSectionEndIndex_section on Section {</span><span class="se">\n</span><span class="s2">  startIndex</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment getParagraphStyles_richText on RichText {</span><span class="se">\n</span><span class="s2">  paragraphs {</span><span class="se">\n</span><span class="s2">    text</span><span class="se">\n</span><span class="s2">    type</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  sections {</span><span class="se">\n</span><span class="s2">    ...getSectionEndIndex_section</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment getParagraphSpaces_richText on RichText {</span><span class="se">\n</span><span class="s2">  paragraphs {</span><span class="se">\n</span><span class="s2">    layout</span><span class="se">\n</span><span class="s2">    metadata {</span><span class="se">\n</span><span class="s2">      originalHeight</span><span class="se">\n</span><span class="s2">      originalWidth</span><span class="se">\n</span><span class="s2">      id</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    type</span><span class="se">\n</span><span class="s2">    ...paragraphExtendsImageGrid_paragraph</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  ...getSeriesParagraphTopSpacings_richText</span><span class="se">\n</span><span class="s2">  ...getPostParagraphTopSpacings_richText</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment paragraphExtendsImageGrid_paragraph on Paragraph {</span><span class="se">\n</span><span class="s2">  layout</span><span class="se">\n</span><span class="s2">  type</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">  id</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment getSeriesParagraphTopSpacings_richText on RichText {</span><span class="se">\n</span><span class="s2">  paragraphs {</span><span class="se">\n</span><span class="s2">    id</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  sections {</span><span class="se">\n</span><span class="s2">    startIndex</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n\n</span><span class="s2">fragment getPostParagraphTopSpacings_richText on RichText {</span><span class="se">\n</span><span class="s2">  paragraphs {</span><span class="se">\n</span><span class="s2">    layout</span><span class="se">\n</span><span class="s2">    text</span><span class="se">\n</span><span class="s2">    codeBlockMetadata {</span><span class="se">\n</span><span class="s2">      lang</span><span class="se">\n</span><span class="s2">      mode</span><span class="se">\n</span><span class="s2">      __typename</span><span class="se">\n</span><span class="s2">    }</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  sections {</span><span class="se">\n</span><span class="s2">    startIndex</span><span class="se">\n</span><span class="s2">    __typename</span><span class="se">\n</span><span class="s2">  }</span><span class="se">\n</span><span class="s2">  __typename</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n</span><span class="s2">"</span><span class="w">
      </span><span class="p">}</span><span class="w">
</span><span class="p">]</span><span class="w">
</span></code></pre></div></div>
<ul>
  <li><code class="language-plaintext highlighter-rouge">posdId</code> : 文章 ID，上面取得文章列表的 API 有給。</li>
</ul>

<p><a href="https://www.postman.com/" target="_blank"><strong>Postman</strong></a> <strong>:</strong></p>

<p><img src="/assets/88f0fb935120/1*K7ldG92qvdNIMG89NJC3nw.webp" alt="" loading="lazy" decoding="async" width="1200" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjEwMjQiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><strong>Response:</strong></p>

<p><img src="/assets/88f0fb935120/1*r3va7Mjc0DTD3pAYkDsozQ.webp" alt="" loading="lazy" decoding="async" width="987" height="1060" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5ODciIGhlaWdodD0iMTA2MCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>文章 Source 如上圖：</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">response[0]["data"]["post"]["viewerEdge"]["fullContent"]["bodyModel"]["paragraphs"]</code> :整篇文章段落，組合起來就是全篇文章</li>
  <li><code class="language-plaintext highlighter-rouge">["markups"]</code> : 段落的文字樣式，例如粗體、超連結…</li>
</ul>

<blockquote>
  <p><strong><em>至於如何把這個 JSON 描述格式轉換成 Markdown 可以直接參考或使用我之前開發的開源工具 — <a href="https://github.com/ZhgChgLi/ZMediumToMarkdown" target="_blank">ZMediumToMarkdown</a></em></strong></p>
</blockquote>

<p><a href="https://github.com/ZhgChgLi/ZMediumToMarkdown" target="_blank"><img src="https://repository-images.githubusercontent.com/493527574/9b5b7025-cc95-4e81-84a9-b38706093c27" alt="" /></a></p>

<ul>
  <li>如果要爬取自己的付費文章需要帶上 sid, uid 登入權杖資訊 (參考下文)</li>
</ul>

<h3 id="medium-x-cloudflare-攻防">Medium x Cloudflare 攻防</h3>

<p>本篇文章的第二個主題，就是 Cloudflare，大約在 2025 下半年開始，Medium 的 Cloudflare 防護設定幾乎到了最高等級，所有來自雲服務的請求都會被阻擋(我用 Google Apps Script / GitHub Actions 全都會被擋)，導致爬取資料失敗。</p>

<p><strong>阻擋訊息：403</strong></p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">&lt;!DOCTYPE html&gt;</span><span class="nt">&lt;html</span> <span class="na">lang=</span><span class="s">"en-US"</span><span class="nt">&gt;&lt;head&gt;&lt;title&gt;</span>Just a moment...<span class="nt">&lt;/title&gt;&lt;meta</span> <span class="na">http-equiv=</span><span class="s">"Content-Type"</span> <span class="na">content=</span><span class="s">"text/html; charset=UTF-8"</span><span class="nt">&gt;</span>....
</code></pre></div></div>

<p>這讓我非常苦惱，因為我的鏡像站 ( <a href="https://zhgchg.li" target="_blank">https://zhgchg.li</a> ) 已經穩定運作了好幾年，只要 Medium 有新文章發佈，那邊就會定時自動執行腳本同步過去 (透過 Private API Graphql)， <strong>如果雲端服務會被阻擋，我就只能手動在本地電腦執行腳本</strong> 。</p>
<h4 id="header-cookies-多加上-sid-uid-"><strong>Header Cookies 多加上 sid, uid ❌</strong></h4>

<p><strong>最一開始我在 Header Cookies 多加上 sid, uid 等於是 Medium 登入身份，可以順利通過，但過了幾個月之後連這個方法也行不通了。</strong></p>

<p><img src="/assets/88f0fb935120/1*nKrcxHJP80VHkDUnQLEFFg.webp" alt="" loading="lazy" decoding="async" width="1200" height="401" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjQwMSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">uid</code> : 你的使用者 ID</li>
  <li><code class="language-plaintext highlighter-rouge">sid</code> : 你的 access token <strong>(務必保密)</strong></li>
</ul>

<h4 id="cloudflare-worker-橋接請求-">Cloudflare Worker 橋接請求 ✅</h4>

<p>亂試一通後找不到方法，突發奇想用 Cloudflare 的魔法打敗魔法，用 Cloudflare，沒想到就成功了！不會被 Cloudflare Bot 防護阻擋。</p>

<p><strong>Demo Code:</strong></p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">export</span> <span class="k">default</span> <span class="p">{</span>

  <span class="k">async</span> <span class="nf">fetch</span><span class="p">(</span><span class="nx">request</span><span class="p">,</span> <span class="nx">env</span><span class="p">,</span> <span class="nx">ctx</span><span class="p">)</span> <span class="p">{</span>

    <span class="kd">const</span> <span class="nx">url</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">URL</span><span class="p">(</span><span class="nx">request</span><span class="p">.</span><span class="nx">url</span><span class="p">);</span>
    <span class="kd">const</span> <span class="nx">path</span> <span class="o">=</span> <span class="nx">url</span><span class="p">.</span><span class="nx">pathname</span><span class="p">;</span>

    <span class="k">if </span><span class="p">(</span><span class="nx">path</span> <span class="o">==</span> <span class="dl">"</span><span class="s2">/graphql</span><span class="dl">"</span><span class="p">)</span> <span class="p">{</span>
      <span class="kd">let</span> <span class="nx">body</span><span class="p">;</span>
      <span class="k">try</span> <span class="p">{</span>
        <span class="nx">body</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">request</span><span class="p">.</span><span class="nf">json</span><span class="p">();</span>
      <span class="p">}</span> <span class="k">catch</span> <span class="p">{</span>
        <span class="k">return</span> <span class="k">new</span> <span class="nc">Response</span><span class="p">(</span><span class="dl">"</span><span class="s2">Invalid JSON body</span><span class="dl">"</span><span class="p">,</span> <span class="p">{</span> <span class="na">status</span><span class="p">:</span> <span class="mi">400</span> <span class="p">});</span>
      <span class="p">}</span>

      <span class="kd">let</span> <span class="nx">apiURL</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">https://medium.com/_/graphql</span><span class="dl">"</span><span class="p">;</span>

      <span class="kd">const</span> <span class="nx">apiResponse</span> <span class="o">=</span> <span class="k">await</span> <span class="nf">fetch</span><span class="p">(</span><span class="nx">apiURL</span><span class="p">,</span> <span class="p">{</span>
        <span class="na">method</span><span class="p">:</span> <span class="dl">"</span><span class="s2">POST</span><span class="dl">"</span><span class="p">,</span>
        <span class="na">headers</span><span class="p">:</span> <span class="p">{</span>
          <span class="dl">"</span><span class="s2">Accept</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">application/json</span><span class="dl">"</span><span class="p">,</span>
          <span class="dl">"</span><span class="s2">Content-Type</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">application/json</span><span class="dl">"</span><span class="p">,</span>
          <span class="dl">"</span><span class="s2">User-Agent</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36 Edg/142.0.0.0</span><span class="dl">"</span><span class="p">,</span>
          <span class="dl">"</span><span class="s2">Cookie</span><span class="dl">"</span><span class="p">:</span> <span class="nx">request</span><span class="p">.</span><span class="nx">headers</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="dl">"</span><span class="s2">cookie</span><span class="dl">"</span><span class="p">)</span>
        <span class="p">},</span>
        <span class="na">body</span><span class="p">:</span> <span class="nx">JSON</span><span class="p">.</span><span class="nf">stringify</span><span class="p">(</span><span class="nx">body</span><span class="p">),</span>
      <span class="p">});</span>

      <span class="kd">const</span> <span class="nx">json</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">apiResponse</span><span class="p">.</span><span class="nf">json</span><span class="p">();</span>
      <span class="k">return</span> <span class="k">new</span> <span class="nc">Response</span><span class="p">(</span><span class="nx">JSON</span><span class="p">.</span><span class="nf">stringify</span><span class="p">(</span><span class="nx">json</span><span class="p">),</span> <span class="p">{</span>
        <span class="na">status</span><span class="p">:</span> <span class="nx">apiResponse</span><span class="p">.</span><span class="nx">status</span><span class="p">,</span>
        <span class="na">headers</span><span class="p">:</span> <span class="p">{</span>
          <span class="dl">"</span><span class="s2">Content-Type</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">application/json; charset=utf-8</span><span class="dl">"</span><span class="p">,</span>
        <span class="p">},</span>
      <span class="p">});</span>
    <span class="p">}</span>

    <span class="k">return</span> <span class="k">new</span> <span class="nc">Response</span><span class="p">(</span><span class="dl">"</span><span class="s2">Not Found</span><span class="dl">"</span><span class="p">,</span> <span class="p">{</span> <span class="na">status</span><span class="p">:</span> <span class="mi">404</span> <span class="p">});</span>
  <span class="p">},</span>
<span class="p">};</span>
</code></pre></div></div>

<p>Request:</p>

<p>從 <code class="language-plaintext highlighter-rouge">https://medium.com/_/graphql</code> 改成你部署的 Cloudflare Worker URL 即可。</p>
<h4 id="總結">總結</h4>

<p>以上就是我跟 Medium API 搏鬥這麼多年的心路歷程，目前所有的文章 (Markdown 檔)及圖片都有備份到 <a href="https://zhgchg.li" target="_blank">https://zhgchg.li</a> 包含所有附加圖片檔案，GitHub Repo 有一份、電腦硬碟也有一份，非常安全。</p>

<blockquote>
  <p>還是希望 Medium 能長長久久！本文內容只供實驗參考，作者不負任何使用責任。</p>
</blockquote>]]></content>
  </entry><entry>
    <title type="html">App Store Connect API Webhook 串接｜提升 iOS CI/CD 自動化效率與通知流程</title>
    <link href="https://zhgchg.li/posts/zrealm-dev/app-store-connect-api-webhook-%E4%B8%B2%E6%8E%A5-%E6%8F%90%E5%8D%87-ios-ci-cd-%E8%87%AA%E5%8B%95%E5%8C%96%E6%95%88%E7%8E%87%E8%88%87%E9%80%9A%E7%9F%A5%E6%B5%81%E7%A8%8B-7c0974856393/" rel="alternate" type="text/html" title="App Store Connect API Webhook 串接｜提升 iOS CI/CD 自動化效率與通知流程" />
    <published>2025-12-27T17:02:01+08:00</published>
    <updated>2025-12-27T17:05:14+08:00</updated>
    <id>https://zhgchg.li/posts/zrealm-dev/7c0974856393</id><summary type="html">針對 iOS 開發者面臨的建置等待與通知延遲問題，透過 App Store Connect API Webhook 實現即時事件推送，結合 Fastlane 與 CI/CD 工具，打造零等待成本的自動化工作流程，提升團隊開發效率與發布準確度。</summary><author>
      <name>ZhgChgLi</name>
    </author><category term="ZRealm Dev." /><category term="ios-app-development" /><category term="cicd" /><category term="webhooks" /><category term="fastlane" /><category term="app-store" /><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://zhgchg.li/assets/7c0974856393/1*IqytjX72CmAx9WOx4Rm3gg.webp" /><content type="html" xml:base="https://zhgchg.li/posts/zrealm-dev/app-store-connect-api-webhook-%E4%B8%B2%E6%8E%A5-%E6%8F%90%E5%8D%87-ios-ci-cd-%E8%87%AA%E5%8B%95%E5%8C%96%E6%95%88%E7%8E%87%E8%88%87%E9%80%9A%E7%9F%A5%E6%B5%81%E7%A8%8B-7c0974856393/"><![CDATA[<h3 id="cicd-使用-app-store-connect-api-webhook-串接自動化工作流程">[CI/CD] 使用 App Store Connect API Webhook 串接自動化工作流程</h3>

<p>App Store Connect Webhook 應用案例分析與實際串接使用教學。</p>

<p><img src="/assets/7c0974856393/1*IqytjX72CmAx9WOx4Rm3gg.webp" alt="Photo by Volodymyr Hryshchenko" loading="lazy" decoding="async" width="5472" height="3648" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1NDcyIiBoZWlnaHQ9IjM2NDgiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>Photo by <a href="https://unsplash.com/@lunarts?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" target="_blank">Volodymyr Hryshchenko</a></p>
<h4 id="前言">前言</h4>

<p>蘋果近年持續擴充 App Store Connect API，對開發者是一大福音。早期連憑證管理都得靠「Hardcore」的 Web Session（有期限、還要收簡訊驗證碼），很難整合進 CI/CD；像商城評價也只能依賴不穩定的 RSS。</p>

<p>這幾年幾乎每年都有新功能補齊，從開發、測試到部署流程，甚至後期的評價、財務、數據報表都逐步原生支援，另外也強化了使用者管理、群組、TestFlight 等能力；讓 <strong>App Store Connect API 可以更好的提升 Apple 開發者開發體驗</strong> 。</p>

<blockquote>
  <p><em>延伸閱讀：「 <a href="/posts/zrealm-dev/app-store-connect-api-快速讀取與管理-customer-reviews-提升-ios-app-評價整合效率-f1365e51902c/">App Store Connect API 現已支援 讀取和管理 Customer Reviews</a> 」</em></p>
</blockquote>

<h4 id="wwdc-2025-automate-your-development-process-with-the-app-store-connect-api"><a href="https://developer.apple.com/videos/play/wwdc2025/324/" target="_blank">WWDC 2025 Automate your development process with the App Store Connect API</a></h4>

<p><a href="https://developer.apple.com/videos/play/wwdc2025/324/" target="_blank"><img src="https://devimages-cdn.apple.com/wwdc-services/images/3055294D-836B-4513-B7B0-0BC5666246B0/9859/9859_wide_250x141_2x.jpg" alt="" /></a></p>

<p><strong>2025 WWDC 更帶來引頸期盼的重磅功能 — Webhook 通知：</strong></p>
<ul>
  <li><strong>建置版本上傳狀態 (The <a href="https://developer.apple.com/help/app-store-connect/reference/app-uploads/build-upload-statuses" target="_blank">status of a build upload</a> changes.)</strong> 
當建置版本上傳狀態更改時收到相關資料。
<code class="language-plaintext highlighter-rouge">Complete / Failed / Processing</code></li>
  <li><strong>App 版本狀態 (The <a href="https://developer.apple.com/help/app-store-connect/reference/app-information/app-and-submission-statuses#app-statuses" target="_blank">status of an app version</a> changes.)</strong> 
當 App 版本狀態更改時收到相關資料。
<code class="language-plaintext highlighter-rouge">Prepare for Submission / Ready for Review / Waiting for Review / Ready for Distribution / Rejected…</code></li>
  <li><strong>TestFlight 版本狀態 (New <a href="https://developer.apple.com/help/app-store-connect/test-a-beta-version/view-tester-feedback" target="_blank">TestFlight feedback</a> is submitted by a tester.)</strong> 
當測試人員留下回饋(當機回饋/截圖回饋)時收到相關資料。</li>
  <li><strong>Apple-hosted 的資源包狀態改變 (The <a href="https://developer.apple.com/help/app-store-connect/reference/app-uploads/apple-hosted-asset-pack-statuses" target="_blank">status of an Apple-hosted asset pack version</a> changes.)</strong> 
當 Apple 託管的素材包版本出現特定變更時收到相關資料。</li>
</ul>

<h4 id="app-store-connect-api--webhook-notifications-"><a href="https://developer.apple.com/documentation/AppStoreConnectAPI/webhook-notifications" target="_blank">App Store Connect API / Webhook notifications</a> :</h4>

<blockquote>
  <p><em>Webhooks enable a system to send real-time data to another system over the web.</em></p>
</blockquote>

<blockquote>
  <p><em>Webhook 讓一個系統能夠透過網路即時將資料傳送給另一個系統。</em></p>
</blockquote>

<blockquote>
  <p><em>Unlike traditional APIs, where one system must make a request when receiving data, a webhook enables you to push data to the receiving system as soon as an event occurs.</em></p>
</blockquote>

<blockquote>
  <p><em>與傳統 API 不同的是，傳統 API 需要由接收資料的一方主動發送請求，而 Webhook 則能在事件發生的當下，立即將資料推送給接收系統。</em></p>
</blockquote>

<blockquote>
  <p><em>Webhooks are event-driven, meaning they are triggered by a specific action or event and immediately send the relevant data to a predefined URL, also called the “webhook URL” or “callback URL”.</em></p>
</blockquote>

<blockquote>
  <p><em>Webhook 是事件驅動的，表示它們會在特定動作或事件被觸發時，立即將相關資料傳送到一個預先設定的 URL，該 URL 也稱為「Webhook URL」或「Callback URL」。</em></p>
</blockquote>

<blockquote>
  <p><em>A notification webhook is an endpoint you create on your server.</em></p>
</blockquote>

<blockquote>
  <p><em>通知型 Webhook 是你在自己伺服器上建立的一個端點（endpoint）。</em></p>
</blockquote>

<blockquote>
  <p><em>This webhook endpoint receives HTTP POST requests from App Store Connect.</em></p>
</blockquote>

<blockquote>
  <p><em>這個 Webhook 端點會接收來自 App Store Connect 的 HTTP POST 請求。</em></p>
</blockquote>

<blockquote>
  <p><em>The POST requests describe important events about your app.</em></p>
</blockquote>

<blockquote>
  <p><em>這些 POST 請求會描述與你的 App 相關的重要事件。</em></p>
</blockquote>

<blockquote>
  <p><em>Use the webhooks notifications endpoint to configure the notifications for events happening to your apps.</em></p>
</blockquote>

<blockquote>
  <p><em>你可以使用 Webhook 通知端點，來設定當你的 App 發生各種事件時所要接收的通知。</em></p>
</blockquote>

<h3 id="5-個應用案例">5 個應用案例</h3>
<h4 id="1-建置處理完成後觸發送審">1. 建置處理完成後觸發送審</h4>

<p><strong>Before:</strong></p>

<p><img src="/assets/7c0974856393/1*usYZtMkzAu-bA4fVX8ZbUQ.webp" alt="" loading="lazy" decoding="async" width="1200" height="465" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjQ2NSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>以往在實現 App CI/CD 打包送審時，打包上傳後 <strong>需要等待 Apple 處理完成</strong> 才能繼續送審；Fastlane 默認的做法是 Polling App Store Connect 檢查上傳的建置狀態，直到 Complete 後才會繼續執行送審 Lane。</p>

<blockquote>
  <p><strong><em>等待時間大約 20 分鐘</em></strong> <em>，如果是 Self-hosted CI/CD 沒什麼差，但如果是使用雲端服務，這 20 分鐘的等待非常的浪費資源，以 GitHub Runner mac-OS 1 分鐘 US$ 0.062， <strong>光等待送審每次就產生無意義的花費 1.24 US$。</strong></em></p>
</blockquote>

<p><img src="/assets/7c0974856393/1*2-Zn2ApgVYd5S5KzxMLEMQ.webp" alt="Ref: Build Completed Processing 通知信 搭配 Gmail Filter + Google Apps Script" loading="lazy" decoding="async" width="694" height="340" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2OTQiIGhlaWdodD0iMzQwIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>Ref: <a href="/posts/zrealm-robotic-process-automation/google-apps-script-教學-自動轉發-gmail-郵件到-slack-channel-客製化通知-d414bdbdb8c9/">Build Completed Processing 通知信 搭配 Gmail Filter + Google Apps Script</a></p>

<p>在沒有 Webhook 可以主動通知的年代，之前曾用過「 <a href="/posts/zrealm-robotic-process-automation/google-apps-script-教學-自動轉發-gmail-郵件到-slack-channel-客製化通知-d414bdbdb8c9/">Build Completed Processing 通知信搭配 Gmail Filter + Google Apps Script 來觸發</a> 」可以達到效果，但是過程有點 Hardcore。</p>

<p><strong>After:</strong></p>

<p><img src="/assets/7c0974856393/1*ADkNoScFn4M_dIgv3sAKaw.webp" alt="" loading="lazy" decoding="async" width="1200" height="301" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjMwMSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<ul>
  <li>有了 Webhook 之後，打包上傳的步驟上傳完畢即可結束。</li>
  <li><strong>App Store Connect Build Process Complete 後會發 Webhook 通知，我們收到通知後再繼續執行送審步驟。</strong></li>
  <li><strong>零乾等成本</strong></li>
</ul>

<h4 id="2-gitflow-release-流程對齊-app-release-時機點">2. GitFlow Release 流程對齊 App Release 時機點</h4>

<p><strong>Before:</strong></p>

<blockquote>
  <p><em>GitFlow 最後一步上線時需要把 develop 分支 merge 回 master 分支，master 分支對應的是當前線上版。</em></p>
</blockquote>

<p>以往只能定時手動/自動執行，例如週一 PM 會發佈 App，週一固定把 develop 回 master；手動執行很煩、自動執行的話假設延期？週一剛好放假？實際 App 沒有 Release 但是 develop 先回 master 了。</p>

<p>在大多數情況不太重要，只有很極端的狀況，例如這時間中間又要插 Hotfix 就會有可能有落差；但若要追求完整穩定的 CI/CD 開發流程，這也是一個值得探討的案例。</p>

<p><strong>另一條思路同上，用「 <a href="/posts/zrealm-robotic-process-automation/google-apps-script-教學-自動轉發-gmail-郵件到-slack-channel-客製化通知-d414bdbdb8c9/">App is Ready for Sale 通知信搭配 Gmail Filter + Google Apps Script 來觸發</a> 」是可行的。</strong></p>

<p><strong>After:</strong></p>

<p><img src="/assets/7c0974856393/1*6JjHC6GKc4fKXrh-WWCJVA.webp" alt="" loading="lazy" decoding="async" width="1200" height="224" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjIyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<ul>
  <li>有了 Webhook 之後，可以直接在收到 App 發布通知後觸發 CI/CD Action (Master to Develop)。</li>
  <li>可以確保 App 是真的上線才回 Master</li>
</ul>

<h4 id="3-app-release-messages">3. App Release Messages</h4>

<blockquote>
  <p><em>App 上線發佈給使用者後，還有另一個很常見內部工作流程，發布通知給相關團隊、版本包含的任務、Complete 相關任務。</em></p>
</blockquote>

<p><strong>Before:</strong></p>

<p>同上，只能手動或定時自動或使用信件 <a href="/posts/zrealm-robotic-process-automation/google-apps-script-教學-自動轉發-gmail-郵件到-slack-channel-客製化通知-d414bdbdb8c9/"><strong>搭配 Gmail Filter + Google Apps Script 來觸發</strong></a> 。</p>

<p><strong>After:</strong></p>

<p><img src="/assets/7c0974856393/1*UuGPKonNhXRUMZ71rDQcdw.webp" alt="" loading="lazy" decoding="async" width="1200" height="443" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjQ0MyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<ul>
  <li>有了 Webhook 之後，可以在收到 App 發布通知後串接 Jira/Asana API 撈取對應版號的單批次 Complete 並且發佈包含的任務單完成釋出訊息到 Slack。</li>
</ul>

<h4 id="4-build-failed--review-rejected-notifier">4. Build Failed / Review Rejected Notifier</h4>

<blockquote>
  <p><em>前面 1,2 有提到以往有機會透過收信件通知來觸發工作流程，但在團隊規模很大權限控管嚴格的組織裡， <strong>iOS 開發者只會有「開發者」後台權限，無法釋出、管理 App，因此也無法收到任何 App 狀態改變的信件通知；其中也包含上傳的建置版本被拒或是送審被拒的通知信</strong> 。</em></p>
</blockquote>

<p><strong>Before:</strong></p>

<p>以往只能靠好心人 (a.k.a PM) 轉發信件給工程，如果好心人也沒注意到可能就真的到要上線時才發現 App 被拒絕了無法上線！</p>

<p><strong>After:</strong></p>

<p><img src="/assets/7c0974856393/1*unKq9zdc8tJEqlNmfXiRYA.webp" alt="" loading="lazy" decoding="async" width="1200" height="582" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjU4MiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<ul>
  <li>這個案例相對簡單，就是收到 Webhook 通知之後，轉發訊息到 Slack。</li>
</ul>

<h4 id="5-testflight-feedback-notifier">5. Testflight Feedback Notifier</h4>

<blockquote>
  <p><em>類似 4. 只是改接 Testflight Feedback Webhook 通知。</em></p>
</blockquote>

<p><strong>Before:</strong></p>

<p>以往開發者只能自己上去 App Store Connect Testflight 後台查看測試使用者的回饋或閃退問題， <strong>非常容易被忽略（甚至曾經發生過一年前回報的建議問題一年後才有人看到）</strong> 。</p>

<p><strong>After:</strong></p>
<ul>
  <li>收到 Testflight Feedback Webhook 通知之後，轉發訊息到 Slack 。</li>
</ul>

<p>— — —</p>

<blockquote>
  <p><em>其他應用方式歡迎大家天馬行空發想，再來將介紹如何串接使用。</em></p>
</blockquote>

<h3 id="app-store-connect-api-webhook-設定">App Store Connect API Webhook 設定</h3>

<blockquote>
  <p><em>權限要求： <strong>需要 Admin, Account Holder 權限才能設定</strong> 。</em></p>
</blockquote>

<h4 id="建立-app-store-connect-api-webhook-通知">建立 App Store Connect API Webhook 通知</h4>
<ol>
  <li><a href="https://appstoreconnect.apple.com/access/users" target="_blank">前往 App Store Connect 管理後台</a></li>
  <li>前往「使用者與存取權限 (Users and Access)」 -&gt; 「整合 (Integrations)」</li>
  <li>點擊「其他整合 (Additional)」 下的 「Webhooks」</li>
  <li>點擊「建立 Webhook」按鈕</li>
</ol>

<p><img src="/assets/7c0974856393/1*e_6oin7tJjAB2gXl3txJow.webp" alt="" loading="lazy" decoding="async" width="716" height="767" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3MTYiIGhlaWdodD0iNzY3Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<ul>
  <li>名稱：輸入 Webhook 名稱</li>
  <li>承載 URL(Payload URL)：輸入你的 Webhook 通知接收服務網址</li>
  <li>密鑰(Secret) 字串：Webhook Request 驗證密鑰(可 <a href="https://www.random.org/strings/?num=1&amp;len=32&amp;digits=on&amp;upperalpha=on&amp;loweralpha=on&amp;unique=on&amp;format=html&amp;rnd=new" target="_blank">隨機產生一個字串</a> 來用)</li>
  <li>App：選擇預接收 Webhook 通知的 App</li>
  <li><strong>觸發事件：</strong></li>
</ul>

<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">TestFlight</span> <span class="nt">回饋</span>
<span class="nt">當測試人員留下回饋時收到相關資料</span><span class="err">。</span>
<span class="o">[]</span> <span class="nt">當機回饋</span>
<span class="o">[]</span> <span class="nt">截圖回饋</span>

<span class="o">[]</span> <span class="nt">TestFlight</span> <span class="nt">版本狀態</span>
<span class="nt">當</span> <span class="nt">TestFlight</span> <span class="nt">版本狀態更改時收到相關資料</span><span class="err">。</span><span class="nt">進一步瞭解</span>

<span class="o">[]</span><span class="nt">App</span> <span class="nt">版本狀態</span>
<span class="nt">當</span> <span class="nt">App</span> <span class="nt">版本狀態更改時收到相關資料</span><span class="err">。</span><span class="nt">進一步瞭解</span>

<span class="o">[]</span><span class="nt">建置版本上傳狀態</span>
<span class="nt">當建置版本上傳狀態更改時收到相關資料</span><span class="err">。</span><span class="nt">進一步瞭解</span>

<span class="nt">背景素材</span>
<span class="nt">當</span> <span class="nt">Apple</span> <span class="nt">託管的素材包版本出現特定變更時收到相關資料</span><span class="err">。</span><span class="nt">進一步瞭解</span>

<span class="o">[]</span><span class="nt">更新App</span> <span class="nt">Store</span> <span class="nt">發佈版本</span>
<span class="o">[]</span><span class="nt">更新外部</span> <span class="nt">TestFlight</span> <span class="nt">發佈版本</span>
<span class="o">[]</span><span class="nt">建立內部</span> <span class="nt">TestFlight</span> <span class="nt">發佈版本</span>
<span class="o">[]</span><span class="nt">更新素材包版本</span>
</code></pre></div></div>

<p>依照需求勾選，也可以全選收到通知後再判斷要不要處理。</p>

<p><strong>最後點擊「新增」建立 Webhook。</strong></p>
<h4 id="測試-app-store-connect-api-webhook-通知">測試 App Store Connect API Webhook 通知</h4>

<p>進入 Webhook 頁。</p>

<p><img src="/assets/7c0974856393/1*-pEVZ24cpSPBabtzMwll4Q.webp" alt="" loading="lazy" decoding="async" width="979" height="548" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NzkiIGhlaWdodD0iNTQ4Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>點擊右上角「測試」接收測試通知。</p>

<p><strong>測試通知內容如下：</strong></p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">Headers:</span><span class="w">
</span><span class="p">{</span><span class="w">
  </span><span class="nl">"content-type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"application/json"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"x-apple-jingle-correlation-key"</span><span class="p">:</span><span class="w"> </span><span class="s2">"PNSCHDQW3MY2AX6VSRFHYYNUL4"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"x-apple-request-uuid"</span><span class="p">:</span><span class="w"> </span><span class="s2">"7b64238e-16db-31a0-5fd5-944a7c61b45f"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"x-apple-signature"</span><span class="p">:</span><span class="w"> </span><span class="s2">"hmacsha256=cf50020f0bbd3c5274860594f616f1806965c1f9fb765d8d278f512dff5b4c0e"</span><span class="p">,</span><span class="w">
</span><span class="p">}</span><span class="w">

</span><span class="err">Body:</span><span class="w">
</span><span class="p">{</span><span class="w">
  </span><span class="nl">"data"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"type"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"webhookPingCreated"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"id"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"65726e27-cb79-47f2-a3e4-c8ced9f356e8"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"version"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w">
    </span><span class="nl">"attributes"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"timestamp"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"2025-12-26T15:47:38.472168681Z"</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<h4 id="app-store-connect-api-webhook-通知發送紀錄">App Store Connect API Webhook 通知發送紀錄</h4>

<p>Webhook 頁下方的「最近的提交項目」會顯示最近發送的 Webhook 事件。</p>

<p><img src="/assets/7c0974856393/1*lacj5lpaDwJtN0hgA5EAVg.webp" alt="" loading="lazy" decoding="async" width="1200" height="502" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjUwMiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<h4 id="驗證-app-store-connect-api-webhook-通知">驗證 App Store Connect API Webhook 通知</h4>

<p>建立 Webhook 時我們有輸入一個「密鑰(Secret) 字串」建議對請求進行驗證，防止這個 Webhook URL 外洩，有心之人能隨意偽造發送 Webhook 事件到你的服務上。</p>

<p><strong>驗證方式：</strong></p>

<blockquote>
  <p><em>對 Request Body 用你設定的密鑰字串做 HMAC-SHA256 並輸出 HEX 進位字串，用這個字串比對 Request Headers 中的 <code class="language-plaintext highlighter-rouge">x-apple-signature</code> 值的 <code class="language-plaintext highlighter-rouge">hmacsha256=</code> 後面帶的字串。</em></p>
</blockquote>

<p><strong>實現方式 — Nodejs:</strong></p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="nx">crypto</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">crypto</span><span class="dl">'</span><span class="p">;</span>

<span class="kd">function</span> <span class="nf">verifyAppleWebhook</span><span class="p">(</span><span class="nx">rawBody</span><span class="p">,</span> <span class="nx">appleSignature</span><span class="p">,</span> <span class="nx">secret</span><span class="p">)</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">hex</span> <span class="o">=</span> <span class="nx">crypto</span>
    <span class="p">.</span><span class="nf">createHmac</span><span class="p">(</span><span class="dl">'</span><span class="s1">sha256</span><span class="dl">'</span><span class="p">,</span> <span class="nx">secret</span><span class="p">)</span>
    <span class="p">.</span><span class="nf">update</span><span class="p">(</span><span class="nx">rawBody</span><span class="p">,</span> <span class="dl">'</span><span class="s1">utf8</span><span class="dl">'</span><span class="p">)</span>
    <span class="p">.</span><span class="nf">digest</span><span class="p">(</span><span class="dl">'</span><span class="s1">hex</span><span class="dl">'</span><span class="p">);</span>

  <span class="k">return</span> <span class="s2">`hmacsha256=</span><span class="p">${</span><span class="nx">hex</span><span class="p">}</span><span class="s2">`</span> <span class="o">===</span> <span class="nx">appleSignature</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p><strong>實現方式 — Cloudflare Worker:</strong></p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nf">bufferToHex</span><span class="p">(</span><span class="nx">buffer</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">return</span> <span class="p">[...</span><span class="k">new</span> <span class="nc">Uint8Array</span><span class="p">(</span><span class="nx">buffer</span><span class="p">)]</span>
    <span class="p">.</span><span class="nf">map</span><span class="p">(</span><span class="nx">b</span> <span class="o">=&gt;</span> <span class="nx">b</span><span class="p">.</span><span class="nf">toString</span><span class="p">(</span><span class="mi">16</span><span class="p">).</span><span class="nf">padStart</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="dl">'</span><span class="s1">0</span><span class="dl">'</span><span class="p">))</span>
    <span class="p">.</span><span class="nf">join</span><span class="p">(</span><span class="dl">''</span><span class="p">);</span>
<span class="p">}</span>

<span class="k">async</span> <span class="kd">function</span> <span class="nf">hmacSha256Hex</span><span class="p">(</span><span class="nx">secret</span><span class="p">,</span> <span class="nx">message</span><span class="p">)</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">enc</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">TextEncoder</span><span class="p">();</span>

  <span class="kd">const</span> <span class="nx">key</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">crypto</span><span class="p">.</span><span class="nx">subtle</span><span class="p">.</span><span class="nf">importKey</span><span class="p">(</span>
    <span class="dl">'</span><span class="s1">raw</span><span class="dl">'</span><span class="p">,</span>
    <span class="nx">enc</span><span class="p">.</span><span class="nf">encode</span><span class="p">(</span><span class="nx">secret</span><span class="p">),</span>
    <span class="p">{</span> <span class="na">name</span><span class="p">:</span> <span class="dl">'</span><span class="s1">HMAC</span><span class="dl">'</span><span class="p">,</span> <span class="na">hash</span><span class="p">:</span> <span class="dl">'</span><span class="s1">SHA-256</span><span class="dl">'</span> <span class="p">},</span>
    <span class="kc">false</span><span class="p">,</span>
    <span class="p">[</span><span class="dl">'</span><span class="s1">sign</span><span class="dl">'</span><span class="p">]</span>
  <span class="p">);</span>

  <span class="kd">const</span> <span class="nx">signature</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">crypto</span><span class="p">.</span><span class="nx">subtle</span><span class="p">.</span><span class="nf">sign</span><span class="p">(</span>
    <span class="dl">'</span><span class="s1">HMAC</span><span class="dl">'</span><span class="p">,</span>
    <span class="nx">key</span><span class="p">,</span>
    <span class="nx">enc</span><span class="p">.</span><span class="nf">encode</span><span class="p">(</span><span class="nx">message</span><span class="p">)</span>
  <span class="p">);</span>

  <span class="k">return</span> <span class="nf">bufferToHex</span><span class="p">(</span><span class="nx">signature</span><span class="p">);</span>
<span class="p">}</span>

<span class="k">async</span> <span class="kd">function</span> <span class="nf">verifyAppleWebhook</span><span class="p">(</span><span class="nx">request</span><span class="p">,</span> <span class="nx">secret</span><span class="p">)</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">appleSignature</span> <span class="o">=</span> <span class="nx">request</span><span class="p">.</span><span class="nx">headers</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="dl">'</span><span class="s1">X-Apple-Signature</span><span class="dl">'</span><span class="p">);</span>

  <span class="kd">const</span> <span class="nx">rawBody</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">request</span><span class="p">.</span><span class="nf">clone</span><span class="p">().</span><span class="nf">text</span><span class="p">();</span>
  <span class="kd">const</span> <span class="nx">calculated</span> <span class="o">=</span> <span class="k">await</span> <span class="nf">hmacSha256Hex</span><span class="p">(</span><span class="nx">secret</span><span class="p">,</span> <span class="nx">rawBody</span><span class="p">);</span>

  <span class="k">return</span> <span class="dl">"</span><span class="s2">hmacsha256=</span><span class="dl">"</span><span class="o">+</span><span class="nx">calculated</span> <span class="o">===</span> <span class="nx">appleSignature</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<ul>
  <li>Cloudflare Worker 無 crypto module，要使用 Web Crypto API（crypto.subtle）</li>
</ul>

<p><strong>實現方式 — Google Apps Script Web App ❌</strong></p>

<p>礙於技術限制，Google Apps Script Web App <code class="language-plaintext highlighter-rouge">doGet(e)/doPost(e)</code> 無法從中取得 Request Headers，因此無法使用此方法對請求來源進行驗證。</p>

<p>最多只能自己在 URL Query 加一些密鑰參數做很簡單的判斷保護。</p>
<h3 id="app-store-connect-api-webhook-通知-payload">App Store Connect API Webhook 通知 Payload</h3>

<p>這邊有搜集了幾個 App 上傳、送審過程會收到的事件 Payload，提供給大家做自動化開發時可以直接參考。</p>

<blockquote>
  <p><em>Webhook 只會發送事件跟狀態名稱，不會給其他詳細資訊，例如：版本號、拒審原因…等等； <strong>我們需要自己再打 App Store Connect API 帶上 Event Payload 裡的 Relationships Link 才能取得完整資訊。</strong></em></p>
</blockquote>

<h4 id="建置版本上傳--process-complete">建置版本上傳 — Process Complete</h4>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"data"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"type"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"buildUploadStateUpdated"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"id"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"xxx-xx-xx-xx-xxx"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"version"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w">
    </span><span class="nl">"attributes"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"oldState"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"PROCESSING"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"newState"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"COMPLETE"</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="nl">"relationships"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"instance"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"data"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"type"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"buildUploads"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"id"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"xxx-xx-xx-xx-xxx"</span><span class="w">
        </span><span class="p">},</span><span class="w">
        </span><span class="nl">"links"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"self"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"https://api.appstoreconnect.apple.com/v1/buildUploads/xxx-xx-xx-xx-xxx"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">}</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<h4 id="建置版本上傳--process-failed">建置版本上傳 — Process Failed</h4>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"data"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"buildUploadStateUpdated"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"xxx-xx-xx-xx-xxx"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"version"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w">
    </span><span class="nl">"attributes"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"oldState"</span><span class="p">:</span><span class="w"> </span><span class="s2">"PROCESSING"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"newState"</span><span class="p">:</span><span class="w"> </span><span class="s2">"FAILED"</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="nl">"relationships"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"instance"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"data"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"buildUploads"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"xxx-xx-xx-xx-xxx"</span><span class="w">
        </span><span class="p">},</span><span class="w">
        </span><span class="nl">"links"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"self"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://api.appstoreconnect.apple.com/v1/buildUploads/xxx-xx-xx-xx-xxx"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">}</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>多半為 Binary 遭拒，例如有用到麥克風但是沒宣告之類的。</p>
<h4 id="app-版本狀態--prepare-for-submission-準備提交">App 版本狀態 — Prepare For Submission (準備提交)</h4>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"data"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"type"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"appStoreVersionAppVersionStateUpdated"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"id"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"xxx-xx-xx-xx-xxx"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"version"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w">
    </span><span class="nl">"attributes"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"newValue"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"PREPARE_FOR_SUBMISSION"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"oldValue"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"DEVELOPER_REJECTED"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"timestamp"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"2025-12-18T05:01:47.118Z"</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="nl">"relationships"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"instance"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"data"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"type"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"appStoreVersions"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"id"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"xxx-xx-xx-xx-xxx"</span><span class="w">
        </span><span class="p">},</span><span class="w">
        </span><span class="nl">"links"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"self"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xx-xx-xx-xxx"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">}</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>當新版本號建立好，準備提交送審時，這階段可填寫版本資訊、更新內容、選擇要送審的 Build。</p>
<h4 id="app-版本狀態--ready-for-review-準備送審"><strong>App 版本狀態 — Ready For Review (準備送審)</strong></h4>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"data"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"type"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"appStoreVersionAppVersionStateUpdated"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"id"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"xxx-xx-xx-xx-xxx"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"version"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w">
    </span><span class="nl">"attributes"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"newValue"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"READY_FOR_REVIEW"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"oldValue"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"PREPARE_FOR_SUBMISSION"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"timestamp"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"2025-12-18T03:41:12.516Z"</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="nl">"relationships"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"instance"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"data"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"type"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"appStoreVersions"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"id"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"xxx-xx-xx-xx-xxx"</span><span class="w">
        </span><span class="p">},</span><span class="w">
        </span><span class="nl">"links"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"self"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xx-xx-xx-xxx"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">}</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>送審資料確定，準備送審時。</p>
<h4 id="app-版本狀態--waiting-for-review-已送審等待審核">App 版本狀態 — Waiting For Review (已送審，等待審核)</h4>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"data"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"type"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"appStoreVersionAppVersionStateUpdated"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"id"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"xxx-xx-xx-xx-xxx"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"version"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w">
    </span><span class="nl">"attributes"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"newValue"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"WAITING_FOR_REVIEW"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"oldValue"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"READY_FOR_REVIEW"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"timestamp"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"2025-12-18T03:41:21.179Z"</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="nl">"relationships"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"instance"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"data"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"type"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"appStoreVersions"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"id"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"xxx-xx-xx-xx-xxx"</span><span class="w">
        </span><span class="p">},</span><span class="w">
        </span><span class="nl">"links"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"self"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xx-xx-xx-xxx"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">}</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>App 已完成送審，正在等待審查。</p>
<h4 id="app-版本狀態--developer-rejected-開發者拒絕">App 版本狀態 — Developer Rejected (開發者拒絕)</h4>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"data"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"type"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"appStoreVersionAppVersionStateUpdated"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"id"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"xxx-xx-xx-xx-xxx"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"version"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w">
    </span><span class="nl">"attributes"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"newValue"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"DEVELOPER_REJECTED"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"oldValue"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"WAITING_FOR_REVIEW"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"timestamp"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"2025-12-18T03:50:30.552Z"</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="nl">"relationships"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"instance"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"data"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"type"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"appStoreVersions"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"id"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"xxx-xx-xx-xx-xxx"</span><span class="w">
        </span><span class="p">},</span><span class="w">
        </span><span class="nl">"links"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"self"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xx-xx-xx-xxx"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">}</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>開發者撤回正在送審的版本。</p>
<h4 id="app-版本狀態--in-review-官方正在審核">App 版本狀態 — In Review (官方正在審核)</h4>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"data"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"type"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"appStoreVersionAppVersionStateUpdated"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"id"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"xxx-xx-xx-xx-xxx"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"version"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w">
    </span><span class="nl">"attributes"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"newValue"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"IN_REVIEW"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"oldValue"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"WAITING_FOR_REVIEW"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"timestamp"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"2025-12-18T22:05:50.038Z"</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="nl">"relationships"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"instance"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"data"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"type"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"appStoreVersions"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"id"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"xxx-xx-xx-xx-xxx"</span><span class="w">
        </span><span class="p">},</span><span class="w">
        </span><span class="nl">"links"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"self"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xx-xx-xx-xxx"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">}</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<h4 id="app-版本狀態--pending-developer-release-審核完畢等待發佈">App 版本狀態 — Pending Developer Release (審核完畢等待發佈)</h4>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"data"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"type"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"appStoreVersionAppVersionStateUpdated"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"id"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"xxx-xx-xx-xx-xxx"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"version"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w">
    </span><span class="nl">"attributes"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"newValue"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"PENDING_DEVELOPER_RELEASE"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"oldValue"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"IN_REVIEW"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"timestamp"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"2025-12-18T22:34:18.785Z"</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="nl">"relationships"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"instance"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"data"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"type"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"appStoreVersions"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"id"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"xxx-xx-xx-xx-xxx"</span><span class="w">
        </span><span class="p">},</span><span class="w">
        </span><span class="nl">"links"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"self"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xx-xx-xx-xxx"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">}</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<blockquote>
  <p><em>Pending Developer Release 事件的時間 減掉 Waiting For Review 事件的時間就 等於 App 送審到可發佈狀態中間等待的時間了。</em></p>
</blockquote>

<h4 id="app-版本狀態--ready-for-distribution-app-準備發佈-aka-ready-for-sale">App 版本狀態 — Ready for Distribution (App 準備發佈) a.k.a Ready For Sale</h4>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"data"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"type"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"appStoreVersionAppVersionStateUpdated"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"id"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"xxx-xx-xx-xx-xxx"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"version"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w">
    </span><span class="nl">"attributes"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"newValue"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"READY_FOR_DISTRIBUTION"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"oldValue"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"PENDING_DEVELOPER_RELEASE"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"timestamp"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"2025-12-23T06:03:50.925Z"</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="nl">"relationships"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"instance"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"data"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"type"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"appStoreVersions"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"id"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"xxx-xx-xx-xx-xxx"</span><span class="w">
        </span><span class="p">},</span><span class="w">
        </span><span class="nl">"links"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"self"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xx-xx-xx-xxx"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">}</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>App 已準備發佈 (幾乎等同 Ready For Sale，沒有 Ready For Sale 這個事件)。</p>

<blockquote>
  <p><em>Your app has been accepted and is ready for distribution.</em></p>
</blockquote>

<blockquote>
  <p><em>To distribute your app, your <a href="https://developer.apple.com/help/app-store-connect/manage-agreements/view-agreements-status" target="_blank">agreements</a> must be in effect. The Account Holder can accept the latest agreements in the Business section.</em></p>
</blockquote>

<h3 id="app-store-connect-api-webhook-工作流程串接">App Store Connect API Webhook 工作流程串接</h3>
<h4 id="方法-1--借用-fastlane-串接-app-store-connect-api">方法 1 — 借用 Fastlane 串接 App Store Connect API</h4>

<p>這邊最快的方式是直接用 CI/CD 服務觸發，然後借用 Fastlane 自帶的 Spaceship 串接 App Store Connect API。</p>

<blockquote>
  <p><em>如果你本來 <a href="https://docs.fastlane.tools/app-store-connect-api/" target="_blank">Fastlane 就有使用 App Store Connect API</a> 管理 Match 憑證、送審，這個方法可以直接無痛使用；如果沒有要先 <a href="https://docs.fastlane.tools/app-store-connect-api/" target="_blank">參考官方文件</a> 產生 API Key 跟安全存放在 CI/CD 服務的密鑰中。</em></p>
</blockquote>

<p><img src="/assets/7c0974856393/1*FrJoXtV2lrk09KnAt2Hydg.webp" alt="" loading="lazy" decoding="async" width="2141" height="577" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMTQxIiBoZWlnaHQ9IjU3NyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<ul>
  <li><strong>App Store Connect</strong>
    <ol>
      <li>App 有狀態改變時</li>
      <li>觸發 Webhook</li>
    </ol>
  </li>
  <li><strong>Webhook Endpoint</strong> 
<code class="language-plaintext highlighter-rouge">可以是 自架服務/API 或是 簡單用 FAAS 服務 (Cloudflare Worker / AWS Lambda / Cloud Functions / Google Apps Script)</code>
    <ol>
      <li>驗證 Webhook (optional)</li>
      <li>處理 Webhook 事件、轉發事件請求到 CI/CD 服務上執行
<code class="language-plaintext highlighter-rouge">e.g. 透過 GitHub API 觸發 GitHub Actions..</code></li>
    </ol>
  </li>
  <li><strong>CI/CD Service</strong> 
<code class="language-plaintext highlighter-rouge">GitHub Actions / Bitbucket Pipeline / Gitlab Runner…</code>
    <ol>
      <li>觸發 Action</li>
      <li>執行 Fastlane 腳本，復用 Fastlane Spaceship 驗證</li>
    </ol>
  </li>
  <li><strong>App Store Connect</strong>
    <ol>
      <li>打 App Store Connect API 取得完整資訊</li>
    </ol>
  </li>
  <li><strong>CI/CD Service</strong>
    <ol>
      <li>後續步驟，例如發通知、觸發另一個 Action</li>
    </ol>
  </li>
</ul>

<p><strong>Fastlane Example:</strong></p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="c1"># Usage:</span>
  <span class="c1">#   bundle exec fastlane appStoreConnectWebhookHandler \</span>
  <span class="c1">#     data:'{"data":{"type":"buildUploadStateUpdated","id":"xxx-xxx-xxx-xx-xxx","version":1,"attributes":{"oldState":"PROCESSING","newState":"COMPLETE"},"relationships":{"instance":{"data":{"type":"buildUploads","id":"xxx-xxx-xxx-xx-xxx"},"links":{"self":"https://api.appstoreconnect.apple.com/v1/buildUploads/xxx-xxx-xxx-xx-xxx"}}}}}'</span>
  <span class="c1"># Notes:</span>
  <span class="c1"># - `data:` must be a JSON string.</span>
  <span class="c1"># - This lane is intended for local debugging (it prints the GET response).</span>
  <span class="n">desc</span> <span class="s2">"[Automation] Handle App Store Connect webhook payload and fetch related instance via ASC API"</span>
  <span class="n">lane</span> <span class="ss">:appStoreConnectWebhookHandler</span> <span class="k">do</span> <span class="o">|</span><span class="n">options</span><span class="o">|</span>
    <span class="k">begin</span>
      <span class="n">data</span> <span class="o">=</span> <span class="n">options</span><span class="p">[</span><span class="ss">:data</span><span class="p">]</span>
      <span class="no">UI</span><span class="p">.</span><span class="nf">user_error!</span><span class="p">(</span><span class="s2">"Missing data"</span><span class="p">)</span> <span class="k">if</span> <span class="n">data</span><span class="p">.</span><span class="nf">empty?</span>
      <span class="n">data</span> <span class="o">=</span> <span class="no">JSON</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
      <span class="n">url</span> <span class="o">=</span> <span class="n">data</span><span class="p">.</span><span class="nf">dig</span><span class="p">(</span><span class="s2">"data"</span><span class="p">,</span> <span class="s2">"relationships"</span><span class="p">,</span> <span class="s2">"instance"</span><span class="p">,</span> <span class="s2">"links"</span><span class="p">,</span> <span class="s2">"self"</span><span class="p">).</span><span class="nf">to_s</span><span class="p">.</span><span class="nf">strip</span>
      <span class="no">UI</span><span class="p">.</span><span class="nf">user_error!</span><span class="p">(</span><span class="s2">"Missing instance self url in JSON"</span><span class="p">)</span> <span class="k">if</span> <span class="n">url</span><span class="p">.</span><span class="nf">empty?</span>

      <span class="n">api_key</span> <span class="o">=</span> <span class="n">app_store_connect_api_key</span><span class="p">(</span>
        <span class="ss">key_id: </span><span class="s2">"xxxx"</span><span class="p">,</span>
        <span class="ss">issuer_id: </span><span class="s2">"xxxx-xxxx-xxxx-xxxx-165aa6465141"</span><span class="p">,</span>
        <span class="ss">key_filepath: </span><span class="s2">"./AuthKey_xxxx.p8"</span><span class="p">,</span>
        <span class="ss">duration: </span><span class="mi">1200</span><span class="p">,</span> <span class="c1"># optional (maximum 1200)</span>
        <span class="ss">in_house: </span><span class="kp">false</span> <span class="c1"># optional but may be required if using match/sigh</span>
      <span class="p">)</span>

      <span class="n">loadAppStoreConnectAPIKey</span>
      <span class="c1">#</span>
      <span class="n">uri</span> <span class="o">=</span> <span class="no">URI</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="n">url</span><span class="p">)</span>
      <span class="n">http</span> <span class="o">=</span> <span class="no">Net</span><span class="o">::</span><span class="no">HTTP</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">uri</span><span class="p">.</span><span class="nf">host</span><span class="p">,</span> <span class="n">uri</span><span class="p">.</span><span class="nf">port</span><span class="p">)</span>
      <span class="n">http</span><span class="p">.</span><span class="nf">use_ssl</span> <span class="o">=</span> <span class="kp">true</span>
      <span class="n">http</span><span class="p">.</span><span class="nf">verify_mode</span> <span class="o">=</span> <span class="no">OpenSSL</span><span class="o">::</span><span class="no">SSL</span><span class="o">::</span><span class="no">VERIFY_PEER</span>
      <span class="n">store</span> <span class="o">=</span> <span class="no">OpenSSL</span><span class="o">::</span><span class="no">X509</span><span class="o">::</span><span class="no">Store</span><span class="p">.</span><span class="nf">new</span>
      <span class="n">store</span><span class="p">.</span><span class="nf">set_default_paths</span>
      <span class="n">http</span><span class="p">.</span><span class="nf">cert_store</span> <span class="o">=</span> <span class="n">store</span>

      <span class="n">request</span> <span class="o">=</span> <span class="no">Net</span><span class="o">::</span><span class="no">HTTP</span><span class="o">::</span><span class="no">Get</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">uri</span><span class="p">.</span><span class="nf">request_uri</span><span class="p">)</span>
      <span class="n">token</span> <span class="o">=</span> <span class="no">Spaceship</span><span class="o">::</span><span class="no">ConnectAPI</span><span class="p">.</span><span class="nf">token</span>
      <span class="no">UI</span><span class="p">.</span><span class="nf">user_error!</span><span class="p">(</span><span class="s2">"App Store Connect API token is not available. Make sure app_store_connect_api_key is configured correctly."</span><span class="p">)</span> <span class="k">if</span> <span class="n">token</span><span class="p">.</span><span class="nf">nil?</span>
      <span class="n">request</span><span class="p">[</span><span class="s1">'Authorization'</span><span class="p">]</span> <span class="o">=</span> <span class="s2">"Bearer </span><span class="si">#{</span><span class="n">token</span><span class="p">.</span><span class="nf">text</span><span class="si">}</span><span class="s2">"</span>

      <span class="n">request</span><span class="p">[</span><span class="s1">'Content-Type'</span><span class="p">]</span> <span class="o">=</span> <span class="s1">'application/json'</span>
      <span class="n">request</span><span class="p">[</span><span class="s1">'Accept'</span><span class="p">]</span> <span class="o">=</span> <span class="s1">'application/json'</span>

      <span class="n">response</span> <span class="o">=</span> <span class="n">http</span><span class="p">.</span><span class="nf">request</span><span class="p">(</span><span class="n">request</span><span class="p">)</span>
      <span class="no">UI</span><span class="p">.</span><span class="nf">message</span><span class="p">(</span><span class="s2">"📡 GET </span><span class="si">#{</span><span class="n">url</span><span class="si">}</span><span class="s2"> Response: [</span><span class="si">#{</span><span class="n">response</span><span class="p">.</span><span class="nf">code</span><span class="si">}</span><span class="s2">] </span><span class="si">#{</span><span class="n">response</span><span class="p">.</span><span class="nf">message</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
      <span class="no">UI</span><span class="p">.</span><span class="nf">message</span><span class="p">(</span><span class="n">response</span><span class="p">.</span><span class="nf">body</span><span class="p">)</span>
      <span class="c1">#</span>

      <span class="n">response</span>
      <span class="c1">## handle response...do next actions...</span>
      
    <span class="k">rescue</span> <span class="o">=&gt;</span> <span class="n">e</span>
        <span class="no">UI</span><span class="p">.</span><span class="nf">error</span><span class="p">(</span><span class="s2">"❌ Failed handle App Store Connect API Webhook: </span><span class="si">#{</span><span class="n">e</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
    <span class="k">end</span>

  <span class="k">end</span>
</code></pre></div></div>
<h4 id="方法-2-在-webhook-endpoint-自行處理">方法 2— 在 Webhook Endpoint 自行處理</h4>

<p>第二個方法就是直接在 Webhook Endpoint 服務上處理完所有事情，但 <strong>缺點是需要把 App Store Connect API Key 放到服務上跟要自己做 Token 驗證</strong> 。</p>

<p><img src="/assets/7c0974856393/1*AUw59sLt97RquLhoRBp5Rw.webp" alt="" loading="lazy" decoding="async" width="1491" height="590" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDkxIiBoZWlnaHQ9IjU5MCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><strong>Ruby Example:</strong></p>
<div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">require</span> <span class="s1">'jwt'</span>
<span class="k">require</span> <span class="s1">'net/http'</span>
<span class="k">require</span> <span class="s1">'time'</span>

<span class="n">keyFile</span> <span class="o">=</span> <span class="nc">File</span><span class="mf">.</span><span class="nf">read</span><span class="p">(</span><span class="s1">'./AuthKey_XXXX.p8'</span><span class="p">)</span> <span class="c1"># replace to your .p8 private key file (donwload from app stroe connect)</span>
<span class="n">privateKey</span> <span class="o">=</span> <span class="nc">OpenSSL</span><span class="o">::</span><span class="nc">PKey</span><span class="o">::</span><span class="no">EC</span><span class="mf">.</span><span class="k">new</span><span class="p">(</span><span class="n">keyFile</span><span class="p">)</span>

<span class="n">payload</span> <span class="o">=</span> <span class="p">{</span>
            <span class="n">iss</span><span class="o">:</span> <span class="s1">'YOUR_ISSUE_ID'</span><span class="p">,</span> <span class="c1"># replace to your issue id (get in app store connect user access-&gt;key-&gt;app store connect api page)</span>
            <span class="n">iat</span><span class="o">:</span> <span class="nc">Time</span><span class="mf">.</span><span class="n">now</span><span class="mf">.</span><span class="n">to_i</span><span class="p">,</span>
            <span class="n">exp</span><span class="o">:</span> <span class="nc">Time</span><span class="mf">.</span><span class="n">now</span><span class="mf">.</span><span class="n">to_i</span> <span class="o">+</span> <span class="mi">60</span><span class="o">*</span><span class="mi">20</span><span class="p">,</span>
            <span class="n">aud</span><span class="o">:</span> <span class="s1">'appstoreconnect-v1'</span>
          <span class="p">}</span>

<span class="n">token</span> <span class="o">=</span> <span class="no">JWT</span><span class="mf">.</span><span class="n">encode</span> <span class="n">payload</span><span class="p">,</span> <span class="n">privateKey</span><span class="p">,</span> <span class="s1">'ES256'</span><span class="p">,</span> <span class="n">header_fields</span><span class="o">=</span><span class="p">{</span><span class="n">kid</span><span class="o">:</span><span class="s2">"YOUR_KEY_ID"</span><span class="p">,</span> <span class="n">typ</span><span class="o">:</span><span class="s2">"JWT"</span><span class="p">}</span> <span class="c1"># replace to your key id (get in app store connect user access-&gt;key-&gt;app store connect api page)</span>
<span class="n">puts</span> <span class="n">token</span>

<span class="n">decoded_token</span> <span class="o">=</span> <span class="no">JWT</span><span class="mf">.</span><span class="n">decode</span> <span class="n">token</span><span class="p">,</span> <span class="n">privateKey</span><span class="p">,</span> <span class="kc">true</span><span class="p">,</span> <span class="p">{</span> <span class="n">algorithm</span><span class="o">:</span> <span class="s1">'ES256'</span> <span class="p">}</span>
<span class="n">puts</span> <span class="n">decoded_token</span>

<span class="c1"># 替換成 Webhook Payload 中的 relationships link</span>
<span class="n">uri</span> <span class="o">=</span> <span class="nf">URI</span><span class="p">(</span><span class="s2">"https://api.appstoreconnect.apple.com/v1/apps/APPID/customerReviews"</span><span class="p">)</span> <span class="c1"># repleace APPID to your app id in app store connect -&gt; your app -&gt; app information -&gt; apple id</span>
<span class="n">https</span> <span class="o">=</span> <span class="nc">Net</span><span class="o">::</span><span class="no">HTTP</span><span class="mf">.</span><span class="k">new</span><span class="p">(</span><span class="n">uri</span><span class="mf">.</span><span class="n">host</span><span class="p">,</span> <span class="n">uri</span><span class="mf">.</span><span class="n">port</span><span class="p">)</span>
<span class="n">https</span><span class="mf">.</span><span class="n">use_ssl</span> <span class="o">=</span> <span class="kc">true</span>

<span class="n">request</span> <span class="o">=</span> <span class="nc">Net</span><span class="o">::</span><span class="no">HTTP</span><span class="o">::</span><span class="nc">Get</span><span class="mf">.</span><span class="k">new</span><span class="p">(</span><span class="n">uri</span><span class="p">)</span>
<span class="n">request</span><span class="p">[</span><span class="s1">'Authorization'</span><span class="p">]</span> <span class="o">=</span> <span class="s2">"Bearer #</span><span class="si">{</span><span class="nv">token</span><span class="si">}</span><span class="s2">"</span><span class="p">;</span>

<span class="n">response</span> <span class="o">=</span> <span class="n">https</span><span class="mf">.</span><span class="nf">request</span><span class="p">(</span><span class="n">request</span><span class="p">)</span>
<span class="n">puts</span> <span class="n">response</span><span class="mf">.</span><span class="n">read_body</span>
</code></pre></div></div>

<blockquote>
  <p><em>App Store Connect API Key 產生方式、Token 產生方式、API 使用請參考「 <a href="/posts/zrealm-dev/app-store-connect-api-快速讀取與管理-customer-reviews-提升-ios-app-評價整合效率-f1365e51902c/">App Store Connect API 現已支援 讀取和管理 Customer Reviews</a> 」。</em></p>
</blockquote>

<h3 id="app-store-connect-api-response">App Store Connect API Response</h3>

<p>這裡附上一些收到 Webhook Event 後再打 Relationships Link 取得完整資訊的一些 Response 範例。</p>
<h4 id="建置版本上傳--process-complete-1">建置版本上傳 — Process Complete</h4>

<p><code class="language-plaintext highlighter-rouge">https://api.appstoreconnect.apple.com/v1/buildUploads/xxx-xx-xx-xx-xxx</code></p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"data"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"buildUploads"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"xx-xx-xx-xxx-xx"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"attributes"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"cfBundleShortVersionString"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1.101.0"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"cfBundleVersion"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"createdDate"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2025-12-25T08:26:43-08:00"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"state"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"errors"</span><span class="p">:</span><span class="w"> </span><span class="p">[],</span><span class="w">
        </span><span class="nl">"warnings"</span><span class="p">:</span><span class="w"> </span><span class="p">[],</span><span class="w">
        </span><span class="nl">"infos"</span><span class="p">:</span><span class="w"> </span><span class="p">[],</span><span class="w">
        </span><span class="nl">"state"</span><span class="p">:</span><span class="w"> </span><span class="s2">"COMPLETE"</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="nl">"platform"</span><span class="p">:</span><span class="w"> </span><span class="s2">"IOS"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"uploadedDate"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2025-12-25T08:28:35-08:00"</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="nl">"relationships"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"buildUploadFiles"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"links"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"self"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://api.appstoreconnect.apple.com/v1/buildUploads/xx-xx-xx-xxx-xx/relationships/buildUploadFiles"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"related"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://api.appstoreconnect.apple.com/v1/buildUploads/xx-xx-xx-xxx-xx/buildUploadFiles"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">}</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="nl">"links"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"self"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://api.appstoreconnect.apple.com/v1/buildUploads/xx-xx-xx-xxx-xx"</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"links"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"self"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://api.appstoreconnect.apple.com/v1/buildUploads/xx-xx-xx-xxx-xx"</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<h4 id="建置版本上傳--process-failed-1">建置版本上傳 — Process Failed</h4>

<p><code class="language-plaintext highlighter-rouge">https://api.appstoreconnect.apple.com/v1/buildUploads/xxx-xx-xx-xx-xxx</code></p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"data"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"buildUploads"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"xx-xx-xx-xx-xxx"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"attributes"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"cfBundleShortVersionString"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1.101.0"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"cfBundleVersion"</span><span class="p">:</span><span class="w"> </span><span class="s2">"3"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"createdDate"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2025-12-12T09:03:32-08:00"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"state"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"errors"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
          </span><span class="p">{</span><span class="w">
            </span><span class="nl">"code"</span><span class="p">:</span><span class="w"> </span><span class="s2">"90683"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"description"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Missing purpose string in Info.plist. Your app’s code references one or more APIs that access sensitive user data, or the app has one or more entitlements that permit such access. The Info.plist file for the “My.app” bundle should contain a NSMicrophoneUsageDescription key with a user-facing purpose string explaining clearly and completely why your app needs the data. If you’re using external libraries or SDKs, they may reference APIs that require a purpose string. While your app might not use these APIs, a purpose string is still required. For details, visit: https://developer.apple.com/documentation/uikit/protecting_the_user_s_privacy/requesting_access_to_protected_resources."</span><span class="w">
          </span><span class="p">}</span><span class="w">
        </span><span class="p">],</span><span class="w">
        </span><span class="nl">"warnings"</span><span class="p">:</span><span class="w"> </span><span class="p">[],</span><span class="w">
        </span><span class="nl">"infos"</span><span class="p">:</span><span class="w"> </span><span class="p">[],</span><span class="w">
        </span><span class="nl">"state"</span><span class="p">:</span><span class="w"> </span><span class="s2">"FAILED"</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="nl">"platform"</span><span class="p">:</span><span class="w"> </span><span class="s2">"IOS"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"uploadedDate"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2025-12-12T09:05:26-08:00"</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="nl">"relationships"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"buildUploadFiles"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"links"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"self"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://api.appstoreconnect.apple.com/v1/buildUploads/xx-xx-xx-xx-xxx/relationships/buildUploadFiles"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"related"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://api.appstoreconnect.apple.com/v1/buildUploads/xx-xx-xx-xx-xxx/buildUploadFiles"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">}</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="nl">"links"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"self"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://api.appstoreconnect.apple.com/v1/buildUploads/xx-xx-xx-xx-xxx"</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"links"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"self"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://api.appstoreconnect.apple.com/v1/buildUploads/xx-xx-xx-xx-xxx"</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>ITMS-90683 為例。</p>
<h4 id="app-版本狀態">App 版本狀態</h4>

<p><code class="language-plaintext highlighter-rouge">https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xx-xx-xx-xxx</code></p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"data"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"appStoreVersions"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"xxx-xxx-xxx-xxx"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"attributes"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"platform"</span><span class="p">:</span><span class="w"> </span><span class="s2">"IOS"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"versionString"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1.101.0"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"appStoreState"</span><span class="p">:</span><span class="w"> </span><span class="s2">"READY_FOR_SALE"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"appVersionState"</span><span class="p">:</span><span class="w"> </span><span class="s2">"READY_FOR_DISTRIBUTION"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"copyright"</span><span class="p">:</span><span class="w"> </span><span class="s2">"© 2025 ZhgChgLi."</span><span class="p">,</span><span class="w">
      </span><span class="nl">"reviewType"</span><span class="p">:</span><span class="w"> </span><span class="s2">"APP_STORE"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"releaseType"</span><span class="p">:</span><span class="w"> </span><span class="s2">"MANUAL"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"earliestReleaseDate"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w">
      </span><span class="nl">"usesIdfa"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w">
      </span><span class="nl">"downloadable"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
      </span><span class="nl">"createdDate"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2025-12-15T19:12:55-08:00"</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="nl">"relationships"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"ageRatingDeclaration"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"links"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"self"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xxx-xxx-xxx/relationships/ageRatingDeclaration"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"related"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xxx-xxx-xxx/ageRatingDeclaration"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="nl">"appStoreVersionLocalizations"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"links"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"self"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xxx-xxx-xxx/relationships/appStoreVersionLocalizations"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"related"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xxx-xxx-xxx/appStoreVersionLocalizations"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="nl">"build"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"links"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"self"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xxx-xxx-xxx/relationships/build"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"related"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xxx-xxx-xxx/build"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="nl">"appStoreVersionPhasedRelease"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"links"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"self"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xxx-xxx-xxx/relationships/appStoreVersionPhasedRelease"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"related"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xxx-xxx-xxx/appStoreVersionPhasedRelease"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="nl">"gameCenterAppVersion"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"links"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"self"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xxx-xxx-xxx/relationships/gameCenterAppVersion"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"related"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xxx-xxx-xxx/gameCenterAppVersion"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="nl">"routingAppCoverage"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"links"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"self"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xxx-xxx-xxx/relationships/routingAppCoverage"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"related"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xxx-xxx-xxx/routingAppCoverage"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="nl">"appStoreReviewDetail"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"links"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"self"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xxx-xxx-xxx/relationships/appStoreReviewDetail"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"related"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xxx-xxx-xxx/appStoreReviewDetail"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="nl">"appStoreVersionSubmission"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"links"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"self"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xxx-xxx-xxx/relationships/appStoreVersionSubmission"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"related"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xxx-xxx-xxx/appStoreVersionSubmission"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="nl">"appClipDefaultExperience"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"links"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"self"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xxx-xxx-xxx/relationships/appClipDefaultExperience"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"related"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xxx-xxx-xxx/appClipDefaultExperience"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="nl">"appStoreVersionExperiments"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"links"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"self"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xxx-xxx-xxx/relationships/appStoreVersionExperiments"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"related"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xxx-xxx-xxx/appStoreVersionExperiments"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="nl">"appStoreVersionExperimentsV2"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"links"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"self"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xxx-xxx-xxx/relationships/appStoreVersionExperimentsV2"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"related"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xxx-xxx-xxx/appStoreVersionExperimentsV2"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="nl">"customerReviews"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"links"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"self"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xxx-xxx-xxx/relationships/customerReviews"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"related"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xxx-xxx-xxx/customerReviews"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="nl">"alternativeDistributionPackage"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"links"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"self"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xxx-xxx-xxx/relationships/alternativeDistributionPackage"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"related"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xxx-xxx-xxx/alternativeDistributionPackage"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">}</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="nl">"links"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"self"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xxx-xxx-xxx"</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"links"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"self"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://api.appstoreconnect.apple.com/v1/appStoreVersions/xxx-xxx-xxx-xxx"</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<blockquote>
  <p><em>如前述，詳細 App 資訊、版號、錯誤原因都要打 API 才會拿到。</em></p>
</blockquote>

<h3 id="done">Done</h3>

<p>至此我們已經可以透過 App Store Connect API Webhook 更好地完善 App CI/CD 及自動化工作流程，提升團隊開發效率。</p>
<h4 id="延伸閱讀">延伸閱讀</h4>
<ul>
  <li><a href="/posts/zrealm-dev/ci-cd-是什麼-打造高效穩定開發團隊的關鍵流程與工具選擇-c008a9e8ceca/"><strong>CI/CD 實戰指南（一）：CI/CD 是什麼？如何透過 CI/CD 打造穩定高效的開發團隊？工具選擇？</strong></a></li>
  <li><a href="/posts/zrealm-dev/zreviewtender-免費開源-app-評價監控機器人-自動整合-slack-與-google-sheet-e36e48bb9265/">ZReviewTender — 免費開源的 App Reviews 監控機器人</a></li>
  <li><a href="/posts/zrealm-dev/app-store-connect-api-快速讀取與管理-customer-reviews-提升-ios-app-評價整合效率-f1365e51902c/">App Store Connect API 現已支援 讀取和管理 Customer Reviews</a></li>
</ul>]]></content>
  </entry><entry>
    <title type="html">iOS Timer 與 DispatchSourceTimer 選擇與安全封裝技巧｜有限狀態機防止閃退</title>
    <link href="https://zhgchg.li/posts/zrealm-dev/ios-timer-%E8%88%87-dispatchsourcetimer-%E9%81%B8%E6%93%87%E8%88%87%E5%AE%89%E5%85%A8%E5%B0%81%E8%A3%9D%E6%8A%80%E5%B7%A7-%E6%9C%89%E9%99%90%E7%8B%80%E6%85%8B%E6%A9%9F%E9%98%B2%E6%AD%A2%E9%96%83%E9%80%80-62f68ebeecd3/" rel="alternate" type="text/html" title="iOS Timer 與 DispatchSourceTimer 選擇與安全封裝技巧｜有限狀態機防止閃退" />
    <published>2025-12-14T16:17:06+08:00</published>
    <updated>2025-12-15T00:24:36+08:00</updated>
    <id>https://zhgchg.li/posts/zrealm-dev/62f68ebeecd3</id><summary type="html">iOS 開發必備 Timer 使用指南，解析 Timer 與 DispatchSourceTimer 優缺點，並提供有限狀態機封裝 DispatchSourceTimer，避免閃退及 Race Condition，實現高精度且安全的定時任務管理。</summary><author>
      <name>ZhgChgLi</name>
    </author><category term="ZRealm Dev." /><category term="ios-app-development" /><category term="design-patterns" /><category term="timer" /><category term="swift" /><category term="finite-state-machine" /><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://zhgchg.li/assets/62f68ebeecd3/1*2CWJW1fPOAZdU3nqeJ2JeQ.webp" /><content type="html" xml:base="https://zhgchg.li/posts/zrealm-dev/ios-timer-%E8%88%87-dispatchsourcetimer-%E9%81%B8%E6%93%87%E8%88%87%E5%AE%89%E5%85%A8%E5%B0%81%E8%A3%9D%E6%8A%80%E5%B7%A7-%E6%9C%89%E9%99%90%E7%8B%80%E6%85%8B%E6%A9%9F%E9%98%B2%E6%AD%A2%E9%96%83%E9%80%80-62f68ebeecd3/"><![CDATA[<h3 id="ios-timer-與-dispatchsourcetimer-如何選擇與安全的使用">[iOS] Timer 與 DispatchSourceTimer 如何選擇與安全的使用?</h3>

<p>使用有限狀態機與 Design Patterns 封裝 DispatchSourceTimer，使其更安全易用。</p>

<p><img src="/assets/62f68ebeecd3/1*2CWJW1fPOAZdU3nqeJ2JeQ.webp" alt="Photo by Ralph Hutter" loading="lazy" decoding="async" width="1200" height="800" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjgwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>Photo by <a href="https://unsplash.com/@pixelfreund?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" target="_blank">Ralph Hutter</a></p>
<h4 id="關於-timer">關於 Timer</h4>

<p>在 iOS 開發中一定會遇到的需求場景「Timer 定時觸發器」；從 UI 層面上顯示倒數計時、Banner 輪播到資料邏輯層面上的定時發送 Events、定時清除釋放資料；我們都需要 Timer 來幫助我們達成目標。</p>
<h3 id="foundation--timer-nstimer"><a href="https://developer.apple.com/documentation/foundation/timer" target="_blank">Foundation — Timer (NSTimer)</a></h3>

<p>Timer 應該是大家最直覺會先想到的 API，但是在選擇及使用 Timer 上我們需要注意以下幾個點。</p>
<h4 id="優缺點">優缺點</h4>

<p><strong>Timer 的優點：</strong></p>
<ul>
  <li>默認與 UI 工作整合，不需要特別切 Main Thread 執行</li>
  <li>自動調整觸發時機優化使用電量</li>
  <li>使用複雜度較低，最多只會發生 Retain Cycle 或忘記停止 Timer，但不會直接造成 Crash</li>
</ul>

<p><strong>Timer 的缺點：</strong></p>
<ul>
  <li>精確度受 RunLoop 狀態影響，在 UI 高互動或 Mode 切換時可能延後觸發</li>
  <li>不支援 <code class="language-plaintext highlighter-rouge">suspend</code> , <code class="language-plaintext highlighter-rouge">resume</code> , <code class="language-plaintext highlighter-rouge">activate</code> …等進階操作</li>
</ul>

<h4 id="適合場景">適合場景</h4>

<p>UI 層面需求，例如輪播 Banners (Auto Scroll ScrollView)或是優惠券領取倒數計時；這些只要求使用者在前景當前畫面能響應內容的場景，我會選擇直接用 Timer，方便、快速、安全的達成目的。</p>
<h4 id="生命週期">生命週期</h4>

<p><img src="/assets/62f68ebeecd3/1*rQrIoCxCdwoFCgqZc1zE8A.webp" alt="" loading="lazy" decoding="async" width="1187" height="1237" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMTg3IiBoZWlnaHQ9IjEyMzciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>在 UI Main Thread 上建立 Timer，Timer 會被 Main Thread 的 RunLoop 強持有、並透過 RunLoop 輪詢機制定期觸發，直到 Timer invalidate() 才會被釋放；因此我們需要在 ViewController 上強持有 Timer 並在 deinit 時呼叫 Timer invalidate()，才能在畫面退出後正確終止釋放 Timer。</p>
<ul>
  <li>⭐️️️View Controller 強持有 Timer， <strong>Timer 的 Execution Block (handler / closure)務必為 Weak Self；否則會 Retain Cycle。</strong></li>
  <li>⭐️️️務必在 View Controller 生命週期結束時呼叫 Timer invalidate()，否則 RunLoop 仍會持有 Timer 繼續執行。</li>
</ul>

<blockquote>
  <p><em>RunLoop 是 Thread 內的事件處理迴圈，會輪詢接收處理事件； <strong>Main Thread 系統會自動建立 RunLoop (RunLoop.main)</strong> ，除此之外其他 Thread 不一定會有 RunLoop。</em></p>
</blockquote>

<h4 id="使用">使用</h4>

<p>我們可以直接使用 <code class="language-plaintext highlighter-rouge">Timer.scheduledTimer</code> 宣告一個 Timer (會自動加入 RunLoop.main &amp; <strong>Mode: .default</strong> ):</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">final</span> <span class="kd">class</span> <span class="kt">HomeViewController</span><span class="p">:</span> <span class="kt">UIViewController</span> <span class="p">{</span>
    
    <span class="kd">private</span> <span class="k">var</span> <span class="nv">timer</span><span class="p">:</span> <span class="kt">Timer</span><span class="p">?</span>
    
    <span class="kd">deinit</span> <span class="p">{</span>
        <span class="k">self</span><span class="o">.</span><span class="n">timer</span><span class="p">?</span><span class="o">.</span><span class="nf">invalidate</span><span class="p">()</span>
        <span class="k">self</span><span class="o">.</span><span class="n">timer</span> <span class="o">=</span> <span class="kc">nil</span>
    <span class="p">}</span>

    <span class="k">override</span> <span class="kd">func</span> <span class="nf">viewDidLoad</span><span class="p">()</span> <span class="p">{</span>
        <span class="k">super</span><span class="o">.</span><span class="nf">viewDidLoad</span><span class="p">()</span>
        <span class="nf">startCarousel</span><span class="p">()</span>
    <span class="p">}</span>
    
    <span class="kd">private</span> <span class="kd">func</span> <span class="nf">startCarousel</span><span class="p">()</span> <span class="p">{</span>
        <span class="k">self</span><span class="o">.</span><span class="n">timer</span> <span class="o">=</span> <span class="kt">Timer</span><span class="o">.</span><span class="nf">scheduledTimer</span><span class="p">(</span><span class="nv">withTimeInterval</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="nv">repeats</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span> <span class="nv">block</span><span class="p">:</span> <span class="p">{</span> <span class="p">[</span><span class="k">weak</span> <span class="k">self</span><span class="p">]</span> <span class="n">_</span> <span class="k">in</span>
            <span class="k">self</span><span class="p">?</span><span class="o">.</span><span class="nf">doSomething</span><span class="p">()</span>
        <span class="p">})</span>
    <span class="p">}</span>

    <span class="kd">private</span> <span class="kd">func</span> <span class="nf">doSomething</span><span class="p">()</span> <span class="p">{</span>
        <span class="nf">print</span><span class="p">(</span><span class="s">"Hello World!"</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>也可以自行宣告 Timer 物件加入到 RunLoop:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">timer</span><span class="o">=</span> <span class="kt">Timer</span><span class="p">(</span><span class="nv">timeInterval</span><span class="p">:</span> <span class="mf">1.0</span><span class="p">,</span> <span class="nv">repeats</span><span class="p">:</span> <span class="kc">true</span><span class="p">)</span> <span class="p">{</span> <span class="p">[</span><span class="k">weak</span> <span class="k">self</span><span class="p">]</span> <span class="n">_</span> <span class="k">in</span>
  <span class="c1">// do something..</span>
<span class="p">}</span>
<span class="k">self</span><span class="o">.</span><span class="n">timer</span> <span class="o">=</span> <span class="n">timer</span>
<span class="c1">// 加入 RunLoop 後才會開始執行</span>
<span class="kt">RunLoop</span><span class="o">.</span><span class="n">main</span><span class="o">.</span><span class="nf">add</span><span class="p">(</span><span class="n">timer</span><span class="p">,</span> <span class="nv">forMode</span><span class="p">:</span> <span class="o">.</span><span class="k">default</span><span class="p">)</span>
</code></pre></div></div>
<h4 id="timer-的操作方法">Timer 的操作方法</h4>
<ul>
  <li><code class="language-plaintext highlighter-rouge">invalidate()</code> 終止 Timer</li>
  <li><code class="language-plaintext highlighter-rouge">fire()</code> 立即觸發一次</li>
</ul>

<h4 id="runloop-mode-的影響">RunLoop <strong>Mode 的影響</strong></h4>
<ul>
  <li><code class="language-plaintext highlighter-rouge">.default</code> ：預設加入的 Mode，主要是處理 UI 顯示。
<strong>會在切換到 <code class="language-plaintext highlighter-rouge">.tracking</code> Mode 時先暫停</strong></li>
  <li><code class="language-plaintext highlighter-rouge">.tracking</code> ：處理 ScrollView 滾動、Gesture 手勢。</li>
  <li><code class="language-plaintext highlighter-rouge">.common</code> ： <code class="language-plaintext highlighter-rouge">.default</code> + <code class="language-plaintext highlighter-rouge">.tracking</code> 都會處理。</li>
</ul>

<blockquote>
  <p><em>⭐️️️⭐️️️⭐️️️因此在默認情況下，我們的 Timer 是加到 <code class="language-plaintext highlighter-rouge">.default</code> Mode， <strong>會在使用者滾動 ScrollView 或是手勢操作時自動暫停</strong> ，等到操作結束後才會繼續，可能造成 Timer 延後觸發或是次數低於預期。</em></p>
</blockquote>

<p><strong>對此，我們可以把 Timer 改加入到 .common Mode 就能解決以上問題:</strong></p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">RunLoop</span><span class="o">.</span><span class="n">main</span><span class="o">.</span><span class="nf">add</span><span class="p">(</span><span class="n">timer</span><span class="p">,</span> <span class="nv">forMode</span><span class="p">:</span> <span class="o">.</span><span class="n">common</span><span class="p">)</span>
</code></pre></div></div>
<h3 id="grand-central-dispatch--dispatchsourcetimer"><a href="https://developer.apple.com/documentation/dispatch/dispatchsourcetimer" target="_blank">Grand Central Dispatch — DispatchSourceTimer</a></h3>

<p>除了 Timer 之外，GCD 也提供了另一個 DispatchSourceTimer 方法可供選擇。</p>
<h4 id="優缺點-1">優缺點</h4>

<p><strong>DispatchSourceTimer 的優點：</strong></p>
<ul>
  <li>操作彈性(支援 <code class="language-plaintext highlighter-rouge">suspend</code> , <code class="language-plaintext highlighter-rouge">resume</code> ) 較好</li>
  <li>精確度與可靠程度較高：依賴 GCD Queue</li>
  <li>可自行設定 leeway 控制耗電量</li>
  <li>可穩定常駐任務 (GCD Queue)</li>
</ul>

<p><strong>DispatchSourceTimer 的缺點：</strong></p>
<ul>
  <li>UI 操作需自行切換回 Main Thread</li>
  <li>API 使用複雜且有順序， <strong>用錯會 Crash</strong></li>
  <li>需要封裝才能安全使用</li>
</ul>

<h4 id="適合場景-1">適合場景</h4>

<p>相較 Timer 適合 UI 層面的場景，DispatchSourceTimer 比較適合做那些跟 UI 或使用者當前畫面無關的任務場景；最常見的就是發送 Tracking 事件，我們會定時把使用者操作產生的事件發送到伺服器，或是定時清理無用的 CoreData 資料；這些就很適合使用 DispatchSourceTimer。</p>
<h4 id="生命週期-1">生命週期</h4>

<p><img src="/assets/62f68ebeecd3/1*2hIwImzm6ubpc5zK_roI3Q.webp" alt="" loading="lazy" decoding="async" width="1289" height="914" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjg5IiBoZWlnaHQ9IjkxNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>DispatchSourceTimer 的生命週期取決於是否仍被外部物件持有；GCD queue 本身不會強持有 timer 的 owner，只負責調度與執行事件。</p>
<h4 id="閃退問題">閃退問題</h4>

<p>DispatchSourceTimer 雖然提供更多可操作方法： <code class="language-plaintext highlighter-rouge">activate</code> , <code class="language-plaintext highlighter-rouge">suspend</code> , <code class="language-plaintext highlighter-rouge">resume</code> , <code class="language-plaintext highlighter-rouge">cancel</code> ；但是它極其敏感，只要呼叫的順序不對就會直閃退 (EXC_BREAKPOINT/DispatchSourceTimer) 非常危險。</p>

<p><img src="/assets/62f68ebeecd3/1*04cApJDt29a-98zgb9Wd2A.webp" alt="" loading="lazy" decoding="async" width="683" height="185" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2ODMiIGhlaWdodD0iMTg1Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><strong>以下情況均會直接閃退：</strong></p>
<ul>
  <li>❌ suspend() 與 resume() 沒有成對使用
suspend() 後又呼叫一次 suspend()
resume() 後又呼叫一次 resume()</li>
  <li>❌ suspend() 後呼叫 cancel()
需要先 resume() 才能 cancel()</li>
  <li>❌ suspend() 狀態下 Timer 被釋放 (nil)</li>
  <li>❌ cancel() 後再呼叫其他操作</li>
</ul>

<h4 id="使用-finite-state-machine-有限狀態機封裝操作">使用 Finite-State Machine 有限狀態機封裝操作</h4>

<p>進入本篇文章的另一個重點，該如何安全的使用 DispatchSourceTimer?</p>

<p><img src="/assets/62f68ebeecd3/1*S33K4OWFUPMocZvM4dLWvw.webp" alt="" loading="lazy" decoding="async" width="1400" height="1151" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjExNTEiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>如上圖所示，我們使用有限狀態機封裝 DispatchSourceTimer 的操作，使其可以更安全、更容易的使用:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">final</span> <span class="kd">class</span> <span class="kt">DispatchSourceTimerMachine</span> <span class="p">{</span>
    <span class="c1">// 有限狀態機有哪些狀態</span>
    <span class="kd">private</span> <span class="kd">enum</span> <span class="kt">TimerState</span> <span class="p">{</span>
        <span class="c1">// 初始狀態</span>
        <span class="k">case</span> <span class="n">idle</span>
        <span class="c1">// 執行中</span>
        <span class="k">case</span> <span class="n">running</span>
        <span class="c1">// 暫停中</span>
        <span class="k">case</span> <span class="n">suspended</span>
        <span class="c1">// 終止中</span>
        <span class="k">case</span> <span class="n">cancelled</span>
    <span class="p">}</span>

    <span class="kd">private</span> <span class="k">var</span> <span class="nv">timer</span><span class="p">:</span> <span class="kt">DispatchSourceTimer</span><span class="p">?</span>
    <span class="kd">private</span> <span class="kd">lazy</span> <span class="k">var</span> <span class="nv">timerQueue</span><span class="p">:</span> <span class="kt">DispatchQueue</span> <span class="o">=</span> <span class="p">{</span>
        <span class="kt">DispatchQueue</span><span class="p">(</span><span class="nv">label</span><span class="p">:</span> <span class="s">"li.zhgchg.DispatchSourceTimerMachine"</span><span class="p">,</span> <span class="nv">qos</span><span class="p">:</span> <span class="o">.</span><span class="n">background</span><span class="p">)</span>
    <span class="p">}()</span>

    <span class="kd">private</span> <span class="k">var</span> <span class="nv">_state</span><span class="p">:</span> <span class="kt">TimerState</span> <span class="o">=</span> <span class="o">.</span><span class="n">idle</span>
    
    <span class="kd">deinit</span> <span class="p">{</span>
        <span class="c1">// Owner 物件消失時，同步 cancel timer</span>
        <span class="c1">// 雖不做也不影響(handler 是 weak)，但是可以確保流程符合預期</span>
        <span class="k">if</span> <span class="n">_state</span> <span class="o">==</span> <span class="o">.</span><span class="n">suspended</span> <span class="p">{</span>
            <span class="n">timer</span><span class="p">?</span><span class="o">.</span><span class="nf">resume</span><span class="p">()</span>
            <span class="n">_state</span> <span class="o">=</span> <span class="o">.</span><span class="n">running</span>
        <span class="p">}</span>
        <span class="k">if</span> <span class="n">_state</span> <span class="o">==</span> <span class="o">.</span><span class="n">running</span> <span class="p">{</span>
            <span class="n">timer</span><span class="p">?</span><span class="o">.</span><span class="nf">cancel</span><span class="p">()</span>
            <span class="n">timer</span> <span class="o">=</span> <span class="kc">nil</span>
            <span class="n">_state</span> <span class="o">=</span> <span class="o">.</span><span class="n">cancelled</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="c1">// 啟動 Timer</span>
    <span class="kd">func</span> <span class="nf">activate</span><span class="p">(</span><span class="nv">repeatTimeInterval</span><span class="p">:</span> <span class="kt">DispatchTimeInterval</span><span class="p">,</span> <span class="nv">handler</span><span class="p">:</span> <span class="kt">DispatchSource</span><span class="o">.</span><span class="kt">DispatchSourceHandler</span><span class="p">?)</span> <span class="p">{</span>
        <span class="c1">// 只有 idle, cancelled 狀態可以啟用 Timer</span>
        <span class="k">guard</span> <span class="p">[</span><span class="o">.</span><span class="n">idle</span><span class="p">,</span> <span class="o">.</span><span class="n">cancelled</span><span class="p">]</span><span class="o">.</span><span class="nf">contains</span><span class="p">(</span><span class="n">_state</span><span class="p">)</span> <span class="k">else</span> <span class="p">{</span> <span class="k">return</span> <span class="p">}</span>
        
        <span class="c1">// 建立 Timer and activate()</span>
        <span class="k">let</span> <span class="nv">timer</span> <span class="o">=</span> <span class="nf">makeTimer</span><span class="p">(</span><span class="nv">repeatTimeInterval</span><span class="p">:</span> <span class="n">repeatTimeInterval</span><span class="p">,</span> <span class="nv">handler</span><span class="p">:</span> <span class="n">handler</span><span class="p">)</span>
        <span class="k">self</span><span class="o">.</span><span class="n">timer</span> <span class="o">=</span> <span class="n">timer</span>
        <span class="n">timer</span><span class="o">.</span><span class="nf">activate</span><span class="p">()</span>
        
        <span class="c1">// 切換到 running 狀態</span>
        <span class="n">_state</span> <span class="o">=</span> <span class="o">.</span><span class="n">running</span>
    <span class="p">}</span>

    <span class="c1">// 暫停 Timer</span>
    <span class="kd">func</span> <span class="nf">suspend</span><span class="p">()</span> <span class="p">{</span>
        <span class="c1">// 只有在 running 狀態可以暫停 Timer</span>
        <span class="k">guard</span> <span class="p">[</span><span class="o">.</span><span class="n">running</span><span class="p">]</span><span class="o">.</span><span class="nf">contains</span><span class="p">(</span><span class="n">_state</span><span class="p">)</span> <span class="k">else</span> <span class="p">{</span> <span class="k">return</span> <span class="p">}</span>
        
        <span class="c1">// 暫停 Timer</span>
        <span class="n">timer</span><span class="p">?</span><span class="o">.</span><span class="nf">suspend</span><span class="p">()</span>
        
        <span class="c1">// 切換到 suspended 狀態</span>
        <span class="n">_state</span> <span class="o">=</span> <span class="o">.</span><span class="n">suspended</span>
    <span class="p">}</span>

    <span class="c1">// 恢復 Timer</span>
    <span class="kd">func</span> <span class="nf">resume</span><span class="p">()</span> <span class="p">{</span>
        <span class="c1">// 只有在 suspended 狀態可以恢復 Timer</span>
        <span class="k">guard</span> <span class="p">[</span><span class="o">.</span><span class="n">suspended</span><span class="p">]</span><span class="o">.</span><span class="nf">contains</span><span class="p">(</span><span class="n">_state</span><span class="p">)</span> <span class="k">else</span> <span class="p">{</span> <span class="k">return</span> <span class="p">}</span>
        
        <span class="c1">// 恢復 Timer</span>
        <span class="n">timer</span><span class="p">?</span><span class="o">.</span><span class="nf">resume</span><span class="p">()</span>
        
        <span class="c1">// 切換到 running 狀態</span>
        <span class="n">_state</span> <span class="o">=</span> <span class="o">.</span><span class="n">running</span>
    <span class="p">}</span>

    <span class="c1">// 終止 Timer</span>
    <span class="kd">func</span> <span class="nf">cancel</span><span class="p">()</span> <span class="p">{</span>
        <span class="c1">// 只有在 suspended, running 狀態可以終止 Timer</span>
        <span class="k">guard</span> <span class="p">[</span><span class="o">.</span><span class="n">suspended</span><span class="p">,</span> <span class="o">.</span><span class="n">running</span><span class="p">]</span><span class="o">.</span><span class="nf">contains</span><span class="p">(</span><span class="n">_state</span><span class="p">)</span> <span class="k">else</span> <span class="p">{</span> <span class="k">return</span> <span class="p">}</span>
        
        <span class="c1">// 如果當前是 suspended 狀態，先 resume() 再終止</span>
        <span class="c1">// 此為 DispatchSourceTimer 的限制，只能在 running 才能 cancel()</span>
        <span class="k">if</span> <span class="n">_state</span> <span class="o">==</span> <span class="o">.</span><span class="n">suspended</span> <span class="p">{</span>
            <span class="k">self</span><span class="o">.</span><span class="nf">resume</span><span class="p">()</span>
        <span class="p">}</span>

        <span class="c1">// 終止 Timer</span>
        <span class="n">timer</span><span class="p">?</span><span class="o">.</span><span class="nf">cancel</span><span class="p">()</span>
        <span class="n">timer</span> <span class="o">=</span> <span class="kc">nil</span>
        
        <span class="c1">// 切換到 cancelled 狀態</span>
        <span class="n">_state</span> <span class="o">=</span> <span class="o">.</span><span class="n">cancelled</span>
    <span class="p">}</span>
    
    <span class="kd">private</span> <span class="kd">func</span> <span class="nf">makeTimer</span><span class="p">(</span><span class="nv">repeatTimeInterval</span><span class="p">:</span> <span class="kt">DispatchTimeInterval</span><span class="p">,</span> <span class="nv">handler</span><span class="p">:</span> <span class="kt">DispatchSourceProtocol</span><span class="o">.</span><span class="kt">DispatchSourceHandler</span><span class="p">?)</span> <span class="o">-&gt;</span> <span class="kt">DispatchSourceTimer</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nv">timer</span> <span class="o">=</span> <span class="kt">DispatchSource</span><span class="o">.</span><span class="nf">makeTimerSource</span><span class="p">(</span><span class="nv">queue</span><span class="p">:</span> <span class="n">timerQueue</span><span class="p">)</span>
        <span class="n">timer</span><span class="o">.</span><span class="nf">schedule</span><span class="p">(</span><span class="nv">deadline</span><span class="p">:</span> <span class="o">.</span><span class="nf">now</span><span class="p">(),</span> <span class="nv">repeating</span><span class="p">:</span> <span class="n">repeatTimeInterval</span><span class="p">)</span>
        <span class="n">timer</span><span class="o">.</span><span class="nf">setEventHandler</span><span class="p">(</span><span class="nv">qos</span><span class="p">:</span> <span class="o">.</span><span class="n">background</span><span class="p">,</span> <span class="nv">handler</span><span class="p">:</span> <span class="n">handler</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">timer</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>我們簡單使用有限狀態機「封裝了狀態可以轉換成什麼狀態」與「狀態需要做什麼」的邏輯，如果在錯誤的狀態下呼叫會被忽略(不會閃退)，我們還多做了一些優化，例如 suspended 狀態也能 cancel、cancelled 狀態能重新 activate。</p>

<blockquote>
  <p><strong><em>延伸閱讀：</em></strong></p>
</blockquote>

<blockquote>
  <p><em>之前寫過另一篇文章「 <a href="https://zhgchg.li/posts/pinkoi-engineering/design-patterns-%E5%AF%A6%E6%88%B0%E6%87%89%E7%94%A8-%E5%B0%81%E8%A3%9D-socket-io-%E5%8D%B3%E6%99%82%E9%80%9A%E8%A8%8A%E6%9E%B6%E6%A7%8B%E8%88%87%E4%B8%83%E5%A4%A7%E8%A8%AD%E8%A8%88%E6%A8%A1%E5%BC%8F%E8%A7%A3%E6%9E%90-78507a8de6a5/#%E9%9C%80%E6%B1%82%E5%A0%B4%E6%99%AF-3" target="_blank">Design Patterns 實戰應用｜封裝 Socket.IO 即時通訊架構</a> 」中也有使用到有限狀態機，另外還多使用了 State Pattern。</em></p>
</blockquote>

<blockquote>
  <p><em>Finite-State Machine 有限狀態機: 關注的是狀態之間的轉換控制與該做什麼。</em></p>
</blockquote>

<blockquote>
  <p><em>State Pattern: 關注的是每個狀態內的行為邏輯。</em></p>
</blockquote>

<h4 id="使用-serial-queue-操作有限狀態機狀態轉換">使用 Serial Queue 操作有限狀態機狀態轉換</h4>

<p>有了狀態機確保 DispatchSourceTimer 能安全使用之後還沒結束，我們無法保證在外部呼叫使用 DispatchSourceTimerMachine 的地方是在同個 Thread，如果不同 Thread 都操作了這個物件就會造成 Race Condition 一樣會引發閃退。</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">final</span> <span class="kd">class</span> <span class="kt">DispatchSourceTimerMachine</span> <span class="p">{</span>
    <span class="c1">// 有限狀態機有哪些狀態</span>
    <span class="kd">private</span> <span class="kd">enum</span> <span class="kt">TimerState</span> <span class="p">{</span>
        <span class="c1">// 初始狀態</span>
        <span class="k">case</span> <span class="n">idle</span>
        <span class="c1">// 執行中</span>
        <span class="k">case</span> <span class="n">running</span>
        <span class="c1">// 暫停中</span>
        <span class="k">case</span> <span class="n">suspended</span>
        <span class="c1">// 終止中</span>
        <span class="k">case</span> <span class="n">cancelled</span>
    <span class="p">}</span>

    <span class="kd">private</span> <span class="k">var</span> <span class="nv">timer</span><span class="p">:</span> <span class="kt">DispatchSourceTimer</span><span class="p">?</span>
    <span class="kd">private</span> <span class="kd">lazy</span> <span class="k">var</span> <span class="nv">timerQueue</span><span class="p">:</span> <span class="kt">DispatchQueue</span> <span class="o">=</span> <span class="p">{</span>
        <span class="kt">DispatchQueue</span><span class="p">(</span><span class="nv">label</span><span class="p">:</span> <span class="s">"li.zhgchg.DispatchSourceTimerMachine"</span><span class="p">,</span> <span class="nv">qos</span><span class="p">:</span> <span class="o">.</span><span class="n">background</span><span class="p">)</span>
    <span class="p">}()</span>

    <span class="kd">private</span> <span class="k">var</span> <span class="nv">_state</span><span class="p">:</span> <span class="kt">TimerState</span> <span class="o">=</span> <span class="o">.</span><span class="n">idle</span>

    <span class="kd">private</span> <span class="kd">static</span> <span class="k">let</span> <span class="nv">operationQueueSpecificKey</span> <span class="o">=</span> <span class="kt">DispatchSpecificKey</span><span class="o">&lt;</span><span class="kt">ObjectIdentifier</span><span class="o">&gt;</span><span class="p">()</span>
    <span class="kd">private</span> <span class="kd">lazy</span> <span class="k">var</span> <span class="nv">operationQueueSpecificValue</span><span class="p">:</span> <span class="kt">ObjectIdentifier</span> <span class="o">=</span> <span class="kt">ObjectIdentifier</span><span class="p">(</span><span class="k">self</span><span class="p">)</span>
    <span class="kd">private</span> <span class="kd">lazy</span> <span class="k">var</span> <span class="nv">operationQueue</span><span class="p">:</span> <span class="kt">DispatchQueue</span> <span class="o">=</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nv">queue</span> <span class="o">=</span> <span class="kt">DispatchQueue</span><span class="p">(</span><span class="nv">label</span><span class="p">:</span> <span class="s">"li.zhgchg.DispatchSourceTimerMachine.operationQueue"</span><span class="p">)</span>
        <span class="n">queue</span><span class="o">.</span><span class="nf">setSpecific</span><span class="p">(</span><span class="nv">key</span><span class="p">:</span> <span class="k">Self</span><span class="o">.</span><span class="n">operationQueueSpecificKey</span><span class="p">,</span> <span class="nv">value</span><span class="p">:</span> <span class="n">operationQueueSpecificValue</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">queue</span>
    <span class="p">}()</span>
    <span class="kd">private</span> <span class="kd">func</span> <span class="nf">operation</span><span class="p">(</span><span class="nv">async</span><span class="p">:</span> <span class="kt">Bool</span> <span class="o">=</span> <span class="kc">true</span><span class="p">,</span> <span class="n">_</span> <span class="nv">work</span><span class="p">:</span> <span class="kd">@escaping</span> <span class="p">()</span> <span class="o">-&gt;</span> <span class="kt">Void</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">if</span> <span class="kt">DispatchQueue</span><span class="o">.</span><span class="nf">getSpecific</span><span class="p">(</span><span class="nv">key</span><span class="p">:</span> <span class="k">Self</span><span class="o">.</span><span class="n">operationQueueSpecificKey</span><span class="p">)</span> <span class="o">==</span> <span class="n">operationQueueSpecificValue</span> <span class="p">{</span>
            <span class="nf">work</span><span class="p">()</span>
        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
            <span class="k">if</span> <span class="k">async</span> <span class="p">{</span>
                <span class="n">operationQueue</span><span class="o">.</span><span class="nf">async</span><span class="p">(</span><span class="nv">execute</span><span class="p">:</span> <span class="n">work</span><span class="p">)</span>
            <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
                <span class="n">operationQueue</span><span class="o">.</span><span class="nf">sync</span><span class="p">(</span><span class="nv">execute</span><span class="p">:</span> <span class="n">work</span><span class="p">)</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>
    
    <span class="kd">deinit</span> <span class="p">{</span>
        <span class="c1">// Owner 物件消失時，同步 cancel timer</span>
        <span class="c1">// 雖不做也不影響(handler 是 weak)，但是可以確保流程符合預期</span>
        <span class="c1">// 確保 sync 執行完畢</span>
        <span class="nf">operation</span><span class="p">(</span><span class="nv">async</span><span class="p">:</span> <span class="kc">false</span><span class="p">)</span> <span class="p">{</span> <span class="p">[</span><span class="k">self</span><span class="p">]</span> <span class="k">in</span>
            <span class="k">if</span> <span class="n">_state</span> <span class="o">==</span> <span class="o">.</span><span class="n">suspended</span> <span class="p">{</span>
                <span class="n">timer</span><span class="p">?</span><span class="o">.</span><span class="nf">resume</span><span class="p">()</span>
                <span class="n">_state</span> <span class="o">=</span> <span class="o">.</span><span class="n">running</span>
            <span class="p">}</span>
            <span class="k">if</span> <span class="n">_state</span> <span class="o">==</span> <span class="o">.</span><span class="n">running</span> <span class="p">{</span>
                <span class="n">timer</span><span class="p">?</span><span class="o">.</span><span class="nf">cancel</span><span class="p">()</span>
                <span class="n">timer</span> <span class="o">=</span> <span class="kc">nil</span>
                <span class="n">_state</span> <span class="o">=</span> <span class="o">.</span><span class="n">cancelled</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="c1">// 啟動 Timer</span>
    <span class="kd">func</span> <span class="nf">activate</span><span class="p">(</span><span class="nv">repeatTimeInterval</span><span class="p">:</span> <span class="kt">DispatchTimeInterval</span><span class="p">,</span> <span class="nv">handler</span><span class="p">:</span> <span class="kt">DispatchSource</span><span class="o">.</span><span class="kt">DispatchSourceHandler</span><span class="p">?)</span> <span class="p">{</span>
        <span class="n">operation</span> <span class="p">{</span> <span class="p">[</span><span class="k">weak</span> <span class="k">self</span><span class="p">]</span> <span class="k">in</span>
            <span class="k">guard</span> <span class="k">let</span> <span class="nv">self</span> <span class="o">=</span> <span class="k">self</span> <span class="k">else</span> <span class="p">{</span> <span class="k">return</span> <span class="p">}</span>
            <span class="c1">// 只有 idle, cancelled 狀態可以啟用 Timer</span>
            <span class="k">guard</span> <span class="p">[</span><span class="o">.</span><span class="n">idle</span><span class="p">,</span> <span class="o">.</span><span class="n">cancelled</span><span class="p">]</span><span class="o">.</span><span class="nf">contains</span><span class="p">(</span><span class="n">_state</span><span class="p">)</span> <span class="k">else</span> <span class="p">{</span> <span class="k">return</span> <span class="p">}</span>
            
            <span class="c1">// 建立 Timer and activate()</span>
            <span class="k">let</span> <span class="nv">timer</span> <span class="o">=</span> <span class="nf">makeTimer</span><span class="p">(</span><span class="nv">repeatTimeInterval</span><span class="p">:</span> <span class="n">repeatTimeInterval</span><span class="p">,</span> <span class="nv">handler</span><span class="p">:</span> <span class="n">handler</span><span class="p">)</span>
            <span class="k">self</span><span class="o">.</span><span class="n">timer</span> <span class="o">=</span> <span class="n">timer</span>
            <span class="n">timer</span><span class="o">.</span><span class="nf">activate</span><span class="p">()</span>
            
            <span class="c1">// 切換到 running 狀態</span>
            <span class="n">_state</span> <span class="o">=</span> <span class="o">.</span><span class="n">running</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="c1">// 暫停 Timer</span>
    <span class="kd">func</span> <span class="nf">suspend</span><span class="p">()</span> <span class="p">{</span>
        <span class="n">operation</span> <span class="p">{</span> <span class="p">[</span><span class="k">weak</span> <span class="k">self</span><span class="p">]</span> <span class="k">in</span>
            <span class="k">guard</span> <span class="k">let</span> <span class="nv">self</span> <span class="o">=</span> <span class="k">self</span> <span class="k">else</span> <span class="p">{</span> <span class="k">return</span> <span class="p">}</span>
            <span class="c1">// 只有在 running 狀態可以暫停 Timer</span>
            <span class="k">guard</span> <span class="p">[</span><span class="o">.</span><span class="n">running</span><span class="p">]</span><span class="o">.</span><span class="nf">contains</span><span class="p">(</span><span class="n">_state</span><span class="p">)</span> <span class="k">else</span> <span class="p">{</span> <span class="k">return</span> <span class="p">}</span>
            
            <span class="c1">// 暫停 Timer</span>
            <span class="n">timer</span><span class="p">?</span><span class="o">.</span><span class="nf">suspend</span><span class="p">()</span>
            
            <span class="c1">// 切換到 suspended 狀態</span>
            <span class="n">_state</span> <span class="o">=</span> <span class="o">.</span><span class="n">suspended</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="c1">// 恢復 Timer</span>
    <span class="kd">func</span> <span class="nf">resume</span><span class="p">()</span> <span class="p">{</span>
        <span class="n">operation</span> <span class="p">{</span> <span class="p">[</span><span class="k">weak</span> <span class="k">self</span><span class="p">]</span> <span class="k">in</span>
            <span class="k">guard</span> <span class="k">let</span> <span class="nv">self</span> <span class="o">=</span> <span class="k">self</span> <span class="k">else</span> <span class="p">{</span> <span class="k">return</span> <span class="p">}</span>
            <span class="c1">// 只有在 suspended 狀態可以恢復 Timer</span>
            <span class="k">guard</span> <span class="p">[</span><span class="o">.</span><span class="n">suspended</span><span class="p">]</span><span class="o">.</span><span class="nf">contains</span><span class="p">(</span><span class="n">_state</span><span class="p">)</span> <span class="k">else</span> <span class="p">{</span> <span class="k">return</span> <span class="p">}</span>
            
            <span class="c1">// 恢復 Timer</span>
            <span class="n">timer</span><span class="p">?</span><span class="o">.</span><span class="nf">resume</span><span class="p">()</span>
            
            <span class="c1">// 切換到 running 狀態</span>
            <span class="n">_state</span> <span class="o">=</span> <span class="o">.</span><span class="n">running</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="c1">// 終止 Timer</span>
    <span class="kd">func</span> <span class="nf">cancel</span><span class="p">()</span> <span class="p">{</span>
        <span class="n">operation</span> <span class="p">{</span> <span class="p">[</span><span class="k">weak</span> <span class="k">self</span><span class="p">]</span> <span class="k">in</span>
            <span class="k">guard</span> <span class="k">let</span> <span class="nv">self</span> <span class="o">=</span> <span class="k">self</span> <span class="k">else</span> <span class="p">{</span> <span class="k">return</span> <span class="p">}</span>
            <span class="c1">// 只有在 suspended, running 狀態可以終止 Timer</span>
            <span class="k">guard</span> <span class="p">[</span><span class="o">.</span><span class="n">suspended</span><span class="p">,</span> <span class="o">.</span><span class="n">running</span><span class="p">]</span><span class="o">.</span><span class="nf">contains</span><span class="p">(</span><span class="n">_state</span><span class="p">)</span> <span class="k">else</span> <span class="p">{</span> <span class="k">return</span> <span class="p">}</span>
            
            <span class="c1">// 如果當前是 suspended 狀態，先 resume() 再終止</span>
            <span class="c1">// 此為 DispatchSourceTimer 的限制，只能在 running 才能 cancel()</span>
            <span class="k">if</span> <span class="n">_state</span> <span class="o">==</span> <span class="o">.</span><span class="n">suspended</span> <span class="p">{</span>
                <span class="k">self</span><span class="o">.</span><span class="nf">resume</span><span class="p">()</span>
            <span class="p">}</span>
            
            <span class="c1">// 終止 Timer</span>
            <span class="n">timer</span><span class="p">?</span><span class="o">.</span><span class="nf">cancel</span><span class="p">()</span>
            <span class="n">timer</span> <span class="o">=</span> <span class="kc">nil</span>
            
            <span class="c1">// 切換到 cancelled 狀態</span>
            <span class="n">_state</span> <span class="o">=</span> <span class="o">.</span><span class="n">cancelled</span>
        <span class="p">}</span>
    <span class="p">}</span>
    
    <span class="kd">private</span> <span class="kd">func</span> <span class="nf">makeTimer</span><span class="p">(</span><span class="nv">repeatTimeInterval</span><span class="p">:</span> <span class="kt">DispatchTimeInterval</span><span class="p">,</span> <span class="nv">handler</span><span class="p">:</span> <span class="kt">DispatchSourceProtocol</span><span class="o">.</span><span class="kt">DispatchSourceHandler</span><span class="p">?)</span> <span class="o">-&gt;</span> <span class="kt">DispatchSourceTimer</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nv">timer</span> <span class="o">=</span> <span class="kt">DispatchSource</span><span class="o">.</span><span class="nf">makeTimerSource</span><span class="p">(</span><span class="nv">queue</span><span class="p">:</span> <span class="n">timerQueue</span><span class="p">)</span>
        <span class="n">timer</span><span class="o">.</span><span class="nf">schedule</span><span class="p">(</span><span class="nv">deadline</span><span class="p">:</span> <span class="o">.</span><span class="nf">now</span><span class="p">(),</span> <span class="nv">repeating</span><span class="p">:</span> <span class="n">repeatTimeInterval</span><span class="p">)</span>
        <span class="n">timer</span><span class="o">.</span><span class="nf">setEventHandler</span><span class="p">(</span><span class="nv">qos</span><span class="p">:</span> <span class="o">.</span><span class="n">background</span><span class="p">,</span> <span class="nv">handler</span><span class="p">:</span> <span class="n">handler</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">timer</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>現在，我們可以安全無憂的使用 <code class="language-plaintext highlighter-rouge">DispatchSourceTimerMachine</code> 物件作為 Timer 了:</p>
<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">final</span> <span class="kd">class</span> <span class="nc">TrackingEventSender</span> <span class="p">{</span>

    <span class="k">private</span> <span class="kd">let</span> <span class="nx">timerMachine</span> <span class="o">=</span> <span class="nc">DispatchSourceTimerMachine</span><span class="p">()</span>
    <span class="k">public</span> <span class="kd">var</span> <span class="nx">events</span><span class="p">:</span> <span class="p">[</span><span class="nb">String</span><span class="p">:</span> <span class="nb">String</span><span class="p">]</span> <span class="o">=</span> <span class="p">[]</span>

    <span class="c1">// 啟動定期 tracking</span>
    <span class="nx">func</span> <span class="nf">startTracking</span><span class="p">()</span> <span class="p">{</span>
        <span class="nx">timerMachine</span><span class="p">.</span><span class="nf">activate</span><span class="p">(</span><span class="nx">repeatTimeInterval</span><span class="p">:</span> <span class="p">.</span><span class="nf">seconds</span><span class="p">(</span><span class="mi">30</span><span class="p">))</span> <span class="p">{</span> <span class="p">[</span><span class="nx">weak</span> <span class="nb">self</span><span class="p">]</span> <span class="k">in</span>
            <span class="nb">self</span><span class="p">?.</span><span class="nf">sendTrackingEvent</span><span class="p">()</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="c1">// 暫停 tracking（例如 App 進背景）</span>
    <span class="nx">func</span> <span class="nf">pauseTracking</span><span class="p">()</span> <span class="p">{</span>
        <span class="nx">timerMachine</span><span class="p">.</span><span class="nf">suspend</span><span class="p">()</span>
    <span class="p">}</span>

    <span class="c1">// 恢復 tracking（例如 App 回前景）</span>
    <span class="nx">func</span> <span class="nf">resumeTracking</span><span class="p">()</span> <span class="p">{</span>
        <span class="nx">timerMachine</span><span class="p">.</span><span class="nf">resume</span><span class="p">()</span>
    <span class="p">}</span>

    <span class="c1">// 停止 tracking（例如頁面離開）</span>
    <span class="nx">func</span> <span class="nf">stopTracking</span><span class="p">()</span> <span class="p">{</span>
        <span class="nx">timerMachine</span><span class="p">.</span><span class="nf">cancel</span><span class="p">()</span>
    <span class="p">}</span>

    <span class="k">private</span> <span class="nx">func</span> <span class="nf">sendTrackingEvent</span><span class="p">()</span> <span class="p">{</span>
        <span class="c1">// send events to server...</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>到此如何安全的使用 DispatchSourceTimer 環節已結束，再來延伸幾個 Design Patterns 的使用，方便我們抽象物件進行測試跟 DispatchSourceHandler 執行邏輯抽象。</p>
<h4 id="延伸--使用-adapter-pattern--factory-pattern-產生-dispatchsourcetimer-利於抽象測試">延伸 — 使用 Adapter Pattern + Factory Pattern 產生 DispatchSourceTimer (利於抽象測試)</h4>

<p>DispatchSourceTimer 是 GCD 的 Objective-C 物件，在測試環節我們很難對其 Mock (無 Protocol)；因此我們需要自己定義一層 Protocol + Factory Pattern 產生，讓 TimerStateMachine 是能寫測試的。</p>

<p><strong>Adapter Pattern— 封裝 DispatchSourceTimer 操作:</strong></p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">protocol</span> <span class="kt">TimerAdapter</span> <span class="p">{</span>
    <span class="kd">func</span> <span class="nf">schedule</span><span class="p">(</span><span class="nv">repeating</span><span class="p">:</span> <span class="kt">DispatchTimeInterval</span><span class="p">)</span>
    <span class="kd">func</span> <span class="nf">setEventHandler</span><span class="p">(</span><span class="nv">handler</span><span class="p">:</span> <span class="kt">DispatchSourceProtocol</span><span class="o">.</span><span class="kt">DispatchSourceHandler</span><span class="p">?)</span>
    <span class="kd">func</span> <span class="nf">activate</span><span class="p">()</span>
    <span class="kd">func</span> <span class="nf">suspend</span><span class="p">()</span>
    <span class="kd">func</span> <span class="nf">resume</span><span class="p">()</span>
    <span class="kd">func</span> <span class="nf">cancel</span><span class="p">()</span>
<span class="p">}</span>

<span class="c1">// DispatchSourceTimer 的 Adapter 實現</span>
<span class="kd">final</span> <span class="kd">class</span> <span class="kt">DispatchSourceTimerAdapter</span><span class="p">:</span> <span class="kt">TimerAdapter</span> <span class="p">{</span>
    <span class="c1">// 原始的 DispatchSourceTimer</span>
    <span class="kd">private</span> <span class="k">let</span> <span class="nv">timer</span><span class="p">:</span> <span class="kt">DispatchSourceTimer</span>

    <span class="nf">init</span><span class="p">(</span><span class="nv">label</span><span class="p">:</span> <span class="kt">String</span> <span class="o">=</span> <span class="s">"li.zhgchg.DispatchSourceTimerAdapter"</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nv">queue</span> <span class="o">=</span> <span class="kt">DispatchQueue</span><span class="p">(</span><span class="nv">label</span><span class="p">:</span> <span class="n">label</span><span class="p">,</span> <span class="nv">qos</span><span class="p">:</span> <span class="o">.</span><span class="n">background</span><span class="p">)</span>
        <span class="k">let</span> <span class="nv">timer</span> <span class="o">=</span> <span class="kt">DispatchSource</span><span class="o">.</span><span class="nf">makeTimerSource</span><span class="p">(</span><span class="nv">queue</span><span class="p">:</span> <span class="n">queue</span><span class="p">)</span>
        <span class="k">self</span><span class="o">.</span><span class="n">timer</span> <span class="o">=</span> <span class="n">timer</span>
    <span class="p">}</span>

    <span class="kd">func</span> <span class="nf">schedule</span><span class="p">(</span><span class="nv">repeating</span><span class="p">:</span> <span class="kt">DispatchTimeInterval</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">timer</span><span class="o">.</span><span class="nf">schedule</span><span class="p">(</span><span class="nv">deadline</span><span class="p">:</span> <span class="o">.</span><span class="nf">now</span><span class="p">(),</span> <span class="nv">repeating</span><span class="p">:</span> <span class="n">repeating</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="kd">func</span> <span class="nf">setEventHandler</span><span class="p">(</span><span class="nv">handler</span><span class="p">:</span> <span class="kt">DispatchSourceProtocol</span><span class="o">.</span><span class="kt">DispatchSourceHandler</span><span class="p">?)</span> <span class="p">{</span>
        <span class="n">timer</span><span class="o">.</span><span class="nf">setEventHandler</span><span class="p">(</span><span class="nv">qos</span><span class="p">:</span> <span class="o">.</span><span class="n">background</span><span class="p">,</span> <span class="nv">handler</span><span class="p">:</span> <span class="n">handler</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="kd">func</span> <span class="nf">activate</span><span class="p">()</span> <span class="p">{</span>
        <span class="n">timer</span><span class="o">.</span><span class="nf">activate</span><span class="p">()</span>
    <span class="p">}</span>

    <span class="kd">func</span> <span class="nf">suspend</span><span class="p">()</span> <span class="p">{</span>
        <span class="n">timer</span><span class="o">.</span><span class="nf">suspend</span><span class="p">()</span>
    <span class="p">}</span>

    <span class="kd">func</span> <span class="nf">resume</span><span class="p">()</span> <span class="p">{</span>
        <span class="n">timer</span><span class="o">.</span><span class="nf">resume</span><span class="p">()</span>
    <span class="p">}</span>

    <span class="kd">func</span> <span class="nf">cancel</span><span class="p">()</span> <span class="p">{</span>
        <span class="n">timer</span><span class="o">.</span><span class="nf">cancel</span><span class="p">()</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p><strong>Factory Pattern — 抽象產生 TimerAdapter 的方法:</strong></p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">protocol</span> <span class="kt">DispatchSourceTimerAdapterFactorySpec</span> <span class="p">{</span>
    <span class="kd">func</span> <span class="nf">makeTimer</span><span class="p">(</span><span class="nv">repeatTimeInterval</span><span class="p">:</span> <span class="kt">DispatchTimeInterval</span><span class="p">,</span> <span class="nv">handler</span><span class="p">:</span> <span class="kt">DispatchSourceProtocol</span><span class="o">.</span><span class="kt">DispatchSourceHandler</span><span class="p">?)</span> <span class="o">-&gt;</span> <span class="kt">TimerAdapter</span>
<span class="p">}</span>

<span class="c1">// 封裝 DispatchSourceTimerAdapter 產生步驟</span>
<span class="kd">final</span> <span class="kd">class</span> <span class="kt">DispatchSourceTimerAdapterFactory</span><span class="p">:</span> <span class="kt">DispatchSourceTimerAdapterFactorySpec</span> <span class="p">{</span>
    <span class="kd">public</span> <span class="kd">func</span> <span class="nf">makeTimer</span><span class="p">(</span><span class="nv">repeatTimeInterval</span><span class="p">:</span> <span class="kt">DispatchTimeInterval</span><span class="p">,</span> <span class="nv">handler</span><span class="p">:</span> <span class="kt">DispatchSourceProtocol</span><span class="o">.</span><span class="kt">DispatchSourceHandler</span><span class="p">?)</span> <span class="o">-&gt;</span> <span class="kt">TimerAdapter</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nv">timer</span> <span class="o">=</span> <span class="kt">DispatchSourceTimerAdapter</span><span class="p">()</span>
        <span class="n">timer</span><span class="o">.</span><span class="nf">schedule</span><span class="p">(</span><span class="nv">repeating</span><span class="p">:</span> <span class="n">repeatTimeInterval</span><span class="p">)</span>
        <span class="n">timer</span><span class="o">.</span><span class="nf">setEventHandler</span><span class="p">(</span><span class="nv">handler</span><span class="p">:</span> <span class="n">handler</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">timer</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p><strong>組合使用:</strong></p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">var</span> <span class="nv">stateMachine</span> <span class="o">=</span> <span class="kt">DispatchSourceTimerMachine</span><span class="p">(</span><span class="nv">timerFactory</span><span class="p">:</span> <span class="kt">DispatchSourceTimerAdapterFactory</span><span class="p">())</span>

<span class="c1">//</span>
<span class="kd">final</span> <span class="kd">class</span> <span class="kt">DispatchSourceTimerMachine</span> <span class="p">{</span>
    <span class="c1">// 略..</span>
    <span class="kd">private</span> <span class="k">var</span> <span class="nv">timer</span><span class="p">:</span> <span class="kt">TimerAdapter</span><span class="p">?</span>
    <span class="kd">private</span> <span class="k">let</span> <span class="nv">timerFactory</span><span class="p">:</span> <span class="kt">DispatchSourceTimerAdapterFactorySpec</span>
    <span class="kd">public</span> <span class="nf">init</span><span class="p">(</span><span class="nv">timerFactory</span><span class="p">:</span> <span class="kt">DispatchSourceTimerAdapterFactorySpec</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">self</span><span class="o">.</span><span class="n">timerFactory</span> <span class="o">=</span> <span class="n">timerFactory</span>
    <span class="p">}</span>
    <span class="c1">// 略..</span>

    <span class="kd">func</span> <span class="nf">activate</span><span class="p">(</span><span class="nv">repeatTimeInterval</span><span class="p">:</span> <span class="kt">DispatchTimeInterval</span><span class="p">,</span> <span class="nv">handler</span><span class="p">:</span> <span class="kt">DispatchSource</span><span class="o">.</span><span class="kt">DispatchSourceHandler</span><span class="p">?)</span> <span class="p">{</span>
        <span class="n">onQueue</span> <span class="p">{</span> <span class="p">[</span><span class="k">weak</span> <span class="k">self</span><span class="p">]</span> <span class="k">in</span>
            <span class="k">guard</span> <span class="k">let</span> <span class="nv">self</span> <span class="k">else</span> <span class="p">{</span> <span class="k">return</span> <span class="p">}</span>
            <span class="k">guard</span> <span class="p">[</span><span class="o">.</span><span class="n">idle</span><span class="p">,</span> <span class="o">.</span><span class="n">cancelled</span><span class="p">]</span><span class="o">.</span><span class="nf">contains</span><span class="p">(</span><span class="n">_state</span><span class="p">)</span> <span class="k">else</span> <span class="p">{</span> <span class="k">return</span> <span class="p">}</span>
            <span class="c1">// 使用 Factory MakeTimer</span>
            <span class="k">let</span> <span class="nv">timer</span> <span class="o">=</span> <span class="n">timerFactory</span><span class="o">.</span><span class="nf">makeTimer</span><span class="p">(</span><span class="nv">repeatTimeInterval</span><span class="p">:</span> <span class="n">repeatTimeInterval</span><span class="p">,</span> <span class="nv">handler</span><span class="p">:</span> <span class="n">handler</span><span class="p">)</span>
            <span class="k">self</span><span class="o">.</span><span class="n">timer</span> <span class="o">=</span> <span class="n">timer</span>

            <span class="n">timer</span><span class="o">.</span><span class="nf">activate</span><span class="p">()</span>
            <span class="n">_state</span> <span class="o">=</span> <span class="o">.</span><span class="n">running</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="c1">// 略..</span>
<span class="p">}</span>
</code></pre></div></div>

<p>這樣我們就能對 <code class="language-plaintext highlighter-rouge">TimerAdapter</code> / <code class="language-plaintext highlighter-rouge">DispatchSourceTimerAdapterFactorySpec</code> 在測試環節撰寫 Mock Object 跑單元測試。</p>
<h4 id="延伸--使用-strategy-pattern-封裝-dispatchsourcehandler-工作">延伸 — 使用 <strong><em>Strategy</em></strong> Pattern 封裝 DispatchSourceHandler 工作</h4>

<p>假設我們的 DispatchSourceHandler 希望執行的事能動態改變，可以使用 Strategy Pattern 來封裝工作內容。</p>

<p><strong>TrackingHandlerStrategy:</strong></p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">protocol</span> <span class="kt">TrackingHandlerStrategy</span> <span class="p">{</span>
    <span class="kd">static</span> <span class="k">var</span> <span class="nv">target</span><span class="p">:</span> <span class="kt">String</span> <span class="p">{</span> <span class="k">get</span> <span class="p">}</span>
    <span class="kd">func</span> <span class="nf">execute</span><span class="p">()</span>
<span class="p">}</span>

<span class="c1">// Home Event</span>
<span class="kd">final</span> <span class="kd">class</span> <span class="kt">HomeTrackingHandlerStrategy</span><span class="p">:</span> <span class="kt">TrackingHandlerStrategy</span> <span class="p">{</span>
    <span class="kd">static</span> <span class="k">var</span> <span class="nv">target</span><span class="p">:</span> <span class="kt">String</span> <span class="o">=</span> <span class="s">"home"</span>
    <span class="kd">func</span> <span class="nf">execute</span><span class="p">()</span> <span class="p">{</span>
       <span class="c1">// fetch home event logs..and send</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="c1">// Product Event</span>
<span class="kd">final</span> <span class="kd">class</span> <span class="kt">ProductTrackingHandlerStrategy</span><span class="p">:</span> <span class="kt">TrackingHandlerStrategy</span> <span class="p">{</span>
    <span class="kd">static</span> <span class="k">var</span> <span class="nv">target</span><span class="p">:</span> <span class="kt">String</span> <span class="o">=</span> <span class="s">"product"</span>
    <span class="kd">func</span> <span class="nf">execute</span><span class="p">()</span> <span class="p">{</span>
       <span class="c1">// fetch product event logs..and send</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p><strong>組合使用：</strong></p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">var</span> <span class="nv">sender</span> <span class="o">=</span> <span class="kt">TrackingEventSender</span><span class="p">()</span>
<span class="n">sender</span><span class="o">.</span><span class="nf">register</span><span class="p">(</span><span class="nv">event</span><span class="p">:</span> <span class="kt">HomeTrackingHandlerStrategy</span><span class="p">())</span>
<span class="n">sender</span><span class="o">.</span><span class="nf">register</span><span class="p">(</span><span class="nv">event</span><span class="p">:</span> <span class="kt">ProductTrackingHandlerStrategy</span><span class="p">())</span>
<span class="n">sender</span><span class="o">.</span><span class="nf">startTracking</span><span class="p">()</span>

<span class="c1">// ...</span>

<span class="c1">//</span>

<span class="kd">final</span> <span class="kd">class</span> <span class="kt">TrackingEventSender</span> <span class="p">{</span>

    <span class="kd">private</span> <span class="k">let</span> <span class="nv">timerMachine</span> <span class="o">=</span> <span class="kt">DispatchSourceTimerMachine</span><span class="p">()</span>
    <span class="kd">private</span> <span class="k">var</span> <span class="nv">events</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">:</span> <span class="kt">TrackingHandlerStrategy</span><span class="p">]</span> <span class="o">=</span> <span class="p">[:]</span>

    <span class="c1">// 註冊需要的 Event 策略</span>
    <span class="kd">func</span> <span class="nf">register</span><span class="p">(</span><span class="nv">event</span><span class="p">:</span> <span class="kt">TrackingHandlerStrategy</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">events</span><span class="p">[</span><span class="nf">type</span><span class="p">(</span><span class="nv">of</span><span class="p">:</span> <span class="n">event</span><span class="p">)</span><span class="o">.</span><span class="n">target</span><span class="p">]</span> <span class="o">=</span> <span class="n">event</span>
    <span class="p">}</span>

    <span class="kd">func</span> <span class="n">retrive</span><span class="o">&lt;</span><span class="kt">T</span><span class="p">:</span> <span class="kt">TrackingHandlerStrategy</span><span class="o">&gt;</span><span class="p">(</span><span class="nv">event</span><span class="p">:</span> <span class="kt">T</span><span class="o">.</span><span class="k">Type</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">T</span><span class="p">?</span> <span class="p">{</span>
        <span class="k">return</span> <span class="n">events</span><span class="p">[</span><span class="n">event</span><span class="o">.</span><span class="n">target</span><span class="p">]</span> <span class="k">as?</span> <span class="kt">T</span>
    <span class="p">}</span>

    <span class="c1">// 啟動定期 tracking</span>
    <span class="kd">func</span> <span class="nf">startTracking</span><span class="p">()</span> <span class="p">{</span>
        <span class="n">timerMachine</span><span class="o">.</span><span class="nf">activate</span><span class="p">(</span><span class="nv">repeatTimeInterval</span><span class="p">:</span> <span class="o">.</span><span class="nf">seconds</span><span class="p">(</span><span class="mi">30</span><span class="p">))</span> <span class="p">{</span> <span class="p">[</span><span class="k">weak</span> <span class="k">self</span><span class="p">]</span> <span class="k">in</span>
            <span class="k">self</span><span class="p">?</span><span class="o">.</span><span class="n">events</span><span class="o">.</span><span class="n">values</span><span class="o">.</span><span class="n">forEach</span> <span class="p">{</span> <span class="n">event</span> <span class="k">in</span>
                <span class="n">event</span><span class="o">.</span><span class="nf">execute</span><span class="p">()</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="c1">// 暫停 tracking（例如 App 進背景）</span>
    <span class="kd">func</span> <span class="nf">pauseTracking</span><span class="p">()</span> <span class="p">{</span>
        <span class="n">timerMachine</span><span class="o">.</span><span class="nf">suspend</span><span class="p">()</span>
    <span class="p">}</span>

    <span class="c1">// 恢復 tracking（例如 App 回前景）</span>
    <span class="kd">func</span> <span class="nf">resumeTracking</span><span class="p">()</span> <span class="p">{</span>
        <span class="n">timerMachine</span><span class="o">.</span><span class="nf">resume</span><span class="p">()</span>
    <span class="p">}</span>

    <span class="c1">// 停止 tracking（例如頁面離開）</span>
    <span class="kd">func</span> <span class="nf">stopTracking</span><span class="p">()</span> <span class="p">{</span>
        <span class="n">timerMachine</span><span class="o">.</span><span class="nf">cancel</span><span class="p">()</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<h4 id="鳴謝">鳴謝</h4>

<p>感謝 <a href="https://medium.com/u/e13f6afcf9b9" target="_blank">Ethan Huang</a> 大大 Donate 的 <strong>5 Beers</strong> :</p>

<p><a href="https://www.patreon.com/cw/ethanhuang13/home?vanity=ethanhuang13" target="_blank"><img src="https://c7.patreon.com/https%3A%2F%2Fwww.patreon.com%2F%2Fcard-teaser-image%2Fcreator%2F6512681%3Fc=6796995314395033145/selector/%23creator-teaser%2C.png" alt="" /></a></p>

<blockquote>
  <p><em>的確快半年沒寫什麼了，新工作剛到職，持續找尋靈感中！💪</em></p>
</blockquote>

<blockquote>
  <p>下一篇可能分享 Fastlane Match 憑證管理跟 Self-hosted Runner 的建置過程..或是 Bitbucket Pipeline..或是 AppStoreConnect API…</p>
</blockquote>

<h3 id="延伸閱讀">延伸閱讀</h3>
<ul>
  <li><a href="/posts/pinkoi-engineering/design-patterns-實戰應用-封裝-socket-io-即時通訊架構與七大設計模式解析-78507a8de6a5/"><strong>Design Patterns 的實戰應用紀錄 (封裝 Sockiet.io)</strong></a></li>
  <li><a href="/posts/kkday-tech-blog/wkwebview-設計模式實戰-builder-strategy-與責任鏈模式最佳應用技巧-f4b02ee342a4/">Design Patterns 的實戰應用紀錄 (封裝 WKWebView)</a></li>
  <li><a href="/posts/zrealm-dev/visitor-pattern-在-ios-swift-分享功能應用-設計模式實務解析與最佳架構優化-ba5773a7bfea/">Visitor Pattern in Swift</a></li>
  <li><a href="/posts/zrealm-dev/visitor-pattern-提升-tableview-可擴充性與可維護性設計範例-60473cb47550/">Visitor Pattern in TableView</a></li>
</ul>]]></content>
  </entry><entry>
    <title type="html">日本樂天集運教學｜快速購買Horie鈦杯與Amazon商品寄送台灣全攻略</title>
    <link href="https://zhgchg.li/posts/zrealm-life/%E6%97%A5%E6%9C%AC%E6%A8%82%E5%A4%A9%E9%9B%86%E9%81%8B%E6%95%99%E5%AD%B8-%E5%BF%AB%E9%80%9F%E8%B3%BC%E8%B2%B7horie%E9%88%A6%E6%9D%AF%E8%88%87amazon%E5%95%86%E5%93%81%E5%AF%84%E9%80%81%E5%8F%B0%E7%81%A3%E5%85%A8%E6%94%BB%E7%95%A5-bea62c251f1b/" rel="alternate" type="text/html" title="日本樂天集運教學｜快速購買Horie鈦杯與Amazon商品寄送台灣全攻略" />
    <published>2025-10-26T17:58:35+08:00</published>
    <updated>2025-10-27T08:41:53+08:00</updated>
    <id>https://zhgchg.li/posts/zrealm-life/bea62c251f1b</id><summary type="html">想從日本樂天市場及Amazon購買Horie鈦杯並寄回台灣？詳細註冊、集運設定、運費計算與禁運品說明，實測最快4天收貨，運費省錢技巧一次掌握，輕鬆避開購物與物流風險。</summary><author>
      <name>ZhgChgLi</name>
    </author><category term="ZRealm Life." /><category term="生活" /><category term="開箱" /><category term="樂天" /><category term="鈦杯" /><category term="集運" /><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://zhgchg.li/assets/bea62c251f1b/1*bXj9-XVgjfsVta6cZg8Fpw.webp" /><content type="html" xml:base="https://zhgchg.li/posts/zrealm-life/%E6%97%A5%E6%9C%AC%E6%A8%82%E5%A4%A9%E9%9B%86%E9%81%8B%E6%95%99%E5%AD%B8-%E5%BF%AB%E9%80%9F%E8%B3%BC%E8%B2%B7horie%E9%88%A6%E6%9D%AF%E8%88%87amazon%E5%95%86%E5%93%81%E5%AF%84%E9%80%81%E5%8F%B0%E7%81%A3%E5%85%A8%E6%94%BB%E7%95%A5-bea62c251f1b/"><![CDATA[<h3 id="日本海外購物寄送台灣教學--使用樂天轉運購買樂天拍賣與-amazon-商品-horie-鈦杯-最快-4-天到貨">日本海外購物寄送台灣教學 — 使用樂天轉運購買樂天拍賣與 Amazon 商品 (Horie 鈦杯) 最快 4 天到貨</h3>

<p>想要的東西自己買，用樂天轉運購買日本商品寄回台灣的紀錄與教學，以購買 Horie 鈦杯為例。</p>

<h3 id="horie-鈦杯"><a href="https://a.r10.to/hkPx9m" target="_blank">Horie 鈦杯</a></h3>

<p>上次去日本被燒到，Horie 鈦杯的主要特色：</p>

<p><img src="/assets/bea62c251f1b/1*7m-tH8lB3Sx0yxtdc9Bn_g.webp" alt="官方 Horie 樂天賣場" loading="lazy" decoding="async" width="776" height="970" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NzYiIGhlaWdodD0iOTcwIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><a href="https://a.r10.to/hkPx9m" target="_blank">官方 Horie 樂天賣場</a></p>
<ul>
  <li>獨家的氧化上色花紋技術，色彩斑斕璀璨、顏色多元可選</li>
  <li>獨家的雙隔層保溫結構，保冷不流汗、保溫不燙手</li>
  <li>獨家特殊內杯面設計，喝啤酒時可以自動產生持久的綿密泡泡</li>
  <li>杯口收密，不刮嘴</li>
  <li>純鈦材質，非常輕、抗菌、不殘留味道</li>
</ul>

<p>最吸引我的是前三項獨家技術 <strong>＋它是保溫杯</strong> ，況且我目前沒有任何鈦杯，對保溫杯容易殘留味道的問題也很苦惱，因此很想買一個來用用。</p>
<h4 id="欲購買的商品"><strong>欲購買的商品：</strong></h4>

<blockquote>
  <p><em>品牌：Horie 鈦杯</em></p>
</blockquote>

<blockquote>
  <p><em>型號：窯創り ブースター/400ml</em></p>
</blockquote>

<blockquote>
  <p><em>樣式： G4B</em></p>
</blockquote>

<p><a href="https://a.r10.to/hkPx9m" target="_blank">官方售價</a> <strong>¥19,800 円 (約 NT $4,080 台幣)</strong> ，日本國內免運費。</p>

<p>查了一下在台灣買，不管是電商平台或是個人賣家價格都在 NT $5,500 台幣上下；偶有團購或展覽活動價格可以壓到 NT$4,200 左右，才適合入手。</p>
<h3 id="樂天轉運集運回台灣總花費時間及金額">樂天轉運集運回台灣總花費時間及金額</h3>

<p>結果寫在前面。</p>

<p><img src="/assets/bea62c251f1b/1*n3ahp0WYgvO36vendy3GEQ.webp" alt="" loading="lazy" decoding="async" width="1200" height="569" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjU2OSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<ul>
  <li>因為我要刻字，所以需要 7–10 天製作，另外又買了雜誌多等了雜誌到樂天轉運的時間。</li>
  <li>單純看轉運時間，轉運站收到貨只花 1 天、轉運從打包出貨到收到只花 3 天，可以說是非常快速！</li>
  <li>花費總計含運費：NT $4,744 入手。(細節請看內文，因為買的少所以運費較貴)</li>
</ul>

<h3 id="樂天轉運--rakuten-global-express"><a href="https://secure.globalexpress.rakuten.co.jp/mypage/register/invite?key=a6nn9r3fq6j4sskcsOrXE%2Bgdygp0zER%2FMe83%2FUDPw6k7LFkMFZBfgN0hX48E%3D" target="_blank">樂天轉運 — Rakuten Global Express</a></h3>

<p>想說既然都要買日本樂天市場的東西，就直接試用樂天的轉運/集運服務；當然樂天轉運也不只侷限於購買樂天的東西，其他電商平台例如：日本亞馬遜(Amazon JP)、日本 Yahoo 購物 / Yahoo 拍賣、Mercari、Biccamera…等等都能寄送。</p>
<ul>
  <li>可以累積樂天點數！及使用點數抵付！</li>
</ul>

<h4 id="使用樂天轉運購買樂天商品的特殊保障">使用樂天轉運購買樂天商品的特殊保障</h4>

<p>除了提供轉運/集運服務之外， <strong>使用樂天</strong> 轉 <strong>運購買樂天市場的商品</strong> 還額外提供以下保障：</p>
<ul>
  <li>賠償服務：已付款但商品未送達</li>
  <li>賠償服務：商品損壞 (請取得物流公司開立之損壞報告)</li>
  <li>賠償服務：收到與商品頁商品說明明顯相異的商品或瑕疵品</li>
  <li>賠償服務：收到的品牌商品是仿冒品，即侵害商標權之物品（在安心購物保障範圍内的 1,000+ 個品牌）</li>
  <li>賠償金額：上限為 30 萬日圓</li>
  <li>無需手續費、年費</li>
</ul>

<p>細節請 <a href="https://event.rakuten.co.jp/anshin/anshinshopping/zh-tw/top/" target="_blank">參考官方說明</a> 。</p>
<h4 id="購買非樂天的保障">購買非樂天的保障</h4>

<p>如購買的商品非樂天市場，提供基礎的轉運寄送保障：</p>
<ul>
  <li>賠償服務：已付款但商品未送達</li>
  <li>賠償服務：商品損壞 (請取得物流公司開立之損壞報告)</li>
</ul>

<p>細節請 <a href="https://globalexpress.rakuten.co.jp/help/insurance?l-id=help_tw_insurance" target="_blank">參考官方說明</a> 。</p>
<h4 id="運費計算報價">運費計算、報價</h4>

<p>可以使用 <a href="https://globalexpress.rakuten.co.jp/estimate/?l-id=top_estimate" target="_blank">官方工具</a> 試算轉運運費價格。</p>

<p><img src="/assets/bea62c251f1b/1*4mfK-p6n2FUgWpDCq8xisQ.webp" alt="https://globalexpress.rakuten.co.jp/estimate/?l-id=top_estimate" loading="lazy" decoding="async" width="949" height="596" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NDkiIGhlaWdodD0iNTk2Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><a href="https://globalexpress.rakuten.co.jp/estimate/?l-id=top_estimate" target="_blank">https://globalexpress.rakuten.co.jp/estimate/?l-id=top_estimate</a></p>

<p><strong>配送方式與台灣運費表</strong></p>

<p><img src="/assets/bea62c251f1b/1*Bi9oS0C32l8Mhvtwfdg-zg.webp" alt="https://globalexpress.rakuten.co.jp/help/delivery" loading="lazy" decoding="async" width="947" height="670" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NDciIGhlaWdodD0iNjcwIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><a href="https://globalexpress.rakuten.co.jp/help/delivery" target="_blank">https://globalexpress.rakuten.co.jp/help/delivery</a></p>

<p><img src="/assets/bea62c251f1b/1*k4M0QBRRDbPQZ7BSyB6c4A.webp" alt="https://globalexpress.rakuten.co.jp/help/country/fee?country=TW" loading="lazy" decoding="async" width="980" height="943" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5ODAiIGhlaWdodD0iOTQzIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><a href="https://globalexpress.rakuten.co.jp/help/country/fee?country=TW" target="_blank">https://globalexpress.rakuten.co.jp/help/country/fee?country=TW</a></p>

<blockquote>
  <p><em>請注意這裡的大小跟重量是最終集貨後的數字。</em></p>
</blockquote>

<h3 id="日本樂天帳號註冊">日本樂天帳號註冊</h3>

<p>前往 <a href="https://login.account.rakuten.com/sso/register?client_id=rakuten_ichiba_top_web&amp;service_id=s245&amp;response_type=code&amp;scope=openid&amp;redirect_uri=https%3A%2F%2Fwww.rakuten.co.jp%2F#/registration/1" target="_blank"><strong>註冊頁面</strong></a> ：</p>

<p><img src="/assets/bea62c251f1b/1*b3acAhAfqIrsIe7-hHuQLA.webp" alt="" loading="lazy" decoding="async" width="1307" height="1467" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMzA3IiBoZWlnaHQ9IjE0NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>輸入電子郵件信箱、密碼、英文姓名，送出後即可完成註冊。</p>

<blockquote>
  <p><em>這邊有發現一個隨機的網站問題， <strong>如果在樂天網站上一直出現伺服器錯誤，可以把語系改成日文</strong> ，就能正常使用了。(疑似是多語系錯誤)</em></p>
</blockquote>

<h4 id="先前往樂天轉運完成實名認證">先前往樂天轉運完成實名認證</h4>

<p><img src="/assets/bea62c251f1b/1*roub7uHzS2bUtIP5qIg8pA.webp" alt="" loading="lazy" decoding="async" width="1330" height="920" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMzMwIiBoZWlnaHQ9IjkyMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>註冊完樂天到 <a href="https://secure.globalexpress.rakuten.co.jp/mypage/register/invite?key=aanar03e62e0ckog4nWPIFcV7FTuy6JGEeCL1HwxrPgcSjKRlGHs1GY6PkH0%3D" target="_blank">樂天轉運</a> 預設就是已登入狀態。</p>
<ol>
  <li>先點擊語言換成繁體中文方便操作</li>
  <li>點擊我的頁面開通轉運</li>
</ol>

<p><img src="/assets/bea62c251f1b/1*LtS88oCvn5XKCHImq_8Twg.webp" alt="" loading="lazy" decoding="async" width="537" height="715" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1MzciIGhlaWdodD0iNzE1Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><img src="/assets/bea62c251f1b/1*EBb5vLI574judf0zb4up9Q.webp" alt="" loading="lazy" decoding="async" width="522" height="701" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1MjIiIGhlaWdodD0iNzAxIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>前往信箱收取驗證碼完成信箱驗證。</p>

<p><img src="/assets/bea62c251f1b/1*caABYLKqpF995bAS9QULpQ.webp" alt="" loading="lazy" decoding="async" width="530" height="692" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1MzAiIGhlaWdodD0iNjkyIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>下一步，補填姓名資訊。</p>
<ul>
  <li>姓(漢字)、名(漢字)：填寫中文姓名</li>
  <li>姓(片假名)、名(片假名)：填寫姓名片假名，可以 <a href="https://dokochina.com/katakana.php" target="_blank">用這網站翻譯</a> ，或用暱稱，例如我是：リー, ハリー。</li>
</ul>

<h4 id="開通樂天轉運地址完成實名驗證">開通樂天轉運地址、完成實名驗證</h4>

<p><img src="/assets/bea62c251f1b/1*XH92Vc8EmKSvvF3RH7hYJg.webp" alt="" loading="lazy" decoding="async" width="1200" height="716" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjcxNiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>第一步同意使用者條款， <strong>部分商品如易燃物、噴霧、生鮮食品禁止寄送</strong> 。</p>

<p><img src="/assets/bea62c251f1b/1*WOxQ8TpaXdOS_BJqyWeZYg.webp" alt="" loading="lazy" decoding="async" width="892" height="1109" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4OTIiIGhlaWdodD0iMTEwOSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>第二步輸入本人資訊，務必輸入真實護照英文姓名、與生日。</p>
<h4 id="取得專屬樂天轉運地址">取得專屬樂天轉運地址</h4>

<p><img src="/assets/bea62c251f1b/1*u8ObmbBY2rsVw93cS8ADDA.webp" alt="" loading="lazy" decoding="async" width="880" height="1180" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4ODAiIGhlaWdodD0iMTE4MCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<ul>
  <li>轉運倉地址務必要輸入到最後的 <code class="language-plaintext highlighter-rouge">白石町6–1 RGXセンター XXXXXXX</code> 英文數字編號，那才是你的專屬識別地址。</li>
  <li>可點擊「立即新增」直接把地址新增到樂天帳戶</li>
</ul>

<blockquote>
  <p><em>有了這個地址跟電話號碼就能在日本各大電商網站進行購物跟寄送囉！不過為了確保轉運能安全寄回到你手上， <strong>請先「繼續完成本人認證手續」再去購物網站購買、寄送商品。</strong></em></p>
</blockquote>

<h4 id="繼續完成本人認證手續">繼續完成本人認證手續</h4>

<p><img src="/assets/bea62c251f1b/1*Fv2V7TPlqTBQHTeo3bPuWg.webp" alt="" loading="lazy" decoding="async" width="860" height="941" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4NjAiIGhlaWdodD0iOTQxIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<ul>
  <li>準備好身分證正本跟手機後，點透過臉部認證進行認證，直接線上 24 小時自助完成認證手續。</li>
</ul>

<blockquote>
  <p><em>但我的身分證照片可能跟本人已經差太多，線上認證失敗自動轉人工，隔天才收到官方通知人工審核完成。</em></p>
</blockquote>

<h4 id="我的頁面">我的頁面</h4>

<p>點擊上方「我的頁面」或「我的頁面頂端」前往我的頁面主頁：</p>

<p><img src="/assets/bea62c251f1b/1*MhXfS0YfDj4TVZC_h1uBmA.webp" alt="" loading="lazy" decoding="async" width="950" height="584" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NTAiIGhlaWdodD0iNTg0Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><img src="/assets/bea62c251f1b/1*_qPCZpRDpfbrNfytEsjBGA.webp" alt="" loading="lazy" decoding="async" width="1322" height="596" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMzIyIiBoZWlnaHQ9IjU5NiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>如果認證手續未完成會有以下警告提示：</p>

<p><img src="/assets/bea62c251f1b/1*qkgvVofPd8uI6CLH_0rnhw.webp" alt="" loading="lazy" decoding="async" width="694" height="200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2OTQiIGhlaWdodD0iMjAwIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<blockquote>
  <p><strong><em>請務必先完成認證再進行購物。</em></strong></p>
</blockquote>

<h4 id="您專屬的收件地址與資訊">您專屬的收件地址與資訊</h4>

<p>我的頁面右下方會顯示專屬您資訊。</p>

<p><img src="/assets/bea62c251f1b/1*_qsR1A94i83sEDAVpGxgbg.webp" alt="" loading="lazy" decoding="async" width="1313" height="1083" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMzEzIiBoZWlnaHQ9IjEwODMiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<h3 id="寄送台灣的禁運商品">寄送台灣的禁運商品</h3>

<p>購買任何物品想寄回台灣時請確認是否為禁止商品。</p>

<p><img src="/assets/bea62c251f1b/1*c7qamfTN89ipGwM9LZUblQ.webp" alt="https://globalexpress.rakuten.co.jp/help/country/detail?country=TW" loading="lazy" decoding="async" width="959" height="873" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NTkiIGhlaWdodD0iODczIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><a href="https://globalexpress.rakuten.co.jp/help/country/detail?country=TW" target="_blank">https://globalexpress.rakuten.co.jp/help/country/detail?country=TW</a></p>

<p>不同寄送方式有不同限制， <a href="https://globalexpress.rakuten.co.jp/help/country/detail?country=TW" target="_blank">務必詳閱</a> 。</p>

<blockquote>
  <p><em>如果要買酒買菸之類的務必先上網查好別人的教學，我看大略是寄回台灣後要拿通知信去補交稅金，才能放行。</em></p>
</blockquote>

<h3 id="樂天市場購物--horie-鈦杯-下單教學"><a href="https://a.r10.to/hkPx9m" target="_blank">樂天市場購物 — Horie 鈦杯</a> 下單教學</h3>

<blockquote>
  <p><em>1055℃の窯から生まれた高保冷チタン2重タンブラー 。アレルギーフリーで清潔、錆びずに軽く丈夫で長持ちします。大切な方への贈り物や自分へのご褒美に最適です。【無料ラッピング対応実施】HORIE 公式ショップ/ 窯創り 名入れ 純チタン製二重タンブラー horie チタンタンブラー 母の日 父の日 敬老の日 ギフト ビールグラス ホリエ 燕三条 誕生日プレゼント お餞別 退職祝 内祝 結婚祝 SDGs クリスマス</em></p>
</blockquote>

<blockquote>
  <p><em>13,200〜19,800円送料無料</em></p>
</blockquote>

<h4 id="前往商品頁-"><a href="https://a.r10.to/hkPx9m" target="_blank">前往商品頁</a> ：</h4>

<p><img src="/assets/bea62c251f1b/1*8aYksm9WZ5UUkmSKSI_5EA.webp" alt="商品頁" loading="lazy" decoding="async" width="938" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MzgiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><a href="https://a.r10.to/hkPx9m" target="_blank">商品頁</a></p>

<p>這是窯創系列的商品頁，要選擇其他系列 <a href="https://a.r10.to/hkG3CK" target="_blank">可以點 HORIE 商城首頁</a> 查看。</p>

<p>拉到最下方商品規格選擇 (商品詳細を選択)。</p>
<ul>
  <li><strong>形状/サイズ</strong> ：選擇你要的款式 (請參考商品頁的圖片，有廣口、窄口、容量大小也不同)</li>
  <li><strong>カラー</strong> ：選擇你要的樣式(請參考商品頁的圖片)</li>
</ul>

<p><strong>我是買：窯創り ブースター/400ml &amp; G4B：</strong></p>

<p><img src="/assets/bea62c251f1b/1*rwCNpways8AGJZ3DglI0qw.webp" alt="" loading="lazy" decoding="async" width="727" height="1016" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3MjciIGhlaWdodD0iMTAxNiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><strong>名入れオプション</strong> ：有沒有要刻字(網路商城限定， <strong>免費刻字</strong> )，需要 7–10 個工作天：</p>

<p><img src="/assets/bea62c251f1b/1*8Zfq6b9q2X2jORa51cjm1w.webp" alt="" loading="lazy" decoding="async" width="657" height="825" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2NTciIGhlaWdodD0iODI1Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><strong>ラッピングの有無</strong> ：包裝紙樣式，A or B or 無樣式。</p>

<p><img src="/assets/bea62c251f1b/1*TeJNyUkr6nsAapTC9H504A.webp" alt="" loading="lazy" decoding="async" width="640" height="623" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2NDAiIGhlaWdodD0iNjIzIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><strong>桐箱デザインの選択</strong> ：包裝盒樣式，WakuWaku驚喜版(左，限單入)或是一般版(右)</p>

<p><strong>2個セット用桐箱 (※2個以上ご注文・必要な場合のみチェックを入れる)：</strong> 購買兩個杯子以上才需勾選， <strong>勾選放同一個盒子</strong> (如下圖右下)。
只買一個可跳過。</p>

<p><strong>2個セットの場合の組み合わせ (記入例:プレミアムとライト) (※2セット以上ご注文の場合は必ず明記)：</strong> 購買兩組以上該怎麼組合盒子的描述，嫌麻煩的話直接分開加入購物車比較快。
只買一個可跳過。</p>

<p><img src="/assets/bea62c251f1b/1*49fEtjiRRBvNyEGxdFeE3A.webp" alt="" loading="lazy" decoding="async" width="645" height="621" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2NDUiIGhlaWdodD0iNjIxIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><strong>名入れタイプ※単品名入れ商品を2個以上ご注文の場合は、お手数ですが1個づつ買い物かごに入れてください：</strong> 名入れ不要 (不要刻字)，可跳過以下欄位，要刻字就照商品頁圖上資訊選擇樣式跟填入每一行的文字：</p>
<ul>
  <li><strong>名入れ1行目</strong></li>
  <li><strong>名入れ2行目</strong></li>
  <li><strong>名入れ3行目</strong></li>
  <li><strong>名入れ4行目</strong></li>
</ul>

<blockquote>
  <p><strong><em>請注意如果要刻字要分開一筆一筆加入購物車，不要寫在刻字內容。</em></strong></p>
</blockquote>

<blockquote>
  <p><strong><em>我是選擇 B 樣式刻字(文末有圖)。</em></strong></p>
</blockquote>

<p><img src="/assets/bea62c251f1b/1*i50V97FHogvQ4PVCFTLX7Q.webp" alt="" loading="lazy" decoding="async" width="625" height="630" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2MjUiIGhlaWdodD0iNjMwIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><strong>送禮包裝賀簽樣式、內容：</strong></p>
<ul>
  <li>のしの内容：賀簽類型，例如御祝、祝壽..等等，不用就選「のし不要」</li>
  <li>のしの種類：賀簽樣式，如下圖，不用就選「のし不要」</li>
  <li>のしの水引きの種類：賀簽綁線樣式，如下圖，不用就選「のし不要」</li>
</ul>

<p><img src="/assets/bea62c251f1b/1*uLCkqmso0Ad9MTynZL0T-w.webp" alt="" loading="lazy" decoding="async" width="626" height="854" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2MjYiIGhlaWdodD0iODU0Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><strong>のしに記入するお名前(贈り主)※連名の場合は間に・を入力。内祝い等でふりがなをご希望の際はふりがなもご入力ください。20文字以上の場合は備考欄にお願いします：</strong> 請填寫將印在賀禮外包裝（熨斗）上的贈送者姓名、若為多人聯名，請在姓名間加入「・」、不用則留空。</p>
<h4 id="加入購物車完成結帳">加入購物車、完成結帳</h4>

<p><img src="/assets/bea62c251f1b/1*UCbosHBXeND4C0Q7hoX38A.webp" alt="" loading="lazy" decoding="async" width="627" height="711" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2MjciIGhlaWdodD0iNzExIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>都填好規格內容後點「かごに追加」加入購物車，お届け先 收件人資訊選我們樂天轉運的「神奈川縣」，下方也會顯示預計配送到樂天轉運的時間、免運費；都加好後點上方購物車結帳：</p>

<p><img src="/assets/bea62c251f1b/1*XP6gv2xNgplYY_IvQyKNCQ.webp" alt="" loading="lazy" decoding="async" width="1048" height="615" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDQ4IiBoZWlnaHQ9IjYxNSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>確認商品規格無誤之後，點右方「購入手続き」</p>

<p><img src="/assets/bea62c251f1b/1*lsPBGTQGkyJ_RAG0VjayAw.webp" alt="" loading="lazy" decoding="async" width="1049" height="989" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDQ5IiBoZWlnaHQ9Ijk4OSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<ul>
  <li><strong>お届け先：</strong> 寄送資訊，可以直接選擇剛樂天轉運新增的轉運地址，或自行新增轉運地址(轉運地址請務必輸入到最後編號部分)</li>
  <li><strong>支払い方法：</strong> 支付方式，我直接刷卡(算是日本購物，可以看有哪個銀行有優惠)。</li>
</ul>

<p>都沒問題後點「注文を確定する」完成下單。</p>
<h4 id="horie-刻字確認">HORIE 刻字確認</h4>

<p><img src="/assets/bea62c251f1b/1*Q8t3tiej00RrQqsEBVsOLg.webp" alt="" loading="lazy" decoding="async" width="921" height="1190" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MjEiIGhlaWdodD0iMTE5MCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>如果有選擇要刻字，HORIE 會再收到訂單後傳送確認結果給你，沒問題可以回覆「ご送付いただいたレイアウトを確認しました。問題ございませんので製作を進めてください。」讓他們開始製作。</p>

<p>他也會傳訊息告知出貨時間。</p>
<h4 id="商家出貨樂天轉運到貨通知">商家出貨、樂天轉運到貨通知</h4>

<p><img src="/assets/bea62c251f1b/1*DGN-R9yxdnrjEMlSR5W0Bw.webp" alt="" loading="lazy" decoding="async" width="850" height="761" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4NTAiIGhlaWdodD0iNzYxIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><img src="/assets/bea62c251f1b/1*j5pKRcNpLfuiToRzoNG-bg.webp" alt="" loading="lazy" decoding="async" width="894" height="642" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4OTQiIGhlaWdodD0iNjQyIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>收到 HORIE 出貨通知後約 1–3 天就送達樂天轉運倉庫了。</p>
<h3 id="從日本亞馬遜-amazon-jp-寄送到樂天轉運">從日本亞馬遜 Amazon JP 寄送到樂天轉運</h3>

<p>這次除了買 HORIE 鈦杯之外，還從 日本亞馬遜 Amazon JP 買了一個雜誌，打算一起轉運寄回台灣。</p>

<p><img src="/assets/bea62c251f1b/1*70bmfM40fgui1EptFuLylA.webp" alt="" loading="lazy" decoding="async" width="1400" height="707" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjcwNyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>在購物網站上同樣的操作只要記得照樂天轉運的你的收件資訊填寫寄送地址、聯絡電話(填轉運的)。</p>
<h4 id="非樂天市場購物-貨件到貨事前登記">(非樂天市場購物) 貨件到貨事前登記</h4>

<p>非樂天本家買的商品寄送到轉運建議在下單後登記一下。</p>

<p><img src="/assets/bea62c251f1b/1*hbc4AK4PyGSScxgg8Q9OWg.webp" alt="" loading="lazy" decoding="async" width="1217" height="716" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjE3IiBoZWlnaHQ9IjcxNiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>點擊「貨物一覽表」 → 「登錄/確認」</p>

<p><img src="/assets/bea62c251f1b/1*IYs0xH6f-y55OFHDGmWpHQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="574" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjU3NCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>輸入其他平台購買的店家、商品資訊跟金額。</p>
<h4 id="日本亞馬遜-amazon-jp-購買商品-樂天轉運到貨通知">日本亞馬遜 Amazon JP 購買商品-樂天轉運到貨通知</h4>

<p><img src="/assets/bea62c251f1b/1*eWxfcNY_Wsr7VT9-5ZuFtQ.webp" alt="" loading="lazy" decoding="async" width="610" height="437" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2MTAiIGhlaWdodD0iNDM3Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>都完成後等商家出貨，寄送到達樂天轉運也會收到通知。</p>
<h3 id="樂天轉運寄回台灣">樂天轉運寄回台灣</h3>

<p><a href="https://globalexpress.rakuten.co.jp/faq/detail?id=99" target="_blank">樂天轉運最長可以免費保管貨物 30 天</a> ，東西都寄到集合好之後就可以請求包裝發貨囉。</p>

<p><img src="/assets/bea62c251f1b/1*m3TCoxNu_7ltuthkRAXFBg.webp" alt="" loading="lazy" decoding="async" width="1200" height="663" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjY2MyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>已送達商品：總共三樣。</p>

<p>點擊「貨物一覽表」：</p>

<p><img src="/assets/bea62c251f1b/1*4YRX_1LsNWvrExJAeoQ0nQ.webp" alt="" loading="lazy" decoding="async" width="795" height="831" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3OTUiIGhlaWdodD0iODMxIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>點擊「提出包裝委託」</p>

<p><img src="/assets/bea62c251f1b/1*gUgWUivN-7jH9wNcFn-Y6g.webp" alt="" loading="lazy" decoding="async" width="1400" height="637" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjYzNyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>確認合併包裝(寄送)的貨品內容，無誤後點「確認發貨之包裹」。</p>

<p><img src="/assets/bea62c251f1b/1*PVcM448g7pTwBjOeKh3n0Q.webp" alt="" loading="lazy" decoding="async" width="1400" height="561" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjU2MSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>點擊「選擇其他付費包裝服務」。</p>

<p><img src="/assets/bea62c251f1b/1*5tmccDXzlPpxIvo1CMNIow.webp" alt="" loading="lazy" decoding="async" width="1400" height="603" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjYwMyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>可以選擇包裝服務：(可點 i 有圖文說明)</p>
<ul>
  <li>去除包裝盒、鞋盒、廣告單、目錄、出貨單..等等：讓重量體積減少，省運費。</li>
  <li>包裹防水處理</li>
  <li>使用國際配送專用加固箱：我有勾，實際就是再用個大紙箱+防撞紙包裝你的東西。</li>
</ul>

<h4 id="包裝委託-選擇寄送地點">包裝委託 (選擇寄送地點)</h4>

<p><img src="/assets/bea62c251f1b/1*uJDDeUEF1kAT3thtzpzwDw.webp" alt="" loading="lazy" decoding="async" width="1400" height="333" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjMzMyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/bea62c251f1b/1*yrnzSlMcUafXAIhIaJy4Hw.webp" alt="" loading="lazy" decoding="async" width="743" height="797" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NDMiIGhlaWdodD0iNzk3Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>輸入寄送地址：</p>
<ul>
  <li>護照英文姓名、 <a href="https://www.post.gov.tw/post/internet/SearchZone/index.jsp?ID=130112" target="_blank">英文地址</a> 、 <strong>你的手機號碼(去掉開頭0改成886)</strong></li>
</ul>

<p><img src="/assets/bea62c251f1b/1*3o3wRZOd4t8lldmdU-NObw.webp" alt="" loading="lazy" decoding="async" width="1200" height="676" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjY3NiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>送出後狀態會變成：包裝作業中，等作業完成再付款跟出貨。</p>

<p><img src="/assets/bea62c251f1b/1*5ogwtSaFBNCO3r2gJIA48w.webp" alt="" loading="lazy" decoding="async" width="1200" height="537" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjUzNyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>大約隔天早上就收到包裝完成通知了。</p>
<h4 id="付轉運運費寄送請求">付轉運運費、寄送請求</h4>

<p><img src="/assets/bea62c251f1b/1*lA5p3upIWTdp6FTDrOdtqA.webp" alt="" loading="lazy" decoding="async" width="1160" height="725" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMTYwIiBoZWlnaHQ9IjcyNSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>狀態：待付款。</p>

<p><img src="/assets/bea62c251f1b/1*psFozqxO72KXbbEKY0k37g.webp" alt="" loading="lazy" decoding="async" width="1098" height="887" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDk4IiBoZWlnaHQ9Ijg4NyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>點「貨物一覽」 → 「待付款」 貨物 → 「前往付款」。</p>

<p><img src="/assets/bea62c251f1b/1*XC-tScyTBFqb-XGTedttWA.webp" alt="" loading="lazy" decoding="async" width="1200" height="530" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjUzMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<blockquote>
  <p><strong><em>最終大小、重量：2,330 g, 38 x 30 x 21 cm</em></strong></p>
</blockquote>

<p>確認貨物內容跟收件人資訊，無誤後點「選擇運送方法」。</p>

<p><img src="/assets/bea62c251f1b/1*q9ImvJKflcvx1uHFSxS4pw.webp" alt="" loading="lazy" decoding="async" width="1200" height="577" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjU3NyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>可以選擇：</p>
<ul>
  <li>DHL 快遞 (超級特快空運)：1–3 天 9,100 日圓
據朋友提供的意見，如果用 DHL 記得 <a href="https://roo.cash/blog/dhl-introduction/#DHL_%E5%85%88%E7%A8%85%E5%BE%8C%E6%94%BE%E6%98%AF%E4%BB%80%E9%BA%BC%EF%BC%9F" target="_blank">先申請先稅後放</a> 以免被海關抽檢後補關稅時還要被多收處理費。</li>
  <li><strong>EMS 特快空運：3–8 天 6,550 日圓 (我選這個，實際2天就收到，很快)</strong></li>
  <li>ECMS 經濟空運：6–11 天 6,280 日圓</li>
  <li>船運：20–40 天, 5,620 日圓</li>
</ul>

<blockquote>
  <p><em>價格跟 <a href="https://globalexpress.rakuten.co.jp/estimate/" target="_blank">費用報價工具</a> 報的差不多。</em></p>
</blockquote>

<p>下一步，輸入信用卡 or Paypal 完成付款：</p>

<p><img src="/assets/bea62c251f1b/1*uPqI-nncfu_uToP6427_Jw.webp" alt="" loading="lazy" decoding="async" width="1200" height="561" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjU2MSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/bea62c251f1b/1*ydX1HyUTXvumBk__QH4UJw.webp" alt="" loading="lazy" decoding="async" width="1200" height="569" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjU2OSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>運費也能獲得樂天積分(1%)！</p>
<h4 id="出貨委託完成">出貨委託完成</h4>

<p><img src="/assets/bea62c251f1b/1*2jZefbTEBHYcnc8szKlx6Q.webp" alt="" loading="lazy" decoding="async" width="1208" height="492" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjA4IiBoZWlnaHQ9IjQ5MiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/bea62c251f1b/1*_f1zLXqHYGcWXdgzxCfL_g.webp" alt="" loading="lazy" decoding="async" width="1161" height="777" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMTYxIiBoZWlnaHQ9Ijc3NyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>狀態會到：出貨作業中。</p>
<h4 id="完成註冊-ezway">完成註冊 EZWay</h4>

<p>如果你沒有從海外買東西回來的經驗， <a href="https://www.rakuten.com.tw/magazine/life/2022/012802/#D" target="_blank">務必要完成 EZWay 實名驗證，下載 EZWay App、完成註冊、完成實名認證即可</a> 。</p>
<h4 id="樂天轉運出貨">樂天轉運出貨</h4>

<p><img src="/assets/bea62c251f1b/1*ZNzn3Zpuk_HN0kbmo_zEzg.webp" alt="" loading="lazy" decoding="async" width="849" height="698" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4NDkiIGhlaWdodD0iNjk4Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>樂天完成出貨後也會收到通知，有追蹤碼可以確認貨物狀態。</p>

<blockquote>
  <p><em>剛出貨點可能會找不到，過陣子再查就會有了。</em></p>
</blockquote>

<p><img src="/assets/bea62c251f1b/1*U0L23fpYbBKTeoPtZcLsBA.webp" alt="" loading="lazy" decoding="async" width="904" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDQiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<h4 id="等待收貨">等待收貨！</h4>

<p>到此從日本樂天網站註冊、樂天轉運使用、下單購物、出貨、寄回台灣、EZWay，都完成了，就等貨物到手！</p>
<h3 id="樂天轉運-horie-鈦杯-開箱">樂天轉運、 <a href="https://a.r10.to/hkPx9m" target="_blank">Horie 鈦杯</a> 開箱</h3>

<p>10/14 請求樂天轉運出貨，10/16 就收到了！</p>

<p><img src="/assets/bea62c251f1b/1*LnzDj0-qp5JrhXe40RiewQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1088" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwODgiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>兩個杯子＋一本雜之實際箱子並不大。</p>

<p><img src="/assets/bea62c251f1b/1*x7NZZf3oWLDLAqZzktyYoA.webp" alt="" loading="lazy" decoding="async" width="1200" height="1052" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjEwNTIiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>貼滿日系注意貼紙。</p>

<p><img src="/assets/bea62c251f1b/1*Rub-oAEfRnZjpn4eIAhPHg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1157" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjExNTciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>國際配送專用加固箱就是多這個紙箱＋周圍跟底部防撞紙包裝。</p>

<p><img src="/assets/bea62c251f1b/1*sR3X_vfh6_7VH8x7jTA4GQ.webp" alt="" loading="lazy" decoding="async" width="913" height="1102" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MTMiIGhlaWdodD0iMTEwMiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/bea62c251f1b/1*a2ZQxQbXhyFm790bAqnZ4Q.webp" alt="" loading="lazy" decoding="async" width="931" height="1184" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MzEiIGhlaWdodD0iMTE4NCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>上方是 Amazon 買的雜誌(郵寄的紙包裝已破，不過內容物沒事)，下方才是 Horie 鈦杯。</p>

<p><img src="/assets/bea62c251f1b/1*jDdIWaMN0U_l0KrULJjk7g.webp" alt="" loading="lazy" decoding="async" width="1200" height="925" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkyNSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>Horie 鈦杯寄送的紙箱原封不動放在樂天轉運的紙箱中。</p>

<blockquote>
  <p><em>感覺可以在轉運包裝時勾選去除原本的包裝減少體積與重量，節省運費。</em></p>
</blockquote>

<p><img src="/assets/bea62c251f1b/1*ZVCaomYElKOQktBAGerrkg.webp" alt="" loading="lazy" decoding="async" width="1200" height="817" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjgxNyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>內容物：2 個鈦杯。</p>
<h3 id="horie-鈦杯-1"><a href="https://a.r10.to/hkPx9m" target="_blank">Horie 鈦杯</a></h3>

<p><img src="/assets/bea62c251f1b/1*bXj9-XVgjfsVta6cZg8Fpw.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/bea62c251f1b/1*z2CIeS7Y4Z65SV2WXSv-1Q.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/bea62c251f1b/1*EZAkrzE5WkIEF0YMSoT3CA.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>整體色彩跟紋路及漸層非常鮮艷魔幻，美的很不真實。</p>

<p><img src="/assets/bea62c251f1b/1*F_L3JKPr1kd-Ljs9c2TA2Q.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>馬上開一瓶生啤酒加冰塊測試，保冰效果確實好、啤酒也有綿密的氣泡，很好喝、 <strong>而且真的不會流汗</strong> 。</p>
<h3 id="最終花費計算">最終花費計算</h3>
<ul>
  <li><a href="https://a.r10.to/hkPx9m" target="_blank">Horie 鈦杯</a> 窯創り ブースター/400ml ¥19,800 円 — NT $4,079</li>
  <li>運費分擔/人：NT$665</li>
  <li>關稅：未被收取</li>
</ul>

<p>總計：NT $4,744 入手。</p>

<blockquote>
  <p><strong><em>因為鈦杯非常輕感覺可以多找幾個人一起買分擔運費，應該可以把價格壓到 NT4,200 左右，我只買兩個因此運費有點太貴。</em></strong></p>
</blockquote>

<blockquote>
  <p>當然最划算還是親自去日本百貨公司買＋退稅！價格猜測在 NT$ 3,600 左右。</p>
</blockquote>]]></content>
  </entry><entry>
    <title type="html">東京自由行攻略｜川越小江戶一日遊與熱海海上花火大會全紀錄</title>
    <link href="https://zhgchg.li/posts/z-%E5%BA%A6%E6%97%85%E8%A1%8C%E9%81%8A%E8%A8%98/%E6%9D%B1%E4%BA%AC%E8%87%AA%E7%94%B1%E8%A1%8C%E6%94%BB%E7%95%A5-%E5%B7%9D%E8%B6%8A%E5%B0%8F%E6%B1%9F%E6%88%B6%E4%B8%80%E6%97%A5%E9%81%8A%E8%88%87%E7%86%B1%E6%B5%B7%E6%B5%B7%E4%B8%8A%E8%8A%B1%E7%81%AB%E5%A4%A7%E6%9C%83%E5%85%A8%E7%B4%80%E9%8C%84-958599363857/" rel="alternate" type="text/html" title="東京自由行攻略｜川越小江戶一日遊與熱海海上花火大會全紀錄" />
    <published>2025-10-10T19:01:28+08:00</published>
    <updated>2025-10-16T23:55:33+08:00</updated>
    <id>https://zhgchg.li/posts/z-%E5%BA%A6%E6%97%85%E8%A1%8C%E9%81%8A%E8%A8%98/958599363857</id><summary type="html">規劃東京5天4夜自由行，深入川越小江戶文化與熱海煙火大會，詳細交通票券購買、必訪景點與美食推薦，助你避開人潮、掌握行程節奏，享受無憂旅遊體驗。</summary><author>
      <name>ZhgChgLi</name>
    </author><category term="Z 度旅行遊記" /><category term="生活" /><category term="travel" /><category term="japan" /><category term="tokyo" /><category term="travel-writing" /><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://zhgchg.li/assets/958599363857/1*E-ZA-OYCi5vsTBGlsxyS0g.webp" /><content type="html" xml:base="https://zhgchg.li/posts/z-%E5%BA%A6%E6%97%85%E8%A1%8C%E9%81%8A%E8%A8%98/%E6%9D%B1%E4%BA%AC%E8%87%AA%E7%94%B1%E8%A1%8C%E6%94%BB%E7%95%A5-%E5%B7%9D%E8%B6%8A%E5%B0%8F%E6%B1%9F%E6%88%B6%E4%B8%80%E6%97%A5%E9%81%8A%E8%88%87%E7%86%B1%E6%B5%B7%E6%B5%B7%E4%B8%8A%E8%8A%B1%E7%81%AB%E5%A4%A7%E6%9C%83%E5%85%A8%E7%B4%80%E9%8C%84-958599363857/"><![CDATA[<h3 id="遊記-2025-東京地區--川越小江戶與熱海海上花火大會-5-日自由行">[遊記] 2025 東京地區 — 川越小江戶與熱海海上花火大會 5 日自由行</h3>

<p>東京市區+近郊 5 天 4 夜自由行，熱海花火大會與川越一日遊交通、體驗全攻略</p>

<p><img src="/assets/958599363857/1*E-ZA-OYCi5vsTBGlsxyS0g.webp" alt="夏日的熱海花火" loading="lazy" decoding="async" width="1400" height="933" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjkzMyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>夏日的熱海花火</p>

<p>趁著新工作剛上工先把電充飽， <a href="/posts/z-度旅行遊記/東京自由行攻略-5-天食住行全記錄與必訪景點推薦-9da2c51fa4f2/">再訪東京</a> — 來場一生必看一次的日本花火大會及前往東京近郊川越一日遊。</p>
<h3 id="kkday-推廣-">KKday 推廣 🛒</h3>
<ul>
  <li><a href="https://www.kkday.com/zh-tw/product/9558-tokyo-private-day-tour-mt-fuji-hakone-and-downtown-tokyo-japan?cid=19365" target="_blank">【專享包車】 東京市區,富士山,箱根,鎌倉,伊豆,熱海,輕井澤,成田機場接送, 客製化包車一日遊</a></li>
  <li><a href="https://www.kkday.com/zh-tw/product/7913-keisei-skyliner-narita-airport-express-ticket?cid=19365" target="_blank">Skyliner京成電鐵車票</a></li>
  <li><a href="https://www.kkday.com/zh-tw/product/559745?cid=19365" target="_blank">日本東京包車一日遊|羽田・成田機場接送|東京往返富士山/箱根/東京市區/橫濱/鐮倉/白馬/輕井澤</a></li>
  <li><a href="https://www.kkday.com/zh-tw/product/137689-japan-unlimited-data-esim-card?cid=19365" target="_blank">【50% OFF】日本 eSIM|Docomo / KDDI / SoftBank / Rakuten|支援ChatGPT 及 Gemini</a></li>
  <li><a href="https://www.kkday.com/zh-tw/product/267443?cid=19365" target="_blank">【日本東京機場專車接送機】成田機場(NRT)・羽田機場(HND)↔東京23區・東京迪士尼樂園/迪士尼海洋・橫濱・箱根・伊豆/熱海・河口湖・鎌倉|包車配備專屬管家服務</a></li>
  <li><a href="https://www.kkday.com/zh-tw/product/573264?cid=19365" target="_blank">【日本東京接送機服務】成田機場 (NRT)・羽田機場 (HND)・東京23區・東京迪士尼・橫濱地區</a></li>
</ul>

<h4 id="上次行程">上次行程</h4>

<blockquote>
  <p><em>上次「 <a href="/posts/z-度旅行遊記/東京自由行攻略-5-天食住行全記錄與必訪景點推薦-9da2c51fa4f2/"><strong>[遊記] 2023 東京 5 日自由行</strong></a> 」 是 2023/06 月來東京的，沒有 9 月來這麼熱，上次主要去了：東京鐵塔、Shibuya Sky、晴空塔、淺草雷門、迪士尼海洋、橫濱鋼彈、橫濱纜車、台場、新宿、皇居。</em></p>
</blockquote>

<blockquote>
  <p><em>有興趣的朋友可以 <a href="/posts/z-度旅行遊記/東京自由行攻略-5-天食住行全記錄與必訪景點推薦-9da2c51fa4f2/">移步閱覽</a> 。</em></p>
</blockquote>

<h3 id="樂">樂</h3>
<ul>
  <li>Day 1 (09/13 六) — 抵達東京、澀谷、Shibuya Sky</li>
  <li>Day 2 (09/14 日) —川越一日遊、晚上池袋逛街</li>
  <li>Day 3 (09/15 一) —東京車站地下街、熱海花火大會</li>
  <li>Day 4 (09/16 二) — 麻布台 Teamlab、原宿逛街、原宿 HARRY 水獺咖啡廳</li>
  <li>Day 5 (09/17 三) —上野逛逛、返程</li>
</ul>

<h4 id="shibuya-sky">Shibuya Sky</h4>
<ul>
  <li><strong>Shibuya Sky 門票很熱門，務必提前購買。</strong></li>
  <li><a href="https://www.shibuya-scramble-square.com.t.apy.hp.transer.com/sky/ticket/" target="_blank">官網</a> 開賣時間：台灣時間晚間 11:00 可購買 +2 週後的入場日門票
官網購買請先註冊會員跟先熟悉購買流程，票券使用人姓名可以填暱稱，不用真的填寫詳細護照姓名。</li>
  <li>我們是買 18:40~18:59 夜景的時段，也可以選擇早一點看夕陽或是最末場 21:00 吃完晚餐再去。</li>
  <li>其他購買渠道： <a href="https://www.kkday.com/zh-tw/product/133300-shibuya-sky-observatory-e-ticket-tokyo?cid=19365" target="_blank">KKday 澀谷SHIBUYA SKY展望台門票｜即買即用</a></li>
</ul>

<blockquote>
  <p><em>價格：¥3,400 日幣/人</em></p>
</blockquote>

<h4 id="熱海花火大會">[熱海花火大會</h4>

<p>熱海煙花大會](https://hoshinoresorts.com/zh_tw/hotels/risonareatami/activities/13288/){:target=”_blank”} 官網。</p>
<ul>
  <li>2025 施放日：3/23、4/20、4/28、5/31、7/25、8/5、8/8、8/18、8/25、 <strong>9/15</strong> 、9/23、10/13、11/3、11/24、12/7、12/19。</li>
  <li>7、8 月施放時間：8:15 PM~8:40 PM 25分鐘</li>
  <li><strong>其他月份施放時間：8:20 PM~8:40 PM 20分鐘</strong></li>
</ul>

<p>在海灘釋放、免費入場、無付費席，從 JR 熱海站走路會到， <strong>保險起見只需要提前預約好來回的 JR 或熱海的住宿就好</strong> 。</p>

<p><strong>建議要自備野餐墊！建議要自備野餐墊！建議要自備野餐墊！</strong></p>

<blockquote>
  <p><em>我們是去 9/15 8:20 PM 施放的場次。</em></p>
</blockquote>

<h4 id="麻布台-teamlab-teamlab-borderless-mori-building-digital-art-museum"><a href="https://borderless-azabudai.ticket.teamlab.art/#/" target="_blank">麻布台 TeamLab (teamLab Borderless: MORI Building DIGITAL ART MUSEUM)</a></h4>
<ul>
  <li>票蠻多的，不過還是建議出發前提前購買。</li>
  <li>其他購買渠道： <a href="https://affiliate.klook.com/redirect?aid=99683&amp;aff_adid=1134759&amp;k_site=https%3A%2F%2Fwww.klook.com%2Fzh-TW%2Factivity%2F20707-teamlab-borderless-admission-ticket-tokyo%2F" target="_blank">東京teamLab Borderless數位藝術美術館門票</a></li>
</ul>

<blockquote>
  <p><em>價格：¥4,800 日幣/人</em></p>
</blockquote>

<h4 id="ｈａｒｒｙ原宿テラス店-水獺咖啡廳"><a href="https://harinezumi-cafe.com/store/harajuku-terrace/" target="_blank">ＨＡＲＲＹ原宿テラス店</a> 水獺咖啡廳</h4>

<p>之前在 ig 被燒到的可以跟水獺互動的 <a href="https://harinezumi-cafe.com/store/harajuku-terrace/" target="_blank">咖啡廳</a> 。</p>

<p><img src="/assets/958599363857/1*gV6F9gn_uu9pYeVZSlPQbQ.webp" alt="https://www.instagram.com/p/DPBKurVD99c/" loading="lazy" decoding="async" width="1148" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMTQ4IiBoZWlnaHQ9IjEyMDAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><a href="https://www.instagram.com/p/DPBKurVD99c/" target="_blank">https://www.instagram.com/p/DPBKurVD99c/</a></p>
<ul>
  <li><a href="https://book.squareup.com/appointments/3kuc83ozz99u5q/location/G3XVJ4FPKRGCP/services/5TOZDR3PNU7X52TOXELQN575" target="_blank">官網預約</a></li>
  <li>東京其他地方也有分店可以去 <a href="https://harinezumi-cafe.com/store/" target="_blank">官網查看</a> 。</li>
  <li><strong>建議要提前預約，當日去會撲空。</strong></li>
  <li>如果日期前行程有異動可以免費取消。</li>
  <li>時間：16:30–17:30</li>
</ul>

<blockquote>
  <p><em>價格：¥3,080 日幣/人一小時基本費 + 到現場後加 ¥880/人 跟水獺親密互動 5 分鐘。</em></p>
</blockquote>

<p><a href="https://kantenlife.tw/2024/04/18/house-of-otters-japan/#%E6%97%A5%E6%9C%AC%E6%B0%B4%E7%8D%BA%E5%92%96%E5%95%A1%E5%BB%B3%EF%BD%9C%E3%82%AB%E3%83%AF%E3%82%A6%E3%82%BD_%E8%8B%A5%E6%9E%97%E3%81%AE%E5%AE%B6%E3%80%81HARRY_HARAJUKU_terrace%E6%AF%94%E8%BC%83%E5%88%86%E4%BA%AB" target="_blank"><strong>不過後來查資料，感覺另一家 カワウソ_若林の家 比較好，互動時間較長。</strong></a></p>
<h3 id="行">行</h3>
<h4 id="機票--日本航空-japan-airlines">機票 — 日本航空 Japan Airlines</h4>
<ul>
  <li>去程 9月13日：JL802日本航空 —10:00 TPE 桃園國際機場 T2 -&gt; 14:25 NRT成田機場 T2</li>
  <li>回程 9月17日：JL809日本航空 — 18:10 NRT成田機場T2 -&gt; 18:10 TPE桃園國際機場 T2</li>
</ul>

<blockquote>
  <p><em>價格：NT $12,324/人</em></p>
</blockquote>

<p><a href="https://www.jal.co.jp/tw/zhtw/inter/baggage/checked/" target="_blank">另外日本航空的托運額度蠻多的，經濟艙： <strong>2 件23 公斤/件</strong></a></p>
<h4 id="jr-新幹線-東京---熱海--45-分鐘">JR 新幹線 東京 &lt;-&gt; 熱海 (~= 45 分鐘)</h4>

<p>因為怕當天沒有車來回所以提前先買好車票。</p>
<ul>
  <li>開賣時間：台灣時間晚間 11:00 可購買 +4 週後日期的車票</li>
  <li>可以從 <a href="https://e5489.jr-odekake.net/e5489/ibpc/CBTrainEntryExternalPC?LANG=tc" target="_blank">官網購買</a> 或是透過 <a href="https://www.kkday.com/zh-tw/transportation/japan-rail?cid=19365" target="_blank">KKday</a> / <a href="https://affiliate.klook.com/redirect?aid=99683&amp;aff_adid=1134759&amp;k_site=https%3A%2F%2Fwww.klook.com%2Fzh-TW%2Fjapan-rail%2F" target="_blank">Klook</a> 購買 (自行比價)</li>
</ul>

<blockquote>
  <p><em>我們買的是：</em></p>
</blockquote>

<blockquote>
  <p><em>- <strong>東京 14:57 -&gt; 熱海 15:42</strong> — こだま Kodama 735</em></p>
</blockquote>

<blockquote>
  <p><em>- <strong>熱海 22:02 -&gt; 東京 22:48 —</strong> こだま Kodama 752</em></p>
</blockquote>

<blockquote>
  <p><em>價格：NT $1,882+NT $56 (代訂平台的座位偏好費)/人</em></p>
</blockquote>

<h4 id="skyliner-京成電鐵車票--41-分鐘"><a href="https://www.kkday.com/zh-tw/product/7913-keisei-skyliner-narita-airport-express-ticket?cid=19365" target="_blank">Skyliner 京成電鐵車票</a> (~= 41 分鐘)</h4>

<p>應該是目前最快從成田機場到東京市區(上野)的方式。</p>
<ul>
  <li>無自由席</li>
  <li>KKday 購買： <a href="https://www.kkday.com/zh-tw/product/7913-keisei-skyliner-narita-airport-express-ticket?cid=19365" target="_blank">Skyliner 京成電鐵車票</a></li>
  <li>從網路先買比較便宜，到車站再劃位，彈性好用</li>
</ul>

<blockquote>
  <p><em>價格：NT $912 (來回兩張票)/人</em></p>
</blockquote>

<blockquote>
  <p><em>要注意的是 KKday 買來回，就是出兩張票，不要誤會買錯了。</em></p>
</blockquote>

<h4 id="川越周遊券-電車巴士--30-分鐘"><a href="https://affiliate.klook.com/redirect?aid=99683&amp;aff_adid=1134759&amp;k_site=https%3A%2F%2Fwww.klook.com%2Fzh-TW%2Factivity%2F100464-kawagoe-pass-digital-ticket%2F" target="_blank">川越周遊券 (電車+巴士)</a> (~= 30 分鐘)</h4>
<ul>
  <li>東武東上線往返：池袋站 -&gt; 川越站或川越市站</li>
  <li>全天無限次搭乘東武巴士 (包括東武小江戶循環巴士)</li>
  <li>請注意：巴士有西武跟東武，此票券為東武</li>
  <li>KLook 購買： <a href="https://affiliate.klook.com/redirect?aid=99683&amp;aff_adid=1134759&amp;k_site=https%3A%2F%2Fwww.klook.com%2Fzh-TW%2Factivity%2F100464-kawagoe-pass-digital-ticket%2F" target="_blank">川越優惠周遊券 Kawagoe Discount Pass</a></li>
  <li>無需兌換，只需要使用前線上點兌換，出示 QRCode 進入電車或巴士司機即可。</li>
</ul>

<blockquote>
  <p><em>價格：NT $214/人</em></p>
</blockquote>

<h4 id="機場接送">機場接送</h4>
<ul>
  <li><a href="https://www.kkday.com/zh-tw/airport-transfer?cid=19365" target="_blank">KKday 機場接送預約</a> / <a href="https://affiliate.klook.com/redirect?aid=99683&amp;aff_adid=1134759&amp;k_site=https%3A%2F%2Fwww.klook.com%2Fzh-TW%2Fairport-transfers%2F" target="_blank">Klook 機場接送預約</a></li>
  <li>目前雙 K 都在打機場接送，便宜安全又快速，可以自行比價選擇。</li>
</ul>

<blockquote>
  <p><em>雙北 -&gt; 桃機</em></p>
</blockquote>

<blockquote>
  <p><em>價格：NT $752/台</em></p>
</blockquote>

<h4 id="esim">eSIM</h4>

<p>同樣直接在 KKday 購買 5G 吃到飽 eSIM，這次在東京、熱海、川越使用上沒遇到特別問題或被降速。</p>
<ul>
  <li>KKday 購買： <a href="https://www.kkday.com/zh-tw/product/121004-unlimited-data-esim-card-japan?cid=19365" target="_blank">日本 eSIM 無限流量 KDDI / Softbank</a></li>
</ul>

<blockquote>
  <p><em>價格：NT $309/5 日/無限流量</em></p>
</blockquote>

<h4 id="vist-japan">Vist Japan</h4>

<p><a href="https://zhgchg.li/posts/z-%E5%BA%A6%E6%97%85%E8%A1%8C%E9%81%8A%E8%A8%98/%E4%BA%AC%E9%98%AA%E7%A5%9E%E8%87%AA%E7%94%B1%E8%A1%8C%E6%94%BB%E7%95%A5-%E4%BA%AC%E9%83%BD%E5%A4%A7%E9%98%AA%E7%A5%9E%E6%88%B68%E6%97%A5%E9%81%8A%E5%85%A8%E7%B4%80%E9%8C%84%E8%88%87%E5%AF%A6%E7%94%A8%E4%BA%A4%E9%80%9A%E4%BD%8F%E5%AE%BF%E6%8C%87%E5%8D%97-76d66c2e34af/#20241125-%E6%9B%B4%E6%96%B0%E5%85%A5%E5%A2%83%E5%AF%A9%E6%9F%A5%E8%88%87%E6%B5%B7%E9%97%9C%E7%94%B3%E5%A0%B1qr-code-%E5%B7%B2%E5%90%88%E4%BD%B5%E6%88%90%E5%90%8C%E4%B8%80%E5%80%8B%E5%85%A5%E5%A2%83%E5%AE%A1%E6%9F%A5%E5%8F%8A%E6%B5%B7%E5%85%B3%E7%94%B3%E6%8A%A5%E7%9A%84qr%E7%A0%81%E6%B2%92%E6%9C%89%E8%97%8D%E7%A2%BC%E9%BB%83%E7%A2%BC%E5%8D%80%E5%88%A5%E4%BB%A5%E4%B8%8B%E5%85%A7%E5%AE%B9%E5%8F%AA%E7%95%B6%E7%B4%80%E9%8C%84%E5%8F%AF%E4%BB%A5%E5%BF%BD%E7%95%A5" target="_blank">請參考之前的文章，事前完成登錄就好，目前入境審查與海關審查 QRCode 以合成同一個。</a></p>
<h3 id="住">住</h3>

<p>因為找的時間接近出發日，綜合比較了價格跟便利性之後選擇住在新橋站附近。(上次住汐留覺得新橋更方便一些)</p>
<h4 id="東京新橋大和-roynet-飯店-4-晚"><a href="https://affiliate.klook.com/redirect?aid=99683&amp;aff_adid=1134759&amp;k_site=https%3A%2F%2Fwww.klook.com%2Fzh-TW%2Fhotels%2Fdetail%2F127656-daiwa-roynet-hotel-shimbashi%2F" target="_blank">東京新橋大和 ROYNET 飯店</a> (4 晚)</h4>

<p><img src="/assets/958599363857/1*FPa2l4mYfm_ApuJt_LqSag.webp" alt="東京新橋大和 ROYNET 飯店" loading="lazy" decoding="async" width="579" height="539" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1NzkiIGhlaWdodD0iNTM5Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><a href="https://affiliate.klook.com/redirect?aid=99683&amp;aff_adid=1134759&amp;k_site=https%3A%2F%2Fwww.klook.com%2Fzh-TW%2Fhotels%2Fdetail%2F127656-daiwa-roynet-hotel-shimbashi%2F" target="_blank">東京新橋大和 ROYNET 飯店</a></p>
<ul>
  <li><a href="https://affiliate.klook.com/redirect?aid=99683&amp;aff_adid=1134759&amp;k_site=https%3A%2F%2Fwww.klook.com%2Fzh-TW%2Fhotels%2Fdetail%2F127656-daiwa-roynet-hotel-shimbashi%2F" target="_blank">一樣直接從 Klook 訂，累積點數</a></li>
  <li>距離新橋站走路約 2 分鐘，很近。</li>
  <li>五天四夜住到底，不更換飯店。</li>
</ul>

<blockquote>
  <p><em>價格：NT$ 18,404/4晚/2人/標準雙人房，約 NT$ 2,300/每人每晚。</em></p>
</blockquote>

<blockquote>
  <p>準備就緒！出發！</p>
</blockquote>

<p><a href="https://digital.jal.co.jp/ssci/identification?lang=zh-tw" target="_blank"><strong>起飛前 24 小時可先完成網路報到、選位，到機場就可以直接排行李托運，加快流程。</strong></a></p>
<h3 id="day-1-0913-六--抵達東京shibuya-sky">Day 1 (09/13 六) — 抵達東京、Shibuya Sky</h3>
<h4 id="0650-搭乘機場接送">06:50 搭乘機場接送</h4>
<h4 id="0720-抵達桃園機場第二航廈">07:20 抵達桃園機場第二航廈</h4>

<p><img src="/assets/958599363857/1*TkF8qP3e1-iladiHOZgERQ.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<h4 id="0730-排行李托運">07:30 排行李托運</h4>

<p><img src="/assets/958599363857/1*sNzgznqv8RxRYfwyCaIntg.webp" alt="" loading="lazy" decoding="async" width="1200" height="843" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg0MyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>分配到 1 號邊邊角角的櫃位， <a href="https://digital.jal.co.jp/ssci/identification?lang=zh-tw" target="_blank">已事先完成網路報到</a> 直接排托運行李，但是前面的人卡了很久，所以大概排了 30 多分鐘才完成托運手續。</p>
<h4 id="-0820-完成安檢出境">~= 08:20 完成安檢、出境</h4>

<p><img src="/assets/958599363857/1*lKRxUWP4BGQA1q97X3jJJg.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>一早人蠻多的，大約 8:20 左右完成安檢出境，先去二樓吃個摩斯當早餐；分配到的登機門也是最遠的 D1。</p>
<h4 id="二航出境免稅店-le-labo">二航出境免稅店 Le Labo</h4>

<p><img src="/assets/958599363857/1*wyJNusrf7ElLdfZ33YaPWA.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>意外發現二航出境免稅店有 Le Labo，櫃姐有介紹現在有活動，新會員領券折抵後 50ml 價格 NT $5,500，比去日本境內買便宜(約 NT $5,800)。</p>

<p><strong>2025 年最新搜集的價格表 — 50ml：</strong></p>
<ul>
  <li>台灣門市價：NT $7,600</li>
  <li>台灣機場門市免稅價：NT $5,500 (活動價)</li>
  <li>日本門市退完稅價格：約 NT $5,700</li>
  <li><a href="https://www.fasola.jp/en/searchByBrand.aspx" target="_blank"><strong>日本機場門市</strong></a> <strong>免稅價：約 NT $5,200 🤩 — 我這次就是最後在成田機場</strong> T2 <strong>航廈出境後才買。</strong></li>
</ul>

<p><img src="/assets/958599363857/1*4AU39FGOzbR7TnCVzCCjKg.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*sgPd1BrkZsr2F37f1m1Dlg.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*I6HuL5NdPzQxp3lu43fiUw.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/958599363857/1*gkCuhDtktYmN6ed170fRZg.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>在機場亂逛等登機，在 D1 The North Face 旁有兩排免費的按摩椅可以體驗(跟附近店家拿免費代幣即可使用)。</p>
<h4 id="0940-前往登機口準備登機">09:40 前往登機口準備登機</h4>

<p><img src="/assets/958599363857/1*U5QpF19SbJlVAllxZZiQMA.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*izSSFMEAPqbhFtTgt13ndw.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>這次搭到的飛機是 BOEING 737–800 機型有點老舊。</p>

<blockquote>
  <p><em>但我回來才發現，機上有提供免費一小時機上 WiFi。</em></p>
</blockquote>

<h4 id="1006-起飛">10:06 起飛</h4>

<p><img src="/assets/958599363857/1*McNWkLo576Z3uJTm9QgYWA.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<h4 id="japan-airlines-飛機餐">JAPAN AIRLINES 飛機餐</h4>

<p><img src="/assets/958599363857/1*VoM9bEiz7mMma7fAwp48tw.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*tu0L7uohZIqsd9V4JlKGFg.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*s69aVqVz7J7wXFSNi66SsQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>雖然飛機有點年紀，但是飛機餐真的不錯，漢堡肉肉醬斜管麵，還有一碗小蕎麥豆皮涼麵跟甜點檸檬布丁(好吃!)。</p>
<h4 id="1439-降落日本成田國際機場-誤點-14-分鐘">14:39 降落日本成田國際機場 (誤點 14 分鐘)</h4>

<p><img src="/assets/958599363857/1*0Dvy2jcY6ED8uW6NgmxcXw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*W_bKd7fHb6kBH3uKPqepPg.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>地板有點濕潤，天氣多雲，看來稍早下過大雨；抵達後就開始成田機場大地遊戲(下機走到入境審查大概要 5 分鐘吧…)。</p>
<h4 id="-1505-完成入境手續提領行李出機場">~= 15:05 完成入境手續＋提領行李出機場</h4>

<p><img src="/assets/958599363857/1*Xru5o3DBU_GZyTlVW8Wfcw.webp" alt="" loading="lazy" decoding="async" width="937" height="1016" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MzciIGhlaWdodD0iMTAxNiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*oD9JhXDuh47h4aBYxepu7Q.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>出機場後直接搭乘手扶梯到樓下往「鐵道」方向，找到「SKYLINER &amp; KEISEI INFORMATION CENTER」櫃檯出示預先在網上購買的 <a href="https://www.kkday.com/zh-tw/product/7913-keisei-skyliner-narita-airport-express-ticket?cid=19365" target="_blank">Skyliner 京成電鐵車票</a> QRCode 給站務員，就能兌換化為下一班的車票。</p>

<blockquote>
  <p><em>要注意，車站這邊還有其他鐵路的 INFORMATION CENTER，請認明圖上的 INFORMATION CENTER 才是。</em></p>
</blockquote>

<p><img src="/assets/958599363857/1*yvMJw_HeZ9Mk8qXaFj6gKw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*3rXtVnwanfORgy2DPDrKAA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>沒劃到 15:16 的班次，只能搭 15:42 的。</p>

<p>車站這邊有賣 <a href="https://www.jreast.co.jp/zh-CHT/multi/welcomesuica/welcomesuica.html" target="_blank">Welcome Suica (28 天內要用完)</a> 跟一般 Suica 卡加值機器，可以趁等車空檔先把這次旅程需要花的交通費用儲一儲 ( <a href="https://zhgchg.li/posts/z-%E5%BA%A6%E6%97%85%E8%A1%8C%E9%81%8A%E8%A8%98/%E4%BA%AC%E9%98%AA%E7%A5%9E%E8%87%AA%E7%94%B1%E8%A1%8C%E6%94%BB%E7%95%A5-%E4%BA%AC%E9%83%BD%E5%A4%A7%E9%98%AA%E7%A5%9E%E6%88%B68%E6%97%A5%E9%81%8A%E5%85%A8%E7%B4%80%E9%8C%84%E8%88%87%E5%AF%A6%E7%94%A8%E4%BA%A4%E9%80%9A%E4%BD%8F%E5%AE%BF%E6%8C%87%E5%8D%97-76d66c2e34af/#-2" target="_blank">但我自己是用 iPhone 的 Suica 方便簡單好用！</a> )</p>

<p><img src="/assets/958599363857/1*Rx_dIfNqzUwhc21VzUvIkw.webp" alt="" loading="lazy" decoding="async" width="954" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NTQiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>看「京成線」指示牌進站後找到對應的月台候車。</p>
<h4 id="-1542-上車">~= 15:42 上車</h4>

<p><img src="/assets/958599363857/1*gM-xQihcfGhbEXFv8_uxIg.webp" alt="" loading="lazy" decoding="async" width="949" height="1081" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NDkiIGhlaWdodD0iMTA4MSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>這月台有很多種車次會經過停靠，請務必確認後再上車；工作人員也會沿途宣導，可以給他看車票確認， <a href="https://www.jreast.co.jp/tc/downloads/pdf/ntrt10_tc.pdf" target="_blank"><strong>成田特快全車指定席，無自由座</strong></a> <strong>，無車票請勿上車</strong> 。</p>

<p><img src="/assets/958599363857/1*IEQNy3qmUMrm_GaBAFqUTw.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>成田特快的位子夠大，28 寸行李箱放得下；如果覺得卡可以放在車前車後的區域，或是放行李架(要拿小心就是)。</p>

<p><img src="/assets/958599363857/1*Z0UWLv0qvBf77Ue0xfUQ4w.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>天氣陰陰。</p>
<h4 id="1623-抵達京成上野站">16:23 抵達京成上野站</h4>

<p><img src="/assets/958599363857/1*Z8aKd12JgWIzCE7qtBsgiQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*NEojh1UeOaVynhrnp_d7bQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>下車後依照指示走地下道到 G 銀座線搭車，澀谷方向上野廣小路到新橋站。</p>
<h4 id="1650-抵達-東京新橋大和-roynet-飯店">16:50 抵達 東京新橋大和 ROYNET 飯店</h4>

<p><img src="/assets/958599363857/1*b1FSV2C8ZsLRq_A_PHnI5Q.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*09O3hfLEqGKXuUjmv5ALMA.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>新橋站出來走一下就到了，非常方便！</p>

<p>飯店提供的免費備品非常多，還有面膜、入浴劑、耳塞、隨身攜帶的漱口水…等</p>
<iframe class="embed-video" loading="lazy" src="https://www.youtube.com/embed/jKsZna0d38c" title="東京新橋大和 ROYNET 飯店" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>

<p><img src="/assets/958599363857/1*JXSKiQeoOOtAlQt946SXiw.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>有點像 <a href="/posts/z-度旅行遊記/山陽地區廣島岡山自由行攻略-6日行程-交通-住宿全解析-31b9b3a63abc/"><strong>前年去廣島、岡山</strong></a> 住的「 <a href="https://www.youtube.com/watch?v=gmwHRihsyXI" target="_blank">利夫馬克斯岡山倉敷站前飯店</a> 」，房間很小，感覺是單人房只是放了一張雙人床。</p>

<blockquote>
  <p><em>缺點除了小，還有房間冰箱沒有冷凍功能。</em></p>
</blockquote>

<p>東西放完就出發去澀谷。</p>
<h4 id="1830-排隊入場-shibuya-sky">18:30 排隊入場 Shibuya SKY</h4>

<p>在 澀谷 Scramble Square 一樓找到上去「Shibuya SKY」展望設施的電梯，排隊搭電梯上去後給驗票人員看 QRCode 就能排隊等入場。(入場時核銷)</p>

<p><img src="/assets/958599363857/1*XTalH3d03U4tmBXD-iK5yw.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>這邊看得到澀谷十字路口。</p>

<blockquote>
  <p><em>上來後先去免費置物櫃寄物，自拍桿之類的是不能帶的。</em></p>
</blockquote>

<p><img src="/assets/958599363857/1*y5jXwXKVIcG5Df9BxPTHew.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*DcQwikjwYR6G4JQ9c22cIQ.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<blockquote>
  <p><a href="https://zhgchg.li/posts/z-%E5%BA%A6%E6%97%85%E8%A1%8C%E9%81%8A%E8%A8%98/%E6%9D%B1%E4%BA%AC%E8%87%AA%E7%94%B1%E8%A1%8C%E6%94%BB%E7%95%A5-5-%E5%A4%A9%E9%A3%9F%E4%BD%8F%E8%A1%8C%E5%85%A8%E8%A8%98%E9%8C%84%E8%88%87%E5%BF%85%E8%A8%AA%E6%99%AF%E9%BB%9E%E6%8E%A8%E8%96%A6-9da2c51fa4f2/#%E6%B8%8B%E8%B0%B7--shibuya-sky" target="_blank"><em>舊地重遊</em></a> <em>，相較兩年前發福了不少。</em></p>
</blockquote>

<blockquote>
  <p><em>手扶梯有兩邊，一邊上一邊下都蠻好拍的。</em></p>
</blockquote>

<p><img src="/assets/958599363857/1*cRbCKPoZF8FPxklnYTtZ0A.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*Bzn4qBq4UfpqSXbcf8Gk-A.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>頂樓夜景，不過相較上次，這次天氣比較差，霧濛濛還帶點毛毛細雨。</p>

<p><img src="/assets/958599363857/1*dinKHMOg-9bNoG3Chn5HWA.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*WNjqJ1VyhKnBzWEgV2lGfg.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>看完一路往下走，經過 Bar，這邊的景也不錯：</p>

<p><img src="/assets/958599363857/1*gsgDlAcVwHQ-pG49di-PIQ.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*Hfioo92OP-JnADRbPH4oMA.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<h4 id="1945-回到澀谷十字路口">19:45 回到澀谷十字路口</h4>

<p><img src="/assets/958599363857/1*kLQ5R8LmFXiOduVRoXC-Jw.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/958599363857/1*OOV8P-Wd_qcLWKvUcPdj_A.webp" alt="" loading="lazy" decoding="async" width="1200" height="854" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg1NCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>剛好今際之國的闖關者第三季即將開播，來回味一下場景；體感上人比之前還多很多。</p>

<p><img src="/assets/958599363857/1*nSlNoHtXjCcmZwLJdhK5Wg.webp" alt="" loading="lazy" decoding="async" width="955" height="1222" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NTUiIGhlaWdodD0iMTIyMiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>過馬路一路往 澀谷 Parco 走，想去吃 <a href="https://maps.app.goo.gl/B11rcg3MLFRgJLqT8" target="_blank">極味屋</a> 。</p>

<blockquote>
  <p><a href="https://zhgchg.li/posts/z-%E5%BA%A6%E6%97%85%E8%A1%8C%E9%81%8A%E8%A8%98/%E6%9D%B1%E4%BA%AC%E8%87%AA%E7%94%B1%E8%A1%8C%E6%94%BB%E7%95%A5-5-%E5%A4%A9%E9%A3%9F%E4%BD%8F%E8%A1%8C%E5%85%A8%E8%A8%98%E9%8C%84%E8%88%87%E5%BF%85%E8%A8%AA%E6%99%AF%E9%BB%9E%E6%8E%A8%E8%96%A6-9da2c51fa4f2/#%E6%B8%8B%E8%B0%B7-parco--%E6%A5%B5%E5%91%B3%E5%B1%8B" target="_blank"><em>上次來吃過一次，念念不忘。</em></a></p>
</blockquote>

<h4 id="2000-抵達澀谷-parco">20:00 抵達澀谷 Parco</h4>

<p><img src="/assets/958599363857/1*5UmVsCAy5wcJHCK9h8xQvg.webp" alt="" loading="lazy" decoding="async" width="1400" height="984" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9Ijk4NCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>很可惜 <a href="https://maps.app.goo.gl/B11rcg3MLFRgJLqT8" target="_blank">極味屋</a> 已售罄（過幾天再來吃），在同樣的美食街吃了另一家炸串店。</p>
<h4 id="2015-串カツあらた-渋谷パルコ店-kushikatsu-arata">20:15 <a href="https://maps.app.goo.gl/PTZVZEqdZayWUiap9" target="_blank">串カツあらた 渋谷パルコ店 Kushikatsu Arata</a></h4>

<p><img src="/assets/958599363857/1*cbSR83bqt56GgvT8gTYRQw.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>點了幾個招牌炸串跟肉加倍炸串還有一碗牛腩飯跟兩杯酒，一共 ¥6,340 日幣。</p>

<blockquote>
  <p><em>日式粉粉的炸串好吃不膩，牛腩飯很入味墊墊胃。</em></p>
</blockquote>

<blockquote>
  <p><strong><em>要注意炸串店會多收高麗菜基本費(お通し)</em></strong> <em>，實際忘了多少，應該是一人</em> ¥ <em>300 日幣左右。</em></p>
</blockquote>

<h4 id="2130-c-pla-shibuya">21:30 <a href="https://maps.app.goo.gl/bTwvHKkbxHofxnig6" target="_blank">C-pla Shibuya</a></h4>

<p><img src="/assets/958599363857/1*G0pG6izsdcwUT_Pnfr7xYQ.webp" alt="" loading="lazy" decoding="async" width="950" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NTAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*a8oZSBgBI3ZIOftsOxSH5A.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>吃飽走回車站的路上經過一家很大的 C-pla 扭蛋店，進去尋寶。</p>

<blockquote>
  <p><em>沒看到想扭的，撤退！</em></p>
</blockquote>

<h4 id="晚安東京">晚安！東京</h4>

<p><img src="/assets/958599363857/1*WwT578CuW9uDdF6FEoJogA.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>買個宵夜回飯店休息。</p>
<h3 id="day-20914-日--川越一日遊池袋逛街">Day 2(09/14 日) — 川越一日遊、池袋逛街</h3>
<h4 id="0930-出門先去吃麥當勞早餐--mcdonalds-麥當勞新橋日比谷口店">09:30 出門，先去吃麥當勞早餐 — <a href="https://maps.app.goo.gl/7kZzxwNhtNjBKdZC9" target="_blank">McDonald’s 麥當勞新橋日比谷口店</a></h4>

<p><img src="/assets/958599363857/1*ylLKnhvJGSh1inGDz4QPXw.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*PBCIcEtyaPLO7bcKEr_X9A.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>一樣的鬆餅火腿起司蛋堡！</p>

<blockquote>
  <p><em>這家麥當勞 <a href="/posts/z-度旅行遊記/東京自由行攻略-5-天食住行全記錄與必訪景點推薦-9da2c51fa4f2/">上次也來過</a> ，缺點是很舊、環境沒有很乾淨、沒有自動點餐機。</em></p>
</blockquote>

<h4 id="1050-抵達-池袋站-搭-山手線">10:50 抵達 池袋站 (搭 山手線)</h4>

<p><img src="/assets/958599363857/1*1V90DtPc7kU51PnAhjgL8A.webp" alt="" loading="lazy" decoding="async" width="1200" height="663" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjY2MyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>轉搭 東武東上線 前往川越。</p>

<p><strong>我們有在線上先買好 <a href="https://affiliate.klook.com/redirect?aid=99683&amp;aff_adid=1134759&amp;k_site=https%3A%2F%2Fwww.klook.com%2Fzh-TW%2Factivity%2F100464-kawagoe-pass-digital-ticket%2F" target="_blank">川越周遊券 (電車+巴士)</a> ，線上點擊啟用開始計算時間：</strong></p>

<p><img src="/assets/958599363857/1*-RIG2ZudPZbbPykqbqCb0g.webp" alt="" loading="lazy" decoding="async" width="935" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MzUiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*hD_LqigAsv0lTWLS4uI1uA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>直接進最旁邊的 駅事務室，給車站人員看 QRCode 就能直接進出站。(無需劃位)</p>
<h4 id="1100-搭乘-東武東上線川越特急--往小川町埼玉縣--經川越">11:00 搭乘 東武東上線川越特急 — 往小川町(埼玉縣) — 經川越</h4>

<p><img src="/assets/958599363857/1*3oFkNAprNiko_GHcF4r-3A.webp" alt="" loading="lazy" decoding="async" width="1400" height="976" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9Ijk3NiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<h4 id="1126-抵達-川越站">11:26 抵達 川越站</h4>

<p><img src="/assets/958599363857/1*TxaLPPDw4GDqEEWfRnOx7Q.webp" alt="" loading="lazy" decoding="async" width="1200" height="850" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg1MCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*fnZcZ9Ct4iNfcWq9Kog7jw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*pxQYLZnHM7-UA6B7RisQnw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>一樣從最旁邊的 駅事務室出站後，先去拿了一份觀光手冊，決定先搭公車去最遠的 <a href="https://maps.app.goo.gl/3vVRnikJQL3zYuGi6" target="_blank">冰川神社</a> ，再一路逛回來。</p>

<p><img src="/assets/958599363857/1*h0c9y56GEfHWTrtP7q8kOQ.webp" alt="" loading="lazy" decoding="async" width="1200" height="848" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg0OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>直接從二樓天橋出站往巴士搭乘處下樓。</p>
<h4 id="1135-川越巴士站等車">11:35 川越巴士站等車</h4>

<p><img src="/assets/958599363857/1*Z92lcpTR1OitKAL3LRVrtw.webp" alt="" loading="lazy" decoding="async" width="1200" height="853" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg1MyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>一號月台、三號月台都有很多車會到冰川神社，最後是在三號月台搭 名 01。</p>

<blockquote>
  <p><strong><em>只要認明「東武</em> バス <em>」，使用 <a href="https://affiliate.klook.com/redirect?aid=99683&amp;aff_adid=1134759&amp;k_site=https%3A%2F%2Fwww.klook.com%2Fzh-TW%2Factivity%2F100464-kawagoe-pass-digital-ticket%2F" target="_blank">川越周遊券 (電車+巴士)</a> ，不用特別兌換；只要下車的時候給司機看 QRCode 畫面即可。</em></strong></p>
</blockquote>

<blockquote>
  <p><strong><em>用周遊券不要上車刷到 Suica 哦。</em></strong></p>
</blockquote>

<h4 id="1155-抵達-冰川神社">11:55 抵達 冰川神社</h4>

<p><img src="/assets/958599363857/1*ebEe1SnjEeYVieePNBdmuw.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<h4 id="冰川神社">冰川神社</h4>

<p><img src="/assets/958599363857/1*ouWflkODbhhc7t8gRUwhwA.webp" alt="" loading="lazy" decoding="async" width="1200" height="865" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg2NSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*9W_Haty-iabNIwCd3m7Hiw.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>神社不大，有很多風車風鈴。</p>

<p><strong>除穢氣的人形流：</strong></p>

<p><img src="/assets/958599363857/1*ZzDJul6maBw4Km4g3ixHeQ.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><strong>風鈴祈福跟繪馬區</strong></p>

<p><img src="/assets/958599363857/1*Xj_rvn15hT9p2t9Xte8Q7w.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/958599363857/1*ZnfYoNsMDNVNt1-pDg-jaQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<h4 id="鯛魚御神籤">鯛魚御神籤</h4>

<p><img src="/assets/958599363857/1*QJZOlovCi13dIiAQuwobDw.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*0R_KFD3lJnrkfxQTxXt82w.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*U6w5eGph85nVYP5rAAR76w.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>有好幾個魚池可以自己釣鯛魚御神籤XD，我是釣紅色的一年安鯛。</p>

<p><img src="/assets/958599363857/1*FHiQfCIBVx-EY93PQKur-Q.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*rKug8yofs-uBooqelaIEMQ.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*wA2-rSUSEQ-5ad0L2A2RCw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>大吉！</p>

<p><img src="/assets/958599363857/1*wPAUnu2NWojn3xDCq2Bb4Q.webp" alt="" loading="lazy" decoding="async" width="1400" height="995" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9Ijk5NSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*W0t4XZQ14zTYCb6k-84daA.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>如果零錢不夠可以跟神社人員換錢、旁邊有志工可以免費幫忙英文解籤，如果抽到不好的籤就直接綁在神社就好(意即消災解厄)。</p>
<h4 id="1245-離開冰川神社">12:45 離開冰川神社</h4>

<p>拍拍照逛了一圈大約 12:45 離開冰川神社，準備搭車去小江戶街跟 吉伊卡哇 MOGUMOGU 本舗 川越店。</p>
<h4 id="西武--小江戶巡迴巴士">西武 — 小江戶巡迴巴士</h4>

<p><img src="/assets/958599363857/1*lbJXQmrjFElNnSoh_Rtc2g.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>在候車的時候看到一台很酷的觀光巴士，還有音樂 — 是 <a href="https://eaglebus.group/co-edo/zh/" target="_blank">西武運輸的小江戶巡迴巴士</a> ； <strong>不能使用東武的周遊券</strong> ，可以刷交通卡付費搭乘或購買西武鐵道發行的 「 <a href="https://eaglebus.group/co-edo/2025/06/18/ryde0624/" target="_blank">周遊卷</a> 」。</p>
<h4 id="1320-chiikawa-mogumogu-hompo-kawagoe-吉伊卡哇-mogumogu-本舗-川越店">13:20 <a href="https://maps.app.goo.gl/ccQP8s28jfD9iVne6" target="_blank">Chiikawa Mogumogu Hompo Kawagoe 吉伊卡哇 MOGUMOGU 本舗 川越店</a></h4>

<p><img src="/assets/958599363857/1*cRVbxgypDnyUhkdduGQrGQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="993" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9Ijk5MyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*s1n-pLkMRKHB9LSTegvrsw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>門口有很多可愛的寶寶，川越限定的地瓜系列。</p>

<p><img src="/assets/958599363857/1*f1PB498M6RshUWre7oxG9w.webp" alt="" loading="lazy" decoding="async" width="1400" height="1935" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE5MzUiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>雖然還是實行全天人數管制，但人潮沒有剛開幕的時候多了，我們是到現場才掃 QRCode 直接預約下一個時段 13:40 (還有空位)；如果怕撲空可以提早在線上預約。</p>

<blockquote>
  <p><em>吉伊卡哇 MOGUMOGU 本舗 川越店 預約、預先抽 <a href="https://reurl.cc/ekrR5x" target="_blank">整理券網址(點我)</a> 。</em></p>
</blockquote>

<blockquote>
  <p><em>請注意：一隻手機一個帳號就是一個人，一天只能預約一次、一個時段</em></p>
</blockquote>

<p><strong>13:40 準時回到店門口排隊入場</strong></p>

<p><img src="/assets/958599363857/1*NtS_w3kiOP3Z_zdVg45duQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*vEMmOWjXOAfNvjloEDclgA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>商品貨量都很充足，部分商品有限制每人最多購買數量，消費可以加價扭扭蛋。</p>

<p><strong>我的戰利品：</strong></p>

<p><img src="/assets/958599363857/1*E4oKILRhstaUJTIRX-912Q.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*Ctanxv2irMlgQ0HSQnwMNQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<blockquote>
  <p><em>地瓜兔兔是垂耳的，超級可愛！</em></p>
</blockquote>

<blockquote>
  <p><em>商品列表跟價格可 <a href="https://chiikawamogumogu.shop/collections/nuigurumimasukoxtuto" target="_blank">參考官網</a> 。</em></p>
</blockquote>

<blockquote>
  <p><em>大地瓜兔兔：¥2,970 日幣 / 小地瓜吉伊卡哇：¥1,870 日幣</em></p>
</blockquote>

<p><strong>其他還有 Miffy 米飛兔跟 Snoopy 專賣店</strong></p>

<p><img src="/assets/958599363857/1*8_7sCUHDuYn4_vMIl54Rcg.webp" alt="" loading="lazy" decoding="async" width="1200" height="846" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg0NiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*O9rii8mVXEZKpzy1wGqsuA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<h4 id="在路上伴手禮店買了川越仙貝跟黑糖地瓜條">在路上伴手禮店買了川越仙貝跟黑糖地瓜條</h4>

<p><img src="/assets/958599363857/1*-l022L-y7yY7hAeZpzwOVg.webp" alt="" loading="lazy" decoding="async" width="1200" height="535" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjUzNSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<blockquote>
  <p><em>海鮮的好吃，原味的很硬，黑糖地瓜條就是台灣的黑糖地瓜片餅乾只是是條狀。</em></p>
</blockquote>

<p>逛完時間接近 14:00，實在太餓太累了。</p>
<h4 id="旧第八十五銀行本店本館"><a href="https://maps.app.goo.gl/y9z4S4ApsRjsREY37" target="_blank">旧第八十五銀行本店本館</a></h4>

<p><img src="/assets/958599363857/1*BpgkEdsDYYvqfpgVuZBKgQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>走到一個古蹟建築的二樓咖啡廳隨便吃個東西再出發。</p>

<p><img src="/assets/958599363857/1*QcyUQuLZg3kb-TuYqJzERg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1000" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwMDAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<blockquote>
  <p><em>如果不是因為太餓跟時間晚，應該會想去排蕎麥麵。</em></p>
</blockquote>

<p>吃完走到外面露台拍照：</p>

<p><img src="/assets/958599363857/1*ndOuiIyVVmYi3e5gCyr2Fw.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><strong>這裏可以鳥瞰一番街商店，很美：</strong></p>

<p><img src="/assets/958599363857/1*4Q0Q_xZ45C5_xCsYbZp-Wg.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<blockquote>
  <p><em>露台這邊是公共區域，不需要消費就可以進入。</em></p>
</blockquote>

<h4 id="1530-時之鐘">15:30 <a href="https://maps.app.goo.gl/5zsx2vLHKZvrKmp3A" target="_blank">時之鐘</a></h4>

<p>繼續漫步小江戶商店街到時之鐘。</p>

<p><img src="/assets/958599363857/1*gqfXKCkDR8r32kbh52lX3w.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*uPy0lTzewXYdF-GwaOSgrw.webp" alt="" loading="lazy" decoding="async" width="959" height="1225" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NTkiIGhlaWdodD0iMTIyNSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>買了旁邊攤販的章魚燒仙貝當點心。</p>
<h4 id="大正浪漫夢通-商店街"><a href="https://maps.app.goo.gl/nyidMKyz3L48WvNA7" target="_blank">大正浪漫夢通</a> (商店街)</h4>

<p><img src="/assets/958599363857/1*SVz_ZtnmAp_L_yK-DFRJSQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*gfbr5A94--q0WEdldobPsg.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>最後再一路經過大正浪漫通，搭公車回川越站，準備回池袋。</p>
<h4 id="1625-回到川越站搭-東武東上線-回-池袋">16:25 回到川越站，搭 東武東上線 回 池袋</h4>

<p><img src="/assets/958599363857/1*geGRZVP6HHBY9TKrjrUSqQ.webp" alt="" loading="lazy" decoding="async" width="1200" height="843" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg0MyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<h4 id="1710-池袋站-parco">17:10 池袋站 Parco</h4>

<p><img src="/assets/958599363857/1*w50-WxlZgKpVvH5aIQR7BQ.webp" alt="" loading="lazy" decoding="async" width="1200" height="844" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg0NCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>回池袋後先在池袋站逛逛百貨公司，這邊有 Parco、東武百貨，很多東西可以逛。</p>
<h4 id="吉伊卡哇咖啡廳the-guest-cafe--diner-ikebukuro--吉伊卡哇拉麵chikawa-ramen-pork"><a href="https://maps.app.goo.gl/tGxXYhytPWoMXzY6A" target="_blank">吉伊卡哇咖啡廳(THE GUEST cafe &amp; diner Ikebukuro)</a> 、 <a href="https://maps.app.goo.gl/Xjj1pvnPpyyvUGH48" target="_blank">吉伊卡哇拉麵(Chikawa Ramen Pork)</a></h4>

<p><img src="/assets/958599363857/1*DrWhK8JSk5XfcqqefpV_jA.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*cbhgmbw3iv1dQ6rB6c0VPA.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>這裡樓上有兩間吉伊卡哇餐廳，咖啡廳路過看預約全滿、拉麵是現場排隊就能入場。</p>

<p><strong>排邊還有一間周邊轉賣店，可以買餐廳系列吉伊卡哇:</strong></p>

<p><img src="/assets/958599363857/1*AsMwLutWXcmRiXED4rVSuA.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/958599363857/1*cOPdUeltj43X_CKspzfxCg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<h4 id="1745-牛たん焼き-仙台辺見-池袋パルコ店">17:45 <a href="https://maps.app.goo.gl/PEMEHM7uMc8K5i6c8" target="_blank">牛たん焼き 仙台辺見 池袋パルコ店</a></h4>

<p><img src="/assets/958599363857/1*AInBZ1mRnIZ3T2US8N7r2Q.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>接近晚餐時間，想說先在百貨公司吃一吃，最後直接在 Parco 選擇吃牛舌。</p>

<blockquote>
  <p><a href="https://www.n-rs.co.jp/brand/menu/en/henmi_menu_1565.pdf" target="_blank"><em>線上菜單：點此查看</em></a></p>
</blockquote>

<h4 id="1800-抽抽-隔天的-chiikawa-tokyo-station-吉伊卡哇東京車站-整理券想看寶寶-baby-系列">18:00 抽抽 隔天的 Chiikawa TOKYO Station 吉伊卡哇東京車站 整理券(想看寶寶 baby 系列)</h4>

<blockquote>
  <p><a href="https://chiikawa-info.jp/chiikawaland/tokyo/" target="_blank"><em>整理券網址：點此</em></a></p>
</blockquote>

<p><img src="/assets/958599363857/1*tEZULMHIAdtguLKCJzaZDw.webp" alt="" loading="lazy" decoding="async" width="602" height="1306" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2MDIiIGhlaWdodD0iMTMwNiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>Loading 轉完….沒抽到QQ，非常熱門。</p>

<p><strong>抽完開始用餐：</strong></p>

<p><img src="/assets/958599363857/1*NKh1itXkvzs63EAiPtCZ6Q.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<blockquote>
  <p><em>這家店應該 <a href="https://zhgchg.li/posts/z-%E5%BA%A6%E6%97%85%E8%A1%8C%E9%81%8A%E8%A8%98/%E5%B1%B1%E9%99%B0%E9%97%9C%E8%A5%BF%E8%87%AA%E7%94%B1%E8%A1%8C%E6%94%BB%E7%95%A5-%E5%B3%B6%E6%A0%B9%E5%87%BA%E9%9B%B2%E6%9D%BE%E6%B1%9F%E9%B3%A5%E5%8F%96%E5%A7%AC%E8%B7%AF%E5%A4%A7%E9%98%AA%E7%A5%9E%E6%88%B67%E6%97%A5%E7%8D%A8%E6%97%85%E5%85%A8%E8%A8%98%E9%8C%84-aacd5f5cacd1/#%E8%A6%93%E9%A3%9F" target="_blank">曾經在別的地方外帶吃過</a> ，牛舌口感跟味道都很好，湯有點像牛肉湯很好喝！</em></p>
</blockquote>

<h4 id="1900-池袋逛街">19:00 池袋逛街</h4>

<p>吃飽繼續逛街。</p>

<p><img src="/assets/958599363857/1*VJ9rPJWTSgULeGzvtPEMFg.webp" alt="" loading="lazy" decoding="async" width="939" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MzkiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*BeovdjRzqdZdRxU1086meg.webp" alt="" loading="lazy" decoding="async" width="942" height="1273" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NDIiIGhlaWdodD0iMTI3MyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*XAtHfdnJdmXLQGFJxxyHaA.webp" alt="" loading="lazy" decoding="async" width="929" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MjkiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>池袋這邊也很熱鬧，人很多店也很多。</p>
<h4 id="chiikawa-park"><a href="https://maps.app.goo.gl/H9nivHDc5i8ySG18A" target="_blank">Chiikawa Park</a></h4>

<p><img src="/assets/958599363857/1*15SBRn2tB_oNRXAztKzWdQ.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>Chiikawa Park 也在這邊，不過時間晚已經關門了。</p>
<h4 id="1915-太陽城--gashapon-ikebukuro-main-store-扭蛋百貨公司--世界最大扭蛋店3000台">19:15 <a href="https://maps.app.goo.gl/sMrThaudF2VwjBGP8" target="_blank">太陽城</a> — <a href="https://maps.app.goo.gl/6JgukBwhaJmmoAFQ7" target="_blank">Gashapon Ikebukuro Main Store 扭蛋百貨公司 — 世界最大扭蛋店(3000+台)</a></h4>

<p><img src="/assets/958599363857/1*HFUXnWbMb_i-gSmgTzNdZQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1006" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwMDYiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>一路走到世界最大扭蛋店，尋覓戰利品。</p>

<blockquote>
  <p><em>最後沒扭什麼，這邊機台雖然多但是沒看到什麼特別的而且分類蠻亂的逛得眼花撩亂。</em></p>
</blockquote>

<h4 id="2010-回池袋站--回飯店休息">20:10 回池袋站 — 回飯店休息</h4>

<p><img src="/assets/958599363857/1*N6AmcHvBHP0LeQswh9iXSQ.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><strong>本日宵夜：</strong></p>

<p><img src="/assets/958599363857/1*pjTtrIIs2qmtQmlun7-P1A.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>經典豆皮泡麵＋超商炸雞＋酒。</p>
<h3 id="day-30915-一--東京車站一番街熱海煙花大會">Day 3(09/15 一) — 東京車站一番街、熱海煙花大會</h3>

<p>預計搭乘下午 14:57 的新幹線前往熱海。</p>

<p><strong>此行的重點行程 — 熱海花火大會</strong> ；一早睡到飽才出門。</p>
<h4 id="1110-東京車站">11:10 東京車站</h4>

<p><img src="/assets/958599363857/1*pohebZUwh5jI-z7iyBsUfQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1003" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwMDMiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>接近中午，先在車站內覓食。</p>

<p><img src="/assets/958599363857/1*lUVuLvW3e19wluc4X3LupA.webp" alt="" loading="lazy" decoding="async" width="1200" height="865" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg2NSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><strong>東京車站也有一家 <a href="https://maps.app.goo.gl/vkmPHf4j6SgRGprb8" target="_blank">極味屋</a></strong> ，不過人超多，11:00 才開始營業就很多人在排隊了，目測應該要排個一小時左右。</p>

<p>轉頭去吃對面的神戶牛。</p>
<h4 id="kobebeef-daia-tokyo-station-store--神戸牛ダイア-東京駅店"><a href="https://maps.app.goo.gl/K1grYFDpH52WxMkdA" target="_blank">Kobebeef Daia tokyo station store — 神戸牛ダイア 東京駅店</a></h4>

<p><img src="/assets/958599363857/1*67WiU_TZIuxie7OBx31Z6w.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*ybIjFMLqTTSnvUne_N2qwA.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<blockquote>
  <p><em>懷念的好滋味， <a href="https://zhgchg.li/posts/z-%E5%BA%A6%E6%97%85%E8%A1%8C%E9%81%8A%E8%A8%98/%E5%B1%B1%E9%99%B0%E9%97%9C%E8%A5%BF%E8%87%AA%E7%94%B1%E8%A1%8C%E6%94%BB%E7%95%A5-%E5%B3%B6%E6%A0%B9%E5%87%BA%E9%9B%B2%E6%9D%BE%E6%B1%9F%E9%B3%A5%E5%8F%96%E5%A7%AC%E8%B7%AF%E5%A4%A7%E9%98%AA%E7%A5%9E%E6%88%B67%E6%97%A5%E7%8D%A8%E6%97%85%E5%85%A8%E8%A8%98%E9%8C%84-aacd5f5cacd1/#1700-%E7%A5%9E%E6%88%B8%E7%89%9B-%E5%90%89%E7%A5%A5%E5%90%89" target="_blank">上次去神戶也特別去吃神戶牛</a> (吉祥吉)，價格上東京大約比神戶產地約貴 ¥8,000 日幣 (Prime Kobe beef 沙朗 220g 東京 ¥39,160 / 神戶 ¥31,600)。</em></p>
</blockquote>

<p><img src="/assets/958599363857/1*d8dRk0mNMIBZ2WRlRvxdpQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>跟懷念中的滋味一樣，肉很嫩、沒有異味只有牛肉該有的香氣；不知不覺一下就吃完了！</p>
<h4 id="1220-開逛東京車站地下一番街">12:20 開逛東京車站地下一番街</h4>

<p><img src="/assets/958599363857/1*L0hqu-EQj9j98YtOCGqfWw.webp" alt="" loading="lazy" decoding="async" width="931" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MzEiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*8RfVcL7vNuaLnQ8RTqx77Q.webp" alt="" loading="lazy" decoding="async" width="1400" height="995" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9Ijk5NSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>地下街人很多，什麼 IP 幾乎都有，三麗鷗、航海王、Jump Store、拉拉熊、橡子共和國、Snoopy、Chiikawa..等等</p>
<h4 id="chiikawa-baby-tokyto-station"><a href="https://maps.app.goo.gl/ujTHi4i1YM2Jhq7u6" target="_blank">Chiikawa baby Tokyto station</a></h4>

<p><img src="/assets/958599363857/1*5FO6T-tcqFfmD6qWrHfhTw.webp" alt="" loading="lazy" decoding="async" width="954" height="1201" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NTQiIGhlaWdodD0iMTIwMSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*c2ParIuNtMQ0WReypFW_AQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*14U3tJHvWtVDyF7A2XM3Gg.webp" alt="" loading="lazy" decoding="async" width="1200" height="853" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg1MyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>沒抽到 <a href="https://chiikawa-info.jp/chiikawaland/tokyo/" target="_blank">整理券</a> ，只能路過拍拍照，貨量看起來很充足、人很少(感覺又控制太少人了？)</p>

<p><img src="/assets/958599363857/1*RhUU4XWAV_mo818lqez6qw.webp" alt="" loading="lazy" decoding="async" width="1200" height="843" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg0MyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>逛不夠還可以去隔壁大丸百貨或繼續走到其他周圍地下街。</p>
<h4 id="1400-東京車站退稅寄物">14:00 東京車站退稅、寄物</h4>

<p><img src="/assets/958599363857/1*V1V-6jaK-XgaIJtFUqaUTQ.webp" alt="" loading="lazy" decoding="async" width="1200" height="857" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg1NyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>在東京車站購物要退稅可以直接回到一樓，找到東海道山陽新幹線日本橋口對面的退稅櫃檯退稅。</p>

<p><img src="/assets/958599363857/1*Y0o7PYsSkdhHB0F-pdG6jQ.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>買了一些東西，因為寄物櫃臺只開到晚上 20:00，所以找置物櫃寄物，這邊也有很多置物櫃可以使用。</p>

<blockquote>
  <p><em>寄物完又去逛美食街買了一些吃的準備在路上吃。</em></p>
</blockquote>

<h4 id="1430-準備搭車前往熱海">14:30 準備搭車前往熱海</h4>

<p><img src="/assets/958599363857/1*PgBfGRbUfgWsyh1i24C8Mw.webp" alt="" loading="lazy" decoding="async" width="947" height="1057" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NDciIGhlaWdodD0iMTA1NyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>ㄧ樣是在東海道山陽新幹線日本橋口進站。</p>

<p><img src="/assets/958599363857/1*0PMsHuVFQWeAGUgHwpO7pA.webp" alt="" loading="lazy" decoding="async" width="950" height="1160" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NTAiIGhlaWdodD0iMTE2MCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*OKIhEwPPdSa-tDFMyiHC_A.webp" alt="" loading="lazy" decoding="async" width="854" height="1262" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4NTQiIGhlaWdodD0iMTI2MiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>在 <a href="https://www.kkday.com/zh-tw/transportation/japan-rail?cid=19365" target="_blank">KKday</a> / <a href="https://affiliate.klook.com/redirect?aid=99683&amp;aff_adid=1134759&amp;k_site=https%3A%2F%2Fwww.klook.com%2Fzh-TW%2Fjapan-rail%2F" target="_blank">Klook</a> 預先購買的電子車票，不用再去換票，直接走藍色閘門把手機 QRCode 緊貼感應區域即可進站。</p>

<p><img src="/assets/958599363857/1*8XhMmWSYamkoIpZcrcexiw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>進站成功會吐出車票(純方便看資訊或車上遇到驗票用)，出入站還是要用手機 QRCode。</p>

<p><img src="/assets/958599363857/1*VjszDJiQ3FvhmPZ6lezlaQ.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<h4 id="1457-東京出發前往熱海">14:57 東京出發前往熱海</h4>

<p><img src="/assets/958599363857/1*LtxEb2m13uqztgio8zrwrA.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*zEZdPMxvFTM1ZdGGClg_SQ.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*lMSwYOhx4zEClwuaRJqpnw.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>小吃小憩一下。</p>
<h4 id="1542-抵達熱海">15:42 抵達熱海</h4>

<p><img src="/assets/958599363857/1*6snvQ0gvBAze0Z4Poh7ywg.webp" alt="" loading="lazy" decoding="async" width="1200" height="868" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*bMpYH8-Dvf2iqufVYC1w0w.webp" alt="" loading="lazy" decoding="async" width="954" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NTQiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*ECAXngp1TSBZehMFgTfruw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>歡迎蒞臨 2025 熱海海上花火大會！</p>

<blockquote>
  <p><em>官方溫馨提醒：花火大會結束會很多人，請提前購買新幹線車票。</em></p>
</blockquote>

<p>新幹線出站後還要再通過在來線閘口(給工作人員看車票 QRCode 即可出來)。</p>

<p><img src="/assets/958599363857/1*MzafXGx-5W__v6ER9L55qg.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>天氣陰陰，但優點是不會太曬！</p>

<p><img src="/assets/958599363857/1*sfEvxO-r_6o1zXerVBmmxw.webp" alt="光榮的燒肉之路" loading="lazy" decoding="async" width="1200" height="675" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjY3NSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>光榮的燒肉之路</p>

<p><img src="/assets/958599363857/1*7-Gke4s_SdS3Ak7FshhbYw.webp" alt="" loading="lazy" decoding="async" width="948" height="1208" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NDgiIGhlaWdodD0iMTIwOCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*klSuH4wuVQev1vAE8uhrzg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/958599363857/1*NoN8BV-7HvjIqgoMvrNo_Q.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>穿過商店街出來一路往下走就會到海灘了。</p>

<p><img src="/assets/958599363857/1*xsPseN91hTY7i51fJPjcTA.webp" alt="" loading="lazy" decoding="async" width="963" height="1126" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NjMiIGhlaWdodD0iMTEyNiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><a href="https://maps.app.goo.gl/xZ6QHf3UAWu4iXtD8" target="_blank">路線如上圖</a> ，先走去 Lawson 買一些吃的喝的再去海灘野餐＋等煙火。</p>

<p><img src="/assets/958599363857/1*x0YEr4v70zdBu1cMZyq7zw.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*OSrew6n7pXgiQMkmz6TFrw.webp" alt="" loading="lazy" decoding="async" width="1200" height="880" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg4MCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>煙火大約在上圖位置釋放，可以走到 <a href="https://maps.app.goo.gl/XZmFfi3wDBJFERyQ8" target="_blank">Higashikaigancho</a> 靠後面的位置沙灘等待，這裡海灘也能下去玩水、附近也有共用廁所、衛浴可以換洗。(但建議先查一下，有些設備感覺年久失修了)</p>

<p><img src="/assets/958599363857/1*93jLDQa0cb1jewFvsazD9Q.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>熱海沙灘還能遠眺熱海城。</p>
<h4 id="1700-熱海陽光沙灘">17:00 熱海陽光沙灘</h4>

<p><img src="/assets/958599363857/1*MaQ6luHYefGS_EdREO8Z8A.webp" alt="" loading="lazy" decoding="async" width="675" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2NzUiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>感覺不用這麼早到熱海，人沒有想像中多；大概 18:30 之前來都還有沙灘位子，即使是到 20:20 煙火釋放，要來看都還有位。</p>

<p><img src="/assets/958599363857/1*7TaD649tgt3KS0hfcDNuzg.webp" alt="" loading="lazy" decoding="async" width="1200" height="675" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjY3NSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<h4 id="1800-熱海煙火大會-海灘人潮">18:00 熱海煙火大會 海灘人潮</h4>
<ul>
  <li>KKday 推薦： <a href="https://www.kkday.com/zh-tw/product/163325-tokyo-atami-fireworks-festival-kamakura-tour-japan?cid=19365" target="_blank">限時特惠|熱海花火大會&amp;鎌倉一日遊or 箱根一日遊(可選乘船觀賞)|東京新宿出發</a></li>
</ul>

<p><img src="/assets/958599363857/1*_FH0OfAii22pyLOikGi2Tg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/958599363857/1*ET9HOKp_AUG6BXKz9YFRfQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>可以看到 18:00 還有很多位子，或是很多人是先來放野餐墊佔位就去周圍逛逛，快到煙火再回來。</p>

<p><strong>再往後面走一點 <a href="https://maps.app.goo.gl/fYK3FxCFxZ8oMzRj6" target="_blank">大約在 Sky Deck 的地方</a> ，有一個小夜市有賣吃的(CP值不高就是了)：</strong></p>

<p><img src="/assets/958599363857/1*ABMNXDPrn2rHZ_BT_q6piA.webp" alt="" loading="lazy" decoding="async" width="1200" height="841" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg0MSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>海灘周圍也都有廁所，不怕沒得上。</p>
<h4 id="2015-接近煙火釋放時間的海灘人潮">20:15 接近煙火釋放時間的海灘人潮</h4>

<p><img src="/assets/958599363857/1*MhEq7Oix6_1SHWcLHWXxLg.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*GltFsKbSIEPBGCU0tujfHQ.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>後面(步道)的人很多。</p>
<h4 id="2020-花火大會開始">20:20 花火大會開始</h4>

<p><img src="/assets/958599363857/1*tQkiAdu0D5xhrsRaSwx2ow.webp" alt="" loading="lazy" decoding="async" width="1200" height="675" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjY3NSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<iframe class="embed-video" loading="lazy" src="https://www.youtube.com/embed/mI8vaJUpAzY" title="2025/09/15 熱海煙花大會" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>

<p><img src="/assets/958599363857/1*qsOMKdPSeRp3svZjdxg0bw.webp" alt="" loading="lazy" decoding="async" width="1342" height="929" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMzQyIiBoZWlnaHQ9IjkyOSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>煙火有好幾 Part 一路放到 20:40 (20分鐘)，最喜歡的 Part 是一整面的壯麗煙火，可惜今天濕氣太重，到後面煙有點散不太開。</p>
<h4 id="2045-開始離場">20:45 開始離場</h4>

<p><img src="/assets/958599363857/1*Is19CY4ydHSl6o_koAwUxw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*-Op_qDpZVTPamDHpMjrLGA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>離場的人潮非常多，不過大家都很有秩序地慢慢往車站移動；來的時候是下坡，回去是上坡（累）。</p>
<h4 id="2105-回到熱海站">21:05 回到熱海站</h4>

<p><img src="/assets/958599363857/1*0YVBx1LVIM38oZnnFl-uHw.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*AAk3JHAyjLZtD47VozQxhQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="999" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9Ijk5OSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>回程人多大約要 20–25 分鐘才能走回熱海站，進站人數也超多；如果是搭在來線(當地鐵路)要排隊慢慢進站， <strong>如果是你已經買好新幹線車票可以直接走右邊人工通道穿過再來線上去新幹線搭乘。</strong></p>

<blockquote>
  <p><em>本來怕時間太趕，所以買 22:02 分熱海回東京的車票；看現場情況應該早一班的 21:26 時間也是綽綽有餘</em></p>
</blockquote>

<h4 id="2120-進站候車">21:20 進站候車</h4>

<p><img src="/assets/958599363857/1*FlXrgL64tIESh9YANzlCgA.webp" alt="" loading="lazy" decoding="async" width="1400" height="864" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9Ijg2NCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*P8UbBT810VfOu1d-FjmGfQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>新幹線這裡很空曠、冷氣很涼。</p>

<p><img src="/assets/958599363857/1*1yTYNHadfcjXGCssVCOYuQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*Xh7kHOo5wLCsZq0HsfcxjA.webp" alt="" loading="lazy" decoding="async" width="1200" height="882" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg4MiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>投了一罐蘋果汽水續命，遇到其他地區大雨影響，所有班次都誤點。</p>
<h4 id="2225-上車延誤近-20-分鐘">~=22:25 上車(延誤近 20 分鐘)</h4>

<p><img src="/assets/958599363857/1*ffvGyMO0-1naq-JFmy9kfw.webp" alt="" loading="lazy" decoding="async" width="941" height="1192" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NDEiIGhlaWdodD0iMTE5MiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<h4 id="-2310-抵達東京">~= 23:10 抵達東京</h4>

<p>轉搭山手線回新橋。</p>

<p><img src="/assets/958599363857/1*JUzg76hP_ZQMXHzkFKDmdA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<h4 id="-2340-回到飯店休息">~= 23:40 回到飯店休息</h4>

<p><img src="/assets/958599363857/1*xcMi52GR3TaBBuVYXjAVjg.webp" alt="" loading="lazy" decoding="async" width="1200" height="846" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg0NiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*TZma2hnWNg9KTatodIad5w.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>真是充實的一天！</p>
<h3 id="day-40916-二-麻布台-teamlab原宿逛街水獺咖啡廳">Day 4(09/16 二) —麻布台 Teamlab、原宿逛街、水獺咖啡廳</h3>

<p>睡飽搭公車出門。</p>
<h4 id="1010-抵達-麻布台之森">10:10 抵達 <a href="https://maps.app.goo.gl/PbjLmES23cBRVRwK6" target="_blank">麻布台之森</a></h4>

<p><img src="/assets/958599363857/1*Vxi-ROH7326xUK1Q_RaSSA.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>我以為麻布台之森只有 <a href="https://maps.app.goo.gl/yyYwwHoZbHBmysqN6" target="_blank">JP Tower</a> ，原來是一整片的碎片建築群，有很多百貨櫃位、吃的。</p>

<p><img src="/assets/958599363857/1*l04M339xYWoaX0St4PmTGA.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>先去咖啡廳吃早餐跟醒腦。</p>
<h4 id="1050-前往-teamlab-borderless-mori-building-digital-art-museum-azabudai-hills-garden-plaza-b-b1">10:50 <a href="https://maps.app.goo.gl/a1VeYdbgBhvPCPXC7" target="_blank">前往 teamLab Borderless: MORI Building DIGITAL ART MUSEUM (Azabudai Hills Garden Plaza B, B1)</a></h4>

<p><img src="/assets/958599363857/1*G2WDXXLUhC1r2oueJDxDcQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>裡面沒有地圖，如果想全部探索可以先在外面拿官方手冊。</p>

<p><img src="/assets/958599363857/1*GKfgsOIo0qXpIshU_0N3UA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*6BC5_ZTb1w6zcBWf5mNeng.webp" alt="" loading="lazy" decoding="async" width="1200" height="842" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg0MiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>右轉有免費置物區，置物完給工作人員驗票就能入場。</p>
<h4 id="1100-teamlab-borderless">11:00 <a href="https://maps.app.goo.gl/a1VeYdbgBhvPCPXC7" target="_blank">teamLab Borderless</a></h4>

<p><img src="/assets/958599363857/1*ZgsR6mKRK6TjNZkS22p_7A.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*UXRiHpWae9wvx-9kSnGZjQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/958599363857/1*w3vwhwr_94ICABHOe_z46Q.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/958599363857/1*nMWtkgSZwXV7BDd0PHLNig.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*kTlH8oGRnbrMmf3L_nWSrg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>teamlab 真的很讚，裡有各種很魔幻、有創意，讓人覺得不像是真實世界能有的場景，互動包括光影、鏡面、球體、柱狀、葉子、氣霧…等等，而且幾乎都是 360 度全場景，很有沈浸感。</p>

<blockquote>
  <p><em>沒有地圖，所有展場房間要自己探索，如果女生穿裙子怕走光也會在入口貼心提供圍裙可以使用。</em></p>
</blockquote>

<blockquote>
  <p><em>每個房間入場前幾乎都會有工作人員說明，幾乎都是請勿觸碰、小心走路。</em></p>
</blockquote>

<blockquote>
  <p><em>裡面有廁所但要自己找指標或問工作人員。</em></p>
</blockquote>

<p><img src="/assets/958599363857/1*rOQik1WqrSYIlOlNE8GCNg.webp" alt="" loading="lazy" decoding="async" width="665" height="1182" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2NjUiIGhlaWdodD0iMTE4MiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>拍照起來也很魔幻。</p>

<p><img src="/assets/958599363857/1*-QUcAm5LHPzXTaNqzEe-2g.webp" alt="To have seen something, is to not have seen something else" loading="lazy" decoding="async" width="1400" height="601" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjYwMSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>To have seen something, is to not have seen something else</p>

<blockquote>
  <p><em>大約逛到 12:15 才離場，但不能保證都有逛到；要不是肚子餓要在裡面玩了兩三個小時應該不是問題。</em></p>
</blockquote>

<h4 id="harbs-麻布台之丘店"><a href="https://maps.app.goo.gl/GYvhb3ucV6Xykrtr9" target="_blank">HARBS 麻布台之丘店</a></h4>

<p><img src="/assets/958599363857/1*H3hzyw1S7R8QJqGClj7-Kg.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>teamlab 逛完出來就有一家 HARBS，先吃個水果千層蛋糕墊墊胃。</p>

<blockquote>
  <p><a href="https://www.harbs.co.jp/zh-TW/allmenu/flag/" target="_blank"><em>線上菜單：點我查看。</em></a></p>
</blockquote>

<p><img src="/assets/958599363857/1*KofxiaB6kF2_MBL4_xSMJA.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>招牌水果千層蛋糕(Mille Crepes/ミルクレープ) 1カット ￥1,050 ＋ 低消一杯飲料。</p>

<blockquote>
  <p><em>裡面有哈密瓜(好甜好好吃)+香蕉+奇異果。</em></p>
</blockquote>

<h4 id="1320-東京鐵塔">13:20 東京鐵塔</h4>

<p><img src="/assets/958599363857/1*bxUphy3A0fYyf5koQkTQNw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><a href="https://zhgchg.li/posts/z-%E5%BA%A6%E6%97%85%E8%A1%8C%E9%81%8A%E8%A8%98/%E6%9D%B1%E4%BA%AC%E8%87%AA%E7%94%B1%E8%A1%8C%E6%94%BB%E7%95%A5-5-%E5%A4%A9%E9%A3%9F%E4%BD%8F%E8%A1%8C%E5%85%A8%E8%A8%98%E9%8C%84%E8%88%87%E5%BF%85%E8%A8%AA%E6%99%AF%E9%BB%9E%E6%8E%A8%E8%96%A6-9da2c51fa4f2/#%E6%9D%B1%E4%BA%AC%E9%90%B5%E5%A1%94" target="_blank">上次有來過還有上去 <strong>(請參考之前的遊記)</strong></a> ，這次只有路過拍拍照就走了。</p>

<p>搭車前往原宿。</p>
<h4 id="1400-原宿">14:00 原宿</h4>

<p><img src="/assets/958599363857/1*6SJEx_1Xe1cRtxNtxCACUw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>一出來又又又看到吉伊卡哇， <a href="https://maps.app.goo.gl/Qo9uS7tVHoRda1MQ7" target="_blank">東急Plaza表參道原宿</a> 樓上有 <a href="https://maps.app.goo.gl/7rPeXTvCVEtWonMd7" target="_blank">吉伊卡哇麵包店</a> 。</p>
<h4 id="伊卡哇麵包店-chiikawa-bakery"><a href="https://maps.app.goo.gl/7rPeXTvCVEtWonMd7" target="_blank">伊卡哇麵包店</a> Chiikawa Bakery</h4>

<p><img src="/assets/958599363857/1*z3t6dt_iKNpI2quFtdIylg.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*AZPe4DkecEm5YCnjvnokvA.webp" alt="" loading="lazy" decoding="async" width="879" height="1157" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4NzkiIGhlaWdodD0iMTE1NyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>左邊真的吃的麵包、果醬店需要抽整理券入場；右邊周邊娃娃店則不用。</p>

<p>(因為只是剛好路過就不進去了)</p>

<blockquote>
  <p><a href="https://airwait.jp/WCSP/storeDetail?storeNo=AKR3416883372&amp;langType=KeyTW" target="_blank"><em>點此線上排隊，領取號碼牌</em></a></p>
</blockquote>

<p><img src="/assets/958599363857/1*W-7h4iIZMQRL6yk5WL7IQQ.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>吃的，麵包店的部分。</p>

<p><img src="/assets/958599363857/1*zPGCxhXve2PB8gO21lFyLg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/958599363857/1*AQGlxwc5OIDgwn8dJ62YRw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*BAveEbCyszfKzZc3L7CE9w.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>周邊商店這邊貨源蠻充足的，不過我已經了地瓜不能再買了，只有看看沒買。</p>
<h4 id="kiddy-land-キデイランド-原宿店"><a href="https://maps.app.goo.gl/JBGBKiEBNdcycYPz8" target="_blank">Kiddy Land キデイランド 原宿店</a></h4>

<p>出來之後又去附近另一家 Kiddy Land 逛逛。( <a href="https://www.kiddyland.co.jp/harajuku/" target="_blank">但這家沒有吉伊卡哇</a> )</p>

<p><img src="/assets/958599363857/1*WTTkpsTLZ9_35_KVXD01wQ.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*KG4Y3IlpSLIcD-SOBxb4Ug.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>有很多 Snoopy、三麗鷗、寶可夢、拉拉熊…等等 IP</p>

<p>逛一逛時間接近下午 15:00 了，中午只吃了一塊水果千層，有點飢餓，隨便找了一家路過評價蠻高的美式漢堡店吃吃。</p>
<h4 id="1450-the-great-burger"><a href="https://maps.app.goo.gl/xgoLanLfSSYFC5jP9" target="_blank">14:50 The Great Burger</a></h4>

<p><img src="/assets/958599363857/1*CxJhjOkE7NSkV2wwiZWlWA.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/958599363857/1*0wzaWAsar2xUmujgMfHVUg.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>特色是和牛小漢堡。</p>

<p><img src="/assets/958599363857/1*Anb4bgPGloM3oNkV6btS-g.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/958599363857/1*2x_JAyGUZmJ58CSR30Rr7A.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>味道還不錯，只是吃太多會很膩。</p>
<h4 id="1540-原宿逛街">15:40 原宿逛街</h4>

<p>吃完繼續在原宿逛逛等傍晚(16:30)去預約的水獺咖啡廳。</p>

<p><img src="/assets/958599363857/1*sWQbbQUfLq3EWzMhJrvTng.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*XyVXantdzPWEZA0pYpTXKA.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/958599363857/1*EfEP8VdYHAbuyxi2C6EFSA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>隨便亂逛又走來上次就是在這家 Le Labo 買香水的店，2025 日本境內價格 50ml 香水：¥31,350 日幣，退稅完差不多 ¥28,500。</p>
<h4 id="1630-harry-harajuku-terrace-水獺咖啡廳">16:30 <a href="https://maps.app.goo.gl/WHyJ88K5hRVc4vnk9" target="_blank">HARRY HARAJUKU terrace 水獺咖啡廳</a></h4>

<p><img src="/assets/958599363857/1*b20FSUf7uuIrFFNRLP6vwQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>在這棟建築的頂樓，走旁邊的樓梯上去。</p>

<p><img src="/assets/958599363857/1*eoDe1nHReBDY4bv070eEBQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/958599363857/1*x3phhvrHJ3ocidaw3glJLw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*CpEDCXiZ8Js_st4tzrXtGA.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>場地不大，外面有三個水池有三隻水獺在裡面趴趴走，可以買零食餵他(¥880)。 (會伸手手)</p>

<p><img src="/assets/958599363857/1*U30ypE-233jdjvcR0aejGA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>咖啡就是販賣機自己按的咖啡。(買 30 分鐘附贈 1 杯)</p>

<p><img src="/assets/958599363857/1*fBpOYC967t_nalgHfuGCEg.webp" alt="" loading="lazy" decoding="async" width="1200" height="872" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg3MiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*3dL7ITdswBmvR6KUU9MQSw.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>裡面的話場地也不大，中間一曲水獺付費互動區(要加價)、旁邊是龍貓互動區(要加價)，另一邊是免費的刺蝟互動區，跟幾隻在柵欄裡的水獺。</p>

<p><img src="/assets/958599363857/1*L1tbx1lOqgPbpgBmEhGkVA.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>我們加了 ¥880/人 跟水獺親密互動 5 分鐘，不然其實都摸不到。</p>
<h4 id="1720-摸摸水獺">17:20 <a href="https://maps.app.goo.gl/WHyJ88K5hRVc4vnk9" target="_blank">摸摸水獺</a></h4>

<blockquote>
  <p><em>跟水獺互動的遊客蠻多的，到 17:20 才輪到我們，店員會計時。</em></p>
</blockquote>

<p><img src="/assets/958599363857/1*BNyIXI7P-rccj0dy7yeXeg.webp" alt="" loading="lazy" decoding="async" width="1200" height="704" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjcwNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<iframe class="embed-video" loading="lazy" src="https://www.youtube.com/embed/-h9OupA3kCY" title="HARRY HARAJUKU terrace 水獺咖啡廳 ＨＡＲＲＹ原宿テラス店" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>

<p>可以趁他吃東西翻肚肚的時候摸摸，毛很柔順。</p>

<blockquote>
  <p><strong><em>整體來說：評價可能只有 2.5–3 顆星吧</em></strong></p>
</blockquote>

<blockquote>
  <p><em>整體場地太小，只付 ¥3,080 日幣/人一小時基本費除了刺蝟可以互動其他都沒有，咖啡也只是販賣機；雖然店員對動物都蠻溫柔的，但在一旁休息的其他水獺或露天水池的水獺可以活動的範圍都不大，覺得有點可憐。</em></p>
</blockquote>

<blockquote>
  <p><em>好像跟想像中正常咖啡廳門市的大小＋有水獺跑來跑去不太一樣。</em></p>
</blockquote>

<p><a href="https://kantenlife.tw/2024/04/18/house-of-otters-japan/#%E6%97%A5%E6%9C%AC%E6%B0%B4%E7%8D%BA%E5%92%96%E5%95%A1%E5%BB%B3%EF%BD%9C%E3%82%AB%E3%83%AF%E3%82%A6%E3%82%BD_%E8%8B%A5%E6%9E%97%E3%81%AE%E5%AE%B6%E3%80%81HARRY_HARAJUKU_terrace%E6%AF%94%E8%BC%83%E5%88%86%E4%BA%AB" target="_blank"><strong>後來查資料，感覺另一家 カワウソ_若林の家 比較好，互動時間較長。</strong></a></p>
<h4 id="1800-前往澀谷">18:00 前往澀谷</h4>

<p><img src="/assets/958599363857/1*Do_SdWdOCG8ZhXXDVcyEtg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<h4 id="1820-排-極味屋">18:20 排 <a href="https://maps.app.goo.gl/oZR5RprBErWbkAjq8" target="_blank">極味屋</a></h4>

<p>時間還很早，再次來到澀谷，先來吃極味屋。</p>

<p><img src="/assets/958599363857/1*bA7IPZZUq2LVdCbFs9H5lw.webp" alt="" loading="lazy" decoding="async" width="1200" height="861" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg2MSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>這時間來人沒有太多，稍微排隊一下就吃到了。</p>
<h4 id="1855-入座-極味屋">18:55 入座 <a href="https://maps.app.goo.gl/oZR5RprBErWbkAjq8" target="_blank">極味屋</a></h4>

<p><img src="/assets/958599363857/1*WRCSVqkiXQFWZcMWKs8GPw.webp" alt="" loading="lazy" decoding="async" width="711" height="1220" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3MTEiIGhlaWdodD0iMTIyMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*hb9il-B-7Hh_BJccl-OfyQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>中午吃太多漢堡排很膩，我是點左邊的純牛排套餐，juice 好吃。</p>

<blockquote>
  <p><a href="https://zhgchg.li/posts/z-%E5%BA%A6%E6%97%85%E8%A1%8C%E9%81%8A%E8%A8%98/%E6%9D%B1%E4%BA%AC%E8%87%AA%E7%94%B1%E8%A1%8C%E6%94%BB%E7%95%A5-5-%E5%A4%A9%E9%A3%9F%E4%BD%8F%E8%A1%8C%E5%85%A8%E8%A8%98%E9%8C%84%E8%88%87%E5%BF%85%E8%A8%AA%E6%99%AF%E9%BB%9E%E6%8E%A8%E8%96%A6-9da2c51fa4f2/#%E6%B8%8B%E8%B0%B7-parco--%E6%A5%B5%E5%91%B3%E5%B1%8B" target="_blank"><em>上次是吃漢堡排＋牛排套餐</em></a> <em>， <strong>一樣只要記得兩雙筷子，鐵筷負責煎、木筷負責吃。</strong></em></p>
</blockquote>

<p><strong>招牌 — 伊萬里牛排＋極味屋漢堡牛肉價格：</strong></p>
<ul>
  <li>Small (120g): ¥2,480 日幣</li>
  <li>Medium (160g): ¥2,850 日幣</li>
  <li>Large (200g): ¥3,180 日幣</li>
</ul>

<p><strong>純伊萬里牛排價格：</strong></p>
<ul>
  <li>Medium (130g): ¥2,480 日幣</li>
  <li>Large (180g): ¥3,280 日幣</li>
</ul>

<p><strong>升級套餐：</strong> ¥450 日幣</p>
<ul>
  <li>多湯、沙拉、冰淇淋</li>
  <li>個人覺得普普</li>
</ul>

<blockquote>
  <p><em>價格非常實惠。</em></p>
</blockquote>

<h4 id="逛街採購藥妝">逛街、採購藥妝</h4>

<p><img src="/assets/958599363857/1*mOCaZv_M3wUL_shhpsklRw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>在百貨公司逛到香氛，買了兩瓶跟枕頭噴霧回去試試。</p>

<p><img src="/assets/958599363857/1*hHziMmw8-TXZgTU96AaLAQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>吃完在澀谷又逛了一下，然後回飯店錢去藥妝店買個藥妝，回飯店休息，準備要跟東京說再見了。</p>

<p><img src="/assets/958599363857/1*3F0Blw-rzDL6OCLtVJQJtA.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*p8osyPhc0M_e21JVJLnmbQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>吃太多肉今晚就吃甜點就好吧！</p>

<blockquote>
  <p><em>還有同事在 <a href="https://maps.app.goo.gl/ptoRy58awP8gVibR6" target="_blank">原宿 I’m donut? Harajuku</a> 買來請我們的甜甜圈。(台灣排爆日本不用排)</em></p>
</blockquote>

<h3 id="day-50917-三-上野站回程">Day 5(09/17 三) —上野站、回程</h3>
<h4 id="1050-最後退房時間前出門">10:50 最後退房時間前出門</h4>

<p><img src="/assets/958599363857/1*_fnEVTQzT63qUc3tmEtY5A.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>再會新橋。</p>

<p><img src="/assets/958599363857/1*9iw9U6X2Rih37WcuoITOPw.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>搭山手線到上野。</p>
<h4 id="1110-抵達上野站">11:10 抵達上野站</h4>

<p><img src="/assets/958599363857/1*O_gvBqPHvCHYXyWu3h2sjw.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>沒特別逛，怕找不到地方寄物，就在車站內的寄物櫃臺寄物了。</p>

<blockquote>
  <p><em>後來發現挺不划算的，因為要取物又要付錢進站，而且也貴；重點是不順路。</em></p>
</blockquote>

<blockquote>
  <p><strong><em>後來才知道 Skyliner 京成上野在 上野站 山下口 或 不忍改</em></strong> <em>出口那邊，走站內就會到，不用出站曬太陽(但是站內很容易迷路就是)；在這邊寄物要再走回來(約 15 分鐘)，那邊也有很多投幣寄物櫃。</em></p>
</blockquote>

<p><img src="/assets/958599363857/1*stvzWK4vvJ2IxguPptM3Bg.webp" alt="" loading="lazy" decoding="async" width="1400" height="980" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9Ijk4MCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>這邊走出來就是上野動物園，左轉， <strong>準備先去 Skyliner 京成上野 劃位。</strong></p>

<p><img src="/assets/958599363857/1*8sL69lXMh1Mzhs05TclHqQ.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*R8RkmEqHQLr_405KQIulcg.webp" alt="" loading="lazy" decoding="async" width="946" height="1205" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NDYiIGhlaWdodD0iMTIwNSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>終於看到 Skyliner 京成上野 站了。</p>

<p><img src="/assets/958599363857/1*9_ErGzabreeyy-qw0P8zVQ.webp" alt="" loading="lazy" decoding="async" width="945" height="1198" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NDUiIGhlaWdodD0iMTE5OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<blockquote>
  <p><em>因為 Skyliner 全車指定席，所以建議還是提早劃位比較保險。</em></p>
</blockquote>

<blockquote>
  <p><em>站務員會講中文(只是我一開始沒聽出來他講中文就是了)。</em></p>
</blockquote>

<p>抓準最後的時間在上野逛逛。</p>
<h4 id="yodobashi-上野"><a href="https://maps.app.goo.gl/KfAz3oU7DBQpXrUc8" target="_blank">Yodobashi 上野</a></h4>

<p><img src="/assets/958599363857/1*dBKdSU8BqKOanuALBUEesQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*DDnDqOR4o8G6Jg60AospOg.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>旁邊還有一間二號店。</p>
<h4 id="一路走到-上野丸井百貨公司-逛街"><a href="https://maps.app.goo.gl/E3EfJWipQzSuC2Uv5" target="_blank">一路走到 上野丸井百貨公司 逛街</a></h4>

<p><img src="/assets/958599363857/1*O5JJZtfdU1OyJlD1_b8UPw.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/958599363857/1*jmnShS8jUrzCbG0vej3Ksw.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<blockquote>
  <p><em>對面是上野站另一個出口，上野站真的好大…</em></p>
</blockquote>

<p><img src="/assets/958599363857/1*Ziu-Golfdiu6XpDVxun10g.webp" alt="" loading="lazy" decoding="async" width="1200" height="1017" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjEwMTciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>地理位置大致如上圖。</p>
<h4 id="1200-逛-丸井百貨">12:00 逛 <a href="https://maps.app.goo.gl/DvPAhnaEz8HYGSQs7" target="_blank">丸井百貨</a></h4>

<p><img src="/assets/958599363857/1*GsKiVACufVHtpDYxncJ40A.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/958599363857/1*XoxAFbNqAcwJ8YDPjQlQxw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>二樓有一區扭蛋可以逛。</p>

<p><img src="/assets/958599363857/1*37rlYGSdZtckKzwgv9gLWw.webp" alt="" loading="lazy" decoding="async" width="495" height="491" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OTUiIGhlaWdodD0iNDkxIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<iframe class="embed-video" loading="lazy" src="https://www.youtube.com/embed/jyFapci9NxY" title="東海道新幹線 チケット音声キーホルダー 新幹線 車票 廣播 扭蛋 Gotcha" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>

<p>扭了一個 <a href="https://www.so-ta.com/products_detail/capsuletoy/tokaido_shinkansen/" target="_blank">新幹線車票扭蛋</a> 。</p>

<p><img src="/assets/958599363857/1*rysyBldMgjEOuR__7hQuOg.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>也被推坑在 Loft 買了一個 ReFa 心型按摩梳。</p>

<p><img src="/assets/958599363857/1*sGjazwQmnuqRKWbTubIZXA.webp" alt="" loading="lazy" decoding="async" width="1200" height="857" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg1NyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>在這裏吃了麵包可頌果腹。</p>
<h4 id="1320-回到上野車站">13:20 回到上野車站</h4>

<p><img src="/assets/958599363857/1*Ht4JmIU70nKQWu560yEM2A.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>順便先去買 Sugar Butter Tree 當伴手禮。</p>

<p>最後回到公園口提領了行李走了一大圈才又回到京成上野。</p>

<p><img src="/assets/958599363857/1*oR_x1HqE9qwg094sF-uygA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*maLcXgZQHSKZdJB2CQngZg.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>路線有點奇妙，需要先下到月台再從另一個出口上去，出來才會是不忍改口。</p>

<p><img src="/assets/958599363857/1*XLFR3n_fxsGWBK6sU5Lkhw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>這個地下道有很多投幣式置物櫃，而且很空。</p>

<p><img src="/assets/958599363857/1*TyOvYS_0FCfSujRBojgRvg.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>從不忍口出來過馬路就是 Skyliner 京成上野了。(不確定有沒有其他更近的出口，怕迷路就沒亂走了)</p>
<h4 id="1410-回到-skyliner-京成上野-準備搭車前往-成田機場">14:10 回到 Skyliner 京成上野 準備搭車前往 成田機場</h4>

<p><img src="/assets/958599363857/1*j-oOGCBRflbJWVfkQLrA_Q.webp" alt="" loading="lazy" decoding="async" width="1200" height="873" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg3MyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<h4 id="1420-出發前往-成田機場">14:20 出發前往 成田機場</h4>

<p><img src="/assets/958599363857/1*vV-Yxbd2JQ9acITed5qGpw.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*-bEzz-shmaBClAhFkmjf8A.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>剛在路上又去超商買了一些吃的喝的補充體力。</p>
<h4 id="1503-抵達-成田機場-第二航廈">15:03 抵達 成田機場 第二航廈</h4>

<p><img src="/assets/958599363857/1*6thsMc0VyilD-DBmtf85Aw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*jaAOzbQTRmETdiie_Vi7Zg.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*lpge7uQIA81oEOD9JjK3pQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>出來一路往上就會到國際線出發了。</p>

<blockquote>
  <p><em>本來有打算去一航吃 <a href="https://matcha-jp.com/tw/25632" target="_blank">新開幕的 Shake Shack 漢堡</a> ，不過實在太累了懶得去。</em></p>
</blockquote>

<h4 id="1540-完成托運">15:40 完成托運</h4>

<p><img src="/assets/958599363857/1*qtNeXNg2HuWhOQBd0YqnyQ.webp" alt="" loading="lazy" decoding="async" width="946" height="1207" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NDYiIGhlaWdodD0iMTIwNyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<blockquote>
  <p><a href="https://digital.jal.co.jp/ssci/identification?lang=zh-tw" target="_blank"><em>起飛前 24 小時可先完成網路報到、劃位，到機場就可以直接排行李托運，加快流程。</em></a></p>
</blockquote>

<p>托運完就直接去排出境了(累)。</p>
<h4 id="1600-完成安檢出境">16:00 完成安檢、出境</h4>

<p><img src="/assets/958599363857/1*41hw6bw7XnkIpESE-es7iw.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*XksioltS7RRgmtf4Gz-N3A.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>往數字越來越小的方向走就會到中間連接大廳，吃的買的都在這。</p>
<h4 id="先去-japan-food-hall-b1-吃個東西">先去 JAPAN FOOD HALL B1 吃個東西</h4>

<p><img src="/assets/958599363857/1*xj-6KVdDgwx_GPgGF0bHqQ.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<h4 id="1625-吃吃">16:25 吃吃</h4>

<p><img src="/assets/958599363857/1*Khh3IUrvCCYk1Ccx_fiF7w.webp" alt="" loading="lazy" decoding="async" width="1200" height="842" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg0MiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*bgttA6-L8uW93zM7TpwOmw.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<blockquote>
  <p><em>點了一份天婦羅炸蝦蕎麥麵加生啤。</em></p>
</blockquote>

<blockquote>
  <p><em>實刷 $955 台幣 (機場就是貴啊…)</em></p>
</blockquote>

<h4 id="1650-免稅店伴手禮店逛街">16:50 免稅店、伴手禮店逛街</h4>

<p><img src="/assets/958599363857/1*39UCCUtGuFBVV1FEBQG-9A.webp" alt="" loading="lazy" decoding="async" width="1400" height="921" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjkyMSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*UrCaiyBHekSvy0tzTbM5Gw.webp" alt="" loading="lazy" decoding="async" width="1200" height="848" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg0OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*_ID9YTxBia4tz-CaQrGcEw.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/958599363857/1*DubJNBeqI-tWI5amx_VbbQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>成田機場免稅店真的什麼都有，除了精品保養品菸酒外，也有很玩整的伴手禮店，什麼有，而且價格其實跟外面差不多或更便宜；沒買夠的可以把握最後登機前採買。</p>
<h4 id="1705-le-labo">17:05 Le Labo</h4>

<p><img src="/assets/958599363857/1*J2hChay4G6BPHCItaueHFA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>其實沒特別找，意外發現成田機場有 La Lebo 在 <a href="https://www.fasola.jp/en/searchByBrand.aspx" target="_blank">Fa-So-La DUTY FREE Cosmetics &amp; Perfumery</a> 看官網一二航廈都有。</p>

<p><img src="/assets/958599363857/1*9XCr-56Q5AlUCDRedP1cBA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<blockquote>
  <p><em>更意外的是價格， <strong>50ml — ¥25,700</strong> 比日本境內退完稅的價格 ¥28,500 還便宜。</em></p>
</blockquote>

<p><img src="/assets/958599363857/1*ar416ZWoAliERsR_Fg1GJQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>最後手癢買了一瓶 26–30ml (機場限定 Size)，實刷 $3,720 台幣。</p>
<h4 id="1730-前往登機門候機">17:30 前往登機門候機</h4>

<p><img src="/assets/958599363857/1*5PiE4fENJsi1dynUUL08OQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>登機門 67B 在很角落，很遠；遺憾是一路上的販賣機都沒看到賣桃子水。</p>

<p><img src="/assets/958599363857/1*K8V9RnmpGnx--VJ8HTZOaw.webp" alt="" loading="lazy" decoding="async" width="945" height="1202" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NDUiIGhlaWdodD0iMTIwMiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<h4 id="1750-登機東京再會">17:50 登機，東京再會！</h4>

<p><img src="/assets/958599363857/1*OxCGLuV_XrFB_c-BHM10gA.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/958599363857/1*0qZy2nb8aXRxB1fmR-uCUA.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>機型 BOEING 737–800 跟來的時候ㄧ樣。</p>
<h4 id="成田機場大塞機本來預計-1810-起飛最後排隊等到快-1900-才飛">成田機場大塞機，本來預計 18:10 起飛，最後排隊等到快 19:00 才飛</h4>

<p><img src="/assets/958599363857/1*LbcG5trqHcp_4GejuI6iDg.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<h4 id="日本航空免費機上-wifi">日本航空免費機上 WiFi</h4>

<p><img src="/assets/958599363857/1*1sP5H5jRMsQxm02W2TG6QA.webp" alt="" loading="lazy" decoding="async" width="602" height="1306" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2MDIiIGhlaWdodD0iMTMwNiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*eZ9beWgHlSQufFt-2Mnegg.webp" alt="" loading="lazy" decoding="async" width="602" height="1306" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2MDIiIGhlaWdodD0iMTMwNiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>無意間發現機上有提供免費一小時的 WiFi 上網。</p>

<p><img src="/assets/958599363857/1*jnrR9EmlSy0PcQkKe1eOGA.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*5CozWE6P0BhIjE6vksu9cQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>飛機餐一樣很優秀，是咖喱豬排飯跟甜點哈根達斯。</p>
<h4 id="2107-回到台灣">21:07 回到台灣</h4>
<h4 id="戰利品">戰利品</h4>

<p><img src="/assets/958599363857/1*ILt_i5ONuyBnDnJXMg0BTw.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/958599363857/1*SUJ70ie42fbD9Y5_LL86Sg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1271" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEyNzEiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<h4 id="感謝閱讀">感謝閱讀</h4>

<p>感謝您跟我一起體驗這次的東京五日遊，希望文中的資訊對您的下一次旅行有所幫助。</p>
<h4 id="更多遊記">更多遊記</h4>
<ul>
  <li><a href="/posts/z-度旅行遊記/釜山自由行攻略-8天7夜必去景點-美食與交通全解析-釜山通行證省錢秘訣-8ace34a1a3d8/">[遊記] 2025 韓國釜山 8 天 7 夜自由行</a></li>
  <li><a href="/posts/z-度旅行遊記/九州自由行攻略-釜山新山茶花號郵輪入境日本博多-深入由布院-大分-福岡等地-cb65fd5ab770/">[遊記] 2024 二訪九州 9 日自由行，經釜山→博多郵輪入境</a></li>
  <li><a href="/posts/z-度旅行遊記/山陰關西自由行攻略-島根出雲松江鳥取姬路大阪神戶7日獨旅全記錄-aacd5f5cacd1/">[遊記] 2024 山陰廣域地區 島根 出雲 松江 鳥取 姬路 大阪 神戶 7 日獨旅自由行</a></li>
  <li><a href="/posts/z-度旅行遊記/九州自由行攻略-福岡-長崎-熊本10日獨旅全紀錄與交通住宿實戰經驗-d78e0b15a08a/">[遊記] 2023 九州 10 日自由行獨旅</a></li>
  <li><a href="/posts/z-度旅行遊記/山陽地區廣島岡山自由行攻略-6日行程-交通-住宿全解析-31b9b3a63abc/">[遊記] 2023 廣島岡山 6 日自由行</a></li>
  <li><a href="/posts/z-度旅行遊記/名古屋一日快閃自由行-樂桃航空紅眼班機省錢攻略與行程規劃-7b8a0563c157/">[遊記] 9/11 名古屋一日快閃</a></li>
  <li>[遊記] <a href="/posts/z-度旅行遊記/東京自由行攻略-5-天食住行全記錄與必訪景點推薦-9da2c51fa4f2/">2023 東京 5 日自由行</a></li>
  <li>[遊記] <a href="/posts/z-度旅行遊記/京阪神自由行攻略-京都大阪神戶8日遊全紀錄與實用交通住宿指南-76d66c2e34af/">2023 京阪神 8 日自由行</a></li>
</ul>

<h3 id="kkday-推廣--1">KKday 推廣 🛒</h3>
<ul>
  <li><a href="https://www.kkday.com/zh-tw/product/9558-tokyo-private-day-tour-mt-fuji-hakone-and-downtown-tokyo-japan?cid=19365" target="_blank">【專享包車】 東京市區,富士山,箱根,鎌倉,伊豆,熱海,輕井澤,成田機場接送, 客製化包車一日遊</a></li>
  <li><a href="https://www.kkday.com/zh-tw/product/7913-keisei-skyliner-narita-airport-express-ticket?cid=19365" target="_blank">Skyliner京成電鐵車票</a></li>
  <li><a href="https://www.kkday.com/zh-tw/product/559745?cid=19365" target="_blank">日本東京包車一日遊|羽田・成田機場接送|東京往返富士山/箱根/東京市區/橫濱/鐮倉/白馬/輕井澤</a></li>
  <li><a href="https://www.kkday.com/zh-tw/product/137689-japan-unlimited-data-esim-card?cid=19365" target="_blank">【50% OFF】日本 eSIM|Docomo / KDDI / SoftBank / Rakuten|支援ChatGPT 及 Gemini</a></li>
  <li><a href="https://www.kkday.com/zh-tw/product/267443?cid=19365" target="_blank">【日本東京機場專車接送機】成田機場(NRT)・羽田機場(HND)↔東京23區・東京迪士尼樂園/迪士尼海洋・橫濱・箱根・伊豆/熱海・河口湖・鎌倉|包車配備專屬管家服務</a></li>
  <li><a href="https://www.kkday.com/zh-tw/product/573264?cid=19365" target="_blank">【日本東京接送機服務】成田機場 (NRT)・羽田機場 (HND)・東京23區・東京迪士尼・橫濱地區</a></li>
</ul>]]></content>
  </entry><entry>
    <title type="html">LINE Bank 入金 Firstrade 教學｜快速到帳、低手續費150元、帳戶升級與手續費補助申請全攻略</title>
    <link href="https://zhgchg.li/posts/zrealm-life/line-bank-%E5%85%A5%E9%87%91-firstrade-%E6%95%99%E5%AD%B8-%E5%BF%AB%E9%80%9F%E5%88%B0%E5%B8%B3-%E4%BD%8E%E6%89%8B%E7%BA%8C%E8%B2%BB150%E5%85%83-%E5%B8%B3%E6%88%B6%E5%8D%87%E7%B4%9A%E8%88%87%E6%89%8B%E7%BA%8C%E8%B2%BB%E8%A3%9C%E5%8A%A9%E7%94%B3%E8%AB%8B%E5%85%A8%E6%94%BB%E7%95%A5-e4d139fe0685/" rel="alternate" type="text/html" title="LINE Bank 入金 Firstrade 教學｜快速到帳、低手續費150元、帳戶升級與手續費補助申請全攻略" />
    <published>2025-09-02T15:09:54+08:00</published>
    <updated>2026-01-04T11:45:25+08:00</updated>
    <id>https://zhgchg.li/posts/zrealm-life/e4d139fe0685</id><summary type="html">投資美股遇到匯款繁瑣與高手續費問題？透過 LINE Bank 入金 Firstrade，享受手續費只要150元、快速到帳，搭配帳戶升級與約定轉帳設定，還能申請每月最高25美元手續費補助，讓海外匯款更省時省錢，輕鬆開啟美股投資之路。</summary><author>
      <name>ZhgChgLi</name>
    </author><category term="ZRealm Life." /><category term="line-bank" /><category term="firstrade" /><category term="nasdaq" /><category term="line" /><category term="入金" /><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://zhgchg.li/assets/e4d139fe0685/1*u5qqz4o0pCvUTF16sYHNCg.webp" /><content type="html" xml:base="https://zhgchg.li/posts/zrealm-life/line-bank-%E5%85%A5%E9%87%91-firstrade-%E6%95%99%E5%AD%B8-%E5%BF%AB%E9%80%9F%E5%88%B0%E5%B8%B3-%E4%BD%8E%E6%89%8B%E7%BA%8C%E8%B2%BB150%E5%85%83-%E5%B8%B3%E6%88%B6%E5%8D%87%E7%B4%9A%E8%88%87%E6%89%8B%E7%BA%8C%E8%B2%BB%E8%A3%9C%E5%8A%A9%E7%94%B3%E8%AB%8B%E5%85%A8%E6%94%BB%E7%95%A5-e4d139fe0685/"><![CDATA[<h3 id="手把手教學--line-bank-入金-firstrade-快速到帳手續費只要-150-元及帳戶升級約定轉帳手續費補助申請教學">手把手教學 — LINE Bank 入金 Firstrade 快速到帳、手續費只要 150 元及帳戶升級/約定轉帳/手續費補助申請教學</h3>

<p>Line Bank 入金 Firstrade 及帳戶升級約定與手續費補助完整圖文教學</p>
<h3 id="line-bank-的優勢">LINE Bank 的優勢</h3>
<ul>
  <li>開戶簡單：有其他銀行聯徵紀錄只需網路填寫資料就能開戶</li>
  <li>跨行轉帳免手續費：最高 50次/月</li>
  <li><strong>無痛整合 Line 轉帳：</strong> 好友之間轉帳不用在透過 iPASS MONEY</li>
  <li>24 小時都能換匯</li>
  <li>多種彈性高利率存款方案可自行搭配選擇</li>
  <li>信貸：快速/整合/10年輕鬆付</li>
  <li>證券：100元起投資台股</li>
</ul>

<h4 id="line-bank-美國匯款優惠--匯出手續費150元匯入手續費50元"><a href="https://www.linebank.com.tw/wealth-investment/promotions/10" target="_blank">Line Bank 美國匯款優惠 — 匯出手續費150元、匯入手續費50元</a></h4>

<p><strong>[2026/01/01 加碼再次延長]:</strong></p>

<blockquote>
  <p><em>活動期間：原活動期間為2026/1/1~2026/6/30</em></p>
</blockquote>

<blockquote>
  <p><em>活動內容：外幣匯出手續費優惠價每筆NT$150元(原價NT$600元)、外幣匯入手續費優惠價每筆NT$50元（原價NT$400元）</em></p>
</blockquote>

<blockquote>
  <p><em>活動產品：外幣匯出匯款、外幣匯入匯款</em></p>
</blockquote>

<p>這是我們使用 LINE Bank 最大的誘因，到 2026/06/30 之前用 LINE Bank 帳戶入金 Firstrade 只收取 150 元手續費，不用電報費或其他費用。</p>

<blockquote>
  <p><em>Memo: 這活動從 2025/01–06 就開始，目前已好評延長兩次 (2025/06 延長至 2025/12、2025/12 再次延長至 2026/06)， <strong>敬請把時間！</strong></em></p>
</blockquote>

<h4 id="點我前往申辦"><a href="https://www.linebank.com.tw/R/mgm-portal?campaignId=2&amp;uid=bfQqph" target="_blank">點我前往申辦</a></h4>

<p><img src="/assets/e4d139fe0685/1*u5qqz4o0pCvUTF16sYHNCg.webp" alt="點我前往申辦" loading="lazy" decoding="async" width="1200" height="632" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjYzMiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><a href="https://www.linebank.com.tw/R/mgm-portal?campaignId=2&amp;uid=bfQqph" target="_blank">點我前往申辦</a></p>
<h4 id="缺點">缺點</h4>

<p>這次遇到要設定約定帳戶，因為 LINE Bank 沒有實體銀行，只能透過自然人憑證認證；所以變成要跑一趟戶政事務所申辦自然人憑證才能使用，比直接去銀行還麻煩。</p>
<h3 id="firstrade-的優勢">Firstrade 的優勢</h3>
<ul>
  <li>開戶、介面、功能簡單，美股小白友善</li>
  <li>支援中文介面</li>
  <li>交易 0 手續費</li>
  <li>老牌券商</li>
</ul>

<blockquote>
  <p><em>開戶姓名請務必使用護照名。</em></p>
</blockquote>

<h4 id="入金-10000-美元以上可以申請最高為-25-美元手續費補助"><a href="https://www.firstrade.com/zh-TW/accounts/wire-fee-rebate" target="_blank">入金 $10,000 美元以上，可以申請最高為 $25 美元手續費補助</a></h4>

<p><img src="/assets/e4d139fe0685/1*-4JN2hY_QP3oZaon2BES0A.webp" alt="入金 $10,000 美元以上，可以申請最高為 $25 美元手續費補助 (每月最多三次)" loading="lazy" decoding="async" width="464" height="441" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0NjQiIGhlaWdodD0iNDQxIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><a href="https://www.firstrade.com/zh-TW/accounts/wire-fee-rebate" target="_blank">入金 $10,000 美元以上，可以申請最高為 $25 美元手續費補助</a> (每月最多三次)</p>

<blockquote>
  <p>帳戶一切準備就緒之後我們就能開始入金囉。</p>
</blockquote>

<h3 id="入金花費時間軸參考最快-2-天入金到帳">入金花費時間軸參考(最快 2 天入金到帳)</h3>

<p><img src="/assets/e4d139fe0685/1*mhmGifQYaJacIOXwPqIC_A.webp" alt="這邊以我 10/14 入金到 Firstrade 到最後拿到匯款補貼的時間軸。" loading="lazy" decoding="async" width="1200" height="657" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjY1NyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>這邊以我 10/14 入金到 Firstrade 到最後拿到匯款補貼的時間軸。</p>
<ul>
  <li>請注意以上是建立在約定帳戶都已綁定完成或是金額小於 5 萬的情況，第一次綁定約定帳戶要等 +2 天才會生效。</li>
  <li>Line Bank 顯示受款銀行已收到 到 Firstrade 真的收到錢會有時間差。(應該是要等 Firstrade 營業時間處理完才會顯示已收到)</li>
  <li>Firstrade 收到錢要申請匯款補貼(單筆超過一萬鎂)也會有時間差，沒看到紀錄的話可以稍後或隔天再看。</li>
</ul>

<h3 id="line-bank-入金-firstrade">LINE Bank 入金 Firstrade</h3>

<p>首先來介紹 Line Bank 如何入金到 Firstrade。</p>
<h4 id="將您的資產存入第一證券"><a href="https://invest.firstrade.com/cgi-bin/main#/content/customerservice/fundaccount/" target="_blank">將您的資產存入第一證券</a></h4>

<p><img src="/assets/e4d139fe0685/1*QEJlVLLykQrqTB2UACx95Q.webp" alt="" loading="lazy" decoding="async" width="1015" height="506" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDE1IiBoZWlnaHQ9IjUwNiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>(請使用電腦版) 登入 Firstrade 後選擇：</p>
<ol>
  <li>客戶服務</li>
  <li>賬戶注資</li>
</ol>

<blockquote>
  <p><em>如果頁面是英文可以在上方選擇「繁體中文」更改語言。</em></p>
</blockquote>

<h4 id="取得匯款訊息"><a href="https://invest.firstrade.com/scripts/fundmgt/wire_instruction/" target="_blank">取得匯款訊息</a></h4>

<p><img src="/assets/e4d139fe0685/1*66aA2JSjEWOLHdj4P6JwVA.webp" alt="" loading="lazy" decoding="async" width="1000" height="426" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDAwIiBoZWlnaHQ9IjQyNiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>選擇「匯款」 → 「匯款信息」，會開啟一個 PDF 檔案，可以把這個檔案儲存下來方便之後入金填寫：</p>

<p><img src="/assets/e4d139fe0685/1*11qj5ItxI9rxNrOqCVBw8g.webp" alt="" loading="lazy" decoding="async" width="781" height="1109" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3ODEiIGhlaWdodD0iMTEwOSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<ol>
  <li>受款銀行資訊 — SWIFT 代碼</li>
  <li>受款人戶名</li>
  <li>受款人英文地址</li>
  <li>受款人帳號</li>
  <li><strong>附言</strong></li>
</ol>

<h4 id="line-bank-app--匯款至海外">LINE Bank App — 匯款至海外</h4>

<p><img src="/assets/e4d139fe0685/1*8zCXdiJWyPNCFW_NuIKNhQ.webp" alt="" loading="lazy" decoding="async" width="683" height="749" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2ODMiIGhlaWdodD0iNzQ5Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<ol>
  <li>點擊「更多」</li>
  <li>下滑找到「外匯」區塊 → 點擊「匯款至海外」</li>
  <li>扣款金額選擇「台幣 or 美元」、輸入要匯款的金額
<strong>請先參考下方 Line Bank 帳戶升級與約定帳戶綁定教學。</strong> 
<strong>請先參考下方 Line Bank 帳戶升級與約定帳戶綁定教學。</strong> 
<strong>請先參考下方 Line Bank 帳戶升級與約定帳戶綁定教學。</strong> 
<strong>否則只能匯款 1 萬台幣。</strong></li>
  <li>點擊「前往匯款」</li>
</ol>

<h4 id="手續費比率-20260630-前均一價-150">手續費比率 (2026/06/30 前均一價 $150)</h4>
<ul>
  <li>匯款 NTD $10,000 / 手續費 NTD $150 = 1.5%</li>
  <li>匯款 NTD $50,000 / 手續費 NTD $150 = 0.3%</li>
  <li>匯款 NTD $300,000 / 手續費 NTD $150 = 0.05%</li>
  <li>匯款 NTD $500,000 / 手續費 NTD $150 = 0.03%</li>
</ul>

<blockquote>
  <p><em>一次匯款的金額越多，比率越低， <strong>Firstrade 單筆入金超過 $10,000 美金 (約 $310,000 台幣)，還可再申請最高 $25 美元、每月最多三次入金手續費補貼、需在匯款後 30 天內申請(參考文末教學)</strong> 。</em></p>
</blockquote>

<h4 id="輸入匯款資料">輸入匯款資料</h4>

<p><img src="/assets/e4d139fe0685/1*5Bc1Gogt-HJRVdh_ygOPbg.webp" alt="" loading="lazy" decoding="async" width="791" height="789" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3OTEiIGhlaWdodD0iNzg5Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><strong>輸入上一步 <a href="https://invest.firstrade.com/scripts/fundmgt/wire_instruction/" target="_blank">匯款訊息</a> 的資訊：</strong></p>
<ol>
  <li>受款人英文戶名： <code class="language-plaintext highlighter-rouge">Apex Clearing Corporation</code></li>
  <li>受款銀行資訊 — 直接填寫 SWIFT 代碼自動帶入</li>
  <li>輸入搜尋： <code class="language-plaintext highlighter-rouge">HATRUS44</code></li>
  <li>選擇第一個： <code class="language-plaintext highlighter-rouge">HATRUS44 — BMO Bank N.A CHICAGO</code></li>
  <li>受款人帳號： <code class="language-plaintext highlighter-rouge">1617711</code> <strong>(可能會變，請以實際資訊為主)</strong></li>
</ol>

<p><strong>往下滑繼續輸入：</strong></p>

<p><img src="/assets/e4d139fe0685/1*i2kQnOFRD-azviSj3fCfyA.webp" alt="" loading="lazy" decoding="async" width="358" height="762" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzNTgiIGhlaWdodD0iNzYyIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<ol>
  <li>受款人身份別： <code class="language-plaintext highlighter-rouge">民間(法人、個人)</code></li>
  <li>受款人英文地址 — 國家：選擇 <code class="language-plaintext highlighter-rouge">United States</code></li>
  <li>受款人英文地址 — 城市/區：填寫 <code class="language-plaintext highlighter-rouge">Texas</code></li>
  <li>受款人英文地址 — 詳細地址： <code class="language-plaintext highlighter-rouge">One Dallas Center 350 N. St Paul, Suite 1300, Dallas, TX 75201</code> (參考上一步匯款資訊)</li>
  <li><strong>⭐️️️️️️ 最重要的附言，請務必填寫</strong> 
<strong>⭐️️️️️️ 最重要的附言，請務必填寫</strong> 
<strong>⭐️️️️️️ 最重要的附言，請務必填寫</strong> 
<strong>參考上一步匯款資訊輸入：</strong> <code class="language-plaintext highlighter-rouge">8 碼帳戶數字 + 賬戶姓名</code> 
如上圖所示： <code class="language-plaintext highlighter-rouge">12345678 ZXXXG CXXXG LI</code></li>
  <li>點擊「下一步」</li>
</ol>

<h4 id="匯款性質">匯款性質</h4>

<p><img src="/assets/e4d139fe0685/1*jrDAKxcP5jxjHQ41Wf5LWQ.webp" alt="" loading="lazy" decoding="async" width="841" height="794" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4NDEiIGhlaWdodD0iNzk0Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<ul>
  <li>選擇「262 投資國外股權證券」</li>
  <li>點擊「同意」申報注意事項</li>
</ul>

<h4 id="匯款確認">匯款確認</h4>

<p><img src="/assets/e4d139fe0685/1*yiYOU7hagogCvHF1AUxEHQ.webp" alt="" loading="lazy" decoding="async" width="743" height="686" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NDMiIGhlaWdodD0iNjg2Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>最後一部，確認一下資訊是否輸入正確，尤其是受款人帳戶資訊跟 <strong>附言部分</strong> 。</p>
<ul>
  <li>如果 60 秒內無法確認交易內容，匯率報價可能會進行更新。</li>
  <li>沒問題點擊「下一步」就 <strong>完成匯款申請了</strong></li>
</ul>

<h4 id="完成">完成</h4>

<p><img src="/assets/e4d139fe0685/1*jaADV3k1wdNZuz2zpogLQA.webp" alt="" loading="lazy" decoding="async" width="869" height="1884" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4NjkiIGhlaWdodD0iMTg4NCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>看到這畫面代表匯款申請已完成，請靜待到帳通知。</p>

<blockquote>
  <p><em>本文圖片中的匯款金額僅共參考。(有的圖是 5萬、有的是 10 萬)</em></p>
</blockquote>

<h3 id="line-bank-匯款入金-firstrade-進度查詢">LINE Bank 匯款入金 Firstrade 進度查詢</h3>

<p><img src="/assets/e4d139fe0685/1*nEUD0C8nOO1E2uS9p6u8rw.webp" alt="" loading="lazy" decoding="async" width="1114" height="774" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMTE0IiBoZWlnaHQ9Ijc3NCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>可以從 LINE Bank App 右上角通知找到，外幣匯出通知，點擊進入查看匯款進度。</p>
<ul>
  <li>非假日最快當天就匯款成功，假日就要等 2 天後上班日才會處理</li>
  <li><strong>如果資料有填錯受款單位會拒收，需要支付退匯手續費重新匯款</strong></li>
</ul>

<h4 id="申請匯款單交易紀錄水單">申請匯款單(交易紀錄/水單)</h4>

<p><img src="/assets/e4d139fe0685/1*EH8g25Pxoewchc5gt7qK_w.webp" alt="" loading="lazy" decoding="async" width="1179" height="2556" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMTc5IiBoZWlnaHQ9IjI1NTYiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/e4d139fe0685/1*DMrWpw9JdcFVOYuEWgNmkw.webp" alt="" loading="lazy" decoding="async" width="790" height="1031" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3OTAiIGhlaWdodD0iMTAzMSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>Line Bank App 進度查詢通知(如前文)，往下滑有一個「申請水單/交易憑證」按鈕，點擊後就會寄送匯款單到你的 Email，用身分證字號就能開啟檔案查看。</p>
<h4 id="受款行已收到款項但是-firstrade-沒看到資金">受款行已收到款項但是 Firstrade 沒看到資金？</h4>

<p>這是正常的，雖然在 Line Bank 上看到匯款狀態是「受款行已收到款項」匯款完成，但是 Firstrade 那邊還要再等美國上班日(時間)才會確認款項。</p>

<p>如上圖雖然 8/27 AM 09:00 就已收到款項，但是到 8/28 AM 02:10 Firstrade 才確認款項，並收到入帳通知信：</p>

<p><img src="/assets/e4d139fe0685/1*7g0qrnxcoE5aYYz0erJrpw.webp" alt="" loading="lazy" decoding="async" width="625" height="715" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2MjUiIGhlaWdodD0iNzE1Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>這時候再登入 Firstrade 就能看到資金進來囉，可以開始操作美股投資：</p>

<p><img src="/assets/e4d139fe0685/1*83_EXDsxQpwqgfmkHogYbw.webp" alt="" loading="lazy" decoding="async" width="980" height="704" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5ODAiIGhlaWdodD0iNzA0Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>「我的賬戶」 →「賬戶記錄」也會有存款紀錄可查詢。</p>
<h3 id="line-bank-帳戶升級綁定約定帳戶教學">Line Bank 帳戶升級、綁定約定帳戶教學</h3>

<p><img src="/assets/e4d139fe0685/1*ZbYDlcVZlCEE4fdKgeaWWQ.webp" alt="" loading="lazy" decoding="async" width="1200" height="410" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjQxMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><strong>權益比較：</strong></p>
<ul>
  <li>(開戶後即享) 基本帳戶：單筆最高 1 萬、每日最高 3 萬、每月最高 5 萬。</li>
  <li>(客服視訊後) 升級帳戶：單筆最高 5 萬、每日最高 10萬、每月最高 20 萬。</li>
  <li>(自然人憑證+客服視訊後) 約定帳戶：單筆最高 50 萬。</li>
</ul>

<h4 id="line-bank-帳戶升級">Line Bank 帳戶升級</h4>

<p>其實就是提升到其他銀行的基礎限額。 <strong>(這一步不用自然人憑證。)</strong></p>

<p><img src="/assets/e4d139fe0685/1*aFqGx67be74ZZr75wUvT-w.webp" alt="" loading="lazy" decoding="async" width="1117" height="779" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMTE3IiBoZWlnaHQ9Ijc3OSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>打開 LINE Bank App 在「更多」下滑找到「其他」 → 「帳戶升級」，選擇要升級的帳戶類型 → 選擇 <strong>2. 視訊驗證「開始驗證 &gt;」。</strong></p>
<ul>
  <li>請先確保網路正常穩定</li>
  <li>確保當前可以視訊通話</li>
</ul>

<p>點擊後就會播視訊電話給客服人員，會需要跟客服對話視訊，可能會再跟你確認一下帳戶資訊(例如：問你當前餘額?) 確認無誤之後；就完成帳戶升級囉。</p>
<h3 id="約定-firstrade-匯款帳戶">約定 Firstrade 匯款帳戶</h3>

<p>比較推薦的是直接約定 Firstrade 匯款帳戶為約定帳戶，方便快速又安全。</p>
<ul>
  <li>約定帳戶需要 2 天後才會生效</li>
  <li>約定帳戶需要先通過自然人憑證驗證</li>
  <li><strong>自然人憑證驗證只支援 TP07 開頭，裡面有 NFC 感應晶片的卡片</strong></li>
</ul>

<h4 id="自然人憑證申請">自然人憑證申請</h4>

<p>請先在線上 <a href="https://moica.nat.gov.tw/rac_form.html" target="_blank">完成填寫自然人申請表格</a> ：</p>

<p><img src="/assets/e4d139fe0685/1*4xi8pHeUNAPZKijWiXfj1Q.webp" alt="自然人申請表格" loading="lazy" decoding="async" width="727" height="1237" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3MjciIGhlaWdodD0iMTIzNyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><a href="https://moica.nat.gov.tw/rac_form.html" target="_blank">自然人申請表格</a></p>

<p>送出後，7 天內到任何地方戶政事務所(不用是戶籍地)櫃檯都能辦理。</p>
<ul>
  <li><strong>工本費： $250 元</strong></li>
  <li>現場辦理現場發放</li>
  <li>戶政事務所辦理自然人憑證的人不多、中午一般不休息</li>
</ul>

<p><img src="/assets/e4d139fe0685/1*zrmIss8K7vrn18f_CGffDg.webp" alt="" loading="lazy" decoding="async" width="1200" height="873" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg3MyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<h4 id="line-bank-新增約定-firstrade-海外匯款帳戶">Line Bank 新增約定 Firstrade 海外匯款帳戶</h4>

<p><img src="/assets/e4d139fe0685/1*Mi9tRTDMpubsVfWF6AXidg.webp" alt="" loading="lazy" decoding="async" width="1127" height="767" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMTI3IiBoZWlnaHQ9Ijc2NyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>打開 LINE Bank App →「更多」 → 下滑找到外匯，點擊「我的匯款紀錄」 → 「匯款約定帳號」 → 「新增約定帳號」。</p>
<h4 id="第一次使用--完成帳戶驗證">第一次使用 — 完成帳戶驗證</h4>

<p><img src="/assets/e4d139fe0685/1*nLPxsTJ1ALuv4hLZPcdqmw.webp" alt="" loading="lazy" decoding="async" width="1179" height="1199" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMTc5IiBoZWlnaHQ9IjExOTkiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>第一次綁定需要完成「自然人憑證驗證」與「視訊驗證」，視訊驗證同帳戶升級需要跟客服視訊完成帳戶資料驗證。</p>

<p><strong>自然人憑證則需要拿出我們剛申辦的自然人憑證卡靠卡完成驗證：</strong></p>

<p><img src="/assets/e4d139fe0685/1*-N0scmyLMlqgPHbuDpmD-Q.webp" alt="" loading="lazy" decoding="async" width="1200" height="579" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjU3OSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<blockquote>
  <p><em>自然人憑證的感應範圍有一點小，我重新試了好幾次才成功；iPhone 用鏡頭中間靠近卡片中間，卡片左右橫移，嘗試幾次就能驗證成功。</em></p>
</blockquote>

<h4 id="新增約定帳戶">新增約定帳戶</h4>

<p><img src="/assets/e4d139fe0685/1*RvHTvjTzjap4M09RG5Q8xQ.webp" alt="" loading="lazy" decoding="async" width="749" height="765" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NDkiIGhlaWdodD0iNzY1Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>帳戶資訊同前文介紹的匯款資訊，依序填入，確認無誤後點擊「下一步」。</p>

<p><img src="/assets/e4d139fe0685/1*2VbwWyl9zQCY9p2fVVoL2w.webp" alt="" loading="lazy" decoding="async" width="1143" height="786" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMTQzIiBoZWlnaHQ9Ijc4NiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>需要完成雙相簡訊驗證，跳到剛收到的簡訊輸入四位數驗證碼；等待約 1–2 分鐘，收到確認成功簡訊代表驗證完成。</p>

<p><img src="/assets/e4d139fe0685/1*WmXzGCmOWMkOxwMhYJ-mfQ.webp" alt="" loading="lazy" decoding="async" width="715" height="791" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3MTUiIGhlaWdodD0iNzkxIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>驗證成功後，約定帳戶就新增成功囉！</p>
<ul>
  <li>剛新增完，右上角會有一個驚嘆號，提示尚未生效。</li>
  <li><strong>需要 2 日後才會生效。</strong></li>
</ul>

<h4 id="line-bank-firstrade-約定帳戶匯款">Line Bank Firstrade 約定帳戶匯款</h4>

<p><img src="/assets/e4d139fe0685/1*TfcH3RFr1wR7XKjKjqnvJQ.webp" alt="" loading="lazy" decoding="async" width="740" height="766" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NDAiIGhlaWdodD0iNzY2Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>「我的匯款紀錄」 → 「匯款約定帳號」 → 選擇帳戶 → 輸入金額 → 選擇「以約定帳戶進行匯款」 → 資料會自動都帶好。</p>
<h3 id="firstrade-手續費補助申請教學">Firstrade 手續費補助申請教學</h3>
<ul>
  <li>匯款金額要求： <strong>單筆</strong> 大於 $10,000 美元</li>
  <li>補貼金額上限：$25 美元</li>
  <li>次數限制：每月最多 3 次</li>
  <li>申請時間：匯款後 30 天內提出申請</li>
</ul>

<p><img src="/assets/e4d139fe0685/1*TFU213ZsLmlrAt7Wj7Vmtw.webp" alt="" loading="lazy" decoding="async" width="1032" height="450" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDMyIiBoZWlnaHQ9IjQ1MCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/e4d139fe0685/1*JbqxnPqZkNsZuqvSGpkRqw.webp" alt="" loading="lazy" decoding="async" width="1025" height="695" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI1IiBoZWlnaHQ9IjY5NSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><a href="https://invest.firstrade.com/cgi-bin/main#/content/customerservice/promos/wirerebate/" target="_blank">登入 Firstrade 後選擇「客戶服務」 → 「特惠活動」→ 「匯費補貼」。</a></p>

<p><img src="/assets/e4d139fe0685/1*3UDeTN3dnh3fnM9HMdIurw.webp" alt="" loading="lazy" decoding="async" width="866" height="674" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4NjYiIGhlaWdodD0iNjc0Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>選擇匯款資料。</p>

<blockquote>
  <p><strong><em>如果沒出現匯款資料可選擇，請再等幾天匯款資料進來。(剛到帳不一定會馬上出現)</em></strong></p>
</blockquote>

<p><img src="/assets/e4d139fe0685/1*U598Ew1kBCBC4fxCjLVv_w.webp" alt="" loading="lazy" decoding="async" width="872" height="918" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4NzIiIGhlaWdodD0iOTE4Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><img src="/assets/e4d139fe0685/1*-KOs-E0FQ_Wy80GLl581OA.webp" alt="" loading="lazy" decoding="async" width="866" height="766" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4NjYiIGhlaWdodD0iNzY2Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><strong>選擇匯款紀錄資料：</strong></p>
<ul>
  <li>匯款金融機構名稱： <code class="language-plaintext highlighter-rouge">LINE Bank Taiwan Limited</code></li>
  <li>金融機構所收匯款費用： <code class="language-plaintext highlighter-rouge">25</code></li>
</ul>

<p>點擊「送出」 → 「確定」完成申請。</p>

<p><img src="/assets/e4d139fe0685/1*RYYu26VBBJhxT76Ht1toMQ.webp" alt="" loading="lazy" decoding="async" width="691" height="388" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2OTEiIGhlaWdodD0iMzg4Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>大約在 2–3 個工作天左右就能收到匯款補貼囉。</p>

<p><img src="/assets/e4d139fe0685/1*L-g13ZQ2uS2PqHRyomDD4g.webp" alt="" loading="lazy" decoding="async" width="976" height="124" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NzYiIGhlaWdodD0iMTI0Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>可以去「我的賬戶」 →「賬戶記錄」中查看匯費補貼記錄。</p>

<blockquote>
  <p><em>Firstrade 有點問題，大約會有一天的空檔期：申請狀態消失、賬戶紀錄沒看到補貼進來；大約再過了一天之後就出現入帳記錄了！</em></p>
</blockquote>

<blockquote>
  <p><strong><em>雖然 LINE Bank 只跟我們收 TWD $150，但實際匯費是超過 USD $25，所以還是補貼 USD $25。</em></strong></p>
</blockquote>

<h3 id="免責聲明">免責聲明</h3>

<p>本文僅為個人操作分享，不保證完全正確，一切使用請以實際為主，作者不為使用上的損失負任何責任。</p>
<h3 id="反詐專區">反詐專區</h3>
<h4 id="兩階段驗證">兩階段驗證</h4>

<p><img src="/assets/e4d139fe0685/1*jPfNxJGPBCY20do8y8Pz_g.webp" alt="" loading="lazy" decoding="async" width="1016" height="736" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDE2IiBoZWlnaHQ9IjczNiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>務必到「我的賬戶」 → 「設定」 → 啟用「身份驗證應用」。</p>

<p>這樣在陌生裝置登入都需要先完成兩階段驗證才能登入。</p>
<h4 id="信件與連結網域">信件與連結網域</h4>

<p><img src="/assets/e4d139fe0685/1*Rqr5E1ptXgjXh686EAr_UQ.webp" alt="" loading="lazy" decoding="async" width="922" height="646" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MjIiIGhlaWdodD0iNjQ2Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><img src="/assets/e4d139fe0685/1*R_zbtsoMNVK6ryeSjkMzIQ.webp" alt="" loading="lazy" decoding="async" width="564" height="56" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1NjQiIGhlaWdodD0iNTYiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>不管信件的樣式如何跟真的一樣，請注意寄件人是否為 <strong>firstrade.com</strong> 及不要點及信件中任何網址， <strong>若一定要點也請多確認網址是否為 firstrade.com。</strong></p>

<blockquote>
  <p><em>技術上 <strong>詐騙信件、假網站能做到一模ㄧ樣</strong> ，真假難辨；網址部分：</em></p>
</blockquote>

<blockquote>
  <p><strong><em>只要不是 firstrade.com 都是詐騙！都是詐騙！都是詐騙！</em></strong></p>
</blockquote>

<blockquote>
  <p><strong><em>只要不是 firstrade.com 都是詐騙！都是詐騙！都是詐騙！</em></strong></p>
</blockquote>

<blockquote>
  <p><strong><em>只要不是 firstrade.com 都是詐騙！都是詐騙！都是詐騙！</em></strong></p>
</blockquote>]]></content>
  </entry><entry>
    <title type="html">釜山自由行攻略｜8天7夜必去景點、美食與交通全解析｜釜山通行證省錢秘訣</title>
    <link href="https://zhgchg.li/posts/z-%E5%BA%A6%E6%97%85%E8%A1%8C%E9%81%8A%E8%A8%98/%E9%87%9C%E5%B1%B1%E8%87%AA%E7%94%B1%E8%A1%8C%E6%94%BB%E7%95%A5-8%E5%A4%A97%E5%A4%9C%E5%BF%85%E5%8E%BB%E6%99%AF%E9%BB%9E-%E7%BE%8E%E9%A3%9F%E8%88%87%E4%BA%A4%E9%80%9A%E5%85%A8%E8%A7%A3%E6%9E%90-%E9%87%9C%E5%B1%B1%E9%80%9A%E8%A1%8C%E8%AD%89%E7%9C%81%E9%8C%A2%E7%A7%98%E8%A8%A3-8ace34a1a3d8/" rel="alternate" type="text/html" title="釜山自由行攻略｜8天7夜必去景點、美食與交通全解析｜釜山通行證省錢秘訣" />
    <published>2025-08-13T12:38:54+08:00</published>
    <updated>2025-08-17T10:55:29+08:00</updated>
    <id>https://zhgchg.li/posts/z-%E5%BA%A6%E6%97%85%E8%A1%8C%E9%81%8A%E8%A8%98/8ace34a1a3d8</id><summary type="html">規劃釜山自由行？掌握8天7夜釜山必玩景點、熱門美食與交通卡使用技巧，搭配釜山通行證節省旅費，享受順暢交通與深度體驗，讓你的釜山行程更高效精彩。</summary><author>
      <name>ZhgChgLi</name>
    </author><category term="Z 度旅行遊記" /><category term="生活" /><category term="travel" /><category term="busan" /><category term="korea" /><category term="travel-writing" /><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://zhgchg.li/assets/8ace34a1a3d8/1*06AC2xvA-jb4pjaIQg6fbQ.webp" /><content type="html" xml:base="https://zhgchg.li/posts/z-%E5%BA%A6%E6%97%85%E8%A1%8C%E9%81%8A%E8%A8%98/%E9%87%9C%E5%B1%B1%E8%87%AA%E7%94%B1%E8%A1%8C%E6%94%BB%E7%95%A5-8%E5%A4%A97%E5%A4%9C%E5%BF%85%E5%8E%BB%E6%99%AF%E9%BB%9E-%E7%BE%8E%E9%A3%9F%E8%88%87%E4%BA%A4%E9%80%9A%E5%85%A8%E8%A7%A3%E6%9E%90-%E9%87%9C%E5%B1%B1%E9%80%9A%E8%A1%8C%E8%AD%89%E7%9C%81%E9%8C%A2%E7%A7%98%E8%A8%A3-8ace34a1a3d8/"><![CDATA[<h3 id="遊記-2025-韓國釜山-8-天-7-夜自由行">[遊記] 2025 韓國釜山 8 天 7 夜自由行</h3>

<p>從南到北，使用釜山通行證 VISIT BUSAN PASS，暢遊美景、品味美食，開啟專屬你的釜山之旅。</p>

<p><img src="/assets/8ace34a1a3d8/1*06AC2xvA-jb4pjaIQg6fbQ.webp" alt="廣安大橋" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>廣安大橋</p>
<h3 id="kkday-推廣-">KKday 推廣 🛒</h3>
<ul>
  <li><a href="https://www.kkday.com/zh-tw/product/138477-visit-busan-pass-discount-free-attractions?cid=19365" target="_blank">釜山通行證 VISIT BUSAN PASS</a></li>
  <li><a href="https://www.kkday.com/zh-tw/product/2930-korea-ktx-train-discounted-korail-day-pass?cid=19365" target="_blank">KR PASS 韓國鐵道周遊券</a></li>
  <li><a href="https://www.kkday.com/zh-tw/product/133154-south-korea-esim-sk-telecom-4g-lte-unlimited-data?cid=19365" target="_blank">【特別活動】韓國網卡|SK電信 4G 高速吃到飽 eSIM</a></li>
  <li><a href="https://www.kkday.com/zh-tw/product/534007?cid=19365" target="_blank">韓國釜山包車/釜山出發-慶州/金海/鎮海/浦項/密陽/安東/巨濟島/大邱/蔚山/包車一日遊/金海機場接送</a></li>
  <li><a href="https://www.kkday.com/zh-tw/product/134952?cid=19365" target="_blank">韓國網卡|5G上網 每日高速流量型 / 總量型 eSIM</a></li>
</ul>

<h4 id="背景">背景</h4>

<p>去年 <a href="/posts/z-度旅行遊記/九州自由行攻略-釜山新山茶花號郵輪入境日本博多-深入由布院-大分-福岡等地-cb65fd5ab770/"><strong>2024 年「二訪九州 9 日自由行，經釜山→博多郵輪入境」</strong></a> ，從韓國釜山入境再搭船前往日本福岡，那次短暫在釜山待了一個早上，只走馬看花去了海東龍宮寺跟海雲台而已；今年 7–8 月有一些工作空檔，因此再次出發、更深入的探索釜山，這次跟同樣是閒人的 Pinkoi 好同事 Sean 一同前往。</p>

<p>日期：2025/07/29–08/05</p>
<h3 id="tldr">TL;DR</h3>

<p>一樣把總結寫在前面「韓國釜山雖然沒有日本的寧靜跟秩序，但是有隨性跟人情味」。</p>

<p><strong>必去景點：</strong></p>
<ul>
  <li>[北] <a href="https://www.kkday.com/zh-tw/product/152412-gijang-skyline-luge-ticket-busan-south-korea?cid=19365" target="_blank">Skyline Luge 滑坡車</a></li>
  <li>[北] <a href="https://www.kkday.com/zh-tw/product/134684-yacht-holic-busan-yacht-public-tour-gwangan-ri-haeundae-south-korea?cid=19365" target="_blank">釜山遊艇之旅 Y Holic</a></li>
  <li>[北] <a href="https://www.kkday.com/zh-tw/product/123012-haeundae-blueline-park-sky-capsule-beach-train-ticket?cid=19365" target="_blank">膠囊列車</a></li>
  <li>[北] <a href="https://www.kkday.com/zh-tw/product/12213-busan-spa-land-centum-city-ticket?cid=19365" target="_blank">汗蒸幕</a></li>
  <li>[北] 海東龍宮寺</li>
  <li>[北] 新世界百貨</li>
  <li>[南] 影島大橋</li>
</ul>

<p><strong>必吃：</strong></p>
<ul>
  <li>[北] <a href="https://naver.me/GEiuqenG" target="_blank">烤鰻魚 — PUNGCHEONMAN</a></li>
  <li>[北] <a href="https://naver.me/x9zcuKNm" target="_blank">鹽麵包 — Jayeondo saltbread</a></li>
  <li>[南] <a href="https://naver.me/xHmnku7F" target="_blank">喉嚨鍋蓋烤肉 — Moggumung Nampo Branch</a></li>
  <li>[南] <a href="https://naver.me/GEiuXPsf" target="_blank">Milgot</a> 紅豆奶油艾草糯米糕</li>
</ul>

<p><strong>必備：</strong></p>
<ul>
  <li><a href="https://www.kkday.com/zh-tw/product/138477-visit-busan-pass-discount-free-attractions?cid=19365" target="_blank"><strong>韓國釜山通行證 VISIT BUSAN PASS</strong></a> <strong>(絕對要用，可以省很多錢)</strong></li>
  <li><a href="https://www.kkday.com/zh-tw/product/138477-visit-busan-pass-discount-free-attractions?cid=19365" target="_blank"><strong>韓國釜山通行證 VISIT BUSAN PASS</strong></a> <strong>(絕對要用，可以省很多錢)</strong></li>
  <li><a href="https://www.kkday.com/zh-tw/product/138477-visit-busan-pass-discount-free-attractions?cid=19365" target="_blank"><strong>韓國釜山通行證 VISIT BUSAN PASS</strong></a> <strong>(絕對要用，可以省很多錢)</strong></li>
</ul>

<blockquote>
  <p><em>這次沒吃到的東西：海鮮煎餅(錯過)、豬肉湯飯( <a href="/posts/z-度旅行遊記/九州自由行攻略-釜山新山茶花號郵輪入境日本博多-深入由布院-大分-福岡等地-cb65fd5ab770/">上次吃過</a> )、生章魚(不吃生的，看 ig 很多人吃完食物中毒)、醬蟹(不吃生的)</em></p>
</blockquote>

<h3 id="行前準備">行前準備</h3>
<h4 id="行">行</h4>
<h4 id="機票">機票</h4>

<p><img src="/assets/8ace34a1a3d8/1*UkWjvZsjTVT4z6IbvwBBFQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="634" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjYzNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>釜山航空 ( <strong>含托運 15KG</strong> )。</p>

<p>價格： <strong>NT$6,728 / 人</strong> 。</p>
<ul>
  <li>出發：07/29(二) 台北桃園(TPE) 16:25 -&gt; 釜山金海(PUS) 19:55</li>
  <li>回程：08/05(二) 釜山金海(PUS) 10:50 -&gt; 台北桃園(TPE) 12:35</li>
</ul>

<p>總天數 8 天，實際天數 6 天 (第一天跟最後一天基本上就是搭飛機而已)。</p>
<h4 id="交通卡">交通卡</h4>

<p><img src="/assets/8ace34a1a3d8/1*AnOi_kWaK4ET_udnUwB6nQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="702" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjcwMiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*TwuSm6wAS-J7_s6nLrnKLg.webp" alt="KKday 實體交通卡" loading="lazy" decoding="async" width="554" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1NTQiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><a href="https://www.kkday.com/zh-tw/product/18161-t-money-public-transit-card-pick-up-at-taiwan-taoyuan-international-airport-south-korea?cid=19365" target="_blank">KKday 實體交通卡</a></p>

<p>今年 iPhone 可以直接用 Apple 錢包交通卡裡的 Tmoney，可以直接儲值使用很方便；但是我已經 <a href="https://www.kkday.com/zh-tw/product/18161-t-money-public-transit-card-pick-up-at-taiwan-taoyuan-international-airport-south-korea?cid=19365" target="_blank">加值了去年買的實體卡</a> 所以就沒用了。</p>

<p>另外今年開始也能直接透過 <a href="https://wowpass.io/zh-TW" target="_blank">Wowpass App</a> 加值交通卡，流程是先用信用卡儲值 Wowpass 再從 Wowpass 入金到 Tmoney，流程較多而且需要支付手續費。</p>

<p><img src="/assets/8ace34a1a3d8/1*Sz8gh4I3s8sx5_0fsyT4oA.webp" alt="" loading="lazy" decoding="async" width="553" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1NTMiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*PzzH6uT3djtj-egP-1A3YA.webp" alt="" loading="lazy" decoding="async" width="602" height="1306" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2MDIiIGhlaWdodD0iMTMwNiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*C5s-bq9vOTiwDkIIbGHQig.webp" alt="" loading="lazy" decoding="async" width="553" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1NTMiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<blockquote>
  <p>不管是哪一種都建議行前先在台灣用信用卡(因為有可能只支援某些發卡機構)先加值好或是攜帶一些現金到韓國用機台加值。 <a href="https://map.naver.com/p/" target="_blank">Naver Map</a></p>
</blockquote>

<blockquote>
  <p><a href="https://map.naver.com/p/" target="_blank"><strong><em>韓國務必使用 Naver Map。</em></strong></a></p>
</blockquote>

<blockquote>
  <p><strong><em><a href="https://map.naver.com/p/" target="_blank">韓國務必使用 Naver Map。</a></em></strong></p>
</blockquote>

<blockquote>
  <p><strong><em><a href="https://map.naver.com/p/" target="_blank">韓國務必使用 Naver Map。</a></em></strong></p>
</blockquote>

<h4 id="公車規定">公車規定</h4>

<p><img src="/assets/8ace34a1a3d8/1*SdjC7IxAtx08bLLFngwnzA.webp" alt="" loading="lazy" decoding="async" width="928" height="720" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MjgiIGhlaWdodD0iNzIwIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<ul>
  <li><strong>釜山公車規定，大於 20 吋行李箱、手持手搖杯禁止搭乘(拿著不喝也不行，真的會攔)</strong></li>
  <li>公車一律 <strong>前門上車，後門下車</strong> ；前門刷卡處一般在司機座位側後方</li>
  <li><strong>另外商店也不收外來的垃圾，因此如果有自己調冰杯飲料最好在景點、百貨喝完丟掉再走，不然很麻煩。</strong></li>
  <li>兩人以上其實可以直接計程車搭到爽，釜山計程車很多又便宜又都很新很方便</li>
  <li>地鐵請確認方向後再進站</li>
</ul>

<h4 id="韓國釜山通行證-visit-busan-pass"><a href="https://www.kkday.com/zh-tw/product/138477-visit-busan-pass-discount-free-attractions?cid=19365" target="_blank">韓國釜山通行證 VISIT BUSAN PASS</a></h4>

<p>Busan Pass 有分限時(24/48 HR)無限量型跟限制次數型(180天內用完)。</p>

<p><img src="/assets/8ace34a1a3d8/1*87TkvJQSQbrD1GcZsN3i7w.webp" alt="" loading="lazy" decoding="async" width="1252" height="1124" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjUyIiBoZWlnaHQ9IjExMjQiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/8ace34a1a3d8/1*ujmSSMVX5IINmCmCknawlQ.webp" alt="KKday 2025/08 釜山 Pass 資訊" loading="lazy" decoding="async" width="1058" height="1240" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDU4IiBoZWlnaHQ9IjEyNDAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><a href="https://www.kkday.com/zh-tw/product/138477-visit-busan-pass-discount-free-attractions?cid=19365" target="_blank">KKday 2025/08 釜山 Pass 資訊</a></p>
<ul>
  <li>因為行程安排分散在這 8 天，加上想去的景點就那些，我們 <strong>選擇買兩張 BIG5。</strong></li>
  <li>價格： <strong>NT$1,380 *2 張 = NT$2,760</strong></li>
  <li>電子版直接出示訂單 QRCode 使用</li>
</ul>

<p><strong>實際使用狀況如下：</strong></p>

<p><img src="/assets/8ace34a1a3d8/1*ItIzgsZFoDEzwSLJK870Dw.webp" alt="" loading="lazy" decoding="async" width="1020" height="826" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDIwIiBoZWlnaHQ9IjgyNiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*Q1L0AgRhDM9Eh2WiByj06w.webp" alt="KKday 2025/08 釜山 Pass 資訊" loading="lazy" decoding="async" width="1000" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDAwIiBoZWlnaHQ9IjEyMDAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><a href="https://www.kkday.com/zh-tw/product/138477-visit-busan-pass-discount-free-attractions?cid=19365" target="_blank">KKday 2025/08 釜山 Pass 資訊</a></p>
<ul>
  <li>藍色少一次(太宗台沒去)</li>
  <li>海雲台海岸列車兩天各坐了一次</li>
</ul>

<h4 id="釜山遊艇之旅-y-holic-預約️">釜山遊艇之旅 Y Holic 預約⚠️</h4>

<p>使用釜山 Pass 欲搭乘釜山遊艇之旅，需要先跟船家預約，我們是選 Y Holic，以這家為例。</p>

<p><strong>買完 Pass 之後透過以下管道傳訊息給船家：</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>IG：@yachtholic_info (我們是透過 Line)
LINE：@yachtholic
Email：yh@yachtholic.com
WeChat：yachtholic
</code></pre></div></div>

<p>告知人數、日期、時段、Email 或聯繫方式跟 釜山 Pass 的 QR Code。</p>

<p>等待確認回覆，沒問題就預約成功囉！</p>
<ul>
  <li>夜間場次 (18:00) 開始 <strong>每個人需要另外收 $5,000 韓元</strong> ，可以現場給或是提前轉帳</li>
  <li><strong>需要提前 30 分鐘到場排隊確認身份</strong></li>
  <li>我們是約 18:30 看夕陽</li>
</ul>

<blockquote>
  <p><a href="https://www.kkday.com/zh-tw/product/134684-yacht-holic-busan-yacht-public-tour-gwangan-ri-haeundae-south-korea?cid=19365" target="_blank"><em>如果你不想買 Pass 也能直接預約購買遊艇行。</em></a></p>
</blockquote>

<h4 id="韓國釜山海雲台藍線公園海岸列車天空膠囊列車票"><a href="https://www.kkday.com/zh-tw/product/123012-haeundae-blueline-park-sky-capsule-beach-train-ticket?cid=19365" target="_blank">韓國釜山｜海雲台藍線公園海岸列車・天空膠囊列車票</a></h4>

<p>請注意： <strong>海岸列車跟膠囊列車不一樣</strong> 不要買錯，美美的膠囊列車不包含在釜山 Pass 中，需要另外購買車票；而且 <strong>非常熱門，務必要在出發前購買預約</strong> 。</p>
<ul>
  <li>天空膠囊 尾浦出發</li>
  <li>2 人座 (1 張 2 人使用)</li>
  <li><strong>價格：NT$861</strong></li>
</ul>

<h4 id="韓國網卡韓國-高速無限流量-esim"><a href="https://www.kkday.com/zh-tw/product/138273-korea-network-card-korea-express-esim-500mb-5gb-50gb-unlimited-plan-south-korea?cid=19365" target="_blank">韓國網卡｜韓國 高速無限流量 eSIM</a></h4>
<ul>
  <li>7 日無限流量方案 (最後一天早上就去機場，所以就不買了)</li>
</ul>

<h4 id="naver-map"><a href="https://map.naver.com/p/" target="_blank">Naver Map</a></h4>

<p><img src="/assets/8ace34a1a3d8/1*PrUwaqdQWWW0uvNzc6NnLQ.webp" alt="" loading="lazy" decoding="async" width="1200" height="959" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijk1OSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<blockquote>
  <p><a href="https://map.naver.com/p/" target="_blank"><strong><em>韓國務必使用 Naver Map。</em></strong></a></p>
</blockquote>

<blockquote>
  <p><strong><em><a href="https://map.naver.com/p/" target="_blank">韓國務必使用 Naver Map。</a></em></strong></p>
</blockquote>

<blockquote>
  <p><strong><em><a href="https://map.naver.com/p/" target="_blank">韓國務必使用 Naver Map。</a></em></strong></p>
</blockquote>

<p><strong>插曲 — 出發前一天要登入 Naver Map 發現帳號被鎖</strong></p>

<p><img src="/assets/8ace34a1a3d8/1*0SzoLbnn_g-Nu1Gx0aYF9Q.webp" alt="" loading="lazy" decoding="async" width="456" height="616" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0NTYiIGhlaWdodD0iNjE2Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><img src="/assets/8ace34a1a3d8/1*vjnO23ajw0h8XUZ5LBhryw.webp" alt="https://help.naver.com/service/5640/contents/20783?lang=en&amp;osType=COMMONOS" loading="lazy" decoding="async" width="440" height="616" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0NDAiIGhlaWdodD0iNjE2Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><a href="https://help.naver.com/service/5640/contents/20783?lang=en&amp;osType=COMMONOS" target="_blank">https://help.naver.com/service/5640/contents/20783?lang=en&amp;osType=COMMONOS</a></p>

<p><strong>原因疑似是在台灣登入被認為是可疑活動觸發 Naver 帳號保護機制。</strong></p>

<p>可以透過身份認證重新啟用帳號，不過我不確定是忘記名字填什麼還是因為外國人無法通過實名驗證，反正不管怎麼輸入都顯示輸入資訊錯誤，最後直接被鎖死。</p>

<p>爬 <a href="https://www.dcard.tw/f/korea_study/p/257877877" target="_blank">網路資料</a> 說可以填寫表單申請取消保護機制「 <a href="https://help.naver.com/inquiry/input.help?categoryNo=18202&amp;serviceNo=5640&amp;lang=en" target="_blank">Members residing outside of Korea_Request to lift account protection measures</a> 」，但是我不太確定名字的問題，因此第一次申請被拒：</p>

<p><img src="/assets/8ace34a1a3d8/1*lc5k61s4A4CBsVQ7ZYBOKg.webp" alt="申訴失敗" loading="lazy" decoding="async" width="1101" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMTAxIiBoZWlnaHQ9IjEyMDAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>申訴失敗</p>

<p>第二封我 <a href="https://help.naver.com/inquiry/input.help?categoryNo=14955&amp;serviceNo=5640&amp;lang=ko" target="_blank">直接去 Help Center</a> 填寫申請表單：</p>

<p><img src="/assets/8ace34a1a3d8/1*Mn_YZfHr9U8tXKYCjySHVg.webp" alt="" loading="lazy" decoding="async" width="1200" height="1071" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjEwNzEiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>申訴內容我輸入：</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>1. Your NAVER ID.
你的 Naver 帳號，如果連帳號都忘記可以先從忘記帳號用信像、手機找回

2. The registered name, date of birth, and contact information (mobile phone number or email address) of your NAVER ID.
Date of birth: YYYY/MM/DD 你註冊填的生日
Mobile Number: +886XXXXXXXXX 你註冊填的手機
Email: 你註冊填的 Email
Registered Name: Sorry I really forget which name I've input, it may be one of down below:
你想得到的姓名組合
XXXXX LI
LI XXXXX
XXX
李XX
but my real first name is XXXX, last name is Li

3. Attach your overseas identification card.
as attached file.

4. The details of your request to the NAVER Help Center.
I forgot my name which I input and I want lift the account protection measures.
</code></pre></div></div>

<p>能提供的資訊越多越好，最後附加檔案附上護照個人資料頁圖片。</p>

<p><strong>然後就成功解鎖了：</strong></p>

<p><img src="/assets/8ace34a1a3d8/1*uvUo0CgFkqTfuOp87UtahQ.webp" alt="" loading="lazy" decoding="async" width="1038" height="1122" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDM4IiBoZWlnaHQ9IjExMjIiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>會再收到一封臨時密碼信，用臨時密碼就能登入 Naver Map 了， <strong>記得去更改密碼跟加綁兩階段驗證登入(因為 Naver 登入保護機制我們取消了)</strong> 。</p>

<blockquote>
  <p><em>Naver 的回信速度蠻快的，都在半天內解決；如果怕意外被鎖可以先申請「 <a href="https://help.naver.com/inquiry/input.help?categoryNo=18202&amp;serviceNo=5640&amp;lang=en" target="_blank">Members residing outside of Korea_Request to lift account protection measures</a> 」(</em> <a href="https://www.dcard.tw/f/korea_study/p/257877877" target="_blank">參考這篇填寫</a> <em>)。</em></p>
</blockquote>

<h4 id="食">食</h4>

<p>提前預約的只有 西面的韓牛烤肉 <a href="https://www.catchtable.net/zh-TW/shop/busan_kosaljip?operationType=REMOTE_WAITING_GLOBAL" target="_blank">花肉店 田浦店</a> 。</p>

<p><img src="/assets/8ace34a1a3d8/1*oXyIDJJvynQfqfD_-Yb8cw.webp" alt="CatchTable" loading="lazy" decoding="async" width="998" height="1198" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5OTgiIGhlaWdodD0iMTE5OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>CatchTable</p>

<p>是說 <a href="https://www.catchtable.net/zh-TW/" target="_blank">Catchtable</a> 這個平台還蠻方便的，可以提前預約韓國美食餐廳。</p>
<h4 id="樂">樂</h4>
<ul>
  <li>Day 1 07/29 (二)：落地釜山、直奔海雲台飯店</li>
  <li>Day 2 07/30 (三)：早上 Skyline Luge 滑坡車、海東龍宮寺，下午海岸列車＋膠囊列車、回海雲台、釜山 BUSAN X the SKY，晚上味贊王烤肉</li>
  <li>Day 3 07/31 (四)：早上青沙浦、蜂蜜奶油吐司、海岸列車回海雲台、下午 Spa Land、晚上釜山遊艇之旅、晚上烤鰻魚</li>
  <li>Day 4 08/01 (五)： 移動到南浦洞、松島海上纜車、龍宮雲橋、甘川洞文化村、晚上喉嚨鍋蓋烤肉、釜山塔看夜景</li>
  <li>Day 5 08/02 (六)：樂天百貨、影島大橋開橋(只有週六有)。</li>
  <li>Day 6 08/03 (日)：樂天百貨、富平罐頭市場、白淺灣文化村、福泉寺夜景。</li>
  <li>Day 7 08/04 (一)：移動到西面、晚上吃韓牛。</li>
  <li>Day 8 08/05 (二)：釜山金海國際機場，回程。</li>
</ul>

<h4 id="住">住</h4>
<h4 id="海雲台高麗良宵酒店-day-1--day-4-三晚-"><a href="https://www.booking.com/hotel/kr/benikea-haeundae.zh-tw.html?label=gen173nr-10CAEoggI46AdIM1gEaOcBiAEBmAEzuAEHyAEM2AED6AEB-AEBiAIBqAIBuAKWo-bEBsACAdICJGZjZWEwODk4LTZkNzAtNGY0MS04OGViLTE1ZmUxZmJkOGQwZtgCAeACAQ&amp;sid=594a3964e5a18310cf9a473a9d09eb36&amp;aid=304142" target="_blank">海雲台高麗良宵酒店 (Day 1 — Day 4 三晚</a> )</h4>

<p><img src="/assets/8ace34a1a3d8/1*f5NuKjYVcPd6G78lEtIncQ.webp" alt="" loading="lazy" decoding="async" width="1090" height="872" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDkwIiBoZWlnaHQ9Ijg3MiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>地點還算方便，走路到海雲台大街大約 10 分鐘，有窗但無海景、雙人雙床房。</p>
<ul>
  <li><strong>價格：實刷 NT$12,032，一晚一人約 NT$2,000。</strong></li>
</ul>

<h4 id="hotel-noah-day-4--day-7-三晚"><a href="https://www.agoda.com/zh-cn/hotel-noah_3/hotel/busan-kr.html" target="_blank">Hotel Noah (Day 4 — Day 7 三晚)</a></h4>

<p><img src="/assets/8ace34a1a3d8/1*UgPFi5UgawBzJbgluC5Vdw.webp" alt="" loading="lazy" decoding="async" width="1100" height="652" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMTAwIiBoZWlnaHQ9IjY1MiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>地鐵札嘎其站出來走路約 5 分鐘、對街就是 BIFF 廣場，有窗戶、雙人雙床房。</p>
<ul>
  <li><strong>價格：實刷 NT$7,783，一晚一人約 NT$1,298。</strong></li>
</ul>

<h4 id="air-sky-hotel-day-7--day-8-一晚"><a href="https://www.booking.com/hotel/kr/eeoseukaigwangwanghotel.zh-tw.html" target="_blank">Air Sky Hotel (Day 7 — Day 8 一晚)</a></h4>

<p><img src="/assets/8ace34a1a3d8/1*mosy-Q7nQzyOIfPmAi3twQ.webp" alt="" loading="lazy" decoding="async" width="1124" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMTI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*26rsk_MCYmlbNhmKiTKqKw.webp" alt="" loading="lazy" decoding="async" width="553" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1NTMiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>距離機場只有一站，等車＋搭車＋走路時間大約只要 10 分鐘就能到機場，有窗、雙人雙床房。</p>
<ul>
  <li><strong>價格：實刷 NT$2,835，一晚一人約 NT$1,418。</strong></li>
</ul>

<blockquote>
  <p><em>題外話：這間飯店在完成預訂後會傳罐頭訊息要求支付訂金，如果本就已經在當下全額刷付可以無需理會。</em></p>
</blockquote>

<h4 id="簽證">簽證</h4>

<p>韓國免簽證，只需出發前三天提前在 <a href="https://www.e-arrivalcard.go.kr/portal/apply/agreementPolicy.do?applyType=P" target="_blank">線上填好入境申請表</a> ，就能在落地後直接憑護照完成入境。</p>

<blockquote>
  <p>準備就緒，出發！</p>
</blockquote>

<h3 id="day-1-0729-二-台灣前往海雲台">Day 1 07/29 (二) 台灣前往海雲台</h3>
<h4 id="1330-抵達桃園國際機場第二航廈">~=13:30 抵達桃園國際機場第二航廈</h4>

<p><img src="/assets/8ace34a1a3d8/1*V9fB_UU_9T-t0Z4QN1WE4g.webp" alt="" loading="lazy" decoding="async" width="1200" height="807" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjgwNyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*ZQfPfUI56V4EI_U2vgPj5A.webp" alt="" loading="lazy" decoding="async" width="964" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NjQiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>大約 13:50 開櫃報到，約 14:10 完成報到＋托運。</p>

<blockquote>
  <p><em>可以先在釜山航空官網 <a href="https://mtw.airbusan.com/mw/checkin/checkinList" target="_blank">線上完成報到＋選位(免費)</a> 。</em></p>
</blockquote>

<h4 id="1430-完成出境吃東西">14:30 完成出境＋吃東西</h4>

<p><img src="/assets/8ace34a1a3d8/1*QzSioHsBFjr_LBpX2NlgWw.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>因為落地釜山後就要直接趕去飯店，所以在台灣先吃飽飽。</p>
<h4 id="1500-開始候機啟用-esim">15:00 開始候機、啟用 eSIM</h4>

<p><img src="/assets/8ace34a1a3d8/1*AXE6WNQdcgDXCFuGn7k5PA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*ZdqpL-V_F7GA8qsTKEa93g.webp" alt="" loading="lazy" decoding="async" width="553" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1NTMiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*QbB73S1hJj3Buwzqf8wdSw.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>因為怕機場網路不好，所以先在 <a href="https://zhgchg.li/posts/aacd5f5cacd1/#%E4%BA%8B%E5%85%88%E5%95%9F%E7%94%A8-esim-iphone-%E7%82%BA%E4%BE%8B" target="_blank">台灣啟用 eSIM</a> 。</p>
<h4 id="1640-飛機起飛">~=16:40 飛機起飛</h4>

<p><img src="/assets/8ace34a1a3d8/1*TnEN0BSfol902HADb8_rNw.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*_2jmZ8VV-D_8uI43mnnLHQ.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*mKI4jAmJiEQdoyY8E1VU8g.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>小延誤 15 分鐘起飛，座位蠻小的。</p>
<h4 id="2005-抵達韓國釜山">~=20:05 抵達韓國釜山</h4>

<p><img src="/assets/8ace34a1a3d8/1*GmQ4yzkxgxt9MJgcXwwauA.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/8ace34a1a3d8/1*ZmBqlt5oYtJRpCHdqD1W1g.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<blockquote>
  <p><em>釜山機場是軍民兩用機場，禁止拍照。</em></p>
</blockquote>

<h4 id="2030-提領行李完成入境">~=20:30 提領行李＋完成入境</h4>

<p><img src="/assets/8ace34a1a3d8/1*6TN-R2eaqfuh_m2cddvJRA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>先去機場的超商買一些小東西補充熱量，還有換錢跟休息了一下。</p>
<h4 id="2100-抵達金海輕軌--機場站">~=21:00 抵達金海輕軌 — 機場站</h4>

<p>從機場出來大概走路 5 分鐘就到了。</p>

<p><img src="/assets/8ace34a1a3d8/1*9qFJw2RhaG6aaQae1WHrPA.webp" alt="" loading="lazy" decoding="async" width="1204" height="1470" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjA0IiBoZWlnaHQ9IjE0NzAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/8ace34a1a3d8/1*qovf8TmzIHYe6jOPqAyqAw.webp" alt="" loading="lazy" decoding="async" width="990" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5OTAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*S4Gkbfsb_0uApwcUSEiTnA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>先陪 Sean 儲值交通卡，這邊有兩台可以儲值交通卡的機器 (Self Service Charger)， <strong>只能以韓幣 1000 元為倍數加值</strong> 。</p>

<p><img src="/assets/8ace34a1a3d8/1*o-mrmvVu8YonD9G7kFgn0A.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*5PANgTxqra2B7_S1knjfkA.webp" alt="" loading="lazy" decoding="async" width="1200" height="1023" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjEwMjMiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/8ace34a1a3d8/1*jILCIPwHH4B2sUCZb5glEg.webp" alt="" loading="lazy" decoding="async" width="742" height="1382" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NDIiIGhlaWdodD0iMTM4MiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>刷卡進站後請找前往沙上(Sasang Station)的月台，下一站是「西釜山流通地區」。</p>
<h4 id="2115-抵達沙上轉乘綠線">~=21:15 抵達沙上轉乘綠線</h4>

<p><img src="/assets/8ace34a1a3d8/1*54uF-nAbXp068jDEvOSRxQ.webp" alt="" loading="lazy" decoding="async" width="732" height="1592" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3MzIiIGhlaWdodD0iMTU5MiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*SU68g_MSrPTZeJQ7BFQLhg.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*WR7xmcLqxByn81lIAdRl6g.webp" alt="" loading="lazy" decoding="async" width="676" height="1572" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2NzYiIGhlaWdodD0iMTU3MiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>這邊只要一路跟著地上的綠色指示就能從輕軌出站轉地鐵綠線，往「 <strong>萇山</strong> 」方向(另一個方向是往 <strong>梁山</strong> ，不要看錯字了)，下一站「甘田」。</p>
<h4 id="2120-抵達綠線沙上站等車">~=21:20 抵達綠線沙上站等車</h4>

<p><img src="/assets/8ace34a1a3d8/1*mhSqB1__ButsItcOkBuTBw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*rpFYA13Lb9nyNy1dAp50PA.webp" alt="" loading="lazy" decoding="async" width="1200" height="501" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjUwMSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<h4 id="2220-抵達海雲台">~=22:20 抵達海雲台</h4>

<p>快到海雲台站的時候地鐵廣播會有海鷗的叫聲。</p>

<p><img src="/assets/8ace34a1a3d8/1*FCNMmVBqS2AKfNQjh5cVmg.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<h4 id="2230-抵達飯店">~=22:30 抵達飯店</h4>

<p><img src="/assets/8ace34a1a3d8/1*LOXnjLsRW91_eVYck5kGeQ.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*30mP_dahFonL0ve1n_vEig.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<iframe class="embed-video" loading="lazy" src="https://www.youtube.com/embed/YbU4uG3pLjI" title="海雲台高麗良宵酒店" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
<h4 id="2250-出門海雲台大街覓食">~=22:50 出門海雲台大街覓食</h4>

<p><img src="/assets/8ace34a1a3d8/1*yKUlVFqVaS2D6M7j16mfoQ.webp" alt="" loading="lazy" decoding="async" width="971" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NzEiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*64lyMg61i9rRbmJcwxjjgQ.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*HjfYqKBHDe5_c0iF7QaHuw.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>住的位子會先經過海雲台傳統市場有賣很多吃的。</p>
<h4 id="2330-回飯店開吃">~=23:30 回飯店開吃</h4>

<p><img src="/assets/8ace34a1a3d8/1*vQBulLYRFkoUQC_Os4hPFw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*SyBfYDcG8uh5ArLioXV9vg.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*My3vIyTLBNbTSDtfh4FtkA.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>最後買了 BBQ 炸雞 兩種口味跟傳統市場的辣年糕還有超商的酒。</p>

<blockquote>
  <p><em>炸雞一份就夠兩個人吃了，兩份太多了(感覺是台灣的四份)，實刷 NT $1,210。</em></p>
</blockquote>

<blockquote>
  <p><em>這款芒果酒(</em> Iced tea highball with mango cubes <em>)好喝(5%) 裡面有芒果罐頭果肉，後來發現台灣全家超商也有進。</em></p>
</blockquote>

<h3 id="day-2-0730-三-skyline-luge-滑坡車海東龍宮寺海岸列車膠囊列車釜山-busan-x-the-sky味贊王烤肉">Day 2 07/30 (三) Skyline Luge 滑坡車、海東龍宮寺、海岸列車、膠囊列車、釜山 BUSAN X the SKY、味贊王烤肉</h3>
<ul>
  <li>KKday 推薦： <a href="https://www.kkday.com/zh-tw/product/11186-busan-day-tour-taejongdae-gamcheon-culture-village-songdo-skywalk-haedongyonggungsa-temple-or-yacht-tour?cid=19365" target="_blank">釜山全攻略一日遊|海雲台藍線公園 (天空膠囊 &amp; 海岸列車)・甘川文化村・豪華遊艇・白淺海岸村・海東龍宮寺 &amp; More (釜山出發)</a>
    <h4 id="0930-出門--超級好天氣">09:30 出門 — 超級好天氣</h4>
  </li>
</ul>

<p><img src="/assets/8ace34a1a3d8/1*zShkz0mzeWJhZKml3P1D2A.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/8ace34a1a3d8/1*Yf9ZxEjhX1K-YMwhTaDgFw.webp" alt="" loading="lazy" decoding="async" width="553" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1NTMiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>飯店門口就有公車直達 Skyline Luge。</p>
<h4 id="1025-skyline-luge-">10:25 Skyline Luge 👍👍👍</h4>

<p><img src="/assets/8ace34a1a3d8/1*Yv8wNXoEqXLZZJII4DQsxg.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*QXuBBjbiGpENtziQS-sagg.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*s71ofWNtvFfoIBqnf-0VJw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>大約 10:30 開始排換票 (釜山 Pass 需要換票後才能使用)，人蠻多的，大約排了 20 分鐘拿到門票；釜山 Pass 可以選擇玩兩次滑坡車或是玩一次滑坡車跟一次高空滑索，大家幾乎都選玩兩次滑坡車。</p>

<blockquote>
  <p><a href="https://www.kkday.com/zh-tw/product/152412-gijang-skyline-luge-ticket-busan-south-korea?cid=19365" target="_blank"><em>如果沒有買 Pass 也能先在 KKday 購票。</em></a> <em><strong>(比現場買便宜，但也要換票才能進)</strong></em></p>
</blockquote>

<p><img src="/assets/8ace34a1a3d8/1*cLgWZh4YrAkhLDfIkUICpw.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*_V9oQVANR9ncErO9tWEsCw.webp" alt="" loading="lazy" decoding="async" width="978" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NzgiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>換好票之後直接去旁邊選好符合 Size 的安全帽，戴好安全帽就能搭纜車上去囉。</p>
<h4 id="1100-搭上纜車">~=11:00 搭上纜車</h4>

<p><img src="/assets/8ace34a1a3d8/1*PT-mGar4So6od4dD3A6F5g.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*YzED-cEVaw7wrylcSUjuKw.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*YHu8QYJ2Cx1grupJ55ZmsQ.webp" alt="" loading="lazy" decoding="async" width="703" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3MDMiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>人都集中站換票、買票櫃檯，後面搭纜車跟滑坡車都不用怎麼排隊，纜車上去的途中可以看到下面的賽道，大約 5 分鐘就抵達出發點。</p>

<p><img src="/assets/8ace34a1a3d8/1*doGVKHy5yunEpR7OFaa6rw.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/8ace34a1a3d8/1*f0KgbnLu921Hiq7QAZg5HQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>如果很怕開放式纜車的，這次去有看到旁邊另一條箱型式纜車感覺也快開通了(?)。</p>
<h4 id="1105-準備出發">11:05 準備出發</h4>

<p><img src="/assets/8ace34a1a3d8/1*QmbycT8d2Gh5wimLP18CPQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/8ace34a1a3d8/1*SA-bWwl8EuFsllB6_O5IrA.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>上面可以眺望隔壁的樂天樂園。</p>

<p>第一次請排「首次搭乘入場」會蓋手章跟有工作人員做簡單教學。</p>

<p><img src="/assets/8ace34a1a3d8/1*UCPhQh3lnSwMiO9UBqoHDQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*loe9DLrp_dtUFnpYrwxAOw.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/8ace34a1a3d8/1*i9BnnhpabwgnxuBDw_7dbA.webp" alt="" loading="lazy" decoding="async" width="917" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MTciIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>操作方式總之就是：</p>
<ul>
  <li><strong>腳打直</strong></li>
  <li><strong>往後前進、往前放煞車</strong></li>
  <li>中途不能停、中途不能停、中途不能停，完全停下來就很難繼續滑動了</li>
  <li>適時轉彎前減速不然會翻車</li>
  <li>小心超車</li>
  <li><strong>雙手操控不可手持手機，中途有攝影機會拍照可以去紀念品店購買</strong></li>
  <li>個人物品要小心不要掉了</li>
</ul>

<p><img src="/assets/8ace34a1a3d8/1*GGsBBz5201M0fOrH4T1sqQ.webp" alt="" loading="lazy" decoding="async" width="1200" height="862" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg2MiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>從上滑到下大約 6 分鐘而已。</p>

<p>下來就是紀念品中心跟可以買中途拍的照片。</p>

<p><img src="/assets/8ace34a1a3d8/1*17zdVTPo_LP48TizhwValw.webp" alt="" loading="lazy" decoding="async" width="941" height="1185" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NDEiIGhlaWdodD0iMTE4NSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>穿過紀念品中心回到入口，再排一次纜車上去。</p>

<p><img src="/assets/8ace34a1a3d8/1*I0y7aZ0bpynBInkP18lcUQ.webp" alt="" loading="lazy" decoding="async" width="694" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2OTQiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*2Y0iQTWf9pE0fAZJEK7JDg.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>這次排再入場通道（可以直接玩，不會有工作人員教學）。</p>

<p><img src="/assets/8ace34a1a3d8/1*1aOU7IAPVNd6bSLNTaXRsw.webp" alt="" loading="lazy" decoding="async" width="1200" height="865" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg2NSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>ㄏ勝。</p>
<h4 id="1145-離開前往海東龍宮寺">11:45 離開，前往海東龍宮寺</h4>

<p>出 Skyline Luge 後對面直直走就是海東龍宮寺了，大約需步行 10 分鍾、上坡路。</p>

<p><img src="/assets/8ace34a1a3d8/1*W8pHXqKZ-gEPUqqLQV7cSA.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<h4 id="1155-海東龍宮寺">11:55 海東龍宮寺</h4>

<p>因為 <a href="/posts/z-度旅行遊記/九州自由行攻略-釜山新山茶花號郵輪入境日本博多-深入由布院-大分-福岡等地-cb65fd5ab770/"><strong>去年有來過了</strong></a> ，所以這次只走馬看花。</p>

<p><img src="/assets/8ace34a1a3d8/1*QO6ie1PN4o8liIjIs2Y0qQ.webp" alt="" loading="lazy" decoding="async" width="1200" height="861" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg2MSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*5RNxsL0nD0TQ3I_UvwZQPg.webp" alt="" loading="lazy" decoding="async" width="947" height="1216" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NDciIGhlaWdodD0iMTIxNiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*NllO9Hy73kJQzWNGoW0ABA.webp" alt="" loading="lazy" decoding="async" width="917" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MTciIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>大約 11:55 就到海東龍宮寺攤販入口。</p>

<p><img src="/assets/8ace34a1a3d8/1*m-tact5LRxGmwTBeDj7D2w.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*K2ByL3USjy1fUlipsZLv1g.webp" alt="" loading="lazy" decoding="async" width="1400" height="984" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9Ijk4NCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>這次才發現海東龍宮寺匾牌入口右邊也可以走，是緩下坡可以直達本殿；走匾牌入口這邊是很陡的樓梯，不好走。</p>

<p><img src="/assets/8ace34a1a3d8/1*dyci1YPm-ApaeV2MDLsW2Q.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/8ace34a1a3d8/1*wpe6ymEfLMASAEivSVyyJQ.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*AvKx5_BLU1cAJSkrvY8u7A.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*8RX8RXzZJ_4mW8hxss7hTw.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<h4 id="1245-等公車去松亭站">12:45 等公車去松亭站</h4>

<p><img src="/assets/8ace34a1a3d8/1*hnGZPnjF3eIAeAp7nu-Seg.webp" alt="" loading="lazy" decoding="async" width="942" height="1196" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NDIiIGhlaWdodD0iMTE5NiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*05IR75W3B-T4g2QkInLjHg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>釜山的避暑措施做得蠻好的，路上等紅綠燈有陽傘、公車站幾乎都有風扇，可以按一下啟動。</p>
<h4 id="路線計劃如下圖">路線計劃如下圖：</h4>

<p><img src="/assets/8ace34a1a3d8/1*wbyHE15DgoW3_pkit_OR_A.webp" alt="" loading="lazy" decoding="async" width="1400" height="947" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9Ijk0NyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>從 Skyline Luge 搭公車+走路到海岸列車松亭站，搭海岸列車到青沙浦站，轉搭膠囊列車回海雲台。</p>
<h4 id="1315-抵達松亭車站">~=13:15 抵達松亭車站</h4>

<p><img src="/assets/8ace34a1a3d8/1*1W63YJR27-niR0rPFiNkdw.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/8ace34a1a3d8/1*qKTgaE6w7QYNnHU41kgdRg.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*E1rZoLfWgHKUxb3qRAXhkg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>從公車站走過來還要約 10 分鐘，很熱很曬。</p>

<p><img src="/assets/8ace34a1a3d8/1*uQegRUm5OVql_OGz5L4TQg.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*PvM5nguf_-Fv6NfoWztP-w.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*J76MOVL-li-Tg6c__KNMKg.webp" alt="ref" loading="lazy" decoding="async" width="927" height="322" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MjciIGhlaWdodD0iMzIyIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><a href="https://www.kkday.com/zh-tw/product/123012-haeundae-blueline-park-sky-capsule-beach-train-ticket?cid=19365" target="_blank">ref</a></p>

<p>13:30 的人滿了，劃位劃 14:00 的。( <a href="http://m.site.naver.com/0URl8" target="_blank">時刻表</a> )</p>

<blockquote>
  <p><em>小插曲：這站站務員不給用釜山 Pass PDF 檔，要我們一定要從 KKday App 開憑證給他。</em></p>
</blockquote>

<blockquote>
  <p><em>釜山 Pass 換的車票可當日、每個車站上車一次，所以其實可以沿途停靠各個景點。</em></p>
</blockquote>

<p><img src="/assets/8ace34a1a3d8/1*mCtniHyCFiDVmGqsr8fFfw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*LBR8tx9HfigYr51-ih8dag.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>時間還很早，先去便利超商果腹；松亭站出來就是松亭海水浴場(感覺人比較少)，這邊有一家超商。</p>
<h4 id="1340-開始候車">~=13:40 開始候車</h4>

<p>記得提早去排隊候車，不然只能站在最後一排。</p>

<p><img src="/assets/8ace34a1a3d8/1*wMw1Bmz47wE2_ivQ4LtQAw.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/8ace34a1a3d8/1*_jexN3EvstbOgkts27zQhg.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>穿過車站出來一路走到月台，在月台驗票入場就能開始候車了。</p>

<p>大約 13:50 車就會到，人群開始騷動排隊。</p>
<h4 id="1400-出發">~=14:00 出發</h4>

<p><img src="/assets/8ace34a1a3d8/1*HT17e69lak7tbTqoXCpVUg.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*QBfTvy19T_Jl6zvxVNDqfQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>坐到第二排的位子，風景還不錯。</p>
<h4 id="1415-抵達青沙浦">~=14:15 抵達青沙浦</h4>

<p><img src="/assets/8ace34a1a3d8/1*xeOjrCl9pQx_u28XnJXELA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*6JA5jGLBfsMl2GJMmehhtw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*H0sBRecqIdECSploIAYVpw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>下車後在月台看到往 2 樓的樓梯就是膠囊列車的乘車處了。</p>

<p><img src="/assets/8ace34a1a3d8/1*Y4jeQwk6jnpsbAklaKZI6g.webp" alt="" loading="lazy" decoding="async" width="945" height="1193" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NDUiIGhlaWdodD0iMTE5MyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*XDAIgiQb3qdEDSr546kNzg.webp" alt="" loading="lazy" decoding="async" width="572" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1NzIiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*hrnYxziizbB6PrFv97Qiow.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>打開 <a href="https://www.kkday.com/zh-tw/product/123012-haeundae-blueline-park-sky-capsule-beach-train-ticket?cid=19365" target="_blank">KKday 訂單憑證中有一個連結</a> ，會出現車票憑證；上二樓後給亭子的工作人員看憑證(我們預約的是 14:30–15:00 的梯次)，他會發一張號碼牌給你，時間到會開始叫號，如果已經開始叫號那就只能等叫號完了。</p>

<p><img src="/assets/8ace34a1a3d8/1*KCbwTzka0L2cWDvX2O37iQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*FjPJSkqC67-UAhlwm9RRzA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*5Tx02qf1e3U_IKWJaWjp8g.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>等待期間去隔壁紀念品店買了一隻扇子。</p>

<blockquote>
  <p><em>膠囊列車也有免費的扇子可以拿(最右張圖)。</em></p>
</blockquote>

<h4 id="1430-膠囊列車叫號排隊">~=14:30 膠囊列車叫號排隊</h4>

<p>時間到工作人員會開始依序叫號排隊。</p>

<p><img src="/assets/8ace34a1a3d8/1*ZPQgbN2k7kRG2KI-3YEfEA.webp" alt="" loading="lazy" decoding="async" width="934" height="1229" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MzQiIGhlaWdodD0iMTIyOSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*7S7dQYAf2Zqj_dxMjl_pDw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>排隊區域上方有冷氣，上車前才會核銷憑證(記得先開好)，上車後工作人員會拍一張合照，再看下車後要不要跟他們購買。</p>
<h4 id="1440-膠囊列車青沙浦---尾浦海雲台-出發-">~=14:40 膠囊列車青沙浦 -&gt; 尾浦(海雲台) 出發! 👍👍👍</h4>

<p><img src="/assets/8ace34a1a3d8/1*fLpUWvXcdl3Ryc1ary_arQ.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*nMQDysR6bNzOxVTLLU7rPQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*l1JWUq3qAKEyDoyLJyzSPQ.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>看起來很刺激，實際走得很慢；車廂內沒冷氣但有通風扇跟擴音箱可以插手機放音樂。</p>

<p><img src="/assets/8ace34a1a3d8/1*w4zPWlwQcqHhF4WCokBVyQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*L4f0Ebz-fi6SeW7ZZJeTRQ.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*67tHAjdjNoZLmHdg0bLZFw.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>一路看看風景拍拍網美照(?)</p>
<h4 id="1510-抵達尾浦海雲台">~=15:10 抵達尾浦(海雲台)</h4>

<p><img src="/assets/8ace34a1a3d8/1*hTEuxliIs2s_098pdVaOsg.webp" alt="" loading="lazy" decoding="async" width="944" height="1203" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NDQiIGhlaWdodD0iMTIwMyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*mrsat28nAJ7e1PWJcb-KMQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="986" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9Ijk4NiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>朝著海雲台方向(高樓)走出來到路口，對面右邊就是有名好吃的 <a href="https://naver.me/x9zcuKNm" target="_blank">鹽麵包 — Jayeondo saltbread</a> (等下會回來買)左邊一路往下走到面海的入口就是 釜山 Sky 入場處。</p>

<p><img src="/assets/8ace34a1a3d8/1*mxRI19obHDJEYWgaWpLC4g.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*kG7OMT38Kq5n4inz0Zi1VQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<h4 id="1535-釜山-busan-x-the-sky">~=15:35 釜山 BUSAN X the SKY</h4>

<p><img src="/assets/8ace34a1a3d8/1*wLhiiMFHth1q0M0I52w0BQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/8ace34a1a3d8/1*4GZH5tai41pbv0WbydQBzQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/8ace34a1a3d8/1*MZtM_pbelaE_9DSkReik6Q.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>海雲台沙灘方向的玻璃是有圍欄阻隔的，沒辦法貼著拍照。</p>

<p><img src="/assets/8ace34a1a3d8/1*LphemYMPN0KncWvpEvv-5w.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*59D8d2G9l56zMvbn3yw55Q.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*Naj3fWNQzQthcPQJvr3onA.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>有一個透明玻璃地板橋，不過玻璃磨損嚴重效果普普；還有可以透明玻璃廁所。</p>

<p><img src="/assets/8ace34a1a3d8/1*-H0j9zTeDr4mJ6Cqk3wWfA.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*8kH5BBvg3v0xYPxl-NknSw.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>往下走到咖啡廳、紀念品店可以回望剛剛膠囊列車來的路線。</p>

<p><img src="/assets/8ace34a1a3d8/1*89JfpBVVoL3GcEbkqqMNyQ.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*3MGWqKezW4zdue_uNECOWw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>吃了一根魚板香腸補充熱量(味道普普)。</p>

<p><img src="/assets/8ace34a1a3d8/1*--lNPiay_O4al6tfX5spwg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>除了咖啡廳、紀念品店，這層也有免費的展覽。</p>
<h4 id="1600-離開走回去買-鹽麵包--jayeondo-saltbread-">~=16:00 離開，走回去買 <a href="https://naver.me/x9zcuKNm" target="_blank">鹽麵包 — Jayeondo saltbread</a> 👍👍👍</h4>

<p><img src="/assets/8ace34a1a3d8/1*w9_aMzKXaNfaz_haZN-XIw.webp" alt="" loading="lazy" decoding="async" width="1200" height="853" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg1MyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*XF58GQnLL6PnuqbvlxDNEQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*O8DuQ0ywuuN5fxZXc_0_JQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>一份就是 6 個，買一份回飯店分著吃，香+脆+微微的鹽粒提味，我覺得很好吃。</p>

<p><img src="/assets/8ace34a1a3d8/1*2oV0hKVku4_AgQI7cf_cag.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*8-6mY9FWINdMJiYxpfPZEQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>海雲台這邊有很多大興土木改造工程，看起來在為 2030 爭取辦世博做準備(不過已經失敗)。</p>

<blockquote>
  <p><em>回飯店休息一下，晚上再去吃味贊王。</em></p>
</blockquote>

<h4 id="1750-抵達-味贊王海雲台店--matchandeul-salted-grill-haeundae-branch">17:50 抵達 <a href="https://naver.me/5sG3vEe5" target="_blank">味贊王海雲台店 — Matchandeul Salted Grill Haeundae Branch</a></h4>

<p><img src="/assets/8ace34a1a3d8/1*AsOMjouHVz3BRue3olauuw.webp" alt="" loading="lazy" decoding="async" width="943" height="1198" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NDMiIGhlaWdodD0iMTE5OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*O39pGmtLGJIa-bXxK-Nnig.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*52iHV2Dv0vDDxNuqJxYmhg.webp" alt="" loading="lazy" decoding="async" width="484" height="811" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0ODQiIGhlaWdodD0iODExIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>門口有一台機器可以候位，輸入信箱會收到通知，17:50 來就開始小排隊了。</p>

<p><img src="/assets/8ace34a1a3d8/1*PQL9i8rdeGLZD0rNLEaLxw.webp" alt="" loading="lazy" decoding="async" width="515" height="682" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1MTUiIGhlaWdodD0iNjgyIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><img src="/assets/8ace34a1a3d8/1*NNmf9vfpx9sX-EPv-6-MCQ.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>點按鈕可以查看當前號碼，時間到也會收到可入場通知信，很方便； <strong>另外，對面、隔壁大樓都在施工要小心。</strong></p>
<h4 id="1825-入場用餐">18:25 入場用餐</h4>

<p>大約等了 30 多分鐘才入場。</p>

<p><img src="/assets/8ace34a1a3d8/1*7MgY9p6aZMbGejj56iU11A.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/8ace34a1a3d8/1*1dn6e5L69VTVnrcEfhMvDw.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>我們直接點雙人套餐(內容是肉各一+大醬湯+釜飯)然後再一瓶 Cass 啤酒，實刷約 NT$1,500。</p>

<blockquote>
  <p><em>會烤好給你吃，評價是肉好吃但份量不多&amp;要排隊等候。</em></p>
</blockquote>

<h4 id="2030-吃飽回海雲台大街">~=20:30 吃飽回海雲台大街</h4>

<p><img src="/assets/8ace34a1a3d8/1*O2UXV7D8gKQ0hImc8iDnMA.webp" alt="" loading="lazy" decoding="async" width="948" height="1199" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NDgiIGhlaWdodD0iMTE5OSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*YINokSS0bT-ebB5VXJhW_w.webp" alt="" loading="lazy" decoding="async" width="918" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MTgiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*PjSGfzXsbc9bYFb1sK18Tw.webp" alt="" loading="lazy" decoding="async" width="943" height="1233" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NDMiIGhlaWdodD0iMTIzMyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>海雲台大街走走消化一下，買了一家抹茶專賣店的冰淇淋當甜點解膩。</p>

<p><img src="/assets/8ace34a1a3d8/1*eEqklwquew6rGg4-Il6FUw.webp" alt="" loading="lazy" decoding="async" width="937" height="1186" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MzciIGhlaWdodD0iMTE4NiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*XIW1F6tF8P0ExjjlixWKxQ.webp" alt="" loading="lazy" decoding="async" width="1146" height="961" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMTQ2IiBoZWlnaHQ9Ijk2MSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>隔壁就是 Olive Young 新開的 Delight Project 專賣店，搜刮了一波；很多買二送一並且滿15,000 韓元可以直接現場退稅。</p>

<p><img src="/assets/8ace34a1a3d8/1*9eNytaAkCGcIgbOzagwTiQ.webp" alt="" loading="lazy" decoding="async" width="1200" height="833" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjgzMyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*hW_N_93CpyyzWCGZ-OtJ4Q.webp" alt="" loading="lazy" decoding="async" width="943" height="1214" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NDMiIGhlaWdodD0iMTIxNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>再去海雲台海灘晃了一下才回飯店休息。</p>
<h3 id="day-3-0731-四-青沙浦-spa-land海岸列車釜山遊艇之旅">Day 3 07/31 (四) 青沙浦、 Spa Land、海岸列車、釜山遊艇之旅</h3>

<p>因為昨天只在青沙浦轉乘沒特別逛，今天又來。</p>
<h4 id="1100-上公車出發前往青沙浦">11:00 上公車出發前往青沙浦</h4>

<p><img src="/assets/8ace34a1a3d8/1*rbpv52Shw77IaUdT2d7-8w.webp" alt="" loading="lazy" decoding="async" width="954" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NTQiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*DJj6wZCG0KRomyT94xO1lA.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>從海雲台直達青沙浦的公車是小巴，沿途會經過釜山山莊之類的社區路比較小也很陡。</p>
<h4 id="1130-抵達青沙浦">11:30 抵達青沙浦</h4>

<p><img src="/assets/8ace34a1a3d8/1*fRTe_hokNNcPNgX97qZcQw.webp" alt="" loading="lazy" decoding="async" width="1200" height="846" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg0NiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*77MdQpIY5lupV8oQ3chyDw.webp" alt="" loading="lazy" decoding="async" width="1200" height="990" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijk5MCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>附近先隨便找了一家有吃的店吃午餐 ( <a href="https://naver.me/GlGmYxyY" target="_blank">Gohyang katsu</a> )，我點豬排三明治，肉很厚很嫩而且價格實惠。</p>
<h4 id="1230-diart-coffee-蜂蜜奶油吐司">~=12:30 <a href="https://naver.me/GkUJQI6r" target="_blank">DIART COFFEE 蜂蜜奶油吐司</a></h4>

<p><img src="/assets/8ace34a1a3d8/1*Su_kwIRgZb18zoMS0Tv0sw.webp" alt="" loading="lazy" decoding="async" width="948" height="1196" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NDgiIGhlaWdodD0iMTE5NiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*smtxM51j_tPmnW-RQql09g.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*8XzQQm3AZ0JR7Gr_TfaYxA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*6kxh55_key4KL-Tj5V5WNQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>吃完午餐走出來就是大家激推的釜山蜂蜜奶油吐司 <a href="https://naver.me/GkUJQI6r" target="_blank">DIART COFFEE</a> ，點了一份($10,000 韓元)跟咖啡($5,500 韓元)休息一下。</p>

<p>食用方式可參考 <a href="https://m.site.naver.com/1Abyy" target="_blank">小卡</a> ，總之就是先把麵包一塊一塊切好，然後均勻塗抹奶油跟蜂蜜食用。</p>

<blockquote>
  <p><em>蜂蜜不會太甜加上奶油的奶香配上香脆的麵包，好吃又不膩。</em></p>
</blockquote>

<h4 id="1330-回青沙浦準備搭海岸列車回尾浦海雲台">~=13:30 回青沙浦準備搭海岸列車回尾浦(海雲台)</h4>

<p><img src="/assets/8ace34a1a3d8/1*W-yaVbvzi87W8xZk_cza4g.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/8ace34a1a3d8/1*VKTdBhWwiPmOoE4eX7pwVw.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*dICe7b0tdWsGppYFDADrhQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/8ace34a1a3d8/1*fWJrPrMNalY2YbTFk9ct0Q.webp" alt="" loading="lazy" decoding="async" width="950" height="1214" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NTAiIGhlaWdodD0iMTIxNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>昨日的已無法使用，又再用釜山 Pass 換了一張海岸列車車票；這邊是中途站上車只能站在最後一排了。</p>

<p><img src="/assets/8ace34a1a3d8/1*_a9Gqyx5xSatuoqZVvDwPQ.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*6ypVO-ZjcG0zkZAuJKC7Mg.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>中途會經過幾個觀景平台，因為太熱就沒下去了。</p>
<h4 id="1355-抵達尾浦海雲台">~=13:55 抵達尾浦(海雲台)</h4>

<p><img src="/assets/8ace34a1a3d8/1*viDnwtL1r7EWe3K1FtDKiA.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>回到海雲台後就走路＋搭公車前往新世界 Spa Land 了。</p>
<h4 id="1430-抵達-新世界shinsegae百貨">~=14:30 抵達 <a href="https://naver.me/xfYRYAfu" target="_blank">新世界(SHINSEGAE)百貨</a></h4>

<p><img src="/assets/8ace34a1a3d8/1*ZqGLAD3UKpURs3HKbviTwQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="989" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9Ijk4OSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*WneX4dSsqbfvEps927Zqjw.webp" alt="" loading="lazy" decoding="async" width="925" height="1176" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MjUiIGhlaWdodD0iMTE3NiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>沒有逛，直衝一樓 Spa Land。</p>
<h4 id="1440-spa-land">14:40 Spa Land</h4>

<p>用釜山 Pass 兌換 Spa Land 使用。</p>

<blockquote>
  <p><a href="https://www.kkday.com/zh-tw/product/12213-busan-spa-land-centum-city-ticket?cid=19365" target="_blank"><em>沒買 Pass 也能在 Kkday 先購買。</em></a></p>
</blockquote>

<p>進去會發一個感應手環，用手環寄放鞋子、衣物，在場內換汗蒸幕專用衣服，也可以先去三溫暖(需全裸)再去汗蒸幕，汗蒸幕入口在女生更衣室那個方向。</p>

<p><img src="/assets/8ace34a1a3d8/1*2UvncJZg2YqeC9pmzSstww.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*rgOo09p9c0DJFD9h_UH7fQ.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*9oMyMc4jYnkhHcJKydzXJw.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>一樓有日光浴區、美甲、小吃攤、各種功能的汗蒸幕(暑假人太多了每一間幾乎都滿滿的人)，三樓有餐廳(可以去吃烤蛋、泡麵)、按摩、按摩椅跟瑜伽室。</p>

<p>去完三溫暖就去日光浴區躺著曬太陽了，點了一杯飲料(超甜)。</p>

<blockquote>
  <p><em>場內所有消費都是憑手環感應，最後出場再結帳。</em></p>
</blockquote>

<blockquote>
  <p><em><strong>老實說這次來根本沒體驗到汗蒸幕，人多到爆炸，下次再說了。</strong></em></p>
</blockquote>

<h4 id="1700-準備前往遊艇碼頭集合">17:00 準備前往遊艇碼頭集合</h4>

<p>預約 18:30 的場次，需提前去集合點名。</p>

<p><img src="/assets/8ace34a1a3d8/1*I05lgEC-kLYtbw1s_OWJ6Q.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>出發前先在新世界百貨 B1 買了吃了一些東西果腹。</p>
<h4 id="坐地鐵前往冬柏站-dongbaek">坐地鐵前往冬柏站 (Dongbaek)</h4>

<p><img src="/assets/8ace34a1a3d8/1*nLZyTcQcoS4NBTV_EozYFA.webp" alt="" loading="lazy" decoding="async" width="721" height="514" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3MjEiIGhlaWdodD0iNTE0Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>出站要再走約 15 分鐘才會到碼頭。</p>

<blockquote>
  <p><em>碼頭這邊只有販賣機跟廁所比較髒、人多要排隊； <strong>建議在市區買好需要的東西、上好廁所再來。</strong></em></p>
</blockquote>

<h4 id="1800-抵達遊艇碼頭">~=18:00 抵達遊艇碼頭</h4>

<p><img src="/assets/8ace34a1a3d8/1*2IAvf0t9-X64N-VhcqU3Hw.webp" alt="" loading="lazy" decoding="async" width="944" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NDQiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*gzCg-qoszdj6FPTDnl6OxA.webp" alt="" loading="lazy" decoding="async" width="1200" height="854" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg1NCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*SEdvFIL6AktezC9W238zDA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>穿過遊客中心後就能來到碼頭集合處，各個船家有不同的碼頭集合處。</p>

<p><img src="/assets/8ace34a1a3d8/1*XWHxS8z33CPpiugdE2ddVw.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/8ace34a1a3d8/1*uGw5fIqLJIJoxtzTpzNm3g.webp" alt="" loading="lazy" decoding="async" width="947" height="1182" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NDciIGhlaWdodD0iMTE4MiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>等到大約 18:15 船家才出現開始整隊點名，現場收夜間加價的費用(一人 $5,000 韓元)。</p>
<h4 id="1830-海上遊艇出發-">18:30 海上遊艇出發 👍👍</h4>

<p><img src="/assets/8ace34a1a3d8/1*UdLDGZfqHM_HneNjqh6qlA.webp" alt="" loading="lazy" decoding="async" width="951" height="1209" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NTEiIGhlaWdodD0iMTIwOSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*Z52u6yd6i_bwsvmdKECD1A.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>上船要穿救生衣，坐到第二層最前面的位子。</p>

<p><img src="/assets/8ace34a1a3d8/1*SEVJsECS6H4ngAof9V0nQQ.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*fLsO9aiXUtVx3vm_etmEaA.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>開得很慢、不會太晃，一路前往穿過廣安大橋。</p>

<blockquote>
  <p><strong><em>不過怕暈船的還是建議要吃暈船藥。</em></strong></p>
</blockquote>

<h4 id="大致的路綫圖">大致的路綫圖：</h4>

<p><img src="/assets/8ace34a1a3d8/1*J0YqfliMZuX7rnrgzKItkA.webp" alt="" loading="lazy" decoding="async" width="1200" height="798" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijc5OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<h4 id="1850-抵達廣安里海灘前">~=18:50 抵達廣安里海灘前</h4>

<p><img src="/assets/8ace34a1a3d8/1*pDMeZQ252T2zQHU4fnsdmg.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*0xgRuRCfH8hUew63a7fqqg.webp" alt="" loading="lazy" decoding="async" width="1182" height="665" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMTgyIiBoZWlnaHQ9IjY2NSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>大約 20 分鐘後就會到達廣安里海灘前的海域，可以看到很多玩 SUP 的遊客；會在這停留跟放煙火；煙火就是這時段出航的各個遊艇的船家工作人員一起手持燃放，不過天還很亮所以看不太到。</p>

<p><img src="/assets/8ace34a1a3d8/1*aPIQ_aNeOjF3F0vc_ghnEA.webp" alt="" loading="lazy" decoding="async" width="1200" height="858" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg1OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*xlY1CbhaJm_y2MH2H2x6FA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>沿途工作人會也會很熱情的幫忙拍照。</p>
<h4 id="1900-回程">~=19:00 回程</h4>

<p><img src="/assets/8ace34a1a3d8/1*YFAwnj0StqttQX1mpGD93Q.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/8ace34a1a3d8/1*XfSCLtKiHYCFpboyNPiWcg.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>剛好看到晚霞。</p>
<h4 id="1920-回到陸地">~=19:20 回到陸地</h4>

<p>回陸地後就搭公車回海雲台了。</p>
<h4 id="1935-回到海雲台">~=19:35 回到海雲台</h4>

<p><img src="/assets/8ace34a1a3d8/1*5fS1FH22njGy1ppRJNvRhQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/8ace34a1a3d8/1*hS21eQGZ-VvsLKqQINEiEw.webp" alt="" loading="lazy" decoding="async" width="932" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MzIiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*nEMJPKds-CrjF2BDsR3ZcA.webp" alt="" loading="lazy" decoding="async" width="1200" height="734" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjczNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*K8XU7m63X0bGVYcaMOwgGQ.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>海雲台沙灘有市集、餐車。</p>
<h4 id="1955-去吃海雲台-烤鰻魚--pungcheonman-">~=19:55 去吃海雲台 <a href="https://naver.me/GEiuqenG" target="_blank">烤鰻魚 — PUNGCHEONMAN</a> 👍👍</h4>

<p><img src="/assets/8ace34a1a3d8/1*--tFcHDWgpPDD__zrK8Jlg.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>店內位子很多不用排隊，直接進來了。</p>

<p><img src="/assets/8ace34a1a3d8/1*R-kS0zI7e106d0e2SAaMKg.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*CHuWZKV3-zp9Z5vDy3h2kQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>我們點烤鰻魚+烤牛肉片+冷麵*2+燒啤，實刷 NT$2,387。</p>

<p><img src="/assets/8ace34a1a3d8/1*eebei6lY1kzg2ZkEunW1Gw.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/8ace34a1a3d8/1*3v8CcPgaFWmH4190p4q1og.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/8ace34a1a3d8/1*qku3gySyCW3u98SQ7qXA7g.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>烤鰻魚脆脆軟嫩多汁，很好吃，完全沒有任何腥味；烤牛肉片也不錯但是普通；蕎麥冷麵很像什麼黑糖ㄘㄨㄚˋ冰，實則是醬油味、有碎冰的，很清爽好吃。</p>

<p><img src="/assets/8ace34a1a3d8/1*IakblW3IGN6wxf1NkX29Ww.webp" alt="" loading="lazy" decoding="async" width="957" height="1213" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NTciIGhlaWdodD0iMTIxMyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*NUtsqQvPLceti09GjSi1Ew.webp" alt="" loading="lazy" decoding="async" width="947" height="1182" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NDciIGhlaWdodD0iMTE4MiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*NqVaJBSjpavz5Pqaee01Zg.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>吃完又去海雲台大街走走、海雲台傳統市場(忘了吃海鮮煎餅，可惡！)跟買伴手禮；明天就要離開海雲台了。</p>
<h3 id="day-4-0801-五-南浦洞松島海上纜車龍宮雲橋甘川洞文化村釜山塔">Day 4 08/01 (五) 南浦洞、松島海上纜車、龍宮雲橋、甘川洞文化村、釜山塔</h3>
<ul>
  <li>KKday 推薦： <a href="https://www.kkday.com/zh-tw/product/131061-busan-one-day-tour-south-korea?cid=19365" target="_blank">釜山精華景點一日遊|天空膠囊列車、甘川文化村、海雲台藍線公園等熱門景點</a></li>
  <li>KKday 推薦： <a href="https://www.kkday.com/zh-tw/product/19674-busan-air-cruise-songdo-marine-cable-car-ticket-south-korea?cid=19365" target="_blank">韓國釜山|松島海上纜車來回門票</a></li>
  <li>KKday 推薦： <a href="https://www.kkday.com/zh-tw/product/547950?cid=19365" target="_blank">【韓國旅遊】樂透釜慶蔚~松島海上纜車、世界文化遺產、天空膠囊列車、五星溫德姆飯店五日</a>
    <h4 id="0915-出門前往南浦洞">09:15 出門前往南浦洞</h4>
  </li>
</ul>

<p><img src="/assets/8ace34a1a3d8/1*amKvoodXorVlvXyKiBALLg.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>一早準備離開海雲台前往南浦洞。</p>
<h4 id="1030-抵達札嘎其站-hotel-noah-飯店">~=10:30 抵達札嘎其站， <a href="https://www.agoda.com/zh-cn/hotel-noah_3/hotel/busan-kr.html" target="_blank">Hotel Noah</a> 飯店</h4>

<p><img src="/assets/8ace34a1a3d8/1*Kq_QYpAk4Kr-F09JJ_FRWQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/8ace34a1a3d8/1*sMUSi1v4EluBNNT5emHqOg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>寄放行李。</p>
<h4 id="biff-廣場">BIFF 廣場</h4>

<p><img src="/assets/8ace34a1a3d8/1*cQXerZP_VIvP5ToSBrLgEw.webp" alt="" loading="lazy" decoding="async" width="945" height="1194" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NDUiIGhlaWdodD0iMTE5NCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>飯店出來對面就是 BIFF 廣場，很熱鬧、方便。</p>
<h4 id="1100-吃午餐--hongkong-0410-nampo-1st-branch">~=11:00 吃午餐 — <a href="https://naver.me/G28VhTtY" target="_blank">Hongkong 0410 Nampo 1st Branch</a></h4>

<p>接近中午先在 BIFF 內隨意找了家餐廳吃午餐，這家是韓式中華料理。</p>

<p><img src="/assets/8ace34a1a3d8/1*PFYie7L_VFf4E70JCoCjAw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*YNNdD826iQ8fqvZnDgJQFA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*ERESHYR_kpkJluI7uTd3CQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>點了炸水餃、糖醋肉(中，感覺可以點小的)、韓式炸醬麵。</p>

<p>炸水餃有點膩，糖醋肉不錯。</p>
<h4 id="1215-搭公車走路抵達松島纜車站">12:15 搭公車+走路，抵達松島纜車站</h4>

<p><img src="/assets/8ace34a1a3d8/1*O-7uqMkqeEY_hnPImCZ4nA.webp" alt="" loading="lazy" decoding="async" width="604" height="729" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2MDQiIGhlaWdodD0iNzI5Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><img src="/assets/8ace34a1a3d8/1*e07O9sxOWghCiDG4t57fTQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*G55PQci1BikM7kS7jRyBUg.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>下公車大約還要走 10 分鐘，太熱了，先去超商調一杯自製咖啡拿鐵消暑。</p>

<p><img src="/assets/8ace34a1a3d8/1*ntKH7dp9EHgiSQfDiETb1w.webp" alt="" loading="lazy" decoding="async" width="929" height="1189" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MjkiIGhlaWdodD0iMTE4OSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*gMrRyaT7YG7QVkW6vs4aYw.webp" alt="" loading="lazy" decoding="async" width="928" height="1186" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MjgiIGhlaWdodD0iMTE4NiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*9hyji_BcmpmX8mnbQ_8sYg.webp" alt="" loading="lazy" decoding="async" width="938" height="1196" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MzgiIGhlaWdodD0iMTE5NiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>進來之後排櫃檯兌換釜山 Pass 纜車車票(Pass 是水晶車廂、 <strong>來回，所以票要收好</strong> )。</p>

<p>領完票就直接搭手扶梯上樓排隊等纜車。(有發棒棒糖但我沒拿)</p>
<h4 id="1225-上車">12:25 上車</h4>

<p><img src="/assets/8ace34a1a3d8/1*2rQjr9yJHeMqKNL2WsdftQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/8ace34a1a3d8/1*zzQbyUo9b66wPcze_M5CTw.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*OL7mGet2prUpedl96EX1PA.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>5–6 人一台。</p>
<h4 id="1235-抵達松島">~=12:35 抵達松島</h4>

<p><img src="/assets/8ace34a1a3d8/1*qPfpLan9npUSJvb3WCumNA.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*iMGGxqNqL_cW6nGUL700ag.webp" alt="" loading="lazy" decoding="async" width="940" height="1176" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NDAiIGhlaWdodD0iMTE3NiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>出來直接右轉往下走。</p>

<p><img src="/assets/8ace34a1a3d8/1*OTbo_MgGmzY5e3BUofU85A.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*jeVCIVeLt_fhFSTgRBKjow.webp" alt="" loading="lazy" decoding="async" width="955" height="1194" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NTUiIGhlaWdodD0iMTE5NCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>走樓梯往下右轉到底就是「松島龍宮吊橋」了。</p>
<h4 id="1240-抵達松島龍宮吊橋">~=12:40 抵達松島龍宮吊橋</h4>

<p><img src="/assets/8ace34a1a3d8/1*witcQsiZpHm_OvIQKoGY3g.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>旁邊售票處使用釜山 Pass 換票進場。</p>

<p><img src="/assets/8ace34a1a3d8/1*0vBkG9nJIvyD7hrhYj-xnw.webp" alt="" loading="lazy" decoding="async" width="945" height="1203" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NDUiIGhlaWdodD0iMTIwMyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*PhNqmy6wBPgUbjY3zRT82w.webp" alt="" loading="lazy" decoding="async" width="360" height="480" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzNjAiIGhlaWdodD0iNDgwIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><img src="/assets/8ace34a1a3d8/1*hjmWsaA7pNfx77EXr5M7aQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1001" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwMDEiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/8ace34a1a3d8/1*-KX5b9yL0bniAwPYFgXcgw.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>這邊完全沒遮蔭，大太陽下很曬，可以鳥瞰整個松島纜車。</p>
<h4 id="1300-回到松島纜車排纜車回去">~=13:00 回到松島纜車，排纜車回去</h4>

<p><img src="/assets/8ace34a1a3d8/1*Og_17fLIQsrceTBxCRxZow.webp" alt="" loading="lazy" decoding="async" width="960" height="1192" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NjAiIGhlaWdodD0iMTE5MiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*OHD1o7Nau7E8xTUOb1yjeQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/8ace34a1a3d8/1*zkbCoM-RhqaU33418vE2aQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<h4 id="1325-離開松島走回去搭公車">~=13:25 離開松島走回去搭公車</h4>

<p><img src="/assets/8ace34a1a3d8/1*RCy2E4mmH3saWnKRoc7xWg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>纜車這邊也有一個觀景平台，太熱就沒去了。</p>

<p>走回公車站回到市區再轉乘小巴到甘川洞文化村。</p>

<p><img src="/assets/8ace34a1a3d8/1*zTVKv3tTtOdHSoOj81jtTQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<h4 id="1420-甘川洞文化村">~=14:20 甘川洞文化村</h4>

<p><img src="/assets/8ace34a1a3d8/1*rn4LG3Z1ckz_GMiNQe-8FA.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/8ace34a1a3d8/1*iJV9jBpSfiIbdpOf6t02Kw.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*EDPA83YXOSXbRqmWTUzirg.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*Dhi69ZSRcSJyvFgv_Oz5Cw.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>我們沒要拍照，純踩點；感覺就是個彩虹社區跟一些咖啡廳，一路走馬看花往下走，到另一個公車站就搭公車回飯店了。</p>

<blockquote>
  <p><em>這邊公車是小巴，有點像上陽明山的小巴，司機也開得很兇、人也很多很擠。</em></p>
</blockquote>

<h4 id="1500-回到-noah-hotel-飯店休息">~=15:00 回到 Noah Hotel 飯店休息</h4>
<iframe class="embed-video" loading="lazy" src="https://www.youtube.com/embed/ESP5SgZ4MMA" title="Hotel Noah" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>

<p><img src="/assets/8ace34a1a3d8/1*c7JIBKmacB_0FpkD1VSV3A.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/8ace34a1a3d8/1*fAl0cr7HLrPVGIc76JM94Q.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<ul>
  <li>房間窗戶外景色</li>
  <li>本來以為沒有投幣洗衣機，結果在三樓廁所有附設免費的洗衣機可以使用。</li>
</ul>

<h4 id="1740-出發去吃晚餐">~=17:40 出發去吃晚餐</h4>

<p><img src="/assets/8ace34a1a3d8/1*IX5LmJYb0y7bIOE2FWW1DQ.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*4qK7MSMMHkG60tIe-3yoDQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>從飯店走過去大約 10 分鐘。</p>
<h4 id="1750-喉嚨鍋蓋烤肉--moggumung-nampo-branch-">~=17:50 <a href="https://naver.me/xHmnku7F" target="_blank">喉嚨鍋蓋烤肉 — Moggumung Nampo Branch</a> 👍👍👍</h4>

<p><img src="/assets/8ace34a1a3d8/1*4nvoaUKeXZOVIb1IOCYYXQ.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*2KHw99GTRvBgKhL2jVZxBw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*DZdWxcnw-93O_23CzoNHGg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<ul>
  <li>特色是烤盤是鍋蓋</li>
  <li>小菜會有一顆維他命C (查了一下吃燒烤前或後補充維他命 C 可以減少自由基產生降低致癌風險)</li>
  <li>最低要點三份肉，一樣都有店員烤好給你吃</li>
  <li>點了兩份豬+一份牛+兩碗泡麵+Cass 啤酒，肉一份都是 $18,000 韓元， <strong>實刷 NT$1,060 相當划算，而且肉也很好吃！！大推</strong></li>
</ul>

<h4 id="-1830-走路前往釜山塔">~= 18:30 走路前往釜山塔</h4>
<ul>
  <li>KKday 推薦： <a href="https://www.kkday.com/zh-tw/product/19378?cid=19365" target="_blank">釜山塔展望台門票</a></li>
</ul>

<p>吃飽後再一路走到釜山塔，消化一下，大約 20 分鐘。</p>

<p><img src="/assets/8ace34a1a3d8/1*sLs757OOzNAkMKeiRv3lPQ.webp" alt="" loading="lazy" decoding="async" width="698" height="539" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2OTgiIGhlaWdodD0iNTM5Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><img src="/assets/8ace34a1a3d8/1*wrz7_9izA0-r02hyNB38sg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/8ace34a1a3d8/1*k3Z6gYsAotOt5OVIdFIKgw.webp" alt="" loading="lazy" decoding="async" width="933" height="1170" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MzMiIGhlaWdodD0iMTE3MCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>一路上很熱鬧、很多吃的餐廳。</p>

<p><img src="/assets/8ace34a1a3d8/1*U8r_Qon2rFimBcnEdWpmyw.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/8ace34a1a3d8/1*Ovz-36q_zBh3L-jFrMdUZQ.webp" alt="" loading="lazy" decoding="async" width="1200" height="833" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjgzMyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*DL3TyNS4bk-sKRx3rNkFqw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*mrV2TAQELoqtWkPK1uj22g.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<blockquote>
  <p><strong><em>導航其實應該要設定 — <a href="https://naver.me/GDL5X5TV" target="_blank">龍頭山紀念公園入口 Yongdusan Media Park Entrance</a></em></strong> <em>，那邊有電扶梯往上；從這邊走要小爬一下山 (約 5 分鐘)。</em></p>
</blockquote>

<h4 id="1900-抵達釜山塔下等日落之後再上去看夜景">19:00 抵達釜山塔下，等日落之後再上去看夜景</h4>

<p><img src="/assets/8ace34a1a3d8/1*q4kM-CGN-dIC7w5NU32a0A.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*JcfGi8vcQtevAvPWGNnuMg.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>期間先去紀念品店逛逛。</p>
<h4 id="1930-上釜山塔">19:30 上釜山塔</h4>

<p>在一樓櫃檯出示釜山 Pass 換門票進入。</p>

<p><img src="/assets/8ace34a1a3d8/1*xA5Zg72P8NEN_tUVyPZMkA.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*SOJbuAh7kPr2jx-dl1x1zw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*YCkHak-FlJ-mnq0yQWx-yQ.webp" alt="" loading="lazy" decoding="async" width="602" height="1306" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2MDIiIGhlaWdodD0iMTMwNiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>釜山塔憑門票掃描 QRCord 可以玩解謎遊戲，解出所有密碼可以得到一個小獎品。(密碼後面我有寫)</p>

<p><img src="/assets/8ace34a1a3d8/1*JnjlJkLecp-f0MseLSVbwQ.webp" alt="" loading="lazy" decoding="async" width="949" height="1148" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NDkiIGhlaWdodD0iMTE0OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>上來已經有很多人在等了。</p>

<p><img src="/assets/8ace34a1a3d8/1*Kmf1d-_Vm4iMYNKmF1sb_g.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*lIwknEUuQ6jAeggYxZZegA.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>19:45 的釜山晚霞。</p>

<p><img src="/assets/8ace34a1a3d8/1*iYaorZ3UxxKx0uFa5qpe6A.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>20:00/20:10…有光雕投影煙火，效果普普。</p>
<h4 id="2000-釜山夜景">20:00 釜山夜景</h4>

<p><img src="/assets/8ace34a1a3d8/1*DH6uu6N5y8MvhoUoJYt-GQ.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*9WInxaDdY5aNuL-5CcyuKQ.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<h4 id="-2010-下塔離場">~= 20:10 下塔離場</h4>

<p><img src="/assets/8ace34a1a3d8/1*SYJBo1VeGTPz0_-ba7Okbw.webp" alt="" loading="lazy" decoding="async" width="956" height="1223" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NTYiIGhlaWdodD0iMTIyMyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*bpHpZ2j7hlGjfUqN24QkXQ.webp" alt="" loading="lazy" decoding="async" width="918" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MTgiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*trPGYy37LVKvPbDFepZS6g.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>離場之前還有很多可以拍照的點。</p>
<h4 id="-2015-兌換小紀念品">~= 20:15 兌換小紀念品</h4>

<p><img src="/assets/8ace34a1a3d8/1*GIz7xVJewRWy35RBleksMw.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*kUe5ECb7bsWp_Qwh2hlEug.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>下山之前記得去外面無人售票處 (Ticket Box) 機台：</p>
<ul>
  <li>輸入密碼 ： <code class="language-plaintext highlighter-rouge">19731121</code> (釜山塔建塔日期)</li>
  <li>就能持憑證去外面藍色房子的紀念品中心(不是釜山塔一樓那個)兌換小獎品。</li>
</ul>

<h4 id="小紀念品是一張小卡有多種樣式可以選我選了一張透明的釜山塔">小紀念品是一張小卡，有多種樣式可以選，我選了一張透明的釜山塔：</h4>

<p><img src="/assets/8ace34a1a3d8/1*jPcatm0ieKrZEYkk75hpmA.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*gpu_v-fM13P4wilbaGzEwA.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>一路從龍頭山公園下山。</p>

<p><img src="/assets/8ace34a1a3d8/1*oDNlLOobzNrq0E5Hue-GHA.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/8ace34a1a3d8/1*y3olhVcaCU5pYyDz3U8lcQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<blockquote>
  <p><strong><em>這邊有電扶梯啊啊啊啊！！(往上，下山沒有、有一段在整修)</em></strong></p>
</blockquote>

<h4 id="2030-回到--龍頭山紀念公園入口-yongdusan-media-park-entrance">20:30 回到 — <a href="https://naver.me/GDL5X5TV" target="_blank">龍頭山紀念公園入口 Yongdusan Media Park Entrance</a></h4>

<p><img src="/assets/8ace34a1a3d8/1*G_jAYR7TS8ESc3ddWf-CpA.webp" alt="" loading="lazy" decoding="async" width="961" height="1195" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NjEiIGhlaWdodD0iMTE5NSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>記得從這裡去釜山塔比較近。</p>

<p><img src="/assets/8ace34a1a3d8/1*gavdcweW2vtAfov9NgR9_w.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*QXT1p_8zTY09QXle3f8JTA.webp" alt="" loading="lazy" decoding="async" width="947" height="1197" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NDciIGhlaWdodD0iMTE5NyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*QdwuA16DAl-Abv2AqrjJEw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>在商店街逛了逛，這裡有一家 HBAF 杏仁果專賣店，應該是口味最齊全的地方；買了 Yellow 組合包(裡面是小包的)跟一些其他特殊(跳跳糖，真的會跳、辣炒年糕)、熱門口味(蜂蜜、Oreo、玉米)回家當伴手禮。</p>
<h4 id="2045-回到-biff-廣場買宵夜">20:45 回到 —BIFF 廣場買宵夜</h4>

<p><img src="/assets/8ace34a1a3d8/1*hGy9kfVNX80z2gYTpXsO1A.webp" alt="" loading="lazy" decoding="async" width="958" height="1196" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NTgiIGhlaWdodD0iMTE5NiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*I2_vOk86JV7s2IcRV4nwAw.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>這邊有很多攤販、地板有明星的手印。</p>
<h4 id="瑪格利酒-bhc-炸雞--bhc-chicken-busan-nampo-branch-就在-biff-廣場-">瑪格利酒、 <a href="https://naver.me/xww9nJx2" target="_blank">BHC 炸雞 — BHC Chicken Busan Nampo Branch</a> (就在 BIFF 廣場) 👍👍</h4>

<p><img src="/assets/8ace34a1a3d8/1*ps1VVbDmZzRPf5CZTrzdtQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*qf2-1dMVz6KrPmTQRXO0Pw.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*oEo33HTYo8UdkOZ60P3Gxw.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*C1NraGTlXNjmzfeCg7-5RA.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>最後買回去吃了：</p>
<ul>
  <li>瑪格利酒：類似小米酒、酒釀，很讚。</li>
  <li>BHC 無骨炸雞：調味跟雞肉都很嫩、很好吃。</li>
</ul>

<h4 id="0000-晚安南浦">00:00 晚安南浦</h4>

<p><img src="/assets/8ace34a1a3d8/1*Hh1Rxl4e47yN-iFuGxer9g.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>大約 00:00 後攤販就都清空了。</p>
<h4 id="day-5-0802-六-樂天百貨影島大橋開橋只有週六有">Day 5 08/02 (六) 樂天百貨、影島大橋開橋(只有週六有)</h4>
<h4 id="1045-樂天百貨光復店樂天超市--lotte-dept-store-gwangbok-branch-aqua-mall">10:45 <a href="https://naver.me/xWziztAc" target="_blank">樂天百貨光復店、樂天超市 — Lotte Dept. Store Gwangbok Branch Aqua Mall</a></h4>

<p><img src="/assets/8ace34a1a3d8/1*cCougQTjZSMEe_nhgbQF2w.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*CsRLupeh763WBqVnJ8pQDA.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>大約 10:XX 出門，樂天百貨 10:30 開始營業，慢慢走去。</p>

<p><img src="/assets/8ace34a1a3d8/1*9fHXV2Og6JodL5Pjad3JDg.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>先去 B1 美食街咖啡廳隨便吃個東西當早餐。(普普，咖啡是 Lungo)</p>
<h4 id="1130-開始百貨公司亂逛">11:30 開始百貨公司亂逛</h4>

<p><img src="/assets/8ace34a1a3d8/1*A75a3-GEjeoMSxnuqlFt-w.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*Nk9RtTQ2z7ruxWDgKhhIrg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/8ace34a1a3d8/1*-usgnffatzz53RtQHbC6PA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*J957_He9MA3RC16vdzSjbA.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<ul>
  <li>去 Kodak 店買了幾件衣服(平均比台灣代購便宜 NT$300–$500)
當店直接退稅。</li>
  <li>買了支手持風扇
憑收據去 B1 美食街找退稅機列印退稅單，最後再去機場退現金。(大概能退 NT$60)</li>
</ul>

<p><img src="/assets/8ace34a1a3d8/1*QxFRNLVX2vM0ISZf3vM2-g.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*TxOTSeqke8P2TE0MWWgiPA.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<ul>
  <li>百貨這邊 11 點開始每小時都有水舞秀，蠻精彩的</li>
</ul>

<h4 id="1230-b1-downtown-漢堡">12:30 B1 Downtown 漢堡</h4>

<p><img src="/assets/8ace34a1a3d8/1*i3upBh9hp-ZjdfMxeskBnQ.webp" alt="" loading="lazy" decoding="async" width="553" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1NTMiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*oK7jL0JkiRD69PAlv-fO5Q.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*1BHaKNhjT3BIOkW4-qnE4g.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>逛累了，中午時分就去 B1 吃 Downtown 漢堡；道理我都懂，但是美式漢堡也有提供泡菜啊！</p>

<p><img src="/assets/8ace34a1a3d8/1*TvleF3CnUri6-JtSSah9mg.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*czdiuWsarXuyo7IfATd7hg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/8ace34a1a3d8/1*SCYBfHs65_54TAWjMMssnQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>吃飽到百貨公司頂樓觀景台晃晃，(超曬、超熱)。</p>
<h4 id="1330-走出百貨到外面影島大橋等開橋">~=13:30 走出百貨到外面影島大橋等開橋</h4>

<p><img src="/assets/8ace34a1a3d8/1*SY6E9kWJ4lQ4WfXaX9Lvww.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/8ace34a1a3d8/1*PB-U5oD_Iw1PaNIj9vu1Rw.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*ml5meRbBGO-wiXskpKv7FQ.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>外面很曬，越接近 14:00 聚集的人越來越多；此時 <strong>有一個阿珠嬤走過來跟我們說 8/2–30 因為太熱改晚上 20:00！！震驚！！</strong> 然後大家就開始人傳人說改到 20:00，大家開始慢慢散場。</p>

<p><img src="/assets/8ace34a1a3d8/1*GQondkcuwv5QwfzkKZ7QPw.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*Lyms5xvH6lwxp0hPFF2DuA.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>後來才在欄杆上看到公告布條。(左邊看板是例行時間)</p>
<h4 id="1350-回樂天百貨樂天-mart-繼續亂逛">13:50 回樂天百貨、樂天 Mart 繼續亂逛</h4>

<p><img src="/assets/8ace34a1a3d8/1*O6oXHk-CkB8vJ-vxvVIfSQ.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*0fkOrRbJw84kNZ_Eu_zuZA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*e2LIsRj5B6KtM1tS_Pvldw.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*ndyL7-V4wbHHLtu5uYqxlQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*Vi3D5-dWbMN0XVogyhAYBA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>樂天超市也有賣一些伴手禮跟保養品(就是家樂福)。</p>
<h4 id="b1-退稅機台">B1 退稅機台</h4>

<p><img src="/assets/8ace34a1a3d8/1*VJW26Pss8GIKP_EdOKhPbQ.webp" alt="" loading="lazy" decoding="async" width="930" height="1147" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MzAiIGhlaWdodD0iMTE0NyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*QpYPLAIoTGVwYHZoYljzGQ.webp" alt="" loading="lazy" decoding="async" width="948" height="1199" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NDgiIGhlaWdodD0iMTE5OSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>找了好久才找到退稅機台，在鼎泰豐旁的小路。</p>
<ul>
  <li>如果不是立即退稅的就要到這邊刷單據條碼換退稅單，最後到機場再退錢。(有點麻煩)</li>
</ul>

<blockquote>
  <p><strong><em>才知道韓國是有分不同退稅機構的 Global Blue、GLOBAL TAX FREE、Easy Tax Refund、CubeRefund、eTAX FREE，只能使用對應的退稅機台</em></strong> <em>；因為我在 Olive Young 有看到能退現金的機台但是是 GLOBAL TAX FREE 的，我的單字是 CubeRefund，還是只能去機場退。</em></p>
</blockquote>

<h4 id="1635-回飯店休息">16:35 回飯店休息</h4>

<p><img src="/assets/8ace34a1a3d8/1*ngRJ5SY55QsOVeEuesGIXg.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*IoTCxYVKEGU4JpUwsCMEUQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/8ace34a1a3d8/1*hA48uOURihuc-sbE-Qrn3Q.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>逛累了就去 B1 買了韓國的紅豆和桃果子跟老配方自己調的超商手搖(冰杯+香蕉牛奶+無糖黑咖啡)，走地下街回去飯店休息。</p>
<h4 id="1900-出飯店去-biff-olive-young-逛逛">19:00 出飯店去 BIFF Olive Young 逛逛</h4>

<p><img src="/assets/8ace34a1a3d8/1*LVHpdVT1I-X7_BStMRVfnQ.webp" alt="" loading="lazy" decoding="async" width="932" height="1192" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MzIiIGhlaWdodD0iMTE5MiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>在海雲台都沒逛 OY，剛好有時間先來看看，這家店很大店員很多也都很親切。</p>

<p><img src="/assets/8ace34a1a3d8/1*urwdAXWOvdtMGRzUZ2cMWw.webp" alt="戰利品" loading="lazy" decoding="async" width="1400" height="1049" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNDkiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>戰利品</p>
<h4 id="1940-到達影島大橋等開橋">19:40 到達影島大橋，等開橋</h4>

<p><img src="/assets/8ace34a1a3d8/1*JG_o0tkChN-UKCtRdkx8Mw.webp" alt="" loading="lazy" decoding="async" width="1200" height="872" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg3MiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*ZOT3YuHUnfvKjglbTllFRQ.webp" alt="" loading="lazy" decoding="async" width="896" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4OTYiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>橋下這邊有紀念照合成的攤位。</p>

<p>大約 19:50 就會開始交通管制。</p>
<h4 id="2000-開橋">20:00 開橋</h4>

<p><img src="/assets/8ace34a1a3d8/1*Zpgsucjjm5BNpq_D4zfh1A.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*NTI7_uUkEmOE2SLCN3fclQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>先在橋下看，大約 20:05 開的差不多在移動上去。</p>

<p><img src="/assets/8ace34a1a3d8/1*h8neY7OqYx6WG5QVgxAEGQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*n_oF3q1EJ4xy7iF-_mNIYA.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>有投影介紹影島大橋歷史，不過可能是第一天投影機角度有問題，看不太清楚。</p>
<h4 id="2015-橋重新下降恢復通車">20:15 橋重新下降，恢復通車</h4>

<p><img src="/assets/8ace34a1a3d8/1*CsyWpHHwp2x8lLZfbvXvvA.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/8ace34a1a3d8/1*gV_RKoppNxGGwl8BMErUkw.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<h4 id="2030-吃晚餐--ton-black--tempura-nine">20:30 吃晚餐 — <a href="https://naver.me/Fm3VOYcH" target="_blank">Ton Black &amp; Tempura Nine</a></h4>

<p><img src="/assets/8ace34a1a3d8/1*VEY9B-esGVFWEHU-1y5OoA.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<ul>
  <li>晚上走回飯店的路上隨意找了家豬排飯店吃吃 (豬排一樣很嫩，好吃)</li>
  <li>一份實刷 NT$366</li>
</ul>

<h4 id="2150-回飯店休息">~=21:50 回飯店休息</h4>

<p><img src="/assets/8ace34a1a3d8/1*fd1YC6o2Gytnh_shoUXxdg.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*O-5YaMfjKPHJ3oCMVfn1UQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<h3 id="day-6-0803-日-樂天百貨富平罐頭市場白淺灣文化村福泉寺夜景">Day 6 08/03 (日) 樂天百貨、富平罐頭市場、白淺灣文化村、福泉寺夜景</h3>

<blockquote>
  <p><strong><em>今天都在亂走消磨時間，內容不建議參考，純紀錄。</em></strong></p>
</blockquote>

<h4 id="1000-出門先去吃--egg-drop">10:00 出門先去吃 — <a href="https://naver.me/5gYr3qdc" target="_blank">Egg Drop</a></h4>

<p><img src="/assets/8ace34a1a3d8/1*Kh1xpKnZV-MMtVanIicSyA.webp" alt="" loading="lazy" decoding="async" width="942" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NDIiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<ul>
  <li>位置在樂天百貨對面的商店街。</li>
</ul>

<p><img src="/assets/8ace34a1a3d8/1*aRSQiMUWKNfjDtDVZ8V3oQ.webp" alt="" loading="lazy" decoding="async" width="945" height="1196" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NDUiIGhlaWdodD0iMTE5NiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*MnFVcEjz135UauqnyR5H3Q.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*BD26TaxsDjAO3wb9rbeFwQ.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<ul>
  <li>一樓機台點完餐拿號碼牌上二樓找位子 (應該要先上去看有沒有位子)</li>
  <li>大約等了 20 分鐘</li>
  <li>點了一份火腿起司歐姆蛋吐司＋薯餅＋冰咖啡：$8,400 韓元</li>
</ul>

<blockquote>
  <p><em>是不錯，但我覺得台灣有些早午餐比這更好吃。</em></p>
</blockquote>

<h4 id="1100-吃飽再出發">11:00 吃飽再出發</h4>

<p><img src="/assets/8ace34a1a3d8/1*p3YDCVRrvV7QQgx6UomwYA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*MQgkZeRyQFfcU5Zti1s4eA.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/8ace34a1a3d8/1*zl6x8Pt3pxiIR94C8KwkIg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/8ace34a1a3d8/1*zB10iTHzqS8mD4T678Lgnw.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>往樂天百貨的路上有一家「Big Shop」釜山紀念品店專賣店，有很多 Boogi 跟釜山形象周邊商品。</p>
<h4 id="樂天百貨光復店樂天超市--lotte-dept-store-gwangbok-branch-aqua-mall"><a href="https://naver.me/xWziztAc" target="_blank">樂天百貨光復店、樂天超市 — Lotte Dept. Store Gwangbok Branch Aqua Mall</a></h4>

<p><img src="/assets/8ace34a1a3d8/1*IlBfn7L4in16Sqh0S9u89A.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>無聊繼續逛樂天百貨。</p>
<h4 id="1145-b1-無國食卓-蒸食料理-lotte-dept-store-gwangbok-branch">11:45 B1 <a href="https://naver.me/5YSwvcB0" target="_blank">無國食卓 蒸食料理 Lotte Dept. Store Gwangbok Branch</a></h4>

<p><img src="/assets/8ace34a1a3d8/1*OebNrqD4ge6viEehCRbprA.webp" alt="" loading="lazy" decoding="async" width="1200" height="851" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg1MSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*TYSdoPRJBzjaNXE9MImmng.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*M1rt5vPWP34qVzr4Rwq8OQ.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>吃了好幾天重口味食物，午餐想吃清淡一點。</p>
<ul>
  <li>飯要另外加點。</li>
  <li>本身沒什麼味道。</li>
</ul>

<p>吃飽後又逛了一下。</p>
<h4 id="1250-離開樂天百貨前往-天空之眼展望台--yeongju-haneul-nun-observatory">12:50 離開樂天百貨，前往 <a href="https://naver.me/xYvTULtB" target="_blank">天空之眼展望台 — Yeongju Haneul Nun Observatory</a></h4>

<p>沒事附近隨便晃晃，查到一個 <a href="/posts/z-度旅行遊記/九州自由行攻略-釜山新山茶花號郵輪入境日本博多-深入由布院-大分-福岡等地-cb65fd5ab770/">去年沒去到的展望台</a> ，有公車直達(21 分鐘)於是就去了。</p>

<p><img src="/assets/8ace34a1a3d8/1*vzM0d49ldD5u40myXdTgug.webp" alt="" loading="lazy" decoding="async" width="382" height="721" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzODIiIGhlaWdodD0iNzIxIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><img src="/assets/8ace34a1a3d8/1*T8YCaXpAOoHzt08VBNO2zQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*zlS5hxZG5BXyZNASUNzXMA.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<ul>
  <li>釜山的公車幾乎都是全新的電車、冷氣很涼</li>
  <li>途經 <a href="/posts/z-度旅行遊記/九州自由行攻略-釜山新山茶花號郵輪入境日本博多-深入由布院-大分-福岡等地-cb65fd5ab770/">去年路過的點：釜山站</a></li>
</ul>

<h4 id="1300-天空之眼展望台--yeongju-haneul-nun-observatory">13:00 <a href="https://naver.me/xYvTULtB" target="_blank">天空之眼展望台 — Yeongju Haneul Nun Observatory</a></h4>

<p><img src="/assets/8ace34a1a3d8/1*clTNOo-ZktYUKpKKh75heg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/8ace34a1a3d8/1*EQMOhh1KhjP0i9bwys0YuQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>公車站下車後走到對面往下就能看到了。</p>

<p><img src="/assets/8ace34a1a3d8/1*YDfNJ4--5A0Kt0fiScCvVQ.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*fzBzk65b4cALhHQ3_Ej2ow.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>這邊的景色很好，可以眺望整個釜山南浦。</p>
<h4 id="下山去富平罐頭市場--先去買同事推薦的-the-liter-手搖杯">下山去富平罐頭市場 — <a href="https://naver.me/FMT6cQrZ" target="_blank">先去買同事推薦的 The Liter 手搖杯</a></h4>

<p><img src="/assets/8ace34a1a3d8/1*-B44RvmQkP8ROxbo_ycOpA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<ul>
  <li>只推薦：藍莓優格冰沙(Yogurt Smoothie — Blueberry)，有 600ML or 1,000ML 兩種大小；好喝解暑，也不會太甜覺得越喝越渴，價格約 $4,300 韓元。</li>
</ul>

<h4 id="富平罐頭市場"><a href="https://naver.me/Goi5D3F6" target="_blank">富平罐頭市場</a></h4>

<p><img src="/assets/8ace34a1a3d8/1*V2RiLAAeaBdJUF4RVKtZCg.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*o9T02VtSuMwlGadw7Mq1Jg.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*9Zbj66ueqAwQvyD6OTvZqQ.webp" alt="" loading="lazy" decoding="async" width="940" height="1196" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NDAiIGhlaWdodD0iMTE5NiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>罐頭市場內有很多乾糧、雜貨、海鮮攤販(像菜市場)，範圍很大。</p>
<h4 id="富平罐頭市場--milgot-紅豆奶油艾草糯米糕-">富平罐頭市場 — <a href="https://naver.me/GEiuXPsf" target="_blank">Milgot</a> 紅豆奶油艾草糯米糕 👍</h4>

<p><img src="/assets/8ace34a1a3d8/1*Wj_6WUHzrJCVCSyo-gsGSw.webp" alt="" loading="lazy" decoding="async" width="951" height="1180" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NTEiIGhlaWdodD0iMTE4MCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*2Nj6AKmSZkzlQ5SO013W7w.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*R14q-E0U_yWC9iXarLhRZg.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>應該是罐頭市場最有名的店，特色是 — 紅豆奶油艾草糯米糕。</p>
<ul>
  <li>因為不會韓文所以直接拍照只給店員看就要這個</li>
  <li>店員說回去要冰，吃之前退冰半小時到一小時最好吃</li>
  <li>另外還買了一個鹹的麵包，也不錯！</li>
</ul>

<p><img src="/assets/8ace34a1a3d8/1*qm0Wh7MPbrWfst0Y_4eF-A.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/8ace34a1a3d8/1*GJ8R6Is85xYOA8HOlA_bQg.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>後來帶回飯店吃，艾草糕糯糯的口感配上黃豆粉跟紅豆奶油內餡，我覺得蠻特別的，稍微有一點甜；如果有路過可以試試！</p>

<p><img src="/assets/8ace34a1a3d8/1*XQYb4xKnzk0rEMbMJWlglQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/8ace34a1a3d8/1*cwJikydjyNJW3SDbudKmOA.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>另外又再買了一盒 <a href="https://naver.me/xww9nJx2" target="_blank">BHC 炸雞</a> 回去當晚餐吃。</p>
<h4 id="1830-重新出發">18:30 重新出發</h4>

<p>傍晚休息完又再出門，主要是要去福泉寺拍夜景，先去白淺灣文化村看看。</p>
<h4 id="1845-白淺灣文化村">18:45 <a href="https://naver.me/FgTvHYSL" target="_blank">白淺灣文化村</a></h4>

<blockquote>
  <p><em>這邊最有名的景點是 — <a href="https://naver.me/52chRHd4" target="_blank">邊看風景邊喝咖啡邊泡足浴 — VIEW, FOOT BATH CAFE</a> 足浴咖啡廳。</em></p>
</blockquote>

<p><img src="/assets/8ace34a1a3d8/1*wFYAic5cJEcssp2DziS85Q.webp" alt="" loading="lazy" decoding="async" width="945" height="1234" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NDUiIGhlaWdodD0iMTIzNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*9xd7-RARtJnwkQUwLqoz2w.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*_bg1OXnenjgxXpfoX3xwog.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>但是因為時間晚了，所以只走馬看花；靠海岸這邊有很多咖啡廳跟小店，但不知道是不是因為時間晚了沒什麼人，下方海岸線步道在維修無法下去走；今天天氣陰陰景色不佳。</p>
<h4 id="1900-前往-福泉寺--bokcheonsa-temple">19:00 前往 <a href="https://naver.me/5L7oHSSB" target="_blank">福泉寺 — Bokcheonsa Temple</a></h4>

<p>離開白淺灣後就要往上前往福泉寺。</p>

<p><img src="/assets/8ace34a1a3d8/1*kCU_DP31_Q8oVxfZPA7hOw.webp" alt="" loading="lazy" decoding="async" width="1136" height="908" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMTM2IiBoZWlnaHQ9IjkwOCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*7BgsQXSUyo7X3sQnbDpgCQ.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>原本看 Map 說公車只搭一站，想說用走的就好，走幾步之後就放棄了；地圖寫公車 1 站(1 分鐘)；實際走應該要 20 分鐘 (全是很抖的山坡路)。</p>
<ul>
  <li>Yeongdogu 1 或 5 都可</li>
</ul>

<p><strong>只搭一站到下一站 <a href="https://naver.me/xzlLlZhM" target="_blank">Sinseon Elementary School</a> ，剩下只能用走的：</strong></p>

<p><img src="/assets/8ace34a1a3d8/1*O0Q9eorhHVOa5e9W69pK2A.webp" alt="" loading="lazy" decoding="async" width="688" height="310" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2ODgiIGhlaWdodD0iMzEwIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>下車後傻了，依然是很抖的山坡路，從這邊一路往上走：</p>

<p><img src="/assets/8ace34a1a3d8/1*KF-K6yfIdIQ_oxTPMFu6TA.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*A232HHlbcIoIpCEE770RhA.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>經過國小之後再往上就能看到福泉寺入口：</p>

<p><img src="/assets/8ace34a1a3d8/1*8ISDSkUNQK1J6SJokF48Zg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>這裡才是開始，要再一路爬很抖的山坡路往上到底才是福泉寺：</p>

<p><img src="/assets/8ace34a1a3d8/1*f7K27vuhRDimn9H-2KB6Hw.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/8ace34a1a3d8/1*DIL09kj3M9GtyrJSHP9G0A.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/8ace34a1a3d8/1*JBWBSHF6GYn6fIuWPd4Dsw.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<blockquote>
  <p><strong><em>這裡會有車通行，要注意安全 ⚠️</em></strong></p>
</blockquote>

<h4 id="1925-抵達福泉寺">19:25 抵達福泉寺</h4>

<p><img src="/assets/8ace34a1a3d8/1*a9Rw-kNrof1PF3jtwV5Y1A.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*ojagYvbJbUhKBz1JOVOlwA.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<blockquote>
  <p><em>大約 19:25 才走到福泉寺（差點累死），Map 寫 10 分鐘，實際全是上坡路走+休息大概花了 25 分鐘。</em></p>
</blockquote>

<p>但更悲劇的事是 — 他關門了！他關門了！他關門了！</p>

<p>沒錯，福泉寺不太算公開的觀光景點，他是寧靜的寺院，時間到門就關了；聽同事說大約 18:00 後就只進不出了，不會特別趕人，但是超過時間關門就不給進去了。</p>

<p><img src="/assets/8ace34a1a3d8/1*DgPxNoCjrJWvL1uURfNIJA.webp" alt="" loading="lazy" decoding="async" width="944" height="1248" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NDQiIGhlaWdodD0iMTI0OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*VsLe3FUcx9XaJ8WTVWjlIg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>悲劇，只能在站外面閣樓看景，後來也有一些慕名而來的人也是都站在這。</p>

<blockquote>
  <p><strong><em>不過住持看我們大粒汗小粒汗，就從裡面丟了兩罐冰水給我們，太感謝了。</em></strong></p>
</blockquote>

<p><img src="/assets/8ace34a1a3d8/1*5lpfrhdVVFrsN-O0qrTmQw.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*lEg8_dHkPXgVIPbMLGpAVQ.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>從這邊看市區的景色視角。</p>

<p><img src="/assets/8ace34a1a3d8/1*fFLE2v0oSj9z5snHg_KINQ.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>最後隨手拍了張夜景就走下山了，當初是看其他部落客拍的圖才想來的：</p>

<p><img src="/assets/8ace34a1a3d8/1*gcXOAkYiOkfJZLVpHVqQnw.webp" alt="真的進去拍應該長這樣" loading="lazy" decoding="async" width="789" height="743" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3ODkiIGhlaWdodD0iNzQzIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><a href="https://judyer.com/bstp/" target="_blank">真的進去拍應該長這樣</a></p>

<p>下山一路下坡，一片黑：</p>

<p><img src="/assets/8ace34a1a3d8/1*uYq1hyMs27u89dlhoSsz7A.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*qAWrK94KUe0KxtQDv0kAsQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/8ace34a1a3d8/1*KQ_lF81lFAdeVBQ5FiexTQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>是個很安靜的村莊，走下來就搭公車回札嘎其站了。</p>

<p><img src="/assets/8ace34a1a3d8/1*X0odkWacFr5MXbc1iBV-tg.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*2wUeYbwRYkMnLrNhrTMWLg.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*3nwYcNLllFSmQ8zjmiKxjA.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>回飯店前買了 BIFF 廣場蠻多人排的 <a href="https://naver.me/5eZTZe4y" target="_blank">種子糖餅</a> ，我覺得很油很甜不怎麼樣。</p>

<p><img src="/assets/8ace34a1a3d8/1*dPAGNfr1hlXfTu3CnFl7ng.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>大約 22:00 釜山開始下雨！還好我們旅程快結束了。</p>
<h3 id="day-7-0804-一-釜山西面新世界百貨韓牛">Day 7 08/04 (一) 釜山西面、新世界百貨、韓牛</h3>

<p>早上出門移動到西面站。</p>
<h4 id="1045-抵達西面站">10:45 抵達西面站</h4>

<p><img src="/assets/8ace34a1a3d8/1*o5yuGlxr94cLNs65fiuL5A.webp" alt="" loading="lazy" decoding="async" width="1400" height="984" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9Ijk4NCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>到了之後想說直接在地鐵站寄物，其他地方：樂天百貨也能寄放行李。</p>

<blockquote>
  <p><strong><em>這邊寄放行李很貴：早上 10:45 寄放，晚上 20:10 取回。</em></strong></p>
</blockquote>

<blockquote>
  <p><strong><em>被收了 $6,000 + $7,000 韓元 (約 NT$280)。</em></strong></p>
</blockquote>

<p><img src="/assets/8ace34a1a3d8/1*Wc20JDECq1yHn3CgB1qXFA.webp" alt="" loading="lazy" decoding="async" width="1400" height="998" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9Ijk5OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>這站地下街也有一家 Olive Young，再去補貨了一波。</p>
<h4 id="1100-午餐-韓式-刀削麵--gijang-sonkalguksu">11:00 午餐 韓式 <a href="https://naver.me/Fr0l0gR5" target="_blank">刀削麵 — Gijang Sonkalguksu</a></h4>

<p><img src="/assets/8ace34a1a3d8/1*sd_qEyJXkxRxFOBwd40Tvg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/8ace34a1a3d8/1*GUBEh-TqX2boCuU2I78HKA.webp" alt="" loading="lazy" decoding="async" width="1200" height="846" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg0NiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>逛完接近中午，到附近的市場吃刀韓式削麵。</p>

<p><img src="/assets/8ace34a1a3d8/1*eELPbEa7ehdgUH6X47-C9A.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>菜單選擇很單純，就只有刀削麵跟海苔捲。</p>

<p><img src="/assets/8ace34a1a3d8/1*EsG2Zr3hz1BvZ6ZYMtfplA.webp" alt="" loading="lazy" decoding="async" width="1200" height="861" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg2MSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*B3iHfLVRtZ7zTuzh5pmOag.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<blockquote>
  <p><em>點了海苔捲分著吃跟我點了刀削冷麵，麵的口感很勁道，冰涼爽口；缺點是沒什麼配料(肉)，純碳水吃起來很單調。</em></p>
</blockquote>

<p>吃完之後走回樂天百貨。</p>
<h4 id="the-liter-seo-myeon-medical-street-branch"><a href="https://naver.me/FytxtsbC" target="_blank">The Liter Seo-myeon Medical Street Branch</a></h4>

<p><img src="/assets/8ace34a1a3d8/1*2yLbWMPpyCtAruEp2Cz46g.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/8ace34a1a3d8/1*i4mbPG5JVPgWfVZvr40MJg.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>再去買了一杯 Liter 藍莓冰沙優格解膩。</p>
<ul>
  <li>這家的阿珠媽很熱情</li>
</ul>

<h4 id="樂天百貨--釜山總店-lotte-dept-store-busan-main-branch"><a href="https://naver.me/GA868sJB" target="_blank">樂天百貨 — 釜山總店 Lotte Dept. Store Busan Main Branch</a></h4>

<p><img src="/assets/8ace34a1a3d8/1*VmJEjJZMfCoxu_HLUxnTIA.webp" alt="" loading="lazy" decoding="async" width="952" height="1186" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NTIiIGhlaWdodD0iMTE4NiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*JnKRh6VL4zQAj5DN0QjOPw.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/8ace34a1a3d8/1*lBdiaq_smyUqUNRW1ZTLYg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>我覺得很難逛，或是說比昨天的樂天百貨-光復店還難逛，樓層少商家少； <strong>唯一優點是樓上有很大的公共休息區，可以充電休息。</strong></p>

<p><img src="/assets/8ace34a1a3d8/1*FIKndhK0XDcFCTNeGi9myA.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*HrvD0lkdT605U-UVMyqdnw.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>這裡也有一家釜山紀念品店，但是東西比南浦的少很多。</p>

<p><img src="/assets/8ace34a1a3d8/1*hSXu5dj5gWdLB99e5TaHXA.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>逛完樂高店，不知道要去哪就改坐公車去逛新世界百貨了。</p>

<blockquote>
  <p><em>西面另一家 NC百貨已歇業， <strong>其他要逛可能可以去 田浦咖啡街 那邊有很多小店(類似東區的感覺)，但是對男生來說興趣普普。</strong></em></p>
</blockquote>

<h4 id="busan-is-good">Busan is good!</h4>

<p><img src="/assets/8ace34a1a3d8/1*FtpmNOEbpZm_wqMEE0XZXA.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<blockquote>
  <p><em>在釜山到處可以看到「Busan is good」的標語，確實旅遊體驗下來真的不錯，公車超級多、交通方便、公車很新很涼、物價便宜東西好吃。</em></p>
</blockquote>

<h4 id="新世界shinsegae百貨--shinsegae-centum-city-"><a href="https://naver.me/xfYRYAfu" target="_blank">新世界(SHINSEGAE)百貨 — Shinsegae Centum City</a> 👍👍👍</h4>

<p>前幾天來只去了 Spa Land 沒有逛到，這次來補逛。</p>

<p><img src="/assets/8ace34a1a3d8/1*_Z3co8etvvt8uJ6IW2WBZg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/8ace34a1a3d8/1*U0WsMct8QALuKAQXMVp-fQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<blockquote>
  <p><strong><em>新世界百貨超級大(金氏世界紀錄第一大)</em></strong> <em>，有很多東西可以逛。</em></p>
</blockquote>

<blockquote>
  <p><em>逛不夠隔壁也有 <a href="https://naver.me/5b0d0jtI" target="_blank">樂天百貨 — Lotte Dept. Store Centum City Branch</a> 。</em></p>
</blockquote>

<p><img src="/assets/8ace34a1a3d8/1*QGugJUaJZPoHgPEOElZ4uA.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/8ace34a1a3d8/1*wFK6WxUwNb_DeaQyLu_dtA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>由兩棟建築組成，每一棟都有好幾層，每一層的面積也都很大；順手紀錄了一下釜山 La Lebo(在一樓) 的價格。</p>

<p><img src="/assets/8ace34a1a3d8/1*yCeQQeere4hq9i_JBXxVug.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/8ace34a1a3d8/1*HCpT3BycowwHj4YH7T3kZg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>本棟樓頂有 ZOORAJI 免費的恐龍公園可以遛小孩。</p>

<p><img src="/assets/8ace34a1a3d8/1*oFC6DVRe9pA7T-R5TUG0HA.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/8ace34a1a3d8/1*ZRWtwouGGpGv9F2cvFCz_A.webp" alt="" loading="lazy" decoding="async" width="922" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MjIiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>隨意逛了逛，基本上所有品牌、所有類型的櫃位都有。</p>

<p><strong>走到另一棟的 1 樓發現有 Shake Shack 那我還不吃爆：</strong></p>
<h4 id="shake-shack-busan-centum"><a href="https://naver.me/GCgugmaP" target="_blank">SHAKE SHACK BUSAN CENTUM</a></h4>

<p><img src="/assets/8ace34a1a3d8/1*kuCM6Mh79ZFYGXtuk2qv7Q.webp" alt="" loading="lazy" decoding="async" width="1200" height="841" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg0MSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*w8wX3ItxckiitSpt-SPH9Q.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>點了這個有脆脆的雞肉套餐，Shake Shack 除了招牌我覺得雞肉鮮嫩多汁也很好吃。</p>

<p><img src="/assets/8ace34a1a3d8/1*aVPzCfKsq8FJ5xpyl0Wleg.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*3WaFG6qy3Eg9mm9BvXbLxg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>呼叫器好像有點問題，其實早做好了只是沒響。</p>

<blockquote>
  <p><em>懷念的好滋味。</em></p>
</blockquote>

<blockquote>
  <p><em>遊記: <a href="https://zhgchg.li/posts/aacd5f5cacd1/#1200-shake-shack" target="_blank">大阪 Shack Shake</a> / <a href="https://zhgchg.li/posts/b7e7c0938985/#central-world" target="_blank">曼谷 Shake Shack</a></em></p>
</blockquote>

<h4 id="1700-離開新世界百貨">~=17:00 離開新世界百貨</h4>
<h4 id="1745-回到田浦站西面">~=17:45 回到田浦站(西面)</h4>

<p>準備晚上吃韓牛。</p>

<p><img src="/assets/8ace34a1a3d8/1*A18rMP1xmxzXPrQ9RPTkDQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/8ace34a1a3d8/1*ytbdvCf_b5ARZf_PyPolmQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/8ace34a1a3d8/1*vS3dZ4vmhuLZLOkifzpZ4Q.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/8ace34a1a3d8/1*6cr53hsYgiwQJNrgtETIPQ.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*3Sg_E9SpDVQi2sIZs5HR7w.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>田浦站出來就能看到往田浦咖啡街的指示牌，出來這邊有一家獨棟的 Olive Young。</p>

<p>田浦這塊有很多咖啡店、小店，有點像東區的感覺。</p>
<h4 id="花肉店-田浦店--flowerbeefhouse"><a href="https://naver.me/Gj7G7BxZ" target="_blank">花肉店 田浦店 — flowerbeefhouse</a></h4>

<p><img src="/assets/8ace34a1a3d8/1*C7814gYWbA1ajdvCX24HBA.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*6lXBVu2a8ooPredaeD98YA.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>有點東區網美店的感覺，還好有在 <a href="https://www.catchtable.net/zh-TW/shop/busan_kosaljip?operationType=REMOTE_WAITING_GLOBAL" target="_blank">CatchTable 提前預約</a> 不然現場沒位子。</p>

<p><img src="/assets/8ace34a1a3d8/1*0MfOvlWLctZiRYYM9YI2Ow.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*dODWMoS7WzgH-siJ6pjpbA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*IknycW3IPMrRvAW5jl0aZw.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/8ace34a1a3d8/1*nXobUwdMajACPXrjnt4M8w.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*MoSNzb94xyAqw7GFRHdxYA.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<blockquote>
  <p><em>點了一份韓牛 Set+炸醬麵+Cass 啤酒，韓牛嫩但沒什麼牛味，我覺得普普；加上 <strong>價格實刷 NT$2,458 C/P 值不太高</strong> ，不如前幾天吃的喉嚨鍋蓋烤肉。</em></p>
</blockquote>

<blockquote>
  <p><em>優點大概是拍照漂亮、店員親切還會中文。</em></p>
</blockquote>

<h4 id="1915-吃飽走回西面站">19:15 吃飽走回西面站</h4>

<p><img src="/assets/8ace34a1a3d8/1*6p05-utFJo4ZG1eFY6yNdQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/8ace34a1a3d8/1*OJJd1Cu8EacCLHW4NqKJzQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="986" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9Ijk4NiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*b-EarSDTdr74ICykPiQrKw.webp" alt="" loading="lazy" decoding="async" width="1200" height="837" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjgzNyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>田浦到西面這邊真的有很多小店、咖啡店、還有看到 ZARA。</p>
<h4 id="回西面站後去樂天百貨-b1-美食街買吃的準備去機場飯店">回西面站後去樂天百貨 B1 美食街買吃的，準備去機場飯店</h4>

<p><img src="/assets/8ace34a1a3d8/1*w_NQLTD-KKle0HD3xlyngw.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/8ace34a1a3d8/1*Rifq7iofXWw4XwV4irb2kQ.webp" alt="" loading="lazy" decoding="async" width="1200" height="875" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg3NSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>這裏也有超市，最後買了這個章魚燒回飯店吃，選了最右邊的韓式口味。</p>
<h4 id="2030-前往機場飯店">~=20:30 前往機場飯店</h4>

<p><img src="/assets/8ace34a1a3d8/1*rAoS5socLEE80cN-joYtPg.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*D0WhmbgOA3EgJVJvW0S_TQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<blockquote>
  <p><em>這次是反方向往伽倻大學，同樣是週二，彷彿看到上週二剛到釜山的自己。</em></p>
</blockquote>

<h4 id="2100-抵達-air-sky-hotel">~=21:00 抵達 <a href="https://naver.me/FnViVS4g" target="_blank">AIR SKY HOTEL</a></h4>

<p><img src="/assets/8ace34a1a3d8/1*Uv0KMnohG6_icObDYCwnoQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/8ace34a1a3d8/1*A1HoGDP-gPzEhYhr7jBa-Q.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*ZO4RT_vb0kK09_OwDQCfow.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>飯店在機場的上一站 — 西釜山流通地区站，對街有 CU/GS25 超商。</p>
<iframe class="embed-video" loading="lazy" src="https://www.youtube.com/embed/KCmYPL1Joko" title="AIR SKY HOTEL (Busan)" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>

<p><img src="/assets/8ace34a1a3d8/1*MmEJgGoFwwQ58ZeA01MznQ.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<blockquote>
  <p><em>這間房間跟浴室都是三天最大、設施最齊全的，不過我們也只是住一天的過客。</em></p>
</blockquote>

<p>房間窗戶外可以直接看到飛機起降。</p>

<p><img src="/assets/8ace34a1a3d8/1*1o095_AJ41otB1aOuQrdiw.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*DqayI4Jguehj-4uKyeUkEw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*s7ieNDtJQZQ8SdCa8FnYEg.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>去超商買了水蜜桃酒配剛剛買的章魚燒，我覺得這個章魚燒很讚，雖然帶回來已經軟掉，但是他是黑色墨魚漿為基底、台灣章魚燒的脆感配上韓式的辣醬，很好吃。😋</p>
<h4 id="day-8-0805-二-釜山金海國際機場回程">Day 8 08/05 (二) 釜山金海國際機場、回程</h4>

<p>時光匆匆，很快的八天的釜山行要畫下句點了，最後一天就只有去機場一個行程。</p>

<p>從飯店出來搭一站就到達機場 (約 10 分鐘)。</p>

<p><img src="/assets/8ace34a1a3d8/1*Dww8cdw_xtLtDoh2R13GNA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>到機場後直接上二樓國際線出發。</p>

<blockquote>
  <p><em>早上還在睏，忘了順便去 <a href="https://www.instagram.com/reel/C8MojavSlZ2/" target="_blank">申請 SES</a> 加速下次入境通關。</em></p>
</blockquote>

<h4 id="退稅">退稅</h4>

<p>如果有退稅單要退稅，記得 <a href="https://www.funliday.com/posts/2024-korea-tax-refund/" target="_blank">照這篇文章</a> 去辦理。
我的退稅單弄不見了…</p>

<p><img src="/assets/8ace34a1a3d8/1*teDaGZTayKhkL2N84UJ25A.webp" alt="" loading="lazy" decoding="async" width="1400" height="988" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9Ijk4OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*pW5hYSUPeitfaOU9aYqklQ.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>二樓邊邊有很多自助報到機，直接在這邊按一按報到、劃位；可以看到三樓也很多吃的跟商店。</p>

<p><img src="/assets/8ace34a1a3d8/1*8qabU_7i8fR_lKUelRAukw.webp" alt="" loading="lazy" decoding="async" width="1200" height="791" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijc5MSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*CqJalfOGffMcPy86EiF2NQ.webp" alt="" loading="lazy" decoding="async" width="1200" height="689" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjY4OSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>櫃檯被分配到 D 在最邊邊。</p>

<p><img src="/assets/8ace34a1a3d8/1*Fe08xzMZ2pZa1ZLJz0i5qQ.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<ul>
  <li>釜山航空免費額度只有：15 KG</li>
  <li>會檢查隨身攜帶的行動電源有沒有裝在夾鏈袋</li>
  <li><strong>掛完行李務必在那一區域等待 5 分鐘，看有沒有問題</strong> 
那天有人一直被廣播找不到人…</li>
</ul>

<h4 id="-0930-出境候機">~= 09:30 出境候機</h4>

<p><img src="/assets/8ace34a1a3d8/1*zp0SV0ILqoWylQLYr0S-uA.webp" alt="" loading="lazy" decoding="async" width="1400" height="958" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9Ijk1OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*5xLQvy-TjwOBT2RAmlzq6g.webp" alt="" loading="lazy" decoding="async" width="1200" height="843" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg0MyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*6GHIaGkDU8ajLtBZFc5KdA.webp" alt="" loading="lazy" decoding="async" width="1200" height="1185" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjExODUiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<blockquote>
  <p><em>出境大廳不大，就一排而已；有幾家免稅店跟一家超商跟咖啡廳，賣少許吃的。(如果很餓記得在外面吃完再出境)</em></p>
</blockquote>

<blockquote>
  <p><em><strong>因為是軍民兩用機場所以不能拍外面飛機。</strong></em></p>
</blockquote>

<p><img src="/assets/8ace34a1a3d8/1*jeZ-QLfYxlbkCFR3s9_6IQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/8ace34a1a3d8/1*oKbFo2YD2LVe80YMI5odeQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>不知道要買什麼，去超商買了兩罐 GD 酒帶回台灣。</p>
<h4 id="delay-1107-起飛提早-1158-抵達">Delay 11:07 起飛，提早 11:58 抵達</h4>

<p><img src="/assets/8ace34a1a3d8/1*PEiW_t5NUyQw-EGRq-SyZg.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<h4 id="釜山行大成功">釜山行大成功！</h4>

<p><img src="/assets/8ace34a1a3d8/1*Oz4R-jMiTOqh0Qa-rh6cHg.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<h4 id="戰利品">戰利品</h4>

<p><img src="/assets/8ace34a1a3d8/1*mANxdMpisGZuM1PYkslrxg.webp" alt="" loading="lazy" decoding="async" width="968" height="812" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NjgiIGhlaWdodD0iODEyIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>沒買什麼呢。</p>
<h4 id="感謝閱讀">感謝閱讀</h4>

<blockquote>
  <p>如果您喜歡我的遊記歡迎透過我的推廣連結購買 <a href="https://www.kkday.com/zh-tw/product/138477-visit-busan-pass-discount-free-attractions?cid=19365" target="_blank">韓國釜山通行證 VISIT BUSAN PASS</a> 我將獲得部分分潤收益，謝謝。</p>
</blockquote>

<h3 id="更多遊記">更多遊記</h3>
<ul>
  <li><a href="/posts/z-度旅行遊記/九州自由行攻略-釜山新山茶花號郵輪入境日本博多-深入由布院-大分-福岡等地-cb65fd5ab770/"><strong>[遊記] 2024 二訪九州 9 日自由行，經釜山→博多郵輪入境</strong></a></li>
  <li><a href="/posts/z-度旅行遊記/山陰關西自由行攻略-島根出雲松江鳥取姬路大阪神戶7日獨旅全記錄-aacd5f5cacd1/">[遊記] 2024 山陰廣域地區 島根 出雲 松江 鳥取 姬路 大阪 神戶 7 日獨旅自由行</a></li>
  <li><a href="/posts/z-度旅行遊記/九州自由行攻略-福岡-長崎-熊本10日獨旅全紀錄與交通住宿實戰經驗-d78e0b15a08a/">[遊記] 2023 九州 10 日自由行獨旅</a></li>
  <li><a href="/posts/z-度旅行遊記/山陽地區廣島岡山自由行攻略-6日行程-交通-住宿全解析-31b9b3a63abc/">[遊記] 2023 廣島岡山 6 日自由行</a></li>
  <li><a href="/posts/z-度旅行遊記/名古屋一日快閃自由行-樂桃航空紅眼班機省錢攻略與行程規劃-7b8a0563c157/">[遊記] 9/11 名古屋一日快閃</a></li>
  <li>[遊記] <a href="/posts/z-度旅行遊記/東京自由行攻略-5-天食住行全記錄與必訪景點推薦-9da2c51fa4f2/">2023 東京 5 日自由行</a></li>
  <li>[遊記] <a href="/posts/z-度旅行遊記/京阪神自由行攻略-京都大阪神戶8日遊全紀錄與實用交通住宿指南-76d66c2e34af/">2023 京阪神 8 日自由行</a></li>
</ul>

<h3 id="kkday-推廣--1">KKday 推廣 🛒</h3>
<ul>
  <li><a href="https://www.kkday.com/zh-tw/product/138477-visit-busan-pass-discount-free-attractions?cid=19365" target="_blank">釜山通行證 VISIT BUSAN PASS</a></li>
  <li><a href="https://www.kkday.com/zh-tw/product/2930-korea-ktx-train-discounted-korail-day-pass?cid=19365" target="_blank">KR PASS 韓國鐵道周遊券</a></li>
  <li><a href="https://www.kkday.com/zh-tw/product/133154-south-korea-esim-sk-telecom-4g-lte-unlimited-data?cid=19365" target="_blank">【特別活動】韓國網卡|SK電信 4G 高速吃到飽 eSIM</a></li>
  <li><a href="https://www.kkday.com/zh-tw/product/534007?cid=19365" target="_blank">韓國釜山包車/釜山出發-慶州/金海/鎮海/浦項/密陽/安東/巨濟島/大邱/蔚山/包車一日遊/金海機場接送</a></li>
  <li><a href="https://www.kkday.com/zh-tw/product/134952?cid=19365" target="_blank">韓國網卡|5G上網 每日高速流量型 / 總量型 eSIM</a></li>
</ul>]]></content>
  </entry><entry>
    <title type="html">Google Offerwall 廣告｜內容創作者快速轉換流量為收益的獎勵牆方案</title>
    <link href="https://zhgchg.li/posts/zrealm-dev/google-offerwall-%E5%BB%A3%E5%91%8A-%E5%85%A7%E5%AE%B9%E5%89%B5%E4%BD%9C%E8%80%85%E5%BF%AB%E9%80%9F%E8%BD%89%E6%8F%9B%E6%B5%81%E9%87%8F%E7%82%BA%E6%94%B6%E7%9B%8A%E7%9A%84%E7%8D%8E%E5%8B%B5%E7%89%86%E6%96%B9%E6%A1%88-ba132457e6a5/" rel="alternate" type="text/html" title="Google Offerwall 廣告｜內容創作者快速轉換流量為收益的獎勵牆方案" />
    <published>2025-07-24T16:15:09+08:00</published>
    <updated>2025-07-24T16:26:29+08:00</updated>
    <id>https://zhgchg.li/posts/zrealm-dev/ba132457e6a5</id><summary type="html">內容創作者透過 Google Offerwall 廣告，輕鬆設定任務解鎖內容，無需自行開發系統，快速將流量轉換為穩定收益，適合中小型網站與個人部落格，提升變現效率並擴展多元獲利模式。</summary><author>
      <name>ZhgChgLi</name>
    </author><category term="ZRealm Dev." /><category term="ios-app-development" /><category term="google-ads" /><category term="offerwall" /><category term="google-adsense" /><category term="jekyll" /><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://zhgchg.li/assets/ba132457e6a5/1*fBIJDsQ994Wn0JNyPDJt8Q.webp" /><content type="html" xml:base="https://zhgchg.li/posts/zrealm-dev/google-offerwall-%E5%BB%A3%E5%91%8A-%E5%85%A7%E5%AE%B9%E5%89%B5%E4%BD%9C%E8%80%85%E5%BF%AB%E9%80%9F%E8%BD%89%E6%8F%9B%E6%B5%81%E9%87%8F%E7%82%BA%E6%94%B6%E7%9B%8A%E7%9A%84%E7%8D%8E%E5%8B%B5%E7%89%86%E6%96%B9%E6%A1%88-ba132457e6a5/"><![CDATA[<h3 id="google-offerwall-廣告--內容創作者的全新收益選擇">Google Offerwall 廣告 — 內容創作者的全新收益選擇</h3>

<p>Google Offerwall 廣告的淺談與試用，可快速將既有流量內容轉換成限制小額支持或觀看廣告後才能存取。</p>

<h3 id="live-demo"><a href="/posts/zrealm-dev/google-offerwall-廣告-內容創作者快速轉換流量為收益的獎勵牆方案-ba132457e6a5/">Live Demo</a></h3>

<p><img src="/assets/ba132457e6a5/1*V4LD8JJoor7SusoHX01_eQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="994" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9Ijk5NCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/ba132457e6a5/1*fBIJDsQ994Wn0JNyPDJt8Q.webp" alt="https://zhgchg.li/posts/ba132457e6a5/" loading="lazy" decoding="async" width="1400" height="973" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9Ijk3MyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><a href="/posts/zrealm-dev/google-offerwall-廣告-內容創作者快速轉換流量為收益的獎勵牆方案-ba132457e6a5/">https://zhgchg.li/posts/ba132457e6a5/</a></p>

<blockquote>
  <p><strong><em>Live Demo -&gt; <a href="/posts/zrealm-dev/google-offerwall-廣告-內容創作者快速轉換流量為收益的獎勵牆方案-ba132457e6a5/">https://zhgchg.li/posts/ba132457e6a5/</a></em></strong></p>
</blockquote>

<h3 id="google-offerwall-廣告">Google Offerwall 廣告</h3>

<p><img src="/assets/ba132457e6a5/1*DNW0ylu9LHLs4ztWVzw3UQ.webp" alt="https://support.google.com/admanager/answer/13860694?hl=zh-Hant" loading="lazy" decoding="async" width="1400" height="976" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9Ijk3NiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><a href="https://support.google.com/admanager/answer/13860694?hl=zh-Hant" target="_blank">https://support.google.com/admanager/answer/13860694?hl=zh-Hant</a></p>

<p>「 <a href="https://support.google.com/admanager/answer/13860694?hl=zh-Hant" target="_blank">Google Offerwall</a> 」，個人覺得是「 <strong>獎勵型的 Offerwall 獎勵牆廣告</strong> 」結合獎勵型廣告的特性，開發者可以很輕易的 <strong>指定網站內容頁面需要完成任務才能存取</strong> ， <strong>任務則為 Offerwall 獎勵牆任務</strong> ，可以設定使用者須先完成興趣調查、看廣告、小額支付、訂閱電子報…或 <a href="https://support.google.com/admanager/answer/13566866?hl=zh-Hant&amp;ref_topic=13821812&amp;sjid=14925592883586634734-NC" target="_blank">串接自家系統的任務</a> ，獎勵內容則為存取權限可以是幾小時或幾天的無限制數量存取、也可以是數量限制；而這一切都只需要嵌入 Google Adsense 動態廣告程式碼就能達成，不需要自行開發系統。</p>
<h4 id="-優點">✅ 優點</h4>
<ul>
  <li><strong>實現容易</strong> ：只要網站 <a href="https://support.google.com/adsense/answer/9189560?hl=zh-Hant" target="_blank">嵌入動態廣告程式碼</a> 即可，一次設定終身使用。</li>
  <li><strong>直接獲利</strong> ：同 Google Adesnse 好處，不需要自己建置複雜的廣告系統、數據追蹤系統；也不需要自己找廣告商投放廣告，一切都由平台代為管理操作。</li>
  <li><strong>彈性多元</strong> ：提供更彈性多元的內容、流量變現方式。</li>
  <li><strong>跨平台</strong> ：支援手機、電腦版。</li>
</ul>

<h4 id="-缺點">❌ 缺點</h4>
<ul>
  <li><strong>出價低</strong> ：這也是 Google Adesnse 的通病，廣告出價低。</li>
  <li><strong>容易阻擋</strong> ：因為 Google Offerwall 是純前端遮蔽廣告，所以可以很輕易的被擋廣告外掛組饒或使用者使用開發工具可以直接繞過； <strong>因此不建議將其實現在敏感重要有價值的內容之上。</strong></li>
</ul>

<h4 id="-技術要求">📝 技術要求</h4>
<ul>
  <li>需要先申請通過 <a href="https://adsense.google.com/" target="_blank">Google Adsense</a> 
<strong>現在門檻比較低了，只要有網站網址，內容有5–10 篇文章基本上就能申請通過。</strong></li>
  <li>網站需要能在 HTML Head 中 <a href="https://support.google.com/adsense/answer/9189560?hl=zh-Hant" target="_blank">嵌入動態廣告程式碼</a></li>
</ul>

<h4 id="️-適合場景">❤️ 適合場景</h4>
<ul>
  <li><strong>For 廣告</strong> ：同 Google Adsense 適合無自售廣告版位的中小型內容網站或個人部落格，例如使用 <a href="https://wordpress.com/zh-tw/" target="_blank">Wordpress</a> 、 <a href="https://pages.github.com/" target="_blank">GitHub Pages</a> 託管的網站；除原本的廣告版位顯示，還能對有價值的內容文章設定 Offerwall 獎勵牆廣告。
<strong>本篇文章會以 For 廣告為案例，目前除廣告外都還在 Beta。</strong></li>
  <li><strong>For 問券、小額支付、訂閱電子報</strong> ：任何網站都能使用此機制快速建立Pop-up 廣告。</li>
</ul>

<h3 id="安裝啟用步驟">安裝啟用步驟</h3>
<h4 id="嵌入動態廣告程式碼">嵌入動態廣告程式碼</h4>

<p>首先要確認網站有嵌入 Google Adsense 廣告程式碼，如果之前就有使用動態廣告則可忽略此步驟。</p>

<p><strong>前往 <a href="https://adsense.google.com/" target="_blank">Google Adsense</a> -&gt; 廣告 -&gt; 按網站 -&gt; 取得程式碼：</strong></p>

<p><img src="/assets/ba132457e6a5/1*CYX4r932I3qkkOgKRVYTSg.webp" alt="" loading="lazy" decoding="async" width="1200" height="636" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjYzNiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/ba132457e6a5/1*JAEN6wEMLaSgu5wpUkRTnw.webp" alt="" loading="lazy" decoding="async" width="1400" height="609" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjYwOSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>將程式碼內容貼到網站的 <code class="language-plaintext highlighter-rouge">&lt;head&gt;&lt;/head&gt;</code> 區塊之間：</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;head&gt;</span>
  //...
  <span class="nt">&lt;script</span> <span class="err">async</span> <span class="na">src=</span><span class="s">"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3184248473087645"</span> <span class="na">crossorigin=</span><span class="s">"anonymous"</span><span class="nt">&gt;&lt;/script&gt;</span>
<span class="nt">&lt;/head&gt;</span>
</code></pre></div></div>
<h4 id="建立-offerwall-廣告">建立 Offerwall 廣告</h4>

<p><strong>前往 <a href="https://adsense.google.com/" target="_blank">Google Adsense</a> -&gt; 隱私權與訊息 -&gt; 管理 -&gt; 建立訊息：</strong></p>

<p><img src="/assets/ba132457e6a5/1*aQ3svqP3lo16CGH8R-28yw.webp" alt="" loading="lazy" decoding="async" width="1200" height="1171" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjExNzEiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/ba132457e6a5/1*ziurWzmp3N6S5-P4O9GO7w.webp" alt="" loading="lazy" decoding="async" width="1200" height="868" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<blockquote>
  <p><em>也可以在這個頁面查看廣告收益。</em></p>
</blockquote>

<p><strong>設定 Offerwall 廣告：</strong></p>

<p><img src="/assets/ba132457e6a5/1*QfG2HVUy728iSMOdKb7rLw.webp" alt="" loading="lazy" decoding="async" width="1400" height="743" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9Ijc0MyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/ba132457e6a5/1*E0Pkk8T4e9YVBxJ8xnv1ow.webp" alt="" loading="lazy" decoding="async" width="826" height="960" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4MjYiIGhlaWdodD0iOTYwIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><img src="/assets/ba132457e6a5/1*J6wnirCtE8Qp5VsnsTAn_w.webp" alt="" loading="lazy" decoding="async" width="980" height="778" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5ODAiIGhlaWdodD0iNzc4Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><img src="/assets/ba132457e6a5/1*akZZ5_K3BS1FVSomf8AvvA.webp" alt="3, 5, 6" loading="lazy" decoding="async" width="1200" height="723" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjcyMyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>3, 5, 6</p>

<p><img src="/assets/ba132457e6a5/1*g_PkT5TqCEFYajdT4ugPCQ.webp" alt="" loading="lazy" decoding="async" width="1200" height="842" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg0MiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/ba132457e6a5/1*wnbt-BkLE-0Bk3KGr9ED7Q.webp" alt="4, 8" loading="lazy" decoding="async" width="1400" height="747" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9Ijc0NyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>4, 8</p>
<ol>
  <li><strong>廣告名稱</strong></li>
  <li><strong>您的網站</strong> ：設定要套用的網站</li>
  <li><strong>網頁納入項目和排除項目</strong> ：可設定要涵蓋的頁面網址或是排除的網址
<em>這邊我是設定 <code class="language-plaintext highlighter-rouge">https://zhgchg.li/posts/</code> 路徑下的網址才會觸發。</em></li>
  <li><strong>預設語言</strong> ：設定預設語言與支援的其他語言，可在左上方切換語言編輯不同語言的文案。</li>
  <li><strong>計量</strong> ：可設定使用者瀏覽第幾個(次)網頁後才觸發
<em>這邊我設 0 代表第一次就觸發。</em></li>
  <li><strong>獎勵廣告</strong> ：可設定完成任務的內容解鎖獎勵，可以是時間內無限次數瀏覽或是有限次數網頁瀏覽，獎勵到期後需要重新完成任務。
<em>這邊我設完成一次任務 24 小時可無限瀏覽。</em></li>
  <li><strong>文案設定：</strong> 指定文案內容、記得至少要上傳個 Logo 增加品牌力</li>
  <li><strong>樣式設定：</strong> 點擊 7 文案後還可以從這邊設定文字樣式、顏色
可以參考 <a href="https://support.google.com/admanager/answer/13860694?hl=zh-Hant#zippy=%2C%E5%A6%82%E4%BD%95%E6%B8%AC%E8%A9%A6-offerwall" target="_blank">測試方式</a> 在頁面網址加上 <code class="language-plaintext highlighter-rouge">?fc=alwaysshow&amp;fctype=monetization</code> 預覽結果。</li>
  <li><strong>發佈變更</strong> ：都設定好之後記得按發佈</li>
</ol>

<p><img src="/assets/ba132457e6a5/1*fRqD1tZ-513FjKsTTwDYkw.webp" alt="" loading="lazy" decoding="async" width="1400" height="1047" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNDciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>獎勵完成後訊息，目前不能更改。</p>

<p><img src="/assets/ba132457e6a5/1*fjfArCVeVNus7J48rEHcaQ.webp" alt="" loading="lazy" decoding="async" width="1200" height="781" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijc4MSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>確認廣告是已發布狀態。</p>
<h4 id="測試-offerwall-廣告">測試 Offerwall 廣告</h4>

<p>使用無痕瀏覽器、確認沒有啟用擋廣告、反追蹤套件，後前往規則內的網頁：</p>

<p><img src="/assets/ba132457e6a5/1*OSEzFBhd-wXSAdm-41aLjQ.webp" alt="https://zhgchg.li/posts/c008a9e8ceca/" loading="lazy" decoding="async" width="1400" height="1349" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEzNDkiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><a href="/posts/zrealm-dev/ci-cd-是什麼-打造高效穩定開發團隊的關鍵流程與工具選擇-c008a9e8ceca/">https://zhgchg.li/posts/c008a9e8ceca/</a></p>

<blockquote>
  <p>成功🙌🙌🙌</p>
</blockquote>

<p>盡量使用 Chrome 無痕進行測試，Safari 要關閉擋廣告、反追蹤會比較麻煩。</p>
<h4 id="收益方面">收益方面</h4>

<p>因為我的瀏覽量不高，所以單次點擊出價也都不太高，大約點擊一次廣告在 USD $0.01 ~ 0.07 之間而已。</p>
<h3 id="medium-to-github-pages-x-google-offerwall">Medium To Github Pages x Google Offerwall</h3>

<p>可參考我之前的文章「 <a href="/posts/zrealm-dev/無痛轉移-medium-文章到自架-jekyll-靜態網站-快速部署與自動同步教學-a0c08d579ab1/">無痛轉移 Medium 到自架網站</a> 」先將 Medium 文章無痛鏡像到 GitHub Pages 託管的靜態網站，然後在網站上再加入 Google Offerwall 即可將內容變現收益。</p>

<p>或直接參考「 <a href="/posts/zrealm-life/medium-partner-program-全球開放-台灣創作者輕鬆加入賺取文章收益-cefdf4d41746/">Medium Partner Program 終於對全球(包含台灣)寫作者開放啦！</a> 」將 Medium 文章加入付費牆獲得收益。</p>

<blockquote>
  <p><em>因為 Medium 付費牆機制要求讀者必須花月費加入 Medium 才能閱讀文章，我覺得對資訊傳播不太友善，因此我的文章都沒有加入付費牆計劃；有了 Google Offerwall 正好補足了這塊中間地帶，使用者可以簡單地看廣告支持內容創作者，不用強綁月費付費，創作者也能獲得收益，一舉兩得！</em></p>
</blockquote>

<h3 id="補充-offerwall-獎勵牆廣告與獎勵型廣告類型說明"><strong><em>[補充] Offerwall 獎勵牆廣告與獎勵型廣告類型說明</em></strong></h3>
<h4 id="offerwall--獎勵牆廣告是什麼">Offerwall — 獎勵牆廣告是什麼？</h4>

<p>相比傳統的固定版位廣告，獎勵牆廣告更強調與使用者之間的互動與目的性。它整合了內容展示、獎勵激勵以及使用者行為，使得使用者除了可以選擇主動付費解鎖加值項目外，還能以更有趣、參與性更高的方式獲得額外資源。而創作者則能從中獲得額外收益，實現雙贏效果。</p>

<p><strong>Example — Line Points 獎勵牆</strong></p>

<p><img src="/assets/ba132457e6a5/1*SWF8K0p5asGiDoPMhZRAWQ.webp" alt="" loading="lazy" decoding="async" width="554" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1NTQiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>經典的日常生活中的案例就是 — Line Points，使用者除了直接加值購買 Line Points 之外也能從 Offwall 獎勵牆當中挑選喜歡的任務(加好友、註冊帳號、填問券、看廣告…等等)完成後獲得對應獎勵。</p>
<h4 id="rewarded-ads--獎勵型廣告">Rewarded Ads — <strong>獎勵型廣告</strong></h4>

<p>獎勵型廣告是一種常見於遊戲與行動應用中的廣告形式，使用者只需自願完成特定行為，例如 <strong>觀看一段影片、點擊互動內容，或試用特定功能</strong> ，就能獲得虛擬貨幣、額外生命、遊戲道具等獎勵。</p>

<p><strong>Example — Candy Crush</strong></p>

<p><img src="/assets/ba132457e6a5/1*D9PdoZg-mlCbtO19roltdQ.webp" alt="" loading="lazy" decoding="async" width="700" height="1400" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3MDAiIGhlaWdodD0iMTQwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>經典案例就是遊戲中死亡繼續遊玩的廣告，這類廣告不僅提升使用者的參與度與滿意度，也讓開發者在不影響用戶體驗的前提下創造收益，同樣實現雙贏效果。</p>]]></content>
  </entry><entry>
    <title type="html">CI/CD 打包工具平台｜使用 Google Apps Script Web App 串接 GitHub Actions 提升跨團隊效率</title>
    <link href="https://zhgchg.li/posts/zrealm-dev/ci-cd-%E6%89%93%E5%8C%85%E5%B7%A5%E5%85%B7%E5%B9%B3%E5%8F%B0-%E4%BD%BF%E7%94%A8-google-apps-script-web-app-%E4%B8%B2%E6%8E%A5-github-actions-%E6%8F%90%E5%8D%87%E8%B7%A8%E5%9C%98%E9%9A%8A%E6%95%88%E7%8E%87-4273e57e7148/" rel="alternate" type="text/html" title="CI/CD 打包工具平台｜使用 Google Apps Script Web App 串接 GitHub Actions 提升跨團隊效率" />
    <published>2025-07-10T20:30:51+08:00</published>
    <updated>2025-11-19T23:59:42+08:00</updated>
    <id>https://zhgchg.li/posts/zrealm-dev/4273e57e7148</id><summary type="html">打造免費且易用的打包工具平台，解決非工程人員操作 GitHub Actions 的複雜性與權限控管問題，透過 Google Apps Script Web App 整合 GitHub、Slack、Firebase 等 API，實現跨團隊任務單打包與即時進度通知，提升開發與驗證效率。</summary><author>
      <name>ZhgChgLi</name>
    </author><category term="ZRealm Dev." /><category term="ios-app-development" /><category term="cicd" /><category term="google-apps-script" /><category term="web-development" /><category term="tools" /><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://zhgchg.li/assets/4273e57e7148/1*kJhD4PCaIphZ9G1BG_dtNw.webp" /><content type="html" xml:base="https://zhgchg.li/posts/zrealm-dev/ci-cd-%E6%89%93%E5%8C%85%E5%B7%A5%E5%85%B7%E5%B9%B3%E5%8F%B0-%E4%BD%BF%E7%94%A8-google-apps-script-web-app-%E4%B8%B2%E6%8E%A5-github-actions-%E6%8F%90%E5%8D%87%E8%B7%A8%E5%9C%98%E9%9A%8A%E6%95%88%E7%8E%87-4273e57e7148/"><![CDATA[<h3 id="cicd-實戰指南四使用-google-apps-script-web-app-串接-github-actions-建置免費易用的打包工具平台">CI/CD 實戰指南（四）：使用 Google Apps Script Web App 串接 GitHub Actions 建置免費易用的打包工具平台</h3>

<p>GAS Web App 串接 GitHub, Slack, Firebase 或 Asana/Jira API，建置中繼站，提供跨團隊共用的打包工具平台</p>

<p><img src="/assets/4273e57e7148/1*kJhD4PCaIphZ9G1BG_dtNw.webp" alt="Photo by Lee Campbell" loading="lazy" decoding="async" width="1200" height="801" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjgwMSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>Photo by <a href="https://unsplash.com/@leecampbell?utm_content=creditCopyText&amp;utm_medium=referral&amp;utm_source=unsplash" target="_blank">Lee Campbell</a></p>
<h3 id="前言">前言</h3>

<p>前篇「 <a href="/posts/zrealm-dev/github-actions-ios-ci-cd-完整實作自動化建置-測試與部署流程教學-4b001d2e8440/"><strong>CI/CD 實戰指南（三）：使用 GitHub Actions 實作 App iOS CI 與 CD 工作流程</strong></a> 」我們已經完成了 App iOS 專案的 CI/CD 底層功能建設，現在已經可以 CI 自動測試驗證跟 CD 打包部署；然而在實務產品開發流程上， <strong>打包部署工作多半是為了後續交付給其他職能夥伴</strong> 進 行 QA(Quality Assurance) 功能驗證，這時候 CD 場景就不在只侷限在工程端了，可能跨足 QA、PM、設計(Design QA) 甚至是老闆想先玩看看。</p>

<p>GitHub Actions <code class="language-plaintext highlighter-rouge">workflow_dispatch</code> 手動表單觸發事件，雖然能提供簡易的表單讓使用者操作打包，但如果操作的對象是非工程人員就相對很不友善，他們不懂：什麼是分支？欄位要不要填？怎麼知道打包好了？好了怎麼下載？…etc
另外還有權限控管問題，如果要讓其他職能夥伴直接使用 GitHub Actions 打包，就需要把他的帳號加到 Repo 當中， <strong>資安控管上很不安全也不合理</strong> ，只是要操作打包表單卻要開整個 Source Code 給他。</p>

<p>不同於 Jenkins 有獨立的 Web 工具平台，GitHub Actions 就只有這個功能。</p>

<p><img src="/assets/4273e57e7148/1*qDD8HAAHxDxPEU3vJPEhjA.webp" alt="`workflow_dispatch 的表單樣式`" loading="lazy" decoding="async" width="342" height="445" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzNDIiIGhlaWdodD0iNDQ1Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><code class="language-plaintext highlighter-rouge">workflow_dispatch 的表單樣式</code></p>

<p>因此 <strong>我們需要一個中繼站打包平台，用來服務其他職能使用者</strong> ，整合 Asana/Jira 的任務單，讓使用的人可以直接用任務單打包 App，並且直接在上面查看進度、下載打包結果。</p>

<p><img src="/assets/4273e57e7148/1*bHYawmSnhqwB4TzIJVfGow.webp" alt="GAS Web App 中繼站" loading="lazy" decoding="async" width="1400" height="1037" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwMzciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>GAS Web App 中繼站</p>

<p><a href="/posts/zrealm-dev/github-actions-ios-ci-cd-完整實作自動化建置-測試與部署流程教學-4b001d2e8440/">上一篇</a> 關注的是右邊核心 GitHub Actions CI/CD 工作流程開發；本文關注的是左半部 to End-User 打包工具平台、增加使用者體驗的部分。</p>
<h4 id="google-apps-script--web-app-打包工具平台-成果圖">Google Apps Script — Web App 打包工具平台 成果圖</h4>

<p><img src="/assets/4273e57e7148/1*yXMeaOELhqdvMCxIJ5ElBw.gif" alt="" loading="lazy" decoding="async" width="1048" height="822" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDQ4IiBoZWlnaHQ9IjgyMiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/4273e57e7148/1*EM0goWpuDeHGVkZoybLm8g.webp" alt="" loading="lazy" decoding="async" width="980" height="899" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5ODAiIGhlaWdodD0iODk5Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><img src="/assets/4273e57e7148/1*haoGMvAUroz7rUMDEez9gA.webp" alt="" loading="lazy" decoding="async" width="984" height="826" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5ODQiIGhlaWdodD0iODI2Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><img src="/assets/4273e57e7148/1*afCwKITlerG1g6swB_2wZA.webp" alt="" loading="lazy" decoding="async" width="955" height="758" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NTUiIGhlaWdodD0iNzU4Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><img src="/assets/4273e57e7148/1*mvwpHBdrC73_PjL8H32nkw.webp" alt="" loading="lazy" decoding="async" width="554" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1NTQiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/4273e57e7148/1*BbbEd_thhUOdbAQqsCt-Tw.webp" alt="" loading="lazy" decoding="async" width="1046" height="585" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDQ2IiBoZWlnaHQ9IjU4NSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/4273e57e7148/1*HXjfMpUPssaw3sJgH6KKJQ.webp" alt="" loading="lazy" decoding="async" width="312" height="292" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzMTIiIGhlaWdodD0iMjkyIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<ul>
  <li><strong>打包表單：</strong> 整合專案管理工具撈取單號、整合 GitHub 撈取開啟中的 Pull Request</li>
  <li><strong>把包紀錄：</strong> 顯示打包歷史紀錄、正在打包的任務進度狀態、點擊取得下載連結撈取 Firebase App Distribution 下載連結、資訊</li>
  <li><strong>Runner 狀態：</strong> 顯示 Self-hosted Runner 狀態。</li>
  <li><strong>Slack 打包進度通知。</strong></li>
  <li>支援手機版</li>
  <li>支援限制組織團隊內帳號使用</li>
</ul>

<p><strong>主要職責</strong></p>

<p>0 狀態、0 資料庫， <strong>單純為中繼交換站</strong> ，整合顯示各個 API 資料 (e.g. Asana/Jira/GitHub)、轉發表單請求到 GitHub Actions。</p>

<p><strong>操作要求：</strong> 支援手機與電腦。</p>

<p><strong>權限要求：</strong> 能限制只有團隊組織成員能存取。</p>
<h4 id="online-demo-web-app"><a href="https://script.google.com/macros/s/AKfycbwNW6N5ozKbIz_E1HK6yFEUtA8KQrUciS-jcPsQptvIKlARmKgLxbQzNu8ksVeg-BmEfg/exec" target="_blank">Online Demo Web App</a></h4>
<ul>
  <li>初次使用請參考下圖授權(Only For Demo App)：</li>
</ul>

<p><img src="/assets/4273e57e7148/0*7pJ876yQbcakQvdO.webp" alt="" loading="lazy" decoding="async" width="700" height="607" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3MDAiIGhlaWdodD0iNjA3Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>專案原始碼： <a href="https://script.google.com/home/projects/1CBB39OMedqP9Ro1WSlvgDnMBin4-ksyhgly2h_KrbOuFiPHTalNgwHOp/edit" target="_blank">https://script.google.com/home/projects/1CBB39OMedqP9Ro1WSlvgDnMBin4-ksyhgly2h_KrbOuFiPHTalNgwHOp/edit</a></p>
<h3 id="技術選擇">技術選擇</h3>

<p><a href="/posts/zrealm-dev/ci-cd-是什麼-打造高效穩定開發團隊的關鍵流程與工具選擇-c008a9e8ceca/">第一篇文章</a> 有提到過，這邊再詳細整理一次。</p>
<h4 id="整合進-slack">整合進 Slack</h4>

<p>我們嘗試過讓 Slack 擔任打包平台，自行開發後端服務並架設在 GCP 上，與 Slack API、Asana API 進行串接整合，再將 Slack 送來的表單轉手發送給 GitHub API 觸發後續 GitHub Actions；體驗上非常爽而且統一在團隊的協作工具上，可以無痛使用； <strong>缺點是開發及後續維護成本很高</strong> ，因為是自己用 Ktor 開發的後端服務，需要 App 工程師同時兼做後端、處理 Google 服務 OAuth 整合問題、部分功能可能在這做掉(例如：送審)…功能複雜；如果後來的新人沒銜接好這整塊幾乎沒辦法維護下去了，另外每個月還要花 $15 USD GCP 伺服器費用。</p>

<p>初期也嘗試過用 FaaS 服務串接 Slack API，例如 Cloud Funtions，但是 Slack API 要求串接的服務要能在 3 秒內回應請求，否則視為失敗；FaaS 會有 <a href="https://www.cloudflare.com/zh-tw/learning/serverless/what-is-serverless/" target="_blank">冷啟動問題</a> ，當服務一段時間沒呼叫就會進入休眠，再次呼叫時會需要較長時間才能響應 (≥ 5秒)，會造成 Slack 打包表單很不穩定，時常出現 Timeout Error。</p>
<h4 id="整合進內部系統">整合進內部系統</h4>

<p>這當然是最優解，如果團隊有 Web、後端人力，直接與現有系統整合是最好也最安全的。</p>

<blockquote>
  <p><em>本文的前提是：沒有，App 自立自強。</em></p>
</blockquote>

<h4 id="google-apps-script--web-app">Google Apps Script — Web App</h4>

<p>Google Apps Script 是我們的老夥伴，之前做過很多 RPA 專案都是用他去做排程觸發執行任務，例如：「 <a href="/posts/zrealm-robotic-process-automation/crashlytics與google-analytics自動查詢app-crash-free-users-rate-google-apps-script整合實作-793cb8f89b72/">Crashlytics + Google Analytics 自動查詢 App Crash-Free Users Rate</a> 」、「 <a href="/posts/zrealm-robotic-process-automation/google-apps-script-打造每日數據報表自動化-rpa-快速串接-google-analytics-與-google-sheet-f6713ba3fee3/">使用 Google Apps Script 實現每日數據報表 RPA 自動化</a> 」，這時候我又想起了它；GAS 有一個功能是部署成 Web (App) 直接當網頁服務。</p>

<p><strong>Google Apps Script 優點：</strong></p>
<ul>
  <li>✅ 免費、 <a href="https://developers.google.com/apps-script/guides/services/quotas?hl=zh-tw" target="_blank">正常使用下幾乎摸不到上限</a></li>
  <li>✅ Functions as a Service 方法即服務，不需自行架設維護伺服器</li>
  <li>✅ 權限控管同 Google Workspace，可設定僅限組織內 Google 帳號使用</li>
  <li>✅ 無痛整合 Google 生態系相關服務(e.g. Firebase, GA…etc)與資料 (不用自己做 OAuth)</li>
  <li>✅ 程式語言使用 JavaScript 易上手 (V8 Runtime 支援 ES6+)</li>
  <li>✅ 快速撰寫、快速上線、快速使用</li>
  <li>✅ 服務穩定、長久 (已推出超過 16 年)</li>
  <li><strong>✅ AI Can Help! 實測使用 ChatGPT 輔助開發，準確率可達 95%</strong></li>
</ul>

<p><strong>Google Apps Script 缺點：</strong></p>
<ul>
  <li>❌ 內建的版本管控一言難盡</li>
  <li>❌ 內建不支援檔案、資料儲存、金鑰/憑證管理</li>
  <li>❌ Web App 無法做到 100% 體驗的 RWD</li>
  <li>❌ 專案只能綁在個人帳號而不是組織</li>
  <li>❌ 雖然 Google 有持續在開發維護，但整體功能更新緩慢</li>
  <li>❌ 網路請求 <code class="language-plaintext highlighter-rouge">UrlFetchApp</code> 不支援設定 User-Agent</li>
  <li>❌ Web App <code class="language-plaintext highlighter-rouge">doGet</code> / <code class="language-plaintext highlighter-rouge">doPost</code> 不支援取得 Headers 資訊</li>
  <li>❌ FaaS <a href="https://www.cloudflare.com/zh-tw/learning/serverless/what-is-serverless/" target="_blank">冷啟動問題</a></li>
  <li>❌ <strong>不支援多人同時開發</strong> 
但在 Web App 影響不大，頂多要多等幾秒才進入網頁。</li>
</ul>

<p>以上是 GAS 本身服務的優缺點，對做打包工具 Web 影響不大；選擇使用此方案對比 Slack 方案，更快速、輕量跟容易交接上手， <strong>缺點是團隊需要多知道這個工具的網址在哪跟怎麼用、還有因 GAS 程式庫功能有限</strong> (e.g. 無內建加密演算法庫) 基本上只能做純中繼平台，例如送審，那也只能轉發送審請求請 GitHub Actions 做。</p>

<blockquote>
  <p><strong><em>另外也僅適用是 Google Workspace 工作環境的團隊；</em></strong> <em>權衡了資源與需求，因此採用 Google Apps Script — Web App 來實現打包工具平台。</em></p>
</blockquote>

<h4 id="ui-framework">UI Framework</h4>

<p><a href="https://getbootstrap.com/" target="_blank"><img src="https://getbootstrap.com/docs/5.3/assets/brand/bootstrap-social.png" alt="" /></a></p>

<p>我們直接使用 Bootstrap CDN，不然要自己弄 CSS 樣式太麻煩了，Bootstrap 問 AI 怎麼組合使用也更準確方便。</p>
<h3 id="動手做">動手做</h3>

<p>這邊已經把整個平台架構開源了，大家可以依照自己團隊的需求基於這個版本再客製化即可。</p>
<h4 id="開源範例專案">開源範例專案</h4>

<p>在 GAS 上直接檢視專案：</p>

<p><a href="https://script.google.com/home/projects/1CBB39OMedqP9Ro1WSlvgDnMBin4-ksyhgly2h_KrbOuFiPHTalNgwHOp/edit" target="_blank"><img src="https://www.gstatic.com/devrel-devsite/prod/v78ce60439c72b9da3632137223a86ae38b78a872a1f6dee1b5c1c8cfa57fe81d/developers/images/opengraph/white.png" alt="" /></a></p>

<p><img src="/assets/4273e57e7148/1*Xzh2eBpVR92uZq-8POPegw.webp" alt="" loading="lazy" decoding="async" width="760" height="860" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjAiIGhlaWdodD0iODYwIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>GitHub Repo 備份：</p>

<p><a href="https://github.com/ZhgChgLi/google-apps-script-cd-web-app-demo" target="_blank"><img src="https://opengraph.githubassets.com/3d7c517cd747bc49656701ef275357816667a72ebe2b7744c22e8a34c82b4a26/ZhgChgLi/google-apps-script-cd-web-app-demo" alt="" /></a></p>

<h4 id="檔案架構">檔案架構</h4>

<p><img src="/assets/4273e57e7148/1*ohVakhlaflRXCI_3j2-cOA.webp" alt="" loading="lazy" decoding="async" width="227" height="716" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMjciIGhlaWdodD0iNzE2Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><img src="/assets/4273e57e7148/1*AyIEF0wqEFdMDBuRzQXDEQ.webp" alt="" loading="lazy" decoding="async" width="1177" height="991" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMTc3IiBoZWlnaHQ9Ijk5MSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>寫了一個很陽春的 類-MVC 架構，如要調整或不知道功能問 AI 都能得到準確答案。</p>

<p><strong>系統</strong></p>
<ul>
  <li>appsscript.json: GAS 系統的 Metadata 設定檔案
<strong>重點是「oauthScopes」變數，宣告這個 Script 會用到的外部權限。</strong></li>
  <li>Entrypoint.gs: 定義 doGet() 進入點</li>
</ul>

<p><strong>Controller</strong></p>
<ul>
  <li>Controller_iOS.gs: iOS 打包工具頁面 Controller，負責撈數據給 View 顯示</li>
</ul>

<p><strong>View</strong></p>
<ul>
  <li>View_index.html: 整個打包工具骨架、首頁</li>
  <li>View_iOS.html: iOS 打包工具頁面骨架</li>
  <li>View_iOS_Runs.html: iOS 打包工具 — 打包紀錄內容頁面</li>
  <li>View_iOS_Form.html: iOS 打包工具 —打包表單頁面</li>
  <li>View_iOS_Runners.html: iOS 打包工具 — Self-hosted Runner 狀態頁面</li>
</ul>

<p><strong>Model(Lib)</strong></p>
<ul>
  <li>Credentials.gs: 定義金鑰內容
(⚠️️️請注意️，GAS 如果要走 GCP IAM 會蠻複雜的因此我們直接定義金鑰在此， <strong>所以這個 GAS 專案會包含機密資訊，請不要隨意共享專案檢視編輯權限</strong> )</li>
  <li>StubData.gs: Online Demo 用的 Stub 方法、資料。</li>
  <li>Settings.gs: 一些常用設定，跟 lib init。</li>
  <li>GitHub.gs: GitHub API 操作封裝。</li>
  <li>Slack.gs: Slack API 操作封裝。</li>
  <li>Firebase.gs: Firebase — App Distribution API 操作封裝。</li>
</ul>

<h4 id="建立自己的打包平台">建立自己的打包平台</h4>

<p>1.建立一個 <a href="https://script.google.com/home" target="_blank">Google Apps Script 專案</a> 、命名</p>

<p><img src="/assets/4273e57e7148/1*_uVqy6cDNvFSEL0feOS-pw.webp" alt="" loading="lazy" decoding="async" width="919" height="369" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MTkiIGhlaWdodD0iMzY5Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>到專案設定 → 勾選「在編輯器中顯示「appsscript.json」資訊清單檔案」才會出現「appsscript.json」Metadata 檔案。</p>

<p><img src="/assets/4273e57e7148/1*tvXsQufQs5-5UzV0bg5WLA.webp" alt="" loading="lazy" decoding="async" width="736" height="466" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3MzYiIGhlaWdodD0iNDY2Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>2.參考我的 <a href="https://script.google.com/home/projects/1CBB39OMedqP9Ro1WSlvgDnMBin4-ksyhgly2h_KrbOuFiPHTalNgwHOp/edit" target="_blank">開源專案檔案</a> ，把所有檔案按照範例建立好跟把內容無腦複製過來</p>

<p><img src="/assets/4273e57e7148/1*PxZvHNeW6PFaaqmZqifFfg.webp" alt="" loading="lazy" decoding="async" width="572" height="272" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1NzIiIGhlaWdodD0iMjcyIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<blockquote>
  <p><strong><em>很蠢，但沒辦法。</em></strong></p>
</blockquote>

<blockquote>
  <p><em>StubData.gs 先一起複製過來，第一次部署測試可以使用。</em></p>
</blockquote>

<blockquote>
  <p><em>另一個方法是用 <a href="https://developers.google.com/apps-script/guides/clasp?hl=zh-tw" target="_blank">clasp (Google Apps Script CLI)</a> git clone demo project 後把 Code 推上去。</em></p>
</blockquote>

<p><img src="/assets/4273e57e7148/1*FLM2SGIhAI7Z7OWIJpeT9A.webp" alt="" loading="lazy" decoding="async" width="1150" height="852" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMTUwIiBoZWlnaHQ9Ijg1MiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>複製完長得會跟範例專案一模一樣。</p>

<p>3. 首次部署「網頁應用程式」查看結果</p>

<p><img src="/assets/4273e57e7148/1*KbWpaEinVZh6M8o8bCCl6A.webp" alt="" loading="lazy" decoding="async" width="833" height="239" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4MzMiIGhlaWdodD0iMjM5Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><img src="/assets/4273e57e7148/1*2RrwNxZVidodX6Kq037Yvw.webp" alt="" loading="lazy" decoding="async" width="761" height="601" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjEiIGhlaWdodD0iNjAxIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><img src="/assets/4273e57e7148/1*CZSIfASW4hmQeXwTS3x7Fg.webp" alt="" loading="lazy" decoding="async" width="761" height="602" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjEiIGhlaWdodD0iNjAyIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>專案右上角「部署」 → 「新增部署作業」 → 類型「網頁應用程式」:</p>

<p><strong>執行身份：</strong></p>
<ul>
  <li>我 <code class="language-plaintext highlighter-rouge">統一都用你的帳號身分執行腳本。</code></li>
  <li>存取網頁程式的使用者 <code class="language-plaintext highlighter-rouge">會以當前登入的 Google 帳號使用者身份執行腳本。</code></li>
</ul>

<p><strong>誰可以存取：</strong></p>
<ul>
  <li>只有我自己</li>
  <li><strong>XXX 同個組織中的所有使用者</strong> <code class="language-plaintext highlighter-rouge">只有同組織＋已登入的 Google 帳號使用者可以存取。</code></li>
  <li>所有已登入 Google 帳號的使用者 <code class="language-plaintext highlighter-rouge">已登入的 Google 帳號使用者都可以存取。</code></li>
  <li>所有人 <code class="language-plaintext highlighter-rouge">不需要登入 Google 帳號、所有人都可以公開存取。</code></li>
</ul>

<blockquote>
  <p><em>如果是內部工具：可以選擇「 <strong>誰可以存取：XXX 同個組織中的所有使用者」+「執行身份：存取網頁程式的使用者」進行安全控管。</strong></em></p>
</blockquote>

<p>完成部署後的「網頁應用程式」網址，就是你的 Web App 打包工具網址，可以分享給團隊成員使用。(網址很醜，可以自己用短網址服務包裝一下、 <strong>更新部署內容不會異動網址</strong> )</p>
<h4 id="使用者首次使用需要同意授權">使用者首次使用需要同意授權</h4>

<p>首次點擊 Web App 網址，需要先同意授權。</p>

<p><img src="/assets/4273e57e7148/1*lckRkGtc7EVqz9aTZaBIOw.webp" alt="" loading="lazy" decoding="async" width="519" height="491" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1MTkiIGhlaWdodD0iNDkxIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><img src="/assets/4273e57e7148/1*l1t290T_4xs1ly-RjjZXtg.webp" alt="" loading="lazy" decoding="async" width="766" height="674" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjYiIGhlaWdodD0iNjc0Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><img src="/assets/4273e57e7148/1*O9ItCSBNoBcuqepd-6ygCA.webp" alt="" loading="lazy" decoding="async" width="722" height="630" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3MjIiIGhlaWdodD0iNjMwIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<ul>
  <li>Review Permission → 選擇要使用此 Web App 的帳戶身份</li>
  <li>未驗證警告視窗，點擊「進階」展開 → 點擊 前往「XXX」(不安全)</li>
</ul>

<p><img src="/assets/4273e57e7148/1*cYloLsBROLbZDPIciYUm5g.webp" alt="" loading="lazy" decoding="async" width="1054" height="876" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDU0IiBoZWlnaHQ9Ijg3NiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<ul>
  <li>點擊「允許」</li>
</ul>

<blockquote>
  <p><em>爾後如果腳本權限沒更動不用重新授權。</em></p>
</blockquote>

<p><strong>完成授權同意後就會進入打包工具首頁：</strong></p>

<p><img src="/assets/4273e57e7148/1*vG5SsVFHPPDukc4ej0hqLA.webp" alt="" loading="lazy" decoding="async" width="1149" height="1113" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMTQ5IiBoZWlnaHQ9IjExMTMiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<blockquote>
  <p>Demo 打包工具部署成功 🎉🎉🎉</p>
</blockquote>

<p>註:「這個應用程式是由 Google Apps Script 的使用者建立」這個提示無法自動隱欌。</p>
<h4 id="更新部署">更新部署</h4>

<blockquote>
  <p>⚠️所有程式碼的變動都需要更新部署才會生效。</p>
</blockquote>

<blockquote>
  <p>️️⚠️所有程式碼的變動都需要更新部署才會生效。</p>
</blockquote>

<blockquote>
  <p>⚠️所有程式碼的變動都需要更新部署才會生效。</p>
</blockquote>

<p>這邊要注意程式碼變動儲存完不會直接變動到 Web App 上，所以發現重整沒效果就是這個原因； <strong>需要到「部署」 → 「管理部署作業」→「編輯」→ 版本「建立新版本」 → 點擊「部署」 → 「完成」</strong> 。</p>

<p><img src="/assets/4273e57e7148/1*VsfCEfwnPlx9RbQ8DtXpnA.webp" alt="" loading="lazy" decoding="async" width="298" height="229" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyOTgiIGhlaWdodD0iMjI5Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><img src="/assets/4273e57e7148/1*n62MVd6o8W3hUtQpfn9Q0w.webp" alt="" loading="lazy" decoding="async" width="761" height="603" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjEiIGhlaWdodD0iNjAzIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<blockquote>
  <p><em>更新部署完再重整網頁就能看到改動生效。</em></p>
</blockquote>

<h4 id="新增測試部署方便開發">新增測試部署方便開發</h4>

<p><img src="/assets/4273e57e7148/1*4fLoW6jf8z1AIXvULW-AOA.webp" alt="" loading="lazy" decoding="async" width="413" height="235" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0MTMiIGhlaWdodD0iMjM1Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><img src="/assets/4273e57e7148/1*yvTxeaImQ4Mr7FwESsuf5A.webp" alt="" loading="lazy" decoding="async" width="761" height="599" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjEiIGhlaWdodD0iNTk5Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>如同前述，所有改動要生效都要更新部署；這在開發階段非常麻煩，因此在開發階段我們可以用「測試部署」來快速驗證改動是否正確。</p>

<p><strong>到「部署」 → 「測試部署作業」 → 取得測試用「網頁應用程式」網址。</strong></p>

<p><img src="/assets/4273e57e7148/1*Rt0XEw9uAev58isLEnsoPA.webp" alt="" loading="lazy" decoding="async" width="762" height="603" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjIiIGhlaWdodD0iNjAzIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>在開發階段我們直接使用此網址就能儲存，檔案更改儲存完，回到這個開發用網址重整網頁就能看到成果！</p>

<blockquote>
  <p><strong><em>都開發好之後再照前文說的更新部署，釋出給使用者使用。</em></strong></p>
</blockquote>

<h3 id="修改-demo-範例專案串接真實資料">修改 Demo 範例專案串接真實資料</h3>

<p>再來才是重點，串接真實資料，GitHub Actions Workflow 參考的是 <a href="/posts/zrealm-dev/github-actions-ios-ci-cd-完整實作自動化建置-測試與部署流程教學-4b001d2e8440/"><strong>上一篇文章中建立好的 CI/CD 流程</strong></a> ，你也可依照實際的 Actions Workflow 調整參數。</p>
<h4 id="️修改前請注意">⚠️修改前請注意</h4>

<p>Google Apps Script 平台並不能很好的支援多人或甚至多開視窗開發，我自己踩過的雷是不小心開了兩個編輯視窗，在 A 編輯完，後來在 B 編輯，所以修改都被 B 的舊版覆蓋掉了；因此 <strong>建議同時間只有一個人一個視窗在編輯 Script</strong> 。</p>
<h4 id="github-串接">GitHub 串接</h4>

<p><strong>帶入 GitHub API Token:</strong></p>

<p>GitHub -&gt; 帳號 -&gt; Settings -&gt; Developer Settings -&gt; Fine-grained personal access tokens or Personal access tokens (classic)。</p>

<p>建議使用 Fine-grained personal access tokens 比較安全(但有期限)。</p>

<p><strong>Fine-grained personal access tokens 需要的權限如下：</strong></p>

<p><img src="/assets/4273e57e7148/1*lCNQHwC4EMU4gNXYC-zs2A.webp" alt="" loading="lazy" decoding="async" width="821" height="579" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4MjEiIGhlaWdodD0iNTc5Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<ul>
  <li>Repo: 記得選要操作的 Repo</li>
  <li>Permissions: <code class="language-plaintext highlighter-rouge">Actions (read/write)</code> 、 <code class="language-plaintext highlighter-rouge">Administration (read only)</code></li>
</ul>

<blockquote>
  <p><em>如果不想依賴在某人的帳號上建議建立一支乾淨的團隊 GitHub 帳號，使用它的 Token。</em></p>
</blockquote>

<p>到 GAS 專案 → <code class="language-plaintext highlighter-rouge">Credentials.gs</code> → 將 Token 帶入到 <code class="language-plaintext highlighter-rouge">githubToken</code> 變數中。</p>

<p><strong>替換 GithubStub 成 GitHub:</strong></p>

<p>到 GAS 專案 → <code class="language-plaintext highlighter-rouge">Settings.gs</code> → 將：</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">iOSGitHub</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">GitHubStub</span><span class="p">(</span><span class="nx">githubToken</span><span class="p">,</span> <span class="nx">iOSRepoPath</span><span class="p">);</span>
</code></pre></div></div>

<p><strong>改成</strong></p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">iOSGitHub</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">GitHub</span><span class="p">(</span><span class="nx">githubToken</span><span class="p">,</span> <span class="nx">iOSRepoPath</span><span class="p">);</span>
</code></pre></div></div>

<p>儲存檔案。</p>

<p><strong>重新整理測試用「網頁應用程式」網址查看改動是否正確:</strong></p>

<p><img src="/assets/4273e57e7148/1*30k1iDALT9WdupmuBiG2uQ.webp" alt="" loading="lazy" decoding="async" width="958" height="859" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NTgiIGhlaWdodD0iODU5Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<blockquote>
  <p><em>能正確顯示資料代表： <strong>GitHub 改串真實資料成功</strong> 🎉🎉🎉</em></p>
</blockquote>

<p>也能順手切到「Runner 狀態」查看 Self-hosted Runner 狀態撈取正不正常：</p>

<p><img src="/assets/4273e57e7148/1*K3yzISn5J7o1e1F5zhIQpg.webp" alt="" loading="lazy" decoding="async" width="972" height="394" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NzIiIGhlaWdodD0iMzk0Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>註：我 Runner 沒開…所以是離線中。</p>
<h4 id="slack-串接">Slack 串接</h4>

<p>為了串接 Slack 通知，我們首先要回到 Repo → GitHub Actions 新增一個包裹打包 Action Workflow 的通知容器 Action。</p>

<p><strong>CD-Deploy-Form.yml:</strong></p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Workflow(Action) 名稱</span>
<span class="na">name</span><span class="pi">:</span> <span class="s">CD-Deploy-Form</span>

<span class="c1"># Actions Log 的標題名稱</span>
<span class="na">run-name</span><span class="pi">:</span> <span class="s2">"</span><span class="s">[CD-Deploy-Form]</span><span class="nv"> </span><span class="s">${{</span><span class="nv"> </span><span class="s">github.ref</span><span class="nv"> </span><span class="s">}}"</span>

<span class="c1"># 同個 Concurrency Group 如果有新的 Job 會取消正在跑的</span>
<span class="c1"># 例如 重複觸發相同分支的打包任務，會取消前一個任務</span>
<span class="na">concurrency</span><span class="pi">:</span>
  <span class="na">group</span><span class="pi">:</span> <span class="s">${{ github.workflow }}-${{ github.ref }}</span>
  <span class="na">cancel-in-progress</span><span class="pi">:</span> <span class="kc">true</span>

<span class="c1"># 觸發事件</span>
<span class="na">on</span><span class="pi">:</span>
  <span class="c1"># 手動表單觸發</span>
  <span class="na">workflow_dispatch</span><span class="pi">:</span>
    <span class="c1"># 表單 Inputs 欄位</span>
    <span class="na">inputs</span><span class="pi">:</span>
      <span class="c1"># App 版本號</span>
      <span class="na">VERSION_NUMBER</span><span class="pi">:</span>
        <span class="na">description</span><span class="pi">:</span> <span class="s1">'</span><span class="s">Version</span><span class="nv"> </span><span class="s">Number</span><span class="nv"> </span><span class="s">of</span><span class="nv"> </span><span class="s">the</span><span class="nv"> </span><span class="s">app</span><span class="nv"> </span><span class="s">(e.g.,</span><span class="nv"> </span><span class="s">1.0.0).</span><span class="nv"> </span><span class="s">Auto-detect</span><span class="nv"> </span><span class="s">from</span><span class="nv"> </span><span class="s">the</span><span class="nv"> </span><span class="s">Xcode</span><span class="nv"> </span><span class="s">project</span><span class="nv"> </span><span class="s">if</span><span class="nv"> </span><span class="s">left</span><span class="nv"> </span><span class="s">blank.'</span>
        <span class="na">required</span><span class="pi">:</span> <span class="kc">false</span>
        <span class="na">type</span><span class="pi">:</span> <span class="s">string</span>
      <span class="c1"># App Build Number</span>
      <span class="na">BUILD_NUMBER</span><span class="pi">:</span>
        <span class="na">description</span><span class="pi">:</span> <span class="s1">'</span><span class="s">Build</span><span class="nv"> </span><span class="s">number</span><span class="nv"> </span><span class="s">of</span><span class="nv"> </span><span class="s">the</span><span class="nv"> </span><span class="s">app</span><span class="nv"> </span><span class="s">(e.g.,</span><span class="nv"> </span><span class="s">1).</span><span class="nv"> </span><span class="s">Will</span><span class="nv"> </span><span class="s">use</span><span class="nv"> </span><span class="s">a</span><span class="nv"> </span><span class="s">timestamp</span><span class="nv"> </span><span class="s">if</span><span class="nv"> </span><span class="s">left</span><span class="nv"> </span><span class="s">blank.'</span>
        <span class="na">required</span><span class="pi">:</span> <span class="kc">false</span>
        <span class="na">type</span><span class="pi">:</span> <span class="s">string</span>
      <span class="c1"># App Release Note</span>
      <span class="na">RELEASE_NOTE</span><span class="pi">:</span>
        <span class="na">description</span><span class="pi">:</span> <span class="s1">'</span><span class="s">Release</span><span class="nv"> </span><span class="s">notes</span><span class="nv"> </span><span class="s">of</span><span class="nv"> </span><span class="s">the</span><span class="nv"> </span><span class="s">deployment.'</span>
        <span class="na">required</span><span class="pi">:</span> <span class="kc">false</span>
        <span class="na">type</span><span class="pi">:</span> <span class="s">string</span>
      <span class="c1"># 觸發者的 Slack User ID</span>
      <span class="na">SLACK_USER_ID</span><span class="pi">:</span>
        <span class="na">description</span><span class="pi">:</span> <span class="s1">'</span><span class="s">Slack</span><span class="nv"> </span><span class="s">user</span><span class="nv"> </span><span class="s">id.'</span>
        <span class="na">required</span><span class="pi">:</span> <span class="kc">true</span>
        <span class="na">type</span><span class="pi">:</span> <span class="s">string</span>
      <span class="c1"># 觸發者的 Email</span>
      <span class="na">AUTHOR</span><span class="pi">:</span>
        <span class="na">description</span><span class="pi">:</span> <span class="s1">'</span><span class="s">Trigger</span><span class="nv"> </span><span class="s">author</span><span class="nv"> </span><span class="s">email.'</span>
        <span class="na">required</span><span class="pi">:</span> <span class="kc">true</span>
        <span class="na">type</span><span class="pi">:</span> <span class="s">string</span>
        
<span class="c1"># Job 工作項目</span>
<span class="na">jobs</span><span class="pi">:</span>
  <span class="c1"># 開始打包時傳送 Slack 訊息</span>
  <span class="c1"># Job ID</span>
  <span class="na">start-message</span><span class="pi">:</span>
    <span class="c1"># 小工作直接用 GitHub Hosted Runner 跑，用量不大</span>
    <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
    
    <span class="c1"># 設定最長 Timeout 時間，防止異常情況發生時無止盡的等待</span>
    <span class="c1"># 正常情況不可能跑超過 5 分鐘</span>
    <span class="na">timeout-minutes</span><span class="pi">:</span> <span class="m">5</span>

    <span class="c1"># 工作步驟</span>
    <span class="na">steps</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Post a Start Slack Message</span>
        <span class="na">id</span><span class="pi">:</span> <span class="s">slack</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">slackapi/slack-github-action@v2.0.0</span>
        <span class="na">with</span><span class="pi">:</span>
          <span class="na">method</span><span class="pi">:</span> <span class="s">chat.postMessage</span>
          <span class="na">token</span><span class="pi">:</span> <span class="s">${{ secrets.SLACK_BOT_TOKEN }}</span>
          <span class="na">payload</span><span class="pi">:</span> <span class="pi">|</span>
            <span class="s">channel: ${{ inputs.SLACK_USER_ID }}</span>
            <span class="s">text: "已收到打包請求。\nID: &lt;${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|${{ github.run_id }}&gt;\nBranch: ${{ github.ref_name }}\ncc'ed &lt;@${{ inputs.SLACK_USER_ID }}&gt;"</span>
    <span class="c1"># Job Output 給後續 Job 使用</span>
    <span class="c1"># ts = Slack 訊息 ID，後續通知才可以 Reply 在同個 Threads</span>
    <span class="na">outputs</span><span class="pi">:</span>
      <span class="na">ts</span><span class="pi">:</span> <span class="s">${{ steps.slack.outputs.ts }}</span>

  <span class="na">deploy</span><span class="pi">:</span>
    <span class="c1"># Job 預設是並發執行，用 needs 限制需等待 start-message 完成才執行</span>
    <span class="c1"># 執行打包部署任務</span>
    <span class="na">needs</span><span class="pi">:</span> <span class="s">start-message</span>
    <span class="na">uses</span><span class="pi">:</span> <span class="s">./.github/workflows/CD-Deploy.yml</span>
    <span class="na">secrets</span><span class="pi">:</span> <span class="s">inherit</span>
    <span class="na">with</span><span class="pi">:</span>
      <span class="na">VERSION_NUMBER</span><span class="pi">:</span> <span class="s">${{ inputs.VERSION_NUMBER }}</span>
      <span class="na">BUILD_NUMBER</span><span class="pi">:</span> <span class="s">${{ inputs.BUILD_NUMBER }}</span>
      <span class="na">RELEASE_NOTE</span><span class="pi">:</span> <span class="s">${{ inputs.RELEASE_NOTE }}</span>
      <span class="na">AUTHOR</span><span class="pi">:</span> <span class="s">${{ inputs.AUTHOR }}</span>

  <span class="c1"># 打包部署任務成功訊息</span>
  <span class="na">end-message-success</span><span class="pi">:</span>
    <span class="na">needs</span><span class="pi">:</span> <span class="pi">[</span><span class="nv">start-message</span><span class="pi">,</span> <span class="nv">deploy</span><span class="pi">]</span>
    <span class="na">if</span><span class="pi">:</span> <span class="s">${{ needs.deploy.result == 'success' }}</span>
    <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
    <span class="na">timeout-minutes</span><span class="pi">:</span> <span class="m">5</span>
    <span class="na">steps</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Post a Success Slack Message</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">slackapi/slack-github-action@v2.0.0</span>
        <span class="na">with</span><span class="pi">:</span>
          <span class="na">method</span><span class="pi">:</span> <span class="s">chat.postMessage</span>
          <span class="na">token</span><span class="pi">:</span> <span class="s">${{ secrets.SLACK_BOT_TOKEN }}</span>
          <span class="na">payload</span><span class="pi">:</span> <span class="pi">|</span>
            <span class="s">channel: ${{ inputs.SLACK_USER_ID }}</span>
            <span class="s">thread_ts: "${{ needs.start-message.outputs.ts }}"</span>
            <span class="s">text: "✅ 打包部署成功。\n\ncc'ed &lt;@${{ inputs.SLACK_USER_ID }}&gt;"</span>
  
  <span class="c1"># 打包部署任務失敗訊息</span>
  <span class="na">end-message-failure</span><span class="pi">:</span>
    <span class="na">needs</span><span class="pi">:</span> <span class="pi">[</span><span class="nv">deploy</span><span class="pi">,</span> <span class="nv">start-message</span><span class="pi">]</span>
    <span class="na">if</span><span class="pi">:</span> <span class="s">${{ needs.deploy.result == 'failure' }}</span>
    <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
    <span class="na">timeout-minutes</span><span class="pi">:</span> <span class="m">5</span>
    <span class="na">steps</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Post a Failure Slack Message</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">slackapi/slack-github-action@v2.0.0</span>
        <span class="na">with</span><span class="pi">:</span>
          <span class="na">method</span><span class="pi">:</span> <span class="s">chat.postMessage</span>
          <span class="na">token</span><span class="pi">:</span> <span class="s">${{ secrets.SLACK_BOT_TOKEN }}</span>
          <span class="na">payload</span><span class="pi">:</span> <span class="pi">|</span>
            <span class="s">channel: ${{ inputs.SLACK_USER_ID }}</span>
            <span class="s">thread_ts: "${{ needs.start-message.outputs.ts }}"</span>
            <span class="s">text: "❌ 打包部署失敗，請檢查執行狀況結果或稍後再試。\n\ncc'ed &lt;@${{ inputs.SLACK_USER_ID }}&gt;"</span>

  <span class="c1"># 打包部署任務取消訊息</span>
  <span class="na">end-message-cancelled</span><span class="pi">:</span>
    <span class="na">needs</span><span class="pi">:</span> <span class="pi">[</span><span class="nv">deploy</span><span class="pi">,</span> <span class="nv">start-message</span><span class="pi">]</span>
    <span class="na">if</span><span class="pi">:</span> <span class="s">${{ needs.deploy.result == 'cancelled' }}</span>
    <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
    <span class="na">timeout-minutes</span><span class="pi">:</span> <span class="m">5</span>
    <span class="na">steps</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Post a Cancelled Slack Message</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">slackapi/slack-github-action@v2.0.0</span>
        <span class="na">with</span><span class="pi">:</span>
          <span class="na">method</span><span class="pi">:</span> <span class="s">chat.postMessage</span>
          <span class="na">token</span><span class="pi">:</span> <span class="s">${{ secrets.SLACK_BOT_TOKEN }}</span>
          <span class="na">payload</span><span class="pi">:</span> <span class="pi">|</span>
            <span class="s">channel: ${{ inputs.SLACK_USER_ID }}</span>
            <span class="s">thread_ts: "${{ needs.start-message.outputs.ts }}"</span>
            <span class="s">text: ":black_square_for_stop: 打包部署已取消。\n\ncc'ed &lt;@${{ inputs.SLACK_USER_ID }}&gt;"</span>
</code></pre></div></div>

<p><strong>完整程式碼：</strong> <a href="https://github.com/ZhgChgLi/github-actions-ci-cd-demo/blob/main/.github/workflows/CD-Deploy-Form.yml" target="_blank">CD-Deploy-Form.yml</a></p>

<p><img src="/assets/4273e57e7148/1*NatyC_Oid4BrKYk4nehuKA.webp" alt="" loading="lazy" decoding="async" width="952" height="287" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NTIiIGhlaWdodD0iMjg3Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>這個 Action 只是個容器，串接 Slack 通知，實際是復用 <a href="/posts/zrealm-dev/github-actions-ios-ci-cd-完整實作自動化建置-測試與部署流程教學-4b001d2e8440/">上一篇文章</a> 中寫好的 <a href="https://github.com/ZhgChgLi/github-actions-ci-cd-demo/blob/main/.github/workflows/CD-Deploy.yml" target="_blank">CD-Deploy.yml</a> Action。</p>
<ul>
  <li>Slack Bot App 建立、發訊息權限設定可參考我 <a href="/posts/zrealm-robotic-process-automation/slack-app-整合-chatgpt-利用-openai-api-與-google-cloud-functions-自建智能助理-bd94cc88f9c9/">之前的文章</a></li>
  <li>記得到 Repo → Secrets 新增對應的 <code class="language-plaintext highlighter-rouge">SLACK_BOT_TOKEN</code> 跟帶入 Slack Bot App Token 值</li>
</ul>

<p>回到 GAS 專案 → <code class="language-plaintext highlighter-rouge">Credentials.gs</code> → 將 Token 帶入到 <code class="language-plaintext highlighter-rouge">slackBotToken</code> 變數中。</p>

<p>再到 GAS 專案 → <code class="language-plaintext highlighter-rouge">Settings.gs</code> → 將：</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">slack</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">SlackStub</span><span class="p">(</span><span class="nx">slackBotToken</span><span class="p">);</span>
</code></pre></div></div>

<p><strong>改成</strong></p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">slack</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Slack</span><span class="p">(</span><span class="nx">slackBotToken</span><span class="p">);</span>
</code></pre></div></div>

<p>儲存檔案。</p>

<blockquote>
  <p><em>如果你沒有現成的 Slack Bot App 可發通知也懶得建立，可以忽略這裡的所有步驟，並刪除 GAS 專案中有關 slack 的使用。</em></p>
</blockquote>

<h4 id="github-串接--打包表單">GitHub 串接 — 打包表單</h4>

<p>到 GAS 專案 → <code class="language-plaintext highlighter-rouge">Controller_iOS.gs</code> → 調整 <code class="language-plaintext highlighter-rouge">View_iOS_Form.html</code> 內容:
移除假 Asana Tasks 串接方法：</p>
<pre><code class="language-php-template">      &lt;? tasks.forEach(function(task) { ?&gt;
      &lt;option value="&lt;?=task.githubBranch?&gt;"&gt;[&lt;?=task.id?&gt;] &lt;?=task.title?&gt;&lt;/option&gt;
      &lt;? }) ?&gt;
</code></pre>

<p>也可以在這邊自己調整預設分支(這邊是 <code class="language-plaintext highlighter-rouge">main</code> )。</p>

<p>—</p>

<p>到 GAS 專案 → <code class="language-plaintext highlighter-rouge">Controller_iOS.gs</code> → 調整 <code class="language-plaintext highlighter-rouge">iOSLoadForm()</code> 內容:</p>
<ul>
  <li>移除 <code class="language-plaintext highlighter-rouge">template.tasks = Stubable.fetchStubAsanaTasks();</code> 這行假串接 Asana 的方法。
如果要串 Asana/Jira 可以直接問 ChatGPT 請他幫忙產串接方法。</li>
  <li><code class="language-plaintext highlighter-rouge">template.prs = iOSGitHub.fetchOpenPRs();</code> 是真的串 GitHub API 拿 Opened PR List，可依需求保留。</li>
</ul>

<p><strong>送出後的處理</strong> 在 <code class="language-plaintext highlighter-rouge">iOSSubmitForm()</code> 內容:</p>

<p>可依照實際 GitHub Actions Workflow 檔案名稱、 <code class="language-plaintext highlighter-rouge">workflow_dispatch</code> inputs 欄位參數進行調整：</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="nx">iOSGitHub</span><span class="p">.</span><span class="nf">dispatchWorkflow</span><span class="p">(</span><span class="dl">"</span><span class="s2">CD-Deploy-Form.yml</span><span class="dl">"</span><span class="p">,</span> <span class="nx">branch</span><span class="p">,</span> <span class="p">{</span>
    <span class="dl">"</span><span class="s2">BUILD_NUMBER</span><span class="dl">"</span><span class="p">:</span> <span class="nx">buildNumber</span><span class="p">,</span>
    <span class="dl">"</span><span class="s2">VERSION_NUMBER</span><span class="dl">"</span><span class="p">:</span> <span class="nx">versionNumber</span><span class="p">,</span>
    <span class="dl">"</span><span class="s2">VERSION_NUMBER</span><span class="dl">"</span><span class="p">:</span> <span class="nx">versionNumber</span><span class="p">,</span>
    <span class="dl">"</span><span class="s2">RELEASE_NOTE</span><span class="dl">"</span><span class="p">:</span> <span class="nx">releaseNote</span><span class="p">,</span>
    <span class="dl">"</span><span class="s2">AUTHOR</span><span class="dl">"</span><span class="p">:</span> <span class="nx">email</span><span class="p">,</span>
    <span class="dl">"</span><span class="s2">SLACK_USER_ID</span><span class="dl">"</span><span class="p">:</span> <span class="nx">slack</span><span class="p">.</span><span class="nf">fetchUserID</span><span class="p">(</span><span class="nx">email</span><span class="p">)</span>
  <span class="p">});</span>
</code></pre></div></div>

<p>也可以加入自己的必填條件驗證，這邊只驗證一定要填分支，不然會跳錯誤訊息。</p>

<p>如果認為這樣不夠安全可以自己再加入密碼驗證或只有特殊帳號可以使用。</p>

<blockquote>
  <p><em>最後一行 <strong>Slack 通知功能需要設定好 Slack</strong> ，如果沒有 Slack Bot App 或懶得串接 Slack，在 <a href="https://github.com/ZhgChgLi/github-actions-ci-cd-demo/actions" target="_blank">Demo Actions Repo</a> 中可以直接改用 <code class="language-plaintext highlighter-rouge">iOSGitHub.dispatchWorkflow("CD-Deploy.yml")</code> 然後移除掉 <code class="language-plaintext highlighter-rouge">SLACK_USER_ID</code> 參數即可。</em></p>
</blockquote>

<p><strong>重新整理測試用「網頁應用程式」網址查看改動是否正確：</strong></p>

<p><img src="/assets/4273e57e7148/1*OkJJrssZBcJPss_cMW-Qew.webp" alt="" loading="lazy" decoding="async" width="754" height="711" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NTQiIGhlaWdodD0iNzExIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>可以看到打包表單就只剩 Opened PR List 了。</p>

<p><strong>填好資料按「送出請求」測試看看打包表單：</strong></p>

<p><img src="/assets/4273e57e7148/1*dl-g3j6GH0AnYL8dqvSyYA.webp" alt="" loading="lazy" decoding="async" width="523" height="252" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1MjMiIGhlaWdodD0iMjUyIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><strong>提示送出成功代表沒問題，回到打包紀錄也能看到任務開始執行了 <em>🎉</em></strong> ：</p>

<p><img src="/assets/4273e57e7148/1*cByPF6T8WdXKionifRj1Ew.webp" alt="" loading="lazy" decoding="async" width="959" height="383" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NTkiIGhlaWdodD0iMzgzIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<blockquote>
  <p><em>重複點打包紀錄能更新進度。</em></p>
</blockquote>

<p><strong>常見送出錯誤：</strong></p>

<blockquote>
  <p><code class="language-plaintext highlighter-rouge">Required input ‘SLACK_USER_ID’ not provided</code> <em>: GitHub Actions 這個SLACK_USER_ID 欄位為必填，但沒有帶，可能是 Slack 設定失敗、當前 User Email 找不到對應的 Slack UID。</em></p>
</blockquote>

<blockquote>
  <p><em><code class="language-plaintext highlighter-rouge">Workflow does not have ‘workflow_dispatch’ trigger</code> 、 <code class="language-plaintext highlighter-rouge">分支過舊，請更新 xxx 分支</code> : 選擇的分支找不到對應的 Action Workflow 檔案(iOSGitHub.dispatchWorkflow 指定的檔案)。</em></p>
</blockquote>

<blockquote>
  <p><em><code class="language-plaintext highlighter-rouge">No ref found for</code> 、 <code class="language-plaintext highlighter-rouge">找不到分支</code> : 找不到此分支。</em></p>
</blockquote>

<h3 id="firebase-app-distribution--取得下載連結串接">Firebase App Distribution — 取得下載連結串接</h3>

<p>最後一個小功能是串接 Firebase App Distribution 直接取得下載資訊跟連結，方便在手機上開啟打包平台工具點擊直接下載安裝。</p>

<blockquote>
  <p><em>之前介紹過「 <a href="/posts/zrealm-robotic-process-automation/google-apps-script-快速串接-firebase-app-distribution-api-教學與進階設定-71400d408dc8/">Google Apps Script x Google APIs 快速串接整合方式</a> 」GAS 能快速無痛整合 Firebase。</em></p>
</blockquote>

<h4 id="串接原理"><strong>串接原理</strong></h4>

<blockquote>
  <p><strong><em>在串接之前先講一下這個「Tricky」的串接原理。</em></strong></p>
</blockquote>

<p>我們的打包平台是沒有資料庫的，純做 API 中繼站；所以實際上是我們在 <a href="https://github.com/ZhgChgLi/github-actions-ci-cd-demo/actions/workflows/CD-Deploy.yml" target="_blank"><strong>GitHub Acitons CD-Deploy.yml</strong></a> 打包作業時，把 Job Run ID 帶入到 Release Note (當然也可以帶到 Build Number)：</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">ID</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="p">{ github.run_id </span><span class="k">}</span><span class="s2">}"</span> // Job Run ID
<span class="nv">COMMIT_SHA</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="p">{ github.sha </span><span class="k">}</span><span class="s2">}"</span>
<span class="nv">BRANCH_NAME</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="p">{ github.ref_name </span><span class="k">}</span><span class="s2">}"</span>
<span class="nv">AUTHOR</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="p">{ env.AUTHOR </span><span class="k">}</span><span class="s2">}"</span>

<span class="c"># 組合 Release Note</span>
<span class="nv">RELEASE_NOTE</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="p">{ env.RELEASE_NOTE </span><span class="k">}</span><span class="s2">}
ID: </span><span class="k">${</span><span class="nv">ID</span><span class="k">}</span><span class="s2">
Commit SHA: </span><span class="k">${</span><span class="nv">COMMIT_SHA</span><span class="k">}</span><span class="s2">
Branch: </span><span class="k">${</span><span class="nv">BRANCH_NAME</span><span class="k">}</span><span class="s2">
Author: </span><span class="k">${</span><span class="nv">AUTHOR</span><span class="k">}</span><span class="s2">
"</span>

<span class="c"># 執行 Fastlane 打包＆部署 Lane</span>
bundle <span class="nb">exec </span>fastlane beta release_notes:<span class="s2">"</span><span class="k">${</span><span class="nv">RELEASE_NOTE</span><span class="k">}</span><span class="s2">"</span> version_number:<span class="s2">"</span><span class="k">${</span><span class="nv">VERSION_NUMBER</span><span class="k">}</span><span class="s2">"</span> build_number:<span class="s2">"</span><span class="k">${</span><span class="nv">BUILD_NUMBER</span><span class="k">}</span><span class="s2">"</span>
</code></pre></div></div>

<p>這樣 Firebase App Distribution Release Notes 就會有 Job Run ID。</p>

<p>GAS Web App 打包工具平台會串 GitHub API 取得 GitHub Actions 執行紀錄我們直接用 API 給的 Job Run ID 帶到 Firebase App Distribution API 查詢 Release Notes 中有包含 <code class="language-plaintext highlighter-rouge">*ID: XXX*</code> 的版本，就能找到對應的打包紀錄了。</p>

<p><strong>不用任何資料庫也能做到兩個工具平台的對應。</strong></p>

<p><img src="/assets/4273e57e7148/1*AJRMWv_rqu64H0ZrY4q6QA.webp" alt="" loading="lazy" decoding="async" width="559" height="347" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1NTkiIGhlaWdodD0iMzQ3Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><img src="/assets/4273e57e7148/1*Sdzt1MDXh7TNnMDkpc5uJg.webp" alt="" loading="lazy" decoding="async" width="1055" height="702" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDU1IiBoZWlnaHQ9IjcwMiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<h4 id="串接專案設定">串接專案設定</h4>

<p>到 GAS → 專案設定 → Google Cloud Platform (GCP) 專案 → 變更專案：</p>

<p><img src="/assets/4273e57e7148/1*klclBbiBQXNBzbzzj1jH0Q.webp" alt="" loading="lazy" decoding="async" width="1140" height="1115" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMTQwIiBoZWlnaHQ9IjExMTUiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/4273e57e7148/1*gv8m_v5O_yyUrq8uQoverQ.webp" alt="" loading="lazy" decoding="async" width="1086" height="632" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDg2IiBoZWlnaHQ9IjYzMiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>輸入想串接的 Firebase 專案編號。</p>

<p><img src="/assets/4273e57e7148/1*OEaxA6SZWYbiVstOK60hQg.webp" alt="" loading="lazy" decoding="async" width="760" height="444" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjAiIGhlaWdodD0iNDQ0Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<blockquote>
  <p><strong><em>初次設定可能會跳錯誤</em></strong> <em>「如要變更專案，請設定 OAuth 同意畫面。設定 OAuth 同意畫面詳細資料。」沒有的話可跳過以下步驟。</em></p>
</blockquote>

<p><strong>點擊「OAuth 同意畫面詳細資料」連結 → 點擊「設定同意畫面」：</strong></p>

<p><img src="/assets/4273e57e7148/1*oiZeO5mEqH-D3tHrJsnE1A.webp" alt="" loading="lazy" decoding="async" width="1149" height="676" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMTQ5IiBoZWlnaHQ9IjY3NiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><strong>點擊「開始」:</strong></p>

<p><img src="/assets/4273e57e7148/1*NK9RG0kaXoxJB5X-B8vhww.webp" alt="" loading="lazy" decoding="async" width="762" height="550" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjIiIGhlaWdodD0iNTUwIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><strong>應用程式資訊:</strong></p>
<ul>
  <li>應用程式名稱: <code class="language-plaintext highlighter-rouge">輸入你的工具名稱</code></li>
  <li>使用者支援電子郵件: <code class="language-plaintext highlighter-rouge">選擇電子郵件</code></li>
</ul>

<p><strong>目標對象:</strong></p>
<ul>
  <li>內部：僅限組織內部夥伴使用</li>
  <li>外部：所有 Google 帳戶使用者都能同意授權後使用</li>
</ul>

<p><strong>聯絡資訊:</strong></p>
<ul>
  <li>輸入接收通知用電子郵件</li>
</ul>

<p><strong>勾選同意《 <a href="https://developers.google.com/terms/api-services-user-data-policy?hl=zh_TW" target="_blank">Google API 服務：使用者資料政策</a> 》。</strong></p>

<p>最後點擊「 <strong>建立</strong> 」。</p>

<p>—</p>

<p>回到 GAS → 專案設定 → Google Cloud Platform (GCP) 專案 → 變更專案：</p>

<p>再次輸入 Firebase 專案編號點擊「變更專案」。</p>

<p><img src="/assets/4273e57e7148/1*g-Lo04WjhaDjtXeXthNtDg.webp" alt="" loading="lazy" decoding="async" width="779" height="272" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NzkiIGhlaWdodD0iMjcyIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><strong>沒出現錯誤就是完成綁定了。</strong></p>

<p>—</p>

<p><strong>如果選擇的是「外部」可能還需要完成以下設定：</strong></p>

<p>點擊「專案編號」 → 展開左側欄 → 「API 和服務」→ 「OAuth 同意畫面」</p>

<p><img src="/assets/4273e57e7148/1*RJNQv8v3KZoTcVwUbsXSHQ.webp" alt="" loading="lazy" decoding="async" width="808" height="719" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4MDgiIGhlaWdodD0iNzE5Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>選擇「目標對象」 → 測試 點擊「發布應用程式」→ 完成。</p>

<p><img src="/assets/4273e57e7148/1*8vqrxhchsILPy4eSdvXfNg.webp" alt="" loading="lazy" decoding="async" width="626" height="445" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2MjYiIGhlaWdodD0iNDQ1Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<blockquote>
  <p><em>使用者就能照前文的「 <strong>使用者首次使用需要同意授權</strong> 」步驟完成授權就能使用了！</em></p>
</blockquote>

<p><strong>如果上述步驟沒設定，使用者會遇到以下錯誤：</strong></p>

<p><img src="/assets/4273e57e7148/1*2omUPFoubsrXVLHBPFkPbg.webp" alt="已封鎖存取權「XXX」未完成 Google 驗證程序" loading="lazy" decoding="async" width="766" height="673" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjYiIGhlaWdodD0iNjczIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>已封鎖存取權「XXX」未完成 Google 驗證程序</p>

<p>— — —</p>
<h4 id="串接專案">串接專案</h4>

<p>回到串接上，Firebase 是直接用 <code class="language-plaintext highlighter-rouge">ScriptApp.getOAuthToken()</code> 依照執行身份動態取得 Token 使用，因此不需要設定 Token。</p>

<p>只需要到 GAS 專案 → <code class="language-plaintext highlighter-rouge">Settings.gs</code> → 將：</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">iOSFirebase</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">FirebaseStub</span><span class="p">(</span><span class="nx">iOSFirebaseProject</span><span class="p">);</span>
</code></pre></div></div>

<p><strong>改成</strong></p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">iOSFirebase</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Firebase</span><span class="p">(</span><span class="nx">iOSFirebaseProject</span><span class="p">);</span>
</code></pre></div></div>

<p>即可。</p>

<p><strong>重新整理測試用「網頁應用程式」網址到打包紀錄 → 找一筆紀錄點擊「取得下載連結」：</strong></p>

<p><img src="/assets/4273e57e7148/1*9z-KsIAL5jyEzQvJv3YLgg.webp" alt="" loading="lazy" decoding="async" width="913" height="452" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MTMiIGhlaWdodD0iNDUyIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><img src="/assets/4273e57e7148/1*q0HUYN2W3UonQLzwxZKXdQ.webp" alt="" loading="lazy" decoding="async" width="1179" height="2556" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMTc5IiBoZWlnaHQ9IjI1NTYiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>如果有在 Firebase App Distribution Release Notes 找到對應的 Job Run Id 打包，就會直接顯示下載資訊跟點下載能直接導到下載頁。</p>

<blockquote>
  <p>完成！🎉🎉🎉</p>
</blockquote>

<h3 id="成果">成果</h3>

<p><img src="/assets/4273e57e7148/1*znvPmqsaivk3KhsE26sFwA.webp" alt="Demo Web App" loading="lazy" decoding="async" width="1010" height="766" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDEwIiBoZWlnaHQ9Ijc2NiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><a href="https://script.google.com/macros/s/AKfycbwNW6N5ozKbIz_E1HK6yFEUtA8KQrUciS-jcPsQptvIKlARmKgLxbQzNu8ksVeg-BmEfg/exec" target="_blank">Demo Web App</a></p>

<p>至此你已經把範例都改成您的實際可用的打包工具，剩下的客製化功能、更多第三方 API 串接、更多表單可以自行延伸 (跟 ChatGPT 討論)。</p>

<blockquote>
  <p><em>最後不要忘記都開發好測試完畢要上線，需要照前文步驟 — <strong>更新部署作業才會生效喔！</strong></em></p>
</blockquote>

<h3 id="串接延伸">串接延伸</h3>

<p>延續我們「中繼站」角色的精神，這邊提供幾個快速串接的 CheatSheet：</p>

<p><a href="https://developers.asana.com/reference/gettasksforproject" target="_blank"><strong>Asana API — 取得 Tasks:</strong></a></p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="kd">function</span> <span class="nf">asanaAPI</span><span class="p">(</span><span class="nx">endPoint</span><span class="p">,</span> <span class="nx">method</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">GET</span><span class="dl">"</span><span class="p">,</span> <span class="nx">data</span> <span class="o">=</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
    <span class="kd">var</span> <span class="nx">options</span> <span class="o">=</span> <span class="p">{</span>
      <span class="dl">"</span><span class="s2">method</span><span class="dl">"</span> <span class="p">:</span> <span class="nx">method</span><span class="p">,</span>
      <span class="dl">"</span><span class="s2">headers</span><span class="dl">"</span><span class="p">:</span> <span class="p">{</span>
          <span class="dl">"</span><span class="s2">Authorization</span><span class="dl">"</span><span class="p">:</span>  <span class="dl">"</span><span class="s2">Bearer </span><span class="dl">"</span><span class="o">+</span><span class="nx">asanaToken</span>
      <span class="p">},</span>
      <span class="dl">"</span><span class="s2">payload</span><span class="dl">"</span> <span class="p">:</span> <span class="nx">data</span>
    <span class="p">};</span>

    <span class="kd">var</span> <span class="nx">url</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">https://app.asana.com/api/1.0</span><span class="dl">"</span><span class="o">+</span><span class="nx">endPoint</span><span class="p">;</span>
    <span class="kd">var</span> <span class="nx">res</span> <span class="o">=</span> <span class="nx">UrlFetchApp</span><span class="p">.</span><span class="nf">fetch</span><span class="p">(</span><span class="nx">url</span><span class="p">,</span> <span class="nx">options</span><span class="p">);</span>
    <span class="kd">var</span> <span class="nx">data</span> <span class="o">=</span> <span class="nx">JSON</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="nx">res</span><span class="p">.</span><span class="nf">getContentText</span><span class="p">());</span>
    <span class="k">return</span> <span class="nx">data</span><span class="p">;</span>
<span class="p">}</span>

<span class="nf">asanaAPI</span><span class="p">(</span><span class="dl">"</span><span class="s2">/projects/{project_gid}/tasks</span><span class="dl">"</span><span class="p">)</span>
</code></pre></div></div>

<p><a href="https://developer.atlassian.com/cloud/jira/platform/rest/v3/intro/#about" target="_blank"><strong>Jira API — 取得 Tickets (JQL):</strong></a></p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// jql = 篩選條件</span>
<span class="kd">function</span> <span class="nf">jiraTickets</span><span class="p">(</span><span class="nx">jql</span><span class="p">)</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">url</span> <span class="o">=</span> <span class="s2">`https://xxx.atlassian.net/rest/api/3/search`</span><span class="p">;</span>
  <span class="kd">const</span> <span class="nx">maxResults</span> <span class="o">=</span> <span class="mi">100</span><span class="p">;</span>

  <span class="kd">let</span> <span class="nx">allIssues</span> <span class="o">=</span> <span class="p">[];</span>
  <span class="kd">let</span> <span class="nx">startAt</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
  <span class="kd">let</span> <span class="nx">total</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

  <span class="k">do</span> <span class="p">{</span>
    <span class="kd">const</span> <span class="nx">queryParams</span> <span class="o">=</span> <span class="p">{</span>
      <span class="na">jql</span><span class="p">:</span> <span class="nx">jql</span><span class="p">,</span>
      <span class="na">startAt</span><span class="p">:</span> <span class="nx">startAt</span><span class="p">,</span>
      <span class="na">maxResults</span><span class="p">:</span> <span class="nx">maxResults</span><span class="p">,</span>
      <span class="na">fields</span><span class="p">:</span> <span class="dl">"</span><span class="s2">assignee,summary,status</span><span class="dl">"</span>
    <span class="p">};</span>

    <span class="kd">const</span> <span class="nx">queryString</span> <span class="o">=</span> <span class="nb">Object</span><span class="p">.</span><span class="nf">keys</span><span class="p">(</span><span class="nx">queryParams</span><span class="p">)</span>
      <span class="p">.</span><span class="nf">map</span><span class="p">(</span><span class="nx">key</span> <span class="o">=&gt;</span> <span class="s2">`</span><span class="p">${</span><span class="nf">encodeURIComponent</span><span class="p">(</span><span class="nx">key</span><span class="p">)}</span><span class="s2">=</span><span class="p">${</span><span class="nf">encodeURIComponent</span><span class="p">(</span><span class="nx">queryParams</span><span class="p">[</span><span class="nx">key</span><span class="p">])}</span><span class="s2">`</span><span class="p">)</span>
      <span class="p">.</span><span class="nf">join</span><span class="p">(</span><span class="dl">"</span><span class="s2">&amp;</span><span class="dl">"</span><span class="p">);</span>

    <span class="kd">const</span> <span class="nx">options</span> <span class="o">=</span> <span class="p">{</span>
      <span class="na">method</span><span class="p">:</span> <span class="dl">"</span><span class="s2">get</span><span class="dl">"</span><span class="p">,</span>
      <span class="na">headers</span><span class="p">:</span> <span class="p">{</span>
        <span class="na">Authorization</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Basic </span><span class="dl">"</span> <span class="o">+</span> <span class="nx">jiraToken</span><span class="p">,</span>
        <span class="dl">"</span><span class="s2">Content-Type</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">application/json</span><span class="dl">"</span><span class="p">,</span>
      <span class="p">},</span>
      <span class="na">muteHttpExceptions</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
    <span class="p">};</span>

    <span class="kd">const</span> <span class="nx">response</span> <span class="o">=</span> <span class="nx">UrlFetchApp</span><span class="p">.</span><span class="nf">fetch</span><span class="p">(</span><span class="s2">`</span><span class="p">${</span><span class="nx">url</span><span class="p">}</span><span class="s2">?</span><span class="p">${</span><span class="nx">queryString</span><span class="p">}</span><span class="s2">`</span><span class="p">,</span> <span class="nx">options</span><span class="p">);</span>
    <span class="kd">const</span> <span class="nx">json</span> <span class="o">=</span> <span class="nx">JSON</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="nx">response</span><span class="p">.</span><span class="nf">getContentText</span><span class="p">());</span>
    <span class="k">if </span><span class="p">(</span><span class="nx">response</span><span class="p">.</span><span class="nf">getResponseCode</span><span class="p">()</span> <span class="o">!=</span> <span class="mi">200</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">throw</span> <span class="k">new</span> <span class="nc">Error</span><span class="p">(</span><span class="dl">"</span><span class="s2">Failed to fetch Jira issues.</span><span class="dl">"</span><span class="p">);</span> 
    <span class="p">}</span>

    <span class="k">if </span><span class="p">(</span><span class="nx">json</span><span class="p">.</span><span class="nx">issues</span> <span class="o">&amp;&amp;</span> <span class="nx">json</span><span class="p">.</span><span class="nx">issues</span><span class="p">.</span><span class="nx">length</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
      <span class="nx">allIssues</span> <span class="o">=</span> <span class="nx">allIssues</span><span class="p">.</span><span class="nf">concat</span><span class="p">(</span><span class="nx">json</span><span class="p">.</span><span class="nx">issues</span><span class="p">);</span>
      <span class="nx">total</span> <span class="o">=</span> <span class="nx">json</span><span class="p">.</span><span class="nx">total</span><span class="p">;</span>
      <span class="nx">startAt</span> <span class="o">+=</span> <span class="nx">json</span><span class="p">.</span><span class="nx">issues</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span>
    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
      <span class="k">break</span><span class="p">;</span>
    <span class="p">}</span>
  <span class="p">}</span> <span class="k">while </span><span class="p">(</span><span class="nx">startAt</span> <span class="o">&lt;</span> <span class="nx">total</span><span class="p">);</span>

  <span class="kd">var</span> <span class="nx">groupIssues</span> <span class="o">=</span> <span class="p">{};</span>
  <span class="k">for</span><span class="p">(</span><span class="kd">var</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o">&lt;</span> <span class="nx">allIssues</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="kd">const</span> <span class="nx">issue</span> <span class="o">=</span> <span class="nx">allIssues</span><span class="p">[</span><span class="nx">i</span><span class="p">];</span>
    <span class="k">if </span><span class="p">(</span><span class="nx">groupIssues</span><span class="p">[</span><span class="nx">issue</span><span class="p">.</span><span class="nx">fields</span><span class="p">.</span><span class="nx">status</span><span class="p">.</span><span class="nx">name</span><span class="p">]</span> <span class="o">==</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
      <span class="nx">groupIssues</span><span class="p">[</span><span class="nx">issue</span><span class="p">.</span><span class="nx">fields</span><span class="p">.</span><span class="nx">status</span><span class="p">.</span><span class="nx">name</span><span class="p">]</span> <span class="o">=</span> <span class="p">[];</span>
    <span class="p">}</span>
    <span class="nx">groupIssues</span><span class="p">[</span><span class="nx">issue</span><span class="p">.</span><span class="nx">fields</span><span class="p">.</span><span class="nx">status</span><span class="p">.</span><span class="nx">name</span><span class="p">].</span><span class="nf">push</span><span class="p">(</span><span class="nx">issue</span><span class="p">);</span>
  <span class="p">}</span>

  <span class="k">return</span> <span class="nx">groupIssues</span><span class="p">;</span>
<span class="p">}</span>

<span class="nf">jiraTickets</span><span class="p">(</span><span class="s2">`project IN(App)`</span><span class="p">);</span>
</code></pre></div></div>

<p><strong>如果真的需要資料庫，可以使用 Google Sheet 代替：</strong></p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">Saveable</span> <span class="p">{</span>
  <span class="nf">constructor</span><span class="p">(</span><span class="nx">type</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// https://docs.google.com/spreadsheets/d/Sheet-ID/edit</span>
    <span class="kd">const</span> <span class="nx">spreadsheet</span> <span class="o">=</span> <span class="nx">SpreadsheetApp</span><span class="p">.</span><span class="nf">openById</span><span class="p">(</span><span class="dl">"</span><span class="s2">Sheet-ID</span><span class="dl">"</span><span class="p">);</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">sheet</span> <span class="o">=</span> <span class="nx">spreadsheet</span><span class="p">.</span><span class="nf">getSheetByName</span><span class="p">(</span><span class="dl">"</span><span class="s2">Data</span><span class="dl">"</span><span class="p">);</span> <span class="c1">// Sheet Name</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">type</span> <span class="o">=</span> <span class="nx">type</span><span class="p">;</span>
  <span class="p">}</span>

  <span class="nf">write</span><span class="p">(</span><span class="nx">key</span><span class="p">,</span> <span class="nx">value</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">sheet</span><span class="p">.</span><span class="nf">appendRow</span><span class="p">([</span>
      <span class="k">this</span><span class="p">.</span><span class="nx">type</span><span class="p">,</span>
      <span class="nx">key</span><span class="p">,</span>
      <span class="nx">JSON</span><span class="p">.</span><span class="nf">stringify</span><span class="p">(</span><span class="nx">value</span><span class="p">)</span>
    <span class="p">]);</span>
  <span class="p">}</span>

  <span class="nf">read</span><span class="p">(</span><span class="nx">key</span><span class="p">)</span> <span class="p">{</span>
    <span class="kd">const</span> <span class="nx">data</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">sheet</span><span class="p">.</span><span class="nf">getDataRange</span><span class="p">().</span><span class="nf">getValues</span><span class="p">();</span>
    <span class="kd">const</span> <span class="nx">row</span> <span class="o">=</span> <span class="nx">data</span><span class="p">.</span><span class="nf">find</span><span class="p">(</span><span class="nx">r</span> <span class="o">=&gt;</span> <span class="nx">r</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">===</span> <span class="k">this</span><span class="p">.</span><span class="nx">type</span> <span class="o">&amp;&amp;</span> <span class="nx">r</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">===</span> <span class="nx">key</span><span class="p">);</span>
    <span class="k">if </span><span class="p">(</span><span class="nx">row</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">return</span> <span class="nx">JSON</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="nx">row</span><span class="p">[</span><span class="mi">2</span><span class="p">]);</span>
    <span class="p">}</span>
    <span class="k">return</span> <span class="kc">null</span><span class="p">;</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="kd">let</span> <span class="nx">saveable</span> <span class="o">=</span> <span class="nc">Saveable</span><span class="p">(</span><span class="dl">"</span><span class="s2">user</span><span class="dl">"</span><span class="p">);</span>
<span class="c1">// Write</span>
<span class="nx">saveable</span><span class="p">.</span><span class="nf">write</span><span class="p">(</span><span class="dl">"</span><span class="s2">birthday_zhgchgli</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">0718</span><span class="dl">"</span><span class="p">);</span>
<span class="c1">// Read</span>
<span class="nx">saveable</span><span class="p">.</span><span class="nf">read</span><span class="p">(</span><span class="dl">"</span><span class="s2">birthday_zhgchgli</span><span class="dl">"</span><span class="p">);</span> <span class="c1">// -&gt; 0718</span>
</code></pre></div></div>

<p><a href="https://api.slack.com/methods/chat.postMessage" target="_blank"><strong>Slack API &amp; 發送訊息方法：</strong></a></p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nf">slackSendMessage</span><span class="p">(</span><span class="nx">channel</span><span class="p">,</span> <span class="nx">text</span> <span class="o">=</span> <span class="dl">""</span><span class="p">,</span> <span class="nx">blocks</span> <span class="o">=</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">content</span> <span class="o">=</span> <span class="p">{</span>
    <span class="na">channel</span><span class="p">:</span> <span class="nx">channel</span><span class="p">,</span>
    <span class="na">unfurl_links</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
    <span class="na">unfurl_media</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
    <span class="na">text</span><span class="p">:</span> <span class="nx">text</span><span class="p">,</span>
    <span class="na">blocks</span><span class="p">:</span> <span class="nx">blocks</span>
  <span class="p">};</span>

  <span class="k">try</span> <span class="p">{</span>
    <span class="kd">const</span> <span class="nx">response</span> <span class="o">=</span> <span class="nf">slackRequest</span><span class="p">(</span><span class="dl">"</span><span class="s2">chat.postMessage</span><span class="dl">"</span><span class="p">,</span> <span class="nx">content</span><span class="p">);</span>
    <span class="k">return</span> <span class="nx">response</span><span class="p">;</span>
  <span class="p">}</span> <span class="k">catch </span><span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">throw</span> <span class="k">new</span> <span class="nc">Error</span><span class="p">(</span><span class="s2">`Failed to send Slack message: </span><span class="p">${</span><span class="nx">error</span><span class="p">}</span><span class="s2">`</span><span class="p">);</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="kd">function</span> <span class="nf">slackRequest</span><span class="p">(</span><span class="nx">path</span><span class="p">,</span> <span class="nx">content</span><span class="p">)</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">options</span> <span class="o">=</span> <span class="p">{</span>
    <span class="na">method</span><span class="p">:</span> <span class="dl">"</span><span class="s2">post</span><span class="dl">"</span><span class="p">,</span>
    <span class="na">contentType</span><span class="p">:</span> <span class="dl">"</span><span class="s2">application/json</span><span class="dl">"</span><span class="p">,</span>
    <span class="na">headers</span><span class="p">:</span> <span class="p">{</span>
      <span class="na">Authorization</span><span class="p">:</span> <span class="s2">`Bearer </span><span class="p">${</span><span class="nx">slackBotToken</span><span class="p">}</span><span class="s2">`</span><span class="p">,</span>
      <span class="dl">'</span><span class="s1">X-Slack-No-Retry</span><span class="dl">'</span><span class="p">:</span> <span class="mi">1</span>
    <span class="p">},</span>
    <span class="na">payload</span><span class="p">:</span> <span class="nx">JSON</span><span class="p">.</span><span class="nf">stringify</span><span class="p">(</span><span class="nx">content</span><span class="p">)</span>
  <span class="p">};</span>

  <span class="k">try</span> <span class="p">{</span>
    <span class="kd">const</span> <span class="nx">response</span> <span class="o">=</span> <span class="nx">UrlFetchApp</span><span class="p">.</span><span class="nf">fetch</span><span class="p">(</span><span class="dl">"</span><span class="s2">https://slack.com/api/</span><span class="dl">"</span><span class="o">+</span><span class="nx">path</span><span class="p">,</span> <span class="nx">options</span><span class="p">);</span>
    <span class="kd">const</span> <span class="nx">responseData</span> <span class="o">=</span> <span class="nx">JSON</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="nx">response</span><span class="p">.</span><span class="nf">getContentText</span><span class="p">());</span>
    <span class="k">if </span><span class="p">(</span><span class="nx">responseData</span><span class="p">.</span><span class="nx">ok</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">return</span> <span class="nx">responseData</span>
    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
      <span class="k">throw</span> <span class="k">new</span> <span class="nc">Error</span><span class="p">(</span><span class="s2">`Slack: </span><span class="p">${</span><span class="nx">responseData</span><span class="p">.</span><span class="nx">error</span><span class="p">}</span><span class="s2">`</span><span class="p">);</span>
    <span class="p">}</span>
  <span class="p">}</span> <span class="k">catch </span><span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">throw</span> <span class="nx">error</span><span class="p">;</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<h4 id="更多-google-apps-script-案例">更多 Google Apps Script 案例：</h4>
<ul>
  <li><a href="/posts/zrealm-robotic-process-automation/google-apps-script-快速串接-firebase-app-distribution-api-教學與進階設定-71400d408dc8/">Google Apps Script x Google APIs 快速串接整合方式</a></li>
  <li><a href="/posts/zrealm-robotic-process-automation/ga4-自動數據通知機器人-3-步驟用-google-apps-script-串接-telegram-bot-1e85b8df2348/">簡單 3 步驟 — 打造免費 GA4 自動數據通知機器人</a></li>
  <li><a href="/posts/zrealm-robotic-process-automation/google-apps-script-打造每日數據報表自動化-rpa-快速串接-google-analytics-與-google-sheet-f6713ba3fee3/">使用 Google Apps Script 實現每日數據報表 RPA 自動化</a></li>
  <li><a href="/posts/zrealm-robotic-process-automation/slack-app-整合-chatgpt-利用-openai-api-與-google-cloud-functions-自建智能助理-bd94cc88f9c9/">Slack &amp; ChatGPT Integration</a></li>
  <li><a href="/posts/zrealm-robotic-process-automation/google-apps-script-三步驟打造免費-github-repo-star-通知器-實時獲取-star-動態-382218e15697/">使用 Google Apps Script 三步驟免費建立 Github Repo Star Notifier</a></li>
  <li><a href="/posts/zrealm-robotic-process-automation/crashlytics與google-analytics自動查詢app-crash-free-users-rate-google-apps-script整合實作-793cb8f89b72/">Crashlytics + Google Analytics 自動查詢 App Crash-Free Users Rate</a></li>
  <li><a href="/posts/zrealm-robotic-process-automation/crashlytics-big-query-串接教學-自動查詢並轉發-ios-app-閃退報告到-slack-e77b80cc6f89/">Crashlytics + Big Query 打造更即時便利的 Crash 追蹤工具</a></li>
</ul>

<h3 id="總結">總結</h3>

<p>感謝您的耐心閱讀與參與，CI/CD 從 0 到 1 系列文章到此告一段落；希望能實際幫助到您與您的團隊建置完善的 CI/CD 工作流程，提升效率與產品穩定性；有任何實作問題歡迎留言討論，這四篇文章大約花了 14+ 天撰寫， <strong>如果您覺得不錯歡迎 Follow 我的 Medium 和與朋友同事分享</strong> 。</p>

<blockquote>
  <p>謝謝。</p>
</blockquote>

<h4 id="-buy-me-a-beer-on-paypal"><a href="https://www.paypal.com/ncp/payment/CMALMPT8UUTY2" target="_blank">🍺 Buy me a beer on PayPal</a></h4>

<blockquote>
  <p><a href="https://www.paypal.com/ncp/payment/CMALMPT8UUTY2" target="_blank"><strong><em>本系列文章花費了大量的時間精力撰寫，如果內容對您有幫助、對您的團隊有實質提升工作效率與產品品質；歡迎請我喝杯咖啡，感謝支持！</em></strong></a></p>
</blockquote>

<p><img src="/assets/4273e57e7148/1*QJj54G9gOjtQS-rbHVT1SQ.webp" alt="Buy me a coffee" loading="lazy" decoding="async" width="700" height="700" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3MDAiIGhlaWdodD0iNzAwIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><a href="https://www.paypal.com/ncp/payment/CMALMPT8UUTY2" target="_blank">🍺 Buy me a beer on PayPal</a></p>
<h4 id="系列文章">系列文章：</h4>
<ul>
  <li><a href="/posts/zrealm-dev/ci-cd-是什麼-打造高效穩定開發團隊的關鍵流程與工具選擇-c008a9e8ceca/"><strong>CI/CD 實戰指南（一）：CI/CD 是什麼？如何透過 CI/CD 打造穩定高效的開發團隊？工具選擇？</strong></a></li>
  <li><a href="/posts/zrealm-dev/github-actions-self-hosted-runner-詳解與實戰案例-ci-cd-自動化工作流程建置-404bd5c70040/"><strong>CI/CD 實戰指南（二）：GitHub Actions 與 self-hosted Runner 使用與建置大全</strong></a></li>
  <li><a href="/posts/zrealm-dev/github-actions-ios-ci-cd-完整實作自動化建置-測試與部署流程教學-4b001d2e8440/"><strong>CI/CD 實戰指南（三）：使用 GitHub Actions 實作 App 專案的 CI 與 CD 工作流程</strong></a></li>
  <li><a href="/posts/zrealm-dev/ci-cd-打包工具平台-使用-google-apps-script-web-app-串接-github-actions-提升跨團隊效率-4273e57e7148/"><strong>CI/CD 實戰指南（四）：使用 Google Apps Script Web App 串接 GitHub Actions 建置免費易用的打包工具平台</strong></a></li>
</ul>]]></content>
  </entry><entry>
    <title type="html">GitHub Actions iOS CI/CD｜完整實作自動化建置、測試與部署流程教學</title>
    <link href="https://zhgchg.li/posts/zrealm-dev/github-actions-ios-ci-cd-%E5%AE%8C%E6%95%B4%E5%AF%A6%E4%BD%9C%E8%87%AA%E5%8B%95%E5%8C%96%E5%BB%BA%E7%BD%AE-%E6%B8%AC%E8%A9%A6%E8%88%87%E9%83%A8%E7%BD%B2%E6%B5%81%E7%A8%8B%E6%95%99%E5%AD%B8-4b001d2e8440/" rel="alternate" type="text/html" title="GitHub Actions iOS CI/CD｜完整實作自動化建置、測試與部署流程教學" />
    <published>2025-07-07T23:02:24+08:00</published>
    <updated>2026-01-04T11:38:01+08:00</updated>
    <id>https://zhgchg.li/posts/zrealm-dev/4b001d2e8440</id><summary type="html">針對iOS開發者打造的GitHub Actions CI/CD流程，解決手動建置與測試繁瑣問題，結合Fastlane與Firebase實現自動化打包與部署，提升團隊開發效率與產品品質。</summary><author>
      <name>ZhgChgLi</name>
    </author><category term="ZRealm Dev." /><category term="ios-app-development" /><category term="cicd" /><category term="github-actions" /><category term="firebase" /><category term="cicd-pipeline" /><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://zhgchg.li/assets/4b001d2e8440/1*0LK6m6CTImL6rcsrliiOQA.webp" /><content type="html" xml:base="https://zhgchg.li/posts/zrealm-dev/github-actions-ios-ci-cd-%E5%AE%8C%E6%95%B4%E5%AF%A6%E4%BD%9C%E8%87%AA%E5%8B%95%E5%8C%96%E5%BB%BA%E7%BD%AE-%E6%B8%AC%E8%A9%A6%E8%88%87%E9%83%A8%E7%BD%B2%E6%B5%81%E7%A8%8B%E6%95%99%E5%AD%B8-4b001d2e8440/"><![CDATA[<h3 id="cicd-實戰指南三使用-github-actions-實作-app-ios-ci-與-cd-工作流程">CI/CD 實戰指南（三）：使用 GitHub Actions 實作 App iOS CI 與 CD 工作流程</h3>

<p>iOS App 自動化建置、測試、部署的 GitHub Actions 實作步驟完整教學</p>

<p><img src="/assets/4b001d2e8440/1*0LK6m6CTImL6rcsrliiOQA.webp" alt="Photo by Robs" loading="lazy" decoding="async" width="1200" height="801" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjgwMSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>Photo by <a href="https://unsplash.com/@robinne?utm_content=creditCopyText&amp;utm_medium=referral&amp;utm_source=unsplash" target="_blank">Robs</a></p>
<h4 id="前言">前言</h4>

<p>前篇「 <a href="/posts/zrealm-dev/github-actions-self-hosted-runner-詳解與實戰案例-ci-cd-自動化工作流程建置-404bd5c70040/"><strong>CI/CD 實戰指南（二）：GitHub Actions 與 Self-hosted Runner 使用與建置大全</strong></a> 」我們介紹了 GitHub Actions 的基礎知識與運作流程還有如何使用自己的機器當成 Runner、帶大家實現了三個簡單的自動化 Actions； <strong>本篇將深入著重在現實使用 GitHub Actions 建置 App (iOS) CI/CD 工作流程上</strong> ，一樣手把手帶大家一步一步完成並一邊補足 GitHub Actions 相關知識。</p>
<h3 id="app-cicd-流程關係圖">App CI/CD 流程關係圖</h3>

<p><img src="/assets/4b001d2e8440/1*VRygfRAkBRNEDAC4RyGzRA.webp" alt="" loading="lazy" decoding="async" width="1400" height="669" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjY2OSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>本篇將關注在 GitHub Actions 建置 CI/CD 的區塊，下一篇「 <a href="/posts/zrealm-dev/ci-cd-打包工具平台-使用-google-apps-script-web-app-串接-github-actions-提升跨團隊效率-4273e57e7148/">CI/CD 實戰指南（四）：使用 Google Apps Script Web App 串接 GitHub Actions 建置免費易用的打包工具平台</a> 」才會介紹右半部分的使用 Google Apps Script Web App 建置跨團隊協作打包平台。</p>
<h4 id="運作流程">運作流程：</h4>
<ol>
  <li>GitHub Actions 開 Pull Request 觸發 or 表單觸發 or 定時觸發</li>
  <li>執行對應 Workflow Jobs/Steps</li>
  <li>Step 執行對應 Fastlane (iOS) or (Android Gradle) 腳本</li>
  <li>Fastlane 執行對應 xcodebuild (iOS) 指令</li>
  <li>取得執行結果</li>
  <li>後續 Workflow Jobs/Steps 處理結果</li>
  <li>完成</li>
</ol>

<h4 id="github-actions-成果圖">GitHub Actions 成果圖</h4>

<p>先上最終成果給大家一點實作動力！</p>

<p><img src="/assets/4b001d2e8440/1*5gnQYdVAOtGR-bMK4ZrOhA.webp" alt="CI Testing" loading="lazy" decoding="async" width="911" height="566" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MTEiIGhlaWdodD0iNTY2Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><a href="https://github.com/ZhgChgLi/github-actions-ci-cd-demo/pull/11" target="_blank">CI Testing</a></p>

<p><img src="/assets/4b001d2e8440/1*u6A77KwkXS2SY5-DPPPR9A.webp" alt="" loading="lazy" decoding="async" width="1200" height="802" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjgwMiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/4b001d2e8440/1*t9PrQfcTANyvG7gfXXC-bw.webp" alt="CI Nightly Build, CD Deploy" loading="lazy" decoding="async" width="554" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1NTQiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><a href="https://github.com/ZhgChgLi/github-actions-ci-cd-demo/actions/runs/16119750747" target="_blank">CI Nightly Build, CD Deploy</a></p>
<h3 id="github-actions-x-self-hosted-runner-基礎知識">GitHub Actions x Self-hosted Runner 基礎知識</h3>

<p>如果你對 GitHub Actions 與架設 Self-hosted Runner 還不熟悉強烈建議先看過上篇「 <a href="/posts/zrealm-dev/github-actions-self-hosted-runner-詳解與實戰案例-ci-cd-自動化工作流程建置-404bd5c70040/"><strong>CI/CD 實戰指南（二）：GitHub Actions 與 Self-hosted Runner 使用與建置大全</strong></a> 」或是搭配上篇知識一起實作。</p>

<blockquote>
  <p><strong><em>實作開始！</em></strong></p>
</blockquote>

<h3 id="ios-demo-專案的-infra-架構">iOS Demo 專案的 Infra 架構</h3>

<p><a href="https://github.com/ZhgChgLi/github-actions-ci-cd-demo" target="_blank"><img src="https://opengraph.githubassets.com/eb233d74ad1bc6b0eceb9494487579557fb7f17066a3981d20b52fde2c00ef45/ZhgChgLi/github-actions-ci-cd-demo" alt="" /></a></p>

<p><strong>本文用到的 iOS 專案內容包含測試項目都是用 AI 產生的</strong> ，不需在意 iOS 的程式細節，只針對 Infra &amp; CI/CD 部分做討論。</p>

<blockquote>
  <p><strong><em>以下工具只是憑以往經驗，如果是新專案可以考慮使用更新的 <a href="https://github.com/jdx/mise" target="_blank">mise</a> 和 <a href="https://github.com/tuist/tuist" target="_blank">tuist</a> 。</em></strong></p>
</blockquote>

<h4 id="mint">Mint</h4>

<p><a href="https://github.com/yonaskolb/Mint" target="_blank"><img src="https://opengraph.githubassets.com/21a1c9531478638f34289d108bd478e5d7afec08dce1aa16123feb0e226a47a2/yonaskolb/Mint" alt="" /></a></p>

<p>Mint 工具可以幫我們統一管理依賴工具的版本(Gemfile 只能管理 Ruby Gems)，例如 XCodeGen、XCodeGen、SwiftFormat、SwiftLint、Periphery</p>

<p>…etc</p>

<p><strong>Mintfile:</strong></p>
<div class="language-graphql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">yonaskolb/Mint@0.17.5</span><span class="w">
</span><span class="err">yonaskolb/XcodeGen@2.35.0</span><span class="w">
</span><span class="err">nicklockwood/SwiftFormat@0.51.13</span><span class="w">
</span></code></pre></div></div>

<p>這邊我們只用到三個。</p>

<blockquote>
  <p><strong><em>覺得太複雜不使用也可以，直接在 Action Workflow Step 中用 brew install 安裝需要的工具即可。</em></strong></p>
</blockquote>

<h4 id="bundle">Bundle</h4>

<p><strong>Gemfile:</strong></p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">source</span> <span class="s1">'https://rubygems.org'</span>
gem <span class="s1">'cocoapods'</span>, <span class="s1">'~&gt;1.16.0'</span>
gem <span class="s1">'fastlane'</span>, <span class="s1">'~&gt;2.228.0'</span>

plugins_path <span class="o">=</span> File.join<span class="o">(</span>File.dirname<span class="o">(</span>__FILE__<span class="o">)</span>, <span class="s1">'Product'</span>, <span class="s1">'fastlane'</span>, <span class="s1">'Pluginfile'</span><span class="o">)</span>
eval_gemfile<span class="o">(</span>plugins_path<span class="o">)</span> <span class="k">if </span>File.exist?<span class="o">(</span>plugins_path<span class="o">)</span>
</code></pre></div></div>

<p>管理 Ruby (Gems) 相關依賴，一般 iOS 專案最常用的就是這兩個 <code class="language-plaintext highlighter-rouge">cocoapods</code> 跟 <code class="language-plaintext highlighter-rouge">fastlane</code> 。</p>
<h4 id="cocoapods">Cocoapods</h4>

<p><strong>Product/podfile:</strong></p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">platform</span> <span class="ss">:ios</span><span class="p">,</span> <span class="s1">'13.0'</span>
<span class="n">use_frameworks!</span>

<span class="n">target</span> <span class="s1">'app-ci-cd-github-actions-demo'</span> <span class="k">do</span>
  <span class="n">pod</span> <span class="s1">'SnapKit'</span>
<span class="k">end</span> 
</code></pre></div></div>

<p>雖然已宣告 <a href="https://blog.cocoapods.org/CocoaPods-Specs-Repo/" target="_blank">即將停止維護</a> ，但 Cocoapods 在有年代的 iOS 專案中仍很常見，這邊簡單加一個 Snapkit 當 Demo。</p>
<h4 id="xcodegen">XCodeGen</h4>

<p>避免多人開發中 <code class="language-plaintext highlighter-rouge">.xcodeproj</code> / <code class="language-plaintext highlighter-rouge">.xcworkspace</code> 異動造成的衝突，統一使用 Project.yaml 定義 XCode Project 內容，然後在本地自己 Gen Project 檔案(不上 Git)。</p>

<p><strong>Product/project.yaml:</strong></p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">name</span><span class="pi">:</span> <span class="s">app-ci-cd-github-actions-demo</span>
<span class="na">options</span><span class="pi">:</span>
  <span class="na">bundleIdPrefix</span><span class="pi">:</span> <span class="s">com.example</span>
  <span class="na">deploymentTarget</span><span class="pi">:</span>
    <span class="na">iOS</span><span class="pi">:</span> <span class="s1">'</span><span class="s">13.0'</span>
  <span class="na">usesTabs</span><span class="pi">:</span> <span class="kc">false</span>
  <span class="na">indentWidth</span><span class="pi">:</span> <span class="m">2</span>
  <span class="na">tabWidth</span><span class="pi">:</span> <span class="m">2</span>

<span class="na">configs</span><span class="pi">:</span>
  <span class="na">Debug</span><span class="pi">:</span> <span class="s">debug</span>
  <span class="na">Release</span><span class="pi">:</span> <span class="s">release</span>

<span class="na">targets</span><span class="pi">:</span>
  <span class="na">app-ci-cd-github-actions-demo</span><span class="pi">:</span>
    <span class="na">type</span><span class="pi">:</span> <span class="s">application</span>
    <span class="na">platform</span><span class="pi">:</span> <span class="s">iOS</span>
    <span class="na">sources</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s">app-ci-cd-github-actions-demo</span>
    <span class="na">resources</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s">app-ci-cd-github-actions-demo/Assets.xcassets</span>
      <span class="pi">-</span> <span class="s">app-ci-cd-github-actions-demo/Base.lproj</span>
    <span class="na">info</span><span class="pi">:</span>
      <span class="na">path</span><span class="pi">:</span> <span class="s">app-ci-cd-github-actions-demo/Info.plist</span>
      <span class="na">properties</span><span class="pi">:</span>
        <span class="na">CFBundleIdentifier</span><span class="pi">:</span> <span class="s">$(PRODUCT_BUNDLE_IDENTIFIER)</span>
    <span class="na">settings</span><span class="pi">:</span>
      <span class="na">base</span><span class="pi">:</span>
        <span class="na">PRODUCT_BUNDLE_IDENTIFIER</span><span class="pi">:</span> <span class="s">com.test.appcicdgithubactionsdemo</span>
    <span class="na">cocoapods</span><span class="pi">:</span> <span class="kc">true</span>

  <span class="na">app-ci-cd-github-actions-demoTests</span><span class="pi">:</span>
    <span class="na">type</span><span class="pi">:</span> <span class="s">bundle.unit-test</span>
    <span class="na">platform</span><span class="pi">:</span> <span class="s">iOS</span>
    <span class="na">sources</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s">app-ci-cd-github-actions-demoTests</span>
    <span class="na">dependencies</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">target</span><span class="pi">:</span> <span class="s">app-ci-cd-github-actions-demo</span>
    <span class="na">info</span><span class="pi">:</span>
      <span class="na">path</span><span class="pi">:</span> <span class="s">app-ci-cd-github-actions-demoTests/Info.plist</span>
    <span class="na">settings</span><span class="pi">:</span>
      <span class="na">base</span><span class="pi">:</span>
        <span class="na">PRODUCT_BUNDLE_IDENTIFIER</span><span class="pi">:</span> <span class="s">com.test.appcicdgithubactionsdemo.tests</span>

  <span class="na">app-ci-cd-github-actions-demoUITests</span><span class="pi">:</span>
    <span class="na">type</span><span class="pi">:</span> <span class="s">bundle.ui-testing</span>
    <span class="na">platform</span><span class="pi">:</span> <span class="s">iOS</span>
    <span class="na">sources</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s">app-ci-cd-github-actions-demoUITests</span>
    <span class="na">dependencies</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">target</span><span class="pi">:</span> <span class="s">app-ci-cd-github-actions-demo</span>
    <span class="na">info</span><span class="pi">:</span>
      <span class="na">path</span><span class="pi">:</span> <span class="s">app-ci-cd-github-actions-demoUITests/Info.plist</span>
    <span class="na">settings</span><span class="pi">:</span>
      <span class="na">base</span><span class="pi">:</span>
        <span class="na">PRODUCT_BUNDLE_IDENTIFIER</span><span class="pi">:</span> <span class="s">com.test.appcicdgithubactionsdemo.uitests</span>

  <span class="na">app-ci-cd-github-actions-demoSnapshotTests</span><span class="pi">:</span>
    <span class="na">type</span><span class="pi">:</span> <span class="s">bundle.unit-test</span>
    <span class="na">platform</span><span class="pi">:</span> <span class="s">iOS</span>
    <span class="na">sources</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">path</span><span class="pi">:</span> <span class="s">app-ci-cd-github-actions-demoSnapshotTests</span>
        <span class="na">excludes</span><span class="pi">:</span>
          <span class="pi">-</span> <span class="s2">"</span><span class="s">**/__Snapshots__/**"</span>
    <span class="na">dependencies</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">target</span><span class="pi">:</span> <span class="s">app-ci-cd-github-actions-demo</span>
      <span class="pi">-</span> <span class="na">product</span><span class="pi">:</span> <span class="s">SnapshotTesting</span>
        <span class="na">package</span><span class="pi">:</span> <span class="s">SnapshotTesting</span>
    <span class="na">info</span><span class="pi">:</span>
      <span class="na">path</span><span class="pi">:</span> <span class="s">app-ci-cd-github-actions-demoSnapshotTests/Info.plist</span>
      <span class="na">settings</span><span class="pi">:</span>
        <span class="na">base</span><span class="pi">:</span>
          <span class="na">PRODUCT_BUNDLE_IDENTIFIER</span><span class="pi">:</span> <span class="s">com.test.appcicdgithubactionsdemo.snapshottests</span>

<span class="na">packages</span><span class="pi">:</span>
  <span class="na">SnapshotTesting</span><span class="pi">:</span>
    <span class="na">url</span><span class="pi">:</span> <span class="s">https://github.com/pointfreeco/swift-snapshot-testing</span>
    <span class="na">from</span><span class="pi">:</span> <span class="s">1.18.4</span>
</code></pre></div></div>

<p>SnapshotTesting: 使用 Swift Package Manager 管理。</p>
<h4 id="fastlane">Fastlane</h4>

<p>封裝 xcodebuild 指令、封裝串接 App Store Connect API、Firebase API..等服務的複雜步驟。</p>

<p><strong>Product/fastlane/Fastfile:</strong></p>
<div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="nf">default_platform</span><span class="p">(</span><span class="o">:</span><span class="n">ios</span><span class="p">)</span>

<span class="n">platform</span> <span class="o">:</span><span class="n">ios</span> <span class="k">do</span>
  <span class="n">desc</span> <span class="s2">"Run all tests (Unit Tests + UI Tests)"</span>
  <span class="n">lane</span> <span class="o">:</span><span class="n">run_all_tests</span> <span class="k">do</span> <span class="o">|</span><span class="n">options</span><span class="o">|</span>
    <span class="n">device</span> <span class="o">=</span> <span class="n">options</span><span class="p">[</span><span class="o">:</span><span class="n">device</span><span class="p">]</span>
    <span class="nf">scan</span><span class="p">(</span>
      <span class="n">scheme</span><span class="o">:</span> <span class="s2">"app-ci-cd-github-actions-demo"</span><span class="p">,</span>
      <span class="n">device</span><span class="o">:</span> <span class="n">device</span><span class="p">,</span>
      <span class="n">clean</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
      <span class="n">output_directory</span><span class="o">:</span> <span class="s2">"fastlane/test_output"</span><span class="p">,</span>
      <span class="n">output_types</span><span class="o">:</span> <span class="s2">"junit"</span>
    <span class="p">)</span>
  <span class="n">end</span>

  <span class="n">desc</span> <span class="s2">"Run only Unit Tests"</span>
  <span class="n">lane</span> <span class="o">:</span><span class="n">run_unit_tests</span> <span class="k">do</span> <span class="o">|</span><span class="n">options</span><span class="o">|</span>
    <span class="n">device</span> <span class="o">=</span> <span class="n">options</span><span class="p">[</span><span class="o">:</span><span class="n">device</span><span class="p">]</span>
    <span class="nf">scan</span><span class="p">(</span>
      <span class="n">scheme</span><span class="o">:</span> <span class="s2">"app-ci-cd-github-actions-demo"</span><span class="p">,</span>
      <span class="n">device</span><span class="o">:</span> <span class="n">device</span><span class="p">,</span>
      <span class="n">clean</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
      <span class="n">only_testing</span><span class="o">:</span> <span class="p">[</span>
        <span class="s2">"app-ci-cd-github-actions-demoTests"</span>
      <span class="p">],</span>
      <span class="n">output_directory</span><span class="o">:</span> <span class="s2">"fastlane/test_output"</span><span class="p">,</span>
      <span class="n">output_types</span><span class="o">:</span> <span class="s2">"junit"</span>
    <span class="p">)</span>
  <span class="n">end</span>

  <span class="n">desc</span> <span class="s2">"Build and upload to Firebase App Distribution"</span>
  <span class="n">lane</span> <span class="o">:</span><span class="n">beta</span> <span class="k">do</span> <span class="o">|</span><span class="n">options</span><span class="o">|</span>
    
    <span class="k">if</span> <span class="n">options</span><span class="p">[</span><span class="o">:</span><span class="n">version_number</span><span class="p">]</span> <span class="o">&amp;&amp;</span> <span class="n">options</span><span class="p">[</span><span class="o">:</span><span class="n">version_number</span><span class="p">]</span><span class="mf">.</span><span class="n">to_s</span><span class="mf">.</span><span class="n">strip</span> <span class="o">!=</span> <span class="s2">""</span>
      <span class="nf">increment_version_number</span><span class="p">(</span><span class="n">version_number</span><span class="o">:</span> <span class="n">options</span><span class="p">[</span><span class="o">:</span><span class="n">version_number</span><span class="p">])</span>
    <span class="n">end</span>

    <span class="k">if</span> <span class="n">options</span><span class="p">[</span><span class="o">:</span><span class="n">build_number</span><span class="p">]</span> <span class="o">&amp;&amp;</span> <span class="n">options</span><span class="p">[</span><span class="o">:</span><span class="n">build_number</span><span class="p">]</span><span class="mf">.</span><span class="n">to_s</span><span class="mf">.</span><span class="n">strip</span> <span class="o">!=</span> <span class="s2">""</span>
      <span class="nf">increment_build_number</span><span class="p">(</span><span class="n">build_number</span><span class="o">:</span> <span class="n">options</span><span class="p">[</span><span class="o">:</span><span class="n">build_number</span><span class="p">])</span>
    <span class="n">end</span>

    <span class="nf">update_code_signing_settings</span><span class="p">(</span>
      <span class="n">use_automatic_signing</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span>
      <span class="n">path</span><span class="o">:</span> <span class="s2">"app-ci-cd-github-actions-demo.xcodeproj"</span><span class="p">,</span>
      <span class="n">team_id</span><span class="o">:</span> <span class="no">ENV</span><span class="p">[</span><span class="s1">'TEAM_ID'</span><span class="p">],</span>
      <span class="n">code_sign_identity</span><span class="o">:</span> <span class="s2">"iPhone Developer"</span><span class="p">,</span>
      <span class="n">sdk</span><span class="o">:</span> <span class="s2">"iphoneos*"</span><span class="p">,</span>
      <span class="n">profile_name</span><span class="o">:</span> <span class="s2">"cicd"</span>
    <span class="p">)</span>

    <span class="nf">gym</span><span class="p">(</span>
      <span class="n">scheme</span><span class="o">:</span> <span class="s2">"app-ci-cd-github-actions-demo"</span><span class="p">,</span>
      <span class="n">clean</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
      <span class="n">export_method</span><span class="o">:</span> <span class="s2">"development"</span><span class="p">,</span>
      <span class="n">output_directory</span><span class="o">:</span> <span class="s2">"fastlane/build"</span><span class="p">,</span>
      <span class="n">output_name</span><span class="o">:</span> <span class="s2">"app-ci-cd-github-actions-demo.ipa"</span><span class="p">,</span>
      <span class="n">export_options</span><span class="o">:</span> <span class="p">{</span>
          <span class="n">provisioningProfiles</span><span class="o">:</span> <span class="p">{</span>
            <span class="s2">"com.test.appcicdgithubactionsdemo"</span> <span class="o">=&gt;</span> <span class="s2">"cicd"</span><span class="p">,</span>
          <span class="p">},</span>
      <span class="p">}</span>
    <span class="p">)</span>

    <span class="nf">firebase_app_distribution</span><span class="p">(</span>
      <span class="n">app</span><span class="o">:</span> <span class="s2">"1:127683058219:ios:98896929fa131c7a80686e"</span><span class="p">,</span>
      <span class="n">firebase_cli_token</span><span class="o">:</span> <span class="no">ENV</span><span class="p">[</span><span class="s2">"FIREBASE_CLI_TOKEN"</span><span class="p">],</span>
      <span class="n">release_notes</span><span class="o">:</span> <span class="n">options</span><span class="p">[</span><span class="o">:</span><span class="n">release_notes</span><span class="p">]</span> <span class="o">||</span> <span class="s2">"New beta build"</span>
    <span class="p">)</span>
  <span class="n">end</span>
<span class="n">end</span>
</code></pre></div></div>

<p>註: provisioningProfiles、profile_name 對應的是 <a href="https://developer.apple.com/account/resources/certificates/list" target="_blank">App Developer 中的 Profiles 憑證名稱</a> 。(如果有用 match 則也不需要這些指定。)</p>

<p><img src="/assets/4b001d2e8440/1*WXqqnErto3nn8rnNg6TXgw.webp" alt="" loading="lazy" decoding="async" width="1117" height="616" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMTE3IiBoZWlnaHQ9IjYxNiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><strong>Fastlane 是 iOS CI/CD 當中不可或缺的一部分</strong> ，直接使用它封裝好的方法就能快速開發 CI/CD 實際執行的步驟；我們只需關注在整體的腳本設計，而不需去處理複雜的 API 串接或指令撰寫。</p>

<p>例如：Fastlane 只需要寫「scan(xxx)」就能執行測試，如果要寫成 xcodebuild 則需要「 <code class="language-plaintext highlighter-rouge">xcodebuild -workspace ./xxx.xcworkspace -scheme xxx -derivedDataPath xxx ‘platform=iOS Simulator,id=xxx’ clean build test</code> 」，打包部署要自己做更是麻煩，要自行串接 App Store Connect/Firebase API，光金鑰驗證都要寫超過 10 行程式了。</p>

<p><strong>Demo 專案我們只有三個 Lane:</strong></p>
<ul>
  <li>run_all_tests: 跑所有類型的測試 (Snapshot+Unit)</li>
  <li>run_unit_tests: 只跑單元測試 (Unit)</li>
  <li>beta: 打包部署到 Firebase App Distribution</li>
</ul>

<h4 id="fastlane--match">Fastlane — Match</h4>

<p>因 Demo 專案限制，這邊沒用到 Match 管理團隊開發、部署憑證，但這邊還是要提一下，建議使用 Match 去管理團隊的所有開發、部署憑證，方便控管跟統一更新。</p>

<blockquote>
  <p><em>有用 Match 就能在專案 Setup 步驟直接使用 <code class="language-plaintext highlighter-rouge">match all</code> 之類的指令一鍵安裝好所有開發需要的憑證。</em></p>
</blockquote>

<ul>
  <li><strong>Fastlane Match 會使用另一個 Private Repo 管理憑證金鑰，在 GitHub Actions 中需要設定好 SSH Agent 才能 Clone 另一個 Private Repo。</strong> 
(請參考文末補充)</li>
</ul>

<p><strong>[2026/02 Update] 延伸閱讀 — 補充 iOS 憑證、Fastlane Match、CI/CD 使用細節:</strong></p>
<ul>
  <li>「 <a href="/posts/zrealm-dev/ios-certificates-identifiers與profiles詳解-fastlane-match統一管理憑證與ci-cd整合實戰-823ac523ccc8/">iOS Certificates, Identifiers &amp; Profiles 是什麼及 Fastlane Match 統一管理憑證與 CI/CD 的一些筆記</a> 」</li>
</ul>

<p>— — —</p>
<h4 id="makefile">Makefile</h4>

<p><img src="/assets/4b001d2e8440/1*jYACUYFP3MkeHCgeUY7j3w.webp" alt="Makefile" loading="lazy" decoding="async" width="1200" height="423" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjQyMyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>Makefile</p>

<p>讓開發端與 CI/CD 統一都使用 Makefile 執行指令，方便我們封裝同樣的環境、路徑與操作行爲。</p>

<blockquote>
  <p><em>經典的案例是有的人使用的是本機安裝的 <code class="language-plaintext highlighter-rouge">pod install</code> 有的人則使用的是 Bundel 管理的 <code class="language-plaintext highlighter-rouge">bundle exec pod install</code> 如果版本不同就可能產生差異。</em></p>
</blockquote>

<blockquote>
  <p><strong><em>覺得太複雜不使用也可以，那就是在 Action Workflow Step 中直接寫要執行的指令即可。</em></strong></p>
</blockquote>

<p><strong>Makefile:</strong></p>
<div class="language-makefile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!make
</span><span class="nv">PRODUCT_FOLDER</span> <span class="o">=</span> ./Product/
<span class="nv">SHELL</span>         <span class="o">:=</span> /bin/zsh
<span class="nv">.DEFAULT_GOAL</span> <span class="o">:=</span> <span class="nb">install</span>
<span class="nv">MINT_DIRECTORY</span> <span class="o">:=</span> ./mint/

<span class="k">export </span><span class="nv">MINT_PATH</span><span class="o">=</span><span class="p">$(</span>MINT_DIRECTORY<span class="p">)</span>

<span class="c">## 👇 Help function
</span><span class="nl">.PHONY</span><span class="o">:</span> <span class="nf">help</span>
<span class="nl">help</span><span class="o">:</span>
 <span class="err">@echo</span> <span class="s2">""</span>
 <span class="nl">@echo "📖 可用指令</span><span class="o">:</span><span class="nf">"</span>
 <span class="nl">@grep -E '^[a-zA-Z_-]+</span><span class="o">:</span><span class="nf">.*?</span><span class="c">##</span><span class="nf"> .*$$' $(MAKEFILE_LIST) | </span>\
<span class="nf">  awk 'BEGIN {FS = ":.*?</span><span class="c">##</span><span class="nf"> "}; {printf "  </span>\0<span class="nf">33[36m%-20s</span>\0<span class="nf">33[0m %s</span>\n<span class="nf">"</span><span class="p">,</span><span class="nf"> $$1</span><span class="p">,</span><span class="nf"> $$2}'</span>
 <span class="err">@echo</span> <span class="s2">""</span>

<span class="c">## Setup
</span><span class="nl">.PHONY</span><span class="o">:</span> <span class="nf">setup</span>
<span class="nl">setup</span><span class="o">:</span> <span class="nf">check-mint </span><span class="c">##</span><span class="nf"> 安裝 Ruby 和 Mint 依賴</span>
 <span class="err">@echo</span> <span class="s2">"🔨 Installing Ruby dependencies..."</span>
 <span class="err">bundle</span> <span class="err">config</span> <span class="err">set</span> <span class="err">path</span> <span class="s1">'vendor/bundle'</span>
 <span class="err">bundle</span> <span class="err">install</span>
 <span class="err">@echo</span> <span class="s2">"🔨 Installing Mint dependencies..."</span>
 <span class="err">mint</span> <span class="err">bootstrap</span>

<span class="c">## Install
</span><span class="nl">.PHONY</span><span class="o">:</span> <span class="nf">install</span>
<span class="nl">install</span><span class="o">:</span> <span class="nf">XcodeGen PodInstall </span><span class="c">##</span><span class="nf"> 執行 XcodeGen 和 CocoaPods 安裝</span>

<span class="nl">.PHONY</span><span class="o">:</span> <span class="nf">XcodeGen</span>
<span class="nl">XcodeGen</span><span class="o">:</span> <span class="nf">check-mint </span><span class="c">##</span><span class="nf"> 用 XcodeGen 產生 .xcodeproj</span>
 <span class="err">@echo</span> <span class="s2">"🔨 Execute XcodeGen"</span>
 <span class="err">cd</span> <span class="err">$(PRODUCT_FOLDER)</span> <span class="err">&amp;&amp;</span> <span class="err">\</span>
 <span class="err">mint</span> <span class="err">run</span> <span class="err">yonaskolb/XcodeGen</span> <span class="err">--quiet</span>

<span class="nl">.PHONY</span><span class="o">:</span> <span class="nf">PodInstall</span>
<span class="nl">PodInstall</span><span class="o">:</span> <span class="c">##</span><span class="nf"> 安裝 CocoaPods 依賴</span>
 <span class="err">@echo</span> <span class="s2">"📦 Installing CocoaPods dependencies..."</span>
 <span class="err">cd</span> <span class="err">$(PRODUCT_FOLDER)</span> <span class="err">&amp;&amp;</span> <span class="err">\</span>
 <span class="err">bundle</span> <span class="err">exec</span> <span class="err">pod</span> <span class="err">install</span>

<span class="c">### Mint
</span><span class="nl">check-mint</span><span class="o">:</span> <span class="nf">check-brew </span><span class="c">##</span><span class="nf"> 檢查 Mint 是否安裝，沒有就自動安裝</span>
 <span class="err">@if</span> <span class="err">!</span> <span class="err">command</span> <span class="err">-v</span> <span class="err">mint</span> <span class="err">&amp;&gt;</span> <span class="err">/dev/null;</span> <span class="err">then</span> <span class="err">\</span>
  <span class="err">echo</span> <span class="s2">"🔨 Installing mint..."</span><span class="err">;</span> <span class="err">\</span>
  <span class="err">brew</span> <span class="err">install</span> <span class="err">mint;</span> <span class="err">\</span>
 <span class="err">fi</span>

<span class="c">### Brew
</span><span class="nl">check-brew</span><span class="o">:</span> <span class="c">##</span><span class="nf"> 檢查 Homebrew 是否安裝，沒有就自動安裝</span>
 <span class="err">@if</span> <span class="err">!</span> <span class="err">command</span> <span class="err">-v</span> <span class="err">brew</span> <span class="err">&amp;&gt;</span> <span class="err">/dev/null;</span> <span class="err">then</span> <span class="err">\</span>
  <span class="err">echo</span> <span class="s2">"🔨 Installing Homebrew..."</span><span class="err">;</span> <span class="err">\</span>
  <span class="nl">/bin/bash -c "$$(curl -fsSL https</span><span class="o">:</span><span class="nf">//raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"; </span>\
<span class="nf"> fi</span>

<span class="c">## Format only git swift files
</span><span class="nl">.PHONY</span><span class="o">:</span> <span class="nf">format</span>
<span class="nl">format</span><span class="o">:</span> <span class="nf">check-mint </span><span class="c">##</span><span class="nf"> 格式化 Product/ 下所有 Swift 檔</span>
 <span class="err">mint</span> <span class="err">run</span> <span class="err">swiftformat</span> <span class="err">$(PRODUCT_FOLDER)</span>
</code></pre></div></div>

<p>為了避免污染整個系統或其他專案，我們盡可能把 Dependency 套件(e.g. mint, bundle…etc)的路徑都改指定在專案目錄之下 (再搭配 <code class="language-plaintext highlighter-rouge">.gitignore</code> 排除)。</p>
<div class="language-scss highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">├──</span> <span class="nt">mint</span> <span class="o">(</span><span class="nt">Mint</span> <span class="err">依賴</span><span class="o">)</span>
<span class="err">│</span>   <span class="err">└──</span> <span class="nt">packages</span>
<span class="err">├──</span> <span class="nt">Product</span>
<span class="err">│</span>   <span class="err">├──</span> <span class="nt">app-ci-cd-github-actions-demo</span>
<span class="err">│</span>   <span class="err">├──</span> <span class="nt">app-ci-cd-github-actions-demo</span><span class="nc">.xcodeproj</span>
<span class="err">│</span>   <span class="err">├──</span> <span class="nt">app-ci-cd-github-actions-demo</span><span class="nc">.xcworkspace</span>
<span class="err">│</span>   <span class="err">├──</span> <span class="nt">app-ci-cd-github-actions-demoSnapshotTests</span>
<span class="err">│</span>   <span class="err">├──</span> <span class="nt">app-ci-cd-github-actions-demoTests</span>
<span class="err">│</span>   <span class="err">├──</span> <span class="nt">app-ci-cd-github-actions-demoUITests</span>
<span class="err">│</span>   <span class="err">├──</span> <span class="nt">fastlane</span>
<span class="err">│</span>   <span class="err">└──</span> <span class="nt">Pods</span> <span class="o">(</span><span class="nt">Cocoapods</span> <span class="err">依賴</span><span class="o">)</span>
<span class="err">└──</span> <span class="nt">vendor</span> <span class="o">(</span><span class="nt">Bundle</span> <span class="err">依賴</span><span class="o">)</span>
    <span class="err">└──</span> <span class="nt">bundle</span>
</code></pre></div></div>

<p><img src="/assets/4b001d2e8440/1*F1KFntT8bCZzyYm9JahiuA.webp" alt="make help" loading="lazy" decoding="async" width="449" height="142" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0NDkiIGhlaWdodD0iMTQyIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>make help</p>

<p><strong>使用 Makefine 的統一專案 Setup 步驟：</strong></p>
<ol>
  <li><code class="language-plaintext highlighter-rouge">git clone repo</code></li>
  <li><code class="language-plaintext highlighter-rouge">cd ./repo</code></li>
  <li><code class="language-plaintext highlighter-rouge">make setup</code> 
安裝必要的工具依賴 ( <strong>brew</strong> , <strong>mint</strong> , <strong>bundle</strong> , xcodegen, swiftformat,…)</li>
  <li><code class="language-plaintext highlighter-rouge">make install</code> 
產生專案 (執行 pod install, xcodegen)</li>
  <li>完成</li>
  <li>打開、執行專案</li>
</ol>

<blockquote>
  <p><strong><em>不管是 CI/CD 或新人 onboard 都是照以上步驟把專案建置起來。</em></strong></p>
</blockquote>

<h3 id="本篇-github-actions-cicd-案例">本篇 GitHub Actions CI/CD 案例</h3>

<p>本篇會介紹三個 GitHub Actions CI/CD 工作流程建置案例，大家也可以參考其中的步驟建置符合自己團隊工作流程的 CI/CD。</p>
<ol>
  <li>CI — 發 Pull Request 執行單元測試</li>
  <li>CD — 打包+部署到 Firebase App Distribution</li>
  <li>CI +CD— Nightly Build 執行快照+單元測試+打包+部署到 Firebase App Distribution</li>
</ol>

<blockquote>
  <p><em>因 Demo 限制，本文只會串接 打包部署到 Firebase App Distribution，打包到 Testflight 或是 App Store 也是同樣步驟只差別在 Fastlane 裡的腳本不同，大家可以自行發揮。</em></p>
</blockquote>

<h3 id="ci--發-pull-request-執行單元測試">CI — 發 Pull Request 執行單元測試</h3>
<h4 id="流程">流程</h4>

<p>Develop 分支 <strong>無法直接推送</strong> ，必須發 Pull Request 才能更新；所有 Pull Request <strong>均需 Review 通過加上單元測試通過才能 Merge</strong> 、有新 Commit Push 會重新測試。</p>
<h4 id="ci-testingyml">CI-Testing.yml</h4>

<p>Repo → Actions → New workflow → set up a workflow yourself。</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Workflow(Action) 名稱</span>
<span class="na">name</span><span class="pi">:</span> <span class="s">CI-Testing</span>

<span class="c1"># Actions Log 的標題名稱</span>
<span class="na">run-name</span><span class="pi">:</span> <span class="s2">"</span><span class="s">[CI-Testing]</span><span class="nv"> </span><span class="s">${{</span><span class="nv"> </span><span class="s">github.event.pull_request.title</span><span class="nv"> </span><span class="s">||</span><span class="nv"> </span><span class="s">github.ref</span><span class="nv"> </span><span class="s">}}"</span>

<span class="c1"># 同個 Concurrency Group 如果有新的 Job 會取消正在跑的</span>
<span class="c1"># 例如 Push Commit 觸發的任務還沒執行就又 Push Commit 時，會取消前一個任務</span>
<span class="na">concurrency</span><span class="pi">:</span>
  <span class="na">group</span><span class="pi">:</span> <span class="s">${{ github.workflow }}-${{ github.event_name }}-${{ github.event.pull_request.number || github.ref }}</span>
  <span class="na">cancel-in-progress</span><span class="pi">:</span> <span class="kc">true</span>

<span class="c1"># 觸發事件</span>
<span class="na">on</span><span class="pi">:</span>
  <span class="c1"># PR 事件</span>
  <span class="na">pull_request</span><span class="pi">:</span>
    <span class="c1"># PR - 開啟、重開、有新 Push Commit 時</span>
    <span class="na">types</span><span class="pi">:</span> <span class="pi">[</span><span class="nv">opened</span><span class="pi">,</span> <span class="nv">synchronize</span><span class="pi">,</span> <span class="nv">reopened</span><span class="pi">]</span>
  <span class="c1"># 手動表單觸發</span>
  <span class="na">workflow_dispatch</span><span class="pi">:</span>
    <span class="c1"># 表單 Inputs 欄位</span>
    <span class="na">inputs</span><span class="pi">:</span>
      <span class="c1"># 執行的 Test Fastlane Lane</span>
      <span class="na">TEST_LANE</span><span class="pi">:</span>
        <span class="na">description</span><span class="pi">:</span> <span class="s1">'</span><span class="s">Test</span><span class="nv"> </span><span class="s">Lane'</span>
        <span class="na">default</span><span class="pi">:</span> <span class="s1">'</span><span class="s">run_unit_tests'</span>
        <span class="na">type</span><span class="pi">:</span> <span class="s">choice</span>
        <span class="na">options</span><span class="pi">:</span>
          <span class="pi">-</span> <span class="s">run_unit_tests</span>
          <span class="pi">-</span> <span class="s">run_all_tests</span>
  <span class="c1"># 其他 Workflow 呼叫此 Workflow 觸發</span>
  <span class="c1"># Nightly Build 會呼叫使用</span>
  <span class="na">workflow_call</span><span class="pi">:</span>
    <span class="c1"># 表單 Inputs 欄位</span>
    <span class="na">inputs</span><span class="pi">:</span>
      <span class="c1"># 執行的 Test Fastlane Lane</span>
      <span class="na">TEST_LANE</span><span class="pi">:</span>
        <span class="na">description</span><span class="pi">:</span> <span class="s1">'</span><span class="s">Test</span><span class="nv"> </span><span class="s">Lane'</span>
        <span class="na">default</span><span class="pi">:</span> <span class="s1">'</span><span class="s">run_unit_tests'</span>
        <span class="c1"># workflow_call inputs 不支援 choice</span>
        <span class="na">type</span><span class="pi">:</span> <span class="s">string</span>
      <span class="na">BRANCH</span><span class="pi">:</span>
        <span class="na">description</span><span class="pi">:</span> <span class="s1">'</span><span class="s">Branch'</span>
        <span class="na">type</span><span class="pi">:</span> <span class="s">string</span>
  
<span class="c1"># Job 工作項目</span>
<span class="c1"># Job 會並發執行</span>
<span class="na">jobs</span><span class="pi">:</span>
  <span class="c1"># Job ID</span>
  <span class="na">testing</span><span class="pi">:</span>
    <span class="c1"># Job 名稱 (可省略，有設定在 Log 顯示比較好讀)</span>
    <span class="na">name</span><span class="pi">:</span> <span class="s">Testing</span>
    
    <span class="c1"># Runner Label - 使用 GitHub Hosted Runner macos-15 來執行工作</span>
    <span class="c1"># 請注意：因為此專案是 Public Repo 可以無限免費使用</span>
    <span class="c1"># 請注意：因為此專案是 Public Repo 可以無限免費使用</span>
    <span class="c1"># 請注意：因為此專案是 Public Repo 可以無限免費使用</span>
    <span class="c1"># 如果是 Private Repo 需要按計量收費，macOS 機器是最貴的(10倍)，可能跑 10 次就達到 2,000 分鐘免費上限</span>
    <span class="c1"># 建議使用 self-hosted Runner</span>
    <span class="na">runs-on</span><span class="pi">:</span> <span class="s">macos-15</span>

    <span class="c1"># 設定最長 Timeout 時間，防止異常情況發生時無止盡的等待</span>
    <span class="na">timeout-minutes</span><span class="pi">:</span> <span class="m">30</span>

    <span class="c1"># use zsh</span>
    <span class="c1"># 可省略，只是我習慣用 zsh，預設是 bash</span>
    <span class="na">defaults</span><span class="pi">:</span>
      <span class="na">run</span><span class="pi">:</span>
        <span class="na">shell</span><span class="pi">:</span> <span class="s">zsh {0}</span>
          
    <span class="c1"># 工作步驟</span>
    <span class="c1"># 工作步驟會照順序執行  </span>
    <span class="na">steps</span><span class="pi">:</span>
      <span class="c1"># git clone 當前專案 &amp; checkout 到執行的分支</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Checkout repository</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/checkout@v3</span>
        <span class="na">with</span><span class="pi">:</span>
          <span class="c1"># Git Large File Storage，我們的測試環境用不到</span>
          <span class="c1"># default: false</span>
          <span class="na">lfs</span><span class="pi">:</span> <span class="kc">false</span>
          
          <span class="c1"># 如果有指定則 Checkout 指定分支，沒有則使用預設(當前分支)</span>
          <span class="c1"># 因 on: schedule 事件只能在 main 主分支執行，因此想做 Nightly Build 之類的工作就需要指定分支</span>
          <span class="c1"># e.g. on: schedule -&gt; main 分支，Nightly Build master 分支</span>
          <span class="na">ref</span><span class="pi">:</span> <span class="s">${{ github.event.inputs.BRANCH || '' }}</span>

      <span class="c1"># ========== Env Setup Steps ==========</span>
      
      <span class="c1"># 讀取專案指定的 XCode 版本</span>
      <span class="c1"># 在後續之中，我們自己手動指定使用的 XCode_x.x.x.app</span>
      <span class="c1"># 而不使用 xcversion，因為 xcversion 已經 sunset 不穩定。 </span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Read .xcode-version</span>
        <span class="na">id</span><span class="pi">:</span> <span class="s">read_xcode_version</span>
        <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
          <span class="s">XCODE_VERSION=$(cat .xcode-version)</span>
          <span class="s">echo "XCODE_VERSION: ${XCODE_VERSION}"</span>
          <span class="s">echo "xcode_version=${XCODE_VERSION}" &gt;&gt; $GITHUB_OUTPUT</span>

          <span class="s"># 也可以直接在這指定全域 XCode 版本，這樣就不用在後續步驟指定 DEVELOPER_DIR</span>
          <span class="s"># 但此指令需要 sudoer 權限，如果是 self-hosted runner 就要確定 runner 執行環境有 sudo 權限</span>
          <span class="s"># sudo xcode-select -s "/Applications/Xcode_${XCODE_VERSION}.app/Contents/Developer"</span>

      <span class="c1"># 讀取專案指定的 Ruby 版本</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Read .ruby-version</span>
        <span class="na">id</span><span class="pi">:</span> <span class="s">read_ruby_version</span>
        <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
          <span class="s">RUBY_VERSION=$(cat .ruby-version)</span>
          <span class="s">echo "RUBY_VERSION: ${RUBY_VERSION}"</span>
          <span class="s">echo "ruby_version=${RUBY_VERSION}" &gt;&gt; $GITHUB_OUTPUT</span>

      <span class="c1"># 安裝或設定 Runner Ruby 版本成專案指定版本</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Set up Ruby</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">ruby/setup-ruby@v1</span>
        <span class="na">with</span><span class="pi">:</span>
          <span class="na">ruby-version</span><span class="pi">:</span> <span class="s2">"</span><span class="s">${{</span><span class="nv"> </span><span class="s">steps.read_ruby_version.outputs.ruby_version</span><span class="nv"> </span><span class="s">}}"</span>

      <span class="c1"># 可設可不設，原因是之前在 self-hosted 起多個 runner 跑 CI/CD 因為 cocoapods repos 是共用目錄</span>
      <span class="c1"># 解決的問題是：有很小的機率會出現在同時 pod install 時拉 cocoapods repos 出現衝突(因為預設都是用) $HOME/.cocoapods/</span>
      <span class="c1"># GitHub Hosted Runner 則不需此設定</span>
      <span class="c1"># - name: Change Cocoapods Repos Folder</span>
      <span class="c1">#   if: contains(runner.labels, 'self-hosted')</span>
      <span class="c1">#   run: |</span>
      <span class="c1">#     # 每個 Runner 用自己的 .cocoapods 資料夾，防止資源衝突</span>
      <span class="c1">#     mkdir -p "$HOME/.cocoapods-${{ env.RUNNER_NAME }}/"</span>
      <span class="c1">#     export CP_HOME_DIR="$HOME/.cocoapods-${{ env.RUNNER_NAME }}"</span>
      <span class="c1">#     rm -f "$HOME/.cocoapods-${{ env.RUNNER_NAME }}/repos/cocoapods/.git/index.lock"</span>

      <span class="c1"># ========== Cache Setting Steps ==========</span>
      <span class="c1"># 請注意，就算是 self-hosted，Cache 目前也是 Cloud Cache 會計算用量</span>
      <span class="c1"># 規則：7 天未 hit 自動刪除、單個 Cache 上限 10 GB、Action 成功才會 Cache</span>
      <span class="c1"># Public Repo: 免費無限制</span>
      <span class="c1"># Private Repo: 5 GB 起</span>
      <span class="c1"># Self-hosted 可以自己用 shell script 撰寫 Cache &amp; Restore 策略或使用其他工具協助</span>
      
      <span class="c1"># Bundle Cache (Gemfile)</span>
      <span class="c1"># 對應 Makefile 中我們指定了 Bundle  安裝路徑 ./vendor 下</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Cache Bundle</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/cache@v3</span>
        <span class="na">with</span><span class="pi">:</span>
          <span class="na">path</span><span class="pi">:</span> <span class="pi">|</span>
            <span class="s">./vendor</span>
          <span class="na">key</span><span class="pi">:</span> <span class="s">${{ runner.os }}-bundle-${{ hashFiles('Gemfile.lock') }}</span>
          <span class="na">restore-keys</span><span class="pi">:</span> <span class="pi">|</span>
            <span class="s">${{ runner.os }}-bundle-</span>

      <span class="c1"># CocoaPods Cache (Podfile)</span>
      <span class="c1"># 默認就是 專案/Pods 下</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Cache CocoaPods</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/cache@v3</span>
        <span class="na">with</span><span class="pi">:</span>
          <span class="na">path</span><span class="pi">:</span> <span class="pi">|</span>
            <span class="s">./Product/Pods</span>
          <span class="na">key</span><span class="pi">:</span> <span class="s">${{ runner.os }}-cocoapods-${{ hashFiles('Product/Podfile.lock') }}</span>
          <span class="na">restore-keys</span><span class="pi">:</span> <span class="pi">|</span>
            <span class="s">${{ runner.os }}-cocoapods-</span>

      <span class="c1"># Mint cache</span>
      <span class="c1"># 對應 Makefile 中我們指定的 Mint 安裝路徑 ./mint 下</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Cache Mint</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/cache@v3</span>
        <span class="na">with</span><span class="pi">:</span>
          <span class="na">path</span><span class="pi">:</span> <span class="s">./mint</span>
          <span class="na">key</span><span class="pi">:</span> <span class="s">${{ runner.os }}-mint-${{ hashFiles('Mintfile') }}</span>
          <span class="na">restore-keys</span><span class="pi">:</span> <span class="pi">|</span>
            <span class="s">${{ runner.os }}-mint-</span>

      <span class="c1"># ====================</span>

      <span class="c1"># 專案 Setup &amp; 依賴安裝</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Setup &amp; Install Dependency</span>
        <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
          <span class="s"># 執行 Makefile 中封裝的 Setup 指令，對應成指令大概是：</span>
          <span class="s"># brew install mint</span>
          <span class="s"># bundle config set path 'vendor/bundle'</span>
          <span class="s"># bundle install</span>
          <span class="s"># mint bootstrap</span>
          <span class="s"># ...</span>
          <span class="s"># 等等 setup 指令</span>
          <span class="s">make setup</span>

          <span class="s"># 執行 Makefile 中封裝的 Install 指令，對應成指令大概是：</span>
          <span class="s"># mint run yonaskolb/XcodeGen --quiet</span>
          <span class="s"># bundle exec pod install</span>
          <span class="s"># ...</span>
          <span class="s"># 等等 install 指令</span>
          <span class="s">make install</span>

      <span class="c1"># 執行 Fastlane Unit 測試 Lane</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Run Tests</span>
        <span class="na">id</span><span class="pi">:</span> <span class="s">testing</span>
        <span class="c1"># 指定工作目錄，這樣後續指令就不用在特別 cd ./Product/</span>
        <span class="na">working-directory</span><span class="pi">:</span> <span class="s">./Product/</span>
        <span class="na">env</span><span class="pi">:</span>
          <span class="c1"># 測試計劃，全跑還是只跑單元測試</span>
          <span class="c1"># 如為開 PR 觸發則使用 run_unit_tests，否則看 inputs.TEST_LANE 的值，預設值 run_all_tests</span>
          <span class="na">TEST_LANE</span><span class="pi">:</span> <span class="s">${{ github.event_name == 'pull_request' &amp;&amp; 'run_unit_tests' || github.event.inputs.TEST_LANE || 'run_all_tests' }}</span>
          
          <span class="c1"># 指定這個 Job 要使用 XCode_x.x.x 指定的版本執行</span>
          <span class="na">DEVELOPER_DIR</span><span class="pi">:</span> <span class="s2">"</span><span class="s">/Applications/Xcode_${{</span><span class="nv"> </span><span class="s">steps.read_xcode_version.outputs.xcode_version</span><span class="nv"> </span><span class="s">}}.app/Contents/Developer"</span>
          
          <span class="c1"># Repo -&gt; Settings -&gt; Actions secrets and variables -&gt; variables</span>
          <span class="c1"># 使用的模擬器名稱</span>
          <span class="na">SIMULATOR_NAME</span><span class="pi">:</span> <span class="s">${{ vars.SIMULATOR_NAME }}</span>
          <span class="c1"># 模擬器的 iOS 版本</span>
          <span class="na">SIMULATOR_IOS_VERSION</span><span class="pi">:</span> <span class="s">${{ vars.SIMULATOR_IOS_VERSION }}</span>

          <span class="c1"># 當前 Runner 名稱</span>
          <span class="na">RUNNER_NAME</span><span class="pi">:</span> <span class="s">${{ runner.name }}</span>
          
          <span class="c1"># 提升 XCodebuild 指令 timeout 時間, retry 次數</span>
          <span class="c1"># 因為機器 Loading 比較大的時候可能 3 次就失敗了</span>
          <span class="na">FASTLANE_XCODEBUILD_SETTINGS_TIMEOUT</span><span class="pi">:</span> <span class="m">60</span>
          <span class="na">FASTLANE_XCODEBUILD_SETTINGS_RETRIES</span><span class="pi">:</span> <span class="m">10</span>
        <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>

          <span class="s"># 如果是 self-hosted 在同一台機器起多個 Runner 會出現搶模擬器的問題 (文章後會講)</span>
          <span class="s"># 要避免這問題建議將模擬名稱命名成 Runner 名稱，每個 Runner 都設一個模擬器，這樣就不會互搶導致測試失敗</span>
          <span class="s"># e.g. bundle exec fastlane run_unit_tests device:"${RUNNER_NAME} (${SIMULATOR_IOS_VERSION})"</span>
          <span class="s"># 這邊是用 GitHub Hosted Runner 沒這問題，所以直接用 device:"${SIMULATOR_NAME} (${SIMULATOR_IOS_VERSION})"</span>

          <span class="s"># 發生錯誤不直接退出並將所有輸出都寫入 temp/testing_output.txt 檔案</span>
          <span class="s"># 後續我們會分析檔案內容區分出是 Build Failed 還是 Test Failed，Comment 不同訊息到 PR</span>
          <span class="s">set +e</span>
          
          <span class="s"># EXIT_CODE 儲存執行結果的 exit code.</span>
          <span class="s"># 0 = OK</span>
          <span class="s"># 1 = exit</span>
          <span class="s">EXIT_CODE=0</span>
          
          <span class="s"># 所有輸出都寫入檔案</span>
          <span class="s">bundle exec fastlane ${TEST_LANE} device:"${SIMULATOR_NAME} (${SIMULATOR_IOS_VERSION})" | tee "$RUNNER_TEMP/testing_output.txt"</span>
          <span class="s"># 如果目前 EXIT_CODE 是 0，則將 ${PIPESTATUS[0]} = bundle exec fastlane ${TEST_LANE}.. 結果</span>
          <span class="s">[[ $EXIT_CODE -eq 0 ]] &amp;&amp; EXIT_CODE=${PIPESTATUS[0]}</span>

          <span class="s"># 恢復出錯就退出</span>
          <span class="s">set -e</span>

          <span class="s"># 檢查 Testing Output</span>
          <span class="s"># 如果 Testing Output 包含 "Error building"，則設 is_build_error=true 給 Actions 環境變數，為 Build 就失敗</span>
          <span class="s"># 如果 Testing Output 包含 "Tests have failed"，則設 is_test_error=true 給 Actions 環境變數，為測試失敗</span>
          
          <span class="s">if grep -q "Error building" "$RUNNER_TEMP/testing_output.txt"; then</span>
            <span class="s">echo "is_build_error=true" &gt;&gt; $GITHUB_OUTPUT</span>
            <span class="s">echo "❌ Detected Build Error"</span>
          <span class="s">elif grep -q "Tests have failed" "$RUNNER_TEMP/testing_output.txt"; then</span>
            <span class="s">echo "is_test_error=true" &gt;&gt; $GITHUB_OUTPUT</span>
            <span class="s">echo "❌ Detected Test Error"</span>
          <span class="s">fi</span>

          <span class="s"># 恢復 Exit Code Output</span>
          <span class="s">exit $EXIT_CODE</span>
          
      <span class="c1"># ========== Handle Result Steps ==========</span>
      
      <span class="c1"># 解析 *.junit 測試報告，並標記結果、Comment(如果是 PR 的話)</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Publish Test Report</span>
        <span class="c1"># 直接復用別人寫好的 .junit Paser Actions: https://github.com/mikepenz/action-junit-report</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">mikepenz/action-junit-report@v5</span>
        <span class="c1"># if:</span>
        <span class="c1"># 上一步(Testing) success or</span>
        <span class="c1"># 上一步(Testing) failed and is_test_error (build failed 不執行這個 step)</span>
        <span class="na">if</span><span class="pi">:</span> <span class="s">${{ (failure() &amp;&amp; steps.testing.outputs.is_test_error == 'true') || success() }}</span>
        <span class="na">with</span><span class="pi">:</span>
          <span class="na">check_name</span><span class="pi">:</span> <span class="s2">"</span><span class="s">Testing</span><span class="nv"> </span><span class="s">Report"</span>
          <span class="na">comment</span><span class="pi">:</span> <span class="kc">true</span>
          <span class="na">updateComment</span><span class="pi">:</span> <span class="kc">false</span>
          <span class="na">require_tests</span><span class="pi">:</span> <span class="kc">true</span>
          <span class="na">detailed_summary</span><span class="pi">:</span> <span class="kc">true</span>
          <span class="na">report_paths</span><span class="pi">:</span> <span class="s2">"</span><span class="s">./Product/fastlane/test_output/*.junit"</span>

      <span class="c1"># 測試建置失敗 Comment</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Build Failure Comment</span>
        <span class="c1"># if:</span>
        <span class="c1"># 上一步(Testing) failed and is_build_error and 有 PR Number</span>
        <span class="c1"># </span>
        <span class="na">if</span><span class="pi">:</span> <span class="s">${{ failure() &amp;&amp; steps.testing.outputs.is_build_error == 'true' &amp;&amp; github.event.pull_request.number }}</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/github-script@v6</span>
        <span class="na">env</span><span class="pi">:</span>
          <span class="na">action_url</span><span class="pi">:</span> <span class="s2">"</span><span class="s">${{</span><span class="nv"> </span><span class="s">github.server_url</span><span class="nv"> </span><span class="s">}}/${{</span><span class="nv"> </span><span class="s">github.repository</span><span class="nv"> </span><span class="s">}}/actions/runs/${{</span><span class="nv"> </span><span class="s">github.run_id</span><span class="nv"> </span><span class="s">}}/attempts/${{</span><span class="nv"> </span><span class="s">github.run_attempt</span><span class="nv"> </span><span class="s">}}"</span>
        <span class="na">with</span><span class="pi">:</span>
            <span class="na">script</span><span class="pi">:</span> <span class="pi">|</span>
              <span class="s">const action_url = process.env.action_url</span>
              <span class="s">const pullRequest = context.payload.pull_request || {}</span>
              <span class="s">const commitSha = pullRequest.head?.sha || context.sha</span>
              <span class="s">const creator = pullRequest.user?.login || context.actor</span>
        
              <span class="s">const commentBody = [</span>
                <span class="s">`# 專案或測試建置失敗 ❌`,</span>
                <span class="s">`請確認您的 Pull Request 是否可以正確編譯與執行測試。`,</span>
                <span class="s">``,</span>
                <span class="s">`🔗 **Action**: [View Workflow Run](${action_url})`,</span>
                <span class="s">`📝 **Commit**: ${commitSha}`,</span>
                <span class="s">`👤 **Author**: @${creator}`</span>
              <span class="s">].join('\n')</span>
        
              <span class="s">await github.rest.issues.createComment({</span>
                <span class="s">owner: context.repo.owner,</span>
                <span class="s">repo: context.repo.repo,</span>
                <span class="s">issue_number: context.payload.pull_request.number,</span>
                <span class="s">body: commentBody</span>
              <span class="s">})</span>
</code></pre></div></div>

<p><strong>技術重點說明：</strong></p>
<ul>
  <li>runs-on: 建議改用 self-hosted Runner， <a href="https://docs.github.com/en/billing/managing-billing-for-your-products/about-billing-for-github-actions#per-minute-rates" target="_blank">GitHub Hosted Runner macOS 很貴的</a> 。</li>
  <li>手動讀取 <code class="language-plaintext highlighter-rouge">.xcode-version</code> 檔案取得指定的 XCode 版本並在需要指定 XCode 的 Step 設定 <code class="language-plaintext highlighter-rouge">DEVELOPER_DIR</code> env 可以輕鬆免 Sudo 切換 XCode</li>
  <li>Cache: 可以加速依賴安裝速度，但需注意就算是 self-hosted Runner 依然會使用 GitHub Cloud Cache，受到收費限制</li>
  <li>使用 <code class="language-plaintext highlighter-rouge">set +e</code> 使指令執行失敗不會馬上退出+將 output 都輸出到檔案+讀取檔案判斷是 Build Failed or Test Failed；如果不這樣做訊息統一都會是 Test Failed。
也可以延伸去判斷其他錯誤，例如： <code class="language-plaintext highlighter-rouge">Underlying Error: Unable to boot the Simulator.</code> 模擬器啟動失敗，請重新嘗試。</li>
  <li>Checkout Code 可接受指定分支：因為 <code class="language-plaintext highlighter-rouge">on: schedule</code> 事件只能在 main (Default Branch) 觸發，如果我們希望排程對某個其他分支操作，就需要指定分支。</li>
  <li>指定 .cocoapods Repo 路徑可做可不做，之前是遇過 self-hosted 同一台機器兩個 Runner 同時卡在 pod install 就是因為剛好都在針對 .cocoapods Repo 操作造成 git lock。
(但機率很低就是了)</li>
  <li><strong>如果你有 Private Pods Repo 需要設定 SSH Agent 才有權限 Clone。</strong> 
<strong>(請參考文末補充)</strong></li>
  <li>記得到 Repo -&gt; Settings -&gt; Actions secrets and variables -&gt; variables 新增：
<code class="language-plaintext highlighter-rouge">SIMULATOR_IOS_VERSION</code> 模擬器 iOS 版本
<code class="language-plaintext highlighter-rouge">SIMULATOR_NAME</code> 模擬器名稱</li>
</ul>

<p><img src="/assets/4b001d2e8440/1*Vj37P9vZ6KL5wWNvU3Cq3Q.webp" alt="" loading="lazy" decoding="async" width="1156" height="549" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMTU2IiBoZWlnaHQ9IjU0OSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><strong>Commit 檔案到 Repo 主分支，手動觸發一次驗證正不正確：</strong></p>

<p><img src="/assets/4b001d2e8440/1*tA2nKehTJ7aURSGgU7MM0g.webp" alt="" loading="lazy" decoding="async" width="1200" height="413" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjQxMyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/4b001d2e8440/1*Qap3KmhIJrLov2O7GneV3w.webp" alt="" loading="lazy" decoding="async" width="1031" height="910" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDMxIiBoZWlnaHQ9IjkxMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>正確後繼續設定。</p>
<h4 id="github-流程設定">GitHub 流程設定</h4>

<p>Repo → Settins → Rules → Rulesets。</p>

<p><img src="/assets/4b001d2e8440/1*cf_M6NnakdcbzvC2VHSfpw.webp" alt="" loading="lazy" decoding="async" width="1202" height="964" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAyIiBoZWlnaHQ9Ijk2NCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<ul>
  <li>Ruleset Name: 規則名稱</li>
  <li>Enforcement status: 啟用/停用 此規則限制</li>
  <li>Target branches: 目標的 Base 分支，設 Default Branch 就是所有想合進到 main or develop 的分支都受到此規則限制</li>
  <li>Bypass list: 可指定特殊身份、Team 可以不受此限</li>
  <li>Branch rules:</li>
</ul>

<p><img src="/assets/4b001d2e8440/1*Hzd9CCTsu18kyDR1goRWag.webp" alt="" loading="lazy" decoding="async" width="792" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3OTIiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<ul>
  <li>Restrict deletions: 禁止刪除分支</li>
  <li>Require a pull request before mergin: 只能透過 PR Merge
Required approvals: 限制需要幾人 Approve</li>
  <li>Require status checks to pass: 限制哪些 Checks 要 Passed 才能 Merge
點 + Add checks 輸入 <code class="language-plaintext highlighter-rouge">Testing</code> 選擇有 GitHub Actions 標誌的。
<strong>這邊有個小問題，如果 Suggestions</strong> <strong>找不到 <code class="language-plaintext highlighter-rouge">Testing</code> 那需要先回到 Actions 觸發(開 PR 試試)成功一次，這裡才會出現。</strong></li>
</ul>

<p><img src="/assets/4b001d2e8440/1*MRPwUEVVbbz8nH0sS2hFfQ.webp" alt="" loading="lazy" decoding="async" width="834" height="394" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4MzQiIGhlaWdodD0iMzk0Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<ul>
  <li>Block force pushes: 禁止 Force push</li>
</ul>

<p>儲存、確定 Enforcement status 是 Active 後，規則就會生效了。</p>

<p><strong>都設定好之後，開 PR 測試看看：</strong></p>

<p><img src="/assets/4b001d2e8440/1*iPETBWx6Boq12rY1fHXmuQ.webp" alt="" loading="lazy" decoding="async" width="919" height="546" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MTkiIGhlaWdodD0iNTQ2Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<ul>
  <li>Checks 有出現 CI-Testing ( <strong>Requried</strong> )、Merging is blocked、At least X approving review is required by reviewers with write access. 代表設置成功了。</li>
</ul>

<p><strong>如果專案建置失敗 (Build Failed) 會 Comment：</strong></p>

<p><img src="/assets/4b001d2e8440/1*4E35VRPo--pI8uqrR4UZNA.webp" alt="" loading="lazy" decoding="async" width="915" height="295" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MTUiIGhlaWdodD0iMjk1Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><strong>如果專案建置成功但測試案例失敗 (Test Failed) 會 Comment：</strong></p>

<p><img src="/assets/4b001d2e8440/1*wYFzsn5a_yvi8CbZxi-QdQ.webp" alt="" loading="lazy" decoding="async" width="921" height="389" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MjEiIGhlaWdodD0iMzg5Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><strong>如果專案建置成功測試也成功(Test Success) 會 Comment：</strong></p>

<p><img src="/assets/4b001d2e8440/1*WqPK1629jYdYTEWCWBHWqg.webp" alt="" loading="lazy" decoding="async" width="915" height="321" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MTUiIGhlaWdodD0iMzIxIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><strong>完成 Review Approve + Check 測試通過後：</strong></p>

<p><img src="/assets/4b001d2e8440/1*5gnQYdVAOtGR-bMK4ZrOhA.webp" alt="Demo PR" loading="lazy" decoding="async" width="911" height="566" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MTEiIGhlaWdodD0iNTY2Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><a href="https://github.com/ZhgChgLi/github-actions-ci-cd-demo/pull/11" target="_blank">Demo PR</a></p>

<p>就能 Merge PR。</p>
<ul>
  <li>如果有 Push New Commit 會自動重跑 Checks 測試。</li>
</ul>

<p><strong>完整程式碼： <a href="https://github.com/ZhgChgLi/github-actions-ci-cd-demo/blob/main/.github/workflows/CI-Testing.yml" target="_blank">CI-Testing.yml</a></strong></p>

<p><strong>Auto-merge:</strong></p>

<p>另外，也可以打開 Repo Settings → General → Pull Request 中的：</p>

<p><img src="/assets/4b001d2e8440/1*hl6mfqnnv_zAA3YVrI_M0w.webp" alt="" loading="lazy" decoding="async" width="856" height="257" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4NTYiIGhlaWdodD0iMjU3Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<ul>
  <li>Automatically delete head branches: Merge PR 後自動刪除分支</li>
  <li>Allow Auto-merge: 當 Checks 通過＋符合條件的 Approvals 達成會自動 Merge PR
<strong>只有當有設條件＋當前條件還不能 Merge 時才會出現 Enable auto-merge 按鈕。</strong></li>
</ul>

<p><img src="/assets/4b001d2e8440/1*gi0LaaMVXit-DGKnsxS1lQ.webp" alt="" loading="lazy" decoding="async" width="927" height="431" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MjciIGhlaWdodD0iNDMxIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<h3 id="cd--打包部署到-firebase-app-distribution">CD — 打包+部署到 Firebase App Distribution</h3>
<h4 id="流程-1">流程</h4>

<p>使用 GitHub Actions 表單觸發打包工作，可指定版本號、Release Notes，打包完會自動上傳到 Firebase App Distribution 供團隊下載測試使用。</p>
<h4 id="cd-deployyml">CD-Deploy.yml</h4>

<p>Repo → Actions → New workflow → set up a workflow yourself。</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Workflow(Action) 名稱</span>
<span class="na">name</span><span class="pi">:</span> <span class="s">CD-Deploy</span>

<span class="c1"># Actions Log 的標題名稱</span>
<span class="na">run-name</span><span class="pi">:</span> <span class="s2">"</span><span class="s">[CD-Deploy]</span><span class="nv"> </span><span class="s">${{</span><span class="nv"> </span><span class="s">github.ref</span><span class="nv"> </span><span class="s">}}"</span>

<span class="c1"># 同個 Concurrency Group 如果有新的 Job 會取消正在跑的</span>
<span class="c1"># 例如 重複觸發相同分支的打包任務，會取消前一個任務</span>
<span class="na">concurrency</span><span class="pi">:</span>
  <span class="na">group</span><span class="pi">:</span> <span class="s">${{ github.workflow }}-${{ github.event_name }}-${{ github.ref }}</span>
  <span class="na">cancel-in-progress</span><span class="pi">:</span> <span class="kc">true</span>

<span class="c1"># 觸發事件</span>
<span class="na">on</span><span class="pi">:</span>
  <span class="c1"># 手動表單觸發</span>
  <span class="na">workflow_dispatch</span><span class="pi">:</span>
    <span class="c1"># 表單 Inputs 欄位</span>
    <span class="na">inputs</span><span class="pi">:</span>
      <span class="c1"># App 版本號</span>
      <span class="na">VERSION_NUMBER</span><span class="pi">:</span>
        <span class="na">description</span><span class="pi">:</span> <span class="s1">'</span><span class="s">Version</span><span class="nv"> </span><span class="s">Number</span><span class="nv"> </span><span class="s">of</span><span class="nv"> </span><span class="s">the</span><span class="nv"> </span><span class="s">app</span><span class="nv"> </span><span class="s">(e.g.,</span><span class="nv"> </span><span class="s">1.0.0).</span><span class="nv"> </span><span class="s">Auto-detect</span><span class="nv"> </span><span class="s">from</span><span class="nv"> </span><span class="s">the</span><span class="nv"> </span><span class="s">Xcode</span><span class="nv"> </span><span class="s">project</span><span class="nv"> </span><span class="s">if</span><span class="nv"> </span><span class="s">left</span><span class="nv"> </span><span class="s">blank.'</span>
        <span class="na">required</span><span class="pi">:</span> <span class="kc">false</span>
        <span class="na">type</span><span class="pi">:</span> <span class="s">string</span>
      <span class="c1"># App Build Number</span>
      <span class="na">BUILD_NUMBER</span><span class="pi">:</span>
        <span class="na">description</span><span class="pi">:</span> <span class="s1">'</span><span class="s">Build</span><span class="nv"> </span><span class="s">number</span><span class="nv"> </span><span class="s">of</span><span class="nv"> </span><span class="s">the</span><span class="nv"> </span><span class="s">app</span><span class="nv"> </span><span class="s">(e.g.,</span><span class="nv"> </span><span class="s">1).</span><span class="nv"> </span><span class="s">Will</span><span class="nv"> </span><span class="s">use</span><span class="nv"> </span><span class="s">a</span><span class="nv"> </span><span class="s">timestamp</span><span class="nv"> </span><span class="s">if</span><span class="nv"> </span><span class="s">left</span><span class="nv"> </span><span class="s">blank.'</span>
        <span class="na">required</span><span class="pi">:</span> <span class="kc">false</span>
        <span class="na">type</span><span class="pi">:</span> <span class="s">string</span>
      <span class="c1"># App Release Note</span>
      <span class="na">RELEASE_NOTE</span><span class="pi">:</span>
        <span class="na">description</span><span class="pi">:</span> <span class="s1">'</span><span class="s">Release</span><span class="nv"> </span><span class="s">notes</span><span class="nv"> </span><span class="s">of</span><span class="nv"> </span><span class="s">the</span><span class="nv"> </span><span class="s">deployment.'</span>
        <span class="na">required</span><span class="pi">:</span> <span class="kc">false</span>
        <span class="na">type</span><span class="pi">:</span> <span class="s">string</span>
  <span class="c1"># 其他 Workflow 呼叫此 Workflow 觸發</span>
  <span class="c1"># Nightly Build 會呼叫使用</span>
  <span class="na">workflow_call</span><span class="pi">:</span>
    <span class="na">inputs</span><span class="pi">:</span>
      <span class="c1"># App 版本號</span>
      <span class="na">VERSION_NUMBER</span><span class="pi">:</span>
        <span class="na">description</span><span class="pi">:</span> <span class="s1">'</span><span class="s">Version</span><span class="nv"> </span><span class="s">Number</span><span class="nv"> </span><span class="s">of</span><span class="nv"> </span><span class="s">the</span><span class="nv"> </span><span class="s">app</span><span class="nv"> </span><span class="s">(e.g.,</span><span class="nv"> </span><span class="s">1.0.0).</span><span class="nv"> </span><span class="s">Auto-detect</span><span class="nv"> </span><span class="s">from</span><span class="nv"> </span><span class="s">the</span><span class="nv"> </span><span class="s">Xcode</span><span class="nv"> </span><span class="s">project</span><span class="nv"> </span><span class="s">if</span><span class="nv"> </span><span class="s">left</span><span class="nv"> </span><span class="s">blank.'</span>
        <span class="na">required</span><span class="pi">:</span> <span class="kc">false</span>
        <span class="na">type</span><span class="pi">:</span> <span class="s">string</span>
      <span class="c1"># App Build Number</span>
      <span class="na">BUILD_NUMBER</span><span class="pi">:</span>
        <span class="na">description</span><span class="pi">:</span> <span class="s1">'</span><span class="s">Build</span><span class="nv"> </span><span class="s">number</span><span class="nv"> </span><span class="s">of</span><span class="nv"> </span><span class="s">the</span><span class="nv"> </span><span class="s">app</span><span class="nv"> </span><span class="s">(e.g.,</span><span class="nv"> </span><span class="s">1).</span><span class="nv"> </span><span class="s">Will</span><span class="nv"> </span><span class="s">use</span><span class="nv"> </span><span class="s">a</span><span class="nv"> </span><span class="s">timestamp</span><span class="nv"> </span><span class="s">if</span><span class="nv"> </span><span class="s">left</span><span class="nv"> </span><span class="s">blank.'</span>
        <span class="na">required</span><span class="pi">:</span> <span class="kc">false</span>
        <span class="na">type</span><span class="pi">:</span> <span class="s">string</span>
      <span class="c1"># App Release Note</span>
      <span class="na">RELEASE_NOTE</span><span class="pi">:</span>
        <span class="na">description</span><span class="pi">:</span> <span class="s1">'</span><span class="s">Release</span><span class="nv"> </span><span class="s">notes</span><span class="nv"> </span><span class="s">of</span><span class="nv"> </span><span class="s">the</span><span class="nv"> </span><span class="s">deployment.'</span>
        <span class="na">required</span><span class="pi">:</span> <span class="kc">false</span>
        <span class="na">type</span><span class="pi">:</span> <span class="s">string</span>
      <span class="na">BRANCH</span><span class="pi">:</span>
        <span class="na">description</span><span class="pi">:</span> <span class="s1">'</span><span class="s">Branch'</span>
        <span class="na">type</span><span class="pi">:</span> <span class="s">string</span>


<span class="c1"># 定義全域靜態變數</span>
<span class="na">env</span><span class="pi">:</span>
  <span class="na">APP_STORE_CONNECT_API_KEY_FILE_NAME</span><span class="pi">:</span> <span class="s2">"</span><span class="s">app_store_connect_api_key.json"</span>

<span class="c1"># Job 工作項目</span>
<span class="c1"># Job 會並發執行</span>
<span class="na">jobs</span><span class="pi">:</span>
  <span class="c1"># Job ID</span>
  <span class="na">deploy</span><span class="pi">:</span>
    <span class="c1"># Job 名稱 (可省略，有設定在 Log 顯示比較好讀)</span>
    <span class="na">name</span><span class="pi">:</span> <span class="s">Deploy - Firebase App Distribution</span>
    
    <span class="c1"># Runner Label - 使用 GitHub Hosted Runner macos-15 來執行工作</span>
    <span class="c1"># 請注意：因為此專案是 Public Repo 可以無限免費使用</span>
    <span class="c1"># 請注意：因為此專案是 Public Repo 可以無限免費使用</span>
    <span class="c1"># 請注意：因為此專案是 Public Repo 可以無限免費使用</span>
    <span class="c1"># 如果是 Private Repo 需要按計量收費，macOS 機器是最貴的(10倍)，可能跑 10 次就達到 2,000 分鐘免費上限</span>
    <span class="c1"># 建議使用 self-hosted Runner</span>
    <span class="na">runs-on</span><span class="pi">:</span> <span class="s">macos-15</span>

    <span class="c1"># 設定最長 Timeout 時間，防止異常情況發生時無止盡的等待</span>
    <span class="na">timeout-minutes</span><span class="pi">:</span> <span class="m">30</span>

    <span class="c1"># use zsh</span>
    <span class="c1"># 可省略，只是我習慣用 zsh，預設是 bash</span>
    <span class="na">defaults</span><span class="pi">:</span>
      <span class="na">run</span><span class="pi">:</span>
        <span class="na">shell</span><span class="pi">:</span> <span class="s">zsh {0}</span>

    <span class="c1"># 工作步驟</span>
    <span class="c1"># 工作步驟會照順序執行  </span>
    <span class="na">steps</span><span class="pi">:</span>
      <span class="c1"># git clone 當前專案 &amp; checkout 到執行的分支</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Checkout repository</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/checkout@v3</span>
        <span class="na">with</span><span class="pi">:</span>
          <span class="c1"># Git Large File Storage，我們的測試環境用不到</span>
          <span class="c1"># default: false</span>
          <span class="na">lfs</span><span class="pi">:</span> <span class="kc">false</span>
          
          <span class="c1"># 如果有指定則 Checkout 指定分支，沒有則使用預設(當前分支)</span>
          <span class="c1"># 因 on: schedule 事件只能在 main 主分支執行，因此想做 Nightly Build 之類的工作就需要指定分支</span>
          <span class="c1"># e.g. on: schedule -&gt; main 分支，Nightly Build master 分支</span>
          <span class="na">ref</span><span class="pi">:</span> <span class="s">${{ github.event.inputs.BRANCH || '' }}</span>

      <span class="c1"># ========== Certificates Steps ==========</span>
      
      <span class="c1"># 建議是使用 Fastlnae - Match 管理開發憑證並在 Lane 中直接執行 match 安裝設定好</span>
      <span class="c1"># Match 會用另一個 Private Repo 管理憑證，但要設定好 SSH Agent 才有權限 git clone private repo</span>
      <span class="c1"># ref: https://stackoverflow.com/questions/57612428/cloning-private-github-repository-within-organisation-in-actions</span>
      <span class="c1">#</span>
      <span class="c1">#</span>
      <span class="c1"># --- 以下是沒有使用 Fastlane - Match 的情況下直接下載 &amp; Import 憑證給 Runner 的做法 ---</span>
      <span class="c1"># ref: https://docs.github.com/en/actions/how-tos/use-cases-and-examples/deploying/installing-an-apple-certificate-on-macos-runners-for-xcode-development</span>
      <span class="c1">#</span>
      <span class="c1"># GitHub Actions Secret 無法儲存檔案，因此所有憑證檔案都要先轉成 Base64 Encoded 文字格式存在 Secret</span>
      <span class="c1"># 在 GitHub Actions Step 中再動態讀出來寫入 TEMP 檔案並移動到正確位置給系統讀取使用</span>
      <span class="c1"># 其他設定細節請參考文章</span>
      <span class="c1">#</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Install the Apple certificate and provisioning profile</span>
        <span class="na">env</span><span class="pi">:</span>
          <span class="na">BUILD_CERTIFICATE_BASE64</span><span class="pi">:</span> <span class="s">${{ secrets.BUILD_CERTIFICATE_BASE64 }}</span>
          <span class="na">P12_PASSWORD</span><span class="pi">:</span> <span class="s">${{ secrets.BUILD_CERTIFICATE_P12_PASSWORD }}</span>
          <span class="na">BUILD_PROVISION_PROFILE_BASE64</span><span class="pi">:</span> <span class="s">${{ secrets.BUILD_PROVISION_PROFILE_BASE64 }}</span>
          <span class="c1"># GitHub Hosted Runner 為自定義字串</span>
          <span class="c1"># Self-hosted Runner 為機器登入密碼</span>
          <span class="na">KEYCHAIN_PASSWORD</span><span class="pi">:</span> <span class="s">${{ secrets.KEYCHAIN_PASSWORD }}</span>
        <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
          <span class="s"># create variables</span>
          <span class="s">CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12</span>
          <span class="s">PP_PATH=$RUNNER_TEMP/build_pp.mobileprovision</span>
          <span class="s">KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db</span>

          <span class="s"># import certificate and provisioning profile from secrets</span>
          <span class="s">echo -n "$BUILD_CERTIFICATE_BASE64" | base64 --decode -o $CERTIFICATE_PATH</span>
          <span class="s">echo -n "$BUILD_PROVISION_PROFILE_BASE64" | base64 --decode -o $PP_PATH</span>

          <span class="s"># create temporary keychain</span>
          <span class="s">security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH</span>
          <span class="s">security set-keychain-settings -lut 21600 $KEYCHAIN_PATH</span>
          <span class="s">security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH</span>

          <span class="s"># import certificate to keychain</span>
          <span class="s">security import $CERTIFICATE_PATH -P "$P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH</span>
          <span class="s">security set-key-partition-list -S apple-tool:,apple: -k "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH</span>
          <span class="s">security list-keychain -d user -s $KEYCHAIN_PATH</span>

          <span class="s"># apply provisioning profile</span>
          <span class="s">mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles</span>
          <span class="s">cp $PP_PATH ~/Library/MobileDevice/Provisioning\ Profiles</span>

      <span class="c1"># App Store Connect API Fastlane JSON Key</span>
      <span class="c1"># 另一個在打包環境幾乎是必須的 App Store Connect API Fastlane JSON Key (.json)</span>
      <span class="c1"># format: .json 內容格式：https://docs.fastlane.tools/app-store-connect-api/</span>
      <span class="c1"># 裡面包含 App Store Connect API .p8 Key</span>
      <span class="c1"># 會在後續帶給 Fastlane，用於上傳到 Testflight、App Store API 使用</span>
      <span class="c1">#</span>
      <span class="c1"># GitHub Actions Secret 無法儲存檔案，因此所有憑證檔案都要先轉成 Base64 Encoded 文字格式存在 Secret</span>
      <span class="c1"># 在 GitHub Actions Step 中再動態讀出來寫入 TEMP 檔案供其他步驟引用使用</span>
      <span class="c1"># 其他設定細節請參考文章</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Read and Write Apple Store Connect API Key to Temp</span>
        <span class="na">env</span><span class="pi">:</span>
          <span class="na">APP_STORE_CONNECT_API_KEY_BASE64</span><span class="pi">:</span> <span class="s">${{ secrets.APP_STORE_CONNECT_API_KEY_BASE64 }}</span>
          <span class="na">APP_STORE_CONNECT_API_KEY_PATH</span><span class="pi">:</span> <span class="s2">"</span><span class="s">${{</span><span class="nv"> </span><span class="s">runner.temp</span><span class="nv"> </span><span class="s">}}/${{</span><span class="nv"> </span><span class="s">env.APP_STORE_CONNECT_API_KEY_FILE_NAME</span><span class="nv"> </span><span class="s">}}"</span>
        <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
          <span class="s"># import certificate and provisioning profile from secrets</span>
          <span class="s">echo -n "$APP_STORE_CONNECT_API_KEY_BASE64" | base64 --decode -o $APP_STORE_CONNECT_API_KEY_PATH</span>

      <span class="c1"># ========== Env Setup Steps ==========</span>
      
      <span class="c1"># 讀取專案指定的 XCode 版本</span>
      <span class="c1"># 在後續之中，我們自己手動指定使用的 XCode_x.x.x.app</span>
      <span class="c1"># 而不使用 xcversion，因為 xcversion 已經 sunset 不穩定。 </span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Read .xcode-version</span>
        <span class="na">id</span><span class="pi">:</span> <span class="s">read_xcode_version</span>
        <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
          <span class="s">XCODE_VERSION=$(cat .xcode-version)</span>
          <span class="s">echo "XCODE_VERSION: ${XCODE_VERSION}"</span>
          <span class="s">echo "xcode_version=${XCODE_VERSION}" &gt;&gt; $GITHUB_OUTPUT</span>

          <span class="s"># 也可以直接在這指定全域 XCode 版本，這樣就不用在後續步驟指定 DEVELOPER_DIR</span>
          <span class="s"># 但此指令需要 sudoer 權限，如果是 self-hosted runner 就要確定 runner 執行環境有 sudo 權限</span>
          <span class="s"># sudo xcode-select -s "/Applications/Xcode_${XCODE_VERSION}.app/Contents/Developer"</span>

      <span class="c1"># 讀取專案指定的 Ruby 版本</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Read .ruby-version</span>
        <span class="na">id</span><span class="pi">:</span> <span class="s">read_ruby_version</span>
        <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
          <span class="s">RUBY_VERSION=$(cat .ruby-version)</span>
          <span class="s">echo "RUBY_VERSION: ${RUBY_VERSION}"</span>
          <span class="s">echo "ruby_version=${RUBY_VERSION}" &gt;&gt; $GITHUB_OUTPUT</span>

      <span class="c1"># 安裝或設定 Runner Ruby 版本成專案指定版本</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Set up Ruby</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">ruby/setup-ruby@v1</span>
        <span class="na">with</span><span class="pi">:</span>
          <span class="na">ruby-version</span><span class="pi">:</span> <span class="s2">"</span><span class="s">${{</span><span class="nv"> </span><span class="s">steps.read_ruby_version.outputs.ruby_version</span><span class="nv"> </span><span class="s">}}"</span>

      <span class="c1"># 可設可不設，原因是之前在 self-hosted 起多個 runner 跑 CI/CD 因為 cocoapods repos 是共用目錄</span>
      <span class="c1"># 解決的問題是：有很小的機率會出現在同時 pod install 時拉 cocoapods repos 出現衝突(因為預設都是用) $HOME/.cocoapods/</span>
      <span class="c1"># GitHub Hosted Runner 則不需此設定</span>
      <span class="c1"># - name: Change Cocoapods Repos Folder</span>
      <span class="c1">#   if: contains(runner.labels, 'self-hosted')</span>
      <span class="c1">#   run: |</span>
      <span class="c1">#     # 每個 Runner 用自己的 .cocoapods 資料夾，防止資源衝突</span>
      <span class="c1">#     mkdir -p "$HOME/.cocoapods-${{ env.RUNNER_NAME }}/"</span>
      <span class="c1">#     export CP_HOME_DIR="$HOME/.cocoapods-${{ env.RUNNER_NAME }}"</span>
      <span class="c1">#     rm -f "$HOME/.cocoapods-${{ env.RUNNER_NAME }}/repos/cocoapods/.git/index.lock"</span>

      <span class="c1"># ========== Cache Setting Steps ==========</span>
      <span class="c1"># 請注意，就算是 self-hosted，Cache 目前也是 Cloud Cache 會計算用量</span>
      <span class="c1"># 規則：7 天未 hit 自動刪除、單個 Cache 上限 10 GB、Action 成功才會 Cache</span>
      <span class="c1"># Public Repo: 免費無限制</span>
      <span class="c1"># Private Repo: 5 GB 起</span>
      <span class="c1"># Self-hosted 可以自己用 shell script 撰寫 Cache &amp; Restore 策略或使用其他工具協助</span>
      
      <span class="c1"># Bundle Cache (Gemfile)</span>
      <span class="c1"># 對應 Makefile 中我們指定了 Bundle  安裝路徑 ./vendor 下</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Cache Bundle</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/cache@v3</span>
        <span class="na">with</span><span class="pi">:</span>
          <span class="na">path</span><span class="pi">:</span> <span class="pi">|</span>
            <span class="s">./vendor</span>
          <span class="na">key</span><span class="pi">:</span> <span class="s">${{ runner.os }}-bundle-${{ hashFiles('Gemfile.lock') }}</span>
          <span class="na">restore-keys</span><span class="pi">:</span> <span class="pi">|</span>
            <span class="s">${{ runner.os }}-bundle-</span>

      <span class="c1"># CocoaPods Cache (Podfile)</span>
      <span class="c1"># 默認就是 專案/Pods 下</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Cache CocoaPods</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/cache@v3</span>
        <span class="na">with</span><span class="pi">:</span>
          <span class="na">path</span><span class="pi">:</span> <span class="pi">|</span>
            <span class="s">./Product/Pods</span>
          <span class="na">key</span><span class="pi">:</span> <span class="s">${{ runner.os }}-cocoapods-${{ hashFiles('Product/Podfile.lock') }}</span>
          <span class="na">restore-keys</span><span class="pi">:</span> <span class="pi">|</span>
            <span class="s">${{ runner.os }}-cocoapods-</span>

      <span class="c1"># Mint cache</span>
      <span class="c1"># 對應 Makefile 中我們指定的 Mint 安裝路徑 ./mint 下</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Cache Mint</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/cache@v3</span>
        <span class="na">with</span><span class="pi">:</span>
          <span class="na">path</span><span class="pi">:</span> <span class="s">./mint</span>
          <span class="na">key</span><span class="pi">:</span> <span class="s">${{ runner.os }}-mint-${{ hashFiles('Mintfile') }}</span>
          <span class="na">restore-keys</span><span class="pi">:</span> <span class="pi">|</span>
            <span class="s">${{ runner.os }}-mint-</span>

      <span class="c1"># ====================</span>

      <span class="c1"># 專案 Setup &amp; 依賴安裝</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Setup &amp; Install Dependency</span>
        <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
          <span class="s"># 執行 Makefile 中封裝的 Setup 指令，對應成指令大概是：</span>
          <span class="s"># brew install mint</span>
          <span class="s"># bundle config set path 'vendor/bundle'</span>
          <span class="s"># bundle install</span>
          <span class="s"># mint bootstrap</span>
          <span class="s"># ...</span>
          <span class="s"># 等等 setup 指令</span>
          <span class="s">make setup</span>

          <span class="s"># 執行 Makefile 中封裝的 Install 指令，對應成指令大概是：</span>
          <span class="s"># mint run yonaskolb/XcodeGen --quiet</span>
          <span class="s"># bundle exec pod install</span>
          <span class="s"># ...</span>
          <span class="s"># 等等 install 指令</span>
          <span class="s">make install</span>

      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Deploy Beta</span>
        <span class="na">id</span><span class="pi">:</span> <span class="s">deploy</span>
        <span class="c1"># 指定工作目錄，這樣後續指令就不用在特別 cd ./Product/</span>
        <span class="na">working-directory</span><span class="pi">:</span> <span class="s">./Product/</span>
        <span class="na">env</span><span class="pi">:</span>
          <span class="c1"># 打包 Input 參數</span>
          <span class="na">VERSION_NUMBER</span><span class="pi">:</span> <span class="s">${{ inputs.VERSION_NUMBER || '' }}</span>
          <span class="na">BUILD_NUMBER</span><span class="pi">:</span> <span class="s">${{ inputs.BUILD_NUMBER || '' }}</span>
          <span class="na">RELEASE_NOTE</span><span class="pi">:</span> <span class="s">${{ inputs.RELEASE_NOTE || '' }}</span>
          <span class="na">AUTHOR</span><span class="pi">:</span> <span class="s">${{ github.actor }}</span>

          <span class="c1"># Repo -&gt; Settings -&gt; Actions secrets and variables -&gt; secrets</span>
          <span class="c1"># Firebase CLI Token 密鑰 (取得方式請參考文章)</span>
          <span class="na">FIREBASE_CLI_TOKEN</span><span class="pi">:</span> <span class="s">${{ secrets.FIREBASE_CLI_TOKEN }}</span>
          <span class="c1"># Apple Developer Program Team ID</span>
          <span class="na">TEAM_ID</span><span class="pi">:</span> <span class="s">${{ secrets.TEAM_ID }}</span>
                    
          <span class="c1"># 指定這個 Job 要使用 XCode_x.x.x 指定的版本執行</span>
          <span class="na">DEVELOPER_DIR</span><span class="pi">:</span> <span class="s2">"</span><span class="s">/Applications/Xcode_${{</span><span class="nv"> </span><span class="s">steps.read_xcode_version.outputs.xcode_version</span><span class="nv"> </span><span class="s">}}.app/Contents/Developer"</span>
        <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
          <span class="s"># 取得當前 Timestamp</span>
          <span class="s">BUILD_TIMESTAMP=$(date +'%Y%m%d%H%M%S')</span>

          <span class="s"># 如果 BUILD_NUMBER 沒有值，用 Timestamp 當 App Build Number</span>
          <span class="s">BUILD_NUMBER="${BUILD_NUMBER:-$BUILD_TIMESTAMP}"</span>
  
          <span class="s">ID="${{ github.run_id }}"</span>
          <span class="s">COMMIT_SHA="${{ github.sha }}"</span>
          <span class="s">BRANCH_NAME="${{ github.ref_name }}"</span>
          <span class="s">AUTHOR="${{ env.AUTHOR }}"</span>

          <span class="s"># 組合 Release Note</span>
          <span class="s">RELEASE_NOTE="${{ env.RELEASE_NOTE }}</span>
          <span class="s">ID: ${ID}</span>
          <span class="s">Commit SHA: ${COMMIT_SHA}</span>
          <span class="s">Branch: ${BRANCH_NAME}</span>
          <span class="s">Author: ${AUTHOR}</span>
          <span class="s">"</span>

          <span class="s"># 執行 Fastlane 打包＆部署 Lane</span>
          <span class="s">bundle exec fastlane beta release_notes:"${RELEASE_NOTE}" version_number:"${VERSION_NUMBER}" build_number:"${BUILD_NUMBER}"</span>

      <span class="c1"># GitHub Actions 建議的 self-hosted 安全性設定：</span>
      <span class="c1"># ref: https://docs.github.com/en/actions/how-tos/use-cases-and-examples/deploying/installing-an-apple-certificate-on-macos-runners-for-xcode-development#required-clean-up-on-self-hosted-runners</span>
      <span class="c1"># 對應 Step: Install the Apple certificate and provisioning profile</span>
      <span class="c1"># 用途是刪除機器上下載下來的金鑰憑證</span>
      <span class="c1"># 如果你是用 Match 則需要改寫成 Match 的 Clean</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Clean up keychain and provisioning profile</span>
        <span class="na">if</span><span class="pi">:</span> <span class="s">${{ always() &amp;&amp; contains(runner.labels, 'self-hosted') }}</span>
        <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
          <span class="s">security delete-keychain $RUNNER_TEMP/app-signing.keychain-db</span>
          <span class="s">rm ~/Library/MobileDevice/Provisioning\ Profiles/build_pp.mobileprovision</span>
</code></pre></div></div>
<ul>
  <li>記得到 Repo -&gt; Settings -&gt; Actions secrets and variables -&gt; secrets 新增一個 <code class="language-plaintext highlighter-rouge">TEAM_ID</code> 變數，內容是 Apple Developer Team ID 字串。</li>
</ul>

<p><img src="/assets/4b001d2e8440/1*dxdmT_N_w_VUd56f6GRLSQ.webp" alt="" loading="lazy" decoding="async" width="1181" height="498" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMTgxIiBoZWlnaHQ9IjQ5OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><strong>Commit 檔案到 Repo 主分支，測試看看打包功能：</strong></p>

<p><img src="/assets/4b001d2e8440/1*Z_UAfWAsJSIoWxeTRMvtDQ.webp" alt="" loading="lazy" decoding="async" width="1072" height="685" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDcyIiBoZWlnaHQ9IjY4NSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<blockquote>
  <p><em>請注意若其他分支要使用此 Action 需要先 Merge 主分支的 CD-Deploy.yml 檔案。</em></p>
</blockquote>

<p><strong>等待任務跑完：</strong></p>

<p><img src="/assets/4b001d2e8440/1*Q-c2IUlJpssooiqcqcm_Bg.webp" alt="" loading="lazy" decoding="async" width="1386" height="1288" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMzg2IiBoZWlnaHQ9IjEyODgiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/4b001d2e8440/1*W8PBkatfsITMDFSlp7xpjg.webp" alt="Demo" loading="lazy" decoding="async" width="1200" height="975" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijk3NSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><a href="https://github.com/ZhgChgLi/github-actions-ci-cd-demo/actions/runs/16114046420" target="_blank">Demo</a></p>

<blockquote>
  <p><strong><em>打包＋部署成功 ✅</em></strong></p>
</blockquote>

<p><strong>完整程式碼： <a href="https://github.com/ZhgChgLi/github-actions-ci-cd-demo/blob/main/.github/workflows/CD-Deploy.yml" target="_blank">CD-Deploy.yml</a></strong></p>
<h4 id="技術細節--firebase-cli-token-取得設定">技術細節 — Firebase CLI Token 取得＆設定</h4>

<p>按照 <a href="https://firebase.google.com/docs/cli?hl=zh-tw#install-cli-mac-linux" target="_blank">Firebase 官方文件步驟</a> ：</p>

<p>先安裝好 Firebase CLI 工具：</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl <span class="nt">-sL</span> https://firebase.tools | bash
</code></pre></div></div>

<p>執行：</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>firebase login:ci
</code></pre></div></div>

<p>完成登入、授權：</p>

<p><img src="/assets/4b001d2e8440/1*Jex5aYzRSs7Trfx6z_e1Aw.webp" alt="" loading="lazy" decoding="async" width="1063" height="701" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDYzIiBoZWlnaHQ9IjcwMSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/4b001d2e8440/1*S-1ckn9WC6j0DIqZGSdUUg.webp" alt="" loading="lazy" decoding="async" width="427" height="213" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0MjciIGhlaWdodD0iMjEzIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>回到 Terminal 複製 Firebase CLI Token:</p>

<p><img src="/assets/4b001d2e8440/1*sa_h8L08JbeC2nA0kACQtQ.webp" alt="" loading="lazy" decoding="async" width="682" height="483" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2ODIiIGhlaWdodD0iNDgzIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>到 Repo → Settings → Secrets and variables → Actions → 新增一個 Secret: <code class="language-plaintext highlighter-rouge">FIREBASE_CLI_TOKEN</code> 並貼上 Firebase CLI Token。</p>

<p><img src="/assets/4b001d2e8440/1*Lc6yGPu7L_0z6u1HH2PB5A.webp" alt="" loading="lazy" decoding="async" width="1171" height="515" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMTcxIiBoZWlnaHQ9IjUxNSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<blockquote>
  <p><strong><em>這個 Token = 你的登入身份</em></strong> <em>，請妥善保存，如果帳號離職也需要進行更換。</em></p>
</blockquote>

<h4 id="技術細節--install-the-apple-certificate-and-provisioning-profile">技術細節 — Install the Apple certificate and provisioning profile</h4>

<p>補充開發憑證匯入 Runner 的步驟細節。</p>

<p>因 GitHub Actions Secret 無法儲存檔案，因此所有憑證檔案都要先轉成 Base64 Encoded 文字格式存在 Secrets，GitHub Actions Step 中再動態讀出來寫入 TEMP 檔案並移動到正確位置給系統讀取使用。</p>

<p><strong>打包 Development 需要兩把金鑰憑證：</strong></p>
<ul>
  <li><a href="https://developer.apple.com/account/resources/certificates/list" target="_blank">Provision Profile (.mobileprovision)</a></li>
  <li><a href="https://developer.apple.com/account/resources/certificates/list" target="_blank">Development Certifcate (.p12)</a></li>
</ul>

<p><img src="/assets/4b001d2e8440/1*WXqqnErto3nn8rnNg6TXgw.webp" alt="cicd.mobileprovision" loading="lazy" decoding="async" width="1117" height="616" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMTE3IiBoZWlnaHQ9IjYxNiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>cicd.mobileprovision</p>

<p><img src="/assets/4b001d2e8440/1*RkeZ1PkXeY9Nt1kSRJKEQw.webp" alt="development.cer" loading="lazy" decoding="async" width="1078" height="738" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDc4IiBoZWlnaHQ9IjczOCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>development.cer</p>

<p>從 <a href="https://developer.apple.com/account/resources/certificates/list" target="_blank">Apple Developer</a> 中下載的 Certificate 是 .cer 格式，而我們需要的是 .p12 格式，可以先把下載下來的 .cer 點兩下安裝到 Keychain，然後打開 Keychain 選擇該憑證右鍵 Export 匯出。</p>

<p><img src="/assets/4b001d2e8440/1*HJMxwM3IDjxT-UGqnoUtWw.webp" alt="" loading="lazy" decoding="async" width="924" height="526" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MjQiIGhlaWdodD0iNTI2Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>檔案名稱：cicd.p12、格式 .p12</p>

<p>P12 金鑰密碼：輸入一組安全的自訂義字串 (範例是不好的示範，用 <code class="language-plaintext highlighter-rouge">123456</code> )</p>

<p><img src="/assets/4b001d2e8440/1*tyH0XqDVPGPWFJPL4dxksw.webp" alt="" loading="lazy" decoding="async" width="572" height="356" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1NzIiIGhlaWdodD0iMzU2Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><img src="/assets/4b001d2e8440/1*qKeZWel3w_5wW7meMtHmLA.webp" alt="" loading="lazy" decoding="async" width="556" height="381" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1NTYiIGhlaWdodD0iMzgxIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><strong>現在兩個檔案：</strong> cicd.p12、cicd.mobileprovision 都準備好了</p>

<p><strong>轉換成 BASE64 格式字串並存到 Repo Secrets：</strong></p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">base64</span> <span class="nt">-i</span> cicd.mobileprovision | pbcopy
</code></pre></div></div>

<p>到 Repo → Settings → Secrets and variables → Actions → 新增一個 Secret: <code class="language-plaintext highlighter-rouge">BUILD_PROVISION_PROFILE_BASE64</code> 並貼上以上內容。</p>

<p>-</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">base64</span> <span class="nt">-i</span> cicd.p12 | pbcopy
</code></pre></div></div>

<p>到 Repo → Settings → Secrets and variables → Actions → 新增一個 Secret: <code class="language-plaintext highlighter-rouge">BUILD_CERTIFICATE_BASE64</code> 並貼上以上內容。</p>

<p>-</p>

<p>到 Repo → Settings → Secrets and variables → Actions → 新增一個 Secret: <code class="language-plaintext highlighter-rouge">P12_PASSWORD</code> 內容是剛匯出 P12 金鑰設定的密碼。</p>

<p>-
到 Repo → Settings → Secrets and variables → Actions → 新增一個 Secret: <code class="language-plaintext highlighter-rouge">KEYCHAIN_PASSWORD</code> ：
如果是 GitHub Hosted Runner 則隨便輸入一個任意字串， <strong>如果是 Self-hosted Runner 則為 macOS Runner 使用者的登入密碼</strong> 。</p>

<p><img src="/assets/4b001d2e8440/1*JZCkFUJCQsggqYtW8acjTw.webp" alt="" loading="lazy" decoding="async" width="1088" height="493" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDg4IiBoZWlnaHQ9IjQ5MyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<h4 id="技術細節-app-store-connect-api-key">技術細節 —App Store Connect API Key</h4>

<p>Fastlane 打包部署到 App Store, Testflight <a href="https://docs.fastlane.tools/app-store-connect-api/" target="_blank">必須提供的 .json 金鑰</a> ，同樣受限 GitHub Actions Secrets 只能存字串不能存檔案，所以我們也要把金鑰內容轉成 Base64 字串，GitHub Actions Step 中再動態讀出來寫入 TEMP 檔案並把檔案路徑給 Fastlane 引用使用。</p>

<p><strong>首先到 <a href="/posts/zrealm-dev/app-store-connect-api-快速讀取與管理-customer-reviews-提升-ios-app-評價整合效率-f1365e51902c/">App Store Connect 建立&amp;下載好 App Store Connect API Key</a> (.p8) ：</strong></p>
<pre><code class="language-vbnet">-----BEGIN PRIVATE KEY-----
sss
axzzvcxz
zxzvzcxv
vzxcvzxvczxcvz
-----END PRIVATE KEY-----
</code></pre>

<p>新增一個 <code class="language-plaintext highlighter-rouge">app_store_connect_api.json</code> 檔案( <a href="https://docs.fastlane.tools/app-store-connect-api/" target="_blank">內容參考</a> )：</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">{</span>
  <span class="s2">"key_id"</span>: <span class="s2">"App Store Connect 上寫的 Key ID"</span>,
  <span class="s2">"issuer_id"</span>: <span class="s2">"App Store Connect 上寫的 Issuer ID"</span>,
  <span class="s2">"key"</span>: <span class="s2">"-----BEGIN PRIVATE KEY-----記得把換行改成</span><span class="se">\n</span><span class="s2">-----END PRIVATE KEY-----"</span>,
  <span class="s2">"duration"</span>: 1200, <span class="c"># optional (maximum 1200)</span>
  <span class="s2">"in_house"</span>: <span class="nb">false</span> <span class="c"># optional but may be required if using match/sigh</span>
<span class="o">}</span>
</code></pre></div></div>

<p>儲存檔案後執行：</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">base64</span> <span class="nt">-i</span> app_store_connect_api.json | pbcopy
</code></pre></div></div>

<p>將字串內容貼到 Repo → Settings → Secrets and variables → Actions → 新增一個 Secret: <code class="language-plaintext highlighter-rouge">APP_STORE_CONNECT_API_KEY_BASE64</code> 並貼上以上內容。</p>

<p><img src="/assets/4b001d2e8440/1*QxRuxEPEfWbJ383hhnxGkA.webp" alt="" loading="lazy" decoding="async" width="1159" height="549" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMTU5IiBoZWlnaHQ9IjU0OSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><code class="language-plaintext highlighter-rouge">Read and Write Apple Store Connect API Key to Temp</code> Step 完成之後在後續的 Step 只要傳入 env <code class="language-plaintext highlighter-rouge">APP_STORE_CONNECT_API_KEY_PATH</code> :</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Deploy</span>
  <span class="na">env</span><span class="pi">:</span>
    <span class="na">APP_STORE_CONNECT_API_KEY_PATH</span><span class="pi">:</span> <span class="s2">"</span><span class="s">${{</span><span class="nv"> </span><span class="s">runner.temp</span><span class="nv"> </span><span class="s">}}/${{</span><span class="nv"> </span><span class="s">env.APP_STORE_CONNECT_API_KEY_FILE_NAME</span><span class="nv"> </span><span class="s">}}"</span>
  <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
    <span class="s">....</span>
</code></pre></div></div>

<p>Fastlane 就能自動取得使用。</p>
<h4 id="技術延伸--reuse-action-workflow-拆分打包和部署動作">技術延伸 — Reuse Action Workflow 拆分打包和部署動作</h4>

<p>在這個案例中我們直接使用 Fastlane <code class="language-plaintext highlighter-rouge">beta</code> Lane 執行打包＋部署兩個動作。</p>

<p>在實際案例中我們可能需要將同一包打包結果分別部署到不同平台上(Firebase, Testflight…etc) 因此比較好的做法是打包是一個 Action、部署是一個 Action，不然會重復跑兩次打包；而且也更符合 CI/CD 的權責劃分。</p>

<blockquote>
  <p><strong><em>以下爲範例介紹：</em></strong></p>
</blockquote>

<p><strong>CI-Build.yml:</strong></p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">name</span><span class="pi">:</span> <span class="s">Build</span>

<span class="na">on</span><span class="pi">:</span>
  <span class="na">push</span><span class="pi">:</span>
    <span class="na">branches</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s">main</span>
  <span class="na">workflow_call</span><span class="pi">:</span>
     <span class="na">inputs</span><span class="pi">:</span>
        <span class="na">RELEASE_NOTE</span><span class="pi">:</span>
          <span class="na">description</span><span class="pi">:</span> <span class="s1">'</span><span class="s">Release</span><span class="nv"> </span><span class="s">notes</span><span class="nv"> </span><span class="s">of</span><span class="nv"> </span><span class="s">the</span><span class="nv"> </span><span class="s">deployment.'</span>
          <span class="na">required</span><span class="pi">:</span> <span class="kc">false</span>
          <span class="na">type</span><span class="pi">:</span> <span class="s">string</span>

<span class="na">jobs</span><span class="pi">:</span>
  <span class="na">build</span><span class="pi">:</span>
    <span class="na">runs-on</span><span class="pi">:</span> <span class="s">macos-latest</span>

    <span class="na">steps</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Checkout Code</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/checkout@v4</span>

      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Install Dependencies</span>
        <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
          <span class="s">make steup</span>
          <span class="s">make instal</span>

      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Build Project</span>
        <span class="na">run</span><span class="pi">:</span> <span class="s">bundle exec fastlane build</span>

      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Upload Build Artifact</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/upload-artifact@v4</span>
        <span class="na">with</span><span class="pi">:</span>
          <span class="na">name</span><span class="pi">:</span> <span class="s">build-artifact</span>
          <span class="na">path</span><span class="pi">:</span> <span class="s">./fastlane/build/</span>
</code></pre></div></div>

<p><strong>CD-Deploy-Firebase.yml:</strong></p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">name</span><span class="pi">:</span> <span class="s">Deploy Firebase</span>

<span class="na">on</span><span class="pi">:</span>
  <span class="c1"># 當 Build Action 完成時自動觸發執行</span>
  <span class="na">workflow_run</span><span class="pi">:</span>
    <span class="na">workflows</span><span class="pi">:</span> <span class="pi">[</span><span class="s2">"</span><span class="s">Build"</span><span class="pi">]</span>
    <span class="na">types</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s">completed</span>

<span class="na">jobs</span><span class="pi">:</span>
  <span class="na">deploy</span><span class="pi">:</span>
    <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
    <span class="c1"># 完成＋執行成功才執行部署</span>
    <span class="na">if</span><span class="pi">:</span> <span class="s">${{ github.event.workflow_run.conclusion == 'success' }}</span>

    <span class="na">steps</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Checkout Code</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/checkout@v4</span>

      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Install Dependencies</span>
        <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
          <span class="s">make steup</span>

      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Download Build Artifact</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/download-artifact@v4</span>
        <span class="na">with</span><span class="pi">:</span>
          <span class="na">name</span><span class="pi">:</span> <span class="s">build-artifact</span>
          <span class="na">path</span><span class="pi">:</span> <span class="s">./fastlane/build/</span>

      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Deploy to Production</span>
        <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
          <span class="s">bundle exec fastlane deploy-firebase</span>
</code></pre></div></div>

<p><strong>CD-Deploy-Testflight.yml:</strong></p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">name</span><span class="pi">:</span> <span class="s">Deploy Testflight</span>

<span class="na">on</span><span class="pi">:</span>
  <span class="c1"># 當 Build Action 完成時自動觸發執行</span>
  <span class="na">workflow_run</span><span class="pi">:</span>
    <span class="na">workflows</span><span class="pi">:</span> <span class="pi">[</span><span class="s2">"</span><span class="s">Build"</span><span class="pi">]</span>
    <span class="na">types</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s">completed</span>

<span class="na">jobs</span><span class="pi">:</span>
  <span class="na">deploy</span><span class="pi">:</span>
    <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
    <span class="c1"># 完成＋執行成功才執行部署</span>
    <span class="na">if</span><span class="pi">:</span> <span class="s">${{ github.event.workflow_run.conclusion == 'success' }}</span>

    <span class="na">steps</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Checkout Code</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/checkout@v4</span>

      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Install Dependencies</span>
        <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
          <span class="s">make steup</span>

      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Download Build Artifact</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/download-artifact@v4</span>
        <span class="na">with</span><span class="pi">:</span>
          <span class="na">name</span><span class="pi">:</span> <span class="s">build-artifact</span>
          <span class="na">path</span><span class="pi">:</span> <span class="s">./fastlane/build/</span>

      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Deploy to Production</span>
        <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
          <span class="s">bundle exec fastlane deploy-testflight</span>
</code></pre></div></div>

<p><strong>另外也可以用 <a href="https://docs.github.com/en/actions/how-tos/sharing-automations/reusing-workflows" target="_blank">Reusing Workflow</a> :</strong></p>

<p><strong>CD-Deploy-Firebase.yml:</strong></p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">name</span><span class="pi">:</span> <span class="s">Deploy Firebase</span>

<span class="na">on</span><span class="pi">:</span>
  <span class="c1"># 任意的觸發條件，這裡以手動表單觸發為例</span>
  <span class="na">workflow_dispatch</span><span class="pi">:</span>
    <span class="na">inputs</span><span class="pi">:</span>
      <span class="na">RELEASE_NOTE</span><span class="pi">:</span>
        <span class="na">description</span><span class="pi">:</span> <span class="s1">'</span><span class="s">Release</span><span class="nv"> </span><span class="s">notes</span><span class="nv"> </span><span class="s">of</span><span class="nv"> </span><span class="s">the</span><span class="nv"> </span><span class="s">deployment.'</span>
        <span class="na">required</span><span class="pi">:</span> <span class="kc">false</span>
        <span class="na">type</span><span class="pi">:</span> <span class="s">string</span>
<span class="na">jobs</span><span class="pi">:</span>
  <span class="na">build</span><span class="pi">:</span>
    <span class="na">needs</span><span class="pi">:</span> <span class="s">Build</span>
    <span class="na">uses</span><span class="pi">:</span> <span class="s">./.github/workflows/CD-Build.yml</span>
    <span class="na">secrets</span><span class="pi">:</span> <span class="s">inherit</span>
    <span class="na">with</span><span class="pi">:</span>
      <span class="na">RELEASE_NOTE</span><span class="pi">:</span> <span class="s">${{ inputs.RELEASE_NOTE }}</span>

  <span class="na">deploy</span><span class="pi">:</span>
    <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
    <span class="c1"># Job 預設是並發執行，用 needs 限制需等待 build 完成才執行</span>
    <span class="na">needs</span><span class="pi">:</span> <span class="pi">[</span><span class="nv">build</span><span class="pi">]</span>
    <span class="c1"># 執行成功，才部署</span>
    <span class="na">if</span><span class="pi">:</span> <span class="s">${{ always() &amp;&amp; needs.deploy.result == 'success' }}</span>
    <span class="na">steps</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Checkout Code</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/checkout@v4</span>

      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Install Dependencies</span>
        <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
          <span class="s">make steup</span>

      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Download Build Artifact</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/download-artifact@v4</span>
        <span class="na">with</span><span class="pi">:</span>
          <span class="na">name</span><span class="pi">:</span> <span class="s">build-artifact</span>
          <span class="na">path</span><span class="pi">:</span> <span class="s">./fastlane/build/</span>

      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Deploy to Production</span>
        <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
          <span class="s">bundle exec fastlane deploy-firebase</span>
</code></pre></div></div>
<h4 id="github-actions--artifact">GitHub Actions — Artifact</h4>

<p>同 Cache，目前 <strong>就算是 Self-hosted Runner Artifcact 功能依然會走 GitHub Cloud</strong> 受到使用量限制 ( <a href="https://docs.github.com/en/billing/managing-billing-for-your-products/about-billing-for-github-actions#included-storage-and-minutes" target="_blank">免費帳號 500MB 起</a> )。</p>

<blockquote>
  <p><em>Self-hosted Runner 要達到類似的效果可以自行建立共享主機目錄或找其他工具替代。</em></p>
</blockquote>

<p>因此目前 Artifact 實際我只用來存放小資料，例如 Snapshot Tests 的錯誤結果、測試報告…等等</p>
<h3 id="ci-nightly-build-執行快照單元測試打包cd-部署到-firebase-app-distribution">CI— Nightly Build 執行快照+單元測試+打包+CD 部署到 Firebase App Distribution</h3>
<h4 id="流程-2">流程</h4>

<p>每天凌晨 3 點自動針對 main(develop or master) 分支跑全部測試(unit+snapshot tests)，如果失敗則傳送失敗通知到 Slack 工作群組；如果成功則打包+部署一個版本到 Firebase App Disturbution，打包成功/失敗都會傳送 Slack 通知。</p>
<h4 id="ci-nightly-build-and-deployyml">CI-Nightly-Build-And-Deploy.yml</h4>

<p>Repo → Actions → New workflow → set up a workflow yourself。</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Workflow(Action) 名稱</span>
<span class="na">name</span><span class="pi">:</span> <span class="s">CI-Nightly Build And Deploy</span>

<span class="c1"># Actions Log 的標題名稱</span>
<span class="na">run-name</span><span class="pi">:</span> <span class="s2">"</span><span class="s">[CI-Nightly</span><span class="nv"> </span><span class="s">Build</span><span class="nv"> </span><span class="s">And</span><span class="nv"> </span><span class="s">Deploy]</span><span class="nv"> </span><span class="s">${{</span><span class="nv"> </span><span class="s">github.ref</span><span class="nv"> </span><span class="s">}}"</span>

<span class="c1"># 觸發事件</span>
<span class="na">on</span><span class="pi">:</span>
  <span class="c1"># 排程定時自動執行</span>
  <span class="c1"># https://crontab.guru/</span>
  <span class="c1"># UTC 時間</span>
  <span class="na">schedule</span><span class="pi">:</span>
    <span class="c1"># UTC 的 19:00 = 每天 UTC+8 的 03:00</span>
    <span class="pi">-</span> <span class="na">cron</span><span class="pi">:</span> <span class="s1">'</span><span class="s">0</span><span class="nv"> </span><span class="s">19</span><span class="nv"> </span><span class="s">*</span><span class="nv"> </span><span class="s">*</span><span class="nv"> </span><span class="s">*'</span>
  <span class="c1"># 手動觸發</span>
  <span class="na">workflow_dispatch</span><span class="pi">:</span>

<span class="c1"># Job 工作項目</span>
<span class="c1"># Job 會並發執行</span>
<span class="na">jobs</span><span class="pi">:</span>
  <span class="c1"># 測試工作</span>
  <span class="na">testing</span><span class="pi">:</span>
    <span class="c1"># Reuse Workflow (workflow_call)</span>
    <span class="na">uses</span><span class="pi">:</span> <span class="s">./.github/workflows/CI-Testing.yml</span>
    <span class="c1"># 傳遞所有 Secrets 給 CD-Testing.yml</span>
    <span class="na">secrets</span><span class="pi">:</span> <span class="s">inherit</span>
    <span class="na">with</span><span class="pi">:</span>
      <span class="c1"># 執行全部測試</span>
      <span class="na">TEST_LANE</span><span class="pi">:</span> <span class="s2">"</span><span class="s">run_all_tests"</span>
      <span class="c1"># 目標分支：main, develop or master...etc</span>
      <span class="na">BRANCH</span><span class="pi">:</span> <span class="s2">"</span><span class="s">main"</span>

  <span class="na">deploy-env</span><span class="pi">:</span>
    <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
    <span class="na">outputs</span><span class="pi">:</span>
      <span class="na">DATE_STRING</span><span class="pi">:</span> <span class="s">${{ steps.get_date.outputs.DATE_STRING }}</span>
    <span class="na">steps</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Get Date String</span>
        <span class="na">id</span><span class="pi">:</span> <span class="s">get_date</span>
        <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
          <span class="s">VERSION_DATE=$(date -u '+%Y%m%d')</span>
          <span class="s">echo "${VERSION_DATE}"</span>
          <span class="s">echo "DATE_STRING=${VERSION_DATE}" &gt;&gt; $GITHUB_ENV</span>
          <span class="s">echo "DATE_STRING=${VERSION_DATE}" &gt;&gt; $GITHUB_OUTPUT</span>
    
  <span class="na">deploy</span><span class="pi">:</span>
    <span class="c1"># Job 預設是並發執行，用 needs 限制需等待 testing 和 deploy-env 完成才執行</span>
    <span class="na">needs</span><span class="pi">:</span> <span class="pi">[</span><span class="nv">testing</span><span class="pi">,</span> <span class="nv">deploy-env</span><span class="pi">]</span>
    <span class="c1"># 如果測試成功才執行</span>
    <span class="na">if</span><span class="pi">:</span> <span class="s">${{ needs.testing.result == 'success' }}</span>
    <span class="c1"># Reuse Workflow (workflow_call)</span>
    <span class="na">uses</span><span class="pi">:</span> <span class="s">./.github/workflows/CD-Deploy.yml</span>
    <span class="c1"># 傳遞所有 Secrets 給 CD-Deploy.yml</span>
    <span class="na">secrets</span><span class="pi">:</span> <span class="s">inherit</span>
    <span class="na">with</span><span class="pi">:</span>
      <span class="na">VERSION_NUMBER</span><span class="pi">:</span> <span class="s">NightlyBuild-${{ needs.deploy-env.outputs.DATE_STRING }}</span>
      <span class="na">RELEASE_NOTE</span><span class="pi">:</span> <span class="s">NightlyBuild-${{ needs.deploy-env.outputs.DATE_STRING }}</span>
      <span class="c1"># 目標分支：main, develop or master...etc</span>
      <span class="na">BRANCH</span><span class="pi">:</span> <span class="s2">"</span><span class="s">main"</span>

<span class="c1"># ----- Slack Notify -----</span>
  <span class="na">testing-failed-slack-notify</span><span class="pi">:</span>
    <span class="na">needs</span><span class="pi">:</span> <span class="pi">[</span><span class="nv">testing</span><span class="pi">]</span>
    <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
    <span class="na">if</span><span class="pi">:</span> <span class="s">${{ needs.testing.result == 'failure' }}</span>
    <span class="na">steps</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Post text to a Slack channel</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">slackapi/slack-github-action@v2.1.0</span>
        <span class="na">with</span><span class="pi">:</span>
          <span class="na">method</span><span class="pi">:</span> <span class="s">chat.postMessage</span>
          <span class="na">token</span><span class="pi">:</span> <span class="s">${{ secrets.SLACK_BOT_TOKEN }}</span>
          <span class="na">payload</span><span class="pi">:</span> <span class="pi">|</span>
            <span class="s">channel: ${{ vars.SLACK_TEAM_CHANNEL_ID }}</span>
            <span class="s">text: ":x: Nightly Build - Testing 失敗\nWorkflow: &lt;${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View Run&gt;"</span>

  <span class="na">deploy-failed-slack-notify</span><span class="pi">:</span>
    <span class="na">needs</span><span class="pi">:</span> <span class="pi">[</span><span class="nv">deploy</span><span class="pi">]</span>
    <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
    <span class="na">if</span><span class="pi">:</span> <span class="s">${{ needs.deploy.result == 'failure' }}</span>
    <span class="na">steps</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Post text to a Slack channel</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">slackapi/slack-github-action@v2.1.0</span>
        <span class="na">with</span><span class="pi">:</span>
          <span class="na">method</span><span class="pi">:</span> <span class="s">chat.postMessage</span>
          <span class="na">token</span><span class="pi">:</span> <span class="s">${{ secrets.SLACK_BOT_TOKEN }}</span>
          <span class="na">payload</span><span class="pi">:</span> <span class="pi">|</span>
            <span class="s">channel: ${{ vars.SLACK_TEAM_CHANNEL_ID }}</span>
            <span class="s">text: ":x: Nightly Build Deploy 失敗\nWorkflow: &lt;${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View Run&gt;"</span>

  <span class="na">deploy-success-slack-notify</span><span class="pi">:</span>
    <span class="na">needs</span><span class="pi">:</span> <span class="pi">[</span><span class="nv">deploy</span><span class="pi">]</span>
    <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
    <span class="na">if</span><span class="pi">:</span> <span class="s">${{ needs.deploy.result == 'success' }}</span>
    <span class="na">steps</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Post text to a Slack channel</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">slackapi/slack-github-action@v2.1.0</span>
        <span class="na">with</span><span class="pi">:</span>
          <span class="na">method</span><span class="pi">:</span> <span class="s">chat.postMessage</span>
          <span class="na">token</span><span class="pi">:</span> <span class="s">${{ secrets.SLACK_BOT_TOKEN }}</span>
          <span class="na">payload</span><span class="pi">:</span> <span class="pi">|</span>
            <span class="s">channel: ${{ vars.SLACK_TEAM_CHANNEL_ID }}</span>
            <span class="s">text: ":white_check_mark: Nightly Build Deploy 成功\nWorkflow: &lt;${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View Run&gt;"</span>
</code></pre></div></div>

<p><strong>Commit 檔案到 Repo 主分支，手動觸發測試、打包功能看看結果：</strong></p>

<p><img src="/assets/4b001d2e8440/1*l71fL8oLoeAv-JX_FgkqzQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="564" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjU2NCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<blockquote>
  <p><em>日後會每日自動觸發。</em></p>
</blockquote>

<p><img src="/assets/4b001d2e8440/1*u6A77KwkXS2SY5-DPPPR9A.webp" alt="Demo" loading="lazy" decoding="async" width="1200" height="802" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjgwMiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><a href="https://github.com/ZhgChgLi/github-actions-ci-cd-demo/actions/runs/16119750747" target="_blank">Demo</a></p>

<p><img src="/assets/4b001d2e8440/1*6DQL_v4eahSqSuVJZRPrEA.webp" alt="" loading="lazy" decoding="async" width="310" height="92" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzMTAiIGhlaWdodD0iOTIiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>等待測試任務、打包部署任務、通知任務都完成後，查看結果。</p>

<p><img src="/assets/4b001d2e8440/1*4UVgyCQljqZQgzxHpXPB4g.webp" alt="" loading="lazy" decoding="async" width="903" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDMiIGhlaWdodD0iOTAwIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><img src="/assets/4b001d2e8440/1*t9PrQfcTANyvG7gfXXC-bw.webp" alt="" loading="lazy" decoding="async" width="554" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1NTQiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>我們就能直接在手機上安裝 Nightly Build 版本來進行搶先體驗測試。</p>
<h4 id="技術細節">技術細節</h4>

<p>這個 Action 我們直接復用前面設計的 CI-Testing 和 CD-Deploy，組合成我們的 Nightly Build，非常彈性好用！</p>

<p><strong>完整程式碼： <a href="https://github.com/ZhgChgLi/github-actions-ci-cd-demo/actions/workflows/CI-Nightly-Build-And-Deploy.yml" target="_blank">CI-Nightly-Build-And-Deploy.yml</a></strong></p>
<h3 id="self-hosted-runner-注意事項">Self-hosted Runner 注意事項</h3>

<p>本文是 Public Repo 因此直接使用 GitHub Hosted 的 macOS Runner，但在實際工作上我們的 Repo 一定是 Private，直接使用 GitHub Hosted Runner 超貴不划算(約一個月就能買一台 Mac Mini 放在公司吃到飽爽爽跑)，每一台依照效能可以起多個 Runner 同時並發接任務來做。</p>

<blockquote>
  <p><strong><em>細節可參考 <a href="/posts/zrealm-dev/github-actions-self-hosted-runner-詳解與實戰案例-ci-cd-自動化工作流程建置-404bd5c70040/">上一篇</a> 「 <a href="/posts/zrealm-dev/github-actions-self-hosted-runner-詳解與實戰案例-ci-cd-自動化工作流程建置-404bd5c70040/">Self-hosted Runner 建置與改用</a> 」部分</em></strong> <em>，在本地電腦安裝好 XCode 跟基本環境後註冊、啟用 Runner、Action Worflow YAML 中把 <code class="language-plaintext highlighter-rouge">runs-on</code> 改成 <code class="language-plaintext highlighter-rouge">[self-hosted]</code> 即可。</em></p>
</blockquote>

<p>多個 Runner 在同一台電腦的問題，我們多半已在上面的 Actions 裡解決了，例如把所有依賴的共用目錄都改成本地目錄，還有一個測試會遇到的問題要解決，就是搶模擬器問題：「 <strong>當兩個測試 Job 被兩個 Runner 在同一台機器上同時檢來做，如果指定同個模擬器就會互相干擾導致測試失敗。」</strong></p>

<p>解決方法也很容易，就是為個別 Runner 都設定一台模擬器。</p>
<h4 id="多個-runner-在同台機器的模擬器設定">多個 Runner 在同台機器的模擬器設定</h4>

<p>假設我 <strong>同一台電腦上有兩個 Runner</strong> 在並行接收任務：</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">ZhgChgLideMacBook-Pro-Runner-A</code></li>
  <li><code class="language-plaintext highlighter-rouge">ZhgChgLideMacBook-Pro-Runner-B</code></li>
</ul>

<p><img src="/assets/4b001d2e8440/1*3ptg9Tl5fBbEIYB4kgh0kw.webp" alt="" loading="lazy" decoding="async" width="819" height="333" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4MTkiIGhlaWdodD0iMzMzIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>在 XCode 模擬器設定我們就需要新增兩個模擬器：</p>

<p><img src="/assets/4b001d2e8440/1*k9bR2C12Wk11HAKKiYJ2zg.webp" alt="" loading="lazy" decoding="async" width="1037" height="695" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDM3IiBoZWlnaHQ9IjY5NSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/4b001d2e8440/1*iigArewZEW0063Q6xwZn7g.webp" alt="" loading="lazy" decoding="async" width="322" height="281" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzMjIiIGhlaWdodD0iMjgxIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<ul>
  <li>型號、iOS 版本同測試環境</li>
</ul>

<p><strong>CI-Testing.yml 中測試步驟改成：</strong></p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 執行 Fastlane Unit 測試 Lane</span>
      - name: Run Tests
        <span class="nb">id</span>: testing
        <span class="c"># 指定工作目錄，這樣後續指令就不用在特別 cd ./Product/</span>
        working-directory: ./Product/
        <span class="nb">env</span>:
          <span class="c"># ...</span>
          <span class="c"># Repo -&gt; Settings -&gt; Actions secrets and variables -&gt; variables</span>
          <span class="c"># 模擬器的 iOS 版本</span>
          SIMULATOR_IOS_VERSION: <span class="k">${</span><span class="p">{ vars.SIMULATOR_IOS_VERSION </span><span class="k">}</span><span class="o">}</span>

          <span class="c"># 當前 Runner 名稱</span>
          RUNNER_NAME: <span class="k">${</span><span class="p">{ runner.name </span><span class="k">}</span><span class="o">}</span>
          
          <span class="c"># ...</span>
        run: |

          <span class="c"># ...</span>
          bundle <span class="nb">exec </span>fastlane <span class="k">${</span><span class="nv">TEST_LANE</span><span class="k">}</span> device:<span class="s2">"</span><span class="k">${</span><span class="nv">RUNNER_NAME</span><span class="k">}</span><span class="s2"> (</span><span class="k">${</span><span class="nv">SIMULATOR_IOS_VERSION</span><span class="k">}</span><span class="s2">)"</span> | <span class="nb">tee</span> <span class="s2">"</span><span class="nv">$RUNNER_TEMP</span><span class="s2">/testing_output.txt"</span>
          <span class="c"># ...</span>
</code></pre></div></div>
<ul>
  <li><code class="language-plaintext highlighter-rouge">device</code> <strong>改成</strong> <code class="language-plaintext highlighter-rouge">${RUNNER_NAME} (${SIMULATOR_IOS_VERSION})</code></li>
  <li><code class="language-plaintext highlighter-rouge">SIMULATOR_IOS_VERSION</code> 還是統一看 Repo variables 變數</li>
</ul>

<p><strong>組合結果就會是(以 18.4 為例)：</strong></p>
<ul>
  <li>Runner: <code class="language-plaintext highlighter-rouge">ZhgChgLideMacBook-Pro-Runner-A</code> 
模擬器: <strong>ZhgChgLideMacBook-Pro-Runner-A(18.4)</strong></li>
  <li>Runner: <code class="language-plaintext highlighter-rouge">ZhgChgLideMacBook-Pro-Runner-B</code> 
模擬器: <strong>ZhgChgLideMacBook-Pro-Runner-B(18.4)</strong></li>
</ul>

<p>這樣兩個 Runner 同時在執行測試時就會起兩個模擬器自己跑自己的了。</p>
<h4 id="完整專案-repo">完整專案 Repo</h4>

<p><a href="https://github.com/ZhgChgLi/github-actions-ci-cd-demo" target="_blank"><img src="https://opengraph.githubassets.com/eb233d74ad1bc6b0eceb9494487579557fb7f17066a3981d20b52fde2c00ef45/ZhgChgLi/github-actions-ci-cd-demo" alt="" /></a></p>

<h3 id="補充-ssh-agent-設定--for-fastlane-match-or-private-cocoapods-repo">補充 SSH Agent 設定 — For Fastlane Match or Private CocoaPods Repo</h3>

<p>在使用 Fastlane Match 或 Private CocoaPods Repo，因為是在另一個 Private Repo 當中，當前 Repo/Action 環境無法直接 git clone，需要使用 ssh agent 設定好環境，在 Action 執行時才有權限操作。</p>
<h4 id="step-1-產生-ssh-key">Step 1. 產生 SSH Key</h4>

<p><img src="/assets/4b001d2e8440/1*kPWl9hombBgQ15i-n2sY2A.webp" alt="" loading="lazy" decoding="async" width="682" height="483" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2ODIiIGhlaWdodD0iNDgzIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ssh-keygen <span class="nt">-t</span> ed25519 <span class="nt">-C</span> <span class="s2">"zhgchgli@gmail.com"</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">Enter file in which to save the key (/Users/zhgchgli/.ssh/id_ed25519):</code> /Users/zhgchgli/Downloads/zhgchgli</p>
<ul>
  <li>輸入到下載路徑方便我們拷貝內容</li>
</ul>

<p><code class="language-plaintext highlighter-rouge">Enter passphrase for “/Users/zhgchgli/Downloads/zhgchgli” (empty for no passphrase):</code></p>
<ul>
  <li><strong>留空：</strong> CI/CD 使用，無法在 CLI 交互下輸入 Passphrase， <strong>因此請留空</strong></li>
  <li>產生完畢 (.pub/private_key)</li>
</ul>

<p><img src="/assets/4b001d2e8440/1*aF5P6EK9Hfv-CI3MRZ8MCA.webp" alt="" loading="lazy" decoding="async" width="220" height="51" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMjAiIGhlaWdodD0iNTEiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<h4 id="step-2-到-private-repo-設定-deploy-key">Step 2. 到 Private Repo 設定 Deploy Key</h4>

<p><img src="/assets/4b001d2e8440/1*3K6JvCHa23QfvWoNq2xdBQ.webp" alt="github-actions-ci-cd-demo-certificates Repo" loading="lazy" decoding="async" width="1172" height="781" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMTcyIiBoZWlnaHQ9Ijc4MSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>github-actions-ci-cd-demo-certificates Repo</p>

<p>Settings → Security → Deploy keys → Add deploy key。</p>
<ul>
  <li>Title: 輸入 Key 名稱</li>
  <li>Key: <code class="language-plaintext highlighter-rouge">貼上 .pub Key 內容</code></li>
</ul>

<p>完成。</p>

<p><img src="/assets/4b001d2e8440/1*Xo_g-AchEk0j4SGXOmBuxg.webp" alt="" loading="lazy" decoding="async" width="1168" height="814" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMTY4IiBoZWlnaHQ9IjgxNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<h4 id="step-3-回-action-的-repo-設定-ssh-private-key-to-secrets">Step 3. 回 Action 的 Repo 設定 SSH Private Key to Secrets</h4>

<p><img src="/assets/4b001d2e8440/1*Jm92pQHMOImQzZtmmvd1ng.webp" alt="github-actions-ci-cd-demo Repo" loading="lazy" decoding="async" width="1174" height="899" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMTc0IiBoZWlnaHQ9Ijg5OSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>github-actions-ci-cd-demo Repo</p>

<p>Settings → Secrets and variables → Actions → New repository secret。</p>
<ul>
  <li>Name: 輸入密鑰變數名稱 <code class="language-plaintext highlighter-rouge">SSH_PRIVATE_KEY</code></li>
  <li>Secret: <code class="language-plaintext highlighter-rouge">貼上 private_key 內容</code></li>
</ul>

<p>完成。</p>
<h4 id="step-4-ssh-agent-設定完成來驗證一下-git-clone-private-repo-的權限">Step 4. SSH Agent 設定完成，來驗證一下 Git Clone Private Repo 的權限</h4>

<p><a href="https://github.com/ZhgChgLi/github-actions-ci-cd-demo/actions/workflows/Demo-Git-Clone-Private-Repo.yml" target="_blank"><strong>Demo-Git-Clone-Private-Repo.yml</strong></a> <strong>:</strong></p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">name</span><span class="pi">:</span> <span class="s">Demo Git Clone Private Repo</span>

<span class="na">on</span><span class="pi">:</span>
  <span class="na">workflow_dispatch</span><span class="pi">:</span>

<span class="na">jobs</span><span class="pi">:</span>
  <span class="na">clone-private-repo</span><span class="pi">:</span>
    <span class="na">name</span><span class="pi">:</span> <span class="s">Git Clone Private Repo</span>
    <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
    <span class="na">timeout-minutes</span><span class="pi">:</span> <span class="m">30</span>

    <span class="na">steps</span><span class="pi">:</span>
      <span class="c1"># 🔐 啟用 SSH Agent 並加入私鑰</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Setup SSH Agent</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">webfactory/ssh-agent@v0.9.0</span>
        <span class="na">with</span><span class="pi">:</span>
          <span class="na">ssh-private-key</span><span class="pi">:</span> <span class="s">${{ secrets.SSH_PRIVATE_KEY }}</span>

      <span class="c1"># 🛡️ 將 github.com 加入 known_hosts 以避免 host verification 錯誤</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Add GitHub to known_hosts</span>
        <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
          <span class="s">mkdir -p ~/.ssh</span>
          <span class="s">ssh-keyscan github.com &gt;&gt; ~/.ssh/known_hosts</span>
      <span class="c1"># 📦 使用 SSH clone private repo 並驗證</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Clone and Verify Private Repo</span>
        <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
          <span class="s">git clone git@github.com:ZhgChgLi/github-actions-ci-cd-demo-certificates.git ./fakeMatch/</span>
          <span class="s">if [ -d "./fakeMatch/.git" ]; then</span>
            <span class="s">echo "✅ Repo cloned successfully into ./fakeMatch/"</span>
            <span class="s">cd ./fakeMatch</span>
            <span class="s">echo "📌 Current commit: $(git rev-parse --short HEAD)"</span>
          <span class="s">else</span>
            <span class="s">echo "❌ Clone failed. SSH Agent may not be configured properly."</span>
            <span class="s">exit 1</span>
          <span class="s">fi</span>
</code></pre></div></div>

<p>可以用以上 Action 驗證一下是否設定成功。</p>

<p><img src="/assets/4b001d2e8440/1*YLbr7l0FBSOYx_fQT-tiCA.webp" alt="" loading="lazy" decoding="async" width="1400" height="805" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjgwNSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>成功，後續 <code class="language-plaintext highlighter-rouge">fastlane match</code> or <code class="language-plaintext highlighter-rouge">pod install</code> private pods 應該就能正確執行。</p>
<h3 id="總結">總結</h3>

<p>這篇文章詳細紀錄了使用 GitHub Actions 開發完整的 iOS CI/CD 流程，下一篇將優化使用者端(工程師/PM/設計師)體驗， <strong>完善 Slack 通知及使用 Google Apps Script Web App 串接 GitHub Actions 打造免費易用的跨團隊打包平台工具。</strong></p>
<h3 id="系列文章">系列文章：</h3>
<ul>
  <li><a href="/posts/zrealm-dev/ci-cd-是什麼-打造高效穩定開發團隊的關鍵流程與工具選擇-c008a9e8ceca/"><strong>CI/CD 實戰指南（一）：CI/CD 是什麼？如何透過 CI/CD 打造穩定高效的開發團隊？工具選擇？</strong></a></li>
  <li><a href="/posts/zrealm-dev/github-actions-self-hosted-runner-詳解與實戰案例-ci-cd-自動化工作流程建置-404bd5c70040/"><strong>CI/CD 實戰指南（二）：GitHub Actions 與 self-hosted Runner 使用與建置大全</strong></a></li>
  <li><a href="/posts/zrealm-dev/github-actions-ios-ci-cd-完整實作自動化建置-測試與部署流程教學-4b001d2e8440/"><strong>CI/CD 實戰指南（三）：使用 GitHub Actions 實作 App 專案的 CI 與 CD 工作流程</strong></a></li>
  <li><a href="/posts/zrealm-dev/ci-cd-打包工具平台-使用-google-apps-script-web-app-串接-github-actions-提升跨團隊效率-4273e57e7148/"><strong>CI/CD 實戰指南（四）：使用 Google Apps Script Web App 串接 GitHub Actions 建置免費易用的打包工具平台</strong></a></li>
</ul>

<h4 id="-buy-me-a-beer-on-paypal"><a href="https://www.paypal.com/ncp/payment/CMALMPT8UUTY2" target="_blank">🍺 Buy me a beer on PayPal</a></h4>

<blockquote>
  <p><a href="https://www.paypal.com/ncp/payment/CMALMPT8UUTY2" target="_blank"><strong><em>本系列文章花費了大量的時間精力撰寫，如果內容對您有幫助、對您的團隊有實質提升工作效率與產品品質；歡迎請我喝杯咖啡，感謝支持！</em></strong></a></p>
</blockquote>

<p><img src="/assets/4b001d2e8440/1*QJj54G9gOjtQS-rbHVT1SQ.webp" alt="Buy me a coffee" loading="lazy" decoding="async" width="700" height="700" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3MDAiIGhlaWdodD0iNzAwIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><a href="https://www.paypal.com/ncp/payment/CMALMPT8UUTY2" target="_blank">🍺 Buy me a beer on PayPal</a></p>]]></content>
  </entry><entry>
    <title type="html">GitHub Actions｜Self-hosted Runner 詳解與實戰案例｜CI/CD 自動化工作流程建置</title>
    <link href="https://zhgchg.li/posts/zrealm-dev/github-actions-self-hosted-runner-%E8%A9%B3%E8%A7%A3%E8%88%87%E5%AF%A6%E6%88%B0%E6%A1%88%E4%BE%8B-ci-cd-%E8%87%AA%E5%8B%95%E5%8C%96%E5%B7%A5%E4%BD%9C%E6%B5%81%E7%A8%8B%E5%BB%BA%E7%BD%AE-404bd5c70040/" rel="alternate" type="text/html" title="GitHub Actions｜Self-hosted Runner 詳解與實戰案例｜CI/CD 自動化工作流程建置" />
    <published>2025-07-02T20:22:32+08:00</published>
    <updated>2025-07-12T22:49:01+08:00</updated>
    <id>https://zhgchg.li/posts/zrealm-dev/404bd5c70040</id><summary type="html">針對開發團隊自動化需求，解析 GitHub Actions 與 Self-hosted Runner 架構與設定，並透過三大實戰案例教你快速建立自動標記 PR、指派負責人及每日 PR 統計通知，提升 CI/CD 執行效率與成本優化。</summary><author>
      <name>ZhgChgLi</name>
    </author><category term="ZRealm Dev." /><category term="ios-app-development" /><category term="cicd" /><category term="github-actions" /><category term="github" /><category term="self-hosted" /><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://zhgchg.li/assets/404bd5c70040/1*_vGYh_XSI3ZDbdeT8xCihA.webp" /><content type="html" xml:base="https://zhgchg.li/posts/zrealm-dev/github-actions-self-hosted-runner-%E8%A9%B3%E8%A7%A3%E8%88%87%E5%AF%A6%E6%88%B0%E6%A1%88%E4%BE%8B-ci-cd-%E8%87%AA%E5%8B%95%E5%8C%96%E5%B7%A5%E4%BD%9C%E6%B5%81%E7%A8%8B%E5%BB%BA%E7%BD%AE-404bd5c70040/"><![CDATA[<h3 id="cicd-實戰指南二github-actions-與-self-hosted-runner-使用與建置大全">CI/CD 實戰指南（二）：GitHub Actions 與 Self-hosted Runner 使用與建置大全</h3>

<p>帶您從頭了解 GitHub Actions/Self-hosted Runner 運作方式與手把手使用教學。</p>

<p><img src="/assets/404bd5c70040/1*_vGYh_XSI3ZDbdeT8xCihA.webp" alt="Photo by Dan Taylor" loading="lazy" decoding="async" width="1400" height="933" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjkzMyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>Photo by <a href="https://unsplash.com/@theoneandonlydantaylor?utm_content=creditCopyText&amp;utm_medium=referral&amp;utm_source=unsplash" target="_blank">Dan Taylor</a></p>
<h4 id="前言">前言</h4>

<p>前篇「 <a href="/posts/zrealm-dev/ci-cd-是什麼-打造高效穩定開發團隊的關鍵流程與工具選擇-c008a9e8ceca/"><strong>CI/CD 實戰指南（一）：CI/CD 是什麼？如何透過 CI/CD 打造穩定高效的開發團隊？工具選擇？</strong></a> 」我們介紹了 CI/CD 是什麼？能帶來哪些效益與工具的選擇， <strong>這篇將著重在 GitHub Actions, Self-hosted Runner 的架構與使用介紹</strong> ，並手把手一起建立幾個有趣的自動化工作流程，帶您慢慢上手。</p>
<h3 id="github-actions-架構流程圖">GitHub Actions 架構流程圖</h3>

<p>在開始之前我們先來確定一下 GitHub Actions 的運作架構流程關係與職責。</p>

<p><img src="/assets/404bd5c70040/1*iacfyTX_b3YTSzMcn2ldjw.webp" alt="" loading="lazy" decoding="async" width="1400" height="937" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjkzNyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<h4 id="github-repo"><strong>GitHub Repo</strong></h4>
<ul>
  <li>在 GitHub Actions 的世界裡，所有 Actions (Workflow YAML 檔案) 都要存放在某個 Git Repo 之中 ( <code class="language-plaintext highlighter-rouge">REPO/.github/workflows/</code> )</li>
</ul>

<h4 id="github-repo-actions-secrets">GitHub Repo —Actions Secrets</h4>

<p>Repo → Settings → Secrets and variables → Actions → Secrets。</p>
<ul>
  <li>存放 Actions 步驟中會用到的 Secret Key, Token
e.g. Slack Bot Token、Apple Store Connect API .p8 Key</li>
  <li><strong>Secrets 內容無法在 Action Log 中查看，會自動用 **** 隱蔽</strong></li>
  <li>Secrets 內容無法查看、編輯，只能覆蓋</li>
  <li>Secrets <strong>目前只支援純文字內容，無法上傳檔案</strong> 
<strong>-</strong> 如果是二進制的金要請參考 <a href="https://docs.github.com/en/actions/how-tos/security-for-github-actions/security-guides/using-secrets-in-github-actions#storing-base64-binary-blobs-as-secrets" target="_blank">官方步驟使用 Base64 編碼轉換後儲存</a> 。</li>
  <li>iOS 開發憑證儲存方式可參考官方教學： <a href="https://docs.github.com/en/actions/how-tos/use-cases-and-examples/deploying/installing-an-apple-certificate-on-macos-runners-for-xcode-development" target="_blank">Installing an Apple certificate on macOS runners for Xcode development</a></li>
  <li>可以儲存組織層級的 Secrets，跨 Repo 共享</li>
</ul>

<h4 id="github-repo--actions-variables">GitHub Repo — Actions Variables</h4>

<p>Repo → Settings → Secrets and variables → Actions → Variables。</p>
<ul>
  <li>存放 Actions 步驟中常用到的變數
e.g. 模擬器 iOS 版本、工作目錄</li>
  <li>Variables 內容可以查看、編輯</li>
  <li>Variables 內容可以輸出在 Action Log 中</li>
  <li>Variables 只支援純文字，也可存放 json 字串然後自己解析使用</li>
  <li>可以儲存組織層級的 Variables，跨 Repo 共享</li>
</ul>

<h4 id="github-actions--trigger">GitHub Actions — Trigger</h4>
<ul>
  <li><strong>Github Action 中最重要的起始點 — 觸發事件(條件)</strong></li>
  <li>符合觸發事件的 GitHub Actions 才會觸發執行</li>
  <li>完整事件列表可 <a href="https://docs.github.com/en/actions/reference/events-that-trigger-workflows" target="_blank">參考官方文件</a></li>
  <li>基本上涵蓋了所有 CI/CD、自動化會遇到的事件場景。
但 <strong>如果有特殊場景沒有事件，那就只能用其他事件+在 Job 中判斷組合或是用 Schedule 排程手動檢查了</strong> 。
e.g. 例如沒有 PR Merged 事件，就只能用 <code class="language-plaintext highlighter-rouge">pull_request: closed</code> + Job <code class="language-plaintext highlighter-rouge">if: github.event.pull_request.merged == true</code> 達成</li>
</ul>

<p><strong>常用事件：</strong></p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">schedule</code> (cron)：排程定時執行(同 crontab)
可以用來做自動化：定時檢查 PR、定時打包、定時執行自動化腳本
<strong>統一都在 main / develop (Default Branch) 執行。</strong></li>
  <li><code class="language-plaintext highlighter-rouge">pull_request:</code> ：PR 相關事件
當 PR 開啟時、PR Assign 時、加 Label 時、有新 Push Commit 時…等等</li>
  <li><code class="language-plaintext highlighter-rouge">issues</code> 、 <code class="language-plaintext highlighter-rouge">issue_comment</code> ：Issue 相關事件
當 Issue 開啟時、有新留言時…等等</li>
  <li><code class="language-plaintext highlighter-rouge">workflow_dispatch</code> ：手動觸發；可以設定需要提供的欄位，GitHub Actions 提供簡易的表單讓使用者可以填寫資訊。
e.g.:</li>
</ul>

<p><img src="/assets/404bd5c70040/1*XogIJsCbrNPerWBto_PG8w.webp" alt="" loading="lazy" decoding="async" width="332" height="432" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzMzIiIGhlaWdodD0iNDMyIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">workflow_call</code> ：觸發另一個 Action(Workflow) 執行任務。</li>
  <li><code class="language-plaintext highlighter-rouge">workflow_run</code> ：當別的 Action(Workflow) 執行任務，觸發執行此任務。</li>
</ul>

<blockquote>
  <p><em>更多事件類型、設定細節請 <a href="https://docs.github.com/en/actions/reference/events-that-trigger-workflows" target="_blank">參考官方文件</a> 。</em></p>
</blockquote>

<h4 id="github-actions--workflow"><strong>GitHub Actions — Workflow</strong></h4>
<ul>
  <li>a.k.a Action</li>
  <li>使用 YAML 撰寫 .yaml 檔案，檔案統一放置在 <code class="language-plaintext highlighter-rouge">REPO/.github/workflows/</code> 之下</li>
  <li><strong>以主分支內的 Workflow YAML 檔案為主</strong> 
如果在其他分支看不到正在開發的 Action 或執行上有誤，可以先 Merge 回主分支看看。</li>
  <li>GitHub Actions 中的最基礎單位，每個 Workflow 就代表一項 CI/CD 或自動化操作</li>
  <li>Workflow 可以呼叫別的 Workflow 執行任務
(可以利用這個特性拆出核心 Workflow 和呼叫的 Workflow)</li>
  <li>當中會定義任務名稱、執行策略、觸發事件、任務工作…等等所有 Action 相關設定</li>
  <li>目前檔案結構不支援子目錄</li>
  <li>Action 完全免費 (Public and Private Repo)</li>
</ul>

<h4 id="github-actions--workflow--job">GitHub Actions — Workflow — Job</h4>
<ul>
  <li>GitHub Actions 中的執行單位</li>
  <li>定義 Workflow 中的任務工作有哪些</li>
  <li>每個 Workflow 可以有多個 Jobs</li>
  <li>每個 Job 需要指定使用哪個 Runner Label，執行的時候會使用對應的 Runner 機器來執行任務</li>
  <li><strong>多個 Jobs 是並發執行</strong> (如有順序可以用 <code class="language-plaintext highlighter-rouge">needs</code> 約束)</li>
  <li><strong>每個 Job 應該視為獨立執行個體(每個都要當成是 Sandbox)</strong> ，Job 結束後如果有產出資源檔案要給後續其他 Job/Workflow 使用，需要 Upload Artifacts 或在 self-hosted 移動到共用產出目錄。</li>
  <li>Job 做完可以 Output 字串給其他 Job 參考使用。
(例如執行結果 true or false)</li>
  <li>沒特別設流程條件的話，多個 Job 如果其中有 Job 發生錯誤， <strong>其他 Job 依然會繼續執行</strong></li>
</ul>

<h4 id="github-actions--reuse-workflowjob">GitHub Actions — Reuse Workflow/Job</h4>
<ul>
  <li>Workflow 定義 <code class="language-plaintext highlighter-rouge">on: workflow_call</code> 就能封裝給其他 Workflow 當成 Job 複用</li>
  <li>同個組織下可以跨 Repo 分享使用</li>
  <li>因此可以把跨 Repo 共用的 CI/CD 工作放到共享的 Repo 一起使用</li>
</ul>

<h4 id="github-actions--workflow--job--step">GitHub Actions — Workflow — Job — Step</h4>
<ul>
  <li>GitHub Actions 中的最小執行項目</li>
  <li>Job 中實際執行任務的程式</li>
  <li>每個 Job 可以有多個 Steps</li>
  <li><strong>多個 Steps 是照順序執行</strong></li>
  <li>Step 做完可以 Output 字串給後續 Steps 參考使用。</li>
  <li><strong>Step 可以直接撰寫 shell script 程式</strong> 
可以引用 <a href="https://cli.github.com/manual/gh" target="_blank">gh cli</a> 、當前環境變數(例如取得 PR 編號)，直接做想做的事</li>
  <li>沒特別設流程條件的話，Step 如果發生錯誤， <strong>會直接中斷</strong> ，後續的 Steps 不會執行</li>
</ul>

<h4 id="github-actions--workflow--job--reuse-action-step">GitHub Actions — Workflow — Job — Reuse Action Step</h4>
<ul>
  <li><strong>可以直接復用 <a href="https://github.com/marketplace?type=actions" target="_blank">Marketplace</a> 上各路大神包好的現有工作步驟。</strong> 
例如： <a href="https://github.com/marketplace/actions/comment-pull-request" target="_blank">Comment 內容到 PR</a> 。</li>
  <li>也可以將自己一系列的工作任務 Step 打包成一個 Action GitHub Repo 讓其他工作直接復用</li>
  <li>Public Repo 的 Action 可以上架到 Marketplace</li>
</ul>

<p><strong>打包 Action 支援使用：</strong></p>
<ul>
  <li><strong>Docker Action</strong> — 代表 GitHub Actions 會把環境變數傳到 Docker 容器中，再看你要怎麼處理，可以是 shell script、Java、PHP…etc.</li>
  <li><strong>JavaScript/TypeScript Action</strong> — 直接使用 node.js 撰寫 GitHub Actions 處理邏輯，同樣的會把環境變數都傳給你參考使用。
e.g. <a href="https://github.com/pozil/auto-assign-issue/blob/v2/action.yml" target="_blank">pozil/auto-assign-issue</a></li>
  <li><strong>Composite (YAML) —</strong> 純 YAML 描述任務步驟 (同 GitHub Actions — Workflow — Job — Step) 可以宣告有哪寫步驟要做或直接在上面寫 shell script。
e.g. <a href="https://github.com/ZhgChgLi/ZReviewTender/blob/main/action.yml" target="_blank">ZhgChgLi/ZReviewTender</a></li>
</ul>

<blockquote>
  <p><em>礙於篇幅，本篇文章不會介紹如何打包 Github Actions Action，有興趣可以參考官方文件： <a href="https://docs.github.com/en/actions/tutorials/creating-a-composite-action" target="_blank">tutorials/creating-a-composite-action</a> 。</em></p>
</blockquote>

<h4 id="github-runner">GitHub Runner</h4>
<ul>
  <li>GitHub 會根據 Runner Label 派發對應的 Job 給 Runner 執行</li>
  <li>Runner 只做為監聽者，輪詢監聽 GitHub 派發任務</li>
  <li>只關心 Job 不關心是哪個 Action(Workflow)
因此會出現 Action A 的 Job-1 執行完，下一個換 Action B 的 Job-1，而不是 Action A 的 Jobs 都執行完才換 Action B。</li>
  <li>Runner 可以使用 GitHub Hosted Runner 或 Self-hosted Runner。</li>
</ul>

<h4 id="github-hosted-runner"><strong>GitHub Hosted Runner</strong></h4>
<ul>
  <li>GitHub 提供的 Runner，可參考官方 Repo 列表：</li>
</ul>

<p><a href="https://github.com/actions/runner-images" target="_blank"><img src="https://opengraph.githubassets.com/752d8c5522e9674fddc4c85962169e07ac91e262e2e3bd299cccec91a8f20b26/actions/runner-images" alt="" /></a></p>

<p><img src="/assets/404bd5c70040/1*KtQV4kDCWscEeaZ8jQI8Dg.webp" alt="2025/06 的 Images 列表" loading="lazy" decoding="async" width="1200" height="1154" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjExNTQiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><a href="https://github.com/actions/runner-images" target="_blank">2025/06 的 Images 列表</a></p>
<ul>
  <li>Runner 預先安裝了什麼可以點進去查看：
e.g. <a href="https://github.com/actions/runner-images/blob/main/images/macos/macos-14-arm64-Readme.md" target="_blank">macos-14-arm64</a></li>
</ul>

<p><img src="/assets/404bd5c70040/1*svXtXH78-TvK1C_XXCyYLA.webp" alt="macos-14-arm64" loading="lazy" decoding="async" width="1200" height="669" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjY2OSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><a href="https://github.com/actions/runner-images/blob/main/images/macos/macos-14-arm64-Readme.md" target="_blank">macos-14-arm64</a></p>
<ul>
  <li>iOS 開發優先使用 -arm64 (M系列) 處理器的 Runner，跑起來比較快</li>
  <li>只要在 Job <code class="language-plaintext highlighter-rouge">run-on</code> 貼上表格上的 YAML Label，就能使用該 Runner 執行任務</li>
  <li><strong>Public Repo 收費方式: 完全免費無限使用</strong></li>
  <li><strong>️</strong> Private Repo 免費額度:
免費額度(依照帳號不同額度不同，以 GitHub Free 為例)：
用量：每月免費 2,000 分鐘
儲存：500 MB</li>
  <li><strong>⚠️️Private Repo 計費方式:</strong> 
<strong>超過免費額度之後開始用用量計費(可設上限跟通知)，依照 Runner 所屬的機器作業系統、核心不同，價格也不同：</strong></li>
</ul>

<p><img src="/assets/404bd5c70040/1*rkhRJN4ZRas_lDOJh-pjkQ.webp" alt="about-billing-for-github-actions" loading="lazy" decoding="async" width="1064" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDY0IiBoZWlnaHQ9IjEyMDAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><a href="https://docs.github.com/en/billing/managing-billing-for-your-products/about-billing-for-github-actions" target="_blank">about-billing-for-github-actions</a></p>

<p>可以看到 macOS 的價格因為設備成本很高所以貴。</p>
<ul>
  <li>最多並發任務數限制：</li>
</ul>

<p><img src="/assets/404bd5c70040/1*LU3zGSBe57NBVMfDwl0wdQ.webp" alt="usage-limits-billing-and-administration" loading="lazy" decoding="async" width="1200" height="490" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjQ5MCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><a href="https://docs.github.com/en/actions/concepts/overview/usage-limits-billing-and-administration#usage-limits" target="_blank">usage-limits-billing-and-administration</a></p>

<blockquote>
  <p><em>這邊扯太多了，我們的重點是 Self-hosted Runner。</em></p>
</blockquote>

<h4 id="self-hosted-runner-on-in-house-server">Self-hosted Runner on In-house Server</h4>
<ul>
  <li>將自己的機器作為 Runner</li>
  <li><strong>一台實體機器可以起多個 Runner 並發接任務來做</strong></li>
  <li><strong>免費無限量無限制使用</strong> 
只有機器購買成本，花一次使用到飽！
以 32G RAM M4 Mini (=NT$40,900) 計算，如果用 GitHub Hosted Runner 一個月要花 500 USD； <strong>買一台架設好用超過三個月就回本</strong> 了！</li>
  <li>支援 Windows, macOS, Linux (x64/ARM/ARM64)</li>
  <li><strong>同個組織可以跨 Repo 共享 Runner</strong></li>
  <li><strong>⚠️目前：actions/cache, actions/upload-artifact, actions/download-artifact 都只支援 GitHub 雲端服務，代表這些內容還是會上傳到 GitHub 伺服器並計算儲存量收費。</strong> 
可以在自己的機器上開共用目錄取代。</li>
  <li>Self-hosted Runner 也支援 <a href="https://docs.github.com/en/actions/concepts/runners/about-actions-runner-controller" target="_blank">Docker, k8s</a> 只是我沒研究。</li>
</ul>

<blockquote>
  <p><strong><em>架設 Self-hosted Runner 只需幾步 (10 分鐘內設定好) 就能上線開始接任務執行(本文稍後會介紹)。</em></strong></p>
</blockquote>

<h4 id="github-workflow-x-job-x-step-x-runner-流程關係圖">GitHub Workflow x Job x Step x Runner 流程關係圖</h4>

<p><img src="/assets/404bd5c70040/1*WHIVfXdVRoEsdXGfJhu2Rw.webp" alt="" loading="lazy" decoding="async" width="1400" height="852" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9Ijg1MiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>這邊用一張圖總結工作流程與關係，假設我們有兩個 Workflow、兩個 Runner:</p>
<ul>
  <li>CI —假設 R有 3 個 Jobs，每個 Job 各有幾個 Steps，Runner Label(run-on) — <code class="language-plaintext highlighter-rouge">self-hosted-app</code></li>
  <li>CD — 有 4 個 Jobs，每個 Job 各有幾個 Steps，Runner Label(run-on) — <code class="language-plaintext highlighter-rouge">self-hosted-app</code></li>
  <li>Runner — 有 2 個，Runner Label 也都是— <code class="language-plaintext highlighter-rouge">self-hosted-app</code></li>
</ul>

<p>同前述，Runner 跟 Workflow 是依照 Runnler Label 來派發、接收任務來做，這個案例都是用同個 <code class="language-plaintext highlighter-rouge">self-hosted-app</code> Runner Label，Workflow 的 Jobs 預設是並發執行、Steps 是每個 Job 的實際執行內容，串行照順序執行，Steps 都執行完等於 Job 執行完畢。</p>

<blockquote>
  <p><strong><em>因此 Workflow 實際執行的時間軸會是在兩個 Runner 之間穿梭執行 Job，也會並發執行、當前 Job 執行完下一個也不一定是接著同個 Worflow 的下一個 Job。</em></strong></p>
</blockquote>

<blockquote>
  <p><em>⚠️ 所以才說確保每個 Job 是獨立的很重要 (尤其在 Self-hosted Runner 環境之下，Job 結束之後可能不會清除的太乾淨，我們可能會下意識覺得這個 Job 的產出，下一個 Job 可以接著做事)，不應該這樣用。</em></p>
</blockquote>

<h4 id="如果-job-有產出要給後續的-job-使用">如果 Job 有產出要給後續的 Job 使用:</h4>
<ul>
  <li><strong>Job Output String</strong> : 輸出純文字到變數給其他 Job 引用</li>
  <li><strong>Artifact-upload/download:</strong> Job 的 Step 最後一步將執行結果上傳到 GitHub Artifact，另一個 Job 的第一個 Step 下載回來繼續處理使用。
e.g. 例如 Job — 打包 → 將打包結果 .ipa 傳到 <strong>Artifact →</strong> Job — 部署 → 下載回來 .ipa → 上傳 App Store 部署
要注意： <strong>目前就算是 Self-hosted 也是會上 GitHub Artifact 雲儲存。</strong></li>
  <li><strong>AWS/GCP…雲端儲存</strong> : 同上，只是是用自己的雲端儲存服務。</li>
  <li><strong>[Self-hosted Only] 共用硬碟、目錄:</strong> Self-hosted Runner 如果都有掛載一個共用目錄，則可用這個目錄依照 UUID 建立資料夾存放產出結果，然後後續 Job 去讀取前一個 Job Output UUID 去找到對應的儲存再拿回來用。
要注意，不同主機的 Runner 也都要掛載同個共用目錄。</li>
  <li>在同個 Job 的 Steps 把所有事情做完。</li>
</ul>

<h3 id="做中學-github-actions--案例實作">做中學 GitHub Actions — 案例實作</h3>

<p>「坐而言，不如起而行」以上名詞解釋跟流程架構介紹相信大家也是看得懵懵懂懂，接下來會直接舉三個功能例子，帶大家實際動手做，並一邊解釋碰到的東西，從做當中學習，以了解 GitHub Actions 到底是什麼。</p>
<h3 id="案例--1">案例 — 1</h3>

<p>建立 Pull Request 後自動標記 File Changes Size Label 讓 Reviewer 方便安排 Review 工作。</p>
<h4 id="成果圖">成果圖</h4>

<p><img src="/assets/404bd5c70040/1*vjSWeu2zB-hmVpfziMDR5Q.webp" alt="Demo PR" loading="lazy" decoding="async" width="774" height="206" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NzQiIGhlaWdodD0iMjA2Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><a href="https://github.com/ZhgChgLi/github-actions-ci-cd-demo/pull/11" target="_blank">Demo PR</a></p>
<h4 id="運作流程">運作流程</h4>
<ul>
  <li>使用者開 PR、重開 PR、Push 新 Commit 到 PR</li>
  <li>觸發 GitHub Actions Workflow</li>
  <li>shell script 取的 file changes 數量</li>
  <li>判斷數量對 PR 標記上 Label</li>
  <li>完成</li>
</ul>

<h4 id="動手做">動手做</h4>

<p>Repo → Actions → New workflow → set up a workflow yourself。</p>

<p><strong>檔案名稱：</strong> <code class="language-plaintext highlighter-rouge">Automation-PullRequest.yml</code></p>

<p>Action Workflow 可以每個任務獨立一個檔案，也可以依照觸發事件、目的，同個目的聚合在同個檔案，反正多個 Job 是並發執行的，另外 <strong>因為 GitHub Actions 暫時不支援目錄結構，所以檔案少一點、使用階層命名檔案會比較好管理</strong> 。</p>

<p>這邊把 PR 相關事件的 Actions 都放在同個 Workflow。</p>
<h4 id="automation-pullrequestyml"><code class="language-plaintext highlighter-rouge">Automation-PullRequest.yml</code></h4>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Workflow(Action) 名稱</span>
<span class="na">name</span><span class="pi">:</span> <span class="s">Pull Reqeust Automation</span>

<span class="c1"># Actions Log 的標題名稱</span>
<span class="na">run-name</span><span class="pi">:</span> <span class="s2">"</span><span class="s">[Pull</span><span class="nv"> </span><span class="s">Reqeust</span><span class="nv"> </span><span class="s">Automation]</span><span class="nv"> </span><span class="s">${{</span><span class="nv"> </span><span class="s">github.event.pull_request.title</span><span class="nv"> </span><span class="s">||</span><span class="nv"> </span><span class="s">github.ref</span><span class="nv"> </span><span class="s">}}"</span>

<span class="c1"># 觸發事件</span>
<span class="na">on</span><span class="pi">:</span>
  <span class="c1"># PR 事件</span>
  <span class="na">pull_request</span><span class="pi">:</span>
    <span class="c1"># PR - 開啟、重開、有新 Push Commit 時</span>
    <span class="na">types</span><span class="pi">:</span> <span class="pi">[</span><span class="nv">opened</span><span class="pi">,</span> <span class="nv">synchronize</span><span class="pi">,</span> <span class="nv">reopened</span><span class="pi">]</span>


<span class="c1"># 同個 Concurrency Group 如果有新的 Job 會取消正在跑的</span>
<span class="c1"># 例如 Push Commit 觸發的任務還沒執行就又 Push Commit 時，會取消前一個任務</span>
<span class="na">concurrency</span><span class="pi">:</span>
  <span class="na">group</span><span class="pi">:</span> <span class="s">${{ github.workflow }}-${{ github.ref_name }}</span>
  <span class="na">cancel-in-progress</span><span class="pi">:</span> <span class="kc">true</span>

<span class="c1"># Job 工作項目</span>
<span class="c1"># Job 會並發執行</span>
<span class="na">jobs</span><span class="pi">:</span>
  <span class="c1"># Job ID</span>
  <span class="na">label-pr-by-file-count</span><span class="pi">:</span>
    <span class="c1"># Job 名稱 (可省略，有設定在 Log 顯示比較好讀)</span>
    <span class="na">name</span><span class="pi">:</span> <span class="s">Label PR by changes file count</span>

    <span class="c1"># 如果這個 Job 失敗，不影響整個 Workflow，繼續其他 Job</span>
    <span class="na">continue-on-error</span><span class="pi">:</span> <span class="kc">true</span>
    
    <span class="c1"># 設定最長 Timeout 時間，防止異常情況發生時無止盡的等待</span>
    <span class="na">timeout-minutes</span><span class="pi">:</span> <span class="m">10</span>

    <span class="c1"># Runner Label - 使用 GitHub Hosted Runner ubuntu-latest 來執行工作</span>
    <span class="c1"># 如果是 Private Repo 會計算用量，超過可能會產生費用</span>
    <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>

    <span class="c1"># 工作步驟</span>
    <span class="c1"># 工作步驟會照順序執行</span>
    <span class="na">steps</span><span class="pi">:</span>
      <span class="c1"># 步驟名稱</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Get changed file count and apply label</span>
        <span class="c1"># 步驟 ID (可省略，後續若沒有 Step 要引用 Output 輸出則不需設定)</span>
        <span class="na">id</span><span class="pi">:</span> <span class="s">get-changed-files-count-by-gh</span>
        <span class="c1"># 注入外部環境參數到執行階段</span>
        <span class="na">env</span><span class="pi">:</span>
          <span class="c1"># secrets.GITHUB_TOKEN 是 GitHub Actions 執行時自動產生的 Token，不需自行在 Secrets 設定，擁有一些 GitHub Repo API Scopes 權限</span>
          <span class="c1"># https://docs.github.com/en/actions/how-tos/security-for-github-actions/security-guides/use-github_token-in-workflows</span>
          <span class="c1"># gh(GitHub) cli 需要注入 GH_TOKEN 到 ENV，gh 才有權限操作  </span>
          <span class="na">GH_TOKEN</span><span class="pi">:</span> <span class="s">${{ secrets.GITHUB_TOKEN }}</span>
        <span class="c1"># Shell script</span>
        <span class="c1"># GitHub Hosted Runner 內建都有安裝好 gh cli，不需要安裝 Job 就能直接使用</span>
        <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
          <span class="s">#   ${{ github.xxx }} 是 GitHub Actions Context 表達式</span>
          <span class="s">#   不是 Shell 變數，而是 YAML 解析階段由 GitHub Actions 替換成對應值</span>
          <span class="s">#   其他參數：https://docs.github.com/en/actions/learn-github-actions/contexts#github-context</span>
          
          <span class="s"># 取得 PR 編號:</span>
          <span class="s">PR_NUMBER=${{ github.event.pull_request.number }}</span>

          <span class="s"># 取得 Repo:</span>
          <span class="s">REPO=${{ github.repository }}</span>

          <span class="s"># 使用 GitHub API (gh cli) 取得 File changed 數量</span>
          <span class="s">FILE_COUNT=$(gh pr view $PR_NUMBER --repo $REPO --json files --jq '.files | length')</span>
          
          <span class="s"># Print Log</span>
          <span class="s">echo "Changed file count: $FILE_COUNT"</span>

          <span class="s"># Label 邏輯</span>
          <span class="s">if [ "$FILE_COUNT" -lt 5 ]; then</span>
            <span class="s">LABEL="XS"</span>
          <span class="s">elif [ "$FILE_COUNT" -lt 10 ]; then</span>
            <span class="s">LABEL="S"</span>
          <span class="s">elif [ "$FILE_COUNT" -lt 30 ]; then</span>
            <span class="s">LABEL="M"</span>
          <span class="s">elif [ "$FILE_COUNT" -lt 80 ]; then</span>
            <span class="s">LABEL="L"</span>
          <span class="s">elif [ "$FILE_COUNT" -lt 200 ]; then</span>
            <span class="s">LABEL="XL"</span>
          <span class="s">else</span>
            <span class="s">LABEL="XXL"</span>
          <span class="s">fi</span>

          <span class="s"># 使用 GitHub API (gh cli) 移除目前的 Size Label</span>
          <span class="s">EXISTING_LABELS=$(gh pr view "$PR_NUMBER" --repo "$REPO" --json labels --jq '.labels[].name')</span>
          <span class="s">for EXISTING in $EXISTING_LABELS; do</span>
            <span class="s">case "$EXISTING" in</span>
              <span class="s">XS|S|M|L|XL|XXL)</span>
                <span class="s">echo "🧹 Removing existing label: $EXISTING"</span>
                <span class="s">gh pr edit "$PR_NUMBER" --repo "$REPO" --remove-label "$EXISTING"</span>
                <span class="s">;;</span>
            <span class="s">esac</span>
          <span class="s">done</span>

          <span class="s"># (可選)如果 Label 不存在則建立</span>
          <span class="s">if ! gh label list --repo "$REPO" | grep -q "^$LABEL"; then</span>
            <span class="s">echo "🆕 Creating missing label: $LABEL"</span>
            <span class="s">gh label create "$LABEL" --repo "$REPO" --description "Size label: $LABEL" --color "ededed"</span>
          <span class="s">else</span>
            <span class="s">echo "✅ Label '$LABEL' already exists"</span>
          <span class="s">fi</span>
          
          <span class="s"># 使用 GitHub API (gh cli) 標記上 Label</span>
          <span class="s">gh pr edit $PR_NUMBER --repo $REPO --add-label "$LABEL"</span>
</code></pre></div></div>

<p>Commit 檔案到 Repo 主分支之後，我們再開新 PR 就會自動觸發 GitHub Actions：</p>

<p><img src="/assets/404bd5c70040/1*wdAztL0BgPeSZdqXxqUEXg.webp" alt="" loading="lazy" decoding="async" width="938" height="744" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MzgiIGhlaWdodD0iNzQ0Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>Action 執行狀態顯示 <strong>Queued</strong> 代表任務正在等待 Runner 接任務回去做。</p>
<h4 id="執行結果">執行結果</h4>

<p><img src="/assets/404bd5c70040/1*-JoD8IQYHVrDqHmLmrXyaQ.webp" alt="Demo PR" loading="lazy" decoding="async" width="1338" height="948" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMzM4IiBoZWlnaHQ9Ijk0OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><a href="https://github.com/ZhgChgLi/github-actions-ci-cd-demo/pull/11" target="_blank">Demo PR</a></p>

<p>執行完畢並且成功後 PR 上就會自動標記好對應的 Label 了！紀錄會顯示由 <code class="language-plaintext highlighter-rouge">github-actions</code> 標記。</p>

<p><strong>完整程式碼： <a href="https://github.com/ZhgChgLi/github-actions-ci-cd-demo/blob/main/.github/workflows/Automation-PullRequest.yml" target="_blank">Automation-PullRequest.yml</a></strong></p>
<h4 id="直接使用別人包好的-action-步驟-pascalgnsize-label-action">直接使用別人包好的 Action 步驟： <a href="https://github.com/pascalgn/size-label-action/tree/main" target="_blank">pascalgn/size-label-action</a></h4>

<p>前面有說到可以直接使用別人封裝好的 Action，標記 PR Size Label 這任務已經有現成的輪子可以使用，上述只是為了教學目的，實際上不需要自己重造輪子。</p>

<p>只需要在 Action Workflow Job Step 中直接使用就能完成任務：</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Workflow(Action) 名稱</span>
<span class="na">name</span><span class="pi">:</span> <span class="s">Pull Reqeust Automation</span>

<span class="c1"># 觸發事件</span>
<span class="na">on</span><span class="pi">:</span>
  <span class="c1"># PR 事件</span>
  <span class="na">pull_request</span><span class="pi">:</span>
    <span class="c1"># PR - 開啟、重開、有新 Push Commit 時</span>
    <span class="na">types</span><span class="pi">:</span> <span class="pi">[</span><span class="nv">opened</span><span class="pi">,</span> <span class="nv">synchronize</span><span class="pi">,</span> <span class="nv">reopened</span><span class="pi">]</span>


<span class="c1"># 同個 Concurrency Group 如果有新的 Job 會取消正在跑的</span>
<span class="c1"># 例如 Push Commit 觸發的任務還沒執行就又 Push Commit 時，會取消前一個任務</span>
<span class="na">concurrency</span><span class="pi">:</span>
  <span class="na">group</span><span class="pi">:</span> <span class="s">${{ github.workflow }}-${{ github.ref_name }}</span>
  <span class="na">cancel-in-progress</span><span class="pi">:</span> <span class="kc">true</span>

<span class="c1"># Job 工作項目</span>
<span class="c1"># Job 會並發執行</span>
<span class="na">jobs</span><span class="pi">:</span>
  <span class="c1"># Job ID</span>
  <span class="na">label-pr-by-file-count</span><span class="pi">:</span>
    <span class="c1"># Job 名稱 (可省略，有設定在 Log 顯示比較好讀)</span>
    <span class="na">name</span><span class="pi">:</span> <span class="s">Label PR by changes file count</span>

    <span class="c1"># 如果這個 Job 失敗，不影響整個 Workflow，繼續其他 Job</span>
    <span class="na">continue-on-error</span><span class="pi">:</span> <span class="kc">true</span>
    
    <span class="c1"># 設定最長 Timeout 時間，防止異常情況發生時無止盡的等待</span>
    <span class="na">timeout-minutes</span><span class="pi">:</span> <span class="m">10</span>

    <span class="c1"># Runner Label - 使用 GitHub Hosted Runner ubuntu-latest 來執行工作</span>
    <span class="c1"># 如果是 Private Repo 會計算用量，超過可能會產生費用</span>
    <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>

    <span class="c1"># 工作步驟</span>
    <span class="c1"># 工作步驟會照順序執行</span>
    <span class="na">steps</span><span class="pi">:</span>
      <span class="c1"># 步驟名稱</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Get changed file count and apply label</span>
        <span class="c1"># 步驟 ID (可省略，後續若沒有 Step 要引用 Output 輸出則不需設定)</span>
        <span class="na">id</span><span class="pi">:</span> <span class="s">get-changed-files-count-by-gh</span>
        <span class="c1"># 直接使用別人封裝好的程式</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s2">"</span><span class="s">pascalgn/size-label-action@v0.5.5"</span>
        <span class="c1"># 注入外部環境參數到執行階段</span>
        <span class="c1"># 參數命名、可用參數要參考說明：https://github.com/pascalgn/size-label-action/tree/main</span>
        <span class="na">env</span><span class="pi">:</span>
          <span class="c1"># secrets.GITHUB_TOKEN 是 GitHub Actions 執行時自動產生的 Token (github-actions 身份)，不需自行在 Secrets 設定，擁有一些 GitHub Repo API Scopes 權限</span>
          <span class="c1"># https://docs.github.com/en/actions/how-tos/security-for-github-actions/security-guides/use-github_token-in-workflows</span>
          <span class="na">GITHUB_TOKEN</span><span class="pi">:</span> <span class="s2">"</span><span class="s">${{</span><span class="nv"> </span><span class="s">secrets.GITHUB_TOKEN</span><span class="nv"> </span><span class="s">}}"</span>
</code></pre></div></div>

<p><a href="https://github.com/pascalgn/size-label-action/tree/main" target="_blank"><img src="https://opengraph.githubassets.com/746d87e558bda612364f12277484f99cae4c57fbb2eb910bfd59d6865c1b6e94/pascalgn/size-label-action" alt="" /></a></p>

<p>這個包好的 Action 是 JavaScript Action，實際執行程式碼可以參考以下檔案： <a href="https://github.com/pascalgn/size-label-action/blob/main/dist/index.js" target="_blank">dist/index.js</a> 。</p>
<h4 id="name-run-name-的補充">name, run-name 的補充：</h4>

<p><img src="/assets/404bd5c70040/1*zBmjm_3AU53NN0UhTrmOBQ.webp" alt="" loading="lazy" decoding="async" width="1012" height="569" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDEyIiBoZWlnaHQ9IjU2OSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<ul>
  <li>name: Action Workflow 的名稱</li>
  <li>run-name: 執行紀錄的標題名稱 (可帶入 PR Title or Branch or Author…etc)
如果是 on:pull_request 事件預設是 PR Title。</li>
</ul>

<h3 id="案例--2">案例 — 2</h3>

<p>建立 Pull Request 後如果沒有 Assignee 則自動 Assign 作者自己並且 Comment 提示。(只有在初次建立時才會執行)</p>
<h4 id="成果圖-1">成果圖</h4>

<p><img src="/assets/404bd5c70040/1*EL-0nQF7jhP34d6ZoSkJAg.webp" alt="Demo PR" loading="lazy" decoding="async" width="1200" height="682" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjY4MiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><a href="https://github.com/ZhgChgLi/github-actions-ci-cd-demo/pull/11" target="_blank">Demo PR</a></p>
<h4 id="運作流程-1">運作流程</h4>
<ul>
  <li>使用者開 PR</li>
  <li>觸發 GitHub Actions Workflow</li>
  <li>github script 取得 assignee</li>
  <li>如果沒有 asignee 則 assign 開 PR 的作者 &amp; Comment 訊息</li>
  <li>完成</li>
</ul>

<h4 id="動手做-1">動手做</h4>

<p>Repo → Actions → New workflow → set up a workflow yourself。</p>

<p><strong>檔案名稱：</strong> <code class="language-plaintext highlighter-rouge">Automation-PullRequest.yml</code> (同上)</p>
<h4 id="automation-pullrequestyml-1"><code class="language-plaintext highlighter-rouge">Automation-PullRequest.yml</code></h4>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Workflow(Action) 名稱</span>
<span class="na">name</span><span class="pi">:</span> <span class="s">Pull Reqeust Automation</span>

<span class="c1"># Actions Log 的標題名稱</span>
<span class="na">run-name</span><span class="pi">:</span> <span class="s2">"</span><span class="s">Pull</span><span class="nv"> </span><span class="s">Reqeust</span><span class="nv"> </span><span class="s">Automation</span><span class="nv"> </span><span class="s">-</span><span class="nv"> </span><span class="s">Daily</span><span class="nv"> </span><span class="s">Checker"</span>

<span class="c1"># 觸發事件</span>
<span class="na">on</span><span class="pi">:</span>
  <span class="c1"># PR 事件</span>
  <span class="na">pull_request</span><span class="pi">:</span>
    <span class="c1"># PR - 開啟、重開、有新 Push Commit 時</span>
    <span class="na">types</span><span class="pi">:</span> <span class="pi">[</span><span class="nv">opened</span><span class="pi">,</span> <span class="nv">synchronize</span><span class="pi">,</span> <span class="nv">reopened</span><span class="pi">]</span>


<span class="c1"># 同個 Concurrency Group 如果有新的 Job 會取消正在跑的</span>
<span class="c1"># 例如 Push Commit 觸發的任務還沒執行就又 Push Commit 時，會取消前一個任務</span>
<span class="na">concurrency</span><span class="pi">:</span>
  <span class="na">group</span><span class="pi">:</span> <span class="s">${{ github.workflow }}-${{ github.ref_name }}</span>
  <span class="na">cancel-in-progress</span><span class="pi">:</span> <span class="kc">true</span>

<span class="c1"># Job 工作項目</span>
<span class="c1"># Job 會並發執行</span>
<span class="na">jobs</span><span class="pi">:</span>
  <span class="c1"># Job ID</span>
  <span class="na">label-pr-by-file-count</span><span class="pi">:</span>
    <span class="c1"># 請參考前文，略....</span>
  <span class="c1"># ---------</span>
  <span class="na">assign-self-if-no-assignee</span><span class="pi">:</span>
    <span class="na">name</span><span class="pi">:</span> <span class="s">Automatically assign to self if no assignee is specified</span>
    <span class="c1"># 因為是共用觸發事件，所以在 Job 上自己判斷，當是 Pull Request Opened(首次建立) 時才執行 Job 否則會 Skipped</span>
    <span class="na">if</span><span class="pi">:</span> <span class="s">github.event_name == 'pull_request' &amp;&amp; github.event.action == 'opened'</span>

    <span class="c1"># 如果這個 Job 失敗，不影響整個 Workflow，繼續其他 Job</span>
    <span class="na">continue-on-error</span><span class="pi">:</span> <span class="kc">true</span>
    
    <span class="c1"># 設定最長 Timeout 時間，防止異常情況發生時無止盡的等待</span>
    <span class="na">timeout-minutes</span><span class="pi">:</span> <span class="m">10</span>
    
    <span class="c1"># Runner Label - 使用 GitHub Hosted Runner ubuntu-latest 來執行工作</span>
    <span class="c1"># 如果是 Private Repo 會計算用量，超過可能會產生費用</span>
    <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>

    <span class="na">steps</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Assign self if No Assignee</span>
        <span class="c1"># 使用 GitHub Script (JavaScript) 撰寫腳本 (Node.js 環境)</span>
        <span class="c1"># 相較上面直接用 Shell Script 寫起來更方便漂亮</span>
        <span class="c1"># 也不需要自行注入環境變數、GITHUB_TOKEN</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/github-script@v7</span>
        <span class="na">with</span><span class="pi">:</span>
          <span class="na">script</span><span class="pi">:</span> <span class="pi">|</span>
            <span class="s">// github-script 中會自動注入到 context 變數供 javascript 直接引用</span>
            <span class="s">// https://docs.github.com/en/actions/learn-github-actions/contexts#github-context</span>

            <span class="s">const issue = context.payload.pull_request; // 如果要連 Issue 一起支援可寫成 context.payload.issue || context.payload.pull_request</span>
            <span class="s">const assignees = issue.assignees || [];</span>
            <span class="s">const me = context.actor;</span>

            <span class="s">if (assignees.length === 0) {</span>
              <span class="s">// Assignee 設成自己</span>
              <span class="s">await github.rest.issues.addAssignees({</span>
                <span class="s">owner: context.repo.owner,</span>
                <span class="s">repo: context.repo.repo,</span>
                <span class="s">issue_number: issue.number,</span>
                <span class="s">assignees: [me]</span>
              <span class="s">});</span>

              <span class="s">// 留言通知</span>
              <span class="s">await github.rest.issues.createComment({</span>
                <span class="s">owner: context.repo.owner,</span>
                <span class="s">repo: context.repo.repo,</span>
                <span class="s">issue_number: issue.number,</span>
                <span class="s">body: `🔧 No assignee was set, so I have assigned this to myself (@${me}).`</span>
              <span class="s">});</span>
            <span class="s">}</span>
</code></pre></div></div>

<p>這次我們示範改用 GitHub Script (JavaScript) 撰寫腳本，程式碼語法上更彈性更好撰寫。</p>

<p>當然如果你想照我之前說的每個任務一個檔案，就可以拔掉 Job If.. 直接在 Action Workflow 觸發條件設定：</p>

<p><code class="language-plaintext highlighter-rouge">Automation-PullRequest-Auto-Assign.yml</code> <strong>：</strong></p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Workflow(Action) 名稱</span>
<span class="na">name</span><span class="pi">:</span> <span class="s">Pull Reqeust Automation - Auto Assignee Self</span>

<span class="c1"># 觸發事件</span>
<span class="na">on</span><span class="pi">:</span>
  <span class="c1"># PR 事件</span>
  <span class="na">pull_request</span><span class="pi">:</span>
    <span class="c1"># PR - 開啟時</span>
    <span class="na">types</span><span class="pi">:</span> <span class="pi">[</span><span class="nv">opened</span><span class="pi">]</span>

<span class="na">jobs</span><span class="pi">:</span>
  <span class="na">assign-self-if-no-assignee</span><span class="pi">:</span>
    <span class="na">name</span><span class="pi">:</span> <span class="s">Automatically assign to self if no assignee is specified</span>
    <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
    <span class="na">steps</span><span class="pi">:</span>
      <span class="c1"># 請參考前文，略....</span>
</code></pre></div></div>

<p>Commit 檔案到 Repo 主分支之後，我們再開新 PR 就會自動觸發 GitHub Actions：</p>

<p><img src="/assets/404bd5c70040/1*Y-A6owUibNEFBoRFHnqIYg.webp" alt="" loading="lazy" decoding="async" width="1271" height="708" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjcxIiBoZWlnaHQ9IjcwOCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>現在有兩個 Job 要執行了！</p>
<h4 id="執行結果-1">執行結果</h4>

<p><img src="/assets/404bd5c70040/1*23muCVgUJKmZt746khBMFQ.webp" alt="Demo PR" loading="lazy" decoding="async" width="1200" height="979" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijk3OSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><a href="https://github.com/ZhgChgLi/github-actions-ci-cd-demo/pull/11" target="_blank">Demo PR</a></p>

<p>執行完畢並且成功後 PR 如果沒有 Asignees 會自動 Assign PR 作者並且 Comment 訊息。(都是用 <code class="language-plaintext highlighter-rouge">github-actions</code> 身份操作)</p>

<p><strong>完整程式碼： <a href="https://github.com/ZhgChgLi/github-actions-ci-cd-demo/blob/main/.github/workflows/Automation-PullRequest.yml" target="_blank">Automation-PullRequest.yml</a></strong></p>
<h4 id="測試重開reopened-pr">測試重開(Reopened) PR</h4>

<p><img src="/assets/404bd5c70040/1*mcDa7TZjv6mO7HtwhOE-Vw.webp" alt="" loading="lazy" decoding="async" width="960" height="407" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NjAiIGhlaWdodD0iNDA3Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>可看到只會執行 Size Label Job，Auto Assignee Job 被 Skipped 了。</p>

<blockquote>
  <p><em>這個任務也有人包好 Action 可以直接復用，可參考： <a href="https://github.com/pozil/auto-assign-issue" target="_blank">pozil/auto-assign-issue</a> 。</em></p>
</blockquote>

<h3 id="案例--3">案例 — 3</h3>

<p>每日早上 9 點自動統計當前 PR 數量及已開啟多久時間發送通知訊息到 Slack 工作尋組、自動關閉已開啟超過 3 個月的 PR。</p>
<h4 id="成果圖-2">成果圖</h4>

<p><img src="/assets/404bd5c70040/1*0stX9KpZi6PcXpG-90wyIg.webp" alt="" loading="lazy" decoding="async" width="508" height="147" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1MDgiIGhlaWdodD0iMTQ3Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><img src="/assets/404bd5c70040/1*Nbg3r1zzhx24YIBEjPJnaw.webp" alt="" loading="lazy" decoding="async" width="928" height="187" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MjgiIGhlaWdodD0iMTg3Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<ul>
  <li>Slack 工作群組每天早上自動收到報告</li>
  <li>自動關閉超過 90 天的 PR</li>
</ul>

<h4 id="運作流程-2">運作流程</h4>
<ul>
  <li>GitHub Actions 每天早上 9 點自動觸發</li>
  <li>觸發 GitHub Actions Workflow</li>
  <li>github script 取得 開啟中的 PR 列表、統計開啟了幾天</li>
  <li>傳送統計報告訊息到 Slack</li>
  <li>關閉超過 90 天的 PR</li>
  <li>完成</li>
</ul>

<h4 id="動手做-2">動手做</h4>

<p>Repo → Actions → New workflow → set up a workflow yourself。</p>

<p><strong>檔案名稱：</strong> <code class="language-plaintext highlighter-rouge">Automation-PullRequest-Daily.yml</code></p>
<h4 id="automation-pullrequest-dailyyml">Automation-PullRequest-Daily.yml</h4>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Workflow(Action) 名稱</span>
<span class="na">name</span><span class="pi">:</span> <span class="s">Pull Reqeust Automation - Daily Checker</span>

<span class="c1"># 觸發事件</span>
<span class="na">on</span><span class="pi">:</span>
  <span class="c1"># 排程定時自動執行</span>
  <span class="c1"># https://crontab.guru/</span>
  <span class="c1"># UTC 時間</span>
  <span class="na">schedule</span><span class="pi">:</span>
    <span class="c1"># UTC 的 01:00 = 每天 UTC+8 的 09:00</span>
    <span class="pi">-</span> <span class="na">cron</span><span class="pi">:</span> <span class="s1">'</span><span class="s">0</span><span class="nv"> </span><span class="s">1</span><span class="nv"> </span><span class="s">*</span><span class="nv"> </span><span class="s">*</span><span class="nv"> </span><span class="s">*'</span>
  <span class="c1"># 手動觸發</span>
  <span class="na">workflow_dispatch</span><span class="pi">:</span>

<span class="c1"># Job 工作項目</span>
<span class="c1"># Job 會並發執行</span>
<span class="na">jobs</span><span class="pi">:</span>
  <span class="c1"># Job ID</span>
  <span class="na">caculate-pr-status</span><span class="pi">:</span>
    <span class="c1"># Job 名稱 (可省略，有設定在 Log 顯示比較好讀)</span>
    <span class="na">name</span><span class="pi">:</span> <span class="s">Caculate PR Status</span>
    <span class="c1"># Runner Label - 使用 GitHub Hosted Runner ubuntu-latest 來執行工作</span>
    <span class="c1"># 如果是 Private Repo 會計算用量，超過可能會產生費用</span>
    <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>

    <span class="c1"># Job Output</span>
    <span class="na">outputs</span><span class="pi">:</span>
      <span class="na">pr_list</span><span class="pi">:</span> <span class="s">${{ steps.pr-info.outputs.pr_list }}</span>

    <span class="c1"># 工作步驟</span>
    <span class="c1"># 工作步驟會照順序執行</span>
    <span class="na">steps</span><span class="pi">:</span>
      <span class="c1"># 步驟名稱</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Fetch open PRs and caculate</span>
        <span class="c1"># Step 外部要引用 Output 輸出，需設定</span>
        <span class="na">id</span><span class="pi">:</span> <span class="s">pr-info</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/github-script@v7</span>
        <span class="na">with</span><span class="pi">:</span>
          <span class="na">script</span><span class="pi">:</span> <span class="pi">|</span>
            <span class="s">const now = new Date();</span>
            <span class="s">const per_page = 100;</span>
            <span class="s">let page = 1;</span>
            <span class="s">let allPRs = [];</span>
      
            <span class="s">while (true) {</span>
              <span class="s">const { data: prs } = await github.rest.pulls.list({</span>
                <span class="s">owner: context.repo.owner,</span>
                <span class="s">repo: context.repo.repo,</span>
                <span class="s">state: 'open',</span>
                <span class="s">per_page,</span>
                <span class="s">page,</span>
              <span class="s">});</span>
              <span class="s">if (prs.length === 0) break;</span>
              <span class="s">allPRs = allPRs.concat(prs);</span>
              <span class="s">if (prs.length &lt; per_page) break;</span>
              <span class="s">page++;</span>
            <span class="s">}</span>
      
            <span class="s">const result = allPRs.map(pr =&gt; {</span>
              <span class="s">const created = new Date(pr.created_at);</span>
              <span class="s">const daysOpen = Math.floor((now - created) / (1000 * 60 * 60 * 24));</span>
              <span class="s">return {</span>
                <span class="s">pr: pr.number.toString(),</span>
                <span class="s">title: pr.title,</span>
                <span class="s">idle: daysOpen</span>
              <span class="s">};</span>
            <span class="s">});</span>

            <span class="s">// 設定回 Output，只接受 String</span>
            <span class="s">core.setOutput('pr_list', JSON.stringify(result));</span>
  <span class="c1"># ----</span>
  <span class="na">send-pr-summary-message-to-slack</span><span class="pi">:</span>
    <span class="na">name</span><span class="pi">:</span> <span class="s">Send PR Summary Messag to Slack</span>
    <span class="c1"># Job 預設是並發，使用 needs 可以迫使當前 Job 等到 need Job 完成時才會執行</span>
    <span class="na">needs</span><span class="pi">:</span> <span class="pi">[</span><span class="nv">caculate-pr-status</span><span class="pi">]</span>
    <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
    
    <span class="na">steps</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Generate Message</span>
        <span class="c1"># Step 外部要引用 Output 輸出，需設定</span>
        <span class="na">id</span><span class="pi">:</span> <span class="s">gen-msg</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/github-script@v7</span>
        <span class="na">with</span><span class="pi">:</span>
          <span class="na">script</span><span class="pi">:</span> <span class="pi">|</span>
            <span class="s">const prList = JSON.parse(`${{ needs.caculate-pr-status.outputs.pr_list }}`);</span>
            <span class="s">const blocks = [];</span>
      
            <span class="s">// 標題</span>
            <span class="s">blocks.push({</span>
              <span class="s">type: "section",</span>
              <span class="s">text: {</span>
                <span class="s">type: "mrkdwn",</span>
                <span class="s">text: `📬 *Open PR Report*\nTotal: *${prList.length}* PR(s)`</span>
              <span class="s">}</span>
            <span class="s">});</span>
      
            <span class="s">// 每個 PR 一行</span>
            <span class="s">for (const pr of prList) {</span>
              <span class="s">blocks.push({</span>
                <span class="s">type: "section",</span>
                <span class="s">text: {</span>
                  <span class="s">type: "mrkdwn",</span>
                  <span class="s">text: `• &lt;https://github.com/${context.repo.owner}/${context.repo.repo}/pull/${pr.pr}|PR #${pr.pr}&gt; *${pr.title}* - 🕒 ${pr.idle} day(s)`</span>
                <span class="s">}</span>
              <span class="s">});</span>
            <span class="s">}</span>

            <span class="s">// 設定回 Output，只接受 String</span>
            <span class="s">core.setOutput('blocks', JSON.stringify(blocks));</span>

            
      <span class="c1"># 使用 Slack 官方封裝好的 Slack API Github Actions</span>
      <span class="c1"># https://tools.slack.dev/slack-github-action/sending-techniques/sending-data-slack-api-method/</span>
      <span class="c1"># 發送訊息</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Post text to a Slack channel</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">slackapi/slack-github-action@v2.1.0</span>
        <span class="na">with</span><span class="pi">:</span>
          <span class="na">method</span><span class="pi">:</span> <span class="s">chat.postMessage</span>
          <span class="na">token</span><span class="pi">:</span> <span class="s">${{ secrets.SLACK_BOT_TOKEN }}</span>
          <span class="na">payload</span><span class="pi">:</span> <span class="pi">|</span>
            <span class="s">channel: ${{ vars.SLACK_TEAM_CHANNEL_ID }}</span>
            <span class="s">blocks: ${{ steps.gen-msg.outputs.blocks }}</span>
  <span class="c1"># ----</span>
  <span class="na">auto-close-old-prs</span><span class="pi">:</span>
    <span class="na">name</span><span class="pi">:</span> <span class="s">Auto Close Old PRs</span>
    <span class="na">needs</span><span class="pi">:</span> <span class="pi">[</span><span class="nv">caculate-pr-status</span><span class="pi">]</span>
    <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>

    <span class="na">steps</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Auto close PRs opened more than 90 days</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/github-script@v7</span>
        <span class="na">with</span><span class="pi">:</span>
          <span class="na">script</span><span class="pi">:</span> <span class="pi">|</span>
            <span class="s">const prList = JSON.parse(`${{ needs.caculate-pr-status.outputs.pr_list }}`);</span>
            <span class="s">const oldPRs = prList.filter(pr =&gt; pr.idle &gt; 90);</span>

            <span class="s">for (const pr of oldPRs) {</span>
              <span class="s">await github.rest.pulls.update({</span>
                <span class="s">owner: context.repo.owner,</span>
                <span class="s">repo: context.repo.repo,</span>
                <span class="s">pull_number: parseInt(pr.pr),</span>
                <span class="s">state: 'closed'</span>
              <span class="s">});</span>

              <span class="s">await github.rest.issues.createComment({</span>
                <span class="s">owner: context.repo.owner,</span>
                <span class="s">repo: context.repo.repo,</span>
                <span class="s">issue_number: parseInt(pr.pr),</span>
                <span class="s">body: `⚠️ This pull request has been automatically closed because it has been open for more than 90 days. Please reopen if needed.`</span>
              <span class="s">});</span>
            <span class="s">}</span>
            <span class="s">console.log(`Closed ${oldPRs.length} PR(s)`);</span>
</code></pre></div></div>

<p>在這個範例中我們使用：</p>
<ul>
  <li>on: schedule Crontab 排程自動觸發跟 workflow_dispatch 支援手動觸發</li>
  <li>Job output/Step output (都只能是字串)</li>
  <li>多個 Jobs 預設是並發但可以用 <code class="language-plaintext highlighter-rouge">needs</code> 時間依賴等待關係</li>
  <li>從 Repo Secrets/Variables 取得設定</li>
  <li>串接 Slack API</li>
</ul>

<p><strong>Repo Secrets — 新增</strong> <code class="language-plaintext highlighter-rouge">SLACK_BOT_TOKEN</code></p>

<p><img src="/assets/404bd5c70040/1*YC4DBHtKmX5XGgcSSzaE5A.webp" alt="" loading="lazy" decoding="async" width="1200" height="970" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijk3MCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<ul>
  <li>Slack App 建立、發訊息權限設定可參考我 <a href="/posts/zrealm-robotic-process-automation/slack-app-整合-chatgpt-利用-openai-api-與-google-cloud-functions-自建智能助理-bd94cc88f9c9/">之前的文章</a></li>
</ul>

<p><strong>Repo Variables — 新增</strong> <code class="language-plaintext highlighter-rouge">SLACK_TEAM_CHANNEL_ID</code></p>

<p><img src="/assets/404bd5c70040/1*X4gZ5_IRvsiehAL0O9AleQ.webp" alt="" loading="lazy" decoding="async" width="1174" height="998" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMTc0IiBoZWlnaHQ9Ijk5OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>Commit 檔案到 Repo 主分支之後，回到 Actions 手動觸發看看：</p>

<blockquote>
  <p><em>日後會每日自動觸發。</em></p>
</blockquote>

<p><img src="/assets/404bd5c70040/1*WNFKXl-QC2WGyaSWep5-DA.webp" alt="" loading="lazy" decoding="async" width="1048" height="438" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDQ4IiBoZWlnaHQ9IjQzOCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>Actions → Pull Request Automation — Daily Checker → Run workflow → Branch: main → Run workflow。</p>

<p><strong>執行後可以點擊進入查看執行狀況：</strong></p>

<p><img src="/assets/404bd5c70040/1*4mvOxnd1uS4ZlxnNoNdHaQ.webp" alt="" loading="lazy" decoding="async" width="1087" height="545" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDg3IiBoZWlnaHQ9IjU0NSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/404bd5c70040/1*BluzFgY2tM2E7DwoVq1jQw.webp" alt="" loading="lazy" decoding="async" width="1030" height="458" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDMwIiBoZWlnaHQ9IjQ1OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>因為有 <code class="language-plaintext highlighter-rouge">needs</code> 的限制，Jobs 流程就會是 <code class="language-plaintext highlighter-rouge">Cacluate PR Status</code> 先完成後才會並發執行 <code class="language-plaintext highlighter-rouge">Auto Close Old PRs</code> 和 <code class="language-plaintext highlighter-rouge">Send PR Summary Message to Slack</code> 。</p>
<h4 id="執行結果-2">執行結果</h4>

<p><strong>任務都成功執行後可以查看 Slack 訊息：</strong></p>

<p><img src="/assets/404bd5c70040/1*0stX9KpZi6PcXpG-90wyIg.webp" alt="" loading="lazy" decoding="async" width="508" height="147" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1MDgiIGhlaWdodD0iMTQ3Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>成功 🚀🚀🚀</p>

<p><strong>完整程式碼： <a href="https://github.com/ZhgChgLi/github-actions-ci-cd-demo/blob/main/.github/workflows/Automation-PullRequest-Daily.yml" target="_blank">Automation-PullRequest-Daily.yml</a></strong></p>
<h3 id="小結">小結</h3>

<blockquote>
  <p>希望上面三個案例能讓您對 GitHub Actions 有個初步的了解也希望有激起你的自動化工作創意，可以自己發想工作流程(請務必先參考 <a href="https://docs.github.com/en/actions/reference/events-that-trigger-workflows" target="_blank">觸發事件</a> )，然後撰寫腳本執行；也記得先去 <a href="https://github.com/marketplace?type=actions" target="_blank"><strong>Marketplace</strong></a> 找找現有的步驟可以站在巨人的肩膀直接使用。</p>
</blockquote>

<p>本篇只是入門基礎(甚至沒 Checkout Code)，下一篇「 <a href="/posts/zrealm-dev/github-actions-ios-ci-cd-完整實作自動化建置-測試與部署流程教學-4b001d2e8440/"><strong>CI/CD 實戰指南（三）：使用 GitHub Actions 實作 App 專案的 CI 與 CD 工作流程</strong></a> 」會在介紹更複雜深入的 GitHub Actions Workflow。</p>
<h4 id="github-自動化延伸議題">GitHub 自動化延伸議題</h4>

<p>GitHub 可以與 Slack 整合，訂閱 Repo 的 PR 更新通知、Push Default Branch 通知…等等。</p>

<p><img src="/assets/404bd5c70040/1*BMTT4koRBIIsQ7_mklC86w.webp" alt="" loading="lazy" decoding="async" width="439" height="430" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0MzkiIGhlaWdodD0iNDMwIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><strong>問題 1. GitHub 訊息無法標記到人，只標記了 GitHub 帳號，Slack 帳號收不到通知：</strong></p>

<p><img src="/assets/404bd5c70040/1*Ddp0cEtXE10hScM1rozmtg.webp" alt="" loading="lazy" decoding="async" width="1300" height="946" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMzAwIiBoZWlnaHQ9Ijk0NiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/404bd5c70040/1*uHGJ7l__AdYoWT9KpvihLQ.webp" alt="" loading="lazy" decoding="async" width="1043" height="775" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDQzIiBoZWlnaHQ9Ijc3NSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><a href="https://slack.github.com/" target="_blank">Slack App</a> or Apps 搜尋 GitHub → 打開訊息視窗 →完成 <strong>Connect GitHub Account</strong> 步驟，這樣 GitHub 才知道你對應的 Slack UID 是什麼，才能標記你。</p>

<p><strong>問題 2. Pull Request Reminder</strong></p>

<p>我記得這本來是第三方開發的功能，後來直接整合在 GitHub 裡面了，不要傻傻的自己用 GitHub Actions 寫腳本做！</p>

<p><img src="/assets/404bd5c70040/1*BpMd2K0NBDE6xB1XzFY94Q.webp" alt="" loading="lazy" decoding="async" width="1084" height="1291" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDg0IiBoZWlnaHQ9IjEyOTEiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<blockquote>
  <p><a href="https://docs.github.com/en/organizations/organizing-members-into-teams/managing-scheduled-reminders-for-your-team" target="_blank"><strong><em>GitHub 內建的是 by Team 設置，你需要先為組織增加 Team、Repo 加入 Team 然後才能設置 Pull Request Reminder。</em></strong></a></p>
</blockquote>

<ul>
  <li>輸入完 Slack Channel 跟規則儲存之後每天就會自動傳送提示訊息跟標記 Reviewer 了，設置可 <a href="https://michaelheap.com/github-scheduled-reminders/" target="_blank">參考</a> ，結果如下圖：</li>
</ul>

<p><img src="/assets/404bd5c70040/1*kvqFXkIKIxP79Wm8vIaN0Q.webp" alt="https://michaelheap.com/github-scheduled-reminders/" loading="lazy" decoding="async" width="500" height="315" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1MDAiIGhlaWdodD0iMzE1Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><a href="https://michaelheap.com/github-scheduled-reminders/" target="_blank">https://michaelheap.com/github-scheduled-reminders/</a></p>

<p><strong>問題 4. GitHub PR 訊息，作者收不到通知：</strong></p>

<p><img src="/assets/404bd5c70040/1*wmtXwXAcQZDXqInRd0ivPw.webp" alt="" loading="lazy" decoding="async" width="792" height="535" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3OTIiIGhlaWdodD0iNTM1Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<blockquote>
  <p><em>這是一個萬年的問題，就是 PR 的 Slack 訊息 <strong>其實作者並不知道有人 Reply Threads 討論</strong> ，GitHub 發的這則 Slack 訊息只會標記 Reviewers，不會標記 Assignee or PR 作者。</em></p>
</blockquote>

<p>這時候 GitHub Actions 做自動化小工具就派上用場了，我們可以加一個 Job 在 PR Description 最後 Append 標記作者，這樣訊息傳到 Slack 也會一並標記到作者的 Slack:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Workflow(Action) 名稱</span>
name: Pull Reqeust Automation
<span class="c"># 請參考前文，略....</span>

  <span class="c"># ---------</span>
  append-author-to-pr-description:
    name: Append author to PR description
    <span class="c"># 因為是共用觸發事件，所以在 Job 上自己判斷，當是 Pull Request Opened(首次建立) 時才執行 Job 否則會 Skipped</span>
    <span class="k">if</span>: github.event_name <span class="o">==</span> <span class="s1">'pull_request'</span> <span class="o">&amp;&amp;</span> github.event.action <span class="o">==</span> <span class="s1">'opened'</span>
    
    <span class="c"># 如果這個 Job 失敗，不影響整個 Workflow，繼續其他 Job</span>
    <span class="k">continue</span><span class="nt">-on-error</span>: <span class="nb">true</span>
    
    <span class="c"># 設定最長 Timeout 時間，防止異常情況發生時無止盡的等待</span>
    timeout-minutes: 10
    
    <span class="c"># Runner Label - 使用 GitHub Hosted Runner ubuntu-latest 來執行工作</span>
    <span class="c"># 如果是 Private Repo 會計算用量，超過可能會產生費用</span>
    <span class="c"># 但這種自動化小工作不太容易用過量</span>
    runs-on: ubuntu-latest
    steps:
      - name: Append author to PR description
        <span class="nb">env</span>:
          <span class="c"># secrets.GITHUB_TOKEN 是 GitHub Actions 執行時自動產生的 Token，不需自行在 Secrets 設定，擁有一些 GitHub Repo API Scopes 權限</span>
          <span class="c"># gh(GitHub) cli 需要注入 GH_TOKEN 到 ENV，gh 才有權限操作</span>
          <span class="c"># https://docs.github.com/en/actions/how-tos/security-for-github-actions/security-guides/use-github_token-in-workflows</span>
          GH_TOKEN: <span class="k">${</span><span class="p">{ secrets.GITHUB_TOKEN </span><span class="k">}</span><span class="o">}</span>

          <span class="c">#   ${{ github.xxx }} 是 GitHub Actions Context 表達式</span>
          <span class="c">#   不是 Shell 變數，而是 YAML 解析階段由 GitHub Actions 替換成對應值</span>
          <span class="c">#   其他參數：https://docs.github.com/en/actions/learn-github-actions/contexts#github-context</span>
          PR_NUMBER: <span class="k">${</span><span class="p">{ github.event.pull_request.number </span><span class="k">}</span><span class="o">}</span>
          AUTHOR_TAG: <span class="s1">'@${{ github.event.pull_request.user.login }}'</span>
        run: |
          <span class="nv">PR_BODY</span><span class="o">=</span><span class="si">$(</span>gh <span class="nb">pr </span>view <span class="nv">$PR_NUMBER</span> <span class="nt">--repo</span> <span class="k">${</span><span class="p">{ github.repository </span><span class="k">}</span><span class="o">}</span> <span class="nt">--json</span> body <span class="nt">-q</span> <span class="s2">".body"</span><span class="si">)</span>
          <span class="nv">NEW_BODY</span><span class="o">=</span><span class="si">$(</span><span class="nb">printf</span> <span class="s2">"%s</span><span class="se">\n\n</span><span class="s2">Created by %s"</span> <span class="s2">"</span><span class="nv">$PR_BODY</span><span class="s2">"</span> <span class="s2">"</span><span class="nv">$AUTHOR_TAG</span><span class="s2">"</span><span class="si">)</span>
          gh <span class="nb">pr </span>edit <span class="nv">$PR_NUMBER</span> <span class="nt">--repo</span> <span class="k">${</span><span class="p">{ github.repository </span><span class="k">}</span><span class="o">}</span> <span class="nt">--body</span> <span class="s2">"</span><span class="nv">$NEW_BODY</span><span class="s2">"</span>
</code></pre></div></div>

<p><strong>完整程式碼： <a href="https://github.com/ZhgChgLi/github-actions-ci-cd-demo/blob/main/.github/workflows/Automation-PullRequest.yml" target="_blank">Automation-PullRequest.yml</a></strong></p>

<p>開 PR 時會自動在描述後加上作者資訊、Slack 訊息也會正確的標記作者：</p>

<p><img src="/assets/404bd5c70040/1*UtTDtIQJ0vJks79UzCQSAQ.webp" alt="" loading="lazy" decoding="async" width="953" height="370" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NTMiIGhlaWdodD0iMzcwIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><img src="/assets/404bd5c70040/1*MjVsFqE--xAq1zSQfCNVdQ.webp" alt="" loading="lazy" decoding="async" width="416" height="244" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0MTYiIGhlaWdodD0iMjQ0Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><strong>問題 5. GitHub 瘋狂寄通知信到信箱很干擾：</strong></p>

<p>GitHub Repo 通知跟 Slack 都有設定好之後，統一都在 Slack 收通知了，可以去 GitHub 個人設定把 Email 通知關掉：</p>

<p><img src="/assets/404bd5c70040/1*INgfO9B2bNFy-Nd1ZjnzOw.webp" alt="" loading="lazy" decoding="async" width="1200" height="630" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjYzMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<h4 id="其他">其他</h4>

<p>Actions 目前沒有目錄結構，但可以在 Actions 頁，置頂(Pin) 五個 Actions；也可以使用 Disable 暫停某個 Action。</p>

<p><img src="/assets/404bd5c70040/1*ZD-5xkparhJqtnm2REeu6A.webp" alt="" loading="lazy" decoding="async" width="1238" height="599" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjM4IiBoZWlnaHQ9IjU5OSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>可以在 Insights 查看 GitHub Actions 用量跟執行成效：</p>

<p><img src="/assets/404bd5c70040/1*re91ZRtQ0frOF6GVJ-MKrw.webp" alt="" loading="lazy" decoding="async" width="1271" height="626" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjcxIiBoZWlnaHQ9IjYyNiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<h3 id="reuse-workflow-補充">Reuse Workflow 補充</h3>

<p><a href="/posts/zrealm-dev/ci-cd-打包工具平台-使用-google-apps-script-web-app-串接-github-actions-提升跨團隊效率-4273e57e7148/">下一篇文章</a> 有用到同個 Repo 拆 Reuse Workflow，這邊補充一下不同 Repo 拆 Reuse Workflow 的範例。</p>

<p>先定義一個 Reuse Workflow: <a href="https://github.com/ZhgChgLi/github-actions-ci-cd-demo-share-actions/blob/main/.github/workflows/Automation-label-pr-base-branch.yml" target="_blank"><strong>Automation-label-pr-base-branch.yml:</strong></a> <strong>在 <a href="https://github.com/ZhgChgLi/github-actions-ci-cd-demo-share-actions/blob/main/.github/workflows/Automation-label-pr-base-branch.yml" target="_blank">ZhgChgLi/github-actions-ci-cd-demo-share-actions</a> Repo</strong> 。</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">name</span><span class="pi">:</span> <span class="s">Automation-label-pr-base-branch</span>
<span class="na">run-name</span><span class="pi">:</span> <span class="s2">"</span><span class="s">[Automation-label-pr-base-branch]</span><span class="nv"> </span><span class="s">${{</span><span class="nv"> </span><span class="s">github.event.inputs.PR_NUMBER</span><span class="nv"> </span><span class="s">||</span><span class="nv"> </span><span class="s">github.ref</span><span class="nv"> </span><span class="s">}}"</span>

<span class="na">concurrency</span><span class="pi">:</span>
  <span class="na">group</span><span class="pi">:</span> <span class="s">${{ github.workflow }}-${{ github.event.inputs.PR_NUMBER || github.ref }}</span>
  <span class="na">cancel-in-progress</span><span class="pi">:</span> <span class="kc">true</span>

<span class="c1"># 觸發事件</span>
<span class="na">on</span><span class="pi">:</span>
  <span class="c1"># 其他 Workflow 呼叫此 Workflow 觸發</span>
  <span class="na">workflow_call</span><span class="pi">:</span>
    <span class="c1"># 資料輸入</span>
    <span class="na">inputs</span><span class="pi">:</span>
      <span class="c1"># PR Number</span>
      <span class="na">PR_NUMBER</span><span class="pi">:</span>
        <span class="na">required</span><span class="pi">:</span> <span class="kc">true</span>
        <span class="na">type</span><span class="pi">:</span> <span class="s">string</span>
    <span class="c1"># 密鑰輸入</span>
    <span class="na">secrets</span><span class="pi">:</span>
      <span class="na">GH_TOKEN</span><span class="pi">:</span>
        <span class="na">description</span><span class="pi">:</span> <span class="s2">"</span><span class="s">GitHub</span><span class="nv"> </span><span class="s">token</span><span class="nv"> </span><span class="s">for</span><span class="nv"> </span><span class="s">API</span><span class="nv"> </span><span class="s">access"</span>
        <span class="na">required</span><span class="pi">:</span> <span class="kc">true</span>
<span class="na">jobs</span><span class="pi">:</span>
  <span class="na">label-pr-base-branch</span><span class="pi">:</span>
    <span class="na">name</span><span class="pi">:</span> <span class="s">Label PR Base Branch</span>
    <span class="na">continue-on-error</span><span class="pi">:</span> <span class="kc">true</span>
    <span class="na">timeout-minutes</span><span class="pi">:</span> <span class="m">10</span>
    <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>

    <span class="na">steps</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Label PR by Base Branch</span>
        <span class="na">id</span><span class="pi">:</span> <span class="s">label-pr-base-branch</span>
        <span class="na">env</span><span class="pi">:</span>
          <span class="na">GH_TOKEN</span><span class="pi">:</span> <span class="s">${{ secrets.GITHUB_TOKEN }}</span>
        <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
          <span class="s">PR_NUMBER="${{ inputs.PR_NUMBER }}"</span>
          <span class="s">REPO="${{ github.repository }}"</span>

          <span class="s">echo "📦 Processing PR #$PR_NUMBER in repo $REPO"</span>

          <span class="s"># 取得 PR 的 base branch</span>
          <span class="s">BASE_BRANCH=$(gh pr view "$PR_NUMBER" --repo "$REPO" --json baseRefName --jq '.baseRefName')</span>
          <span class="s">echo "🔖 PR Base Branch: $BASE_BRANCH"</span>

          <span class="s"># 允許的 base branch labels</span>
          <span class="s">BRANCH_LABELS=("develop" "main" "master")</span>

          <span class="s"># Label 是否在允許列表</span>
          <span class="s">if [[ " ${BRANCH_LABELS[@]} " =~ " ${BASE_BRANCH} " ]]; then</span>
            <span class="s">LABEL="$BASE_BRANCH"</span>
          <span class="s">else</span>
            <span class="s">echo "⚠️ Base branch '$BASE_BRANCH' not in allowed list, skipping label."</span>
            <span class="s">exit 0</span>
          <span class="s">fi</span>

          <span class="s"># 移除現有的 base branch labels</span>
          <span class="s">EXISTING_LABELS=$(gh pr view "$PR_NUMBER" --repo "$REPO" --json labels --jq '.labels[].name')</span>
          <span class="s">for EXISTING in $EXISTING_LABELS; do</span>
            <span class="s">if [[ " ${BRANCH_LABELS[@]} " =~ " ${EXISTING} " ]]; then</span>
              <span class="s">echo "🧹 Removing existing base branch label: $EXISTING"</span>
              <span class="s">gh pr edit "$PR_NUMBER" --repo "$REPO" --remove-label "$EXISTING"</span>
            <span class="s">fi</span>
          <span class="s">done</span>

          <span class="s"># 如果 Label 不存在則建立</span>
          <span class="s">if ! gh label list --repo "$REPO" | grep -q "^$LABEL$"; then</span>
            <span class="s">echo "🆕 Creating missing label: $LABEL"</span>
            <span class="s">gh label create "$LABEL" --repo "$REPO" --description "PR targeting $LABEL branch" --color "ededed"</span>
          <span class="s">else</span>
            <span class="s">echo "✅ Label '$LABEL' already exists"</span>
          <span class="s">fi</span>

          <span class="s"># 加上 base branch label</span>
          <span class="s">echo "🏷️ Adding label '$LABEL' to PR #$PR_NUMBER"</span>
          <span class="s">gh pr edit "$PR_NUMBER" --repo "$REPO" --add-label "$LABEL"</span>
</code></pre></div></div>

<p>回到我們的 <strong>主 <a href="https://github.com/ZhgChgLi/github-actions-ci-cd-demo/blob/main/.github/workflows/Demo-Reuse-Action-From-Another-Repo.yml" target="_blank">ZhgChgLi/github-actions-ci-cd-demo</a> Repo，</strong> 新增一個主 <strong>Workflow <a href="https://github.com/ZhgChgLi/github-actions-ci-cd-demo/blob/main/.github/workflows/Demo-Reuse-Action-From-Another-Repo.yml" target="_blank">Demo-Reuse-Action-From-Another-Repo.yml</a> :</strong></p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">name</span><span class="pi">:</span> <span class="s">Automation-label-pr-base-branch</span>

<span class="na">on</span><span class="pi">:</span>
  <span class="na">pull_request</span><span class="pi">:</span>
    <span class="na">types</span><span class="pi">:</span> <span class="pi">[</span><span class="nv">opened</span><span class="pi">]</span>

<span class="na">jobs</span><span class="pi">:</span>
  <span class="na">call-label-pr-workflow</span><span class="pi">:</span>
    <span class="na">name</span><span class="pi">:</span> <span class="s">Call Base Branch Label Workflow</span>
    <span class="c1"># ✅ 如果被呼叫的 workflow 在同一個 repo：</span>
    <span class="c1">#    uses: ./.github/workflows/Automation-label-pr-base-branch.yml</span>
    <span class="c1">#    注意：這種寫法無法指定分支（固定使用 caller workflow 的 branch）</span>
    <span class="c1">#    參考範例：</span>
    <span class="c1">#    - CD-Deploy-Form.yml 呼叫本 repo workflow</span>
    <span class="c1">#    - CD-Deploy.yml 呼叫跨 repo workflow</span>
    <span class="c1">#</span>
    <span class="c1"># ✅ 如果被呼叫的 workflow 在其他 repo：</span>
    <span class="c1">#    uses: {owner}/{repo}/.github/workflows/{file}.yml@{branch_or_tag}</span>
    <span class="c1">#    可以指定 branch 或 tag</span>
    <span class="c1">#    ref: https://github.com/ZhgChgLi/github-actions-ci-cd-demo-share-actions/blob/main/.github/workflows/Automation-label-pr-base-branch.yml</span>
    <span class="na">uses</span><span class="pi">:</span> <span class="s">ZhgChgLi/github-actions-ci-cd-demo-share-actions/.github/workflows/Automation-label-pr-base-branch.yml@main</span>
    <span class="na">with</span><span class="pi">:</span>
      <span class="na">PR_NUMBER</span><span class="pi">:</span> <span class="s">${{ github.event.number }}</span>
      
    <span class="c1"># 如果所有 secrets 都要繼承 caller workflow，直接用 `inherit`</span>
    <span class="c1"># secrets: inherit</span>
    <span class="c1">#</span>
    <span class="c1"># 如果只要傳特定 secrets，個別指定</span>
    <span class="na">secrets</span><span class="pi">:</span>
      <span class="na">GH_TOKEN</span><span class="pi">:</span> <span class="s">${{ secrets.GITHUB_TOKEN }}</span>
</code></pre></div></div>

<p>Commit 檔案後開 PR 試試看。</p>

<p><strong>工作流程：主 Repo 的 <a href="https://github.com/ZhgChgLi/github-actions-ci-cd-demo/blob/main/.github/workflows/Demo-Reuse-Action-From-Another-Repo.yml" target="_blank">Demo-Reuse-Action-From-Another-Repo.yml</a> → 呼叫並把參數傳到另一個 Repo 的 <a href="https://github.com/ZhgChgLi/github-actions-ci-cd-demo-share-actions/blob/main/.github/workflows/Automation-label-pr-base-branch.yml" target="_blank">Automation-label-pr-base-branch.yml</a> 執行任務 → 回到主 Repo 回報結果。</strong></p>

<p><img src="/assets/404bd5c70040/1*13bqlAgpBkHM7UthTJAuOg.webp" alt="" loading="lazy" decoding="async" width="1119" height="453" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMTE5IiBoZWlnaHQ9IjQ1MyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/404bd5c70040/1*CAF3HzS4PHBveDQYu3H73g.webp" alt="" loading="lazy" decoding="async" width="402" height="145" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0MDIiIGhlaWdodD0iMTQ1Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>成功！</p>
<h4 id="private-repo-存取問題">Private Repo 存取問題</h4>

<p>這邊要注意一個安全性問題，Public Repo 完全沒問題，但如果 Reuse Workflow 在 Private Repo 當中，只有在 <strong>同個組織下的其他 Repo + Repo 同樣也是 Private + 設定允許</strong> =才能存取使用 Private Repo Reuse Workflow。</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">Public — ZhgChgLi/ReuseRepo</code> + <code class="language-plaintext highlighter-rouge">Private or Public-ZhgChgli or harry/myRepo</code> = ✅</li>
  <li><code class="language-plaintext highlighter-rouge">Private — ZhgChgLi/ReuseRepo</code> + <code class="language-plaintext highlighter-rouge">Private or Public— harry/myRepo</code> = ❌</li>
  <li><code class="language-plaintext highlighter-rouge">Private — ZhgChgLi/ReuseRepo</code> + <code class="language-plaintext highlighter-rouge">Public — ZhgChgLi/anotherRepo</code> = ❌</li>
  <li><code class="language-plaintext highlighter-rouge">Private — ZhgChgLi/ReuseRepo</code> + <code class="language-plaintext highlighter-rouge">Private — ZhgChgLi/anotherRepo</code> = ✅</li>
</ul>

<p><strong>設定允許的方式，要去 Reuse Workflow Repo → Settings → Actions → General → Acces:</strong></p>

<p><img src="/assets/404bd5c70040/1*iZCEifgyArTJS3URMSk01g.webp" alt="" loading="lazy" decoding="async" width="725" height="300" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3MjUiIGhlaWdodD0iMzAwIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>改勾選：</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Accessible from repositories in the 'ZhgChgLi' organization
Workflows in other repositories that are part of the 'ZhgChgLi' organization can access the actions and reusable workflows in this repository. Access is allowed only from private repositories.
</code></pre></div></div>

<p>點擊「Save」儲存，完成。</p>

<p><strong>如果沒權限存取會出現以下錯誤訊息：</strong></p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Invalid workflow file: .github/workflows/Demo-reuse-action-from-another-repo.yml#L21
error parsing called workflow
<span class="s2">".github/workflows/Demo-reuse-action-from-another-repo.yml"</span>
-&gt; <span class="s2">"ZhgChgLi/github-actions-ci-cd-demo-share-actions/.github/workflows/Automation-label-pr-base-branch.yml@main"</span>
: workflow was not found.
</code></pre></div></div>
<h3 id="github-actions-script-語法補充">GitHub Actions Script 語法補充</h3>

<p>補充一下大家可能會感到疑惑的 <code class="language-plaintext highlighter-rouge">${{ XXX }}</code> or <code class="language-plaintext highlighter-rouge">${XXX}</code> or <code class="language-plaintext highlighter-rouge">$XXX</code> 變數語法。</p>

<p><strong>Demo-Env-Vars-.yml:</strong></p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">name</span><span class="pi">:</span> <span class="s">Demo-Env-Vars</span>

<span class="na">on</span><span class="pi">:</span>
  <span class="na">workflow_dispatch</span><span class="pi">:</span>

<span class="na">jobs</span><span class="pi">:</span>
  <span class="na">print-env-vars</span><span class="pi">:</span>
    <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>

    <span class="na">steps</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">print-env</span>
        <span class="na">run</span><span class="pi">:</span> <span class="s">env</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">context-vars-vs-vars</span>
        <span class="na">env</span><span class="pi">:</span>
          <span class="na">SAY_MY_NAME</span><span class="pi">:</span> <span class="s2">"</span><span class="s">Heisenberg"</span>
          <span class="c1"># GitHub Actions Context 表達式，同下方解釋</span>
          <span class="na">FROM_REF</span><span class="pi">:</span> <span class="s2">"</span><span class="s">${{</span><span class="nv"> </span><span class="s">github.ref</span><span class="nv"> </span><span class="s">}}"</span>
        <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
          <span class="s"># Sehll Script:</span>
          <span class="s"># 引用自訂注入的 ENV 變數內容</span>
          <span class="s"># ${SAY_MY_NAME} or $SAY_MY_NAME 都可以</span>
          <span class="s"># ${XX} 遇到字串拼接比較好用:</span>
          <span class="s">echo "HI: ${SAY_MY_NAME}"</span>
          
          <span class="s"># 💡 GitHub Actions Context 表達式</span>
          <span class="s"># 這不是 shell 變數，而是 YAML 解析階段由 GitHub Actions 替換成對應值</span>
          <span class="s"># ⚠ 在本機或非 GitHub Actions 環境執行會失敗</span>
          <span class="s"># 🔗 https://docs.github.com/en/actions/learn-github-actions/contexts#github-context</span>
          <span class="s">BRANCH_NAME_FROM_CONTEXT="${{ github.ref }}"</span>
          
          <span class="s"># 💡 GitHub Actions 執行階段的環境變數</span>
          <span class="s"># 這些變數在 runner 執行 shell 時由 GitHub Actions 自動注入</span>
          <span class="s"># ✅ 在其他環境可用 export 或 ENV 預先定義相同變數</span>
          <span class="s"># 🔗 https://docs.github.com/en/actions/learn-github-actions/environment-variables</span>
          <span class="s">BRANCH_NAME_FROM_ENV_VARS="${GITHUB_REF}"</span>
          
          <span class="s">echo "FROM_REF: ${FROM_REF}"</span>
          <span class="s">echo "BRANCH_NAME_FROM_CONTEXT: ${BRANCH_NAME_FROM_CONTEXT}"</span>
          <span class="s">echo "BRANCH_NAME_FROM_ENV_VARS: ${BRANCH_NAME_FROM_ENV_VARS}"</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">print-github-script-env</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/github-script@v7</span>
        <span class="na">env</span><span class="pi">:</span>
          <span class="na">SAY_MY_NAME</span><span class="pi">:</span> <span class="s2">"</span><span class="s">Heisenberg"</span>
          <span class="c1"># GitHub Actions Context 表達式，同上方解釋</span>
          <span class="na">FROM_REF</span><span class="pi">:</span> <span class="s2">"</span><span class="s">${{</span><span class="nv"> </span><span class="s">github.ref</span><span class="nv"> </span><span class="s">}}"</span>
        <span class="na">with</span><span class="pi">:</span>
          <span class="na">script</span><span class="pi">:</span> <span class="pi">|</span>
            <span class="s">// GitHub Script: (JavaScript (Node.js)):</span>
            <span class="s">// 從 process.env 取 ENV 值</span>
            <span class="s">console.log(`HI: ${process.env.SAY_MY_NAME}`);</span>
            <span class="s">console.log(`FROM_REF: ${process.env.FROM_REF}`);</span>
            <span class="s">// github-script 中會自動注入到 context 變數供 javascript 直接引用</span>
            <span class="s">// https://docs.github.com/en/actions/learn-github-actions/contexts#github-context</span>
            <span class="s">const branch_name_from_context_vars = context.ref;</span>
            <span class="s">console.log(`branch_name_from_context_vars: ${branch_name_from_context_vars}`);</span>
            <span class="s">// 同樣也能用 GitHub Actions Context 表達式(只是意義不大):</span>
            <span class="s">const branch_name_from_context = "${{ github.ref }}";</span>
            <span class="s">console.log(`branch_name_from_context: ${branch_name_from_context}`);</span>
            
            <span class="s">for (const [key, value] of Object.entries(process.env)) {</span>
              <span class="s">console.log(`${key}=${value}`);</span>
            <span class="s">}</span>
            <span class="s">// github-script 中的 github 物件是 Octokit REST API 實例</span>
            <span class="s">// 用來操作 github api 使用</span>
            <span class="s">// 例如：</span>
            <span class="s">// await github.rest.pulls.list({</span>
            <span class="s">//   owner: context.repo.owner,</span>
            <span class="s">//   repo: context.repo.repo,</span>
            <span class="s">//   state: "open"</span>
            <span class="s">//  });</span>
      <span class="c1"># gh CLI does NOT use GITHUB_TOKEN by default; requires GH_TOKEN</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">gh CLI without GH_TOKEN (expected to fail)</span>
        <span class="na">continue-on-error</span><span class="pi">:</span> <span class="kc">true</span>
        <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
          <span class="s">PR_COUNT=$(gh pr list --repo $GITHUB_REPOSITORY --json number --jq 'length')</span>
          <span class="s">echo "Found $PR_COUNT open pull requests"</span>
      
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">gh CLI with GH_TOKEN (expected to succeed)</span>
        <span class="na">env</span><span class="pi">:</span>
          <span class="c1"># Assign GH_TOKEN so gh CLI can authenticate</span>
          <span class="c1"># https://docs.github.com/en/actions/how-tos/security-for-github-actions/security-guides/use-github_token-in-workflows</span>
          <span class="na">GH_TOKEN</span><span class="pi">:</span> <span class="s">${{ secrets.GITHUB_TOKEN }}</span>
        <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
          <span class="s">PR_COUNT=$(gh pr list --repo $GITHUB_REPOSITORY --json number --jq 'length')</span>
          <span class="s">echo "Found $PR_COUNT open pull requests"</span>
      
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">github-script auto-authentication (no GH_TOKEN needed)</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/github-script@v7</span>
        <span class="na">with</span><span class="pi">:</span>
          <span class="na">script</span><span class="pi">:</span> <span class="pi">|</span>
            <span class="s">// github = Octokit REST client (auto-authenticated with GITHUB_TOKEN)</span>
            <span class="s">const pulls = await github.rest.pulls.list({</span>
              <span class="s">owner: context.repo.owner,</span>
              <span class="s">repo: context.repo.repo,</span>
              <span class="s">state: "open"</span>
            <span class="s">});</span>
            <span class="s">console.log(`Found ${pulls.data.length} open pull requests`);</span>
</code></pre></div></div>
<h4 id="-xxx--github-actions-context-表達式">${{ XXX }} GitHub Actions Context 表達式</h4>

<p><img src="/assets/404bd5c70040/1*e3jpPuwdmf005cjbaXjAwQ.webp" alt="" loading="lazy" decoding="async" width="789" height="684" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3ODkiIGhlaWdodD0iNjg0Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><img src="/assets/404bd5c70040/1*ed5Wm7Xk48xuP8yQzHpJ9w.webp" alt="" loading="lazy" decoding="async" width="880" height="800" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4ODAiIGhlaWdodD0iODAwIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">${{ XXX }}</code> 是 GitHub Actions Context 表達式， <strong>在YAML 解析階段由 GitHub Actions 替換成對應值</strong> (如上圖 <code class="language-plaintext highlighter-rouge">BRANCH_NAME_FROM_CONTEXT</code> )</li>
  <li>完整可用表達式可 <a href="https://docs.github.com/en/actions/learn-github-actions/contexts#github-context" target="_blank">參考官方文件</a></li>
  <li><code class="language-plaintext highlighter-rouge">${XXX}</code> or <code class="language-plaintext highlighter-rouge">$XXX</code> 是真實環境變數， <code class="language-plaintext highlighter-rouge">${XXX}</code> 在字串拼接比較好用</li>
  <li>GitHub Actions 預設會注入一些環境變數，完整可 <a href="https://docs.github.com/en/actions/learn-github-actions/environment-variables" target="_blank">參考官方文件</a></li>
</ul>

<h4 id="actionsgithub-scriptv7">actions/github-script@v7</h4>

<p><img src="/assets/404bd5c70040/1*TjJQnlAAU3VRwS9_dFz1yA.webp" alt="" loading="lazy" decoding="async" width="718" height="913" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3MTgiIGhlaWdodD0iOTEzIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><img src="/assets/404bd5c70040/1*5bHEdjrGsVdWnYSQgpnsZA.webp" alt="" loading="lazy" decoding="async" width="1018" height="1150" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDE4IiBoZWlnaHQ9IjExNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<ul>
  <li>在 <code class="language-plaintext highlighter-rouge">github-script</code> 中同樣可以用 <code class="language-plaintext highlighter-rouge">${{ XXX }}</code> 是 GitHub Actions Context 表達式，但是意義不大；因為 <strong>在 github-scirpt 環境中預設會注入 context 變數可以直接在 javascript 中引用</strong></li>
  <li>在 <code class="language-plaintext highlighter-rouge">github-script</code> 中 <code class="language-plaintext highlighter-rouge">github</code> 物件是 Octokit REST API 實例</li>
  <li><code class="language-plaintext highlighter-rouge">github-script</code> 也能從 <code class="language-plaintext highlighter-rouge">env:</code> 注入變數，但是要用 <code class="language-plaintext highlighter-rouge">${process.env.xxx}</code> 存取</li>
  <li>同樣預設會注入一些環境變數，完整可 <a href="https://docs.github.com/en/actions/learn-github-actions/environment-variables" target="_blank">參考官方文件</a> <code class="language-plaintext highlighter-rouge">${process.env.xxx}</code> 存取；但是已經有 <code class="language-plaintext highlighter-rouge">context</code> 變數了，從這存取意義也不大</li>
</ul>

<p><img src="/assets/404bd5c70040/1*oW85Sju0sN4caXiaLSg32A.webp" alt="" loading="lazy" decoding="async" width="735" height="408" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3MzUiIGhlaWdodD0iNDA4Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">github-script</code> 會自動帶入 <code class="language-plaintext highlighter-rouge">github-token</code> ，無需指定</li>
</ul>

<h4 id="gh-cli-gh_token">gh cli GH_TOKEN</h4>

<p><img src="/assets/404bd5c70040/1*NUfaaKIobZWVBuXrGg0TPw.webp" alt="" loading="lazy" decoding="async" width="866" height="431" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4NjYiIGhlaWdodD0iNDMxIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<ul>
  <li>在 GitHub Actions Shell Script 使用 <code class="language-plaintext highlighter-rouge">gh cli</code> 需指定 <code class="language-plaintext highlighter-rouge">GH_TOKEN</code> 參數</li>
  <li>可以直接帶入預設 Token — <code class="language-plaintext highlighter-rouge">secrets.GITHUB_TOKEN</code> (GitHub Actions 執行時自動產生)</li>
  <li><strong>預設 Token 有一些基本權限，如果要操作的指令權限不夠就要改用 <a href="https://docs.github.com/en/actions/how-tos/security-for-github-actions/security-guides/use-github_token-in-workflows" target="_blank">Personal Access Token</a></strong> ，團隊使用建議用共用帳號建立 PAT。</li>
</ul>

<p><strong>完整程式碼： <a href="https://github.com/ZhgChgLi/github-actions-ci-cd-demo/blob/main/.github/workflows/Demo-Env-Vars-.yml" target="_blank">Demo-Env-Vars-.yml</a></strong></p>

<p>GitHub Actions 已經開發好了，下一步我們可以抽換 GitHub Hosted Runner 成自己的 Self-hosted Runner。</p>

<blockquote>
  <p><em>GitHub Hosted Runner 有免費額度 2,000 分鐘一個月(起)，跑這種小的自動化任務花不了多少時間，而且是跑在 linux 機器花費很低，可能還用不到免費上限； <strong>不一定要改成 Self-hosted Runner</strong> ，改 Runner 還要確保 Runner 環境正確(例如 GitHub Hosted Runner 自帶 gh cli，self-hosted Runner 就記得要自己安裝好)， <strong>本文純粹是因為教學才會這樣抽換</strong> 。</em></p>
</blockquote>

<blockquote>
  <p><em>如果是用在執行 CI/CD 任務才必須要用 Self-hosted Runner。</em></p>
</blockquote>

<h4 id="新增-self-hosted-runner">新增 Self-hosted Runner</h4>

<blockquote>
  <p><em>本文以 <strong>macOS M1</strong> 為例。</em></p>
</blockquote>

<p><img src="/assets/404bd5c70040/1*mEInaj-tLaprMAa2OfowCw.webp" alt="" loading="lazy" decoding="async" width="1225" height="1136" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjI1IiBoZWlnaHQ9IjExMzYiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<ul>
  <li>Settings → Actions → Runners → New self-hosted runner。</li>
  <li><strong>Runner image:</strong> macOS</li>
  <li><strong>Architecture</strong> : M1 記得選 ARM64 執行比較快</li>
</ul>

<p><strong>在實體電腦上開一個 Termnial。</strong></p>

<p><strong>按照 Download 步驟在本地電腦完成：</strong></p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 在你想要的路徑，建立 Runner 目錄</span>
<span class="nb">mkdir </span>actions-runner <span class="o">&amp;&amp;</span> <span class="nb">cd </span>actions-runner
<span class="c"># 下載 Runner image</span>
curl <span class="nt">-o</span> actions-runner-osx-x64-2.325.0.tar.gz <span class="nt">-L</span> https://github.com/actions/runner/releases/download/v2.325.0/actions-runner-osx-x64-2.325.0.tar.gz

<span class="c"># Optional: Validate the hash</span>
<span class="nb">echo</span> <span class="s2">"0562bd934b27ca0c6d8a357df00809fbc7b4d5524d4aeb6ec152e14fd520a4c3  actions-runner-osx-x64-2.325.0.tar.gz"</span> | shasum <span class="nt">-a</span> 256 <span class="nt">-c</span>

<span class="c"># 解壓縮</span>
<span class="nb">tar </span>xzf ./actions-runner-osx-x64-2.325.0.tar.gz
</code></pre></div></div>

<blockquote>
  <p><em>以上只是參考；建議照你設定頁裡的步驟來做，才會是最新版本的 Runner image。</em></p>
</blockquote>

<p><img src="/assets/404bd5c70040/1*Q0k0EPjrY2V4owseESe5lg.webp" alt="" loading="lazy" decoding="async" width="1161" height="197" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMTYxIiBoZWlnaHQ9IjE5NyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><strong>設定 Configure:</strong></p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 請參考設定頁指令，Token 會隨時間改變</span>
./config.sh <span class="nt">--url</span> https://github.com/ORG/REPO <span class="nt">--token</span> XXX
</code></pre></div></div>

<p>會依序要你輸入：</p>
<ul>
  <li>Enter the name of the runner group to add this runner to: [press Enter for Default] <strong>直接 Enter</strong> 
*只有註冊到 Organization 組織層級的 Runner 才有 Group 分組功能</li>
  <li>Enter the name of runner: [press Enter for ZhgChgLideMacBook-Pro] <strong>可以輸入想設定的 Runner 名稱 e.g.</strong> <code class="language-plaintext highlighter-rouge">app-runner-1</code> <strong>或直接 Enter</strong></li>
  <li>This runner will have the following labels: ‘self-hosted’, ‘macOS’, ‘X64’
Enter any additional labels (ex. label-1,label-2): [press Enter to skip] 
<strong>輸入想要設定的 Runner label，可以多輸入自訂 Label 方便之後使用</strong> 
同前述 GitHub Acitons/Runner 是依照對應 Label 找任務來做， <strong>如果只用 default label，Runner 可能會撿到組織內的其他 Runner 來執行工作，自訂一個最保險</strong> 。 
這邊我自己亂定了一個 label <code class="language-plaintext highlighter-rouge">self-hosted-zhgchgli</code></li>
  <li>Enter name of work folder: [press Enter for _work] <strong>直接 Enter</strong></li>
</ul>

<p>出現 √ Settings Saved. 代表設定完成了。</p>

<p><img src="/assets/404bd5c70040/1*yxLfii0rWhzsiFuYVmPgFw.webp" alt="" loading="lazy" decoding="async" width="929" height="501" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MjkiIGhlaWdodD0iNTAxIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><strong>啟動 Runner:</strong></p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./run.sh
</code></pre></div></div>

<p>出現 √ Connected to GitHub、Listening for Jobs 就代表已經在監聽 Actions 任務了：</p>

<p><img src="/assets/404bd5c70040/1*1RmPPOosAoCxHA9tg_WE2w.webp" alt="" loading="lazy" decoding="async" width="290" height="98" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyOTAiIGhlaWdodD0iOTgiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<blockquote>
  <p><strong><em>這個 Terminal 視窗不關閉就會持續接收任務來做。</em></strong></p>
</blockquote>

<blockquote>
  <p><strong><em>🚀🚀🚀同一台電腦開多個 Terminal 在不同目錄就能起多個 Runner。</em></strong></p>
</blockquote>

<p><strong>回到 Repo 設定頁也能看到 Runner 正在等待任務：</strong></p>

<p><img src="/assets/404bd5c70040/1*jjq5NTPYMuC3gH3ey_VTtg.webp" alt="" loading="lazy" decoding="async" width="802" height="247" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4MDIiIGhlaWdodD0iMjQ3Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><strong>Status:</strong></p>
<ul>
  <li>Idle: 閑置，在等待任務</li>
  <li>Active: 有任務正在執行</li>
  <li>Offline: Runner 不在線上</li>
</ul>

<h4 id="workflowgithub-actions-runner-改用-self-hosted-runner">Workflow(GitHub Actions) Runner 改用 Self-hosted Runner</h4>

<p>以 <code class="language-plaintext highlighter-rouge">Automation-PullRequest.yml</code> 為例：</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># 請參考前文，略....</span>
<span class="na">jobs</span><span class="pi">:</span>
  <span class="na">label-pr-by-file-count</span><span class="pi">:</span>
    <span class="c1"># 請參考前文，略....</span>
    <span class="na">runs-on</span><span class="pi">:</span> <span class="pi">[</span><span class="nv">self-hosted-zhgchgli</span><span class="pi">]</span>
    <span class="c1"># 請參考前文，略....</span>
  <span class="c1"># ---------</span>
  <span class="na">assign-self-if-no-assignee</span><span class="pi">:</span>
    <span class="c1"># 請參考前文，略....</span>
    <span class="na">runs-on</span><span class="pi">:</span> <span class="pi">[</span><span class="nv">self-hosted-zhgchgli</span><span class="pi">]</span>

    <span class="na">steps</span><span class="pi">:</span>
      <span class="c1"># 請參考前文，略....</span>
</code></pre></div></div>

<p>Commit 檔案到 Repo 主分支之後，重新開 PR 觸發驗證一下 Actions。</p>

<p><strong>回到 Runner Terminal 就能看到有新的任務進來了，正在執行跟執行結果：</strong></p>

<p><img src="/assets/404bd5c70040/1*Hu0DxKQbenPmBEw8swv65A.webp" alt="" loading="lazy" decoding="async" width="628" height="100" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2MjgiIGhlaWdodD0iMTAwIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>失敗了，因為我本機電腦沒有安裝 <a href="https://github.com/cli/cli" target="_blank">gh cli</a> 環境：</p>

<p><img src="/assets/404bd5c70040/1*9xguacdPATIeEZdbFszmHw.webp" alt="" loading="lazy" decoding="async" width="973" height="123" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NzMiIGhlaWdodD0iMTIzIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>使用 <code class="language-plaintext highlighter-rouge">brew install gh</code> 在實體電腦上安裝完 gh 後再次觸發執行：</p>

<p><img src="/assets/404bd5c70040/1*TbkAt00K89Ysbix33dK8ZA.webp" alt="" loading="lazy" decoding="async" width="631" height="29" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2MzEiIGhlaWdodD0iMjkiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>成功！現在這個任務就完全是使用我們自己的電腦在執行，不使用 GitHub Hosted Runner、不計費使用。</p>

<p><strong>我們可以點進去 Action Log 查看任務執行在的 Runner、機器是哪一個：</strong></p>

<p><img src="/assets/404bd5c70040/1*oqLcWfn6cbWsCt9fVsrikQ.webp" alt="" loading="lazy" decoding="async" width="1092" height="826" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDkyIiBoZWlnaHQ9IjgyNiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<h4 id="runs-on--runner-label-設定">runs-on: [ <strong>Runner Label] 設定</strong></h4>

<p>這裡是 AND 不是 OR，GitHub Runner 暫不支援 OR 挑選 Runner 執行。</p>

<p>例如： <code class="language-plaintext highlighter-rouge">[self-hosted, macOS, app]</code> → 代表 Runner 要 <strong>同時有</strong> <code class="language-plaintext highlighter-rouge">self-hosted, macOS, app</code> 這 3 個 Labels 才會匹配接任務來執行。</p>

<p>如果一個 Job 想同時測試不同 Runner 環境下的結果可以使用 <code class="language-plaintext highlighter-rouge">matrix</code> 參數：</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">jobs</span><span class="pi">:</span>
  <span class="na">test</span><span class="pi">:</span>
    <span class="na">runs-on</span><span class="pi">:</span> <span class="s">${{ matrix.runner }}</span>
    <span class="na">strategy</span><span class="pi">:</span>
      <span class="na">matrix</span><span class="pi">:</span>
        <span class="na">runner</span><span class="pi">:</span>
          <span class="pi">-</span> <span class="pi">[</span><span class="nv">self-hosted</span><span class="pi">,</span> <span class="nv">linux</span><span class="pi">,</span> <span class="nv">high-memory</span><span class="pi">]</span>
          <span class="pi">-</span> <span class="pi">[</span><span class="nv">self-hosted</span><span class="pi">,</span> <span class="nv">macos</span><span class="pi">,</span> <span class="nv">xcode-15</span><span class="pi">]</span>

    <span class="na">steps</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">run</span><span class="pi">:</span> <span class="s">echo "Running on ${{ matrix.runner }}"</span>
</code></pre></div></div>

<p>這樣這個 Job 會在下列兩個 Runner Labels Runner 中並行各執行一次：</p>
<ul>
  <li>self-hosted, linux, high-memory</li>
  <li>self-hosted, macos, xcode-15</li>
</ul>

<blockquote>
  <p><strong><em>Runner 暫不支援：</em></strong></p>
</blockquote>

<blockquote>
  <p><em>- OR 挑選 Runner</em></p>
</blockquote>

<blockquote>
  <p><em>- Runner 權重設定</em></p>
</blockquote>

<h4 id="註冊-runner-成-service">註冊 Runner 成 Service</h4>

<p>可以參考官方文件「 <a href="https://docs.github.com/en/actions/how-tos/hosting-your-own-runners/managing-self-hosted-runners/configuring-the-self-hosted-runner-application-as-a-service?platform=mac" target="_blank">Configuring the self-hosted runner application as a service</a> 」將 Runner 直接註冊成系統 Service，這樣就能在背景執行(不用開 Terminal 在前景)、開機後也會自動啟動。</p>

<p>有多個 Runner 記得調整「 <a href="https://docs.github.com/en/actions/how-tos/hosting-your-own-runners/managing-self-hosted-runners/configuring-the-self-hosted-runner-application-as-a-service?platform=mac#customizing-the-self-hosted-runner-service-1" target="_blank">Customizing the self-hosted runner service</a> 」設定註冊不同名稱。</p>

<blockquote>
  <p><em>iOS 這邊我有一個待研究排除的問題， <strong>就是我改用背景 Service 之後在 Archive 會遇到錯誤(疑似是跟 keychain 權限有關)</strong> ，當時沒時間解決，就先用起前景 Terminal Runner 了。</em></p>
</blockquote>

<p>如果是傳統前景要做到開機自動啟動就要去 <code class="language-plaintext highlighter-rouge">~/Library/LaunchAgents</code> 新增一個自動啟動設定檔案：</p>

<p><code class="language-plaintext highlighter-rouge">actions.runner.REPO.RUNNER_NAME.plist</code> ：</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">&lt;?xml version="1.0" encoding="UTF-8"?&gt;</span>
<span class="cp">&lt;!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"&gt;</span>
<span class="nt">&lt;plist</span> <span class="na">version=</span><span class="s">"1.0"</span><span class="nt">&gt;</span>
 <span class="nt">&lt;dict&gt;</span>
  <span class="nt">&lt;key&gt;</span>Label<span class="nt">&lt;/key&gt;</span>
  <span class="nt">&lt;string&gt;</span>actions.runner.REPO.RUNNER_NAME<span class="nt">&lt;/string&gt;</span>
  <span class="c">&lt;!-- 指定 Terminal.app 來啟動 --&gt;</span>
  <span class="nt">&lt;key&gt;</span>ProgramArguments<span class="nt">&lt;/key&gt;</span>
  <span class="nt">&lt;array&gt;</span>
   <span class="nt">&lt;string&gt;</span>/usr/bin/open<span class="nt">&lt;/string&gt;</span>
   <span class="nt">&lt;string&gt;</span>-a<span class="nt">&lt;/string&gt;</span>
   <span class="nt">&lt;string&gt;</span>Terminal<span class="nt">&lt;/string&gt;</span>
   <span class="nt">&lt;string&gt;</span>/Users/zhgchgli/Documents/actions-runner/run.sh<span class="nt">&lt;/string&gt;</span>
  <span class="nt">&lt;/array&gt;</span>
  <span class="nt">&lt;key&gt;</span>RunAtLoad<span class="nt">&lt;/key&gt;</span>
  <span class="nt">&lt;true/&gt;</span>
  <span class="nt">&lt;key&gt;</span>WorkingDirectory<span class="nt">&lt;/key&gt;</span>
  <span class="nt">&lt;string&gt;</span>/Users/zhgchgli/Documents/actions-runner<span class="nt">&lt;/string&gt;</span>
 <span class="nt">&lt;/dict&gt;</span>
<span class="nt">&lt;/plist&gt;</span>
</code></pre></div></div>

<blockquote>
  <p><em>有興趣深入研究 DevOps 的朋友可參考官方 <a href="https://docs.github.com/en/actions/concepts/runners/about-actions-runner-controller#scaling-runners" target="_blank">k8s Runner</a> 文件。</em></p>
</blockquote>

<h4 id="完整專案-repo">完整專案 Repo</h4>

<p><a href="https://github.com/ZhgChgLi/github-actions-ci-cd-demo" target="_blank"><img src="https://opengraph.githubassets.com/eb233d74ad1bc6b0eceb9494487579557fb7f17066a3981d20b52fde2c00ef45/ZhgChgLi/github-actions-ci-cd-demo" alt="" /></a></p>

<h3 id="官方文件">官方文件</h3>

<p><a href="https://docs.github.com/en/actions/get-started/quickstart" target="_blank"><img src="https://docs.github.com/assets/cb-345/images/social-cards/actions.png" alt="" /></a></p>

<p>更細節的設置方式請務必參考官方手冊。</p>
<h4 id="ai-can-help">AI Can Help!</h4>

<p><img src="/assets/404bd5c70040/1*JecNUCqujV1oeAvHA14X-g.webp" alt="" loading="lazy" decoding="async" width="1114" height="1140" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMTE0IiBoZWlnaHQ9IjExNDAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>實測給 ChatGPT 完整的步驟跟時機點就能幫你寫好 GitHub Actions 直接使用!</p>
<h3 id="總結">總結</h3>

<p>現在你應該已經對 GitHub Actions + Self-hosted Runner 有了一定程度的了解，下一篇我將開始以 App (iOS) CI/CD 為案例，手把手建置整套流程。</p>
<h3 id="系列文章">系列文章：</h3>
<ul>
  <li><a href="/posts/zrealm-dev/ci-cd-是什麼-打造高效穩定開發團隊的關鍵流程與工具選擇-c008a9e8ceca/"><strong>CI/CD 實戰指南（一）：CI/CD 是什麼？如何透過 CI/CD 打造穩定高效的開發團隊？工具選擇？</strong></a></li>
  <li><a href="/posts/zrealm-dev/github-actions-self-hosted-runner-詳解與實戰案例-ci-cd-自動化工作流程建置-404bd5c70040/"><strong>CI/CD 實戰指南（二）：GitHub Actions 與 self-hosted Runner 使用與建置大全</strong></a></li>
  <li><a href="/posts/zrealm-dev/github-actions-ios-ci-cd-完整實作自動化建置-測試與部署流程教學-4b001d2e8440/"><strong>CI/CD 實戰指南（三）：使用 GitHub Actions 實作 App 專案的 CI 與 CD 工作流程</strong></a></li>
  <li><a href="/posts/zrealm-dev/ci-cd-打包工具平台-使用-google-apps-script-web-app-串接-github-actions-提升跨團隊效率-4273e57e7148/"><strong>CI/CD 實戰指南（四）：使用 Google Apps Script Web App 串接 GitHub Actions 建置免費易用的打包工具平台</strong></a></li>
</ul>

<h4 id="-buy-me-a-beer-on-paypal"><a href="https://www.paypal.com/ncp/payment/CMALMPT8UUTY2" target="_blank">🍺 Buy me a beer on PayPal</a></h4>

<blockquote>
  <p><a href="https://www.paypal.com/ncp/payment/CMALMPT8UUTY2" target="_blank"><strong><em>本系列文章花費了大量的時間精力撰寫，如果內容對您有幫助、對您的團隊有實質提升工作效率與產品品質；歡迎請我喝杯咖啡，感謝支持！</em></strong></a></p>
</blockquote>

<p><img src="/assets/404bd5c70040/1*QJj54G9gOjtQS-rbHVT1SQ.webp" alt="Buy me a coffee" loading="lazy" decoding="async" width="700" height="700" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3MDAiIGhlaWdodD0iNzAwIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><a href="https://www.paypal.com/ncp/payment/CMALMPT8UUTY2" target="_blank">🍺 Buy me a beer on PayPal</a></p>]]></content>
  </entry><entry>
    <title type="html">CI/CD 是什麼｜打造高效穩定開發團隊的關鍵流程與工具選擇</title>
    <link href="https://zhgchg.li/posts/zrealm-dev/ci-cd-%E6%98%AF%E4%BB%80%E9%BA%BC-%E6%89%93%E9%80%A0%E9%AB%98%E6%95%88%E7%A9%A9%E5%AE%9A%E9%96%8B%E7%99%BC%E5%9C%98%E9%9A%8A%E7%9A%84%E9%97%9C%E9%8D%B5%E6%B5%81%E7%A8%8B%E8%88%87%E5%B7%A5%E5%85%B7%E9%81%B8%E6%93%87-c008a9e8ceca/" rel="alternate" type="text/html" title="CI/CD 是什麼｜打造高效穩定開發團隊的關鍵流程與工具選擇" />
    <published>2025-06-30T15:10:16+08:00</published>
    <updated>2025-12-18T00:24:11+08:00</updated>
    <id>https://zhgchg.li/posts/zrealm-dev/c008a9e8ceca</id><summary type="html">針對 iOS App 團隊，解析無 CI/CD 流程的痛點，透過 GitHub Actions 與 self-hosted Runner 自動化測試與打包，減少人力浪費與錯誤，提升團隊穩定性與開發效率，搭配 Google Apps Script Web App 實現跨職能便捷操作。</summary><author>
      <name>ZhgChgLi</name>
    </author><category term="ZRealm Dev." /><category term="ios-app-development" /><category term="cicd" /><category term="github-actions" /><category term="jenkins" /><category term="agile" /><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://zhgchg.li/assets/c008a9e8ceca/1*vokpvb4dyWHOnVnF3WGbfw.webp" /><content type="html" xml:base="https://zhgchg.li/posts/zrealm-dev/ci-cd-%E6%98%AF%E4%BB%80%E9%BA%BC-%E6%89%93%E9%80%A0%E9%AB%98%E6%95%88%E7%A9%A9%E5%AE%9A%E9%96%8B%E7%99%BC%E5%9C%98%E9%9A%8A%E7%9A%84%E9%97%9C%E9%8D%B5%E6%B5%81%E7%A8%8B%E8%88%87%E5%B7%A5%E5%85%B7%E9%81%B8%E6%93%87-c008a9e8ceca/"><![CDATA[<h3 id="cicd-實戰指南一cicd-是什麼如何透過-cicd-打造穩定高效的開發團隊工具選擇">CI/CD 實戰指南（一）：CI/CD 是什麼？如何透過 CI/CD 打造穩定高效的開發團隊？工具選擇？</h3>

<p>以 App (iOS) Team 為例，帶您從 0 認識 CI/CD 與導入後能帶來的實質價值。</p>

<p><img src="/assets/c008a9e8ceca/1*vokpvb4dyWHOnVnF3WGbfw.webp" alt="Photo by Leif Christoph Gottwald" loading="lazy" decoding="async" width="1200" height="675" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjY3NSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>Photo by <a href="https://unsplash.com/@project2204?utm_content=creditCopyText&amp;utm_medium=referral&amp;utm_source=unsplash" target="_blank">Leif Christoph Gottwald</a></p>
<h3 id="2026-update--pricing-changes-for-github-actions-including-self-hosted-runner">2026 Update — Pricing changes for GitHub Actions (including Self-hosted Runner)</h3>

<blockquote>
  <p><a href="https://resources.github.com/actions/2026-pricing-changes-for-github-actions/" target="_blank">TLDR — On January 1, 2026, we are lowering the price of hosted runners, and beginning March 1, 2026, we are charging $0.002 per-minute across self-hosted runners. The vast majority of customers will not see a change to their bill. Actions will remain free in public repositories.</a></p>
</blockquote>

<p>GitHub 2025/12/14 <a href="https://resources.github.com/actions/2026-pricing-changes-for-github-actions/" target="_blank">最新公告</a> ：</p>
<ul>
  <li>2026 年 1 月 1 日：GitHub-hosted Runner 更便宜了，價格下調最高 39%。</li>
  <li>2026 年 3 月 1 日： <strong>使用 Self-hosted Runner 需要多支付服務維運費，每分鐘 $0.002 美元(不分 OS)、等於 500 分鐘花費 1 美元，目前來看還是划算。</strong> ️</li>
  <li>Public repository 仍保持相同免費額度。</li>
</ul>

<p><strong>GitHub 也會投入更多資源提升 GitHub Actions 功能：</strong></p>
<ul>
  <li><strong>Scale Set Client：</strong> 簡化 Self-hosted Runner 的 <strong>Autoscaling</strong> 。</li>
  <li><strong>multi-label：支援重啟</strong> ，改進 Runner 標籤體驗。</li>
  <li><strong>Actions Runner Controller (ARC)</strong> 更新：提升配置、可觀察性與版本管理。</li>
  <li><strong>Actions Data Stream：</strong> 提供接近實時的 workflow/runner 事件數據串流，用於監控跟分析。</li>
</ul>

<p><a href="https://github.com/pricing/calculator" target="_blank"><strong>官方的價格計算器也更新了</strong></a> <strong>：</strong></p>

<p><img src="/assets/c008a9e8ceca/1*CuNXq0t_70UiVY0N3JEacA.webp" alt="" loading="lazy" decoding="async" width="1144" height="921" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMTQ0IiBoZWlnaHQ9IjkyMSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<ul>
  <li>GitHub Hosted Runner — macOS 3-core(M1): $0.062 USD/分鐘、1,000 分鐘等於 $62 USD</li>
  <li>Self-Hosted Runner — $0.002 USD/分鐘、1,000 分鐘等於 $2 USD</li>
</ul>

<blockquote>
  <p><em>自己買一台最新型號的 <a href="https://www.apple.com/tw/shop/buy-mac/mac-mini" target="_blank">Mac Mini 10-core(M4)</a> <strong>約 $600 USD</strong> ，放在辦公室當 Self-hosted Runner 大約一年就回本了；說效能更好更快而且用量幾乎等於無上限( <strong>維運費微乎其微</strong> )。</em></p>
</blockquote>

<blockquote>
  <p><em>也可以用汰換下來的舊 M1 MacBook Pro 當 Runner = 0 元。</em></p>
</blockquote>

<blockquote>
  <p>*以上不考慮電費跟網路費。</p>
</blockquote>

<h4 id="前言">前言</h4>

<p>歷經了兩次在不同開發團隊建置 App CI/CD 的經驗，最近終於有時間整理這段從「為什麼要做」到「該怎麼做」的心路歷程；不敢保證是最標準的 CI/CD 工作流程，但絕對是一個值得參考的起點，幫助您的團隊開始導入、提升產品穩定性與整體開發效率。</p>
<h4 id="章節">章節</h4>

<p>本系列文章會從「CI/CD 是什麼、能帶來哪些價值」開始講起，接著開始手把手實作「如何用 GitHub Actions + self-hosted Runner 搭建 CI/CD 環境」並「以 App 開發為例，實際導入 CI 和 CD」，最後還會介紹如何「使用Google Apps Script Web App 結合 GitHub Actions，打造一個方便跨團隊使用的 App 打包平台」希望這系列內容對你有所幫助。</p>
<ul>
  <li><a href="/posts/zrealm-dev/ci-cd-是什麼-打造高效穩定開發團隊的關鍵流程與工具選擇-c008a9e8ceca/"><strong>CI/CD 實戰指南（一）：CI/CD 是什麼？如何透過 CI/CD 打造穩定高效的開發團隊？工具選擇？</strong></a></li>
  <li><a href="/posts/zrealm-dev/github-actions-self-hosted-runner-詳解與實戰案例-ci-cd-自動化工作流程建置-404bd5c70040/"><strong>CI/CD 實戰指南（二）：GitHub Actions 與 self-hosted Runner 使用與建置大全</strong></a></li>
  <li><a href="/posts/zrealm-dev/github-actions-ios-ci-cd-完整實作自動化建置-測試與部署流程教學-4b001d2e8440/"><strong>CI/CD 實戰指南（三）：使用 GitHub Actions 實作 App 專案的 CI 與 CD 工作流程</strong></a></li>
  <li><a href="/posts/zrealm-dev/ci-cd-打包工具平台-使用-google-apps-script-web-app-串接-github-actions-提升跨團隊效率-4273e57e7148/"><strong>CI/CD 實戰指南（四）：使用 Google Apps Script Web App 串接 GitHub Actions 建置免費易用的打包工具平台</strong></a></li>
</ul>

<h4 id="最終成果">最終成果</h4>

<p>廢話不多說，先上最終結果。</p>

<p><img src="/assets/c008a9e8ceca/1*7-abScyjHQno1XzH4aGkaw.webp" alt="Demo PR" loading="lazy" decoding="async" width="1265" height="1093" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjY1IiBoZWlnaHQ9IjEwOTMiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><a href="https://github.com/ZhgChgLi/github-actions-ci-cd-demo/pull/11" target="_blank">Demo PR</a></p>

<p><img src="/assets/c008a9e8ceca/1*yXMeaOELhqdvMCxIJ5ElBw.gif" alt="Demo Web App (首次使用請參考下方教學)" loading="lazy" decoding="async" width="1048" height="822" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDQ4IiBoZWlnaHQ9IjgyMiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><a href="https://script.google.com/macros/s/AKfycbwNW6N5ozKbIz_E1HK6yFEUtA8KQrUciS-jcPsQptvIKlARmKgLxbQzNu8ksVeg-BmEfg/exec" target="_blank">Demo Web App (首次使用請參考下方教學)</a></p>

<p>CI/CD — 全部都使用 GitHub Actions 開發，好維護、好擴充。</p>

<p><strong>CI:</strong></p>
<ul>
  <li>發 PR 自動觸發單元測試</li>
  <li>依照改動檔案範圍執行對應測試</li>
  <li>測試通過 (passed) 後才能 Merge PR</li>
</ul>

<p><strong>CD:</strong></p>
<ul>
  <li>Google Apps Script Web App (CD 打包介面) 工程師、QA、PM 都可以透過這個網站在電腦或手機上打包 App</li>
  <li>GitHub Actions Self-hosted Runner 使用自己的機器跑 CI/CD 用量吃到飽</li>
  <li>串接 Firebase App Distribution API 直接取得打包的測試版下載連結</li>
</ul>

<p><strong>Automation</strong> :</p>
<ul>
  <li>發 PR 自動 Assign self</li>
  <li>發 PR 自動隨機指定 Reviewer</li>
  <li>標記 PR Size Label</li>
</ul>

<h4 id="demo-web-appproject">Demo Web App/Project</h4>
<ul>
  <li><a href="https://script.google.com/macros/s/AKfycbwNW6N5ozKbIz_E1HK6yFEUtA8KQrUciS-jcPsQptvIKlARmKgLxbQzNu8ksVeg-BmEfg/exec" target="_blank">Online Demo</a> 初次使用請參考下圖授權(Only For Demo App)：</li>
</ul>

<p><img src="/assets/c008a9e8ceca/1*0cD7NrUr9J9roqIe4Upfzg.webp" alt="" loading="lazy" decoding="async" width="1200" height="1041" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjEwNDEiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><a href="https://script.google.com/home/projects/1CBB39OMedqP9Ro1WSlvgDnMBin4-ksyhgly2h_KrbOuFiPHTalNgwHOp/edit?pli=1" target="_blank"><img src="https://www.gstatic.com/devrel-devsite/prod/v78ce60439c72b9da3632137223a86ae38b78a872a1f6dee1b5c1c8cfa57fe81d/developers/images/opengraph/white.png" alt="" /></a></p>

<p><a href="https://github.com/ZhgChgLi/github-actions-ci-cd-demo/" target="_blank"><img src="https://opengraph.githubassets.com/eb233d74ad1bc6b0eceb9494487579557fb7f17066a3981d20b52fde2c00ef45/ZhgChgLi/github-actions-ci-cd-demo" alt="" /></a></p>

<h3 id="cicd-是什麼">CI/CD 是什麼？</h3>
<h4 id="故事--無-cicd-的開發流程">故事 — 無 CI/CD 的開發流程</h4>

<p>在談 CI/CD 究竟是什麼之前我們先拋開「CI/CD」這個詞，先來回想一下一個初創什麼工作流程都沒有導入的開發團隊，會是怎麼工作的，大致簡略為下圖流程：</p>

<p><img src="/assets/c008a9e8ceca/1*rg4hbs7MsYDU9HZoehrvjQ.webp" alt="" loading="lazy" decoding="async" width="1200" height="687" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjY4NyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<ol>
  <li>產品有一個 Bug，Developer T 從主分支開分支 fix/bug-c 分支進行修正，修正完後 Merge 回主分支。</li>
  <li>緊接著 Developer Z 從主分支開分支 feature/a 做 需求 A，做到一半發現功能怪怪的， <strong>查了一下才發現原來當前功能被改壞、測試也壞了</strong> ，回頭通知 Developer T 進行修正。</li>
  <li>一切開發完畢後，Developer Z <strong>使用他的電腦打包版本給 QA 測試，來來回回修正跟打包</strong> ，最後沒問題後將功能回到主分支。</li>
  <li>很快地也到了 Sprint 尾聲需要打包釋出給使用者；Developer Z <strong>先放下手上工作</strong> ，協助從主分支打包給 QA 進行 Regression 測試，同樣 <strong>來來回回修正問題跟重新打包</strong> ，完成後打包送審 App。</li>
  <li>Apple/Google 審核完畢後發布給使用者使用。</li>
</ol>

<h4 id="問題">問題</h4>

<p>在以上故事中我們可以歸納出兩個大問題。</p>

<p><strong>Question 1: 對當前正確的功能異動無任何統一檢查機制。</strong></p>
<ul>
  <li>不符合 Coding Style 的程式碼也能 Merge</li>
  <li>就算 Build 不起來我也能 Merge</li>
  <li>異動後基本的 Unit Tests、重要檢查項目都沒過也能 Merge</li>
  <li>我的環境功能正確但其他人不一定正確</li>
  <li>影響到其他正在開發的人</li>
</ul>

<p><strong>Question 2: 耗費大量人力時間在打包工作上。</strong></p>
<ul>
  <li>打包要透過工程師人力打包，中斷當前開發工作</li>
  <li>來回在打包與開發之間，心流切換成本極高</li>
  <li>打包等待時間無法進行其他開發工作</li>
  <li>工程師的時間成本就是金錢</li>
  <li>人工操作可能出錯</li>
  <li>QA 要請工程師打包 (來回溝通)</li>
</ul>

<h4 id="ci--continuous-integration-持續整合">CI — Continuous Integration 持續整合</h4>

<p>對應 Question 1「持續整合」旨在確保所有變動都能自動執行統一環境的 Build &amp; Test 確保進入生產環境之前的改動都通過所有測試案例及符合團隊規範 — 「持續地自動確保正確的程式碼整合到生產環境」。</p>

<p>另外也可以增加 Nightly Build、更多的自動化測試環節，確保穩定性。</p>
<h4 id="cd--continuous-delivery--deployment-持續交付部署">CD — Continuous Delivery / Deployment 持續交付/部署</h4>

<p>對應 Question 2「持續部署」旨在確保程式碼在 CI 環節無異常之後，將改動結果自動完成打包部署繁瑣流程給內部測試(QA, Debug, Staging, Beta…)或外部上線(Production, Release…)。</p>
<ul>
  <li><strong>Continuous Deployment:</strong> 全自動直接部署到 Production 環境</li>
  <li><strong>Continuous Delivery:</strong> 只自動部署到 Staging/Debug 環境可，需手動驗證確認沒問題之後才會再部署到 Production 環境</li>
</ul>

<p>依照 App 開發的場景，比較偏向是 <strong>Continuous Delivery 持續交付</strong> ，我們希望 App 最終上線之前是由人工把關確認完全無問題後才發布，確保釋出時間與功能正確性。</p>
<h4 id="故事--透過-cicd-打造穩定高效的開發團隊">故事 — <strong>透過 CI/CD 打造穩定高效的開發團隊</strong></h4>

<p><img src="/assets/c008a9e8ceca/1*rszgT5yFKCcfCUka_t9b3g.webp" alt="" loading="lazy" decoding="async" width="1200" height="824" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjgyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><strong>回頭看我們的故事，導入 CI/CD 後：</strong></p>
<ul>
  <li>CI
所有人的調整都需要經過 自動化測試驗證通過後才能進入主分支、增加 Nightly Build 定時自動化測試環節提升穩定性。</li>
  <li>CD
統一都使用 CD 打包，Developer T 和 Developer Z 可以完全專注在業務開發上、減少人工溝通與操作錯誤。</li>
</ul>

<blockquote>
  <p>團隊工作效率與產品穩定性 🚀🚀🚀🚀🚀</p>
</blockquote>

<h3 id="cicd-的價值">CI/CD 的價值</h3>

<p>結合敏捷開發的核心理念「小步快跑，快速迭代」CI/CD 為其提供了「頻繁持續迭代功能」時的穩定性與工作效率的基石。</p>

<p><strong>自動統一驗證迭代結果</strong></p>
<ul>
  <li>確保所有調整都符合正確的預期結果、不影響其他功能、不影響其他成員</li>
</ul>

<p><strong>自動執行繁瑣的部署流程</strong></p>
<ul>
  <li>讓團隊成員可以專注在主要業務開發、減少人工操作錯誤</li>
</ul>

<h4 id="cicd-的成效">CI/CD 的成效</h4>

<p>回顧 2021 年在 Pinkoi 的演講「 <a href="/posts/pinkoi-engineering/高效率工程團隊大解密-pinkoi-團隊協作與自動化實踐提升工作效率-11f6c8568154/"><strong>2021 Pinkoi Tech Career Talk — 高效率工程團隊大解密</strong></a> 」其實說來說去都是差不多的內容，不外乎就是「自動化、減少人跟人的依賴、專注在主要業務上」導入 CI/CD 也完全符合上述三個方向，藉此我們也可以用同樣的方法來估算成效。</p>

<p><strong>另外有一點需要再次 Highlight 的是 <a href="https://zh.wikipedia.org/wiki/%E5%BF%83%E6%B5%81%E7%90%86%E8%AB%96" target="_blank">心流切換成本</a> ：</strong></p>

<p><img src="/assets/c008a9e8ceca/1*xOzjG-lSiFmdT-C4GHf0JA.webp" alt="" loading="lazy" decoding="async" width="960" height="540" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NjAiIGhlaWdodD0iNTQwIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>當我們持續投入工作一段時間後就會進入「心流」狀態，此時的思緒、生產力都達到巔峰，能提供做好最有效的產出；但如果被打斷，要重新回到心流，又會再需要一段時間，這邊以 30 分鐘為例。</p>

<p><strong>在無 CI/CD 的場景中可能是：</strong> 花很多時間才發現是被改壞再回頭溝通調整(CI)、QA/PM 來請工程師幫忙打包測試版 App (CD)。</p>
<h4 id="cicd-成效估算">CI/CD 成效估算</h4>

<p><img src="/assets/c008a9e8ceca/1*SinoHlHKbtXiRubUS8Bm9Q.webp" alt="團隊人數 6 人 / 一個月來計算" loading="lazy" decoding="async" width="1200" height="790" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijc5MCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>團隊人數 6 人 / 一個月來計算</p>

<p>這邊我們以每個月為基準，假設在無 CI/CD 流程時，每個月會發生 4 次意外改壞主分支的問題，造成後續修正、溝通的成本，約花費 720 分鐘；加上每個月打包測試版、正式版及因人工操作造成錯誤的可能，總和約 1,010 分鐘；以工程師月薪 8 萬計算，每個月就造成約 1 萬 3 的成本浪費。</p>

<blockquote>
  <p><em>這裡漏估了 <a href="https://resources.github.com/actions/2026-pricing-changes-for-github-actions/" target="_blank">2026–03–01 起 Self-hosted 需要支付每分鐘 $0.002 USD 維運費用</a> ，等於執行 500 分鐘需要繳 $1 USD。</em></p>
</blockquote>

<h4 id="cicd-建置成本">CI/CD <strong>建置成本</strong></h4>
<ul>
  <li>人力成本：
照本系列文章建置，粗估需投入人力 1 人花費 10 天 = <strong>4,800 分鐘</strong> 能完成。(~= <strong>NT$36,384</strong> )</li>
  <li>設備與執行成本：
使用 GitHub Actions self-hosted Runner 只需在前期採購 1–2 台 <a href="https://www.apple.com/tw/shop/buy-mac/mac-mini/m4" target="_blank">Mac-Mini</a> 或直接使用現有汰換的 MacBook Pro 就能當成 CI/CD Runner 提供服務使用。
以 6 人團隊採購一台全新的 Mac Mini 為例：32G RAM M4 Mini (= <strong>NT$40,900</strong> )</li>
</ul>

<p>總花費成本約 <strong>NT$80,000</strong> 元就能完成建置，約半年後開始享受效益。</p>

<blockquote>
  <p><strong><em>聲明：</em></strong> <em>這裡只是提出一種效益計算的方式，不一定是最準確的；只是給大家一個概念去延伸， <strong>讓管理決策層能看到 CI/CD 的效益</strong> 進而授權推動完成整個工作流程。</em></p>
</blockquote>

<h3 id="cicd-的工具選擇">CI/CD 的工具選擇</h3>
<h4 id="雲服務-bitrise--xcode-cloud">雲服務 Bitrise / XCode Cloud</h4>
<ul>
  <li><strong>Bitrise：</strong> 最早期主打提供 App CI/CD 的雲服務，第一次接觸 CI/CD 也是使用 Bitrise，它提供友善直覺的步驟編輯工具，可以很快的設定好 App CI/CD 流程。
<strong>缺點：</strong> 最早期是 $99 鎂吃到飽，Apple M 系列處理器剛出來的時候一度改成用量計費( <strong>養套殺</strong> )，那時候估團隊用量每個月至少要花 $500 鎂，索性就遷移至 GitHub Actions 了。
但最近看官網，現在有提供 1 App / 1 Concurrent / 吃到飽 / $89 鎂 / 月。</li>
  <li><strong>XCode Cloud：</strong> 100 小時 / 1 個月 / $50 鎂，優點是與 XCode、App 開發高度整合；但缺點同樣是，無法提供 Android 使用、要客製化一些步驟會有些困難；但如果是純 iOS 的小型 App 我會再次考慮直接使用。</li>
</ul>

<blockquote>
  <p><em>實在很怕 <strong>雲服務又養套殺</strong> 、希望可控性在自己手上，所以改考慮地端服務。</em></p>
</blockquote>

<h4 id="地端服務-jenkins--github-actions--gitlab-cicd">地端服務 Jenkins / GitHub Actions / Gitlab CI/CD</h4>
<ul>
  <li><strong>Gitlab CI/CD：</strong> 
比 GitHub Actions 推出的時間更早、功能更完整，但是我們的專案是託管在 GitHub 上所以就不考慮用 Gitlab CI/CD 了；但是兩邊功能類似，本系列文章會以使用 GitHub Actions 為例。</li>
  <li><strong>GitHub Actions</strong> 
GitHub 2018 年才推出的 CI/CD 服務，跟 GitHub 專案直接綁定，在這幾年持續更新完善功能，有很多封裝好的步驟 ( <a href="https://github.com/marketplace?type=actions" target="_blank">Marketplace</a> ) 可以直接使用；支援 self-hosted runner 可以使用自己的機器無限使用。(等於混合雲了)</li>
  <li><strong>Jenkins：</strong> 
一個專門處理 CI/CD 的開源免費工具、古老但是功能強大；從應用層任務設計、權限管理到底層服務派發執行，Jenkins 全包；同樣有 <a href="https://plugins.jenkins.io/" target="_blank">Plugins</a> 可以直接搭配使用，是早期 DevOps CI/CD 的必備工具。</li>
</ul>

<h3 id="jenkins-vs-github-actions">Jenkins v.s. GitHub Actions</h3>
<h4 id="tldr"><strong>TL;DR</strong></h4>

<blockquote>
  <p><em>在沒有專門做 DevOps 的 App Team，要 App 開發者從 0 到 1 架設維護 Jenkins 環境門檻太高、會的人也不多還會衍生出網路安全問題；選擇直接用 GitHub Actions，App 開發者只需專注在 CI/CD 流程設計，大概掃一下官方文件怎麼撰寫跟啟動 Runner，就能快速搭建出免費穩定又安全的 CI/CD 服務。</em></p>
</blockquote>

<p><img src="/assets/c008a9e8ceca/1*qg8SkzoJqNVvWPxx2Lh6uA.webp" alt="" loading="lazy" decoding="async" width="504" height="434" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1MDQiIGhlaWdodD0iNDM0Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<blockquote>
  <p><em>以下僅以 App CI/CD 架設為出發點做比較，並不適用所有技術場景。</em></p>
</blockquote>

<h4 id="架設與維護難易度-jenkins--github-actions"><strong>架設與維護難易度</strong> Jenkins &gt;&gt;&gt; GitHub Actions</h4>

<p><img src="/assets/c008a9e8ceca/1*k2XGXjV_VZEt618DnDm0lA.webp" alt="" loading="lazy" decoding="async" width="999" height="1117" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5OTkiIGhlaWdodD0iMTExNyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>這邊先用一張不是很專業的結構分層圖來說明兩者的差異，Jenkins 如前述是從上到下功能全包，所以在自架上會複雜許多；而 GitHub Actions，GitHub Actions 本身只需要在 GitHub 上撰寫 YAML 工作流程，本地機器只需要註冊好 GitHub self-hosted Runner (5 個指令就完成)，GitHub 就會自動派發任務給本地機器執行了，其他包涵 Github Actions/Runner 版本升級或是任務派發問題都是 GitHub 負責維護，我們無需處理。</p>

<p>還有另一個比較麻煩的點是 Jenkins 是獨立於 Git 的服務，之間要透過 API (e.g. GitHub API/WebHook) 進行溝通，設定又更複雜了。</p>

<p>之前也從身邊能接觸到的 iOS 開發者(約 30 位)做過調查，懂 Jenkins 的屈指可數( 2 位) 而有在使用 GitHub Actions 的則超過 (10 位) 畢竟就是寫一寫 YAML 就能完成 CI/CD 任務。</p>
<h4 id="學習難易度-jenkins-github-actions">學習難易度 Jenkins »&gt; GitHub Actions</h4>

<p>同上只需參考官方文件學習 GitHub Actions 可用 YAML 指令跟如何在本地起自己的 Runner 即可。</p>
<h4 id="穩定性-github-actions--jenkins">穩定性 GitHub Actions &gt; Jenkins</h4>

<p>這點我覺得 GitHub Actions 略勝 Jenkins。</p>

<p>Jeknins 有機會因為系統升級或裝到有衝突的 Plugin 導致服務崩潰 (不過如果跑得好好的不去動它基本上沒有問題)。</p>

<p>GitHub Actions 受限於 <a href="https://www.githubstatus.com/" target="_blank">GitHub 服務狀態</a> (如果 GitHub 掛也會跟著掛)，但是發生頻率不高，平均在線率都能維持在 99.9%；真出問題也不用處理，坐等修復。</p>
<h4 id="安全性-github-actions--jenkins">安全性 GitHub Actions &gt; Jenkins</h4>

<p>考量到 GitHub Actions/Runner 服務本身是 GitHub 在維護跟自動更新這點可能會比 Jenkins 需要手動更新來的更安全。</p>

<p>另外 Jenkins 跟 GitHub 溝通之間需要開 API/WebHook 口相對比較危險，GitHub 與 GitHub Actions 之間是無痛整合、GitHub Actions 與 self-hosted Runner 之間是觀察者模式，self-hosted Runner 會去跟 GitHub 要任務回來做，所以 Runner 本身不需開對外接口。</p>

<p>但如果是全封閉網路環境，Jenkins 會比 GitHub Actions 安全。</p>
<h4 id="權限控管-jenkins-github-actions">權限控管 Jenkins »&gt; GitHub Actions</h4>

<p>這點需要特別挑出來比較，Jenkins 可以另外設定帳號登入權限進行控管；GitHub Actions 則直接與 GitHub Repo 進行綁定，要有 Repo 權限的人才能使用。</p>

<blockquote>
  <p><em>*因此後面的文章才會又用 GAS Web App 搭建跨團隊的操作平台。</em></p>
</blockquote>

<h4 id="使用廣度-jenkins-github-actions">使用廣度 Jenkins »&gt; GitHub Actions</h4>

<p>在有完整 DevOps Team 的團隊無庸置疑的應該還是會選擇 Jenkins，畢竟在其他領域 (例如 Web, 後端, Java…) 還是 Jenkins 運行最久、Plugin 做多最好用，並且可以統一建置一套 CI/CD 服務所有團隊使用方便控管，或是後端部署完前端自動部署這種複雜的 CI/CD 場景。</p>

<blockquote>
  <p><em>*GitHub Actions 後來也支援跨 Repo Actions/Runner 了。</em></p>
</blockquote>

<h4 id="第三方套件豐富度-jenkins--github-actions">第三方套件豐富度 Jenkins &gt; GitHub Actions</h4>

<p>就數量上 GitHub Actions 大於 Jenkins，但是 Jenkins 理的 CI/CD 功能比較有深度跟強大，GitHub Actions 很多只是自動化的功能。</p>
<h4 id="功能深度-jenkins-github-actions">功能深度 Jenkins »&gt; GitHub Actions</h4>

<p>這點沒法比，Jenkins 已做了快 20 年，GitHub Actions 還有很多功能需要再補上；例如：權限管理、Secret 管理(目前只限純文字，密鑰檔案的話要先轉成純文字)、Cache/Artifact 目前只支援 Cloud…等等</p>

<p><strong>擴展上，GitHub Self-hosted Runner 也支援 <a href="https://docs.github.com/en/actions/concepts/runners/about-actions-runner-controller#scaling-runners" target="_blank">Docker or k8s</a> 。</strong></p>
<h4 id="客製化深度-jenkins-github-actions">客製化深度 Jenkins »&gt; GitHub Actions</h4>

<p>Jenkins 從頭到尾都掌控在自己手上，客製化的權限比較大，可以影響到整個系統，GitHub Actions 只能在應用層客製化不同步驟而已。</p>

<p>例如目前 GitHub Actions 內建的 Artifacts 不支援 self-hosted，那也只能在步驟中改成 sh copy 到其他目錄，無法自行客製化實現 Artifacts。</p>

<blockquote>
  <p><em>*App CI/CD 場景也用不到太深度的功能。</em></p>
</blockquote>

<h4 id="易用性-github-actions-jenkins">易用性 GitHub Actions »&gt; Jenkins</h4>

<p>介面上 GitHub Actions 是新的工具在介面使用上比 Jenkins 更容易使用； 腳本設定上 Jenkins 是 Pipeline Script 儲存在 Jenkins 上、GitHub Actions 是 YAML 檔跟著專案 Git 管理，也比 Jenkins 容易設定。</p>
<h4 id="費用風險-jenkins--github-actions">費用風險 Jenkins &gt; GitHub Actions</h4>

<p>Jenkins 全開源免費權掌握在手，GitHub Actions 部分開源但任務派發與執行是 GitHub 封閉的 SAAS 服務；目前政策是 GitHub Actions 完全免費，使用 GitHub Runner 才要付費、使用 self-hosted Runner <a href="https://resources.github.com/actions/2026-pricing-changes-for-github-actions/" target="_blank">自 2026-03-01 起也需要支付每分鐘 $0.002 USD 維運費用</a> ；以上收費 Public Repo 都有免費額度。</p>
<h3 id="google-apps-script-web-app-的用途與為何選擇">Google Apps Script Web App 的用途與為何選擇</h3>

<p>另一個工具選擇是 Google Apps Script Web App，會需要多一個這個的原因是 GitHub Actions 自帶的表單功能過於陽春(介面太工程、只能靜態)並且執行權限跟 GitHub Repo 綁定在一起，如果我們需要提供給其他職能夥伴使用會非常的麻煩。</p>

<p><strong>如下：</strong></p>

<p><img src="/assets/c008a9e8ceca/1*kULMefCX5D6I5z9xt71A5A.webp" alt="" loading="lazy" decoding="async" width="348" height="447" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzNDgiIGhlaWdodD0iNDQ3Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>CD 打包會希望讓操作人填寫一些資訊，例如 Release Notes。</p>

<p>因此我們需要一個「介面」工具提供給其他夥伴使用或甚至是自己工程師更方便的使用。</p>

<p><strong>所需情境：</strong></p>

<blockquote>
  <p><em>在這個比較好用的「介面」上填寫需要的資訊，串接專案管理工具(e.g. Jira, Asana) 取得 Task 或是 GitHub 直接取得 PR 列表，直接下拉選單選擇後送出，再透過 GitHub API 觸發 GitHub Actions 進行打包。</em></p>
</blockquote>

<h4 id="slack">Slack</h4>

<p>第一次導入 CI/CD 的時候我們選擇串接 Slack API 達成類似如下效果：</p>

<p><img src="/assets/c008a9e8ceca/1*m85bTTlrwAmCxoVqVXo2fg.webp" alt="https://slack.com/intl/zh-tw/blog/productivity/workflow-builder-tools-automation-examples" loading="lazy" decoding="async" width="647" height="497" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2NDciIGhlaWdodD0iNDk3Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><a href="https://slack.com/intl/zh-tw/blog/productivity/workflow-builder-tools-automation-examples" target="_blank">https://slack.com/intl/zh-tw/blog/productivity/workflow-builder-tools-automation-examples</a></p>
<ul>
  <li>夥伴可以直接在 Slack 中使用表單填寫資訊、並觸發 CD 打包、收到 Slack 通知</li>
</ul>

<p>使用操作起來很順暢並且統一在日常的辦公工具上(SSOT) 不需重新學習；但是他背後的問題是 <strong>開發跟維護成本非常的高</strong> ，其中有一個原因是 Slack Outgoing-Webhook API 對響應要求非常高 (需要在 3 秒內)，因此基本上可以直接排除使用 FAAS 服務簡單串接了 (e.g. Cloud Functions, GAS, Lambda…)。</p>

<p><strong>之前的做法是有一位對自動化跟後端很有興趣的夥伴自己用 Kotlin+ktor 開發了一整套後端服務，然後再 GCP 起伺服器供 Slack 串接使用。</strong></p>

<blockquote>
  <p><strong><em>開發與維護成本非常非常高而且很難交接。</em></strong></p>
</blockquote>

<h4 id="google-apps-script--web-app">Google Apps Script — Web App</h4>

<p>之前有分享過「 <a href="/posts/zrealm-dev/google-apps-script-web-app-打造-github-action-ci-cd-自訂表單提升開發效率-4cb4437818f2/">使用 Google Apps Script Web App 表單串接 Github Action CI/CD 工作</a> 」:</p>

<p><img src="/assets/c008a9e8ceca/1*Gr4PnV2J2AB9cVFuXMLjcA.webp" alt="" loading="lazy" decoding="async" width="1200" height="596" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjU5NiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/c008a9e8ceca/1*NJRcY2ULVylZlsKnBtM27A.webp" alt="Demo Web App Form URL" loading="lazy" decoding="async" width="428" height="559" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0MjgiIGhlaWdodD0iNTU5Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><a href="https://script.google.com/macros/s/AKfycbwNW6N5ozKbIz_E1HK6yFEUtA8KQrUciS-jcPsQptvIKlARmKgLxbQzNu8ksVeg-BmEfg/exec" target="_blank">Demo Web App Form URL</a></p>

<p><strong>使用 Google Apps Script — Web App 的優勢是：</strong></p>
<ul>
  <li>網站 Web</li>
  <li>同 Google Workspace 企業帳號權限管理，可設定只有組織內的 Google 帳號才能存取</li>
  <li><strong>完全免費</strong></li>
  <li><strong>Function as a Service 不需自行架設維護伺服器</strong></li>
  <li>較容易維護與交接</li>
  <li>手機上可以操作</li>
  <li><strong>AI Can Help!</strong> 
<strong>不管是 ChatGPT 或其他 AI 工具對 GAS 都非常熟悉，可以直接動口請他幫我們製作打包表單與串接 GitHub API</strong></li>
  <li>一樣能串接 Jira,Asana,Slack 通知 API</li>
</ul>

<p>第二推廣我就改用 GAS Web App 給夥伴使用，同樣得到很好的反響，跟 Slack 差別就只在要多記一個網址到書籤，需要打包的時候打開網址從網頁表單操作打包。</p>
<h3 id="app-cicd-完整工具流程">App CI/CD 完整工具流程</h3>

<p>這邊先附上完整的工作流程，下篇開始會逐步介紹每個工具如何使用與串接。</p>

<p><img src="/assets/c008a9e8ceca/1*pQ-2Jj6s2qlvwTrLghJSjg.webp" alt="" loading="lazy" decoding="async" width="1200" height="573" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjU3MyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<h4 id="工具角色">工具角色：</h4>
<ul>
  <li><strong>GitHub Actions</strong> : CI/CD 邏輯腳本程式碼</li>
  <li><strong>GitHub Actions — Self-hosted Runner</strong> : CI/CD 實際執行的地方，使用自己架設的 Runner 執行，只需要負擔機器購買成本，就能無限制使用量的執行任務。</li>
  <li><strong>Google Apps Script Web App</strong> : 因打包不一定是工程師負責，需要有一個平台給跨職能夥伴可以使用； GAS Web App 能快速打造一個 Web 工具分享網址給其他人操作使用。</li>
  <li><strong>Asana/Jira</strong> : 專案管理工具，可與 GAS Web App 串接讓 QA/PM 可以直接選擇想把包的 Task 進行打包。</li>
  <li><strong>Slack</strong> : 負責接收執行結果通知</li>
</ul>

<h4 id="場景">場景：</h4>
<ul>
  <li>End-User (QA/PM/PD/Developer): 使用 GAS Web App 送出打包表單 (撈取 Jira or Asana 任務單對應的 Branch) -&gt; GAS 打 GitHub API -&gt; 觸發 CD 打包 Github Actions &lt;- GitHub self-hosted runner 監聽到任務拉回機器執行 -&gt; 執行完畢 Slack 通知、更新 GAS Web App 打包狀態。</li>
  <li>End-User (Developer): 開 PR、Push new commit 到 PR -&gt; 觸發 CI 測試流程 &lt;- GitHub self-hosted runner 監聽到任務拉回機器執行 -&gt; 執行完畢 Comment 測試結果、更新 Checks。</li>
</ul>

<h3 id="總結">總結</h3>

<p>本篇主要是帶大家初步了解 CI/CD 是什麼跟帶來的效益，下一篇開始會進入技術環節，手把手帶您了解、實作 GitHub Actions CI/CD 到最終完成前文的最終成果。</p>
<h3 id="系列文章">系列文章：</h3>
<ul>
  <li><a href="/posts/zrealm-dev/ci-cd-是什麼-打造高效穩定開發團隊的關鍵流程與工具選擇-c008a9e8ceca/"><strong>CI/CD 實戰指南（一）：CI/CD 是什麼？如何透過 CI/CD 打造穩定高效的開發團隊？工具選擇？</strong></a></li>
  <li><a href="/posts/zrealm-dev/github-actions-self-hosted-runner-詳解與實戰案例-ci-cd-自動化工作流程建置-404bd5c70040/"><strong>CI/CD 實戰指南（二）：GitHub Actions 與 self-hosted Runner 使用與建置大全</strong></a></li>
  <li><a href="/posts/zrealm-dev/github-actions-ios-ci-cd-完整實作自動化建置-測試與部署流程教學-4b001d2e8440/"><strong>CI/CD 實戰指南（三）：使用 GitHub Actions 實作 App 專案的 CI 與 CD 工作流程</strong></a></li>
  <li><a href="/posts/zrealm-dev/ci-cd-打包工具平台-使用-google-apps-script-web-app-串接-github-actions-提升跨團隊效率-4273e57e7148/"><strong>CI/CD 實戰指南（四）：使用 Google Apps Script Web App 串接 GitHub Actions 建置免費易用的打包工具平台</strong></a></li>
</ul>

<h4 id="-buy-me-a-beer-on-paypal"><a href="https://www.paypal.com/ncp/payment/CMALMPT8UUTY2" target="_blank">🍺 Buy me a beer on PayPal</a></h4>

<blockquote>
  <p><a href="https://www.paypal.com/ncp/payment/CMALMPT8UUTY2" target="_blank"><strong><em>本系列文章花費了大量的時間精力撰寫，如果內容對您有幫助、對您的團隊有實質提升工作效率與產品品質；歡迎請我喝杯咖啡，感謝支持！</em></strong></a></p>
</blockquote>

<p><img src="/assets/c008a9e8ceca/1*QJj54G9gOjtQS-rbHVT1SQ.webp" alt="Buy me a coffee" loading="lazy" decoding="async" width="700" height="700" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3MDAiIGhlaWdodD0iNzAwIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><a href="https://www.paypal.com/ncp/payment/CMALMPT8UUTY2" target="_blank">🍺 Buy me a beer on PayPal</a></p>]]></content>
  </entry><entry>
    <title type="html">XCode 升級必測：Build Settings 優化等級調整快速定位 Release 版幽靈閃退問題｜iOS 開發實戰技巧</title>
    <link href="https://zhgchg.li/posts/zrealm-dev/xcode-%E5%8D%87%E7%B4%9A%E5%BF%85%E6%B8%AC-build-settings-%E5%84%AA%E5%8C%96%E7%AD%89%E7%B4%9A%E8%AA%BF%E6%95%B4%E5%BF%AB%E9%80%9F%E5%AE%9A%E4%BD%8D-release-%E7%89%88%E5%B9%BD%E9%9D%88%E9%96%83%E9%80%80%E5%95%8F%E9%A1%8C-ios-%E9%96%8B%E7%99%BC%E5%AF%A6%E6%88%B0%E6%8A%80%E5%B7%A7-7508328d8b8d/" rel="alternate" type="text/html" title="XCode 升級必測：Build Settings 優化等級調整快速定位 Release 版幽靈閃退問題｜iOS 開發實戰技巧" />
    <published>2025-04-11T22:50:31+08:00</published>
    <updated>2025-04-11T22:50:31+08:00</updated>
    <id>https://zhgchg.li/posts/zrealm-dev/7508328d8b8d</id><summary type="html">iOS 開發者遇到 Release 版才出現的閃退或邏輯異常，藉由將 Debug 優化等級調整與 Release 同步，快速在本地復現問題，節省測試時間並精準定位 XCode 優化造成的 Bug，提升發佈穩定度與維護效率。</summary><author>
      <name>ZhgChgLi</name>
    </author><category term="ZRealm Dev." /><category term="ios-app-development" /><category term="xcode" /><category term="build-settings" /><category term="swift" /><category term="troubleshooting" /><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://zhgchg.li/assets/7508328d8b8d/1*j4gTyeQwM-T7Ad3Fi29saQ.webp" /><content type="html" xml:base="https://zhgchg.li/posts/zrealm-dev/xcode-%E5%8D%87%E7%B4%9A%E5%BF%85%E6%B8%AC-build-settings-%E5%84%AA%E5%8C%96%E7%AD%89%E7%B4%9A%E8%AA%BF%E6%95%B4%E5%BF%AB%E9%80%9F%E5%AE%9A%E4%BD%8D-release-%E7%89%88%E5%B9%BD%E9%9D%88%E9%96%83%E9%80%80%E5%95%8F%E9%A1%8C-ios-%E9%96%8B%E7%99%BC%E5%AF%A6%E6%88%B0%E6%8A%80%E5%B7%A7-7508328d8b8d/"><![CDATA[<h3 id="通靈筆記-xcode-升級時最好測一下的事">[通靈筆記] XCode 升級時最好測一下的事…</h3>

<p>遇到僅在 Build Configuration Release (正式版、線上版)才會出現的幽靈閃退或程式邏輯問題，但是 Debug 安然無恙。</p>

<p><img src="/assets/7508328d8b8d/1*j4gTyeQwM-T7Ad3Fi29saQ.webp" alt="Photo by Tommaso Pecchioli" loading="lazy" decoding="async" width="1400" height="933" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjkzMyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>Photo by <a href="https://unsplash.com/@pecchio?utm_content=creditCopyText&amp;utm_medium=referral&amp;utm_source=unsplash" target="_blank">Tommaso Pecchioli</a></p>
<h3 id="tldr">TL;DR</h3>

<p>使用新 XCode 打包發佈專案之前，除了直接 Build &amp; Run 玩玩看有沒有跑版或異常， <strong>請記得也試試看</strong> ：</p>
<ol>
  <li>App Target</li>
  <li>選擇 <code class="language-plaintext highlighter-rouge">Build Settings</code></li>
  <li>搜尋 <code class="language-plaintext highlighter-rouge">Optimization Level</code></li>
  <li>找到 <code class="language-plaintext highlighter-rouge">Optimization Level</code> 區塊</li>
  <li>將 <code class="language-plaintext highlighter-rouge">Debug</code> 環境也設定同 <code class="language-plaintext highlighter-rouge">Release</code> 的值 (e.g. <code class="language-plaintext highlighter-rouge">Fastest, Smallest [-Os]</code> )</li>
  <li>Build &amp; Run 測看看有沒有異常</li>
</ol>

<p><img src="/assets/7508328d8b8d/1*CUqYYFVjyXtxGkMlyd0Suw.webp" alt="" loading="lazy" decoding="async" width="832" height="401" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4MzIiIGhlaWdodD0iNDAxIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>不選擇直接打包上 Testflight 測試是為了方便我們遇到問題的時候，能即時用斷點找到問題根源。</p>

<blockquote>
  <p><em>如果遇到使用者在 Release (正式版、線上版) 回報的問題(閃退或行為異常)，但開發者無法在本地復現，也可以試試改這個設定在本地試試看。</em></p>
</blockquote>

<h4 id="可能出現的問題"><strong>可能出現的問題</strong></h4>
<ul>
  <li>程式上看正確，但結果異常</li>
  <li>程式上看不會閃退的地方，卻會閃退</li>
</ul>

<p>以上在 Debug 環境 <code class="language-plaintext highlighter-rouge">Optimization Level = None [-O0]</code> 都是正常的，只會出現在 <code class="language-plaintext highlighter-rouge">Optimization Level = Fastest, Smallest [-Os]</code> 也就是 Release 的設定值。</p>
<h4 id="解法">解法</h4>

<p><strong>有問題的話，問題多半跟開發者無關</strong> ；是 XCode 優化的 Bug 導致，若一定要用這版 XCode 打包那只能先自己調整程式 Workaround，等待新版 XCode 出來再測看看是否正常。</p>

<blockquote>
  <p><strong><em>不建議直接把 Release 改成 None，因為可能會有更多其他問題。</em></strong></p>
</blockquote>

<h3 id="説故事時間">説故事時間</h3>

<p>以下是這幾年工作中實際被這坑坑過的問題場景。</p>
<h4 id="故事-1--app-一直跳邀請評價-app-popup">故事 1 — App 一直跳邀請評價 App Popup</h4>

<p>我們的 App 之前有一個功能是打開 App 時會「邀請使用者去應用商城給評價」規則是跳三次之後就不會在跳；但是收到很多使用者回報每次開 App 都會跳，持續很久了，一直問一直問很煩。</p>

<p>但是我們從 Code 上看跟在本地 Build &amp; Run 在模擬器或實機上都沒問題，也試過各種 edge case 場景都無法復現；我甚至撰寫了一個 UI Test 瘋狂重跑路徑、清除資料重試…跑了幾千次都沒遇到問題。</p>

<p>記得那次我苦惱到半夜三點多，萬念俱灰實在想不到是什麼問題，開始漫無目的的查看專案設定，突然靈機一動想說那把 <code class="language-plaintext highlighter-rouge">Build Settings</code> 都改成 <code class="language-plaintext highlighter-rouge">Release</code> 的值試試看好了，這才發現問題在 <code class="language-plaintext highlighter-rouge">Optimization Level = Fastest, Smallest [-Os]</code> 能重現，也因此才定位到出問題的位置。</p>

<p><strong>Pseudocode</strong></p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">var</span> <span class="nv">invitedTimes</span> <span class="o">=</span> <span class="mi">0</span> <span class="c1">//  Loaded from UserDefaults; will be saved back after update</span>
<span class="kd">func</span> <span class="nf">requestAppStoreReviewIfNeeded</span><span class="p">()</span> <span class="p">{</span>
  <span class="k">defer</span> <span class="p">{</span>
    <span class="n">invitedTimes</span> <span class="o">+=</span> <span class="mi">1</span> <span class="c1">// Works for now, but may have unintended side effects</span>
  <span class="p">}</span>

  <span class="k">guard</span> <span class="n">invitedTimes</span> <span class="o">&lt;</span> <span class="mi">3</span> <span class="k">else</span> <span class="p">{</span>
    <span class="k">return</span>
  <span class="p">}</span>
  
  <span class="k">self</span><span class="o">.</span><span class="nf">present</span><span class="p">(</span><span class="kt">AppStoreReviewRequestAlert</span><span class="p">())</span>
<span class="p">}</span>
</code></pre></div></div>

<blockquote>
  <p><em>這段程式是前人開發的，從程式上看這段程式碼雖然有 side effect，可是邏輯無問題、可以正常 Compile、在執行上之前的版本也都沒有問題。</em></p>
</blockquote>

<p>但當我把 <code class="language-plaintext highlighter-rouge">Optimization Level = Fastest, Smallest [-Os]</code> 之後下斷點 Print 值就發現異常了， <code class="language-plaintext highlighter-rouge">invitedTimes += 1</code> 之後會直接爆炸變成 <code class="language-plaintext highlighter-rouge">-24760045646797946</code> 一個極大的負數，也因此使用者每次都會跳邀請評價。</p>

<p>當時先直接改掉這邊 defer 的寫法，就再也沒有使用者回報類似的問題了；後來回頭測試後續的 XCode 版本，相同的寫法、 <code class="language-plaintext highlighter-rouge">Optimization Level = Fastest, Smallest [-Os]</code> 也能正常運作了。</p>
<h4 id="故事-2--某個頁面會直接閃退">故事 2 — 某個頁面會直接閃退</h4>

<p>Release (Testflight) 版在內測的時候發現有一個頁面(WebView) 只要一點擊就會閃退，可是工程師怎麼 Build &amp; Run 在模擬器或實機上都沒問題；每每猜測一個問題點就打包一個埋 Log 或是嘗試修正的版本上 Testflight 測試，非常痛苦費時；這時又喚起我上次被支配的恐懼，立刻請同事把本地的設定改成 <code class="language-plaintext highlighter-rouge">Optimization Level = Fastest, Smallest [-Os]</code> 然後在 Build &amp; Run 果然在本地復現了閃退的問題點。</p>

<p>主要問題是出在我們自己特化的 WebView Obj-c Code 中有一個變數在 <code class="language-plaintext highlighter-rouge">ptimization Level = Fastest, Smallest [-Os]</code> 時會變 null，原因不明，只能先多加判斷保護；在之前版本也都正常，只能等新的 XCode 推出後再試試看是否正常。</p>
<h3 id="總結">總結</h3>

<p>其實不只被這個坑坑了兩次，有些已經不記得了，總之就是留個心法：</p>
<ol>
  <li>使用新 XCode 版本第一次打包上版時，最好測一下這個</li>
  <li>問題只發生在 Release (正式版、線上版)，基本上就是這個問題，可以直接改設定在本地看能不能復現</li>
</ol>]]></content>
  </entry><entry>
    <title type="html">Medium 1,000 追蹤者里程碑｜自學 iOS 開發與持續分享經驗的成長歷程</title>
    <link href="https://zhgchg.li/posts/zrealm-dev/medium-1-000-%E8%BF%BD%E8%B9%A4%E8%80%85%E9%87%8C%E7%A8%8B%E7%A2%91-%E8%87%AA%E5%AD%B8-ios-%E9%96%8B%E7%99%BC%E8%88%87%E6%8C%81%E7%BA%8C%E5%88%86%E4%BA%AB%E7%B6%93%E9%A9%97%E7%9A%84%E6%88%90%E9%95%B7%E6%AD%B7%E7%A8%8B-6fd11e9704f2/" rel="alternate" type="text/html" title="Medium 1,000 追蹤者里程碑｜自學 iOS 開發與持續分享經驗的成長歷程" />
    <published>2025-04-03T20:51:56+08:00</published>
    <updated>2025-04-03T20:51:56+08:00</updated>
    <id>https://zhgchg.li/posts/zrealm-dev/6fd11e9704f2</id><summary type="html">累積超過 800 小時與 112 篇文章，從自學 iOS 開發到達成 Medium 1,000 追蹤者，透過持續分享實戰經驗與教學內容，幫助開發者突破學習瓶頸，實現穩定成長與影響力提升。</summary><author>
      <name>ZhgChgLi</name>
    </author><category term="ZRealm Dev." /><category term="ios-app-development" /><category term="medium" /><category term="writer" /><category term="followers" /><category term="milestones" /><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://zhgchg.li/assets/6fd11e9704f2/1*OMahkVPjT8XjSrYpDdpu-g.webp" /><content type="html" xml:base="https://zhgchg.li/posts/zrealm-dev/medium-1-000-%E8%BF%BD%E8%B9%A4%E8%80%85%E9%87%8C%E7%A8%8B%E7%A2%91-%E8%87%AA%E5%AD%B8-ios-%E9%96%8B%E7%99%BC%E8%88%87%E6%8C%81%E7%BA%8C%E5%88%86%E4%BA%AB%E7%B6%93%E9%A9%97%E7%9A%84%E6%88%90%E9%95%B7%E6%AD%B7%E7%A8%8B-6fd11e9704f2/"><![CDATA[<h3 id="a-milestone-of-1000-followers-on-medium"><strong>A Milestone of 1,000 Followers On Medium</strong></h3>

<p>經營 Medium 七年，終於達成 1,000 追蹤者里程碑！</p>

<p><img src="/assets/6fd11e9704f2/1*OMahkVPjT8XjSrYpDdpu-g.webp" alt="" loading="lazy" decoding="async" width="1200" height="556" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjU1NiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<blockquote>
  <p><em>English version down below.</em></p>
</blockquote>

<p>經營 Medium 7 年、累積 112 篇文章、約 13 萬字，投入超過 800 小時，終於在 2025 年 4 月 2 日達成了夢想中的目標 — — 1,000 Followers。</p>

<p>說來話長，從 2018 年開始，自己從 Web 轉戰 App 開發，一切對我來說都充滿新鮮感，總是抱著求知若渴的心態，不斷自行查閱資料，邊學邊做，最終完成並上架了自己的第一個 App 作品。</p>

<p>由於完全自學，當時從許多前輩們留下的網路文章中獲益良多，也因此萌生了撰寫文章「教學相長」的念頭，希望自己也能創造出一個良善的循環。</p>

<p>一開始，也曾陷入「分享迷思」，總擔心內容不夠完整、擔心被人挑惕，更怕對讀者毫無幫助。
但後來轉念一想，寫文章就像是在種下一顆種子，總有一天，一位遇到類似問題的開發者能從我的文章中得到幫助，或者藉此進一步深入探索。
即使寫的內容不一定會被看見，但若不開始，這些美好的可能性就無法發生，也因為這樣不斷地堅持寫作，才逐漸提升了自己的能力，讓文章內容更加豐富，更有機會幫助到他人。</p>

<p>那段期間正好搭上時代的順風車，App 與軟體工程蓬勃發展。日常持續學習、開發新功能，並且將自己的經驗轉化為文章不斷分享出去；同時，也有源源不絕的新鮮人加入開發行列，因此每次發文總能獲得新的追蹤者。</p>

<p>近幾年，隨著產業降溫、AI 對數位內容創作領域的衝擊，以及 Medium SEO 政策的變化，新追蹤人數的增長明顯放緩；對於達成「1K 目標」開始抱持佛系心態，但內心依舊保有最初的分享精神，繼續堅持記錄並協助遇到相似問題的朋友。
此外也開始分享一些日常生活、旅行遊記、開箱文，以及個人興趣的自動化（RPA）相關內容。</p>

<p>1K 只是個里程碑，未來還請大家多多指教。
不論之後是否繼續撰寫 iOS 開發內容，都會持續分享自己所經歷的問題和所學到的經驗。</p>

<p>由衷感謝大家這 7 年來的支持與陪伴！</p>

<p><strong>Harry,</strong> 
<strong>2025/04/02.</strong></p>
<h4 id="english">English</h4>

<p>After seven years on Medium, publishing 112 articles, approximately 130,000 words, and investing over 800 hours, I finally reached my long-awaited milestone of 1,000 followers on April 2, 2025.</p>

<p>It’s been quite a journey. Back in 2018, when I switched my focus from web development to app development, everything felt new and exciting. With an insatiable curiosity, I constantly searched for resources, learned by doing, and eventually completed and published my first app.</p>

<p>As someone entirely self-taught, I gained a tremendous amount from online articles shared by many seasoned developers. This inspired me to begin writing myself, driven by the idea of “learning by teaching,” hoping to foster a positive cycle of knowledge-sharing.</p>

<p>Initially, I was caught up in doubts about sharing — always concerned whether my content was thorough enough, whether I’d be criticized, or even worse, whether it provided no real value to readers.</p>

<p>Eventually, I shifted my mindset. Writing is like planting a seed — someday, a developer encountering a similar problem might find help or inspiration through my posts. Even if my articles weren’t immediately seen, if I never started writing, those possibilities would never materialize. With continuous writing and persistence, I gradually improved my skills, enriching my content and increasing its potential to benefit others.</p>

<p>Fortunately, I rode the wave of rapid growth in apps and software engineering. Day after day, I learned, built new features, and shared my experiences through writing. There was also a steady influx of new developers entering the field, and each article brought new followers.</p>

<p>In recent years, growth has notably slowed, impacted by industry cooling, AI-driven changes to digital content creation, and shifts in Medium’s SEO policies. Gradually, I adopted a more relaxed approach towards reaching the 1,000-follower mark. Yet, I never lost the initial passion for sharing and continued documenting my experiences to help those encountering similar issues. Additionally, I started sharing daily life stories, travel experiences, product reviews, and content about my personal interest in automation (RPA).</p>

<p>Reaching 1,000 followers is just a milestone, and I look forward to your continued support in the future. Regardless of whether I continue writing about iOS development, I’ll keep sharing the problems I encounter and the knowledge I gain.</p>

<p>I sincerely thank everyone for your support and companionship over these past seven years!</p>

<p><strong>Harry,</strong> 
<strong>2025/04/02.</strong></p>]]></content>
  </entry><entry>
    <title type="html">Google Apps Script｜快速串接 Firebase App Distribution API 教學與進階設定</title>
    <link href="https://zhgchg.li/posts/zrealm-robotic-process-automation/google-apps-script-%E5%BF%AB%E9%80%9F%E4%B8%B2%E6%8E%A5-firebase-app-distribution-api-%E6%95%99%E5%AD%B8%E8%88%87%E9%80%B2%E9%9A%8E%E8%A8%AD%E5%AE%9A-71400d408dc8/" rel="alternate" type="text/html" title="Google Apps Script｜快速串接 Firebase App Distribution API 教學與進階設定" />
    <published>2025-03-20T22:12:48+08:00</published>
    <updated>2025-07-10T21:00:24+08:00</updated>
    <id>https://zhgchg.li/posts/zrealm-robotic-process-automation/71400d408dc8</id><summary type="html">解決 Google Apps Script 串接 Firebase App Distribution API 權限與 GCP 專案設定問題，掌握 OAuthScopes、進階服務啟用步驟，10 行程式碼輕鬆完成下載連結整合，提升 CI/CD 自動化效率。</summary><author>
      <name>ZhgChgLi</name>
    </author><category term="ZRealm Robotic Process Automation" /><category term="ios-app-development" /><category term="firebase-app-distribution" /><category term="google-apps-script" /><category term="google-api" /><category term="google-cloud-platform" /><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://zhgchg.li/assets/71400d408dc8/1*O6JWKIIS5oxyrOS3q9NXRQ.webp" /><content type="html" xml:base="https://zhgchg.li/posts/zrealm-robotic-process-automation/google-apps-script-%E5%BF%AB%E9%80%9F%E4%B8%B2%E6%8E%A5-firebase-app-distribution-api-%E6%95%99%E5%AD%B8%E8%88%87%E9%80%B2%E9%9A%8E%E8%A8%AD%E5%AE%9A-71400d408dc8/"><![CDATA[<h3 id="google-apps-script-x-google-apis-快速串接整合方式">Google Apps Script x Google APIs 快速串接整合方式</h3>

<p>以 Google Apps Script x Firebase App Distribution API 串接為例</p>

<h3 id="背景">背景</h3>

<p>之前寫了好幾篇關於使用 Google Apps Script 的文章，其中「 <a href="/posts/zrealm-robotic-process-automation/google-apps-script-打造每日數據報表自動化-rpa-快速串接-google-analytics-與-google-sheet-f6713ba3fee3/">使用 Google Apps Script 實現每日數據報表 RPA 自動化</a> 」、「 <a href="/posts/zrealm-robotic-process-automation/ga4-自動數據通知機器人-3-步驟用-google-apps-script-串接-telegram-bot-1e85b8df2348/">簡單 3 步驟 — 打造免費 GA4 自動數據通知機器人</a> 」介紹了如何使用 Google Apps Script 串接 Google Analytics 和 Google Sheet, Web App, Slack, Telegram… 快速建置可視化數據中台及通知服務；另外上個月發的文章「 <a href="/posts/zrealm-dev/google-apps-script-web-app-打造-github-action-ci-cd-自訂表單提升開發效率-4cb4437818f2/">使用 Google Apps Script Web App 表單串接 Github Action CI/CD 工作</a> 」則是直接使用 Google Apps Script Web App 串接 Github API 作為 CI/CD GUI 表單服務；以上要嘛是直接使用 Google Apps Script 內建的服務進行串接或是直接串接外部服務 (Slack, Github…) 都沒碰到需要串接 Google APIs 的場景。</p>

<blockquote>
  <p><em>這次在優化 CI/CD GUI 表單時希望打包內測版後能在 Web App 上直接顯示 Firebase Distribution 下載連結，這塊就只能串接 Google APIs 才能達成。</em></p>
</blockquote>

<h4 id="google-apps-script-x-firebase-app-distribution-api-v1">Google Apps Script x <a href="https://firebase.google.com/docs/reference/app-distribution/rest/" target="_blank">Firebase App Distribution API v1</a></h4>

<p><img src="/assets/71400d408dc8/1*O6JWKIIS5oxyrOS3q9NXRQ.webp" alt="" loading="lazy" decoding="async" width="771" height="740" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NzEiIGhlaWdodD0iNzQwIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>如上圖，與以往串接 Google Analytics 有內建服務「AnalyticsData」不同，Firebase App Distribution 並沒有提供內建串接服務，所以要用進階方式進行串接。</p>

<blockquote>
  <p><em>本來以為要自行使用 Service Account 完成 Access Token 產生、交換（有點麻煩，可參考我之前的 <a href="https://github.com/ZhgChgLi/ZReviewTender/blob/main/lib/GoogleAPI.rb" target="_blank">評價機器人開源專案</a> ）， <strong>但其實不用這麼麻煩。</strong></em></p>
</blockquote>

<h3 id="google-apps-script-x-google-apis-進階服務串接">Google Apps Script x Google APIs 進階服務串接</h3>
<h4 id="串接設定">串接設定</h4>

<p><strong>參考 <a href="https://developers.google.com/apps-script/guides/services/advanced?hl=zh-tw#requirements" target="_blank">官方文件描述</a> ，我們需要照以下步驟進行進階設定：</strong></p>
<ol>
  <li>您必須在指令碼專案中 <a href="https://developers.google.com/apps-script/guides/services/advanced?hl=zh-tw#enable_advanced_services" target="_blank">啟用進階服務</a> 。</li>
  <li>您必須確認在指令碼使用的 <a href="https://developers.google.com/apps-script/guides/cloud-platform-projects?hl=zh-tw" target="_blank">Cloud Platform (GCP) 專案</a> 中，已啟用對應進階服務的 API。</li>
  <li>如果指令碼專案使用的是 2019 年 4 月 8 日當天或之後建立的 <a href="https://developers.google.com/apps-script/guides/cloud-platform-projects?hl=zh-tw#default_cloud_platform_projects" target="_blank">預設 GCP 專案</a> ，您啟用進階服務並儲存指令碼專案後，API 就會自動啟用。如果您尚未同意，系統可能也會要求您同意《 <a href="https://cloud.google.com/terms/?hl=zh-tw" target="_blank">Google Cloud</a> 》和《 <a href="https://developers.google.com/terms?hl=zh-tw" target="_blank">Google API</a> 》服務條款。</li>
  <li>如果指令碼專案使用 <a href="https://developers.google.com/apps-script/guides/cloud-platform-projects?hl=zh-tw#standard_cloud_platform_projects" target="_blank">標準 GCP 專案</a> 或較舊的預設 GCP 專案，您必須手動在 GCP 專案中 <a href="https://developers.google.com/apps-script/guides/cloud-platform-projects?hl=zh-tw#enabling_an_api_in_a_standard_gcp_project" target="_blank">啟用進階服務的對應 API</a> 。您必須具備 GCP 專案的編輯權限，才能進行這項變更。</li>
</ol>

<h4 id="1-專案設定-設定關聯的-google-cloud-platform-gcp-專案">1. 專案設定 —設定關聯的 Google Cloud Platform (GCP) 專案</h4>

<p>Google Apps Script x Google APIs 進階服務串接會需要你建立 GCP 專案並關聯到 Google Apps Script，Google APIs 使用權限是依照 GCP 設定。</p>

<p>因此你需要建立一個 GCP 專案(同 Firebase GCP 專案亦可)，記下資訊主頁的「 <code class="language-plaintext highlighter-rouge">專案編號</code> 」並確定這個 GCP 專案有啟用你想使用的 Google APIs 及當前登入帳號或欲使用的帳號是有該 GCP 專案、Google APIs 權限。</p>

<p><img src="/assets/71400d408dc8/1*-BlhyGAEtkcDRrZ86H2BHg.webp" alt="" loading="lazy" decoding="async" width="1013" height="767" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDEzIiBoZWlnaHQ9Ijc2NyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/71400d408dc8/1*fycqt1HNNO9qiu6RZ-WMlw.webp" alt="" loading="lazy" decoding="async" width="912" height="871" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MTIiIGhlaWdodD0iODcxIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<blockquote>
  <p><strong><em>本文以「 <a href="https://firebase.google.com/docs/reference/app-distribution/rest/" target="_blank">Firebase App Distribution API</a> 」為例。</em></strong></p>
</blockquote>

<p><img src="/assets/71400d408dc8/1*T13M_lq7z-ui0_cLZtuHXg.webp" alt="" loading="lazy" decoding="async" width="869" height="830" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4NjkiIGhlaWdodD0iODMwIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>將 <code class="language-plaintext highlighter-rouge">專案編號</code> 輸入到 Goolge Apps Script 專案設定下方的 Google Cloud Platform 專案編號中，當前登入帳號有該 GCP 專案權限就能自動綁定設定成功。</p>
<h4 id="2-專案設定--啟用-在編輯器中顯示appsscriptjson資訊清單檔案">2. 專案設定 — 啟用 在編輯器中顯示「appsscript.json」資訊清單檔案</h4>

<p><img src="/assets/71400d408dc8/1*YnhdxRSyr_CYB2CFnO_31Q.webp" alt="" loading="lazy" decoding="async" width="765" height="498" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjUiIGhlaWdodD0iNDk4Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>啟用後回到編輯器，檔案列表就會出現「 <code class="language-plaintext highlighter-rouge">appsscript.json</code> 」設定檔：</p>

<p><img src="/assets/71400d408dc8/1*r8bheyYWj-TX7vFIum2GIQ.webp" alt="" loading="lazy" decoding="async" width="845" height="358" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4NDUiIGhlaWdodD0iMzU4Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>確保 oauthScopes 中包含以下兩個描述設定：</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="w">  </span><span class="nl">"oauthScopes"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="s2">"https://www.googleapis.com/auth/script.external_request"</span><span class="p">,</span><span class="w">
    </span><span class="s2">"https://www.googleapis.com/auth/cloud-platform"</span><span class="w">
  </span><span class="p">]</span><span class="w">
</span></code></pre></div></div>

<p>如果沒有請手動貼上儲存。</p>
<h4 id="3撰寫串接程式碼">3.撰寫串接程式碼</h4>

<p>設定好 GCP 專案之後我們就能開始撰寫串接程式碼，直接參考想串接的 Google APIs 官方文件， <a href="https://firebase.google.com/docs/reference/app-distribution/rest/" target="_blank">Firebase App Distribution API v1</a> ：</p>

<p><img src="/assets/71400d408dc8/1*N0Zg4TEZQwTTLGCsaTdx8A.webp" alt="" loading="lazy" decoding="async" width="1400" height="1193" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjExOTMiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">project</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">projects/[Firebase Project ID]/apps/[Firebase APP ID]</span><span class="dl">"</span><span class="p">;</span> <span class="c1">// 請換成你的 Project ID &amp; App ID</span>

<span class="kd">function</span> <span class="nf">debug</span><span class="p">()</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">releases</span> <span class="o">=</span> <span class="nf">firebaseDistribution</span><span class="p">(</span><span class="dl">""</span><span class="p">);</span>

  <span class="nx">releases</span><span class="p">.</span><span class="nf">forEach</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">release</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// Release Object: https://firebase.google.com/docs/reference/app-distribution/rest/v1/projects.apps.releases?hl=zh-tw#Release</span>
    <span class="nx">Logger</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="s2">`</span><span class="p">${</span><span class="nx">release</span><span class="p">.</span><span class="nx">name</span><span class="p">}</span><span class="s2"> Download URL: </span><span class="p">${</span><span class="nx">release</span><span class="p">.</span><span class="nx">testingUri</span><span class="p">}</span><span class="s2">`</span><span class="p">);</span>
  <span class="p">});</span>
<span class="p">}</span>

<span class="kd">function</span> <span class="nf">firebaseDistribution</span><span class="p">(</span><span class="nx">releaseNote</span><span class="p">)</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">url</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">https://firebaseappdistribution.googleapis.com/v1/</span><span class="dl">"</span><span class="o">+</span><span class="nx">project</span><span class="o">+</span><span class="dl">"</span><span class="s2">/releases?filter=releaseNotes.text%3D*</span><span class="dl">"</span><span class="o">+</span><span class="nx">releaseNote</span><span class="o">+</span><span class="dl">"</span><span class="s2">*</span><span class="dl">"</span><span class="p">;</span>
  <span class="c1">// Filter: https://firebase.google.com/docs/reference/app-distribution/rest/v1/projects.apps.releases/list?hl=zh-tw</span>
  <span class="kd">const</span> <span class="nx">headers</span> <span class="o">=</span> <span class="p">{</span>
    <span class="dl">"</span><span class="s2">Content-Type</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">application/json; charset=UTF-8</span><span class="dl">"</span><span class="p">,</span>
    <span class="dl">"</span><span class="s2">Authorization</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Bearer </span><span class="dl">"</span> <span class="o">+</span> <span class="nx">ScriptApp</span><span class="p">.</span><span class="nf">getOAuthToken</span><span class="p">(),</span> <span class="c1">// 直接使用當前帳號換取權杖</span>
  <span class="p">};</span>
  
  <span class="kd">const</span> <span class="nx">options</span> <span class="o">=</span> <span class="p">{</span>
    <span class="dl">"</span><span class="s2">method</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">get</span><span class="dl">"</span><span class="p">,</span>
    <span class="dl">"</span><span class="s2">headers</span><span class="dl">"</span><span class="p">:</span> <span class="nx">headers</span>
  <span class="p">};</span>
  <span class="kd">const</span> <span class="nx">data</span> <span class="o">=</span> <span class="nx">UrlFetchApp</span><span class="p">.</span><span class="nf">fetch</span><span class="p">(</span><span class="nx">url</span><span class="p">,</span> <span class="nx">options</span><span class="p">);</span>
  <span class="kd">const</span> <span class="nx">result</span> <span class="o">=</span> <span class="nx">JSON</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="nx">data</span><span class="p">.</span><span class="nf">getContentText</span><span class="p">()).</span><span class="nx">releases</span><span class="p">;</span>
  
  <span class="k">if </span><span class="p">(</span><span class="nx">result</span> <span class="o">==</span> <span class="kc">undefined</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="p">[];</span>
  <span class="p">}</span>

  <span class="k">return</span> <span class="nx">result</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">[Firebase Project ID]</code> <strong>與 <code class="language-plaintext highlighter-rouge">[Firebase APP ID]</code> 可在 Firebase 專案設定中取得：</strong></p>

<p><img src="/assets/71400d408dc8/1*oql_FnmO7_ct8S3cOhP8lg.webp" alt="" loading="lazy" decoding="async" width="1200" height="1051" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjEwNTEiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>貼上程式碼後，第一次執行需要完成權限授權。</p>

<p><img src="/assets/71400d408dc8/1*sW3ZjQA3dKS8HhAOCDsmBA.webp" alt="" loading="lazy" decoding="async" width="1125" height="647" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMTI1IiBoZWlnaHQ9IjY0NyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/71400d408dc8/0*p-Zl0cob4mPrsNO6.webp" alt="" loading="lazy" decoding="async" width="700" height="557" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3MDAiIGhlaWdodD0iNTU3Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<ul>
  <li>點擊「審查權限」</li>
  <li>選擇要執行的身份帳戶，通常等於當前 Google Apps Script 帳戶</li>
</ul>

<p><img src="/assets/71400d408dc8/0*m7enb51ZiNlWYeoT.webp" alt="" loading="lazy" decoding="async" width="700" height="557" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3MDAiIGhlaWdodD0iNTU3Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><img src="/assets/71400d408dc8/0*SN-0owiePlKXIxLk.webp" alt="" loading="lazy" decoding="async" width="700" height="557" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3MDAiIGhlaWdodD0iNTU3Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<ul>
  <li>選擇「進階」展開 -&gt; 點擊「前往 XXX」
這是我們自己寫給自己用的應用程式，不需經過 Google 驗證。</li>
  <li>點擊「允許」</li>
</ul>

<blockquote>
  <p><em>以上畫面不一定會出現，沒有可忽略。</em></p>
</blockquote>

<p><strong>允許之後再點「偵錯」或「執行」就能執行程式：</strong></p>

<p>如果出現錯誤：</p>
<div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Exception</span><span class="o">:</span> <span class="nc">Request</span> <span class="n">failed</span> <span class="k">for</span> <span class="n">https</span><span class="o">://</span><span class="n">firebaseappdistribution</span><span class="mf">.</span><span class="n">googleapis</span><span class="mf">.</span><span class="n">com</span> <span class="n">returned</span> <span class="n">code</span> <span class="mf">403.</span> <span class="nc">Truncated</span> <span class="n">server</span> <span class="n">response</span><span class="o">:</span> <span class="p">{</span>
  <span class="s2">"error"</span><span class="o">:</span> <span class="p">{</span>
    <span class="s2">"code"</span><span class="o">:</span> <span class="mi">403</span><span class="p">,</span>
    <span class="s2">"message"</span><span class="o">:</span> <span class="s2">"Request had insufficient authentication scopes."</span><span class="p">,</span>
    <span class="s2">"status"</span><span class="o">:</span> <span class="s2">"PERMISSION_DENIED"</span><span class="p">,</span>
    <span class="s2">"details"</span><span class="o">:...</span> <span class="p">(</span><span class="kn">use</span> <span class="n">muteHttpExceptions</span> <span class="n">option</span> <span class="n">to</span> <span class="n">examine</span> <span class="n">full</span> <span class="n">response</span><span class="p">)</span>
<span class="mf">...</span>
</code></pre></div></div>

<p>或</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Exception: Specified permissions are not sufficient to call UrlFetchApp.fetch. Required permissions: https://www.googleapis.com/auth/script.external_request
</code></pre></div></div>

<p>請確認 <code class="language-plaintext highlighter-rouge">appsscript.json</code> 的 oauthScopes 中包含以下兩個描述設定(參考步驟 2)：</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nl">"oauthScopes"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="s2">"https://www.googleapis.com/auth/script.external_request"</span><span class="p">,</span><span class="w">
    </span><span class="s2">"https://www.googleapis.com/auth/cloud-platform"</span><span class="w">
  </span><span class="p">]</span><span class="w">
</span></code></pre></div></div>

<p>如果出現錯誤：</p>
<div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Exception</span><span class="o">:</span> <span class="nc">Request</span> <span class="n">failed</span> <span class="k">for</span> <span class="n">https</span><span class="o">://</span><span class="n">firebaseappdistribution</span><span class="mf">.</span><span class="n">googleapis</span><span class="mf">.</span><span class="n">com</span> <span class="n">returned</span> <span class="n">code</span> <span class="mf">401.</span> <span class="nc">Truncated</span> <span class="n">server</span> <span class="n">response</span><span class="o">:</span> <span class="p">{</span>
  <span class="s2">"error"</span><span class="o">:</span> <span class="p">{</span>
    <span class="s2">"code"</span><span class="o">:</span> <span class="mi">401</span><span class="p">,</span>
    <span class="s2">"message"</span><span class="o">:</span> <span class="s2">"Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or othe... (use muteHttpExceptions option to examine full response)
</span></code></pre></div></div>

<p>代表尚未設定綁定的 GCP 專案 (參考步驟 1.) 或是該 GCP 專案尚未啟用欲使用的 Google APIs 或是當前帳號無該 GCP / Google APIs 使用權限或無該 Firebase App 權限，請檢查設定。</p>

<p>如果出現錯誤：</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Exception: Request failed for https://firebaseappdistribution.googleapis.com returned code 404. Truncated server response:
</code></pre></div></div>

<p>或</p>
<div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Exception</span><span class="o">:</span> <span class="nc">Request</span> <span class="n">failed</span> <span class="k">for</span> <span class="n">https</span><span class="o">://</span><span class="n">firebaseappdistribution</span><span class="mf">.</span><span class="n">googleapis</span><span class="mf">.</span><span class="n">com</span> <span class="n">returned</span> <span class="n">code</span> <span class="mf">400.</span> <span class="nc">Truncated</span> <span class="n">server</span> <span class="n">response</span><span class="o">:</span> <span class="p">{</span>
  <span class="s2">"error"</span><span class="o">:</span> <span class="p">{</span>
    <span class="s2">"code"</span><span class="o">:</span> <span class="mi">400</span><span class="p">,</span>
    <span class="s2">"message"</span><span class="o">:</span> <span class="s2">"Request contains an invalid argument."</span><span class="p">,</span>
    <span class="s2">"status"</span><span class="o">:</span> <span class="s2">"INVALID_ARGUMENT"</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>請確認 Google APIs 請求路徑是否正確。</p>
<h4 id="串接成功">串接成功🎉🎉🎉</h4>

<p><img src="/assets/71400d408dc8/1*9OyoPtblFnGGIBRvlGw10Q.webp" alt="" loading="lazy" decoding="async" width="1026" height="474" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI2IiBoZWlnaHQ9IjQ3NCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>可以看到短短不到 10 行程式碼就能無痛串接 Google APIs，真的非常方；有興趣的朋友可以看一下相較之下， <a href="https://github.com/ZhgChgLi/ZReviewTender/blob/main/lib/GoogleAPI.rb" target="_blank">自己實作</a> Google APIs 串接時身份驗證權杖交換的程式步驟，非常繁瑣。</p>
<h4 id="下一步">下一步：</h4>

<p>搭配之前的文章「 <a href="/posts/zrealm-dev/google-apps-script-web-app-打造-github-action-ci-cd-自訂表單提升開發效率-4cb4437818f2/">使用 Google Apps Script Web App 表單串接 Github Action CI/CD 工作</a> 」串上 CI/CD 工作項目，將 Firebase App Distribution 下載連結也呈現在 Web App 上方便同仁直接在上面下載。</p>

<p><img src="/assets/71400d408dc8/1*nK3H3e3KgHEg8qz-KZ9ZGA.webp" alt="Demo Result" loading="lazy" decoding="async" width="797" height="716" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3OTciIGhlaWdodD0iNzE2Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>Demo Result</p>

<blockquote>
  <p><em>其他 Google APIs 也能以此類推串接。</em></p>
</blockquote>

<h3 id="202507-update">2025/07 Update:</h3>

<p>此功能已整合到實際打包工具使用，可參考最新文章案例：「 <a href="/posts/zrealm-dev/ci-cd-打包工具平台-使用-google-apps-script-web-app-串接-github-actions-提升跨團隊效率-4273e57e7148/"><strong>CI/CD 實戰指南（四）：使用 Google Apps Script Web App 串接 GitHub Actions 建置免費易用的打包工具平台</strong></a> 」</p>]]></content>
  </entry><entry>
    <title type="html">XCode 虛擬目錄轉實體目錄｜解決專案結構混亂與 XcodeGen、Tuist 整合痛點</title>
    <link href="https://zhgchg.li/posts/zrealm-dev/xcode-%E8%99%9B%E6%93%AC%E7%9B%AE%E9%8C%84%E8%BD%89%E5%AF%A6%E9%AB%94%E7%9B%AE%E9%8C%84-%E8%A7%A3%E6%B1%BA%E5%B0%88%E6%A1%88%E7%B5%90%E6%A7%8B%E6%B7%B7%E4%BA%82%E8%88%87-xcodegen-tuist-%E6%95%B4%E5%90%88%E7%97%9B%E9%BB%9E-fd719053b376/" rel="alternate" type="text/html" title="XCode 虛擬目錄轉實體目錄｜解決專案結構混亂與 XcodeGen、Tuist 整合痛點" />
    <published>2025-03-02T20:21:31+08:00</published>
    <updated>2025-03-09T15:59:17+08:00</updated>
    <id>https://zhgchg.li/posts/zrealm-dev/fd719053b376</id><summary type="html">針對 XCode 早期虛擬目錄造成專案結構混亂、難以使用 XcodeGen 與 Tuist，提供純 Swift 開源工具 XCFolder，快速轉換虛擬目錄為實體目錄，降低合併衝突風險，提升團隊協作與 CI/CD 效率。</summary><author>
      <name>ZhgChgLi</name>
    </author><category term="ZRealm Dev." /><category term="ios-app-development" /><category term="xcode" /><category term="swift" /><category term="tuist" /><category term="xcodegen" /><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://zhgchg.li/assets/fd719053b376/1*fYk27y-BjMBjBFnwDfhxlw.webp" /><content type="html" xml:base="https://zhgchg.li/posts/zrealm-dev/xcode-%E8%99%9B%E6%93%AC%E7%9B%AE%E9%8C%84%E8%BD%89%E5%AF%A6%E9%AB%94%E7%9B%AE%E9%8C%84-%E8%A7%A3%E6%B1%BA%E5%B0%88%E6%A1%88%E7%B5%90%E6%A7%8B%E6%B7%B7%E4%BA%82%E8%88%87-xcodegen-tuist-%E6%95%B4%E5%90%88%E7%97%9B%E9%BB%9E-fd719053b376/"><![CDATA[<h3 id="xcode-虛擬目錄萬年問題探究與我的開源工具解決方案">XCode 虛擬目錄萬年問題探究與我的開源工具解決方案</h3>

<p>Apple 開發者職業傷害之 Xcode 早期使用虛擬目錄，導致目錄結構混亂且難以整合 XcodeGen, Tuist 等現代工具。</p>

<p><img src="/assets/fd719053b376/1*fYk27y-BjMBjBFnwDfhxlw.webp" alt="Photo by Saad Salim" loading="lazy" decoding="async" width="1200" height="832" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjgzMiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>Photo by <a href="https://unsplash.com/@saadx?utm_content=creditCopyText&amp;utm_medium=referral&amp;utm_source=unsplash" target="_blank">Saad Salim</a></p>
<h4 id="english-version-of-this-post">English version of this post:</h4>
<h4 id="exploring-the-long-standing-issues-of-xcode-virtual-directories-and-my-open-source-tool-solution"><a href="/posts/zrealm-dev/xcode-虛擬目錄轉實體目錄-解決專案結構混亂與-xcodegen-tuist-整合痛點-fd719053b376/">Exploring the Long-Standing Issues of XCode Virtual Directories and My Open Source Tool Solution</a></h4>
<h3 id="背景">背景</h3>

<p>隨著團隊規模與專案的成長，XCode 專案檔 (.xcodeproj) 的體積會逐漸增大，依照專案複雜程度可以到十萬行、甚至百萬行，加上多人多條線並行開發，免不了會出現衝突問題； <strong>XcodeProj 檔案一旦出現衝突就跟 Storyboard / .xib 的衝突一樣難解</strong> ，因也是純描述檔，很容易在解衝突時不小心把別人新增的檔案移除或是別人移除的檔案參照又跑回來。</p>

<p>另外的問題是隨著模組化推進，在 XCode 專案檔 (.xcodeproj) 建立、管理模組的流程非常不友善，模組有變動一樣只能從 XCode 專案檔 的 Diff 中查看，整體不利於團隊朝模組化邁進。</p>

<blockquote>
  <p><em>如果只是為了防止衝突可以簡單地在 pre-commit 做 <a href="https://github.com/chiahsien/sort-Xcode-project-file" target="_blank">File Sorting</a> ，現有的腳本 Github 上很多，可以直接參考設置。</em></p>
</blockquote>

<h4 id="xcfolder"><a href="https://github.com/ZhgChgLi/XCFolder" target="_blank">XCFolder</a></h4>

<p><a href="https://github.com/ZhgChgLi/XCFolder" target="_blank"><img src="https://repository-images.githubusercontent.com/941421589/27855f06-b938-4458-a20f-3e317b4283b7" alt="" /></a></p>

<blockquote>
  <p><strong><em>長話短說</em></strong> <em>，開發了一個工具可以幫你把 XCode 早期的虛擬目錄轉依照 XCode 裡的目錄結構換成實體目錄。</em></p>
</blockquote>

<p><img src="/assets/fd719053b376/1*eFNN12WDaAk6mr49OzmC_g.webp" alt="" loading="lazy" decoding="async" width="1400" height="484" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjQ4NCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<blockquote>
  <p><em>下滑繼續看故事…</em></p>
</blockquote>

<h4 id="現代化-xcode-project-檔案管理">現代化 Xcode Project 檔案管理</h4>

<p>具體思維同我們在多人開發時不鼓勵使用 Storyboard or .xib 一樣， <strong>我們需要一個好維護、可迭代、可被 Code Review 的介面來管理「XCode 專案檔」</strong> ，目前市場主流有兩套免費工具可以使用：</p>
<ul>
  <li><a href="https://github.com/yonaskolb/XcodeGen" target="_blank">XCodeGen</a> ：老牌工具，使用 YAML 定義 XCode 專案內容，再透過它轉換成 XCode 專案檔 (.xcodeproj)。
直接使用 YAML 定義結構、導入難度及學習成本較低、但是對模組化或是依賴管理功能較弱、YAML 動態設置弱。</li>
  <li><a href="https://github.com/tuist/tuist" target="_blank">Tuist</a> ：近幾年出的新工具，使用 Swift DSL 定義 XCode 專案內容，再透過它轉換成 XCode 專案檔 (.xcodeproj)。
更穩定多變，內建模組化及依賴管理功能，但學習與導入門檻較高。</li>
</ul>

<p><strong>不論是那一套我們的核心工作流程都會變成是：</strong></p>
<ol>
  <li><strong>建立 XCode 專案內容設定檔 (XCodeGen <code class="language-plaintext highlighter-rouge">project.yaml</code> or Tuist <code class="language-plaintext highlighter-rouge">Project.swift</code> )</strong></li>
  <li>將 XCodeGen or Tuist 加入開發者與 CI/CD Server 環境中</li>
  <li><strong>使用 XCodeGen or Tuist 透過設定檔產生 <code class="language-plaintext highlighter-rouge">.xcodeproj</code> XCode 專案檔</strong></li>
  <li><code class="language-plaintext highlighter-rouge">/*.xcodeproj</code> 目錄檔案 加入 <code class="language-plaintext highlighter-rouge">.gitignore</code></li>
  <li>調整開發者流程，切換 Branch 時需要跑 XCodeGen or Tuist 透過設定檔產生 <code class="language-plaintext highlighter-rouge">.xcodeproj</code> XCode 專案檔</li>
  <li>調整 CI/CD 流程，需要跑 XCodeGen or Tuist 透過設定檔產生 <code class="language-plaintext highlighter-rouge">.xcodeproj</code> XCode 專案檔</li>
  <li>完成</li>
</ol>

<p><code class="language-plaintext highlighter-rouge">.xcodeproj</code> XCode 專案檔是由 XCodeGen or Tuist 基於 YAML of Swift DSL 設定檔產生的，同一份設定檔 &amp; 同個工具版本會產生相同結果；因此我們不需要再把 <code class="language-plaintext highlighter-rouge">.xcodeproj</code> XCode 專案檔上到 Git 當中，就此可以保證以後不會再有 <code class="language-plaintext highlighter-rouge">.xcodeproj</code> 檔案衝突；專案構建的改變、模組的新增改動我們都會回到定義的設定檔中做調整，因為是 Yaml or Swift 寫成的，我們可以很輕易地進行迭代跟 Code Review。</p>

<p><strong>Tuist Swift 範例：</strong> <code class="language-plaintext highlighter-rouge">Project.swift</code></p>
<div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">import</span> <span class="nc">ProjectDescription</span>

<span class="n">let</span> <span class="n">project</span> <span class="o">=</span> <span class="nf">Project</span><span class="p">(</span>
    <span class="n">name</span><span class="o">:</span> <span class="s2">"MyApp"</span><span class="p">,</span>
    <span class="n">targets</span><span class="o">:</span> <span class="p">[</span>
        <span class="nf">Target</span><span class="p">(</span>
            <span class="n">name</span><span class="o">:</span> <span class="s2">"MyApp"</span><span class="p">,</span>
            <span class="n">platform</span><span class="o">:</span> <span class="mf">.</span><span class="n">iOS</span><span class="p">,</span>
            <span class="n">product</span><span class="o">:</span> <span class="mf">.</span><span class="n">app</span><span class="p">,</span>
            <span class="n">bundleId</span><span class="o">:</span> <span class="s2">"com.example.myapp"</span><span class="p">,</span>
            <span class="n">deploymentTarget</span><span class="o">:</span> <span class="mf">.</span><span class="nf">iOS</span><span class="p">(</span><span class="n">targetVersion</span><span class="o">:</span> <span class="s2">"15.0"</span><span class="p">,</span> <span class="n">devices</span><span class="o">:</span> <span class="p">[</span><span class="mf">.</span><span class="n">iphone</span><span class="p">,</span> <span class="mf">.</span><span class="n">ipad</span><span class="p">]),</span>
            <span class="n">infoPlist</span><span class="o">:</span> <span class="mf">.</span><span class="k">default</span><span class="p">,</span>
            <span class="n">sources</span><span class="o">:</span> <span class="p">[</span><span class="s2">"Sources/**"</span><span class="p">],</span>
            <span class="n">resources</span><span class="o">:</span> <span class="p">[</span><span class="s2">"Resources/**"</span><span class="p">],</span>
            <span class="n">dependencies</span><span class="o">:</span> <span class="p">[]</span>
        <span class="p">),</span>
        <span class="nf">Target</span><span class="p">(</span>
            <span class="n">name</span><span class="o">:</span> <span class="s2">"MyAppTests"</span><span class="p">,</span>
            <span class="n">platform</span><span class="o">:</span> <span class="mf">.</span><span class="n">iOS</span><span class="p">,</span>
            <span class="n">product</span><span class="o">:</span> <span class="mf">.</span><span class="n">unitTests</span><span class="p">,</span>
            <span class="n">bundleId</span><span class="o">:</span> <span class="s2">"com.example.myapp.tests"</span><span class="p">,</span>
            <span class="n">deploymentTarget</span><span class="o">:</span> <span class="mf">.</span><span class="nf">iOS</span><span class="p">(</span><span class="n">targetVersion</span><span class="o">:</span> <span class="s2">"15.0"</span><span class="p">,</span> <span class="n">devices</span><span class="o">:</span> <span class="p">[</span><span class="mf">.</span><span class="n">iphone</span><span class="p">,</span> <span class="mf">.</span><span class="n">ipad</span><span class="p">]),</span>
            <span class="n">infoPlist</span><span class="o">:</span> <span class="mf">.</span><span class="k">default</span><span class="p">,</span>
            <span class="n">sources</span><span class="o">:</span> <span class="p">[</span><span class="s2">"Tests/**"</span><span class="p">],</span>
            <span class="n">dependencies</span><span class="o">:</span> <span class="p">[</span><span class="mf">.</span><span class="nf">target</span><span class="p">(</span><span class="n">name</span><span class="o">:</span> <span class="s2">"MyApp"</span><span class="p">)]</span>
        <span class="p">)</span>
    <span class="p">]</span>
<span class="p">)</span>
</code></pre></div></div>

<p><strong>XCodeGen YAML 範例：</strong> <code class="language-plaintext highlighter-rouge">project.yaml</code></p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">name</span><span class="pi">:</span> <span class="s">MyApp</span>
<span class="na">options</span><span class="pi">:</span>
  <span class="na">bundleIdPrefix</span><span class="pi">:</span> <span class="s">com.example</span>
  <span class="na">deploymentTarget</span><span class="pi">:</span>
    <span class="na">iOS</span><span class="pi">:</span> <span class="s1">'</span><span class="s">15.0'</span>

<span class="na">targets</span><span class="pi">:</span>
  <span class="na">MyApp</span><span class="pi">:</span>
    <span class="na">type</span><span class="pi">:</span> <span class="s">application</span>
    <span class="na">platform</span><span class="pi">:</span> <span class="s">iOS</span>
    <span class="na">sources</span><span class="pi">:</span> <span class="pi">[</span><span class="nv">Sources</span><span class="pi">]</span>
    <span class="na">resources</span><span class="pi">:</span> <span class="pi">[</span><span class="nv">Resources</span><span class="pi">]</span>
    <span class="na">info</span><span class="pi">:</span>
      <span class="na">path</span><span class="pi">:</span> <span class="s">Info.plist</span>
      <span class="na">properties</span><span class="pi">:</span>
        <span class="na">UILaunchScreen</span><span class="pi">:</span> <span class="pi">{}</span>
    <span class="na">dependencies</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">framework</span><span class="pi">:</span> <span class="s">Vendor/SomeFramework.framework</span>
      <span class="pi">-</span> <span class="na">sdk</span><span class="pi">:</span> <span class="s">UIKit.framework</span>
      <span class="pi">-</span> <span class="na">package</span><span class="pi">:</span> <span class="s">Alamofire</span>

  <span class="na">MyAppTests</span><span class="pi">:</span>
    <span class="na">type</span><span class="pi">:</span> <span class="s">bundle.unit-test</span>
    <span class="na">platform</span><span class="pi">:</span> <span class="s">iOS</span>
    <span class="na">sources</span><span class="pi">:</span> <span class="pi">[</span><span class="nv">Tests</span><span class="pi">]</span>
    <span class="na">dependencies</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">target</span><span class="pi">:</span> <span class="s">MyApp</span>
</code></pre></div></div>
<h4 id="檔案目錄結構">檔案目錄結構</h4>

<p>XCodeGen or Tuist 會依照檔案實際目錄、位置產生 XCode 專案檔 (.xcodeproj) 的目錄結構， <strong>實際目錄即為 XCode 專案檔案目錄</strong> 。</p>

<p><img src="/assets/fd719053b376/1*rYgdRpEDwRiZoMLzM0WpSQ.webp" alt="" loading="lazy" decoding="async" width="1080" height="752" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDgwIiBoZWlnaHQ9Ijc1MiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>因此檔案的實際目錄位置就很重要，我們會直接用它來當 XCode 專案檔案目錄。</p>

<p>這在現代的 XCode / XCode 專案中，這兩個目錄的位置相等是再日常不過的事，但這個議題就是本文想要探究的項目。</p>
<h3 id="早期-xcode-使用虛擬目錄">早期 Xcode 使用虛擬目錄</h3>

<p>在早期的 XCode 中在檔案目錄上按右鍵「New Group」是不會建立實際目錄的，檔案會被放在專案根目錄下並在 <code class="language-plaintext highlighter-rouge">.xcodeproj</code> XCode 專案檔做 File Reference，因此目錄只有在 XCode 專案檔中可見，實際沒有。</p>

<p><img src="/assets/fd719053b376/1*LPuedcbhL4OUJuLcO0xSPw.webp" alt="" loading="lazy" decoding="async" width="1168" height="746" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMTY4IiBoZWlnaHQ9Ijc0NiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>隨著時代演變，Apple 在 XCode 上逐漸汰換了這個詭異的設計，後來的 XCode 從加上「New Group with Folder」到預設會建實體目錄、不要建要選「New Group without Folder」在到 <strong>現在 (XCode 16) 只剩「New Group」＋ 自動根據實體目錄產生XCode 專案檔案目錄</strong> 。</p>
<h4 id="虛擬目錄的問題">虛擬目錄的問題</h4>
<ul>
  <li><strong>無法使用 XCodeGen or Tuist，因為都需要實體目錄位置產生 XCode 專案檔 (.xcodeproj)。</strong></li>
  <li>Code Review 困難：在 Git Web GUI 上看不到目錄結構，整個檔案都是攤平的。</li>
  <li>DevOps, 第三方工具整合困難：例如 Sentry, Github 可以依照目錄派發警告或 Auto-Assigner，沒有目錄只有檔案無法設定。</li>
  <li>專案目錄結構極為複雜，一大堆檔案攤平在根目錄。</li>
</ul>

<blockquote>
  <p><em>對於一個年代久遠並且沒有在早期意識到虛擬目錄問題的專案，虛擬目錄的檔案有 3,000+ 個，手動對照搬移的話搬完應該就辭職去賣狀元糕了，這真的可以稱得上是「 <a href="https://x.com/1star_therapist" target="_blank"><strong>Apple 開發者職業傷害</strong></a> <strong>😔</strong> 」。</em></p>
</blockquote>

<h3 id="xcode-專案虛擬目錄-轉-實體目錄">Xcode 專案虛擬目錄 轉 實體目錄</h3>

<p>基於上述種種原因，我們急迫的需要把 XCode 專案的虛擬目錄轉成實體目錄，否則後續的專案現代化、更高效的開發流程都無法推進。</p>
<h4 id="-xcode-16-convert-to-folder選項">❌ Xcode 16 「Convert to folder」選項</h4>

<p><img src="/assets/fd719053b376/1*FlhWWH5Qz4aLfB4hYVUgNA.webp" alt="XCode 16" loading="lazy" decoding="async" width="285" height="562" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyODUiIGhlaWdodD0iNTYyIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>XCode 16</p>

<p>去年 XCode 16 剛出時有關注到這個選單新選項，本來的期待是它可以自動幫我們把虛擬目錄檔案轉換成實體目錄。</p>

<p>但實際並不然，他需要你先把檔案建好目錄、放到對應的實體位置，點擊「Convert to folder」後會幫你改成新的 XCode Project 目錄設定方式「 <code class="language-plaintext highlighter-rouge">PBXFileSystemSynchronizedRootGroup</code> 」，說實話 <strong>對轉換沒有任何用，這比較是轉換後可以升級成新的目錄設定方式。</strong></p>

<p>目錄沒有建立、檔案沒有放好，點「Convert to folder」就會報以下錯誤：</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Missing Associated Folder
Each group must have an associated folder. The following groups are not associated with a folder:
• xxxx
Use the file inspector to associate a folder with each group, or delete the groups after moving their content to another group.
</code></pre></div></div>
<h4 id="-開源專案-venmo--synx">🫥 開源專案 <a href="https://github.com/venmo" target="_blank">venmo</a> / <a href="https://github.com/venmo/synx" target="_blank">synx</a></h4>

<p><a href="https://github.com/venmo/synx" target="_blank"><img src="https://opengraph.githubassets.com/c526221a2dff30443c7e38d86c4da063595d493a064d970245ff75a5593dc402/venmo/synx" alt="" /></a></p>

<p>在 Github 搜尋許久只找到這個使用 Ruby 撰寫的虛擬轉實體目錄開源專案工具，實際跑下來是有效果的但因年久失修 (~= 10 年未更新) 很多檔案還是需要手動對應搬移，無法完全轉換，因此放棄。</p>

<blockquote>
  <p><em>不過還是很感謝這個開源專案的啟發，我才想說可以自己開發轉換工具。</em></p>
</blockquote>

<h4 id="-我的開源專案-zhgchgli--xcfolder">✅ 我的開源專案 <a href="https://github.com/ZhgChgLi" target="_blank">ZhgChgLi</a> / <a href="https://github.com/ZhgChgLi/XCFolder" target="_blank">XCFolder</a></h4>

<p><a href="https://github.com/ZhgChgLi/XCFolder" target="_blank"><img src="https://repository-images.githubusercontent.com/941421589/27855f06-b938-4458-a20f-3e317b4283b7" alt="" /></a></p>

<p>使用純 Swift 開發的 Command Line Tools，基於 <a href="https://github.com/tuist/XcodeProj" target="_blank">XcodeProj</a> 解析 <code class="language-plaintext highlighter-rouge">.xcodeproj</code> XCode 專案檔，讀取所有檔案取得所屬目錄、解析目錄將虛擬目錄轉換成實體目錄，將檔案搬移到正確位置、最後再調整 <code class="language-plaintext highlighter-rouge">.xcodeproj</code> XCode 專案檔目錄設定，即可完成轉換。</p>

<p><img src="/assets/fd719053b376/1*eFNN12WDaAk6mr49OzmC_g.webp" alt="" loading="lazy" decoding="async" width="1400" height="484" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjQ4NCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><strong>使用方式</strong></p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git clone https://github.com/ZhgChgLi/XCFolder.git
<span class="nb">cd</span> ./XCFolder
swift run XCFolder YOUR_XCODEPROJ_FILE.xcodeproj ./Configuration.yaml
</code></pre></div></div>

<p><strong>For Example:</strong></p>
<div class="language-objectivec highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">swift</span> <span class="n">run</span> <span class="n">XCFolder</span> <span class="p">.</span><span class="o">/</span><span class="n">TestProject</span><span class="o">/</span><span class="n">DCDeviceTest</span><span class="p">.</span><span class="n">xcodeproj</span> <span class="p">.</span><span class="o">/</span><span class="n">Configuration</span><span class="p">.</span><span class="n">yaml</span>
</code></pre></div></div>

<p><strong>CI/CD 模式 (</strong> <code class="language-plaintext highlighter-rouge">Non Interactive Mode</code> <strong>)：</strong></p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">swift</span> <span class="n">run</span> <span class="kt">XCFolder</span> <span class="kt">YOUR_XCODEPROJ_FILE</span><span class="o">.</span><span class="n">xcodeproj</span> <span class="o">./</span><span class="kt">Configuration</span><span class="o">.</span><span class="n">yaml</span> <span class="o">--</span><span class="k">is</span><span class="o">-</span><span class="n">non</span><span class="o">-</span><span class="n">interactive</span><span class="o">-</span><span class="n">mode</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">Configuration.yaml</code> 可以設定想要的執行參數：</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 忽略的目錄，不會解析轉換</span>
ignorePaths:
- <span class="s2">"Pods"</span>
- <span class="s2">"Frameworks"</span>
- <span class="s2">"Products"</span>

<span class="c"># 忽略的檔案類型，不會被轉換搬移</span>
ignoreFileTypes:
- <span class="s2">"wrapper.framework"</span> <span class="c"># Frameworks</span>
- <span class="s2">"wrapper.pb-project"</span> <span class="c"># Xcode project files</span>
<span class="c">#- "wrapper.application" # Applications</span>
<span class="c">#- "wrapper.cfbundle" # Bundles</span>
<span class="c">#- "wrapper.plug-in" # Plug-ins</span>
<span class="c">#- "wrapper.xpc-service" # XPC services</span>
<span class="c">#- "wrapper.xctest" # XCTest bundles</span>
<span class="c">#- "wrapper.app-extension" # App extensions</span>

<span class="c"># 只建立目錄、搬移檔案，不調整 .xcodeproj XCode 專案檔目錄設定</span>
moveFileOnly: <span class="nb">false</span>

<span class="c"># 優先使用 git mv 指令搬移檔案</span>
gitMove: <span class="nb">true</span>
</code></pre></div></div>

<p>⚠️ <strong>執行前請注意：</strong></p>
<ul>
  <li>請確保 Git 無任何未 Commit 改變，因為怕腳本有誤污染你的專案目錄
(腳本執行會檢查，若有未 Commit 改變會拋錯誤 <code class="language-plaintext highlighter-rouge">❌ Error: There are uncommitted changes in the repository</code> )</li>
  <li>預設優先使用 <code class="language-plaintext highlighter-rouge">git mv</code> 指令搬移檔案以確保 git file log 紀錄完整，如果搬移失敗或是非 Git 專案才會使用 FileSystem Move 搬移檔案。</li>
</ul>

<p><strong>等待執行完成即可：</strong></p>

<p><img src="/assets/fd719053b376/1*IL_-Ht1SH5ZCYogZHXULbQ.webp" alt="" loading="lazy" decoding="async" width="638" height="439" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2MzgiIGhlaWdodD0iNDM5Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><img src="/assets/fd719053b376/1*8JNbDbR7pvZLhcoQLU_B6A.webp" alt="" loading="lazy" decoding="async" width="682" height="483" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2ODIiIGhlaWdodD0iNDgzIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>⚠️ <strong>執行後請注意：</strong></p>
<ul>
  <li>請檢查專案目錄是否有遺漏(紅色)的檔案，數量少可以手動修正，數量多請確認 Configuration.yaml 裡的 ignorePaths, ignoreFileTypes 設定是否正確，或是 <a href="https://github.com/ZhgChgLi/XCFolder/issues" target="_blank"><strong>建立一個 Issue</strong></a> 讓我知道。</li>
  <li>檢查 Build Setting 裡的相關路徑 e.g. <code class="language-plaintext highlighter-rouge">LIBRARY_SEARCH_PATHS</code> 是否需要手動更動路徑</li>
  <li>建議 Clean &amp; Build 看看</li>
  <li><strong>如果懶得管現在的 <code class="language-plaintext highlighter-rouge">.xcodeproj</code> XCode 專案檔，也可以直接開始使用 XCodeGen or Tuist 直接重新產生目錄檔案</strong></li>
</ul>

<p><strong>修改腳本：</strong></p>

<p>直接點擊 <code class="language-plaintext highlighter-rouge">./Package.swift</code> 就能開啟專案調整腳本內容。</p>
<h4 id="其他開發隨記">其他開發隨記</h4>
<ul>
  <li>得力於 <a href="https://github.com/tuist/XcodeProj" target="_blank">XcodeProj</a> 我們可以很輕易的使用 Swift 物件方式存取 <code class="language-plaintext highlighter-rouge">.xcodeproj</code> <strong>XCode 專案檔案內容</strong></li>
  <li>同樣使用 Clean Architecture 架構開發</li>
  <li>PBXGroup 設定中若沒有 path 只有 name 則為虛擬目錄，反之則為實體目錄</li>
  <li>XCode 16 新的目錄設定 <code class="language-plaintext highlighter-rouge">PBXFileSystemSynchronizedRootGroup</code> 只需宣告主目錄就會自動從實體目錄解析，不需要再把每個目錄跟檔案都宣告在 <code class="language-plaintext highlighter-rouge">.xcodeproj</code> XCode 專案檔案內</li>
  <li>直接用 SPM (Package.swift) 方式開發 Command line tool 真的很方便！</li>
</ul>]]></content>
  </entry><entry>
    <title type="html">自動備份 Medium 文章到 Github Pages｜Jekyll 靜態備份與部署全攻略</title>
    <link href="https://zhgchg.li/posts/zrealm-dev/%E8%87%AA%E5%8B%95%E5%82%99%E4%BB%BD-medium-%E6%96%87%E7%AB%A0%E5%88%B0-github-pages-jekyll-%E9%9D%9C%E6%85%8B%E5%82%99%E4%BB%BD%E8%88%87%E9%83%A8%E7%BD%B2%E5%85%A8%E6%94%BB%E7%95%A5-5bb7d3a4954f/" rel="alternate" type="text/html" title="自動備份 Medium 文章到 Github Pages｜Jekyll 靜態備份與部署全攻略" />
    <published>2025-01-18T23:12:33+08:00</published>
    <updated>2025-03-08T22:48:25+08:00</updated>
    <id>https://zhgchg.li/posts/zrealm-dev/5bb7d3a4954f</id><summary type="html">透過自製工具將 Medium 文章自動轉成 Markdown，並利用 Jekyll Chirpy Theme 部署到 Github Pages，解決文章遺失風險與滾動卡頓問題，實現免費且穩定的持續備份與客製化展示。</summary><author>
      <name>ZhgChgLi</name>
    </author><category term="ZRealm Dev." /><category term="medium" /><category term="jekyll" /><category term="automation" /><category term="ios-app-development" /><category term="github-pages" /><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://zhgchg.li/assets/5bb7d3a4954f/1*oM79EdbsiBYiWnqb0mH8QQ.webp" /><content type="html" xml:base="https://zhgchg.li/posts/zrealm-dev/%E8%87%AA%E5%8B%95%E5%82%99%E4%BB%BD-medium-%E6%96%87%E7%AB%A0%E5%88%B0-github-pages-jekyll-%E9%9D%9C%E6%85%8B%E5%82%99%E4%BB%BD%E8%88%87%E9%83%A8%E7%BD%B2%E5%85%A8%E6%94%BB%E7%95%A5-5bb7d3a4954f/"><![CDATA[<h3 id="自動備份-medium-文章到-github-pages-jekyll-的那些事">自動備份 Medium 文章到 Github Pages (Jekyll) 的那些事</h3>

<p>個人 Medium 文章備份鏡像站搭建、維護、升級、客製化的一些紀錄</p>

<h4 id="前言">前言</h4>

<p>經營 Medium 來到了第 6 年，文章總數在去年突破 100 篇；隨著經營時間越長、文章越多，越怕哪天 Medium 突然關閉或是帳號異常造成所有文章心血付之一炬，有的文章含金量不高道無妨，但更多的是記錄技術架構跟當時的解題思維，我時常也會回來看之前寫的文章，重新複習知識；另外後面幾年也開始記錄出國旅遊遊記，都是回憶並且流量表現不錯；這些內容一但遺失就不可能再重新撰寫了。</p>
<h4 id="自行開發備份工具">自行開發備份工具</h4>

<p>我習慣都是直接在 Medium 平台上撰寫文章，沒有自己的備份，因此在 2022 年過年期間花時間開發了一個 Medium 文章下載＆轉換成 Markdown 文件(包含文章圖片、文章內嵌的程式碼…等內容) 的工具 <strong>— <a href="https://github.com/ZhgChgLi/ZMediumToMarkdown" target="_blank">ZMediumToMarkdown</a> ：</strong></p>

<p><a href="https://github.com/ZhgChgLi/ZMediumToMarkdown" target="_blank"><img src="https://repository-images.githubusercontent.com/493527574/9b5b7025-cc95-4e81-84a9-b38706093c27" alt="" /></a></p>

<p>並延伸使用此工具將下載下來的 Markdown 使用 <a href="https://github.com/cotes2020/jekyll-theme-chirpy" target="_blank">Jekyll (Chirpy Theme)</a> 做為靜態備份鏡像網站部署在 Github Pages 上 — <a href="https://zhgchg.li/" target="_blank">https://zhgchg.li/</a></p>

<p><img src="/assets/5bb7d3a4954f/1*oM79EdbsiBYiWnqb0mH8QQ.webp" alt="https://zhgchg.li/" loading="lazy" decoding="async" width="1400" height="658" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjY1OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><a href="https://zhgchg.li/" target="_blank">https://zhgchg.li/</a></p>

<p>那時候把這整套整合成一個 Github Template Repo 給有同樣需求的朋友可以快速部署使用 — <a href="https://github.com/ZhgChgLi/ZMediumToJekyll" target="_blank">ZMediumToJekyll</a> ，在此之後(2022)，我就沒有再更新過 <a href="https://github.com/cotes2020/jekyll-theme-chirpy" target="_blank">Jekyll (Chirpy Theme)</a> 的版本跟設定了； <a href="https://github.com/ZhgChgLi/ZMediumToMarkdown" target="_blank"><strong>ZMediumToMarkdown</strong></a> <strong>持續有在維護，偶爾會發現格式解析錯誤就會立刻修正，目前趨於穩定。</strong></p>

<p>那時候使用的 <a href="https://github.com/cotes2020/jekyll-theme-chirpy" target="_blank">Jekyll (Chirpy Theme)</a> 版本是 v5.x 沒有太大的問題，該有的功能也都有(e.g. 置頂、分類、標籤、封面圖、留言…)；只有在畫面滾動時很常會出現無法滾動問題，但是在滑個幾下又正常，一個操作體驗缺憾，曾經嘗試升級到 v6.x 還是有、回報給官方也沒得到回應；再加上隨著版本提升升級會遇到的衝突就越多，因此後來完全放棄升級這個念頭。</p>

<p>近期才下定決心要解決 <a href="https://github.com/cotes2020/jekyll-theme-chirpy" target="_blank">Jekyll (Chirpy Theme)</a> 問題、升級版本、順手重新優化快速部署工具 <a href="https://github.com/ZhgChgLi/ZMediumToJekyll" target="_blank">ZMediumToJekyll</a> 。</p>
<h3 id="new-medium-to-jekyll-starter-">New! medium-to-jekyll-starter 🎉🎉</h3>
<h4 id="medium-to-jekyll-startergithubio"><a href="https://github.com/ZhgChgLi/medium-to-jekyll-starter.github.io" target="_blank">medium-to-jekyll-starter.github.io</a></h4>

<p><a href="https://github.com/ZhgChgLi/medium-to-jekyll-starter.github.io" target="_blank"><img src="https://repository-images.githubusercontent.com/918538745/779cd996-4dc3-4ee0-88b7-951b39fc4463" alt="" /></a></p>

<p>我將 <a href="https://github.com/cotes2020/jekyll-theme-chirpy" target="_blank"><strong>Jekyll (Chirpy Theme)</strong></a> 最新版 v7.x 加上我的 <a href="https://github.com/ZhgChgLi/ZMediumToMarkdown" target="_blank"><strong>ZMediumToMarkdown</strong></a> Medium 文章下載轉換工具重新整合成新的 — <a href="https://github.com/ZhgChgLi/medium-to-jekyll-starter.github.io" target="_blank">medium-to-jekyll-starter.github.io</a> Github Template Repo。</p>

<p>大家可以直接使用這個範本 Repo 快速設定搭建自己的 Medium 鏡像內容備份網站， <strong>一次設定永久持續自動備份、部署在 Github Pages 上完全免費</strong> 。</p>

<blockquote>
  <p><strong><em>手把手設定教學請參考此篇文章： <a href="https://zhgchg.li/posts/medium-to-jekyll/" target="_blank">https://zhgchg.li/posts/medium-to-jekyll/</a></em></strong></p>
</blockquote>

<h4 id="成果">成果</h4>

<p><img src="/assets/5bb7d3a4954f/1*Nyg7Fg93sDUAIMZQfN5QTg.webp" alt="https://zhgchg.li/" loading="lazy" decoding="async" width="1200" height="852" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg1MiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><a href="https://zhgchg.li/" target="_blank">https://zhgchg.li/</a></p>

<blockquote>
  <p><em>*上面的所有文章都是 <strong>自動</strong> 從我的 Medium 下載所有內容＆轉換成 Markdown 格式＆重新上傳。</em></p>
</blockquote>

<blockquote>
  <p><strong><em>附上隨便一篇文章的轉換成果作為比較範例：</em></strong></p>
</blockquote>

<blockquote>
  <p><strong><em><a href="https://shorturl.at/CG9ua" target="_blank">Medium 上的原始內容</a> / <a href="/posts/pinkoi-engineering/design-patterns-實戰應用-封裝-socket-io-即時通訊架構與七大設計模式解析-78507a8de6a5/">轉換後在個人網站的結果</a></em></strong></p>
</blockquote>

<p>升級後沒再出現滾動卡住的問題了，藉由這次升級也多加上了客製化動態內容 (顯示 Medium 追蹤人數)。</p>
<h3 id="一些技術紀錄">一些技術紀錄</h3>

<p><a href="https://github.com/cotes2020/jekyll-theme-chirpy" target="_blank">Jekyll (Chirpy Theme)</a> 在 Github Pages 上的部署設定方式主要是直接參考官方 Start Repo：</p>

<p><a href="https://github.com/cotes2020/chirpy-starter/tree/main" target="_blank"><img src="https://opengraph.githubassets.com/e23e4502475ff31a4a9ea0acb4524983a049cf151ec9ef48fe0777bbc9d8edb1/cotes2020/chirpy-starter" alt="" /></a></p>

<blockquote>
  <p><em>上個月也參考這個專案的方式，做了一個新的開源專案 — <a href="https://github.com/ZhgChgLi/linkyee" target="_blank">Linkyee</a> 開源版的 Link Tree 個人連結頁面。</em></p>
</blockquote>

<p><img src="/assets/5bb7d3a4954f/1*OTotv1Nw-KnhsflSSiNgkg.webp" alt="https://link.zhgchg.li/" loading="lazy" decoding="async" width="1400" height="854" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9Ijg1NCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><a href="https://link.zhgchg.li/" target="_blank">https://link.zhgchg.li/</a></p>
<h4 id="jekyll-客製化方式-1--override-html">Jekyll 客製化方式 (1) — Override HTML</h4>

<p><a href="https://jekyllrb.com/" target="_blank">Jekyll</a> 是一套很強大的 Ruby 靜態內容網站生成引擎， <a href="https://github.com/cotes2020/jekyll-theme-chirpy" target="_blank">Jekyll (Chirpy Theme)</a> 只是一套基於 Jekyll 的主題，比較過其他主題還是 <a href="https://github.com/cotes2020/jekyll-theme-chirpy" target="_blank">Chirpy Theme</a> 最有質感跟操作體驗優異、功能俱全。</p>

<p>Jekyll 的頁面具有繼承性，我們可以在 <code class="language-plaintext highlighter-rouge">./_layouts</code> 新增 <a href="https://github.com/cotes2020/jekyll-theme-chirpy/tree/master/_layouts" target="_blank">與 Jekyll 相同的頁面檔案名</a> ，引擎在產生網站內容時就會使用你自訂的頁面內容取代掉原本的。</p>

<p>例如我希望在每個文章頁末尾加上一行文字，我先把原本的文章頁面檔案( <a href="https://github.com/cotes2020/jekyll-theme-chirpy/blob/master/_layouts/post.html" target="_blank">post.html</a> )複製出來，放到 <code class="language-plaintext highlighter-rouge">./_layouts</code> 目錄下：</p>

<p><img src="/assets/5bb7d3a4954f/1*oDykwzZ0P5o8GEw8lc3WPQ.webp" alt="" loading="lazy" decoding="async" width="133" height="38" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMzMiIGhlaWdodD0iMzgiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>使用編輯器打開 post.html 在相應的位置加上文字或客製化，重新部署網站就能看到客製化結果。</p>

<p><img src="/assets/5bb7d3a4954f/1*7ni973_1JykXoj8le78v1A.webp" alt="" loading="lazy" decoding="async" width="303" height="124" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzMDMiIGhlaWdodD0iMTI0Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>也可以建立一個 <code class="language-plaintext highlighter-rouge">./_include</code> 目錄，放一些想要共用的頁面內容檔案：</p>

<p><img src="/assets/5bb7d3a4954f/1*rExx8jMcMfEQZ3LfvhwT2w.webp" alt="" loading="lazy" decoding="async" width="193" height="163" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxOTMiIGhlaWdodD0iMTYzIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>然後再 <code class="language-plaintext highlighter-rouge">post.html</code> 中我們就可以直接使用 <code class="language-plaintext highlighter-rouge">{% include buymeacoffee.html %}</code> 引入剛檔案的 HTML 內容重複使用。</p>

<blockquote>
  <p><em>複寫 HTML Layout 檔案的優點是 100% 客製化，頁面內容、排版要怎麼呈現都可以隨意調整；缺點是這次在升級的過程就會遇到衝突或是預期外結果，要自己重新檢視一次客製化的內容。</em></p>
</blockquote>

<h4 id="jekyll-客製化方式-2--plugin">Jekyll 客製化方式 (2) — Plugin</h4>

<p>第二種方式是使用 <a href="https://jekyllrb.com/docs/plugins/" target="_blank">Plugin</a> 中的 <a href="https://jekyllrb.com/docs/plugins/hooks/#built-in-hook-owners-and-events" target="_blank">Hook</a> 方法，在 Jekyll 產生靜態內容階段注入自己想要的客製化內容。</p>

<p><img src="/assets/5bb7d3a4954f/1*JI-uJ8tIKnomJyQk9cVfyQ.webp" alt="" loading="lazy" decoding="async" width="651" height="423" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2NTEiIGhlaWdodD0iNDIzIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><img src="/assets/5bb7d3a4954f/1*GrUJn6HXoBqYXUQMKrnqTA.webp" alt="Built-in Hook Owners and Events" loading="lazy" decoding="async" width="647" height="285" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2NDciIGhlaWdodD0iMjg1Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>[Built-in Hook Owners and Events</p>

<p>Hook 事件](https://jekyllrb.com/docs/plugins/hooks/#built-in-hook-owners-and-events){:target=”_blank”} 有很多，這邊只附上我用到的 <code class="language-plaintext highlighter-rouge">site:pre_render</code> 跟 <code class="language-plaintext highlighter-rouge">post:pre_render</code></p>

<p>新增方式也很簡單，只要在 <code class="language-plaintext highlighter-rouge">./_plugins</code> 新增一個 Ruby 檔案即可。</p>

<p><img src="/assets/5bb7d3a4954f/1*1QTCNuYJbJPlfJoMrc6v5g.webp" alt="posts-lastmod-hook.rb 是原本就有的 Plugin" loading="lazy" decoding="async" width="216" height="66" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMTYiIGhlaWdodD0iNjYiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>posts-lastmod-hook.rb 是原本就有的 Plugin</p>

<p>我想要幾個「偽」動態內容功能，第一個是在個人資料下顯示 Medium 追蹤人數還有在頁底顯示頁面內容最後更新時間。</p>

<p><img src="/assets/5bb7d3a4954f/1*6JA9ONLP_A0eNL_q-5b6yg.webp" alt="" loading="lazy" decoding="async" width="1081" height="802" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDgxIiBoZWlnaHQ9IjgwMiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>在 <code class="language-plaintext highlighter-rouge">./_plugins</code> 下建立了一個 <code class="language-plaintext highlighter-rouge">zhgchgli-customize.rb</code> ：</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#!/usr/bin/env ruby</span>
<span class="c1">#</span>
<span class="nb">require</span> <span class="s1">'net/http'</span>
<span class="nb">require</span> <span class="s1">'nokogiri'</span>
<span class="nb">require</span> <span class="s1">'uri'</span>
<span class="nb">require</span> <span class="s1">'date'</span>


<span class="k">def</span> <span class="nf">load_medium_followers</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">limit</span> <span class="o">=</span> <span class="mi">10</span><span class="p">)</span>
  <span class="k">return</span> <span class="mi">0</span> <span class="k">if</span> <span class="n">limit</span><span class="p">.</span><span class="nf">zero?</span>

  <span class="n">uri</span> <span class="o">=</span> <span class="no">URI</span><span class="p">(</span><span class="n">url</span><span class="p">)</span>
  <span class="n">response</span> <span class="o">=</span> <span class="no">Net</span><span class="o">::</span><span class="no">HTTP</span><span class="p">.</span><span class="nf">get_response</span><span class="p">(</span><span class="n">uri</span><span class="p">)</span>
  <span class="k">case</span> <span class="n">response</span>
  <span class="k">when</span> <span class="no">Net</span><span class="o">::</span><span class="no">HTTPSuccess</span> <span class="k">then</span>
      <span class="n">document</span> <span class="o">=</span> <span class="no">Nokogiri</span><span class="o">::</span><span class="no">HTML</span><span class="p">(</span><span class="n">response</span><span class="p">.</span><span class="nf">body</span><span class="p">)</span>

      <span class="n">follower_count_element</span> <span class="o">=</span> <span class="n">document</span><span class="p">.</span><span class="nf">at</span><span class="p">(</span><span class="s1">'span.pw-follower-count &gt; a'</span><span class="p">)</span>
      <span class="n">follower_count</span> <span class="o">=</span> <span class="n">follower_count_element</span><span class="o">&amp;</span><span class="p">.</span><span class="nf">text</span><span class="o">&amp;</span><span class="p">.</span><span class="nf">split</span><span class="p">(</span><span class="s1">' '</span><span class="p">)</span><span class="o">&amp;</span><span class="p">.</span><span class="nf">first</span>

      <span class="k">return</span> <span class="n">follower_count</span> <span class="o">||</span> <span class="mi">0</span>
  <span class="k">when</span> <span class="no">Net</span><span class="o">::</span><span class="no">HTTPRedirection</span> <span class="k">then</span>
    <span class="n">location</span> <span class="o">=</span> <span class="n">response</span><span class="p">[</span><span class="s1">'location'</span><span class="p">]</span>
    <span class="k">return</span> <span class="n">load_medium_followers</span><span class="p">(</span><span class="n">location</span><span class="p">,</span> <span class="n">limit</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span>
  <span class="k">else</span>
      <span class="k">return</span> <span class="mi">0</span>
  <span class="k">end</span>
<span class="k">end</span>

<span class="vg">$medium_url</span> <span class="o">=</span> <span class="s2">"https://medium.com/@zhgchgli"</span>
<span class="c1"># could also define in _config.yml and retrieve in Jekyll::Hooks.register :site, :pre_render do |site| site.config</span>

<span class="vg">$medium_followers</span> <span class="o">=</span> <span class="n">load_medium_followers</span><span class="p">(</span><span class="vg">$medium_url</span><span class="p">)</span>

<span class="vg">$medium_followers</span> <span class="o">=</span> <span class="mi">1000</span> <span class="k">if</span> <span class="vg">$medium_followers</span> <span class="o">==</span> <span class="mi">0</span>
<span class="vg">$medium_followers</span> <span class="o">=</span> <span class="vg">$medium_followers</span><span class="p">.</span><span class="nf">to_s</span><span class="p">.</span><span class="nf">reverse</span><span class="p">.</span><span class="nf">scan</span><span class="p">(</span><span class="sr">/\d{1,3}/</span><span class="p">).</span><span class="nf">join</span><span class="p">(</span><span class="s1">','</span><span class="p">).</span><span class="nf">reverse</span>


<span class="no">Jekyll</span><span class="o">::</span><span class="no">Hooks</span><span class="p">.</span><span class="nf">register</span> <span class="ss">:site</span><span class="p">,</span> <span class="ss">:pre_render</span> <span class="k">do</span> <span class="o">|</span><span class="n">site</span><span class="o">|</span>

  <span class="n">tagline</span> <span class="o">=</span> <span class="n">site</span><span class="p">.</span><span class="nf">config</span><span class="p">[</span><span class="s1">'tagline'</span><span class="p">]</span>
  
  <span class="n">followMe</span> <span class="o">=</span> <span class="o">&lt;&lt;-</span><span class="no">HTML</span><span class="sh">
  &lt;a href="</span><span class="si">#{</span><span class="vg">$medium_url</span><span class="si">}</span><span class="sh">" target="_blank" style="display: block;text-align: center;font-style: normal;/* text-decoration: underline; */font-size: 1.2em;color: var(--heading-color);"&gt;</span><span class="si">#{</span><span class="vg">$medium_followers</span><span class="si">}</span><span class="sh">+ Followers on Medium&lt;/a&gt;
</span><span class="no">  HTML</span>

  <span class="n">site</span><span class="p">.</span><span class="nf">config</span><span class="p">[</span><span class="s1">'tagline'</span><span class="p">]</span> <span class="o">=</span> <span class="s2">"</span><span class="si">#{</span><span class="n">followMe</span><span class="si">}</span><span class="s2">"</span><span class="p">;</span>
  <span class="n">site</span><span class="p">.</span><span class="nf">config</span><span class="p">[</span><span class="s1">'tagline'</span><span class="p">]</span> <span class="o">+=</span> <span class="n">tagline</span><span class="p">;</span>

  <span class="n">meta_data</span> <span class="o">=</span> <span class="n">site</span><span class="p">.</span><span class="nf">data</span><span class="p">.</span><span class="nf">dig</span><span class="p">(</span><span class="s1">'locales'</span><span class="p">,</span> <span class="s1">'en'</span><span class="p">,</span> <span class="s1">'meta'</span><span class="p">);</span>
  <span class="c1"># only impletation in en, could impletation to all langs.</span>

  <span class="k">if</span> <span class="n">meta_data</span>
    <span class="n">gmt_plus_8</span> <span class="o">=</span> <span class="no">Time</span><span class="p">.</span><span class="nf">now</span><span class="p">.</span><span class="nf">getlocal</span><span class="p">(</span><span class="s2">"+08:00"</span><span class="p">)</span>
    <span class="n">formatted_time</span> <span class="o">=</span> <span class="n">gmt_plus_8</span><span class="p">.</span><span class="nf">strftime</span><span class="p">(</span><span class="s2">"%Y-%m-%d %H:%M:%S"</span><span class="p">)</span>
    <span class="n">site</span><span class="p">.</span><span class="nf">data</span><span class="p">[</span><span class="s1">'locales'</span><span class="p">][</span><span class="s1">'en'</span><span class="p">][</span><span class="s1">'meta'</span><span class="p">]</span> <span class="o">+=</span> <span class="s2">"&lt;br/&gt;Last updated: </span><span class="si">#{</span><span class="n">formatted_time</span><span class="si">}</span><span class="s2"> +08:00"</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>
<ul>
  <li>原理是註冊一個 Hook 在網站 Render 前，對 config 中的 <code class="language-plaintext highlighter-rouge">tagline</code> 個人資料下方介紹內容區塊，多塞上 Medium 追蹤人數顯示 HTML。</li>
  <li>Medium 追蹤人數會在每次執行都去爬取拿到最新數字</li>
  <li>頁底最後更新時間邏輯也差不多，就是對 locales-&gt;en-&gt;meta 在產生網站時多塞上最後更新時間字串</li>
  <li>補充如果是 Hook 文章產生前，可以拿到 Markdown、Hook 文章產生後，可以拿到產生後的 HTML</li>
</ul>

<p>儲存後可以先在本機下 <code class="language-plaintext highlighter-rouge">bundle exec jekyll s</code> 測試結果：</p>

<p><img src="/assets/5bb7d3a4954f/1*T1idAZIWAJ2N9J054-PFSA.webp" alt="" loading="lazy" decoding="async" width="710" height="483" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3MTAiIGhlaWdodD0iNDgzIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>用瀏覽器打開 <code class="language-plaintext highlighter-rouge">127.0.0.1:4000</code> 查看結果。</p>

<p><img src="/assets/5bb7d3a4954f/1*6JA9ONLP_A0eNL_q-5b6yg.webp" alt="" loading="lazy" decoding="async" width="1081" height="802" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDgxIiBoZWlnaHQ9IjgwMiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>最後在 Github Pages Repo 上的 Actions 加上排程定時自動重新產生網站，就完成了：</p>

<p><img src="/assets/5bb7d3a4954f/1*2BFHmkhnytEwHTkNHwZsTg.webp" alt="" loading="lazy" decoding="async" width="1050" height="589" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDUwIiBoZWlnaHQ9IjU4OSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>在 <a href="https://github.com/cotes2020/jekyll-theme-chirpy" target="_blank">Jekyll (Chirpy Theme)</a> Repo 專案中的 Actions 找到「 <code class="language-plaintext highlighter-rouge">pages-deploy.yml</code> 」在 <code class="language-plaintext highlighter-rouge">on:</code> 新增：</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="na">schedule</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="na">cron</span><span class="pi">:</span> <span class="s2">"</span><span class="s">10</span><span class="nv"> </span><span class="s">1</span><span class="nv"> </span><span class="s">*</span><span class="nv"> </span><span class="s">*</span><span class="nv"> </span><span class="s">*"</span> <span class="c1"># 每天 UTC 01:10 自動執行一次, https://crontab.guru</span>
</code></pre></div></div>

<blockquote>
  <p><em>Plugin 的優點是可以達到動態內容效果(排程更新內容)、不影響網站架構不會在升級時遇到衝突；缺點就是能調整的內容、顯示位置有局限。</em></p>
</blockquote>

<h4 id="jekyll-chirpy-theme-v7x-後的-github-pages-部署問題"><a href="https://github.com/cotes2020/jekyll-theme-chirpy" target="_blank">Jekyll (Chirpy Theme)</a> v7.x 後的 Github Pages 部署問題</h4>

<p>除了網站架構的調整外，v.7.x 的部署腳本也有改變；移除了原本的 <a href="https://github.com/ZhgChgLi/medium-to-jekyll-starter.github.io/blob/main/tools/deploy.sh" target="_blank">deploy.sh</a> 部署腳本，直接使用 Github Actions 的部署步驟：</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># build:</span>
<span class="c1"># ...</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Upload site artifact</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/upload-pages-artifact@v3</span>
        <span class="na">with</span><span class="pi">:</span>
          <span class="na">path</span><span class="pi">:</span> <span class="s2">"</span><span class="s">_site${{</span><span class="nv"> </span><span class="s">steps.pages.outputs.base_path</span><span class="nv"> </span><span class="s">}}"</span>

<span class="err">  </span><span class="na">deploy</span><span class="pi">:</span>
    <span class="na">environment</span><span class="pi">:</span>
      <span class="na">name</span><span class="pi">:</span> <span class="s">github-pages</span>
      <span class="na">url</span><span class="pi">:</span> <span class="s">${{ steps.deployment.outputs.page_url }}</span>
    <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
    <span class="na">needs</span><span class="pi">:</span> <span class="s">build</span>
    <span class="na">steps</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Deploy to GitHub Pages</span>
        <span class="na">id</span><span class="pi">:</span> <span class="s">deployment</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/deploy-pages@v4</span>
</code></pre></div></div>

<p>但是我在部署的過程遇到了問題：</p>

<p><code class="language-plaintext highlighter-rouge">Uploaded artifact size of 1737778940 bytes exceeds the allowed size of 1 GB</code> 因為我的網站內容太大了，導致 Upload Artifact 失敗；但是之前的部署腳本是可以的，所以只好退回去用原本的 <a href="https://github.com/ZhgChgLi/medium-to-jekyll-starter.github.io/blob/main/tools/deploy.sh" target="_blank">deploy.sh</a> ＋ <a href="https://github.com/ZhgChgLi/zhgchgli.github.io/blob/main/.github/workflows/pages-deploy.yml" target="_blank">註解掉上面這一段</a> 。</p>
<h4 id="github-pages-部署時-test-site-步驟一直不通過">Github Pages 部署時 Test Site 步驟一直不通過</h4>

<p><a href="https://github.com/cotes2020/jekyll-theme-chirpy" target="_blank">Jekyll (Chirpy Theme)</a> 部署有一個步驟是 Test Site 自檢測網頁內容是否正確，例如連結是否正常、HTML 標籤是否有缺漏…等等</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># build:</span>
<span class="c1"># ...</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Test site</span>
        <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
          <span class="s">bundle exec htmlproofer _site \</span>
            <span class="s">\-\-disable-external \</span>
            <span class="s">\-\-no-enforce-https \</span>
            <span class="s">\-\-ignore-empty-alt \</span>
            <span class="s">\-\-ignore-urls "/^http:\/\/127.0.0.1/,/^http:\/\/0.0.0.0/,/^http:\/\/localhost/"</span>
</code></pre></div></div>

<p>我自己多加了 <code class="language-plaintext highlighter-rouge">--no-enforce-https</code> <code class="language-plaintext highlighter-rouge">--ignore-empty-alt</code> 忽略 https、html tag沒有 alt 的檢查， <strong>忽略這兩條讓檢查通過(因為暫時無法去改內容)</strong> 。</p>

<p><a href="https://github.com/gjtorikian/html-proofer" target="_blank">htmlproofer</a> 的 CLI 指令官方文件沒有提，翻了好久才在某個 Issue 的 <a href="https://github.com/gjtorikian/html-proofer/issues/727#issuecomment-1334430268" target="_blank">Comment</a> 找到規則：</p>

<p><img src="/assets/5bb7d3a4954f/1*kn6TE3wlIqIA8Nxe8OqTww.webp" alt="https://github.com/gjtorikian/html-proofer/issues/727#issuecomment-1334430268" loading="lazy" decoding="async" width="814" height="234" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4MTQiIGhlaWdodD0iMjM0Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><a href="https://github.com/gjtorikian/html-proofer/issues/727#issuecomment-1334430268" target="_blank">https://github.com/gjtorikian/html-proofer/issues/727#issuecomment-1334430268</a></p>
<h4 id="其他文章補充">其他文章補充</h4>
<ul>
  <li><a href="/posts/zrealm-dev/github-pages-自訂網域教學-namecheap-購域設定完整流程與部署指引-483af5d93297/">Github Pages 自訂網域教學</a></li>
  <li><a href="/posts/zrealm-robotic-process-automation/github-pages-免費快速建置個人-linktree-連結頁面-linkyee-開源模板教學-70aeddb1fd9b/">Linkyee — 使用 GitHub Pages 快速免費建立個人類 LinkTree 連結頁面</a></li>
</ul>]]></content>
  </entry><entry>
    <title type="html">[正體中文] Medium to Jekyll 安裝設定教學</title>
    <link href="https://zhgchg.li/posts/tools/%E6%AD%A3%E9%AB%94%E4%B8%AD%E6%96%87-medium-to-jekyll-%E5%AE%89%E8%A3%9D%E8%A8%AD%E5%AE%9A%E6%95%99%E5%AD%B8-medium-to-jekyll/" rel="alternate" type="text/html" title="[正體中文] Medium to Jekyll 安裝設定教學" />
    <published>2025-01-17T08:00:00+08:00</published>
    <updated>2025-01-17T08:00:00+08:00</updated>
    <id>https://zhgchg.li/posts/tools/medium-to-jekyll</id><summary type="html">Meidum to Jekyll 安裝設定教學</summary><author>
      <name>ZhgChgLi</name>
    </author><category term="tools" /><category term="meidum" /><category term="github" /><category term="jekyll" /><category term="ruby" /><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://zhgchg.li/assets/images/zmediumtomarkdown.jpeg" /><content type="html" xml:base="https://zhgchg.li/posts/tools/%E6%AD%A3%E9%AB%94%E4%B8%AD%E6%96%87-medium-to-jekyll-%E5%AE%89%E8%A3%9D%E8%A8%AD%E5%AE%9A%E6%95%99%E5%AD%B8-medium-to-jekyll/"><![CDATA[<h1 id="start">Start!</h1>
<p><a href="https://github.com/ZhgChgLi/medium-to-jekyll-starter.github.io"><img src="https://opengraph.githubassets.com/91a5dd913bf4d51e6b76fbcc7442c845023bdf93cb1a0ce1ac1c8a40d554f781/ZhgChgLi/medium-to-jekyll-starter.github.io" alt="" /></a></p>

<h2 id="1-前往-template-repo---medium-to-jekyll-startergithubio">1. 前往 Template Repo -&gt; <a href="https://github.com/ZhgChgLi/medium-to-jekyll-starter.github.io">medium-to-jekyll-starter.github.io</a></h2>

<p><img src="/assets/medium-to-jekyll-starter/start-6.png" alt="" loading="lazy" decoding="async" width="1200" height="800" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjgwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>點擊右上角「Use this template」-&gt;「Create a new repository」</p>

<h2 id="2-create-a-new-repository">2. Create a new repository</h2>

<p><img src="/assets/medium-to-jekyll-starter/start-2.png" alt="" loading="lazy" decoding="async" width="1200" height="800" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjgwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<ul>
  <li>Repository name: 通常為 <code class="language-plaintext highlighter-rouge">帳號或組織名稱.github.io</code>，必須以 <code class="language-plaintext highlighter-rouge">*.github.io</code> 為結尾。</li>
  <li>必須為 <code class="language-plaintext highlighter-rouge">Public</code> Repo 才能使用 Github Pages</li>
</ul>

<h3 id="調整-github-actions-執行權限">調整 GitHub Actions 執行權限</h3>
<p><img src="/assets/medium-to-jekyll-starter/github-action-permissions.png" alt="" loading="lazy" decoding="async" width="1200" height="800" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjgwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<ul>
  <li>建立後，由於 GitHub 的安全性設定，您需要前往儲存庫設定中啟用 GitHub Actions 的執行權限。</li>
</ul>

<h2 id="3-create-gh-pages-branch-if-needed">3. Create gh-pages branch if needed</h2>
<p><img src="/assets/medium-to-jekyll-starter/start-3.png" alt="" loading="lazy" decoding="async" width="1200" height="800" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjgwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<ul>
  <li>在 Repo 主頁點擊「<code class="language-plaintext highlighter-rouge">main</code>」分支選單，輸入「<code class="language-plaintext highlighter-rouge">gh-pages</code>」，若無則選擇「Create branch <code class="language-plaintext highlighter-rouge">gh-pages</code> from <code class="language-plaintext highlighter-rouge">main</code>」</li>
  <li>若 <code class="language-plaintext highlighter-rouge">gh-pages</code> 分支已存在、或建立時出現「Sorry, that branch already exists.」則可跳過此步驟</li>
</ul>

<h2 id="4-enable-github-pages前往-settings---pages---build-and-deployment">4. Enable Github Pages，前往 Settings -&gt; Pages -&gt; Build and deployment</h2>
<p><img src="/assets/medium-to-jekyll-starter/start-4.png" alt="" loading="lazy" decoding="async" width="1200" height="800" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjgwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<ul>
  <li>選擇「<code class="language-plaintext highlighter-rouge">gh-pages</code>」分支，點擊「<code class="language-plaintext highlighter-rouge">Save</code>」儲存設定</li>
</ul>

<h3 id="執行首次部署">執行首次部署</h3>
<p><img src="/assets/medium-to-jekyll-starter/first-deploy.png" alt="" loading="lazy" decoding="async" width="1200" height="800" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjgwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<ul>
  <li>Repo -&gt; 「Actions」-&gt;「Build and Deploy」-&gt;「Run workflow」-&gt;「Branch: main, Run workflow」</li>
</ul>

<h2 id="5-等待所有部署工作完成">5. 等待所有部署工作完成</h2>
<p><img src="/assets/medium-to-jekyll-starter/start-5.png" alt="" loading="lazy" decoding="async" width="1200" height="800" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjgwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<ul>
  <li>🟢 pages build and deployment</li>
  <li>🟢 Build and Deploy</li>
</ul>

<h2 id="6-前往網站查看結果">6. 前往網站查看結果</h2>
<blockquote>
  <p>https://<code class="language-plaintext highlighter-rouge">帳號或組織名稱.github.io</code></p>
</blockquote>

<p><img src="/assets/medium-to-jekyll-starter/done.png" alt="" loading="lazy" decoding="async" width="1200" height="800" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjgwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<h3 id="troubleshooting">Troubleshooting</h3>
<p>如果頁面只顯示：</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>--- layout: home # Index page ---
</code></pre></div></div>
<p>代表 Github Pages 設定錯誤或還在部署中、或是之前的頁面 Cache，請使用強制刷新貨無痕瀏覽器重新打開一次網頁。</p>

<blockquote>
  <p>首次部署成功！🎉🎉🎉請繼續設定成同步您的 Medium 帳號。</p>
</blockquote>

<hr />

<h1 id="github-repo-github-actions-設定">Github Repo (Github Actions) 設定</h1>
<h2 id="1-前往所屬-github-repo-的-github-actions-頁面---點擊zmediumtomarkdown--點擊zmediumtomarkdownyml">1. 前往所屬 Github Repo 的 Github Actions 頁面 -&gt; 點擊「ZMediumToMarkdown」-&gt; 點擊「ZMediumToMarkdown.yml」</h2>
<p><img src="/assets/medium-to-jekyll-starter/github-1.png" alt="" loading="lazy" decoding="async" width="1200" height="800" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjgwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>
<blockquote>
  <p>https://github.com/{ORG}/{REPO_NAME}/blob/main/.github/workflows/ZMediumToMarkdown.yml</p>
</blockquote>

<h2 id="2-點擊右方編輯按鈕">2. 點擊右方編輯按鈕</h2>
<p><img src="/assets/medium-to-jekyll-starter/github-2.png" alt="" loading="lazy" decoding="async" width="1200" height="800" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjgwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<h2 id="3-設定-medium-文章自動同步參數">3. 設定 Medium 文章自動同步參數</h2>
<p><img src="/assets/medium-to-jekyll-starter/github-3.png" alt="" loading="lazy" decoding="async" width="1200" height="800" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjgwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">name</span><span class="pi">:</span> <span class="s">ZMediumToMarkdown</span>
<span class="na">on</span><span class="pi">:</span>
  <span class="na">workflow_dispatch</span><span class="pi">:</span>
  <span class="na">schedule</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="na">cron</span><span class="pi">:</span> <span class="s2">"</span><span class="s">10</span><span class="nv"> </span><span class="s">1</span><span class="nv"> </span><span class="s">15</span><span class="nv"> </span><span class="s">*</span><span class="nv"> </span><span class="s">*"</span> <span class="c1"># Runs at 01:10(UTC), everyday.</span>
    <span class="c1"># 設定排程多久自動同步一次</span>
    <span class="c1"># ref: https://crontab.guru/</span>

<span class="na">jobs</span><span class="pi">:</span>
  <span class="na">ZMediumToMarkdown</span><span class="pi">:</span>
    <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
    <span class="na">steps</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">ZMediumToMarkdown Automatic Bot</span>
      <span class="na">uses</span><span class="pi">:</span> <span class="s">ZhgChgLi/ZMediumToMarkdown@main</span>
      <span class="na">with</span><span class="pi">:</span>
        <span class="na">command</span><span class="pi">:</span> <span class="s2">"</span><span class="s">--cookie_uid</span><span class="nv"> </span><span class="s">${{</span><span class="nv"> </span><span class="s">secrets.MEDIUM_COOKIE_UID</span><span class="nv"> </span><span class="s">}}</span><span class="nv"> </span><span class="s">--cookie_sid</span><span class="nv"> </span><span class="s">${{</span><span class="nv"> </span><span class="s">secrets.MEDIUM_COOKIE_SID</span><span class="nv"> </span><span class="s">}}</span><span class="nv"> </span><span class="s">-j</span><span class="nv"> </span><span class="s">zhgchgli_test"</span>
        <span class="c1"># zhgchgli_test 替換成你的 Meidum 使用者</span>
        <span class="c1"># 例如 https://medium.com/@zhgchgli -&gt; zhgchgli</span>
        <span class="c1"># ref: https://github.com/ZhgChgLi/ZMediumToMarkdown?tab=readme-ov-file#usage</span>
</code></pre></div></div>

<h3 id="提供有存取權限的-medium-帳號-cookies">提供有存取權限的 Medium 帳號 Cookies。</h3>
<ul>
  <li>若您有在 Paywall 的文章則必須提供</li>
  <li>若發現 Medium 文章同步不完整 (缺漏文章)，代表同步時被 Medium 防火牆阻擋，也必須提供</li>
</ul>

<h4 id="取得-meidum-帳號-cookies-medium_cookie_uid--medium_cookie_sid-步驟">取得 Meidum 帳號 Cookies MEDIUM_COOKIE_UID &amp; MEDIUM_COOKIE_SID 步驟：</h4>

<p><img src="/assets/medium-to-jekyll-starter/github-4.png" alt="" loading="lazy" decoding="async" width="1200" height="800" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjgwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>
<ol>
  <li>登入擁有存取權限的 Medium 帳號，進入 <a href="https://medium.com/me/stats">Medium 後台</a></li>
  <li>在空白處點擊右鍵</li>
  <li>選擇「Inspect」</li>
  <li>出現 Developer Console 後選擇「Application」</li>
  <li>選擇「Cookies」-&gt;「https://medium.com」</li>
  <li>往下滾動找到「<code class="language-plaintext highlighter-rouge">sid</code>」「<code class="language-plaintext highlighter-rouge">uid</code>」</li>
  <li>點兩下複製這兩個欄位的值</li>
</ol>

<h4 id="將-meidum-帳號-cookies-安全存放在-github-repo-secrets">將 Meidum 帳號 Cookies 安全存放在 Github Repo Secrets</h4>
<h5 id="1-前往-github-repo-settings---secrets-and-variables---actions---new-repository-secret">1. 前往 Github Repo Settings -&gt; Secrets and variables -&gt; Actions -&gt; New repository secret</h5>
<p><img src="/assets/medium-to-jekyll-starter/github-5.png" alt="" loading="lazy" decoding="async" width="1200" height="800" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjgwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>
<blockquote>
  <p>https://github.com/{ORG}/{REPO_NAME}/settings/secrets/actions/new</p>
</blockquote>

<h5 id="2-new-secret---medium_cookie_sid">2. New secret - MEDIUM_COOKIE_SID</h5>
<p><img src="/assets/medium-to-jekyll-starter/github-6.png" alt="" loading="lazy" decoding="async" width="1200" height="800" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjgwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>
<ul>
  <li>Name: <code class="language-plaintext highlighter-rouge">MEDIUM_COOKIE_SID</code></li>
  <li>Secret: 貼上上一步複製的 Medium 帳號 <code class="language-plaintext highlighter-rouge">sid</code> 值
    <h5 id="3-new-secret---medium_cookie_uid">3. New secret - MEDIUM_COOKIE_UID</h5>
    <p><img src="/assets/medium-to-jekyll-starter/github-7.png" alt="" loading="lazy" decoding="async" width="1200" height="800" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjgwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>
  </li>
  <li>Name: <code class="language-plaintext highlighter-rouge">MEDIUM_COOKIE_UID</code></li>
  <li>Secret: 貼上上一步複製的 Medium 帳號 <code class="language-plaintext highlighter-rouge">uid</code> 值</li>
</ul>

<h5 id="完成">完成</h5>
<p>帳號無特別登出或遇到問題，Cookies 不會失效。</p>

<p>如果在同步中出現以下訊息並且同步的文章不完整：</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>This post is behind Medium's paywall. You must provide valid Medium Member login cookies to download the full post.
</code></pre></div></div>
<p>代表 Cookies 已失效，請重新照上述步驟重新設定。</p>

<h2 id="4-首次手動同步repo---github-actions---點擊zmediumtomarkdown--點擊enable-workflow">4. 首次手動同步，Repo -&gt; Github Actions -&gt; 點擊「ZMediumToMarkdown」-&gt; 點擊「Enable workflow」</h2>
<p>初次執行，我們可以自己手動同步一次檢查設定是否正確。
<img src="/assets/medium-to-jekyll-starter/github-9.png" alt="" loading="lazy" decoding="async" width="1200" height="800" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjgwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<h2 id="5-等待同步文章與網站部署工作完成">5. 等待同步文章與網站部署工作完成</h2>
<p><img src="/assets/medium-to-jekyll-starter/github-10.png" alt="" loading="lazy" decoding="async" width="1200" height="800" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjgwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>等待以下三個 Actions 工作執行完成並且沒有錯誤：</p>
<ul>
  <li>🟢 ZMediumToMarkdown</li>
  <li>🟢 pages build and deployment</li>
  <li>🟢 Build and Deploy</li>
</ul>

<h2 id="6-重整網頁查看結果enjoy">6. 重整網頁查看結果，Enjoy!</h2>

<blockquote>
  <p>⚠️ 請注意！所有檔案變更都會觸發：</p>

  <ul>
    <li>🟢 pages build and deployment</li>
    <li>🟢 Build and Deploy</li>
  </ul>

  <p>需等待以上兩個部署工作完成，網站更改才會生效。</p>
</blockquote>

<hr />

<h1 id="jekyll-網站設定">Jekyll 網站設定</h1>

<h2 id="網站基本設定">網站基本設定</h2>
<ul>
  <li>
    <p><code class="language-plaintext highlighter-rouge">./_config.yml</code></p>

    <p><strong>請務必調整</strong> 其中的 <code class="language-plaintext highlighter-rouge">url:</code> 改成您的 Github Pages 網址以及其他網站資料設定。</p>
  </li>
  <li>分享功能設定：<code class="language-plaintext highlighter-rouge">./_data/share.yml</code></li>
  <li>定義文章作者資訊：<code class="language-plaintext highlighter-rouge">./_data/authors.yml</code></li>
</ul>

<h2 id="左側-sidebar-設定">左側 Sidebar 設定</h2>
<ul>
  <li><code class="language-plaintext highlighter-rouge">./tabs</code></li>
  <li>底部連結按鈕：<code class="language-plaintext highlighter-rouge">./_data/contact.yml</code></li>
</ul>

<h2 id="網站底部及其他文字內容設定">網站底部及其他文字內容設定</h2>
<ul>
  <li><code class="language-plaintext highlighter-rouge">./locales/{Lang}.yml</code> default is <code class="language-plaintext highlighter-rouge">/locales/en.yml</code></li>
</ul>

<h2 id="本地測試">本地測試</h2>
<ol>
  <li>確定你的環境有安裝並使用 Ruby &gt;= 3.1 版本</li>
  <li><code class="language-plaintext highlighter-rouge">cd ./</code></li>
  <li><code class="language-plaintext highlighter-rouge">bundle install</code></li>
  <li><code class="language-plaintext highlighter-rouge">bundle exec jekyll s</code></li>
  <li>Go to <a href="http://127.0.0.1:4000/">http://127.0.0.1:4000/</a> 查看結果</li>
  <li>Press <code class="language-plaintext highlighter-rouge">Ctrl-c</code> to stop.</li>
</ol>

<p>*網站基本設定檔案有調整需要重新執行才會生效。</p>]]></content>
  </entry><entry>
    <title type="html">Google Apps Script Web App｜打造 Github Action CI/CD 自訂表單提升開發效率</title>
    <link href="https://zhgchg.li/posts/zrealm-dev/google-apps-script-web-app-%E6%89%93%E9%80%A0-github-action-ci-cd-%E8%87%AA%E8%A8%82%E8%A1%A8%E5%96%AE%E6%8F%90%E5%8D%87%E9%96%8B%E7%99%BC%E6%95%88%E7%8E%87-4cb4437818f2/" rel="alternate" type="text/html" title="Google Apps Script Web App｜打造 Github Action CI/CD 自訂表單提升開發效率" />
    <published>2025-01-11T19:19:43+08:00</published>
    <updated>2025-12-17T23:45:42+08:00</updated>
    <id>https://zhgchg.li/posts/zrealm-dev/4cb4437818f2</id><summary type="html">針對缺乏動態 GUI 表單的 Github Action CI/CD，使用 Google Apps Script Web App 建立組織內專屬表單，串接 Github API 自動取得分支並觸發工作流程，並整合 Slack 通知與 Jira、Asana 工單查詢，實現免維護、低成本的自動化打包工具。</summary><author>
      <name>ZhgChgLi</name>
    </author><category term="ZRealm Dev." /><category term="ios-app-development" /><category term="google-apps-script" /><category term="github-actions" /><category term="slack" /><category term="github" /><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://zhgchg.li/assets/4cb4437818f2/1*TiGXBQdPaCM6r2J1RHrgnA.webp" /><content type="html" xml:base="https://zhgchg.li/posts/zrealm-dev/google-apps-script-web-app-%E6%89%93%E9%80%A0-github-action-ci-cd-%E8%87%AA%E8%A8%82%E8%A1%A8%E5%96%AE%E6%8F%90%E5%8D%87%E9%96%8B%E7%99%BC%E6%95%88%E7%8E%87-4cb4437818f2/"><![CDATA[<h3 id="使用-google-apps-script-web-app-表單串接-github-action-cicd-工作">使用 Google Apps Script Web App 表單串接 Github Action CI/CD 工作</h3>

<p>Github Action Workflow 表單優化與整合其他工作流程工具(Jira, Asana, Slack..)提升開發效率。</p>

<p><img src="/assets/4cb4437818f2/1*TiGXBQdPaCM6r2J1RHrgnA.webp" alt="左：原始的 Github Action Workflow Form / 右： 最終成果 (GAS Web App Form)" loading="lazy" decoding="async" width="1222" height="692" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjIyIiBoZWlnaHQ9IjY5MiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>左：原始的 Github Action Workflow Form / 右： <a href="https://script.google.com/macros/s/AKfycbw8SuK7lLLMdY86y3jxMJyzXqa5tdxJryRnteOnNi-lK--j6CmKYXj7UuU58DiS0NSVvA/exec" target="_blank">最終成果 (GAS Web App Form)</a></p>
<h3 id="202507-update">2025/07 Update:</h3>

<p>此功能已整合到實際打包工具使用，可參考最新文章案例：「 <a href="/posts/zrealm-dev/ci-cd-打包工具平台-使用-google-apps-script-web-app-串接-github-actions-提升跨團隊效率-4273e57e7148/"><strong>CI/CD 實戰指南（四）：使用 Google Apps Script Web App 串接 GitHub Actions 建置免費易用的打包工具平台</strong></a> 」</p>
<h3 id="背景">背景</h3>

<p>之前的團隊使用 Github Action &amp; Self-hosted Github Runner + Slack 搭建整套 CI/CD 服務；整體效果不錯，對於 App 開發者來說建置與維護相對容易，只需照著官方文件提供的 YAML 參數完成設定就會自動觸發、機器方面也可以輕易的使用自己的機器當成 Runner 使用，服務本身都是由 Github 維護，我們不需在意版本升級等問題並且 Runner 是反向跟 Github 要任務下來做，不需要特別開對外網路開口。</p>

<blockquote>
  <p><em>等於同時享受到類似 Bitrise 的 GUI YAML 建置方式又有像 Jenkins 那樣使用自架機器的彈性跟較低的建置成本，但不用像 Jenkins 那樣需要花時間維護服務本身。</em></p>
</blockquote>

<blockquote>
  <p><strong>未來有時間再寫一篇完整的 App CI/CD x Github Action 搭建過程。</strong></p>
</blockquote>

<h4 id="問題github-action-cicd-gui-form">問題：Github Action CI/CD GUI Form</h4>

<p><img src="/assets/4cb4437818f2/1*55tCLFvuHtTyyvSLSv1vMA.webp" alt="Github Action GUI Form" loading="lazy" decoding="async" width="406" height="499" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0MDYiIGhlaWdodD0iNDk5Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>Github Action GUI Form</p>

<blockquote>
  <p><em>在 App 開發中，在 CD 觸發打包測試版、正式版或送審時通常會需要提供一些外部參數或是依照需求選擇環境、分支，才會開始執行工作。</em></p>
</blockquote>

<p>不同於 Jenkins 是自建服務，有完整的 Web GUI；Github Action 沒有，唯一的 Web GUI Form 是在 Actions 中點擊 Run workflow 可以自訂一個簡易的表單供使用者輸入外部參數，然後觸發 CI/CD 工作流程。</p>

<p>通常會使用這個 CD 打包的使用者，不一定是 App 開發者本身、也不一定擁有這個專案的權限；例如 QA 需要打包指定版本、PM/Backend 需要打包某個開發中版本進行測試；Github Action Form 需要有該專案的權限才能使用，但使用者不要說能不能有專案權限、甚至是不一定有工程背景。</p>

<p><strong>而且我們無法在這邊做動態表單或資料驗證。</strong></p>

<blockquote>
  <p><em>因此我們需要另外拉一個 GUI 服務給其他使用者操作使用。</em></p>
</blockquote>

<h4 id="自建-slack-app-解決">自建 Slack App 解決</h4>

<p>之前團隊是有熱愛做自動化的同仁自己用 Kotlin+Ktor 搭建了一個完整的 Slack App 網路服務，串接 Slack 訊息、表單、指令…等功能，接收、轉發 CD 打包請求，觸發 Github Action 執行操作並將結果串回 Slack。</p>

<blockquote>
  <p><em>目前沒有開發資源跟之前一樣使用 Kotlin+Ktor 建置服務</em></p>
</blockquote>

<h4 id="自己寫-webiosmacos-app-工具">自己寫 Web/iOS/macOS App 工具</h4>

<p>目前團隊原本是使用 Jenkins 有基礎的 Web 可以給其他使用者登入使用、另外還有自行開發了一個 App 串接 Jenkins 封裝一些參數讓非工程背景的使用者更方便使用。</p>

<blockquote>
  <p><strong><em>不過遷移到 Github Action 之後這整套就廢棄了。</em></strong></p>
</blockquote>

<h4 id="-private-github-pages">❌ Private Github Pages</h4>

<p>應該有機會直接建置 Github Pages 來當 CI/CD Web GUI，但是目前只有 Github <a href="https://docs.github.com/en/enterprise-server@3.13/admin/configuration/configuring-your-enterprise/configuring-github-pages-for-your-enterprise#enabling-public-sites-for-github-pages" target="_blank">Enterprise</a> 可以設定 Github Pages 存取權限，其他方案就算是 Private Repo 也會是公開的；無安全性可言。</p>
<h4 id="-slack-app但使用-google-apps-script-建置">❌ Slack App，但使用 Google Apps Script 建置</h4>

<p>一開始想說依照之前團隊的經驗使用 Slack App 做為 CI/CD GUI Form 服務，但是目前沒有資源跟之前一樣使用 Kotlin+Ktor 建置服務；所以想說先使用 Function as a Service 服務快速嘗試建置。</p>

<p>Function as a Service 有很多種， <a href="https://cloud.google.com/functions?hl=zh-tw" target="_blank">Cloud Functions</a> 的自由度比較高，但由於組織 IT 限制，無法隨意新增 Public Cloud Functions 並且有收費問題；因此還是回到我們的老朋友 — Google Apps Script。</p>

<blockquote>
  <p><em>之前寫過好幾篇關於 Google Apps Script 做自動化的文章，有興趣的朋友可以參考：</em></p>
</blockquote>

<blockquote>
  <p><em>1. <strong>「 <a href="/posts/zrealm-robotic-process-automation/google-apps-script-打造每日數據報表自動化-rpa-快速串接-google-analytics-與-google-sheet-f6713ba3fee3/">使用 Google Apps Script 實現每日數據報表 RPA 自動化</a> 」</strong></em></p>
</blockquote>

<blockquote>
  <p><em>2. 「 <a href="https://medium.com/zrealm-robotic-process-automation/%E7%B0%A1%E5%96%AE-3-%E6%AD%A5%E9%A9%9F-%E6%89%93%E9%80%A0%E5%85%8D%E8%B2%BB-ga4-%E8%87%AA%E5%8B%95%E6%95%B8%E6%93%9A%E9%80%9A%E7%9F%A5%E6%A9%9F%E5%99%A8%E4%BA%BA-1e85b8df2348?source=collection_home---6------1-----------------------" target="_blank">簡單 3 步驟 — 打造免費 GA4 自動數據通知機器人</a> 」</em></p>
</blockquote>

<blockquote>
  <p><em>3. 「 <a href="https://medium.com/zrealm-robotic-process-automation/crashlytics-google-analytics-%E8%87%AA%E5%8B%95%E6%9F%A5%E8%A9%A2-app-crash-free-users-rate-793cb8f89b72?source=collection_home---6------8-----------------------" target="_blank">Crashlytics + Google Analytics 自動查詢 App Crash-Free Users Rate</a> 」</em></p>
</blockquote>

<blockquote>
  <p><em>4. 「 <a href="https://medium.com/zrealm-robotic-process-automation/crashlytics-big-query-%E6%89%93%E9%80%A0%E6%9B%B4%E5%8D%B3%E6%99%82%E4%BE%BF%E5%88%A9%E7%9A%84-crash-%E8%BF%BD%E8%B9%A4%E5%B7%A5%E5%85%B7-e77b80cc6f89?source=collection_home---6------9-----------------------" target="_blank">Crashlytics + Big Query 打造更即時便利的 Crash 追蹤工具</a> 」</em></p>
</blockquote>

<p>總而言之，Google Apps Script 是 Google 另一個 Function as a Service 服務，主要特色是免費跟與 Google 服務之間可以快速整合；但是限制也較多，例如只能用它的語言、執行時間不能超過 6 分鐘、有執行次數上限、不支援多執行緒…等等，細節可以參考 <a href="/posts/zrealm-robotic-process-automation/google-apps-script-打造每日數據報表自動化-rpa-快速串接-google-analytics-與-google-sheet-f6713ba3fee3/">我之前的文章</a> 。</p>

<p>結論是不可行，原因是：</p>
<ul>
  <li><a href="https://www.cloudflare.com/zh-tw/learning/serverless/what-is-serverless/" target="_blank">Function as a Service 冷啟動問題</a> 。
服務一段時間沒呼叫會進入睡眠，再次呼叫要花較長時間啟動(3~≥ 5 秒)； <strong>Slack App 對於 API 響應時間非常嚴格，服務需要在 3 秒內回應否則視為失敗</strong> ，Slack 這邊就會直接噴錯誤、事件監聽也會被認為丟失，導致重複發送。</li>
  <li><strong>Google Apps Script doGet, doPost 方法無法取得 Headers。</strong> 
<strong>這會導致無法走 <a href="https://api.slack.com/authentication/verifying-requests-from-slack" target="_blank">官方的安全驗證</a> 、無法關閉 <a href="https://api.slack.com/authentication/verifying-requests-from-slack" target="_blank">Slack Retry</a> 。</strong></li>
  <li>Google Apps Script 單執行緒問題。
如果要串接其他服務響應時間都會超過 3 秒，直接被 Slack 判定為失敗。</li>
</ul>

<p>有勉強使用 Slack 訊息、Block Kit、Form 串接完整個流程，但是太容易觸發上述問題，後來直接放棄。</p>

<blockquote>
  <p><strong><em>如果要做這套還是要走自己起伺服器、服務，不要走 Function as a Service!!</em></strong></p>
</blockquote>

<h4 id="-slack-workflow-form">❌ Slack Workflow Form</h4>

<p><img src="/assets/4cb4437818f2/1*KWBMdFswvl1KPdnTLStzhQ.webp" alt="" loading="lazy" decoding="async" width="1136" height="812" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMTM2IiBoZWlnaHQ9IjgxMiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/4cb4437818f2/1*hnDPyOfGCTW_yJf71krMnA.webp" alt="Slack Workflow Form (❌ 無法客制化)" loading="lazy" decoding="async" width="519" height="458" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1MTkiIGhlaWdodD0iNDU4Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><a href="https://slack.com/intl/zh-tw/help/articles/24720245025555-%E8%87%AA%E5%8B%95%E5%8C%96%EF%BC%9A%E4%BD%BF%E7%94%A8%E7%B0%A1%E6%98%93%E8%A1%A8%E5%96%AE%E6%94%B6%E9%9B%86%E8%B3%87%E8%A8%8A" target="_blank">Slack Workflow Form</a> (❌ 無法客制化)</p>

<p>另外也嘗試過 Slack 內建的自動化功能 Workflow Form，不過他無法做到動態表單內容 (e.g. 撈分支讓使用者選)，唯一能客製化的只有後續送資料的步驟。</p>
<h3 id="-google-apps-script-web-app-gui-form-表單">✅ Google Apps Script Web App GUI Form 表單</h3>

<p>山不轉路轉，轉念一想好像也不用糾結在一定要用 Slack 整合上，用 Slack 整合是最好的方案，因為是直接整進既有的團隊協作工具，不需要發散額外去學新工具；但是迫於資源限制，我們只能退而求其次找其他穩定又好用的方法。</p>

<blockquote>
  <p><em>回頭想到 Google Apps Script 本身就能部署成 Web App，可以在 Web doGet 時回應 GUI Form 表單，送出表單後觸發後續的 Github 串接處理。</em></p>
</blockquote>

<h4 id="最終成果-">最終成果 🎉</h4>

<p><img src="/assets/4cb4437818f2/1*pzW-Yki-4HbE2nYXC4q-Aw.webp" alt="Demo Web App Form" loading="lazy" decoding="async" width="653" height="672" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2NTMiIGhlaWdodD0iNjcyIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><a href="https://script.google.com/macros/s/AKfycbw8SuK7lLLMdY86y3jxMJyzXqa5tdxJryRnteOnNi-lK--j6CmKYXj7UuU58DiS0NSVvA/exec" target="_blank">Demo Web App Form</a></p>
<h4 id="工作流程"><strong>工作流程</strong></h4>

<p>我們使用 Google Apps Script Web App 建置 CI/CD 表單，直接綁定 Google Workspace 帳號，設定只有組織內的使用者可以存取；自動獲取當前登入的使用者信箱、使用 Github Repo 共用帳號(或借用某個有權限的帳號)的 Personal Access Token 打 Github API 獲得分支列表，送出後同樣打 API 觸發 Github Action 開始執行 CI/CD 工作。</p>

<p>另外，我們可以用使用者的信箱透過 Slack App 去打 Slack API，取得該使用者的 Slack ID，再透過 Slack App 傳送訊息，通知 CI/CD 任務執行情況。</p>

<p>也可以再與其他工具、開發流程進行整合，例如先從 Asana, Jira 取得工單，選擇後再透過 Github API 查找分支、觸發 Github Action，最後再透過 Slack 通知給使用者。</p>
<h4 id="step-1-建立-google-apps-script-web-app-form">Step 1. 建立 Google Apps Script Web App Form</h4>

<p>前往 &gt; <a href="https://script.google.com/home" target="_blank">Google Apps Script</a> ，新增專案。</p>

<p><img src="/assets/4cb4437818f2/1*T3if7Dfo0iJaa4N5VZyA1Q.webp" alt="" loading="lazy" decoding="async" width="928" height="370" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MjgiIGhlaWdodD0iMzcwIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<h4 id="step-2-建立表單內容gas-程式">Step 2. 建立表單內容、GAS 程式</h4>

<p>太久沒有寫 HTML,CSS 也懶得自己設計樣式，直接請 ChatGPT 產一個有一點設計的 HTML 表單範本。</p>

<p><img src="/assets/4cb4437818f2/1*IPv0afE5FAFj40F22s8Umg.webp" alt="" loading="lazy" decoding="async" width="1316" height="1139" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMzE2IiBoZWlnaHQ9IjExMzkiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/4cb4437818f2/1*F_HrfV_k16g_ojm1WIDDuA.webp" alt="" loading="lazy" decoding="async" width="978" height="608" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NzgiIGhlaWdodD0iNjA4Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>在 GAS 左方檔案列表點「+」新增檔案，輸入檔案名稱「 <code class="language-plaintext highlighter-rouge">Form.html</code> 」並把 GPT 產的 HTML 表單範本內容貼上。</p>

<p><code class="language-plaintext highlighter-rouge">Form.html：</code></p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">&lt;!--HTML &amp; Style Gen by ChatGPT 4o--&gt;</span>
<span class="cp">&lt;!DOCTYPE html&gt;</span>
<span class="nt">&lt;html&gt;</span>
<span class="nt">&lt;head&gt;</span>
  <span class="nt">&lt;meta</span> <span class="na">charset=</span><span class="s">"UTF-8"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;meta</span> <span class="na">name=</span><span class="s">"viewport"</span> <span class="na">content=</span><span class="s">"width=device-width, initial-scale=1.0"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;title&gt;</span><span class="cp">&lt;?=title?&gt;</span><span class="nt">&lt;/title&gt;</span>
  <span class="nt">&lt;style&gt;</span>
    body {
      font-family: Arial, sans-serif;
      margin: 0;
      padding: 20px;
      background-color: #f7f7f7;
    }
    .form-container {
      max-width: 600px;
      margin: auto;
      padding: 20px;
      background-color: #ffffff;
      border-radius: 8px;
      box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
    }
    .form-container h2 {
      margin-bottom: 20px;
      color: #333333;
    }
    .form-group {
      margin-bottom: 15px;
    }
    .form-group label {
      display: block;
      margin-bottom: 5px;
      font-weight: bold;
      color: #555555;
    }
    .form-group input,
    .form-group select,
    .form-group textarea {
      width: 95%;
      padding: 10px;
      border: 1px solid #cccccc;
      border-radius: 4px;
      font-size: 16px;
    }
    .form-group input[type="radio"] {
      width: auto;
      margin-right: 10px;
    }
    .form-group .radio-label {
      display: inline-block;
      margin-right: 20px;
    }
    .form-group button {
      background-color: #4CAF50;
      color: white;
      padding: 10px 20px;
      border: none;
      border-radius: 4px;
      cursor: pointer;
      font-size: 16px;
    }
    .form-group button:hover {
      background-color: #45a049;
    }
    .message {
      margin-top: 20px;
      padding: 15px;
      border-radius: 5px;
      font-size: 1em;
      text-align: center;
    }
    .message.success {
      background-color: #d4edda;
      color: #155724;
      border: 1px solid #c3e6cb;
    }
    .message.error {
      background-color: #f8d7da;
      color: #721c24;
      border: 1px solid #f5c6cb;
    }
    .hidden {
      display: none;
    }
  <span class="nt">&lt;/style&gt;</span>
<span class="nt">&lt;/head&gt;</span>
<span class="nt">&lt;body&gt;</span>
  <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"form-container"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;h2&gt;</span><span class="cp">&lt;?=title?&gt;</span><span class="nt">&lt;/h2&gt;</span>
    <span class="nt">&lt;form</span> <span class="na">id=</span><span class="s">"myForm"</span><span class="nt">&gt;</span>
      <span class="nt">&lt;div</span> <span class="na">id=</span><span class="s">"message-block"</span> <span class="na">class=</span><span class="s">"hidden"</span><span class="nt">&gt;&lt;/div&gt;</span>
      <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"form-group"</span><span class="nt">&gt;</span>
        <span class="nt">&lt;label</span> <span class="na">for=</span><span class="s">"email"</span><span class="nt">&gt;</span>電子郵件：<span class="nt">&lt;/label&gt;</span>
        <span class="nt">&lt;input</span> <span class="na">type=</span><span class="s">"email"</span> <span class="na">value=</span><span class="s">"&lt;?=email?&gt;"</span> <span class="err">readonly</span><span class="nt">/&gt;</span>
      <span class="nt">&lt;/div&gt;</span>
      <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"form-group"</span><span class="nt">&gt;</span>
        <span class="nt">&lt;label</span> <span class="na">for=</span><span class="s">"buildNumber"</span><span class="nt">&gt;</span>版本號：<span class="nt">&lt;/label&gt;</span>
        <span class="nt">&lt;input</span> <span class="na">type=</span><span class="s">"number"</span> <span class="na">value=</span><span class="s">"&lt;?=buildNumber?&gt;"</span><span class="nt">/&gt;</span>
      <span class="nt">&lt;/div&gt;</span>
      <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"form-group"</span><span class="nt">&gt;</span>
        <span class="nt">&lt;label</span> <span class="na">for=</span><span class="s">"branch"</span><span class="nt">&gt;</span>Review 中的 PR：<span class="nt">&lt;/label&gt;</span>
        <span class="nt">&lt;select</span> <span class="na">id=</span><span class="s">"branch"</span> <span class="na">name=</span><span class="s">"branch"</span><span class="nt">&gt;</span>
          <span class="nt">&lt;option&gt;</span>請選擇<span class="nt">&lt;/option&gt;</span>
          <span class="cp">&lt;? pullRequests.forEach(pullRequest =&gt; { ?&gt;</span>
            <span class="nt">&lt;option</span> <span class="na">value=</span><span class="s">"&lt;?=pullRequest.head.ref?&gt;"</span><span class="nt">&gt;</span>[<span class="cp">&lt;?=pullRequest.state?&gt;</span>] <span class="cp">&lt;?=pullRequest.title?&gt;</span><span class="nt">&lt;/option&gt;</span>
          <span class="cp">&lt;? }); ?&gt;</span>
        <span class="nt">&lt;/select&gt;</span>
      <span class="nt">&lt;/div&gt;</span>
      <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"form-group"</span><span class="nt">&gt;</span>
        <span class="nt">&lt;label</span> <span class="na">for=</span><span class="s">"message"</span><span class="nt">&gt;</span>更新內容：<span class="nt">&lt;/label&gt;</span>
        <span class="nt">&lt;textarea</span> <span class="na">id=</span><span class="s">"message"</span> <span class="na">name=</span><span class="s">"message"</span> <span class="na">rows=</span><span class="s">"4"</span> <span class="na">placeholder=</span><span class="s">"請輸入您的訊息"</span><span class="nt">&gt;&lt;/textarea&gt;</span>
      <span class="nt">&lt;/div&gt;</span>
      <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"form-group"</span><span class="nt">&gt;</span>
        <span class="nt">&lt;button</span> <span class="na">type=</span><span class="s">"submit"</span><span class="nt">&gt;</span>送出<span class="nt">&lt;/button&gt;</span>
      <span class="nt">&lt;/div&gt;</span>
    <span class="nt">&lt;/form&gt;</span>
  <span class="nt">&lt;/div&gt;</span>
  <span class="nt">&lt;script&gt;</span>
    function displayMessage(ok, message) {
      const messageBlock = document.getElementById('message-block');
      messageBlock.className = ok ? 'message success' : 'message error';
      messageBlock.innerHTML = message;
      messageBlock.classList.remove('hidden');
    }
    
    document.getElementById("myForm").addEventListener("submit", function(e) {
      e.preventDefault();
      const formData = new FormData(this);
      const formObject = Object.fromEntries(formData);
      google.script.run.withSuccessHandler((response) =&gt; {
        displayMessage(response.ok, response.message);
      }).processForm(formObject);
    });
  <span class="nt">&lt;/script&gt;</span>
<span class="nt">&lt;/body&gt;</span>
<span class="nt">&lt;/html&gt;</span>
</code></pre></div></div>

<p>表單內容可依照需求自行調整。</p>

<p><code class="language-plaintext highlighter-rouge">程式碼.gs：</code></p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nf">doGet</span><span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
  <span class="c1">// 對應到左方檔案 Form.html</span>
  <span class="kd">const</span> <span class="nx">htmlTemplate</span> <span class="o">=</span> <span class="nx">HtmlService</span><span class="p">.</span><span class="nf">createTemplateFromFile</span><span class="p">(</span><span class="dl">'</span><span class="s1">Form</span><span class="dl">'</span><span class="p">);</span>
  
  <span class="kd">const</span> <span class="nx">email</span> <span class="o">=</span> <span class="nx">Session</span><span class="p">.</span><span class="nf">getActiveUser</span><span class="p">().</span><span class="nf">getEmail</span><span class="p">();</span>
  <span class="c1">// 取得使用者信箱，只限 執行身份：存取網頁程式的使用者 設定有效</span>
  
  <span class="kd">const</span> <span class="nx">title</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">App CD 打包請求表單</span><span class="dl">"</span><span class="p">;</span>
  
  <span class="kd">const</span> <span class="nx">buildNumber</span> <span class="o">=</span> <span class="nf">genBuildNumber</span><span class="p">();</span>

  <span class="nx">htmlTemplate</span><span class="p">.</span><span class="nx">email</span> <span class="o">=</span> <span class="nx">email</span><span class="p">;</span>
  <span class="nx">htmlTemplate</span><span class="p">.</span><span class="nx">title</span> <span class="o">=</span> <span class="nx">title</span><span class="p">;</span>
  <span class="nx">htmlTemplate</span><span class="p">.</span><span class="nx">pullRequests</span> <span class="o">=</span> <span class="p">[];</span> <span class="c1">// 下一步在串接 Github...</span>
  <span class="nx">htmlTemplate</span><span class="p">.</span><span class="nx">buildNumber</span> <span class="o">=</span> <span class="nx">buildNumber</span><span class="p">;</span>

  <span class="kd">const</span> <span class="nx">html</span> <span class="o">=</span> <span class="nx">htmlTemplate</span><span class="p">.</span><span class="nf">evaluate</span><span class="p">();</span>
  <span class="nx">html</span><span class="p">.</span><span class="nf">setTitle</span><span class="p">(</span><span class="nx">title</span><span class="p">);</span>
  <span class="c1">//html.setWidth(600) // 設定頁面寬度</span>

  <span class="k">return</span> <span class="nx">html</span>
<span class="p">}</span>

<span class="kd">function</span> <span class="nf">processForm</span><span class="p">(</span><span class="nx">object</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">return</span> <span class="p">{</span><span class="dl">"</span><span class="s2">ok</span><span class="dl">"</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span> <span class="dl">"</span><span class="s2">message</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">請求送出成功！</span><span class="dl">"</span><span class="p">};</span>
<span class="p">}</span>

<span class="kd">function</span> <span class="nf">genBuildNumber</span><span class="p">()</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">now</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Date</span><span class="p">();</span>
  <span class="kd">const</span> <span class="nx">formattedDate</span> <span class="o">=</span> <span class="nx">Utilities</span><span class="p">.</span><span class="nf">formatDate</span><span class="p">(</span><span class="nx">now</span><span class="p">,</span> <span class="dl">"</span><span class="s2">Asia/Taipei</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">yyyyMMddHHmmss</span><span class="dl">"</span><span class="p">);</span>
  <span class="kd">const</span> <span class="nx">milliseconds</span> <span class="o">=</span> <span class="nx">now</span><span class="p">.</span><span class="nf">getMilliseconds</span><span class="p">().</span><span class="nf">toString</span><span class="p">().</span><span class="nf">padStart</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="dl">'</span><span class="s1">0</span><span class="dl">'</span><span class="p">);</span> <span class="c1">// 確保毫秒是 3 位數</span>
  <span class="k">return</span> <span class="s2">`</span><span class="p">${</span><span class="nx">formattedDate</span><span class="p">}${</span><span class="nx">milliseconds</span><span class="p">}</span><span class="s2">`</span><span class="p">;</span> 
<span class="p">}</span>
</code></pre></div></div>

<p>這一步，我們先把表單 GUI 完成，下一步再來串接 Github API 拿到 PR 分支列表。</p>

<p><img src="/assets/4cb4437818f2/1*RN9ftVBdgCA-IL8ra9jvTg.webp" alt="" loading="lazy" decoding="async" width="509" height="324" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1MDkiIGhlaWdodD0iMzI0Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<h4 id="step-2-部署-google-apps-script-web-app-form">Step 2. 部署 Google Apps Script Web App Form</h4>

<p>我們先把剛剛的內容部署一次，查看結果。</p>

<p>在 GAS 右上角選擇「部署」-&gt; 「新增部署作業」-&gt;「網頁應用程式」：</p>

<p><img src="/assets/4cb4437818f2/1*ouVO18FtOcX8vdeGCEtwCw.webp" alt="" loading="lazy" decoding="async" width="330" height="215" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzMzAiIGhlaWdodD0iMjE1Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><img src="/assets/4cb4437818f2/1*4BuEtIA4H_-Q9WALonYxYg.webp" alt="" loading="lazy" decoding="async" width="396" height="302" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzOTYiIGhlaWdodD0iMzAyIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><img src="/assets/4cb4437818f2/1*JagpVPTGD-W0lhIJ5nrRew.webp" alt="" loading="lazy" decoding="async" width="762" height="599" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjIiIGhlaWdodD0iNTk5Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>執行身份跟誰可以存取分別可以設置成：</p>

<p><strong>執行身份：</strong></p>
<ul>
  <li>我 <code class="language-plaintext highlighter-rouge">統一都用你的帳號身分執行腳本。</code></li>
  <li>存取網頁程式的使用者 <code class="language-plaintext highlighter-rouge">會以當前登入的 Google 帳號使用者身份執行腳本。</code></li>
</ul>

<p><strong>誰可以存取：</strong></p>
<ul>
  <li>只有我自己</li>
  <li><strong>XXX 同個組織中的所有使用者</strong> <code class="language-plaintext highlighter-rouge">只有同組織＋已登入的 Google 帳號使用者可以存取。</code></li>
  <li>所有已登入 Google 帳號的使用者 <code class="language-plaintext highlighter-rouge">已登入的 Google 帳號使用者都可以存取。</code></li>
  <li>所有人 <code class="language-plaintext highlighter-rouge">不需要登入 Google 帳號、所有人都可以公開存取。</code></li>
</ul>

<blockquote>
  <p><em>我們選擇「誰可以存取：XXX 同個組織中的所有使用者」＋「執行身份：存取網頁程式的使用者」就能 <strong>自動限制只有組織帳號的人可以使用</strong> ，並且用他自己的身份執行！</em></p>
</blockquote>

<blockquote>
  <p><em>是一個很方便權限控管的功能！</em></p>
</blockquote>

<p>選完點擊右下「部署」。</p>

<p><img src="/assets/4cb4437818f2/1*dvyqYb5kcta402j3RUYJog.webp" alt="" loading="lazy" decoding="async" width="770" height="605" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NzAiIGhlaWdodD0iNjA1Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>網頁應用程式中的網址，就是 Web App 存取網址。</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>https://script.google.com/macros/s/AKfycbw8SuK7lLLMdY86y3jxMJyzXqa5tdxJryRnteOnNi-lK--j6CmKYXj7UuU58DiS0NSVvA/exec
</code></pre></div></div>

<blockquote>
  <p><em>網址很長很醜，但是沒辦法，只能自己找短網址工具縮短一下。</em></p>
</blockquote>

<p><strong>點網址打開頁面查看效果：</strong></p>

<p><img src="/assets/4cb4437818f2/1*jbx4IO4DEhqYfwI_UTQk5Q.webp" alt="" loading="lazy" decoding="async" width="709" height="641" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3MDkiIGhlaWdodD0iNjQxIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><strong>這邊要多提兩個 GAS 限制：</strong></p>
<ul>
  <li>GAS Web App 上方警告提示，預設無法隱藏</li>
  <li>GAS Web App 是用 IFrame 把我們的頁面嵌在裡面，因此很難做到 100% RWD 效果
只能使用 <code class="language-plaintext highlighter-rouge">.setWidth()</code> 調整視窗寬度</li>
</ul>

<h4 id="google-apps-script-授權警告">Google Apps Script 授權警告</h4>

<p><strong>第一次使用</strong> 、點擊「偵錯」或「執行」可能會出下以下授權警告：</p>

<p><img src="/assets/4cb4437818f2/1*9q8KZGHER9vdtnbKVVQB9g.webp" alt="" loading="lazy" decoding="async" width="301" height="196" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzMDEiIGhlaWdodD0iMTk2Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><img src="/assets/4cb4437818f2/1*iWrkqMf8vkEGkiwkI1amIw.webp" alt="" loading="lazy" decoding="async" width="521" height="503" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1MjEiIGhlaWdodD0iNTAzIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>選擇想要執行的身份帳號，如果有出現「這個應用程式未經 Google 驗證」則點擊「進階」-&gt;「前往 XXX (不安全)」，選擇「允許」：</p>

<p><img src="/assets/4cb4437818f2/1*ucaqLxh-TOgJIaGyqqFd3A.webp" alt="" loading="lazy" decoding="async" width="504" height="595" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1MDQiIGhlaWdodD0iNTk1Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><img src="/assets/4cb4437818f2/1*l-tma_YICU24goKvZvl7Ww.webp" alt="" loading="lazy" decoding="async" width="653" height="468" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2NTMiIGhlaWdodD0iNDY4Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><img src="/assets/4cb4437818f2/1*JAs_3__Qt2XeDcQiKEUNhg.webp" alt="" loading="lazy" decoding="async" width="504" height="789" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1MDQiIGhlaWdodD0iNzg5Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>如果 GAS 程式權限有變動(例如：多加存取 Google Sheet…etc)才會需要再重新授權，不然點過一次就不會再出現。</p>

<blockquote>
  <p><em>如遇到：已封鎖存取權「XXX」未完成 Google 驗證程序， <a href="/posts/zrealm-dev/ci-cd-打包工具平台-使用-google-apps-script-web-app-串接-github-actions-提升跨團隊效率-4273e57e7148/">可參考我最新文章的 GCP 設定。</a></em></p>
</blockquote>

<h4 id="step-3-串接-github-api-取得-pr-分支列表">Step 3. 串接 Github API 取得 PR 分支列表</h4>

<p>我們新增一個 <code class="language-plaintext highlighter-rouge">Github.gs</code> 程式檔案存放 Github API 相關邏輯。</p>

<p><code class="language-plaintext highlighter-rouge">Github.gs：</code></p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// SECRET</span>
<span class="kd">const</span> <span class="nx">githubPersonalAccessToken</span> <span class="o">=</span> <span class="dl">""</span>
<span class="c1">// 使用你的 Github 帳號或組織共用的 Github 帳號創建 PAT</span>
<span class="c1">// https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens</span>

<span class="c1">// 方式 1: Restful API 存取</span>
<span class="kd">function</span> <span class="nf">githubAPI</span><span class="p">(</span><span class="nx">method</span><span class="p">,</span> <span class="nx">path</span><span class="p">,</span> <span class="nx">payload</span> <span class="o">=</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">try</span> <span class="p">{</span>
    <span class="kd">const</span> <span class="nx">url</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">https://api.github.com</span><span class="dl">"</span><span class="o">+</span><span class="nx">path</span><span class="p">;</span>  
    <span class="kd">var</span> <span class="nx">options</span> <span class="o">=</span> <span class="p">{</span>
      <span class="na">method</span><span class="p">:</span> <span class="nx">method</span><span class="p">,</span>
      <span class="na">headers</span><span class="p">:</span> <span class="p">{</span>
        <span class="dl">"</span><span class="s2">Accept</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">application/vnd.github+json</span><span class="dl">"</span><span class="p">,</span>
        <span class="dl">"</span><span class="s2">Authorization</span><span class="dl">"</span><span class="p">:</span> <span class="s2">`Bearer </span><span class="p">${</span><span class="nx">githubPersonalAccessToken</span><span class="p">}</span><span class="s2">`</span><span class="p">,</span>
        <span class="dl">"</span><span class="s2">X-GitHub-Api-Version</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">2022-11-28</span><span class="dl">"</span>
      <span class="p">}</span>
    <span class="p">};</span>

    <span class="k">if </span><span class="p">(</span><span class="nx">method</span><span class="p">.</span><span class="nf">toLowerCase</span><span class="p">().</span><span class="nf">trim</span><span class="p">()</span> <span class="o">==</span> <span class="dl">"</span><span class="s2">post</span><span class="dl">"</span><span class="p">)</span> <span class="p">{</span>
      <span class="nx">options</span><span class="p">.</span><span class="nx">payload</span> <span class="o">=</span> <span class="nx">JSON</span><span class="p">.</span><span class="nf">stringify</span><span class="p">(</span><span class="nx">payload</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="kd">const</span> <span class="nx">response</span> <span class="o">=</span> <span class="nx">UrlFetchApp</span><span class="p">.</span><span class="nf">fetch</span><span class="p">(</span><span class="nx">url</span><span class="p">,</span> <span class="nx">options</span><span class="p">);</span>
    <span class="kd">const</span> <span class="nx">data</span> <span class="o">=</span> <span class="nx">JSON</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="nx">response</span><span class="p">.</span><span class="nf">getContentText</span><span class="p">());</span>
    <span class="k">return</span> <span class="nx">data</span><span class="p">;</span>
  <span class="p">}</span> <span class="k">catch </span><span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">throw</span> <span class="nx">error</span><span class="p">;</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="c1">// 方式 2: GraphQL 存取</span>
<span class="c1">// Github API 部分更細的查詢功能只有 GraphQL API 有提供</span>
<span class="c1">// https://docs.github.com/en/graphql</span>
<span class="kd">function</span> <span class="nf">githubGraphQL</span><span class="p">(</span><span class="nx">query</span><span class="p">,</span> <span class="nx">variables</span><span class="p">)</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">url</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">https://api.github.com/graphql</span><span class="dl">"</span><span class="p">;</span>
  <span class="kd">const</span> <span class="nx">payload</span> <span class="o">=</span> <span class="p">{</span>
    <span class="na">query</span><span class="p">:</span> <span class="nx">query</span><span class="p">,</span>
    <span class="na">variables</span><span class="p">:</span> <span class="nx">variables</span>
  <span class="p">};</span>

  <span class="kd">const</span> <span class="nx">options</span> <span class="o">=</span> <span class="p">{</span>
    <span class="na">method</span><span class="p">:</span> <span class="dl">"</span><span class="s2">post</span><span class="dl">"</span><span class="p">,</span>
    <span class="na">contentType</span><span class="p">:</span> <span class="dl">"</span><span class="s2">application/json</span><span class="dl">"</span><span class="p">,</span>
    <span class="na">headers</span><span class="p">:</span> <span class="p">{</span>
      <span class="dl">"</span><span class="s2">Accept</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">application/vnd.github+json</span><span class="dl">"</span><span class="p">,</span>
      <span class="dl">"</span><span class="s2">Authorization</span><span class="dl">"</span><span class="p">:</span> <span class="s2">`Bearer </span><span class="p">${</span><span class="nx">githubPersonalAccessToken</span><span class="p">}</span><span class="s2">`</span><span class="p">,</span>
      <span class="dl">"</span><span class="s2">X-GitHub-Api-Version</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">2022-11-28</span><span class="dl">"</span>
    <span class="p">},</span>
    <span class="na">payload</span><span class="p">:</span> <span class="nx">JSON</span><span class="p">.</span><span class="nf">stringify</span><span class="p">(</span><span class="nx">payload</span><span class="p">)</span>
  <span class="p">};</span>

  <span class="k">try</span> <span class="p">{</span>
    <span class="kd">const</span> <span class="nx">response</span> <span class="o">=</span> <span class="nx">UrlFetchApp</span><span class="p">.</span><span class="nf">fetch</span><span class="p">(</span><span class="nx">url</span><span class="p">,</span> <span class="nx">options</span><span class="p">);</span>
    <span class="kd">const</span> <span class="nx">data</span> <span class="o">=</span> <span class="nx">JSON</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="nx">response</span><span class="p">.</span><span class="nf">getContentText</span><span class="p">());</span>
    <span class="k">return</span> <span class="nx">data</span><span class="p">;</span>
  <span class="p">}</span> <span class="k">catch </span><span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">throw</span> <span class="nx">error</span><span class="p">;</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="c1">// GraphQL Example:</span>
<span class="c1">// const query = `</span>
<span class="c1">//   query($owner: String!, $repo: String!) {</span>
<span class="c1">//     repository(owner: $owner, name: $repo) {</span>
<span class="c1">//       pullRequests(states: OPEN, first: 100, orderBy: { field: CREATED_AT, direction: DESC }) {</span>
<span class="c1">//         nodes {</span>
<span class="c1">//           title</span>
<span class="c1">//           url</span>
<span class="c1">//           number</span>
<span class="c1">//           createdAt</span>
<span class="c1">//           author {</span>
<span class="c1">//             login</span>
<span class="c1">//           }</span>
<span class="c1">//           headRefName</span>
<span class="c1">//           baseRefName</span>
<span class="c1">//           body</span>
<span class="c1">//         }</span>
<span class="c1">//         pageInfo {</span>
<span class="c1">//           hasNextPage</span>
<span class="c1">//           endCursor</span>
<span class="c1">//         }</span>
<span class="c1">//       }</span>
<span class="c1">//     }</span>
<span class="c1">//   }</span>
<span class="c1">// `;</span>
<span class="c1">// const variables = {</span>
<span class="c1">//   owner: "swiftlang",</span>
<span class="c1">//   repo: "swift"</span>
<span class="c1">// };</span>
<span class="c1">// const response = githubGraphQL(query, variables);</span>
</code></pre></div></div>

<p>Github API 有兩種存取方式，一種是傳統的 Restful，另一種是更彈性的 GraphQL；本文以 Restful 為例。</p>

<p><code class="language-plaintext highlighter-rouge">程式碼.gs：</code></p>
<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">function</span> <span class="nf">doGet</span><span class="p">(</span><span class="n">e</span><span class="p">)</span> <span class="p">{</span>
  <span class="c1">// 對應到左方檔案 Form.html</span>
  <span class="k">const</span> <span class="n">htmlTemplate</span> <span class="p">=</span> <span class="nc">HtmlService</span><span class="p">.</span><span class="nf">createTemplateFromFile</span><span class="p">(</span><span class="err">'</span><span class="nc">Form</span><span class="err">'</span><span class="p">);</span>
  
  <span class="k">const</span> <span class="n">email</span> <span class="p">=</span> <span class="nc">Session</span><span class="p">.</span><span class="nf">getActiveUser</span><span class="p">().</span><span class="nf">getEmail</span><span class="p">();</span>
  <span class="c1">// 取得使用者信箱，只限 執行身份：存取網頁程式的使用者 設定有效</span>

  <span class="k">const</span> <span class="n">title</span> <span class="p">=</span> <span class="s">"App CD 打包請求表單"</span><span class="p">;</span>
  
  <span class="k">const</span> <span class="n">pullRequests</span> <span class="p">=</span> <span class="nf">githubAPI</span><span class="p">(</span><span class="s">"get"</span><span class="p">,</span> <span class="s">"/repos/swiftlang/swift/pulls"</span><span class="p">);</span>
  <span class="c1">// 以 https://github.com/swiftlang/swift/pulls 為例</span>
  
  <span class="k">const</span> <span class="n">buildNumber</span> <span class="p">=</span> <span class="nf">genBuildNumber</span><span class="p">();</span>

  <span class="n">htmlTemplate</span><span class="p">.</span><span class="n">email</span> <span class="p">=</span> <span class="n">email</span><span class="p">;</span>
  <span class="n">htmlTemplate</span><span class="p">.</span><span class="n">title</span> <span class="p">=</span> <span class="n">title</span><span class="p">;</span>
  <span class="n">htmlTemplate</span><span class="p">.</span><span class="n">pullRequests</span> <span class="p">=</span> <span class="n">pullRequests</span><span class="p">;</span>
  <span class="n">htmlTemplate</span><span class="p">.</span><span class="n">buildNumber</span> <span class="p">=</span> <span class="n">buildNumber</span><span class="p">;</span>

  <span class="k">const</span> <span class="n">html</span> <span class="p">=</span> <span class="n">htmlTemplate</span><span class="p">.</span><span class="nf">evaluate</span><span class="p">();</span>
  <span class="n">html</span><span class="p">.</span><span class="nf">setTitle</span><span class="p">(</span><span class="n">title</span><span class="p">);</span>
  <span class="c1">//html.setWidth(600) // 設定頁面寬度</span>

  <span class="k">return</span> <span class="n">html</span>
<span class="p">}</span>

<span class="n">function</span> <span class="nf">processForm</span><span class="p">(</span><span class="k">object</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">if</span> <span class="p">(</span><span class="k">object</span><span class="p">.</span><span class="n">buildNumber</span> <span class="p">==</span> <span class="s">""</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="p">{</span><span class="s">"ok"</span><span class="p">:</span> <span class="k">false</span><span class="p">,</span> <span class="s">"message"</span><span class="p">:</span> <span class="s">"請輸入版本號！"</span><span class="p">};</span>
  <span class="p">}</span>
  <span class="k">if</span> <span class="p">(</span><span class="k">object</span><span class="p">.</span><span class="n">branch</span> <span class="p">==</span> <span class="s">""</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="p">{</span><span class="s">"ok"</span><span class="p">:</span> <span class="k">false</span><span class="p">,</span> <span class="s">"message"</span><span class="p">:</span> <span class="s">"請選擇分支版本！"</span><span class="p">};</span>
  <span class="p">}</span>

  <span class="c1">// 帶上你要帶到 Github Action 的參數</span>
  <span class="k">const</span> <span class="n">payload</span> <span class="p">=</span> <span class="p">{</span>
    <span class="n">ref</span><span class="p">:</span> <span class="k">object</span><span class="p">.</span><span class="n">branch</span><span class="p">,</span>
    <span class="n">inputs</span><span class="p">:</span> <span class="p">{</span>
      <span class="n">buildNumber</span><span class="p">:</span> <span class="k">object</span><span class="p">.</span><span class="n">buildNumber</span>
    <span class="p">}</span>
  <span class="p">};</span>
  
  <span class="c1">//  </span>
  <span class="k">try</span> <span class="p">{</span>
    <span class="k">const</span> <span class="n">response</span> <span class="p">=</span> <span class="nf">githubAPI</span><span class="p">(</span><span class="s">"post"</span><span class="p">,</span> <span class="s">"/repos/zhgchgli0718/ios-project-for-github-action-ci-cd-demo/actions/workflows/CD-Job.yml/dispatches"</span><span class="p">,</span> <span class="n">payload</span><span class="p">);</span>
    <span class="c1">// 以 https://github.com/zhgchgli0718/ios-project-for-github-action-ci-cd-demo/blob/main/.github/workflows/CD-Job.yml 為例</span>

    <span class="k">return</span> <span class="p">{</span><span class="s">"ok"</span><span class="p">:</span> <span class="k">true</span><span class="p">,</span> <span class="s">"message"</span><span class="p">:</span> <span class="err">`打包請求發送成功</span><span class="p">!&lt;</span><span class="n">br</span><span class="p">/&gt;</span><span class="err">對應分支：</span><span class="p">&lt;</span><span class="n">strong</span><span class="p">&gt;</span><span class="err">$</span><span class="p">{</span><span class="k">object</span><span class="p">.</span><span class="n">branch</span><span class="p">}&lt;/</span><span class="n">strong</span><span class="p">&gt;&lt;</span><span class="n">br</span><span class="p">/&gt;</span><span class="err">版號：</span><span class="p">&lt;</span><span class="n">strong</span><span class="p">&gt;</span><span class="err">$</span><span class="p">{</span><span class="k">object</span><span class="p">.</span><span class="n">buildNumber</span><span class="p">}&lt;/</span><span class="n">strong</span><span class="p">&gt;</span><span class="err">`</span><span class="p">};</span>
  <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="n">error</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="p">{</span><span class="s">"ok"</span><span class="p">:</span> <span class="k">false</span><span class="p">,</span> <span class="s">"message"</span><span class="p">:</span> <span class="s">"發生錯誤："</span><span class="p">+</span><span class="n">error</span><span class="p">.</span><span class="n">message</span><span class="p">};</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">processForm</code> 方法中處理表單回傳的內容，也可以多加上更多吃</p>
<h4 id="gas-x-github-api-x-github-action">GAS x Github API x Github Action</h4>

<p>這邊多補充一下對應到的 Github Action。</p>

<p><code class="language-plaintext highlighter-rouge">CD-Job.yml：</code></p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># CD 打包工作</span>

<span class="na">name</span><span class="pi">:</span> <span class="s">CD-Job</span>

<span class="na">on</span><span class="pi">:</span>
  <span class="na">workflow_dispatch</span><span class="pi">:</span>
    <span class="na">inputs</span><span class="pi">:</span>
      <span class="na">buildNumber</span><span class="pi">:</span> <span class="c1"># 對應 GAS payload.inpus.xxx</span>
        <span class="na">description</span><span class="pi">:</span> <span class="s1">'</span><span class="s">版本號碼'</span>
        <span class="na">required</span><span class="pi">:</span> <span class="kc">false</span>
        <span class="na">type</span><span class="pi">:</span> <span class="s">string</span>
      <span class="c1"># ...More</span>
      <span class="c1"># Inputs 類型可參考官方文件：https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#onworkflow_dispatchinputs</span>
      
<span class="na">jobs</span><span class="pi">:</span>
  <span class="na">some-job</span><span class="pi">:</span>
    <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
    <span class="na">steps</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Print Inputs</span>
        <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
          <span class="s">echo "Release Build Number: ${{ github.event.inputs.buildNumber }}"    </span>
</code></pre></div></div>
<h4 id="step-4-重新部署-google-apps-script-web-app-form">Step 4. 重新部署 Google Apps Script Web App Form</h4>

<blockquote>
  <p><strong><em>⚠️請注意，GAS 程式碼的任何調整都需要重新部署，才會生效。⚠️</em></strong></p>
</blockquote>

<blockquote>
  <p><strong><em>⚠️請注意，GAS 程式碼的任何調整都需要重新部署，才會生效。⚠️</em></strong></p>
</blockquote>

<blockquote>
  <p><strong><em>⚠️請注意，GAS 程式碼的任何調整都需要重新部署，才會生效。⚠️</em></strong></p>
</blockquote>

<p>GAS 右上角選擇「部署」-&gt; 選擇右上角「編輯」-&gt; 版本選擇「建立新版本」</p>

<p><img src="/assets/4cb4437818f2/1*ZMAB_m6HmsSsGSqqfZiRpA.webp" alt="" loading="lazy" decoding="async" width="208" height="238" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDgiIGhlaWdodD0iMjM4Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><img src="/assets/4cb4437818f2/1*rUWlfzASAaeXcXUh4LavZw.webp" alt="" loading="lazy" decoding="async" width="776" height="619" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NzYiIGhlaWdodD0iNjE5Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><img src="/assets/4cb4437818f2/1*HAr5TZtpnQeG-Ril0Rjf-g.webp" alt="" loading="lazy" decoding="async" width="782" height="612" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3ODIiIGhlaWdodD0iNjEyIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>點擊「部署」-&gt; 完成。</p>

<p><img src="/assets/4cb4437818f2/1*z6lw7R8ivUseSzR4IZgPdg.webp" alt="" loading="lazy" decoding="async" width="777" height="611" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NzciIGhlaWdodD0iNjExIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><img src="/assets/4cb4437818f2/1*eTdgKGQ1lRM7sCRXQgZ-DA.webp" alt="" loading="lazy" decoding="async" width="770" height="610" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NzAiIGhlaWdodD0iNjEwIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><strong>再回到網頁上重新整理，就能看到修改後的結果了：</strong></p>

<p><img src="/assets/4cb4437818f2/1*iI49OJC1uTyMEgzGTBowxQ.webp" alt="" loading="lazy" decoding="async" width="657" height="583" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2NTciIGhlaWdodD0iNTgzIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<blockquote>
  <p><strong><em>⚠️請注意，GAS 程式碼的任何調整都需要重新部署，才會生效。⚠️</em></strong></p>
</blockquote>

<blockquote>
  <p><strong><em>⚠️請注意，GAS 程式碼的任何調整都需要重新部署，才會生效。⚠️</em></strong></p>
</blockquote>

<blockquote>
  <p><strong><em>⚠️請注意，GAS 程式碼的任何調整都需要重新部署，才會生效。⚠️</em></strong></p>
</blockquote>

<h3 id="done-">Done! 🎉🎉🎉</h3>

<p><img src="/assets/4cb4437818f2/1*pzW-Yki-4HbE2nYXC4q-Aw.webp" alt="" loading="lazy" decoding="async" width="653" height="672" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2NTMiIGhlaWdodD0iNjcyIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><img src="/assets/4cb4437818f2/1*9aaNeemezNPRlSgbLFrseA.webp" alt="Demo Web App Form" loading="lazy" decoding="async" width="648" height="628" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2NDgiIGhlaWdodD0iNjI4Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><a href="https://script.google.com/macros/s/AKfycbw8SuK7lLLMdY86y3jxMJyzXqa5tdxJryRnteOnNi-lK--j6CmKYXj7UuU58DiS0NSVvA/exec" target="_blank">Demo Web App Form</a></p>

<p>現在你可以在組織內分享這個連結給其他夥伴，他們可以直接使用這個網頁 GUI 執行 CI/CD 工作。</p>
<h4 id="延伸-1-用使用者的信箱查詢-slack-user-id--發送更新進度通知">延伸 (1)— 用使用者的信箱查詢 Slack User ID &amp; 發送、更新進度通知</h4>

<p>前文有提到，我們希望及時通知使用者 CI/CD 執行狀況，我們可以使用使用者提供的信箱去查 Slack User ID。</p>

<p><code class="language-plaintext highlighter-rouge">Slack.gs：</code></p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">slackBotToken</span> <span class="o">=</span> <span class="dl">""</span>
<span class="c1">// https://medium.com/zrealm-robotic-process-automation/slack-chatgpt-integration-bd94cc88f9c9</span>

<span class="kd">function</span> <span class="nf">slackRequest</span><span class="p">(</span><span class="nx">path</span><span class="p">,</span> <span class="nx">content</span><span class="p">)</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">options</span> <span class="o">=</span> <span class="p">{</span>
    <span class="na">method</span><span class="p">:</span> <span class="dl">"</span><span class="s2">post</span><span class="dl">"</span><span class="p">,</span>
    <span class="na">contentType</span><span class="p">:</span> <span class="dl">"</span><span class="s2">application/json</span><span class="dl">"</span><span class="p">,</span>
    <span class="na">headers</span><span class="p">:</span> <span class="p">{</span>
      <span class="na">Authorization</span><span class="p">:</span> <span class="s2">`Bearer </span><span class="p">${</span><span class="nx">slackBotToken</span><span class="p">}</span><span class="s2">`</span><span class="p">,</span> <span class="c1">// Use the bot token for authorization,</span>
      <span class="dl">'</span><span class="s1">X-Slack-No-Retry</span><span class="dl">'</span><span class="p">:</span> <span class="mi">1</span>
    <span class="p">},</span>
    <span class="na">payload</span><span class="p">:</span> <span class="nx">JSON</span><span class="p">.</span><span class="nf">stringify</span><span class="p">(</span><span class="nx">content</span><span class="p">)</span>
  <span class="p">};</span>

  <span class="k">try</span> <span class="p">{</span>
    <span class="kd">const</span> <span class="nx">response</span> <span class="o">=</span> <span class="nx">UrlFetchApp</span><span class="p">.</span><span class="nf">fetch</span><span class="p">(</span><span class="dl">"</span><span class="s2">https://slack.com/api/</span><span class="dl">"</span><span class="o">+</span><span class="nx">path</span><span class="p">,</span> <span class="nx">options</span><span class="p">);</span>
    <span class="kd">const</span> <span class="nx">responseData</span> <span class="o">=</span> <span class="nx">JSON</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="nx">response</span><span class="p">.</span><span class="nf">getContentText</span><span class="p">());</span>
    <span class="k">if </span><span class="p">(</span><span class="nx">responseData</span><span class="p">.</span><span class="nx">ok</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">return</span> <span class="nx">responseData</span>
    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
      <span class="k">throw</span> <span class="k">new</span> <span class="nc">Error</span><span class="p">(</span><span class="s2">`Slack: </span><span class="p">${</span><span class="nx">responseData</span><span class="p">.</span><span class="nx">error</span><span class="p">}</span><span class="s2">`</span><span class="p">);</span>
    <span class="p">}</span>
  <span class="p">}</span> <span class="k">catch </span><span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">throw</span> <span class="nx">error</span><span class="p">;</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="c1">// 用信箱查詢 Slask UID</span>
<span class="kd">function</span> <span class="nf">getSlackUserId</span><span class="p">(</span><span class="nx">email</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">return</span> <span class="nf">slackRequest</span><span class="p">(</span><span class="s2">`users.lookupByEmail?email=</span><span class="p">${</span><span class="nf">encodeURIComponent</span><span class="p">(</span><span class="nx">email</span><span class="p">)}</span><span class="s2">`</span><span class="p">)?.</span><span class="nx">user</span><span class="p">?.</span><span class="nx">id</span><span class="p">;</span>
<span class="p">}</span>

<span class="c1">// 發送訊息給目標 Slack UID (channelID)</span>
<span class="kd">function</span> <span class="nf">sendSlackMessage</span><span class="p">(</span><span class="nx">channelId</span><span class="p">,</span> <span class="nx">ts</span> <span class="o">=</span> <span class="kc">null</span><span class="p">,</span> <span class="nx">value</span><span class="p">)</span>  <span class="p">{</span>
  <span class="kd">var</span> <span class="nx">content</span> <span class="o">=</span> <span class="p">{</span>
    <span class="na">channel</span><span class="p">:</span> <span class="nx">channelId</span>
  <span class="p">};</span>

  <span class="k">if </span><span class="p">(</span><span class="nx">ts</span> <span class="o">!=</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">content</span><span class="p">.</span><span class="nx">thread_ts</span> <span class="o">=</span> <span class="nx">ts</span><span class="p">;</span>
  <span class="p">}</span>
  
  <span class="k">if </span><span class="p">(</span><span class="k">typeof</span> <span class="nx">value</span> <span class="o">===</span> <span class="dl">"</span><span class="s2">string</span><span class="dl">"</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">content</span><span class="p">.</span><span class="nx">text</span> <span class="o">=</span> <span class="nx">value</span><span class="p">;</span>
  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
    <span class="nx">content</span><span class="p">.</span><span class="nx">blocks</span> <span class="o">=</span> <span class="nx">value</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="k">return</span> <span class="nf">slackRequest</span><span class="p">(</span><span class="dl">"</span><span class="s2">chat.postMessage</span><span class="dl">"</span><span class="p">,</span> <span class="nx">content</span><span class="p">);</span>
<span class="p">}</span>

<span class="c1">// 更新發送的訊息內容</span>
<span class="kd">function</span> <span class="nf">updateSlackMessage</span><span class="p">(</span><span class="nx">channelId</span><span class="p">,</span> <span class="nx">ts</span> <span class="o">=</span> <span class="kc">null</span><span class="p">,</span> <span class="nx">value</span><span class="p">)</span>  <span class="p">{</span>
  <span class="kd">var</span> <span class="nx">content</span> <span class="o">=</span> <span class="p">{</span>
    <span class="na">channel</span><span class="p">:</span> <span class="nx">channelId</span>
  <span class="p">};</span>

  <span class="k">if </span><span class="p">(</span><span class="nx">ts</span> <span class="o">!=</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">content</span><span class="p">.</span><span class="nx">ts</span> <span class="o">=</span> <span class="nx">ts</span><span class="p">;</span>
  <span class="p">}</span>
  
  <span class="k">if </span><span class="p">(</span><span class="k">typeof</span> <span class="nx">value</span> <span class="o">===</span> <span class="dl">"</span><span class="s2">string</span><span class="dl">"</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">content</span><span class="p">.</span><span class="nx">text</span> <span class="o">=</span> <span class="nx">value</span><span class="p">;</span>
  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
    <span class="nx">content</span><span class="p">.</span><span class="nx">blocks</span> <span class="o">=</span> <span class="nx">value</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="k">return</span> <span class="nf">slackRequest</span><span class="p">(</span><span class="dl">"</span><span class="s2">chat.update</span><span class="dl">"</span><span class="p">,</span> <span class="nx">content</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Slack API 使用請參考 <a href="https://api.slack.com/methods/chat.postMessage" target="_blank">官方文件</a> 。</p>

<p><strong>Githun Action YAML 可以使用這個 Action 持續更新訊息、發送 Slack 訊息：</strong></p>

<p><a href="https://github.com/slackapi/slack-github-action" target="_blank"><img src="https://opengraph.githubassets.com/4b7679d54edd2c5fbafdaa5a79df23035f15b7a3eda256a43b710aab59a6164e/slackapi/slack-github-action" alt="" /></a></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># ...</span>
<span class="na">on</span><span class="pi">:</span>
  <span class="na">workflow_dispatch</span><span class="pi">:</span>
    <span class="na">inputs</span><span class="pi">:</span>
      <span class="na">buildNumber</span><span class="pi">:</span> <span class="c1"># 對應 GAS payload.inpus.xxx</span>
        <span class="na">description</span><span class="pi">:</span> <span class="s1">'</span><span class="s">版本號碼'</span>
        <span class="na">required</span><span class="pi">:</span> <span class="kc">false</span>
        <span class="na">type</span><span class="pi">:</span> <span class="s">string</span>
      <span class="c1"># ...More</span>
      <span class="na">SLACK_USER_ID</span><span class="pi">:</span>
        <span class="na">description</span><span class="pi">:</span> <span class="s1">'</span><span class="s">Slack</span><span class="nv"> </span><span class="s">User</span><span class="nv"> </span><span class="s">Id</span><span class="nv"> </span><span class="s">for</span><span class="nv"> </span><span class="s">receive</span><span class="nv"> </span><span class="s">action</span><span class="nv"> </span><span class="s">notification'</span>
        <span class="na">type</span><span class="pi">:</span> <span class="s">string</span>
      <span class="na">SLACK_CHANNEL_ID</span><span class="pi">:</span>
        <span class="na">description</span><span class="pi">:</span> <span class="s1">'</span><span class="s">Slack</span><span class="nv"> </span><span class="s">Channel</span><span class="nv"> </span><span class="s">Id</span><span class="nv"> </span><span class="s">for</span><span class="nv"> </span><span class="s">receive</span><span class="nv"> </span><span class="s">action</span><span class="nv"> </span><span class="s">notification'</span>
        <span class="na">type</span><span class="pi">:</span> <span class="s">string</span>
      <span class="na">SLACK_THREAD_TS</span><span class="pi">:</span>
        <span class="na">description</span><span class="pi">:</span> <span class="s1">'</span><span class="s">Slack</span><span class="nv"> </span><span class="s">message</span><span class="nv"> </span><span class="s">ts'</span>
        <span class="na">type</span><span class="pi">:</span> <span class="s">string</span>
      
<span class="na">jobs</span><span class="pi">:</span>
  <span class="c1"># some jobs...</span>

  <span class="na">if-deploy-failed-message</span><span class="pi">:</span>
    <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
    <span class="na">if</span><span class="pi">:</span> <span class="s">failure()</span>
      <span class="s">- name</span><span class="err">:</span> <span class="s">update slack message</span>
        <span class="s">uses</span><span class="err">:</span> <span class="s">slackapi/slack-github-action@v2.0.0</span>
        <span class="s">with</span><span class="err">:</span>
            <span class="na">method</span><span class="pi">:</span> <span class="s">chat.update</span>
            <span class="na">token</span><span class="pi">:</span> <span class="s">${{ secrets.SLACK_BOT_TOKEN }}</span>
            <span class="na">payload</span><span class="pi">:</span> <span class="pi">|</span>
              <span class="s">channel: ${{ github.event.inputs.SLACK_CHANNEL_ID }}</span>
              <span class="s">ts: ${{ github.event.inputs.SLACK_THREAD_TS }}</span>
              <span class="s">text: "❌ 打包任務失敗，請檢查執行狀況結果或稍後再試。\n&lt;${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|點此查看執行狀況&gt; cc'ed &lt;@${{ github.event.inputs.SLACK_USER_ID }}&gt;"</span>
</code></pre></div></div>

<p><strong>效果：</strong></p>

<p><img src="/assets/4cb4437818f2/1*-w5jqjkx6p2alpzlLcz_Nw.webp" alt="" loading="lazy" decoding="async" width="428" height="559" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0MjgiIGhlaWdodD0iNTU5Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<blockquote>
  <p><strong><em>Slack App 串接細節可參考我之前的文章： <a href="/posts/zrealm-robotic-process-automation/slack-app-整合-chatgpt-利用-openai-api-與-google-cloud-functions-自建智能助理-bd94cc88f9c9/">Slack &amp; ChatGPT Integration</a> 。</em></strong></p>
</blockquote>

<h4 id="延伸-2-查詢-jira-工單">延伸 (2) —查詢 Jira 工單</h4>

<p><code class="language-plaintext highlighter-rouge">Jira.gs：</code></p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">jiraPersonalAccessToken</span> <span class="o">=</span> <span class="dl">""</span>
<span class="c1">// https://confluence.atlassian.com/enterprise/using-personal-access-tokens-1026032365.html</span>

<span class="kd">function</span> <span class="nf">getJiraTickets</span><span class="p">()</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">url</span> <span class="o">=</span> <span class="s2">`https://xxx.atlassian.net/rest/api/3/search`</span><span class="p">;</span>

  <span class="c1">// JQL query</span>
  <span class="kd">const</span> <span class="nx">jql</span> <span class="o">=</span> <span class="s2">`project = XXX`</span><span class="p">;</span>
  <span class="kd">const</span> <span class="nx">queryParams</span> <span class="o">=</span> <span class="p">{</span>
    <span class="na">jql</span><span class="p">:</span> <span class="nx">jql</span><span class="p">,</span>
    <span class="na">maxResults</span><span class="p">:</span> <span class="mi">50</span><span class="p">,</span> <span class="c1">// Adjust as needed</span>
  <span class="p">};</span>

  <span class="kd">const</span> <span class="nx">options</span> <span class="o">=</span> <span class="p">{</span>
    <span class="na">method</span><span class="p">:</span> <span class="dl">"</span><span class="s2">get</span><span class="dl">"</span><span class="p">,</span>
    <span class="na">headers</span><span class="p">:</span> <span class="p">{</span>
      <span class="na">Authorization</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Basic </span><span class="dl">"</span> <span class="o">+</span> <span class="nx">jiraPersonalAccessToken</span><span class="p">,</span>
      <span class="dl">"</span><span class="s2">Content-Type</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">application/json</span><span class="dl">"</span><span class="p">,</span>
    <span class="p">},</span>
    <span class="na">muteHttpExceptions</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
  <span class="p">};</span>

  <span class="kd">const</span> <span class="nx">queryString</span> <span class="o">=</span> <span class="nb">Object</span><span class="p">.</span><span class="nf">keys</span><span class="p">(</span><span class="nx">queryParams</span><span class="p">).</span><span class="nf">map</span><span class="p">(</span><span class="nx">key</span> <span class="o">=&gt;</span> <span class="s2">`</span><span class="p">${</span><span class="nf">encodeURIComponent</span><span class="p">(</span><span class="nx">key</span><span class="p">)}</span><span class="s2">=</span><span class="p">${</span><span class="nf">encodeURIComponent</span><span class="p">(</span><span class="nx">queryParams</span><span class="p">[</span><span class="nx">key</span><span class="p">])}</span><span class="s2">`</span><span class="p">).</span><span class="nf">join</span><span class="p">(</span><span class="dl">"</span><span class="s2">&amp;</span><span class="dl">"</span><span class="p">);</span>
  <span class="kd">const</span> <span class="nx">response</span> <span class="o">=</span> <span class="nx">UrlFetchApp</span><span class="p">.</span><span class="nf">fetch</span><span class="p">(</span><span class="nx">url</span> <span class="o">+</span> <span class="dl">"</span><span class="s2">?</span><span class="dl">"</span> <span class="o">+</span> <span class="nx">queryString</span> <span class="o">+</span> <span class="dl">"</span><span class="s2">&amp;fields=</span><span class="dl">"</span><span class="p">,</span> <span class="nx">options</span><span class="p">);</span>
  <span class="c1">// could specify only return some fields</span>

  <span class="k">if </span><span class="p">(</span><span class="nx">response</span><span class="p">.</span><span class="nf">getResponseCode</span><span class="p">()</span> <span class="o">===</span> <span class="mi">200</span><span class="p">)</span> <span class="p">{</span>
    <span class="kd">const</span> <span class="nx">issues</span> <span class="o">=</span> <span class="nx">JSON</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="nx">response</span><span class="p">.</span><span class="nf">getContentText</span><span class="p">()).</span><span class="nx">issues</span><span class="p">;</span>
    <span class="k">return</span> <span class="nx">issues</span><span class="p">;</span>
  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
    <span class="nx">Logger</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="s2">`Error: </span><span class="p">${</span><span class="nx">response</span><span class="p">.</span><span class="nf">getResponseCode</span><span class="p">()}</span><span class="s2"> - </span><span class="p">${</span><span class="nx">response</span><span class="p">.</span><span class="nf">getContentText</span><span class="p">()}</span><span class="s2">`</span><span class="p">);</span>
    <span class="k">throw</span> <span class="k">new</span> <span class="nc">Error</span><span class="p">(</span><span class="dl">"</span><span class="s2">Failed to fetch Jira issues.</span><span class="dl">"</span><span class="p">);</span>
  <span class="p">}</span>
<span class="p">}</span> 
</code></pre></div></div>

<p>其他 Jira API 使用請參考 <a href="https://developer.atlassian.com/cloud/jira/platform/rest/v3/" target="_blank">官方文件</a> 。</p>
<h4 id="延伸-3--查詢-asana-工單">延伸 (3) — 查詢 Asana 工單</h4>

<p><code class="language-plaintext highlighter-rouge">Asana.gs：</code></p>
<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">const</span> <span class="n">asanaPersonalAccessToken</span> <span class="p">=</span> <span class="s">""</span>
<span class="c1">// https://developers.asana.com/docs/personal-access-token</span>

<span class="n">function</span> <span class="nf">asanaAPI</span><span class="p">(</span><span class="n">endpoint</span><span class="p">,</span> <span class="n">method</span> <span class="p">=</span> <span class="s">"GET"</span><span class="p">,</span> <span class="n">data</span> <span class="p">=</span> <span class="k">null</span><span class="p">)</span> <span class="p">{</span>
    <span class="kd">var</span> <span class="py">options</span> <span class="p">=</span> <span class="p">{</span>
      <span class="s">"method"</span> <span class="p">:</span> <span class="n">method</span><span class="p">,</span>
      <span class="s">"contentType"</span> <span class="p">:</span> <span class="s">"application/json"</span><span class="p">,</span>
      <span class="s">"headers"</span><span class="p">:</span> <span class="p">{</span>
          <span class="s">"Authorization"</span><span class="p">:</span>  <span class="s">"Bearer "</span><span class="p">+</span><span class="n">asanaPersonalAccessToken</span>
      <span class="p">}</span>
    <span class="p">};</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">data</span> <span class="p">!=</span> <span class="k">null</span><span class="p">)</span> <span class="p">{</span>
      <span class="n">options</span><span class="p">[</span><span class="s">"payload"</span><span class="p">]</span> <span class="p">=</span> <span class="nc">JSON</span><span class="p">.</span><span class="nf">stringify</span><span class="p">({</span><span class="s">"data"</span><span class="p">:</span><span class="n">data</span><span class="p">});</span>
    <span class="p">}</span>

    <span class="k">const</span> <span class="n">url</span> <span class="p">=</span> <span class="s">"https://app.asana.com/api/1.0"</span><span class="p">+</span><span class="n">endpoint</span><span class="p">;</span>
    <span class="k">const</span> <span class="n">res</span> <span class="p">=</span> <span class="nc">UrlFetchApp</span><span class="p">.</span><span class="nf">fetch</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">options</span><span class="p">);</span>
    <span class="k">const</span> <span class="n">data</span> <span class="p">=</span> <span class="nc">JSON</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="n">res</span><span class="p">.</span><span class="nf">getContentText</span><span class="p">());</span>
    <span class="k">return</span> <span class="n">data</span><span class="p">;</span>
<span class="p">}</span>

<span class="c1">// 查找 tasks in project</span>
<span class="c1">// asanaAPI("/projects/PROJECT_ID/tasks");</span>
</code></pre></div></div>

<p>其他 Asana API 使用請參考 <a href="https://developers.asana.com/reference/gettasksforproject" target="_blank">官方文件</a> 。</p>
<h3 id="總結">總結</h3>

<p>自動化、工作及開發流程優化缺的永遠不是技術，而是想法；只要有想法我們都能找到適合的技術來實現，共勉之！</p>
<h3 id="202507-update-1">2025/07 Update:</h3>

<p>此功能已整合到實際打包工具使用，可參考最新文章案例：「 <a href="/posts/zrealm-dev/ci-cd-打包工具平台-使用-google-apps-script-web-app-串接-github-actions-提升跨團隊效率-4273e57e7148/"><strong>CI/CD 實戰指南（四）：使用 Google Apps Script Web App 串接 GitHub Actions 建置免費易用的打包工具平台</strong></a> 」</p>]]></content>
  </entry><entry>
    <title type="html">Swift 原生類型擴展｜打造 Namespace 容器避免命名衝突，提升模組化維護性</title>
    <link href="https://zhgchg.li/posts/zrealm-dev/swift-%E5%8E%9F%E7%94%9F%E9%A1%9E%E5%9E%8B%E6%93%B4%E5%B1%95-%E6%89%93%E9%80%A0-namespace-%E5%AE%B9%E5%99%A8%E9%81%BF%E5%85%8D%E5%91%BD%E5%90%8D%E8%A1%9D%E7%AA%81-%E6%8F%90%E5%8D%87%E6%A8%A1%E7%B5%84%E5%8C%96%E7%B6%AD%E8%AD%B7%E6%80%A7-a8925ad9ed01/" rel="alternate" type="text/html" title="Swift 原生類型擴展｜打造 Namespace 容器避免命名衝突，提升模組化維護性" />
    <published>2025-01-01T22:02:32+08:00</published>
    <updated>2025-01-01T22:02:32+08:00</updated>
    <id>https://zhgchg.li/posts/zrealm-dev/a8925ad9ed01</id><summary type="html">iOS 開發中擴充原生類型常導致命名衝突與介面混亂，透過 Swift 泛型容器與協議封裝擴展方法，實現 Namespace 功能，有效區隔自訂與原生 API，提升專案模組化與維護效率。</summary><author>
      <name>ZhgChgLi</name>
    </author><category term="ZRealm Dev." /><category term="ios-app-development" /><category term="swift" /><category term="wrapper" /><category term="app-modularization" /><category term="ios" /><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://zhgchg.li/assets/a8925ad9ed01/1*kJFHiAuTZ8TP-4aTa_daqA.webp" /><content type="html" xml:base="https://zhgchg.li/posts/zrealm-dev/swift-%E5%8E%9F%E7%94%9F%E9%A1%9E%E5%9E%8B%E6%93%B4%E5%B1%95-%E6%89%93%E9%80%A0-namespace-%E5%AE%B9%E5%99%A8%E9%81%BF%E5%85%8D%E5%91%BD%E5%90%8D%E8%A1%9D%E7%AA%81-%E6%8F%90%E5%8D%87%E6%A8%A1%E7%B5%84%E5%8C%96%E7%B6%AD%E8%AD%B7%E6%80%A7-a8925ad9ed01/"><![CDATA[<h3 id="swift-一個優雅的-原生類型擴展方式">[Swift] 一個優雅的 <strong>原生類型擴展方式</strong></h3>

<p>自行封裝擴充方法，使其有 Namespace 的功能</p>

<p><img src="/assets/a8925ad9ed01/1*kJFHiAuTZ8TP-4aTa_daqA.webp" alt="https://www.swift.org/" loading="lazy" decoding="async" width="1352" height="484" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMzUyIiBoZWlnaHQ9IjQ4NCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><a href="https://www.swift.org/" target="_blank">https://www.swift.org/</a></p>

<blockquote>
  <p><em>做法實際出處不詳，是從厲害同事的 Code 上學到的。</em></p>
</blockquote>

<h3 id="原生類型擴展">原生類型擴展</h3>

<p>在日常的 iOS/Swift 開發中，我們常常需要對原生 API 進行擴充、撰寫自己的 Helper。</p>

<p><strong>以下以擴充 UIColor 為例，我們希望擴充 UIColor 使其能轉換成 HEX Color String：</strong></p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">extension</span> <span class="kt">UIColor</span> <span class="p">{</span>
    <span class="c1">/// Convert a UIColor to a hex string representation.</span>
    <span class="c1">/// - Returns: A hex string (e.g., "#RRGGBB" or "#RRGGBBAA").</span>
    <span class="kd">func</span> <span class="nf">toHexString</span><span class="p">(</span><span class="nv">includeAlpha</span><span class="p">:</span> <span class="kt">Bool</span> <span class="o">=</span> <span class="kc">false</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">String</span><span class="p">?</span> <span class="p">{</span>
        <span class="k">var</span> <span class="nv">red</span><span class="p">:</span> <span class="kt">CGFloat</span> <span class="o">=</span> <span class="mi">0</span>
        <span class="k">var</span> <span class="nv">green</span><span class="p">:</span> <span class="kt">CGFloat</span> <span class="o">=</span> <span class="mi">0</span>
        <span class="k">var</span> <span class="nv">blue</span><span class="p">:</span> <span class="kt">CGFloat</span> <span class="o">=</span> <span class="mi">0</span>
        <span class="k">var</span> <span class="nv">alpha</span><span class="p">:</span> <span class="kt">CGFloat</span> <span class="o">=</span> <span class="mi">0</span>

        <span class="k">guard</span> <span class="k">self</span><span class="o">.</span><span class="nf">getRed</span><span class="p">(</span><span class="o">&amp;</span><span class="n">red</span><span class="p">,</span> <span class="nv">green</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">green</span><span class="p">,</span> <span class="nv">blue</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">blue</span><span class="p">,</span> <span class="nv">alpha</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">alpha</span><span class="p">)</span> <span class="k">else</span> <span class="p">{</span>
            <span class="k">return</span> <span class="kc">nil</span> <span class="c1">// Color could not be represented in RGB space</span>
        <span class="p">}</span>

        <span class="k">if</span> <span class="n">includeAlpha</span> <span class="p">{</span>
            <span class="k">return</span> <span class="kt">String</span><span class="p">(</span><span class="nv">format</span><span class="p">:</span> <span class="s">"#%02X%02X%02X%02X"</span><span class="p">,</span>
                          <span class="kt">Int</span><span class="p">(</span><span class="n">red</span> <span class="o">*</span> <span class="mi">255</span><span class="p">),</span>
                          <span class="kt">Int</span><span class="p">(</span><span class="n">green</span> <span class="o">*</span> <span class="mi">255</span><span class="p">),</span>
                          <span class="kt">Int</span><span class="p">(</span><span class="n">blue</span> <span class="o">*</span> <span class="mi">255</span><span class="p">),</span>
                          <span class="kt">Int</span><span class="p">(</span><span class="n">alpha</span> <span class="o">*</span> <span class="mi">255</span><span class="p">))</span>
        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
            <span class="k">return</span> <span class="kt">String</span><span class="p">(</span><span class="nv">format</span><span class="p">:</span> <span class="s">"#%02X%02X%02X"</span><span class="p">,</span>
                          <span class="kt">Int</span><span class="p">(</span><span class="n">red</span> <span class="o">*</span> <span class="mi">255</span><span class="p">),</span>
                          <span class="kt">Int</span><span class="p">(</span><span class="n">green</span> <span class="o">*</span> <span class="mi">255</span><span class="p">),</span>
                          <span class="kt">Int</span><span class="p">(</span><span class="n">blue</span> <span class="o">*</span> <span class="mi">255</span><span class="p">))</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>直接對 UIColor 進行擴充 (Extension) 後的存取方式如下：</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">color</span> <span class="o">=</span> <span class="kt">UIColor</span><span class="o">.</span><span class="n">blue</span>
<span class="n">color</span><span class="o">.</span><span class="nf">toHexString</span><span class="p">()</span> <span class="c1">// #0000ff</span>
</code></pre></div></div>
<h4 id="問題">問題</h4>

<p>當我們自定義的擴充方式越來越多會讓存取介面開始變得混亂，例如：</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">color</span> <span class="o">=</span> <span class="kt">UIColor</span><span class="o">.</span><span class="n">blue</span>
<span class="n">color</span><span class="o">.</span><span class="nf">getRed</span><span class="p">(</span><span class="o">...</span><span class="p">)</span>
<span class="n">color</span><span class="o">.</span><span class="nf">getWhite</span><span class="p">(</span><span class="o">...</span><span class="p">)</span>
<span class="n">color</span><span class="o">.</span><span class="nf">getHue</span><span class="p">(</span><span class="o">...</span><span class="p">)</span>
<span class="n">color</span><span class="o">.</span><span class="nf">getCMYK</span><span class="p">()</span> <span class="c1">// 自行擴充的方法</span>
<span class="n">color</span><span class="o">.</span><span class="nf">toHexString</span><span class="p">()</span> <span class="c1">// 自行擴充的方法</span>
<span class="n">color</span><span class="o">.</span><span class="nf">withAlphaComponent</span><span class="p">(</span><span class="o">...</span><span class="p">)</span>
<span class="n">color</span><span class="o">.</span><span class="nf">setFill</span><span class="p">(</span><span class="o">...</span><span class="p">)</span>
<span class="n">color</span><span class="o">.</span><span class="nf">setToBlue</span><span class="p">()</span> <span class="c1">// 自行擴充的方法</span>

<span class="c1">// A Module</span>
<span class="kd">public</span> <span class="kd">extension</span> <span class="kt">UIColor</span> <span class="p">{</span>
  <span class="kd">func</span> <span class="nf">getCMYK</span><span class="p">()</span> <span class="p">{</span>
    <span class="c1">// ...</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="c1">// B Module</span>
<span class="c1">// Invalid redeclaration of 'getCMYK()'</span>
<span class="kd">public</span> <span class="kd">extension</span> <span class="kt">UIColor</span> <span class="p">{</span>
  <span class="kd">func</span> <span class="nf">getCMYK</span><span class="p">()</span> <span class="p">{</span>
    <span class="c1">// ...</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>我們自行擴充的方法跟原生提供的方法全部混合在一起，難以區分；另外專案規模越大、引用的套件越多也有可能遇到 Extension 命名衝突，例如兩個套件都對 UIColor Extension 也都叫 <code class="language-plaintext highlighter-rouge">getCMYK()</code> 就會有問題。</p>
<h3 id="自訂擴展-namespace-容器">自訂擴展 Namespace 容器</h3>

<p>我們可以運用 Swift Protocol, Computed Property 與 泛型的特型來自行封裝擴充方法，使其有 Namespace 的功能。</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// 宣告一個泛型容器 ExtensionContainer&lt;Base&gt;：</span>
<span class="kd">public</span> <span class="kd">struct</span> <span class="kt">ExtensionContainer</span><span class="o">&lt;</span><span class="kt">Base</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="kd">public</span> <span class="k">let</span> <span class="nv">base</span><span class="p">:</span> <span class="kt">Base</span>
    <span class="kd">public</span> <span class="nf">init</span><span class="p">(</span><span class="n">_</span> <span class="nv">base</span><span class="p">:</span> <span class="kt">Base</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">self</span><span class="o">.</span><span class="n">base</span> <span class="o">=</span> <span class="n">base</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="c1">// 定義 for AnyObject, Class(Reference) Type 實現的接口：</span>
<span class="c1">// 例如 Foundation 中的 NSXXX 類別</span>
<span class="kd">public</span> <span class="kd">protocol</span> <span class="kt">ExtensionCompatibleObject</span><span class="p">:</span> <span class="kt">AnyObject</span> <span class="p">{}</span>
<span class="c1">// 定義 for Struct(Value) Type 實現的接口：</span>
<span class="kd">public</span> <span class="kd">protocol</span> <span class="kt">ExtensionCompatible</span> <span class="p">{}</span>

<span class="c1">// 自訂義 Namespace Computed Property：</span>
<span class="kd">public</span> <span class="kd">extension</span> <span class="kt">ExtensionCompatibleObject</span> <span class="p">{</span>
    <span class="k">var</span> <span class="nv">zhg</span><span class="p">:</span> <span class="kt">ExtensionContainer</span><span class="o">&lt;</span><span class="k">Self</span><span class="o">&gt;</span> <span class="p">{</span>
        <span class="k">return</span> <span class="kt">ExtensionContainer</span><span class="p">(</span><span class="k">self</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="kd">public</span> <span class="kd">extension</span> <span class="kt">ExtensionCompatible</span> <span class="p">{</span>
    <span class="k">var</span> <span class="nv">zhg</span><span class="p">:</span> <span class="kt">ExtensionContainer</span><span class="o">&lt;</span><span class="k">Self</span><span class="o">&gt;</span> <span class="p">{</span>
        <span class="k">return</span> <span class="kt">ExtensionContainer</span><span class="p">(</span><span class="k">self</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<h4 id="擴展原生類型">擴展原生類型</h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">extension</span> <span class="kt">UIColor</span><span class="p">:</span> <span class="kt">ExtensionCompatibleObject</span> <span class="p">{}</span>

<span class="kd">extension</span> <span class="kt">ExtensionContainer</span> <span class="k">where</span> <span class="kt">Base</span><span class="p">:</span> <span class="kt">UIColor</span> <span class="p">{</span>
    <span class="c1">/// Convert a UIColor to a hex string representation.</span>
    <span class="c1">/// - Returns: A hex string (e.g., "#RRGGBB" or "#RRGGBBAA").</span>
    <span class="kd">func</span> <span class="nf">toHexString</span><span class="p">(</span><span class="nv">includeAlpha</span><span class="p">:</span> <span class="kt">Bool</span> <span class="o">=</span> <span class="kc">false</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">String</span><span class="p">?</span> <span class="p">{</span>
        <span class="k">var</span> <span class="nv">red</span><span class="p">:</span> <span class="kt">CGFloat</span> <span class="o">=</span> <span class="mi">0</span>
        <span class="k">var</span> <span class="nv">green</span><span class="p">:</span> <span class="kt">CGFloat</span> <span class="o">=</span> <span class="mi">0</span>
        <span class="k">var</span> <span class="nv">blue</span><span class="p">:</span> <span class="kt">CGFloat</span> <span class="o">=</span> <span class="mi">0</span>
        <span class="k">var</span> <span class="nv">alpha</span><span class="p">:</span> <span class="kt">CGFloat</span> <span class="o">=</span> <span class="mi">0</span>

        <span class="k">guard</span> <span class="k">self</span><span class="o">.</span><span class="n">base</span><span class="o">.</span><span class="nf">getRed</span><span class="p">(</span><span class="o">&amp;</span><span class="n">red</span><span class="p">,</span> <span class="nv">green</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">green</span><span class="p">,</span> <span class="nv">blue</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">blue</span><span class="p">,</span> <span class="nv">alpha</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">alpha</span><span class="p">)</span> <span class="k">else</span> <span class="p">{</span>
            <span class="k">return</span> <span class="kc">nil</span> <span class="c1">// Color could not be represented in RGB space</span>
        <span class="p">}</span>

        <span class="k">if</span> <span class="n">includeAlpha</span> <span class="p">{</span>
            <span class="k">return</span> <span class="kt">String</span><span class="p">(</span><span class="nv">format</span><span class="p">:</span> <span class="s">"#%02X%02X%02X%02X"</span><span class="p">,</span>
                          <span class="kt">Int</span><span class="p">(</span><span class="n">red</span> <span class="o">*</span> <span class="mi">255</span><span class="p">),</span>
                          <span class="kt">Int</span><span class="p">(</span><span class="n">green</span> <span class="o">*</span> <span class="mi">255</span><span class="p">),</span>
                          <span class="kt">Int</span><span class="p">(</span><span class="n">blue</span> <span class="o">*</span> <span class="mi">255</span><span class="p">),</span>
                          <span class="kt">Int</span><span class="p">(</span><span class="n">alpha</span> <span class="o">*</span> <span class="mi">255</span><span class="p">))</span>
        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
            <span class="k">return</span> <span class="kt">String</span><span class="p">(</span><span class="nv">format</span><span class="p">:</span> <span class="s">"#%02X%02X%02X"</span><span class="p">,</span>
                          <span class="kt">Int</span><span class="p">(</span><span class="n">red</span> <span class="o">*</span> <span class="mi">255</span><span class="p">),</span>
                          <span class="kt">Int</span><span class="p">(</span><span class="n">green</span> <span class="o">*</span> <span class="mi">255</span><span class="p">),</span>
                          <span class="kt">Int</span><span class="p">(</span><span class="n">blue</span> <span class="o">*</span> <span class="mi">255</span><span class="p">))</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<h4 id="使用">使用</h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">color</span> <span class="o">=</span> <span class="kt">UIColor</span><span class="o">.</span><span class="n">blue</span>
<span class="n">color</span><span class="o">.</span><span class="n">zhg</span><span class="o">.</span><span class="nf">toHexString</span><span class="p">()</span> <span class="c1">// #0000ff</span>
</code></pre></div></div>
<h4 id="範例-2-url-queryitems-擴充">範例 2. URL .queryItems 擴充</h4>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>extension URL: ExtensionCompatible {}

extension ExtensionContainer where Base == URL {
    
    var queryParameters: [String: String]? {
        URLComponents(url: base, resolvingAgainstBaseURL: true)?
            .queryItems?
            .reduce(into: [String: String]()) { $0[$1.name] = $1.value }
    }
}
let url = URL(string: "https://zhgchg.li?a=b&amp;c=d")!
url.zhg.queryParameters // ["c": "d", "a": "b"]
</code></pre></div></div>
<h3 id="結合-builder-pattern">結合 <a href="https://refactoring.guru/design-patterns/builder" target="_blank">Builder Pattern</a></h3>

<p>另外我們也可以將此封裝方式結合 Builder Pattern 操作：</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">final</span> <span class="kd">class</span> <span class="kt">URLBuilder</span> <span class="p">{</span>
    <span class="kd">private</span> <span class="k">var</span> <span class="nv">components</span><span class="p">:</span> <span class="kt">URLComponents</span>

    <span class="nf">init</span><span class="p">(</span><span class="nv">base</span><span class="p">:</span> <span class="kt">URL</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">self</span><span class="o">.</span><span class="n">components</span> <span class="o">=</span> <span class="kt">URLComponents</span><span class="p">(</span><span class="nv">url</span><span class="p">:</span> <span class="n">base</span><span class="p">,</span> <span class="nv">resolvingAgainstBaseURL</span><span class="p">:</span> <span class="kc">true</span><span class="p">)</span><span class="o">!</span>
    <span class="p">}</span>

    <span class="kd">func</span> <span class="nf">setQueryParameters</span><span class="p">(</span><span class="n">_</span> <span class="nv">parameters</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">:</span> <span class="kt">String</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="kt">URLBuilder</span> <span class="p">{</span>
        <span class="n">components</span><span class="o">.</span><span class="n">queryItems</span> <span class="o">=</span> <span class="n">parameters</span><span class="o">.</span><span class="n">map</span> <span class="p">{</span> <span class="o">.</span><span class="nf">init</span><span class="p">(</span><span class="nv">name</span><span class="p">:</span> <span class="nv">$0</span><span class="o">.</span><span class="n">key</span><span class="p">,</span> <span class="nv">value</span><span class="p">:</span> <span class="nv">$0</span><span class="o">.</span><span class="n">value</span><span class="p">)</span> <span class="p">}</span>
        <span class="k">return</span> <span class="k">self</span>
    <span class="p">}</span>

    <span class="kd">func</span> <span class="nf">setScheme</span><span class="p">(</span><span class="n">_</span> <span class="nv">scheme</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">URLBuilder</span> <span class="p">{</span>
        <span class="n">components</span><span class="o">.</span><span class="n">scheme</span> <span class="o">=</span> <span class="n">scheme</span>
        <span class="k">return</span> <span class="k">self</span>
    <span class="p">}</span>

    <span class="kd">func</span> <span class="nf">build</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="kt">URL</span><span class="p">?</span> <span class="p">{</span>
        <span class="k">return</span> <span class="n">components</span><span class="o">.</span><span class="n">url</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="kd">extension</span> <span class="kt">URL</span><span class="p">:</span> <span class="kt">ExtensionCompatible</span> <span class="p">{}</span>

<span class="kd">extension</span> <span class="kt">ExtensionContainer</span> <span class="k">where</span> <span class="kt">Base</span> <span class="o">==</span> <span class="kt">URL</span> <span class="p">{</span>
    <span class="kd">func</span> <span class="nf">builder</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="kt">URLBuilder</span> <span class="p">{</span>
        <span class="k">return</span> <span class="kt">URLBuilder</span><span class="p">(</span><span class="nv">base</span><span class="p">:</span> <span class="n">base</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">let</span> <span class="nv">url</span> <span class="o">=</span> <span class="kt">URL</span><span class="p">(</span><span class="nv">string</span><span class="p">:</span> <span class="s">"https://zhgchg.li"</span><span class="p">)</span><span class="o">!.</span><span class="n">zhg</span><span class="o">.</span><span class="nf">builder</span><span class="p">()</span><span class="o">.</span><span class="nf">setQueryParameters</span><span class="p">([</span><span class="s">"a"</span><span class="p">:</span> <span class="s">"b"</span><span class="p">,</span> <span class="s">"c"</span><span class="p">:</span> <span class="s">"d"</span><span class="p">])</span><span class="o">.</span><span class="nf">setScheme</span><span class="p">(</span><span class="s">"ssh"</span><span class="p">)</span><span class="o">.</span><span class="nf">build</span><span class="p">()</span>
<span class="c1">// ssh://zhgchg.li?a=b&amp;c=d</span>
</code></pre></div></div>]]></content>
  </entry><entry>
    <title type="html">山陰關西自由行攻略｜島根出雲松江鳥取姬路大阪神戶7日獨旅全記錄</title>
    <link href="https://zhgchg.li/posts/z-%E5%BA%A6%E6%97%85%E8%A1%8C%E9%81%8A%E8%A8%98/%E5%B1%B1%E9%99%B0%E9%97%9C%E8%A5%BF%E8%87%AA%E7%94%B1%E8%A1%8C%E6%94%BB%E7%95%A5-%E5%B3%B6%E6%A0%B9%E5%87%BA%E9%9B%B2%E6%9D%BE%E6%B1%9F%E9%B3%A5%E5%8F%96%E5%A7%AC%E8%B7%AF%E5%A4%A7%E9%98%AA%E7%A5%9E%E6%88%B67%E6%97%A5%E7%8D%A8%E6%97%85%E5%85%A8%E8%A8%98%E9%8C%84-aacd5f5cacd1/" rel="alternate" type="text/html" title="山陰關西自由行攻略｜島根出雲松江鳥取姬路大阪神戶7日獨旅全記錄" />
    <published>2024-12-03T00:04:56+08:00</published>
    <updated>2025-08-13T13:38:47+08:00</updated>
    <id>https://zhgchg.li/posts/z-%E5%BA%A6%E6%97%85%E8%A1%8C%E9%81%8A%E8%A8%98/aacd5f5cacd1</id><summary type="html">規劃7天山陰關西獨旅，涵蓋島根出雲松江、鳥取沙丘、姬路城及大阪神戶，詳細交通、JR Pass使用與住宿分享，解決行程緊湊與交通難題，助你輕鬆踩點千公里經典景點。</summary><author>
      <name>ZhgChgLi</name>
    </author><category term="Z 度旅行遊記" /><category term="生活" /><category term="japan" /><category term="travel-writing" /><category term="島根縣" /><category term="旅遊" /><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://zhgchg.li/assets/aacd5f5cacd1/1*ucEUq8avIv1nBoNnCMlgRw.webp" /><content type="html" xml:base="https://zhgchg.li/posts/z-%E5%BA%A6%E6%97%85%E8%A1%8C%E9%81%8A%E8%A8%98/%E5%B1%B1%E9%99%B0%E9%97%9C%E8%A5%BF%E8%87%AA%E7%94%B1%E8%A1%8C%E6%94%BB%E7%95%A5-%E5%B3%B6%E6%A0%B9%E5%87%BA%E9%9B%B2%E6%9D%BE%E6%B1%9F%E9%B3%A5%E5%8F%96%E5%A7%AC%E8%B7%AF%E5%A4%A7%E9%98%AA%E7%A5%9E%E6%88%B67%E6%97%A5%E7%8D%A8%E6%97%85%E5%85%A8%E8%A8%98%E9%8C%84-aacd5f5cacd1/"><![CDATA[<h3 id="遊記-2024-山陰廣域地區-島根-出雲-松江-鳥取-姬路-大阪-神戶-7-日獨旅自由行">[遊記] 2024 山陰廣域地區 島根 出雲 松江 鳥取 姬路 大阪 神戶 7 日獨旅自由行</h3>

<p>岡山進出，先去山陰島根縣出雲松江再去鳥取，最後回到姬路、關西、大阪；7 天 10 萬步、交通橫跨 1,000 公里，一次踩點山陰關西地區。</p>

<p><img src="/assets/aacd5f5cacd1/1*ucEUq8avIv1nBoNnCMlgRw.webp" alt="宍道湖夕陽" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>宍道湖夕陽</p>
<h4 id="前言">前言</h4>

<p>最一開始 2024 下半年是想去仙台的，不過查了一下景點資料，這個時間點去，我的興趣普普；回想到去年同一週 11/13–18 去了 <strong>「 <a href="/posts/z-度旅行遊記/山陽地區廣島岡山自由行攻略-6日行程-交通-住宿全解析-31b9b3a63abc/">山陽地區 廣島岡山 6 日自由行</a> 」</strong> 對中國地方、山陽、岡山的印象很好 — 景色很美、交通方便、觀光客較少；因此想說這次把山陰地區(島根、鳥取)也走一遍，剛好工作也有空檔跟近期心煩意亂急需放空；促成了這次 7 天 6 夜山陰地區(加上關西地區：姬路、大阪)獨旅自由行。</p>
<h3 id="準備工作">準備工作</h3>
<h4 id="日期111218">日期：11/12–18</h4>

<p>這次也是 P 人發作，10 月中臨時抓到時間空檔，隨即安排 11/12 出發，11/18 回程。</p>
<h4 id="️機票">✈️機票</h4>

<p><img src="/assets/aacd5f5cacd1/1*XRfz2GcB_EiuvwxLYIFCTw.webp" alt="" loading="lazy" decoding="async" width="1189" height="580" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMTg5IiBoZWlnaHQ9IjU4MCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>今年沒有特別搶虎航秋季活動機票， <strong>去年有買到優惠機票</strong> 時間比較好又比較便宜( <code class="language-plaintext highlighter-rouge">去年：去(11:10)、回(15:25)，含來回 20KG 託運＋選位＋雜費：NT$ 7,012</code> )。</p>
<ul>
  <li>去：13:05 TPE 台北桃園國際機場-&gt; <strong>16:30 OKJ</strong> 岡山桃太郎機場</li>
  <li>回： <strong>17:30 OKJ</strong> 岡山桃太郎機場 -&gt; 19:35 TPE 台北桃園國際機場</li>
  <li>含來回 20KG 托運＋選位＋雜費價格： <code class="language-plaintext highlighter-rouge">NT$ 9,118</code></li>
</ul>

<p>沒特別便宜，但還可以接受，如果選同去年的去回時間會更貴，大概要 <code class="language-plaintext highlighter-rouge">NT$ 12,000</code> 。</p>
<h4 id="虎航-2025-新直飛航線">虎航 2025 新直飛航線</h4>

<p><a href="https://chugoku.letsgojp.com/archives/731900" target="_blank">台灣虎航於2025年5月29(四)，起新增「桃園國際機場－鳥取米子機場」直飛航班，去山陰地區就更方便了！</a></p>
<h4 id="️計畫">🏝️計畫</h4>

<p>沒去過山陰地區跟姬路，以這三個地方為主。</p>
<ul>
  <li>11/12 晚上抵達岡山，住一晚為明早去島根做準備</li>
  <li>11/13 一早搭 JR 前往島根，先去出雲大社、稻佐海濱，下午傍晚去宍道湖看夕陽</li>
  <li>11/14 早上松江城、松江堀川遊船，下午足立美術館，晚上抵達鳥取</li>
  <li>11/15 早上白兔神社、下午鳥取沙丘，晚上抵達姬路</li>
  <li>11/16 姬路城、姬路市區逛街
實際多去：書寫山圓教寺</li>
  <li>11/17 大鳴門橋遊歩道 渦之道、鳴門旋渦、明石海峽大橋
實際因天氣不佳沒去，改去大阪、神戶逛街吃東西。</li>
  <li>11/18 岡山站逛街、回程</li>
</ul>

<h4 id="行--日本-jr-pass關西山陰地區鐵路周遊券emco-電子票">🚅行- <a href="https://www.kkday.com/zh-tw/product/20307-jr-sanin-okayama-area-pass?cid=19365" target="_blank"><strong>日本 JR PASS｜關西&amp;山陰地區鐵路周遊券｜eMCO 電子票</strong></a></h4>

<p>這次也是長距離移動，必買 JR Pass，本來還怕只有山陰、山陽 JRass，沒想到日本 JR 很貼心的把有可能的路線都規劃好了，有「 <a href="https://www.kkday.com/zh-tw/product/20307-jr-sanin-okayama-area-pass?cid=19365" target="_blank"><strong>關西&amp;山陰地區鐵路周遊券 JRPass</strong></a> 」正合我意！</p>

<p><img src="/assets/aacd5f5cacd1/1*H0ztJ3sX7oPQmJz7L2j87w.webp" alt="關西&amp;山陰地區鐵路周遊券 JRPass 可用範圍" loading="lazy" decoding="async" width="1330" height="1080" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMzMwIiBoZWlnaHQ9IjEwODAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><a href="https://www.kkday.com/zh-tw/product/20307-jr-sanin-okayama-area-pass?cid=19365" target="_blank">關西&amp;山陰地區鐵路周遊券 JRPass 可用範圍</a></p>
<ul>
  <li>價格： <code class="language-plaintext highlighter-rouge">NT$ 3,779</code> 多送一張 <a href="https://www.kkday.com/zh-tw/product/20307-jr-sanin-okayama-area-pass?cid=19365" target="_blank"><strong>山陰山陽地區PASS周遊3日券 (搭配用起來很划算)</strong></a></li>
</ul>

<h4 id="網路">📱網路</h4>

<p>這次也一併從 KKday 買「 <a href="https://www.kkday.com/zh-tw/product/137689-japan-high-speed-daily-unlimited-data-japanese-esim?cid=19365" target="_blank"><strong>日本eSIM卡｜每日高速、總量、無限流量吃到飽方案｜優惠65折</strong></a> 」</p>
<ul>
  <li>7 日 無限流量 eSIM 方案</li>
  <li>價格： <code class="language-plaintext highlighter-rouge">NT$ 847</code></li>
</ul>

<p>實際用下來很好，很穩定、沒遇到被降速或斷線問題。</p>

<blockquote>
  <p><em>eSIM 啟用方式、注意事項本文中會附上，請繼續閱讀。</em></p>
</blockquote>

<h4 id="日幣">日幣</h4>

<p><img src="/assets/aacd5f5cacd1/1*ljW0bI5fjkI2Xasi4KvA5A.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>換了日幣新鈔。</p>

<blockquote>
  <p><em>建議新舊鈔都帶一些，部分販賣機無法吃新鈔。</em></p>
</blockquote>

<p><img src="/assets/aacd5f5cacd1/1*_Zz6O5dhksVtJvi8TqN1yw.webp" alt="可以吃新鈔的販賣機" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>可以吃新鈔的販賣機</p>
<h4 id="住宿">🏠住宿</h4>

<p>這次很幸運地只有松江一晚沒訂到 <a href="https://www.toyoko-inn.com/china/index2" target="_blank">東橫 Inn</a> ，其他都有訂到。</p>

<p><strong>11/12 <a href="https://www.toyoko-inn.com/china/search/detail/00232/" target="_blank">東橫INN 岡山站東口</a> (一晚)</strong></p>

<p><img src="/assets/aacd5f5cacd1/1*Kidzn6hm372657rCN8jTgw.webp" alt="出岡山車站走 4 分鐘就會到" loading="lazy" decoding="async" width="620" height="289" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2MjAiIGhlaWdodD0iMjg5Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>出岡山車站走 4 分鐘就會到</p>
<ul>
  <li>禁煙雙床房，入住：1 人</li>
  <li>價格： <code class="language-plaintext highlighter-rouge">NT$ 1,616</code></li>
</ul>

<p><strong>11/13 <a href="https://www.station.matsue-urban.co.jp/en/" target="_blank">Matsue Urban Hotel</a> (一晚)</strong></p>

<p><img src="/assets/aacd5f5cacd1/1*9QIJ7lY1W7L94dgKGm6VQQ.webp" alt="松江站出站走 2 分鐘就會到" loading="lazy" decoding="async" width="413" height="287" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0MTMiIGhlaWdodD0iMjg3Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>松江站出站走 2 分鐘就會到</p>
<ul>
  <li>Double Room with Small Double Bed — Non-Smoking — Building 2
入住：1 人</li>
  <li>價格： <code class="language-plaintext highlighter-rouge">NT$ 2,431</code></li>
</ul>

<p><strong>11/14 <a href="https://www.toyoko-inn.com/china/search/detail/00046/" target="_blank">東橫INN 鳥取站南口</a> (一晚)</strong></p>

<p><img src="/assets/aacd5f5cacd1/1*hIqnM7MaRDWsRoRsydTqCg.webp" alt="鳥取站出站走 4 分鐘就會到" loading="lazy" decoding="async" width="587" height="373" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1ODciIGhlaWdodD0iMzczIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>鳥取站出站走 4 分鐘就會到</p>
<ul>
  <li>禁煙雙人房，入住：1 人</li>
  <li>價格： <code class="language-plaintext highlighter-rouge">NT$ 1,595</code></li>
</ul>

<p><strong>11/15 <a href="https://www.toyoko-inn.com/china/search/detail/00317/" target="_blank">東橫INN 姬路站新幹線北口</a> (三晚)</strong></p>

<p><img src="/assets/aacd5f5cacd1/1*P4xut5p60ZVSDtPkpoO6Pw.webp" alt="姬路站出站走 5 分鐘會到" loading="lazy" decoding="async" width="777" height="591" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NzciIGhlaWdodD0iNTkxIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>姬路站出站走 5 分鐘會到</p>
<ul>
  <li>禁煙雙人房，入住：1 人</li>
  <li>價格： <code class="language-plaintext highlighter-rouge">NT$ 6,055</code> ， <code class="language-plaintext highlighter-rouge">NT$ 2,018</code> /晚</li>
</ul>

<p>雖然幾乎都有訂到東橫，但還是因為預定時間太晚只剩雙人房，所以一晚的價格比較高，如果有訂到單人房型應該可以壓到一晚 <code class="language-plaintext highlighter-rouge">NT$ 1,500</code> 以內；不過以往經驗，日期接近就算是雙人房也都會全滿，可能地點真的比較少人去？</p>

<blockquote>
  <p><em>房間開箱影片本文中會附上，請繼續閱讀。</em></p>
</blockquote>

<p>—</p>

<blockquote>
  <p><a href="/posts/z-度旅行遊記/京阪神自由行攻略-京都大阪神戶8日遊全紀錄與實用交通住宿指南-76d66c2e34af/"><em>Flight Tracker、iPhone Suica 使用、Visit Japan 預入境申請…之前文章有提過，這篇就不多贅述了。</em></a></p>
</blockquote>

<blockquote>
  <p><em><a href="http://vjw.digital.go.jp/" target="_blank">Visit Japan</a> <strong>唯一跟之前不一樣的只有「入境審查」與「海關申報」QR Code 已合併成同一個「入境审查及海关申报的QR码」，沒有藍碼黃碼區別。</strong></em></p>
</blockquote>

<h4 id="虎航線上自助報到">虎航線上自助報到</h4>

<p>約出發前 1~3 天會收到虎航寄發的行程通知信， <strong>務必！務必！務必！</strong> 在出發前直接完成線上「自助報到」：</p>

<p><img src="/assets/aacd5f5cacd1/1*3K4biik6RnLXrqp453siiA.webp" alt="" loading="lazy" decoding="async" width="1120" height="1153" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMTIwIiBoZWlnaHQ9IjExNTMiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>點擊自助報到後會前往虎航官網，確認旅客資料、行李安全，最後會產生、寄發「登機證」。</p>

<p><img src="/assets/aacd5f5cacd1/1*oupKW1tFwXdEwj4pEAL8hw.webp" alt="" loading="lazy" decoding="async" width="511" height="846" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1MTEiIGhlaWdodD0iODQ2Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>保存好登機證，憑此去機場就可以直接排「行李自助托運」掛行李，不用浪費時間排長長的「一般報到」櫃檯。</p>
<h4 id="さあ行こう-出發">さあ行こう 出發！</h4>
<h4 id="kkday-推廣-">KKday 推廣 🛒</h4>
<ul>
  <li><a href="https://www.kkday.com/zh-tw/product/153331?cid=19365" target="_blank">日本 JR PASS|鳥取・松江區域周遊券|eMCO 電子票</a> 
(如果只去松江、鳥取松江地區可考慮)</li>
  <li><a href="https://www.kkday.com/zh-tw/product/20307-jr-sanin-okayama-area-pass?cid=19365" target="_blank"><strong>關西&amp;山陰地區鐵路周遊券 JRPass</strong></a> 
(岡山機場進出最佳選擇)</li>
  <li><a href="https://www.kkday.com/zh-tw/product/137689-japan-high-speed-daily-unlimited-data-japanese-esim?cid=19365" target="_blank"><strong>日本eSIM卡｜每日高速、總量、無限流量吃到飽方案</strong></a></li>
  <li><a href="https://www.kkday.com/zh-tw/product/20315-jr-sanyo-sanin-area-pass?cid=19365" target="_blank">日本 JR PASS｜山陽＆山陰地區鐵路周遊券｜eMCO 電子票</a> 
(一口氣玩遍山陽山陰地區)</li>
  <li><a href="https://www.kkday.com/zh-tw/product/11463-osaka-bus-tour-tottori-sand-dunes-uratomi-coast-cruise-sand-museum-japan?cid=19365" target="_blank">【鳥取巴士一日遊】鳥取砂丘、浦富海岸巡航、砂之美術館、採梨子體驗｜含特色午餐（大阪出發）</a></li>
  <li><a href="https://www.kkday.com/zh-tw/product/20314-japan-jr-sanyo-sanin-northern-kyushu-pass-delivery-to-multiple-countries?cid=19365" target="_blank">日本 JR PASS|山陽&amp;山陰&amp;北九州地區鐵路周遊券|多國郵寄</a></li>
</ul>

<h3 id="day-1-1112-週二-出發">Day 1 (11/12 週二) 出發</h3>

<p>下午 13:05 的飛機，早上 8 點起床快 9 點出門，時間上綽綽有餘。</p>

<p><img src="/assets/aacd5f5cacd1/1*LyhBB17WajIUYryRUrIgaA.webp" alt="2024/11 擷取自預辦登機官網" loading="lazy" decoding="async" width="546" height="338" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1NDYiIGhlaWdodD0iMzM4Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><a href="https://www.taoyuan-airport.com/ITCI/index.html" target="_blank">2024/11 擷取自預辦登機官網</a></p>

<p>虎航 <a href="https://www.taoyuan-airport.com/ITCI/index.html" target="_blank"><strong>預辦登機服務(在機捷就報到+掛行李，到機場直接出境)</strong></a> 只能在 <strong>機捷A3新北產業園區站</strong> ；不能在北車，所以就一路搭機捷直達車前往第一航廈了。</p>
<h4 id="-1000-抵達機場">~= 10:00 抵達機場</h4>

<p><img src="/assets/aacd5f5cacd1/1*4_ZTToEL1W6fwNWpo_GvBw.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>大約 10:00 就抵達機場，要等 10:20 才開櫃。</p>
<h4 id="-1020-排行李自助托運掛行李">~= 10:20 排「行李自助托運」掛行李</h4>

<p><img src="/assets/aacd5f5cacd1/1*4y0Kgdbk8bbcUgajmi27IQ.webp" alt="" loading="lazy" decoding="async" width="951" height="1238" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NTEiIGhlaWdodD0iMTIzOCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*5_TKj5PZ-iUMi3r3CqrmzA.webp" alt="去程：9 KG" loading="lazy" decoding="async" width="611" height="776" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2MTEiIGhlaWdodD0iNzc2Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>去程：9 KG</p>

<p>這時候是先從虎航網站使用網路報到的優勢就來了，當天目測現場「一般報到」櫃檯大排長龍，10:20 就已經排滿，預估至少要等 45 分鐘到 1 小時；「行李自助托運」這邊大概花不到 15 分鐘就完成手續，節省很多等待時間。</p>

<blockquote>
  <p><em>現場完成線上報到，再去排「行李自助托運」都比去排「一般報到」快得多。</em></p>
</blockquote>

<h4 id="-1050-完成出境">~= 10:50 完成出境</h4>

<p><img src="/assets/aacd5f5cacd1/1*aD3atJOMZWAhStzpdoaZDg.webp" alt="" loading="lazy" decoding="async" width="937" height="1167" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MzciIGhlaWdodD0iMTE2NyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*OLORgiz8R6pOfG-m9P-aOw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>這次在第一航廈的 A1 登機門。(終於不用再搭擺渡車了)</p>

<p><img src="/assets/aacd5f5cacd1/1*MDpHGgVgU0QRH0BGFmzLbA.webp" alt="" loading="lazy" decoding="async" width="951" height="1236" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NTEiIGhlaWdodD0iMTIzNiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*ZU4N6uWzEYhp_J2C3JE0Dg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1034" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwMzQiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>之前來第一航廈每次都吃頂呱呱，這次改吃老董牛肉麵，店面位置就在快到 A1 登機門的地方也很順路。</p>
<h4 id="-1130-候機">~= 11:30 候機</h4>

<p><img src="/assets/aacd5f5cacd1/1*Xaux1WjOTpXcqxeF1-UlXw.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>吃飽亂晃一下大約 11:30，A1 登機門的壞處是離 <a href="/posts/z-度旅行遊記/九州自由行攻略-釜山新山茶花號郵輪入境日本博多-深入由布院-大分-福岡等地-cb65fd5ab770/"><strong>一航免費貴賓室</strong></a> 很遠，懶得走來走去就直接來候機室了。</p>

<p>沒事先處理一下兩個網路問題，第一個是我的 <a href="https://123.cht.com.tw/IVRredir/" target="_blank"><strong>中華電信漫遊功能是關閉的</strong></a> ，有需要漫遊方案無法直接購買；所以先打電話去中華電信解鎖漫遊功能，以備不時之需。</p>

<p><img src="/assets/aacd5f5cacd1/1*dF9XuqFK3bW2X249DYtqZg.webp" alt="需要打電話給中華電信、確認個人資料後請客服解除鎖定。" loading="lazy" decoding="async" width="574" height="1206" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1NzQiIGhlaWdodD0iMTIwNiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>需要打電話給中華電信、確認個人資料後請客服解除鎖定。</p>
<h4 id="事先啟用-esim-iphone-為例">事先啟用 eSIM (iPhone 為例)</h4>

<p>第二個網路問題是先在機場啟用 eSIM，因 eSIM 輸入啟用需要連接網路，怕岡山機場的 WiFi 不穩定，所以事先在台灣網路正常的情況下輸入啟用日本 eSIM。</p>

<blockquote>
  <p><em>如果還是很怕網路造成入境問題， <strong>也可先截圖入境 QR Code</strong> 或同步寫紙本入境卡，雙重保障。</em></p>
</blockquote>

<p>之前去 <a href="/posts/z-度旅行遊記/曼谷自由行攻略-5-日行程規劃-必訪景點與實用旅遊安全提醒-b7e7c0938985/">泰國買的 eSIM 可以直接在購買憑證信、PDF 或截圖照片中長按 QR Code 出現「加入 eSIM」點擊即可自動加入</a> ，但這次買的日本 eSIM 卻沒有出現：</p>

<p><img src="/assets/aacd5f5cacd1/1*su5YVQrLdjFXORHuiMZxuw.webp" alt="" loading="lazy" decoding="async" width="567" height="1013" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1NjciIGhlaWdodD0iMTAxMyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><strong>有找到一個 <a href="https://www.ptt.cc/bbs/iOS/M.1719797706.A.916.html" target="_blank">網友提供</a> 的方法可以成功出現「加入 eSIM」選項。(感覺是 iOS Bug，iOS 17.4 之後就壞了)：</strong></p>

<p><img src="/assets/aacd5f5cacd1/1*DrYXYUfGj_BddPe8FwbM4A.webp" alt="" loading="lazy" decoding="async" width="578" height="1047" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1NzgiIGhlaWdodD0iMTA0NyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*Z-Ls2JPUbJAw93L7ey91gw.webp" alt="" loading="lazy" decoding="async" width="569" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1NjkiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*BwNRm45uuwJcWjnYicDIEA.webp" alt="" loading="lazy" decoding="async" width="562" height="1151" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1NjIiIGhlaWdodD0iMTE1MSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<ol>
  <li>前往 eSIM 憑證信、PDF 按下手機截圖，將截圖存在照片相簿中</li>
  <li>打開手機相機，點左下角進入相簿</li>
  <li>在相簿中找到 eSIM 照片，長按 QRCode 就會出現「加入 eSIM」選項</li>
  <li>點擊「加入 eSIM」直接帶入啟用資訊啟用 eSIM</li>
</ol>

<h4 id="iphone-手動啟用-esim">iPhone 手動啟用 eSIM</h4>

<p><strong>前往「設定」-&gt;「行動服務」-&gt;「加入 eSIM」-&gt;「使用行動條碼」：</strong></p>

<p><img src="/assets/aacd5f5cacd1/1*TUIEbNDhczwvTehfFza_sg.webp" alt="" loading="lazy" decoding="async" width="562" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1NjIiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*7hYiMadhEnU5NOt58ashJw.webp" alt="" loading="lazy" decoding="async" width="602" height="1306" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2MDIiIGhlaWdodD0iMTMwNiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*YUjZUpfB6BeLQKNWTMx-5A.webp" alt="" loading="lazy" decoding="async" width="581" height="1239" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1ODEiIGhlaWdodD0iMTIzOSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>我有把 eSIM QRCode 印出來，所以我是從這邊直接掃描啟用的。</p>

<p>如果你什麼都沒有可以點「手動輸入詳細資訊」複製貼上 eSIM 憑證資訊手動啟用：</p>

<p><img src="/assets/aacd5f5cacd1/1*TdmMDosao98OATMJNa0NLw.webp" alt="" loading="lazy" decoding="async" width="1200" height="1170" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjExNzAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>對應信件內容填上 <code class="language-plaintext highlighter-rouge">SM-DP+ 位址</code> 、 <code class="language-plaintext highlighter-rouge">啟用碼</code> 、 <code class="language-plaintext highlighter-rouge">確認碼</code> (這次沒有)，點下一步完成啟用。</p>

<p><img src="/assets/aacd5f5cacd1/1*cXjnkvYvDGnE-ByT3hcnaw.webp" alt="" loading="lazy" decoding="async" width="602" height="1306" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2MDIiIGhlaWdodD0iMTMwNiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*sc6xr-nA3J_BdYMcNBNMFA.webp" alt="" loading="lazy" decoding="async" width="602" height="1306" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2MDIiIGhlaWdodD0iMTMwNiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*Rh7hWbEiUtLbR3VYXq9NOw.webp" alt="" loading="lazy" decoding="async" width="581" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1ODEiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>輸入完成後需要花一點時間啟用，可能會卡在「啟用中…」一陣子或是跳出啟用失敗稍後再試之類的；再等一下子就好了，啟用成功在「設定」-&gt;「行動服務」中會顯示「開啟」右上角訊號圖標也會出現下面一排 … 顯示訊號狀況。</p>
<ul>
  <li>行動方案標籤可選擇「旅遊」</li>
</ul>

<blockquote>
  <p><strong><em>我們還在台灣，暫時還無法連接使用 eSIM 日本網路，只要確認啟用成功即可。</em></strong></p>
</blockquote>

<h4 id="-1220-開始登機無誤點">~= 12:20 開始登機，無誤點</h4>

<p><img src="/assets/aacd5f5cacd1/1*R1I1UY831lnjto_6jqwCFw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<h4 id="-1305-準時起飛">~= 13:05 準時起飛</h4>

<p><img src="/assets/aacd5f5cacd1/1*ovXkaMS4yzLWpm2_YBTPFw.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*IQ1kIFLjFgrYHUqulJMO7Q.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>此時台灣開始下大雨，但是沒什麼風對起飛影響不大。</p>

<p><img src="/assets/aacd5f5cacd1/1*Ooxdur72IKkRc4u5q52esQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*ECU47qVPUd7cv6UcBy6i3Q.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>虎航的位子舒適度我覺得算廉航中優秀的了(相較樂桃太小、越捷椅子不好坐)，不過虎航的缺點是 <a href="https://www.threads.net/@kechi.syonen/post/DCYnEQNzU5L/%E4%BB%80%E9%BA%BC%E8%83%BD%E5%B8%B6%E4%B8%8A%E9%A3%9B%E6%A9%9F%E4%BB%80%E9%BA%BC%E4%B8%8D%E8%83%BD%E5%B8%B6%E5%BE%88%E6%B8%85%E6%A5%9A%E4%BB%99%E5%8F%B0%E6%A9%9F%E5%A0%B4" target="_blank"><strong>禁止自帶食物</strong></a> ，剛吃飽不餓！</p>

<p>機上無 WiFi 只能聽聽音樂玩玩單機遊戲。</p>

<p><img src="/assets/aacd5f5cacd1/1*LaNvJoYfPvU3LJchfvJHlg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*B5bSSUIBkoDGo147pFsEVQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*9Ibz55I3hZC0C7lSO-4o1A.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>可以從窗外看到日本天氣很好。👍👍👍</p>
<h4 id="1630-台灣時間-1530-準時降落">16:30 (台灣時間 15:30) 準時降落</h4>

<p><img src="/assets/aacd5f5cacd1/1*paerDziYhfHNdjAg1QeJBg.webp" alt="" loading="lazy" decoding="async" width="532" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1MzIiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*y36b_kkD7RMz1R501bPkIw.webp" alt="" loading="lazy" decoding="async" width="566" height="1267" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1NjYiIGhlaWdodD0iMTI2NyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>降落日本後我們的日本 eSIM 應該就能收到訊號了。</p>

<blockquote>
  <p><em>有訊號但還無法上網，要再到「設定」-&gt;「行動服務」-&gt; 點選進入 <strong>旅遊 日本 eSIM</strong> -&gt; 確認有「開啟此號碼」跟 <strong>打開「數據漫遊」這樣才會有網路！⚠️⚠️⚠️</strong></em></p>
</blockquote>

<blockquote>
  <p><em>另外也可以進入 <strong>主要 中華電信</strong> 號碼，如果您沒有購買中華電信的漫遊方案請記得 <strong>關閉這邊的「數據漫遊」、「開啟此號碼」可以繼續保持啟用，這樣才能收台灣的簡訊！</strong></em></p>
</blockquote>

<blockquote>
  <p><strong><em>請注意：不要隨意接電話、海外接國內電話收話方也會被收費！⚠️⚠️⚠️ (只有收簡訊免費)</em></strong></p>
</blockquote>

<h4 id="-1700-完成入境拿行李">~= 17:00 完成入境、拿行李</h4>
<ul>
  <li>岡山桃太郎機場是非常小的機場，同一時間只有一班國際航班(就是你這班而已)，跟著同機的人一起入境、拿行李，很快。
(本次航班看起來快滿載，大約花 30 分鐘就出來了)</li>
  <li>意外的事是機場工作人員都會講中文</li>
  <li>可能因為小機場比較嚴格，如果跟我一樣獨旅的幾乎都被問會待幾天？會去哪些地方？有的還被要求出示飯店訂單。
(因此自己去的話請提早準備應對)</li>
</ul>

<h4 id="機場-wifi">機場 WiFi</h4>

<p><img src="/assets/aacd5f5cacd1/1*evDWJrEApdI2laaMO01asw.webp" alt="" loading="lazy" decoding="async" width="553" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1NTMiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*YqPFrPIOePpu3WgHmBMXBA.webp" alt="" loading="lazy" decoding="async" width="602" height="1306" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2MDIiIGhlaWdodD0iMTMwNiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*7sCEBxL7NtIZHBeap-IW-w.webp" alt="" loading="lazy" decoding="async" width="560" height="1127" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1NjAiIGhlaWdodD0iMTEyNyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<blockquote>
  <p><em>岡山機場免費 WiFi 連上之後要輸入電子郵件，去信箱收信點連結啟用，才能持續連上使用；如果只是為了 Visit Japan 可以在連上 10 分鐘內打開網頁叫出入境 QR Code 即可。</em></p>
</blockquote>

<h4 id="-1704-搭上岡山機場前往-jr-岡山車站的巴士">~= 17:04 搭上岡山機場前往 JR 岡山車站的巴士</h4>

<p><img src="/assets/aacd5f5cacd1/1*YNxPMPmNH-YBWNemZR6t-Q.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*4c4diTl_B42J1RTeChslgA.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*k3bf2CWOwFKCWeC1eC1yTQ.webp" alt="最新班次時刻表請參考官網" loading="lazy" decoding="async" width="371" height="634" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzNzEiIGhlaWdodD0iNjM0Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><a href="https://www.okayama-airport.org/tw/access/bus" target="_blank">最新班次時刻表請參考官網</a></p>
<ul>
  <li>機場很小，順著路走出來就是往岡山車站的巴士，而且會有工作人員引導、不怕迷路直接上車。</li>
  <li><strong>航班太少，所以巴士除了按照表定發車時間，還會跟著班機抵達時間，加開班次；也不用怕滿員搭不上，滿員會再發車。</strong> 
(照 <a href="https://www.okayama-airport.org/tw/access/bus" target="_blank">表定要等到 19:05 才有車</a> ，但實際有加班車。)</li>
  <li><a href="https://www.ptt.cc/bbs/Japan_Travel/M.1730096575.A.AB6.html" target="_blank">2024/11/30 前出示護照可以</a> 免費搭乘；免費接駁不定期舉辦， <a href="/posts/z-度旅行遊記/山陽地區廣島岡山自由行攻略-6日行程-交通-住宿全解析-31b9b3a63abc/">去年來的時候沒有</a> 。</li>
</ul>

<h4 id="-1800-抵達-jr-岡山車站">~= 18:00 抵達 JR 岡山車站</h4>

<p><img src="/assets/aacd5f5cacd1/1*8OhJp66_cz7XpEzC8I6WpQ.webp" alt="" loading="lazy" decoding="async" width="912" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MTIiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*nxBsum1hduuDJJluFTz4DQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1010" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwMTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>遇到下班時間路上有點塞車，18:00 左右才到岡山車站。 <strong>(比預期多 30 分鐘)</strong></p>
<h4 id="jr-岡山站兌換-jr-pass--預定明日前往松江的車票">JR 岡山站兌換 JR Pass &amp; 預定明日前往松江的車票</h4>

<p><img src="/assets/aacd5f5cacd1/1*-2saCSONEdJw9FIYeVFZ4Q.webp" alt="" loading="lazy" decoding="async" width="953" height="1192" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NTMiIGhlaWdodD0iMTE5MiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*KJeCftSlvW2t-a1Ef5JvEA.webp" alt="" loading="lazy" decoding="async" width="952" height="1232" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NTIiIGhlaWdodD0iMTIzMiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*t9I2iSC2-97Dv5fX6FNqWw.webp" alt="" loading="lazy" decoding="async" width="640" height="745" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2NDAiIGhlaWdodD0iNzQ1Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<ul>
  <li>官網可查詢 <a href="https://www.westjr.co.jp/global/tc/ticket/receive/?railpass=11&amp;mco=1&amp;area=0" target="_blank">可兌換 JR Pass 的車站</a> ，不是每個站都可以換喔！
(JR 岡山站可以)</li>
  <li>岡山站找到自動售票處，排中間綠色的機器，只有這一台可以換 JR Pass</li>
</ul>

<p><strong>兌換流程：</strong></p>

<p><strong>準備好護照跟購買的 JR Pass 憑證 QR Code，可先選右上語言「繁體中文」，直接點左下「QRコードの読取り(QR碼掃描)」黃色按鈕兌換。</strong></p>

<blockquote>
  <p><strong><em>可以選擇開始使用日</em></strong> <em>，可預約指定席的時間也是從開始使用日開始計算。 <strong>⚠️</strong></em></p>
</blockquote>

<blockquote>
  <p><em>(像我是選擇明天開始)</em></p>
</blockquote>

<p>按照步驟掃描 JR Pass QR Code 和護照即可完成兌換。</p>

<p><img src="/assets/aacd5f5cacd1/1*nj6t65muUKTM8rL9b9MnUQ.webp" alt="JR Pass QR Code" loading="lazy" decoding="async" width="845" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4NDUiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>JR Pass QR Code</p>

<p><strong>兌換結果：</strong></p>

<p><img src="/assets/aacd5f5cacd1/1*A-kqDIwfmGwif12qTiUU1g.webp" alt="上圖拿去年去兌換的 JR Pass 為圖例" loading="lazy" decoding="async" width="604" height="770" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2MDQiIGhlaWdodD0iNzcwIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>上圖拿去年去兌換的 JR Pass 為圖例</p>

<p>兌換完會有三張券，其他兩張長方形的是說明書，沒有用； <strong>印有 PASS 的這張最重要，車站搭車進出站都是憑這張，要收好！</strong></p>
<h4 id="預約-jr-指定席">預約 JR 指定席</h4>

<p>這邊很重要，根據 <a href="https://www.westjr.co.jp/global/tc/ticket/pass/kansai_sanin/" target="_blank">JR Pass 關西＆山陰周遊券官網</a> 提供的資訊， <a href="https://www.westjr.co.jp/global/tc/ticket/pass/pdf/Reserved_seats_tc.pdf" target="_blank"><strong>前往山陰地區的 JR YAKUMO 八雲 やくも 號 是全車指定席</strong></a> <strong>，無自由座⚠️⚠️⚠️ (</strong> 2024年3月16日開始 <strong>)，均需提前劃位。</strong></p>

<blockquote>
  <p><em>應該也可以從網路先預約， <a href="https://medium.com/ztravel/%E9%81%8A%E8%A8%98-2024-%E4%BA%8C%E8%A8%AA%E4%B9%9D%E5%B7%9E-9-%E6%97%A5%E8%87%AA%E7%94%B1%E8%A1%8C-%E7%B6%93%E9%87%9C%E5%B1%B1-%E5%8D%9A%E5%A4%9A%E9%83%B5%E8%BC%AA%E5%85%A5%E5%A2%83-cb65fd5ab770?source=collection_home---4------1-----------------------" target="_blank">之前就是從網路先預約博多到由布院的 JR</a> ，只是要查一下。</em></p>
</blockquote>

<p><img src="/assets/aacd5f5cacd1/1*jPSXmntXpBrCzRGnB0Ty5A.webp" alt="" loading="lazy" decoding="async" width="1200" height="1051" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjEwNTEiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*YUzrKBjkuis18Az9sZMM5Q.webp" alt="" loading="lazy" decoding="async" width="751" height="575" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NTEiIGhlaWdodD0iNTc1Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<blockquote>
  <p><em>Google Map 上的交通資訊不完整，詳細班次跟 <strong>列車名稱</strong> 可以從 <a href="https://www.westjr.co.jp/global/sc/timetable/" target="_blank">JR 官網查詢得知</a> 。 <strong>⚠️⚠️⚠️</strong></em></p>
</blockquote>

<p><strong>JR Pass 預約指定席流程：</strong></p>
<ul>
  <li><a href="https://www.westjr.co.jp/global/tc/ticket/pass/kansai_sanin/" target="_blank"><strong>JR Pass 關西＆山陰周遊券</strong></a> <strong>的規定是可以使用機器預約 6 次指定席，超過要去人工櫃檯預約；各地區 JR Pass 次數跟限制不同，請參考官網資訊。</strong></li>
  <li>先查好出發地點的英文名與要去的地方 <strong>JR 站英文名</strong> ，例如：OKAYAMA(岡山) -&gt; MATSUE(松江)</li>
  <li>請注意是 JR 站英文名， <em>所以 <a href="/posts/z-度旅行遊記/九州自由行攻略-福岡-長崎-熊本10日獨旅全紀錄與交通住宿實戰經驗-d78e0b15a08a/">要去福岡要打 Hakata</a></em> XD</li>
  <li>第一步先選「繁體中文」</li>
  <li>第二步直接插入一張「JR Pass 車票」
(同行多人要一起劃位會在第四步請你繼續插入。)</li>
  <li>其他步驟照以下影片示範完成預約：</li>
</ul>

<iframe class="embed-video" loading="lazy" src="https://www.youtube.com/embed/oG8xrxEC6mk" title="JR Pass 預約指定席示範" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>

<blockquote>
  <p><em>影片是我在 安來站 Yasugi 預約前往 鳥取站 Tottori 為例拍攝的整個過程。</em></p>
</blockquote>

<blockquote>
  <p><em>(那站沒人在排自動售票機，才敢拿出手機錄下整個過程)</em></p>
</blockquote>

<p><strong>預約完成：</strong></p>

<p><img src="/assets/aacd5f5cacd1/1*wdAemMFceaXQ6mZEPJfzWQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*9E0CaLNkFZHyp6RfFbW8VQ.webp" alt="" loading="lazy" decoding="async" width="938" height="1226" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MzgiIGhlaWdodD0iMTIyNiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<ul>
  <li>因班次與明日行程安排，所以預約早 07:05 的班次先前往松江的飯店寄放行李</li>
  <li><strong>實際進出站還是用 JR Pass (上面那張)</strong> ⚠️⚠️⚠️，指定券這張只是讓你知道你的車廂跟座位編號。</li>
  <li>已經沒靠窗位只能坐走道 QQ</li>
</ul>

<p><img src="/assets/aacd5f5cacd1/1*eWbl5HfHDsgs10xMbQByBA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*flqVSVO61pO1j1rk6sYICw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*cFmuaiXspF7Fk3FeKY_Vwg.webp" alt="" loading="lazy" decoding="async" width="952" height="1225" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NTIiIGhlaWdodD0iMTIyNSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>在岡山車站隨便買了一個炸牛排便當跟超商買些吃的喝的後就去飯店休息了；岡山車站正在整修，最後一天回程會再回來岡山。</p>

<p><img src="/assets/aacd5f5cacd1/1*5OK4O6vRpvTbBHY1or45Ww.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>從桃太郎商店街這邊走五分鐘就到飯店。</p>
<h4 id="-1900-回飯店開吃休息">~= 19:00 回飯店開吃、休息</h4>
<iframe class="embed-video" loading="lazy" src="https://www.youtube.com/embed/UfWKBa1FAto" title="東橫INN 岡山站東口" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>

<p><img src="/assets/aacd5f5cacd1/1*1JJAxuSJ6NLnq4lRXZPajw.webp" alt="" loading="lazy" decoding="async" width="1200" height="843" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg0MyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*5aXGicaDQzX-9EREx0ooaw.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*0XBFIrZROFSOuAr7psn7NA.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>便當的份量有點少、岡山白桃這罐水蜜桃酒很好喝！再加上懷念的日本超商(全家的)熱狗跟炸雞，今晚很滿足；晚安岡山！為明早早起開始日本行做足準備。</p>
<h4 id="岡山後樂園點燈"><a href="http://genso-teien.okayama.jp/" target="_blank">岡山後樂園點燈</a></h4>

<p>今年 2024 的岡山後樂園點燈是 11月15日(金)～11月24日(日)，來的這天也還沒有；如果有點燈可以去看看，很美。</p>

<p><img src="/assets/aacd5f5cacd1/1*tVwuOQS4DytQGzfk3tn52g.webp" alt="去年 2023 山陽郵寄點燈我拍的照片" loading="lazy" decoding="async" width="1242" height="939" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjQyIiBoZWlnaHQ9IjkzOSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><a href="/posts/z-度旅行遊記/山陽地區廣島岡山自由行攻略-6日行程-交通-住宿全解析-31b9b3a63abc/">去年 2023 山陽郵寄點燈我拍的照片</a></p>
<h3 id="day-2-1113-週三-島根--出雲大社宍道湖夕陽">Day 2 (11/13 週三) 島根 — 出雲大社、宍道湖夕陽</h3>
<ul>
  <li>KKday 推薦： <a href="https://www.kkday.com/zh-tw/product/20314-japan-jr-sanyo-sanin-northern-kyushu-pass-delivery-to-multiple-countries?cid=19365" target="_blank">日本 JR PASS|山陽&amp;山陰&amp;北九州地區鐵路周遊券|多國郵寄</a>
    <h4 id="0705-搭乘-yakumo-やくも-八雲號-1-前往-松江">07:05 搭乘 Yakumo やくも 八雲號 1 前往 松江</h4>
  </li>
</ul>

<p><img src="/assets/aacd5f5cacd1/1*-ppEnPrSVSls2IfkppB_pA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*NBhG889zmADfQDGI-7zlAQ.webp" alt="" loading="lazy" decoding="async" width="963" height="751" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NjMiIGhlaWdodD0iNzUxIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><img src="/assets/aacd5f5cacd1/1*yb0D7yt38ygSmnGJyEjoEA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*168t2gezMC-ucwNzG3_m5g.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*0DUCHWLSu7X5LG35oylmBw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*m4JyAVEfNrUoaIt1Hk3Zeg.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>車內蠻舒適的，前後有行李區可以直接放行李，座位也有充電座。</p>

<p><img src="/assets/aacd5f5cacd1/1*vg7v1Xz5RlcQ9KqnMwiZjw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*M8ciQoiAy5GWHQoa34vo1w.webp" alt="" loading="lazy" decoding="async" width="879" height="994" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4NzkiIGhlaWdodD0iOTk0Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>總路程約需 2 小時 45 分鐘，可以感受到一路穿山越嶺，一大清早的很多地區都還是霧氣濛濛，頗有穿過山洞來到世外桃源的感覺；可惜沒劃到靠窗位，一路只能遠觀。</p>
<h4 id="0949-抵達松江">09:49 抵達松江</h4>

<p><img src="/assets/aacd5f5cacd1/1*RNcfgkoIlBNqAbZiJGXUeA.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*wuK_7kxYguDnQoYd5nNXtA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*GfeSKNvv30aqgIOUIcVAzw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>松江天氣超級好☀️，抵達松江站後直接前往今晚入住的飯店「 <a href="https://www.station.matsue-urban.co.jp/en/" target="_blank"><strong>Matsue Urban Hotel</strong></a> 」寄放行李。</p>

<p><img src="/assets/aacd5f5cacd1/1*YCar9o50KKuyrx5blLVipg.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*58UjwwimiMZZS5fhCc2o7w.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>寄放完再回到松江站此時大約 10:00，重新劃位 10:48 從 松江 前往 出雲市 的 Yakumo 3 號位子。</p>

<blockquote>
  <p><strong><em>應該昨天劃位 岡山到松江 就要順便再劃位一張 松江到出雲市 的</em></strong> <em>，因為下午想去看夕陽所以才先在松江寄放行李，不然就直接到出雲市不中途浪費時間換車了。</em></p>
</blockquote>

<blockquote>
  <p><em>另外也可以搭乘 一畑電車大社線 前往出雲大社，總時間差不多。</em></p>
</blockquote>

<p><img src="/assets/aacd5f5cacd1/1*Wvd6qczYLcEb8XywbGV_Vg.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*602_X70DAMAYJiEI2d024Q.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*Ji2S8d1orIkJB5Wyu3DCSw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>還有一點時間跑去超商買了兩個御飯糰、咖啡、炸雞帶著當午餐吃。</p>

<p><img src="/assets/aacd5f5cacd1/1*OkaENzl4tJ0nSMqMSznEmw.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*NNFURE5UfVdWOgkpYuAtrg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*5-vTR2_TeMIlIiPggNON5w.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>一路上會經過宍到湖，超級大一開始以為是海，其實是湖。</p>

<blockquote>
  <p><em>劃位可以劃 D 靠窗位子才可以直接看宍到湖，我劃 A 側靠山看不到 QQ。</em></p>
</blockquote>

<h4 id="1112-抵達出雲市">11:12 抵達出雲市</h4>

<p><img src="/assets/aacd5f5cacd1/1*IGzRRA0zChR7eosgVBM30g.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*_dWPzssuOTsPLhGlPeaxBg.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>抵達松江站後一路小跑步，要趕 11:25 一畑電車北松江線 先前往 川跡車站。</p>

<blockquote>
  <p><em>一畑電車無法刷交通卡進站，要先在門口自動售票機(可用電子支付)買車票，不知道怎麼買直接跟站務人員說到 <code class="language-plaintext highlighter-rouge">Izumo Taisha</code> (出雲大社)即可。</em></p>
</blockquote>

<p><img src="/assets/aacd5f5cacd1/1*zOWAvtPBUa4Q6p_O0UI_4A.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*hqp8E0znWzflcoF_wdXbYA.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>在 川跡車站 需要再換車 (跟著大家走到對面月台)，轉乘 一畑電車大社線 前往 出雲大社前 站。</p>
<h4 id="約-12-點抵達-出雲大社">約 12 點抵達 出雲大社</h4>

<p><img src="/assets/aacd5f5cacd1/1*DbbHj7dvLzIZ5yucQPqW2Q.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*TNniVRe3T0Lx2h8ZEO6UoQ.webp" alt="" loading="lazy" decoding="async" width="902" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDIiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>大神前的表參道很清幽漂亮，有時間的話這邊也有很多小店美食可以逛。</p>

<blockquote>
  <p><em>後來才知道出雲四座鳥居也很有名，其中一個白色大鳥居就在出車站表參道往神社反方向走就能在入口找到：</em></p>
</blockquote>

<p><img src="/assets/aacd5f5cacd1/1*PsT0vPHhgZXJUJVbJzg9TA.webp" alt="來源：島根旅遊官網" loading="lazy" decoding="async" width="1268" height="823" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjY4IiBoZWlnaHQ9IjgyMyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><a href="https://www.kankou-shimane.com/zh-tw/highlights/1615" target="_blank">來源：島根旅遊官網</a></p>

<blockquote>
  <p><em>詳細可參考 <a href="https://www.kankou-shimane.com/zh-tw/highlights/1615" target="_blank">島根旅遊官網</a></em></p>
</blockquote>

<p><img src="/assets/aacd5f5cacd1/1*l9SL74ldAxYcnEpLmIFgsA.webp" alt="" loading="lazy" decoding="async" width="1200" height="875" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg3NSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*NmTX-LAuUYpyNxZH1iyOmA.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*lL6Lshf934FqRMAHwuf8Sw.webp" alt="" loading="lazy" decoding="async" width="988" height="1029" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5ODgiIGhlaWdodD0iMTAyOSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>往上走到出雲大社入口，來的時間點 <code class="language-plaintext highlighter-rouge">2024/11/13</code> 正好是 <strong>神在祭</strong> 期間 ( <code class="language-plaintext highlighter-rouge">2024 令和 6 年 11/11~11/17</code> )，所以來參拜的日本人非常多。</p>

<blockquote>
  <p><em>日本每年的農曆 10 月(西曆 11 月)，全國八百萬神明齊聚出雲「神議」討論人間事務，如緣分安排與自然調和。</em></p>
</blockquote>

<blockquote>
  <p><em>因此 <strong>這個月份在日本其他區稱為「神無月」（神不在）且多半無祭典</strong> 。</em></p>
</blockquote>

<blockquote>
  <p><strong><em>只有在出雲這稱為「神在月」意即只有出雲地區有神。</em></strong></p>
</blockquote>

<blockquote>
  <p><em>祭典包括迎接神明的「神迎祭」、祈求良緣的「縁結大祭」、以及送別神明的「神送り祭」</em></p>
</blockquote>

<blockquote>
  <p><strong><em>出雲大社供奉主神：大國主命（おおくにぬしのみこと）</em></strong></p>
</blockquote>

<p>真的很巧，當初在安排時間的時候其實沒有刻意選這個時間點，是在查景點資訊的時候才知道。</p>

<p><img src="/assets/aacd5f5cacd1/1*mHn3NX4zb31WmXFITQBOIQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*iehGyLgSPf4LgUzjhMjfzw.webp" alt="" loading="lazy" decoding="async" width="952" height="1285" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NTIiIGhlaWdodD0iMTI4NSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*IVJtJR2htbOjimqgqKvLrg.webp" alt="" loading="lazy" decoding="async" width="896" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4OTYiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>穿過入口鳥居後還要再走大約 5 分鐘才會到神社。</p>
<h4 id="日本第一大日之丸國旗">日本第一大「日之丸」國旗</h4>

<p><img src="/assets/aacd5f5cacd1/1*WBerwVlV_I8PeYqj2KRX1w.webp" alt="" loading="lazy" decoding="async" width="959" height="1183" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NTkiIGhlaWdodD0iMTE4MyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>進入神社後往左方看就會看到一根聳立的旗竿，上面是日本最大的日之丸國旗(約 75 張榻榻米、137 平方公尺)。</p>

<p>朝這支旗杆的方向走就會來到「神樂殿」這裡有日本第一大 <a href="https://sanin-japan.com/zh-tw/special/territory-of-the-gods-yokozuna-and-izumo-taisha/" target="_blank">注連繩</a> 。</p>

<p><img src="/assets/aacd5f5cacd1/1*x8zuQYtIXNILxGLkMjQbJg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1056" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTYiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*3WjUHQV3lwqRTi3IegRb6w.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*KeY6bAoW8DsPQf4BtMuhcA.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>近看真的超大，尤其跟其他殿的一般注連繩比較，大概是其他的 10 倍大。(長度為 14 公尺、重量為 5 公噸、每隔幾年會換一次、純手工製作)</p>

<blockquote>
  <p><em>請記得最大的在「神樂殿」這裡，不要跟我一樣一開始以為在本殿。</em></p>
</blockquote>

<p><img src="/assets/aacd5f5cacd1/1*KZnzwRAX5CdPvprYWwCzTg.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*L3BUl_V1wCtc9hiRiiGlqw.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>環繞走了一圈，人蠻多的，有的需要排很長的隊伍參拜。</p>

<p><img src="/assets/aacd5f5cacd1/1*7iUkAuzNjt8d1wHbtiUWhQ.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*_DCfvI3CSAqMeC0hnZzCHA.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<blockquote>
  <p><em>出雲大社的參拜方式跟其他神社也不同：⚠️⚠️⚠️</em></p>
</blockquote>

<blockquote>
  <p><em>是 <a href="https://www.japan.travel/hk/spot/937/" target="_blank"><strong>先鞠躬兩次、拍手四次，最後再鞠躬一次</strong></a> 。</em></p>
</blockquote>

<p><img src="/assets/aacd5f5cacd1/1*uKMCGxlcrtLXp_wuz0Okqg.webp" alt="" loading="lazy" decoding="async" width="890" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4OTAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>買了神在祭期間限定的御守。 <strong>(僅此一周販售)</strong></p>

<p><img src="/assets/aacd5f5cacd1/1*Zg626XNieV5uT7JLszzsXQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*kGHi22HqIHPB3-vtdGXShg.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*KUh8eckUM7jasbTZFl3nmw.webp" alt="" loading="lazy" decoding="async" width="815" height="1019" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4MTUiIGhlaWdodD0iMTAxOSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>參拜完後拿了一本印章搜集簿，蓋出雲大社紀念章。</p>

<p><img src="/assets/aacd5f5cacd1/1*M2zO2XrTYc941f8q6n439A.webp" alt="出雲神在月神在社巡拜五個神大社位置。" loading="lazy" decoding="async" width="1400" height="664" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjY2NCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><a href="https://www.google.com/maps/d/u/0/edit?mid=1InF3hfsLjC7uW8T2J2ojcXJ_Ej3Eo0k&amp;usp=sharing" target="_blank">出雲神在月神在社巡拜五個神大社位置。</a></p>

<p>因時間有限出雲其他神社就沒去參拜了，沒有自駕感覺也很難搜集。</p>
<h4 id="-1245-徒步前往-稻佐之濱-稻佐海灘-稲佐の浜">~= 12:45 徒步前往 稻佐之濱 稻佐海灘 稲佐の浜</h4>

<p><img src="/assets/aacd5f5cacd1/1*tp8izFV2pcWAkHrOxguusw.webp" alt="" loading="lazy" decoding="async" width="960" height="235" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NjAiIGhlaWdodD0iMjM1Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><img src="/assets/aacd5f5cacd1/1*-hHa8tNAXHA8hkuI3ySAVA.webp" alt="" loading="lazy" decoding="async" width="953" height="1245" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NTMiIGhlaWdodD0iMTI0NSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*Yj37xFyP1gByfLvNdDTOtA.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*RQmCSnIsPBVcJW1r9nSS3w.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>公車班次太少只能用走的去，大約要 15 分鐘，途中會經過出雲阿國之墓，一路走到底就是稻佐之濱了。</p>

<blockquote>
  <p><strong><em>稻佐之濱：</em></strong></p>
</blockquote>

<p><img src="/assets/aacd5f5cacd1/1*YRUdkflWxYERcA62-AQ2Eg.webp" alt="來源：島根旅遊官網" loading="lazy" decoding="async" width="1200" height="1015" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjEwMTUiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><a href="https://www.kankou-shimane.com/zh-tw/destinations/991" target="_blank">來源：島根旅遊官網</a></p>

<blockquote>
  <p><em>日本神話淵源深厚的景點，圖中的小島叫弁天島，島上有一座小祠堂。</em></p>
</blockquote>

<blockquote>
  <p><a href="https://www.kankou-shimane.com/zh-tw/destinations/991" target="_blank"><em>日本百大海灘，「日沒聖地出雲」地標、夕陽從弁天島背後落入海中的景色十分美麗，並獲登錄為日本遺產、也是神迎儀式的舉辦地點，在此恭迎日本全國神祇。</em></a></p>
</blockquote>

<p><img src="/assets/aacd5f5cacd1/1*PIyVVCAMngDrgpErhjGRMw.webp" alt="" loading="lazy" decoding="async" width="958" height="1245" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NTgiIGhlaWdodD0iMTI0NSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*lYRW_DUWWfI1aDJVkBtphA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*tI4HMhJCNhlYlHKkFIJm9w.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>因為到訪時間有限，無法待到夕陽，逛了一下就折返離開了。</p>

<blockquote>
  <p><strong><em>回來台灣滑到 IG，才知道這邊有 <a href="https://www.japaholic.com/tw/article/detail/887878-%E4%BE%86%E5%B3%B6%E6%A0%B9%E7%B8%A3%E5%87%BA%E9%9B%B2%E5%A4%A7%E7%A4%BE%E6%B1%82%E5%BA%87%E8%AD%B7%E8%A6%81%E6%8C%96%E7%A0%82%EF%BC%9F%E7%A8%BB%E4%BD%90%E4%B9%8B%E6%BF%B1%E5%8F%96%E7%A5%9E%E7%A0%82%E6%B1%82%E5%BE%A1%E5%AE%88%E6%95%99%E5%AD%B8" target="_blank">砂御守可以求</a> ：</em></strong></p>
</blockquote>

<p><img src="/assets/aacd5f5cacd1/1*9XZ8oaxmYdrNU3I68EgTnw.webp" alt="https://www.instagram.com/reel/CynStXPyZAX/" loading="lazy" decoding="async" width="696" height="620" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2OTYiIGhlaWdodD0iNjIwIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><a href="https://www.instagram.com/reel/CynStXPyZAX/?utm_source=ig_web_copy_link" target="_blank">https://www.instagram.com/reel/CynStXPyZAX/</a></p>

<p>走回出雲大社公車站準備搭乘公車回到 JR 出雲站。</p>

<p><img src="/assets/aacd5f5cacd1/1*AqsQ2S3q61tJaq7k6fgdIA.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>可以看到來參拜等待進入出雲大社停車場的車輛綿延不絕。</p>

<p><img src="/assets/aacd5f5cacd1/1*kL0wZHJiMcqSCE3ChzTpNQ.webp" alt="" loading="lazy" decoding="async" width="759" height="415" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NTkiIGhlaWdodD0iNDE1Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><img src="/assets/aacd5f5cacd1/1*8aaLagUno9PZTAYUCpX4-Q.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*XrDT2aXpUPfF7NT5IEFFNA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>出雲大社公車站在紀念品店穿過廁所後面這邊，小小的一開始一直很怕等錯。</p>

<p><strong>此時約 13:15，要等 13:40 回 JR 出雲站的公車，肚子很餓就先去前面紀念品店跟旁邊買些東西拿手上簡單吃。</strong></p>

<p><img src="/assets/aacd5f5cacd1/1*FYPk9dJKH166TgfMJbKinA.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>在記念品店買了幾個中二貼紙。</p>
<h4 id="1340-搭乘公車回-jr-出雲站"><strong>13:40 搭乘公車回 JR 出雲站</strong></h4>

<p><img src="/assets/aacd5f5cacd1/1*gvzJ1xXYnLyxNU_HAlFyNA.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<blockquote>
  <p><em>回出雲的路上遇到一位日本人跟我借行動電源，他的手機完全掛了，正好問他確認一下(順便練習我為數不多有記的日文)「 <strong>このバスは JR 出雲市駅へ行きますか</strong> 」，他回答「 <strong>はい</strong> 」(我也只聽得懂 hai)。</em></p>
</blockquote>

<h4 id="-1415-回到-jr-出雲市站">~= 14:15 回到 JR 出雲市站</h4>

<p>(趕日落前去看宍道湖夕陽，根據資訊 <strong>當日 17:05 太陽下山</strong> )</p>

<p><img src="/assets/aacd5f5cacd1/1*roLGAz0BE_7yrZ7010cJ1w.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*JxPpTZphqlQpjXjOlEmRfA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>這次就沒搭八雲號了，搭 14:54 前往米子的普通區間車，照 Google Map 建議從乃木站下車走路到宍道湖夕日觀景點。</p>

<p><img src="/assets/aacd5f5cacd1/1*6-c6bp49Is4qZ_IdbiMPCg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*kXIIylCmu_fP_FA6IGQ3_g.webp" alt="" loading="lazy" decoding="async" width="947" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NDciIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<blockquote>
  <p><em>往松江方向坐左邊(A)的話，經過宍道湖就可以直接看到湖景，沒錯我又坐錯邊了，坐到靠山的這邊。</em></p>
</blockquote>

<blockquote>
  <p><strong><em>這小站的區間車要按按鈕才會開門，第一次遇到。</em></strong> <em>⚠️</em></p>
</blockquote>

<h4 id="1535-抵達-乃木站">~=15:35 抵達 乃木站</h4>

<p><img src="/assets/aacd5f5cacd1/1*4CNzZ0RmgGHRVr4NJ899zA.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*5mXpmq_hk-UlrD_KF-x1xg.webp" alt="" loading="lazy" decoding="async" width="489" height="583" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0ODkiIGhlaWdodD0iNTgzIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>乃木站非常小，沒有站務員也沒有檢票機， <strong>持 JR Pass 搭乘就直接出站就好，不要傻傻的把 JR Pass 留在收票盒子裡。⚠️⚠️⚠️</strong></p>

<p><img src="/assets/aacd5f5cacd1/1*hiBPok9dbi9XGQeAPRQ5NQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*E4Yd7PB_MJh1CyuwFJS3zw.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*MAtnP-62r3MY0NSPPSy0yg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>從乃木站走有點失策，這邊很荒涼沒什麼人…</p>

<p>不過還好中途有一家 7–11 買了熱狗跟炸雞、飲料邊吃邊等夕陽。</p>
<h4 id="1600-宍道湖等夕陽">16:00 宍道湖等夕陽</h4>

<p><img src="/assets/aacd5f5cacd1/1*ptNqLKfhX0azm3b5HxfBZg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*krl-46lxVoh33r7TcwgpkQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<blockquote>
  <p><a href="https://www.kankou-shimane.com/zh-tw/destinations/985" target="_blank"><em>宍道湖日本第七大湖泊、宍道湖夕陽，日本第一夕日。</em></a></p>
</blockquote>

<blockquote>
  <p><em>圖中小島嫁島神社，有一個白色小鳥居。</em></p>
</blockquote>

<blockquote>
  <p><em>之前打錯打成穴道湖。</em></p>
</blockquote>

<p>有一隻鷺。</p>

<p><img src="/assets/aacd5f5cacd1/1*ceThDvVZuO8MOR9OXn74ew.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*tz5i0B2jBta5vBsz-JMWHQ.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*1oDwRChVUUz-EVOv-HAWkw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>往上走會到島根縣立美術館，那邊比較熱鬧也有咖啡店；但是階梯平台這邊景色角度比較好。</p>
<h4 id="1645-夕陽西下">16:45 夕陽西下</h4>

<p><img src="/assets/aacd5f5cacd1/1*rmwJe1rnyjhQkJ9erx7q_w.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>本來沒什麼感覺，但接近夕日的時候開始體會到第一夕陽的魅力，剛剛的鷺也很配合地融入景色。</p>

<p><img src="/assets/aacd5f5cacd1/1*CdDLOmzP80ovaY94HDD3Wg.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>夕陽接近地平線後時間用肉眼可見的速度在消逝，每一分每一秒太陽都在下降。</p>

<p><img src="/assets/aacd5f5cacd1/1*pT7mh8XcC_Xt1_BO61SbfQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>很快地 17:01 就已經看不到太陽了。</p>
<iframe class="embed-video" loading="lazy" src="https://www.youtube.com/embed/K-xDmVjqG9g" title="20241113 宍道湖夕日" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>

<blockquote>
  <p><strong><em>真的很美、很療癒。</em></strong></p>
</blockquote>

<p><img src="/assets/aacd5f5cacd1/1*OG9M3KjHgChA0KY-MyBQPA.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*fzAcAYv8VNf8TjnrqwuqSg.webp" alt="" loading="lazy" decoding="async" width="923" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MjMiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>最後再環顧一下 <a href="https://maps.app.goo.gl/LRQAMZNht9km1DFm6" target="_blank">觀夕日階梯平台</a> 一同看夕陽的人們。</p>

<p><img src="/assets/aacd5f5cacd1/1*sV6sLyOiVzxv0W_K2i8SOQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*VaZo5Kl6x-oLJOs9Id3pYg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>日落之後轉身等公車準備回松江了。</p>
<h4 id="夕陽指數">夕陽指數</h4>

<p><img src="/assets/aacd5f5cacd1/1*gWAukxe8ynZEUN6Yr3-WAg.webp" alt="夕陽指數可參考島根旅遊網站" loading="lazy" decoding="async" width="1035" height="902" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDM1IiBoZWlnaHQ9IjkwMiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><a href="https://tw.visit-matsue.com/sunset" target="_blank">夕陽指數可參考島根旅遊網站</a></p>
<h4 id="宍道湖遊覽船">宍道湖遊覽船</h4>

<p><img src="/assets/aacd5f5cacd1/1*owwW5fWHIBKb0PQU_gMqVg.webp" alt="https://hakuchougo.jp/timetable/" loading="lazy" decoding="async" width="1136" height="834" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMTM2IiBoZWlnaHQ9IjgzNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><a href="https://hakuchougo.jp/timetable/" target="_blank">https://hakuchougo.jp/timetable/</a></p>

<p><a href="https://www.kankou-shimane.com/zh-tw/destinations/1086" target="_blank">宍道湖這邊是有遊覽船的</a> ，可以在接近夕陽的時候搭乘，從船上欣賞夕陽；如果下次再來會想搭搭看。</p>

<p><img src="/assets/aacd5f5cacd1/1*RGoFqjg8YVZwTzpINu4PCQ.webp" alt="" loading="lazy" decoding="async" width="554" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1NTQiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*nt2iPompX1qAecOKd_C7hA.webp" alt="" loading="lazy" decoding="async" width="1179" height="2556" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMTc5IiBoZWlnaHQ9IjI1NTYiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<blockquote>
  <p><em>可以直接使用 買 JR Pass 送的 <a href="https://www.kkday.com/zh-tw/product/20307-jr-sanin-okayama-area-pass?cid=19365" target="_blank"><strong>山陰山陽地區PASS周遊3日券</strong></a> <strong>免費搭乘，現省 1,800 日圓。</strong></em></p>
</blockquote>

<h4 id="1800-回到松江站飯店">18:00 回到松江站、飯店</h4>
<h4 id="在松江站一樓伴手禮店買一些伴手禮">在松江站一樓伴手禮店買一些伴手禮</h4>

<p>想說山陰這邊比較特別就在這買伴手禮吧。</p>

<p><img src="/assets/aacd5f5cacd1/1*gEdRChjcIYrXT_A8f6viyw.webp" alt="" loading="lazy" decoding="async" width="1144" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMTQ0IiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*fQj5GAdIRZ8uKKofUerGSw.webp" alt="" loading="lazy" decoding="async" width="1400" height="995" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9Ijk5NSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>比較特別的是牛骨湯拉麵，一般是豚骨，買了一盒回台灣品嚐。</p>

<p><img src="/assets/aacd5f5cacd1/1*n7-mvNNxKfjHJ7eNusg_Dg.webp" alt="" loading="lazy" decoding="async" width="1200" height="801" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjgwMSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*vqDE5sO1mYFkzrYP2WNsZg.webp" alt="" loading="lazy" decoding="async" width="1400" height="937" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjkzNyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*P175dGuAEKQkW-rvGbIIeg.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>買了兩大盒餅乾(適合分同事)回來分同事，甜的普通但是是出雲大社參拜紀念，鹹的海鮮煎餅餅乾我覺得很好吃！</p>
<h4 id="回飯店">回飯店</h4>

<p><img src="/assets/aacd5f5cacd1/1*kmBiT9EX6g-d743wStYgxQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*9NE5tv23D6k51TvDFNEypw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*MAHLZoAXHY9TYQO4fO8OEw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*VkEob5Q4_DKkVV14e67bQA.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*e3EL2ltwD0nNVb2TbJzonQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>本棟這邊沒有電梯，訂到的是 2 號館，可以從本棟大廳穿過天橋到 2 號館，2 號館 2 樓這邊有交誼廳微波爐、洗衣房、販賣機。</p>

<blockquote>
  <p><em>2 號館這邊體感沒有很安全，電梯沒有門禁、 <a href="https://www.cubic-room.jp/en/" target="_blank">1, 2 樓是膠囊旅館</a> 感覺出入比較複雜、離本館大廳又有距離，因此入住後自己用行李箱把門堵好，怕有怪人闖進來。</em></p>
</blockquote>

<iframe class="embed-video" loading="lazy" src="https://www.youtube.com/embed/NLHi7arAioA" title="" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>

<p><img src="/assets/aacd5f5cacd1/1*jXAp31kbI-h2r6EC9p3GTg.webp" alt="" loading="lazy" decoding="async" width="1200" height="920" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkyMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>大廳可以拿免費的面膜、咖啡、茶包。</p>
<h4 id="覓食">覓食</h4>

<p><img src="/assets/aacd5f5cacd1/1*bXI0YIH7eaO7vZj8MaAfDA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*O6V7aJeRtLcN5NWwYYtEUw.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*rNzPDu9gW2s89oW0oqxWSA.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>Check-in 後去外面覓食，外面蠻冷的也很暗、冷清，速速去車站周圍的餐廳外帶便當跟去超商買些吃的喝的就回飯店了。</p>

<p><img src="/assets/aacd5f5cacd1/1*FaaPod-j1hdncbRKhnNUOQ.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*UAYwYj0cNQZJsC99V7wq8A.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*Oubxpe7aZ2GPBXI92NEMuQ.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>牛舌便當好吃，超商買的水蜜桃氣泡酒也好喝。</p>

<blockquote>
  <p><em>晚安松江。</em></p>
</blockquote>

<p><strong>還有買幾乎每天一樣的隔日早餐組合…</strong></p>

<p><img src="/assets/aacd5f5cacd1/1*YTezWlFUnTYd6qv1J-yjTw.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>全家的這個果肉果汁每次來日本必喝！</p>
<h4 id="松江-宍道湖-24-小時直播">松江 宍道湖 24 小時直播：</h4>
<iframe class="embed-video" loading="lazy" src="https://www.youtube.com/embed/QYIjp0flsdQ" title="【LIVE】松江・宍道湖ライブカメラ" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>

<p>無意間找到的 Youtube 頻道，可以看到宍道湖天氣景色。</p>
<h3 id="day-3-1114-週四-松江堀川遊覽船松江城足立美術館">Day 3 (11/14 週四) 松江堀川遊覽船、松江城、足立美術館</h3>
<ul>
  <li>KKday 推薦： <a href="https://www.kkday.com/zh-tw/product/138193-customizable-chartered-car-to-kyoto-osaka-japan-himeji-okayama-tottori-adachi-museum-of-art-japan?cid=19365" target="_blank">日本大阪京都周邊,姫路,岡山,鳥取,島根足立美術館等10小時客製化包車</a></li>
</ul>

<p>一早先把行李寄放在飯店，到松江公車站搭公車去松江城遊船。</p>

<p><img src="/assets/aacd5f5cacd1/1*6Urhj-Bh4CXFgweozc99fQ.webp" alt="" loading="lazy" decoding="async" width="928" height="1183" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MjgiIGhlaWdodD0iMTE4MyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>Google Map 看不出月台，只能現場看，2 號月台這邊的車可以到松江城。</p>

<p><img src="/assets/aacd5f5cacd1/1*xXKGKy95X00KjnN50FBzvw.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*gdwD77FXsNcNeaKz7NlXBQ.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*4i5NFX226vWGW4yTEqOqIw.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>在縣民會館前下車，一路往松江城走，停車場這邊就能看到「堀川遊覽船」乘船場。</p>
<h4 id="啟用買-jr-pass-山陰山陽地區pass周遊3日券-免費搭乘-堀川遊覽船">啟用買 JR Pass <a href="https://www.kkday.com/zh-tw/product/20307-jr-sanin-okayama-area-pass?cid=19365" target="_blank"><strong>山陰山陽地區PASS周遊3日券</strong></a> <strong>免費搭乘</strong> 堀川遊覽船</h4>

<p><img src="/assets/aacd5f5cacd1/1*NKAYef5qS_1KVVW7sx2BKg.webp" alt="" loading="lazy" decoding="async" width="554" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1NTQiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*ksm-qt0AJ9qWES0k9HQ8-g.webp" alt="" loading="lazy" decoding="async" width="843" height="828" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4NDMiIGhlaWdodD0iODI4Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><img src="/assets/aacd5f5cacd1/1*dBYTcpDfIH02XoGUtviyTQ.webp" alt="" loading="lazy" decoding="async" width="583" height="1261" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1ODMiIGhlaWdodD0iMTI2MSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<ol>
  <li>下載 <a href="https://apps.apple.com/tw/app/discover-another-japan/id1530307213" target="_blank">Discover Another Japan App</a></li>
  <li>複製憑證信中的序號</li>
  <li>打開 App 選擇「輸入」貼上序號按下「有效化」</li>
</ol>

<p><img src="/assets/aacd5f5cacd1/1*bXVG4mS9rEj5I2nV8hUpqQ.webp" alt="" loading="lazy" decoding="async" width="553" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1NTMiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*6s5mwpiKNIIC3bNIw48qjw.webp" alt="" loading="lazy" decoding="async" width="553" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1NTMiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>切換到「使用」填寫健康問券，填完會出現 QR Code 相機；掃描店家提供的 QR Code 把結果給他看即完成兌換。</p>

<p>也可點「目標內容」查看可兌換項目。</p>

<blockquote>
  <p><strong><em>實測計算時間是從第一次兌換目標開始</em></strong> <em>，不是輸入序號有效化開始(我是 11/13 輸入的)。</em></p>
</blockquote>

<p><strong>幫大家整理幾個熱門可使用地點：</strong></p>
<ul>
  <li>宍道湖遊覽船，原價 1,800 日圓</li>
  <li>松江城游船，原價 1600 日圓</li>
  <li>足立美術館，原價 2300 日圓</li>
  <li>松江城，原價 680 日圓</li>
  <li>鳥取沙丘美術館，原價 800 日圓</li>
  <li>通天閣，原價 800 日圓</li>
  <li>岡山後樂園，原價 410 日圓</li>
</ul>

<p>真的很划算，光上面這些加總就可以省 8,390 日圓，已經是整個 JR Pass 金額的一半了。</p>
<h4 id="0915-搭乘松江遊覽船">09:15 搭乘松江遊覽船</h4>

<p><img src="/assets/aacd5f5cacd1/1*yTUoyuDBmxnZi7Z-LW5scg.webp" alt="" loading="lazy" decoding="async" width="943" height="1191" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NDMiIGhlaWdodD0iMTE5MSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*D0KiYKloofkru6tcupBTWw.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*0xbwCSI0IA7-pvoRwIhPVQ.webp" alt="" loading="lazy" decoding="async" width="1200" height="852" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg1MiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>直接用 <a href="https://www.kkday.com/zh-tw/product/20307-jr-sanin-okayama-area-pass?cid=19365" target="_blank">周遊3日券</a> 免費兌換搭乘。</p>
<ul>
  <li>今年比較溫暖還沒有換暖桌船</li>
  <li>會有四個過橋點需要下降船身，要小心手頭不要夾到</li>
  <li>全程約 50 分鐘</li>
  <li><strong>上船要脫鞋</strong> ⚠️</li>
  <li><strong>面向船夫的右邊那排才能直接拍到靠松江城的景，離船夫越遠離船頭越近(=剛進去的位子第一排)</strong> ⚠️ 如下圖</li>
</ul>

<p><img src="/assets/aacd5f5cacd1/1*NNpCqdpowVo3qcGUB2MtIA.webp" alt="" loading="lazy" decoding="async" width="963" height="1194" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NjMiIGhlaWdodD0iMTE5NCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*mycuhLZJdCqQCLjmdKBVuA.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>上船囉。</p>

<p><img src="/assets/aacd5f5cacd1/1*OaajDPfbGMPtNGE45b0D7w.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*pnqFPC8vK3sPGShIQY8vwg.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*_zTAi1v6rP1ObrmmGAyivw.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*LqE3q-z47EBROFnfkCzONA.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*7hUFpDj5m7TYkRzY_QhrOA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>可能是因為楓葉季還沒開始，所以沒什麼人，同船的只有四位，很空。</p>
<iframe class="embed-video" loading="lazy" src="https://www.youtube.com/embed/1dP7cQcqkW8" title="松江堀川遊覽船過橋" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>

<p>有四座橋過橋時會下降船身，要稍微彎腰趴著通過。</p>

<p><img src="/assets/aacd5f5cacd1/1*f71WOF93FIEGnY_AFJ94OQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*CgP34NWi1KsKk8je60g2Aw.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*mqX7jwzT8WnOhg9hwZKZPA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>一路吹風遊河很愜意，船夫也會一路介紹四周景點歷史(雖然我只聽得懂左邊hidari 右邊 migi…)。</p>

<p><img src="/assets/aacd5f5cacd1/1*vY1xPWmFrFq7m4ErBFYRAQ.webp" alt="https://youtube.com/shorts/KA9xKZV16bk" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><a href="https://youtube.com/shorts/KA9xKZV16bk" target="_blank">https://youtube.com/shorts/KA9xKZV16bk</a></p>

<p>最後快到回程的地方會有一個松江城拍攝點，可以從這邊拍松江城遠景。</p>

<p>遊船結束大約 10:00。</p>
<h4 id="1015-松江城">10:15 松江城</h4>

<p><img src="/assets/aacd5f5cacd1/1*ceaDL6BSPF0gB-VCXOw3fA.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*KTDVdtsyThXmXZC0hlXqPQ.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>遊船回來走回來的方向，另一邊就是松江城入口，往上走就會到松江城。</p>

<p><img src="/assets/aacd5f5cacd1/1*1zbsEA6XAM1EhL6AoGwyng.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*PNjiGAlCMJvOWEgPCi6Vrg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>途經松江神社，來到松江城。</p>

<p><img src="/assets/aacd5f5cacd1/1*rrtm3CezY7W1ZWPIbj5p-A.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>因為時間有限想繼續前往足立美術館，就沒有上去了。</p>

<p><img src="/assets/aacd5f5cacd1/1*9JAHlSW6QmLNzAWDU0-THA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*XmGdiTwZJk7pWY95CCZkrQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>回來到來的時候的反方向縣民會館前公車站等車回去。</p>
<h4 id="1030-回飯店拿行李">~=10:30 回飯店拿行李</h4>

<p><img src="/assets/aacd5f5cacd1/1*z5o-twtGfhsaje84wIlhlA.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*cG2vaqw6VB1jXg2Eg78Jnw.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>大約 10:30 回到松江站，回飯店拿行李。</p>

<blockquote>
  <p><strong><em>在這邊等紅綠燈的時候差點被煞車失靈的腳踏車撞，還好我站的比較後面沒撞到我，出門在外還是要小心。</em></strong> <em>⚠️⚠️⚠️</em></p>
</blockquote>

<p><img src="/assets/aacd5f5cacd1/1*J13Zlai29b-V7OR6-NSDuw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>到車站後同樣去劃位 Yakumo 14 指定席前往 安来(安來) (足立美術館)。</p>
<h4 id="1103-前往安来">11:03 前往安来</h4>

<p><img src="/assets/aacd5f5cacd1/1*-eYBNFeXAXeaT3i73YPeRg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*5JTFZNrBFGhSKo0M54Rwrg.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*49TFnjK8AnLDrYCA-naQtw.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>11:03 才發車，在車站附近買一些吃的在車上果腹。</p>

<blockquote>
  <p><strong><em>再見松江。</em></strong></p>
</blockquote>

<p><img src="/assets/aacd5f5cacd1/1*g-Sewe5VgTJrqioiNt32mw.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*gCedzvZrVNY7_hfPkBBtEg.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>鬆餅跟鹽味烤雞肉都很好吃。</p>
<h4 id="1112-抵達安來站">11:12 抵達安來站</h4>

<p><img src="/assets/aacd5f5cacd1/1*6VLLQilNbIvambZZ2FqlTg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*fzfeXO1wERdPoGfoSeImew.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>到安來站先別急著出站，找到觀光案內所可以寄放大型行李。
(車站外有自動置物櫃但多半都是滿的)</p>
<h4 id="1130-搭乘接駁車前往足立美術館">11:30 搭乘接駁車前往足立美術館</h4>

<p><img src="/assets/aacd5f5cacd1/1*BHs4S2OQTB2L5GC5beFFCQ.webp" alt="" loading="lazy" decoding="async" width="894" height="942" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4OTQiIGhlaWdodD0iOTQyIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><img src="/assets/aacd5f5cacd1/1*Ng6cROhDXC8dFa-jX9Rr9g.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*ZGpQ1IE2Rs3sdSVHF4FKKw.webp" alt="最新時刻表請參考官網" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><a href="https://www.adachi-museum.or.jp/en/shuttle-bus" target="_blank">最新時刻表請參考官網</a></p>

<p>行李安放好之後就去搭乘往足立美術館的接駁車(免費、排隊依序上車)，早上來的時候沒什麼人。</p>

<p>穿過鄉間小巷大約 20 分鐘抵達足立美術館。</p>

<p><img src="/assets/aacd5f5cacd1/1*yWU0ZbZ-BplOe9PpE7gLKw.webp" alt="" loading="lazy" decoding="async" width="931" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MzEiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*EzIv3NLDJE4hVA0BgtIQoQ.webp" alt="" loading="lazy" decoding="async" width="947" height="1217" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NDciIGhlaWdodD0iMTIxNyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*dKhsS-vXIfeBwam-mmARTQ.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>下車後一路往前走就是足立美術館入口。</p>

<p><img src="/assets/aacd5f5cacd1/1*pNf0-7iKNi1tn50chXc1-w.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*ROq_9K0FNF31iLA7f-A5Ng.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*Q_wkpK8esb5iKNWHxOf4vg.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>一樣直接使用 <a href="https://www.kkday.com/zh-tw/product/20307-jr-sanin-okayama-area-pass?cid=19365" target="_blank">周遊3日券</a> 免費兌換入場。</p>

<p>第一件事是在入口前櫃檯依照自己行程安排領取對應時間的「接駁車整理券」，一人一張；在時間到的時候回到下車的地方憑券搭乘回安來站。</p>

<p><img src="/assets/aacd5f5cacd1/1*mxWMQacl1bNSfdRo8X9JQA.webp" alt="" loading="lazy" decoding="async" width="923" height="1185" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MjMiIGhlaWdodD0iMTE4NSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>我的行程預計是搭乘 14:46 從安來出發前往鳥取的 JR，請注意 <strong>往鳥取的班次沒有很多⚠️。</strong></p>

<blockquote>
  <p><strong><em>很重要！很重要！很重要！一定要拿，不然不保證有位子能上車。⚠️⚠️⚠️</em></strong></p>
</blockquote>

<blockquote>
  <p><em>我就是那個不知道要拿，本來要搭 13:00 的，結果發現要拿券才回去拿，改搭 13:35 的車回安來。</em></p>
</blockquote>

<blockquote>
  <p><em>還好我時間抓得很寬鬆。</em></p>
</blockquote>

<h4 id="足立美術館--日本第一庭園">足立美術館 — 日本第一庭園</h4>

<p><img src="/assets/aacd5f5cacd1/1*P4YjMlS0gulw8Z9Jr4a2mA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*cIY8_Z-eP8OSvK-NBQ8Rtw.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*bBghMwu_IRFmgbT7fW3EXw.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>可以靜靜的坐在這邊享受庭院美景。</p>

<p><img src="/assets/aacd5f5cacd1/1*l_vmr2dgh14H2GQ3LV3t4Q.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*U6yP0ur5hbX5RPLBCUJhOA.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>除了庭院，足立美術館核心還是館藏的諸多藝術品，禁止拍照，一路跟著動線觀賞藝術品，跟庭院。</p>

<p><img src="/assets/aacd5f5cacd1/1*wBCdJ9HmdU55eywoII8eZQ.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*ftl17PV6R9Xvj6u_W2uTeg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>外面可看到部分的庭院景觀，可以感覺得到花費了很大的時間人力在維護這個造景。</p>

<p><img src="/assets/aacd5f5cacd1/1*jNTMLjG2TDFOdTE4Sl72DA.webp" alt="" loading="lazy" decoding="async" width="945" height="1234" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NDUiIGhlaWdodD0iMTIzNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*tcvXnD0yZU2J36q-7DFPPw.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>裡面有兩家喫茶室，除了甜點咖啡也有賣熟食；不妨在此駐足休息吃個東西看看景色。</p>

<blockquote>
  <p><em>因為我一個人來就沒有特別停留吃東西了。</em></p>
</blockquote>

<p><img src="/assets/aacd5f5cacd1/1*r-y6_oqYcCr7gHBkO2htAw.webp" alt="" loading="lazy" decoding="async" width="1400" height="980" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9Ijk4MCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*P2G-cBOftGDBhXjRe93JmA.webp" alt="" loading="lazy" decoding="async" width="1400" height="1009" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwMDkiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>在紀念品店買了幅看到的館藏畫作 <a href="https://artsandculture.google.com/asset/mt-fuji/gQGjCiwQOJy4bg?hl=zh-TW" target="_blank">横山大觀《神国日本》(Yokoyama Taikan “Mt.Fuji”) 畫作</a> 的紙板複製畫當紀念品。</p>
<h4 id="1230-在外面商店街晃晃">12:30 在外面商店街晃晃</h4>

<p>外面有一條小商店街，因為時間還很早就先在這亂晃。</p>

<p><img src="/assets/aacd5f5cacd1/1*F0w3N5TCBhJnHxDTPKvIlw.webp" alt="" loading="lazy" decoding="async" width="942" height="1182" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NDIiIGhlaWdodD0iMTE4MiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*vUCovPlzvYxsnyfTqahbdg.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<blockquote>
  <p><em>這個島根和牛米漢堡 <strong>蠻難吃的</strong> ，就是微波米漢堡飯黏黏的，我吃到的中間和牛還沒完全加熱冰冰的…</em></p>
</blockquote>

<p><img src="/assets/aacd5f5cacd1/1*04ZXVwciPcQrHZaCi_mP_A.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*IIi5iKRgO4Z3X-WVGXSAlg.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>如同前述，我一開始不知道要拿整理券，傻傻的就去等車，等到 13:00 發現不能上車，才去拿 13:35 的整理券。</p>

<p><img src="/assets/aacd5f5cacd1/1*_qEUfX9ZPhw4e7GoCkd1mA.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*CYcgaIGY63OyZ8AVqxp6zQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>拿完整理券又再入場(可再入場)坐在一開始的室內觀景區域放空看景等車。</p>
<h4 id="1355-回到安來站">13:55 回到安來站</h4>

<p><img src="/assets/aacd5f5cacd1/1*32LdARFSPE8UZX5Ncpp__Q.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*wiaNCbcWfMAr83dH_VyhVg.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>回安來站的路上有看到很紅的楓葉。</p>

<p><img src="/assets/aacd5f5cacd1/1*hmDP79nt4683WCrugG3F6A.webp" alt="" loading="lazy" decoding="async" width="1200" height="848" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg0OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>這時間前往足立美術館的人反而比較多。</p>

<p><img src="/assets/aacd5f5cacd1/1*pNI1d_ksSnexO0WKcSSLww.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*9O3RZpisQ42q9L5HBVfK2w.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>這站沒什麼人、時間也還很早， <a href="https://www.youtube.com/shorts/oG8xrxEC6mk" target="_blank">趁機錄了一段示範如何使用 JR Pass 預約指定席的影片</a> 。</p>

<p><img src="/assets/aacd5f5cacd1/1*09Yg8-nz6BzwsrB4MboUmg.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*hgL7isSy3JI2ynErDWL-bw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*xtWvh1YCoprT8eqqP_uxoQ.webp" alt="島根貓" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><a href="https://www.kankou-shimane.com/shimanekko/zh-TW/" target="_blank">島根貓</a></p>

<p>在觀光案內所亂逛消磨時間。</p>

<p><img src="/assets/aacd5f5cacd1/1*90geto8qdEZ4we55uwzlmw.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>安來這邊的清水寺感覺也是一個賞楓秘境。</p>

<p><img src="/assets/aacd5f5cacd1/1*ekgTftsMdymYawBX5WrRZw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*9UPTdrAVmNFfUTae3Z4jbQ.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<h4 id="1446-搭乘特急火車前往鳥取">14:46 搭乘特急火車前往鳥取</h4>

<p><img src="/assets/aacd5f5cacd1/1*9xXAaj9MkAymGU2AgeaMKg.webp" alt="" loading="lazy" decoding="async" width="687" height="1177" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2ODciIGhlaWdodD0iMTE3NyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*4QAm8AK5zXIGMVeIjRc8sg.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*2gCmHxDsPcbPLcngamFmpg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>車型很古老也只有兩節列車(一節自由座、一節指定席)，我的 26 寸行李箱放不上上面的行李層(新的 JR 可以但這車型的太小)，忘記可以走到最後一排放最後一排椅子後面，只能硬塞在自己的位子上。</p>

<p>還好一路隔壁都沒人，自由座車廂看起來人比較多，指定席這邊大概才坐 6 成而已。</p>

<blockquote>
  <p><em>因為行程安排就沒有特別去 鬼太郎車站(境港車站) 跟 柯南車站(由良車站)了。</em></p>
</blockquote>

<p><img src="/assets/aacd5f5cacd1/1*STYxvjU_ogrZ2fDlL9Y6ng.webp" alt="來源" loading="lazy" decoding="async" width="894" height="667" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4OTQiIGhlaWdodD0iNjY3Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><a href="https://www.pref.tottori.lg.jp/289054.htm" target="_blank">來源</a></p>

<p><img src="/assets/aacd5f5cacd1/1*hZSjEq5-ijzcPGGte6C40g.webp" alt="來源" loading="lazy" decoding="async" width="875" height="595" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4NzUiIGhlaWdodD0iNTk1Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><a href="https://www.pref.tottori.lg.jp/289055.htm" target="_blank">來源</a></p>

<blockquote>
  <p><em>有興趣的朋友可以多安排前往。</em></p>
</blockquote>

<h4 id="1558-抵達-jr-鳥取站">15:58 抵達 JR 鳥取站</h4>

<p><img src="/assets/aacd5f5cacd1/1*oPupIuWiEBmo4l8oYjdXEg.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*BkBOsOOK3AJtMY4Fh4nWlw.webp" alt="" loading="lazy" decoding="async" width="946" height="1195" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NDYiIGhlaWdodD0iMTE5NSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*kGq-zyfiHwjuwxWFtSTlnA.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>前往今晚的飯店 <a href="https://www.toyoko-inn.com/china/search/detail/00046/" target="_blank"><strong>東橫INN 鳥取站南口</strong></a> ，北口也有一間東橫 INN 沒訂到，北口那邊才是商店街比較熱鬧。</p>
<iframe class="embed-video" loading="lazy" src="https://www.youtube.com/embed/d8wVYHG4zkA" title="東橫INN 鳥取站南口" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
<h4 id="1700-鳥取亂逛">17:00 鳥取亂逛</h4>

<p><img src="/assets/aacd5f5cacd1/1*vHGHHoGQxbgvDqexHR0gLQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*k3EiMwp0DAViTzXwjie4pw.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>離吃飯時間還有一小時，先去附近的 AEON 逛逛。</p>

<p><img src="/assets/aacd5f5cacd1/1*4ZSUZ9tpUCmTPjwuSXtUBg.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*mOuFR0sVsVoQ1RxuPH3YlA.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>約莫快 18:00 一路走到鳥取北口商店街附近覓食，前幾天都亂吃，今天想找一間餐廳吃好一點。</p>

<p><img src="/assets/aacd5f5cacd1/1*UPSjd6gbL9TCVXl8qXLPvw.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*_Qkjzr6Xf_PV642XoYTcgw.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>找了一間在大街上評價也不錯的鳥取和牛餐廳 — 「 <a href="https://maps.app.goo.gl/pSZF8bCsiRNJ4aSt6" target="_blank">肉料理 Nick</a> 」品嚐看看鳥取和牛，不知道是不是平日關係只有我一個人。</p>

<blockquote>
  <p><em>鳥取和牛雖然沒有神戶牛、近江牛有名，但鳥取是 <strong>日本和牛文化的發源地</strong> 。</em></p>
</blockquote>

<p><img src="/assets/aacd5f5cacd1/1*4fDg5JJ-GGjiV8AVasNE3A.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*sI_cXfEuxYrVLS6SUqDArQ.webp" alt="" loading="lazy" decoding="async" width="782" height="1143" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3ODIiIGhlaWdodD0iMTE0MyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>我點的是「鳥取和牛ひつまぶし」鳥取和牛三吃飯 3,500 日圓，價格上比神戶牛便宜很多。</p>

<p><img src="/assets/aacd5f5cacd1/1*2jNeW3RO6WBh83rZqe0ZNA.webp" alt="" loading="lazy" decoding="async" width="1400" height="998" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9Ijk5OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*qkqZMeWtQcobrIOS8mrBJw.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>鳥取和牛很嫩，但是牛味較少，第一吃純吃和牛沒什麼味道。</p>

<p><img src="/assets/aacd5f5cacd1/1*jkud_7mVWN_F6ALBWCj14g.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>第二、三吃加上它附的湯變茶泡飯就很好吃。</p>
<h4 id="1830-吃飽回車站飯店">18:30 吃飽回車站、飯店</h4>

<p><img src="/assets/aacd5f5cacd1/1*OyxvrME3X2vnxLFRhB_IwQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*ZDMJBQeQx2jvWFpFmbhB9g.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>順路先去劃隔天到姬路的車票。</p>

<p><img src="/assets/aacd5f5cacd1/1*2v_BIl1Ho5wcJS0WYAuDGg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*21KCGclvNLUL2WojrkxJMA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*VJNLFfTTyZCRFH0kgSGL5w.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>順路買宵夜回飯店晚上吃，還有 Y1000 養樂多 1000，這次其實沒特別喝也都睡得不錯…</p>

<blockquote>
  <p><em>晚安鳥取。</em></p>
</blockquote>

<h3 id="day-4-1115-週五-白兔神社鳥取沙丘姬路">Day 4 (11/15 週五) 白兔神社、鳥取沙丘、姬路</h3>

<p>早上先去白兔神社(姻緣)參拜，回到鳥取站北口往公車總站走，穿過公車大廳到後面 4 號月台等公車。</p>

<p><img src="/assets/aacd5f5cacd1/1*XR70UqceYMl6CmDWivqTaQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*mpW7ne5REM1X5yfOAa5m4A.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*JzTpCld7REqa-Rqt4UpkgA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*KJUGKv9wIKoO7sDwEnTYIw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*z_BfpEdl3Z-O_QW6OfC4SA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>依照 Google Map 搭乘白兔海岸線鹿野營業所 41 前往 白兔神社前。</p>

<blockquote>
  <p><strong><em>鳥取的公車無法刷電子支付，只能付現金</em></strong></p>
</blockquote>

<blockquote>
  <p><strong><em>鳥取的公車無法刷電子支付，只能付現金</em></strong></p>
</blockquote>

<blockquote>
  <p><strong><em>鳥取的公車無法刷電子支付，只能付現金</em></strong></p>
</blockquote>

<blockquote>
  <p><strong><em>請先提前準備好零錢，或是仟元大鈔去司機旁邊換零錢。</em></strong> <em>⚠️⚠️⚠️</em></p>
</blockquote>

<blockquote>
  <p><em>車資約日圓 610 元。</em></p>
</blockquote>

<p>或先去鳥取車站買觀光一日交通券比較划算。</p>

<p><img src="/assets/aacd5f5cacd1/1*9cUeNishMRW8xDRs7-ri_Q.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*qHL6pCuXKRfxH86xFFs45g.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>下車前依照螢幕顯示金額投幣，我們是總站上車的無券所以看第一格前一格的金額付錢。</p>
<h4 id="-0930-抵達白兔神社">~= 09:30 抵達白兔神社</h4>

<p><img src="/assets/aacd5f5cacd1/1*40gg6SkBkYmveH13l0nvXQ.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*-PFzilV6gkDEpOvbXLbG_A.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*m33qZ2cJ4FwqBjkb-6IsAg.webp" alt="" loading="lazy" decoding="async" width="938" height="1194" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MzgiIGhlaWdodD0iMTE5NCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>下車後往回天橋方向走就是白兔神社入口。</p>

<blockquote>
  <p><em>白兔神社源於「因幡的白兔」傳說。傳說中，大國主命(出雲大社)在幫助受傷的白兔恢復健康後，獲得白兔的祝福，成功贏得八上比賣公主的愛情。</em></p>
</blockquote>

<blockquote>
  <p><em>白兔神社，象徵良緣與治癒，兩座神社也共同承載著祈求幸福與緣結的神聖意義。</em></p>
</blockquote>

<p><img src="/assets/aacd5f5cacd1/1*69iTu4F0VaHW07l992wjMA.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*OFRxkKGCLjl2SPMHKOlgcA.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*eUPpPJUou85qulUku9cDVA.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>走到神社的距離不遠但路上有很多兔兔神像，都堆滿了姻緣石。</p>

<blockquote>
  <p><em>出雲大社也有很多因幡白兔雕像只是我忘了拍。</em></p>
</blockquote>

<p><img src="/assets/aacd5f5cacd1/1*RKT2AGgUHkzeHVEP7OlkgA.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*1awKOYwzIPQ5BHVD0OgePQ.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>鳥居上也堆滿了姻緣石。</p>

<p><img src="/assets/aacd5f5cacd1/1*6JmuOYncQTNDnamDE8lhoA.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*KCuCyqwlHaza-Kz1r2J2Pw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>神社整體不大，大約 15–20 分鐘就可以參拜完。</p>

<p><img src="/assets/aacd5f5cacd1/1*E5qACETPs0VUTwxrsXGXlg.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*l2_nfwxbvPJW78TGZAQdng.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*_mqNm0k5ubWNxOFxlWaIuw.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>也花了 500 日圓買了姻緣石去雕像擺放。</p>

<p><img src="/assets/aacd5f5cacd1/1*fvYIjtTDJYlLzG5Zi9eJJg.webp" alt="" loading="lazy" decoding="async" width="1200" height="844" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg0NCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*NFlQA9p4TtNZTg6oEcFi-w.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><strong>我買的因幡之白兔：</strong></p>
<ul>
  <li>左邊：在出雲大社外的禮品店買的</li>
  <li>右邊：在白兔神社買的(有詩籤)</li>
</ul>

<iframe class="embed-video" loading="lazy" src="https://www.youtube.com/embed/HbxBcKkfCnk" title="一隻兔子爲何要招惹一群鯊魚？經歷過兩次的死而復生，他竟然迎娶了自己祖先的女兒！大國主神話|大國主|因幡之白兔|根之囯|日本神話|蘭爸爸說故事" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>

<p>因幡之白兔跟大國主神的故事。</p>
<h4 id="-0950-白兔神社外觀光案內所白兔海灘">~= 09:50 白兔神社外觀光案內所、白兔海灘</h4>

<p><img src="/assets/aacd5f5cacd1/1*OElc39nUjmOmxt1b2l_8vQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*y-ATn5pE43fRSQoNCQKHAA.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>觀光案內所裡真的有養一隻白色兔兔，也有賣一些吃的跟伴手禮，買了一個麵包果腹。</p>

<p><img src="/assets/aacd5f5cacd1/1*HHmrUUqcvXrDsCAvzyOkAw.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*bs7k1F-QNASY8qe7v9HFzQ.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*ayrC4DXTeXmXI1axNs29CA.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>白兔海灘很荒涼。</p>

<p><img src="/assets/aacd5f5cacd1/1*yGwYlF_RqRc9JJ6wMFmjug.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*rXW71tg5gJziOeVd5X3iRw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>公車要等到 10:45 才有往鳥取站方向的，覺得比較好的時間是更早來搭更早的車回去節省時間。</p>

<p><img src="/assets/aacd5f5cacd1/1*JfxN2JQq74dWK90Tsor_YQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*PuL80Noe3RS2fR-rkUF8vg.webp" alt="from: 圣炜" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>from: 圣炜</p>

<blockquote>
  <p><strong><em>上車記得拿整理券，上面有編號，下車依照編號對應金額硬幣連同整理券一同投入。</em></strong> <em>⚠️</em></p>
</blockquote>

<p>車上遇到一位中國福建來的觀光客；他的行程剛好跟我相反，他先去姬路再去島根；於是互相推薦了一下景點，他推薦姬路可以先去書寫山圓教寺然後姬路城可以去圖中的攝影點拍照。</p>

<blockquote>
  <p><strong><em>感謝他！最後去了覺得很值得。</em></strong></p>
</blockquote>

<p><img src="/assets/aacd5f5cacd1/1*ItWdhOq9YNwojhJt9l1pgA.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*ZRMhH7FMc8ypC3jQoBKqkA.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>在相生町下車，到對向公車站轉搭前往鳥取沙丘方向的公車。</p>

<p><img src="/assets/aacd5f5cacd1/1*3hf1z-tdcV_FVh_wW9JSKw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*UjpATzEo91FX4Ptdxtlb4g.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>搭到的這班要在沙丘東口下車走上去。</p>

<blockquote>
  <p><em>上車整理券拿到 3 號，下車看螢幕對應 3 號的金額投幣，以此為例就是要投 250 日圓。</em></p>
</blockquote>

<p><img src="/assets/aacd5f5cacd1/1*b3fTmvHBaj__OiMbjr5BpA.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*KGIqShdtrpqRYB31M253yQ.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*wiMKI8135q9TNJcrhuWiZQ.webp" alt="我混亂的參觀路線。" loading="lazy" decoding="async" width="1200" height="917" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkxNyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>我混亂的參觀路線。</p>

<p>下車後回頭岔路依照指標往上經過鳥取沙丘美術館繼續走到鳥取沙丘入口。</p>

<p><img src="/assets/aacd5f5cacd1/1*FOByNsR18dk-tExeF5VPUw.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*M1l7figNu-hmgZAbR_xjaw.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<blockquote>
  <p><em>鳥取沙丘禁止事項， <strong>尤其禁止在沙上作畫破壞自然</strong> 。⚠️</em></p>
</blockquote>

<p><img src="/assets/aacd5f5cacd1/1*UCu5KdaTZWThGBgaSompbg.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*87V9nbswSrqQfx3JcDcVYQ.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>鳥取沙丘真的有夠大，此時天空不作美有點陰雨，但如果是大太陽的話應該會很曬。</p>
<h4 id="1200-抵達鳥取沙丘">~=12:00 抵達鳥取沙丘</h4>

<p>日本第一沙丘。</p>

<p><img src="/assets/aacd5f5cacd1/1*WkiGJSHcUrPZi5h7zSJGYA.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*lwqbSawD2wG3wO1MyAf2Bg.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*NEuDeBgnNxtTSaYVQCd2NQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>人看起來都很渺小，一路走到沙丘脊上，有點抖而且沙子很鬆散不好走。</p>

<p><img src="/assets/aacd5f5cacd1/1*Gu98SJFPwWWKTj5PQR0zUw.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*S44sCcfn1C1zx3--fIIoBw.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*w0d4u78ensGTedfoTINN4g.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>走到最高點可以鳥瞰整個海岸線，背沙丘面海的場景只有這裡有。</p>

<p><img src="/assets/aacd5f5cacd1/1*B-Oqyt2XYx4TVTMblCdEdw.webp" alt="" loading="lazy" decoding="async" width="1400" height="1008" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwMDgiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*9ZoPtPjdHz3aaFTGNDT8iw.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>查網路資料好像可以騎駱駝但今天看起來是因為天氣不好所以沒有。</p>

<p><img src="/assets/aacd5f5cacd1/1*9WiZqF_alWi6M1PrHwG3sg.webp" alt="" loading="lazy" decoding="async" width="1400" height="994" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9Ijk5NCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*JxF65EE6eeACd1uRENVwNQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>最後離開鳥取沙丘再回頭看一眼，真的很巨大；鞋子裡不免也積了一些沙子， <strong>看有人直接套塑膠鞋套，很聰明。</strong></p>

<p><img src="/assets/aacd5f5cacd1/1*6cq3xUwokLToOCx38TXV9g.webp" alt="" loading="lazy" decoding="async" width="946" height="1199" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NDYiIGhlaWdodD0iMTE5OSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*OacuYCefS-wThnPRbo49xw.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>這邊可以直接搭乘纜車到鳥取沙丘展望台，我想說先去下面吃有名的鳥取第一沙丘布丁，就沒搭了；後來順路用走的上去也不遠(~= 15 分鐘而已)。</p>

<p><img src="/assets/aacd5f5cacd1/1*SkKPyRhs_REhQrWDiLluNQ.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*30ZIWteqj87esh4ptH2w7A.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*6LhlMpfWyszDfyJ7PTf1vQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>一路從停車場這邊出來，可以看到對面由隈研吾操刀的鳥取砂丘「Takahama Cafe 高濱咖啡廳」。</p>

<p><img src="/assets/aacd5f5cacd1/1*3OinDP-RDFHBQHLpY0olPw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*iGxKRD9KvMZrGuuHEjGNcA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*YA0J3gRmOPgWhacPYV0LEg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>買了一個嚐一嚐，味道真的不錯，布丁牛奶味濃郁口感像奶酪。</p>

<p><img src="/assets/aacd5f5cacd1/1*cRVlgCNRJysVUNkOkCHgFg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*TafJZGPlGd5gft3gYj8Q3A.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*sF3liqjkM6eQ6KURqwEfxA.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>特色是會給你一小包像沙子的砂糖，加下去一起吃。</p>

<p><img src="/assets/aacd5f5cacd1/1*qOmSScKgkmuIBhtPJKcbiA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*hv6d0AdzA-Ubx4XTqW8ZXQ.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*30_-rtFKE2Az1z6ikKIOSg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>這邊路上也有一些餐廳可以吃飯，繼續往回走有一條小路，往上走一小段右邊就是沙丘美術館第二入口，左邊再往上走一點就是剛剛纜車會到的地方「鳥取沙丘展望台」。</p>

<p><img src="/assets/aacd5f5cacd1/1*D9v4msXBz0mfsAWzXugtTw.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*N9shdulrUJo5pphjg4lKMw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>先往左邊去鳥取沙丘展望台看風景、吃東西，會先到纜車上下車處。</p>

<p><img src="/assets/aacd5f5cacd1/1*mo37aWPygCHfl-dAetnVQg.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>可以先到販賣及旁邊這邊拿刷子清理鞋子的沙子。(門口工作人員也會提醒)</p>

<p><img src="/assets/aacd5f5cacd1/1*gTZJLV-MjOhVf3kxsPHwmg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*lQX5yEbrhiEqUxf6ekViVw.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>最後也在這邊等公車回鳥取。</p>

<p><img src="/assets/aacd5f5cacd1/1*Re9IYuYqgtJre9oHgws_aA.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*GLQ1-BDnEqB25ZBp2X3BAA.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>展望台一二樓都是賣伴手禮跟有一個小餐廳，三樓可以上去眺望鳥取沙丘(雖然看不到什麼東西)。</p>

<p><img src="/assets/aacd5f5cacd1/1*fyEhTboh7RX-ZXqz9-FCPw.webp" alt="" loading="lazy" decoding="async" width="1200" height="868" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>裡面吃的選擇也不太多，要先去旁邊自動販賣機投幣買對應的套餐票券，直接入場找位子坐再把券交給工作人員即可。</p>

<p>隨便吃了個牡蠣套餐。</p>

<p><img src="/assets/aacd5f5cacd1/1*bN0RPnkejfCFjKjmcGwNCw.webp" alt="" loading="lazy" decoding="async" width="1071" height="873" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDcxIiBoZWlnaHQ9Ijg3MyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>也在這邊買了一些鳥取伴手禮。</p>
<h4 id="1330-沙丘美術館">13:30 沙丘美術館</h4>

<p><img src="/assets/aacd5f5cacd1/1*30_-rtFKE2Az1z6ikKIOSg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>吃飽往下走從二號入口往下逛沙丘美術館。(同樣用 <a href="https://www.kkday.com/zh-tw/product/20307-jr-sanin-okayama-area-pass?cid=19365" target="_blank"><strong>周遊3日券</strong></a> <strong>免費入場</strong> )</p>

<p><img src="/assets/aacd5f5cacd1/1*Y_ybpatDG6ffIfa3CDMm8g.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*OvdkGmr4yDdAyhW9XwMMaQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*BBkrd9MBZZdcAEwYz8gYxw.webp" alt="" loading="lazy" decoding="async" width="1200" height="843" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg0MyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*NALOYc1jrxf6O7nYgweBvg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1184" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjExODQiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>從二號入口開始參觀是從外面的沙雕開始參觀再一路從 3 樓 2 樓往下走，實際館內只有 2 樓有沙雕作品，場館不大、如果不是免費的話我可能不會進來。</p>

<p><img src="/assets/aacd5f5cacd1/1*bRQIVt04vcixxfHrHh2_yg.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*W01cTTu7FAgiW_zJRAeo-g.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*WZLGVTQPbJoyEK5-cZG7NQ.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*LiBlPyu3seaWbMkGm0eBnw.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*SnJ5-b_QTJ19ATurPLHEpw.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*rubP2uOF0-o0QdJsoBzSWg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>雖然場地小但是每個沙雕作品的細節都很精緻。</p>

<p><img src="/assets/aacd5f5cacd1/1*GRvwUBqcwGumVvkL8utiJw.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*lB74w9zvrdIiJBqpIIIT7g.webp" alt="" loading="lazy" decoding="async" width="686" height="881" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2ODYiIGhlaWdodD0iODgxIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>還有一個沙雕的沙丘美術館的 QR Code 可以掃描。</p>

<p><img src="/assets/aacd5f5cacd1/1*uH_T1z2iVu4fs6XPKwCstg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*K7XJXE5cRDMV7MYzrVybIQ.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*lZu_yUKJOB44L4wuG04uRA.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>逛完一圈又往上走回到剛剛的鳥取沙丘展望台，東晃西晃吃個鳥取梨冰淇淋大約 14:20 在站牌這邊等車，等 14:42 往鳥取站的公車。</p>

<blockquote>
  <p><em>這站回鳥取的公車來的時候就已經接近滿人了，勉強擠上車，如果有時間可能會找一下從上一站上車會比較好。</em></p>
</blockquote>

<h4 id="202501-update">2025/01 Update</h4>

<p>回來才看到鳥取因為交通比較不方便，所以有提供計程車包車一日遊， <a href="https://chugoku.letsgojp.com/archives/661034" target="_blank">詳情請參考遊客中心或此網站</a> 。</p>
<h4 id="1510-回到鳥取站">15:10 回到鳥取站</h4>

<p><img src="/assets/aacd5f5cacd1/1*zD1KRkIAiYp-wtxk48AogA.webp" alt="" loading="lazy" decoding="async" width="704" height="1176" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3MDQiIGhlaWdodD0iMTE3NiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>大約 15:10 回到鳥取站，此時開始下雨。</p>

<p><img src="/assets/aacd5f5cacd1/1*7Ex8903aKslRQc4IpfH6GA.webp" alt="" loading="lazy" decoding="async" width="949" height="1189" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NDkiIGhlaWdodD0iMTE4OSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*cfJLhRhuQjLhQqWfzVqgFA.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>在車站的商店亂晃，這邊的伴手禮店也有賣沙丘布丁！</p>

<p><img src="/assets/aacd5f5cacd1/1*HyBzvP9I1y2YC4KbxyLr0w.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*-Q6nYvGZP-PBIgMdW6Lk2A.webp" alt="" loading="lazy" decoding="async" width="637" height="432" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2MzciIGhlaWdodD0iNDMyIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>本來想去鳥取當地有名的 すなば珈琲（砂場咖啡），但下午三點多他們已經休息，就在車站內找了家 nana’s green tea 點個甜點＋咖啡休息充電(人跟手機都要充電)。</p>

<p><img src="/assets/aacd5f5cacd1/1*a6nZx2RTbeZAsTfxOvTPKw.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*UrMfxX9LNlDIR7suza8tCQ.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>店內舒適沒什麼人。</p>

<p><img src="/assets/aacd5f5cacd1/1*5os6rpDnUSjttVgCpigSRA.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*4RdqbhEiL0vrRwrMKnIXLw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>蕨餅＋冰淇淋＋熱咖啡，很滿足。</p>
<h4 id="1615-回飯店拿行李再回車站等車">16:15 回飯店拿行李再回車站等車</h4>

<p><img src="/assets/aacd5f5cacd1/1*UHCBHtR1kg15a4alJuXYlw.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*i5dxFrKSOdkjH9186Q9nQA.webp" alt="" loading="lazy" decoding="async" width="931" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MzEiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*zlhp1-kuQp679hHmnFJE-g.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>時間還很早，我們要搭 16:55 的特急超級白兔前往姬路。</p>

<p><img src="/assets/aacd5f5cacd1/1*8UmhqEYZA-9f5gzPNnhsiQ.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>等車期間有看到柯南列車。</p>
<h4 id="1655-上車前往姬路">16:55 上車前往姬路</h4>

<blockquote>
  <p><strong><em>再見山陰。</em></strong></p>
</blockquote>

<p><img src="/assets/aacd5f5cacd1/1*-Xupoj1iDwTdGF6ofS94Dw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*yVwbXNEx2C4NV80hjdj3uA.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*j3SvEKoIHa7lEXyq5_jcxA.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>劃到 Green Car 第一排座位，很舒適。</p>

<blockquote>
  <p><strong><em>行李的話因為車比較晃不敢放到上面，索性走到最後一排座位，橫放在最後一排座位的後面。</em></strong></p>
</blockquote>

<h4 id="1827-抵達姬路站">18:27 抵達姬路站</h4>

<p><img src="/assets/aacd5f5cacd1/1*3jfwcI6yLbuWuWzDM4mC8A.webp" alt="" loading="lazy" decoding="async" width="708" height="1190" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3MDgiIGhlaWdodD0iMTE5MCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*OdglmUjNMHHemWGLuppg3g.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*XbSKEhcwPLxItxqUXd6CVg.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>剛好遇到他們的議員選舉，站外有議員在造勢演講，人很多。</p>

<p><img src="/assets/aacd5f5cacd1/1*9HmKh497akO8a6922Z7kLA.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*uBit7SVmrOOBDLSPv9IEGw.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>出站後直接去飯店 <a href="https://www.toyoko-inn.com/china/search/detail/00317/" target="_blank"><strong>東橫INN 姬路站新幹線北口</strong></a> ，最後三晚都住在這裡，北口這邊的東橫 INN 比較熱鬧；地圖上看很近，實際走起來體感有點距離，大約要 10 分鐘左右。</p>

<p><img src="/assets/aacd5f5cacd1/1*VT_z6TemLvMX-ujCfUewiQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>分配到 14 樓頂樓的房間，窗外視野很好。</p>
<iframe class="embed-video" loading="lazy" src="https://www.youtube.com/embed/fAnO4AoRX-Q" title="東橫INN 姬路站新幹線北口" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
<iframe class="embed-video" loading="lazy" src="https://www.youtube.com/embed/xl9eOyv4A-I" title="東橫INN 姬路站新幹線北口 14 樓窗外鐵路景觀" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>

<p>可以直接從窗外回頭看姬路站鐵路，火車經過也會有輕微的軌道聲，但還好晚上之後就沒火車了。</p>
<h4 id="1900-出去亂晃覓食">19:00 出去亂晃覓食</h4>

<p>一出來看到姬路城夜景覺得很美，不知道發什麼神經想走到姬路城拍夜景，太接近反而不好取景沒拍什麼照片就走，一路又冷又餓，應該直接去吃飯的。</p>

<p><img src="/assets/aacd5f5cacd1/1*dy69JBLXC40kKDIYB3adGw.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*dyjiO-KVxTv99UskMZs9Ag.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>姬路城距離車站約 1 公里，來回走了快 30 分鐘。</p>

<p><img src="/assets/aacd5f5cacd1/1*hEbq2RVL18Yscc-y9ozE8g.webp" alt="https://himeji-kyoukasuigetsu.com/en/" loading="lazy" decoding="async" width="1400" height="1040" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNDAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><a href="https://himeji-kyoukasuigetsu.com/en/" target="_blank">https://himeji-kyoukasuigetsu.com/en/</a></p>

<p><img src="/assets/aacd5f5cacd1/1*KLFCGcyq_F3ZVnM4w_GK2w.webp" alt="https://himeji-kyoukasuigetsu.com/en/" loading="lazy" decoding="async" width="1042" height="1059" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDQyIiBoZWlnaHQ9IjEwNTkiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><a href="https://himeji-kyoukasuigetsu.com/en/" target="_blank">https://himeji-kyoukasuigetsu.com/en/</a></p>

<blockquote>
  <p><strong><em>可惜這次錯過了姬路城冬季點燈活動，感覺很漂亮浪漫，之後有機會再來一次吧。</em></strong></p>
</blockquote>

<blockquote>
  <p><strong><em>活動時間 2024年11月22日（五）才開始，一路到 2025年2月23日（日）。</em></strong></p>
</blockquote>

<p><img src="/assets/aacd5f5cacd1/1*tnvYT7YGcklotJdPdfpY8w.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>最後發現車站北口二樓這個看台拍姬路城就是最佳位子。</p>

<p><img src="/assets/aacd5f5cacd1/1*dRxfkrBM2x_gMgusOlnZ5A.webp" alt="" loading="lazy" decoding="async" width="1036" height="861" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDM2IiBoZWlnaHQ9Ijg2MSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*zCHRn2ZG88rg_a7WnfXz1g.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*Pqy-Rk3t-bA36QHoaNrTPg.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>買晚餐前先在姬路站的百貨公司 <a href="https://www.jrw-urban.co.jp/piole-himeji/tw" target="_blank">Piole Himeji</a> 稍微逛了一下跟扭扭蛋。</p>
<h4 id="2000-買晚餐回去">~=20:00 買晚餐回去</h4>

<p><img src="/assets/aacd5f5cacd1/1*8hgCoDK6cgGQJT4re9WRAA.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*piFnI5vcIs2l8lG5gQZRdg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>從二樓空橋一路走到隔壁的 TERASSO 找到一家還沒關門的燒肉店外帶燒肉便當回飯店吃。</p>

<p><img src="/assets/aacd5f5cacd1/1*PfaI0llzUMpwdqEzyz5iPg.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*vGLx0HKICVp2XggRVuXvQQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*V_AQ9WNRUuMFogArfhYn_g.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>途經超商同樣買了些吃的喝的。</p>

<p><img src="/assets/aacd5f5cacd1/1*VfGo_hbbLnLFXz2wdnLF4A.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*x3_zZ9A0QkjHdWDNkjuQqg.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*1nvfzK3jA2eVr-tTRIAUBw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>吃飽喝足洗完澡下樓洗個衣服，這次知道東橫要自備洗劑，先投幣買洗衣粉連同衣服投入洗衣機；這間的烘衣機很新很高級，還可以選溫度。</p>

<p><img src="/assets/aacd5f5cacd1/1*1b9IFBlF0z6jwwChXs1zPg.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<blockquote>
  <p><strong><em>晚安姬路。</em></strong></p>
</blockquote>

<h3 id="day-5-1116-週六-書寫山圓教寺姬路城姬路周邊逛街">Day 5 (11/16 週六) 書寫山圓教寺、姬路城、姬路周邊逛街</h3>
<ul>
  <li>KKday 推薦： <a href="https://www.kkday.com/zh-tw/product/148856?cid=19365" target="_blank">兵庫神戶一日遊｜姬路城＆家老屋敷跡公園＆有馬溫泉＆六甲山｜可選豪華天婦羅套餐 （大阪出發）</a></li>
</ul>

<p><img src="/assets/aacd5f5cacd1/1*EqvQAU91nnYsl8w7vT7CTg.webp" alt="" loading="lazy" decoding="async" width="1200" height="851" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg1MSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>每家東橫 INN 都有提供免費早餐，但我都為了趕行程早上幾乎都自己隨便吃個前一晚買的超商麵包跟咖啡就出門了。</p>
<h4 id="0845-出門">08:45 出門</h4>

<p><img src="/assets/aacd5f5cacd1/1*HIa6B4yuh-MjFWMeBs97IQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>早上 8:45 出飯店穿過空無一人的商店街前往車站。</p>
<h4 id="0855-搭公車前往書寫山圓教寺">08:55 搭公車前往書寫山圓教寺</h4>

<p><img src="/assets/aacd5f5cacd1/1*Z3_zOdcbly3jEyXyN8RKWA.webp" alt="" loading="lazy" decoding="async" width="918" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MTgiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*3KNztIIzy_Yw85kEm5ai-Q.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>姬路車站有好幾個月台，Google Map 沒寫在哪個月台跟搭幾號公車，只能現場看指標，在 10 號月台搭乘 10 號 日 姫路駅北〜書写山ロープウェイ 公車。</p>

<blockquote>
  <p><em>書寫山圓教寺是電影末代武士的取景地點：</em></p>
</blockquote>

<p><img src="/assets/aacd5f5cacd1/1*ElCz1QpeR3XaKMbYyPvuPQ.webp" alt="https://visit-himeji.com/zh-hant/trip-ideas/featured-in-the-last-samurai-shoshazan-engyoji-temple-in-himeji/" loading="lazy" decoding="async" width="1069" height="973" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDY5IiBoZWlnaHQ9Ijk3MyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><a href="https://visit-himeji.com/zh-hant/trip-ideas/featured-in-the-last-samurai-shoshazan-engyoji-temple-in-himeji/" target="_blank">https://visit-himeji.com/zh-hant/trip-ideas/featured-in-the-last-samurai-shoshazan-engyoji-temple-in-himeji/</a></p>
<h4 id="0920-抵達書寫山纜車搭乘處">~=09:20 抵達書寫山纜車搭乘處</h4>

<p><img src="/assets/aacd5f5cacd1/1*a7E1GJ36evNeg3HIpTajiA.webp" alt="" loading="lazy" decoding="async" width="1395" height="1049" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMzk1IiBoZWlnaHQ9IjEwNDkiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*deyU47Fxzh-iOMa3Cm6RMg.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*d-BdwUp1HRFSSl_Zu16cDQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>下車後就是纜車搭乘處，在自動售票機買完來回票就能排隊等纜車了。(15 分鐘一班)</p>

<p><img src="/assets/aacd5f5cacd1/1*Q96ZZxOE04V57QXJcsL2iA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>書寫山這邊 11/15–17 有夜間特別開放活動，開到晚上 20:00，纜車時間也延長到 20:30。</p>
<h4 id="0930-搭乘纜車上山">09:30 搭乘纜車上山</h4>

<p><img src="/assets/aacd5f5cacd1/1*-t6iFYeu1txyQ6-B9pZdCQ.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*2MnYBWj9005tN57I3IRrXQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<h4 id="0935-抵達書寫山圓教寺入口">09:35 抵達書寫山圓教寺入口</h4>

<p><img src="/assets/aacd5f5cacd1/1*4AYY1gP1Kdb589JwBHaFuw.webp" alt="" loading="lazy" decoding="async" width="1400" height="1010" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwMTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>需要買門票入場，那時候沒仔細看只買了 <strong>一般的入山志納金，無法乘坐來回交通車，要用走的上山</strong> 。⚠️⚠️⚠️</p>

<blockquote>
  <p><em>坐車約：5 分鐘</em></p>
</blockquote>

<blockquote>
  <p><em>爬山約：20 分鐘 (1 KM)</em></p>
</blockquote>

<blockquote>
  <p><strong><em>讓我再選的話我選擇多 500 日圓節省時間體力。</em></strong></p>
</blockquote>

<p><img src="/assets/aacd5f5cacd1/1*p4QIb3vPtJivhpdoDQ6GAQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*7e4WBDn1nRnJYdz5B_hYOA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*ewt3SGQwn6WyhRV7Am0_MA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>不過還好是一大早體力精神還很充沛，坡度平緩就一路走走賞楓上山了。</p>

<p><img src="/assets/aacd5f5cacd1/1*AS55vHl96nZYuwqjRa93ZA.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*T8xwRPwgXPqr5nZ5LXUq2w.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*W-yWWLLv0UJlQ2QU10Q3Vg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*fcCW8202ZiA2OcPgUm-gKw.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>穿過仁王門大概還有 400 公尺。</p>
<h4 id="0955-抵達摩尼殿">09:55 抵達摩尼殿</h4>

<p><img src="/assets/aacd5f5cacd1/1*ceS4fQCizXrx1avWg9Y3yw.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*80TxXA5vgZrLaPnSGHQVlQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*6m7mDkJHyCYezj5AlTKznA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*fLzPG1W3E1eJ4_pEYPKepQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*AxTqVqv_JomxzIlBE_HbSA.webp" alt="" loading="lazy" decoding="async" width="965" height="1232" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NjUiIGhlaWdodD0iMTIzMiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*5nc-fnNgZHhIwlJhTcYKjg.webp" alt="" loading="lazy" decoding="async" width="1400" height="899" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9Ijg5OSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>書寫山圓教寺幅員遼闊，全部走完我覺得至少要 2 小時起跳。</p>

<p><img src="/assets/aacd5f5cacd1/1*U4wMNwdurbeaNEOqUKkC9Q.webp" alt="" loading="lazy" decoding="async" width="1400" height="529" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjUyOSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>我只參拜了摩尼殿跟大講堂就折返下山了。</p>

<p><img src="/assets/aacd5f5cacd1/1*h70I3V6EU2UIea8JaVs_jw.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*cQEiVSO01SrHGYuhQb8Y6A.webp" alt="" loading="lazy" decoding="async" width="946" height="1234" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NDYiIGhlaWdodD0iMTIzNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*IyDpkfR-0QmrIiNwGg0aJA.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>摩尼殿可以脫鞋走進去參拜，也可以從這邊鳥瞰楓葉。</p>

<p><img src="/assets/aacd5f5cacd1/1*BbDp9dGeoep-40MgJeDiOQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*fImTadRZFNlut58qayxvhQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>很清幽安靜，但今年的楓葉較晚只有零星幾株轉紅。</p>

<p><img src="/assets/aacd5f5cacd1/1*mVNbxhCrjF5jWx5MXgRNWA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*9j_zrDZmNITjHHmA56PmCQ.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*mbYDfRbjjG6Z5_w8mI--EQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>從殿後小路往前走前往大講堂。</p>

<p><img src="/assets/aacd5f5cacd1/1*hsCWaBEKnGp5QMX_YaF4Gw.webp" alt="" loading="lazy" decoding="async" width="1200" height="870" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg3MCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*0yUCPUF_oEy4manAS8kJ0A.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*gjTjoLDLZC6EeAP3Gcnbdw.webp" alt="" loading="lazy" decoding="async" width="960" height="1244" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NjAiIGhlaWdodD0iMTI0NCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>大講堂有一個 <a href="https://x.com/kugikumo" target="_blank">圓教寺 x 隈研吾</a> 裝置藝術，展出到 12/1。</p>

<p><img src="/assets/aacd5f5cacd1/1*4m561KwGCbbgXnrrYERkJw.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>大講堂旁的楓葉很紅。</p>

<blockquote>
  <p><em>到大講堂的時候剛好在誦經，也可以脫鞋入內參拜，日本的僧侶誦經方式跟台灣也不太一樣，會把經文整本拉開然後邊詠誦最後再合起來拍一下。</em></p>
</blockquote>

<p><img src="/assets/aacd5f5cacd1/1*V_0ogPmKoPiQ9-nuUfxvlw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*NKB_Hzykps-3rC3HHrxHIg.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>旁邊的食堂也能進去參觀，有很多歷史文物陳列。</p>

<p>所有建築都是古蹟(建於1282 年)，走在上面有一種穿越歷史的感覺，整體建築保存維護的很好。</p>

<p><img src="/assets/aacd5f5cacd1/1*T4ybcdOD3Fr9qJPRvKyjvA.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>從食堂二樓看大講堂。</p>

<p><img src="/assets/aacd5f5cacd1/1*kwc7TD6kOkAMUEGCyQZyHw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>參觀完大講堂後折返回摩尼殿，在摩尼殿外的商店買了一個現烤紅豆麻糬補充體力。(現烤的好吃，皮脆脆)</p>

<p><img src="/assets/aacd5f5cacd1/1*3vuyM_hvr2vqmMigtjrJfQ.webp" alt="Have a safe trip home." loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>Have a safe trip home.</p>

<p>吃完後就再花 20 分鐘走路下山回到志納所入口。</p>
<h4 id="1055-搭乘纜車下山">~=10:55 搭乘纜車下山</h4>

<p><img src="/assets/aacd5f5cacd1/1*HYi218ukiOp87of9sIPW7g.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*MxZa66WDA3XJLESJ6bTAHA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*CPsPU2ed-0Dr7Xk1yyszAw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<h4 id="1110-回姬路市區">11:10 回姬路市區</h4>

<p><img src="/assets/aacd5f5cacd1/1*--Rj51jGDSdIKsruPXX5OQ.webp" alt="" loading="lazy" decoding="async" width="938" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MzgiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*mLKgafi4UWM-lqsxIxnTHg.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>下山走回來的時候的公車站，大約 11:05，等 11:10 公車回姬路市區。</p>
<h4 id="-1135-抵達-姬路城">~= 11:35 抵達 姬路城</h4>

<p><img src="/assets/aacd5f5cacd1/1*rVlB-YbFKOoCB_4vUzk50w.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*kxFG8jVc9-4cfH5uTVpbsQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>公車下車後往前走一點就是姬路城遊船，不過天氣陰陰快下雨就沒去了。</p>

<p><img src="/assets/aacd5f5cacd1/1*ReisZ-PRti3sK13ozeceDA.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*OwoTR1-C35HhJiv-rq8o2w.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>走進姬路城，門口這個橋景很好拍照！</p>

<p><img src="/assets/aacd5f5cacd1/1*KmgaWgD9UXFA3R6UGw8QoQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*NISOgxwwrowWgi9jHz_qWw.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>姬路城前的三之丸廣場很大，從這邊還要再走 5 分鐘才會到城入口。</p>

<p><img src="/assets/aacd5f5cacd1/1*TczWjw__6r_H15vgUwkcUQ.webp" alt="" loading="lazy" decoding="async" width="726" height="660" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3MjYiIGhlaWdodD0iNjYwIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>想說先去找昨天路人推薦的姬路城拍攝點，拍攝點在跟入城口相反的方向，在右邊的「姬路市動物園」裡面。</p>

<p><img src="/assets/aacd5f5cacd1/1*NAGnOqG4plLpo1AVEtwCgg.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*UzJF2eGGGmIhgK_a3xANAA.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*Ms3Z4aqqUSa2RWA35dGc1g.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*GmIPAY57WWuEBRXl6tmcBw.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>看宣傳進入右手邊有卡皮巴拉，但可能因為太冷沒出來；進去後直接直走過橋看到摩天輪後右轉，在他後面有一塊空地就是拍攝點。</p>

<p><img src="/assets/aacd5f5cacd1/1*Rupw705abK9nx8m8x0MPdw.webp" alt="https://youtube.com/shorts/XDquPOanhpQ" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><a href="https://youtube.com/shorts/XDquPOanhpQ" target="_blank">https://youtube.com/shorts/XDquPOanhpQ</a></p>

<p>這個拍攝點的取景很好，可惜沒有櫻花或楓葉陪襯。</p>

<p><img src="/assets/aacd5f5cacd1/1*84kPNVhjXpUqyGMCbSgE1Q.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*JPyc8SBiGKtcrh9r31l3iQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*xUkRrlHFLbKJWKjnVAw59A.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<blockquote>
  <p><em>拍完後大概繞了一下這個動物園，設施非常老舊(遊樂設施另外付費)，動物空間狹小、 <strong>很多看起來都很憂鬱、出現刻板行為，不忍直視</strong> ，逛一下就離開了。</em></p>
</blockquote>

<p><img src="/assets/aacd5f5cacd1/1*Ua__vVJVs-uFK2f4yD6erA.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*pNyzUtrYYyKahNitfuhxBg.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*KEQvJI90xEVXc4r4GQ0wew.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>拍完姬路城走回左側城入口參觀城內。</p>

<p><img src="/assets/aacd5f5cacd1/1*NDwQbWnS0PRC8k90t6MIcw.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*h0xLPH3EGTMSljU26o5GLw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*wVG-5blJurZ0maq8rx5IDg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>買完票順著參觀路線一路進到城內。</p>

<p><img src="/assets/aacd5f5cacd1/1*j-yJn4Drf_XR8nZn9aO19w.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*6NcZZitF85yrZIt8J6kSmw.webp" alt="" loading="lazy" decoding="async" width="928" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MjgiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>進入室內後同樣要脫鞋，會發一個塑膠袋裝鞋子。</p>

<p><img src="/assets/aacd5f5cacd1/1*LD55vnyY6e_RUQpxAhenDg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*dRqPqCQhSezJ5wsttSJeVg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*zl-BV1uAW_u3qxG-KR4wLQ.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>從二階往回看姬路站，一共有六階越往上越小、樓梯越陡峭，要排隊上樓。</p>

<blockquote>
  <p><em>此時已接近中午 12:30，飢腸轆轆加上今天出門忘了帶行動電源，手機已快沒電加上排隊人潮眾多，就沒再繼續往上爬了，在二階這裡折返出城。</em></p>
</blockquote>

<p><img src="/assets/aacd5f5cacd1/1*yjWyIFXF4Adh-MEwmBLpsA.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*zcaGTdjsKLn3hEBR92zb4g.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*Zohjzd5XZTXDuf-IkRpI8Q.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>出城後一路走回三之丸廣場然後走出來外面。</p>

<p><img src="/assets/aacd5f5cacd1/1*Q-BpgNDNoWH4qo3mxZFA5A.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*JmXBE3gSYq898CG_1y-rLA.webp" alt="Kushiyaki Kobe beef" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><a href="https://maps.app.goo.gl/3ANcmi6uR1wnDgqYA" target="_blank">Kushiyaki Kobe beef</a></p>

<p>在姬路城外面商店街找了一家有賣神戶牛的店，外帶了神戶牛丼飯跟串燒回飯店休息充電。</p>
<h4 id="1320-回飯店休息">13:20 回飯店休息</h4>

<p>天候不佳＋早上爬山＋手機沒電＝回飯店休息。</p>

<p><img src="/assets/aacd5f5cacd1/1*Sp_2bL6TiDs-FTIT1hf4cg.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*0BxN4BJO9GZ3jCHQr7_qpg.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>除了份量有點小之外，這家的神戶牛味道還不錯。</p>
<h4 id="1530-姬路站附近逛街">15:30 姬路站附近逛街</h4>

<p>大約休息到 15:30 又重新出門。</p>

<p><img src="/assets/aacd5f5cacd1/1*VoC5czZ4ep3Pej07BaIl8g.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*bU2RJeIL3HZ-eIE5aGQ0-A.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*1yvJy0uUoWXpna40MC5ZTg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>姬路站這邊意外的沒什麼好逛的，只有車站這邊的 Piole 跟 SANYO 山陽電車那棟的百貨公司、周圍一條商店街，沒有唐吉訶德、麥當勞，Lawson 也只有一家、藥妝店也只有一家松本清。</p>

<p><img src="/assets/aacd5f5cacd1/1*EtwTImtOX4SlPrRo6CKBYg.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*H_cAfQPJP8QXU_7MPgzYyA.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>Piole 三樓的 Kiddy Land 有一些吉伊卡哇、這裡沒什麼人也不用排隊，帶了三隻寶寶回家，烏薩奇沒有買到睡衣版本的QQ。</p>

<p><img src="/assets/aacd5f5cacd1/1*kG_MA7MnFH9QHo2YPKbofw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*DZIjEJdNFvBHGIdphvJ8Kw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*AGyjVuNTXv_gO9DfdSKAIg.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>商店街亂逛，找扭蛋行程。</p>

<p><img src="/assets/aacd5f5cacd1/1*61f9uD9NUYRW1QumaFpNLA.webp" alt="" loading="lazy" decoding="async" width="1400" height="961" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9Ijk2MSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*wxoORyKqaWXDmUt9lk9vRg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1016" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwMTYiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*YtH0na_odN_spz7_I1o6cQ.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*yNe-JTCDJuloVlg7xdtGwA.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>雖然剛說沒什麼好逛，但至少扭蛋店還是有也有安利美特。</p>

<p><img src="/assets/aacd5f5cacd1/1*o5h-XaDa6kZ6kjXjPAq7Rw.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*9u7HGC1YdSWWcS_1_iqMzw.webp" alt="" loading="lazy" decoding="async" width="695" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2OTUiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>接近傍晚時分開始下大雨，還好今天本來就無特別行程安排。</p>
<h4 id="1800-吃晚餐">~=18:00 吃晚餐</h4>

<p><img src="/assets/aacd5f5cacd1/1*UJqc0DWngnbESTHo_PybYw.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*IJ8XIPnSJdaAx-6OkmCoJg.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*66Zxcn9rbOdkL3GqjV_cNA.webp" alt="おだしとワインとお料理と。motto" loading="lazy" decoding="async" width="602" height="1306" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2MDIiIGhlaWdodD0iMTMwNiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><a href="https://maps.app.goo.gl/arsJs8CYEW3kAdg46" target="_blank">おだしとワインとお料理と。motto</a></p>

<p>在商店街隨便找了一家有神戶牛的店當晚餐。</p>

<p><img src="/assets/aacd5f5cacd1/1*5Cc0K6Xd5p0FMJbxhB4gOg.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>這家的特色是有一個前菜招財貓甜點，不會太甜還不錯。</p>

<p><img src="/assets/aacd5f5cacd1/1*1b3oeJs9c2rjOL-TaleOQg.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*6ijkG1uNzojnZlIkRc2fMA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*i_EvfGqTY-fQQdXlK7aBjA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>點了神戶牛排跟茶泡飯還有生啤。</p>

<p>這家的神戶牛排我覺得普普通通，不太好咬也吃不太出神戶牛的味道，茶泡飯倒是蠻好吃的。</p>

<p><img src="/assets/aacd5f5cacd1/1*Z0oLUni88-W6TPpVm2gF6g.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*q_ek8sUX3nM5xCBTPkXa6Q.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>吃飽再亂逛一下，雨後的姬路。</p>
<h4 id="2000-回飯店休息看台日大戰">~=20:00 回飯店休息＆看台日大戰</h4>

<p><img src="/assets/aacd5f5cacd1/1*j17lqXN-p1caPqDaC6z-rA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*oBZH4_-Pb7difuUCdwvSBw.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>日本視角、轉播內容完全聽不懂，但日本的轉播資訊很完整，左邊還有棒球知識補充，右邊有完整的球路分析。</p>

<p><img src="/assets/aacd5f5cacd1/1*4wJ-WWxbgYAeGgcf-KPtyQ.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>最後這場是日本勝。</p>

<blockquote>
  <p><em>晚安姬路。</em></p>
</blockquote>

<h3 id="day-6-1117週日-大阪神戶">Day 6 (11/17週日) 大阪、神戶</h3>

<p>本來想衝 鳴門旋渦、明石海峽大橋 但是天候依然不佳，所以改去大阪神戶逛街。</p>
<h4 id="-0845-搭乘新幹線自由座前往新大阪">~= 08:45 搭乘新幹線自由座前往新大阪</h4>

<p><img src="/assets/aacd5f5cacd1/1*T5blo2Kdt0KsYaItd6H5dA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*RvSje4c8thVxGXJpBZwnTA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*gGkwpzey7E9tlg0gbbV35g.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>搭乘新幹線到新大阪(25 分鐘)再轉乘御堂筋線到大阪的時間跟搭一般的直接到大阪差不多、新幹線的班次很多，所以選擇搭比較舒適又可以充電的新幹線。</p>
<h4 id="917-抵達新大阪">9:17 抵達新大阪</h4>

<p><img src="/assets/aacd5f5cacd1/1*apUd_VYqJJ_mM_Kr_R3YnQ.webp" alt="" loading="lazy" decoding="async" width="1200" height="765" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijc2NSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*6ptZt3XbRNnIK1Ph62j9eg.webp" alt="" loading="lazy" decoding="async" width="553" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1NTMiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>到新大阪後還要走一下到御堂筋線。</p>

<p>先去 <a href="/posts/z-度旅行遊記/京阪神自由行攻略-京都大阪神戶8日遊全紀錄與實用交通住宿指南-76d66c2e34af/">去年來京阪神</a> 沒去到的難波八阪神社。</p>

<p><img src="/assets/aacd5f5cacd1/1*vWqBuGY0nf4NQY1pw5Pb7w.webp" alt="" loading="lazy" decoding="async" width="882" height="1095" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4ODIiIGhlaWdodD0iMTA5NSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*uCKGMuqTmPVL61gvJU_AKg.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>搭到難波站出來再走一小段在某個巷子內就是神社。</p>

<blockquote>
  <p><em>才發現大阪也在測試人臉辨識進出站了。</em></p>
</blockquote>

<h4 id="950-抵達難波八阪神社">~=9:50 抵達難波八阪神社</h4>

<p><img src="/assets/aacd5f5cacd1/1*DCyi2vXA6WzEvhxJ9Qx6Uw.webp" alt="" loading="lazy" decoding="async" width="866" height="1107" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4NjYiIGhlaWdodD0iMTEwNyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*qu1yc41lJWfbKvt2iiI8Og.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*eENL5QrW5I0g_Bb3Iqlc6Q.webp" alt="" loading="lazy" decoding="async" width="1200" height="873" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg3MyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>神社不大，觀光客很多，到此一遊拍幾張照片就離開了。</p>

<p><img src="/assets/aacd5f5cacd1/1*Ms6lilqCGKSJEQY-2Ad_NQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*1YnEjMoc8KV-OaV08XrMcA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>離開後繼續走到下一站「大國町」搭一站到「動物園前」下車，前往通天閣。</p>

<p><img src="/assets/aacd5f5cacd1/1*bxBxGhqonhPE-kNU7jl5DA.webp" alt="" loading="lazy" decoding="async" width="877" height="1071" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4NzciIGhlaWdodD0iMTA3MSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*_ZGMWno1bnS5VIsevjVuGw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>出站穿過地下道、商店街繼續往前走就會看到通天閣塔。</p>

<blockquote>
  <p><em>這邊感覺治安蠻亂的，一出站過地下道有很多遊民，還有遊民在吵架很大聲，有點可怕。</em></p>
</blockquote>

<p><img src="/assets/aacd5f5cacd1/1*WOzCL0xA6hirl6Ru3iYxlg.webp" alt="" loading="lazy" decoding="async" width="859" height="1130" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4NTkiIGhlaWdodD0iMTEzMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*xWz0KkdvI5042L31ritmjw.webp" alt="" loading="lazy" decoding="async" width="1200" height="852" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg1MiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>商店街巷子狹窄左右都有吃的，有幾家感覺是排隊名店、很多人。</p>

<p><img src="/assets/aacd5f5cacd1/1*-cCMXQtTVn7ExtRxRLyOcA.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*89McCOa7Itm_L97WibLASw.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*GiVI9kQjjJLKp5E0BYq__Q.webp" alt="" loading="lazy" decoding="async" width="875" height="1093" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4NzUiIGhlaWdodD0iMTA5MyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>約 10:30 抵達通天閣下，準備上去的人大排長龍，我直接放棄。</p>

<p><img src="/assets/aacd5f5cacd1/1*oRVH7x_G3Ga19FD04YQCIg.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>走回地鐵站回大阪。</p>
<h4 id="1100-回到大阪梅田">~=11:00 回到大阪梅田</h4>

<p><img src="/assets/aacd5f5cacd1/1*A97lYcYLqPya_YJp3UFQxw.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*kbWzjvQnQicbyGhGx7qLAw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*i-1TYGeHu_AxlzRaluBkGw.webp" alt="" loading="lazy" decoding="async" width="874" height="1099" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4NzQiIGhlaWdodD0iMTA5OSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>出站先去 LINKS 百貨，五樓可以直通 Yodobashi 扭蛋玩具區。</p>

<p><img src="/assets/aacd5f5cacd1/1*LHUFlm9UQQto7rkwZ4oJ6A.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*4BeiVIybWx9ixjgSA3Kp6A.webp" alt="" loading="lazy" decoding="async" width="1400" height="977" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9Ijk3NyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*RafKwlM7JvNBLvYwtTFhrA.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>逛完 LINKS 穿過大阪車站、樓上的時空廣場，直達大阪大丸百貨。</p>

<p><img src="/assets/aacd5f5cacd1/1*a02vHVVx_CBehnuTqyh9jA.webp" alt="" loading="lazy" decoding="async" width="1200" height="849" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg0OSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*Xa_Xd-BwWMKuukRyd5Tz8w.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*iCYhY01vtTyAvBSsh3NWuw.webp" alt="" loading="lazy" decoding="async" width="1200" height="843" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg0MyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>來到 13 樓這邊有寶可夢中心、大阪任天堂、CAPCOM…</p>

<p><img src="/assets/aacd5f5cacd1/1*EJivBHlcWiG1tgWQoVQWQQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>喵了一下薩爾達周邊， <a href="/posts/z-度旅行遊記/京阪神自由行攻略-京都大阪神戶8日遊全紀錄與實用交通住宿指南-76d66c2e34af/">上次已經買很多了</a> ，這次什麼都沒買就離開了。</p>

<p><img src="/assets/aacd5f5cacd1/1*1H-S0fCwZQFmXJNLTMEISQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*YxL275thElHiiaMctikMGg.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*pNCyg_53IysVfZa5ndJLXg.webp" alt="" loading="lazy" decoding="async" width="1200" height="845" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg0NSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>路過阪神虎周邊商店。</p>

<p>準備去吃 Shake Shack，七月去曼谷吃過， <a href="https://medium.com/ztravel/%E9%81%8A%E8%A8%98-2024-%E6%B3%B0%E5%9C%8B%E6%9B%BC%E8%B0%B7-bangkok-5-%E6%97%A5%E8%87%AA%E7%94%B1%E8%A1%8C-b7e7c0938985?source=collection_home---4------0-----------------------" target="_blank">意猶未盡</a> ；來大阪回味。</p>
<h4 id="1200-shake-shack">~=12:00 Shake Shack</h4>

<p><img src="/assets/aacd5f5cacd1/1*EV2jpsFyxgjE9lVIg3isgw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*w3Nl5TcRSrFT-9ugnIpuug.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><a href="https://maps.app.goo.gl/LfNFo7TJK7gEZrcN6" target="_blank">Shake Shack 阪神梅田店</a> 就在阪神虎商店隔壁。</p>

<p><img src="/assets/aacd5f5cacd1/1*GZ_3ywx9RZuH5BuTp0bseQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*KdW0FlYYw-Iit-2uYclbLQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*chnnVelVCqU-6IsNNWHEGg.webp" alt="" loading="lazy" decoding="async" width="873" height="1136" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4NzMiIGhlaWdodD0iMTEzNiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>直接用自助點餐，這次吃酪梨培根雞肉堡跟奶昔；這家空間蠻大的、位子也多。</p>

<p><img src="/assets/aacd5f5cacd1/1*Hqk1yAuVB8DpddJFgHrPZg.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*-Ud0FELy464hSGqc-6i2mg.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<h4 id="大阪車站">大阪車站</h4>

<p><img src="/assets/aacd5f5cacd1/1*cBx2n43-tFXz7zHHk9YIBQ.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*fhj864wdmrcC9LyNv0Xl6A.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>吃飽後再大阪車站附近閒晃，想說去大阪的 Kiddy Land 找找吉伊卡哇。</p>

<p><img src="/assets/aacd5f5cacd1/1*lKUQzWMeXVljaUZvdyKy9Q.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*oyvLMv7OPW7GbnylhM072g.webp" alt="" loading="lazy" decoding="async" width="1200" height="856" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg1NiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*qyNQwZ0U6ZWvoyVpBq69yA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>在大阪地下街迷路，走了很久才找到阪急三番街，再走地下聯絡道從南館到北館，美食街上來就是了。</p>

<p><img src="/assets/aacd5f5cacd1/1*EJF9zT0iOWMNlMddrLdCkQ.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*7vqSVT1kVyDJQMFtlcOy_w.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*BYv-IJr_v2YuYmD_-FLjoA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>吉伊卡哇自已獨立一個櫃位在另外一邊。</p>
<h4 id="1300-kiddy-land-osaka-chiikawa">13:00 Kiddy Land Osaka Chiikawa</h4>

<p><img src="/assets/aacd5f5cacd1/1*Cct5wKtjkqkFmVhrZSEraw.webp" alt="" loading="lazy" decoding="async" width="1200" height="865" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg2NSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*xvhRITM1s26Q9sgxLP5ZWg.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*r4v-nQd9yCftkhFxmVafyA.webp" alt="" loading="lazy" decoding="async" width="880" height="1095" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4ODAiIGhlaWdodD0iMTA5NSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>排隊的人蠻多的，粗估至少要等 40 分鐘以上。</p>

<blockquote>
  <p><strong><em>想當然，我沒有花時間排隊等。</em></strong></p>
</blockquote>

<p><img src="/assets/aacd5f5cacd1/1*uJHEegOR7Zr8Vlp-DqbYCA.webp" alt="" loading="lazy" decoding="async" width="1163" height="636" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMTYzIiBoZWlnaHQ9IjYzNiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*QYlEBbbuFij9v2_60PQXeg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*E3rZ-ET_Z8qEO6Eo3QIyeA.webp" alt="" loading="lazy" decoding="async" width="868" height="1102" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4NjgiIGhlaWdodD0iMTEwMiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>一樓有 <a href="/posts/z-度旅行遊記/九州自由行攻略-福岡-長崎-熊本10日獨旅全紀錄與交通住宿實戰經驗-d78e0b15a08a/">熊本熊</a> 活動。</p>

<p><img src="/assets/aacd5f5cacd1/1*RW5sFcypjE_mj4CVwxKotQ.webp" alt="" loading="lazy" decoding="async" width="1200" height="852" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg1MiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*ipoT-8QkWmfXhInjaK3Q2w.webp" alt="" loading="lazy" decoding="async" width="1330" height="2364" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMzMwIiBoZWlnaHQ9IjIzNjQiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>可能是假日的關係活動比較多，Kiddy Land 外也有卡娜赫拉的合影活動。(另一隻 Usagi XD)</p>
<h4 id="1320-前往神戶">13:20 前往神戶</h4>

<p><img src="/assets/aacd5f5cacd1/1*iuobD0lpLgt6UCJ8Wqk3ag.webp" alt="" loading="lazy" decoding="async" width="1400" height="1012" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwMTIiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>在地下街又迷路了一下才找到大阪梅田地鐵站，搭阪急神戶線前往神戶三宮（阪急）。</p>
<h4 id="1350-抵達神戶">13:50 抵達神戶</h4>

<p>時間還很早，打算先去「 <a href="https://maps.app.goo.gl/fspZDr7MyGMpT24r6" target="_blank">神戶動物王國</a> 」找卡皮巴拉。</p>

<p><img src="/assets/aacd5f5cacd1/1*L_861E8BgHXpjDqG7B-ExA.webp" alt="" loading="lazy" decoding="async" width="1200" height="816" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjgxNiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*wkKdFGjiN_C6aGWTNSNxXA.webp" alt="" loading="lazy" decoding="async" width="553" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1NTMiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>Google Map 要我搭乘「新交通港灣人工島線」，在地下街繞了老半天找不到對應的指標。(迷路 Day)</p>

<p><img src="/assets/aacd5f5cacd1/1*OAOZe3cdGLU6r_6Nv2QEww.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*Rx_UFhPm-pM6Nxn2DTKx6w.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>出站才發現就是天上跑的這條，從二樓三宮站入站。</p>

<p><img src="/assets/aacd5f5cacd1/1*yWTsJjuy_Xn01e9Lus8YOw.webp" alt="" loading="lazy" decoding="async" width="1400" height="585" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjU4NSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>車站很多人，後來才知道今天剛好是 <a href="https://zh-tw.kobe-marathon.net/2024/global/" target="_blank">神戶馬拉松</a> 。</p>

<p>迷路 Day…因為車剛好來沒仔細看上錯車，上到往北埠頭的車，悲劇從此刻開始。</p>

<p><img src="/assets/aacd5f5cacd1/1*sURfSLVO2IkE2QPTq3zm7g.webp" alt="" loading="lazy" decoding="async" width="1265" height="286" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjY1IiBoZWlnaHQ9IjI4NiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<blockquote>
  <p><strong><em>往 神戶動物王國 要坐往 神戶空港 的，往 北埠頭 的會在 北埠頭 就回頭回神戶。</em></strong> <em>⚠️⚠️⚠️</em></p>
</blockquote>

<p><img src="/assets/aacd5f5cacd1/1*Yb1jgpn5nKnHA0to0JlBvA.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*z8eEqxV_DQKM1FGLRxRU7A.webp" alt="" loading="lazy" decoding="async" width="879" height="1096" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4NzkiIGhlaWdodD0iMTA5NiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>神戶馬拉松有封路跑上高架，這條地鐵跟北捷文湖線一樣是早期的輪胎捷運。</p>
<h4 id="1420-發現坐錯車-在中埠頭-跳車">14:20 發現坐錯車 在中埠頭 跳車</h4>

<p><img src="/assets/aacd5f5cacd1/1*IGsTOnGQ2wiFBthumdIybw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*_BOkMH5_0AnTJcaZCtwUtg.webp" alt="" loading="lazy" decoding="async" width="553" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1NTMiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>搭到這邊苗頭不對直接跳車，Google Map 建議我走回市民廣場站轉搭下一班。</p>

<p><img src="/assets/aacd5f5cacd1/1*wCBuXOfGc6fSMVTM1MLZ_w.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*g8uGboUzgjpperWLcaCe-A.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>可能是因為是海埔新生地港口區，這邊很荒涼走起來覺得很遠。</p>

<p><img src="/assets/aacd5f5cacd1/1*ZyXQTkEYvXAbIv0TVUgCzQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="978" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9Ijk3OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*5ZyBkEGQ_NiYON5ofd9faw.webp" alt="" loading="lazy" decoding="async" width="1200" height="876" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg3NiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>走快到市民廣場前站想說怎麼那麼熱鬧，原來是神戶馬拉松的終點活動會場。</p>

<p><img src="/assets/aacd5f5cacd1/1*ULve9yF_dxl_KN_wjREO0g.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*YGSebYSQT5maJTHBLgu2xA.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<blockquote>
  <p><em>悲劇 2 因為人太多所以這站不給進了，要再往前走到南公園站。</em></p>
</blockquote>

<p><img src="/assets/aacd5f5cacd1/1*2Gq2LpscnF2cQqBST6ueyA.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*gW7yF6pDt4RDQIzLnd-f0A.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*HoUGLVRBxl2s45E40BMLGQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>走到南公園站再一站就到了，就懶得搭捷運直接走去神戶動物王國…</p>

<p>同前述，因為太荒涼走起來感覺很遠，確實也很遠，從中埠頭走來 2 公里(約 30 分鐘)。</p>
<h4 id="1450-抵達神戶動物王國">14:50 抵達神戶動物王國</h4>

<p><img src="/assets/aacd5f5cacd1/1*HzgFgMtmQ3Sw6piVC4Mblg.webp" alt="" loading="lazy" decoding="async" width="1400" height="977" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9Ijk3NyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>門票：2,200 日圓</p>

<p><img src="/assets/aacd5f5cacd1/1*bwUP-L5-s7kjH5O2TAcf6g.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*jqf3mJy-K5HtBDnG0lcThA.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*PYEx2kx73xmpbMrGZNTcqg.webp" alt="鯨頭鸛、兔猻、小熊貓" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>鯨頭鸛、兔猻、小熊貓</p>

<p>神戶動物王國雖然是半室內半室外，但裡面的動線規劃蠻好的，各種飛禽走獸都有，而且多半可以近距離接觸，很適合親子花個半天來這裡玩；動物的狀況相較姬路市立動物園好很多、環境也很乾淨。</p>
<h4 id="卡皮巴拉">卡皮巴拉</h4>

<p><img src="/assets/aacd5f5cacd1/1*DK2DEH8iPJmiTyDHldeqHQ.webp" alt="" loading="lazy" decoding="async" width="876" height="1087" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4NzYiIGhlaWdodD0iMTA4NyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*CqjDL7S99NtLuamD8V3A0w.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*XtvMffuoulVGOt0AK88BbQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>來這邊的目的達成，來摸卡皮巴拉，好肥、毛摸起來好粗糙很像掃把。</p>

<p><img src="/assets/aacd5f5cacd1/1*AoazZpBcYuh-LpiZLCk6fw.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>每隻都一臉慵懶，不用上班真好！</p>

<p><img src="/assets/aacd5f5cacd1/1*Dui-4l3rOZkGVjVuEAjqZw.webp" alt="" loading="lazy" decoding="async" width="569" height="764" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1NjkiIGhlaWdodD0iNzY0Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><img src="/assets/aacd5f5cacd1/1*a0JZ_DEQph3dAI-Mlzhxvw.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>可能因為太胖，現在不販賣食物在場內餵食、不能餵場外食物或放東西在他頭上。</p>

<p>另一區還有袋鼠、羊近距離互動。</p>

<p><img src="/assets/aacd5f5cacd1/1*D4K3htSuQ7vwCDGqizRLrg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>摸前、摸完都要記得洗個手。</p>

<p><img src="/assets/aacd5f5cacd1/1*A540bEA8Z1WvYv2BZZB6XQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>還有一隻海豹，但看起來場地太小他一直在繞圈圈QQ</p>

<p><img src="/assets/aacd5f5cacd1/1*V3fEMjXiFUrjBW01U3hWOw.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*3YjSHmLP5S50YO2yFl6aBg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>企鵝餵食（需要排整理券）。</p>

<p><img src="/assets/aacd5f5cacd1/1*N2xebJcxnHYGn8e1rTojCw.webp" alt="" loading="lazy" decoding="async" width="1330" height="2364" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMzMwIiBoZWlnaHQ9IjIzNjQiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*a_o37TUg0JELGpeHIlmrZQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>可愛的小爪水獺家族，一直嚶嚶叫。</p>

<p><img src="/assets/aacd5f5cacd1/1*CuP3tdfDMluPWnxc-9Wq1A.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>水獺這邊有一隻馬來貘，但是沒什麼理他 QQ</p>

<p><img src="/assets/aacd5f5cacd1/1*JNkNZEh__ouaMUnwCCIdNg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*xniRR-ET-l2adsy9Yd30Mw.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*aW3oScEuZgY3pgBar7RWrA.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>巨嘴鳥跟梟，還有腳邊會出現的雉雞。(走路要小心不要踩到⚠️)</p>

<p><img src="/assets/aacd5f5cacd1/1*yEYyShOUZxvFBGvis-Vrow.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*cgKffBX23EMZtHYSuLmf9w.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*7Ic9m_24xoIpTB-TS_o_wg.webp" alt="朱利安國王跟會吃同事的鵜鶘、也有夜行館貓頭鷹" loading="lazy" decoding="async" width="675" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2NzUiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>朱利安國王跟會吃同事的鵜鶘、也有夜行館貓頭鷹</p>

<p><img src="/assets/aacd5f5cacd1/1*zUgtU9kQCKScJsprv1NTQQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="964" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9Ijk2NCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*pM5AvtCSA_REaZKTOYODgA.webp" alt="" loading="lazy" decoding="async" width="860" height="1091" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4NjAiIGhlaWdodD0iMTA5MSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>室內還有兔兔貓貓狗狗互動區（要排整理券）、飲食、紀念品店…等等</p>

<p>整體來說體驗蠻好的！</p>
<h4 id="1550-離開準備返回神戶">15:50 離開，準備返回神戶</h4>

<p>主要是來摸卡皮巴拉，加上時間不太夠所以沒有停留太久。</p>

<p><img src="/assets/aacd5f5cacd1/1*yfpUfEWTujRfFn1SLBi8aQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*42N4yOOTADXSR3TnaE0foA.webp" alt="" loading="lazy" decoding="async" width="1400" height="983" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9Ijk4MyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<h4 id="-1605-抵達神戶三宮站">~= 16:05 抵達(神戶)三宮站</h4>

<p><img src="/assets/aacd5f5cacd1/1*oNUQVDNJXP_zpuS0vPWIWA.webp" alt="" loading="lazy" decoding="async" width="872" height="1098" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4NzIiIGhlaWdodD0iMTA5OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>因為神戶馬拉松的緣故，出站的人很多。</p>

<p><img src="/assets/aacd5f5cacd1/1*92IdBTMJ1Z1AyzHdCQVNHA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*g-zbhehAAcO3tmvdJ6Dotw.webp" alt="" loading="lazy" decoding="async" width="858" height="1081" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4NTgiIGhlaWdodD0iMTA4MSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*4kvY1I-JyKoMbo6Xo3Mg6A.webp" alt="" loading="lazy" decoding="async" width="877" height="1079" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4NzciIGhlaWdodD0iMTA3OSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>離吃飯時間還有一個多小時，先在神戶三宮附近逛逛，一樣去找扭蛋跟 Kiddy Land。</p>

<p><img src="/assets/aacd5f5cacd1/1*LAu0vz0PiOzK3QC8xQbeJg.webp" alt="" loading="lazy" decoding="async" width="872" height="1076" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4NzIiIGhlaWdodD0iMTA3NiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*eDM9G1Lrm5nQLnusWKD7uA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>神戶的 Kiddy Land 沒什麼人但是吉伊卡哇的商品很少，沒找到我要的睡衣烏薩奇。</p>

<p><img src="/assets/aacd5f5cacd1/1*Cmt7wQ4UV4cYAaTNzxvgHA.webp" alt="" loading="lazy" decoding="async" width="878" height="1102" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4NzgiIGhlaWdodD0iMTEwMiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*q7C947BYHvNxBDhPSGG6uQ.webp" alt="" loading="lazy" decoding="async" width="1200" height="819" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjgxOSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>Center Plaza 這邊有點年代但是有很多商店可逛。</p>

<p><img src="/assets/aacd5f5cacd1/1*8vFujFuXpB65kosAzXrtkw.webp" alt="" loading="lazy" decoding="async" width="1400" height="986" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9Ijk4NiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*XD7jraHi5_yQZfB7YAkv9Q.webp" alt="" loading="lazy" decoding="async" width="1400" height="959" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9Ijk1OSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*WlCIdUIpqTHUN4jwg1x_Hg.webp" alt="" loading="lazy" decoding="async" width="820" height="1122" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4MjAiIGhlaWdodD0iMTEyMiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>去二樓找扭蛋也去二手商店看看有沒有我要的烏薩奇。</p>

<p><img src="/assets/aacd5f5cacd1/1*3AtESgoEYKHf6RFMBy7a4A.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*DhlS3byoBALyjqUNQNZlqw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>有一家信長書店，可以買御守送朋友，嘻嘻。</p>
<h4 id="1700-神戸牛-吉祥吉">~=17:00 <a href="https://maps.app.goo.gl/8UcmAfUDGpH8F1PX8" target="_blank">神戸牛 吉祥吉</a></h4>

<p><img src="/assets/aacd5f5cacd1/1*_kTtTzMcYzgucSSbZBpOMg.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*YeGNnRYe3W2AId3Ey5Xoxg.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*AF8Ium09DuuXVdb6wgirtQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>B1 就有一家 神戸牛 吉祥吉，差不多快五點就先來吃晚餐了。</p>

<p>時間還很早，沒什麼人；老闆聽到我是台灣來的很熱情，這餐也是來神戶的目的， <strong>吃一餐好的 — 神戶牛</strong> ，不然來的幾天幾乎都亂吃。</p>

<p><img src="/assets/aacd5f5cacd1/1*TplkGy7shiHlWAHyLf9-KQ.webp" alt="" loading="lazy" decoding="async" width="1200" height="824" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjgyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*0ufGhslTqU6l0UUlsFCOCg.webp" alt="" loading="lazy" decoding="async" width="822" height="626" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4MjIiIGhlaWdodD0iNjI2Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>我點「神戶牛赤身肉牛排 220g」7,700 日圓 (未稅價⚠️，結帳要含稅)加購套餐(生菜、飯、湯)跟生啤。</p>

<p><img src="/assets/aacd5f5cacd1/1*794jNjGugkDnsJOPEdOJZw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*xSB_UvOX-MwvXzzmDx-KoQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*H55tOkGS36r6WTGAqT560w.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>生菜清爽，湯是牛肉湯，很好喝。</p>

<p><img src="/assets/aacd5f5cacd1/1*MJLdfXQZoH_4vUy0KUOq5w.webp" alt="" loading="lazy" decoding="async" width="1400" height="1002" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwMDIiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*MUe5xLDpu4wLU2OCnrddTw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>肉很嫩、汁多、肉味足，相較昨天吃的神戶牛，這才是神戶牛的滋味。</p>

<blockquote>
  <p><em>不過真要說我還是比較喜歡鐵板的，更勝洋食。</em></p>
</blockquote>

<p>最後實刷： <code class="language-plaintext highlighter-rouge">$2,158 台幣</code> 。</p>
<h4 id="1740-吃飽離開前往神戶塔">17:40 吃飽離開，前往神戶塔</h4>

<p>吃完走到海岸線的舊居留地、大丸前站，搭一站後再步行前往神戶塔。</p>

<p><img src="/assets/aacd5f5cacd1/1*ynQiJ9mgKhOxFdSUESJFnQ.webp" alt="" loading="lazy" decoding="async" width="873" height="1095" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4NzMiIGhlaWdodD0iMTA5NSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*_BKRsKmQrLdl9fLPgOrFaQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*4l2DyckNmvx97IuF256JUg.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>神戶這邊好熱鬧(相較姬路)，有好多百貨可以逛。</p>

<p>搭乘往 新長田，在下一站 港元町 下車。</p>
<h4 id="1805-抵達神戶塔周邊">18:05 抵達神戶塔周邊</h4>

<p><img src="/assets/aacd5f5cacd1/1*441FBjZsUDMXDMQxz8XCmg.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*wH_baSj-1o9q5GyFZ97-xg.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><a href="/posts/z-度旅行遊記/京阪神自由行攻略-京都大阪神戶8日遊全紀錄與實用交通住宿指南-76d66c2e34af/">去年來的時候在維修</a> ，目前已完全開放了。</p>

<p><img src="/assets/aacd5f5cacd1/1*NdyL8AiQtaLIkZfb54dFVA.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*cD32qAyEg7AOlZJg04FM_w.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>神戶港這邊蠻有港灣小鎮的感覺，有機會的話會考慮在神戶這邊住一晚。</p>

<p>這次來走馬看花一下就走了。</p>
<h4 id="1830-回神戶中華街">18:30 回神戶中華街</h4>

<p>回神戶站的路上先去中華街買 <a href="https://frantz.co.jp/en/" target="_blank">Kobe Frantz 巧克力伴手禮</a> 。</p>

<p><img src="/assets/aacd5f5cacd1/1*XjaKzeVqxVLXk_MQFlABzw.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*D2ge3h9VtwH0vCO8-rT6kQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*usONmjglsnKER-HS5DS2yQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*Y_VKoWARSlj-ZuQHwEYbCQ.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<h4 id="回到-jr-元町站">回到 JR 元町站</h4>

<p><img src="/assets/aacd5f5cacd1/1*HFFt6e3ORaTBS_A2xfk_Mw.webp" alt="" loading="lazy" decoding="async" width="1200" height="791" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijc5MSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>請注意是 JR 元町站不要跑去阪神電車元町站。</p>
<h4 id="1853-搭乘-快速-姬路方面網干-前往神戶兵庫">18:53 搭乘 快速 姬路方面網干 前往神戶(兵庫)</h4>

<p><img src="/assets/aacd5f5cacd1/1*kQMXFTC_jkFXbQrLjTKawA.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*CIaO_bZsDuNIThM4GNUXqA.webp" alt="" loading="lazy" decoding="async" width="384" height="351" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzODQiIGhlaWdodD0iMzUxIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<h4 id="1857-同月台另一側-換乘-新快速-湖西線經由網干">18:57 同月台另一側 換乘 新快速 湖西線經由網干</h4>

<blockquote>
  <p><strong><em>換乘快 20 分鐘抵達</em></strong> <em>，還好有換成才趕在姬路唯一一家藥妝店松本清免稅時間結束前抵達。</em></p>
</blockquote>

<p><img src="/assets/aacd5f5cacd1/1*LL8pEAjsoOyIBY7Zng5EOg.webp" alt="" loading="lazy" decoding="async" width="873" height="1100" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4NzMiIGhlaWdodD0iMTEwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*GNDjeJjhEgixMjH69bSMuQ.webp" alt="" loading="lazy" decoding="async" width="872" height="1106" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4NzIiIGhlaWdodD0iMTEwNiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*zEh8247pPwlJJShPWXRhNg.webp" alt="" loading="lazy" decoding="async" width="867" height="1096" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4NjciIGhlaWdodD0iMTA5NiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*kNw192v7znWE0utpE0cVGA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>這列車車型也是要按旁邊開門按鈕才會開門的、車廂彼此是分開的。</p>
<h4 id="1934-抵達姬路">19:34 抵達姬路</h4>

<p><img src="/assets/aacd5f5cacd1/1*JGEsWM6ZsBJhQ9MDEFgjyg.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<h4 id="1945-抵達姬路-piole-松本清採購藥妝">19:45 抵達姬路 Piole 松本清採購藥妝</h4>

<p><img src="/assets/aacd5f5cacd1/1*1yNFLnhhhRjr32KRjndzZQ.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<blockquote>
  <p><em>請注意雖然他營業時間寫到 21:00 但是 <strong>免稅時間只到 20:30</strong> 。⚠️⚠️⚠️</em></p>
</blockquote>

<p><img src="/assets/aacd5f5cacd1/1*gEwopdiZt9hWS18pwDmbog.webp" alt="買了一個超迷你 Cup Noodle" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>買了一個超迷你 Cup Noodle</p>

<p><img src="/assets/aacd5f5cacd1/1*XIL4YdSSE1aSMvOCfo_Eog.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*1UO4_sG-Z1vARToCEpeSGQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>在日本的最後一晚，在飯店吃吃吃喝喝宵夜、啤酒。(這丸子是鬆軟口感…)</p>

<blockquote>
  <p><strong><em>晚安姬路。</em></strong></p>
</blockquote>

<h3 id="day-7-1118-週一-岡山回程">Day 7 (11/18 週一) 岡山、回程</h3>
<h4 id="0830-早安姬路">08:30 早安姬路</h4>

<p><img src="/assets/aacd5f5cacd1/1*gIylwTEdczfgkIyCgRg-PQ.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*ZfhXzMRapc-5g77Nrk7GCQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>最後看一眼姬路的早晨。</p>

<p><img src="/assets/aacd5f5cacd1/1*KR95SMQ2Nrdf6lK0wxqEcg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*u1yIgbMQGfGFLOJ4fsFBbA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>到新幹線站準備搭乘 08:52 的新幹線前往岡山。</p>
<h4 id="搭乘-0852-的新幹線自由座前往岡山">搭乘 08:52 的新幹線自由座前往岡山</h4>

<p><img src="/assets/aacd5f5cacd1/1*WzQVvLKiQSx-5LXpw75O3w.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<h4 id="0913-抵達岡山車站">09:13 抵達岡山車站</h4>

<p>搭乘新幹線大約 21 分鐘就會到岡山車站了，可惜這次時間不夠無法 <a href="https://medium.com/ztravel/%E9%81%8A%E8%A8%98-2023-%E5%BB%A3%E5%B3%B6%E5%B2%A1%E5%B1%B1-6-%E6%97%A5%E9%81%8A-31b9b3a63abc?source=collection_home---4------2-----------------------" target="_blank">再訪廣島</a> 。</p>

<p><img src="/assets/aacd5f5cacd1/1*PgFJG7RNh8w5f4pK5Dv6Eg.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>跟上次一樣直接到岡山車站西口搭乘往機場巴士的地方，這裡就有置物櫃寄放行李、也有廁所(這時間百貨還沒開廁所很少)。</p>
<h4 id="0930-岡山車站麥當勞吃這陣子台灣很紅的鬆餅漢堡">09:30 岡山車站麥當勞吃這陣子台灣很紅的「鬆餅漢堡」</h4>

<p><img src="/assets/aacd5f5cacd1/1*1O8uNrClegWyQoAJE12e5g.webp" alt="" loading="lazy" decoding="async" width="838" height="809" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4MzgiIGhlaWdodD0iODA5Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>看不懂日文差點選錯，要選這個「マックグリドル」才是鬆餅系列。</p>

<blockquote>
  <p><em>日本麥當勞早餐時段到 10:30。</em></p>
</blockquote>

<p><img src="/assets/aacd5f5cacd1/1*OOoRAGeCSlq2AhtBBs3ZaA.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*tuExEIAQs1IIAede34Odqg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>吃完大約 10:00 沒有特別想去的地點。</p>

<p><img src="/assets/aacd5f5cacd1/1*17imSbehBjflWDXLWBedlQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><a href="/posts/z-度旅行遊記/山陽地區廣島岡山自由行攻略-6日行程-交通-住宿全解析-31b9b3a63abc/">上次已經去過岡山城跟吉備津神社了</a> ，最後時間就去逛逛岡山 AEON 永旺夢樂城。</p>

<p><img src="/assets/aacd5f5cacd1/1*j6NZ7W08WVWsntCa7qvN6A.webp" alt="岡山藝術季" loading="lazy" decoding="async" width="682" height="506" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2ODIiIGhlaWdodD0iNTA2Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><a href="https://forestartfest-okayama.jp/" target="_blank">岡山藝術季</a></p>

<p>今年岡山有 <a href="https://forestartfest-okayama.jp/" target="_blank">藝術季活動</a> 。</p>

<p><img src="/assets/aacd5f5cacd1/1*dhgBrvzJrFsPimXmsJb_QQ.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*YEmu3f0rStKaWXCl890OSQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*HaQeBv92EgDp2qdq3EHFvQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>施工中的岡山站跟暫時被移到車站旁的桃太郎。</p>

<p><img src="/assets/aacd5f5cacd1/1*_aly6JVMgGDzW4BogUg9Wg.webp" alt="" loading="lazy" decoding="async" width="878" height="1101" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4NzgiIGhlaWdodD0iMTEwMSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*6bKTUuYl1MMeXhkb2JKOLg.webp" alt="" loading="lazy" decoding="async" width="882" height="1097" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4ODIiIGhlaWdodD0iMTA5NyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>從地下街過馬路到 AEON MALL B2，這邊也有一家麥當勞。</p>

<p><img src="/assets/aacd5f5cacd1/1*I7cFknifEeInNlJfK7UYSw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*h_77ECXhHjNHSl7Lr8L7-Q.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*nvJY17geWnC69iCT0kQHrg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*BlcO-BaPAFCkd_q9L30Etw.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>這邊很大很好逛，幾乎所有品牌都有。</p>

<p><img src="/assets/aacd5f5cacd1/1*vjssm5svEoPfePGcNgElCg.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>要吃東西、買伴手禮 (B1-B2) 也都有。</p>

<p><img src="/assets/aacd5f5cacd1/1*8oOH2Pe4c_SbgDzwoR21Yg.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*NapTm7rBZ4qVZ7EpNqr7xg.webp" alt="" loading="lazy" decoding="async" width="1200" height="866" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijg2NiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*YClpxnjnyZQuS_ZNi6ZvvQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1008" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwMDgiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*HJ4J2jAs8mruW6BZYvyWcA.webp" alt="" loading="lazy" decoding="async" width="602" height="1306" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2MDIiIGhlaWdodD0iMTMwNiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>繼續找扭蛋，但都沒找到我要的電車按鈕扭蛋，放棄…應該是沒再出了。</p>

<p><img src="/assets/aacd5f5cacd1/1*HdWGWOOs9agTb9qWYaiqiQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*hK5tJ8UD4aNoz9LNTYBWyA.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*MQnwEbWs96IBpBEKW7IB4w.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>也有幾家店有賣吉伊卡哇。</p>
<h4 id="1100-準備吃午餐">~=11:00 準備吃午餐</h4>

<blockquote>
  <p><strong><em>雖然沒有很餓但是為了備戰下午候機跟虎航沒供餐，所以還是要吃。</em></strong> <em>⚠️⚠️⚠️</em></p>
</blockquote>

<p><img src="/assets/aacd5f5cacd1/1*s8axzlQR2pkJASqoyiPjQg.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*3JEFC2MwRYJbT7WR4kb2Sg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*uxodtgWIX2tokvA8GANlBQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>樓上這邊吃的選擇很多。</p>

<p><img src="/assets/aacd5f5cacd1/1*8OX9V42Q-S0S7W-1Ve7n6A.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*mIUREsw2cy1DfnAuxi9lWg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*vAiLB635e_VNF1BrY7U3ZA.webp" alt="**豚ステーキ専門店 B 岡山店**" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><a href="https://tabelog.com/okayama/A3301/A330101/33018608/" target="_blank"><strong>豚ステーキ専門店 B 岡山店</strong></a></p>

<p>選了一家主打鐵板豬排的店。</p>

<p><img src="/assets/aacd5f5cacd1/1*f9-cymGYzbWqmSgVSwht8Q.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*-8Sx-7tZ5l-DPua6GOunTA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>點了一份鐵板豬排定食(200g)跟生啤。最後付 <code class="language-plaintext highlighter-rouge">2,178 日圓</code></p>

<p><img src="/assets/aacd5f5cacd1/1*rT1vHotHA_BsYMp5BFadUQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*weW08CNKF-sFY48mz-Qw7g.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>比較訝異的是豬排上來的時候沒有全熟，大約 7 分熟；查了一下評價跟介紹好像就是這家的特色，不過我還是趁鐵板熱的時候把能用熟的肉塊都用熟了。</p>

<blockquote>
  <p><em>吃起來是真的好吃，很 Juice、肉香味足、不會咬不爛；私心覺得不輸昨天的神戶牛(我可能沒富貴命)。</em></p>
</blockquote>

<p><img src="/assets/aacd5f5cacd1/1*4SkncSHSzMb-s7cnwrAqFQ.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*E44bH_Sc3tK3PS-gSzr3jQ.webp" alt="小廢物行李箱(大約跟 Airpods Pro 一樣大)跟娃包" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>小廢物行李箱(大約跟 Airpods Pro 一樣大)跟娃包</p>

<p>最後在一樓無印良品買了些東西大約 12:15 就離開了。</p>
<h4 id="1230-回到岡山站">~=12:30 回到岡山站</h4>

<p>準備搭乘 13:00 的交通車去岡山機場。</p>

<p><img src="/assets/aacd5f5cacd1/1*g0faLCzFGCjUq6xndxPXCA.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*wp34SWl53PM47RyotD7_7A.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>最後再看一眼岡山。</p>
<h4 id="1300-搭乘交通車前往岡山機場">~=13:00 搭乘交通車前往岡山機場</h4>

<p><img src="/assets/aacd5f5cacd1/1*WQPyQGy5C0nZLfJ-3GIZcA.webp" alt="" loading="lazy" decoding="async" width="869" height="1101" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4NjkiIGhlaWdodD0iMTEwMSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*YD-OAt75d1s69u6qZpWrsw.webp" alt="" loading="lazy" decoding="async" width="872" height="1102" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4NzIiIGhlaWdodD0iMTEwMiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>在 21 月台這邊排隊候車，不用擔心搭不上，只要是 13:00 前來排隊的都會送你到機場(滿了會再加開交通車)。</p>

<p>可以直接刷交通卡上下車(Suica) 或是去候車亭裡面投幣買票。</p>

<p><img src="/assets/aacd5f5cacd1/1*wZz-iSrK6orko9_30h31gw.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*2Ey3Ma7iGU0aZcvM-5-KuA.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>因為滿員提早 12:45 就發車了。</p>
<h4 id="1320-抵達岡山桃太郎機場">~=13:20 抵達岡山桃太郎機場</h4>

<p><img src="/assets/aacd5f5cacd1/1*7gnnl4V4uwpVHg9wq9sliw.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*Zev8bJycwEj6HruuaPqWyA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>時間還非常早，今年搭的是 17:30 起飛的航班、去年是搭 15:25 的這個時間來剛剛好，17:30 起飛的現在來要乾等快 4 個小時。</p>

<p><img src="/assets/aacd5f5cacd1/1*EL5tuKai1e95lToFw7agNQ.webp" alt="https://www.okayama-airport.org/tw/access/bus" loading="lazy" decoding="async" width="367" height="600" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzNjciIGhlaWdodD0iNjAwIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><a href="https://www.okayama-airport.org/tw/access/bus" target="_blank">https://www.okayama-airport.org/tw/access/bus</a></p>

<blockquote>
  <p><em>但是目前沒辦法，因為 <strong>下一班是 15:55，到機場 16:25</strong> ；雖然機場很小同時間航班也只有一班出境算快，但怕塞車之類的因素，很難保證 16:25 會到，像我來的時候遇到塞車整整多了快 30 分鐘車程。</em></p>
</blockquote>

<blockquote>
  <p><em>就看之後岡山機場會不多開交通車班次了，或是去 <a href="https://www.facebook.com/groups/207266832755595" target="_blank"><strong>日本自由行討論區</strong></a> 社團找人拼計程車。</em></p>
</blockquote>

<blockquote>
  <p><em>看別人分享岡山車站到機場計程車大約 7,000 日圓上下。</em></p>
</blockquote>

<p><img src="/assets/aacd5f5cacd1/1*BAf8ZKtaE0PZp3CSqHp5oQ.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*KulgQh4uI3M8GHBzX3_hSw.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*VrbZuTSIDQztMYoFgjVMOA.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*WloJT1lwu9sE7K1YY2Lmfg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>在機場附近亂晃，機場外有一個縮小版的岡山後樂園還有很紅的楓葉。</p>

<p><img src="/assets/aacd5f5cacd1/1*DFOrcpULEpM1ZAaCsxVbDQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*OO1rTCEh9kw5aQMfaPk_wQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>車站外也有一尊桃太郎，另外機場溫馨提醒要買保險， <strong>我這次就真的就差點被自行車撞。⚠️</strong></p>

<p><img src="/assets/aacd5f5cacd1/1*oMmge-1kLe8YMumlz4I-dw.webp" alt="" loading="lazy" decoding="async" width="1200" height="900" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjkwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*BRsrPzntiPcckzgHSzFOaw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*JZnvFBhhDjBVHdeEqPDYHw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>國內線這邊有蠻多位子可以坐著休息。</p>

<p>一樓只有一家伴手禮店其他什麼都沒有， <a href="/posts/z-度旅行遊記/山陽地區廣島岡山自由行攻略-6日行程-交通-住宿全解析-31b9b3a63abc/">跟去年一樣</a> 買了一個冰桃麻糬吃。</p>

<p><img src="/assets/aacd5f5cacd1/1*PQ1GM0STjbTPYN0p6_S_aA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*dRHe9n0Rpk7sXzbOplk9NA.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*PQ1GM0STjbTPYN0p6_S_aA.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*MZVip0FtjrMIxj3t3ppxPg.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*1vlNWZvaNY8I47Qydyd9cw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>二樓在整修只剩部分店有開， <a href="/posts/z-度旅行遊記/山陽地區廣島岡山自由行攻略-6日行程-交通-住宿全解析-31b9b3a63abc/">去年能去的觀景平台</a> 好像也沒開放了；但至少還有一家伴手禮店跟幾家吃的。</p>
<h4 id="1500-開始辦理登機手續">~=15:00 開始辦理登機手續</h4>

<p><img src="/assets/aacd5f5cacd1/1*lGDYdJexHQY6XkiyPZJOhw.webp" alt="" loading="lazy" decoding="async" width="882" height="1102" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4ODIiIGhlaWdodD0iMTEwMiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*1oLSBw-IwqMJh4-RA81Apg.webp" alt="" loading="lazy" decoding="async" width="1200" height="827" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjgyNyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>桃太郎機場太小，跟其他機場報到＋托運的流程不一樣。</p>

<blockquote>
  <p><strong><em>這邊是統一排隊，要托運的行李過 X 光＋會貼封條貼紙、沒要托運的就不用(出境前的安全檢查會掃描其他的)，無托運行李的也要跟著排</em></strong> <em>，排到就跟機場人員說沒要托運直接過、如果貼完貼紙又打開行李箱就要重新掃描＋貼貼紙。⚠️⚠️⚠️</em></p>
</blockquote>

<blockquote>
  <p><em>機場人員會講中文…很厲害</em></p>
</blockquote>

<h4 id="1515-報到托運完畢">~=15:15 報到＋托運完畢</h4>

<p><img src="/assets/aacd5f5cacd1/1*k2WrB1ooYpRa8zSD8A2Dlw.webp" alt="" loading="lazy" decoding="async" width="651" height="1104" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2NTEiIGhlaWdodD0iMTEwNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*MNH21H-7oPaOq-GP_uKUpA.webp" alt="" loading="lazy" decoding="async" width="879" height="1102" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4NzkiIGhlaWdodD0iMTEwMiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>這次沒買什麼、沒買酒就 17 kg 了…但是我幾乎沒手提行李，全塞進去了。</p>
<h4 id="1620-開始排安檢出境">~=16:20 開始排安檢、出境</h4>

<p><img src="/assets/aacd5f5cacd1/1*Wjgk2cV8hkiBkIjxjke6AA.webp" alt="" loading="lazy" decoding="async" width="876" height="1091" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4NzYiIGhlaWdodD0iMTA5MSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*iezQ2KMlAUzJrKjqlHzyLw.webp" alt="" loading="lazy" decoding="async" width="1400" height="1050" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEwNTAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>機場整修中..但依然有扭蛋。</p>

<p><img src="/assets/aacd5f5cacd1/1*di7LhsC-MYyRIVwYRWVU-Q.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*AHTIPdSzU7TJrq1hVeKACg.webp" alt="" loading="lazy" decoding="async" width="774" height="931" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NzQiIGhlaWdodD0iOTMxIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>雖然只有一個航班但是因為機場小、工作人員少、只有一條線、檢查的也很細，所以排安檢出境就大概花了快 20–30 分鐘。</p>

<p>倒水跟丟棄物品的地方也就只是個箱子。</p>

<blockquote>
  <p><em>感覺如果趕 15:55 岡山出發，到機場 16:25 的交通車，如果能準時抵達，基本上報到托運那邊已經全空了可以直接辦理，然後再上來直接排出境安檢應該是來得及的。</em></p>
</blockquote>

<blockquote>
  <p><em>只有我們這一航班，所以應該會等到所有已報到的人安檢完才會起飛，早來機場晚排安檢的人也沒比較快。</em></p>
</blockquote>

<h4 id="1642-開始候機">~=16:42 開始候機</h4>

<p><img src="/assets/aacd5f5cacd1/1*dZLP9y156IaL2ZYxefWyAQ.webp" alt="" loading="lazy" decoding="async" width="1400" height="1867" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjE4NjciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/aacd5f5cacd1/1*vb73mCN99CDryoSCqdo_Dw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*0-fhLcrfopTMrx9rvE0nYg.webp" alt="" loading="lazy" decoding="async" width="1400" height="965" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9Ijk2NSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>候機室不大，只有一家免稅店，有賣一些伴手禮、菸酒品相不多。</p>

<p><img src="/assets/aacd5f5cacd1/1*SAM-saIjWrap5wL4dAxENQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*QCxhYYWHW2RLvZsTgWQ0Sw.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>想投幾罐桃子水帶回台灣，全賣完了；只好投一罐紅茶花伝，回來後發現很好喝、不會太甜，後悔沒多投幾罐。</p>
<h4 id="1810-起飛-延誤-40-分鐘">~=18:10 起飛 (延誤 40 分鐘)</h4>

<p><img src="/assets/aacd5f5cacd1/1*f3Yqp6letQnKZI8spjNn-Q.webp" alt="" loading="lazy" decoding="async" width="1024" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDI0IiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*PSlGa7hB3SwG6zFHnEVFLQ.webp" alt="" loading="lazy" decoding="async" width="900" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MDAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<blockquote>
  <p><strong><em>再見岡山、再見日本。</em></strong></p>
</blockquote>

<p>在機上隔壁坐一位 Youtuber 大哥「 <a href="https://www.youtube.com/@lw.chill2022" target="_blank"><strong>跟著我們一起Chill with us</strong></a> 」人很 Nice，他這次是去四國的，互相交流了一下景點推薦，有機會也想去四國。</p>
<h4 id="1945-抵達台灣-延誤-10-分鐘">19:45 抵達台灣 (延誤 10 分鐘)</h4>

<p>還好只影響 10 分鐘，速速提領完行李飛奔巴士站。</p>
<h4 id="2020-前往台北的大有巴士">20:20 前往台北的大有巴士</h4>

<p><img src="/assets/aacd5f5cacd1/1*EDhpsb6FiVOOwKoDslj3nA.webp" alt="" loading="lazy" decoding="async" width="879" height="1085" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4NzkiIGhlaWdodD0iMTA4NSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*63kNa-lLfoQ6uKfjEy9BVw.webp" alt="" loading="lazy" decoding="async" width="875" height="1094" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4NzUiIGhlaWdodD0iMTA5NCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/aacd5f5cacd1/1*uRb-Shzk65f1cuiiBxd6SQ.webp" alt="" loading="lazy" decoding="async" width="768" height="1024" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NjgiIGhlaWdodD0iMTAyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>依照地點不同可以搭不同的車回家，之前是搭國光直接回台北，這次我要搭大有巴士 11 月台回新北。</p>

<blockquote>
  <p><em>好巧不巧悠遊卡餘額不夠刷不過，又飛奔去櫃檯用現金買票(還好前一天把台幣帶在身上)，最終有趕上客運。</em></p>
</blockquote>

<h4 id="旅程結束">旅程結束。</h4>
<h3 id="心得總結">心得總結</h3>

<p>這次的行程安排太緊湊，很多地方都走馬看花，尤其前三天因為移動距離較長加上停留時間太短，沒有好好慢慢享受島根出雲、松江的美麗景色；如果有機會會想再去一次，看夕陽、看楓葉、看神迎神在祭典。</p>

<p>如果想了解歷史再去出雲可參考蘭爸爸的影片：</p>
<ol>
  <li><a href="https://www.youtube.com/watch?v=cnp9H--KPyY" target="_blank">日本這片土地是怎麽誕生的？竟然是被生出來的！！|日本神話|日本的誕生|天地開闢|国産み|神産み|大八島囯|伊邪那岐|伊邪那美|蘭爸爸說故事</a></li>
  <li><a href="https://www.youtube.com/watch?v=W8SjLltb3U4" target="_blank">日本最調皮的神，與自己的姐姐生下了日本皇室的直系祖先！斬殺八岐大蛇，迎娶美麗的妻子，堪稱神仙界的“神生贏家”！日本最初的和歌竟然出自他口？！|日本神話|須佐之男|岩戶隱|八岐大蛇|稻田姬|蘭爸爸說故事</a></li>
  <li><a href="https://www.youtube.com/watch?v=HbxBcKkfCnk" target="_blank">一隻兔子爲何要招惹一群鯊魚？經歷過兩次的死而復生，他竟然迎娶了自己祖先的女兒！大國主神話|大國主|因幡之白兔|根之囯|日本神話|蘭爸爸說故事</a> 
<strong>有介紹到天照大神、大國主神(出雲神社)、因幡之白兔的故事。</strong>
    <h4 id="山陽-岡山廣島地區遊記">山陽 岡山廣島地區遊記</h4>
    <ul>
      <li><a href="/posts/z-度旅行遊記/山陽地區廣島岡山自由行攻略-6日行程-交通-住宿全解析-31b9b3a63abc/"><strong>[遊記] 2023 山陽地區 廣島岡山 6 日自由行</strong></a></li>
    </ul>
  </li>
</ol>

<p>可以一起參考山陽地區遊記，有時間的話可以山陽＋山陰一起玩。</p>
<h4 id="kkday-推廣--1">KKday 推廣 🛒</h4>
<ul>
  <li><a href="https://www.kkday.com/zh-tw/product/153331?cid=19365" target="_blank">日本 JR PASS|鳥取・松江區域周遊券|eMCO 電子票</a> 
(如果只去松江、鳥取松江地區可考慮)</li>
  <li><a href="https://www.kkday.com/zh-tw/product/20307-jr-sanin-okayama-area-pass?cid=19365" target="_blank"><strong>關西&amp;山陰地區鐵路周遊券 JRPass</strong></a> 
(岡山機場進出最佳選擇)</li>
  <li><a href="https://www.kkday.com/zh-tw/product/137689-japan-high-speed-daily-unlimited-data-japanese-esim?cid=19365" target="_blank"><strong>日本eSIM卡｜每日高速、總量、無限流量吃到飽方案</strong></a></li>
  <li><a href="https://www.kkday.com/zh-tw/product/20315-jr-sanyo-sanin-area-pass?cid=19365" target="_blank">日本 JR PASS｜山陽＆山陰地區鐵路周遊券｜eMCO 電子票</a> 
(一口氣玩遍山陽山陰地區)</li>
  <li><a href="https://www.kkday.com/zh-tw/product/11463-osaka-bus-tour-tottori-sand-dunes-uratomi-coast-cruise-sand-museum-japan?cid=19365" target="_blank">【鳥取巴士一日遊】鳥取砂丘、浦富海岸巡航、砂之美術館、採梨子體驗｜含特色午餐（大阪出發）</a></li>
  <li><a href="https://www.kkday.com/zh-tw/product/20314-japan-jr-sanyo-sanin-northern-kyushu-pass-delivery-to-multiple-countries?cid=19365" target="_blank">日本 JR PASS|山陽&amp;山陰&amp;北九州地區鐵路周遊券|多國郵寄</a></li>
</ul>

<blockquote>
  <p><a href="https://www.kkday.com/zh-tw?cid=19365" target="_blank">如果這篇文章對您有幫助，歡迎使用我的 推廣連結 選購 KKday 商品、行程，我將獲得部分收益，持續更多旅遊創作，謝謝。</a></p>
</blockquote>

<h3 id="更多遊記">更多遊記</h3>
<ul>
  <li><a href="https://blog.zhgchg.li/%E9%81%8A%E8%A8%98-2025-%E9%9F%93%E5%9C%8B%E9%87%9C%E5%B1%B1-8-%E5%A4%A9-7-%E5%A4%9C%E8%87%AA%E7%94%B1%E8%A1%8C-8ace34a1a3d8?source=collection_home_page----96927b78a281-----0-----------------------------------" target="_blank">[遊記] 2025 韓國釜山 8 天 7 夜自由行</a></li>
  <li><a href="/posts/z-度旅行遊記/九州自由行攻略-福岡-長崎-熊本10日獨旅全紀錄與交通住宿實戰經驗-d78e0b15a08a/">[遊記] 2023 九州 10 日自由行獨旅</a></li>
  <li><a href="/posts/z-度旅行遊記/山陽地區廣島岡山自由行攻略-6日行程-交通-住宿全解析-31b9b3a63abc/"><strong>[遊記] 2023 山陽地區 廣島岡山 6 日自由行</strong></a></li>
  <li><a href="/posts/z-度旅行遊記/名古屋一日快閃自由行-樂桃航空紅眼班機省錢攻略與行程規劃-7b8a0563c157/">[遊記] 9/11 名古屋一日快閃</a></li>
  <li>[遊記] <a href="/posts/z-度旅行遊記/東京自由行攻略-5-天食住行全記錄與必訪景點推薦-9da2c51fa4f2/">2023 東京 5 日自由行</a></li>
  <li>[遊記] <a href="/posts/z-度旅行遊記/京阪神自由行攻略-京都大阪神戶8日遊全紀錄與實用交通住宿指南-76d66c2e34af/">2023 京阪神 8 日自由行</a></li>
</ul>]]></content>
  </entry><entry>
    <title type="html">GitHub Pages 免費快速建置個人 LinkTree 連結頁面｜linkyee 開源模板教學</title>
    <link href="https://zhgchg.li/posts/zrealm-robotic-process-automation/github-pages-%E5%85%8D%E8%B2%BB%E5%BF%AB%E9%80%9F%E5%BB%BA%E7%BD%AE%E5%80%8B%E4%BA%BA-linktree-%E9%80%A3%E7%B5%90%E9%A0%81%E9%9D%A2-linkyee-%E9%96%8B%E6%BA%90%E6%A8%A1%E6%9D%BF%E6%95%99%E5%AD%B8-70aeddb1fd9b/" rel="alternate" type="text/html" title="GitHub Pages 免費快速建置個人 LinkTree 連結頁面｜linkyee 開源模板教學" />
    <published>2024-10-27T13:36:34+08:00</published>
    <updated>2024-10-27T21:57:18+08:00</updated>
    <id>https://zhgchg.li/posts/zrealm-robotic-process-automation/70aeddb1fd9b</id><summary type="html">透過 linkyee 範本在 GitHub Pages 免費部署專屬連結頁，支援自訂網域與動態追蹤數據，免廣告且可客製化樣式，幾步驟完成快速上線，提升個人品牌曝光與連結管理效率。</summary><author>
      <name>ZhgChgLi</name>
    </author><category term="ZRealm Robotic Process Automation" /><category term="linktree" /><category term="github-pages" /><category term="automation" /><category term="ruby" /><category term="jekyll" /><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://zhgchg.li/assets/70aeddb1fd9b/1*CbiCUtVY5CV4wRXBaZBoyw.webp" /><content type="html" xml:base="https://zhgchg.li/posts/zrealm-robotic-process-automation/github-pages-%E5%85%8D%E8%B2%BB%E5%BF%AB%E9%80%9F%E5%BB%BA%E7%BD%AE%E5%80%8B%E4%BA%BA-linktree-%E9%80%A3%E7%B5%90%E9%A0%81%E9%9D%A2-linkyee-%E9%96%8B%E6%BA%90%E6%A8%A1%E6%9D%BF%E6%95%99%E5%AD%B8-70aeddb1fd9b/"><![CDATA[<h3 id="linkyee--使用-github-pages-快速免費建立個人類-linktree-連結頁面">linkyee — 使用 GitHub Pages 快速免費建立個人類 LinkTree 連結頁面</h3>

<p>使用 GitHub Pages 快速建立自己的連結頁面，100% 免費、客製化並支援自訂義網域</p>

<h3 id="成果">成果</h3>

<p><img src="/assets/70aeddb1fd9b/1*EtG_srpR0i0BRE1dziNDXg.webp" alt="https://link.zhgchg.li" loading="lazy" decoding="async" width="1400" height="948" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9Ijk0OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><a href="https://link.zhgchg.li" target="_blank">https://link.zhgchg.li</a></p>

<blockquote>
  <p><strong><em>我以把成果開源並打包成 Template Repo (linkyee)，有需要的朋友可以直接 Fork 快速部署使用。</em></strong></p>
</blockquote>

<h3 id="linkyee--your-own-link-pages">linkyee — Your Own Link Pages</h3>

<p><a href="https://github.com/ZhgChgLi/linkyee" target="_blank"><img src="https://repository-images.githubusercontent.com/877945203/b2d2ec07-9a56-400c-b24c-db0b180f7d3e" alt="" /></a></p>

<h4 id="優點">優點：</h4>
<ul>
  <li>直接部署在 Github Pages 穩定、免費</li>
  <li>掌握 HTML 原始檔案，可隨意修改排版、樣式、移除廣告、版權宣告； <em>(預設樣式就是我用 GenAI ChatGPT 建立的)</em></li>
  <li>支援自訂域名</li>
  <li><strong>支援動態變數，例如我預設建了 Medium 追蹤者、Github Repo 星星數變數，可自動帶入、更新追蹤者數量在頁面上。</strong> 🚀🚀🚀</li>
  <li>頁面載入快速</li>
  <li>照本文簡單幾步就能完成設定部署</li>
</ul>

<h3 id="github-pages">Github Pages</h3>

<p>Github Pages 是由 Github 提供的免費靜態頁面寄存服務，所有 Github Free 帳號只要是 Public Repo 都能直接使用，如果是 Private Repo 則須先付費升級 Github 帳號。</p>
<h4 id="限制">限制</h4>
<ul>
  <li><strong>只能寄存靜態檔案資源：</strong> HTML, CSS, JavaScript, 字體檔案, 圖片檔案, PDF, 音訊檔案, 文字檔案…等等</li>
  <li><strong>網站(Repo)大小不得超過：</strong> 1 GB 這猜測也是軟限制，因為我的 Github Pages Jekyll Repo 已經快 5 GB 了。</li>
  <li><strong>部署工作時間最長：</strong> 10 分鐘</li>
  <li><strong>每小時最多部署：</strong> 10 次 (軟限制)</li>
  <li><strong>每月流量限制</strong> ：100 GB (軟限制)</li>
  <li>請求過於頻繁可能會響應 HTTP 429</li>
</ul>

<h4 id="其他-github-pages-應用文章">其他 Github Pages 應用文章</h4>
<ul>
  <li><a href="/posts/zrealm-dev/無痛轉移-medium-文章到自架-jekyll-靜態網站-快速部署與自動同步教學-a0c08d579ab1/">我使用 Github Pages 部署的 Jekyll Blog</a> ➡️ <a href="https://zhgchg.li/" target="_blank"><strong>ZhgChgLi</strong></a></li>
  <li><a href="/posts/zrealm-dev/無痛轉移-medium-文章到自架-jekyll-靜態網站-快速部署與自動同步教學-a0c08d579ab1/">無痛轉移 Medium 到 Github Pages</a></li>
  <li><a href="/posts/zrealm-dev/github-pages-自訂網域教學-namecheap-購域設定完整流程與部署指引-483af5d93297/"><strong>Github Pages 自訂網域教學</strong></a></li>
</ul>

<h3 id="開始使用--部署在-github-pages">開始使用 — 部署在 GitHub Pages</h3>
<h4 id="步驟-1-點擊-linkyee-範本庫右上角的use-this-template按鈕--create-a-new-repository">步驟 1. 點擊 linkyee 範本庫右上角的「Use this template」按鈕 -&gt;「Create a new repository」：</h4>

<p><img src="/assets/70aeddb1fd9b/1*G6pU845OnIyEdl-Os0EnwQ.webp" alt="" loading="lazy" decoding="async" width="1200" height="993" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijk5MyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<h4 id="步驟-2-勾選include-all-branches輸入您想要的-github-pages-倉庫名稱完成後點擊create-repository">步驟 2. 勾選「Include all branches」，輸入您想要的 GitHub Pages 倉庫名稱，完成後點擊「Create repository」：</h4>

<p><img src="/assets/70aeddb1fd9b/1*PN9zygdxqJmFtUz9Pq35cQ.webp" alt="" loading="lazy" decoding="async" width="1200" height="993" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijk5MyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<blockquote>
  <p><em>GitHub Pages Repo 名稱就會是訪問網址。</em></p>
</blockquote>

<blockquote>
  <p><em>如果將 Repo 名稱輸入為 <code class="language-plaintext highlighter-rouge">your-username.github.io</code> ，那麼這將成為您的 GitHub Pages 網站的直接訪問網址。</em></p>
</blockquote>

<blockquote>
  <p><em>如果您已有 <code class="language-plaintext highlighter-rouge">your-username.github.io</code> Repo，那麼 GitHub Pages 網址將會是 <code class="language-plaintext highlighter-rouge">your-username.github.io/Repo-Name</code> 。</em></p>
</blockquote>

<h4 id="等待-fork-完成初次建立時可能會遇到部署錯誤這是由於-fork-倉庫的權限問題接下來我們按照步驟進行調整">等待 Fork 完成。初次建立時可能會遇到部署錯誤，這是由於 Fork 倉庫的權限問題。接下來我們按照步驟進行調整。</h4>

<p><img src="/assets/70aeddb1fd9b/1*EYXix1zABfKXboAxkn_-yw.webp" alt="" loading="lazy" decoding="async" width="374" height="74" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzNzQiIGhlaWdodD0iNzQiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<h4 id="步驟-4-前往-settings---actions---general確保選擇以下選項">步驟 4. 前往 Settings -&gt; Actions -&gt; General，確保選擇以下選項：</h4>

<p><img src="/assets/70aeddb1fd9b/1*5c4TZm0ZjolIPPalwEbJMA.webp" alt="" loading="lazy" decoding="async" width="1200" height="993" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijk5MyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<ul>
  <li>Actions permissions: Allow all actions and reusable workflows</li>
  <li>Workflow permissions: Read and write permissions</li>
</ul>

<p><strong>選擇完畢後，點擊 Save 按鈕儲存變更。</strong></p>
<h4 id="步驟-5-前往-settings---pages確認-github-pages-的分支設定為gh-pages">步驟 5. 前往 Settings -&gt; Pages，確認 GitHub Pages 的分支設定為「gh-pages」：</h4>

<p><img src="/assets/70aeddb1fd9b/1*2mmeneQOLEuhRqZIovSh9A.webp" alt="" loading="lazy" decoding="async" width="1400" height="1159" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjExNTkiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<blockquote>
  <p><em>「Your site is live at: XXXX」訊息即為您的 GitHub Pages 公開訪問網址。</em></p>
</blockquote>

<h4 id="步驟-6-前往-settings---actions等待第一次部署完成">步驟 6. 前往 Settings -&gt; Actions，等待第一次部署完成：</h4>

<p><img src="/assets/70aeddb1fd9b/1*mFQmtcTZr-OjBhqtHwebEw.webp" alt="" loading="lazy" decoding="async" width="1400" height="1159" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjExNTkiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<h4 id="步驟-7-訪問-github-pages-網址確認-fork-是否成功">步驟 7. 訪問 GitHub Pages 網址，確認 Fork 是否成功：</h4>

<p><img src="/assets/70aeddb1fd9b/1*1vaJpnwjZtsWEjBvKcGjVw.webp" alt="" loading="lazy" decoding="async" width="1200" height="993" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijk5MyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<blockquote>
  <p>恭喜！部署成功，現在您可以修改設定檔案，將其替換為您的資料。🎉🎉🎉</p>
</blockquote>

<h3 id="設定">設定</h3>
<h4 id="設定檔案">設定檔案</h4>

<p>編輯位於根目錄中的 config.yml 文件。</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># 網站配置  </span>

<span class="c1"># 主題名稱，對應至目錄：./theme/xxxx  </span>
<span class="na">theme</span><span class="pi">:</span> <span class="s">default</span>  

<span class="c1"># HTML 語言設定  </span>
<span class="na">lang</span><span class="pi">:</span> <span class="s2">"</span><span class="s">en"</span>  

<span class="c1"># 插件（實作於 ./plugins/PLUGIN_NAME）  </span>
<span class="c1"># 在下方設定中使用 {{ vars.PLUGIN_NAME }}  </span>

<span class="c1"># 插件的輸出結果可在下方使用，例如：{{vars.MediumFollowersCountPlugin}}  </span>
<span class="na">plugins</span><span class="pi">:</span>  
  <span class="c1"># 自動獲取 Medium 的追蹤者數量  </span>
  <span class="pi">-</span> <span class="na">MediumFollowersCountPlugin</span><span class="pi">:</span>  
      <span class="na">username</span><span class="pi">:</span> <span class="s">zhgchgli</span>  
  <span class="c1"># 自動獲取 GitHub 倉庫的星標數量  </span>
  <span class="pi">-</span> <span class="na">GithubRepoStarsCountPlugin</span><span class="pi">:</span>  
      <span class="pi">-</span> <span class="s">ZhgChgLi/ZMarkupParser</span>  
      <span class="pi">-</span> <span class="s">ZhgChgLi/ZReviewTender</span>  
      <span class="pi">-</span> <span class="s">ZhgChgLi/ZMediumToMarkdown</span>  
      <span class="pi">-</span> <span class="s">ZhgChgLi/linkyee</span>  

<span class="c1"># Google Analytics 追蹤 ID  </span>
<span class="na">google_analytics_id</span><span class="pi">:</span>  

<span class="c1"># HTML 標題  </span>
<span class="na">title</span><span class="pi">:</span> <span class="s2">"</span><span class="s">ZhgChgLi</span><span class="nv"> </span><span class="s">的連結集"</span>  

<span class="c1"># 頭像圖片路徑  </span>
<span class="na">avatar</span><span class="pi">:</span> <span class="s2">"</span><span class="s">./images/profile.jpeg"</span>  

<span class="c1"># 名稱區塊文字  </span>
<span class="na">name</span><span class="pi">:</span> <span class="s2">"</span><span class="s">@zhgchgli"</span>  

<span class="c1"># 標語區塊文字  </span>
<span class="na">tagline</span><span class="pi">:</span> <span class="pi">&gt;-</span>  
    <span class="s">一位來自台灣的 iOS、網頁與自動化開發者，熱愛分享、旅行與寫作。  </span>

<span class="c1"># 連結列表  </span>
<span class="c1"># icon：使用 Font Awesome 圖示 (https://fontawesome.com/search?o=r&amp;m=free)  </span>
<span class="c1"># text：顯示在連結上的文字  </span>
<span class="c1"># title：連結的標題  </span>
<span class="c1"># url：連結的網址  </span>
<span class="c1"># alt：替代文字（無障礙使用）  </span>
<span class="c1"># target：`_blank` 在新頁籤打開，`_self` 在同頁面打開  </span>
<span class="na">links</span><span class="pi">:</span>  
  <span class="pi">-</span> <span class="na">link</span><span class="pi">:</span>  
      <span class="na">icon</span><span class="pi">:</span> <span class="s2">"</span><span class="s">fa-brands</span><span class="nv"> </span><span class="s">fa-medium"</span>  
      <span class="na">text</span><span class="pi">:</span> <span class="s2">"</span><span class="s">技術部落格</span><span class="nv"> </span><span class="s">&lt;span</span><span class="nv"> </span><span class="s">class='link-button-text'&gt;({{vars.MediumFollowersCountPlugin}}</span><span class="nv"> </span><span class="s">Followers)&lt;/span&gt;"</span>  
      <span class="na">url</span><span class="pi">:</span> <span class="s2">"</span><span class="s">https://blog.zhgchg.li"</span>  
      <span class="na">alt</span><span class="pi">:</span> <span class="s2">"</span><span class="s">ZhgChgLi</span><span class="nv"> </span><span class="s">的技術部落格"</span>  
      <span class="na">title</span><span class="pi">:</span> <span class="s2">"</span><span class="s">ZhgChgLi</span><span class="nv"> </span><span class="s">的技術部落格"</span>  
      <span class="na">target</span><span class="pi">:</span> <span class="s2">"</span><span class="s">_blank"</span>  
  <span class="pi">-</span> <span class="na">link</span><span class="pi">:</span>  
      <span class="na">icon</span><span class="pi">:</span> <span class="s2">"</span><span class="s">fa-brands</span><span class="nv"> </span><span class="s">fa-medium"</span>  
      <span class="na">text</span><span class="pi">:</span> <span class="s2">"</span><span class="s">旅行日誌</span><span class="nv"> </span><span class="s">&lt;span</span><span class="nv"> </span><span class="s">class='link-button-text'&gt;({{vars.MediumFollowersCountPlugin}}</span><span class="nv"> </span><span class="s">Followers)&lt;/span&gt;"</span>  
      <span class="na">url</span><span class="pi">:</span> <span class="s2">"</span><span class="s">https://medium.com/ztravel"</span>  
      <span class="na">alt</span><span class="pi">:</span> <span class="s2">"</span><span class="s">ZhgChgLi</span><span class="nv"> </span><span class="s">的旅行日誌"</span>  
      <span class="na">title</span><span class="pi">:</span> <span class="s2">"</span><span class="s">ZhgChgLi</span><span class="nv"> </span><span class="s">的旅行日誌"</span>  
      <span class="na">target</span><span class="pi">:</span> <span class="s2">"</span><span class="s">_blank"</span>  
  <span class="pi">-</span> <span class="na">link</span><span class="pi">:</span>  
      <span class="na">icon</span><span class="pi">:</span> <span class="s2">"</span><span class="s">fa-solid</span><span class="nv"> </span><span class="s">fa-rss"</span>  
      <span class="na">text</span><span class="pi">:</span> <span class="s2">"</span><span class="s">個人網站"</span>  
      <span class="na">url</span><span class="pi">:</span> <span class="s2">"</span><span class="s">https://zhgchg.li/"</span>  
      <span class="na">alt</span><span class="pi">:</span> <span class="s2">"</span><span class="s">ZhgChgLi</span><span class="nv"> </span><span class="s">的網站"</span>  
      <span class="na">title</span><span class="pi">:</span> <span class="s2">"</span><span class="s">ZhgChgLi</span><span class="nv"> </span><span class="s">的網站"</span>  
      <span class="na">target</span><span class="pi">:</span> <span class="s2">"</span><span class="s">_blank"</span>  
  <span class="pi">-</span> <span class="na">link</span><span class="pi">:</span>  
      <span class="na">icon</span><span class="pi">:</span> <span class="s2">"</span><span class="s">fa-brands</span><span class="nv"> </span><span class="s">fa-swift"</span>  
      <span class="na">text</span><span class="pi">:</span> <span class="s2">"</span><span class="s">ZMarkupParser</span><span class="nv"> </span><span class="s">&lt;span</span><span class="nv"> </span><span class="s">class='link-button-text'&gt;({{vars.GithubRepoStarsCountPlugin['ZhgChgLi/ZMarkupParser']}}</span><span class="nv"> </span><span class="s">Stars)&lt;/span&gt;"</span>  
      <span class="na">url</span><span class="pi">:</span> <span class="s2">"</span><span class="s">https://github.com/ZhgChgLi/ZMarkupParser"</span>  
      <span class="na">alt</span><span class="pi">:</span> <span class="s2">"</span><span class="s">ZMarkupParser</span><span class="nv"> </span><span class="s">是一個純</span><span class="nv"> </span><span class="s">Swift</span><span class="nv"> </span><span class="s">庫，用於將</span><span class="nv"> </span><span class="s">HTML</span><span class="nv"> </span><span class="s">字串轉換為具自訂樣式的</span><span class="nv"> </span><span class="s">NSAttributedString。"</span>  
      <span class="na">title</span><span class="pi">:</span> <span class="s2">"</span><span class="s">ZMarkupParser</span><span class="nv"> </span><span class="s">是一個純</span><span class="nv"> </span><span class="s">Swift</span><span class="nv"> </span><span class="s">庫，用於將</span><span class="nv"> </span><span class="s">HTML</span><span class="nv"> </span><span class="s">字串轉換為具自訂樣式的</span><span class="nv"> </span><span class="s">NSAttributedString。"</span>  
      <span class="na">target</span><span class="pi">:</span> <span class="s2">"</span><span class="s">_blank"</span>  
  <span class="pi">-</span> <span class="na">link</span><span class="pi">:</span>  
      <span class="na">icon</span><span class="pi">:</span> <span class="s2">"</span><span class="s">fa-brands</span><span class="nv"> </span><span class="s">fa-app-store-ios"</span>  
      <span class="na">text</span><span class="pi">:</span> <span class="s2">"</span><span class="s">ZReviewTender</span><span class="nv"> </span><span class="s">&lt;span</span><span class="nv"> </span><span class="s">class='link-button-text'&gt;({{vars.GithubRepoStarsCountPlugin['ZhgChgLi/ZReviewTender']}}</span><span class="nv"> </span><span class="s">Stars)&lt;/span&gt;"</span>  
      <span class="na">url</span><span class="pi">:</span> <span class="s2">"</span><span class="s">https://github.com/ZhgChgLi/ZReviewTender"</span>  
      <span class="na">alt</span><span class="pi">:</span> <span class="s2">"</span><span class="s">ZReviewTender</span><span class="nv"> </span><span class="s">是一個從</span><span class="nv"> </span><span class="s">App</span><span class="nv"> </span><span class="s">Store</span><span class="nv"> </span><span class="s">和</span><span class="nv"> </span><span class="s">Google</span><span class="nv"> </span><span class="s">Play</span><span class="nv"> </span><span class="s">Console</span><span class="nv"> </span><span class="s">獲取應用評論並整合至工作流程的工具。"</span>  
      <span class="na">title</span><span class="pi">:</span> <span class="s2">"</span><span class="s">ZReviewTender</span><span class="nv"> </span><span class="s">是一個從</span><span class="nv"> </span><span class="s">App</span><span class="nv"> </span><span class="s">Store</span><span class="nv"> </span><span class="s">和</span><span class="nv"> </span><span class="s">Google</span><span class="nv"> </span><span class="s">Play</span><span class="nv"> </span><span class="s">Console</span><span class="nv"> </span><span class="s">獲取應用評論並整合至工作流程的工具。"</span>  
      <span class="na">target</span><span class="pi">:</span> <span class="s2">"</span><span class="s">_blank"</span>  
  <span class="pi">-</span> <span class="na">link</span><span class="pi">:</span>  
      <span class="na">icon</span><span class="pi">:</span> <span class="s2">"</span><span class="s">fa-brands</span><span class="nv"> </span><span class="s">fa-markdown"</span>  
      <span class="na">text</span><span class="pi">:</span> <span class="s2">"</span><span class="s">ZMediumToMarkdown</span><span class="nv"> </span><span class="s">&lt;span</span><span class="nv"> </span><span class="s">class='link-button-text'&gt;({{vars.GithubRepoStarsCountPlugin['ZhgChgLi/ZMediumToMarkdown']}}</span><span class="nv"> </span><span class="s">Stars)&lt;/span&gt;"</span>  
      <span class="na">url</span><span class="pi">:</span> <span class="s2">"</span><span class="s">https://github.com/ZhgChgLi/ZMediumToMarkdown"</span>  
      <span class="na">alt</span><span class="pi">:</span> <span class="s2">"</span><span class="s">ZMediumToMarkdown</span><span class="nv"> </span><span class="s">是一個強大的工具，可輕鬆下載並將</span><span class="nv"> </span><span class="s">Medium</span><span class="nv"> </span><span class="s">文章轉換為</span><span class="nv"> </span><span class="s">Markdown</span><span class="nv"> </span><span class="s">格式。"</span>  
      <span class="na">title</span><span class="pi">:</span> <span class="s2">"</span><span class="s">ZMediumToMarkdown</span><span class="nv"> </span><span class="s">是一個強大的工具，可輕鬆下載並將</span><span class="nv"> </span><span class="s">Medium</span><span class="nv"> </span><span class="s">文章轉換為</span><span class="nv"> </span><span class="s">Markdown</span><span class="nv"> </span><span class="s">格式。"</span>  
      <span class="na">target</span><span class="pi">:</span> <span class="s2">"</span><span class="s">_blank"</span>  
  <span class="pi">-</span> <span class="na">link</span><span class="pi">:</span>  
      <span class="na">icon</span><span class="pi">:</span> <span class="s2">"</span><span class="s">fa-brands</span><span class="nv"> </span><span class="s">fa-github"</span>  
      <span class="na">text</span><span class="pi">:</span> <span class="s2">"</span><span class="s">linkyee</span><span class="nv"> </span><span class="s">&lt;span</span><span class="nv"> </span><span class="s">class='link-button-text'&gt;({{vars.GithubRepoStarsCountPlugin['ZhgChgLi/linkyee']}}</span><span class="nv"> </span><span class="s">Stars)&lt;/span&gt;"</span>  
      <span class="na">url</span><span class="pi">:</span> <span class="s2">"</span><span class="s">https://github.com/ZhgChgLi/linkyee"</span>  
      <span class="na">alt</span><span class="pi">:</span> <span class="s2">"</span><span class="s">linkyee</span><span class="nv"> </span><span class="s">是一個完全自訂且開源的</span><span class="nv"> </span><span class="s">LinkTree</span><span class="nv"> </span><span class="s">替代方案，可直接部署在</span><span class="nv"> </span><span class="s">GitHub</span><span class="nv"> </span><span class="s">Pages</span><span class="nv"> </span><span class="s">上。"</span>  
      <span class="na">title</span><span class="pi">:</span> <span class="s2">"</span><span class="s">linkyee</span><span class="nv"> </span><span class="s">是一個完全自訂且開源的</span><span class="nv"> </span><span class="s">LinkTree</span><span class="nv"> </span><span class="s">替代方案，可直接部署在</span><span class="nv"> </span><span class="s">GitHub</span><span class="nv"> </span><span class="s">Pages</span><span class="nv"> </span><span class="s">上。"</span>  
      <span class="na">target</span><span class="pi">:</span> <span class="s2">"</span><span class="s">_blank"</span>  

<span class="c1"># 社交媒體連結列表  </span>
<span class="c1"># icon：使用 Font Awesome 圖示 (https://fontawesome.com/search?o=r&amp;m=free)  </span>
<span class="c1"># title：連結的標題  </span>
<span class="c1"># url：社交媒體連結的網址  </span>
<span class="c1"># alt：替代文字（無障礙使用）  </span>
<span class="c1"># target：`_blank` 在新頁籤打開，`_self` 在同頁面打開  </span>
<span class="na">socials</span><span class="pi">:</span>  
  <span class="pi">-</span> <span class="na">social</span><span class="pi">:</span>  
      <span class="na">icon</span><span class="pi">:</span> <span class="s2">"</span><span class="s">fa-brands</span><span class="nv"> </span><span class="s">fa-medium"</span>  
      <span class="na">url</span><span class="pi">:</span> <span class="s2">"</span><span class="s">https://blog.zhgchg.li"</span>  
      <span class="na">title</span><span class="pi">:</span> <span class="s2">"</span><span class="s">ZhgChgLi</span><span class="nv"> </span><span class="s">的</span><span class="nv"> </span><span class="s">Medium"</span>  
      <span class="na">alt</span><span class="pi">:</span> <span class="s2">"</span><span class="s">ZhgChgLi</span><span class="nv"> </span><span class="s">的</span><span class="nv"> </span><span class="s">Medium"</span>  
      <span class="na">target</span><span class="pi">:</span> <span class="s2">"</span><span class="s">_blank"</span>  
  <span class="pi">-</span> <span class="na">social</span><span class="pi">:</span>  
      <span class="na">icon</span><span class="pi">:</span> <span class="s2">"</span><span class="s">fa-brands</span><span class="nv"> </span><span class="s">fa-github"</span>  
      <span class="na">url</span><span class="pi">:</span> <span class="s2">"</span><span class="s">https://github.com/ZhgChgLi"</span>  
      <span class="na">title</span><span class="pi">:</span> <span class="s2">"</span><span class="s">ZhgChgLi</span><span class="nv"> </span><span class="s">的</span><span class="nv"> </span><span class="s">GitHub"</span>  
      <span class="na">alt</span><span class="pi">:</span> <span class="s2">"</span><span class="s">ZhgChgLi</span><span class="nv"> </span><span class="s">的</span><span class="nv"> </span><span class="s">GitHub"</span>  
      <span class="na">target</span><span class="pi">:</span> <span class="s2">"</span><span class="s">_blank"</span>  
  <span class="pi">-</span> <span class="na">social</span><span class="pi">:</span>  
      <span class="na">icon</span><span class="pi">:</span> <span class="s2">"</span><span class="s">fa-brands</span><span class="nv"> </span><span class="s">fa-twitter"</span>  
      <span class="na">url</span><span class="pi">:</span> <span class="s2">"</span><span class="s">https://twitter.com/zhgchgli"</span>  
      <span class="na">title</span><span class="pi">:</span> <span class="s2">"</span><span class="s">ZhgChgLi</span><span class="nv"> </span><span class="s">的</span><span class="nv"> </span><span class="s">Twitter"</span>  
      <span class="na">alt</span><span class="pi">:</span> <span class="s2">"</span><span class="s">ZhgChgLi</span><span class="nv"> </span><span class="s">的</span><span class="nv"> </span><span class="s">Twitter"</span>  
      <span class="na">target</span><span class="pi">:</span> <span class="s2">"</span><span class="s">_blank"</span>  
  <span class="pi">-</span> <span class="na">social</span><span class="pi">:</span>  
      <span class="na">icon</span><span class="pi">:</span> <span class="s2">"</span><span class="s">fa-brands</span><span class="nv"> </span><span class="s">fa-linkedin"</span>  
      <span class="na">url</span><span class="pi">:</span> <span class="s2">"</span><span class="s">https://www.linkedin.com/in/zhgchgli/"</span>  
      <span class="na">title</span><span class="pi">:</span> <span class="s2">"</span><span class="s">ZhgChgLi</span><span class="nv"> </span><span class="s">的</span><span class="nv"> </span><span class="s">LinkedIn"</span>  
      <span class="na">alt</span><span class="pi">:</span> <span class="s2">"</span><span class="s">ZhgChgLi</span><span class="nv"> </span><span class="s">的</span><span class="nv"> </span><span class="s">LinkedIn"</span>  
      <span class="na">target</span><span class="pi">:</span> <span class="s2">"</span><span class="s">_blank"</span>  
  <span class="pi">-</span> <span class="na">social</span><span class="pi">:</span>  
      <span class="na">icon</span><span class="pi">:</span> <span class="s2">"</span><span class="s">fa-brands</span><span class="nv"> </span><span class="s">fa-instagram"</span>  
      <span class="na">url</span><span class="pi">:</span> <span class="s2">"</span><span class="s">https://www.instagram.com/zhgchgli/"</span>  
      <span class="na">title</span><span class="pi">:</span> <span class="s2">"</span><span class="s">Instagram"</span>  
      <span class="na">alt</span><span class="pi">:</span> <span class="s2">"</span><span class="s">ZhgChgLi</span><span class="nv"> </span><span class="s">的</span><span class="nv"> </span><span class="s">Instagram"</span>  
      <span class="na">target</span><span class="pi">:</span> <span class="s2">"</span><span class="s">_blank"</span>  
  <span class="pi">-</span> <span class="na">social</span><span class="pi">:</span>  
      <span class="na">icon</span><span class="pi">:</span> <span class="s2">"</span><span class="s">fa-solid</span><span class="nv"> </span><span class="s">fa-envelope"</span>  
      <span class="na">url</span><span class="pi">:</span> <span class="s2">"</span><span class="s">zhgchgli@gmail.com"</span>  
      <span class="na">title</span><span class="pi">:</span> <span class="s2">"</span><span class="s">Email:</span><span class="nv"> </span><span class="s">zhgchgli@gmail.com"</span>  
      <span class="na">alt</span><span class="pi">:</span> <span class="s2">"</span><span class="s">zhgchgli@gmail.com"</span>  
      <span class="na">target</span><span class="pi">:</span> <span class="s2">"</span><span class="s">_blank"</span>  

<span class="c1"># 頁尾文字  </span>
<span class="na">footer</span><span class="pi">:</span> <span class="pi">&gt;</span>  
    <span class="s">歡迎來到我的網站！追蹤我在 Medium 或 GitHub 上的最新動態，或在 Instagram 和 LinkedIn 上保持聯繫。  </span>

<span class="c1"># 頁尾版權聲明  </span>
<span class="c1"># Linkyee 是一個 100% 免費的開源專案—您可以隨意修改版權聲明。:)  </span>
<span class="na">copyright</span><span class="pi">:</span> <span class="pi">&gt;</span>  
  <span class="s">© 2024 &lt;a href="https://zhgchg.li" target="_blank"&gt;ZhgChgLi&lt;/a&gt;。由 &lt;a href="https://github.com/ZhgChgLi/linkyee" target="_blank"&gt;linkyee&lt;/a&gt; 提供技術支持。  </span>
</code></pre></div></div>

<blockquote>
  <p><strong><em>請注意，每次修改文件後，您需要等待 GitHub Actions 完成自動建置和頁面建置與部署任務。</em></strong></p>
</blockquote>

<p><img src="/assets/70aeddb1fd9b/1*56qGAyuECrqDJQMoKbPiOw.webp" alt="" loading="lazy" decoding="async" width="1200" height="999" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijk5OSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<blockquote>
  <p><strong><em>重新整理頁面以使變更生效。🚀</em></strong></p>
</blockquote>

<p><img src="/assets/70aeddb1fd9b/1*CbiCUtVY5CV4wRXBaZBoyw.webp" alt="" loading="lazy" decoding="async" width="1200" height="809" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjgwOSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>成功！！</p>
<h4 id="自訂風格樣式修改預設主題">自訂風格樣式、修改預設主題</h4>
<ul>
  <li><code class="language-plaintext highlighter-rouge">./themes/default/index.html</code></li>
  <li><code class="language-plaintext highlighter-rouge">./themes/default/styles.css</code></li>
  <li><code class="language-plaintext highlighter-rouge">./themes/default/scripts.js</code></li>
</ul>

<h4 id="創建新主題">創建新主題</h4>
<ul>
  <li>./themes/ <code class="language-plaintext highlighter-rouge">YOUR_THEME</code></li>
  <li>在 config.yml 文件中更新為 <code class="language-plaintext highlighter-rouge">theme:YOUR_THEME</code></li>
</ul>

<p><strong>沒錯，您可以使用像 ChatGPT 這樣的 GenAI 工具，來幫助您創建自訂的連結頁面！(預設樣式也是我用 ChatGPT 產的)</strong></p>
<h4 id="自動重新部署">自動重新部署</h4>

<p>預設情況下，專案每天會自動重新部署一次，以刷新插件的動態變數值。您可以在 <a href="https://github.com/ZhgChgLi/linkyee/blob/main/.github/workflows/build.yml" target="_blank">Github Action — Automatic build (.github/workflows/build.yml)</a> 中調整 cron 設定：</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">schedule</span><span class="pi">:</span>
 <span class="pi">-</span> <span class="na">cron</span><span class="pi">:</span> <span class="s1">'</span><span class="s">0</span><span class="nv"> </span><span class="s">0</span><span class="nv"> </span><span class="s">*</span><span class="nv"> </span><span class="s">*</span><span class="nv"> </span><span class="s">*'</span> <span class="c1"># 每日午夜 00:00 (UTC) 執行</span>
</code></pre></div></div>

<p>如果不需要定時重新部署，可以直接刪除 <code class="language-plaintext highlighter-rouge">schedule</code> 區塊。</p>
<h3 id="自訂網域-️️️">自訂網域 ❤️❤️❤️</h3>

<p>您可以設置自訂的 GitHub Pages 網域， <strong>例如我的： <a href="https://link.zhgchg.li" target="_blank">https://link.zhgchg.li</a> 。</strong></p>

<p>可以找我之前的文章「 <a href="/posts/zrealm-dev/github-pages-自訂網域教學-namecheap-購域設定完整流程與部署指引-483af5d93297/"><strong>Github Pages 自訂網域教學</strong></a> 」一步一步從購買到綁定網域，也可以通過 <a href="https://namecheap.pxf.io/P0jdZQ" target="_blank"><strong>我的 Namecheap 推廣連結購買網域</strong></a> — 我將獲得部分分潤，這將幫助我繼續貢獻開源專案。</p>
<h3 id="buy-me-a-coffee-️️️">Buy me a coffee ❤️❤️❤️</h3>

<p><img src="/assets/70aeddb1fd9b/1*QCQqlZr6doDP-cszzpaSpw.webp" alt="https://www.paypal.com/ncp/payment/CMALMPT8UUTY2" loading="lazy" decoding="async" width="1090" height="306" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDkwIiBoZWlnaHQ9IjMwNiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><a href="https://www.paypal.com/ncp/payment/CMALMPT8UUTY2" target="_blank">https://www.paypal.com/ncp/payment/CMALMPT8UUTY2</a></p>

<p>如果這個專案對您有幫助，請考慮 <a href="https://github.com/ZhgChgLi/linkyee" target="_blank">Star Repo、推薦給朋友</a> ， <strong>或 <a href="https://www.paypal.com/ncp/payment/CMALMPT8UUTY2" target="_blank">贊助我一杯咖啡。感謝您的支持！</a></strong></p>

<p>歡迎提出 Issue，或透過 Pull Request 提交修正或貢獻。:)</p>]]></content>
  </entry><entry>
    <title type="html">GA4 自動數據通知機器人｜3 步驟用 Google Apps Script 串接 Telegram Bot</title>
    <link href="https://zhgchg.li/posts/zrealm-robotic-process-automation/ga4-%E8%87%AA%E5%8B%95%E6%95%B8%E6%93%9A%E9%80%9A%E7%9F%A5%E6%A9%9F%E5%99%A8%E4%BA%BA-3-%E6%AD%A5%E9%A9%9F%E7%94%A8-google-apps-script-%E4%B8%B2%E6%8E%A5-telegram-bot-1e85b8df2348/" rel="alternate" type="text/html" title="GA4 自動數據通知機器人｜3 步驟用 Google Apps Script 串接 Telegram Bot" />
    <published>2024-10-20T16:19:52+08:00</published>
    <updated>2024-10-20T16:19:52+08:00</updated>
    <id>https://zhgchg.li/posts/zrealm-robotic-process-automation/1e85b8df2348</id><summary type="html">針對希望即時掌握網站數據的使用者，教你用 3 步驟打造免費 GA4 自動通知機器人，結合 Google Apps Script 與 Telegram Bot，定時推送近 7 天流量與熱門頁面，減少人工查詢時間，提升數據監控效率。</summary><author>
      <name>ZhgChgLi</name>
    </author><category term="ZRealm Robotic Process Automation" /><category term="automation" /><category term="google-apps-script" /><category term="telegram" /><category term="google-analytics" /><category term="rpa-solutions" /><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://zhgchg.li/assets/1e85b8df2348/1*La0AKKSrGNP9EZUV-vrONQ.webp" /><content type="html" xml:base="https://zhgchg.li/posts/zrealm-robotic-process-automation/ga4-%E8%87%AA%E5%8B%95%E6%95%B8%E6%93%9A%E9%80%9A%E7%9F%A5%E6%A9%9F%E5%99%A8%E4%BA%BA-3-%E6%AD%A5%E9%A9%9F%E7%94%A8-google-apps-script-%E4%B8%B2%E6%8E%A5-telegram-bot-1e85b8df2348/"><![CDATA[<h3 id="簡單-3-步驟--打造免費-ga4-自動數據通知機器人">簡單 3 步驟 — 打造免費 GA4 自動數據通知機器人</h3>

<p>使用 Google Apps Script 完成 RPA，自行串接 GA4 + Telegram Bot 數據通知機器人</p>

<p><img src="/assets/1e85b8df2348/1*La0AKKSrGNP9EZUV-vrONQ.webp" alt="Photo by BoliviaInteligente" loading="lazy" decoding="async" width="1200" height="750" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijc1MCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>Photo by <a href="https://unsplash.com/@boliviainteligente?utm_content=creditCopyText&amp;utm_medium=referral&amp;utm_source=unsplash" target="_blank">BoliviaInteligente</a></p>
<h3 id="前言">前言</h3>

<p>大約從 2020 年開始就自己在摸索使用手邊工具實現 RPA，從一開始只是為了自動化個人例行任務，到後來工作也加入了規模更大型的組織，時常會遇到跨團隊或仰賴人與人的任務、甚至是重複性任務，才發現 RPA 自動化的效益所在。</p>

<p>舉例來說：某個重複任務每個月會發生 10 次，每次需花費 30 分鐘處理，有 60 人會遇到，每年團隊等於消耗 3,600 小時在這上面，如果能投資 100 小時開發成自動化，後續解放出來的時間，就能投入在更有價值的工作上；實際等於 3,600 浪費的工時＋3,600 更值得投資的產出。</p>
<h4 id="詳細可參考我之前的文章"><strong>詳細可參考我之前的文章：</strong></h4>
<ul>
  <li><a href="/posts/pinkoi-engineering/高效率工程團隊大解密-pinkoi-團隊協作與自動化實踐提升工作效率-11f6c8568154/">2021 Pinkoi Tech Career Talk — 高效率工程團隊大解密</a></li>
  <li><a href="/posts/zrealm-robotic-process-automation/google-apps-script-打造每日數據報表自動化-rpa-快速串接-google-analytics-與-google-sheet-f6713ba3fee3/">使用 Google Apps Script 實現每日數據報表 RPA 自動化</a></li>
</ul>

<h4 id="其他做過的-rpa">其他做過的 RPA：</h4>
<ul>
  <li>[GMail to Slack] <a href="/posts/zrealm-robotic-process-automation/google-apps-script-教學-自動轉發-gmail-郵件到-slack-channel-客製化通知-d414bdbdb8c9/">運用 Google Apps Script 轉發 Gmail 信件到 Slack</a></li>
  <li>[Google Form x Google Sheet xSlack] <a href="https://medium.com/zrealm-robotic-process-automation/slack-%E6%89%93%E9%80%A0%E5%85%A8%E8%87%AA%E5%8B%95-wfh-%E5%93%A1%E5%B7%A5%E5%81%A5%E5%BA%B7%E7%8B%80%E6%B3%81%E5%9B%9E%E5%A0%B1%E7%B3%BB%E7%B5%B1-d61062833c1a?source=collection_home---6------9-----------------------" target="_blank">Slack 打造全自動 WFH 員工健康狀況回報系統</a></li>
  <li>[Big Query x Slack ] <a href="https://medium.com/zrealm-robotic-process-automation/crashlytics-big-query-%E6%89%93%E9%80%A0%E6%9B%B4%E5%8D%B3%E6%99%82%E4%BE%BF%E5%88%A9%E7%9A%84-crash-%E8%BF%BD%E8%B9%A4%E5%B7%A5%E5%85%B7-e77b80cc6f89?source=collection_home---6------7-----------------------" target="_blank">Crashlytics + Big Query 打造更即時便利的 Crash 追蹤工具</a></li>
  <li>[Google Analytics x Slack] <a href="https://medium.com/zrealm-robotic-process-automation/crashlytics-google-analytics-%E8%87%AA%E5%8B%95%E6%9F%A5%E8%A9%A2-app-crash-free-users-rate-793cb8f89b72?source=collection_home---6------6-----------------------" target="_blank">Crashlytics + Google Analytics 自動查詢 App Crash-Free Users Rate</a></li>
  <li>[Github Webhook x Line Notify] <a href="https://medium.com/zrealm-robotic-process-automation/%E4%BD%BF%E7%94%A8-google-apps-script-%E4%B8%89%E6%AD%A5%E9%A9%9F%E5%85%8D%E8%B2%BB%E5%BB%BA%E7%AB%8B-github-repo-star-notifier-382218e15697?source=collection_home---6------5-----------------------" target="_blank">使用 Google Apps Script 三步驟免費建立 Github Repo Star Notifier</a></li>
  <li>[Slack x OpenAI (ChatGTP)] <a href="https://medium.com/zrealm-robotic-process-automation/slack-chatgpt-integration-bd94cc88f9c9?source=collection_home---6------4-----------------------" target="_blank">Slack &amp; ChatGPT Integration</a></li>
  <li>[Google Analytics x Google Sheet] <a href="https://medium.com/zrealm-robotic-process-automation/%E4%BD%BF%E7%94%A8-google-apps-script-%E5%AF%A6%E7%8F%BE-google-%E6%9C%8D%E5%8B%99-rpa-%E8%87%AA%E5%8B%95%E5%8C%96-f6713ba3fee3?source=collection_home---6------3-----------------------" target="_blank">使用 Google Apps Script 實現每日數據報表 RPA 自動化</a></li>
  <li>[iOS Shortcut x Line x Reminders] <a href="https://medium.com/zrealm-robotic-process-automation/ios-%E6%8D%B7%E5%BE%91%E8%87%AA%E5%8B%95%E5%8C%96%E6%87%89%E7%94%A8%E5%A0%B4%E6%99%AF-%E8%87%AA%E5%8B%95%E8%BD%89%E7%99%BC%E7%B0%A1%E8%A8%8A%E8%88%87%E8%87%AA%E5%8B%95%E5%BB%BA%E7%AB%8B%E6%8F%90%E9%86%92%E5%BE%85%E8%BE%A6%E4%BA%8B%E9%A0%85-309d0302877b?source=collection_home---6------2-----------------------" target="_blank">iOS 捷徑自動化應用場景 — 自動轉發簡訊與自動建立提醒待辦事項</a></li>
  <li>[Apple Store API x Google Play Console API x Github Action] <a href="https://medium.com/zrealm-robotic-process-automation/quick-start-github-action-x-zreviewtender-%E5%85%8D%E8%B2%BB%E5%BF%AB%E9%80%9F%E9%83%A8%E7%BD%B2%E4%BD%A0%E7%9A%84-app-%E5%95%86%E5%9F%8E%E8%A9%95%E5%83%B9%E7%9B%A3%E6%8E%A7%E6%A9%9F%E5%99%A8%E4%BA%BA-0095528cf875?source=collection_home---6------1-----------------------" target="_blank">Github Action x ZReviewTender 免費快速部署你的 App 商城評價監控機器人</a></li>
  <li>[Telegram Bot] <a href="https://medium.com/zrealm-robotic-process-automation/10-%E5%88%86%E9%90%98%E5%BF%AB%E9%80%9F%E7%A7%BB%E8%BD%89-line-notify-%E5%88%B0-telegram-bot-%E9%80%9A%E7%9F%A5-6922e90ba90c?source=collection_home---6------0-----------------------" target="_blank">10 分鐘快速移轉 Line Notify 到 Telegram Bot 通知</a></li>
  <li>[Medium to Jekyllrb] <a href="/posts/zrealm-dev/無痛轉移-medium-文章到自架-jekyll-靜態網站-快速部署與自動同步教學-a0c08d579ab1/">無痛轉移 Medium 到自架網站</a></li>
</ul>

<p>從後台數據看之前蠻多文章有被 ChatGPT 或各種 GenAI 服務收錄，間接的幫助了許多非工程背景但也想嘗試使用 RPA 解決問題的朋友，因此我仍會持續分享自己生活或工作上遇到的 RPA 場景跟我的解決方案與大家分享 — <a href="/posts/zrealm-robotic-process-automation/google-apps-script-教學-自動轉發-gmail-郵件到-slack-channel-客製化通知-d414bdbdb8c9/">ZRealm Robotic Process Automation</a> 。</p>
<h4 id="工商時間">工商時間</h4>

<p>如果您與您的團隊有自動化工具、流程串接需求，不論是 Slack App 開發、Notion、Asana、Google Sheet、Google Form、GA 數據，各種串接需求，歡迎與我 <a href="https://zhgchg.li/contact/" target="_blank"><strong>聯絡開發</strong></a> 。</p>
<h3 id="本篇-google-analytics-4-x-telegram-bot">本篇 Google Analytics 4 x Telegram Bot</h3>

<p>這次要介紹的串接場景是接續上一篇「 <a href="https://medium.com/zrealm-robotic-process-automation/10-%E5%88%86%E9%90%98%E5%BF%AB%E9%80%9F%E7%A7%BB%E8%BD%89-line-notify-%E5%88%B0-telegram-bot-%E9%80%9A%E7%9F%A5-6922e90ba90c?source=collection_home---6------0-----------------------" target="_blank">10 分鐘快速移轉 Line Notify 到 Telegram Bot 通知</a> 」時想到我的 Medium 備份站「 <a href="https://zhgchg.li/" target="_blank">zhgchg.li</a> 」一直都沒有關注他的 GA4 網站數據，想說好像可以多做一個通知機器人，每日傳送過去 7 天的網站數據到指定的 Telegram Channel 讓我知道。</p>

<p>本篇只是小品，如果要做完整的自動化數據報表請參考之前的文章「 <a href="https://medium.com/zrealm-robotic-process-automation/%E4%BD%BF%E7%94%A8-google-apps-script-%E5%AF%A6%E7%8F%BE-google-%E6%9C%8D%E5%8B%99-rpa-%E8%87%AA%E5%8B%95%E5%8C%96-f6713ba3fee3?source=collection_home---6------3-----------------------" target="_blank">使用 Google Apps Script 實現每日數據報表 RPA 自動化</a> 」另外之前也曾串過 GA4 撈取 App Crash-free rate 可參考此篇文章「 <a href="https://medium.com/zrealm-robotic-process-automation/crashlytics-google-analytics-%E8%87%AA%E5%8B%95%E6%9F%A5%E8%A9%A2-app-crash-free-users-rate-793cb8f89b72?source=collection_home---6------6-----------------------" target="_blank">Crashlytics + Google Analytics 自動查詢 App Crash-Free Users Rate</a> 」。</p>
<ul>
  <li>Google Apps Script 免費限制、詳細使用、部署、功能介紹本篇不會再多介紹，請參考 <a href="https://medium.com/zrealm-robotic-process-automation/%E4%BD%BF%E7%94%A8-google-apps-script-%E5%AF%A6%E7%8F%BE-google-%E6%9C%8D%E5%8B%99-rpa-%E8%87%AA%E5%8B%95%E5%8C%96-f6713ba3fee3?source=collection_home---6------3-----------------------" target="_blank">之前文章</a> 。</li>
  <li>Telegram Bot 創建、使用本篇也不會再多介紹，同樣請參考 <a href="https://medium.com/zrealm-robotic-process-automation/10-%E5%88%86%E9%90%98%E5%BF%AB%E9%80%9F%E7%A7%BB%E8%BD%89-line-notify-%E5%88%B0-telegram-bot-%E9%80%9A%E7%9F%A5-6922e90ba90c?source=collection_home---6------0-----------------------" target="_blank">之前文章</a> 。</li>
</ul>

<h4 id="成果">成果</h4>

<p><img src="/assets/1e85b8df2348/1*J7QdgrJRNzVFQJfW5UBBFA.webp" alt="" loading="lazy" decoding="async" width="555" height="460" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1NTUiIGhlaWdodD0iNDYwIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>先上最終效果，Google Apps Script 每日下午 12–13 點之間會自動去撈取我想要的 Google Analytics 4 網站數據並組合成訊息透過 Telegram Bot 傳送到我的 Telegram Channel，我可以快速檢閱近 7 天網站數據。</p>

<p><strong>我想要觀測的數據是：</strong></p>
<ul>
  <li>近 7 天 <code class="language-plaintext highlighter-rouge">7daysAgo ~ today</code> 總瀏覽數 <code class="language-plaintext highlighter-rouge">screenPageViews</code></li>
  <li>活躍使用者數 <code class="language-plaintext highlighter-rouge">active7DayUsers</code></li>
  <li>新使用者數 <code class="language-plaintext highlighter-rouge">newUsers</code></li>
  <li>Top 10 瀏覽頁面 <code class="language-plaintext highlighter-rouge">screenPageViews</code> / <code class="language-plaintext highlighter-rouge">pageTitle</code></li>
  <li>新使用者最初來源媒介 <code class="language-plaintext highlighter-rouge">newUsers</code> / <code class="language-plaintext highlighter-rouge">firstUserSourceMedium</code></li>
</ul>

<blockquote>
  <p><em>實際可依照你自己的需求使用 <a href="https://ga-dev-tools.google/ga4/query-explorer/" target="_blank">GA Dev Tools</a> 產生調整。</em></p>
</blockquote>

<h3 id="step-1-使用-ga4-query-explorer-官方工具-產生數據報表查詢參數">Step 1. 使用 <a href="https://ga-dev-tools.google/ga4/query-explorer/" target="_blank">GA4 Query Explorer 官方工具</a> 產生數據報表查詢參數</h3>

<p>首先，我們需要使用 <a href="https://ga-dev-tools.google/ga4/query-explorer/" target="_blank">GA4 Query Explorer</a> 官方提供的工具產生我們需要的查詢數據報表參數：</p>

<p><img src="/assets/1e85b8df2348/1*4b1S9nYSmO7OmGgDPllxeQ.webp" alt="" loading="lazy" decoding="async" width="1144" height="1291" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMTQ0IiBoZWlnaHQ9IjEyOTEiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<ol>
  <li>Select Property: 記下你的 <code class="language-plaintext highlighter-rouge">property 編號</code> 
<strong>property 編號稍後撰寫 Google Apps Script 會使用。</strong></li>
  <li>start date, end date: 報表開始～結束的日期範圍，可使用 <code class="language-plaintext highlighter-rouge">YYYY-MM-DD</code> 或 <code class="language-plaintext highlighter-rouge">yesterday</code> , <code class="language-plaintext highlighter-rouge">today</code> , <code class="language-plaintext highlighter-rouge">NdaysAgo</code> 魔術變數。</li>
  <li>metrics: 選擇你想要查詢的指標</li>
  <li>dimensions: 選擇你想要查詢的維度</li>
  <li>metric aggregations: 數據合併計算規則</li>
</ol>

<p>這邊以我的場景為例：</p>
<ol>
  <li>property 編號: <code class="language-plaintext highlighter-rouge">318495208</code></li>
  <li>start_date: <code class="language-plaintext highlighter-rouge">7daysAgo</code></li>
  <li>end_date: <code class="language-plaintext highlighter-rouge">yesterday</code> 
因 GA 數據報表會延遲，查詢前一天～七天最準確。</li>
  <li>metric aggregations: <code class="language-plaintext highlighter-rouge">total</code></li>
</ol>

<p><strong>其他 filter, limit 可依照自己需求設定：</strong></p>

<p><img src="/assets/1e85b8df2348/1*g2psNn3gMZWs4OFRx7phWQ.webp" alt="" loading="lazy" decoding="async" width="832" height="694" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4MzIiIGhlaWdodD0iNjk0Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>filter 我用不到留空；limit 我輸入 10，因為我只想知道 Top 10。</p>
<h4 id="點擊make-request產生對應的數據報表查詢參數及結果"><strong>點擊「MAKE REQUEST」產生對應的數據報表查詢參數及結果：</strong></h4>

<p><img src="/assets/1e85b8df2348/1*Rj1kMTyZiEYGvAztEEngIQ.webp" alt="" loading="lazy" decoding="async" width="808" height="693" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4MDgiIGhlaWdodD0iNjkzIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<h4 id="記下以下請求參數稍後撰寫-google-apps-script-會使用"><strong>記下以下請求參數，稍後撰寫 Google Apps Script 會使用：</strong></h4>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"dimensions"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"pageTitle"</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">],</span><span class="w">
  </span><span class="nl">"metrics"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"screenPageViews"</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">],</span><span class="w">
  </span><span class="nl">"dateRanges"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"startDate"</span><span class="p">:</span><span class="w"> </span><span class="s2">"7daysAgo"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"endDate"</span><span class="p">:</span><span class="w"> </span><span class="s2">"yesterday"</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">],</span><span class="w">
  </span><span class="nl">"limit"</span><span class="p">:</span><span class="w"> </span><span class="s2">"10"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"metricAggregations"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="s2">"TOTAL"</span><span class="w">
  </span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<h4 id="結果"><strong>結果：</strong></h4>

<p><img src="/assets/1e85b8df2348/1*ivAXeRTVB8Y7zbP-Gq2I2A.webp" alt="" loading="lazy" decoding="async" width="801" height="903" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4MDEiIGhlaWdodD0iOTAzIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><img src="/assets/1e85b8df2348/1*FFThyuptIYrGsfddHcjupQ.webp" alt="" loading="lazy" decoding="async" width="1179" height="2556" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMTc5IiBoZWlnaHQ9IjI1NTYiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<ul>
  <li>與 GA 上的數據比較是否正確，正確相符 ✅✅✅</li>
</ul>

<h3 id="step-2-建立-google-apps-script--使用-google-analytics-data-api-查詢資料">Step 2. 建立 Google Apps Script &amp; 使用 Google Analytics Data API 查詢資料</h3>
<ul>
  <li>前往 <a href="https://script.google.com/home" target="_blank">https://script.google.com/home</a></li>
  <li>建立新專案，命名專案名稱</li>
  <li>點擊「服務」-&gt;「+」新增服務</li>
  <li>選擇「Google Analytics Data API」</li>
  <li>點擊「新增」</li>
</ul>

<p><img src="/assets/1e85b8df2348/1*gcMDDQTXd-Gos5AdGUXIOw.webp" alt="" loading="lazy" decoding="async" width="1200" height="963" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijk2MyI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><strong>貼上 Google Analytics Data API 查詢程式碼並組合：</strong></p>

<p>將前面步驟產生的報表查詢數據參數：</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"dimensions"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"pageTitle"</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">],</span><span class="w">
  </span><span class="nl">"metrics"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"screenPageViews"</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">],</span><span class="w">
  </span><span class="nl">"dateRanges"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"startDate"</span><span class="p">:</span><span class="w"> </span><span class="s2">"7daysAgo"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"endDate"</span><span class="p">:</span><span class="w"> </span><span class="s2">"yesterday"</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">],</span><span class="w">
  </span><span class="nl">"limit"</span><span class="p">:</span><span class="w"> </span><span class="s2">"10"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"metricAggregations"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="s2">"TOTAL"</span><span class="w">
  </span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p><strong>轉換成程式：</strong></p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nf">execute</span><span class="p">()</span> <span class="p">{</span>
  <span class="nx">Logger</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="nx">JSON</span><span class="p">.</span><span class="nf">stringify</span><span class="p">(</span><span class="nf">fetchScreenPageViews</span><span class="p">(</span><span class="dl">"</span><span class="s2">318495208</span><span class="dl">"</span><span class="p">)));</span>
<span class="p">}</span>

<span class="c1">// 拆成獨立方法，方便日後重複使用...</span>
<span class="c1">// 預設 startDate=7daysAgo, endDate=yesterday</span>
<span class="c1">// 其他用法：</span>
<span class="c1">// e.g. fetchScreenPageViews("1111". "3daysAgo", "yesterday")</span>
<span class="c1">// e.g. fetchScreenPageViews("2222". "yesterday", "today")</span>
<span class="kd">function</span> <span class="nf">fetchScreenPageViews</span><span class="p">(</span><span class="nx">propertyId</span><span class="p">,</span> <span class="nx">startDate</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">7daysAgo</span><span class="dl">"</span><span class="p">,</span> <span class="nx">endDate</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">yesterday</span><span class="dl">"</span><span class="p">)</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">screenPageViewsMetric</span> <span class="o">=</span> <span class="nx">AnalyticsData</span><span class="p">.</span><span class="nf">newMetric</span><span class="p">();</span>
  <span class="nx">screenPageViewsMetric</span><span class="p">.</span><span class="nx">name</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">screenPageViews</span><span class="dl">"</span><span class="p">;</span>

  <span class="kd">const</span> <span class="nx">dateRange</span> <span class="o">=</span> <span class="nx">AnalyticsData</span><span class="p">.</span><span class="nf">newDateRange</span><span class="p">();</span>
  <span class="nx">dateRange</span><span class="p">.</span><span class="nx">startDate</span> <span class="o">=</span> <span class="nx">startDate</span><span class="p">;</span>
  <span class="nx">dateRange</span><span class="p">.</span><span class="nx">endDate</span> <span class="o">=</span> <span class="nx">endDate</span><span class="p">;</span>

  <span class="kd">const</span> <span class="nx">pageTitleDimension</span> <span class="o">=</span> <span class="nx">AnalyticsData</span><span class="p">.</span><span class="nf">newDimension</span><span class="p">();</span>
  <span class="nx">pageTitleDimension</span><span class="p">.</span><span class="nx">name</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">pageTitle</span><span class="dl">"</span><span class="p">;</span>

  <span class="kd">const</span> <span class="nx">request</span> <span class="o">=</span> <span class="nx">AnalyticsData</span><span class="p">.</span><span class="nf">newRunReportRequest</span><span class="p">();</span>
  <span class="nx">request</span><span class="p">.</span><span class="nx">dimensions</span> <span class="o">=</span> <span class="p">[</span><span class="nx">pageTitleDimension</span><span class="p">];</span>
  <span class="nx">request</span><span class="p">.</span><span class="nx">metrics</span> <span class="o">=</span> <span class="p">[</span><span class="nx">screenPageViewsMetric</span><span class="p">];</span>
  <span class="nx">request</span><span class="p">.</span><span class="nx">dateRanges</span> <span class="o">=</span> <span class="nx">dateRange</span><span class="p">;</span>

  <span class="nx">request</span><span class="p">.</span><span class="nx">limit</span> <span class="o">=</span> <span class="mi">10</span><span class="p">;</span>
  <span class="nx">request</span><span class="p">.</span><span class="nx">metricAggregations</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">TOTAL</span><span class="dl">"</span><span class="p">;</span>

  <span class="k">return</span> <span class="nx">AnalyticsData</span><span class="p">.</span><span class="nx">Properties</span><span class="p">.</span><span class="nf">runReport</span><span class="p">(</span><span class="nx">request</span><span class="p">,</span> <span class="dl">"</span><span class="s2">properties/</span><span class="dl">"</span> <span class="o">+</span> <span class="nx">propertyId</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>程式碼解析：</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// metric 指標，可以是多個，請分開宣告...</span>
<span class="kd">const</span> <span class="nx">screenPageViewsMetric</span> <span class="o">=</span> <span class="nx">AnalyticsData</span><span class="p">.</span><span class="nf">newMetric</span><span class="p">();</span>
<span class="nx">screenPageViewsMetric</span><span class="p">.</span><span class="nx">name</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">screenPageViews</span><span class="dl">"</span><span class="p">;</span>

<span class="c1">// 例如另一個 active1DayUsers：</span>
<span class="kd">const</span> <span class="nx">active1DayUsersMetric</span> <span class="o">=</span> <span class="nx">AnalyticsData</span><span class="p">.</span><span class="nf">newMetric</span><span class="p">();</span>
<span class="nx">active1DayUsersMetric</span><span class="p">.</span><span class="nx">name</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">active1DayUsers</span><span class="dl">"</span><span class="p">;</span>

<span class="c1">// 宣告日期範圍</span>
<span class="kd">const</span> <span class="nx">dateRange</span> <span class="o">=</span> <span class="nx">AnalyticsData</span><span class="p">.</span><span class="nf">newDateRange</span><span class="p">();</span>
<span class="nx">dateRange</span><span class="p">.</span><span class="nx">startDate</span> <span class="o">=</span> <span class="nx">startDate</span><span class="p">;</span>
<span class="nx">dateRange</span><span class="p">.</span><span class="nx">endDate</span> <span class="o">=</span> <span class="nx">endDate</span><span class="p">;</span>

<span class="c1">// dimension 維度，可以是多個，請分開宣告...</span>
<span class="kd">const</span> <span class="nx">pageTitleDimension</span> <span class="o">=</span> <span class="nx">AnalyticsData</span><span class="p">.</span><span class="nf">newDimension</span><span class="p">();</span>
<span class="nx">pageTitleDimension</span><span class="p">.</span><span class="nx">name</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">pageTitle</span><span class="dl">"</span><span class="p">;</span>

<span class="c1">// 例如另一個 dimension：</span>
<span class="kd">const</span> <span class="nx">firstUserSourceMediumDimension</span> <span class="o">=</span> <span class="nx">AnalyticsData</span><span class="p">.</span><span class="nf">newDimension</span><span class="p">();</span>
<span class="nx">firstUserSourceMediumDimension</span><span class="p">.</span><span class="nx">name</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">firstUserSourceMedium</span><span class="dl">"</span><span class="p">;</span>

<span class="c1">//</span>

<span class="c1">// 產生 Request 物件</span>
<span class="kd">const</span> <span class="nx">request</span> <span class="o">=</span> <span class="nx">AnalyticsData</span><span class="p">.</span><span class="nf">newRunReportRequest</span><span class="p">();</span>
<span class="nx">request</span><span class="p">.</span><span class="nx">metrics</span> <span class="o">=</span> <span class="p">[</span><span class="nx">active1DayUsersMetric</span><span class="p">,</span> <span class="nx">active1DayUsersMetric</span><span class="p">];</span> <span class="c1">// 多個就都帶入...</span>
<span class="nx">request</span><span class="p">.</span><span class="nx">dimensions</span> <span class="o">=</span> <span class="p">[</span><span class="nx">pageTitleDimension</span><span class="p">,</span> <span class="nx">firstUserSourceMediumDimension</span><span class="p">];</span> <span class="c1">// 多個就都帶入...</span>

<span class="nx">request</span><span class="p">.</span><span class="nx">dateRanges</span> <span class="o">=</span> <span class="nx">dateRange</span><span class="p">;</span>

<span class="c1">// 只需要前 10 筆資料 (Top 10)</span>
<span class="nx">request</span><span class="p">.</span><span class="nx">limit</span> <span class="o">=</span> <span class="mi">10</span><span class="p">;</span>

<span class="c1">// 設定數據合併計算邏輯：Total (SUM)</span>
<span class="nx">request</span><span class="p">.</span><span class="nx">metricAggregations</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">TOTAL</span><span class="dl">"</span><span class="p">;</span>

<span class="c1">// 產生查詢結果</span>
<span class="k">return</span> <span class="nx">AnalyticsData</span><span class="p">.</span><span class="nx">Properties</span><span class="p">.</span><span class="nf">runReport</span><span class="p">(</span><span class="nx">request</span><span class="p">,</span> <span class="dl">"</span><span class="s2">properties/</span><span class="dl">"</span> <span class="o">+</span> <span class="nx">propertyId</span><span class="p">).</span><span class="nx">rows</span><span class="p">;</span>
</code></pre></div></div>

<p><strong>第一次執行，會需要授權(日後若程式碼有新增需要的權限也會要重新認證一次)：</strong></p>

<blockquote>
  <p><em>實際上就是授權 Google Apps Script 日後使用你的帳號身份執行這些程式，所以需要確保你選擇的帳號身份有對應的 GA 報表存取權限。</em></p>
</blockquote>

<p><img src="/assets/1e85b8df2348/1*_z-P8a4PZCozhtgssH82lA.webp" alt="" loading="lazy" decoding="async" width="579" height="477" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1NzkiIGhlaWdodD0iNDc3Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<ul>
  <li>撰寫好程式碼後，點擊「偵錯」-&gt; 點擊「審查權限」</li>
</ul>

<p><img src="/assets/1e85b8df2348/1*ehox-4AjKv3Ddf1jJ_VmTQ.webp" alt="" loading="lazy" decoding="async" width="1200" height="954" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijk1NCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<ul>
  <li>選擇要執行的身份帳戶，通常等於當前 Google Apps Script 帳戶</li>
</ul>

<p><img src="/assets/1e85b8df2348/1*onR-n1sI4-G9KhD_fhef3Q.webp" alt="" loading="lazy" decoding="async" width="1265" height="1006" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjY1IiBoZWlnaHQ9IjEwMDYiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<ul>
  <li>選擇「進階」展開 -&gt; 點擊「前往 XXX」
這是我們自己寫給自己用的應用程式，不需經過 Google 驗證。</li>
</ul>

<p><img src="/assets/1e85b8df2348/1*73fQnaB__qKhO7NndQ0GyQ.webp" alt="" loading="lazy" decoding="async" width="1200" height="954" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijk1NCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<ul>
  <li>點擊「允許」</li>
</ul>

<p><strong>允許之後再點「偵錯」或「執行」就能執行程式：</strong></p>

<p><img src="/assets/1e85b8df2348/1*DbcQnfp8xdJCFnrDc778tw.webp" alt="" loading="lazy" decoding="async" width="1265" height="1006" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjY1IiBoZWlnaHQ9IjEwMDYiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>這邊我們先使用 <code class="language-plaintext highlighter-rouge">Logger.log(JSON.stringify())</code> 取得輸出結果：</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"kind"</span><span class="p">:</span><span class="w"> </span><span class="s2">"analyticsData#runReport"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"dimensionHeaders"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"pageTitle"</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">],</span><span class="w">
  </span><span class="nl">"rowCount"</span><span class="p">:</span><span class="w"> </span><span class="mi">71</span><span class="p">,</span><span class="w">
  </span><span class="nl">"metadata"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"currencyCode"</span><span class="p">:</span><span class="w"> </span><span class="s2">"TWD"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"timeZone"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Asia/Taipei"</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"rows"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"dimensionValues"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
        </span><span class="p">{</span><span class="w">
          </span><span class="nl">"value"</span><span class="p">:</span><span class="w"> </span><span class="s2">"ZhgChgLi"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">],</span><span class="w">
      </span><span class="nl">"metricValues"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
        </span><span class="p">{</span><span class="w">
          </span><span class="nl">"value"</span><span class="p">:</span><span class="w"> </span><span class="s2">"166"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">]</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"metricValues"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
        </span><span class="p">{</span><span class="w">
          </span><span class="nl">"value"</span><span class="p">:</span><span class="w"> </span><span class="s2">"109"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">],</span><span class="w">
      </span><span class="nl">"dimensionValues"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
        </span><span class="p">{</span><span class="w">
          </span><span class="nl">"value"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Apple Watch 原廠不鏽鋼米蘭錶帶開箱 | ZhgChgLi"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">]</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"dimensionValues"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
        </span><span class="p">{</span><span class="w">
          </span><span class="nl">"value"</span><span class="p">:</span><span class="w"> </span><span class="s2">"iOS ≥ 13.1 使用「捷徑」自動化功能搭配米家智慧家居 | ZhgChgLi"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">],</span><span class="w">
      </span><span class="nl">"metricValues"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
        </span><span class="p">{</span><span class="w">
          </span><span class="nl">"value"</span><span class="p">:</span><span class="w"> </span><span class="s2">"101"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">]</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"dimensionValues"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
        </span><span class="p">{</span><span class="w">
          </span><span class="nl">"value"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Medium Partner Program 終於對全球(包含台灣)寫作者開放啦！ | ZhgChgLi"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">],</span><span class="w">
      </span><span class="nl">"metricValues"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
        </span><span class="p">{</span><span class="w">
          </span><span class="nl">"value"</span><span class="p">:</span><span class="w"> </span><span class="s2">"85"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">]</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"metricValues"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
        </span><span class="p">{</span><span class="w">
          </span><span class="nl">"value"</span><span class="p">:</span><span class="w"> </span><span class="s2">"77"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">],</span><span class="w">
      </span><span class="nl">"dimensionValues"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
        </span><span class="p">{</span><span class="w">
          </span><span class="nl">"value"</span><span class="p">:</span><span class="w"> </span><span class="s2">"iOS 捷徑自動化應用場景 — 自動轉發簡訊與自動建立提醒待辦事項 | ZhgChgLi"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">]</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"metricValues"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
        </span><span class="p">{</span><span class="w">
          </span><span class="nl">"value"</span><span class="p">:</span><span class="w"> </span><span class="s2">"51"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">],</span><span class="w">
      </span><span class="nl">"dimensionValues"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
        </span><span class="p">{</span><span class="w">
          </span><span class="nl">"value"</span><span class="p">:</span><span class="w"> </span><span class="s2">"遊記 9/11 名古屋一日快閃自由行 | ZhgChgLi"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">]</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"metricValues"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
        </span><span class="p">{</span><span class="w">
          </span><span class="nl">"value"</span><span class="p">:</span><span class="w"> </span><span class="s2">"42"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">],</span><span class="w">
      </span><span class="nl">"dimensionValues"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
        </span><span class="p">{</span><span class="w">
          </span><span class="nl">"value"</span><span class="p">:</span><span class="w"> </span><span class="s2">"iOS 隱私與便利的前世今生 | ZhgChgLi"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">]</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"dimensionValues"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
        </span><span class="p">{</span><span class="w">
          </span><span class="nl">"value"</span><span class="p">:</span><span class="w"> </span><span class="s2">"iOS Vision framework x WWDC 24 Discover Swift enhancements in the Vision framework Session | ZhgChgLi"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">],</span><span class="w">
      </span><span class="nl">"metricValues"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
        </span><span class="p">{</span><span class="w">
          </span><span class="nl">"value"</span><span class="p">:</span><span class="w"> </span><span class="s2">"34"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">]</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"dimensionValues"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
        </span><span class="p">{</span><span class="w">
          </span><span class="nl">"value"</span><span class="p">:</span><span class="w"> </span><span class="s2">"iOS ≥ 18 NSAttributedString attributes Range 合併的一個行為改變 | ZhgChgLi"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">],</span><span class="w">
      </span><span class="nl">"metricValues"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
        </span><span class="p">{</span><span class="w">
          </span><span class="nl">"value"</span><span class="p">:</span><span class="w"> </span><span class="s2">"30"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">]</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"metricValues"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
        </span><span class="p">{</span><span class="w">
          </span><span class="nl">"value"</span><span class="p">:</span><span class="w"> </span><span class="s2">"30"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">],</span><span class="w">
      </span><span class="nl">"dimensionValues"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
        </span><span class="p">{</span><span class="w">
          </span><span class="nl">"value"</span><span class="p">:</span><span class="w"> </span><span class="s2">"手工打造 HTML 解析器的那些事 | ZhgChgLi"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">]</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">],</span><span class="w">
  </span><span class="nl">"metricHeaders"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"TYPE_INTEGER"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"screenPageViews"</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">],</span><span class="w">
  </span><span class="nl">"totals"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"dimensionValues"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
        </span><span class="p">{</span><span class="w">
          </span><span class="nl">"value"</span><span class="p">:</span><span class="w"> </span><span class="s2">"RESERVED_TOTAL"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">],</span><span class="w">
      </span><span class="nl">"metricValues"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
        </span><span class="p">{</span><span class="w">
          </span><span class="nl">"value"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1229"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">]</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<ul>
  <li><strong>Google Apps Script 請求 GA 數據成功！🎉🎉🎉</strong></li>
</ul>

<h3 id="step-3-組合起來google-apps-script--ga4--telegram-bot">Step 3. 組合起來！Google Apps Script + GA4 + Telegram Bot</h3>

<p>依照上篇文章「 <a href="https://medium.com/zrealm-robotic-process-automation/10-%E5%88%86%E9%90%98%E5%BF%AB%E9%80%9F%E7%A7%BB%E8%BD%89-line-notify-%E5%88%B0-telegram-bot-%E9%80%9A%E7%9F%A5-6922e90ba90c?source=collection_home---6------0-----------------------" target="_blank">10 分鐘快速移轉 Line Notify 到 Telegram Bot 通知</a> 」建立你的 Telegram Bot 取得 <code class="language-plaintext highlighter-rouge">Bot Token</code> &amp; 想要傳送到的 <code class="language-plaintext highlighter-rouge">Channel Chat ID</code> 。</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">telegramToken</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">XXXX</span><span class="dl">"</span> <span class="c1">// 帶入你的 Telegram Bot Token</span>
<span class="c1">//</span>

<span class="kd">function</span> <span class="nf">execute</span><span class="p">()</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">screenPageViewsReport</span> <span class="o">=</span> <span class="nf">fetchScreenPageViews</span><span class="p">(</span><span class="dl">"</span><span class="s2">318495208</span><span class="dl">"</span><span class="p">);</span>
  
  <span class="c1">//</span>
  <span class="kd">const</span> <span class="nx">total</span> <span class="o">=</span> <span class="nf">parseInt</span><span class="p">(</span><span class="nx">screenPageViewsReport</span><span class="p">.</span><span class="nx">totals</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">metricValues</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">value</span><span class="p">);</span>
  <span class="kd">var</span> <span class="nx">message</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">總瀏覽數：</span><span class="dl">"</span><span class="o">+</span><span class="nx">total</span><span class="p">.</span><span class="nf">toLocaleString</span><span class="p">()</span><span class="o">+</span><span class="dl">"</span><span class="se">\n</span><span class="dl">"</span><span class="p">;</span>

  <span class="nx">screenPageViewsReport</span><span class="p">.</span><span class="nx">rows</span><span class="p">.</span><span class="nf">forEach</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">element</span><span class="p">,</span> <span class="nx">index</span><span class="p">)</span> <span class="p">{</span>
    <span class="kd">const</span> <span class="nx">pageTitle</span> <span class="o">=</span> <span class="nx">element</span><span class="p">.</span><span class="nx">dimensionValues</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">value</span><span class="p">;</span>
    <span class="kd">const</span> <span class="nx">value</span> <span class="o">=</span> <span class="nf">parseInt</span><span class="p">(</span><span class="nx">element</span><span class="p">.</span><span class="nx">metricValues</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">value</span><span class="p">);</span>

    <span class="nx">message</span> <span class="o">+=</span> <span class="dl">"</span><span class="s2">[Top </span><span class="dl">"</span><span class="o">+</span><span class="p">(</span><span class="nx">index</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span><span class="o">+</span><span class="dl">"</span><span class="s2">] </span><span class="dl">"</span><span class="o">+</span><span class="nx">pageTitle</span><span class="o">+</span><span class="dl">"</span><span class="s2">: </span><span class="dl">"</span><span class="o">+</span><span class="nx">value</span><span class="p">.</span><span class="nf">toLocaleString</span><span class="p">()</span><span class="o">+</span><span class="dl">"</span><span class="se">\n</span><span class="dl">"</span><span class="p">;</span>
  <span class="p">});</span>

  <span class="nf">sendNotifyMessage</span><span class="p">(</span><span class="nx">message</span><span class="p">,</span> <span class="o">-</span><span class="nx">xxxx</span><span class="p">);</span> <span class="c1">// 帶入你的 Channel Chat ID</span>
<span class="p">}</span>


<span class="c1">// 發送訊息到 Telegram 指定 Channel Chat ID</span>
<span class="kd">function</span> <span class="nf">sendNotifyMessage</span><span class="p">(</span><span class="nx">message</span><span class="p">,</span> <span class="nx">chatId</span><span class="p">)</span> <span class="p">{</span>
  <span class="kd">var</span> <span class="nx">url</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">https://api.telegram.org/bot</span><span class="dl">"</span><span class="o">+</span><span class="nx">telegramToken</span><span class="o">+</span><span class="dl">"</span><span class="s2">/sendMessage</span><span class="dl">"</span><span class="p">;</span>
  
  <span class="kd">const</span> <span class="nx">payload</span> <span class="o">=</span> <span class="p">{</span>
    <span class="dl">"</span><span class="s2">chat_id</span><span class="dl">"</span><span class="p">:</span> <span class="nx">chatId</span><span class="p">,</span>
    <span class="dl">"</span><span class="s2">text</span><span class="dl">"</span><span class="p">:</span> <span class="nx">message</span><span class="p">,</span>
    <span class="dl">"</span><span class="s2">disable_web_page_preview</span><span class="dl">"</span><span class="p">:</span> <span class="kc">true</span>
  <span class="p">}</span> 
  <span class="kd">const</span> <span class="nx">options</span> <span class="o">=</span> <span class="p">{</span>
    <span class="dl">'</span><span class="s1">method</span><span class="dl">'</span><span class="p">:</span> <span class="dl">'</span><span class="s1">post</span><span class="dl">'</span><span class="p">,</span>
    <span class="dl">'</span><span class="s1">contentType</span><span class="dl">'</span><span class="p">:</span> <span class="dl">'</span><span class="s1">application/json</span><span class="dl">'</span><span class="p">,</span>
    <span class="dl">'</span><span class="s1">muteHttpExceptions</span><span class="dl">'</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
    <span class="dl">'</span><span class="s1">payload</span><span class="dl">'</span><span class="p">:</span> <span class="nx">JSON</span><span class="p">.</span><span class="nf">stringify</span><span class="p">(</span><span class="nx">payload</span><span class="p">)</span>
  <span class="p">};</span>

  <span class="kd">const</span> <span class="nx">response</span> <span class="o">=</span> <span class="nx">UrlFetchApp</span><span class="p">.</span><span class="nf">fetch</span><span class="p">(</span><span class="nx">url</span><span class="p">,</span> <span class="nx">options</span><span class="p">);</span>
  <span class="kd">const</span> <span class="nx">data</span> <span class="o">=</span> <span class="nx">JSON</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="nx">response</span><span class="p">.</span><span class="nf">getContentText</span><span class="p">());</span>

  <span class="k">if </span><span class="p">(</span><span class="nx">data</span><span class="p">[</span><span class="dl">"</span><span class="s2">ok</span><span class="dl">"</span><span class="p">]</span> <span class="o">==</span> <span class="kc">undefined</span> <span class="o">||</span> <span class="nx">data</span><span class="p">[</span><span class="dl">"</span><span class="s2">ok</span><span class="dl">"</span><span class="p">]</span> <span class="o">!=</span> <span class="kc">true</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">if </span><span class="p">(</span><span class="nx">data</span><span class="p">[</span><span class="dl">"</span><span class="s2">error_code</span><span class="dl">"</span><span class="p">]</span> <span class="o">!=</span> <span class="kc">undefined</span> <span class="o">&amp;&amp;</span> <span class="nx">data</span><span class="p">[</span><span class="dl">"</span><span class="s2">error_code</span><span class="dl">"</span><span class="p">]</span> <span class="o">==</span> <span class="mi">429</span><span class="p">)</span> <span class="p">{</span>
      <span class="nx">Utilities</span><span class="p">.</span><span class="nf">sleep</span><span class="p">(</span><span class="mi">1000</span><span class="p">);</span>
      <span class="nf">sendNotifyMessage</span><span class="p">(</span><span class="nx">message</span><span class="p">,</span> <span class="nx">chatId</span><span class="p">);</span>
    <span class="p">}</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="kd">function</span> <span class="nf">fetchScreenPageViews</span><span class="p">(</span><span class="nx">propertyId</span><span class="p">,</span> <span class="nx">startDate</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">7daysAgo</span><span class="dl">"</span><span class="p">,</span> <span class="nx">endDate</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">yesterday</span><span class="dl">"</span><span class="p">)</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">screenPageViewsMetric</span> <span class="o">=</span> <span class="nx">AnalyticsData</span><span class="p">.</span><span class="nf">newMetric</span><span class="p">();</span>
  <span class="nx">screenPageViewsMetric</span><span class="p">.</span><span class="nx">name</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">screenPageViews</span><span class="dl">"</span><span class="p">;</span>

  <span class="kd">const</span> <span class="nx">dateRange</span> <span class="o">=</span> <span class="nx">AnalyticsData</span><span class="p">.</span><span class="nf">newDateRange</span><span class="p">();</span>
  <span class="nx">dateRange</span><span class="p">.</span><span class="nx">startDate</span> <span class="o">=</span> <span class="nx">startDate</span><span class="p">;</span>
  <span class="nx">dateRange</span><span class="p">.</span><span class="nx">endDate</span> <span class="o">=</span> <span class="nx">endDate</span><span class="p">;</span>

  <span class="kd">const</span> <span class="nx">pageTitleDimension</span> <span class="o">=</span> <span class="nx">AnalyticsData</span><span class="p">.</span><span class="nf">newDimension</span><span class="p">();</span>
  <span class="nx">pageTitleDimension</span><span class="p">.</span><span class="nx">name</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">pageTitle</span><span class="dl">"</span><span class="p">;</span>

  <span class="kd">const</span> <span class="nx">request</span> <span class="o">=</span> <span class="nx">AnalyticsData</span><span class="p">.</span><span class="nf">newRunReportRequest</span><span class="p">();</span>
  <span class="nx">request</span><span class="p">.</span><span class="nx">dimensions</span> <span class="o">=</span> <span class="p">[</span><span class="nx">pageTitleDimension</span><span class="p">];</span>
  <span class="nx">request</span><span class="p">.</span><span class="nx">metrics</span> <span class="o">=</span> <span class="p">[</span><span class="nx">screenPageViewsMetric</span><span class="p">];</span>
  <span class="nx">request</span><span class="p">.</span><span class="nx">dateRanges</span> <span class="o">=</span> <span class="nx">dateRange</span><span class="p">;</span>

  <span class="nx">request</span><span class="p">.</span><span class="nx">limit</span> <span class="o">=</span> <span class="mi">10</span><span class="p">;</span>
  <span class="nx">request</span><span class="p">.</span><span class="nx">metricAggregations</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">TOTAL</span><span class="dl">"</span><span class="p">;</span>

  <span class="k">return</span> <span class="nx">AnalyticsData</span><span class="p">.</span><span class="nx">Properties</span><span class="p">.</span><span class="nf">runReport</span><span class="p">(</span><span class="nx">request</span><span class="p">,</span> <span class="dl">"</span><span class="s2">properties/</span><span class="dl">"</span> <span class="o">+</span> <span class="nx">propertyId</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>程式碼解析：</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">//...</span>
  <span class="c1">// 依照報表回傳的 json 找到 total 位置，parseInt 將字串轉成 INT 數字格式</span>
  <span class="c1">// .toLocaleString() -&gt; 格式化數字，123456 -&gt; 123,456</span>
  <span class="kd">const</span> <span class="nx">total</span> <span class="o">=</span> <span class="nf">parseInt</span><span class="p">(</span><span class="nx">screenPageViewsReport</span><span class="p">.</span><span class="nx">totals</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">metricValues</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">value</span><span class="p">);</span>
  <span class="kd">var</span> <span class="nx">message</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">總瀏覽數：</span><span class="dl">"</span><span class="o">+</span><span class="nx">total</span><span class="p">.</span><span class="nf">toLocaleString</span><span class="p">()</span><span class="o">+</span><span class="dl">"</span><span class="se">\n</span><span class="dl">"</span><span class="p">;</span>

  <span class="c1">// 依照報表回傳的 json 遍歷資料，組合成訊息</span>
  <span class="nx">screenPageViewsReport</span><span class="p">.</span><span class="nx">rows</span><span class="p">.</span><span class="nf">forEach</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">element</span><span class="p">,</span> <span class="nx">index</span><span class="p">)</span> <span class="p">{</span>
    <span class="kd">const</span> <span class="nx">pageTitle</span> <span class="o">=</span> <span class="nx">element</span><span class="p">.</span><span class="nx">dimensionValues</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">value</span><span class="p">;</span>
    <span class="kd">const</span> <span class="nx">value</span> <span class="o">=</span> <span class="nf">parseInt</span><span class="p">(</span><span class="nx">element</span><span class="p">.</span><span class="nx">metricValues</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">value</span><span class="p">);</span>

    <span class="nx">message</span> <span class="o">+=</span> <span class="dl">"</span><span class="s2">[Top </span><span class="dl">"</span><span class="o">+</span><span class="p">(</span><span class="nx">index</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span><span class="o">+</span><span class="dl">"</span><span class="s2">] </span><span class="dl">"</span><span class="o">+</span><span class="nx">pageTitle</span><span class="o">+</span><span class="dl">"</span><span class="s2">: </span><span class="dl">"</span><span class="o">+</span><span class="nx">value</span><span class="p">.</span><span class="nf">toLocaleString</span><span class="p">()</span><span class="o">+</span><span class="dl">"</span><span class="se">\n</span><span class="dl">"</span><span class="p">;</span>
  <span class="p">});</span>
<span class="c1">//...</span>
</code></pre></div></div>
<h4 id="執行點擊上方執行或偵錯並確定方法名稱是選擇-execute-"><strong>執行：點擊上方「執行」或「偵錯」並確定方法名稱是選擇「 <code class="language-plaintext highlighter-rouge">execute</code> 」：</strong></h4>

<p><img src="/assets/1e85b8df2348/1*R7uVMGODJR2Z_GAYnyMYXA.webp" alt="" loading="lazy" decoding="async" width="512" height="109" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1MTIiIGhlaWdodD0iMTA5Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><img src="/assets/1e85b8df2348/1*Pl2v19Ed8COUwCc1deTYFw.webp" alt="" loading="lazy" decoding="async" width="643" height="274" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2NDMiIGhlaWdodD0iMjc0Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<ul>
  <li><strong>成功！🎉🎉🎉</strong></li>
</ul>

<h4 id="設定排程定時自動執行">設定排程定時自動執行</h4>

<p>最後一步是我們希望報表機器人能定時自動執行，從左側選單前往「觸發條件」：</p>

<p><img src="/assets/1e85b8df2348/1*SY1YKLwTvDtP1hQTRqj50w.webp" alt="" loading="lazy" decoding="async" width="866" height="805" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4NjYiIGhlaWdodD0iODA1Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<ul>
  <li>選擇右下角「新增觸發條件」按鈕</li>
</ul>

<p><img src="/assets/1e85b8df2348/1*wYOwwQBTEtns8nN_7bmvNQ.webp" alt="" loading="lazy" decoding="async" width="875" height="874" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4NzUiIGhlaWdodD0iODc0Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<ol>
  <li>選擇鐔要執行的功能：選擇 「 <code class="language-plaintext highlighter-rouge">execute</code> 」方法名稱</li>
  <li>選擇執行的部署作業：選擇「 <code class="language-plaintext highlighter-rouge">上端</code> 」</li>
  <li>選取活動來源：選擇「 <code class="language-plaintext highlighter-rouge">時間驅動</code> 」</li>
  <li>選取時間型觸發條件類型：選擇你想要的觸發頻率</li>
  <li>選取時段：選擇你想要每日自動觸發的時間</li>
  <li>如果執行失敗…通知信件的頻率設定</li>
  <li>儲存</li>
</ol>

<blockquote>
  <p><strong><em>完成！這樣時間到就會自動執行囉。</em></strong> 🎉🎉🎉</p>
</blockquote>

<h3 id="延伸作業">延伸作業</h3>

<p>其他數據如 新使用者數、來源媒介等，同樣使用前面程式碼就能達成，這邊就不在重複贅述，就當成給大家的回家作業囉。</p>]]></content>
  </entry><entry>
    <title type="html">Line Notify 快速移轉 Telegram Bot｜10 分鐘完成個人通知服務切換</title>
    <link href="https://zhgchg.li/posts/zrealm-robotic-process-automation/line-notify-%E5%BF%AB%E9%80%9F%E7%A7%BB%E8%BD%89-telegram-bot-10-%E5%88%86%E9%90%98%E5%AE%8C%E6%88%90%E5%80%8B%E4%BA%BA%E9%80%9A%E7%9F%A5%E6%9C%8D%E5%8B%99%E5%88%87%E6%8F%9B-6922e90ba90c/" rel="alternate" type="text/html" title="Line Notify 快速移轉 Telegram Bot｜10 分鐘完成個人通知服務切換" />
    <published>2024-10-12T21:10:46+08:00</published>
    <updated>2024-10-20T16:57:41+08:00</updated>
    <id>https://zhgchg.li/posts/zrealm-robotic-process-automation/6922e90ba90c</id><summary type="html">面對 Line Notify 即將終止服務，教你用 10 分鐘快速將通知系統切換到免費且功能更強大的 Telegram Bot，實現多群組分類、豐富訊息格式與互動指令，提升通知管理效率與使用體驗。</summary><author>
      <name>ZhgChgLi</name>
    </author><category term="ZRealm Robotic Process Automation" /><category term="ios-app-development" /><category term="line" /><category term="telegram" /><category term="google-apps-script" /><category term="automation" /><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://zhgchg.li/assets/6922e90ba90c/1*r59nJAx__InU09hYMenePg.webp" /><content type="html" xml:base="https://zhgchg.li/posts/zrealm-robotic-process-automation/line-notify-%E5%BF%AB%E9%80%9F%E7%A7%BB%E8%BD%89-telegram-bot-10-%E5%88%86%E9%90%98%E5%AE%8C%E6%88%90%E5%80%8B%E4%BA%BA%E9%80%9A%E7%9F%A5%E6%9C%8D%E5%8B%99%E5%88%87%E6%8F%9B-6922e90ba90c/"><![CDATA[<h3 id="10-分鐘快速移轉-line-notify-到-telegram-bot-通知">10 分鐘快速移轉 Line Notify 到 Telegram Bot 通知</h3>

<p>手把手將 Line Notify 個人通知服務遷移至同樣免費、更強大的 Telegram Bot</p>

<p><img src="/assets/6922e90ba90c/1*r59nJAx__InU09hYMenePg.webp" alt="Photo by Lana Codes" loading="lazy" decoding="async" width="1200" height="801" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjgwMSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>Photo by <a href="https://unsplash.com/@lanacodes?utm_content=creditCopyText&amp;utm_medium=referral&amp;utm_source=unsplash" target="_blank">Lana Codes</a></p>
<h4 id="line-notify結束服務公告"><a href="https://notify-bot.line.me/closing-announce" target="_blank">LINE Notify結束服務公告</a></h4>

<blockquote>
  <p><em>感謝您長期以來使用LINE Notify。</em></p>
</blockquote>

<blockquote>
  <p><em>LINE Notify自2016年9月推出以來，一直致力於為開發者提供服務。為能提供更優質的服務，並將經營資源集中於後續的類似商品服務，我們決定於2025年3月31日結束本服務。對於長期以來支持並使用LINE作為通知連動服務的所有用戶，我們深表感謝。</em></p>
</blockquote>

<blockquote>
  <p><em>若您仍需使用LINE向用戶傳送通知的商品服務，建議可改用功能更豐富的Messaging API。</em></p>
</blockquote>

<p>擷取自 <a href="https://notify-bot.line.me/closing-announce" target="_blank">Line Notify 官網</a> ， Line 於 2024/10/08 發文公告 Line Notify 將於 2025/04/01 完全關閉，如需繼續使用 Line 做為訊息通知，只能用付費的 <a href="https://developers.line.biz/en/services/messaging-api/" target="_blank">Message API</a> 。</p>

<p>Line Notify 的優點是非常容易串接，拿來做個人通知機器人非常方便好用，一些 Line Bot 或第三方服務也會使用 Line Notify 進行通知 (例如：路易莎、你訂的訂單通知功能)；但缺點也蠻多的，例如訊息內容單一、無法分群組(統一都是傳送到 Line Notify Bot)、訊息內容長度有限…等等</p>

<p>隨著 Line Notify 宣告終止，正好也給我了一個遷移至其他通訊、通知服務的機會：</p>
<ul>
  <li>Slack：免費版訊息只保留 30 天、我的通知比較是個人，用 Slack 有點大材小用。(Slack 傳送訊息可參考我之前的文章： <a href="/posts/zrealm-robotic-process-automation/slack-app-整合-chatgpt-利用-openai-api-與-google-cloud-functions-自建智能助理-bd94cc88f9c9/">Slack &amp; ChatGPT Integration</a> )</li>
  <li>Discord：我的通知比較是個人，一樣有點大材小用。</li>
  <li><strong>Telegram：免費、幾乎無限使用。</strong></li>
</ul>

<p>對我來說 Telegram 的通訊服務比較符合我原本 Line Notify 的使用需求，我需要一個能接收通知的頻道，最好是能依照不同需求有不同的頻道，可接受的內容、格式越豐富越好、並且能快速簡單串接；Telegram 都符合上述需求，還能多實現跟 Bot 的交互功能。</p>
<h4 id="成果">成果</h4>

<p>先上最終效果圖(以 <a href="/posts/zrealm-robotic-process-automation/google-apps-script-三步驟打造免費-github-repo-star-通知器-實時獲取-star-動態-382218e15697/">Github Star 通知、Repo Stats 通知為例</a> )：</p>

<p><img src="/assets/6922e90ba90c/1*1kHJu5yZMUST-wrna6KqkA.gif" alt="" loading="lazy" decoding="async" width="380" height="550" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzODAiIGhlaWdodD0iNTUwIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<ul>
  <li>✅ 有人按 Repo 星星時會觸發 Webhook -&gt; Google Apps Script -&gt; Telegram Bot 發送通知到 Telegram — Github Stats Group</li>
  <li>✅ Google Apps Script 每日定時 -&gt; 撈取 Github Repo Stats 狀態 -&gt; Telegram Bot 發送通知到 Telegram — Github Stats Group</li>
  <li>✅ 使用 <code class="language-plaintext highlighter-rouge">/update</code> Telegram Bot Command 觸發撈取 Github Repo Stats 狀態 -&gt; Telegram Bot 發送通知到 Telegram — Github Stats Group</li>
</ul>

<h4 id="對比原本-line-notify">對比原本 Line Notify</h4>

<p><img src="/assets/6922e90ba90c/1*mTycFPe7rPh1qc0BagUXdw.webp" alt="" loading="lazy" decoding="async" width="960" height="1404" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NjAiIGhlaWdodD0iMTQwNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<ul>
  <li>❌ 所有訊息無法分類、分群組都傳到 Line Notify</li>
  <li>❌ 無法針對個別訊息做特殊設定 (如通知聲、靜音…)</li>
  <li>❌ 無法輸入訊息互動</li>
</ul>

<h4 id="本文目錄">本文目錄</h4>
<ul>
  <li>設定 Telegram Bot</li>
  <li>移轉 Line Notify 發送訊息到 Telegram Bot (Google Apps Script)</li>
  <li>與 Telegram Bot 交互 (Command) x 使用 Google Apps Script</li>
</ul>

<h3 id="12-設定-telegram-bot">(1/2) 設定 Telegram Bot</h3>

<p>Telegram Bot 的申請非常簡單，連網頁都不需要開，只要跟官方的 <a href="https://t.me/BotFather" target="_blank">BotFather 機器人</a> 互動就可以了。</p>
<h4 id="step-1-申請-telegram-bot">Step 1. 申請 Telegram Bot</h4>

<p><a href="https://telegram.org/" target="_blank">安裝、註冊好 Telegram 服務</a> 之後，點擊加「 <a href="https://t.me/BotFather" target="_blank">BotFather 機器人</a> 」為好友。</p>

<p><img src="/assets/6922e90ba90c/1*-3qluwPXk-HeGRCTOd-EAA.webp" alt="" loading="lazy" decoding="async" width="691" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2OTEiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/6922e90ba90c/1*QfzbJYQ2vVI2CNvvLU7r_w.webp" alt="" loading="lazy" decoding="async" width="595" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1OTUiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<ol>
  <li>打開、加入 <a href="https://t.me/BotFather" target="_blank">BotFather 機器人</a></li>
  <li>加入後直接傳送訊息「 <code class="language-plaintext highlighter-rouge">/newbot</code> 」建立你的機器人。</li>
  <li>輸入你的機器人名稱</li>
  <li>輸入你的機器人帳號 (不可重複、必須以 <code class="language-plaintext highlighter-rouge">bot</code> 為結尾，例如我的 <code class="language-plaintext highlighter-rouge">zhgchgli_bot</code> )</li>
  <li>你的 Bot 連結，點進進入開始使用 (e.g. t.me/harrytest56_bot)</li>
  <li>取得 <code class="language-plaintext highlighter-rouge">你的_BOT_API_Token</code> ， <strong>請妥善保存</strong> ⚠️⚠️⚠️</li>
</ol>

<p>點擊 4. 取得的 Bot 連結，開始使用 Bot：</p>

<p><img src="/assets/6922e90ba90c/1*5q5PE3D9nwQPlYoY8SqCHQ.webp" alt="" loading="lazy" decoding="async" width="705" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3MDUiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/6922e90ba90c/1*NKz9GSDlGov9q7xIlHYlCQ.webp" alt="" loading="lazy" decoding="async" width="756" height="1390" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NTYiIGhlaWdodD0iMTM5MCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/6922e90ba90c/1*VU7EyeZL2BpVnLRHQBNOPQ.webp" alt="" loading="lazy" decoding="async" width="732" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3MzIiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p>目前無任何功能，可以點右上角 Info，編輯名稱或上傳大頭貼。</p>
<h4 id="step-2-創建-telegram-通知-group--加入機器人帳號">Step 2. 創建 Telegram 通知 Group &amp; 加入機器人帳號</h4>

<blockquote>
  <p><em>我希望不同的個人通知類型傳到不同的 Group，這 <strong>邊因為 Demo 只創建一個 My Notify Group</strong> 。</em></p>
</blockquote>

<blockquote>
  <p><em>你可以依據實際需求創建不同的 Group &amp; 依照步驟加入、設定機器人。</em></p>
</blockquote>

<p><img src="/assets/6922e90ba90c/1*geb_lwnJOUXiuLHvguuXjQ.webp" alt="" loading="lazy" decoding="async" width="1040" height="726" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDQwIiBoZWlnaHQ9IjcyNiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/6922e90ba90c/1*bfXDTACy4mW3LZlq6ErKzQ.webp" alt="" loading="lazy" decoding="async" width="896" height="1526" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4OTYiIGhlaWdodD0iMTUyNiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/6922e90ba90c/1*ZmsCLqEZbNN3IS2uoZV8cw.webp" alt="" loading="lazy" decoding="async" width="896" height="1526" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4OTYiIGhlaWdodD0iMTUyNiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<ol>
  <li>New Group</li>
  <li>搜尋你的機器人帳號＆點擊加入</li>
  <li>設定 Group 名稱、大頭貼</li>
</ol>

<h4 id="step-3-取得-group-chat-id">Step 3. 取得 Group Chat ID</h4>

<p>Telegram Bot API 沒有直接取得 Group 列表、Group Chat ID 的 Endpoint，只能透過 <code class="language-plaintext highlighter-rouge">/getUpdates</code> 取得機器人訊息列表，並從中找到 Group Chat ID：</p>

<p><strong>Request:</strong></p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">curl</span> <span class="nv">'https</span><span class="p">:</span><span class="c1">//api.telegram.org/bot你的_BOT_API_Token/getUpdates'</span>
</code></pre></div></div>
<ul>
  <li>Telegram API 格式為 <code class="language-plaintext highlighter-rouge">https://api.telegram.org/bot</code> <strong>你的_BOT_API_Token</strong> <code class="language-plaintext highlighter-rouge">/getUpdates</code> ， BOT API Token 字串前要加上 <code class="language-plaintext highlighter-rouge">bot</code> 字串</li>
  <li>範例： <code class="language-plaintext highlighter-rouge">curl 'https://api.telegram.org/bot7814194578:AAEWpPJvKn06ID7D9FjV65aDKQLkGkz8cc8/getUpdates'</code></li>
</ul>

<p><strong>Response:</strong></p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
    </span><span class="nl">"ok"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
    </span><span class="nl">"result"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
        </span><span class="p">{</span><span class="w">
            </span><span class="nl">"update_id"</span><span class="p">:</span><span class="w"> </span><span class="mi">706454235</span><span class="p">,</span><span class="w">
            </span><span class="nl">"my_chat_member"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
                </span><span class="nl">"chat"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
                    </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">-4532420331</span><span class="p">,</span><span class="w">
                    </span><span class="nl">"title"</span><span class="p">:</span><span class="w"> </span><span class="s2">"My Nofify"</span><span class="p">,</span><span class="w">
                    </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"group"</span><span class="p">,</span><span class="w">
                    </span><span class="nl">"all_members_are_administrators"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="w">
                </span><span class="p">},</span><span class="w">
                </span><span class="nl">"from"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
                    </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">986128250</span><span class="p">,</span><span class="w">
                    </span><span class="nl">"is_bot"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w">
                    </span><span class="nl">"first_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Harry"</span><span class="p">,</span><span class="w">
                    </span><span class="nl">"last_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Li"</span><span class="p">,</span><span class="w">
                    </span><span class="nl">"username"</span><span class="p">:</span><span class="w"> </span><span class="s2">"zhgchgli"</span><span class="w">
                </span><span class="p">},</span><span class="w">
                </span><span class="nl">"date"</span><span class="p">:</span><span class="w"> </span><span class="mi">1728726861</span><span class="p">,</span><span class="w">
                </span><span class="nl">"old_chat_member"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
                    </span><span class="nl">"user"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
                        </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">7814194578</span><span class="p">,</span><span class="w">
                        </span><span class="nl">"is_bot"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
                        </span><span class="nl">"first_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Harry Test"</span><span class="p">,</span><span class="w">
                        </span><span class="nl">"username"</span><span class="p">:</span><span class="w"> </span><span class="s2">"harrytest56_bot"</span><span class="w">
                    </span><span class="p">},</span><span class="w">
                    </span><span class="nl">"status"</span><span class="p">:</span><span class="w"> </span><span class="s2">"left"</span><span class="w">
                </span><span class="p">},</span><span class="w">
                </span><span class="nl">"new_chat_member"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
                    </span><span class="nl">"user"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
                        </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">7814194578</span><span class="p">,</span><span class="w">
                        </span><span class="nl">"is_bot"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
                        </span><span class="nl">"first_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Harry Test"</span><span class="p">,</span><span class="w">
                        </span><span class="nl">"username"</span><span class="p">:</span><span class="w"> </span><span class="s2">"harrytest56_bot"</span><span class="w">
                    </span><span class="p">},</span><span class="w">
                    </span><span class="nl">"status"</span><span class="p">:</span><span class="w"> </span><span class="s2">"member"</span><span class="w">
                </span><span class="p">}</span><span class="w">
            </span><span class="p">}</span><span class="w">
        </span><span class="p">},</span><span class="w">
        </span><span class="p">{</span><span class="w">
            </span><span class="nl">"update_id"</span><span class="p">:</span><span class="w"> </span><span class="mi">706454236</span><span class="p">,</span><span class="w">
            </span><span class="nl">"message"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
                </span><span class="nl">"message_id"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w">
                </span><span class="nl">"from"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
                    </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">986128250</span><span class="p">,</span><span class="w">
                    </span><span class="nl">"is_bot"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w">
                    </span><span class="nl">"first_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Harry"</span><span class="p">,</span><span class="w">
                    </span><span class="nl">"last_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Li"</span><span class="p">,</span><span class="w">
                    </span><span class="nl">"username"</span><span class="p">:</span><span class="w"> </span><span class="s2">"zhgchgli"</span><span class="w">
                </span><span class="p">},</span><span class="w">
                </span><span class="nl">"chat"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
                    </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">-4532420331</span><span class="p">,</span><span class="w">
                    </span><span class="nl">"title"</span><span class="p">:</span><span class="w"> </span><span class="s2">"My Nofify"</span><span class="p">,</span><span class="w">
                    </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"group"</span><span class="p">,</span><span class="w">
                    </span><span class="nl">"all_members_are_administrators"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
                </span><span class="p">},</span><span class="w">
                </span><span class="nl">"date"</span><span class="p">:</span><span class="w"> </span><span class="mi">1728726861</span><span class="p">,</span><span class="w">
                </span><span class="nl">"group_chat_created"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
            </span><span class="p">}</span><span class="w">
        </span><span class="p">},</span><span class="w">
        </span><span class="p">{</span><span class="w">
            </span><span class="nl">"update_id"</span><span class="p">:</span><span class="w"> </span><span class="mi">706454237</span><span class="p">,</span><span class="w">
            </span><span class="nl">"message"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
                </span><span class="nl">"message_id"</span><span class="p">:</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span><span class="w">
                </span><span class="nl">"from"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
                    </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">986128250</span><span class="p">,</span><span class="w">
                    </span><span class="nl">"is_bot"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w">
                    </span><span class="nl">"first_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Harry"</span><span class="p">,</span><span class="w">
                    </span><span class="nl">"last_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Li"</span><span class="p">,</span><span class="w">
                    </span><span class="nl">"username"</span><span class="p">:</span><span class="w"> </span><span class="s2">"zhgchgli"</span><span class="w">
                </span><span class="p">},</span><span class="w">
                </span><span class="nl">"chat"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
                    </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">-4532420331</span><span class="p">,</span><span class="w">
                    </span><span class="nl">"title"</span><span class="p">:</span><span class="w"> </span><span class="s2">"My Nofify"</span><span class="p">,</span><span class="w">
                    </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"group"</span><span class="p">,</span><span class="w">
                    </span><span class="nl">"all_members_are_administrators"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
                </span><span class="p">},</span><span class="w">
                </span><span class="nl">"date"</span><span class="p">:</span><span class="w"> </span><span class="mi">1728726864</span><span class="p">,</span><span class="w">
                </span><span class="nl">"new_chat_photo"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
                    </span><span class="p">{</span><span class="w">
                        </span><span class="nl">"file_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"AgACAgUAAxkBAAMCZwpHUEaLZSvFFYu8GiO-8qI_jVYAApfAMRu0B1BUJP-4u2wF6scBAAMCAANhAAM2BA"</span><span class="p">,</span><span class="w">
                        </span><span class="nl">"file_unique_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"AQADl8AxG7QHUFQAAQ"</span><span class="p">,</span><span class="w">
                        </span><span class="nl">"file_size"</span><span class="p">:</span><span class="w"> </span><span class="mi">5922</span><span class="p">,</span><span class="w">
                        </span><span class="nl">"width"</span><span class="p">:</span><span class="w"> </span><span class="mi">160</span><span class="p">,</span><span class="w">
                        </span><span class="nl">"height"</span><span class="p">:</span><span class="w"> </span><span class="mi">160</span><span class="w">
                    </span><span class="p">},</span><span class="w">
                    </span><span class="p">{</span><span class="w">
                        </span><span class="nl">"file_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"AgACAgUAAxkBAAMCZwpHUEaLZSvFFYu8GiO-8qI_jVYAApfAMRu0B1BUJP-4u2wF6scBAAMCAANiAAM2BA"</span><span class="p">,</span><span class="w">
                        </span><span class="nl">"file_unique_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"AQADl8AxG7QHUFRn"</span><span class="p">,</span><span class="w">
                        </span><span class="nl">"file_size"</span><span class="p">:</span><span class="w"> </span><span class="mi">15097</span><span class="p">,</span><span class="w">
                        </span><span class="nl">"width"</span><span class="p">:</span><span class="w"> </span><span class="mi">320</span><span class="p">,</span><span class="w">
                        </span><span class="nl">"height"</span><span class="p">:</span><span class="w"> </span><span class="mi">320</span><span class="w">
                    </span><span class="p">},</span><span class="w">
                    </span><span class="p">{</span><span class="w">
                        </span><span class="nl">"file_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"AgACAgUAAxkBAAMCZwpHUEaLZSvFFYu8GiO-8qI_jVYAApfAMRu0B1BUJP-4u2wF6scBAAMCAANjAAM2BA"</span><span class="p">,</span><span class="w">
                        </span><span class="nl">"file_unique_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"AQADl8AxG7QHUFQB"</span><span class="p">,</span><span class="w">
                        </span><span class="nl">"file_size"</span><span class="p">:</span><span class="w"> </span><span class="mi">37988</span><span class="p">,</span><span class="w">
                        </span><span class="nl">"width"</span><span class="p">:</span><span class="w"> </span><span class="mi">640</span><span class="p">,</span><span class="w">
                        </span><span class="nl">"height"</span><span class="p">:</span><span class="w"> </span><span class="mi">640</span><span class="w">
                    </span><span class="p">}</span><span class="w">
                </span><span class="p">]</span><span class="w">
            </span><span class="p">}</span><span class="w">
        </span><span class="p">}</span><span class="w">
    </span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>可以從回應中找到對應 Group Name + type=group 的巢狀 JSON 資料，其中的 id 就是 Group Chat ID:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nl">"chat"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">-4532420331</span><span class="p">,</span><span class="w">
  </span><span class="nl">"title"</span><span class="p">:</span><span class="w"> </span><span class="s2">"My Nofify"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"group"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"all_members_are_administrators"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<ul>
  <li><code class="language-plaintext highlighter-rouge">Group Chat Id</code> = <code class="language-plaintext highlighter-rouge">-4532420331</code></li>
</ul>

<blockquote>
  <p><strong><em>⚠️⚠️⚠️️</em></strong> <em>如果你的 Response 為空 <code class="language-plaintext highlighter-rouge">{ "ok": true, "result": [] }</code> 請嘗試在你的 Group 發送訊息 (e.g. <code class="language-plaintext highlighter-rouge">Hello</code> ) 再重新 Call API 應該就有了。</em></p>
</blockquote>

<h4 id="step-4-發送訊息">Step 4. 發送訊息</h4>

<p>我們可以使用 <code class="language-plaintext highlighter-rouge">/sendMessage</code> 發送訊息到 Group。</p>

<p><strong>Request:</strong></p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">curl</span> <span class="nv">'https</span><span class="p">:</span><span class="c1">//api.telegram.org/bot你的_BOT_API_Token/sendMessage' \</span>
<span class="o">--</span><span class="n">form</span> <span class="nv">'chat_id</span><span class="o">=</span><span class="s">"Group Chat Id"</span><span class="err">'</span> <span class="err">\</span>
<span class="o">--</span><span class="n">form</span> <span class="nv">'text</span><span class="o">=</span><span class="s">"訊息內容"</span><span class="err">'</span>
</code></pre></div></div>

<p><strong>範例：</strong></p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">curl</span> <span class="nv">'https</span><span class="p">:</span><span class="c1">//api.telegram.org/bot7814194578:AAEWpPJvKn06ID7D9FjV65aDKQLkGkz8cc8/sendMessage' \</span>
<span class="o">--</span><span class="n">form</span> <span class="nv">'chat_id</span><span class="o">=</span><span class="s">"-4532420331"</span><span class="err">'</span> <span class="err">\</span>
<span class="o">--</span><span class="n">form</span> <span class="nv">'text</span><span class="o">=</span><span class="s">"test"</span><span class="err">'</span>
</code></pre></div></div>
<ul>
  <li><a href="https://core.telegram.org/bots/api#sendmessage" target="_blank"><strong>API 參數可參考官方文件</strong></a></li>
</ul>

<p><strong>Response &amp; Result:</strong></p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"ok"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
  </span><span class="nl">"result"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"message_id"</span><span class="p">:</span><span class="w"> </span><span class="mi">5</span><span class="p">,</span><span class="w">
    </span><span class="nl">"from"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">7814194578</span><span class="p">,</span><span class="w">
      </span><span class="nl">"is_bot"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
      </span><span class="nl">"first_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Harry Test"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"username"</span><span class="p">:</span><span class="w"> </span><span class="s2">"harrytest56_bot"</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="nl">"chat"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">-4532420331</span><span class="p">,</span><span class="w">
      </span><span class="nl">"title"</span><span class="p">:</span><span class="w"> </span><span class="s2">"My Nofify"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"group"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"all_members_are_administrators"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="nl">"date"</span><span class="p">:</span><span class="w"> </span><span class="mi">1728727847</span><span class="p">,</span><span class="w">
    </span><span class="nl">"text"</span><span class="p">:</span><span class="w"> </span><span class="s2">"test"</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<ul>
  <li>發送成功取得以上 Response</li>
</ul>

<p><img src="/assets/6922e90ba90c/1*WG4ngRKKac1cICU4NvoEJA.webp" alt="" loading="lazy" decoding="async" width="984" height="1614" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5ODQiIGhlaWdodD0iMTYxNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<ul>
  <li>回 Telegram Group 就會出現剛發送的訊息內容。</li>
</ul>

<h3 id="22-移轉-line-notify-發送訊息到-telegram-bot-google-apps-script">(2/2) 移轉 Line Notify 發送訊息到 Telegram Bot (Google Apps Script)</h3>

<p>我的個人通知機器人服務是使用 Google Apps Script 達成的，因此以 Google Apps Script 轉換為例 (類 JavaScript)。</p>
<h4 id="原始的-line-notify-發送程式碼">原始的 Line Notify 發送程式碼：</h4>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">lineToken</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">XXXX</span><span class="dl">"</span><span class="p">;</span>

<span class="kd">function</span> <span class="nf">sendLineNotifyMessage</span><span class="p">(</span><span class="nx">message</span><span class="p">)</span> <span class="p">{</span>
  <span class="kd">var</span> <span class="nx">url</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">https://notify-api.line.me/api/notify</span><span class="dl">'</span><span class="p">;</span>
  
  <span class="kd">var</span> <span class="nx">options</span> <span class="o">=</span> <span class="p">{</span>
    <span class="na">method</span><span class="p">:</span> <span class="dl">'</span><span class="s1">post</span><span class="dl">'</span><span class="p">,</span>
    <span class="na">headers</span><span class="p">:</span> <span class="p">{</span>
      <span class="dl">'</span><span class="s1">Authorization</span><span class="dl">'</span><span class="p">:</span> <span class="dl">'</span><span class="s1">Bearer </span><span class="dl">'</span><span class="o">+</span><span class="nx">lineToken</span>
    <span class="p">},</span>
    <span class="na">payload</span><span class="p">:</span> <span class="p">{</span>
      <span class="dl">'</span><span class="s1">message</span><span class="dl">'</span><span class="p">:</span> <span class="nx">message</span>
    <span class="p">}</span>
  <span class="p">};</span> 
  <span class="kd">const</span> <span class="nx">response</span> <span class="o">=</span> <span class="nx">UrlFetchApp</span><span class="p">.</span><span class="nf">fetch</span><span class="p">(</span><span class="nx">url</span><span class="p">,</span> <span class="nx">options</span><span class="p">);</span>
  <span class="nx">Logger</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="nx">response</span><span class="p">.</span><span class="nf">getContentText</span><span class="p">());</span>
<span class="p">}</span>
</code></pre></div></div>

<p>可以看到非常簡單方便好用…</p>
<h4 id="移轉成-telegram-bot-發送程式碼">移轉成 Telegram Bot 發送程式碼：</h4>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">telegramToken</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">你的_BOT_API_Token</span><span class="dl">"</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">TelegramChatId</span> <span class="o">=</span> <span class="p">{</span>
  <span class="na">GA</span><span class="p">:</span> <span class="o">-</span><span class="mi">123456</span><span class="p">,</span>
  <span class="na">GITHUB</span><span class="p">:</span> <span class="o">-</span><span class="mi">123457</span><span class="p">,</span>
  <span class="na">MEDIUM</span><span class="p">:</span> <span class="o">-</span><span class="mi">123458</span>
<span class="p">};</span>

<span class="kd">function</span> <span class="nf">sendNotifyMessage</span><span class="p">(</span><span class="nx">message</span><span class="p">,</span> <span class="nx">chatId</span><span class="p">)</span> <span class="p">{</span>
  <span class="kd">var</span> <span class="nx">url</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">https://api.telegram.org/bot</span><span class="dl">"</span><span class="o">+</span><span class="nx">telegramToken</span><span class="o">+</span><span class="dl">"</span><span class="s2">/sendMessage</span><span class="dl">"</span><span class="p">;</span>
  
  <span class="kd">const</span> <span class="nx">payload</span> <span class="o">=</span> <span class="p">{</span>
    <span class="dl">"</span><span class="s2">chat_id</span><span class="dl">"</span><span class="p">:</span> <span class="nx">chatId</span><span class="p">,</span>
    <span class="dl">"</span><span class="s2">text</span><span class="dl">"</span><span class="p">:</span> <span class="nx">message</span><span class="p">,</span>
    <span class="dl">"</span><span class="s2">parse_mode</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Markdown</span><span class="dl">"</span>
  <span class="p">}</span> 
  <span class="kd">const</span> <span class="nx">options</span> <span class="o">=</span> <span class="p">{</span>
    <span class="dl">'</span><span class="s1">method</span><span class="dl">'</span><span class="p">:</span> <span class="dl">'</span><span class="s1">post</span><span class="dl">'</span><span class="p">,</span>
    <span class="dl">'</span><span class="s1">contentType</span><span class="dl">'</span><span class="p">:</span> <span class="dl">'</span><span class="s1">application/json</span><span class="dl">'</span><span class="p">,</span>
    <span class="dl">'</span><span class="s1">muteHttpExceptions</span><span class="dl">'</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
    <span class="dl">'</span><span class="s1">payload</span><span class="dl">'</span><span class="p">:</span> <span class="nx">JSON</span><span class="p">.</span><span class="nf">stringify</span><span class="p">(</span><span class="nx">payload</span><span class="p">)</span>
  <span class="p">};</span>

  <span class="kd">const</span> <span class="nx">response</span> <span class="o">=</span> <span class="nx">UrlFetchApp</span><span class="p">.</span><span class="nf">fetch</span><span class="p">(</span><span class="nx">url</span><span class="p">,</span> <span class="nx">options</span><span class="p">);</span>
  <span class="nx">Logger</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="nx">response</span><span class="p">.</span><span class="nf">getContentText</span><span class="p">());</span>
<span class="p">}</span>
</code></pre></div></div>

<p>照前面 Telegram Bot 設定步驟取得的資訊。</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">telegramToken</code> = <code class="language-plaintext highlighter-rouge">你的_BOT_API_Token</code></li>
  <li><code class="language-plaintext highlighter-rouge">TelegramChatId</code> 這邊是我自己多定義的方法，因為實務上我希望不同的通知分別傳送到不同的 Group，因此定義了一個結構管理目標 Group 與他的 <code class="language-plaintext highlighter-rouge">Group Chat Id</code> 。</li>
</ul>

<p><code class="language-plaintext highlighter-rouge">/sendMessage</code> <a href="https://core.telegram.org/bots/api#sendmessage" target="_blank"><strong>API 參數，更多參數、細節可參考官方文件</strong></a> ，以下是我自己會用到的參數：</p>
<ul>
  <li>text: 訊息內容 (必帶)</li>
  <li>chat_id: 目標 Group Chat Id (必帶)</li>
  <li>parse_mode: 訊息內容解析方式，這邊我指定 <code class="language-plaintext highlighter-rouge">Markdown</code></li>
  <li>disable_web_page_preview: 訊息內容的連結是否不要預覽，這邊設 <code class="language-plaintext highlighter-rouge">true</code> 關閉，可以讓訊息顯示更簡潔。</li>
</ul>

<p><strong>使用方式：</strong></p>
<div class="language-scss highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">sendNotifyMessage</span><span class="o">(</span><span class="s2">"Hello"</span><span class="o">,</span> <span class="nt">TelegramChatId</span><span class="nc">.MEDIUM</span><span class="o">)</span> <span class="o">//</span> <span class="err">發送</span> <span class="nt">Hello</span> <span class="err">訊息到</span> <span class="nt">MEDIUM</span> <span class="nt">Group</span> <span class="nt">Chat</span> <span class="nt">Id</span>
<span class="nt">sendNotifyMessage</span><span class="o">(</span><span class="s2">"Hello"</span><span class="o">,</span> <span class="nt">-1234</span><span class="o">)</span> <span class="o">//</span> <span class="err">發送</span> <span class="nt">Hello</span> <span class="err">訊息到</span> <span class="nt">-1234</span> <span class="nt">Group</span> <span class="nt">Chat</span> <span class="nt">Id</span>
</code></pre></div></div>
<h3 id="成果-1">成果</h3>

<p>以我的 <a href="/posts/zrealm-robotic-process-automation/google-apps-script-三步驟打造免費-github-repo-star-通知器-實時獲取-star-動態-382218e15697/">Github Repo Star Notifier 機器人</a> 為例：</p>

<p><img src="/assets/6922e90ba90c/1*yJDcnb7n1fIJAM-AV1Qk6w.webp" alt="" loading="lazy" decoding="async" width="962" height="1374" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NjIiIGhlaWdodD0iMTM3NCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/6922e90ba90c/1*5kBotSkNf9nN8NV-lCwZoQ.webp" alt="" loading="lazy" decoding="async" width="870" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4NzAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<ul>
  <li>驗證成功！ 當有人按我的 Repo Star 時能正確改發送通知到 Telegram Group！🎉🎉🎉</li>
  <li>製作方式可參考我之前的文章「 <a href="/posts/zrealm-robotic-process-automation/google-apps-script-三步驟打造免費-github-repo-star-通知器-實時獲取-star-動態-382218e15697/">使用 Google Apps Script 三步驟免費建立 Github Repo Star Notifier</a> 」</li>
</ul>

<h4 id="設定特殊聲音或靜音">設定特殊聲音或靜音</h4>

<p><img src="/assets/6922e90ba90c/1*kQsYkefi0SD7e9t4PerdRA.webp" alt="" loading="lazy" decoding="async" width="1004" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDA0IiBoZWlnaHQ9IjEyMDAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p>比 Line Notify 更棒的事是我們還可以對不同的 Group 設定不同的通知聲音或是靜音。</p>
<h3 id="與-telegram-bot-交互-command-x-使用-google-apps-script">與 Telegram Bot 交互 (Command) x 使用 Google Apps Script</h3>

<p>除了替代 Notify 功能之外， Telegram Bot 還能輕易地實現與使用者交互的功能 — Telegram Bot Command。</p>

<p>回到我的使用場景，我的機器人會定時或是 Webhook 觸發傳送通知訊息送給我；但有時候我也想手動觸發機器人立刻取得當前結果，以往 Line Notify 沒有這個功能，以 Google Apps Script 來說就只能暴力的設定一個網址，只要打開網址就會觸發，很不好用。</p>

<p>Telegram Bot Command 就能讓我直接輸入指令訊息，命令機器人立刻執行我想做的事情。</p>

<blockquote>
  <p><em>本文以 <a href="https://script.google.com/home" target="_blank">Goolge Apps Script</a> 為例，Google Apps Script 詳細介紹可參考我之前文章「 <a href="/posts/zrealm-robotic-process-automation/google-apps-script-打造每日數據報表自動化-rpa-快速串接-google-analytics-與-google-sheet-f6713ba3fee3/"><strong>使用 Google Apps Script 實現 Google 服務 RPA 自動化</strong></a> 」。</em></p>
</blockquote>

<h4 id="step-1-使用-google-apps-script-實現-command-處理邏輯">Step 1. 使用 Google Apps Script 實現 Command 處理邏輯</h4>
<ul>
  <li>前往 Google Apps Script 首頁</li>
  <li>左上方「建立新專案」</li>
  <li>點擊「未命名專案」輸入專案名稱 e.g. <code class="language-plaintext highlighter-rouge">Telegram</code></li>
  <li>貼上基礎程式碼：</li>
</ul>

<p><img src="/assets/6922e90ba90c/1*ZeW4O7Mdgcyj0VsSLDpxeA.webp" alt="" loading="lazy" decoding="async" width="1400" height="1288" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjEyODgiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">telegramToken</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">你的_BOT_API_Token</span><span class="dl">"</span><span class="p">;</span>

<span class="kd">function</span> <span class="nf">doPost</span><span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">content</span> <span class="o">=</span> <span class="nx">JSON</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">postData</span><span class="p">.</span><span class="nx">contents</span><span class="p">);</span>
  <span class="k">if </span><span class="p">(</span><span class="nx">content</span><span class="p">.</span><span class="nx">message</span> <span class="o">&amp;&amp;</span> <span class="nx">content</span><span class="p">.</span><span class="nx">message</span><span class="p">.</span><span class="nx">text</span><span class="p">)</span> <span class="p">{</span>
    <span class="kd">const</span> <span class="nx">command</span> <span class="o">=</span> <span class="nx">content</span><span class="p">.</span><span class="nx">message</span><span class="p">.</span><span class="nx">text</span><span class="p">.</span><span class="nf">split</span><span class="p">(</span><span class="dl">'</span><span class="s1"> </span><span class="dl">'</span><span class="p">)[</span><span class="mi">0</span><span class="p">];</span>
    <span class="kd">const</span> <span class="nx">chatId</span> <span class="o">=</span> <span class="nx">content</span><span class="p">.</span><span class="nx">message</span><span class="p">.</span><span class="nx">chat</span><span class="p">.</span><span class="nx">id</span><span class="p">;</span>

    <span class="k">if </span><span class="p">(</span><span class="nx">command</span><span class="p">.</span><span class="nf">startsWith</span><span class="p">(</span><span class="dl">"</span><span class="s2">/update</span><span class="dl">"</span><span class="p">))</span> <span class="p">{</span> 
      <span class="c1">// 收到 /update 指令</span>
      <span class="c1">// 在這邊處理你想做的事...然後回應...</span>
      <span class="nf">sendNotifyMessage</span><span class="p">(</span><span class="dl">"</span><span class="s2">你好.....</span><span class="se">\n</span><span class="s2">指令:</span><span class="dl">"</span><span class="o">+</span><span class="nx">command</span><span class="p">,</span> <span class="nx">chatId</span><span class="p">);</span>
    <span class="p">}</span>
  <span class="p">}</span>

  <span class="k">return</span> <span class="nx">HtmlService</span><span class="p">.</span><span class="nf">createHtmlOutput</span><span class="p">(</span><span class="dl">"</span><span class="s2">OK!</span><span class="dl">"</span><span class="p">);</span>
<span class="p">}</span>

<span class="kd">function</span> <span class="nf">sendNotifyMessage</span><span class="p">(</span><span class="nx">message</span><span class="p">,</span> <span class="nx">chatId</span><span class="p">)</span> <span class="p">{</span>
  <span class="kd">var</span> <span class="nx">url</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">https://api.telegram.org/bot</span><span class="dl">"</span><span class="o">+</span><span class="nx">telegramToken</span><span class="o">+</span><span class="dl">"</span><span class="s2">/sendMessage</span><span class="dl">"</span><span class="p">;</span>
  
  <span class="kd">const</span> <span class="nx">payload</span> <span class="o">=</span> <span class="p">{</span>
    <span class="dl">"</span><span class="s2">chat_id</span><span class="dl">"</span><span class="p">:</span> <span class="nx">chatId</span><span class="p">,</span>
    <span class="dl">"</span><span class="s2">text</span><span class="dl">"</span><span class="p">:</span> <span class="nx">message</span><span class="p">,</span>
    <span class="dl">"</span><span class="s2">disable_web_page_preview</span><span class="dl">"</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
    <span class="dl">"</span><span class="s2">parse_mode</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Markdown</span><span class="dl">"</span>
  <span class="p">}</span> 
  <span class="kd">const</span> <span class="nx">options</span> <span class="o">=</span> <span class="p">{</span>
    <span class="dl">'</span><span class="s1">method</span><span class="dl">'</span><span class="p">:</span> <span class="dl">'</span><span class="s1">post</span><span class="dl">'</span><span class="p">,</span>
    <span class="dl">'</span><span class="s1">contentType</span><span class="dl">'</span><span class="p">:</span> <span class="dl">'</span><span class="s1">application/json</span><span class="dl">'</span><span class="p">,</span>
    <span class="dl">'</span><span class="s1">muteHttpExceptions</span><span class="dl">'</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
    <span class="dl">'</span><span class="s1">payload</span><span class="dl">'</span><span class="p">:</span> <span class="nx">JSON</span><span class="p">.</span><span class="nf">stringify</span><span class="p">(</span><span class="nx">payload</span><span class="p">)</span>
  <span class="p">};</span>

  <span class="kd">const</span> <span class="nx">response</span> <span class="o">=</span> <span class="nx">UrlFetchApp</span><span class="p">.</span><span class="nf">fetch</span><span class="p">(</span><span class="nx">url</span><span class="p">,</span> <span class="nx">options</span><span class="p">);</span>
  <span class="nx">Logger</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="nx">response</span><span class="p">.</span><span class="nf">getContentText</span><span class="p">());</span>
<span class="p">}</span>
</code></pre></div></div>
<ul>
  <li><code class="language-plaintext highlighter-rouge">telegramToken</code> = <code class="language-plaintext highlighter-rouge">你的_BOT_API_Token</code></li>
  <li>上述 Demo 程式我們在收到 Post 請求、Command 參數等於 <code class="language-plaintext highlighter-rouge">/update</code> 時回應 <code class="language-plaintext highlighter-rouge">你好…</code> 模擬收到指令後處理後回應的場景。</li>
</ul>

<h4 id="step-2-完成-google-apps-script-web-部署">Step 2. 完成 Google Apps Script Web 部署</h4>

<p><img src="/assets/6922e90ba90c/1*JfJfs4bYsSfsZYGVkApCSg.webp" alt="" loading="lazy" decoding="async" width="1200" height="705" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjcwNSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/6922e90ba90c/1*TwSm45_Xwv4p7z4o9HJz2w.webp" alt="" loading="lazy" decoding="async" width="1400" height="1109" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjExMDkiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<p><img src="/assets/6922e90ba90c/1*7RLJXZ3APnEI4V9bKqp3eg.webp" alt="" loading="lazy" decoding="async" width="1200" height="941" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAwIiBoZWlnaHQ9Ijk0MSI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<ul>
  <li>右上角「部署」-&gt; 「新增部署作業」</li>
  <li>左上角「設定」-&gt;「網頁應用程式」</li>
  <li>誰可以存取選擇「所有人」</li>
</ul>

<p><img src="/assets/6922e90ba90c/1*aRBaOOnhwBeK05l9e3Yv8g.webp" alt="" loading="lazy" decoding="async" width="568" height="380" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1NjgiIGhlaWdodD0iMzgwIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p><img src="/assets/6922e90ba90c/1*FtY8NL36peDbOB4JWdzhDA.webp" alt="" loading="lazy" decoding="async" width="1296" height="932" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjk2IiBoZWlnaHQ9IjkzMiI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/6922e90ba90c/1*e2E41TCEf5O7nSOnVzpgbw.webp" alt="" loading="lazy" decoding="async" width="1400" height="768" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9Ijc2OCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<ul>
  <li>新增部署作業，選擇「授予存取權」</li>
  <li>跳出帳號視窗，選擇你的 Google 登入帳號</li>
  <li>跳出警告視窗，選擇「Advanced」-&gt;「Go to <code class="language-plaintext highlighter-rouge">專案名稱</code> (unsafe)」</li>
  <li>選擇「Allow」</li>
</ul>

<p><img src="/assets/6922e90ba90c/1*n0iO-XSPqifUKUkIgVQUSg.webp" alt="" loading="lazy" decoding="async" width="1400" height="1109" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAwIiBoZWlnaHQ9IjExMDkiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlZGUyY2YiLz48L3N2Zz4=" /></p>

<ul>
  <li>網頁應用程式網址： <code class="language-plaintext highlighter-rouge">你的 Webhook 網址</code> <strong>。</strong> 
複製下來。
e.g. <code class="language-plaintext highlighter-rouge">https://script.google.com/macros/s/AKfycbx2oFv-eB4LezdOk3P3aoEZVhx_PI6n_YnTNP7WVVQSaiRU52di5bKNThsvIZxus3Si/exec</code></li>
</ul>

<blockquote>
  <p><em>Google Apps Script 網頁應用程式部署、更新、使用、除錯可參考我之前的文章「 <a href="/posts/zrealm-robotic-process-automation/google-apps-script-打造每日數據報表自動化-rpa-快速串接-google-analytics-與-google-sheet-f6713ba3fee3/"><strong>使用 Google Apps Script 實現 Google 服務 RPA 自動化</strong></a> 」。</em></p>
</blockquote>

<blockquote>
  <p><strong><em>⚠️⚠️⚠️ 請注意，如果 Google Apps Script 程式碼有變更要點擊部署-&gt;管理部署-&gt;選擇建立新版本才會生效，細節可參考上述文章。</em></strong></p>
</blockquote>

<blockquote>
  <p><strong><em>⚠️⚠️⚠️請注意，如果 Google Apps Script 程式碼有變更要點擊部署-&gt;管理部署-&gt;選擇建立新版本才會生效，細節可參考上述文章。</em></strong></p>
</blockquote>

<blockquote>
  <p><strong><em>⚠️⚠️⚠️請注意，如果 Google Apps Script 程式碼有變更要點擊部署-&gt;管理部署-&gt;選擇建立新版本才會生效，細節可參考上述文章。</em></strong></p>
</blockquote>

<h4 id="step-3-註冊-webhook">Step 3. 註冊 Webhook</h4>

<p>使用 Telegram API <code class="language-plaintext highlighter-rouge">/setWebhook</code> 註冊你的 Webhook 網址。</p>

<p><strong>Request:</strong></p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">curl</span> <span class="o">--</span><span class="n">location</span> <span class="nv">'https</span><span class="p">:</span><span class="c1">//api.telegram.org/你的_BOT_API_Token/setWebhook' \</span>
<span class="o">--</span><span class="n">form</span> <span class="nv">'url</span><span class="o">=</span><span class="s">"你的 Webhook 網址"</span><span class="err">'</span>
</code></pre></div></div>

<p><strong>Response:</strong></p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
    </span><span class="nl">"ok"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
    </span><span class="nl">"result"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
    </span><span class="nl">"description"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Webhook was set"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<h4 id="測試">測試</h4>

<p><img src="/assets/6922e90ba90c/1*wNhksFLSip5DC0rXqMA_0A.webp" alt="" loading="lazy" decoding="async" width="870" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4NzAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<p><img src="/assets/6922e90ba90c/1*d_B3h3C30vI61p3ALP77yw.webp" alt="" loading="lazy" decoding="async" width="870" height="1200" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4NzAiIGhlaWdodD0iMTIwMCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2VkZTJjZiIvPjwvc3ZnPg==" /></p>

<ul>
  <li>我們會根據不同的 Chat Id 回應，因此不管是 1:1 對機器人或是有機器人在的 Group 群組都能回應。</li>
  <li>成功 🎉🎉🎉</li>
</ul>

<h3 id="下篇">下篇：</h3>
<ul>
  <li><a href="/posts/zrealm-robotic-process-automation/ga4-自動數據通知機器人-3-步驟用-google-apps-script-串接-telegram-bot-1e85b8df2348/">簡單 3 步驟 — 打造免費 GA4 自動數據通知機器人</a></li>
</ul>

<h3 id="其他-google-apps-script-自動化文章">其他 Google Apps Script 自動化文章</h3>
<ul>
  <li><a href="/posts/zrealm-robotic-process-automation/google-apps-script-打造每日數據報表自動化-rpa-快速串接-google-analytics-與-google-sheet-f6713ba3fee3/">使用 Google Apps Script 實現 Google 服務 RPA 自動化</a></li>
  <li><a href="/posts/zrealm-robotic-process-automation/slack-app-整合-chatgpt-利用-openai-api-與-google-cloud-functions-自建智能助理-bd94cc88f9c9/">Slack &amp; ChatGPT Integration</a></li>
  <li><a href="/posts/zrealm-robotic-process-automation/google-apps-script-三步驟打造免費-github-repo-star-通知器-實時獲取-star-動態-382218e15697/">使用 Google Apps Script 三步驟免費建立 Github Repo Star Notifier</a></li>
</ul>

<h4 id="附註">附註</h4>

<p><img src="/assets/6922e90ba90c/1*zOFjvO5SSwbJgFL8kMff0w.webp" alt="" loading="lazy" decoding="async" width="706" height="284" lqip="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3MDYiIGhlaWdodD0iMjg0Ij48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZWRlMmNmIi8+PC9zdmc+" /></p>

<p>本文也是我 Medium 的第 100 篇文章(2018/10 發表 <a href="/posts/zrealm-life/blog經營心路歷程-從新手到技術分享-重燃寫作動力的秘訣-b7a3fb3d5531/">第一篇</a> ，6 年了)，堅持不懈、繼續努力，詳細心得跟數據等到破 1000 Followers (2024/10 目前 925) 或是總瀏覽數破 1,000,000 (2024/10 目前 984,549) 時再來分享。</p>]]></content>
  </entry>
</feed>
