OmniAICreator/Qiita-1.07M huggingface dataset:
MCPサーバーを実装してCursorエディタで叩いてみる

#  はじめに
今回はMCPサーバーを実装して、Cursorエディタのチャットからアクセスするまでの手順を紹介します。

## 事前準備
プロジェクトを作成します。

```zsh
mkdir mcp-test
code mcp-test
npm init
```

今回使用するライブラリをインストールします

```zsh
npm i npm install @modelcontextprotocol/sdk  
```
## 実装

https://github.com/modelcontextprotocol/typescript-sdk?tab=readme-ov-file#quick-start

クイックスタートのコードをそのまま持ってきます。
`server.resource`につづく部分は、現在Cursorエディタからresourceが参照できないため削除します。(残していても問題はないです)

```js:index.js
import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";

// Create an MCP server
const server = new McpServer({
  name: "Demo",
  version: "1.0.0"
});

// Add an addition tool
server.tool("add",
  { a: z.number(), b: z.number() },
  async ({ a, b }) => ({
    content: [{ type: "text", text: String(a + b) }]
  })
);

// Start receiving messages on stdin and sending messages on stdout
const transport = new StdioServerTransport();
await server.connect(transport);
```

package.jsonに追記します。

```json
{
  "name": "mcp-test",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "type": "module", // <--追記
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.9.0"
  }
}

```

## MCPサーバーをCursorに設定

基本設定 → Cursor Setting → MCP → Add new Global MCP serverを選択、
`mcp.json`に以下を追記します。(argsは前述したindex.jsのフルパスを記載してください)

```json
{
  "mcpServers": {
    "Demo": {
      "command": "node",
      "args": ["index.jsのパスを記載"]
    }
  }
}

```

基本設定 → Cursor Setting → MCPにもどって、以下のスクショのように緑色のランプが付いていたら問題ないです。

![スクリーンショット 2025-04-13 23.57.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2778030/711eeaaf-6c9c-4419-a3f8-4b12f65a1e43.png)


## Chatから叩く

テキストで質問して見ます。

![スクリーンショット 2025-04-13 23.59.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2778030/fdb52f5a-79aa-4d7c-9a36-8915abc384d3.png)

`add`というtoolを参照して、きちんと返答が行われました！


## toolを追加してみる

toolは複数定義できます。
試しに、uuidを生成するtoolを定義して見ましょう。

```js:index.js
// uuidを生成するtool
server.tool("generate_uuid", {}, async () => ({
  content: [{ type: "text", text: crypto.randomUUID() }],
}));
```

![スクリーンショット 2025-04-14 0.01.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2778030/4df905ef-8945-4491-8545-b7122e29d622.png)

refreshボタンを押すと、toolの部分に`generate_uuid`が追加されているのが確認できます。

uuidを生成してもらいましょう。

![スクリーンショット 2025-04-14 0.03.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2778030/fefaf97a-a0fa-406f-982d-744eeac5d57e.png)

正常に生成できたようです！

## 参考

https://azukiazusa.dev/blog/typescript-mcp-server/

https://github.com/modelcontextprotocol/typescript-sdk
【Safari】VSCodeを使用してブラウザの入力欄に書き込む方法

GhostTextを使用するとブラウザ内の入力欄にVSCodeなどのエディタを使用して書き込むことができます。


https://ghosttext.fregante.com/

## インストール

AppStoreからGhostTextをインストールします。

https://apps.apple.com/jp/app/ghosttext/id1552641506?mt=12

VSCodeにGhostTextの拡張機能をインストールします。

https://marketplace.visualstudio.com/items?itemName=fregante.ghost-text

## 使用方法

入力欄にカーソルを置き、VSCodeが起動した状態で`cmd + shift + K`を押下するとVSCodeでエディタが開き、以下の画像のように編集できます。

![screen-recording-15.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342443/0b10fd04-52f9-480f-9c0b-860a5d3cbd48.gif)
最近の生成AIまわりで思ったことをざっくり考えてみた

# はじめに

生成AIやエージェントについて最近思っていることを書いています。
調べきれていない部分もありますがざっくばらんに書いています。

# ソフトウェアエンジニアは生成AIに仕事を奪われるのか

まず、AIを活用して業務の効率化出来るスキルが無いと厳しいと思います。生成AIを全く活用出来ていない企業と活用している企業では差が顕著に出るからです。同じ結果になる手作業と機械作業の差と同じです。

また、AIは一般的な技術であればものすごく強いですが、例えばその企業のドメイン知識や社内だけのルール、設計方針、セキュリティ対策などは知らないので、それらを把握した上で設計やAI活用できるエンジニアはしばらく生き残れると思います。逆にそういうことはしておらず上位者が考えたタスクをこなしているだけのエンジニアは淘汰されてしまうかもしれません。コーディングが早い等では勝てない世界になるためです。

まだ2~3年は大丈夫でしょうが、今の加速度を持ってAIが進化していくと過程すれば想定より早くその時がくるかもしれません。

# なぜ生成AIは使われないのか？その理由と対策

道具の使い方を知らず、体験出来ていないからだと思います。人は不明なものや変化に恐怖を抱きます。（特に日本人は不安になりやすいようです）私の身近な話をすると、パソコンを初めて触った時怖かった覚えがあります。パソコンは便利と知っているにも関わらずです。
人からこれすごく良いから使ってみてと言われても使わないのは、新しい道具を使うことによる成功体験が少ないのではと思います。
※そもそも忙しすぎて試している暇が無いのかもしれません。

対策としては、一人でも良いと感じた人がいるならその方がリーダーシップを持って、ハンズオンなりで体験をさせるのが良いと思います。

あとやる気はあるが、流れが早すぎてキャッチアップできずついて行けないパターンも考えられますね。そう思っている仲間を見つけてお互いに得た知識を補完し合うと良いと思います。

# 生成AIによる知識の平均化について

AI系の動画見たりや本を読んで、こういう話があり面白いと思ったのですが
いままで30点40点だった人がChatGPTなどを活用することで、60~80点の結果を出せるようになるため、60~80点ラインの点数が普通になるということです。今まで60~80点で仕事していた人の需要が減ると思われます。

逆に良いこともあって、私はエンジニアですがチャットやメールでの文章作成能力は30点くらいなもので、それをGPTに聞くことで60点になる上に素早く文章作れるというメリットもあります。

ちなみに0~10点くらいの方は、どういう依頼を出したら良い結果が出てくるのかそもそもわからないので難しいです。私も例えばロケットの作り方は全く知らないので、依頼内容がかなり抽象的になるし、GPTの回答の良し悪しが判断できないです。

# 最後に

ここまでお読み下さった方ありがとうございます。

未来は誰にもわかりませんが、だからこそ「今のカオス」を楽しみつつ、
* 業務で活かせることがないか？
* チームや部署の効率化に使えないか？
* 逆に社外の人たちはどうしているのか？

といった視点で、小さな改善と適応を続けていくことが大切だと感じています。
【Python】json.dumpはやめとけ

Pythonの標準ライブラリ`json`でJSONファイルに書き込みを行うとき、こんな感じのコードになるのではないでしょうか。

```python
import json

data = {"a": 1}
path = "path/to/mydata.json"
with open(path, "w") as f:
    json.dump(data, f)
```

しかしこれは危険なのでやめておいた方が良いです。

## `json.dump`が危険な場合

シリアライズできない値がある場合を考えてみましょう。

```python
import json

data = {"a": "ok", "b": object()}  # <-- objectはシリアライズできない！
path = "path/to/mydata.json"
with open(path, "w") as f:
    json.dump(data, f)
```

これはエラーになります。エラーになるのはいいですが、いざそのJSONファイルを覗いてみると

```json
{"a": "ok", "b": 
```

というふうに途中で止まっています。つまり**上書きでデータが壊れる**ということです。これは、`json.dump`ではデータを少し文字列に変換してはそれを書き込んでいるからです。

これは、たとえばアプリケーションの設定などをJSONで同じファイルに書き出しているときなんかは、JSONの場所を探して手動で修復しないとアプリケーションが開けなくなるなんて事態になり得ます。

## `json.dumps`を使おう

`json.dump`の問題は、データを少しずつ文字列に変換してファイルに書き込んでいる点です。
`json.dumps`で一度すべて文字列に変換してから、まるごとファイルに書き込めば、この問題は解決ですね。

```python
import json
from pathlib import Path

data = {"a": "ok", "b": object()}  # <-- objectはシリアライズできない！
path = "path/to/mydata.json"

data_str = json.dumps(data)  # <-- ここでエラー
Path(path).write_text(data_str)  # <-- ここに来る前に止まる
```
【第2回】登山＆キャンプ記録アプリ開発｜投稿機能と画像アップロードを実装してみた！

# 【第2回】登山＆キャンプ記録アプリ開発｜投稿機能と画像アップロードを実装してみた！

こんにちは、Soraです！  
前回はアプリの概要や技術スタックについて紹介しました。

今回は、実際に記録を投稿できるようにする「投稿機能」について紹介します！  
写真のアップロードやGoogleマップの埋め込み、日付の取り扱いなど、初学者としてつまずいたポイントも含めて振り返ります。

---

## ✍️ 投稿機能でできること

- タイトル・日付・場所（住所）
- 感想の入力
- Google Map の埋め込み
- 写真の複数枚アップロード（1枚目は一覧に表示）

