OmniAICreator/Qiita-1.07M huggingface dataset:
テスト112

```vb

Sub PDF_Proc()
'------------------------------------
'参照設定(Acrobat Proが必要)
'AcrobatまたはAdobe Acrobat 10.0 Type Library
'------------------------------------
Dim FSO As New Scripting.FileSystemObject
Dim Fol_Path1 As String
Dim Fol_Path2 As String
Dim PDF_Path1 As String
Dim PDF_Path2 As String
Dim PDF_SavePath As String
Dim New_Acro As New Acrobat.AcroPDDoc
Dim Acro_Join1 As New Acrobat.AcroPDDoc
Dim Acro_Join2 As New Acrobat.AcroPDDoc
Dim Page_Num1 As Long
Dim Page_Num2 As Long
Dim lRet As Long
Dim lPages As Long

'結合前
Fol_Path1 = "C:\Users\User\Desktop\VBA関連\VBA練習\PDF結合\結合前"

'結合後
Fol_Path2 = "C:\Users\User\Desktop\VBA関連\VBA練習\PDF結合\結合後"

PDF_Path1 = FSO.BuildPath(Fol_Path1, "テスト1.pdf")
PDF_Path2 = FSO.BuildPath(Fol_Path1, "テスト2.pdf")
PDF_SavePath = FSO.BuildPath(Fol_Path2, "保存.pdf")

'空のPDFファイルを作成する
lRet = New_Acro.Create()

lPages = 0

'PDFファイルを開く1
lRet = Acro_Join1.Open(PDF_Path1)

'ページ数を取得1
Page_Num1 = Acro_Join1.GetNumPages()

'空のPDFファイルに追加する
lRet = New_Acro.InsertPages(lPages - 1, Acro_Join1, 0, Page_Num1, False)

'PDFファイルを閉じる
lRet = Acro_Join1.Close()

lPages = lPages + Page_Num1
'-------------------------------------------
'PDFファイルを開く2
lRet = Acro_Join2.Open(PDF_Path2)

'ページ数を取得2
Page_Num2 = Acro_Join2.GetNumPages()

'PDFファイルを結合
lRet = New_Acro.InsertPages(lPages - 1, Acro_Join2, 0, Page_Num2, False)

'結合したPDFファイルを指定の場所に保存
lRet = New_Acro.Save(1, PDF_SavePath)

'PDFファイルを閉じる
lRet = Acro_Join2.Close()

'オブジェクト解放
Set New_Acro = Nothing
Set Acro_Join1 = Nothing
Set Acro_Join2 = Nothing

End Sub
Ubuntu 24.04に上げたら Rails で libMagickCore-6.Q16.so.6 が見つからなくなった話

## 状況の説明

私のRailsプロジェクトでは、画像処理に`ImageMagick`を利用しており、特に`rmagick`というGemを使っています。Ubuntuを`22.04`から`24.04`にアップグレードした後、アプリケーションをデプロイしようとしたところ、次のようなエラーが発生しました。

```zsh
LoadError: libMagickCore-6.Q16.so.6: cannot open shared object file: No such file or directory - /deploy/apps/langfix/shared/bundle/ruby/3.3.0/gems/rmagick-6.0.1/lib/RMagick2.so (LoadError)
```

このエラーは、`libMagickCore-6.Q16.so.6`という`ImageMagick`のライブラリが見つからないことを示しています。

## 原因

Ubuntuのバージョンアップによって、多くのライブラリも更新されます。その結果、依存関係にあるライブラリのバージョンが変更され、互換性がなくなることがあります。この場合、`ImageMagick`の新しいバージョンがインストールされ、古いライブラリファイルが削除されたため、`rmagick` Gemが参照する古いライブラリファイルにアクセスできなくなったらしい。

## 解決案

サーバー上の`/shared/bundle`ディレクトリを削除し、Railsアプリケーションを再デプロイしたら無事に治りました。

```zsh
rm -rf /deploy/apps/myapp/shared/bundle
```

Capistranoで再デプロイ：

```zsh
cap production deploy
```
Linuxコマンド

■treeコマンドがない時のカレントディレクトリ配下のTree表示
find . print | sed -e "s;[^/]*/; | __;g;s;__ |; | ; g"

■ディレクトリ全検索
find / -name "<検索対象名>"

■ファイル容量(k:キロバイト b:バイト m:メガバイト) ※パス指定なしならカレント
du -a -k "<ディレクトリパス>"

aオプション：ディレクトリ内のファイルやディレクトリについて、個別に表示(デフォルトはディレクトリのサマリーのみ表示)

■ディスク容量 ※パス指定なしならカレント
df -h --total "<ディレクトリパス>"

hオプション：読みやすい単位で表示
totalオプション：全体の合計を表示
メモ apt-mark hold : apt upgradeするときに特定のパッケージを除外する 

`apt upgrade`でパッケージを更新するとき，更新対象を特定のパッケージ以外にしたい場合がある．
`apt list --upgradable`の出力に対してawkやgrepなどを使って対象を絞り込み，その結果を渡すことで実現は可能．
ただ毎回そのようなコマンドを実行するのは面倒．
そこで便利なのが`apt-mark hold`．

これを使うと，特定のパッケージに対して，更新・削除等が行われないよう，保留対象としてマークできる．

https://manpages.ubuntu.com/manpages/kinetic/ja/man8/apt-mark.8.html

> hold
>     hold は、パッケージを保留としてマークします。パッケージを自動的なインストール、アップグレード、削除から防ぎます。

例えば，terraformを保留対象としたいときは以下のコマンドで可能．

```bash
> sudo apt-mark hold terraform
```

保留対象から除外するには以下のコマンドを実行．

```bash
> sudo apt-mark unhold terraform
```

保留対象の一覧を取得するには以下のコマンドを実行

```bash
> apt-mark showhold
```

※何かしらのパッケージの依存関係にあるパッケージを保留対象にすると面倒なことになるかもなので，そこは注意したほうが良さそう．
コンデンサ形計器用変圧器(CVT)の電圧に対する過渡応答解析

# はじめに
特別高圧の送電線の電圧を『直接』計測することは不可能に近い。理由としては、高電圧より高い絶縁性能を有していなければならない。そこで、コンデンサを用いて系統電圧を分流させることで電圧を低下させるという方法が考えられる。この方法で系統電圧を測定可能電圧にする機器をコンデンサ形計器用変圧器(以下、CVT)という。今回は、CVTを系統に接続した瞬間に生じるコンデンサ電圧の過渡応答についてPythonを用いてシミュレーションを行う。

# 問題設定とモデル
電験一種H30の電力管理の問3をモデルとする。詳しい問題設定は電験王様を参考にした。

https://denken-ou.com/c1/denryokukanrih30-3/

まず、CVTの回路設定は以下のとおりである。（画像は電験王様のサイトより引用した）　

![H30問3問題1-3(CVT回路図).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1686019/dff9c5d6-8192-8d1d-e81a-8f51424e288b.jpeg)

このように、まずは$C_1,C_2$で交流校電圧を分圧する。その後変圧器をとおして降圧させる。

ここで、$C_1,C_2$や系統をひとまとまりの巨大な電圧源$E$とするとテブナンの定理を用いることができる。したがって、以下のような回路に単純化することができる。（画像は電験王様のサイトより引用した）　

![H30問3解答2_等価回路tr1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1686019/8e3ac2a3-abf1-dbe1-2090-06a2aab30f57.jpeg)

さらに、トランスの励磁インダクタンスを$L_m$とし、$z$を開放として考え単純化すると以下のような回路図になる。（画像は電験王様のサイトより引用した）　

![H30問3解答3_等価回路.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1686019/d50baa8e-0a23-c66e-8bec-7e1f336b8f38.jpeg)

したがってコンデンサ電圧$v_c(t)$に関する微分方程式は

```math
v_c(t)+(L+L_m)\frac{di_c}{dt}=e(t)
```

ただし

```math
e(t)=\frac{C_1}{C_1+C_2}V sin \omega t
```
であるものとする。

そこで、コンデンサ全体に蓄積される電荷である$q_{CVT}$は以下のように表すことができる。

```math
q_{CVT}=(C_1+C_2)v_c(t)
```

まとめると、

```math
(L+L_m)(C_1+C_2)\frac{d^2 v_c}{dt^2}+v_c=\frac{C_1}{C_1+C_2} 
 V sin\omega t
```

ゆえに、

```math
\frac{d^2 v_c(t)}{dt^2}=\frac{1}{(L+L_m)(C_1+C_2)}(\frac{C_1}{C_1+C_2} 
 V sin\omega t -v_c(t))
```
ただし、初期条件は以下のとおりである。

```math
i_c(t=0)=0,v_c(t=0)=0
```







# アルゴリズム

アルゴリズムは微分方程式を離散化する差分法を用いる。詳しい説明等は以下の記事を参照されたい。

https://qiita.com/arairuca/items/54923ed3be68334f8d88

# プログラム
以下のようなプログラムを作成した。

``` python CVT_v.py
import numpy as np
import matplotlib.pyplot as plt
import japanize_matplotlib
import math
# 初期条件
## 電圧
v_c = 0
## 電流
i_c = 0
## 初期時刻
t=0
delta_t=0.00001
#記録用配列
Time=[]
V_c=[]
# 電源の定数
V=1.0
f=50
# パラメータ
C_1=50*10**-6
C_2=50*10**-6
L=1.0*10**-6
## L_mについて
Lm=1000
L_m=Lm*10**-6

while(t<0.02):
  v=V*np.sin(2*np.pi*f*t)
  E=(C_1/(C_1+C_2))*v
  dd_v_c= (E-v_c)/((L+L_m)*(C_1+C_2))
  i_c= i_c + dd_v_c*delta_t
  v_c= v_c + i_c*delta_t
  Time.append(t)
  V_c.append(v_c)
  t= t + delta_t
