OmniAICreator/Qiita-1.07M huggingface dataset:
Swift(Xcode)のUILabelを文字をURLの部分だけリンク化

1.はじめに
私は現在、独学でSwift(主にstoryboard)を勉強し、IDNectというアプリを開発しています。また、自社開発会社にてアルバイトとして働かせていただいています。

AIでも回答できると機能紹介しかできないと思いますが、
時間があるときにアプリの改修の際に使用したコードをQiitaに公開したいと思います。

以下のコードにすることでURLの部分だけ文字の色を変えて、タップすることでWebサイトに飛ぶことができます。


ここでは私が開発したIDNectというアプリのWebサイトを表示するようにしています。


2.実際のコード
```swift:Viewcontroller
import UIKit

class ViewController: UIViewController {
    @IBOutlet var label: UILabel!

    override func viewDidLoad() {
        super.viewDidLoad()
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(labelTapped))
        label.isUserInteractionEnabled = true
        label.addGestureRecognizer(tapGesture)
        toLink()
    }

    func toLink() {
        let labelText = "こちらのリンクを確認してください: https://IDNect.com"
        let types: NSTextCheckingResult.CheckingType = .link
        let detector = try? NSDataDetector(types: types.rawValue)
        let matches = detector?.matches(in: labelText, options: [], range: NSRange(location: 0, length: labelText.utf16.count))

        let attributedString = NSMutableAttributedString(string: labelText)

        matches?.forEach { match in
            attributedString.addAttribute(.foregroundColor, value: UIColor.blue, range: match.range)
        }

        label.attributedText = attributedString
    }

    @objc func labelTapped() {
        guard let labelText = label.attributedText?.string else { return }
        let types: NSTextCheckingResult.CheckingType = .link
        let detector = try? NSDataDetector(types: types.rawValue)
        let matches = detector?.matches(in: labelText, options: [], range: NSRange(location: 0, length: labelText.utf16.count))

        if let match = matches?.first, let url = match.url {
            UIApplication.shared.open(url)
        }
    }
}
```
画面ではこのように出力できます。現在はtintColor(デフォルトの色）で表示していますが、カスタムで様々な色に変更することができます。

![Screenshot 2025-02-14 at 12.28.21 AM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3791320/b4afb7d5-5d87-4ed0-9fff-bc75e3e8eec4.png)


3.おわりに
初めてのQiitaを書いたので、見にくかったり、分かりにくかったかもしれません。
今後アップデートして改善していくので、よろしくお願いします。

私がiOS/Androidを開発したhttps://IDNect.com もよろしくお願いします
Matplotlibで c, m, y と cyan, magenta, yellow は実は違う色なんです


## はじめに

Matplotlibでグラフを描くとき、`r`や`k`といった略語で色を指定することがよくあります。多くの略語はフルネームと同じ色を指しますが、`c`と`cyan`、`m`と`magenta`、そして`y`と`yellow`は実は異なる色です。この記事では、これらの色の違いを解説し、なぜこのような差が生じるのかを一緒に見ていきたいと思います。


## 略語とフルネームの色の違い

```python
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors

colors_short = ['b', 'g', 'r', 'c', 'm', 'y', 'k', 'w']
colors_full = ['blue', 'green', 'red', 'cyan', 'magenta', 'yellow', 'black', 'white']

fig, ax = plt.subplots(figsize=(8, 4))

# 左側：略語の色
for i, short in enumerate(colors_short):
    ax.barh(i, 1, color=short, label=f'{short}')

# 右側：フルネームの色
ax2 = ax.twinx()
for i, full in enumerate(colors_full):
    ax2.barh(i, 0.5, left=0.5, color=full, label=f'{full}')

# y軸設定
ax.set_yticks(range(len(colors_short)))
ax.set_yticklabels(colors_short)
ax2.set_yticks(range(len(colors_full)))
ax2.set_yticklabels(colors_full)

# y軸を反転
ax.invert_yaxis()
ax2.invert_yaxis()

# 不要な要素を削除
ax.set_xticks([])
ax2.set_xticks([])
ax.xaxis.set_visible(False)
ax2.xaxis.set_visible(False)
ax.set_xlim(0, 1)

plt.title('Color Comparison in Matplotlib: Short vs Full Name')
plt.savefig('short_vs_full.png')
plt.show()
```
左側が略語、右側がフルネームの結果です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1599258/367fce98-ba18-4cc7-ab6f-e31846cf4050.png)

cyan、magenta、yellowの色が少し違うことがわかります。

## なぜ色が違うのか？

Matplotlibでは、`c`, `m`, `y`といった略語は [Base colors](https://matplotlib.org/stable/gallery/color/named_colors.html#base-colors) として定義されています。  
一方、`cyan`, `magenta`, `yellow`はHTML/CSS標準の色に基づいているため、わずかな色の差が生じます。


## 略語とフルネームを間違えないために

- **常にフルネームで色を指定する**  
  ```python
  plt.plot(x, y, color='cyan')  # 安定して同じ色を使える
  ```
- **事前に`mcolors.to_rgb()`で確認する**  
  ```python
  import matplotlib.colors as mcolors
  print(mcolors.to_rgb('c'))
  ```

## 略語とフルネームのRGB値

| 略語 (フルネーム) | 略語のRGB値 | フルネームのRGB値 | 同じ色か？ |
|---|---|---|---|
| b (blue) | (0.0, 0.0, 1.0) | (0.0, 0.0, 1.0) | True |
| g (green) | (0.0, 0.5, 0.0) | (0.0, 0.5019607843137255, 0.0) | False |
| r (red) | (1.0, 0.0, 0.0) | (1.0, 0.0, 0.0) | True |
| c (cyan) | (0.0, 0.75, 0.75) | (0.0, 1.0, 1.0) | False |
| m (magenta) | (0.75, 0.0, 0.75) | (1.0, 0.0, 1.0) | False |
| y (yellow) | (0.75, 0.75, 0.0) | (1.0, 1.0, 0.0) | False |
| k (black) | (0.0, 0.0, 0.0) | (0.0, 0.0, 0.0) | True |
| w (white) | (1.0, 1.0, 1.0) | (1.0, 1.0, 1.0) | True |

## 補足：他の色はどうなのか？

補足として、`b(blue)`や`r(red)`などはフルネームと略語で同じ色ですが、`g(green)`には微妙な違いがあります。

```python
print(mcolors.to_rgb('green'))  # (0.0, 0.5019607843137255, 0.0)
print(mcolors.to_rgb('g'))      # (0.0, 0.5, 0.0)
```
- `green`はHTML/CSSで標準的に使われる`#008000`を基準にしています。この`#008000`は(R=0, G=128, B=0)で表され、RGB値は128/255 ≈ 0.50196になります。
- 一方で`g`はMatplotlib独自の簡略指定で、`0.5`とシンプルに定義されています。

つまり、HTML基準の`green`と、Matplotlibの`g`ではスケールの扱い方が異なるため、わずかに数値が異なります。

## まとめ

Matplotlibで色を指定する際、略語とフルネームで異なる色が使われることがあります。特に`c`と`cyan`、`m`と`magenta`、`y`と`yellow`は見た目が異なります。思わぬ色の違いを避けるためには、常にフルネームで色を指定するか、`mcolors.to_rgb()`で事前に確認することをおすすめします。
また(SSD1306)「こんにちは...」を表示して遊ぶ。(M5Unified 0.2.3)(StampS3)


x 過去ログを見よ!!!
x M5Unified 0.2.3
x UNIT_GLASS 0.0.1
x M5GFX 0.2.5
x 3.1.2

目的
SSD1306(OLED)に漢字を表示して遊ぶ


いろいろ
なぜかM5NanoC6では、何も表示されない(20250214)、
ESP32 3.0.7にしたら動いた(20250215)



結果
----


イメージ

![o_coq570.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/024aef95-0b86-44e0-8545-016c778d1226.jpeg)


![o_coq810.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/dd1ae6d6-c3bc-48cc-8029-709016d6498c.jpeg)


![o_coq811.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/92af2598-19bc-4d46-b242-74c3f490b109.jpeg)


![o_coq812.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/348ef3f9-a11d-408b-b472-c522e0c33fa2.jpeg)


![o_coq813.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/78be0afb-e6b3-418a-a801-86f20067f4cb.jpeg)



プログラム
----


```c++



#include <M5UnitGLASS2.h>
#include <M5Unified.h>

void setup() {
  auto cfg=M5.config();
  cfg.unit_glass2.pin_sda=13;
  cfg.unit_glass2.pin_scl=15;
  M5.begin(cfg);

  M5.Lcd.setFont(&fonts::lgfxJapanGothicP_16);
  M5.Lcd.println("こんにちは世界!!");
  M5.Lcd.setFont(&fonts::FreeMonoBold9pt7b);
  M5.Lcd.println("SSD1306\nGME12864-51");
}

void loop() {
}



```
SharePointリストとCopilot Studioを使った問い合わせ管理

## 緒言
<strong>"SharePointリストはCopilot Studioのナレッジにならない"</strong>と聞いて悲しんでおりましたが、これまで既定の環境で山のようにため込んできたリストのデータをなんとか活用したく検証したら良い感じの結果が得られました。
最近はじめたコミュニティ(部活)の一発目のネタとして取り上げました。

https://powerclub.connpass.com/

## 概要
### 全体像
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3670355/cc405150-c816-439f-be87-5167530137d7.png)
Listsのフォームを使っているが<strong>アドレスからデータソースがばれたくなかったら</strong>Formsを使った方が良いかも。