---

## 💡 使った機能とライブラリ

| 機能             | 使用技術／関数         |
|------------------|------------------------|
| フォーム入力     | Flask / request.form   |
| 日付変換         | `datetime.strptime()`  |
| 写真の保存       | `uuid` + `secure_filename()` + `os.path.join()`  
| 複数枚アップ     | `request.files.getlist()`  
| 画像の保存先     | `static/uploads/` フォルダ  

---

## 🔧 実装コード（抜粋）

```python
@app.route('/create', methods=['GET', 'POST'])
@login_required
def create():
    if request.method == 'POST':
        title = request.form['title']
        date_str = request.form['date']
        date = datetime.strptime(date_str, "%Y-%m-%d").date() if date_str else None
        address = request.form['address']
        description = request.form['description']
        map_iframe = request.form.get('map_iframe')

        new_post = Post(
            title=title,
            date=date,
            address=address,
            description=description,
            map_iframe=map_iframe,
            user_id=current_user.id
        )
        db.session.add(new_post)
        db.session.commit()

        # 写真アップロード
        if 'photos' in request.files:
            photos = request.files.getlist('photos')
            for photo in photos:
                if photo and photo.filename != '':
                    filename = str(uuid.uuid4()) + "_" + secure_filename(photo.filename)
                    filepath = os.path.join('static/uploads', filename)
                    photo.save(filepath)

                    new_photo = Photo(post_id=new_post.id, photo_path=filename)
                    db.session.add(new_photo)

            db.session.commit()

        return redirect(url_for('home'))

    return render_template('create.html')
```

## 😅つまずきポイントと解決法
1. equest.form['date'] が文字列だった → datetime.strptime()で日付に変換
1. 写真のファイル名がバラバラになる → uuidで一意な名前を生成
1. request.files.getlist() で複数枚対応できることに気づくのが遅れた！


### 🛠 つまずきポイント①：フォームから取得した日付の扱い

Flaskでは `request.form['date']` で取得した値は文字列（str）として渡ってくる。

しかし、SQLAlchemyで `Date` 型として保存するには、**Pythonの `date` オブジェクト**である必要がある！

なので `datetime.strptime()` を使って変換した👇

```python
date = datetime.strptime(request.form['date'], "%Y-%m-%d").date()
```
    
### 🛠 つまずきポイント②：アップされた写真のファイル名
😭同じファイル名の写真がアップロードされたら上書きされる。
→✅ 解決法：uuid を使って一意な名前にする！
**💡uuidとは？**
絶対に重複しないIDを自動で作るための仕組み！
毎回ランダムでユニークなIDを作ってくれるから上書きの心配なし！
```python
import uuid
from werkzeug.utils import secure_filename

filename = str(uuid.uuid4()) + "_" + secure_filename(photo.filename)
```

### 🛠 つまずきポイント③：複数枚の画像をアップロードしたい！

最初は `request.files['photo']` で取得していたため、複数選択しても**1枚目しか保存されませんでした**。

調べてみると、Flaskでは `request.files.getlist('photos')` を使うことで、**複数の画像をリストで取得できる**と判明！

HTMLの `<input>` に `multiple` 属性をつける必要もあります。

```html
<input type="file" name="photos" multiple>
```

## やってみた感想✨
- 写真を複数枚アップできるようにすると完成度が増した！
- 実際の運用を想定すると、保存先やファイル名の工夫が必要だと実感！

## 次回予告ッ‼️
次回は、投稿の詳細ページや編集・削除機能について紹介予定です！
gp5ファイルとハヌマーンとAlphaTab(日記)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4046871/48ae43da-ea90-4a91-a5cd-3594f942503a.png)



ハヌマーンというバンドが好きで、彼らのTAB譜を探しているうちに、譜面データからそのまま楽曲を再生できるWebサイトを見つけました。調べてみると、そのサイトはGitHubで公開されているリポジトリを基に構築されており、譜面ファイルには `.gp5`（Guitar Pro 5形式）が使用されていることが分かりました。

この形式のファイルは、**Guitar Pro** や **TuxGuitar** といったソフトで開くことができ、さらにMIDI形式に変換することも可能です。
また、譜面再生には **AlphaTab** というJavaScriptライブラリが使われており、ブラウザ上でインタラクティブに演奏を確認できる仕組みになっていました。
時間のあるときに、実際に`.gp5`ファイルをMIDIへ変換し、Pythonで音楽データとして扱ってみようと思っています。


以下、記録用メモ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4046871/ff1c0529-e224-4484-993b-5b4b7e725ebb.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4046871/5c51b595-a86a-4c35-841f-1bcf11d25655.png)


https://tabs.morishin.me/player/%E3%83%88%E3%83%A9%E3%83%99%E3%83%AB%E3%83%97%E3%83%A9%E3%83%B3%E3%83%8A%E3%83%BC/



https://tabs.morishin.me/


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4046871/847b0ad5-4aa8-4570-849c-0b79248e8833.png)



https://github.com/morishin/TAB/tree/2c163b1d23574786e47093617ad69ab7dd4e18c4


https://github.com/CoderLine/alphaTab
Javaで文字列⇔バイト配列の変換を行う際は、Charset.forName使った方がいい

Javaで文字列⇔バイト配列の変換を行う際に、検査例外のUnsupportedEncodingExceptionの対応として、基本的例外発生しないのにtry-catchかかないといけなくなる。

それを防ぐ書き方がある。
Charset.forNameを使うのがよい

文字コードの指定にCharset型を使用すると、UnsupportedEncodingExceptionが発生しなくなる

例)
```java
	String str = "テスト";

    // UTF-8
	byte[] utf8Bytes = str.getBytes(StandardCharsets.UTF_8);

    // windows-31j(MS932)
	byte[] sjisBytes = str.getBytes(Charset.forName("windows-31j"));
```
🚀 Vite × React × TypeScript × Tailwind CSS を Vercel にデプロイする手順


## 📦 プロジェクト構成

- フロントエンド: **React (v19)**
- バンドラ: **Vite**
- 言語: **TypeScript**
- スタイリング: **Tailwind CSS**
- ルーティング: **React Router v7**
- デプロイ先: **Vercel**

---

## 🔧 セットアップ手順

### 1. プロジェクト作成

```bash
npm create vite@latest my-app -- --template react-ts
cd my-app
``` 
### 2. パッケージインストール

```bash
npm install
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
``` 
### 3. tailwind.config.js 設定
```js
/** @type {import('tailwindcss').Config} */
export default {
  content: [
    "./index.html",
    "./src/**/*.{js,ts,jsx,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
};
```
### 4. index.css に Tailwind を読み込む
```css
@tailwind base;
@tailwind components;
@tailwind utilities;
```
## 📁 ディレクトリ構成
```pgsql
my-app/
├── public/
├── src/
│   ├── pages/
│   │   └── Home.tsx
│   ├── routes/
│   │   └── AppRoutes.tsx
│   ├── App.tsx
│   └── main.tsx
├── index.html
├── tailwind.config.js
└── package.json
```

## 🧩 ソースコード
### 🟢 src/pages/Home.tsx
```tsx
const Home = () => {
  return (
    <div className="text-center mt-10">
      <h1 className="text-3xl font-bold">ようこそ！</h1>
      <p className="mt-4">これはReact + Vite + Tailwind CSSのテンプレートです。</p>
    </div>
  );
};

export default Home;
```

### 🟢 src/routes/AppRoutes.tsx
```tsx
import { BrowserRouter, Routes, Route } from "react-router-dom";
import Home from "../pages/Home";

const AppRoutes = () => {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<Home />} />
      </Routes>
    </BrowserRouter>
  );
};

export default AppRoutes;
```
### 🟢 src/App.tsx
```tsx
import AppRoutes from "./routes/AppRoutes";

const App = () => {
  return <AppRoutes />;
};

export default App;
```
### 🟢 src/main.tsx
```tsx
import ReactDOM from "react-dom/client";
import App from "./App";
import "./index.css";

ReactDOM.createRoot(document.getElementById("root")!).render(
  <App />
);
```
## 🧼 Vercelデプロイ時にエラーが出た場合の対応
#### ビルド時に @rollup/rollup-linux-x64-gnu のエラーが出る場合：

```bash
rm -rf node_modules package-lock.json
npm install
```
#### ※ PowerShellの場合は：
```powershell
Remove-Item -Recurse -Force node_modules, package-lock.json
npm install
```
## 🚀 Vercelでデプロイする
##### 1.[Vercel公式サイト](https://vercel.com/) にアクセスしてGitHub連携
##### 2.このプロジェクトのリポジトリを選択
##### 3.ビルドコマンド: ```npm run build ```
##### 4.Output Directory:``` dist ```
##### 5.デプロイ完了！

## ✅ 補足
- ##### ``` vite.config.ts ``` は ``` @vitejs/plugin-react ``` を使って自動構成されるため編集不要

- ##### TailwindのJITが有効なので高速リロード可能

- ##### .env による環境変数管理もVercelと相性良し
【初心者向け】React + TypeScript + Tailwind CSS プロジェクトを GitHub & Vercel に公開する手順

## 1. プロジェクトの準備

すでに以下の環境が整っている前提です：

- React + TypeScript プロジェクト（例: `create-react-app` または `Next.js`）
- Tailwind CSS の導入済み
- VSCode を使用

