OmniAICreator/Qiita-1.07M huggingface dataset:
FastRTCを日本語対応に！リアルタイム音声処理拡張パッケージ fastrtc-jp を作りました

# FastRTCを日本語対応に！リアルタイム音声処理拡張パッケージ `fastrtc-jp` を作りました

この記事では、私が開発したオープンソースプロジェクト [`fastrtc-jp`](https://github.com/route250/fastrtc-jp) を紹介します。

## FastRTCって何？

FastRTCは、Hugging Faceが公開した**リアルタイム通信に特化したPythonフレームワーク**です。

> マイク音声を取得 → 音声認識 → AI応答生成 → 音声合成してスピーカーに出力
> という「対話型AIアプリ」に必要な処理を、簡単に実装できます。

### 特徴：

* LLMとの対話を前提にしたストリーム処理設計
* 音声や動画のリアルタイム入出力に対応　(WebRTCベース)
* GradioによるUI統合
* Hugging FaceのHubと統合されていて便利

ただし...

---

## FastRTCの課題：日本語が不十分！

FastRTC自体は非常に便利なのですが、FastRTCで標準の `moonshine` `Kokoro-TTS`は日本語がうまく扱えません


---

## 💡 そこで `fastrtc-jp`

`fastrtc-jp` は、FastRTCを **日本語音声処理対応に拡張**するPythonパッケージです。

FastRTCの構成にそのまま組み込めるよう、TTS/STT処理をプラグイン形式で追加できます。

FastRTCを使いたいけど、日本語対応がないなら作るしかないな...と思ってこのプロジェクトを始めました。

---

## `fastrtc-jp` で使える音声処理モデル

### ✅ 音声合成（TTS）

| モデル                  | 概要                       |
| -------------------- | ------------------------ |
| **VOICEVOX**         | 無料で高品質、キャラ豊富、感情表現も可      |
| **Style-Bert-VITS2** | Hugging Face対応。自然で滑らかな発声 |
| **gTTS**             | Google TTS API。手軽でナチュラル  |

### ✅ 音声認識（STT）

| モデル             | 概要                   |
| --------------- | -------------------- |
| **Vosk**        | 軽量・オフライン対応・高速        |
| **mlx-whisper** | Apple Silicon最適化。超高速 |
| **Google STT**  | APIベースで高精度。日本語対応可    |

---

## WebRTCの音声対話アプリをさくっとPythonで

`fastrtc-jp` を使えば、たとえばこんな構成がPythonだけで実現できます：

```
🎤マイク入力
　↓
🧠 音声認識　(STT)
　↓
🤖 AI応答生成 (LLM)
　↓
🕺 音声合成　(TTS)
　↓
🔊 スピーカー出力
```

UIはGradioですぐに触って確認できるのもポイントです！

---

## インストール方法

```bash
pip install fastrtc-jp
```

️オプション機能（使用するラリブラリによって)

```bash
pip install fastrtc-jp[vosk]
pip install fastrtc-jp[sbv2]
pip install fastrtc-jp[gtts]
```

---

## 🧳 GitHubはこちら！

