OmniAICreator/Qiita-1.07M huggingface dataset:
Wi-Fi 6 (IEEE 802.11ax)と EasyMesh の疑問を検証してみる

# 知ってるようで知らないWi-Fi
Wi-Fiという言葉は知ってる。でも、中身はよく知らない。
最近、Wi-Fi6やEasyMeshやら新しい言葉も聞く。
なので素朴な疑問に基づいて、Wi-Fi6やEasyMeshを検証してみる。

# 素朴な疑問
素朴な疑問として、
・Wi-Fi 6ってなに？　IEEE 802.11ax？　どのくらい速いの
・ルーターの性能で、1201Mbsとかってなに？帯域幅？ストリーム数？
・5GHzと2.4GHzってホントはどんだけちがうの？
・Wi-Fi EasyMeshが気になるんだけど、実際どんなもんなの？

# ルーター買い換えたいんだけど、どれを買えばいいの？
そりゃ、高い機種が性能はいいんだろうけれど。予算もあるよね。コスパも大事だよね。
でも遅いのは困る。

# 検索しても、実測値は出てこないのは何で？
検索しても検索しても、実際にどうなのかはさっぱりわからない。
5GHzは速くて2.4GHzは届きやすいとか、EasyMeshにすると激速とか。抽象的な話ばかり。
本当にそうなの？　実際にはどうなの？

# 全然わからないから、自分で検証する
いろんなメーカーがルーターを出しているけど、Buffaloの
WSR-3200AX4S
WSR-1800AX4
がたまたま手元にあるので、これで検証してみる。

# 5Ghzと2.4Ghzってホントはどんだけちがうの？
通説では。
・5Ghzは速い。
・2.4Ghzは届きやすい。

でも5GhzはWi-Fi5だからでないの？2.4GhzはWi-Fi4だから条件違うよね？

検証してみる。
WSR-1800AX4は
5GHz：IEEE 802.11ax:1201Mbps (80MHz 2x2)
2.4GHz：IEEE 802.11ax:573Mbps (40MHz 2x2)
に対応している。どっちもWi-Fi6だ。


設定初期値では5GHzと2.4GHzで帯域幅が違う。ちゃんとそろえてみた。
【初期値】
5GHz：IEEE 802.11ax:1201Mbps (80MHz 2x2)
2.4GHz：IEEE 802.11ax:573Mbps (40MHz 2x2)

【検証時】


帯域幅とストリーム数をそろえたので、周波数以外のスペックは同じだ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176228/2d2cc361-94d9-a626-43b7-29de85c451a4.png)



至近距離で接続して、Fast.comで測定してみる。
使用したのは、Google Pixel 6a 。Wi-Fi6対応は、これしか持ってないのだ！

# 5Ghzで実測
5Ghzのアクセスポイントに接続。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176228/3416c2d4-5286-7237-df28-d2ebe5deb830.png)

スペック通りのリンク速度になった。
fast.comで測定。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176228/4418d602-4508-a945-6f5e-1d35ca6352aa.png)

リンク速度の8割ってところ。

# 2.4Ghzで実測
2.4Ghzのアクセスポイントに接続。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176228/f2dcde28-ea9b-ace3-6474-9c68225b5fbd.png)
間違いなくWi-Fi 6で接続。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176228/d230c153-d9b8-4f52-4fdf-337bb0c6a54a.png)
そもそもスペック通りでリンクしない・・・至近距離なのに。

実測。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176228/d147fb9c-b116-c6d7-c605-a0096e5f609a.png)
確かに2.4Ghzのほうが遅い。だけど・・・そもそものリンク速度が違うんだからねぇ。
3回試したが、リンク速度は変わらず。

# 2.4Ghzは届きやすい
アクセスポイントから離れていく。ここならぎりぎり入る、というところで、
2.4Ghz
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176228/b72eefac-2e45-f327-5c97-3d1799767b4f.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176228/af190132-b19b-d8fa-3bb2-67f32737f690.png)
さすが2.4Ghz。離れても届くんだね。


# 実は5Ghzのほうが届く
次は同じ場所で5Ghz。リンク速度は似たり寄ったり。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176228/19d71681-b429-00b3-5906-a2550fcc49b7.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176228/8b1fc457-c7e6-c6f3-8f2c-57ec2a554f1d.png)


5Ghzのほうが早い。ええっそうなの！？いや、リンク速度より速いのはおかしくね？

# 念のため実測2回目
2.4Ghz
今度は接続すらしない…

5Ghz
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176228/870d9827-5f49-5372-1d6a-5f057cbf504f.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176228/bd754201-5c55-eab8-ad9e-0784cc555d00.png)



ということで、通説とは違って5Ghzのほうが届く。

つづく
Next.js + Amplifyでのデプロイ

## Objective

- ローカルでNext.jsアプリ作成
- GitHubでリポジトリ管理
- AWS Amplifyを用いたデプロイ

## Environment

- GitHub
- node 18.12.1
- npm 8.19.2

## Create Next.js App

Next.jsアプリの作成

1. 開発用ディレクトリで以下のコマンドを実行

```
npx create-next-app deploy-front
```

1. TypeScriptとESLintを使うか聞かれるのでYes

```
? Would you like to use TypeScript with this project? › No / Yes
? Would you like to use ESLint with this project? › No / Yes
```

1. プロジェクトのフォルダが作成されるので作成したディレクトリに移動、サーバー起動

```
cd next-sample
```
```
npm run dev
```

