OmniAICreator/Qiita-1.07M huggingface dataset:
Dataformの単体テストをCloud Buildで実行する


# GitHub Actions を使う場合

Dataform CLI では単体テストを実施することができます。普段はassertion機能がメインで単体テストは使ってないというユーザーも多いかと思います。確かに最低限のデータ品質という意味では assertion で十分かもしれませんが、クエリが複雑化してきたり、運用フェーズでクエリがバージョンアップを繰り返してくると、ちゃんとテストを書いておくことで、ロジックが壊れていないかを確認することができます。

そこで運用の中でテストの自動実行をしていこうと考えた際に、GitHub Actions を使ったテスト自動化の方法を調べていくと以下のようなブログにたどり着きました。

[Github ActionsでDataformをデプロイする方法](https://qiita.com/leemunhui/items/d2a9f060341113b00c07)

記事の内容ですが、GitHub Actions から Dataform をデプロイする方法として以下のように記載されています。

- Dataform CLI を叩く方法と、API を使う方法の2つがある
- 前者は credential 情報を暗号化しているとはいえリポジトリ上で管理するというデメリットがある
- 一方、単体テストは API が用意されておらず、CLIを叩くしかない

となると、credentialをリポ管理するというリスクやサービスアカウントキーの管理などの問題が発生してしまいます。特に人数が多くなると鍵の管理は結構面倒な問題です。

※ 補足
ローカル環境で`dataform init-creds`を叩いて、`.df-credentials.json`を作成する場合、ADC(Application Default Credential)を使う方法と、サービスアカウントキーを使う方法があります。上記の記事で使用している`.df-credentials.json`はGitHub Actionsで使うものになるので、サービスアカウントキーを使う方法になります。（なので暗号化は必須）

# Cloud Build を使う場合

いきなり結論ですが、Cloud Buildを使うことで、上の問題を解決してしまおうという試みが今回のブログのテーマです。

まず、Dataformと同じプロジェクト内に必要なロールを付与したサービスアカウントを作成します。

```hcl
# サービスアカウントの作成
resource "google_service_account" "dataform_unit_test" {
  project      = var.project_name
  account_id   = "dataform-unit-test"
  display_name = "dataform-unit-test"
  description  = "Dataform単体テスト用のサービスアカウント"
}

# サービスアカウントに付与するロールを作成
resource "google_project_iam_member" "bigquery_job_user_member" {
  project = var.project_name
  role    = "roles/bigquery.jobUser"
  member  = "serviceAccount:${google_service_account.dataform_unit_test.email}"
}
resource "google_project_iam_member" "logs_writer_member" {
  project = var.project_name
  role    = "roles/logging.logWriter"
  member  = "serviceAccount:${google_service_account.dataform_unit_test.email}"
}
```

次に、このサービスアカウントを使って Cloud Build Trigger を作成します。

```hcl
resource "google_cloudbuild_trigger" "dataform_unit_test" {
  name        = "dataform-unit-test"
  description = "Dataformの単体テストを実行する"

  github {
    owner = "hoge"
    name  = "repo-name"
    pull_request {
      # ^main$
      # ^develop$
      # ^(main|develop)$ などのように複数のブランチを指定することも可能
      branch = "^saga$"
    }
  }
  filename           = "cloudbuild/dataform-test.yml"
  include_build_logs = "INCLUDE_BUILD_LOGS_WITH_STATUS"
  service_account    = google_service_account.dataform_unit_test.id
}
```

こうすることで、指定したリポジトリの指定したブランチに対して、PRが発行されたタイミングで`repo-name/cloudbuild/dataform-test.yml`で定義した単体テストを実行するように設定できます。

後は、レポジトリ直下に`cloudbuild`ディレクトリを作成し、その中に`dataform-test.yml`を作成します。

```yaml
steps:
  - id: Run dataform unit test
    name: node:lts
    entrypoint: bash
    args:
      - "-c"
      - |
        echo '{"projectId": "'${PROJECT_ID}'", "location": "US"}' > .df-credentials.json
        npm i -g --quiet @dataform/cli@3.0.0
        dataform install
        dataform test

timeout: 60s
options:
  logging: CLOUD_LOGGING_ONLY
```

※ 補足
先ほど紹介した GitHub Actions で使用する`.df-credentials.json`は、サービスアカウントキーを使用する関係上、認証情報を含んでいましたが、今回は必要ロールを付与した Cloud Build で実行するため、認証情報を含んでいません。よって以下のjsonをリポジトリ直下に出力することで事足りています。

```json
{
  "projectId": "your-project-id",
  "location": "US"
}
```
pyinstaller + yolo v8 でエラー

## pyinstaller + YOLOv8 でEXEがうまく起動できなかった。
こんなエラーが出て起動できない。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3679605/ce4162b1-da13-77c1-e8a7-520e7f66c59b.png)

スクロールして一番最後を見てみると、`default.yaml`ファイルが見つからないとのこと。
`FileNotFoundError: [Errno 2] No such file or directory: 'D:\\WoLNamesBlackedOut\\dist\\WoLNamesBlackedOut\\_internal\\ultralytics\\cfg\\default.yaml'`

## こんなこともあろうかと、既にQiitaの記事をストックしておいた！

https://qiita.com/kotai2003/items/c1c3193966448bb29af7

ただ私のspecの書き方が悪いのか、すぱっと解決しませんでした。
学習させた重みファイルを参照したいので、

```python:main.py
def resource_path(relative_path):
    if hasattr(sys, '_MEIPASS'):
        return os.path.join(sys._MEIPASS, relative_path)
    return os.path.join(os.path.abspath("."), relative_path)

model = YOLO(resource_path("my_yolov8n.yaml"))
model = YOLO(resource_path("my_yolov8n.pt"))
```
とし、pyinstallerのスペックでは、
```python:main.spec
datas=[('my_yolov8n.pt','.'),('my_yolov8n.yaml','.'),]
```
としていたので、参考としたQiita記事のように、うまくultralyticsを追加できませんでした。

## よろしい。ならば検索だ！

https://github.com/orgs/ultralytics/discussions/1356

眺めていると、

> Find yolo/utils/init.py, in about line34 change DEFAULT_CFG_PATH to absolute path above, official value
> start with ‘ROOT / ...’.

という記述を見つけ、YOLOv8がパスを見失っていることが原因らしい・・・
そういえば最近そんなことあったな！

https://qiita.com/calcMCalcm/items/178901c752f6cef481f1#pyinstallermmdetectionmmcv%E3%81%A7%E3%81%AE%E5%95%8F%E9%A1%8C

手前味噌ですが、自分の投稿が役に立ちました。
この時は、MMCV対応でしたが、同じ方法でうまく対応できました。
具体的には、
### 1.hook-ultralytics.py を作成
```python:hook-ultralytics.py
from PyInstaller.utils.hooks import collect_data_files
datas = collect_data_files("ultralytics")
```
### 2.hook-ultralytics.py を`hooks`というフォルダに保存

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3679605/a41fa719-f212-36f1-4c33-452994085f5c.png)

### 3. main.specを次のように記述
```python:main.spec
a = Analysis(
    ['main.py'],
    pathex=[],
    binaries=[],
    datas=[('my_yolov8n.pt','.'),('my_yolov8n.yaml','.'),],
    hiddenimports=[],
    hookspath=['./hooks'], #こちら
    hooksconfig={},
    runtime_hooks=[],
    excludes=[],
    noarchive=False,
    optimize=0,
)
```
このようにすることで、pyinstallerはhooksを参照し、hook-ultralytics.pyを実行してくれるようです。
結局、参考とさせていただいた記事と同じ内容を実施しているのではないかと思います。

めでたし、めでたし。
Webデザイナーの実務経験が2年になるので、未経験から2年の間で、できるようになったことを書いてみる。

## 2年で何ができるようになったんだろう？
突然＆個人的な話ですが、今日で28歳になりました:star2:
去年の12月から毎月Qiita投稿してますが、だいたい月末投稿になり、今月も月末になり、なぜか誕生日に記事書いて投稿するという自分へのサプライズプレゼント？になりました:joy:
（技術的なことも投稿したいので、今月はもう1記事投稿します！:muscle:）

26歳でWebデザイナーになり、**そろそろ実務経験が2年になります**。 
早・・・:scream:。
今回は技術的な話ではないですが、この2年で何ができるようになったかな？と思いまして、この機会に振り返ろうかなとおもいます。


### 2年の間で、少しはできるようになったと思うこと
#### コーディング領域:computer:
:::note info　
- HTML、CSSの基礎理解、活用
- JavaScriptの簡単な活用
- レスポンシブデザインについて考慮したコーディング
:::
綺麗で分かりやすいコーディングはまだ、時間が掛かってしまう＆難しく考えすぎてしまうときがあります。
コーディングの時間を早く正確に、そして柔軟に捉えていくのが今後の課題かと思いました。

#### Web知識領域:earth_asia:
:::note info
- SSLの理解＆発行まで
- SEOの意義を理解する
- ドメインの管理
- サーバー間でのWebサイト移行
- Webサーバーとメールサーバーの繋がり基礎だけ理解
- メールフォーム関係
- Google Workspace関係
:::
サーバーについては難しいことは全く分かりませんが、Webサイトが裏側でどういう仕組みで支えられているのか？という基礎的な部分は理解できている気がします。
サーバーのOSが切れそうという理由で、旧サーバーから新サーバーへWebサイトの移行を半年かけて100ドメイン近く行ったので、大変でしたが苦労した分、Webの体系がちょっと理解できるようになりました:joy:
メールフォーム部分の理解を今後より深めたいです。

#### デザイン領域:art:
:::note info　
- デザインの基礎
:::
どの分野でもそうですが、特にデザインについて、まだまだ未熟だなと通貫することが多いです。一番難しいと感じているかもしれない。
なので、デザイン分野をどう伸ばしていくかをまず考えないとな〜と、書いていて思いました。:thinking:

#### CMS領域　（a-blog cms）
:::note info
- モジュールの活用
- カスタムユニットの活用
- グローバル変数の理解　
- バージョンアップ（メジャーバージョンもできるようになった）
- カスタムフィールドの活用　などなど
:::
CMSは奥が深いです・・・！
こんなことも可能なの？！ということが、最近多いです。
2年経ってやっとCMSの柔軟さと便利さを改めて実感しています。
もっと使いこなせるようになりたいもんです。

#### 企画領域:point_up:
:::note info
- 用途に合ったサイト構成の提案、企画
- UIに配慮した細部のパーツの提案、企画
- 主旨に合ったバナーの企画
- メインビジュアルなどの企画
- ライティング関係
:::
今挙げた分野の中で、一番やっていて楽しいと感じる分野かな？と思います。
UIに興味があるので、UI・UX分野も知識を入れたいです。

## まとめ
2年間で、多少は成長したのかな？と感じています。:thinking:
今日課題が見えてきたので、これからも、分野を絞らず、吸収できることは吸収していきたいなと思っています。
iOS向け Unityアプリケーション実機テストガイド

# iOS向け Unityアプリケーション実機テストガイド

Unityで開発したアプリケーションをiPhoneやiPadでテストすることは、アプリが実際にiOSデバイス上で正しく動作するかを確認するために非常に重要です。このガイドでは、初学者でもわかりやすいように、UnityアプリケーションをiOSデバイスで実機テストする方法をステップごとに解説します。

---

### 1. UnityでのiOS向け設定

まず、iOS向けのビルド設定をUnityで行い、必要なモジュールをインストールします。

#### a. iOSプラットフォームの選択
UnityでiOSに対応したビルドを行うために、以下の手順でターゲットプラットフォームをiOSに切り替えます。

1. Unityの「**File**」メニューから「**Build Settings**」を選択します。
2. プラットフォームリストから「**iOS**」を選択し、「**Switch Platform**」をクリックします。

#### b. 必要なモジュールのインストール
iOS向けのビルドには、MacにインストールされたXcodeが必要です。以下の手順で準備を進めます。

- XcodeをApp Storeからインストールします。
- Unity Hubの「インストール」タブで「iOS Build Support」がインストールされているか確認してください。

---

### 2. iOSデバイスの接続準備

次に、iOSデバイス（iPhoneやiPad）をMacに接続し、アプリを実機でテストできるようにします。

#### a. デバイスをMacに接続
1. iPhoneまたはiPadをUSBケーブルを使ってMacに接続します。
2. Xcodeを起動し、デバイスが正しく認識されているか確認します。Xcodeの「**Window**」メニューから「**Devices and Simulators**」を開き、接続されたデバイスがリストに表示されていることを確認してください。

---

### 3. UnityでiOSアプリをビルド

Unityの設定が完了したら、アプリを実機で動作させるためにビルドを行います。

1. Unityの「**File**」 > 「**Build Settings**」を開きます。
2. プラットフォームリストで「**iOS**」を選択し、「**Build**」をクリックします。
3. ビルドが完了すると、Xcodeプロジェクトが生成されます。このプロジェクトをXcodeで開いて次のステップに進みます。

---

### 4. Xcodeでアプリを実機テスト

XcodeでiOSアプリをビルドし、iPhoneまたはiPadにインストールします。

1. XcodeでUnityから生成されたプロジェクトを開きます。
2. Xcodeの「**Signing & Capabilities**」タブで、Apple IDやチームの設定を行い、正しいサインができるようにします。デバイスにアプリをインストールするためにはApple Developerアカウントが必要です。
3. 「**Product**」メニューから「**Run**」をクリックしてアプリを実機で実行します。アプリはiPhoneまたはiPadにインストールされ、起動されます。

---

### 5. 実機での動作確認

アプリが実機にインストールされたら、動作確認を行います。iOSデバイス上で実際にアプリを操作し、正常に動作しているかをチェックします。

- Xcodeのコンソールを使用して、デバッグ情報をリアルタイムで確認できます。バグやエラーが発生した場合は、コンソールログを参考に修正を行いましょう。

---

### 6. バグ修正と再ビルド

実機テスト中にバグや不具合が見つかった場合は、Unityで修正を行い、再度ビルドして実機にインストールします。問題が解決するまでこのプロセスを繰り返します。

---

### まとめ

iOSでの実機テストは、リリース前にデバイス上でアプリが正しく動作するかを確認するために非常に重要です。このガイドに従って、Unityで作成したアプリをスムーズにテストし、より高品質なアプリをリリースしましょう。
【AWS,Azure】電子請求書授受サービスをクラウドサービスで実現したいので、GPTに比べてもらった

# 結論：Azureのほうが安いみたい
個人的にいじってるけど使い勝手が悪すぎるし、これセキュアなのかと疑う部分が多い。
たぶん設定できるんだけども、AWSに慣れすぎてるせいでよくわからないことが多い印象でした。


# 電子請求書発行サービスのAzure上での構築経緯

## 1. プロジェクト概要
- **目標**: 電子請求書発行サービスをAzure上で構築し、顧客が発行された請求書をダウンロードできるシステムを作成する。
- **使用する技術**:
  - **Azure Static Web Apps**: フロントエンドのホスティング
  - **Azure Functions**: サーバーレスバックエンドAPIの構築
  - **Azure Table Storage**: データの保存
  - **Azure Blob Storage**: 請求書PDFなどのファイル保存
  - **Azure Active Directory B2C**: 顧客認証

## 2. Azure Functionsの構築
- **作成したHTTP Trigger Function**:
  - **`GetData`**: Azure Table Storageからデータを取得するAPI
  - **`AddData`**: Azure Table Storageにデータを追加するAPI
- **解決した課題**:
  - `func`コマンドがGit Bashで認識されない問題が発生し、環境変数の設定やエイリアス作成で解決。
  - `host.json`の構文エラーを修正し、正しいJSON形式に設定。
  - `FUNCTIONS_WORKER_RUNTIME`設定の不一致を解消し、ローカルとAzureの設定を一致させた。

## 3. Azure Functionsのデプロイ
- **手順**: `func azure functionapp publish`コマンドを使用してAzureにデプロイ。
- **結果**: デプロイが成功し、Azure Portalで動作確認を実施。

## 4. Azure Table StorageとBlob Storageの設定
- **未作成の部分**:
  - **Azure Table Storage**: データ保存用。ストレージアカウントとテーブルの作成が必要。
  - **Azure Blob Storage**: 請求書ファイル保存用。ストレージアカウントとBlobコンテナーの作成が必要。
- **提供した手順**:
  - Azure Portalでストレージアカウントを作成し、テーブルとBlobコンテナーを設定する手順を提供。

## 5. 料金面の考察
- **Azure Static Web Apps**:
  - **プラン**: Standard プラン ($9/月)
  - **役割**: フロントエンドのホスティング。
- **Azure Functions**:
  - **料金**: 約 $0.20/100万回の実行 (使用量に依存)
  - **役割**: バックエンドAPIのホスティング。
- **Azure Table Storage**:
  - **料金**: $0.05/GB (データ保存量に依存)
  - **役割**: データベースとして使用。
- **Azure Blob Storage**:
  - **料金**: $0.0184/GB (ホットストレージ)
  - **役割**: 請求書などのファイル保存。
- **Azure Active Directory B2C**:
  - **料金**: 5万MAUまでは無料。超過時は$0.0055/MAU。
  - **役割**: ユーザー認証管理。
- **Azure Monitor**:
  - **料金**: $2.76/GB (ログデータの監視と保存)
  - **役割**: アプリケーションの監視とログ管理。

### 合計月額コスト (概算)
- **総コスト**: 
  - Static Web Apps: $9.00
  - Functions: $0.20
  - Table Storage: $5.00 (100GB使用時)
  - Blob Storage: $1.84 (100GB使用時)
  - Monitor: $5.52 (2GBログデータ使用時)
  - **合計**: 約 $21.56/月 + 使用量に応じた追加費用

## 6. 次のステップ
- **Azure Table Storage**と**Azure Blob Storage**の設定を完了する。
- **Azure Functions**とこれらのストレージを統合し、システム全体の動作確認を行う。

## 7. 残タスク
1. **Azure Table Storage**と**Azure Blob Storage**の設定をAzure Portalで行う。
2. **Azure Functions**とこれらのストレージを統合し、データの保存と取得、ファイルのアップロード機能を実装する。
3. **システム全体の動作確認**と、**最終テスト**を行い、本番リリースの準備を進める。
React + TypeScriptのButtonコンポーネントのpropsで使う型定義

# React + JavaScript

```
<Button className="..." onClick={...} ...>アカウントを作成</Button>
```
```
export default function Button({ children, ...props }) {
    return (
        <button {...props}>{children}</button>
    );
}
```
React + JavaScriptでは上記のように記述してButtonコンポーネントを作成しています。
propsには``disabled``や``onClick``など、buttonタグで使用する要素が入ってきます。

# React + TypeScript
TypeScriptには型が存在します。
React + TypeScriptでもJavaScriptと同様にButtonコンポーネントを作成したいのですが、その際の型の記述はどうしたら良いでしょうか。

