OmniAICreator/Qiita-1.07M huggingface dataset:
GitLab CI ジョブでファイルを作成したら消えていた話

GitLab CICD に興味があって色々な検証作業を行っていたところ、ジョブで作成したファイルが消えてしまうことがあったので、忘れないようにメモしておこうと思います。

## 検証環境

Ubuntuサーバに`GitLab`と`GitLab-Runner`をインストールしました。
`Executor: shell` の「プロジェクトランナー」を1つ登録して検証を行いました。

各種バージョンは次の通りです。
- Ubuntu 22.04.5 LTS
  - GitLab v17.8.6-ee
  - GitLab-Runner 17.8.0

## .gitlab-ci.yml

`.gitlab-ci.yml`の中身は次の通りです。
build ジョブで`hoge.txt`ファイルを作成できるのか？ test ジョブで`hoge.txt`を読み込めるのか？の検証となります。

### 予想される挙動

gitlab-runner のワーキングディレクトリは`/home/gitlab-runner`を設定しているため、`/home/gitlab-runner/`配下に`hoge.txt`が作成され、test ジョブで`hoge.txt`を読み込める、という挙動を予想しました。

```yaml
stages:
  - build
  - test

build-job:
  stage: build
  script:
    - echo "hoge" > hoge.txt
    - sleep 30 # hoge.txtが作成されたことを確認するためにスリープする

test-job:
  stage: test
  script:
    - HOGE_VARIABLE=$(cat hoge.txt)
    - echo $HOGE_VARIABLE
```

## パイプラインを動かす

パイプラインを動かしてみると、次のような結果となりました。
test ジョブが失敗したようです。

### build ジョブ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/759747/301e67ea-a391-432a-825f-4e80702cccd9.png)

`hoge.txt`は想定とは違い`$CI_PROJECT_DIR`配下に作成されました。
具体的には`/home/gitlab-runner/builds/t3_C2xT9/0/user1/project2`配下となります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/759747/861b949e-3614-4ba7-8860-f647dccd6957.png)

`$CI_PROJECT_DIR`は"定義済みのCI/CD変数"であり、公式ドキュメントによると「リポジトリのクローン先のフルパスと、ジョブの実行元」を表しているようです。

・定義済みCI/CD変数
https://docs.gitlab.com/ci/variables/predefined_variables/

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/759747/97edc136-fc37-4f33-a0ea-cd9e019a4d95.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/759747/f4c33949-0f1d-4fbc-a01a-ae5b06caab8a.png)

上記のスクリーンショットを見ても、確かに、`$CI_PROJECT_DIR`ディレクトリ配下に、リポジトリに登録しているファイル一式がクローン（コピー）されていることが分かります。

### test ジョブ

エラーになった test ジョブの実行ログを確認します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/759747/e5aed532-2bf4-4a1c-b5cb-838bdba4c5c7.png)

ジョブが失敗（エラー）した理由は、ログの15行目にある通り「hoge.txt No such file or directory」となります。
build ジョブにて`hoge.txt`が作成されたことを確認済みですが、これはいったいどういう事でしょうか。

ログを上に見ていくと、11行目に思いっきり「**Removing** hoge.txt」のメッセージが表示されています。
さらに、ログの9行目を見ると、既存の`$CI_PROJECT_DIR`配下を再初期化したようなメッセージが表示されています。

test ジョブ開始により`$CI_PROJECT_DIR`配下が再初期化された結果、**リポジトリに登録していない**（Git管理していない）`hoge.txt`は**消えた**のだと思われます。

そのため、`$(cat hoge.txt)`の箇所でエラーが出てしまったようです。

## ファイルが消えないようにするには？

「`$CI_PROJECT_DIR`配下に`hoge.txt`を作成しない」という方法があるかと思います。
例えば、`/home/gitlab-runner/`配下に`hoge.txt`を作成すると、test ジョブでも`hoge.txt`の読み込みができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/759747/00ca6c9a-9e12-4a7f-ad91-ae9a4b3b2d38.png)

この方法だと、パイプラインが完了した後でも`hoge.txt`が残り続けるため、`hoge.txt`がパイプライン実行中にだけ"一時的に使用したい"テンポラリファイルなのであれば、パイプライン完了後に削除するような仕組みを実装した方が良いかも知れません。
※後始末用ジョブを設けてそこで削除する、など。

実用性がある検証かどうかはアレですが、ジョブでファイルを作成する際は「どこに作成するか？」を気を付けたいと思います。

まだ検証を始めたばかりで、公式ドキュメントを読み込めば他にも方法が見つかりそうですが、そこはこれからやってみようと思います。
【Rails初学者】Heroku RedisをRails/Sidekiqに繋いでみる

【Rails初学者】Heroku RedisをRails/Sidekiqに繋いでみる
--------

こんにちは。
エンジニア転職を目指し、学習中の現役看護師です。
今回、RailsアプリをHerokuにデプロイしつつ、Redisを使ってSidekiqを動かすまでの手順を、表示されたエラーや対処も含めて備忘録のために、まとめてみました。

---

## この設定でできるようにしたいこと

- RailsアプリにHeroku Redisを導入したい
- Sidekiq（非同期処理）をRedisで動かしたい
- Redisの証明書エラーを解決したい

---

## 環境

- Ruby 2.7.7
- Rails 6.0.6.1
- Sidekiq
- Heroku（Miniプラン）
- Redis gem 4.8.1

---

## 1. Heroku Redisを追加

Heroku CLIでアドオンを追加：

```bash
heroku addons:create heroku-redis:mini --app アプリ名
```

## 数分待つと、以下のように環境変数が自動で設定される
REDIS_URL

REDIS_TLS_URL ← これがSSL接続用

## 表示されたエラー：SSL証明書エラー
```
SSL_connect returned=1 errno=0 state=error: certificate verify failed (self signed certificate in certificate chain)
```
Heroku Redisは自己署名証明書を使用しているため、そのままだとSSL接続時に失敗してしまうこともある。

##  Redis設定（config/initializers/redis.rb）
```
$redis = Redis.new(
  url: ENV["REDIS_TLS_URL"],
  ssl_params: { verify_mode: OpenSSL::SSL::VERIFY_NONE }
)
```
 REDIS_TLS_URL を指定し、verify_mode を VERIFY_NONE にするといいらしい。

 ## Sidekiqの設定（config/initializers/sidekiq.rb）
 ```
 Sidekiq.configure_server do |config|
  config.redis = {
    url: ENV["REDIS_TLS_URL"],
    ssl_params: { verify_mode: OpenSSL::SSL::VERIFY_NONE }
  }
end

Sidekiq.configure_client do |config|
  config.redis = {
    url: ENV["REDIS_TLS_URL"],
    ssl_params: { verify_mode: OpenSSL::SSL::VERIFY_NONE }
  }
end

```
##  動作確認
Herokuのコンソールから接続確認
```
heroku run rails console --app アプリ名
```
```
Rails.cache.write("test_key", "Hello Redis")
Rails.cache.read("test_key")
# => "Hello Redis"
```
これで値が返ってきたらRedis接続できている。

## エラーが表示された際の確認事項

| エラー内容                 | 原因                          | 解決策                                                |
|--------------------------|-----------------------------|-------------------------------------------------------|
| `certificate verify failed` | SSL証明書の検証失敗             | `ssl_params: { verify_mode: VERIFY_NONE }` を忘れてないか |
| `REDIS_TLS_URL` がない      | Redisアドオンがまだ作成中なのか     | `heroku addons:info` で確認                         |
| Sidekiqが動かない          | workerが停止中なのか              | `heroku ps:scale worker=1` を実行              |

## ActionCableなど他用途でも同じ対応が可能らしい
```
# config/cable.yml
production:
  adapter: redis
  url: <%= ENV["REDIS_TLS_URL"] %>
  channel_prefix: your_app_production
  ssl_params:
    verify_mode: <%= OpenSSL::SSL::VERIFY_NONE %>
```

## まとめ

-  **Heroku Redisは自己署名証明書なのでSSL設定が必須**
-  **`REDIS_TLS_URL` を使用（← `REDIS_URL` ではない）**
-  **`verify_mode: OpenSSL::SSL::VERIFY_NONE` を忘れない**

----

初学者のため、間違えていたらすいません。
引き続き、学習を進めます。
GROWI - データのみのバックアップ/リストア

## はじめに