plt.xlabel("応答時間[s]")
plt.ylabel("電圧[V]")

plt.plot(Time,V_c)
    
#グラフを保存する
plt.savefig("CVTに生じる電圧の過渡応答解析_"+str(Lm)+".png")
#グラフの表示
plt.show()
```


# 結果
以下のような画像が出力される。
## Lmが小さいとき
$L_m=1[mH]$のときのグラフを以下に示す。
![CVTに生じる電圧の過渡応答解析_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1686019/f390e78d-914f-55f6-e8ec-9755fa381141.png)

## Lmが大きいとき
$L_m=1000[mH]$のときのグラフを以下に示す。
![CVTに生じる電圧の過渡応答解析_1000.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1686019/6eb097e8-d999-135d-0b5d-2ef8cac41546.png)

このように、CVTの投入直後において過渡的な低周波の電圧成分が存在しているということが分かる。


# まとめ
今回はCVTの過渡応答について差分法を用いたシミュレーションを行うことで調査した。そのための準備としてまず、テブナンの定理を用いて電源の等価回路を単純化し、変圧器をインダクタンスとみなすことで単純なLCで表される等価回路を作成した。その等価回路を用いてシミュレーションした結果、過渡応答電圧は系統周波数よりも低い低周波成分を含んでいるということが分かった。
【Unity】List Viewの中に複雑なUIを構築する（VisualElement.userData）【UI Toolkit】

# 自己紹介
サマーインターンでウハウハな大学生

# List View
UI ToolkitにはList ViewというVisual Elementが存在します。
基本的に、なにかの一覧を並べるものなのですが、少しだけ工夫しないとその一覧から選択するなど複雑なUIを正しく構築するのが難しいです。今回はそのTipsを紹介します。

## List Viewについて
List Viewの詳細は下記の記事が良くまとまっているので、ご覧ください。

https://qiita.com/tamutamuta/items/d330b87035ff29e4dc31

# Toggle in List View
以下のようなUIがあったとしましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3083521/7fdbf40e-3060-1458-83a4-68dd828925f7.png)

ストレートに考えると、イベント発火させたいときは以下のようなコードを書くと思います。
```csharp:間違ったコード
// なにかしらのList Viewの初期化処理

listView.makeItem += () => new Toggle();

listView.bindItem += (element, index) =>
{
    var toggle = (Toggle)element;
    toggle.text = strings[index];
    toggle.style.fontSize = slider.value;

    toggle.RegisterValueChangedCallback(e =>
    {
        Debug.Log(index);
    });
};
```

しかし、bindItemの発火タイミングを考えると、これではバグが生まれます。

## bindItemの発火タイミング
List Viewは画面に表示される要素だけを`makeItem`で生成し、そのVisual Elementを**使いまわす形**で、画面に表示していきます。これによって、パフォーマンスの最適化がされているのです。

`bindItem`はスクロールしていったときに要素が切り替わるときに呼び出されます。
そのため、要素が切り替わるタイミングで`RegisterValueChangeCallback`（`Register`なんちゃら全部）を動かしてしまうと、２重で登録されてしまうのです。
これによって、正しく発火しなかったり、使いまわす過程で表示が崩れたりします。

## indexがない問題
しかし、`makeItem`ではindexが取得できません。これは`VisualElement.userData`というプロパティを使うことで解決できます。このプロパティは`object`型です。つまり、なんでも入れられます。
`makeItem`でイベントを登録し、`bindItem`で`userData`にindexを入れることで更新するという処理を行うことが良いでしょう。

```csharp:正しいコード？
// なにかしらのList Viewの初期化処理

listView.makeItem += () =>
{
    var toggle = new Toggle();

    toggle.RegisterValueChangedCallback(e =>
    {
        var index = (int)toggle.userData;
        
        Debug.Log(index);
    });
    return toggle;
};

listView.bindItem += (element, index) =>
{
    var toggle = (Toggle)element;
    toggle.text = strings[index];
    toggle.style.fontSize = slider.value;

    toggle.userData = index;
};
```
## まだ終わらない、バグ
このコードを実行すると正しくイベントは発火されるはずです。
しかし、スクロールしていくと選択してもいない`Toggle`の`value`が`true`になる問題が発生します。
これは`VisualElement`を使いまわすからですね。

純粋に、状態を保存する配列やListを生成してあげるのが良いでしょう。

```csharp:正しいコード？
// なにかしらのList Viewの初期化処理
// なにかしらの状態を保存する配列などの初期化処理

listView.makeItem += () =>
{
    var toggle = new Toggle();

    toggle.RegisterValueChangedCallback(e =>
    {
        var index = (int)toggle.userData;
        _listState[index] = e.newValue;
        Debug.Log(index);
    });
    return toggle;
};