---

## 2. Git 初期設定（1回だけ）

```bash
git config --global user.name "あなたの名前"
git config --global user.email "あなたのメールアドレス"
```

## 3. Git 初期化と初回コミット

```bash
cd プロジェクトのフォルダ
git init
git add .
git commit -m "chore: initial commit"
```

## 4. GitHub リポジトリと接続
##### GitHub で新しいリポジトリを作成し、以下のように接続：

```bash
git remote add origin https://github.com/ユーザー名/リポジトリ名.git
```
##### すでに origin が存在する場合は：
```bash
git remote set-url origin https://github.com/ユーザー名/リポジトリ名.git
```

## 5. GitHub へ初回プッシュ
```bash
git push -u origin main
```

### エラー対応まとめ
##### ❌ ```403 permission denied```

```bash
gh auth login
```

##### ❌ ```rejected (non-fast-forward)```

```bash
git pull origin main --allow-unrelated-histories
```

## 6. マージコンフリクト解決方法

##### 1.対象のファイルを開く（例: ```.gitignore```）
##### 2.以下のような記号をすべて削除：

```markdown
<<<<<<< HEAD
（ローカルの内容）
=======
（リモートの内容）
>>>>>>> origin/main
```

##### 3.残したい内容に整理して保存
###### 4.解決後、以下を実行：

```bash
git add .
git commit -m "fix: resolve merge conflict"
git push -u origin main
```

## 7. Vercel にデプロイ

##### 1.https://vercel.com にアクセスし GitHub アカウントでログイン

##### 2.「New Project」→ GitHub から対象リポジトリを選択

##### 3.自動設定のままでデプロイ（Next.jsならそのままでOK）

##### 4.公開URLが発行され、サイトが表示される

## よく使うコミットメッセージ例

```bash
git commit -m "chore: initial commit"
git commit -m "fix: resolve merge conflict"
```

## フォルダ構成の注意点

- ##### pages/ や components/ フォルダを作成して整理（Next.js）

- ##### 不要なファイルは削除OK（例: README.old.mdなど）

- ##### ! マークが付いたファイルはコンフリクトしている可能性あり

## まとめ
| ステップ | 内容                        | 
| -------- | --------------------------- | 
| 1        | Git 初期設定                | 
| 2        | Git 初期化とコミット        | 
| 3        | GitHub リポジトリ作成と接続 | 
| 4        | GitHub へプッシュ           | 
| 5        | マージコンフリクト解決      | 
| 6        | Vercel へデプロイ           | 
| 7        | Webサイト公開完了！         |
C_TS452_2021 - SAP Certified Application Associate - SAP S/4HANA Sourcing and Procurement対策（24/100）

【24日目】
トピック3：調達プロセス（5/13）

≪問題24≫
外注調達プロセスの機能は次のうちどれですか。
（３つ選択）
▢部品表/配合表に支給される構成品目を更新できる。
▢外注発注明細の非評価入庫を転記できる。
▢外注明細カテゴリの制御パラメータをカスタマイズできる。
▢外注発注明細を勘定に割り当てることができる。
▢MRPによって外注購買依頼が登録されるようにできる。

≪解答≫
█部品表/配合表に支給される構成品目を更新できる。
▢外注発注明細の非評価入庫を転記できる。
▢外注明細カテゴリの制御パラメータをカスタマイズできる。
█外注発注明細を勘定に割り当てることができる。
█MRPによって外注購買依頼が登録されるようにできる。

≪解説≫  
◆外注加工では、外部の仕入先に品目を発注しますが、通常の外部調達プロセスとは異なり、製品の製造に必要な構成品目を外注先が使用できるように準備しておく必要があります。
◆外注プロセスの主な特徴は以下のとおりです。
（1）外注発注により最終製品を調達します。この発注には、外注先に支給する構成品目に関する情報も含まれます。
（2）在庫管理では、構成品目を外注先に納入する際に「仕入先支給品」として転記します。必要に応じて、外注コックピットから出荷伝票を登録することも可能です。また、構成品目を別の仕入先から直接外注先に納入することもできます。
（3）外注先は、サービスの提供および製造または加工済みの品目を納入します。最終製品の入庫を在庫管理に転記すると、使用された構成品目は仕入先支給品から差し引かれます。
（4）入庫後に外注先から構成品目の過剰使用または不足使用が報告された場合は、後続の調整処理により入庫を修正できます。
（5）外注先からサービスに対する請求が行われた場合は、ロジスティクス請求書照合機能で請求書を登録します。
◆購買伝票での外注明細は、特別な明細カテゴリ「L」によって表されます。各外注明細には、支給する構成品目について1件以上の副明細を登録する必要があります。発注品目にBOM（部品表/配合表）が設定されている場合は、そこに含まれる構成品目を外注明細にコピーできます。構成品目を手動で追加することも可能です。この明細では、品目コードと勘定設定が使用でき、構成品目の指定と入庫の登録が必須となります（非評価入庫は不可）。また、請求書の受領処理も可能です。
◆購買発注価格は、仕入先による外注作業および外注品目に対する価格に基づきます。価格条件は外注用の購買情報にて定義できます。
◆MRP実行時に外注の購買依頼を自動生成するための前提条件は次のとおりです。
（1）品目マスタ（MRP2ビュー）にて、特殊調達キー「外注」が設定されていること。
（2）供給元（外注購買情報、または明細カテゴリ「外注」が設定された購買契約明細）が1件以上存在していること。
（3）供給元一覧にて、いずれかの供給元にMRP関連区分が設定されていること。
（4）支給品としての構成品目を含むBOMが存在すること。
◆明細カテゴリの設定はSAPシステム内の標準テーブルにより管理されており、ユーザによる変更はできません
【バッチ】任意のファイルを圧縮(ワイルドカードを使用して複数指定) 


```
@echo off

set FILE=*.csv
set ZIP=%userprofile%\Desktop\%computername%_info.zip

powershell Compress-Archive -Path "%FILE%" -DestinationPath "%ZIP%"
echo 圧縮完了: %ZIP%
```
「Cannot read properties of undefined (reading 'reduce')」エラーの原因と解決

## はじめに
今回はReact × Viteでアプリケーション開発中に `Cannot read properties of undefined (reading 'reduce')` というエラーがブラウザ上で発生したので、解決方法をまとめます。

## 症状
Reactアプリケーションで、ブラウザ(Google Chrome)のコンソール上に以下のエラーが表示されました。
```console
[vite] TypeError: Cannot read properties of undefined (reading 'reduce')
    at optionMatcher (cli-options.js:4:15)
    at config.js:6:7
    at config.js:9:1
```
:::note warn
このエラーは、あるオブジェクトが`undefined`なのに対して`reduce`メソッドを呼び出そうとしていることを意味します。
:::

## 発生原因
最初は `supabase` 上のテーブルにデータを登録する関数の引数に`undefined`の値が含まれているのが原因ではないかと疑い、以下のように関数の呼び出し元で変換処理を追記しました。
```tsx:register.tsx
// 登録
const onSubmitUser = async (data: UserForm) => {
    try {
      const formData = {
        ...data,
        // 🔵未定義の場合はnullに変換する処理を追記
        githubId: data.githubId || null,
        qiitaId: data.qiitaId || null,
        xId: data.xId || null,
      };

      // supabaseへの登録処理
      const result = await insertUser(formData);
      if (!result) {
        console.error('ユーザー登録に失敗しました');
      }
      // 登録成功後にトップページへ遷移
      navigate('/'); 
    } catch (error) {
      console.error('Submit error:', error);
    }
  };
```

しかし、エラーは解消されませんでした。
改めて `insertUser` 関数を定義しているファイルを確認したところ、以下のインポートが含まれていることに気づきました。
```ts:user.ts
import { User } from '../domain/user';
import 'dotenv/config'; // 🔵ココが原因
import { supabase } from '../utils/supabase';
import { UserForm } from '../domain/interfaces/userForm';
```

`dotenv/config` は `Node.js` の実行環境でのみ使えるものです。
`Vite` はクライアントサイドの開発ツールなので、ブラウザ環境ではこのインポートは解決できず、未定義の状態が生まれてしまっていた、というのが原因でした。

## 解決策
ブラウザ環境で動作するコード（Viteで実行される部分）からdotenv/configのインポートを削除することで、エラーは解消されました。
```ts:user.ts
import { User } from '../domain/user';
// 'dotenv/config'のインポートを削除
import { supabase } from '../utils/supabase';
import { UserForm } from '../domain/interfaces/userForm';
```
## 終わりに
このエラーは、一見オブジェクトの変換処理の不備などが原因に見えましたが、実際には「Node.js用のコードをフロントエンドに含めてしまった」ことが根本原因でした。
`Vite` では、クライアントとサーバーのコードの境界に注意したいと思います。
打刻アプリにおける代返対策をやってみた話

こんにちは。現在、私はある**イベント出席用の打刻アプリ**のサーバサイド開発・保守を担当しています。この記事では、近年増えてきた「代返行為（＝出席の不正代行）」を防ぐために行った対策について、背景から実装、そして結果までを紹介します。

---

## 背景：なぜ代返が問題なのか？

この打刻アプリは、イベントの出席確認を目的に使用されています。仕組みとしては、

