OmniAICreator/Qiita-1.07M huggingface dataset:
【備忘録】ファクトリ関数が役に立った事例

# ファクトリ関数の解釈

https://qiita.com/koichi2426/items/d7ad1bdd33030b6e9767


# 役に立った事例：UUIDの生成とテスト対応

### 背景

以下はドメイン層における `uuid.go` のコードである。

```go
// domain/uuid.go
package domain

// UUIDGenerator は UUID を生成するインターフェース
type UUIDGenerator interface {
    NewUUID() (UUID, error)
}

// UUID は UUID を表す値オブジェクト
type UUID struct {
    value string
}

// Value は UUID の値を返す
func (u UUID) Value() string {
    return u.value
}
```

UUID の生成方法が外部に依存しないよう、`UUIDGenerator` はインターフェースとして定義している。  
インフラ層やテストコードでは、このインターフェースを実装する必要がある。

### 問題点

テストコード内で以下のように `UUIDGenerator` をモック実装した。

```go
// モックUUIDGenerator実装
type mockUUIDGenerator struct{}

func (mockUUIDGenerator) NewUUID() (domain.UUID, error) {
    // テスト用の固定UUIDを返す
    return domain.UUID{value: "550e8400-e29b-41d4-a716-446655440000"}, nil
}
```

しかし、以下のようなエラーが発生した。

```
usecase/create_account_test.go:40:24: cannot refer to unexported field value in struct literal of type domain.UUID
```

これは Go 言語のカプセル化（非公開フィールド）のルールにより、外部から `value` フィールドを直接参照できないことが原因である。  
値オブジェクトが「生成時の一貫性保証」という特性を持つ以上、これは自然な振る舞いであるとも言える。

だがこのままでは、ドメイン外から `UUID` を生成できず、インターフェースの実装が不可能になる。


### 解決策：ファクトリ関数の導入

この問題は、ファクトリ関数を用いることで解決できる。

 修正後の `uuid.go`

```go
package domain

import "errors"

var (
    ErrInvalidUUID = errors.New("invalid UUID format")
)

// UUIDGenerator は UUID を生成するインターフェース
type UUIDGenerator interface {
    NewUUID() (UUID, error)
}

// UUID は UUID を表す値オブジェクト
type UUID struct {
    value string
}

// Value は UUID の値を返す
func (u UUID) Value() string {
    return u.value
}

// ファクトリ関数：バリデーション付きで UUID を生成する
func NewUUID(value string) (UUID, error) {
    if !isValidUUID(value) {
        return UUID{}, ErrInvalidUUID
    }
    return UUID{value: value}, nil
}

// プライベートなバリデーション関数（簡易的なUUIDv4形式チェック）
func isValidUUID(value string) bool {
    return len(value) == 36 && 
           value[8] == '-' && 
           value[13] == '-' && 
           value[18] == '-' && 
           value[23] == '-'
}
```

※ `UUIDGenerator` のメソッドとファクトリ関数が同名であるが、それぞれのスコープが異なるため問題はない。

### テストコードの修正

テストコードでは、ファクトリ関数を通じて UUID を生成するように変更する。

```go
// モックUUIDGenerator実装
type mockUUIDGenerator struct{}

func (mockUUIDGenerator) NewUUID() (domain.UUID, error) {
    return domain.NewUUID("550e8400-e29b-41d4-a716-446655440000")
}
```


# まとめ

ドメイン層の構造体にカプセル化された非公開フィールドがある場合、外部から直接生成することができない。  
そのような状況でも、ファクトリ関数を導入することで、生成ロジックの一貫性を保ちつつ、外部からのインスタンス生成が可能になる。

特にテストやモックの場面で、値オブジェクトの生成に制限がある場合には、ファクトリ関数が非常に有効な手段となる。
テスト電子レンジ

=IF(LEN(A1)-LEN(SUBSTITUTE(A1,"/",""))=1, LEFT(A1, FIND("/", A1)-1), LEFT(A1, FIND("/", A1, FIND("/", A1, LEN(SUBSTITUTE(A1, "/", "", INT((LEN(A1) - LEN(SUBSTITUTE(A1, "/", ""))) / 2) + 1)))) - 1))
WSLでLinuxからWindowsのファイルにアクセスする方法

## 前提条件

WSL から Windows のファイルにアクセスするには、以下の条件を満たしている必要があります。

- Windows 10 バージョン 1903 以降、または Windows 11 であること
- WSL2 がインストールされていること
- 必要に応じて、Linux ディストリビューション（Ubuntu など）がインストールされていること
- Windows ファイルシステム（C: ドライブなど）が WSL にマウントされていること

## WSL から Windows のファイルにアクセスする

### やり方
Linux のターミナルを開き、以下のコマンドを実行します。

```bash
cd /mnt/c
```
これでC:ドライブの中身を Linux 上で操作できます。例えば、デスクトップに移動したい場合は次のように入力します。

:::note warn
OneDriveで同期していない場合
:::

```bash
cd /mnt/c/Users/ユーザー名/Desktop
```
venvによる環境構築（mac）

macにはデフォルトでpython3がインストールされています。
そのため、仮想環境を用意してバージョン管理する必要があるのでその手順を備忘録として残します。

---

# バージョンの確認
1. バージョン確認
```bash
# ターミナルを立ち上げてバージョンを確認する
$ python3 -V
Python 3.9.6
```

ちなみにですが...(生成AIによると)

システムに Python 3.9.6 が表示される理由はおそらく、macOS に標準でインストールされている Python バージョンがそれだからです。Homebrew で Python 3.12 と 3.13 をインストールしていますが、システムの PATH 設定で macOS の標準 Python が優先されています。

---

PATHを指定してpython3.9.6以外のバージョンを使用することも可能なようですが、仮想環境を作成してバージョン管理した方がいいと私は判断ました。

2. brew環境のバージョンを確認
```bash
$ brew list | grep python
python@3.12
python@3.13
```

私の環境では、標準環境に3.9.6がインストールされており、brew環境には3.12と3.13がインストールされています。

---

# 指定したバージョンを仮想環境内で使う

1. まずbrew環境にpython3.10をインストールする
```bash
brew update
brew install python@3.10
```

2. バージョンの確認
```bash
$ brew list | grep python
python@3.10
python@3.12
python@3.13
```

3. 仮想環境を作成する
```bash
mkdir test
cd test
```

```bash
$ python3.10 -m venv venv
cd venv
```
4. 仮想環境を有効にする
```bash
$ source bin/activate
(venv)
```

5. バージョンの確認
```bash
$ python -V
Python 3.10.16
(venv)
```
TeraTermコマンド一覧

## pwd
現在いる位置を表示

## cd 
ディレクトリに移動

## ls
ディレクトリの中身を表示
```ruby:オプション
-a : ピリオド始まりのファイルやフォルダを表示
-l : ファイルの詳細を表示
```

## mv
ファイル（またはディレクトリ）を移動・ファイル（ディレクトリ）の名称変更
```ruby:構文
# mv 元のファイル（ディレクトリ） 変更後のファイル（ディレクトリ）名
```

```ruby:オプション
-b : 上書き・削除されるファイルのバックアップ
-f : 上書きの場合確認メッセージを表示させない
-i : 上書きされる場合は確認メッセージを表示する
-n : 移動先に同じ名称のファイル・ディレクトリが存在する場合は移動しない
-v : 詳細を表示する
```

## mkdir
ディレクトリ（フォルダ）を作成する
```ruby:構文
# mkdir [オプション] ディレクトリ名
```
```ruby:オプション
-m : 作成するディレクトリのパーミッションを設定
-p : 必要に応じて親ディレクトリも作成
-v : 経過を表示
```

## cat
ファイルの内容を表示

## tail(Linux) 
ファイルの最終行から数行を表示する
```ruby:オプション
-f : ファイルの追記を監視する
```

## sudo(Linux)
スーパーユーザ（=root）としてsudoに続くコマンドを実行する。
その瞬間だけroot権限になってコマンドを実行する。
```ruby:使用例
sudo su -
```
【概念理解】ファクトリ：ソフトウェア開発における「工場」



## **1. ファクトリ関数＝ソフトウェアの工場**
ファクトリ関数はまさにプログラミング世界の「工場」です。その特徴を現実の工場と比較しながら解説します。

### **工場の特徴と対応関係**
| 現実の工場 | ファクトリ関数 |
|------------|----------------|
| 原材料を受け取る | パラメータを受け取る |
| 製造工程がある | オブジェクト生成ロジック |
| 品質検査を行う | バリデーション処理 |
| 完成品を出荷 | オブジェクトを返却 |
| 生産ラインを隠蔽 | 実装詳細をカプセル化 |

## **2. ファクトリ関数の3大機能**
### **① 生産機能（オブジェクト生成）**
```go
// 自動車工場
func CreateCar(maker string, model string) *Car {
    return &Car{
        Maker: maker,
        Model: model,
    }
}
```
- 原材料（パラメータ）から製品（オブジェクト）を生産