listView.bindItem += (element, index) =>
{
    var toggle = (Toggle)element;
    toggle.text = strings[index];
    toggle.style.fontSize = slider.value;
    
    toggle.SetValueWithoutNotify(_listState[index]);

    toggle.userData = index;
};
```
# おわり
癖がありますが、まぁ言われてみたらそうだよなって感じの内容でした。
ではでは、またお会いしましょう。いずれ、どこかで。
AndroidApp-Termuxでできること

久しぶりにAndroidを購入したらTermuxというアプリを知り、iPhoneには戻れないと思ったので出来ることを記述する

TermuxとはAndroid上内のTerminalを操作出来、Macrodroid(iPhoneでいうショートカットapp)との相性もよくオートメーションでコマンドを実行可能だ

# Termuxで出来ること

- unixとほぼ同様なコマンド操作が可能
- apk installでパッケージのインストールが可能
- vimの使用が可能
- sshでサーバーにリモートアクセスが可能
- Ubuntsuのインストール
- apkのビルド環境（検証中だが別サイトで実績あり）→GPU不足で厳しそうだった
- Python,Javaなどの実行環境
- 仮装環境構築
- データベースの作成
- termuxのAPIから通話履歴などを取得可能
- MacroDroidとの連携

# 導入方法

1.PlayStore又はF-DroidでTermuxをインストール（これはF-Droid推奨）
2.F-DroidでTermux-taskerをインストール(PlayStoreは有料でした)
3.F-DroidでTermux-APIをインストール
Unity Physicsを使う時に「Physics Shape」や「Physics Body」が見つからない時

# 環境
Unity Entities v1.2.4
Unity Physics v1.2.4

# 何があったか
UnityECSとPhysic併用する時に「Physics Shape」や「Physics Body」のコンポーネントが見つからなかった

# 解決方法
どうやら、Unity Physicsをインポートした後に、Samplesにある「Custom Physics Authoring」もインポートする必要がある様子
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/78379/2dde8b7a-c784-69be-9ab9-8b4c99c7b719.png)

# 結果
きちんとEntities用のコンポーネントが追加されました
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/78379/f08487c9-d7b6-e0f8-dccf-c888ecfb3d87.png)
バッチファイル

★exec.bat
※Tera Term呼び出しバッチ

@echo off

rem 第1引数:ユーザー名
rem 第2引数:パスワード
rem 第3引数:IPアドレス
rem 第4引数:TeraTermパス
rem 第5引数:TeraTermマクロ

rem 引数格納
set UserName=%~1
set UserPass=%~2
set IPAddr=%~3
set TTPMacroPath=%~4
set TTL=%~5

rem TeraTermマクロを実行
%TTPMacroPath% %TTL% %UserName% %UserPass% %IPAddr%
TTL

※exec.batから実行

call login
call exec

sendln 'exit'
end

:login
;
;ログイン情報
USERNAME = param2
PASSWORD = param3
IPADDR = param4

;ログインコマンド生成
COMMAND = IPADDR
strconcat COMMAND ' /ssh /auth=password /user='
strconcat COMMAND USERNAME
strconcat COMMAND ' /passwd='
strconcat COMMAND PASSWORD

;接続
connect COMMAND

if result <> 2 then
    messagebox 'サーバへの接続に失敗しました' 'error'
    return
end if

:exec
;
;入力できる状態まで待つ
wait '$'
CD = 'cd'
strconcat CD ' /<移動するパスを指定>/ '
sendln CD
wait '$'

;シェルを実行
RUN = 'bash <実行するシェル名>.sh'
sendln RUN
wait '$'
return
Flutterで開発したアプリをアプリストアに出したかった話（なお未公開）


# 筆者について

現在高校3年生
（元）水泳部マネージャー
生徒会長
（元）とある団体の代表

# 本編

## どんなアプリを開発したん？

ということで、早速ですがここで少しだけ開発したアプリを紹介させていただきます。
SOKFIというアプリ名で、水泳の計測タイムを記録するためのアプリになります。

https://www.sokfi.net/

このアプリ、実はお友達の水泳選手が「こんなアプリがあればな〜」っていう話をされたのがきっかけで開発しました。将来的には、チーム機能を付帯させて一括で記録を管理できるようにする予定です。

## アプリがGoogle Playで公開できない！！

さて、実はこのアプリについて、実はこのQiita上でもアドカレ23で記事にしており、すでにWeb上ではデプロイしています。

https://qiita.com/0_terarin_0/items/b92cfbc56853dc981f33

https://qiita.com/0_terarin_0/items/daed1262a16e9c8cd0c1

https://qiita.com/0_terarin_0/items/2ccfdba178c2a9308394

https://qiita.com/0_terarin_0/items/1a6f0df21458d98dce49

https://qiita.com/0_terarin_0/items/c4781880515d69fee82f

アプリURLは以下の通りです。

https://swim.app.sokfi.net/

そこで、皆さんに以下のような疑問が浮かぶと思います

:::note info
この記事の読者の正しい反応
「なんで去年末にできとってまだアプリをリリースできてないん？何しとったん？？」
:::

この反応は一般的には間違ってませんが、僕の状況においては実はどうしようもない問題だったのです。それについて書いているのが以下の記事です

https://qiita.com/0_terarin_0/items/5c2780041caf31f39b35

どういうことに触れているかというと、

- 金銭的な問題
- 責任問題
- 開発環境問題

その上、実はあまり当該記事では触れていないのですが、僕の家では全くもって支援というものがありませんでした。（金銭的な意味も含めて）

その上で、これらの問題を乗り越えるためには複数の問題が浮かび上がります

- Google PlayやApp Storeに提出うするのに登録費が必要である
- Google Playで公開するためにはAndroidの実機が必要である（もしかしたら必要ないかもしれませんが）

はて、困ったものです。まずそもそも当時は未成年でしたので誕生日を待つ必要がありました。その上で、デビットカードを持っていましたので外貨決済はできますが、そもそも現在は円安の時代です。以前の数倍の決済費用がかかりますので、これまた未成年の僕には厳しい状況です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/583428/09e8eb4d-1f39-5ef4-106f-944267fd6ad9.png)

（参照元：Google Finance）

ということで、頑張ってお金を貯めてとりあえずGoogle Playの開発者アカウントだけでも登録しようと頑張りました。そして、これで運いいのか悪いのか、あの日銀の利上げ決定からの一気に円安が進み、なんとかこれなら払えるだろうということで登録に踏み切ったものの…。

## Google Playで審査を通したら終わり！（と思ってました）

さて、アカウントも登録できたことやし、もうGoogle Playの審査にかけるだけや！とか思ってたんですけど。どうやらそんな簡単にはいかないことが発覚して

https://play.google.com/intl/ja/console/about/closed-testing/

さて、この問題、結構でかいです。そもそも僕の周りでAndroidを使ってる人なんてせいぜい３人程度です。どうやって集めるねんっていう、これ結構よくないポリシーだと思うんですけどね。

## どうやってレビュワー増やすねん問題（現在ここ）

Google Playのクローズドテストの方法は二つです。

- メーリングリストに試してくれる人のGoogle Playのメールアドレスを追加する
- Google Groupに参加してもらう

僕の場合は管理をしやすくするためにGoogle Groupを利用しました！が、ここにも落とし穴が。

:::note alert
思いもしない落とし穴（Google Workspace利用時）
ここで指定することのできるGoogle Groupはグループのメールアドレスが```@googlegroups.com```のグループのみです
:::

どういうことかと言いますと、Google Woekspaceのアカウントを利用している場合にはGoogle GroupのアドレスにWorkspaceに登録しているドメインを利用することができます
が、このアドレスを使えないというのが今回の落とし穴です

:::note alert
もしかしたら、僕がGoogle Playの開発者アカウントを個人で登録したからかもしれないです:(
:::

んで、そもそも20人のテスターをどうやってアルメルねん！っていうのに、今ぶち当たっています:( もし試していただける場合は、以下のGoogle Groupに参加してからグループ内のアプリリンクよりインストールしていただけると嬉しいです

https://groups.google.com/g/net-sokfi-app-swim_beta

# 最後に

今回のこの問題、きっと僕以外にもたくさんの人がレビュワーを集められない問題にぶち当たってると思います。20人じゃなくてせめて5人とかならいけるんですけどね、、、。ということで、ちょっと愚痴をこぼしながら終わりとさせていただきます。
CloudWatchの「Application Signals」


# はじめに
Cloudwatchで知らない機能が意外に多かったのでまとめていきたいと思います。
随時アップデートしていきます。


# Application Signals

今回は、「Application Signals」に着目してみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223559/34cfdd99-1715-d4e9-9eee-daf067d2518c.png)


Application Signalsだけでも
- サービス
- Service Map
- サービスレベル目標 (SLO)
- Synthetics Canaries
- RUM
- Evidently

と機能がとにかく多いです。

一旦、Cloudwatchの全体像も見てみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223559/c9f65ed0-4a15-246b-b637-28d889780397.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223559/92c78997-8eac-0403-1d9b-05f962ec7244.png)





## Cloud Synthetics

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223559/e50d924c-be8c-22d3-1648-69e8cc263447.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223559/9cd4533e-b15b-f424-1ddd-50a6b0e34efd.png)


## Real User Monitoring (RUM)
各リソースのメトリクスやログだけでは、ユーザーにどのように影響しているかわからない。`ユーザー視点`に立った監視も重要

- 概要
    - アプリのパフォーマンスに関するクライアントサイドのデータをリアルタイムで取得し、ユーザー体験を最適化
    - ブラウザやデバイスの種類、位置、ネットワークの接続性の問題などを把握する


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223559/d076b186-875b-8947-55fe-9a631e01b195.png)



https://pages.awscloud.com/rs/112-TZM-766/images/AWS-Black-Belt_2023_Amazon-CloudWatch-RUM_0430_v1.pdf

## CloudWath Evidently

新機能をこっそりリリースしたり新旧効果測定してくれる（機能フラグ＆A/Bテスト）
- 機能
    - 機能フラグ
    - A/Bテスト





## ServiceLens


- 概要
    - X-Ray、CloudWatch Logs、CloudWatch Metricsなどの各種サービスに散っていた情報（トレース、ログ、メトリクスなど）を、1か所に統合して表示してくれるサービス
    - CloudWatch ServiceLensを利用することでの追加料金は発生しません


# その他

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223559/e3caaa6f-b004-2114-f4f3-de4ecee12c3c.png)




# 参考資料


https://pages.awscloud.com/rs/112-TZM-766/images/AWS-Black-Belt_2023_AmazonCloudWatch_0330_v1.pdf


https://pages.awscloud.com/rs/112-TZM-766/images/AWS-Black-Belt_2023_Amazon-CloudWatch-Synthetics_0331_v1.pdf

https://qiita.com/minorun365/items/fbff5d0be798c09b6100

https://speakerdeck.com/o11yfes2023/observabilitysiyaniwoshi-xian-surutamenoawssahisu-cloudwatchbian?slide=3
ハッシュ関数とハッシュ値

## はじめに
この記事は、ハッシュ関数とハッシュ値について学んだことの備忘録となります。
ただのメモ書きのような記事で見づらいかもしれませんが、興味のある方は是非ご覧いただけますと幸いです。

## 1. ハッシュ関数とは
ハッシュ関数とは、任意の長さのデータを固定長のデータに変換する関数です。

#### ハッシュ関数の主な特徴

1. **固定長の出力**
任意の長さの入力データに対して、一定の長さのハッシュ値（固定長のビット列）を生成します。例えば、SHA-256は常に256ビットのハッシュ値を出力します。

2. **一方向性**
ハッシュ関数は一方向性であり、ハッシュ値から元の入力データを逆算することが非常に困難です。これは、暗号化やデータの検証において重要な性質です。

3. **高速計算**
入力データに対するハッシュ値の計算が高速に行われるため、大量のデータに対しても効率的にハッシュ値を求めることができます。

4. **わずかな変更による大きな変化**
入力データに少しでも変更を加えると、生成されるハッシュ値が大きく変わるという特性があります。これにより、データの変更を容易に検出できます。
また、同じ入力に対しては常に同じハッシュ値を返します。


## 2. ハッシュ値とは
ハッシュ値は、ハッシュ関数によって生成される固定長のデータです。ハッシュ値はデータの「指紋」とも言え、データの整合性を確認するために使用されます。

#### ハッシュ値の用途

1. **データの整合性チェック**
ファイルのハッシュ値を計算し、その値を保存しておくことで、後にファイルが改ざんされたり破損したりしていないかを検証できます。
例えば、ダウンロードしたファイルの整合性を確認するために使用されます。

2. **デジタル署名**
公開鍵暗号方式と組み合わせてメッセージやドキュメントのデジタル署名に使用されます。送信者はメッセージのハッシュ値を計算すると、それを自分の秘密鍵で署名し、受信者は署名を検証してメッセージの真正性と送信者の身元を確認します。

3. **パスワードの保管**
パスワードはそのまま保存されず、ハッシュ値として保存されることが一般的です。これにより、もしデータベースが侵害されても、攻撃者は元のパスワードを容易に取得できません。パスワードはハッシュ値に変換され、そのハッシュ値と比較されることで認証が行われます。

4. **キャッシュの管理**
キャッシュのキーとしてハッシュ値を利用することで、データの再計算を防ぎ、パフォーマンスを向上させることができます。ハッシュ値を用いることで、同じ入力に対して一貫したキャッシュキーを生成し、キャッシュの効果を最大化します。

5. **データベースのインデックス作成**
ハッシュテーブルを使用してデータベースの検索を高速化します。
データベースのキーに対してハッシュ値を計算し、そのハッシュ値を基にデータを素早く計算できるようにします。

## 3. 代表的なハッシュ関数

1. **MD5（Message Digest Algorithm 5）**
MD5は、128ビットのハッシュ値を生成する古典的なハッシュ関数です。かつては、ファイルの整合性チェックやパスワードハッシュに広く使われていました。しかし、現在は衝突のリスクが高く、セキュリティ用途には推奨されていません。

2. **SHA-1（Secure Hash Algorithm 1）**
160ビットのハッシュ値を生成し、MD5の後継としてデジタル署名や証明書の生成に使用されましたが、衝突が発見されており、こちらも現在はセキュリティ用途には非推奨です。

3. **SHA-2（Secure Hash Algorithm 2）**
SHA-224、SHA-256、SHA-384、SHA-512など複数のバリエーションがあり、ビット長が異なるハッシュ値を生成します。暗号通貨、デジタル署名、証明書の生成など、安全性が求められる場面で広く使用されており、SHA-1やMD5に比べて衝突耐性が強化されています。

4. **SHA-3（Secure Hash Algorithm 3）**
SHA-2の後継として設計されたハッシュ関数で、元々は別の暗号アルゴリズム（Keccak）から派生している。SHA-2の代替として、さらに高いセキュリティが求められる用途に使用されており、非常に高い衝突耐性を持っています。


## 4. Rubyでのハッシュ関数の使用例
Rubyには、標準ライブラリとしてDigestモジュールがあり、これを使ってハッシュ値を生成することができます。以下は、SHA256ハッシュを生成する例です。

```ruby:ruby
require 'digest'