1. イベント会場に設置されたビーコンの信号をスマホ端末が受信
2. スマホが「位置情報」と「打刻時刻」をサーバに送信
3. サーバが打刻情報を記録

といった流れです。

ところが最近、**代返**（＝別人が他人のアカウントで打刻する行為）が増加してきました。主な手口は次のようなものです：

> イベントに出席している学生が、出席していない知人のアカウントに切り替えて、代理で打刻する。

---

## 対策方針：「1端末1アカウント」の制限

代返を防ぐには、「**1台の端末に1アカウントしかログインできない**」ようにすれば良いと考えました。この方針に基づいて、次の2案を検討・実装しました。

---

## 案1：サーバ側で端末識別子を検証（主にAndroid向け）

### 方法

1. 端末から送られてくるリクエストに「端末識別子（identifier）」を追加してもらう。
2. サーバは、その識別子とユーザーIDのペアをRedisに保存。
3. 打刻時に、同じ識別子で過去に記録されたユーザーIDと異なる場合、**切り替えが発生した**と判断。

### 技術的な補足

- Androidでは**Widevine ID**という端末固有の識別子を利用できます。
- Redisに有効期限（TTL）を1週間設定することで、「中古スマホを譲渡した場合」などにも対応可能です。
- **Widevine ID**はセンシティブな情報なので、ヘッダーで送信したほうが無難です。

---

## 案2：クライアント（iOS）側で切り替え検知

### 方法

1. 初回ログイン時に、ユーザーIDと日時を端末内に保存（localStorageなど）。
2. 打刻時には保存されたユーザーIDと照合し、異なる場合は打刻を送信しない。

### 技術的な補足

- iOSは、アプリをアンインストールしても削除されない領域（Keychainなど）を使うことで、情報を持続的に保持できます。
- Androidはユーザー側で比較的簡単にlocalStorageのデータを削除できてしまうため、この方法は不向きです。

---

## 実装概要

### 条件

以下のユースケースも想定して、対策を設計しました：

- **複数端末でログインしたい人**：端末識別子ごとにユーザーIDを記録すればOK
- **スマホを譲渡された人**：1週間以上ログインや打刻操作がなければ、切り替えを許可

---

## サーバ側のコード（TypeScript + Redis）

### 1. 打刻時に識別子とユーザーIDを紐づける

```ts
public async updateAccessInfo(identifier: string | string[] | undefined, userObjectId: string): Promise<void> {
  if (!this.isIdentifierValid(identifier)) return;

  const identifierString = identifier as string;
  const currentUserObjectId = await this.redisService.getUserIdByIdentifier(identifierString);

  if (currentUserObjectId && currentUserObjectId !== userObjectId) {
    console.warn('アカウント切り替え検出:', { identifier, userObjectId, currentUserObjectId });
    return;
  }

  // 一週間（604800秒）の有効期限付きで保存
  await this.redisService.updateIdentifierValue(identifierString, userObjectId);
}
```

### 2. ログイン時に切り替えをチェック

```ts
public async checkAccessInfo(
  identifier: string | string[] | undefined,
  userObjectId: string,
): Promise<{ result: boolean }> {
  const identifierString = identifier as string;

  try {
    const currentUserObjectId = await this.redisService.getUserObjectIdByIdentifier(identifierString);
    if (!currentUserObjectId) return { result: true }; // 初回や期限切れならOK

    return { result: currentUserObjectId === userObjectId };
  } catch (error) {
    console.error('Redis query failed:', error);
    return { result: true }; // Redisが落ちていても通す（誤検出防止）
  }
}
```

---

## iOS側の挙動

- ローカルに「最初にログインしたユーザーID」と「日付」を保存
- 打刻時に照合。異なる場合、日付が1週間以上前なら上書き。それ以外は拒否

---

## 結果と感想

今のところは、**代返の抑止に一定の効果が出ており、安定して動作しています**。  
ただ、私自身もイベントに参加していて、**もう自分の代返はできなくなりました**（当たり前ですが…）。

---

## 最後に

今回の対策は、「完璧な防止策」ではありません。ですが、**代返のハードルを上げる**ことで、不正を大幅に減らすことはできます。

「不正に厳しくしつつ、正規ユーザーにはできる限り不便をかけない」そんなバランスを目指して、今後も改善を続けていきます。
B+木と自由文脈文法

