OmniAICreator/Qiita-1.07M huggingface dataset:
GitHub Actions の ${{ github.ref_name }} は checkout しても値は変わらない

先日、 GitHub Actions で遭遇した問題について。

main ブランチに入っている workflow にて他のブランチを checkout して CI/CD 関連の処理をしたいことがありました。
通常は他のブランチに main ブランチを merge すればよいのですが、**並行で開発ラインが走っていて main ブランチの workflow に修正が入ったときに古い workflow を他のブランチで実行してほしくない**という背景があり、このような方法を採用することになりました。

# やったこと

GitHub Actions を手動実行します。ただし、「Use workflow form」を常に main ブランチで実行するようにし、`${{ github.ref }}` で main ブランチでの実行になっているか判定します。

「Use workflow form」というのはブランチが選べるプルダウンのことです。
workflow_dispatch で手動実行する場合、ここでどのブランチに入っている workflow のバージョンで実施するかを選択できます。当然、main ブランチには入っているけど、feature/A ブランチには入っていない workflow がある場合、feature/A ブランチはここでは指定できません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114728/83205087-28b0-fc1f-5ca0-fc264891fea6.png)

main ブランチで実施したかどうかを以下のように判定していました。

```yaml:display-branch.yml
name: ブランチ名の表示

on:
  workflow_dispatch:

jobs:
  show-branch:
    runs-on: ubuntu-latest

    steps:
      - name: main ブランチの workflow かを検証する
        run: |
          BRANCH_NAME="${{ github.ref_name }}"

          if [ "$BRANCH_NAME" != "main" ]; then
            echo "::error::この workflow は main ブランチで実行する必要があります。現在のブランチ: $BRANCH_NAME"
            exit 1
          fi

          echo "ブランチの検証結果: main ブランチで実施されています。"

      - name: チェックアウト
        uses: actions/checkout@v4
        with:
          # main ブランチ以外のブランチを checkout したいが、
          # default は直前のコミット 1 件のみ fetch するので、 0 （全ての履歴）と指定
          fetch-depth: 0
          ref: feature/not-main-branch
```

そして、この処理の後の step にて、ブランチ名の命名規則で、処理を分けたい箇所があり、再び `${{ github.ref_name }}` で参照することに。
ただ、`${{ github.ref_name }}` は依然として main ブランチのままでした。あれ？

# 原因

`${{ github.ref_name }}` は workflow 実行時に一度だけ自動で設定される環境変数でしかなく、checkout しているブランチが都度反映されるわけではありません。

公式ドキュメントにはそういった記載はありませんが、逆に step ごとで値が更新されるような自動環境変数には「ジョブのステップごとに変更されます。」などの注意書きが入っています。

https://docs.github.com/ja/actions/writing-workflows/choosing-what-your-workflow-does/store-information-in-variables#default-environment-variables

そのため、ブランチ名を取得する際は `BRANCH_NAME=$(git branch --show-current)` などで取得するのが好ましいようです。

既に記載した workflow の続きに、エビデンスとして以下を実行しました。

```yaml:display-branch.yml
      - name: ブランチ名を表示
        run: |
          # GitHub Actions の自動変数にて表示
          echo "現在のブランチ名を表示（github.ref_name）: ${{ github.ref_name }}"
          echo "現在のブランチ名を表示（github.ref）: ${{ github.ref }}"
          # git コマンドにて表示
          echo "現在のブランチ名を表示（git branch --show-current）: $(git branch --show-current)"
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114728/36def568-6cae-ed48-85fc-a94229392ec9.png)

確かに、feature/not-main-branch が checkout された後でも `${{ github.ref_name }}` は main のままになっていることがわかります。`${{ github.ref }}` も同様です。

# 教訓

actions/checkout で ref で別ブランチを checkout するときは、以降のブランチ名は git コマンドから取得するようにします。
【小ネタ】文字列に対して1文字ごとに特定の文字を追加するPowerShellワンライナー

# 文字列に対して1文字ごとに特定の文字を追加する

SNSやチャットなどで、突然「あ゛あ゛あ゛あ゛あ゛あ゛あ゛あ゛」と叫びたくなることがあります。
こんなとき、どうやって作りますか。
僕はこんなかんじです
- `ああああああああ`と入力
- カーソルを戻して１文字目と２文字目の間に`だくてん`と入力して変換→`゛`
- `゛`をコピー（`Ctrl+v`）
- `右キー`と`Ctrl+V`を交互に連打

まあこれで十分なんですが、たまに一か所抜けたり、連続で挿入したりしてしまうことがありますね。まあ人間なので仕方ない…

というわけで、今回はWindowsPCなら環境を用意せずに誰でも簡単にこの処理を行えるコマンドを書いてみました。

## コマンド
```powershell
("ああああああああ" -split '(?<=.)') -join "゛"
```

- `ああああああああ`、`゛`部分は任意の文字列に変更してください
- `゛`部分は1文字でなくてもOK

### 実行結果
```powershell
PS C:\> ("ああああああああ" -split '(?<=.)') -join "゛"
あ゛あ゛あ゛あ゛あ゛あ゛あ゛あ゛
```



ちなみに、、、いちいち調べるくらいだったら普段通りコピペしたほうがいい気もします。。
`(?<=.)`という覚えにくい（？）正規表現のところをやめると、1文字目の前にも挿入されます。
これを手で削除したほうが楽ということもあるかもしれませんね。

以上です。

.★☆★.メ.★☆★.リ.★☆★.ー.★☆★.ク.★☆★.リ.★☆★.ス.★☆★.マ.★☆★.ス.★☆★.

```
PS C:\> ("メリークリスマス" -split '') -join ".★☆★."
```

### 構文の解説（ChatGPTによる）

1. **`-split '(?<=.)'`**
   - `-split` は文字列を指定の模式に基づいて分割する
   - `(?<=.)`は正規表現で「一文字の後」を意味する
   - これにより各文字の間で分割される

2. **`-join "゛"`**
   - `-join` は配列の要素を指定の文字列で結合する
   - 各文字の間に『゛』を追加して結合される
Terraform 触ってみる

## はじめに
この記事は [DENSO アドベントカレンダー 2024](https://qiita.com/advent-calendar/2024/denso) の25日目の記事です。

普段はIaCツールとしてAWS CloudFormationを活用しています。
5年ほど使っており不自由なく使えているのですが、
世間ではTerraformの事例も多いためこの機会に触ってみました。
今回は、Terraformに関して記事にします。

## 構成
API Gateway + Lambda の簡易的なAPI
TerraformでIaCを行う

## 成果物
https://github.com/kohei-fujii/terraform-practice
TeraTerm を使って EC2 に IPv6 アドレスで接続する方法 

TeraTerm を使って EC2 に IPv6 アドレスで接続しようとした際に、地味にハマったので備忘録として残します。

## IPv6 アドレスの確認

EC2 インスタンスで IPv6 アドレスが有効になっていることを確認します。
ついでに IPv6 アドレスをクリップボードにコピーしておきます。

![rapture_20241224061203.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212972/f8bc7e44-ab2c-e9d0-865a-35511b5329dd.png)

なお、 EC2 を IPv6 にする方法については [Hide SAK](https://qiita.com/Hide-Zaemon) さんがまとめてくださっているので、そちらをご覧ください。

https://qiita.com/Hide-Zaemon/items/f317e222bf671e2e0752

## TeraTerm での接続

コピーした IPv6 アドレスを `[]` でくくって、 `ホスト(T)` に貼り付けます。
`OK` をクリックして接続します。

例： `[2406:da14:xxxx:xxxx:xxxx:xxxx:572d:222]`

![rapture_20241224062806.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212972/6dd62f13-0fdb-0d3f-9034-3acd81fc9cae.png)
paiza.ioでc# その14

# 概要
paiza.ioでc#、やってみた。
AtCoderシリーズ


# 整数 a,b,cと、文字列 s が与えられます。 a+b+c の計算結果と、文字列 s を並べて表示しなさい。

https://qiita.com/ohisama@github/items/2b8bdcdaf830f08e632a

# 文字列内の、'1' の個数をカウントする

https://qiita.com/ohisama@github/items/7e67d3581f9aece92a12

# 二つの整数a,bの積の偶奇を判定する。

https://qiita.com/ohisama@github/items/89cee43909fbd4473ace

# 文字列Sが、'dream','dreamer','erase','eraser'の組み合わせで作れるかどうかを判定する。

https://qiita.com/ohisama@github/items/f460311d6bbbba04761f

# N個の整数値を受け取り、一度の操作ですべての数を2で割る。割り切れなくなるまで割っていった時、何回割ることが出来るか？

https://qiita.com/ohisama@github/items/9b7dd6d36345adf54089

# 1以上N以下の整数のうち、10進法での各桁の和がA以上B以下であるものの総和を求めてください。

https://qiita.com/ohisama@github/items/c03296f22f26b3f696b0

# N個の整数の重複しない値の数を求める。

https://qiita.com/ohisama@github/items/83c77c7dee18fde99a9d

# 10000円札、5000円札、1000円札がある前提で、合計がN枚、Y円になる組み合わせを求める。

https://qiita.com/ohisama@github/items/bc7889e8a360e0d40665

# 座標(0,0)を出発地点とした場合、時刻ti時点で(xi,yi)にいることができるかどうかを判定する。

https://qiita.com/ohisama@github/items/cface220d72c778c0240

# N枚のカードを交互に自身の得点が最大になるように取っていった場合の、二人の得点の差分を求める。

https://qiita.com/ohisama@github/items/f73b8bb1fa05c8634262

# 500円玉、100円玉、50円玉を使ってXX円を支払う方法が何通りあるかを求める。

https://qiita.com/ohisama@github/items/10799ac96749e56703d9

以上。
【C#】文字列結合時とStringBuilder使用時のパフォーマンス差異

O'Reilly『C#クックブック』 p.40「レシピ2.1 文字列の効率的な処理」について、知らなかったのでまとめた。

C#の文字列を結合する時にどのようなメソッドを実装したほうが効率的か、という項目。

https://www.oreilly.co.jp/books/9784814400188/

<br>

p.40-41 より、コード引用 + 筆者コメントによる補足付与

## 処理対象のInvoiceItemクラス
- 扱うデータのクラスを作成

``` c#:InvoiceItemクラス
public class InvoiceItem{
    //費用
    public decimal Cost { get; set; }
    //費用に関する説明
    public string Description { get; set; }
}
```

## サンプルデータを作成
- サンプルとしてデータがあればいいので、ランダムな値を生成し、それを費用としている

``` c#:GetInvoiceItemsメソッド
static List<InvoiceItem> GetInvoiceItem(){
    //InvoiceItem型のインスタンスを作成
    var Items = new List<InvoiceItem>();
    //ランダム抽出するため、ライブラリのランダムクラスのインスタンスを作成
    var rand = new Random();
    for (int i=0; i<100; i++)
        items.Add(
            new InvoiceItem{
                cost = rand.Next(i), //ランダム値を生成
                Discription = "請求事項#" + (i+1)
        });
    return items;
}
```

<br>

## １. 文字列処理の非効率なメソッド
- 単純な文字列結合のほうが、直感的でコードにもしやすく、多くの場合で採用される
- しかし同時に**アプリケーションのパフォーマンスを落としてしまう場合もある**
    - なぜか？
        - 文字列結合のたびに高価なメモリ確保処理が実行されるため
    - そもそも
        - C#のstring型は不変であり、一度作成された文字列はその内容を変更することができない
    - つまり
        - **+=演算子を使用して結合する文字列を取り出すたびに、新しいstring型のために新しいメモリ領域が作られ、一度結合できればいいものが永遠にメモリ上に残り続ける**
        - これは、結合したい値が増えるほどメモリを逼迫していく
        - C#はガベージコレクション（GC）が自動で行われるが、ガベージコレクションによる不要オブジェクトの回収にも負荷がかかるため、回収負荷の点でも増加にはたらきネガティブ

``` c#:DoStringConcatenationメソッド
static string DoStringConcatenation(List<InvoiceItem> lineItems){
    //文字列をくっつけるための空の文字列を作成
    string report = "";
    foreach(var item in lineItems){
        //「$」および「:C」はC#の文字列間補完
        //作成したサンプルデータから一つずつ取り出してガッチャンコ
        report += $"{item.Cost:C}-{item.Discription}\n";
    }
    //文字列結合した文字列を返す
    return report;
}
```

**※文字列間補完について**

https://learn.microsoft.com/ja-jp/dotnet/csharp/tutorials/string-interpolation

文字列間補完式を使うと、引数として使用したい値をそのまま埋め込むことができる

https://qiita.com/Nossa/items/c2226232b31d7665267f

<br>

## ２. より効率的な文字列処理のメソッド
- **StringBuilderは、可変長の文字列バッファを内部に持つ**クラス
- 初期化時にバッファの容量を確保し、その中で文字列を変更・結合する
    - そもそもバッファとは
        - >「データを一時的に保持しておくための記憶領域」
🔗 [NTT西日本 ICT用語集>バッファ](https://www.ntt-west.co.jp/business/glossary/words-00124.html)
- 再割り当てが必要になった場合でも**はじめに確保したバッファを使いまわすことで、余分なメモリ割り当てとコピーを避けることができる**

``` c#:DoStringBuilderConcatenationメソッド
static string DoStringBuilderConcatenation(List<InvoiceItem> lineItems){
    //まずreportBuilderというインスタンスを生成
    var reportBuilder = new StringBuilder();

    foreach(var item in lineItems){
        //生成したインスタンスreportBuilderでAppendTextを繰り返し呼び出している
        //記法、こちらも詳しくは「文字列間補完」を参照
        reportBuilder.Append($"{item.Cost:C}-{item.Discription}\n");
    }
    return reportBuilder.ToString();
}
```

**※StringBuilderについて**

https://learn.microsoft.com/ja-jp/dotnet/fundamentals/runtime-libraries/system-text-stringbuilder

## 文字列結合を使うか、StringBuilderを使用するかの分岐点

**The answer : ４回以上文字列を結合させるか否か**

p.42
>経験則として、４回以上文字列を結合させる場合にはStringBuilderを使用した方がパフォーマンス的に有利です。



## 余談
業務でもたまに文字列結合の処理を見かけることがありますが、観測範囲ではStringBuilderを使用しているシーンは… なかったかも…

細かいTipsですが、ちりつもでパフォーマンスの悪化に繋がるため、心掛けていきたいです。

<br>
ORMとは

## ORM概要

ORM (Object-Relational Mapping) は、プログラミングの上で、オブジェクト指向プログラミング（以下、OOP）とリレーショナルデータベース（以下、RDB)の連携を実現するプログラミング技法です。ORMを使用することで、データベースの操作を書く際にSQLを直接記述せずに、プログラム言語のオブジェクトを使用して操作を実現できます。

## ORMをなぜ使うのか
OOPとRDB、これら2つがもつそれぞれのデータモデルに違いによって生じる、**インピーダンスミスマッチ**を解消するためです。
RDBは基本的にはテーブル型のデータ構造に対し、OOPは基本的にクラス（データ＋振る舞い）を階層型に持つことが多いです。
テーブルは、DBの考え方で最適化され、クラスはオブジェクト思考の考え方に沿って設計されることを考えると、二つの指向性には差異(インピーダンスミスマッチ)が生まれます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/620431/2c040ee8-6ea4-8d48-df86-cf3daab606cc.png)

## ORMを使うには

### モデルの定義
ORMでは、データベースの構造に対応するモデルを作成します。
例えば、ユーザー表は下記のようにモデルで表現されます。（例はRuby on Rails）

```ruby:ruby
class User < ApplicationRecord
    # 振る舞いなどの処理