1. ログに表示される `http://localhost:3000` にアクセスして画面表示されればOK
![](https://storage.googleapis.com/zenn-user-upload/76b16e3f64a3-20230819.png)


## Push to GitHub

1. GitHubにリポジトリ作成(以下リポジトリ名を `deploy-front` とする)
2. 先ほど作成したNext.jsアプリのディレクトリで以下コマンド実行
```
git init
git add .
git commit -m 'first commit'
git remote add origin {作成したリポジトリURL}
git push origin main
```
3. GitHubを確認してプッシュできていればOK

## Hosting with Amplify

Amplifyでホスティングします

1. AWS > AWS Amplify > New app > Host web appからホスティングするコードを選択
![](https://storage.googleapis.com/zenn-user-upload/1bd3cf017a3a-20230819.png)
2. GitHubを選択
![](https://storage.googleapis.com/zenn-user-upload/53bd2a4cdc0d-20230819.png)
3. 作成したリポジトリとブランチを選択
![](https://storage.googleapis.com/zenn-user-upload/1c38d6661585-20230819.png)
4. アプリ名、ビルド設定、IAM Roleを設定
![](https://storage.googleapis.com/zenn-user-upload/b5d9ef9d83b4-20230819.png)
5. Review画面で確認して問題なければデプロイ
6. デプロイに成功してアプリの詳細画面に行くとこんな感じ
![](https://storage.googleapis.com/zenn-user-upload/01a9bd691ca0-20230819.png)
7. 黒塗り部分にamplifyのURLが表示されるので、アクセスするとローカルで確認した画面が表示されます
![](https://storage.googleapis.com/zenn-user-upload/3f18618e68bb-20230819.png)

## Continuous Deploy
Amplifyで作成したコードはGitHubに紐づいているので、実際にmainブランチにpushされると、自動で検知してデプロイまでやってくれます
VSCodeのRemote Developmentを使って、Vagrantにリモートアクセスする

# Vagrantのsshの設定を確認する
vagrantを起動し、以下のコマンドを実行してください。
HostName、User、Port、IdentityFileの値をメモしておいてください。
```
% vagrant ssh-config
Host default
  HostName 127.0.0.1
  User vagrant
  Port 2222
  IdentityFile xxxx/private_key
```  

# SSHの接続を設定する
.ssh/configファイルに、先ほど、メモした値を記述して、保存してください。
HostはSSHの接続先が分かるような、好きな名前を記述してください。
```
Host ubuntu
    HostName 127.0.0.1 
    Port 2222
    User vagrant
    IdentityFile
```

# VSCodeのリモートエクスプローラーをインストールする
VS Codeを起動して、[拡張機能]をクリックしてください。
![スクリーンショット 2023-08-19 8.25.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14202/e87d12c5-c52d-da0a-9b21-cdea5aa0f65c.png)  
  
[Remote Development]をインストールしてください。
![スクリーンショット 2023-08-19 8.27.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14202/a3e47ff0-14a3-5d74-d1db-7fa4e623e137.png)  
 
# リモートエクスプローラーを使い、SSHで接続する
[リモートエクスプローラー]をクリックしてください。
![スクリーンショット 2023-08-19 8.29.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14202/0e3219f4-35e2-a944-16b1-3e17ca94c75a.png)

[SSH] - [ubuntu] - [新しいウィンドウで接続...]をクリックしてください。
ubuntuは、.ssh/configのHostに記述した名前になります。
接続先を削除したい場合は、.ssh/configの記述を削除してください。
![スクリーンショット 2023-08-20 6.46.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14202/1e847a8f-9f94-7be9-4542-95bed778c1e5.png)
  
以上
Leetcode 70. Climbing Stairs

# Problem
この問題では、n段の階段を1段または2段ずつ上る場合の独自の方法の数を求めることが目的です。つまり、いくつの異なる組み合わせで階段の上まで到達できるかを計算することが求められています。

> You are climbing a staircase. It takes n steps to reach the top. Each time you can either climb 1 or 2 steps. In how many distinct ways can you climb to the top?

InputとOutputの例は次になります。
```
Input: n = 3
Output: 3
Explanation: There are three ways to climb to the top.
1. 1 step + 1 step + 1 step
2. 1 step + 2 steps
3. 2 steps + 1 step
```

https://leetcode.com/problems/climbing-stairs/description/

## Key Idea
この問題はクラシックな問題で、再帰、Dynamic Programming(動的計画法）、またはそのコンビネーションによって解くことができます。

具体例を使って考えると、この問題の構造がフィボナッチ数列に似ていることがわかります。
- n = 1の場合、1通りの方法しかありません（1段登る）。
- n = 2の場合、2通りの方法があります（1段ずつ登る、または2段一度に登る）。
- n = 3の場合、n = 2の場合の方法に1段登る方法を追加したものと、n = 1の場合の方法に2段登る方法を追加したもので、計3通りの方法があります。
- n = 4の場合、n = 3の場合の方法に1段登る方法を追加したものと、n = 2の場合の方法に2段登る方法を追加したもので、計5通りの方法があります。


これを一般化すると、n段登る方法の数は、n-1段登る方法の数とn-2段登る方法の数の和になります。


## Approach #1 再帰
「n段登る方法の数は、n-1段登る方法の数とn-2段登る方法の数の和」ということから再帰を使うことが考えられます。ただし、再帰を使った解法は、O(2^n)であるため、Leetcodeでは、Time Limit Exceededになってしまいます。
```python
class Solution:
    def climbStairs(self, n: int) -> int:

        if n == 1:
            return 1
        elif n == 2:
            return 2
        return self.climbStairs(n-1) + self.climbStairs(n-2)
```

## Approach #2 Dynamic Programming (A)
Dynamic Programmingでは、計算結果を途中で保存しておき、再利用することで効率的に問題を解くことができます。
具体的には、Decision treeのようにN段目までの到達方法をパターン分岐させると、下記のようになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99549/ca8611f6-d0cd-333e-0b6b-98e93a1f39f8.png)

N段目までの到達方法をメモしておく場合、ほとんど枝刈りをすることができ、O(n)の計算量になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99549/0e826b30-f331-6afe-a78a-8db0a63ec4cf.png)

下記がサンプルコードになります。dpという配列についてですが、0番目は初期化状態のまま特に利用せず、1番目からn番目までを使っています。そのため、要素数はn+1個含むように配列を初期化しています。

```python
class Solution:
    def climbStairs(self, n: int) -> int:

        if n == 1:
            return 1
        elif n == 2:
            return 2
        
        dp = [None] * (n + 1)
        dp[1] = 1
        dp[2] = 2
        for i in range(3, n+1):
            dp[i] = dp[i-1] + dp[i-2]
            print(i)

        return dp[n]
```

## Approach 3: Dynamic Programming (B)
Approach 2の方法は、配列を使うことでO(n)の空間計算量を必要としていました。しかし、n段登る方法の数は、n-1段登る方法の数とn-2段登る方法の数の和になるため、それぞれを、prev1とprev2という変数に保存しておけば、メモリを効率化させることができます。

```Python
class Solution:
    def climbStairs(self, n: int) -> int:

        if n == 1:
            return 1
        elif n == 2:
            return 2
        
        prev1 = 1 # n-1段登る方法の数
        prev2 = 2 # n-2段登る方法の数

        for i in range(3, n+1):
            current = prev1 + prev2

            prev1 = prev2
            prev2 = current

        return current
```

## Complexity Analysis
計算量は下記の様になります。

| Approach  |Time complexity  | Space complexity |
|:---:|:---:|:---:|
|1. Recursion   | O(2^n) | O(n) |
|2. DP(A)   | O(n)  | O(n)  | 
|3. DP(B)   |  O(n) | O(1)  |



## Reference
https://www.youtube.com/watch?v=Y0lT9Fck7qI

https://leetcode.com/problems/fibonacci-number/description/
Mac USキーボードにて[を含めたショートカットが効かない問題の解決方法

# 起こっていた事象
最近PCをM2 MacBook AirのUSキーボードに買い替えました。
いつものようにObsidianにメモをまとめていたところ、インデント(cmd+])は問題なく効くものの、アンインデント(cmd+[)が効かないことに気づきました。
色々試し、キーボードの不具合やObsidian特有の問題ではなく(BigQueryのエディタでも同様の事象が起きる)そもそも[を含めたショートカットが効きません。

# 解決方法
以下の記事を見て解決しました。

https://teratail.com/questions/364989

自分はfn(地球儀キー)とcmdボタンを入れ替えており、記事の解決内容と若干異なる入力である左fn(入れ替えたcmdキー)+右cmd(入れ替えたfnキー)+[をすることでショートカットが効くことが確認できました。

小一時間ほど試行錯誤したので、VS codeなど同様の事象でお困りの方はぜひ試してみてください！
useを最短で理解してみる【importの違いを知りたい】

サンプルソース作成

# useの場合

```Elixir:ymn.ex
defmodule Ymn do
  use Hoge

  def hello do
    :world
  end

end
```


```Elixir:hoge.ex
defmodule Hoge do
  defmacro __using__(_opts) do
    quote do
      def use_hello() do
        "use_hogehoge"
      end
    end
  end
end
```

## 実行結果
```Elixir
iex(1)> Ymn.use_hello
"use_hogehoge"

iex(2)> Ymn.hello
:world
```

Ymnモジュールでuse_helloが使えることを確認
共通化するときに便利そう

【感想】使いすぎると混乱しそう

# importの場合

```Elixir:ymn.ex
defmodule Ymn do
  import Hoge

  def hello do
    :world
  end

  def test do
    import_hello()
  end

end
```

```Elixir:hoge.ex
defmodule Hoge do
  def import_hello do
    "import_hogehoge"
  end
end
```

## 実行結果
```Elixir
iex(1)> Ymn.test
"import_hogehoge"

iex(2)> Ymn.test
"import_hogehoge"

iex(3)> Ymn.import_hello
** (UndefinedFunctionError) function Ymn.import_hello/0 is undefined or private
    (ymn 0.1.0) Ymn.import_hello()
    iex:1: (file)

```

Ymnモジュールでtest関数の中にあるimport_helloが呼べることを確認
useと違って直接Ymn.import_helloは呼べない
PlantUMLのセットアップ

# はじめに
最近は時間のある時にUnityの勉強をしております。入門書を一冊購入したのですが、こちらがとても興味深く、良い出会いを得たと喜んでおります。本の紹介は別途させていただきますが、今回はそんな本の内容を整理するべく、UMLにしようと考えております。
UMLの作り方には色々とあるようですが、その中でもPlantUMLというものが検索にヒットしましたので、こちらを導入してまいります。
環境はMacでのインストールとなります。
VSCodeも必要となりますが、こちらは既にインストール済みでしたので、割愛させていただきます。
参考にしましたのは、以下のサイト様です。

https://qiita.com/BlueEventHorizon/items/fd5e8733e539f7b26da4

# 目次
<!-- タイトルとアンカー名を編集 -->
1. [インストール手順](#Chapter1)
1. [HomeBrewのインストール](#Chapter2)
1. [Javaのインストール](#Chapter3)
1. [Graphvizのインストール](#Chapter4)
1. [PlantUMLのインストール](#Chapter5)

<a id="Chapter1"></a>
# インストール手順

インストール手順は、以下の通りです。
PlantUMLはJavaで動くようですので、JavaをHomeBrewを使って導入するようです。

1. HomeBrewのインストール
1. Javaのインストール
1. Graphvizのインストール
1. PlantUMLのインストール


<a id="Chapter2"></a>
# HomeBrewのインストール

HomeBrewは、Macのインストーラのようです。
こちらをインストールし、Javaを手に入れます。
HomeBrewは、以下のホームページで入手することが可能です。

https://brew.sh/index_ja

macOSを利用している場合は、.pkgインストーラの利用を推奨されておりましたので、こちらを利用してまいります。
GitHubからpkgをダウンロードいたします。
ですが、ダウンロード後にpkgを実行しても、Command Line Toolsが無いですよとお叱りを受けてしまいます。
これはxcode経由で入手することができるようです。以下のサイト様を参考にさせていただきました。

https://qiita.com/payreikit/items/4bb0f863afc7b56d0809

以下のコマンドでインストールいたしました。

```bash
xcode-select --install
```

これでpkgを実行することで、HomeBrewのインストールができます。
ですが、このままではHomeBrewを実行することができません。どうやらMacのM1チップやM2チップを利用している場合、パスが通っていないようです。
気にせずにbrewコマンドをターミナルで実行しようとしても、command not foundエラーが出てしまいます。
パスを通すには、zshrcに定義いたしました。以下のサイト様を参考にさせていただきました。

https://qiita.com/howaito01/items/242ba23e8a8fb00dac05

```bash
echo $path | grep brew
```
このコマンドを実行した時に何も出力されない場合、brewのパスが通っていないことになります。
以下のコマンドでシェルの種類をご確認ください。

```bash
echo $SHELL
```

私の場合は、/bin/zshと表示されました。
そこでvimを使ってzshrcファイルを作成します。

```bash
vim ~/.zshrc
```

中には以下を記載します。
```bash
typeset -U path PATH
path=(
  /opt/homebrew/bin(N-/)
  /opt/homebrew/sbin(N-/)
  /usr/bin
  /usr/sbin
  /bin
  /sbin
  /usr/local/bin(N-/)
  /usr/local/sbin(N-/)
  /Library/Apple/usr/bin
)
```

これが終わりましたら、最後に反映します。

```bash
source ~/.zshrc
```

<a id="Chapter3"></a>
# Javaのインストール

javaのインストールには、jdkを使います。先ほどインストールしたHomeBrewを使って、jdkをインストールしてまいります。こちらもM1チップやM2チップを使っている場合、以下のコマンドとなります。

```bash
brew install openjdk
```

こちらでインストールすると、出力結果に以下のコマンドを実行するように記載がありましたので、実行いたします。

```bash
sudo ln -sfn /usr/local/opt/openjdk/libexec/openjdk.jdk /Library/Java/JavaVirtualMachines/openjdk.jdk
echo 'export PATH="/opt/homebrew/opt/openjdk/bin:$PATH"' >> ~/.zshrc
export CPPFLAGS="-I/opt/homebrew/opt/openjdk/include"
source ~/.zshrc
```

この３つのコマンドにより、Javaのインストールが完了します。
試しに以下のコマンドを実行してみましたところ、Javaのバージョンを表示させることができましたので、問題無いことを確認できます。

```bash
java -version
```

<a id="Chapter4"></a>
# Graphvizのインストール

続いてGraphvizをインストールいたします。
PlantUMLで図を描画するために必要な処理だそうです。
こちらはbrewでインストールすることで、すんなりとインストールすることができました。

```bash
brew install graphviz
```

<a id="Chapter5"></a>
# PlantUMLのインストール

最後にPlantUMLをインストールしてまいります。
VSCodeからインストールすることになります。
VSCodeを開き、Command＋PでPlantUMLを検索します。
対象に対し、インストールボタンをクリックすれば完了となります。
termuxで立ち上げたsshdに繋がらない時の対処法

# 状態
F-Droid経由からインストールしたTermux環境にsshdを入れて起動しても別クライアントから繋がらない(Connection refusedと言われる)。

# 原因
sshdのバインド先が不適切だった。
sshd -dして状態を見ると
```
debug1: Bind to port 8022 on 0.0.0.0
Server listening on 0.0.0.0 port 8022
```
ipアドレスが虚無っています。
# 対処
sshdの設定ファイルがあるところまで移動して(筆者の環境の場合、/data/data/com.termux/files/usr/etc/ssh)
sshd_configに以下を追記
```
ListenAddress [ネットワークアダプタのIP(ip -4 aで確認可能)]
```
これで別端末からssh -p 8022 [termuxユーザ名]@[hostのip]で繋がるはずです。
PuLPで連立一次方程式を解く

# PuLPで連立一次方程式を解く

## 前提

あらかじめpulpをインストールしておく。

## 問題1

    2x + 3y = 8

    4x + 5y = 14

## pulpで解く
  
  ```python
  import pulp

  # 問題の宣言
  problem = pulp.LpProblem('test', pulp.LpMinimize)

  # 変数の宣言
  x = pulp.LpVariable('x', lowBound=0)
  y = pulp.LpVariable('y', lowBound=0)

  # 制約条件の宣言
  problem += 2 * x + 3 * y == 8
  problem += 4 * x + 5 * y == 14

  # 解く
  status = problem.solve()

  # 結果の表示
  print(pulp.LpStatus[status])
  print(x.value(), y.value())
  ```

## 結果1

  ```
  Optimal
  1.0 2.0
  ```
  確かに解けました。

## 問題2

    2x + 3y = 8

    2x + 3y = 14

## pulpで解く
  
  ```python
  import pulp

  # 問題の宣言
  problem = pulp.LpProblem('test', pulp.LpMinimize)

  # 変数の宣言
  x = pulp.LpVariable('x', lowBound=0)
  y = pulp.LpVariable('y', lowBound=0)

  # 制約条件の宣言
  problem += 2 * x + 3 * y == 8
  problem += 2 * x + 3 * y == 14

  # 解く
  status = problem.solve()

  # 結果の表示
  print(pulp.LpStatus[status])
  print(x.value(), y.value())
  ```

## 結果2

  ```
  Infeasible
  7.0 0.0
  ```
  答えがあるように見えますが、statusがInfeasibleになっています。これは、制約条件が矛盾していることを示しています。
Jetson Nano上のコンテナでpytorch使えるまでメモ（2023/08）

# JetPack4.6.1をインストール
SDカードに入れるイメージは以下のリンクにあります。
https://developer.nvidia.com/jetpack-sdk-464

SDカードを焼くのは私は[balenaEtcher](https://etcher.balena.io/)を使っていますが好きなのを使えば良いと思います。

# nvidia-containerとnvidia-jetpackインストール
```
sudo apt dist-upgrade
sudo apt update
sudo apt install nvidia-container
sudo apt install nvidia-jetpack
```

# コンテナ立ち上げる
```
sudo docker pull nvcr.io/nvidia/l4t-ml:r32.5.0-py3
sudo docker run -it --gpus all nvcr.io/nvidia/l4t-ml:r32.5.0-py3
```

# コンテナ
```
python3
>>> import torch
>>> torch.cuda.is_available()
True
```
CheckStyleをGradleに導入＋文字化け対応

# 環境

+ windows10
+ gradle 8.2.1
+ CheckStyle 10.12.2


# CheckStyle Pluginの設定


``` build.gradle
plugins {
    id 'checkstyle'
}

checkstyle {
    // 使用するCheckstyleのバージョン
    toolVersion = '10.12.2'

    // 定義ファイル
    configFile = file("${rootDir}/config/checkstyle/google_checks.xml")

    // Checkstyleによる警告が出た場合もビルドを継続するか？
    ignoreFailures = false

    // ビルドを中断または失敗プロパティを設定する前に許容される警告の最大数
    maxWarnings = 0
}
```

[google_checks.xml](https://github.com/checkstyle/checkstyle/blob/master/src/main/resources/google_checks.xml)はgithubからダウンロードした


## 文字化け対応

CheckStyleを実行して文字化けした場合、gradle.propertiesを作成して書きを設定する


```gradle.properties
# Gradle実行時のjvm引数
# チェック失敗時のエラーメッセージが文字化けするため設定を追加
org.gradle.jvmargs = -Dfile.encoding=UTF-8
```
【Next.js】安全なコンポーネント設計について

こんばんわ。まちゅと申します。

今回は保守性がよくTypeScriptの特徴をシッカリと活用した、
無駄が少ない設計方法を書いていきます。
拙い記事ですが、何卒ご容赦の程よろしくお願いいたします。

### typeとinterfaceの違いと、どちらを使うべきか
本題に入る前に、基本的な所です。
https://www.totaltypescript.com/type-vs-interface-which-should-you-use

- `interface`では共用体、条件付き型を表現できない。`type`は任意の型を表現できる
- `interface`では`extends`が使用できる。`type`ではできない。
- `extends`は`&`よりもわずかにパフォーマンスがいい
- `interface`は予期せぬ拡張で不具合発生可能性がある
- `type`は暗黙的インデックスシグネチャがある

特別な理由が無い限りは、
基本的に`type`を使うが良さそうです。

TypeScriptの公式ドキュメントでの比較です。

https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#differences-between-type-aliases-and-interfaces

### コンポーネント設計
では、typeを活用した最小構成は下記です。
今回は、例としてただ文字を表示させるだけです。

```tsx:component.tsx
import { type FC } from "react";

export type HogeProps = {
  title: string;
};

export const Hoge: FC<HogeProps> = ({ title }) =>
 <>{title}</>;
```

```tsx:parent.tsx
import { Hoge, type HogeProps } from "./component";
import { NextPage } from "next";

type ParentProps = {};

const hogeProps: HogeProps = {
  title: "fuga",
};

const Parent: NextPage<ParentProps> = () => <Hoge {...hogeProps} />;

export default Parent;
```

上記は例ですが、
コンポーネントの最小構成かと思います。

ポイントは、`FC`の型引数を定義する事です。

プロパティの型を`export`してあげることで、
呼び出し側とコンポーネントで
インターフェースの確約ができて
安全です。

パッと見ですが、
FCやNextPageに何も入れてない記事が多すぎます。

`getStaticProps`を使う場合にもプロパティに型が効くので型を設定したいところです。

TypeScriptの利用は、
こういったオブジェクト同士のインターフェースのエラーを無くすのが第一目的なので意識したいですね。


********
追記していきます・・

### まとめ

ざっくりと、自分が書いている方法を書きましたが、
問題点やもっと効率な方法があれば教えていただけると助かります。
【2023年8月版】AWS EC2プランの一覧表（クラウドプラクティショナー試験対策用 備忘用）

# 目的
AWS 認定クラウドプラクティショナー（CLF）合格を目的としたEC2プランの全体像把握のための備忘録
# 課題
EC2のプランがたくさんあり、問題を解くだけではプランごとの特徴、料金体系、メリデメなど頭の中で整理ができていない
# 対策
EC2プランの料金一覧表を作り大枠を理解する
# EC2プラン一覧表 2023年8月20日現在
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/292262/90ce570b-a2df-62b7-96bd-b7086497aef2.png)
# 参考元
https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/concepts-reserved-instances-application.html
https://aws.amazon.com/jp/ec2/pricing/
https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/concepts-reserved-instances-application.html
https://docs.aws.amazon.com/awsaccountbilling/latest/aboutv2/getting-viewing-bill.html
【Mac】ターミナルでリマインダーの一覧を表示する方法


## AppleScriptで取得する

次のコマンドを実行することでリマインダーの一覧を取得できます。

```terminal
osascript -e 'tell application "Reminders" to get name of every reminder' | tr ',' '\n'
```

```terminal
$ osascript -e 'tell application "Reminders" to get name of every reminder' | tr ',' '\n'
タスク1
タスク2
タスク3
タスク4
```

未完了のタスクのみ取得する場合には次のコマンドを実行します。

```terminal
osascript -e 'tell application "Reminders" to get name of every reminder whose completed is false' | tr ',' '\n'
```

```terminal
$ osascript -e 'tell application "Reminders" to get name of every reminder whose completed is false' | tr ',' '\n'
未完了タスク1
未完了タスク2
未完了タスク3
```

## reminders-cliで取得する

reminders-cliをインストールします。

```terminal
brew install keith/formulae/reminders-cli
```

次のコマンドを実行して特定のリストのリマインダー一覧を取得します。

```terminal
$ reminders show リスト名
0 タスク1
1 タスク2
2 タスク3
```
【AWS】VPCエンドポイント

VPCエンドポイントを使用することでインターネットを介さずに、VPC内のサービスからVPC外のAWSサービスに接続することができる。これにより、インターネットを経由せずにEC2インスタンスからS3バケット等、VPC外への通信を実施することができる。<font color="darkorange">**同一リージョン内であれば通信料が発生しない**</font>

<img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3412108/c379feb1-e8d3-8f4b-5ee1-f834ea5d5ad6.png">
Next.js13のnot-found.jsxでchakraUIスタイルの適応ラグ

### 現象
Next.js13のAppRouterで404ページ（not-found.jsx）を作成したところ、スタイル（chakraUI）の適応ラグ（2〜5秒程）が発生していました。
![スクリーンショット 2023-08-19 23.21.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3535425/5d011f8c-2f0f-b683-a46b-61a457fb9fb9.png)
### 解決方法
next.jsのバージョンを13.4.13から13.4.15へ変更することで解消されました。

どのようなバージョンアップ内容が有効だったのかまでは分かっていないのですが、同様の状態で困っている方が居ましたらバージョンの変更を試してみて下さい。
Android Kotlin Timberを別モジュールに導入してみた

# 初めに
Timberとはなんやら、みたいな話は既に様々な方が書いてくれてます。
そのため、割愛します。

# きっかけ
元々、僕が作っている個人アプリは、appモジュールのみで構成されてました。
ただ、今後Jetpack Composeを導入した際のプレビュー表示までの時間やビルド時間、今後仕事でマルチモジュール化の知見が求められるのでは？と考えて、マルチモジュールの学習を始めました。

とりあえず、お試しでログ機能を別モジュールに分けることにしたんですね。

# モジュール構成
とりあえず、いろんなサイト見て考えたモジュール構成。(これが良いのかよくわかってない)
水色の部分は全てサブモジュールになってて、狙いとしてはビルド時間を早めたい！ということくらい。

![名称未設定ファイル.drawio(2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3355973/c8cfc845-3176-e06b-0da9-d73037547024.png)




# 手順1. 別モジュールを用意する
1.メニューバーのFile→New→New ModuleでCreate New Moduleウィンドウを表示する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3355973/ceab3fb6-3bde-ac9d-185b-90659db4c55e.png)

# 手順2. Android Libraryを作成する
__必ず、Android Libraryにしてください。__
TimberはAndroid特化のログライブラリになってて、内部で「android.util.Log」を参照してます。
そのため、Java/Kotlin Libraryで作成すると失敗します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3355973/4b741770-0b54-58a5-846a-846e1edb5807.png)

# 手順3. build.gradleにimplementionを追加する
```groovy
dependencies {
    implementation 'com.jakewharton.timber:timber:5.0.1'
}
```

# 手順4. sync実行
ここは特に問題ないと思います。

# 手順5. Timberのimport
作成したモジュール内にロガー用のktファイルを作ってTimberをimportする
```kotlin
import timber.log.Timber
```
# 手順6. Timberを使う
```kotlin
fun LogInit(){
    Timber.plant();
}
```

# 終わりに
Timberを使うこと自体は簡単なんですが、Android Libraryを使用せずKotlin Libraryを使用して沼る人が一定層いそうなので共有です。
僕はAndroid開発が初心者なのもあってこの問題のせいで数時間沼りました。
Visual Studio よりAzure App Service へ Web アプリケーションを発行した際に発生したエラー ERROR_USER_UNAUTHORIZED

# いつ発生したか

Azure App Serviceのチュートリアルの "ASP.NET アプリをデプロイする" 時に発生

https://learn.microsoft.com/ja-jp/azure/app-service/app-service-web-tutorial-dotnet-sqldatabase#deploy-your-aspnet-app

以下Visual Studioの発行(赤枠)押下時
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/232184/af892f90-fc73-f499-f1e0-d68769ec07cd.png)

# 発生したエラー

```
重大度レベル	コード	説明	プロジェクト	ファイル	行	抑制状態
エラー		Web Deploy タスクに失敗しました。(Web 管理サービスを使用してリモート コンピューター ("<appサービス名>.scm.azurewebsites.net") に接続しましたが、承認されませんでした。正しいユーザー名とパスワードを使用していること、接続先のサイトが存在すること、および資格情報がそのサイトへのアクセス許可のあるユーザーを表していることを確認してください。  詳細情報の参照先: https://go.microsoft.com/fwlink/?LinkId=221672#ERROR_USER_UNAUTHORIZED)	DotNetAppSqlDb		0	
```

# 原因

Azure App Service の [設定 -> 構成 -> General settings]内のBasic Auth Publishing Credential
sが`Off`(defalt)となっている。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/232184/54675827-60ae-e1a1-b3bc-4a88319f74b0.png)

`On`にすれば解消

# 感想

詳細情報の参照先( https://go.microsoft.com/fwlink/?LinkId=221672#ERROR_USER_UNAUTHORIZED )に書いてないんだもん、わかるわけないよ。
HTTP2について

https://www.kagoya.jp/howto/it-glossary/security/http-2/

https://blog.redbox.ne.jp/http2-cdn.html

上記のサイトの自分なりのまとめ（というか必要な部分のコピペ）

## HTTP1.1では
コンテンツは1つ1つしかリクエストを送れない。
そのため、１つ１つレスポンスが返ってくる
1つのリクエストが終わらないと次のリクエストが遅れない。

※Chromeなどは同時に送信するリクエストは6つまでに制限されています。ダウンロードに時間がかかるファイルは標準では6個以上同時ダウンロード出来ません。また、HTTP/1.1の仕様としては2つまでに制限することとされています。

## HTTP2では
複数のリクエストを同時に処理できる。

### 1つのTCPのコネクションで複数の仮想的なコネクションを生成でき、一方がファイルサイズの大きい通信でも、他の仮想的コネクションに影響を与えない。

### HTTP/2のストリームとは何ですか？

https://knowledge.cpi.ad.jp/basic-knowledge/http2-help/#:~:text=%E3%82%B9%E3%83%88%E3%83%AA%E3%83%BC%E3%83%A0%E3%81%A8%E3%81%AF%E3%80%81%E3%82%AF%E3%83%A9%E3%82%A4%E3%82%A2%E3%83%B3%E3%83%88%E3%81%A8,%E3%81%99%E3%82%8B%E3%81%93%E3%81%A8%E3%81%8C%E3%81%A7%E3%81%8D%E3%81%BE%E3%81%99%E3%80%82

HTTP/1.1で圧縮というとGZIP化してCSSやJS、JSON等のテキスト形式ファイルを小さくすることをイメージしがちですが、

HTTP/2ではこれに加えヘッダ部分の圧縮が可能となっています。現在のWEBは様々な内容がヘッダに付与されヘッダ容量自体が膨大になったことに着目しコンテンツボディ以外のヘッダも圧縮する必要がでてきたということになります。

## H2を使うことはどのタイミングで決まるか？

### client helloのタイミングでクライアント側からh2を使用できることを提示している
### Extension: application_layer_protocol_negotiation (len=14)
### server helloでh2を使うことの回答をもらう。Wiresharkでは自分の場合発見できなかった。。。

https://laboradian.com/alpn/

### 「HTTP/2」の制限事項
TLSによる暗号化が行われた環境でしか使えないことがデメリットとして挙げられます。

## HTTP2のヘッダ情報 

https://qiita.com/0xfffffff7/items/39b944e3845ab3776b63
カオスなコントローラーから洗練されたデザインへ

# はじめに

この記事では、Laravel内でコントローラーからビジネスロジックを切り離す方法について書く。具体的には、ビジネスロジックの分離にUsecaseというアプローチを導入する。

[ちびキャラパレット](https://chibipalette.com/)のトップページを例に取り上げ、バックエンドから取得した最新の20件とランダムな20件の投稿をフロントエンドに表示する機能を改善した。

# 対象となる画面

[ちびキャラパレット](https://chibipalette.com/)のトップページ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/122701/63d8383d-18bb-05f6-ad22-f17ab789de61.png)

# 修正前のコード
app/Http/Controllers/TopController.php
```php
class TopController extends Controller
{
    public function index()
    {
        $latestPosts = Post::with('user', 'user.avatars')
            ->latest()
            ->limit(20)
            ->get();

        $latestPostIds = $latestPosts->pluck('id')->toArray();

        $randomPosts = Post::with('user', 'user.avatars')
            ->whereNotIn('id', $latestPostIds) // 最新20件のIDを除外する
            ->inRandomOrder()
            ->limit(20)
            ->get();

        return view('top.index', [
            'latestPosts' => $latestPosts,
            'randomPosts' => $randomPosts,
        ]);
    }
}
```

# 修正方針

**改善すべき点**
- ビジネスロジックをコントローラーから分離する
- DBからデータを取得する処理はModel層に実装し、ビジネスロジックとDBアクセスを分離する

**ディレクトリ構造**
```
app/
├── Http/
│   └── Controllers/
│       └── TopController.php
├── Models/
│   └── Post.php
└── Usecase/
    └── Top/
        └── IndexAction.php
```

# 修正後のコード

app/Http/Controllers/TopController.php
```php
class TopController extends Controller
{
    public function index(IndexAction $action): View
    {
        $posts = $action();

        return view('top.index', [
            'latestPosts' => $posts['latestPosts'],
            'randomPosts' => $posts['randomPosts'],
        ]);
    }
}
```

app/Models/Post.php
```php
public static function getLatestPosts()
{
    return static::with('user', 'user.avatars')
        ->latest()
        ->limit(20)
        ->get();
}

public static function getRandomPostsExcluding($postIds)
{
    return static::with('user', 'user.avatars')
        ->whereNotIn('id', $postIds)
        ->inRandomOrder()
        ->limit(20)
        ->get();
}
```

app/Usecase/Top/IndexAction.php
```php
class IndexAction
{
    public function __invoke()
    {
        $latestPosts = Post::getLatestPosts();
        $latestPostIds = $latestPosts->pluck('id')->toArray();
        $randomPosts = Post::getRandomPostsExcluding($latestPostIds);

        return [
            'latestPosts' => $latestPosts,
            'randomPosts' => $randomPosts,
        ];
    }
}
```
ホレリス定数について

## ホレリス定数とは

FORTRAN66 には現代的な意味での文字型はなく、かなり厳しい制約がありました。

* 整数型変数に文字コードを埋め込んで使う（文字数の上限は処理系依存）
* 「文字型」定数が書ける場所は DATA 文と CALL 文だけ

FORTRAN 66 の文字型定数は「字数 `H` 値」という異様な形式です。たとえば `1HX` が FORTRAN77 の `'X'` に相当し、`4HABCD` が `'ABCD'` に相当するといった具合です。FORMAT 文をよく知っている人は「H型編集記述子」と同じ形だと気づかれるでしょう。読み書きしやすいものではないので、後の FORTRAN77 以後では引用符によるほうが文字列定数と呼ばれ、「字数 `H` 値」の形式をホレリス定数と呼びます。

DATA 文の例です。

```FORTRAN
C     型宣言がなくても KSYM は整数型になる
      INTEGER KSYM
      DATA KSYM/4HABCD/
```

これで `KSYM` という変数ができて（整数型ですから変数名は普通 IJKLMN で始まる名前が使われます）、`'ABCD'` の文字コードにあたる値が入るわけです。整数型変数ですから整数として使えるわけですが、リトルエンディアン機では 0x44434241 が入ることになります。

CALL 文の例です。

```FORTRAN
      CALL SUB(4HABCD, 他の引数...)
```

この例では、あたかも上のDATA文があったかのように整数型の値が作られ、それが引数として渡されます。

## 単純に文字型定数で置き換えてはいけない

ホレリス定数の代わりに（FORTRAN77以後の引用符による）文字列定数が作られたのですから、文字列定数で置き換えてしまいたいですよね。`CALL SUB(4HABCD, 他の引数...)` とする代わりに `CALL SUB('ABCD', 他の引数...)` としたくなります。

ところがこれが駄目なんです。

FORTRANの大抵の実装では、サブルーチンに入るときに数値型であればその変数のポインタをスタックに積みますが、文字型であれば文字データへのポインタを積むだけでなく、すべての引数をスタックに積んだあとで文字列長をスタックに積みます。最近は先頭数個の引数をスタックじゃなくてレジスタに置いたりしますが原則は変わりません。

ホレリス定数は整数型ですから、文字型の引数を渡してしまうと、文字列長が余計にスタックに積まれてしまうわけですね。特にホレリス定数の引数のあとに文字型の引数がある場合（長期間保守され続けてきたコードにはままある）はほぼ確実に予期しない動作となります。

（上記の説明から「動いてしまう場合」がわかる人は自己責任で。悪いことはいわんからやめておけ）

## 整数型変数に文字コードを入れる対処

ホレリス定数を受けるサブルーチンを変更しない場合は、整数型変数に文字コード値を入れることになります。

最近知ったのですが古いコンパイラ（少なくともIntel Fortran）ではそのものズバリ代入できてしまうようです。でも gfortran では通りません。

```FORTRAN
C ＜非推奨＞古いコンパイラでは使えるが gfortran では通らない
      KSYM = 'ABCD'
```

Fortran90 以後のお作法では、TRANSFER 関数を使います。

```FORTRAN
C ＜推奨＞Fortran90 対応ならばすべてのコンパイラで使える
C     文字列 'ABCD' のビット列を KSYM に転写
      KSYM = TRANSFER('ABCD', KSYM)
```

文字コードというと、FORTRAN77 の教科書で ICHAR 関数が使えないのかと考える人がいるかもしれません。実は文字列長が1の場合に限り、X86などリトルエンディアン機では、次のコードが動いてしまいます。数値的下位ビットが、記憶列での下位アドレス（記憶列の先頭）に来るからですね。でもビッグエンディアン機では動かないので、これもまた自己責任でお願いします。

```FORTRAN
C ＜非推奨＞リトルエンディアン機で1文字に限り動作
      KSYM = ICHAR('A')
```

## 追記：逆にホレリス定数を受けるサブルーチンは

蛇足ですが、上と同じ理由で、ホレリス定数を受けるサブルーチンを脱ホレリスしようとするとき、呼び出し側を変えないで（多数あって変えられない場合など）無闇に文字型に書き換えてはいけません。

```FORTRAN
      SUBROUTINE SUB(KSYM, 他の引数...)
C     型宣言がなくても KSYM は整数型になる
      INTEGER KSYM
```

とあったら

```FORTRAN
C 呼び出し規約上非互換になります
      SUBROUTINE SUB(KSYM, 他の引数...)
      CHARACTER(4):: KSYM
```

にすると余計にスタックを掘りにいって最悪SEGVします。
このようにして文字型に変換することで、互換の動作になります。

```FORTRAN
      SUBROUTINE SUB(KSYM, 他の引数...)
C     型宣言がなくても KSYM は整数型になる
      INTEGER:: KSYM
      CHARACTER(4):: CSYM
C     KSYM のビット列を CSYM に転写
      CSYM = TRANSFER(KSYM, CSYM)
```
iOS Simulatorでタップポインターを表示させる

## タップポインターを表示させてみた

![sample01.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/779616/74129463-6604-75f0-b20b-0a78ab83720a.gif)

百聞は一見に如かず。先ずはこちらをご覧ください。
Simulatorでタップと同時にポインターが表示されます。

## 設定は何をすればいいの？

以下のコマンドをterminalで実行させてSimulatorを起動させる。

``` txt:terminaで実行するコマンド
defaults write com.apple.iphonesimulator ShowSingleTouches 1
```


**（※この時、Simulatorが立ち上がった状態のまま再度ビルドしてSimulatorを実行してもキャッシュが残っているので、ポインターは表示されない。Simlatorを完全に終了してから再起動する）**


## なぜ、表示・非表示になるのか？

 `com.apple.iphonesimulator`はSimulatorの設定を管理しているplistのことです。

こちらのplistに`ShowSingleTouches` というKeyが存在しており、ValueにString（この場合は1)を入れることでSimlator上でタップポインターが発生します。

ポインター非表示にする。または戻す場合はShowSingleTouchesの値を0にします。
以下のコマンドをterminalで叩きます。

``` txt:ポインター非表示にする
defaults write com.apple.iphonesimulator ShowSingleTouches 0
```


## コマンドについて

`defaults write com.apple.iphonesimulator ShowSingleTouches 1`
上記のコマンドは何をしているのか分解して説明。<br>

**`defaults write`**
アプリケーション設定の一覧表示と操作のコマンドでwriteで書き込みを意味しています。<br>

**`com.apple.iphonesimulator ShowSingleTouches 1`**

こちらに関しては「com.apple.iphonesimulator.plist」の「ShowSingleTouches」というキーに「１」という値を書き込むということになります。


## 補足
`defaults`のサブコマンドで`read`を使用すれば`ShowSingleTouches`のValueを確認することができます。
``` text:terminalで叩いてみよう
defaults read com.apple.iphonesimulator
```

または、Finderからでもポインター表示/非表示の設定が行えます。<br>基本は以下のパスを辿ればplistが存在しているはずです。Finderから移動して値を書き込むのでもOKです

``` swift: 
　~/Library/Preferences/com.apple.iphonesimulator.plist
```

**↓↓このキーの値を設定すればポインター表示/非表示↓↓**
![image01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/779616/075ecb1a-68fa-a7b9-65e9-b18d96418b96.png)
正規化

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3457284/715397e8-a59e-a04e-f808-c9d55d1094fa.png)
　正規化とは、表を分割してデータの重複を排除することです。データの重複を排除することによって、不整合の発生を未然に防ぐことができます。

　データベースを運用するにあたって、新旧のデータが混在してデータに不整合が発生すると大変です。新旧データが混在するのは、同じ情報が複数の箇所に記録してあるからです。正規化では、表の構成を見直し、同じ情報を重複して記録しないようにします。
[資格勉強]ワイヤレスIoTプランナー

電話サーバアプリエンジニアから、リスキリングでワイヤレスIoTエンジニア目指してみます。

# 参考文献
## [1] [ワイヤレスIoTプランナー公式HP](https://www.mcpc-jp.org/wip-kentei/kentei_cbt_kiso/index.html)
## [2] ワイヤレスIoTプランナーテキスト[基礎編] 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2311625/09386dbd-1162-9370-eaef-dc2c298c305e.png)

# 試験受験のきっかけ
資格の話を飲み会で教えてもらって、テキスト[2]のイラストが素敵だったので、取ろうと思いました。

イラストの作者はこちらの TAOさんです。
https://twitter.com/tao15102/status/1417002833019236355

せっかく[ローカル5Gの資格](https://eng-blog.iij.ad.jp/archives/19412)を取ったので、それを活かしたIoTシステムが作れないかなともいました。
# 資格の取得方法
 CBT(テストセンタでパソコンで受ける) or 6時間の講習を受ける　のどちらかです。

# 内容
- DX
- IoT
- 電波
- ネットワーク
- セキュリティ
- データ活用とAI
- 関連法規
となかなか広範囲にわたります。ザクっと英にするとこんな感じ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2311625/27336dcc-7426-673f-b3f2-784dccb04196.png)



ということで、今回は概要の紹介。次回から各項目の勉強をしていきたいと思います。
さて、あと2週間程度で受かるだろうか。
[リサーチ・メモ] マイクロサービスについて

## 背景
プロジェクト概要の説明を受けた際に、触れられたが改めて話に上がり内容を忘れていた為、纏める。

## モノリスとの違い
||マイクロサービス|モノリス|
|:--:|:--:|:--:|
|概要|アプリケーションが小さな独立したサービス群に分割されている|多くのサービス機能が1つに統合されている|
|開発言語|サービスごとに異なる言語を使用可能|アプリケーション全体で統一|
|障害の影響|各サービス独立の為、一部サービスの障害が他サービスに影響を及ぼすことは少ない|一部のサービスが障害を受けると、全体に影響する可能性がある|
|DB|各サービスは独自のデータベースを持つ|単一のデータベースを使用|

## まとめ
上記の違いを見ると、マイクロサービスの方が利点が多いように見えるが、モノリスではDBの単一によりデータの整合性を維持することが簡単であったり、サービスが1つに統合されている為シンプルであること等、それぞれ利点がある。
また、マイクロサービスを考えるにあたり、コンポーネントの分割単位というのは重要であり生命線となる。
システム的/技術的な観点、業務的な観点、非機能的な観点、運用していく組織の観点などなど、
を考慮しながらシステムの未来まで想像しながら分割しないといけない。
【Python】辞書ネスト取得方法

# 【Python】辞書型のネストから値を取得する方法

辞書の値に、辞書を用いることが出来ます。

```py:例
{1:{'x':0}}
```

キーが「'x'」、値が「0」（内側の辞書）
キーが「1」、値が「{'x':0}」（外側の辞書）

### 辞書のネストから値を取得する2種類の方法

```py
a = {1:{'x':100}}

# 変数に代入させて、アクセスする方法
a1 = a[1]
print(a1['x'])

# 変数使わずに一発で、アクセスする方法
print(a[1]['x'])
```

```py:実行結果
100
100
```
---
書いてみた。

```py
order = {
    'cake':{
        1:'chocolate', 
        2:'strawberry', 
        3:'fruits'}, 
    'drink':{
        1:'milk', 
        2:'coffee', 
        3:'tea'
     }
}

print(order['cake'][2])
print(order['drink'][3])
````

```py:実行結果
strawberry
tea
```

こんな感じです。
orderにデータを代入する。
print(データを代入してあるorderを書く。)
そしてorderの中の、`キー`を[]に書いていきます。
その指示に従って、print関数を使って値を抽出していきます。
【個人用】Scala チートシート

# はじめに
本記事はScala再履修用に作成した

# install (M1 mac)
```
$ brew install scala
```

# Hello world
``` test.scala
object test {
	def main (args: Array[String]) = {
		printf("hello\n")
	}
}
```

### 実行方法
```
$ scalac test.scala
$ scala test
```
`test.scala` ファイル名
`test` クラス名

# 自作関数（１行のやつ）
``` test.scala
object test {
	def main (args: Array[String]) = {
		println(testFunc(12, 30))
	}

	def testFunc (a: Int, b: Int): Int = a + b
}
```

## オバロ
```test.scala
object test {
	def main (args: Array[String]) = {
		println(testFunc(12, 30))
		println(testFunc(12, 14, 16))
	}

	def testFunc (a: Int, b: Int): Int = a + b
	def testFunc (a: Int, b: Int, c: Int): Int = a + b + c
}
```
関数のオーバーロードは許される


# if else
``` test.scala
object test {
	def main (args: Array[String]) = {
		println(testFunc(12, 30, true))
	}

	def testFunc (a: Int, b: Int, flag: Boolean): Int = {
		if (flag) {
			a
		} else if (!flag) {
			b
		} else {
			42
		}
	}

}
```

# for 文 + 配列
``` test.scala
object test {
	def main (args: Array[String]) = {
		var arrval: Array[Int] = Array(1, 2, 3, 4, 5)

		println(testFunc(arrval))
	}

	def testFunc (inp: Array[Int]): Int = {
		var res = 0

		for (i <- 0 until inp.size) {
			res += inp(i)
		}

		res
	}
}
```
配列アクセスは ( ) を用いる
配列要素数は [配列インスタンス].sizeで求める

### Tips
`var a: Array[Int] = Array.empty`：空配列の初期化

## foreach 文
```test.scala
object test {
	def main (args: Array[String]) = {
		var arrval: Array[Int] = Array(1, 2, 3, 4, 5)

		println(testFunc(arrval))
	}

	def testFunc (inp: Array[Int]): Int = {
		var res = 0
		(0 until inp.size).foreach (i =>
			res += inp(i))

		res
	}
}
```
こっちの方がおしゃん

## foreach 文
```test.scala
object test {
	def main (args: Array[String]) = {
		var arrval: Array[Int] = Array(1, 2, 3, 4, 5)

		println(testFunc(arrval))
	}

	def testFunc (inp: Array[Int]): Int = {
		var res = 0
		inp.foreach (i => res += i)

		res
	}
}
```
そもそも添字がいらないならこっちのほうがお洒落

# static class = object

```test.scala
object test {
	def main (args: Array[String]) = {
		var arrval: Array[Int] = Array(1, 2, 3, 4, 5)

		println(s_funcs.testFunc(arrval))
	}
}

object s_funcs {
	def testFunc (inp: Array[Int]): Int = {
		var res = 0
		inp.foreach (i => res += i)

		res
	}
}
```
シングルトンとか言うらしい
c# でいう static class でインスタンス生成が不要

# dynamic class = class
```test.scala
object test {
	def main (args: Array[String]) = {
		var arrval: Array[Int] = Array(1, 2, 3, 4, 5)

		var df = new d_funcs
		println(df.testFunc(arrval))
	}
}

class d_funcs {
	def testFunc (inp: Array[Int]): Int = {
		var res = 0
		inp.foreach (i => res += i)

		res
	}
}
```
c# でいう class でインスタンス生成が必要

## コンストラクタ
``` test.scala
object test {
	def main (args: Array[String]) = {
		var arrval: Array[Int] = Array(1, 2, 3, 4, 5)

		var df = new d_funcs(arrval)
		println(df.testFunc())
	}
}

class d_funcs (inp: Array[Int]) {
	var in_arr: Array[Int] = inp

	def testFunc (): Int = {
		var res = 0
		in_arr.foreach (i => res += i)

		res
	}
}
```
SwiftUIのPickerでAPIで取得したデータを扱う

APIでとってきたデータを次の様な感じでPickerで選択したいことがあるかと思います。

```swift
@State private var responses:[Response] = []    //APIでfetchしたデータ
@State private var selectedResponse:Response?   //Pickerで選択したデータ
//簡便のため`@State`にしてますが適宜変更してください
```

この時、`tag`で`as`でOptionalで型キャストしないとエラーになります。`[Response]`の要素と`Response?`とが違うからですね。


```swift
Picker("", selection: $selectedResponse) {
    ForEach(responses) { response in
        Text(response.name).tag(response as Response?)
    }
}
```

エラー:
```swift
Picker: the selection "Optional(***.Response(***))" is invalid and does not have an associated tag, this will give undefined results.
```
【生産化向上/業務効率化】Bing AIと仲良くなろう

# 概要

Microsoftが提供しているAIチャットツールであるBing AIを利用して業務効率化した実体験を記載します。

## Bing AIとは

Microsoftが提供しているAIチャットツールであり、検索エンジンのBingとAIツール(GPT-4)が統合されたチャットツールです。<br>
ChatGPTと比較すると以下の面で優れていると思います。
- GPT-4が無料で利用できる(ただし1セッションあたり30回しか質問できない制限があります)
- ChatGPT(有料版のプラグイン導入など除く)では2021年9月までのデータしか検索できませんが、Bing AIは2021年9月以降の最新情報を回答できます。
- - 例：ツイッターの名称変更に関する質問
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307261/b3fdcd13-24f9-89ab-7ec7-40cd681cb536.png)

参考サイト：https://www.gizmodo.jp/2023/03/how-to-use-bing-ai-vs-chatgpt.html

## (注意)Bing AIを利用する際の注意

- Bing AIはインターネット上の最新情報を元に回答を作成しますが、そのAIの回答が必ずしも正しいとは限らない可能性があります。回答内容をそのまま鵜吞みにせずに利用しましょう。
- - 例：Bing AIの仕様に関する質問
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307261/ec6591fa-d165-8bd5-c94e-3259c24d7db3.png)

- Bing AIの質問文には個人情報を入れないようにしましょう。
- - AIチャットツールを通して個人情報が漏洩する危険性があります。
- - - 参考サイト：https://www.mbsd.jp/research/20230511/chatgpt-security/

## 何を効率化した？

- 文章作成
- エラー文の情報収集
- 最新技術の取得 (特にChatGPTとは異なり、2021年9月以降の情報取得に強いです)

### 文章作成

Bing AIで進捗報告等のひな形を作成し、人の手で微修正してから文章を投稿しています。<br>特に急ぎの連絡の際にどのような文章を作れば良いかわからない場合は、Bing AIの回答内容を参考にしながら文章を投稿しています。
- 例：急ぎで進捗報告したい場合
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307261/fbab8037-3210-739e-6189-6de14af73cc0.png)

### エラー文の情報収集

どのように対象のエラーが発生したか、またどこを見れば対象のエラーが解消できそうか、エラー解決に向けての情報源として利用しています。<br>これによってエラーの原因調査のためにインターネットサーフィンをする時間が減りました。

- 例：EC2(Ubunt)でSSHでログインできない
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307261/f8701e1a-58cc-6a0a-e1e9-166f3edbae23.png)

### 最新技術の取得

- 最新技術を調査する場合にどのような機能などがあるか、概要を教えてくれます。

- 例：Windows11移行関連
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307261/c5c4548c-06e6-1267-6007-675339f965cc.png)

<br>

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307261/b8b60fb5-5ef6-d84c-87d3-7d135668e0cb.png)


- 例：AWSの最新ニュースの取得
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307261/02608f3b-a16f-787e-901f-0c3e0bdd9c47.png)


# 最後に
上記のようにBing AIを使いこなして、インターネットサーフィンの時間を削減して業務効率化しましょう。
Proxmox VEのデータを救出 (LVM2)

Proxmoxを入れてた鯖が壊れて、データ救出したかった時に助けられたやつです。
探しても日本語の記事でこういうのがなかったので...

:::note
あらかじめ`lvscan`で論理ボリュームの名前を探しておいてください。
```
$ sudo lvscan
  inactive          '/dev/pve/hello-everyone-000' [4.00 GiB] inherit
```
:::

***

下準備
```bash
#/dev/loop の空き探し (記事内では/dev/loop10にします)
losetup -f
```

***

マウント
```bash
losetup -P /dev/loop10 /dev/pve01/vm-001-disk-0
mkdir /mnt/data01 #/mnt/内に保存するためのディレクトリを作る
mount /dev/loop10p2 /mnt/data01
```

***

これでデータを`/mnt/data01`から開けるようになってるはずです。
やったね！
firewalldと連携したfail2banの設定

SSHへのログインアクセスのアタックが止まらんのでfail2banを導入。
firewalldとの連携でみすったので備忘録
# インストール
```bash
$ sudo dnf install epel-release
$ sido dnf install firewalld
$ sudo dnf install fail2ban fail2ban-firewalld

```
# 設定
fail2banの方針として.confファイルはアップデートなどで上書きされることがあるのでデフォルトでから変えたい設定が有る場合はファイル拡張子.localとしてコピーもしくは新規作成してそちらのファイルで値を変更・追加する。
```bash
$ cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
$ vi /etc/fail2ban/jail.local
```

# 詰まったところ
## firewalldでのポート指定
firewalldのrich-ruleでのポート指定方法は特定の番号か`x-y`の範囲指定なので
fail2banでのデフォルトの範囲指定`1:65535`ではだめっぽい
```conf:/etc/fail2ban/jail.local
[sshd]
enabled = true
port = 1-65535

[recidive]
enabled = true
port = 1-65535
```

## banする際のrejectからdropに変える方法
dnfでインストールした`fail2ban-firewalld`で以下のファイルが読み込まれる

```conf:/etc/fail2ban/jail.d/00-firewalld.conf
[DEFAULT]
banaction = firewallcmd-rich-rules
banaction_allports = firewallcmd-rich-rules
```
banactionは`/etc/fail2ban/action.d/firewallcmd-common.conf`なのでこちらの設定をlocalファイルを用意して値を変える

```conf:/etc/fail2ban/action.d/firewallcmd-common.local
[Init]
blocktype = DROP
rich-blocktype = drop
```
rich ruleを使っているのでblocktypeに加えて`rich-blocktype`も設定する
API Management / Websocket - Azure - Memo

# 概要
(自分用メモ)
AzureのAPI Managementを試す
APIM(API Management)はAzureの各種リソースのエンドポイント(HTTPサービス)のセキュリティをまとめて引き受ける。認証の検証等。

[総目次](https://qiita.com/shokkaa/private/7a1633dbf58ddc81f894)
 [目次: Azure Development](https://qiita.com/search?sort=&q=tag%3AAzure+tag%3Adevelopment+user%3Ashokkaa)

## 前提
- [Azure Function App準備](https://qiita.com/shokkaa/items/ee33b6d7736e81119be7)
- [WebPubSub準備](https://qiita.com/shokkaa/items/11221e817744673c595c)

# Azure portalサイトでお試し

## API Managementリソース作成
`Home`➔`リソースの作成`➔`API Management`➔`作成`
- `リソース名`=エンドポイント(URL)のホスト名に含まれる。Azure全体でユニーク。例:`apim230707`
- `価格レベル`=`Consumption`(従量制)を指定。

➔`作成`
デプロイには多少の時間が必要。

## FunctionAppでデプロイしたWebAPIを収容
`概要`➔`作業の開始`➔`API`➔`HTTP`
- `Web service URL`=Functionsで生成したURL
- `API URL suffix`はとりあえず空にする
➔`Create`

➔`Frontend`➔`Add Operation` ➔ 例:`URL`=`Get`,`/`
- `Subscription required`=unchecked (本来ダメ、お試しのみ)

## テスト
`Settings`➔`Base URL`で取得したURL(例:`https://apim230101.azure-api.net`)

[以前作ったFunction](https://qiita.com/shokkaa/items/ee33b6d7736e81119be7)なら、パラメタ`?name=Shokkaa`をつけて
`https://apim230101.azure-api.net?name=Shokkaa`
をブラウザでアクセス。
`Hello, Shokkaa. This HTTP triggered function executed successfully.`
みたいに帰ってくれば成功。
APIMは何もしない設定なので、直接Functionsを呼び出すのと(URLを除いて)まったく同じ。

## [TODO]認証
[TODO]

# CLI操作
下記リソース作成と、設定をCLIで実行。 
リソースグループ、Function App、APIインスタンス、APIM上のAPIセット、APIに対する操作
```sh:リソース名など設定とAzureへのログイン
RESGR=r1003
LOCATION=westus
FUNCAPP=app1003
APIM=${RESGR}apim

EMAIL=myid@my.domain

az login --use-device-code
```

```sh:Azure上にリソースグループ・Function・APIMリソース作成
az group create -n $RESGR --location $LOCATION
az storage account create -n s$FUNCAPP -g $RESGR -l $LOCATION 
az functionapp create -n $FUNCAPP -c $LOCATION -g $RESGR --storage-account s$FUNCAPP --os-type Linux --functions-version 4 --runtime node --runtime-version 18
az apim create --name $APIM -g $RESGR --publisher-name 'Shokkaa' --sku-name 'Developer' --publisher-email $EMAIL
# WebSocketはDeveloper(開発者)以上のプランが必要

FUNCURL=https://$FUNCAPP.azurewebsites.net/api/frev?param=
az apim api create --service-name $APIM -g $RESGR --api-id RevProxy --path '/' --display-name 'RevProxy'  --service-url $FUNCURL
az apim api operation create --service-name $APIM -g $RESGR --api-id RevProxy --url-template '/{param1}' --method 'GET' --display-name 'Get' --description 'Descrition' --template-parameters name=param1 description='Descrition' type=string
```

```sh:Azure上にWebPubSubリソース作成・PubSubにHub作成
PUBSUB=${RESGR}ps
HUB=hub1
az webpubsub create --name $PUBSUB -g $RESGR --sku 'Free_F1' --location $LOCATION
az webpubsub hub create --name $PUBSUB --hub-name $HUB -g $RESGR

# 確認
az webpubsub hub list --name $PUBSUB -g $RESGR
```
```sh:WebPubSub接続文字列取得
az webpubsub key show --name $PUBSUB -g $RESGR --query primaryConnectionString
# "Endpoint=https://r1003ps.webpubsub.azure.com;AccessKey=xxxxxxxx;Version=1.0;"
```



# 後片付け
リソース削除お忘れなく。
```
az group delete --name $RESGR
```
道具としての電子計算機

電子計算機の教育は、あやしげなこと、おかしなことが罷り通っていることがある。　

原理を教えずに、流行りを教えたりする。

過去の教育の失敗を、いかに回復してきたかを整理する。　　




# 道具としてのコンピュータ(D)版管理、構成管理
https://researchmap.jp/blogs/blog_entries/view/78772/ed04590db361985bcdc6a9535d816397

ファイルを共有するだけでは，同一目的のファイルに複数の版が存在したり，
どの版が最新のものであるかなどが分からなくなることがある。

また，どの版の構成対象はどこまでかとか，どのファイルを含んでどのファイルを含まないかなど，構成管理もたいへんになる。

ソフトウェアの更新において，無矛盾な組み合わせの管理を行うのにも使うことができる。


cvs
subverion(SVN)
Redmine
などのフリーソフトと
有償のソフトウェアなどがある。
# 道具としてのコンピュータ(E)図形、映像，音入力編集ソフト
https://researchmap.jp/blogs/blog_entries/view/78772/2744955aec877c21cad1779be4ae18e9

絵、図を書いたり、編集したりするソフトウェア。
写真、映像を入力編集するソフトウェア
音声、音楽を作成、編集するソフトウェア。

# 道具としてのコンピュータ(F)計算機言語
https://researchmap.jp/blogs/blog_entries/view/78772/4f8b4fb7c4f8a5120d3b767a45768573

ここでは計算機言語には，計算機を作るための言語、計算機上で動作する言語を含むことにする。
前者はHDL(hardware description language)で
VHDL
Verilog-HDL
System-Verilog
Syatem-C
などがある。
後者は，
機械語
アセンブラ
C
JAVA
などがある。
MacでDB/SQLを触ってみよう-導入-

## はじめに
postgresqlを利用してDB構築をしていこうって記事です．
私がいつも使うコマンド等を書き込もうかなあと💭
まずはインストールとか済ませてしまいましょう！！
大して私も使えるわけではないけれどpostgresqlがわかればmysqlとかも意外に使えるようになるので頑張ってみよう

## インストール
今回はHomebrewで導入します
まだインストールしていない人はHomebrewのインストールをしてみてください⭐️
```ruby:Homebrew.install
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
```
このサイトへアクセスしてインストールコマンドをターミナルで実行
https://brew.sh/index_ja

`Updated 3 taps (homebrew/services, homebrew/core and homebrew/cask).
==> Installation successful!`

:::note
成功したよ⭐️って文が出てきたらOK！！
:::

## postgresqlを確認
```ruby:confirmation
brew search postgresql
```
これは今どれくらいのバージョンが配信されてるか見れるコマンド，
すでにインストールされていれば自分のバージョンを確認することもできる⭕️

## postgresqlのインストール
最新版をインストールする場合はpostgresqlのみを指定します
```ruby:install1
brew install postgresql
```
バージョンを指定したい場合はpostgresql@バージョンを入れます
下記は例えばで12を指定してみました．
```ruby:install2
brew install postgresql@12
```
## postgresqlのバージョンをチェック
```ruby:version
psql --version
```
`psql (PostgreSQL) 14.8 (Homebrew)`
みたいな表示が出れば⭕️

## postgresqlを起動
```ruby:
brew services start postgresql
```
このbrewコマンドで大抵は起動できると思うのですが，
postgresqlの最新バージョンは15に対して私みたいに14のままアップデートを怠っていることに連動してFormula名が変更されてた！！なんて事態に陥っていると
![エラー](https://github.com/kinmedwuai/Portfolio/assets/117531104/f264e890-1bd6-479d-a313-1c1548f2c37e)
上記のような警告文が出てきちゃう💦
だから，
```ruby:
brew services restart postgresql@14
```
という感じで**Formula名**を変更してあげれば動かすことができる！！

:::note 
Formula名が変更される理由には，新しいバージョンのソフトウェアがリリースされた場合や，特定のバージョンに対応するための変更があった場合などに発生する．
:::

**それかいっそのことアンインストールして再インストールをしちゃいましょう⭐️**
```ruby:
brew uninstall postgresql@14
brew install postgresql
```

## postgresqlの状態確認
```ruby:
brew services list
```
で出力することでpostgresqlが起動しているかチェックできる⭕️
***
たまーになんですけど（私だけかもしれない）
`postgresql@14 error  512`
という感じのエラーが出ちゃうんですけどこれの大抵は「起動できてるけど権限がちょっときもいからどうにかしてくんない？」ってエラーだと勝手に解釈している（実際に普通に使えている）のであまり気に病む必要はないと思ってます
まあ`psql`して動かせれば大抵問題ないよねっ⭐️という気持ちで生きてます
***
## 終わりに
今回はまじまじの導入部分で終わっておきます．次から実際に使ってみましょう．
コマンドとかもまとめながら私用の教科書を作りたいなあ
次に続きます🐈

## 参考
https://amateur-engineer.com/postgresql-mac-install/#toc7
imgタグの変化 - 気づいたら時代遅れなHTMLを書いてた

( 2022年、仕事中であった事例からいろいろまとめた資料を Qiita に記載 )

---

私は自社開発の会社に勤めるサーバーサイドエンジニア。
去年(2022年)、フロントエンドエンジニアが書いてたHTMLを見てたら以下のようなコードを見つけた。

```html
<img src=”./hoge.jpg” width=”640” height=”480” alt=”hoge”>
```

「あれ？ width/heightって指定して良いんだっけ？🤔」と思った私は、imgタグの指定方法についていろいろ調べたのだが、私が知っている時とは結構変わっていて衝撃を受けた😓

## HTML3.2(1997年頃)

HTML3.2の頃、HTMLで見た目を変更するためにHTMLのタグや属性がいろいろ追加された。

```html
<font color=”red”>Hello</font>
<center>World</center>
<img src=”./hoge.jpg” width=”640” height=”480”>
```

## HTML5/CSS3の登場(2008年頃)

その後、HTMLとCSSの役割が整理された。

```html
<span style=”color: red”>Hello</span>
<span class=”text-center”>World</span>
```

HTML4.01で見た目に関連する要素がいろいろ非推奨となり、HTML5でそれらの要素が廃止された。
ちなみにいくつかのタグの width や height の属性はこの時に廃止になったようだ。
(imgはそれに含まれてないようだった)

### レスポンシブコード

CSSで制御すると画面サイズやメディアの種類などで、見た目を変更することができる。
HTML5/CSS3の登場以降、PCだけでなくスマホやタブレットが普及したことで、画面サイズでUIを変更する重要度は増した。
例えば以下のようなコードを書くことで、画像のアス比を維持したまま画面や上位のタグサイズの想定指定で画像のサイズを制御できる。

```html
<img src=”./hoge.jpg” alt=”hoge”>
```

```css
img {
  width: 90%; height: auto;
}
```

## 2019年後半頃〜

その後、2019年後半から以下のような記載が推奨となった。

```html
<img src=”./hoge.jpg” width=”640” height=”480” alt=”hoge”>
```

```css
img {
  width: 90%; height: auto;
}
```

……HTMLが以前に戻ったようだ😓
CSSは別の場所に書くことが多いので余計に戻った感じがすごかった。

### レイアウトシフト

前述の記載になった理由はレイアウトシフトの対策のためである。
レイアウトシフトとは、ページを開いて最初に表示されてから完全にロードが終わるまでに UI が勝手にずれることである。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/263164/095c67a8-f794-9541-8950-676410401224.png)

発生すると誤タップによる意図しない遷移などが発生するため、UX低下の要因となる。
ちなみに Google では、この UX の指標を CLS(Cumulative Layout Shift) として定めている。

レイアウトシフトをさせない最も単純な方法は全てのロードが完了した後に画面を描画することだが、画面のロード時間が長いのは UX 低下の原因となる。
モダンな画面は、webpack や Vite などで JS をまとめたり、 HTTP2 でアクセスの並列度は上がっているが、それでも画像や動画などのメディアが多かったり、色々な機能を持っていたりするので、基本的に重い。
なので、画面描画をいかに早くするかも UX を向上するために重要な考慮点である。

#### 原因と対策

レイアウトシフトの要因は以下のようなことが挙げられる。

 - HTML/CSS は上から順に処理され、優先度が同じ場合は後に 処理された方が優先される。
 - 画像の場合、画像のダウンロードが完了しないと画像サイズや アスペクト比が分からなかった。

モダンブラウザは img に width/height 属性がある場合、画像 ダウンロードを待たずにアス比を計算してくれるので、以下のように記載することで画像ロードを待たずに領域の確保ができるようになった。

```html
<img src=”./hoge.jpg” width=”640” height=”480” alt=”hoge”>
```

```css
img {
  width: 90%; height: auto;
}
```

## 2021年頃〜

これで完璧!😏
……と言いたいところだが、2021年頃、全てのモダンブラウザが、 CSS(aspect-ratio) でアス比が指定可能になったようで、以下のような方法でも同じようなことができるようになった。

```html
<img src=”./hoge.jpg” alt=”hoge”>
```

```css
img {
  aspect-ratio: 3 / 2;
  width: 90%;
  height: auto;
}
```

基本的に CSS の方がレスポンシブな記載ではあるが、 HTML の属性指定の方が良い場合もあるようだ。
複数画像指定(srcset)や JS による動的制御の場合とかだろうか？🤔