OmniAICreator/Qiita-1.07M huggingface dataset:
(相反定理)例題6:マクスウェルの定理 ＜【⑧ 相反定理 例題（仮想仕事の原理とエネルギ原理）】様を参考にsympyで実行した。


:::note info
[私のsympyの目次 へ](https://qiita.com/mrrclb48z/items/b6207ec7e429458fe7a7#-%E7%9B%B8%E5%8F%8D%E5%AE%9A%E7%90%86-%E4%BE%8B%E9%A1%8C%E4%BB%AE%E6%83%B3%E4%BB%95%E4%BA%8B%E3%81%AE%E5%8E%9F%E7%90%86%E3%81%A8%E3%82%A8%E3%83%8D%E3%83%AB%E3%82%AE%E5%8E%9F%E7%90%86)
:::


# オリジナル
Youtube 仮想仕事の原理の教育に関する研究会 様 　
**(相反定理)例題6:マクスウェルの定理(13:10〜19:35)**
https://youtu.be/GbH3tAg0LLQ?t=695
＜まとめ(18:15)
https://youtu.be/GbH3tAg0LLQ?t=1095
＜フル 【⑧ 相反定理 例題 （仮想仕事の原理とエネルギ原理）】(0:00〜19:35)
https://youtu.be/GbH3tAg0LLQ

# sympyのweb上での実行方法
・例題1を参照して下さい。

# sympyで
・ver0.1
```python
# ver0.1
from sympy import *
var('MA,θA')
var('MB,θB')
var('EI,l')
def myTawami(equations, variables):
    return Matrix([[eq.rhs.coeff(var) for var in variables] for eq in equations])
equ12  =Eq(θA  , MA*l/(3*EI)-MB*l/(6*EI))
equ13  =Eq(θB  ,-MA*l/(6*EI)+MB*l/(3*EI))
# 
mθAθB  =Matrix([θA,θB])
mMAMB  =Matrix([MA,MB])
mTawami=myTawami([equ12,equ13],mMAMB) 
print("#",mθAθB,"=",mTawami      ,"*",mMAMB)
print("#",mMAMB,"=",mTawami**(-1),"*",mθAθB)
print("#",mMAMB,"=",mTawami          *mMAMB.subs({MA:1,MB:0}))
print("#",mMAMB,"=",mTawami          *mMAMB.subs({MA:0,MB:1}))
print("#")
print("#",equ12)
equkasa=Eq(θA, MA*l/(3*EI)-MB*l/(6*EI)) ;print("#",equkasa)
# Matrix([[θA], [θB]]) = Matrix([[l/(3*EI), -l/(6*EI)], [-l/(6*EI), l/(3*EI)]]) * Matrix([[MA], [MB]])
# Matrix([[MA], [MB]]) = Matrix([[4*EI/l, 2*EI/l], [2*EI/l, 4*EI/l]]) * Matrix([[θA], [θB]])
# Matrix([[MA], [MB]]) = Matrix([[l/(3*EI)], [-l/(6*EI)]])
# Matrix([[MA], [MB]]) = Matrix([[-l/(6*EI)], [l/(3*EI)]])
#
# Eq(θA, MA*l/(3*EI) - MB*l/(6*EI))
# Eq(θA, MA*l/(3*EI) - MB*l/(6*EI))
```
#### 作図
・省略

# いつもの? sympyの実行環境と 参考のおすすめです。
[(テンプレート)](https://qiita.com/mrrclb48z/items/00dd08b0317069be9342)

# いつもと違うおすすめです。
#### Qiita内

https://qiita.com/mrrclb48z/items/7c6ed7af0c177a9025aa

[相反定理 共通編 Qiita内](https://qiita.com/mrrclb48z/items/b6207ec7e429458fe7a7#%E7%9B%B8%E5%8F%8D%E5%AE%9A%E7%90%86-%E5%85%B1%E9%80%9A%E7%B7%A8-qiita%E5%86%85)

#### 参考文献

~~＞一部に等分布荷重を受ける単純支持はり~~
~~＞JSME p89~~
　
~~＞片持ちばり 1)~~
~~＞構造力学公式集 表5.1 片持ちばりの公式　p128~~
初心者がReactで作るTodoリスト③ トグル機能編

# トグル機能 - チェック状態の更新
```jsx
function handleToggleTodo(id) { // todo.idをここで受け取る
    setTodos(previousTodos =>
      previousTodos.map(item =>
        item.id === id ? { ...item, isChecked: !item.isChecked } : item
      ) 
    );
  }
```
チェックボックスをクリックすると、
そのアイテムのid（todo.id）が引数としてhandleToggleTodoに渡されます。
現在のTodoリスト（previousTodos）をもとにmap()で1つずつループしながらidの一致を見つけます。
- item.idがクリックされたtodo.idと一致するとき、
isCheckedの状態を反転（true ↔︎ false）させて、新しいオブジェクトを返します。


- 一致しないときはそのままitem（オブジェクト）を返します（変更なし）。 
<br>
こうして更新されたTodoリストがsetTodos()で反映されて画面に表示されます。

![2025-05-07 22.18の画像.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4075355/58204a2a-82e1-4549-b30d-d8b29b2da458.jpeg)

# チェック状態に応じて打ち消し線をつける
上記の通りisCheckedは、チェックボックスのON/OFFに応じてtrueかfalseになります。
- trueのときは打ち消し線のスタイル
（textDecoration: 'line-through'）を適用します。
- falseのときは通常のスタイルのままです。

各アイテム ⬇︎
```jsx
<li key={todo.id}>
  <div>
    <input
      type="checkbox"
      onChange={() => onToggleTodo(todo.id)}
      checked={todo.isChecked}
    />
    <span style={todo.isChecked ? { textDecoration: 'line-through' } : {}}>
      {todo.text}
    </span>
  </div>
  <button onClick={() => onDeleteTodo(todo.id)}>削除</button>
</li>
```
## ソースコード（GitHub）
https://github.com/constsn/react-todoApp
勉強目的で簡易ブログサービスを作ってみた


## 全体像
Next.js、Supabase, Clerk あたりの雰囲気をつかんでみたかったので、Next.js 15（App Router）をベースに、Clerk を用いた認証機能と Supabase を用いたデータベースストレージを組み合わせた超シンプルなブログサービスを開発してみた。Next.js の実装はかなり AI (ChatGPT o4-mini-high) に助けて貰った。

## アプリケーションの全体像

* **フロントエンド**：Next.js 15（App Router）＋TypeScript＋Tailwind CSS でページ構築
* **認証・認可**：Clerk（`@clerk/nextjs`）によるログイン・ユーザー管理
* **データ層**：Supabase（Postgres）に記事を保存、Row‑Level Security で公開制御
* **運用**：Supabase Studio でのテーブル編集による記事追加・更新

## アーキテクチャ

1. **Next.js App Router**

   * `app/news/page.tsx`：一覧取得（Supabase クエリ）
   * `app/news/[slug]/page.tsx`：認証チェック→記事取得→Markdown→HTML
2. **Clerk**

   * `middleware.ts` またはコンポーネント内で `auth()` を呼び、未認証者をサインインページへリダイレクト
3. **Supabase**

   * **テーブル構成**：`news_posts(id, slug, title, content, published, created_at)`
   * **RLS ポリシー**：`published = true` のみ SELECT 許可
   * **Client**：`lib/supabaseClient.ts` で `createClient` を共通管理
4. **スクリプト**

   * `scripts/migrate-md-to-db.ts`：`data/news/*.md` → Supabase upsert
5. **ホスティング**

   * Vercel 等にデプロイするところは今回は省略。

## 実装手順、所要時間

| フェーズ     | タスク内容                                 | 所要時間 |   
| -------- | ------------------------------------- | ---- |
| 初期実装    | Markdownファイルの読み込みによる記事一覧・詳細ページの実装   | 2時間  |   
| UI整備     | Tailwind CSSによる一覧・詳細ページのスタイリング        | 1時間  |   
| 認証組み込み   | Clerk SDK導入および認証保護ルート実装               | 2時間  |   
| マイグレーション | MarkdownからSupabaseへのデータマイグレーション       | 1時間  |   
| データ取得切替  | `app/news` のファイル読み込みをSupabaseクエリに切り替え | 1時間  |   
| テスト | 動作確認、RLS・環境変数チェック         | 1時間  |   

## 学び、所感

* **高速プロトタイピング**：細かい所は気にせずとりあえず作ってみたが、Clerk × Supabase の組み合わせで、認証→データ管理→公開まで短期間で実装可能という感覚が掴めた。Clerk 認証で無限ループが発生する問題のトラシューに時間が掛かった。Next.js 側で Cookie を読むページは静的化せず動的レンダリングに切り替えることで直った。当初はリダイレクトも含めて静的ビルドしてしまっていた。
* **App Router の恩恵**：Server Component での非同期処理と Server Actions の併用で、クライアントとサーバーの境界が明確化。
k8sでのcommnadとargsの挙動について

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2985550/ef382c05-3efa-4709-a7fc-d7e8d913abdb.png)

## はじめに

`Kubernetes`で`Pod`をデプロイする際、
コンテナの起動コマンドを制御するために`command`や`args`を指定することがあります。

例えば、以下のような`Dockerfile`と`manifest`ファイルがあるとします。

```Dockerfile:Dockerfile
FROM alpine:latest

ENTRYPOINT ["echo", "hello"]
CMD ["world"]
```

```yaml:manifest.yaml
apiVersion: v1
kind: Pod
metadata:
  name: echo
spec:
  containers:
  - name: echo
    image: echo:latest
    imagePullPolicy: Never
    command: ["echo", "1"]
    args: ["2"]
```

`manifest`で指定した`commnad`と`args`は、
Dockerfileの`ENTRYPOINT`や`CMD`とどのように関係しているのでしょうか。

本記事では、その挙動について解説します。

## DockerfileでのCMDとENTRYPOINT

以下のDockerfileを例に挙動を見てみます

```Dockerfile:Dockerfile
FROM alpine:latest

ENTRYPOINT ["echo", "hello"]
CMD ["world"]
```

この`Dockerfile`からimageを作成し、実行してみます

```bash
$ docker build -t echo .
$ docker run -it --rm echo
hello world
```

`hello world`と出力されました。

つまり、`ENTRYPOINT`の実行コマンドに`CMD`が引数として渡されて実行されます

`ENTRYPOINT`だけ、`CMD`だけの時にどうなるかは、リファレンスを参照してみてください

https://docs.docker.com/reference/dockerfile

## (本題) k8sでのcommnadとargsの挙動について

さて、本題ですが、
KubernetesのPodについてのドキュメントに以下のように書いてありました。

> Note:
> The command field corresponds to ENTRYPOINT, and the args field corresponds to CMD in some container runtimes.

つまり、`command`を指定すると、`ENTRYPONIT`が上書かれ、`args`を指定すると、`CMD`を上書きすることができます。

最初の例を見てみましょう。

```Dockerfile:Dockerfile
FROM alpine:latest

ENTRYPOINT ["echo", "hello"]
CMD ["world"]
```


```yaml:manifest.yaml
apiVersion: v1
kind: Pod
metadata:
  name: echo
spec:
  containers:
  - name: echo
    image: echo:latest
    imagePullPolicy: Never
    command: ["echo", "1"]
    args: ["2"]
```

上書きが起きるとすると、
ENTRYPOINT: `echo hello` -> `echo 1`
CMD: `world` -> `echo 2`
となるので、`echo 1 2`が実行されるのでしょうか。

実行してみます。

```bash
$ kubectl apply -f manifest.yaml
$ kubectl logs echo       
1 2
```

予想が当たり、`1 2`が出力されましたね

## まとめ

まとめです。

|Dockerfile|Kubernetes|実際に実行されるもの|
|----|----|----|
|ENTRYPOINT|command|command が優先（ENTRYPOINTを置き換える）|
|CMD|args|args が優先（CMDを置き換える）|

`CMD`と`command`が同じじゃ無いんですね〜

## サンプルコード

https://github.com/maaaashi/docker-k8s-command-args-sample


## 参考

https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/
Spring における @PreDestroy の実行タイミングと内部処理の流れ

## はじめに

Spring Framework では、Bean のライフサイクル管理の一環として`@PreDestroy`アノテーションを使うことで、Beanが破棄される直前に任意の処理を実行できます。
しかし、「実際にどこで、どのようにこのアノテーションが呼ばれるのか？」について明確に理解していなかったので調べてみました。

本記事では、Spring Boot アプリケーション終了時に`@PreDestroy`がどこで呼ばれるのか、内部ソースコードに一部踏み込んで解説してみます。


## 解説

Spring Boot では、JVM のシャットダウン時に`SpringApplicationShutdownHook`というクラスが実行され、すべての`ApplicationContext`をクローズする処理が行われます。

```kotlin
public void run() {
    // 省略
    contexts.forEach(this::closeAndWait);
    // 省略
}
```

この`closeAndWait()`の中で呼ばれる`context.close()`が、`@PreDestroy`呼び出しの実質的なトリガーとなります。

---

`ConfigurableApplicationContext#close()` → `AbstractApplicationContext#doClose()`

`context.close()`の呼び出しは、Springの`AbstractApplicationContext`クラスにたどり着き、以下のような `doClose()`が実行されます：

```kotlin
protected void doClose() {
    // 省略
    this.destroyBeans();
    // 省略
}
```

この`destroyBeans()`が、Bean の破棄ロジックの起点です。

---

`destroyBeans()` → `destroySingleton()` → `destroySingleton(String)` -> `destroyBean(...)`

この流れで各 Bean の破棄処理に入ります。以下のような destroyBean() が呼び出されます。

```kotlin
protected void destroyBean(String beanName, @Nullable DisposableBean bean) {
    // 省略
    if (bean != null) {
       try {
            bean.destroy();
        } catch (Throwable ex) {
            if (this.logger.isWarnEnabled()) {
                this.logger.warn("Destruction of bean with name '" + beanName + "' threw an exception", ex);
            }
        }
    }
    // 省略
}
```

まだ、ここでは`@PreDestroy`は呼ばれていないように見えます。実はその通りで、`destroy()`の呼び出し対象は`DisposableBean`インターフェースの実装です。

では`@PreDestroy`はどこで呼ばれているのでしょうか？

---

実際に`@PreDestroy`を実行しているのは、Spring内部クラスである`DisposableBeanAdapter`です。
Bean登録時、`@PreDestory`を付与したメソッドを持つBeanは`DisposableBeanAdapter`でwrapされており、`DisposableBeanAdapter`は`DisposableBean`を継承してるので、`DefaultSingletonBeanRegistry`に登録されています。
そのため、先述の`destroyBean`の中で一緒に呼び出されています。
以下のように、`@PreDestroy`を付与したメソッドを呼び出すようになっています。

```kotlin
public void destroy() {
    // 省略
    else if (this.destroyMethods != null) {
        for (Method destroyMethod : this.destroyMethods) {
            this.invokeCustomDestroyMethod(destroyMethod);
        }
    }
    // 省略
}
```

このメソッドたちは、ReflectionUtils.makeAccessible() + invoke() によってリフレクションで呼び出されます。

---

`@PreDestroy`を検出するクラス

`DisposableBeanAdapter`自体は`@PreDestroy`の存在をスキャンしているわけではありません。
実際に @PreDestroy を見つけてメソッドとしてリストアップしているのは以下のクラスです：
- `CommonAnnotationBeanPostProcessor`
- `InitDestroyAnnotationBeanPostProcessor`

これらがクラススキャン中に`@PreDestroy`アノテーションを検出し、破棄メソッドのリストを作成します。
そしてそのリストが `DisposableBeanAdapter`に渡され、最終的に実行されるという仕組みです。

---


`@PreDestroy`ひとつ取っても、ここまで深い仕組みがあるのは興味深いですね。
unity　レイヤーの使いかた

Unityにおけるレイヤー
ゲームでも同じようにレイヤーを使っていて、例えばUnityの場合であれば、スプライトを表示する順番を制御するためにSorting Layer(ソーティングレイヤー)を使います。SortingLayerの選択は『SpriteRenderer』のコンポーネントの中で行います。

SortingLayerを追加する場合は『Add Sorting Layer』を選択してタグやレイヤーを追加する画面を開きます。デフォルトだと『 Default』ひとつになっていて、ここに追加できます。

スプライトを表示する場合　順番を決めることも可能

キャラクターレイヤーのなかで「Order In Layer」という設定項目を使って手前、奥を設定できます。

「Order In Layer」は値が小さいものから描画されます。つまり、値が小さければ画面奥に表示され、値が大きければその上に描画されて結果として画面手前に表示されるようになります。
数式の計算（多桁）

一桁だと動いたコードが、多桁になった瞬間に崩壊。やっぱり文字列処理、奥深い！

<br>

## 🧪 問題概要

文字列で渡される数式（演算子は `+` と `-` のみ）を計算して、結果を出力せよ。


入力例:
```
781781+272-178781+3919-1737389
```
出力例:
```
-1130198
```
<br>

https://paiza.jp/works/mondai/string_primer/advance_step9

<br>

## 💥 NGコード例
```js
const chars = Array.from(input); // 一文字ずつバラされる
```
こうすると、例 `"781"` が `["7", "8", "1"]` にバラバラ…。
前回の一桁問題と同じようにやって失敗(´;ω;｀)

<br><br>

## ✅ OKコード（正規表現で意味ごと抽出）
```js
const numbers = input.match(/\d+/g).map(Number);
const operators = input.match(/[+-]/g);

let result = numbers[0];
for (let i = 0; i < operators.length; i++) {
  result = operators[i] === "+" ? result + numbers[i + 1] : result - numbers[i + 1];
}
```

### 🔍 解説

- `\d+` ：連続する数字 → 多桁でも1つの塊に
- `[+-]` ：`+` か `-` のどちらか → 記号だけ抽出
- `.match()` ：文字列を「意味単位」で切り分けられる超便利メソッド！
- `g` ：global（全体） → 該当するすべてを対象に抽出！


<br><br>

## ☝️ 技術ポイントまとめ

- 数字と記号をロジック的に分離するには「正規表現 ＋ `match()`」をつかう
- 特に多桁対応では `\d+` が便利
- `split()` や `Array.from()` では構造を失うリスクあり

<br><br>
[僕の失敗談と解決話！](https://paizabeginner.wordpress.com/2025/05/08/paiza%e3%81%a7%e5%9f%ba%e6%9c%ac%e3%83%9e%e3%82%b9%e3%82%bf%e3%83%bc-%e6%95%b0%e5%bc%8f%e3%81%ae%e8%a8%88%e7%ae%97%ef%bc%88%e5%a4%9a%e6%a1%81%ef%bc%89/)
WordPressで記事の読了時間を計算表示するショートコードを作ってみた！

![reading-time.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4067506/c5c266e8-67f3-4c64-b1fc-8f3616686be9.jpeg)

## はじめに

ブログ記事に「読了時間の目安」を表示すると、読者の安心感がアップし、直帰率の低下や滞在時間の延長につながります。  
今回は「1分あたり600文字」を基準に、記事本文の文字数から読了時間を算出し、`[reading-time]` ショートコードで表示する方法を紹介します。

※サンプルコードはテーマの `functions.php`（子テーマ推奨）に追記する、もしくは管理プラグインで新規スニペットとして作成してください。

## 事前準備

- WordPress
- PHP 
- 子テーマがあればそちらを利用（アップデートで削除されないように）
- functions.phpもしくは管理プラグイン（私は『Code Snippets』を利用しています）を触れる程度の能力

## 実装手順

`functions.php`に追記するか、管理プラグインで新規スニペットとして登録してください。

### ショートコードの作成

ここでは、`calculate_reading_time()`という関数を定義します。
記事の文字数をカウントして、読了時間を計算。
HTMLとして出力するようにしています。
そして、`add_shortcode()`でショートコードを登録します。

```php
function calculate_reading_time() {
    $content = get_post_field('post_content', get_the_ID());
    $text = wp_strip_all_tags($content);
    $char_count = mb_strlen($text);
    $chars_per_min = 600;
    $minutes = ceil($char_count / $chars_per_min);
    return return '<div class="reading-time">読了時間：約<span class="minute">'.$minutes.'</span>分</div>';
}
add_shortcode('reading-time', 'calculate_reading_time');
```

あとは読了時間を表示したいところで、以下のように表記すればOKです。

```html
[reading-time]
```

私の場合は、ウィジェットの『記事はじめ』の部分に追加しています。

### CSSのカスタマイズ

読了時間の見た目を整えます。
この例では、点線のボーダーで、読了時間の分の部分が赤い太字になります。

```css
.reading-time {
    padding: 5px;
    border: 1px dashed #999999;
    text-align: right;
}
.reading-time .minute {
    color: #cc0000;
    font-size: 1.25em;
    font-weight: bold;
}
```

実装が完了すると、以下のような表示になるはずです。

![スクリーンショット 2025-05-08 16.09.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4067506/b1217cc0-87a5-4086-b6a0-9db84d12c309.png)

## まとめ

読了時間表示機能の実装自体は、意外と簡単にできます。
ちょっとした時間で実装できますので、もし良かったらあなたも導入してみてはいかがでしょうか？

この記事は、ブログで公開した内容の再構成版です。
詳細を確認したい方は、ぜひブログもチェックしてみてくださいね。

- <a href="https://suzushinlab.com/reading-time-in-wordpress/" title="【WordPress】記事の読了時間を計算表示するショートコードを作成してみた！｜スズシンラボ">【WordPress】記事の読了時間を計算表示するショートコードを作成してみた！｜スズシンラボ</a>
windows11で、sketchup6 その103

# 概要
windows11に、sketchup6を入れてみた。
rubyで、3Dを書く。
練習問題やってみた。

# 練習問題
九九をアニメーションせよ。

# 写真

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/648e469f-2b2b-412f-a6bb-c367a86012ce.png)

# サンプルコード

```
class Kuku
	def initialize
		model = Sketchup.active_model
		@entities = model.active_entities
		coordinates = [10, 10, 10]
		@point = Geom::Point3d.new coordinates
		@text = @entities.add_text "This is a Test", @point
		@k = 0
		@tid = UI.start_timer(1.9, true) {
			tick
		}
	end
	def tick
		@text.erase!
		i = Integer(@k / 9) + 1
		j = @k % 9 + 1
		if (j > 0)
			puts "#{i}x#{j}=#{i * j}"
		end
		@text = @entities.add_text "#{i}x#{j}=#{i * j}", @point
		@k += 1
	end
end
```


以上。
データ分析 スタックエリアチャート

## はじめに
データ分析をやる中でこれいいチャートだなと思うものがあったので、乗っけておきます。

## 📊 スタックエリアチャート（Stacked Area Chart）とは

スタックエリアチャートは、**複数の系列データを“積み上げた形”で時系列に可視化**するグラフです。  
- 各系列の値を縦方向に積み上げ、その合計を時間軸（X軸）上にプロット  
- 系列ごとの“流れ（トレンド）”と「構成比」の両方を同時に把握できる

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599049/2f49cc71-0015-4e95-9036-b6f4b6762546.png)


