OmniAICreator/Qiita-1.07M huggingface dataset:
XBeeを使った自宅状態遠隔見守りサービス実現方法の紹介

自宅状態の遠隔見守りを以下のしくみを使って実現しました。

- XBee(ZigBee)近距離無線通信
- ESP32マイコン(Wifi内臓Arduino準拠マイコン)
- AWS(APIGateway,lambda 等)
- Line Messaging API

<img src="https://websa12.z11.web.core.windows.net/image-data/202504/20250427/1745731501205.png" alt="image" width="70%"/>

👆実現概要構成

## 構成および処理概要

<img src="https://websa12.z11.web.core.windows.net/image-data/202504/20250427/1745732923930.png" alt="image" width="70%"/>  

👆デバイス構成  

&nbsp;  

<img src="https://websa12.z11.web.core.windows.net/image-data/202504/20250427/1745734344885.png" alt="image" width="20%"/>  

👆Line受信画面  

### 処理概要

1. CdSセル（光センサー）で電灯のON/OFFを検出します。
1. XBeeエンドデバイスで、光センサーの検出値を定期周期で無線送信します。
1. XBeeコーディネータ受信した光センサーの検出値を、シリアル通信経由でESP32マイコンで処理します。
1. ESP32で光センサーの検出値をjson形式に整形し、Wifi経由で`AWS lambda`へデータ送信します。
1. `AWS lambda`で光センサーの検出値変化が閾値を超えたら電灯操作が行われたと判断し、`Line Messaging API`で通知します。

## 各処理詳細

各処理の詳細をこれからおいおい記事にします。  
なんだかんだハマったところ、手順などをまとめるつもりです。  