data = "Hello, world!"
hash_value = Digest::SHA256.hexdigest(data)

puts "元のデータ: #{data}"
puts "ハッシュ値: #{hash_value}"
```

## まとめ
ハッシュ関数とハッシュ値は、情報セキュリティやデータ管理において欠かせない技術です。それぞれの特徴や使用例を理解することで、より安全なシステムを構築する手助けとなります。
Go実行バイナリの中身を知りたい！

# Go実行バイナリの中身が見たいんじゃ！！！
Ghidraとか使えないときもあるんじゃ！

## ファイル形式の確認
使用コマンド：file

アプリケーションが動くCPUアーキテクチャを特定できる
```bash
file bynary_app
```
## システムコールの監視
使用コマンド：strace

アプリが開いたファイル、通信先のURLを特定できる
```bash:アプリが確認しているファイルが何かを特定
strace -ff -s 4096 ./bynary_app 2>&1 | grep openat
```

## 可読部分の表示
使用コマンド：strings

アプリのコードに関わる情報が取れる
```bash
strings bynary_app
```


`grep`と連携すると5文字以上30文字以下の単語を抜き出すみたいなことも可能
```bash
strings ./bynary_app  | grep -E '^[a-zA-Z]{5,30}$' 
```
## AWS APIの呼び出しの監視
使用コマンド：tcpdump

アプリが使用しているAWSサービスの名前が特定できる
```bash
tcpdump -i eth0 -s 0 -vvv -l -n port 53
```

以下、よくお読みください。
# 注意事項  
このガイドでは、バイナリファイルの解析やネットワークトラフィックの監視に関するコマンドを紹介しています。これらのコマンドを使用する際には、以下の点に注意してください。  
  
1. **権限とプライバシー**: `strace`や`tcpdump`などのツールは、権限が必要な場合やプライバシーに関わる情報を扱う可能性があります。使用する際には、必ず自身の権限の範囲内で行い、他人のプライバシーを侵害しないようにしてください。  
  
2. **法的な遵守**: ネットワークトラフィックの監視やキャプチャは、法律に触れる可能性があります。特に、他人の通信を無断でキャプチャすることは違法です。必ず適用される法律を遵守してください。  
  
3. **セキュリティリスク**: バイナリ解析やネットワークキャプチャは、機密情報の漏洩につながる可能性があります。これらの操作を行う際には、リスクを十分に理解し、適切なセキュリティ対策を講じてください。
ワンクリックで動画をかんたんGIF変換のショートカット


変換後、GIFファイル保存前にクイックルックで確認できます。

https://x.com/maochanz/status/1829755550542021099

30 fps狙い の width 480 にしていますが、当然ショートカットなので好きに変更できます。

GIFサイズはやや大きいか。

作成したショートカットはメニューバーにも入れることができます。

![sc 2024-08-31 at 16.13.17.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588914/f61f554e-60f4-b39d-4543-b794d6f05a11.gif)

```
ショートカットクリック

    ↓

読み込み元動画ファイルの選択

    ↓  GIFへ変換

クイックルックによるプレビュー

    ↓

書き出し先フォルダの選択
                                                
    ↓