* プロジェクト: [https://github.com/route250/fastrtc-jp](https://github.com/route250/fastrtc-jp)
* ライセンス: MIT
* 動作環境: Python 3.12+, Ubuntu 24.04, macOS Sonoma 15.4

---

## おわりに

FastRTCは「音声×AI」の世界を誰でも扱えるものにするための素晴らしいフレームワークです。
でも、日本語ユーザーにとってはまだ「素のまま」では使いにくい部分もあります。

そのギャップを埋めるために `fastrtc-jp` を作りました。

興味を持ってくださった方、GitHubにスター頂けるととても嬉しいです！
Issue・PRも大歓迎です！
React Routerの理解を深めるための旅

# はじめに
Reactでアプリケーションを開発していると、Routerの設定に疑問を感じることがあります。特に「Routeタグの属性”element”に指定するコンポーネントがどこから来るのか？」という点は、（たぶん）React初心者にとって最初の壁となると思う。

# そもそもの疑問点
React Routerの記事を読んでいると、<Route element={<SomeComponent />} />のような記述を目にします。しかし、このSomeComponentがどこからインポートされているのか、直感的に理解しにくいことがある。

それに関連して、import SomeComponent from './SomeComponent'という構文も混乱を招く。通常、JavaScriptのimport〜from構文のfromは「ファイルパス」形式で記述しているが、実際にはファイルそのものではなくモジュールやコンポーネントが参照されている。

# Reactのモジュール構成を探る
この疑問を解消するために、Reactのモジュール構成とRouterを定義しているJavaScriptの記述を視覚的に整理することにしてみると以下のような関係性が見えてくる。
1. コンポーネントファイルの構成：src/components/SomeComponent.jsのように分かれている。
2. インポートの仕組み：import SomeComponent from './components/SomeComponent'で読み込まれ、SomeComponentがRouteのelement属性に渡される。
3. Routerの設定：App.jsやroutes.jsなどでRouterが定義され、各パスに対応するコンポーネントがマッピングされる。

# 図解で理解を深める
このリンクを視覚化するために、Reactのモジュール構成とRouter設定の関係を示す図を描いてみる。

![名称未設定-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/136860/750a17e5-7a87-4e73-ad6b-1260acbcc656.png)

これにより、どのコンポーネントがどこから来て、どのようにRouterと繋がっているのかが一目でわかるようになった。


理解が進むことで、Reactでのルーティング設定やモジュール構成がよりスムーズに感じられるはず。もし同じように疑問を持っている方がいたら、ぜひ参考にしてみてほしい！
いろんなアルゴリズム"00.00.00.07"

[知恵の館の目次](https://qiita.com/rionQ/items/0ba7a700620a1d617bc6)
# 立ち位置・仕様
* いろんなアルゴリズムをコピペできるように記録するのみ
* 最適化が目的でない
* たまにメモとして解説を残す
* 最低限でまとめる
* データを抽出しやすいように個別のIDを付与
* あるアルゴリズムにおいて、他のアルゴリズムが登場する際にはそのIDを付与
* IDは16進数表記
* なるべく最低限なパッケージで実装
* なるべく特殊なオブジェクト型は使用しない

# TargetID
00.00.00.07

# ReferenceID

# マージソート
```python:
def mergeSort(l):
    if len(l) <= 1:
        return l

    mid = len(l) // 2
    left = l[:mid]
    right = l[mid:]

    left = mergeSort(left)
    right = mergeSort(right)

    return merge(left, right)

def merge(left, right):
    merged = []
    left_i, right_i = 0, 0

    while left_i < len(left) and right_i < len(right):
        if left[left_i] <= right[right_i]:
            merged.append(left[left_i])
            left_i += 1
        else:
            merged.append(right[right_i])
            right_i += 1

    if left_i < len(left):
        merged.extend(left[left_i:])
    if right_i < len(right):
        merged.extend(right[right_i:])

    return merged
```

# 使用方法
```python:
l = [5,2,3,6,2,1]
l = mergeSort(l):
print(l) # [1, 2, 2, 3, 5, 6]
```
torchvisionのDatasetFolderでSVGを読み込む

ImageFolderのようにベクター画像のSVGを読み込みたい。

# 💡アプローチ
1. ImageFolderの親クラスDatasetFolderはloaderメソッドでファイルの読み込みロジックを定義しているので、SVG用のloaderメソッドsvg_loaderを作る。
2. svg_loaderでは、読み込んだSVGファイルをcairosvgのsvg2pngでラスタ画像化。ラスタ画像化したデータをBytesIOとPillowを組み合わせてImageとしてロード。
3. svg_loaderをDatasetFolderのインスタンス化の際にloaderに指定する。

# 📄コード
```python:ecample.py

from PIL import Image
from io import BytesIO
from cairosvg import svg2png
from torchvision.datasets.folder import DatasetFolder

def svg_loader(path: Union[str, Path]) -> Image.Image:
  # svgからpngへ変換
  # (background_color="white"とか与えると背景色を指定できる。)
  png = svg2png(bytestring=svg_data)
  # Imageにして返す。
  return Image.open(BytesIO(png)).convert('RGB')
  
dataset = DatasetFolder(root=dataset_svg, loader=svg_loader, extensions=("svg"))
```

👍
いろんなアルゴリズム"00.00.00.06"

[知恵の館の目次](https://qiita.com/rionQ/items/0ba7a700620a1d617bc6)
# 立ち位置・仕様
* いろんなアルゴリズムをコピペできるように記録するのみ
* 最適化が目的でない
* たまにメモとして解説を残す
* 最低限でまとめる
* データを抽出しやすいように個別のIDを付与
* あるアルゴリズムにおいて、他のアルゴリズムが登場する際にはそのIDを付与
* IDは16進数表記
* なるべく最低限なパッケージで実装
* なるべく特殊なオブジェクト型は使用しない

# TargetID
00.00.00.06

# ReferenceID

# ヒープソート
```python:
def heapSort(l):
    n = len(l)
    for i in range(n//2-1, -1, -1):
        heapify(l, i, n)

    for i in range(n-1, 0, -1):
        l[0], l[i] = l[i], l[0]
        heapify(l, 0, i)
        
    return l

def heapify(l, i, heap_size):
    largest = i
    left = 2 * i + 1
    right = 2 * i + 2

    if left < heap_size and l[left] > l[largest]:
        largest = left

    if right < heap_size and l[right] > l[largest]:
        largest = right

    if largest != i:
        l[largest], l[i] = l[i], l[largest]
        heapify(l, largest, heap_size)
```

# 使用方法
```python:
l = [5,2,3,6,2,1]
l = heapSort(l):
print(l) # [1, 2, 2, 3, 5, 6]
```
いろんなアルゴリズム"00.00.00.05"

[知恵の館の目次](https://qiita.com/rionQ/items/0ba7a700620a1d617bc6)
# 立ち位置・仕様
* いろんなアルゴリズムをコピペできるように記録するのみ
* 最適化が目的でない
* たまにメモとして解説を残す
* 最低限でまとめる
* データを抽出しやすいように個別のIDを付与
* あるアルゴリズムにおいて、他のアルゴリズムが登場する際にはそのIDを付与
* IDは16進数表記
* なるべく最低限なパッケージで実装
* なるべく特殊なオブジェクト型は使用しない

# TargetID
00.00.00.05

# ReferenceID

# クイックソート
```python:
def quickSort(l):
	if len(l) <= 1:
		return l

	pivot = l[0]
	left = []
	center = []
	right = []

	for v in l:
		if v < pivot:
			left.append(v)
		elif v > pivot:
			right.append(v)
		else:
			center.append(v)
	left = quickSort(left)
	right = quickSort(right)

	return left + center + right
```

# 使用方法
```python:
l = [5,2,3,6,2,1]
l = quickSort(l):
print(l) # [1, 2, 2, 3, 5, 6]
```
いろんなアルゴリズム"00.00.00.04"

[知恵の館の目次](https://qiita.com/rionQ/items/0ba7a700620a1d617bc6)
# 立ち位置・仕様
* いろんなアルゴリズムをコピペできるように記録するのみ
* 最適化が目的でない
* たまにメモとして解説を残す
* 最低限でまとめる
* データを抽出しやすいように個別のIDを付与
* あるアルゴリズムにおいて、他のアルゴリズムが登場する際にはそのIDを付与
* IDは16進数表記
* なるべく最低限なパッケージで実装
* なるべく特殊なオブジェクト型は使用しない

# TargetID
00.00.00.04

# ReferenceID

# シェルソート
```python:
def shellSort(l):
    n = len(l)
    h = 1
    while h < n / 9:
        h = h * 3 + 1
    while h > 0:
        for i in range(h, n):
            j = i
            while j >= h and l[j-h] > l[j]:
                tmp = l[j-h]
                l[j-h] = l[j]
                l[j] = tmp
                j -= h
        h = h // 3
    return l
```

# 使用方法
```python:
l = [5,2,3,6,2,1]
l = shellSort(l):
print(l) # [1, 2, 2, 3, 5, 6]
```
【puppeteer】ログイン認証が必要なWebサイトで手動でログインだけした後に自動でブラウザ操作してスクレイピングする

# 背景
タイトルの通り。
以前、ダルいブラウザ操作を自動化しようと思ってスクレイピングに手を出すも、ログイン認証が必要なWebアプリケーションはURLにアクセスしてもログイン画面に飛ばされてしまうため「スクレイピングしょーもな」となっていた。
（本目的でない且つ一度しか出ないログイン画面の操作を自動化するのは面倒くさい。つーか単純操作を手抜きする精神で始めたような人間がスクレイピングの細かい部分まで学習するわけねーだろ）

最近、ログインだけ手動で行って、ログイン済みのcookieを保存しておき、それを読み込めばログイン操作無しでスクレイピングできることを知ったので備忘として残しておく。

# やりかた
ログインしてcookieを保存する処理とメインのスクレイピングの2つを作る。

Node.jsが好きなので[Puppeteer](https://pptr.dev/)というライブラリを使ってスクレイピングを行う。
手法自体は他のライブラリにも使いまわせると思う。

## 1.cookieの保存
setTimeoutで待機している間に手動でログイン操作するという、ただそれだけのこと。
```javascript:cookie.js
const fs = require('node:fs');
const puppeteer = require('puppeteer');

// メニュー画面のURLを指定してもログインしていないとログイン画面にリダイレクトされる的なサイト
const url = 'https://example.com/menu';

(async () => {
  // GUI表示したいのでヘッドレスモードをオフにする
  const browser = await puppeteer.launch({ headless: false });
  const page = await browser.newPage();

  // 目的のサイトに移動してもログイン画面にリダイレクトされる想定
  await page.goto(url);

  // 任意の時間を指定して手動でログインを済ませる（後々ここを自動化しても良い）
  await new Promise(resolve => setTimeout(resolve, 60000));

  // cokkieを取得してファイルとして保存する
  const cookies = await page.cookies();
  fs.writeFileSync('cookies.json', JSON.stringify(cookies));

  // ブラウザを閉じる
  await browser.close();
})();
```
## 2.保存したcookieを読み込んでスクレイピングする
cookieを読み込んでやりたいスクレイピングをやるだけ
```javascript:scraping.js
const fs = require('node:fs');
const puppeteer = require('puppeteer');

// メニュー画面のURLを指定してもログインしていないとログイン画面にリダイレクトされる的なサイト
const url = 'https://example.com/menu';

(async () => {
  const browser = await puppeteer.launch({ headless: false });
  const page = await browser.newPage();

  // 1.で保存したcookieを読み込む（期限切れ注意）
  const cookies = JSON.parse(fs.readFileSync('cookies.json'));
  await page.setCookie(...cookies);

  // 目的のサイトのログイン後の画面が表示されるはず
  await page.goto(url);

  // 任意の自動操作（例: 特定のクラス名のボタンクリック）
  await page.click('.search-btn');

  await browser.close();
})();
```

# まとめ
細かいことはいいからさっさとWebスクレイピングしたい人向け。
手動ログイン操作部分を自動化したり、2つの処理をまとめたり、工夫の余地はあると思います
統計検定2級 標準誤差

## はじめに
今回は統計検定2級の標準誤差について学んでいこうとおもいます！！

# 📊 標準誤差（Standard Error）とは？

**標準誤差（SE）** とは、サンプルから計算した **統計量（例：平均）** のばらつきの程度を表す指標です。

---

## 🔍 標準誤差の意味

- 標準誤差は、**「標本平均が母平均からどれくらいズレるか」** を示す。
- 同じ母集団から何度もサンプルを取って平均を計算すると、その平均値は毎回少しずつ違う。
- その**ばらつきの大きさ**が標準誤差。

---

## 🧮 標準誤差の公式

### 母分散が既知の場合

$$
SE = \frac{\sigma}{\sqrt{n}}
$$

- $\sigma$：母標準偏差
- $n$：標本サイズ

---

### 母分散が未知の場合（通常はこちら）

$$
SE = \frac{s}{\sqrt{n}}
$$

- $s$：標本標準偏差

---

## 🧠 標準偏差との違い

| 用語 | 意味 |
|------|------|
| 標準偏差（SD） | データそのもののばらつき |
| 標準誤差（SE） | 標本平均など**推定値のばらつき**（＝推定の不確かさ） |

---

## 📈 例：95%信頼区間の計算

サンプル平均 $\bar{x}$ の 95% 信頼区間は次のように表される：

$$
\bar{x} \pm 1.96 \times SE
$$

（※ 標本サイズが大きいときは正規分布近似が使える）

## 📘 想定問題（復習）

ある商品の重量は平均100g、標準偏差10gの正規分布に従うとする。
標本を n = 1, 3, 10, 30, 50, 100, 1000 個抽出したとき、母平均の95%信頼区間を求めよ。


```py:
# 📌 日本語フォントインストール（Google Colab専用）
!apt-get -y install fonts-ipafont-gothic > /dev/null

# ライブラリのインポート
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.font_manager import FontProperties
from scipy.stats import norm

# フォント設定（日本語対応）
jp_font = FontProperties(fname="/usr/share/fonts/opentype/ipafont-gothic/ipag.ttf")
plt.rcParams['font.family'] = jp_font.get_name()

# パラメータ設定
mu = 100       # 母平均
sigma = 10     # 母標準偏差
z = norm.ppf(0.975)  # 95%信頼区間のz値（約1.96）
sample_sizes = [1, 3, 10, 30, 50, 100, 1000]

# 計算
data = []
for n in sample_sizes:
    se = sigma / np.sqrt(n)
    lower = mu - z * se
    upper = mu + z * se
    data.append({
        "標本サイズ (n)": n,
        "標準誤差 (SE)": round(se, 3),
        "95%信頼区間 下限": round(lower, 2),
        "95%信頼区間 上限": round(upper, 2)
    })

df = pd.DataFrame(data)
display(df)

# グラフ描画
plt.figure(figsize=(10, 6))
plt.plot(sample_sizes, df["標準誤差 (SE)"], marker='o', label='標準誤差 (SE)')
plt.title("標本サイズごとの標準誤差と95%信頼区間", fontsize=14, fontproperties=jp_font)
plt.xlabel("標本サイズ (n)", fontsize=12, fontproperties=jp_font)
plt.ylabel("標準誤差", fontsize=12, fontproperties=jp_font)
plt.grid(True)
plt.xticks(sample_sizes)
plt.legend(prop=jp_font)
plt.show()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599049/0074c495-b55a-40d4-ae1d-428e7d93b0bc.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599049/902cdec0-8f78-4233-9f88-1ed1c5adca27.png)
いろんなアルゴリズム"00.00.00.03"

[知恵の館の目次](https://qiita.com/rionQ/items/0ba7a700620a1d617bc6)
# 立ち位置・仕様
* いろんなアルゴリズムをコピペできるように記録するのみ
* 最適化が目的でない
* たまにメモとして解説を残す
* 最低限でまとめる
* データを抽出しやすいように個別のIDを付与
* あるアルゴリズムにおいて、他のアルゴリズムが登場する際にはそのIDを付与
* IDは16進数表記
* なるべく最低限なパッケージで実装
* なるべく特殊なオブジェクト型は使用しない

# TargetID
00.00.00.03

# ReferenceID

# 挿入ソート
```python:
def insertionSort(l):
	for i in range(1, len(l)):
		tmp = l[i]
		j = i - 1

		while j >= 0 and l[j] > tmp:
			l[j+1] = l[j]
			j -= 1
		l[j+1] = tmp
	return l
```

# 使用方法
```python:
l = [5,2,3,6,2,1]
l = insertionSort(l):
print(l) # [1, 2, 2, 3, 5, 6]
```
UdonSharpをノリから理解する

# なんで書いた？
すでに詳しい人が解説しており、私みたいな新参者が書く必要はないかと思いました
……が、なんかノリが合うような記事を見つけられませんでした。
実際のコードを自分で書いてまとめたほうが理解につながるな、と思ったのでここに書きながら理解を深めます。

# 参考
コードの借用とかはしてないですが、読んでる中で特に参考になったのでありがとうの意をこめて

https://booth.pm/ja/items/6288167

# 前提とする知識
C#でFizzBuzzが組めるくらいの知識
「Unity+C#ではイベント関数をoverrideしたスクリプトをアタッチしているなあ」が理解できる程度の知識
「ネット同期なんもわからん」といえるくらいの謙虚さ

# 役割分担
VRCのUIと連携する必要がある機能、グラブ判定であるとか、トリガーであるとかはVRChatづてにもらう他ない。
オブジェクトに付与する必要のある性質はコンポーネントとして用意してある。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/422353/797bf9d7-460f-47b6-8439-2e45b0e568f4.png)
つまり、「持てるよ！」だけだったらわざわざUdonSharpを書かなくてもよいということだ。
しかしそれ以上のことがしたくなった。
投げたら3秒後に爆発するとか、ボタンを押したときに物が降ってくるとか、何をするかという動作を書くのにUdonSharpは使われる。

# Unity+C#との違い
何にしてもVRCHATからの情報が大事になってくる。
UdonSharpプログラムはUdonSharpBehaviourを継承しているが、これのイベント関数を上書きすることで挙動を記述できる。
例えばOnPickupで持ったときの挙動を書いたり。
また、VRCHATのUIを通して更新される状況、例えばOwnerかどうかなどはグローバル関数で用意されているのでそれを呼び出す。
例えばNetworking.IsOwner。

UdonSharpはローカル動作に限ればほぼC#と変わらないのでそこに難しい部分は特にない。
が、リモート動作に関わる部分で制約があったり、超最新の機能が使えなかったりという方向で制約がかかる。
またそれをどのように送受信しているか、どのようにコールバックが呼び出されるかというVRCHAT側の実装に気を払う必要がある。
ここはいろいろ調べたり実験したりして進める。
・static変数は使えない
・Listがつかえない（arrayは使える）
・TryGetComponentがつかえない（GetComponentは使える）

・これは1.0以前のU#の話？

https://qiita.com/toRisouP/items/16bd06aa303a1bb1a747

# 厄介な仕様の回避
どうやらFukuroUdonというライブラリが便利っぽい。
というより使わないと不便っぽい。
わかりやすく便利そうなのがその中の以下。

https://github.com/mimyquality/FukuroUdon/wiki/Manual-ObjectSync

VRCHATデフォルトの同期方法だと融通が効かないのでUdonSharpとして同期させちゃる！
というのがこの機能。
うーん便利そう
（ただしアタッチの順で実行順が変わることには注意）

# 同期のさせ方
①SendCustomNetworkEventで引数なしイベントを起こす
（eventNameは関数名のstringなのでnameof(関数名)として渡そう）
②UdonSynced属性をつけた変数だけをRequestSerializationで同期する
（Synchronization MethodはManualにしている前提）
というざっくり2つの方法があるっぽい。
まあでも別にそんなにややこしくないか。

# 同期の具体的なさせ方
問題は、これを具体的にどう使うのかという話だ。
①は正直使いにくい。拡張性が皆無である。
ということで②を使ってごりごりとコーディングしていくことになるのだが、公式のAPIをみてもしっくりこない。
ということで具体例を見ていこう。
以下の記事はライブラリを提供しているみみーさんの記事。
https://note.com/mimyquality/n/n159a765dfa15
三目並べの例がとてもわかりやすいのだが、同期をリクエストしたのち、リクエストした本人はそれを反映、されたほうは変化後の変数に合わせて変化とする。
こういった更新フローになるので時系列を含めた「経緯」にかかわらず「結果」によって反映することになる。
逆にいえば経緯をうまく再現するような同期は面倒になる。
過去のA状態を保持しておき、B状態に同期されたときその差分を計算することになるからだ。

# アンチパターン
この「経緯」自体を送信したくなるかもしれない。
しかしこれは一度でも抜けがあると成立しなくなることに注意だ。
10回のカウントアップで一度でも抜けがあると最終的な見た目に変化が起きる。
これを防ぐのが根本的に難しい。
擬似的なUDPなどを組まないといけなくなる。
一方結果を送るやり方の場合は最終結果を送る通信が失敗するとずれるのだが、なんども通信をやり直せばいつか同期される。
こっちのほうが数段楽なので、この結果を送信する方法をメインとするほうがよい。
（通信量を考えてときどき状態を同期するがほとんどはこの経緯にあたるところの差分を送信する方法もあるが……難しくなりすぎるし高度だし、そんな需要が出るほど複雑なゲームはVRCHAT上に実装するのが無理なのではと思ったりもする）

# いかがでしたか？
いろんなアルゴリズム"00.00.00.02"

[知恵の館の目次](https://qiita.com/rionQ/items/0ba7a700620a1d617bc6)
# 立ち位置・仕様
* いろんなアルゴリズムをコピペできるように記録するのみ
* 最適化が目的でない
* たまにメモとして解説を残す
* 最低限でまとめる
* データを抽出しやすいように個別のIDを付与
* あるアルゴリズムにおいて、他のアルゴリズムが登場する際にはそのIDを付与
* IDは16進数表記
* なるべく最低限なパッケージで実装
* なるべく特殊なオブジェクト型は使用しない

# TargetID
00.00.00.02

# ReferenceID

# 選択ソート
```python:
def selectionSort(l):
    n = len(l)
    for i in range(0, n-1):
        min = i
        for j in range(i+1, n):
            if l[j] < l[min]:
                min = j
        tmp = l[min]
        l[min] = l[i] 
        l[i] = tmp
    return l
```

# 使用方法
```python:
l = [5,2,3,6,2,1]
l = selectionSort(l):
print(l) # [1, 2, 2, 3, 5, 6]
```
Oracle Databaseでswapを軽減させる方法

## はじめに
Oracle Databaseを運用していると、システムメモリの不足によりスワップ（swap）領域が頻繁に使われる状況に遭遇することがあります。
これは、データベースのパフォーマンス低下やレスポンスタイム悪化の直接的な原因になります。

本記事では、Linux上で稼働するOracle Databaseにおいて、swapの発生を軽減または回避するための方法を解説します。
なお、直近でDBサーバのスワップの障害が発生したため、調査した結果を纏めております。

## 1.なぜswapが問題なのか？
**swap発生の主な影響：**
・Oracle SGAの一部がディスクへ退避 → ディスクI/Oの増加
・OSのコンテキストスイッチが増加 → CPU負荷の上昇
・PGA処理の遅延 → ユーザ処理時間の悪化

## 2. swapが発生する主な原因 
・SGAサイズやPGAサイズが物理メモリに対して大きすぎる
・Oracleインスタンスが複数起動している
・HugePagesが未設定（SGAが通常の4KBページで動作）
・メモリ割り当てに対するカーネルチューニング不足
・OS上で他のプロセス（監視、ジョブ、バックアップなど）がメモリを大量使用している

## 3. swap軽減のための対策方法
以下の施策を実施することで、swapの発生を軽減することができます。
### 3-1. HugePagesの利用
#### 1) Oracleメモリ構成の変更（AMM → ASMM）
AMM（SGA_TARGET＋MEMORY_TARGET）を使用している場合は、AMMとHugePagesは併用不可のため、ASMMに切り替えが必要です。

※AMM：Automatic Memory Management（自動メモリ管理）
　OracleがSGA（System Global Area）とPGA（Program Global Area）の両方を1つのパラメータ（MEMORY_TARGET）で自動調整する仕組み。
　使用される共有メモリ領域は``/dev/shm``で、HugePagesと併用不可。

※ASMM：Automatic Shared Memory Management（自動共有メモリ管理）
　OracleがSGAのみを自動管理し、PGAは別のパラメータ（PGA_AGGREGATE_TARGET）で個別に設定。
　``SGA_TARGET`` や ``SGA_MAX_SIZE`` を使用。HugePagesと併用可能。

```
-- 設定例
ALTER SYSTEM SET MEMORY_TARGET=0 SCOPE=SPFILE;
ALTER SYSTEM SET SGA_TARGET=8G SCOPE=SPFILE;
ALTER SYSTEM SET PGA_AGGREGATE_TARGET=4G SCOPE=SPFILE;
```

#### 2) Transparent HugePages（THP）の無効化
Transparent HugePagesを使用すると、実行中にメモリ割当ての遅延が生じるため、Transparent HugePagesはOracle Databaseサーバで無効にすることが推奨されています。

　・ ``/etc/grub.conf`` に ``transparent_hugepage=never`` を設定追加
　・ ``grub2–mkconfig`` コマンドを実行して ``grub.cfg``ファイルを再生成
　・ OS再起動後、``cat /sys/kernel/mm/redhat_transparent_hugepage/enabled``で ``never`` になっていることを確認
 
#### 3) HugePagesを有効化する
SGAが8GB(昔は4GBぐらい)を超える場合はHugePagesを利用することが推奨されています。HugePagesでは、OSがSGA領域をswap対象外とします。

　・ Oracle提供スクリプト（Doc ID 401749.1）で必要ページ数を見積
　・ ``/etc/sysctl.conf`` に ``vm.nr_hugepages`` を設定
　・ ``limits.conf``に oracleユーザの memlock 制限を設定
　・ OS再起動後、``/proc/meminfo`` でHugePagesの使用を確認

### 3-2. OSのカーネルパラメータ設定

#### 1) vm.swappinessの設定

/etc/sysctl.conf に以下を設定して、スワップの使用を抑制する。
swappiness は0～100の値（低いほどswapを回避）

``` bash {iscopy=true}
vm.swappiness = 1
```

以下のコマンドにて設定を反映する。
``` bash {iscopy=true}
sysctl -p
```

#### 2) vm.min_free_kbytesの設定
``vm.min_free_kbytes`` パラメータは、Linuxが確保しておくべき最小限の「空きメモリ」量（KB単位）を指定します。つまり、システムはこの量を下回らないように積極的にメモリを解放します。
この値が小さいと、カーネルはよりギリギリまでメモリを使用し、突然のメモリ要求に対応できずスワップを発生させることがあります。
この値をある程度大きくすると、予防的にメモリの余裕を確保し、スワップが発生しにくくなるという効果があります。

/etc/sysctl.conf に以下を設定して、空きメモリ量を指定する(設定例:128MB → 131072KB)。

``` bash {iscopy=true}
vm.min_free_kbytes = 131072
```

以下のコマンドにて設定を反映する。
``` bash {iscopy=true}
sysctl -p
```


## 4. まとめ
Oracle Databaseのスワップ発生はパフォーマンスに直結する重要な要素です。適切なメモリチューニング、HugePagesの活用、THPの無効化など、OSとDB両面からの対策が必要です。これらを実施することで、より安定したDB運用が可能になります。
impdp実行後にパッケージとファンクションのステータスがinvalidになる

# 事象

regionsテーブルを参照するパッケージとファンクションがそれぞれ存在します。

```
CREATE OR PACKAGE P_EXAMPLE AS 
  ASIA CONSTANT regions.region_name%type := 'Asia'; 
END P_EXAMPLE;
```

```
CREATE OR FUNCTION F_EXAMPLE RETURN regions.region_id%type AS 
  ret regions.region_id%type;
BEGIN
  SELECT region_id into ret FROM regions WHERE region_name = P_EXAMPLE.ASIA;
  RETURN ret;
END;
```

このパッケージとファンクションはそれぞれコンパイルOKの状態です。

```
SELECT OBJECT_NAME, OBJECT_TYPE, STATUS 
FROM USER_OBJECTS 
WHERE OBJECT_NAME LIKE '%EXAMPLE'
```

|OBJECT_NAME|OBJECT_TYPE|STATUS|
|:---|:---|:---|
|F_EXAMPLE|FUNCTION|VALID|
|P_EXAMPLE|PACKAGE|VALID|

この状態で、regionsテーブルに対し、impdpを実施します。

```
impdp dump_user/passw0rd@localhost:1521/xepdb1 \
	directory=DUMP_DIRECTORY \
	LOGFILE=impdp.log \
	dumpfile=HR.dmp \
	content=all \
	table_exists_action=replace \
	tables=HR.regions
```

すると、impdpは成功しますが、regionsテーブルを参照するパッケージとファンクションのステータスがINVALIDになってしまいました。

```
SELECT OBJECT_NAME, OBJECT_TYPE, STATUS 
FROM USER_OBJECTS 
WHERE OBJECT_NAME LIKE '%EXAMPLE'
```

|OBJECT_NAME|OBJECT_TYPE|STATUS|
|:---|:---|:---|
|F_EXAMPLE|FUNCTION|INVALID|
|P_EXAMPLE|PACKAGE|INVALID|

# 考察

今回はimpdpのtable_exists_actionオプションにreplaceを指定した結果、内部的にregionsテーブルのdropが行われ、関連するパッケージやファンクションのステータスがINVALIDになったものと思われます。

もっとも、impdpは内部的な仕様が公開されていないこともあり、table_exists_actionオプションにreplaceを指定しても、INVALIDにならない環境もあるでしょう。table_exists_actionオプションに別の値を指定したからといって、絶対にこの問題が起こらないかといわれると、それも断言できません。

Oracle Databaseでは、ステータスがINVALIDのパッケージやファンクションにアクセスすると、再コンパイルしますが、状況によっては再コンパイルが走らない、再コンパイルに失敗してINVALIDのままになるといったことも考えられます。現にわたしはそのような状況におちいったことがあります。

**とりあえず、パッケージやファンクションを多用する環境においてimpdpを実施する場合は、そのあたりも考慮して、運用設計する必要があると思います。**

※ Oracle Databaseはちょっとしたパラメータの差異やデータ量の違いによって、挙動を大きく変えることがあるため、問題の調査にてこずりがちです。Oracle社や第三者保守のサポート窓口に問い合わせようにも、何をどこまで情報をまとめればよいのかに困ることもしばしばあります (保守期限が切れているなんてことも…)

# 環境情報

- Oracle Database 21c Express Edition Release 21.0.0.0.0 - Production
いろんなアルゴリズム"00.00.00.01"

[知恵の館の目次](https://qiita.com/rionQ/items/0ba7a700620a1d617bc6)
# 立ち位置・仕様
* いろんなアルゴリズムをコピペできるように記録するのみ
* 最適化が目的でない
* たまにメモとして解説を残す
* 最低限でまとめる
* データを抽出しやすいように個別のIDを付与
* あるアルゴリズムにおいて、他のアルゴリズムが登場する際にはそのIDを付与
* IDは16進数表記
* なるべく最低限なパッケージで実装
* なるべく特殊なオブジェクト型は使用しない

# TargetID
00.00.00.01

# ReferenceID

# バブルソート
```python:
def bubbleSort(l):
    n = len(l)
    for i in range(n):
        for j in range(n-1):
            if l[j] >= l[j+1]:
                tmp = l[j+1]
                l[j+1] = l[j]
                l[j] = tmp
    return l
```

# 使用方法
```python:
l = [5,2,3,6,2,1]
l = bubbleSort(l):
print(l) # [1, 2, 2, 3, 5, 6]
```
webサーバーって何

### 簡単に言うと　運送屋
バックエンド(APIサーバーとか)とフロントエンド(Webブラウザで動作するアプリケーション)の間に入って、橋渡しのような役割を担う

webサーバは
1. フロントエンドからPOST/GETなどのHTTPリクエストを受け取る
1. そのリクエストの宛先を確認して、どこに届けるべきかを確認する
1. その宛先(適切なバックエンドのAPIサーバなど)に届ける
1. バックエンドでの処理が終わったら、処理したもの(レスポンス)を受け取る
1. 最初にリクエストを送ったフロントエンドにレスポンスを届ける

Nginxとかは超優秀な運送屋みたいな感じ

[Webサーバ（ウェブサーバ / HTTPサーバ）とは？](https://e-words.jp/w/Web%E3%82%B5%E3%83%BC%E3%83%90.html)
３Dセキュアとは

## 3Dセキュア
オンライン決済時にクレジットカード会社を通して本人認証を行うシステム
**<目的>**
オンライン決済の安全性向上とクレジットカードの不正利用防止  

※日本は、2025年3月末までに原則すべてのEC加盟店がEMV 3-Dセキュアを導入するよう要求

### ３Dセキュア導入後のフロー　
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4077009/a7247040-9de3-4bd9-a726-b2cbc3d31d25.png)
商品購入時にECサイトカード情報入力後、カード会社のページにリダイレクトされてそこで本人認証を行う。これによりECサイトはカード情報を保持しなくて良いためセキュリティ向上につながる。

### 3Dセキュア1.0と3Dセキュア2.0(EMV 3Dセキュア)

**◇3Dセキュア 1.0**
1999年にVisaが開発
 **<特徴>**
- すべての取引で認証を求めるため、ユーザーは毎回パスワード入力が必要
- 固定パスワード方式が主流で、使い回しや忘れやすい問題が発生
- ユーザーにとって煩雑  

**<課題>**  
ユーザーの利便性が低く、不正利用防止には一定の効果があるものの、パスワードの安全性や入力の手間が問題
***
 **◇3Dセキュア 2.0（EMV 3-D Secure）**
2016年に発表された改良版

**<特徴>**
**リスクベース認証**  
- 取引の安全性をリアルタイムで分析し、必要な場合のみ追加認証を実施
- 信頼できる取引では認証を省略し、ユーザーの手間を減らす  

 **多要素認証の導入**  
- ワンタイムパスワード（OTP）や生体認証（指紋・顔認証）を利用可能  

 **モバイル＆マルチデバイス対応**  
- スマホ決済にも適応し、アプリ内でスムーズに認証できる  

## 参考文献
[3Dセキュア 2.0について - TechFirm](https://www.techfirm.co.jp/blog/3dsecure2point0)
トリガ入力信号が来ているかLEDで確認する回路(FPGA,VHDL)

# 概要
基板の外部から入力するトリガ信号が来ているかをLEDで目視確認できるような構成について紹介します。
トリガはFPGAで受けて処理していますので、残念ながらフルディスクリートではありません。

# おすすめのバッファIC

TI社の[SN74LV1T34DBVR](https://www.ti.com/product/ja-jp/SN74LV1T34/part-details/SN74LV1T34DBVR)がおすすめです。

* 単一電源で動作可能
* FPGAのIO電源3.3Vのとき、トリガ入力1.8V～5Vまで入力可能
* 安い(40円くらい)
* 手づけでもリフローでも実装がしやすい((SOT-23)
* IO電圧が1.8でも、3.3Vでも、5Vでも使える

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/204268/50079cc0-0f0f-4a69-a14b-78a42c43e6ec.png)

基板外部からの信号を入力するので、念のため保護ダイオードとプルダウンして使っているケースが多いです。

# 利用するFPGAのモジュール

過去に紹介したVHDLのモジュールを組み合わせることで、処理を実現しています。
各モジュールの中身は過去記事を参照してください。

* [FPGA入力信号のエッジ検出
](https://qiita.com/takeru0x5569/items/56cf4d1da39b38b75521)

こちらのモジュールで入力信号の同期化及びエッジの検出をしています。

* [FPGA内部の短いトリガをLEDで目視できるように引き延ばすRTL](https://qiita.com/takeru0x5569/items/85e4e44bdb21d4aa1344)

こちらの記事で紹介したモジュールに検出した立ち上がりエッジを入力することで、一定時間より長くLEDを点灯させることができ、目視可能となります。

# VHDLソース
過去記事のモジュールをインスタンスしているだけですが、このようにTRIGGER_INDICATORモジュールとして用意しておくと再利用しやすいです。

```vhdl:TRIGGER_INDICATOR.vhd
-------------------------------------------------------------------
-- トリガインジケーター
-- 非同期のトリガ入力信号を同期化して、立ち上がりエッジを検出し、
-- エッジから一定時間引き延ばしたモニタ信号を生成する。モニタ信号はLEDにて目視。
-------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;

entity TRIGGER_INDICATOR is
    Port(
    i_CLK           :in  std_logic;
    i_RST_p         :in  std_logic;
    i_TRIGGER_p     :in  std_logic;--
    o_TRIGGER_SYNC  :out std_logic;--同期化したトリガ
    o_TRIGGER_RISE  :out std_logic;--トリガの立ち上がりエッジ
    o_TRIGGER_FALL  :out std_logic;--トリガの立ち下がりエッジ
    o_INDICATOR_p   :out std_logic --トリガ入力目視用のインジケーター(LEDなどに接続)
    );
end entity;

architecture RTL of TRIGGER_INDICATOR is

signal sync_trg         :std_logic;
signal rise_p           :std_logic;
signal fall_p           :std_logic;
signal extend_pulse_p   :std_logic;

begin
--同期化FFとエッジ検出
u_SYNCHRO_DFF:entity work.SYNCHRO_DFF
    port map
    (i_CLK     =>i_CLK
    ,i_RST_p   =>i_RST_p
    ,i_PORT    =>i_TRIGGER_p
    ,o_SYNCHRO =>sync_trg
    ,o_RISE_p  =>rise_p
    ,o_FALL_p  =>fall_p
    );
--駆動パルスのエッジを引き延ばす(レジスタリード用)
u_PULSE_EXTENDER:entity work.PULSE_EXTENDER
     generic map(
     EXTEND_CYCLE => 25000000--引き延ばすクロックサイクル数(50MHzで0.5s)
     )
     port map
    (i_CLK              =>i_CLK
    ,i_RST_p            =>i_RST_p
    ,i_SOURCE_PULSE_p   =>rise_p
    ,o_EXTEND_PULSE_p   =>extend_pulse_p
     );

o_TRIGGER_SYNC <=sync_trg;
o_TRIGGER_RISE <=rise_p;
o_TRIGGER_FALL <=fall_p;
o_INDICATOR_p  <=extend_pulse_p;
end architecture;

```
RESTとは

## RESTとは?
RESTとは、Webアプリケーションを開発する際の設計思想のこと。
クライアント・サーバ間の通信が効率的で一貫性のあるものとなることを目的とする。

Webアプリケーションでは、サーバ側がDBなどから取得した情報をリソースとしてWeb上に用意するが、その**リソースに対してどのように操作するか**についてのガイドラインを定めたものがRESTである。

## 代表的なRESTのガイドライン
#### ■ URIでリソースを識別する
Web上に配置するリソースには、一意に識別できるURIを割り当てる。
Webにおける、データの住所みたいなもの。
例えば /users/1 は「IDが1のユーザ」を表す。

#### ■ HTTPメソッドでリソースを操作する
URIで特定したリソースに対する操作をHTTPメソッドで使い分ける。
| HTTPメソッド | 用途 |
|:------------|:----|
|GET|取得|
|POST|新規登録|
|PUT|更新|
|DELETE|削除|

#### ■ レスポンスボディに適切なデータ形式を使用
サーバから取得するデータはレスポンスボディに記述されるが、このレスポンスボディのデータ形式は、視認性の高いJSONやXML形式を使用する。

レスポンスボディのデータ形式はクライアント側で指定できる。
(JSONの場合は、HTTPリクエストの「Acceptヘッダ」に「application/json」と指定する)
#### ■  リクエストボディに適切なデータ形式を使用
リクエストのデータ形式も、クライアント側で決めることができる。
(JSONの場合は、HTTPリクエストの「Content-Typeヘッダ」に「application/json」と指定する)

#### ■  ステータスコードの活用
リクエストに対する処置の結果を表すステータスコードを活用する。
|ステータスコード|意味|
|:------------|:---|
|200(OK)|処理が正常に行われた|
|400(Bad Request)|リクエストに不備がある|
|403(Forbidden)|認証されたが権限不足|
|404(Not Found)|リソースが存在しない|
|405(Method Not Allowed)|対応していないメソッドを使用|
|500(Internal Server Error)|サーバ側でエラー発生|

#### ■  サーバ側をステートレスにする
サーバ側は各リクエストを独立したものとして扱い、過去のやり取りを記憶しない。
つまり、セッションを使用しない。
アプリケーションの状態はクライアント側(JavaScript)で管理する。

##### 【補足】サーバ側をステートレスにする理由
スケールアウトに対応するため。
サーバがステートフルだと、サーバマシンの台数を増やした際に困難が生じる。
リクエストをステートの存在するサーバへ振り分けたり、サーバ間でステートの共有をしたりする必要が出てくる。
Dockerインストール後のrunコマンドで「 401 Unauthorized」が出た場合の対処法

最近PCを変えて、docker環境を整えたときに以下の事象が発生したので、
書いておこうと思います。

## 環境
- OS：macOS Sonoma バージョン14.6
- チップ：Apple M3 
- dockerバージョン：Docker version 28.0.4

## 発生した事象
```brew install docker```を実行した後、```docker run hello-world```を実行すると、以下のエラーが出力された。

```
Unable to find image 'hello-world:latest' locally
docker: Error response from daemon: failed to resolve reference 
"docker.io/library/hello-world:latest": failed to authorize: 
failed to fetch oauth token: unexpected status from GET request to 
https://auth.docker.io/token?scope=repository%3Alibrary%2Fhello-world%
3Apull&service=registry.docker.io: 401 Unauthorized
```
## 結論と対処法
### 結論
Docker Hubへの接続設定ができていなかった。

### 過程
原因は、エラー内容にもある通り、401 Unauthorized
dockerはimageがローカルに存在しない場合、Docker Hubにimageを取得しに行く。
その際に、認可が通ってなくてエラーとなっている。
つまり、エラー原因はDocker Hubへの接続がログインできてなかったことにあると考えた。
また、同様の事象が別記事でも起こってることも確認したので、結論に至った。

### 対処法
以下の手順で、Docker Hubへの接続設定を行うことで可能
1. Docker Hubでユーザ登録をする
1. ```docker login -u <username>```の実行
1. 以下のように、ワンタイムコードとコード認証用のリンクが貼られるので、リンクをクリックする（リンク先で認証しないとdocker loginコマンドが終了しない）
    ```
    Your one-time device confirmation code is: ???????
    Press ENTER to open your browser or submit your device code here: https://login.docker.com/activate
    ```
4. 以下のような画面に移動するので、ワンタイムコードを入力して認証を行う
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2005551/e4e95777-e644-462e-b990-79f3e8e7806c.png)

以上で認証完了。すると、CLI側で```Login Succeeded```と表示される。

最後に、最初のdocker run hello-worldを実行すると以下のようにDockerHubからのPullが完了してHello from Docker!と出力されます。
```
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
c9c5fd25a1bd: Pull complete 
Digest: ーーーーーーーーーーー
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (arm64v8)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/

```

## 参考
- https://e-words.jp/w/401エラー.html
- https://qiita.com/2no553/items/0e4348c0fd2678058bed
Excel VBA 連想配列(Dictionary)を理解したい

## 気づいたらAccessのデータベースが16万件を超えていた
AccessからテーブルをExcelに読み込んで、集計したり、テーブルの重複を判定していました。扱うデータの件数が10万件超えると For ~ Next のループだと処理速度が10分を超えてしまいます。
処理速度を上げるため、連想配列について調べました。

``` vb
Sub test()
  
  Dim i As Long
  Dim dData As New Dictionary
  Dim data As Variant
  Dim shDB, shSUM As Worksheet
  
  Set shDB = ThisWorkbook.Sheets("データベース")
  Set shSUM = ThisWorkbook.Sheets("集計")
  
  data = shDB.Range("A1").CurrentRegion
  
  For i = 1 To UBound(data)
  
          If dData.Exists(data(i, 1)) Then
          dData(data(i, 1)) = dData(data(i, 1)) + data(i, 2)
          Else
          dData.Add data(i, 1), data(i, 2)
          End If
          
  Next i
  
  Dim keys As Variant
  keys = dData.keys
  
  For i = 1 To UBound(keys)
          shSUM.Cells(1 + i, "A").Value = keys(i)
          shSUM.Cells(1 + i, "B").Value = dData(keys(i))
  Next i
  
End Sub
```
実行すると、集計シートに名前ごとのスコアが集計された結果が表示されます。
集計シートのC列には、確認のためSUMIFが入力されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3812230/e912f727-e1fb-4013-bcc2-efc03a73106b.png)

## なぜこれで動くのかわからない、ウォッチウインドウで配列の中身を確認してみた

dDataにどのタイミングでデータがキーとデータが入るのかを確認するため、
For i = 1 To UBound(data) の行をブレークポイントにして、ウォッチウインドウを準備します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3812230/9c6709da-dd0c-4d61-ae3a-0b4909e062b2.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3812230/849dcb38-e5a4-4fa6-8db3-193cf826e48c.png)

## ステップイン

data = shDB.Range("A1").CurrentRegion 実行直後
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3812230/f94c6a32-c9d6-4001-8505-4cf3301c1730.png)

dDataはEmpty、dataにはデータベースシートのA列B列の値が格納されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3812230/b0a78b0b-7bcf-4054-b430-803c34bbddbf.png)

If dData.Exists(data(i, 1)) Then
          dData(data(i, 1)) = dData(data(i, 1)) + data(i, 2) 実行直後
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3812230/d9fe6884-ddb9-4f00-9916-6c2db807e75d.png)

dDataに異変が
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3812230/805510b0-dea9-442a-8818-2f271c4771e4.png)

さらに実行
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3812230/2a0083c9-9009-4847-915c-5c336cc65f61.png)

dData.Add data(i, 1), data(i, 2) でひとつずつ格納されていることが理解できました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3812230/2472399b-11be-43be-8aa7-92e3015e9c3d.png)

言葉で説明されても理解できないので、自分で検証してみました。

参考文献
https://www.youtube.com/watch?v=HOTfZnBz_uI
Linucレベル１勉強メモ　＃１３

＃ 第一章　Linuxのインストールと仮想マシン・コンテナの利用

練習問題1.13
fredユーザーが実行しているプロセスnanoを終了させようと、以下の操作を実行しました。

$ whoami
linucuser
$ ps aux | grep nano
fred    2474  0.0 0.0 115104 1804 pts/1 S+ 22:04 0:00 nano sample.txt
$ kill 2474
-bash: kill: (2474) - Operation not permitted

エラーが発生した原因として適切なものを選択してください。

A. fredユーザー以外の一般ユーザーでkillコマンドを実行したから

B. 指定すべきPIDが誤っているから

C. killコマンドの引数にはプロセス名を指定すべきだから

D. killコマンドでシグナルが指定されていないから

E. プロセスを終了できるのはrootユーザーだけだから

（解答）A.

（解説）
一般ユーザーがkillコマンドを使って終了できるプロセスは、そのユーザー自身が実行しているプロセスのみです。設問では、whoamiコマンドの実行結果から、killコマンドを実行したユーザーがfredユーザーではない（linucuserユーザーである）ことがわかります。なお、whoamiコマンドは、コマンドを実行したユーザーを表示するコマンドです。psコマンドの出力から、nanoプロセスのPIDは2474です。killコマンドの引数は間違えていません。引数にプロセス名を指定できるのは、killコマンドではなくkillallコマンドです。killコマンドでシグナルが指定されていない場合はTERMシグナルが使われますので設問のケースでは、シグナルの指定は必要ありません。
Sumsung Galaxy S25 に関するあれこれ

## 概要
Sumsung Galaxy S25を使い始めたのであれこれをまとめる

## 困ったこと
- テザリング
    - セキュリティのデフォルトがWPA3-Personalになっており，Wi-Fi6に対応していないクライアントでは接続できない
    - WPA2-Personalにすることで解決
- VISAタッチ決済
    - Google Walletを使ってセブンイレブンでVISAタッチ決済しようとしたら反応しない
    - ユニクロはできた
JetpackComposeでFlexboxみたいなUIを作る

JetpackComposeでFlexboxみたいなUIの作成方法です。

Flowレイアウトについてはこちらを参照

https://developer.android.com/develop/ui/compose/layouts/flow?hl=ja

FlowRowにViewを入れていくと自動的に折り返します。

```FlowRowSample.kt
@OptIn(ExperimentalLayoutApi::class)
@Composable
fun FlowRowSample() {
    val items = listOf("Arctic Fox", "Bumblebee", "Chipmunk", "Dolphin","Electric Eel", "Flamingo", "Hedgehog", "Koala", "Meerkat")

    Box(
        modifier = Modifier.fillMaxSize(),
        contentAlignment = Alignment.Center
    ) {
        FlowRow(
            horizontalArrangement = Arrangement.Center
        ) {
            items.forEach {
                AssistChip(
                    onClick = {},
                    label = { Text(it) }
                )
            }
        }
    }
}

```

実行結果

![Screenshot_20250504_121305.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274615/4c40ac2a-da12-42c4-acbd-0da3c38c5bda.png)

簡単に実装できそうですね。

以上になります。
Linucレベル１勉強メモ　＃１２

＃ 第一章　Linuxのインストールと仮想マシン・コンテナの利用

練習問題1.12
PIDが568のプロセスが異常な動作をしているので終了させたいと考え、次のコマンドを入力しました。

＃ kill 568

ところがプロセスはそのまま動作し続けています。強制的に終了させるための適切なコマンドを選択してください。

A. kill -HUP 568

B. kill -INT 568

C. kill -TERM 568

D. kill -KILL 568

E. kill 1 568

（解答）　D.

（解説）
KILLシグナルを指定するとプロセスを強制終了させることができるので、正解は選択肢Dです。HUPシグナルはプロセスに対して設定の再読み込みを促したいときに利用します。INTシグナルは一時停止を行います。TERMシグナルはオプションなしでkill　コマンドを実行した場合のデフォルト値です。シグナルを数値で示した場合、１はHUPシグナルになります。

プロセスの終了
* HUP １<br>ハングアップ（端末が制御不能もしくが切断による終了）
* INT（SIGINT） ２<br>キーボードからの割り込み（Ctrl＋Cキー）
* KILL（SIGKILL) 9<br>強制終了
* TERM 15<br>終了（デフォルト）
* CONT 18<br>停止しているプロセスの再開
* STOP 19<br>一時停止
「上下トリミングされたテキストを検索する」Figmaプラグインを作成しました

いたってシンプルな、**上下トリミングされたテキストを検索するFigmaプラグイン**をリリースしました。

「誤ってトリミング設定したままデザインしてしまった」等、ピンチの時にご使用ください。

[Vertical Trim Cap Height Scanner](https://www.figma.com/community/plugin/1496255808052370411/vertical-trim-cap-height-scanner)

![cover_1920x1080.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4033219/9ebf30ac-727e-4950-b7ca-7ef6c12bb73d.jpeg)


## 記事の内容
この記事では、簡単な使い方と、作成した背景をご紹介します。

## 使い方
使い方は、**ボタンをクリックして、表示されたテキストを修正するだけ**です。
修正後は、再度ボタンを押して、検索結果に現れないことを確認してください。

![How to use compression.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4033219/50bb76bc-5deb-4f89-b41c-23535733ce41.gif)

## 修正のコツ
先に「メインコンポーネント」タグのついたテキストから修正して、ボタンを押すと、一気に検索結果を減らすことができます。

## そもそも、上下トリミングについて
上下トリミング自体は、バナーやアイコン、ボタンなどの位置調整には便利で、CSSでもtext-box-trimで調整できるようになりました。
参考：[CSSでテキストの上下余白が調整可能に！text-box-trimの使い方](https://ics.media/entry/250319/)

しかし、流し込む文章やサイト全体に当ててしまうと、line-heightのみで実装できるところに調整が必要になります。
また、デザイナーがトリミング無しに修正する場合も、「使い方」画像のようにテキストの高さが変わってしまい、続く他要素の配置が崩れたりします。


## 作成した背景①
**私自身がやらかしたため作りました**:cry:。
トリミングに気づかずだいぶデザインを作り進めてしまい、「時間がかかってもいいからデザイン全部直して」と言われた時は、「テキストをひとつひとつ開いて確認･･･！？」と絶望しました。

その時は、AIに頼んで、なんとか検索ツールを作り、なんとか効率よく解決できたのでした。

## 作成した背景②
現在フロントエンド勉強中で、JavaScriptとTypeScriptを学んだのち、実践して身につけるため作成しました。

Figma公式ドキュメントや、プラグインを作成された方の記事を読み、AIに頼らず2くらいから作りました。（前回AIによる解決方法をうっすら知っていたため）
10にする前に、AIにレビューしてもらいました。

前回AIに作ってもらったものは世に出せる状態では無かったので、今回ちゃんとコードを理解し、エラー時の処理も入れて作成する機会が出来てよかったです。

審査も無事通りましたので、安心してご使用いただければと思います。

## 最後に
ご要望や改善案などありましたら、コメント欄やFigmaコミュニティからお気軽にお問い合わせください。

また[GitHubリポジトリも公開](https://github.com/waka-works/figma-plugin-vertical_trim_cap_height_scanner)しておりますので、初学者へのアドバイスもお待ちしております！よろしくお願いいたします。


[Vertical Trim Cap Height Scanner](https://www.figma.com/community/plugin/1496255808052370411/vertical-trim-cap-height-scanner)


## 参考記事
作成の際、大変お世話になりました！
- [Figmaプラグイン"Artboard Creator"をリリースするまでの流れ](https://qiita.com/yasuhiro-y/items/16da3a0c02adc7894ea6)
- [【Figma Plugins】はじめてのFigmaのプラグイン作り（セットアップ）](https://qiita.com/degudegu2510/items/929b54959f5917f2b218) 
- [【Figma Plugins】ノードを検索する](https://qiita.com/degudegu2510/items/0a6c6a9e109dbb5d18a1) 
- [【Figma Plugins】UI側とcode側でデータを受け渡す](https://qiita.com/degudegu2510/items/742fbdd3ade2bb61b947) 
- [Figmaプラグイン開発のデバッグ方法](https://qiita.com/maeshiiii/items/a54e0bc8396e902b2a51) 
- [デザイナーでもやればできる！FigmaをJavaScriptで操作してみる](https://qiita.com/hiloki@github/items/8f97c7705d0768c9462d)
【React】devcontainerのreactでlive reloadを効かせる方法


# はじめに

こんにちは。アメリカに住みながら、独学でエンジニアを目指している Taira です。
現在 devcontainer を使用して react 学習しています。codesandbox のようなクラウドサービスならば、`npm run dev`で react のサーバーが起動し、live reload が機能しますが、devcontainer(もしかしたら docker だけの場合も?)の場合は、設定をしないと live reload 機能が作動しません。
本日は tips として記事を書こうと思います。

## devcontainer に containerENV を追加

devcontainer.json に containerEnv を追加すれば live reload が有効化されます。

```json
// See https://containers.dev/implementors/json_reference/ for configuration reference
{
  "name": "Untitled Node.js project",
  "build": {
    "dockerfile": "Dockerfile"
  },
  "customizations": {
    "vscode": {
      "extensions": ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode"]
    }
  },
  "forwardPorts": [5173],
  "containerEnv": {
    "CHOKIDAR_USEPOLLING": "true" // live reloadを有効化
  },

  "remoteUser": "node"
}
```
Go言語 再回帰

# 再回帰とは、
関数自身から関数自身を呼び出す、無限入れ子です。
主に階層構造、フォルダーの検索によく使われます。

```go:fileTools.go
import (
	"os"
	"path/filepath"
)

func findFiles(find string, dir string, deep int) ([]string, error) {
	var fulldir string
	var results []string

	fulldir, err := filepath.Abs(dir)
	if err != nil {
		return results, err
	}

	var _findFiles func(find string, dir string, deep int) ([]string, error)

	_findFiles = func(find string, dir string, deep int) ([]string, error) {
		var results []string

		fulldir, err := filepath.Abs(dir)
		if err != nil {
			return results, err
		}

		files, err := os.ReadDir(fulldir)
		if err != nil {
			return results, err
		}

		var childFind bool
		if deep != 0 {
			childFind = true
		}

		childDeep := deep
		if deep > 0 {
			childDeep--
		}

		for _, f := range files {
			fullName := filepath.Join(fulldir, f.Name())

			if f.IsDir() {
				if childFind {
					files, _ := _findFiles(find, fullName, childDeep)

					for i := range files {
						results = append(results, files[i])
					}
				}
			} else {
				if len(find) > 0 {
					if ok, _ := filepath.Match(find, f.Name()); ok {
						results = append(results, fullName)
					}
				} else {
					results = append(results, fullName)
				}
			}
		}

		return results, nil
	}

	results, err = _findFiles(find, fulldir, deep)
	if err != nil {
		return results, err
	}

	return results, nil
}
```
# 解説
Go言語では、関数の中に無名関数を宣言できます。
無名関数は、「func(引数)」のように関数名を宣言しません。

```go
_findFiles = func(find string, dir string, deep int) ([]string, error) {...}
```
Go言語では、関数を呼び出すには、関数名が必要なため、関数を変数として先に宣言しておきます。

```go
var _findFiles func(find string, dir string, deep int) ([]string, error)
```

再回帰は、無名関数の必要は無いが、今回のように親関数と子関数が異なる場合、子関数を独立させると、間違って子関数を使ってしまう場合があります。
公開したくない関数は、無名関数として埋め込んでしまいます。

# この findFiles関数の使い方
引数1: find は、ワイルドカードが使えます。
　※注意: "*.xlsx"は Excelファイルです。
引数2: dir は、検索したいパスです。
　※注意: "../"は、現在の親フォルダーです。
引数3: deep は、-1:制限なし, 0:このフォルダーのみ, 1以上:深さ指定

```go:main.go
func main() {
	files, err := findFiles("*.xlsx", "../", -1)
	if err != nil {
		return
	}

	for i := range files {
		fmt.Println(i, files[i])
	}
}

```
Geminiで三味線を使う魔法少女を描いてみた。

# 概要

Geminiで三味線を使う魔法少女を描いてみました。

# 実装

プロンプトと実行結果は以下の通りでした。

```
光の三味線を使う魔法少女を描いてください。
```

![光の三味線を使う魔法少女_20250504.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3192024/2f2268d7-0110-464a-bad5-04436829515b.png)


```
闇の三味線を使う魔法少女を描いてください。
```

![闇の三味線を使う魔法少女_20250504.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3192024/25ede7d5-43d5-4337-8d21-830327dc258f.png)


```
聖なる三味線を使う魔法少女を描いてください。
```

![聖なる三味線を使う魔法少女_20250504.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3192024/6b243ebb-b852-41cb-9b78-029bdff5ea99.png)

# まとめ

光の子と聖なる子が可愛いかも。
何かの役に立てばと。
【HashiCorp】Terraform Cli / Vault CLIのインストール

# 1.Hashicorp Terrafom/Vault CLIのインストールガイド

各CLIインストールの個人メモです。
Hasicorp社のガイドはこちら。

https://developer.hashicorp.com/terraform/install
https://developer.hashicorp.com/vault/install



# 2.Terraform CLIのインストール

## Windows
- モジュールをダウンロード
- 任意のディレクトリに配置
- Powershellを起動して、PAHT設定(システム環境変数の場合は、"Machine")
```
$oldPath = [Environment]::GetEnvironmentVariable("Path", "User")
$newPath = "$oldPath;C:\<モジュール配置先フォルダ>"
[Environment]::SetEnvironmentVariable("Path", $newPath, "User")
[Environment]::GetEnvironmentVariable("Path", "User")
```
確認
```
C:\Users\>terraform version
Terraform v1.11.4
on windows_amd64
```
## Linux（Ubuntu）
- 下記コマンドを実行
```
wget -O - https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update && sudo apt install terraform
```
確認
```
$ terraform version
Terraform v1.11.4
on linux_amd64
```

## MaC
環境がないため、ガイドを参照ください。
https://developer.hashicorp.com/terraform/install#darwin

# 3.Vault CLIのインストール

## Windows
- モジュールをダウンロード
- 任意のディレクトリに配置
- Powershellを起動して、PAHT設定(システム環境変数の場合は、"Machine")
```
$oldPath = [Environment]::GetEnvironmentVariable("Path", "User")
$newPath = "$oldPath;C:\<モジュール配置先フォルダ>"
[Environment]::SetEnvironmentVariable("Path", $newPath, "User")
[Environment]::GetEnvironmentVariable("Path", "User")
```
確認
```
C:\Users\>vault version
Vault v1.19.3 (a2de3bb7bcf4a073cbb8724863a5a88d3c2f83da), built 2025-04-29T10:34:52Z
```

## Linux（Ubuntu）
- 下記コマンドを実行。Ubuntu以外のLinuxについてはガイドを参照ください。
```
wget -O - https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update && sudo apt install vault
```

確認
```
$ vault version
Vault v1.19.3 (a2de3bb7bcf4a073cbb8724863a5a88d3c2f83da), built 2025-04-29T10:34:52Z
```

## MaC
環境がないため、ガイドを参照ください。
https://developer.hashicorp.com/vault/install#darwin


以上です。
【Redmine】EasyGanttプラグインが表示されない問題と解決方法【移行】

# 📋 はじめに

### 記事を書く目的
- アウトプットの練習
- 自分のためのナレッジ
- 同じ事象で困ってる人の参考になればうれしいです

# 💡この記事の結論
先に結論を書きます。
詳細を知りたい方は読み続けていただければとおもいます🙇‍♀️

**＜問題だったこと＞**
- プラグインのアセットファイル（JS/CSS）が`public/plugin_assets/easy_gantt/`に生成されていなかった

**＜解決方法＞**
- アセットファイルを所定の場所に配置
- アプリケーションの再起動

# 🔍詳細
### 🤔なにがあったの？
- Redmineは正常に動作しているが、EasyGanttプラグインの画面だけが表示されない
- ブラウザのコンソールで多数の404エラーが発生
（ほとんどがプラグイン関連のJS/CSSファイルが見つからないというエラーメッセージ）

### 👀問題が発生したときの状況
直近で、以下の対応を行った後の出来事でした
- オンプレミス環境から AWS EC2 へのRedmine移行を実施した
- 移行時にバージョンアップも同時に行った（4.0.x系 → 6.0.x系）
- 移行作業の中でプラグインの対応は時間の関係で後回しにしていた（←あ。）

### 🧐 原因分析

調査の結果、次の原因が判明しました

**アセットファイルが不足してた**：
   - プラグイン自体は正しくインストールされている
   - しかし、プラグインのアセットファイル（JS/CSS）が`public/plugin_assets/easy_gantt/`に生成されていない状態

**なんで？**：
   - 移行作業時にプラグインアセットの手動コピーが必要だったが、それが行われなかった可能性が高い

### 🛠️ 解決手順

#### 1. Redmineルートディレクトリの確認

```bash
# Redmineのルートディレクトリを確認
ls -la /path/to/redmine
# 実際のRedmineディレクトリ: /path/to/redmine-6.0.x
```

#### 2. アセットのプリコンパイル

```bash
# Redmineディレクトリに移動
cd /path/to/redmine-6.0.x

# アセットのプリコンパイル（適切な権限で実行）
RAILS_ENV=production bundle exec rake assets:precompile
```

#### 3. プラグインアセットディレクトリの作成とファイルコピー

```bash
# プラグインアセットディレクトリを作成
mkdir -p public/plugin_assets/easy_gantt

# アセットファイルをコピー
cp -R plugins/easy_gantt/assets/* public/plugin_assets/easy_gantt/

# 適切な権限を設定
chmod -R 755 public/plugin_assets/easy_gantt/
```

#### 4. アプリケーションサーバーの再起動とキャッシュクリア

```bash
# unicornの場合の再起動（PIDは環境によって異なります）
kill -HUP `cat tmp/pids/unicorn.pid`
# または
kill -HUP <プロセスID>  # プロセスIDを指定
# キャッシュのクリア
RAILS_ENV=production bundle exec rake tmp:cache:clear
```

#### 5. 動作確認

- ブラウザのキャッシュをクリアしてRedmineにアクセス
- EasyGanttのページを開いて正常表示を確認

## 💡 まとめ
EasyGanttプラグインが表示されない問題は、主にアセットファイルの配置に関する問題です。
特に、Redmineの移行時には、プラグインのアセットもしっかり再配置しましょう🫠

いじょう！
同じような問題で悩んでいる方の参考になれば幸いです。
Linucレベル１勉強メモ　＃１１

＃ 第一章　Linuxのインストールと仮想マシン・コンテナの利用

練習問題1.11
フォアグラウンドで実行中のジョブを一時停止させるには、どのような操作をすれば良いかを選択してください。
A. Altキーを押しながらF2キーを押す

B. Ctrlキーを押しながらZキーを押す

C. Ctrlキーを押しながらPキーを押す

D. CtrlキーとAltキーとDeleteキーを同時に押す

（解答）　B. Ctrlキーを押しながらZキーを押す

（解説）
選択肢A. Altキーを押しながらF2キーを押すは仮想コンソールが切り替わります。

選択肢C. Ctrlキーを押しながらPキーを押すはジョブに対しては特になんの変化もありません。

選択肢D. CtrlキーとAltキーとDeleteキーを同時に押すは一般的には、マシンが再起動します。

（わからない単語解説）
* 仮想コンソール<br>コンピュータとユーザインターフェースにおいて、キーボードとディスプレイの概念的な組み合わせである。
新人エンジニアのためのインフラ入門（第6回）を読んで学んだこと

## はじめに

本日は「運用とは？」というテーマで深掘りを行います。

正直、運用はまだ経験したことがないので、イメージがぼんやりしていました。

「なんか面倒そうだけど、大事らしい」という印象だけ。

でも今回の記事を通じて、運用の全体像や意義が見えてきました。

---

## 運用とは？

### 1. 日常対応（常に状態を確認し、安定化させること）

システムは、止まってはいけない。

そのため、正常に動いているかを常に確認し、万が一に備えて準備をします。

- バックアップの取得
- リソースの監視（CPU・メモリ・ディスク使用量など）
- ログ収集・ジョブの定期実行
- サーバの起動・停止・再起動

この日々の対応が、いざという時に「すぐ復旧できる」情報源になる。

---

### 2. 障害対応（トラブルが起きたとき、すぐに元に戻す）

どんなに注意しても、障害は起きる。

そのときに 「いかに早く、元の状態に戻せるか」がポイント。

- 障害検知（監視ツールやアラート）
- ログ分析（事前に蓄積しておいた情報を元に原因特定）
- 一時対応（フェールオーバー・再起動など）
- 恒久対応（根本的な改善）

---

### 3. 保守対応（よりよい状態へと改善すること）

- パフォーマンスチューニング
- 脆弱性対応
- 新機能の追加・改善

「動いていればいい」ではなく、「よりよくする」ことも運用の一部。

---

## おわりに：人生もシステムのようだ

今回、「運用ってなんだろう？」と深掘りする中で、

ふとこんなことを思いました。

> システム開発って、人生に似ている。


| フェーズ | システム開発 | 人生 |
| --- | --- | --- |
| 要点定義 | 目的や要件を明確にする | 自分のビジョンを明確にする |
| 設計 | 全体の構造・計画を立てる | 目標に向けた具体的な行動計画を作る |
| 実装 | コーディングして動かす | 実際に行動し、経験から学び成長する |
| テスト | バグがないか確認する | ※人生には存在しない。行動して気づくしかない |
| 運用 | 障害に対応し、継続して改善する | 失敗してもすぐに立ち直り、また進む |

唯一の違いは、人生にはテストがないこと。

だからこそ、失敗を前提に、自分というシステムを運用し続けることが大切なんだと気づきました。
モバイルエンジニア「clean Architectureの図は見飽きたから要はどうやって実装しろっていうんだよ」

# はじめに
はい、ということで今回はアーキテクチャの図はみた！
でもうまくわからんから文字に起こしてくれ！
という方に向けた雑多な記事です。
画像の説明？うるせえ！知らねえ！ファイナルファンタジーというかたは`実装に落とし込む`というとこまでスクロールしてもらって大丈夫です。

例の画像と１番の外周
---
あの丸っこい図がありますよね。
これですこれ
![CleanArchitecture.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/638092/208ad7f6-9c4f-4a08-8ed3-ca38f33077a3.jpeg)
見飽きたよ、ややこしいよ！
わかります。自分も初めて見たときなんだぁ？てめぇ？ってなりました。
ただ、見方さえわかってしまえばなんてことないんですよね。
`cleanArchitecture`ってどのプラットフォームの開発でも使える汎用的な設計思想なんです。
つまり、一番外周は各プラットフォームだと思ってもらえばいいです。無視しておk

2個目の円
---
ViewModelなどがここにあたりますUIのママです。
UIという子供が食べやすいようにUseCaseから受け取ったものを加工してくれたり、逆にあまり意思の疎通ができない子供のUIくんが頼みたいものを代わりにUseCaseという店員さんに注文してくれます。

3個目の円
---
UseCaseです。
もうそのまま、共通化できて使う状況などによって切り分けられていく処理です。
基本的には１つのUseCaseには１つの関数のみが存在すべきです。
複数あったらUseCaseって名前の意味ほぼないですからね。

4個目の円
---
データです。
ただそれだけ、ドシンプル。

### 実装に落とし込む
これで最初の疑問に戻りますね、わかってんだよ！とどうしろってんだよ！っていう風に思いますよね。
図にしてみたり、どれに当たるのかを説明してみたりしましたが、要は依存も認知も一方通行にしてねということです。
マルチモジュールにしろ、シングルモジュールにしろまずは下記を抑えましょう。
- Dataモジュール(フォルダ)にあるstructやDataClassなどはデータソースとのやりとり
- Domainモジュール(フォルダ)にあるstructやDataClassなどはUIとのやりとり
- Data層からデータを取得したいときはDomainにInterfaceを実装してDataでClassを実装する

もし、マルチモジュールの場合は、Internalなどを付与して秘匿を忘れずに。
そして、データの流れとしては下記のようになるべきです。
`data -> datasource -> repositoyImpl -> repository -> domain -> useCaseImpl -> useCase`
こうすることで、implで実装された内側は誰が使おうがどこから使おうが、認知しているのは自分より前に存在しているものか、並列に存在しているものになります。
逆に`Interface`で実装されているただの`repository`や`useCase`はimplで本実装されているものにのみ依存していることになります。

### 最後に
自分も最初のころは何が何だかわからなかったので、右に倣えで既に実装されているものなどを参考に実装を進めていた時期もありましたが、新機能開発などをするようになるとＰＲでの指摘やあとから見返した時の違和感などで自己嫌悪に陥ります。
そうならないためにも少しでもお役に立てたらさいわいです
Linucレベル１勉強メモ　＃１０

＃ 第一章　Linuxのインストールと仮想マシン・コンテナの利用

練習問題1.10
バックグラウンドでupdatedbコマンドを実行させたいと思います。シェルにbashを使っている場合、updatedbコマンドに続けて、なんと入力すれば良いですか。１文字を記入してください。

＃　updatedb _____

（解答）　＆

（解説）
コマンドをバックグラウンドで実行するには、コマンドラインの末尾に「＆」をつけて実行します。updatedbコマンドは、ファイルを検索するlocateコマンドのためのデータベースを更新するコマンドです。

ジョブの管理
バックグラウンドジョブの実行
フォアグラウンドでジョブが実行されていると、キーボードからの入力は全てそのジョブに渡されます。つまり、そのジョブが終了するまで、シェル上で他の作業をすることができません。なのでそれを解消するためには、バックグラウンドを使用する必要があります。バックグラウンドを実行するには末尾に「＆」を追加すれば良いです。実行中のジョブはjobsコマンドで確認することができます。

（わからない単語解説）
* ジョブ<br>ユーザーがコマンドやプログラムをシェル上で実行する人まとまりの処理単位です。ジョブはフォアグラウンドとバックグラウンドに分かれる