B+木で自由文脈文法を構築する企画。その規則は最頻出の2語を1語に置換しまくるという事で生成(RePair風)。規則はある法則で拡張していく。[この記事](https://qiita.com/mashuel/items/913364a107bec7fb45a9)の動作原理を視覚的に理解して頂ける…はず。

<p class=codepen data-height="500" data-default-tab="html,result" data-slug-hash="jEOgdEE" data-pen-title="Context Free Grammar by B+Tree" data-user="xezz" style="height:300px;box-sizing:border-box;display:flex;align-items:center;justify-content:center;border:2px solid;margin:1em 0;padding:1em"><span>See the Pen <a href="https://codepen.io/xezz/pen/jEOgdEE">Context Free Grammar by B+Tree</a> by xezz (<a href="https://codepen.io/xezz">@xezz</a>) on <a href="https://codepen.io">CodePen</a>.</span></p><script async src="https://public.codepenassets.com/embed/index.js"></script>

## 参考文献
[LittleBit](https://github.com/kapenga/LittleBit)
【PowerShell】既定のプログラムの状態をログに出力する

```
# ファイル名: Export-DefaultPrograms.ps1

$pcName = $env:COMPUTERNAME
$logPath = "$pcName`_default.log"
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
Add-Content $logPath "`n[$timestamp] 既定のプログラム一覧の出力開始"

# レジストリキーを取得
$extsPath = 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts'
$exts = Get-ChildItem $extsPath

foreach ($ext in $exts) {
    $userChoicePath = "$extsPath\$($ext.PSChildName)\UserChoice"
    if (Test-Path $userChoicePath) {
        $progId = (Get-ItemProperty $userChoicePath).ProgId
        Add-Content $logPath "$($ext.PSChildName) => $progId"
    }
}

$timestampEnd = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
Add-Content $logPath "[$timestampEnd] 出力完了"
```
Cursorで「Cmd + クリック」を使って定義先へ移動する設定方法

Cursorではデフォルトの設定と異なり、多くのIDEのように「Cmd（またはCtrl）＋クリック」で定義先へジャンプできるよう設定を変更できます。

この記事では、Cursorのキーボードショートカット設定（`keybindings.json`）を編集して、`Cmd + クリック`で定義先へジャンプする方法を紹介します。

# 設定方法

## 手順1: `keybindings.json`を開く

- Cursorを起動します。
- コマンドパレットを開きます（`Cmd + Shift + P`）。
- 「Preferences: Open Keyboard Shortcuts (JSON)」を選択します。

## 手順2: ショートカット設定を追加する

開いた`keybindings.json`ファイルに、以下の設定を追加します。

```json
[
  {
    "key": "cmd+click",
    "command": "goToNextReference",
    "when": "inReferenceSearchEditor || referenceSearchVisible"
  },
  {
    "key": "f12",
    "command": "-goToNextReference",
    "when": "inReferenceSearchEditor || referenceSearchVisible"
  }
]
```

- **1つ目の設定**: `cmd+click`で定義先にジャンプする動作を有効化します。
- **2つ目の設定**: デフォルトの`F12`の割り当てを解除します（任意）。

## 手順3: 設定を保存して確認

- 設定ファイルを保存します。
- エディタ上で定義先へ移動したい変数や関数などにカーソルを合わせ、「Cmd + クリック」します。
- 定義先へジャンプすることを確認します。
ChatVRMをVoiceVoxに対応させる+α



# 概要
LLMによるエージェントとの音声対話を手軽に試せる[ChatVRM](https://github.com/pixiv/ChatVRM)をフォークして、[自分好みに改変したコード](https://github.com/jiroshimaya/ChatVRM/tree/v0.0.7)を公開しました。

記事執筆時点（v0.0.7）で以下の変更をしています。

- 音声合成エンジンをkoeiromapからVoiceVoxにした
- OpenAIのモデル、APIキー、VoiceVoxのホストなどを環境変数で設定可能にした
- 環境変数がフロントに露出しないように、OpenAI、VoiceVoxの呼び出しをバックエンドにした


備忘録として、変更の背景やコードの変更点などをメモします。

なおTypeScriptを書くのは今回が初めてのため、間違いが含まれるかもしれません。
なにかお気づきの点があればご指摘いただけると幸いです。

# 背景
ChatVRMはLLMを用いたVRMキャラクターとの音声対話を気軽に試せるプログラムです。
2024年7月にpublic archiveとなっています。

ChatVRMでは、LLMとしてOpenAI、音声合成エンジンとしてkoeiromapが使用されています。
また、設定画面からAPIキーやプロンプトなどを登録できます。

このままでも使えはするのですが、自環境で繰り返し使う可能性を考慮して、
音声合成エンジンは使い慣れたVoiceVoxに変更し、APIキーやモデルなどはUIからではなく環境変数から設定できるようにすることにしました。
合わせてAPIキーなどがフロントに露出することを避けるため、LLMや音声合成の処理をバックエンドで実施することにしました。

なおChatVRMをベースにしつつ多様なLLMや音声合成エンジンを利用できるように拡張したリポジトリはいくつかあるのですが（e.g. [aituber-kit](https://github.com/tegnike/aituber-kit)（v1はMITライセンス））、コードベースを理解するのに時間がかかりそうだったため、今回はChatVRMからスタートすることにしました。

# 実装
詳細はリポジトリをご確認ください。
要点だけ簡単に記載します。

## 音声合成エンジンをVoiceVoxに
ChatVRMではもともと音声合成エンジンとしてkoeiromapが使われていましたが、API利用料などを気にせずに試したかったのと、他のプロジェクトでも使用経験があったことなどから、VoiceVoxに変更することにしました。

## APIキーや各種設定を環境変数で行えるように
ChatVRMでは、APIキーを用いたLLMや音声合成の呼び出しがフロントエンドのみで行われます。
デモページでユーザが自身のAPIキーを使って安心して試せるようにそうなっていると思われますが、逆に開発者が自身のAPIキーを登録したアプリを公開しようと思うと、APIキーがフロントに露出してしまい不便です。

そこで、LLMや音声合成を呼び出すバックエンドの処理を実装し、APIキーも環境変数からバックエンド側でのみ読み取るようにしました。
ChatVRMにはもともとsrc/pages/apiにバックエンドでLLMを呼び出す実装が存在するのですが、ストリーミングに対応していないなど、そのまま使いやすい感じではなかったので、新規に実装しました。
OpenAIの呼び出しはvercelのai-sdkを、voicevoxの呼び出しはvoicevox-clientを使用しました。

OpenAIのモデル名などがハードコーディングされていたので、APIキーなどと同様に、環境変数に記載できるようにしました。

ルーティング方式を従来のpages routerから新しい方式であるapp routerに変更しました。

jestを用いて、LLMや音声合成の呼び出しなどをマニュアルテストできるようにしました。



# おわりに
とりあえずVoiceVoxで喋るようになったので良かったです。
LLMや音声合成は現状1種類しか使えませんが、ある程度抽象化したので、必要に応じて増やそうと思います。
OCI学習#5 ネットワークパスアナライザ

## 概要
OCI Architect Associate 2024資格取得に向けて学習を進めているが、
実際に触ったほうが理解も進み、いざ構築する際に手が止まらなくなると考えたため
OCIチュートリアルを参考に操作感を覚える。
その際の手順等を見返せるように、簡単に記す。

## OCI学習記事まとめ
今回の一連の記事を下記にまとめます。
1.[OCI学習#1 ネットワーク設定](https://qiita.com/nonZy/items/adbbf47646a28e59fe36)
予算設定、コンパートメント作成、VCN、IGW、ルート表、セキュリティリスト作成。
2.[OCI学習#2 インスタンス作成、接続](https://qiita.com/nonZy/items/b821d419c03ca7c7cd7c)
インスタンス作成、SSH接続
3.[OCI学習#3 Block Volume追加](https://qiita.com/nonZy/items/5053c853c77cfb68bc51)
Block Volumeの作成、アタッチ、OSでのパーティション設定、ファイルシステム作成、マウントまで
4.[OCI学習#4 Object設定、アクセス](https://qiita.com/nonZy/items/abb5717666d8f74eb690)
ObjectStorageにおいてのバケット作成、オブジェクトアップロード、オブジェクトへのアクセス

## ゴール
[入門編 - Oracle Cloud Infrastructure を使ってみよう](https://oracle-japan.github.io/ocitutorials/beginners/)
チュートリアルを参考に、下記を実施して操作感を覚える。
1.クライアントからパブリックサブネットのインスタンスSV01へ接続。
2.インスタンスSV01からプライベートサブネットのインスタンスSV02へ接続。
3.SV02へBlock Volumeを追加、マウント。
4.クライアントからObject StorageのObjectにアクセス。
![edu02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/510919/c45ec428-e355-4c9b-887e-20550417f585.png)

## 本記事の内容
"2.OCI学習#2 インスタンス作成、接続"において、VM01からVM02へSSH接続ができるかを
ネットワークパスアナライザーを使用して確認を行う。

## 手順
### 分析の実行
1.[ネットワーキング]-[ネットワーク・コマンド・センター]-[ネットワーク・パス・アナライザ]をクリック
2.[パス分析の作成]をクリック
![51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/510919/677d6274-67f7-4a60-a199-ee2817c70b1c.png)

VM01からVM02へのSSH接続が可能かを分析するために下記を設定。ついでに戻りも確認。
ソース：VM01
デスト：VM02
![52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/510919/cf1db337-079f-498a-9aed-908eb8f1ede1.png)
![53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/510919/4a580b44-260b-47bd-b380-2fceb6cef7b5.png)

・結果
行き：OK
戻り：OK
この後、OS側で秘密鍵の権限変更を忘れてSSH接続できませんでしたが、
ルート表やセキュリティリストでの設定は問題ないことが分かりました。
![54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/510919/ba2d6e69-383a-412f-bd41-cd19eeda5ccc.png)

## まとめ
次はNGにして確認を行います。
【Flutter】MediaQueryを使ったらgo_routerが無限リダイレクト？その原因と回避方法


Flutterで `go_router` を使っているとき、**iOSでキーボードを表示すると無限リダイレクトが発生する**現象に遭遇しました。

この記事ではその原因と、どのように解決したかを共有します！

## 🔥 問題の現象

```dart
theme: ThemeData(
  navigationBarTheme: NavigationBarThemeData(
    height: MediaQuery.of(context).size.height * 0.07,
  ),
),
```

このように `MediaQuery` を `ThemeData` 内で使用していたところ、

- iOSでキーボードを開いた瞬間  
- `go_router` の `redirect:` が毎回実行される  
- 無限にリダイレクトが走り、ルーティングが止まる

という現象が発生しました。


## 🧠 なぜ起きたのか？

### 原因は「MediaQueryによるbuildの再実行」でした。

Flutterでは、`MediaQuery`, `Theme`, `Directionality` などの「InheritedWidget」に依存している場合、  
**その値が変わると自動的にWidgetツリーが再構築されます**。

> `MediaQuery` はキーボード表示・非表示などで `size` や `viewInsets` が変化するため、  
> `build()` が再実行されるトリガーになります。

そして、`MaterialApp.router()` の中で `GoRouter(...)` を初期化していたため、  
**`redirect:` が毎回再評価 → 状況によっては無限ループ**が起こっていたわけです。


## 🛠 解決策：再構築の影響を最小限に抑える

### ✅ 解決策1：MediaQueryの使用箇所を下層に移す

`MediaQuery` は **ThemeDataやGoRouterの初期化部分では使わず**、  
実際に必要なWidget内で使うようにしましょう。

```dart
NavigationBar(
  height: MediaQuery.of(context).size.height * 0.07,
)
```

→ こうすれば、キーボードなどによる画面サイズの変化が発生しても、  
NavigationBarだけが再構築され、`GoRouter` や `ThemeData` には影響しません。

---

### ✅ 解決策2：MediaQueryの値をキャッシュする

初回だけMediaQueryのサイズを取得して保存しておけば、  
再構築のたびに毎回取得し直す必要はありません。

```dart
double? _screenHeight;

@override
void didChangeDependencies() {
  super.didChangeDependencies();
  _screenHeight ??= MediaQuery.of(context).size.height;
}
```

---

### ✅ 解決策3：再構築を検知して明示的に制御する

Flutterの `WidgetsBindingObserver` を使えば、  
画面サイズやキーボード表示の変化をイベントとして検知できます。

```dart
class _MyState extends State<MyPage> with WidgetsBindingObserver {
  double _keyboardHeight = 0;

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }

  @override
  void didChangeMetrics() {
    final bottomInset = WidgetsBinding.instance.window.viewInsets.bottom;
    setState(() {
      _keyboardHeight = bottomInset;
    });
  }
}
```

---

## ✅ まとめ

| ポイント | 説明 |
|----------|------|
| `MediaQuery`はInheritedWidget | 値が変わると依存Widgetの`build()`が再実行される |
| `GoRouter`などの初期化に影響を与えると危険 | `redirect:` が毎回評価され無限ループになる |
| **MediaQueryは必要な場所で、必要なタイミングだけ使おう** | これが一番の安定策！ |

---

## 💬 おわりに

レスポンシブ対応のために `MediaQuery` を使うのは正しいアプローチです。  
ただし、それを「どこで使うか」で挙動が大きく変わる点には注意が必要！

同じようなハマりポイントに遭遇した方の参考になれば幸いです 🙌

---

🧑‍💻 ご質問やご意見があれば、コメントでお気軽にどうぞ！
UE5勉強日記2025/04/13 mouse指す場所の座標取得

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3574567/f625410f-a797-43f5-bde3-4f07831ec05c.png)
上記BlueprintのBreak hit ResultのLoctionはmouse指す場所の座標です。

説明

まず、Convert Mouse Location To World Space出力するworld location,World Directionについて説明
world locationはゲーム世界中のmouseの座標
World DirectionはMouse指す方向

次、Line Trace For Objectは特定の方向にレイ（光線）を飛ばし、衝突したオブジェクトを検出するもの
startはmouseの座標を設定しました
endはmouse指す方向の単位ベクトル　*　Distanceを設定しました
※Distanceの初期値困る場合は、int型　4000を設定してください。
Object Typeはworld static

それで、Mouseからmouse指す方向　4000距離以内にworld staticが存在したら、hit resultを返す
そのhit resultはmouse指す場所の座標です。
【Excel】関数_基本１　メモ


## 日付データから開始日と終了日を抽出する
````ruby:1番古い日付を表示
=MIN(A1:A10)

※日付表示にする場合は↓
=TEXT(MIN(A1:A10), "yyyy/mm/dd")
````
````ruby:1番新しい日付を表示
=MAX(A1:A10)

※日付表示にする場合は↓
=TEXT(MAX(A1:A10), "yyyy/mm/dd")
````
日付を表すシリアルデータは日付が古いほど値が小さいため、開始日（古データ）はmin、直近の新しいデータはmaxで抽出。
Tailwind CSS適用時にh1タグなどが適用されなくなる件について

# Tailwind CSSで`h1`や`h2`にスタイルを適用する方法

## 使用開発環境
- Vite 5.0.0
- React 18.2.0
- Tailwind CSS 3.2.1
- @tailwindcss/forms 0.5.3
- @inertiajs/react 1.0.0
- laravel-vite-plugin 1.0.0

※React環境でやってますが他の環境でもできることは確認済みです。

## 問題
- デフォルトのTailwindは`preflight`という初期リセットCSSを自動で適用する。
- そのせいで、`h1`や`h2`のデフォルトスタイル（太字、大きさなど）がリセットされてしまう。

## 解決方法
### 1. `tailwind.config.js` に以下を追加

```tailwind.config.js
export default {
  corePlugins: {
    preflight: false, // Preflightを無効化する
  },
}
```


### 2. その後、ビルドする

```bash
npm run build
```

またはリアルタイム監視なら

```bash
npm run watch
```

### 3. これで `h1`, `h2` タグなどにブラウザ標準のスタイルがそのまま適用される！
「Batch Normalization」について簡単にまとめる

# バッチ正規化 (Batch Normalization)
「Transformer」を学ぶための「Layer Normalization」を学ぶための「Batch Normalization」についてまとめていきます。
## 1. バッチ正規化とは
端的に言えばミニバッチ内の全データに対してチャンネル方向に正規化する手法。  
## 2. 利点
- **学習の高速化**
データが正規化されるため各層で安定した出力が得られる。これにより最適化アルゴリズムで局所最適解を避けることがき、学習を効率的に行えるような高い学習率を設定することが可能になる。

&#13;&#10;
 - **初期化の柔軟性**
バッチ正規化によりパラメータ初期化の依存度を減らせる。

&#13;&#10;
 - **正則化**
データを正規化させることにより過学習を防げる。原論文ではドロップアウトの必要性を低減させられるともある。

他にも**内部共変量シフトの低減**や**勾配消失問題の緩和**などがあげられる

## 3. 欠点
- **ミニバッチのサイズへの依存性**
バッチサイズを小さく設定すると正規化によって出力される統計量にノイズが多く含まれる可能性がある。これにより、訓練が不安定になる場合がある。

&#13;&#10;
- **訓練時と推論時とのギャップ**
訓練時はミニバッチの統計(平均値、分散)を使用して正規化を行うが、推論時は全体の統計を使用する。データの分布が訓練時と推論時によって大きく異なる場合、正規化に使用される統計も大きな差が発生し、期待しない動作になることがある。

&#13;&#10;
- **正規化の計算コスト**
正規化層をモデルに含めることで追加の計算コストがかかる。ネットワークが深くなるほどそのコストも大きくなる。

&#13;&#10;
- **データ数が少ない場合での正規化**
訓練データ少ないときはバッチ正規化の恩恵が制限され、逆に過学習を起こす可能性がある。

<br/>

バッチ正規化の利点を生かすためには、**十分な量のデータセットを用意**し、**小さすぎない最適なバッチサイズを選択する**ことが最も効果的かと思う。
<br/>
## 4.「Batch Normalization」の原論文

https://arxiv.org/pdf/1502.03167v3
【AI-900対策】責任ある AI（Responsible AI）の6つの原則をやさしく解説！

Microsoft Learn「AI の基本的な概念」より、**責任ある AI（Responsible AI）の6つの原則**を、わかりやすく解説します！

https://learn.microsoft.com/ja-jp/training/modules/get-started-ai-fundamentals/

---

## 1. 公平性（Fairness）

### 意味：
AIが人種・性別・年齢などによって不公平な判断をしないようにすること。

### たとえ：
> AIが面接官だとして、「性別や年齢で不採用」はダメ。  
> みんなを平等に評価できるAIが理想！

---

## 2. 信頼性と安全性（Reliability and Safety）

### 意味：
AIが安定して安全に動作し、予期せぬミスをしないようにすること。

### たとえ：
> 自動運転AIが雨の日に事故ったら怖すぎる！  
> どんな状況でも安全に使える信頼感が大事。

---

## 3. プライバシーとセキュリティ（Privacy and Security）

### 意味：
AIが使う個人情報などのデータを、しっかり守ること。

### たとえ：
> AIに秘密を話しても「絶対に口外しない親友」でいてほしい！

---

## 4. 包括性（Inclusiveness）

### 意味：
誰も取り残さない設計で、すべての人がAIの恩恵を受けられるようにする。

### たとえ：
> ハンディキャップのある人も、年齢層が違っても、  
> みんなが使いやすいAIを目指す！

---

## 5. 透明性（Transparency）

### 意味：
AIの判断や動きについて、理由や仕組みを説明できるようにすること。

### たとえ：
> 「なぜこの結果になったの？」って聞いたら、  
> AIが「こういう理由だよ」とちゃんと教えてくれる状態！

---

## 6. 説明責任（Accountability）

### 意味：
AIが問題を起こしたとき、人間側が責任を持てるようにしておくこと。

### たとえ：
> 「AIのせいです〜」で逃げるのはNG。  
> 最終的には人間が責任を取るのが基本！

---

## ゴロ合わせで覚えよう！

**「公信プ包透説（こうしんぷほうとうせつ）」**

- 公：公平性  
- 信：信頼性と安全性  
- プ：プライバシーとセキュリティ  
- 包：包括性  
- 透：透明性  
- 説：説明責任  

---

## まとめ

責任あるAIは「正しいことを、みんなに、安全に、わかりやすく」届けるための基本姿勢です。  
AIを使う私たちも、これを理解しておくことがとっても大切！

---


ちなみにこのブログは私が育てているChat GTPに書いてもらいました！
【Excel】VBA_基本1 メモ

````ruby:シートをコピー
Sub シートコピー()
Worksheets("コピーしたいシート").Copy Before:=Worksheets("挿入位置（指定したシートの前）")
End sub
````
````ruby:シートを削除
Sub シートを削除()
    Worksheets("削除したいシート名").Delete
End Sub
````
JavaScriptの分割代入って？

# はじめに
今までJavaScritpの分割代入という機能を知らなかったので、簡単にまとめます。

# 問題
分割代入の構文ってどうやって書くの？
# 解決方法
**分割代入とは？**

■オブジェクトの場合
従来はこのように「.」で参照する必要があります。
```js
const myProfile = {
  name: 'じゃけえ',
  age: 31,
};

const message1 = `名前は${myProfile.name}です。年齢は${myProfile.age}歳です。`;
```
ですが、分割代入を使用すると
```js
const { name, age } = myProfile;
const message2 = `名前は${name}です。年齢は${age}歳です。`;
```
これだけで済みます◎

■配列の場合
従来はこのように順番を指定してあげる必要があります。
```js
const myProfile = ['じゃけえ', 31];
const message1 = `名前は${myProfile[0]}です。年齢は${myProfile[1]}歳です。`;
```
ですが、分割代入を使用すると
```js
const [name, age] = myProfile;
const message2 = `名前は${name}です。年齢は${age}歳です。`;
```
これで済みます◎
配列の場合は好きな変数を[]に指定してあげる必要があります。
また、格納先の変数には配列の順番通りに格納されるので注意してください。


# おわりに
短くかけるのは便利なので積極的に使用していきましょう！
「検索システム 実務者のための開発改善ガイドブック」を読んだ (第4章まで)

# 第一章 イントロダクション
- 検索における課題は、 **表記揺れ** と **ランキング問題**
- 大規模なWebサイトでは、 **全文検索エンジン** が利用される
    - データベース + 全文検索エンジン
- **全文検索エンジン** は **転置インデックス** を利用
- 検索システムの全体像
    - ユーザーインターフェース
    - クエリプリプロセッサ
    - クエリポストプロセッサ
    - 検索エンジン
        - 転置インデックス利用
    - インデクサ
        - 検索エンジンに登録する機能
    - ドキュメントのデータベース
    - 検索履歴
    - データ活用基盤
    - 適合性コントローラー

# 第二章　検索エンジンの仕組み
- **転置インデックス** は、 tableとしては行に単語、列に出てくる箇所の0,1として表す
- **転置インデックス** は、0の無駄な空間をなくすために、 **ターム辞書**, **ポスティングリスト** を利用
- 転置インデックスを利用した検索の流れ (クエリ処理)
    - テキスト解析
    - 辞書引き
    - ポスティングリスト走査
    - ランキング

# 第三章　テキスト解析
- 英語のテキスト解析は、大文字小文字を区別しない、複数形、三人称単数、現在進行形をまとめてstem変換(ステミング)する
- 日本語のテキスト解析は、 **形態素解析機** もしくは **N-Gram**
    - 形態素解析機は、辞書を利用してトークンにする
    - N-Gramは、x文字ごとにトークンにする
- そのほかの処理として、Unicode正規化、ストップワード、類義語の展開がある

# 第四章 ポスティングリストの走査とランキングのアルゴリズム
- ランキングを考慮しないときは、ポスティングリストを順番に進めていくだけ
- ドキュメントベクトルとクエリベクトルの**コサイン類似度**が「近さ」の指標
- TF-IDFによる重み付のドキュメントベクトル
    - $$weight(t,d) = (1 + log(tf_{t,d}))・log(N/{df_t})$$
    - $weight(t,d)$は、あるタームとあるドキュメントの重み
    - $tf_{t,d}$は、ドキュメント$d$中のターム $t$の出現回数
    - $df_t$は、ターム $t$ が出現するドキュメント数
- ランキングを考慮するときは、TF-IDFを計算して進めていく
- パフォーマンスは、**レイテンシ**と**スループット**を計算する
-
try! Swift Tokyo 2025 登壇記録

## はじめに

先日、開催された [try! Swift Tokyo 2025](https://tryswift.jp/) で、「Graph Art with Charts API – Beyond Data Visualization」というテーマでLT登壇しました。  

この記事では、発表に至るまでの体験や準備の裏側についてご紹介します。これから登壇を目指す方にとって、少しでも参考になれば幸いです。

## プロポーザル応募〜採択まで

今回のプロポーザル募集期間は1月中旬〜1月末、採択通知は2月中旬頃に行われ、全体としてかなりタイトなスケジュールだったと思います。

これまでの try! Swift の内容や英語での登壇を考えるとハードルの高さが気になり、プロポーザルを提出するかどうか迷っていたのが正直なところです。それでも、「出さなければチャンスは掴めない」と思い直し、思い切って提出することに。実際に応募したのは締切の数時間前でした。

今年はワークショップの開催がなく、3日間すべてトークセッションだったこともあり、採択数は例年より多かったそうです。

## テーマ選定

昨年の WWDC2024 のセッションで、Swift Charts がパラメトリック関数に対応したことを知ったときから、実際に試してみたいと考えていました。もともと数学には「関数アート」と呼ばれる分野があり、世界大会が開催されるほど注目されています。そこで、この新しい API を用いて、Swift で関数アートを表現することをテーマに選びました。

![スクリーンショット 2025-04-13 17.57.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2299135/c6bebf47-32dc-44e8-8aa6-161b0c25d840.png)


参考：[Swift Charts: Vectorized and function plots](https://developer.apple.com/videos/play/wwdc2024/10155/)

## スケジュール

スライドの一次提出期限が3月中旬だったため、登壇当日までのスケジュールは以下のように組み立てました。

* 2月末まで  
    * 関数アートの作成  
* 3月中旬まで  
    * スライドの作成  
* 3月末まで  
    * 登壇練習  
* 当日まで  
    * 最終調整

登壇が決まってから、2月中は Charts API を使っていくつかのアートを試作しましたが、アート向けの関数ではなかったため、思い描いたような表現ができずに苦労しました。公式の API ドキュメントもあまり詳しくなかったため、実際にコードを書きながら手探りで進めていきました。

特に、複雑なパラメトリック関数やバラ曲線を LinePlot で描画し、その内側を AreaPlot で塗りつぶすことは、現在の Charts API では簡単ではなく、関数アートの完成には多くの時間を要しました。

## 英語登壇

まずは、登壇用のスクリプト作成から始めました。

スライドをもとに、プロトタイプの原稿を ChatGPT に生成してもらい、それを自分らしい表現に修正しながらブラッシュアップしていきました。

その過程では、実際に声に出して読みながら、話しにくい単語や言い回しを、より自然で言いやすい英語に調整しました。ChatGPT に複数の言い換え案を提案してもらい、その中から話しやすいものを選んで使いました。

完成したスクリプトは [ElevenLabs](https://elevenlabs.io/ja) を使って音声化し、オーバーラッピングで発音やリズムの練習をしました。慣れてきたら、なるべくスクリプトを見ずに話す練習も取り入れました。抑揚については、自分が伝えたいニュアンスに合うよう、自分なりに調整しました。

練習の中で、全体を7分程度で話せることがわかっていたため、本番前日まで繰り返し練習を重ねました。

![GoEsPMaacAAOp1l.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2299135/df6ed5ff-09cb-4a12-bf68-f2e834a00c69.jpeg)


### 英語学習

英語学習は日々時間を確保して取り組んでいますが、上達にはかなり時間がかかっていると感じています。

昨年末までは、英語学習プログラムの [COMAS](https://comasjapan.com/) を利用していましたが、カリキュラムを履修し終えたため、現在は [Native Camp](https://nativecamp.net/) を使って、毎日30分ほどオンラインレッスンを受講しています。

レッスン以外では、レッスン用テキストを使ってオーバーラッピングやシャドーイングの練習もしていますが、抑揚など、まだ課題は多く残っています。

こうしたチャンスがいつ訪れるかわからないからこそ、今後も継続的にトレーニングを続けていきたいと思います。

## さいごに

今回、初めて try! Swift Tokyo に挑戦しましたが、自分の成長にとって非常に貴重な経験となりました。この経験をきっかけに、今後は国際カンファレンスにもプロポーザルを提出して、さらに挑戦を広げていきたいと考えています。

最後に、このような素晴らしいカンファレンスを開催してくださった運営の皆様に、改めて心より感謝申し上げます。

以下が今回の発表動画とスライドです。もしよければぜひご覧ください。

### 🎥 発表動画

<iframe width="560" height="315" src="https://www.youtube.com/embed/9i7TWeu4kz0?si=bSA0cKO-RFFYbsQQ&amp;start=5" 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>

### 📑 スライド

https://speakerdeck.com/akkie76/graph-art-with-charts-api-beyond-data-visualization

### 関連記事

他の登壇者による英語登壇に関する記事もご紹介します。ぜひあわせてご覧ください。

* [AIが変えた英語登壇。活用と実践テクニック](https://zenn.dev/noppe/articles/a7cd2f8787f8c7)
* [初めて英語で登壇するにあたってやったことの振り返り]( https://rockname.hatenablog.com/entry/2025/04/14/first-time-speaking-in-english)
Macのメニューバーで生産性を上げられる無料アプリをまとめてみた

## 1. Folder Hub
### 体験
最近のMacではスクリーンノードがあると思いますが（以下画像の画面の上中央がはげるやつ）、
そのノードの領域にホバーすると指定したフォルダーの中をすぐ見ることができるアプリです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3754242/02e5074f-d7a1-4239-9a1b-2f21dcb32d31.png)

毎回Finderを開くのがめんどくさいのと、たまにFinderを開いてブラウザにドラッグしたい時、ブラウザが裏に隠れたりして不便な時がありますよね。

以下のような操作でPR作成時にも簡単に画像を貼ることができます！

![Screen Recording 2025-04-13 at 20.53.16.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3754242/c0cea7da-37c2-485a-92d6-8819c735660c.gif)

### 無料で使える
無料バージョンではフォルダーを一つしか設定できませんが、私はいろんなフォルダーが設定できると逆に責務が増えて操作が複雑になるのでスクショのフォルダーだけをみにいく用途で使っています。

フォルダーを複数指定したい方は有料バージョンの方が良さそうですね！

### おすすめの設定
![Screenshot 2025-04-13 at 21.06.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3754242/90163f10-94e9-40f2-a78d-46ca9736ab4f.png)

- Menu Bar Icon: チェックしない
私はなるべくメニューバーのアイコンを減らしたいので隠しています。

- Trigger Delay(seconds): 0.2
普段の操作でマウスがノードを通ることはよくあります。
毎回出るとうざいので、0.2秒待ってから表示するようにしています。

- Shortcut系はなし
ChromeやVSCodeと被るショートカットがあるためなしにしています。

### ダウンロードリンク

https://www.finderhub.app/

## 2. Hidden Bar
### 体験
メニューバーで見せたいアイコンだけが見えるようにしてくれます。
メニューバーに要らないアイコンがいっぱい出てると必要なものがすぐ見当たらないため使っています。

アプリを開くとわかりやすい説明の図があります。
ずっとみたいものは右に、`>`でトグルしてみたいものは真ん中に、常に見たくないものは左に入れます。

![Screenshot 2025-04-13 at 21.13.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3754242/577f9beb-02b2-404a-bb25-15c91a11051e.png)

そうすると、普段はみたいものだけ出しておきつつ、ちょっとみたいものはトグルですぐ出せるのでおすすめです。

![Screen Recording 2025-04-13 at 21.11.12.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3754242/4a51fb41-ccb4-417c-87c4-6d15462fb2dd.gif)

### ダウンロードリンク
https://apps.apple.com/jp/app/hidden-bar/id1452453066?l=en-US&mt=12

## 3. Menubar X
### 体験
メニューバーからいろいろなアプリの画面をすぐ出せるようにしてくれるアプリです。
私はYouTube Musicを出せるようにしています。

![Screenshot 2025-04-13 at 21.16.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3754242/f9521421-ab8e-471a-a9ae-acf76d99f22d.png)

他にも対応しているアプリはたくさんあります！

![Screenshot 2025-04-13 at 21.18.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3754242/f24826e9-31c2-4c21-853b-bc8d511b5b97.png)

画面の大きさをいい感じに設定することが微妙に難しいですが、他はとても使いやすいです

### ダウンロードリンク
https://apps.apple.com/jp/app/menubarx-floating-browser/id1575588022?l=en-US&mt=12

## 4. Gitify
GitHubからの通知があるとき、メニューバーで簡単に確認ができるアプリです！
クリックすると以下のような画面が出て、レビューリクエストやマージ状況などがわかります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3754242/c9b60c40-7024-48b1-8fbc-8341e4adbed2.png)

また、通知事項がある時は緑色に変わってくれるので気づきやすくなります。（音も出してくます！）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3754242/d9ce1188-ce05-4b78-9b35-532ff39df63b.png)

### ダウンロードリンク
https://gitify.io/
統計検定準1級・第2章の勉強したところをまとめた

# 累積分布関数
累積分布関数は, 確率変数 $X$ がある値 $x$ 以下を取る確率として以下で表される.

$$
F(x) = P(X \leq x)
$$

$X$ が離散型確率変数であれば以下のように確率関数の和で表される.

$$
F(x) = \sum_{x^{'} \leq x} p(x^{'})
$$

$X$ が連続型確率変数であれば以下のように積分を用いて表される.

$$
F(x) = \int^{x}_{-\infty} f(x^{'}) dx^{'}
$$

また, 微分と積分の関係より $f(x) = F^{'}(x)$ である.

<!-- ------------------------------------- -->
# 同時確率関数

#### 同時確率関数
同時確率関数は, $X,Y$ を 2 つの離散型確率変数とするとき「 $X$ が値 $x$ をとり, $Y$ が値 $y$ をとる確率」として以下で表される.

$$
p(x,y) = P(X = x, Y = y)
$$

#### 2変数の累積分布関数
2変数の累積分布関数は, 離散確率変数 $X$ がある値 $x$ 以下かつ 離散確率変数 $Y$ がある値 $y$ 以下とる確率として以下で表される.

$$
F(x,y) =  P(X \leq x, Y \leq y) = \sum_{x^{'} \leq x, y^{'} \leq y} p(x^{'}, y^{'})
$$

#### 周辺確率関数
同時確率関数 $p(x,y)$ から $X$ のみの確率関数 $p_X(x)$ を得るには, 以下のように, ある値 $x$ における $y$ の和をとればよい.

$$
p_X(x) = \sum_{y} p(x, y)
$$

このような $X$ の分布を周辺分布といい, $p_X(x)$ を周辺確率関数とよぶ.

#### 条件付き確率関数
条件付き確率関数は, $X=x$ が与えられたときに $Y=y$ となる条件付き確率として以下で表される.

$$
p_{Y|X}(y|x) = \frac {p(x, y)} {p_X(x)}
$$


<!-- ------------------------------------- -->
# 同時確率密度関数

#### 同時確率関数
$X,Y$ を連続確率変数とし, $F(x,y) = P(X \leq x, Y \leq y)$ を累積分布関数とする. $X,Y$ の同時確率密度関数 $f(x,y)$ は $F(x,y)$ を $x$ と $y$ でそれぞれ偏微分して以下で表される.

$$
f(x,y) = \frac {\partial^2} {\partial x \partial y} F(x,y)
$$

#### 2変数の累積分布関数
離散確率変数 $X$ がある値 $x$ 以下かつ 離散確率変数 $Y$ がある値 $y$ 以下とる確率として以下で表される.

$$
F(x,y) =  P(X \leq x, Y \leq y) = \int_{-\infty}^{x} \int_{-\infty}^{y} f(x^{'}, y^{'}) dy^{'} dx^{'}
$$

#### 周辺確率関数
$X$ の周辺確率密度関数は, ある値 $x$ において $y$ で積分すればよい.

$$
f_X(x) = \int_{-\infty}^{\infty} f(x, y) dy
$$

#### 条件付き確率関数
$X = x$ が与えられたときに $Y = y$ として以下で表される. 

$$
f_{Y|X}(y|x) = \frac {f(x, y)} {f_X(x)}
$$

<!-- ------------------------------------- -->
# 母関数
### 確率母関数
$X$ の確率関数を $p(x)$ とし, $s$ を任意の実数とするとき, $X$ の確率母関数は以下で表される. なお, 確率母関数は整数値を取る確率変数に主に用いられる.

$$
G(s) = E[s^X] = \sum_{x} s^x p(x)
$$

$X$ の期待値と分散は確率母関数を用いて以下のように表される.

```math
\begin{align}
E[X] &= G^{'}(1) \\
V[X] &= G^{''}(1) + G^{'}(1) - (G^{'}(1))^2
\end{align}
```
### モーメント母関数
モーメント母関数 $m(\theta)$は, 確率母関数において $s = e^{\theta}$ と置いたものであり, 以下で表される. モーメント母関数は連続確率変数の場合に用いられることが多い.

$$
m(\theta) = E[e^{\theta X}] = \sum_{x} e^{\theta x} p(x)
$$

$X$ の期待値と分散はモーtメント母関数を用いて以下のように表される.

```math
\begin{align}
E[X] &= m^{'}(0) \\
V[X] &= m^{''}(0) - (m^{'}(0))^2
\end{align}
```

### 母関数の性質
(1) 確率分布との 1 対 1 対応
(2) 独立な変数の和が母関数の積に対応

(1)から, 確率分布が異なればモーメント母関数も異なる. この性質により, たとえば確率変数 $X$ のモーメント母関数が正規分布のモーメント母関数に等しければ, $X$ の分布は正規分布であると分かる.

(2)から, $X_1, X_2$ を独立な確率変数とし, モーメント母関数をそれぞれ $m_1(\theta), m_2(\theta)$ とすれば, $X_1+X_2$ のモーメント母関数 $m(\theta)$ は,

$$
m(\theta) = E[e^{\theta(X_1+X_2)}] = E[e^{\theta X_1} e^{\theta X_2}] = E[e^{\theta X_1}]E[e^{\theta X_2}] = m_1(\theta)m_2(\theta)
$$

となる. このように, 独立な変数の和が母関数の積に対応する.


特に, $X_1,...,X_n$ を独立で同一な分布に従う確率変数とし, $m(\theta)$ を共通のモーメント母関数とすると, $X_1+...+X_n$ のモーメント母関数 $m_n(\theta)$ は

$$
m_n(\theta) = m(\theta)^n
$$

とべき乗の形で得られる.
【Windows】Snipping ToolとPowerPointでキャプチャ動画からGIFを作る

:::note warn
Windowsで実施した記事となります。
:::

# 問題
・アプリを操作しているキャプチャ画面をQiitaにのせようとしていた。
・そのためにキャプチャ動画からGIFを作ろうとしていた。
・オンラインのGIF作成サイトだとなかなかうまくいかずかなり時間がかかった。
（GIFのサイズが合わなかったり、動画の途中までしかGIF化できていないなど）

PowerPointで結構簡単に・早く、動画からGIFを作成できたので、ここでまとめ。
念のため、キャプチャの撮り方から載せています。

## 環境・使ったもの

- Windows 11
- PowerPoint（Microsoft 365 or 2016以降）
- お好きなオンラインGIF圧縮サイト（例：[EZGIF](https://ezgif.com/)、[ILoveIMG](https://www.iloveimg.com/ja/compress-image) など）


## 手順1：Snipping Toolで画面キャプチャ（動画）

1. **Snipping Tool** を起動（Winキー → 「Snipping Tool」と検索）
2. 「録画」アイコン（カメラの隣）を選択し、「＋新規」を押下

　　![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4048214/319b10ef-50b2-4d4e-9a63-0a7188b793b1.png)

3\. キャプチャ範囲を選び、「録画開始」
4\. 終了後、「保存」して `.mp4` ファイルを出力

---

## 手順2：PowerPointに貼り付けてGIF化

1. PowerPointを起動
2. 新規スライドを作成
3. [挿入] → [ビデオ] → [このデバイス] を選択し、先ほどの `.mp4` を挿入
(もちろん、動画のドラッグアンドドロップでも可）
4. [ファイル] → [エクスポート] → [アニメーションGIFの作成] を選択

　![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4048214/70c06864-df8a-42aa-9e77-3536926f57e4.png)

5\. 「GIFを作成」を押下

おすすめ設定：
　- サイズ：ミディアム（中）
　- 「背景を透明にします」にチェック

---

## 手順3：GIFを圧縮

GIFはそのままだと重くなりがちなので、軽量化しておくのがおすすめ。
今回はオンラインサイトで実施。お好きなサイトをどうぞ

- [EZGIF – GIF Compressor](https://ezgif.com/optimize)
- [ILoveIMG – GIF圧縮](https://www.iloveimg.com/ja/compress-image)
- [GIFコンプレッサー](https://www.freeconvert.com/ja/gif-compressor)


## 補足Tips

今回は22秒のキャプチャ動画で、サイズ「中」で作成してみました。
がしかしそのままだとQiitaでは読み込めなかったので、圧縮サイトを使いました。

▼実際に作成したGIF▼

![プレゼンテーション1 (1).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4048214/5c03812b-679a-4ae9-aa22-96289777a668.gif)

## 最後に
とにかく早く作りたい！という方にはおすすめできるんじゃないかと思います。