```
export type ButtonType = React.ButtonHTMLAttributes<HTMLButtonElement> & {
    children: React.ReactNode
}
```
※詳しくは[こちら](https://zenn.dev/u_10/articles/8c3cda00a701e9#1.props%E3%81%AFbuttonhtmlattributes%E3%82%92%E6%8B%A1%E5%BC%B5%E3%81%99%E3%82%8B)を参照

この型指定により、通常のbuttonタグと同様、``<Button>``の中で属性を指定することができるようになります。

あとはButtonコンポーネントの引数に型を指定してあげれば、解決します。
```
export default function Button({ children, ...props }: ButtonType) {
    return (
        <button {...props}>{children}</button>
    );
}
```
「JS基礎」Debounce, Curry and 関数コンテストにつて実現します


## 紹介とこのブログを書く理由
クロージャと`this`　の概念は、ずっと私を混乱させてきました。今でも、これたを正確に理解するのに時々苦労することがあります。そこで、これらを本当に理解するためには実際に練習するのが一番だと思いました。もし、私と同じようにこれらの概念に悩んでいる方がいれば、このブログが私たちの理解を深める手助けになればと思います。

**Debounce**と**Curry**を一歩ずつ実装しながら、これらの概念を深く掘り下げていきます。その過程で、クリージャが変数をどのようにキャプチャするか、そして`apply/call/bind`が関数の呼び出しをどのように制御するかを見ていきます。
## クロージャの理解
### クロージャとは何か
クロージャはJavaScriptの中でも難しい概念の一つです。その概念をできる限り簡単に説明します。クロージャについて詳しく知りたい方は、優れた記事がたくさんありますので、それらを読むことをお勧めします。

クロージャは、生成されたの環境を「記憶」する関数です。つまり、外部の関数が実行を完了した後でも、内部の関数は外部の関数のスコープの変数にアクセスできるのことです。

### クロージャの実行
例を見て、よりわかりやすくしましょう。
```javascript
function outer() {
	let count = 0
	function inner () {
		count++
		console.log(count)
	}
	return inner
}

let innerFunc = outer()

innerFunc() // 1
```

この例では、`inner`関数が`outer`関数のスコープから`count`変数をキャプチャしているため、`inner`はクロージャとなります。`outer`関数が実行を完了した後でも、`inner`は`count`変数にアクセスすることができます。

### 一歩ずつクロージャを使用してDebounceの実現
```javascript
function debounce(func, delay) {
    let timeoutId;
    return function(...args) {
        clearTimeout(timeoutId);
        timeoutId = setTimeout(() => {
            func.apply(this, args);
        }, delay);
    };
}
const user = {
	name: 'Y',
	printMyInfo: function (age) {
		console.log(`Hello! My name is ${this.name}. I'm ${age} years old.`)
	}
}

const debouncedNamePrint = debounce(user.printMyInfo.bing(user), 3000)
debouncedNamePrint(30) // Hello! My name is Y. I'm 30 years old.
```

それでは、細かく説明しましょう。

まず、`setTimeout`を追跡するために、`timeoutId`という変数を定義します。この関数が遅延期間が終了する前に再度呼び出された場合、`clearTimeout(timeoutId)`が前のタイマーをキャンセルします。

次に、Debounceは関数を返す必要があります。この新しい関数は、既存のタイムアウトをクリアして遅延をリセットし、指定された期間の後に`func`を呼び出すための新しいタイムアウトを設定します。ここでクロージャが関係してきます。返された関数は`timeoutId`にアクセスし続けることができます。

## カリー化の理解
カリー化は、関数を一連の関数に変換し、各関数が一度に1つの引数を取るようにします。

**例**
```javascript
function currySum(a) {
	return function(b) {
		return function(c) {
			return a + b + c
		}
	}
}
```
この例から、カリー化とクロージャがどのように連携して動作するかがわかります。一連の間すで返された各関数は、それぞれが受け取った引数にアクセスすることができます。

## カリー化の実現
カリー化の基礎を理解して、シンプルなカリー化関数の実現方法を見てみましょう。
```javascript
// implement
function curry(fn) {
	return function curried(...args) {
		if(args.length >= fn.length) {
			return fn.call(this, ...args)
		}
		return curried.bind(this, ...args)
	}
}

// example
function multiply(a, b, c) {
	console.log(a * b * c)
}

const curriedMuti = curry(multiply)

curriedMuti(2)(3)(4) // 24
```
まず、`fn`関数を引数に取る`curry`関数を定義します。関数の内部では、新しい関数`curried`を定義して返します。この関数は引数を集めて`fn`関数に渡します。

次は、引数をチェックします。
- `fn.length`: `fn` 関数が期待する引数の数を示します。
- `args.length`: 現在までに集めた引数の総数を示します。
チェックポイント：
- もし `fn.length` が `args.length` 以上であれば、必要な引数が揃ったことを意味します。その場合、`fn`を`args`とともに呼び出し結果を返します。
- まだ引数が足りない場合、さらに引数を集めるための別の関数を消します。この新しい関数は、これまでに集めた引数と新しい引数を組み合わせて、更新された引数のリストで`curried`を再び呼び出します。

## `apply`, `call` と `bind`の適用
上記の例では、関数が実行されるコンテキスト（`this`）を制御するために、`apply`、`call`、`bind`を使用しました。以下にそれらの違いを簡単に説明します。

```javascript
// apply
func.apply(thisArg, [argsArray])
// call
func.call(thisArg, arg1, arg2, arg3, ...)
// bind
const boundFunc = func.bind(thisArg, arg1, arg2, ...)
```
これで違いがわかりやすくになると思います。共通点として、最初の引数は常に`thisArg`です。違いは以下の通りです。
- `apply`: 引数は配列として提供します。
- `call`: 引数は個別に提供します。
- `bind`: 引数は個別に提供して、新しい関数を返します。

これで説明は以上です。ハッピーコーディング！
【Chrome拡張機能】Reactで数式生成ツールを作った話

# 🧩 はじめに
React(TypeScript)でChrome拡張機能を開発したので制作記録として解説を掲載したいと思います．また筆者は開発初心者であるため，至らぬ点があるかと思われますがご了承ください．

# 🧩 自己紹介
初めまして．趣味でweb開発を勉強している273*(ﾂﾅｻﾝﾄﾞ) / Kei.と申します．関西の大学生です．最近はフルスタック開発やツール制作を行なっています．まだまだ初心者です．

# 🧩 完成品
以下のChromeウェブストアからインストールすることができます．
開発期間は3週間です．

~~審査中なので公開されたら添付します．~~
MathJaxの内部にcdnが含まれているため，ストアに公開することができませんでした．READMEにてローカルで使用する方法を記載しています．(解決できそうな方法を知っている方がおられましたら教えてください！！)

ソースコード

https://github.com/273Do/FormulaGenerator-ChromeExtension

# 🧩 制作に至った経緯
卒業論文の執筆がそろそろということで，最近はLatexを勉強しています．数式を書くことが多いのですが，複雑な数式になるほど数式を作るのに時間がかかるという課題を抱えていました．以前，そのような課題を解決するツールとして，webツールを制作し公開しましたが，いざ執筆をするのにサイトを行き来するのは面倒だと指摘を受けました．従来のツールに便利な機能を追加し，Chrome拡張機能として実装することで，さらに使いやすくなると考えて開発をしました．

# 🧩 サービス
[こちら(FormulaGenerator)](https://formula-generator.273doworks.com/)が以前制作した数式生成ツールのweb版になります．webサイト開発の勉強をし始めて3ヶ月くらいの時期に制作したものですので非常に使い勝手が悪いです．

今回は，以前のツールから機能をそのままに，デザインや使用技術を大幅に変更して使いやすくなりました．ぜひ導入して使っていただけると幸いです．

# 🧩 仕様

### 1. 数式生成機能

テキストエリアにTexを入力すると数式が表示されます．また，あらかじめ用意されたテンプレートを使用することも可能です．

### 2. 数式コピー機能

生成した数式をクリップボードにコピーすることができます．  Texをそのままテキスト形式でコピーするか，Wordなどに対応したMathML形式でコピーするか選択することができます．

### 3. お気に入り/編集機能

残しておきたい数式があればお気に入りに追加して保存しておくことができます．  
お気に入りに登録した数式は再度編集したり削除したりすることが可能です．  
これらはchromeにログインしているアカウント間で同期ができます．

### 4. テーマ/言語切り替え機能

テーマは，ダークテーマ，ライトテーマ，システム設定から選択することができます．  
言語は英語と日本語に対応しています．  
デフォルトではダークテーマ，英語になっています．一度設定すると保存されますので都度切り替える必要はありません．


# 🧩 開発

### 環境
今回はRedux Toolkitを用いたテンプレートを用いて開発を行いました．Chrome拡張機能はwebの技術で開発することができるので非常に楽でした．Reduxでタブとお気に入り保持，Storage APIで数式入力とお気に入り情報の管理を行なっています．

https://github.com/sinanbekar/browser-extension-react-typescript-starter

https://github.com/extend-chrome/storage

### フロント
最近話題のUIライブラリであるshadcn/uiを用いています．Radix UIベースでTailwindCSSを採用されているので非常に扱いやすいです．iconはいつものlucideを使用し，テーマ切り替えはshadcn/uiのテンプレートを，言語切り替えはi18nextを採用しました．

https://ui.shadcn.com/

https://lucide.dev/

https://www.i18next.com/

# 🧩 デザイン

### ロゴ
ロゴに関しては以前のツールと同様のものを使用しています．3DCG制作ツールのBlenderを使用しました．手前にすりガラス風のマテリアルのオブジェクトを，背面に放射マテリアルのオブジェクトを配置することでリッチなデザインに仕上げています．他制作物のロゴも全てこのツールを用いています．統一感が出て良きです(繰り返し)．

<hr>

### アプリデザイン / UI
スマホアプリに寄せたUIを設計しました．アイコンや色を最小限に抑えて，シンプルで使いやすいUIを目指しました．タブバーはすりガラス風になっており全体的にiOSっぽくなっています(意識しました)．Figmaのテンプレートを添付しておきますので興味があればどうぞ!

![screenshot.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3530539/080068e4-2b63-958c-cc81-8e2661dabd71.png)


https://www.figma.com/design/yOo7uFgNcSjlLkR2xz0DiB/formula-generator(ChromeExtension)?node-id=0-1&t=75cmikuCXgP1Ysuv-1


# 🧩 心残り
稀に数式の表示がうまくできなかったりします．再起動すると治るようですが原因が不明です．．．

# 🧩 最後に
以前の投稿から半年以上が経過していますが，チームスマホアプリ開発プロジェクトが終了したり，就職先が決まったり，ハッカソンで優秀賞をいただいたり．．．色々ありました．時間がなかなか取れず記事にはできていませんが😭．これからも気まぐれに開発していきたいと思います！最後まで読んでいただきありがとうございました．それでは！
OpenAIのAPIをモデル指定（gpt-4-turbo-with-vision）で呼ぶとKeyError: 'choices'がかえってくる対策の備忘録

OpenAIのAPIをモデル指定（gpt-4-turbo-with-vision）で呼ぶとKeyError: 'choices'がかえってくるエラー対策の備忘録

以下を参考にAPIを呼ぶとエラー

https://recruit.gmo.jp/engineer/jisedai/blog/gpt-4-turbo-with-vision/

エラー内容

```sh
$ python3 visionapi_test.py
Traceback (most recent call last):
  File "/Users/seigo/Desktop/python/tools/openai/visionapi_test.py", line 40, in <module>
    print(response.json()['choices'][0]['message']['content'])
          ~~~~~~~~~~~~~~~^^^^^^^^^^^