end
```
以下、Ruby on RailsのORMライブラリであるActive Recordを例に説明します。

### レコードの作成
新しいレコードを作成する際、Active RecordはSQLを自動生成します。

```ruby:ruby
# 新しいユーザーを作成
user = User.new(name: "Alice", email: "alice@example.com")
user.save

# または、1行で作成
User.create(name: "Bob", email: "bob@example.com")
```
生成されるSQL例

```sql:sql
INSERT INTO users (name, email) VALUES ('Alice', 'alice@example.com');
```
### レコードの取得
データベースからデータを取得するクエリも記述できます。

```ruby:ruby
# 全てのユーザーを取得
users = User.all

# 特定の条件で取得
user = User.find_by(email: "alice@example.com")
```
生成されるSQL例

```sql:sql
SELECT * FROM users WHERE email = 'alice@example.com' LIMIT 1;
```
### レコードの更新
既存のデータを更新する場合

```ruby:ruby
user = User.find(1)
user.update(name: "Alice Wonderland")
```
生成されるSQL例

```sql:sql
UPDATE users SET name = 'Alice Wonderland' WHERE id = 1;
```
### レコードの削除
データを削除する場合

```ruby:ruby
user = User.find(1)
user.destroy
```

生成されるSQL例

```sql:sql
DELETE FROM users WHERE id = 1;
```


## ORMのメリットとデメリット
### メリット
#### 生産性の向上
SQLを直接書かずにオブジェクト指向のコードを書けるため、コード量が減り、読みやすくなります。

#### 安全性の向上
直接SQL書かないため、SQLインジェクション攻撃などの脆弱性を減らすことができます。

### デメリット
#### 学習コスト
特定のORMライブラリの使用法と動作方法の両方を学ぶ必要があります。

#### 複雑なクエリなどは実装できない場合がある
ORMの構造から外れた変更が必要な場合は直接SQLを書かざるを得ない場合もあります。

#### 性能問題 
ORMの内部動作やクエリの最適化失敗などからパフォーマンスの問題が発生することがあります。

## 終わりに
読んでいただきありがとうございました！
ORMは、データ操作を短時間で実装するための有力な手段ですが、クエリへの変換が上手くいってなかったりすることもあるため昨今ではORM不要論なども持ち上がっていたりします。
ORMを利用するか、直接クエリを書くかは開発プロジェクトによっても異なると思いますが、１手段として覚えておくのが良いかと思います。

## 参考文献
https://qiita.com/EaE/items/2af9802a489e2dd6169b

https://qiita.com/minimabot/items/0a3fcc41fd7140dfdc41
toioとプラモデル（無改造編）

# 投稿の背景
先日投稿した「toioとプラモデルの作例（肉抜き編)」では、プラモデルのパーツに穴を開ける必要があり、少し抵抗感がある改造となっていました。。。

https://qiita.com/wotakuro/items/8d9096a7b6b42a0b3f7d

なので、今回はプラモデル自体を破壊することなく、できる組み合わせにチャレンジしたいと思います。

今回は 30mmというBANDAIさんのプラモを使います。
https://bandai-hobby.net/brand/30mm/

# 3mmの穴を活かして無改造で動くプラモデルにする

こちらのプラモデルの最大の特徴が 3mmメートルの穴や突起があちこちにあり、それを連結して自由に改造が出来るということにあります。

この3mmの穴をどうにか toio側につけられれば、色々と出来るのでは・・・ということでやってみました。



まず、3Dプリンターで3mmの穴を開けても精度的に不安がありました。
そこで以下にあるPC-03を埋め込む方針にしました。
https://www.hobby-wave.com/products/op381-384/

レゴのパーツと、このPC-03パーツの間を埋める3Dプリンターのパーツを作れば何とかなりそうです。
（3Dプリンターなくても、エポキシパテなどで自作するでもぜんぜんよさそうです。）

そうして出来上がったのがこちらです。

<iframe width="560" height="315" src="https://www.youtube.com/embed/eKyHQSr64fg?si=F9yvLShmwfo_qupo" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

そして、動作するのがこちらです。

<iframe width="414" height="736" src="https://www.youtube.com/embed/RK0U4VXQ02Q" title="30mmがtoioで動くところ" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

# その他・・・
この記事アップロード直前に気付いたのですが・・・、最近のプラモデルはディスプレイスタンド用に3mmの穴がついている事が多いです。
この穴にうまく繋げてやれば、動くプラモディスプレイも作りやすいのでは・・・と・・・・・
![PXL_20241223_194807642.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35544/2034eaef-74ff-c81e-2651-2a7ec30139f8.jpeg)

このサイズだとバランス悪そうですが・・・
SDガンダムくらいの等身で、いい感じのがあればよさげに見えます。
個人的に実装で気をつけている8つのポイント

今回は私が実装をする際に特に気をつけているポイントをいくつかピックアップしました。

### 1. 単一責任の原則とテスト容易性

クラスや関数には**単一責任**を持たせることが大切です。これにより、テストや保守がしやすくなり、モックや修正にも柔軟に対応できます。
例えば以下のように、各クラスや関数が明確な責任を持つことを意識します。

```python
class User:
    def calculate_age_from_birthdate(self):
        # 年齢を計算する責任だけを持つ
        ...