[GROWI - Ubuntu 24.04 への導入と Windows C# クライアント](https://qiita.com/chai0917/items/8644b50d955a5cb1acc9) で導入した GROWI に対する、データのみのバックアップ/リストア手法を記載します。
GROWI 導入時 docker-compose.yml で volumes 指定をして、ホストでアクセス可能なディレクトリをマウントしたので、バックアップ/リストア作業はシンプルです。

### volumes 指定 参考資料
- [Docker Composeのトップレベルvolumesでホストのディレクトリをマウントする - ニューなんとなく書くブログ](https://blog.moo-channel.net/2017/04/24/docker-composenototupureberuvolumesdehosutonodeirekutoriwomauntosuru-2/)


## バックアップ
データのみのバックアップは、GROWI コンテナを停止して、必要なフォルダの tar アーカイブ作成となります。

```
# GROWI コンテナ停止
$ cd /opt/growi/growi
$ docker compose stop

# データのみバックアップ
$ cd /opt/growi/data
$ sudo tar -zcvf data.tar.gz growi_data mongo_db

# GROWI コンテナ開始
$ cd /opt/growi/growi
$ docker compose up -d
```

## リストア

データのみのリストアは、GROWI コンテナを停止して、不要データの削除後、tar アーカイブ展開となります。

```
# GROWI コンテナ停止
$ cd /opt/growi/growi
$ docker compose stop

# データのみリストア
$ cd /opt/growi/data
$ sudo rm -rf es_data/*
$ sudo rm -rf growi_data mongo_db
$ sudo tar -zxvf data.tar.gz

# GROWI コンテナ開始
$ cd /opt/growi/growi
$ docker compose up -d
```

### 再設定

GROWI に管理者権限を所有したユーザでサインインして、左下の設定を選択します。

![Growi-11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3963267/a7db6b2f-deb2-453f-9e3c-c7a5976b50d2.png)

異なるマシンからデータ移行した場合は、［アプリ設定］［サイトURL設定］を実施します。

![Growi-22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3963267/3e311354-068c-499a-a55a-57e1e3dc9920.png)


リストア作業で es_data/* を削除したので、［全文検索管理］［Elasticsearch 管理］で［インデックスのリビルド］を行ってください。（同一マシンデータのリストアでも必要な作業です）

![Growi-13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3963267/a941ea2d-31a2-46fc-aea0-c352717a4104.png)
トランジットVIFとプライベートVIFの違い

# 最初のイメージ
違いがよくわからない。。

## 比較表

| 項目               | **トランジットVIF**                          | **プライベートVIF**                     |
|-------------------|-------------------------------------------|-------------------------------------|
| **用途**           | 複数VPCをAWS Transit Gateway経由で接続     | 単一VPCに直接接続                   |
| **接続先**         | **AWS Transit Gateway**                       | **VPCのDirect Connectゲートウェイ**      |
| **ユースケース**   | マルチVPC接続、ハブ＆スポーク型ネットワーク | 単一VPCとのプライベート接続          |
| **マルチリージョン対応** | **可能**（TGWがリージョンを跨いで接続） | **不可**（同一リージョン内でのみ使用可） |
| **帯域幅**         | 最大**50 Gbps**                           | 最大**10 Gbps**                     |
| **CIDR重複**       | **対応不可**（TGWでCIDRの重複がない必要あり） | **対応不可**                        |
| **利用例**         | 複数AWSアカウントやリージョン間を接続       | オンプレミスと特定VPCを直接接続       |
| **コスト**         | **高め**（複数VPC管理が前提）               | **比較的安価**                       |

:::note info
**ポイント**
**接続先がTransit Gateway
→ トランジットVIF**（名前通り！

**接続先がDirect Connectゲートウェイ
→プライベートVIF**
:::

## まとめ
トランジットVIFは、複数VPCやマルチリージョンをまとめてつなぐためのインターフェースです。帯域幅は最大50 Gbpsで、ハブ型ネットワーク構成に最適ですが、コストがやや高めです。
一方、プライベートVIFは、単一VPCとオンプレミスを直結するタイプで、帯域幅は最大10 Gbps、比較的コストが安価です。
要は、広範囲や多拠点をつなぎたいならトランジットVIF、シンプルに1つのVPCだけならプライベートVIFを選ぶのがベターです。
Papa ParseでCSVを解析する

## 概要

JavaScriptでCSVを読み込む場合、自力を実装するよりは[Papa Parse](https://github.com/mholt/PapaParse)のようなライブラリを利用したほうが色々ラクです。

## Papa Parseを利用するメリット

- シンプルで使いやすい
- RFC 4180に基づいている
- 区切り文字は自動検知
- 改行やクォーテーションを正しく処理してくれる

## ネットファイルをダウンロードして読み込む例

```typescript
const res = await fetch('http://localhost/test.csv');
if (!res.ok) {
  throw new Error(`HTTP error! status: ${res.status}`);
}

const csvText = await res.text();

Papa.parse<CsvData>(csvText, {
  header: true, // ヘッダ行あり
  skipEmptyLines: true, // 空行を無視
  complete: ({ data }) => {
    setCsvData(data);
  },
});
```
個人的備忘録：SQLのIF文みたいなCASE式について書き方忘れがちなCASE式をまとめてみた

## はじめに
SQLを使ってデータを操作・表示する際、条件によって異なる値を返したいケースがあります。

:::note info
**そんなときに役立つのが「CASE式」です。**
:::

この記事では、CASE式の基本的な使い方と実用例を、個人の備忘録としてまとめています。

---

## 書こうと思ったきっかけ
実務でデータベースから集計結果を表示する際に、「条件によって出力値を変えたい」という場面が多くありました。IF文のような感覚で使えるCASE式を整理することで、より柔軟なSQLを書くための参考にしようと思いました。

---

## CASE式とは？
CASE式は、SQL文の中で条件分岐を行うための構文です。IFやswitch文のような役割を果たし、条件に応じて異なる値を返すことができます。

基本構文：
```sql
CASE
  WHEN 条件1 THEN 値1
  WHEN 条件2 THEN 値2
  ...
  ELSE デフォルト値
END
```

SELECT句、WHERE句、ORDER BY句など、さまざまな箇所で使用可能です。

---

## 使用例
### 1. 評価に応じた文字列の変換
```sql
SELECT 名前,
       点数,
       CASE
         WHEN 点数 >= 80 THEN '優'
         WHEN 点数 >= 60 THEN '良'
         WHEN 点数 >= 40 THEN '可'
         ELSE '不可'
       END AS 評価
FROM 成績表;
```

### 2. フラグのON/OFFを表示用に変換
```sql
SELECT ユーザーID,
       メール配信フラグ,
       CASE メール配信フラグ
         WHEN 1 THEN '配信中'
         WHEN 0 THEN '停止中'
         ELSE '不明'
       END AS 配信ステータス
FROM ユーザー一覧;
```

---

## まとめ
CASE式は、SQL文における柔軟な条件分岐を実現する強力なツールです。

:::note warn
**IF文感覚で使えるため、数値やフラグに応じた文字列変換、表示調整、グループ化などに非常に便利です。**
:::

シンプルな集計処理にとどまらず、出力の表現力を高めるためにも、CASE式は積極的に活用していきたい構文のひとつです！
2025年04月未解決の質問

[2025年04月の質問の中から整理します](https://qiita.com/Keiji_otsubo/items/7e8c44a3ca71c8179dab)

--------------

+ [画面コンポーネントの日付形式が間違っているため困っています。2 つの異なる組織、同じフロー、同じロケール設定があるのですが、1 つの画面コンポーネントでは日付が yyyy/mm/dd (予想通り) の形式で表示され、もう 1 つの画面コンポーネントでは dd/mm/yyyy の形式で表示されます。原因は何でしょうか?](https://trailhead.salesforce.com/trailblazer-community/feed/0D5KX00000MZhxy)


+ [How do i complete this trailhead - grow your customer loyalty program - : requires a unique name](https://trailhead.salesforce.com/trailblazer-community/feed/0D5KX00000MaA890AF)


+ [デバッグ時のエラー](https://trailhead.salesforce.com/trailblazer-community/feed/0D5KX00000KDb1Z)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/75e7dd02-2e40-45d7-a1cb-3a8d5331a248.png)


+ [We can’t find 'AuraHandledException' in the ContactController class.  ](https://trailhead.salesforce.com/trailblazer-community/feed/0D5KX00000MZnLe0AL)

サポート預かり

+ [Error: Attempting to create a new Task and assign to a Queue Using Flow. User ID can't equal Queue ID on Create Record.](https://trailhead.salesforce.com/trailblazer-community/feed/0D5KX00000Mb9fN)


+ [残念ながら、HTTP アクション要素は常に getBody() を実行するようで、代わりに getBodyAsBlob() を実行するオプションがありません。](https://trailhead.salesforce.com/trailblazer-community/feed/0D5KX00000PYt3m)


+ [App controlling Chatter Feed Filter Options?](https://trailhead.salesforce.com/trailblazer-community/feed/0D5KX00000PZJGk)

よく分かりませんでしたね。



# AI 関係

+ [「検証ステップ」は間違った UI で実行されていますか?](https://trailhead.salesforce.com/trailblazer-community/feed/0D5KX00000MaDdf0AF)


----

+ [Un able to create lookup for relationship: Number_of_Abandoned_Carts1_CartGuestID_map_External_Abandoned_Cart_c_Home_Guest_ID_c_1744025030357](https://trailhead.salesforce.com/trailblazer-community/feed/0D5KX00000PY4u60AD)

+ [We couldn't find your insight details. Try again later.](https://trailhead.salesforce.com/trailblazer-community/feed/0D54V00007epjHKSAY)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/a6e37d79-82b9-4228-bca4-9fd9e53c206b.png)


# Lead

+ [To make the "Lead Assignment Checkbox" by default checked is not working](https://trailhead.salesforce.com/trailblazer-community/feed/0D5KX00000SV09L)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/c5774000-da17-48c3-a8d4-539ca0788ece.png)


# データローダ

+ [データローダー 63.0 の問題](https://trailhead.salesforce.com/trailblazer-community/feed/0D5KX00000PYebT0AT)


# 日本のコミュニティー

+ [forceChatter:feedにおけるフィードの並び順について](https://trailhead.salesforce.com/trailblazer-community/feed/0D5KX00000PYO0m)
+ [アプリケーション内ガイダンスのプロファイル、権限設定について](https://trailhead.salesforce.com/trailblazer-community/feed/0D5KX00000MbB9o0AF)
個人的備忘録：SQLにおけるGROUP BY句の理解が曖昧だったため、整理を兼ねて記事としてまとめてみた

## はじめに
SQLにおける`GROUP BY`句は、集計処理や統計的な分析を行う上で欠かせない機能です。

:::note info
**特に、売上や件数などの合計・平均をカテゴリ別に集計したい場合などに活躍します。**
:::

本記事では、GROUP BY句の基本的な使い方とポイントを備忘録としてまとめます。

---

## 書こうと思ったきっかけ
業務でSQLの集計処理を繰り返す中で、GROUP BY句の使い方や挙動を毎回調べる場面が多かったため、効率よく思い出せるように整理しておきたいと思いました。

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

---

## 基本構文
```sql
SELECT カラム名, 集計関数(カラム名)
FROM テーブル名
GROUP BY カラム名;
```

---

## 使用例
テーブル：sales（売上データ）
```sql
| id | 商品名   | 金額 |
|----|--------|------|
| 1  | パン    | 100  |
| 2  | パン    | 120  |
| 3  | 牛乳    | 200  |
| 4  | パン    | 110  |
| 5  | 牛乳    | 220  |
```

### 商品ごとの売上合計を出す場合：
```sql
SELECT 商品名, SUM(金額) AS 合計金額
FROM sales
GROUP BY 商品名;
```

実行結果：
```text
| 商品名 | 合計金額 |
|--------|----------|
| パン   | 330      |
| 牛乳   | 420      |
```

---

## 注意点
- `GROUP BY`句に指定されていないカラムを`SELECT`に含めるとエラーになる（またはDBにより未定義の動作）。
- `GROUP BY`の後に複数カラムを指定することで、より細かい集計も可能。

---

## よく使う集計関数
- `SUM(カラム)`：合計
- `AVG(カラム)`：平均
- `COUNT(*)`：件数
- `MAX(カラム)`：最大値
- `MIN(カラム)`：最小値

---

## まとめ
`GROUP BY`は、データをカテゴリ別に集計するための強力なSQL機能です。

:::note warn
***売上集計や件数の集約など、日常的なデータ分析に広く使われています。***
:::

基本を押さえつつ、複数カラム指定やHAVING句と組み合わせた条件指定にもチャレンジしてみます！
個人的備忘録：データベースの種類を理解し、適材適所に使いたくて、RDB・NoSQL・グラフ型など多様なDBの特徴をまとめてみた

## はじめに
本記事では、代表的なデータベースの種類とそれぞれの特徴について、自分用の備忘録として整理しています。

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

開発プロジェクトやインフラ設計において、どのタイプのデータベースを選ぶかは重要な判断ポイントです。

---

## 書こうと思ったきっかけ
クラウドアプリやWebシステムを設計する中で、関係型データベース以外の選択肢（NoSQLなど）にも触れることが増えてきました。それぞれの違いを把握することで、要件に合ったデータベース選定ができるようにしたいと感じたためです。

---

## データベースの主な種類

### 関係型データベース（RDBMS）
- **代表例**：MySQL、PostgreSQL、Oracle Database、SQL Server
- **特徴**：
  - テーブル形式でデータを管理（行・列）
  - SQL（構造化クエリ言語）を用いて操作
  - トランザクション処理やスキーマによる整合性維持に強い
- **利用シーン**：業務アプリケーション、会計・在庫システムなど

**参考文献**

https://products.sint.co.jp/siob/blog/rdbms

### キー・バリュー型データベース
- **代表例**：Redis、Amazon DynamoDB
- **特徴**：
  - シンプルなキーと値の組み合わせでデータを管理
  - 高速アクセスが可能
- **利用シーン**：セッション管理、キャッシュ、リアルタイム分析など

**参考文献**

https://www.rworks.jp/cloud/azure/azure-column/azure-entry/21517/

### ドキュメント型データベース
- **代表例**：MongoDB、CouchDB、Firebase Firestore
- **特徴**：
  - JSONやBSONなどの形式でデータを保存
  - 柔軟なスキーマ定義（またはスキーマレス）
- **利用シーン**：CMS、ユーザーデータ、柔軟性が求められるシステム

**参考文献**

https://qiita.com/uenohara/items/0d99f809f37b812d2ac7

### カラム指向データベース
- **代表例**：Apache Cassandra、HBase、Amazon Redshift
- **特徴**：
  - 列単位でデータを管理
  - 大規模データにおいてクエリが効率的
- **利用シーン**：ビッグデータ分析、データウェアハウス

**参考文献**

https://www.publickey1.jp/blog/11/post_175.html

### グラフ型データベース
- **代表例**：Neo4j、Amazon Neptune
- **特徴**：
  - ノードとリレーション（エッジ）でデータを表現
  - 複雑な関係性の検索が得意
- **利用シーン**：SNS、推薦システム、経路検索など

**参考文献**

https://qiita.com/yodaaa/items/13e56f4d978076eda2a4

---

## まとめ
データベースには多様な種類があり、それぞれに得意・不得意があります。要件に合わせて適切なDBを選定することが、パフォーマンスや保守性に大きく影響します。

今後は実際にそれぞれのデータベースを触ってみて、運用やパフォーマンスの差も体感していきたいと思います！
個人用備忘録：SELECT文がパワーアップ！JOINで必要な情報を引き出す方法を体系的にまとめてみた

## はじめに
データベースを扱う上で欠かせないのが「結合（JOIN）」です。

:::note info
**特にMySQLなどのリレーショナルデータベースでは、複数のテーブルを結びつけて必要なデータを取り出すことがよくあります。**
:::

この記事では、よく使われる**内部結合**（INNER JOIN）と外部結合（OUTER JOIN）について、図解と実例を交えながら解説します。

---

## 結合とは？
結合（JOIN）とは、複数のテーブルを特定の条件で結びつけて、ひとつの結果セットとして扱うことです。

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

例えば「ユーザー情報」と「注文情報」が別のテーブルにある場合、それらをつなぎ合わせて「誰が何を注文したか」を一覧にしたいことがあります。

---

## 内部結合（INNER JOIN）
### 特徴
- 両方のテーブルに存在するデータのみ取得
- 結合条件を満たさない行は結果に含まれない

### 構文
```sql
SELECT *
FROM users
INNER JOIN orders ON users.id = orders.user_id;
```

### 例
**users テーブル**
| id | name    |
|----|---------|
| 1  | Alice   |
| 2  | Bob     |
| 3  | Charlie |

**orders テーブル**
| user_id | item |
|---------|------|
| 1       | Book |
| 2       | Pen  |

**結果**
| id | name  | user_id | item |
|----|-------|---------|------|
| 1  | Alice | 1       | Book |
| 2  | Bob   | 2       | Pen  |

Charlie（id=3）は注文がないため、結果に含まれません。

**参考文献**

https://techplay.jp/column/1742

---

## 外部結合（OUTER JOIN）
外部結合は、片方のテーブルにしかないデータも含めたいときに使います。主に以下の2種類があります。

### LEFT OUTER JOIN（左外部結合）
左側のテーブルのすべての行を取得し、右側に一致するものがなければNULL。

```sql
SELECT *
FROM users
LEFT JOIN orders ON users.id = orders.user_id;
```

**結果**
| id | name    | user_id | item |
|----|---------|---------|------|
| 1  | Alice   | 1       | Book |
| 2  | Bob     | 2       | Pen  |
| 3  | Charlie | NULL    | NULL |

### RIGHT OUTER JOIN（右外部結合）
右側のテーブルのすべての行を取得し、左側に一致するものがなければNULL。

```sql
SELECT *
FROM users
RIGHT JOIN orders ON users.id = orders.user_id;
```

この場合は orders にしか存在しない行も取り出せます。

**参考文献**

https://techplay.jp/column/1743

---

## 使い分けのポイント
| JOINの種類       | 取得される行                                 | よく使う場面                        |
|------------------|---------------------------------------------|-----------------------------------|
| INNER JOIN       | 両方のテーブルに存在するデータのみ             | 必要なデータを絞り込みたい時        |
| LEFT OUTER JOIN  | 左のテーブルのすべて + 右の一致するデータ      | 左が「主」で右が「従」の場合         |
| RIGHT OUTER JOIN | 右のテーブルのすべて + 左の一致するデータ      | 右が主となる場合（使用頻度は少ない）|

---

## ON と USING の違い
### ON は結合条件を自由に書ける
```sql
SELECT * FROM users INNER JOIN orders ON users.id = orders.user_id;
```

### USING はカラム名が同じ場合に簡潔に書ける
```sql
SELECT * FROM users INNER JOIN orders USING (id);
```

---

## まとめ
- INNER JOIN：両方に存在するデータのみ
- LEFT OUTER JOIN：左側はすべて＋右があれば結合
- RIGHT OUTER JOIN：右側はすべて＋左があれば結合

結合を使いこなすことで、SQLの表現力がグッと上がります！まずは INNER JOIN と LEFT JOIN をしっかり使えるようになるのがオススメです！
音スイッチを作って遊ぶ。(Arduino UNO 3) スタックチャンフェス展示0405


いろいろ、注意
----

- 過去ログを見よ!!!
- いろいろ　ランキングとか集計を見ていると、スタックチャン関連だな　俺、に、やれということ。

目的
----

音スイッチを作って、何らかのギミックスと連動させて「人の関心」を引く。
音スイッチでLEDを光らせる。

- アナログ入力[デジタル]
- 理想ダイオード[デジタル]
- LPF、ローパスフィルター(平均)[デジタル]
- 非反転増幅[デジタル]
- ピークレベルコンパレータ[デジタル]


ブロック図(アローダイヤグラム、ネットワーク図)

![image_original (74).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/7e3895de-bda4-4fa7-8c85-8df98d747408.jpeg)



結果
----

![image_original (73).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/b0650605-46a7-4a64-9e19-51c4f09cd9b2.jpeg)



プログラム
----

```c++


//秋月のOLEDとアイテンドウのOLEDのアドレスは3C
//SoundLed_1_uno_1


//ヘッダー
#include <Arduino.h>


//定義


//初期化
void setup() {

  //シリアルポートの初期化
  //Serial.begin(9600); //debug

  //LEDのGPIO初期化
  pinMode(LED_BUILTIN, OUTPUT);

}//setup


//メインループ
void loop() {

  unsigned int vo; //電圧
  int cl = 0;      //L ch

  //L1
  while (cl < 60) { //ピークを越えたらループから抜ける 好みでピーク値を変える
    for (int i = 0; i < 8; i++) { //平均化する
      //センサーの値から電圧に変換(x39.0625/8)
      vo = analogRead(0);                 //センサーの値
      vo = ((vo * 39U) + (vo >> 4)) >> 3; //電圧こと 0～5000
      if (vo < 1650) {vo = 1650;} //マイナス側のカット(マイクだから+-あり)
      vo = vo - 1650;             //中心にする
      cl = cl + vo;
    } //for
    cl = cl / 8;
    //Serial.println(cl); //debug
    cl = cl * 1 + cl >> 0;         //倍率を掛ける(好みで変える)
  } //while

  //LEDの点滅
  digitalWrite(LED_BUILTIN, HIGH);
  delay(100); //100ms待つ
  digitalWrite(LED_BUILTIN, LOW);
  //delay(1000); //1秒待つ

}//loop



```
【EXCEL】アドレスを表示する関数

# やること
[【EXCEL】計測機器なしでSequence Makerを楽しむ #Excel - Qiita](https://qiita.com/a_kata/items/038a9f85b5fa72f8cba9)のようなアドレスを指定する際に使いそうなアドレスを表示する関数を作成する。


# 作成した関数
(入力範囲チェックはしていません。:sweat_smile:)
```vb
Function GetCellAddress(i As Integer, j As Integer) As String
    ' ワークシートのセルアドレスを取得する関数
    GetCellAddress = Cells(i, j).Address(False, False)  ' GetCellAddress(1,1) = A1
'    GetCellAddress = Cells(i, j).Address(True, False)  ' = A$1
'    GetCellAddress = Cells(i, j).Address(False, True)  ' = $A1
'    GetCellAddress = Cells(i, j).Address(True, True)   ' = $A$1
End Function
```
![20250402_063000.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3838258/fcf01f60-6e59-4798-b534-bd6186f9179e.jpeg)


# 関連記事
[チャレンジ！EXCELの列数字から、列英字を取得する #UiPath - Qiita](https://qiita.com/HANACCHI/items/3d299034551c99891286)
[Excel列番号を変換する関数 #VBA - Qiita](https://qiita.com/westdepth/items/46a827554e2603ca830e)
[EXCEL VBA 自分用メモ　crossfish21 #ExcelVBA - Qiita](https://qiita.com/anchorIndigo_215632/items/4046c615cff54857ed8e)
[VBAでよく使う処理まとめ #VBA - Qiita](https://qiita.com/torinist/items/bc1602a8fece903cd2d8)
Geminiで花を描いてみた。

# 概要

Geminiで花を描いてみました。

# 実装

以下のようなリクエストをGeminiに与えました。

```
虹色の花を描いてみてください。
```

![虹の花_20250402.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3192024/19c13413-7d6e-4c56-8703-65f01e93ebf4.png)

```
赤色の花を描いてみてください。
```

![赤色の花_20250402.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3192024/aff55f3f-0692-4329-9350-945e853815df.png)

```
青色の花を描いてみてください。
```

![青色の花_20250402.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3192024/40531b15-9f70-4f52-8d32-50466dcb5656.png)

```
黄色の花を描いてみてください。 
```

![黄色の花_20250402.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3192024/691af5a4-1214-429e-84f5-fc4df0e2ba41.png)

```
紫色の花を描いてみてください。
```

![紫色の花_20250402.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3192024/abcc82e9-8889-4c8b-9175-255e82f55c56.png)

```
銀色の花を描いてみてください。
```

![銀色の花_20250402.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3192024/623ca221-42c0-409b-95b0-02a49d2b8961.png)

```
金色の花を描いてみてください。
```

![金色の花_20250402.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3192024/3df02954-d439-4079-8455-d2a2787b0afb.png)

```
緑色の花を描いてみてください。
```

![緑色の花_20250402.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3192024/059d6ce5-2aac-4ddc-b714-d85136b09d6d.png)

# まとめ

色を変えて花を描いてみました。Geminiいい感じですね。
何かの役に立てばと。
チェリーピック（cherry-pick）で特定のコミットだけを別のブランチに取り込んでみた

## はじめに
チェリーピックでのマージコミット取り込みで、どっちの親を取り込むのだ、、と混乱したので、自分なりに整理してみました。

## チェリーピック（cherry-pick）とは
チェリーピック（cherry-pick）とは、Gitの操作のひとつで、**特定のコミットだけを別のブランチに取り込む**ことを意味します。
通常、Gitではブランチをマージすると、ブランチ全体の変更がまとめて取り込まれますが、チェリーピックを使うと、特定のコミットだけを選んで別のブランチに反映させることができます。

## 基本的な操作
例）feature/new-uiブランチのコミットCの変更だけfeature/loginブランチに取り込みたい。

・feature/new-uiブランチのコミット履歴（A~E）
`A -- B -- C -- D -- E`

このとき、feature/loginブランチ上で以下のように操作します。
```sh
git checkout feature/login
git cherry-pick <CのコミットID>
```
そうすると、Cの変更だけが feature/loginブランチに取り込まれます。

## コミットIDとは
Git における「コミットID」は**各コミットを一意に識別するための名前**みたいなものです。各コミットには、ハッシュ値（40桁の英数字）が自動的に付けられます。
例）a3c9f56f23edb5cd4d2b86b3f6a3ef1b1eac9abc 
長いので、普通は最初の7文字くらいで省略されていることが多いです。
`git log`コマンドやGitHub上のコミット一覧などから確認できます。

## 親が2つ以上ある場合
マージコミット（例：main に feature をマージした時のコミット）を取り込む際は、どちらの親との「差分」を取りたいのかによって、意味が変わってくるため、`-m`によって、どちらを取り込むかを選択する必要があります。
親1：main（マージ先）
親2：feature（マージ元）

### マージ元
・変更を加えたブランチのこと。
・プルリクエストで「この変更をマージしてください！」と提案する側。
・例えば、新機能を追加した feature/login ブランチなど。

### マージ先
・変更を取り込みたいブランチのこと。
・多くの場合は main や develop など、プロジェクトの中心になるブランチです。
・プルリクエストでは「このブランチに対して変更を加えますよ」という意味。

### cherry-pickの基本的な考え方
・取り込みたいコミットを選んで（＝マージ元）
・現在のブランチ（＝マージ先）に、その変更内容だけを反映する。

### 実際のコマンド
```sh
git cherry-pick -m 1 <マージコミットID> 
git cherry-pick -m 2 <マージコミットID> 
```

### -m 1 の意味
・「親1（＝マージ先）を基準にして、マージコミットとの差分を取り出す」ということ。
・マージ先（main）とマージコミットの差分 = マージ元（feature）で加えられた変更。
・つまり「マージ元の変更をcherry-pickする」ということ！

### -m 2 の意味
・「親2（＝マージ元）を基準にして、マージコミットとの差分を取り出す」ということ。
・マージ元から見た差分 = マージ先で上書きされた変更。
・-m 2は特殊なケースで使うことが多く、通常の「feature → main に入った変更を取り出したい」という目的には -m 1 を基本的には使う。-m 2はあまり使わない。

## おわりに
作業ブランチから、mainやdevelopなどの中心ブランチなどへ、マージコミットを取り込む際は、基本的には-m 1を使うようです。
`regexp_replace`関数を使って文字列置換する際は、繰り返しを全部置換する`g`オプションをつける

改行コードを複数含む文字列に対して、登場するすべての改行コードを`<改行>`という文字列に置換するSQLのサンプル。もちろん、改行コードに限らず`regexp_replace`関数で他の文字列を置換する際にも応用が効きます。

`regexp_replace`関数に`g`オプションをつけないと、改行コードが文字列中に複数回登場した際、1つ目のものしか置換できません。

```sql
select
	regexp_replace(
		E'これは\nLF\nと\rCR\rと\r\nCR+LF\r\nが混在する文字列です'
		, E'(\r\n|\r|\n)'
		, '<改行>'
		, 'g' -- このオプションが必要
		) AS "全部置換できるパターン"
	,regexp_replace(
		E'これは\nLF\nと\rCR\rと\r\nCR+LF\r\nが混在する文字列です'
		, E'(\r\n|\r|\n)'
		, '<改行>'
		) AS "1つ目の改行しか置換できないパターン"
;
```
実行結果：

|全部置換できるパターン|1つ目の改行しか置換できないパターン|
|-----------|------------------|
|これは<改行>LF<改行>と<改行>CR<改行>と<改行>CR+LF<改行>が混在する文字列です|これは<改行>LF<br>と<br>CR<br>と<br>CR+LF<br>が混在する文字列です|
comelang version 30.0.0

ちょっと実装に無理があったexceptionを削除しました。
実装を安全側に変更しました。
あまり実装している機能にバグがないと思います。
後はリストの[]による範囲外アクセスなどはRubyやPHPのようにzero clearされた値を返します。
まあ、普通に範囲外アクセスはその前にチェックしてください。
itemによるアクセスは範囲外アクセスの時に返す値を指定できるためint型の範囲外アクセスの場合は-1などを指定すれば、範囲外のアクセスもチェックできます。

概ね整いました。あとはちょっとリファクタリングやtuple2を返す関数やメソッドの第二要素をチェックしてrescueできるパターンマッチングを入れたいと思います。概ね例外のようなことができるはずです。まあ、このプロジェクトに悔いはありません。
大きな仕事でしたが、やり遂げたと思います。自己満足だとは認めます。しかし、結果C言語の仕様を把握できましたし、エディッターやインタプリタ、シェル、webサーバー、データベースサーバー、マイコンの簡単なOSなどを実装することができ、それらのシステムが把握できてよかったです。
この世の中の生きるモチベーションには競争に勝つことと知的好奇心を満たすことの二つがあると思いますが、僕はこのプロジェクトによって知的好奇心が満たせたと思います。このプロジェクトの意味としてはそれかもしれません。所詮その程度のことなので、フリーソフトのプログラミングのために何かを犠牲にすることはやめておいた方が無難です。お金をもらって仕事でやるとすればやり遂げる必要があるため、仕事の時間内は精一杯やる必要があるとは思います。ただ、フリーソフトウェアを作る意味はその程度なので、知的好奇心からTVを見て夜更かしするようなものです。
僕は馬鹿なので、この馬鹿げたソフトウェアのために色々なものを犠牲にしました。
迷惑をかけた方々大変申し訳ありません。後悔としてはそれかもしれません。
Flutterでのモバイルアプリ開発時のIcon・Splash設定メモ

## アイコン設定

### アイコンの作成
- [Microsoft Designer - Stunning designs in a flash](https://designer.microsoft.com/image-creator)などのサイトでよさげなアイコンを生成する
    - 今はChatGPT 4oの画像生成が使えるかも
    - 解像度は1024*1024にしておくと色々と便利
    
### アイコンの設定
- pubspec.yamlに[flutter_launcher_icons](https://pub.dev/packages/flutter_launcher_icons)を追加する
- 公式サイトのテンプレに従ってflutter_launcher_icons.yamlファイルを作成する
    - flavor対応しているので、必要に応じてsuffixに-devまたは-prodを付与する
    - adaptive_icon_backgroundとadaptive_icon_foregroundは設定しないとアイコンが真っ黒になる場合がある
        - https://github.com/fluttercommunity/flutter_launcher_icons/issues/600#issuecomment-2420305803
    - 例：
```yaml
flutter_launcher_icons:
  android: true
  ios: true
  image_path: "lib/assets/launcher_icon/icon-dev.jpeg"
  adaptive_icon_background: "#01002CFF"
  adaptive_icon_foreground: "lib/assets/launcher_icon/icon-dev.jpeg"
```
- lib/assets/launcher_icon配下に画像ファイルを配置する
    - ファイル名はdev環境用はicon-dev.png、prod環境用はicon.pngとする
    - 拡張子がjpgやjpegの場合は設定ファイルの記述を修正する
- 以下のコマンドを実行することで設定が反映される

```bash
dart run flutter_launcher_icons
```

### Splash設定

- pubspec.yamlに[flutter_native_splash](https://pub.dev/packages/flutter_native_splash)を追加する
- 公式サイトのテンプレに従ってflutter_native_splash.yamlファイルを作成する
    - flavor対応しているので、分けたい場合はsuffixに-devまたは-prodを付与する
    - colorの指定はアイコン画像の周囲に表示する色を設定する
    - 例：
```yaml
flutter_native_splash:
  color: "#01002c"
  image: res/splash/icon.jpeg

  android_12:
    color: "#01002c"
    image: res/splash/icon_android12.jpeg

android: true
ios: true
web: false
```

- res/splash配下にスプラッシュ画像ファイルを配置する
    - ファイル名はandroid12未満用はicon.png、android12以降用はicon_android12.png
    - 拡張子がjpgやjpegの場合は設定ファイルの記述を修正する
- 以下のコマンドを実行することで設定が反映される

```bash
flutter pub run flutter_native_splash:create
```
【Python】logファイルを出力するには？

こんにちは！
今回はpythonでlogファイルを出力する方法を紹介します。

## 1. logging モジュールを使った基本的なログ出力

Pythonの標準ライブラリである logging モジュールを使えば、簡単にログを出力できます。

```php
import logging

# ログの基本設定
logging.basicConfig(level=logging.INFO, filename="app.log", format="%(asctime)s - %(levelname)s - %(message)s")

# ログを記録
logging.info("これは情報レベルのログです")
logging.warning("これは警告レベルのログです")
logging.error("これはエラーレベルのログです")

```
このコードを実行すると、app.log というファイルにログが出力されます。

## 2. ログのレベルについて

logging モジュールでは、以下のようなログレベルを使用できます。

| ログレベル | 説明 |
|:-----------|------------:|
| DEBUG       | デバッグ用の詳細な情報        |
| INFO     | 一般的な情報メッセージ      |
| WARNING       | 警告メッセージ        |
| ERROR         | エラーメッセージ          |
| CRITICAL       | 重大なエラーメッセージ       |

適切なレベルを選んでログを記録しましょう。

## 3. ロガーをカスタマイズする

複数のファイルで共通のロガーを使う場合は、Logger オブジェクトを作成して管理すると便利です。

カスタムロガーの作成

```php
import logging

def setup_logger():
    logger = logging.getLogger("my_logger")
    if not logger.hasHandlers():
        handler = logging.FileHandler("app.log")
        formatter = logging.Formatter("%(asctime)s - %(filename)s - %(levelname)s - %(message)s")
        handler.setFormatter(formatter)
        logger.addHandler(handler)
        logger.setLevel(logging.INFO)
    return logger

logger = setup_logger()

logger.info("カスタムロガーでログを記録")
```

## 4. コンソールとファイルの両方にログを出力する

ログをファイルに保存しながら、コンソールにも表示したい場合は、StreamHandler を追加します。

```php
import logging

def setup_logger():
    logger = logging.getLogger("my_logger")
    if not logger.hasHandlers():
        file_handler = logging.FileHandler("app.log")
        console_handler = logging.StreamHandler()
        
        formatter = logging.Formatter("%(asctime)s - %(filename)s - %(levelname)s - %(message)s")
        file_handler.setFormatter(formatter)
        console_handler.setFormatter(formatter)
        
        logger.addHandler(file_handler)
        logger.addHandler(console_handler)
        logger.setLevel(logging.INFO)
    return logger

logger = setup_logger()

logger.info("ファイルとコンソールの両方にログを出力")
```

## 最後に
今回は以上になります。
最後まで見ていただき、ありがとうございました。
では、また次の記事で～！
モバイルアプリのリリース準備に必要な作業一覧 iOS編

Flutterでモバイルアプリを開発後、iOSアプリをリリースする際に必要となる作業について整理したのでそのメモ。

### ストア素材

以下を素材ベースにストア掲載資材を作成する。

[【ストア申請】ストアに掲載する素材](https://brandedapp-faq.stores.jp/hc/ja/articles/21540356421401--%E3%82%B9%E3%83%88%E3%82%A2%E7%94%B3%E8%AB%8B-%E3%82%B9%E3%83%88%E3%82%A2%E3%81%AB%E6%8E%B2%E8%BC%89%E3%81%99%E3%82%8B%E7%B4%A0%E6%9D%90)

### サポートURL

Googleフォームを利用するのが楽。

[新作リリースでApp Store審査リジェクト3件にハマッた話｜ギガビット＠ゲームつくるひと](https://note.com/gigabit_million/n/ne8d02b110b3c)

### デベロッパーURL

AdMob用にapp-ads.txtを配置したウェブサイトを用意する必要がある。

以下のような構成で、Firebase Hostingでホスト。ルートのリダイレクト先を↑で設定したGoogleフォームのURLにすることで、index.htmlへのアクセスを防げる。

[Firebase Hostingを使って、app-ads.txtを無料で公開する](https://zenn.dev/k9i/articles/8b91ee1e7dcc53)

### プライバシーポリシー

Notionでページを作成してホストする。

- テンプレ

[プライバシーポリシーのひな型 | Webサイトの利用規約（無料ひな型（雛形・テンプレート）・商用利用可）](https://kiyaku.jp/hinagata/privacy.html)

- アプリ内組み込み

[【Flutter】個人開発で利用規約・プライバシーポリシーを作成する](https://zenn.dev/ryoya_fukasawa/articles/be19cc06d6b925)


## iOS

### リリース対応

TestFlightでテストする際は以下に則って対応する

[【iOS】 TestFlightにアプリをリリースするやり方 Flutter](https://zenn.dev/lisras/articles/14b90852fe5525)

商用リリース時は以下に則って対応を進める。

[（2024年版）iPhoneアプリをApp Storeで公開する手順まとめ](https://zenn.dev/moutend/articles/59bddeac3ff3d3)

### Appのプライバシー設定

AdMobを使用する場合、以下に従いAppStoreにおける利用データを設定する。

[AdMob利用時の「Appのプライバシー」の入力方法虎の巻](https://zenn.dev/kazushige/articles/9afa10b36d6828)

### 著作権

著作権入力フォームの書き方。

[【iOS】アプリリリース時に著作権情報の入力が必須化した話](https://zenn.dev/nekoniki/articles/ddbd7e03e59b72)

### ATT対応

広告を導入する場合、ATT対応が必須。

[app_tracking_transparency | Flutter package](https://pub.dev/packages/app_tracking_transparency)

### **Missing Push Notification Entitlement**

プッシュ通知を利用しているのにXCode側で権限を設定していない場合に警告メールが届くのでその対応。

[Apple Storeから警告？が来た Missing Push Notification Entitlement の対処方 - Qiita](https://qiita.com/kokogento/items/f088f75688f44b797c2c)
(Linux)テキストファイルの文字コードをSJISからUTF8に変更する

(Linuxコマンド)
```
iconv -f SHIFT_JIS -t UTF-8 input.dat > output.dat
```
- iconvは文字コードを変換するLinuxコマンド
- f SHIFT_JIS は元の文字コード
- UTF-8 は変換先の文字コード
- 「>」 の前は対象ファイル名
- 「>」 の後は出力ファイル名
【Kubernetes】ノードをデバッグするスクリプト

 Kubernetesのノードをデバッグするスクリプトを書きました。
 意外と便利なので共有です。

※ 中身は `kubectl debug node/ ...` を使ってデバッグ用のコンテナをノードにたてるだけです。
※ fzfとjqとkubectlが必要です。

 `node-debugger.sh`

 ```bash
#!/bin/bash
set -euo pipefail

function usage {
cat <<EOF >&2
指定したNodeをDebugするPodを立ち上げるコマンド

Usage:
  $0 [Options]

Options:
  -h | --help
    ヘルプ
EOF
exit 1
}

function error {
  echo "[error] $1" >&2
  exit 1
}

function info {
  echo "[info] $1"
}

args=()
while [ "$#" != 0 ]; do
  case $1 in
    -h | --help ) usage ;;
    -* | --*    ) error "$1 : 不正なオプションです" ;;
    *           ) args+=("$1") ;;
  esac
  shift
done
[ "${#args[@]}" != 0 ] && usage

# ノードの選択
NODE="$(kubectl get node -o wide | sed '1d' | fzf --height 25% --header 'Select a node')"
NODE_NAME="$(echo $NODE | awk '{print $1}')"

# 起動するイメージの選択
IMAGE=$(
cat <<EOF | fzf --height 25% --header "Select a image"
nicolaka/netshoot
public.ecr.aws/aws-cli/aws-cli
ubuntu:24.04
EOF
)

# ノードデバッガーのPodを起動
echo kubectl debug node/${NODE_NAME} -ti --image=$IMAGE -- /bin/bash
kubectl debug node/${NODE_NAME} -ti --image=$IMAGE -- /bin/bash

# ノードデバッガーのPodを削除
for pod in $(kubectl get po -o json | jq -r ".items[].metadata.name" | grep -e "^node-debugger"); do
  kubectl delete po $pod
done
```


# 使ってみる


```bash
node-debugger.sh
```

まずはデバッグしたいノードを選択

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/92383/a85eee2e-bd16-4b92-8011-03889aca2765.png)

続いて、起動するPodのイメージを選択

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/92383/4674ac4d-05d6-4c05-9922-e62251fb02fb.png)


`/host` に ノードのファイルシステムがマウントされているので確認してみる

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/92383/37d4182d-00c8-4532-a6f0-cb994c88520a.png)

シェルからログアウトすると自動的にPodが削除されます。

```bash
exit
# pod "node-debugger-ip-10-32-1-221.ap-northeast-1.compute.internal-bst6b" deleted
```
【Bruno】Postmanから移行する方法

## インストール

以下のコマンドでBrunoをインストールします。

```terminal
brew install bruno
```

## Postmanのデータをエクスポート

ヘッダーのアイコンをクリック -> 「設定」をクリック

![https---qiita-image-store.s3.ap-northeast-1.amazonaws.com-0-2342443-3a05bcdb-8b50-4cb7-a0f7-4c59f63d89be.png.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342443/67b04587-d3b1-4ca7-bb1b-bde200811989.jpeg)

「Account」の「Export Data」をクリック

![screen-shot-49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342443/26c72c36-b9a2-40b1-93ca-8fc8c33f9cf2.png)

「Export Data」をクリック

![screen-shot-50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342443/0c50e964-0716-46d9-8764-949389e6603f.png)

エクスポートするデータを選択し、「Request Data Export」をクリック

![screen-shot-51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342443/6701190e-097c-4e2c-88fb-f33011e5bba7.png)

登録しているメールアドレスに以下のようなメールが届くため、「Download」をクリック

![screen-shot-53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342443/1df9cd41-d6b7-432b-aaa1-bcf00f871557.png)

https://learning.postman.com/docs/getting-started/importing-and-exporting/exporting-data/#export-data-dumps

## Brunoにインポート

Brunoのアプリを開き、「Import Collection」をクリック

![screen-shot-54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342443/62dca017-a863-43be-8000-c550d02afad5.png)

「Postman Collection」をクリック

![screen-shot-56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342443/48fd3176-d75a-4b70-b14d-a2ec7bc60cfd.png)

先ほどダウンロードしたPostmanのCollectionファイルを選択

インポートするCollectionを選択
保存先を指定
「Import」をクリック

![screen-shot-57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342443/c22b2dfc-ad3d-4878-a0d4-be3659910119.png)

![screen-shot-58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342443/47e27ce9-2d0a-4ed4-9b11-6be82e2b6f39.png)

以上でPostmanのCollectionのインポートができます。
JavaScriptにおける状態管理戦略：スコープの切り分け、ローカル/グローバルの責務、再レンダリング制御と同期整合性の確保



## 概要

状態管理とは「変数を持つこと」ではない。  
それは**“いつ・どこで・なぜ・どのように変わるか”を制御する設計行為**である。

JavaScriptにおいて、状態はコンポーネント内・グローバルストア・URL・DOM属性など複数の場所に分布する。  
この分布を制御せずに進むと、**バグの温床・パフォーマンスの劣化・テスト不能な構造**が生まれる。

本稿では、**状態のスコープ設計・責務分離・同期の設計・再レンダリング最適化**を主軸に、**破綻しない状態管理の構築戦略**を解説する。

---

## 1. 状態スコープは「責務」で分ける

```text
| 状態        | スコープ       | 例                             |
|-------------|----------------|--------------------------------|
| UI状態      | ローカル       | フォーム入力、モーダル開閉     |
| アプリ状態  | グローバル     | 認証ユーザー、テーマ、言語     |
| 一時状態    | URL or storage | ページ番号、ソート条件         |
```

- ✅ 状態の種類によって**保存先と責任範囲を分離**
- ✅ **状態がどこにあるか**ではなく、**なぜそこにあるか**を定義する

---

## 2. ローカル状態（コンポーネント内部）の適切な管理

```js
const [value, setValue] = useState('');
```

- ✅ UIと密接に関係する状態はローカルに閉じ込める
- ❌ 親 → 子 → 孫に props-drilling して破綻 → **Context or Store へ昇格検討**

---

## 3. グローバル状態（Context / Store）の分離と集約

```js
// e.g. Zustand / Redux / Pinia (Vue)
const useAuthStore = create((set) => ({
  user: null,
  setUser: (u) => set({ user: u }),
}));
```

- ✅ **1機能 = 1ストア**の原則（例：Auth / User / Settings）
- ✅ ドメイン単位で責務を明確にする

---

## 4. 非同期状態と整合性保証

```js
useEffect(() => {
  let cancel = false;

  async function fetchData() {
    const res = await fetch('/api/data');
    if (!cancel) setData(res);
  }

  fetchData();
  return () => (cancel = true);
}, []);
```

- ✅ 非同期は race condition を考慮する設計に
- ✅ UI更新は **“最後に呼ばれた非同期” だけが責任を持つ**ように

---

## 5. 再レンダリングの最小化と selector分離

```js
const count = useStore((s) => s.counter);
```

- ✅ **必要なstateだけ抽出（selector設計）**
- ✅ 不要な再描画を避けるため、**stateの分解と構造化が必須**

---

## 6. 状態の可視化とデバッグ

- ✅ Redux DevTools / Zustand Devtools などを活用
- ✅ **stateの変更履歴を追える構造は設計の証明になる**

---

## 設計判断フロー

```text
① 状態はUIに閉じているか？ → useState / local に保つ

② 複数コンポーネントが参照しているか？ → グローバル状態に昇格

③ 状態が非同期と絡むか？ → 整合性保証とキャンセル設計

④ 状態更新が不要に描画を発生させていないか？ → selectorや分離の導入

⑤ 状態の出入りが可視化されているか？ → Devtoolsまたはログ設計
```

---

## よくあるミスと対策

### ❌ どのコンポーネントがどのstateを持っているか把握できない

→ ✅ **責務ごとに分離・昇格 / 降格ルールを持つ**

---

### ❌ 状態更新が全体再レンダリングを引き起こす

→ ✅ **stateの粒度を細分化 + selector設計で分離**

---

### ❌ 非同期状態で古い結果が画面を上書きする

→ ✅ **キャンセル設計 or fetch番号管理で整合性保証**

---

## 結語

状態とは「値を持つこと」ではない。  
それは**“今を記憶し、未来の動作を制御し、過去を辿れる構造”**である。

- スコープを切り分け  
- 責務ごとに昇格 / 降格を設計し  
- 非同期と同期の整合性を制御し  
- 再レンダリングを抑えて、構造を守る

**JavaScriptにおける状態管理とは、  
“UIとロジックの架け橋を構造化し、破綻を防ぐための制御戦略”である。**
JavaScriptにおけるユーティリティ設計戦略：関数分離、ユースケース単位の抽出、共通化とドメイン知識の分離

## 概要

ユーティリティ関数とは「よく使う処理」ではない。  
それは**“本質的ではない処理をドメインロジックから追い出し、構造を純粋に保つための抽象”**である。

JavaScriptの開発において、**形式変換・日付操作・バリデーション・文字列整形**などは本質とは異なるが必須の処理である。  
これらを適切にユーティリティ関数として設計・整理することで、**ロジックの見通し・再利用性・テスト性**が劇的に向上する。

---

## 1. ユーティリティ関数の設計原則

### ✅ 「再利用される」ではなく「再利用可能である」ことが前提

- 依存がない（ピュア関数）
- 構造が小さい（1つの責務）
- 意味が明確（名前で処理内容が分かる）

```js
function toUpperCaseFirst(str) {
  return str.charAt(0).toUpperCase() + str.slice(1);
}
```

---

## 2. 関数の抽出単位は「意味 + 頻度 + 汚れ」

### ✅ 以下の3条件で抽出判断：

```text
① 意味が共通で汎用的  
② 頻度が高い or 重要な流れを支える  
③ ドメイン外の処理（＝ロジックのノイズ）
```

```js
// 抽出すべき例
function formatDate(date) {
  return date.toISOString().split('T')[0];
}
```

---

## 3. ユーティリティ vs ドメインロジックの分離

```text
❌ ドメイン層に日付の整形や文字列操作が散在  
✅ ユーティリティに抽出してロジックは “業務の意味” だけに集中
```

```js
// ドメイン層
const label = `契約日: ${formatDate(user.contractAt)}`;
```

---

## 4. ユーティリティ階層と分類

```text
utils/
├─ date.ts       → 日付操作
├─ string.ts     → 文字列整形
├─ number.ts     → 桁区切りや丸め
├─ array.ts      → フィルタ・グループ処理
├─ validation.ts → 汎用バリデーション
```

- ✅ 領域別の抽象に整理
- ✅ index.tsで**集約エクスポート**すればimportも簡潔に

---

## 5. ユーティリティの過剰抽象を避ける

### ❌ 汎用性を追いすぎて意味が消える

```js
function doSomethingToList(list, fn) {
  return list.map(fn);
}
```

→ ✅ **意味ある用途に絞る方が保守性が高い**

---

## 6. テスト性とユニット性の最大化

- ✅ ユーティリティ関数は**最もテストしやすい領域**
- ✅ 入出力が固定化されており**副作用ゼロ**が理想

```js
test('toUpperCaseFirst', () => {
  expect(toUpperCaseFirst('hello')).toBe('Hello');
});
```

---

## 設計判断フロー

```text
① 処理はドメインロジックの一部か？ → 汎用ならユーティリティへ

② 関数は再利用可能な構造か？ → 外部依存があれば再設計

③ 意味が明確か？ → 名前と内容が一致しているかを確認

④ 処理を変更する頻度と影響範囲は？ → 共通化が過剰でないか判断

⑤ テストがしやすいか？ → 副作用・非同期・状態依存がないかを確認
```

---

## よくあるミスと対策

### ❌ ドメイン層で文字列整形・日付変換を毎回書く

→ ✅ `utils/` に抽出し、**業務ロジックに集中**

---

### ❌ 関数名が抽象的すぎて目的が不明

→ ✅ `formatDate`, `padZero`, `groupBy` など**用途が明確な名前**にする

---

### ❌ utils関数がアプリ固有の依存を持って再利用不能

→ ✅ **引数と戻り値のみで完結する設計**に限定

---

## 結語

ユーティリティとは「便利な関数」ではない。  
それは**“ノイズを外に逃がし、本質を見通すための構造戦略”**である。

- ドメインから汚れを抽出し  
- 領域ごとに整理し  
- 再利用可能で安全に保ち  
- テストしやすく設計する

**JavaScriptにおけるユーティリティ設計とは、  
“ロジックを純化し、構造を美しく保つための補助層のデザイン”である。**
JavaScriptにおける非同期設計戦略：Promise / async-await の原則、並列制御、キャンセル設計とエラーフロー

## 概要

非同期とは「遅延する」ことではない。  
それは**“待つことを制御し、失敗を受け入れ、構造として管理する”**ことに他ならない。

JavaScriptはイベント駆動・シングルスレッド。  
非同期処理はアプリの応答性・性能・安全性に直結する。  
本稿では、**非同期処理を「動作」ではなく「設計」として扱うための戦略**を解説する。

---

## 1. Promise と async/await の設計原則

### ✅ async/await は Promise をラップした構造

```js
async function getData() {
  const res = await fetch('/api/data');
  return await res.json();
}
```

- ✅ `await` は **“その場で中断”ではなく、“Promiseの完了を待って次へ”**
- ✅ 例外は `try-catch` で受ける → 明確な制御構造を持つ

---

## 2. 並列 vs 直列：Promise.all / for-await-of の使い分け

### ✅ 並列に投げる

```js
const [a, b] = await Promise.all([fetchA(), fetchB()]);
```

### ✅ 直列で制御する

```js
for (const url of urls) {
  const res = await fetch(url);
  handle(res);
}
```

- ✅ 並列：**全て同時に開始してまとめて取得**
- ✅ 直列：**順番や依存がある場合に必須**

---

## 3. 非同期エラーの捕捉と伝播

```js
async function safeFetch(url) {
  try {
    const res = await fetch(url);
    if (!res.ok) throw new Error('Fetch failed');
    return await res.json();
  } catch (e) {
    return { ok: false, error: e.message };
  }
}
```

- ✅ catch内で構造的なエラー返却を設計
- ✅ 失敗時にも UI が破綻しない構造が必要

---

## 4. キャンセル可能な非同期：AbortController の利用

```js
const controller = new AbortController();

fetch('/api', { signal: controller.signal });

setTimeout(() => controller.abort(), 3000);
```

- ✅ **重複リクエストやコンポーネント破棄時のキャンセルに有効**
- ✅ UI操作と連動して**非同期のキャンセル設計**を導入すべき

---

## 5. UIと非同期の境界設計

### ❌ UI側で直接fetchを実行し状態が管理されない

```js
button.onclick = () => {
  fetch('/api'); // 非管理な副作用
};
```

### ✅ UI → ロジック層（サービス） → fetch / 処理

```js
async function onClick() {
  const data = await fetchUser();
  setUser(data);
}
```

- ✅ **状態と副作用は分離**して再現性とテスト性を確保

---

## 6. 非同期状態の「今」をUIで表現する

```js
const [loading, setLoading] = useState(false);

const onClick = async () => {
  setLoading(true);
  try {
    const data = await fetchData();
    setData(data);
  } finally {
    setLoading(false);
  }
};
```

- ✅ ローディング・成功・失敗を明示的に管理
- ✅ 非同期の「今の状態」をUIで表現し、体験と同期する

---

## 設計判断フロー

```text
① async/await を単に使うだけでなく、責務に応じた関数構成になっているか？

② 並列にできる処理を逐次で書いていないか？

③ エラーが握りつぶされていないか？ → catch構造を明示

④ UI破棄時にも非同期処理が生きていないか？ → AbortController活用

⑤ UIと非同期処理が分離されているか？ → プレゼンテーション層とロジック層を区別
```

---

## よくあるミスと対策

### ❌ 複数awaitを逐次で記述 → パフォーマンス劣化

→ ✅ `Promise.all` による並列化で高速化

---

### ❌ 非同期処理が途中でUIが破棄され、状態更新エラー

→ ✅ `AbortController`でキャンセル可能に設計 + cleanup設計

---

### ❌ UIが「何が起きているか」を示さずユーザーが混乱

→ ✅ loading, success, error を明示的にstate設計

---

## 結語

非同期とは「待つこと」ではない。  
それは**“流れを制御し、今を把握し、未来を保証する”構造的な戦略**である。

- 並列と直列を分け  
- エラーを受け入れて構造化し  
- UIと非同期の責務を分離し  
- 状態を「今」としてUIに同期する

**JavaScriptにおける非同期設計とは、  
“挙動ではなく構造として制御する、再現可能な戦略”である。**
JavaScriptにおけるモジュール分離と依存戦略：レイヤー設計、境界の定義、循環参照の防止構造


## 概要

モジュールとは「機能をまとめる箱」ではない。  
それは**責務・依存・拡張性・再利用性を保証する、構造的な単位**である。

JavaScriptでのモジュール設計は、その柔軟さから無秩序な依存・結合・循環を生みやすい。  
それを制御し、**壊れない構造・テスト可能な分離・変更に強い境界**を設計することが不可欠になる。

本稿では、**依存構造を制御するためのレイヤー分離、境界戦略、循環参照の防止技術**を解説する。

---

## 1. モジュール構造は「責務」で分ける

```text
src/
├─ features/
│   ├─ user/
│   │   ├─ index.ts
│   │   ├─ service.ts
│   │   └─ validator.ts
│   └─ auth/
│       ├─ login.ts
│       └─ session.ts
├─ libs/
│   ├─ api.ts
│   └─ storage.ts
```

- ✅ **機能単位（Feature-Based）** で分離
- ✅ 機能間を**直接依存させず、共通ライブラリ越しに橋渡す**

---

## 2. レイヤー設計と依存方向の一方向化

```text
UI → Application → Domain → Infrastructure
```

- ✅ 上層から下層へは依存可能
- ❌ 下層から上層への**逆依存は禁止**
- ✅ 依存の方向性は「読みやすく」「テストしやすく」「循環を防ぐ」

---

## 3. 循環参照の検出と防止

### ✅ 循環参照の症状

- `undefined`が返ってくる
- `Cannot read property` エラー
- 静的解析エラー（ツールによっては）

### ✅ 対策

- 明確な依存境界を引く（依存のハブを作らない）
- index.ts の**双方向import禁止**
- `depcheck`, `madge`, `eslint-plugin-import` などで検出

---

## 4. 共通ロジックは “低層に抽象化して” 橋渡す

```text
features/
├─ user/
│   └─ useUser.ts → domain/user.ts + libs/api.ts を使う
```

- ✅ 共通処理（ログ、バリデーション、変換）は **libs 層で抽象化**
- ✅ features から直接他の features に依存しない

---

## 5. DI（依存注入）と Adapter層の活用

```ts
// domain/use-case.ts
export function createUserService({ userRepo }) {
  return {
    getUser: () => userRepo.find(),
  };
}
```

- ✅ **依存を明示的に渡す構造**で、テスト・差し替えが容易に
- ✅ Adapter層で外部依存（API, DB）とのインターフェースを統一

---

## 6. index.ts は「公開窓口」であり、構造を隠す

```ts
// features/user/index.ts
export { createUserService } from './service';
```

- ✅ 公開する機能だけを `index.ts` に定義
- ✅ 内部構造は外部から見えないようにし、**意図的に使わせる構造へ**

---

## 設計判断フロー

```text
① このファイルが他モジュールに依存していないか？ → 依存構造を可視化

② 双方向のimportが発生していないか？ → 共通化 or 分離構造へ

③ UIとロジックが同一モジュールにないか？ → Presentation / Logic の分離

④ 共通ロジックがfeature配下に埋もれていないか？ → libs層に抽出

⑤ テスト用モックが依存に組み込めているか？ → DI構造へ移行
```

---

## よくあるミスと対策

### ❌ features同士でimportし合って依存が複雑化

→ ✅ **共通処理はlibs層に抽象化して橋渡す**

---

### ❌ index.ts が再帰的にindex.tsを読み込み循環参照

→ ✅ **index.tsは1方向の“公開インターフェース”だけに使う**

---

### ❌ APIやDBを直接サービスで呼び、テスト不能に

→ ✅ **use-case + DI構造 + Adapter層で依存を外部化**

---

## 結語

モジュールとは「コードを分けること」ではない。  
それは**“意味・依存・責任を構造化し、破綻を防ぐための戦略単位”**である。

- 機能でモジュールを切り  
- 依存は一方向に制御し  
- 外部はAdapterに抽象化し  
- 公開構造はindexで制御する

**JavaScriptにおけるモジュール依存戦略とは、  
“複雑さを壊さず拡張可能に保つための、構造的ガードレール”である。**
JavaScriptにおけるデータ構造とアルゴリズム選定戦略：Map/Setの活用、計算量ベースの判断、配列操作の最適化

## 概要

データ構造とは「値をしまう箱」ではない。  
それは**“意味・操作・性能・制約”の全てを包括する設計構造**である。

JavaScriptでは何でも配列・何でもオブジェクトで済まされがちだが、  
**本当に意味を持つ構造を選び、適切な操作で処理することがパフォーマンスと保守性の鍵**となる。

本稿では、**構造・操作・計算量を意識した戦略的なデータ設計とアルゴリズム選定法**を解説する。

---

## 1. 配列（Array） vs Set：重複と検索の違い

```js
const arr = ['a', 'b', 'a'];
const set = new Set(arr); // → {'a', 'b'}
```

| 操作         | Array       | Set         |
|--------------|-------------|-------------|
| 重複         | 許容        | 自動排除    |
| 含むか？     | `includes()`| `has()`     |
| 挿入         | `push()`    | `add()`     |

### ✅ セット集合的な意味（タグ一覧など）は `Set` が明確で高速

---

## 2. オブジェクト vs Map：キーの型と順序の違い

```js
const map = new Map();
map.set({ id: 1 }, 'value'); // OK

const obj = {};
obj[{ id: 1 }] = 'value';    // ❌ "[object Object]"
```

| 特性              | Object               | Map                      |
|-------------------|----------------------|--------------------------|
| キーの型          | 文字列のみ           | 任意の型（オブジェクト含む） |
| 順序保証          | なし                 | 挿入順に保持             |
| イテレーション   | for...in             | map.forEach / entries   |

### ✅ データ → メタ情報のマッピングには `Map` が正しい構造

---

## 3. 計算量に基づく操作選定（Big-O）

| 操作             | Array      | Set / Map     |
|------------------|------------|---------------|
| 挿入             | O(1)       | O(1)          |
| 検索             | O(n)       | O(1)          |
| 削除             | O(n)       | O(1)          |

→ ✅ **検索/存在判定/削除が多い構造はMap/Setに置き換え可能かを常に検討**

---

## 4. 配列操作の最適化と破壊的変更の回避

### ❌ 非イミュータブルな更新

```js
arr.splice(1, 1); // 破壊的
```

### ✅ イミュータブルな構造で再生成

```js
const updated = arr.filter((_, i) => i !== 1);
```

- ✅ UIと状態の一貫性を保証するため、**状態変更は常に非破壊的に**

---

## 5. 構造変換のユーティリティ化（構造はロジックにしない）

```js
function arrayToMap(items, keyFn) {
  const map = new Map();
  for (const item of items) {
    map.set(keyFn(item), item);
  }
  return map;
}
```

- ✅ 配列 → Map変換を構造的に記述
- ✅ **変換処理 = 意図のある変換として明示**

---

## 6. ストリーム的操作と遅延評価の選択肢

```js
const result = [1, 2, 3, 4, 5]
  .map(x => x * 2)
  .filter(x => x > 5);
```

- ✅ チェーン可能な処理構造は**可読性が高く意味が明確**
- ❌ 重い処理には `for...of` で**早期break**を意識

---

## 設計判断フロー

```text
① 重複排除や存在判定が多い → Setを選ぶ

② キーがオブジェクト or 順序が必要 → Mapが妥当

③ 配列操作で毎回filter/mapしていないか？ → 計算量を確認

④ 状態更新が破壊的か？ → イミュータブルに書き換えられるか

⑤ データ変換ロジックが散乱していないか？ → ユーティリティとして再利用可能に
```

---

## よくあるミスと対策

### ❌ 配列.includesでO(n)検索を毎回行ってパフォーマンス劣化

→ ✅ `Set`に置き換えることで**検索がO(1)**に

---

### ❌ Mapの代わりにオブジェクトを使って複雑なキーが壊れる

→ ✅ `Map`は**非プリミティブキー対応 & 順序保証付き**

---

### ❌ 配列に対する破壊的更新でUIステートが壊れる

→ ✅ `filter/map/slice` で**非破壊な操作を常に優先**

---

## 結語

データ構造とは「どう持つか」ではない。  
それは**“どう使うか、どう守るか、どう制御するか”を含めた設計戦略**である。

- 意味に沿った構造を選び  
- 操作の意図を構文に込め  
- 計算量に根拠を持ち  
- 状態を壊さず設計する

**JavaScriptにおけるデータ構造とアルゴリズム設計とは、  
“書き方の違いではなく、拡張性と性能を決める構造的な選択”である。**