---

### 🧐 使うべきシーン・メリット

| シーン・目的                     | 理由・効果                                      |
|----------------------------------|-----------------------------------------------|
| **全体のボリューム推移を見たい**   | 各時点の合計値がひと目でわかる                       |
| **構成比の変化を同時に確認したい** | 時系列における各カテゴリのシェア（％）や量の変化を把握   |
| **複数系列間のトレンド比較**       | 系列ごとの増減を、他系列との重なりで直感的に理解できる   |
| **ダッシュボードやレポート**       | 一枚で「全体感＋内訳」を示す省スペースな可視化が可能     |

---

### 🚀 主な実例（ユースケース）

1. **Webアクセス解析**  
   - デバイス別（PC／モバイル／タブレット）のセッション数推移  
   - チャネル別（Organic／Paid／Referral）の流入シェア

2. **売上分析**  
   - 商品カテゴリ別の月次売上トレンド  
   - 地域別・店舗別の売上構成比

3. **システム監視**  
   - サーバーリソース（CPU／メモリ／ディスク）の利用率推移  
   - マイクロサービス間のリクエスト数分布

4. **コールセンター運営**  
   - 問い合わせ種別（商品質問／クレーム／サポート）の時間帯別推移  
   - 月間チャット・電話・メールでの対応件数比較