KeyError: 'choices'
```

# 今回の解決方法

以下を参照すると、

https://platform.openai.com/docs/deprecations

`gpt-4-vision-preview`はdeprecateということで`gpt-4o`に置き換えて解決


# 参考

https://qiita.com/omo_taku/items/130d5143816cae4ae896
レビューアイの操作マニュアル：レビュー分析画面

株式会社リテジーリンクは
レビュー分析AI [レビューアイ](https://reviewai.next-seed.work) の無料デモの提供を開始しました。
無料デモの詳細は[ランディングページ](https://reviewai.next-seed.work)でご確認ください。
この記事では、簡単な操作方法をご紹介します。

## 1. サイドバーとトップバーの基本操作
画面左側のサイドバーにはメニューが並んでおり、本記事ではレビュー分析画面の
操作方法に焦点を当てて説明します。トップバーでは、分析したいデータや
対象期間を選択することができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3776808/b5782d2a-130d-c147-8560-a3bd8538549c.png)


## 2. レビュー分析結果の表示
データを選択すると、レビュー分析結果の概要パネルが表示されます。
パネル内では各観点ごとの評価が一目でわかるようになっており、観点ごとに
お客様がどのように評価しているかを視覚的に確認できます。
各観点の横に表示される件数で観点に言及されているレビュー文の数を確認できます。

![point_of_view.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3776808/8ededdfe-3883-e07a-5f82-5c6f5049d1b9.png)

### 観点の探し方

観点は初期状態では5件のみ表示されていますが、「さらに表示」をクリックすると、
より多くの観点が表示されます。右上のセレクトボックスで観点のソートが可能で、
デフォルトの「注目順」ではレビューで多く言及されている観点が上位に表示されます。
また、「強み順」では高評価されている観点が上位に表示され、自社の強みを
把握する際に役立ちます。「改善順」を選択すると、改善の余地のある観点が
上位に表示され、サービス向上の指針として利用できます。

#### 検索機能の活用
検索欄に入力を始めると、リアルタイムで検索結果が表示されます。部分一致や
読みの検索にも対応しており、スムーズに目的の観点を見つけることができます。

## 3. 詳細パネル
概要パネルで **詳細を確認** をクリックすると、その観点の詳細パネルが表示されます。
詳細パネルでは、その観点に対する具体的な評価を確認できます。
操作方法は概要パネルと同様です。

![detail.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3776808/d84c1237-d3ec-0d4d-1c9c-e39a9eb257ab.png)

## 4.レビューの閲覧
さらに、「レビューを見る」をクリックすると、該当観点に関連するレビュー文が抽出され、
評価別に表示されます。評価は各レビュー文に紐づいており、レビュー全文を確認したい
場合は該当文をクリックすることで、詳細を確認することができます。

![review.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3776808/2425aa78-ace7-0a8c-95eb-e12f57294502.png)


レビュー全文の上部に表示される日付は、基本的にはレビュー投稿日を示していますが、商品やサービスの購入日とは異なる場合がある点にご注意ください。

## 最後に
[レビューアイの無料デモ](https://reviewai.next-seed.work)を公開中です。
詳細は[レビューアイのご案内ページ](https://reviewai.next-seed.work)でご確認ください。
Profileでパスワードの期限設定

まず、以下のSQLを実行して状況を確認する。

set lines 150
col limit for a20

select RESOURCE_NAME, RESOURCE_TYPE, LIMIT from DBA_PROFILES where RESOURCE_NAME 
in ('FAILED_LOGIN_ATTEMPTS','PASSWORD_LIFE_TIME','PASSWORD_LOCK_TIME','PASSWORD_GRACE_TIME','PASSWORD_REUSE_TIME','PASSWORD_REUSE_MAX')
and PROFILE='DEFAULT';


●FAILED_LOGIN_ATTEMPTS	指定した回数ログインに失敗するとアカウントをロックする
●PASSWORD_LIFE_TIME	    パスワードを設定してからの有効期間（日数）

あまりおすすめはしないが
上記二つをUNLIMITEDにしておくと、ユーザーのロックなどで悩まない。

例）
alter profile default limit password_life_time unlimited;

参考サイト
https://oracle-chokotto.com/ora_profile.html
flutter_blue_plusのスキャンでデバイスが見つからなった時にチェックすること(自分用メモ)

# デバイスがスキャンで見つからない場合の対処方法
FlutterBluePlusを使用してBLEデバイスをスキャンしてもデバイスが見つからない場合、いくつかの考えられる原因とその対処方法があります。以下にそれぞれのケースについて説明します。

### 1. エミュレーターを使用している
エミュレーターではなく、物理デバイスを使用してください。エミュレーターはBluetooth機能が制限されているため、BLEデバイスのスキャンが正しく行えない場合があります。物理デバイスを使用してアプリをテストすることが重要です。

### 2. 別のBLEスキャナーアプリを試す
別のBLEスキャナーアプリを使用して、デバイスが検出できるか確認してください。以下のアプリがオススメです：

iOS: nRF Connect
Android: BLE Scanner
これらのアプリをインストールして、デバイスが表示されるか確認してください。

### 3. デバイスがBluetooth Classicを使用している
ヘッドホン、スピーカー、キーボード、マウス、ゲームパッド、プリンターなどのデバイスは、Bluetooth Classicを使用していることがあります。これらのデバイスはシステム設定で見つかるかもしれませんが、FlutterBluePlusでは接続できません。FlutterBluePlusはBluetooth Low Energy（BLE）のみをサポートしています。

### 4. デバイスのadvertisingが停止している
デバイスがアドバタイジングを停止している可能性があります。以下の手順を試してみてください：

1. デバイスを再起動する
2. デバイスを「ディスカバリーモード」にする
3. スマートフォンが自動的にデバイスに接続したか確認する
4. 他のアプリが既にデバイスに接続していないか確認する
5. 他のスマートフォンが既にデバイスに接続していないか確認する
また、以下のコードを使用してシステムに接続されているデバイスを確認できます：

```dart
List<BluetoothDevice> system = await FlutterBluePlus.systemDevices;
for (var d in system) {
    print('${d.platformName} already connected to! ${d.remoteId}');
    if (d.platformName == "myBleDevice") {
         await d.connect();
    }
}
```
### 5. スキャンフィルターが間違っている
スキャンフィルターが間違っている可能性があります。すべてのスキャンフィルターを一旦削除してみてください。また、withServicesを使用する場合、デバイスがadvertisingでそのサービスUUIDを積極的にサポートしている必要があります。

### 6. Android: startScanを頻繁に呼び出しすぎている
Androidでは、30秒間に5回以上startScanを呼び出すことはできません。これはプラットフォームの制限です。そのため、スキャンの頻度を調整するか、必要に応じてタイミングをずらしてスキャンを行うようにしてください。
Springでcsrf対策を行った処理に対してPostmanでアクセスする方法

# 前提条件
* Spring SecurityのCSRF対策として、CookieCsrfTokenRepositoryを用いた方法を採用していること

# やり方

## うまく行かなかったケースについて
本来なら、PostmanでCookieからXSRF-TOKENの値を読みだして、その値を環境変数に設定。
そして、次回のリクエスト送信時に、リクエストヘッダのX-XSRF-TOKENにその環境変数送信するように設定して、前回実行時のクッキーXSRF-TOKENの値を送信する、というやり方をするのが本来かと思います。
ネットでもそのような方法がいくつか紹介されている記事を見ました。

しかし、当方が使用しているPostmanの最新のバージョン（2024/8/23現在）だと、処理後にクッキーを取得して環境変数に入れるところまではうまく行くのですが、その後、リクエストヘッダに環境変数を設定しても、設定した環境変数をうまく読み込んでくれません。

行ったことは、以下のような感じです。

```javascript:Testタブ
var csrfToken = pm.cookies.get("XSRF-TOKEN");
pm.globals.set("csrf-token", csrfToken);
console.log(pm.globals.get("csrf-token"));
```
上記のように、設定した値をコンソールに出力すると、確かに、クッキーのXSRF-TOKENの値が出力されていることは確認できました。

```javascript:Testタブ
var csrfToken = pm.cookies.get("XSRF-TOKEN");
pm.environment.set("csrf-token" ,csrfToken);
console.log(pm.environment.get("csrf-token"));
```
上記についても同様です。

しかし、上記２つのいずれのケースにおいても、これを実行後に、Headerタブで
| Key | Value |
|:-:|:-:|
|X-XSRF-TOKEN|{{csrf-token}}|

と設定しても、サーバー側に飛ばされたリクエストのリクエストヘッダのX-XSRF-TOKENの値は、{{csrf-token}} となってしまい、クッキーのXSRF-TOKENから読みだした値にはなってくれませんでした。

## 解決法
あまりスマートな方法ではありませんが、テスト用にCSRFトークンを取得するためのコントローラーを用意しました。
srcフォルダ配下だとビルド対象として組み込まれてしまうので、mockフォルダを別途作成して、それをビルドパスとして設定します。ローカル環境でのみ有効なmockを作成する感じです。

eclipseだと、以下のような感じになります。

![eclipse.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3863714/3b0c4c16-d3a6-68b2-9a80-b29212051b3d.png)
そして、mockフォルダ配下に、公式サイトで紹介しているような、csrfエンドポイント用のコントローラークラスを作成します。

https://spring.pleiades.io/spring-security/reference/servlet/exploits/csrf.html#csrf-integration-mobile

以下、作成するコントローラーです。
公式サイトからの引用となります。
```java
@RestController
public class CsrfController {

