<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <author>
    <name>Heidi Liu</name>
  </author>
  <generator uri="https://hexo.io/">Hexo</generator>
  <id>https://heidiliu2020.github.io/</id>
  <link href="https://heidiliu2020.github.io/" rel="alternate"/>
  <link href="https://heidiliu2020.github.io/atom.xml" rel="self"/>
  <rights>All rights reserved 2026, Heidi Liu</rights>
  <subtitle>Heidi's Blog</subtitle>
  <title>前端新米</title>
  <updated>2026-04-23T07:13:04.312Z</updated>
  <entry>
    <author>
      <name>Heidi Liu</name>
    </author>
    <category term="日本生活" scheme="https://heidiliu2020.github.io/categories/%E6%97%A5%E6%9C%AC%E7%94%9F%E6%B4%BB/"/>
    <content>
      <![CDATA[<p>Coming Soon…</p>]]>
    </content>
    <id>https://heidiliu2020.github.io/japan-life-coming-soon/</id>
    <link href="https://heidiliu2020.github.io/japan-life-coming-soon/"/>
    <published>2026-04-23T04:00:00.000Z</published>
    <summary>
      <![CDATA[<p>Coming Soon…</p>]]>
    </summary>
    <title>日本生活｜Coming Soon</title>
    <updated>2026-04-23T07:13:04.312Z</updated>
  </entry>
  <entry>
    <author>
      <name>Heidi Liu</name>
    </author>
    <category term="技術學習" scheme="https://heidiliu2020.github.io/categories/%E6%8A%80%E8%A1%93%E5%AD%B8%E7%BF%92/"/>
    <category term="AI" scheme="https://heidiliu2020.github.io/categories/%E6%8A%80%E8%A1%93%E5%AD%B8%E7%BF%92/AI/"/>
    <category term="AI" scheme="https://heidiliu2020.github.io/tags/AI/"/>
    <category term="Claude" scheme="https://heidiliu2020.github.io/tags/Claude/"/>
    <category term="Structured Outputs" scheme="https://heidiliu2020.github.io/tags/Structured-Outputs/"/>
    <content>
      <![CDATA[<h2 id="Structured-outputs-結構化輸出"><a href="#Structured-outputs-結構化輸出" class="headerlink" title="Structured outputs 結構化輸出"></a>Structured outputs 結構化輸出</h2><p><span class="exturl" data-url="aHR0cHM6Ly9wbGF0Zm9ybS5jbGF1ZGUuY29tL2RvY3MvZW4vYnVpbGQtd2l0aC1jbGF1ZGUvc3RydWN0dXJlZC1vdXRwdXRz" title="https://platform.claude.com/docs/en/build-with-claude/structured-outputs">Structured outputs（結構化輸出）<i class="fa fa-external-link"></i></span>是 Claude API 提供的功能，用來讓模型輸出符合指定的 JSON schema。透過 <strong>Constrained decoding（約束解碼）</strong>，在「生成過程中」限制模型只能產生合法的 JSON 結構，而不是在輸出後再做檢查。</p><span id="more"></span><p>換言之，並非在回應生成後才做 JSON 檢查，而是在 token 生成階段就進行的限制：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">// 一般生成：</span><br><span class="line">  Claude → 任意 token → 任意文字（格式不可預測）</span><br><span class="line"></span><br><span class="line">// Constrained decoding：</span><br><span class="line">  Claude → 只允許符合 JSON schema 的 token → 保證有效 JSON</span><br></pre></td></tr></table></figure><p>因此可以大幅降低以下問題：</p><ul><li>JSON syntax error</li><li>缺少必要欄位</li><li>型別錯誤</li></ul><hr><h2 id="兩個核心功能：JSON-outputs-Strict-tool-use"><a href="#兩個核心功能：JSON-outputs-Strict-tool-use" class="headerlink" title="兩個核心功能：JSON outputs / Strict tool use"></a>兩個核心功能：JSON outputs / Strict tool use</h2><p>Structured outputs 提供兩種互補功能：</p><table><thead><tr><th>功能</th><th>控制對象</th><th>API 參數</th><th>用途</th></tr></thead><tbody><tr><td><strong>JSON outputs</strong></td><td>Claude 的回應本身</td><td><code>output_config.format</code></td><td>限制回應為符合 schema 的 JSON</td></tr><tr><td><strong>Strict tool use</strong></td><td>Claude 的 tool_use block</td><td><code>strict: true</code></td><td>限制 tool 呼叫參數符合 input_schema</td></tr></tbody></table><p>兩者目的都是限制 <strong>Claude 的輸出</strong>，不是驗證 tool 的執行結果（tool 本身仍需由應用程式負責處理與驗證）。當需要「工具 + 結構化最終回應」的情況時也可以同時使用。</p><h3 id="JSON-outputs-範例"><a href="#JSON-outputs-範例" class="headerlink" title="JSON outputs 範例"></a>JSON outputs 範例</h3><ul><li>使用場景：不需要工具，直接讓 Claude 回應結構化資料（JSON），用來做資料提取與分類</li></ul><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> response = <span class="keyword">await</span> client.<span class="property">messages</span>.<span class="title function_">create</span>(&#123;</span><br><span class="line">  <span class="attr">model</span>: <span class="string">&quot;claude-opus-4-6&quot;</span>,</span><br><span class="line">  <span class="attr">max_tokens</span>: <span class="number">1024</span>,</span><br><span class="line">  <span class="attr">messages</span>: [</span><br><span class="line">    &#123; <span class="attr">role</span>: <span class="string">&quot;user&quot;</span>, <span class="attr">content</span>: <span class="string">&quot;Extract: John Smith (john@example.com) wants Enterprise plan&quot;</span> &#125;</span><br><span class="line">  ],</span><br><span class="line">  <span class="attr">output_config</span>: &#123;</span><br><span class="line">    <span class="attr">format</span>: &#123;</span><br><span class="line">      <span class="attr">type</span>: <span class="string">&quot;json_schema&quot;</span>,</span><br><span class="line">      <span class="attr">schema</span>: &#123;</span><br><span class="line">        <span class="attr">type</span>: <span class="string">&quot;object&quot;</span>,</span><br><span class="line">        <span class="attr">properties</span>: &#123;</span><br><span class="line">          <span class="attr">name</span>: &#123; <span class="attr">type</span>: <span class="string">&quot;string&quot;</span> &#125;,</span><br><span class="line">          <span class="attr">email</span>: &#123; <span class="attr">type</span>: <span class="string">&quot;string&quot;</span> &#125;,</span><br><span class="line">          <span class="attr">plan</span>: &#123; <span class="attr">type</span>: <span class="string">&quot;string&quot;</span> &#125;,</span><br><span class="line">        &#125;,</span><br><span class="line">        <span class="attr">required</span>: [<span class="string">&quot;name&quot;</span>, <span class="string">&quot;email&quot;</span>, <span class="string">&quot;plan&quot;</span>],</span><br><span class="line">        <span class="attr">additionalProperties</span>: <span class="literal">false</span>,</span><br><span class="line">      &#125;,</span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// → &#123; &quot;name&quot;: &quot;John Smith&quot;, &quot;email&quot;: &quot;john@example.com&quot;, &quot;plan&quot;: &quot;Enterprise&quot; &#125;</span></span><br></pre></td></tr></table></figure><h4 id="搭配-Zod（TypeScript-SDK）"><a href="#搭配-Zod（TypeScript-SDK）" class="headerlink" title="搭配 Zod（TypeScript SDK）"></a>搭配 Zod（TypeScript SDK）</h4><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; z &#125; <span class="keyword">from</span> <span class="string">&quot;zod&quot;</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; zodOutputFormat &#125; <span class="keyword">from</span> <span class="string">&quot;@anthropic-ai/sdk/helpers/zod&quot;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> <span class="title class_">ContactSchema</span> = z.<span class="title function_">object</span>(&#123;</span><br><span class="line">  <span class="attr">name</span>: z.<span class="title function_">string</span>(),</span><br><span class="line">  <span class="attr">email</span>: z.<span class="title function_">string</span>(),</span><br><span class="line">  <span class="attr">plan</span>: z.<span class="title function_">string</span>(),</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> response = <span class="keyword">await</span> client.<span class="property">messages</span>.<span class="title function_">parse</span>(&#123;</span><br><span class="line">  <span class="attr">model</span>: <span class="string">&quot;claude-opus-4-6&quot;</span>,</span><br><span class="line">  <span class="attr">max_tokens</span>: <span class="number">1024</span>,</span><br><span class="line">  <span class="attr">messages</span>: [&#123; <span class="attr">role</span>: <span class="string">&quot;user&quot;</span>, <span class="attr">content</span>: <span class="string">&quot;Extract: John (john@example.com) wants Enterprise&quot;</span> &#125;],</span><br><span class="line">  <span class="attr">output_config</span>: &#123; <span class="attr">format</span>: <span class="title function_">zodOutputFormat</span>(<span class="title class_">ContactSchema</span>) &#125;,</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(response.<span class="property">parsed_output</span>.<span class="property">email</span>);</span><br><span class="line"><span class="comment">// 自動帶有型別推斷</span></span><br></pre></td></tr></table></figure><h3 id="Strict-tool-use-範例"><a href="#Strict-tool-use-範例" class="headerlink" title="Strict tool use 範例"></a>Strict tool use 範例</h3><ul><li>使用場景：在 Claude 呼叫工具時，確保產生的 <code>tool_use</code> block 參數符合 <code>input_schema</code></li></ul><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> response = <span class="keyword">await</span> client.<span class="property">messages</span>.<span class="title function_">create</span>(&#123;</span><br><span class="line">  <span class="attr">model</span>: <span class="string">&quot;claude-opus-4-6&quot;</span>,</span><br><span class="line">  <span class="attr">max_tokens</span>: <span class="number">1024</span>,</span><br><span class="line">  <span class="attr">messages</span>: [&#123; <span class="attr">role</span>: <span class="string">&quot;user&quot;</span>, <span class="attr">content</span>: <span class="string">&quot;What&#x27;s the weather in San Francisco?&quot;</span> &#125;],</span><br><span class="line">  <span class="attr">tools</span>: [&#123;</span><br><span class="line">    <span class="attr">name</span>: <span class="string">&quot;get_weather&quot;</span>,</span><br><span class="line">    <span class="attr">description</span>: <span class="string">&quot;Get current weather for a city&quot;</span>,</span><br><span class="line">    <span class="attr">strict</span>: <span class="literal">true</span>,      <span class="comment">// ← 啟用嚴格模式</span></span><br><span class="line">    <span class="attr">input_schema</span>: &#123;</span><br><span class="line">      <span class="attr">type</span>: <span class="string">&quot;object&quot;</span>,</span><br><span class="line">      <span class="attr">properties</span>: &#123;</span><br><span class="line">        <span class="attr">location</span>: &#123; <span class="attr">type</span>: <span class="string">&quot;string&quot;</span> &#125;,</span><br><span class="line">        <span class="attr">unit</span>: &#123; <span class="attr">type</span>: <span class="string">&quot;string&quot;</span>, <span class="attr">enum</span>: [<span class="string">&quot;celsius&quot;</span>, <span class="string">&quot;fahrenheit&quot;</span>] &#125;,</span><br><span class="line">      &#125;,</span><br><span class="line">      <span class="attr">required</span>: [<span class="string">&quot;location&quot;</span>],</span><br><span class="line">      <span class="attr">additionalProperties</span>: <span class="literal">false</span>,</span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;],</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// tool_use arguments 保證符合 input_schema</span></span><br><span class="line"><span class="comment">// 不會出現 location: 123 型別錯誤 or 缺少 location 欄位</span></span><br></pre></td></tr></table></figure><hr><h2 id="Claude-API-的資料流"><a href="#Claude-API-的資料流" class="headerlink" title="Claude API 的資料流"></a>Claude API 的資料流</h2><p>根據官方文件，一段 Agent Flow 的基本資料流如下：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">User</span><br><span class="line">↓</span><br><span class="line">Claude    // 模型決定是否呼叫工具</span><br><span class="line">↓</span><br><span class="line">tool_use  // Claude 產生 tool 呼叫（Strict tool use 限制參數）</span><br><span class="line">↓</span><br><span class="line">Tool execution  // 應用程式執行工具</span><br><span class="line">↓</span><br><span class="line">tool_result</span><br><span class="line">↓</span><br><span class="line">Claude    // Claude 產生最終回應（可使用 JSON outputs）</span><br><span class="line">↓</span><br><span class="line">Assistant response</span><br></pre></td></tr></table></figure><p>在這個流程中，Structured outputs 主要影響兩個階段：</p><ul><li><strong>JSON outputs</strong>：限制 Claude 的最終回應格式</li><li><strong>Strict tool use</strong>：限制 <code>tool_use</code> block 的參數格式</li></ul><p>透過 Structured outputs，能夠讓應用程式可以更安全地解析 Claude 的輸出，避免 JSON 格式錯誤或欄位缺失。</p><h2 id="在-AI-應用中的角色"><a href="#在-AI-應用中的角色" class="headerlink" title="在 AI 應用中的角色"></a>在 AI 應用中的角色</h2><p>如果從 AI 應用系統的角度來看，可以把整體流程放在一個簡化的架構中（此為常見 application pattern，非 Claude 官方定義）：</p><table><thead><tr><th>Layer</th><th>功能</th></tr></thead><tbody><tr><td>Model interaction</td><td>與 LLM 溝通（Claude API）</td></tr><tr><td>Integration</td><td>呼叫外部服務與工具</td></tr><tr><td>Presentation</td><td>將結果呈現在 UI</td></tr></tbody></table><p>在這個架構下，Structured outputs 的角色可以理解為：</p><blockquote><p>提供一個「可靠的 LLM → application data interface」</p></blockquote><p>也就是讓 LLM 的輸出，從原本難以預測的自然語言，轉變為可以被程式穩定處理的資料格式。</p><p>在沒有 structured outputs 的情況下，Claude 的回應可能是：</p><ul><li>段落文字</li><li>Markdown</li><li>或每次都是不同格式</li></ul><p>這對應用程式來說是難以直接解析的。而透過 Structured outputs 機制：</p><ul><li>JSON outputs → 保證最終輸出結構</li><li>Strict tool use → 保證 tool 呼叫參數正確</li></ul><p>資料流會變成：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">LLM（Claude）</span><br><span class="line">↓</span><br><span class="line">可靠的 JSON / tool arguments</span><br><span class="line">↓</span><br><span class="line">Application code（安全解析與使用）</span><br></pre></td></tr></table></figure><p>換句話說，Structured outputs 的核心價值，是讓 LLM 從「不可預期的文字生成」變成「可預測的資料接口」，這也是在實作 AI Agent 或 Generative UI 時非常重要的一環。</p><h2 id="小結"><a href="#小結" class="headerlink" title="小結"></a>小結</h2><p>Structured outputs 提供兩個核心能力：</p><ul><li>JSON outputs：控制最終回應格式</li><li>Strict tool use：控制工具呼叫參數</li></ul><p>讓 LLM 的輸出可以被穩定地當成資料使用，而不只是自然語言，這也是在實作 AI Agent 與工具整合時的關鍵基礎。。</p><h2 id="參考資料"><a href="#參考資料" class="headerlink" title="參考資料"></a>參考資料</h2><ul><li><span class="exturl" data-url="aHR0cHM6Ly9wbGF0Zm9ybS5jbGF1ZGUuY29tL2RvY3MvZW4vYnVpbGQtd2l0aC1jbGF1ZGUvc3RydWN0dXJlZC1vdXRwdXRz" title="https://platform.claude.com/docs/en/build-with-claude/structured-outputs">Structured outputs - Claude API Docs<i class="fa fa-external-link"></i></span></li><li><span class="exturl" data-url="aHR0cHM6Ly9wbGF0Zm9ybS5jbGF1ZGUuY29tL2RvY3MvZW4vYnVpbGQtd2l0aC1jbGF1ZGUvdG9vbC11c2U=" title="https://platform.claude.com/docs/en/build-with-claude/tool-use">Tool use with Claude - Claude API Docs<i class="fa fa-external-link"></i></span></li></ul>]]>
    </content>
    <id>https://heidiliu2020.github.io/claude-structured-outputs/</id>
    <link href="https://heidiliu2020.github.io/claude-structured-outputs/"/>
    <published>2026-04-16T09:14:00.000Z</published>
    <summary>
      <![CDATA[<h2 id="Structured-outputs-結構化輸出"><a href="#Structured-outputs-結構化輸出" class="headerlink" title="Structured outputs 結構化輸出"></a>Structured outputs 結構化輸出</h2><p><span class="exturl" data-url="aHR0cHM6Ly9wbGF0Zm9ybS5jbGF1ZGUuY29tL2RvY3MvZW4vYnVpbGQtd2l0aC1jbGF1ZGUvc3RydWN0dXJlZC1vdXRwdXRz" title="https://platform.claude.com/docs/en/build-with-claude/structured-outputs">Structured outputs（結構化輸出）<i class="fa fa-external-link"></i></span>是 Claude API 提供的功能，用來讓模型輸出符合指定的 JSON schema。透過 <strong>Constrained decoding（約束解碼）</strong>，在「生成過程中」限制模型只能產生合法的 JSON 結構，而不是在輸出後再做檢查。</p>]]>
    </summary>
    <title>【學習筆記】Claude API：Structured Outputs 結構化輸出</title>
    <updated>2026-04-23T06:14:18.206Z</updated>
  </entry>
  <entry>
    <author>
      <name>Heidi Liu</name>
    </author>
    <category term="技術學習" scheme="https://heidiliu2020.github.io/categories/%E6%8A%80%E8%A1%93%E5%AD%B8%E7%BF%92/"/>
    <category term="AI" scheme="https://heidiliu2020.github.io/categories/%E6%8A%80%E8%A1%93%E5%AD%B8%E7%BF%92/AI/"/>
    <category term="AI" scheme="https://heidiliu2020.github.io/tags/AI/"/>
    <category term="Claude" scheme="https://heidiliu2020.github.io/tags/Claude/"/>
    <category term="Agent SDK" scheme="https://heidiliu2020.github.io/tags/Agent-SDK/"/>
    <category term="Human-in-the-loop" scheme="https://heidiliu2020.github.io/tags/Human-in-the-loop/"/>
    <content>
      <![CDATA[<p><img src="https://hackmd.io/_uploads/HJ6TA0x2bg.png" alt="claude"></p><p>最近在研究 AI Agent 技術，調查 Claude Agent SDK 是否能支援 <strong>Human-in-the-loop（人機協作）</strong> 以及 <strong>不同工作階段的 Agent workflow</strong>。</p><p>這系列筆記整理了調查過程中的重點，主要參考 <span class="exturl" data-url="aHR0cHM6Ly9kb2NzLmFudGhyb3BpYy5jb20vZW4vZG9jcy9hZ2VudHMtYW5kLXRvb2xzL2NsYXVkZS1hZ2VudC1zZGs=" title="https://docs.anthropic.com/en/docs/agents-and-tools/claude-agent-sdk">Anthropic 官方文件<i class="fa fa-external-link"></i></span> 和實際的 Demo 實作經驗，方便日後查詢。本篇會先介紹 Claude Agent SDK 的基本概念，以及 Human-in-the-loop 在 Agent 系統中的運作方式。</p><span id="more"></span><hr><h2 id="Claude-Agent-SDK-是什麼？"><a href="#Claude-Agent-SDK-是什麼？" class="headerlink" title="Claude Agent SDK 是什麼？"></a>Claude Agent SDK 是什麼？</h2><p><strong><span class="exturl" data-url="aHR0cHM6Ly9wbGF0Zm9ybS5jbGF1ZGUuY29tL2RvY3MvZW4vYWdlbnQtc2RrL292ZXJ2aWV3" title="https://platform.claude.com/docs/en/agent-sdk/overview">Claude Agent SDK<i class="fa fa-external-link"></i></span></strong> （前身為 Claude Code SDK）是 <span class="exturl" data-url="aHR0cHM6Ly93d3cuYW50aHJvcGljLmNvbS8=" title="https://www.anthropic.com/">Anthropic<i class="fa fa-external-link"></i></span> 官方提供的 SDK，讓開發者在自己的應用程式中嵌入 Claude 的 Agent 能力。</p><p>簡單來說，就是把 Claude Code 的能力包成一個函式庫，讓使用者可以在 Web App、API Server 等場景中使用。</p><table><thead><tr><th>項目</th><th>內容</th></tr></thead><tbody><tr><td>套件名稱</td><td><code>@anthropic-ai/claude-agent-sdk</code>（TypeScript）/ <code>claude-agent-sdk</code>（Python）</td></tr><tr><td>安裝指令</td><td><code>npm install @anthropic-ai/claude-agent-sdk</code></td></tr><tr><td>核心 API</td><td><code>query()</code> — 開發者大多只需使用 <code>query()</code></td></tr><tr><td>前提條件</td><td>需要 <code>ANTHROPIC_API_KEY</code></td></tr></tbody></table><h3 id="SDK-vs-Claude-Code-CLI"><a href="#SDK-vs-Claude-Code-CLI" class="headerlink" title="SDK vs. Claude Code CLI"></a>SDK vs. Claude Code CLI</h3><p>這兩者的關係可以這樣理解：</p><ul><li>CLI 是給「人」用的互動工具</li><li>SDK 是給「程式」用的函式庫</li></ul><table><thead><tr><th>使用場景</th><th>最佳選擇</th></tr></thead><tbody><tr><td>互動式開發（終端機 / IDE）</td><td>CLI</td></tr><tr><td>CI/CD 管線</td><td>SDK</td></tr><tr><td>自訂應用程式（Web / API）</td><td><strong>SDK</strong></td></tr><tr><td>生產環境自動化</td><td>SDK</td></tr></tbody></table><p>如果想在自己的 Web App 或 API 中整合 Agent 功能，SDK 就是你需要的東西。</p><h3 id="Agent-的基本組成"><a href="#Agent-的基本組成" class="headerlink" title="Agent 的基本組成"></a>Agent 的基本組成</h3><p>在 Claude Agent SDK 中，一個 Agent 通常由三個部分組成：</p><table><thead><tr><th>元件</th><th>說明</th></tr></thead><tbody><tr><td>LLM</td><td>Claude 模型負責推理</td></tr><tr><td>Tools</td><td>Agent 可以呼叫的外部能力</td></tr><tr><td>Control Layer</td><td>開發者控制 Agent 行為（例如 HITL）</td></tr></tbody></table><p>SDK 的 <code>query()</code> 函式負責協調這三個部分。</p><h2 id="AI-Agent-的兩個控制層"><a href="#AI-Agent-的兩個控制層" class="headerlink" title="AI Agent 的兩個控制層"></a>AI Agent 的兩個控制層</h2><p>在實際設計 AI Agent 系統時，通常需要考慮兩個核心問題：</p><ol><li><strong>Agent 能做什麼？（能力控制）</strong></li><li><strong>Agent 什麼時候需要人類介入？（決策控制）</strong></li></ol><p>在 Claude Agent SDK 中，這兩個層面分別由不同機制負責：</p><table><thead><tr><th>控制層</th><th>對應機制</th></tr></thead><tbody><tr><td>Capability Control</td><td>systemPrompt + tools</td></tr><tr><td>Decision Control</td><td>Human-in-the-loop (<code>canUseTool</code>)</td></tr></tbody></table><p>本篇會先介紹第二個部分：Human-in-the-loop 的運作方式。</p><h2 id="Human-in-the-loop-人機協作"><a href="#Human-in-the-loop-人機協作" class="headerlink" title="Human-in-the-loop 人機協作"></a>Human-in-the-loop 人機協作</h2><h3 id="什麼是-HITL？"><a href="#什麼是-HITL？" class="headerlink" title="什麼是 HITL？"></a>什麼是 HITL？</h3><p>Human-in-the-loop（HITL）是指 Agent 在執行過程中，<strong>暫停動作、請求人類確認後再繼續</strong>的機制。</p><p>為什麼需要這個機制呢？因為 Agent 可能會需要執行一些高風險操作（例如刪除檔案、執行系統指令），這時讓人類在關鍵節點上進行審核，可以避免不當操作。</p><h3 id="核心機制：canUseTool-callback"><a href="#核心機制：canUseTool-callback" class="headerlink" title="核心機制：canUseTool callback"></a>核心機制：<code>canUseTool</code> callback</h3><p>SDK 提供 <strong>一個統一的 callback</strong> 來處理所有的人機互動，流程大致如下：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">User Prompt</span><br><span class="line">   ↓</span><br><span class="line">Claude Agent 推理</span><br><span class="line">   ↓</span><br><span class="line">Agent 想使用 Tool（例如 write_file(&quot;config.json&quot;)）</span><br><span class="line">   ↓</span><br><span class="line">SDK 呼叫 canUseTool(&quot;Write&quot;, &#123;...&#125;)</span><br><span class="line">   ↓</span><br><span class="line">應用程式決定</span><br><span class="line">  ✓ Allow  → 繼續執行</span><br><span class="line">  ✗ Deny   → 停止這個動作</span><br><span class="line">  ✎ Modify input → 改參數後執行</span><br><span class="line">   ↓</span><br><span class="line">Tool 根據結果執行</span><br><span class="line">   ↓</span><br><span class="line">Agent 繼續推理</span><br></pre></td></tr></table></figure><p>重點在於：<strong>所有的人機互動都通過這一個 callback</strong>，包含工具審核和 Agent 提問，不需要分別處理。</p><h3 id="實作程式碼"><a href="#實作程式碼" class="headerlink" title="實作程式碼"></a>實作程式碼</h3><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> result = <span class="title function_">query</span>(&#123;</span><br><span class="line">  <span class="attr">prompt</span>: <span class="string">&quot;幫我重構這個專案&quot;</span>,</span><br><span class="line">  <span class="attr">options</span>: &#123;</span><br><span class="line">    <span class="attr">canUseTool</span>: <span class="title function_">async</span> (toolName, input, &#123; signal, decisionReason &#125;) =&gt; &#123;</span><br><span class="line">      <span class="comment">// 允許執行</span></span><br><span class="line">      <span class="keyword">return</span> &#123; <span class="attr">behavior</span>: <span class="string">&quot;allow&quot;</span>, <span class="attr">updatedInput</span>: input &#125;;</span><br><span class="line"></span><br><span class="line">      <span class="comment">// 拒絕執行</span></span><br><span class="line">      <span class="keyword">return</span> &#123; <span class="attr">behavior</span>: <span class="string">&quot;deny&quot;</span>, <span class="attr">message</span>: <span class="string">&quot;使用者拒絕了這個操作&quot;</span> &#125;;</span><br><span class="line"></span><br><span class="line">      <span class="comment">// 修改參數後允許</span></span><br><span class="line">      <span class="keyword">return</span> &#123; <span class="attr">behavior</span>: <span class="string">&quot;allow&quot;</span>, <span class="attr">updatedInput</span>: &#123; ...input, <span class="attr">path</span>: <span class="string">&quot;/safe/path&quot;</span> &#125; &#125;;</span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><h3 id="兩種觸發情境"><a href="#兩種觸發情境" class="headerlink" title="兩種觸發情境"></a>兩種觸發情境</h3><h4 id="情境-A：工具審核（Tool-Approval）"><a href="#情境-A：工具審核（Tool-Approval）" class="headerlink" title="情境 A：工具審核（Tool Approval）"></a>情境 A：工具審核（Tool Approval）</h4><p>Agent 要使用工具時（如寫入檔案），SDK 自動呼叫 <code>canUseTool</code> 讓人類審核：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">Agent 想執行 write_file(&quot;utils.go&quot;)</span><br><span class="line">    ↓</span><br><span class="line">SDK 呼叫 canUseTool(&quot;Write&quot;, &#123; file_path: &quot;utils.go&quot;, ... &#125;)</span><br><span class="line">    ↓</span><br><span class="line">應用程式顯示審核 UI → 使用者選擇 Approve / Deny</span><br><span class="line">    ↓</span><br><span class="line">Agent 根據結果繼續或停止</span><br></pre></td></tr></table></figure><h4 id="情境-B：AskUserQuestion（Agent-主動提問）"><a href="#情境-B：AskUserQuestion（Agent-主動提問）" class="headerlink" title="情境 B：AskUserQuestion（Agent 主動提問）"></a>情境 B：AskUserQuestion（Agent 主動提問）</h4><p>除了工具審核之外，Agent 有時也需要向使用者詢問資訊。</p><p>當 Agent 判斷資訊不足時，會呼叫 <code>AskUserQuestion</code> 工具向使用者提問。<br>這個操作 **同樣會觸發 <code>canUseTool</code>**，但角色不同，此時應用程式需要將問題顯示給使用者，收集答案後再回傳給 Agent。</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">canUseTool</span>: <span class="title function_">async</span> (toolName, input) =&gt; &#123;</span><br><span class="line">  <span class="keyword">if</span> (toolName === <span class="string">&quot;AskUserQuestion&quot;</span>) &#123;</span><br><span class="line">    <span class="comment">// Agent 在提問 → 顯示問題、收集答案</span></span><br><span class="line">    <span class="keyword">const</span> answers = <span class="keyword">await</span> <span class="title function_">showQuestionUI</span>(input.<span class="property">questions</span>);</span><br><span class="line">    <span class="keyword">return</span> &#123;</span><br><span class="line">      <span class="attr">behavior</span>: <span class="string">&quot;allow&quot;</span>,</span><br><span class="line">      <span class="attr">updatedInput</span>: &#123; ...input, answers &#125;</span><br><span class="line">    &#125;;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 其他工具 → 審核是否允許</span></span><br><span class="line">  <span class="keyword">const</span> approved = <span class="keyword">await</span> <span class="title function_">showApprovalUI</span>(toolName, input);</span><br><span class="line">  <span class="keyword">if</span> (approved) &#123;</span><br><span class="line">    <span class="keyword">return</span> &#123; <span class="attr">behavior</span>: <span class="string">&quot;allow&quot;</span>, <span class="attr">updatedInput</span>: input &#125;;</span><br><span class="line">  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> &#123; <span class="attr">behavior</span>: <span class="string">&quot;deny&quot;</span>, <span class="attr">message</span>: <span class="string">&quot;使用者拒絕&quot;</span> &#125;;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>用一個具體的例子來說明 <code>AskUserQuestion</code> 的流程：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">Agent 不確定要用哪個測試框架</span><br><span class="line">    ↓</span><br><span class="line">Agent 呼叫 AskUserQuestion(&#123;</span><br><span class="line">  questions: [&#123;</span><br><span class="line">    question: &quot;你想用哪個測試框架？&quot;,</span><br><span class="line">    options: [</span><br><span class="line">      &#123; label: &quot;Jest&quot;,   description: &quot;最常用的 JS 測試框架&quot; &#125;,</span><br><span class="line">      &#123; label: &quot;Vitest&quot;, description: &quot;較新，與 Vite 整合好&quot; &#125;</span><br><span class="line">    ]</span><br><span class="line">  &#125;]</span><br><span class="line">&#125;)</span><br><span class="line">    ↓</span><br><span class="line">SDK 呼叫 canUseTool(&quot;AskUserQuestion&quot;, &#123; questions: [...] &#125;)</span><br><span class="line">    ↓</span><br><span class="line">應用程式顯示問題 UI，使用者選了 &quot;Vitest&quot;</span><br><span class="line">    ↓</span><br><span class="line">回傳 &#123; behavior: &quot;allow&quot;, updatedInput: &#123; ...input, answers: &#123; &quot;你想用哪個測試框架？&quot;: &quot;Vitest&quot; &#125; &#125; &#125;</span><br><span class="line">    ↓</span><br><span class="line">Agent 收到答案，用 Vitest 繼續開發</span><br></pre></td></tr></table></figure><h3 id="內建權限策略：permissionMode"><a href="#內建權限策略：permissionMode" class="headerlink" title="內建權限策略：permissionMode"></a>內建權限策略：<code>permissionMode</code></h3><p><code>permissionMode</code> 是 SDK 內建的預設策略，而 <code>canUseTool</code> 則提供更細緻的控制。這種方式適合快速原型或 CLI 工具，如果需要自訂 UI 或更細緻的控制，通常會搭配 <code>canUseTool</code> 使用。</p><p>如果兩者同時存在，SDK 會先套用 <code>permissionMode</code>，再透過 <code>canUseTool</code> 讓應用程式做最終決策。</p><table><thead><tr><th>permissionMode</th><th>行為</th></tr></thead><tbody><tr><td><code>&quot;default&quot;</code></td><td>標準模式，需要審核</td></tr><tr><td><code>&quot;plan&quot;</code></td><td>規劃模式，唯讀工具自動通過</td></tr><tr><td><code>&quot;acceptEdits&quot;</code></td><td>自動接受檔案編輯</td></tr><tr><td><code>&quot;bypassPermissions&quot;</code></td><td>全部放行（謹慎使用）</td></tr></tbody></table><h2 id="Claude-Agent-SDK-在-Web-App-中的基本架構"><a href="#Claude-Agent-SDK-在-Web-App-中的基本架構" class="headerlink" title="Claude Agent SDK 在 Web App 中的基本架構"></a>Claude Agent SDK 在 Web App 中的基本架構</h2><h3 id="系統架構"><a href="#系統架構" class="headerlink" title="系統架構"></a>系統架構</h3><p>在實際的 Web 應用中，Claude Agent SDK 通常會部署在後端 API 層，由前端透過 HTTP 請求與 Agent 互動。</p><p>以下是一個典型的 Next.js 架構：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">┌────────────────────────────────────────────┐</span><br><span class="line">│  Next.js Web App                           │</span><br><span class="line">│                                            │</span><br><span class="line">│  ┌──────────┐    POST     ┌─────────────┐  │</span><br><span class="line">│  │ React UI │  ────────→  │ API Route   │  │</span><br><span class="line">│  │ (Chat)   │  ←──SSE───  │ /api/agent  │  │</span><br><span class="line">│  └──────────┘             └──────┬──────┘  │</span><br><span class="line">│                                  │         │</span><br><span class="line">└──────────────────────────────────│─────────┘</span><br><span class="line">                                   │ query()</span><br><span class="line">                            ┌──────▼──────┐</span><br><span class="line">                            │ Claude      │</span><br><span class="line">                            │ Agent SDK   │</span><br><span class="line">                            └──────┬──────┘</span><br><span class="line">                                   │</span><br><span class="line">                            ┌──────▼──────┐</span><br><span class="line">                            │ Claude API  │</span><br><span class="line">                            │ (Anthropic) │</span><br><span class="line">                            └─────────────┘</span><br></pre></td></tr></table></figure><p>前端負責顯示聊天 UI 與使用者互動，後端 API Route 則負責呼叫 Claude Agent SDK，並將串流結果回傳給前端。當 Agent 需要人類審核或提問時，則透過 <code>canUseTool</code> callback 觸發應用程式的 UI。</p><h3 id="串流回覆與互動流程"><a href="#串流回覆與互動流程" class="headerlink" title="串流回覆與互動流程"></a>串流回覆與互動流程</h3><p>實際實作時，Agent 的回覆通常會透過 <strong>串流（streaming）</strong> 的方式傳回前端，例如使用 SSE（Server-Sent Events）或 WebSocket。</p><p>當 <code>canUseTool</code> 觸發需要使用者回覆的情境（例如 <code>AskUserQuestion</code>）時，系統會暫停 Agent 的執行並等待使用者輸入，再繼續推理。</p><p>由於 SSE 是單向通信，許多 Web 應用會採用 <strong>SSE + REST</strong> 的雙通道架構：</p><ul><li>SSE：Server → Client 傳送 Agent 回覆</li><li>REST POST：Client → Server 回傳使用者回答</li></ul><p>這種串流與互動的通信設計，涉及較多 Web 架構與 Agent session 管理，本文先聚焦於 Agent SDK 與 Human-in-the-loop 的基本概念，相關實作細節將在後續文章中介紹。</p><hr><h2 id="總結"><a href="#總結" class="headerlink" title="總結"></a>總結</h2><p>Claude Agent SDK 提供了一個簡單但強大的模型來構建 AI Agent：</p><ul><li><code>query()</code> 負責執行 Agent 推理流程</li><li>Tools 讓 Agent 可以與外部系統互動</li><li><code>canUseTool</code> 提供 Human-in-the-loop 機制，讓人類在關鍵操作前介入</li></ul><p>透過這些機制，開發者可以在 <strong>自動化與人工監督之間取得平衡</strong>，建立更安全可控的 AI Agent 系統。</p><p>不過在實際的 Web 應用中，為了讓 AI Agent 在自動化與可控性之間取得更好的平衡，還需要進一步設計：</p><ul><li>Agent 回覆的 <strong>串流傳輸（SSE / WebSocket）</strong></li><li><code>AskUserQuestion</code> 等互動流程的 <strong>session 管理</strong></li><li>不同任務階段的 <strong>Agent workflow 設計</strong></li></ul><p>後續文章將會進一步介紹：</p><ul><li><strong>Agent 串流與互動設計</strong>（SSE / WebSocket / REST 架構）</li><li><strong>Agent Workflow 設計</strong>（Plan / Execute / Review 模式切換）</li></ul><h2 id="參考資料"><a href="#參考資料" class="headerlink" title="參考資料"></a>參考資料</h2><ul><li><span class="exturl" data-url="aHR0cHM6Ly9kb2NzLmFudGhyb3BpYy5jb20vZW4vZG9jcy9hZ2VudHMtYW5kLXRvb2xzL2NsYXVkZS1hZ2VudC1zZGs=" title="https://docs.anthropic.com/en/docs/agents-and-tools/claude-agent-sdk">Claude Agent SDK 官方文件<i class="fa fa-external-link"></i></span></li><li><span class="exturl" data-url="aHR0cHM6Ly9kb2NzLmFudGhyb3BpYy5jb20vZW4vYXBp" title="https://docs.anthropic.com/en/api">Anthropic API Reference<i class="fa fa-external-link"></i></span></li></ul>]]>
    </content>
    <id>https://heidiliu2020.github.io/claude-agent-sdk/</id>
    <link href="https://heidiliu2020.github.io/claude-agent-sdk/"/>
    <published>2026-04-02T09:27:00.000Z</published>
    <summary>
      <![CDATA[<p><img src="https://hackmd.io/_uploads/HJ6TA0x2bg.png" alt="claude"></p>
<p>最近在研究 AI Agent 技術，調查 Claude Agent SDK 是否能支援 <strong>Human-in-the-loop（人機協作）</strong> 以及 <strong>不同工作階段的 Agent workflow</strong>。</p>
<p>這系列筆記整理了調查過程中的重點，主要參考 <span class="exturl" data-url="aHR0cHM6Ly9kb2NzLmFudGhyb3BpYy5jb20vZW4vZG9jcy9hZ2VudHMtYW5kLXRvb2xzL2NsYXVkZS1hZ2VudC1zZGs=" title="https://docs.anthropic.com/en/docs/agents-and-tools/claude-agent-sdk">Anthropic 官方文件<i class="fa fa-external-link"></i></span> 和實際的 Demo 實作經驗，方便日後查詢。本篇會先介紹 Claude Agent SDK 的基本概念，以及 Human-in-the-loop 在 Agent 系統中的運作方式。</p>]]>
    </summary>
    <title>【學習筆記】Claude Agent SDK：概觀與 Human-in-the-loop 機制</title>
    <updated>2026-04-23T06:14:18.205Z</updated>
  </entry>
  <entry>
    <author>
      <name>Heidi Liu</name>
    </author>
    <category term="技術學習" scheme="https://heidiliu2020.github.io/categories/%E6%8A%80%E8%A1%93%E5%AD%B8%E7%BF%92/"/>
    <category term="Back-End" scheme="https://heidiliu2020.github.io/categories/%E6%8A%80%E8%A1%93%E5%AD%B8%E7%BF%92/Back-End/"/>
    <category term="golang" scheme="https://heidiliu2020.github.io/categories/%E6%8A%80%E8%A1%93%E5%AD%B8%E7%BF%92/Back-End/golang/"/>
    <category term="Back-End" scheme="https://heidiliu2020.github.io/tags/Back-End/"/>
    <category term="golang" scheme="https://heidiliu2020.github.io/tags/golang/"/>
    <content>
      <![CDATA[<p>公司目前在架構規劃中，App API 層傾向使用 Go 語言，相較於 <span class="exturl" data-url="aHR0cHM6Ly9ydXN0LWxhbmcudHcvYm9vay10dy8=" title="https://rust-lang.tw/book-tw/">Rust<i class="fa fa-external-link"></i></span>學習曲線較高，Go 在高並發處理和資料串流中繼上同樣有優勢，語法也比較直覺，如果本來就有 TypeScript 背景，上手門檻相對較低。</p><p>這系列筆記預計以 <span class="exturl" data-url="aHR0cHM6Ly93aWxsaC5naXRib29rLmlvL2J1aWxkLXdlYi1hcHBsaWNhdGlvbi13aXRoLWdvbGFuZy16aHR3" title="https://willh.gitbook.io/build-web-application-with-golang-zhtw">《Golang 打造 Web 應用程式》<i class="fa fa-external-link"></i></span> 搭配 <span class="exturl" data-url="aHR0cHM6Ly93d3cueW91dHViZS5jb20vd2F0Y2g/dj04dWlaQzBsNEFqdw==" title="https://www.youtube.com/watch?v=8uiZC0l4Ajw">《GOを早く学ぶ：完全チュートリアル》<i class="fa fa-external-link"></i></span> 入門影片，一邊實作一邊整理重點，當作日後查詢用的學習筆記。</p><span id="more"></span><hr><h2 id="Why-Go？"><a href="#Why-Go？" class="headerlink" title="Why Go？"></a>Why Go？</h2><p><span class="exturl" data-url="aHR0cHM6Ly9nby5kZXYv" title="https://go.dev/">Golang（Go Language）<i class="fa fa-external-link"></i></span>是 Google 在 2009 年推出的<span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL2dvbGFuZy9nbw==" title="https://github.com/golang/go">開源程式語言<i class="fa fa-external-link"></i></span>，設計目標很單純：<strong>語法簡潔、高效能、高並發、好維護</strong>。目前廣泛應用在後端服務、雲端平台、DevOps 工具（如 Docker、Kubernetes）等領域。</p><p>從語言特性來看，Go 具有：</p><ul><li><strong>Statically Typed / Strongly Typed</strong>：靜態、強型別語言，編譯期就會進行型別檢查</li><li><strong>Compiled &amp; Fast Compile Time</strong>：編譯型語言，編譯速度快，可以直接產生可執行的二進位檔，部署流程單純</li><li><strong>Built-in Concurrency</strong>：內建並發模型，透過 Goroutines（Go 例程）和 Channels，容易寫出高並發的網路服務</li><li><strong>Simplicity</strong>：語言設計簡潔，整個語言只有 25 個關鍵字，搭配官方工具鏈（<code>go build</code>、<code>go test</code>、<code>gofmt</code> 等）就能完成大部分開發流程</li><li><strong>Garbage Collection</strong>：內建垃圾回收機制（GC），不必手動管理記憶體，在效能與開發效率之間取得良好平衡</li></ul><h2 id="Install-Go"><a href="#Install-Go" class="headerlink" title="Install Go"></a>Install Go</h2><p><span class="exturl" data-url="aHR0cHM6Ly9nby5kZXYvZG9jL2luc3RhbGw=" title="https://go.dev/doc/install">Go 官網<i class="fa fa-external-link"></i></span>提供標準安裝套件，支援 Linux、 Mac、Windows 系統，。下載對應平台的安裝檔並安裝完成後，可以在終端機（Terminal）輸入指令確認是否安裝成功：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ go version</span><br><span class="line"></span><br><span class="line">// 看到 go version go1.xx.x darwin/arm64 代表安裝完成</span><br></pre></td></tr></table></figure><h2 id="建立第一個程式：Hello-Go"><a href="#建立第一個程式：Hello-Go" class="headerlink" title="建立第一個程式：Hello, Go"></a>建立第一個程式：Hello, Go</h2><p>環境建置完成後，先撰寫一個最簡單的「Hello World」程式，確認整體開發流程沒有問題。</p><ol><li>建立專案資料夾</li></ol><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ mkdir hello-go</span><br><span class="line">$ cd hello-go</span><br></pre></td></tr></table></figure><ol start="2"><li>在資料夾內建立 <code>main.go</code> 檔案，內容如下：</li></ol><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">&quot;fmt&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">    fmt.Println(<span class="string">&quot;Hello, Go!&quot;</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol start="3"><li>直接執行程式（不用先編譯成檔案）：</li></ol><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ go run main.go</span><br><span class="line"></span><br><span class="line">// Hello, Go!</span><br></pre></td></tr></table></figure><p>如果能順利印出 <code>Hello, Go!</code> 即可確認：</p><ul><li>Go 安裝正確</li><li>環境變數與路徑設定無誤</li><li>編譯與執行流程均可正常運作</li></ul><h2 id="Go-Modules-與-GOPATH"><a href="#Go-Modules-與-GOPATH" class="headerlink" title="Go Modules 與 GOPATH"></a>Go Modules 與 GOPATH</h2><p>在較新的 Go 版本（1.13 之後），預設採用 Go Modules 模式進行相依套件管理。實務上代表：</p><p>專案不再強制需要放在 GOPATH 底下。<br>每個專案資料夾會有一個 go.mod 檔案，用來描述模組名稱與相依套件。<br>在專案資料夾中執行以下指令，即可初始化一個 Module：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ go mod init example.com/hello</span><br></pre></td></tr></table></figure><p>此指令會建立 go.mod 檔案，之後在該資料夾內執行 go run、go build 等指令時，Go 會依照 Module 模式來管理相依套件。</p><p>GOPATH 則是較早期的工作目錄概念，目前主要用途包括：</p><ul><li>作為共用工具與套件快取的儲存位置</li><li>偶爾在錯誤訊息或文件中仍會看到相關說明</li></ul><p>若想查看目前環境的 GOPATH 設定，可以使用：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ go env GOPATH</span><br></pre></td></tr></table></figure><p>對入門階段而言，了解「一般專案直接使用 Module 模式即可，不必刻意依附在 GOPATH 之下」大致就足夠，細節可以在實際開發過程中再逐步補充。</p><h2 id="常用-Go-指令"><a href="#常用-Go-指令" class="headerlink" title="常用 Go 指令"></a>常用 Go 指令</h2><p>開始撰寫程式後，會經常使用到以下幾個 Go 指令：</p><ul><li><code>go run main.go</code>：直接編譯並執行指定的 <code>.go</code> 檔，適合開發中快速測試</li><li><code>go build</code>：在目前資料夾下編譯專案，產生可執行檔（但不會自動執行）</li></ul><p>例如：</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ <span class="keyword">go</span> build</span><br><span class="line">$ ./hello-<span class="keyword">go</span></span><br></pre></td></tr></table></figure><ul><li><code>go test</code>：執行以 <code>_test.go</code> 結尾的測試檔，是撰寫單元測試與自動化測試時的重要工具</li><li><code>go fmt</code> / <code>gofmt</code>：依照官方格式自動排版程式碼。多數編輯器可以設定為在檔案儲存時自動執行，因此日常開發很少需要手動下指令</li><li><code>go mod tidy</code>：清理與整理 <code>go.mod</code> / <code>go.sum</code>，清理與整理 go.mod、go.sum，移除未使用的套件並補上缺少的相依，讓相依關係保持一致</li></ul><h2 id="開發工具：編輯器與外掛"><a href="#開發工具：編輯器與外掛" class="headerlink" title="開發工具：編輯器與外掛"></a>開發工具：編輯器與外掛</h2><p>在開發體驗上，建議的工具組合如下：</p><ul><li>VS Code + Go 擴充套件<ul><li>提供程式碼自動補完、定義跳轉、型別資訊顯示等功能</li><li>可設定為在檔案儲存時自動執行 gofmt / goimports，統一程式碼風格與 import 排序</li><li>內建除錯（Debug）整合，搭配 dlv（Delve）即可進行斷點除錯</li></ul></li><li>Go 官方工具鏈（隨 Go 一併安裝）<ul><li>gofmt：程式碼格式化工具</li><li>goimports：在格式化同時自動新增或移除 import。多半由編輯器在背景自動呼叫</li><li>go tool 系列：涵蓋 Profiler、Benchmark 等進階功能，有需要時可以再進一步深入。</li></ul></li></ul><p>若已經習慣使用 JetBrains 系列產品，也可以考慮專門的 Go IDE（例如 GoLand）；不過在入門階段，使用 VS Code 搭配官方擴充套件，通常已足以應付多數開發情境。</p><p>本篇先將「環境建置」與「基本開發工具」整理過一遍，確認本機環境可以順利撰寫並執行簡單的 Go 程式。下一步，便可以開始嘗試實作實際的 Web 應用程式，例如：</p><ul><li>使用標準庫 <code>net/http</code> 實作最基礎的 HTTP Server</li><li>建立一個簡單的 RESTful API</li><li>逐步加入 Router、Middleware、設定管理（Config）等結構</li></ul><h2 id="參考資料"><a href="#參考資料" class="headerlink" title="參考資料"></a>參考資料</h2><ul><li><span class="exturl" data-url="aHR0cHM6Ly93aWxsaC5naXRib29rLmlvL2J1aWxkLXdlYi1hcHBsaWNhdGlvbi13aXRoLWdvbGFuZy16aHR3" title="https://willh.gitbook.io/build-web-application-with-golang-zhtw">Golang 打造 Web 應用程式<i class="fa fa-external-link"></i></span> </li><li><span class="exturl" data-url="aHR0cHM6Ly93d3cueW91dHViZS5jb20vd2F0Y2g/dj04dWlaQzBsNEFqdw==" title="https://www.youtube.com/watch?v=8uiZC0l4Ajw">GOを早く学ぶ：完全チュートリアル<i class="fa fa-external-link"></i></span> </li></ul>]]>
    </content>
    <id>https://heidiliu2020.github.io/golang/</id>
    <link href="https://heidiliu2020.github.io/golang/"/>
    <published>2026-04-01T07:27:00.000Z</published>
    <summary>
      <![CDATA[<p>公司目前在架構規劃中，App API 層傾向使用 Go 語言，相較於 <span class="exturl" data-url="aHR0cHM6Ly9ydXN0LWxhbmcudHcvYm9vay10dy8=" title="https://rust-lang.tw/book-tw/">Rust<i class="fa fa-external-link"></i></span>學習曲線較高，Go 在高並發處理和資料串流中繼上同樣有優勢，語法也比較直覺，如果本來就有 TypeScript 背景，上手門檻相對較低。</p>
<p>這系列筆記預計以 <span class="exturl" data-url="aHR0cHM6Ly93aWxsaC5naXRib29rLmlvL2J1aWxkLXdlYi1hcHBsaWNhdGlvbi13aXRoLWdvbGFuZy16aHR3" title="https://willh.gitbook.io/build-web-application-with-golang-zhtw">《Golang 打造 Web 應用程式》<i class="fa fa-external-link"></i></span> 搭配 <span class="exturl" data-url="aHR0cHM6Ly93d3cueW91dHViZS5jb20vd2F0Y2g/dj04dWlaQzBsNEFqdw==" title="https://www.youtube.com/watch?v=8uiZC0l4Ajw">《GOを早く学ぶ：完全チュートリアル》<i class="fa fa-external-link"></i></span> 入門影片，一邊實作一邊整理重點，當作日後查詢用的學習筆記。</p>]]>
    </summary>
    <title>【學習筆記】Golang 入門：環境建置與開發工具</title>
    <updated>2026-04-23T06:14:18.215Z</updated>
  </entry>
  <entry>
    <author>
      <name>Heidi Liu</name>
    </author>
    <category term="技術學習" scheme="https://heidiliu2020.github.io/categories/%E6%8A%80%E8%A1%93%E5%AD%B8%E7%BF%92/"/>
    <category term="Front-End" scheme="https://heidiliu2020.github.io/categories/%E6%8A%80%E8%A1%93%E5%AD%B8%E7%BF%92/Front-End/"/>
    <category term="JavaScript" scheme="https://heidiliu2020.github.io/categories/%E6%8A%80%E8%A1%93%E5%AD%B8%E7%BF%92/Front-End/JavaScript/"/>
    <category term="TypeScript" scheme="https://heidiliu2020.github.io/tags/TypeScript/"/>
    <category term="type" scheme="https://heidiliu2020.github.io/tags/type/"/>
    <category term="interface" scheme="https://heidiliu2020.github.io/tags/interface/"/>
    <content>
      <![CDATA[<p>在 TypeScript 中，<code>type</code>（型別別名）與 <code>interface</code>（介面宣告）都可以用來描述資料的型別結構。</p><p>在定義 Object Types（物件型別）時，兩者寫法看起來幾乎相同，許多情況下還可以互換使用，但它們在設計目的與使用範圍上仍存在一點差異。</p><p>本文的結論其實很簡單：</p><blockquote><p><strong>只在 <code>type</code> 做不到的時候，才使用 <code>interface</code>。</strong></p></blockquote><p>這篇文章將從以下幾個角度說明為什麼：</p><ul><li><code>type</code> 與 <code>interface</code> 的核心差異</li><li><code>interface</code> 不可取代的場景</li><li>實務上的使用判斷流程</li></ul><span id="more"></span><hr><h2 id="type-與-interface"><a href="#type-與-interface" class="headerlink" title="type 與 interface"></a><code>type</code> 與 <code>interface</code></h2><p>在進入正題前，試著判斷以下三題「是否能編譯」，感受 <code>type</code> 和 <code>interface</code> 的差異：</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Q1:</span></span><br><span class="line"><span class="keyword">type</span> A = &#123; <span class="attr">name</span>: <span class="built_in">string</span> &#125;</span><br><span class="line"><span class="keyword">type</span> A = &#123; <span class="attr">age</span>: <span class="built_in">number</span> &#125;            <span class="comment">// X:　Duplicate identifier &#x27;A&#x27;. (同名報錯)</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// Q2:</span></span><br><span class="line"><span class="keyword">interface</span> B &#123; <span class="attr">name</span>: <span class="built_in">string</span> &#125;</span><br><span class="line"><span class="keyword">interface</span> B &#123; <span class="attr">age</span>: <span class="built_in">number</span> &#125;         <span class="comment">// O: Declaration Merging (自動合併)</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// Q3:</span></span><br><span class="line"><span class="keyword">type</span> C = <span class="built_in">string</span> | <span class="built_in">number</span>            <span class="comment">// O: 定義為 Union 聯合型別</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">interface</span> D = <span class="built_in">string</span> | <span class="built_in">number</span>       // X: <span class="keyword">interface</span> 只能是物件格式</span><br></pre></td></tr></table></figure><p>乍看之下，兩者都是在定義型別，為什麼會出現不同的行為？</p><p>以下參考官方原文：</p><blockquote><p>“A <strong>type alias</strong> is exactly that — <strong>a name for any type</strong>.”</p></blockquote><ul><li><code>type</code> 是 **<span class="exturl" data-url="aHR0cHM6Ly93d3cudHlwZXNjcmlwdGxhbmcub3JnL2RvY3MvaGFuZGJvb2svMi9ldmVyeWRheS10eXBlcy5odG1sI3R5cGUtYWxpYXNlcw==" title="https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#type-aliases">型別別名（Type Alias）<i class="fa fa-external-link"></i></span>**，用來為任意型別取一個名字</li></ul><blockquote><p>“An interface declaration is another way to name an object type.” </p></blockquote><ul><li><code>interface</code>  是 <strong><span class="exturl" data-url="aHR0cHM6Ly93d3cudHlwZXNjcmlwdGxhbmcub3JnL2RvY3MvaGFuZGJvb2svMi9ldmVyeWRheS10eXBlcy5odG1sI2ludGVyZmFjZXM=" title="https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#interfaces">物件型別的另一種命名方式<i class="fa fa-external-link"></i></span></strong></li></ul><blockquote><p>“The key distinction is that a type cannot be re-opened to add new properties vs an <strong>interface which is always extendable</strong>.”</p></blockquote><ul><li>且 <code>interface</code> 可被重複宣告，用來 <strong><span class="exturl" data-url="aHR0cHM6Ly93d3cudHlwZXNjcmlwdGxhbmcub3JnL2RvY3MvaGFuZGJvb2svMi9ldmVyeWRheS10eXBlcy5odG1sI2RpZmZlcmVuY2VzLWJldHdlZW4tdHlwZS1hbGlhc2VzLWFuZC1pbnRlcmZhY2Vz" title="https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#differences-between-type-aliases-and-interfaces">擴展屬性<i class="fa fa-external-link"></i></span>（Declaration Merging）</strong></li></ul><hr><h3 id="基本語法比較"><a href="#基本語法比較" class="headerlink" title="基本語法比較"></a>基本語法比較</h3><p>在最常見的物件型別定義中，兩者寫法幾乎相同：</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 使用 type</span></span><br><span class="line"><span class="keyword">type</span> <span class="title class_">User</span> = &#123;</span><br><span class="line">  <span class="attr">id</span>: <span class="built_in">string</span></span><br><span class="line">  <span class="attr">name</span>: <span class="built_in">string</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用 interface</span></span><br><span class="line"><span class="keyword">interface</span> <span class="title class_">User</span> &#123;</span><br><span class="line">  <span class="attr">id</span>: <span class="built_in">string</span></span><br><span class="line">  <span class="attr">name</span>: <span class="built_in">string</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>官方原文也指出兩者的相似性：</p><blockquote><p>“Type aliases and interfaces are <strong>very similar</strong>, and in many cases you can choose between them freely.”</p></blockquote><ul><li>在一般物件型別中，兩者通常可以 <strong><span class="exturl" data-url="aHR0cHM6Ly93d3cudHlwZXNjcmlwdGxhbmcub3JnL2RvY3MvaGFuZGJvb2svMi9ldmVyeWRheS10eXBlcy5odG1sI2RpZmZlcmVuY2VzLWJldHdlZW4tdHlwZS1hbGlhc2VzLWFuZC1pbnRlcmZhY2Vz" title="https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#differences-between-type-aliases-and-interfaces">自由選擇<i class="fa fa-external-link"></i></span></strong></li></ul><hr><h3 id="type-才能做到的事"><a href="#type-才能做到的事" class="headerlink" title="type 才能做到的事"></a><code>type</code> 才能做到的事</h3><p><code>type</code> 能為任意型別取一個名字，適用範圍比 <code>interface</code> 更廣：</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Union（聯合型別）</span></span><br><span class="line"><span class="keyword">type</span> <span class="title class_">Status</span> = <span class="string">&#x27;active&#x27;</span> | <span class="string">&#x27;inactive&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// Tuple（元組）</span></span><br><span class="line"><span class="keyword">type</span> <span class="title class_">Pair</span> = [<span class="built_in">string</span>, <span class="built_in">number</span>]</span><br><span class="line"></span><br><span class="line"><span class="comment">// Conditional Type（條件型別）</span></span><br><span class="line"><span class="keyword">type</span> <span class="title class_">IsString</span>&lt;T&gt; = T <span class="keyword">extends</span> <span class="built_in">string</span> ? <span class="literal">true</span> : <span class="literal">false</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// Mapped Type（映射型別）</span></span><br><span class="line"><span class="keyword">type</span> <span class="title class_">MyReadonly</span>&lt;T&gt; = &#123; <span class="keyword">readonly</span> [K <span class="keyword">in</span> keyof T]: T[K] &#125;</span><br></pre></td></tr></table></figure><p>官方原文明確說明了 <code>interface</code> 的限制：</p><blockquote><p>“Unlike a type alias, an interface can <strong>only</strong> describe object shapes; it <strong>cannot</strong> be used to rename primitives.”</p></blockquote><ul><li><code>interface</code> **<span class="exturl" data-url="aHR0cHM6Ly93d3cudHlwZXNjcmlwdGxhbmcub3JnL2NoZWF0c2hlZXRzLw==" title="https://www.typescriptlang.org/cheatsheets/">只能描述物件的形狀<i class="fa fa-external-link"></i></span>**，無法用於定義 Primitive（基本型別）、Union、Tuple 等型別</li></ul><p>簡單來說：</p><ul><li><strong><code>interface</code></strong> → 專門描述「物件結構」</li><li><strong><code>type</code></strong> → TypeScript 型別系統的通用工具</li></ul><p>那麼，什麼情況下必須使用 <code>interface</code> 呢？</p><hr><h2 id="interface-不可取代的場景"><a href="#interface-不可取代的場景" class="headerlink" title="interface 不可取代的場景"></a><code>interface</code> 不可取代的場景</h2><h3 id="Declaration-Merging-宣告合併"><a href="#Declaration-Merging-宣告合併" class="headerlink" title="Declaration Merging 宣告合併"></a>Declaration Merging 宣告合併</h3><p>同名的 <code>interface</code> 會被 TypeScript 自動合併：</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">interface</span> <span class="title class_">Config</span> &#123; <span class="attr">debug</span>: <span class="built_in">boolean</span> &#125;</span><br><span class="line"><span class="keyword">interface</span> <span class="title class_">Config</span> &#123; <span class="attr">lang</span>: <span class="built_in">string</span> &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 結果：Config = &#123; debug: boolean; lang: string &#125;</span></span><br></pre></td></tr></table></figure><p>使用 <code>type</code> 則會直接報錯：</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">type</span> <span class="title class_">Config</span> = &#123; <span class="attr">debug</span>: <span class="built_in">boolean</span> &#125;</span><br><span class="line"><span class="keyword">type</span> <span class="title class_">Config</span> = &#123; <span class="attr">lang</span>: <span class="built_in">string</span> &#125;   <span class="comment">// ❌ Duplicate identifier</span></span><br></pre></td></tr></table></figure><p>官方原文定義：</p><blockquote><p>“The simplest, and perhaps most common, type of declaration merging is interface merging. At the most basic level, the merge mechanically <strong>joins the members of both declarations into a single interface</strong> with the same name.”</p></blockquote><ul><li>Declaration Merging 會將同名 <code>interface</code> 的成員 <strong><span class="exturl" data-url="aHR0cHM6Ly93d3cudHlwZXNjcmlwdGxhbmcub3JnL2RvY3MvaGFuZGJvb2svZGVjbGFyYXRpb24tbWVyZ2luZy5odG1s" title="https://www.typescriptlang.org/docs/handbook/declaration-merging.html">機械式地結合為單一介面<i class="fa fa-external-link"></i></span></strong></li></ul><hr><h4 id="實務應用：Module-Augmentation-擴充第三方型別"><a href="#實務應用：Module-Augmentation-擴充第三方型別" class="headerlink" title="實務應用：Module Augmentation 擴充第三方型別"></a>實務應用：Module Augmentation 擴充第三方型別</h4><p>Declaration Merging 最重要的實務用途是「擴充第三方套件的型別」，這個做法稱為 Module Augmentation（擴充第三方型別）。</p><ul><li>使用 <code>interface</code> 擴充型別</li></ul><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ✅ interface → merge 成功</span></span><br><span class="line"><span class="keyword">declare</span> <span class="variable language_">module</span> <span class="string">&#x27;react-i18next&#x27;</span> &#123;</span><br><span class="line">  <span class="keyword">interface</span> <span class="title class_">CustomTypeOptions</span> &#123;</span><br><span class="line">    <span class="attr">defaultNS</span>: <span class="string">&#x27;common&#x27;</span></span><br><span class="line">    <span class="attr">resources</span>: <span class="keyword">typeof</span> resources</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>使用 <code>type</code> 則會同名報錯</li></ul><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ❌ type → Duplicate identifier</span></span><br><span class="line"><span class="keyword">declare</span> <span class="variable language_">module</span> <span class="string">&#x27;react-i18next&#x27;</span> &#123;</span><br><span class="line">  <span class="keyword">type</span> <span class="title class_">CustomTypeOptions</span> = &#123;</span><br><span class="line">    <span class="attr">defaultNS</span>: <span class="string">&#x27;common&#x27;</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>官方原文：</p><blockquote><p>“Although JavaScript modules do not support merging, you can <strong>patch existing objects</strong> by importing and then updating them.”</p></blockquote><ul><li>Module Augmentation 透過 Declaration Merging 來 <strong><span class="exturl" data-url="aHR0cHM6Ly93d3cudHlwZXNjcmlwdGxhbmcub3JnL2RvY3MvaGFuZGJvb2svZGVjbGFyYXRpb24tbWVyZ2luZy5odG1sI21vZHVsZS1hdWdtZW50YXRpb24=" title="https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation">對現有物件進行擴充<i class="fa fa-external-link"></i></span></strong></li></ul><hr><h3 id="class-implements-類別契約"><a href="#class-implements-類別契約" class="headerlink" title="class implements 類別契約"></a>class implements 類別契約</h3><ul><li><code>implements</code> 用於檢查類別是否 <strong><span class="exturl" data-url="aHR0cHM6Ly93d3cudHlwZXNjcmlwdGxhbmcub3JnL2RvY3MvaGFuZGJvb2svMi9jbGFzc2VzLmh0bWwjaW1wbGVtZW50cy1jbGF1c2Vz" title="https://www.typescriptlang.org/docs/handbook/2/classes.html#implements-clauses">滿足特定介面的契約<i class="fa fa-external-link"></i></span></strong></li></ul><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">interface</span> <span class="title class_">Pingable</span> &#123;</span><br><span class="line">  <span class="title function_">ping</span>(): <span class="built_in">void</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Sonar</span> <span class="keyword">implements</span> <span class="title class_">Pingable</span> &#123;</span><br><span class="line">  <span class="title function_">ping</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;ping!&quot;</span>)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>官方原文：</p><blockquote><p>“You can use an implements clause to check that a class <strong>satisfies a particular interface</strong>. An error will be issued if a class fails to correctly implement it.”</p></blockquote><p>技術上 <code>type</code> 也能搭配 <code>implements</code> 使用，但 <code>interface</code> 更能表達 <strong>「這是類別必須遵守的契約」</strong> 這層語意，且轉換為 <code>type</code> 沒有實質好處，因此建議維持 <code>interface</code>。</p><hr><h2 id="總結"><a href="#總結" class="headerlink" title="總結"></a>總結</h2><p>以下整理前面提到的所有差異：</p><table><thead><tr><th>能力</th><th align="center"><code>type</code></th><th align="center"><code>interface</code></th></tr></thead><tbody><tr><td>物件型別</td><td align="center">✅</td><td align="center">✅</td></tr><tr><td>Union 聯合 / Tuple 元組</td><td align="center">✅</td><td align="center">❌</td></tr><tr><td>Conditional 條件 / Mapped 映射</td><td align="center">✅</td><td align="center">❌</td></tr><tr><td>Primitive 基本型別</td><td align="center">✅</td><td align="center">❌</td></tr><tr><td>Declaration Merging 宣告合併</td><td align="center">❌</td><td align="center">✅</td></tr></tbody></table><p>根據使用差異，可以透過以下流程快速判斷：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">要定義型別</span><br><span class="line">  │</span><br><span class="line">  ▼</span><br><span class="line">在 declare module 內嗎？（Module Augmentation）</span><br><span class="line">  ├─ Yes → 使用 interface（必須）：避免報錯</span><br><span class="line">  │</span><br><span class="line">  ▼</span><br><span class="line">是 class implements 的契約嗎？</span><br><span class="line">  ├─ Yes → 使用 interface（推薦）：語意明確</span><br><span class="line">  │</span><br><span class="line">  ▼</span><br><span class="line">其他所有情況</span><br><span class="line">  └─ 使用 type（預設選擇）：統一風格</span><br></pre></td></tr></table></figure><p>在實務開發中，<code>type</code> 與 <code>interface</code> 經常會混合出現在同一個專案中。<br>當團隊嘗試統一型別定義風格時，才會發現兩者的行為其實並不完全相同。</p><p>理解差異後，選擇的標準其實很簡單：</p><blockquote><p><strong>只在 <code>type</code> 做不到的時候，才使用 <code>interface</code>。</strong></p></blockquote><p>這樣既能維持型別工具的一致性，也能在需要擴充型別時保留 <code>interface</code> 的優勢。</p><h3 id="補充：interface-extends-vs-type-amp-的效能差異"><a href="#補充：interface-extends-vs-type-amp-的效能差異" class="headerlink" title="補充：interface extends vs type &amp; 的效能差異"></a>補充：<code>interface extends</code> vs <code>type &amp;</code> 的效能差異</h3><p>TypeScript 官方的 <span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL21pY3Jvc29mdC9UeXBlU2NyaXB0L3dpa2kvUGVyZm9ybWFuY2UjcHJlZmVycmluZy1pbnRlcmZhY2VzLW92ZXItaW50ZXJzZWN0aW9ucw==" title="https://github.com/microsoft/TypeScript/wiki/Performance#preferring-interfaces-over-intersections">Performance Wiki<i class="fa fa-external-link"></i></span> 中提到：</p><blockquote><p>“Interfaces create a single flat object type that detects property conflicts…<br>Intersections on the other hand just recursively merge properties, and in some cases produce <code>never</code>.”</p></blockquote><p>簡單來說：</p><ul><li><code>interface extends</code> 會建立 <strong>扁平化（flat）的物件型別</strong></li><li><code>type A &amp; B</code>（intersection）則會 <strong>遞迴地合併型別結構</strong></li></ul><p>在複雜型別組合時，intersection 有時需要重新展開計算，甚至可能產生 <code>never</code>。因此 TypeScript 官方建議，在物件型別的繼承或組合場景中，優先使用 <code>interface extends</code>。</p><p>不過這個差異通常只會在 <strong>大型專案中或複雜型別組合</strong> 中才比較明顯，對於一般規模的專案而言，幾乎不會感受到實際影響，因此這裡僅作為補充說明。</p><blockquote><p>延伸閱讀：<span class="exturl" data-url="aHR0cHM6Ly96ZW5uLmRldi9ibXRoL2FydGljbGVzL2ludGVyZmFjZS1wcm9wcy1leHRlbmRz" title="https://zenn.dev/bmth/articles/interface-props-extends">【結論】TypeScriptの型定義はtypeよりinterfaceを使うべき理由<i class="fa fa-external-link"></i></span></p></blockquote><h2 id="參考資料"><a href="#參考資料" class="headerlink" title="參考資料"></a>參考資料</h2><ul><li><span class="exturl" data-url="aHR0cHM6Ly93d3cudHlwZXNjcmlwdGxhbmcub3JnL2RvY3MvaGFuZGJvb2svMi9ldmVyeWRheS10eXBlcy5odG1sI3R5cGUtYWxpYXNlcw==" title="https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#type-aliases">Everyday Types — Type Aliases<i class="fa fa-external-link"></i></span></li><li><span class="exturl" data-url="aHR0cHM6Ly93d3cudHlwZXNjcmlwdGxhbmcub3JnL2RvY3MvaGFuZGJvb2svMi9ldmVyeWRheS10eXBlcy5odG1sI2ludGVyZmFjZXM=" title="https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#interfaces">Everyday Types — Interfaces<i class="fa fa-external-link"></i></span></li><li><span class="exturl" data-url="aHR0cHM6Ly93d3cudHlwZXNjcmlwdGxhbmcub3JnL2RvY3MvaGFuZGJvb2svMi9ldmVyeWRheS10eXBlcy5odG1sI2RpZmZlcmVuY2VzLWJldHdlZW4tdHlwZS1hbGlhc2VzLWFuZC1pbnRlcmZhY2Vz" title="https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#differences-between-type-aliases-and-interfaces">Everyday Types — Differences Between Type Aliases and Interfaces<i class="fa fa-external-link"></i></span></li><li><span class="exturl" data-url="aHR0cHM6Ly93d3cudHlwZXNjcmlwdGxhbmcub3JnL2NoZWF0c2hlZXRzLw==" title="https://www.typescriptlang.org/cheatsheets/">TypeScript Cheat Sheets — Interfaces<i class="fa fa-external-link"></i></span></li><li><span class="exturl" data-url="aHR0cHM6Ly93d3cudHlwZXNjcmlwdGxhbmcub3JnL2RvY3MvaGFuZGJvb2svZGVjbGFyYXRpb24tbWVyZ2luZy5odG1s" title="https://www.typescriptlang.org/docs/handbook/declaration-merging.html">Declaration Merging<i class="fa fa-external-link"></i></span></li><li><span class="exturl" data-url="aHR0cHM6Ly93d3cudHlwZXNjcmlwdGxhbmcub3JnL2RvY3MvaGFuZGJvb2svZGVjbGFyYXRpb24tbWVyZ2luZy5odG1sI21vZHVsZS1hdWdtZW50YXRpb24=" title="https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation">Declaration Merging — Module Augmentation<i class="fa fa-external-link"></i></span></li><li><span class="exturl" data-url="aHR0cHM6Ly93d3cudHlwZXNjcmlwdGxhbmcub3JnL2RvY3MvaGFuZGJvb2svMi9jbGFzc2VzLmh0bWwjaW1wbGVtZW50cy1jbGF1c2Vz" title="https://www.typescriptlang.org/docs/handbook/2/classes.html#implements-clauses">Classes — implements Clauses<i class="fa fa-external-link"></i></span></li></ul>]]>
    </content>
    <id>https://heidiliu2020.github.io/type-vs-interface/</id>
    <link href="https://heidiliu2020.github.io/type-vs-interface/"/>
    <published>2026-03-12T09:06:00.000Z</published>
    <summary>
      <![CDATA[<p>在 TypeScript 中，<code>type</code>（型別別名）與 <code>interface</code>（介面宣告）都可以用來描述資料的型別結構。</p>
<p>在定義 Object Types（物件型別）時，兩者寫法看起來幾乎相同，許多情況下還可以互換使用，但它們在設計目的與使用範圍上仍存在一點差異。</p>
<p>本文的結論其實很簡單：</p>
<blockquote>
<p><strong>只在 <code>type</code> 做不到的時候，才使用 <code>interface</code>。</strong></p>
</blockquote>
<p>這篇文章將從以下幾個角度說明為什麼：</p>
<ul>
<li><code>type</code> 與 <code>interface</code> 的核心差異</li>
<li><code>interface</code> 不可取代的場景</li>
<li>實務上的使用判斷流程</li>
</ul>]]>
    </summary>
    <title>TypeScript：type vs interface 使用差異與選擇策略</title>
    <updated>2026-04-23T06:14:18.274Z</updated>
  </entry>
  <entry>
    <author>
      <name>Heidi Liu</name>
    </author>
    <category term="技術學習" scheme="https://heidiliu2020.github.io/categories/%E6%8A%80%E8%A1%93%E5%AD%B8%E7%BF%92/"/>
    <category term="Front-End" scheme="https://heidiliu2020.github.io/categories/%E6%8A%80%E8%A1%93%E5%AD%B8%E7%BF%92/Front-End/"/>
    <category term="React" scheme="https://heidiliu2020.github.io/categories/%E6%8A%80%E8%A1%93%E5%AD%B8%E7%BF%92/Front-End/React/"/>
    <category term="Front-End" scheme="https://heidiliu2020.github.io/tags/Front-End/"/>
    <category term="React" scheme="https://heidiliu2020.github.io/tags/React/"/>
    <category term="Next.js" scheme="https://heidiliu2020.github.io/tags/Next-js/"/>
    <content>
      <![CDATA[<p><img src="https://hackmd.io/_uploads/B1RYAaBtA.png" alt="cover"></p><blockquote><p>GitHub - <span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL3NoYWRjbi11aS91aQ==" title="https://github.com/shadcn-ui/ui">https://github.com/shadcn-ui/ui<i class="fa fa-external-link"></i></span></p></blockquote><h2 id="What-is-Shadcn-UI"><a href="#What-is-Shadcn-UI" class="headerlink" title="What is Shadcn UI?"></a>What is Shadcn UI?</h2><p>Shadcn UI 是基於 <span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL3RhaWx3aW5kbGFicy90YWlsd2luZGNzcw==" title="https://github.com/tailwindlabs/tailwindcss">Tailwind CSS<i class="fa fa-external-link"></i></span> 底層封裝 <span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL3JhZGl4LXVpL3ByaW1pdGl2ZXM=" title="https://github.com/radix-ui/primitives">Radix UI<i class="fa fa-external-link"></i></span> 的 React UI 元件集合，能夠支援 <span class="exturl" data-url="aHR0cHM6Ly91aS5zaGFkY24uY29tL2RvY3MvaW5zdGFsbGF0aW9uL25leHQ=" title="https://ui.shadcn.com/docs/installation/next">Next.js<i class="fa fa-external-link"></i></span>, <span class="exturl" data-url="aHR0cHM6Ly91aS5zaGFkY24uY29tL2RvY3MvaW5zdGFsbGF0aW9uL2FzdHJv" title="https://ui.shadcn.com/docs/installation/astro">Astro<i class="fa fa-external-link"></i></span>, <span class="exturl" data-url="aHR0cHM6Ly91aS5zaGFkY24uY29tL2RvY3MvaW5zdGFsbGF0aW9uL3JlbWl4" title="https://ui.shadcn.com/docs/installation/remix">Remix<i class="fa fa-external-link"></i></span>, <span class="exturl" data-url="aHR0cHM6Ly91aS5zaGFkY24uY29tL2RvY3MvaW5zdGFsbGF0aW9uL2dhdHNieQ==" title="https://ui.shadcn.com/docs/installation/gatsby">Gatsby<i class="fa fa-external-link"></i></span> 等框架。</p><span id="more"></span><p><span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL3NoYWRjbi11aS91aQ==" title="https://github.com/shadcn-ui/ui">shadcn-ui/ui<i class="fa fa-external-link"></i></span> 專案於 2023 年 1 月發布到 GitHub，截至目前（2024 年 7 月）已有超過 65K 星星數，榮登 <span class="exturl" data-url="aHR0cHM6Ly9yaXNpbmdzdGFycy5qcy5vcmcvMjAyMy9lbg==" title="https://risingstars.js.org/2023/en">2023 JavaScript Rising Stars<i class="fa fa-external-link"></i></span> 榜首。</p><p>之所以能夠成為炙手可熱的開源專案，如 <span class="exturl" data-url="aHR0cHM6Ly91aS5zaGFkY24uY29tL2RvY3M=" title="https://ui.shadcn.com/docs">Shadcn 官方文件<i class="fa fa-external-link"></i></span>介紹所述：</p><blockquote><p>Beautifully designed components that you can copy and paste into your apps. Accessible. Customizable. Open Source.<br>（您可以複製貼上設計精美的元件至應用程式中。無障礙、可客製化且開源。）</p></blockquote><p>Shadcn UI 並不是一個 component library（元件庫），而是可重用元件的「集合」。換言之，Shadcn UI 並不會以 dependency 的形式出現在 <code>pacakge.json</code>，使用者可以直接把原始碼複製貼上到專案中，也能自行修改客製化。</p><h2 id="Why-Shadcn-UI"><a href="#Why-Shadcn-UI" class="headerlink" title="Why Shadcn UI?"></a>Why Shadcn UI?</h2><p>Shadcn UI 在前端生態圈崛起的原因，可從過去我們所熟悉的 UI Framework，如 <span class="exturl" data-url="aHR0cHM6Ly9nZXRib290c3RyYXAuY29tLw==" title="https://getbootstrap.com/">Bootstrap<i class="fa fa-external-link"></i></span>, <span class="exturl" data-url="aHR0cHM6Ly9tdWkuY29tLw==" title="https://mui.com/">Meterial UI<i class="fa fa-external-link"></i></span>, <span class="exturl" data-url="aHR0cHM6Ly9hbnQuZGVzaWduLw==" title="https://ant.design/">Ant Design<i class="fa fa-external-link"></i></span> 等說起。</p><p>透過元件庫提供的預設元件進行開發，就不需要從零開始造輪子，能夠統一介面樣式並實現 RWD 等需求，然而在實際使用元件庫時，會發現在客製化上有一定限制，經常會為了改一小部分樣式，而透過 <code>!important</code>、<code>:deep</code> 等「魔改」的方式，來達到自訂義樣式需求，如此不只增加程式碼的複雜性，也不易後續維護使用。</p><p>如上所述，Shadcn UI 具備的種種特性，提供現有的元件庫解決方案，其優點如下：</p><ul><li>基於 Tailwind CSS 開發：學習曲線相對較低</li><li>底層封裝 <span class="exturl" data-url="aHR0cHM6Ly93d3cucmFkaXgtdWkuY29tL3ByaW1pdGl2ZXM=" title="https://www.radix-ui.com/primitives">Radix UI<i class="fa fa-external-link"></i></span>：是一種 <span class="exturl" data-url="aHR0cHM6Ly9oZWFkbGVzc3VpLmNvbS8=" title="https://headlessui.com/">Headless UI<i class="fa fa-external-link"></i></span>，只包含功能和邏輯、提供完全無樣式、支援無障礙的 UI 元件庫，因此易於客製化</li><li>可重用的元件集合：官方文件提供許多範例，可根據需求選擇元件、主題樣式，透過 CLI 或手動複製程式碼到專案中編輯使用</li><li>輕量有彈性：不需一次安裝所有套件，需要時再透過 <code>shadcn-ui</code> 引入使用</li><li>支援主流框架，如 <span class="exturl" data-url="aHR0cHM6Ly91aS5zaGFkY24uY29tL2RvY3MvaW5zdGFsbGF0aW9uL25leHQ=" title="https://ui.shadcn.com/docs/installation/next">Next.js<i class="fa fa-external-link"></i></span>, <span class="exturl" data-url="aHR0cHM6Ly91aS5zaGFkY24uY29tL2RvY3MvaW5zdGFsbGF0aW9uL3JlbWl4" title="https://ui.shadcn.com/docs/installation/remix">Remix<i class="fa fa-external-link"></i></span>, <span class="exturl" data-url="aHR0cHM6Ly93d3cuc2hhZGNuLXZ1ZS5jb20v" title="https://www.shadcn-vue.com/">Vue<i class="fa fa-external-link"></i></span> 等</li></ul><h2 id="How-to-use-Shadcn-UI"><a href="#How-to-use-Shadcn-UI" class="headerlink" title="How to use Shadcn UI?"></a>How to use Shadcn UI?</h2><p>詳細步驟可參考官方文件，本篇以 <span class="exturl" data-url="aHR0cHM6Ly91aS5zaGFkY24uY29tL2RvY3MvaW5zdGFsbGF0aW9uL25leHQ=" title="https://ui.shadcn.com/docs/installation/next">Next.js<i class="fa fa-external-link"></i></span> 專案為例：</p><h3 id="專案建置-Getting-Start"><a href="#專案建置-Getting-Start" class="headerlink" title="專案建置 Getting Start"></a>專案建置 Getting Start</h3><ol><li>透過 <code>creat-next-app</code> 指令建置 Next.js 專案</li></ol><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npx create-next-app@latest my-app --typescript --tailwind --eslint</span><br></pre></td></tr></table></figure><p><img src="https://hackmd.io/_uploads/rk3c9__tA.png" alt="next.js"></p><ol start="2"><li>執行 CLI <code>shadcn-ui</code> 初始專案</li></ol><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">cd my-app</span><br><span class="line">npx shadcn-ui@latest init</span><br></pre></td></tr></table></figure><ol start="3"><li>選擇系統初始樣式配置，後續也可在設定檔 <code>components.json</code> 做調整</li></ol><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">Which style would you like to use? › Default</span><br><span class="line">Which color would you like to use as base color? › Slate</span><br><span class="line">Do you want to use CSS variables for colors? › no / yes</span><br></pre></td></tr></table></figure><p><img src="https://hackmd.io/_uploads/HJ1SsudtC.png" alt="init config"></p><p>建立好的 Next.js 專案架構可參考如下，其中引入的 Shadcn 元件會放在 <code>compoent/ui</code> 資料夾底下：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">.</span><br><span class="line">├── app</span><br><span class="line">│   ├── layout.tsx</span><br><span class="line">│   └── page.tsx</span><br><span class="line">├── components    </span><br><span class="line">│   ├── ui                   // 引入的元件可在專案中進行編輯</span><br><span class="line">│   │   ├── alert-dialog.tsx</span><br><span class="line">│   │   ├── button.tsx</span><br><span class="line">│   │   ├── dropdown-menu.tsx</span><br><span class="line">│   │   └── ...</span><br><span class="line">│   ├── main-nav.tsx         // 將引入的元件進一步客製化元件</span><br><span class="line">│   ├── page-header.tsx</span><br><span class="line">│   ├── alert.tsx</span><br><span class="line">│   ├── sidebar.tsx</span><br><span class="line">│   └── ...</span><br><span class="line">├── lib</span><br><span class="line">│   └── utils.ts</span><br><span class="line">├── styles                   // 自定義樣式</span><br><span class="line">│   └── globals.css</span><br><span class="line">├── next.config.js</span><br><span class="line">├── package.json</span><br><span class="line">├── postcss.config.js</span><br><span class="line">├── tailwind.config.js       // Tailwind 設定檔</span><br><span class="line">└── tsconfig.json</span><br></pre></td></tr></table></figure><h3 id="新增字型-Font"><a href="#新增字型-Font" class="headerlink" title="新增字型 Font"></a>新增字型 Font</h3><ol><li>引入 font 字型至 root layout</li></ol><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> <span class="keyword">type</span> &#123; <span class="title class_">Metadata</span> &#125; <span class="keyword">from</span> <span class="string">&quot;next&quot;</span>;</span><br><span class="line"><span class="keyword">import</span> <span class="string">&quot;./globals.css&quot;</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">Inter</span> <span class="keyword">as</span> <span class="title class_">FontSans</span> &#125; <span class="keyword">from</span> <span class="string">&quot;next/font/google&quot;</span></span><br><span class="line"><span class="keyword">import</span> &#123; cn &#125; <span class="keyword">from</span> <span class="string">&quot;@/lib/utils&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> fontSans = <span class="title class_">FontSans</span>(&#123;</span><br><span class="line">  <span class="attr">subsets</span>: [<span class="string">&quot;latin&quot;</span>],</span><br><span class="line">  <span class="attr">variable</span>: <span class="string">&quot;--font-sans&quot;</span>,</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> <span class="attr">metadata</span>: <span class="title class_">Metadata</span> = &#123;</span><br><span class="line">  <span class="attr">title</span>: <span class="string">&quot;Create Next App&quot;</span>,</span><br><span class="line">  <span class="attr">description</span>: <span class="string">&quot;Generated by create next app&quot;</span>,</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">RootLayout</span>(<span class="params">&#123;</span></span><br><span class="line"><span class="params">  children,</span></span><br><span class="line"><span class="params">&#125;: <span class="title class_">Readonly</span>&lt;&#123;</span></span><br><span class="line"><span class="params">  children: React.ReactNode;</span></span><br><span class="line"><span class="params">&#125;&gt;</span>) &#123;</span><br><span class="line">  <span class="keyword">return</span> (</span><br><span class="line">    <span class="language-xml"><span class="tag">&lt;<span class="name">html</span> <span class="attr">lang</span>=<span class="string">&quot;en&quot;</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">      <span class="tag">&lt;<span class="name">body</span> <span class="attr">className</span>=<span class="string">&#123;cn(</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml">        &quot;<span class="attr">min-h-screen</span> <span class="attr">bg-background</span> <span class="attr">font-sans</span> <span class="attr">antialiased</span>&quot;,</span></span></span><br><span class="line"><span class="tag"><span class="language-xml">        <span class="attr">fontSans.variable</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml">      )&#125;&gt;</span></span></span><br><span class="line"><span class="language-xml">        &#123;children&#125;</span></span><br><span class="line"><span class="language-xml">      <span class="tag">&lt;/<span class="name">body</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">    <span class="tag">&lt;/<span class="name">html</span>&gt;</span></span></span><br><span class="line">  );</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol start="2"><li>在 <code>tailwind.config.js</code> 調整設定檔 <code>theme.extend.fontFamily</code>，即可根據需求自定義樣式</li></ol><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> &#123; fontFamily &#125; = <span class="built_in">require</span>(<span class="string">&quot;tailwindcss/defaultTheme&quot;</span>)</span><br><span class="line"> </span><br><span class="line"><span class="comment">/** <span class="doctag">@type</span> &#123;<span class="type">import(&#x27;tailwindcss&#x27;).Config</span>&#125; */</span></span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = &#123;</span><br><span class="line">  <span class="attr">darkMode</span>: [<span class="string">&quot;class&quot;</span>],</span><br><span class="line">  <span class="attr">content</span>: [<span class="string">&quot;app/**/*.&#123;ts,tsx&#125;&quot;</span>, <span class="string">&quot;components/**/*.&#123;ts,tsx&#125;&quot;</span>],</span><br><span class="line">  <span class="attr">theme</span>: &#123;</span><br><span class="line">    <span class="attr">extend</span>: &#123;</span><br><span class="line">      <span class="attr">fontFamily</span>: &#123;</span><br><span class="line">        <span class="attr">sans</span>: [<span class="string">&quot;var(--font-sans)&quot;</span>, ...fontFamily.<span class="property">sans</span>],</span><br><span class="line">      &#125;,</span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="新增按鈕-Button"><a href="#新增按鈕-Button" class="headerlink" title="新增按鈕 Button"></a>新增按鈕 Button</h3><ol><li>透過 CLI 安裝 button 元件，或直接從<span class="exturl" data-url="aHR0cHM6Ly91aS5zaGFkY24uY29tL2RvY3MvY29tcG9uZW50cy9idXR0b24=" title="https://ui.shadcn.com/docs/components/button">官方文件<i class="fa fa-external-link"></i></span>手動複製</li></ol><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npx shadcn-ui@latest add button</span><br></pre></td></tr></table></figure><p>安裝好的元件路徑會在 components 底下：<code>@components/ui/button</code></p><p><img src="https://hackmd.io/_uploads/S1LnfY_F0.png" alt="button"></p><ol start="2"><li>即可引入專案使用</li></ol><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// app/page.tsx</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">Button</span> &#125; <span class="keyword">from</span> <span class="string">&quot;@/components/ui/button&quot;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">Home</span>(<span class="params"></span>) &#123;</span><br><span class="line">  <span class="keyword">return</span> (</span><br><span class="line">    <span class="language-xml"><span class="tag">&lt;&gt;</span></span></span><br><span class="line"><span class="language-xml">      <span class="tag">&lt;<span class="name">h1</span>&gt;</span>Hello World<span class="tag">&lt;/<span class="name">h1</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">      <span class="tag">&lt;<span class="name">Button</span>&gt;</span>Enter!<span class="tag">&lt;/<span class="name">Button</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">    <span class="tag">&lt;/&gt;</span></span></span><br><span class="line">  )</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><img src="https://hackmd.io/_uploads/H1cGNtdtC.png" alt="demo-1"></p><ol start="3"><li>除了預設樣式，也可直接編輯元件程式碼，例如在 <code>components/ui/button.tsx</code> 檔案中新增自訂義樣式 <code>newButton</code>，即可在頁面使用</li></ol><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// app/page.tsx</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">Button</span> &#125; <span class="keyword">from</span> <span class="string">&quot;@/components/ui/button&quot;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">Home</span>(<span class="params"></span>) &#123;</span><br><span class="line">  <span class="keyword">return</span> (</span><br><span class="line">    <span class="language-xml"><span class="tag">&lt;&gt;</span></span></span><br><span class="line"><span class="language-xml">      <span class="tag">&lt;<span class="name">h1</span>&gt;</span>Hello World<span class="tag">&lt;/<span class="name">h1</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">      <span class="tag">&lt;<span class="name">Button</span> <span class="attr">variant</span>=<span class="string">&quot;outline&quot;</span>&gt;</span>outline<span class="tag">&lt;/<span class="name">Button</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">      <span class="tag">&lt;<span class="name">Button</span> <span class="attr">variant</span>=<span class="string">&quot;link&quot;</span>&gt;</span>link<span class="tag">&lt;/<span class="name">Button</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">      <span class="tag">&lt;<span class="name">Button</span> <span class="attr">variant</span>=<span class="string">&quot;newButton&quot;</span>&gt;</span>newButton<span class="tag">&lt;/<span class="name">Button</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">    <span class="tag">&lt;/&gt;</span></span></span><br><span class="line">  )</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>結果如下：</p><p><img src="https://hackmd.io/_uploads/ryOrYj_tR.png" alt="button"></p><h3 id="暗色主題-Dark-Mode"><a href="#暗色主題-Dark-Mode" class="headerlink" title="暗色主題 Dark Mode"></a>暗色主題 Dark Mode</h3><p>由於 Shadcn UI 所有元件已內建 Dark Mode 樣式設定，因此只需搭配 Themes 來實作，而 Next.js 可搭配 <code>next-themes</code> 套件</p><ol><li>透過 CLI 安裝 <code>next-themes</code></li></ol><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install next-themes</span><br></pre></td></tr></table></figure><ol start="2"><li>引入 <code>next-themes</code> 中的 ThemeProvider 來管理主題樣式：<code>components/theme-provider.tsx</code></li></ol><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// components/theme-provider.tsx</span></span><br><span class="line"></span><br><span class="line"><span class="string">&quot;use client&quot;</span></span><br><span class="line"> </span><br><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> <span class="title class_">React</span> <span class="keyword">from</span> <span class="string">&quot;react&quot;</span></span><br><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">ThemeProvider</span> <span class="keyword">as</span> <span class="title class_">NextThemesProvider</span> &#125; <span class="keyword">from</span> <span class="string">&quot;next-themes&quot;</span></span><br><span class="line"><span class="keyword">import</span> &#123; <span class="keyword">type</span> <span class="title class_">ThemeProviderProps</span> &#125; <span class="keyword">from</span> <span class="string">&quot;next-themes/dist/types&quot;</span></span><br><span class="line"> </span><br><span class="line"><span class="keyword">export</span> <span class="keyword">function</span> <span class="title function_">ThemeProvider</span>(<span class="params">&#123; children, ...props &#125;: <span class="title class_">ThemeProviderProps</span></span>) &#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="language-xml"><span class="tag">&lt;<span class="name">NextThemesProvider</span> &#123;<span class="attr">...props</span>&#125;&gt;</span>&#123;children&#125;<span class="tag">&lt;/<span class="name">NextThemesProvider</span>&gt;</span></span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol start="3"><li>將 ThemeProvider 引入至 root layout</li></ol><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// app/layout.tsx</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">ThemeProvider</span> &#125; <span class="keyword">from</span> <span class="string">&quot;@/components/theme-provider&quot;</span></span><br><span class="line"> </span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">RootLayout</span>(<span class="params">&#123; children &#125;: <span class="title class_">RootLayoutProps</span></span>) &#123;</span><br><span class="line">  <span class="keyword">return</span> (</span><br><span class="line">    <span class="language-xml"><span class="tag">&lt;&gt;</span></span></span><br><span class="line"><span class="language-xml">      <span class="tag">&lt;<span class="name">html</span> <span class="attr">lang</span>=<span class="string">&quot;en&quot;</span> <span class="attr">suppressHydrationWarning</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">        <span class="tag">&lt;<span class="name">head</span> /&gt;</span></span></span><br><span class="line"><span class="language-xml">        <span class="tag">&lt;<span class="name">body</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">          <span class="tag">&lt;<span class="name">ThemeProvider</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml">            <span class="attr">attribute</span>=<span class="string">&quot;class&quot;</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml">            <span class="attr">defaultTheme</span>=<span class="string">&quot;system&quot;</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml">            <span class="attr">enableSystem</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml">            <span class="attr">disableTransitionOnChange</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml">          &gt;</span></span></span><br><span class="line"><span class="language-xml">            &#123;children&#125;</span></span><br><span class="line"><span class="language-xml">          <span class="tag">&lt;/<span class="name">ThemeProvider</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">        <span class="tag">&lt;/<span class="name">body</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">      <span class="tag">&lt;/<span class="name">html</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">    <span class="tag">&lt;/&gt;</span></span></span><br><span class="line">  )</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol start="4"><li>建立能觸發切換深淺模式的 ModeToggle 按鈕，這裡可安裝 <code>radix-ui/react-icons</code> 引入 icon 使用</li></ol><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install @radix-ui/react-icons</span><br></pre></td></tr></table></figure><ol start="5"><li>新增 toggle button 到頁面，注意需加上 <code>use client</code> 才能引入 <code>useTheme</code> hook 使用</li></ol><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">&quot;use client&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> <span class="title class_">React</span> <span class="keyword">from</span> <span class="string">&quot;react&quot;</span></span><br><span class="line"><span class="keyword">import</span> &#123; useTheme &#125; <span class="keyword">from</span> <span class="string">&quot;next-themes&quot;</span></span><br><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">MoonIcon</span>, <span class="title class_">SunIcon</span> &#125; <span class="keyword">from</span> <span class="string">&quot;@radix-ui/react-icons&quot;</span></span><br><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">Button</span> &#125; <span class="keyword">from</span> <span class="string">&quot;@/components/ui/button&quot;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">Home</span>(<span class="params"></span>) &#123;</span><br><span class="line">  <span class="keyword">const</span> &#123; theme, setTheme &#125; = <span class="title function_">useTheme</span>();</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> (</span><br><span class="line">    <span class="language-xml"><span class="tag">&lt;&gt;</span></span></span><br><span class="line"><span class="language-xml">      <span class="tag">&lt;<span class="name">h1</span>&gt;</span>Hello World<span class="tag">&lt;/<span class="name">h1</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">      <span class="tag">&lt;<span class="name">Button</span> <span class="attr">variant</span>=<span class="string">&quot;outline&quot;</span>&gt;</span>outline<span class="tag">&lt;/<span class="name">Button</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">      <span class="tag">&lt;<span class="name">Button</span> <span class="attr">variant</span>=<span class="string">&quot;link&quot;</span>&gt;</span>link<span class="tag">&lt;/<span class="name">Button</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">      <span class="tag">&lt;<span class="name">Button</span> <span class="attr">variant</span>=<span class="string">&quot;newButton&quot;</span>&gt;</span>newButton<span class="tag">&lt;/<span class="name">Button</span>&gt;</span></span></span><br><span class="line"><span class="language-xml"></span></span><br><span class="line"><span class="language-xml">      <span class="tag">&lt;<span class="name">div</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">        <span class="tag">&lt;<span class="name">Button</span> <span class="attr">variant</span>=<span class="string">&quot;outline&quot;</span> <span class="attr">size</span>=<span class="string">&quot;icon&quot;</span> <span class="attr">onClick</span>=<span class="string">&#123;()</span> =&gt;</span> setTheme(theme === &quot;light&quot; ? &quot;dark&quot; : &quot;light&quot;)&#125;&gt;</span></span><br><span class="line"><span class="language-xml">          <span class="tag">&lt;<span class="name">SunIcon</span> <span class="attr">className</span>=<span class="string">&quot;h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0&quot;</span> /&gt;</span></span></span><br><span class="line"><span class="language-xml">          <span class="tag">&lt;<span class="name">MoonIcon</span> <span class="attr">className</span>=<span class="string">&quot;absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100&quot;</span> /&gt;</span></span></span><br><span class="line"><span class="language-xml">          <span class="tag">&lt;<span class="name">span</span> <span class="attr">className</span>=<span class="string">&quot;sr-only&quot;</span>&gt;</span>Toggle theme<span class="tag">&lt;/<span class="name">span</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">        <span class="tag">&lt;/<span class="name">Button</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">      <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">    <span class="tag">&lt;/&gt;</span></span></span><br><span class="line">  )</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>效果如下：</p><p><img src="https://hackmd.io/_uploads/Hy4Jv6uK0.gif" alt="demo-dark"></p><h2 id="小結"><a href="#小結" class="headerlink" title="小結"></a>小結</h2><p>前陣子在開發 Next.js 專案時，是搭配使用 <span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL211aS9tYXRlcmlhbC11aQ==" title="https://github.com/mui/material-ui">material-ui<i class="fa fa-external-link"></i></span> 這套 UI Framework，卻意外發現在過程中遇到許多痛點：</p><ul><li>MUI 和 Tailwind 樣式需分開設定，可能發生樣式衝突，不易整合管理</li><li>需注意部分元件為付費方案，如 <span class="exturl" data-url="aHR0cHM6Ly9tdWkuY29tL3gvcmVhY3QtZGF0YS1ncmlkLw==" title="https://mui.com/x/react-data-grid/">MUI X Data Grid<i class="fa fa-external-link"></i></span>，免費版的 Data Grid 功能相對較少</li><li>MUI 能達到風格一致性的需求，但元件客製化不易，在開發上較缺乏彈性</li></ul><p>因此藉此機會來研究 Shadcn UI，希望能改善開發上遇到的問題，後續再根據專案需求來導入合適的方案，畢竟任何工具沒有絕對好壞，而是根據使用情境來評估是否導入使用。</p><h2 id="Reference"><a href="#Reference" class="headerlink" title="Reference"></a>Reference</h2><ul><li><span class="exturl" data-url="aHR0cHM6Ly9tZWRpdW0uY29tLyVFNiU4OSU4QiVFNSVBRiVBQiVFNyVBRCU4NiVFOCVBOCU5OC93aHktc2hhZGNuLXVpLWlzLXNvLXBvcHVsYXItaW4tMjAyMy0wZjk1ZTY2ZjNkZGM=" title="https://medium.com/%E6%89%8B%E5%AF%AB%E7%AD%86%E8%A8%98/why-shadcn-ui-is-so-popular-in-2023-0f95e66f3ddc">為什麼 Shadcn UI 是 2023 年前端最熱門的開源專案？<i class="fa fa-external-link"></i></span></li><li><span class="exturl" data-url="aHR0cHM6Ly9tZWRpdW0uY29tL0BLZWxseV9DSEkvc2hhZGNuLXVpLXRhaWx3aW5kLWNvbXBvbmVudHMtNmZkNGYxOTU5MTQ3" title="https://medium.com/@Kelly_CHI/shadcn-ui-tailwind-components-6fd4f1959147">Shadcn-ui : 美觀、無障礙、又能 100 % 客製化的「元件合集」<i class="fa fa-external-link"></i></span></li><li><span class="exturl" data-url="aHR0cHM6Ly9qdWVqaW4uY24vcG9zdC83MzQ0NzE5OTEzMDE5Mjc3MzIz" title="https://juejin.cn/post/7344719913019277323">在 2023 年屌爆了一整年的 shadcn/ui 用的 Headless UI 到底是何方神圣？<i class="fa fa-external-link"></i></span></li></ul>]]>
    </content>
    <id>https://heidiliu2020.github.io/shadcn-ui/</id>
    <link href="https://heidiliu2020.github.io/shadcn-ui/"/>
    <published>2024-08-02T03:13:00.000Z</published>
    <summary>
      <![CDATA[<p><img src="https://hackmd.io/_uploads/B1RYAaBtA.png" alt="cover"></p>
<blockquote>
<p>GitHub - <span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL3NoYWRjbi11aS91aQ==" title="https://github.com/shadcn-ui/ui">https://github.com/shadcn-ui/ui<i class="fa fa-external-link"></i></span></p>
</blockquote>
<h2 id="What-is-Shadcn-UI"><a href="#What-is-Shadcn-UI" class="headerlink" title="What is Shadcn UI?"></a>What is Shadcn UI?</h2><p>Shadcn UI 是基於 <span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL3RhaWx3aW5kbGFicy90YWlsd2luZGNzcw==" title="https://github.com/tailwindlabs/tailwindcss">Tailwind CSS<i class="fa fa-external-link"></i></span> 底層封裝 <span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL3JhZGl4LXVpL3ByaW1pdGl2ZXM=" title="https://github.com/radix-ui/primitives">Radix UI<i class="fa fa-external-link"></i></span> 的 React UI 元件集合，能夠支援 <span class="exturl" data-url="aHR0cHM6Ly91aS5zaGFkY24uY29tL2RvY3MvaW5zdGFsbGF0aW9uL25leHQ=" title="https://ui.shadcn.com/docs/installation/next">Next.js<i class="fa fa-external-link"></i></span>, <span class="exturl" data-url="aHR0cHM6Ly91aS5zaGFkY24uY29tL2RvY3MvaW5zdGFsbGF0aW9uL2FzdHJv" title="https://ui.shadcn.com/docs/installation/astro">Astro<i class="fa fa-external-link"></i></span>, <span class="exturl" data-url="aHR0cHM6Ly91aS5zaGFkY24uY29tL2RvY3MvaW5zdGFsbGF0aW9uL3JlbWl4" title="https://ui.shadcn.com/docs/installation/remix">Remix<i class="fa fa-external-link"></i></span>, <span class="exturl" data-url="aHR0cHM6Ly91aS5zaGFkY24uY29tL2RvY3MvaW5zdGFsbGF0aW9uL2dhdHNieQ==" title="https://ui.shadcn.com/docs/installation/gatsby">Gatsby<i class="fa fa-external-link"></i></span> 等框架。</p>]]>
    </summary>
    <title>初探 Shadcn UI：基於 Tailwind CSS + Radix UI 的元件合集</title>
    <updated>2026-04-23T06:14:18.273Z</updated>
  </entry>
  <entry>
    <author>
      <name>Heidi Liu</name>
    </author>
    <category term="技術學習" scheme="https://heidiliu2020.github.io/categories/%E6%8A%80%E8%A1%93%E5%AD%B8%E7%BF%92/"/>
    <category term="JavaScript" scheme="https://heidiliu2020.github.io/categories/%E6%8A%80%E8%A1%93%E5%AD%B8%E7%BF%92/JavaScript/"/>
    <category term="Front-End" scheme="https://heidiliu2020.github.io/tags/Front-End/"/>
    <category term="JavaScript" scheme="https://heidiliu2020.github.io/tags/JavaScript/"/>
    <content>
      <![CDATA[<p>最近又開始用 <span class="exturl" data-url="aHR0cHM6Ly93d3cuY29kZXdhcnMuY29tL2Rhc2hib2FyZA==" title="https://www.codewars.com/dashboard">Codewars<i class="fa fa-external-link"></i></span> 來練習刷題，也嘗試開一個 Repository：<span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL2hlaWRpbGl1MjAyMC9jb2Rld2Fycy1jaGFsbGVuZ2Vz" title="https://github.com/heidiliu2020/codewars-challenges">codewars-challenges<i class="fa fa-external-link"></i></span> 記錄作答結果，蠻喜歡這種能夠慢慢升級的感覺，答題結束還能參考其他人的解法，才恍然大悟原來還有更簡潔的思路。</p><p>發現自己對於一些 JavaScript 函式使用還是不太熟，把之前寫過的筆記重新複習過，本篇主要整理 JS 常見的內建函式，列表如下：</p><span id="more"></span><ul><li>Math.floor(x)：無條件捨去，回傳「小於等於」所給數字的最大整數</li><li>Bitwise NOT (~)：位元反向運算子（波浪號）</li><li>Math.ceil(x)：無條件進位，回傳「大於等於」所給數字的最小整數</li><li>Math.round(x)：四捨五入</li><li>toFixed(x)：四捨六入五留雙</li><li>Math.sqrt(x)：返回一個數的平方根</li><li>Math.abs(x)：返回一個數的絕對值</li></ul><h2 id="Math-floor-x-：無條件捨去，回傳「小於等於」所給數字的最大整數"><a href="#Math-floor-x-：無條件捨去，回傳「小於等於」所給數字的最大整數" class="headerlink" title="Math.floor(x)：無條件捨去，回傳「小於等於」所給數字的最大整數"></a><span class="exturl" data-url="aHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvemgtVFcvZG9jcy9XZWIvSmF2YVNjcmlwdC9SZWZlcmVuY2UvR2xvYmFsX09iamVjdHMvTWF0aC9mbG9vcg==" title="https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Math/floor">Math.floor(x)<i class="fa fa-external-link"></i></span>：無條件捨去，回傳「小於等於」所給數字的最大整數</h2><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="title class_">Math</span>.<span class="title function_">floor</span>(<span class="number">.95</span>);    <span class="comment">// 0</span></span><br><span class="line"><span class="title class_">Math</span>.<span class="title function_">floor</span>(<span class="number">4</span>);      <span class="comment">// 4</span></span><br><span class="line"><span class="title class_">Math</span>.<span class="title function_">floor</span>(<span class="number">7.004</span>);  <span class="comment">// 7</span></span><br><span class="line"><span class="title class_">Math</span>.<span class="title function_">floor</span>(-<span class="number">0.95</span>);  <span class="comment">// -1</span></span><br><span class="line"><span class="title class_">Math</span>.<span class="title function_">floor</span>(-<span class="number">4</span>);     <span class="comment">// -4</span></span><br><span class="line"><span class="title class_">Math</span>.<span class="title function_">floor</span>(-<span class="number">7.004</span>); <span class="comment">// -8</span></span><br></pre></td></tr></table></figure><h2 id="Bitwise-NOT-：位元反向運算子（波浪號）"><a href="#Bitwise-NOT-：位元反向運算子（波浪號）" class="headerlink" title="Bitwise NOT (~)：位元反向運算子（波浪號）"></a><span class="exturl" data-url="aHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZW4tVVMvZG9jcy9XZWIvSmF2YVNjcmlwdC9SZWZlcmVuY2UvT3BlcmF0b3JzL0JpdHdpc2VfTk9U" title="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_NOT">Bitwise NOT<i class="fa fa-external-link"></i></span> (<code>~</code>)：位元反向運算子（波浪號）</h2><ul><li>Bitwise NOT（<code>~</code>）作用是將數字 <code>x</code> 轉換為 <code>-(x + 1)</code></li><li>Double Bitwise NOT(<code>~~</code>)：雙位元元反向運算子（兩個波浪號），對正數的作用類似 <code>Math.floor(x)</code> 的無條件捨去，但對負數則結果不同</li></ul><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">~<span class="number">10</span>    <span class="comment">// -11</span></span><br><span class="line">~-<span class="number">10</span>   <span class="comment">// 9</span></span><br><span class="line">~<span class="number">10.5</span>  <span class="comment">// -11</span></span><br><span class="line">~-<span class="number">10.5</span> <span class="comment">// 9</span></span><br><span class="line"></span><br><span class="line">~~<span class="number">10</span>    <span class="comment">// 10</span></span><br><span class="line">~~-<span class="number">10</span>   <span class="comment">// -10</span></span><br><span class="line">~~<span class="number">10.5</span>  <span class="comment">// 10</span></span><br><span class="line">~~-<span class="number">10.5</span> <span class="comment">// -10</span></span><br></pre></td></tr></table></figure><h2 id="Math-ceil-x-：無條件進位，回傳「大於等於」所給數字的最小整數"><a href="#Math-ceil-x-：無條件進位，回傳「大於等於」所給數字的最小整數" class="headerlink" title="Math.ceil(x)：無條件進位，回傳「大於等於」所給數字的最小整數"></a><span class="exturl" data-url="aHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvemgtVFcvZG9jcy9XZWIvSmF2YVNjcmlwdC9SZWZlcmVuY2UvR2xvYmFsX09iamVjdHMvTWF0aC9jZWls" title="https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Math/ceil">Math.ceil(x)<i class="fa fa-external-link"></i></span>：無條件進位，回傳「大於等於」所給數字的最小整數</h2><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="title class_">Math</span>.<span class="title function_">ceil</span>(<span class="number">.95</span>);     <span class="comment">// 1</span></span><br><span class="line"><span class="title class_">Math</span>.<span class="title function_">ceil</span>(<span class="number">4</span>);       <span class="comment">// 4</span></span><br><span class="line"><span class="title class_">Math</span>.<span class="title function_">ceil</span>(<span class="number">7.004</span>);   <span class="comment">// 8</span></span><br><span class="line"><span class="title class_">Math</span>.<span class="title function_">ceil</span>(-<span class="number">0.95</span>);   <span class="comment">// -0</span></span><br><span class="line"><span class="title class_">Math</span>.<span class="title function_">ceil</span>(-<span class="number">7.004</span>);  <span class="comment">// -7</span></span><br></pre></td></tr></table></figure><h2 id="Math-round-x-：四捨五入"><a href="#Math-round-x-：四捨五入" class="headerlink" title="Math.round(x)：四捨五入"></a><span class="exturl" data-url="aHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvemgtVFcvZG9jcy9XZWIvSmF2YVNjcmlwdC9SZWZlcmVuY2UvR2xvYmFsX09iamVjdHMvTWF0aC9yb3VuZA==" title="https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Math/round">Math.round(x)<i class="fa fa-external-link"></i></span>：四捨五入</h2><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="title class_">Math</span>.<span class="title function_">round</span>(<span class="number">20.49</span>);  <span class="comment">// 20</span></span><br><span class="line"><span class="title class_">Math</span>.<span class="title function_">round</span>(<span class="number">20.5</span>);   <span class="comment">// 21</span></span><br><span class="line"><span class="title class_">Math</span>.<span class="title function_">round</span>(-<span class="number">20.5</span>);  <span class="comment">// -20</span></span><br><span class="line"><span class="title class_">Math</span>.<span class="title function_">round</span>(-<span class="number">20.51</span>); <span class="comment">// -21</span></span><br></pre></td></tr></table></figure><h2 id="toFixed-x-：四捨六入五留雙"><a href="#toFixed-x-：四捨六入五留雙" class="headerlink" title="toFixed(x)：四捨六入五留雙"></a><span class="exturl" data-url="aHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvemgtVFcvZG9jcy9XZWIvSmF2YVNjcmlwdC9SZWZlcmVuY2UvR2xvYmFsX09iamVjdHMvTnVtYmVyL3RvRml4ZWQ=" title="https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed">toFixed(x)<i class="fa fa-external-link"></i></span>：四捨六入五留雙</h2><ul><li>以字串返回，精確至「小數點後」的數字，小數部分依指定長度補充零</li></ul><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">1.23</span>.<span class="title function_">toFixed</span>(<span class="number">2</span>);    <span class="comment">// &#x27;1.23&#x27;</span></span><br><span class="line"><span class="number">1.234</span>.<span class="title function_">toFixed</span>(<span class="number">2</span>);   <span class="comment">// &#x27;1.23&#x27;</span></span><br><span class="line"><span class="number">1.235</span>.<span class="title function_">toFixed</span>(<span class="number">2</span>);   <span class="comment">// &#x27;1.24&#x27;</span></span><br><span class="line"><span class="number">1.236</span>.<span class="title function_">toFixed</span>(<span class="number">2</span>);   <span class="comment">// &#x27;1.24&#x27;</span></span><br><span class="line"><span class="number">1.244</span>.<span class="title function_">toFixed</span>(<span class="number">2</span>);   <span class="comment">// &#x27;1.24&#x27;</span></span><br><span class="line"><span class="number">1.245</span>.<span class="title function_">toFixed</span>(<span class="number">2</span>);   <span class="comment">// &#x27;1.25&#x27;</span></span><br><span class="line"><span class="number">1.246</span>.<span class="title function_">toFixed</span>(<span class="number">2</span>);   <span class="comment">// &#x27;1.25&#x27;</span></span><br><span class="line"></span><br><span class="line">(<span class="number">36</span>).<span class="title function_">toFixed</span>(<span class="number">4</span>)     <span class="comment">// &#x27;36.0000&#x27;</span></span><br></pre></td></tr></table></figure><h2 id="Math-sqrt-x-：返回一個數的平方根"><a href="#Math-sqrt-x-：返回一個數的平方根" class="headerlink" title="Math.sqrt(x)：返回一個數的平方根"></a><span class="exturl" data-url="aHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvemgtQ04vZG9jcy9XZWIvSmF2YVNjcmlwdC9SZWZlcmVuY2UvR2xvYmFsX09iamVjdHMvTWF0aC9zcXJ0" title="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Math/sqrt">Math.sqrt(x)<i class="fa fa-external-link"></i></span>：返回一個數的平方根</h2><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="title class_">Math</span>.<span class="title function_">sqrt</span>(<span class="number">2</span>);   <span class="comment">// 1.4142135623730951</span></span><br><span class="line"><span class="title class_">Math</span>.<span class="title function_">sqrt</span>(<span class="number">4</span>);   <span class="comment">// 2</span></span><br><span class="line"><span class="title class_">Math</span>.<span class="title function_">sqrt</span>(-<span class="number">2</span>);  <span class="comment">// NaN</span></span><br><span class="line"><span class="title class_">Math</span>.<span class="title function_">sqrt</span>(<span class="number">0</span>);   <span class="comment">// 0</span></span><br></pre></td></tr></table></figure><h2 id="Math-abs-x-：返回一個數的絕對值"><a href="#Math-abs-x-：返回一個數的絕對值" class="headerlink" title="Math.abs(x)：返回一個數的絕對值"></a><span class="exturl" data-url="aHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZW4tVVMvZG9jcy9XZWIvSmF2YVNjcmlwdC9SZWZlcmVuY2UvR2xvYmFsX09iamVjdHMvTWF0aC9hYnM=" title="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/abs">Math.abs(x)<i class="fa fa-external-link"></i></span>：返回一個數的絕對值</h2><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">difference</span>(<span class="params">a, b</span>) &#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="title class_">Math</span>.<span class="title function_">abs</span>(a - b);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="title function_">difference</span>(<span class="number">3</span>, <span class="number">5</span>);    <span class="comment">// 2</span></span><br><span class="line"><span class="title function_">difference</span>(<span class="number">5</span>, <span class="number">3</span>);    <span class="comment">// 2</span></span><br><span class="line"><span class="title function_">difference</span>(-<span class="number">5</span>, -<span class="number">3</span>);  <span class="comment">// 2</span></span><br></pre></td></tr></table></figure><h2 id="Reference"><a href="#Reference" class="headerlink" title="Reference"></a>Reference</h2><ul><li><span class="exturl" data-url="aHR0cHM6Ly9wamNoZW5kZXIuZGV2L2phdmFzY3JpcHQvanMtb3BlcmF0b3Iv" title="https://pjchender.dev/javascript/js-operator/">[JS] JavaScript 運算子（Operator）<i class="fa fa-external-link"></i></span></li><li><span class="exturl" data-url="aHR0cDovL3d3dy5hdWEuY29tLnR3L2Jsb2dnZXIvP1BpZD0xMTcz" title="http://www.aua.com.tw/blogger/?Pid=1173">JavaScript 四捨五入、無條件捨去、無條件進位<i class="fa fa-external-link"></i></span></li><li><span class="exturl" data-url="aHR0cHM6Ly9ibG9nLmh1bGkudHcvMjAyMi8wMy8xNC9qYXZhc2NyaXB0LW51bWJlci8=" title="https://blog.huli.tw/2022/03/14/javascript-number/">使用 JavaScript 的數字時的常見錯誤<i class="fa fa-external-link"></i></span></li></ul>]]>
    </content>
    <id>https://heidiliu2020.github.io/javascript-floating-number/</id>
    <link href="https://heidiliu2020.github.io/javascript-floating-number/"/>
    <published>2024-06-17T03:28:00.000Z</published>
    <summary>
      <![CDATA[<p>最近又開始用 <span class="exturl" data-url="aHR0cHM6Ly93d3cuY29kZXdhcnMuY29tL2Rhc2hib2FyZA==" title="https://www.codewars.com/dashboard">Codewars<i class="fa fa-external-link"></i></span> 來練習刷題，也嘗試開一個 Repository：<span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL2hlaWRpbGl1MjAyMC9jb2Rld2Fycy1jaGFsbGVuZ2Vz" title="https://github.com/heidiliu2020/codewars-challenges">codewars-challenges<i class="fa fa-external-link"></i></span> 記錄作答結果，蠻喜歡這種能夠慢慢升級的感覺，答題結束還能參考其他人的解法，才恍然大悟原來還有更簡潔的思路。</p>
<p>發現自己對於一些 JavaScript 函式使用還是不太熟，把之前寫過的筆記重新複習過，本篇主要整理 JS 常見的內建函式，列表如下：</p>]]>
    </summary>
    <title>【學習筆記】JavaScript 的浮點數計算、平方根</title>
    <updated>2026-04-23T06:14:18.240Z</updated>
  </entry>
  <entry>
    <author>
      <name>Heidi Liu</name>
    </author>
    <category term="技術學習" scheme="https://heidiliu2020.github.io/categories/%E6%8A%80%E8%A1%93%E5%AD%B8%E7%BF%92/"/>
    <category term="Front-End" scheme="https://heidiliu2020.github.io/categories/%E6%8A%80%E8%A1%93%E5%AD%B8%E7%BF%92/Front-End/"/>
    <category term="React" scheme="https://heidiliu2020.github.io/categories/%E6%8A%80%E8%A1%93%E5%AD%B8%E7%BF%92/Front-End/React/"/>
    <category term="Front-End" scheme="https://heidiliu2020.github.io/tags/Front-End/"/>
    <category term="React" scheme="https://heidiliu2020.github.io/tags/React/"/>
    <category term="Next.js" scheme="https://heidiliu2020.github.io/tags/Next-js/"/>
    <category term="i18n" scheme="https://heidiliu2020.github.io/tags/i18n/"/>
    <content>
      <![CDATA[<p><img src="https://hackmd.io/_uploads/ry0hNlrZA.png" alt="i18n"></p><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>在開發網站時，多國語系功能（i18n）是很常見的需求，能根據使用者需求切換網站顯示的語言。這陣子在 Next.js 專案中意外踩了幾個坑，寫下學習筆記作為紀錄。</p><span id="more"></span><p>本篇分為以下幾個段落：</p><ul><li>What is i18n？</li><li>如何實作<ul><li>Next.js 內建：路由層級</li><li>選擇套件：react-i18next &amp; next-i18next</li></ul></li><li>使用範例<ul><li>SSG/SSR：使用 next-i18next</li><li>SPA：使用 react-i18next</li></ul></li></ul><h2 id="What-is-i18n？"><a href="#What-is-i18n？" class="headerlink" title="What is i18n？"></a>What is i18n？</h2><p>i18n 是由  internationalization（國際化）英文縮寫而來，18 代表 i 到 n 之間的字母數量。</p><p>透過 i18n 多國語系功能，能夠讓不同語系的使用者，根據需求選定顯示語言和格式，減少在地化的時間成本，達到國際化的目的。</p><h2 id="如何實作"><a href="#如何實作" class="headerlink" title="如何實作"></a>如何實作</h2><h3 id="Next-js-內建：路由層級"><a href="#Next-js-內建：路由層級" class="headerlink" title="Next.js 內建：路由層級"></a>Next.js 內建：路由層級</h3><p>在介紹套件之前，先介紹 Next.js 官方<span class="exturl" data-url="aHR0cHM6Ly9uZXh0anMub3JnL2RvY3MvcGFnZXMvYnVpbGRpbmcteW91ci1hcHBsaWNhdGlvbi9yb3V0aW5nL2ludGVybmF0aW9uYWxpemF0aW9u" title="https://nextjs.org/docs/pages/building-your-application/routing/internationalization">內建 i18n 功能<i class="fa fa-external-link"></i></span>，透過路由層級（routing）設定不同語言轉至不同路徑，設定範例如下：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// next.config.js</span></span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = &#123;</span><br><span class="line">  <span class="attr">i18n</span>: &#123;</span><br><span class="line">    <span class="attr">locales</span>: [<span class="string">&#x27;en&#x27;</span>, <span class="string">&#x27;zh&#x27;</span>, <span class="string">&#x27;jp&#x27;</span>],</span><br><span class="line">    <span class="attr">defaultLocale</span>: <span class="string">&#x27;en&#x27;</span>,</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>根據上述配置，即可搭配 <code>next/link</code>、<code>next/router</code>，根據路由顯示對應的語言，路徑如下：</p><ul><li><code>/posts</code>：預設為 <code>en</code></li><li><code>/zh/blog</code></li><li><code>/jp/blog</code></li></ul><p>其中需注意 <span class="exturl" data-url="aHR0cHM6Ly9uZXh0anMub3JnL2RvY3MvcGFnZXMvYnVpbGRpbmcteW91ci1hcHBsaWNhdGlvbi9yb3V0aW5nL2ludGVybmF0aW9uYWxpemF0aW9u" title="https://nextjs.org/docs/pages/building-your-application/routing/internationalization">Page Router<i class="fa fa-external-link"></i></span> 和 <span class="exturl" data-url="aHR0cHM6Ly9uZXh0anMub3JnL2RvY3MvYXBwL2J1aWxkaW5nLXlvdXItYXBwbGljYXRpb24vcm91dGluZy9pbnRlcm5hdGlvbmFsaXphdGlvbg==" title="https://nextjs.org/docs/app/building-your-application/routing/internationalization">App Router<i class="fa fa-external-link"></i></span> 在路由設定上有所不同，詳細可參考官方提供的範例，以下介紹常見的 i18n 套件作為範例。</p><blockquote><p>關於 Page router 和 App router 的差別，可參考之前的筆記：<a href="https://heidiliu2020.github.io/nextjs-app-router/">【學習筆記】Next.js 路由系統：App Router vs Page Router</a></p></blockquote><h3 id="選擇套件：react-i18next-amp-next-i18next"><a href="#選擇套件：react-i18next-amp-next-i18next" class="headerlink" title="選擇套件：react-i18next &amp; next-i18next"></a>選擇套件：react-i18next &amp; next-i18next</h3><ul><li><span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL2kxOG5leHQvbmV4dC1pMThuZXh0" title="https://github.com/i18next/next-i18next">i18next/next-i18next<i class="fa fa-external-link"></i></span><ul><li>支援 Page Router </li><li>支援 SSG/SSR</li></ul></li><li><span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL2kxOG5leHQvcmVhY3QtaTE4bmV4dA==" title="https://github.com/i18next/react-i18next">i18next/react-i18next<i class="fa fa-external-link"></i></span><ul><li>支援 APP Router，因此 Next.js v13 後的版本建議搭配使用</li><li>支援 SPA</li></ul></li></ul><p>可參考上述兩種套件的 npm 下載數：</p><p><img src="https://hackmd.io/_uploads/HyMMe1Sb0.png" alt="next-i18n"></p><h2 id="使用範例"><a href="#使用範例" class="headerlink" title="使用範例"></a>使用範例</h2><h3 id="SSR：使用-next-i18next"><a href="#SSR：使用-next-i18next" class="headerlink" title="SSR：使用 next-i18next"></a>SSR：使用 <span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL2kxOG5leHQvbmV4dC1pMThuZXh0" title="https://github.com/i18next/next-i18next">next-i18next<i class="fa fa-external-link"></i></span></h3><p>(1) 首先是安裝套件：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install next-i18next --save</span><br></pre></td></tr></table></figure><p>(2) 在根目錄新增 <code>next-i18next.config.js</code> 設定檔：</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// next-i18next.config.js</span></span><br><span class="line"></span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = &#123;</span><br><span class="line">  <span class="attr">i18n</span>: &#123;</span><br><span class="line">    <span class="attr">locales</span>: [<span class="string">&#x27;en&#x27;</span>, <span class="string">&#x27;jp&#x27;</span>, <span class="string">&#x27;zh&#x27;</span>],</span><br><span class="line">    <span class="attr">defaultLocale</span>: <span class="string">&#x27;en&#x27;</span>,</span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="attr">fallbackLng</span>: &#123;</span><br><span class="line">    <span class="attr">default</span>: [<span class="string">&#x27;en&#x27;</span>],</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>(3) 在 <code>next.config.js</code> 設定檔引入使用 <code>next-i18next</code>：</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// next.config.js</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> &#123; i18n &#125; = <span class="built_in">require</span>(<span class="string">&#x27;./next-i18next.config&#x27;</span>)</span><br><span class="line"></span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = &#123;</span><br><span class="line">  i18n,</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>(4) 修改 <code>src/pages/_app.tsx</code> 檔案，以 <code>appWithTranslation</code> 這個 <span class="exturl" data-url="aHR0cHM6Ly9sZWdhY3kucmVhY3Rqcy5vcmcvZG9jcy9oaWdoZXItb3JkZXItY29tcG9uZW50cy5odG1s" title="https://legacy.reactjs.org/docs/higher-order-components.html">HOC（高階組件）<i class="fa fa-external-link"></i></span>包住整個 App：</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// src/pages/_app.tsx</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> &#123; appWithTranslation &#125; <span class="keyword">from</span> <span class="string">&#x27;next-i18next&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">MyApp</span> = (<span class="params">&#123; Component, pageProps &#125;</span>) =&gt; (</span><br><span class="line">  <span class="language-xml"><span class="tag">&lt;<span class="name">Component</span> &#123;<span class="attr">...pageProps</span>&#125; /&gt;</span></span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="title function_">appWithTranslation</span>(<span class="title class_">MyApp</span>)</span><br></pre></td></tr></table></figure><h4 id="【補充】Higher-Order-Components（HOC，高階組件）"><a href="#【補充】Higher-Order-Components（HOC，高階組件）" class="headerlink" title="【補充】Higher-Order Components（HOC，高階組件）"></a>【補充】Higher-Order Components（HOC，高階組件）</h4><ul><li>HOC 並不是 React 提供的 API，而是和 JavsScript 中的 <span class="exturl" data-url="aHR0cHM6Ly96aC53aWtpcGVkaWEub3JnL3poLXR3LyVFOSVBQiU5OCVFOSU5OCVCNiVFNSU4NyVCRCVFNiU5NSVCMA==" title="https://zh.wikipedia.org/zh-tw/%E9%AB%98%E9%98%B6%E5%87%BD%E6%95%B0">Higher Order Function（高階函式）<i class="fa fa-external-link"></i></span>類似的一個函式，高階函式可代入另一個函式作為參數，最終回傳一個函式作為結果</li><li>而 HOC 則是可代入元件（Component）作為參數，並回傳一個新的元件</li><li>目的是將共用邏輯放在 HOC 中，變動的部分由 Component 的 props 和 state 傳入</li></ul><p>(5) 在 <code>public/locales/&lt;locale&gt;/&lt;namespace&gt;.json</code> 路徑加入多國語系檔案，架構參考如下：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">.</span><br><span class="line">└── public</span><br><span class="line">    └── locales</span><br><span class="line">        ├── en</span><br><span class="line">        |   ├── posts.json</span><br><span class="line">        |   └── common.json</span><br><span class="line">        ├── jp</span><br><span class="line">        |   ├── posts.json</span><br><span class="line">        |   └── common.json</span><br><span class="line">        └── zh</span><br><span class="line">            ├── posts.json</span><br><span class="line">            └── common.json</span><br></pre></td></tr></table></figure><p>(6) 在頁面引入語系檔案：</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; serverSideTranslations &#125; <span class="keyword">from</span> <span class="string">&#x27;next-i18next/serverSideTranslations&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">async</span> <span class="keyword">function</span> <span class="title function_">getStaticProps</span>(<span class="params">&#123; locale &#125;</span>) &#123;</span><br><span class="line">  <span class="keyword">return</span> &#123;</span><br><span class="line">    <span class="attr">props</span>: &#123;</span><br><span class="line">      ...(<span class="keyword">await</span> <span class="title function_">serverSideTranslations</span>(locale, [</span><br><span class="line">        <span class="string">&#x27;common&#x27;</span>,</span><br><span class="line">        <span class="string">&#x27;posts&#x27;</span>,</span><br><span class="line">      ])),</span><br><span class="line">      <span class="comment">// Will be passed to the page component as props</span></span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>(7) 即可在頁面引入 <code>useTranslation</code> hook 使用，注意需從 <code>next-i18next</code> 引用：</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; useTranslation &#125; <span class="keyword">from</span> <span class="string">&#x27;next-i18next&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> <span class="title function_">Header</span> = (<span class="params"></span>) =&gt; &#123;</span><br><span class="line">  <span class="keyword">const</span> &#123; t &#125; = <span class="title function_">useTranslation</span>(<span class="string">&#x27;common&#x27;</span>)</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> (</span><br><span class="line">    <span class="language-xml"><span class="tag">&lt;<span class="name">div</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">      <span class="tag">&lt;<span class="name">h1</span>&gt;</span>&#123;t(&#x27;home-title&#x27;)&#125;<span class="tag">&lt;/<span class="name">h1</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">    <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span><br><span class="line">  )</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="SPA：使用-react-i18next"><a href="#SPA：使用-react-i18next" class="headerlink" title="SPA：使用 react-i18next"></a>SPA：使用 <span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL2kxOG5leHQvcmVhY3QtaTE4bmV4dA==" title="https://github.com/i18next/react-i18next">react-i18next<i class="fa fa-external-link"></i></span></h3><p>非 SSR、SSG 環境，只需直接引用 <code>react-i18next</code> 套件即可。</p><p>(1) 首先是安裝套件：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install react-i18next i18next --save</span><br></pre></td></tr></table></figure><p>(2) 建立 <code>app/i18n</code> 目錄，放置管理 i18n 的相關檔案（初始 i18n、翻譯文檔），範例如下：</p><ul><li><code>i18n/index.ts</code>：初始 i18n 相關設定</li></ul><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// i18n/index.ts</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> i18n <span class="keyword">from</span> <span class="string">&#x27;i18next&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; initReactI18next &#125; <span class="keyword">from</span> <span class="string">&#x27;react-i18next&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> resources <span class="keyword">from</span> <span class="string">&#x27;./lang-resource&#x27;</span>;</span><br><span class="line"></span><br><span class="line">i18n</span><br><span class="line">  .<span class="title function_">use</span>(initReactI18next)  <span class="comment">// 初始化設定</span></span><br><span class="line">  .<span class="title function_">init</span>(&#123;</span><br><span class="line">    resources,            <span class="comment">// 引入定義語系與對應文字的 json 檔</span></span><br><span class="line">    <span class="attr">lng</span>: <span class="string">&#x27;en&#x27;</span>,            <span class="comment">// 預設語系為 en</span></span><br><span class="line">    <span class="attr">fallbackLng</span>: <span class="string">&#x27;en&#x27;</span>,    <span class="comment">// 若找不到對應語系則回傳 en</span></span><br><span class="line">    <span class="attr">defaultNS</span>: <span class="string">&#x27;common&#x27;</span>,</span><br><span class="line">    <span class="attr">preload</span>: [<span class="string">&#x27;en&#x27;</span>, <span class="string">&#x27;ja&#x27;</span>, <span class="string">&#x27;zh&#x27;</span>],</span><br><span class="line">    <span class="attr">ns</span>: <span class="string">&#x27;common&#x27;</span>,</span><br><span class="line">    <span class="attr">interpolation</span>: &#123;</span><br><span class="line">      <span class="attr">escapeValue</span>: <span class="literal">false</span>,</span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="attr">parseMissingKeyHandler</span>: <span class="function">() =&gt;</span> &#123;</span><br><span class="line">      <span class="keyword">return</span> <span class="string">&#x27;&#x27;</span>;</span><br><span class="line">    &#125; ,</span><br><span class="line">    <span class="attr">react</span>: &#123;</span><br><span class="line">      <span class="attr">useSuspense</span>: <span class="literal">false</span></span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;);</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> i18n;</span><br></pre></td></tr></table></figure><ul><li><code>i18n/lang-resource</code>：定義語系與對應文字的 json 檔</li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">// i18n/lang-resource</span><br><span class="line"></span><br><span class="line">const resources = &#123;</span><br><span class="line">  en: &#123;</span><br><span class="line">    common: &#123;</span><br><span class="line">        &#x27;login&#x27;: &#x27;Login&#x27;,</span><br><span class="line">        &#x27;logout&#x27;: &#x27;Log out&#x27;,</span><br><span class="line">      &#125;,</span><br><span class="line">  &#125;,</span><br><span class="line">  ja: &#123;</span><br><span class="line">    common: &#123;</span><br><span class="line">        &#x27;login&#x27;: &#x27;ログイン&#x27;,</span><br><span class="line">        &#x27;logout&#x27;: &#x27;ログアウト&#x27;, </span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;,</span><br><span class="line">  zh: &#123;</span><br><span class="line">    common: &#123;</span><br><span class="line">        &#x27;login&#x27;: &#x27;登入&#x27;,</span><br><span class="line">        &#x27;logout&#x27;: &#x27;登出&#x27;, </span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">export default resources;</span><br></pre></td></tr></table></figure><p>(3) 設定完成後，即可在 <code>app/pages/layout</code> 引入 <code>i18n/index.ts</code> 使用：</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// app/pages/layout</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">&#x27;./i18n&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">RootLayout</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>以 <code>app/pages/login/page.tsx</code> 為例：</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// app/pages/login/page.tsx</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> &#123; useTranslation &#125; <span class="keyword">from</span> <span class="string">&#x27;react-i18next&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">Login</span>(<span class="params"></span>) &#123;</span><br><span class="line">      <span class="keyword">const</span> &#123; t &#125; = <span class="title function_">useTranslation</span>();</span><br><span class="line">    </span><br><span class="line">        <span class="keyword">return</span> (</span><br><span class="line">        &lt;div&gt;</span><br><span class="line">            <span class="language-xml"><span class="tag">&lt;<span class="name">p</span>&gt;</span>&#123;t(&#x27;login&#x27;)&#125;<span class="tag">&lt;/<span class="name">p</span>&gt;</span></span></span><br><span class="line">            <span class="language-xml"><span class="tag">&lt;<span class="name">p</span>&gt;</span>&#123;t(&#x27;logout&#x27;)&#125;<span class="tag">&lt;/<span class="name">p</span>&gt;</span></span></span><br><span class="line">        <span class="language-xml"><span class="tag">&lt;<span class="name">div</span>/&gt;</span></span></span><br><span class="line">    )    </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>(4) 若要切換語系，可使用 <code>i18n.changeLanguage</code> 方法：</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// app/component/header.tsx</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> &#123; useTranslation &#125; <span class="keyword">from</span> <span class="string">&#x27;react-i18next&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">function</span> <span class="title function_">Header</span>(<span class="params"></span>) &#123;</span><br><span class="line">  <span class="keyword">const</span> &#123; t, i18n &#125; = <span class="title function_">useTranslation</span>();</span><br><span class="line">    </span><br><span class="line">  <span class="keyword">const</span> <span class="title function_">handleChangeLanguage</span> = (<span class="params"></span>) =&gt; &#123;</span><br><span class="line">      i18n.<span class="title function_">changeLanguage</span>(<span class="string">&#x27;en&#x27;</span>)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="結論"><a href="#結論" class="headerlink" title="結論"></a>結論</h2><p>其實一開始研究實作 i18n 功能時，看到官方文件整個頭昏眼花，Next.js 內建的路由實作上又稍嫌複雜，React 生態不像 Angular 框架能直接引入內建功能實作，反而有很多種套件能夠選擇使用。</p><p>但問題來了，套件引入使用下來卻噴一堆 Error，才發現到由於 Next.js 路由系統架構，需搭配支援 App Router 或 Page Router 的套件使用，其實想成是在實作 React 多國語系功能就單純許多；雖然文中提及的 Page Router 還沒有機會實作，概念上還有點模糊，但還是先寫下筆記留作紀錄。</p><h2 id="參考資料"><a href="#參考資料" class="headerlink" title="參考資料"></a>參考資料</h2><ul><li><span class="exturl" data-url="aHR0cHM6Ly9qdWVqaW4uY24vcG9zdC83MjM4NjE5ODkwOTkzNzU4MjYz" title="https://juejin.cn/post/7238619890993758263">一篇文章了解如何在 Next.js 中集成 i18n 国际化（含踩坑及开发配置）<i class="fa fa-external-link"></i></span></li><li><span class="exturl" data-url="aHR0cHM6Ly9qaWFuZ3NodXV1LmNvbS9kb2NzL1JlYWN0L25leHQtaTE4bi8=" title="https://jiangshuuu.com/docs/React/next-i18n/">【Next】i18n使用 - JohnShu Blog<i class="fa fa-external-link"></i></span></li><li><span class="exturl" data-url="aHR0cHM6Ly9pdGhlbHAuaXRob21lLmNvbS50dy9hcnRpY2xlcy8xMDMyMzAxNw==" title="https://ithelp.ithome.com.tw/articles/10323017">【DAY20】React Native - 多語系切換 (react-i18next)<i class="fa fa-external-link"></i></span></li><li><span class="exturl" data-url="aHR0cHM6Ly9pdGhlbHAuaXRob21lLmNvbS50dy9hcnRpY2xlcy8xMDMwODMxNQ==" title="https://ithelp.ithome.com.tw/articles/10308315">使用 next-i18next 實作中英文多語系 - Modern Next.js Blog 系列 #28<i class="fa fa-external-link"></i></span></li></ul>]]>
    </content>
    <id>https://heidiliu2020.github.io/nextjs-i18next/</id>
    <link href="https://heidiliu2020.github.io/nextjs-i18next/"/>
    <published>2024-04-23T08:44:00.000Z</published>
    <summary>
      <![CDATA[<p><img src="https://hackmd.io/_uploads/ry0hNlrZA.png" alt="i18n"></p>
<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>在開發網站時，多國語系功能（i18n）是很常見的需求，能根據使用者需求切換網站顯示的語言。這陣子在 Next.js 專案中意外踩了幾個坑，寫下學習筆記作為紀錄。</p>]]>
    </summary>
    <title>
      <![CDATA[【學習筆記】Next.js 實現多國語系：react-i18next & next-i18next]]>
    </title>
    <updated>2026-04-23T06:14:18.251Z</updated>
  </entry>
  <entry>
    <author>
      <name>Heidi Liu</name>
    </author>
    <category term="技術學習" scheme="https://heidiliu2020.github.io/categories/%E6%8A%80%E8%A1%93%E5%AD%B8%E7%BF%92/"/>
    <category term="Front-End" scheme="https://heidiliu2020.github.io/categories/%E6%8A%80%E8%A1%93%E5%AD%B8%E7%BF%92/Front-End/"/>
    <category term="React" scheme="https://heidiliu2020.github.io/categories/%E6%8A%80%E8%A1%93%E5%AD%B8%E7%BF%92/Front-End/React/"/>
    <category term="Front-End" scheme="https://heidiliu2020.github.io/tags/Front-End/"/>
    <category term="React" scheme="https://heidiliu2020.github.io/tags/React/"/>
    <category term="Next.js" scheme="https://heidiliu2020.github.io/tags/Next-js/"/>
    <content>
      <![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>這篇主要用來記錄 Next.js 開發過程採的坑，後續也會不定期更新：</p><ul><li>問題一：如何修復 <code>&quot;Window is not defined&quot;</code> error</li><li>問題二：在 use client 頁面實作動態路由顯示 <code>missing generateStaticParams()</code> error</li></ul><span id="more"></span><h2 id="問題一：如何修復-“Window-is-not-defined”-error-in-Next-js"><a href="#問題一：如何修復-“Window-is-not-defined”-error-in-Next-js" class="headerlink" title="問題一：如何修復 “Window is not defined” error in Next.js"></a>問題一：如何修復 “Window is not defined” error in Next.js</h2><p>事情發生在開發 Next.js APP，要實作 Google 登入驗證，需要使用 <span class="exturl" data-url="aHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZW4tVVMvZG9jcy9XZWIvQVBJL1dpbmRvdw==" title="https://developer.mozilla.org/en-US/docs/Web/API/Window">Window<i class="fa fa-external-link"></i></span>、<span class="exturl" data-url="aHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZW4tVVMvZG9jcy9XZWIvQVBJL0RvY3VtZW50" title="https://developer.mozilla.org/en-US/docs/Web/API/Document">Document<i class="fa fa-external-link"></i></span> 物件時，發現程式會報出以下錯誤：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">ReferenceError: window is not defined</span><br><span class="line"></span><br><span class="line">or</span><br><span class="line"></span><br><span class="line">ReferenceError: document is not defined</span><br></pre></td></tr></table></figure><h3 id="發生原因"><a href="#發生原因" class="headerlink" title="發生原因"></a>發生原因</h3><p>這是由於 Next.js 預設為伺服器渲染（Server-side Rendering），會在 Node.js 環境下預渲染頁面，並將生成的 HTML 內容發送給 Client 端。</p><p>因此渲染過程是在 Server 端而非在瀏覽器中，由於程式無法識別 Window / Document 物件而回報上述 Error。</p><h3 id="如何解決？"><a href="#如何解決？" class="headerlink" title="如何解決？"></a>如何解決？</h3><p>解決方法可透過條件渲染（Conditional Rendering），確保 Next.js 只在 Client 端執行指定的程式碼，</p><p>可透過以下兩種方式來實現：</p><ul><li>檢查 window 是否存在</li><li>使用 useEffect hook</li></ul><h4 id="1-檢查-window-是否存在"><a href="#1-檢查-window-是否存在" class="headerlink" title="(1) 檢查 window 是否存在"></a>(1) 檢查 window 是否存在</h4><p>判斷 window 是否存在，確定在瀏覽器中才執行指定的程式碼：</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="title function_">isBrowser</span> = (<span class="params"></span>) =&gt; <span class="keyword">typeof</span> <span class="variable language_">window</span> !== <span class="string">&#x27;undefined&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (<span class="keyword">typeof</span> <span class="variable language_">window</span> !== <span class="string">&#x27;undefined&#x27;</span>) &#123;</span><br><span class="line">    <span class="comment">// Client-side-only</span></span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;window: &#x27;</span>, <span class="variable language_">window</span>);</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><h4 id="2-使用-useEffect-等-Hooks"><a href="#2-使用-useEffect-等-Hooks" class="headerlink" title="(2) 使用 useEffect 等 Hooks"></a>(2) 使用 useEffect 等 Hooks</h4><p>透過 useEffect 等方法，可確保程式碼只會在 Client 端執行：</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">&#x27;use client&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> <span class="title class_">React</span>, &#123; useEffect &#125; <span class="keyword">from</span> <span class="string">&#x27;react&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ...</span></span><br><span class="line"></span><br><span class="line"><span class="title function_">useEffect</span>(<span class="function">() =&gt;</span> &#123;</span><br><span class="line">    <span class="comment">// Client-side-only        </span></span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;window: &#x27;</span>, <span class="variable language_">window</span>);</span><br><span class="line">    </span><br><span class="line">    <span class="variable language_">window</span>.<span class="title function_">addEventListener</span>(<span class="string">&#x27;scroll&#x27;</span>, <span class="function">(<span class="params">e</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;srcoll: &#x27;</span>, e)</span><br><span class="line">  &#125;)</span><br><span class="line">&#125;,[])</span><br><span class="line"></span><br></pre></td></tr></table></figure><h3 id="參考資料"><a href="#參考資料" class="headerlink" title="參考資料"></a>參考資料</h3><ul><li><span class="exturl" data-url="aHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvNTUxNTEwNDEvd2luZG93LWlzLW5vdC1kZWZpbmVkLWluLW5leHQtanMtcmVhY3QtYXBw" title="https://stackoverflow.com/questions/55151041/window-is-not-defined-in-next-js-react-app">Window is not defined in Next.js React app<i class="fa fa-external-link"></i></span></li><li><span class="exturl" data-url="aHR0cHM6Ly9qdWVqaW4uY24vcy9yZWFjdCUyMHdpbmRvdyUyMGlzJTIwbm90JTIwZGVmaW5lZCUyMG5leHQlMjBqcw==" title="https://juejin.cn/s/react%20window%20is%20not%20defined%20next%20js">如何修复Next.js中的 “window is not defined”？ - 掘金<i class="fa fa-external-link"></i></span></li></ul><h2 id="問題二：在-use-client-頁面實作動態路由顯示-missing-generateStaticParams-error"><a href="#問題二：在-use-client-頁面實作動態路由顯示-missing-generateStaticParams-error" class="headerlink" title="問題二：在 use client 頁面實作動態路由顯示 missing generateStaticParams() error"></a>問題二：在 use client 頁面實作動態路由顯示 <code>missing generateStaticParams()</code> error</h2><p>事情發生在開發 Next.js APP，實作 dynamic routing 時（如：<code>server/[evo]/page.tsx</code>），會顯示以下錯誤：</p><p><img src="https://hackmd.io/_uploads/HJ8MieYbA.png" alt="error"></p><p>上述錯誤訊息中的<code>&quot;output: export&quot;</code>，是 Next.js 提供支援 <span class="exturl" data-url="aHR0cHM6Ly9uZXh0anMub3JnL2RvY3MvYXBwL2J1aWxkaW5nLXlvdXItYXBwbGljYXRpb24vZGVwbG95aW5nL3N0YXRpYy1leHBvcnRz" title="https://nextjs.org/docs/app/building-your-application/deploying/static-exports">Static Exports（靜態導出）<i class="fa fa-external-link"></i></span>，透過在設定檔 <code>next.config.js</code> 加上參數：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/** <span class="doctag">@type</span> &#123;<span class="type">import(&#x27;next&#x27;).NextConfig</span>&#125; */</span></span><br><span class="line"><span class="keyword">const</span> nextConfig = &#123;</span><br><span class="line">  <span class="attr">output</span>: <span class="string">&#x27;export&#x27;</span>, <span class="comment">// Outputs a Single-Page Application (SPA)</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> nextConfig</span><br></pre></td></tr></table></figure><p>如此一來，即可在 <code>next build</code> 建置時實現 SPA （single-page application），將會在 <code>out</code> 資料夾底下生成靜態檔案，將每個路由分解為單獨的 HTML 檔案，避免在 Client 端載入不必要的 JS 程式碼，減少 bundle 大小以提高頁面效能。</p><h3 id="發生原因-1"><a href="#發生原因-1" class="headerlink" title="發生原因"></a>發生原因</h3><p>但在 Next.js 若想要實現 Dynamic Routes（動態路由），必須加上 <code>generateStaticParams()</code> 方法，這段 function 只能在 Server Component 執行，在 Client Component 並不支援。</p><p><span class="exturl" data-url="aHR0cHM6Ly9uZXh0anMub3JnL2RvY3MvYXBwL2J1aWxkaW5nLXlvdXItYXBwbGljYXRpb24vZGVwbG95aW5nL3N0YXRpYy1leHBvcnRzI3Vuc3VwcG9ydGVkLWZlYXR1cmVz" title="https://nextjs.org/docs/app/building-your-application/deploying/static-exports#unsupported-features">官方文件（Deploying: Static Exports | Next.js）<i class="fa fa-external-link"></i></span> 也提到 App Routing 若想要 Dynamic Routes 必須搭配「只能在 SSR 運行的 <code>generateStaticParams()</code>」：</p><p><img src="https://hackmd.io/_uploads/S1xrigt-A.png" alt="unsupported"></p><h3 id="如何解決？-1"><a href="#如何解決？-1" class="headerlink" title="如何解決？"></a>如何解決？</h3><p>如果想要輸出 SPA，又希望能在 <code>&#39;use client&#39;</code> 情境中實現動態路由，則需要將元件拆成兩個部分實作：</p><ul><li>在 Server Component 引入 <code>generateStaticParams()</code> 方法，以實現動態路由</li><li>接著引入 Client Component，即可使用 Hooks</li></ul><p>以下是範例程式碼，詳細可參考這篇文章<span class="exturl" data-url="aHR0cHM6Ly9tZWRpdW0uY29tL0BnaXZ2ZW1lZWUvdXNhZ2Utb2YtZ2VuZXJhdGVzdGF0aWNwYXJhbXMtd2l0aC11c2UtY2xpZW50LWEwNTljMjNmNzMxNg==" title="https://medium.com/@givvemeee/usage-of-generatestaticparams-with-use-client-a059c23f7316">《usage of generateStaticParams with use client | by Vivi - Medium》<i class="fa fa-external-link"></i></span></p><ul><li><code>server/[evo]/page.tsx</code>：在 Server Component 引入 <code>generateStaticParams()</code> 方法，以實現動態路由</li></ul><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// server/[evo]/page.tsx</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="title class_">EvoPage</span>, &#123; <span class="title class_">Props</span> &#125; <span class="keyword">from</span> <span class="string">&quot;.&quot;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">function</span> <span class="title function_">generateStaticParams</span>(<span class="params"></span>) &#123;</span><br><span class="line">  <span class="keyword">return</span> [</span><br><span class="line">    &#123; <span class="attr">evo</span>: <span class="string">&#x27;test&#x27;</span> &#125;,</span><br><span class="line">    &#123; <span class="attr">evo</span>: <span class="string">&#x27;stage&#x27;</span> &#125;,</span><br><span class="line">    &#123; <span class="attr">evo</span>: <span class="string">&#x27;public&#x27;</span> &#125;</span><br><span class="line">  ];</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">ServerEvoPage</span>(<span class="params">&#123; params &#125;: <span class="title class_">Props</span></span>) &#123;</span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;[ServerEvoPage] params&#x27;</span>, params)</span><br><span class="line">  <span class="keyword">return</span> <span class="language-xml"><span class="tag">&lt;<span class="name">EvoPage</span> <span class="attr">params</span>=<span class="string">&#123;&#123;</span> <span class="attr">evo:</span> <span class="attr">params.evo</span> &#125;&#125;/&gt;</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><code>server/[evo]/index.tsx</code>：在上述 Server Component 引入 Client Component，即可使用 Hooks：</li></ul><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// server/[evo]/index.tsx</span></span><br><span class="line"></span><br><span class="line"><span class="string">&#x27;use client&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> <span class="title class_">React</span> <span class="keyword">from</span> <span class="string">&#x27;react&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">type</span> <span class="title class_">Props</span> = &#123;</span><br><span class="line">  <span class="attr">params</span>: &#123; <span class="attr">evo</span>: <span class="built_in">string</span>&#125;;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">EvoPage</span>(<span class="params">&#123; params &#125;: <span class="title class_">Props</span></span>) &#123;</span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;[EvoPage] params: &#x27;</span>, params);</span><br><span class="line">  <span class="keyword">const</span> [data, setData] = <span class="title class_">React</span>.<span class="title function_">useState</span>(<span class="string">&#x27;&#x27;</span>);</span><br><span class="line">    </span><br><span class="line">  <span class="keyword">return</span> (</span><br><span class="line">    <span class="language-xml"><span class="tag">&lt;<span class="name">div</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">        This is Client Component.</span></span><br><span class="line"><span class="language-xml">    <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span><br><span class="line">  );</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="參考資料-1"><a href="#參考資料-1" class="headerlink" title="參考資料"></a>參考資料</h3><ul><li><span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL3ZlcmNlbC9uZXh0LmpzL2lzc3Vlcy80NjczNQ==" title="https://github.com/vercel/next.js/issues/46735">[NEXT-1049] use client with generateStaticParams will opt out of static generation #46735<i class="fa fa-external-link"></i></span></li><li><span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL3ZlcmNlbC9uZXh0LmpzL2lzc3Vlcy80ODAyMg==" title="https://github.com/vercel/next.js/issues/48022">[NEXT-1030] output: ‘export’ with use client in dynamic routes doesn’t work #48022<i class="fa fa-external-link"></i></span></li><li><span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL3ZlcmNlbC9uZXh0LmpzL2lzc3Vlcy81NDM5Mw==" title="https://github.com/vercel/next.js/issues/54393">App Router with output: export does not support useParams() on client #54393<i class="fa fa-external-link"></i></span></li><li><span class="exturl" data-url="aHR0cHM6Ly93d3cueXVhbnN1ZG9uZy5uZXQvZG9jdW1lbnQvTmV4dEpzLzM1LiVFOSU5RCU5OSVFNiU4MCU4MSVFNSVBRiVCQyVFNSU4NyVCQS5odG1s" title="https://www.yuansudong.net/document/NextJs/35.%E9%9D%99%E6%80%81%E5%AF%BC%E5%87%BA.html">NextJs-静态导出<i class="fa fa-external-link"></i></span></li><li><span class="exturl" data-url="aHR0cHM6Ly9pdGhlbHAuaXRob21lLmNvbS50dy9hcnRpY2xlcy8xMDMyMjI2MQ==" title="https://ithelp.ithome.com.tw/articles/10322261">Day 19 - Next.js 13 App Router 動態路由 Dynamic Routes &amp; getStaticParams()<i class="fa fa-external-link"></i></span></li></ul>]]>
    </content>
    <id>https://heidiliu2020.github.io/nextjs-error-fix/</id>
    <link href="https://heidiliu2020.github.io/nextjs-error-fix/"/>
    <published>2024-03-18T09:47:01.000Z</published>
    <summary>
      <![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>這篇主要用來記錄 Next.js 開發過程採的坑，後續也會不定期更新：</p>
<ul>
<li>問題一：如何修復 <code>&quot;Window is not defined&quot;</code> error</li>
<li>問題二：在 use client 頁面實作動態路由顯示 <code>missing generateStaticParams()</code> error</li>
</ul>]]>
    </summary>
    <title>
      <![CDATA[【學習筆記】Next.js 錯誤修復紀錄： 「Window is not defined」、「use client & missing generateStaticParams() error」]]>
    </title>
    <updated>2026-04-23T06:14:18.250Z</updated>
  </entry>
  <entry>
    <author>
      <name>Heidi Liu</name>
    </author>
    <category term="技術學習" scheme="https://heidiliu2020.github.io/categories/%E6%8A%80%E8%A1%93%E5%AD%B8%E7%BF%92/"/>
    <category term="Front-End" scheme="https://heidiliu2020.github.io/categories/%E6%8A%80%E8%A1%93%E5%AD%B8%E7%BF%92/Front-End/"/>
    <category term="React" scheme="https://heidiliu2020.github.io/categories/%E6%8A%80%E8%A1%93%E5%AD%B8%E7%BF%92/Front-End/React/"/>
    <category term="Front-End" scheme="https://heidiliu2020.github.io/tags/Front-End/"/>
    <category term="React" scheme="https://heidiliu2020.github.io/tags/React/"/>
    <category term="Next.js" scheme="https://heidiliu2020.github.io/tags/Next-js/"/>
    <content>
      <![CDATA[<p><img src="https://hackmd.io/_uploads/SkK9xkWqp.png" alt="Parallel Routes"></p><blockquote><p>Ref: <span class="exturl" data-url="aHR0cHM6Ly9uZXh0anMub3JnL2RvY3MvYXBwL2J1aWxkaW5nLXlvdXItYXBwbGljYXRpb24vcm91dGluZy9wYXJhbGxlbC1yb3V0ZXMjc3RyZWFtaW5n" title="https://nextjs.org/docs/app/building-your-application/routing/parallel-routes#streaming">Next.js - Parallel Routes<i class="fa fa-external-link"></i></span></p></blockquote><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>接續上篇文章 <a href="https://heidiliu2020.github.io/next-react-ssr/">【學習筆記】談談 Next.js：基於 React 的 SSR 框架</a>，初步瞭解 Next.js 這套框架的特性以及網頁渲染方式，本篇將實際建立 Next.js 專案，以及介紹 Page Router 與 APP Router 兩種路由系統的差異。</p><span id="more"></span><h2 id="Getting-Started"><a href="#Getting-Started" class="headerlink" title="Getting Started"></a>Getting Started</h2><p>詳細步驟可參考<span class="exturl" data-url="aHR0cHM6Ly9uZXh0anMub3JnL2RvY3MvZ2V0dGluZy1zdGFydGVkL2luc3RhbGxhdGlvbg==" title="https://nextjs.org/docs/getting-started/installation">官方文件<i class="fa fa-external-link"></i></span>，注意目前版本的 Next.js v14 需要安裝 <span class="exturl" data-url="aHR0cHM6Ly9ub2RlanMub3JnLw==" title="https://nodejs.org/">Node.js v18.17<i class="fa fa-external-link"></i></span> 或以上版本才支援。</p><h3 id="nvm：Node-Version-Manager"><a href="#nvm：Node-Version-Manager" class="headerlink" title="nvm：Node Version Manager"></a>nvm：Node Version Manager</h3><p>若已經安裝過 Node.js 卻顯示版本不符，可透過 <span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL252bS1zaC9udm0=" title="https://github.com/nvm-sh/nvm">nvm<i class="fa fa-external-link"></i></span> 這項 Node.js 版本管理工具，在不同專案中切換 Node.js 版本。</p><p>首先在終端機輸入下方指令安裝 nvm：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash</span><br></pre></td></tr></table></figure><p>接著透過下方指令，即可安裝與套用指定版本的 Node.js：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">$ nvm install v18.17.0</span><br><span class="line">// 安裝指定版本 Node.js</span><br><span class="line"> </span><br><span class="line">$ nvm use v18.17.0</span><br><span class="line">// 套用指定版本的 Node.js</span><br></pre></td></tr></table></figure><p>也可透過以下指定確認目前版本，以及已安裝的版本：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">$ node -v</span><br><span class="line">// 確認目前 Node.js 版本</span><br><span class="line"></span><br><span class="line">$ nvm ls</span><br><span class="line">// 列出所有本機端已安裝的 Node.js 版本</span><br><span class="line"></span><br><span class="line">$ nvm ls-remote</span><br><span class="line">// 列出目前遠端可使用的 Node.js 版本</span><br></pre></td></tr></table></figure><h3 id="專案建置"><a href="#專案建置" class="headerlink" title="專案建置"></a>專案建置</h3><p>依照下方指令建立 next 專案，在後方加上 <code>--ts</code> 或 <code>--typescript</code> 即可支援 TypeScript 語法：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ npx create-next-app</span><br><span class="line">or</span><br><span class="line">$ npx create-next-app --ts</span><br></pre></td></tr></table></figure><p>接著會依序出現下列幾個提問，分別如下：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">What is your project named? my-app</span><br><span class="line">// 專案名稱，格式需為英文小寫</span><br><span class="line"></span><br><span class="line">Would you like to use TypeScript? No / Yes</span><br><span class="line">// 是否支援 TypeScript</span><br><span class="line"></span><br><span class="line">Would you like to use ESLint? No / Yes</span><br><span class="line">// 是否使用 ESLint（用來規範 Coding Style 的套件）</span><br><span class="line"></span><br><span class="line">Would you like to use Tailwind CSS? No / Yes</span><br><span class="line">// 是否使用 Tailwind CSS</span><br><span class="line"></span><br><span class="line">Would you like to use `src/` directory? No / Yes</span><br><span class="line">// 是否在 /app 外加一層 src 資料夾</span><br><span class="line"></span><br><span class="line">Would you like to use App Router? (recommended) No / Yes</span><br><span class="line">// 是否使用 App Router </span><br><span class="line"></span><br><span class="line">Would you like to customize the default import alias (@/*)? No / Yes</span><br><span class="line">// 是否自訂 alias 調整預設的 baseURL 匯入路徑</span><br><span class="line"></span><br><span class="line">What import alias would you like configured? @/*</span><br><span class="line">// alias 預設使用 @ 是否修改</span><br></pre></td></tr></table></figure><p>建置完成後，初始專案架構主要分成以下三大類：</p><ul><li>app：放置 components、pages 與 api 等檔案<ul><li>layout.tsx：在多個頁面之間定義共用 UI，其狀態將會被保存，如：nav、header、footer 等元件</li><li>page.tsx：在資料夾底下需包含 <code>page.tsx</code> 檔案，才會被定義為一個 route segment，如：<code>app/blog/page.tsx</code></li><li>globals.css：定義全域樣式</li></ul></li><li>public：放置靜態檔案，如圖片等<ul><li>需要引入 <code>/public/next.svg</code> 檔案時，路徑可直接指向 <code>/next.svg</code></li></ul></li><li>設定檔：包含 <code>next.config.js</code>、<code>tsconfig.json</code>、<code>package.json</code> 等用於設定專案配置的檔案</li></ul><p>接著輸入 <code>cd my-app</code> 指令移動到專案根目錄，再以 <code>npm run dev</code> 指令運行開發伺服器，進入 <code>http://localhost:3000/</code> 即可看到初始頁面如下，也就是 <code>app/page.tsx</code> 的內容：</p><p><img src="https://hackmd.io/_uploads/ryQN1aXtT.png" alt="image"></p><h2 id="Router-System-路由系統"><a href="#Router-System-路由系統" class="headerlink" title="Router System 路由系統"></a>Router System 路由系統</h2><p>過去我們在 React 專案中，曾使用 <code>react-router-dom</code> 這項套件來實現路由功能，詳細可參考這篇筆記：<a href="https://heidiliu2020.github.io/react-router/">[week 22] React：用 SPA 架構實作一個部落格（一）- Router</a>。</p><p>由於 Next.js 使用基於檔案系統的路由（file-system based router），會依照專案的檔案結構自動定義路由。</p><p>而根據版本不同，Next.js 提供兩種管理頁面路由的方式，分別是舊版本適用的 Pages Router 以及 v13 後推出的 App Router，兩者差異在於：</p><ul><li>Pages Router<ul><li>定義頁面層級的路由</li><li>所有元件為 React Client Component（客戶端元件）</li><li>只能使用 Next.js 提供的預設規則，如：檔案名稱即為路徑</li></ul></li><li>App Router<ul><li>定義應用程式層級的路由</li><li>所有元件預設為 React Server Component（伺服器端元件）</li><li>可自定義路由規則，如：使用正則表達式匹配特定路徑</li></ul></li></ul><p>如上所言，在 App Router 中所有元件預設為 React Server Component（RSC），意思是由伺服器將 React Component 準備好，再傳給 Client 顯示在畫面上。</p><p>而 RSC 的優缺點如下：</p><ul><li>優點<ul><li>整合後端操作，如存取資料庫（DB）、讀取檔案（File System）</li><li>降低資料間的依賴關係，改善請求瀑布流（Waterfall）導致的效能問題</li><li>降低 JS Bundle Size 以提升頁面效能</li></ul></li><li>缺點<ul><li>無法使用 React Hooks</li><li>無法使用瀏覽器 API</li><li>無法操作 DOM 事件監聽</li></ul></li></ul><p>面對上述缺點，Next.js 可依照使用情境不同，將元件定義為 Server Component 或 Client Component。舉例來說，當某個元件需要使用 Hooks 管理時，可透過在程式碼開頭加上 <code>&#39;use client&#39;</code> 來標示元件類型，該元件底下的子元件也會自動視為 Client Component。</p><p>但也因為如此，相較於 Page Router，新版的 App Router 學習曲線會較高，需瞭解 Server 如何運作，以及判斷哪些元件適合放在 Server 或 Client 端，在使用時須特別注意。</p><p>介紹完 Page Router 和 App Router 之間的差異，接著談談兩者專案架構，以及對應路由的方式。</p><h3 id="Page-Router：基於檔案的路由系統"><a href="#Page-Router：基於檔案的路由系統" class="headerlink" title="Page Router：基於檔案的路由系統"></a>Page Router：基於檔案的路由系統</h3><p>檔案架構示意：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">└── pages</span><br><span class="line">    ├── index.tsx</span><br><span class="line">    ├── login.tsx</span><br><span class="line">    ├── api</span><br><span class="line">    │   └── user.tsx</span><br><span class="line">    ├── posts</span><br><span class="line">    │   └── [id].tsx</span><br><span class="line">    └── blog</span><br><span class="line">        ├── index.tsx</span><br><span class="line">        └── setting.tsx</span><br></pre></td></tr></table></figure><p>檔案與對應的頁面路由如下：</p><ul><li><code>pages/index.tsx</code> → <code>/</code></li><li><code>pages/blog/index.tsx</code> → <code>/blog</code></li><li><code>pages/blog/setting.tsx</code> → <code>/blog/setting</code></li><li><code>pages/posts/[id].tsx</code>  → <code>/posts/[id]</code><ul><li>檔名可作為動態路由的參數，透過 <span class="exturl" data-url="aHR0cHM6Ly9uZXh0anMub3JnL2RvY3MvcGFnZXMvYXBpLXJlZmVyZW5jZS9mdW5jdGlvbnMvdXNlLXJvdXRlcg==" title="https://nextjs.org/docs/pages/api-reference/functions/use-router">useRoute<i class="fa fa-external-link"></i></span> 這個 Hook 取得 route 相關資訊</li></ul></li><li>呼叫 API：<code>page/api/user.tsx</code></li></ul><p>可參考官方部落格的這篇文章<span class="exturl" data-url="aHR0cHM6Ly9uZXh0anMub3JnL2Jsb2cvbGF5b3V0cy1yZmM=" title="https://nextjs.org/blog/layouts-rfc">《Layouts RFC》<i class="fa fa-external-link"></i></span>，包含以下檔案對應頁面路由的示意圖：</p><p><img src="https://hackmd.io/_uploads/B1-kgTiKp.png" alt="image"></p><h3 id="App-Router：基於目錄的路由系統"><a href="#App-Router：基於目錄的路由系統" class="headerlink" title="App Router：基於目錄的路由系統"></a>App Router：基於目錄的路由系統</h3><p>目錄架構示意：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">└── app</span><br><span class="line">    ├── blog</span><br><span class="line">    │   └── [slug]</span><br><span class="line">    │        └── page.tsx</span><br><span class="line">    ├── login</span><br><span class="line">    │   └── page.tsx</span><br><span class="line">    ├── @analytics</span><br><span class="line">    │   ├── page.tsx</span><br><span class="line">    │   ├── error.tsx</span><br><span class="line">    │   └── loading.tsx</span><br><span class="line">    ├── api</span><br><span class="line">    │   └── user</span><br><span class="line">    │       ├── index.ts</span><br><span class="line">    │       └── route.ts  </span><br><span class="line">    ├── components</span><br><span class="line">    │   ├── loading.tsx</span><br><span class="line">    │   └── button.tsx    </span><br><span class="line">    ├── globals.css</span><br><span class="line">    ├── layout.tsx</span><br><span class="line">    └── page.tsx</span><br></pre></td></tr></table></figure><p>文件目錄與對應的頁面路由如下：</p><ul><li><code>app/page.tsx</code> → <code>/</code></li><li><code>app/login/page.tsx</code> → <code>/login</code></li><li><code>app/blog/[slug]/page.tsx</code> → <code>/blog/[slug]</code><ul><li>目錄可作為動態路由的參數，並以 props 傳入元件</li></ul></li><li>呼叫 API：<code>app/api/user/route.tsx</code></li><li><code>@</code> 開頭的 folder 不會對路由造成影響：<ul><li><code>app/@analytics/page.tsx</code> 實際渲染的路由為 /，這個特殊的檔案夾是用來切分同一個路由底下的不同邏輯區塊。</li></ul></li></ul><p>可參考<span class="exturl" data-url="aHR0cHM6Ly9uZXh0anMub3JnL2RvY3MvYXBwL2J1aWxkaW5nLXlvdXItYXBwbGljYXRpb24vcm91dGluZw==" title="https://nextjs.org/docs/app/building-your-application/routing">官方文件<i class="fa fa-external-link"></i></span>，以下是目錄對應頁面路由的示意圖：</p><p><img src="https://hackmd.io/_uploads/SyBL03sK6.png" alt="image"></p><h2 id="File-Convention-檔案規則"><a href="#File-Convention-檔案規則" class="headerlink" title="File Convention 檔案規則"></a>File Convention 檔案規則</h2><p>詳細的路由架構，可參考<span class="exturl" data-url="aHR0cHM6Ly9uZXh0anMub3JnL2RvY3MvYXBwL2J1aWxkaW5nLXlvdXItYXBwbGljYXRpb24vcm91dGluZw==" title="https://nextjs.org/docs/app/building-your-application/routing">官方文件<i class="fa fa-external-link"></i></span>，也可以直接在頁面左上方切換查看 App Router 或 Page Router。</p><p>此外，Next.js 有設定<span class="exturl" data-url="aHR0cHM6Ly9uZXh0anMub3JnL2RvY3MvYXBwL2J1aWxkaW5nLXlvdXItYXBwbGljYXRpb24vcm91dGluZyNmaWxlLWNvbnZlbnRpb25z" title="https://nextjs.org/docs/app/building-your-application/routing#file-conventions">保留字<i class="fa fa-external-link"></i></span>給特殊檔案，以建立具有特定行為的 UI，以下副檔名為 <code>.js</code>、<code>.jsx</code>、<code>.tsx</code> 可視專案而定：</p><ul><li><span class="exturl" data-url="aHR0cHM6Ly9uZXh0anMub3JnL2RvY3MvYXBwL2FwaS1yZWZlcmVuY2UvZmlsZS1jb252ZW50aW9ucy9sYXlvdXQ=" title="https://nextjs.org/docs/app/api-reference/file-conventions/layout">layout.js<i class="fa fa-external-link"></i></span>：定義共用 UI 元件</li><li><span class="exturl" data-url="aHR0cHM6Ly9uZXh0anMub3JnL2RvY3MvYXBwL2FwaS1yZWZlcmVuY2UvZmlsZS1jb252ZW50aW9ucy90ZW1wbGF0ZQ==" title="https://nextjs.org/docs/app/api-reference/file-conventions/template">template.js<i class="fa fa-external-link"></i></span>：類似 layout，處理需要重新渲染的 Layout UI</li><li><span class="exturl" data-url="aHR0cHM6Ly9uZXh0anMub3JnL2RvY3MvYXBwL2FwaS1yZWZlcmVuY2UvZmlsZS1jb252ZW50aW9ucy9wYWdl" title="https://nextjs.org/docs/app/api-reference/file-conventions/page">page.js<i class="fa fa-external-link"></i></span>：建立路由的主要 UI，並使路徑可公開存取</li><li><span class="exturl" data-url="aHR0cHM6Ly9uZXh0anMub3JnL2RvY3MvYXBwL2FwaS1yZWZlcmVuY2UvZmlsZS1jb252ZW50aW9ucy9yb3V0ZQ==" title="https://nextjs.org/docs/app/api-reference/file-conventions/route">route.js<i class="fa fa-external-link"></i></span>：建立伺服器端 API 端點</li><li><span class="exturl" data-url="aHR0cHM6Ly9uZXh0anMub3JnL2RvY3MvYXBwL2FwaS1yZWZlcmVuY2UvZmlsZS1jb252ZW50aW9ucy9sb2FkaW5n" title="https://nextjs.org/docs/app/api-reference/file-conventions/loading">loading.js<i class="fa fa-external-link"></i></span>：在載入時顯示載入中 UI</li><li><span class="exturl" data-url="aHR0cHM6Ly9uZXh0anMub3JnL2RvY3MvYXBwL2FwaS1yZWZlcmVuY2UvZmlsZS1jb252ZW50aW9ucy9ub3QtZm91bmQ=" title="https://nextjs.org/docs/app/api-reference/file-conventions/not-found">not-found.js<i class="fa fa-external-link"></i></span>：處理 notFound error（HTTP 404）或任何未知路徑錯誤</li><li><span class="exturl" data-url="aHR0cHM6Ly9uZXh0anMub3JnL2RvY3MvYXBwL2FwaS1yZWZlcmVuY2UvZmlsZS1jb252ZW50aW9ucy9lcnJvcg==" title="https://nextjs.org/docs/app/api-reference/file-conventions/error">error.js<i class="fa fa-external-link"></i></span>：顯示錯誤 UI，必須為 Client Components</li><li><span class="exturl" data-url="aHR0cHM6Ly9uZXh0anMub3JnL2RvY3MvYXBwL2J1aWxkaW5nLXlvdXItYXBwbGljYXRpb24vcm91dGluZy9lcnJvci1oYW5kbGluZw==" title="https://nextjs.org/docs/app/building-your-application/routing/error-handling">global-error.js<i class="fa fa-external-link"></i></span>：全域錯誤 UI</li><li><span class="exturl" data-url="aHR0cHM6Ly9uZXh0anMub3JnL2RvY3MvYXBwL2FwaS1yZWZlcmVuY2UvZmlsZS1jb252ZW50aW9ucy9kZWZhdWx0" title="https://nextjs.org/docs/app/api-reference/file-conventions/default">default.js<i class="fa fa-external-link"></i></span>：處理 <span class="exturl" data-url="aHR0cHM6Ly9uZXh0anMub3JnL2RvY3MvYXBwL2J1aWxkaW5nLXlvdXItYXBwbGljYXRpb24vcm91dGluZy9wYXJhbGxlbC1yb3V0ZXM=" title="https://nextjs.org/docs/app/building-your-application/routing/parallel-routes">Parallel Routes（平行路由）<i class="fa fa-external-link"></i></span> 遇到渲染問題時，用來替代顯示的 UI（fallback UI）</li></ul><p>以下是官方文件提供的路由範例架構，可以看到父層和子層均有 layout、error 以及 loading 元件，用來處理各自的邏輯：</p><p><img src="https://hackmd.io/_uploads/BJGCACec6.png" alt="router image"></p><h2 id="結語"><a href="#結語" class="headerlink" title="結語"></a>結語</h2><p>在查路由相關的資料時，會發現因為 App Router 是 Next.js v13 後才推出的路由系統，架構上和 Page Router 不相容，規則也有所差異，因此特別列出來進行比較，實際開發時使用預設的 App Router 即可。</p><p>接下來預計會實作一個的部落格，希望包含簡易的登入機制、API 串接、顯示文章列表等功能。</p><h2 id="參考資料"><a href="#參考資料" class="headerlink" title="參考資料"></a>參考資料</h2><ul><li>Next.js 官方文件：<span class="exturl" data-url="aHR0cHM6Ly9uZXh0anMub3JnL2RvY3MvYXBwL2J1aWxkaW5nLXlvdXItYXBwbGljYXRpb24=" title="https://nextjs.org/docs/app/building-your-application">App Router<i class="fa fa-external-link"></i></span> &amp; <span class="exturl" data-url="aHR0cHM6Ly9uZXh0anMub3JnL2RvY3MvcGFnZXM=" title="https://nextjs.org/docs/pages">Page Router<i class="fa fa-external-link"></i></span></li><li><span class="exturl" data-url="aHR0cHM6Ly9ibG9nLnR5cGVhcnQuY2MvbmV4dGpzMTMtcXVpY2stZ3VpZGUv" title="https://blog.typeart.cc/nextjs13-quick-guide/">快速入門 Next.Js 13 App Router, RSC(React Server Component), SEO相關說明<i class="fa fa-external-link"></i></span></li><li><span class="exturl" data-url="aHR0cHM6Ly9pdGhlbHAuaXRob21lLmNvbS50dy9hcnRpY2xlcy8xMDMzNDQyNQ==" title="https://ithelp.ithome.com.tw/articles/10334425">Day 19 - Parallel Routes 路由的平行宇宙 - iT 邦幫忙<i class="fa fa-external-link"></i></span></li></ul>]]>
    </content>
    <id>https://heidiliu2020.github.io/nextjs-app-router/</id>
    <link href="https://heidiliu2020.github.io/nextjs-app-router/"/>
    <published>2024-01-26T07:25:01.000Z</published>
    <summary>
      <![CDATA[<p><img src="https://hackmd.io/_uploads/SkK9xkWqp.png" alt="Parallel Routes"></p>
<blockquote>
<p>Ref: <span class="exturl" data-url="aHR0cHM6Ly9uZXh0anMub3JnL2RvY3MvYXBwL2J1aWxkaW5nLXlvdXItYXBwbGljYXRpb24vcm91dGluZy9wYXJhbGxlbC1yb3V0ZXMjc3RyZWFtaW5n" title="https://nextjs.org/docs/app/building-your-application/routing/parallel-routes#streaming">Next.js - Parallel Routes<i class="fa fa-external-link"></i></span></p>
</blockquote>
<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>接續上篇文章 <a href="https://heidiliu2020.github.io/next-react-ssr/">【學習筆記】談談 Next.js：基於 React 的 SSR 框架</a>，初步瞭解 Next.js 這套框架的特性以及網頁渲染方式，本篇將實際建立 Next.js 專案，以及介紹 Page Router 與 APP Router 兩種路由系統的差異。</p>]]>
    </summary>
    <title>【學習筆記】Next.js 路由系統：App Router vs Page Router</title>
    <updated>2026-04-23T06:14:18.250Z</updated>
  </entry>
  <entry>
    <author>
      <name>Heidi Liu</name>
    </author>
    <category term="技術學習" scheme="https://heidiliu2020.github.io/categories/%E6%8A%80%E8%A1%93%E5%AD%B8%E7%BF%92/"/>
    <category term="Front-End" scheme="https://heidiliu2020.github.io/categories/%E6%8A%80%E8%A1%93%E5%AD%B8%E7%BF%92/Front-End/"/>
    <category term="React" scheme="https://heidiliu2020.github.io/categories/%E6%8A%80%E8%A1%93%E5%AD%B8%E7%BF%92/Front-End/React/"/>
    <category term="Front-End" scheme="https://heidiliu2020.github.io/tags/Front-End/"/>
    <category term="React" scheme="https://heidiliu2020.github.io/tags/React/"/>
    <category term="Next.js" scheme="https://heidiliu2020.github.io/tags/Next-js/"/>
    <content>
      <![CDATA[<p><img src="https://hackmd.io/_uploads/SkvRg5cd6.png" alt="image"></p><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>在認識 Next.js 之前，建議先複習 React 的相關知識，可參考過去的<a href="https://heidiliu2020.github.io/react-jsx-props/">《[week 21] 初探 React：Component、JSX 語法、props 與 state 的不同》</a>系列筆記。</p><p>距離上次碰 React 感覺已經是好久以前的事情了，趁著這次組內專案需求，預計導入 Next.js 框架技術，寫下這篇學習筆記做記錄。</p><span id="more"></span><h2 id="初探-Next-js"><a href="#初探-Next-js" class="headerlink" title="初探 Next.js"></a>初探 Next.js</h2><h3 id="React-vs-Next-js：函式庫與框架"><a href="#React-vs-Next-js：函式庫與框架" class="headerlink" title="React vs Next.js：函式庫與框架"></a>React vs Next.js：函式庫與框架</h3><p>眾所周知，React 其實並不是框架（Framework），而是用來建構使用者介面的 JavaScript 函式庫（Library）。</p><p>而 Next.js 則是建立 React 之上的框架，除了客戶端渲染（Client-side Rendering），還支援兩種形式的預渲染：靜態網頁生成（Static Site Generation）和伺服器端渲染（Server-side Rendering）。</p><p>可參考官網的標語介紹：</p><ul><li><span class="exturl" data-url="aHR0cHM6Ly9yZWFjdC5kZXYv" title="https://react.dev/">React<i class="fa fa-external-link"></i></span>：用於打造使用者介面的函式庫（The <strong>library</strong> for web and native user interfaces）<ul><li>此外官方也提到：「建議使用者使用全端 React 框架，如 Next.js 或 <span class="exturl" data-url="aHR0cHM6Ly9yZW1peC5ydW4v" title="https://remix.run/">Remix<i class="fa fa-external-link"></i></span>，以處理專案中的路由與資料處理等需求」</li></ul></li><li><span class="exturl" data-url="aHR0cHM6Ly9uZXh0anMub3JnLw==" title="https://nextjs.org/">Next.js<i class="fa fa-external-link"></i></span>：用於網頁開發的 React 框架（The React <strong>Framework</strong> for the Web）</li></ul><h2 id="為何選擇-Next-js？"><a href="#為何選擇-Next-js？" class="headerlink" title="為何選擇 Next.js？"></a>為何選擇 Next.js？</h2><p>在使用任何新技術之前，都要先問為什麼會做出這個選擇，是否有解決實作上面臨的問題 <del>（畢竟面試也很常會被問到）</del>，先瞭解前因後果，也有助於掌握各種工具的使用時機。</p><p>這裡我們可以從 React 提供的功能作為切入點，React 是由 <span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL2ZhY2Vib29rL3JlYWN0" title="https://github.com/facebook/react">Facebook<i class="fa fa-external-link"></i></span> 團隊所維護的 JavaScript 函式庫。</p><p>Angular、React、Vue，也就是俗稱的前端三大「框架」，常用於快速開發單頁式應用程式（Single Page Application，SPA），可達到前後端分離，以及提升使用者體驗等目的，例如 Gmail、Facebook 和 Netflix 等平台。</p><p>然而，由於 SPA 是利用客戶端渲染（CSR）技術，由 JavaScript 動態產生內容，若檢視原始碼會發現無法看到新增的內容，這將導致 SEO（搜尋引擎最佳化）較差的問題；也因為初次載入頁面需下載大量的 JavaScript 檔案，使第一次渲染較為費時，反而降低使用者體驗。</p><p>那該怎麼辦呢？</p><p>只要第一個畫面改由伺服器端渲染（SSR），即可解決 SEO 問題，其他畫面仍維持使用者端渲染（CSR），減少伺服器運算負荷，如此不就能夠兩者兼得了嗎？</p><p>React 可透過「搭建後端 Server + 處理 Hydration + Webpack 打包配置」等設定來實現 SSR，但相對來說門檻較高，需耗費較多成本學習。</p><p>於是前端 SSR 框架就誕生了，如 React 的 <span class="exturl" data-url="aHR0cHM6Ly9uZXh0anMub3JnLw==" title="https://nextjs.org/">Next.js<i class="fa fa-external-link"></i></span> 和 <span class="exturl" data-url="aHR0cHM6Ly93d3cuZ2F0c2J5anMuY29tLw==" title="https://www.gatsbyjs.com/">Gatsby.js<i class="fa fa-external-link"></i></span>，以及 Vue 的 <span class="exturl" data-url="aHR0cHM6Ly9udXh0LmNvbS8=" title="https://nuxt.com/">Nuxt.js<i class="fa fa-external-link"></i></span>。</p><p>【延伸閱讀】關於 MVC、SPA 到 SSR 詳細演進，可閱讀 Huli 寫的這兩篇文章：<span class="exturl" data-url="aHR0cHM6Ly9saWZlLmh1bGkudHcvMjAxOC8wNS8wNC9pbnRyb2R1Y3Rpb24tbXZjLXNwYS1hbmQtc3NyLTU0NWM5NDE2NjllOS8=" title="https://life.huli.tw/2018/05/04/introduction-mvc-spa-and-ssr-545c941669e9/">《跟著小明一起搞懂技術名詞：MVC、SPA 與SSR》<i class="fa fa-external-link"></i></span>以及<span class="exturl" data-url="aHR0cHM6Ly9ibG9nLmh1bGkudHcvMjAyMy8xMS8yNy9zZXJ2ZXItc2lkZS1yZW5kZXJpbmctc3NyLWFuZC1pc29tb3JwaGljLw==" title="https://blog.huli.tw/2023/11/27/server-side-rendering-ssr-and-isomorphic/">《從歷史的角度探討多種 SSR（Server-side rendering）》<i class="fa fa-external-link"></i></span>，前者明明是兩年前才讀過的文章，現在又有一層新的體悟。</p><h3 id="Next-js-特別在哪？"><a href="#Next-js-特別在哪？" class="headerlink" title="Next.js 特別在哪？"></a>Next.js 特別在哪？</h3><p><img src="https://hackmd.io/_uploads/r1anuqcu6.png" alt="image"></p><p>由 <span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL3ZlcmNlbC9uZXh0Lmpz" title="https://github.com/vercel/next.js">Vercel<i class="fa fa-external-link"></i></span> 團隊創建的 Next.js，解決了上述幾點網頁開發遇到的問題，以下是官網提及有關 Next.js v14（發布於 2023 年 12 月）的幾項特點：</p><ul><li>Data Fetching：可控制資料載入的時機點</li><li>CSS Support：內建支援 CSS、Sass 檔案，並支援 CSS 模組化</li><li>Route Handlers：基於檔案架構的路由系統，如 <code>page/home.tsx</code></li><li>API Routes：支援 API 路由，易於建立與管理 API 端點</li><li>Pre-rendering：支援兩種形式的預渲染，分別是靜態網頁生成（SSG）和伺服器渲染（SSR）</li><li>Built-in Optimizations：針對圖片、字體、JavaScript 載入進行自動優化，包括延遲載入與緩存處理</li></ul><p>綜合上述優點，Next.js 有助於優化網頁效能與 SEO，適合用於建立著陸頁面（Landing Page）或是產品展示頁面，但較不適合應用在經常變動的網站，避免伺服器負荷過大。</p><p>而 Next.js 實際使用案例可參考 <span class="exturl" data-url="aHR0cHM6Ly9uZXh0anMub3JnL3Nob3djYXNl" title="https://nextjs.org/showcase">Showcase<i class="fa fa-external-link"></i></span> 頁面，如 Notion、Tik Tok 以及 Twitch 等網站。</p><h2 id="比較：CSR-vs-SSR-vs-SSG-vs-ISR"><a href="#比較：CSR-vs-SSR-vs-SSG-vs-ISR" class="headerlink" title="比較：CSR vs SSR vs SSG vs ISR"></a>比較：CSR vs SSR vs SSG vs ISR</h2><p><img src="https://hackmd.io/_uploads/Sk0QYQzYa.png" alt="image"></p><blockquote><p>Ref: <span class="exturl" data-url="aHR0cHM6Ly9kZXYudG8vZ3V5ZHVtYWlzL25leHQtanMtdGhlLXVsdGltYXRlLWNoZWF0LXNoZWV0LXRvLXBhZ2UtcmVuZGVyaW5nLTU1ZWI=" title="https://dev.to/guydumais/next-js-the-ultimate-cheat-sheet-to-page-rendering-55eb">Next.js: The Ultimate Cheat Sheet To Page Rendering<i class="fa fa-external-link"></i></span></p></blockquote><p>最後針對 Next.js 支援的網頁渲染方式，以及如何在 Next.js 搭配使用，列點整理如下：</p><ul><li>Client-side Rendering（CSR）：客戶端渲染<ul><li>Client（瀏覽器）第一次發送 Request 給 Server =&gt; Server 回傳只有容器不含內容的 HTML 檔 =&gt; 再由瀏覽器執行 JavaScript 動態產生資料 =&gt; 最後將資料渲染到畫面上</li><li>因此第一次渲染較為費時，而只有容器沒有內容的原始碼也將不利於 SEO</li><li>執行函數：<code>useEffect()</code> 或使用由 Next.js 團隊開發的 <span class="exturl" data-url="aHR0cHM6Ly9zd3IudmVyY2VsLmFwcC8=" title="https://swr.vercel.app/">SWR<i class="fa fa-external-link"></i></span> 開源<br>套件</li></ul></li><li>Server-side Rendering（SSR）：伺服器端渲染<ul><li>Server（伺服器）在每次收到 Request 時，會建立好完整的 HTML 內容並發送給 Client</li><li>適用於需經常更新數據的頁面，缺點是可能導致伺服器負荷較大</li><li>執行函數：<code>getServerSideProps()</code>，只會在 Server side 執行，可跳過呼叫 API 步驟直接到資料庫抓取資料</li></ul></li><li>Static Site Generation（SSG）：靜態網站生成<ul><li>在網頁打包（built time）時，就由 Server 產生所有需要用到的內容，因此每次 Server 收到 Request 均回傳相同的 HTML 給 Client</li><li>因 HTML 內容不變可搭配快取機制，適用於資料變動較小的頁面，無法動態更新</li><li>執行函數：<code>getStaticProps()</code></li></ul></li><li>Incremental Site Rendering（ISR）：增量式網站渲染<ul><li>結合 SSG 和 SSR 的渲染方式，可設定條件保存上一次渲染結果，當靜態檔案過期，將觸發 Server 重新 build HTML 檔案以更新頁面</li><li>執行函數: <code>getStaticProps()</code> 搭配 <code>revalidate</code> 屬性</li></ul></li></ul><p>也可參考 web.dev 這篇文章<span class="exturl" data-url="aHR0cHM6Ly93ZWIuZGV2L2FydGljbGVzL3JlbmRlcmluZy1vbi10aGUtd2ViP2hsPXpoLXR3" title="https://web.dev/articles/rendering-on-the-web?hl=zh-tw">《Rendering on the Web》<i class="fa fa-external-link"></i></span> 提供的表格整理：</p><p><img src="https://hackmd.io/_uploads/SyQLDXGKp.png" alt="image"></p><h2 id="結語"><a href="#結語" class="headerlink" title="結語"></a>結語</h2><p>在實際瞭解 Next.js 這套框架之前，在社群或其他平台略有耳聞，透過這次學習，除了把 React 的基礎，也把過去常被問到的 MVC、SPA、CSR、SSR 等專有名詞再重新複習一遍。</p><p>不管選擇什麼技術或框架，重要的是為何要使用，又想解決什麼問題，不外乎結果都是為了提升使用者體驗，畢竟科技始終源自於人性。這篇只提及基本觀念，再來就是實際動手操作啦！</p><h2 id="參考資料"><a href="#參考資料" class="headerlink" title="參考資料"></a>參考資料</h2><ul><li><span class="exturl" data-url="aHR0cHM6Ly9pdGhlbHAuaXRob21lLmNvbS50dy9hcnRpY2xlcy8xMDIyNDc3Mg==" title="https://ithelp.ithome.com.tw/articles/10224772">18. [FE] 為什麼網站要做成SPA？SSR 的優點是什麼？<i class="fa fa-external-link"></i></span></li><li><span class="exturl" data-url="aHR0cHM6Ly9pdGhlbHAuaXRob21lLmNvbS50dy91c2Vycy8yMDE2MTg1My9pcm9ubWFuLzYxMjI=" title="https://ithelp.ithome.com.tw/users/20161853/ironman/6122">深入淺出，完整認識Next.js 13 - iT 邦幫忙<i class="fa fa-external-link"></i></span></li><li><span class="exturl" data-url="aHR0cHM6Ly9oYWNrbWQuaW8vQHBlcmljb2RlL0h5UkpIdkFzag==" title="https://hackmd.io/@pericode/HyRJHvAsj">快速了解Next.js中的網頁渲染技術重點: SSR、SSG、ISR與CSR<i class="fa fa-external-link"></i></span></li></ul>]]>
    </content>
    <id>https://heidiliu2020.github.io/next-react-ssr/</id>
    <link href="https://heidiliu2020.github.io/next-react-ssr/"/>
    <published>2024-01-16T03:50:01.000Z</published>
    <summary>
      <![CDATA[<p><img src="https://hackmd.io/_uploads/SkvRg5cd6.png" alt="image"></p>
<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>在認識 Next.js 之前，建議先複習 React 的相關知識，可參考過去的<a href="https://heidiliu2020.github.io/react-jsx-props/">《[week 21] 初探 React：Component、JSX 語法、props 與 state 的不同》</a>系列筆記。</p>
<p>距離上次碰 React 感覺已經是好久以前的事情了，趁著這次組內專案需求，預計導入 Next.js 框架技術，寫下這篇學習筆記做記錄。</p>]]>
    </summary>
    <title>【學習筆記】談談 Next.js：基於 React 的 SSR 框架</title>
    <updated>2026-04-23T06:14:18.249Z</updated>
  </entry>
  <entry>
    <author>
      <name>Heidi Liu</name>
    </author>
    <category term="技術學習" scheme="https://heidiliu2020.github.io/categories/%E6%8A%80%E8%A1%93%E5%AD%B8%E7%BF%92/"/>
    <category term="Google" scheme="https://heidiliu2020.github.io/categories/%E6%8A%80%E8%A1%93%E5%AD%B8%E7%BF%92/Google/"/>
    <category term="Advertising" scheme="https://heidiliu2020.github.io/tags/Advertising/"/>
    <category term="Google" scheme="https://heidiliu2020.github.io/tags/Google/"/>
    <category term="Translation" scheme="https://heidiliu2020.github.io/tags/Translation/"/>
    <content>
      <![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>在上一篇文章  <a href="https://heidiliu2020.github.io/google-advertising-service/">Google 廣告｜比較 Google Ads、AdSense、AdMob 和 Ad Manager 的區別</a> 中，介紹幾種與 Google 廣告相關的服務。</p><p>接下來，將會介紹如何使用 Google AdSense，從申請帳號資格到實際設定廣告投放。</p><h2 id="如何使用-Google-AdSense"><a href="#如何使用-Google-AdSense" class="headerlink" title="如何使用 Google AdSense"></a>如何使用 Google AdSense</h2><p>以下是官方 AdSense 頻道的影片介紹：</p><iframe width="694" height="520" src="https://www.youtube.com/embed/YSqOvgfbPhI" title="Welcome to Google AdSense" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe><span id="more"></span><h3 id="申請資格"><a href="#申請資格" class="headerlink" title="申請資格"></a>申請資格</h3><p>若想參加 AdSense 計畫，需要註冊 AdSense 帳戶，在這之前，必須確認是否符合 <span class="exturl" data-url="aHR0cHM6Ly9zdXBwb3J0Lmdvb2dsZS5jb20vYWRzZW5zZS9hbnN3ZXIvOTcyND9obD16aC1IYW50JnJlZl90b3BpYz0xMzE5NzU2JnNqaWQ9MjYyNzk1MjA4NTc4NTczMTQ1OC1BUA==" title="https://support.google.com/adsense/answer/9724?hl=zh-Hant&ref_topic=1319756&sjid=2627952085785731458-AP">AdSense 資格規定<i class="fa fa-external-link"></i></span>：</p><ul><li>網站提供獨創內容</li><li>內容需符合<span class="exturl" data-url="aHR0cHM6Ly9zdXBwb3J0Lmdvb2dsZS5jb20vYWRzZW5zZS9hbnN3ZXIvNDgxODI=" title="https://support.google.com/adsense/answer/48182">計畫政策<i class="fa fa-external-link"></i></span>規定</li><li>需為年滿 18 歲成年人，若未滿 18 歲則可由父母或監護人代為註冊</li></ul><h3 id="申請流程"><a href="#申請流程" class="headerlink" title="申請流程"></a>申請流程</h3><ul><li>Step1. 進入 <span class="exturl" data-url="aHR0cHM6Ly93d3cuZ29vZ2xlLmNvbS50dy9hZHNlbnNlL3N0YXJ0Lw==" title="https://www.google.com.tw/adsense/start/">Google AdSense<i class="fa fa-external-link"></i></span> 官網，點擊開始使用</li></ul><p><img src="https://hackmd.io/_uploads/S15Wc6Jfa.png"></p><ul><li>Step2. 填寫網站、收款國家等資訊<ul><li><span class="exturl" data-url="aHR0cHM6Ly9zdXBwb3J0Lmdvb2dsZS5jb20vYWRzZW5zZS9hbnN3ZXIvMTIxNzA0MjE/aGw9emgtSGFudCZzamlkPTEzNTA5Njk4NjQ0NzU0ODc5MzkzLUFQ" title="https://support.google.com/adsense/answer/12170421?hl=zh-Hant&sjid=13509698644754879393-AP">AdSense 的網站管理異動 2023/2/20<i class="fa fa-external-link"></i></span>：需注意輸入的網站網址，限制「必須是不含路徑、參數、片段或通訊埠號碼的標準網域」，因此不支援路徑（path）或子網域（subdomain），詳細可<span class="exturl" data-url="aHR0cHM6Ly9zdXBwb3J0Lmdvb2dsZS5jb20vYWRzZW5zZS9hbnN3ZXIvMjc4NDQzOD9obD16aC1IYW50JnNqaWQ9MTM1MDk2OTg2NDQ3NTQ4NzkzOTMtQVAjemlwcHk9JTJDJUU3JTg0JUExJUU2JTk1JTg4JUU3JUI2JUIyJUU1JTlEJTgwJUU3JUE0JUJBJUU0JUJFJThCJTJDJUU2JTlDJTg5JUU2JTk1JTg4JUU3JUI2JUIyJUU1JTlEJTgwJUU3JUE0JUJBJUU0JUJFJThC" title="https://support.google.com/adsense/answer/2784438?hl=zh-Hant&sjid=13509698644754879393-AP#zippy=%2C%E7%84%A1%E6%95%88%E7%B6%B2%E5%9D%80%E7%A4%BA%E4%BE%8B%2C%E6%9C%89%E6%95%88%E7%B6%B2%E5%9D%80%E7%A4%BA%E4%BE%8B">參考官方文件<i class="fa fa-external-link"></i></span><ul><li>有效範例：<code>example.com</code>、<code>example.blogspot.com</code></li><li>無效範例：<code>example.com/page.html</code>、<code>example.com/directory</code>、<code>subdomain.example.com</code></li></ul></li></ul></li></ul><p><img src="https://hackmd.io/_uploads/rJ_X5akG6.png"></p><ul><li>Step3. 點選最左側的「填寫付款資訊」</li></ul><p><img src="https://hackmd.io/_uploads/H1tLH6eza.png"></p><ul><li>Step4. 接著點選最右側的「將網站連結到 Adsense」會出現以下頁面，可選擇驗證方法並更新至自己的網站上，方法共有三種：<ul><li>(1) 在網頁的 HTML <code>&lt;head&gt;&lt;/head&gt;</code> 區塊內嵌 AdSense 程式碼片段</li><li>(2) 新增 Ads.txt 檔案並貼上相關程式碼片段</li><li>(3) 在網頁的 HTML <code>&lt;head&gt;&lt;/head&gt;</code> 區塊之間插入 <code>&lt;meta&gt;</code> 中繼標記</li></ul></li></ul><p><img src="https://hackmd.io/_uploads/B1cwHagMp.png"></p><ul><li>Step5. 完成後打勾並點選驗證，約需經過 2 至 4 週的時間審查</li></ul><h2 id="廣告類型與設定"><a href="#廣告類型與設定" class="headerlink" title="廣告類型與設定"></a>廣告類型與設定</h2><p>在啟用 AdSense 帳戶後，即可在網站上設定廣告，廣告類型可分為「<span class="exturl" data-url="aHR0cHM6Ly9zdXBwb3J0Lmdvb2dsZS5jb20vYWRzZW5zZS9hbnN3ZXIvNzAzNzYyND9obD16aC1IYW50JnJlZl90b3BpYz0xMjUwMTAyJnNqaWQ9MjYyNzk1MjA4NTc4NTczMTQ1OC1BUCNhdXRvX2Fkcw==" title="https://support.google.com/adsense/answer/7037624?hl=zh-Hant&ref_topic=1250102&sjid=2627952085785731458-AP#auto_ads">自動廣告<i class="fa fa-external-link"></i></span>」和「<span class="exturl" data-url="aHR0cHM6Ly9zdXBwb3J0Lmdvb2dsZS5jb20vYWRzZW5zZS9hbnN3ZXIvNzAzNzYyND9obD16aC1IYW50JnJlZl90b3BpYz0xMjUwMTAyJnNqaWQ9MjYyNzk1MjA4NTc4NTczMTQ1OC1BUCNhZF91bml0cw==" title="https://support.google.com/adsense/answer/7037624?hl=zh-Hant&ref_topic=1250102&sjid=2627952085785731458-AP#ad_units">廣告單元<i class="fa fa-external-link"></i></span>」兩種方式。</p><h3 id="自動廣告-Auto-ads"><a href="#自動廣告-Auto-ads" class="headerlink" title="自動廣告 Auto ads"></a>自動廣告 Auto ads</h3><p>在網站導入自動廣告（Auto ads）時，只需將程式碼放到想要顯示廣告的頁面中，系統就會自動掃描網站，並根據版面配置與內容，找出位置自動刊登廣告。</p><p><img src="https://hackmd.io/_uploads/rkmKS6eMa.png"></p><p>點擊上圖頁面中的「取得程式碼」，並將程式碼放到網頁的 <strong><code>&lt;head&gt;&lt;/head&gt;</code></strong> 標籤中即可。</p><p><img src="https://hackmd.io/_uploads/Skd5STgMT.png"></p><p>程式碼示意如下：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">&lt;html&gt;</span><br><span class="line">&lt;head&gt;</span><br><span class="line">// ...</span><br><span class="line">&lt;script async src=&quot;https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-XXXXXXXXXXXXX&quot;</span><br><span class="line">     crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;</span><br><span class="line">&lt;/head&gt;</span><br><span class="line"></span><br><span class="line">&lt;body&gt;</span><br><span class="line">// ...</span><br><span class="line">&lt;/body&gt;</span><br><span class="line">&lt;/html&gt;</span><br></pre></td></tr></table></figure><p>點選右下角的「鉛筆符號」進入自動廣告的設定頁面，在這裡可以預覽選擇的廣告格式，以及希望排除的區域或頁面等：</p><p><img src="https://hackmd.io/_uploads/rkagLTxG6.png"></p><h3 id="廣告單元-Ads-units"><a href="#廣告單元-Ads-units" class="headerlink" title="廣告單元 Ads units"></a>廣告單元 Ads units</h3><p>另一種<span class="exturl" data-url="aHR0cHM6Ly9zdXBwb3J0Lmdvb2dsZS5jb20vYWRzZW5zZS9hbnN3ZXIvOTE4MzU0OT9obD16aC1IYW50JnNqaWQ9OTU0NTM5Mjk1NTMwMjU1MjU5MS1BUCZ2aXNpdF9pZD02MzgzMDU5NzYzNDEzMzczMDAtMzM5NDg5NzkxMCZyZWZfdG9waWM9OTE4MzI0MiZyZD0x" title="https://support.google.com/adsense/answer/9183549?hl=zh-Hant&sjid=9545392955302552591-AP&visit_id=638305976341337300-3394897910&ref_topic=9183242&rd=1">廣告單元<i class="fa fa-external-link"></i></span>（Ad units），是指透過 AdSense 廣告程式碼，根據設定的類別與樣式，顯示一至多個 Google 廣告。</p><p>可在 AdSense 帳戶的「按廣告單元」頁面建立、自訂及管理廣告單元；不同於自動廣告，根據廣告單元的程式碼，能夠控制廣告的顯示位置，而原生廣告單元則在自訂方面更提供更大的彈性。</p><p><img src="https://hackmd.io/_uploads/BJRWLpezT.png"></p><p>廣告單元類型分成以下幾種，可根據不同需求選用：</p><ul><li><span class="exturl" data-url="aHR0cHM6Ly9zdXBwb3J0Lmdvb2dsZS5jb20vYWRzZW5zZS90b3BpYy85MTgzMzYw" title="https://support.google.com/adsense/topic/9183360">多媒體廣告<i class="fa fa-external-link"></i></span><ul><li>適用於任何情境</li><li>預設為回應式廣告，因此會隨網頁版面和使用者裝置自動調整大小</li><li>可自訂為只顯示大小固定的廣告</li><li>與 <span class="exturl" data-url="aHR0cHM6Ly9zdXBwb3J0Lmdvb2dsZS5jb20vYWRzZW5zZS9hbnN3ZXIvOTE4NzIzOQ==" title="https://support.google.com/adsense/answer/9187239">AMP 網頁<i class="fa fa-external-link"></i></span>相容</li></ul></li><li><span class="exturl" data-url="aHR0cHM6Ly9zdXBwb3J0Lmdvb2dsZS5jb20vYWRzZW5zZS90b3BpYy85MTgzNTgy" title="https://support.google.com/adsense/topic/9183582">動態內廣告<i class="fa fa-external-link"></i></span><ul><li>在動態饋給 (例如文章或產品的清單) 中放送與內容自然呼應的原生廣告，提供良好使用者體驗</li></ul></li><li><span class="exturl" data-url="aHR0cHM6Ly9zdXBwb3J0Lmdvb2dsZS5jb20vYWRzZW5zZS90b3BpYy85MTgzNTk0" title="https://support.google.com/adsense/topic/9183594">文章內廣告<i class="fa fa-external-link"></i></span><ul><li>讓廣告自然融入網頁段落的原生廣告，可有效提升閱讀體驗</li></ul></li><li><span class="exturl" data-url="aHR0cHM6Ly9zdXBwb3J0Lmdvb2dsZS5jb20vYWRzZW5zZS90b3BpYy85MTg1MDUz" title="https://support.google.com/adsense/topic/9185053">Multiplex 廣告<i class="fa fa-external-link"></i></span><ul><li>用來顯示內容建議式原生廣告的格狀廣告單元</li></ul></li><li><span class="exturl" data-url="aHR0cHM6Ly9zdXBwb3J0Lmdvb2dsZS5jb20vYWRzZW5zZS90b3BpYy8xMDAzMzE5Ng==" title="https://support.google.com/adsense/topic/10033196">搜尋引擎<i class="fa fa-external-link"></i></span><ul><li>由 Google 技術提供的搜尋引擎，可在搜尋結果中顯示廣告</li></ul></li></ul><p>點選想要建立的廣告單元會進入設定頁面，需輸入名稱以及選擇廣告大小：</p><p><img src="https://hackmd.io/_uploads/H1lQUaxGp.png"></p><p>建立完成廣告單元後，將廣告單元程式碼放到網頁的 <strong><code>&lt;body&gt;&lt;/body&gt;</code></strong> 標籤中，希望顯示廣告的區塊即可：</p><p><img src="https://hackmd.io/_uploads/HyTXLTlMT.png"></p><p>根據<span class="exturl" data-url="aHR0cHM6Ly9zdXBwb3J0Lmdvb2dsZS5jb20vYWRzZW5zZS9hbnN3ZXIvOTE5MDAyOD9obD16aC1IYW50" title="https://support.google.com/adsense/answer/9190028?hl=zh-Hant">在 HTML 中安插廣告程式碼的位置（廣告單元）<i class="fa fa-external-link"></i></span>，可透過程式碼來調整廣告單元顯示的位置與樣式：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">&lt;body&gt;</span><br><span class="line">&lt;!-- Ad units container --&gt;</span><br><span class="line">&lt;div align=&quot;center&quot;&gt;</span><br><span class="line"></span><br><span class="line">&lt;script async src=&quot;https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-XXXXXXXXXXXXX&quot;</span><br><span class="line">     crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;</span><br><span class="line">&lt;!-- Responsive_ads --&gt;</span><br><span class="line">&lt;ins class=&quot;adsbygoogle&quot;</span><br><span class="line">     style=&quot;display:block&quot;</span><br><span class="line">     data-ad-client=&quot;ca-pub-4274798044223706&quot;</span><br><span class="line">     data-ad-slot=&quot;3472959732&quot;</span><br><span class="line">     data-ad-format=&quot;auto&quot;</span><br><span class="line">     data-full-width-responsive=&quot;true&quot;&gt;&lt;/ins&gt;</span><br><span class="line">&lt;script&gt;</span><br><span class="line">     (adsbygoogle = window.adsbygoogle || []).push(&#123;&#125;);</span><br><span class="line">&lt;/script&gt;</span><br><span class="line"></span><br><span class="line">&lt;/div&gt;</span><br><span class="line">  </span><br><span class="line">  // ...</span><br><span class="line"></span><br><span class="line">&lt;/body&gt;</span><br></pre></td></tr></table></figure><h3 id="在本地端測試-AdSense"><a href="#在本地端測試-AdSense" class="headerlink" title="在本地端測試 AdSense"></a>在本地端測試 AdSense</h3><p>此外，若想要在本地端（Localhost）測試廣告，可加上 <code>data-ad-test=&quot;on&quot;</code> 屬性：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">&lt;script async src=&quot;https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-XXXXXXXXXXXXX&quot;</span><br><span class="line">     crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;</span><br><span class="line">&lt;!-- ad_0113 --&gt;</span><br><span class="line">&lt;ins class=&quot;adsbygoogle&quot;</span><br><span class="line">     style=&quot;display:block&quot;</span><br><span class="line">     data-ad-client=&quot;ca-pub-4274798044223706&quot;</span><br><span class="line">     data-ad-slot=&quot;7260167051&quot;</span><br><span class="line">     data-ad-format=&quot;auto&quot;</span><br><span class="line">     data-ad-test=&quot;on&quot;</span><br><span class="line">     data-full-width-responsive=&quot;true&quot;&gt;&lt;/ins&gt;</span><br><span class="line">&lt;script&gt;</span><br><span class="line">     (adsbygoogle = window.adsbygoogle || []).push(&#123;&#125;);</span><br><span class="line">&lt;/script&gt;</span><br></pre></td></tr></table></figure><h3 id="設定-Domain-name-對應-IP"><a href="#設定-Domain-name-對應-IP" class="headerlink" title="設定 Domain name 對應 IP"></a>設定 Domain name 對應 IP</h3><p>接下來，透過調整設定檔 <code>\etc\hosts</code>，手動設定網址（Domain name）對應的 IP 位置，即可方便在本地端進行測試。以下是 Mac 修改 hosts 的方式：</p><ul><li>Step1. 開啟資量夾，點選上方「前往」&gt;「前往資料夾」</li><li>Step2. 輸入 <code>/private/etc/hosts</code> 並點擊前往</li><li>Step3. 由於無法直接進行編輯，可透過 sudo 指令來變更權限，或是把檔案拉到桌面，編輯完成再拉回 <code>/private/etc</code> 取代原本的檔案</li><li>Step4. 輸入 <code>IP domain_name</code>，範例如下所示：</li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">##</span></span><br><span class="line"><span class="comment"># Host Database</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment"># localhost is used to configure the loopback interface</span></span><br><span class="line"><span class="comment"># when the system is booting.  Do not change this entry.</span></span><br><span class="line"><span class="comment">##</span></span><br><span class="line">127.0.0.1local.ugamenow.com</span><br><span class="line">127.0.0.1localhost</span><br><span class="line"></span><br><span class="line">127.0.0.1 heidiliu2020.github.io</span><br></pre></td></tr></table></figure><blockquote><p>有關 Mac 設定詳細可參考：<span class="exturl" data-url="aHR0cHM6Ly9tcm1hZC5jb20udHcvbWFjLW9zLXgtaG9zdHM=" title="https://mrmad.com.tw/mac-os-x-hosts">[教學]Mac OS X也能編輯與修改Hosts檔案方法<i class="fa fa-external-link"></i></span><br>Windows 和 Linux 如何進行設定，則可參考：<span class="exturl" data-url="aHR0cHM6Ly9ibG9nLm1pbmlhc3AuY29tL3Bvc3QvMjAwOC8wOC8yNS9Nb2RpZnktaG9zdHMtZmlsZS10by1jaGFuZ2UtRG9tYWluLU5hbWUtSVAtTWFwcGluZw==" title="https://blog.miniasp.com/post/2008/08/25/Modify-hosts-file-to-change-Domain-Name-IP-Mapping">手動設定網址對應 IP 的方式 ( 主機 IP 域名對應檔 hosts )<i class="fa fa-external-link"></i></span></p></blockquote><h2 id="結語"><a href="#結語" class="headerlink" title="結語"></a>結語</h2><p>透過這篇文章，認識到如何申請 Google AdSense 帳號，以及提供刊登的廣告類型，設定上算是較容易上手。此外，也能夠先透過修改 <code>\etc\hosts</code> 檔案，在本地端進行測試，瞭解廣告實際在頁面中呈現的樣式並即時修正。</p><h2 id="參考資料"><a href="#參考資料" class="headerlink" title="參考資料"></a>參考資料</h2><ul><li><span class="exturl" data-url="aHR0cHM6Ly9zdXBwb3J0Lmdvb2dsZS5jb20vYWRzZW5zZS9hbnN3ZXIvMTcwNTgyMj9obD1lbiZyZWZfdG9waWM9MTcwNjAwNCZzamlkPTE4ODM0OTc3Nzc0NjY3NDkzMTgtQVA=" title="https://support.google.com/adsense/answer/1705822?hl=en&ref_topic=1706004&sjid=1883497777466749318-AP">AdSense for video overview - Google AdSense Help<i class="fa fa-external-link"></i></span></li></ul>]]>
    </content>
    <id>https://heidiliu2020.github.io/google-adsense/</id>
    <link href="https://heidiliu2020.github.io/google-adsense/"/>
    <published>2023-10-21T03:39:01.000Z</published>
    <summary>
      <![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>在上一篇文章  <a href="https://heidiliu2020.github.io/google-advertising-service/">Google 廣告｜比較 Google Ads、AdSense、AdMob 和 Ad Manager 的區別</a> 中，介紹幾種與 Google 廣告相關的服務。</p>
<p>接下來，將會介紹如何使用 Google AdSense，從申請帳號資格到實際設定廣告投放。</p>
<h2 id="如何使用-Google-AdSense"><a href="#如何使用-Google-AdSense" class="headerlink" title="如何使用 Google AdSense"></a>如何使用 Google AdSense</h2><p>以下是官方 AdSense 頻道的影片介紹：</p>
<iframe width="694" height="520" src="https://www.youtube.com/embed/YSqOvgfbPhI" title="Welcome to Google AdSense" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>]]>
    </summary>
    <title>Google 廣告｜如何使用 Google AdSense 設定廣告</title>
    <updated>2026-04-23T06:14:18.215Z</updated>
  </entry>
  <entry>
    <author>
      <name>Heidi Liu</name>
    </author>
    <category term="技術學習" scheme="https://heidiliu2020.github.io/categories/%E6%8A%80%E8%A1%93%E5%AD%B8%E7%BF%92/"/>
    <category term="2023鐵人賽" scheme="https://heidiliu2020.github.io/categories/%E6%8A%80%E8%A1%93%E5%AD%B8%E7%BF%92/2023%E9%90%B5%E4%BA%BA%E8%B3%BD/"/>
    <category term="入門篇" scheme="https://heidiliu2020.github.io/categories/%E6%8A%80%E8%A1%93%E5%AD%B8%E7%BF%92/2023%E9%90%B5%E4%BA%BA%E8%B3%BD/%E5%85%A5%E9%96%80%E7%AF%87/"/>
    <category term="Translation" scheme="https://heidiliu2020.github.io/tags/Translation/"/>
    <category term="ironman2023" scheme="https://heidiliu2020.github.io/tags/ironman2023/"/>
    <category term="Qiita" scheme="https://heidiliu2020.github.io/tags/Qiita/"/>
    <content>
      <![CDATA[<p>在最後結語部分，稍微記錄這次在翻譯時的一點心得感想，內容可分為以下幾點：</p><ul><li>翻譯流程</li><li>文章管理</li><li>輔助翻譯工具</li><li>其他選擇</li><li>iThome 平台使用心得</li><li>結語</li></ul><span id="more"></span><h2 id="翻譯流程"><a href="#翻譯流程" class="headerlink" title="翻譯流程"></a>翻譯流程</h2><p>簡單記述這次在翻譯文章時的流程，步驟大致如下：</p><ul><li>在 <span class="exturl" data-url="aHR0cHM6Ly9xaWl0YS5jb20v" title="https://qiita.com/">Qiita<i class="fa fa-external-link"></i></span> 篩選感興趣的文章</li><li>使用 <span class="exturl" data-url="aHR0cHM6Ly93d3cubm90aW9uLnNvLw==" title="https://www.notion.so/">Notion<i class="fa fa-external-link"></i></span> 管理日文初稿</li><li>搭配 <span class="exturl" data-url="aHR0cHM6Ly9jaGF0Lm9wZW5haS5jb20v" title="https://chat.openai.com/">ChatGPT<i class="fa fa-external-link"></i></span> 等工具進行翻譯</li><li>翻譯完成後，會再轉移到 <span class="exturl" data-url="aHR0cHM6Ly9oYWNrbWQuaW8v" title="https://hackmd.io/">HackMD<i class="fa fa-external-link"></i></span> 管理，主要是確認 Markdown 語法是否有問題</li><li>貼到 iThome 編輯器，進行最後確認，沒問題就儲存成草稿</li><li>隔天起床發文送出</li></ul><p>關於文章篩選，除了是挑自己感興趣且討論度高的主題以外，也會盡量挑選文章字數在 3 千字上下，感覺翻譯起來比較不會有負擔，但凡事總是有例外，還是有幾篇字數突破到 5 千字以上，卻又無法輕易割捨⋯⋯</p><p>在正式開賽前，庫存約有十篇翻譯，到第十天左右進度也很有餘裕，因此天真地以為肯定能夠提早完成，舒舒服服迎接完賽。</p><p>隨著時間進行到中期，開始進入到偏技術性的文章，就不能單純仰賴翻譯工具進行翻譯，而是時常需要 Google 或查看 Wiki 等確保用字，以避免自創新詞的情況發生。甚至途中還發生點小插曲，雖然已經完成整篇翻譯，自己卻覺得主題有點太淺，結果乾脆當作練習，另外再找篇新的文章。</p><p>結果可想而知，到了後期，進度急速被壓縮，幾乎變成一兩天內就要完成一篇翻譯。也因此，真的還是需要提前把文章準備好，才不會整天被死線追著跑Orz</p><p>最後挑選主題，依照類別如下所示，這系列文章也已經整理到<a href="https://heidiliu2020.github.io/categories/">個人部落格</a>：</p><ul><li>非程式語言<ul><li>入門篇(9)</li><li>豆知識篇(6)</li><li>職涯篇(4)</li><li>工具篇(4)</li></ul></li><li>程式語言<ul><li>前端技術篇(4)</li><li>後端技術篇(2)</li><li>雲端篇(1)</li></ul></li></ul><p>其實選文結果跟自己原本預想差蠻多的，因為一開始的想法，是希望能夠程式與非程式各佔一半，程式語言的部分，理想是能夠介紹到四五種語言。</p><p>但現實是骨感的，想要能夠在一篇文章中清楚介紹一門程式語言，其實不是件容易的事。如果想要深入探討，這類型文章通常篇幅較長；如果篇幅較短，又可能會是比較粗淺的入門介紹。也因此，最後還是選擇非技術語言類別的文章居多。</p><h2 id="文章管理"><a href="#文章管理" class="headerlink" title="文章管理"></a>文章管理</h2><p>平時在做筆記的時候，也會像這樣搭配 Notion 與 HackMD 來管理文章。以前的我，其實是忠實的 HackMD 粉，但自從瞭解到 Notion 強大的資料管理功能，漸漸轉變成雙棲用戶。之所以仍無法捨棄 HackMD，是因為作為 Markdown 檔案編輯器，使用體驗還是很棒，但對於分類管理就不是強項，這點就靠 Notion 來進行互補。</p><p>總結兩者的優缺點如下：</p><ul><li>Notion<ul><li><p>強大的資料管理功能</p></li><li><p>可以直接內嵌圖片、影片和連結</p></li><li><p>不需花太多心思，就能讓畫面賞心悅目</p></li><li><p>因為功能眾多，一開始可能需要點時間熟悉使用方法</p></li><li><p>雖然支援 Markdown 語法，但匯出檔案有時符號會出現在奇怪位置</p><p><img src="https://hackmd.io/_uploads/ry9tq1sb6.png"></p></li></ul></li><li>HackMD<ul><li>支援 Markdown 語法</li><li>編輯器能使用雙欄位模式，編輯的同時馬上顯示預覽結果</li><li>能夠直接插入圖片並生成語法</li><li>能夠與 GitHub 同步筆記，進行文章備份</li><li>文章管理較弱，僅能夠以「標籤」進行文章分類</li></ul></li></ul><p><img src="https://hackmd.io/_uploads/HJpcc1jW6.png"></p><h2 id="輔助翻譯工具"><a href="#輔助翻譯工具" class="headerlink" title="輔助翻譯工具"></a>輔助翻譯工具</h2><p>現在的翻譯工具，已經比起過去充滿「機翻味」的 Google 翻譯成熟許多，尤其是在初次見識到 ChatGPT 的翻譯功能，更是令人嘆為觀止。</p><p>一如寫程式能夠直接問 ChatGPT，翻譯也能夠輕易達成。雖然遇到較複雜的情境，或是文法斷句較不規則的情況，還是很容易出現奇怪的結果，甚至翻譯出完全相反的語意。因此，想要完全取代人力可能還為之過早，但不可置否，若能夠妥善使用這些工具，將能夠大幅提升工作效率。或許機器能夠達到 80% 的要求，剩下更細微的部分，就需要由人力去進行優化。</p><p>除了 ChatGPT，主要還使用以下幾種翻譯工具：</p><h3 id="ChatGPT"><a href="#ChatGPT" class="headerlink" title="ChatGPT"></a>ChatGPT</h3><ul><li>網頁版：<span class="exturl" data-url="aHR0cHM6Ly9jaGF0Lm9wZW5haS5jb20v" title="https://chat.openai.com/">https://chat.openai.com/<i class="fa fa-external-link"></i></span></li><li>Chrome 擴充功能：**<span class="exturl" data-url="aHR0cHM6Ly9jaHJvbWUuZ29vZ2xlLmNvbS93ZWJzdG9yZS9kZXRhaWwvY2hhdGdwdC1mb3ItZ29vZ2xlL2pnamFlYWNka29uYW9hZmVubGZra2ttYmFvcGtiaWxm" title="https://chrome.google.com/webstore/detail/chatgpt-for-google/jgjaeacdkonaoafenlfkkkmbaopkbilf">ChatGPT for Google<i class="fa fa-external-link"></i></span>**<ul><li>個人認為比 ChatGPT 官網好用，使用上穩定很多，不會經常出現錯誤導致對話卡住</li></ul></li><li>使用免費版 GPT-3.5</li></ul><h3 id="Bing-Chat"><a href="#Bing-Chat" class="headerlink" title="Bing Chat"></a>Bing Chat</h3><ul><li>網頁版：<span class="exturl" data-url="aHR0cHM6Ly93d3cuYmluZy5jb20vc2VhcmNoP3E9QmluZytBSSZhbXA7c2hvd2NvbnY9MSZhbXA7Rk9STT1ocGNvZHg=" title="https://www.bing.com/search?q=Bing+AI&amp;showconv=1&amp;FORM=hpcodx">https://www.bing.com/search?q=Bing+AI&amp;showconv=1&amp;FORM=hpcodx<i class="fa fa-external-link"></i></span></li><li>支援 GPT-4，就翻譯而言沒有太大差別，但或許對寫程式仍有一定幫助</li><li>次數限制這點真的不太友善，如果不是遇到特別複雜的結構，可能不太會想用 Bing 來協助翻譯</li><li>詳細可參考這篇：<span class="exturl" data-url="aHR0cHM6Ly9pdGhlbHAuaXRob21lLmNvbS50dy9hcnRpY2xlcy8xMDMzNjg4Mg==" title="https://ithelp.ithome.com.tw/articles/10336882">Day24 - 啊？你還在用 ChatGPT 嗎？ Bing 可以免費使用 GPT-4 喔！<i class="fa fa-external-link"></i></span></li></ul><h3 id="DeepL-Translate"><a href="#DeepL-Translate" class="headerlink" title="DeepL Translate"></a>DeepL Translate</h3><ul><li>網頁版：<span class="exturl" data-url="aHR0cHM6Ly93d3cuZGVlcGwuY29tL3poL3RyYW5zbGF0b3IvbC9lbi96aA==" title="https://www.deepl.com/zh/translator/l/en/zh">https://www.deepl.com/zh/translator/l/en/zh<i class="fa fa-external-link"></i></span></li><li>Chrome 擴充功能：<span class="exturl" data-url="aHR0cHM6Ly9jaHJvbWUuZ29vZ2xlLmNvbS93ZWJzdG9yZS9kZXRhaWwvZGVlcGwtdHJhbnNsYXRlLXJlYWRpbmctdy9jb2ZkYnBvZWdlbXBqbG9vZ2JhZ2tuY2VraW5mbGNuag==" title="https://chrome.google.com/webstore/detail/deepl-translate-reading-w/cofdbpoegempjloogbagkncekinflcnj">DeepL Translate: Reading &amp; writing translator<i class="fa fa-external-link"></i></span></li><li>中文只有簡體，尚未支援繁體<ul><li>雖然可搭配「簡轉繁中」的工具，但多一步驟就是有點麻煩</li></ul></li></ul><h3 id="Mate-Translate"><a href="#Mate-Translate" class="headerlink" title="Mate Translate"></a>Mate Translate</h3><ul><li>Chrome 擴充功能：<span class="exturl" data-url="aHR0cHM6Ly9jaHJvbWUuZ29vZ2xlLmNvbS93ZWJzdG9yZS9kZXRhaWwvbWF0ZS10cmFuc2xhdGUtJUUyJTgwJTkzLXRyYW5zbGF0L2lobWdpY2xpYmJuZGZmZWplZGppbWZqbWZvYWJwY2tl" title="https://chrome.google.com/webstore/detail/mate-translate-%E2%80%93-translat/ihmgiclibbndffejedjimfjmfoabpcke">Mate Translate – translator, dictionary<i class="fa fa-external-link"></i></span></li><li>在閱讀文章時，無意間發現這項工具，體驗上比只有簡體的 DeepL 優秀很多</li><li>用法同樣是選取翻譯區塊，旁邊會出現 icon 可點選進行翻譯，或是點選右上角的 icon 進行全頁翻譯</li></ul><p>使用畫面如下所示，選取區塊旁的兩個 icon 分別是 DeepL 和 Mate：</p><p><img src="https://hackmd.io/_uploads/Bk83qki-6.png"></p><p>然而，以上這些翻譯工具，即使輸入提示文字「請翻譯成繁體中文」或下一些「限制語句」，偶爾還是會冒出對岸用語或簡體中文。</p><p>舉出部分例子作為對照，如下表所示，這點在翻譯時需特別注意：</p><table><thead><tr><th>台灣用語</th><th>對岸用語</th></tr></thead><tbody><tr><td>程式</td><td>程序</td></tr><tr><td>程式碼</td><td>代碼</td></tr><tr><td>程式語言</td><td>編程語言</td></tr><tr><td>影片</td><td>視頻</td></tr><tr><td>資訊</td><td>信息</td></tr><tr><td>資料庫</td><td>數據庫</td></tr><tr><td>軟體</td><td>軟件</td></tr><tr><td>文章</td><td>帖子</td></tr><tr><td>網際網路</td><td>互聯網</td></tr></tbody></table><h2 id="其他選擇"><a href="#其他選擇" class="headerlink" title="其他選擇"></a>其他選擇</h2><p>除了文章編輯管理工具，以及輔助翻譯工具以外，也會搭配以下方式：</p><h3 id="goo-辭書"><a href="#goo-辭書" class="headerlink" title="goo 辭書"></a><span class="exturl" data-url="aHR0cHM6Ly9kaWN0aW9uYXJ5Lmdvby5uZS5qcC8=" title="https://dictionary.goo.ne.jp/">goo 辭書<i class="fa fa-external-link"></i></span></h3><ul><li>日語辭典</li><li>遇到一些即使 Google 還是不懂意思，或甚至找不到中文翻譯，直接翻日語詞典也是個辦法</li><li>遇到較口語或新潮的用語，還是要善用 Google 搜尋<ul><li>如何提升 Google 搜尋能力，可參考這篇：<span class="exturl" data-url="aHR0cHM6Ly9pdGhlbHAuaXRob21lLmNvbS50dy9hcnRpY2xlcy8xMDMyMTI0NQ==" title="https://ithelp.ithome.com.tw/articles/10321245">Day3 - 工程師必備技能——Google 之力<i class="fa fa-external-link"></i></span></li></ul></li></ul><h3 id="教育部重編國語辭典"><a href="#教育部重編國語辭典" class="headerlink" title="教育部重編國語辭典"></a><span class="exturl" data-url="aHR0cHM6Ly9kaWN0LnJldmlzZWQubW9lLmVkdS50dy9pbmRleC5qc3A=" title="https://dict.revised.moe.edu.tw/index.jsp">教育部重編國語辭典<i class="fa fa-external-link"></i></span></h3><ul><li>沒錯！就是中文辭典</li><li>避免口語化，確保不是使用台語或對岸用語<ul><li>例如：來去～、觀看視頻</li></ul></li><li>有時翻譯奧妙的點就在於，發現自己明明懂日文意思，卻不知道該如何用中文表達 <del>（或突然覺得自己不會中文）</del> 的時候，避免出現奇怪自創用詞</li></ul><h3 id="問問強者朋友"><a href="#問問強者朋友" class="headerlink" title="問問強者朋友"></a>問問強者朋友</h3><ul><li>不懂就問！問問精通日文的親朋好友，或是在 PTT、<span class="exturl" data-url="aHR0cHM6Ly90dy5oaW5hdGl2ZS5jb20v" title="https://tw.hinative.com/">HiNative<i class="fa fa-external-link"></i></span> 等平台發問</li><li>此外，「翻譯時開著教育部辭典和日文辭典，只要有疑慮就查」這點，就是詢問有在接案翻譯的朋友得到的建議</li></ul><h2 id="iThome-平台使用心得"><a href="#iThome-平台使用心得" class="headerlink" title="iThome 平台使用心得"></a>iThome 平台使用心得</h2><p>接下來，雖然跟主題比較無關，但這次藉由鐵人賽的機會，第一次使用 iThome 平台發文，在過程中對文章編輯器有些感想：</p><ol><li>不能一次上傳多張圖片，只能一張一張分開傳<ul><li>解決方法：先上傳到 HackMD 或 Imgur 等圖片空間，調整語法後再貼過來</li></ul></li><li>不支援內嵌影片語法，只能用編輯器的內建功能插入影片</li><li>無法用 markdown 語法控制圖片大小，如以下範例：<ul><li><code>![](https://sample.png =300x450)</code></li><li><code>&lt;img src=&quot;https://sample.png&quot; width=“300”&gt;</code></li></ul></li><li>文章部分沒有內建目錄系統，也不支援 <code>[TOC]</code>（<span class="exturl" data-url="aHR0cHM6Ly96aC53aWtpcGVkaWEub3JnL3dpa2kvJUU3JTlCJUFFJUU2JUFDJUExXyglRTYlOUIlQjglRTclQjElOEQp" title="https://zh.wikipedia.org/wiki/%E7%9B%AE%E6%AC%A1_(%E6%9B%B8%E7%B1%8D)">Table Of Contents<i class="fa fa-external-link"></i></span>） 這類能夠建立目錄的語法</li></ol><p>雖然在編輯器頁面中，就有顯示「不支援部分 HTML 內容」的標語，並舉出可使用的 Markdown 常用語法，但還是感覺使用上稍微不方便就是：</p><p><img src="https://hackmd.io/_uploads/BkYGjko-6.png"></p><h2 id="結語"><a href="#結語" class="headerlink" title="結語"></a>結語</h2><p>轉職成為前端工程師以來，終於輪到自己參賽，從閱讀者的角色轉變為創作者，感覺還是很不可思議。</p><p>先前也有在文章中提到，原本自己是想要以 AWS 為主題參賽的。起因是，一直在思考今年能不能有什麼突破，從年中就有計畫要考取 AWS 證照，卻遲遲沒辦法有可見的進度，甚至陷入一段小小低潮。</p><p>雖然是題外話，原本其實有過想去日本留學的夢想，甚至還在今年申請到別科資格，雖然後來因為職涯考量還是放棄了。仔細想了想，其實自己已經具備基礎的日文能力，畢竟打從一開始，就是以「興趣」驅動學習日文這件事，那麼何不以這為切入點，試試看能不能借此點燃學習程式的熱情呢？於是就開啟了這次鐵人賽的旅程。</p><p>最後，儘管說是創作，但翻譯本身仍舊是建立在他人的「成果」上，透過這次大量閱讀技術文章，除了瞭解與技術、軟硬實力與學習相關的知識內容，同時也瞭解文章架構，以及敘事方式。期許明年或是未來的自己，能夠真正「創作」文章進行技術分享。</p><p>感謝您的閱讀，如果發現文章內容有誤或有什麼建議，歡迎留言告訴我，我們有緣再相見！</p><blockquote><p>15th鐵人賽目錄傳送門：<span class="exturl" data-url="aHR0cHM6Ly9pdGhlbHAuaXRob21lLmNvbS50dy91c2Vycy8yMDEzNTU1OC9pcm9ubWFuLzYyOTA=" title="https://ithelp.ithome.com.tw/users/20135558/ironman/6290">https://ithelp.ithome.com.tw/users/20135558/ironman/6290<i class="fa fa-external-link"></i></span></p></blockquote>]]>
    </content>
    <id>https://heidiliu2020.github.io/ironman-2023-day-31/</id>
    <link href="https://heidiliu2020.github.io/ironman-2023-day-31/"/>
    <published>2023-10-16T17:12:01.000Z</published>
    <summary>
      <![CDATA[<p>在最後結語部分，稍微記錄這次在翻譯時的一點心得感想，內容可分為以下幾點：</p>
<ul>
<li>翻譯流程</li>
<li>文章管理</li>
<li>輔助翻譯工具</li>
<li>其他選擇</li>
<li>iThome 平台使用心得</li>
<li>結語</li>
</ul>]]>
    </summary>
    <title>
      <![CDATA[[2023 15th鐵人賽] Day31 - 結語 & 關於翻譯的一點心得]]>
    </title>
    <updated>2026-04-23T06:14:18.234Z</updated>
  </entry>
  <entry>
    <author>
      <name>Heidi Liu</name>
    </author>
    <category term="技術學習" scheme="https://heidiliu2020.github.io/categories/%E6%8A%80%E8%A1%93%E5%AD%B8%E7%BF%92/"/>
    <category term="2023鐵人賽" scheme="https://heidiliu2020.github.io/categories/%E6%8A%80%E8%A1%93%E5%AD%B8%E7%BF%92/2023%E9%90%B5%E4%BA%BA%E8%B3%BD/"/>
    <category term="職涯篇" scheme="https://heidiliu2020.github.io/categories/%E6%8A%80%E8%A1%93%E5%AD%B8%E7%BF%92/2023%E9%90%B5%E4%BA%BA%E8%B3%BD/%E8%81%B7%E6%B6%AF%E7%AF%87/"/>
    <category term="ironman2023" scheme="https://heidiliu2020.github.io/tags/ironman2023/"/>
    <category term="Qiita" scheme="https://heidiliu2020.github.io/tags/Qiita/"/>
    <category term="Learning" scheme="https://heidiliu2020.github.io/tags/Learning/"/>
    <content>
      <![CDATA[<blockquote><p>原文連結：<span class="exturl" data-url="aHR0cHM6Ly9xaWl0YS5jb20vaW5kaWdvbmlnaHRpc20vaXRlbXMvYzFiZjc0N2JlZDE1ZTI4NDA5MzY=" title="https://qiita.com/indigonightism/items/c1bf747bed15e2840936">技術に興味がなくて何が悪い？ - Qiita<i class="fa fa-external-link"></i></span></p></blockquote><p>最後選擇這篇文章作為結尾，是自己也覺得挺有意思的主題。</p><p>作者從自身角度，說明為何不贊同「對技術不感興趣的人不適合成為工程師」這項論點；再進一步透過自身經歷，分享即使對技術不感興趣，該如何以工程師的身份面對工作。</p><p>畢竟自己對於技術本身，可能也稱不上是「充滿興趣」的那一側人種，但不可否認的是，學習也好，工作也罷，若能從中獲得成就感，仍舊是讓人樂此不疲的事情。</p><span id="more"></span><p>那麼，以下正文開始！</p><hr><h2 id="TL-DR"><a href="#TL-DR" class="headerlink" title="TL;DR"></a>TL;DR</h2><p>即使對技術不感興趣，也可以成為一名工程師。</p><blockquote><p>【註】<span class="exturl" data-url="aHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvVEw7RFI=" title="https://en.wikipedia.org/wiki/TL;DR">TL:DR<i class="fa fa-external-link"></i></span> = Too Long; Didn’t Read.（長話短說、簡言之）</p></blockquote><h2 id="目標讀者"><a href="#目標讀者" class="headerlink" title="目標讀者"></a>目標讀者</h2><p>認為自己對技術不感興趣的人</p><h2 id="關於筆者"><a href="#關於筆者" class="headerlink" title="關於筆者"></a>關於筆者</h2><ul><li>Web 應用程式開發工程師，主要工作包括程式細部設計、畫面設計和編碼。</li><li>對技術不太感興趣。</li></ul><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>在工程師的圈子，經常可以看到以下類似的主張：</p><ul><li>應該在假日學習。</li><li>應該經常關注最新的技術趨勢。</li><li>應該參加技術活動。</li><li>應該每天寫程式。</li><li>應該學習現代技術，而不是過時的技術。</li></ul><p>etc…</p><p>而當這些「應該論」變得更加強烈時，甚至會出現「<strong>對技術不感興趣的人不適合當工程師</strong>」這樣的言論，並且有不少人贊同這種觀點。</p><p>前陣子，我在某個社群（SNS）看到以下對話：</p><blockquote><p>新手工程師的提問<br><em>我應該在假日學習嗎？</em></p></blockquote><blockquote><p>現役工程師的回答<br><em>提出這種問題說明你不適合做工程師，還是辭職吧</em>。</p></blockquote><p>接下來撰寫的內容，即使是對技術不感興趣的新手工程師，也完全沒有問題。我自己也是如此，很高興從事工程師的工作。不論是因為「可以賺錢」、「想要遠端工作」或是「IT 領域感覺很熱門」，基於這些動機而成為一名工程師。但為了生存，仍必須學習必要的知識。</p><p>我認為，即使對技術不感興趣，也不代表不適合成為工程師。對技術是否感興趣，和是否適合工程師這個職業不一定相符。我自己對技術並不太感興趣，但我認為自己非常適合工程師這個職業。<br>因此，我決定撰寫這篇文章，重新整理關於 <strong>「工程師和對技術的興趣」</strong> 的想法，希望能夠給那些因為對技術缺乏興趣而感到壓力的工程師，或那些正在考慮成為工程師的人一點鼓勵。</p><h2 id="「對技術不感興趣的人不適合成為工程師」的根據"><a href="#「對技術不感興趣的人不適合成為工程師」的根據" class="headerlink" title="「對技術不感興趣的人不適合成為工程師」的根據"></a>「對技術不感興趣的人不適合成為工程師」的根據</h2><p>首先，「對技術不感興趣的人不適合成為工程師」這種主張，可能是從什麼樣的思維中產生的？我認為以下是根本原因：</p><p><strong>工程師需要持續學習技術，如果沒有興趣將難以做到。</strong></p><p>然而，我認為事情並非如此。</p><h3 id="是否需要持續學習技術"><a href="#是否需要持續學習技術" class="headerlink" title="是否需要持續學習技術"></a>是否需要持續學習技術</h3><p>這在某種程度上是真實的。因為技術的進化和變遷非常劇烈，而且變化的速度似乎每年都在加快。<br>雖然計算機科學的基礎技術不會經常改變，但實際在工作現場需要掌握的技術，例如程式語言或 AWS/GCP/Azure 等雲端服務，可以說變化非常快速。</p><p>然而，一旦掌握了現場所需的技術，不太可能會因為日後不再持續學習，而失去工程師的職位。</p><p>這可能是一個極端的例子，但即使是動態語言從這個世界上消失，Ruby 或 PHP 的工程師是否會突然失業呢？我認為不會，既然擁有現場等級的技術能力，適應靜態語言可能也不需要花太多時間。此外，現在的編輯器具備強大的補完功能，因此也未必所有使用靜態語言的工程師，都能夠對「型別安全」或「編譯器行為」等有正確概念，有些可能只是按照感覺來編寫程式。</p><p>這也就是為何在開頭提到「某種程度上是真實的」，一旦掌握了現場所需技術，就不太可能會因為日後不再持續學習而失去工作，從這層意義上來看，將會有所抵觸。</p><p>另外，近來在與工程技術的領域中，出現像 ChatGPT 和 GitHub Copilot 這樣的創新 AI 相關服務，並且有顯著發展；但我認為不是所有技術人員，都需要不斷追蹤這些最新資訊。即使是創新的技術或服務，在實際導入現場時也會漸進式地逐步發展。當然，追蹤最新技術趨勢並沒有壞處，但若沒有足夠的技術興趣或體力，那麼根據目前工作職場對於導入技術的氛圍，在必要時把握需要的訊息，也能夠足以生存下去。</p><p><strong>對於那些對技術不感興趣的人來說，沒有必要自己開闢道路，只需要感謝前人鋪好的道路，並堂堂正正地前進就好。</strong></p><h3 id="如果沒有興趣就不能持續學習嗎"><a href="#如果沒有興趣就不能持續學習嗎" class="headerlink" title="如果沒有興趣就不能持續學習嗎"></a>如果沒有興趣就不能持續學習嗎</h3><p>對於這一點，我認為明顯是錯誤的。</p><h3 id="即使沒有興趣也可以學習"><a href="#即使沒有興趣也可以學習" class="headerlink" title="即使沒有興趣也可以學習"></a>即使沒有興趣也可以學習</h3><p>很顯然地，即使沒有興趣也可以學習，興趣和行動應該分開思考。</p><p>就我自己而言，即使對某種技術沒有興趣，只要目前的工作有需要，或者對未來的職涯發展有必要，我會在休息日抽出時間學習，這並不是什麼特別的事情。</p><p>在成為工程師之前，我曾在金融機構工作。當我看到周圍的同事，不一定都對「金融」感興趣，甚至可以說，對金融感興趣的人還比較少。</p><p>然而，他們在下班後或休息日也會學習。這是因為，這些對<strong>當前工作或未來晉升是必要的</strong>。</p><p>工程師也是一樣的，只要對當前工作執行或能夠接近自己未來的理想，那麼無論是否感興趣，學習就會是必要的。</p><p><strong>相較於其他職業，我認為工程師更傾向將興趣和行動視為不可分割</strong>，但實際上，有許多行業即使沒有興趣也必須學習，甚至是在工作之外，學習的情況並不少見。在經歷高中或大學入學考試的人當中，有多少是因為對學術有興趣而學習的呢？</p><h2 id="題外話：工程師和懈怠"><a href="#題外話：工程師和懈怠" class="headerlink" title="題外話：工程師和懈怠"></a>題外話：工程師和懈怠</h2><p>這並不僅限於這部分，但這些只代表個人觀點，並且未經過科學根據的調查。由於主題涉及到個人性格，因此特別強調這只是我的猜測。</p><p>在社群上看到工程師的貼文時，經常能看到「懶惰」或「麻煩」的詞語。雖然只是主觀感受，但相較於非工程師的貼文，這些詞語似乎更常見。</p><p>容易懈怠的人，擁有對感興趣的事物非常專注這項優點，甚至到可能忘記時間的程度。然而，另一方面，對於不感興趣的事物，可能難以保持專注。如果是對技術感興趣的工程師，這可能不是問題，但如果沒有興趣，那可能就是悲劇了。</p><p>「對技術不感興趣的人不適合成為工程師」的觀點，也許被誤解為與這部分有關。可能因此產生「懈怠的人對不感興趣的事情難以保持專注」 + 「懈怠的人需要學習技術才能成為工程師」 =&gt; 「對技術不感興趣且<strong>懈怠的</strong>人不適合成為工程師」這樣的論點。</p><p><strong>20230814 註記</strong></p><p>在評論中，有讀者指出，在工程師的貼文中經常看到「懶惰」這個詞，這可能與 <span class="exturl" data-url="aHR0cHM6Ly96aC53aWtpcGVkaWEub3JnL3poLXR3LyVFNiU4QiU4OSVFOSU4NyU4QyVDMiVCNyVFNiVCMiU4MyVFNSVCMCU5NA==" title="https://zh.wikipedia.org/zh-tw/%E6%8B%89%E9%87%8C%C2%B7%E6%B2%83%E5%B0%94">Larry Wall<i class="fa fa-external-link"></i></span> 所提倡程式工程師的三大美德之一「懶惰」有關。確實，在這種語境和含義使用是很有可能的。</p><h2 id="首先是作為公司職員而不是工程師"><a href="#首先是作為公司職員而不是工程師" class="headerlink" title="首先是作為公司職員而不是工程師"></a>首先是作為公司職員而不是工程師</h2><p>這裡只討論在組織中工作的工程師。</p><p>在組織中工作，評價一位工程師價值的標準，<strong>將取決於如何為該組織創造利益</strong>，而技術能力只是其中的一種手段。</p><p>如果是這樣，即使對最新技術不感興趣或缺乏知識也沒關係，只要能把握該組織所採用的技術，並能用來解決組織的問題就足夠了。</p><p>即使只能使用過時的技術，對最新技術一無所知，只要能用自己的技術解決所屬組織的問題，那麼這個人就是一個出色的工程師，同時也是一位職員。</p><h2 id="成為職業的工程師"><a href="#成為職業的工程師" class="headerlink" title="成為職業的工程師"></a>成為<strong>職業的工程師</strong></h2><p>在組織中工作，用自己的能力為組織貢獻利益是理所當然的。如果身為一名工程師，那麼方法通常會是基於技術的工程。</p><p>若是如此，對技術是否感興趣並不重要。因為動機更簡單，只需「學習對組織利益有貢獻的事情」，這樣就足夠了。將興趣和行動分開，<strong>採取「因為需要而學習」的態度非常重要</strong>。</p><p>如果不能學習對工作必要的知識，那麼不論是否適合成為工程師，也很難成為能被組織認可的人才。</p><h2 id="面對未來的態度"><a href="#面對未來的態度" class="headerlink" title="面對未來的態度"></a>面對未來的態度</h2><p>如果已滿足於現在（目前工作職務）的需求，那麼只需要對未來做好準備，就不會有任何不安了。如果未來有想要成為的樣貌，那就朝著那個目標努力。然而，對於那些沒有未來目標，但仍想以工程師身份生活下去的人，以下將介紹一些我自己正在實踐的方法。</p><h3 id="取得證照"><a href="#取得證照" class="headerlink" title="取得證照"></a>取得證照</h3><p>我個人推薦的方法之一，是參加每年兩次由 <span class="exturl" data-url="aHR0cHM6Ly93d3cuaXBhLmdvLmpwL2luZGV4Lmh0bWw=" title="https://www.ipa.go.jp/index.html">IPA（資訊處理推進機構）<i class="fa fa-external-link"></i></span>舉辦的「<span class="exturl" data-url="aHR0cHM6Ly9qYS53aWtpcGVkaWEub3JnL3dpa2kvJUU2JTgzJTg1JUU1JUEwJUIxJUU1JTg3JUE2JUU3JTkwJTg2JUU2JThBJTgwJUU4JUExJTkzJUU4JTgwJTg1JUU4JUE5JUE2JUU5JUE4JTkz" title="https://ja.wikipedia.org/wiki/%E6%83%85%E5%A0%B1%E5%87%A6%E7%90%86%E6%8A%80%E8%A1%93%E8%80%85%E8%A9%A6%E9%A8%93">情報処理技術者試験（資訊處理技術者試驗）<i class="fa fa-external-link"></i></span>」。如此可以避免一年中完全沒有學習的時期，且由於一年舉辦兩次，也不需每天忙於準備考試。</p><p>之所以選擇 IPA，是因爲資格沒有有效期限。一旦取得資格，就可以永遠列在履歷表上。對於對技術不感興趣的人來說，可能不太願意花時間在只有 2、3 年有效期限的資格上。</p><p> ※ 關於「<span class="exturl" data-url="aHR0cHM6Ly9qYS53aWtpcGVkaWEub3JnL3dpa2kvJUU2JTgzJTg1JUU1JUEwJUIxJUU1JTg3JUE2JUU3JTkwJTg2JUU1JUFFJTg5JUU1JTg1JUE4JUU3JUEyJUJBJUU0JUJGJTlEJUU2JTk0JUFGJUU2JThGJUI0JUU1JUEzJUFC" title="https://ja.wikipedia.org/wiki/%E6%83%85%E5%A0%B1%E5%87%A6%E7%90%86%E5%AE%89%E5%85%A8%E7%A2%BA%E4%BF%9D%E6%94%AF%E6%8F%B4%E5%A3%AB">情報処理安全確保支援士（資訊處理安全支援專家）<i class="fa fa-external-link"></i></span>」，由於是採取註冊制度，因此說沒有有效期限可能不太準確，詳細資訊請參考 <span class="exturl" data-url="aHR0cHM6Ly93d3cuaXBhLmdvLmpwL2luZGV4Lmh0bWw=" title="https://www.ipa.go.jp/index.html">IPA 官方網站<i class="fa fa-external-link"></i></span>。</p><p>如果是身為 Web 工程師，基本資訊、應用資訊、資訊處理安全確保支援師、網路專家、資料專家、系統架構師等資格，都與實務相關，擁有這些資格也不會有損失。此外，根據自身的情況，參加專案經理的考試也是不錯的選擇。</p><p>在完成 IPA 必要的試驗後，可以參加 <span class="exturl" data-url="aHR0cHM6Ly96aC53aWtpcGVkaWEub3JnL3poLXR3L0xpbnV4JUU1JTlDJThCJUU5JTlBJTlCJUU1JUIwJTg4JUU2JUE1JUFEJUU1JThEJTk0JUU2JTlDJTgz" title="https://zh.wikipedia.org/zh-tw/Linux%E5%9C%8B%E9%9A%9B%E5%B0%88%E6%A5%AD%E5%8D%94%E6%9C%83">LPI（Linux Professional Institute）<i class="fa fa-external-link"></i></span>、<span class="exturl" data-url="aHR0cHM6Ly9hd3MuYW1hem9uLmNvbS90dy9jZXJ0aWZpY2F0aW9uLw==" title="https://aws.amazon.com/tw/certification/">AWS<i class="fa fa-external-link"></i></span>、<span class="exturl" data-url="aHR0cHM6Ly96aC53aWtpcGVkaWEub3JnL3poLXR3LyVFNyU5NCVCMiVFOSVBQSVBOCVFNiU5NiU4NyVFNSU4NSVBQyVFNSU4RiVCOCVFOCVBRSVBNCVFOCVBRiU4MSVFOSVBMSVCOSVFNyU5QiVBRQ==" title="https://zh.wikipedia.org/zh-tw/%E7%94%B2%E9%AA%A8%E6%96%87%E5%85%AC%E5%8F%B8%E8%AE%A4%E8%AF%81%E9%A1%B9%E7%9B%AE">Oracle<i class="fa fa-external-link"></i></span> 等的認證測驗，只需在 Google 上搜索「工程師 證照 推薦」，選擇與自身工作相關認證，每年參加兩次測驗即可。</p><p>這裡需注意的是，不要考取太多證照。假設對技術不感興趣，那麼每天為了學習某個領域將會非常痛苦。將認證視為對未來的準備，能有一層安全網應對可能情況，因此在不超出能夠容忍的範圍內學習就足夠了。</p><p>此外，這裡並不打算討論工程師證照是否有其必要性的問題，但我認為擁有相關資格總是有益的。特別是對於像我這樣對技術不感興趣的人來說，不太可能採取將個人開發成果上傳到 GitHub 這類的行動，因此<strong>證照可能是唯一能夠客觀證明 IT 技能的方式</strong>。我的 GitHub 個人帳號已經荒廢許久，最近某汽車公司的除草劑成了熱門話題，我甚至懷疑是否也散布到了我的 GitHub 上。</p><p><img src="https://hackmd.io/_uploads/rkoBNIuZp.png"></p><p>順帶一提，有時在就業市場上，經常會聽到「實務經驗比證照更重要」的看法。由於有些人在面試中善於說真假話，除了依賴由個人主觀認定在過去工作中的貢獻度，證照資格也應該列入評估對象。無論這個人是否能執行與該資格相應的實務工作，至少存在「在某個時刻滿足取得該資格的要求」這項事實。而<strong>在工作中，能否按時交付成果也非常重要</strong>。</p><h3 id="閱讀技術書籍"><a href="#閱讀技術書籍" class="headerlink" title="閱讀技術書籍"></a>閱讀技術書籍</h3><p>只需閱讀就可以。我通常每天閱讀約 30 頁技術書籍。因為覺得麻煩，所以不會打開編輯器寫程式碼，只是閱讀而已。這樣就不會花太多時間，也不會感到負擔，僅僅閱讀就可以在某種程度上加深對技術的理解。</p><h3 id="「看」影片"><a href="#「看」影片" class="headerlink" title="「看」影片"></a>「看」影片</h3><p>在 YouTube 或 Udemy 等平台上，觀看影片內容，這裡重要的是「看」而不是「觀看」。即使試圖「認真觀看」來完全理解，但如果對內容不感興趣，也不太能持之以恆，最終可能會發現自己正在看其他內容。當然，也不需要打開編輯器。（即使打開也無妨）</p><p>為了能夠保持適度的專注，只需專心「看」就好。人類的大腦和五感是很神奇的，即使只存在過去腦海中模糊的記憶，「啊，是那時候的某項事物」也可能像這樣在未來產生連結。</p><h3 id="和工程師朋友出去吃飯"><a href="#和工程師朋友出去吃飯" class="headerlink" title="和工程師朋友出去吃飯"></a>和工程師朋友出去吃飯</h3><p>和工程師朋友一起吃飯時，總會有段時間談論技術相關的話題。在這個過程中，還可以瞭解最新的技術趨勢。</p><p>我有個非常熟悉區塊鏈技術的朋友。因為想知道他對區塊鏈的哪一方面感興趣，於是我閱讀了有關區塊鏈的書籍和觀看相關影片內容，查看了他的成果，甚至嘗試自己撰寫程式碼。結果，我對於區塊鏈技術有了一定程度的理解。</p><p>像這樣，<strong>即使對技術沒有興趣，只要對某個人感興趣，也許有機會透過這個人去瞭解某些技術。</strong></p><h3 id="關注-X-的技術用戶"><a href="#關注-X-的技術用戶" class="headerlink" title="關注 X 的技術用戶"></a>關注 X 的技術用戶</h3><p>我經常查看在 X 上所關注技術專家的貼文。這樣一來，我可以粗淺瞭解技術領域正在討論的話題。</p><p>在撰寫本文時，我觀察到有關「Ruby 的 return 語句」的小爭論。僅僅只是觀察一些關於這方面的貼文，也可以學習到「原來技術人員是用這種角度看待語言的」。</p><h2 id="嘗試去做不感興趣的事情"><a href="#嘗試去做不感興趣的事情" class="headerlink" title="嘗試去做不感興趣的事情"></a>嘗試去做不感興趣的事情</h2><p>取得證照、閱讀技術書籍、觀看影片等，做這些事情，可能會讓人懷疑自己其實對技術感興趣。因此，以下將依時間順序介紹今天的行動：</p><table><thead><tr><th>Time</th><th>Action</th></tr></thead><tbody><tr><td>15:00</td><td>起床</td></tr><tr><td>~16:00</td><td>早餐兼午餐</td></tr><tr><td>~21:00</td><td>YouTube, Netflix</td></tr><tr><td>~22:30</td><td>晚餐、散步</td></tr><tr><td>22:30</td><td>朋友要求我寫一篇文章</td></tr><tr><td>~24:00</td><td>YouTube, Netflix</td></tr><tr><td>24:00~</td><td>開始撰寫該文章</td></tr></tbody></table><p>如上所示，我通常將大部分的時間花在 YouTube 和 Netflix 上。說個題外話，但我目前非常熱衷於 TBS 電視台的「日曜劇場」節目正在播放的《VIVANT》劇集，經常花很多時間回顧主要劇情，或觀看關於這部劇的分析影片。</p><p>雖然有點偏題，但我想傳達的是，<strong>正因為沒有興趣，學習才具有其價值。</strong></p><p>即使可能存在細微差別，但那些在 X 上追蹤的人，無論是否有動力，都會強迫自己投入一定的時間完成某件事，期望由此產生（內在）動機，可以用 <strong>「念能力攻擊」</strong> 這個詞來形容。</p><p><strong>努力學習不感興趣的事物，往後也可能因此產生興趣。</strong></p><h2 id="相對於對技術感興趣的人，那些不感興趣的人能勝過嗎"><a href="#相對於對技術感興趣的人，那些不感興趣的人能勝過嗎" class="headerlink" title="相對於對技術感興趣的人，那些不感興趣的人能勝過嗎"></a>相對於對技術感興趣的人，那些不感興趣的人能勝過嗎</h2><p>很遺憾的，越是追求卓越，越難在技術能力和知識方面，勝過那些對技術有濃厚興趣的人。如果對技術感興趣的人每天花 3 小時學習，而對技術不感興趣的人來說，花 4 小時學習就有辦法勝過嗎？這並不是那麼簡單的問題。</p><p>對技術充滿興趣的人，除了在桌前學習，還會在日常生活的各種場合，思考與技術相關的事情，例如上廁所、吃飯或散步時，也許在這些時候花的時間更長。</p><p>因此很遺憾的，面對這樣的人，不抱持興趣的一方是無法與其競爭的。<strong>原因很單純，因為面對技術的學習時間、量和質都不同。</strong></p><p>但也不必感到過於悲觀，因為在我觀察的範圍內，這樣的人只佔極少數，只要能不懈怠地持續必要的學習，就能獲得在多數環境通用的技術能力。</p><p>此外，即使身為工程師，並不意味著單靠磨練技術就足以生存。能夠進行技術性討論的前提，是建立在能與客戶進行談判的能力、與工程師之間橫向協作的能力、領導其他工程師的能力、管理技術工程師團隊的能力等等，除了單純的技術能力以外，實際工作中所需的技能範圍非常廣泛。</p><h2 id="儘管如此我仍然認為自己適合成為工程師"><a href="#儘管如此我仍然認為自己適合成為工程師" class="headerlink" title="儘管如此我仍然認為自己適合成為工程師"></a>儘管如此我仍然認為自己適合成為工程師</h2><p>到目前為止，作為一名對技術不感興趣的工程師，我寫下自己對此的看法與實踐。儘管我依舊對技術不抱持興趣，但我仍然認為自己適合成為一名工程師。以下是我的原因。</p><h3 id="喜歡細節工作"><a href="#喜歡細節工作" class="headerlink" title="喜歡細節工作"></a>喜歡細節工作</h3><p>在工程領域中，包含許多細節工作。不用說，光是缺少冒號就可能導致無法運作，或空格變全形可能導致故障發生。然而，近來這些問題通常能被編輯器自動處理，這讓我有點嫉妒。<br>不僅是在寫程式的時候，在建立設計文件等情況，只不過是為了微調樣式，需要刪除字句、添加標點符號、修正錯字，或按上司喜好做細部調整，像這樣細緻而簡單的工作，我個人非常喜歡。</p><h3 id="能夠迅速看到成果"><a href="#能夠迅速看到成果" class="headerlink" title="能夠迅速看到成果"></a>能夠迅速看到成果</h3><p>只需添加一行程式碼，即可修復故障的功能，並且結果能夠立即顯示在螢幕上。例如，為了解決「這部分的數字沒有顯示」的問題，只需打開編輯器，稍微修改一下程式碼，然後刷新畫面，問題就解決了。</p><p>如果稍微拓寬視野，可以在許多場合迅速應用所學知識。這樣的例子多不勝數，如果被分配到的工作需要用到自己不熟悉的技術，透過自身研究和學習能夠成功完成這項工作，多數工程師可能或多或少都曾有過這些經歷。</p><p>如此一來，對於輸入和行動，能夠迅速看到結果，以及透過所學知識完成工作的實感，都使我樂在其中而不會感到厭倦。</p><h3 id="「喜愛」所具有的熱度"><a href="#「喜愛」所具有的熱度" class="headerlink" title="「喜愛」所具有的熱度"></a>「喜愛」所具有的熱度</h3><p>如同再三提到的，雖然我個人對技術沒有太大興趣，但周圍有許多人是因為熱愛技術，才選擇擔任工程師。與其他職業相比，真正對工作充滿興趣的人可能佔據相當大的比例。</p><p>在這樣的環境中，可能有機會體驗到珍貴的時刻。雖然無法完全用言語表達，但面對某人的「喜愛」所散發的熱度，能夠意識到周圍的人，甚至是自己都被這份熱情所牽引。</p><p><strong>「喜愛」這份情感所蘊含的熱情，感覺似乎遠遠超乎我們的想像。</strong></p><h3 id="著迷於網路的動力"><a href="#著迷於網路的動力" class="headerlink" title="著迷於網路的動力"></a>著迷於網路的動力</h3><p>無論是在網路上，還是私有網路空間，當別人向我建立的程式發送請求，並藉由回應滿足使用者的需求，可說是身為工程師所能夠體會到的<strong>最強大動力</strong>。</p><p>此外，自己完成的程式能透過網路向全球公開，並且被陌生人使用，<strong>這點讓我感到非常浪漫，甚至帶來一種興奮感</strong>。</p><h3 id="喜歡尋求自己的美感"><a href="#喜歡尋求自己的美感" class="headerlink" title="喜歡尋求自己的美感"></a>喜歡尋求自己的美感</h3><p>程式碼中存在著「美感」，即使是具有相同功能的程式碼，有的可能很髒亂，而有的可能具有令人嘆為觀止的美麗。</p><p>當然必須優先考慮的，是專案的程式碼規範，以及響應速度和內存效率等性能指標，但在滿足這些要求後，還存在一些空間，能夠寫出自己認為充滿美感的程式碼。這已不再被稱作是重構，而是<strong>類似補妝或盆景的至高領域</strong>了。</p><p><strong>如上所述，雖然我對工程技術本身並不感興趣，但透過工程領域獲得的經驗讓我樂此不疲，並且使我認為自己適合成為工程師。</strong></p><h2 id="最後"><a href="#最後" class="headerlink" title="最後"></a>最後</h2><p>基於以上這些理由，我認為並不一定要對技術感興趣，才能夠成為一名工程師。</p><p>即使對技術不感興趣，只要能夠發揮組織所需的能力，就不需擔心謀生的問題，有些人可能更關心透過工程領域所獲得的經驗。<br>此外，即使現在沒有興趣，也不代表將來會一直如此。</p><p>最後，我想分享這段話：</p><blockquote><p>頂への途を見つけたなら険しいのか？己に向いているのか？可能なのか？そんなものは関係ない　”ただ登る”少なくとも俺にアメリカンフットボールを教えたマルコという男はそうしてきた才能が足りぬのなら臆面もなく人の手にすがり己の手を汚し愛する者に侮辱され　それでもなお頂点を獲るために全ての男が本来持っている焼け付くような渇き　ただ頂点を獲るために！by アイシールド21 峨王力哉</p></blockquote><p>內容翻譯如下：</p><blockquote><p>既然看到了通往頂點的路；危險嗎？自己夠格嗎？可能嗎？這根本就無所謂了。只要「往上爬」，至少——那個教會我美式足球的男人，就是那麼做的啊！力量不夠那就用卑鄙的手段也要得到，即使弄髒自己的雙手；即使被喜歡的人輕視。就算是那樣，也要爬上頂點。所有人心裡本就該存在的那種熊熊燃燒的渴望，就只為了登上頂點！<br>——摘自<span class="exturl" data-url="aHR0cHM6Ly96aC53aWtpcGVkaWEub3JnL3poLXR3LyVFNSU4NSU4OSVFOSU4MCU5RiVFOCU5MiU5OSVFOSU5RCVBMiVFNCVCRiVBMDIx" title="https://zh.wikipedia.org/zh-tw/%E5%85%89%E9%80%9F%E8%92%99%E9%9D%A2%E4%BF%A021">《光速蒙面俠21》<i class="fa fa-external-link"></i></span>峨王力哉</p></blockquote><p>並不是說在這個時刻一定要追求巔峰。但如果有需要，不論自己是否適合，只要為了滿足這種需求，就應該「繼續往上爬」。</p><p>我相信這樣一來，絕對能夠以工程師的身份謀生。</p><hr><p>到這裡，結束鐵人賽最後一篇文章！（撒花）<del>雖然有點爆字數，差點以為要來不及-_-;;</del></p><p>看到這篇標題的時候不禁想著，不僅限於工程師這項職業，也許多數人對於工作本身的想法是：</p><blockquote><p>勞動就是狗屎。（労働はクソということです。）<br>by《咒術迴戰》七海健人</p></blockquote><p><img src="https://hackmd.io/_uploads/r1-rXPYWT.png"></p><p>咳咳、我想表達的是，或許大多數的人可能都曾懷疑過自己，懷疑是否有能力從事這份工作，但卻因為各種原因堅持下來。我想這沒有錯，畢竟能夠將興趣和工作完美結合的天選之人，在世上可能只佔極少數，但即使不感興趣，若能夠從中獲得成就感，也是很棒的事情呢。</p><p>最後，預計明天會再整理一篇結語，為今年鐵人賽初體驗做個結尾，以上！</p><blockquote><p>15th鐵人賽目錄傳送門：<span class="exturl" data-url="aHR0cHM6Ly9pdGhlbHAuaXRob21lLmNvbS50dy91c2Vycy8yMDEzNTU1OC9pcm9ubWFuLzYyOTA=" title="https://ithelp.ithome.com.tw/users/20135558/ironman/6290">https://ithelp.ithome.com.tw/users/20135558/ironman/6290<i class="fa fa-external-link"></i></span></p></blockquote>]]>
    </content>
    <id>https://heidiliu2020.github.io/ironman-2023-day-30/</id>
    <link href="https://heidiliu2020.github.io/ironman-2023-day-30/"/>
    <published>2023-10-15T13:36:01.000Z</published>
    <summary>
      <![CDATA[<blockquote>
<p>原文連結：<span class="exturl" data-url="aHR0cHM6Ly9xaWl0YS5jb20vaW5kaWdvbmlnaHRpc20vaXRlbXMvYzFiZjc0N2JlZDE1ZTI4NDA5MzY=" title="https://qiita.com/indigonightism/items/c1bf747bed15e2840936">技術に興味がなくて何が悪い？ - Qiita<i class="fa fa-external-link"></i></span></p>
</blockquote>
<p>最後選擇這篇文章作為結尾，是自己也覺得挺有意思的主題。</p>
<p>作者從自身角度，說明為何不贊同「對技術不感興趣的人不適合成為工程師」這項論點；再進一步透過自身經歷，分享即使對技術不感興趣，該如何以工程師的身份面對工作。</p>
<p>畢竟自己對於技術本身，可能也稱不上是「充滿興趣」的那一側人種，但不可否認的是，學習也好，工作也罷，若能從中獲得成就感，仍舊是讓人樂此不疲的事情。</p>]]>
    </summary>
    <title>[2023 15th鐵人賽] Day30 - 對技術不感興趣有什麼錯？</title>
    <updated>2026-04-23T06:14:18.234Z</updated>
  </entry>
  <entry>
    <author>
      <name>Heidi Liu</name>
    </author>
    <category term="技術學習" scheme="https://heidiliu2020.github.io/categories/%E6%8A%80%E8%A1%93%E5%AD%B8%E7%BF%92/"/>
    <category term="2023鐵人賽" scheme="https://heidiliu2020.github.io/categories/%E6%8A%80%E8%A1%93%E5%AD%B8%E7%BF%92/2023%E9%90%B5%E4%BA%BA%E8%B3%BD/"/>
    <category term="雲端篇" scheme="https://heidiliu2020.github.io/categories/%E6%8A%80%E8%A1%93%E5%AD%B8%E7%BF%92/2023%E9%90%B5%E4%BA%BA%E8%B3%BD/%E9%9B%B2%E7%AB%AF%E7%AF%87/"/>
    <category term="ironman2023" scheme="https://heidiliu2020.github.io/tags/ironman2023/"/>
    <category term="Qiita" scheme="https://heidiliu2020.github.io/tags/Qiita/"/>
    <category term="AWS" scheme="https://heidiliu2020.github.io/tags/AWS/"/>
    <category term="Roadmap" scheme="https://heidiliu2020.github.io/tags/Roadmap/"/>
    <content>
      <![CDATA[<blockquote><p>原文連結：<span class="exturl" data-url="aHR0cHM6Ly9xaWl0YS5jb20vS3Vyb2thd2FLb3VoZWkvaXRlbXMvMzgzNjgwMmZjM2EwMjg2ZjY2OGY=" title="https://qiita.com/KurokawaKouhei/items/3836802fc3a0286f668f">AWSエンジニアロードマップ2023<i class="fa fa-external-link"></i></span></p></blockquote><p>終於來到了這篇，其實當初在決定要報名今年鐵人賽的時候，是想要從 AWS 主題著手，但一方面沒什麼自信能夠在三十天內寫出完整架構，一方面也還在摸索學習的步調。</p><p>本篇文章將會介紹 AWS 學習指標，部分圖解和影片以日文為主，但透過 Roadmap 還有每週主題，還是能夠參考並安排自己的學習進度，架構如下所示：</p><ul><li>第 1 週：AWS 基礎知識（AWS Basics）</li><li>第 2～3 週：網路與內容傳遞（Networking and Content Delivery）</li><li>第 4 週：運算服務（Compute Services）</li><li>第 5 週：安全、身份和合規性（Security, Identity, and Compliance）</li><li>第 6 週：資料庫（Database）</li><li>第 7 週：儲存（Storage）</li><li>第 8 週：管理和管控治理（Management and Governance）</li><li>第 9 週：容器、無伺服器運算、應用程式整合（Containers and Serverless Computing, Application Integration）</li><li>第 10 週：分析、遷移、其他（Analytics, Migrations）</li><li>第 11〜12 週：SAA 認證模擬測驗<ul><li>練習 SAA 的注意事項</li></ul></li></ul><p>實際學習主要還是建議搭配 <span class="exturl" data-url="aHR0cHM6Ly9kb2NzLmF3cy5hbWF6b24uY29tL3poX3R3Lz9uYzI9aF9xbF9kb2NfZG8=" title="https://docs.aws.amazon.com/zh_tw/?nc2=h_ql_doc_do">AWS 官方文件<i class="fa fa-external-link"></i></span>，或是其他線上課程等管道來進行。此外，也給自己訂下目標，希望今年結束前能夠達成，有機會的話，到時候再來整理一篇更詳細的準備心得。</p><span id="more"></span><p>那麼，以下正文開始。</p><hr><p><img src="https://i.imgur.com/gDb4iD0.png"></p><ul><li>將 AWS 的學習路線統整後，以「路線圖風格」呈現。</li><li>黃色的圓圈代表重要的項目。</li><li>「完全沒有 AWS 經驗」的人來說，通常需要約「3 個月」的學習時間才能「通過 SAA 取得證照資格」。</li></ul><p>（對於有經驗的人來說，可以在更短時間內通過考試。）</p><ul><li>通過 SAA 測驗後，建議學習其他證照內容的同時，需搭配實際操作來學習。</li></ul><p>即使是 ChatGPT，應該無法提供這樣的路線圖⛅️</p><h2 id="第-1-週：AWS-基礎知識（AWS-Basics）"><a href="#第-1-週：AWS-基礎知識（AWS-Basics）" class="headerlink" title="第 1 週：AWS 基礎知識（AWS Basics）"></a>第 1 週：<span class="exturl" data-url="aHR0cHM6Ly9hd3MuYW1hem9uLmNvbS90dy9nZXR0aW5nLXN0YXJ0ZWQv" title="https://aws.amazon.com/tw/getting-started/">AWS 基礎知識（AWS Basics）<i class="fa fa-external-link"></i></span></h2><p>首先，以下整理一下有關「AWS」和「傳統的 IT 基礎架構」的基本知識。<br>為什麼 AWS 會受到如此多的關注？瞭解 AWS 擁有哪些優勢非常重要。</p><iframe width="734" height="413" src="https://www.youtube.com/embed/156B6cZ_2aQ" title="AWSが使われる理由、クラウドサービスのメリット、なぜ今AWSを学ぶべきなのか？【11:32】" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe><p>就地部署軟體（On-Premises）的缺點如下：</p><ul><li>需要大量的初期成本</li><li>需要預測容量（Capacity）</li><li>從購買到部署伺服器，通常需要約一個月的時間</li></ul><p>等項目。</p><p>另一方面，AWS 能夠根據實際使用量來支付費用，因此不需預測容量。</p><p>可以立即使用、從任何地方連接，且應用標準化的技術。</p><p>基於上述幾項特點，可預期 AWS 的優勢將遠高於就地部署軟體，並且在成本、彈性和可訪問性等方面能夠得到提升。</p><p><img src="https://hackmd.io/_uploads/Skq9SoSba.png"></p><iframe width="708" height="315" src="https://www.youtube.com/embed/nNWdBqdbn9w" title="AWSグローバルインフラストラクチャ【4:50】" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe><blockquote><p><span class="exturl" data-url="aHR0cHM6Ly93d3cueW91dHViZS5jb20vcGxheWxpc3Q/bGlzdD1QTDJuQ0UyaVItbHBtN1hGQUpSMG5nRDFya2hlSGdYXzBi" title="https://www.youtube.com/playlist?list=PL2nCE2iR-lpm7XFAJR0ngD1rkheHgX_0b">更多 AWS 相關基礎知識的影片可參考這裡<i class="fa fa-external-link"></i></span></p></blockquote><h2 id="第-2～3-週：網路與內容傳遞（Networking-and-Content-Delivery）"><a href="#第-2～3-週：網路與內容傳遞（Networking-and-Content-Delivery）" class="headerlink" title="第 2～3 週：網路與內容傳遞（Networking and Content Delivery）"></a>第 2～3 週：<span class="exturl" data-url="aHR0cHM6Ly9hd3MuYW1hem9uLmNvbS90dy9wcm9kdWN0cy9uZXR3b3JraW5nLw==" title="https://aws.amazon.com/tw/products/networking/">網路與內容傳遞（Networking and Content Delivery）<i class="fa fa-external-link"></i></span></h2><p><span class="exturl" data-url="aHR0cHM6Ly9kb2NzLmF3cy5hbWF6b24uY29tL3poX3R3L3ZwYy9sYXRlc3QvdXNlcmd1aWRlL3doYXQtaXMtYW1hem9uLXZwYy5odG1s" title="https://docs.aws.amazon.com/zh_tw/vpc/latest/userguide/what-is-amazon-vpc.html">VPC<i class="fa fa-external-link"></i></span> 是最重要的項目。應具備網路知識，如果對<span class="exturl" data-url="aHR0cHM6Ly96aC53aWtpcGVkaWEub3JnL3poLXR3LyVFNSVBRCU5MCVFNyVCRCU5MQ==" title="https://zh.wikipedia.org/zh-tw/%E5%AD%90%E7%BD%91">子網路遮罩<i class="fa fa-external-link"></i></span>或<span class="exturl" data-url="aHR0cHM6Ly96aC53aWtpcGVkaWEub3JnL3poLXR3LyVFNiU5NyVBMCVFNyVCMSVCQiVFNSU4OCVBQiVFNSU5RiU5RiVFOSU5NyVCNCVFOCVCNyVBRiVFNyU5NCVCMQ==" title="https://zh.wikipedia.org/zh-tw/%E6%97%A0%E7%B1%BB%E5%88%AB%E5%9F%9F%E9%97%B4%E8%B7%AF%E7%94%B1">無類別域間路由（CIDR）<i class="fa fa-external-link"></i></span>計算不熟悉，則需要複習這部分內容。</p><iframe width="781" height="439" src="https://www.youtube.com/embed/2317shavNoo" title="【VPC講座1】リージョン / アベイラビリティゾーン / サブネット【9:02】" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe><p>對於 <span class="exturl" data-url="aHR0cHM6Ly9hd3MuYW1hem9uLmNvbS90dy9yb3V0ZTUzLw==" title="https://aws.amazon.com/tw/route53/">Route 53<i class="fa fa-external-link"></i></span>，只要能瞭解必備知識 DNS，與名稱解析相關的概念，就不會太困難。<br>由於 <span class="exturl" data-url="aHR0cHM6Ly9hd3MuYW1hem9uLmNvbS90dy9jbG91ZGZyb250Lw==" title="https://aws.amazon.com/tw/cloudfront/">CloudFront<i class="fa fa-external-link"></i></span> 設定項目較多，如果在初期階段過於深入研究，可能會因此感到挫折。</p><p><img src="https://hackmd.io/_uploads/SyyLLsSZ6.png"></p><h2 id="第-4-週：運算服務（Compute-Services）"><a href="#第-4-週：運算服務（Compute-Services）" class="headerlink" title="第 4 週：運算服務（Compute Services）"></a>第 4 週：<span class="exturl" data-url="aHR0cHM6Ly9kb2NzLmF3cy5hbWF6b24uY29tL3poX3R3L3doaXRlcGFwZXJzL2xhdGVzdC9hd3Mtb3ZlcnZpZXcvY29tcHV0ZS1zZXJ2aWNlcy5odG1s" title="https://docs.aws.amazon.com/zh_tw/whitepapers/latest/aws-overview/compute-services.html">運算服務（Compute Services）<i class="fa fa-external-link"></i></span></h2><p>瞭解如何購買 <span class="exturl" data-url="aHR0cHM6Ly9hd3MuYW1hem9uLmNvbS90dy9lYzIv" title="https://aws.amazon.com/tw/ec2/">EC2<i class="fa fa-external-link"></i></span>，對於考試和實務都非常重要，這部分也須充分理解。<br>如果只是建立伺服器，可透過圖形使用介面（GUI）在一分鐘內搞定，而進一步架構和維護，瞭解 Linux 的基本知識和命令更為重要。</p><p><img src="https://hackmd.io/_uploads/HyqIIiBZp.png"></p><p><span class="exturl" data-url="aHR0cHM6Ly9hd3MuYW1hem9uLmNvbS90dy9hdXRvc2NhbGluZy8=" title="https://aws.amazon.com/tw/autoscaling/">Auto Scaling<i class="fa fa-external-link"></i></span> 則需要瞭解各種擴展策略（Scaling Policies）的差異。</p><p><img src="https://hackmd.io/_uploads/HJ_KUiBZa.png"></p><p><span class="exturl" data-url="aHR0cHM6Ly9hd3MuYW1hem9uLmNvbS90dy9lbGFzdGljYmVhbnN0YWxrLw==" title="https://aws.amazon.com/tw/elasticbeanstalk/">Elastic Beanstalk<i class="fa fa-external-link"></i></span><br>只需掌握服務概述，並試著實際運行即可（以下是 <span class="exturl" data-url="aHR0cHM6Ly9kb2NzLmF3cy5hbWF6b24uY29tL3poX3R3L2VsYXN0aWNiZWFuc3RhbGsvbGF0ZXN0L2RnL2NvbmNlcHRzLXdlYnNlcnZlci5odG1s" title="https://docs.aws.amazon.com/zh_tw/elasticbeanstalk/latest/dg/concepts-webserver.html">Web 伺服器環境<i class="fa fa-external-link"></i></span>，還有<span class="exturl" data-url="aHR0cHM6Ly9kb2NzLmF3cy5hbWF6b24uY29tL3poX3R3L2VsYXN0aWNiZWFuc3RhbGsvbGF0ZXN0L2RnL2NvbmNlcHRzLXdvcmtlci5odG1s" title="https://docs.aws.amazon.com/zh_tw/elasticbeanstalk/latest/dg/concepts-worker.html">工作者環境<i class="fa fa-external-link"></i></span>也需掌握）。</p><p><img src="https://hackmd.io/_uploads/BJ6_LoSb6.png"></p><h2 id="第-5-週：安全、身份和合規性（Security-Identity-and-Compliance）"><a href="#第-5-週：安全、身份和合規性（Security-Identity-and-Compliance）" class="headerlink" title="第 5 週：安全、身份和合規性（Security, Identity, and Compliance）"></a>第 5 週：<span class="exturl" data-url="aHR0cHM6Ly9hd3MuYW1hem9uLmNvbS90dy9hcmNoaXRlY3R1cmUvc2VjdXJpdHktaWRlbnRpdHktY29tcGxpYW5jZS8=" title="https://aws.amazon.com/tw/architecture/security-identity-compliance/">安全、身份和合規性（Security, Identity, and Compliance）<i class="fa fa-external-link"></i></span></h2><p>AWS 除了以一般的密碼方式，還使用<span class="exturl" data-url="aHR0cHM6Ly9kb2NzLmF3cy5hbWF6b24uY29tL3poX3R3L3JlZHNoaWZ0L2xhdGVzdC9kZy90X1JvbGVzLmh0bWw=" title="https://docs.aws.amazon.com/zh_tw/redshift/latest/dg/t_Roles.html">基於角色型的存取控制（RBAC）<i class="fa fa-external-link"></i></span>功能進行權限管理。透過瞭解<span class="exturl" data-url="aHR0cHM6Ly9kb2NzLmF3cy5hbWF6b24uY29tL3poX3R3L0lBTS9sYXRlc3QvVXNlckd1aWRlL2lkLmh0bWw=" title="https://docs.aws.amazon.com/zh_tw/IAM/latest/UserGuide/id.html">角色的概念<i class="fa fa-external-link"></i></span>以及如何<span class="exturl" data-url="aHR0cHM6Ly9kb2NzLmF3cy5hbWF6b24uY29tL3poX3R3L0lBTS9sYXRlc3QvVXNlckd1aWRlL2FjY2Vzc19wb2xpY2llc19jcmVhdGUuaHRtbA==" title="https://docs.aws.amazon.com/zh_tw/IAM/latest/UserGuide/access_policies_create.html">建立 IAM 政策<i class="fa fa-external-link"></i></span>，將能夠從容地處理權限錯誤等問題。</p><p><img src="https://hackmd.io/_uploads/rkB9IoB-p.png"></p><p><span class="exturl" data-url="aHR0cHM6Ly9kb2NzLmF3cy5hbWF6b24uY29tL3poX3R3L2NvZ25pdG8vbGF0ZXN0L2RldmVsb3Blcmd1aWRlL3doYXQtaXMtYW1hem9uLWNvZ25pdG8uaHRtbA==" title="https://docs.aws.amazon.com/zh_tw/cognito/latest/developerguide/what-is-amazon-cognito.html">Cognito<i class="fa fa-external-link"></i></span> 是一項受管服務，用於控制對 Web 和行動應用程式的存取，提供使用者註冊和登入的身份驗證功能。</p><p><img src="https://hackmd.io/_uploads/HyE3LoBbT.png"></p><h2 id="第-6-週：資料庫（Database）"><a href="#第-6-週：資料庫（Database）" class="headerlink" title="第 6 週：資料庫（Database）"></a>第 6 週：<span class="exturl" data-url="aHR0cHM6Ly9hd3MuYW1hem9uLmNvbS90dy9wcm9kdWN0cy9kYXRhYmFzZXMv" title="https://aws.amazon.com/tw/products/databases/">資料庫（Database）<i class="fa fa-external-link"></i></span></h2><p>瞭解如何區別 <span class="exturl" data-url="aHR0cHM6Ly9hd3MuYW1hem9uLmNvbS90dy9yZHMv" title="https://aws.amazon.com/tw/rds/">Relational Database Service（RDS）<i class="fa fa-external-link"></i></span> 的 <span class="exturl" data-url="aHR0cHM6Ly9hd3MuYW1hem9uLmNvbS90dy9yZHMvZmVhdHVyZXMvbXVsdGktYXov" title="https://aws.amazon.com/tw/rds/features/multi-az/">Multi AZ 部署<i class="fa fa-external-link"></i></span> 和<span class="exturl" data-url="aHR0cHM6Ly9hd3MuYW1hem9uLmNvbS90dy9yZHMvZmVhdHVyZXMvcmVhZC1yZXBsaWNhcy8=" title="https://aws.amazon.com/tw/rds/features/read-replicas/">讀取複本（Read Replica）<i class="fa fa-external-link"></i></span>非常重要。</p><p><img src="https://hackmd.io/_uploads/SyNTUiB-T.png"></p><p>此外，也需瞭解 <span class="exturl" data-url="aHR0cHM6Ly9hd3MuYW1hem9uLmNvbS90dy9keW5hbW9kYi8=" title="https://aws.amazon.com/tw/dynamodb/">NoSQL 的 DynamoDB<i class="fa fa-external-link"></i></span> 和記憶體資料庫的 <span class="exturl" data-url="aHR0cHM6Ly9hd3MuYW1hem9uLmNvbS90dy9lbGFzdGljYWNoZS8=" title="https://aws.amazon.com/tw/elasticache/">ElastiCache<i class="fa fa-external-link"></i></span> 等，以及與傳統的<span class="exturl" data-url="aHR0cHM6Ly96aC53aWtpcGVkaWEub3JnL3poLXR3LyVFOSU5NyU5QyVFOCU4MSVBRiVFNSVCQyU4RiVFOCVCMyU4NyVFNiU5NiU5OSVFNSVCQSVBQiVFNyVBRSVBMSVFNyU5MCU4NiVFNyVCMyVCQiVFNyVCNSVCMQ==" title="https://zh.wikipedia.org/zh-tw/%E9%97%9C%E8%81%AF%E5%BC%8F%E8%B3%87%E6%96%99%E5%BA%AB%E7%AE%A1%E7%90%86%E7%B3%BB%E7%B5%B1">關連式資料庫管理系統（RDBMS）<i class="fa fa-external-link"></i></span>的不同之處。</p><p><img src="https://hackmd.io/_uploads/SydgRdvbp.png"></p><h2 id="第-7-週：儲存（Storage）"><a href="#第-7-週：儲存（Storage）" class="headerlink" title="第 7 週：儲存（Storage）"></a>第 7 週：<span class="exturl" data-url="aHR0cHM6Ly9hd3MuYW1hem9uLmNvbS90dy9wcm9kdWN0cy9zdG9yYWdlLw==" title="https://aws.amazon.com/tw/products/storage/">儲存（Storage）<i class="fa fa-external-link"></i></span></h2><p>需要理解 <span class="exturl" data-url="aHR0cHM6Ly9hd3MuYW1hem9uLmNvbS90dy9zMy9zdG9yYWdlLWNsYXNzZXMv" title="https://aws.amazon.com/tw/s3/storage-classes/">Amazon Simple Storage Service（Amazon S3）儲存類別<i class="fa fa-external-link"></i></span>，以及把握三種 <span class="exturl" data-url="aHR0cHM6Ly9hd3MuYW1hem9uLmNvbS90dy9zMy9zdG9yYWdlLWNsYXNzZXMvZ2xhY2llci8=" title="https://aws.amazon.com/tw/s3/storage-classes/glacier/">Glacier<i class="fa fa-external-link"></i></span> 的特性。</p><p><img src="https://hackmd.io/_uploads/r1IQDiHba.png"></p><h2 id="第-8-週：管理和管控治理（Management-and-Governance）"><a href="#第-8-週：管理和管控治理（Management-and-Governance）" class="headerlink" title="第 8 週：管理和管控治理（Management and Governance）"></a>第 8 週：<span class="exturl" data-url="aHR0cHM6Ly9hd3MuYW1hem9uLmNvbS90dy9wcm9kdWN0cy9tYW5hZ2VtZW50LWFuZC1nb3Zlcm5hbmNlLw==" title="https://aws.amazon.com/tw/products/management-and-governance/">管理和管控治理（Management and Governance）<i class="fa fa-external-link"></i></span></h2><p>在工作現場，通常需要設定 <span class="exturl" data-url="aHR0cHM6Ly9kb2NzLmF3cy5hbWF6b24uY29tL3poX3R3L3N5c3RlbXMtbWFuYWdlci9sYXRlc3QvdXNlcmd1aWRlL3Nlc3Npb24tbWFuYWdlci5odG1s" title="https://docs.aws.amazon.com/zh_tw/systems-manager/latest/userguide/session-manager.html">Session Manager<i class="fa fa-external-link"></i></span>，因此需要能夠在不查看文件的情況下進行設定。</p><p>儘管初學者可以透過 <span class="exturl" data-url="aHR0cHM6Ly96aC53aWtpcGVkaWEub3JnL3poLXR3L1RlcmFfVGVybQ==" title="https://zh.wikipedia.org/zh-tw/Tera_Term">Teraterm<i class="fa fa-external-link"></i></span> 的 SSH 連接來理解如何運作，但目前這種連接方式已經有點過時。</p><p><img src="https://hackmd.io/_uploads/S1CMwoSWa.png"></p><h2 id="第-9-週：容器、無伺服器運算、應用程式整合（Containers-and-Serverless-Computing-Application-Integration）"><a href="#第-9-週：容器、無伺服器運算、應用程式整合（Containers-and-Serverless-Computing-Application-Integration）" class="headerlink" title="第 9 週：容器、無伺服器運算、應用程式整合（Containers and Serverless Computing, Application Integration）"></a>第 9 週：<span class="exturl" data-url="aHR0cHM6Ly9hd3MuYW1hem9uLmNvbS90dy9jb250YWluZXJzLw==" title="https://aws.amazon.com/tw/containers/">容器<i class="fa fa-external-link"></i></span>、<span class="exturl" data-url="aHR0cHM6Ly9hd3MuYW1hem9uLmNvbS90dy9zZXJ2ZXJsZXNzLw==" title="https://aws.amazon.com/tw/serverless/">無伺服器運算<i class="fa fa-external-link"></i></span>、<span class="exturl" data-url="aHR0cHM6Ly9hd3MuYW1hem9uLmNvbS90dy9wcm9kdWN0cy9hcHBsaWNhdGlvbi1pbnRlZ3JhdGlvbi8=" title="https://aws.amazon.com/tw/products/application-integration/">應用程式整合<i class="fa fa-external-link"></i></span>（Containers and Serverless Computing, Application Integration）</h2><p>理解 <span class="exturl" data-url="aHR0cHM6Ly9hd3MuYW1hem9uLmNvbS90dy9lY3Mv" title="https://aws.amazon.com/tw/ecs/">Elastic Container Service（ESC）<i class="fa fa-external-link"></i></span>的結構至關重要。特別是理解<span class="exturl" data-url="aHR0cHM6Ly9kb2NzLmF3cy5hbWF6b24uY29tL3poX3R3L0FtYXpvbkVDUy9sYXRlc3QvZGV2ZWxvcGVyZ3VpZGUvdGFza19kZWZpbml0aW9ucy5odG1s" title="https://docs.aws.amazon.com/zh_tw/AmazonECS/latest/developerguide/task_definitions.html">任務定義（Task Definition）<i class="fa fa-external-link"></i></span>的必備知識 <span class="exturl" data-url="aHR0cHM6Ly9kb2NzLmRvY2tlci5jb20vZW5naW5lL3JlZmVyZW5jZS9idWlsZGVyLw==" title="https://docs.docker.com/engine/reference/builder/">Dockerfile<i class="fa fa-external-link"></i></span>。</p><p><img src="https://hackmd.io/_uploads/BJ2bwsrWa.png"></p><h2 id="第-10-週：分析、遷移、其他（Analytics-Migrations）"><a href="#第-10-週：分析、遷移、其他（Analytics-Migrations）" class="headerlink" title="第 10 週：分析、遷移、其他（Analytics, Migrations）"></a>第 10 週：分析、遷移、其他（Analytics, Migrations）</h2><p>需記住 <span class="exturl" data-url="aHR0cHM6Ly9hd3MuYW1hem9uLmNvbS90dy9raW5lc2lzL2RhdGEtc3RyZWFtcy8=" title="https://aws.amazon.com/tw/kinesis/data-streams/">Kinesis Data Streams<i class="fa fa-external-link"></i></span> 是即時的，<span class="exturl" data-url="aHR0cHM6Ly9hd3MuYW1hem9uLmNvbS90dy9raW5lc2lzL2RhdGEtZmlyZWhvc2Uv" title="https://aws.amazon.com/tw/kinesis/data-firehose/">Firehose<i class="fa fa-external-link"></i></span> 則幾乎是即時的，熟記這點或許對考試得分有所幫助。</p><p><span class="exturl" data-url="aHR0cHM6Ly9hd3MuYW1hem9uLmNvbS90dy9raW5lc2lzL2RhdGEtc3RyZWFtcy8=" title="https://aws.amazon.com/tw/kinesis/data-streams/">Kinesis Data Streams<i class="fa fa-external-link"></i></span> 是即時的：</p><p><img src="https://hackmd.io/_uploads/HywNvsSWT.png"></p><p><span class="exturl" data-url="aHR0cHM6Ly9hd3MuYW1hem9uLmNvbS90dy9raW5lc2lzL2RhdGEtZmlyZWhvc2Uv" title="https://aws.amazon.com/tw/kinesis/data-firehose/">Kinesis Data Firehose<i class="fa fa-external-link"></i></span> 是幾乎即時的：</p><p><img src="https://hackmd.io/_uploads/HJDrDoSZT.png"></p><h2 id="第-11〜12-週：SAA-認證模擬測驗"><a href="#第-11〜12-週：SAA-認證模擬測驗" class="headerlink" title="第 11〜12 週：SAA 認證模擬測驗"></a>第 11〜12 週：SAA 認證模擬測驗</h2><p>最後，建議參加「<span class="exturl" data-url="aHR0cHM6Ly9hd3MuYW1hem9uLmNvbS90dy9jZXJ0aWZpY2F0aW9uL2NlcnRpZmllZC1zb2x1dGlvbnMtYXJjaGl0ZWN0LWFzc29jaWF0ZS8=" title="https://aws.amazon.com/tw/certification/certified-solutions-architect-associate/">SAA - 解決方案架構師<i class="fa fa-external-link"></i></span>」認證考試，作為知識的客觀證明。</p><p>即使對完全沒有經驗的人來說，也能夠在三個月內取得認證資格。</p><p>然而，除了取得認證以外，透過實際操作來取得實作技能並加深理解，將能夠提高在該領域解決問題的能力以及適應力，成為更有價值的技術人員」。</p><p>測驗費用：16,500 日圓（含稅） = 150 USD<br>全年可以考試：可以線上或在最近的測試中心參加考試</p><h3 id="練習-SAA-的注意事項"><a href="#練習-SAA-的注意事項" class="headerlink" title="練習 SAA 的注意事項"></a>練習 SAA 的注意事項</h3><p>建議使用符合以下條件的練習問題集：</p><ul><li>問題的質量是否達到正式考試水平</li></ul><p><strong>解答大量質量低下的問題等同是在浪費時間</strong>，應該避免花時間在不太重要的問題上。相對於盲目地解答大量質量低下的問題，透過多次解答正式考試水平的問題 3～4 次（大約 200 到 260 道問題），將能更有效地提高技能。</p><ul><li>解答是否易於理解</li></ul><p>為了使初學者更容易理解，<strong>除了文字以外，還應提供相關的圖片解說</strong>。<br>這將有助於更深入理解概念，並提升學習效率。</p><ul><li>是否有能夠提問的環境</li></ul><p>如果在學習過程中，無法妥善解決疑問，可能會成為進入下一個主題的障礙，導致學習進度緩慢。透過有一個能夠提問的環境，將能迅速解決疑問，以順利推進學習不掉，還可以防止失去動力。</p><h2 id="最後"><a href="#最後" class="headerlink" title="最後"></a>最後</h2><p>感謝您看到最後！希望這個路線圖對於學習指標或公司培訓有所幫助。</p><p>如果對路線圖有任何想法，請在 Qiita 的評論中，或透過 <span class="exturl" data-url="aHR0cHM6Ly90d2l0dGVyLmNvbS9Bd3Nza2lsbEMvc3RhdHVzLzE2MzY4NzczMzAyNDQzMjEyODA/cz0yMA==" title="https://twitter.com/AwsskillC/status/1636877330244321280?s=20">Twitter<i class="fa fa-external-link"></i></span> 發送私訊給筆者，期待聽到建設性的回饋！</p><p>那麼！預祝擁有美好的 AWS Life！</p><blockquote><p>15th鐵人賽目錄傳送門：<span class="exturl" data-url="aHR0cHM6Ly9pdGhlbHAuaXRob21lLmNvbS50dy91c2Vycy8yMDEzNTU1OC9pcm9ubWFuLzYyOTA=" title="https://ithelp.ithome.com.tw/users/20135558/ironman/6290">https://ithelp.ithome.com.tw/users/20135558/ironman/6290<i class="fa fa-external-link"></i></span></p></blockquote>]]>
    </content>
    <id>https://heidiliu2020.github.io/ironman-2023-day-29/</id>
    <link href="https://heidiliu2020.github.io/ironman-2023-day-29/"/>
    <published>2023-10-14T03:03:01.000Z</published>
    <summary>
      <![CDATA[<blockquote>
<p>原文連結：<span class="exturl" data-url="aHR0cHM6Ly9xaWl0YS5jb20vS3Vyb2thd2FLb3VoZWkvaXRlbXMvMzgzNjgwMmZjM2EwMjg2ZjY2OGY=" title="https://qiita.com/KurokawaKouhei/items/3836802fc3a0286f668f">AWSエンジニアロードマップ2023<i class="fa fa-external-link"></i></span></p>
</blockquote>
<p>終於來到了這篇，其實當初在決定要報名今年鐵人賽的時候，是想要從 AWS 主題著手，但一方面沒什麼自信能夠在三十天內寫出完整架構，一方面也還在摸索學習的步調。</p>
<p>本篇文章將會介紹 AWS 學習指標，部分圖解和影片以日文為主，但透過 Roadmap 還有每週主題，還是能夠參考並安排自己的學習進度，架構如下所示：</p>
<ul>
<li>第 1 週：AWS 基礎知識（AWS Basics）</li>
<li>第 2～3 週：網路與內容傳遞（Networking and Content Delivery）</li>
<li>第 4 週：運算服務（Compute Services）</li>
<li>第 5 週：安全、身份和合規性（Security, Identity, and Compliance）</li>
<li>第 6 週：資料庫（Database）</li>
<li>第 7 週：儲存（Storage）</li>
<li>第 8 週：管理和管控治理（Management and Governance）</li>
<li>第 9 週：容器、無伺服器運算、應用程式整合（Containers and Serverless Computing, Application Integration）</li>
<li>第 10 週：分析、遷移、其他（Analytics, Migrations）</li>
<li>第 11〜12 週：SAA 認證模擬測驗<ul>
<li>練習 SAA 的注意事項</li>
</ul>
</li>
</ul>
<p>實際學習主要還是建議搭配 <span class="exturl" data-url="aHR0cHM6Ly9kb2NzLmF3cy5hbWF6b24uY29tL3poX3R3Lz9uYzI9aF9xbF9kb2NfZG8=" title="https://docs.aws.amazon.com/zh_tw/?nc2=h_ql_doc_do">AWS 官方文件<i class="fa fa-external-link"></i></span>，或是其他線上課程等管道來進行。此外，也給自己訂下目標，希望今年結束前能夠達成，有機會的話，到時候再來整理一篇更詳細的準備心得。</p>]]>
    </summary>
    <title>[2023 15th鐵人賽] Day29 - AWS Engineer Roadmap 2023</title>
    <updated>2026-04-23T06:14:18.233Z</updated>
  </entry>
  <entry>
    <author>
      <name>Heidi Liu</name>
    </author>
    <category term="技術學習" scheme="https://heidiliu2020.github.io/categories/%E6%8A%80%E8%A1%93%E5%AD%B8%E7%BF%92/"/>
    <category term="2023鐵人賽" scheme="https://heidiliu2020.github.io/categories/%E6%8A%80%E8%A1%93%E5%AD%B8%E7%BF%92/2023%E9%90%B5%E4%BA%BA%E8%B3%BD/"/>
    <category term="Back-End" scheme="https://heidiliu2020.github.io/categories/%E6%8A%80%E8%A1%93%E5%AD%B8%E7%BF%92/2023%E9%90%B5%E4%BA%BA%E8%B3%BD/Back-End/"/>
    <category term="Back-End" scheme="https://heidiliu2020.github.io/tags/Back-End/"/>
    <category term="ironman2023" scheme="https://heidiliu2020.github.io/tags/ironman2023/"/>
    <category term="Qiita" scheme="https://heidiliu2020.github.io/tags/Qiita/"/>
    <category term="Log" scheme="https://heidiliu2020.github.io/tags/Log/"/>
    <content>
      <![CDATA[<blockquote><p>原文連結：<span class="exturl" data-url="aHR0cHM6Ly9xaWl0YS5jb20vdGFkYXNoaXJvX25pbm9taXlhL2l0ZW1zLzE5Yzc3NDg5OGM2OGFkZDYxODVlP3V0bV9zb3VyY2U9UWlpdGElRTMlODMlOEIlRTMlODMlQTUlRTMlODMlQkMlRTMlODIlQjkmdXRtX2NhbXBhaWduPTUwN2YwOTU1NGMtUWlpdGFfbmV3c2xldHRlcl81ODNfMDlfMTMmdXRtX21lZGl1bT1lbWFpbCZ1dG1fdGVybT0wX2U0NGZlYWEwODEtNTA3ZjA5NTU0Yy02MjgyMDQ0OQ==" title="https://qiita.com/tadashiro_ninomiya/items/19c774898c68add6185e?utm_source=Qiita%E3%83%8B%E3%83%A5%E3%83%BC%E3%82%B9&utm_campaign=507f09554c-Qiita_newsletter_583_09_13&utm_medium=email&utm_term=0_e44feaa081-507f09554c-62820449">今さら聞けないログの基本と設計指針 - Qiita<i class="fa fa-external-link"></i></span></p></blockquote><p>在進行程式開發時，不論是前後端，應該都對 Log（日誌）再熟悉不過。尤其是在遇到非預期性的錯誤時，通常會需要立即調閱 Log，查看是否顯示錯誤訊息；或是在改良軟體時，需要透過 Log 來分析使用者行為等等。</p><p>接下來這篇文章，將會針對 Log（日誌）主題進行介紹，從 Log 相關的基礎知識、設計方法、操作時的注意事項，再到實際應用說明。 </p><span id="more"></span><p>文章架構如下：</p><ul><li>為什麼要輸出 Log？</li><li>為什麼要設計 Log？</li><li>有關 Log 的基礎知識<ul><li>Log 的級別與意義</li><li>Log 的輸出位置</li><li>Log 格式</li></ul></li><li>Log 的設計方法介紹<ul><li>設計 Log 時的確認事項與技巧</li><li>操作 Log 時的注意事項</li></ul></li><li>對實際的 Log 輸出進行說明<ul><li>基於 VM 的 Log 輸出</li><li>基於 Docker 的 Log 輸出</li><li>設計 Log 時不該做什麼</li></ul></li></ul><p>那麼，以下正文開始。</p><hr><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>各位對於 Log（日誌）的理解是什麼呢？可能有從機制到設計方法都完全理解的工程師，或者只是隨便用用的工程師存在。</p><p>Log 是系統中按照時間順序記錄，關於錯誤或故障發生、使用者操作或設定變更、與外部通信等內容。透過深入瞭解有關 Log 的知識，將能夠進行複雜的系統開發和運用。此外，如果使用 AWS、Azure、GCP 等雲端服務，不只能夠實現系統開發，還可能節省經費開支。</p><p>本文將介紹 Log 的基本設計方法。如果有任何疑問，請繼續閱讀下去。</p><h2 id="為什麼要輸出-Log？"><a href="#為什麼要輸出-Log？" class="headerlink" title="為什麼要輸出 Log？"></a>為什麼要輸出 Log？</h2><p>在討論 Log 的基本知識和 Log  的設計之前，首先必須瞭解為什麼需要輸出 Log。</p><p>主要考慮以下四個理由：</p><ul><li>在問題發生時進行調查</li><li>為了防止問題發生</li><li>通過資料分析來改良軟體</li><li>作為審計日誌使用</li></ul><p>通過輸出 Log，不只能夠調查和防止問題，還可以分析使用者的行為。或許目前還不太能感覺到輸出 Log 的必要性，但將來肯定會變得不可或缺。</p><h2 id="為什麼要設計-Log？"><a href="#為什麼要設計-Log？" class="headerlink" title="為什麼要設計 Log？"></a>為什麼要設計 Log？</h2><p>那麼，為什麼需要設計 Log 呢？</p><p>首先，Log 的設計將決定 Log 輸出的訊息和格式，基本方針是根據 Log 的使用目的來反推設計。</p><p>而設計 Log 的原因在於，將有助於選擇符合目的的 Log 並適當顯示。如上所述，Log 存在各種不同的類型，也因為如此，如何進行選擇和顯示其實相當複雜。</p><p>例如，以下將介紹使用 AWS 等雲端服務，進行系統開發的範例。雲端服務可以輕鬆實現橫向擴充和縮減，是非常方便的功能。</p><p>然而，容易擴展也意味著，可能存在大量不知道何時會出現或消失的伺服器。因此，在雲端系統中，各伺服器的 Log 會非同步寫入，即使將範圍縮小到毫秒單位，也很難找到特定的 Log。</p><p><img src="https://hackmd.io/_uploads/rJGyp9rZT.png"></p><p>因此，Log 的設計就變得非常重要。透過為特定事件的請求和回應，分配唯一可識別的 ID，即可藉此搜尋與目標事件相關的 Log。透過提前設計 Log，將有助於大幅減少 Log 搜尋時間。</p><p>以上只是一個範例，但建議在開發和操作複雜的系統時，務必進行 Log 設計。</p><h2 id="有關-Log-的基礎知識"><a href="#有關-Log-的基礎知識" class="headerlink" title="有關 Log 的基礎知識"></a>有關 Log 的基礎知識</h2><p>在介紹 Log 的設計之前，這裡先來瞭解有關 Log 的基礎知識！以下將簡單解釋相關內容，因此可以只挑選還不熟悉的部分閱讀。</p><ul><li>Log 的級別</li><li>Log 的輸出位置</li><li>Log 的格式</li></ul><h3 id="Log-的級別與意義"><a href="#Log-的級別與意義" class="headerlink" title="Log 的級別與意義"></a>Log 的級別與意義</h3><p>Log 具備權限分析、故障調查、安全性和稽核等多種用途。</p><p>下表整理出代表 Log 重要性的等級和類型，內容可能會依據所使用的開發語言、Log 管理系統和設計而有所變化，但基本如下所示：</p><table><thead><tr><th>種類</th><th>意思</th></tr></thead><tbody><tr><td>EMERG</td><td>系統不可利用</td></tr><tr><td>ALERT</td><td>必須立即採取行動</td></tr><tr><td>CRIT</td><td>嚴重缺失狀況</td></tr><tr><td>ERROR</td><td>發生錯誤的狀態</td></tr><tr><td>WARN</td><td>被警告的狀態</td></tr><tr><td>NOTICE</td><td>正常但重要的情況</td></tr><tr><td>INFO</td><td>資訊訊息</td></tr><tr><td>DEBUG</td><td>有關系統如何運作的訊息</td></tr></tbody></table><h3 id="Log-的輸出位置"><a href="#Log-的輸出位置" class="headerlink" title="Log 的輸出位置"></a>Log 的輸出位置</h3><p>原則上，Log 應該輸出到開發人員和維運人員容易找到的位置。如果想輸出到文件，建議創建一個 <code>log</code> 的目錄。基本上，預期會輸出以下四個位置：</p><ul><li><strong>輸出到文檔</strong><ul><li>這種方法適合在控制台以外啟動的應用程式。</li></ul></li><li><strong>標準輸出</strong><ul><li>這種方法適合用於從控制台啟動的應用程式，用於輸出中間過程等資訊。</li></ul></li><li><strong>輸出到外部日誌管理工具的文檔</strong><ul><li>如果可以使用外部日誌管理工具，建議將 Log 輸出到專用的記錄位置。</li></ul></li><li><strong>輸出到外部系統</strong><ul><li>為了方便開發者和運維人員的工作和溝通，有時會將 Log 輸出到如 Slack 等聊天工具中。但需注意維護適當的運行效率，避免輸出過多的 Log。</li></ul></li></ul><p>基本上，若使用外部日誌管理系統，或在雲端環境進行開發，這些專用工具通常會輸出到適當的位置。以雲端環境為例，如  Azure Application Insights 或  Amazon CloudWatch Logs 等工具。</p><p>此外，需要注意的是，不應該將 Log 輸出到未指定數量的使用者可以訪問的位置，或將系統內重要的資訊輸出到 Log，這通常被視為禁忌。</p><h3 id="Log-格式"><a href="#Log-格式" class="headerlink" title="Log 格式"></a>Log 格式</h3><p>Log 格式將決定 Log 輸出的內容。 例如輸出以下表格中的項目：</p><table><thead><tr><th>項目</th><th>内容</th><th>備註</th></tr></thead><tbody><tr><td>時間</td><td>記錄 Log 的時間</td><td>年月日時分秒毫秒。最少必須以毫秒為單位輸出</td></tr><tr><td>事件 ID</td><td>事件的 ID</td><td>追蹤事件序列時所需的</td></tr><tr><td>Log 級別</td><td>Log 的級別</td><td>INFO、ERROR 等</td></tr><tr><td>請求目標</td><td>對哪裡提出的請求</td><td>URL 等</td></tr><tr><td>使用者資訊</td><td>請求的使用者資訊</td><td>使用者 ID 和 IP 位置等</td></tr><tr><td>執行對象</td><td>對什麼進行處理</td><td>已執行的來源 ID 等</td></tr><tr><td>執行內容</td><td>執行什麼樣的處理</td><td>DELETE、PUT、GET 等</td></tr><tr><td>執行結果</td><td>執行結果是什麼</td><td>成功、失敗、執行件數等</td></tr><tr><td>訊息</td><td>其他要輸出的內容</td><td>Log 級別若大於 Error 時會希望顯示的訊息</td></tr></tbody></table><p>Log 輸出基本上以 5W1H 為基礎，並且輸出必要的資訊，不應過多或不足。</p><p>建議注意以下內容：</p><p>When → 何時執行該程序</p><p>Who → 是誰執行該程序</p><p>Where → 在哪執行該程序</p><p>What → 執行該程序做什麼</p><p>Why → 為什麼執行該程序</p><p>例如，以下就是一個理想格式的範例：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">(Log 級數)　(時間)　(IP 地址)　(執行對象)　(執行結果)　(訊息)</span><br></pre></td></tr></table></figure><p>此格式的輸出 Log 如下所示：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[Error] Feb 21 12:32:23, 193.121.123, https-8080, Failed, client denied by server</span><br></pre></td></tr></table></figure><p>輸出的困難點在於，並非所有可輸出的訊息都要進行輸出，在進入 Log 設計之前，必須先釐清使用用途，這部分將在後半章節進行介紹。</p><p>以上資訊作為參考，接下來讓我們更深入理解 Log 設計吧！</p><h2 id="Log-的設計方法介紹"><a href="#Log-的設計方法介紹" class="headerlink" title="Log 的設計方法介紹"></a>Log 的設計方法介紹</h2><p>這裡開始，將會對 Log 的設計方法進行說明。</p><p>在設計 Log 時，比起建立具突破性的出色設計，更重要的是不犯錯誤。對此，接下來將對確認事項、技巧和需注意的部分進行說明。</p><p>首先是針對設計時的確認事項和技巧進行說明！</p><h3 id="設計-Log-時的確認事項與技巧"><a href="#設計-Log-時的確認事項與技巧" class="headerlink" title="設計 Log 時的確認事項與技巧"></a>設計 Log 時的確認事項與技巧</h3><p>接下來，將會說明設計 Log 時必須確認的要點，以及有關設計的技巧。透過牢記這些內容，將能夠有效進行 Log 分析。反之，如果忽略這些要點，Log 將變得毫無用處。</p><p>先是針對確認內容進行說明。</p><h4 id="・Log-的使用目的是什麼？"><a href="#・Log-的使用目的是什麼？" class="headerlink" title="・Log 的使用目的是什麼？"></a>・Log 的使用目的是什麼？</h4><p>Log 可使用於多種用途，如本文開頭附上的「Log 的類型與使用目的」表所介紹。正因為用途非常多樣，因此有必要確立使用目的。不只是開發人員，包括維運人員、企劃和法務部門等相關利益者的意見，都必須納入考量。</p><p>舉例來說，Log 的使用目的可能有以下三種：</p><ul><li>瞭解來自外部未經授權的訪問</li><li>防止資訊洩露等的內部控制</li><li>監控系統的使用狀態</li></ul><p>請確認適用於哪一個項目。</p><h4 id="・Log-的讀者是誰？"><a href="#・Log-的讀者是誰？" class="headerlink" title="・Log 的讀者是誰？"></a>・Log 的讀者是誰？</h4><p>確定使用目的後，應該考慮 Log 的讀者是誰。這些讀者可能包括開發人員、運維人員、客戶公司的員工等各種對象。根據不同的讀者，可能需要不同的輸出方式和輸出內容，因此，結合使用目的來考慮將會更理想。</p><p>例如，根據使用目的預期目標讀者，可以得出以下結果：</p><table><thead><tr><th>使用目的</th><th>讀者</th></tr></thead><tbody><tr><td>瞭解來自外部未經授權的訪問</td><td>運維人員、客戶公司的員工、法務人員、系統負責人等</td></tr><tr><td>防止資訊洩露等的內部控制</td><td>開發人員、運維人員、客戶公司的員工、法務人員等</td></tr><tr><td>監控系統的使用狀態</td><td>開發人員、運維人員等</td></tr></tbody></table><p>掌握這些確認事項後，設計 Log 的準備工作也差不多完成。</p><p>接下來，將介紹設計 Log 時需考慮的要點。</p><h4 id="・考慮使用哪種格式"><a href="#・考慮使用哪種格式" class="headerlink" title="・考慮使用哪種格式"></a>・考慮使用哪種格式</h4><p>在實際設計 Log 時，需要確定 Log 的格式。Log 的格式代表 Log 中包含哪些資訊，這部分必須和 Log 的目標讀者討論並確定格式。</p><p>可參考以下格式：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">(Log 級數)　(時間)　(IP 地址)　(執行對象)　(執行結果)　(訊息)</span><br></pre></td></tr></table></figure><p>使用這種格式輸出的 Log，將如下所示：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[Error] Feb 21 12:32:23, 193.121.123, https-8080, Failed, client denied by server</span><br></pre></td></tr></table></figure><p>在這裡，可以考慮添加 Log 讀者的需求。例如，如果目標對象是法務人員，那麼列出 IP 地址和執行對象資訊，可能不會有太大意義。</p><p>因此，將訊息部分移至 Log 的前半部等其他改進，或許會更有幫助。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">(Log 級數)　(時間)　(訊息)　(IP 地址)　(執行對象)　(執行結果)</span><br></pre></td></tr></table></figure><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[Error] Feb 21 12:32:23, client denied by server, 193.121.123, https-8080, Failed</span><br></pre></td></tr></table></figure><h4 id="・思考包括什麼樣的訊息"><a href="#・思考包括什麼樣的訊息" class="headerlink" title="・思考包括什麼樣的訊息"></a>・思考包括什麼樣的訊息</h4><p>Log 格式中，包括一個名為訊息的項目。Log 扮演的角色，是以易於理解的方式傳達系統狀態。透過充分利用這些訊息，將能發揮其效用至最大限度。如果是在輸出錯誤 Log 的情況，則務必描述預期錯誤的原因。</p><p>此外，也必須考慮設計符合潛在讀者 IT 素養的訊息。假設使用目的是「瞭解來自外部未經授權的訪問」，那麼非技術人員可能也會閱讀這些訊息，因此應該以簡單的語言解釋問題和錯誤發生原因，即使沒有專業知識背景，也能夠理解內容。</p><h4 id="・斟酌選擇-Log-級別"><a href="#・斟酌選擇-Log-級別" class="headerlink" title="・斟酌選擇 Log 級別"></a>・斟酌選擇 Log 級別</h4><p>正如本文前半部分提供的「Log 級別和含義」表中所示，Log 具有級別的概念。透過定義級別，能夠在達到一定程度以上級別的訊息時通知負責人，同時避免收到不必要的通知造成干擾。</p><p>具體來說，除了「監控系統的使用狀態」以外的目的，並不需要使用到 DEBUG 級別的 Log。如果使用目的為「防止資訊洩漏等內部控制」，即使出現無法使用系統的 EMERG 的 Log，也無法有任何作為，因此沒有這麼必要。</p><p>請參考這些內容來設計 Log。</p><h3 id="操作-Log-時的注意事項"><a href="#操作-Log-時的注意事項" class="headerlink" title="操作 Log 時的注意事項"></a>操作 Log 時的注意事項</h3><p>接下來，將會介紹操作 Log 時的注意事項，主要是避免在搜索目標 Log 時出現問題。</p><h4 id="將-Log-檔案分割處理"><a href="#將-Log-檔案分割處理" class="headerlink" title="將 Log 檔案分割處理"></a>將 Log 檔案分割處理</h4><p>在調查 Log 時，可以透過減少目標 Log的數量來縮短調查時間。 為此，建議將 Log 檔進行分割處理，例如：按時間或網站為單位來分割 Log 檔，藉此提升工作效率。</p><p>如果使用的是 <span class="exturl" data-url="aHR0cHM6Ly96aC53aWtpcGVkaWEub3JnL3poLXR3LyVFNiU5NyVBNSVFNSVCRiU5NyVFOCVCRCVBRSVFNiU5QiVCRg==" title="https://zh.wikipedia.org/zh-tw/%E6%97%A5%E5%BF%97%E8%BD%AE%E6%9B%BF">logrotate<i class="fa fa-external-link"></i></span>，並希望分割 Log 檔案時，則應修改位於 <code>etc/logrotate.d</code> 的日誌輪替（Log Rotation）設定檔。</p><p>舉例來說，<code>sample-service</code> 使用的 Log 設定檔位於 <code>etc/logrotate.d/sample-service</code> ，內容如下所示：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">/var/log/sample-service/sample.log &#123; <span class="comment"># 目標 Log 檔案</span></span><br><span class="line">    ifempty            <span class="comment"># 即使 Log 檔案為空也進行輪替</span></span><br><span class="line">    missingok          <span class="comment"># 即使沒有 Log 檔案也不會發出錯誤</span></span><br><span class="line">    compress           <span class="comment"># 進行壓縮</span></span><br><span class="line">    daily              <span class="comment"># 毎日輪替</span></span><br><span class="line">    rotate 10          <span class="comment"># 保留最近 10 個分割後的 Log 檔案</span></span><br><span class="line">    create             <span class="comment"># 更新後建立一個新的 Log 檔案</span></span><br><span class="line">    endscript</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><code>create</code> 這個指令，可用於分割 Log 檔案，因此請確認檔案中是否有此指令。</p><p>此外，如果希望新建立的 Log 檔能包含更新日期，請執行以下操作：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">/var/log/sample-service/sample.log &#123; <span class="comment"># 目標 Log 檔案</span></span><br><span class="line">    ifempty            <span class="comment"># 即使 Log 檔案為空也進行輪替</span></span><br><span class="line">    missingok          <span class="comment"># 即使沒有 Log 檔案也不會發出錯誤</span></span><br><span class="line">    compress           <span class="comment"># 進行壓縮</span></span><br><span class="line">    daily              <span class="comment"># 毎日輪替</span></span><br><span class="line">    rotate 10          <span class="comment"># 保留最近 10 個分割後的 Log 檔案</span></span><br><span class="line">    dateext            <span class="comment"># 建立一個包含更新日期的檔案</span></span><br><span class="line">    endscript</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>透過添加 <code>dateext</code> 這個指令，檔名將會更改為 <code>原檔案名稱 + -YYYYMMDD</code></p><h4 id="採用結構化日誌記錄"><a href="#採用結構化日誌記錄" class="headerlink" title="採用結構化日誌記錄"></a>採用結構化日誌記錄</h4><p>如果情況允許，請務必採用結構化日誌記錄（Structured Logging）。 結構化日誌記錄是透過標準化 Log 輸出格式，並作為結構化資料合集而非單文本處理，常見格式為 Json。 結構化日誌記錄不只易於和其他工具連結，還具備高性能搜索等優點。</p><p>請參考以下範例：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[Error] Feb 21 12:32:23, 193.121.123, https-8080, Failed, client denied by server</span><br></pre></td></tr></table></figure><p>以上顯示的 Log 會是：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">jsonPayload: &#123;</span><br><span class="line">    <span class="string">&quot;level&quot;</span>: <span class="string">&quot;[Error]&quot;</span>,</span><br><span class="line">    <span class="string">&quot;timestamp&quot;</span>: <span class="string">&quot;Feb 21 12:32:23&quot;</span>,</span><br><span class="line">    <span class="string">&quot;host&quot;</span>: <span class="string">&quot;193.121.123&quot;</span>,</span><br><span class="line">    <span class="string">&quot;port-number&quot;</span>: <span class="string">&quot;https-8080&quot;</span>,</span><br><span class="line">    <span class="string">&quot;result&quot;</span>: <span class="string">&quot;Failed&quot;</span>,</span><br><span class="line">    <span class="string">&quot;message&quot;</span>: <span class="string">&quot;client denied by server&quot;</span>,</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>可以像這樣進行結構化。</p><p>對人類來說可能有點難以閱讀，但從機器的角度來看，結構化日誌將更容易閱讀，因此建議導入使用。</p><h4 id="時間同步和時區設定"><a href="#時間同步和時區設定" class="headerlink" title="時間同步和時區設定"></a>時間同步和時區設定</h4><p>在調查 Log 時，可能想知道該 Log 的輸出時間。如果未完成伺服器和本地時間同步，或沒有設定相同時區，將無法好好處理時間。此外，若是更改設定，則需重新啟動伺服器以使其生效！</p><p>若要更改本地時間 <code>/etc/localtime</code> ，請在終端機輸入以下指令：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">// 東京時區</span><br><span class="line"><span class="built_in">cp</span> /usr/share/zoneinfo/Asia/Tokyo /etc/localtime</span><br><span class="line"></span><br><span class="line">// 台北時區</span><br><span class="line"><span class="built_in">cp</span> /usr/share/zoneinfo/Asia/Taipei /etc/localtime</span><br></pre></td></tr></table></figure><p>接著輸入 <code>date</code> 指令，確認是否顯示以下內容（是否為 JST）：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Wed Dec  12 13:15:11 JST 2015</span><br></pre></td></tr></table></figure><p>此外，可透過 <code>/etc/timezone</code> 檢查時區。 輸入 <code>$ cat /etc/timezone</code> 指令後，檢查輸出是否類似以下內容：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">Asia/Tokyo</span><br><span class="line"></span><br><span class="line">Asia/Taipei</span><br></pre></td></tr></table></figure><p>如果輸出結果不同，請試著重寫內容。</p><h4 id="確認日誌輪替的基本設定"><a href="#確認日誌輪替的基本設定" class="headerlink" title="確認日誌輪替的基本設定"></a>確認日誌輪替的基本設定</h4><p>日誌輪替（Log rotation）是種按照一定容量或時間間隔，刪除較舊的 Log 或拆分檔案，以防止 Log 無限增長的功能。在執行日誌輪替時，應確認使用指令的預設配置。輪替不一定是在 0 點進行，有時可能也無法按日期拆分檔案。</p><p>如果要更改日誌輪替的時間，請按照以下步驟進行（以使用 logrotate 為例）。</p><p>首先，從正在運行 logrotate 的 <code>etc/cron.daily</code> 中，刪除 anacron 設定，請註解或刪除以下行列：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[hour]     [minutes]     cron.daily     <span class="built_in">nice</span> run-parts /etc/cron.daily</span><br></pre></td></tr></table></figure><p>接下來，在 crontab 中註冊 <code>/etc/cron.daily</code> 寫上希望運行的時間。 具體來說，就是開啟 <code>etc/crontab</code> 檔案，並添加以下行列：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">23     59     cron.daily     root run-parts /etc/cron.daily</span><br></pre></td></tr></table></figure><p>如此一來，日誌輪換將會在 23:59 進行。</p><h4 id="使用-Dokcer-時需注意"><a href="#使用-Dokcer-時需注意" class="headerlink" title="使用 Dokcer 時需注意"></a>使用 <span class="exturl" data-url="aHR0cHM6Ly96aC53aWtpcGVkaWEub3JnL3poLXR3L0RvY2tlcg==" title="https://zh.wikipedia.org/zh-tw/Docker">Dokcer<i class="fa fa-external-link"></i></span> 時需注意</h4><p>如果使用 Docker 的情況，可能會在 Docker 內部容器中的檔案輸出 Log。在這種情況下，若是重新建立 Docker 容器，將導致 Log 輸出檔案也一併被刪除，因此請務必更改輸出位置。</p><p>想要更改輸出位置，請在啟動 Docker 時執行以下指令：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker run --log-driver=&lt;Driver Name&gt;</span><br></pre></td></tr></table></figure><p>本文的後半部分，將會詳細介紹有關 Docker 的 Log 輸出。如果對 Driver（驅動程式）部分感到疑惑，也請繼續往下閱讀。</p><h4 id="使用-CDN-或反向代理時需注意"><a href="#使用-CDN-或反向代理時需注意" class="headerlink" title="使用 CDN 或反向代理時需注意"></a>使用 CDN 或反向代理時需注意</h4><p>如果使用 CDN 或反向代理，需注意其 IP 地址可能會被輸出為 Log。但由於 Client 端的 IP 地址已經正確記錄在別處的標頭欄位中，因此需要更改設定以記錄相關內容。</p><ul><li><span class="exturl" data-url="aHR0cHM6Ly96aC53aWtpcGVkaWEub3JnL3dpa2kvTmdpbng=" title="https://zh.wikipedia.org/wiki/Nginx">Nginx<i class="fa fa-external-link"></i></span></li><li><span class="exturl" data-url="aHR0cHM6Ly96aC53aWtpcGVkaWEub3JnL3poLXR3L1Zhcm5pc2hfY2FjaGU=" title="https://zh.wikipedia.org/zh-tw/Varnish_cache">Varnish<i class="fa fa-external-link"></i></span></li><li><span class="exturl" data-url="aHR0cHM6Ly96aC53aWtpcGVkaWEub3JnL3dpa2kvVHJhZmZpY19TZXJ2ZXI=" title="https://zh.wikipedia.org/wiki/Traffic_Server">Apache Traffic Server<i class="fa fa-external-link"></i></span></li><li><span class="exturl" data-url="aHR0cHM6Ly96aC53aWtpcGVkaWEub3JnL3poLXR3L0hBUHJveHk=" title="https://zh.wikipedia.org/zh-tw/HAProxy">HAProxy<i class="fa fa-external-link"></i></span></li></ul><p>更改設定的方法，將根據反向代理而有所差異。 以上列舉幾項反向代理，建議根據自己使用的工具，查詢更改設定的相關方法。</p><p>但我希望您能找到如何使用您正在使用的內容更改設置。</p><h2 id="對實際的-Log-輸出進行說明"><a href="#對實際的-Log-輸出進行說明" class="headerlink" title="對實際的 Log 輸出進行說明"></a>對實際的 Log 輸出進行說明</h2><p>到目前為止，已介紹通用的 Log 設計相關基礎知識。在最後部分中，將藉由一系列的情境來說明 Log 輸出的不同階段。</p><p>在實際輸出 Log 時，總共有三個階段：</p><ul><li><strong>日誌輸出 Log Output</strong><ul><li>是以什麼格式，在何時輸出</li></ul></li><li><strong>日誌輪換 Log Rotation</strong><ul><li>如何處理不斷增長的日誌數量</li></ul></li><li><strong>日誌聚合 Log Aggregation</strong><ul><li>如何長時間保存日誌</li></ul></li></ul><p>在每個階段中，都需要注意不同事項。以下是針對各種情境的注意事項進行說明。</p><h3 id="基於-VM-的-Log-輸出"><a href="#基於-VM-的-Log-輸出" class="headerlink" title="基於 VM 的 Log 輸出"></a>基於 VM 的 Log 輸出</h3><p>首先，將對基於 VM（虛擬機器）的 Log 輸出流程進行說明。舉例來說，對於在 AWS 的 EC2 上運行的應用程式，在上述三個階段（誌輸出、日誌輪替、日誌集約）當中，應特別注意「日誌輸出」。</p><h4 id="日誌輸出"><a href="#日誌輸出" class="headerlink" title="日誌輸出"></a>日誌輸出</h4><p>通常最初想到的輸出方法，是由應用程式直接輸出到 Log 檔案的模式，這是許多框架的預設配置。</p><p>另一方面，被稱為應用程式開發的最佳實踐 <span class="exturl" data-url="aHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvVHdlbHZlLUZhY3Rvcl9BcHBfbWV0aG9kb2xvZ3k=" title="https://en.wikipedia.org/wiki/Twelve-Factor_App_methodology">The Twelve-factor App（12 要素應用程式）<i class="fa fa-external-link"></i></span>，則表示應用程式生成的 Log 不應該直接由 Log 檔案進行管理。<br>而作為替代方案，建議採取以下方法：</p><ul><li>以標準輸出的方式輸出日誌，並使用日誌管理系統將其輸出到檔案中</li></ul><p>透過這種方式，在測試或部署環境中，將無法透過應用程式查看或設定 Log，只能由執行環境進行全面管理。</p><h3 id="基於-Docker-的-Log-輸出"><a href="#基於-Docker-的-Log-輸出" class="headerlink" title="基於 Docker 的 Log 輸出"></a>基於 Docker 的 Log 輸出</h3><p>如「操作 Log 時的注意事項」段落所述，在 Docker 容器中，刪除容器時會將連同所有寫入的資料一起被棄用。換句話說，基於 Docker 的 Log 輸出之所以困難，在於日誌聚合。為了正確執行日誌聚合，可參考以下兩種方法：</p><ul><li>指定 volume 目標並永久保存</li><li>使用 log driver 傳輸</li></ul><p>以下將會逐一進行介紹。</p><h4 id="指定-volume-目標並永久保存"><a href="#指定-volume-目標並永久保存" class="headerlink" title="指定 volume 目標並永久保存"></a>指定 volume 目標並永久保存</h4><p>透過使用 volume，將能夠永久保存主機的資料。為了避免中斷引用，請使用 <code>--volumes-from &lt;container_name&gt;</code> 或 <code>--v &lt;host_path&gt;:&lt;container_path&gt;</code> 將其保存在資料容器中。</p><p>然而另一方面，若是因為<span class="exturl" data-url="aHR0cHM6Ly96aC53aWtpcGVkaWEub3JnL3poLXR3LyVFNSVCQyVCOSVFNiU4MCVBNyVFNCVCQyVCOCVFNyVCQyVBOQ==" title="https://zh.wikipedia.org/zh-tw/%E5%BC%B9%E6%80%A7%E4%BC%B8%E7%BC%A9">自動規模化<i class="fa fa-external-link"></i></span>等功能，導致實例本身也被頻繁刪除的情況，則必須將 Log 轉移到其他位置保存。在這種情況下，則必須準備一個類似於 <span class="exturl" data-url="aHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvRmx1ZW50ZA==" title="https://en.wikipedia.org/wiki/Fluentd">fluentd<i class="fa fa-external-link"></i></span> 容器，透過能夠將收集的資料暫存到緩衝區的機制，將 volume 掛載（mount）以進行傳送等處理。</p><h4 id="使用-Logging-Driver-傳輸"><a href="#使用-Logging-Driver-傳輸" class="headerlink" title="使用 Logging Driver 傳輸"></a>使用 Logging Driver 傳輸</h4><p>透過 Docker 提供的日誌記錄驅動機制（Logging Driver），能夠將 Log 傳輸到外部日誌記錄軟體，即使是在結構複雜的容器，也能夠實現日誌聚合。</p><p>在 Logging Driver 中，可以在容器啟動時指定輸出目標，將 Log 輸出到指定的日誌管理系統。通常使用「json-file」Driver，但從 Docker 1.12 版本開始，還提供以下幾種類型的 Driver 使用：</p><table><thead><tr><th>Driver</th><th>輸出目標</th></tr></thead><tbody><tr><td>json-file</td><td>以 Json 格式儲存在檔案</td></tr><tr><td>（Docker Default）</td><td></td></tr><tr><td>none</td><td>不記錄 Log</td></tr><tr><td>syslog</td><td>syslog</td></tr><tr><td>journald</td><td>journald</td></tr><tr><td>gelf</td><td>支援 gelf 的 Log 管理系統</td></tr><tr><td>fluentd</td><td>Log 管理工具「fluentd」</td></tr><tr><td>awslogs</td><td>AWS 提供的 Log 管理系統「Amazon CloudWatch Logs」</td></tr><tr><td>splunk</td><td>Log 管理系統「Splunk」</td></tr><tr><td>etwlogs</td><td>Windows 的 Event Tracing for Windows</td></tr><tr><td>gcplogs</td><td>GCP 提供的 Log 管理系統「Google Cloud Logging」</td></tr></tbody></table><p>若要實際指定 Driver，可在容器啟動時輸入 <code>docker run --log-driver=&lt;driver name&gt;</code>。如果只輸入 <code>docker run</code>，則會使用預設的「json-file」Driver。</p><h3 id="設計-Log-時不該做什麼"><a href="#設計-Log-時不該做什麼" class="headerlink" title="設計 Log 時不該做什麼"></a>設計 Log 時不該做什麼</h3><p>最後要介紹的，是設計 Log 時不應該執行的兩件事。</p><h4 id="不輸出機密資訊"><a href="#不輸出機密資訊" class="headerlink" title="不輸出機密資訊"></a>不輸出機密資訊</h4><p>導致資訊洩露的原因之一，就是 Log 輸出機密資訊。 對於應該只有開發人員和負責人才能知道 Log，是否就此感到安心了？ 事實上，透過未經授權的訪問，將有可能查看 Log。 因此為了以防萬一，請再三確保重要的機密資訊不會作為 Log 輸出。</p><p>請注意並確保重要的機密資訊不會作為日誌輸出，</p><p>如下所示：</p><ul><li>姓名</li><li>地址</li><li>電話號碼</li><li>電子郵件地址</li><li>密碼</li><li>Access Token</li><li>信用卡號碼</li></ul><p>等機密資訊。</p><h4 id="不輸出不必要的資訊"><a href="#不輸出不必要的資訊" class="headerlink" title="不輸出不必要的資訊"></a>不輸出不必要的資訊</h4><p>不輸出不必要的資訊，原因有以下三種：</p><ul><li>若增加 Log 的大小，將會增加服務使用費。</li><li>進行調查時可能造成妨礙</li><li>將導致系統性能下降</li></ul><h2 id="總結"><a href="#總結" class="headerlink" title="總結"></a>總結</h2><p>在本文中，介紹了如何設計 Log！ 如果正在進行開發，一定會使用到這些 Log，希望從明天開始就能夠實際應用開發中！</p><p>敝公司 Nuco 目前正在招募中，詳細可<span class="exturl" data-url="aHR0cHM6Ly93d3cucmVjcnVpdC5udWNvLmNvLmpwLz9xaWl0YV9pdGVtX2lkPTE5Yzc3NDg5OGM2OGFkZDYxODVl" title="https://www.recruit.nuco.co.jp/?qiita_item_id=19c774898c68add6185e">參考這裡<i class="fa fa-external-link"></i></span>。</p><blockquote><p>15th鐵人賽目錄傳送門：<span class="exturl" data-url="aHR0cHM6Ly9pdGhlbHAuaXRob21lLmNvbS50dy91c2Vycy8yMDEzNTU1OC9pcm9ubWFuLzYyOTA=" title="https://ithelp.ithome.com.tw/users/20135558/ironman/6290">https://ithelp.ithome.com.tw/users/20135558/ironman/6290<i class="fa fa-external-link"></i></span></p></blockquote>]]>
    </content>
    <id>https://heidiliu2020.github.io/ironman-2023-day-28/</id>
    <link href="https://heidiliu2020.github.io/ironman-2023-day-28/"/>
    <published>2023-10-13T00:46:01.000Z</published>
    <summary>
      <![CDATA[<blockquote>
<p>原文連結：<span class="exturl" data-url="aHR0cHM6Ly9xaWl0YS5jb20vdGFkYXNoaXJvX25pbm9taXlhL2l0ZW1zLzE5Yzc3NDg5OGM2OGFkZDYxODVlP3V0bV9zb3VyY2U9UWlpdGElRTMlODMlOEIlRTMlODMlQTUlRTMlODMlQkMlRTMlODIlQjkmdXRtX2NhbXBhaWduPTUwN2YwOTU1NGMtUWlpdGFfbmV3c2xldHRlcl81ODNfMDlfMTMmdXRtX21lZGl1bT1lbWFpbCZ1dG1fdGVybT0wX2U0NGZlYWEwODEtNTA3ZjA5NTU0Yy02MjgyMDQ0OQ==" title="https://qiita.com/tadashiro_ninomiya/items/19c774898c68add6185e?utm_source=Qiita%E3%83%8B%E3%83%A5%E3%83%BC%E3%82%B9&utm_campaign=507f09554c-Qiita_newsletter_583_09_13&utm_medium=email&utm_term=0_e44feaa081-507f09554c-62820449">今さら聞けないログの基本と設計指針 - Qiita<i class="fa fa-external-link"></i></span></p>
</blockquote>
<p>在進行程式開發時，不論是前後端，應該都對 Log（日誌）再熟悉不過。尤其是在遇到非預期性的錯誤時，通常會需要立即調閱 Log，查看是否顯示錯誤訊息；或是在改良軟體時，需要透過 Log 來分析使用者行為等等。</p>
<p>接下來這篇文章，將會針對 Log（日誌）主題進行介紹，從 Log 相關的基礎知識、設計方法、操作時的注意事項，再到實際應用說明。 </p>]]>
    </summary>
    <title>[2023 15th鐵人賽] Day28 - 事到如今問不出口的 Log 基礎和設計指南</title>
    <updated>2026-04-23T06:14:18.232Z</updated>
  </entry>
  <entry>
    <author>
      <name>Heidi Liu</name>
    </author>
    <category term="技術學習" scheme="https://heidiliu2020.github.io/categories/%E6%8A%80%E8%A1%93%E5%AD%B8%E7%BF%92/"/>
    <category term="2023鐵人賽" scheme="https://heidiliu2020.github.io/categories/%E6%8A%80%E8%A1%93%E5%AD%B8%E7%BF%92/2023%E9%90%B5%E4%BA%BA%E8%B3%BD/"/>
    <category term="入門篇" scheme="https://heidiliu2020.github.io/categories/%E6%8A%80%E8%A1%93%E5%AD%B8%E7%BF%92/2023%E9%90%B5%E4%BA%BA%E8%B3%BD/%E5%85%A5%E9%96%80%E7%AF%87/"/>
    <category term="ironman2023" scheme="https://heidiliu2020.github.io/tags/ironman2023/"/>
    <category term="Qiita" scheme="https://heidiliu2020.github.io/tags/Qiita/"/>
    <category term="提問方法" scheme="https://heidiliu2020.github.io/tags/%E6%8F%90%E5%95%8F%E6%96%B9%E6%B3%95/"/>
    <content>
      <![CDATA[<blockquote><p>原文連結：<span class="exturl" data-url="aHR0cHM6Ly9xaWl0YS5jb20vZGV2LXRhdHN1eWEvaXRlbXMvYmNhMzg2YmM0NzMyMmYyMjA3MmE=" title="https://qiita.com/dev-tatsuya/items/bca386bc47322f22072a">[あるある]「詰まったら、すぐに質問してください」の克服法 - Qiita<i class="fa fa-external-link"></i></span></p></blockquote><p>倒數三天！不知不覺也來到第二十七天，終於快看到終點了似乎有點感概XD</p><p>接下來這篇文章，是打從自己在<span class="exturl" data-url="aHR0cHM6Ly9oZWlkaS1jb2RpbmcubWVkaXVtLmNvbS8lRTglQkQlODklRTglODElQjclRTUlODklOEQlRTclQUIlQUYlRTUlQjclQTUlRTclQTglOEIlRTUlQjglQUItbGlkZW15LSVFNyVCNSU5MCVFNiVBNSVBRCVFNSVCRiU4MyVFNSVCRSU5NyVFOCU4OCU4NyVFNiVCMSU4MiVFOCU4MSVCNyVFNyVCOCVCRCVFNyVCNSU5MC05ODA2NDRlYjRmNzQ=" title="https://heidi-coding.medium.com/%E8%BD%89%E8%81%B7%E5%89%8D%E7%AB%AF%E5%B7%A5%E7%A8%8B%E5%B8%AB-lidemy-%E7%B5%90%E6%A5%AD%E5%BF%83%E5%BE%97%E8%88%87%E6%B1%82%E8%81%B7%E7%B8%BD%E7%B5%90-980644eb4f74">轉職學習程式<i class="fa fa-external-link"></i></span>的階段，直到現在第二份工作，有時仍是備感困擾的問題。因為不善於提問，或是希望先自己做好功課，等到萬不得已才找人求救，殊不知中間已經浪費太多時間，放在工作上也很有可能因此耽誤進度。</p><p>但是呢，這些過程其實是能夠訓練的，本文中提供面對問題時，應該如何善用「提問模板」訓練思考，並且著重於「輸出導向的輸入」，透過這些過程，將有助於改善語言表達能力，並有效提升學習的效果。</p><span id="more"></span><p>那麼，以下正文開始。</p><hr><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p><strong>「當遇到問題卡住時，請立刻提問！」</strong></p><p>你是否曾經被上司、PM 或資深工程師這樣告誡過？</p><p>通常這種建議可能會針對初學者，但你是否曾經有以下的經驗呢？</p><ol><li>遇到不懂的問題！</li><li>因為被告誡要「立刻」提問，所以毫不猶豫馬上提問…</li><li>( ﾟдﾟ) &lt; <strong>「難道你不會試著自己查一下嗎？」</strong> 遭到了拒絕！！！噹(´・ω・｀) <code>[註解1]</code></li></ol><p>（明明被要求馬上提問的⋯⋯(´；ω；｀)）</p><p>還是應該自己查嗎？嗯嗯⋯⋯</p><ol><li>遇到不懂的問題！</li><li>即使自己思考和查找已經到極限，還是不懂…</li><li>（數小時後）小心翼翼地提問…</li><li>( ﾟдﾟ) &lt; <strong>「一開始可能會遇到無法解決的問題，所以別猶豫，馬上提問吧！」</strong> 被重擊！！！噹(´・ω・｀)</li></ol><p><strong>「到底該怎麼辦啊啊啊啊啊啊啊啊啊啊啊啊啊啊！！！！！！！！」</strong></p><hr><p>在這篇文章中，我將分享一種克服這種困境的方法。</p><p>相信正確的方法有非常多種，因此如果有其他克服方法，也歡迎分享出來！</p><h2 id="克服方法"><a href="#克服方法" class="headerlink" title="克服方法"></a>克服方法</h2><p>我認為克服的方法有 3 個步驟（逆向思考）。</p><ol><li>建立問題模板，填寫後再提問</li><li>強調語言表達</li><li>注重以輸出為前提的輸入</li></ol><h3 id="建立問題模板，填寫後再提問"><a href="#建立問題模板，填寫後再提問" class="headerlink" title="建立問題模板，填寫後再提問"></a>建立問題模板，填寫後再提問</h3><p>首先，我會建立一種稱為「問題模板」的東西，並填寫後再提問。</p><p>「問題模板」是在想要提問時，用來引導思考的範本。</p><p>為什麼會想要創建這個模板呢？因為剛進公司工作的我，即使想要提問，但多數情況都是模糊不清，無法理清思緒，提出具體化問題。</p><p>因此，我建立了一個模板，試著視覺化所提出的問題，和自己的思考進行連結。</p><p>儘管稱之為「問題模板」，但實際上非常簡單。</p><p>以下是問題模板的範例：</p><figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 日文版</span></span><br><span class="line"><span class="number">1.</span> 〜がわかりません。</span><br><span class="line"><span class="number">2.</span> 具体的には、〜です。</span><br><span class="line"><span class="number">3.</span> 私は〜と考えました。</span><br><span class="line"><span class="number">4.</span> なぜなら〜です。</span><br><span class="line"></span><br><span class="line"><span class="comment">// 中文版</span></span><br><span class="line"><span class="number">1.</span> 我不瞭解～。</span><br><span class="line"><span class="number">2.</span> 具體來說，這是～。</span><br><span class="line"><span class="number">3.</span> 我的想法是～。</span><br><span class="line"><span class="number">4.</span> 原因是～。</span><br></pre></td></tr></table></figure><p><strong>簡單！</strong></p><p>１.<br>首先是寫下結論。<br>我不懂這個！！！像這樣，讓提問者和回答者都一目瞭然！</p><p>２.<br>接著，提供詳細說明，以及<strong>具體地</strong>描述遇到問題的經過。<br>即使某些程式碼或操作對自己來說是顯而易見的，但對別人來說可能是第一次見到，因此建議盡可能提供詳細的訊息。<br>就算寫得比自己想得還要詳細，讀者可能瞬間就能夠讀懂。</p><p>３.<br>然後，陳述自己的觀點，即使是錯的也沒關係。<br>重要的是傳達給對方「我已經思考過了」這件事。<br>如果不知道答案，透過自己的方式嘗試並寫下來。</p><p>４.</p><p>最後，提供支持該觀點的客觀證據。<br>如果缺乏這一點，觀點就僅止於預測或猜想。<br>對於程式設計師來說，尤其不喜歡模糊不清的想法⋯⋯（猜測）</p><hr><p>如果能用語言表達以上四點，就可以提問！這是我給自己定下的規則。</p><p>建立好提問的目標，即可迅速且毫不猶豫地提出問題，而不會陷入迷茫。</p><p>如此一來，</p><p>( ﾟдﾟ) &lt; 「<strong>難道你不會試著自己查一下嗎？</strong>」</p><p>( ﾟдﾟ) &lt; <strong>「一開始可能會遇到無法解決的問題，所以別猶豫，馬上提問吧！」</strong></p><p>就能擺脫被夾在這中間的過去了！ ！ ！</p><p>.</p><p>.</p><p>.</p><p>儘管如此，即使能夠在腦中思考自己的想法，要將文本內容實際用語言傳達給他人並不容易。（我也是這樣）</p><h2 id="強調語言表達"><a href="#強調語言表達" class="headerlink" title="強調語言表達"></a>強調語言表達</h2><p>我對語言表達的定義是「將腦中的想法記錄下來」。</p><p>（準確來說，除了文本內容，還包括口頭等詞語，但在這裡將限制其含義。）</p><p>就我而言，起初甚至從未聽過「語言表達」這個詞彙。</p><p>一直以來，比起發郵件我更喜歡打電話。</p><p>我認為直接說出來即可，如果是在 Slack 等的通訊工具上出現意見不一致，可透過通話來解決。如此除了節省時間，也能透過互動減少認知差異的可能性。</p><p>然而，日常中我們經常使用的是 Slack 等透過文本進行的交流。舉例來說，與合作夥伴或遠程工作的同事，彼此之間的互動、會議記錄、任務管理工具等。</p><p>實際上，對語言表達的認知，是自從我開始向遠端資深工程師和合作夥伴，提出針對規格和技術方面的問題時開始的。</p><p>某一天，我向一位遠端工程師提出一個技術問題。<br>當然，是按照問題模板來提問。<br>但得到的回答卻是「我不明白你的意思」。<br>噢，這裡我沒有傳達好。<br>因為對方理解不足的可能性為零，完全是因為我沒有解釋清楚。<br>這是我第一次遇到「記錄腦中的想法是如此困難」的情況。</p><p>現在的我，比以前更善於語言表達。<br>我認為，雖然這可能比起直接溝通，需要花費更多時間，但卻具有更多的優勢！</p><p>具體而言：</p><ul><li>可以獲得更深入的理解</li><li>增加自信和確信</li><li>可以得到反饋</li><li>可以看到自己的變化</li><li>作為記錄留下來</li></ul><p>那麼，該如何提高語言表達能力？<br>當然，思考方式也很重要，但更關鍵的是<strong>提高輸入的質量</strong>。</p><h3 id="注重以輸出為前提的輸入"><a href="#注重以輸出為前提的輸入" class="headerlink" title="注重以輸出為前提的輸入"></a>注重以輸出為前提的輸入</h3><p>提高輸入的質量意味著什麼？<br>是否已經清楚瞭解定義是什麼？瞭解基本的基礎？這些就是關鍵。</p><p>在語言表達時，我經常遇到在「術語」方面的問題。<br>例如對於某個詞語 A’，其抽象的單詞是 A 嗎？還是 B 呢？</p><p>再來，該如何提高輸入的質量呢？<br>以下提供的範例將有助於理解：</p><ul><li>如果在工作中發現需要某項技術的知識，那麼閱讀相關的技術書籍或網站，將會提供非常高質量的輸入吧？</li><li>如果計劃在某次會議上作為演講者登台，將會透過思考來提高輸入的質量吧？</li><li>如果決定撰寫 qiita 文章或技術部落格時，因為不希望提供錯誤的知識，將會研究需要的所有資訊吧？</li></ul><p>為什麼呢？</p><p>因為必須<strong>輸出</strong>。<br>想要提高輸入的質量，必須以輸出為前提進行輸入！</p><p>透過這些例子也可以瞭解到，與獨自學習程式語言相比，在工作中累積實務經驗的學習方式將會更有效率。</p><h2 id="總結"><a href="#總結" class="headerlink" title="總結"></a>總結</h2><p>當遇到問題卡住時，應該先填寫問題模板再提問！<br>透過提高輸入的質量，提高語言表達能力，將有助於更快提出問題！<br>為了實現這一點，必須始終以輸出為前提！</p><h2 id="最後"><a href="#最後" class="headerlink" title="最後"></a>最後</h2><p>這是我第一次撰寫 How-to 文章，結果變成類似自我啟發的內容⋯⋯<br>因為這是一篇個人散文，請別在意這點。</p><p><strong>後記（2019.10.03）</strong></p><p>收到了許多反饋，並且深切感受到 Qiita 的卓越之處＼(^o^)／ 謝謝大家！</p><p><code>[註解1]</code>: 敝公司從未說過 or 被告知過這樣的事。公司的政策之一是「初學者應該<strong>學習如何查找資料</strong>」。</p><blockquote><p>15th鐵人賽目錄傳送門：<span class="exturl" data-url="aHR0cHM6Ly9pdGhlbHAuaXRob21lLmNvbS50dy91c2Vycy8yMDEzNTU1OC9pcm9ubWFuLzYyOTA=" title="https://ithelp.ithome.com.tw/users/20135558/ironman/6290">https://ithelp.ithome.com.tw/users/20135558/ironman/6290<i class="fa fa-external-link"></i></span></p></blockquote>]]>
    </content>
    <id>https://heidiliu2020.github.io/ironman-2023-day-27/</id>
    <link href="https://heidiliu2020.github.io/ironman-2023-day-27/"/>
    <published>2023-10-12T00:18:01.000Z</published>
    <summary>
      <![CDATA[<blockquote>
<p>原文連結：<span class="exturl" data-url="aHR0cHM6Ly9xaWl0YS5jb20vZGV2LXRhdHN1eWEvaXRlbXMvYmNhMzg2YmM0NzMyMmYyMjA3MmE=" title="https://qiita.com/dev-tatsuya/items/bca386bc47322f22072a">[あるある]「詰まったら、すぐに質問してください」の克服法 - Qiita<i class="fa fa-external-link"></i></span></p>
</blockquote>
<p>倒數三天！不知不覺也來到第二十七天，終於快看到終點了似乎有點感概XD</p>
<p>接下來這篇文章，是打從自己在<span class="exturl" data-url="aHR0cHM6Ly9oZWlkaS1jb2RpbmcubWVkaXVtLmNvbS8lRTglQkQlODklRTglODElQjclRTUlODklOEQlRTclQUIlQUYlRTUlQjclQTUlRTclQTglOEIlRTUlQjglQUItbGlkZW15LSVFNyVCNSU5MCVFNiVBNSVBRCVFNSVCRiU4MyVFNSVCRSU5NyVFOCU4OCU4NyVFNiVCMSU4MiVFOCU4MSVCNyVFNyVCOCVCRCVFNyVCNSU5MC05ODA2NDRlYjRmNzQ=" title="https://heidi-coding.medium.com/%E8%BD%89%E8%81%B7%E5%89%8D%E7%AB%AF%E5%B7%A5%E7%A8%8B%E5%B8%AB-lidemy-%E7%B5%90%E6%A5%AD%E5%BF%83%E5%BE%97%E8%88%87%E6%B1%82%E8%81%B7%E7%B8%BD%E7%B5%90-980644eb4f74">轉職學習程式<i class="fa fa-external-link"></i></span>的階段，直到現在第二份工作，有時仍是備感困擾的問題。因為不善於提問，或是希望先自己做好功課，等到萬不得已才找人求救，殊不知中間已經浪費太多時間，放在工作上也很有可能因此耽誤進度。</p>
<p>但是呢，這些過程其實是能夠訓練的，本文中提供面對問題時，應該如何善用「提問模板」訓練思考，並且著重於「輸出導向的輸入」，透過這些過程，將有助於改善語言表達能力，並有效提升學習的效果。</p>]]>
    </summary>
    <title>[2023 15th鐵人賽] Day27 - [常有的事] 克服「遇到問題卡住時，請立刻提問」的方法</title>
    <updated>2026-04-23T06:14:18.232Z</updated>
  </entry>
  <entry>
    <author>
      <name>Heidi Liu</name>
    </author>
    <category term="技術學習" scheme="https://heidiliu2020.github.io/categories/%E6%8A%80%E8%A1%93%E5%AD%B8%E7%BF%92/"/>
    <category term="2023鐵人賽" scheme="https://heidiliu2020.github.io/categories/%E6%8A%80%E8%A1%93%E5%AD%B8%E7%BF%92/2023%E9%90%B5%E4%BA%BA%E8%B3%BD/"/>
    <category term="入門篇" scheme="https://heidiliu2020.github.io/categories/%E6%8A%80%E8%A1%93%E5%AD%B8%E7%BF%92/2023%E9%90%B5%E4%BA%BA%E8%B3%BD/%E5%85%A5%E9%96%80%E7%AF%87/"/>
    <category term="ironman2023" scheme="https://heidiliu2020.github.io/tags/ironman2023/"/>
    <category term="Qiita" scheme="https://heidiliu2020.github.io/tags/Qiita/"/>
    <category term="規格書" scheme="https://heidiliu2020.github.io/tags/%E8%A6%8F%E6%A0%BC%E6%9B%B8/"/>
    <category term="設計文件" scheme="https://heidiliu2020.github.io/tags/%E8%A8%AD%E8%A8%88%E6%96%87%E4%BB%B6/"/>
    <content>
      <![CDATA[<blockquote><p>原文連結：<span class="exturl" data-url="aHR0cHM6Ly9xaWl0YS5jb20veS1zb21lL2l0ZW1zLzkwNjUxYzFlMjdmNzc5OGY4N2M2" title="https://qiita.com/y-some/items/90651c1e27f7798f87c6">エンジニア歴20数年の私が、設計書を書く際に心がけていること - Qiita<i class="fa fa-external-link"></i></span></p></blockquote><p>上一篇主要介紹「需求定義到內部設計」的流程，接下來這篇文章，將著重於設計規格書的寫法。</p><p>先是從作者個人角度統整觀點，如何提升文件的易讀性，以及如何避免資訊混淆等；而在團隊開發中，如何制定一致的「規格書撰寫方式」，使其易於後續維護。</p><span id="more"></span><p>以下正文開始。</p><hr><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>時間過得真快，轉眼間我已經在 IT 行業度過了四分之一個世紀。</p><p>在這段時間裡，我寫過大量的「設計文件（規格書）」，但內心仍充滿困惑和迷惘。</p><p>儘管如此，俗話說「<span class="exturl" data-url="aHR0cDovL2tvdG93YXphLWFsbGd1aWRlLmNvbS9rYS9rYW1lbm9rb3V5b3JpdG9zaGkuaHRtbA==" title="http://kotowaza-allguide.com/ka/kamenokouyoritoshi.html">亀の甲より年（薑是老的辣）<i class="fa fa-external-link"></i></span>」，我試著從「個人」和「團隊」兩種角度總結出經驗法則。</p><ul><li>本文的主題是「針對設計規格書，開發文件的撰寫方式」</li><li>本文假定的規格書，是以 Excel 或 Word 撰寫成正式的（≒ 交付物）設計文件。</li><li>因此，比起以自家服務開發或敏捷開發為前提，委託開發、瀑布式開發可能更適合本文內容。</li></ul><p><strong>＜請注意＞</strong></p><p>本文內容是作者個人見解，並不代表所屬公司的立場、策略、意見。</p><h2 id="個人所注意的事情"><a href="#個人所注意的事情" class="headerlink" title="個人所注意的事情"></a>個人所注意的事情</h2><h3 id="在文章開頭說明該文件的建立目的和定位"><a href="#在文章開頭說明該文件的建立目的和定位" class="headerlink" title="在文章開頭說明該文件的建立目的和定位"></a>在文章開頭說明該文件的建立目的和定位</h3><ul><li>例如「本書將描述 ◯◯ 功能中 ×× 畫面的佈局設計以及輸入輸出設計」。</li><li>若有相關文件，可以寫上「關於 △△，請參考 □□」。</li><li>特別適用於設計階段之後（例如系統測試、維護或新增功能時），「搜尋」設計文件的情況。</li></ul><h3 id="首先考慮章節架構"><a href="#首先考慮章節架構" class="headerlink" title="首先考慮章節架構"></a>首先考慮章節架構</h3><ul><li>考慮章節架構，也可以說是設計一份設計文件。</li><li>如果可以，最好在只寫出章節標題的狀態，給合適的人過目，如此可以降低基本認知差異的風險。</li></ul><h3 id="先顯示整體-→-再解釋細節"><a href="#先顯示整體-→-再解釋細節" class="headerlink" title="先顯示整體 → 再解釋細節"></a>先顯示整體 → 再解釋細節</h3><ul><li>這不僅適用於設計文件和發表文稿，任何文件檔也同樣適用。</li></ul><h3 id="注意每行的字數"><a href="#注意每行的字數" class="headerlink" title="注意每行的字數"></a>注意每行的字數</h3><ul><li>有些人會在 A4 橫向格式中，寫很長的文句，但這將導致閱讀困難。</li><li>每行最佳文字數<ul><li>如果用 Google 搜尋，可以發現許多網站文章，對橫向書寫的建議是每行 30〜35 個字。</li><li>以我來看，設計文件最多不超過 40 個字，這是為了易讀性和每頁資訊量之間的平衡。<ul><li>40 個字並沒有明確的根據，但如果是「一行 80 字節」，可能會讓人有所感觸⋯⋯</li></ul></li><li>字數可以根據個人的基準來決定，重點是「需要注意每行文字」這一點。</li></ul></li></ul><h3 id="縮短句子"><a href="#縮短句子" class="headerlink" title="縮短句子"></a>縮短句子</h3><ul><li>長句子往往形成複雜的結構，容易產生誤解。</li><li>個人建議以「每句不超過兩行（即 80 個字符以內）」為目標。對於跨越三行的句子，應該考慮是否能進行拆解。</li></ul><h3 id="使用條列式"><a href="#使用條列式" class="headerlink" title="使用條列式"></a>使用條列式</h3><ul><li>列舉項目的長句，通常可以轉換為條列式。</li><li>在分解結構複雜的句子時，條列式通常非常有效。</li></ul><h3 id="使用圖表"><a href="#使用圖表" class="headerlink" title="使用圖表"></a>使用圖表</h3><ul><li>比起文字，人類更容易透過圖表直觀理解。</li><li>特別是在表現整體概念時，圖表比文字更有說服力。</li></ul><h3 id="使用表格"><a href="#使用表格" class="headerlink" title="使用表格"></a>使用表格</h3><ul><li>這對預防損害方面非常重要。</li><li>若試圖只用文字描述複雜的情況，可能導致讀者產生誤解。</li></ul><h3 id="使用縮排（Indentation）"><a href="#使用縮排（Indentation）" class="headerlink" title="使用縮排（Indentation）"></a>使用縮排（Indentation）</h3><ul><li>縮排是一種有效的方式，可以讓讀者在視覺上理解文章結構。</li></ul><h3 id="統一主語"><a href="#統一主語" class="headerlink" title="統一主語"></a>統一主語</h3><ul><li>「當 ◯◯ 按鈕被點擊時，將顯示 ×× 畫面」和「當點擊 ◯◯ 按鈕時，×× 畫面將被顯示」的主語是不同的。</li><li>在設計文件中，通常主語是「系統」或「用戶」。雖然沒有必要堅持哪一種，但至少在文件中要保持一致。</li><li>順帶一提，我在設計文件中傾向於使用「系統」作為主語。</li></ul><h3 id="統一用詞"><a href="#統一用詞" class="headerlink" title="統一用詞"></a>統一用詞</h3><ul><li>對於相同功能、相同概念，應該使用完全相同的詞彙，不要用不同的詞彙來描述同一件事。<ul><li>例：「日締め」、「日次締め」、「日締処理」（代表截止日期）</li></ul></li><li>這些看似小事，但在系統開發中，團隊成員之間微小的認知差異，往往會引發大問題。</li><li>在專案中建立詞彙表。</li><li>對於縮寫也要在詞彙表中記錄，或在文件開頭寫上「Ruby on Rails（以下簡稱 RoR）」等內容。（除非是已經廣泛使用的縮寫，如「IT」等）</li></ul><h3 id="不要創造新詞"><a href="#不要創造新詞" class="headerlink" title="不要創造新詞"></a>不要創造新詞</h3><ul><li>常見例子是與日期相關的詞。<ul><li>「執行日期」、「處理日期」、「當日」⋯⋯<ul><li>是從哪裡取得？包含時刻嗎？時區是什麼？是否有曆法差異？</li></ul></li></ul></li><li>如果是專案內共享的概念，請在詞彙表中記錄定義。</li><li>如果是在該文件內，獨自建立的詞語，應該建立該詞語的定義項目，並明確做記錄。</li></ul><h3 id="排除寫法不一致"><a href="#排除寫法不一致" class="headerlink" title="排除寫法不一致"></a>排除寫法不一致</h3><p>例如：</p><ul><li>「サーバ」和「サーバー」：統一拼音</li><li>「Web」和「ウェブ」：名詞統一使用中、英或日文表示</li><li>「4月」、「４月」和「四月」：數字統一用半形、全形或中文表示</li><li>「です・ます」和「だ・である」：句尾型態統一</li></ul><p>如果是自己專用的筆記，可能不太需要在意這些細節。<br>但我認為，考慮有讀者存在，以「也許會有人根據這些細節來評價文件的好壞」為前提會比較保險。</p><h3 id="專有名詞必須準確"><a href="#專有名詞必須準確" class="headerlink" title="專有名詞必須準確"></a>專有名詞必須準確</h3><ul><li>例如「JAVA」是錯誤寫法，正確是「Java」。</li><li>對於產品名稱或公司名稱，容易出現混淆的情況，應該仔細查證，確保文字大小寫、單詞間有無空格等內容。</li></ul><h2 id="在領導團隊時所注意的事情"><a href="#在領導團隊時所注意的事情" class="headerlink" title="在領導團隊時所注意的事情"></a>在領導團隊時所注意的事情</h2><p>我認為，影響設計文件好壞的因素，比起個人的努力和心態，團隊之間的協力更為重要。</p><p>如果整個團隊在撰寫設計文件時沒有統一感，對於外部觀察者而言，設計文件的整體價值將會降低。</p><h3 id="確認記錄的細節"><a href="#確認記錄的細節" class="headerlink" title="確認記錄的細節"></a>確認記錄的細節</h3><ul><li>對於基本設計文件和詳細設計文件，應該要記錄什麼，以及寫到什麼程度。</li><li>如果寫得過於詳細，將難以進行維護，最終導致沒有人會查看設計文件。</li></ul><h3 id="確認章節的記錄方式"><a href="#確認章節的記錄方式" class="headerlink" title="確認章節的記錄方式"></a>確認章節的記錄方式</h3><p>這裡提供一個範例：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">１．ｘｘｘ</span><br><span class="line">    １．１．ｘｘｘ</span><br><span class="line">        １．１．１．ｘｘｘ</span><br><span class="line">            （１）ｘｘｘ</span><br></pre></td></tr></table></figure><p>無論是使用全形/半形文字，以及是否使用縮排等細節，都需要仔細確認，這將有助於文件的統一性。</p><h3 id="在量產規格書之前先製作樣本"><a href="#在量產規格書之前先製作樣本" class="headerlink" title="在量產規格書之前先製作樣本"></a>在量產規格書之前先製作樣本</h3><ul><li>最好由設計文件的審查者，也就是工程師來製作樣本。</li><li>樣本完成後，除了說「請看一下」以外，最好有一個場合，能夠向所有人解釋希望如何編寫樣本，以及樣本創作者的想法。</li></ul><h3 id="模板盡可能簡單"><a href="#模板盡可能簡單" class="headerlink" title="模板盡可能簡單"></a>模板盡可能簡單</h3><ul><li>過多的規則將會降低生產效率。</li><li>過度拘泥於模板，將會使內容變得膚淺。在某些情況下，自由格式可能更適合表達。</li><li>確實格式也是品質的一部分，但製作一個平衡的模板更為重要。</li></ul><h3 id="確定如何保留變更歷史紀錄"><a href="#確定如何保留變更歷史紀錄" class="headerlink" title="確定如何保留變更歷史紀錄"></a>確定如何保留變更歷史紀錄</h3><ul><li>紅→藍→綠→⋯⋯，有些成員可能會在每次進行變更時，把顏色變得很繽紛。</li><li>對此可能會有其他成員抱怨「難以閱讀！」（就是我）。</li><li>雖然從 Excel 轉移到 Markdown 等格式，將其納入版本控制系統可能會更好⋯⋯（有時也涉及到政治因素），但可能很難實現。</li><li>建立變更歷史紀錄表，並記錄在哪個表的哪個部分，進行了什麼修改，可能是現實的解決方案。</li></ul><h3 id="將上述事項整理成指南"><a href="#將上述事項整理成指南" class="headerlink" title="將上述事項整理成指南"></a>將上述事項整理成指南</h3><ul><li>使審查更加順利</li><li>當成員更換時，能夠順利進行說明</li><li>無論有多忙碌，都不應忽視指南的維護</li></ul><h2 id="總結"><a href="#總結" class="headerlink" title="總結"></a>總結</h2><ul><li>為了提高書寫能力，只有透過訓練。</li><li>最重要的是，身邊有人能夠嚴格指導並提供建議。</li></ul><p>最後，如果這篇文章有任何不足之處，請在評論等地方告訴我！</p><p><strong>（2018 年 2 月 15 日補充）</strong></p><p>非常感謝得到許多反饋。</p><p>這篇文章僅基於我個人的經驗，可能存在思慮不足之處，也希望能夠根據各位的開發需求進行修改與應用。</p><p>我認為，透過教育和指導，可以在團隊內制定一致的「設計規格書撰寫方式」，從而在設計審查中更專注於「設計本身」。</p><p><strong>（2018 年 3 月 11 日補充）</strong></p><p>關於「每行字數〜」、「縮短句子」和「使用條列式」，已重新審視表達方式，主要內容將保持不變。</p><blockquote><p>15th鐵人賽目錄傳送門：<span class="exturl" data-url="aHR0cHM6Ly9pdGhlbHAuaXRob21lLmNvbS50dy91c2Vycy8yMDEzNTU1OC9pcm9ubWFuLzYyOTA=" title="https://ithelp.ithome.com.tw/users/20135558/ironman/6290">https://ithelp.ithome.com.tw/users/20135558/ironman/6290<i class="fa fa-external-link"></i></span></p></blockquote>]]>
    </content>
    <id>https://heidiliu2020.github.io/ironman-2023-day-26/</id>
    <link href="https://heidiliu2020.github.io/ironman-2023-day-26/"/>
    <published>2023-10-11T00:08:01.000Z</published>
    <summary>
      <![CDATA[<blockquote>
<p>原文連結：<span class="exturl" data-url="aHR0cHM6Ly9xaWl0YS5jb20veS1zb21lL2l0ZW1zLzkwNjUxYzFlMjdmNzc5OGY4N2M2" title="https://qiita.com/y-some/items/90651c1e27f7798f87c6">エンジニア歴20数年の私が、設計書を書く際に心がけていること - Qiita<i class="fa fa-external-link"></i></span></p>
</blockquote>
<p>上一篇主要介紹「需求定義到內部設計」的流程，接下來這篇文章，將著重於設計規格書的寫法。</p>
<p>先是從作者個人角度統整觀點，如何提升文件的易讀性，以及如何避免資訊混淆等；而在團隊開發中，如何制定一致的「規格書撰寫方式」，使其易於後續維護。</p>]]>
    </summary>
    <title>[2023 15th鐵人賽] Day26 - 擁有 20 多年工程師經驗的我，在撰寫設計規格書時所注意的事情</title>
    <updated>2026-04-23T06:14:18.230Z</updated>
  </entry>
  <entry>
    <author>
      <name>Heidi Liu</name>
    </author>
    <category term="技術學習" scheme="https://heidiliu2020.github.io/categories/%E6%8A%80%E8%A1%93%E5%AD%B8%E7%BF%92/"/>
    <category term="2023鐵人賽" scheme="https://heidiliu2020.github.io/categories/%E6%8A%80%E8%A1%93%E5%AD%B8%E7%BF%92/2023%E9%90%B5%E4%BA%BA%E8%B3%BD/"/>
    <category term="Back-End" scheme="https://heidiliu2020.github.io/categories/%E6%8A%80%E8%A1%93%E5%AD%B8%E7%BF%92/2023%E9%90%B5%E4%BA%BA%E8%B3%BD/Back-End/"/>
    <category term="Back-End" scheme="https://heidiliu2020.github.io/tags/Back-End/"/>
    <category term="System Design" scheme="https://heidiliu2020.github.io/tags/System-Design/"/>
    <category term="ironman2023" scheme="https://heidiliu2020.github.io/tags/ironman2023/"/>
    <category term="Qiita" scheme="https://heidiliu2020.github.io/tags/Qiita/"/>
    <category term="Demand" scheme="https://heidiliu2020.github.io/tags/Demand/"/>
    <content>
      <![CDATA[<blockquote><p>原文連結：<span class="exturl" data-url="aHR0cHM6Ly9xaWl0YS5jb20vU2FrdTczMS9pdGVtcy83NDFmY2YwZjQwZGQ5ODllZTRmOCMlRTUlOUYlQkElRTYlOUMlQUMlRTglQTglQUQlRTglQTglODglRTclOTQlQkIlRTklOUQlQTIlRTglQTglQUQlRTglQTglODglRTYlQTklOUYlRTglODMlQkQlRTglQTglQUQlRTglQTglODglRTMlODMlODclRTMlODMlQkMlRTMlODIlQkYlRTglQTglQUQlRTglQTglODglRTMlODElQUUlRTQlQkQlOUMlRTMlODIlOEElRTYlOTYlQjk=" title="https://qiita.com/Saku731/items/741fcf0f40dd989ee4f8#%E5%9F%BA%E6%9C%AC%E8%A8%AD%E8%A8%88%E7%94%BB%E9%9D%A2%E8%A8%AD%E8%A8%88%E6%A9%9F%E8%83%BD%E8%A8%AD%E8%A8%88%E3%83%87%E3%83%BC%E3%82%BF%E8%A8%AD%E8%A8%88%E3%81%AE%E4%BD%9C%E3%82%8A%E6%96%B9">要件定義～システム設計ができる人材になれる記事 - Qiita<i class="fa fa-external-link"></i></span></p></blockquote><p>本篇介紹的主題是「需求定義到系統設計」，從為什麼需要需求定義，到如何進行這段流程，再接著說明基本設計的三個步驟，透過一步步解說，到最後完成系統設計圖。</p><p>文章架構如下：</p><ul><li>需求定義的必要性<ul><li>需求定義的目的</li></ul></li><li>確定需求定義的過程</li><li>在需求定義中決定的事項</li><li>基本設計（畫面設計・功能設計・資料設計）的作法</li><li>畫面設計（UI 設計）</li><li>功能設計</li><li>資料設計<ul><li>資料的具體內容</li><li>資料庫設計</li><li>資料的流向（Data Flow）</li></ul></li><li>進一步推進基本設計</li></ul><span id="more"></span><p>以下正文開始。</p><hr><p>由於收到許多詢問，這裡提供需求評估～設計相關 Udemy 課程的 Coupon。<br>歡迎多多利用＆給予評價 m(_ _)m</p><p>■入門編：</p><p><span class="exturl" data-url="aHR0cHM6Ly93d3cudWRlbXkuY29tL2NvdXJzZS9kaWdpc2FrdV9yZXF1aXJlbWVudHNfZGVmaW5pdGlvbl9hbmRfc3lzdGVtX2Rlc2lnbi8/Y291cG9uQ29kZT03QTk0NDEwMEE1RkU3RTNGOTcxQQ==" title="https://www.udemy.com/course/digisaku_requirements_definition_and_system_design/?couponCode=7A944100A5FE7E3F971A">【入門】システム要件定義と基本設計（実践ワークで理解する上流工程の進め方）<i class="fa fa-external-link"></i></span></p><p>■應用編：</p><p><span class="exturl" data-url="aHR0cHM6Ly93d3cudWRlbXkuY29tL2NvdXJzZS9idXNpbmVzc19yZXF1aXJlbWVudF9hbmFseXNpc19hbmRfc3lzdGVtX2Rlc2lnbi8/Y291cG9uQ29kZT1GOTE2NUM1MDhDNjdBMTAxREI0Qw==" title="https://www.udemy.com/course/business_requirement_analysis_and_system_design/?couponCode=F9165C508C67A101DB4C">【超実践】ビジネス要件分析・基本設計・詳細設計をやり抜く実践ワーク講座<i class="fa fa-external-link"></i></span></p><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>這是由 <span class="exturl" data-url="aHR0cHM6Ly9kaWdpc2FrdS5jby5qcC8=" title="https://digisaku.co.jp/">DEGISAKU 株式會社<i class="fa fa-external-link"></i></span>提供的程式設計文章。</p><p>這次要討論的是與需求定義・系統設計相關的主題。</p><p>在學習程式設計時，是否曾經有過這樣的經驗呢？</p><p><strong>「即使學習也做不出產品」<br>「到底開發是怎麼進行？」<br>「需求定義是什麼？」</strong></p><p>為了解決這些困擾，首先讓我們瞭解<strong>開發過程的整體概念</strong>。<br>請參考下圖的「軟體開發過程」，</p><p>我們平常在學習程式設計的階段，將對應到 <strong>「實作」</strong>的部分。<br>換句話說，在展現程式設計能力之前，還<strong>存在四道壁壘</strong>需要克服。</p><p><img src="https://hackmd.io/_uploads/B1KZLebW6.png"></p><blockquote><p>軟體開發過程，由左至右分別是：</p><ul><li>企劃 &gt; 業務設計 &gt; 需求定義 &gt; 設計 &gt; 實作（程式設計） &gt; 測試 &gt; 發布 &gt; 維護</li></ul></blockquote><p>因此，本文將針對開始實作（程式設計）之前，必須進行的「企劃 &gt; 設計」過程依序做介紹。<br>內容將特別著重於，工程師必須理解的「需求定義」和「設計」階段。</p><p>此外，據說在整個開發過程中，大約<strong>有二到三成的時間用於實作（程式設計）</strong>；<strong>另一方面，需求定義和設計等上游流程，約占總時間的五成</strong>。</p><p>這意味著，<strong>擁有需求定義和設計能力的人，必然能提高對開發專案的貢獻度</strong>。藉由這個機會，讓我們好好學習這些技能，來擴大發展的機會吧。</p><p>本文的章節安排如下：</p><ul><li>需求定義的必要性</li><li>确定需求定義的過程</li><li>在需求定義中決定的事項</li><li>基本設計的建立方式</li><li>進一步推進基本設計</li><li>總結</li></ul><p>※ 我在 SNS 上也有分享各種訊息，若閱讀本文感覺不錯，也歡迎關注 <span class="exturl" data-url="aHR0cHM6Ly90d2l0dGVyLmNvbS9kaWdpc2FrdTcxMA==" title="https://twitter.com/digisaku710">Twitter 帳號「Saku731」<i class="fa fa-external-link"></i></span> 。</p><h2 id="需求定義的必要性"><a href="#需求定義的必要性" class="headerlink" title="需求定義的必要性"></a>需求定義的必要性</h2><h3 id="需求定義的目的"><a href="#需求定義的目的" class="headerlink" title="需求定義的目的"></a>需求定義的目的</h3><p>在系統開發專案中，通常分成兩類主要參與者：</p><ul><li><strong>「製作」系統的人（開發者）</strong></li><li><strong>「使用」系統的人（委託者）</strong></li></ul><p>如果是建立自己想要的系統，那麼在腦海中應該已經有了完整概念，因此不會有太大問題。但在更多情況下，開發工作會是透過「委託者⇒開發者」來委託完成。</p><p>而在這種情況，如果不<strong>明確化委託者腦中的想法</strong>，開發者將不知道該從何做起。因此，需求定義的目的就是為了能夠與委託者溝通。</p><p>試著用日常生活中的例子來思考看看。</p><p>當覺得口渴時，您是否會提出「幫我買飲料來！」的要求？<br>如果沒有表達「幫我買杯咖啡」，或更具體的「<strong>幫我買杯微糖熱咖啡，不要罐裝，要瓶裝的！</strong>」，可能無法如願得到想要的咖啡。</p><p>系統開發也是如此。</p><p>需求定義的目標，就是為了明確達成「該做什麼才能滿足需求？」的共識。</p><p>也就是說，<strong>需求定義 = 在交付時可以明確確認「這樣 OK！」的具體先決條件</strong>，請務必理解這點。</p><h2 id="確定需求定義的過程"><a href="#確定需求定義的過程" class="headerlink" title="確定需求定義的過程"></a>確定需求定義的過程</h2><p>瞭解需求定義的目的之後，接下來，讓我們來理解完成需求定義的三個階段：</p><ul><li>要望（ようぼう）：「如果有這樣的系統就好了」的想法</li><li>要求（ようきゅう）：希望在系統實現的<strong>粗略功能清單</strong></li><li>要件（ようけん）：雙方都同意的<strong>具體功能清單與實作方法</strong></li></ul><blockquote><p>「要望」&gt;「要求」&gt;「要件」分別表示不同程度或類型的需求。</p></blockquote><p>可參考下圖，由外到內依序代表：</p><ul><li>要望：比較隨意的願望或期望<ul><li>「如果有這樣的系統就好了」</li></ul></li><li>要求：強制性、正式的需求<ul><li>「希望實現這樣的功能」</li></ul></li><li>要件：特定的條件、規格或要求<ul><li>「來實現這個功能吧」</li></ul></li></ul><p><img src="https://hackmd.io/_uploads/B1P9M5W-a.png"></p><p>這三個階段的過程，需要透過「審查」和「建議」來溝通，按照順序整理如下：</p><ul><li><strong>①要望（期望）：需要解決的問題（委託者的任務）</strong><ul><li>現有的問題</li><li>目標（本應達到的狀態）</li><li>現況與目標之間的差距（需要解決的問題）</li></ul></li><li><strong>②要求（正式需求）：想要在系統中實現的功能（委託者的任務）</strong><ul><li>專案背景（需要解決的問題）</li><li>解決問題所需的系統概述</li><li>具體想要實現的功能清單</li></ul></li><li><strong>③評估：思考實現需求的可行性（開發者的任務）</strong><ul><li>開發在技術上是否可行？</li><li>預算大約需要多少？</li><li>交付期限差不多是何時？</li></ul></li><li><strong>④提議：將討論結果反饋給委託者（開發者的任務）</strong><ul><li>可以實現的功能</li><li>請求金額</li><li>可以交付的日期</li></ul></li><li><strong>⑤要件（特定條件的需求）：雙方達成共識的決策事項（委託者・開發者協商決定）</strong><ul><li>在系統中實現的功能清單</li><li>交付期限、收費金額的參考資訊也可能包含在其中</li></ul></li></ul><p><img src="https://hackmd.io/_uploads/BkFXJs-WT.png"></p><h2 id="在需求定義中決定的事項"><a href="#在需求定義中決定的事項" class="headerlink" title="在需求定義中決定的事項"></a>在需求定義中決定的事項</h2><p>在瞭解流程之後，接下來將粗略介紹需求定義中決定的事項。</p><p>這裡使用 3W（Why・What・How）來整理排列，將有助於清楚理解：</p><ul><li><strong>Why：系統開發目的（要望）</strong><ul><li>現有的問題</li><li>目標（本應達到的狀態）</li><li>現況與目標之間的差距（需要解決的問題）</li></ul></li><li><strong>What：需要解決什麼問題</strong><ul><li>系統導入後的業務流程</li><li>功能需求<ul><li>想要實現的功能清單</li></ul></li><li>非功能需求<ul><li>處理速度、安全性等</li></ul></li></ul></li><li><strong>How：具體的使用難易度與實作方法（類似系統設計的任務）</strong><ul><li>基本設計<ul><li>畫面設計（UI 設計）</li><li>功能設計</li><li>資料設計</li></ul></li><li>詳細設計<ul><li><span class="exturl" data-url="aHR0cHM6Ly96aC53aWtpcGVkaWEub3JnL3poLXR3LyVFOSVBMSU5RSVFNSU4OCVBNSVFNSU5QyU5Ng==" title="https://zh.wikipedia.org/zh-tw/%E9%A1%9E%E5%88%A5%E5%9C%96">類別圖（Class Diagram）<i class="fa fa-external-link"></i></span>、<span class="exturl" data-url="aHR0cHM6Ly96aC53aWtpcGVkaWEub3JnL3poLXR3LyVFNiU5NyVCNiVFNSVCQSU4RiVFNSU5QiVCRQ==" title="https://zh.wikipedia.org/zh-tw/%E6%97%B6%E5%BA%8F%E5%9B%BE">序列圖（Sequence Diagram）<i class="fa fa-external-link"></i></span></li><li>系統架構（<strong>Systems architecture）</strong></li><li>實現各部位的技術等</li></ul></li></ul></li></ul><h2 id="基本設計（畫面設計・功能設計・資料設計）的作法"><a href="#基本設計（畫面設計・功能設計・資料設計）的作法" class="headerlink" title="基本設計（畫面設計・功能設計・資料設計）的作法"></a>基本設計（畫面設計・功能設計・資料設計）的作法</h2><p>對於在需求定義中決定的事項，可能因此產生繁雜的印象，但請不用擔心。</p><p>在 What 部分（業務流程、功能需求和非功能需求），通常是由開發團隊中的「<strong>業務人員</strong>」或「<strong>銷售工程師</strong>」來處理。</p><p>因此，我們將<strong>深入探討工程師在 How（設計）方面發揮其能力</strong>。其中，<strong>扮演關鍵角色的基本設計</strong>，將是本文的討論重點。</p><p>如上所述，基本設計包括「畫面、功能和資料」的設計，只有當這些內容都到位後，程式設計師才能開始著手軟體開發。</p><ul><li><strong>畫面設計（UI 設計）</strong><ul><li>可以在各畫面做什麼？</li><li>顯示資訊（文字、圖像等）和佈局</li><li>將上述內容統整到畫面轉換圖（Screen transition diagram）</li></ul></li><li><strong>功能設計</strong><ul><li>背後執行（功能名稱和處理內容）</li><li>處理需要的資料、資料的取得來源（從畫面輸入、從 DB 取得等）</li><li>將處理後的資料傳送目的地（畫面顯示、儲存到 DB 等）</li></ul></li><li><strong>資料設計</strong><ul><li>資料的具體內容</li><li>資料庫設計</li><li>資料的流向（Data Flow）</li></ul></li></ul><h2 id="畫面設計（UI-設計）"><a href="#畫面設計（UI-設計）" class="headerlink" title="畫面設計（UI 設計）"></a>畫面<strong>設計（UI 設計）</strong></h2><p>首先，必須優先決定的部分是<strong>畫面設計（UI 設計）</strong>。</p><p>使用者透過畫面（UI）來使用軟體，因此在「<strong>畫面的易用性</strong>」這點意見一致，將是通往專案成功的捷徑。</p><p>如前面所述，畫面設計包含以下項目：</p><ul><li>可以在各畫面做什麼？</li><li>顯示資訊（文字、圖像等）和佈局</li><li>將上述內容統整到畫面轉換圖（Screen transition diagram）</li></ul><p><img src="https://hackmd.io/_uploads/r1tr6iWbT.png">)</p><p>當整理到這一步時，即可想像出系統的具體成品，這就是為什麼要先進行畫面設計的原因。</p><h2 id="功能設計"><a href="#功能設計" class="headerlink" title="功能設計"></a>功能設計</h2><p>完成畫面設計（UI 設計）後，接著將設計系統要實現的功能。</p><p>功能設計是確定「背後<strong>執行的流程和所需資料</strong>」的階段。</p><p>需要決定的事項，如前面所述：</p><ul><li>背後執行（功能名稱和處理內容）</li><li>處理需要的資料、資料的取得來源（從畫面輸入、從 DB 取得等）</li><li>將處理後的資料傳送目的地（畫面顯示、儲存到 DB 等）</li></ul><p>接下來，試著粗略設計畫面轉換圖中出現的各頁面功能。</p><p><img src="https://hackmd.io/_uploads/B1IT1n-bp.png"></p><p><img src="https://hackmd.io/_uploads/SJq1g2-Z6.png"></p><p><img src="https://hackmd.io/_uploads/Sy0xg3bWT.png"></p><p><img src="https://hackmd.io/_uploads/Sk8Ze2-Za.png"></p><p><img src="https://hackmd.io/_uploads/B1vMg3Wb6.png"></p><p><img src="https://hackmd.io/_uploads/HyyXenbW6.png"></p><p>透過這個方式，無論是誰都<strong>能夠藉此想像應該寫什麼樣的程式</strong>。構成系統的各個部分也變得清晰，能更容易<strong>在團隊內部進行分工</strong>。</p><p>作為功能設計的最後一步，便是將畫面設計和功能設計連接起來。藉此可視化<strong>使用者操作與背後執行的處理</strong>，在畫面之間的關係。</p><p><img src="https://hackmd.io/_uploads/ByuzNNG-p.png"></p><p>全部統整後如下圖所示。<br>※ 這部分資料量較大，因此可放大感興趣的部分查閱。</p><p><img src="https://hackmd.io/_uploads/S1AEehWbT.png"></p><h2 id="資料設計"><a href="#資料設計" class="headerlink" title="資料設計"></a>資料設計</h2><p>最後是資料設計。</p><p>準備工作到這一步，終於要開始進行資料設計。</p><p>如上所述，需要決定的事項有三個：</p><ul><li>資料的具體內容</li><li>資料庫設計</li><li>資料的流向（Data Flow）</li></ul><p><strong>資料大致上可分為四種類型</strong>。</p><p>接下來，將要設計這四種資料類型，在系統內的流向是如何。</p><ul><li><strong>在程式「輸入」資料（參數）</strong><ul><li>使用者在 Web 畫面輸入資料</li><li>從資料庫讀取資料</li></ul></li><li><strong>從程式「返回」資料（返回值）</strong><ul><li>在 Web 畫面顯示的資料</li><li>在資料庫儲存的資料</li></ul></li></ul><h3 id="資料的具體內容"><a href="#資料的具體內容" class="headerlink" title="資料的具體內容"></a>資料的具體內容</h3><p>在這個階段，將會具體化之前抽象定義的資料，例如 <strong>「會員資訊」</strong> 等。</p><p>此外，如果要達到實務水平，就必須瞭解 <strong>「正規化」</strong> 的相關知識。</p><p>※ 如果感興趣，可參考相關文章：**<span class="exturl" data-url="aHR0cHM6Ly9xaWl0YS5jb20vbW9jaGljaG9jby9pdGVtcy8yOTA0Mzg0YjI4NTZkYjJiZjQ2Yw==" title="https://qiita.com/mochichoco/items/2904384b2856db2bf46c">正規化の要点を理解する<i class="fa fa-external-link"></i></span>**。</p><ul><li><strong>會員資訊</strong><ul><li>姓名</li><li>姓名的片假名讀音</li><li>電子郵件地址</li><li>密碼</li></ul></li><li><strong>商品資訊</strong><ul><li>商品名稱</li><li>詳細説明</li><li>商品圖像 URL</li><li>類型</li><li>製造商</li><li>價格</li><li>評價</li></ul></li><li><strong>購買紀錄</strong><ul><li>會員 ID</li><li>商品 ID</li><li>購買日期</li><li>購買數量</li></ul></li></ul><h3 id="資料庫設計"><a href="#資料庫設計" class="headerlink" title="資料庫設計"></a>資料庫設計</h3><p>在明確資料內容之後，再來將進行資料庫設計。</p><p>首先要記住的是，到目前為止，我們雖然稱資料為「會員訊息」、「商品訊息」、「購買歷史紀錄」等，但在資料庫設計中，將會用「Table（表格）」來表示。</p><p>※ 例如：<strong>「會員訊息表格」、「商品訊息表格」、「購買紀錄表格」</strong>。</p><p>在資料庫設計中，需要執行三項任務：</p><ul><li>區分表格的角色為「<strong>主表格</strong>」和「<strong>紀錄表格</strong>」</li><li>整理表格之間的<strong>參考關係</strong></li><li>將參考關係繪製成 <strong><span class="exturl" data-url="aHR0cHM6Ly96aC53aWtpcGVkaWEub3JnL3poLXR3L0VSJUU2JUE4JUExJUU1JTlFJThC" title="https://zh.wikipedia.org/zh-tw/ER%E6%A8%A1%E5%9E%8B">ER 圖（Entity-Relation Model = 實體關係圖）<i class="fa fa-external-link"></i></span></strong></li></ul><p>※（註）「主表格」和「紀錄表格」等稱呼是作者自身觀點。</p><p>接下來，按照以下標準來區分每個表格的角色：</p><ul><li><strong>主表格：包含基本訊息（其他表格會參照的資料）</strong><ul><li>會員訊息表格（以下簡稱為「<strong>會員主表格</strong>」）</li><li>商品訊息表格（以下簡稱為「<strong>商品主表格</strong>」）</li></ul></li><li><strong>紀錄表格：包含系統在運行中累積的資料</strong><ul><li>購買紀錄表格</li></ul></li></ul><p>下一步，使用箭頭表示<strong>參考來源與參考目標</strong>，整理「表格之間的參考關係」。<br>同時，也明確標明是「<strong>根據使用者操作產生的資料</strong>」，將有助於理解資料流動，而不易有所遺漏。<br>※ 有時也會透過網路從外部引入資料，如：天氣預報 API 等。</p><p><img src="https://hackmd.io/_uploads/SyR95mG-6.png"></p><p>最後，完成的 ER 圖即為資料庫設計圖。</p><p>※ 有關繪製 ER 圖的詳細內容，可參考這篇文章：<span class="exturl" data-url="aHR0cHM6Ly9pdC1rb2FsYS5jb20vZW50aXR5LXJlbGF0aW9uc2hpcC1kaWFncmFtLTE4OTc=" title="https://it-koala.com/entity-relationship-diagram-1897">若手プログラマー必読！５分で理解できるER図の書き方５ステップ<i class="fa fa-external-link"></i></span></p><p><img src="https://hackmd.io/_uploads/HkJd4VfWa.png"></p><h3 id="資料的流向（Data-Flow）"><a href="#資料的流向（Data-Flow）" class="headerlink" title="資料的流向（Data Flow）"></a>資料的流向（Data Flow）</h3><p>最後，將 <strong>「資料庫與畫面轉移圖之間的關係」</strong>整理完成後，即可看到系統整體面貌<strong>「畫面設計×功能設計×資料設計」</strong>。</p><p>每個程式都將基於這份設計圖來撰寫。</p><p>透過反覆練習這段過程，將能夠清楚呈現整體面貌。</p><p><img src="https://hackmd.io/_uploads/Hkvh2XMba.png"></p><h2 id="進一步推進基本設計"><a href="#進一步推進基本設計" class="headerlink" title="進一步推進基本設計"></a>進一步推進基本設計</h2><p>若能夠理解以上內容，即可進行基本設計，並且已足夠開發自己的產品。遇到不理解的部分，可以在查詢資料的同時，繼續進行編程。</p><p>開發完成後，如何設定伺服器為公開，Qiita 上已經有發表許多相關文章，這篇文章可供參考：**<span class="exturl" data-url="aHR0cHM6Ly9xaWl0YS5jb20vdGFjaGliYW5heXUyNC9pdGVtcy9iOGQ3M2NkZmQ0Y2JkNDJjNWIxZA==" title="https://qiita.com/tachibanayu24/items/b8d73cdfd4cbd42c5b1d">【20分でデプロイ】AWS EC2にDjango+PostgreSQL+Nginx環境を構築してササッと公開<i class="fa fa-external-link"></i></span>**</p><p>另外，在實際的開發現場，若是不進行<strong>詳細設計</strong>，將無法向程式設計師提出明確的要求。</p><p>在充分理解本文內容後，請先學習以下內容：</p><ul><li>建立類別圖和序列圖</li><li>決定系統架構</li><li>決定實現架構各部位的技術</li></ul><p>儘管如此，最近技術變革非常迅速，在<strong>詳細設計過程中可能已出現新技術</strong>的情況發生。</p><p>根據敏捷開發的方式，即可透過「<strong>在詳細設計過程中做出決策</strong>」來繼續進行。</p><p>※ 根據公司內部的開發方針進行即可。</p><h2 id="最後"><a href="#最後" class="headerlink" title="最後"></a>最後</h2><p>在團隊開發現場，<strong>角色分工是前提</strong>。<br>確實描繪整體樣貌，明確訂出「是誰、要做什麼」，以便開發順利進行。</p><p>若是覺得這篇文章有所幫助，也歡迎關注我的 <span class="exturl" data-url="aHR0cHM6Ly90d2l0dGVyLmNvbS9kaWdpc2FrdTcxMA==" title="https://twitter.com/digisaku710">Twitter 帳號「Saku731」<i class="fa fa-external-link"></i></span> 。</p><p>（後記）</p><p>我撰寫了一篇，關於使用 Python 進行系統開發所需技能的文章。可參考以下內容：</p><ul><li>瞭解系統開發所需技能的整體概況：<span class="exturl" data-url="aHR0cHM6Ly9xaWl0YS5jb20vU2FrdTczMS9pdGVtcy81MmEzYmJhY2QwMDJmMjZmNDA4ZQ==" title="https://qiita.com/Saku731/items/52a3bbacd002f26f408e">Pythonでゼロからでもサービス開発・公開できる学習ロードマップ<i class="fa fa-external-link"></i></span></li><li>使用 Django 進行系統開發的一系列步驟：<span class="exturl" data-url="aHR0cHM6Ly9xaWl0YS5jb20vU2FrdTczMS9pdGVtcy9lZDY0MTkwYTEyYTQ0OThiOTQ0Ng==" title="https://qiita.com/Saku731/items/ed64190a12a4498b9446">Django(Python)でシステム開発できるようになる記事_入門編<i class="fa fa-external-link"></i></span></li></ul><blockquote><p>15th鐵人賽目錄傳送門：<span class="exturl" data-url="aHR0cHM6Ly9pdGhlbHAuaXRob21lLmNvbS50dy91c2Vycy8yMDEzNTU1OC9pcm9ubWFuLzYyOTA=" title="https://ithelp.ithome.com.tw/users/20135558/ironman/6290">https://ithelp.ithome.com.tw/users/20135558/ironman/6290<i class="fa fa-external-link"></i></span></p></blockquote>]]>
    </content>
    <id>https://heidiliu2020.github.io/ironman-2023-day-25/</id>
    <link href="https://heidiliu2020.github.io/ironman-2023-day-25/"/>
    <published>2023-10-10T02:39:01.000Z</published>
    <summary>
      <![CDATA[<blockquote>
<p>原文連結：<span class="exturl" data-url="aHR0cHM6Ly9xaWl0YS5jb20vU2FrdTczMS9pdGVtcy83NDFmY2YwZjQwZGQ5ODllZTRmOCMlRTUlOUYlQkElRTYlOUMlQUMlRTglQTglQUQlRTglQTglODglRTclOTQlQkIlRTklOUQlQTIlRTglQTglQUQlRTglQTglODglRTYlQTklOUYlRTglODMlQkQlRTglQTglQUQlRTglQTglODglRTMlODMlODclRTMlODMlQkMlRTMlODIlQkYlRTglQTglQUQlRTglQTglODglRTMlODElQUUlRTQlQkQlOUMlRTMlODIlOEElRTYlOTYlQjk=" title="https://qiita.com/Saku731/items/741fcf0f40dd989ee4f8#%E5%9F%BA%E6%9C%AC%E8%A8%AD%E8%A8%88%E7%94%BB%E9%9D%A2%E8%A8%AD%E8%A8%88%E6%A9%9F%E8%83%BD%E8%A8%AD%E8%A8%88%E3%83%87%E3%83%BC%E3%82%BF%E8%A8%AD%E8%A8%88%E3%81%AE%E4%BD%9C%E3%82%8A%E6%96%B9">要件定義～システム設計ができる人材になれる記事 - Qiita<i class="fa fa-external-link"></i></span></p>
</blockquote>
<p>本篇介紹的主題是「需求定義到系統設計」，從為什麼需要需求定義，到如何進行這段流程，再接著說明基本設計的三個步驟，透過一步步解說，到最後完成系統設計圖。</p>
<p>文章架構如下：</p>
<ul>
<li>需求定義的必要性<ul>
<li>需求定義的目的</li>
</ul>
</li>
<li>確定需求定義的過程</li>
<li>在需求定義中決定的事項</li>
<li>基本設計（畫面設計・功能設計・資料設計）的作法</li>
<li>畫面設計（UI 設計）</li>
<li>功能設計</li>
<li>資料設計<ul>
<li>資料的具體內容</li>
<li>資料庫設計</li>
<li>資料的流向（Data Flow）</li>
</ul>
</li>
<li>進一步推進基本設計</li>
</ul>]]>
    </summary>
    <title>[2023 15th鐵人賽] Day25 - 成為具備需求定義～系統設計能力人才的文章！</title>
    <updated>2026-04-23T06:14:18.229Z</updated>
  </entry>
  <entry>
    <author>
      <name>Heidi Liu</name>
    </author>
    <category term="技術學習" scheme="https://heidiliu2020.github.io/categories/%E6%8A%80%E8%A1%93%E5%AD%B8%E7%BF%92/"/>
    <category term="2023鐵人賽" scheme="https://heidiliu2020.github.io/categories/%E6%8A%80%E8%A1%93%E5%AD%B8%E7%BF%92/2023%E9%90%B5%E4%BA%BA%E8%B3%BD/"/>
    <category term="工具篇" scheme="https://heidiliu2020.github.io/categories/%E6%8A%80%E8%A1%93%E5%AD%B8%E7%BF%92/2023%E9%90%B5%E4%BA%BA%E8%B3%BD/%E5%B7%A5%E5%85%B7%E7%AF%87/"/>
    <category term="ironman2023" scheme="https://heidiliu2020.github.io/tags/ironman2023/"/>
    <category term="Qiita" scheme="https://heidiliu2020.github.io/tags/Qiita/"/>
    <category term="ChatGPT" scheme="https://heidiliu2020.github.io/tags/ChatGPT/"/>
    <category term="Bing" scheme="https://heidiliu2020.github.io/tags/Bing/"/>
    <category term="GPT-4" scheme="https://heidiliu2020.github.io/tags/GPT-4/"/>
    <content>
      <![CDATA[<blockquote><p>原文連結：<span class="exturl" data-url="aHR0cHM6Ly9xaWl0YS5jb20vdGFrYW8tdGFrYXNzL2l0ZW1zLzE2YTcwNTJhNGEwZTg1N2I3Yzkw" title="https://qiita.com/takao-takass/items/16a7052a4a0e857b7c90">えっ、まだChatGPT使ってんの？ Bingは無料でGPT-4使えますよ！ - Qiita<i class="fa fa-external-link"></i></span></p></blockquote><p>這篇文章主要是介紹 Bing Chat，和 ChatGPT Plus 同樣支援 GPT-4，卻能夠免費使用！然而，使用次數限制卻是一大硬傷，這點文中也有提及，但用作個人開發等用途，或許也不失為一種選擇。</p><p>此外，作者也舉出幾項實際導入開發使用的範例，可提供參考：</p><ul><li>用於建立 Debug 配置</li><li>完成轉換（移植）腳本語言</li><li>改善程式碼</li></ul><span id="more"></span><p>以下正文開始。</p><hr><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>儘管使用這種吸睛的標題，但我無意說 ChatGPT 任何壞話。無庸置疑，ChatGPT 是非常優秀的產品，實際上也帶給世界巨大變化。ChatGPT 作為大力普及 LLM 的先驅者，是相當偉大的存在。</p><p>而這一回，我會將重點聚焦在使用費用上。</p><p>對於那些想使用 Chat GPT Plus（或已經在使用），但覺得價格有點高的人，希望這篇文章能夠有所幫助。</p><h2 id="ChatGPT⋯⋯感覺有點貴"><a href="#ChatGPT⋯⋯感覺有點貴" class="headerlink" title="ChatGPT⋯⋯感覺有點貴"></a>ChatGPT⋯⋯感覺有點貴</h2><p>GPT-4 的高準確度，真的很棒啊！</p><p>我在編寫程式，或決定使用哪些產品時也經常使用！</p><p>然而，要使用 ChatGPT 的 GPT-4，每月個需支付 3,000日圓(※)。</p><p>如果公司出錢的話完全沒問題，但如果是用於個人使用，老實說不覺得有點貴嗎？</p><blockquote><p>※ 確切來說是 20 美元，以 1 美元兌換 150 日圓的匯率計算，會是 3000 日圓。</p></blockquote><p><img src="https://hackmd.io/_uploads/BJocvZxb6.png"></p><h2 id="一起免費使用-GPT-4-吧"><a href="#一起免費使用-GPT-4-吧" class="headerlink" title="一起免費使用 GPT-4 吧"></a>一起免費使用 GPT-4 吧</h2><p>想使用 GPT-4！但每個月 3000 日圓有點貴啊！ ！</p><p>在這種情況下，試試免費使用 GPT-4 吧！ ！</p><p>是否能在不支付 3,000 日圓的情況使用 GPT-4？</p><p>是否真有這麼好的事情？</p><p>原來這種好事，還真的存在著。</p><h2 id="雖然叫做-Bing"><a href="#雖然叫做-Bing" class="headerlink" title="雖然叫做 Bing"></a>雖然叫做 Bing</h2><p>你是否聽過 Bing 搜尋呢？這是 Microsoft 的搜尋引擎。</p><p><span class="exturl" data-url="aHR0cHM6Ly93d3cuYmluZy5jb20vc2VhcmNoP3E9QmluZytBSSZhbXA7c2hvd2NvbnY9MSZhbXA7Rk9STT1ocGNvZHg=" title="https://www.bing.com/search?q=Bing+AI&amp;showconv=1&amp;FORM=hpcodx">https://www.bing.com/search?q=Bing+AI&amp;showconv=1&amp;FORM=hpcodx<i class="fa fa-external-link"></i></span></p><p>Bing 搜尋有個名為「Chat」的功能，其功能與 ChatGPT 完全相同，並且也可以使用 GPT-4。</p><p>選擇「より厳密に（更精確）」的選項，將得到更準確的答案（與 AI 查詢資訊來源一致）。<br>如果使用個人帳號登入，UI 會略有不同，可以透過開啟「使用 GPT-4」的開關來使用 GPT-4。</p><p>當然，這是可以免費使用的。</p><p><img src="https://hackmd.io/_uploads/HJjAoGeZT.png"></p><p>在過去兩週中，我透過 Bing Chat 來編寫程式和進行研究。我的感想是，Bing Chat 能做到與  ChatGPT 的 GPT-4（每個月 3,000日圓的方案）相同的事情。</p><p>首先，由於是 GPT-4，回答給出的程式碼相當準確。</p><p>我當時基於興趣，正在學習 Go 語言，僅透過複製貼上的動作，就完成了一個 Web 程式。</p><p>若是 ChatGPT 的免費版本（GPT-3.5），在許多情況下都需要進行些微修改，因此不出所料 GPT-4 表現得更好。</p><h2 id="唯一的缺點"><a href="#唯一的缺點" class="headerlink" title="唯一的缺點"></a>唯一的缺點</h2><p><del>依照 Microsoft 的慣例，使用 Bing Chat 必須透過 Microsoft Edge 瀏覽器。</del><br><del>請注意如果不是用 Edge 瀏覽器，就無法使用 Bing Chat。</del><br><del>Fu○k Microsoft！</del></p><p>2023 年 9 月 4 日 追記<br>Bing Chat 現在也可以在 Google Chrome 中使用了。謝謝 Microsoft！</p><p><img src="https://hackmd.io/_uploads/HJ-RXQgZp.png"></p><p>2023 年 9 月 28 日 追記<br>雖然在 Google Chrome 也可以使用 Bing Chat，但似乎只能保留 5 次對話紀錄。而在 Microsoft Edge 可保留 30 次對話紀錄，看來 Edge 以外的瀏覽器還是存在著限制⋯⋯（此外，這是以 Microsoft 帳號登入使用 Bing 為前提。）</p><p><img src="https://hackmd.io/_uploads/BJ-QEveZT.png"></p><p>上下分別代表使用 Microsoft Edge 和 Google Chrome 瀏覽器的對話紀錄次數。</p><h2 id="實踐：嘗試在實際開發中導入使用"><a href="#實踐：嘗試在實際開發中導入使用" class="headerlink" title="實踐：嘗試在實際開發中導入使用"></a>實踐：嘗試在實際開發中導入使用</h2><h3 id="建立用於-Debug-的配置！"><a href="#建立用於-Debug-的配置！" class="headerlink" title="建立用於 Debug 的配置！"></a>建立用於 Debug 的配置！</h3><p>雖然想要自己建立 VS Code 的 Debug 配置，但完全不知道該怎麼寫才好。</p><p>因此，我決定完全交給 Bing Chat。</p><p><img src="https://hackmd.io/_uploads/BJiXZrlZa.png"></p><p>將 Bing Chat 編寫的 Debug 配置複製貼上，並嘗試進行 Debug。 </p><p>結果，不需要任何人工介入，Debug 就可以完美進行。</p><p>這樣的高準確度，不愧是 GPT-4。 （如果是用 GPT-3.5，可能會出現部分錯誤，導致無法運行。）</p><p><img src="https://hackmd.io/_uploads/r1wIZSeWp.png"></p><h3 id="完成轉換（移植）腳本語言！"><a href="#完成轉換（移植）腳本語言！" class="headerlink" title="完成轉換（移植）腳本語言！"></a>完成轉換（移植）腳本語言！</h3><p>過去建立的 Windows 批次檔（.bat），通常缺乏可維護性。</p><p>如果習慣可能就沒有問題，但對於只熟悉高階語言（例如 Python 或 Java）的人來說，或許會感到困難。現代 Windows 似乎已經將 PowerShell 作為標準，那麼何不將其移植到 PowerShell，使其向高級語言一樣，藉此提高可維護性。</p><p>在這種情況下，透過 Bing Chat，語言轉換（移植）即可瞬間完成。</p><p><img src="https://hackmd.io/_uploads/SJAlE8xbp.png"></p><p>只需複製貼上 Bing Chat 生成的腳本，建立 PowerShell 腳本（.ps1 文件），即可正常運行。</p><p><img src="https://hackmd.io/_uploads/BJMzVLlW6.png"></p><h3 id="試著改善了程式！"><a href="#試著改善了程式！" class="headerlink" title="試著改善了程式！"></a>試著改善了程式！</h3><h4 id="之一：重構所寫的程式"><a href="#之一：重構所寫的程式" class="headerlink" title="之一：重構所寫的程式"></a>之一：重構所寫的程式</h4><p>對於自己編寫的程式碼，可能會想知道是否有更好的寫法。</p><p>但也不可能每次都請專家來檢查⋯⋯這種時候，就使用 GPT-4 來幫忙檢查重構吧！</p><p><img src="https://i.imgur.com/GnDAoz5.png"></p><h4 id="之二：在自己編寫的程式導入-OR-Mapper（對象關係對映）"><a href="#之二：在自己編寫的程式導入-OR-Mapper（對象關係對映）" class="headerlink" title="之二：在自己編寫的程式導入 OR Mapper（對象關係對映）"></a>之二：在自己編寫的程式導入 <span class="exturl" data-url="aHR0cHM6Ly96aC53aWtpcGVkaWEub3JnL3poLXR3LyVFNSVBRiVCOSVFOCVCMSVBMSVFNSU4NSVCMyVFNyVCMyVCQiVFNiU5OCVBMCVFNSVCMCU4NA==" title="https://zh.wikipedia.org/zh-tw/%E5%AF%B9%E8%B1%A1%E5%85%B3%E7%B3%BB%E6%98%A0%E5%B0%84">OR Mapper（對象關係對映）<i class="fa fa-external-link"></i></span></h4><p>在編寫程式時，你是否曾經出現過「有沒有更好的作法啊？」這樣的想法呢？<br>這種時候，就讓 GPT-4 來實現這個更好的做法吧！</p><p>這次因為不想對 SQL 硬編碼（Hard Code），因此想導入使用 OR Mapper。如果想要靠自己引入 OR Mapper，可能會花費相當大的學習成本。</p><p>但有了 GPT-4 就是一瞬間的事情，更友善的是，還可以得知推薦使的工具以及安裝方式（如以下回答，是介紹 Go 的使用方式）。</p><p><img src="https://i.imgur.com/yXdIYEN.png"></p><h4 id="之三：由於替換不同的-Library，需要修改程式"><a href="#之三：由於替換不同的-Library，需要修改程式" class="headerlink" title="之三：由於替換不同的 Library，需要修改程式"></a>之三：由於替換不同的 Library，需要修改程式</h4><p>在替換 Library 時，大多數的情況下，函數的使用方式也會改變。換句話說，必須全面重新檢查該程式。</p><p>因為通常會發生錯誤，因此需要瞭解該修復的部分，但是「那該如何重新編寫才對？」這件事將會產生學習成本。閱讀文本學習需要花費許多時間，希望能夠快速完成。</p><p>能夠自己閱讀和理解固然重要，但如果能更先查看完成的程式碼，或許有助於更快理解內容。特別是在工作時間內，可能無法提供太多的學習時間。</p><p>像這種程式修改的情況，有了 GPT-4 將能夠立即完成，準確性也非常出色，只需複製貼上程式碼，程式即可如預期運行。</p><p><img src="https://i.imgur.com/tW2Pckm.png"></p><h4 id="之四：將冗長的程式碼進行拆分"><a href="#之四：將冗長的程式碼進行拆分" class="headerlink" title="之四：將冗長的程式碼進行拆分"></a>之四：將冗長的程式碼進行拆分</h4><p>有時在實作中得意忘形，可能一不小心寫出冗長的程式碼⋯⋯<br>雖然想要拆分成多個檔案，但又不確定該如何分割才好⋯⋯</p><p>你是否有過這樣的想法呢？<br>在這種時候，使用 GPT-4 來協助進行分割吧。</p><p><img src="https://i.imgur.com/nIjqJce.png"></p><h2 id="最後"><a href="#最後" class="headerlink" title="最後"></a>最後</h2><p>至今為止，為了使用 GPT-4，必須每個月支付 3,000 日圓給 ChatGPT，但當我意識到 Bing Chat 的 GPT-4 也相當優秀後，便開始用於個人興趣的開發用途，並停止支付 ChatGPT 的費用。</p><p>GPT-4 回答的精確度非常高，和程式設計的相容性極佳。若是想使用 GPT-4 的同時節省費用，請務必嘗試使用 Bing Chat！</p><blockquote><p>15th鐵人賽目錄傳送門：<span class="exturl" data-url="aHR0cHM6Ly9pdGhlbHAuaXRob21lLmNvbS50dy91c2Vycy8yMDEzNTU1OC9pcm9ubWFuLzYyOTA=" title="https://ithelp.ithome.com.tw/users/20135558/ironman/6290">https://ithelp.ithome.com.tw/users/20135558/ironman/6290<i class="fa fa-external-link"></i></span></p></blockquote>]]>
    </content>
    <id>https://heidiliu2020.github.io/ironman-2023-day-24/</id>
    <link href="https://heidiliu2020.github.io/ironman-2023-day-24/"/>
    <published>2023-10-09T01:50:01.000Z</published>
    <summary>
      <![CDATA[<blockquote>
<p>原文連結：<span class="exturl" data-url="aHR0cHM6Ly9xaWl0YS5jb20vdGFrYW8tdGFrYXNzL2l0ZW1zLzE2YTcwNTJhNGEwZTg1N2I3Yzkw" title="https://qiita.com/takao-takass/items/16a7052a4a0e857b7c90">えっ、まだChatGPT使ってんの？ Bingは無料でGPT-4使えますよ！ - Qiita<i class="fa fa-external-link"></i></span></p>
</blockquote>
<p>這篇文章主要是介紹 Bing Chat，和 ChatGPT Plus 同樣支援 GPT-4，卻能夠免費使用！然而，使用次數限制卻是一大硬傷，這點文中也有提及，但用作個人開發等用途，或許也不失為一種選擇。</p>
<p>此外，作者也舉出幾項實際導入開發使用的範例，可提供參考：</p>
<ul>
<li>用於建立 Debug 配置</li>
<li>完成轉換（移植）腳本語言</li>
<li>改善程式碼</li>
</ul>]]>
    </summary>
    <title>[2023 15th鐵人賽] Day24 - 啊？你還在用 ChatGPT 嗎？ Bing 可以免費使用 GPT-4 喔！</title>
    <updated>2026-04-23T06:14:18.229Z</updated>
  </entry>
</feed>