### **② 加工機能（データ変換）**
```go
// 車体ナンバー加工付き工場
func CreateCarWithSerial(maker string) (*Car, error) {
    serial, err := generateSerial() // 加工工程
    if err != nil {
        return nil, err
    }
    
    return &Car{
        Maker:  maker,
        Serial: serial, // 加工済みデータ
    }, nil
}
```

### **③ 品質管理（バリデーション）**
```go
// 品質検査付き工場
func CreateValidCar(maker string, year int) (*Car, error) {
    if year < 1900 {
        return nil, errors.New("製造年が古すぎます")
    }
    if len(maker) == 0 {
        return nil, errors.New("メーカー名必須")
    }
    
    return &Car{
        Maker: maker,
        Year:  year,
    }, nil
}
```

## **3. 優れた工場（ファクトリ）の設計ポイント**
### **ポイント1：統一されたインターフェース**
```go
// 良い例：常に*Carとerrorを返す
func CreateCar(...) (*Car, error)

// 悪い例：場合によって返り値が異なる
func CreateCar(...) (*Car, error, string)
```

### **ポイント2：生産工程の隠蔽**
```go
// 内部実装は隠す（例：シリアル番号生成）
func generateSerial() string {
    return fmt.Sprintf("CAR-%d", time.Now().UnixNano())
}
```

### **ポイント3：拡張可能な設計**
```go
// 車種ごとの工場
func CreateSedan() *Car { ... }
func CreateSUV() *Car { ... }
```

## **4. 実際の工場（ファクトリ）運用例**
### **ケース1：DB接続工場**
```go
func CreateDBConnection(config Config) (*sql.DB, error) {
    connStr := fmt.Sprintf(
        "host=%s port=%d user=%s password=%s",
        config.Host, config.Port, config.User, config.Pass,
    )
    
    db, err := sql.Open("postgres", connStr)
    if err != nil {
        return nil, fmt.Errorf("接続失敗: %w", err)
    }
    
    if err := db.Ping(); err != nil {
        return nil, fmt.Errorf("接続確認失敗: %w", err)
    }
    
    return db, nil
}
```

### **ケース2：設定ファイル工場**
```go
func LoadConfig(path string) (*Config, error) {
    raw, err := os.ReadFile(path)
    if err != nil {
        return nil, fmt.Errorf("設定ファイル読み込み失敗: %w", err)
    }
    
    var config Config
    if err := yaml.Unmarshal(raw, &config); err != nil {
        return nil, fmt.Errorf("設定解析失敗: %w", err)
    }
    
    if err := config.Validate(); err != nil {
        return nil, fmt.Errorf("設定検証失敗: %w", err)
    }
    
    return &config, nil
}
```

## **5. 工場長（開発者）へのアドバイス**
1. **単一責任原則**  
   「1つの工場では1種類の製品だけを作る」

2. **不良品対策**  
   「必ずバリデーション工程を設ける」

3. **生産ラインの最適化**  
   「複雑な生成処理はサブ関数に分割」

4. **工場の安全性**  
   「非公開フィールドで内部状態を保護」

5. **工場の拡張性**  
   「インターフェースを活用して差し替え可能に」

> **🎯 究極の目標**  
> 「クライアントコードが『どう作られるか』を気にせず、  
> 　ただ『何が欲しいか』だけを伝えられる工場を作る」
【Android】【iOS】AndroidからiOSの開発をするようになって困惑した件：開発環境編

## Android開発 -> iOS開発へ
Androidの開発に携わっていましたが、この度iOSの開発へ異動することに。
開発環境がAndroid studioからxcodeへと移ることになったわけですが、その使い勝手の違いに戸惑うばかり......。
本記事ではAndroid studioからxcodeで開発するようになって煩わしさを感じた点を何となくまとめてます。

## Case 1: このメソッドってどうやって使ったっけ？
開発を進めていると「あれ、このメソッドの引数って何が渡せたっけ......？」となることが多々あります。
Android studioでは対象のメソッドにカーソルを合わせるとヒントを出してくれます。
これに結構頼って開発を行ってました。

![スクリーンショット 2025-03-27 15.16.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3887743/189dbfac-6594-41e0-82ed-bec4b8c77ece.png)


一方Xcodeではカーソルを合わせてもヒントを出してくれず、option + クリックでようやく出してくれます。

![スクリーンショット 2025-03-27 15.14.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3887743/4cc690b6-6af6-47cf-bdd0-c5a26625414e.png)
*option + クリックでようやく出してくれる*

ワンクッション置かないと表示できなくて煩わしいな.....と思っていたんですが、しばらくしたのちにどうやらそんなことをしなくてもいいことを知ります。

Xcodeの右側のエリア（"ユーティリティエリア"と呼ぶらしい）の？マークをクリックすると、カーソルを合わせたメソッドのヘルプが出るように。
これでわざわざoption + クリックしなくてもよくなりました。

![スクリーンショット 2025-03-27 15.15.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3887743/b436b66d-4c9e-4138-a34a-44a7e6f9f85f.png)

## Case 2: Git操作
Android StudioではGitの操作ができるので、comittやpushをはじめ、ブランチの移動やターミナルの操作もAndroid Studioで完結させることができます。
これに慣れてしまっていましたが、XcodeではGitの操作ができないのでXcodeとは別にForkを導入してGitの操作を行うことにしました。

Androidの開発をしている時に、「このコードはいつ改変されたのだろうか」「どのような意図でこのようなコードになっているのだろうか」と疑問に思った時、そのファイルを右クリックして Git > Show History でそのファイルに行われた変更の一覧を見ることができます。

![スクリーンショット 2025-03-27 15.40.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3887743/292f632c-5f25-4856-b167-d4453754e2e0.png)
*昔研修で作ったコード（懐かしい）*

Xcodeでは直接確認できないので、Forkで確認する必要があります。
ForkのFile Tree から対象のファイルを選択して右クリックして、メニューから"History..."を選択しすると、過去の変更の一覧を見ることができます。
本当はXcodeで開いたファイルから直接参照できると楽で良いのですが......。

![スクリーンショット 2025-03-27 15.50.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3887743/f8031e77-a39e-414a-ba1b-486382e63c65.png)
![スクリーンショット 2025-03-27 15.50.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3887743/915a96b0-9f6e-4b71-8776-d87ec42049e8.png)
*注：Gitのアカウント名を黒塗りしてます*

## Case3: ショートカットキーが微妙に違う
ファイルの検索はcommanf + O, コードの検索はcommand + F など、似通っている部分もあるんですが、微妙に違ったりするものも。
頻繁に使うものでは、コードのフォーマットをかける際のショートカットキーが、Android studioでは command + option + L でできるんですが、Xcodeだと control + shift + I がフォーマットのショートカットキーになっており、非常に紛らわしいです。
Androidの開発とiOSの開発を行ったり来たりしているとごっちゃになってよく間違えます......。
このあたり統一できたらいいなと思っています。

## 開発環境の移行は慣れない
しばらくはiOSでの開発がメインとなるので、Xcodeに慣れていきたいものですが、なかなかAndroid studioでの癖は治らず......。
他にも色々違うところはあるので、気が向いた時に追記できればと思っています。
【iOS】NavigationStack周りを整理してみた

## NavigationStack周りがよくわからなかった件
この2年間ほどAndroidエンジニアとして仕事してきましたが、急な異動（？）によりAndroidからiOSエンジニアになることに......。
とりあえずSwift, SwiftUiから勉強し始めましたが、その際にNavigationStackの使い方がいまいち整理つかなかったのでここで整理しようかなと思います。
（Androidエンジニアから転身したばかりですので、あまり詳しいことは書けない/抽象的な表銀が多くなりそうですが......）

## そもそもNavigationStackとは？
NavigationStackはiOS16以降で使用できるようになった、画面遷移用のコンポーネント。
画面遷移はこれでできますよ、という代物。

## NavigationStack 基本系
```Swift
import SwiftUI

struct ScreenA: View {
    var body: some View {
        NavigationStack {
            NavigationLink("Go to ScreenB") {
                ScreenB()
            }
        }
    }
}

struct ScreenB: View {
    var body: some View {
        Text("Screen B")
    }
}

```

NavigationStackを宣言した後に、NavigationLinkを置き、中に遷移先のviewを配置します。
NavigationLinkにStringを渡していると、この文字列をタップした時に画面遷移が行われるようになります。
便利ですね。

<p align="center">
  <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3887743/934b5ca8-edc1-4cb0-9910-f46742fea0b2.gif" />
</p>

## 変化形
基本形を少し変えて応用してみます。
例えばリストを表示して、選択したコンテンツに応じて次の画面で表示するものを変化させたい時は以下の様にします。

```Swift
struct ScreenA: View {
    var body: some View {
        NavigationStack {
            List(["Apple", "Banana", "Cherry"], id: \.self) { item in
                NavigationLink(value: item) {
                    ViewContentsA(text: item)
                }
            }
            .navigationDestination(for: String.self) { item in
                ScreenB(text: item)
            }
        }
    }
}

struct ScreenB: View {
    var text: String
    var body: some View {
        Text(text)
    }
}

struct ViewContentsA: View {
    var text: String
    var body: some View {
        VStack {
            Text(text)
        }
        .padding()
        .border(Color.black, width: 2)
    }
}
```