class UserValidator:
    def validate_user(self, user: User):
        # ユーザー検証の責任だけを持つ
        ...
```

### 2. 命名規則

一貫した命名規則を守ることで、コードの可読性を高め、認知負荷を下げることができます。例えば以下のような規則を意識して命名します。

- 名は体を表すを意識して命名する
- マジックナンバー禁止。必ず命名する
- ぱっと見で理解できない省略語は使わない
- True/Falseの変数や返り値の関数には`has`, `is`などから始める
- 定数は大文字＋アンダースコア（例: `MAX_RETRIES`）
- クラス名はキャメルケース（例: `UserAuthenticator`）

### 3. Docstring と Type Hint

関数やメソッドには**Docstring**と**Type Hint**を追加し、コードの目的や引数・戻り値を明確にします。これにより、他の開発者や自分がコードを理解しやすくなります。

```python
def add_numbers(a: int, b: int) -> int:
    """
    2つの数値を足し合わせる

    Args:
        a (int): 1つ目の数値
        b (int): 2つ目の数値

    Returns:
        int: 足し算の結果
    """
    return a + b
```

### 4. リカバリ対応を意識したアトミック処理

**アトミック処理**とは、**ある操作が全体として完了するか、まったく実行されないか**のどちらかを保証することです。特にデータベースへのCRUD操作で意識しておかないと、インシデント時のリカバリ対応が困難になります。

例えば、振込処理で一部の処理が成功し、別の処理が失敗することを避けるために、**トランザクション**を利用してアトミック処理を実現できます。Djangoでは`transaction.atomic()`を活用します。

```python
from django.db import transaction

with transaction.atomic():
    # 複数のデータベース操作を一度に実行
    update_balance(user)
    ...
    if some_error_condition:
        raise Exception("処理失敗")  # すべての操作がキャンセルされる
```

### 5. メモリ使用量の最適化

大量のデータを扱う際には、メモリ効率を考慮しないと、メモリ超過エラーを引き起こす原因になります。
そのため、リストではなく**ジェネレータ**を使用するなど、メモリ効率を意識した実装を心がけています。
### 6. スパゲッティコードの回避

**スパゲッティコード**とは、コードが複雑に絡み合い、どこで何が起こっているのかが分かりづらい状態です。この状態になると、保守や修正が非常に困難になります。
スパゲッティコードを回避するためには、コードの構造を整理し、各モジュールや関数が明確な責任を持つように設計することが重要です。

### 7. 早期リターン

複雑な条件分岐やネストを避けるために、**早期リターン**を積極的に使用します。これにより、コードがすっきりし、可読性が向上します。

```python
# 早期リターンを使わないとネストが深くなりがち
def sample():
    for item in data:
        if condition(item):
            # 処理
        else:
            # 処理
    # 他の処理

# 早期リターンを使うとネストが浅くなる
def sample():
    for item in data:
        if not condition(item):
            return  # 早期リターン
        # 処理
    # 他の処理
```

### 8. TODOコメントの活用

仕様が未確定であったり、後から修正が必要な場合は、必ず`TODO`コメントをつけて記載します。`TODO`を使うことで、後から曖昧な実装を洗い出し、修正を漏れなく行えます。

```python
# TODO: ユーザーのロール管理機能を実装
def manage_roles(user_id: int):
    pass