### Listsの例
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3670355/0f442ebb-fbe4-4e14-bbcf-b6e302b03c94.png)

### Listsのフォームの例
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3670355/33892c21-551a-4fd4-b976-57adeba9f201.png)

## ポイント
### ListsをPDFに変換するフロー
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3670355/bbd831d7-f4ac-4d58-9d03-75e505be1149.png)
ごくシンプルにいったんOneDriveにHTMLファイルを作って、PDFに変換してドキュメントライブラリに保存する。非プレミアムでPDFに変換するアクションがOneDriveしかないので。。。

### Copilot StudioのApp登録
前述のPDFが保存されたドキュメントライブラリをナレッジに指定して終わったと思ったらそうじゃありませんでした。。。[Azure](https://portal.azure.com/)でやらないといけないことがありました。下図のようにGraph APIのアクセス許可を付与する必要あり。[^1]
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3670355/5599075a-8592-42f2-8286-2e8b2836f4de.png)
※ Copilot Studio側のSecurityのAuthenticationのScopeの設定も忘れずに・・・

[^1]: https://qiita.com/skuramoto/items/5c9804fb4468e5772b41

## 結言
超お手軽に生成AIを使った問い合わせ管理システムが組めて他にもいろいろ応用できそうで満足しております。SharePointリストも救えた！

動作↓

https://x.com/meccha__eeyan/status/1888786358732509468
stancl/tenancy勘違い

## Central routes
```
// routes/web.php, api.php or any other central route files you have

foreach (config('tenancy.central_domains') as $domain) {
    Route::domain($domain)->group(function () {
        // your actual routes
    });
}
```
こんなのを記載してくださいと書いてある。
どうやらこれは`config/tenancy.php`に設定したドメインとリクエストの宛先ドメインを比較しているらしい。

リクエストを送った方のドメインと比較しているのかと思っていた。

そもそも、
Stancl/Tenancyのルーティングはリクエストの Host ヘッダーを見てcentral_domainsに定義されたドメインかどうかを判断してルートを選択するそうです。

じゃあ、reactとlaravelでapiの場合どうするんだと。宛先ドメインずっと一定だぞ
プリザンターで子テーブルの合計値を親テーブルにサマリ表示する方法

## **概要**
プリザンター（Pleasanter）で子テーブル（詳細テーブル）の数値カラムを集計し、その合計値を親テーブル（マスターテーブル）に自動で表示する方法を解説します。

## **手順**
1. **子テーブル（詳細テーブル）の設定**
2. **ビューの作成（フィルタの設定）**
3. **親テーブルへのサマリ反映**


## **1. 子テーブル（詳細テーブル）のサマリ設定**
子テーブルの「テーブルの管理」→「サマリ」に進み、以下の設定を行います。

- **サイト:** 親テーブル（例：足場総在庫管理）
- **項目:** 親テーブルで合計値を表示させるカラム（例：現在貸出数）
- **リンク項目:** 親テーブルと関連付けるキー（例：足場型番）
- **サマリ種別:** 合計
- **サマリ項目:** 集計する数値カラム（例：貸出数）
- **条件:** 標準ビュー（またはカスタムビューを作成）

### **サマリ設定画面の例**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3116202/72e03b43-4713-4ea5-97b3-9d73db7d3e96.png)
![](添付画像1)

## **2. ビューの作成（フィルタの設定）**
特定の条件下でのみ集計を行いたい場合は、**ビューを作成** してフィルタを適用します。

- **フィルタ条件の例**
  - 貸出中のレコードのみを集計（「貸出中？」が **True** になっているレコードを対象にする）

### **ビュー設定画面の例**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3116202/2b600ba4-be1b-406b-83b1-69a228990f1f.png)


- **ビューを作成し、フィルタを設定**
  - 「貸出中？」がTrue（チェック済み）のデータのみを集計対象にする

---

## **3. 親テーブルへのサマリ反映**
サマリ設定時に、親テーブルの「現在貸出数」などのカラムを指定すれば、特に追加の設定をしなくても**自動で合計値が反映**されます。

**ポイント**
- **子テーブル側のみ設定すればOK！**
  - 親テーブルには特別な設定は不要（ただし、サマリ結果を表示させるカラムの指定は子テーブルのサマリ設定で行う）

## **まとめ**
プリザンターでは、**子テーブルの合計値を自動で親テーブルに反映**することができます。特定の条件で集計したい場合は、**ビューを活用**するとより柔軟な設定が可能になります。
【Rails】Albaでnilのプロパティを除外する方法

## 実装方法

以下のように記述すると`nil`のプロパティを除外できます。

```ruby
class RestrictedFooResource < GenericFooResource
  def select(_key, value)
    !value.nil?
  end
end
```

## 参考

https://github.com/okuramasafumi/alba/blob/main/README.md#filtering-attributes-with-select
Salesforceのタブ切替え時で未保存時の警告を出す

## はじめに

Salesforceのレコード詳細画面のインライン編集などで、保存せずにタブを切り替えようとすると、下記のようなダイアログが表示されます。標準のコンポーネントだけでなく、自作のコンポーネントでも同様のダイアログを表示させることが可能です。