NavagationStackにはListをおき、Listのラムダの中でNavigationLinkを置きます。
NavigationStackの中に遷移元の画面を置くようなイメージですね。
この時、NavigationLinkには引数valueを渡します。
NavigationLinkのラムダにはリストとして表示したいviewを設置します。

そしてListに`.navigationDestination(for:)`を設定します。
`.navigationDestination(for:)`を使うことで、先ほどはNavigationLinkに直接渡していた遷移先のviewを定義することができます。
`.navigationDestination`の引数forとNavigationLinkの引数valueの型は一致させてください。
画面遷移ができなくなります。

<p align="center">
  <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3887743/d51ea2ee-abee-46fd-a454-b17eb8233b89.gif" />
</p>

あと、`.navigationTitle()` を使うことでタイトルをつけることもできます。
呼び出しもとにつけておけば遷移先のViewにも表示されます。

```Swift
 NavigationStack {
            List(["Apple", "Banana", "Cherry"], id: \.self) { item in
                NavigationLink(value: item) {
                    ViewContentsA(text: item)
                }
            }
            .navigationDestination(for: String.self) { item in
                ScreenB(text: item)
            }
            .navigationTitle("Fruits")
        }
```

## ざっくりまとめ
- 画面遷移ではNavigationStackとNavigationLinkを使う
- 最初にNavigationStackを宣言、NavigationStackの中には画面遷移元のViewを配置するイメージ
- NavigationLinkで画面遷移のためにタップするUI（ボタンなど）を定義する
- .navigationDestinationで遷移先のviewを紐づける

正直最初はこのあたりがごちゃごちゃになってました、、、、、、
実はもう少し凝った使い方もできるようなのですが、追々試していければなと思っています。
【Docker】コンテナの基礎を理解する

# 1. Dockerとは
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3562533/1a6f82c1-c20b-4bd9-8893-e876e4786bba.png)

Dockerは、コンテナ技術を利用してアプリケーションとその依存関係を1つのパッケージにまとめるツールです。

* **コンテナの特徴**:
    * 軽量で迅速に起動
    * OSや環境に依存せず動作
* **主な用途**:
    * 開発環境の再現
    * 本番環境へのデプロイ
* **仮想マシンとの違い**: コンテナはOSを共有するため、リソース消費が少ない。

# 2. Dockerの基本用語
以下はDockerを理解する上で重要な用語です。

* **イメージ**: アプリケーションやライブラリをまとめたテンプレート。
* **コンテナ**: イメージを実行可能なインスタンスにしたもの。
* **Dockerfile**: イメージを作成するための設定ファイル。
* **Docker Hub**: 公開イメージを共有するリポジトリ。
  


# 3. 環境準備
Dockerを使用するには、まずインストールが必要です。

