OmniAICreator/Qiita-1.07M huggingface dataset:
Next.js13.4(app directory)でfaviconを設定する方法

### 背景
`favicon.ico`を `public/`に配置してもfaviconが表示されない。


### 解決策

* 用意した`favicon.ico`を `icon.ico` にリネームして `app/`に配置
* 元々あるfavicon.icoは削除する


公式の情報（↓）だと、この結論に辿り着かない。
https://nextjs.org/docs/app/api-reference/file-conventions/metadata/app-icons


### 参考
https://stackoverflow.com/questions/75674866/adding-favicon-to-nextjs-13-beta-no-pages-folder
[Tip] 小さい画面を有効に使うための Design Studio カスタマイズ

## はじめに

普段は大きいサイズのディスプレイで Design Studio（以降、DS）を使っていますが、場合によっては13インチのノートパソコンで作業することもあります。

製品の特性上、自動化対象アプリの画面をツール内に表示して動作記録させるため、画面の小ささは結構ストレスになります。

ということで、小さな画面を有効に使うための DS カスタマイズ方法を紹介します。

:::note
本記事は、BizRobo! v11.3.0.2 の利用を前提に解説します。
:::

## その1：不要なビューを閉じる

以下が DS 表示の初期状態です。

画面構成は大きく３列の構成になっておりますが、メインで操作するのは真ん中のビューなので、両側のビューについてはタブ化して画面を広くしましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3090491/f720e653-1687-757b-bc16-9da6fb86830d.png)
下記の様に、各ビューの右上に表示されている「`＿`」マークをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3090491/ac8c7a7d-6407-56e9-56eb-ffdc28e2a48a.png)
YES。これで横幅すっきりしました。

## その2：ビューの表示サイズを縮小

ワークフロービューはステップが閉じた状態であれば気になりませんが、編集時にステップを広げると縦横に幅を取るため、デフォルトの表示縮尺では手狭に感じます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3090491/31e465c4-3320-4753-f5c6-f58734f28ead.png)
画面表示上は気づきませんが、実は表示縮尺を変更する方法があります。（以下の様に[オンラインヘルプ](https://docshield.kofax.com/RPA/ja_JA/11.3.0_5cdzhlgb3t/help/rpa_help/help_main/designstudio/c_robot_editor.html)に記載があります。）

>ユーザビリティを向上させるため、Web ブラウザと同じ方法で拡大および縮小できます。
>
>- 拡大するには: Ctrl キーと +、または Ctrl キーとマウス ホイールのスクロール アップ
>- 縮小するには: Ctrl キーと -、または Ctrl キーとマウス ホイールのスクロール ダウン

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3090491/49af4148-4038-6bbf-3a0b-c0dd3877deb5.png)
ブラウザーや Excel の表示縮尺を変更する時と同じ方法で拡大/縮小ができるので、是非試していただきたい！

:::note
レコーダービューに対しても同じやり方で表示の拡大/縮小を変更できますが、以下の様に画面上に用意された縮尺のプルダウンを変更することでも同じ効果が得られます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3090491/c0138f3b-5a90-4f18-24f1-e08381e36ac6.png)
:::

## その３：レコーダービューのフローティング化

レコーダービューの右上（画面上はちょうど中段）に表示されている「`⤴`」マークをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3090491/5312769c-7651-7151-15de-c42d9bebf5d0.png)
ウィンドウが切り離されて、フローティング状態になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3090491/18cc19a9-36cc-1501-653e-c2690fc0f2aa.png)
ロボット作成中は

1. レコーダービューを操作してステップを自動記録させる。
1. 記録したステップをワークフロービューで調整・チューニングする。

の繰り返しだと思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3090491/f41a1aec-c3da-c19a-a4b2-a33aecfc99a6.png)
レコーダービューの縦幅は画面いっぱいに広げたうえで、適宜上下に動かしてワークフローを操作するのがいいですね。

## まとめ

縮尺変更はかなり便利！
【Python】レーベンシュタイン距離

# 作るもの
2つの文字列間のLevenshtein距離を計算するアルゴリズム。

# 補足
- レーベンシュタイン距離とは？
2つの文字列間の類似度や差異を測る指標

例）
元の文字列: "kitten"
目標文字列: "sitting"

この場合、元の文字列を目標文字列に変換するためには次の操作が必要。

"k" を "s" に置換
"e" を "i" に置換
"n" を "t" に置換

合計で3回の置換操作が必要。したがって、この2つの文字列間のLevenshtein距離は3となる。

# 実装
```python:levenshtein_distance.py
def levenshteinDistance(str1, str2):
    # 長い方の文字列を big に、短い方の文字列を sma に代入
    big = str1 if len(str1) > len(str2) else str2
    sma = str2 if big == str1 else str1

    # 初期化: prev_row と curr_row の作成
    prev_row = [k for k in range(len(sma) + 1)]  # sma の文字数分の初期値を持つリスト
    curr_row = [None for _ in range(len(sma) + 1)]  # 同様に None のリストを作成

    # ダイナミックプログラミングのループ: 大きな文字列を基準にループ
    for i in range(1, len(big) + 1):
        curr_row[0] = i  # 現在の行の最初の要素は i

        # サブループ: 小さな文字列に対してループ
        for j in range(1, len(sma) + 1):
            # 置換のコストを計算
            repl_cost = 0 if big[i - 1] == sma[j - 1] else 1

            # 最小コストの計算と代入
            curr_row[j] = min(
                prev_row[j] + 1,  # 挿入
                curr_row[j - 1] + 1,  # 削除
                prev_row[j - 1] + repl_cost,  # 置換 or 同一文字
            )

        # 行の切り替え: prev_row と curr_row を交換
        prev_row, curr_row = curr_row, prev_row

    # 最終的な Levenshtein 距離を返す（大きな文字列の終端の値）
    return prev_row[-1]

# テスト
if __name__ == "__main__":
    str1 = "abc"
    str2 = "yabd"
    print(levenshteinDistance(str1, str2))

```

# 参考
https://www.algoexpert.io
text-generation-webuiで学習モデルダウンロード時にhuggingfaceにログインする

## 結論

環境変数HF_TOKENにhuggingfaceのトークンをセットしてから、`text-generation-webui`を起動してください。
トークンのURL： https://huggingface.co/settings/tokens

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/515611/6206d92e-15e0-4f70-079e-c2064a9b7bb6.png)

## 環境

| 項目 | 情報 |
| :--: | :--: |
| OS | Ubuntu 22.04 LTS server |

`text-generation-webui`は、下記手順でインストール。

https://qiita.com/kararu88/items/86ed1e9314ddd3065c08

## text-generation-webui 学習モデルダウンロード機能

`text-generation-webui`には、huggingfaceから学習モデルをダウンロードする便利な機能が用意されています。
この機能ですが、設定なしだと **huggingfaceにログインせず** に学習モデルのダウンロードを行います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/515611/9ceee59c-ef42-ca6f-4ad5-8ed9ed4380c1.png)

※ 他にインストールディレクトリ配下に「`download-model.py`」というスクリプトがあり、こちらでも学習モデルのダウンロードが行えます。使い方については、今回は割愛します。

学習モデルによっては、モデル作成者へ情報を提供して利用規約に同意しないとダウンロードできない学習モデルがあります。そういった学習モデルを`text-generation-webui`からダウンロードするには、huggingfaceへのログイン情報を本アプリに伝える必要があります。

`text-generation-webui`では、huggingfaceへのログイン情報を環境変数から読み取るようになっています。
トークンか、ユーザ名＆パスワードのいずれかで認証を行います。どの環境変数を使えばいいかは以下のテーブルをご覧ください。

| No | 認証 | セットする環境変数 |
| :-- | :-- | :-- |
| 1 | トークン認証 | HF_TOKEN |
| 2 | ユーザ名＆パスワード認証 | HF_USER、HF_PASS  |

なお、当方の環境では No2 のユーザ＆パスワード認証については、一度試してみたものの上手くいかなかったため、諦めてトークン認証を行うようにしました。

### 備考

`text-generation-webui`のHPにはトークン認証の場合、どんな環境変数を使えばよいか記載がありませんでした。
そのため、本アプリの`download-model.py`のコードからHF_TOKEN環境変数を特定しています。おそらく問題ないとは思いますが、今後、環境変数名が変わるかもしれないということを、ご留意ください。

## 参考HP