5. **プロジェクト管理**  
   - タスクステータス（未着手／進行中／完了）の日次推移  
   - リソース配分（要員A／要員B／要員C）の工数シェア

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

# 📦 必要なライブラリ
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.font_manager import FontProperties

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

# 📊 サンプルデータの作成
data = {
    'hour': list(range(24)),
    'product_inquiry':  [0,0,0,0,0,5,20,50,60,55,50,40,30,25,20,15,10,10,5,3,2,1,0,0],
    'delivery_issue':   [0,0,0,0,0,2,10,30,40,45,40,35,30,25,20,15,10,10,5,3,1,0,0,0],
    'complaint':        [0,0,0,0,0,1,5,15,20,18,15,13,12,10,8,7,6,5,4,3,2,1,1,0],
    'refund_request':   [0,0,0,0,0,0,3,8,10,12,10,9,8,7,6,5,4,3,3,2,1,1,0,0],
}

df = pd.DataFrame(data)

# 📈 グラフの描画
plt.figure(figsize=(10, 6))
plt.stackplot(
    df['hour'],
    df['product_inquiry'],
    df['delivery_issue'],
    df['complaint'],
    df['refund_request'],
    labels=['商品について', '配送トラブル', 'クレーム', '返品・返金'],
    alpha=0.8
)

plt.title('1日のコールセンター問い合わせ状況', fontproperties=jp_font)
plt.xlabel('時間帯（時）', fontproperties=jp_font)
plt.ylabel('問い合わせ件数', fontproperties=jp_font)
plt.xticks(range(0, 24, 2))
plt.legend(loc='upper left', prop=jp_font)
plt.grid(True)
plt.tight_layout()
plt.show()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599049/cb05e78f-f3df-4307-9bca-51b7c3a60ef7.png)
【Windows】/etc/hosts ファイルの編集方法

# はじめに
コマンド上での編集だとうまくいかなかったため、手動でファイルを書き換えました。
参考になれば。。

# 手順
**１．スタートメニューから「メモ帳」を検索し右クリック→管理者権限で実行**
※管理者権限で実行しないと、元のファイルを上書きできません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3675500/a175659b-679b-4983-b994-0ea25ada36af.png)


**２．元のetc/hosts ファイルの中身をコピペして、メモ上で編集**

**３．編集が終わったら、メモ左上の「ファイル」→「名前を付けて保存」**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3675500/5c2e613d-1e37-4373-932d-65554a4b7179.png)