```

IDE(例: PyCharm)ではTODOコメントを一括管理できるので便利です。
![スクリーンショット 2024-12-23 23.16.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3314085/d4b4c401-13f8-e985-e9d3-5b385a2fdc76.png)

## まとめ
私が実装を行う際に特に意識しているポイントを紹介しました。
何かしら参考になれば幸いです。
ご拝読ありがとうございました！
2024/12/24主にITとかセキュリティの記事

2025年サイバー脅威予測｜生成AIがもたらす新たなリスクと今後の展望(12/22)
https://www.manegy.com/news/detail/10536/

米国による中国大手科学技術企業・機関へのサイバー攻撃2件発見(12/22)
https://www.afpbb.com/articles/-/3555116?cx_part=top_category&cx_position=2

LINEでURLを開くと「このサイトは安全ではないため閉じてください」と表示が…対処法はある？(12/21)
https://news.yahoo.co.jp/articles/8507e0b25535a24bb4284d7233ab748642595ad2?source=rss

NewsPicks トピックス 『デジタル政策と未来社会』のまとめ(2024.12.16～2024.12.22）
https://blogs.itmedia.co.jp/business20/2024/12/newspicks_2024121620241222.html?ref=rss

ターゲット攻撃とは？巧妙な手口と今すぐできる防御策とは？
https://cybersecurity-park.com/3768/

Accessからサクッと移行してみた(12/22)
https://qiita.com/lamo-shibata/items/2834b1cfbd8cef81eaf5

Microsoft Edgeのセキュリティ機能が2024年の1年間でブロックした攻撃の数は14億件、トラッカーは1兆8000億個以上をブロック(12/21)
https://gigazine.net/news/20241221-microsoft-2024-year-in-review/

iOS18.2.1が月内もしくは2025年1月にリリースか〜Apple社内で試験中(12/22)
https://iphone-mania.jp/ios-591372/

au PAYのニセモノが5万ポイントをチラつかせてアカウントを盗もうとする詐欺増加中(12/22)
https://ascii.jp/elem/000/004/242/4242417/?rss

アイルランド データ保護委員会 メタ社に約410億円の罰金 (2024.12.17)(12/22)
http://maruyama-mitsuhiko.cocolog-nifty.com/security/2024/12/post-5ac709.html

Google Cloud の Assured OSS とは？ Google 検証済 OSS パッケージを活用しソフトウェアサプライチェーンを保護する(12/22)
https://dev.classmethod.jp/articles/google-cloud-assured-oss/

個人情報保護委員会、政府横断のサイバー防衛会議を開催(12/22)
https://www.nikkei.com/article/DGXZQOUA196N40Z11C24A2000000/

「どこか他人ごと」な防災DX、実際どれだけ進んでいる？ 国・自治体が提供しているシステムと自分でできる備え(12/20)
https://codezine.jp/article/detail/19779?p=2

子どもたちのインターネットの安全利用を考えるフォーラム(12/21)
https://www3.nhk.or.jp/lnews/tokushima/20241221/8020022025.html

『マイナ保険証』の「セキュリティは病院に“丸投げ”」大きな負担に診療所院長が苦慮　対策不足だと被害者なのに罰せられる恐れも(12/22)
https://www.fnn.jp/articles/-/805472

“ドコモのプラチナ”や“JRの銀行”が登場！2024年もっとも勢力拡大したポイント経済圏は？(12/22)
https://kakakumag.com/money/?id=21630
ブラウザがHTMLを受け取ってからDOMツリーを構築するまで

## はじめに
ブラウザはアドレスバーに入力されたURLをもとにHTTPリクエストを作成し、Webサーバーへ送ります。

Webサーバーから返されるHTTPレスポンスにはステータスラインやレスポンスヘッダ、レスポンスボディが含まれています。

レスポンスボディにはHTMLや画像などが含まれますが、ブラウザはこのHTTPレスポンスからHTMLを受け取り、解析・表示を行っています。

本記事では、HTMLがどのように解析され、DOMツリーが構築されていくのかの概要についてみていきます。

## アドレスバーにURLを入力してからHTMLを受け取るまで
アドレスバーにURLを入力してからHTMLを受け取るまでの流れを簡単にまとめてみます。
### URLの解析
まず、アドレスバーに入力されたURLを解析し、スキーム（HTTP/HTTPSやFTP、FILEなど）、ホスト（ドメイン名）、ポート番号、パス、クエリパラメータなどの構成要素に分割します。
### DNSによる名前解決
ドメイン名からリクエスト先のIPアドレスを取得します（名前解決）。

まずは`hosts`ファイルやDNSキャッシュ[^1]を確認し、対象のレコードがないかを確認します[^1]。

キャッシュがなければブラウザはOSのDNSリゾルバに問い合わせを行います。
OSのDNSリゾルバは（ISPなどの）ローカルDNSサーバやルートDNSサーバ、トップレベルドメインサーバや権威サーバへと順次問い合わせていき、IPアドレスを取得します。
### TCP接続の確立
IPアドレスが判明したら、ブラウザはそのサーバとTCP接続を確立します（3ウェイハンドシェイク）。
### TLS(HTTPSの場合)
HTTPSを使用している場合には、ブラウザとサーバーの間でTLSハンドシェイクが行われ、通信が暗号化されます。
### HTTPリクエストの送信
ブラウザはHTTPリクエストを作成してサーバーに送信します。
HTTPリクエストにはリソースのパス、HTTPメソッド（GETなど）、ヘッダー情報などが含まれます。
### サーバーからHTTPレスポンスが返される
サーバーはHTTPレスポンスをブラウザに送ります。
HTTPレスポンスにはステータスラインやレスポンスヘッダ、レスポンスボディ（HTML、CSS、JavaScriptなどのコンテンツを含む）が含まれます。

大まかには上記の流れで、アドレスバーにURLを入力してからHTMLを受け取ります。

## レンダリングエンジン
レンダリングエンジンとは、ブラウザ内にあるHTMLの描画のためのコンポーネントです。

HTMLはレンダリングエンジンのHTMLパーサーによって解析されます。
この解析結果をもとに、レンダリングエンジンはDOMツリーを構築します。

レンダリングエンジンにはいくつか種類があり、ブラウザによって採用されているものが異なります。
Google Chrome やMicrosoft Edge[^2]では[Blink](https://www.chromium.org/blink/)というレンダリングエンジンが採用されています。
また、Safari ではWebKit、FireFox ではGecko が採用されています。

ちなみに、ブラウザのシェア率は以下のサイトで確認できます。

https://gs.statcounter.com/

## HTMLの解析
レンダリングエンジンはまずHTMLパーサーによって、HTML文字列に対して字句解析・構文解析を行い、その結果をもとにDOMツリーを構築します。

https://html.spec.whatwg.org/multipage/parsing.html#parsing

### 字句解析
レンダリングエンジンのHTMLパーサーは、HTMLを構成する文字列をトークンと呼ばれる、「意味を持つ最小の単位」へと分割します。
`<!DOCTYPE html>`などが意味を持つ最小の単位となります。
他にも、開始タグを表すトークンや終了タグを表すトークン、文字トークンやコメントトークン、空要素タグトークンなどへとそれぞれ分割されていきます。

https://html.spec.whatwg.org/multipage/parsing.html#tokenization

### 構文解析
字句解析によってトークン化された結果をもとに、構文木と呼ばれる木構造を構築していきます。
トークン化された要素の親子関係、入れ子構造などを階層構造に反映していきます。

### DOMツリーの構築
構文木をもとにして、DOMツリーを作成していきます。
構文木をもとにパーサーがDOMツリーに1つずつ要素を追加していきます。

ここで、`<script>`タグが見つかった場合には、パーサーの処理を一時的に停止してJavaScriptを実行します。
パーサーの処理を一時的に停止するのは、JavaScriptによってDOMツリーの構造に変更がはいる可能性があるためです。
パーサーの処理が停止することはパフォーマンス上もよくないため、実行タイミングをDOMツリー構築後に遅らせる`defer`属性を使用することでパーサーの一時停止を回避する方法をとることもできます。
また、外部スクリプトの場合にはダウンロード中もパーサーが停止してしまいます。
そのような場合には、`async`属性を`<script>`タグに付与することで、ダウンロード中はパーサーを停止させず、ダウンロードが完了し次第パーサーを一時停止してJavaScriptを実行させることもできます。

また、JavaScriptからはこの時点までで構築されているDOMツリーにアクセスすることも可能です。
`getElementById`などで要素を取得する際に、`<script>`タグよりも前に記述されている要素しか取得できないのは、`<script>`タグの後ろに記述されている要素がまだDOMツリーへ追加されていないためです。


以上の流れでDOMツリーが構築されていきます。


https://html.spec.whatwg.org/multipage/parsing.html#tree-construction



[^1]:ブラウザのDNSキャッシュ、OSのDNSキャッシュやルーターのDNSキャッシュを確認します。
[^2]:かつては`EdgeHTML`が採用されていましたが、`Blink`へ移行しています。
Pythonの位置専用引数とキーワード専用引数についてまとめてみた

# はじめに  
Pythonには、引数の受け渡し方法を制御する便利な機能として「**位置専用引数**」と「**キーワード専用引数**」がありますが、私自身まだ実際の開発で上手く活用できていないため、今回まとめてみました。

## 位置専用引数とは？  
位置専用引数は、関数に渡す引数を**位置**（**順番**）でのみ指定可能にする方法です。これにより、引数名を使ったキーワード形式での指定を防ぎます。  

### 宣言方法  
位置専用引数は、関数定義時にスラッシュ（`/`）を使って指定します。スラッシュの前に定義された引数が位置専用引数になります。

```python
def greet(name, /, message):
    return f"{name}さん、{message}！"

# 使用例
print(greet("田中", "こんにちは"))      # OK  
print(greet("田中", message="こんにちは"))  # OK  
# print(greet(name="田中", message="こんにちは"))  # エラー (TypeError)
```

### 特徴と利点  
- **読みやすさの向上**: キーワードを省略することで、シンプルなコードを記述できます。
- **パフォーマンスの向上**: 引数解析（特にキーワード引数のマッピング処理）を省略できるため、処理速度が向上するみたいです。

### 標準ライブラリでの使用例: `math` モジュール  
Pythonの標準ライブラリ `math` モジュールには、**位置専用引数**が活用されています。  

#### `math.pow` の例  
```python
import math

# math.powでは引数が位置専用
result = math.pow(2, 3)  # OK
# result = math.pow(x=2, y=3)  # エラー (TypeError)
print(result)  # 出力: 8.0
```

#### なぜ位置専用なのか？  
`math.pow` のような関数は計算処理に特化しており、頻繁に呼び出されます。そのため、位置専用引数を使用することで以下が実現されます：
1. **コードの簡潔さ**: 順番だけで引数を指定できるため、コードが分かりやすくなります。  
2. **パフォーマンスの向上**: キーワード引数のマッピング処理を省略することで、関数呼び出しのコストが削減されます。


参考: PEP 570 Positional-Only Parametersのパフォーマンスについて

https://peps.python.org/pep-0570/#performance

---

## キーワード専用引数とは？  
キーワード専用引数は、引数を**キーワード形式でのみ指定可能**にする方法です。位置での指定を禁止し、引数名を明示的に指定させます。

### 宣言方法  
キーワード専用引数は、関数定義時にアスタリスク（`*`）を使って指定します。アスタリスク以降に定義された引数がキーワード専用引数になります。

```python
def greet(*, name, message):
    return f"{message}、{name}さん！"

# 使用例
# print(greet("田中", "こんにちは"))  # エラー (TypeError)  
print(greet(name="田中", message="こんにちは"))  # OK  
```

### 特徴と利点  
- **引数の順番ミスを防止**: キーワードを必須化することで、間違った順序で引数を渡すリスクがなくなります。  
- **意図が明確**: 引数名が明示されるため、関数の使い方が直感的に理解できます。

### 標準ライブラリでの使用例: `datetime.datetime`  
Pythonの標準ライブラリ **`datetime`** モジュールでは、キーワード専用引数が使用されているケースがあります。  

#### `datetime.datetime.replace` の例  
```python
from datetime import datetime

dt = datetime(2023, 12, 23, 15, 30, 45)

# replace関数ではキーワード専用引数が活用されている
new_dt = dt.replace(year=2024, month=1)
print(new_dt)  # 出力: 2024-01-23 15:30:45

# 引数名が必須のため、順番を間違える心配がない
# dt.replace(2024, 1)  # エラー (TypeError)
```

#### なぜキーワード専用なのか？  
`replace` の引数はオプションで変更したい値のみを指定することを想定しています。キーワード専用にすることで、どの部分を変更しているのかが明確になり、誤った引数の解釈を防ぎます。

---

## 位置専用引数とキーワード専用引数の併用  
Pythonでは、位置専用引数とキーワード専用引数を**同じ関数内で併用**することが可能です。  

### 併用の例  
```python
def greet(name, /, *, message):
    return f"{name}さん、{message}！"

# 使用例
print(greet("田中", message="こんにちは"))  # OK  
# print(greet("田中", "こんにちは"))  # エラー (TypeError)  
```

### 利点  
1. **柔軟性の向上**: 一部の引数は位置専用にしてシンプルさを保ちつつ、他の引数ではキーワード形式による明確性を維持できます。  
2. **エラーの防止**: 引数の指定ミスを抑制できます。

## まとめ  
今回は位置専用引数とキーワード専用引数についてまとめてみました。
これを機に今後の開発に活かしていきたいです！  
ご拝読ありがとうございました。
[Rails]GeocodingAPI 使ってみた

# 概要
おはこんばんにちわ。お疲れ様です。

RailsでGoogleが提供する**GeocodingAPI**を使ってみましたので、
備忘録として残しておこうと思います。

## 環境
Ruby 3.0.3
Rails 6.1.4

## はじめに

Geocoding APIの[公式ページ](https://developers.google.com/maps/documentation/geocoding/overview?hl=ja)をそっと置いておきます。

今回は、経度と緯度の情報をDBに保存したいので、
バックエンド側で送られてきた住所を元にAPIから経度と緯度を取得しようと思います。
(<font color="red">1ヶ月</font>の使用上限が、<font color="red">1000件</font>のため</font>)

## 結論
以下のようになります。

```geocoding_api.rb