    @GetMapping("/csrf")
    public CsrfToken csrf(CsrfToken csrfToken) {
        return csrfToken;
    }

}
```
これを実行すると、以下のようなJSONが出力されます。
```json:
{
    "token": "270e0283-b0d6-46fa-a072-0eccf2706609",
    "parameterName": "_csrf",
    "headerName": "X-XSRF-TOKEN"
}
```
注意点は、もしログイン認証などでアクセス制御を行ってる場合は、上記エンドポイントは、``.requestMatchers("/csrf").permitAll()`` の対象とする必要があるということです。
ですので、SecurityConfigにアクセス許可設定を別途する必要があります。
リリース対象のSecurityConfigに設定が残ってしまうのは気持ち悪いですが、上記CsrfControllerはリリース対象には含めないので、まぁ問題ないかなと思います。

そして、上記後、PostmanのPre-request Scriptタブを以下のような感じに設定します。
![prerequestscript.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3863714/432f3f5c-dac5-d026-f63e-2fdfd644bc43.png)

設定内容は以下の通りです。
```javascript:Pre-request Scriptタブ
console.log("csrf取得");
pm.sendRequest("http://localhost:8080/sample/csrf", function (err,res){
    var csrfToken = res.json().token;
    pm.environment.set('csrf-token', csrfToken);
    console.log("取得したtoken:"+csrfToken);
});
```
最後に、Postmanのヘッダタブを以下のような感じに設定します。

![header.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3863714/a23d53bf-f3de-9ae0-2fac-e3129f1633df.png)
<br>
**設定内容**
| Key | Value |
|:-:|:-:|
|X-XSRF-TOKEN|{{csrf-token}}|

以上で、Spring SecurityにてCSRF対策済みのAPIにPostmanでアクセスできるようになります。

# 参考サイト
以下の記事を参考にさせていただきました。

https://zenn.dev/nayushi/articles/760ea06a3a9585
よわよわエンジニア、UI・UX修行を始める ~有名なアプリと一緒なら良くないですか！？~

## はじめに
本記事では、UI・UXデザイン方面でつよつよになりたい筆者が、UI・UXデザインに取り組んでいくなかで身に染みて感じたことを書いています。

## 有名なアプリと一緒なら良くないですか？ダメ？

有名なアプリで採用しているUIだからこれでいいよね！ベンチマークヨシ！
……と、ついやりたくなりませんか？　筆者はなります。
有名なアプリと一緒、と言われれば、確かに納得感はあります。説明もしやすいです。

筆者はそうやって決めたUIについて、「本当にこのUIが良いと思ったのか」を問われ、答えられなかったことがあります。

## 錦の御旗はあるようで、ない

世間一般で有名なアプリのUIが正しく常にモダンである……ということはありません。
「UIが改悪された！」「前のほうがよかった！」という批判を目にすることは普通にあります。
世の一流のUI・UXデザイナーの人たちも、試行錯誤しながらより使いやすく、よりユーザーに喜ばれるUI・UXを日々検討しているわけです。
時には良かれと思って入れた変更によって、想定外の批判にさらされたりしながら。

「若干見映え悪いけど有名なアプリで採用されてるならこれでいいや……」は、UI・UXデザイナーとしては敗北どころか、敵前逃亡なのです。
「若干見映え悪い」なら、その見映え悪いポイントがどこなのかを深掘り、言語化すべきです。
もしくは、より良いUI・UXの例を求めて、ベンチマーク元のアプリ以外のアプリのデザインをもう少し調べるべきです。

逆に言えば、「このアプリの見映えはモダンでとても良い」「このUXはスムーズでわかりやすくて良い」というUI・UXがあるのならば、良いところを深掘りし、言語化しておくべきでもあるのです。

## さいごに

UI・UXデザインについては「コントロールの配置や文言が論理的に正しいか」以外にも、「見映えが良いか」や「UXの流れが途切れずスムーズか」など、「ユーザーがどう感じるか」という観点があるように思っています。
正解がないので難しいところではありますが、経験を積んでより良いデザインを提案できるようになっていきたいです。
内積と勾配

## 内積
ベクトルaとベクトルbの内積は、
```
a・b = (1, 3)・(4, 2) = 1×4 + 3×2 = 10
```
のように計算する。

このベクトルの成分がDまであるとすると、
```
a・b = a0×b0 + a1×b1 + … + aD×bD = Σai×bi
```
iは0からDの値で和記号に直せる。

逆に、和記号Σは内積として計算できる。

Pythonではfor文の和よりも内積として計算した方が処理として高速になる。

```python
import numpy as np 

a = np.ones(1000)
b = np.arange(1, 1001)
print(a.dot(b))
```

```実行結果
500500.0
```

## 勾配
`f = w0^2 + 2×w0×w1 + 3`の勾配を表示する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3850581/0d51791d-027b-d53c-8e66-112e3ac0e6a4.png)

上記のグラフの傾斜具合を求める際は関数を偏微分する。
このとき求められる式はfのwに関する勾配、または勾配ベクトルと呼ぶ。

実際に偏微分した式を使って勾配を等高線と勾配ベクトルで表示する。

```python
import numpy as np 
import matplotlib.pyplot as plt

def f(w0, w1):          # fの定義
    return w0**2 + 2 * w0 * w1 + 3

def df_dw0(w0, w1):     # fのw0に関する偏微分
    return 2 * w0 + 2 * w1

def df_dw1(w0, w1):     # fのw1に関する偏微分
    return 2 * w0 + 0 * w1

w_range = 2
dw = 0.25
w0 = np.arange(-w_range, w_range + dw, dw)
w1 = np.arange(-w_range, w_range + dw, dw)
wn = w0.shape[0]

ww0, ww1 = np.meshgrid(w0, w1)

ff = np.zeros((len(w0), len(w1)))
dff_dw0 = np.zeros((len(w0), len(w1)))
dff_dw1 = np.zeros((len(w0), len(w1)))

for i0 in range(wn):
    for i1 in range(wn):
        ff[i1, i0] = f(w0[i0], w1[i1])
        dff_dw0[i1, i0] = df_dw0(w0[i0], w1[i1])
        dff_dw1[i1, i0] = df_dw1(w0[i0], w1[i1])

plt.figure(figsize=(9, 4))

# 等高線
plt.subplots_adjust(wspace=0.3)
plt.subplot(1, 2, 1)
cont = plt.contour(ww0, ww1, ff, 10, colors='k')
cont.clabel(fmt='%2.0f', fontsize=8)
plt.xticks(range(-w_range, w_range + 1, 1))
plt.yticks(range(-w_range, w_range + 1, 1))
plt.xlim(-w_range - 0.5, w_range + .5)
plt.ylim(-w_range - .5, w_range + .5)
plt.xlabel('$w_0$', fontsize=14)
plt.ylabel('$w_1$', fontsize=14)

# 勾配ベクトル
plt.subplot(1, 2, 2)
plt.quiver(ww0, ww1, dff_dw0, dff_dw1)
plt.xlabel('$w_0$', fontsize=14)
plt.ylabel('$w_1$', fontsize=14)
plt.xticks(range(-w_range, w_range + 1, 1))
plt.yticks(range(-w_range, w_range + 1, 1))
plt.xlim(-w_range - 0.5, w_range + .5)
plt.ylim(-w_range - .5, w_range + .5)

plt.show()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3850581/e2e57823-5a7d-575e-1343-144a312378f1.png)

勾配は、傾きの最も大きい方向とその大きさを表す。
SSMSでSaving changes is no permitted（変更が許可されていません）という表示が出る問題

### SSMSでデータベースが保存できない問題

SQL Server Management Studio 20においてDatabase内の表を編集し、保存したところ以下のようなエラーが出ました。

![スクリーンショット 2024-08-26 153710.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2626996/baa4d904-f22a-ecfc-4095-ecd9c9c6ae31.png)

設定を変更したところ解決したので、その手順を備忘録として残しておきます。

###### 1.ToolsタブのOptionsを開く
![スクリーンショット 2024-08-26 154103.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2626996/88bc2545-a21f-94b6-377e-a1cc8b23492d.png)


###### 2.DesignersのTable and Database Designersを選択（検索欄にDesと打ち込めば出てくる）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2626996/8f197a4a-0880-6c36-dc5c-e5a10f32512e.png)

###### 3.Prevent saving changes that require table re-creation（テーブルの再作成が必要になる変更を保存しない）のチェックマークを外す
![スクリーンショット 2024-08-26 154548.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2626996/2f998907-02dd-477b-02df-0f4238a03634.png)
👇
![スクリーンショット 2024-08-26 154729.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2626996/aedd4476-5e13-775c-294b-3fdda1863317.png)

###### 4.OKを押す

:::note info
ちなみに、こちらのサイトではより詳細に説明がされています。
https://learn.microsoft.com/ja-jp/troubleshoot/sql/ssms/error-when-you-save-table
:::
輪読会で品質担保のベストプラクティスを知る

本記事では、プロジェクトチームで輪読会を実施して得た学びと成果物を紹介します。