- [text-generation-webui](https://github.com/oobabooga/text-generation-webui)
- [text-generation-webui 学習モデルのダウンロードについて](https://github.com/oobabooga/text-generation-webui#downloading-models)
- [huggingface](https://huggingface.co/)
SQLBoiler事始め

GoのORMライブラリSQLBoilerを使った簡単なサンプルを作ってみます。

DBはSQLite3を使用します。

- Go: v1.18
- SQLBoiler: v4.14.2

## プロジェクトの作成

```
cd ~/go/src/github.com/TatsuNet
```

```
mkdir sql-boiler-sample
```

```
cd sql-boiler-sample
```

```
go mod init github.com/TatsuNet/sql-boiler-sample
```

## SQLBoilerをまだインストールしていない場合はインストール

```
go install github.com/volatiletech/sqlboiler/v4@latest
```

```
go install github.com/volatiletech/sqlboiler/v4/drivers/sqlboiler-sqlite3@latest
```

```
sqlboiler --version
```

```:出力
SQLBoiler v4.14.2
```

## データベース、テーブルの作成

```
sqlite3 sql-boiler-sample.db
```

```
CREATE TABLE IF NOT EXISTS users(
  id INTEGER PRIMARY KEY NOT NULL,
  name TEXT NOT NULL
);
```

```
.ta
```

```:出力
users
```

テストデータの作成

```
INSERT INTO users(id, name) VALUES (1, "tatsu");
```

```
SELECT * FROM users;
```

```:出力
1|tatsu
```

```
.exit
```

## SQLBoilerの設定ファイルの作成

プロジェクト直下に以下のファイルを作成

```:sqlboiler.toml
pkgname="db"
output="db"
no-tests=true

[sqlite3]
  dbname = "./sql-boiler-sample.db"
```

## コード生成

```
sqlboiler sqlite3
```

`sqlboiler.toml` の `output` で指定したディレクトリに各種ファイルが生成されます。

```
ls db
```

```:出力
boil_queries.go		boil_table_names.go	boil_types.go		boil_view_names.go	sqlite_upsert.go	users.go
```

## サンプルプログラムの作成

`users` テーブルから、取得カラム、検索条件を指定してレコードを1件取得するサンプル

```go:main.go
package main

import (
	"context"
	"database/sql"
	"log"

	"github.com/TatsuNet/sql-boiler-sample/db"
	"github.com/cockroachdb/errors"
	_ "github.com/mattn/go-sqlite3"
	"github.com/volatiletech/sqlboiler/v4/queries/qm"
)

const (
	dbFile = "sql-boiler-sample.db"
)

func main() {
	ctx := context.Background()

	db, err := openDB()
	if err != nil {
		log.Fatalf("%+v", err)
	}
	defer db.Close()

	user, err := getUserByID(ctx, db, 1)
	if err != nil {
		log.Fatalf("%+v", err)
	}

	if user == nil {
		log.Fatal("user not found")
	}
	log.Printf("UserId:%d, UserName:%s", user.ID, user.Name)
}

func openDB() (*sql.DB, error) {
	sqlDB, err := sql.Open("sqlite3", dbFile)
	if err != nil {
		return nil, errors.Wrap(err, "failed to open db")
	}
	return sqlDB, nil
}

func getUserByID(ctx context.Context, sqlDB *sql.DB, id int64) (*db.User, error) {
	user, err := db.Users(
		qm.Select(db.UserTableColumns.ID, db.UserColumns.Name),
		db.UserWhere.ID.EQ(id),
	).One(ctx, sqlDB)
	if err != nil {
		switch err {
		case sql.ErrNoRows:
			return nil, nil
		default:
			return nil, errors.Wrap(err, "failed to get record")
		}
	}
	return user, nil
}
```

実行結果

```
go mod tidy
```

```
go run main.go
```

```:出力
2023/08/11 21:31:44 UserId:1, UserName:tatsu
```
【SwiftUI】TabViewのPageスタイルのバウンスを無効化する

# はじめに
何もないところにスクロールしようとするとバウンドする感じが気に入らないのでこれを無くしたいです。
![Simulator Screen Recording - iPhone 14 Pro - 2023-08-11 at 21.58.23.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/17516b3d-0cb4-0e5f-9c18-4a52f992f522.gif)

`SwiftUIIntrospect`を使うことでなくせたので記事にしておきます。

# 実装
```swift
import SwiftUI
import SwiftUIIntrospect

struct ContentView: View {
    var body: some View {
        TabView {
            Color.red
            
            Color.yellow
            
            Color.blue
        }
        .tabViewStyle(.page(indexDisplayMode: .never))
        .introspect(.tabView(style: .page), on: .iOS(.v14, .v15, .v16, .v17)) {
            $0.bounces = false
        }
    }
}
```

![Simulator Screen Recording - iPhone 14 Pro - 2023-08-11 at 22.01.20.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/0cebb691-9705-8a73-56a7-24537e58517d.gif)

# おわり
これ公式が提供してもいいんじゃない？
【インターン体験記】アイフルのサマーインターンに参加した話

# はじめに
　8月7日から8月18日までの２週間、アイフルのエンジニアインターンに参加してきたのでその体験記を書きたいと思います！
　この記事ではインターンの良かったところ/残念だったところ、このインターンで学んだことなどをアウトプットしていきたいと思います。
# やったこと
このインターンは前半と後半に分かれていて
 - 前半：web開発の知識・技術の講義
 - 後半：参加学生でのチーム開発

前半後半でやったことをそれぞれ詳しく説明したいと思います！

## 前半でやったこと
前半は基本的に社員の方からの講義がメインでした。講義の内容としては
 - HTML, CSS, JSの基礎
 - Git, GitHubの使い方
 - TypeScriptの基礎
 - APIについて
 - ReactJSの基礎
 - Nextjsの基礎
 
 です。
HTML,CSSなどのかなり基本的な内容からモダンなフロントエンド開発の基礎的なところまで幅広いことを教えてもらいました。
講義の中では適宜練習問題が用意されており着実に力をつけていけるようになっていました。

## 後半でやったこと
後半はNextを用いて与えられた要件を満たすシステムを作成する課題をチームで行いました。
作ったものはAPIから得られるデータを用いてグラフ表示をするといった内容のものでした。

他のインターンのメンバーと役割分担やタスクの分担を行う、いわゆるチーム開発を行うことができました。

このチーム開発は前半の講義とは異なってほぼ全てを学生が主導して開発を進めていくことができてかなり楽しかったです。

# 良かったところ
アイフルのインターンで良かったことは大きく分けて**開発面**と**開発以外の面**の２つの観点から紹介します。
## 開発面
 - web開発の知識がなかったが基礎の基礎から始めて、モダンな技術の基本的な使い方までを講義で知ることができた。
 - 社内のエンジニアの方との雑談などを通じて講義＋αの知識を得ることができた
 - チーム開発では開発だけではなく納期や工数についても考えてタスクの割り振りなども考える機会になった
 - MacBook proを借りてから開発することができ、快適に開発することができた

## 開発以外
 - 新卒の社員の方と実際に話をして会社の雰囲気や制度について知ることができた
 - ２週間の期間会社近くのホテルを用意してくれた
 - ほぼ未経験の学生だったのに時給をもらいなが開発をすることができた
 - 懇親会などで社員の方や同じインターン生との交流の機会を用意してくれた
 - インターンがある日は毎日お昼のお弁当がもらえる


# 残念だったところ
逆に残念だったことは以下のとおりです。
 - 実務の内容はほとんど知ることができなかった
     - 実際の開発体制や今作成されているプロダクトの概要はほとんど知る機会がなかった
 - モダンな技術の基礎的なところはわかるがそれが実務でどう使われるか、似た機能の使い分け方などの詳細なところは講義内では紹介されなかった
 - 参加者の経験値やスキルレベルの差が比較的大きく、チーム開発時に基礎知識の差を埋めるところから始まった。

# おすすめな人
アイフルのエンジニアインターンは以下のような人におすすめです
 - 経験が少ない人/未経験の人
 - エンジニアに漠然に興味を持っている人
 - 実際に現場で働いている社員と近い距離で働き、話したい人
 - チーム開発の経験をしたい人


# お勧めできない人
逆にこのインターンがお勧めできない人は以下のような人です。
 - すでに開発経験が豊富にある人
 - 一人でバリバリ開発したい人
 - 強強エンジニア学生と繋がりたい人


# 学んだこと
 - モダンなweb開発に必要な基礎知識
 レンダリングの方式やAPIについてなど今まであやふやな知識で使っていた技術について正しく知ることができた。また、今後何を勉強していけばいいのかの道標になった。
 - チーム開発の難しさ
後半のチーム開発ではタスクの分解から各個人の適性を見てのアサイン、アサインした後の履行管理などの体制が整っていなかったためそこを作成し運用するのが難しかった。
 - コードレビューの難しさ
他の人が書いたコードを読み、レビューするという経験はかなり新鮮でした。レビューで求められていることや、そもそものプリルクの分け方や、レビュワー目線でのタスク分解の方法などを知ることができました。

# 最後に
　web開発がほぼ未経験の自分でもこのインターンの２週間で大きくスキルアップすることができたと思います。
待遇も良く、社員の方からのサポートも手厚かったので非常にお勧めなインターンです！

　もしアイフルで働いてみたい人がいれば、こちらのリンクから採用ページを見ることができます！採用サイトの要件からも分かる通り、かなりホワイトな会社だったのでぜひ一度見てみてください💪

[全体採用サイト](https://aiful-recruit.com/)
[エンジニア専用採用サイト](https://engineer.recruit.aiful.co.jp/recruit/)
SpringBootでWebSocketを使わない簡単なチャットアプリを作りました

# 記事を書いた経緯
SpringBootでチャットを作りたかったので、ネットで「SpringBoot チャット」で調べると、WebSocketを使った記事しか出てきませんでした。WebSocketは簡単にチャットを作れていいのですが、その反面、カスタマイズのしにくさを私は感じました。例えば、「チャットの相手のPCがチャット画面を開いたままスリープに入ったときに自分が送ったメッセージを、相手のPCがスリープから復帰した後にどう受信させるか？」などです。
そこで私は数日間考えたのですが、その結果、「WebSocketを使わなくてもチャットは作れるじゃん！」と気づき、実際に最低限の機能を実装できました。
この記事は、私と同様な悩みを持っている方に届けばいいなと思い、書きました。

# 概要
SpringBootでDMができるチャットアプリを作りました。
このチャットアプリの特徴は、WebSocketを使っていない点です。HTTPメソッドのGETとPOSTのみで作りました。作りが単純なので、カスタマイズしやすいと思います。

# デモ
<iframe width="560" height="315" src="https://www.youtube.com/embed/Yifpp-JgLjU" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>

デモの見どころとしては、
- 24秒あたりからチャットのデモが始まります
- 1分27秒あたりでは、自動的に画面がスクロールします
- 1分33秒あたりでは、チャットルームを一度閉じて、再度チャットルームを開くことで、メッセージが保存されていることを確かめています

# ソースコード
https://github.com/tez3998/simple-chat

データベースを作るためのSQLも同じ場所にアップロードしています。

# 機能
実装した機能としては以下のようなものがあります。
- ユーザー登録
- ログイン
- チャット

# チャットをする仕組み
仕組みは単純で、ユーザーがチャット画面に到達すると、1秒毎に非同期のPOSTで新着メッセージを取ってきています。クライアント側の処理は、ソースコードの[chat.html](https://github.com/tez3998/simple-chat/blob/master/SimpleChat/src/main/resources/templates/chat.html)と[chat.js](https://github.com/tez3998/simple-chat/blob/master/SimpleChat/src/main/resources/static/js/chat.js)を、サーバー側の処理は[MessageController.java](https://github.com/tez3998/simple-chat/blob/master/SimpleChat/src/main/java/com/example/simplechat/controllers/MessageController.java)のrecieve()というメソッドを見ると分かりやすいと思います。

まず、クライアントは新着メッセージを貰うために、チャット相手のIDと自分が最後に受信したメッセージの送信日時（まだ何もメッセージを受信していない場合はnull）というような情報をサーバーに渡します（下図の①）。
サーバーは渡された情報をもとにデータベースを調べ、新着メッセージのリストを返します（下図の②）。
![新着メッセージの取得方法](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2014686/d6ee763d-374d-138b-6cb0-f119ad6eed3e.png)

# 苦労した点
ロジックは以外にも簡単にできました。一方で、HTMLやCSSの知識に乏しいため、チャット画面のUIは苦労しました。チャット画面のUIには、以下の記事を参考にしました。

https://saruwakakun.com/html-css/reference/speech-bubble


# 今後、実装していきたい機能
- ユーザーが一覧できる画面で、LINEのような未読件数の分かる機能
- アイコンや名前などのユーザー情報を変更できる機能
- 全メッセージを保持するテーブルでの検索の回数を減らす機能
ハイブリッド／マルチアカウント時代のネットワーキングとは─AWS認定ANS-C01資格試験合格体験記

先日、__[AWS認定Advanced Networking - Specialty](https://aws.amazon.com/jp/certification/certified-advanced-networking-specialty/)__（高度なネットワーキング - 専門知識）資格試験に合格しました。その折の学習方法をまとめておきます。後学の方の参考になれば幸いです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50571/dd4682ec-2156-9f11-712c-3b65c0105b11.png)

# 前提

* 受験した試験バージョンは __ANS-C01__。バージョンが変わらずとも出題内容は徐々に変わっているようなので、あまり意味がないかなと思いつつも一応。
* 受験時点で __五冠達成済み__（SAA、DVA、SOA、SAP、DOP）。これらの資格試験は参考書やEラーニング教材の揃いが比較的よく、そこで学んだこと（とくにSAP）がANSにも活かせる部分が多々ある。このため __五冠達成ののちに受験することをオススメ__。
* 私自身は実務でAWSの管理コンソールやCLIを利用する機会がほとんどなく、これまでの経験を全部足し合わせてもおそらく8時間に満たない。他のAWS資格同様、出題範囲は広範に及ぶから、 __「実務経験があること」を武器にすることも、「実務経験がないこと」を受験しないことの言い訳にすることもできない__（もちろん、所属する組織やプロジェクトによっても事情が異なると思いますが）。

# 学習方法

受験準備期間は1.5ヶ月ほど。

1. 参考書 __『[要点整理から攻略する『AWS認定 高度なネットワーキング-専門知識』 (Compass Booksシリーズ)](https://amzn.asia/d/8UANcOb)』__ を通読。練習問題は端折り。
2. AWS公式のEラーニングサービス、AWS Skill Builderのコース __"Exam Readiness: AWS Certified Advanced Networking - Specialty (Japanese) (Na) 日本語"__ と __"AWS Certified Advanced Networking - Specialty Official Practice Question Set (ANS-C01 - Japanese)"__ コースを修了。
3. Udemyのコースとして提供されている模擬試験問題集 __"Practice Exam - AWS Certified Advanced Networking Specialty"__ を修了。※機械翻訳 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50571/e0ef9306-3b32-aa31-7b29-e0973bc58370.png)

4. 模擬試験問題集の解説を確認し、不明点や疑問点はAWS公式の __[AWSサービス別資料](https://aws.amazon.com/jp/events/aws-event-resource/archive/?cards.sort-by=item.additionalFields.SortDate&cards.sort-order=desc&awsf.tech-category=*all)__ や、各種ブログ記事（クラスメソッド社やサーバーワークス社が中心）で補足。

2.の段階で一度受験したもののさすがに準備不足で700点台前半。不合格。
その後、3.と4.を加えて、2週間後にリテイクして750点とちょっと。首の皮一枚で合格。

# 所感

実際受けてみての所感。

* 今更言うまでもなく・・・ということなのかもしれないが、ハイブリッドクラウドやマルチアカウント構成は当たり前の時代。それを実現するネットワーキング。それらをどのように実装するか、どのようにコントロールするか、試験ではその知識が求められている。
* 受験準備〜リテイクの過程で、Direct ConnectやSite-to-Site VPNなど、SAPの学習の中で得られた知識がとても重要な土台として機能。Network　FirewallとDNSSEC、NLBなどについて自身の理解不足を痛感。
* UdemyではANS試験についてもう1つの模擬試験問題集 "AWS Certified Advanced Networking Specialty Practice Exams" が提供されており、こちらのほうが問題数は多い。こちらのほうがトレーニングになったかもしれない。![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50571/ac0ea528-fda3-72cf-07b5-6a8f9bce9668.png)
* 参考書については「要点整理」とあるほどには要点整理されていない印象で、さりとて他に選択肢がない以上はやはりありがたい存在で・・・といった感じ。
* 試験の難易度は SAPより易しい印象。求められる知識幅広さや設問と選択肢の文章量の問題（いずれもSAPが上）。前述の通り、五冠達成後に受験することをオススメ。
【2023年8月版】curl で ftpクライアント

# はじめに

ftp で接続できるか動作確認しないと。。。

ftpクライアントはいってないやん。。。

そうだ、curlでやろう

の、いつもコマンド忘れるので、メモ

# curl で ftp

ユーザ名 ftp
パスワード ftppass
サーバ 192.168.100.1
取得するファイル /share/abcd.txt

## ファイルの取得

```terminal
$ curl -O -u ftp:ftppass ftp://192.168.100.1/share/abcd.txt
```

## ファイルの中身を表示

```terminal
$ curl -u ftp:ftppass ftp://192.168.100.1/share/abcd.txt
```

## ファイルの中身を表示＋ftpプロトコルでの詳細表示

```terminal
$ curl -v -u ftp:ftppass ftp://192.168.100.1/share/abcd.txt
```

## IPv6等で拡張PASVモードを利用させない

```terminal
$ curl --disable-epsv　-v -u ftp:ftppass ftp://192.168.100.1/share/abcd.txt
```

## アクティブモードでの接続

curlではPASVがデフォルト

```terminal
$ curl --ftp-port -　-v -u ftp:ftppass ftp://192.168.100.1/share/abcd.txt
```

# さいごに

かんたんでしたね
VDI(Virtual Desktop Infrastructure)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3457284/1954dc35-f142-627c-5008-f95233383c82.png)
　仮想マシンサーバ上にクライアントマシンのデスクトップを構築し、それをネットワーク経由で利用する仕組みです。通常は、PCには各種アプリケーションソフトウェアをインストールして利用しますが、VDIで利用するPCには、仮想マシンサーバへの接続に必要なソフトウェアしかインストールしません。

　VDI利用時には、手元のPC上でのキーボード操作やマウス操作の情報は仮想マシンへ送られます。仮想マシンからは、デスクトップ画面の画像情報がPCへ送られてきます。このような仕組みによって、手元のPCを仮想マシンそのものとして利用するのです。

　VDIで利用するPCには、キーボードやマウス操作の入力を受け付け、デスクトップ画面の画像を表示できれば十分です。従って、通常のPCのような性能は必要とされません。VDI実現に必要最小限のCPU性能、メモリ、ハードディスクなどの搭載しているコンピュータをシンククライアントと言います。

　VDIは、PCがネットワークに接続されていれば、どのような場所でも同一のクライアント環境を利用できます。出張先の支社のPCでも、いつも利用しているデスクトップ環境が利用でき作業効率を向上させることが可能です。じゃた、データはPC内には保管できず、全て、仮想マシン内に保管されます。つまり、情報流出のリスクの低減にもつながります。
【Git】エラー「failed to load library 'libcurl-4.dll'」について (Windows環境)

# 概要
Windowsで`git clone`を実施したら以下のエラーになりました。

```
failed to load library 'libcurl-4.dll'
```

# 原因
このエラーは、プログラムが実行時に必要なライブラリである`libcurl-4.dll`を読み込めなかったことを示しています。

日本語のフォルダ名を含むディレクトリにある場合、Windowsのシステムがライブラリのパスを正しく解決できないことがあるようです。プログラムが文字のエンコーディングを正しく解釈できなかったり、ファイルパスの操作が失敗したりするためらしい。
この問題を回避するためには、日本語のフォルダ名を含まないディレクトリに移動する必要があるとのこと。

### `libcurl-4.dll`とは
`libcurl-4`は、C言語で書かれたオープンソースのライブラリであり、URLを使用してデータを転送するための機能を提供するもの

# 解決方法：
日本語のフォルダ名を含まないディレクトリ上で実行したら解決しました。

## 参考
[Gitlab×VScodeのgit cloneがうまくいかなかった話](https://qiita.com/kimura_r76/items/72e4633b824bd430b6d3)
Nucleo F303K8 UART テスト w/ STM32CubeIDE

- Nucleo F303K8
- STM32CubeIDE 1.13.1
- Mac

# まずは定番

```main.c
	  char msg[]="Hello world\r\n";
	  HAL_UART_Transmit(&huart2, (uint8_t *)msg, sizeof(msg), 1000);
```

# 1文字読んで1文字返す

```main.c
  char p[]=" ";
  while (1)
  {
	  while(!__HAL_UART_GET_FLAG(&huart2, UART_FLAG_RXNE)) {}

	  p[0]=USART2->RDR;

	  /* echo */
	  HAL_UART_Transmit(&huart2, (uint8_t *)p, 1, 1000);
  }
 }
```

受信データを待ちます。
複数バイト一気に来るときの動作は保証されません。
`HAL_UART_Receive(&huart2, (uint8_t *)p, 1, 1000);` でもいいのですが，ここでは `USART2->RDR` を使いました。RXNEフラグもしっかりクリアされてました。

# RXNE フラグを見る方法


```main.c
	  while(!(USART2->ISR & UART_FLAG_RXNE)) {}
```

とか

```main.c
	  while(!__HAL_UART_GET_FLAG(&huart2, UART_FLAG_RXNE)) {}
```

# 複数バイト受け取る

`'\r\n'` が来るまで（最大32バイト）読み込んでエコーバック

```main.c
  while (1)
  {
	  uint8_t buf[32];
	  uint8_t c, p, eol_flag;

	  p=0;
	  eol_flag=0;

	  do {
		  while(!__HAL_UART_GET_FLAG(&huart2, UART_FLAG_RXNE)) {} // wait until RXNE=1
		  c = USART2->RDR;
		  buf[p]=c;
		  p=(p+1) & 31;
		  if (c == '\r') {
			  eol_flag = eol_flag | 0b10;
		  } else {
			  if (c == '\n') {
				  eol_flag = eol_flag | 1;
			  } else {
				  eol_flag = 0;
			  }
		  }
	  } while( eol_flag != 0b11 );

	  /* echo */
	  HAL_UART_Transmit(&huart2, (uint8_t *)buf, p, 1000);

	  HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_3);
	  HAL_Delay(100);
	  HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_3);
  }
```

私，長いこと理解できていなかったのですが，要するにRXNEを見て待ち，データを一つ読んで処理をして，またRXNEを待つようにしないと取りこぼしてしまいます。

今回の例では，エコーバックしてLEDをチカチカさせている間にデータが2個以上来てしまったら，その2個目以降のデータは取れないことになります。

2行のデータを一気に送ってみると，RXNEをクリアできずにフリーズしてしまうことがあるようです。受信割込み等を使って工夫する必要がありそうです。

# 補足

```bash
$ screen /dev/cu.usbmodem14103 9600
```
補足
- [Ctrl]+[a] 離して [k] で終了させる
- CR+LF -> [Enter] and [Ctrl] + [J]
単語帳 16

# 英語の単語
## 背景
これからますます必要になってくる英語の単語を少しピックアップして見ます。

## コンテンツ
・asset 資産・有用なもの
・culinary 調理用の
・despite  にも関わらず
・durable 耐久性のある
・depict を描写する
・fund 資金・資金を提供する
・firm 会社・事務所
・invoice 請求書
・inquiry 問い合わせ
・revise   〜を修正する　〜を改定する
・reimbursement 払い戻し・返済
・revenue 収入・収益
・regulation 規則・規定
・transport 輸送　輸送する
・transfer 転勤・移動
・voucher 引換券

## check
・にも関わらず
・耐久性のある
・を描写する
・資金・資金を提供する
・会社・事務所
・請求書
・問い合わせ
・〜を修正する　〜を改定する
・払い戻し・返済
・収入・収益
・規則・規定
・転勤・移動
・引換券
九九表（PHP）

これも何に使うんですかと言われるでしょうが(;^_^A。
~~~
<?php
for($i=1;$i<=9;$i++){
    for($j=1;$j<=9;$j++){
        if($j!=9){
            echo $j*$i." ";
        }else{
            echo $j*$i."\n";
        }
    }
}
?>
~~~
外側ループの$iと、内側ループの$jをかけて行き、その行の最後の数字だけに改行コードを付けることで完成します。、

結果
~~~
1 2 3 4 5 6 7 8 9
2 4 6 8 10 12 14 16 18
3 6 9 12 15 18 21 24 27
4 8 12 16 20 24 28 32 36
5 10 15 20 25 30 35 40 45
6 12 18 24 30 36 42 48 54
7 14 21 28 35 42 49 56 63
8 16 24 32 40 48 56 64 72
9 18 27 36 45 54 63 72 81
~~~
d3.jsで矢印を操作する

https://qiita.com/hightidesgoodvibes/items/7353616af956175e7b7b

の続き。これでできたpath
```html
<path d="M180,80 l0,-30 l15,0 l-15,-30 l-15,30 l15,0" />
```
をd3.jsで作るにはどうすればいいのだろう？教えて、ChatGPT先生！

```javascript
// SVG要素の幅と高さを設定
const width = 220;
const height = 270;

// SVGコンテナを作成
const svg = d3.select("body").append("svg")
  .attr("width", width)
  .attr("height", height);

// パスのデータを定義
const pathData = "M180,80 l0,-30 l15,0 l-15,-30 l-15,30 l15,0";

// <path>要素を追加
svg.append("path")
  .attr("d", pathData)
  .attr("id", "arrow");
```

へー、前回作ったパスデータをじかにテキストで定義して、d要素にattrするだけで作れるのか。ChatGPT先生からは線の幅とか色とかも教えてくれたけど、idをつけてcssにまとめた。

せっかくJavaScriptになったので、変数を使って沢山の矢印を描いてみる。それにあたって、ちょっとpathの始点と終点を逆にすることにした。始点だけ絶対座標で指定している。始点を真ん中にすることで、回転の中心も同じ座標になるし、地図投影したときも、たぶん、矢印の中心だけ座標変換して、矢の大きさとか形とかはSVG上の相対ピクセルで表せたままになると思う。

JavaScriptでクラスの配列を書くのははじめてなので、うごくかどうかどきどき。

```javascript
var arrowwidth2 = 2.5
var arrowheight = 5
var linelength = 5

var DataSet2 = [{ x: 30, y:250, r:215 },
                { x: 70, y:250, r:250 },
                { x: 70, y:210, r:295 },
                { x: 70, y:170, r:310 },
                { x:110, y:210, r:190 },
                { x:110, y:170, r:240 },
                { x:150, y:210, r:200 },
                { x:150, y:170, r:266 }];
svg.selectAll(".arrowsmall")
   .data(DataSet2)
   .enter()
   .append("path")
   .attr("d",function(d,i){
         return "M" + d.x + "," + d.y +
         " l" + arrowwidth2 + ",0"+
         " l-" + arrowwidth2 + ",-" + arrowheight +
         " l-" + arrowwidth2 + "," + arrowheight + 
         " l" + arrowwidth2 + ",0" + 
         " l0," + linelength ;
   })
   .attr("transform", function(d,i){
         return "rotate(" + d.r + "," + d.x + "," + d.y + ")";
   })
   .attr("class", "arrowsmall");
```

![Web キャプチャ_11-8-2023_204640_localhost.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2167014/82167a91-dea9-999c-0143-53aab063b5e0.jpeg)

満足！
【Laravel】ページネーション対応のAPIを実装する

## 概要
API開発を行っているときに、フロントから?page=2などのように指定して、ページネーションでデータを受け取りたい時があったので、Laravelでページネーション対応のAPIの実装について記述していきたいと思います。

## ルーティングとコントローラーを実装
まずは、api.phpにユーザー情報を取得するためのエンドポイントを定義します。

```api.php
<?php

use App\Http\Controllers\UserController;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;

Route::get('/users', [UserController::class, 'index']);
```

次に、ユーザー情報を取得するためのコントローラーを定義していきます。

```php
<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Models\User;
use Illuminate\Http\Request;

class UserController extends Controller
{
    public function index()
    {
        return User::paginate(5);
    }
}
```

## paginate()メソッド

paginateメソッドの使い方は、引数にページ毎に取得したいアイテム数を渡すだけです。

クエリパラメータを受け取るような記述は一切必要ないのですが、page パラメータを送ると、そのページのデータが取得できます。

## レスポンスを見てみる
これでリクエストを送ってみると、レスポンスはこのようになっています。

```
{
    "current_page": 1,
    "data": [{id: 1, name: miura, email: miura@example.com},{id: 2, name: satou, email: satou@example.com }・・・]
    "first_page_url": "http://localhost/api/users?page=1",
    "from": 1,
    "last_page": 8,
    "last_page_url": "http://localhost/api/users?page=8",
    "links": [
        {
            "url": null,
            "label": "&laquo; Previous",
            "active": false
        },
        {
            "url": "http://localhost/api/users?page=1",
            "label": "1",
            "active": true
        },
        {
            "url": "http://localhost/api/users?page=2",
            "label": "2",
            "active": false
        },
        {
            "url": "http://localhost/api/users?page=3",
            "label": "3",
            "active": false
        },
        {
            "url": "http://localhost/api/users?page=4",
            "label": "4",
            "active": false
        },
        {
            "url": "http://localhost/api/users?page=5",
            "label": "5",
            "active": false
        },
        {
            "url": "http://localhost/api/users?page=6",
            "label": "6",
            "active": false
        },
        {
            "url": "http://localhost/api/users?page=7",
            "label": "7",
            "active": false
        },
        {
            "url": "http://localhost/api/users?page=8",
            "label": "8",
            "active": false
        },
        {
            "url": "http://localhost/api/users?page=2",
            "label": "Next &raquo;",
            "active": false
        }
    ],
    "next_page_url": "http://localhost/api/users?page=2",
    "path": "http://localhost/api/users",
    "per_page": 5,
    "prev_page_url": null,
    "to": 5,
    "total": 38
}
```
dataにユーザーの一覧が格納されています。

また "current_page": 1, の現在ページ、 "last_page": 8, の何ページが存在するかだったり、 "next_page_url": "http://localhost/api/users?page=2" の次ページを取得するためのURLなど、色々な情報を自動的にレスポンスに組み込んでくれています。めちゃくちゃ便利！

フロント側から1ページ目であれば /users 、2ページ目であれば /users?page=2 というようにページ数をクエリパラメータで指定してあげるだけで、簡単にページネーション が実装できます。

## 最後に
使ってみた感想は、ものすごく簡単にページネーションを実装することができますし、なによりもレスポンスの丁寧さにびっくりしました。
configureスクリプトでMakefile作成時に発生する「checking whether the C compiler works... no」の直し方

## 発生した現象

configureスクリプトでMakefile作成時に以下のエラーが発生した。

```sh
$ ../ruby/configure --prefix=$PWD/../install --enable-shared

This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.

It was created by configure, which was
generated by GNU Autoconf 2.71.  Invocation command line was

  $ ../ruby/configure --prefix=/Users/daiki-kudo/repos/ruby/build/../install --enable-shared


...(中略)...


## ----------- ##
## Core tests. ##
## ----------- ##

configure:6061: checking whether the C compiler works
configure:6083: clang  -I/usr/local/opt/llvm/include -L/usr/local/opt/llvm/lib conftest.c  >&5
ld: unsupported tapi file type '!tapi-tbd' in YAML file '/Library/Developer/CommandLineTools/SDKs/MacOSX13.sdk/usr/lib/libSystem.tbd' for architecture x86_64
clang-16: error: linker command failed with exit code 1 (use -v to see invocation)
configure:6087: $? = 1
configure:6127: result: no
configure: failed program was:
| /* confdefs.h */
| #define PACKAGE_NAME ""
| #define PACKAGE_TARNAME ""
| #define PACKAGE_VERSION ""
| #define PACKAGE_STRING ""
| #define PACKAGE_BUGREPORT ""
| #define PACKAGE_URL ""
| #define RUBY_ABI_VERSION 0
| /* end confdefs.h.  */
| 
| int
| main (void)
| {
| 
|   ;
|   return 0;
| }
configure:6132: error: in `/Users/daiki-kudo/repos/ruby/build':
configure:6134: error: C compiler cannot create executables
See `config.log' for more details
```

## 解決方法

以下の二つを行い、Anaconda関連のコードを削除すると直った。([ref](https://stackoverflow.com/a/75236331))

- `~/.zshrc`内のconda関係の設定を削除。
- $ `rm -rf ~/anaconda3`でAnaconda関係のディレクトリを削除。


## 他にやったこと

自分の場合以下では直らなかった。([ref](https://stackoverflow.com/a/63614795))

```sh
brew upgrade llvm
brew upgrade gcc
sudo rm -rf /Library/Developer/CommandLineTools
xcode-select --install
```


## ref. 

https://stackoverflow.com/questions/63592445/ld-unsupported-tapi-file-type-tapi-tbd-in-yaml-file
スッキリわかるSQL入門を読んだ感想

[スッキリわかるSQL入門](https://www.amazon.co.jp/%E3%82%B9%E3%83%83%E3%82%AD%E3%83%AA%E3%82%8F%E3%81%8B%E3%82%8BSQL%E5%85%A5%E9%96%80-%E7%AC%AC3%E7%89%88-%E3%83%89%E3%83%AA%E3%83%AB256%E5%95%8F%E4%BB%98%E3%81%8D-%E3%82%B9%E3%83%83%E3%82%AD%E3%83%AA%E3%82%8F%E3%81%8B%E3%82%8B%E5%85%A5%E9%96%80%E3%82%B7%E3%83%AA%E3%83%BC%E3%82%BA-%E4%B8%AD%E5%B1%B1/dp/4295013390)を一通り読むことができましたため、読んでみて、良かったところや学んだこと、改善点を記載します！

## 良かったところ

### ドリルが256問あるところ

技術書の中にはドリルがついておらず、技術の説明しか書かれていないものもありますが、本書は256問もあるため、自身がインプットした内容でどこらへんを理解していないのかを明確にできる点は良かったと思います！私自身はトランザクション、インデックス、テーブル設計の分野に関する理解が浅かったため、今後はその分野を重点的に復習していきます。


### SQL文を丸暗記ではなく、理解できる

SQLには集計関数(`COUNT、SUM、AVG、MIN、MAX`など)では`where`は使用できませんが、その理屈を理解していない状況でした😵
そのためすぐにルールを忘れたり、エラーに遭遇した際にググって解決しても忘れるという状況でした、、
ですが、本書には理屈が書かれているため、丸暗記にはならず、理屈を理解できるように書かれているため、頭に残りやすいと思いました。

上記の例で挙げたwhereで集計関数を使えない理由として、**集計関数はテーブルを検索した結果、集計していることやSQLの実行順序としては`where`は集計される前のため、`where`で集計関数は使用できないことを学びました。**

:::note info
SQLは FROM→WHERE→GROUP BY→HAVING→SELCT→ORDER BYの順で実行されます。
:::


### 新しい視点でSQLに関する知識を整理できる

今まで私が持っていなかった視点を本書では提供していただけたため、SQLに関する理解が深まったことです。

例えば、今までSELECT、UPDATE、DELTE、INSERTを分類分けすると、参照系のSELECTと更新系のUPDATE、DELTE、INSERTです。この書籍には他の分類分けの仕方も紹介していただいており、新規データのINSERTと既存データのSELECT、UPDATE、DELTEです。さらに、WHEREが使用できるのは、既存データで、新規データでは使用できないという説明も目からウロコが落ちました🤩


## 学んだこと

### インデックスによって、パフォーマンスが改善するパターンやデメリット

インデックスを定義するとSQLのパフォーマンスが改善することやインデックスの作成方法は把握していたものの、どのカラムにインデックスを定義するとパフォーマンスが改善するのかをちゃんと理解できていなかったため、勉強になりました。
具体的には以下のカラムに対してインデックスを定義するとパフォーマンスが改善します。

- order by
- whereの完全一致、前方一致
- joinの結合条件

:::note alert
whereの部分一致、後方一致はインデックスによって、パフォーマンスが改善しません。
:::

一方、インデックスを定義するデメリットもあります。UPDATE、DELTE、INSERTのような更新系の処理はテーブルのデータだけでなく、インデックスの情報も更新しなければいけなくなってしまうため、それによって、更新系の処理のオーバヘッドが増えてしまいます。


### トランザクションについて

前職でOracleデータベースを使用していたものの、Oracleでは明示的にトランザクション(BEGIN〜COMMIT)を定義しなくても利用できてしまっていたため、勘違いしていたことや新たに学ぶことがありました！

- 今まではUPDATE、INSERTを発行したら、テーブルにロックがかかると思っていたのですが、そうではなく、トランザクションによって、テーブルにロックが掛かっていることを学びました。Oracleだと自動的にトランザクションが利用できていたため、そのように勘違いをしておりました😅

- ロックによって、ロックが解除されるまでトランザクションは待ち時間が発生してしまい、パフォーマンスが低下してしまいますが、トランザクション分離レベルを指定することで、パフォーマンスと分離レベルを調整できることを知りました。

- トランザクション分離レベルのREAD UNCOMMITTED
    - 他のトランザクションからUPDATE、DELTE、INSERTなどの更新処理されないようになっているため、自身のトランザクションの中でクエリを複数発行しても同じ結果になります。
    - 一番安全ですが、パフォーマンスが一番低くなってしまいます。

- トランザクション分離レベルのREAD COMMITTED
    - 他のトランザクションからUPDATEは実行されないため、自身のトランザクションの中でクエリを複数発行しても同じ結果になります。
    - SERIALIZABLEみたいにDELTE、INSERTを実行されてしまったら、クエリを複数発行しても同じ結果にならないことがあります。SERIALIZABLEよりは安全性にかけますが、パフォーマンスは良いです。
    
- トランザクション分離レベルのREPEATABLE READ
    - 多くのDBMSではデフォルトの設定
    - 自身のトランザクションでUPDATE、DELTE、INSERTがCOMMITされるまでは他のトランザクションからは変更中のデータを参照できない。

## 改善点

ドリルがついていることは嬉しかったのですが、そのドリルの解説がほぼなく、解答しかなかったため、第4版では解説が充実していることを期待しております🥺
has_manyメソッドとbelongs_toメソッド

## has_manyメソッドとは？
※Twitterの様な投稿アプリを実装しています※

あるユーザーの作成した投稿は複数個あります。
つまり1人のユーザーは複数個の投稿を所有している状態になります。
従ってuserモデルの視点から考えると、tweetモデルは
「User has many Tweet」⇒　「一対多」の関係になります。
従ってapp/models/user.rbには以下のように記載します。
```ruby
class User < ApplicationRecord
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable
  has_many :tweets
end
```
## belongs_toメソッドとは？
1つの投稿を複数人が投稿できないため、投稿は必ず1人のユーザーに所属します。この状態のことをbelongs toの関係といい、
「Tweet belongs to User」⇒　「一対一」の関係になります。
従って、app/models/tweet.rbには以下のように記載します。
```ruby
class Tweet < ApplicationRecord
  validates :text, presence: true
  belongs_to :user
end
```
以上になります。

初心者ですので自分用のアウトプット用で記載しました！
公開鍵の登録までしたのにssh接続できない(ssh/configの設定ミス)

# 結論
 - ssh/configのIdentityFileの欄は`~/.ssh/`を含めて書こう
 - エラー内容を読んでうなろう

# 概要
githubに接続しようと思って秘密鍵と公開鍵を作りました。
githubに作った公開鍵を登録しました。
現時点で以下のような状態になっています。
```
└── ~/
    └── .ssh/
        ├── config
        ├── id_rsa_github
        └── id_rsa_github.pub
```


`ssh/config`を以下のように登録しました。
```ssh:~/.ssh/config
Host github
    HostName github.com
    User git
    IdentityFile id_rsa_github
    IdentitiesOnly yes
```

その場で`ssh github`すると……
```terminal
user@laptop:~/.ssh/$ ssh github
PTY allocation request failed on channel 0
Hi ko-fnks! You've successfully authenticated, but GitHub does not provide shell access.
Connection to github.com closed.
```
おおつながった。
<br>
作業ディレクトリに移ってgit push しようとすると
```terminal
user@laptop:~/code/$ ssh github
no such identity: id_rsa_github: No such file or directory
git@github.com: Permission denied (publickey).
```

ニョ～～（声にならない音）

# 対処法
IdentityFileの記入の仕方を修正しました。
```ssh:~/.ssh/config
Host github
    HostName github.com
    User git
    IdentityFile ~/.ssh/id_rsa_github #ここの行をミスっていた。そらつながりませんわ。
    IdentitiesOnly yes
```
Discord.py でBOT作成初挑戦（語尾ングアスから始まるbot開発）#01

# DiscordのBOTを作ってみた

ことの発端は、ディスコードのサーバーでAmongUsというゲームをしているときのこと・・・
「語尾ングアス（ゴビングアス）で遊んでみませんか？」
と絶賛深夜帯で会議もないも脳が動いてない状態でのおふざけアモアスが始まりました。

その時は、ボイスチャットの自分の下の人に話してほしい語尾を渡すというもの。

その時ふと。（適当にワードを入れてランダムで取り出すくじ引き的なのがあればなぁ）
とか思いながら、ゲームを進めていました。
そして、discord.pyに手を出し始めるのでした。

## まずBOTを作るには。。。

BOTっぽいものは昔、Node.jsを使ってなんとなく作成がしたことがあり、めんどくさいけど作れるのかなぁ。
というノリで作成を始めましたが、Node.jsでは確実に無理と感じたので方向転換
調べてみると、Node.js・Python・Go辺りで作成ができるらしい。。。
Goはよくわかりません・・(;'∀')
もう少し調べを進めると、Discord.py PyCordみたいな言葉が並んでおり、「Pythonで作れるのか！」
となり、作成開始。

python3を使い、Discord.pyというライブラリを使用しています。
※このあたりの説明は、先駆者がとても詳しく解説しているのでそちらをご覧ください。

## 作成の要件
基本的に欲しい機能は下の二つ
1. 好きな言葉が登録できること
1. ランダムで登録された言葉を取得できること

この二つを実現するためには、DBを使う必要があるのかなと判断しこのようなイメージをしました。

```mermaid
  graph LR
    regist[ワードを登録<br>チャットで好きな言葉を入力できる] --> save(登録されたワードを保持<br>DB内に情報を保持)
    save --> get[DBからワンダムでワードを取得<br>できるだけランダムになるように取得]
```

最初は、コマンドベースで登録・取得を作成

DBは環境を整えるのがめんどくさかったので、、、調べたら出てきたメモリDB的な機能を採用（LiteSQL）
（コードはおいおい載せていきます・・・）

ざっくりと形が出来上がったところでプロトタイプ投入

### プロトタイプを運用してみて。
データの保存・取り出しは意外とあっさりできましたが。
データの取得時のランダム性に違和感・・・
この時は、SQLでDBに登録する際にランダムな数値を入れるカラムを作成していました。
そのカラムを昇順に並べ替え一番上を取り出すという何とも強引なやり方・・・・
もう少し調べると、Pythonにランダムにデータをサンプリング（取得）してくれるものを発見
（そりゃありますよね・・・ｗ）

あとは、BOTがエラーなどをはいた時に再起動すると一緒にデータも消えてしまうこと・・・
これはまずい・・・

そこで、DBの導入とランダム取り出しの機能を変更
DBはMySQLを使用しています。

## なんやかんやと作成して

完成したのが、Viewを使ったなんちゃってbotです・・・
（使用できますが、いろいろと不具合もあるので更新していきます。）
https://onl.la/9P2VAZN

ほかにもいろいろ使えそうだなと思うBotになったのではないかなと。
ワードじゃなくても当たりはずれを入れた抽選に使えたり・・・？

・今後の課題はBOT再起動時のインタラクションエラー
・終わったときに表示されるワード表の調整

ちょっとコードとかの解説は私が理解できていないのでおいておきます・・・
とりあえず、これぐらいの知識でも作れるという勇気を皆さんに＾＾

ではよい、BOT開発を！

(2025/04/01:図解をマークダウンに変更)
プロを目指す人のためのRuby入門（チェリー本）を読んで

## はじめに
プロを目指す人のためのRuby入門［改訂2版］（通称：チェリー本）を読んでの感想を記載いたします。

## 良かったところ
* ほかのプログラミング言語に触れたことがある読者に対して、「他言語ではこうだけどRubyの特徴はこう！」というような書き方が全体的に多いこと。
  * 私はJavaをメインに他の言語に触れたことがありますが、別言語としての差分に集中してRubyの情報をインプットできました。
  * 本書は、Rubyの入門本を紹介されているサイトでは完全初心者用ではなく中級者〜上級書用に分類されることが多い印象です。完全なプログミング初心者には少し難易度が高いかもしれません。
* 例題のなかでリファクタリング作業があること。このリファクタリングを通して「Rubyではかなりシンプルな構文でプログラムを書ける」ということを体感できること。
  * 各章に一つ例題があるのですが、動くプログラムが完成した後にリファクタリングの作業を行います。リファクタリングを行った後はかなりシンプルになるので驚きが多かったです。

## 学んだこと
* 他の言語との違いについて印象に残った部分を記載いたします。
### 他の言語との違いについて・基本構文について
* 数値と文字列は暗黙的に変換されない。
* 最後に評価された式がメソッドの戻り値になるのが特徴。returnはメソッドを途中で脱出する際に使われることが多い。
* if文以外のところで`&&`や`||`を意図的に使う場合がある。
* エイリアスメソッドが多くある。
* 文字列や配列の添字に負の数字が使える。-1は最後の要素、-2は最後から２番目の要素。
* `X ||= A`で変数Xがnilまたはfalseなら、AをXに代入(nilガード)
* メソッド定義も式になっている。メソッド名をシンボルとして返す。
* 定数は「絶対変更できない値」ではない。「変更しようと思えばいくらでも変更できる値」になっている。扱う上では自己防止策が必要。

### 他の言語との違いについて・オブジェクト指向に関する構文について
* クラス定義も上から順に実行される。そのため、クラス構文直下でクラスメソッドを呼び出すことができる。
* 継承できるスーパークラスは1つだけ。ただしミックスインという多重継承に似た機能がある。
* `public protected private`のような可視性は、単語名は同じであるが他の言語と少し異なる。
* 抽象クラスやインターフェースは存在しない。

### 他の言語との違いについて・例外処理について
* throw、catchは例外処理とは関係ない。
* 他言語でいう`try~catch~finally`は、`begin~rescue~ensure`と書く。
* rescueにExceptionクラスを指定するのはRuby的には好ましくないコードになる。
* 意図的に例外を発生させるのは`raise`メソッドを使う。
* rescueは修飾子としても使える。

## 難しかったこと
* チェリー本に記載されている初歩的なコードでも「Rubyのコードは言語としての事前知識を知らないと読めないコードが多い」という印象を強く受けました。（nilガード等）
  * Rubyとしての言語の特性を活かした書き方を自分で実際に書いてみてRubyの構文に慣れる必要があると感じました。
  * Rubyは条件文でさえも式として評価され戻り値を返しているので、あらゆるものが式になっているという意識をもつことも大切に感じました。
* 私の理解力ではチェリー本に記載の内容を一度では理解できなかったので、何度も復習することが大事に感じました。
  * 例題以外の演習も行いたかったため、現在は簡単な演習本を購入しチェリー本で学んだ知識定着のための作業を行なっています。
Javaのインスタンス化

Javaのクラスをもとに製造する

```java
//カークラス
//車のナンバーや色などの詳細を定義
class Car{
  double speed; //速さ
  int number; //ナンバー
  String color; //色
  void clac(){ //クラクションを表すclacメソッド
    System.out.println("プップー");
  }
}
```
メインメソッドでcarクラスをmycarという変数でインスタンス化

mycarクラスのclacメソッドを呼び出している。
mycarクラスはcarクラスを参照してる。

```java
public class MyCarDrive{
  public static void main(String[] args){
    Car myCar = new Car(); 
    myCar.clac();
  }
}
Laravel10×MYSQL タイムスタンプが記録されなかった話

LaravelとMySQLを組み合わせて開発を行った際、タイムスタンプが正しく記録されないという問題に遭遇しました。
この記事は解決までの記録です。
 ## 問題の発生
マイグレーションファイルを以下のように書きました。
 ```PHP
Schema::create('cat_chan', function (Blueprint $table) {
    $table->id();
    $table->string('title');
    $table->text('content');
    $table->timestamps(); // created_atとupdated_atのカラムが自動的に追加される
}); 
 ```
しかし、データを入れた際*created_at*と*updated_at*の値がnullになっていました。
 ## 解決方法
*created_at*と*updated_at*を別々に書いて`->useCurrent()`を書き足すことで、現在の時刻を自動的に記録させることができました。
```PHP
Schema::create('cat_chan', function (Blueprint $table) {
    $table->id();
    $table->string('title');
    $table->text('content');
    $table->timestamp('created_at')->useCurrent();//変更
    $table->timestamp('updated_at')->useCurrent();//変更
});
```
 ## まとめ
`useCurrent()`を使用することで、データを挿入するたび、現在時刻が記録されるようになりました。
めでたし！
Custom Elementsからイベントを発火して処理を行う

# はじめに

最近Custom ElementsからイベントをDOMに伝えて処理を行うのをやったのですが、仕組みについて理解するのに時間がかかってしまったので、備忘録としてやり方をまとめます

今回は以下のリポジトリを利用して`Helloボタン`をクリックしたら親で検知してコンソールで`Hello`を表示してみます

https://github.com/jinwatanabe/react-web-component-sample

# 準備

```
$ git clone git@github.com:jinwatanabe/react-web-component-sample.git
$ cd react-web-component-sample
$ yarn dev
```

`localhost:5173`を開くと以下の画面が表示されます

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/810513/c324899a-f798-6db0-989c-41082ed42736.png)

# Custom Elementsでイベント発火する

まずはCustom Elementsでイベントを発火したことを親(DOM)に伝える処理を追加していきます
ここでは、`emitMessage`というイベントを親に伝えます

まずは`src/index.tsx`をひらいて、`SampleElement`にたいしてイベント発火の関数を渡せるようにします

```tsx:index.tsx
  connectedCallback() {
    this.root.render(
      <CacheProvider value={this.cache}>
        <SampleElement
          onEventExecution={(message: string) => {
            this.dispatchEvent(
              new CustomEvent("emitMessage", { detail: { message } })
            );
          }}
        />
      </CacheProvider>
    );
  }
```

`dispatchEvent`でイベント発火が行われます
`customEvent`で`イベント名`と`detailP`プロパティを使用してカスタムデータを渡すことができます

この設定をすることでWebComponentsを利用している側(ここではindex.html)でイベント(emitMessage)を検知できるようになります

次に、実際に関数を呼び出す部分を実装します。
ここでは`Helloボタン`を用意してクリックしたタイミングで先程渡したイベント発火関数を実行します

```tsx:index.tsx
const SampleElement: FC<{
  onEventExecution: (message: string) => void;
}> = ({ onEventExecution }) => {
  const sendHello = () => {
    onEventExecution("Hello");
  };

  return (
    <>
      <h1
        css={css`
          color: red;
        `}
      >
        Hello, world!
      </h1>
      <ul>
        <li>test</li>
      </ul>
      <a href="#">link</a>
      <button onClick={() => sendHello()}>Hello</button>
    </>
  );
};
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/810513/c3959602-4d86-76a2-94aa-03838a5d355c.png)

これでCustom Elementsの準備は整いました

# WebComponentsを利用する側でイベントを検知できるようにする

`/index.html`を以下のように修正します

```html:index.html
  <body>
    <script src="./src/index.tsx" type="module"></script>
    <div>
      <sample-element></sample-element>
    </div>
    <script>
      const emitMessage = (event) => {
        const message = event.detail.message;
        console.log(message);
      };
      const sampleElement = document.querySelector("sample-element");
      sampleElement.addEventListener("emitMessage", emitMessage);
    </script>
  </body>
```

`script`を追加しました
`sample-element`に対してイベントリスナー(emitMessage)を追加しました
イベントが発火したらコンソールでmessageを表示します


これで準備ができたので、Helloボタンをクリックするとコンソールに`Hello`と表示されます

# おわりに

Custom Elementsでの関数名とindex.htmlのイベント名を一緒にしていたので少し迷いましたが、理解するとかなりシンプルに使えるなと感じました

# 参考

https://developer.mozilla.org/ja/docs/Web/Events/Creating_and_triggering_events

https://developer.mozilla.org/ja/docs/Web/API/EventTarget/dispatchEvent
効率的な開発のために、スタイルガイドを作ろう

こんにちは、まちゅです。

家庭の事情によりインプットと記事作成が間に合いません。
Xでは何かしら発信ができているのでよしとしましょう。

### 今回やること
- スタイルガイド **←ここ**
- デザイン作成
- スタイル出力
- プロジェクトに投入

### スタイルガイドについて
ご存じかもしれませんが、デザインガイドの一種に
スタイルガイドという概念が存在しています。

より上位の概念のデザインガイドについては
サービスデザインレベルの話を含んでいるので今回は割愛させていただきます。

スタイルガイドの目的は

- より効率的にデザインをコードにするため。
- デザインの一貫性を保ち、サイトの品質を向上させるため。

の2点です。
制作現場では実際にサイトデザインの作成前にデザイナーとエンジニア間で取り決めを行う場合が多いようです。

コード上でも一々、ここはpaddingは何pxで、marginはいくつだ・・など
考える手間がなくなるので楽になりますよね。

例えば`!important` を禁止する決まりを作るだけでもゴリ押しコードが減るので保守性が高まります。

それと
googleが良さそうな規約を公開しているので、今すぐどんなものか確認したい方は以下を見るといいです。

https://google.github.io/styleguide/htmlcssguide.html

### 実際に規約を作る
前提として
規約を守るためのコストが、
規約によって節約されるコストを上回ってはいけません。

ですので、極力シンプルなものにします。

今回は
- フォントの大きさ
- 要素間のサイズと感覚

の2つの項目を整理した規約を作ろうと思います。

#### フォントの大きさ
最初はなんとなく「4の倍数でいいか・・」と考えていたのですが、
メリハリがなんとなく機械的になってしまい、調べたところ良いサイトを見つけました。

https://typescale.com/

このサイトは、
一般艇に美しいと呼ばれるフォントサイズのプリセットを提供してくれています。
黄金比や、完全五度(よくわからん。。)など。

幸いこのサイトはCSS書き出し機能があります。
試しにNoto Sansで書き出してみました。

```css
@import url('https://fonts.googleapis.com/css?family=Noto+Sans+JP:400');

html {font-size: 100%;} /*16px*/

body {
  background: white;
  font-family: 'Noto Sans JP', sans-serif;
  font-weight: 400;
  line-height: 1.75;
  color: #000000;
}

p {margin-bottom: 1rem;}

h1, h2, h3, h4, h5 {
  margin: 3rem 0 1.38rem;
  font-family: 'Noto Sans JP', sans-serif;
  font-weight: 400;
  line-height: 1.3;
}

h1 {
  margin-top: 0;
  font-size: 7.594rem;
}

h2 {font-size: 5.063rem;}

h3 {font-size: 3.375rem;}

h4 {font-size: 2.25rem;}

h5 {font-size: 1.5rem;}

small, .text_small {font-size: 0.667rem;}
```

これをベースに、8の倍数の最も近い整数に丸めての導入しようかなと思っています。

次は、**要素の12グリッドレイアウト**を考えます。

設定はfigma上で簡単にできます。
今回はお恥ずかしながら自分が製作途中のfigmaのものを載せておきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2611905/5c8dc307-0417-7ff7-5e4f-05f26b71af64.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2611905/0bfa83e9-5eda-b15f-173a-ebd1483cc2cc.png)


随分前の動画ですが、こちらを参考に、
画面幅を**1920**に設定した場合の奇麗に見える数値を調べました。

https://www.youtube.com/watch?v=QoyJItYSjkc&ab_channel=MidasMultimedia

(figmaの詳しい使い方については他のサイトを見てください。。)

これをcssに落とし込む事はまだやっていませんし、SPのデザインもないです。
それはまたデザイン完成後から書こうと思いますので、その時に追記しますね。

### 最後に
今回はフォントサイズと構成に絞りましたが、
テーマカラーやブレークポイントなどまだ決めなければいけない部分は多い自覚ありです。

この記事が少しでも他の方の個人制作の参考になれば幸いです。
vim-lsp-settings で typeprof を無効にするための設定

vim-lsp の設定を vim-lsp-settings で設定させてもらっているのですが、
まだ何もインストールしていない状態ですでに `typeprof`　が有効になっている判定になっていました。


vim-lsp
https://github.com/prabirshrestha/vim-lsp

vim-lsp-settings
https://github.com/mattn/vim-lsp-settings

実際 typeprof は install していないので、 ruby ファイルを vim で開いて
`:LspStatus` を実行すると、エラーが出てしまいます。

本来は solargraph を使いたいので、まずは `:LspUninstallServer solargraph` をして追加してみましたが、
typeprof が有効になってしまっているので、solagraph が認識されませんでした。

# 解決法
そこで、 typeprof を無効にするために、以下の設定を .vimrc に追加しました。

```.vimrc
let g:lsp_settings = {
\  'typeprof': {'disabled': 1},
\}
```

dein を使っている場合は、dein.toml の方に書いてもいいと思います。

```dein.toml
[[plugins]]
repo = 'shun/ddc-vim-lsp'
hook_add = '''
	let g:lsp_settings = {
	\  'typeprof': {'disabled': 1},
	\}
'''

```

この状態で ruby ファイルを開いて `:LspStatus` をすると、 solargraph が使えるようになっていました。
特定のメールを LINE に転送通知


特定のメールをLINEに自動連携する。　LINEを多用する人にとっては、まじで便利っす。
GASなのでお金もかかりません。仮にGASがダメになっても、適当なサーバーでバッチ作れば良い。


line norifyについては別で調べる必要あります。

https://qiita.com/chivi_dump/items/a62a7b8c32e6ea894a09


- 制限
通知1時間に1000件まで。


![スクリーンショット 2023-08-11 18.43.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64334/f0e0e28f-505f-e5ec-f4c2-585a4116051f.png)



- ポイント
普段のメールをgmailに転送し、gmailのスター機能を使って未読チェックをするのがポイント。
普通にメールサーバーである場合は、未読のチェックがやりずらい。

アップスクリプト登録して、トリガー登録してください。
あまりにもメール多いとテストしづらいので、ある程度スターつけてください。
その後にデバッグ実行とかしてください。

[参考の記事がこちら](https://qiita.com/triangleprism/items/197d486b595f4b62cff9)


```js:google app script
// 条件指定：メールタイトルに特定の文字列を含む、かつ、特定の送信者
var condition = "(subject:XXXX会報 from:dummy@dummy.dummy)";

function getMail(){

  // 条件にマッチするスレッドを取得する (最大 10)
  var threads = GmailApp.search(condition, 0, 10); 
  // 各スレッドから個々のメールを取得して二次元配列に格納する
  var messages = GmailApp.getMessagesForThreads(threads);

  for(var i in messages){
    for(var j in messages[i]){

      // スター無しメールのみを処理する
      if(!messages[i][j].isStarred()){ 

        // メールの日時
        var strDate = messages[i][j].getDate() + "\n";
        // メールタイトル
        var strSubject = messages[i][j].getSubject() + "\n";
        // メール本文
        var strMessage = messages[i][j].getPlainBody();

        // line に取得したメールの内容を送信する
        sendLine(strDate, strSubject, strMessage);

        // line に送信したメールにはスターを付与する
        messages[i][j].star(); 
      }
    }
  }
}

function sendLine(strDate, strSubject, strMessage){

  // line に送信するためのトークン
  var strToken = "999999999999999999999999999999";
  var options =
   {
     "method"  : "post",
     "payload" : "message=" + strDate + strSubject + strMessage,
     "headers" : {"Authorization" : "Bearer "+ strToken}
   };

   UrlFetchApp.fetch("https://notify-api.line.me/api/notify", options);
}
```
permitメソッドについて


permitメソッドは、requireメソッドと同様に、paramsが使用できるメソッドです。
permitメソッドは取得したいキーを指定でき、指定したキーと値のセットのみを取得します。
```ruby
params.require(:モデル名).permit(:キー名, :キー名) # 取得したいキーを指定する
```
※permitメソッドでparams.requireの内容からキーを指定すると、それ以外のキーがあっても値を受け付けません。

```ruby
params.require(:post)
#=> { "image" => "image.jpg", "text" => "sample text" }
```
※parmitメソッドを使用しない場合は全てのキーが取得されてしまいます。
　一方で↓
```ruby
params.require(:post).permit(:image) # imageのみを指定（textは取得されない）
#=> { "image" => "image.jpg" }
```
※parmitメソッドを使用すると、取得したいキーを指定することができます。
nodeバージョンの切り替え パッと確認

# はじめに
ズバッと切り替えのみに関して、自分が欲しいと思った情報だけを残します！
設定などがまだの方は、他の方の記事を参考にしてください！

# 手順
## まず確認
### インストールされているバージョンと現在のバージョンの確認
ここで、インストールされていれば、インストールしなくても良い。
[バージョン切り替えへ](#バージョン切り替え)
```
nodebrew ls    
```

### インストール可能なバージョン一覧
```
nodebrew ls-remote  
```

## 特定のバージョンインストール（v16.14.0は例）
```
nodebrew install-binary v16.14.0
```

## バージョン切り替え
```
nodebrew use v16.14.0
```

## 終わり（最後確認）
```
node -v
```
MySQL Docker コンテナに、実行時でなくビルド時に初期データ入力をしたい

# 結論
MySQL docker コンテナを、テスト用途等で同じ初期データで何度も起動する場合、
初期データを実行時でなくビルド時に反映する方が起動が高速になります。

[このWebページ](https://renehernandez.io/tutorials/faster-deployments-mysql-databases-k8s/) の前半に方法が示されています。
公式イメージの docker-entrypoint.sh の一部を sed で置き換える素朴な方法です。
```Dockerfile
FROM mysql as builder
COPY (初期化用の.sqlファイル等) /docker-entrypoint-initdb.d/
# ここでスクリプトの特定の行をおきかえ
RUN ["sed", "-i", "s/exec \"$@\"/echo \"not running $@\"/", "/usr/local/bin/docker-entrypoint.sh"]
# 置き換え済みのスクリプトを呼び出し、初期データを反映
# 本家では一旦--data-dirを別に指定していますが、しなくても動きます
RUN ["/usr/local/bin/docker-entrypoint.sh", "mysqld"]

FROM mysql
COPY --from=builder /var/lib/mysql /var/lib/mysql
```
置き換え対象になっているスクリプトファイルは Github の[ここ](https://github.com/docker-library/mysql/blob/master/8.0/docker-entrypoint.sh)にあります（8.0系）。
この行が変更された場合、想定通りに動作しない可能性があります。
tmpfsを設定してメモリ上にデータベースを構築する方法と同時に使用し、更に高速にすることも出来そうです。

# 背景
MySQL の docker コンテナを、テスト用に毎回 docker compose down (docker container rm ...) して使っています。
ログを見ていると、コンテナを削除せず使い回す場合とは起動時の挙動が異なることに気付きました。
- 毎回コンテナを削除（毎回初期データ入力）：
  1. Temporary server なるものが起動
  2. /docker-entrypoint-initdb.d に入れたスクリプト類の反映
  3. Temporary server のシャットダウン
  4. 通常のmysqlサーバが起動

- コンテナ削除無し（既にデータベース関連のファイル有り）：
  1. 通常のmysqlサーバが起動

コンテナを削除しない場合の方が圧倒的に起動が早いです。
また、コンテナを毎回削除するか否かは本当は重要ではなく、/var/lib/mysql にきちんと初期化されたデータベース関連のファイルが既に存在するかの差だと思われます。
（volume 設定を行い永続化した MySQL Docker コンテナも後者と同じ挙動をするので）

前者の初期化を同じデータに対して毎回行うのは無駄ではないかと考え始めました。テスト環境を docker 化しているので、テストの度に10秒程度の時間が無駄に消費されるのは避けたいです。

結果、上記の方法にたどり着きました。

# 余談
docker-compose 設定には healthcheck という項目が有り、適切に設定すれば「コンテナ内の目的のプログラムが応答可能になるまで別のコンテナの起動を待機」することが出来ます。

ただしMySQLコンテナは初期化時にTemporary serverが起動する時間が10秒程度あるので、healthcheck の test コマンドの指定次第では Temporary server の起動を検出してしまい、思ったように動作しないという問題が起こります。
<details>
  <summary><font color=gray>参考...</font></summary>
  <a href="https://github.com/docker-library/mysql/issues/930">
    https://github.com/docker-library/mysql/issues/930
  </a>
  <a>
  <br/>
  <a href="https://stackoverflow.com/questions/42567475/docker-compose-check-if-mysql-connection-is-ready">
    https://stackoverflow.com/questions/42567475/docker-compose-check-if-mysql-connection-is-ready
  </a>
  <br/>
  <a href="https://genzouw.com/entry/2023/03/23/083027/3325/">
    https://genzouw.com/entry/2023/03/23/083027/3325/
  </a>
  <br/>
  ↑最後の例の様に、ユーザ名とパスワードとデータベース指定し、データを引っ張ってくるコマンドが成功するか否か判断するのが最も確実そうです。<br/>
（極端な話、select 1; でも有効なはずです）<br/>
他の例も試しましたが、Temporary server の起動だけで誤検出している場合が有りました。
</details>

ここで紹介する方法では Temporary server はコンテナ起動時には動作しませんので、この対策をもう少しシンプルに出来るかもしれません。


それでは皆様、良い Docker ライフをお送り下さい。