require "faraday"
require "json"
require 'uri'

class GeocodingApi
  def fetch_map_coordinate(address)
    # address = "100-8111 東京都千代田区千代田１−１" など
    
    api_key = ENV['GEOCODING_API_KEY']

    begin
      coordinate = {}
      
      # そのままの文字列ではエラーになるため、エンコードする
      uri_encode_address = URI.encode_www_form_component("#{address}")
      
      # パスに格納
      target_url = "https://maps.googleapis.com/maps/api/geocode/json?address=#{uri_encode_address}&language=ja&components=country:JP&key=#{api_key}"
      response = Faraday.get(target_url)
      body = JSON.parse(response.body)

      if response.success? && body_hash['results'].present? && body['results'][0]['geometry']['location'].present?
        coordinate = {
          # 緯度座標
          latitude: body['results'][0]['geometry']['location']['lat'],
          # 経度座標
          longitude: body['results'][0]['geometry']['location']['lng']
        }
      end

      coordinate
    rescue => e
      puts(Array(e.message).join("\n"))
      return
    end
  end
end

```

## 解説

```
https://maps.googleapis.com/maps/api/geocode/json?address=#{uri_encode_address}&language=ja&components=country:JP&key=#{api_key}
```
* **address**: 取得したい座標の住所を指定します
* **language**: 結果を返す言語を指定します。今回は日本語です。
* **components**: エリアを絞ることができます。今回は日本国内の座標が欲しいので、指定は日本です。詳しくは[こちら](https://developers.google.com/maps/documentation/geocoding/requests-geocoding?hl=ja&_gl=1*1kuqxi5*_up*MQ..*_ga*OTQxNTU3MjY2LjE3MzQ5NzY5MjI.*_ga_NRWSTWS78N*MTczNDk3NjkyMi4xLjEuMTczNDk3NjkzNi4wLjAuMA..#component-filtering)を参照してください。
* **key**: ご自身の`GeocodingAPI`のAPIキーを指定してください。よく使われる`Google Maps API`とは別である点に注意してください。

### addressに関して

#### エンコード

今回は、引数で受け取った値はフォームなどから送られてきた文字列を想定しています。

この場合、直接文字列をパスに指定すると以下のように怒られますので
認識できるように文字列をエンコードする必要があります。

```
URI must be ascii only \"https://maps.googleapis.com/...
```

#### 誤字

**住所の多少の誤字は許容**してくれます。
ほぼ欲しい位置の座標を返してくれます。（厳密にいえば、座標は多少ズレる）

```
# 入力値: 100-8111 東京都千代田区千代田１−１

# レスポンス
{
  "latitude" => 35.68858013029149,
  "longitude" => 139.7497355802915
}
```

```
# 入力値: 1000000 東京千代田区千代１

# レスポンス
{
  "latitude" => 35.689025,
  "longitude" => 139.7578666
}
```

#### 住所指定

`郵便番号`のみでも`都道府県のみ`でも問題ありません。
しかし、**正確さを求めるのであれば**、住所全体を使用した方が良いです。
取得した座標を元に、Googleマップで検索するとずれていることがあります。

## 最後に
今回は、さらっと紹介してみました。
マップ表示は今じゃ当たり前みたいになっているので、GeocodingAPIも使う機会が増えるかもです。

誰かの役に立つことを願って、また次の記事でお会いましょう。
Cloud Run functions × TypeScript 開発をしよう！【Appendix：Cloud Run functions 開発時に見ると幸せになるリンク集】

# Cloud Run functions
### 公式ドキュメント
正直以下2つのドキュメントとGitHubを読んでいたら大体答えが載っている。

https://cloud.google.com/functions/docs?hl=ja

https://cloud.google.com/functions/docs/concepts/overview?hl=ja


### Cloud Run functions(Node.js)のライブラリ(Functions Framework)を使った実装例
https://github.com/GoogleCloudPlatform/functions-framework-nodejs/tree/main/docs

### Functions Framework × Node.js を構築している動画
<iframe width="560" height="315" src="https://www.youtube.com/embed/yMEcyAkTliU?si=dsmpNQluqenuEZEL" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

### Google Developers Codelabs
「function」で調べるとある程度絞られる。Node.jsかどうかは目視で探そう。

https://codelabs.developers.google.com/?text=function

### 本アドベントカレンダー
割と本気で見ると幸せになると思っています。

https://qiita.com/advent-calendar/2024/cloud-run-functions-typescript

あとは随時追加していく予定ですが、これで十分かなと思っています。
PC_MAC_シンボリックリンクの作成

# Macでシンボリックリンクを作成する手順

## 1. ターミナルを開く
Finderで「アプリケーション」→「ユーティリティ」→「ターミナル」を開きます。

---

## 2. シンボリックリンクの作成
以下のコマンドをターミナルに入力して実行します。

```bash
ln -s "<リンク元のパス>" <リンク先のパス>
```

### 例:
以下は、Google Driveのフォルダ「マイドライブ」をデスクトップに「DriveFolder」という名前でリンクを作成する例です。

```bash
ln -s "/Users/username/Library/CloudStorage/GoogleDrive-example@gmail.com/マイドライブ/" ~/Desktop/DriveFolder
```

> **補足**: パス内にスペースが含まれている場合は、パス全体を二重引用符 `"` で囲む必要があります。

---

## 3. シンボリックリンクの確認
シンボリックリンクが正しく作成されたかを確認します。

```bash
ls -l <リンク先のパス>
```

### 例:
```bash
ls -l ~/Desktop/DriveFolder
```

---

## 4. シンボリックリンクの削除（オプション）
作成したシンボリックリンクを削除する場合、以下のコマンドを実行します。

```bash
rm <リンク先のパス>
```

### 例:
```bash
rm ~/Desktop/DriveFolder
```

> **注意**: `rm`コマンドで削除されるのはシンボリックリンクだけで、リンク元のデータは削除されません。

---

## 注意事項
1. **アクセス権限**: リンク元のデータにアクセスする権限が必要です。
2. **正しいパス指定**: パスが間違っている場合、リンクが正しく機能しません。
3. **エスケープ処理**: パス内のスペースはバックスラッシュ（`\`）でエスケープするか、パス全体を二重引用符 `"` で囲んでください。
   - 例: `/Users/example/My\ Folder` または `"/Users/example/My Folder"`

---

これでMacでシンボリックリンクを簡単に作成できます！
Emacs Lisp 超初心者がコマンドを作ってみる 〜 別フレーム結果表示の実験 フレーム作成〜

# はじめに

今までのプログラミングでまぁまぁに動くようになったのですが、
ちょっと使いにくいのは、作業をしている .po のフレームに結果表示がかぶってしまうことです。

::: note info
Emacs ではいわゆるウインドウの事をフレームという。Emacsでいうウインドウはフレームの中で1つまたは分割表示(縦分割横分割etc)されたそれぞれの領域を示す
:::

これが別フレームに、もっと露骨に言えば別画面に出てくれると嬉しいな。

ということで フレーム 関係の操作です。

# 実験 既に指定のフレームがあればそのまま、無ければ作ってくれるといいな …orz

フレームを作成する。 \*scratch\* バッファで実行してみます。

実験スクリプト

