<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Blog on Bossagyu Blog</title><link>https://bossagyu.com/blog/</link><description>Recent content in Blog on Bossagyu Blog</description><generator>Hugo -- gohugo.io</generator><language>ja-jp</language><lastBuildDate>Sun, 19 Apr 2026 00:00:00 +0900</lastBuildDate><atom:link href="https://bossagyu.com/blog/index.xml" rel="self" type="application/rss+xml"/><item><title>技術習熟の3つのモデル：ドレイファス・守破離・意識的能力の4段階</title><link>https://bossagyu.com/blog/050-skill-proficiency-stages/</link><pubDate>Sun, 19 Apr 2026 00:00:00 +0900</pubDate><guid>https://bossagyu.com/blog/050-skill-proficiency-stages/</guid><description>&lt;h2 id="概要">概要&lt;/h2>
&lt;p>私たちが何らかの技能を身につけていく過程は、一様ではありません。初めは手順書に頼り、やがて状況に応じた判断ができるようになり、さらに進むと無意識のうちに最適な動作ができるようになります。こうした段階的な変化を捉えるために、古くから多くのモデルが提唱されてきました。&lt;/p>
&lt;p>本記事では、代表的な3つの習熟フレームワーク、&lt;strong>ドレイファスモデル&lt;/strong>、&lt;strong>守破離&lt;/strong>、&lt;strong>意識的能力の4段階&lt;/strong> を紹介し、それぞれの背景・段階構造・特徴を比較します。自分の現在地を知り、次の一歩を設計し、他者の学びを支えるための道具として、これらのモデルがどう使えるかを整理します。&lt;/p>
&lt;h2 id="なぜ習熟段階を理解するのか">なぜ習熟段階を理解するのか&lt;/h2>
&lt;p>習熟を段階として捉えるメリットは、大きく3つあります。&lt;/p>
&lt;p>1つ目は &lt;strong>自己理解&lt;/strong> です。自分が今どの段階にいるのかが分かると、次に何を身につけるべきかが見えてきます。新人の頃に必要だった「手順を正確にこなす力」と、中堅になってから求められる「状況に応じた判断力」はまったく別のスキルです。段階を意識することで、努力の方向を誤らずに済みます。&lt;/p>
&lt;p>2つ目は &lt;strong>学習設計&lt;/strong> です。最終ゴールだけを見て漠然と努力するより、段階的なマイルストーンを置くほうが継続しやすく、成長も確認しやすくなります。段階ごとに「今の自分に合った学び方」が変わることも、多くのモデルが示しています。&lt;/p>
&lt;p>3つ目は &lt;strong>他者理解&lt;/strong> です。後輩を指導する場面や、チームメンバーの成長を見守る場面で、相手がどの段階にいるかを見立てられれば、必要な支援や関わり方を選びやすくなります。&lt;/p>
&lt;p>ただし、モデルは現実そのものではありません。人の習熟は連続的で、領域ごとに段階がばらつくこともあります。本記事で紹介するモデルは、あくまで思考の補助線として使うものだと覚えておきたいところです。&lt;/p>
&lt;h2 id="ドレイファスモデル">ドレイファスモデル&lt;/h2>
&lt;h3 id="出自と背景">出自と背景&lt;/h3>
&lt;p>ドレイファスモデルは、Stuart E. Dreyfus と Hubert L. Dreyfus の兄弟が1980年にカリフォルニア大学バークレー校でまとめた報告書 &amp;ldquo;A Five-Stage Model of the Mental Activities Involved in Directed Skill Acquisition&amp;rdquo; で提唱されたモデルです。もともとは米空軍科学研究局の委託研究であり、パイロットの訓練やチェスプレイヤー、自動車運転など、幅広い技能習得を題材に論じられました。&lt;/p>
&lt;p>兄のスチュワートはオペレーションズ・リサーチ、弟のヒューバートは哲学（現象学）を専門とし、2人の共著として「熟達者の判断が必ずしも明示的なルールで説明できない」という問題意識を形にしたのがこのモデルです。&lt;/p>
&lt;p>後にソフトウェアエンジニアリングの領域でも、Andy Hunt の著書『Pragmatic Thinking and Learning』などを通じて広く紹介され、エンジニアの成長を語るときの共通言語のひとつになりました。&lt;/p>
&lt;h3 id="5段階の構造">5段階の構造&lt;/h3>
&lt;p>ドレイファスモデルは、人が未経験の状態から熟達者に至るまでを5段階で描きます。&lt;/p>
&lt;div class="mermaid-wrapper" onclick="openMermaidModal(this)">
&lt;pre class="mermaid">flowchart LR
A[Novice&lt;br/>初心者] --> B[Advanced Beginner&lt;br/>上級初心者]
B --> C[Competent&lt;br/>一人前]
C --> D[Proficient&lt;br/>中堅]
D --> E[Expert&lt;br/>達人]
&lt;/pre>
&lt;span class="mermaid-hint">クリックで拡大&lt;/span>
&lt;/div>
&lt;p>各段階の特徴は次の通りです。&lt;/p>
&lt;p>&lt;strong>1. Novice（初心者）&lt;/strong>
状況を文脈から切り離し、与えられたルールに従って行動します。ルールを守ることで精一杯で、例外や全体像を見る余裕はありません。料理で言えばレシピ通りに計量する段階、運転で言えばマニュアル通りにペダルとハンドルを操作する段階です。&lt;/p>
&lt;p>&lt;strong>2. Advanced Beginner（上級初心者）&lt;/strong>
経験を積む中で、状況に付随する特徴に気づき始めます。とはいえ、どの特徴が重要でどれが些末かの見分けはまだつかず、すべてを同じ重みで扱いがちです。先輩に細かく質問し、カバーできないケースに直面して初めて学ぶ段階と言えます。&lt;/p>
&lt;p>&lt;strong>3. Competent（一人前）&lt;/strong>
複数の情報の中から重要なものを選び、自分で目標と計画を立てられるようになります。ルールは内面化され、状況への適応度が上がります。一方で「正解はひとつではない」ことが分かり始め、判断の責任を感じて悩むのもこの段階の特徴です。&lt;/p>
&lt;p>&lt;strong>4. Proficient（中堅）&lt;/strong>
状況を全体として捉え、何が起きているかを俯瞰できるようになります。過去の類似ケースを引き寄せてパターン認識で判断し、細部をルールで詰めるのはその後、という順序に逆転していきます。直感と分析の両方を使い分けるハイブリッドな段階です。&lt;/p>
&lt;p>&lt;strong>5. Expert（達人）&lt;/strong>
明示的なルールを意識せず、状況に埋め込まれた「正解」を直感的に導きます。熟達した医師が患者を一目見て診断の見当をつけたり、棋士が盤面を見て候補手を絞り込むように、分析より前に判断が立ち上がります。ただし、環境が通常と大きく異なる場合には、達人も意識的な分析に戻る必要があります。&lt;/p>
&lt;h3 id="段階を移るときに起きる認知の変化">段階を移るときに起きる認知の変化&lt;/h3>
&lt;p>このモデルが示唆する重要な点は、段階を上がるにつれて認知の中心が「分析」から「パターン認識」、さらに「直感」へと移っていくことです。これは単に速くなるというだけでなく、何を見ているかが変わるという質的な変化です。&lt;/p>
&lt;p>また、上位段階に進むには、ルールに従うだけでなく「ルールを文脈に応じて解釈する経験」が欠かせません。ドレイファス兄弟は、熟達とは暗黙知の獲得であり、それを教科書的に教えることは難しいと繰り返し指摘しています。&lt;/p>
&lt;h2 id="守破離">守破離&lt;/h2>
&lt;h3 id="出自と背景-1">出自と背景&lt;/h3>
&lt;p>守破離（しゅはり）は、日本の武道・茶道・華道といった伝統芸能の世界で発達してきた修行の段階論です。明確な成文として現れるのは江戸時代中期の茶人、川上不白（1719-1807）による『不白筆記』と伝えられていますが、その源流はさらに古く、世阿弥（1363-1443）が『風姿花伝』で示した「序破急」の発想や、千利休の教えに連なるとされます。&lt;/p>
&lt;p>利休道歌として知られる一首に、「規矩作法 守り尽くして 破るとも 離るるとても 本（もと）を忘るな」という歌があり、これが守破離という言葉を象徴する一節としてよく引用されます。型を守り、やがて破り、さらに離れても、根本を忘れてはならない——というメッセージです。&lt;/p>
&lt;p>近年では、アジャイル開発・スクラム・デザインパターン学習など、西洋生まれの知的実践にも「守破離」が応用され、日本語圏のソフトウェア業界では共通語のひとつになりました。&lt;/p>
&lt;h3 id="3段階の構造">3段階の構造&lt;/h3>
&lt;p>守破離は、師匠と弟子の関係を前提とした3段階のモデルです。&lt;/p>
&lt;div class="mermaid-wrapper" onclick="openMermaidModal(this)">
&lt;pre class="mermaid">flowchart LR
A[守&lt;br/>型を守る] --> B[破&lt;br/>型を破る]
B --> C[離&lt;br/>型から離れる]
C -.新たな型.-> A
&lt;/pre>
&lt;span class="mermaid-hint">クリックで拡大&lt;/span>
&lt;/div>
&lt;p>&lt;strong>守&lt;/strong>
師匠や流派の教える型を、忠実に守って稽古する段階です。自分の解釈を加えずに正確に反復することが求められます。「なぜこうするのか」は後から分かることが多く、まずは身体や思考に型を染み込ませる時期と言えます。&lt;/p>
&lt;p>&lt;strong>破&lt;/strong>
守の段階で体得した型を下敷きに、他流派の型や自分の工夫を取り入れて型を破っていく段階です。単なる反抗ではなく、比較と検証を通じて型を吟味する営みであり、ここで初めて「なぜそうするのか」が腹落ちしてきます。&lt;/p>
&lt;p>&lt;strong>離&lt;/strong>
型から離れ、独自の境地に至る段階です。このとき、型を捨てたのではなく、型が自分の一部になっているため、外から見ると型に縛られず自在に動いているように見えます。利休道歌の「本を忘るな」は、離に至っても根は守の時期にあることを戒めた言葉です。&lt;/p>
&lt;h3 id="西洋モデルとの違い">西洋モデルとの違い&lt;/h3>
&lt;p>ドレイファスモデルが個人の認知活動の変化を描くのに対し、守破離は &lt;strong>共同体の中で型を受け渡す&lt;/strong> ことを前提にしています。型には流派の歴史や先人の知恵が凝縮されており、それを正確に受け継ぐ「守」の時期がまずある、という思想です。&lt;/p>
&lt;p>また、守破離は &lt;strong>学習者の主体性の現れ方&lt;/strong> を段階で示すモデルとも読めます。守では受容、破では対話と検証、離では創造が中心になり、単なるスキル上達ではなく人格の成熟や世界観の拡張まで含意する点も特徴的です。&lt;/p>
&lt;h3 id="現代での応用例">現代での応用例&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>ソフトウェア開発のパターン学習&lt;/strong>: まずはデザインパターンをそのまま適用し（守）、プロジェクトに応じて変形し（破）、最終的にはパターンに頼らず問題に応じた設計ができる（離）、という学びの道筋として語られます。&lt;/li>
&lt;li>&lt;strong>スクラム導入&lt;/strong>: 最初はスクラムガイドに忠実に従い、チームが習熟してきたら自分たちのコンテキストに合わせてイベントや役割を調整する、という段階論として引用されます。&lt;/li>
&lt;li>&lt;strong>新人教育全般&lt;/strong>: 手順書通りに動く時期、自分なりの工夫を加える時期、仕組みそのものを設計する時期、という段階的な関わり方の指針として用いられます。&lt;/li>
&lt;/ul>
&lt;h2 id="意識的能力の4段階">意識的能力の4段階&lt;/h2>
&lt;h3 id="出自と背景-2">出自と背景&lt;/h3>
&lt;p>「意識的能力の4段階（Four Stages of Competence、別名 Conscious Competence Ladder）」は、1970年代に米国の Gordon Training International で講師を務めた Noel Burch が提唱したとされる学習モデルです。ただし、原型となる考え方はそれ以前から存在し、心理学者 Abraham Maslow に帰属させる説、同じく Thomas Gordon に帰属させる説もあるため、出典は完全に一意ではありません。&lt;/p>
&lt;p>このモデルの特徴は、学習者の &lt;strong>能力の有無&lt;/strong> と、その能力の &lt;strong>自覚の有無&lt;/strong> という2つの軸を組み合わせて段階を整理している点です。能力そのものだけでなく「自分が何を知らないか／知っているか」をどれだけ意識できているかに焦点を当てており、学習者の心理面を扱いやすいフレームとして知られています。&lt;/p>
&lt;h3 id="4段階の構造">4段階の構造&lt;/h3>
&lt;p>4つの段階は、意識と能力の組み合わせで次のように表現できます。&lt;/p>
&lt;div class="mermaid-wrapper" onclick="openMermaidModal(this)">
&lt;pre class="mermaid">quadrantChart
title 意識的能力の4段階
x-axis "能力が低い" --> "能力が高い"
y-axis "自覚していない" --> "自覚している"
quadrant-1 "意識的有能"
quadrant-2 "意識的無能"
quadrant-3 "無意識的無能"
quadrant-4 "無意識的有能"
&lt;/pre>
&lt;span class="mermaid-hint">クリックで拡大&lt;/span>
&lt;/div>
&lt;p>&lt;strong>1. Unconscious Incompetence（無意識的無能）&lt;/strong>
できないことに気づいていない段階です。自分が何を知らないかすら分からないため、学習の必要性を感じません。ここからの脱出には、外部からのフィードバックや「できない現実」に気づかせる刺激が鍵になります。&lt;/p>
&lt;p>&lt;strong>2. Conscious Incompetence（意識的無能）&lt;/strong>
できないことを自覚した段階です。4段階のうち心理的にもっともストレスが大きく、「自分はダメだ」と感じて学習を諦めやすい危険地帯でもあります。ここを抜けるには、小さな成功体験を重ねられる環境と、安心して失敗できる関係性が重要です。&lt;/p>
&lt;p>&lt;strong>3. Conscious Competence（意識的有能）&lt;/strong>
意識的に注意を払えばできる段階です。手順を頭で辿りながら実行する状態で、疲れやすく遅くはあるものの、一貫して成果を出せるようになります。ここでの繰り返しが、次の段階への足場になります。&lt;/p>
&lt;p>&lt;strong>4. Unconscious Competence（無意識的有能）&lt;/strong>
考えなくても自然にできる段階です。タスクが自動化され、他のことに注意を振り向ける余裕が生まれます。ただし、言語化できないがゆえに「なぜそうするのか」を人に教えるのが難しくなる、という新たな課題が出てきます。&lt;/p>
&lt;h3 id="心理面の意義と第5段階">心理面の意義と第5段階&lt;/h3>
&lt;p>このモデルが他のモデルと異なるのは、&lt;strong>意識的無能の「谷」&lt;/strong> をはっきりと示すところです。学ぶとは、まず「できない自分」に直面することであり、ここでの支援が学習の継続可否を大きく左右します。&lt;/p>
&lt;p>近年では、第4段階の先に &lt;strong>Conscious Competence of Unconscious Competence（反省的有能／教えられる状態）&lt;/strong> を置く派生モデルも提唱されています。これは、自動化された自分のスキルを再度意識に上げ、他者に言語化して伝えられる状態を指します。指導者・メンターに求められるのは、まさにこの第5段階の能力と言えるでしょう。&lt;/p>
&lt;h2 id="3つのフレームワークの比較">3つのフレームワークの比較&lt;/h2>
&lt;p>3つのモデルは一見似ているようで、着目している側面が異なります。まずは観点ごとに整理してみます。&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>観点&lt;/th>
&lt;th>ドレイファスモデル&lt;/th>
&lt;th>守破離&lt;/th>
&lt;th>意識的能力の4段階&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>段階数&lt;/td>
&lt;td>5&lt;/td>
&lt;td>3&lt;/td>
&lt;td>4（派生で5）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>観察する側面&lt;/td>
&lt;td>判断・行動の質&lt;/td>
&lt;td>型との関係&lt;/td>
&lt;td>能力と自覚の組み合わせ&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>文化的背景&lt;/td>
&lt;td>西洋・哲学（現象学）&lt;/td>
&lt;td>日本・伝統芸能&lt;/td>
&lt;td>西洋・教育心理&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>成立時期&lt;/td>
&lt;td>1980年&lt;/td>
&lt;td>江戸時代（源流はより古い）&lt;/td>
&lt;td>1970年代&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>学習者像&lt;/td>
&lt;td>個人の認知の変化&lt;/td>
&lt;td>共同体で型を受け継ぐ存在&lt;/td>
&lt;td>意識と無意識を行き来する存在&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>熟達の到達点&lt;/td>
&lt;td>直感的判断&lt;/td>
&lt;td>型から離れた自在さ&lt;/td>
&lt;td>自動化された能力&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="共通点">共通点&lt;/h3>
&lt;p>表面的な違いを超えて、3つのモデルには共通する構造もあります。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>初期は外部依存、後期は自律&lt;/strong> という方向性。ルールや型、意識的な努力から始まり、やがてそれらが内面化されていく。&lt;/li>
&lt;li>&lt;strong>最上位は言語化しにくい熟達&lt;/strong> として描かれる。ドレイファスの Expert、守破離の離、4段階の Unconscious Competence はいずれも、明示的な指示を超えた領域を指します。&lt;/li>
&lt;li>&lt;strong>段階の移行は直線ではない&lt;/strong>。どのモデルも、ある段階に必要な学びが次の段階に進むと不要になるのではなく、基礎として残り続けることを前提にしています。&lt;/li>
&lt;/ul>
&lt;h3 id="相違点">相違点&lt;/h3>
&lt;p>一方で、着目点の違いは明確です。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>ドレイファスモデル&lt;/strong> は「判断の質」にフォーカスします。ルール適用から直感的パターン認識への変化を、認知科学的に描いています。&lt;/li>
&lt;li>&lt;strong>守破離&lt;/strong> は「型との関係」にフォーカスします。型を共有する共同体の存在と、そこから個として独立していく筋道を描きます。&lt;/li>
&lt;li>&lt;strong>意識的能力の4段階&lt;/strong> は「意識の状態」にフォーカスします。特に「自分ができないと気づく瞬間」という心理的な転換点を明示的に扱います。&lt;/li>
&lt;/ul>
&lt;h3 id="どのモデルをいつ使うか">どのモデルをいつ使うか&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>自分や他者の行動パターンを分析したい&lt;/strong> とき → ドレイファスモデル&lt;/li>
&lt;li>&lt;strong>師匠・メンター・型が存在する学習環境&lt;/strong> で段階を捉えたいとき → 守破離&lt;/li>
&lt;li>&lt;strong>学習者の心理状態を支えたい、挫折しそうな人を支援したい&lt;/strong> とき → 意識的能力の4段階&lt;/li>
&lt;/ul>
&lt;p>目的に応じて使い分けるのが実践的で、1つに絞る必要はありません。むしろ、ある学習者の状況を複数のモデルで見てみると、違う側面が見えてくることが多いでしょう。&lt;/p>
&lt;h2 id="応用学習とプロダクト設計への活かし方">応用：学習とプロダクト設計への活かし方&lt;/h2>
&lt;p>これらのフレームワークは、単なる知識として知っておくだけでなく、実際の学習や設計の現場で使える道具でもあります。代表的な3つの使い道を挙げておきます。&lt;/p>
&lt;h3 id="1-自分の学習計画に使う">1. 自分の学習計画に使う&lt;/h3>
&lt;p>身につけたい領域について、「今の自分はどの段階にいるか」を複数のモデルから見立ててみます。ドレイファスで見ると一人前だが、意識的能力で見ると実はまだ意識的有能に留まっている——といったズレを発見すると、次に必要な学習行動が具体化します。たとえば「型を破る経験」が足りないと気づけば、他流派（他チーム・他ツール・他言語）の考え方に触れに行く計画が立てられます。&lt;/p>
&lt;h3 id="2-他者の指導メンタリングに使う">2. 他者の指導・メンタリングに使う&lt;/h3>
&lt;p>相手の段階によって、効果的な関わり方は変わります。&lt;/p>
&lt;ul>
&lt;li>初心者・無意識的無能には、正確なルールや型を示し、まず安全に反復できる環境を用意する&lt;/li>
&lt;li>意識的無能の学習者には、小さな成功体験の積み重ねと、失敗を肯定する関係性を&lt;/li>
&lt;li>一人前・破の段階には、判断の責任を渡し、意思決定のレビューで支える&lt;/li>
&lt;li>中堅・達人・離の段階には、自身の経験を言語化して後進に伝える機会を&lt;/li>
&lt;/ul>
&lt;p>相手の段階を見誤ると、助けが押しつけになったり、放任が放置になったりします。段階を意識することで、関わり方を選ぶ精度が上がります。&lt;/p>
&lt;h3 id="3-プロダクトアプリ設計に使う">3. プロダクト・アプリ設計に使う&lt;/h3>
&lt;p>ユーザーの習熟段階に応じた設計も、これらのモデルから多くを学べます。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>オンボーディング&lt;/strong>: 初心者には明示的なガイド・手順・制約を設ける。守の段階に相当します。&lt;/li>
&lt;li>&lt;strong>モード切替&lt;/strong>: 習熟したユーザーには、ショートカットや上級モードを用意し、ルール表示を減らす。破や離の段階に寄り添う設計です。&lt;/li>
&lt;li>&lt;strong>意識的無能の支援&lt;/strong>: 「何が分からないか分からない」状態のユーザーには、何を学ぶべきかを明示する診断・チュートリアルが効きます。&lt;/li>
&lt;li>&lt;strong>段階の移行を促すフィードバック&lt;/strong>: 単にエラーを返すのではなく、今の段階の次に進むヒントを示す設計は、学習者としてのユーザーに寄り添う姿勢の表れです。&lt;/li>
&lt;/ul>
&lt;p>こうした視点でプロダクトを眺め直すと、機能の優先順位や情報設計が整理しやすくなります。&lt;/p>
&lt;h2 id="まとめ">まとめ&lt;/h2>
&lt;p>技術習熟を段階として捉えるフレームワークは数多くありますが、ドレイファスモデル・守破離・意識的能力の4段階の3つは、それぞれ着目点が異なり、互いを補い合う関係にあります。&lt;/p>
&lt;ul>
&lt;li>ドレイファスモデルは &lt;strong>判断の質の変化&lt;/strong> を&lt;/li>
&lt;li>守破離は &lt;strong>型との関係の変化&lt;/strong> を&lt;/li>
&lt;li>意識的能力の4段階は &lt;strong>意識と能力の組み合わせ&lt;/strong> を&lt;/li>
&lt;/ul>
&lt;p>描くモデルです。&lt;/p>
&lt;p>モデルはあくまで地図であり、領土そのものではありません。現実の学習者は連続的に変化し、領域によって段階がばらつきもします。それでも、地図があれば自分の現在地を見立て、次の一歩を選ぶ助けになります。自分自身の学習、他者への関わり、そしてプロダクト設計のそれぞれで、これらのモデルを状況に応じて使い分けてみると、見えてくるものが変わるはずです。&lt;/p>
&lt;h3 id="参考文献">参考文献&lt;/h3>
&lt;ul>
&lt;li>Stuart E. Dreyfus, Hubert L. Dreyfus, &amp;ldquo;A Five-Stage Model of the Mental Activities Involved in Directed Skill Acquisition&amp;rdquo;（1980年, U.S. Air Force Office of Scientific Research）&lt;/li>
&lt;li>Andy Hunt『Pragmatic Thinking and Learning: Refactor Your Wetware』（2008年, Pragmatic Bookshelf）&lt;/li>
&lt;li>川上不白『不白筆記』&lt;/li>
&lt;li>世阿弥『風姿花伝』&lt;/li>
&lt;li>Noel Burch, &amp;ldquo;The Four Stages for Learning Any New Skill&amp;rdquo;（1970年代, Gordon Training International）&lt;/li>
&lt;/ul></description></item><item><title>FastAPIでCRUD APIを作る入門チュートリアル</title><link>https://bossagyu.com/blog/049-fastapi-crud/</link><pubDate>Sat, 28 Feb 2026 00:00:00 +0900</pubDate><guid>https://bossagyu.com/blog/049-fastapi-crud/</guid><description>&lt;h2 id="概要">概要&lt;/h2>
&lt;p>&lt;a class="link" href="https://fastapi.tiangolo.com/" target="_blank" rel="noopener"
>FastAPI&lt;/a>は、Pythonで高性能なAPIを構築するためのWebフレームワークです。以下の特徴があります。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>型ヒントベース&lt;/strong>: Pythonの型ヒントを活用し、リクエスト・レスポンスの自動バリデーションを実現&lt;/li>
&lt;li>&lt;strong>自動ドキュメント&lt;/strong>: Swagger UI（OpenAPI）が自動生成され、ブラウザからAPIをテスト可能&lt;/li>
&lt;li>&lt;strong>高速&lt;/strong>: Starlette + Pydanticをベースにしており、Node.jsやGoに匹敵するパフォーマンス&lt;/li>
&lt;/ul>
&lt;p>この記事では、FastAPIを使ってTODOアプリのCRUD APIを作成する手順を解説します。&lt;/p>
&lt;h2 id="前提条件">前提条件&lt;/h2>
&lt;ul>
&lt;li>Python 3.11以上&lt;/li>
&lt;li>uvがインストール済み（&lt;a class="link" href="https://bossagyu.com/blog/032-python-uv/" >MacでUVを用いてPythonの開発環境を構築する&lt;/a>を参照）&lt;/li>
&lt;/ul>
&lt;h2 id="環境構築">環境構築&lt;/h2>
&lt;p>まず、プロジェクトを作成し、必要なパッケージをインストールします。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">uv init fastapi-todo
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd&lt;/span> fastapi-todo
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">uv add fastapi uvicorn
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;code>fastapi&lt;/code>がWebフレームワーク本体、&lt;code>uvicorn&lt;/code>がASGIサーバーです。&lt;/p>
&lt;h2 id="hello-world">Hello World&lt;/h2>
&lt;p>まずは最小構成でFastAPIの動作を確認します。&lt;code>main.py&lt;/code>を作成してください。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">fastapi&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">FastAPI&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">app&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">FastAPI&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@app.get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;/&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">read_root&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;message&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Hello, FastAPI!&amp;#34;&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>サーバーを起動します。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">uv run uvicorn main:app --reload
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;code>--reload&lt;/code>オプションを付けると、コードの変更時に自動でリロードされます。&lt;/p>
&lt;p>ブラウザで http://localhost:8000 にアクセスすると、以下のレスポンスが返ります。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>&lt;span class="nt">&amp;#34;message&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Hello, FastAPI!&amp;#34;&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="swagger-uiの確認">Swagger UIの確認&lt;/h3>
&lt;p>FastAPIの大きな魅力は、自動生成されるAPIドキュメントです。http://localhost:8000/docs にアクセスすると、Swagger UIが表示されます。&lt;/p>
&lt;p>ここからAPIを直接テストできるため、開発中はcurlやPostmanなしでも動作確認が可能です。&lt;/p>
&lt;h2 id="crud-apiの実装">CRUD APIの実装&lt;/h2>
&lt;p>TODOアプリのCRUD APIを実装していきます。&lt;/p>
&lt;h3 id="モデルの定義">モデルの定義&lt;/h3>
&lt;p>まず、PydanticモデルでTODOのデータ構造を定義します。&lt;code>main.py&lt;/code>を以下のように書き換えてください。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">fastapi&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">FastAPI&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">HTTPException&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">pydantic&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">BaseModel&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">app&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">FastAPI&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">TodoCreate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">BaseModel&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">title&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">completed&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">bool&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">False&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">TodoResponse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">BaseModel&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">id&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">int&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">title&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">completed&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">bool&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">todos&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">dict&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nb">dict&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">next_id&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">int&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;ul>
&lt;li>&lt;code>TodoCreate&lt;/code>: 新規作成・更新時のリクエストボディ&lt;/li>
&lt;li>&lt;code>TodoResponse&lt;/code>: レスポンスの型定義&lt;/li>
&lt;li>&lt;code>todos&lt;/code>: インメモリのデータストア（dict）&lt;/li>
&lt;li>&lt;code>next_id&lt;/code>: IDの自動採番用カウンター&lt;/li>
&lt;/ul>
&lt;h3 id="作成post">作成（POST）&lt;/h3>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@app.post&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;/todos&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">response_model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">TodoResponse&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">create_todo&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">todo&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">TodoCreate&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">global&lt;/span> &lt;span class="n">next_id&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">todo_data&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">next_id&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;title&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">todo&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">title&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;completed&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">todo&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">completed&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">todos&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">next_id&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">todo_data&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">next_id&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="mi">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">todo_data&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;code>response_model=TodoResponse&lt;/code>を指定することで、レスポンスの型がSwagger UIに反映されます。&lt;/p>
&lt;h3 id="一覧取得get">一覧取得（GET）&lt;/h3>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@app.get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;/todos&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">response_model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="nb">list&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">TodoResponse&lt;/span>&lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">list_todos&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nb">list&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">todos&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">values&lt;/span>&lt;span class="p">())&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="個別取得get">個別取得（GET）&lt;/h3>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@app.get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;/todos/&lt;/span>&lt;span class="si">{todo_id}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">response_model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">TodoResponse&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">get_todo&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">todo_id&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">int&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">todo_id&lt;/span> &lt;span class="ow">not&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">todos&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">raise&lt;/span> &lt;span class="n">HTTPException&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">status_code&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">404&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">detail&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;Todo not found&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">todos&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">todo_id&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>存在しないIDの場合は&lt;code>HTTPException&lt;/code>で404エラーを返します。&lt;/p>
&lt;h3 id="更新put">更新（PUT）&lt;/h3>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@app.put&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;/todos/&lt;/span>&lt;span class="si">{todo_id}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">response_model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">TodoResponse&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">update_todo&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">todo_id&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">int&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">todo&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">TodoCreate&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">todo_id&lt;/span> &lt;span class="ow">not&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">todos&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">raise&lt;/span> &lt;span class="n">HTTPException&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">status_code&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">404&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">detail&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;Todo not found&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">todo_data&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">todo_id&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;title&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">todo&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">title&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;completed&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">todo&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">completed&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">todos&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">todo_id&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">todo_data&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">todo_data&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="削除delete">削除（DELETE）&lt;/h3>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@app.delete&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;/todos/&lt;/span>&lt;span class="si">{todo_id}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">delete_todo&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">todo_id&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">int&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">todo_id&lt;/span> &lt;span class="ow">not&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">todos&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">raise&lt;/span> &lt;span class="n">HTTPException&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">status_code&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">404&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">detail&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;Todo not found&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">del&lt;/span> &lt;span class="n">todos&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">todo_id&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;detail&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Todo deleted&amp;#34;&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="完成したコード">完成したコード&lt;/h3>
&lt;p>上記をすべてまとめた&lt;code>main.py&lt;/code>の全体像は以下のとおりです。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;span class="lnt">54
&lt;/span>&lt;span class="lnt">55
&lt;/span>&lt;span class="lnt">56
&lt;/span>&lt;span class="lnt">57
&lt;/span>&lt;span class="lnt">58
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">fastapi&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">FastAPI&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">HTTPException&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">pydantic&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">BaseModel&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">app&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">FastAPI&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">TodoCreate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">BaseModel&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">title&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">completed&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">bool&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">False&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">TodoResponse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">BaseModel&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">id&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">int&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">title&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">completed&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">bool&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">todos&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">dict&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nb">dict&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">next_id&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">int&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@app.post&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;/todos&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">response_model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">TodoResponse&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">create_todo&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">todo&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">TodoCreate&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">global&lt;/span> &lt;span class="n">next_id&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">todo_data&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">next_id&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;title&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">todo&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">title&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;completed&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">todo&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">completed&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">todos&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">next_id&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">todo_data&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">next_id&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="mi">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">todo_data&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@app.get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;/todos&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">response_model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="nb">list&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">TodoResponse&lt;/span>&lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">list_todos&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nb">list&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">todos&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">values&lt;/span>&lt;span class="p">())&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@app.get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;/todos/&lt;/span>&lt;span class="si">{todo_id}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">response_model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">TodoResponse&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">get_todo&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">todo_id&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">int&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">todo_id&lt;/span> &lt;span class="ow">not&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">todos&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">raise&lt;/span> &lt;span class="n">HTTPException&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">status_code&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">404&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">detail&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;Todo not found&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">todos&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">todo_id&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@app.put&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;/todos/&lt;/span>&lt;span class="si">{todo_id}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">response_model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">TodoResponse&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">update_todo&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">todo_id&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">int&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">todo&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">TodoCreate&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">todo_id&lt;/span> &lt;span class="ow">not&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">todos&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">raise&lt;/span> &lt;span class="n">HTTPException&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">status_code&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">404&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">detail&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;Todo not found&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">todo_data&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">todo_id&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;title&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">todo&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">title&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;completed&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">todo&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">completed&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">todos&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">todo_id&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">todo_data&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">todo_data&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@app.delete&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;/todos/&lt;/span>&lt;span class="si">{todo_id}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">delete_todo&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">todo_id&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">int&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">todo_id&lt;/span> &lt;span class="ow">not&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">todos&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">raise&lt;/span> &lt;span class="n">HTTPException&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">status_code&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">404&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">detail&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;Todo not found&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">del&lt;/span> &lt;span class="n">todos&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">todo_id&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;detail&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Todo deleted&amp;#34;&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="動作確認">動作確認&lt;/h2>
&lt;p>サーバーを起動して、Swagger UIから動作確認を行います。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">uv run uvicorn main:app --reload
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>http://localhost:8000/docs にアクセスし、以下の順にテストしてください。&lt;/p>
&lt;h3 id="1-todoの作成">1. TODOの作成&lt;/h3>
&lt;p>&lt;code>POST /todos&lt;/code>を開き、「Try it out」をクリックして以下のJSONを入力します。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;title&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;買い物に行く&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;completed&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">false&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>「Execute」をクリックすると、以下のようなレスポンスが返ります。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;title&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;買い物に行く&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;completed&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">false&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="2-一覧取得">2. 一覧取得&lt;/h3>
&lt;p>&lt;code>GET /todos&lt;/code>を実行すると、作成したTODOの一覧が返ります。&lt;/p>
&lt;h3 id="3-更新">3. 更新&lt;/h3>
&lt;p>&lt;code>PUT /todos/1&lt;/code>で&lt;code>completed&lt;/code>を&lt;code>true&lt;/code>に変更してみましょう。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;title&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;買い物に行く&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;completed&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="4-削除">4. 削除&lt;/h3>
&lt;p>&lt;code>DELETE /todos/1&lt;/code>を実行すると、TODOが削除されます。&lt;code>GET /todos&lt;/code>で空の配列が返ることを確認してください。&lt;/p>
&lt;h2 id="まとめ">まとめ&lt;/h2>
&lt;p>この記事では、FastAPIを使ってTODO管理のCRUD APIを作成しました。FastAPIの主な利点をまとめます。&lt;/p>
&lt;ul>
&lt;li>Pydanticモデルによるリクエスト・レスポンスの自動バリデーション&lt;/li>
&lt;li>Swagger UIによるインタラクティブなAPIドキュメント&lt;/li>
&lt;li>Pythonの型ヒントだけでAPIの仕様が定義できるシンプルさ&lt;/li>
&lt;/ul>
&lt;p>今回はインメモリのデータストアを使いましたが、実際のアプリケーションではSQLAlchemyなどのORMを使ってデータベースと連携することになります。&lt;/p>
&lt;h2 id="関連記事">関連記事&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/032-python-uv/" >MacでUVを用いてPythonの開発環境を構築する&lt;/a> - Python環境構築&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/048-lawve-backend/" >GCSとGemini File Search APIを自動同期するイベント駆動バックエンドの設計&lt;/a> - FastAPIの実践例&lt;/li>
&lt;/ul></description></item><item><title>GCSとGemini File Search APIを自動同期するイベント駆動バックエンドの設計</title><link>https://bossagyu.com/blog/048-lawve-backend/</link><pubDate>Sun, 15 Feb 2026 00:00:00 +0900</pubDate><guid>https://bossagyu.com/blog/048-lawve-backend/</guid><description>&lt;h2 id="概要">概要&lt;/h2>
&lt;p>デジタル庁主催の「法令×デジタル」ハッカソン第三弾に参加し、法令横断検索プロダクト「Lawve」を開発しました。Lawveは、e-Gov法令データやユーザーがアップロードしたドキュメントを横断的に自然言語検索できるプロダクトです。&lt;/p>
&lt;p>この記事では、筆者が担当したバックエンドのアーキテクチャ設計について解説します。バックエンドの主な責務は「Google Cloud Storage（GCS）にファイルが配置・削除されたとき、Gemini File Search APIの状態を自動的に同期する」ことです。&lt;/p>
&lt;h2 id="システム全体像">システム全体像&lt;/h2>
&lt;p>Lawveのシステム構成は以下の通りです。&lt;/p>
&lt;div class="mermaid-wrapper" onclick="openMermaidModal(this)">
&lt;pre class="mermaid">flowchart TB
subgraph ユーザー
U[ブラウザ]
end
subgraph GCP
subgraph フロントエンド
FE[Cloud Run&lt;br/>Next.js]
end
subgraph バックエンド
CR[Cloud Run&lt;br/>FastAPI]
end
subgraph ストレージ・データ
GCS[(Cloud Storage)]
FS[(Firestore)]
end
subgraph イベント基盤
EA[Eventarc]
end
subgraph AI
GEMINI[Gemini File&lt;br/>Search API]
end
end
subgraph 外部API
EGOV[e-Gov&lt;br/>法令API]
end
U --> FE
FE --> GEMINI
FE --> FS
FE --> EGOV
FE -->|ファイルアップロード| GCS
GCS -->|イベント通知| EA
EA -->|CloudEvents| CR
CR -->|登録・削除| GEMINI
CR -->|ダウンロード| GCS
&lt;/pre>
&lt;span class="mermaid-hint">クリックで拡大&lt;/span>
&lt;/div>
&lt;h3 id="各コンポーネントの役割">各コンポーネントの役割&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>コンポーネント&lt;/th>
&lt;th>役割&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Cloud Run（Next.js）&lt;/td>
&lt;td>フロントエンド。検索UI、ファイルアップロード、検索結果の表示&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Cloud Run（FastAPI）&lt;/td>
&lt;td>バックエンド。GCSのイベントを受けてGemini File Search APIを同期&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Cloud Storage&lt;/td>
&lt;td>ドキュメントの保管場所。Single Source of Truth&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Eventarc&lt;/td>
&lt;td>GCSのファイル変更イベントをCloud Runにルーティング&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Gemini File Search API&lt;/td>
&lt;td>ドキュメントの全文検索・セマンティック検索を提供&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Firestore&lt;/td>
&lt;td>ドキュメントメタデータ、コメント情報の管理&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>e-Gov法令API&lt;/td>
&lt;td>法令データの取得&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>この記事では、バックエンドの Cloud Run（FastAPI）の設計に焦点を当てます。&lt;/p>
&lt;h2 id="イベント駆動アーキテクチャの設計">イベント駆動アーキテクチャの設計&lt;/h2>
&lt;h3 id="なぜイベント駆動を選んだか">なぜイベント駆動を選んだか&lt;/h3>
&lt;p>法令検索プロダクトでは、ドキュメントがGCSに配置されたタイミングでGemini File Search APIに自動登録される必要があります。フロントエンドからの同期APIを作る方法もありますが、以下の理由でイベント駆動アーキテクチャを採用しました。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>疎結合&lt;/strong>: フロントエンドはGCSにファイルを配置するだけでよく、バックエンドの存在を意識しない&lt;/li>
&lt;li>&lt;strong>信頼性&lt;/strong>: GCSのイベントをEventarcが確実に検知し、Cloud Runに配信する&lt;/li>
&lt;li>&lt;strong>運用ツールとの親和性&lt;/strong>: CLIやスクリプトからGCSにファイルを配置しても同じように同期される&lt;/li>
&lt;/ul>
&lt;h3 id="eventarcによるイベントルーティング">Eventarcによるイベントルーティング&lt;/h3>
&lt;p>Eventarcは2種類のGCSイベントを監視し、Cloud Runのエンドポイントにルーティングします。&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>イベント&lt;/th>
&lt;th>トリガー条件&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>google.cloud.storage.object.v1.finalized&lt;/code>&lt;/td>
&lt;td>ファイルがGCSにアップロードされたとき&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>google.cloud.storage.object.v1.deleted&lt;/code>&lt;/td>
&lt;td>ファイルがGCSから削除されたとき&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Cloud RunのエンドポイントはCloudEventsフォーマットでイベントを受信します。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@app.post&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;/&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">async&lt;/span> &lt;span class="k">def&lt;/span> &lt;span class="nf">handle_storage_event&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">request&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Request&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">headers&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">dict&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">request&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">headers&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">body&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="n">request&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">body&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">event&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">from_http&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">headers&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">body&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">event_handler&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">process&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">event&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">result&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="ファイルアップロード時の処理フロー">ファイルアップロード時の処理フロー&lt;/h3>
&lt;p>ファイルがGCSに配置されると、以下のフローで処理されます。&lt;/p>
&lt;ol>
&lt;li>&lt;strong>イベント受信・パスフィルタリング&lt;/strong>: &lt;code>file-search/archive/&lt;/code> プレフィックスを持つファイルのみ処理対象とする&lt;/li>
&lt;li>&lt;strong>メタデータ抽出&lt;/strong>: GCSパスから &lt;code>law_id&lt;/code> と &lt;code>source_type&lt;/code> を自動抽出&lt;/li>
&lt;li>&lt;strong>既存ドキュメント削除&lt;/strong>: 同じ &lt;code>source_path&lt;/code> を持つドキュメントがあれば削除（重複防止）&lt;/li>
&lt;li>&lt;strong>ファイルダウンロード&lt;/strong>: GCSからローカルの一時ファイルにダウンロード&lt;/li>
&lt;li>&lt;strong>File Search Storeに登録&lt;/strong>: メタデータを付与してGemini File Search APIに登録&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">EventHandler&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">_handle_event_by_type&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">event_type&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">bucket_name&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">file_name&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">event_type&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;google.cloud.storage.object.v1.finalized&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">_handle_file_upload&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">bucket_name&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">file_name&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">elif&lt;/span> &lt;span class="n">event_type&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;google.cloud.storage.object.v1.deleted&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">_handle_file_delete&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">bucket_name&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">file_name&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="ファイル削除時の処理フロー">ファイル削除時の処理フロー&lt;/h3>
&lt;p>ファイルがGCSから削除されると、対応するドキュメントをGemini File Search APIからも削除します。&lt;/p>
&lt;ol>
&lt;li>&lt;strong>イベント受信・パスフィルタリング&lt;/strong>&lt;/li>
&lt;li>&lt;strong>メタデータでFile Search Store内を検索&lt;/strong>: &lt;code>source_path&lt;/code> メタデータで一致するドキュメントを特定&lt;/li>
&lt;li>&lt;strong>ドキュメント削除&lt;/strong>: File Search Storeから該当ドキュメントを削除&lt;/li>
&lt;/ol>
&lt;h2 id="gemini-file-search-apiとの統合">Gemini File Search APIとの統合&lt;/h2>
&lt;h3 id="gcpエコシステムでの統一">GCPエコシステムでの統一&lt;/h3>
&lt;p>Lawveではインフラ全体をGCPで統一する方針を取りました。RAGサービスとしてOpenAIやPineconeなど他の選択肢もありますが、Cloud Run、GCS、EventarcとシームレスにつながるGemini File Search APIを採用しています。&lt;/p>
&lt;h3 id="file-search-storeの概要">File Search Storeの概要&lt;/h3>
&lt;p>Gemini File Search Storeは、ドキュメントを登録するとベクトル化・インデックス化を行い、セマンティック検索を提供するマネージドサービスです。バックエンドでは以下の操作を行います。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>ドキュメント登録&lt;/strong>: ファイルとメタデータをアップロード&lt;/li>
&lt;li>&lt;strong>ドキュメント検索&lt;/strong>: メタデータをキーに既存ドキュメントを検索&lt;/li>
&lt;li>&lt;strong>ドキュメント削除&lt;/strong>: 不要になったドキュメントを削除&lt;/li>
&lt;/ul>
&lt;h3 id="メタデータを活用した管理">メタデータを活用した管理&lt;/h3>
&lt;p>各ドキュメントには以下の3つのメタデータを付与し、管理に活用しています。&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>メタデータ&lt;/th>
&lt;th>用途&lt;/th>
&lt;th>例&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>law_id&lt;/code>&lt;/td>
&lt;td>法令ID。法令の一意識別に使用&lt;/td>
&lt;td>&lt;code>323AC0000000205&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>source_type&lt;/code>&lt;/td>
&lt;td>ドキュメントの分類&lt;/td>
&lt;td>&lt;code>user&lt;/code>, &lt;code>doc&lt;/code>, &lt;code>admin&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>source_path&lt;/code>&lt;/td>
&lt;td>GCSの完全パス。ドキュメントの一意識別に使用&lt;/td>
&lt;td>&lt;code>gs://bucket/file-search/archive/user/323AC0000000205/医療法.txt&lt;/code>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;code>source_path&lt;/code> はGCSパスそのものであるため、GCSのファイルとFile Search Store内のドキュメントを一意に対応付けることができます。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="n">metadata&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;law_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">law_id&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;source_path&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">source_path&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;source_type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">source_type&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">custom_metadata&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;key&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">key&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;string_value&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">value&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">key&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">value&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">metadata&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">items&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="汎用性を持たせた設計の工夫">汎用性を持たせた設計の工夫&lt;/h2>
&lt;h3 id="パスベースのメタデータ抽出">パスベースのメタデータ抽出&lt;/h3>
&lt;p>バックエンドは法令データに限定せず、GCSの特定パスに配置されたあらゆるファイルを処理できるよう設計しています。パスの規約は以下の通りです。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">gs://&amp;lt;bucket&amp;gt;/file-search/archive/&amp;lt;source_type&amp;gt;/&amp;lt;law_id&amp;gt;/&amp;lt;file_name&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>パスから &lt;code>source_type&lt;/code> と &lt;code>law_id&lt;/code> を正規表現で自動抽出します。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># パスパターン&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">GCS_PATH_PATTERN&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="sa">r&lt;/span>&lt;span class="s1">&amp;#39;file-search/archive/([^/]+)/([^/]+)/.+&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">parse_gcs_path&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">gcs_path&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="n">Tuple&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">Optional&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">str&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="n">Optional&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">str&lt;/span>&lt;span class="p">]]:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">match&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">re&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">search&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">GCS_PATH_PATTERN&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">gcs_path&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="k">match&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="k">match&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">group&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="k">match&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">group&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">None&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kc">None&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>この設計により、法令データだけでなく社内文書や技術ドキュメントなど、任意のドキュメントを同じ仕組みで管理できます。&lt;code>source_type&lt;/code> を変えるだけで分類を追加でき、パス規約さえ守ればバックエンドのコード変更は不要です。&lt;/p>
&lt;h3 id="gcsとgemini-file-search-apiの状態同期の保証">GCSとGemini File Search APIの状態同期の保証&lt;/h3>
&lt;p>GCSを Single Source of Truth（信頼できる唯一の情報源）とし、File Search Storeの状態を常にGCSと一致させることを設計原則としています。&lt;/p>
&lt;p>&lt;strong>アップロード時の冪等性&lt;/strong>: 同じファイルを再度アップロードした場合、既存のドキュメントを削除してから再登録します。これにより重複を防ぎつつ、内容の更新にも対応できます。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">process_file_upload&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">bucket_name&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">file_path&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">source_path&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;gs://&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">bucket_name&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">/&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">file_path&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 既存ドキュメントの確認・削除&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">_delete_existing_documents&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">source_path&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 新規アップロード&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">law_id&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">extract_law_id&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">file_path&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">local_file_path&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">gcs_client&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">download_to_temp_file&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">bucket_name&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">file_path&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># ...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">success&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">store_client&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">upload_file&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">local_file_path&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">file_path&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">metadata&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">success&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;strong>削除時の連動&lt;/strong>: GCSからファイルが削除されると、File Search Storeからも対応するドキュメントが自動的に削除されます。&lt;code>source_path&lt;/code> メタデータをキーに検索し、一致するドキュメントを削除します。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">process_file_delete&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">bucket_name&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">file_path&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">source_path&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;gs://&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">bucket_name&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">/&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">file_path&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">documents&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">store_client&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">find_documents_by_source_path&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">source_path&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="ow">not&lt;/span> &lt;span class="n">documents&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">False&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">store_client&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">delete_document&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">documents&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="ローカルダウンロード経由の設計">ローカルダウンロード経由の設計&lt;/h3>
&lt;p>GCSからGemini File Search APIへの登録は、一度ローカルの一時ファイルにダウンロードしてから行う設計にしています。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">download_to_temp_file&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">bucket_name&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">file_path&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">_&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ext&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">path&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">splitext&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">file_path&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="ow">not&lt;/span> &lt;span class="n">ext&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ext&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;.txt&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">temp_fd&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">temp_path&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">tempfile&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">mkstemp&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">suffix&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">ext&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">close&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">temp_fd&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">blob&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">download_to_filename&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">temp_path&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">temp_path&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>直接アップロードする方がシンプルですが、ローカルに一度保存することで以下のメリットがあります。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>拡張性&lt;/strong>: 将来ExcelをCSVに変換するなど、アップロード前のファイル加工処理を挟める&lt;/li>
&lt;li>&lt;strong>MIME判定&lt;/strong>: ファイル拡張子を保持することで正確なMIMEタイプ判定が可能&lt;/li>
&lt;li>&lt;strong>デバッグ&lt;/strong>: 問題発生時にローカルファイルの内容を確認できる&lt;/li>
&lt;/ul>
&lt;h2 id="エラーハンドリングと運用設計">エラーハンドリングと運用設計&lt;/h2>
&lt;h3 id="常に200-okを返す設計">常に200 OKを返す設計&lt;/h3>
&lt;p>Cloud RunのエンドポイントはEventarcからのリクエストに対し、エラーが発生しても常に200 OKを返します。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@app.post&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;/&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">async&lt;/span> &lt;span class="k">def&lt;/span> &lt;span class="nf">handle_storage_event&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">request&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Request&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">try&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">headers&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">dict&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">request&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">headers&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">body&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="n">request&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">body&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">event&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">from_http&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">headers&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">body&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">event_handler&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">process&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">event&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">result&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">except&lt;/span> &lt;span class="ne">Exception&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="n">e&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;status&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;error&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;message&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;Event processing failed: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="nb">str&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;note&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Error logged and notified via Slack&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>これはEventarcのリトライを防ぐための設計です。エラー時に4xx/5xxを返すとEventarcがイベントを再送し、同じエラーが繰り返し発生します。結果としてSlack通知が重複し、ログが汚染されます。エラーはSlack通知とログで把握し、200 OKで受け取り済みとすることで運用上の問題を防いでいます。&lt;/p>
&lt;h3 id="slack通知によるエラー監視">Slack通知によるエラー監視&lt;/h3>
&lt;p>File Search Storeへのアップロード失敗時にはSlack Webhookでエラー通知を送信します。通知にはGCSパスとエラー詳細を含め、運用者が問題箇所を素早く特定できるようにしています。&lt;/p>
&lt;p>このSlack通知による監視はハッカソンの開発規模では十分ですが、実運用ではCloud Monitoringによるアラート設定やCloud Loggingとの連携、Dead Letter Queueによる失敗イベントの再処理など、より堅牢な監視・リカバリの仕組みが必要になるでしょう。&lt;/p>
&lt;h3 id="遅延初期化によるクライアント管理">遅延初期化によるクライアント管理&lt;/h3>
&lt;p>Gemini APIクライアントやGCSクライアントは遅延初期化（Lazy Initialization）を採用しています。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@property&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">client&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="n">Optional&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">genai&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">Client&lt;/span>&lt;span class="p">]:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">_client&lt;/span> &lt;span class="ow">is&lt;/span> &lt;span class="kc">None&lt;/span> &lt;span class="ow">and&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">api_key&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">_client&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">genai&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">Client&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">api_key&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">api_key&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">_client&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>APIキーが未設定の場合でもアプリケーションが起動でき、テスト環境やローカル開発時にすべての環境変数を揃えなくても動作します。&lt;/p>
&lt;h2 id="まとめ">まとめ&lt;/h2>
&lt;p>Lawveのバックエンドは、以下の設計原則に基づいて構築しました。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>イベント駆動による疎結合な自動同期&lt;/strong>: Eventarcを活用し、フロントエンドとバックエンドを疎結合に保ちつつGCSの変更を自動的にGemini File Search APIに反映&lt;/li>
&lt;li>&lt;strong>メタデータ駆動の汎用設計&lt;/strong>: パス規約からメタデータを自動抽出し、法令以外のドキュメントにも対応可能な拡張性を確保&lt;/li>
&lt;li>&lt;strong>GCSをSingle Source of Truthとした状態管理&lt;/strong>: 冪等なアップロードと連動削除により、GCSとFile Search Storeの状態を常に一致させる&lt;/li>
&lt;/ul>
&lt;p>イベント駆動アーキテクチャとGCPのマネージドサービスを組み合わせることで、少ないコードで信頼性の高いドキュメント同期基盤を実現できました。GCSにファイルを配置するだけで検索可能になるシンプルさは、ハッカソンの限られた時間内での開発にも大きく貢献しました。&lt;/p>
&lt;p>ソースコードは&lt;a class="link" href="https://github.com/23-u-don/lawve-backend" target="_blank" rel="noopener"
>GitHub&lt;/a>で公開しています。&lt;/p>
&lt;h2 id="関連記事">関連記事&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/032-python-uv/" >MacでUVを用いてPythonの開発環境を構築する&lt;/a>（Python環境構築）&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/044-openai-response-api/" >OpenAI Response APIの使い方まとめ&lt;/a>（OpenAI APIの利用）&lt;/li>
&lt;/ul></description></item><item><title>【初心者向け】掃除リマインダーBotの使い方チュートリアル</title><link>https://bossagyu.com/blog/047-clean-bot-tutorial/</link><pubDate>Sat, 07 Feb 2026 20:00:00 +0900</pubDate><guid>https://bossagyu.com/blog/047-clean-bot-tutorial/</guid><description>&lt;h2 id="このチュートリアルで学べること">このチュートリアルで学べること&lt;/h2>
&lt;p>掃除リマインダーBotの基本的な使い方を、5つのステップで学びます。&lt;/p>
&lt;ol>
&lt;li>友だち追加&lt;/li>
&lt;li>タスクを追加する&lt;/li>
&lt;li>通知を設定する&lt;/li>
&lt;li>通知を受け取る&lt;/li>
&lt;li>タスクを完了する&lt;/li>
&lt;/ol>
&lt;p>所要時間は約5分です。一緒にやってみましょう！&lt;/p>
&lt;hr>
&lt;h2 id="step-1-友だち追加">Step 1: 友だち追加&lt;/h2>
&lt;p>まずはBotを友だちに追加します。&lt;/p>
&lt;p>以下のQRコードをLINEで読み取るか、リンクをタップしてください。&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/blog/047-clean-bot-tutorial/qrcode.png"
width="360"
height="360"
srcset="https://bossagyu.com/blog/047-clean-bot-tutorial/qrcode_hu95bee7a533128dc6d7ed5659019be1cb_23771_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/047-clean-bot-tutorial/qrcode_hu95bee7a533128dc6d7ed5659019be1cb_23771_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="QRコード"
class="gallery-image"
data-flex-grow="100"
data-flex-basis="240px"
>&lt;/p>
&lt;p>&lt;a class="link" href="https://lin.ee/kdwsLys" target="_blank" rel="noopener"
>友だち追加はこちら&lt;/a>&lt;/p>
&lt;p>友だち追加が完了すると、トーク画面が開きます。&lt;/p>
&lt;hr>
&lt;h2 id="step-2-タスクを追加する">Step 2: タスクを追加する&lt;/h2>
&lt;p>次に、管理したい掃除タスクを登録します。&lt;/p>
&lt;p>トーク画面で以下のように入力して送信してください。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">追加 掃除機 7
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>これは「&lt;strong>掃除機&lt;/strong>を&lt;strong>7日ごと&lt;/strong>にリマインドする」という意味です。&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/blog/047-clean-bot-tutorial/image.png"
width="533"
height="236"
srcset="https://bossagyu.com/blog/047-clean-bot-tutorial/image_huc08a9d1732aa8d0ae3b083392db511d7_31589_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/047-clean-bot-tutorial/image_huc08a9d1732aa8d0ae3b083392db511d7_31589_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="タスク追加"
class="gallery-image"
data-flex-grow="225"
data-flex-basis="542px"
>&lt;/p>
&lt;p>Botから「掃除機を追加しました」と返信が来れば成功です。&lt;/p>
&lt;h3 id="ポイント">ポイント&lt;/h3>
&lt;ul>
&lt;li>「追加」の後にスペースを入れる&lt;/li>
&lt;li>タスク名は自由（トイレ、お風呂、換気扇など）&lt;/li>
&lt;li>日数は何日ごとにリマインドするかを指定&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="step-3-通知を設定する">Step 3: 通知を設定する&lt;/h2>
&lt;p>タスクを追加したら、通知を受け取る曜日と時間を設定しましょう。&lt;/p>
&lt;p>以下のように入力して送信してください。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">通知設定 月水金 7
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>これは「&lt;strong>月・水・金&lt;/strong>の&lt;strong>7時&lt;/strong>に通知する」という意味です。&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/blog/047-clean-bot-tutorial/image-1.png"
width="533"
height="215"
srcset="https://bossagyu.com/blog/047-clean-bot-tutorial/image-1_hue39ccd1901424b77d43674ee1ebfb08c_26209_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/047-clean-bot-tutorial/image-1_hue39ccd1901424b77d43674ee1ebfb08c_26209_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="通知設定"
class="gallery-image"
data-flex-grow="247"
data-flex-basis="594px"
>&lt;/p>
&lt;p>「通知設定を更新しました」と返信が来れば設定完了です。&lt;/p>
&lt;h3 id="ポイント-1">ポイント&lt;/h3>
&lt;ul>
&lt;li>曜日は続けて入力（月水金、毎日なら月火水木金土日）&lt;/li>
&lt;li>時間は0〜23の数字で指定&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="step-4-通知を受け取る">Step 4: 通知を受け取る&lt;/h2>
&lt;p>設定した曜日・時間になると、期限切れのタスクがあれば通知が届きます。&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/blog/047-clean-bot-tutorial/image-4.png"
width="570"
height="139"
srcset="https://bossagyu.com/blog/047-clean-bot-tutorial/image-4_hu3c9aefd24389e58c00d5d9b27a8218d8_19622_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/047-clean-bot-tutorial/image-4_hu3c9aefd24389e58c00d5d9b27a8218d8_19622_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="通知受診"
class="gallery-image"
data-flex-grow="410"
data-flex-basis="984px"
>&lt;/p>
&lt;p>通知には以下の情報が含まれます。&lt;/p>
&lt;ul>
&lt;li>タスク名&lt;/li>
&lt;li>何日超過しているか（例: +3日）&lt;/li>
&lt;/ul>
&lt;p>超過日数が多いほど、優先的に掃除すべきタスクです。&lt;/p>
&lt;hr>
&lt;h2 id="step-5-タスクを完了する">Step 5: タスクを完了する&lt;/h2>
&lt;p>掃除が終わったら、Botに報告しましょう。&lt;/p>
&lt;p>以下のように入力して送信してください。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">完了 掃除機
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;img src="https://bossagyu.com/blog/047-clean-bot-tutorial/image-2.png"
width="533"
height="214"
srcset="https://bossagyu.com/blog/047-clean-bot-tutorial/image-2_hu9ea239d6fbc92079178c5229c5346eea_27479_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/047-clean-bot-tutorial/image-2_hu9ea239d6fbc92079178c5229c5346eea_27479_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="タスク完了"
class="gallery-image"
data-flex-grow="249"
data-flex-basis="597px"
>&lt;/p>
&lt;p>「掃除機を完了しました」と返信が来れば完了です。&lt;/p>
&lt;p>タスクの期限がリセットされ、次は7日後に再び通知されます。&lt;/p>
&lt;h3 id="ポイント-2">ポイント&lt;/h3>
&lt;ul>
&lt;li>完了のタスクはスペース区切りで複数複数指定可能です。
&lt;ul>
&lt;li>&lt;code>完了 掃除機 トイレ&lt;/code>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="まとめ">まとめ&lt;/h2>
&lt;p>お疲れさまでした！これで基本的な使い方は完了です。&lt;/p>
&lt;h3 id="覚えておきたいコマンド">覚えておきたいコマンド&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>やりたいこと&lt;/th>
&lt;th>コマンド&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>タスクを追加&lt;/td>
&lt;td>&lt;code>追加 [タスク名] [日数]&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>タスクを完了&lt;/td>
&lt;td>&lt;code>完了 [タスク名]&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>通知を設定&lt;/td>
&lt;td>&lt;code>通知設定 [曜日] [時間]&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>残りタスクを確認&lt;/td>
&lt;td>&lt;code>残り&lt;/code>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="次のステップ">次のステップ&lt;/h3>
&lt;p>より詳しい機能については、以下の記事をご覧ください。&lt;/p>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/045-clean-bot/" >掃除リマインダーBotの全機能紹介&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>ぜひ活用して、掃除を習慣化しましょう！&lt;/p>
&lt;h2 id="関連記事">関連記事&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/045-clean-bot/" >掃除を忘れない！掃除リマインダーLINE Bot&lt;/a>（全機能の紹介）&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/002-line-messaging-api/" >LINE Messaging APIの登録と使い方&lt;/a>（LINE Bot開発の基礎）&lt;/li>
&lt;/ul></description></item><item><title>AWS Lambda + LINE Botで掃除リマインダーを作る</title><link>https://bossagyu.com/blog/046-clean-bot-technical/</link><pubDate>Sat, 07 Feb 2026 19:00:00 +0900</pubDate><guid>https://bossagyu.com/blog/046-clean-bot-technical/</guid><description>&lt;h2 id="概要">概要&lt;/h2>
&lt;p>家族で使える掃除リマインダーLINE Botを作りました。この記事では、その技術的な実装について解説します。&lt;/p>
&lt;p>完成したBotはこちら: &lt;a class="link" href="https://bossagyu.com/blog/045-clean-bot/" >掃除リマインダーBot&lt;/a>&lt;/p>
&lt;h2 id="アーキテクチャ">アーキテクチャ&lt;/h2>
&lt;div class="mermaid-wrapper" onclick="openMermaidModal(this)">
&lt;pre class="mermaid">flowchart LR
subgraph ユーザー操作
A[LINE User]
end
subgraph AWS
B[API Gateway]
C[Lambda&lt;br/>process_user_message]
D[(S3&lt;br/>JSON)]
E[Lambda&lt;br/>push_message_periodically]
F[EventBridge&lt;br/>毎時実行]
end
A -->|メッセージ送信| B
B --> C
C &lt;--> D
E &lt;--> D
F -->|トリガー| E
E -->|通知| A
&lt;/pre>
&lt;span class="mermaid-hint">クリックで拡大&lt;/span>
&lt;/div>
&lt;h3 id="使用サービス">使用サービス&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>サービス&lt;/th>
&lt;th>用途&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>API Gateway (HTTP API)&lt;/td>
&lt;td>LINEのWebhook受信&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Lambda&lt;/td>
&lt;td>メッセージ処理、定期通知&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>S3&lt;/td>
&lt;td>ユーザーごとのタスクデータ保存&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>EventBridge&lt;/td>
&lt;td>定期実行スケジュール&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="なぜこの構成か">なぜこの構成か&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>サーバーレス&lt;/strong>: 使った分だけ課金、運用負荷ゼロ&lt;/li>
&lt;li>&lt;strong>S3&lt;/strong>: RDSより安価、JSONでシンプルに管理&lt;/li>
&lt;li>&lt;strong>SAM&lt;/strong>: Infrastructure as Codeでデプロイ管理&lt;/li>
&lt;/ul>
&lt;h2 id="プロジェクト構成">プロジェクト構成&lt;/h2>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">clean-bot/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── lib/ # コアモジュール
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├── clean_task.py # タスク状態管理
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├── message.py # コマンドパーサー
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├── line.py # LINE API
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ └── s3_client.py # S3操作
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── test/ # テスト
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── line_clean_bot.py # Lambdaエントリーポイント
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── template.yaml # SAMテンプレート
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">└── Makefile # 開発コマンド
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="実装のポイント">実装のポイント&lt;/h2>
&lt;h3 id="1-lambdaエントリーポイント">1. Lambdaエントリーポイント&lt;/h3>
&lt;p>2つのLambda関数を用意しています。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># line_clean_bot.py&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">process_user_message&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">event&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">context&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;LINEメッセージのWebhookハンドラ&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">body&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">json&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">loads&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">event&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;body&amp;#39;&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># ユーザーID or グループIDを取得&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">body&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;events&amp;#39;&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="s1">&amp;#39;source&amp;#39;&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="s1">&amp;#39;type&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s1">&amp;#39;user&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">line_id&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">body&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;events&amp;#39;&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="s1">&amp;#39;source&amp;#39;&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="s1">&amp;#39;userId&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">else&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">line_id&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">body&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;events&amp;#39;&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="s1">&amp;#39;source&amp;#39;&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="s1">&amp;#39;groupId&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">message&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">body&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;events&amp;#39;&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="s1">&amp;#39;message&amp;#39;&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="s1">&amp;#39;text&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># S3からユーザーデータを取得&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">s3client&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">S3client&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">BUCKET_NAME&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">obj_key&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">line_id&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="s1">&amp;#39;.json&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="ow">not&lt;/span> &lt;span class="n">s3client&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">check_exist_object&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">obj_key&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">s3client&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">update_object&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">obj_key&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;{&amp;#34;tasks&amp;#34;: []}&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># メッセージを処理して返信&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># ...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">push_message_periodically&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">event&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">context&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;定期実行でリマインド通知を送信&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">current_time&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">datetime&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">now&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">timedelta&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">hours&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">9&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1"># JST&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">s3client&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">S3client&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">BUCKET_NAME&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">obj_list&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">s3client&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">list_objects&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">clean_task&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">CleanTask&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">s3client&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get_object_body&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">obj_list&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">key&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 通知条件を満たすか判定&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">clean_task&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">should_notify&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">current_time&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># メッセージ送信&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># ...&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="2-タスクの期限管理">2. タスクの期限管理&lt;/h3>
&lt;p>「前回から何日経過したか」でタスクの期限を判定します。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># lib/clean_task.py&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">__evaluate_cleanup_timing&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">task&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;タスクが期限切れか判定&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">task_time&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">datetime&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">strptime&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">task&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;updated_at&amp;#39;&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">date_format&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">now&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="n">task_time&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">days&lt;/span> &lt;span class="o">&amp;gt;=&lt;/span> &lt;span class="nb">int&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">task&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;duration&amp;#39;&lt;/span>&lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">get_todo_tasks&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;期限切れのタスク一覧を取得&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="n">task&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="n">task&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">tasks&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="ow">not&lt;/span> &lt;span class="n">task&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;paused&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kc">False&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="ow">and&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">__evaluate_cleanup_timing&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">task&lt;/span>&lt;span class="p">)]&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="3-通知判定ロジック">3. 通知判定ロジック&lt;/h3>
&lt;p>ユーザーが設定した曜日・時間にのみ通知します。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">should_notify&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">current_time&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;通知すべきか判定&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="ow">not&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">notification&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;enabled&amp;#39;&lt;/span>&lt;span class="p">]:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">False&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 曜日チェック&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">weekday_map&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s1">&amp;#39;月&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s1">&amp;#39;火&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s1">&amp;#39;水&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s1">&amp;#39;木&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">4&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s1">&amp;#39;金&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">5&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s1">&amp;#39;土&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">6&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s1">&amp;#39;日&amp;#39;&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">current_weekday&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">weekday_map&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">current_time&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">weekday&lt;/span>&lt;span class="p">()]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">current_weekday&lt;/span> &lt;span class="ow">not&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">notification&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;days&amp;#39;&lt;/span>&lt;span class="p">]:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">False&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 時刻チェック&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">current_time&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">hour&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">notification&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;hour&amp;#39;&lt;/span>&lt;span class="p">]:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">False&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 今日すでに通知済みかチェック&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># ...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">True&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="4-コマンドパーサー">4. コマンドパーサー&lt;/h3>
&lt;p>シンプルな文字列分割でコマンドを解析します。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># lib/message.py&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">get_return_message&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">message&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">s3client&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">task_operation&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">__get_task_operation_name&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">message&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1"># 最初の単語&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">task_name&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">__get_task_name&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">message&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1"># 2番目の単語&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">task_operation&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s1">&amp;#39;完了&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">task_name&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">__get_all_task_name&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">message&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">clean_task&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">update_task_updated_at&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">task_name&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">s3client&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">update_object&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">object_keyname&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">clean_task&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get_json&lt;/span>&lt;span class="p">())&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="s2">&amp;#34;完了しました&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">task_operation&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s1">&amp;#39;追加&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">duration&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">__get_duration&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">message&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1"># 3番目の単語&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">clean_task&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">add_task&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">task_name&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">duration&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># ...&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="5-データ構造json">5. データ構造（JSON）&lt;/h3>
&lt;p>ユーザーごとに1つのJSONファイルをS3に保存します。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;tasks&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;task_name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;掃除機&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;updated_at&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;2024-01-01 12:00:00&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;duration&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">7&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;paused&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">false&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;notification&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;enabled&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;days&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;月&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;水&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;金&amp;#34;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;hour&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">7&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;last_notified_at&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;2024-01-01 07:00:00&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="samテンプレート">SAMテンプレート&lt;/h2>
&lt;p>AWS SAMを使ってインフラを定義します。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># template.yaml&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">AWSTemplateFormatVersion&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;2010-09-09&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">Transform&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">AWS::Serverless-2016-10-31&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">Parameters&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">ChannelAccessToken&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">Type&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">String&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">NoEcho&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">BucketName&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">Type&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">String&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">Default&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">your-bucket-name&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">Globals&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">Function&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">Runtime&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">python3.11&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">Timeout&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">300&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">Environment&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">Variables&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">CHANNEL_ACCESS_TOKEN&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>!&lt;span class="l">Ref ChannelAccessToken&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">BUCKET_NAME&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>!&lt;span class="l">Ref BucketName&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">Resources&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># メッセージ処理Lambda&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">ProcessUserMessageFunction&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">Type&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">AWS::Serverless::Function&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">Properties&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">Handler&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">line_clean_bot.process_user_message&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">CodeUri&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">.&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">Policies&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">S3CrudPolicy&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">BucketName&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>!&lt;span class="l">Ref BucketName&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">Events&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">ApiEvent&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">Type&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">HttpApi&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">Properties&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">Path&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">/process_user_message&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">Method&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">ANY&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># 定期通知Lambda&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">PushMessagePeriodicallyFunction&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">Type&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">AWS::Serverless::Function&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">Properties&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">Handler&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">line_clean_bot.push_message_periodically&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">CodeUri&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">.&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">Policies&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">S3CrudPolicy&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">BucketName&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>!&lt;span class="l">Ref BucketName&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">Events&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">ScheduleEvent&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">Type&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Schedule&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">Properties&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">Schedule&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">cron(0 * * * ? *) &lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># 毎時0分&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="デプロイ方法">デプロイ方法&lt;/h2>
&lt;h3 id="1-事前準備">1. 事前準備&lt;/h3>
&lt;ul>
&lt;li>AWS CLIの設定&lt;/li>
&lt;li>SAM CLIのインストール&lt;/li>
&lt;li>LINE Messaging APIのチャンネルアクセストークン取得&lt;/li>
&lt;/ul>
&lt;h3 id="2-ビルド--デプロイ">2. ビルド &amp;amp; デプロイ&lt;/h3>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># ビルド&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sam build
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># デプロイ&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sam deploy --parameter-overrides &lt;span class="nv">ChannelAccessToken&lt;/span>&lt;span class="o">=&lt;/span>your_token
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="3-line-webhook設定">3. LINE Webhook設定&lt;/h3>
&lt;p>デプロイ後に出力されるAPIエンドポイントを、LINE DevelopersコンソールでWebhook URLとして設定します。&lt;/p>
&lt;h2 id="ローカル開発">ローカル開発&lt;/h2>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># ローカルAPIサーバー起動&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sam &lt;span class="nb">local&lt;/span> start-api --env-vars env.json
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># テスト実行&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">curl -X POST http://localhost:3000/process_user_message &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> -H &lt;span class="s2">&amp;#34;Content-Type: application/json&amp;#34;&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> -d @events/line_message.json
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="コスト">コスト&lt;/h2>
&lt;p>月間コストの目安（100ユーザー想定）:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>サービス&lt;/th>
&lt;th>コスト&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Lambda&lt;/td>
&lt;td>ほぼ無料（無料枠内）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>API Gateway&lt;/td>
&lt;td>ほぼ無料（無料枠内）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>S3&lt;/td>
&lt;td>数円/月&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>合計&lt;/strong>&lt;/td>
&lt;td>&lt;strong>数円〜数十円/月&lt;/strong>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="まとめ">まとめ&lt;/h2>
&lt;ul>
&lt;li>サーバーレス構成で運用コストを最小化&lt;/li>
&lt;li>S3 + JSONでシンプルなデータ管理&lt;/li>
&lt;li>SAMでインフラをコード管理&lt;/li>
&lt;li>「日数経過」という掃除に適した期限管理&lt;/li>
&lt;/ul>
&lt;p>ソースコードは&lt;a class="link" href="https://github.com/bossagyu/line-clean-bot" target="_blank" rel="noopener"
>GitHub&lt;/a>で公開しています。&lt;/p>
&lt;h2 id="関連記事">関連記事&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/045-clean-bot/" >掃除リマインダーBotの使い方&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/002-line-messaging-api/" >LINE Messaging APIの登録と使い方&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/008-aws-eventbrdge/" >AWS EventBridgeを用いてLambdaを定期実行する方法&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/014-aws-apigateway-lambda/" >AWS API GatewayとLambdaを連携させる方法&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/015-s3-object-check/" >S3のオブジェクトの存在確認をする方法【Python boto3】&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>掃除を忘れない！掃除リマインダーLINE Bot</title><link>https://bossagyu.com/blog/045-clean-bot/</link><pubDate>Sat, 07 Feb 2026 18:30:00 +0900</pubDate><guid>https://bossagyu.com/blog/045-clean-bot/</guid><description>&lt;h2 id="こんな悩みはありませんか">こんな悩みはありませんか？&lt;/h2>
&lt;ul>
&lt;li>「掃除機、最後にかけたのいつだっけ&amp;hellip;」&lt;/li>
&lt;li>「トイレ掃除、そろそろやらないと」&lt;/li>
&lt;li>「換気扇の掃除、忘れてた！」&lt;/li>
&lt;/ul>
&lt;p>掃除は「やらなきゃ」と思っていても、つい忘れがち。特に週1回、月1回といった定期的な掃除は、気づいたら期限を過ぎていることが多いですよね。&lt;/p>
&lt;h2 id="掃除リマインダーbotを作りました">掃除リマインダーBotを作りました&lt;/h2>
&lt;p>そんな悩みを解決するために、&lt;strong>掃除リマインダーLINE Bot&lt;/strong>を作りました。&lt;/p>
&lt;p>このBotは、掃除タスクごとに「何日ごとにやるか」を設定でき、期限が来たらLINEで通知してくれます。&lt;/p>
&lt;h3 id="主な機能">主な機能&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>タスク登録&lt;/strong>: 「掃除機 7日」「トイレ 3日」など、タスクと間隔を設定&lt;/li>
&lt;li>&lt;strong>完了報告&lt;/strong>: 掃除が終わったらLINEで報告するだけ&lt;/li>
&lt;li>&lt;strong>リマインド通知&lt;/strong>: 設定した曜日・時間に期限切れタスクを通知&lt;/li>
&lt;li>&lt;strong>一時停止&lt;/strong>: 旅行中などはタスクを停止できる&lt;/li>
&lt;/ul>
&lt;h2 id="使い方">使い方&lt;/h2>
&lt;h3 id="1-友だち追加">1. 友だち追加&lt;/h3>
&lt;p>以下のQRコードまたはリンクから友だち追加してください。&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/blog/045-clean-bot/qrcode.png"
width="360"
height="360"
srcset="https://bossagyu.com/blog/045-clean-bot/qrcode_hu95bee7a533128dc6d7ed5659019be1cb_23771_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/045-clean-bot/qrcode_hu95bee7a533128dc6d7ed5659019be1cb_23771_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="QRコード"
class="gallery-image"
data-flex-grow="100"
data-flex-basis="240px"
>&lt;/p>
&lt;p>&lt;a class="link" href="https://lin.ee/kdwsLys" target="_blank" rel="noopener"
>友だち追加はこちら&lt;/a>&lt;/p>
&lt;h3 id="2-タスクを追加する">2. タスクを追加する&lt;/h3>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">追加 掃除機 7
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>これで「7日ごとに掃除機」というタスクが登録されます。&lt;/p>
&lt;h3 id="3-掃除が終わったら報告">3. 掃除が終わったら報告&lt;/h3>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">完了 掃除機
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>タスクの期限がリセットされ、次の通知まで7日間になります。&lt;/p>
&lt;h3 id="4-残りのタスクを確認">4. 残りのタスクを確認&lt;/h3>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">残り
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>期限切れのタスクが一覧で表示されます。何日超過しているかも分かります。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">タスクは以下の通りです。
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">掃除機 (+3日)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">トイレ (+1日)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="5-通知設定">5. 通知設定&lt;/h3>
&lt;p>好きな曜日と時間に通知を受け取れます。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">通知設定 月水金 7
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>これで月・水・金の7時に通知が届きます。&lt;/p>
&lt;h2 id="コマンド一覧">コマンド一覧&lt;/h2>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>コマンド&lt;/th>
&lt;th>説明&lt;/th>
&lt;th>例&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>追加 [タスク名] [日数]&lt;/td>
&lt;td>タスクを追加&lt;/td>
&lt;td>追加 掃除機 7&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>完了 [タスク名]&lt;/td>
&lt;td>タスクを完了&lt;/td>
&lt;td>完了 掃除機&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>削除 [タスク名]&lt;/td>
&lt;td>タスクを削除&lt;/td>
&lt;td>削除 掃除機&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>停止 [タスク名]&lt;/td>
&lt;td>一時停止&lt;/td>
&lt;td>停止 掃除機&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>再開 [タスク名]&lt;/td>
&lt;td>再開&lt;/td>
&lt;td>再開 掃除機&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>残り&lt;/td>
&lt;td>期限切れタスク表示&lt;/td>
&lt;td>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>確認&lt;/td>
&lt;td>全タスク詳細表示&lt;/td>
&lt;td>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>通知設定 [曜日] [時間]&lt;/td>
&lt;td>通知設定&lt;/td>
&lt;td>通知設定 月水金 7&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>使い方&lt;/td>
&lt;td>ヘルプ表示&lt;/td>
&lt;td>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="なぜ日数経過で管理するのか">なぜ「日数経過」で管理するのか&lt;/h2>
&lt;p>一般的なTODOアプリは「期日」を設定しますが、掃除は「前回からどれくらい経ったか」で判断することが多いです。&lt;/p>
&lt;ul>
&lt;li>掃除機は「1週間に1回くらい」&lt;/li>
&lt;li>トイレは「3日に1回」&lt;/li>
&lt;li>換気扇は「月に1回」&lt;/li>
&lt;/ul>
&lt;p>このBotは、完了報告をするたびに自動で次の期限が設定されるので、毎回日付を入力する手間がありません。&lt;/p>
&lt;h2 id="無料で使えます">無料で使えます&lt;/h2>
&lt;p>このBotは完全無料で利用できます。ぜひお試しください！&lt;/p>
&lt;p>&lt;a class="link" href="https://lin.ee/kdwsLys" target="_blank" rel="noopener"
>友だち追加はこちら&lt;/a>&lt;/p>
&lt;h2 id="関連記事">関連記事&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/047-clean-bot-tutorial/" >【初心者向け】掃除リマインダーBotの使い方チュートリアル&lt;/a>（初心者向け使い方ガイド）&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/002-line-messaging-api/" >LINE Messaging APIの登録と使い方&lt;/a>（LINE Bot開発の基礎）&lt;/li>
&lt;/ul></description></item><item><title>OpenAI Response APIの使い方まとめ</title><link>https://bossagyu.com/blog/044-openai-response-api/</link><pubDate>Wed, 24 Dec 2025 10:00:00 +0900</pubDate><guid>https://bossagyu.com/blog/044-openai-response-api/</guid><description>&lt;h2 id="概要">概要&lt;/h2>
&lt;p>OpenAIのResponse APIを使ってテキスト生成を行う手順をまとめました。
公式ドキュメントの場所、APIキーの取得方法、Pythonからの最小サンプルまでを確認できます。&lt;/p>
&lt;h2 id="response-apiとは">Response APIとは&lt;/h2>
&lt;p>Response APIは、テキスト生成やツール呼び出しなどを統一的に扱えるOpenAIのAPIです。
単発の問い合わせから、会話形式のやり取りまで幅広く利用できます。&lt;/p>
&lt;h2 id="公式ドキュメント">公式ドキュメント&lt;/h2>
&lt;p>最新の仕様は公式ドキュメントを参照するのが確実です。&lt;/p>
&lt;ul>
&lt;li>Response API リファレンス: &lt;a class="link" href="https://platform.openai.com/docs/api-reference/responses" target="_blank" rel="noopener"
>https://platform.openai.com/docs/api-reference/responses&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="apiキーの取得方法">APIキーの取得方法&lt;/h2>
&lt;p>Response APIを利用するにはOpenAIのAPIキーが必要です。
以下の手順で取得できます。&lt;/p>
&lt;ol>
&lt;li>OpenAIのダッシュボードにサインインする&lt;/li>
&lt;li>API Keysページ（&lt;a class="link" href="https://platform.openai.com/api-keys" target="_blank" rel="noopener"
>https://platform.openai.com/api-keys&lt;/a>）を開く&lt;/li>
&lt;li>「Create new secret key」から新しいキーを発行する&lt;/li>
&lt;/ol>
&lt;p>取得したキーは環境変数に設定しておくと安全です。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">OPENAI_API_KEY&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;&amp;lt;YOUR_API_KEY&amp;gt;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="pythonのサンプルコード">Pythonのサンプルコード&lt;/h2>
&lt;p>Python SDKを使う場合は、まずuvでパッケージをインストールします。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">uv pip install openai
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>以下はResponse APIでテキストを生成する最小サンプルです。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">openai&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">OpenAI&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">client&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">OpenAI&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">client&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">responses&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">create&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;gpt-4.1-mini&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">input&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;Response APIの特徴を3行で教えてください。&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">response&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">output_text&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="実行イメージ">実行イメージ&lt;/h3>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">Response APIは単発の生成にも対話にも使える統一APIです。
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">シンプルなリクエストでモデル出力を取得できます。
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">公式ドキュメントを確認しながら用途に合わせて拡張できます。
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="まとめ">まとめ&lt;/h2>
&lt;p>Response APIはシンプルな入力でテキスト生成を行えるOpenAIのAPIです。
公式ドキュメントを確認し、APIキーを準備してからPythonで試すとスムーズに導入できます。&lt;/p>
&lt;h2 id="関連記事">関連記事&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/027-chatgpt-4o/" >ChatGPT 4oの紹介&lt;/a>（ChatGPTの最新機能紹介）&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/048-lawve-backend/" >GCSとGemini File Search APIを自動同期するイベント駆動バックエンドの設計&lt;/a>（API統合パターンの実践例）&lt;/li>
&lt;/ul></description></item><item><title>Codex CLIの使い方まとめ</title><link>https://bossagyu.com/blog/043-codex/</link><pubDate>Fri, 12 Dec 2025 10:00:00 +0900</pubDate><guid>https://bossagyu.com/blog/043-codex/</guid><description>&lt;h2 id="概要">概要&lt;/h2>
&lt;p>Codex CLIを使ってローカル環境からコマンドライン経由でCodexにアクセスする手順をまとめました。
インストールからサインイン、代表的なコマンドの使い方まで一通り確認できます。&lt;/p>
&lt;h2 id="codex-cliのインストール方法">Codex CLIのインストール方法&lt;/h2>
&lt;p>Node.jsが入っている環境ならnpm経由で簡単に導入できます。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">npm install -g codex-cli
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Homebrewを利用している場合は以下でもOKです。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">brew tap codexcli/tap
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">brew install codex
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>インストール後、バージョンが表示されれば準備完了です。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">codex --version
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="サインインの方法">サインインの方法&lt;/h2>
&lt;p>Codex CLIは初回実行時に認証が必要です。用途に応じてAPIキーかChatGPTアカウントでサインインできます。&lt;/p>
&lt;h3 id="apiキーでサインインする">APIキーでサインインする&lt;/h3>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">codex login --token &amp;lt;YOUR_API_KEY&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>環境変数に設定しておくと毎回入力する手間を省けます。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">CODEX_API_KEY&lt;/span>&lt;span class="o">=&lt;/span>&amp;lt;YOUR_API_KEY&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="chatgptのアカウントでサインインする">ChatGPTのアカウントでサインインする&lt;/h3>
&lt;p>ブラウザでの認証フローを利用したい場合は、トークン指定を省いて &lt;code>codex login&lt;/code> を実行します。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">codex login
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>上記を実行するとブラウザが開き、ChatGPTのサインイン画面が表示されます。メールアドレスやGoogle/Appleアカウントでサインインし、許可を与えるとCLIに戻ってトークンが保存されます。&lt;/p>
&lt;p>サインイン後は共通で以下を実行し、アカウント情報が表示されれば認証成功です。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">codex whoami
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="codexの実行方法とサンプル">Codexの実行方法とサンプル&lt;/h2>
&lt;h3 id="単発でコード生成を行う">単発でコード生成を行う&lt;/h3>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">codex run &lt;span class="s2">&amp;#34;PythonでFizzBuzzを書いて&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="ファイルをコンテキストに含めて要約する">ファイルをコンテキストに含めて要約する&lt;/h3>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">codex run &lt;span class="s2">&amp;#34;このファイルを要約して&amp;#34;&lt;/span> --file README.md
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="対話モードで試す">対話モードで試す&lt;/h3>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">codex chat
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>チャットモードでは、プロンプトを入力すると逐次回答が返ってきます。&lt;code>Ctrl + D&lt;/code> で終了できます。&lt;/p>
&lt;h4 id="チャット内のスラッシュコマンド">チャット内のスラッシュコマンド&lt;/h4>
&lt;p>対話モードでは先頭に &lt;code>/&lt;/code> を付けて補助操作を行えます。&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>コマンド&lt;/th>
&lt;th>役割&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>/help&lt;/code>&lt;/td>
&lt;td>利用可能なコマンド一覧を表示する&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>/new&lt;/code>&lt;/td>
&lt;td>会話履歴をリセットして新しいスレッドを開始する&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>/file &amp;lt;path&amp;gt;&lt;/code>&lt;/td>
&lt;td>指定したファイルをコンテキストに添付する&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>/exit&lt;/code>&lt;/td>
&lt;td>チャットを終了する&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="コマンドの使い方">コマンドの使い方&lt;/h2>
&lt;p>主なコマンドは以下の通りです。&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>コマンド&lt;/th>
&lt;th>説明&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>codex login --token &amp;lt;API_KEY&amp;gt;&lt;/code>&lt;/td>
&lt;td>APIキーでサインインする&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>codex login&lt;/code>&lt;/td>
&lt;td>ChatGPTのアカウントでブラウザ認証を開始する&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>codex whoami&lt;/code>&lt;/td>
&lt;td>現在サインインしているアカウントを確認する&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>codex run &amp;quot;&amp;lt;prompt&amp;gt;&amp;quot;&lt;/code>&lt;/td>
&lt;td>単発のプロンプトを投げて結果を取得する&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>codex run &amp;quot;&amp;lt;prompt&amp;gt;&amp;quot; --file &amp;lt;path&amp;gt;&lt;/code>&lt;/td>
&lt;td>ファイルをコンテキストに含めて実行する&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>codex chat&lt;/code>&lt;/td>
&lt;td>対話モードで利用する&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>codex history&lt;/code>&lt;/td>
&lt;td>直近の実行履歴を確認する&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="まとめ">まとめ&lt;/h2>
&lt;p>Codex CLIはインストール後にサインインを済ませるだけで、コマンドラインからすぐにCodexを活用できます。
よく使うコマンドを覚えておくと、スクリプト生成やファイル要約が素早く行えるようになります。&lt;/p>
&lt;h2 id="関連記事">関連記事&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/017-vscode-copilot/" >VSCodeでGithub Copilotを使いこなす完全ガイド&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/042-github-copilot/" >Github CopilotをChat toolを使って便利に使う方法&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/044-openai-response-api/" >OpenAI Response APIの使い方まとめ&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>RedisをGoで使う方法</title><link>https://bossagyu.com/blog/041-redis-go/</link><pubDate>Sun, 05 Oct 2025 20:25:02 +0900</pubDate><guid>https://bossagyu.com/blog/041-redis-go/</guid><description>&lt;h2 id="概要">概要&lt;/h2>
&lt;p>この記事では、GoでRedisを使う方法について説明します。&lt;/p>
&lt;h2 id="redisの起動方法">Redisの起動方法&lt;/h2>
&lt;p>Redisの説明やDockerでの起動方法については、&lt;a class="link" href="https://bossagyu.com/blog/040-redis-local/" target="_blank" rel="noopener"
>こちらの記事&lt;/a>を参照してください。&lt;/p>
&lt;p>Redisを起動する。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker run -d --name my-redis &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> -p 6379:6379 &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> redis:7.4.5-alpine
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>redis-cliで接続確認。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">docker exec -it my-redis redis-cli ping
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>PONGと返ってくればOK。&lt;/p>
&lt;h2 id="goのプロジェクトの作成">Goのプロジェクトの作成&lt;/h2>
&lt;p>Goのプロジェクトを作成する。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">mkdir redis-go
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd&lt;/span> redis-go
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">go mod init redis-go
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="goでredisを使う">GoでRedisを使う&lt;/h2>
&lt;p>Redisに接続して、書き込み・読み込みを行うサンプルコード。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-go" data-lang="go">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">package&lt;/span> &lt;span class="nx">main&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">&amp;#34;context&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">&amp;#34;fmt&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">&amp;#34;log&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">&amp;#34;time&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">&amp;#34;github.com/redis/go-redis/v9&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">func&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// パスワードなし、ローカルの6379に接続
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">rdb&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">redis&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">NewClient&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="nx">redis&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Options&lt;/span>&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">Addr&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s">&amp;#34;127.0.0.1:6379&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">Password&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1">// なし
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">DB&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">DialTimeout&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">3&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="nx">time&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Second&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">ReadTimeout&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">1&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="nx">time&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Second&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">WriteTimeout&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">1&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="nx">time&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Second&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">defer&lt;/span> &lt;span class="nx">rdb&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Close&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">ctx&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">cancel&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">context&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">WithTimeout&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">context&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Background&lt;/span>&lt;span class="p">(),&lt;/span> &lt;span class="mi">5&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="nx">time&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Second&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">defer&lt;/span> &lt;span class="nf">cancel&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 接続確認
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">rdb&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Ping&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">ctx&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nf">Err&lt;/span>&lt;span class="p">();&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="kc">nil&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">log&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Fatalf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;redis ping failed: %v&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">err&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">log&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Println&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;connected to redis ✅&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 基本操作（SET/GET）
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">rdb&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Set&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">ctx&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;hello&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;world&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="nx">time&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Minute&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nf">Err&lt;/span>&lt;span class="p">();&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="kc">nil&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">log&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Fatalf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;SET error: %v&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">err&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">val&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">rdb&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">ctx&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;hello&amp;#34;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nf">Result&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="kc">nil&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">log&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Fatalf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;GET error: %v&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">err&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">fmt&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Println&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;hello =&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">val&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Set型の例
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">key&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="s">&amp;#34;team:dev&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">rdb&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">SAdd&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">ctx&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">key&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;alice&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;bob&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;alice&amp;#34;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nf">Err&lt;/span>&lt;span class="p">();&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="kc">nil&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">log&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Fatalf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;SADD error: %v&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">err&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">members&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">_&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">rdb&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">SMembers&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">ctx&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">key&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nf">Result&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">fmt&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Println&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;members =&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">members&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>パッケージのインストールを行う。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">go mod tidy
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>実行する。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">go run main.go
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>以下のように表示されればOK。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">hello = world
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">members = [alice bob]
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="まとめ">まとめ&lt;/h2>
&lt;p>GoでRedisを使う方法について説明しました。
GoでRedisを使う場合は、&lt;a class="link" href="https://github.com/redis/go-redis" target="_blank" rel="noopener"
>go-redis&lt;/a>を使用します。&lt;/p>
&lt;h2 id="関連記事">関連記事&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/040-redis-local/" >RedisをDockerで起動する方法&lt;/a>（Redisの起動方法）&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/030-go-environment-construction/" >macでGoの開発環境を構築して最速でHello Worldする&lt;/a>（Go環境構築）&lt;/li>
&lt;/ul></description></item><item><title>RedisをDockerで起動する方法</title><link>https://bossagyu.com/blog/040-redis-local/</link><pubDate>Fri, 03 Oct 2025 07:29:31 +0900</pubDate><guid>https://bossagyu.com/blog/040-redis-local/</guid><description>&lt;h2 id="概要">概要&lt;/h2>
&lt;p>この記事では、RedisをDockerで起動する方法について説明します。&lt;/p>
&lt;h2 id="redisとは">Redisとは&lt;/h2>
&lt;p>Redisは、オープンソースのインメモリデータ構造ストアであり、データベース、キャッシュ、メッセージブローカーとして使用されます。高速な読み書き性能を持ち、様々なデータ構造（文字列、リスト、セット、ハッシュなど）をサポートしています。&lt;/p>
&lt;h2 id="dockerでのredis起動">DockerでのRedis起動&lt;/h2>
&lt;h3 id="1-dockerのインストール">1. Dockerのインストール&lt;/h3>
&lt;p>まず、Dockerがインストールされていることを確認します。インストールされ
ていない場合は、&lt;a class="link" href="https://www.docker.com/get-started" target="_blank" rel="noopener"
>Dockerの公式サイト&lt;/a>からインストールしてください。&lt;/p>
&lt;h3 id="2-redisコンテナの起動">2. Redisコンテナの起動&lt;/h3>
&lt;p>以下のコマンドを実行して、Redisコンテナを起動します。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker run --name redis -d -p 6379:6379 redis:7.4.5-alpine
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>コンテナにログインする&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker &lt;span class="nb">exec&lt;/span> -it redis sh
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="3-redisの動作確認">3. Redisの動作確認&lt;/h3>
&lt;p>Redisコンテナ内で以下のコマンドを実行して、Redisクライアントを起動します。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">redis-cli
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>データの設定と取得を試してみます。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">set&lt;/span> name &lt;span class="s2">&amp;#34;hoge&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">get name
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;hoge&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>これで、Redisが正常に動作していることが確認できました。&lt;/p>
&lt;h3 id="4-redisコンテナの停止と削除">4. Redisコンテナの停止と削除&lt;/h3>
&lt;p>Redisコンテナを停止するには、以下のコマンドを実行します。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker stop redis
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Redisコンテナを削除するには、以下のコマンドを実行します。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker rm redis
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="まとめ">まとめ&lt;/h2>
&lt;p>この記事では、Dockerを使用してRedisを起動する方法について説明しました。
Redisは高速なデータベースとして広く利用されており、Dockerを使用することで簡単にセットアップできます。
ぜひ、Redisを活用してみてください。&lt;/p>
&lt;h2 id="関連記事">関連記事&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/041-redis-go/" >RedisをGoで使う方法&lt;/a>（GoでRedisを操作する実践例）&lt;/li>
&lt;/ul></description></item><item><title>Visual Studio CodeでAWS Toolkitを使う方法</title><link>https://bossagyu.com/blog/039-aws-toolkit-vscode/</link><pubDate>Mon, 11 Aug 2025 20:33:30 +0900</pubDate><guid>https://bossagyu.com/blog/039-aws-toolkit-vscode/</guid><description>&lt;h2 id="概要">概要&lt;/h2>
&lt;p>この記事では、Visual Studio Code (VSCode) で AWS Toolkit を使用する方法について説明します。&lt;/p>
&lt;h2 id="aws-toolkitのインストール">AWS Toolkitのインストール&lt;/h2>
&lt;ol>
&lt;li>VSCodeを起動します。&lt;/li>
&lt;li>左側のサイドバーから拡張機能アイコンをクリックします。&lt;/li>
&lt;li>検索バーに「AWS Toolkit」と入力し、表示されたリストから「AWS Toolkit for Visual Studio Code」を選択します。&lt;/li>
&lt;li>「インストール」ボタンをクリックして、インストールを開始します。&lt;/li>
&lt;/ol>
&lt;p>&lt;img src="https://bossagyu.com/blog/039-aws-toolkit-vscode/image.png"
width="1192"
height="265"
srcset="https://bossagyu.com/blog/039-aws-toolkit-vscode/image_hufa1fd2602d4a5c45942f480dd2c9a493_78579_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/039-aws-toolkit-vscode/image_hufa1fd2602d4a5c45942f480dd2c9a493_78579_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="AWS Toolkit Installation"
class="gallery-image"
data-flex-grow="449"
data-flex-basis="1079px"
>&lt;/p>
&lt;h2 id="asia-pacific-tokyoリージョンを設定する方法">Asia Pacific Tokyoリージョンを設定する方法&lt;/h2>
&lt;p>EXPLORERからリージョンをして利用するのですが、デフォルトではus-east-1が選択されているため、Asia Pacific Tokyoリージョンを設定する必要があります。
少しわかりにくいので、以下の手順で設定します。&lt;/p>
&lt;ol>
&lt;li>左側のサイドバーから「AWS Explorer」から、ハンバーガーメニューをクリックします。&lt;/li>
&lt;/ol>
&lt;p>&lt;img src="https://bossagyu.com/blog/039-aws-toolkit-vscode/image-1.png"
width="658"
height="259"
srcset="https://bossagyu.com/blog/039-aws-toolkit-vscode/image-1_hu4dbc19d229257ea92a49e677c002202d_66951_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/039-aws-toolkit-vscode/image-1_hu4dbc19d229257ea92a49e677c002202d_66951_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="AWS Explorer"
class="gallery-image"
data-flex-grow="254"
data-flex-basis="609px"
>&lt;/p>
&lt;ol start="2">
&lt;li>&lt;code>Show or Hide Regions&lt;/code>をクリックします。&lt;/li>
&lt;li>表示されたリストから「Asia Pacific (Tokyo) ap-northeast-1」を選択します。&lt;/li>
&lt;/ol>
&lt;p>&lt;img src="https://bossagyu.com/blog/039-aws-toolkit-vscode/image-2.png"
width="600"
height="211"
srcset="https://bossagyu.com/blog/039-aws-toolkit-vscode/image-2_hu8226c6e3ff7c2fce4d95162999eea97c_27977_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/039-aws-toolkit-vscode/image-2_hu8226c6e3ff7c2fce4d95162999eea97c_27977_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="AWS Explorer"
class="gallery-image"
data-flex-grow="284"
data-flex-basis="682px"
>&lt;/p>
&lt;p>これで、AWS ToolkitがAsia Pacific Tokyoリージョンで使用できるようになります。&lt;/p></description></item><item><title>Visual Studio Codeのアップデート方法</title><link>https://bossagyu.com/blog/038-update-vscode/</link><pubDate>Sun, 27 Jul 2025 18:12:16 +0900</pubDate><guid>https://bossagyu.com/blog/038-update-vscode/</guid><description>&lt;h2 id="概要">概要&lt;/h2>
&lt;p>MacでVisual Studio Code（VSCode）のアップデート方法を説明します。
GUIからの手動アップデートに加え、コマンドラインでの更新方法や自動アップデートの設定についても解説します。&lt;/p>
&lt;h2 id="guiからアップデートする方法">GUIからアップデートする方法&lt;/h2>
&lt;ol>
&lt;li>VSCodeを起動します。&lt;/li>
&lt;li>Code → 「更新の確認」を選択します。
英語の場合は Code → &amp;ldquo;Check for Updates&amp;rdquo; を選択します。&lt;/li>
&lt;/ol>
&lt;p>&lt;img src="https://bossagyu.com/blog/038-update-vscode/image.png"
width="362"
height="266"
srcset="https://bossagyu.com/blog/038-update-vscode/image_hu4cb88fc77c0541cdf7c5ee55b378b842_31419_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/038-update-vscode/image_hu4cb88fc77c0541cdf7c5ee55b378b842_31419_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="更新の確認"
class="gallery-image"
data-flex-grow="136"
data-flex-basis="326px"
>&lt;/p>
&lt;p>更新が始まると以下のような画面になります。&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/blog/038-update-vscode/image-1.png"
width="322"
height="260"
srcset="https://bossagyu.com/blog/038-update-vscode/image-1_hu467c1c2e48f82f20717d31d719e9ad63_31541_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/038-update-vscode/image-1_hu467c1c2e48f82f20717d31d719e9ad63_31541_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="アップデートのダウンロード中"
class="gallery-image"
data-flex-grow="123"
data-flex-basis="297px"
>&lt;/p>
&lt;p>ダウンロードが完了すると「再起動して更新」が表示されます。&lt;/p>
&lt;p>再起動後「Visual Studio Codeのバージョン情報」をクリックして、バージョンが上がっていることを確認します。&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/blog/038-update-vscode/image-3.png"
width="243"
height="338"
srcset="https://bossagyu.com/blog/038-update-vscode/image-3_hud4f1281fbbaa937e53ab24c19c8a91ab_31593_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/038-update-vscode/image-3_hud4f1281fbbaa937e53ab24c19c8a91ab_31593_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="バージョン確認画面"
class="gallery-image"
data-flex-grow="71"
data-flex-basis="172px"
>&lt;/p>
&lt;h2 id="コマンドラインからアップデートする方法">コマンドラインからアップデートする方法&lt;/h2>
&lt;p>Homebrewを使ってVSCodeをインストールしている場合は、ターミナルからアップデートできます。&lt;/p>
&lt;h3 id="現在のバージョンを確認">現在のバージョンを確認&lt;/h3>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">code --version
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="homebrewでアップデート">Homebrewでアップデート&lt;/h3>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">brew upgrade --cask visual-studio-code
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Homebrewでインストールしていない場合は、既存のVSCodeをアプリケーションフォルダから削除した上で、以下のコマンドでHomebrewによる管理に切り替えられます。
設定やインストール済みの拡張機能はアカウント同期で復元できるため、削除しても問題ありません。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">brew install --cask visual-studio-code
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>以降は &lt;code>brew upgrade --cask visual-studio-code&lt;/code> でアップデートが可能になります。&lt;/p>
&lt;h2 id="自動アップデートの設定">自動アップデートの設定&lt;/h2>
&lt;p>VSCodeにはアップデートの挙動を制御する &lt;code>update.mode&lt;/code> 設定があります。
設定画面を開き（&lt;code>Cmd + ,&lt;/code>）、&lt;code>update mode&lt;/code> で検索すると変更できます。&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>設定値&lt;/th>
&lt;th>挙動&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>default&lt;/code>&lt;/td>
&lt;td>バックグラウンドで自動的にアップデートをダウンロード・インストールします&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>manual&lt;/code>&lt;/td>
&lt;td>アップデートの確認は行いますが、インストールは手動で行います&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>start&lt;/code>&lt;/td>
&lt;td>VSCodeの起動時のみアップデートを確認します&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>none&lt;/code>&lt;/td>
&lt;td>自動アップデートを完全に無効化します&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>settings.jsonで直接設定する場合は以下のように記述します。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;update.mode&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;default&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>特別な理由がない限り &lt;code>default&lt;/code> のままで問題ありません。&lt;/p>
&lt;h2 id="トラブルシューティング">トラブルシューティング&lt;/h2>
&lt;h3 id="アップデートが更新の確認に表示されない">アップデートが「更新の確認」に表示されない&lt;/h3>
&lt;p>自動アップデートが無効になっている可能性があります。
設定の &lt;code>update.mode&lt;/code> が &lt;code>none&lt;/code> になっていないか確認してください。&lt;/p>
&lt;h3 id="アップデートのダウンロードが失敗する">アップデートのダウンロードが失敗する&lt;/h3>
&lt;p>プロキシやファイアウォールの設定が原因の場合があります。
以下の設定を確認してください。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;http.proxy&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;http://proxy.example.com:8080&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;http.proxyStrictSSL&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">false&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="アップデート後に拡張機能が動作しなくなった">アップデート後に拡張機能が動作しなくなった&lt;/h3>
&lt;p>拡張機能の互換性の問題が考えられます。以下の手順を試してください。&lt;/p>
&lt;ol>
&lt;li>コマンドパレット（&lt;code>Cmd + Shift + P&lt;/code>）を開く&lt;/li>
&lt;li>「Extensions: Disable All Installed Extensions」を実行&lt;/li>
&lt;li>VSCodeを再起動&lt;/li>
&lt;li>拡張機能を1つずつ有効化して問題のある拡張機能を特定する&lt;/li>
&lt;/ol>
&lt;h3 id="それでも解決しない場合">それでも解決しない場合&lt;/h3>
&lt;p>VSCodeを再インストールすることで解決する場合があります。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Homebrewの場合&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">brew reinstall --cask visual-studio-code
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>設定やインストール済みの拡張機能はGitHubアカウントやMicrosoftアカウントで同期しておくと、再インストール後に自動で復元されます。&lt;/p>
&lt;h2 id="まとめ">まとめ&lt;/h2>
&lt;p>VSCodeのアップデート方法について解説しました。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>GUI&lt;/strong>: Code → 「更新の確認」から数クリックで完了&lt;/li>
&lt;li>&lt;strong>コマンドライン&lt;/strong>: &lt;code>brew upgrade --cask visual-studio-code&lt;/code> で一括更新&lt;/li>
&lt;li>&lt;strong>自動アップデート&lt;/strong>: &lt;code>update.mode&lt;/code> 設定で挙動を制御可能&lt;/li>
&lt;/ul>
&lt;p>定期的にアップデートを行うことで、新機能やセキュリティパッチを利用できます。
アップデート後は、必ずバージョン情報を確認して、正しくアップデートが行われたかをチェックしましょう。&lt;/p></description></item><item><title>vscodeのインデントガイドをカスタマイズする方法</title><link>https://bossagyu.com/blog/036-vscode-indent/</link><pubDate>Tue, 22 Jul 2025 21:01:13 +0900</pubDate><guid>https://bossagyu.com/blog/036-vscode-indent/</guid><description>&lt;h2 id="概要">概要&lt;/h2>
&lt;p>Visual Studio Code (VSCode) では、インデントガイドの色をカスタマイズすることができます。
この記事では、インデントガイドの色を変更する方法を説明します。
indent-rainbowのような拡張機能はインデントガイドが途中で切れるなどの問題がありましたが、VSCodeの設定ファイルを編集するだけで実現できます。&lt;/p>
&lt;h2 id="インデントガイドの色を変更する方法">インデントガイドの色を変更する方法&lt;/h2>
&lt;h3 id="設定ファイルを開く">設定ファイルを開く&lt;/h3>
&lt;p>VSCodeの設定ファイルを開きます。
&lt;code>Command Palette&lt;/code> (Ctrl + Shift + P) を開き、&lt;code>Preferences: Open Settings (JSON)&lt;/code> を選択します。&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/blog/036-vscode-indent/image.png"
width="608"
height="122"
srcset="https://bossagyu.com/blog/036-vscode-indent/image_hu3c9579035f233532becca6f8faa5c647_25704_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/036-vscode-indent/image_hu3c9579035f233532becca6f8faa5c647_25704_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="設定を開く方法"
class="gallery-image"
data-flex-grow="498"
data-flex-basis="1196px"
>&lt;/p>
&lt;h3 id="設定を追加する">設定を追加する&lt;/h3>
&lt;p>以下を参考に設定を追加します。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;workbench.colorCustomizations&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;editorIndentGuide.background1&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;#006400&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;editorIndentGuide.background2&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;#008000&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;editorIndentGuide.background3&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;#00a000&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;editorIndentGuide.background4&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;#006400&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;editorIndentGuide.background5&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;#008000&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;editorIndentGuide.background6&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;#00a000&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;editorIndentGuide.activeBackground1&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;#00ff00&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;editorIndentGuide.activeBackground2&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;#00ff00&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;editorIndentGuide.activeBackground3&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;#00ff00&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;editorIndentGuide.activeBackground4&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;#00ff00&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;editorIndentGuide.activeBackground5&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;#00ff00&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;editorIndentGuide.activeBackground6&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;#00ff00&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;editorWhitespace.foreground&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;#393A3D&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>設定内容&lt;/th>
&lt;th>説明&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>editorIndentGuide.background&amp;lt;number&amp;gt;&lt;/code>&lt;/td>
&lt;td>インデントガイドの背景色を指定します。&lt;br>&lt;code>&amp;lt;number&amp;gt;&lt;/code> は インデントの深さに対応します。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>editorIndentGuide.activeBackground&amp;lt;number&amp;gt;&lt;/code>&lt;/td>
&lt;td>アクティブなインデントガイドの背景色を指定します。&lt;br>&lt;code>&amp;lt;number&amp;gt;&lt;/code> はインデントの深さに対応します。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>editorWhitespace.foreground&lt;/code>&lt;/td>
&lt;td>空白文字の色を指定します。&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>実際に設定を追加した結果以下の画像のようにインデントガイドの色が変更されます。
&lt;img src="https://bossagyu.com/blog/036-vscode-indent/image-1.png"
width="479"
height="328"
srcset="https://bossagyu.com/blog/036-vscode-indent/image-1_hu6f9c993cad77886be734ef5704270561_75507_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/036-vscode-indent/image-1_hu6f9c993cad77886be734ef5704270561_75507_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="インデントの様子"
class="gallery-image"
data-flex-grow="146"
data-flex-basis="350px"
>&lt;/p>
&lt;h2 id="まとめ">まとめ&lt;/h2>
&lt;p>VSCodeでは、インデントガイドの色を簡単にカスタマイズできます。
設定ファイルに必要な項目を追加するだけで、好みの色に変更できます。
これにより、コードの可読性が向上し、インデントの深さを視覚的に把握しやすくなります。
ぜひ試してみてください。&lt;/p></description></item><item><title>JetBrains製品のメジャーバージョンを上げる方法</title><link>https://bossagyu.com/blog/034-jetbrains-update/</link><pubDate>Thu, 12 Jun 2025 20:46:53 +0900</pubDate><guid>https://bossagyu.com/blog/034-jetbrains-update/</guid><description>&lt;h2 id="概要">概要&lt;/h2>
&lt;p>IntelliJやPyCharmなどの JetBrains製品のメジャーバージョンを上げる際にサイドダウンロードするが必要になります。
設定を引き継ぎつつなるべく簡単にアップデートする方法について説明します。&lt;/p>
&lt;h2 id="jetbrains-toolbox-appのインストール">JetBrains Toolbox Appのインストール&lt;/h2>
&lt;p>JetBrains製品のメジャーバージョンを上げる際は、&lt;a class="link" href="https://www.jetbrains.com/toolbox-app/" target="_blank" rel="noopener"
>JetBrains Toolbox App&lt;/a>を利用することをおすすめします。&lt;/p>
&lt;p>上記リンクよりJetBrains Toolbox Appをダウンロードし、インストールします。&lt;/p>
&lt;h2 id="アップデート">アップデート&lt;/h2>
&lt;p>JetBrains Toolbox Appを起動し、アップデートしたい製品を選択します。&lt;/p>
&lt;p>今回はPyCharmをの更新を行うのでクリックすると、アップデートが開始されます。&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/blog/034-jetbrains-update/img.png"
width="443"
height="691"
srcset="https://bossagyu.com/blog/034-jetbrains-update/img_hu942b99710855521c5f32d9b20e79451d_198115_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/034-jetbrains-update/img_hu942b99710855521c5f32d9b20e79451d_198115_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="アップデートの選択画面"
class="gallery-image"
data-flex-grow="64"
data-flex-basis="153px"
>&lt;/p>
&lt;p>アップデートが完了すると、以下のように新しいバージョンがインストールされます。&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/blog/034-jetbrains-update/img_1.png"
width="423"
height="509"
srcset="https://bossagyu.com/blog/034-jetbrains-update/img_1_hu8eb35234db364754b9c1db3a37ad674a_94027_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/034-jetbrains-update/img_1_hu8eb35234db364754b9c1db3a37ad674a_94027_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="アップデート後の画面"
class="gallery-image"
data-flex-grow="83"
data-flex-basis="199px"
>&lt;/p>
&lt;h2 id="まとめ">まとめ&lt;/h2>
&lt;p>JetBrains製品のメジャーバージョンを上げる際は、JetBrains Toolbox Appを利用することで簡単にアップデートが可能です。&lt;/p></description></item><item><title>FeastのチュートリアルをMacで実行する</title><link>https://bossagyu.com/blog/033-feast-tutorial/</link><pubDate>Mon, 13 Jan 2025 11:49:53 +0900</pubDate><guid>https://bossagyu.com/blog/033-feast-tutorial/</guid><description>&lt;h2 id="概要">概要&lt;/h2>
&lt;p>今回は&lt;a class="link" href="https://github.com/feast-dev/feast" target="_blank" rel="noopener"
>Feastのチュートリアル&lt;/a>を参考に、Macで実行してみます。&lt;/p>
&lt;h2 id="事前準備">事前準備&lt;/h2>
&lt;p>&lt;a class="link" href="https://bossagyu.com/blog/032-python-uv/" target="_blank" rel="noopener"
>MacでUVを用いてPythonの開発環境を構築する&lt;/a>を参考にUVを用いて開発できる環境を整えてください。&lt;/p>
&lt;p>uvで構築した環境にfeastをインストールする。&lt;/p>
&lt;h2 id="feastのインストールからuiの起動まで">FeastのインストールからUIの起動まで&lt;/h2>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&amp;gt; uv pip install &lt;span class="nv">feast&lt;/span>&lt;span class="o">==&lt;/span>0.40.1
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>2025/01/13現在、Feastにバグがあり最新版をインストールするとUIが起動しないので注意してください。(&lt;a class="link" href="https://github.com/feast-dev/feast/issues/4743" target="_blank" rel="noopener"
>issue&lt;/a>)&lt;/p>
&lt;p>Feature Repositoryを作成する。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&amp;gt; feast init my_feature_repo
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Creating a new Feast repository in /Users/kouhei/Program/ML/feast/my_feature_repo.
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>以下のようなリポジトリが作成される。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&amp;gt; tree
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">└── my_feature_repo
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├── README.md
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├── __init__.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> └── feature_repo
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├── __init__.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├── data
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> │ └── driver_stats.parquet
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├── example_repo.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├── feature_store.yaml
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> └── test_workflow.py
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Feastのチュートリアルの設定を反映する。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd&lt;/span> my_feature_repo/feature_repo
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">feast apply
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Feastのuiを起動する。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&amp;gt; feast ui
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>http://0.0.0.0:8888/p/my_feature_repo へアクセスするとUIが表示されます。&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/blog/033-feast-tutorial/img-033-001.png"
width="1478"
height="581"
srcset="https://bossagyu.com/blog/033-feast-tutorial/img-033-001_hub81249ce2a094e96f7c08a17dccf8b99_125412_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/033-feast-tutorial/img-033-001_hub81249ce2a094e96f7c08a17dccf8b99_125412_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Feast ui"
class="gallery-image"
data-flex-grow="254"
data-flex-basis="610px"
>&lt;/p>
&lt;h2 id="feastへのデータ操作">Feastへのデータ操作&lt;/h2>
&lt;p>5.Build a training datasetからは jupyter notebookを使うのでインストールしておきます。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">uv pip install jupyter
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>notebookの起動&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">jupyter notebook
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Jupyter Notebookで以下の内容を実行し、トレーニングに利用するデータセットを準備する。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">feast&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">FeatureStore&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">pandas&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="nn">pd&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">datetime&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">datetime&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">entity_df&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">pd&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">DataFrame&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">from_dict&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;driver_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="mi">1001&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1002&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1003&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1004&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;event_timestamp&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">datetime&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">2021&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">4&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">12&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">59&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">42&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">datetime&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">2021&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">4&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">12&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">8&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">12&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">datetime&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">2021&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">4&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">12&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">16&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">40&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">26&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">datetime&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">2021&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">4&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">12&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">15&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1&lt;/span> &lt;span class="p">,&lt;/span> &lt;span class="mi">12&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">store&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">FeatureStore&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">repo_path&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;.&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">training_df&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">store&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get_historical_features&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">entity_df&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">entity_df&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">features&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;driver_hourly_stats:conv_rate&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;driver_hourly_stats:acc_rate&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;driver_hourly_stats:avg_daily_trips&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">to_df&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">training_df&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">head&lt;/span>&lt;span class="p">())&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Train model&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># model = ml.fit(training_df)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>NoteBookでの実行結果は以下の通りとなります。&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/blog/033-feast-tutorial/img-033-002.png"
width="1258"
height="893"
srcset="https://bossagyu.com/blog/033-feast-tutorial/img-033-002_hu9a63a8af0e8adbd21872e8fe4c7e159c_152409_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/033-feast-tutorial/img-033-002_hu9a63a8af0e8adbd21872e8fe4c7e159c_152409_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Feastのトレーニングデータ"
class="gallery-image"
data-flex-grow="140"
data-flex-basis="338px"
>&lt;/p>
&lt;p>オンラインストアにデータを入れる。&lt;br>
サンプルで記載されている &lt;code>feast materialize-incremental $CURRENT_TIME&lt;/code> ではうまく動作しなかったので、データ全体を対象とするように時刻の範囲を設定しています。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">feast materialize 1970-01-01T00:00:00Z 2025-01-04T01:24:24Z
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">01/04/2025 10:28:40 AM root WARNING: _list_feature_views will make breaking changes. Please use _list_batch_feature_views instead. _list_feature_views will behave like _list_all_feature_views in the future.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Materializing &lt;span class="m">2&lt;/span> feature views from 1970-01-01 09:00:00+09:00 to 2025-01-04 10:24:24+09:00 into the sqlite online store.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">driver_hourly_stats_fresh:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 0%&lt;span class="p">|&lt;/span> &lt;span class="p">|&lt;/span> 0/5 &lt;span class="o">[&lt;/span>00:00&amp;lt;?, ?it/s&lt;span class="o">]&lt;/span>01/04/2025 10:28:40 AM root WARNING: Cannot use sqlite_vec &lt;span class="k">for&lt;/span> vector search
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">100%&lt;span class="p">|&lt;/span>███████████████████████████████████████████████████████████████&lt;span class="p">|&lt;/span> 5/5 &lt;span class="o">[&lt;/span>00:00&amp;lt;00:00, 1299.11it/s&lt;span class="o">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">driver_hourly_stats:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">100%&lt;span class="p">|&lt;/span>███████████████████████████████████████████████████████████████&lt;span class="p">|&lt;/span> 5/5 &lt;span class="o">[&lt;/span>00:00&amp;lt;00:00, 4569.95it/s&lt;span class="o">]&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Jupyter Notebookを用いてオンラインストアからデータを取得します。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">from pprint import pprint
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">from feast import FeatureStore
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">store&lt;/span> &lt;span class="o">=&lt;/span> FeatureStore&lt;span class="o">(&lt;/span>&lt;span class="nv">repo_path&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;.&amp;#34;&lt;/span>&lt;span class="o">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">feature_vector&lt;/span> &lt;span class="o">=&lt;/span> store.get_online_features&lt;span class="o">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">features&lt;/span>&lt;span class="o">=[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;driver_hourly_stats:conv_rate&amp;#39;&lt;/span>,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;driver_hourly_stats:acc_rate&amp;#39;&lt;/span>,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;driver_hourly_stats:avg_daily_trips&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">]&lt;/span>,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">entity_rows&lt;/span>&lt;span class="o">=[{&lt;/span>&lt;span class="s2">&amp;#34;driver_id&amp;#34;&lt;/span>: 1001&lt;span class="o">}]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">)&lt;/span>.to_dict&lt;span class="o">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pprint&lt;span class="o">(&lt;/span>feature_vector&lt;span class="o">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Make prediction&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># model.predict(feature_vector)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>出力結果&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="o">{&lt;/span>&lt;span class="s1">&amp;#39;acc_rate&amp;#39;&lt;/span>: &lt;span class="o">[&lt;/span>0.5004482269287109&lt;span class="o">]&lt;/span>,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;avg_daily_trips&amp;#39;&lt;/span>: &lt;span class="o">[&lt;/span>691&lt;span class="o">]&lt;/span>,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;conv_rate&amp;#39;&lt;/span>: &lt;span class="o">[&lt;/span>0.3067885637283325&lt;span class="o">]&lt;/span>,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;driver_id&amp;#39;&lt;/span>: &lt;span class="o">[&lt;/span>1001&lt;span class="o">]}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;img src="https://bossagyu.com/blog/033-feast-tutorial/img-033-002.png"
width="1258"
height="893"
srcset="https://bossagyu.com/blog/033-feast-tutorial/img-033-002_hu9a63a8af0e8adbd21872e8fe4c7e159c_152409_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/033-feast-tutorial/img-033-002_hu9a63a8af0e8adbd21872e8fe4c7e159c_152409_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Feastからデータを取得した結果"
class="gallery-image"
data-flex-grow="140"
data-flex-basis="338px"
>&lt;/p>
&lt;p>Feastのオンラインストアにマテリアラズして、オンラインストアからデータを取得することができました。&lt;/p>
&lt;h2 id="まとめ">まとめ&lt;/h2>
&lt;p>本記事では、Feastのチュートリアルを基に、Mac上でFeastを使用してデータを管理する方法を紹介しました。UVを使ったPython開発環境の構築から、Feastのインストール、UIの起動、そしてトレーニングデータの準備やオンラインストアへのデータマテリアライズまで、一連の操作を丁寧に解説しました。
これにより、Feastを利用してトレーニングデータと推論データの管理を効率的に行うことができ、トレーニングスキューの回避が可能になります。&lt;/p>
&lt;h2 id="関連記事">関連記事&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/032-python-uv/" >MacでUVを用いてPythonの開発環境を構築する&lt;/a>（Python環境構築）&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/004-python-setup/" >Pyenvとvenvを用いたローカル環境のセットアップ方法&lt;/a>（従来のPython環境構築方法）&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/031-read-parquet-file/" >macでparquetファイルを読む方法&lt;/a>（Feastのサンプルデータ形式）&lt;/li>
&lt;/ul></description></item><item><title>MacでUVを用いてPythonの開発環境を構築する</title><link>https://bossagyu.com/blog/032-python-uv/</link><pubDate>Wed, 01 Jan 2025 14:39:53 +0900</pubDate><guid>https://bossagyu.com/blog/032-python-uv/</guid><description>&lt;h2 id="概要">概要&lt;/h2>
&lt;p>MacでPythonの開発環境を構築する方法を紹介します。&lt;/p>
&lt;h2 id="uvとは">uvとは&lt;/h2>
&lt;p>2024年中旬に発表されたばかりのパッケージ管理ツール。&lt;br>
Rustで書かれており、他のパッケージ管理マネージャよりも高速であることが特徴です。&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/blog/032-python-uv/img-032-001.png"
width="585"
height="134"
srcset="https://bossagyu.com/blog/032-python-uv/img-032-001_hud269bb2e4d14314d6b8cb2d2b3093c85_13302_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/032-python-uv/img-032-001_hud269bb2e4d14314d6b8cb2d2b3093c85_13302_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="他のパッケージマネージャとの比較"
class="gallery-image"
data-flex-grow="436"
data-flex-basis="1047px"
>&lt;/p>
&lt;p>公式の説明は &lt;a class="link" href="https://docs.astral.sh/uv/" target="_blank" rel="noopener"
>こちら&lt;/a> を参照してください。&lt;/p>
&lt;h2 id="利用方法">利用方法&lt;/h2>
&lt;h3 id="uvのインストール">uvのインストール&lt;/h3>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">curl -LsSf https://astral.sh/uv/install.sh &lt;span class="p">|&lt;/span> sh
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>PATHを通す&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">source&lt;/span> &lt;span class="nv">$HOME&lt;/span>/.local/bin/env &lt;span class="o">(&lt;/span>sh, bash, zsh&lt;span class="o">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">source&lt;/span> &lt;span class="nv">$HOME&lt;/span>/.local/bin/env.fish &lt;span class="o">(&lt;/span>fish&lt;span class="o">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>インストールできたことを確認&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">uv --version
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">uv 0.5.13 &lt;span class="o">(&lt;/span>c456bae5e 2024-12-27&lt;span class="o">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="使い方">使い方&lt;/h3>
&lt;p>仮想環境の作成&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">uv venv
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>仮想環境のアクティベート&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">source&lt;/span> .venv/bin/activate
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>パッケージのインストール&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-go" data-lang="go">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">uv&lt;/span> &lt;span class="nx">pip&lt;/span> &lt;span class="nx">install&lt;/span> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="kn">package&lt;/span> &lt;span class="nx">name&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>uvの細かい説明は &lt;a class="link" href="https://docs.astral.sh/uv/" target="_blank" rel="noopener"
>公式のドキュメント&lt;/a> を参照してください。&lt;/p>
&lt;h2 id="まとめ">まとめ&lt;/h2>
&lt;p>MacでPythonの開発環境を構築する方法を紹介しました。
uvは高速かつ使いやすいので2025年1月時点ではPythonの開発環境構築において有用なツールと言えるでしょう。&lt;/p>
&lt;h2 id="関連記事">関連記事&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/004-python-setup/" >Pyenvとvenvを用いたローカル環境のセットアップ方法&lt;/a>（従来のPython環境構築方法）&lt;/li>
&lt;/ul></description></item><item><title>セレモニーの見直しによるチームパフォーマンス向上</title><link>https://bossagyu.com/blog/028-scrum-ceremony/</link><pubDate>Mon, 18 Nov 2024 09:36:05 +0900</pubDate><guid>https://bossagyu.com/blog/028-scrum-ceremony/</guid><description>&lt;h2 id="概要">概要&lt;/h2>
&lt;p>Scrumを採用しているチームでは、セレモニーを行います。
セレモニーはチームメンバーが集まり、スプリントの進捗や課題を共有し、次のスプリントに向けての計画を立てるための重要な場です。
しかし、Scrumを惰性で続けてしまっているとセレモニーが長引いたり、効果的な議論が行われなかったりすることがあります。&lt;/p>
&lt;p>この記事では、セレモニーの見直しによってチームのパフォーマンスを向上させる方法について考察します。&lt;/p>
&lt;h2 id="セレモニーの目的を明らかにする">セレモニーの目的を明らかにする&lt;/h2>
&lt;p>各セレモニーの目的は以下の通りです。&lt;br>
もっとも大切なことはこの目的を達成するために必要な議論がなされるアジェンダになっていることです。
目的については&lt;a class="link" href="https://scrumguides.org/docs/scrumguide/v2020/2020-Scrum-Guide-Japanese.pdf" target="_blank" rel="noopener"
>スクラムガイド&lt;/a>を参考に記載していますが、やや筆者の意訳が含まれています。&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>セレモニー&lt;/th>
&lt;th>目的&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>デイリースクラム&lt;/td>
&lt;td>スプリントゴールを達成するために、チームの一日の計画を立てる。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>スプリントレビュー&lt;/td>
&lt;td>スプリントの成果を検査し、今後の適応を決定すること。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>プロダクトバックログリファインメント&lt;/td>
&lt;td>プロダクトバックログを整理し、プロダクトゴールを達成するための計画を立てる。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>スプリントレトロスペクティブ&lt;/td>
&lt;td>スプリントの振り返りを行い、品質と高価を高めるための適応を計画もしくは実行する。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>スプリントプランニング&lt;/td>
&lt;td>次のスプリント期間中の作業計画を立てる&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="タイムボックスの徹底">タイムボックスの徹底&lt;/h2>
&lt;p>セレモニーを効率的に行うためには、タイムボックスを徹底することが重要です。
タイムボックスとは、セレモニーの時間を決めてその時間内に議論を終えるというルールのことです。&lt;/p>
&lt;p>セレモニーではタイムボックスを超えないようにし、超えてしまった場合はなぜ超えてしまったのか、超えないようにはどうしていくと良いのかを振り返ることが重要です。&lt;/p>
&lt;h2 id="セレモニーごとの効率的な議論を行うためのポイント">セレモニーごとの効率的な議論を行うためのポイント&lt;/h2>
&lt;p>セレモニーのうち特に時間を取られがちなものについて、効率的な議論を行うためのポイントを以下に示します。&lt;/p>
&lt;h3 id="デイリースクラム">デイリースクラム&lt;/h3>
&lt;p>デイリースクラムではチームが今日何をするべきかが明確になるようにすることに主眼を置きます。&lt;br>
それ以外の議論や報告についてはデイリースクラムとは呼ばずに関係者を絞り別途時間を設けるようにします。&lt;br>
チームメンバー全員の時間を使って行う価値があるかどうかを常に意識することが重要です。&lt;/p>
&lt;h4 id="ポイント">ポイント&lt;/h4>
&lt;ul>
&lt;li>15分を超えないようにする&lt;/li>
&lt;li>報告内容を絞る&lt;/li>
&lt;li>議論が発生する場合は、別途時間を設け議論を行うというルールにする&lt;/li>
&lt;/ul>
&lt;h3 id="スプリントレビュー">スプリントレビュー&lt;/h3>
&lt;p>スプリントレビューではステークホルダーに対してフィードバックをもらい、次のスプリントに向けての計画を立てるための情報を得ることが目的です。
漫然とインクリメントを見せるだけではなく、事前にフィードバックを得たい内容を明確にしておくことが重要です。&lt;/p>
&lt;h4 id="ポイント-1">ポイント&lt;/h4>
&lt;ul>
&lt;li>レビューの準備を事前に行う&lt;/li>
&lt;li>誰に、どのようなフィードバック得たいのか事前に明らかにしておく（仮説を立てる）&lt;/li>
&lt;/ul>
&lt;h3 id="プロダクトバックログリファインメント">プロダクトバックログリファインメント&lt;/h3>
&lt;p>プロダクトバックログリファインメントは一般的には最大でスプリントの10%程度の時間を割くことが推奨されています。&lt;br>
特に見積もりに時間がかかることが多いのである程度見切りをつけて行うことが大切です。&lt;/p>
&lt;h4 id="ポイント-2">ポイント&lt;/h4>
&lt;ul>
&lt;li>バックログアイテムに対する詳細な議論をしすぎない。
&lt;ul>
&lt;li>次スプリント以降のアイテムはすぐにやらない可能性もあるので、詳細な議論は不要です。スプリント計画のタイミングで実施しましょう。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>見積もりについては相対見積もりを採用し、細かい数値にこだわらない
&lt;ul>
&lt;li>不確実性のコーンコーンが示す通り、見積もりは基本的にはブレるものであり、細かい数値にこだわる必要はありません。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h2 id="まとめ">まとめ&lt;/h2>
&lt;p>今回はセレモニーを効率的に進める方法について説明しました。&lt;br>
セレモニーを効率的に進めることで、チームのパフォーマンスを向上させることができます。&lt;/p></description></item><item><title>macでGoの開発環境を構築して最速でHello Worldする</title><link>https://bossagyu.com/blog/030-go-environment-construction/</link><pubDate>Sun, 15 Sep 2024 16:52:04 +0900</pubDate><guid>https://bossagyu.com/blog/030-go-environment-construction/</guid><description>&lt;h2 id="概要">概要&lt;/h2>
&lt;p>MacでGo言語の開発環境を構築して、最速で Hello World する方法を紹介します。&lt;/p>
&lt;h2 id="go言語のインストール">Go言語のインストール&lt;/h2>
&lt;p>brewを使ってGo言語をインストールします。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&amp;gt; brew install go
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>バージョンを確認&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&amp;gt; go version
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">go version go1.21.3 darwin/arm64
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="hello-world-の実行">Hello World の実行&lt;/h2>
&lt;p>以下のコードを &lt;code>main.go&lt;/code> として保存します。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-go" data-lang="go">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">package&lt;/span> &lt;span class="nx">main&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="s">&amp;#34;fmt&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">func&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">fmt&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Printf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;Hello World\n&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>実行する&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&amp;gt; go run hello.go
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Hello World
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>バイナリをビルドして実行する&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&amp;gt; go build hello.go
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;gt; ls
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">hello* hello.go
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;gt; ./hello
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Hello World
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="まとめ">まとめ&lt;/h2>
&lt;p>以上で、MacでGo言語の開発環境を構築して、最速で Hello World する方法を紹介しました。&lt;/p>
&lt;h2 id="関連記事">関連記事&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/029-grpc/" >gRPCについての調査&lt;/a>（gRPCの実践）&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/041-redis-go/" >RedisをGoで使う方法&lt;/a>（Redis + Goの実践）&lt;/li>
&lt;/ul></description></item><item><title>macでparquetファイルを読む方法</title><link>https://bossagyu.com/blog/031-read-parquet-file/</link><pubDate>Sun, 15 Sep 2024 16:52:04 +0900</pubDate><guid>https://bossagyu.com/blog/031-read-parquet-file/</guid><description>&lt;h2 id="概要">概要&lt;/h2>
&lt;p>macでparquetファイルをコマンドラインで簡単に読む方法を紹介します。&lt;/p>
&lt;h2 id="parquet-cliを使って読む">parquet-cliを使って読む&lt;/h2>
&lt;p>今回は Feastのサンプルで提供されている&lt;a class="link" href="https://github.com/feast-dev/feast/tree/master/examples/podman_local/feature_repo/data" target="_blank" rel="noopener"
>Parquet&lt;/a>ファイルを読んでみます。&lt;/p>
&lt;p>parquet-cliをbrewでmacにインストールする。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">brew install parquet-cli
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>meta情報を確認する。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">$ parquet meta driver_stats.parquet
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">File path: driver_stats.parquet
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Created by: parquet-cpp-arrow version 18.1.0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Properties:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> pandas: {&amp;#34;index_columns&amp;#34;: [{&amp;#34;kind&amp;#34;: &amp;#34;range&amp;#34;, &amp;#34;name&amp;#34;: null, &amp;#34;start&amp;#34;: 0, &amp;#34;stop&amp;#34;: 1807, &amp;#34;step&amp;#34;: 1}], &amp;#34;column_indexes&amp;#34;: [{&amp;#34;name&amp;#34;: null, &amp;#34;field_name&amp;#34;: null, &amp;#34;pandas_type&amp;#34;: &amp;#34;unicode&amp;#34;, &amp;#34;numpy_type&amp;#34;: &amp;#34;object&amp;#34;, &amp;#34;metadata&amp;#34;: {&amp;#34;encoding&amp;#34;: &amp;#34;UTF-8&amp;#34;}}], &amp;#34;columns&amp;#34;: [{&amp;#34;name&amp;#34;: &amp;#34;event_timestamp&amp;#34;, &amp;#34;field_name&amp;#34;: &amp;#34;event_timestamp&amp;#34;, &amp;#34;pandas_type&amp;#34;: &amp;#34;datetimetz&amp;#34;, &amp;#34;numpy_type&amp;#34;: &amp;#34;datetime64[ns]&amp;#34;, &amp;#34;metadata&amp;#34;: {&amp;#34;timezone&amp;#34;: &amp;#34;UTC&amp;#34;}}, {&amp;#34;name&amp;#34;: &amp;#34;driver_id&amp;#34;, &amp;#34;field_name&amp;#34;: &amp;#34;driver_id&amp;#34;, &amp;#34;pandas_type&amp;#34;: &amp;#34;int64&amp;#34;, &amp;#34;numpy_type&amp;#34;: &amp;#34;int64&amp;#34;, &amp;#34;metadata&amp;#34;: null}, {&amp;#34;name&amp;#34;: &amp;#34;conv_rate&amp;#34;, &amp;#34;field_name&amp;#34;: &amp;#34;conv_rate&amp;#34;, &amp;#34;pandas_type&amp;#34;: &amp;#34;float32&amp;#34;, &amp;#34;numpy_type&amp;#34;: &amp;#34;float32&amp;#34;, &amp;#34;metadata&amp;#34;: null}, {&amp;#34;name&amp;#34;: &amp;#34;acc_rate&amp;#34;, &amp;#34;field_name&amp;#34;: &amp;#34;acc_rate&amp;#34;, &amp;#34;pandas_type&amp;#34;: &amp;#34;float32&amp;#34;, &amp;#34;numpy_type&amp;#34;: &amp;#34;float32&amp;#34;, &amp;#34;metadata&amp;#34;: null}, {&amp;#34;name&amp;#34;: &amp;#34;avg_daily_trips&amp;#34;, &amp;#34;field_name&amp;#34;: &amp;#34;avg_daily_trips&amp;#34;, &amp;#34;pandas_type&amp;#34;: &amp;#34;int32&amp;#34;, &amp;#34;numpy_type&amp;#34;: &amp;#34;int32&amp;#34;, &amp;#34;metadata&amp;#34;: null}, {&amp;#34;name&amp;#34;: &amp;#34;created&amp;#34;, &amp;#34;field_name&amp;#34;: &amp;#34;created&amp;#34;, &amp;#34;pandas_type&amp;#34;: &amp;#34;datetime&amp;#34;, &amp;#34;numpy_type&amp;#34;: &amp;#34;datetime64[us]&amp;#34;, &amp;#34;metadata&amp;#34;: null}], &amp;#34;creator&amp;#34;: {&amp;#34;library&amp;#34;: &amp;#34;pyarrow&amp;#34;, &amp;#34;version&amp;#34;: &amp;#34;18.1.0&amp;#34;}, &amp;#34;pandas_version&amp;#34;: &amp;#34;2.2.3&amp;#34;}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ARROW:schema: /////xgGAAAQAAAAAAAKAA4ABgAFAAgACgAAAAABBAAQAAAAAAAKAAwAAAAEAAgACgAAAHAEAAAEAAAAAQAAAAwAAAAIAAwABAAIAAgAAABIBAAABAAAADsEAAB7ImluZGV4X2NvbHVtbnMiOiBbeyJraW5kIjogInJhbmdlIiwgIm5hbWUiOiBudWxsLCAic3RhcnQiOiAwLCAic3RvcCI6IDE4MDcsICJzdGVwIjogMX1dLCAiY29sdW1uX2luZGV4ZXMiOiBbeyJuYW1lIjogbnVsbCwgImZpZWxkX25hbWUiOiBudWxsLCAicGFuZGFzX3R5cGUiOiAidW5pY29kZSIsICJudW1weV90eXBlIjogIm9iamVjdCIsICJtZXRhZGF0YSI6IHsiZW5jb2RpbmciOiAiVVRGLTgifX1dLCAiY29sdW1ucyI6IFt7Im5hbWUiOiAiZXZlbnRfdGltZXN0YW1wIiwgImZpZWxkX25hbWUiOiAiZXZlbnRfdGltZXN0YW1wIiwgInBhbmRhc190eXBlIjogImRhdGV0aW1ldHoiLCAibnVtcHlfdHlwZSI6ICJkYXRldGltZTY0W25zXSIsICJtZXRhZGF0YSI6IHsidGltZXpvbmUiOiAiVVRDIn19LCB7Im5hbWUiOiAiZHJpdmVyX2lkIiwgImZpZWxkX25hbWUiOiAiZHJpdmVyX2lkIiwgInBhbmRhc190eXBlIjogImludDY0IiwgIm51bXB5X3R5cGUiOiAiaW50NjQiLCAibWV0YWRhdGEiOiBudWxsfSwgeyJuYW1lIjogImNvbnZfcmF0ZSIsICJmaWVsZF9uYW1lIjogImNvbnZfcmF0ZSIsICJwYW5kYXNfdHlwZSI6ICJmbG9hdDMyIiwgIm51bXB5X3R5cGUiOiAiZmxvYXQzMiIsICJtZXRhZGF0YSI6IG51bGx9LCB7Im5hbWUiOiAiYWNjX3JhdGUiLCAiZmllbGRfbmFtZSI6ICJhY2NfcmF0ZSIsICJwYW5kYXNfdHlwZSI6ICJmbG9hdDMyIiwgIm51bXB5X3R5cGUiOiAiZmxvYXQzMiIsICJtZXRhZGF0YSI6IG51bGx9LCB7Im5hbWUiOiAiYXZnX2RhaWx5X3RyaXBzIiwgImZpZWxkX25hbWUiOiAiYXZnX2RhaWx5X3RyaXBzIiwgInBhbmRhc190eXBlIjogImludDMyIiwgIm51bXB5X3R5cGUiOiAiaW50MzIiLCAibWV0YWRhdGEiOiBudWxsfSwgeyJuYW1lIjogImNyZWF0ZWQiLCAiZmllbGRfbmFtZSI6ICJjcmVhdGVkIiwgInBhbmRhc190eXBlIjogImRhdGV0aW1lIiwgIm51bXB5X3R5cGUiOiAiZGF0ZXRpbWU2NFt1c10iLCAibWV0YWRhdGEiOiBudWxsfV0sICJjcmVhdG9yIjogeyJsaWJyYXJ5IjogInB5YXJyb3ciLCAidmVyc2lvbiI6ICIxOC4xLjAifSwgInBhbmRhc192ZXJzaW9uIjogIjIuMi4zIn0ABgAAAHBhbmRhcwAABgAAACwBAADcAAAApAAAAHAAAAA0AAAABAAAAPz+//8AAAEKEAAAABgAAAAEAAAAAAAAAAcAAABjcmVhdGVkAGr///8AAAIAKP///wAAAQIQAAAAIAAAAAQAAAAAAAAADwAAAGF2Z19kYWlseV90cmlwcwBo////AAAAASAAAABg////AAABAxAAAAAcAAAABAAAAAAAAAAIAAAAYWNjX3JhdGUAAAAA0v///wAAAQCQ////AAABAxAAAAAgAAAABAAAAAAAAAAJAAAAY29udl9yYXRlAAYACAAGAAYAAAAAAAEAxP///wAAAQIQAAAAJAAAAAQAAAAAAAAACQAAAGRyaXZlcl9pZAAAAAgADAAIAAcACAAAAAAAAAFAAAAAEAAUAAgABgAHAAwAAAAQABAAAAAAAAEKEAAAACgAAAAEAAAAAAAAAA8AAABldmVudF90aW1lc3RhbXAACAAMAAYACAAIAAAAAAADAAQAAAADAAAAVVRDAAAAAAA=
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Schema:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">message schema {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> optional int64 event_timestamp (TIMESTAMP(NANOS,true));
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> optional int64 driver_id;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> optional float conv_rate;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> optional float acc_rate;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> optional int32 avg_daily_trips;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> optional int64 created (TIMESTAMP(MICROS,false));
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Row group 0: count: 1807 16.88 B records start: 4 total(compressed): 29.796 kB total(uncompressed):29.760 kB
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">--------------------------------------------------------------------------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> type encodings count avg size nulls min / max
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">event_timestamp INT64 S _ R 1807 2.78 B 0 &amp;#34;2021-04-12T07:00:00.00000...&amp;#34; / &amp;#34;2024-12-28T14:00:00.00000...&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">driver_id INT64 S _ R 1807 0.07 B 0 &amp;#34;1001&amp;#34; / &amp;#34;1005&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">conv_rate FLOAT S _ R 1807 5.42 B 0 &amp;#34;1.9221554E-4&amp;#34; / &amp;#34;0.9998668&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">acc_rate FLOAT S _ R 1807 5.42 B 0 &amp;#34;2.1329636E-4&amp;#34; / &amp;#34;0.99993944&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">avg_daily_trips INT32 S _ R 1807 3.14 B 0 &amp;#34;0&amp;#34; / &amp;#34;999&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">created INT64 S _ R 1807 0.05 B 0 &amp;#34;2024-12-28T15:20:28.266000&amp;#34; / &amp;#34;2024-12-28T15:20:28.266000&amp;#34;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>headで中を見る。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">$ parquet head driver_stats.parquet
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{&amp;#34;event_timestamp&amp;#34;: 1734102000000000000, &amp;#34;driver_id&amp;#34;: 1005, &amp;#34;conv_rate&amp;#34;: 0.27734742, &amp;#34;acc_rate&amp;#34;: 0.7152132, &amp;#34;avg_daily_trips&amp;#34;: 823, &amp;#34;created&amp;#34;: 1735399228266000}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{&amp;#34;event_timestamp&amp;#34;: 1734105600000000000, &amp;#34;driver_id&amp;#34;: 1005, &amp;#34;conv_rate&amp;#34;: 0.57354224, &amp;#34;acc_rate&amp;#34;: 0.9831811, &amp;#34;avg_daily_trips&amp;#34;: 851, &amp;#34;created&amp;#34;: 1735399228266000}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{&amp;#34;event_timestamp&amp;#34;: 1734109200000000000, &amp;#34;driver_id&amp;#34;: 1005, &amp;#34;conv_rate&amp;#34;: 0.3287562, &amp;#34;acc_rate&amp;#34;: 0.6172164, &amp;#34;avg_daily_trips&amp;#34;: 116, &amp;#34;created&amp;#34;: 1735399228266000}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{&amp;#34;event_timestamp&amp;#34;: 1734112800000000000, &amp;#34;driver_id&amp;#34;: 1005, &amp;#34;conv_rate&amp;#34;: 0.045716193, &amp;#34;acc_rate&amp;#34;: 0.032996926, &amp;#34;avg_daily_trips&amp;#34;: 741, &amp;#34;created&amp;#34;: 1735399228266000}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{&amp;#34;event_timestamp&amp;#34;: 1734116400000000000, &amp;#34;driver_id&amp;#34;: 1005, &amp;#34;conv_rate&amp;#34;: 0.12863782, &amp;#34;acc_rate&amp;#34;: 0.8951942, &amp;#34;avg_daily_trips&amp;#34;: 534, &amp;#34;created&amp;#34;: 1735399228266000}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{&amp;#34;event_timestamp&amp;#34;: 1734120000000000000, &amp;#34;driver_id&amp;#34;: 1005, &amp;#34;conv_rate&amp;#34;: 0.9555806, &amp;#34;acc_rate&amp;#34;: 0.62216556, &amp;#34;avg_daily_trips&amp;#34;: 216, &amp;#34;created&amp;#34;: 1735399228266000}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{&amp;#34;event_timestamp&amp;#34;: 1734123600000000000, &amp;#34;driver_id&amp;#34;: 1005, &amp;#34;conv_rate&amp;#34;: 0.75297666, &amp;#34;acc_rate&amp;#34;: 0.37602386, &amp;#34;avg_daily_trips&amp;#34;: 954, &amp;#34;created&amp;#34;: 1735399228266000}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{&amp;#34;event_timestamp&amp;#34;: 1734127200000000000, &amp;#34;driver_id&amp;#34;: 1005, &amp;#34;conv_rate&amp;#34;: 0.46957988, &amp;#34;acc_rate&amp;#34;: 0.6454945, &amp;#34;avg_daily_trips&amp;#34;: 360, &amp;#34;created&amp;#34;: 1735399228266000}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{&amp;#34;event_timestamp&amp;#34;: 1734130800000000000, &amp;#34;driver_id&amp;#34;: 1005, &amp;#34;conv_rate&amp;#34;: 0.6702387, &amp;#34;acc_rate&amp;#34;: 0.36532214, &amp;#34;avg_daily_trips&amp;#34;: 396, &amp;#34;created&amp;#34;: 1735399228266000}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{&amp;#34;event_timestamp&amp;#34;: 1734134400000000000, &amp;#34;driver_id&amp;#34;: 1005, &amp;#34;conv_rate&amp;#34;: 0.019627139, &amp;#34;acc_rate&amp;#34;: 0.528229, &amp;#34;avg_daily_trips&amp;#34;: 833, &amp;#34;created&amp;#34;: 1735399228266000}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>スキーマの確認。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">$ parquet schema driver_stats.parquet
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;type&amp;#34; : &amp;#34;record&amp;#34;,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;name&amp;#34; : &amp;#34;schema&amp;#34;,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;fields&amp;#34; : [ {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;name&amp;#34; : &amp;#34;event_timestamp&amp;#34;,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;type&amp;#34; : [ &amp;#34;null&amp;#34;, &amp;#34;long&amp;#34; ],
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;default&amp;#34; : null
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> }, {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;name&amp;#34; : &amp;#34;driver_id&amp;#34;,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;type&amp;#34; : [ &amp;#34;null&amp;#34;, &amp;#34;long&amp;#34; ],
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;default&amp;#34; : null
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> }, {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;name&amp;#34; : &amp;#34;conv_rate&amp;#34;,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;type&amp;#34; : [ &amp;#34;null&amp;#34;, &amp;#34;float&amp;#34; ],
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;default&amp;#34; : null
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> }, {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;name&amp;#34; : &amp;#34;acc_rate&amp;#34;,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;type&amp;#34; : [ &amp;#34;null&amp;#34;, &amp;#34;float&amp;#34; ],
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;default&amp;#34; : null
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> }, {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;name&amp;#34; : &amp;#34;avg_daily_trips&amp;#34;,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;type&amp;#34; : [ &amp;#34;null&amp;#34;, &amp;#34;int&amp;#34; ],
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;default&amp;#34; : null
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> }, {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;name&amp;#34; : &amp;#34;created&amp;#34;,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;type&amp;#34; : [ &amp;#34;null&amp;#34;, {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;type&amp;#34; : &amp;#34;long&amp;#34;,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;logicalType&amp;#34; : &amp;#34;local-timestamp-micros&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> } ],
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;default&amp;#34; : null
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> } ]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="まとめ">まとめ&lt;/h2>
&lt;p>macでparquetファイルを簡単に読む方法を紹介しました。&lt;/p>
&lt;h2 id="関連記事">関連記事&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/033-feast-tutorial/" >FeastのチュートリアルをMacで実行する&lt;/a>（Parquetファイルを使ったFeastの活用）&lt;/li>
&lt;/ul></description></item><item><title>共分散についての調査</title><link>https://bossagyu.com/blog/030-covariance/</link><pubDate>Mon, 02 Sep 2024 10:00:00 +0900</pubDate><guid>https://bossagyu.com/blog/030-covariance/</guid><description>&lt;h2 id="共分散とは">共分散とは&lt;/h2>
&lt;p>共分散は、2つの変数がどのように連動して変化するかを示す統計量です。&lt;br>
正の値なら同じ方向に変化しやすく、負の値なら逆方向に変化しやすいことを意味します。&lt;/p>
&lt;h3 id="計算式">計算式&lt;/h3>
&lt;p>共分散は次の式で計算されます：&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/blog/030-covariance/image.png"
width="1022"
height="314"
srcset="https://bossagyu.com/blog/030-covariance/image_hue129742e3e36996d44912e33be1b2cb4_35019_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/030-covariance/image_hue129742e3e36996d44912e33be1b2cb4_35019_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="共分散の数式"
class="gallery-image"
data-flex-grow="325"
data-flex-basis="781px"
>&lt;/p>
&lt;h3 id="ポイント">ポイント&lt;/h3>
&lt;ul>
&lt;li>共分散が 0 に近い場合、2つの変数に強い関係はないと考えられます。&lt;/li>
&lt;li>値がスケールに依存するため、相関係数を使うことが一般的です。&lt;/li>
&lt;/ul>
&lt;h2 id="pythonでの例">Pythonでの例&lt;/h2>
&lt;p>以下は、Pythonを使った共分散の計算例です。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">numpy&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="nn">np&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># サンプルデータ&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">X&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="mf">2.1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mf">2.5&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mf">4.0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mf">3.6&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Y&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="mi">8&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">12&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">14&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 共分散の計算&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">cov_matrix&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">np&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">cov&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">X&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Y&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">bias&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">covariance&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">cov_matrix&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;共分散: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">covariance&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="実行結果">実行結果&lt;/h3>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">共分散: 1.53
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="共分散と相関係数の違い">共分散と相関係数の違い&lt;/h2>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>特徴&lt;/th>
&lt;th>共分散&lt;/th>
&lt;th>相関係数&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>スケール依存&lt;/td>
&lt;td>あり&lt;/td>
&lt;td>なし&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>値の範囲&lt;/td>
&lt;td>-∞ から ∞&lt;/td>
&lt;td>-1 から 1\&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="まとめ">まとめ&lt;/h2>
&lt;p>共分散は、2つの変数の関係性を理解するための基本的な指標です。&lt;br>
ただし、スケール依存性があるため、相関係数と併用することでより深い分析が可能です。&lt;/p></description></item><item><title>gRPCについての調査</title><link>https://bossagyu.com/blog/029-grpc/</link><pubDate>Sun, 01 Sep 2024 17:53:57 +0900</pubDate><guid>https://bossagyu.com/blog/029-grpc/</guid><description>&lt;h2 id="grpcとは">gRPCとは&lt;/h2>
&lt;ul>
&lt;li>PRCを実現するためにGoogleが開発したプロトコルの一つ&lt;/li>
&lt;li>Protocol Bufferを使ってデータをシリアライズし、高速な通信を実現できる点が特徴&lt;/li>
&lt;li>IDLを使ってあらかじめAPIの仕様を.protoファイルとして定義し、そこから、サーバ側&amp;amp;クライアント側に必要なソースコードを生成する。&lt;/li>
&lt;/ul>
&lt;p>REST と gRPCの違い&lt;/p>
&lt;ul>
&lt;li>RESTはリソース志向、RPCはメソッドの呼び出しが起点となり、データは副産物であるという考え方。&lt;/li>
&lt;/ul>
&lt;h2 id="利点と欠点">利点と欠点&lt;/h2>
&lt;h3 id="利点">利点&lt;/h3>
&lt;ul>
&lt;li>HTTP/2による高パフォーマンス&lt;/li>
&lt;li>Protocol Buffersによるデータ転送
&lt;ul>
&lt;li>IDLを書くことになるので、スキーマファーストで開発することになる&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>柔軟なストリーミング方式&lt;/li>
&lt;/ul>
&lt;h3 id="欠点">欠点&lt;/h3>
&lt;ul>
&lt;li>HTTP/2非対応&lt;/li>
&lt;li>ブラウザの対応状況が不十分&lt;/li>
&lt;li>言語によって機能の実装増強にばらつきがある&lt;/li>
&lt;li>バイナリにシリアライズされると人間が読めない&lt;/li>
&lt;li>RESTでも十分早い&lt;/li>
&lt;/ul>
&lt;h2 id="protファイル">.protファイル&lt;/h2>
&lt;p>gRPCではシリアライズフォーマットとしてProtocol Buffersを利用する。&lt;br>
&lt;code>.proto&lt;/code> を拡張子として持つファイル上にスキーマ定義を行い、 &lt;code>protoc&lt;/code> コマンド絵各言語用のコードを生成する。&lt;br>
Protocol Buffersでは全ての値が型を持つ。型はスカラー型とメッセージ型に分けることができる。&lt;/p>
&lt;h3 id="スカラー型">スカラー型&lt;/h3>
&lt;ul>
&lt;li>数値、文字列、真偽値、バイト配列&lt;/li>
&lt;/ul>
&lt;h3 id="メッセージ型">メッセージ型&lt;/h3>
&lt;ul>
&lt;li>複数のフィールドを持ったメッセージ型&lt;/li>
&lt;li>メッセージ型は一つの .proto ファイルに複数定義することができる&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">message Person &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> int32 &lt;span class="nv">id&lt;/span> &lt;span class="o">=&lt;/span> 1&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> string &lt;span class="nv">name&lt;/span> &lt;span class="o">=&lt;/span> 2&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> string &lt;span class="nv">email&lt;/span> &lt;span class="o">=&lt;/span>3&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="grpcのquick-startを実施する">gRPCのQuick Startを実施する&lt;/h2>
&lt;p>今回はPythonの環境を用いて、gRPCのQuick Startを実施する。&lt;br>
&lt;a class="link" href="https://grpc.io/docs/languages/python/quickstart/" target="_blank" rel="noopener"
>https://grpc.io/docs/languages/python/quickstart/&lt;/a>&lt;/p>
&lt;p>起動に必要なPythonの環境を整える。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">python -m pip install grpcio
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">python -m pip install grpcio-tools
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>サンプルコードのダウンロード&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">git clone -b v1.64.0 --depth &lt;span class="m">1&lt;/span> --shallow-submodules https://github.com/grpc/grpc
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd&lt;/span> grpc/examples/python/helloworld
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>サーバを起動する。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">python greeter_server.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 出力&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Server started, listening on &lt;span class="m">50051&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>別のターミナルを起動し、クライアントを起動する。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">python greeter_client.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">## レスポンス&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Will try to greet world ...
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Greeter client received: Hello, you!
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>gRPCのクライアントとサーバを用いて通信を行うことができました。&lt;/p>
&lt;h2 id="proto-ファイルを変更してみる">.proto ファイルを変更してみる&lt;/h2>
&lt;p>今回は、&lt;code>helloworld.proto&lt;/code> ファイルを変更して、新しいメソッドを追加してみます。&lt;/p>
&lt;p>&lt;code>helloworld.prot&lt;/code> ファイルが格納されている&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd&lt;/span> grpc/examples/protos
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>以下のように修正する&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="n">syntax&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;proto3&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">option&lt;/span> &lt;span class="n">java_multiple_files&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">true&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">option&lt;/span> &lt;span class="n">java_package&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;io.grpc.examples.helloworld&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">option&lt;/span> &lt;span class="n">java_outer_classname&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;HelloWorldProto&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">option&lt;/span> &lt;span class="n">objc_class_prefix&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;HLW&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">package&lt;/span> &lt;span class="n">helloworld&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">//&lt;/span> &lt;span class="n">The&lt;/span> &lt;span class="n">greeting&lt;/span> &lt;span class="n">service&lt;/span> &lt;span class="n">definition&lt;/span>&lt;span class="o">.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">service&lt;/span> &lt;span class="n">Greeter&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">//&lt;/span> &lt;span class="n">Sends&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="n">greeting&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">rpc&lt;/span> &lt;span class="n">SayHello&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">HelloRequest&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="n">returns&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">HelloReply&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">//&lt;/span> &lt;span class="n">以下の1行を追加&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">rpc&lt;/span> &lt;span class="n">SayHelloAgain&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">HelloRequest&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="n">returns&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">HelloReply&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">rpc&lt;/span> &lt;span class="n">SayHelloStreamReply&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">HelloRequest&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="n">returns&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">stream&lt;/span> &lt;span class="n">HelloReply&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">rpc&lt;/span> &lt;span class="n">SayHelloBidiStream&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">stream&lt;/span> &lt;span class="n">HelloRequest&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="n">returns&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">stream&lt;/span> &lt;span class="n">HelloReply&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">//&lt;/span> &lt;span class="n">The&lt;/span> &lt;span class="n">request&lt;/span> &lt;span class="n">message&lt;/span> &lt;span class="n">containing&lt;/span> &lt;span class="n">the&lt;/span> &lt;span class="n">user&lt;/span>&lt;span class="s1">&amp;#39;s name.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">message&lt;/span> &lt;span class="n">HelloRequest&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">string&lt;/span> &lt;span class="n">name&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">//&lt;/span> &lt;span class="n">The&lt;/span> &lt;span class="n">response&lt;/span> &lt;span class="n">message&lt;/span> &lt;span class="n">containing&lt;/span> &lt;span class="n">the&lt;/span> &lt;span class="n">greetings&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">message&lt;/span> &lt;span class="n">HelloReply&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">string&lt;/span> &lt;span class="n">message&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>grpcのコードを生成する&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd&lt;/span> examples/python/helloworld
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">python -m grpc_tools.protoc -I../../protos --python_out&lt;span class="o">=&lt;/span>. --pyi_out&lt;span class="o">=&lt;/span>. --grpc_python_out&lt;span class="o">=&lt;/span>. ../../protos/helloworld.proto
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>以下のファイルが再作成されている。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">ls -l
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-rw-r--r--@ &lt;span class="m">1&lt;/span> xx xx &lt;span class="m">1823&lt;/span> &lt;span class="m">9&lt;/span> &lt;span class="m">1&lt;/span> 18:12 helloworld_pb2.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-rw-r--r--@ &lt;span class="m">1&lt;/span> xx xx &lt;span class="m">578&lt;/span> &lt;span class="m">9&lt;/span> &lt;span class="m">1&lt;/span> 18:12 helloworld_pb2.pyi
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-rw-r--r--@ &lt;span class="m">1&lt;/span> xx xx &lt;span class="m">7018&lt;/span> &lt;span class="m">9&lt;/span> &lt;span class="m">1&lt;/span> 18:12 helloworld_pb2_grpc.py
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>更新される &lt;code>_pd&lt;/code> ファイルとは、protocol Buuffersの定義クラスが自動で生成されており基本的にはさわらない。&lt;/p>
&lt;p>greeter_server.py を更新する。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">concurrent&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">futures&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">logging&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">grpc&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">helloworld_pb2&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">helloworld_pb2_grpc&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">Greeter&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">helloworld_pb2_grpc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">GreeterServicer&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">SayHello&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">request&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">context&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">helloworld_pb2&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">HelloReply&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">message&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;Hello, &lt;/span>&lt;span class="si">%s&lt;/span>&lt;span class="s2">!&amp;#34;&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">request&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 以下の関数を追加&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">SayHelloAgain&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">request&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">context&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">helloworld_pb2&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">HelloReply&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">message&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;Hello Again, &lt;/span>&lt;span class="si">%s&lt;/span>&lt;span class="s2">!&amp;#34;&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">request&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">serve&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">port&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;50051&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">server&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">grpc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">server&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">futures&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">ThreadPoolExecutor&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">max_workers&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">10&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">helloworld_pb2_grpc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">add_GreeterServicer_to_server&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Greeter&lt;/span>&lt;span class="p">(),&lt;/span> &lt;span class="n">server&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">server&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">add_insecure_port&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;[::]:&amp;#34;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">port&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">server&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">start&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Server started, listening on &amp;#34;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">port&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">server&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">wait_for_termination&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="vm">__name__&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;__main__&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">logging&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">basicConfig&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">serve&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>greeter_client.py を更新する。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">run&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># NOTE(gRPC Python Team): .close() is possible on a channel and should be&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># used in circumstances in which the with statement does not fit the needs&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># of the code.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Will try to greet world ...&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">with&lt;/span> &lt;span class="n">grpc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">insecure_channel&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;localhost:50051&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="n">channel&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">stub&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">helloworld_pb2_grpc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">GreeterStub&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">channel&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">stub&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">SayHello&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">helloworld_pb2&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">HelloRequest&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;you&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Greeter client received: &amp;#34;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">response&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">message&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">stub&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">SayHelloAgain&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">helloworld_pb2&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">HelloRequest&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;you&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Greeter client received: &amp;#34;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">response&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">message&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>serverを再起動し、clientを実行する。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">python greeter_server.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">python greeter_client.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 出力&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Greeter client received: Hello, you!
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Greeter client received: Hello Again, you!
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>追加したメソッドが正常に動作していることが確認できました。&lt;/p>
&lt;h2 id="まとめ">まとめ&lt;/h2>
&lt;p>今回はgRPCについて調査の調査と公式ドキュメントのチュートリアルを行いました。
gRPCはスキーマファーストで開発することができ、HTTP/2による高パフォーマンス通信が可能であることら、最近ではREST APIの代替手段として注目されている技術ですので、ぜひ抑えておきたいです。&lt;/p>
&lt;h2 id="関連記事">関連記事&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/030-go-environment-construction/" >macでGoの開発環境を構築して最速でHello Worldする&lt;/a>（Go環境構築）&lt;/li>
&lt;/ul></description></item><item><title>ChatGPT 4oの紹介</title><link>https://bossagyu.com/blog/027-chatgpt-4o/</link><pubDate>Tue, 14 May 2024 23:22:39 +0900</pubDate><guid>https://bossagyu.com/blog/027-chatgpt-4o/</guid><description>&lt;h2 id="gpt-4oの登場">GPT-4oの登場&lt;/h2>
&lt;p>2024年5月13日に、OpenAI社より新しいGPTモデル、ChatGPT-4oが発表されました。&lt;/p>
&lt;p>GPT-4oは従来のモデルと比較して以下の内容が向上しています。&lt;/p>
&lt;ul>
&lt;li>自然な対話の実現&lt;/li>
&lt;li>より高速な応答&lt;/li>
&lt;li>多言語対応の強化&lt;/li>
&lt;li>より高い信頼性&lt;/li>
&lt;/ul>
&lt;p>特に注目すべきは、推論速度と質の向上です。これにより、リアルタイムの対話システムにおいても高い性能を発揮し、よりスムーズで自然なコミュニケーションが可能となります。&lt;/p>
&lt;h2 id="リアルタイム対話の応用">リアルタイム対話の応用&lt;/h2>
&lt;p>以下の動画では、スマートフォンを利用してChatGPT-4oとのリアルタイム対話のデモンストレーションが紹介されています。&lt;/p>
&lt;div class="video-wrapper">
&lt;iframe loading="lazy"
src="https://www.youtube.com/embed/vgYi3Wr7v_g"
allowfullscreen
title="YouTube Video"
>
&lt;/iframe>
&lt;/div>
&lt;p>実際にご覧いただけるとわかるように、ChatGPT-4oは人間との対話に近いレベルで応答を返すことができます。&lt;br>
これにより、ユーザーは非常に自然な対話を楽しむことができます。&lt;/p>
&lt;p>今までのChatGPTシリーズでは音声を文字列へ変換し、その後GPT-4へ入力することで対話を行っていました。&lt;br>
これは、音声に含まれる声色などの感情の情報が失われることを意味しています。&lt;/p>
&lt;p>しかし、ChatGPT-4oでは音声からモデルのトレーニングを行っているため、声色などの情報も考慮されており、より自然な対話が実現されています。&lt;br>
また、音声から直接音声を返却するので、テキストを解釈するステップがない分より高速に応答が返ってくるようになっています。&lt;/p>
&lt;h2 id="多言語対応の強化">多言語対応の強化&lt;/h2>
&lt;p>GPT-4oは、多言語対応の強化も図っています。&lt;br>
今までは英語で質問をすると、精度が高い回答が得られるが、日本語で質問をすると精度が落ちるという問題がありました。&lt;/p>
&lt;p>今回の多言語対応の強化により、日本語でも高い精度で回答が得られるようになりました。&lt;/p>
&lt;h2 id="まとめ">まとめ&lt;/h2>
&lt;p>今回は、OpenAI社が発表したChatGPT-4oについて紹介しました。&lt;br>
ChatGPTのモデルの精度の向上はめざましく、どんどん人間の対話に近づいていると感じます。&lt;br>
今後エンジニアは生成AIをどれだけうまく使えるかで、生産性が大きく変わってくると感じているので引き続き動向を注視していきたいと思います。&lt;/p>
&lt;h2 id="関連情報">関連情報&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://openai.com/index/hello-gpt-4o/" target="_blank" rel="noopener"
>Hello ChatGPT-4o&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="関連記事">関連記事&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/023-chatgpt-create-image/" >chatGPTで画像を生成する方法&lt;/a>（ChatGPTによる画像生成）&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/044-openai-response-api/" >OpenAI Response APIの使い方まとめ&lt;/a>（OpenAI API活用）&lt;/li>
&lt;/ul></description></item><item><title>EvernoteからNotionへの移行方法</title><link>https://bossagyu.com/blog/026-evernote-to-notion/</link><pubDate>Mon, 29 Apr 2024 19:32:38 +0900</pubDate><guid>https://bossagyu.com/blog/026-evernote-to-notion/</guid><description>&lt;h2 id="概要">概要&lt;/h2>
&lt;p>2024年4月26日にEvernoteの日本法人が解散するなど、Evernoteのサービスは終了しないもののいよいよEvernoteの雲行きが怪しくなってきました。&lt;br>
Evernoteは無料プランに制限が多いため、有料プランを利用しており費用がかさむこともあり、他のノートアプリへの移行を検討しました。&lt;/p>
&lt;p>以下の理由から移行先はNotionを選択しました。&lt;/p>
&lt;ol>
&lt;li>ノートアプリととして基本的な機能を揃えており、Evernoteの代替として十分利用できる。&lt;/li>
&lt;li>Notionは無料プランの制限がゆるく無料プランで十分使え移行することで有料プランの費用を削減できる。&lt;/li>
&lt;li>Evernoteからインポートする機能がNotion側で提供されており、移行コストが非常に低い。&lt;/li>
&lt;/ol>
&lt;p>今回はEvernoteからNotionへの移行方法をまとめます。&lt;/p>
&lt;h2 id="移行方法">移行方法&lt;/h2>
&lt;p>NotionにはEvernoteからのインポート機能が提供されているため、こちらを利用するだけで簡単に移行ができます。&lt;/p>
&lt;p>Notionのアプリのメニューから &lt;code>設定&lt;/code> を選択します。&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/blog/026-evernote-to-notion/img-026-001.png"
width="476"
height="109"
srcset="https://bossagyu.com/blog/026-evernote-to-notion/img-026-001_hue34c4fc34d425abb2cb8a7822cb5448a_12099_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/026-evernote-to-notion/img-026-001_hue34c4fc34d425abb2cb8a7822cb5448a_12099_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Notionの設定"
class="gallery-image"
data-flex-grow="436"
data-flex-basis="1048px"
>&lt;/p>
&lt;p>設定をクリックすると &lt;code>インポート&lt;/code> が表示されるので、これをクリックします。&lt;br>
インポートをクリックすると、インポート元のアプリケーションが表示されるので、ここで &lt;code>Evernote&lt;/code> を選択します。&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/blog/026-evernote-to-notion/img-026-002.png"
width="1107"
height="553"
srcset="https://bossagyu.com/blog/026-evernote-to-notion/img-026-002_hu5671edaff82d6b0627f9e68841ee4894_113782_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/026-evernote-to-notion/img-026-002_hu5671edaff82d6b0627f9e68841ee4894_113782_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Notionのインポート"
class="gallery-image"
data-flex-grow="200"
data-flex-basis="480px"
>&lt;/p>
&lt;p>連携が完了するとインポートするノートブックが選択できるようになります。&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/blog/026-evernote-to-notion/img-026-003.png"
width="262"
height="131"
srcset="https://bossagyu.com/blog/026-evernote-to-notion/img-026-003_hu22ab7887a397f6d4d5c09ac7940595a1_12909_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/026-evernote-to-notion/img-026-003_hu22ab7887a397f6d4d5c09ac7940595a1_12909_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Notionのインポート"
class="gallery-image"
data-flex-grow="200"
data-flex-basis="480px"
>&lt;/p>
&lt;p>ここでの注意点です！&lt;br>
ノートブックを複数選択すると一気にインポートできるように見えるのですが、一気にインポートするととんでもない時間がかかった挙げ句エラーが出ることがあります。
このため、ノートブックは一つづつインポートすることをおすすめします。&lt;/p>
&lt;p>一つずつインポートした場合でも、ノートの分量が多いと数時間かかったりするので、気長に対応するとよいです。&lt;/p>
&lt;p>インポート後は特に体裁が崩れることもなく、画像やリンク、ラベルも正常にインポートされるので問題なく利用ができました。&lt;/p>
&lt;h2 id="まとめ">まとめ&lt;/h2>
&lt;p>EvernoteからNotionへの移行方法をまとめました。
無料で機能が豊富なNotionに簡単に移行できるので、ぜひ移行を検討してみてください。&lt;/p></description></item><item><title>グローバルなgitignoreを設定してプロジェクト全体に適応する方法</title><link>https://bossagyu.com/blog/025-git-ignore/</link><pubDate>Tue, 16 Apr 2024 23:16:25 +0900</pubDate><guid>https://bossagyu.com/blog/025-git-ignore/</guid><description>&lt;h2 id="概要">概要&lt;/h2>
&lt;p>&lt;code>.gitignore&lt;/code> ファイルをプロジェクトに追加することでプロジェクト事にgitのトラッキング対象から外すことができます。&lt;br>
しかしながら&lt;code>.idea&lt;/code> などIDEがデフォルトで生成するディレクトリを毎回プロジェクト毎にgitignoreに追加するのが面倒です。&lt;br>
本記事では &lt;code>gitignore&lt;/code> に設定した内容をすべてのプロジェクトに 適応する方法をまとめます。&lt;/p>
&lt;h2 id="gitignoreを全体に適応する方法">gitignoreを全体に適応する方法&lt;/h2>
&lt;p>gitはデフォルトで &lt;code>~/.config/git/ignore&lt;/code> へignore設定を見に行きます。&lt;br>
このため、&lt;code>~/.config/git/ignore&lt;/code> にignore設定を記述することですべてのプロジェクトにgitignoreの内容を適応できます。&lt;/p>
&lt;p>よく &lt;code>.gitignore_global&lt;/code> を作成して、&lt;code>core.excludesfile&lt;/code> に登録する方法が案内されていますが、この方法だと &lt;code>.gitconfig&lt;/code> に無駄な設定をいれる必要があるため、こちらの方法をおすすめします。&lt;/p>
&lt;h2 id="プロジェクト全体にgitignoreを適応する手順">プロジェクト全体にgitignoreを適応する手順&lt;/h2>
&lt;p>ignoreファイルを格納するためのディレクトリを作成します。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">mkdir -p ~/.config/git/
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>ignoreファイルを作成し、全プロジェクトで無視したい内容を記述してください。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">vim ~/.config/git/ignore
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>記載例&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">.idea/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">*.log
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">node_modules/
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>この設定を行うことで、全プロジェクトで同じignore設定を適応できます。&lt;br>
すでにトラッキングしているファイルを含む場合は、一度 &lt;code>git rm --cached&lt;/code> でトラッキングを解除してください。&lt;/p>
&lt;h2 id="参考">参考&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://git-scm.com/docs/gitignore" target="_blank" rel="noopener"
>Git - gitignore&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>PythonでBluesky APIを用いて自動投稿する方法</title><link>https://bossagyu.com/blog/024-bluesky-api/</link><pubDate>Sun, 07 Apr 2024 23:52:09 +0900</pubDate><guid>https://bossagyu.com/blog/024-bluesky-api/</guid><description>&lt;h2 id="概要">概要&lt;/h2>
&lt;p>&lt;a class="link" href="https://bsky.app/" target="_blank" rel="noopener"
>Bluesky&lt;/a> とは、旧Twitter社の元CEOであるジャック・ドーシー氏が立ち上げた分散型SNSです。&lt;br>
&lt;a class="link" href="https://atproto.com/docs" target="_blank" rel="noopener"
>ATProtocl&lt;/a> というプロトコルを用いて構築されたSNSで、簡単に言うと中央管理者がいないTwitterのようなものです。&lt;br>
昨今の中央集権である通貨から分散型である仮想通貨への流れのように、SNSも分散型への流れがあるのかなと感じます。&lt;/p>
&lt;p>今回はそんなBlueskyのAPIをPythonを用いて実行する方法をまとめます。&lt;/p>
&lt;h2 id="bluesky-apiを使うまでのステップ">Bluesky APIを使うまでのステップ&lt;/h2>
&lt;ul>
&lt;li>API実行用パスワードの生成&lt;/li>
&lt;li>Python実行環境の構築&lt;/li>
&lt;li>スクリプトの作成と実行&lt;/li>
&lt;/ul>
&lt;h2 id="api実行用パスワードの生成">API実行用パスワードの生成&lt;/h2>
&lt;p>APIを実行するためにはアカウント名とAPI実行用のパスワードの発行が必要です。&lt;/p>
&lt;p>まずは、APIの実行に利用するアカウント名を確認します。&lt;br>
アカウント名は、Blueskyにログインした際に画像の箇所に表示される名前となります。&lt;br>
この際先頭の &lt;code>@&lt;/code> は不要で、私のアカウントであれば &lt;code>bossagyu.bsky.social&lt;/code> がアカウント名となります。&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/blog/024-bluesky-api/img-024-001.png"
width="1246"
height="494"
srcset="https://bossagyu.com/blog/024-bluesky-api/img-024-001_huf800b51d6508b8d55f734f90e398ecce_69330_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/024-bluesky-api/img-024-001_huf800b51d6508b8d55f734f90e398ecce_69330_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Blueskyのアカウント名"
class="gallery-image"
data-flex-grow="252"
data-flex-basis="605px"
>&lt;/p>
&lt;p>次に、API実行用のパスワードを生成します。&lt;/p>
&lt;p>API実行用パスワードは &lt;code>設定&lt;/code> → &lt;code>アプリパスワード&lt;/code> から生成できます。&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/blog/024-bluesky-api/img-024-002.png"
width="702"
height="599"
srcset="https://bossagyu.com/blog/024-bluesky-api/img-024-002_hu4ae5c012eccb5c32725cc4f577581c4d_72656_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/024-bluesky-api/img-024-002_hu4ae5c012eccb5c32725cc4f577581c4d_72656_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Blueskyのアプリパスワード1"
class="gallery-image"
data-flex-grow="117"
data-flex-basis="281px"
>&lt;/p>
&lt;p>その後、&lt;code>アプリパスワードを追加&lt;/code> をクリックします。&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/blog/024-bluesky-api/img-024-003.png"
width="589"
height="281"
srcset="https://bossagyu.com/blog/024-bluesky-api/img-024-003_hu581c5fcbb0f6fca5bc47923c36751a59_43772_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/024-bluesky-api/img-024-003_hu581c5fcbb0f6fca5bc47923c36751a59_43772_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Blueskyのアプリパスワード2"
class="gallery-image"
data-flex-grow="209"
data-flex-basis="503px"
>&lt;/p>
&lt;p>追加ボタンを押すと、パスワードにつける名前を聞かれます。&lt;br>
これ自体はパスワードとならず管理を容易にすることが目的なので、特にこだわりがなければそのまま作成します。&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/blog/024-bluesky-api/img-024-004.png"
width="588"
height="245"
srcset="https://bossagyu.com/blog/024-bluesky-api/img-024-004_hude180766a2493746a1dec1e2149d4e83_41902_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/024-bluesky-api/img-024-004_hude180766a2493746a1dec1e2149d4e83_41902_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Blueskyのアプリパスワード3"
class="gallery-image"
data-flex-grow="240"
data-flex-basis="576px"
>&lt;/p>
&lt;p>パスワードが生成されるので、これをコピーしておきます。&lt;br>
ちなみに二度と表示されなくなるので、コピーを忘れないようにしましょう。コピーを忘れた場合は再生成すればOKです。&lt;/p>
&lt;h2 id="python実行環境の構築">Python実行環境の構築&lt;/h2>
&lt;p>Pythonの実行環境をセットアップしてください。&lt;br>
venvを用いたセットアップについては、&lt;a class="link" href="https://bossagyu.com/blog/004-python-setup/" target="_blank" rel="noopener"
>こちら&lt;/a> にまとめています。&lt;/p>
&lt;p>&lt;a class="link" href="https://atproto.blue/en/latest/" target="_blank" rel="noopener"
>公式ドキュメント&lt;/a> によると、Pythonのバージョンは3.7.1以上を利用する必要がありますので注意しておいてください。&lt;/p>
&lt;p>Pythonの実行環境が整えば、ATProtocolを利用するためにライブラリをインストールします。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">$ pip install atproto
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>インストールの確認&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">$ pip list &lt;span class="p">|&lt;/span> grep atproto
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">atproto 0.0.46
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>これで準備は完了です。&lt;/p>
&lt;h2 id="スクリプトの作成と実行">スクリプトの作成と実行&lt;/h2>
&lt;p>Blueskyに投稿するスクリプトを作成します。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;span class="lnt">9
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">atproto&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">Client&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">client&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Client&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">user_name&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;bossagyu.bsky.social&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">password&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;*******&amp;#34;&lt;/span> &lt;span class="c1"># 生成したAPI実行用パスワードを入力&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">client&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">login&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">user_name&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">password&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">client&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">send_post&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;APIからの投稿です&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>スクリプトはこれだけで、APIを用いてBlueskyに投稿できます。&lt;/p>
&lt;p>それでは実行してみましょう。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">$ python post_bluesky.py
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>実行すると、Blueskyに以下のように無事投稿されました。&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/blog/024-bluesky-api/img-024-005.png"
width="1244"
height="512"
srcset="https://bossagyu.com/blog/024-bluesky-api/img-024-005_hu4f39cd8b24c00a6a84572b28046e189d_95124_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/024-bluesky-api/img-024-005_hu4f39cd8b24c00a6a84572b28046e189d_95124_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Blueskyに投稿された投稿"
class="gallery-image"
data-flex-grow="242"
data-flex-basis="583px"
>&lt;/p>
&lt;h2 id="まとめ">まとめ&lt;/h2>
&lt;p>今回はPythonを用いてBlueskyのAPIを実行する方法をまとめました。
Blueskyはまだまだ開発途中のSNSですが、TwitterはAPIを課金しないと使えないなどの制約があるので、無料でAPIを使ってSNSで遊んでみたいという方にはおすすめです。&lt;/p>
&lt;h2 id="関連記事">関連記事&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/004-python-setup/" >Pyenvとvenvを用いたローカル環境のセットアップ方法&lt;/a>（Python環境構築）&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/032-python-uv/" >MacでUVを用いてPythonの開発環境を構築する&lt;/a>（モダンなPython環境構築）&lt;/li>
&lt;/ul></description></item><item><title>chatGPTで画像を生成する方法</title><link>https://bossagyu.com/blog/023-chatgpt-create-image/</link><pubDate>Sun, 31 Mar 2024 17:35:07 +0900</pubDate><guid>https://bossagyu.com/blog/023-chatgpt-create-image/</guid><description>&lt;h2 id="概要">概要&lt;/h2>
&lt;p>Stable Diffusionなどの画像生成用のモデルではなく ChatGPTでも画像が生成できるので、生成の方法を説明します。&lt;br>
ChatGPTの有料プランを利用している人は新たに課金などせずに利用できるので、大きな手間をかけずに商用利用可能な画像を生成できます。&lt;/p>
&lt;p>今回は、DALL-Eと呼ばれるChatGPT Plusの機能を利用して画像を生成します。
DALL-E3については OpenAI の&lt;a class="link" href="https://openai.com/dall-e-3" target="_blank" rel="noopener"
>公式ページ&lt;/a>を参照してください。&lt;/p>
&lt;h2 id="画像生成の方法">画像生成の方法&lt;/h2>
&lt;p>サイドバーから 「Explore GPTs」を選択&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/blog/023-chatgpt-create-image/img-023-001.png"
width="265"
height="116"
srcset="https://bossagyu.com/blog/023-chatgpt-create-image/img-023-001_hud4b55173ac45fc1d51204f1be6812280_8545_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/023-chatgpt-create-image/img-023-001_hud4b55173ac45fc1d51204f1be6812280_8545_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Explore GPTs"
class="gallery-image"
data-flex-grow="228"
data-flex-basis="548px"
>&lt;/p>
&lt;p>検索窓で &lt;code>DALL-E&lt;/code> と入力し検索を行う。&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/blog/023-chatgpt-create-image/img-023-002.png"
width="1303"
height="365"
srcset="https://bossagyu.com/blog/023-chatgpt-create-image/img-023-002_hu7cb4a7e18b89e3c24f69774acd94f41c_41824_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/023-chatgpt-create-image/img-023-002_hu7cb4a7e18b89e3c24f69774acd94f41c_41824_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="DALL-E"
class="gallery-image"
data-flex-grow="356"
data-flex-basis="856px"
>&lt;/p>
&lt;p>&lt;code>Start Chat&lt;/code> をクリックし、画像生成を開始する。&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/blog/023-chatgpt-create-image/img-023-003.png"
width="1145"
height="540"
srcset="https://bossagyu.com/blog/023-chatgpt-create-image/img-023-003_huafd50c1ff19a49756eba7cc08e0d5a58_52595_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/023-chatgpt-create-image/img-023-003_huafd50c1ff19a49756eba7cc08e0d5a58_52595_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Start Chat"
class="gallery-image"
data-flex-grow="212"
data-flex-basis="508px"
>&lt;/p>
&lt;p>後は、生成したい画像の説明を入力するだけで画像が生成されます。&lt;/p>
&lt;h2 id="実際に画像を生成してみる">実際に画像を生成してみる&lt;/h2>
&lt;p>このブログで利用されている、ピンク色のサングラスを掛けた犬の画像を生成してみます。&lt;/p>
&lt;p>とりあえず &lt;code>ピンク色のサングラスをかけた犬&lt;/code> というプロンプトで打ってみます。&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/blog/023-chatgpt-create-image/img-023-004.png"
width="986"
height="599"
srcset="https://bossagyu.com/blog/023-chatgpt-create-image/img-023-004_hu5e0a02deff970890b46e4e180aa8365f_498318_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/023-chatgpt-create-image/img-023-004_hu5e0a02deff970890b46e4e180aa8365f_498318_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="ChatGPTで生成された画像1"
class="gallery-image"
data-flex-grow="164"
data-flex-basis="395px"
>&lt;/p>
&lt;p>また、普段のChat GPTを使うように出力された画像に対して追加のプロンプトを入力することで加工できます。&lt;br>
今回は &lt;code>アニメ調にしてください&lt;/code> と追加のプロンプトを入力してみます。&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/blog/023-chatgpt-create-image/img-023-005.png"
width="1055"
height="549"
srcset="https://bossagyu.com/blog/023-chatgpt-create-image/img-023-005_hu987fb5d0bfb771c9b7617c23bc8f5dba_474840_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/023-chatgpt-create-image/img-023-005_hu987fb5d0bfb771c9b7617c23bc8f5dba_474840_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="ChatGPTで生成された画像2"
class="gallery-image"
data-flex-grow="192"
data-flex-basis="461px"
>&lt;/p>
&lt;p>アニメ調になっているのがわかります。
このように追加のオーダーをすることでどんどん目的の画像に近づけつつ生成できます。&lt;/p>
&lt;h2 id="まとめ">まとめ&lt;/h2>
&lt;p>今回はChatGPTを利用して画像を生成する方法を説明しました。&lt;/p>
&lt;p>簡単に画像を生成できるのが非常に便利ですが、Stable Diffusion同様期待する画像を出力することはなかなか難しく、プロンプトを調整する必要があります。
Stable Diffusionを利用したときよりもプロンプトを工夫しなくても良い画像が生成されるので、このあたりはモデルの性能差なのかなと思いました。&lt;/p>
&lt;p>ちなみに、やりすぎると以下のような文章が出力され、待つように言われるので生成回数には制限がついていそうです。
マシンソースがある人はやっぱりローカルmacでStable Diffusionでやるのが良さそうですね。&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/blog/023-chatgpt-create-image/img-023-006.png"
width="866"
height="238"
srcset="https://bossagyu.com/blog/023-chatgpt-create-image/img-023-006_hue0d0043ad8a0a3a907671d165ec53110_40149_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/023-chatgpt-create-image/img-023-006_hue0d0043ad8a0a3a907671d165ec53110_40149_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="ChatGPTで画像の生成が制限"
class="gallery-image"
data-flex-grow="363"
data-flex-basis="873px"
>&lt;/p>
&lt;h2 id="関連記事">関連記事&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/027-chatgpt-4o/" >ChatGPT 4oの紹介&lt;/a>（ChatGPT 4oの新機能紹介）&lt;/li>
&lt;/ul></description></item><item><title>TyeScriptにおけるEnumの使い方</title><link>https://bossagyu.com/blog/022-typescript-enum/</link><pubDate>Sat, 23 Mar 2024 13:11:13 +0900</pubDate><guid>https://bossagyu.com/blog/022-typescript-enum/</guid><description>&lt;h2 id="概要">概要&lt;/h2>
&lt;p>この記事では、TypeScriptにおけるEnumの使い方について説明します。&lt;/p>
&lt;h2 id="enumとは">Enumとは&lt;/h2>
&lt;p>Enum（列挙型）は、特定の値の集合を表す型です。&lt;br>
多くの言語に実装されていますが、JavaScriptには存在しません。しかし、TypeScriptではEnumがサポートされています。&lt;/p>
&lt;h2 id="enumの使い方">Enumの使い方&lt;/h2>
&lt;p>以下のようにEnumを定義します。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">enum&lt;/span> &lt;span class="nx">Status&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">zero&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">one&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">two&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">Status&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">zero&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// 0
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Enumは、デフォルトで数値を割り当てられ、0から始まります。
生成されるJavaScriptコードは以下の通りです。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">var&lt;/span> &lt;span class="nx">Status&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">(&lt;/span>&lt;span class="kd">function&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">Status&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">Status&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">Status&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;zero&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;zero&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">Status&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">Status&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;one&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;one&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">Status&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">Status&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;two&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;two&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">})(&lt;/span>&lt;span class="nx">Status&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">Status&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{}));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">Status&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">zero&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// 0
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>また、enumの値を文字列で指定することもできます。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">enum&lt;/span> &lt;span class="nx">Status&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">zero&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;zero&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">one&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;one&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">two&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;two&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">Status&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">zero&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// zero
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>文字列比較を行う場合は、以下のように記述します。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">stringZero&lt;/span> :&lt;span class="kt">String&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;zero&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">value&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">stringZero&lt;/span> &lt;span class="kr">as&lt;/span> &lt;span class="nx">StringStatus&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">value&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="nx">StringStatus&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">zero&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;value is zero&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;value is not zero&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="まとめ">まとめ&lt;/h2>
&lt;p>この記事では、TypeScriptにおけるEnumの使い方について説明しました。
enumを利用することで、コードの可読性、保守性を向上させることができます。&lt;/p>
&lt;h2 id="関連記事">関連記事&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/021-typescript-setup/" >Voltaを利用してTypeScriptの開発環境を簡単にセットアップする方法&lt;/a>（TypeScript環境構築）&lt;/li>
&lt;/ul></description></item><item><title>Voltaを利用してTypeScriptの開発環境を簡単にセットアップする方法</title><link>https://bossagyu.com/blog/021-typescript-setup/</link><pubDate>Sun, 10 Mar 2024 13:11:13 +0900</pubDate><guid>https://bossagyu.com/blog/021-typescript-setup/</guid><description>&lt;h2 id="概要">概要&lt;/h2>
&lt;p>この記事では、TypeScriptの開発環境を簡単にセットアップする方法について説明します。
本記事ではMacOSを対象にしています。&lt;/p>
&lt;h2 id="voltaとは">Voltaとは&lt;/h2>
&lt;p>VoltaはNode.jsのバージョン管理ツールです。&lt;br>
&lt;a class="link" href="https://volta.sh/" target="_blank" rel="noopener"
>Voltaの公式サイト&lt;/a> で紹介されている通り以下の特徴を備えています。&lt;/p>
&lt;ul>
&lt;li>高速
&lt;ul>
&lt;li>Rustで構築されており、Node.jsのバージョン切り替えが高速です。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>信頼できる
&lt;ul>
&lt;li>プロジェクトの全員が同じツールを利用可能&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>ユニバーサル
&lt;ul>
&lt;li>パッケージマネージャー、ノードランタイム、OSに依存なく利用可能。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;p>今まではnodebrewなどを利用することが、一般的でしたが、現在はVoltaを利用するケースが増えている印象です。&lt;/p>
&lt;h2 id="voltaとnodejsのインストール">VoltaとNode.jsのインストール&lt;/h2>
&lt;p>voltaのインストールは以下のコマンドだけで完了です。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">curl https://get.volta.sh &lt;span class="p">|&lt;/span> bash
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>パスが通っていないことがあるのでzshを利用している方は以下のコマンドでパスを通してください。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s1">&amp;#39;VOLTA_HOME=$HOME/.volta&amp;#39;&lt;/span> &amp;gt;&amp;gt; ~/.zshrc
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s1">&amp;#39;export PATH=$VOLTA_HOME/bin:$PATH&amp;#39;&lt;/span> &amp;gt;&amp;gt; ~/.zshrc
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">source&lt;/span> ~/.zshrc
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>動作を確認。バージョンが表示されれば問題なくインストールができています。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">volta -v
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>voltaを利用してNode.jsをインストールします。&lt;br>
バージョンの指定をしない場合最新のLTSがインストールされます。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">volta install node
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="yarnをインストールしてtypescriptのプロジェクトを作成">yarnをインストールしてTypeScriptのプロジェクトを作成&lt;/h2>
&lt;h3 id="npmとyarnの違い">npmとyarnの違い&lt;/h3>
&lt;p>npm, yarnともにNode.jsのパッケージマネージャーとなります。&lt;br>
それぞれの特徴は以下の通りです。&lt;/p>
&lt;p>npm&lt;/p>
&lt;ul>
&lt;li>Node.jsがリリースされた翌年（2010年）リリース&lt;/li>
&lt;li>Node Package Managerの略&lt;/li>
&lt;li>package-lock.jsonファイルを自動的に生成する&lt;/li>
&lt;li>Node.jsをインストールすれば自動的にインストールされる&lt;/li>
&lt;/ul>
&lt;p>yarn&lt;/p>
&lt;ul>
&lt;li>2016年リリース&lt;/li>
&lt;li>Facebook、Google、Exponent、Tildeによって開発された新しいJavaScriptパッケージマネージャー&lt;/li>
&lt;li>npｍと互換性がある
&lt;ul>
&lt;li>同じpackage.jsonが使える&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>npmより厳密にモジュールのバージョンを固定できる&lt;/li>
&lt;li>npmよりインストールが速い&lt;/li>
&lt;/ul>
&lt;p>yarnの方が優れているように見えますが、最近ではnpmがアップデートされて機能の差はあまりないようです。&lt;br>
今回はyarnを利用してTypeScriptのプロジェクトを作成します。&lt;/p>
&lt;h3 id="yarnのインストール">yarnのインストール&lt;/h3>
&lt;p>voltaを利用してyarnをインストールします。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">volta install yarn
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>インストールされているかを確認します。&lt;br>
listの結果にyarnが表示されれば問題なくインストールされています。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">volta list
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="typescriptのプロジェクトを作成">TypeScriptのプロジェクトを作成&lt;/h3>
&lt;p>yarnの初期化&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">yarn init -y
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Node.jsのインストール&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">volta pin node@20.0.0
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>TypeScriptのインストール&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">yarn add typescript
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>node-tsのインストール&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">yarn add --dev ts-node
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>tsconfig.jsonを作成します。&lt;br>
tsconfig.jsonはTypeScriptの設定ファイルで、コンパイル時の設定を記述します。&lt;br>
今回は、console.logを利用するため、今回はtargetを &lt;code>es2016&lt;/code> に設定します。特にデフォルトで生成されるものから変更する必要はありません。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">yarn tsc --init
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>サンプルプログラムを実行してみる。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;console.log(&amp;#39;Hello, TypeScript!&amp;#39;);&amp;#34;&lt;/span> &amp;gt; hello.ts
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">yarn ts-node hello.ts
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 以下のように表示されれば成功&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Hello, TypeScript!
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>無事にテストスクリプトが動きました。&lt;br>
以上でTypeScriptの開発環境のセットアップが完了です。&lt;/p>
&lt;h2 id="まとめ">まとめ&lt;/h2>
&lt;p>本記事では、Voltaを利用してTypeScriptの開発環境を簡単にセットアップする方法について説明しました。
Voltaを利用することで、Node.jsのバージョン管理が簡単になり、開発環境のセットアップがスムーズに行えます。
また、VoltaでNode.jsのバージョンを指定すると、package.jsonにバージョンが記述され、他の開発者とのバージョンの差異を解消することができる点も魅力的ですね。&lt;/p>
&lt;h2 id="関連記事">関連記事&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/022-typescript-enum/" >TyeScriptにおけるEnumの使い方&lt;/a>（TypeScript Enumの使い方）&lt;/li>
&lt;/ul></description></item><item><title>ITIL v4 キャパシティ及びパフォーマンス管理について解説</title><link>https://bossagyu.com/blog/020-itilv4-capacity-and-performance-management/</link><pubDate>Tue, 27 Feb 2024 08:53:36 +0900</pubDate><guid>https://bossagyu.com/blog/020-itilv4-capacity-and-performance-management/</guid><description>&lt;h2 id="概要">概要&lt;/h2>
&lt;p>この記事では、ITIL v4のキャパシティ及びパフォーマンス管理について説明します。
た、理解した内容をもとに自分の経験を当てはめキャパシティ及びパフォーマンス管理のプロセスについて説明します。&lt;/p>
&lt;h2 id="キャパシティおよびパフォーマンス管理とは">キャパシティおよびパフォーマンス管理とは&lt;/h2>
&lt;p>サービス及びサービスを支えるリソースのパフォーマンスを管理することです。&lt;br>
キャパシティとパフォーマンスの管理活動を通じて、サービスのパフォーマンスを最適化し、サービスのキャパシティを適切に確保することが目的です。&lt;/p>
&lt;h2 id="キャパシティ及びパフォーマンス管理のプロセス">キャパシティ及びパフォーマンス管理のプロセス&lt;/h2>
&lt;p>キャパシティ及びパフォーマンス管理のプロセスは以下の2つがあります。&lt;/p>
&lt;ul>
&lt;li>キャパシティとパフォーマンスコントロールの確立&lt;/li>
&lt;li>サービスのキャパシティとパフォーマンスの分析と改善&lt;/li>
&lt;/ul>
&lt;h3 id="キャパシティとパフォーマンスコントロールの確立">キャパシティとパフォーマンスコントロールの確立&lt;/h3>
&lt;p>キャパシティとパフォーマンスコントロールの確立は、サービスが利用するITリソースの使用量と性能基準について、要件を利害関係者と合意し、それらを評価するタイミング・基準値・報告形式を決めることです。&lt;/p>
&lt;p>以下の流れで実現されます。&lt;/p>
&lt;ol>
&lt;li>サービスキャパシティとパフォーマンス要件の特定&lt;/li>
&lt;li>サービスキャパシティとパフォーマンス要件の合意&lt;/li>
&lt;li>キャパシティとパフォーマンスの要件の決定&lt;/li>
&lt;li>キャパシティとパフォーマンス評価指標とレポートの設計&lt;/li>
&lt;/ol>
&lt;p>上記プロセスに対して筆者の経験を当てはめると以下の通りになりました。&lt;/p>
&lt;ul>
&lt;li>サービスキャパシティとパフォーマンス要件の特定
&lt;ul>
&lt;li>筆者は社内PFとしてAPIを提供していたため、社内の利用者から求められるレイテンシー性能（99%ile Nms）を特定しました。&lt;/li>
&lt;li>上記のしきい値をベースにパフォーマンス検証を行い、1インスタンスあたりのスループットを測定しました。&lt;/li>
&lt;li>スループットをベースに必要となる金額を算出しました。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>サービスキャパシティとパフォーマンス要件の合意
&lt;ul>
&lt;li>レイテンシー性能とスループットを関係者と合意を行いました。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>キャパシティとパフォーマンスの要件の決定
&lt;ul>
&lt;li>こちらについては合意内容と変わらず&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>キャパシティとパフォーマンス評価指標のレポートの設計
&lt;ul>
&lt;li>パフォーマンスについてはDynatraceと呼ばれるトレーシングツールを利用して計測、レポートを作成しました。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h3 id="サービスのキャパシティとパフォーマンスの分析と改善">サービスのキャパシティとパフォーマンスの分析と改善&lt;/h3>
&lt;p>サービスの出力ログ・インシデント情報から使用量と性能状況の問題点を分析する。&lt;/p>
&lt;p>以下の流れで実現されます。&lt;/p>
&lt;ol>
&lt;li>キャパシティとパフォーマンスの分析&lt;/li>
&lt;li>キャパシティとパフォーマンスの報告&lt;/li>
&lt;li>キャパシティとパフォーマンスの計画と設計&lt;/li>
&lt;/ol>
&lt;p>上記プロセスに対して筆者の経験を当てはめると以下の通りになりました。&lt;/p>
&lt;ul>
&lt;li>キャパシティとパフォーマンスの分析
&lt;ul>
&lt;li>パフォーマンスの分析については、Dynatraceを利用して、レイテンシー性能とスループットを分析しました。&lt;/li>
&lt;li>インシデント情報から、パフォーマンスの問題点を特定しました。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>キャパシティとパフォーマンスの報告
&lt;ul>
&lt;li>パフォーマンスの報告については、Dynatraceのダッシュボードを利用して、レイテンシー性能とスループットを可視化しました。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>キャパシティとパフォーマンスの計画と設計
&lt;ul>
&lt;li>利用者の拡大によって現状のキャパシティでは受け入れが難しくなります。&lt;/li>
&lt;li>需要予測を立て必要なキャパシティの増強計画を立てて、実行に移すようにしています。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h2 id="まとめ">まとめ&lt;/h2>
&lt;p>今回はキャパシティ及びパフォーマンス管理について学習した内容に基づき筆者の経験を当てはめ説明しました。
キャパシティ及びパフォーマンス管理では、可用性管理相当のことをキャパシティとパフォーマンスの観点から行うことが理解できました。
筆者の経験ではパフォーマンスの話と可用性の話についてはセットで行うことが多いのであまり独立して行わないと感じました。&lt;/p>
&lt;h2 id="関連記事">関連記事&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/016-itilv4-availability-management/" >ITIL v4 可用性管理について解説&lt;/a>（関連ITIL v4記事）&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/018-itilv4-business-analysis/" >ITIL v4 事業分析について解説&lt;/a>（関連ITIL v4記事）&lt;/li>
&lt;/ul></description></item><item><title>MacでStable Diffusion Web UIを使う方法</title><link>https://bossagyu.com/blog/019-stable-diffusion/</link><pubDate>Mon, 12 Feb 2024 11:24:59 +0900</pubDate><guid>https://bossagyu.com/blog/019-stable-diffusion/</guid><description>&lt;h2 id="概要">概要&lt;/h2>
&lt;p>この記事ではMacにStable Diffusion Web UIをインストールし、ローカルで利用する方法を紹介します。&lt;/p>
&lt;h2 id="stable-diffusionとは">Stable Diffusionとは&lt;/h2>
&lt;p>Stable Diffusionは、AIを用いた画像処理技術の一つです。
テキストを入力することで、そのテキストに対応する画像を生成することができます。&lt;/p>
&lt;p>以下は、Stable Diffusionのアニメ画像を出力できるモデルで、黒髪の少女を出力した例です。&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/blog/019-stable-diffusion/img-019-003.png"
width="389"
height="389"
srcset="https://bossagyu.com/blog/019-stable-diffusion/img-019-003_huf43ace8cce0a642242bd8f163fc1e22c_288137_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/019-stable-diffusion/img-019-003_huf43ace8cce0a642242bd8f163fc1e22c_288137_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Stable Diffusionのイメージ"
class="gallery-image"
data-flex-grow="100"
data-flex-basis="240px"
>&lt;/p>
&lt;h2 id="stable-diffusionを利用する方法">Stable Diffusionを利用する方法&lt;/h2>
&lt;p>Stable Diffusionを利用方法は大きく以下の2種類に分かれます。&lt;/p>
&lt;ul>
&lt;li>&lt;a class="link" href="https://huggingface.co/" target="_blank" rel="noopener"
>Hugging Face&lt;/a>, &lt;a class="link" href="https://beta.dreamstudio.ai/generate" target="_blank" rel="noopener"
>Dream Studio&lt;/a> などのwebアプリケーションを利用する&lt;/li>
&lt;li>ローカルでStable Diffusion Web UIを利用する&lt;/li>
&lt;/ul>
&lt;p>本記事では、ローカルでStable Diffusion Web UIを利用する方法を紹介します。
試しに使って見るだけであれば、webアプリケーションを利用するのが簡単ですが、画像を大量に生成する場合は制限があったり、費用がかかったりするので、
ある程度の量を生成する場合はローカルで利用することをおすすめします。&lt;/p>
&lt;h2 id="stable-diffusion-web-uiをローカルで利用する方法">Stable Diffusion Web UIをローカルで利用する方法&lt;/h2>
&lt;p>今回はAUTOMATIC1111氏が公開している、&lt;a class="link" href="https://github.com/AUTOMATIC1111/stable-diffusion-webui" target="_blank" rel="noopener"
>stable-diffusion-web-ui&lt;/a>を利用します。&lt;/p>
&lt;ol>
&lt;li>動作する環境を整える&lt;/li>
&lt;li>stable-diffusion-web-uiをインストールする&lt;/li>
&lt;li>モデルファイルを配置する&lt;/li>
&lt;li>stable-diffusion-web-uiを起動し画像を生成する&lt;/li>
&lt;/ol>
&lt;h3 id="1-動作する環境を整える">1. 動作する環境を整える&lt;/h3>
&lt;p>まずは、ローカルで動作させるにあたって、Pythonやその他ライブラリが必要であるため、homebrewを利用してインストールします。&lt;/p>
&lt;p>homebrewのインストール&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">/bin/bash -c &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="k">$(&lt;/span>curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh&lt;span class="k">)&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>homebrewのパスを通す&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">PATH&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$PATH&lt;/span>&lt;span class="s2">:/opt/homebrew/bin/&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>関連ライブラリのインストール&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">brew install cmake protobuf rust pyenv git wget
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>pyenvを用いて、Python環境のセットアップ。複数のPythonのバージョンを使い分けられるようにします。
このあとにvenvも登場します。pythonの環境の構築については&lt;a class="link" href="https://bossagyu.com/blog/004-python-setup/" target="_blank" rel="noopener"
>こちら&lt;/a>の記事を参考にしてください。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">pyenv install 3.10.6
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pyenv &lt;span class="nb">local&lt;/span> 3.10.6
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="2-stable-diffusion-web-uiをインストールする">2. stable-diffusion-web-uiをインストールする&lt;/h3>
&lt;p>git cloneでリポジトリをクローンします。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">git clone https://github.com/AUTOMATIC1111/stable-diffusion-webui.git
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd&lt;/span> stable-diffusion-webui
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>venvで仮想環境を設定、他の環境を汚さないようにします。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">python -m venv venv
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">source&lt;/span> venv/bin/activate
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>これで環境構築は完了です。&lt;/p>
&lt;h3 id="3-モデルファイルを配置する">3. モデルファイルを配置する&lt;/h3>
&lt;p>次にモデルファイルをダウンロードしてきて、&lt;code>stable-diffusion-webui/models/Stable-diffusion/&lt;/code> ディレクトリに配置します。&lt;br>
モデルファイルは以下のサイトからダウンロードできます。&lt;/p>
&lt;ul>
&lt;li>&lt;a class="link" href="https://dream-studio.tech/" target="_blank" rel="noopener"
>Civitai&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://huggingface.co/models?pipeline_tag=text-to-image&amp;amp;sort=downloads" target="_blank" rel="noopener"
>Hugging Face&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>今回はCivilaiから &lt;code>bule_pencil&lt;/code> のモデルをダウンロードしてきて利用してみます。&lt;/p>
&lt;p>Civilaiの検索窓に&lt;code>bule_pencil&lt;/code>と入力し、検索します。&lt;br>
検索結果から&lt;code>bule_pencil&lt;/code>を選択し、&lt;code>Download&lt;/code>ボタンをクリックします。&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/blog/019-stable-diffusion/img-019-001.png"
width="1357"
height="1019"
srcset="https://bossagyu.com/blog/019-stable-diffusion/img-019-001_hu0340b7643e68d89bf045571164e24a86_1170489_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/019-stable-diffusion/img-019-001_hu0340b7643e68d89bf045571164e24a86_1170489_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="ダウンロード画面"
class="gallery-image"
data-flex-grow="133"
data-flex-basis="319px"
>&lt;/p>
&lt;p>ダウンロードしたモデルをディレクトリに移します。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">mv ~/Downloads/bluePencilXL_v401.safetensors models/Stable-diffusion/
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="4-stable-diffusion-web-uiを起動し画像を生成する">4. stable-diffusion-web-uiを起動し画像を生成する&lt;/h3>
&lt;p>最後に、stable-diffusion-web-uiを起動し、画像を生成します。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">./webui.sh
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>起動したらプロンプトにテキストを入力し、画像を生成します。&lt;/p>
&lt;ol>
&lt;li>Stable Diffusion checkpointで先程ダウンロードしたbule_pencilを選択します。&lt;/li>
&lt;li>promptに生成したい画像の要素を入力します&lt;/li>
&lt;li>Negative promptに生成してほしくない画像の要素を入れます。&lt;/li>
&lt;li>Generateをクリックします。&lt;/li>
&lt;/ol>
&lt;p>&lt;img src="https://bossagyu.com/blog/019-stable-diffusion/img-019-002.png"
width="3304"
height="1860"
srcset="https://bossagyu.com/blog/019-stable-diffusion/img-019-002_hu4a45a04ce22c441948fe1eb974670ec4_1576758_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/019-stable-diffusion/img-019-002_hu4a45a04ce22c441948fe1eb974670ec4_1576758_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="画像生成画面"
class="gallery-image"
data-flex-grow="177"
data-flex-basis="426px"
>&lt;/p>
&lt;p>ピンク色のサングラスをかけた犬という意味のテキストを入力してみたところ、ちゃんと出力されました。&lt;br>
もちろん、他のテキストを入力することで、様々な画像を生成することができます。&lt;/p>
&lt;h2 id="まとめ">まとめ&lt;/h2>
&lt;p>この記事ではMacにStable Diffusion Web UIをインストールし、ローカルで利用する方法を紹介しました。
ローカルで利用することで、制限があったり、費用がかかったりするwebアプリケーションを利用するよりも、自由に画像を生成することができます。&lt;/p></description></item><item><title>ITIL v4 事業分析について解説</title><link>https://bossagyu.com/blog/018-itilv4-business-analysis/</link><pubDate>Fri, 09 Feb 2024 09:00:56 +0900</pubDate><guid>https://bossagyu.com/blog/018-itilv4-business-analysis/</guid><description>&lt;h2 id="概要">概要&lt;/h2>
&lt;p>ITIL v4の事業分析について、学習し理解した内容をまとめます。&lt;br>
また、理解した内容をもとに自分の経験を当てはめ、事業分析のプロセスについて説明します。&lt;/p>
&lt;h2 id="事業分析とは">事業分析とは&lt;/h2>
&lt;p>事業分析とは、事業または他の何らかの要素を分析し、それらのニーズへの対応やビジネス上の課題を解決するためのソリューションを提案するプロセスです。&lt;br>
「事業」分析とされていますが、対象は事業だけでないことに注意が必要です。&lt;/p>
&lt;h2 id="事業分析手法の例">事業分析手法の例&lt;/h2>
&lt;p>事業分析手法の例として以下のようなものがあります。&lt;/p>
&lt;ul>
&lt;li>SWOT分析&lt;/li>
&lt;li>ユーザーストーリー&lt;/li>
&lt;/ul>
&lt;p>具体的ない方法についてはここでは本題ではないため他のサイトに譲ります。&lt;br>
私は例には挙げられていませんが、カスタマージャーニーマップをよく使います。&lt;/p>
&lt;h2 id="事業分析のプロセス">事業分析のプロセス&lt;/h2>
&lt;p>事業分析のプロセスは以下の２つのプロセスがあります。&lt;/p>
&lt;ul>
&lt;li>事業分析アプローチの設計と維持&lt;/li>
&lt;li>事業分析とソリューションの特定&lt;/li>
&lt;/ul>
&lt;h3 id="事業分析アプローチの設計と維持">事業分析アプローチの設計と維持&lt;/h3>
&lt;p>このプロセスの焦点は、組織の現在および予想されるニーズに対処することにより、ビジネス分析に対する一貫した効果的なアプローチを確立することです。&lt;br>
以下の流れで実行されます。&lt;/p>
&lt;ul>
&lt;li>組織と要件を分析する&lt;/li>
&lt;li>ビジネス分析のアプローチ手法のレビュー&lt;/li>
&lt;li>ビジネス分析アプローチを実行する&lt;/li>
&lt;/ul>
&lt;p>私が所属する組織ではあまり、事業レイヤーレベルの分析を行うことはないですが、一般的に要件・要求があればそれを特定の手法で分析しましょうというお話であると理解しています。&lt;/p>
&lt;h3 id="事業分析とソリューションの特定">事業分析とソリューションの特定&lt;/h3>
&lt;p>このプロセスは、利害関係者のニーズと要件を分析することに重点が置かれています。
分析の結果から利害関係者のニーズと要件に対処するためのソリューションの特定と提案が含まれています。&lt;br>
以下の流れで実行されます。&lt;/p>
&lt;ol>
&lt;li>ステークホルダーからの情報収集と分析&lt;/li>
&lt;li>ソリューションのオプションを定義し、推奨されるソリューションを特定する&lt;/li>
&lt;li>ソリューション提供チームへのサポートの提供&lt;/li>
&lt;li>ソリューションのパフォーマンスと評価&lt;/li>
&lt;/ol>
&lt;p>上記活動について、私の経験を当てはめると以下のように理解しました。&lt;/p>
&lt;h4 id="12について">1,2について&lt;/h4>
&lt;ul>
&lt;li>ステークホルダーから情報を収集、得た情報の分析を行う。&lt;/li>
&lt;li>分析結果から解決すべき課題(why)を特定、課題に対してどのような解決策があるか(what)を決定する。&lt;/li>
&lt;/ul>
&lt;h4 id="3について">3について&lt;/h4>
&lt;ul>
&lt;li>1,2で特定されたwhy, whatに対して、どのような解決策(how)があるかをプロダクトチームと一緒に考える&lt;/li>
&lt;li>この際に解決手法とともに評価基準をきめ、どのような変化があればこのソリューションが成功したと言えるかを決定する。
&lt;ul>
&lt;li>またこの際に効果はどの程度の時間軸で現れるのかの認識を合わせておくとよい。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h4 id="4について">4について&lt;/h4>
&lt;ul>
&lt;li>定めた評価基準に対して、ソリューションがどの程度達成されているかを定期的に評価する。&lt;/li>
&lt;li>数値については手動集計だと見なくなるので、個人的には自動化してGrafanaなどで可視化することをおすすめします。&lt;/li>
&lt;/ul>
&lt;h2 id="まとめ">まとめ&lt;/h2>
&lt;p>今回は事業分析について学習した内容に基づき私の経験を当てはめ説明しました。&lt;br>
個人的な理解としては事業分析といいつつ事業だけを対象としないこと、分析といいつつ分析したあとのプロセスも含まれていることが理解できました。&lt;/p>
&lt;h2 id="参考">参考&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://www.axelos.com/resource-hub/practice/business-analysis-management-itil-4-practice-guide" target="_blank" rel="noopener"
>Business analysis management: ITIL 4 Practice Guide&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="関連記事">関連記事&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/016-itilv4-availability-management/" >ITIL v4 可用性管理について解説&lt;/a>（関連ITIL v4記事）&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/020-itilv4-capacity-and-performance-management/" >ITIL v4 キャパシティ及びパフォーマンス管理について解説&lt;/a>（関連ITIL v4記事）&lt;/li>
&lt;/ul></description></item><item><title>VSCodeでGithub Copilotを使いこなす完全ガイド</title><link>https://bossagyu.com/blog/017-vscode-copilot/</link><pubDate>Sun, 04 Feb 2024 22:34:51 +0900</pubDate><guid>https://bossagyu.com/blog/017-vscode-copilot/</guid><description>&lt;h2 id="概要">概要&lt;/h2>
&lt;p>この記事では、VSCodeでGithub Copilotを設定して使う方法から、Markdownでの活用、Chat toolの使い方まで網羅的に説明します。
前提としてGithub Copilotのアカウントが必要です。&lt;/p>
&lt;h2 id="vscodeでgithub-copilotを使えるようにするまで">VSCodeでGithub Copilotを使えるようにするまで&lt;/h2>
&lt;h3 id="拡張機能をインストール">拡張機能をインストール&lt;/h3>
&lt;p>まずは、VSCodeに拡張機能をインストールします。
VSCode を開き、左メニューの四角形が4つあるアイコンをクリックし、検索用テキスト入力に「copilot」と入力します。
「install」をクリックし、インストールを開始してください。&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/blog/017-vscode-copilot/img-017-001.png"
width="626"
height="267"
srcset="https://bossagyu.com/blog/017-vscode-copilot/img-017-001_hua2075257b737ae41d41cddc9273959b8_30520_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/017-vscode-copilot/img-017-001_hua2075257b737ae41d41cddc9273959b8_30520_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="拡張機能"
class="gallery-image"
data-flex-grow="234"
data-flex-basis="562px"
>&lt;/p>
&lt;h3 id="githubとの連携">GitHubとの連携&lt;/h3>
&lt;p>installをクリックし、installが完了すれば以下のような画面が表示されるので、「Sign in to GitHub」をクリックします。&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/blog/017-vscode-copilot/img-017-002.png"
width="574"
height="141"
srcset="https://bossagyu.com/blog/017-vscode-copilot/img-017-002_hud50fff9149b461526033d19f8d6aa177_7541_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/017-vscode-copilot/img-017-002_hud50fff9149b461526033d19f8d6aa177_7541_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Sing in to GitHubの画面"
class="gallery-image"
data-flex-grow="407"
data-flex-basis="977px"
>&lt;/p>
&lt;p>GitHubのアカウントへのアクセスを要求されるので「Allow」で許可します。&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/blog/017-vscode-copilot/img-017-003.png"
width="984"
height="245"
srcset="https://bossagyu.com/blog/017-vscode-copilot/img-017-003_hu2ee054375fc09dad64838e766feb723e_62679_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/017-vscode-copilot/img-017-003_hu2ee054375fc09dad64838e766feb723e_62679_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="アクセス許可確認画面"
class="gallery-image"
data-flex-grow="401"
data-flex-basis="963px"
>&lt;/p>
&lt;p>「Authorize Visual Studio Code」をクリックし、許可します。&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/blog/017-vscode-copilot/img-017-004.png"
width="653"
height="735"
srcset="https://bossagyu.com/blog/017-vscode-copilot/img-017-004_hu681f1d2b9e85acefcddbdd060977aa52_52159_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/017-vscode-copilot/img-017-004_hu681f1d2b9e85acefcddbdd060977aa52_52159_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="確認画面"
class="gallery-image"
data-flex-grow="88"
data-flex-basis="213px"
>&lt;/p>
&lt;p>これでGithub CopilotとVSCodeの連携が完了し、使えるようになりました。&lt;/p>
&lt;h2 id="使い方">使い方&lt;/h2>
&lt;p>基本的には、コードを書いていくだけで自動的に補完されるようになります。
補完内容が提案されるので以下のショートカットを使いながらコードを書いていくと良いでしょう。&lt;/p>
&lt;h3 id="チートシート">チートシート&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>機能&lt;/th>
&lt;th>キー&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>提案を受け入れる&lt;/td>
&lt;td>Tab&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>提案を拒否する&lt;/td>
&lt;td>Esc&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Copilotを開く&lt;/td>
&lt;td>Ctrl + Enter&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>次の提案&lt;/td>
&lt;td>Alt/Option + ]&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>前の提案&lt;/td>
&lt;td>Alt/Option + [&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>インラインCopilotをトリガーする&lt;/td>
&lt;td>Alt/Option + \&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="markdownファイルでの補完を有効にする">Markdownファイルでの補完を有効にする&lt;/h2>
&lt;p>Github Copilotは、デフォルトではMarkdownファイルでの補完が無効になっています。
ブログ執筆などでMarkdownを使う場合は、有効化しておくと便利です。&lt;/p>
&lt;h3 id="設定方法">設定方法&lt;/h3>
&lt;ol>
&lt;li>VSCodeでGitHub Copilotのプラグインのページを開きます&lt;/li>
&lt;li>歯車アイコンをクリックし、設定を開きます&lt;/li>
&lt;li>設定画面で &lt;code>markdown&lt;/code> が &lt;code>false&lt;/code> になっているので &lt;code>true&lt;/code> に変更します&lt;/li>
&lt;/ol>
&lt;p>これでMarkdownファイルでもCopilotの補完が効くようになります。
このブログもGithub Copilotで補完を行いながら書いており、かなり効率化できています。&lt;/p>
&lt;h2 id="chat-toolを使ってさらに便利に">Chat toolを使ってさらに便利に&lt;/h2>
&lt;p>Github Copilot Chatには「Chat tool」という機能があり、チャット内で様々なタスクを実行できます。&lt;/p>
&lt;h3 id="chat-toolの実行方法">Chat toolの実行方法&lt;/h3>
&lt;p>基本的には &lt;code>#&amp;lt;command&amp;gt; &amp;lt;args&amp;gt;&lt;/code> の形式でコマンドを入力します。&lt;/p>
&lt;h3 id="代表的なchat-toolのコマンド">代表的なChat toolのコマンド&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>コマンド&lt;/th>
&lt;th>機能&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>#codebase&lt;/code>&lt;/td>
&lt;td>現在のワークスペースを全部検索する&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>#selection&lt;/code>&lt;/td>
&lt;td>現在のエディタで選択しているコードをコンテキストとしてプロンプトに追加する&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>#terminal_selection&lt;/code>&lt;/td>
&lt;td>現在のターミナルで選択している部分をコンテキストとしてプロンプトに追加する。エラーが出たときにターミナルの出力を参照するのに便利です。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>#fetch_webpage&lt;/code>&lt;/td>
&lt;td>URLを指定してwebページからコンテンツを取得してコンテキストとしてプロンプトに追加する&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>他にも便利なコマンドがあるので、公式ドキュメントを参照してください。&lt;/p>
&lt;ul>
&lt;li>&lt;a class="link" href="https://code.visualstudio.com/docs/copilot/reference/copilot-vscode-features#_chat-tools" target="_blank" rel="noopener"
>Github Copilot Chat tools&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="まとめ">まとめ&lt;/h2>
&lt;p>VSCodeでGithub Copilotを設定して使う方法について説明しました。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>基本設定&lt;/strong>: 拡張機能のインストールとGitHub連携&lt;/li>
&lt;li>&lt;strong>ショートカット&lt;/strong>: Tab/Escで提案の受け入れ・拒否&lt;/li>
&lt;li>&lt;strong>Markdown対応&lt;/strong>: 設定でmarkdownをtrueに変更&lt;/li>
&lt;li>&lt;strong>Chat tool&lt;/strong>: &lt;code>#codebase&lt;/code>や&lt;code>#selection&lt;/code>でコンテキストを追加&lt;/li>
&lt;/ul>
&lt;p>Github Copilotはソースコードだけではなく文章にも補完を行ってくれます。
ぜひ活用してみてください。&lt;/p>
&lt;h2 id="関連記事">関連記事&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/005-github-copilot/" >IntellijでのGithub Copilotの使い方&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/035-github-copilot-markdown/" >GitHub CopilotをMarkdownで有効にする方法&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/042-github-copilot/" >Github CopilotをChat toolを使って便利に使う方法&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/043-codex/" >Codex CLIの使い方まとめ&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>ITIL v4 可用性管理について解説</title><link>https://bossagyu.com/blog/016-itilv4-availability-management/</link><pubDate>Tue, 30 Jan 2024 20:34:58 +0900</pubDate><guid>https://bossagyu.com/blog/016-itilv4-availability-management/</guid><description>&lt;h2 id="概要">概要&lt;/h2>
&lt;p>この記事では、ITIL v4の可用性管理について説明します。
また、理解した内容をもとに自分の経験を当てはあめ、可用性管理のプロセスについて説明します。&lt;/p>
&lt;h2 id="可用性管理とは">可用性管理とは&lt;/h2>
&lt;p>可用性管理とは、サービスの可用性を確保するための活動のことです。
可用性管理の目的は、サービスが顧客とユーザーのニーズを満たすために合意されたレベルの可用性を確実に提供することです。&lt;/p>
&lt;h2 id="可用性管理のプロセス">可用性管理のプロセス&lt;/h2>
&lt;p>可用性管理のプロセスは以下２つがあります。&lt;/p>
&lt;ul>
&lt;li>サービス可用性制御の確立&lt;/li>
&lt;li>サービス可用性の分析と改善&lt;/li>
&lt;/ul>
&lt;h3 id="サービス可用性制御の確立">サービス可用性制御の確立&lt;/h3>
&lt;p>サービス可用性制御の確立は、サービスの可用性を確保するための活動のことです。
以下の流れで実現されます。&lt;/p>
&lt;ol>
&lt;li>サービス可用性要件の特定&lt;/li>
&lt;li>サービス可用性要件の合意&lt;/li>
&lt;li>可用性測定要件の決定&lt;/li>
&lt;li>可用性メトリクスと報告の設計&lt;/li>
&lt;/ol>
&lt;p>上記プロセスに対して自分の経験を当てはめると以下のように理解しました。&lt;/p>
&lt;ul>
&lt;li>サービス可用性要件の特定
&lt;ul>
&lt;li>どのような利用者がおり、サービスが停止したときの事業リスクなどの影響を特定する。&lt;/li>
&lt;li>自分のサービスは社内のPFなので、各サービスがPFとして利用しておりそれぞれどのような影響があるかを特定しました。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>サービス可用性要件の合意
&lt;ul>
&lt;li>SLAの形でサービスの可用性（稼働率99%）などを合意する&lt;/li>
&lt;li>稼働率では停止判定の基準や、例外事由なども明らかにしました。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>可用性測定要件の決定
&lt;ul>
&lt;li>測定要件については可用性要件合意の段階で何をサービス停止とするかを決めているので、測定要件は特になし。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>可用性メトリクスと報告の設計
&lt;ul>
&lt;li>基本的には「ダウンタイム/稼働時間」で設計&lt;/li>
&lt;li>報告については、可用性メトリクスを可視化するためのダッシュボードを作成しました。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h3 id="サービス可用性の分析と改善">サービス可用性の分析と改善&lt;/h3>
&lt;p>サービス可用性の分析と改善は、その名の通り可用性の分析と改善を行うプロセスです。
以下の流れで実現されます。&lt;/p>
&lt;ol>
&lt;li>サービス可用性の分析&lt;/li>
&lt;li>サービス可用性の報告&lt;/li>
&lt;li>サービス可用性の計画と設計&lt;/li>
&lt;/ol>
&lt;p>上記プロセスに対して自分の経験を当てはめると以下のように理解しました。&lt;/p>
&lt;ul>
&lt;li>サービス可用性の分析
&lt;ul>
&lt;li>サービス可用性が達成されていることを確認し、集計します。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>サービス可用性の報告
&lt;ul>
&lt;li>可用性をダッシュボードに反映、誰でも見られる状態にします。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>サービス可用性の計画と設計
&lt;ul>
&lt;li>可用性を割るような障害が発生した場合、再発防止のための計画を立てました。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h2 id="参考">参考&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://www.axelos.com/resource-hub/practice/availability-management-itil-4-practice-guide" target="_blank" rel="noopener"
>Availability management: ITIL 4 Practice Guide&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="関連記事">関連記事&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/018-itilv4-business-analysis/" >ITIL v4 事業分析について解説&lt;/a>（関連ITIL v4記事）&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/020-itilv4-capacity-and-performance-management/" >ITIL v4 キャパシティ及びパフォーマンス管理について解説&lt;/a>（関連ITIL v4記事）&lt;/li>
&lt;/ul></description></item><item><title>S3のオブジェクトの存在確認をする方法【Python boto3】</title><link>https://bossagyu.com/blog/015-s3-object-check/</link><pubDate>Sat, 27 Jan 2024 21:41:37 +0900</pubDate><guid>https://bossagyu.com/blog/015-s3-object-check/</guid><description>&lt;h2 id="概要">概要&lt;/h2>
&lt;p>PythonでS3のオブジェクトが存在するかどうかを確認する方法を説明します。
boto3には&lt;code>resource&lt;/code>と&lt;code>client&lt;/code>の2つのAPIがあり、それぞれの方法を紹介します。&lt;/p>
&lt;p>実際にLINE Botの開発で、ユーザーごとの設定ファイルがS3に存在するかチェックする必要があり、この方法を使いました。&lt;/p>
&lt;h2 id="resource-vs-client-どちらを使うべきか">resource vs client どちらを使うべきか&lt;/h2>
&lt;p>boto3には2つのAPIレベルがあります。&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>API&lt;/th>
&lt;th>特徴&lt;/th>
&lt;th>適したユースケース&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>resource&lt;/code>&lt;/td>
&lt;td>高レベルAPI、オブジェクト指向&lt;/td>
&lt;td>シンプルな操作、可読性重視&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>client&lt;/code>&lt;/td>
&lt;td>低レベルAPI、AWS APIに近い&lt;/td>
&lt;td>細かい制御、パフォーマンス重視&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>結論&lt;/strong>: 単純な存在確認なら&lt;code>client&lt;/code>の&lt;code>head_object&lt;/code>がおすすめです。理由は以下の通りです。&lt;/p>
&lt;ul>
&lt;li>&lt;code>head_object&lt;/code>はオブジェクトのメタデータのみを取得するため軽量&lt;/li>
&lt;li>&lt;code>resource&lt;/code>の&lt;code>load()&lt;/code>も内部的には&lt;code>head_object&lt;/code>を呼んでいる&lt;/li>
&lt;li>&lt;code>client&lt;/code>の方がAWS APIに近く、挙動が明確&lt;/li>
&lt;/ul>
&lt;h2 id="boto3clientを利用する方法推奨">boto3.clientを利用する方法（推奨）&lt;/h2>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">boto3&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">botocore.exceptions&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">ClientError&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">check_s3_object_exists&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">bucket_name&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">object_key&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">bool&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;S3オブジェクトの存在確認&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">s3&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">boto3&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">client&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;s3&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">try&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">s3&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">head_object&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Bucket&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">bucket_name&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Key&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">object_key&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">True&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">except&lt;/span> &lt;span class="n">ClientError&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="n">e&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">error_code&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">e&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">response&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;Error&amp;#39;&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="s1">&amp;#39;Code&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">error_code&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s1">&amp;#39;404&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">False&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 404以外のエラー（権限不足など）は再raise&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">raise&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 使用例&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="n">check_s3_object_exists&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;my-bucket&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;path/to/file.json&amp;#39;&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;オブジェクトが存在します&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">else&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;オブジェクトが存在しません&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="ポイント">ポイント&lt;/h3>
&lt;ul>
&lt;li>&lt;code>head_object&lt;/code>はオブジェクトの内容をダウンロードせず、メタデータのみを取得&lt;/li>
&lt;li>404エラー以外（403: アクセス拒否など）は別のエラーなので再raiseする&lt;/li>
&lt;li>関数化しておくとテストしやすい&lt;/li>
&lt;/ul>
&lt;h2 id="boto3resourceを利用する方法">boto3.resourceを利用する方法&lt;/h2>
&lt;p>オブジェクト指向的に書きたい場合はこちらも使えます。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">boto3&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">botocore.exceptions&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">ClientError&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">check_s3_object_exists_resource&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">bucket_name&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">object_key&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">bool&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;S3オブジェクトの存在確認（resource版）&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">s3&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">boto3&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">resource&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;s3&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">try&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">s3&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">Object&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">bucket_name&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">object_key&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">load&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">True&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">except&lt;/span> &lt;span class="n">ClientError&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="n">e&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">error_code&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">e&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">response&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;Error&amp;#39;&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="s1">&amp;#39;Code&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">error_code&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s1">&amp;#39;404&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">False&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">raise&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="よくあるハマりポイント">よくあるハマりポイント&lt;/h2>
&lt;h3 id="1-権限不足を404と間違える">1. 権限不足を404と間違える&lt;/h3>
&lt;p>S3バケットへのアクセス権限がない場合、403エラーが返ります。
404だけをキャッチして「存在しない」と判断すると、権限の問題を見逃します。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;span class="lnt">9
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># NG: 404以外のエラーを握りつぶしている&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">except&lt;/span> &lt;span class="n">ClientError&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">False&lt;/span> &lt;span class="c1"># 権限エラーも「存在しない」扱いになる&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># OK: 404以外は再raise&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">except&lt;/span> &lt;span class="n">ClientError&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="n">e&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">e&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">response&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;Error&amp;#39;&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="s1">&amp;#39;Code&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s1">&amp;#39;404&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">False&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">raise&lt;/span> &lt;span class="c1"># 権限エラーなどは呼び出し元で処理&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="2-list_objectsを使う方法は非効率">2. list_objectsを使う方法は非効率&lt;/h3>
&lt;p>&lt;code>list_objects&lt;/code>でプレフィックス検索する方法もありますが、オブジェクト数が多いと遅くなります。
単一オブジェクトの存在確認には&lt;code>head_object&lt;/code>を使いましょう。&lt;/p>
&lt;h2 id="まとめ">まとめ&lt;/h2>
&lt;ul>
&lt;li>S3オブジェクトの存在確認には&lt;code>head_object&lt;/code>を使う&lt;/li>
&lt;li>404エラー以外は権限不足などの可能性があるので適切に処理する&lt;/li>
&lt;li>&lt;code>resource&lt;/code>と&lt;code>client&lt;/code>どちらでも可能だが、&lt;code>client&lt;/code>の方がシンプル&lt;/li>
&lt;/ul>
&lt;h2 id="参考">参考&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/s3/client/head_object.html" target="_blank" rel="noopener"
>boto3 S3 head_object&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://stackoverflow.com/questions/33842944/check-if-a-key-exists-in-a-bucket-in-s3-using-boto3" target="_blank" rel="noopener"
>Stack Overflow: check if a file exists in s3 bucket using boto3&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="関連記事">関連記事&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/046-clean-bot-technical/" >AWS Lambda + LINE Botで掃除リマインダーを作る&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/004-python-setup/" >Pyenvとvenvを用いたローカル環境のセットアップ方法&lt;/a>（Python環境構築）&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/032-python-uv/" >MacでUVを用いてPythonの開発環境を構築する&lt;/a>（モダンなPython環境構築）&lt;/li>
&lt;/ul></description></item><item><title>AWS API GatewayとLambdaを連携させる方法</title><link>https://bossagyu.com/blog/014-aws-apigateway-lambda/</link><pubDate>Sat, 13 Jan 2024 18:06:52 +0900</pubDate><guid>https://bossagyu.com/blog/014-aws-apigateway-lambda/</guid><description>&lt;h2 id="概要">概要&lt;/h2>
&lt;p>AWS API GatewayとLambdaを連携させることで、API GatewayからLambdaを呼び出すことができます。
本記事AWS API GatewayとLambdaを連携させる方法を紹介します。&lt;/p>
&lt;h2 id="前提条件">前提条件&lt;/h2>
&lt;p>Lambda関数については作成されていることを前提としています。
作成していない場合は、下記記事を参考に作成してください。&lt;/p>
&lt;ul>
&lt;li>&lt;a class="link" href="https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/getting-started.html" target="_blank" rel="noopener"
>AWS Lambdaを作成する方法&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="どの形式でapi-gatewayとlambdaを連携させるかを考える">どの形式でAPI GatewayとLambdaを連携させるかを考える&lt;/h2>
&lt;p>API GatewayとLambdaを連携させるに当たってどのような方式で連携させるか以下の2点について考える必要があります。&lt;/p>
&lt;ol>
&lt;li>API Gatewayのリクエスト形式について&lt;/li>
&lt;li>プロキシ統合が非プロキシ統合か&lt;/li>
&lt;/ol>
&lt;h3 id="api-gatewayのリクエスト形式について">API Gatewayのリクエスト形式について&lt;/h3>
&lt;p>以下の形式から選択できます。&lt;/p>
&lt;ul>
&lt;li>REST API&lt;/li>
&lt;li>HTTP API&lt;/li>
&lt;li>WebSocket API&lt;/li>
&lt;/ul>
&lt;p>この内、REST APIの形式で利用する場合は、REST APIかHTTP APIのどちらかを選択することになります。&lt;br>
REST APIの方が機能が多いですが、HTTP APIと比べコストがかかります。&lt;br>
特に複雑なことをしないのであればHTTP APIを選択するのが良いと思います。&lt;/p>
&lt;p>詳細な比較内容については&lt;a class="link" href="https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-vs-rest.html" target="_blank" rel="noopener"
>公式ドキュメント&lt;/a>で公開されているのでそちらを参照してください。&lt;/p>
&lt;h2 id="プロキシ統合が非プロキシ統合か">プロキシ統合が非プロキシ統合か&lt;/h2>
&lt;p>プロキシ統合を利用することで、Lambdaから返される値のフォーマットが固定化されます。
基本的にはプロキシ統合を利用することをおすすめします。&lt;/p>
&lt;ul>
&lt;li>&lt;a class="link" href="https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html" target="_blank" rel="noopener"
>API Gateway で Lambda プロキシ統合を設定する&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="設定">設定&lt;/h2>
&lt;p>Lambda関数を作成したら、トリガー追加を選択します。&lt;br>
&lt;img src="https://bossagyu.com/blog/014-aws-apigateway-lambda/img-014-001.png"
width="3298"
height="980"
srcset="https://bossagyu.com/blog/014-aws-apigateway-lambda/img-014-001_hu7a602e3d547dfd28cd38c53d38cddce0_223431_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/014-aws-apigateway-lambda/img-014-001_hu7a602e3d547dfd28cd38c53d38cddce0_223431_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Lambdaトリガー追加"
class="gallery-image"
data-flex-grow="336"
data-flex-basis="807px"
>&lt;/p>
&lt;p>API Gatewayを選択します。&lt;br>
&lt;img src="https://bossagyu.com/blog/014-aws-apigateway-lambda/img-014-002.png"
width="966"
height="522"
srcset="https://bossagyu.com/blog/014-aws-apigateway-lambda/img-014-002_hu446ae81980eb6a0d259b3af0f9d88117_59141_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/014-aws-apigateway-lambda/img-014-002_hu446ae81980eb6a0d259b3af0f9d88117_59141_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="API Gatewayを選択"
class="gallery-image"
data-flex-grow="185"
data-flex-basis="444px"
>&lt;/p>
&lt;p>トリガーを追加の画面で以下のように設定を行います。
&lt;img src="https://bossagyu.com/blog/014-aws-apigateway-lambda/img-014-003.png"
width="892"
height="1203"
srcset="https://bossagyu.com/blog/014-aws-apigateway-lambda/img-014-003_hu45d67fc282f3323f6fa435777af50650_166604_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/014-aws-apigateway-lambda/img-014-003_hu45d67fc282f3323f6fa435777af50650_166604_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="API Gatewayの設定"
class="gallery-image"
data-flex-grow="74"
data-flex-basis="177px"
>&lt;/p>
&lt;p>設定がうまくいくと以下のような画面になります。&lt;br>
&lt;img src="https://bossagyu.com/blog/014-aws-apigateway-lambda/img-014-004.png"
width="1396"
height="969"
srcset="https://bossagyu.com/blog/014-aws-apigateway-lambda/img-014-004_hu4410e1ba85bd0e88e16bb309c05ab865_175168_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/014-aws-apigateway-lambda/img-014-004_hu4410e1ba85bd0e88e16bb309c05ab865_175168_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="API Gatewayの設定完了"
class="gallery-image"
data-flex-grow="144"
data-flex-basis="345px"
>&lt;/p>
&lt;p>&lt;code>API endpoint&lt;/code> に記載のエンドポイントにcurlなどでアクセスするとLambda関数が実行されます。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">$ curl https://xxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/default/apigateway-get-sample
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;Hello from Lambda!&amp;#34;&lt;/span>%
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="まとめ">まとめ&lt;/h2>
&lt;p>本記事ではAWS API GatewayとLambdaを連携させる方法を紹介しました。
API Gatewayと連携させることで外部から任意のタイミングでLambda関数を呼び出すことができるようになります。&lt;/p>
&lt;h2 id="関連記事">関連記事&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/006-intellij-lamda-setup/" >IntellijでAWS Toolkitを使ってLambdaを効率よく開発する&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/008-aws-eventbrdge/" >AWS EventBridgeを用いてLambdaを定期実行する方法&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/046-clean-bot-technical/" >AWS Lambda + LINE Botで掃除リマインダーを作る&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>プロダクト戦略の作り方</title><link>https://bossagyu.com/blog/013-good-strategy-bad-strategy/</link><pubDate>Mon, 08 Jan 2024 21:55:15 +0900</pubDate><guid>https://bossagyu.com/blog/013-good-strategy-bad-strategy/</guid><description>&lt;h2 id="概要">概要&lt;/h2>
&lt;p>良いプロダクト戦略の作り方について、「良い戦略、悪い戦略」という書籍をベースにまとめました。&lt;/p>
&lt;h2 id="背景">背景&lt;/h2>
&lt;p>業務でプロダクトオーナーとして、前プロダクトオーナーからプロダクトを引き継ぎました。&lt;br>
引き継いだプロダクトにはプロダクト戦略がなく、プロダクトの方向性が定まっていない状態であったため、今後プロダクトをどうするかを含めて、プロダクト戦略を作成しました。&lt;/p>
&lt;p>プロダクト戦略を作成するにあたって、そもそも「戦略」というものが人によって様々な意味に解釈されており、なんでも「戦略」という言葉を使ってしまう傾向があると常々感じていました。
そこで、プロダクト戦略を作成するにあたって、どのような戦略を作成すれば良いのかを知るために、「良い戦略、悪い戦略」を読みました。&lt;/p>
&lt;h2 id="良い戦略とは">良い戦略とは&lt;/h2>
&lt;p>良い戦略とは、こちらの打つ手の効果が一気に高まるようなポイントを見つけ、そこに狙いを絞って手持ちのリソースを集中させることで、効果を最大化することができる戦略です。
戦略とは組織が前に進むためにどのようにしたら良いかを示すものである必要があります。&lt;/p>
&lt;p>良い戦略は、以下の3つの基本構造を持っています。&lt;/p>
&lt;ul>
&lt;li>診断&lt;/li>
&lt;li>基本方針&lt;/li>
&lt;li>行動&lt;/li>
&lt;/ul>
&lt;p>&lt;img src="https://bossagyu.com/blog/013-good-strategy-bad-strategy/img-013-001.png"
width="1496"
height="844"
srcset="https://bossagyu.com/blog/013-good-strategy-bad-strategy/img-013-001_hu74bc795a581a6eebaf9972b83112b6d2_147669_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/013-good-strategy-bad-strategy/img-013-001_hu74bc795a581a6eebaf9972b83112b6d2_147669_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="良い戦略の3つの基本構造"
class="gallery-image"
data-flex-grow="177"
data-flex-basis="425px"
>&lt;/p>
&lt;h3 id="診断">診断&lt;/h3>
&lt;p>診断とは、状況を診断し取り組むべき課題を見極めることです。&lt;br>
良い診断は死活的に重要な問題点をより分け、複雑に絡み合った状況を明快に整理することができます。&lt;/p>
&lt;p>戦略を立てる作業の多くは、今何が置きているのかを洗い出すことにあります。まずは情報を集めることが何よりも大切です。&lt;br>
本書ではコンサルが提案するようなフレームに割り当てるだけではまともな戦略はできない。とこき下ろしていますが情報をしっかりと集めた上でフレームに割り当て整理することを診断の段階で行うことは有意義だと私は考えています。&lt;/p>
&lt;p>実際に私がプロダクト戦略を作成するにあたって、SWOT分析でプロダクトの置かれる状況を、インパクトマッピングを用いて現状の施策が一体誰にどのような影響を与えるのかの整理を行いました。&lt;br>
それぞれのやり方については、以下の記事や書籍を参照してください。&lt;/p>
&lt;ul>
&lt;li>&lt;a class="link" href="https://www.innovation.co.jp/urumo/swot/" target="_blank" rel="noopener"
>SWOT分析&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://amzn.asia/d/0FygsZ4" target="_blank" rel="noopener"
>インパクトマッピング&lt;/a>&lt;/li>
&lt;/ul>
&lt;h3 id="基本方針">基本方針&lt;/h3>
&lt;p>基本方針とは、診断で見つかった課題にどう取り組むか、大きな方向性と総合的な方針を示すことです。&lt;br>
良い基本方針とは、目標でもビジョンではない、何曲に立ち向かう方法を固め、他の選択肢を排除することが基本方針である。と本書では述べられていました。&lt;br>
決定的な一点に努力を集中させることによって、大きな効果を上げることができます。&lt;/p>
&lt;p>このため良い戦略の中には、その戦略に従うことによって何に対してリソースを割くのかがはっきりと分かるようになっていることが大切です。&lt;/p>
&lt;p>私が戦略を立てた際には、インパクトマッピングで整理した内容をベースに、プロダクトの置かれた現状やビジョンをもとに勘案し、プロダクトの方向性を決めました。&lt;br>
リソースを選択する意味では、どのターゲットのセグメントに対して、どのような価値を提供するのかを明確にすることを意識しました。&lt;/p>
&lt;h3 id="行動">行動&lt;/h3>
&lt;p>基本方針を実行するために設計された一貫性のある一連の行動のことです。
戦略が存在することですべての行動をコーディネートして方針を実行することができます。&lt;/p>
&lt;p>このため、良い戦略は、行動を実行するための指針が含まれていることが大切となります。&lt;/p>
&lt;h3 id="私が立てた戦略">私が立てた戦略&lt;/h3>
&lt;p>上記をベースに私が立てた戦略は以下となりました。&lt;br>
会社で立てた戦略ですので、一部ぼかして書いています。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">xx機能の利用者の新規利用コストを低減する
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>かなりシンプルなものになりましたが、以下のように良い戦略の3つの基本構造を満たしていると考えています。&lt;/p>
&lt;ul>
&lt;li>診断
&lt;ul>
&lt;li>プロダクトの状況、課題をベースに考えたときにxxの新規利用者を増やすことが会社の利益につながると考えました。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>基本方針
&lt;ul>
&lt;li>新規利用者を確保するために導入コストを下げるという基本方針を定めました。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>行動
&lt;ul>
&lt;li>基本方針を実現するためにいくつかのアプローチを用意し優先順位をつけました。&lt;/li>
&lt;li>ここについては行動を連想できるようなワードを戦略に含められればと考えましたが上記対応としました。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h2 id="悪い戦略とは">悪い戦略とは&lt;/h2>
&lt;p>最後に陥りがちな悪い戦略のパターンについて書いておきます。&lt;/p>
&lt;p>悪い戦略の特徴&lt;/p>
&lt;ul>
&lt;li>空疎である
&lt;ul>
&lt;li>わかり切っていることを専門用語や業界用語で煙に巻くような内容&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>重大な問題に取り組まない
&lt;ul>
&lt;li>本来困難な課題を克服し、障害を乗り越えるためのものが戦略である。&lt;/li>
&lt;li>達成容易性のみを考えた戦略は悪い戦略である。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>目標と戦略と取り違えている
&lt;ul>
&lt;li>売りあげ10%向上など。それはただの目標である。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>間違った戦略目標を掲げている
&lt;ul>
&lt;li>十分な周辺・原因の調査がなく戦略を掲げている状態。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h2 id="まとめ">まとめ&lt;/h2>
&lt;p>今回は、良いプロダクト戦略の作り方について、「良い戦略、悪い戦略」という書籍をベースにまとめました。
一回でいきなり良い戦略は立てられないと考えています。ただ、戦略のない環境はただ闇雲に走っているだけなのでそれが成功したのか失敗したのかすら判断することができない最低の状態です。&lt;br>
最初は下手な戦略でも良いので、良い戦略とは何かを意識しつつ常に周りの状況を観察しながら、戦略をアップデートしプロダクトと組織の方向性を定めていくことが大切だと思います。&lt;/p></description></item><item><title>Twitter Social Cardの設定方法</title><link>https://bossagyu.com/blog/012-social-card/</link><pubDate>Sat, 06 Jan 2024 21:45:12 +0900</pubDate><guid>https://bossagyu.com/blog/012-social-card/</guid><description>&lt;h2 id="概要">概要&lt;/h2>
&lt;p>この記事では、Hugoで作ったブログにTwitter Social Cardを設定する方法を説明します。&lt;/p>
&lt;h2 id="twitter-social-cardとは">Twitter Social Cardとは&lt;/h2>
&lt;p>Twitter Social Cardとは、Twitterで記事をシェアした際に表示される画像のことです。
以下のような画像がTwitter Social Cardです。&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/blog/012-social-card/img-012-001.png"
width="602"
height="220"
srcset="https://bossagyu.com/blog/012-social-card/img-012-001_hua2e3d73bb1829d9bc1818e9b476a1657_53764_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/012-social-card/img-012-001_hua2e3d73bb1829d9bc1818e9b476a1657_53764_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Twitter Social Card"
class="gallery-image"
data-flex-grow="273"
data-flex-basis="656px"
>&lt;/p>
&lt;p>Twitter Social Cardは、以下の種類があります。&lt;/p>
&lt;ul>
&lt;li>Summary Card&lt;/li>
&lt;li>Summary Card with Large Image&lt;/li>
&lt;li>App Card&lt;/li>
&lt;li>Player Card&lt;/li>
&lt;/ul>
&lt;p>この中でもブログのシェアを行う場合は、Summary Card か Summary Card with Large Image を利用することが多いです。&lt;/p>
&lt;p>そのれぞれのカードがどのようなものかについてはTwitterの&lt;a class="link" href="https://developer.twitter.com/en/docs/twitter-for-websites/cards/overview/abouts-cards" target="_blank" rel="noopener"
>公式ドキュメント&lt;/a>を参照してください。&lt;/p>
&lt;h2 id="twitter-social-cardの設定方法">Twitter Social Cardの設定方法&lt;/h2>
&lt;p>Twitter Social Cardの設定方法は、以下の2つの方法があります。&lt;/p>
&lt;ul>
&lt;li>テーマによる設定&lt;/li>
&lt;li>テーマに依存しない設定&lt;/li>
&lt;/ul>
&lt;h3 id="テーマによる設定">テーマによる設定&lt;/h3>
&lt;p>テーマによっては、Twitter Social Cardの設定を行うことができます。&lt;/p>
&lt;p>今回は私が採用している&lt;a class="link" href="https://themes.gohugo.io/hugo-theme-stack/" target="_blank" rel="noopener"
>Stack&lt;/a>を例に説明します。&lt;/p>
&lt;p>Stackでは、&lt;code>config.toml&lt;/code> 対して以下のような設定を行うことでTwitter Social Cardの設定が可能です。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">opengraph&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">twitter&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">site&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">card&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;summary&amp;#34;&lt;/span> &lt;span class="c"># summary or summary_large_image&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">defaultImage&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">opengraph&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">enabled&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="kc">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">local&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="kc">false&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">src&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;/images/share.webp&amp;#34;&lt;/span> &lt;span class="c"># デフォルトで設定したいimageのパス&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="テーマに依存しない設定">テーマに依存しない設定&lt;/h3>
&lt;p>テーマによっては、Twitter Social Cardの設定を行うことができません。
自前で実装して、設定を行う必要があります。&lt;/p>
&lt;p>Hugoの公式で&lt;a class="link" href="https://github.com/gohugoio/hugo/blob/master/tpl/tplimpl/embedded/templates/twitter_cards.html" target="_blank" rel="noopener"
>実装のテンプレート&lt;/a>が公開されていますので、それを利用することで容易に実装することが可能です。&lt;/p>
&lt;h2 id="うまくいかない場合">うまくいかない場合&lt;/h2>
&lt;p>うまくいかない場合はうまく設定が反映されていない、megaタグが正しく設定されていない可能性があります。
Twitterから提供されている&lt;a class="link" href="https://cards-dev.twitter.com/validator" target="_blank" rel="noopener"
>デバッグツール&lt;/a>を利用して、設定が正しく反映されているか確認してみましょう。&lt;/p>
&lt;h2 id="まとめ">まとめ&lt;/h2>
&lt;p>この記事では、Hugoで作ったブログにTwitter Social Cardを設定する方法を説明しました。
Social Cardを設定することで、Twitterで記事をシェアした際に、より多くの人に記事を読んでもらうことができますのでぜひ設定しましょう。&lt;/p>
&lt;h2 id="関連記事">関連記事&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/001-hugo-netlify-build/" >Hugo + Netlify + Githubでブログを公開する&lt;/a> - ブログの初期構築&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/007-google-search-console/" >Google Search Consoleでブログを検索対象にする方法&lt;/a> - SEO対策&lt;/li>
&lt;/ul></description></item><item><title>ChatGPTを利用してHugoで作ったブログを多言語対応する方法</title><link>https://bossagyu.com/blog/011-hugo-multilingul-support/</link><pubDate>Sun, 31 Dec 2023 20:46:36 +0900</pubDate><guid>https://bossagyu.com/blog/011-hugo-multilingul-support/</guid><description>&lt;h2 id="概要">概要&lt;/h2>
&lt;p>この記事では、ChatGPTを利用してHugoで作ったブログを多言語対応する方法を説明します。&lt;/p>
&lt;h2 id="ghatgptを利用して記事を英語化する方法">GhatGPTを利用して記事を英語化する方法&lt;/h2>
&lt;p>Markdownで書かれた記事を&lt;a class="link" href="https://chat.openai.com/" target="_blank" rel="noopener"
>ChatGPT&lt;/a>に英語化させます。
英語化を実施するにあたって、なるべき体裁を崩さないようにするために、以下のようなプロンプトをChatGPTへ入力します。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">マークダウンを体裁を崩さずに英語にしてください。
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">マークダウン以外の余分な出力は行わないでください。
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">英語化された内容をそのままコピーできる形で出力してください。
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>その後、日本語で書いた記事をそのまま貼り付けると、英語化されたMarkdownが出力されます。
出力結果の左下のコピーボタンから出力結果をコピーすれば、英語化は完了です。&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/blog/011-hugo-multilingul-support/img-011-001.png"
width="1542"
height="516"
srcset="https://bossagyu.com/blog/011-hugo-multilingul-support/img-011-001_hu712801eb1dd59e23992097ade27112d6_94833_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/011-hugo-multilingul-support/img-011-001_hu712801eb1dd59e23992097ade27112d6_94833_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="ChatGPTの画面"
class="gallery-image"
data-flex-grow="298"
data-flex-basis="717px"
>&lt;/p>
&lt;p>また、ChatGPTを利用する際はGPT3.5ではなく、課金をしてGPT4を利用することを強くおすすめします。
GPT4は月々お金がかかりますが、GPT3.5と比べて圧倒的に正しい回答を返す確率が高いので、英語化以外の用途にも使えます。&lt;/p>
&lt;h2 id="hugoで多言語対応する方法">Hugoで多言語対応する方法&lt;/h2>
&lt;p>Hugoで多言語化を行うための設定方法を記載します。&lt;/p>
&lt;h3 id="設定ファイルの作成">設定ファイルの作成&lt;/h3>
&lt;p>config.tomlに以下のような設定を追加します。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># デフォルトの言語を設定、設定しない場合英語がデフォルト判定されます。&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">defaultContentLanguage&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;jp&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">languages&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c"># 言語ごとに設定を変更します&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">[&lt;/span>&lt;span class="nx">languages&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">jp&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">title&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;Bossagyu Blog&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">languageName&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;ja-jp 🇯🇵&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">LanguageCode&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;ja-jp&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">contentDir&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;content&amp;#34;&lt;/span> &lt;span class="c"># 日本語ブログ記事を格納するディレクトリ&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">[&lt;/span>&lt;span class="nx">languages&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">jp&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">params&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">[&lt;/span>&lt;span class="nx">languages&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">en&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">title&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;Bossagyu Blog&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">languageName&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;en-US 🇺🇸&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">LanguageCode&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;en-US&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">contentDir&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;content.en&amp;#34;&lt;/span> &lt;span class="c"># 英語ブログ記事を格納するディレクトリ&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">[&lt;/span>&lt;span class="nx">languages&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">en&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">params&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>上記のような設定を行うことで、&lt;code>content&lt;/code>ディレクトリに日本語の記事を、&lt;code>content.en&lt;/code>ディレクトリに英語の記事を書くことで多言語対応が可能となります。&lt;/p>
&lt;p>最終的なディレクトリ構成は以下のような形になります。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">project/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── content/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├── index.md
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ └── blog/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├── index.md
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ └── article1.md
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">└── content.en/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├── index.md
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> └── blog/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├── index.md
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> └── article1.en.md
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>また英語化したディレクトリの中には &lt;code>記事名.en.md&lt;/code> の拡張子とすることで、デフォルト言語に対する英語の記事として認識され、記事に言語変換用のアイコンが出力されるようになります。
記事についてはGhatGPTを用いて英語化したものをコピー&amp;amp;ペーストするだけでOKです。&lt;/p>
&lt;h2 id="まとめ">まとめ&lt;/h2>
&lt;p>この記事では、ChatGPTを利用してHugoで作ったブログを多言語対応する方法を説明しました。
ChatGPTを利用することで、英語化を簡単に行うことができます。
また、多言語対応することで記事を日本以外の国にもリーチでき、より多くの人に記事を読んでもらえます。&lt;/p>
&lt;p>ローコストで多言語対応できるのでぜひやってみましょう。&lt;/p>
&lt;h2 id="関連記事">関連記事&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/001-hugo-netlify-build/" >Hugo + Netlify + Githubでブログを公開する&lt;/a> - ブログの初期構築&lt;/li>
&lt;/ul></description></item><item><title>Lighthouseの使い方の紹介</title><link>https://bossagyu.com/blog/009-light-house/</link><pubDate>Fri, 22 Dec 2023 23:08:00 +0900</pubDate><guid>https://bossagyu.com/blog/009-light-house/</guid><description>&lt;h2 id="概要">概要&lt;/h2>
&lt;p>Lighthouseを用いて、ブログのパフォーマンスを計測する方法を解説します。&lt;/p>
&lt;h2 id="lighthouseとは">Lighthouseとは&lt;/h2>
&lt;p>LighthouseはGoogleが提供している、Webサイトのパフォーマンスを計測するツールです。
Google Chromeの拡張機能として提供されており、プラグインをインストールすることで利用することができます。&lt;/p>
&lt;h2 id="lighthouseのインストール">Lighthouseのインストール&lt;/h2>
&lt;p>Lighthouseをchromeウェブストアからインストールします。&lt;br>
&lt;img src="https://bossagyu.com/blog/009-light-house/img-009-001.png"
width="3090"
height="528"
srcset="https://bossagyu.com/blog/009-light-house/img-009-001_huebb24c65f9e0514e53794d84dae1d193_147708_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/009-light-house/img-009-001_huebb24c65f9e0514e53794d84dae1d193_147708_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Lighthouse chromeストア"
class="gallery-image"
data-flex-grow="585"
data-flex-basis="1404px"
>&lt;/p>
&lt;p>分析したいサイトを開き、Lighthouseのアイコンをクリックします。&lt;br>
&lt;img src="https://bossagyu.com/blog/009-light-house/img-009-002.png"
width="976"
height="184"
srcset="https://bossagyu.com/blog/009-light-house/img-009-002_hu79a554b91c1fd5ce9f2d244e8889fd8e_12110_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/009-light-house/img-009-002_hu79a554b91c1fd5ce9f2d244e8889fd8e_12110_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Lighthouse アイコン"
class="gallery-image"
data-flex-grow="530"
data-flex-basis="1273px"
>&lt;/p>
&lt;p>Generate reportをクリックすると、分析が始まります。&lt;br>
今回は私の&lt;a class="link" href="https://bossagyu.com/blog/001-hugo-netlify-build/" target="_blank" rel="noopener"
>ブログのページ&lt;/a>で実行しました。&lt;br>
&lt;img src="https://bossagyu.com/blog/009-light-house/img-009-003.png"
width="546"
height="1048"
srcset="https://bossagyu.com/blog/009-light-house/img-009-003_hu32cc9d1c5498eef6660862acfbb0c7a0_88803_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/009-light-house/img-009-003_hu32cc9d1c5498eef6660862acfbb0c7a0_88803_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Lighthouse 実行画面"
class="gallery-image"
data-flex-grow="52"
data-flex-basis="125px"
>&lt;/p>
&lt;p>実行すると、以下のような結果が表示されます。&lt;br>
実行の完了までに約1分くらい時間がかかります。&lt;br>
&lt;img src="https://bossagyu.com/blog/009-light-house/img-009-004.png"
width="1756"
height="906"
srcset="https://bossagyu.com/blog/009-light-house/img-009-004_hu5ee3cbd9121e8af4f867f831e4523042_214018_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/009-light-house/img-009-004_hu5ee3cbd9121e8af4f867f831e4523042_214018_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Lighthouse 結果"
class="gallery-image"
data-flex-grow="193"
data-flex-basis="465px"
>&lt;/p>
&lt;h2 id="結果の見方">結果の見方&lt;/h2>
&lt;h3 id="performance">Performance&lt;/h3>
&lt;p>ページの読み込み速度や画像の表示速度など、webサイトのパフォーマンスが評価される。
See calculatorのリンクをクリックすると詳細に飛べる。&lt;br>
&lt;img src="https://bossagyu.com/blog/009-light-house/img-009-005.png"
width="2236"
height="1222"
srcset="https://bossagyu.com/blog/009-light-house/img-009-005_hu09a21b450202618b8da67da25ceb5565_223078_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/009-light-house/img-009-005_hu09a21b450202618b8da67da25ceb5565_223078_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Lighthouse Performance"
class="gallery-image"
data-flex-grow="182"
data-flex-basis="439px"
>&lt;/p>
&lt;h3 id="accessibility">Accessibility&lt;/h3>
&lt;p>すべてのユーザーがコンテンツにアクセス、サイト内を効率的に移動できるかどうかを確認する。
スクロールすると、Accessibilityで指摘されている箇所が表示される。&lt;br>
&lt;img src="https://bossagyu.com/blog/009-light-house/img-009-006.png"
width="1790"
height="1238"
srcset="https://bossagyu.com/blog/009-light-house/img-009-006_hu87f9c989c987a1b95b21e6ceb46b2c76_168048_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/009-light-house/img-009-006_hu87f9c989c987a1b95b21e6ceb46b2c76_168048_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Lighthouse Accessibility"
class="gallery-image"
data-flex-grow="144"
data-flex-basis="347px"
>&lt;/p>
&lt;p>コードスニペットを貼っているところの色のコントラストが弱いことと、リンクに説明がないことを指摘されています。&lt;/p>
&lt;p>ただ、指摘されている内容は自分の記述ではなく、テンプレートに依存している部分なので、これを直そうと思うとHugoのテンプレートをオーバーライドする必要がありますね。。&lt;/p>
&lt;h3 id="best-practices">Best Practices&lt;/h3>
&lt;p>ウェブページの健全性についてテストを行います。
検証項目については結果から閲覧できます。
&lt;img src="https://bossagyu.com/blog/009-light-house/img-009-007.png"
width="962"
height="1070"
srcset="https://bossagyu.com/blog/009-light-house/img-009-007_hub9685d9d8ccde516e384b3026e5fd960_91137_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/009-light-house/img-009-007_hub9685d9d8ccde516e384b3026e5fd960_91137_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Lighthouse Best Practices"
class="gallery-image"
data-flex-grow="89"
data-flex-basis="215px"
>&lt;/p>
&lt;h3 id="seo">SEO&lt;/h3>
&lt;p>ページが検索エンジンの結果ランキング向けに最適化されているかを確認できます。
&lt;img src="https://bossagyu.com/blog/009-light-house/img-009-008.png"
width="957"
height="1215"
srcset="https://bossagyu.com/blog/009-light-house/img-009-008_hu4f2424bc6d0c40fadc50e3b571a98160_118994_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/009-light-house/img-009-008_hu4f2424bc6d0c40fadc50e3b571a98160_118994_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Lighthouse SEO"
class="gallery-image"
data-flex-grow="78"
data-flex-basis="189px"
>&lt;/p>
&lt;h3 id="progressive-web-app">Progressive Web App&lt;/h3>
&lt;p>スマートフォン上のウェブページの読み込み速度を高速化できているか、PWAに最適化できているかを確認できます。
今回はチェックしてません。&lt;/p>
&lt;h2 id="まとめ">まとめ&lt;/h2>
&lt;p>Lighthouseを用いて、ブログのパフォーマンスを計測する方法を解説しました。
特にSEOについては、Googleの検索結果に表示されるかどうかに影響するので、しっかりと対応しておきましょう。&lt;/p>
&lt;h2 id="関連記事">関連記事&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/001-hugo-netlify-build/" >Hugo + Netlify + Githubでブログを公開する&lt;/a> - ブログの初期構築&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/003-google-analytics/" >HugoでGoogle Analyticsの設定をする方法&lt;/a> - アクセス解析&lt;/li>
&lt;/ul></description></item><item><title>AWS EventBridgeを用いてLambdaを定期実行する方法</title><link>https://bossagyu.com/blog/008-aws-eventbrdge/</link><pubDate>Thu, 21 Dec 2023 23:03:13 +0900</pubDate><guid>https://bossagyu.com/blog/008-aws-eventbrdge/</guid><description>&lt;h2 id="概要">概要&lt;/h2>
&lt;p>AWS EventBridgeを用いてLambdaを定期実行する方法を解説します。&lt;/p>
&lt;p>私はこの仕組みを使って、LINE Botに定期的にリマインド通知を送る機能を実装しました。
たとえば「毎朝7時に掃除のリマインドを送る」といった処理を、サーバーを用意せずに実現できます。&lt;/p>
&lt;h2 id="aws-eventbridgeとは">AWS EventBridgeとは&lt;/h2>
&lt;p>AWS EventBridgeは、AWSのサービス間でイベントを受け渡すためのサービスです。
主な用途は以下の2つです。&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>用途&lt;/th>
&lt;th>説明&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>スケジュール実行&lt;/td>
&lt;td>cron式やrate式で定期的にLambdaを実行&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>イベント駆動&lt;/td>
&lt;td>S3へのファイルアップロードなどをトリガーにLambdaを実行&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>今回はスケジュール実行の方法を解説します。&lt;/p>
&lt;h2 id="実際のユースケース">実際のユースケース&lt;/h2>
&lt;p>EventBridge + Lambdaの定期実行は、以下のようなケースで役立ちます。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>リマインド通知&lt;/strong>: 毎日決まった時間にSlackやLINEに通知&lt;/li>
&lt;li>&lt;strong>バッチ処理&lt;/strong>: 毎時データを集計してDBに保存&lt;/li>
&lt;li>&lt;strong>ヘルスチェック&lt;/strong>: 5分ごとに外部APIの死活監視&lt;/li>
&lt;li>&lt;strong>クリーンアップ&lt;/strong>: 毎日深夜に古いログを削除&lt;/li>
&lt;/ul>
&lt;p>私の場合は、&lt;a class="link" href="https://bossagyu.com/blog/045-clean-bot/" >掃除リマインダーBot&lt;/a>で毎時ユーザーへの通知判定を行うために使っています。&lt;/p>
&lt;h2 id="前提">前提&lt;/h2>
&lt;p>Lambda関数についてはすでに作成されていることを前提としています。
Lambda関数の作成方法については、&lt;a class="link" href="https://aws.amazon.com/jp/lambda/getting-started/" target="_blank" rel="noopener"
>AWS Lambda 開始方法&lt;/a> を参照して作成してください。&lt;/p>
&lt;h2 id="手順">手順&lt;/h2>
&lt;h3 id="1-トリガーを追加">1. トリガーを追加&lt;/h3>
&lt;p>EventBridgeで実行する予定のLambda関数を選択し「トリガーを追加」を選択します。&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/blog/008-aws-eventbrdge/img-008-001.png"
width="1682"
height="608"
srcset="https://bossagyu.com/blog/008-aws-eventbrdge/img-008-001_hua3391a0a4e37513ed67145a092be2b5f_101439_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/008-aws-eventbrdge/img-008-001_hua3391a0a4e37513ed67145a092be2b5f_101439_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="トリガー追加画面"
class="gallery-image"
data-flex-grow="276"
data-flex-basis="663px"
>&lt;/p>
&lt;h3 id="2-eventbridgeを選択">2. EventBridgeを選択&lt;/h3>
&lt;p>トリガーから「EventBridge (CloudWatch Events)」を選択します。&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/blog/008-aws-eventbrdge/img-008-002.png"
width="930"
height="400"
srcset="https://bossagyu.com/blog/008-aws-eventbrdge/img-008-002_hu8fd7668508fa3ea65743ea0e537fb483_58214_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/008-aws-eventbrdge/img-008-002_hu8fd7668508fa3ea65743ea0e537fb483_58214_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="EventBridgeの選択画面"
class="gallery-image"
data-flex-grow="232"
data-flex-basis="558px"
>&lt;/p>
&lt;h3 id="3-スケジュールを設定">3. スケジュールを設定&lt;/h3>
&lt;p>トリガーの選択を行うと、ルールの作成画面が表示されるので設定します。&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/blog/008-aws-eventbrdge/img-008-003.png"
width="891"
height="903"
srcset="https://bossagyu.com/blog/008-aws-eventbrdge/img-008-003_hub807cbe0354fdfc130d1a185b4a890c6_114907_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/008-aws-eventbrdge/img-008-003_hub807cbe0354fdfc130d1a185b4a890c6_114907_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="トリガーの追加"
class="gallery-image"
data-flex-grow="98"
data-flex-basis="236px"
>&lt;/p>
&lt;h3 id="4-設定完了">4. 設定完了&lt;/h3>
&lt;p>設定が完了するとLambda関数のダイアグラムのトリガーにEventBridgeが追加されます。&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/blog/008-aws-eventbrdge/img-008-004.png"
width="1761"
height="976"
srcset="https://bossagyu.com/blog/008-aws-eventbrdge/img-008-004_huc419c6d31f69165530a8b0d8165aa994_183418_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/008-aws-eventbrdge/img-008-004_huc419c6d31f69165530a8b0d8165aa994_183418_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="設定完了画面"
class="gallery-image"
data-flex-grow="180"
data-flex-basis="433px"
>&lt;/p>
&lt;h2 id="cron式の書き方">cron式の書き方&lt;/h2>
&lt;p>EventBridgeのcron式は6つのフィールドで構成されます。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">cron(分 時 日 月 曜日 年)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="よく使うパターン">よく使うパターン&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>実行タイミング&lt;/th>
&lt;th>cron式&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>毎日9時（UTC）&lt;/td>
&lt;td>&lt;code>cron(0 9 * * ? *)&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>毎日9時（JST = UTC+9）&lt;/td>
&lt;td>&lt;code>cron(0 0 * * ? *)&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>毎時0分&lt;/td>
&lt;td>&lt;code>cron(0 * * * ? *)&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>5分ごと&lt;/td>
&lt;td>&lt;code>cron(0/5 * * * ? *)&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>平日のみ毎朝9時（UTC）&lt;/td>
&lt;td>&lt;code>cron(0 9 ? * MON-FRI *)&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>毎月1日の0時（UTC）&lt;/td>
&lt;td>&lt;code>cron(0 0 1 * ? *)&lt;/code>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="注意-タイムゾーンはutc">注意: タイムゾーンはUTC&lt;/h3>
&lt;p>EventBridgeのcron式は&lt;strong>UTC基準&lt;/strong>です。日本時間（JST）で設定したい場合は9時間引いてください。&lt;/p>
&lt;p>例: 毎朝7時（JST）に実行 → &lt;code>cron(0 22 * * ? *)&lt;/code> （前日の22時UTC）&lt;/p>
&lt;h2 id="実際に動かしてみた結果">実際に動かしてみた結果&lt;/h2>
&lt;p>私はLINEにメッセージを通知するFunctionを作って動かしてみました。
5分に1回通知がくるようになりました。&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/blog/008-aws-eventbrdge/img-008-005.png"
width="1494"
height="256"
srcset="https://bossagyu.com/blog/008-aws-eventbrdge/img-008-005_hu71f6b6c6b7e02c0c07c0e3b24896f2dd_36361_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/008-aws-eventbrdge/img-008-005_hu71f6b6c6b7e02c0c07c0e3b24896f2dd_36361_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="EventBrideでLambda関数を動作させた結果"
class="gallery-image"
data-flex-grow="583"
data-flex-basis="1400px"
>&lt;/p>
&lt;p>テストの際は短い間隔（5分など）で設定して動作確認し、本番では適切な間隔に変更するのがおすすめです。&lt;/p>
&lt;h2 id="コストについて">コストについて&lt;/h2>
&lt;p>EventBridge + Lambdaの組み合わせは非常に低コストです。&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>サービス&lt;/th>
&lt;th>無料枠&lt;/th>
&lt;th>超過時の料金&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>EventBridge&lt;/td>
&lt;td>無料&lt;/td>
&lt;td>スケジュールルールは無料&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Lambda&lt;/td>
&lt;td>月100万リクエスト無料&lt;/td>
&lt;td>$0.20/100万リクエスト&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>個人開発のリマインダーBot程度なら、ほぼ無料で運用できます。&lt;/p>
&lt;h2 id="ハマりポイント">ハマりポイント&lt;/h2>
&lt;h3 id="1-タイムゾーンの罠">1. タイムゾーンの罠&lt;/h3>
&lt;p>前述の通り、EventBridgeはUTC基準です。「毎朝9時」と設定したつもりが「夕方18時」に動いていた、というのはよくある失敗です。&lt;/p>
&lt;h3 id="2-初回実行のタイミング">2. 初回実行のタイミング&lt;/h3>
&lt;p>cron式を設定した直後は、次のスケジュールまで待つ必要があります。
すぐにテストしたい場合は、Lambdaのテスト機能を使いましょう。&lt;/p>
&lt;h3 id="3-削除を忘れずに">3. 削除を忘れずに&lt;/h3>
&lt;p>テスト用に作成したEventBridgeルールを放置すると、Lambdaが実行され続けます。
不要になったルールは必ず削除しましょう。&lt;/p>
&lt;h2 id="まとめ">まとめ&lt;/h2>
&lt;p>AWS EventBridgeを用いてLambdaを定期実行する方法を解説しました。&lt;/p>
&lt;ul>
&lt;li>EventBridgeはスケジュール実行とイベント駆動の2つの用途がある&lt;/li>
&lt;li>cron式はUTC基準なので、JSTで設定したい場合は9時間引く&lt;/li>
&lt;li>個人開発レベルならほぼ無料で運用可能&lt;/li>
&lt;li>不要になったルールは削除を忘れずに&lt;/li>
&lt;/ul>
&lt;p>サーバーレスで定期実行を実現できるので、リマインダーBotやバッチ処理などに活用してみてください。&lt;/p>
&lt;h2 id="関連記事">関連記事&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/014-aws-apigateway-lambda/" >AWS API GatewayとLambdaを連携させる方法&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/045-clean-bot/" >掃除リマインダーBotの使い方&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/046-clean-bot-technical/" >掃除リマインダーBotの技術解説&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Google Search Consoleを用いてブログをGoogle検索の対象にする方法</title><link>https://bossagyu.com/blog/007-google-search-console/</link><pubDate>Mon, 18 Dec 2023 19:10:04 +0900</pubDate><guid>https://bossagyu.com/blog/007-google-search-console/</guid><description>&lt;h2 id="概要">概要&lt;/h2>
&lt;p>Google検索で引っかかるようにするために、SEO対策を行いましょうとありますが、まずはGoogle検索に認識されないと話になりません。
この記事では、Google Search Consoleを用いて自身が作成した独自ドメインのブログが、Google検索の対象となる方法を解説します。&lt;/p>
&lt;h2 id="実現までの流れ">実現までの流れ&lt;/h2>
&lt;ol>
&lt;li>Google Search Consoleの登録&lt;/li>
&lt;li>ドメインの所有権の確認&lt;/li>
&lt;li>サイトマップの登録&lt;/li>
&lt;li>インデックス登録をリクエスト&lt;/li>
&lt;li>まとめ&lt;/li>
&lt;/ol>
&lt;h2 id="google-search-consoleの登録">Google Search Consoleの登録&lt;/h2>
&lt;p>&lt;a class="link" href="https://search.google.com/search-console/welcome" target="_blank" rel="noopener"
>Google Search Console&lt;/a>に登録します。&lt;/p>
&lt;p>ドメインを選択肢、URLを入力します。&lt;br>
&lt;img src="https://bossagyu.com/blog/007-google-search-console/img-007-001.png"
width="1838"
height="1546"
srcset="https://bossagyu.com/blog/007-google-search-console/img-007-001_hueec663f85e06a371439303da89c78565_290083_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/007-google-search-console/img-007-001_hueec663f85e06a371439303da89c78565_290083_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="URL入力画面"
class="gallery-image"
data-flex-grow="118"
data-flex-basis="285px"
>&lt;/p>
&lt;h2 id="ドメインの所有権の確認">ドメインの所有権の確認&lt;/h2>
&lt;p>以下のような画像が表示されDNSの所有権を確認します。&lt;br>
(念のためTXTレコードの内容は黒く塗りつぶしています。)
&lt;img src="https://bossagyu.com/blog/007-google-search-console/img-007-002.png"
width="1562"
height="1404"
srcset="https://bossagyu.com/blog/007-google-search-console/img-007-002_hu90d036695b5d9438a5ef8f0d8eb9f52d_277880_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/007-google-search-console/img-007-002_hu90d036695b5d9438a5ef8f0d8eb9f52d_277880_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="DNSの所有権確認画面"
class="gallery-image"
data-flex-grow="111"
data-flex-basis="267px"
>&lt;/p>
&lt;p>ドメインのTXTにGoogleが指定した文字列を追加することで、所有権を確認することができます。
ドメインのDNSの設定画面に移動し、TXTレコードを追加します。&lt;/p>
&lt;p>私の場合はNetlifyでドメインを取得しているので、NetlifyのDNSの設定画面に移動します。
&lt;code>Domains -&amp;gt; Domain Settings -&amp;gt; DNS Records&lt;/code> に移動し、TXTレコードを追加します。
&lt;img src="https://bossagyu.com/blog/007-google-search-console/img-007-003.png"
width="2510"
height="1058"
srcset="https://bossagyu.com/blog/007-google-search-console/img-007-003_hu314a39771588211da36db92d8d263e34_237067_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/007-google-search-console/img-007-003_hu314a39771588211da36db92d8d263e34_237067_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Netlify DNS設定画面"
class="gallery-image"
data-flex-grow="237"
data-flex-basis="569px"
>&lt;/p>
&lt;p>レコードの内容はGoogle Search Consoleに表示されているものをコピーしてValueに貼り付けます。&lt;br>
(Valueの部分は黒く塗りつぶしています。)
&lt;img src="https://bossagyu.com/blog/007-google-search-console/img-007-004.png"
width="1174"
height="1128"
srcset="https://bossagyu.com/blog/007-google-search-console/img-007-004_hu1e1863a2c4e003478336f70e9eafc56f_103599_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/007-google-search-console/img-007-004_hu1e1863a2c4e003478336f70e9eafc56f_103599_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="DNS追加例"
class="gallery-image"
data-flex-grow="104"
data-flex-basis="249px"
>&lt;/p>
&lt;p>DNSの反映を待ちます。ものによっては数時間かかる場合があります。&lt;/p>
&lt;p>DNSの反映はコマンドラインからでも確認できます。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">dig -t txt bossagyu.com
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>その後、Google Search Consoleの所有権の確認を押します。&lt;br>
これで所有権の確認が完了し、Google Search Consoleにドメインが登録されます。&lt;/p>
&lt;h2 id="サイトマップの登録">サイトマップの登録&lt;/h2>
&lt;p>サイトマップを登録することで、Googleにサイトの構造を伝え、サイトのクロールを促進することができます。
Hugoで作成したブログの場合は、&lt;code>/sitemap.xml&lt;/code>にサイトマップが作成されているので、これを登録します。&lt;/p>
&lt;p>Google Search Consoleの左側のメニューから「サイトマップ」を選択し、サイトマップを追加します。
&lt;img src="https://bossagyu.com/blog/007-google-search-console/img-007-005.png"
width="1401"
height="649"
srcset="https://bossagyu.com/blog/007-google-search-console/img-007-005_huab186b2aac0819b0a23c39035e1f9b13_109847_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/007-google-search-console/img-007-005_huab186b2aac0819b0a23c39035e1f9b13_109847_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="サイトマップの登録画面"
class="gallery-image"
data-flex-grow="215"
data-flex-basis="518px"
>&lt;/p>
&lt;h2 id="インデックス登録をリクエスト">インデックス登録をリクエスト&lt;/h2>
&lt;p>サイトマップに登録されていても、Googleがクロールして、その後インデックスが登録されるまでには時間がかかります。
私の場合は数日待ってもインデックスが登録されなかったので、インデックス登録をリクエストしました。&lt;/p>
&lt;p>Google Search Consoleの検索窓で登録したいURLを検索し、検索結果の右側にある「インデックス登録をリクエスト」を押します。
&lt;img src="https://bossagyu.com/blog/007-google-search-console/img-007-006.png"
width="2900"
height="868"
srcset="https://bossagyu.com/blog/007-google-search-console/img-007-006_hu89514150a49eb25441f9d3432897d97a_212853_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/007-google-search-console/img-007-006_hu89514150a49eb25441f9d3432897d97a_212853_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="インデックス登録をリクエスト"
class="gallery-image"
data-flex-grow="334"
data-flex-basis="801px"
>&lt;/p>
&lt;p>これでインデックス登録をリクエストできます。
クリックしてから数時間でインデックスが登録されました。&lt;/p>
&lt;h2 id="まとめ">まとめ&lt;/h2>
&lt;p>Google Search Consoleを用いて自身が作成した独自ドメインのブログが検索に引っかかるようにする方法を解説しました。&lt;br>
せっかくブログを作成したのに、Google検索に引っかからないのはもったいないので、ぜひ試してみてください。&lt;/p>
&lt;h2 id="関連記事">関連記事&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/001-hugo-netlify-build/" >Hugo + Netlify + Githubでブログを公開する&lt;/a> - ブログの初期構築&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/003-google-analytics/" >HugoでGoogle Analyticsの設定をする方法&lt;/a> - アクセス解析&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/009-light-house/" >Lighthouseの使い方の紹介&lt;/a> - パフォーマンス計測&lt;/li>
&lt;/ul></description></item><item><title>IntellijでAWS Toolkitを使ってLambdaを効率よく開発する</title><link>https://bossagyu.com/blog/006-intellij-lamda-setup/</link><pubDate>Tue, 12 Dec 2023 22:40:05 +0900</pubDate><guid>https://bossagyu.com/blog/006-intellij-lamda-setup/</guid><description>&lt;h2 id="概要">概要&lt;/h2>
&lt;p>IntellijでAWS Toolkitを使ってLambdaを効率よく開発する方法を解説します。&lt;/p>
&lt;h2 id="実現までの流れ">実現までの流れ&lt;/h2>
&lt;ol>
&lt;li>事前準備&lt;/li>
&lt;li>AWS Toolkitのインストール&lt;/li>
&lt;li>AWS Toolkitの設定&lt;/li>
&lt;li>Lambdaの開発&lt;/li>
&lt;li>Lambdaをローカルで実行&lt;/li>
&lt;li>まとめ&lt;/li>
&lt;/ol>
&lt;h2 id="事前準備">事前準備&lt;/h2>
&lt;h3 id="dockerのインストール">dockerのインストール&lt;/h3>
&lt;p>intellijで利用するAWS Toolkitでは、Lambda動作させるためにDockerを使用します。&lt;br>
このため事前に &lt;a class="link" href="https://docs.docker.jp/docker-for-mac/install.html" target="_blank" rel="noopener"
>こちら&lt;/a>を参考にDockerをインストールしておいてください。&lt;/p>
&lt;h3 id="aws-cliのインストール">AWS CLIのインストール&lt;/h3>
&lt;p>AWS CLI(SAM)をインストールします。&lt;br>
インストール方法は &lt;a class="link" href="https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/install-cliv2-mac.html" target="_blank" rel="noopener"
>こちら&lt;/a> を参考にしてください。&lt;/p>
&lt;p>IntellijにSAM CLI executableのパスを &lt;code>File -&amp;gt; Settings -&amp;gt; Tools -&amp;gt; AWS Toolkit&lt;/code> から設定します。&lt;br>
私の環境ではbrewでインストールしたので、以下のパスを設定しました。
&lt;img src="https://bossagyu.com/blog/006-intellij-lamda-setup/img-006-005.png"
width="976"
height="707"
srcset="https://bossagyu.com/blog/006-intellij-lamda-setup/img-006-005_hu675386622417230d840d91c8ebb2bb8f_99529_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/006-intellij-lamda-setup/img-006-005_hu675386622417230d840d91c8ebb2bb8f_99529_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Intellijの設定画面"
class="gallery-image"
data-flex-grow="138"
data-flex-basis="331px"
>&lt;/p>
&lt;h2 id="aws-toolkitのインストール">AWS Toolkitのインストール&lt;/h2>
&lt;p>IntellijのプラグインからAWS Toolkitをインストールします。
プラグインのインストールは &lt;a class="link" href="https://www.jetbrains.com/help/idea/managing-plugins.html#install_plugin" target="_blank" rel="noopener"
>こちら&lt;/a> を参考にしてください。&lt;/p>
&lt;h2 id="aws-toolkitの設定">AWS Toolkitの設定&lt;/h2>
&lt;p>AWS Toolkitを利用するためにはAWSの認証情報を設定する必要があります。&lt;/p>
&lt;p>AWS ExplorerからAWSの認証情報を設定します。
&lt;img src="https://bossagyu.com/blog/006-intellij-lamda-setup/img-006-001.png"
width="2196"
height="1099"
srcset="https://bossagyu.com/blog/006-intellij-lamda-setup/img-006-001_huf4116ed306cba8df785a2a60b5b23b2e_297569_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/006-intellij-lamda-setup/img-006-001_huf4116ed306cba8df785a2a60b5b23b2e_297569_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="AWSの認証"
class="gallery-image"
data-flex-grow="199"
data-flex-basis="479px"
>&lt;/p>
&lt;p>Access Key IDとSecret Access KeyをAWSのコンソールから取得し、設定します。
設定が完了したら、AWS ExplorerにAWSのリソースが表示されるようになります。
&lt;img src="https://bossagyu.com/blog/006-intellij-lamda-setup/img-006-002.png"
width="400"
height="517"
srcset="https://bossagyu.com/blog/006-intellij-lamda-setup/img-006-002_huef1a4e77eb43fc9eb01b7ee491b04729_36733_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/006-intellij-lamda-setup/img-006-002_huef1a4e77eb43fc9eb01b7ee491b04729_36733_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="AWSリソース追加確認"
class="gallery-image"
data-flex-grow="77"
data-flex-basis="185px"
>&lt;/p>
&lt;p>この画像ではリージョンが &lt;code>us-east-1&lt;/code> になっていますが、Lambdaを作成するリージョンに合わせてください。&lt;/p>
&lt;h2 id="lambdaの開発">Lambdaの開発&lt;/h2>
&lt;p>以下のようなコードスニペットを作成します。&lt;/p>
&lt;p>lamda-sample.py&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">lambda_handler&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">event&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">context&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Hello World&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="s2">&amp;#34;Hello World!&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>AWS ExplorerからLambdaを作成します。&lt;br>
&lt;img src="https://bossagyu.com/blog/006-intellij-lamda-setup/img-006-003.png"
width="382"
height="452"
srcset="https://bossagyu.com/blog/006-intellij-lamda-setup/img-006-003_hu43e53933fae6825db1f62c0ff344925c_42804_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/006-intellij-lamda-setup/img-006-003_hu43e53933fae6825db1f62c0ff344925c_42804_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Lambda関数の作成画面"
class="gallery-image"
data-flex-grow="84"
data-flex-basis="202px"
>&lt;/p>
&lt;p>Create Lambda Functionを選択し、必要な値を入力します。&lt;br>
Handlerにはコードスニペットの &lt;code>&amp;lt;ファイル名&amp;gt;.&amp;lt;関数名&amp;gt;&lt;/code> を入力してください。&lt;br>
&lt;img src="https://bossagyu.com/blog/006-intellij-lamda-setup/img-006-004.png"
width="769"
height="626"
srcset="https://bossagyu.com/blog/006-intellij-lamda-setup/img-006-004_hub0ca7c127cd2ded12c0d6c6eb7b0efd4_80439_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/006-intellij-lamda-setup/img-006-004_hub0ca7c127cd2ded12c0d6c6eb7b0efd4_80439_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Lambda関数の設定"
class="gallery-image"
data-flex-grow="122"
data-flex-basis="294px"
>&lt;/p>
&lt;p>これでLambdaの作成が完了しました。&lt;/p>
&lt;h2 id="lambdaをローカルで実行">Lambdaをローカルで実行&lt;/h2>
&lt;p>またToolkitを利用すると、Lambdaをローカルで実行することができます。
&lt;img src="https://bossagyu.com/blog/006-intellij-lamda-setup/img-006-006.png"
width="544"
height="181"
srcset="https://bossagyu.com/blog/006-intellij-lamda-setup/img-006-006_hue5ae0f1238e4626d26a5e0ef51d83c5a_29793_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/006-intellij-lamda-setup/img-006-006_hue5ae0f1238e4626d26a5e0ef51d83c5a_29793_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Lambda関数の実行画面"
class="gallery-image"
data-flex-grow="300"
data-flex-basis="721px"
>&lt;/p>
&lt;p>Runを選択すると、Lambdaがローカルで実行されます。&lt;/p>
&lt;h2 id="まとめ">まとめ&lt;/h2>
&lt;p>IntellijでAWS Toolkitを使ってLambdaを効率よく開発する方法を解説しました。
Intellijで開発してローカルで実行できるので、開発効率がかなり上がります。&lt;/p>
&lt;h2 id="関連記事">関連記事&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/014-aws-apigateway-lambda/" >AWS API GatewayとLambdaを連携させる方法&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/046-clean-bot-technical/" >AWS Lambda + LINE Botで掃除リマインダーを作る&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>IntellijでのGithub Copilotの使い方</title><link>https://bossagyu.com/blog/005-github-copilot/</link><pubDate>Mon, 11 Dec 2023 22:45:40 +0900</pubDate><guid>https://bossagyu.com/blog/005-github-copilot/</guid><description>&lt;h2 id="概要">概要&lt;/h2>
&lt;p>Github CopilotをIntellijで使う方法を解説します。
合わせてショートカットのチートシートを記載します。&lt;/p>
&lt;h2 id="実現までの流れ">実現までの流れ&lt;/h2>
&lt;ol>
&lt;li>Github Copilotの登録&lt;/li>
&lt;li>Intellijの設定&lt;/li>
&lt;li>Github Copilotの利用&lt;/li>
&lt;li>まとめ&lt;/li>
&lt;/ol>
&lt;h2 id="github-copilotの登録">Github Copilotの登録&lt;/h2>
&lt;p>&lt;a class="link" href="https://copilot.github.com/" target="_blank" rel="noopener"
>Github Copilot&lt;/a> のリンクからGithub Copilotに登録します。&lt;/p>
&lt;h2 id="intellijの設定">Intellijの設定&lt;/h2>
&lt;p>IntellijのプラグインからGithub Copilotをインストールします。&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/blog/005-github-copilot/img-005-001.png"
width="972"
height="237"
srcset="https://bossagyu.com/blog/005-github-copilot/img-005-001_huf1c35ae508766e7174ebde609be4cd7c_63136_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/005-github-copilot/img-005-001_huf1c35ae508766e7174ebde609be4cd7c_63136_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Github Copilot plugin"
class="gallery-image"
data-flex-grow="410"
data-flex-basis="984px"
>&lt;/p>
&lt;p>インストールが完了したら、Intellijを再起動します。&lt;/p>
&lt;h2 id="github-copilotの利用">Github Copilotの利用&lt;/h2>
&lt;h3 id="ショートカット一覧">ショートカット一覧&lt;/h3>
&lt;p>Intellijでコードを書いていると、Github Copilotがコードを補完してくれます。&lt;/p>
&lt;p>macのショートカットの一覧は以下のとおりです。&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>ショートカット&lt;/th>
&lt;th>機能&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>tab&lt;/code>&lt;/td>
&lt;td>コードを補完する&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>Option&lt;/code> + &lt;code>]&lt;/code>&lt;/td>
&lt;td>次の補完候補を表示する&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>Option&lt;/code> + &lt;code>[&lt;/code>&lt;/td>
&lt;td>前の補完候補を表示する&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>Command&lt;/code> + &lt;code>→&lt;/code>&lt;/td>
&lt;td>提案の次の単語のみ受け入れる&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="コメントによるコード補完">コメントによるコード補完&lt;/h3>
&lt;p>Github Copilotはコメントによるコード補完も行うことができます。&lt;/p>
&lt;p>例えば、以下のようなコメントを書くと、コメントの内容に応じてコードを補完してくれます。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// このメソッドは、引数の値を2倍にして返す ← 書いたコメント&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">double&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">value&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// ← 生成されたコード&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">value&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">2&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="まとめ">まとめ&lt;/h2>
&lt;p>IntellijでGithub Copilotを利用する方法を解説しました。
この記事はGithub Copilotを利用して書いており、Markdownでのブログ作成でもかなり補完してくれるので、気になる人は試してみてください。&lt;/p>
&lt;h2 id="関連記事">関連記事&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/017-vscode-copilot/" >VSCodeでGithub Copilotを使いこなす完全ガイド&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/042-github-copilot/" >Github CopilotをChat toolを使って便利に使う方法&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Pyenvとvenvを用いたローカル環境のセットアップ方法</title><link>https://bossagyu.com/blog/004-python-setup/</link><pubDate>Sun, 10 Dec 2023 23:19:33 +0900</pubDate><guid>https://bossagyu.com/blog/004-python-setup/</guid><description>&lt;h2 id="概要">概要&lt;/h2>
&lt;p>macのローカル環境で開発を行う際のpythonの環境構築の方法について記載します。&lt;br>
今回は以下の２つの仕組みを利用して、pythonのバージョン管理と仮想環境の管理を行います。&lt;/p>
&lt;ul>
&lt;li>pyenv
&lt;ul>
&lt;li>複数のpythonのバージョンを扱うために利用します。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>venv
&lt;ul>
&lt;li>プロジェクトごとに環境を分けるために利用します。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;p>それぞれの違いや必要性の解説は&lt;a class="link" href="https://jimaru.blog/programming/python/venv_pyenv_choice/" target="_blank" rel="noopener"
>こちら&lt;/a>の記事が参考になります。&lt;/p>
&lt;h2 id="pythonのインストール">Pythonのインストール&lt;/h2>
&lt;p>まずは、ローカル環境にPyenvをインストールし、任意のPythonバージョンを利用できるようにします。&lt;/p>
&lt;p>pyenvをインストールします。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">brew install pyenv
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>インストールしたpyenvのバージョンを確認します。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">pyenv --version
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pyenv 2.3.35
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>zshに設定を追加します。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s1">&amp;#39;export PYENV_ROOT=&amp;#34;$HOME/.pyenv&amp;#34;&amp;#39;&lt;/span> &amp;gt;&amp;gt; ~/.zshrc
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s1">&amp;#39;export PATH=&amp;#34;$PYENV_ROOT/bin:$PATH&amp;#34;&amp;#39;&lt;/span> &amp;gt;&amp;gt; ~/.zshrc
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> -e &lt;span class="s1">&amp;#39;if command -v pyenv 1&amp;gt;/dev/null 2&amp;gt;&amp;amp;1; then\n eval &amp;#34;$(pyenv init -)&amp;#34;\nfi&amp;#39;&lt;/span> &amp;gt;&amp;gt; ~/.zshrc
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;code>.zshrc&lt;/code>の内容を読み込みます。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">source&lt;/span> ~/.zshrc
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>インストール可能なPythonのバージョン一覧を表示します。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">pyenv install --list
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>指定したバージョンをインストールします。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">pyenv install 3.11.7
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>プロジェクトフォルダに指定したPythonのバージョンを利用します。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd&lt;/span> &amp;lt;作成したプロジェクトフォルダ&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pyenv &lt;span class="nb">local&lt;/span> 3.11.7
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pyenv versions
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>globalの場合は全体に反映されます。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">pyenv global 3.11.7
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>実行されているpythonのバージョンを確認します。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">python -V
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="venvで仮想環境の作成">venvで仮想環境の作成&lt;/h2>
&lt;p>プロジェクトのディレクトリに仮想環境を作成します。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># python -m venv &amp;lt;仮想環境名&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">python -m venv venv
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>仮想環境を有効化します。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">source&lt;/span> venv/bin/activate
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>ディアクティベートは以下のコマンドで実行できます。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">deactivate
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="venvのアップデート方法">venvのアップデート方法&lt;/h2>
&lt;p>依存パッケージを最新化する場合は、仮想環境を有効化した上で以下を実行します。&lt;code>requirements.txt&lt;/code> を利用しているなら、ファイルを更新した後に再インストールするとよいでしょう。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">source&lt;/span> venv/bin/activate
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pip install --upgrade pip
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pip install -U -r requirements.txt
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Pythonのマイナーバージョンを上げるときは、&lt;code>pyenv&lt;/code> で新しいバージョンを入れ直したあと、仮想環境を作り直すとライブラリの整合性が保ちやすいです。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">pyenv install 3.12.2
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pyenv &lt;span class="nb">local&lt;/span> 3.12.2
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">rm -rf venv
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">python -m venv venv
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">source&lt;/span> venv/bin/activate
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pip install -U -r requirements.txt
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="venvの削除方法">venvの削除方法&lt;/h2>
&lt;p>不要になった仮想環境はディレクトリごと削除すればよいです。作業ディレクトリを確認した上で実行してください。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">rm -rf venv
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>以上でローカル環境の構築が完了です。&lt;/p>
&lt;h2 id="関連記事">関連記事&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/032-python-uv/" >MacでUVを用いてPythonの開発環境を構築する&lt;/a>（モダンな代替ツールとして）&lt;/li>
&lt;/ul></description></item><item><title>HugoでGoogle Analyticsの設定をする方法</title><link>https://bossagyu.com/blog/003-google-analytics/</link><pubDate>Sat, 09 Dec 2023 18:09:42 +0900</pubDate><guid>https://bossagyu.com/blog/003-google-analytics/</guid><description>&lt;h2 id="概要">概要&lt;/h2>
&lt;p>HugoをでGoogle Analyticsを設定する方法をサクッと解説します。&lt;/p>
&lt;h2 id="実現までの流れ">実現までの流れ&lt;/h2>
&lt;ol>
&lt;li>Google Analyticsへの登録&lt;/li>
&lt;li>トラッキングIDの取得&lt;/li>
&lt;li>Hugoの設定にトラッキングIDを追加&lt;/li>
&lt;/ol>
&lt;h2 id="google-analyticsの登録">Google Analyticsの登録&lt;/h2>
&lt;p>&lt;a class="link" href="https://support.google.com/analytics/answer/9304153?hl=ja" target="_blank" rel="noopener"
>[GA4] アナリティクスで新しいウェブサイトまたはアプリのセットアップを行う&lt;/a>
に従い登録を行います。&lt;br>
データストリームを追加すると、トラッキングIDが取得できるのでメモしておきます。
&lt;img src="https://bossagyu.com/blog/003-google-analytics/img-003-001.png"
width="1262"
height="387"
srcset="https://bossagyu.com/blog/003-google-analytics/img-003-001_hud6bb49f0ab37d8b4799433827135bf4a_61149_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/003-google-analytics/img-003-001_hud6bb49f0ab37d8b4799433827135bf4a_61149_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="データストリーム追加画面"
class="gallery-image"
data-flex-grow="326"
data-flex-basis="782px"
>
※ トラッキングIDは日本語訳の影響か、測定IDという表示になっています。&lt;/p>
&lt;h2 id="hugoの設定にトラッキングidを追加">Hugoの設定にトラッキングIDを追加&lt;/h2>
&lt;h3 id="tomlに設定を追加">tomlに設定を追加&lt;/h3>
&lt;p>config.tomlに&lt;code>googleAnalytics = トラッキングID&lt;/code>を追加します。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">baseURL&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s1">&amp;#39;https://bossagyu.com&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">languageCode&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s1">&amp;#39;ja-jp&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">title&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s1">&amp;#39;Bossagyu Blog&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">theme&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s1">&amp;#39;hugo-bearcub&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">googleAnalytics&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;G-1234ABCDEF&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c"># ↑ この行を追加、トラッキングIDは自分のものに変更してください。&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="トラッキングコードを埋め込む">トラッキングコードを埋め込む&lt;/h3>
&lt;p>テンプレートによってはtomlの設定を入れるだけで読めるものもあるみたいですが、
私の使用している&lt;a class="link" href="https://github.com/clente/hugo-bearcub/tree/main" target="_blank" rel="noopener"
>bearcub&lt;/a>のテンプレートは対応してなかったので、
自分でヘッダにトラッキングコードを読み込むように追加します。&lt;/p>
&lt;p>コードスニペットについては&lt;a class="link" href="https://github.com/clente/hugo-bearcub/tree/main" target="_blank" rel="noopener"
>まくまく Hugo ノート&lt;/a>を参考にさせていただきました。&lt;/p>
&lt;p>トラッキングコードを読むために &lt;code>layouts/partials/analytics.html&lt;/code> を作成する。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">{{ if not .Site.IsServer }}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{{ with .Site.GoogleAnalytics }}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&amp;lt;!-- Google tag (gtag.js) --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">script&lt;/span> &lt;span class="na">async&lt;/span> &lt;span class="na">src&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;https://www.googletagmanager.com/gtag/js?id={{ . }}&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&amp;lt;/&lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">window&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">dataLayer&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">window&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">dataLayer&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="p">[];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">function&lt;/span> &lt;span class="nx">gtag&lt;/span>&lt;span class="p">(){&lt;/span>&lt;span class="nx">dataLayer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">push&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">arguments&lt;/span>&lt;span class="p">);}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">gtag&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;js&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nb">Date&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">gtag&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;config&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;{{ . }}&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{{ end }}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{{ end }}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>ページヘッダで &lt;code>analytics.html&lt;/code> を読み込むようにする。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># テンプレートの内容をコピーしてきてオーバーライドする&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cp themes/hugo-bearcub/layouts/_default/baseof.html layouts/_default/baseof.html
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">vim layouts/_default/baseof.html
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>baseof.htmlに&lt;code>{{- partial &amp;quot;analytics&amp;quot; . -}}&lt;/code> を追加する。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&amp;lt;!DOCTYPE html&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">html&lt;/span> &lt;span class="na">lang&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{ with .Site.LanguageCode }}{{ . }}{{ else }}en-US{{ end }}&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">head&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> {{- partial &amp;#34;analytics&amp;#34; . -}}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">meta&lt;/span> &lt;span class="na">http-equiv&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;X-Clacks-Overhead&amp;#34;&lt;/span> &lt;span class="na">content&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;GNU Terry Pratchett&amp;#34;&lt;/span> &lt;span class="err">/&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>上記ソースコードを追加した状態で、再度ビルドするとGoogle Analyticsにデータが送信されるようになります。&lt;/p>
&lt;h2 id="tips">tips&lt;/h2>
&lt;p>こちらの対応をしても、Google Analyticsにデータが連携されていないように見える場合は、タグの追加がうまくいっていない可能性があります。&lt;br>
まずはタグがちゃんと入っているかの切り分けをするために、googleデベロッパーツールを開きトラッキングがhtml内に含まれているか確認してみるとよいです。&lt;/p>
&lt;h2 id="関連記事">関連記事&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/001-hugo-netlify-build/" >Hugo + Netlify + Githubでブログを公開する&lt;/a> - ブログの初期構築&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/007-google-search-console/" >Google Search Consoleでブログを検索対象にする方法&lt;/a> - SEO対策&lt;/li>
&lt;/ul></description></item><item><title>LINE Messaging APIの登録と使い方</title><link>https://bossagyu.com/blog/002-line-messaging-api/</link><pubDate>Thu, 07 Dec 2023 09:37:00 +0900</pubDate><guid>https://bossagyu.com/blog/002-line-messaging-api/</guid><description>&lt;h2 id="概要">概要&lt;/h2>
&lt;p>LINEのBotを利用してアプリケーションを作ってみようと考えたのでまずはBotを利用できる状態にする。&lt;br>
本ページでは一番最初にLINE Message APIの登録の方法とcurlでコマンドラインからメッセージを送る方法を紹介します。&lt;/p>
&lt;h2 id="messaging-apiを利用する">Messaging APIを利用する&lt;/h2>
&lt;p>&lt;a class="link" href="https://developers.line.biz/console/" target="_blank" rel="noopener"
>LINE Developers&lt;/a> にログインしてプロバイダーを作成する。&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/blog/002-line-messaging-api/img-002-001.png"
width="1542"
height="947"
srcset="https://bossagyu.com/blog/002-line-messaging-api/img-002-001_hu0f14840654aed48d16eb2fc9422f6fa9_170581_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/002-line-messaging-api/img-002-001_hu0f14840654aed48d16eb2fc9422f6fa9_170581_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="プロバイダー作成画面"
class="gallery-image"
data-flex-grow="162"
data-flex-basis="390px"
>&lt;/p>
&lt;p>プロバイダーとは(&lt;a class="link" href="https://developers.line.biz/ja/docs/line-developers-console/overview/#provider" target="_blank" rel="noopener"
>説明&lt;/a>)&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">LINE Developersサイトでは、サービスを提供し、ユーザーの情報を取得する開発者個人、
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">企業、または団体等をサービス提供者（LINEミニアプリではサービス事業主）と呼びます。
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>なので好きな文字列を入れる。&lt;/p>
&lt;p>そのまま新規チャンネルを作成する。
&lt;img src="https://bossagyu.com/blog/002-line-messaging-api/img-002-002.png"
width="1393"
height="1461"
srcset="https://bossagyu.com/blog/002-line-messaging-api/img-002-002_hu35721a5501bd4d5408347db79d1d688a_163848_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/002-line-messaging-api/img-002-002_hu35721a5501bd4d5408347db79d1d688a_163848_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="チャンネル作成画面"
class="gallery-image"
data-flex-grow="95"
data-flex-basis="228px"
>&lt;/p>
&lt;p>このまま作成ボタンを押すと新規チャンネルが作成される&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/blog/002-line-messaging-api/img-002-003.png"
width="848"
height="267"
srcset="https://bossagyu.com/blog/002-line-messaging-api/img-002-003_hudf15c356e8d7f64c7636ad582293d1a9_33429_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/002-line-messaging-api/img-002-003_hudf15c356e8d7f64c7636ad582293d1a9_33429_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="チャンネル作成結果"
class="gallery-image"
data-flex-grow="317"
data-flex-basis="762px"
>&lt;/p>
&lt;h1 id="コマンドラインからポストをする">コマンドラインからポストをする&lt;/h1>
&lt;p>Messaging API設定からQRコードを読みこんで友達追加する。&lt;/p>
&lt;p>Messaging API設定から「チャンネルアクセストークン(長期)」を取得
チャンネル基本設定から「あなたのユーザーID」取得&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="nv">TOKEN&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;&amp;lt;チャンネルアクセストークン(長期)&amp;gt;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">ID&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;&amp;lt;あなたのユーザーID&amp;gt;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">UUID&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="k">$(&lt;/span>uuidgen &lt;span class="p">|&lt;/span> tr &lt;span class="s2">&amp;#34;[:upper:]&amp;#34;&lt;/span> &lt;span class="s2">&amp;#34;[:lower:]&amp;#34;&lt;/span>&lt;span class="k">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">curl -v -X POST https://api.line.me/v2/bot/message/push &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span>-H &lt;span class="s1">&amp;#39;Content-Type: application/json&amp;#39;&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span>-H &lt;span class="s2">&amp;#34;Authorization: Bearer &lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nv">TOKEN&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span>-H &lt;span class="s2">&amp;#34;X-Line-Retry-Key: &amp;#34;&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span>-d &lt;span class="s2">&amp;#34;{
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> \&amp;#34;to\&amp;#34;: \&amp;#34;&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nv">ID&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">\&amp;#34;,
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> \&amp;#34;messages\&amp;#34;:[
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> {
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> \&amp;#34;type\&amp;#34;:\&amp;#34;text\&amp;#34;,
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> \&amp;#34;text\&amp;#34;:\&amp;#34;Hello, world1\&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> }
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> ]
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">}&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>レスポンスが帰ってきて、LINEのトーク画面でBotからの投稿が行われていれば成功！&lt;/p>
&lt;h2 id="関連記事">関連記事&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/045-clean-bot/" >掃除を忘れない！掃除リマインダーLINE Bot&lt;/a>（LINE Botの実践プロジェクト）&lt;/li>
&lt;/ul></description></item><item><title>Hugo + Netlify + Githubでブログを公開する</title><link>https://bossagyu.com/blog/001-hugo-netlify-build/</link><pubDate>Sat, 02 Dec 2023 00:59:37 +0900</pubDate><guid>https://bossagyu.com/blog/001-hugo-netlify-build/</guid><description>&lt;h2 id="概要">概要&lt;/h2>
&lt;p>Hugoで作ったサイトをGithubで管理、Netlifyでビルドした手順を0から作れるよう記載します。
この方式にすると手元でMarkDownで書いたブログをGithubにPushするだけで簡単に公開できるようになります。&lt;/p>
&lt;p>Faviconの設定方法も合わせて解説します。&lt;/p>
&lt;h2 id="流れ">流れ&lt;/h2>
&lt;ol>
&lt;li>Hugoでサイトを生成&lt;/li>
&lt;li>Githubにプッシュ&lt;/li>
&lt;li>Netlifyでデプロイ&lt;/li>
&lt;li>Faviconを設定&lt;/li>
&lt;/ol>
&lt;h2 id="hugoで静的サイトを生成">Hugoで静的サイトを生成&lt;/h2>
&lt;p>まずはHugoをインストールします。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">brew install hugo
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>blogの雛形を作成します。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">hugo new site my-blog
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>ブログに適応するテーマをsubmoduleとして追加します。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd&lt;/span> my-blog
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git init
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># テーマをgithubのsubmoduleとして追加&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git submodule add https://github.com/theNewDynamic/gohugo-theme-ananke.git themes/ananke
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>hugo.tomlに記載することでテーマを適応する。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;theme = &amp;#39;ananke&amp;#39;&amp;#34;&lt;/span> &amp;gt;&amp;gt; config.toml
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>サーバを起動させます。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">hugo server
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>起動ログの &lt;code>Web Server is available at http://localhost:51517/ (bind address 127.0.0.1)&lt;/code> のような記述の &lt;code>http://localhost:51517/&lt;/code> にアクセスすればローカルに起動した静的サイトが閲覧できます。&lt;/p>
&lt;h3 id="tips">Tips&lt;/h3>
&lt;ul>
&lt;li>Hugoのテーマを変えたい場合は、&lt;a class="link" href="https://themes.gohugo.io/" target="_blank" rel="noopener"
>Hugo Themas&lt;/a> から好きなものを選んで変えてください。
&lt;ul>
&lt;li>これは後からでも変えられるのでとりあえずNetlifyでビルドするところまで走り切るのがおすすめ。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Tomlファイルの書き方は &lt;a class="link" href="https://gohugo.io/getting-started/configuration/" target="_blank" rel="noopener"
>Configure Hugo&lt;/a> に記載されています。&lt;/li>
&lt;/ul>
&lt;br>
&lt;h2 id="githubにpush">Githubにpush&lt;/h2>
&lt;p>&lt;a class="link" href="https://github.com/" target="_blank" rel="noopener"
>Github&lt;/a> にリポジトリを作成。&lt;br>
作成後以下のコマンドを実行し、サイトをpushします。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd&lt;/span> my-blog
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> .hugo_build.lock &amp;gt;&amp;gt; .gitignore
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git add .
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git commit -m &lt;span class="s2">&amp;#34;first commit&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git branch -M main
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># &amp;lt;user name&amp;gt;は自分のユーザー名に置き換えてください。&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 今回はmy-blogというリポジトリを作成している例です。&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git remote add origin git@github.com:&amp;lt;user name&amp;gt;/my-blog
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git push -u origin main
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>pushが完了するとGithubのUI上でソースコードが閲覧できる状態になっています。&lt;/p>
&lt;h2 id="netlifyでデプロイ">Netlifyでデプロイ&lt;/h2>
&lt;p>&lt;a class="link" href="https://www.netlify.com/" target="_blank" rel="noopener"
>netlify&lt;/a> へアクセスし、デプロイを行う。&lt;br>
&lt;a class="link" href="https://gohugo.io/hosting-and-deployment/hosting-on-netlify/" target="_blank" rel="noopener"
>Hugoの公式で案内&lt;/a> があるのでこちらを参考に連携を行う。&lt;/p>
&lt;p>指示に従いデプロイを完了すると以下のようにDeployの結果が &lt;code>published&lt;/code> になる。&lt;/p>
&lt;p>&lt;img src="https://bossagyu.com/blog/001-hugo-netlify-build/img-001-001.png"
width="1259"
height="781"
srcset="https://bossagyu.com/blog/001-hugo-netlify-build/img-001-001_hu712c6624ab1de9c2a2e527b803a3005e_132140_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/001-hugo-netlify-build/img-001-001_hu712c6624ab1de9c2a2e527b803a3005e_132140_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="デプロイの実行結果画面"
class="gallery-image"
data-flex-grow="161"
data-flex-basis="386px"
>&lt;/p>
&lt;p>サイト上に表示されたURLをクリックするとデプロイされたサイトにアクセスできる。
&lt;img src="https://bossagyu.com/blog/001-hugo-netlify-build/img-001-002.png"
width="844"
height="274"
srcset="https://bossagyu.com/blog/001-hugo-netlify-build/img-001-002_hu73c75e92b3844c61fd78de6d6904914d_56626_480x0_resize_box_3.png 480w, https://bossagyu.com/blog/001-hugo-netlify-build/img-001-002_hu73c75e92b3844c61fd78de6d6904914d_56626_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="リンク表示画面"
class="gallery-image"
data-flex-grow="308"
data-flex-basis="739px"
>&lt;/p>
&lt;p>これでデプロイまではおしまい。
以降は変更を加えてmainにpushするだけで自動デプロイが走り、サイトの内容が更新されるようになる。&lt;/p>
&lt;h2 id="faviconを設定する">Faviconを設定する&lt;/h2>
&lt;p>Faviconとは、ウェブサイトのブックマークやタブ、ホーム画面などに表示されるアイコンのことです。
Googleより検索結果に表示されるための&lt;a class="link" href="https://developers.google.com/search/docs/appearance/favicon-in-search?hl=ja#guidelines" target="_blank" rel="noopener"
>faviconのガイドライン&lt;/a>が公開されており、
こちらに従うことで検索結果にも表示されるようになります。&lt;/p>
&lt;h3 id="faviconの作成">Faviconの作成&lt;/h3>
&lt;p>Faviconを作成するには、以下のサイトを利用します。&lt;/p>
&lt;ul>
&lt;li>&lt;a class="link" href="https://www.favicon-generator.org/" target="_blank" rel="noopener"
>Favicon.ico &amp;amp; App Icon Generator&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>サイトにアクセスし、faviconにしたい画像をアップロードして「Generate Favicon」をクリックします。
その後表示される画面で、「Download the generated favicon」のリンクをクリックすると、faviconがダウンロードできます。&lt;/p>
&lt;h3 id="hugoでfaviconを表示する">HugoでFaviconを表示する&lt;/h3>
&lt;p>HugoでFaviconを表示するには、themeによっても違いますが、多くのテーマでは以下のようにtomlに設定するだけでfaviconが表示できます。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">params&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">favicon&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;images/favicon.ico&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>ダウンロードしたfavicon.icoを&lt;code>static/images/&lt;/code>ディレクトリに配置して、上記の設定を追加してください。&lt;/p>
&lt;h2 id="まとめ">まとめ&lt;/h2>
&lt;p>Hugo + Netlify + Githubでブログを公開する方法を解説しました。&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Hugo&lt;/strong>: 静的サイトを生成&lt;/li>
&lt;li>&lt;strong>Github&lt;/strong>: ソースコードを管理&lt;/li>
&lt;li>&lt;strong>Netlify&lt;/strong>: 自動デプロイ&lt;/li>
&lt;li>&lt;strong>Favicon&lt;/strong>: ブランディングのための設定&lt;/li>
&lt;/ol>
&lt;p>この構成なら、Markdownで記事を書いてGithubにpushするだけで自動的にサイトが更新されます。&lt;/p>
&lt;h2 id="関連記事">関連記事&lt;/h2>
&lt;ul>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/003-google-analytics/" >HugoでGoogle Analyticsの設定をする方法&lt;/a> - アクセス解析の導入&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/007-google-search-console/" >Google Search Consoleでブログを検索対象にする方法&lt;/a> - SEO対策&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/009-light-house/" >Lighthouseの使い方の紹介&lt;/a> - パフォーマンス計測&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/011-hugo-multilingul-support/" >ChatGPTを利用してHugoで作ったブログを多言語対応する方法&lt;/a> - 多言語対応&lt;/li>
&lt;li>&lt;a class="link" href="https://bossagyu.com/blog/012-social-card/" >Twitter Social Cardの設定方法&lt;/a> - SNS共有の最適化&lt;/li>
&lt;/ul></description></item></channel></rss>