#### 対象書籍 ： 「この１冊でよくわかるソフトウェアテストの教科書 [増補改訂 第２版]」
ソフトウェアテストにおける基本的な考え方、テストケースの作り方、テスト技法などが、初心者にもわかりやすくまとめられた書籍です。

## 目次
1. 輪読会の目的
2. 輪読会の進め方
3. 学び
4. アウトプット
5. 今後の方針
6. まとめ

## 1. 輪読会の目的
今回の輪読会の目的は、現行業務における品質の向上です。
私たちは、一つのシステムを数年単位で保守運用しているため、積み重ねてきたデバッグの実施方法や観点があります。
しかし、新しいメンバーが参画した際に、改めてデバッグに関する説明をしようとした際に、体系的にまとめて伝えることができず、感覚的な視点でデバッグをしていたのではないかという問題にぶつかりました。
これをきっかけに、品質に対して見落としがあるのではないかという懸念も生まれ、メンバー全員で改めて知識のインプットを図ることにしました。
輪読会という方法をとることによって、メンバーの参画時期問わず、全員が品質について体系的に学び、改めて知識の整理ができるようになることを目指しました。そして、その学びをアウトプットして業務に反映することで、システムの品質向上を図ります。

## 2. 輪読会の進め方
(1) 事前に指定された範囲を読む
(2) 各メンバーが共通ページに以下を記載する
* 気になった文章のピックアップ
* 疑問点
    * 自分で調べて解消した疑問点も含む
* 感想
    * 業務で活かせそうなこと
    * 気がついたこと
    * 新しく知ったこと

(3) 週一回の輪読会（20~40分程度）で内容を共有する
(4) 共有した内容を各回の担当者がまとめる
(5) 書籍から得た知識をアウトプットする

## 3. 学び
* システムリリース当初は「魅力的品質」だった機能も、時間が経つと「当たり前品質」になってきて要望も増えてくる
    * ユーザーからの要望/意見を取り入れることで、「魅力的品質」に近づくことができるかもしれない
* 内部構造を踏まえた上で、各機能のテスト時に何を確認するべきか意識することが大切である
* 自動テストの追加時に、機能確認テスト・制御フローテスト・データフローテストに分類してテストを抽出することで、目的によって実施すべき試験を明確にすることができ、テストの質が向上すると考えられる
* 作業者によって精度にばらつきが出てしまうことを防ぐためには、テストドキュメントを充実させる必要がある
    * 具体的に必要な資料としては以下をピックアップした
        * 機能一覧表：機能の把握・影響範囲の検討という点で、作成するメリットが大きい
        * テスト観点一覧表：テスト抽出の漏れが軽減される
* デバッグ開始前に、あらかじめ重点的に確認すべき項目を整理しておくことが重要である

## 4. アウトプット
学びを成果物としてアウトプットするにあたり、以下の方針を定めました。
* 業務効率を向上させるものを作成する
* お客様にとってもメリットあるものを作成する
* 叩き台でも良いのでまずは作成し、必要に応じてブラッシュアップを重ねる

これらの方針に則って、書籍で挙げられたドキュメントの中から以下を実際に作成することとしました。
* 機能一覧表
    * 機能の把握、影響範囲の検討に有用と判断
* テスト観点一覧表
    * 単体テストや結合テストでの観点漏れを防止するほか、デバッグ前の各テスト項目における重要度の判断時に役立つ
* バグ検出タイミングを整理した資料
    * バグ検出のタイミングに対する課題を可視化し、改善を図る
    * 本資料はこれまでにも作成していたが、改めて重要性を認識


## 5. 今後の方針
（1）輪読会の継続
（2）アウトプットを増やす
（3）資料をブラッシュアップする
（4）成果物を実際に活用して品質担保し、サービスの質の向上に繋げる

## 6. まとめ
輪読会では、書籍の内容を自分たちの業務に当てはめながら、現状における改善点や取り入れるべきことは何かを意識して読み進めました。
その結果、現在の運用や品質担保の状況を客観的に振り返り、開発者向けの資料、特に機能一覧やテスト観点の一覧といった、テスト漏れを防止するための資料に改善の余地があるという課題を見つけることができました。
また、バグ検出タイミングを整理した資料のような、これまでにも作成していたドキュメントの重要性を改めて認識する機会となりました。
今後は成果物の作成を通じて先述した課題を解消し、より高い品質でのサービス提供を目指していきます。