[>>(次の記事) 2. XBee受信側の接続と設定](https://docs.saurus12.com/device/xbee_setting_1)

## 👇関連記事

- [AWS Lambda でLine bot を作成する](https://docs.saurus12.com/aws/line_bot)  
- 1 . XBeeを使った自宅状態遠隔見守りサービス実現方法の紹介
- [2 . XBee受信側の接続と設定](https://docs.saurus12.com/device/xbee_setting_1)
- [3 . XBee送信側の接続と設定](https://docs.saurus12.com/device/xbee_setting_2)
- [4 . XBee接続確認とセンサーの接続](https://docs.saurus12.com/device/xbee_connect)
- [5 . ESP32マイコンのツール設定と接続](https://docs.saurus12.com/device/esp32_preparation)
- [ESP32-C3-WROOM-02-N4 設定方法解説](https://docs.saurus12.com/device/esp32_c3_wroom_setup)

## 本記事へのリンク

<img src="https://websa12.z11.web.core.windows.net/image-data/202504/20250430/1745971492767.png" alt="image" width="30%"/>

### [https://docs.saurus12.com/device/monitoring_introduce](https://docs.saurus12.com/device/monitoring_introduce)

<iframe width="560" height="315" src="https://www.youtube.com/embed/eMVnjbswrp8?si=xhse5Wf8jAuS-XWI" 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>

[keywords]  
XBee ESP32 AWS lambda Line シリアル通信

[XBeeを使った自宅状態遠隔見守りサービス実現方法の紹介](https://docs.saurus12.com/device/monitoring_introduce)

更新日:2025年05月25日
【Rails】Renderにデプロイする際、master.keyの設定が出来てるのにエラーが出た時の解決法

## はじめに

Railsチュートリアルの学習の際、
Renderにデプロイするところでかなり詰まりました。

同じエラーで詰まる人が出ないように、
そして自分の復習のために解決法をまとめておきます。

あくまで、個人の学習段階、
Railsチュートリアルの学習の際に出たエラーを解決する方法としてご覧ください。

※この記事では、  
**RAILS_MASTER_KEY**の環境変数を設定し直す方法をまとめています。

---
## 原因
- 前環境のmaster.keyのキャッシュが残っていた。

Renderにデプロイする際に自分の使ってるRailsアプリとの連携が必要です。

そのために使うのが**master.key**なのですが、
今回はキャッシュが残っていたせいでうまく連携が取れてませんでした。

クラウド環境で学習した所までをGitHubにあげて、
途中からローカル環境にクローンして学習を始めたのですが、
その際にRender側とローカル側での環境変数が異なったのが原因でした。

---
## 解決策
- `環境変数 RAILS_MASTER_KEY` を解除して新しく設定する。
---
## 手順
```bash
1. 環境変数 RAILS_MASTER_KEY を解除する
unset RAILS_MASTER_KEY

確認コマンド：
echo $RAILS_MASTER_KEY
何も表示されなければOK！

２.環境変数　RAILS_MASTER_KEY を設定する
cat config/master.key←表示された内容をコピー
export RAILS_MASTER_KEY='config/master.keyの中身をここに貼り付け'

３.master.key をGitの追跡範囲外にする(設定してない場合)
touch .gitignore　←gitignoreファイルの中身を確認
ファイルの中に config/master.key という行がなければ、新しい行として追加します。


４. Gitで変更をコミットしてプッシュする
git add .
git commit -m "好きなコメント"
git push origin main
※master.keyは絶対に中身をGitHubにプッシュしないようにしてください。
(３.の手順に従ってればGitの追跡範囲外に設定されてるはずです。)

6. Renderの環境変数を設定する
Renderのダッシュボードに行き、
「Environment」タブで新しい環境変数を追加します。
	•	Key: RAILS_MASTER_KEY　←そのまま同じ内容を打ち込みます。
	•	Value: config/master.key の中身をコピーアンドペーストしてください。
```
全コマンドまとめ
--
```bash
# 環境変数解除
unset RAILS_MASTER_KEY

# 環境変数設定
export RAILS_MASTER_KEY='config/master.keyの中身をここに貼り付け'

# gitignoreの編集・確認
touch .gitignore

# git操作
git add .
git commit -m "好きなコメント"
git push origin main
```
まとめ
--
•	環境変数 RAILS_MASTER_KEY を解除する
•	環境変数 RAILS_MASTER_KEY を設定する
•	念の為、Render側にもRAILS_MASTER_KEY を設定する
•	セキュリティを守るためにも、扱いには注意する


以上です。最後まで読んで頂きありがとうございました！
【ChatGPT】My GPTの機能に変化がありました。


　どうも、カーブミラーです。

　今回は、My GPTの機能に変化がありました。

:::note info
本記事は、ChatGPTについての
記事です。　　　　あしからず。
:::
　ChatGPT等の動作状況を知らせる
　　OpenAIのステータスページ。
　　　[OpenAIステータス](https://status.openai.com/)
***
　こちらも載せますね。
　　[ChatGPTが使えない？ログインできない・反応しない原因やエラー時の対処法を解説](https://aismiley.co.jp/ai_news/chatgpt-error-resolution-explanation/)
***
　著作権法については
　　こちらを参照するのが
　　　良いのではないかと。

　文化庁レポートをもとにした
　　著作権法についての動画（約20分）
　　　[かいちのAI大学　【警告】ChatGPTの画像生成は危険です...【4o Image Generation】](https://youtu.be/8VLUnG-iyo0?si=rAfjMboqmNqppDGq)
***
　では、さっそく【本題】です。

　今回は、
　　ChatGPT My GPTの機能に
　　　変化がありました。

　DALL-E3に代わり、
　　4o画像生成
　　（4o Image Generation）が
　　　　登場しています。
![Screenshot_20250426-173638~2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3457238/96597acf-a362-4d23-8bf8-6fc8366c5a36.png)




　ということは、
　　GPTsに4o Image Generationを
　　　組み込むことが可能に！😸

　公式ヘルプページも
　　昨日更新されていたようです！
　　　[Creating images in ChatGPT](https://help.openai.com/en/articles/8932459-creating-images-in-chatgpt)

　要点
　・GPTs の画像生成モデルが
　　　段階的に 4o へ切替中
　・テキスト描画精度UP
　　　／透明背景・部分編集に対応
　・従来の DALL·E は
　　　〈DALL·E GPT〉で引き続き利用可
　・DALL·E 生成画像には
　　　 “Created with DALL·E” ラベル

　[ChatGPT研究所さんポスト](https://x.com/ctgptlb/status/1916146271079035368?t=4i_nWlhekZmpBgsH9HtWbA&s=19)

　みなさんの
　　作品制作の糧に
　　　なれば、幸いです。

　今回は、ここまで。
テキストファイルクローズ

指定したTextStreamをクローズする
~~~
'++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
' テキストファイルクローズ
'++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Public Sub S_File_CloseTextFile( _
        ByRef aTs As TextStream)
    If Not aTs Is Nothing Then
        aTs.Close
        Set aTs = Nothing
    End If
End Sub
~~~
テキストファイルリード

指定したTextStreamのテキストファイルをリードする。
1行読み込み、1行ごと全ファイル読み込み、全ファイル読み込みが指定可能
~~~
'++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
' テキストファイルリード
'++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Public Function F_File_GetReadTextFile( _
        ByRef aRtn As Variant, _
        ByRef aTs As TextStream, _
        ByVal aTextSpec As E_FILE_SPEC_TEXT, _
        Optional ByVal aPath As String = "") As Boolean
    Dim wkRet As Boolean: wkRet = False
    Dim wkRtn As Variant
    
    '引数チェック
    If aTs Is Nothing Then
        'パスでファイルオープンできなかった場合は終了
        If F_File_GetOpenTextFile(aTs, aPath, aIOMode:=ForReading, aCreate:=False) <> True Then
            Exit Function
        End If
    End If
    
    '行ごとにリードする場合
    If M_Common.F_CheckBitOn(aTextSpec, E_FILE_SPEC_TEXT_LINE) = True Then
        '全行リードする場合
        If M_Common.F_CheckBitOn(aTextSpec, E_FILE_SPEC_TEXT_ALL) = True Then
            '最終行でない間はループ
            Do While aTs.AtEndOfStream <> True
                '行を配列で設定
                wkRtn = M_Common.F_ReturnArrayAdd(wkRtn, aTs.ReadLine)
                wkRet = True
            Loop
        '1行読み込む場合は最終行でなければリード
        ElseIf aTs.AtEndOfStream <> True Then
            wkRtn = aTs.ReadLine
            wkRet = True
        End If
    'ファイル全てを読み込む場合
    ElseIf M_Common.F_CheckBitOn(aTextSpec, E_FILE_SPEC_TEXT_ALL) = True Then
        wkRtn = aTs.ReadAll
        wkRet = True
    End If
    
    If wkRet = True Then
        aRtn = wkRtn
        F_File_GetReadTextFile = True
    End If
End Function
~~~
OpenAI Agent SDKで会話履歴を保持する

# はじめに
OpenAIのAgent SDKに入門していて、会話履歴を、Runnerをまたいで引き継ぐ方法がわからなかったので、確認しました。

# 方法

以下のように、`Runner.run`の戻り値からlast_response_idを取得して、次の`Runner.run`の`previous_response_id`に渡せばよいです。

```Python
import asyncio

from agents import Agent, Runner

agent = Agent(
    name="assistant",
    model="gpt-4o-mini",
    instructions=("あなたは親切なアシスタントです"),
)


async def main():
    result = None
    while True:
        question = input("> ")
        result = await Runner.run(
            agent,
            question,
            previous_response_id=result.last_response_id if result else None,
        )
        print("---- 最終出力 ----")
        print(result.final_output)


asyncio.run(main())
```

# 参考
公式のexampleの`previous_response_id.py`にやり方が書いてありました。

https://github.com/openai/openai-agents-python/blob/main/examples/basic/previous_response_id.py
テキストファイルオープン

指定したファイルをオープンし、TextStream型で返却する
~~~
'++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
' テキストファイルオープン
'++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Public Function F_File_GetOpenTextFile( _
        ByRef aRtn As TextStream, _
        ByVal aPath As String, _
        Optional ByVal aIOMode As IOMode = ForReading, _
        Optional ByVal aCreate As Boolean = False) As Boolean
    Dim wkRet As Boolean: wkRet = False
    Dim wkRtn As TextStream
    Dim wkFso As New FileSystemObject
    
    '引数チェック
    On Error GoTo PROC_ERROR
    If aPath = "" Or Dir(aPath) = "" Then
        'ファイルパス指定なし、またはファイルが存在しない場合は異常終了
        Exit Function
    End If
    
    'ファイルオープン
    Set wkRtn = wkFso.OpenTextFile(aPath, aIOMode, aCreate)
    On Error GoTo 0
    'エラー無しの場合
    If Not wkRtn Is Nothing Then
        Set aRtn = wkRtn
        wkRet = True
    End If
    
PROC_ERROR:
    F_File_GetOpenTextFile = wkRet
End Function
~~~
【Linux】grep(global regular expression print)コマンドについて

## grep(global regular expression print)コマンド
　テキストデータ内の文字列検索を行うには、**grep(global regular expression print)コマンド**を使用する。grepコマンドで指定する検索文字列は、「Yuko」のように文字列をそのまま指定するだけでなく、**正規表現**を使用することも可能である。

　正規表現とは、記号や文字列を組み合わせて、目的のキーワードを見つけるためのパターンを作り、検出する手段である。
　任意の文字である「a」や「^」などの記号を使用してパターンを作成している。記号は**メタキャラクタ**と呼ばれるもので、それぞれ意味がある。

## 参考文献
・LPICレベル1スピードマスター問題集: Version5.0対応 101試験、102試験対応 単行本 – 2019/9/1
山本 道子 (著), 大竹 龍史 (著)
p.46-48
【Kotlin】ChannelでJobQueue的なことをする

### はじめに
とある非同期処理を1件ずつ順に処理したかったので、Kotlin Coroutinesの[Channel](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-channel/)を使用して実装しました。

簡略化して残しておきます。

#### JobQueueクラスを作成
実際に処理を積んで実行してくれるクラスを作成します。
```kotlin
class JobQueue {

    private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
    // Channel作成（ジェネリクスにsuspendの関数型を指定しているのでラムダ式のみ送受信します）
    // Channel.BUFFEREDを指定して、積まれるJobの数に制限をかけています。
    private val jobChannel = Channel<suspend () -> Unit>(Channel.BUFFERED)

    init {
        scope.launch {
            for (job in jobChannel) {
                job()
                delay(1000L) // 1秒に1回Job実行させたかったのでdelay
            }
        }
    }

    fun enqueue(job: suspend () -> Unit) {
        val result = jobChannel.trySend(job)
        if (!result.isSuccess) {
            println("キュー満杯")
        }
    }
```
追加するJobが非同期処理で、かつ同時にStartすることに不都合があったため、　delay(1000L)で1秒に1回ずつJobを実行する仕組みになっています。

`enqueue(job:)`内の`jobChannel.trySend(job)`でChannelに処理をつんでいます。
私の場合、Channelが万が一満杯になった場合は、処理をdropしても問題ないケースだったのでtrySendを使用して、満杯であれば失敗を返すようにしています。
代わりに`send()`を使用すると、Channelに空きが出るまで中断して、空き次第sendするようなので、状況によって使いわけが必要です。

#### 使う側
使う側ではenqueueの引数に実行したい処理を入れるだけです。
```kotlin
private val jobQueue = JobQueue()
jobQueue.enqueue {
    println("job1")
}
jobQueue.enqueue {
    println("job2")
}
```

#### 補足
ほぼほぼ自分の使用例のみの記載なので、全てのJobが実行されることを望むのか否かでjobの送り方等変わってくるかと思います。
今回は`Channel.BUFFERED`にしていますが、メモリが許す限り無限にJobを送信できる`Channel.UNLIMITED`もあります。
また、ログアウト時に実行中のJobのキャンセルやChannelのリセットなどしたい場合は別途ハンドリングがひつようになってきますのでご注意ください。
Jetpack ComposeのNavController入門編

## 1. はじめに
- この記事で学べること
    - Jetpack Composeにおける画面遷移の基本
    - NavControllerの役割と使い方
- 対象読者
    - Composeを使い始めた人
    - 画面遷移で迷っている人

## 2. NavControllerとは？

- アプリ内で、「今どの画面を表示するべきか？」を管理するコントローラー
- 画面遷移の情報を持っていて、成功したら自動的に表示を切り替えてくれる

## 3. Composeでのナビゲーション全体像

#### rememberNavController()

- **役割**：NavControllerのインスタンスを作成する
- ポイント：`remember`を使って、再コンポーズされても保持される

#### NavHost

- **役割**：NavControllerの状態を観測し、対応するComposable画面を表示する入れ物
- ポイント：`startDestination`を指定し、最初の画面を決める

#### composable

- **役割**：ルート(パス名)とComposable画面を結びつける
- ポイント：複数の`composable`をNavHost内に登録して画面遷移を実現する

## 4. コード例（簡易バージョン）

```kotlin
val navController = rememberNavController()

NavHost(navController = navController, startDestination = "home") {
    composable("home") { HomeScreen(navController) }
    composable("detail") { DetailScreen(navController) }
}
```

```kotlin
Button(onClick = { navController.navigate("detail") }) {
    Text("Go to Detail")
}
```

- navigate(ルート名)を呼ぶだけで画面遷移できる！

## 6. よくあるミスと対処

- NavHostにルートを登録し忘れる
- startDestinationを設定していない
- NavControllerを適切にComposableに渡していない
安いWi-Fi機能付きのESP8266マイコンを試す（WPS対応　ちょっただけ進展編）

## 前の記事

### 準備編
https://qiita.com/Tama79/items/dec21779bb16a1d04c1e

### 応用編
https://qiita.com/Tama79/items/5a1f9b6b7a07f6b8ad40

### WPS失敗編
https://qiita.com/Tama79/items/44d9675e8542bc0e8555

## Arudiono IDEで使う
### 作りたかった機能
WPS接続する！

ソース変えて＋うまくいくルーターあり。前に、うまくいかないルータはうまくいかない・・。

### つながらなかったサーバ
|-|内容  |
|:--|:--|
|メーカー|IOデータ  |
|製品|WN-DX1167GR  |
|ファームウェアバージョン|1.11.000|
|ブートコードのバージョン|1.24.06|
|IPアドレス割当|DHCP|

### つながったサーバ
|-|内容  |
|:--|:--|
|メーカー|PLANEX  |
|製品|MZK-MF300N2  |
|ファームウェアバージョン|1.14|
|IPアドレス割当|DHCP|

### 試行した結果
#### 試行その1
##### 試行1.WiFi.begin() + beginWPSConfig()編

<details><summary>WiFi.begin() +beginWPSConfig編スケッチ</summary><div>
たぶん、WiFi.begin()もbeginWPSConfig()もいる様子。

スケッチ（色付けてみたｗｗｗ）

```c
#include <ESP8266WiFi.h>

//  Wi-Fi接続関連
const unsigned long WIFI_CONNECTED_CHECK_MS = 500;
const unsigned long WIFI_CONNECTED_TIMEOUT  = 60000;

void setup() {
  Serial.begin(SERIAL_SPEED);
  Serial.println("");

  //  WiFi接続
  ConnectedStatus = false;
  ConnectedStatus = startWPSPBC();
}

bool Wifi_connectWait() {
  int wait=0;
  Serial.println("Plase wait. WiFi connecting");
  while (wait < WIFI_CONNECTED_TIMEOUT) {
    if (WiFi.status() == WL_CONNECTED) {
      Serial.println("WiFi connected");
      return true;
    }
    Serial.print(".");
    wait += WIFI_CONNECTED_CHECK_MS;
    delay(WIFI_CONNECTED_CHECK_MS);
    Serial.println(wait);
  }
  Serial.println("Connect timed out");

  return false;
}

bool startWPSPBC() {
  bool wpsSuccess;

  //  初期設定
  WiFi.mode(WIFI_STA);
  WiFi.begin("","");
  delay(WIFI_CONNECTED_CHECK_MS);
  WiFi.disconnect();

  // wpstest.ino
  Serial.println("WPS config start");
  WiFi.beginWPSConfig();
  wpsSuccess = Wifi_wait();

  if(wpsSuccess) {
    String newSSID = WiFi.SSID();
    if(newSSID.length() > 0) {
      //  newSSIDが割り当てられたら成功
      Serial.printf("WPS finished. Connected successfull to SSID '%s'\n", newSSID.c_str());
      Serial.println("WPS config success.");
      Serial.print("WPS Connected. local IP address: ");
      Serial.println(WiFi.localIP());
    }
  } else {
    Serial.println("WPS config fault.");
  }

  return wpsSuccess; 
}

```
</div>
</details>

## 謝辞
### 以下の記事を参考に作りました。
https://qiita.com/wamisnet/items/18527fd7f2d4e53f5095

## 最後に
スケッチコードはCC BY-SA 4.0（著作者の情報とCCライセンス継承はお願いします。商用利用・改変・再配布は問題なし）です。
apptainerをUbuntu24.04にインストールする

# はじめに
コンテナを実行する環境として，HPC分野ではSingularityがよく用いられています．以前，Singularity入門としていくつか記事を書きましたが，近年SingularityプロジェクトがLinux Foundationに移行したことで名称がApptainerとなって新しくなっています．ただし，コマンド名が新しく変わっただけで基本的な利用方法はsingularityと変わらないようです．

本記事では以前のSingularityの記事の更新として，ApptainerをUbuntu24.04にインストールし使用する方法をまとめたいと思います．

## 過去に投稿したsingularity関連の記事
- [CentOS7にSingularity 3.0.3+GPU環境を構築する](https://qiita.com/HoriThe3rd/items/492afd7600ce714dd240)
- [SingularityコンテナをBuildする方法まとめ(Singularity v3.0.3)](https://qiita.com/HoriThe3rd/items/6982247ac0f612d0474a)
- [Singularity入門：コンテナを使う(shellとexec)](https://qiita.com/HoriThe3rd/items/d1d2c3ac788db60ae78a)

# インストール
インストールするマシンのOSは `Ubuntu 24.04 LTS` とします．

基本的なインストール手順は[apptainer公式のAdmin Guide](https://apptainer.org/docs/admin/latest/installation.html#installation-on-linux)を参照しています．

まず必要なリポジトリ等の準備をします．

```bash
sudo apt update
sudo apt install -y software-properties-common
sudo add-apt-repository -y ppa:apptainer/ppa
```

続いて，apptainerのインストールを行います．

```bash
sudo apt update
sudo apt install -y apptainer
```

以上でAdmin guideにあるインストールは完了ですが，Ubuntu24.04の場合ここでコンテナを起動しようとすると次のようなエラーメッセージが表示されうまく動作しませんでした．なお，コンテナの起動はsudoをつけずに一般ユーザ権限で実施します．

```bash
ERROR  : Could not write info to setgroups: Permission denied
ERROR  : Error while waiting event for user namespace mappings: no event received
```

そこで，[apptainerのGitHub](https://github.com/apptainer/apptainer/blob/main/INSTALL.md#apparmor-profile-ubuntu-2310)を確認したところ，Ubuntu 23.10以上は権限関係のデフォルトの挙動が変わっていることが原因のようでした．以下，apptainerのGitHubの`Apparmor Profile (Ubuntu 23.10+)`から引用します．

> Beginning with the 23.10 release and including the 24.04 LTS release, Ubuntu does not permit applications to create unprivileged user namespaces by default.

そこで，GitHubの記述に従い以下のコマンドでapparmor profileを作成し，apparmorを再起動することで非特権ユーザでもapptainerコンテナを起動できるようになります．

なお，apparmor profile内に記述するパスについて[こちらのissue](https://github.com/apptainer/apptainer/blob/main/INSTALL.md#apparmor-profile-ubuntu-2310)を参考に実際の自分の環境に合わせて修正しています．

```bash
sudo tee /etc/apparmor.d/apptainer << 'EOF'
# Permit unprivileged user namespace creation for apptainer starter
abi <abi/4.0>,
include <tunables/global>
profile apptainer /usr/libexec/apptainer/bin/starter{,-suid} 
    flags=(unconfined) {
  userns,
  # Site-specific additions and overrides. See local/README for details.
  include if exists <local/apptainer>
}
EOF
```

以下のコマンドでapparmorを再起動します．

```bash
sudo systemctl reload apparmor
sudo systemctl daemon-reload
```



# 動作テスト
以下のコマンドで動作テストします．

```bash
apptainer build lolcow.sif docker://sylabsio/lolcow
apptainer run lolcow.sif
```

以下のように，ウシが現在時刻を喋れば動作成功です．

```bash
 ______________________________
< Sun Apr 27 00:04:43 JST 2025 >
 ------------------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
```

# apptainerとsingularityコマンド
apptainerをインストールするとsingularityコマンドも利用可能になります．実際にはsingularityコマンドがapptainerコマンドへのシンボリックリンクとなっていました．そのため，これまで通りsingularityコマンドで利用することもできそうです．

# 終わりに
Ubuntuを24.04でのapptainerインストールについてまとめました．Ubuntu24.04ではaptでインストールするだけでは非特権ユーザでは動かず，今回はインストール時にapparmor profileの記述を行う必要でした．
個人的備忘録：FastAPI バックエンドをECR/ECSへデプロイするGitHub Actionsワークフロー整理②

## はじめに

本記事では、FastAPIで作成したバックエンドアプリケーションをAWSのECR（Elastic Container Registry）とECS（Elastic Container Service）にデプロイするGitHub Actionsワークフローについて整理します。

:::note warn
この内容は、受講しているITスクールのハッカソンにおいて、チームメンバーの方が作成してくださったものを、レビューした際にまとめたものです。（**いろいろと作業いただき、ありがとうございます。**）
:::

ビルド済みイメージをECRに登録し、ECSで運用することで本番環境への安全なデプロイを実現します。

## 書こうと思ったきっかけ

バックエンドアプリケーションの運用において、コンテナイメージのビルドとECSサービスの更新を自動化できるよう、作成いただきました。

https://github.com/2025SpringTeamA/backend/blob/develop/.github/workflows/cd.yml

特に、手動操作によるリスクや作業コストを削減し、確実かつ迅速なリリースを実現するためのデプロイワークフローとなっています。

## ワークフローの概要（2025/04/27）

このGitHub Actionsワークフローは、FastAPIアプリケーションのDockerイメージをECRにプッシュし、ECSサービスに適用するために作成されています。

### トリガー条件
- 「Build and Test FastAPI Backend」ワークフローが成功した後（`main`ブランチ対象）
- 手動実行（`workflow_dispatch`）

### 実行内容

1. **コードチェックアウト**
   - GitHubリポジトリからソースコードを取得

2. **コミット情報のダウンロードと設定**
   - ビルド時に保存したコミットSHAを取得し、タグ付けに利用
   - 手動実行時は入力されたSHAも使用可能

3. **AWS認証情報の設定**
   - GitHub Secretsに保存されたAWSアクセスキー情報を使ってAWS CLI認証を設定

4. **ECRログイン**
   - Amazon ECRに対してDocker認証を実施

5. **Docker Buildxセットアップ**
   - マルチプラットフォーム対応のビルド機能（Buildx）を有効化

6. **APIコンテナビルド＆プッシュ**
   - `./backend/api`をビルドし、ECRリポジトリに`commit_sha`タグと`latest`タグでプッシュ

7. **マイグレーションコンテナビルド＆プッシュ**
   - `./backend/migration`も同様にビルド＆プッシュ

8. **ECSタスク定義の更新（API）**
   - 新しいイメージURIに差し替えたタスク定義ファイルを作成

9. **ECSサービス更新（API）**
   - 更新したタスク定義を使用してECSサービスをデプロイ。サービスの安定化まで待機

10. **DBマイグレーション実行**
    - ECSクラスタ上でマイグレーションタスク（Fargate）をRunTaskとして実行

### 補足ポイント
- APIとマイグレーションのDockerイメージをそれぞれ別々にビルド＆プッシュしている
- 手動デプロイにも対応しており、柔軟な運用が可能

## まとめ

このワークフローにより、FastAPIバックエンドのECR/ECSデプロイが完全自動化され、運用の負荷とリスクが大幅に低減されると思います！

:::note warn
**作成いただいた方、本当にありがとうございます...！**
:::

まずはこの仕組みを堅牢に運用し、迅速で信頼性の高いサービス運用基盤を築いていきたいと思います...！
vue-routerで直接アクセスを判定する方法

## 目的
システム内で直接アクセスやブックマークからアクセスされたくないページがあり、その制御のための判定を作成しました。
## 実装
```js:router/index.js
let isFirst = true;

router.beforeEach((to, from, next) => {

    const beforePage = sessionStorage.getItem('currentPage')
    const nextPage = to.name;
    const isDirectAccess = isFirst && beforePage !== nextPage

    isFirst = false;
    sessionStorage.setItem('currentPage', to.name)

    next()
})
```

| 状況                     | isFirst | beforePage != nextPage | isDirectAccess |
|--------------------------|---------|------------------------|----------------|
| 直接アクセス(ブックマーク)(違うページ) | TRUE    | TRUE                   | ○         |
| 直接アクセス(ブックマーク)(同じページ) | TRUE    | FALSE                  | ×          |
| ページの更新             | TRUE    | FALSE                  | ×          |
| 通常のページ遷移         | FALSE   | TRUE                   | ×          |

## 解説
- isFirstは初回のページアクセス時にtrueとなり、その後はfalseに設定されます。
- sessionStorageを使用して、画面が遷移したかどうかを判定しています。
    - sessionStorageはブラウザのタブごとにデータが管理されるため、異なるタブで直接アクセスした場合はcurrentPageがundefinedとなります。
- アドレスバーで何も変更せずにEnterを押した場合や、ページ更新をした場合も考慮しています。


## 注意点
sessionStorageはブラウザのプライベートモードでは使えないので、この点が要件に合わない場合は別の方法が必要ですね。
生成AIの歴史メモ（2022〜2024）

# 生成AIの歴史（2022〜2024）

## 2022年
- **Stable Diffusion**の公開  
  2020年頃から急速に発展した **拡散モデル（Diffusion Model）** をベースにした画像生成手法であるStable Diffusionが登場。  
  高品質な画像生成が個人でも容易に扱えるようになり、生成AIブームが本格化する。

- **DALL·E 2**の公開  
  OpenAIが開発した画像生成モデルDALL·Eの第2世代である**DALL·E 2**が公開。  
  テキストから極めて高品質な画像を生成できることで話題を集める。

## 2023年
- **ChatGPT-4**の公開  
  OpenAIにより**ChatGPT-4**がリリースされる。  
  従来のChatGPT-3.5に比べて、**論理的推論能力・創造性・マルチモーダル（画像対応）** が大幅に向上した。

- **Midjourney V5**リリース  
  Midjourneyチームが手がける独自スタイルの画像生成AI「Midjourney」のバージョン5が公開される。  
  フォトリアリスティックな表現力が大幅に進化し、デザイナー・アーティスト層にも急速に普及する。

## 2024年
- **Gemini 1**のリリース（Google DeepMind）  
  Google DeepMindが、次世代大型マルチモーダルモデル**Gemini 1**を公開。  
  従来のPaLM 2に代わる基幹モデルとなり、マルチモーダル処理やエージェント指向タスクで高い性能を示す。

- **Claude 3**のリリース（Anthropic）  
  Anthropicが**Claude 3**シリーズ（Opus、Sonnet、Haiku）を公開。  
  セーフティ重視の設計思想を保ちながら、知識量・推論力でGPT-4に迫るレベルに達する。

- **DeepSeek-V3**の発表  
  DeepSeekが **「DeepSeek-V3」** モデルを発表。  
  開発費用は**約560万ドル**と、他の主要な大規模AIモデルと比較して**非常に低コスト**でありながら、高性能を示した。
VSコードでコードは書けるが実行できない場合。（根本的解決ではないです。）

Windowsでのc言語の開発環境を作るとき、VSコードで完結させようとしがちです。

ただし、その方法だとうまくいかないこともしばしば。


この記事は、VSコードはコード記入だけの機能として動作させ、実行自体は、コマンドプロンプトで完了させよう、というものです。

まず、c言語の開発環境を整える大前提として。

１，c言語を動作させることが可能なファイルを入れていること。

２，そのファイルを、パソコンへPATH登録させていること。

あとはコマンドプロンプトを開き、

gcc --version

と入力してみてください。

数行だらだらとでてきたら、インストールは完了ということです。

出てこなかったら、パソコンを再起動させ、もう一回入力しましょう。（私はこれでできた。）

実行させたいc言語ファイルのところへ、コマンドプロントから、

cd ここは開きたいフォルダーへ（例：cd "C:\c_file\kadai1"）
(機器によって違う可能性あり)

と打って行きたいフォルダーへ移動。次は実行ファイルをつくるために、

gcc 実行させたいファイル　-o ここは何かうつ（例：gcc kadai1.c -o 1）

を打ちます。これによって、実行ファイルが作成されました。次に、

何か.exe（例：1.exe）

と打ってみてください。きっと、実行されるはずです。

この記事は以上です。不明な点がありましたら、連絡頂けると幸いです。（返信に気づくのが結構遅れるかもです。）
Google Places API（新版）とは？主なAPI種類を調べる

# はじめに
今回は、Google Maps Platformの「Places API（新版）」について、
どんなAPIがあるのか、どんなことができるのかをまとめてみました

# そもそもPlaces API（新版）って？
Google Places APIは、地図アプリや店舗検索サービスなどで「場所」に関する情報を取得・活用できるAPIです
「新版」として大幅に機能強化され、より多彩な情報や柔軟な検索ができるようになりました

https://developers.google.com/maps/documentation/places/web-service/overview?hl=ja

# 主なAPIの種類とできること
### Place Details API
**特定の場所の詳細情報を取得するAPI**
例えば「近所のカフェの営業時間やレビューを知りたい」「この病院に駐車場はある？」といったニーズに応えるAPIです

- プレイスIDを指定して、住所、電話番号、営業時間、レビュー、評価、支払い方法、駐車場、アクセシビリティ（車椅子対応など）、EV充電設備、燃料タイプなど、多様な情報を取得
- 必要な情報だけをフィールド指定して取得できるため、コストとレスポンスが最適化

### Place Photo API
**場所の写真を取得するAPI**
Googleマップに載っているような写真を、アプリやWebサイトに表示したいときに便利

- プレイスIDや写真リソース名を指定して、高品質な写真を取得
- 写真サイズの指定も可能

### Nearby Search API
**指定したエリア周辺の場所を検索するAPI**
「今いる場所の近くにあるレストランを探したい」「駅周辺のカフェだけ表示したい」など、地図アプリに欠かせない機能

- 緯度・経度と半径を指定し、周辺のレストランやカフェなどを検索
- 場所タイプ、価格帯、営業状況、評価などで絞り込み可能

### Text Search API
**キーワードや条件で場所を検索するAPI**
「ラーメン」「ペット可カフェ」など、ユーザーの入力したキーワードで場所を検索できます

- ユーザーの入力したテキストや条件に基づいて場所を検索
- 地域や場所タイプの指定も可能

### Autocomplete API
**入力補完・予測候補を返すAPI**
ユーザーが地名や施設名を入力し始めたとき、候補をリアルタイムで表示してくれるAPIです

- ユーザーが入力中のテキストに対して、地名や施設名などの候補をリアルタイムで返す
- 地域やタイプの絞り込み、セッショントークンによる精度向上も可能


# 新版の主な特徴・強化点
- **取得データの柔軟性（FieldMask）**
必要なフィールドのみリクエストでき、無駄なデータ取得を防ぎ、コストとレスポンス速度を最適化

- **対応場所タイプの大幅拡充**
コーヒーショップやEV充電ステーションなど、レガシー版の倍近い約200種類以上のプレイスタイプに対応

- **リアルタイム・ダイナミックデータ**
EV充電器の空き状況やガソリン価格、祝日・サービス別営業時間など動的情報も取得可能

- **アクセシビリティ・属性情報の充実**
車椅子対応、ペット可、屋外席、子供向けメニュー、ライブ音楽、スポーツ観戦可など、多彩な属性をカバー

- **新しい認証方式**
APIキーに加えOAuth認証もサポートし、セキュリティ強化

- **JSON専用レスポンス**
レスポンス形式はJSONのみ

# まとめ
Google Places API（新版）は、
- 詳細情報取得
- 写真取得
- 周辺検索
- テキスト検索
- 入力補完
と、さまざまなニーズに応じて使い分けられるAPIがそろっています
地図アプリや店舗検索サービスを、より便利に、よりリッチにしたい方は、ぜひ新版の機能をチェックしてみてください
TypeScriptのkeyof型について

keyof演算子を使うと、型のプロパティからリテラル型の値を取り出した型を宣言できる
```ts
type Human = {
  name: string
  age: number
}

type Humankeys = keyof Human

let key: Humankeys = 'name'
key = 'age1' // age1のリテラルはないので失敗する
```
【RAGにも使える】苦労せずにFirestoreで類似度検索をする

## まとめ
- FirestoreはKNNによる最近傍検索ができる
- コレクショングループを対象にできる
- インデックスが必要だが、作成のためのコマンドはエラーメッセージに出るので安心
- ドキュメント埋め込みはVertex AIのtext-embedding-00X系が便利
    - Task Typeを選んでさらに性能を上げよう

### Firestoreの類似検索
レコメンドシステムの要、類似検索がFirebaseで簡単にできるようになっています。
キーワード検索不要、ベクトル検索のみ必要であればalgoliaなどの外部サービスなしに実装できます。
2048次元まで対応しているので、機械学習モデルが出力する埋め込みベクトルでも余裕をもって対応できるはずです。

https://firebase.google.com/docs/firestore/vector-search?hl=ja



### コレクショングループを対象にできる
Firebaseのコレクションはリソース階層を持たせることができ、セキュリティルール上重要です。
例えばユーザーのドキュメントを`/users/:uid/*`配下に格納し、`uid`が一致していない場合は拒否するルールを作ることが多いでしょう。
通常のクエリでは`/users/:uid/someitem`といった単一のコレクションしかクエリできませんが、コレクショングループを使用すると`/users/*/someitem`を対象として、横断的にクエリできます。



### インデックス作成はエラーメッセージから行うのが簡単
公式ドキュメントにコマンドがありますが、地味にパラメータが多いので間違えやすいです。
作成に時間がかかるので、開発環境でエラーメッセージを確認してコピーペーストするのが簡単です。
ベクトル検索以外でも使用できるテクニックですが、ベクトルインデックスは`firestore.rules`ファイルに反映されないので注意が必要です。
```sh
gcloud firestore indexes composite create \
--collection-group=collection-group \
--query-scope=COLLECTION \
--field-config field-path=vector-field,vector-config='vector-configuration' \
--database=database-id
```

```
`Missing vector index configuration. Please create the required index with the following gcloud command: gcloud firestore indexes composite create --project=broorec-7b512 --collection-group=posts --query-scope=COLLECTION_GROUP --field-config=order=ASCENDING,field-path=userId --field-config=vector-config='{"dimension":"768","flat": "{}"}',field-path=contentEmbeddings`,
```

### おまけ
vertex aiの埋め込みモデルが面白い
https://cloud.google.com/vertex-ai/generative-ai/docs/embeddings/task-types?hl=ja
個人的備忘録：FastAPI バックエンドのビルド・テスト用GitHub Actionsワークフロー整理①

## はじめに

本記事では、FastAPIで作成したバックエンドアプリケーションのCI（継続的インテグレーション）として設定したGitHub Actionsのワークフローについて整理します。

:::note warn
この内容は、受講しているITスクールのハッカソンにおいて、チームメンバーの方が作成してくださったものを、レビューした際にまとめたものです。（**いろいろと作業いただき、ありがとうございます。**）
:::

コード品質維持とDockerイメージビルド検証を目的としています。

## 書こうと思ったきっかけ

バックエンド開発では、コードの静的解析・単体テスト・Dockerビルド検証まで自動化できるよう、作成いただきました。

:::note info
**個人の備忘録程度の走り書きとなっておりますが、温かい目で見守っていただければ幸いです。**
:::

https://github.com/2025SpringTeamA/backend/blob/develop/.github/workflows/ci.yml

特にFastAPIプロジェクトでは、APIとマイグレーションの2つのDockerイメージを扱うため、ビルド検証も重要なステップとなります。

## ワークフローの概要（2025/04/27）

このGitHub Actionsワークフローは、FastAPIバックエンドのテストとDockerイメージビルドを自動化するために作成されたものです。

### トリガー条件
- `main` または `develop` ブランチへの **push**
- `main` または `develop` ブランチへの **pull request**
- **手動実行**（`workflow_dispatch`）

対象ファイルは `backend/` 配下と、ワークフロー自身（`.github/workflows/ci.yml`）です。

### 実行内容

1. **コードチェックアウト**
   - GitHubリポジトリからソースコードを取得

2. **Pythonセットアップ**
   - Python3.11をインストール、pipのキャッシュも有効化

3. **依存パッケージインストール**
   - `backend`ディレクトリで、基本パッケージと`requirements.txt`に記載されたライブラリをインストール

4. **Lintチェック（flake8）**
   - `flake8`でエラーや重大なコードスタイル違反を検出

5. **単体テスト（pytest）**
   - `pytest`でテストコードを実行（テスト未設定時はスキップ）

6. **Docker Buildxセットアップ**
   - マルチプラットフォーム対応のビルド機能（Buildx）を有効化

7. **APIコンテナビルド（ローカル用）**
   - `./backend/api` をコンテキストに、`xxx-api:local-test` というタグでDockerイメージをローカルビルド

8. **マイグレーションコンテナビルド（ローカル用）**
   - `./backend/migration` をコンテキストに、`xxx-migration:local-test` というタグでDockerイメージをローカルビルド

9. **コミット情報の保存とアップロード**
   - プルリク以外のイベント時、コミットSHAをファイル保存し、アーティファクトとしてアップロード

## まとめ

このワークフローにより、バックエンドコードの品質チェック・テスト・Dockerビルド検証までを一連で自動化でき、リリース前の不具合リスクが大幅に低減されると思います...！

:::note warn
**作成いただいた方、本当にありがとうございます...！**
:::

まずはこのCI基盤を着実に運用し、開発の信頼性とスピードを両立させていきたいと思います
Three.js を触ってみたら案外シンプルで楽しかった話

## はじめに
「WebGL は難しい」という先入観を捨てて Three.js少し触ったところ、思いのほか手軽で楽しかったので学習メモを残します。コード全文や動くデモは GitHub に置いてあるので、実際の挙動はそちらで確認してください。  
<https://github.com/tomoya-worldwide/threejs-snipet>

---

## 学習ステップ
1. **環境構築**  
   - Vite + TypeScript + pnpm の組み合わせでセットアップ  
   - `pnpm dev` で即ホットリロード。ブラウザを開きっぱなしで試行錯誤できます

2. **最小サンプル（回転キューブ）**  
   - 押さえるべき要素は *Scene / Camera / Renderer / Mesh* の 4 つだけ  
   - 15 行程度で「回るキューブ」が完成。まずはここで触感を掴むのがおすすめ

3. **GUI で形状を切り替え**  
   - lil-gui を導入し、キューブ ↔︎ スフィア ↔︎ トーラスをワンクリックで変更  
   - OrbitControls を足すとマウスドラッグでモデルを回せて理解が早まる

4. **PBR マテリアルを試す**  
   - MeshPhysicalMaterial で *metalness* と *roughness* を調整するだけで質感が激変  
   - HDRI を環境マップに設定すると一気にリアル感が増す（そして沼が始まる）

5. **ハマりポイント備忘録**  
   - 画面が真っ黒 → `camera.position` を忘れて原点と重なっているだけ  
   - モバイルで重い → `renderer.setPixelRatio(Math.min(devicePixelRatio, 2))` で Retina 抑制  
   - GUI が増殖 → `gui.destroy()` して再生成するか、同じインスタンスを使い回す

6. **次にやりたいこと**  
   - InstancedMesh で数千オブジェクトを一気に描画  
   - Post-processing（Bloom／DOF／SSAO 等）でリッチ化  
   - Three.js の WebGPU レンダラ（r160 予定）を試す

---

## まとめ
- Three.js は「まず動くまで」が驚くほど短く、学習コストが低い  
- lil-gui と組み合わせると “触りながら覚える” スタイルがとにかく楽しい  
- PBR マテリアルと環境マップを足すだけで表現の幅が一気に広がる  

興味が湧いたらぜひリポジトリを clone して、ブラウザ上でグリグリ動かしてみてください。Happy Three-coding! 🚀
JAWS-UG初心者支部 ハンズオン＆LT会に参加してきました！

[イベントページはこちら](https://jawsug-bgnr.connpass.com/event/350578/)

## はじめに
先日、JAWS-UG初心者支部が主催する「ハンズオン＆LT会」に参加してきました！  
JAWS-UGへの参加は2回目でしたがとても楽しく勉強になったので共有します。

## JAWS-UGとは？
JAWS-UG（AWS Users Group – Japan）はAWS( http://aws.amazon.com/jp/ ) を多くの方に知っていただき、活用を推進するために非営利目的で活動をしているユーザーグループです。一人ではできない学びや交流を目的としてボランティアによる勉強会の開催や交流イベントなどを行なっています。
(Conpass公式ページより)

## 今回の内容
今回は、前半1時間がAWSサービスであるIAMのハンズオンで後半がLT(ライトニングトーク)会でした。それぞれ以下にまとめます。

## ハンズオンの感想
これまで私は、AWSのコンソールにルートユーザーで直接ログインして操作するスタイルで学習していました。  
ですが、今回のハンズオンで**IAMユーザー・IAMロールを作成して適切な権限管理を行う**流れを体験できたのが大きな収穫でした。

実際にハンズオンで以下を体験しました。
- IAMユーザーの作成
- 必要最小限のポリシーを付与
- セキュリティ強化のためルートユーザーは基本使わない設計

今までなんとなく「IAMは後回しでいいかな」と思っていたのですが、今回の体験を通じて、AWSにおけるセキュリティベストプラクティスを実感しました。

## LT（ライトニングトーク）の感想
続いて行われたLTセッションも、**初心者向け**の内容中心で非常にわかりやすかったです！

特に印象に残ったのは、「通知アプリ構築」の事例発表でした。というのも
- 当初考えていた構成案が、実際にはうまく動かず
- その場で構成を見直し、本当に必要な機能に絞ってMVP(ミニマムなもの)に変更する
- その過程や気付きも赤裸々に語ってくれた

という初心者の登壇でとても参考になる構成だったからです。
とてもリアルな開発体験談で、ただ「成功事例」を聞くよりも、**現場ならではの試行錯誤**が伝わってきて、すごく参考になりました。

失敗談や改善の過程を共有してもらえると、「あ、自分もこういうとき焦らずやればいいんだな」と勇気づけられますね。

## まとめ
今回のハンズオン＆LT会を通して、
- **AWSを触る上でIAMの重要性**を再認識できた
- **現場のリアルな試行錯誤**に触れることができた

と、非常に有意義な時間になりました。

JAWS-UG初心者支部さん、素敵なイベントをありがとうございました！  
今後も積極的にハンズオン＆勉強会に参加して、AWSのスキルを磨いていきたいと思います。
TypeScriptでプロパティに関数を持つオブジェクト型を宣言する方法

以下のように書く
```ts
type Myfunc = {
  (str?: string) : void
  (num?: number): void
}

const myFunc: Myfunc = (num) => {
  console.log(num)
}
const myStrFunc: Myfunc = (str) => {
  console.log(str)
}

myFunc(100)
myStrFunc('hoge')
```

このように書くと、関数ではなくプリミティブ型になる
string型を渡すとエラーになる
```ts
type Myfunc = {
  str?: string // このように定義すると、関数ではなくプリミティブ型になる
  (num?: number): void
}

// strを渡しているが、関数に対して型を当てているので、numを引数に持つ関数の方を型として設定してしまう
const myStrFunc: Myfunc = (str) => {
  console.log(str)
}

myStrFunc('hoge') // TS2345: Argument of type string is not assignable to parameter of type number
```

エディターで関数がどのように定義されているか都度確認するとすぐ気づける & TypeScriptがどういう風に型を解釈するしようとするか考えるのが大事だと思った
![スクリーンショット 2025-04-27 12.47.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492674/b57149b5-dccc-4d7b-ac60-3fb1b74ab42d.png)
useMemoを使っているとReactのWarningが発生した

# 状況
エディター上では何もwarningが発生しなかったのに、動作確認の段階で下記のwarningが突然発生しました
`Warning: Internal React error: Expected static flag was missing. Please notify the React team.`

# 原因
useMemoよりも手前でif returnをしていたことが原因でした

```.tsx
const SampleComponent = (user) => {
  if(user=null) return

  // user を使った処理

  const [name, setName] = useState("");
  const [age, setAge] = useState(0);

  // useMemoを実行する前にreturnで返される可能性があるため、warning
  const inputData = useMemo(() => ({ name, age }), [name, age]);

  return <>...</>;
}
```

# 解決方法
- `if return`と`useMemo`の順序を入れ替える
    - Reactのコンポーネント内でのフックや変数の宣言の順番に関するベストプラクティスから外れてしまうので、おすすめできないです
- userをnull非許容型の引数として宣言する
    - userがnullでなくなった時のみ、このコンポーネントを呼び出すように上位層のコンポーネントで制御すると解決できます
個人的備忘録：Next.js フロントエンドのビルド・テスト用GitHub Actionsワークフロー整理②

## はじめに

本記事では、Next.jsで作成したフロントエンドアプリケーションをAWS S3とCloudFrontに自動デプロイするGitHub Actionsワークフローについて整理します。

:::note warn
この内容は、受講しているITスクールのハッカソンにおいて、チームメンバーの方が作成してくださったものを、レビューした際にまとめたものです。（**いろいろと作業いただき、ありがとうございます。**）
:::

ビルド後、素早く本番環境へ反映するための仕組みを整えることが目的です。

## 書こうと思ったきっかけ

この内容は、あくまでチームメンバーの方に環境やファイルを整えていただいたうえでのものとなります。

開発中のアプリケーションを本番環境へ手動でアップロードするのは手間がかかり、ヒューマンエラーのリスクも高まるため、ビルド完了後に自動デプロイする流れを作りたいと考えました。

https://github.com/2025SpringTeamA/frontend/blob/main/.github/workflows/cd.yml

特に、S3へのアップロードとCloudFrontキャッシュの無効化をワンストップで行うため、このワークフローを設計いただきました。

## ワークフローの概要（2025/04/27）

このGitHub Actionsワークフローは、ビルド済みのNext.jsフロントエンドをS3にデプロイし、CloudFrontのキャッシュを無効化するために作成されています。

### トリガー条件
- 「Build and Test Next.js Frontend」ワークフローが成功した後（`main`ブランチ対象）
- 手動実行（`workflow_dispatch`）

### 実行内容

1. **ビルド成果物をダウンロード**
   - 事前にアップロードされているビルド成果物（アーティファクト）を取得し、`out`ディレクトリに展開します。

2. **AWS認証情報の設定**
   - GitHub Secretsに登録されたAWSアクセスキー情報を使ってAWS CLIを設定します。

3. **S3へデプロイ**
   - `out`ディレクトリの内容を、指定したS3バケット（例：`xxx-frontend`）に同期。不要ファイルは削除（`--delete`オプション）。

4. **CloudFrontキャッシュの無効化**
   - CloudFrontのキャッシュを無効化し、最新デプロイが即座に反映されるようにします。

### 補足ポイント
- `workflow_dispatch`で手動実行時は、オプションで`run_id`を指定可能。
- 成功したビルド成果物が対象であり、失敗したものは対象外。

## まとめ

このワークフローにより、ビルド後のS3デプロイとCloudFrontキャッシュクリアを完全自動化でき、リリース作業のミス防止とスピードアップに繋がりると思っています...！

:::note warn
**作成いただいた方、本当にありがとうございます...！**
:::

まずはこのベースを固め、信頼性の高い本番リリースフローを目指していきます。
2025年初頭に至る所で発生したDockerが突然起動しなくなる件の解消方法

## 背景
いつものごとく始業を開始したところ、突然Dockerが起動しなくなる事案が発生した。
![スクリーンショット 2025-02-03 10.41.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1202056/4b710f36-fca2-4307-bd2b-669866557381.png)


## 解決方法
①docker削除（ゴミ箱も全て空にすること！！）
②最新インストール
③以下記事に書かれているコマンド実行してたら途中でdokcerが起動するようになった
https://qiita.com/topi_log/items/b65ea476e9da2d7a6155
個人的備忘録：Next.js フロントエンドのビルド・テスト用GitHub Actionsワークフロー整理①

## はじめに

本記事では、Next.js（TypeScript）で作成したフロントエンドプロジェクトのCI（継続的インテグレーション）として設定したGitHub Actionsのワークフローについて整理します。

:::note warn
この内容は、受講しているITスクールのハッカソンにおいて、チームメンバーの方が作成してくださったものを、レビューした際にまとめたものです。（**いろいろと作業いただき、ありがとうございます。**）
:::

**個人の備忘録程度の走り書きとなっておりますが、温かい目で見守っていただければ幸いです。**

## 書こうと思ったきっかけ

チーム開発を進める中で、`main` や `develop` ブランチに対して安全にコードを統合できる仕組みを用意していただきました。

https://github.com/2025SpringTeamA/frontend/blob/main/.github/workflows/ci.yml

特に、lintチェック・型チェック・テスト・ビルド検証を自動で行い、エラーが事前に検出される体制を整えるために、このワークフローを設計していただきました。

## ワークフローの概要（2025/04/27）

このGitHub Actionsワークフローは、Next.jsフロントエンドのビルドとテストを自動化するために作成されたものです。

### トリガー条件
- `main` または `develop` ブランチへの **push**
- `main` または `develop` ブランチへの **pull request**
- **手動実行**（`workflow_dispatch`）

対象ファイルは `frontend/` 配下と、ワークフロー自身（`.github/workflows/ci.yml`）です。

### 実行内容

1. **コードチェックアウト**
   - GitHubリポジトリからソースコードを取得

2. **Node.jsセットアップ**
   - Node.jsバージョン20をインストールし、npmキャッシュを有効化

3. **依存パッケージインストール**
   - `frontend` ディレクトリで `npm ci` を実行し、依存関係をインストール

4. **Lintチェック**
   - `npm run lint` を実行し、コードスタイル違反がないかチェック（設定がなければスキップ）

5. **型チェック**
   - `npm run typecheck` または `npm run check-types` を実行し、型安全性を確認（設定がなければスキップ）

6. **テスト実行**
   - `npm test` を実行して単体テストを実施（テストがなければスキップ）

7. **Next.jsビルド**
   - `npm run build` を実行してビルドエラーがないか検証

8. **ビルド成果物のアップロード**
   - Pull request以外の場合、ビルド成果物（`frontend/out` ディレクトリ）をアーティファクトとして保存

## まとめ

このワークフローにより、プッシュやプルリクエストごとにコード品質が自動チェックされ、問題が早期に検知できる仕組みが整いました。

:::note warn
今後はさらに、E2Eテストやデプロイ自動化なども視野に入れて拡張していただくことも視野に入れていきたいと思います。
:::

まずはこのビルド・テストフローを確実に回すことを基盤に、より安全でスムーズな開発体験を目指していきます...！
Bootstrap おすすめ

### bootstrap おすすめ

![スクリーンショット 2025-04-26 20.27.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628823/a67cd155-86c5-42bf-8349-19c3d02ca415.png)

```ruby:html
<div class="container mt-4">
    <h2>従業員詳細</h2>
    <table class="table table-striped">
        <thead>
            <tr>
              <th scope="col">#</th>
              <th scope="col">名前</th>
              <th scope="col">メール</th>
              <th scope="col">リモート状況</th>
              <th scope="col">部署</th>
              <th scope="col">勤続年数</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <th scope="row">{{ employee.id }}</th>
              <td>{{ employee.name }}</td>
              <td>{{ employee.mail }}</td>
              {% if employee.is_remote %}
                <td>リモート</td>
              {% else %}
                <td>出勤</td>
              {% endif %}
              <td>{{ employee.department }}</td>
              <td>{{ employee.year }}年</td>
            </tr>
          </tbody>
    </table>
</div>
```
ドキュメントは書く時代から生成する時代へ【DeepWiki】が変えるAI時代の新常識

## はじめに

> 「ドキュメントを書くの、正直しんどい…」  
> 「気づいたら情報が古くなってて、誰も更新してない…」

こんな悩み、開発している人なら一度は感じたことがあるんじゃないでしょうか。

先日、そんな悩みを吹き飛ばすかもしれない面白いツールが発表されました。
AIエージェント [**Devin**](https://devin.ai/) で話題の [**Cognition**](https://cognition.ai/) 社が開発している **DeepWiki** という
「**自動ドキュメント生成ツール**」です。

公式のポストはこちら👇

https://x.com/cognition_labs/status/1915816544480989288

## DeepWiki とは

DeepWiki は、コードベースから**自動**で**ドキュメントを生成**してくれるAIツール です。

💡 **ポイントは「書く必要がない」こと**

GitHub で公開されているレポジトリを入れるだけで、リポジトリ全体を読み込んで、
自然言語でわかりやすい説明を書き起こしてくれます。

例えば `https://github.com/fastapi/fastapi` の「github」の部分を「deepwiki」に変えるだけで、レポジトリの[ドキュメント](https://deepwiki.com/fastapi/fastapi)が作られちゃうんです！

もしすでに誰かがそのリポジトリのドキュメントを作成していれば そのまま読むことができて、まだ作られていなければ **自分で生成する**こともできます。
（やり方はこのあと紹介します！）

自分の個人開発リポジトリも試してみたんですが、本当に10分もしないうちに「READMEっぽいもの」ができあがっていてびっくりしました。「え、これ本当に自分で書いたほうがいいの？」と少し考えてしまうレベルです。

## 何ができるの？

DeepWiki で生成できるのは、ただのコードコメントの寄せ集めではありません。

✅ セットアップ方法やリポジトリの使い方
✅ アーキテクチャの設計や実装の意図
✅ 各ファイル・関数・クラスの役割整理

などをコードの引用付きで自然な文章でまとめてくれます。

また、DeepWiki からレポジトリについてチャットで検索できる機能もあり、なんと **DeepResearch** まで無料で使えちゃうとかいう優れもの。

これが無料とかほんとどうなってるんだか、、、

## ドキュメント生成どうやってするの？

めちゃくちゃ簡単です。

1. **[公式サイト](https://deepwiki.com/)** を開く  
2. **Add repo** をクリック  
3. **Enter the URL of a public GitHub repository** に
ドキュメントを生成したいリポジトリの **URL** をペースト  
4. 生成されるのを待つ（だいたい10分くらい）
💡 メールを登録しておくと生成が終わったタイミングで通知してくれます！

あっという間に、コードからドキュメントが生まれます。
公式の説明動画もわかりやすかったので、気になる人はぜひ👇

https://x.com/cognition_labs/status/1915816548738207934

## 今までのツールと何が変わる

これまでの **Wiki** や **Notion** と、**DeepWiki** の大きな違いはこんな感じ。


|                      | **DeepWiki**              | **従来のWiki / Notion**    |
|----------------------|--------------------------|--------------------------|
| **情報源**            | コードそのもの           | 人が手動で記述            |
| **更新**              | コード変更に合わせて自動 | 手動で更新が必要          |
| **属人性**            | 低い（コードがソース）   | 高い（担当者に依存しがち）|
| **検索性 / 回答精度** | 高い（コード理解あり）   | 中程度（キーワード検索のみ）|

「担当者がいなくなったらドキュメントも死んだ」みたいな悲しいことはもうなくなる時代が来そうです。公開されているどんなリポジトリでも無料で Wiki 生成できるのがそもそも恐ろしいですよね。

## まとめ

ドキュメントって、本来はチームの知識を共有するための大事なものなのに、
どうしても「書くのが大変」「更新がめんどい」になりがちですよね。

そんな悩みを、そっと肩代わりしてくれるのが **DeepWiki** です。

もちろん、最後は人の目でのチェックや、思いのこもった一言を加えることも大事だと思います。ただ、その「ベース作り」を自動でやってくれるのは、本当にありがたい。

「とりあえず一回試してみるか」くらいの気持ちでぜひ一度触ってみてください。
自分は、ちょっと感動しました。
Cloud Pub/SubからCloud Runを呼び出すときに、Eventarcのタイムアウトを変更する方法

Cloud Pub/SubからCloud RunやCloud Run Functionsを呼び出す時には、以下のように設定します。
直接Cloud Pub/SubのSubscriptionを作成するのではなく、Eventarcを使って間接的にSubscriptionを作成しています。

```terraform
resource "google_pubsub_topic" "invoke-function" {
  name = "invoke-function"
}

resource "google_service_account" "invoke-function" {
  account_id   = "invoke-function"
}

resource "google_project_iam_member" "eventreceiver" {
  project = "<プロジェクトID>"
  role    = "roles/eventarc.eventReceiver"
  member  = google_service_account.invoke-function.member
}

resource "google_project_iam_member" "runinvoker" {
  project = "プロジェクトID"
  role    = "roles/run.invoker"
  member  = google_service_account.invoke-function.member
}

resource "google_eventarc_trigger" "invoke-function" {
  name     = "invoke-function"
  location = "asia-northeast1"

  matching_criteria {
    attribute = "type"
    value     = "google.cloud.pubsub.topic.v1.messagePublished"
  }

  transport {
    pubsub {
      topic = google_pubsub_topic.invoke-function.id
    }
  }

  destination {
    cloud_run_service {
      service = "Cloud Runのサービス名"
      region  = "asia-northeast1"
    }
  }

  service_account = google_service_account.invoke-function.email
}
```

このとき、Eventarcは以下のようなSubscriptionを裏側で作成します。

![SCR-20250427-lbgt.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74551/c7b21952-7551-46fc-a9c9-c674c8787387.png)

Acknowledgement deadlineやMinimun/Maximum backoff durationなどはEventarcが勝手に設定するものです。

そのため、例えばCloud Runの処理時間が10sを超える場合には、ACK応答がないと判断されて自動的にリトライ処理が実行されてしまいます。この設定値を変えたい場合は、google_eventarc_triggerリソース経由で変更することはできず、直接Subscriptionをいじる必要があります。

Webコンソールからいじるのであれば、上記の画面のEDITボタンから簡単にできるのですが、terraformでこれらをいじる方法がやや技巧的だったので残しておきます。

importブロックのidは他のリソースの属性を参照できるので、Eventarcが裏で作成するSubscriptionのIDを指定しています。ack_deadline_secondsを変更するとACK応答の待ち時間を変更できます。

```terraform
import {
  id = google_eventarc_trigger.invoke-function.transport[0].pubsub[0].subscription
  to = google_pubsub_subscription.invoke-function
}

resource "google_pubsub_subscription" "invoke-function" {
  name  = basename(google_eventarc_trigger.invoke-function.transport[0].pubsub[0].subscription)
  topic = google_eventarc_trigger.invoke-simple-function.transport[0].pubsub[0].topic

  ack_deadline_seconds = 10 # ここを上書きするとACK応答の待ち時間を変更できる

  # 普通にSubscriptionを作成する場合のデフォルト値とEventarcのデフォルト値が異なるので、
  # Eventarc側に合わせておく
  message_retention_duration = "86400s"

  # retry_policyはEventarcとterraformでデフォルト値が一致しているが、
  # これを指定しないとapply時に差分があるような見た目になるので念の為に指定する
  retry_policy {
    maximum_backoff = "600s"
    minimum_backoff = "10s"
  }

  # push_configはEventarcの設定から変更しないため
  lifecycle {
    ignore_changes = [
      push_config
    ]
  }
}
```

なお、この方法はEventarcのタイムアウト値を変更しているだけなので、必要に応じてCloud Run側のタイムアウト値も変更する必要があります。
Second Meを軽く触った

# はじめに
近年，AI技術の進化により，個人の生活や仕事をサポートする新しいツールが続々と登場しています．​その中でも，「Second Me」は，個人の記憶や思考を学習し，ユーザーの分身として機能するAIアシスタントとして注目を集めています．​本記事では，「Second Me」の概要と，実際に使用してみた体験を共有します．​

# Second Meとは
「Second Me」は，ユーザー自身の記憶や思考パターンを学習し，個人の代理として機能するAIアバターを作成できるオープンソースプロジェクトです．​ローカル環境で動作するため，個人データのプライバシーを保護しながら，ユーザーの知識や経験を反映したパーソナライズされたAIを育成できます．​

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3618319/a9103feb-b872-40b8-b55e-82cba9ff215d.png)


主な特徴として，以下が挙げられます．​

- 階層型メモリーモデル（HMM）：​短期的なインタラクションから長期的な認知メモリーまで，三層のメモリ構造を持ち，ユーザーのパターンを迅速に認識し，適応・進化します．​
- Meアライメント：​強化学習技術を基に，ユーザーのデータを深く個別化された理解に変換します．​
- 完全なプライバシー：​ローカル環境で動作し，個人データの完全な制御をユーザーに提供します．​
- Second Meプロトコル（SMP）：​独立したAIエンティティが相互に学習し進化する分散型AIフレームワークで，各「自己」は完全な独立性を保ちながら，安全に他のエンティティと通信します．​

# お題
今回は，Second Meを用いて，「Second Me」を作成し，その有用性を検証します．​

# 結果
「Second Me」をセットアップは用意でdocker composeで起動すれば動作しました(dockerで動作させる場合，動きは少し遅い)．自分の業務に関して問い合わせると，先週やったことなどを適切に回答してくれました．

## セットアップ
私が普段やっている業務内容をテキスト形式でインプットします．また，QwenというモデルをローカルにダウンロードしてそこでFine-tuningを行ないます．embeddingにはOpen AIのEmbeddingモデルをAPI経由で利用します．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3618319/96a377be-3591-47fe-91c1-f30004d45367.png)


### 記憶の作り方
Memoriesをするにあたり，インプットとして与えたドキュメントを，LLMを用いて整理していきます．この過程では，想定質問を作成し，Q&Aのセットを作成したり，Graphragを用いて，ドキュメントからナレッジグラフを作成して，ドキュメントから得られる情報を上手く整理しています．

(Q&Aを作成中)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3618319/273460b5-c46d-4d2c-97e7-b32bff9145e4.png)


(内部で作成したGraph．可視化してみると以下のようになっていました．GraphragやGraphについては[こちら](https://qiita.com/mrsd/items/294588793c37ca59ab7f)に有益な記事があります．)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3618319/338d53ff-a5e0-4c4d-b54d-fa4bebc1ab05.png)


(Training完了．大したドキュメントではないにも関わらず，およそ1時間かかりました．)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3618319/0e7c2279-9afe-48ce-a30f-e000a60b5aff.png)


## チャットで自身の業務内容を確認してみる
「私の業務内容はなにか？」尋ねたところ，それなりに回答されました．Fine-tuningされたモデルと作成したMemoriesを元に回答しているようです．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3618319/229422de-9e0e-47cf-b9bd-266f45a798a1.png)

# 所感
「Second Me」を実際に使用してみて，以下の点が特に優れていると感じました．​

- パーソナライズ性：​自分の行動や思考パターンを学習するため，一般的なタスク管理ツールよりも柔軟で適切なサポートを提供してくれます．​
- プライバシー保護：​ローカル環境で動作するため，個人情報が外部に漏れる心配がなく，安心して使用できます．​
- 進化するAI：​使用すればするほど，AIが自分の特性を理解し，より適切なサポートを提供するようになります．​

私の認識としては，この個人の特徴を記憶させる機構がSecond Meの肝であると理解しています(論文のタイトルもAI-native Memory 2.0: Second Meとなっているので)．アプリケーションも準備されており，この記憶の作成をUI上で簡単に実現できることが素晴らしいと感じました．ネットワーク上で他のユーザーとコミュニケーションを取ることができるなど，できることが拡張されており，さらなる発展が期待できます．

# まとめ
「Second Me」は，個人の記憶や思考を学習し，ユーザーの分身として機能する革新的なAIアシスタントです．​プライバシーを重視しながら，パーソナライズされたサポートを提供する点で非常に魅力的です．​今後，さらなる機能拡張やユーザビリティの向上が期待されますので，引き続きウォッチします．
Githubのスター数も着実に伸びており，MCPとの連携によってますます注目が浴びると思われます．

# 参考
- [Second Me Paper](https://arxiv.org/pdf/2503.08102)
- [Second Me github](https://github.com/mindverse/Second-Me)
SwiftでFirebase Coloud Firestoreを使ってみた！！

# 初めに
個人開発の中で、データを保存する場所としてFirebase Cloud Firestoreを利用したので、備忘録としてFirestore初期設定の仕方と、使い方をまとめました。

# １.Firestoreの初期設定
初期設定については以下の記事で解説しました。
[【サンプルコードあり】Firebase Cloud FirestoreをSwiftUIで使ってみた](https://tomatechnote.com/%e3%80%90%e3%82%b5%e3%83%b3%e3%83%97%e3%83%ab%e3%82%b3%e3%83%bc%e3%83%89%e3%81%82%e3%82%8a%e3%80%91firebase-cloud-firestore%e3%82%92swiftui%e3%81%a7%e4%bd%bf%e3%81%a3%e3%81%a6%e3%81%bf%e3%81%9f/)

内容は、Firebaseのプロジェクトを作成する部分からはじめ、SwiftUIを利用して、Firestoreにデータを登録するまでです。

# 2.Firestoreの使い方
### Firestoreへの基本的なデータの追加
Firestoreへ基本的なデータについては以下の記事にまとめています。
Firestoreの基本的な知識、コレクションやドキュメントについて説明してから、実際にFirestoreへデータの登録のしかたや、更新の仕方についてまとめました。以下の記事から見てください。
[【Swift】Firebase Cloud Firestoreに値を追加・取得・削除をする](https://tomatechnote.com/%e3%80%90swift%e3%80%91firebase-cloud-firestore%e3%81%ab%e5%80%a4%e3%82%92%e8%bf%bd%e5%8a%a0%e3%83%bb%e5%8f%96%e5%be%97%e3%83%bb%e5%89%8a%e9%99%a4%e3%82%92%e3%81%99%e3%82%8b/)

### 配列形式のオブジェクトを追加する方法
次に以下のような形のデータを登録し、更新、削除する方法についてまとめました。
自分ではこの形にたどり着くまでに時間を使ってしまいました。誰かが、効率的にこの形式にたどり着き、利用できたらと思います。

```
{
  "コレクション": {
    "ドキュメント": {
      "配列名": [
        [
          "データ1": "data1",
          "データ2": "data2",
          ....
        ],
        [
          "データ1": "data1",
          "データ2": "data2",
          ....
        ],
        ....
      ]
    }
  }
}
```
以下から読めます！！
[【Firastore】ドキュメントの下に登録した配列　オブジェクトの値を追加、削除する（Swift）](https://tomatechnote.com/%e3%80%90firastore%e3%80%91%e3%83%89%e3%82%ad%e3%83%a5%e3%83%a1%e3%83%b3%e3%83%88%e3%81%ae%e4%b8%8b%e3%81%ab%e7%99%bb%e9%8c%b2%e3%81%97%e3%81%9f%e9%85%8d%e5%88%97%e3%80%80%e3%82%aa%e3%83%96%e3%82%b8/)
AZ-104合格のためにやったこと(2025/4/26)

2025/4/26にAZ-104を受験し無事合格したので、これから受験を考えている人のために情報共有です。
1000点満点で781点でした。(700点が合格ライン)
# 勉強を始める前の私について
- AI系のことをよく扱うITエンジニア(Azure OpenAIやAI SearchなどではAzureを使ったことがある)
- インフラやネットワークあたりのことはあまり知らない(例えば、DNSの仕組みやファイアウォールといった基本的なこともぼんやりとしか理解していない)
- AZ-104試験の下位資格であるAZ-900は受験していない
- 他のIT系の資格も特に持っていない

# 使った教材
- UdemyにあるAZ-104の模擬試験問題(→ [受講生30万講師のAZ-104: Microsoft Azure Administrator模擬試験問題集（400問）](https://www.udemy.com/course/30az-104-microsoft-azure-administrator240/?couponCode=V2JPLETSLEARN))
    - 実際の試験でもここにある問題とほぼ同じ問題が3問出た
    - 誤字脱字が散見されるが学習に支障はない
    - Udemyの仕様上、実際の試験とは形式が少し異なることに注意した方が良い(<font color="red">**この問題集では冒頭にケーススタディ問題があるが、実際の試験では見直し作業などを終えた後にケーススタディ問題が配置されている。私はそれに気づかず、ラスト3分でケーススタディ問題の5問をやる羽目になった。**</font>そこの出来は多分ボロボロ...)
    - 本番よりやや難しい？(勉強した結果Azureに詳しくなって本番が優しく感じただけかも？)
- 市販の参考書([徹底攻略 Microsoft Azure Administrator教科書[AZ-104]対応）](https://www.amazon.co.jp/%E6%A8%A1%E6%93%AC%E5%95%8F%E9%A1%8C%E4%BB%98%E3%81%8D-Microsoft-Azure-Administrator%E6%95%99%E7%A7%91%E6%9B%B8-AZ-104/dp/4295013692/ref=asc_df_4295013692?mcid=0bf7e7b5568e372097587fbf7d58191e&th=1&psc=1&tag=jpgo-22&linkCode=df0&hvadid=707442440817&hvpos=&hvnetw=g&hvrand=13198139943539086050&hvpone=&hvptwo=&hvqmt=&hvdev=c&hvdvcmdl=&hvlocint=&hvlocphy=1009331&hvtargid=pla-1639240210720&psc=1&gad_source=1)
    - 情報が少し古いが詳細まで書かれている(例:[古]Azure Active Directory → [新]Microsoft Entra ID)
    - 通読しようとすると時間がかかりすぎるので辞書的に使うと良い(私も元々は資格のために買ったのではなく、業務でAzureを使う場合の辞書として利用していた)
- [Microsoft Learn](https://learn.microsoft.com/ja-jp/training/)
    - Microsoftが提供する公式資料
    - 本番ではこの資料が閲覧できるが、膨大な資料群であるため、あらかじめどこにどの資料があるのかを理解しておいた方が良い

# 勉強方法
- Udemyの模擬試験問題集は①〜⑤に分かれているのですが、①〜④を解いてみて、間違った箇所と勘で当たってしまった問題を繰り返し復習した(⑤に触れなかったのは時間がなかったのと、明らかに他の回よりも質が低く感じられたから)
- 疑問点があったら市販の参考書の該当箇所やChatGPTに聞くなりして理解するように努めた
- およそ一月前から1~2時間程度勉強し、ラスト一週間くらいは4時間程度勉強した
- Microsoft Learnの確認練習。各サービスのプランごとの制約や条件などがどこにあるのか覚えておいた。(例:Azure Storageのストレージタイプごとに対応している冗長構成、Azure App Serviceのバックエンドで使うプログラミング言語がサポートするOSなど)