````common_lisp
(make-frame '((name . "pepm-frame") (width . 640) (height . 480)))
````

1回目
````text
(make-frame '((name . "pepm-frame") (width . 640) (height . 480)))
#<frame pepm-frame 0x646ba13da080>
````

![pepm-frame,scratch, with #<frame pepm-frame 0x646ba13da080>](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/435164/0cb97805-7ea0-dba6-c833-152df84d5750.png "make-frame 1回目")

2回目
````text
(make-frame '((name . "pepm-frame") (width . 640) (height . 480)))
#<frame pepm-frame 0x646b9b9c2c90>

#<frame pepm-frame 0x646ba13da080>
````
![pepm-frame,scratch, with #<frame pepm-frame 0x646ba13da080>](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/435164/0cb97805-7ea0-dba6-c833-152df84d5750.png "make-frame 1回目")

![pepm-frame,scratch, with #<frame pepm-frame 0x646ba13da080>](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/435164/4b36e957-f966-740f-ec70-71fc555489e2.png "make-frame 2回目")

あれ？ エラーにならないな？ 見ると同一名で幾らでも作れるようです...

::: note info
2回目のあと最初のフレームが1行だけの表示なのは単に再描画されてないだけです。 C-L で再描画すると2回目と同じ画面になります。
:::

## 今回の実験の結果・今後対策(予定)

- make-frame では 同一の名前で幾つでもフレームが作れる

ということで、今後は以下の対策を行った上で採用します。

- 指定の名前のフレームが既に存在するかどうか調べる。
- 既にあればそれを使う。
- 万が一既に同じ名前のフレームが複数あった場合はワーニングを出す(出すけど、実行結果をどれに出すかはこっちで勝手に決める。少なくとも今回のフェイズでは)

今回はここまで。
Javaで「フレームのサイズと表示位置を設定する」の動作を確認してみた

# 概要

Javaで「フレームのサイズと表示位置を設定する」の動作を確認してみました。
以下のページを参考にしました。

https://www.javadrive.jp/tutorial/jframe/index2.html

# 実装

以下のファイルを作成しました。

```Sample.java
import javax.swing.JFrame;

class Sample{
  public static void main(String args[]){
    JFrame frame1 = new JFrame("MyTitle 1");
    frame1.setBounds(100, 100, 600, 400);
    frame1.setVisible(true);

    JFrame frame2 = new JFrame("MyTitle 2");
    frame2.setBounds(400, 200, 400, 600);
    frame2.setVisible(true);
  }
}
```

以下のコマンドを実行しました。

```
$ javac Sample.java 
$ java Sample
```

実行結果は以下の通りでした。

![Screenshot from 2024-12-24 03-28-23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3192024/f8630d30-fb0c-f1e3-84da-d1cf48c86c55.png)

# まとめ

何かの役に立てばと。
【ML初心者アドベントカレンダー】モデルのトレーニングと改良について


# この記事の目的

モデルのトレーニングと改良を実行できるよう、必要な知識を学ぶ。

# トレーニングプロセスの要素

トレーニングを行うにあたり、以下の要素を頭に入れておく必要がある。
各種パラメータは、SageMakerで既存モデルをカスタマイズする際に設定する場合もある。

https://docs.aws.amazon.com/ja_jp/sagemaker/latest/dg/jumpstart-fine-tune.html

## エポック

すべてのトレーニングデータをモデルに通すことを1エポックという。

つまり、モデルにトレーニングデータを何回学習させるかの単位のこと。5エポックの場合は5回繰り返して学習させる。エポックが多すぎると過学習を起こすが、低すぎると学習不足になる。

## ステップ

モデルがパラメータを更新する単位のこと。イテレーションともいう。

モデルは、トレーニングデータを一度ですべてを読み取れるわけではない。通常、何個かの小さなまとまり（バッチ）にわけて学習をする。バッチを処理するたびにモデルがパラメータを更新するため、バッチを処理するたびに1ステップ進む。
以下の計算で求められる。

```
ステップ = トレーニングデータ数 ÷ バッチサイズ
```

## バッチサイズ

モデルが一度に処理をするトレーニングデータの大きさ。

たとえばバッチサイズが100の場合、トレーニングデータ1,000個を処理するためには、1エポックあたり10ステップが必要。
大きいほどメモリ消費量も多くなるが、大量のデータを一度に処理できるようになる。一方で、パラメータの更新頻度が下がるため、汎用的な形にはなりにくい。


## 学習率

ハイパーパラメータのひとつ。
モデルのパラメータが更新されるステップの大きさを制御する。学習率が高すぎると最適解を飛び越えるリスクがあり、低すぎると収束は遅いが最適解にたどり着きやすくなる。


# モデルトレーニング時間を短縮する方法

## 早期停止

トレーニング中にモデルの性能が向上しなくなった時点で、当初決めたエポック数をこなしているかに関わらずトレーニングを終了すること。
時間を節約できる他、過学習を防止できる等のメリットがある。


## 分散トレーニング

トレーニングデータやモデルを複数のGPUやノード（インスタンス）に分散して並列処理をする方法。トレーニング速度がめっちゃ早くなる他、大規模データセットやモデルに対応できる。

もちろん SageMaker でも行える。

https://docs.aws.amazon.com/ja_jp/sagemaker/latest/dg/distributed-training.html

### データ並列

同じモデルを持った複数のインスタンスにトレーニングデータを分割して処理をさせる方法。他のインスタンスとパラメータの更新等の情報を共有する。


### モデル並列

モデル自体を複数のインスタンスに分割すること。複雑なモデルはひとつのインスタンスに収まらない場合があるので、大規模なモデルをトレーニングする時に使う。


# 正則化手法の利点

正則化とは、モデルの過学習を防ぐための手法のこと。
以下のような種類が存在する。

https://docs.aws.amazon.com/ja_jp/machine-learning/latest/dg/training-parameters1.html

## ドロップアウト

トレーニング中、ランダムにいくつかのニューロン（ニュートラルネットワークの中間層のこと）を無効にし、そのエポックでは利用しないようにする手法。
ニューロン同士の過剰な結合を防ぐことができる。

## 重み減衰

トレーニング中にモデルの「重み」（ニューロンの重要度）を少しずつ小さくしていく手法。必要以上に大きな重みをもたないようにすることで、モデルがシンプルになりすぎないようにする。

## L1正則化

重みを小さくしつつ、必要のない重みは「0」に近づける手法。
データに余計な特徴が多いときに使用する。


## L2正則化 

重みを小さく保ちながら、ゼロにはしない手法。ちょうどいい値を探す。
過学習のときはモデルが極端な値をとるので、その防止ができる。


# ハイパーパラメータのチューニング手法

https://aws.amazon.com/jp/what-is/hyperparameter-tuning/


## ベイズ最適化

> ベイズ最適化は、現在の知識に関連してイベントが発生する確率を記述するベイズの定理に基づく手法です。これをハイパーパラメータの最適化に適用すると、アルゴリズムは、特定のメトリクスを最適化するハイパーパラメータのセットから確率モデルを構築します。回帰分析を使用して、ハイパーパラメータの最適なセットを繰り返し選択します。

ハイパーパラメータを試すごとに得られた新しい情報を追加して、次に試すべきハイパーパラメータを選ぶ方法。実装はやや複雑。コストも高いが少ない試行回数で高い精度を達成できる。

## グリッドサーチ

> グリッドサーチでは、ハイパーパラメータのリストとパフォーマンスメトリクスを指定すると、アルゴリズムはすべての可能な組み合わせから最適な組み合わせを決定します。グリッドサーチは良好に機能しますが、特に多数のハイパーパラメータを使用すると、比較的煩雑でコンピューティングの使用量が多くなります。

あらかじめ設定したハイパーパラメータの組み合わせをとりあえず全部やってみて、一番良いものを選ぶ方法。

実装が簡単で確実に最適解を見つけられるが、時間がかかる。また、高次元なハイパーパラメータに対してはもっと時間がかかるので効率が悪い。

## ランダムサーチ

> グリッドサーチと類似の原則に基づいていますが、ランダムサーチはイテレーションごとにハイパーパラメータのグループをランダムに選択します。比較的少数のハイパーパラメータが主にモデルの結果を決定する場合に良好に機能します。


ハイパーパラメータのクループからランダムに選んだ組み合わせを試す方法。
少ない試行回数で良い結果を得られる……かもしれないが、もちろん最適解を見逃す可能性もある。

### Hyperband

> Hyperband は、リソースを動的に再割り当てする多重忠実度ベースの調整方法です。Hyperband では、トレーニングジョブの中間結果と最終結果の両方を使用して、使用率の高いハイパーパラメータ構成にエポックを再割り当てし、パフォーマンスの低いものは自動的に停止します。また、多数の並列トレーニングジョブを使用して、シームレスにスケールします。これらの機能により、ランダム検索やベイズ最適化戦略よりもハイパーパラメータ調整を大幅に高速化できます。

ランダムサーチをもっと効率的かつ高速に行う方法。トレーニングの中間結果と最終結果をみて、よさげなハイパーパラメータの組み合わせについては学習を続行し、だめそうなものについては早々に停止する。
Cloud Run functions × TypeScript 開発をしよう！【おわりに】

# おわりに
というわけでここまで Cloud Run functions を TypeScript で開発するTipsを書き殴ってきました。

Cloud Run functions × TypeScript 開発というよりは、その開発したものを構築する手法を紹介するのがメインになってしまいましたが、Cloud Run functions × TypeScript 開発で重要なことは3日目~13日目のカレンダーの記事で全て書けたかなと思っております。

---
- [開発環境：Node.js 実行環境](https://qiita.com/KyongminS/items/f3848f36d6f2bb7b07b7)
- [開発環境：Functions Framework環境](https://qiita.com/KyongminS/items/bc1f3569f494a6380cdf)
- [開発環境：TypeScript環境 ─ tsconfig.jsonの設定](https://qiita.com/KyongminS/items/a3c3fb6cf88df6098c9d)
- [開発環境：TSの直接実行 × ホットリロード環境](https://qiita.com/KyongminS/items/4a06cfa9e485a60b2166)
- [開発環境：VSCodeでデバッグできるようにする](https://qiita.com/KyongminS/items/fa37f6adc4e9d0c47d39)
- [ビルド：Parcel を使ったTypeScriptのビルド](https://qiita.com/KyongminS/items/dfb190e146858a140d27)
- [ビルド：esbuild を使ったTypeScriptのビルド](https://qiita.com/KyongminS/items/e87b916f8174d233cc84)
- [テスト：JestでTypeScriptの関数をテストする](https://qiita.com/KyongminS/items/d919a1bebca645984d55)
- [テスト：Jestをラクに実行する for VSCode](https://qiita.com/KyongminS/items/04ef4107a9f4b31dac49)
- [デプロイ：Cloud Run functions をデプロイする](https://qiita.com/KyongminS/items/a18487b718c890711105)
- [デプロイ：Terraform でCloud Run functions をデプロイする](https://qiita.com/KyongminS/items/fb787ab645d913a330ab)

---


なかなかニッチな内容であったと思いますが、僕が一からこのカレンダーに書いてあることをやる時に、TypeScriptでのローカル環境構築から本番環境の構築まで系統立てて書かれてある記事がほとんどなく、構築し切るまで大変苦労しました。
このアドベントカレンダーが僕と同じような境遇の皆さんの理解の一助, 道標になっていれば幸いです。

コメントによる編集リクエスト・情報提供は大歓迎ですので、どしどし書き込んでいってください。
BlazorでExcel ライクなグリッドライブラリ「BlazorDatasheet」を使ってみる

# はじめに
これは、[Blazor Advent Calendar 2024](https://qiita.com/advent-calendar/2024/blazor)の24日目の記事となります。

筆者は以前、Excel ライクなグリッドJavaScriptライブラリーの記事を書いています。
現在、仕事で作成しているアプリケーションでも採用しています。

https://qiita.com/yaju/items/bcca337af393f15a6795

そして、今回はBlazor専用のExcel ライクなグリッドライブラリーがあることを知りました。

# BlazorDatasheetの紹介
https://github.com/anmcgrath/BlazorDatasheet

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/78c8d399-f0a4-669e-6a70-b4386ad606ee.png)

## デモ
https://anmcgrath.github.io/BlazorDatasheet/

https://blazorhelpwebsite.com/ViewBlogPost/19069

# サンプル作成
さっそく、サンプルを作成してみましょう。
新規 Blazor Web App プロジェクトを作成します。

## インストール
NuGetで「BlazorDatasheet」を取得します。

## プログラム変更
```cs:Program.cs
builder.Services.AddRazorComponents()
    .AddInteractiveServerComponents();
// 追加
builder.Services.AddBlazorDatasheet();
```

CSSとJSを追加します。

```html:App.razor
    <link rel="stylesheet" href="_content/BlazorDatasheet/sheet-styles.css" />

<body>
    <Routes />
    <script src="_framework/blazor.web.js"></script>
    <script src="_content/BlazorDatasheet/blazor-datasheet.js" type="text/javascript"></script>
</body>
```

カウンターページに追加します。

```html:Counter.razor
@page "/counter"
@using BlazorDatasheet
@using BlazorDatasheet.Core.Commands.Data
@using BlazorDatasheet.Core.Data
@rendermode InteractiveServer

<PageTitle>Counter</PageTitle>

<h1>Counter</h1>

<p role="status">Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

<br />
<br />
<Datasheet Sheet="sheet"/>
<br />
<button class="btn btn-primary" @onclick="Test">Test</button>

@code {
    private int currentCount = 0;
    private Sheet sheet = default!;

    private void IncrementCount()
    {
        currentCount++;
    }

    protected override void OnInitialized()
    {
        sheet = new Sheet(3, 3);
    }

    private void Test()
    {
        sheet.Cells[0, 0].Value = "Test1";
        sheet.Range("B2").Value = "Test2";
        sheet.Cells.SetValue(2, 2, "Test3");
        sheet.Commands.ExecuteCommand(new SetCellValueCommand(2, 0, "Test4"));
    }
}
```

## 実行
3行と3列が作成されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/8a1ed367-d3df-2d14-994b-4a2678da454f.png)


### Testボタンクリック
各セルに値がセットされます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/e0de5f6f-4a8c-7289-11b2-0afbd0fc7685.png)

# 最後に
予定では、Web API コントローラーを追加する記事を書くつもりだったのですが、筆者がWeb API コントローラーを追加したのは、BlazorServerの.NET 7の時で、後に.NET 8で移行しました。
今回、記事用に新規 Blazor Web App プロジェクトを作成した時に、想定していたのと違って`builder.Services.AddControllers();`が追加できないなど、もう記事を書くのに間に合わないと急遽変更しました。

Excel ライクなグリッドライブラリの`Handsontable`を何年にもわたってイジり倒していますので、そことの違いなど、`BlazorDatasheet`をこれから色々さわって記事を書いていきたいです。
生成AIさっぱりなエンジニアが、家事をサポートするコンシェルジュを作ろうとした試行錯誤


# 三行にまとめると

* 自宅で使っている電化製品の取扱方法や料理のレシピを答えるRAGを作ってSlackから呼び出せるようにしたよ
* AWS Bedrock Agent + ナレッジベース + Chatbot でノーコードで作れたよ
* 生成AI初心者でも2時間くらいで作れたよ

以降は試行錯誤した際の備忘録です。グダグダしておりますのでご了承ください:bow:

# 自己紹介

はじめまして、@ytytytと申します。
LITALICOでは社内で使われているツールやサービスサイトのインフラ部分の保守・運用を行ってます。

## 生成AIと私

毎日生成AIの話題が尽きませんね！
私自身は業務で使う機会はほぼなく、個人的にちょっとかじってみたもののうまく使えていません。
生成AIの展開の速さに追いつけなくなり、苦手意識すら芽生えている状態でした。

# そんな私がなぜ？

## 前提

突然ですが、私は家事全般が苦手です。
特に、一定期間しか使わないシーズンもので、細かく分解できる電化製品のお手入れが苦手です。
お手入れを怠ると健康に影響があるものはもっと苦手です。

最近では加湿器の清掃が億劫で仕方ありません。
一般的な加湿器の洗い方として、お湯をちょびっと入れて、クエン酸をぶち込んで放置して、石化したカルキをこそげ落とせばいいだけと知っています。また、お手入れが簡単で、パーツが少なくシンプルな構成なものを選んでいます。
やれば大したことないのに、なぜか洗えずにいました。

## 何が面倒なのか

何が面倒なのか考えるために可能な限り細分化してみます。

```
1. 取扱説明書一式を保管している収納ボックスを棚から取り出す
2. 収納ボックスから加湿器の取扱説明書を探して抜き出す
3. 取扱説明書から清掃ページを探して開く
4. 清掃に必要な道具を用意する
5. 取扱説明書に従ってパーツを分解する
6. 取扱説明書に従ってパーツごとに清掃する
```

この中で私が特に面倒なのは①~③です。

過去に説明書をデジタルデータに置き換えたこともありましたが、型番検索やPDF探しが手間で気力が尽きてしまいます。  

思い返せば、洗濯やごみ捨ての分類は特に苦ではありません。分類を忘れてしまっても、その場ですぐにわかるからです。逆に、もし食洗器を導入すれば苦しむことは確実です。「あれ？この食器いけたっけ？」と毎回悩むと思います。

仕事でも何かを探す時間はもったいなく感じるし、探す時間をできるだけ短くするか、そもそも探さなくてすむようにしたいタイプです。

調べものは検索含めて好きなので意識していなかったですが、探す行為が課題だったようです。
　
## RAGを知ったきっかけ

情報収集していると「社内文書検索」「QAシステム」という文字をよく見かけるようになりました。
そこからRAGを知りました。専門知識がないと難しいんでしょう…？としり込みしていましたが、Bedrockを使えばノーコードで構築できるとわかり、早速試してみることにしました。

# 成果物

Slackから一言で我が家の加湿器の清掃方法がわかるようになりました:v:

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3947062/a8c0f2cb-170f-4e12-9c02-45c3e37ab249.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3947062/6e29ab8e-c9d9-3487-2593-83ed2a8a265d.png)

この仕組みができてから、なんと週1の頻度で加湿器の清掃を続けられています！

# 手順

1. 使用するモデルをリクエスト
2. S3バケットを作成
3. 取扱説明書(PDF)をS3にアップロード
4. Bedrock Knowledgebasesを作成
5. Data sourceを同期
6. Bedrock Agentsを作成
7. Slackにチャンネルを作成し、AWSアプリを追加
8. Chatbotを作成しSlackチャンネルとつなぐ

Bedrock周りの手順はこちらの記事を参考にさせていただきました。

https://qiita.com/minorun365/items/24dfb0ea3afde6ed0a56

SlackからRAGとやりとりできるようになるまで、後述の詰まりポイントに引っかかったことを含めても2時間ほどでできました（SlackワークスペースとAWSアカウントはもとから私用のものなので、アカウントを用意する時間は含まれておりません）。

# 個人的つまりポイント

* どのモデルを使うかわからない

各モデルの説明を調べましたが、このまま読んでいても分からないだけでどんどん時間が溶けると思い、ひとまず`Claude 3 Haiku`を使うことにしました。
東京リージョンで使えるモデルの中で料金がお手頃だったからです。

:::note info
今はバージニア北部で構築しなおし、埋め込みモデルは`Titan Text Embeddings V2`・テキストモデルは`Amazon Nova Micro`を使っています。
:::

* ナレッジベースとAgentsで使うモデルの種類が違うことに気づかなかった

事前に公式ドキュメントを読んでいれば、こんな間違え方をしないと思いますが…。

公式ドキュメントや記事を読んで、用語を頭に入れました。

https://docs.aws.amazon.com/ja_jp/bedrock/latest/userguide/key-definitions.html

https://dev.classmethod.jp/articles/introduction-2024-aws-bedrock/

# 感想

生成AIを試せて、純粋にとても楽しかったです！

登録データによっては誤った回答もありました。
食パンをトーストする際の火加減を聞いたら、フライパンで強火で11分焼くように言われたり…。

回答の正確さを上げるためには、登録したデータの内容や普段どういう運用をしているのか詳しく理解する必要があると感じました。
AIが扱いやすいデータに正規化したり処理したりメタデータを追加することで、より正確な回答をしてくれるのではないかと思っています。今後そのあたりも試す予定です。

AIと仲良くされている皆様、ぜひコツを教えていただけると嬉しいです。

# 参考にした記事

https://dev.classmethod.jp/articles/introduction-2024-aws-bedrock/

https://qiita.com/minorun365/items/24dfb0ea3afde6ed0a56

https://qiita.com/hayao_k/items/3478b43d73f576a591a1
改行入り機密情報を .env から GHA 用 secret にそのまま貼ると動かない

:::note info
コンテキスト
サービスアカウントの秘密鍵など、複数行にわたる認証情報を開発環境と GitHub Actions から利用したい
:::

## TL:DR;

ローカル環境の `.env` などで改行を `\n` で設定している場合、GitHub Actions 用 secret に設定する際には `\n` ではなく改行コードで設定する必要がある

## ローカル開発環境での設定

ローカル開発環境で `direnv` や `.env` ファイルを使って環境変数を設定する場合、改行は `\n` で表現する：
```.env
# .env
HIMITSU_KEY="-----BEGIN PRIVATE KEY-----\nhello\n-----END PRIVATE KEY-----"
```

この設定は実行時に適切に解釈され、下記のように改行が反映される：
```bash
$ echo $HIMITSU_KEY
-----BEGIN PRIVATE KEY-----
hello
-----END PRIVATE KEY-----
```

## GitHub Actionsでの設定

一方、GitHub Actions 用の secrets では、実際の改行を含めて設定する必要がある:

```plaintext
# 良い例
-----BEGIN PRIVATE KEY-----
hello
-----END PRIVATE KEY-----
```

### 悪い例

```
# 悪い例
-----BEGIN PRIVATE KEY-----\ndo not do this\n-----END PRIVATE KEY-----
```

Workflow run において、secrets を `echo` しての値確認はセキュリティリスクがあるため避けたく、このような状況においてのデバッグでは、「ローカルで設定している値とまったく同じものを secrets に設定できているか」という観点でチェックしがちなのでハマることがある。

## とにかく起きているエラーの再現が重要

エラーの親切さはどうしても利用サービスに依存してしまうが、下記のように **キーが全く設定されていない** 旨のエラーが出ている状況なら、そもそもシンタックスに問題があることに気付きやすくなる:
> Connection with BigQuerySqlClient to dataset name ****** failed. Please check if you configured the credentials at all and provided the right credentials values. You can be also denied access or your internet connection may be down. The actual reason given is: No key could be detected.

`invalid key` のような不親切なエラーだと厳しいかも。
いや、まぁ気づけるか。
魔法のランプでビジネス拡大！

#### ピンチヒッターの乾です。
訳ありまして、突然の登板を依頼され、クリスマスイブである24日目を担当です。

入社して1年以上が経った今、当社がMS（Azure）ビジネスだけでなく、AWSビジネスも展開できることを、AWS基盤を活用したビジネスをしているメンバと共に、AWS re:Invent 2024に参加（私自身は2019年以来の2度目）してきたことも踏まえてお伝えしたいと思います。

## AWS re:Invent 2024
2度目でしたが、5年前の前回と大きくは変わっていない印象でした。世界中から6万人、日本から1700人ほどが参加する規模も変わらず、ラスベガスの街はAWS参加者一色に！（F1の名残がまだありました）

### ●アップデート
毎年恒例ですが、AWSの「魔法のランプ」から今年も100個以上のサービス/機能が出てきましたが、特にAmazon.comのCEOであるアンディ・ジャシー自身が凱旋して、生成AI基盤「Amazon Nova」をアナウンスしたのは驚きでした！　今となっては、”AmazonはAWSの顧客”、だからでしょうか？？　

### ●生成AI動向
ユースケースとして、現在は
・情報検索
・問合せ対応（チャットボット）
・高度な分析
といった、社内の生産性向上という目的のもとでの活用されています。
そして、RAGに加えてAgentが加わり、生成AIという機能は、今年は当たり前のようにアプリケーションに組み込まれることなるでしょう。

### ●Agent元年
最近のAgentは以下のようなスペックを有しており、他システム連携、ナレッジベース連携など様々な機能を載せることができるようになります。
| **AIエージェント** | **プロセッサ** | **メモリ** | **ストレージ** | **特徴** |
|------------------|---------------|-----------|---------------|----------|
| GPT-4            | 8-core        | 16GB      | 1TB SSD       | 自然言語処理、高度な会話能力 |
| BERT             | 4-core        | 8GB       | 512GB SSD     | 意味理解、文脈認識 |
| T5               | 6-core        | 12GB      | 512GB SSD     | 多言語対応、高速処理 |

## AWSビジネス拡大！
AWS基盤を活用したビジネスとして、業務DXシステム／ECサイト／CRMシステムなどの実績・事例がありますが、来年は大きな案件の受注を目指して提案に参画しようと思っています。きっと「魔法のランプ」が願いを叶えてくれるでしょう！！


## おまけ
ラスベガスでは、AWSに魅せられただけでなく、同行チームメンバの料理の腕も魅せられました。エビチリ、麻婆茄子、パスタなどなど、仲が深まる晩餐を楽しみました。
また来年もAWS re:Invent 2025報告がきっとあることでしょう！　お楽しみに:relaxed:

![Image.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3640952/bb4cc8ff-2837-d48e-7fec-3886e7423025.jpeg)
ロングネームで保存する

# 挨拶

今年は書く予定なかったのですが、24日が空いてたので投稿です。

# 概要

Mayaでデータを検証する際に、maで保存してテキスト見る事が多いかと思います。
意外と知られてないみたいなので、ちょっとした便利トッピクスを紹介します。

# 保存オプション

![2024-12-24_01h54_14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99554/e40f61e1-caf7-ded6-1c2b-9fd187acac6f.png)

デフォルトだとショートネームで記述されますが、そのままだとドキュメントとにらめっこする事になり、正直効率がよくありません。画像のここにチェックを入れると、アトリビュート名をロングネームで保存できます。

![2024-12-24_02h08_08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99554/a5d5a4f7-09f7-0197-6ce8-27a3489a1519.png)

と言う事で、ここにチェックを入れて出すだけで、ドキュメントを見なくても大体のアトリビュートの意味が分かるようになります。特に隠しアトリビュートの解析や、似たような名前の多いシェーダー周りに役立つかと思います。

# 注意

普段の作業にはデータサイズが増えるだけで何もメリットがないので、設定を元に戻すのを忘れないようにしましょう。
Cloud Run functions × TypeScript 開発をしよう！【発展編：Cloud Schedulerで Cloud Run functionsの定期実行をする】

# はじめに
この記事では以下のことについて話しています。
- Terraform で Cloud Scheduler を構成する方法
- Cloud Run functions と Cloud Scheduler をお得に組み合わせて使うアーキテクチャ

---

この記事では Cloud Schedulerの構成方法と、Cloud Run functionsと組み合わせて使うときのコツみたいなのを紹介したいと思います。Cloud Scheduler で呼び出す Cloud Run functions 関数は認証有りにしておきます。

# 前準備
Cloud Scheduler を初めて使う場合はAPIを有効にしておきましょう。

```bash
gcloud services enable cloudscheduler.googleapis.com
```

# Terraform で Cloud Scheduler を構成する
まずCloud Schedulerのリソースを定義します。Cloud Scheduler は認証付きのエンドポイントを呼び出す際、メールで指定されたサービス アカウントに基づくIDトークン（OIDCトークン）をヘッダーに付与して呼び出します。

```tf: main.tf
resource "google_cloud_scheduler_job" "scheduler" {
  name             = "functions-scheduler"               # スケジューラ名
  description      = "7:00/13:00/17:00で定期処理を実行する" # スケジューラの説明
  schedule         = "0 7,13,17 * * *"                   # 定期実行の設定
  time_zone        = "Asia/Tokyo"                        # タイムゾーン
  region           = "asia-northeast1"                   # リージョン
  attempt_deadline = "180s"                              # HTTP リクエストのタイムアウト
  
  http_target {
    http_method = "GET"
    # Cloud Run functions のエンドポイント
    uri         = google_cloudfunctions2_function.default.service_config[0].uri
    
    oidc_token {
      # 認証付きのエンドポイントを呼び出せるサービスアカウントのメールアドレス
      service_account_email = "${data.google_project.project.number}-compute@developer.gserviceaccount.com"
      # Cloud Run functions のエンドポイント。uri と同じにする
      audience              = google_cloudfunctions2_function.default.service_config[0].uri
    }
  }
  
  depends_on = [
    google_cloudfunctions2_function.default
  ]
}
```
いざ `terraform apply` します。

```bash
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
```

Cloud Scheduler のページに移動します。特定の時間にならないと実行確認できないので「操作」の三点リーダをクリックして「強制実行」してみます。実行が成功すれば「最後の実行のステータス」のところが緑になります。

![スクリーンショット 2024-12-24 1.54.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/374872/8629fbaf-ba5a-ff37-eb31-4e736f82c6a8.png)


### 参考
https://cloud.google.com/scheduler/docs/http-target-auth?_ga=2.111758214.-322356660.1683982375&hl=ja#create-a-cloud-scheduler-job-that-uses-authentication

# Cloud Run functions と Cloud Scheduler をお得に使う
Cloud Schedulerは3つまでジョブを毎月無料で使えます。なので時間帯によって実行する処理がある場合は、関数の中で時間によって処理を分岐させると結構なコスト削減になります。

```ts
import { toZonedTime } from 'date-fns-tz'
const date = new Date()
// 東京の現在時刻
const dateTokyo = toZonedTime(date, 'Asia/Tokyo')
const hour = dateTokyo.getHours();

// 7時, 13時, 17時でロジックの呼び分け
if (hour === 7) {
    // 7時に実行する処理
} else if (hour === 13) {
    // 13時に実行する処理
} else if (hour === 17) {
    // 17時に実行する処理
}
```

見た目は汚くなりますが、ドケチに使ってナンボです。アンチパターンだとか**クリーン**アーキテクチャとかDDDとか知りません。

# 終わりに
Cloud Scheduler で 認証付きの Cloud Run functions 関数を呼び出す方法を紹介しました。発展編とは銘打っているものの特に発展っぽいことはしていませんが、Cloud Schedulerの毎月の無料枠を積極的に使っていきましょう（脈絡がおかしい）。