```
ファイル名：C:\Windows\System32\drivers\etc\hosts
ファイルの種類：すべてのファイル
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3675500/05c72f96-0a80-4050-bbab-3c75ba404c51.png)

上記を入力して上書き保存


**４．元のetc/hosts ファイルが上書きされていることを確認**


# 参考
コマンド上での操作方法はこちらに書いてくださっています
https://qiita.com/hirokik-0076/items/830f18ed733e65defa58
🔐Web開発者必見！セキュリティの基礎を楽しく学ぼう💡

みなさん、こんばんは。韓国在住のsanaです！

開発者の皆さん！👩‍💻👨‍💻
ウェブサイトやアプリケーションを作る際、セキュリティ対策は万全ですか？🛡️
「難しそう…」「後回しにしちゃおうかな…」なんて思っていませんか？😅

そんなみなさんにピッタリの講座をご紹介します！🌟

https://u.inf.run/3F20FhZ


～特徴～
⏱️時間：約6時間（全39レッスン）
🗣️言語：韓国語（日本語字幕付き）
📚レベル：初級者向け

🌈この講座の魅力ポイント✨
1. セキュリティの基本をしっかり学べる📘

・安全なコードの書き方
・ウェブサイトのセキュリティ対策
・暗号化の原理と実践
・ハッキングの種類と防御方法
・セキュリティプロセスの理解


2. 実践的な内容で即戦力に💪
・FlaskとPythonを使った実習もあり、学んだことをすぐに試せます！🐍

3. 初心者でも安心のカリキュラム🍼
・プログラミングの基本知識があればOK！セキュリティの専門知識がなくても大丈夫です😊


🎯こんな方におすすめ！

ウェブ開発を始めたばかりの方👶
セキュリティに興味がある方🔍
安全なウェブサービスを提供したい方🌐
情報セキュリティの資格取得を目指す方🎓



より安全で信頼性の高いウェブサービスを提供するためにもセキュリティを学ぶことも大切ですよね！
今日紹介した講座は無料で視聴可能です。気軽にセキュリティ学習を始めてみませんか？
Next.jsのレンダリング戦略とServer/Client Componentの使い分け

※本記事は React 18 / Next.js 13 以降の App Router に基づく内容です。

🪞 はじめに
Next.js を使っていると、「サーバーコンポーネント？ クライアントコンポーネント？ SSR？ CSR？ 結局どれを使えばいいの？」と悩むことがありますよね。

本記事では、Next.js の最新のレンダリング戦略について整理しながら、Server Component / Client Component の使い分けやパフォーマンス設計について実例ベースで深掘りしていきます。

🧭 Next.js のレンダリング戦略の変遷

| 時代 | 特徴 |
|------|------|
| **Next.js ～12** | SSR を実現するには `getServerSideProps` が必要だった。クライアントサイド中心の構成。 |
| **Next.js 13～（App Router）** | SSR がデフォルト化。RSC（React Server Components）導入によりサーバー中心の設計が可能に。|

⚙️ サーバーコンポーネントとは
app/ ディレクトリ配下で use client が指定されていないコンポーネントは、サーバーコンポーネントとして扱われます。

✅ サーバーコンポーネントのメリット
データ取得が高速：データソースに近い場所（サーバー）で処理。

セキュリティ：トークンやAPIキーなどの機密情報をクライアントに渡さない。

キャッシュ最適化：静的な出力が可能でパフォーマンス向上。

ストリーミング：チャンク単位でレスポンスを返せる。

SEO に強い：サーバー側で HTML が生成されるため、検索エンジンやSNSカードに強い。

💡 クライアントコンポーネントとは
```Javascript
'use client'

export default function Button() {
  return <button onClick={...}>Click</button>;
}
```
このように明示的に use client をつけたコンポーネントは クライアント側で JavaScript により実行されるコンポーネントです。

✅ クライアントコンポーネントの用途
状態管理（useState, useReducer）

ライフサイクル（useEffect）

イベント処理（onClick, onChange）

ブラウザAPI（localStorage, window など）

📌 サーバー / クライアントコンポーネントの使い分けマトリクス
| 必要なこと | Server Component | Client Component |
|------------|------------------|------------------|
| データ取得 | ✅ | ⛔（手動で fetch） |
| 機密情報を扱う | ✅ | ⛔ |
| 大きな依存関係を隠す | ✅ | ⛔ |
| UIのインタラクション | ⛔ | ✅ |
| useState / useEffect | ⛔ | ✅ |
| ブラウザAPI使用 | ⛔ | ✅ |
| カスタムフック（ステート含む） | ⛔ | ✅ |
| クラスコンポーネント | ⛔ | ✅ |

🎯 適切な設計とは？ — 基本的にはSSRを使用。合理的な理由があるときにuse client を使用する。

コンポーネントを複数組み合わせるときに全体をuse clientで作ってしまうのは推奨されない。

// ❌ 悪い例
```Javascript
'use client'

export default function Page() {
  return (
    <>
      <HeavyChart />
      <NonInteractiveContent />
    </>
  );
}
```

// ✅ 良い例
// page.tsx（Server Component）
```Javascript
import NonInteractiveContent from './NonInteractiveContent';
import HeavyChart from './HeavyChart';

export default function Page() {
  return (
    <>
      <NonInteractiveContent />
      <HeavyChart /> {/* ← use client */}
    </>
  );
}
```
⚠️ クライアントコンポーネントは、サーバーコンポーネントの中に 「入れる」ことはできる が、逆は不可。

🧮 SSR vs CSR — 状況に応じて使い分けよう
| 比較軸 | SSR（サーバーサイドレンダリング） | CSR（クライアントサイドレンダリング） |
|--------|----------------------------------|----------------------------------|
| 初期表示速度 | 高速（HTML がすぐ出る） | 遅い（JS読み込み後に描画） |
| サーバー負荷 | 高い（毎回レンダリング） | 低い（API 呼び出しのみ） |
| 検索エンジン対応 | ◎ | △（対策しないと弱い） |
| 表示までの応答性 | 遅め（レンダリング待ち） | 早い（プレースホルダなど工夫次第） |
| セキュリティ | APIキー隠せる | 表に出るリスクあり |

🧵 まとめ
Server Component は パフォーマンスとセキュリティを重視する場面に有効。

Client Component は インタラクティブ性が必要なUI に使用。

use client は必要な箇所のみに、最小限で使用。

SSRとCSRの使い分けは、「何を最速で見せたいか」「どこに計算負荷をかけるか」で判断。

Next.js の App Router はこのハイブリッド戦略を 1つのアプリ内で柔軟に使える設計が強み。

📘 参考資料
Next.js公式ドキュメント（Server Components）

React 公式ブログ（Server Components Deep Dive）

react-application-architecture-for-production
スマホで手軽にAWS認定試験対策ができるサイトを作ってみた

こんにちは！最近AWS認定資格の勉強をしていて、通勤中や寝る前のスキマ時間に勉強できるサイトが欲しいなと思い、自分で作ってみました。その名も「CloudDojo」です！

https://ipdojo.com

無料で全てのAWS認定試験の模擬問題を解くことができます。

## 🌟 作ったきっかけ

AWS認定試験の勉強って、PCの前に座ってじっくり本を読んだり、動画を見たりするのが基本ですよね。でも、忙しい日々の中でそんな時間を確保するのが難しくて...。「スマホでサクッと問題を解けるサイトがあれば便利なのに！」という思いから、CloudDojoが生まれました。

## 💡 CloudDojoの特徴

CloudDojoには、こんな特徴があります：

- **とにかくシンプル**：余計な機能は一切なし！問題を解くことに集中できます
- **スマホ最適化**：電車の中でも片手で快適に使えるUI設計
- **完全無料**：広告なし、会員登録不要で全機能が使えます
- **高速表示**：JavaScriptをほぼ使わない軽量設計なので、通信環境が悪くても快適

## 🤖 問題作成にAIを活用

面白いところは、問題文の作成にGoogle AI Studioの「Gemini 2.5 Pro」を使っていること！もちろん、AIが生成した問題はチェックしていますが、実際の試験の傾向に近い問題を効率的に作れています。

ただ、サイトにも注意書きしていますが、AI生成なので、たまに間違いがあるかもしれません。見つけたら教えてくださいね😅

## 🛠 技術構成

今回のサイト構築は、コスト削減と高速化を重視しました。構成はこんな感じです：

```
Google Gemini (問題文生成) + GitHub Copilot (コーディング支援)
    ↓
Astro (JavaScriptレスな静的サイト生成)
    ↓
GitHub (リポジトリ管理)
    ↓