GIFファイル書き出し保存
```
「ワンクリック」ではなかったが、最低限の操作。

# 👩🏼‍💻 ダウンロード

[![sc 2024-08-31 at 17.47.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588914/3b671407-af69-8e65-07aa-13d8e12659b4.png)](https://www.icloud.com/shortcuts/ee4de22aedf946efa6c6076b673327bd)

[👉 ファイルを選んでGIFに変換](https://www.icloud.com/shortcuts/ee4de22aedf946efa6c6076b673327bd)

# 👩🏼‍💻 参考

https://support.apple.com/ja-jp/guide/shortcuts/welcome/ios

https://support.apple.com/ja-jp/guide/shortcuts-mac/apdf22b0444c/mac

https://qiita.com/chanzmao/items/14c79f45dc996cce86e7
Vault Secrets Operator on Kubernetes を理解したい

# Vault Secrets Operator on Kubernetes

これが素晴らしそう。vaultのUIで追加したpasswordがk8s secretに秒で同期される。

https://developer.hashicorp.com/vault/tutorials/kubernetes/vault-secrets-operator


構成を理解しようと図にしてみた。


![vault.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24737/24203684-8b2d-af11-eca9-000580740ead.png)

1. `vault-secrets-operator-controller-manager`
 が全namespaceの `VaultStaticSecret` を監視している
1. `VaultStaticSecret` があれば `VaultAuth` で指定された serviceaccount で `Vault secret` を取得する
    1. Vault は `Authentication methods` で指定されたserviceaccountが使われているか検証する（これで vault 認証が成功する)
    1. `Authentication methods` が持つ `policy` で vault secret が取得できれば ok
1. `vault-secrets-operator-controller-manager` は `VaultStaticSecret` の指定通りに `k8s secret` を作る


唯一わからなかったので `demo-operator` というserviceaccountの存在。なんのroke, clusterroleもbindされてないように見えた。何のためにあるんだろう？


これを理解すれば、アプリごとにvaultのuser/groupを切り替えるのもいけそうな気がする。
k8s apiがどう使われるのか理解してきたぞ！

---
# チームでの活用方法を考察

## Base
1. vault: authentication method: kubernets は1つでいい

## ガチガチ案
### サービスごとに作るもの
auth methodのroleをサービスごとに分ける。

1. vault: 
    1. secret engine の KV
    1. policy - ↑のKVへの権限
    1. authentication method: kubernets の role
        1. bound_service_account_namesは共通にしてもいい（全nsで同じsa名でもいい、実態はnsが変われば別のsaになるため
        1. bound_service_account_namespaces はサービスごとに異なる
2. k8s
    1. サービス専用のnamespace内に
        1. VaultStaticSecret
        1. ServiceAccount
        1. VaultAuth


## ゆるゆる案
auth methodのroleひとつを使い回す

### 作るもの
1. vault: 
    1. secret engine の KVs
    1. policy ひとつ。
        1. KVを増やすたびに追記していく、全KVへのread/list権限を持つpolicyになる
    1. authentication method: kubernets の role もひとつだけ
        1. bound_service_account_namesはひとつだけ（全nsで同じsa名にする
        1. bound_service_account_namespaces に全nsを追記していく
2. k8s
    1. サービス専用のnamespace内に
        1. VaultStaticSecret
        1. ServiceAccount
        1. VaultAuth


## 危険性の考察
- ゆるゆる案
    - policyがひとつ = 一つバレたら全部バレる
    - k8s appに攻撃者が入り、vault secretを見れるserviceaccountを利用されると、関係ないサービスのsecretも見えてしまう
    - しかし、アプリpodはそのsaを必要としないのでattachしなければいい、する必要がない
    - 実質的に被害は発生しないのでは？

## サービスごとにsecretは見えないようにしたい
- 正直secretになったらk8s clusterにアクセスすれば大体見える運用をしているところが多いのでは
- vault内でGUIから見えなくしたい
    - user/groupを分ければいい
    - 開発者にはほとんどの権限をあげる
        - KV作成、policy作成、Authentication Methods 編集
    - KVだけはgroupごとに見えないようにする

## サービスが増えたらやること
- KV作成
- groupのpolicyにKV追加して、サービス開発者にKVを共有
- auth roleにnamespace追加
- auth が紐づいてるvault operater用のpolicyにkv追加
- k8s ns内に VaultStaticSecret, sa, vaultauthを追加

## ダルだる案
1. vault-k8s間は全secretを見れるrole/policyにしてしまう
	1. e.g.
	```
	# 全secretのデータを読み取る権限を付与
	path "*" {
	  capabilities = ["read", "list"]
	}

	# 全secretのメタデータを読み取り、リストする権限を付与
	path "*/metadata/*" {
	  capabilities = ["read", "list"]
	}
	```
	2. group policyが別にあれば、UIでは他人のsecretは見えなくできる
2. そうすると vault auth methodの更新は namespaceの追加だけで良くなる
    1. policy編集の手間が減る、roleを増やす手間が減る


### だるだる案でサービスが増えたらやること
- KV作成
- groupのpolicyにKV追加して、サービス開発者にKVを共有
- auth roleにnamespace追加
- ~~auth が紐づいてるvault operater用のpolicyにkv追加~~ これが不要
- k8s ns内に VaultStaticSecret, sa, vaultauthを追加
MVV（ミッション・ビジョン・バリュー）ってなんじゃらほい

どうもこんにちは。

今回は、弊社の開発部勉強会で出てきたMVV（ミッション・ビジョン・バリュー）について調べて見ました。

自分がインターネットで調べてみた結果、「こういう風に考えました！」という内容になっているので、あくまでもひとつの考えとして捉えていただきたいです。

# MVV（ミッション・ビジョン・バリュー）とは？

MVVとは、企業が果たすべき「ミッション（Mission）」、目指す「ビジョン（Vision）」、そして行動の指針となる「バリュー（Value）」を明文化したものです。  
これらを明確にすることで、社内外のコミュニケーションを円滑にし、企業の存在意義を共有することができます。

## ミッション（Mission） = 企業が社会に対して「なすべきこと」

- <font color="red">**企業・組織が果たすべき使命や存在意義を表す言葉**</font>
- 例
    - なぜこの企業が存在するのか
    - なぜこのシステム開発部が存在するのか
    - この会社は社会にどのような価値を届ける必要があるのか
    - このシステム開発部は社会にどのような価値を届ける必要があるのか

## ビジョン（Vision） = 企業・組織が目指す「あるべき姿」

- <font color="blue">**企業・組織の理想像、中長期的な目標を表す言葉**</font>
- 例
    - ミッションを実現するために、企業はどのような状況になるべきか
    - ミッションを実現するために、システム開発部はどのような状況になるべきか
    - ミッションを実現するために、システム開発部はどのような開発体制を持っているべきか
    - ミッションを実現するために、どのような志であるべきか

## バリュー（Value） = 企業・組織の構成員が具体的に「やるべきこと」

- <font color="green">**ミッションやビジョンを達成するための具体的な行動指針、行動基準を表す言葉**</font>
- 企業・組織の構成員の行動や判断の基準となる価値観

# MVVの同義語

同じような意味を持つ言葉で、<font color="red">**ステートメント、フィロソフィー、スローガン、クレド**</font>があるようです。
また、<font color="red">**ミッションを企業理念、ビジョンを経営理念、バリューを行動指針**</font>として対応させることもあるようですね。

# MVVの重要性

MVVは、企業のアイデンティティを形成し、経営判断や目標設定の基盤となります。また、従業員のエンゲージメントを高め、組織の一体感を醸成する役割を果たします。

# MVVの作り方

## 1. ミッションを決定

- 企業や組織が何を達成すべきなのか、ミッションを決定

:::note warn
MVVは企業・組織の根幹に関わることなので、事業理解の深いメンバーがそろうことが重要です
:::

## 2. ビジョンの決定

- 企業や組織が、ミッションを達成するためにどのような状況が必要なのかを決定
- そのためには、企業や組織の立ち位置を明確にする必要がある

:::note info
企業の場合、少なくとも以下について考える必要がある。
- 顧客が自社に求めるニーズ
- 競合のMVV
- 自社のポジショニング
:::

## 3. バリューの決定

- 企業や組織が、ミッション/ビジョンを達成するためにどのような行動/業務が必要になるのかを決定

:::note warn
初期の決定も必要だが、決定後にPDCAサイクルを回し、「**本当にミッション/ビジョンにつながる行動ができているのか**」「**現在をよりよくすることはできないか**」を確認,議論し、**Kaizen**していく必要がある。

確認、議論には、現場のメンバーを巻き込む必要がある。
:::

# MVVを策定する際の４つのポイント

## ポイント1．MVVのつながりを意識する

ミッション、ビジョン、バリューのつながりを意識した内容で策定することが大切です。
MVVに一貫性を持たせることで、社員の行動イメージの解像度が上がり、普段から意識しやすくなります。

## ポイント2．頭に残る情報量にする

MVVは、ぱっと見て内容が理解できるボリュームにします。
多すぎる項目や長すぎる文章を避け、社員全員が頭に入れておける情報量を意識してください。

## ポイント3．共感できる言葉にする

表現の格好良さではなく、広く理解が得られる言葉を選びます。
耳慣れない横文字の言葉や人によって解釈が分かれる言葉、難解な言葉の使用は避けましょう。

## ポイント4．時代や社会性を踏まえる

企業視点で伝えたい内容だけでなく、時代性・社会性にあった言葉選びや表現を意識しましょう。
特に現在はネットやSNSの発達で、企業情報を目にする接点が多いので、企業のメッセージがリスクになる可能性もあります。

# MVVを浸透させるための4つのポイント

MVVは策定するだけでなく、社内外に浸透させることが重要です。そのためのポイントをまとめます。

## ポイント1． 継続的な発信

MVVの言葉通りの効果はすぐに表れません。中長期的に実現するものとして、継続して発信していくことが大切です。

## ポイント2． 企業、組織のトップからの発信

企業、組織のトップが発信することで、重要なコンテンツであることを伝えます。
MVVは内容を頭に入れておくだけでなく、情緒的に伝わることも重要です。
企業、組織のトップの言葉で噛み砕いて、熱量を持って発信することで、より強く社員への浸透を図ることが期待できます。

## ポイント3． 会社、組織ごとに紐付ける・組み込む

人事・表彰のタイミングや、決算や新人の入社研修などの会社、組織ごとの都度、発信する機会をつくります。

## ポイント4． 周知だけでなく、「理解」の機会をつくる

ペーペー社員の自分としては、これが一番重要に感じています。

ただ発信の機会を増やすだけでなく、MVVを理解する機会をつくることも重要です。
MVVに関する社内ワークショップを行うことで社員同士がMVVについて考え、語り合い、より深い共通認識の醸成が期待できます。

# まとめ

自分で調べてみて、MVVは企業や組織が前進するために必要なものだと感じました。
特に、企業、組織として決定するのは重要ですが、さらに細かく、プロジェクトごとに決定することで方針を間違えることなく進むことができるのではないかと感じました。

あくまでも個人的な感想です。
Hyper-V 有効化(インストール)

:::note warn
注意
こちらの記事は個人作業の中で備忘録として作成しております。
環境によって手順や動作の差異が生じる可能性のあることをご了承下さい。
:::

Hyper-V ManagerをローカルPCで使用するためには、Hyper-Vをインストールする必要がある。

# 手順
コントロールパネルを開き、「プログラムと機能」をクリックする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3472352/b63750c6-785f-7d95-86b1-f48fb42649ec.png)

「Windowsの機能の有効化または無効化」をクリックする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3472352/237edecc-1fcf-8677-ac58-db69ad8b8648.png)

Hyper-Vに☑を入れて「OK」をクリックする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3472352/1dfced16-0c6a-b342-77b8-a71277000e92.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3472352/a030d717-174f-0fef-2d3f-1d0681b37655.png)

変更のインストールを完了するには、PCの再起動が必要である。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3472352/055b1bd2-8089-2eb4-ef68-327c9231be80.png)

再起動後に、Hyper-Vを起動出来たら、作業完了となる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3472352/cd9d3462-3508-d889-4c41-06f2a97b544d.png)
Build env for Tensor-flow and ML on Mac(Apple Silicon)

# agenda

* homebrew
* pyenv
* metal as GPU driver
* tensor-flow

# homebrew

```
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
```

https://brew.sh

# pyenv

```
brew install xz
brew install pyenv
```

```
echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.zshrc
echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.zshrc
echo 'eval "$(pyenv init -)"' >> ~/.zshrc

source ~/.zshrc
```
https://qiita.com/shott/items/7c4e1b7c5fb53805685d

```
pyenv install --list
pyenv install 3.12.9
pyenv global 3.12.9
```

# miniforge

```sh:install
brew install miniforge
```

## conda init

```
conda init
```

.bash_profileに追記された"conda initialize"というコメントを現在のログインシェルに転記する

```sh:.zshrc
# >>> conda initialize >>>
# !! Contents within this block are managed by 'conda init' !!
__conda_setup="$('/opt/homebrew/Caskroom/miniforge/base/bin/conda' 'shell.bash' 'hook' 2> /dev/null)"
if [ $? -eq 0 ]; then
    eval "$__conda_setup"
else
    if [ -f "/opt/homebrew/Caskroom/miniforge/base/etc/profile.d/conda.sh" ]; then
        . "/opt/homebrew/Caskroom/miniforge/base/etc/profile.d/conda.sh"
    else
        export PATH="/opt/homebrew/Caskroom/miniforge/base/bin:$PATH"
    fi
fi
unset __conda_setup
# <<< conda initialize <<<
```

```
exec -l $SHELl
```

再ログインし、プロンプトに"(base)"と付けばOK

## conda create env

condaの環境設定について、スクラップ&ビルドをしやすくするために名前をつけておく
```
conda env list
```
この時点では、baseのみの想定

```
conda create -n py312 python=3.12
conda activate py312
```
※activateに失敗する場合はターミナルの確実な再起動（Dock上のアイコンにて終了を選択する）または、PCの再起動をする

なお、作った環境以外を使いたい場合などはdeactivateする
```
conda deactivate
```

# tensorflow

```
python -m pip install tensorflow-macos
python -m pip install tensorflow-metal
```

## check

```
python

import tensorflow as tf
print(tf.__version__)