## 参考
[この１冊でよくわかるソフトウェアテストの教科書 [増補改訂第２版]](https://www.amazon.co.jp/%E3%80%90%E3%81%93%E3%81%AE1%E5%86%8A%E3%81%A7%E3%82%88%E3%81%8F%E3%82%8F%E3%81%8B%E3%82%8B%E3%80%91-%E3%82%BD%E3%83%95%E3%83%88%E3%82%A6%E3%82%A7%E3%82%A2%E3%83%86%E3%82%B9%E3%83%88%E3%81%AE%E6%95%99%E7%A7%91%E6%9B%B8-%E5%A2%97%E8%A3%9C%E6%94%B9%E8%A8%82-%E7%AC%AC2%E7%89%88-%E5%B8%83%E6%96%BD/dp/481560875X)
OSPFの設定演習

# はじめに
こちらの動画の設定演習をプレイしました。
https://www.youtube.com/watch?v=pLP6IUBb8J4&t=307s

# OSPFの設定演習
![スクリーンショット 2024-08-26 115615.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3715252/abb62e1f-ba6a-493c-aab3-6318d3d8970f.png)
以上の構成図をパケットトレーサーで再現し,ClientからTR(target Router）までの疎通確認をします。

![スクリーンショット 2024-08-26 143723.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3715252/8d5fef41-d15d-5c22-bbc0-2504e2cd310f.png)

### 各ルータに設定それぞれ設定します

###### RT1
``` 
Router(config)#hostname RT1
RT1(config)#interface gigabitEthernet 0/0 
RT1(config-if)#ip address 172.16.1.254 255.255.255.0 
RT1(config-if)#no shutdown 

RT1(config)#interface Loopback 0
RT1(config-if)ip address 1.1.1.1 255.255.255.255

RT1(config)#interface gigabitEthernet 0/1
RT1(config-if)ip address ip 192.168.10.1 255.255.255.0
RT1(config-if)no shutdown

RT1(config)#Router OSPF 1
RT1(config-router)#network 172.16.1.0 0.0.0.255 area 0
RT1(config-router)#network 192.168.10.0 0.0.0.255 area 0

```
##### RT2 
``` 
router(config)#hostname RT2
RT2(config)#interface gigabitEthernet 0/0
RT2(config-if)#ip address 192.168.10.2 255.255.255.0 
RT2(config-if)#no shutdown 
RT2(config)#interface gigabitEthernet 0/1
RT2(config-if)#ip address 192.168.100.1 
RT2(config-if)#no shutdown 
RT2(config)#interface Loopback 0
RT2(config-if)#ip address 2.2.2.2 255.255.255.255 

RT2(config)#router ospf 1
RT2(config-router)#network 192.168.10.0 0.0.0.255 area 0
RT2(config-router)#network 192.168.100.0 0.0.0.255 area 0

```
###### RT3 
```
router(config)#hostname RT3 
RT3(config)#interface gigabitEthernet 0/0 
RT3(config-if)#ip address 192.168.100.2 255.255.255.0 
RT3(config-if)#no shutdown
RT3(config)#interface gigabitEthernet 0/1
RT3(config-if)#ip address 192.168.100.2 255.255.255.0 
RT3(config-if)#no shutdown
RT3(config)#interface Loopback 0
RT3(config)#ip address 3.3.3.3 255.255.255.255
RT3(config)#ip route 0.0.0.0 0.0.0.0 10.10.10.254
RT3(config)#router ospf 1
RT3(config-router)#network 192.168.100.0 0.0.0.255 area 0
RT3(config-router)#network 10.10.10.0 0.0.0.255 area 0
RT3(config-router)#default-information originate
```
#### TR(target Router)
```
Router(config)#hostname TR
TR(config)#interface gigabitEthernet 0/1
TR(config-if)ip address 10.10.10.1 255.255.255.0
TR(config)ip route 0.0.0.0 0.0.0.0 192.168.100.1
```

###### Client 

![クライアント.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3715252/67fa5654-0311-eb87-e3a6-60a500fd4322.png)

###### PING

![スクリーンショット 2024-08-26 150854.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3715252/5b7068ab-dd84-35e1-6fe2-47a9588244af.png)

ピング成功しました。みなさんもぜひチャレンジしてみてください～！！
WioLTE: i2c のスキャン

## プログラム

```text:i2c_scanner.ino
// ---------------------------------------------------------------------
//	i2c_scanner.ino for WioLTE
//
//						Aug/30/2024
// ---------------------------------------------------------------------
#include <WioLTEforArduino.h>
#include <Wire.h>
#define	PROGRAM	"i2c_scanner.ino"
#define	VERSION	"2024-8-30 PM 12:11"

WioLTE Wio;
// ---------------------------------------------------------------------
void setup()
{
	SerialUSB.println("### I/O Initialize.");
	Wio.Init();
  
	SerialUSB.println("### Power supply ON.");
	Wio.PowerSupplyGrove(true);
	delay(500);

	Wire.begin();

	delay(1000);
	SerialUSB.println(PROGRAM);
	SerialUSB.println(VERSION);
	SerialUSB.println();
	delay(1000);
}

// ---------------------------------------------------------------------
void loop()
{
	byte error, address;
	int nDevices;

	SerialUSB.println("Scanning...");

	nDevices = 0;
	for(address = 1; address < 127; address++ ) 
	{
		// The i2c_scanner uses the return value of
		// the Write.endTransmisstion to see if
		// a device did acknowledge to the address.
		Wire.beginTransmission(address);
		error = Wire.endTransmission();

		if (error == 0)
		{
			SerialUSB.print("I2C device found at address 0x");
			if (address<16) 
				SerialUSB.print("0");
			SerialUSB.print(address,HEX);
			SerialUSB.println("	!");

			nDevices++;
		}
		else if (error==4) 
		{
			SerialUSB.print("Unknown error at address 0x");
			if (address<16) 
				SerialUSB.print("0");
			SerialUSB.println(address,HEX);
		}		
	}
	if (nDevices == 0)
		SerialUSB.println("No I2C devices found\n");
	else
		{
		SerialUSB.print("done   ");
		SerialUSB.print("nDevices = ");
		SerialUSB.println(nDevices);
		}

	delay(10000);
}

// ---------------------------------------------------------------------

```

## Arduino IDE
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179446/eba4f8fd-c493-727d-1378-e7583a4bbb9a.png)
ネットワーク～単体試験・結合試験～

# はじめに
以前、「[ネットワーク～初期導入作業（キッティングetc）～](https://qiita.com/m-yoshimura/items/86180695a36b67e43bd7)」という記事を投稿しましたが、今回は後続作業となる「ネットワーク～単体試験・結合試験～」について書こうと思います。
現場によって試験区分や試験内容は異なる部分があるので、まったく一緒というわけではありませんが、本番稼働前に最低限こうゆう試験を行うんだなと、参考になればとよいかと思っています。:muscle::bangbang:


# 試験の目的
新規構築にしても、既存環境のリプレースでも本番環境に組み込む前に問題ないよね！
と、事前に確認し品質を担保することが単体試験であり結合試験の目的であります。
そのほかにも、、

:::note
試験時に問題があった場合でも、設定修正など対処できること
:::

:::note info
お客様への納品物としてのエビデンス（動作が問題ないこと確認しました！）
:::
ということを担っていたり。
なので、試験手順の誤りやエビデンス不備は再試験したりと入念に行います。


# 試験の内容
+ ### 単体試験
設計を元に作成したパラメータシートとキッティングした機器のコンフィグチェックを行います。
パラメータシート通りの設定が入っているか、余計な設定が実機に入ってないかを確認します。
デフォルトで有効になっている設定など、デフォルト値で問題ないか調査したり、不要な機能であれば無効にするなどチューニングを行うこともあります。

:::note warn
キッティング後、機器単体で確認できることは限られているため、コンフィグチェック以外の確認は結合試験でという現場もあるかと思います。
:::


+ ### 結合試験
周辺機器と接続した状態で、各機能の正常性確認や、障害＆復旧時の動作確認を行います。
構築場所によって、倉庫などキッティング＆試験できるスペースがある場合は、事前に試験を行うこともあります。
但し、事前の試験はあくまでも疑似環境で行うので本番環境でも同様の試験は行います。

# 試験の項目
単体試験は基本的にコンフィグチェックとして、結合試験の項目をあげていきます。

+ ### 正常性試験
設定した機能がちゃんと動作するかの確認試験です。
下記は一般的な項目例で、+αで使う機能や運用に必要な手順を全て確認します。

|# | 区分 |試験項目 |補足 |
|:-:|:-:|:-:|:-:|
|1 |基本機能|IF確認||
|2 |〃   |LAG確認||
|3 |〃   |ルーティング確認|static、eigrp、ospf、bgp、rip等々|
|4 |〃   |疎通確認||
|5 |〃   |冗長機能確認|stack、vpc、vrrp、ha等々|
|6 |〃   |FWポリシ機能確認|許可と拒否ポリシーの動作確認|
|7 |〃   |負荷分散機能確認|テスト用VSの分散動作確認|
|8 |運用機能 |ログイン確認||
|9 |〃   |NTP同期確認||
|10|〃   |syslog確認||
|11|〃   |SNMP確認||
|12|〃    |バックアップ手順確認||
|13|〃    |リストア確認手順||


+ ### 障害試験
各障害を発生させた時と復旧させた時に、設定した機能がちゃんと動作するかの確認試験です。
冗長機能によるステータス遷移や、通信断の時間、経路切替が想定通りかを確認します。
主な項目としては下記２つがあります。

|# |区分|試験項目|補足  |
|:-:|:-:|:-:|:-:|
|1 |リンク障害|障害発生|冗長箇所や経路切替が生じる箇所で実施|
|2 |〃|障害復旧||
|3 |筐体障害|障害発生|全筐体に対して電源OFF/ONで実施|
|4 |〃|障害復旧||


+ ### その他試験
他にも、構築する機器やアプライアンスによっては性能試験や負荷試験、ペネトレーション試験などツールを用いて試験を行う場合もあります。


# 試験項目書
試験項目書は、試験内容や手順、試験の期待値と結果などを纏めた資料です。
試験前に作っておいて、試験結果や試験日などを記載し、エビデンスとともにお客様に納品物として提出します。
試験数が1000を超える場合もあるので、試験消化数のサマリを冒頭に作っておくと進捗状況の把握もできます。

**※試験項目書サンプル**
![無題②png.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/537417/6e597441-381a-cb05-9acc-a12d8b1bd9bf.png)


また、試験構成図も作っておくと便利です。
障害ポイントに試験項目書の項番を振っておいたり、試験端末をどこに繋いで通信確認を行うかなど試験内容と構成把握に役立ちます。

**※試験構成図サンプル**
![無題.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/537417/4e2ecfe9-a1fa-1df9-f8c6-d8c2dc49666d.png)


# 試験実施＆エビデンス取得
試験項目書に沿って実際に試験を行いながら、同時にエビデンスも取得していきます。
エビデンスは、機器のログだったりキャプチャ画面だったり試験の期待値が確認できるものになります。
機器ログの取得は、マクロを使って自動化したり効率化する方法もあります。
またエビデンスのファイル名に試験項目書の項番をつけると、再鑑がしやすくなります。

**※ファイル名サンプル**
<大項番>-<中項番>-<小項番>_機器ホスト名_yyyymmdd.log
➡01-05-78_hoge-l3sw_20240827.log

# 最後に
試験を行う側、納品物をチェックする側も経験しましたがどちらも大変な作業です。
ただ、ネットワークをつくるためには必要な工程であり、責任を感じつつもやりがいがある作業です。
最近は仮想基盤上やクラウド上での構築機会も増えているので、どういった試験が必要か考えるのも楽しみのひとつでもあります。
 
＃試験や検証では、色々と役立つツールやソフトがあります。
＃これらはまたの機会に紹介できればと思います。

では。m(_ _"m)
Ubuntuにchromedriverをインストールする

## はじめに
以前、こちらの記事を書いたことがありますが、そのバージョンアップ版になります。
https://qiita.com/pyon_kiti_jp/items/0ba98f140142d4ac6b25

## 環境
Ubuntu20.4

## chromedriverのダウンロードサイト
chromedriver（Chrome用のWebDriver）のダウンロードサイトは、バージョン114を境に２つ存在しています。

バージョン114以下を取得したい場合は、次のサイトからダウンロードします。（URLはどちらもで同じです）
- https://sites.google.com/chromium.org/driver/downloads
- https://chromedriver.chromium.org/downloads

バージョン115以上を取得したい場合は、こちらのサイトからダウンロードします。
- https://sites.google.com/chromium.org/driver/downloads

参考サイト
https://gammasoft.jp/support/chrome-webdriver-for-ver115-and-newer/

## 最新版のchromedriverのダウンロードパスを調べる

つまり、最新版を取得したい場合は、こちらのサイトから取得することになります。
- https://sites.google.com/chromium.org/driver/downloads

画面の上部にある、タイトル「Current Releases」の下にある赤文字の「the Chrome for Testing availability dashboard」のリンクをクリックします。

![無題01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/94029/4e965c90-d0b6-f8d6-8aa1-5475b5380a14.png)

ChannelのStableをクリックします。Stableは安定版の意味です。Binaryでchromedriver、Platformでlinux64の列のURLが、ダウンロード元のURLになります。このURLをコピーします。

![無題02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/94029/55dbd365-0dd5-2422-82d4-4b0d9c6eb436.png)

## 最新版のchromedriverをダウンロードする

chromedriverはzipで圧縮されています。展開する必要があるため、zipが入っていなければ、インストールする必要があります。

ダウンロードファイルは取り敢えずtmpにいれます。ダウンロードはcurlで行います。

```
$cd /tmp/
$curl -O https://storage.googleapis.com/chrome-for-testing-public/126.0.6478.126/linux64/chromedriver-linux64.zip
```

chromedriver-linux64.zipがダウンロードされたことを確認します。

```
$ls -l
```

zipを展開すると、chromedriver-linux64が現れます。

```
$unzip chromedriver_linux64.zip
$ls -l
```

chromedriver-linux64の配下にある、chromedriverだけを、任意の場所に配置します。自分はこちらに配置することにしました。パスが通っているフォルダであればどこでもいいようです。

```
$sudo mv ./chromedriver-linux64/chromedriver /usr/local/bin
```

移動された事を確認します。

```
$which chromedriver
```

インストールが完了したので、ダウンロードした元ファイルは削除します。

```
$rm -rf chromedriver-linux64
$rm chromedriver-linux64.zip
```

削除された事を確認します。

```
$ls -l
```
フリーランスでも加入できる社会保険の新サービスが爆誕

# はじめに
「社保の窓口」という個人事業主(フリーランス)でも加入できる社会保険サービスが広告で流れてきたので調べたものをまとめました。

https://syahomado.com/?page=1

# 社保の窓口について
「社保の窓口」を運営する一般社団法人シャホマドの理事となります。
毎月96,000円をサービス利用料として一般社団法人シャホマドへ支払いし、
シャホマドから毎月56,000円の役員報酬を受け取ります。
そのため、実質40,000円かかりますが、社会保険に加入できます。

表にすると
|項目  |金額  |
|---|---|
|サービス利用料  |-96,000円  |
|役員報酬  |+56,000円  |
|合計  |-40,000円  |

毎月アンケート業務があります。
社会保険は協会けんぽに加入し、保険サービスが受けられます。

# 加入のメリット
+ 手取りが増える
+ 年金が増える

### 手取りが増える
個人事業主になると、国民年金と国民健康保険料を支払うことになります。
[こちら](https://paytner.co.jp/paytter/freelance/2893/)のサイトを元に、年収が1,000万円の人は、年金+国保で毎月換算すると8万7000円ほど支払うことになります。
しかし、社保の窓口を利用すると、4万円で済むため、約4万7000円安くなります。
年間だと、約57万円ほど安くなります。

|項目  |月額負担  |年間負担  |
|---|---|---|
|通常  |87,000円  |1,000,000円  |
|社保の窓口  |40,000円  |480,000円  |
|差  |47,000円  |520,000円  |

### 年金が増える
[こちら](https://logi-assurance.co.jp/logistics/syahonomadoguchi-kuchikomi/)のサイトによると、「国民年金のみ」と「国民年金+厚生年金」では将来の受給額が単月で見ると約10万円も違ってくるそうです。

以上のメリットがあります。
個人事業主になると、会社員で契約していた社会保険を任意で継続するか国民健康保険に加入することになります。

支払う年金は厚生年金+国民年金から国民年金になります。国民年金は会社員の頃は会社が半分を支払ってくれますが、個人事業主になると支払い額が増えます。

# まとめ
個人事業主が加入できる社会保険はグレーな面が多いようです。YouTubeで検索すると如実に分かりますが、有名YouTuberの解説動画があります。
こういうサービスは色々と賛否両論があるので静観していきたいです。
※加入を促す意図ではありません。

# 関連サイト

https://qiita.com/townsoft/items/888145f82c1e24d3315f

https://qiita.com/xperia0919/items/cb28217d76aac52a8982

https://qiita.com/moroi/items/08b79d976cb45a4d0fa6
PostgreSQLあれこれ

溜まってきたので、一覧化しました

https://qiita.com/Jasmine_teeeeee/items/3c0bb56f400ff7fee517


https://qiita.com/Jasmine_teeeeee/items/fcd060c83b45d21d4e0d

https://qiita.com/Jasmine_teeeeee/items/c781c3366a68143a1921

https://qiita.com/Jasmine_teeeeee/items/2fc6bb8b5a292fc0ccb6

https://qiita.com/Jasmine_teeeeee/items/e978aec06b1b87865c2d
OutsystemsでInput Widget（Number）にInteger型変数をバインドしてハマった話

## 何にハマったか
Outsystemsの画面入力項目にバインドした変数が0になってしまう。
調べていくと、画面入力項目に小数を入力した場合にバインドした変数が0になることが分かった。

## 何をしてしまったか
入力とボタンを配置した簡単なプログラムを作成した。
入力を整数として変数に保持する。

Outsystemsでは入力項目（Input Widget）に対してInput Typeを設定することができる。
また、Variable項目に変数を指定し、入力や変更をその変数に反映することができる。
以下の例は、数値の入力項目（Input_Var_Integer）に数値（Number）を入力することができ、入力値をVar_Integer変数（Integer）に反映させるコードとなる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3796185/68cef5e7-a3ad-e389-0025-3f47a84f6a09.png)

Input Typeに設定できる値は以下の通り。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3796185/48639779-e581-c68a-7742-bc47d99fd095.png)

## 何が起きたか
上記のコードを実行し、値を設定してみる。

・整数の場合は変数に正しい値が反映
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3796185/a111a2e3-a6cf-c39e-e2d1-cd2af6f5634a.png)

・未入力の場合は変数に0が反映（直感的にわからないでもない）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3796185/0d3da2a2-6265-2827-0e78-5a5d9ef059de.png)

・小数の場合は変数に0が反映（！？）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3796185/106f0135-d659-48c3-9673-0427b83b44b9.png)