Cloudflare (自動ビルド・グローバル配信)
```

### Astroを選んだ理由

React、Vue、Svelteなど様々なフレームワークがある中で、Astroを選んだ理由は：

1. **JavaScriptをほぼ使わずに静的サイトが作れる**（表示が爆速！）
2. **必要な部分だけハイドレーションできる**（インタラクティブな部分だけJSを使える）
3. **MDXサポート**（Markdownで問題文を管理できる）

### Cloudflareを選んだ理由

AWS AmplifyやVercelなど他のホスティングサービスと比較して：

| サービス | 利点 | 欠点 |
|---------|------|------|
| **Cloudflare** | ・完全無料枠が使いやすい<br>・グローバルCDNが速い<br>・自動SSL対応<br>・ドメイン管理も同じ場所でできる | ・ビルド時間に制限あり |
| Vercel | ・デプロイが簡単<br>・プレビュー機能が優秀 | ・無料枠の制限が厳しい<br>・追加機能で費用増加 |
| AWS Amplify | ・AWSサービスとの連携が容易<br>・スケーラビリティが高い | ・無料枠超過すると高額に<br>・設定が複雑 |
| Netlify | ・CMS機能が充実<br>・フォーム機能が便利 | ・ビルド時間に制限あり<br>・追加機能で費用増加 |

実際のところ、静的サイトであれば、どのサービスでも十分なパフォーマンスが出ます。でも、**完全無料**で運用したかったので、Cloudflareを選びました。特に良かったのは：

- ドメイン管理からホスティングまで一元化できる
- 無料プランでも世界中のCDNを使える
- 同じ設定でWorkers（サーバーレス関数）も追加できる

## 💰 コスト構造

CloudDojoの運用コストは、ほぼドメイン代のみです：

- **ドメイン代**：.com ドメイン $11.48/年（約¥1,722）
- **ホスティング**：Cloudflare Pages（無料）
- **CDN**：Cloudflare CDN（無料）
- **SSL証明書**：Cloudflare（無料）

ほとんどのサービスが無料枠で十分なので、年間2,000円以下で運用できています。

## 🚀 これからの展開

現在はAWS認定資格の12種全てに対応しています。今後はAzureなどのより多くの認定試験に対応していく予定です。

また、問題の質を高めるために、皆さんからのフィードバックをもとに改善していきたいと思っています。

## ⭐ まとめ

AWSの勉強って大変ですが、スキマ時間を活用すれば効率的に進められます。CloudDojoが皆さんの資格取得の助けになれば嬉しいです！

サイトはこちら(再掲)：

https://ipdojo.com

ぜひ使ってみて、感想や改善点を教えてください。皆さんのフィードバックをお待ちしています！

## 追伸

実は近い将来、この構成を応用してAzureやGoogle Cloudの認定試験対策サイトも作る予定です。乞うご期待！

---

*この記事は、現在運用中の実際のプロジェクトについて書いたものです。もし質問やフィードバックがあれば、コメントや[Twitter](https://twitter.com/uk714)までお気軽にどうぞ！*
【備忘録】LinuxMint 22.1へDockerインストール

備忘録です。
公式サイトのコマンドが古いのと、派生OSを使ってると未だにちょっと迷います。

参考: [docker docs](https://docs.docker.com/engine/install/, "Install Docker Engine")


## リポジトリの登録
Archはamd64なのですが、環境によって変更が必要かもです。
```
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu noble stable"
```
nobleは`/etc/os-release`のUBUNTU_CODENAMEから。


## Dockerの鍵をインストール。
```
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/docker.gpg
```
apt-keyが非推奨になったので、gpgを使います。

## 更新とDocker engineのインストール

```
sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
```
Dockerを使うためにこれが必要らしいのです。

## ユーザをDocker グループに入れる
rootしか使えないので、自分のユーザを追加。
```
sudo gpasswd -a $USER docker
```
ログアウト・ログインし、ユーザのプロファイルをリロード

## 動作確認
```
ajitama@work-os:~$ docker compose version
Docker Compose version v2.35.1
ajitama@work-os:~$ docker version
Client: Docker Engine - Community
 Version:           28.1.1
 API version:       1.49
 Go version:        go1.23.8
 Git commit:        4eba377
 Built:             Fri Apr 18 09:52:14 2025
 OS/Arch:           linux/amd64
 Context:           default

Server: Docker Engine - Community
 Engine:
  Version:          28.1.1
  API version:      1.49 (minimum version 1.24)
  Go version:       go1.23.8
  Git commit:       01f442b
  Built:            Fri Apr 18 09:52:14 2025
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.7.27
  GitCommit:        05044ec0a9a75232cad458027ca83437aae3f4da
 runc:
  Version:          1.2.5
  GitCommit:        v1.2.5-0-g59923ef
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0
```

誰かの役にたてば嬉しいです。
閉域ネットワーク環境におけるAWS MGNリホスト時のネットワーク構成

# オンプレミスからのリホスト
オンプレミス環境の仮想マシンをAWSに移行する際、OSやアプリケーションなどをそのまま移行する「リホスト」という手法があります。リホストを自動化するサービスとしてAWSからApplication Migration Service（以下AWS MGN）とVM Import/Exportが提供されています。AWS MGNは自動的にレプリケーションがされて、カットオーバー処理を行うことでテスト、少ないダウンタイムで移行が可能となります。一方VM Import/Exportは一旦イメージファイルを取得してAWS環境にアップロードする方式のため、手動作業が多く長時間のダウンタイムが発生する可能性があります。今回の記事はAWS MGNを対象としています。

|ツール|特徴|
|---|---|
|AWS MGN|継続レプリケーション、カットオーバーによる切替|
|VM Import/Export|手動作業が多い、ダウンタイムが発生しやすい|

# エージェント版とエージェントレス版
AWS MGNは移行対象の仮想マシンにエージェントをインストールする方式とVMWare vSphere上に移行を中継する仮想マシンを作成する方式があります。後者は仮想マシンを用意する必要がありますが、移行対象の仮想マシンにエージェントをインストールする必要がありません。エージェント版はブロック単位の継続レプリケーションを行うため、カットオーバーにかかる時間が短縮出来るメリットがあります。エージェントレス版は仮想マシンのスナップショットを取得後、周期的な転送を行う仕組みのため、I/Oが多いシステムではレプリケーションに時間がかかる場合があります。

|方式|レプリケーション方式|
|---|---|
|エージェント版|ディスクブロック単位の継続レプリケーション|
|エージェントレス版|スナップショット単位の周期レプリケーション|

# ネットワーク要件
AWS MGNのエージェント版を使用してオンプレミス環境の仮想マシンをAWS環境へのリホストを想定します。オンプレミス環境はインターネット接続出来ない閉域環境、AWS側もインターネット接続出来ない閉域環境とします。その際のネットワークの前提条件を確認します。

https://docs.aws.amazon.com/ja_jp/mgn/latest/ug/preparing-environments.html

上記をまとめるとオンプレミス環境やレプリケーションサーバから必要な通信は下記になります。

|No|送信元|送信先|ポート|
|---|---|---|---|
|①|ソースサーバ|レプリケーションサーバ|TCP1500|
|②|ソースサーバ|MGNのAPIエンドポイント|TCP443|
|③|ソースサーバ|S3のAPIエンドポイント|TCP443|
|④|レプリケーションサーバ|EC2のAPIエンドポイント|TCP443|
|⑤|レプリケーションサーバ|MGNのAPIエンドポイント|TCP443|
|⑥|レプリケーションサーバ|S3のAPIエンドポイント|TCP443|


# VPCエンドポイントを使用した内部通信

![mgn03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2523652/b6eac308-9842-43cf-a817-66d7f39d7522.png)

オンプレミスのソースサーバやステージングサブネットからMGN、EC2、S3など各種サービスのAPIエンドポイントへのアクセスが必要となります。そのため対応するサービスのVPCエンドポイントの作成が必要となります。

:::note info
レプリケーションサーバからS3へのアクセスはゲートウェイエンドポイントの利用が可能です。一方、オンプレミスからS3へのアクセスはインターフェースエンドポイントを使用する必要があります。
:::

# 名前解決
ソースサーバからVPCエンドポイントにアクセスする際、名前解決が必要となります。閉域環境ではグローバルな名前解決が出来ません。ソースサーバがオンプレミスのDNSサーバを参照している場合、`条件：amazonaws.coms 転送先：DNS Inbound EndpointのIPアドレス`でDNSサーバにフォワーディング設定を行うことで名前解決が可能となります。

![mgn04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2523652/a11cc759-0135-429f-aede-b83ea1435b25.png)

# エージェントインストール時の指定

`aws-replication-installer-init.py`でエージェントをインストールするとレプリケーションが開始されます。その際、`--s3-endpoint` と`--endpoint`でそれぞれ、S3のVPCエンドポイント、MGNのVPCエンドポイントを指定しないと閉域での通信になりません。


# レプリケーションテンプレートの変更
レプリケーションサーバの動作を定義するレプリケーションテンプレートの設定を変更します。「プライベートIPを使用する」項目を有効にすることで「プライベート ネットワーク経由」の通信となります。

https://docs.aws.amazon.com/ja_jp/mgn/latest/ug/replication-server-settings.html

# その他
オンプレミス側のファイアウォールのやOSのファイアウォールで必要なポートを開けます。AWS側でもネットワークACLやセキュリティグループ等で必要なポートを開けます。
メンバ変数とローカル変数

## 概要
- メンバ変数
  - 「クラスの持ち物」「クラスの属性」「クラスの特徴・状態を表すもの」「その人の名前、年齢、趣味」
- ローカル変数
    - 「一時的な作業のために使うだけの変数」「関数・メソッドの中だけの持ち物」「その場限りのメモ書き」


## 具体例
```php
class Dog {
    // メンバ変数（フィールド）
    public $name;

    public function bark() {
        // ローカル変数
        $sound = "ワンワン！";
        echo $this->name . " が " . $sound;
    }
}