exit()
```

https://zenn.dev/datasciencekun/articles/74b764515ca246

https://qiita.com/kujirahand/items/9bf1a1e7bd34bdb87da5

# transformer

```
conda install -c huggingface transformers
```

# ML

```
conda install -c conda-forge scikit-learn
conda install -c conda-forge pandas
conda install -c conda-forge jupyterlab notebook
conda install -c pytorch torchvision
```

```
conda install -c conda-forge matplotlib
conda install -c conda-forge polars
conda install -c conda-forge metpy
conda install -c scitools cartopy
```
AWS Elastic Container Registry 概要から詳細まで

# はじめに

イメージを作成してコンテナとして起動するというユースケースは頻繁に行われるものです。その際のレジストリとしてECRを使用すると思いますが簡単にまとめてみました。意外と知られていない機能もあると思うのでお時間があるときにチェックしてみてはいかがでしょうか。





## ECRとは

- コンテナイメージレジストリのマネージドサービス
- Docker CLIコマンドを利用することが可能
- コンテナイメージ/アーティファクトはS3に保存
- サポートされる形式
    - Dockerイメージ(v1,v2)
    - Open Container Initiative (OCI)イメージ
    - OCI互換 artifacts




- 機能
    - `レジストリ`
        - AWSアカウントごとに用意される
        - イメージを保存するためのリポジトリを複数作成可能
        - `Dockerクライアント`を認証して利用
    - `リポジトリ`
        - Dockerイメージ、OCIイメージ、OCIC互換アーティファクトを保存
    - リポジトリポリシー
        - リポジトリとリポジトリ内のイメージへのアクセス権を制御
    - 認証トークン
        - IAM認証情報を利用して、レジストリに対して認証をする

- アクセスタイプ
    - プライベートレジストリ
        - 利用するにはIAMベースの認証が必須
    - パブリックレジストリ
        - ECR Public
        - 認証なしでもイメージのpullが可能
        - 公開されているイメージを検索可能


- ECR Docker Credential Helper
    - Docker Deamon向けのcredential helper
    - Dockerクライアントからより簡単にECRを利用することが可能
    - プライベートレジストリ/パブリックレジストリ共に利用可能


## Replication (※Private Only.)

- コンテナイメージを別リージョン・別AWSアカウントにレプリケーションすることが可能
- イメージをpushした際に自動でレプリケーションされる
- 脆弱性の検出結果はレプリケーションされない

https://dev.classmethod.jp/articles/20240229-ecr-crr/


## Pull Through Cache(※ Private Only.)

- パブリックレジストリのイメージをECR Privateでキャッシュ
- リモートレジストリと自動同期
- アップストリームのパブリックレジストリとしてECR PublicとQuay.ioをサポート

https://dev.classmethod.jp/articles/ecr-pull-through-cache-repositories/



## Repository creation template

透過的なリポジトリ作成が行われた際の初期設定値を リポジトリ作成テンプレート 機能によってコントロール（自動でセットアップ）できるようになりました。



![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223559/d93d9b0e-d037-89b2-6ecf-3425cac4a62c.png)

https://dev.classmethod.jp/articles/ga-amazon-ecr-repository-creation-template/



## Scanning(※ Private Only.)

ECRにpushされたコンテナイメージの脆弱性の検出をする

### (1)Basic scanning

- 検出対象 : OSパッケージ
- スキャン実行 : on push / マニュアル実行(任意のタイミング可)
- 追加料金なしで利用可能
- スキャンエンジン : Clairと呼ばれるオープンソースの脆弱性データベースを使用
    - https://github.com/quay/clair/blob/main/README.md
- スキャンの結果の重要度は2パターンで評価
    - 1.アップストリームディストリビューションのCVE番号
    - 2.CVSS(共通脆弱性評価システム)スコア

※update

https://dev.classmethod.jp/articles/new-version-amazon-ecr-basic-scanning/


### (2)Enhanced scanning

- 検出対象 : OSパッケージ、プログラミング言語パッケージ (ここも対応しているのが肝)
- スキャン実行
    - プッシュ時
    - 継続的なスキャン : Inspectorの脆弱性データベースにCVEが追加されたタイミングでスキャンが実施
- スキャンエンジン : Inspector (+Synk)
- AWS統合サービス : Organizations、SecurityHub、EventBridge

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223559/10e4eb41-506e-e2af-f155-9f1e97ae4fbd.png)




#### Inspector Classic と v2 について

https://dev.classmethod.jp/articles/difference-from-classic-and-v2/

https://dev.classmethod.jp/articles/introduction-2024-amazon-inspector/

https://dev.classmethod.jp/articles/inspector-v2-scan-requirements/



https://docs.aws.amazon.com/ja_jp/inspector/latest/user/slr-permissions-agentless.html




## ECR Public Gallery
- ECR public で公開されているコンテナイメージを検索
- Docker公式イメージも利用可能
- App Runnerサービスを直接起動することも可能





# 参考文献

https://pages.awscloud.com/rs/112-TZM-766/images/202112_AWS_BlackBelt_CON241_ECR.pdf

https://dev.classmethod.jp/articles/snyk-scan-ecr-images/

かなりニッチなECR情報。興味がある方はぜひ

https://zenn.dev/yoshii0110/articles/1de56128d7be2c
基本情報取りたいなぁ。

## 基本情報技術者試験受験
エンジニアの登竜門としておすすめされる"基本情報技術者試験"。私は、その資格を約半年間延期したままにしている。
もうそろそろ、決着をつけたい、、、けど、めんどくさい、、、

社会人1年目。意外にも、私はIT業界に籍を置いている。“意外”というのは、就活中の私にとって選択肢になかった業界だからだ。今考えれば、文系からエンジニアになっている人がたくさんいることは分かっているものの、就職先が決まった当時は周りから驚かれたものだった。

そのため、私はプログラミングだけでなくIT業界の知識が全くない。しかし、周りには知識猛者がたくさんいる。そのギャップ差を埋めるために、4月後半に"基本情報技術者試験"を受験することを決意した。

ジリジリジリジリ……　気づけば、8月後半…　あれ、、

基本情報まだとってない。

気づけば、楽しみにしているイベントにばっか目を向けていて、基本情報の存在が薄れていた。

"まぁ、また今度にすればいっか"、延期をした回数なんて数えきれない。場所も、日時も、好きなように決められるCBT受験に甘えてばっかいたのだ。
なんせ、5分くらいポチポチすればすぐに予定変更できちゃうんだから。

予約から日時変更、日時変更、日時変更、日時変更…を繰り返す中で、
気づいたことがある。

自己管理能力の低さが故に、自分に対する自信がなくなっていることを。

エンジニアの登竜門すら合格できていないことだったり、悪くはないのに楽しい休日の時間に試験が過ってしまったり、試験が終わればなーって思いつつ、全然行動に移せていなかったり。

そこで、私はここに書き留めておくことにする。

# 絶対に、8/24の試験で合格する！

合格して、すっきりするんだ！とりま、合格しておくんだ！
そして、報奨金をゲットして欲しいものでも買おう！

…そうです。結局、金なんです。

お金の余裕は、心の余裕につながりますもんね(o^―^o)ﾆｺ
「WebSocket is closed before the connection is established」が出たときの原因

# トラブル
- webアプリケーションを作っているときに，サーバとwebsocketで通信をできるようにしたい
- 接続しようとするとうまくできず，ブラウザの開発者ツールで次のエラーが出る
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2510180/5eaf497c-a5b1-9409-b9d4-c5036170f7a5.png)

# 原因の特定方法
よくある原因としては<font color="#0072BC">**「ブラウザのページのロードが完了してない時にwebsocketの接続をしようとしている」**</font>というものである．

これが原因かどうかは次の手順を踏んで確認してください．

1. ブラウザでF12キーなどを押して開発者ツールを開き，コンソールに次のようにコマンドを直打ちして実行してみてください
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2510180/2420c984-2a5b-93f9-6dd5-083219745277.png)

2. もしエラーが出ずに，次のように問題なくwebSocketのインスタンスが返ってきた場合，上記の青字の内容が原因であることがほぼ確定します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2510180/b3c544e7-990a-60a2-77c5-af34c9923140.png)

# 解決策
vanillaのjavascriptで書いてる場合は，次のように<div>**ページロード時に起動する関数を`EventListener`で設定すればOK**</div>

```javascript:javascriptの設定
var webSocket; // ウェブソケット
window.addEventListener('load', connect); // ページロード時にconnectを起動するように設定

// サーバとの通信を接続する関数
function connect(){
  webSocket = new WebSocket("ws://localhost:8000/");

  webSocket.onmessage = (message) => {
    console.log(message.data);
  };
}

function sendMessage(){
  // ...
}
```

# 解決策（Next.jsの場合）
どうやら開発者モードで作業する際，`reactStrictMode`を`true`にしていると，useEffectが2回呼び出されるらしい（[参考](https://stackoverflow.com/questions/60618844/react-hooks-useeffect-is-called-twice-even-if-an-empty-array-is-used-as-an-ar)）．
1回目の呼び出しの際はwebsocketを一瞬繋いですぐに接続を切るためか，エラーが発生してしまう．

解決策としては次の2つが考えられる．
- （**正しい対応**）「next.config.js」に書かれている`reactStrictMode`を`false`にする
- （**応急処置**）下のソースコードのように，useEffectのreturn文をコメントアウトする

```javascript
export default function Home() {
  const [ws, setWs] = useState(null);

  // 起動時にwebsocketを接続するuseEffect
  useEffect(() => {
    const socket = new WebSocket(url);

    socket.onmessage = (event) => {
      console.log(event.data);
    };

    // useEffectではコンポーネントのアンマウント時に起動する関数をreturnする
    // !!! ここでのトラブルを解決するために，接続解除する関数のreturnをコメントアウト !!!
    // return () => {
    //   socket.close()
    // };
  }, []);

  return <div>WebSocket Test Page</div>;
}
```

# おわりに
- アプリケーション作るときに毎回同じ失敗してるので覚書として残します．
- 他の誰かも同じ問題を抱えているはず...
- 修正前の問題があるソースコードでも環境を変えるとうまく接続できたりするので，ぶっちゃけよくわかってないところがある．
Ubuntu 22.04 で GitHub Desktopのお試し. 

防備録です。これを読まれた方のご参考になれば幸いです。

https://github.com/shiftkey/desktop?tab=readme-ov-file#github-desktop---the-linux-fork

```
$ wget -qO - https://apt.packages.shiftkey.dev/gpg.key | gpg --dearmor | sudo tee /usr/share/keyrings/shiftkey-packages.gpg > /dev/null
$ sudo sh -c 'echo "deb [arch=amd64 signed-by=/usr/share/keyrings/shiftkey-packages.gpg] https://apt.packages.shiftkey.dev/ubuntu/ any main" > /etc/apt/sources.list.d/shiftkey-packages.list'
```

とりあえず起動した、あとは使い方かな!
M5StackCore2: FIFO のキューのサンプル

## プログラム

```text:fifo_example.ino
#include <string>
#include <queue>