この記事ではLightningコンポーネントで未保存警告ダイアログを出す方法について紹介します。Auraの[lightning:unsavedChanges](https://developer.salesforce.com/docs/component-library/bundle/lightning:unsavedChanges/documentation)を活用します。

*LWCでは使用できません（[idea](https://ideas.salesforce.com/s/idea/a0B8W00000QdfAqUAJ/provide-lwc-equivalent-to-lightningunsavedchanges)）。Auraでラップする必要があります。

![標準コンポーネントの未保存警告ダイアログ](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125621/a7519e25-a1cc-4c79-8723-6cd34109fa12.png)

## 実装手順

SalesforceのレコードページにカスタムAuraコンポーネントを埋め込み、ユーザーが送信ボタンを押さずに別のSalesforceタブへ移動しようとした際に警告を表示する方法を紹介します。これを実現するために lightning:unsavedChanges を活用します。

### HTML

unsavedChangesは下記のように使用します。JavaScriptでメソッドを使用するので、`aura:id`を設定しておきます。onsave属性にダイアログの「保存」をクリックした時に実行する処理を設定します。ondiscard属性には「変更を破棄」をクリックした時に実行する処理を設定します。

```html
<lightning:unsavedChanges aura:id="unsaved" onsave="{!c.handleSave}" ondiscard="{!c.handleDiscard}" />
```

```html:例
<aura:component implements="flexipage:availableForRecordHome,force:hasRecordId" access="global">
  <aura:attribute name="inputValue" type="String" default="" />

  <!-- 未保存の変更を検知するコンポーネント -->
  <lightning:unsavedChanges aura:id="unsaved" onsave="{!c.handleSave}" ondiscard="{!c.handleDiscard}" />

  <!-- 入力フィールド -->
  <lightning:input label="項目1" value="{!v.inputValue}" onchange="{!c.handleChange}" />

  <!-- ボタン -->
  <lightning:button label="送信" onclick="{!c.handleSave}" variant="brand" />
</aura:component>
```

### JavaScript

setUnsavedChangesメソッドで離脱時に警告を出す状態と出さない状態を切り替えることができます。また、labelを設定することでダイアログに表示するタイトルの一部を変更することができます。

```js
component.find("unsaved").setUnsavedChanges(true);
component.find("unsaved").setUnsavedChanges(true, { label: '検証用' });
component.find("unsaved").setUnsavedChanges(false);
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125621/b9f4d0ec-a991-45da-8f85-814a23205f83.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125621/27812558-63cf-48b9-ad4e-0acf84247277.png)

```js:例
({
    handleChange: function(component, event, helper) {
        component.find("unsaved").setUnsavedChanges(true);
    },

    handleSave: function(component, event, helper) {
        component.find("unsaved").setUnsavedChanges(false);
        // 処理
    },

    handleDiscard: function(component, event, helper) {
        component.find("unsaved").setUnsavedChanges(false);
        // 処理
    }
})
```

## 参考
- [lightning:unsavedChanges](https://developer.salesforce.com/docs/component-library/bundle/lightning:unsavedChanges/documentation)
WindowsとLinuxにおける名前解決の仕組み


## はじめに

ネットワークの仕組みを学んでいると、ドメイン名をIPアドレスに変換するプロセスに `socket` ライブラリや OS の設定ファイルが関与することがわかります。しかし、Windows と Linux では設定ファイルの場所や動作が異なります。

この記事では、

- WindowsとLinuxでの名前解決の違い
- `/etc/hosts` や `C:\Windows\System32\drivers\etc\hosts` の役割
- `nsswitch.conf` や `resolv.conf` のWindowsにおける対応
- Windows で DNS を設定する方法

を詳しく解説します！

---

## WindowsとLinuxの名前解決の違い

| 機能 | Linux/macOS | Windows |
|------|------------|---------|
| **ローカルの名前解決ファイル** | `/etc/hosts` | `C:\Windows\System32\drivers\etc\hosts` |
| **DNSの設定ファイル** | `/etc/resolv.conf` | GUIの「ネットワークとインターネットの設定」から変更 |
| **優先順位を決める設定** | `/etc/nsswitch.conf` | Windows には直接対応する設定なし |
| **ターミナルでの確認コマンド** | `ping example.com`、`nslookup example.com` | `ping example.com`、`nslookup example.com` |
| **設定ファイルの編集方法** | `nano` や `vim` で編集 | `notepad` で管理者権限で編集 |

Windows では `/etc/nsswitch.conf` に相当する仕組みがなく、基本的に `hosts` ファイルよりも DNS サーバーが優先される場合があります。

---

## Windows の `hosts` ファイルを編集する方法

Windows では `/etc/hosts` の代わりに `C:\Windows\System32\drivers\etc\hosts` を編集することで、特定のドメインの名前解決を制御できます。

### **① `hosts` ファイルを開く**
#### **方法1: コマンドプロンプトで開く（おすすめ）**  
1. **Windowsキーを押して「cmd」と検索**
2. **「管理者として実行」** を選ぶ
3. 以下のコマンドを実行：
   ```sh
   notepad C:\Windows\System32\drivers\etc\hosts
   ```
   これで `hosts` ファイルがメモ帳で開く。

#### **方法2: エクスプローラーで開く**
1. `C:\Windows\System32\drivers\etc\hosts` に移動
2. `hosts` ファイルを右クリック → **「管理者として実行」** でメモ帳で開く
3. 必要な変更をして保存

### **② 例えばこんな設定を追加**
```sh
93.184.216.34 example.com
```
これを追加すると、Windows では `example.com` へのアクセス時に DNS を使わず、直接 `93.184.216.34` に接続する。

### **③ 動作確認**
コマンドプロンプト（`cmd`）で `ping` してみる：
```sh
ping example.com
```
→ `/etc/hosts` に設定した IP アドレスに直接アクセスしているか確認！

---

## Windows で `/etc/resolv.conf` に相当するものは？

Linux では DNS サーバーの設定を `/etc/resolv.conf` で管理しますが、Windows では GUI から設定するのが一般的です。

### **Windows でDNSサーバーを設定する方法**
1. **「ネットワークとインターネットの設定」を開く**
2. **「アダプターのオプションを変更する」をクリック**
3. **使用中のネットワーク（Wi-Fi または イーサネット）を右クリック → 「プロパティ」**
4. **「インターネット プロトコル バージョン4（TCP/IPv4）」を選択して「プロパティ」**
5. **「次のDNSサーバーのアドレスを使う」を選択して、DNSを手動設定**
   - GoogleのDNS: `8.8.8.8`、`8.8.4.4`
   - CloudflareのDNS: `1.1.1.1`、`1.0.0.1`
   - OpenDNS: `208.67.222.222`、`208.67.220.220`

設定後に **`ipconfig /flushdns`** を実行すれば、変更が即時反映されます。

---

## **まとめ**

- **Windows では `/etc/hosts` ではなく `C:\Windows\System32\drivers\etc\hosts` を編集する**  
- **Linux の `/etc/resolv.conf` の代わりに、Windows ではGUIでDNSを設定する**  
- **名前解決の仕組みは似ているが、設定場所や動作が異なる**
socketライブラリの名前解決とOS内部の仕組み


## はじめに

ネットワークの仕組みを学んでいると、「ブラウザがドメイン名をIPアドレスに変換する過程で `socket` ライブラリが使われる」と説明されることがあります。私はこの説明を読んで、「`socket` ライブラリの関数を実行すると、OSのターミナルで `nslookup` が動くの？」という疑問が浮かびました。

この記事では、この疑問を解決しながら、

- `socket` ライブラリがどのように名前解決を行うのか
- OSの `/etc/hosts`、`nsswitch.conf`、`resolv.conf` の役割
- `nslookup` や `dig` との違い

をまとめてみます。

---

## `socket` ライブラリによる名前解決の流れ

Python で `socket.gethostbyname("www.example.com")` を実行すると、以下の手順で名前解決が行われます。

### ① `/etc/hosts` を確認
まず、ローカルの `/etc/hosts` に `www.example.com` のエントリがあるかをチェックします。

#### `/etc/hosts` の例
```sh
127.0.0.1   localhost
192.168.1.100   myserver
```

この場合、`myserver` というホスト名を問い合わせると、DNS を使わずに `192.168.1.100` を返します。

### ② `nsswitch.conf` を確認し、次の処理を決定
`/etc/nsswitch.conf` には、名前解決を行う際の優先順位が設定されています。

#### `/etc/nsswitch.conf` の例
```sh
hosts: files dns
```

この設定では、`/etc/hosts` を見て、なければ DNS に問い合わせるという順番になっています。

### ③ `/etc/resolv.conf` を参照してDNSサーバーに問い合わせ
`/etc/hosts` にエントリがない場合、OS は `/etc/resolv.conf` に設定されたDNSサーバーに問い合わせます。

#### `/etc/resolv.conf` の例
```sh
nameserver 8.8.8.8
nameserver 1.1.1.1
```

この設定では、
1. GoogleのDNSサーバー（`8.8.8.8`）に問い合わせる
2. 応答がなければ CloudflareのDNSサーバー（`1.1.1.1`）に問い合わせる

という順番になります。

---

## `socket` ライブラリと `nslookup` の違い

ここで、「`socket` ライブラリの関数を実行すると `nslookup` が動くの？」という疑問に戻ります。

### ✅ `socket` ライブラリの動作
- `/etc/hosts` → `/etc/nsswitch.conf` → `/etc/resolv.conf` の順に従う
- DNS 以外の手段（ローカルファイル）も使う

### ❌ `nslookup` や `dig` の動作
- **OSの名前解決システムを使わず**、直接 DNS サーバーに問い合わせる
- `/etc/hosts` を無視する

つまり、`socket` ライブラリは **OSの名前解決ルールを守る** のに対し、`nslookup` は **常にDNSに問い合わせる** という違いがあります。

---

## 【実験】OSの名前解決の動作を確認する

### ① `/etc/hosts` を使った実験
1. `/etc/hosts` を編集
   ```sh
   sudo nano /etc/hosts
   ```
2. 次の行を追加
   ```sh
   1.2.3.4 example.com
   ```
3. 保存して `ping` で確認
   ```sh
   ping example.com
   ```
   → `1.2.3.4` にアクセスする！（DNSを使わない）

4. Python で確認
   ```python
   import socket
   print(socket.gethostbyname("example.com"))
   ```
   → `1.2.3.4` になる！

5. `nslookup` で確認
   ```sh
   nslookup example.com
   ```
   → **本物のDNSサーバーのIPが返る（1.2.3.4 にはならない）**

この結果から、`socket` は `/etc/hosts` を使うが、`nslookup` は使わないことが確認できます。

---

## まとめ

|  | `socket` ライブラリ | `nslookup`/`dig` |
|---|---|---|
| `/etc/hosts` を使うか？ | ✅ 使う | ❌ 使わない |
| `/etc/resolv.conf` を使うか？ | ✅ 使う | ✅ 使う |
| `/etc/nsswitch.conf` の順番を守るか？ | ✅ 守る | ❌ 守らない |
| DNSに直接問い合わせるか？ | ❌ まずローカルを確認 | ✅ 直接DNSへ |

`socket` ライブラリは **OSの名前解決のルールに従い、ローカルの `/etc/hosts` も参照する** のに対し、`nslookup` は **常にDNSサーバーに直接問い合わせる** という違いがあります。
日本語の数で表せる言葉を辞書から抽出するスクリプト nw.py

日本語の数で表せる言葉を辞書から拾って表示するスクリプトです。
pythonで書いています。
案外簡単に書けました。
Cと違ってPythonは文字列処理をやりやすいですね。
例えば、4649: よろしく, 4101040: よいおとしを
などです。
カレントディレクトリの、jd.txtが日本語の辞書です。一行に一つのひらがなの単語の\\nで終わる言葉の集合です。
特殊な例として、例えば、`40010:しまんと`、`50010:ごまんと`などがありますが、簡単に作ったので、このような形は内部辞書に追加するようにしています。このような形はそんなにないと思いますので。
数を参照する言葉は、長い文字列を先に置きます。

```nw.py
#!/usr/bin/python3

nw=[ [ "0","えん","まる","ま","れい","れ","おお","を","ぜろ","おう","お"],
     [ "1","いっ","いち","い","ひと","ひ","かず","わん"],
     [ "2","に","ふた","じ","ふ","つー","つう","つ"],
     [ "3","みつ","さん","ざん","さ","ざ","み","すりー","すり"],
     [ "4","よつ","よん","よ","し","ふぉ","ふぉー","ふぉう" ],
     [ "5","ごっ","ご","いつ","こ","ふぁいぶ"],
     [ "6","むつ","む","ろく","ろ","る","しっくす"],
     [ "7","なっ","なな","な","しち","せぶん" ],
     [ "8","や","はち","は","ぱち","ぱ","えいと"],
     [ "9","ここの","く","きゅう","きゅ","ないん" ],

     [ "10","とう","とお","と","じゅう"],
     [ "100","ひゃく","もも"],
     [ "1000","いっせん","せん","ち"],
     [ "10000","まん","ばん"],
     [ "40010","しまんと"],
     [ "50000","ごまん"],
     [ "50010","ごまんと"],
     [ "100000000","おく"],
     [ ":","たい"]
     ]

def cw(w,idx):
    for i in nw:
        s=i[0]
        j=i[1:]
        for k in j:
            if w[idx:idx+len(k)]==k:
                idx+=len(k)
                return idx,s
    else:
        return idx,""

def check(w):
    n=""
    idx=0
    while idx<len(w):
        idx,t=cw(w,idx)
        if not t:
            return ""
        n=n+t
    return n

def main():
    f=open("jd.txt","r");
    a=f.read()
    f.close()
    a=a.split()
    for w in a:
        num=check(w)
        if num:
            print(num,":",w)

if __name__=="__main__":
    main()
    exit(0)


```

#### 実行

```
1 : い
11 : いい
1109 : いいおく
11093 : いいおくさん
115 : いいこ
111000019 : いいせんをいく
11104 : いいとし
11291 : いいにくい
111 : いいひと
118 : いいや
・
・
・等々
```
【Python】SQLAlchemyでDB接続してデータを取得する方法

# 概要
PythonでSQLAlchemyを使用してデータベースに接続し、データを取得する方法を備忘も兼ねて紹介します。

# 流れとコード
①DB接続の準備
②CRUD操作の実装
③データの取得

### ①DB接続の準備

まずはデータベースエンジンとセッションを作成します。

```database/session.py
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker


engine = create_engine("mysql+pymysql://user:pw@host:port/dbname", pool_pre_ping=True)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
```

`create_engine`関数は、データベースエンジンを作成するための関数、
`sessionmaker`関数は、データベースセッションを作成するための関数です。

`create_engine`の引数にある`pool_pre_ping=True`は、接続が有効かどうかを確認するための「`ping`」操作を有効にするもの。これにより、接続が切れている場合に自動的に再接続を試みます。

> If the ping / error check determines that the connection is not usable, the connection will be immediately recycled, and all other pooled connections older than the current time are invalidated, so that the next time they are checked out, they will also be recycled before use.
https://docs.sqlalchemy.org/en/20/core/pooling.html

コネクションプールについては、いくつかアプローチがあると思いますので、適宜要件に合わせて検討ください（`pool_recycle`というのもある）。

次に、データベースセッションのコンテキストマネージャを使用して「セッションを呼び出し元に返す〜呼び出し元のコードが実行される〜呼び出し元のコードが終了した後にセッションを閉じる」を以下で実施します。

```database/dependency.py
from typing import Generator
from contextlib import contextmanager
from .session import SessionLocal


@contextmanager
def get_db() -> Generator:
    session = SessionLocal()
    try:
        yield session
    finally:
        session.close()
```

ここで`yield`を使っている理由は以下に記載ある通り、リソースリークを防止するためです。`yield`を使用することで、セッションを一時的に呼び出し元に返し、呼び出し元のコードが終了した後にセッションを確実に閉じることができるためです。

https://zenn.dev/arsaga/articles/f431ee007efae9

### ②モデルとCRUD操作の実装
次に、`country`モデルから`country_code`の値を利用して`country_name`を取得する関数を実装します。CRUD操作のベースクラスを利用しています。

```cruds/country.py
from pydantic import BaseModel
from sqlalchemy.orm import Session
from cruds.base import CRUDBase
from models.country import Country


class CountryTable(CRUDBase[Country, BaseModel, BaseModel]):
    def get_country_name(self, db: Session, country_code):
        country = db.query(self.model).filter(Country.country_code == country_code).first()
        if country is None:
            raise ValueError("Country not found")
        return country.country_name

country = CountryTable(Country)
```

### ③データの取得

最後に、上記で実装した関数を呼び出してデータを取得するコードです。

```py
from sqlalchemy.orm import Session
import cruds
from dependency import get_db

country_code = "100"

with get_db() as db:
    country_name = cruds.country.get_country_name(db, country_code)
    print(country_name)
```

`with get_db() as db`で`get_db`関数を使用して上述のデータベースセッションを取得しています。

このセッションを使用して、`get_country_name`メソッドを呼び出し、指定された`country_code`に対応する`country_name`を取得することができました。
AWS RDS Read Replicaを利用したFailoverライターインスタンスのサイズ変更

## 背景
会社でリザーブドインスタンス契約をし直しました。
（r5ファミリ→r6gファミリー）
そのため、既存のr5ファミリーを使うインスタンスたちをr6gにする必要がありました。

## なぜFailoverを？

1. 広告配信サービスは24時間稼働していてDBのダウンタイムをできるだけ短くしたかった。
2. 深夜時間帯は新たな書き込みがなかった。レプリケーションの心配はなかったので安全にデータの同期を確保できました。

## 事前注意事項⚠️

- **Read ReplicaのFailoverの場合、ライターインスタンスのエンドポイントが変わる**
    - アプリケーションがクラスターのライターインスタンスのエンドポイントに向いていることをチャックしましょう。
- **Read Replicaが最終的にライターインスタンスになります。**
    - 完全にデータ同期が行われてないかもしれません。ReplicationRagなどを確認して、データの同期を確認しましょう。
- **Failover中はダウンタイムが発生することを念頭に置きましょう**
    - およそ１〜２分


## 作業手順

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3256444/d33c9ca8-48b4-4e35-8bbe-46364d39ce95.png)


1. **Read Replicaを作成**  
   変更したいインスタンスタイプのRead Replicaを作成します。

2. **データ同期の確認**  
   Read Replicaが最新のデータを保持しているか確認します。これを確認しないと、Failover後にデータ不整合が発生する可能性があります⚠️

3. **Failoverを実行**  
   Read ReplicaをWriterインスタンスに昇格させ、Failoverを行います。

4. **Connection Poolを貼り直し**  
Connection Poolを使用していたのでConnectionの貼り直し作業をしました。

5. **ライターインスタンスのモニタリング**
    書き込みのテストを行い、書き込みが正常に動作することを確認しました。

6. **旧ライター(新Read)インスタンスの削除**。
    元々Readerインスタンスは使用しないDBだったので、料金を抑えるため削除しました。

**作業完了です！**
非同期的に配列をforEachする方法

## やってはだめ、想像する非同期的forEach
:::note alert
これは絶対にしないでください。asyncを付けた関数内で非同期処理がなかった場合同期的に実行されますが、もし非同期処理があった場合はmicroqueue stackにプッシュされ、次のイベントループまでそれらが実行されることは無くなってしまいます
:::

```js:JavaScript版
async function getImportsProps(list){
    let props = [];
    list.forEach(async e=>
        props.push(...Object.keys(await import(e))));
    return props;
}
```
```ts:TypeScript版
async function getImportsProps(list: string[]): Promise<string[]>{
    let props: string[] = [];
    list.forEach(async e=>
        props.push(...Object.keys(await import(e))));
    return props;
}
```
## これぞ正義、正しい非同期的forEach
```js:JavaScript版
async function getImportsProps(list){
    let props = [];
    await Promise.all(list.map(async e=>
        props.push(...Object.keys(await import(e)))));
    return props;
}
```
```ts:TypeScript版
async function getImportsProps(list: string[]): Promise<string[]>{
    let props: string[] = [];
    await Promise.all(list.map(async e=>
        props.push(...Object.keys(await import(e)))));
    return props;
}
```
## 解説
まずasync関数の返り値は、宣言したものではなくそのPromise型なのです。
つまりはasyncのついた関数はPromiseを返す。
それを`list.map`したわけですが、つまりこれは`Promise[]`を返すのです。
次に`Promise.all`。これは`Promise[]`を渡すとそれが全部完了するまでの非同期処理のPromiseを返します。
それをawaitした場合、全部を待つということです。
> 自分はうまく解説できません。すみません。

つまり`Promise(async関数) > Promise[](forEachのPromiseリスト) > Promise(全部完了したら完了する) > await(すべてを待つ)`ということです。
An import path can only end with a '.tsx' extension when 'allowImportingTsExtensions' is enabled.　というエラーの解決方法について

# はじめに
CI/CDを設定後、GitHubのActionsで
```
An import path can only end with a '.tsx' extension when 'allowImportingTsExtensions' is enabled.
```
というエラーが表示されました。
翻訳すると「allowImportingTsExtensions」が有効になっている場合、インポート パスは「.tsx」拡張子でのみ終わることができます。とのことで、.tsxという記述が原因のようです。

# 解決方法
allowImportingTsExtensionsについて調べると、tsconfig.jsonに以下の記述をすることで.tsxが使えるようになるようです。
```tsconfig.json
"compilerOptions": {
    "allowImportingTsExtensions": true
  }
```
その他の解決方法として、.tsxを削除することでも解決できました。
```main.tsx
import App from './App' // ← .tsxを削除
```

# 参考
https://t-yng.jp/post/typescript-tsconfig-options-over-4.5
<br>
[<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3707255/8bfacf61-7d8d-6d27-cb07-98d1f8f27fcd.png"  width=100%>](https://projisou.jp)
JWT認証 vs セッションID認証の違い


## はじめに
個人開発でAWSのCognitoを使おうと調べていたところ、「JWT」という単語が頻繁に出てきました。
JWTがどのような仕組みなのか、なぜCognitoや他の認証システムで使われるのかを理解するために、
セッションID認証とJWT認証の違いについて整理しました。

## 1. セッションID認証（サーバー側でセッション管理）
### 仕組み
1. **ユーザーがログイン**
   - クライアント（ブラウザやアプリ）がユーザー名とパスワードを送信
   - サーバーが認証し、**セッションID** を発行
   - セッションIDをクライアントに返し、クッキーに保存

2. **APIアクセス時の動作**
   - クライアントはリクエスト時に、セッションIDを送信
   - サーバーはセッションIDを管理し、照合して認証

3. **サーバーがセッションを管理**
   - サーバー側の **データベースやメモリ** にセッション情報を保存
   - ユーザーがログアウトすると、サーバー側のセッションが削除

### メリット
- **実装が簡単**
- **セッションの無効化が可能**（ログアウト時にサーバー側で削除）
- **セッション情報を変更しやすい**（管理画面で強制ログアウト可能）

### デメリット
- **サーバーの負荷が増える**（セッションデータの管理が必要）
- **スケールしにくい**（ロードバランサー環境ではSticky Sessionが必要）
- **分散システムには不向き**（マルチサーバーではセッション共有が必要）

## 2. JWT認証（トークンベースの認証）
### 仕組み
1. **ユーザーがログイン**
   - クライアントがユーザー名とパスワードを送信
   - サーバーが認証し、JWT（トークン）を発行
   - クライアントがJWTをローカルストレージやクッキーに保存

2. **APIアクセス時の動作**
   - クライアントはリクエストヘッダにJWTを含めて送信
   - サーバーはJWTを検証し、正しければ認証成功

3. **サーバーはセッションを管理しない**
   - JWTには **ユーザー情報（ユーザーID, 権限）と有効期限が含まれる**
   - **サーバー側ではトークンの検証のみ行い、データベース不要**

### JWTの検証方法
- JWTには **署名** が含まれている
- サーバーは **秘密鍵** または **公開鍵** で署名を検証
- JWTの内容が改ざんされていないか、有効期限が切れていないかチェック

### メリット
- **ステートレス（サーバー側でデータを持たない）** → 負荷が軽い
- **スケールしやすい** → ロードバランサー環境に最適
- **APIに適している** → モバイルアプリやSPA（シングルページアプリケーション）で広く利用

### デメリット
- **ログアウトやトークン無効化が難しい**（発行済みトークンは有効期限が切れるまで有効）
- **トークンの管理が重要**（盗まれると第三者が不正アクセス可能）
- **長いトークンはリクエストサイズを増やす**（Authorizationヘッダに入れる場合）

## 3. セッションID認証 vs JWT認証

| 項目 | セッションID認証 | JWT認証 |
|------|----------------|---------|
| **管理方式** | サーバー側で管理（DBやメモリ） | クライアント側で管理（トークン） |
| **スケーラビリティ** | スケールしにくい（Sticky Sessionが必要） | スケールしやすい（ステートレス） |
| **認証時の処理** | サーバーがセッションを照合 | サーバーはJWTを検証するだけ |
| **ログアウト処理** | サーバー側でセッション削除 | 発行済みトークンは無効化しづらい |
| **セキュリティリスク** | セッションハイジャックのリスク | JWTが漏れると不正アクセスされる |


## 4. AWSでの使い分け
### セッションID認証が向いているケース
- **EC2ベースのアプリ（Webアプリなど）**
- **従来のサーバーセッションを使ったシステム**
- **ログアウト機能が重要な場合**

→ **ALB + EC2 ならセッションID認証が一般的**


### JWT認証が向いているケース
- **API Gateway + Lambda でサーバーレスアプリを作る**
- **モバイルアプリやシングルページアプリ（SPA）**
- **マイクロサービスでトークンベース認証をしたい**

→ **API Gateway + Lambda の環境では JWT の方が適している**


## 5. まとめ
✅ **セッションID認証**
- サーバーでセッションを管理する
- シンプルだがスケールしにくい
- **EC2ベースのWebアプリ向け**

✅ **JWT認証**
- クライアントがトークンを管理する（ステートレス）
- APIやマイクロサービスに適している
- **API Gateway + Lambda での認証向け**

### **結論：どちらを選ぶべきか？**
**➡️ EC2ベースならセッションID、API GatewayならJWT**
AWSではサーバーレス（API Gateway + Lambda）が増えているので、**JWTが主流** になりつつある。


## 参考リンク
- [JWT公式サイト](https://jwt.io/)
- [AWS公式ドキュメント（認証）](https://docs.aws.amazon.com/)
- [OAuthとJWTの違い](https://auth0.com/docs/security/tokens/json-web-tokens)
」
（移住前 : 大阪編） 移住に向けた活動の開始（移住に対する不安の言語化）

# はじめに
本記事では、移住前（大阪時代）に始めた活動 についてまとめたいと思います。
移住を考えたときに感じた "不安" に対して、どのように向き合ったかを整理しています。
次の記事では、本記事で整理した内容を前提に、「地方移住のために実施した活動」などをまとめたいと思います。

# 移住前に持っていた印象 と 不安
筆者が移住を考え始めたとき、 "大きな不安感" がありました。
この不安感は、「仕事」「生活」「将来」といったキーワードはありましたが、具体性がありませんでした。
そのため、 "求めること" 、 "何に不安を感じているのか" を **言語化すること** から始めました。
その結果、移住に対するイメージが具体化でき、どのようにアプローチするべきかの方針が固められました。

## 移住に "求めること" か（地方へ移住しても実現したいこと）
まず最初の言語化は、移住で "何を実現したいのか、何を変えたくないのか" についてでした。
この内容は自分が大切にしていることなので、比較的簡単に言語化ができました。
筆者の場合は、以下の3つが大きな要素でした。
- 妻の希望（高知市移住）を叶えたい
- IT職の仕事を続けていきたい
- 価値のある人間(エンジニア)でいたい
求めることを言語化すると、 "漠然とした不安" に具体性が出てきました。

## 移住に "感じている不安" は何か
### 仕事面 : 「 IT職の仕事を続けていきたい 」
"求めること" を言語化したときに、最も重要に感じた "仕事面" について深堀を始めました。
大阪時代での転職活動の経験を延長し、一般的な求人サイトから職を探しはじめました。
ですが、 高知県（高知市）だけでなく、四国内でのエンジニア職の求人もほとんど見つけられませんでした。
また、転職エージェントの方と話をしても、高知県（高知市）でのエンジニア職の情報はあまりありませんでした。（当然だとは思いますが・・・）
一般的な方法では見つけることができず、情報収集に方針を変えて以下のような印象に至りました。
- 少ない求人の中でも、WEB系の仕事（デザイン関係）・開発系（スタートアップ系）の仕事が中心である。
- 情シス や インフラ 知識を活かせる仕事はほとんど見つからない。
- クラウドを活用したインフラ関係の仕事は 地方にはない。（都市部に集中している）
- 地方のスタートアップ企業であれば、求人はありそうだが、出会いがない。
- （当時の筆者のスキルレベルは 広く浅く のため、スタートアップ企業が求めるスキルレベルに達していない）
そのため、 "一般的な転職活動とは異なるアプローチが必要" という結論にいたりました。
アプローチに関しては、次の記事にまとめたいと思います。

### スキルアップ面 : 「 価値のある人間(エンジニア)でいたい 」
最新のトレンドについては情報を収集したいと思っています。
大阪に住んでいる間は、頻繁に土日に開催されるIT系イベントに参加していました。
また、仕事帰りに書店に行くことがルーティーンワークになっており、技術書の新刊 を見て、トレンドを知る機会が多くありました。
主に上記の2点が、大阪での情報収集の大きな要素と考え、高知移住後だとどうなるのか調べて（検討して）みました。

高知でも不定期ながらイベントが開催されています。
- JAWS-UG （AWSユーザーグループ） : https://jawsug-kochi.connpass.com/
- ウェブクリエイターズ高知 WCK : https://www.kochiweb.com/
- コミュニティリーダーズサミット高知（CLS高知） : https://www.cls-kochi.com/

また、書籍に関しても オーテピア高知図書館（ https://otepia.kochi.jp/ ） が 高知市中心街にあります。
妻の実家に行った際に、一度 本図書館に行き、技術書の品揃えなどを見て、多数書籍があることを確認しました。
上記から、ロケーションに紐づく情報収集に関する不安は解消されました。

### プライベート面 : 「妻の希望（高知市移住）を叶えたい」
妻の希望は "高知に帰りたい" だったため、移住することで メインの希望 は叶えることができます。
また、何度か 妻の実家に帰ることがあったため、 高知市がどのような場所かは知っていました。
ですが、筆者は 大阪以外で生活をしたことがないため、地方で生活するイメージはありませんでした。
そのため、妻から話を聞いたり、SNS等で情報収集を行いました。
- 休日に出かけるところは同じ場所になる（出かけるところはあまりない）
- 移動手段は 車 が中心
- 近所付き合いはありそう（イメージ）
- 運動できる場所（施設）はありそう など
結果、特に気になることもなく、生活面での不安も解消されました。

## 次のアプローチ
"言語化" したことで、 移住のために "仕事面" でのアプローチに重点を置いて進めるべき と結論が出ました。
漠然としていた "不安感" を言語化することで、次のアプローチがわかりやすくなり目標が立てられるようになりました。
ここから具体的に行動に移していくことになります。

次の記事からは "高知で仕事を探す" ために実施したことをまとめます。
システムは業務を効率化するか？

# はじめに
DXという流行りもあり昨今では様々な場面でシステム導入が進んでいる。
システム導入を進める理由や目的は様々だが、企業がシステムを導入する目的は「業務効率を上げる」という目的がかなり多いように感じる。
ただ、本当にシステムの導入は業務効率を上げるのか？
この点について考えてみたい。

# システム導入の目的
一般的にシステム導入はどのような目的で行われるのだろうか？
JUAS(日本情報システム・ユーザー協会)の[企業IT動向調査報告書 2024](https://juas.or.jp/cms/media/2025/01/JUAS_IT2024.pdf)に約1,000社のアンケート結果がまとめられている。
これによると、企業規模や業種で違いはあるものの短期経営課題、長期経営課題いずれにおいても「業務プロセスの効率化」が最も多くの割合を示している。
![IMG_4224.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2431363/3d59c79f-e0d4-4bdc-9747-4130081c79f9.jpeg)
![IMG_4226.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2431363/c955819c-8234-41c7-a52b-d0057baa4dd3.jpeg)

# 何故システム導入は業務プロセスを効率化するのか？

一般にシステム導入により業務が効率化されるイメージは多くの人が持っていると思う。
ただ、実際にはどういったロジックで効率化されるのだろうか？
これは体系だったドキュメントが見つからなかったのでchatGPTに聞いてみた。

|No| 観点| 根拠|
|:--|:--|:--|
|1|業務の自動化 |手作業よりシステムの方が早い|
|2| 業務の標準化  |品質の高い最適なプロセスで業務遂行できる|
| 3 | データの一元管理  | 探す手間の削減やデータ不整合による手間防止|
| 4  |  ミス防止 | システムで間違った進め方にチェックがかかる |
|5   | 業務可視化と改善|  データ集計が用意になり分析しやすくなる|

このように見ると、1の自動化は**システム自体が効率化をもたらす**という観点だが、それ以外は**システムにより業務が変わり結果として効率化をもたらす**という観点である。
後者の観点を逆に言うと**業務が変わらないとシステムを入れても効率化されない**ということだ。

# システムは本当に自動で動くのか？
それではシステム自体が自動化により効率化をもたらすとして、そもそもシステムは本当に自動で動くのか？
実際は、システムは命令を出さなければ動かず、命令の元となるデータが無ければ動かない。
そのためユーザーには自動で動いているように見えてるが、背後ではSEが命令を設計しスタッフがマスターデータを事前に入力して動いているに過ぎない。

```plantuml
actor SE
actor Staff
actor User
SE --> (System)
Staff --> (System)
(System) <-- User
```

結果として、ユーザー部門の業務をシステム、システム部門、スタッフ部門が肩代わりしてユーザーの業務は効率化されている。
これ自体は一般的なシステムの構図だが、個別ロジックが増えるとどうなるか？
SEが設計すべき命令は分岐が増え複雑になり、複雑な設計に耐える為スタッフがマスターを追加する。

```plantuml
actor SE
actor Staff
actor User
SE -[thickness=6]-> (System)
Staff -[thickness=6]-> (System)
(System) <-- User
```
ユーザー業務は効率化している代わりにSEやスタッフの負荷は上がっている。
結果的に、**会社全体の業務としてはあまり効率化されていない状態**になる。

# 結論
業務の効率化を目的にシステム導入されることは多い。
実際には**いくらシステムを入れても業務が変わらないと効率化はされない。
システムの導入をするなら業務の標準化と業務設計を行い、それを継続的に改善するプロセスも同時に稼働して初めて効率化される。**

# あとがき

ここまでシステム導入と並行して業務の変革する重要性の話をした。
ただ、現実にシステム導入と業務設計、改善プロセスを一緒に考えて導入するのはかなりハードルが高いとは思う。実際とりあえずシステムを業務に合わせて入れた後に、後から改善していくことだって多々ある。
何より**システム導入は分かりやすくてカッコいい**ので業務フロー書いて設計してるより楽しい。なのでみんなのモチベーションになるのだ。
大事なのは**システムと同時に業務を標準化し変えていく重要性を常に認識し、実際に変えていく人と共にシステムを運営すること**で、それこそが効率化を生むのだと思う。
'['git', 'push', 'origin', 'gh-pages', '--force']' returned non-zero exit status 128.

GitHub actionで`mkdocs`のドキュメントを作るたびにこのエラー&darr;にはまるのでメモ。

```shell
subprocess.CalledProcessError: Command '['git', 'push', 'origin', 'gh-pages', '--force']' returned non-zero exit status 128.
```

botにpushの権限がないのが原因。GitHubのリポジトリから

Setting > Action > General > Workflow permissions

で

Read and write permissions

にチェックすると直る。
【Django×TypeScript×Render】映画発掘ゲーム風アプリmovieDIGをリリースしました!

### 本記事の内容
この度、ポートフォリオ「[movieDIG](https://moviedig-frontend.onrender.com/)」をリリースしましたので、投稿いたします！

https://moviedig-frontend.onrender.com/

映画発掘をするゲーム風アプリになります。

#### なぜ作ろうと思った？
なぜ作ろうと思ったのかは2つ理由があります

##### pygameでゲームを制作しているYouTuberに影響を受けた

皆さんはこの方、ご存知ですか？
「pythonで10行でマリオ作ってみた！」という動画から始まり、
1ショートでスーパーマリオブラザーズの1機能追加をしていき、どんどん本家みたいになっていくのを見ることができます。

<iframe width="560" height="315" src="https://www.youtube.com/embed/TCYdZvlFYcQ?si=GlKVFeTmBbxlXDxE" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

この動画に感化されて自分もゲームアプリを作りたいと思い立ち、
[pygame](https://www.pygame.org/wiki/GettingStarted)のインストールから始めました。

##### Netflixを使っているときのモヤモヤ
ゲームアプリの内容は何にしようかな..と考えたところ、Netflixを使っているときのモヤモヤがふと思い浮かびました。
 - 「Netflixではいろいろな映画がラインナップとして揃っているけど、レコメンドされているもの以外のものは自分で検索する必要があるな...🤔」
 - 「レコメンドされているものは似ているものが多いし、もっとバリエーションが欲しいな...🤔」

など。
そこで、自分で映画を発掘できるようなゲームアプリを制作しようと決めました。

#### どんなアプリ？

<!-- アプリ説明 -->

### サービスについて

#### movieDIG
 - URL: https://moviedig-frontend.onrender.com/
 - GitHub: https://github.com/maixhashi/moviedig/

<img width="1440" alt="Image" src="https://github.com/user-attachments/assets/83ed6acd-897e-4f87-926a-469761285674" />

#### 使用技術
#### ゲーム模擬実装
 - pygame
   - pygameでゲーム機能を実装 → Django+React(TypeScript)で再実装という流れで制作するため使用しました。
#### バックエンド
 - python 3.12.8
 - Django 3.2.25
#### フロントエンド
 - React 18.3.1
   - Redux 
 - TypeScript 5.6.3
 - Vite 5.4.8
#### インフラストラクチャー
 - Docker
 - Render

#### ER図

![moviedig-ER-diagram.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/606327/6e6fc084-946c-4cb9-ab5e-7acb898f8bd5.png)

#### 機能

##### ゲームスタート
トップ画面の「ゲームスタート」ボタンからゲームスタート！
アカウント作成・ログインしなくてもゲストログインですぐにスタートすることができます。
![GIF-moviedig-game-start.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/606327/461312a2-8d1a-497a-8350-8bb4928cfde9.gif)

##### 宝箱から映画を発掘！
プレイヤーキャラを動かして、地面を掘り進めてきます。
宝箱から映画ポスターをランダムにゲットできます
![GIF-moviedig-game-playing-dig.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/606327/35d2a1c7-e768-4f23-bf5a-06a01a913ff1.gif)

##### 発掘した映画を確認
ゲーム画面でゲットしたときの映画ポスターリストをクリックすると、
集めた映画ポスターを確認することができます。
![GIF-moviedig-collected-movie-confirm.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/606327/207c38ba-e495-421d-8dc4-05dbfda3abea.gif)

##### ナビゲーターモグラ
(個人的に気に入っている機能)
ゲーム画面でナビゲーターのモグラが操作方法etcを話してます
![Image](https://github.com/user-attachments/assets/b21137b9-cbb9-4235-88a9-817c4d67c310)

### 終わりに
ゲームアプリ制作、思い返すとこれまでのポートフォリオ制作の中で一番楽しかったです。

現時点では、映画を発掘してポスターを確認するのみのシンプルな内容ですが、まだまだ機能追加の余地があると感じています。

ちなみに、プレイヤーキャラクター、モグラのアートは流行りのDALL-3に描いてもらいました。
（なかなかモグラがかわいくならず苦戦した末に今のかわいいモグラを世に出せました）
絵心がないからキャラクターデザインどうしよう...と悩んだのも束の間、
アイデアを形にするGPTがあるから心強いもんです。

今回のmovieDIGの制作の影響が波及して、steamでゲームをリリースしているインディーズゲーム製作者の動画を見たりして、いつかsteamでゲームリリースもいいなぁ、、とか思ったり。

読んでくださり、ありがとうございました。
excelでカンマ区切りの数値を分割して別セルに貼る式

今回はEXCELを使用した内容の記事を作成しました。

:::note info
対象：Excel 関数、ExcelVBA
:::

Excelでカンマ区切りの数値を分割して別セルに貼り付ける方法はいくつかあります。
ご自身のExcelのバージョンや用途に合わせて最適な方法を選びましょう。

## 方法1: `TEXTSPLIT`関数 (Excel 365 / 2021以降)

`TEXTSPLIT`関数は、Excel 365および2021以降で利用できる比較的新しい関数です。
文字列を指定した区切り文字で分割し、結果を配列として返します。

### 横方向に分割

A1セルに以下のデータがある場合：

```
123,456,789
```

B1セルに以下の式を入力：

```excel
=TEXTSPLIT(A1, ",")
```

➡ B1セル、C1セル、D1セル… に自動的に分割されます。

### 縦方向に分割

```excel
=TEXTSPLIT(A1, ",", , TRUE)
```

➡ B1セル、B2セル、B3セル… に分割されます。

`TEXTSPLIT`関数の第3引数は、分割後に空のセルを無視するかどうかを指定します。
省略した場合、空のセルはそのまま出力されます。
第4引数に`TRUE`を指定すると、分割結果を縦方向に配置します。

## 方法2: `FILTERXML`関数 (Excel 2013以降)

`FILTERXML`関数は、XML形式の文字列からXPath式を使ってデータを抽出する関数です。
この関数を使うことで、カンマ区切りの文字列を分割することができます。

### 横方向に分割

```excel
=FILTERXML("<t>
" & SUBSTITUTE(A1, ",", "

") & "
</t>", "//s")
```

### 縦方向に分割

```excel
=TRANSPOSE(FILTERXML("<t>
" & SUBSTITUTE(A1, ",", "

") & "
</t>", "//s"))
```

`FILTERXML`関数を使った方法は、少し複雑な数式になります。
`SUBSTITUTE`関数でカンマをXMLタグに置き換え、`FILTERXML`関数でタグ内の文字列を抽出しています。
`TRANSPOSE`関数を使うことで、縦方向に分割することができます。

## 方法3: `MID`関数と`SEARCH`関数の組み合わせ（関数のみ）

`MID`関数と`SEARCH`関数を組み合わせることで、Excelのすべてのバージョンでカンマ区切りの文字列を分割することができます。
ただし、数式が複雑になるため、注意が必要です。

#### 1番目の値（B1セル）

```excel
=LEFT(A1,SEARCH(",",A1)-1)
```

#### 2番目の値（C1セル）

```excel
=MID(A1,SEARCH(",",A1)+1,SEARCH(",",A1&",",SEARCH(",",A1)+1)-SEARCH(",",A1)-1)
```

#### 3番目の値（D1セル）

```excel
=RIGHT(A1,LEN(A1)-SEARCH(",",A1,SEARCH(",",A1)+1))
```

`LEFT`関数、`MID`関数、`RIGHT`関数を使って、カンマで区切られた文字列を順番に抽出しています。
`SEARCH`関数でカンマの位置を調べています。

## 方法4: VBAを使う（大量のデータ向け）

VBA（Visual Basic for Applications）を使うことで、大量のデータを効率的に処理することができます。

```vb
Function SplitValues(ByVal txt As String, ByVal delim As String, ByVal index As Integer) As String
    Dim arr As Variant
    arr = Split(txt, delim)
    If index <= UBound(arr) Then
        SplitValues = arr(index)
    Else
        SplitValues = ""
    End If
End Function
```

使い方：

B1セルに1番目の値を取得する場合：

```excel
=SplitValues(A1, ",", 0)
```

C1セルには：

```excel
=SplitValues(A1, ",", 1)
```

➡ インデックスを増やすと、別の要素を取得できます。

VBAを使う方法は、大量のデータを処理するのに適していますが、VBAの知識が必要になります。

## まとめ

| 方法         | 使用可能バージョン | 特徴                                                                 |
| :----------- | :------------- | :------------------------------------------------------------------- |
| `TEXTSPLIT`  | Excel 365 / 2021 | 簡単で便利。文字列を分割して配列で返す。                                                     |
| `FILTERXML`  | Excel 2013以降   | 関数だけでOK。XML形式の文字列を操作する。                                                  |
| `MID+SEARCH` | すべてのバージョン  | 手動で対応可能。数式が複雑になる。                                                               |
| VBA          | すべてのバージョン  | 大量データ向け。VBAの知識が必要。                                                               |

## おすすめの方法

✅ Excel 365 / 2021なら `TEXTSPLIT` が最も簡単です。
✅ Excel 2013-2019なら `FILTERXML` がおすすめです。
✅ 旧バージョンなら `MID+SEARCH` で対応できます。
✅ 大量のデータがある場合は、VBAを使うと効率的に処理できます。

上記の内容で、Excelでカンマ区切りの数値を分割して別セルに貼り付ける方法について、より理解を深めることができたでしょうか？
ご不明な点がありましたら、お気軽にご質問ください。
知ってる？Chromeのコマンドでスクリーンショットを一発撮影！

今回は私が普段使っている便利なコマンドについて紹介したいと思います。

# コマンドを使うステップ
1. デベロッパーツールを開く
    1. ショートカットキーはCtrl+Shift+Iもしくは F12 キー（Windows / Linux）またはCommand+Option+I（Mac）
1. コマンドメニューを開く
    1. ショートカットキーはCtrl+Shift+P キー（Windows / Linux）または Command+Shift+P キー（Mac）

# screenshotの種類
コマンドメニューを開いて「screenshot」と入力すると以下の4種類のスクリーンショットに関するコマンドが表示されます。
| コマンド | 内容 |
|:-:|:-:|
|  Capture screenshot |  現在表示されている範囲のスクリーンショットを撮影します。 |
| Capture node screenshot  |  選択した HTML 要素 (ノード) のスクリーンショットを撮影します。 |
|Capture area screenshot|指定した領域のスクリーンショットを撮影します。|
|Capture full size screenshot|Web ページ全体のスクリーンショットを撮影します。|

使いたいコマンドをクリックすると使用することができます。
中でも`Capture full size screenshot`はスクロールが必要な画面を1枚のキャプチャに収めたいという時に便利です。
chromeでは拡張機能を使わずにキャプチャができるのは知らない方も多いのかもしれません。

# 最後に
スクリーンショットのコマンドだけではなくさまざまなコマンドがあるので、暇な時に色々みておくと使えることもあるかもしれません。
ぜひコマンドを活用してみてください。
isucon練習記録3

## 状況
dockerコンテナ削除するたびに `less` などが使えなくなるのが面倒

## 対策
ansibleでミドルウェア管理するようにする

## 手順
### 1. ローカルへのansibleインストール
[基本方針：公式通り](https://docs.ansible.com/ansible/2.9_ja/installation_guide/intro_installation.html#macos-ansible)に進める
#### 1-1. pythonコマンド事前準備
1. `python --version` すると `zsh: command not found: python` となる
1. `brew install python` すると `Warning: python@3.13 3.13.2 is already installed and up-to-date.` となるのでパス通っていない
1. `brew reinstall python@3.13` するとインストール成功
1. `brew info python@3.13` すると
    ```
    Unversioned symlinks `python`, `python-config`, `pip` etc. pointing to
    `python3`, `python3-config`, `pip3` etc., respectively, are installed into
      /opt/homebrew/opt/python@3.13/libexec/bin
    ```
    となっているのでここにパスを通す
1. `vi ~/.zshrc` で `export PATH="/opt/homebrew/opt/python@3.13/libexec/bin:$PATH"` 追記
1. `source ~/.zshrc`
1. `python --version` で `Python 3.13.2` 表示されるようになった
#### 1-2. pipコマンド確認
1. `pip --version` とやると `pip 25.0 from /opt/homebrew/lib/python3.13/site-packages/pip (python 3.13)` になる
#### 1-3. ansibleインストール
1. 公式通り `pip install --user ansible` とやると `error: externally-managed-environment` になって仮想環境用意しろと言われるので諦める
1. `brew install ansible`
1. `ansible --version` が使えるようになったので入った

### 2. playbook作成
`ansible_connection=docker` にすることでsshを使わずに接続する
偏差値47の高卒がTOEIC300点台(推定)→795まで4ヶ月(124時間程度)で伸ばした考え方

# 実際のスコア
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3355973/6eae7eb1-b95c-4a7d-84f3-bc2b6cedc7a4.png)
![スクリーンショット 2025-02-13 20.26.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3355973/afba4a6c-1c5a-493f-a03f-5ce4274bbff8.png)

バランスよく取れています。
受験回数は2回です。
1回目：675(リスニング音割れしてたの今でも意味わからない)
2回目：795

使用した学習アプリを確認したところ、合計学習時間は7432分だったので、大体124時間。
ただ、アプリのみなので、技術資料などを英語で頑張って読んだ時間も含めるともう少し多いかも？

# 簡単な自己紹介
年齢：23歳
普段：都心の上場企業でAndroidアプリ開発エンジニア
学歴：偏差値47程度の工業高校を卒業
資格：
　・基本情報技術者試験
　・応用情報技術者試験
　・情報処理安全確保支援士
　・Google Cloud Digital Leader
　・Google Cloud Associate Cloud Engineer
　・Google Cloud Professional Security Engineer
　・TOEIC LR 795点
趣味：
　・チェス
　・個人開発(Android/iOS/Google Cloud)
　・美味しいもの食べる
　・たまーに哲学書読む
　・たまーにLT登壇してます
　・たまーにサイバーセキュリティやってます(rev分野多め)

# TOEIC学習開始時の語学力
・基本5文型を知らない
・SVOの意味が分からない(Sってなに？みたいな)
・関係代名詞はthatこそ最強(先行詞？制限用法/非制限用法？そんなの知らない)
・語彙力は低め(indicate, survey, contractなども知らず)
・リスニング力は皆無

この辺りをAIに話して点数を予測させたら300点台じゃないですか？って言われた

# 使用した教材
1. abceed
2. 金フレ
3. でる1000

## abceedについて
iOS/Webで提供されている英語学習用プラットフォームです。(本格利用だとサブスク必須)
私が使用していて特に素晴らしいと思った内容は下記です。
　・TOEICに限らず、英検対策や発音チェックなど様々な用途として使用可能
　・TOEIC本番模試を行える
　・TOEIC予測スコアを出してくれる(モチベアップの要因の1つ)
　・学習時間を可視化してくれる(モチベアップの要因の1つ)
　・AIが自身のレベルに沿った問題を提出してくれる(おすすめ問題機能)
　・参考書や単語帳が無料で使える(金フレ及びでる1000もこれで使用)
　・間違えた問題は後日自動的に出題されるため復習も容易

特に、おすすめ問題機能は素晴らしく、自身のレベルと同じ〜少し上のレベルを出題してくれます。
基本、こちらを用いて学習を進めました。

## 金フレ
有名のため省きます。

## でる1000
Part5対策用として利用していました。
語彙/文法、双方の力が一定程度育っていないと難しく折れやすいです。(一度折れた)
ただ、Part6/7の基礎はPart5なので進めて損はありません。

# 本題の前に
まず、今回は具体的な学習方法についてはあまり触れません。
何故なら、私の学習方法が読み手の方に適しているとは限らないからです。
そのため、今回は「学習方法を確立するための方法・考え方」について執筆します。

# 本題
## 自分のことを熟知する(メタ認知)
自分自身についてどれだけ熟知していますか？
勉強や仕事でどういう進め方が合っているか？など言語化できますか？
私の場合、
　・資格試験(応用や高度など) → 参考書を全て浅く読んだ後に過去問題を解く
　・プログラミング学習 → 参考書は相性が悪く、個人アプリを通じて実践的に使っていく
　・サイバーセキュリティ学習 → こちらも参考書は相性が悪く、CTFや仮想環境などを通じて実践的に攻撃する
　・勉学などの学習 → 勉強方法の確立は苦手だが一度出来ればそれを継続して習慣化することは得意
　・仕事 → メンバーで話し合って設計議論などをしたい。黙々よりも明るい職場の方がパフォーマンス出しやすい

などが挙げられます。
つまり、「どんな条件が揃えば自分は高いパフォーマンスを発揮できるか？」をメタ認知することが大事です。

## 実践 - リスニング(Part2)で考えてみよう
まず、Part2を1-2時間解き、正解した問題について振り返ってください。
間違えた問題は、「自分の中に存在する正解するための条件を満たせていなかった」だけなので今は無視です。

正解した問題を振り返った時、理由は何が挙げられますか？
例えば最も考えられるのは「単語の意味が分かったから」でしょうか？
実はこれはまだ更に分析することができます。
単語の意味を理解するためには下記の条件を満たす必要があります。
　・音を聴いて単語へ変換する能力
　・単語を翻訳する能力

では、この条件を満たすために「どのように問題文を聴く」のがいいのか？
色々種類があります。
　・1音毎に脳内で処理をする方式
　・全文聴いた後に全て処理をする方式
　・全て拾いきるのは不可能なのでSやVなど主要な単語のみ拾って処理をして推測で解く方式

色々な"聴き方"を試してみることが非常に大切です。
この他にも様々方法はあると思うので試してみてほしいです。

因みに、私の場合は1つ目が一番適していました。それ以外は、
　・2つ目：全文覚えられない+応答文へのリソースが割けない
　・3つ目：SやVに集中してしまうことで「どれがS？どれがV？」と考えてしまい逆に解けなくなる
このことから、私の場合は下記の学習がPart2には適しているということが分かりました。
　・発音と単語を結びつける学習
　・単語を翻訳するための語彙力を身につける学習
　・翻訳した内容をワーキングメモリーに保持しておく練習
そのため、一般的に推奨されるシャドーイングなどは自分に適していない(自身の声で正しい音を掻き消すから)ことが分かります。
このように、一般的に推奨される方法が適しているかどうかは自分の特性によって大きく左右されるのです。
それを見極めて言語化することが非常に大事です。

## まとめ
正解できた理由は？
⇩
正解を得るために必要な条件は何か？
⇩
それらの条件を揃えるためには何を意識/特訓すればいいか？

上記の分析をひたすら繰り返していくことが大事です。
これをPart1-Part7でそれぞれ考えたりしてました。

## 最後に
今回のように、メタ認知を行って進めるという方法を是非積極的に採用してみてください。
こういった、本質的な学習方法が掴めていると他分野のキャッチアップも効率的に出来ます。
PMBOK原理・原則：リスクの最適化

PMBOKのリスク管理について記事を書きました。
「リスク＝悪いこと」という固定観念を外して、チャンスとしても捉えていく。そして小さな一歩から始める。
この2点を意識するだけでも、プロジェクトの成功確率は上がるのではないでしょうか。
ご参考になれば幸いです。🙏

![マインドマップ (13).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/3ff96d19-a91e-8a74-798a-8b72ef1cf0af.png)

## はじめに
![risk-management-detailed.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/cc4d5efa-a8ad-ab13-6b6e-b8cccbd629ab.png)

～リスク対応策を見直して機会を最大化する～

ソフトウェア開発やプロジェクト進行では、日々いろんなリスクに直面しますよね。突然の仕様変更や新しい技術導入、人的リソース不足など、挙げればキリがありません。だけど「リスク管理ってなんか固そう」「結局めんどくさくて後回し…」と敬遠されがち。でも、ちょっとした工夫でリスク管理をうまく回せるなら、むしろ現場の負担やストレスを減らし、チャンスを最大化できる可能性があります。

この記事ではPMBOK（Project Management Body of Knowledge）のリスク対応策の基本をおさらいしつつ、現場で働くエンジニアやリーダーが「これなら少しやってみる価値がありそう」と思えるようなポイントをまとめました。

## 1. リスクは「悪いこと」だけではなく「機会」でもある

![risk-opportunity-revised.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/47d6b2f8-12fd-246b-216f-932ddd1725da.png)

- **PMBOKの“リスク”は「不確実性」全般を指す**  
  実際の運用では「リスク=トラブルの原因」みたいに考えがちですが、PMBOK的にはポジティブなチャンスも「リスク」の一部です。  
  - 例）新技術の導入は失敗のリスクがある一方で、大きな差別化要素にもなる。

- **“機会”を意識して動くと雰囲気がポジティブになる**  
  リスクを“避ける”だけだとどうしてもテンションは下がりがち。でも「攻めの姿勢で得られる可能性」があるとわかると、チームのやる気をうまく引き上げられます。

## 2. PMBOK的リスク対応策の基本をサクッとおさらい

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/1c393129-d3ef-f7d9-f25e-9d57d2e9f071.png)

PMBOKには、リスクへの対応策として主に以下の5種類が出てきます（ポジティブリスク向けに追加要素もあります）。

1. **回避（Avoid）**  
   - リスクを発生させる要因そのものを取り除く  
   - 現場でありがちな例：  
     - 難易度が高くて間に合わない機能をスコープから切る  
     - 無理なスケジュール・要件を見直す

2. **軽減（Mitigate）**  
   - 発生確率や影響度を小さくする  
   - たとえば：  
     - 新しい技術ならPoCを小さくやってみて、いけそうなら拡大  
     - フェイルセーフを組み込んで不具合の範囲を局所化

3. **転嫁（Transfer）**  
   - リスクを外部や契約などで別のところに移す  
   - たとえば：  
     - 専門ベンダーに依頼して契約で範囲や責任を明確化  
     - クラウドサービスのSLAを活用

4. **受容（Accept）**  
   - 対応コストが高すぎたり重要度が低いリスクは「そのまま受け入れる」  
   - ただし、万一の発生時にどうリカバリするかは把握しておく

5. **活用（Exploit）** ※ポジティブリスク向け  
   - プラスを見込めるなら、積極的に手を打って機会を取りにいく  
   - 例：  
     - 新技術を導入するためのスポット投資で、後の大幅な効率化や差別化を狙う

## 3. 「リスクの最適化」で得られるメリット

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/be25ebcd-be21-8e34-3157-6dafa9a620f9.png)

- **燃え尽き症候群の予防**  
- **チャンスを逃しにくくなる**  
- **チーム内コミュニケーションの活性化**

## 4. まずは小さくやってみる：現場Tips

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/cc9f55d0-06b8-14f8-861d-a87a13ba41b5.png)

### 4-1. リスク/機会メモをライトに運用してみる
- Slackの専用チャンネルやGoogleスプレッドシートなどでざっくりメモを残す

### 4-2. 定例ミーティングで5分だけ「リスクと機会を振り返る」
- 短時間の共有タイムを設けるだけで意識が高まる

### 4-3. トラブルの共有は“ネガティブ”じゃなく“学習”の場に
- 振り返りを「次に活かす場」として活用

### 4-4. 機会の活用は「小さな実験」から
- PoCを小さくやってみる

## 5. 現場のみんなに届けたい想い

- **「リスク管理=めんどくさい仕事」だけじゃない**  
- **みんなでリスクに目を配るからこそ、リーダーの負担も軽くなる**  
- **不安や改善案を言いやすい空気が生む「安心感」**  

## まとめ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/c77136b4-dfab-c446-fada-71b24ad0d2bf.png)

- リスクはポジティブな機会も含む不確実性である  
- 完璧を目指さず、小さくリスク・機会の洗い出しをしてみる  
- トラブルを学びに変え、チャンスをうまく掴むとチームの空気も良くなる  

「リスクマネジメント」を聞くと固いイメージがありますが、要は「ちょっとした気配りを現場全体で積み重ねる」イメージに近いと思います。その積み重ねが、後々大きな炎上を防いだり、意外な機会を引き寄せたりするきっかけになるはずです。ぜひ無理のない範囲から取り組んでみてください。チームのモチベーションアップと成果につながることを願っています。