$dog = new Dog();
$dog->name = "ポチ";
$dog->bark();
```

- $name → クラスDogのメンバ変数 → このDogオブジェクトにずっと付いてまわる
- $sound → bark()メソッドのローカル変数 → bark()の中だけで使う、一時的なもの

## まとめ
| 項目 | メンバ変数 | ローカル変数 |
| --- | --- | --- |
| 宣言場所 | クラスの中（メソッドの外） | メソッドの中 |
| 使える範囲 | クラスのインスタンスが生きている間 | そのメソッド・ブロックの中だけ |
| 保存される期間 | インスタンスが存在する間ずっと | メソッドが呼ばれている間だけ |
| 目的 | オブジェクト（クラスのインスタンス）の状態を保持するため | 一時的な計算や処理のために使う |
ReduxとlocalStorageの違いとは？それぞれの役割と併用パターン

Reactアプリで状態管理とデータ保存をどうすればいいのか、よく混乱するポイントの一つに「ReduxとlocalStorageって何が違うの？」「どっちを使えばいいの？」という疑問があります。

本記事では、それぞれの役割と、実際の併用パターンについてわかりやすく解説します。

✅ 結論（先に言う）
Reduxは「アプリ実行中の状態管理」用（一時的）

localStorageは「ブラウザに永続保存」用（永続的）

よって、併用するのが実践的
　→ Reduxで状態管理、localStorageで初期値の永続化

Reduxとは？
ReduxはJavaScriptアプリで使われる「状態管理ライブラリ」です。Reactと組み合わせることで、以下のような用途に使えます。

ログイン状態の共有

UIの表示状態（モーダルの開閉など）

フォームの入力内容の管理

ただし、Reduxの状態はメモリ上にあるだけなので、ページをリロードすると消えます。

localStorageとは？
localStorageは、ブラウザにデータを保存するためのAPIです。

特徴は以下の通り：

ブラウザを閉じてもデータが残る

保存できるのは文字列のみ（JSON.stringifyが必要）

認証トークン、テーマ設定、ユーザー名の保存などに使える

js
복사
편집
// 保存
localStorage.setItem("theme", "dark");

// 読み込み
const theme = localStorage.getItem("theme");
ReduxとlocalStorageの違いまとめ
特徴	Redux	localStorage
保存場所	メモリ（RAM）	ブラウザのディスク
永続性	なし（リロードで消える）	あり（手動で削除しない限り保持）
データ形式	JavaScriptオブジェクト	文字列のみ（JSONが必要）
主な用途	状態管理（UI/ロジック用）	設定や認証情報の永続保存
セキュリティ	両方ともクライアント側なので、機密情報の保存は非推奨	

実践：ReduxとlocalStorageの併用パターン
例えば、「ログイン状態をReduxで管理しつつ、リロードしても保持したい」という場合はこうします：

1. 初期値をlocalStorageから取得
js
복사
편집
const persistedState = JSON.parse(localStorage.getItem("appState"));
const store = createStore(rootReducer, persistedState);
2. Reduxの状態が変わったら保存
js
복사
편집
store.subscribe(() => {
  localStorage.setItem("appState", JSON.stringify(store.getState()));
});
こうすることで、

Reduxで状態管理

localStorageで永続保存

というベストな使い分けができます。

✍️ まとめ
ポイント	説明
Redux	状態管理ライブラリ（リロードで消える）
localStorage	永続的なデータ保存（UI状態管理は弱い）
ベストプラクティス	Redux + localStorageの併用

参考リンク
Redux公式

MDN localStorage

Qiitaっぽくカスタマイズしたいポイント（タグや補足など）あれば教えてください！
出退勤打刻漏れを防ごう！

# はじめに

現在、弊社では「出退勤打刻の漏れ」が問題となっている。

弊社の打刻方法は、各社員に配布されたICカードを専用端末に読み込ませることで行う方式である。打刻が完了すると、専用の勤怠管理サイト上で出退勤時刻を確認できるようになる。

しかし、打刻を忘れた場合は、後でPCの起動ログなどを証跡として提出し、勤怠時間の修正依頼を行わなければならない。依頼を受けた人事部は、その証跡を目視で確認し、必要な修正を手動で許可する。件数が少なければ大した手間ではないが、毎日十数件に及ぶと負荷が非常に大きくなる。

このような背景から、「出退勤打刻の漏れを防ぐ」ための仕組みを考案した。

# 設計方針

筆者はそこら辺にいる一般社員であり、以下のような制約が存在する。

* 有料ツールは導入不可
* 管理者権限を必要とするPC設定の変更は不可
* 勤怠管理サイトのAPIは使用不可
* 実装に割ける時間は数時間程度

このような制約下でも、以下のようなシンプルな仕組みであれば実現可能と判断した。

1. PCの起動・シャットダウン時に何らかの通知を発行する。
1. 通知を私用のスマートフォンで受信する。
1. 通知経由で勤怠管理サイトをすぐに開けるようにする。

![システムイメージ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2878091/76d147b6-803b-4496-9e31-f48d5577a40e.png)

# 方針の選定

専用のスマホアプリを自作し、リッチなUIで実装することも検討したが、時間的な制約から断念した。代替案として、Pushbulletという外部サービスを利用し、通知機能を簡単に実現することにした。

:::note info
Pushbulletとは？
PC・スマートフォン間で手軽に通知やファイルを送信できるサービスである。
気になる方は公式ホームページへ→ https://www.pushbullet.com/
:::

# 初期試作（退勤打刻向け）

まずは退勤打刻の漏れを防ぐことを目指し、以下の手順で仕組みを構築した。

## やったこと
1. Pushbulletのアカウントを作成し、アクセストークンを生成
1. Pushbulletのスマホアプリを私用のスマホにインストール
1. PushbulletのAPIを叩くだけの`.bat`ファイルを作成
1. タスクスケジューラで新しいタスクを作成
トリガー：ユーザーセッションからの切断時
操作：上記`.bat`ファイルを実行

![スクリーンショット 2025-05-08 134924.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2878091/45fbb25f-aea4-4129-aa87-f25933be477b.png)
![スクリーンショット 2025-05-08 134954.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2878091/7d0d832e-cff8-4298-8b64-b26aeb5c8c80.png)


## `.bat`ファイルの中身
```bat
@echo off
curl -X POST "https://api.pushbullet.com/v2/pushes" ^
  -H "access-token: Pushbullet上で生成したアクセストークン" ^
  -H "content-type: application/json" ^
  --data-raw "{\"type\":\"link\",\"title\":\"おつかれさん\",\"body\":\"勤怠打刻を確認してね\",\"url\":\"勤怠管理サイトのURL\"}"
```

# 試作版の問題点
初期実装後、PCをシャットダウンして動作確認を行ったが、プッシュ通知は届かなかった。調査の結果、タスクスケジューラの「ユーザーがログオンしているときのみ実行する」という設定が原因であることが判明した。

「ユーザーがログオンしているかに関係なく実行する」という設定への変更を試みたが、一般ユーザーの権限では変更できなかった。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2878091/0fc9b27b-d08c-4917-80aa-9501f1f7817d.png)

# 修正版の構成

意地でも自分の権限で出来る範囲で納めたかったので、再度方針を見直し、手動操作を前提とした構成に変更した。

## やったこと（修正版）

1. Pushbulletのアカウントを作成し、アクセストークンを生成（変更なし）
1. Pushbulletのスマホアプリを私用のスマホにインストール（変更なし）
1. **プッシュ通知送信とPCシャットダウンを同時に実行する`.bat`ファイルを作成**
1. **作成した`.bat`ファイルをデスクトップ等に配置してすぐ実行できるようにした**

## 修正版`.bat`ファイル

```bat
@echo off
curl -X POST "https://api.pushbullet.com/v2/pushes" ^
  -H "access-token: Pushbullet上で生成したアクセストークン" ^
  -H "content-type: application/json" ^
  --data-raw "{\"type\":\"link\",\"title\":\"おつかれさん\",\"body\":\"勤怠打刻を確認してね\",\"url\":\"勤怠管理サイトのURL\"}"
shutdown /s /f /t 5
```

退勤時にこのファイルを実行すれば、通知の送信とPCのシャットダウンを一括で行える。ログオフ前に通知が発行されるため、権限不足による実行失敗の心配もない。

![Screenshot_20250508-175331.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2878091/baaa3ff9-4ea5-4d99-a905-230eeff28d6b.png)

# おわりに

今回作成したシステムには明確な問題が存在する。

それは、「`.bat`ファイルの実行を忘れず行えるなら、そもそも打刻を忘れることがない」ということである。

とはいえ、何も対策しないよりは遥かにマシでなので、今後もしより良い仕組みを思いついたら改善したい。


ちなみに、出勤打刻の漏れについては「ロック画面の画像を変更する」という力技である程度防止できることが判明したので、実装する気が起きなかった。

![1000015645.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2878091/105b8706-c26b-4d5a-b84d-03515711db2f.jpeg)
ChatGPTで英語スピーチ原稿を作り、Amazon Pollyで音声化する

# ChatGPTで英語スピーチ原稿を作り、Amazon Pollyで音声化する

英語のスピーチ原稿を**ChatGPTでブラッシュアップしながら作成**し、それを**Amazon Polly**で**高品質な音声に変換する**までの流れをまとめます。（この記事は、ChatGPT-4oによって生成された原稿に多少加筆修正することで作成されました。）

---

## 🎯 ゴール

* 自分で書いた草案をベースに、ChatGPTと対話しながら英語スピーチ原稿を仕上げる
* Amazon PollyでSSMLを使って滑らかな音声合成を実現
* 最終的に `.mp3` ファイルとしてダウンロード可能

---

## 🛠️ 使用するツール

| ツール名           | 用途                | リンク                                                                            |
| -------------- | ----------------- | ------------------------------------------------------------------------------ |
| ChatGPT（GPT-4） | 原稿の翻訳・添削・音読スクリプト化 | [https://chat.openai.com/](https://chat.openai.com/)                           |
| Amazon Polly   | 音声合成・SSML読み上げ     | [https://console.aws.amazon.com/polly/](https://console.aws.amazon.com/polly/) |

---

## 📝 手順

### 1. ChatGPTで英語スピーチ原稿を仕上げる

#### 💬 原稿を用意

まず、**日本語または英語の草案**を用意します。とりあえず日本語でもOKです。

例：

```text
私は、気象の研究を行っています…
```

次に、ChatGPTに以下のように依頼します：

```text
この日本語原稿を英語の1分スピーチ用に自然な口調で翻訳・再構成してください。
口頭で話すことを前提に、文を短く区切って、リズムよくしてください。
```

#### ✍️ 添削を繰り返してブラッシュアップ

生成された原稿に対して、

* 「この単語は不自然なので変更してください」
* 「いつも〜〜のように表現しているのですが、アリですか？」
* 「この文は強調したいのでインパクトある言い回しに」

などと**追加の指示を与えると、ChatGPTが改善案を提案してくれます。**

💡辞書を引きながら、おかしな表現になっていないか綿密にチェックしましょう。（手間を惜しまないこと！）
💡ネイティヴスピーカーや、その分野に詳しい先生・先輩に確認することも重要です。AIを過信しないようにしましょう。
💡一旦完成したら、まずは自分で音読してみましょう。言いづらいと感じる部分があったらさらに修正しましょう。

---

### 2. ChatGPTでSSML形式の読み上げスクリプトを作ってもらう

#### SSMLとは？

Amazon Pollyが認識する**音声合成用マークアップ言語**です。

#### 注意点

Amazon Pollyでは、Neural Voice（高品質音声）において一部のタグはサポートされていないため、ChatGPTにはこう依頼しましょう：

```text
Convert this script into SSML for Amazon Polly neural voice. 
Only use <speak>, <break>, and <p>. 
Do not use <emphasis> or <voice>.
```

ChatGPTが生成する例（抜粋）：

```xml
<speak>
  I’m studying paleoclimatology.
  <break time="700ms"/>
  One of our best tools is water isotopes in Antarctic ice cores.