std::queue<std::string> stringQueue;

void setup() {
	Serial.begin(115200);
	Serial.println("*** setup aaa ***");
	delay(1000);
	Serial.println("*** setup bbb ***");
	delay(1000);
	Serial.println("*** setup ccc ***");
	delay(1000);
}

void loop() {
	// キューに文字列を追加する
	stringQueue.push("Hello");
	stringQueue.push("World");
	stringQueue.push("Arduino");
	stringQueue.push("Good");
	stringQueue.push("Morning");

	// キューから文字列を取り出す
	while (!stringQueue.empty()) {
		std::string str = stringQueue.front();
		stringQueue.pop();
		Serial.println(str.c_str());
	delay(1000);
	}

	Serial.println("");

	delay(2000);
}
```

## Arduino IDE
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179446/a4ef3334-a5ec-78c3-c162-8eaaef891a75.png)
【備忘録】AWSのEC2にローカルからファイルを送信

AWSのEC2環境でデータベースの学習をしようと環境構築に調べながら試行錯誤しています。その過程を備忘録として残します。

## 1.EC2にSSH接続確認
ターミナルから以下コマンドを実行
```terminal:Console
ssh -i [プライベートキーのパス] [ユーザー名]@XXXXXX.XXXX.amazonaws.com
```

接続できたらこんな感じのが出てくる。鳥？
```terminal:Console
   ,     #_
   ~\_  ####_        Amazon Linux 2023
  ~~  \_#####\
  ~~     \###|
  ~~       \#/ ___   https://aws.amazon.com/linux/amazon-linux-2023
   ~~       V~' '->
    ~~~         /
      ~~._.   _/
         _/ _/
       _/m/'
```

## 2.別でターミナルを開く(ローカル)
ローカルのターミナルを開いたら、ec2へファイルを送信する。

```terminal:Console
scp -i [プライベートキーのパス] [送信ファイルパス] [ユーザー名]@[パブリックID]:[送信先パス]
```

## 3.送信できてるか確認
EC2のターミナルの方で送信ファイルがあるか確認
```terminal:Console
[ec2-user@ip-XXX-XX-XX-XX ~]$ ll
total 0
-rw-r--r--. 1 ec2-user ec2-user 0 Aug 31 04:31 test.txt
```

送信できていますね。


## 終わりに
初めてQiitaで記事を書いてみました。これからエンジニアとして技術的なアウトプットの場として使っていきたいと考えています。
今の所しょぼい記事ですが、今後もっとブラッシュアップさせていこうと思います。
【備忘録】Webアプリ開発体験インターンシップ備忘録

どうもこんにちは。

今回は、8月後半に[弊社](https://www.pdc-ds.com/)で行なったインターンシップの概要について備忘録として残したいと思います。
ご参加いただいた学生にとってはかなり満足度の高いコンテンツとなったようでしたので、参考になればと思います。

# 概要

Ruby on Railsを使用してWebアプリケーションを開発してもらい、デプロイ、試験を通じてシステム開発の一連の流れを体験してもらう。

# 募集人数

5人

# 前提

本インターンシップで開発してもらったWebアプリは以下です。

**レベル1**: 自分用タスク管理アプリ（Todo機能のみ）
**レベル2**: 自部署用タスク管理アプリ（Todo + ユーザ管理 + ログイン/ログアウト）
**レベル3**: 社内用タスク管理アプリ（Todo + ユーザ管理 + ログイン/ログアウト + 部署管理）

上記のように、レベル/進捗状況によって、成果物を分けました。
（レベル1が終わったらレベル2、レベル2が終わったらレベル3というように実施）

最初のタスク管理アプリを開発するための枠組みは、自分の方で作っています。

- Docker, DockerComposeを使用してタスク管理アプリの枠を作成
    - 枠組みの内容は、ViewファイルやControllerファイルは用意しているが、コーディングがされていない、必要なコードが欠けているという状態です
- 作成した枠組みはgitリポジトリへ

# 実施スケジュール

本インターンは、5日間で実施しました。また、3,4日目の間に3日間の休み期間を設けました。

## 1日目

- タスク管理アプリの要件、概要、設計内容を説明
    - 要件定義から設計までできれば良かったが、今回は自分の方で対応
- AWS Cloud9で開発環境のセットアップ
    - VScode + Dockerを使用した開発も検討しましたが、Cloud9が最も環境を合わせやすいと判断
    - https://aws.amazon.com/jp/cloud9/
    - 今までCloud9を使用して来なかった場合は、新規利用ができなくなっているので、PaizaCloudなどのクラウド型IDEを使用するのをお勧めします
- 基本的な部分の開発
    - Viewはどのように記述すれば良いか、Controllerはどのように記述すれば良いかというのを説明しながら、一緒にコーディング

## 2日目

- 開発実施
    - 2日目からは、自力で開発
    - 質問は適宜受けています

## 3日目

- 開発実施
    - 2日目同様
- 中間成果発表
    - 3日間でどれだけできたか、1人5分程度で発表

## 休み期間

- 強制ではなく任意課題として、自身の気になるところや頑張りたいところを開発
    - 今回参加してくれた学生は、CSS（デザインやレイアウトのところ）に力を入れてくれました

## 4日目

- AWS ElasticBeanstalkでデプロイ体験
    - EC2やElasticBeanstalk,VPCの説明をしたのち、ebコマンドを使用してデプロイ
- 試験仕様書作成
    - 普段は、[Testrail](https://www.techmatrix.co.jp/product/testrail/)を使用してテスト仕様書の管理をしていますが、今回はExcelを使用してテスト仕様書の作成を実施

https://www.techmatrix.co.jp/product/testrail/

## 5日目

- 試験実施
    - 4日目に作成してもらったテスト仕様書を使用してテストを実施
    - 不具合箇所については、[Backlog](https://backlog.com/ja/)に課題を追加
- 不具合箇所の修正
    - Backlogに追加してもらった課題を解決するために、修正作業
- 最終成果発表
    - 経営陣などに向けてどのようなシステムを開発したのかを発表

https://backlog.com/ja/

# 結果

70点かなぁ...

## 理由

- 要件定義、設計も学生にやってもらえれば、学生の満足度はもっと上がるはず
    - その分、各学生の考え方や適正を図ることができる
    - ただし、日程調整などがシビアになる
- デプロイ体験時にエラーが発生してしまったこと
    - 原因は、各学生がユーザ管理機能を開発したときに各環境のマイグレーションファイルのバージョンが異なっていたこと
    - マイグレーションファイルって、`2024083014001010_create_users.rb`みたいに日時でバージョン管理がされているんですよね
    - これが異なっていると、同じDB構成のアプリケーションをデプロイしようとしても、「**またusersテーブル作るの？**」って怒られます
    - エラーの解消に手間取ってグダってしまったので、ここは反省...

# まとめ

こちらとしても新しい気づきを得ることができたインターンシップになりました。

来年はAWS API GatewayやLambda,DynamoDBを使ってサーバーレスのアプリケーションを作ってもらうのもありかなぁと考えている今日この頃です。

https://aws.amazon.com/jp/api-gateway/

https://aws.amazon.com/jp/lambda/

https://aws.amazon.com/jp/dynamodb/

以上
sftp-server と sshfs のプロセス間通信について

## 概要
コンテナや仮想マシンとホストマシンの間で、ファイルシステムを共有するにはどうすればいいのかを知りたくて、いくつかのOSSのソースコードを読んでみた。

今回は `sftp-server` と `sshfs` の組み合わせを題材に、その方法をまとめていく。
なお `sshfs` は現在積極的な開発が行われていないようなのでご注意。


## 結論
ローカルで `sftp-server` を立てて、リモートからの `sshfs`（ローカルからのsshコマンドとして実行）と接続できるようにする。

そのために両プロセスの入出力を相互に接続してプロセス間通信を行う。
`sshfs` の manページの表現を借りると、`passive` オプションにある `communicate  over  stdin  and  stdout  bypassing  network` というもの。

例えば Golang だとコマンドの入出力を宣言的に記述できるが、これをシェルで行う場合は名前付きパイプ（FIFO）を使って実現する。

```sh
fifo=/tmp/fifo-$$
mkfifo -m600 "$fifo"
```

これであたかも存在するファイルのようにパイプを扱うことができる。
この名前付きパイプへの write/read は他プロセスからの read/write があるまで、待機してくれるようなイメージ。

このパイプを使って、`sftp-server` と `sshfs` の入出力を接続し、プロセス間通信を行うには、このように書く。

```sh
< "$fifo" /usr/libexec/sftp-server -e -d /path/to/dir | ssh -p <port> <host> -- sshfs ":/path/to/dir" "/path/to/mountpoint" -o slave -o allow_other > "$fifo"
```

これで、リモートとローカルのファイルシステムを同期できる。
ざっくり同期には1秒ほどかかり、同期されない場合はどちらかのディレクトリで ls などを叩くと反映される。


## 参考
https://github.com/rom1v/rsshfs:embed:cite
ReFTでファインチューニングして保存したモデルがロードできない時の解決方法

LLMのファインチューニングを効率的に行うことのできる[ReFT](https://arxiv.org/abs/2404.03592)という手法を、Pythonの`pyreft`ライブラリでやってみました。
ファインチューニングしたモデルを保存するところまでは難なくできたのですが、その保存したモデルをロードするところでエラーが発生し、解決までにしばらく時間を要してしまったので、その時の備忘録です。

# 起きていたエラー
[公式](https://github.com/stanfordnlp/pyreft/blob/main/main_demo.ipynb)の方法と同様にファインチューニングしたモデルを以下のように読み込もうとしました。ちなみに環境としてはGoogle Colabを使っていました。インストールした`pyreft`のバージョンとしては`pyreft==0.0.6`でした。
```python
import torch, transformers, pyreft
device = "cuda"