* **インストール手順**:
    1. [公式サイト](https://www.docker.com)からDocker Desktop（Windows/Mac）またはDocker Engine（Linux）をダウンロード。
    2. インストール後、以下のコマンドで動作を確認します。
        ```text
        docker --version
        ```

* **動作確認**: 以下のコマンドでサンプルコンテナを起動します。
```text
docker run hello-world
```
※ 「Hello from Docker!」と表示されれば成功。


# 4. 基本操作の実践
Dockerの基本的なコマンドを紹介します。

### イメージの取得
Docker Hubから既存のイメージを取得します。

```text
docker pull ubuntu
```
※ Ubuntuの公式イメージをダウンロード。

### コンテナの起動
取得したイメージからコンテナを起動します。

```text
docker run -it ubuntu
```
* `-i`: インタラクティブモード
* `-t`: ターミナル割り当て ※ コンテナ内でUbuntuのシェルが利用可能。

### コンテナの確認
実行中のコンテナを確認します。

```text
docker ps
```

停止したコンテナも含めて表示するには:
```text
docker ps -a
```

### コンテナの停止と削除
コンテナを停止するには:

```text
docker stop <コンテナID>
```

削除するには:
```text
docker rm <コンテナID>
```
※ `<コンテナID>`は`docker ps -a`で確認。

# 5. 簡単なDockerfileの作成
独自のイメージを作成します。

* **手順**:
    1. 新しいディレクトリを作成し、`Dockerfile`を以下のように記述。
    ```text
    FROM ubuntu:latest
    RUN apt-get update && apt-get install -y curl
    CMD ["echo", "Hello, Docker"]
    ```
    2. イメージをビルド。
    ```text
    docker build -t my-ubuntu .
    ```
    3. コンテナを起動。
    ```text
    docker run my-ubuntu
    ```
    ※ 「Hello, Docker」と表示される。

:::note warn
**注意点**
コンテナは一時的なものと認識し、永続データはボリュームやバインドマウントで管理します。
イメージサイズを小さく保つため、不要なファイルを削除する習慣が推奨されます。
:::

---
### その他資料
* [公式ドキュメント](https://docs.docker.jp/)
* [ゼロから始めるDocker入門](https://qiita.com/suzu12/items/315fba934de11af826bd)
* [Dockerチュートリアルを日本語で説明（第2章）](https://moritomo7315.hatenablog.com/entry/2019/01/18/105849)
* [Dockerを体系的に学べる公式チュートリアル和訳](https://qiita.com/Michinosuke/items/5778e0d9e9c04038903c)
* [実践 Docker - ソフトウェアエンジニアの「Docker よくわからない」を終わりにする本](https://zenn.dev/suzuki_hoge/books/2022-03-docker-practice-8ae36c33424b59)
KotlinでMutableListの操作

# はじめに

仕事でKotlinが必要になるのでを勉強している。
今回はリストの操作についてまとめる。

# リストの操作

## 宣言

```kotlin
// 要素が空のリストを作成  
val emptyList = mutableListOf<String>()  
  
// 要素を指定して作成  
val list = mutableListOf("a", "b", "c")
```

`mutableList` は要素の追加、削除、変更ができるリスト。
一方、ノーマルな `List` は読み取り専用。

## 要素を取得する

```kotlin
val list = mutableListOf("a", "b", "c")

list.get(0) // => a  
list[1]    // => b
```
二通りの取得方法があるようだ。
使い分けは？好み？

## 要素を追加する

```kotlin
val list = mutableListOf("a", "b", "c")

list.add("d")  
println(list) // => [a, b, c, d]
```

イミュータブルな `List` では不可。

## 要素を変更する
イミュータブルな `List` では不可。
### 要素を指定して変更する

```kotlin
list[0] = "A"  
list.set(1, "B")  
println(list) // => [A, B, c, d]
```

### 全要素を同じ値で変更する

```kotlin
list.fill("x")  
println(list) // => [x, x, x]
```

### 全要素を変更する

```kotlin
val intList = mutableListOf(1, 2, 3, 4)  
intList.replaceAll { it * 2 }  
println(intList) // => [2, 4, 6, 8]
```

## 要素を削除する

```kotlin
list.removeAt(0)  
println(list) // => [B, c, d]
```

イミュータブルな `List` では不可。

# おわりに

リストのネスト等、Javaと結構違うなと感じる部分も多いのでそれらはもう少し腹落ちしてからまとめたい。
認証付きRTSPストリームのヘルスチェック(その２)

# はじめに

前回、以下の記事

https://qiita.com/SECUAL_masa/items/48ba4a6bf4344532bd7e

を書き、またその記事中の

> 他の方法で対応(その2)

で記載したコードを組み込み評価を進めてきた。

ところがとあるIPカメラにて、カメラのパスワードとシステム側のパスワードが不一致だが、RTSP DESCRIBEで稀に200 OKが返り、システム側はそのカメラが生きていると判断しているケースに遭遇した。
またそのIPカメラにVLCで視聴を試みると確実に401となり、ID/PASS入力ダイアログを表示していた。

この違いは何か。改めて前回のサンプルとVLCを動かした時のパケットをキャプチャし比較してみた。

# 比較結果

比較した結果、違いがあったのは以下の3点。

* CSeqの振り方
* UserAgent
* OPTIONSの有無

## CSeqの振り方

サンプルでは1からにしているが、VLCでは2からになっていた。でもたかがシーケンス番号なのでね、、、。VLCと同じにしたところで変化はなさそうである。

## UserAgent

たかが文字列なのでね、、、。VLCと同じにしたところで変化はなさそうである。というか同じにするのもイマイチである。

## OPTIONSの有無

前回の記事で書いたサンプル通り、サンプルではDESCRIBEをリクエストしているが、VLCではOPTIONS → DESCRIBEとなっていた。

正しい。確かにOPTIONSで何を利用できるかを判断してからDESCRIBEをするのは。

が、ほぼ確実に利用可能な(と思っている)DESCRIBEを最初から利用しても問題ないんじゃね？？？、とも思うが他の違いと比べると一番改善・回避できる可能性が高そうである。

# 結果

OPTIONSの有無では変化がなかった。

え、じゃぁCSeq？と試してみた。OPTIONSのリクエストは残しつつ、CSeqもVLCと同じで2からに。
となるともうほぼVLCと手順は同じである。しかし！変化はなかった。え？。え？？

UserAgent？？？。たかが文字列じゃないの？？？。
と、まずはVLCのUserAgentをそのまま記載してみる。え？。え？？

ばっちり。今のところ200 OKになっちゃうことはない。

```Text: 変更前のUserAgent
User-Agent: rtspclient/1.0.0
```

```Text: 変更後のUserAgent
User-Agent: LibVLC/3.0.16 (LIVE555 Streaming Media v2016.11.28)
```
※バージョンは若干古い。経緯については後述する。

curlも変更前の形式と同じである。

```Text: curlのUserAgent
User-Agent: curl/7.81.0
```

# UserAgentの書き方

調べてみるとMDN Web Docsにたどり着く。

https://developer.mozilla.org/ja/docs/Web/HTTP/Reference/Headers/User-Agent

```Text: 構文
User-Agent: <product> / <product-version> <comment>
```

となっている。また、よくある書式として、以下のようにも書かれてある。

```Text: よくある書式
User-Agent: Mozilla/5.0 (<system-information>) <platform> (<platform-details>) <extensions>
```

ということは、

```
<product> / <product-version>
```

は

```
Mozilla/5.0
```

で、

```
<comment>
```

は

```
(<system-information>) <platform> (<platform-details>) <extensions>
```

あたりか。

いずれにしろ、このIPカメラは```<comment>```部分の文字列または書式によって想定外の挙動をする問題が潜んでいそうだ。(最悪、認証を回避できたり？？？）

ひとまず

```
User-Agent: rtspclient/1.0.0 (RTSP Streaming Meida v1.0.0)
```

にして様子をみることにする。


# おわりに

## IPカメラのCGI呼び出しでも発生

この記事ではRTSP DESCRIBEの話に焦点を当てているが、システム側から呼び出すCGIも稀に200 OKになることが判明した。

```Text: CGI呼び出しログの抜粋
・
・
・
get : options.url = http://nnn.nnn.nnn.nnn/sd/
dumpResponse : url = http://nnn.nnn.nnn.nnn/sd/ , statusCode = 401
get : options.url = http://nnn.nnn.nnn.nnn/sd/
dumpResponse : url = http://nnn.nnn.nnn.nnn/sd/ , statusCode = 200
get : options.url = http://nnn.nnn.nnn.nnn/sd/20250326/
dumpResponse : url = http://nnn.nnn.nnn.nnn/sd/20250326/ , statusCode = 401
get : options.url = http://nnn.nnn.nnn.nnn/sd/20250326/
dumpResponse : url = http://nnn.nnn.nnn.nnn/sd/20250326/ , statusCode = 401
・
・
・
```

このIPカメラは今回とは逆の事象(通常は200だが稀に401を返す)も発生することがあり、ひとまず200以外になってもリトライするようにしている。上記のログは401が返り、リトライしたら200が返って時のものである。(以降はまた401が返り始め、再度200になることはなかった)

この辺りもUserAgentを変更すると改善するのかもしれない。

## VLCのバージョンが古い

経緯については以下にまとめている。

https://qiita.com/SECUAL_masa/items/a79b994b5088178fdd6c

## curlのUserAgent

RTSP DESCRIBEをcurlで試す話を書いているので良ければ。

https://qiita.com/SECUAL_masa/items/8cbcddbd0c61894f70fd
セレクトボックス内でカテゴリを分ける方法

# やりたいこと
↓ 画像のように、セレクトボックス内でカテゴリを分ける
![スクリーンショット 2025-03-27 9.52.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3726068/426c29b7-bfed-4782-a37d-6cb45877e9be.png)

# optgroupでセレクトボックス内でカテゴリを分ける
```blade.php
<select name="technology_tag_ids[]" id="tech_type" multiple class="rounded-md js-multiple-tag-select">
    @if(!$technologyTags->isEmpty())
        ⭐️@foreach($technologyTags->typeLabels as $type => $label)
            ⭐️<optgroup label="▼ {{ $label }}">{{-- セレクトボックス内でカテゴリを分ける --}}
                @foreach($technologyTags->where('tech_type', $type) as $technologyTag){{-- tech_typeカラムの値が$typeと一致するレコードだけを絞り込み --}}
                    <option value="{{ $technologyTag->id }}">{{ $technologyTag->name }}</option>
                @endforeach
            </optgroup>
        @endforeach
    @endif
</select>
```

## ②上記の⭐️$technologyTags->typeLabelsの部分を作成
```CollectionController.php
public function create()
{
    // 🔹 ログインユーザーの技術タグをtech_type昇順で取得してadmin.collections.createに渡す処理
    $technologyTags = TagService::getTechnologyTagsSorted();

    // 🔹 技術タグのセレクトボックス内テーマ
    $technologyTags->typeLabels = TagService::appendTypeLabelsToTechnologyTags();

    return view('admin.collections.create', compact('technologyTags'));
}
```

```TagService.php
// 🔹 ログインユーザーの技術タグをtech_type昇順で取得してadmin.collections.createに渡す処理
public static function getTechnologyTagsSorted() {
  $technologyTags = Auth::user()
      ->technologyTags()
      ->orderBy('tech_type', 'asc')
      ->get();

  return $technologyTags;
}

// 🔹 技術タグのセレクトボックス内テーマ
public static function appendTypeLabelsToTechnologyTags() {
  return [
      0 => '言語',
      1 => 'フレームワーク',
      2 => 'ツール',
  ];
}

```
LiON 生成AI活用分科会にてプレキックオフミーティングを実施

![20250325_ai_mtg.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3810512/088e179b-70fb-4e1e-b46e-869f4ae23804.png)


## はじめに
こんにちは、エンジニアコミュニティ LinuC Open Network （LiON）の運営事務局の伊藤です！

LiONの生成AI活用分科会にて、3/25にプレキックオフミーティングを開催しましたので、その内容をご報告します。

## ミーティング概要
### 主題：分科会の初期の活動方向性について意見交換する

ミーティングの流れ：
1. 参加者の自己紹介
1. 生成AI活用分科会の概要説明
1. ディスカッション

LiONコミュニティ内で呼びかけ、生成AI活用の分科会活動に関心ある方で集まり、分科会の初期の活動について意見交換を行いました。zoomで開催し、まずはお互いの自己紹介からスタート。そして分科会の概要について簡単な説明があった後に、それぞれどのような活動を行いたいか、またエンジニアの成長と活躍を促すという目的を踏まえた上でどのような活動が有益かという観点で議論しました。

### 結果

議論の結果として、今後4ヶ月間で下記の活動に取り組むこととなりました。

### 「生成AI感動体験ワークショップ」の企画・開催

企画概要：
生成AIを活用したシステム開発を体験するワークショップを企画・開催し、そのインパクトの大きさに感動を味わってもらう。

意図：
これからのエンジニアにとって、生成AIの活用は必須スキルとなるが、まだまだキャッチアップできていない方が多い。一度体験してその感動を味わうことで、生成AI活用に対し大きなモチベーションにつながるだろうと期待。

スケジュール感：
4〜5月：ワークショップ内容の検討と準備
6月　：イベント告知
7月　：実施と振り返り

## まとめ
分科会メンバーとしてはまだまだ少人数ではありますが、ミーティング自体も大変盛り上がり、当初の活動「生成AI感動体験ワークショップ」というアイデアに対しても、期待感を共有しています。

確かに、生成AIのようにアップデートの激しい技術は、日々のキャッチアップや、さらにそれを自身の業務に取り入れ続けるのがなかなかパワーのいる作業になります。好奇心旺盛な方にとっては、それが楽しくて仕方がないようですが（笑）
とはいえ、生成AI活用のインパクトは、一度体験してみないとその衝撃や影響の大きさを実感できません。ですので、「感動を体験してもらう」をコンセプトに、生成AIを活用したシステム開発のワークショップを実施するのは非常に意義のあることであると思います。

早速4月から、追加のメンバーを募集しつつ、ワークショップの企画を詰めていく予定です。

## 分科会メンバー募集！
生成AI活用分科会で一緒にアウトプット共創に取り組んでいただける方を募集しています。
ぜひ、エンジニアコミュニティ LinuC Open Networkにご参加ください！

https://linuc.community/
vProセットアップ


## Intel Management Engine BIOS Extension を起動
起動時の DELL ロゴ 表示前に F12 を押して メニューを表示させ BIOS の設定画面に遷移
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212992/64a0a001-d6c9-45c8-8fab-3bee4c47e2bb.png" width=50%>
BIOS SETUP – System Management – 
Intel AMT Capability を `[Enabled]` に変更
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212992/1278ec43-6c12-49f7-908b-fbe3d0ee4245.png" width=50%>
BIOS SETUP – Passwords – 
 admin のパスワード入力後にエンターキーを押下
 confirm 表示後に再度パスワード入力後にエンターキーを押下
`[APPLY CHANGES]` クリックして設定を反映する
`[EXIT]` クリックしてで再起動する
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212992/ef9e61ee-1fb7-47f2-9fc9-309039865067.png" width=50%>
DELL ロゴ 表示前で F12 キー 押下で 起動メニュー表示
“Intel Management Engine BIOS Extension (MEBx) ”が表示される
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212992/5ddbbe5e-cbcd-4133-8e63-4c032a267141.png" width=50%>
前述で設定した admin のパスワード  入力後にエンターキーを押下
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212992/911e4466-4928-4f85-ad71-e876c2b799c0.png" width=50%>
`[MEBx Login]` でエンターキーを押下
初期パスワード `admin` を入力　（前述で設定したパスワードではないので注意）
続けて、新しいパスワード（要確認を含む） 入力後にエンターキーを押下
`[Intel AMT Configuration]` でエンターキーを押下
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212992/2aecd7f5-859c-4aaf-8a12-b495e5f13eac.png" width=50%>
Intel AMT Configuration 画面
`[User Consent]` でエンターキーを押下
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212992/af98b1d6-691f-4433-8176-344da680d9ee.png" width=50%>
User Consent  画面
User Opt-in : `[None]` を選択
`[ESC]` でメニューに戻る
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212992/993de3cd-bf7b-4cbb-92ef-b90be7192864.png" width=50%>
Intel AMT Configuration 画面
`[Activate Network Access]` でエンターキーを押下
“Activates the current network settings and opens the ME network interface Continue:(Y/N)” 
：Yキーを入力
`[ESC]` でメニューに戻る
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212992/588566f1-327a-41b1-890e-4952dbbc0d80.png" width=50%>
`[MEBx Exit]` を選択してトップメニューに戻る
`[Exit]` を選択して再起動する
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212992/e5dc7dbf-3aaa-467e-9cd1-663f68e63505.png" width=50%>
vPro設定端末（IPアドレス: 172.17.xx.xxx）へブラウザからアクセス ※[MEBx Login]パスワード
`https://172.17.xx.xxx`:16993/logon.htm
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212992/2a9e2b10-fc8c-423f-a6c9-0a235d5f1574.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212992/bda95abc-4edc-4dbc-ba43-e04bec6663ec.png)

参考情報：
[[DELL] Intel vPro, AMT, MEBx でリモート](https://www.tksoft.work/system/dell/3360)
[「INTEL(R) AMT CONFIGURATION」メニュー](https://support.nec-lavie.jp/e-manual/m/nx/ma/201205/pdf/pg/sw1/v1/mst/mate_w7_manual/_manual/03_system/03-04_mekomoku/me/ME_1017030404.htm)
【Mac】.movを.gifに自動変換する

## はじめに

Macの画面収録ってmovファイルになると思うのですが、毎回gifに変換するのが面倒だな〜と思っていたところ今回良いやり方を見つけたので共有します！

n番煎じ感が否めないですが、どなたかのお役に立てれば...！！！

## やりたいこと

- 任意のフォルダ（例：screenshot）に `.mov` を追加すると**自動で同じフォルダに `.gif` が生成される**
- ファイル名はそのまま（例：`sample.mov` → `sample.gif`）

macにはAutomatorなるアプリがプリインストールされているみたいで（初めて知った）、その**フォルダアクション**って機能を使ったら実装できそうだったので使ってみました！

## 事前準備

ffmpeg のインストール
movをgifに変換するためのライブラリです

```bash
brew install ffmpeg
```

## フォルダアクションの作成（Automator）

### 1. Automator を起動
- 「アプリケーション」 > Automator

↓こいつ
![スクリーンショット 2025-03-27 15.38.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3844028/709af1ff-c54f-4726-a0af-b0c80738a279.png)


### 2. 新規作成 → 「フォルダアクション」を選択

![スクリーンショット 2025-03-27 15.37.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3844028/54261221-2b51-46b5-9c8f-088bb547833c.png)

### 3. アクション「シェルスクリプトを実行」を追加

![スクリーンショット 2025-03-27 15.40.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3844028/03763ac7-b9eb-4a6c-8d3a-e47e5fd2362c.png)

### 4. 対象のフォルダを選択

「その他」から選択できます

![スクリーンショット 2025-03-27 15.40.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3844028/9d58048e-cf46-41b7-aa68-525d490d4a1a.png)

### 5. 入力の引き渡し方法：**「引数として渡す」**

![スクリーンショット 2025-03-27 15.41.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3844028/146c5a7e-0ce4-4041-a850-f524ca5a8c8f.png)


### 6. 以下のスクリプトをコピペ：

```bash
for f in "$@"
do
  if [[ "$f" == *.mov ]]; then
    dir_path="$(dirname "$f")"
    base_name="$(basename "$f" .mov)"
    out_path="${dir_path}/${base_name}.gif"
    
    /opt/homebrew/bin/ffmpeg -i "$f" -r 24 "$out_path"
  fi
done
```

:::note info
`ffmpeg` のパスは環境によって変更必要な場合があります。   
`which ffmpeg` で確認できます。
:::

:::note alert
`which ffmpeg`の結果を使ってコマンドを実行するのは無理でした :cry: 

```sh
# ffmpegのパスを自動取得
ffmpeg_path="$(which ffmpeg)"

"$ffmpeg_path" -i "$f" -r 24 "$out_path"
```
:::

### 7. 「cmd + S」で保存して完了

## 動作確認

![画面収録 2025-03-27 15.26.58.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3844028/2c9eaa8c-bf64-4bd0-9bda-c9579dd66652.gif)

いい感じですね :tada: 

## おわりに

Qiitaにgifをはっつける時とか、プロジェクトでちょっとした動画を共有するのが少し楽になりましたね！
Automator今まで使ってこなかったですが、可能性を感じました！
Poetry 利用方法メモ

## 概要

Pythonパッケージ管理ツール`poetry`の使用方法メモ

## 詳細

### poetryとは

[https://python-poetry.org/docs/](https://python-poetry.org/docs/)  
Pythonパッケージ管理ツール。  
従来、Pythonで何かを作る際には、`pyenv-virtualenv`や`pipenv shell`を使って仮想環境を構築し、`pip`コマンドでパッケージをインストール＆`requirements.txt`を使ってパッケージを管理していた。  
`poetry`は仮想環境の構築やパッケージ管理ファイルの作成・変更など、開発に必要な機能を備えている。

### インストール

[https://python-poetry.org/docs/#installation](https://python-poetry.org/docs/#installation)

Macの場合、以下の方法でも可能

```bash
brew install poetry
```

バージョン確認

```bash
poetry --version
```

### 初期設定

[https://python-poetry.org/docs/configuration/#virtualenvsin-project-boolean](https://python-poetry.org/docs/configuration/#virtualenvsin-project-boolean)  
仮想環境をプロジェクトの下に配置するように設定する。
`--local`オプションを追加すると、`virtualenvs.in-project`設定自体をGitで管理できるようになるらしいので参考までに。

```bash
poetry config virtualenvs.in-project true
poetry config --local virtualenvs.in-project true
```

解除する際は以下を参照

```bash
poetry config --unset virtualenvs.in-project
poetry config --unset --local virtualenvs.in-project
```

### プロジェクト作成

プロジェクトを最初から作成する場合  
[https://python-poetry.org/docs/cli/#new](https://python-poetry.org/docs/cli/#new)

```bash
poetry new <プロジェクト名>
```

既存のフォルダに`poetry`を追加する場合  
[https://python-poetry.org/docs/cli/#init](https://python-poetry.org/docs/cli/#init)

```bash
poetry init
```

### ライブラリ追加

`poetry add`を使用してライブラリを追加可能  
[https://python-poetry.org/docs/cli/#add](https://python-poetry.org/docs/cli/#add)

`boto3`と`requests`を追加したい場合は以下を実行

```bash
poetry add boto3 requests
```

### 環境構築

`poetry.lock`が存在する場合、`poetry install`を使用して環境を構築できます  
[https://python-poetry.org/docs/cli/#install](https://python-poetry.org/docs/cli/#install)

```bash
poetry install
```

### 仮想環境利用

`poetry`で作成した仮想環境を利用する際は以下のコマンドを使用 
[https://python-poetry.org/docs/cli/#shell](https://python-poetry.org/docs/cli/#shell)

```bash
poetry shell
```

### その他便利な使い方

#### アップデート可能なパッケージの確認

[https://python-poetry.org/docs/cli/#show](https://python-poetry.org/docs/cli/#show)

```bash
poetry show --outdated
```

## 出典

韓国のブログで書いた内容（[poetry 사용법](https://idenrai.tistory.com/289)）を Perplexity に翻訳してもらった。
【初心者向け】AIとは ～AWS資格 AIプラクティショナーに受かるための勉強1～

# はじめに
AWSのAIプラクティショナーの勉強を始めたので、そこで学んだことを備忘録として記事にしていきます。
記事は細かい単位で書いて、最終的にまとめ記事と、資格取得きｊを書こうと思っています。
ちなみに、AIは**Artificial Intelligence**の略で、日本語だと人工知能になります。

# AIと生成AIの違い
みなさん一度はChatGPTなどの生成AIを使ったことがあるかと思いますが、AIと生成AIは別物になります。 

**AI**とは人口知能そのものを表すもので、**人間の知的活動の課題を、知能を持ったコンピュータが解決する技術全般**のことを指します。
近年登場した生成AIと違いAIの歴史は古く、1950年代にアメリカの計算機科学者の教授である**ジョン・マッカーシー**がAIという言葉を定義し、そこから研究がされてきました。

**生成AI**はテキストや音楽、画像などといった**様々なコンテンツを生成することに特化したAIで、AIの一部分**です。
例えばChatGPTならチャット形式でテキストを生成、Stable Diffusionなら画像を生成、GitHub Copilotならコードを生成、といった具合に生成AIにも種類があります。


:::note info
なお、AI・生成AIどちらも明確に定義は定まっていません。
これはそもそも知能・知性とは何か、を定義できていないためです。
:::

# AIを構成する技術
**生成AIはAIの一部分**であると先ほど言いましたが、細かく見ていくと**生成AIは深層学習の一部分**であり、**深層学習は機械学習の一部分**であり、**機械学習は人工知能の一部分**です。
図で表すと、以下のような形です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/87924/6736e077-1f01-453b-810f-55087d2141fe.png)

この時点でちょっと挫折しそうな私ですが、めげずに学んでいこうと思います:smile:
次回は、機械学習について記事を書くことにします
GitHub Actions入門：CI/CDの検証がはかどる！workflow_dispatchについてまとめてみた

## はじめに

GitHub Actions を使って CI/CD の自動化を進める中で、「手動でワークフローを実行したい」という場面があります。`workflow_dispatch` はそんなニーズに応えてくれる便利な仕組みです。

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

今回は、push などのトリガーを使わず、手動で GitHub Actions を実行する方法について備忘録としてまとめました。

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

開発中、「ちょっとだけ処理を流して確認したい」と思っても、コミットやPushが不要な変更では、トリガーできず面倒でした。

`workflow_dispatch` を使えば、ボタンひとつで必要なときにだけ実行できるため、開発フローの効率化につながると感じたため記録しています。

## GitHub Actions連携（workflow_dispatch）

GitHub Actionsを使うことで、ECSを含むさまざまなワークフローを自動化・効率化できます。

***その中でも `workflow_dispatch` は手動実行のためのトリガーとして非常に便利です。***

### workflow_dispatch とは？
GitHub Actionsのワークフローを「ボタン1つ」で手動実行できる仕組みです。これにより、次のような利点があります：

- pushやpull requestを必要とせず、必要なときだけトリガーできる
- 設定や挙動のテストを、コードの更新なしで行える
- チーム開発で同じ環境・手順での実行が可能になる

### 使用例（YAMLの記述）
```yaml
name: Deploy to ECS
on:
  workflow_dispatch:

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v2
      - name: Deploy to ECS
        run: echo "ここにECSデプロイの処理を書く"
```

このように設定することで、GitHub上から「手動実行ボタン」を使って、任意のタイミングでワークフローを動かすことができます。

## まとめ

GitHub Actionsにおける `workflow_dispatch` は、「今このタイミングで実行したい」という開発者のニーズにフィットする便利な機能です。

:::note warn
***pushなどのトリガーに頼らずに操作できるため、検証作業や一時的な処理実行に非常に有効です。***
:::

実際の現場でも「ちょっと確認したいけどコード変更したくない…」という場面で多く使われており、開発フローの柔軟性を高める一手となります。

***必要な処理だけ安全に実行できる仕組みとして、ぜひ活用していきたいと思います。***
STFリモートスマホ システム構築【Ubuntu】

#### 事前準備
- OS : Ubuntu Server 2024.04 LTS 
- Dcker : OS導入時に初期設定でインストール実施
- Dockerファイルをローカルに作成
    - コピー先（必要に応じて変更してOK）
        - `~/stf-docker/docker-compose.yml`
    - 設定ファイルの修正
        - 対象ファイル: `docker-compose.yml`
```docker-compose.yml
version: '2'
services:
  stfdb:
    image: rethinkdb:2
    restart: always
    ports:
      - 8082:8080
      - 28015:28015
    volumes:
       - db_data:/data
    command: rethinkdb --bind all
  adbd:
    image: sorccu/adb
    ports:
      - 5037:5037
    volumes:
      - /dev/bus/usb:/dev/bus/usb
    privileged: true
  stf:
    image: devicefarmer/stf
    restart: always
    ports:
      - "7100:7100"
      - "7110:7110"
      - "7400-7679:7400-7679"
    links:
      - stfdb:stfd
      - adbd:adbd
    environment:
      - RETHINKDB_PORT_28015_TCP=tcp://stfdb:28015
      - RETHINKDB_ENV_DATABASE=stf
      - RETHINKDB_ENV_AUTHKEY=Fgo_Ba4CuY8q
    command: stf local --no-cleanup --allow-remote --public-ip 172.17.10.250 --adb-host adbd --provider-max-port 7679
  
volumes:
  db_data:
    external: true
```
#### セットアップ手順
##### DB作成
```
sudo docker volume create --name=db_data
```
##### DB起動
```
sudo docker-compose up -d stfdb
```
#####  http://172.17.10.250:8082/ をブラウザで開く
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212992/9ef7cfcb-586e-4841-a32d-9c354054ed1b.png" width="50%">

DB起動後、`Data Explorer` で下記コマンド実行
```
r.db('rethinkdb').table('users').get('admin').update({password:'Fgo_Ba4CuY8q'})
```
##### STF初回起動
```
sudo docker-compose up -d
```
#### STFログイン
http://172.17.10.250:7100 へアクセス
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212992/f1fe7e2d-0295-49e8-9e5e-9e05dd7cfd7b.png)
STFには厳密なログインは存在しない（自己申告のユーザ名とメールアドレスあればログイン可能）

#### 参考サイト
[openstfの環境をdockerを使ってLinux上にゼロから構築する方法](https://qiita.com/k_keisuke/items/3ae15f2e2fc057dc5062)
2025/03取得 Google Cloud Associated Cloud Engineer 合格体験記

## 試験概要と学習期間・時間

**試験名:** Google Cloud Associate Cloud Engineer (ACE)
**試験公式ページ:** [Google Cloud Certification - Associate Cloud Engineer](https://cloud.google.com/learn/certification/cloud-engineer)
Google Cloud 認定資格には複数のレベルがあり、ACE（Associate Cloud Engineer）は **中級（Associate）** に該当。 詳細は [Google Cloud 認定資格一覧](https://cloud.google.com/learn/certification?hl=ja) を参照

**学習期間:** 2025年2月18日 〜 2025年3月23日（33日間）
**学習時間:** 合計 84時間 



## キッカケ

転職活動中に、第一志望の会社で使用されているインフラについての知識が不足していると感じたことがきっかけでした。これまでの業務ではAWSやGCPなどのクラウドサービスを扱う機会がなかったため、スキルアップの一環として受験を決めました。



## 学習方法

①基礎知識やサービスの学習
②模擬問題を多く解き、間違えた箇所を重点的に見直す

### 基礎学習やサービスについて

[『徹底攻略 Google Cloud認定資格 Associate Cloud Engineer教科書』（徹底攻略シリーズ）](https://amzn.to/4277M0f)を使って、Google Cloudの基礎知識やサービスについて学びました。
各章には演習問題があり、理解度の確認に利用しました。
さらに、分かりやすい解説付きの模擬試験（PDF）が1回分付属しており、本番を想定した模擬試験として活用しました。

[『合格対策 Google Cloud認定資格 Cloud Digital Leader テキスト＆演習問題』](https://amzn.to/4j4XcO2) を、書店で立ち読みした際に「マネージドサービス」や「スケーラビリティ」、クラウドとオンプレミスの違いなど、頻出キーワードの解説が分かりやすいと感じ、購入しました。

Cloud Digital LeaderはGoogle Cloudの初級資格であり、もともと何となく理解していた基礎を整理し、より明確に理解するのに役立ちました。

### 模擬問題について

Udemyの [『【Google認定資格】Google Cloud Associate Cloud Engineer模擬問題集』（3回分）](https://www.udemy.com/share/103f4Y3@E88Ilh8KOq0h6rC8dllh86Dhknx1NoFeaDkQeoi-ErV0twCioZMl5vFZI5CZldk-aA==/)を購入しました。模擬試験を1回解いた後、間違えた箇所を中心に見直し、正解の選択肢だけでなく不正解の選択肢についても「なぜ違うのか」を確認しながら学習を進めました。

Udemyの模擬試験は、試験結果がすぐに表示されるため数える手間なく復習し始めることができました。試験中に曖昧な問題にマークをつけて後で見直せる機能もあり便利でした。ただし、PCやタブレットで確認しながら学習する必要があるため、持ち運ぶ荷物が増えてしまうデメリットもありました。



## 工夫した点

勉強する場所を頻繁に変えることで、集中しやすい環境を作るようにしました。自宅での勉強は全体の2〜3割程度で、それ以外はスターバックスやベローチェなどのカフェを利用し、集中できる場所に身を置くことを意識しました。

自宅で学習する際は、ポモドーロテクニックを利用して時間を管理しながら意識的に休憩を取るようにしました。また、学習の記録をつけることでモチベーションの維持にもつなげました。

さらに、毎日 1.5時間（3ポモドーロ）以上勉強することを目標に設定しました。この目標は、最初の数週間の学習記録をもとに、モチベーションを取り戻すために立てたものです。

ChatGPTを活用し、学習中に分からないキーワードを深掘りするようにしていました。単に答えを得るだけでなく、関連情報も調べることで理解を深めることを意識しました。

[『科学的根拠に基づく最高の勉強法』](https://amzn.to/4l5zByx)を参考に、学習した内容を白紙の状態で書き出す "アクティブリコール" という学習法を取り入れました。この方法を実践することで、「理解したつもり」になることを防ぎ、確実に知識を定着させるよう心がけました。



## 受験してよかったこと

Google Cloudのインフラサービスについて、基本的な概要や用途を学べたことが大きかったです。

また、Google Cloudを学ぶ中でAWSやAzureとの違いを知るきっかけになっただけでなく、クラウド事業者に依存しないクラウドの基礎知識を身につける良い機会にもなりました。

さらに、この資格取得を通じてGoogle Cloudを個人で活用してみたいという意欲が湧いたことも良かったです。



## 受験時のドジ

### First NameとLast Nameの登録間違い

Google Cloud ACE試験の際は、[Webassessor](https://webassessor.com/googlecloud)のアカウント登録するのですが名前登録時にFirst NameとLast Nameを逆に登録してしまいました。山田太郎だとしたら Yamada Tarouといった具合にです。

このミスに気づいたのは、試験時に印刷して持参したKryterionのプリントを確認したときでした（笑）

試験後にGoogle Cloud Supportにメールを送り、丁寧な対応を頂けたのでとてもGoogle Cloud Supportの方には感謝しています。

### Kryterionの試験予約ボタンが見当たらない

Kryterionでの試験予約時、予約ボタンが見当たらず、かなり探してしまいました。でもボタンは画面のスクロールを下げたところに隠れていることに気づきました。同じように困った方がいれば、スクロールしてボタンを探してみてください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/136617/302a7592-ed0a-4773-a586-5e2a6a73df64.png)
下スクロールすると、[ACCESS WEBASSESOR]ボタンがあります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/136617/094e14fd-cb33-45bd-97f8-db2952ca69be.png)
【Vuex】モジュールとルートの関係

# 1. はじめに
先日以下の記事を書く中で疑問に思った点がありました。
それは、context オブジェクトのプロパティを調べる中で登場した`rootGetters`や`rootState`です。

  「rootStateはモジュール内からルートのstateにアクセスするために使用される」
  
と理解したものの、モジュールとルートの関係性を理解できていなかったので、この記事ではモジュールという概念についてまとめます。

https://qiita.com/megumi_i/items/5e19c0b1aeebb2192fba

# 2. モジュールとは
モジュールはストアを機能単位・役割単位に分割したものです。
各モジュールは独自に state, mutations, actions, getters を持つことができます。

::: note info
ストアとはVueアプリケーション全体の状態・ロジックなどを一元管理する “中枢”
:::

以下の例では、ストアをモジュールAとモジュールBに分けています。
```javascript
const moduleA = {
  state: () => ({ ... }),
  mutations: { ... },
  actions: { ... },
  getters: { ... }
}

const moduleB = {
  state: () => ({ ... }),
  mutations: { ... },
  actions: { ... }
}

const store = createStore({
  modules: {
    a: moduleA,
    b: moduleB
  }
})
```

# 3. 名前空間(namespace)
モジュールには名前空間という考え方があります。
これは、Vuexモジュールの中の state・mutations・actions・getters にスコープを持たせる仕組みで、名前空間があることにより、他のモジュールと名前が衝突しないようになります。

名前空間がなぜ必要かというと、
Vuexはデフォルトではすべての mutations, actions, getters を**グローバルに登録するから**です。

例えば以下のようなモジュールがあった場合、どちらの`setUser()`もグローバルに登録されてしまい、両者の区別がつかなくなります。

```javascript
const moduleA = {
  mutations: {
    setUser() {}
  }
}

const moduleB = {
  mutations: {
    setUser() {}
  }
}
```

ここで`namespaced: true`を使用することで、moduleAのsetUser()、moduleBのsetUser()というように区別できるようになります。

```javascript
const moduleA = {
  namespaced: true,
  state: () => ({
    user: null
  }),
  mutations: {
    setUser(state, user) {
      state.user = user
    }
  }
}

const moduleB = {
  namespaced: true,
  state: () => ({
    user: null
  }),
  mutations: {
    setUser(state, user) {
      state.user = user
    }
  }
}

const store = createStore({
  modules: {
    a: moduleA,
    b: moduleB
  }
})
```

```javascript
// moduleA の setUser を呼び出す
store.commit('a/setUser', { name: 'Taro from A' })

// moduleB の setUser を呼び出す
store.commit('b/setUser', { name: 'Jiro from B' })
```


# 4. rootGettersとrootStateを使用する場面
ここで、この記事のはじめに触れた
「rootStateはモジュール内からルートのstateにアクセスするために使用される」
の意味が理解できるようになりました。

**モジュールは名前空間（namespaced: true）によって限られたスコープに閉じられている**ため、別のモジュールのstateやgettersを使用したいときは、`rootState`や`rootGetters`を使用します。

**・gettersの引数として使用される場合**
getterの引数として使用されるとき、rootState, rootGetters が第3引数・第4引数として渡されます。
`someGetter (state, getters, rootState, rootGetters){　(略)
}`

**・actionsの引数として使用される場合**
actionsが引数にとるcontext オブジェクトのプロパティにはrootGettersとrootStateが含まれているので、これを使用することができます。
他モジュールのactionやmutationを使用したいときは、dispatchとcommitの3番目の引数として `{root: true}` を渡します。

`dispatch('someAction', null, { root: true })`
`commit('someMutation', null, { root: true })`

# 5. 最後に
モジュール、ルート、グローバル...といった普段見聞きする単語も、いざアウトプットしようとすると理解していなかったことに気づかされました。
これらの概念は、Vuexに限らずさまざまな領域に適用されるものだと思うので、丁寧に理解していきたいです。

最後までお読みいただきありがとうございました！

# 参考

https://vuex.vuejs.org/ja/guide/modules.html
iOS18 SDK で一部バージョンの Unity の Application.OpenURL が機能しない

## 経緯
ビルド環境：
Xcode16.0
Unity2021.3.36f1

Xcode16(iOS18 SDK)でビルドした iOS のバイナリで、外部ブラウザが開けない問題が発生。
アプリ強制アップデート時にストアに飛ばすリンクや、問い合わせのためにメーラーを起動するリンクが軒並み動作しない状態となった。
この条件下では iOS では Application.OpenURL が正しく動作しないことが判明。

調べたところ、Unity のバージョン 2021.3 などで発生しているとのこと。(2022 以降の LTS では発生しないという情報が多かった。)
「2021 の最新の LTS で直る予定」という記事も見たが、この時点で最新付近のバージョンのリリースノートにはそれらしい記述が見当たらず…
運営の都合上、一週間以内に対応しないとマズいということになったので、コミュニティでの記事を参考に iOS の場合のみネイティブの処理を呼び出すように対応しました。

対応の備忘録で記しておきます。

## 追加ファイル
```m:CustomOpenURL.mm
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

extern "C" void _CustomOpenURL(const char* url) {
    NSString* urlString = [NSString stringWithUTF8String:url];
    NSURL* nsUrl = [NSURL URLWithString:urlString];

    if (@available(iOS 10.0, *)) {
        [[UIApplication sharedApplication] openURL:nsUrl options:@{} completionHandler:^(BOOL success) {
            if (!success) {
                // 失敗時にすることがあればここに
            }
        }];
    } else {
        BOOL success = [[UIApplication sharedApplication] openURL:nsUrl];
        if (!success) {
            // 失敗時にすることがあればここに
        }
    }
}
```

これを Assets->Plugins->iOS に追加してビルド。

```cs:OpenURLControl.cs
using System.Runtime.InteropServices;

...

#if UNITY_IOS
        [DllImport("__Internal")]
        private static extern void _CustomOpenURL(string url);
#endif

...

#if UNITY_IOS
        _CustomOpenURL(url);
#else
        Application.OpenURL(url);
#endif

```

呼び出し側でこのように実装しました。

今のところ動作的には問題なさそうなので、Unity のバージョンアップを行うまでは間に合わせで、この対応で様子を見ようと思います。
【Windows】SQLiteの環境構築につまづいた経験から

# **はじめに**
わたしは現在djangoの学習中の実務未経験のエンジニア志望のサラリーマン（営業）です。
参考書は「実践Django Pythonによる本格Webアプリケーション開発」を進めています。
https://www.shoeisha.co.jp/book/detail/9784798153964
![L.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4043863/7b622cb8-1673-4cb2-ab97-e24c3be7d986.png)

# **躓いたこと**
書籍の冒頭は冒頭、、
マイグレコマンドを入力してsqlite3のdbを生成した後のこと
~~~
python manage.py migrate
~~~
生成されたsqliteのデータベースの中をコマンドプロンプト上で確認しましょうというところで、
~~~
'sqlite3' は、内部コマンドまたは外部コマンド、
操作可能なプログラムまたはバッチ ファイルとして認識されていません。
~~~
※Windows版なので「sqlite3.exe db.sqlite3」と入力しています。
調べたところSQLite3のDLが必要なんだなーと説明記事を検索し、手順通りにSQLite3のHPからDLを実施
https://www.sqlite.org/download.html
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4043863/333e5c78-cb05-48e4-a3fc-d057b0c57a82.png)
ダウンロードしたものの圧縮されたzipファイルがダウンロードフォルダに保存されただけ、、
他のソフトならインストーラーが起動されるのに、、とカルチャーショックを受けながら手順通りにプログラムファイルのフォルダ（ディレクトリ）に保存しました。
ディレクトリの並びは下記のようになりました。
~~~
C:\Users\ユーザ名\Program Files\sqlite-tools-win-x64-3490100\中身
~~~

**さっそくコマンドプロンプトでsqliteを動かすぞーっとコマンドを入力したものの、、**
~~~
'sqlite3' は、内部コマンドまたは外部コマンド、
操作可能なプログラムまたはバッチ ファイルとして認識されていません。
~~~
(´・ω・｀)

# **解決策について**
そういえば過去にコマンドプロンプト上で同様にコマンドが動かないときに「環境変数のPATH設定を追加したら使えるようになったな」ということを思い出し、とりあえずやってみることにしました。（正直なところ環境変数がなんぞや、とかPATHがなんぞやは理解していない、、）

* スタートボタン横の検索のところから「環境変数」を検索
* 「システム環境変数の編集」を開く
* 右下の「環境変数(N)」をクリック
* 下の方の「システム環境変数(S)」の中のPathを選択した状態で「編集」をクリック
* 「新規」をクリックし、「Program Fils」に保存したSQLiteのフォルダのパスをコピペ
~~~
C:\Users\ユーザ名\Program Files\sqlite-tools-win-x64-3490100
~~~
* コマンドプロンプトを再起動してコマンドを入力
~~~
C:\Users\名前\Desktop\プログラミング学習\python_work\djago_snippets\djangosnippets>sqlite3.exe db.sqlite3
SQLite version 3.49.1 2025-02-18 13:38:58
Enter ".help" for usage hints.
sqlite> .table
auth_group                  auth_user_user_permissions
auth_group_permissions      django_admin_log
auth_permission             django_content_type
auth_user                   django_migrations
auth_user_groups            django_session
sqlite> Program interrupted.
~~~

**無事コマンド通りに動きました！**

# **最後に**
初歩中の初歩かと思いますが、今回の学びとしては、環境構築の際にコマンドプロンプト上で動かないときはシステム環境変数のPATHを設定すれば動くようになる。
他のソフトでも同様のトラブルが起きた際は、まずはググってみて、対応策がでないときは同様の操作をやってみることにします。
（初のQiita投稿でした）
JAVA - java.lang.NoSuchFieldError: DEFAULT

# 1. Error Log
```
java.lang.NoSuchFieldError: DEFAULT at org.apache.poi.xssf.usermodel.XSSFWorkbook.&lt;init&gt;(XSSFWorkbook.java:122)
```

# 2. Root Cause
編譯過程中使用舊版本的套件，RUNTIME時候使用指定新版套件執行，導致找不到舊版的程式碼。

# REF
https://stackoverflow.com/questions/59492507/
Pythonでテキストファイルを読み込む

方法 1: readlines() を使う(可読性がたか)
```py
lines = []
with open('sample.txt', 'r', encoding='utf-8') as file:
    for line in file:
        lines.append(line.strip())
print(lines)
```
方法 2: リスト内包表記を使う(コードが短い)
```py
with open('sample.txt', 'r', encoding='utf-8') as file:
    lines = [line.strip() for line in file]
print(lines)
```
方法 3: ファイルオブジェクトを直接ループ(ファイルが巨大な場合)
```py
lines = []
with open('sample.txt', 'r', encoding='utf-8') as file:
    for line in file:
        lines.append(line.strip())
print(lines)
```
Databricksノートブックでカラムエクスプローラがサポートされました

好きです。こういうアップデート。

![column-explorer-30c5c1f1dafe0c16b0b135a92816d212.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/7804d399-d167-4025-813e-7079107e5237.gif)

リリースノート。

https://docs.databricks.com/aws/ja/release-notes/product/2025/march#new-column-explorer

> **新たなカラムエクスプローラ**
> 表形式のノートブック出力を操作する際にカラムエクスプローラを使えるようになりました。この列エクスプローラによって、列の検索、表示・非表示、ピン留め、並び替えを行うことができます。[列の探索](https://docs.databricks.com/aws/ja/notebooks/notebook-outputs#column-explorer)をご覧ください。

ノートブックでSQLの`SELECT`や`display`を用いて表形式データを表示した際に、右側に四角が三つ積み重なったアイコンが表示されます。

![Screenshot 2025-03-27 at 14.33.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/cebafa0a-cf14-4109-a21b-2e2d95f943cf.png)

こちらをクリックするとカラムエクスプローラが表示されます。

![Screenshot 2025-03-27 at 14.34.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/c140664d-2a57-4285-820b-3dba24f77887.png)

チェックボックスで列の表示・非表示を切り替えることができます。

![Screenshot 2025-03-27 at 14.34.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/6ab25ed1-7315-4f76-b5cc-451a2547ba57.png)

ドラッグ&ドロップで列の順序を入れ替えることができます。

![Screenshot 2025-03-27 at 14.34.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/4ddd54fb-5f80-4e1d-9e58-bebb968016bd.png)

特定の列を固定することもできます。

![Screenshot 2025-03-27 at 14.34.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/3053cd5a-f882-410b-a949-aeb637f26aa9.png)


### はじめてのDatabricks

[はじめてのDatabricks](https://qiita.com/taka_yayoi/items/8dc72d083edb879a5e5d)

### Databricks無料トライアル

[Databricks無料トライアル](https://databricks.com/jp/try-databricks)
🚀 短時間でPythonを習得！初心者向けおすすめ講座3選


こんにちは、韓国在住のsanaです！

プログラミングを始めたいけれど、「時間がない…」「何から手をつければいいかわからない…」そんな方へ。

今日は Pythonを短時間で学べる入門講座 を3つ紹介します！どれも 完全初心者向けで、プログラミング未経験者でも大丈夫な講座です:point_up_tone1:


📌 おすすめの3つの講座
✅ 12時間でPythonを学ぶ！
✅ 6時間で「自分も開発者になれるかも？」と実感できる！
✅ たった60分でPythonの基礎を理解！

どの講座も 初心者向け で、プログラミングの知識がゼロでもOK。Pythonの基本文法を学びながら、実際にコードを書いていくことで、すぐに実践できるスキルが身につきます。

また、Pythonはデータ分析やAI開発、Webアプリ開発など幅広い分野で活用できるため、学んでおいて損はありません！

今回紹介する講座は 「12時間」「6時間」「60分」 という短時間で学習できる構成になっており、プログラミングの知識がゼロでもOK！　スキマ時間を活用しながら効率的に、Pythonの基本文法を学びながら実際にコードを書いていくことで、すぐに実践できるスキルが身につきます。


🎯 こんな人におすすめ！
✅ プログラミング初心者：「プログラミングって何？Pythonって何？」という方でも大丈夫！
✅ 短時間でPythonの基礎を学びたい人：「1日で学びたい」「スキマ時間で学びたい」そんな方に最適。
✅ 将来エンジニアを目指している人：開発に興味があるけど、どこから始めればいいかわからない人にぴったり！


ファイティン～！


https://u.inf.run/4l3u4IO

https://u.inf.run/3FGuc0B

https://u.inf.run/3DTl7kv
Swiperで左右が少し見切れるスライダー

# Swiperで左右が少し見切れるスライダー
下記を記載する
### app/views/lauouts/application.html.erb
```
<link rel="stylesheet" href="https://unpkg.com/swiper@7/swiper-bundle.min.css" />
 <script src="https://unpkg.com/swiper@7/swiper-bundle.min.js"></script> 
```
</br>

### app/views/companies/search.html.erb
```
<div class="swiper ">
  <div class="swiper-wrapper ">
    
       
  </div>
  <!-- ページネーション -->
  <div class="swiper-pagination"></div>
  <!-- 前後の矢印 -->
  <div class="swiper-button-prev"></div>
  <div class="swiper-button-next"></div>
</div>
</div>

<script>
 const swiper = new Swiper(".swiper",{
    loop: true, //ループ
    speed: 300, 
    slidesPerView: 3, //１度に表示する枚数
    spaceBetween: 30, //スライド間の距離
    centeredSlides: true, //アクティブなスライドを中央にする
    //ページネーション
    pagination: {
      el: ".swiper-pagination",
      clickable: true,
    },
    //前後の矢印
    navigation:{
      nextEl:".swiper-button-next",
      prevEl: ".swiper-button-prev",
    },
    });
</script>

```
</br>
</br>