</speak>
```

---

### 3. Amazon Pollyで音声に変換する

#### ステップ：

1. [Amazon Pollyコンソール](https://console.aws.amazon.com/polly/) にログイン
2. 「Text Type」を **SSML** に設定
3. 作成した SSML スクリプトを貼り付け
4. 音声タイプから `en-US → Joanna` などの落ち着いた声を選択
5. 「Listen」または「Download」をクリック！

🎉 音声ファイル（.mp3）が生成されます！



## ✅ まとめ

| 工程       | 内容                                    |
| -------- | ------------------------------------- |
| ① 原稿作成   | 自分で下書きを作り、ChatGPTで翻訳・整形               |
| ② SSML整形 | Polly向けにタグ付きで整形（`<speak>`, `<break>`） |
| ③ 音声化    | Pollyに貼り付けて.mp3化、発表資料に組み込むだけ！         |

---

## 🧠 おまけ：うまくいかないときは？

* Pollyのエラー：「invalid SSML syntax」が出たら、サポートされていないタグを使っていないか確認！**
* SSMLスクリプトは Pollyの仕様に**厳密に準拠**する必要があります
  👉 [サポートされるタグ一覧（公式）](https://docs.aws.amazon.com/polly/latest/dg/supportedtags.html)

---
【Azure】VMからAzureFilesを見れるようにする方法

# Azure VM から Azure Files へのアクセス設定：効率的な方法と意外な発見

## はじめに

本記事では、Azure Virtual Machine (VM) から Azure Files へのアクセスを可能にする設定手順について解説します。

※ここで説明する設定は、パブリックネットワークアクセスを 
”**すべてのネットワークから有効**”はなく”**選択した仮想ネットワークとIPアドレスから有効**”を選んで、
”**特定のグローバルIPからのみ**”アクセス出来るようにしている環境下での設定になります。

## やりたかったこと
仮想マシンからAzureFilesで作成したドライブを表示できるようにしたい

## 事前に準備しておくもの

- Azure サブスクリプション
- 設定済みのAzureVM
- Azure Filesを含むストレージアカウント
- 仮想ネットワークとサブネット

## 調べてみた

そもそも何故AzureVMからAzureFilesが見えないのか、という点について。
AzureVMのパブリックIP アドレスをストレージアカウントのファイアウォールに追加していたのですが、それではアクセスは許可されません。
その理由は、「同じAzure内でのアクセスはインターネットを経由せず、内部ネットワークを使用するため」でした。
ということで、何かしらしないとつながらない、という訳です。

ここからが本題。

最初にVMからAzureFilesを見たいと思った時に見つけたのがこのサイト↓
https://jpaztech.github.io/blog/storage/storageFirewall-accesscontroll/

これを見るに、サービスエンドポイントを作成すればいけそう、と思ったので
作成方法を調べてみました。

が、その時は見つからず。(探すのが下手だっただけかもしれないですが)

しかし、Azure内を調べているとこんなものを見つけました。

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3890692/d973f566-0902-412b-b3ca-0e4604245955.png)|
|:-:|

ストレージアカウント→ネットワーク内にありました

辿っていった先がこちら

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3890692/b4b3f51f-b12b-4aef-897d-e91b5d0851c3.png)|
|:-:|

仮想ネットワーク→サブネット内にサービスエンドポイント設定があったのです。

## 設定手順

調べるよりやったほうが早い、ということで設定してみました。

画像の通り、
パブリック ネットワーク アクセスを「選択した仮想ネットワークと IP アドレスから有効」にし、
仮想ネットワークから「既存の仮想ネットワークを追加する」を選択
|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3890692/a98ab9e1-419f-4268-93a8-eff917b8761d.png)|
|:-:|

ネットワークを追加するでサブスクリプション・仮想ネットワーク・サブネットを選択します。

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3890692/d96432fc-b219-462a-a984-4f4a208d965e.png)|
|:-:|

選択すると画像のように「次のネットワークには 'Microsoft.Storage' で有効にされたサービス エンドポイントがありません」と表示されますが、そのまま「有効化」を選択してOKです。
|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3890692/4d42f3b1-fb45-419d-8234-dd11229a14b5.png)|
|:-:|

更新中になります
|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3890692/95b7a813-bb50-42b1-b0c0-a30c8ed15012.png)|
|:-:|

時間が経つとサービスエンドポイントの状態が有効になるので、「追加」を選択します
|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3890692/a6026d99-3d31-4ac0-a2be-6299ce1a41bd.png)|
|:-:|

＞を開くとサービスエンドポイントが「有効」になっていることが確認できます。
|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3890692/2edb944d-d167-48c1-903b-82b9b9e699b5.png)|
|:-:|

設定に問題がなければ最後に「保存」を選択します。
|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3890692/ca2459c0-c7d1-43b1-98f5-7b6d393d5ff1.png)|
|:-:|

以上の設定で、AzureFilesがVMから見えるようになりました。

## 結論
もっと時間がかかるかと思いきや、思っていたより簡単にできた印象でした。
もし同じように悩んだ方に届けば嬉しいです。
あああ

Sub ConvertCSVtoXLSX_KeepLeadingZeros()
    Dim csvPath As String
    Dim xlsxPath As String
    Dim ws As Worksheet
    Dim qt As QueryTable
    Dim wb As Workbook

    ' フルパスを設定（必要に応じて変更）
    csvPath = "C:\Users\YourName\Desktop\AAA.csv"
    xlsxPath = Replace(csvPath, ".csv", ".xlsx")

    ' 新しいブックを作成
    Set wb = Workbooks.Add(xlWBATWorksheet)
    Set ws = wb.Sheets(1)

    ' クエリテーブルでCSVを文字列として読み込み
    Set qt = ws.QueryTables.Add(Connection:="TEXT;" & csvPath, Destination:=ws.Range("A1"))
    With qt
        .TextFileParseType = xlDelimited
        .TextFileCommaDelimiter = True
        .TextFileColumnDataTypes = GetColumnTypes(csvPath)
        .Refresh BackgroundQuery:=False
    End With

    ' .xlsx形式で保存
    Application.DisplayAlerts = False
    wb.SaveAs Filename:=xlsxPath, FileFormat:=xlOpenXMLWorkbook
    wb.Close SaveChanges:=False
    Application.DisplayAlerts = True
End Sub
a

Sub ImportCSVWithLeadingZeros()
    Dim ws As Worksheet
    Dim qt As QueryTable
    Dim csvPath As String

    ' CSVファイルのフルパスを指定
    csvPath = "C:\Users\YourName\Desktop\sample.csv"

    ' 新しいワークシートを作成
    Set ws = Worksheets.Add

    ' クエリテーブルとしてCSVをインポート
    Set qt = ws.QueryTables.Add(Connection:="TEXT;" & csvPath, Destination:=ws.Range("A1"))

    With qt
        .TextFileParseType = xlDelimited
        .TextFileConsecutiveDelimiter = False
        .TextFileTabDelimiter = False
        .TextFileSemicolonDelimiter = False
        .TextFileCommaDelimiter = True ' CSVなのでカンマ区切り
        .TextFilePlatform = xlWindows

        ' 各列のデータ型を文字列に設定（すべて1）
        ' 例：5列まで文字列として設定（必要に応じて増やす）
        .TextFileColumnDataTypes = Array(2, 2, 2, 2, 2)

        .Refresh BackgroundQuery:=False
    End With
End Sub
Private Endpoint経由の名前解決回数を把握する方法

## Private Endpoint経由の名前解決回数を把握する方法
Azure Storage AccountにPrivate Endpoint（PE）を設定し、VNetにアタッチしました。また、Private DNS ZoneをVNetにリンクしています。同じVNet内のAzure VMからStorage AccountにPE経由でアクセスする際の**名前解決の回数**を把握したい場合、Private DNS Zoneのメトリック「Query Volume」は現在利用できません。

そこで、**DNSセキュリティーポリシー**を利用して、名前解決の回数をログとして記録するソリューションを紹介します。以下に手順と検証結果をまとめます。

### ソリューション：DNSセキュリティーポリシーの活用
DNSセキュリティーポリシーを使用すると、DNSクエリのログを取得でき、名前解決の回数を確認できます。以下は具体的な作成手順です。

#### 手順
1. **DNSセキュリティーポリシーの作成**
   - Azureポータルで「DNSセキュリティーポリシー」を検索し、作成を開始。
   - 新しいDNSセキュリティーポリシーを作成。
   - ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/620054/fe4b9a36-408f-4fa8-816b-a21db5002305.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/620054/9db0ad91-54ed-4209-874d-bb0ac0e058d7.png)

2. **仮想ネットワークリンクの設定**
   - Private EndpointがアタッチされているVNetを選択。
   - 仮想ネットワークリンクを作成して、DNSセキュリティーポリシーをVNetに適用。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/620054/95a148dc-9fb2-4963-98b8-546feecde330.png)


3. **ルールの設定**
   - 特定のトラフィックをブロックするルールは不要なため、ルール作成はスキップ。
   - そのままポリシーを作成。
   - ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/620054/ae336315-61ac-41c9-af21-5d97c5e983be.png)


4. **診断設定の有効化**
   - DNSセキュリティーポリシーの診断設定を有効化。
   - 「DNS responseログ」を収集するよう設定。
   - ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/620054/661be2f6-e037-4404-8c36-128782bcb6e1.png)


5. **名前解決のテスト**
   - Azure VMからStorage AccountのFQDN（例：`tansatest1987.blob.core.windows.net`）に対し、`nslookup`を6回実行。
   - Private Endpointが正しく利用され、Private IPに名前解決されることを確認。
  ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/620054/b11d9622-d2f3-4e8b-a939-91af303eee76.png)



6. **ログの確認**
   - 約10分後、診断ログを確認。
   - 以下のKQLクエリでログをフィルタリング：
     ```kql
     DNSQueryLogs
     | where QueryName contains "tansatest1987.blob.core.windows.net"
     | where QueryName !contains_cs "cloudapp"
     | where QueryType !contains_cs "AAAA"
     ```
   - 結果：6回の名前解決が正確に記録されていることを確認。
   ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/620054/68d64a51-af20-484f-bb32-4cfff7376c1f.png)


### 検証結果
- `nslookup`を6回実行した結果、DNSセキュリティーポリシーの診断ログに6回のクエリが記録されました。
- Private Endpoint経由の名前解決が正常に行われ、ログに正確に反映されました。

### 補足
- **DNSセキュリティーポリシーの利点**：設定が簡単で、DNSクエリの詳細なログを取得可能。名前解決の監視やトラブルシューティングに有効。
- **注意点**：診断ログの反映には数分から10分程度の遅延が発生する場合があります。

### 参考文章：
https://qiita.com/Isato-Hiyama/items/7bb6483f29e981df9e23
https://learn.microsoft.com/ja-jp/azure/dns/dns-security-policy
個人的備忘録：フロントでつぶやき → バックエンドから応援が返るUIの仕組み実装メモ

## はじめに

今回は、フロントエンド（Next.js）上で「美女から応援メッセージが届くUI」を実装した「bijyoモード」について紹介します。

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

## 書こうと思ったきっかけ

受講しているITスクールのハッカソンの開発の一環で取り組んでいるフロントエンド開発の一部で、この機能の実装内容を後から見返せるよう、備忘録として記事にまとめています。

## 内容

このモードでは、ユーザーが「今日のつぶやき」を入力して送信ボタンを押すと、FastAPI経由でバックエンドにリクエストが送られ、応援メッセージ（AIの返答）が「美女からのやさしいエール」欄に表示される仕組みになっています。

**実際の画面**

![Screenshot 2025-05-08 at 17.40.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3662571/1ed9bb77-4145-43ea-ae79-23f3b72d6321.png)

**つぶやき後の画面**

![Screenshot 2025-05-08 at 17.40.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3662571/d3cd4be4-984c-421b-9f00-c912f30c3253.png)

### 主な構成は以下のとおりです：

#### ステート管理：

```ts
const [diaryMessage, setDiaryMessage] = useState("");
const [cheerMessage, setCheerMessage] = useState("");
const [token, setToken] = useState("");
const [sessionId, setSessionId] = useState("");
```

ユーザーの入力内容、応援メッセージ、セッション情報などを管理しています。

#### 初期処理：

```ts
useEffect(() => {
  localStorage.setItem("session_id", "1");
  document.body.classList.add("washitsu");
  const storedToken = localStorage.getItem("token");
  const storedSessionId = localStorage.getItem("session_id");
  if (storedToken) setToken(storedToken);
  if (storedSessionId) setSessionId(storedSessionId);
  return () => {
    document.body.classList.remove("washitsu");
  };
}, []);
```

セッションIDの初期化と和風スタイルの適用を行っています。

#### API通信：

```ts
const handleEnergy = async () => {
  const payload = {
    session_id: Number(sessionId),
    is_user: true,
    content: diaryMessage,
  };

  const res = await fetch(`http://localhost:8000/api/sessions/${sessionId}/messages`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(payload),
  });

  const result = await res.json();
  setCheerMessage(result.content || "美女から応援が届きました！");
};
```

ユーザーのつぶやきをサーバーへ送り、応援メッセージを受け取って画面に表示しています。

#### UI表示：

```tsx
<div className="bg-[#fff0f5] border-[4px] border-[#d48fb1] rounded-xl shadow-md p-6 w-full max-w-xl">
  <h2 className="text-2xl font-bold mb-4">美女からのやさしいエール</h2>
  <div className="w-full h-32 border border-gray-300 rounded-md p-3 bg-[#fff7fa] whitespace-pre-wrap overflow-y-auto">
    {cheerMessage || "ここに美女からの応援が表示されます"}
  </div>