model_name_or_path = "microsoft/Phi-3-mini-4k-instruct"
model = transformers.AutoModelForCausalLM.from_pretrained(
    model_name_or_path, torch_dtype=torch.bfloat16, device_map=device, trust_remote_code=True
)

model_path = "./finetuned-model"
reft_model = pyreft.ReftModel.load(model_path, model)
```
そうしたらなぜか以下のようにキーエラーが出てしまった。
```
KeyError: <class 'transformers_modules.microsoft.Phi-3-mini-4k-instruct.5a516f86087853f9d560c95eb9209c1d4ed9ff69.modeling_phi3.Phi3ForCausalLM'>
```

# 解決方法
まず、ファインチューニング元となった事前学習モデルを再び用意し、ファインチューニング時のReFTの設定とともに、ReFTモデルを作成します。
そして、torch.loadやtorch.saveを使って、ファインチューニングで保存した重みを手動で読み込ませます。
ReFTはニューラルネットワーク構造の中間層に介入(intervention)していますが、最後にその介入情報をkey-valueペアとしてreft_model.interventionsに保存してあげます。

具体的なコードは以下のようになります。
```Python
import os
import torch, transformers, pyreft
device = "cuda"

#ReFTを利用してファインチューニングしたときに使った、元々の事前学習モデルをロードする。
model_name_or_path = "microsoft/Phi-3-mini-4k-instruct" 
model = transformers.AutoModelForCausalLM.from_pretrained(
    model_name_or_path, torch_dtype=torch.bfloat16, device_map=device, trust_remote_code=True
)

#ファインチューニングで使ったPyReFTの設定を定義する。
layers = range(model.config.num_hidden_layers)
representations = [{
    "component": f"model.layers[{l}].output",
    "intervention": pyreft.LoreftIntervention(
        embed_dim=model.config.hidden_size,
        low_rank_dimension=4
    )
} for l in layers]

reft_config = pyreft.ReftConfig(representations=representations)

# 元々の事前学習モデルとPyReFTの設定からReFTモデルを作る。
reft_model = pyreft.get_reft_model(model, reft_config)

##### ここまではファインチューニング前と共通の下準備。 #####
##### ここからがファインチューニングして保存したモデルをロードする部分。 #####

# 保存したReFTモデルを読み込む。
save_directory = f"./finetuned-model"
interventions = {}
for l in layers:
    component = f"model.layers[{l}].output"
    file_path = os.path.join(save_directory, f"intkey_comp.{component}.unit.pos.nunit.1#0.bin")
    if os.path.exists(file_path):
        with open(file_path, "rb") as f:
            adjusted_key = f"comp.{component}.unit.pos.nunit.1#0"
            interventions[adjusted_key] = torch.load(f)

# torchで手動で読み込んだ重みをReFTモデルに適用する。
for component, state_dict in interventions.items():
    if component in reft_model.interventions:
        reft_model.interventions[component][0].load_state_dict(state_dict)
    else:
        print(f"キーが見つかりません: {component} は reft_model.interventions 内にありませんでした。")
```

なぜ公式のようにすんなりロードができないのかは謎です。
まだ新しいライブラリだから変な挙動を起こしているだけ？？
まあ個人的にはReFTの仕組みの理解の手助けにもなって良かったかもです〜
関数の再帰呼び出しを入れ子で図示して理解を深める

関数の再帰呼び出しを理解するのは、なかなか難しいです。
関数が自分自身を再度呼び出すので、入れ子になっているような図を書いて理解を深めるとよいです。

# ハノイの塔の解を求める関数と再帰呼び出しの入れ子イメージ
ハノイの塔の解の求め方は、
+ なんにせよ円盤nより小さい円盤(n-1)はいったんfromからworkに退避させて、
+ 一番大きな円盤nをfromからtoに移動させ、
+ 退避させた円盤(n-1)をworkからtoに移動させる

これを円盤の大きさをどんどん小さくさせて、同じことを繰り返させて(自分自身を再帰的に呼び出す)解いていきます。

次のように入れ子の図を書いてみると理解が深まります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/76786/cfa9e697-4a41-6e06-ff16-301855ce6a84.png)

```JavaScript:ハノイの塔の解を求める
1:const _hanoi=(n, from, to, work)=>{
2:  if (n === 1) {
3:    console.log(`円盤:${n}を杭${from}から杭${to}へ。`);
4:  } else {
5:    _hanoi(n - 1, from, work, to);
6:    console.log(`円盤:${n}を杭${from}から杭${to}へ。`);
7:    _hanoi(n - 1, work, to, from);
8:  }
9:};
```

次に示す階乗の解を求める関数の再帰呼び出しに比べて、ハノイの塔の解を求める関数の再帰呼び出しの難しいところは、5行と7行の関数再帰呼び出しに挟まれる形で、6行目の``console.log``があるところ。2回も再帰呼び出しがあるのと、間に挟まれた``console.log``がどうなるのかよくわからなくなるところだと思います。

関数呼び出しを入れ子の図にして、その時の引数の状態を書き出してみると理解が深まります。


# 階乗の解を求める関数と再帰呼び出しの入れ子イメージ
次の図は``factorial(2)``を求めるときに、どのような入れ子になっているかを図示しています。
そして、下階層の戻り値を元に現在階層の計算をしているところを矢印で示しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/76786/b98f8cc4-97de-527e-c885-79a503d936d5.png)

```JavaScript:階乗の解を求める
1:const factorial=(n)=>{
2:  if (n === 0) {
3:    return 1;
4:  } else {
5:    return n * factorial(n-1);
6:  }
7:};
```
TypeScriptにおける、RestAPIとGraphQL

### RestAPIとGraphQLの比較

#### RestAPI

RestAPIはリソースベースのアーキテクチャスタイルで、HTTPメソッドを使用してリソースを操作します。以下に、Express.jsとTypeScriptを使用したシンプルなRestAPIの例を示します。

```typescript
import express, { Request, Response } from 'express';
import bodyParser from 'body-parser';

const app = express();
const port = 3000;

app.use(bodyParser.json());

interface User {
  id: number;
  name: string;
}

const users: User[] = [
  { id: 1, name: 'Alice' },
  { id: 2, name: 'Bob' },
];

// 全ユーザーを取得
app.get('/users', (req: Request, res: Response) => {
  res.json(users);
});

// 特定のユーザーをIDで取得
app.get('/users/:id', (req: Request, res: Response) => {
  const user = users.find(u => u.id === parseInt(req.params.id));
  if (user) {
    res.json(user);
  } else {
    res.status(404).send('User not found');
  }
});

// 新しいユーザーを追加
app.post('/users', (req: Request, res: Response) => {
  const newUser: User = req.body;
  users.push(newUser);
  res.status(201).json(newUser);
});

app.listen(port, () => {
  console.log(`Server running at http://localhost:${port}`);
});
```

#### GraphQL

GraphQLは、クライアントが必要なデータを正確に指定できる柔軟なクエリ言語です。以下に、Apollo ServerとTypeScriptを使用したシンプルなGraphQL APIの例を示します。

```typescript
import { ApolloServer, gql } from 'apollo-server';

const typeDefs = gql`
  type User {
    id: ID!
    name: String!
  }

  type Query {
    users: [User!]!
    user(id: ID!): User
  }

  type Mutation {
    addUser(name: String!): User!
  }
`;

interface User {
  id: number;
  name: string;
}

const users: User[] = [
  { id: 1, name: 'Alice' },
  { id: 2, name: 'Bob' },
];

const resolvers = {
  Query: {
    users: () => users,
    user: (_: any, { id }: { id: number }) => users.find(user => user.id === id),
  },
  Mutation: {
    addUser: (_: any, { name }: { name: string }) => {
      const newUser = { id: users.length + 1, name };
      users.push(newUser);
      return newUser;
    },
  },
};

const server = new ApolloServer({ typeDefs, resolvers });

server.listen().then(({ url }) => {
  console.log(`Server ready at ${url}`);
});
```

#### 比較と解説

1. **エンドポイントの数**:
   - **RestAPI**: 各リソースに対して個別のエンドポイントがあります。例えば、`/users`と`/users/:id`。
   - **GraphQL**: 単一のエンドポイント（例: `/graphql`）で全てのリクエストを処理します。

2. **データ取得の方法**:
   - **RestAPI**: 固定されたエンドポイントとHTTPメソッドを使用してデータを取得します。例えば、全ユーザーを取得するためには`GET /users`を使用します。
   - **GraphQL**: クライアントはクエリを使用して必要なデータを指定します。例えば、全ユーザーを取得するためには次のようなクエリを使用します:
     ```graphql
     {
       users {
         id
         name
       }
     }
     ```

3. **柔軟性**:
   - **RestAPI**: エンドポイントごとに決まったデータを返すため、柔軟性が低いです。
   - **GraphQL**: クライアントが必要なフィールドを指定できるため、柔軟性が高いです。

4. **ステート管理**:
   - **RestAPI**: 各リクエストは独立しており、サーバーはクライアントの状態を保持しません（ステートレス）。
   - **GraphQL**: 同様にステートレスですが、クライアント側でのステート管理が柔軟に行えます。

5. **リアルタイムデータ**:
   - **RestAPI**: 通常はポーリングやWebSocketを併用してリアルタイムデータを取得します。
   - **GraphQL**: サブスクリプションを使用してリアルタイムデータを直接取得できます。