C#でfloatをintにキャストしたときのように、100が入るものかと先入観が働いていた。
```C#
      float f = 100.123f;
      int i = (int)f;
```
0になるという予想をしていなかったため、原因箇所はほかにあると思い込んでしまい、原因特定に時間がかかってしまった。

## 結論
Input Widgetにバインドされた変数を参照する前に、FormのValidationがクリアされていることを確認しましょう！

## 余談
Validationをクリアしていない入力項目の値はNullになるのかと予想した。
Email型で検証したところ、Validationをクリアできない値を入れた場合でもバインドされた変数に入力された値が格納されていた。
社外のLT登壇にあたって準備したこと、意識したこと、大切だと思ったマインド

# はじめに
先日、ご縁あってFindyさん主催のLTイベントに登壇させていただきました！
その登壇にあたって私なりに準備したこと、意識したこと、どんなマインドが大切だと感じたのかを備忘録として残したいと思い記事を作成しました。
これから登壇する機会がある方や、登壇してみたいと思っている方の参考になれば幸いです！

当日発表したスライドも載せておきます。もし、興味ございましたら一読していただけると幸いです！

https://findy.connpass.com/event/324444/

https://speakerdeck.com/takashiaoki5816/qian-zhi-toxian-zhi-degan-zita-sukuramunowei-i

# 準備したこと
まず、登壇することが決まったあとは、何を準備する必要があるのかをざっと洗い出すところからはじめました。
主に、以下の順で準備を進めました。
- 今回の発表を通じて伝えたいことは何か
- スライドの構成
- スライドの叩き台の作成
- 発表内容の理解度を上げる
- 第三者視点に立って理解しづらい部分がないか
- 発表練習

## 今回の発表を通じて伝えたいことは何か
登壇前にFindyさんとの事前MTGがあったのですが、そのMTGで今回のLTで期待していることを共有していただきました。
その情報を元に、どの内容に重きを置くのかや今回の発表を通じて私が伝えたいことは何なのか考えました。
今回の発表では下記を最終的な目標としました。

`
スクラムを実践してるけど効果を実感できない、これからスクラムを導入してみたいと感じているチームに対して、私の前職と現職の経験が少しでも参考になり、スクラムを見直すきっかけや手がかりになれば幸い
`

こちらは、はじめは叩き台となる仮目標を立てたのち、スライド作成、発表練習していく中でブラッシュアップしていきました。

## スライドの構成
10分という限られた時間でどのようなスライドの構成にするのかを考えました。
先程の発表を通じて伝えたいことから考えて、どの内容に重きを置いた方が良いのか検討しました。

## スライドの叩き台の作成
スライドの構成を元にざっくり叩き台を作成しました。
最初はあまり時間をかけず、これは話したいと思った内容をピックアップしていきました。

## 発表内容の理解度を上げる
これは今回のケースによるのかもしれませんが、今回の発表は、３ヶ月ほど前に投稿した記事の内容について発表することになっていたので、まずその記事の内容を改めてインプットすることにしました。
記事の内容についての理解度もそうですが、前職など当時の記憶を思い出しながら理解を深めていきました。
また、今回の「スクラム」という領域についてもインプットがまだまだ足りていない状態だったので、改めてこの機会に「スクラムガイド」をしっかり読んでみたり、「ゾンビスクラムサバイバルガイド」という書籍を読み進めたりしました。
それらを読み進めていく中で、この時こういう状態に陥っていしまっていたななど、当時の状態についての理解度が深まりました。

## 第三者視点に立って理解しづらい部分がないか
理解度を深め、スライドを見直していく過程で第三者視点から見て理解しづらい部分はないか確認しました。視聴者を置き去りにするような発表は避けたいと思っていたので、理解しやすいスライドを意識していました。
スライドのデザインに関しましては、同じチームの@tachikouさんにご協力いただきました！
周囲にご協力いただくことで自身だけでは見えてこなかった視点が見えてくると思うので、より良い発表にするためにもご協力いただくことはすごく大切なことだなと感じました！

## 発表練習
ここに一番時間を割きました。頭にスライドを叩き込むくらいの気持ちで回数をこなしていくことを意識してました。
10分という時間に収まっているのか、理解しづらい部分はないか。
練習していく中で違和感を感じる部分や、ここはもう少し詳しく説明したいなという部分を発見できました。
個人的に一番大きかったことはスライドに対しての自信がついたことです。
回数をこなしていく中で、当然のことですがこのスライドに対して一番理解しているのは自分自身なんだという気持ちが芽生えるようになっていきました。
今まで、人前に出て発表することをできるだけ避けてきた人生を送ってきていたのに、当日そこまで緊張しなかったのはここが大きかったのかとも思っています。
勿論、運営さん・登壇者さんの方がよい雰囲気を作ってくださっていたのも、とても大きかったです！

## みんなマサカリを恐れる
個人的にとても衝撃だったのですが、登壇経験豊富な他の登壇者の方達もマサカリを恐れていたことでした。（実際はリラックスした雰囲気を作ってくださっていただけなのかもしれませんが。。）
私の中では、登壇の数をこなしていくことで自然とマサカリを恐れなくなるものだと勝手に思っていました。
この事を知れたことが私の中でもリラックスした気持ちになれた1つの要因だと思ってます。
はじめは、どんな逆質問がくるのか予想してアンサーを考えていたりしたのですが、そこに時間を割くのはあまり得策ではないなと感じました。用意した以外の質問がきた時、用意したアンサーを忘れてしまった時に焦ってしまうと思ったからです。この辺りに関しましては、同じチームメンバーの@TairaNozawaさんにも助言をいただきました！
用意したアンサーを使用するのではなく、今自分自身が思っていることをそのまま伝えればいいんだという考えにシフトしていきました。

# 最後に
登壇にあたって準備したこと、意識したこと、大切だと思ったマインドについて書き連ねていきました。
これから登壇する機会がある方や、登壇してみたいと思っている方の参考になれば幸いです！
今回の発表にあたって、ご協力いただいたスクラムチームの皆さんには大変感謝しております。この場を借りて改めて感謝申し上げます。

ここまで読んでいただきありがとうございました！