</div>
```

応援メッセージをテキストとして表示する専用のエリアです。

## まとめ

この実装では、非同期通信の基本をおさえつつ、状態管理とUIの連動をシンプルにまとめることができました。

:::note info
**今後は、複数メッセージの履歴表示やチャット形式への拡張も視野に入れています。**
:::

同様の構成は「さぶちゃんモード」にも応用できるため、キャラクター別モードの切り替えなどにも展開していきたいと考えています...！

https://qiita.com/free-honda/items/498433d6f14d56ed3107
Power BI でスライサーに表示する列（氏名）を別の列（ふりがな）で並び替えする方法

# はじめに
ごきげんよう、百合宮桜（Miyu）です♪
今回はスライサーには漢字での氏名を表示したい、でもスライサーの並び順はふりがなの昇順を利用したい！！というようなケースに対応する方法を書いていきます！

# 方法
1. テーブルビューに切り替えます

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3032705/9ca108cb-8ce5-4c3a-a72e-56e0c2fde13e.png)

2. スライサーに表示する列（今回の場合は氏名列）を選択します

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3032705/08c3ca71-617d-4f5e-9c91-abe491d0cc6e.png)

3. 列ツール > 列の並び替え > 並び替えで利用したい列（今回の場合はふりがな列）をクリックします

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3032705/2c8c5523-5335-40fa-8d58-b5de72513248.png)

4. レポートビューに切り替えます

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3032705/52cf0fc9-009c-4c8b-8c7b-81294eb8d0f9.png)

5. スライサーのフィールドに氏名列（スライサーに表示したい列）を挿入します

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3032705/1654e212-2530-470c-9bcf-75768b89b9c1.png)


# おわりに
いかがでしたか？ ふりがなの他にも社員番号とか得意先コードのような任意の番号での並び替えにも応用ができるので、ぜひ覚えてくださいね！
役に立つなぁと思った方は💛（いいね）、あとで見返したい方は 📁（ストック）をぽちっと押していただけると嬉しいです!(^^)!
それでは、ごきげんよう♪
ちょちょっと手元でSCSSを書きたい時のマクロ

DOMパーサーを利用してネストだけ解釈します。

```js:testcss.html 

class SCSS{
	compile(css){
		var s = ''
		var r = css
		while(r!=s){
			s = r
			var r = s.replace(/([^;{}<>]+){([^{}]*)}/g,(m,k,v)=>{
				return `<n key="${k.trim()}">${v.trim()}</n>`
			})
		}
		var n = new DOMParser().parseFromString(`<xml>${s}</xml>`,'text/xml').firstChild
		var a = Array(...n.querySelectorAll('n')).map(n=>{
			var b = [[],n]
			while(n.nodeName=='n'){
				b[0].unshift(n.getAttribute('key'))
				n = n.parentNode
			}
			return b
		})
		return a.map(b=>{
			Array(...b[1].children).forEach(n=>n.remove())
			var s = b[1].textContent.replace(/;\s+/g,';').replace(/:\s+/g,':')
			if(s) return `${b[0].join(' ')}{${s}}`
		}).filter(Boolean).join("\n")
	}	
}

/*
out:
#test{font-size:2em;}
#test UL{line-height:1.5;font-size:3em;}
#test UL LI.sss{font-size:50vw;}
#test2{font-size:3em;}
#test2 UL{line-height:1.5;font-size:3em;}
#test2 UL LI.sss{font-size:50vw;}
*/

console.log(new SCSS().compile(`
	#test {
		font-size:2em;
		UL {
			line-height:1.5;
			LI.test	{}
			LI.sss	{font-size:50vw;}
			font-size:3em;
		}
	}
	#test2 {
		font-size:3em;
		UL {
			line-height:1.5;
			LI.test	{}
			LI.sss	{font-size:50vw;}
			font-size:3em;
		}
	}
`))

```