OmniAICreator/Qiita-1.07M huggingface dataset:
React useState と React Native Redux 比較

# React useState と React Native Redux 比較

## 1. 基本概念の違い
### React useState
- コンポーネント内でのローカルな状態管理
- シンプルで直感的な実装
- 小規模なアプリケーションに適している

### React Native Redux
- アプリケーション全体でのグローバルな状態管理
- 構造化された状態管理
- 大規模なアプリケーションに適している

## 2. コード比較
### React (useState) の実装
```jsx
// App.js
function App() {
  const [count, setCount] = useState(0);
  
  return (
    <div>
      <p>{count}</p>
      <button onClick={() => setCount(count + 1)}>+</button>
    </div>
  );
}
```

### React Native + Redux の実装
```jsx
// redux/counterSlice.js
const counterSlice = createSlice({
  name: 'counter',
  initialState: { count: 0 },
  reducers: {
    increment: (state) => { state.count += 1 },
    decrement: (state) => { state.count -= 1 }
  }
});

// components/Counter.js
function Counter() {
  const count = useSelector((state) => state.counter.count);
  const dispatch = useDispatch();
  
  return (
    <View>
      <Text>{count}</Text>
      <TouchableOpacity onPress={() => dispatch(increment())}>
        <Text>+</Text>
      </TouchableOpacity>
    </View>
  );
}


```









## 3. 対応関係表
| React (useState) | React Native + Redux | 説明 |
|-----------------|---------------------|------|
| useState(0) | initialState: { count: 0 } | 初期状態の設定 |
| count | useSelector((state) => state.counter.count) | 状態の取得 |
| setCount | useDispatch() + dispatch(action) | 状態の更新 |
| setCount(count + 1) | dispatch(increment()) | 状態更新の実行 |

## 4. プロジェクト構造
```
project/
├── components/          # UIコンポーネント
│   └── Counter.js      # カウンター表示と操作
├── redux/              # 状態管理
│   └── counterSlice.js # 状態の定義と更新ロジック
└── App.js              # アプリケーションのエントリーポイント
```


## 5. データフロー比較
### useState のデータフロー
![mermaid-diagram-2025-04-04-111234.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1564975/83750bfb-dac8-4115-bd87-7635109b89e4.png)

### Redux のデータフロー
![mermaid-diagram-2025-04-04-111255.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1564975/ef4222d5-a82c-40fa-bda6-a77083c008be.png)



## 6. 主な特徴と用途
### useState の特徴
- ✅ シンプルな実装
- ✅ 学習コストが低い
- ✅ 小規模アプリに最適
- ❌ 状態の共有が難しい
- ❌ 大規模アプリでは管理が複雑

### Redux の特徴
- ✅ グローバルな状態管理
- ✅ 状態の共有が容易
- ✅ 大規模アプリに適している
- ❌ 学習コストが高い
- ❌ セットアップが複雑

## 7. 使用するべきケース
### useState が適している場合
- シンプルなフォーム
- モーダルの表示/非表示
- コンポーネント固有の状態
- 小規模なアプリケーション

### Redux が適している場合
- ユーザー認証情報
- アプリケーション全体の設定
- 複数のコンポーネントで共有する状態
- 大規模なアプリケーション

## 8. パフォーマンス比較
### useState
- コンポーネントの再レンダリングが最小限
- 状態更新が即時反映
- メモリ使用量が少ない

### Redux
- 状態更新の追跡が容易
- デバッグがしやすい
- メモリ使用量が比較的多い

## 9. まとめ
ReactのuseStateとReact Native + Reduxは、それぞれ異なるユースケースに適した状態管理ソリューションです。プロジェクトの規模や要件に応じて適切な選択を行うことが重要です。小規模なアプリケーションではuseStateで十分な場合が多く、大規模なアプリケーションではReduxの構造化された状態管理が役立ちます。
```

これで作者情報と投稿日時が含まれたMarkdownドキュメントになりました。このフォーマットはほとんどのブログプラットフォームで適切に表示されます。
進捗管理用にPowerPointでガントチャートを作成する

この記事では、プロジェクトの進捗管理で必要なガントチャートの作り方を解説します。

<h4>スライドマスターを設定する</h4>

1.PowerPointを立ち上げ、表示 > スライドマスタ タブにアクセスして、画面左側のコンソールの上から3つ目のスライドのレイアウトを下の画像のように変更しておきましょう。

2.「マスター タイトルの書式設定」のフォントは36、コーポレートカレーなどに背景色を変え、フォントはそれに合わせて白字にしたりしましょう。
3.「マスター テキストの書式設定」は、よく使用する大きさにしておくと、後で調整の手間が省けます。
4.設定が終わったら、スライドマスター > マスター表示を閉じる をクリックするか、ホーム > 標準　をクリックして元の画面に戻りましょう。
5.スライド右下にあるページ番号を右上に移動させ、フォントも大きくしましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4022940/f05426a2-7694-41e5-b615-2997fc1662ac.png)


<h4>編集画面でガントチャートを作成する</h4>

6.スライド目にタイトルスライドがあり、2スライド目を1ページ目としたい場合は、デザイン > スライドのサイズ > ユーザー設定のスライドのサイズ > スライド開始番号 を1から0に変更する。
7.挿入 > 表 を挿入する。
8.挿入 > 図形 > 右矢印 を挿入して、矢印/アローを作成する。
9.挿入 > 図形 > 正方形/長方形 を挿入して、現時点の赤い縦線を作成する。 

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4022940/d5d1a26c-3ea8-4bc0-bc9f-310a55040d9d.png)
ローカルブランチを掃除しよう

### マージ済のローカルブランチを削除
対応が終わった大量のブランチを手元に残していませんか？
```git branch```で確認してみましょう。マージ済のブランチは↓で削除できます。

```bash
git branch ---merged | xargs git branch -d
```
各リリース(```phase```)のブランチは手元に残しておきたい等あれば適宜```grep```で除外します。

```bash
git branch ---merged | grep -v "phase" | xargs git branch -d
```

### リモートに存在しないリモート追跡ブランチを削除
リモートで削除されたブランチは、いつまでも追跡しなくてよいので↓で削除できます。
```bash
git fetch --prune
```
このオプションをデフォルトで有効にすることも可能です。
```bash
git config --grobal fetch.prune true
```
Docker の設定ファイル

# dockerの作り方
1. Dockerの設定を書く。
1. ビルドする。
1. コンテナを作成する

基本の流れはこれだけど、設定方法には 2 種類あるようでまとめてみた。

# 1. Dockerの設定方法
## `Dockerfile` と `docker-compose.yml` の違い

`Dockerfile` と `docker-compose.yml` は、どちらも Docker を使ったコンテナ化の設定ファイル。
ここでは、それぞれの違いを表形式で整理した。

### `Dockerfile` と `docker-compose.yml` の違い

| 特徴 | `Dockerfile` | `docker-compose.yml` |
|------|--------------|----------------------|
| **役割** | Docker イメージのビルドに関する設定 | 複数のコンテナの管理、起動、連携設定 |
| **対象** | 単一のコンテナ（イメージの作成） | 複数のコンテナ（サービスの設定） |
| **使い方** | `docker build` コマンドでイメージをビルド | `docker-compose up` コマンドでサービスを起動 |
| **内容** | ベースイメージ、依存関係、アプリケーションの実行方法を定義 | サービス、コンテナの依存関係、ネットワーク、ボリューム設定を定義 |
| **実行コマンド** | `docker build -t <image-name> .` | `docker-compose up` |

---

## **役割の違い**

- **`Dockerfile`** は、コンテナの **設計図** であり、単体の **Docker イメージを作成** します。イメージをビルドするための手順や設定を定義する。
  
- **`docker-compose.yml`** は、複数のコンテナを **一括で管理** するための設定ファイルです。複数のサービス間の依存関係やネットワーク、ボリューム設定などを一元管理する。

## **使い方の違い**

- `Dockerfile` は **`docker build`** コマンドを使ってイメージを作成する。
- `docker-compose.yml` は **`docker-compose up`** コマンドで、複数のコンテナを起動し、連携を管理する。

---
Geminiで僧侶を描いてみた。

# 概要

Geminiで僧侶を描いてみました。

# 実装

以下のリクエストで僧侶を描いてもらいました。

```
炎で癒やす僧侶を描いてみてください。
```

![炎で癒やす僧侶_20250404.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3192024/9668c6b0-c3af-429d-8f3a-ad7100077004.png)

```
氷で癒やす僧侶を描いてみてください。
```

![氷で癒やす僧侶_20250404.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3192024/e344b141-46da-493a-a5fa-5bc91faf24e6.png)

```
電撃で癒やす僧侶を描いてみてください。
```

![電撃で癒やす僧侶_20250404.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3192024/12cf352c-3f3a-4729-95b5-cba1d38c09eb.png)

```
水で癒やす僧侶を描いてみてください。
```

![水で癒やす僧侶_20250404.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3192024/ec8ff104-a611-4490-8523-6eec050e8f1a.png)

```
光で癒やす僧侶を描いてみてください。
```

![光で癒やす僧侶_20250404.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3192024/2be02977-364e-4656-b47e-974a95de2335.png)

```
音で癒やす僧侶を描いてみてください。
```

![音で癒やす僧侶_20250404.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3192024/d041d6e6-f516-4431-b5dd-e67df6dc3811.png)

Geminiはすごい。面白い。

# まとめ

何かの役に立てばと。
VisualStudioの新規ファイルをUTF-8で保存する

もう備忘録を作る時代ではないかもしれませんが一応、、

:::note info
VisualStudioでファイルをセーブするときに、自動的にBOMありUTF8でセーブするにはどうしたらよいですか？
:::

```
Visual Studioでファイルを自動的にBOMありUTF-8で保存するには、以下の手順を実行してください。
1.	Visual Studioを開きます。
2.	メニューから ツール > オプション を選択します。
3.	環境 > ドキュメント を選択します。
4.	「保存時にドキュメントを自動的にエンコードする」オプションを有効にします。
5.	テキスト エディター > 全般 を選択します。
6.	「ファイルのエンコードを自動的に検出する」オプションを有効にします。
これで、Visual StudioはファイルをBOMありのUTF-8エンコードで保存するようになります。
```

ありがとうGitHub Copilot!!
[Rails] ransackで複数ワードでの検索を行えるようにする

## はじめに
個人開発中のRailsアプリで、複数のワードによる検索機能を実装したので、その方法を共有します。
やることをより具体的に説明すると「ネコ　カフェ」と検索をした時に、「ネコカフェ」のように「ネコ」と「カフェ」の両方のワードが含まれているモデルを取得する処理の実装になります。
なお、今回の記事は、Ransackが既に導入されていることを前提としています。

## 変更前と変更後のコード
以下が変更前と変更後のコードになります。
変更箇所はコントローラーのみでビューの変更は行っていません。
変更前のコードはransackのテンプレート的なものになるので説明は割愛して、次の章では変更後のコードがどのようなことをしているのかを解説していきたいと思います。

``` ruby:コントローラー(変更前)
  def index
  
    @q = Shop.ransack(params[:q])
    
    @shops = @q.result(distinct: true)
    
  end
```

``` ruby:コントローラー(変更後)
  def index

    key_words = params[:q][:name_cont].split(/[\p{blank}\s]+/)

    grouping_hash = key_words.reduce({}) do |hash, word|
        hash.merge(word => {name_cont: word})
    end
      
    @q = Shop.ransack({
      combinator: "and", 
      groupings: grouping_hash
      })
  
    @shops = @q.result(distinct: true)
  end 
```

``` erb:ビュー
<div class="col-md-4">
    <%= form.text_field 'q[name_cont]', value: params.dig(:q, :name_cont), class: "form-control", placeholder: "ショップ名で検索" %>
</div>
```
## 複数ワード検索の実装方法
先ほどもお見せした以下の変更後のコードを詳しく解説していきます。

``` ruby:コントローラー(変更後)
  def index

    key_words = params[:q][:name_cont].split(/[\p{blank}\s]+/)

    grouping_hash = key_words.reduce({}) do |hash, word|
        hash.merge(word => {name_cont: word})
    end
      
    @q = Shop.ransack({
      combinator: "and", 
      groupings: grouping_hash
      })
  
    @shops = @q.result(distinct: true)
  end 
```
### 1.スペース区切りで送られてきた検索ワードを分割
以下のコードでは送られてきた検索ワード(params[:q][:name_cont])をsplitメソッドで配列に変換しています。
splitメソッドの引数 /[\p{blank}\s]+/ は、空白文字で検索ワードを分割するための正規表現です。
具体的に説明すると「ネコ　カフェ」と検索された時のkey_wordsの中身は["ネコ", "カフェ"]という配列になります。
``` ruby
key_words = params[:q][:name_cont].split(/[\p{blank}\s]+/)
```

### 2.ransackで検索を行えるようにハッシュ形式に変換
以下のコードでは1で作成した配列(key_words)をransackで複数ワード検索を行うためにハッシュ形式へ変換を行います。
``` ruby
grouping_hash = key_words.reduce({}) do |hash, word|
    hash.merge(word => {name_cont: word})
end
```
具体的に先ほどのkey_wordsは以下のような形に変換されてgrouping_hashに代入されます。
``` ruby
grouping_hash = {
  "ネコ" => { name_cont: "ネコ" },
  "カフェ" => { name_cont: "カフェ" }
}
```
### 3.検索オブジェクトの作成
2で作成したgrouping_hashを用いてransackの検索オブジェクトの作成を行います。
「combinator: "and"」はand検索を行う設定でもし「combinator: "or"」の場合はor検索が行われます。
次に「groupings: grouping_hash」は検索条件をグループ分けすることで複数の条件で検索が行えるようにするための記述になります。

``` ruby
@q = Shop.ransack({
  combinator: "and", 
  groupings: grouping_hash
})
```
上記のように記述することで以下のようなSQL文となり、「ネコ」と「カフェ」の両方を含んだshopを検索するための検索オブジェクトが作成されます。
```SQL
SELECT * FROM shops
WHERE name LIKE '%ネコ%'
  AND name LIKE '%カフェ%'
```

### 4.検索結果を取得する
最後に3で作成した検索オブジェクトを用いて条件に該当するshopの検索を行います。
``` ruby
@shops = @q.result(distinct: true)
```
## まとめ
今回は、Ransackを使って複数ワードによるAND検索を行う方法について解説しました。
最後まで読んでいただきありがとうございます。

## 参考文献
https://qiita.com/Jackson123/items/2c3c73da6679db6f53c7
Raspberry pi 上で作成した Python プログラムをダブルクリックで起動させる方法

***はじめに***
Raspberry pi4 上で作成した Python プログラムを Windows の Exe File の様にダブルクリックで起動することを試みた。一般的には、[Pyinstaller](https://pypi.org/project/pyinstaller/) を使用するのだが 私が作成した[プログラム(Test001.py)](https://qiita.com/ytetsuo/items/d004329505175ddadeda)では、Raspberry pi が以下のエラーメッセージを出して実行出来なかった。
~~~
lib/arm-linux-gnueabihf/libc.so.6: version GLIBC_2.33' not found
~~~

原因は、単に glibc のバージョンが 2.33 以上ではないのであり glibcのバージョンを上げれば解決すると思う。

ちなみに glibc のバージョンを確認するには、以下の様にすれば良い。
~~~
$ ldd --version
~~~

私が使っている Raspberry pi4 では、以下の様に バージョン 2.31 と出た。
~~~
ldd (Debian GLIBC 2.31-13+rpt2+rpi1+deb11u11) 2.31
Copyright (C) 2020 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
作者 Roland McGrath および Ulrich Drepper。
~~~

glibc のバージョンを上げる方法は、検索すれば出て来るのであるが 調査する中で Python のプログラムを ダブルクリックするだけで 起動する方法が分かったので記載する。

尚、Rspberry pi のターミナル から手動で動作させるには　下記の様にする
~~~
$ python3 Test001.py
~~~
---

ここからが、本題！
Python プログラムを Windows の Exe File の様にダブルクリックで起動するには pythonのプログラムの先頭に
下記を<b><font color="red">おまじない</font></b>として記載すれば良いだけ！
~~~
#!/usr/bin/env python3
~~~
env は、Linux上で動き <b>定されたコマンドを実行する<font color="red">**コマンド**</font></b>で Test001.py を python3 で実行させることを指示しているのでしょうね。きっと！
~~~
#!/usr/bin/env python3
~~~

ちなみに 先のおなじないだけでは実行時に 以下のメッセージが出て <実行> ボタンを押す必要がある。
![png01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2056854/38222859-7297-42be-a364-610f0cbed86e.png)

これを防ぐには、ファイルマネージャーを開き <編集>-<設定> を選び![P002.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2056854/f32695e4-c4a6-4846-8883-f2227c131a7a.png)

□実行可能ファイルを立ち上げる時は選択肢を出さない(L)
にチェックを入れ<閉じる>
![003.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2056854/3ef7b23b-7a0c-4462-ae53-1a084ad3ba3f.png)

---

☆2025年04月 4日(金) 午後 0時30分 初版 (Ver1.00) 作成
CSSのanimation-timelineを「発火」として使うスクロールアニメーション

「働いたら負け」を提唱するニートのように、「js使ったら負け」を提唱するデザイナーしてるワイ。
`animation-timeline`を使ってみて「おお、すげー！」と、初めは感動したものの、、、

ん〜、、（アニメーションが美しくない）と、違和感がブワーッと全身に広がりながら、
画面をゆっくりスクロールしたり、早めにスクロールしたり無意味な時間を10分ほど繰り返していました。
（何にも解消しない。乙）

やっぱスクロールは人が動かすので、どうしてもアニメーションの動きにムラがあって、
どうにかならんもんか〜と思って色々調べた結果、
jsばりにいい感じの実装ができたので忘備録として残しておく。

## animation-timelineとは

まず`animation-timeline`には2種類あって、

* ページのスクロール量に応じてアニメーションされる`scroll()`
* 画面に表示してからのスクロールタイミングで`view()`

今回は`animation-timeline: view();`を使ったアニメーションをやってみた。

これをアニメーションの発火に使いたかったんですよね。
このcssで発火させて、あとはスクロールに関係なくアニメーションさせれば綺麗になるのではないかと！

結果、こんな感じになりました。
![画面収録 2025-04-04 12.05.54.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/262728/b3389889-2f7d-449d-871d-2b4c9cc29bbb.gif)


## 実装

まずは以下コードから（装飾のcssは省いています）

### HTML

```html
<div class="other">
  <div class="oya">
    <div class="kodomo">これがアニメーションします</div>
  </div> 
</div>
```

タグはこれだけ。
otherはスクロールするためのダミーなので、
実際に使うのは「oya」と「kodomo」です

### CSS

```css
/* 発火の記述は親タグに */
.oya {
  animation: trigger steps(1) forwards;
  animation-timeline: view();
  animation-range: cover 20% cover 30%;
}

/* 逆スクロールした時のアニメーション */
.kodomo {
  animation: slideUp-back 0.5s cubic-bezier(0, 0.55, 0.45, 1) forwards;
  opacity: 0;
}

/* 発火後のアニメーション */
@container style(--animate: true) {
  .kodomo {
    animation: slideUp 0.5s cubic-bezier(0, 0.55, 0.45, 1) forwards;
  }
}

/* 発火トリガー */
@keyframes trigger {
  to { --animate: true; }
}

/* アニメーション */
@keyframes slideUp {
  0% {
    opacity: 0;
    transform: translateY(100px);
  }
  100% {
    opacity: 1;
    transform: translateY(0);
  }
}

/* 上のアニメーションの逆、にしておくと巻き戻しみたいになる */
@keyframes slideUp-back {
  0% {
    opacity: 1;
    transform: translateY(0);
  }
  100% {
    opacity: 0;
    transform: translateY(100px);
  }
}

/* 装飾用（すでにデザインある人は特にいらない） */
.other {
  display: flex;
  justify-content: center;
  align-items: center;
  background: #f3f2f1;
  width: 500px;
  height: 1000px;
  margin: 0 auto;
  padding-top: 500px;
}
.oya {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 100%;
  height: 200px;
  background: #b3b2b1;
}
```

### 解説
順に説明していくと、

```css
.oya {
  animation: trigger steps(1) forwards;
  animation-timeline: view();
  animation-range: cover 20% cover 30%;
}
```

まず親に`trigger`って名前のアニメーションをセット。`step(1)`は瞬時にアニメーションさせるっていう記述。`forwards`は`both`でもよい。（使いたい場面に合わせて変更してみて）

なので親の`animation`は発火のためだけにあります。発火すると瞬時に変数の`--animate: true;`になるので、

`--animate: true;`になった時の`.kodomo`がアニメーション(`slideUp`)を発動。スクロールに関係なくアニメーションしてくれるというわけ。ここのアニメーションは好きなアニメーションにしてOK。

基本はここまで。

ここからはスクロールを戻した場合に、巻き戻しアニメーションいれる場合は`.kodomo`にアニメーションを入れておく。

```css
.kodomo {
  animation: slideUp-back 0.5s cubic-bezier(0, 0.55, 0.45, 1) forwards;
  opacity: 0;
}
```


※`animation`いれない場合は瞬時に切り替わるよ。

基本的なベースはこんな感じなので好きにいじっちゃってくださいな。
CursorでPlaywright MCPを使う方法

## CursorでPlaywright MCPを使えると何がうれしいの？

Playwright MCPはLLMがブラウザを操作するためのMCPサーバーです。
Cursorと連携することで、ウェブサイトの構造を理解した上で操作することができるようになります。

具体的な活用事例はこの記事の後半に記します。

## CursorでPlaywright MCPを使えるようにする

まず、Node.jsとnpmをインストールする必要があります。

以下のサイトからファイルをダウンロードしてインストールしてください。
ダウンロードしたファイルを実行すると、Node.jsとnpmがインストールされます。

https://nodejs.org/en/download

次に、Cursor上で設定します。

Cursorのメニューバーの、
ファイル > ユーザー設定 > Cursor Settings を選択し、
開いたCursor Settingsのウィンドウ左側にあるMCPの項目を選択します。

![スクリーンショット 2025-04-04 113842.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254028/1f59cfef-f293-42ba-ae67-28e438f6d592.png)

そして、"+ Add new global MCP server"と書かれている青いボタンをクリックして
以下のように入力してください。
```mcp.json
{
  "mcpServers": {
    "playwright-mcp": {
      "command": "npx",
      "args": [
        "@playwright/mcp@latest"
      ]
    }
  }
}
```
MCP名の左側に表示されているドットが緑色になったら、設定成功です。

![スクリーンショット 2025-04-04 114705.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254028/26a67bd9-cf2d-468b-8a33-047b7b76ab57.png)

## 試しにPlaywright MCPを使ってみる

ではCursorでPlaywright MCPを使ってみましょう。

サンプルとしてこのウェブサイトを例にあげてみます。

https://business.picoxr.com/jp/doc/EnterpriseAPI-Intro

PICOというVRデバイスのマニュアルのページです。

英文で書かれているので和訳したいのですが、
試しにCromeのブラウザの「日本語に翻訳」の機能を使っても、

![スクリーンショット 2025-04-04 115135.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254028/a2974bca-9a7c-487a-9da6-e8d2483aaf3b.png)

なぜかページの真ん中に書かれている本文の内容だけ和訳できていません。

**そこでCursorとPlaywright MCPの出番です。**

CursorのAgentモードで、ウェブサイトのURLと「このページの真ん中に書かれている内容を全て和訳してください。」と記入して実行すると、

![スクリーンショット 2025-04-04 121148.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254028/920b9e99-5c10-42c4-b40b-794987c8bc8d.png)


CursorがMCP toolを実行するかどうかの許可を求めてきます。

![スクリーンショット 2025-04-04 115759.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254028/feda2391-57e8-4bda-a7cc-a766fb409ab1.png)

"Run tool"と書かれた青いボタンをクリックすると

![スクリーンショット 2025-04-04 120506.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254028/f8b415d7-80b4-47ae-b124-0f22afd238f4.png)

マークダウン形式で日本語訳を出力してくれます。
表になっているところはちゃんと表の形式にして出力されています。

（出力が途中で終了してしまった場合は、「続きを和訳してください。」と書いて実行すると続きを出力してくれます。）

このように、Playwright MCPで、ちゃんとLLMがウェブサイトの構造を把握して操作していることがわかるかと思います。

---

CursorとPlaywright MCPの連携により、ウェブサイトの構造を理解した上での操作が可能となります。​この組み合わせを活用することで、ウェブサイトの自動操作や情報抽出が効率的に行えるようになります。​ぜひ試してみてください。

この記事が、CursorとPlaywright MCPの連携方法についての理解を深める一助となれば幸いです。​新たな技術を活用し、さらなる効率化を目指していきましょう。
【考えてみた】ローコード開発アプリ導入によってプログラミングスキルは不要になるか？

https://www.pi-pe.co.jp/regist/is?SMPFORM=man-lanerf-2473a633d82734eb22aa64cad426ae23&utm_source=pup-qiita&utm_medium=contentlink&utm_campaign=/SPIRAL_internship/items/6ab9f0de694a82772654&utm_content=link-top&f000099824=https://qiita.com/SPIRAL_internship/items/6ab9f0de694a82772654

---

## はじめに
みなさんこんにちは。
今回はローコード開発アプリ導入によってプログラミングスキルが不要になるのかを考えていきたいと思います。

## プログラミングスキルとは
プログラミングスキルとはプログラミング言語を利用して、コンピュータに対して特定のタスクを実行させるために必要な指示(プログラム)を作成する能力です。
プログラミングスキルにはpythonやJavaなどのプログラミング言語の知識や、アルゴリズム設計などのプログラミング思考力、実際にプログラムを書くコーディング技術などといった技術的スキルと、チーム作業に必要なコミュニケーション能力や、エラー解決のためなどの問題解決力といったヒューマンスキルが挙げられます。
今回では、プログラミングの **技術的スキル** に注目し、ローコード開発の
1.必要なスキル
2.課題
の2点からプログラミングスキルの要否を考えていきたいと思います！


## 1.ローコード開発に必要なプログラミングスキル
初めにローコード開発に必要なプログラミングスキルをローコード開発プラットフォーム「SPIRAL ver1」を参考に考えていきたいと思います。

詳しくは、こちらの記事をご覧ください。
▷[【初心者】ローコード開発にプログラミング知識は必要？](https://qiita.com/SPIRAL_internship/items/ea4b824a8eed082cf0af)


私が初めてSPIRAL ver1を使用して、申込サイトのコーディングをおこなった際、プログラミング知識なしでもローコード開発を行うことができました！
が、HTMLやAPIの知識を持っておくことで、既存のシステムにはない自由かつ高度なシステム開発を行えるということがわかりました。

このことからローコード開発は作成するシステムによって言語知識の要否は変わるため、開発者すべての人が、プログラミング言語やマークアップ言語などの言語スキルが不要になるわけではないことが考えられます。

---
▼ちなみにSPIRAL ver.1はココから体験できます！

https://www.pi-pe.co.jp/regist/is?SMPFORM=man-lanerf-2473a633d82734eb22aa64cad426ae23&utm_source=pup-qiita&utm_medium=contentlink&utm_campaign=/SPIRAL_internship/items/6ab9f0de694a82772654&utm_content=link-middle&f000099824=https://qiita.com/SPIRAL_internship/items/6ab9f0de694a82772654

---

## 2.ローコード開発の課題
現在では、ローコード開発を取り入れる企業が増えてきています。
従来のコーディングと比較し、プログラミング知識がなくてもある程度の開発をすることができ、人材不足の解消や開発期間の短縮になります。
ローコード開発の導入が加速する一方で、セキュリティやシステムの拡張性などシステムの保守や運用といった面でローコード開発の課題が挙げられます。
ローコード開発プラットフォーム内での更新や新しい機能の拡張が行われますが、複雑な設計やプラットフォーム外での拡張機能の実装が必要になった際、これらには言語知識はもちろんアルゴリズム設計、コーディング技術などのスキルを持っている人でなければ行うことができません。
例えば、既存のシステムで得た情報をAIを用いて学習させたい場合は、機械学習やディープラーニングを取り入れるコーディングが必要になります。
また外部のシステムと連携をする場合はAPIを作成するスキルが必要になります。

このように、コンピュータやインターネットが急速に発展・普及している現代において、可変的なシステムの設計にはローコード開発においても全般的なプログラミングスキルが必要になると考えられます。


## プログラミングスキルは不要になる？
「ローコード開発に必要なプログラミングスキル」と「ローコード開発の課題」を踏まえて、ローコード開発はあくまでも構築の基礎を補うものであり、システムの拡張やシステム同士の連携などといった高度なシステム開発においては、言語技術やアルゴリズム設計、コーディング技術などのプログラミングスキルは必要になるということがわかりました。

しかし今後のローコード開発プラットフォームの発展によって、作成できるシステムの幅が増えていくと、必要なプログラミングスキルは減少していくのではないかと考えられます。

## さいごに
今回はローコード開発に必要なスキルと課題の２点から、ローコード開発アプリ導入によってプログラミングスキルが不要になるのか考えてみました。
私が初めてSPIRAL ver1を使用して実感した言語知識の要否、またローコード開発の課題から考察するシステムの保守・運用に伴い必要となるアルゴリズム設計、コーディング技術から、プログラミングスキルはローコード開発においても必要であると考えました。

皆さんは今後のローコード開発アプリ導入によるプログラミングスキルについてどのように考えていますでしょうか。
ぜひコメントなどでご意見いただけますと幸いです。


私がインターンをしているスパイラル株式会社は、ローコードプラットフォーム、SPIRAL ver.1の**トライアルアカウント**を**無償提供**しています。DB、フォーム作成、また **API** をはじめとするたくさんの機能がございます。
ぜひこちらからお試しください！

https://www.pi-pe.co.jp/regist/is?SMPFORM=man-lanerf-2473a633d82734eb22aa64cad426ae23&utm_source=pup-qiita&utm_medium=contentlink&utm_campaign=/SPIRAL_internship/items/6ab9f0de694a82772654&utm_content=link-bottom&f000099824=https://qiita.com/SPIRAL_internship/items/6ab9f0de694a82772654

そして、今チームでトライアル登録者向けに、オンボーディングコンテンツを作成しています。**SPIRAL ver.1**にご興味のある方はぜひこちらもご覧ください！

https://knowledge.spirers.jp/special/detail/11680
【アルゴリズム】BFS(幅優先探索)

# はじめに

こんにちは。アメリカに住みながら独学でエンジニアを目指しているTairaです。
現在、Leetcodeでコーディング面接対策を始める前に重要そうなアルゴリズムをピックアップして勉強しています。
本日はBFS(幅優先探索)について記事にしたいと思います。


## Breadth First Search（BFS）とは？

BFSは、グラフやツリーの探索アルゴリズムの1つで、起点から「近い順」に探索を進めていく手法です。

探索の流れは以下の通りです：

1. スタートノードから始める

2. その隣接ノードすべてを先に探索

3. 次に、その隣接ノードの隣接ノード…と進んでいく

---

## どんな場面で使う？
BFSは「最短経路を求めたいとき」や「すべてのノードを漏れなく探索したいとき」に便利です。


### 代表的な使用場面

| 使用場面               | 説明                                                         | 
| ---------------------- | ------------------------------------------------------------ | 
| 最短経路探索           | 迷路問題・地図アプリなどで、移動回数が最小のルートを見つける | 
| ソーシャルネットワーク | 「友達の友達」を検索する（距離が近い順）                     | 
| ルーティング           | ネットワーク経路検索やWebクローラー                          | 
| 状態探索               | パズルやゲームの状態空間を探索する場合など                   | 


## メリット・デメリット
| 項目       | 説明                                                                                                                            | 
| ---------- | ------------------------------------------------------------------------------------------------------------------------------- | 
| メリット   | 最短経路が保証される（全ての辺の重みが同じ場合）<br>全ノードを公平に探索できる                                                  | 
| デメリット | メモリ消費が大きい（キューと訪問済みリストを保持するため）<br>深さ優先探索（DFS）より非効率な場面もある（ゴールが深い場合など） | 


## Pythonでの実装例（迷路の最短経路）
以下は、BFSを使って迷路の最短距離を探索する例です

```python

from collections import deque

# 迷路（0は通れる, 1は壁）
maze = [
    [0, 1, 0, 0, 0],
    [0, 1, 0, 1, 0],
    [0, 0, 0, 1, 0],
    [1, 1, 0, 1, 0],
    [0, 0, 0, 0, 0]
]

# スタートとゴール
start = (0, 0)
goal = (4, 4)

# 移動方向（上下左右）
directions = [(-1,0),(1,0),(0,-1),(0,1)]

def bfs(maze, start, goal):
    rows, cols = len(maze), len(maze[0])
    visited = [[False]*cols for _ in range(rows)]
    queue = deque()
    queue.append((start[0], start[1], 0))  # (x座標, y座標, 移動回数)

    while queue:
        x, y, dist = queue.popleft()
        if (x, y) == goal:
            return dist  # 最短距離が見つかった

        for dx, dy in directions:
            nx, ny = x + dx, y + dy
            if 0 <= nx < rows and 0 <= ny < cols:
                if maze[nx][ny] == 0 and not visited[nx][ny]:
                    visited[nx][ny] = True
                    queue.append((nx, ny, dist + 1))
    
    return -1  # ゴールにたどり着けない

# 実行
shortest_path = bfs(maze, start, goal)
print("最短距離:", shortest_path)
```


## まとめ

* BFSは「近い順」にノードを探索するアルゴリズム

* 最短経路を探する場面に適している

場合によってはDFS(深度優先探索)と使い分ける必要があるようです。まだDFSは学習していないので手を付け次第記事にしたいと思います
Reactとバックエンドで作る！実践Web開発入門 | 第10章: 総括と次のステップ

## はじめに
第9章までで、Reactとバックエンドを使ったWebアプリをゼロから構築し、デプロイしました。今回はシリーズを振り返り、学んだことをまとめ、次のステップを提案します。

## 目標
- これまでの内容を総括する  
- 学んだ教訓を共有する  
- さらなる学習リソースとプロジェクト案を提供する  

## シリーズの振り返り
1. [**第1章**: 環境構築（ReactとNode.js/Express）。](https://qiita.com/BNR-Long/items/105e3805336a34680080)
2. [**第2章**: Reactで基本UIを構築。](https://qiita.com/BNR-Long/items/8c3a129a6d38674f44ae)
3. [**第3章**: バックエンドでRESTful APIを作成。](https://qiita.com/BNR-Long/items/9b3377956da27f36d83d)
4. [**第4章**: ReactとAPIを連携。](https://qiita.com/BNR-Long/items/9ce306515b63ef45e458)
5. [**第5章**: Context APIで状態管理。](https://qiita.com/BNR-Long/items/173cfdb4a7825111d988)
6. [**第6章**: MongoDBでデータ永続化。](https://qiita.com/BNR-Long/items/0ee5e5d2ce70455eef9b)
7. [**第7章**: JWT認証の実装。](https://qiita.com/BNR-Long/items/43e5b2473b9b71860062)
8. [**第8章**: VercelとHerokuでデプロイ。](https://qiita.com/BNR-Long/items/5a566a4fc1a5d6f7eee5)
9. [**第9章**: パフォーマンス最適化と検索機能。](https://qiita.com/BNR-Long/items/3d79d8b1df30a51096c6)

これで、フルスタックWebアプリの基礎を一通り習得しました！

## 学んだ教訓
- **モジュラー設計**: コンポーネントやAPIを小さく分割すると管理が楽。
- **エラー処理**: ユーザー体験を保つため、適切なフィードバックが重要。
- **環境変数**: 本番環境では秘密情報を安全に管理。
- **最適化**: 小さなアプリでもパフォーマンスを意識すると差が出る。

## よくある課題と解決策
- **CORSエラー**: バックエンドの`cors`設定を正しく調整。
- **APIレスポンス遅延**: キャッシュやデータベースインデックスを検討。
- **認証切れ**: トークンのリフレッシュ機能を追加可能。

## 次のステップ
### 学習リソース
1. **React公式ドキュメント**: HooksやConcurrent Modeを深掘り。
2. **Expressドキュメント**: ミドルウェアやルーティングを強化。
3. **MongoDB University**: 無料コースでデータベース設計を学ぶ。
4. **フルスタックコース**: UdemyやCourseraで体系的に復習。

### プロジェクト案
- **ToDoリスト**: ユーザーごとのタスク管理を追加。
- **ECサイト**: カート機能や支払い処理を統合。
- **リアルタイムチャット**: WebSocketで双方向通信を実装。

## 最後に
このシリーズで、Reactとバックエンドの基本を学び、実際のアプリを作り上げました。ここから先は、あなたのアイデア次第で無限の可能性が広がります。ぜひ挑戦を続けてください！

---

このシリーズが役に立ったら、ぜひ「いいね」や「ストック」をお願いします！質問や感想はコメント欄で気軽にどうぞ。これからも一緒に成長しましょう！
鍵いらずの生活へ！Flutter & Raspberry Piで自宅のオートロックをハック

# はじめに
新卒での上京を機に、新築マンションに引っ越しました。エントランスはオートロックで、自宅玄関の鍵は「セサミ4」で自動化しています。（セサミもハックしてラズパイに接続していますが、この話はまた別の機会に！）
今回は、オートロックを遠隔で解除するシステムを構築したので、その仕組みについて解説します。

# 使用した技術
本システムの構築には、以下の技術を使用しました。
- Flutter（操作用のWebアプリ）
- Raspberry Pi（リレー回路を制御）
- Node-RED（HTTPリクエストに応じてGPIOを制御）

# 適用条件
本システムが利用できる環境には、以下の条件が必要です。
- 自宅のインターホンがタッチパネル式であること
    - リレー回路を用いて「擬似タッチ」を発生させるため
- VPNやSSHを利用し、WANからLAN内に接続可能であること
    - ※セキュリティには十分配慮が必要

# システム概要
ラズパイ（Raspberry Pi）にリレータッチボード（スイッチサイエンス製）接続し、
MOSFETを介してGPIOで制御できるようにします。
1. Node-RED を利用し、HTTPリクエストを受信するとGPIOを0.5秒だけON/OFFするように設定
2. Flutterアプリ でUIを作成し、Node-REDにリクエストを送信
3. 擬似タッチ により、インターホンの「解錠ボタン」を操作
この構成により、スマホからオートロックを解錠できるようになります。

# 各工程の詳細
ここでは、それぞれの実装手順について詳しく解説します。
## リレー回路の接続方法
本システムでは[リレータッチボード（ドライバ有り）SSCI-024556](https://www.switch-science.com/products/2455)を使います。
秋葉原のいくつかの電子部品店においてあります。

スイッチングは3.3V(GPIOそのまま)では反応しなかったため、MOSFET([2N7000](https://akizukidenshi.com/catalog/g/g109723/))を用いて5Vをスイッチングすることにしました。
![図1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/200387/5bc15578-251d-4346-94cd-1c0aabcb7802.png)

![IMG_6343.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/200387/f43961a9-48f9-4643-be66-009c59fd95b7.jpeg)


リレータッチボードをインターホンの画面の解錠ボタンが表示される位置に両面テープで固定します。導電性のある両面テープを探しましたが、すぐには買えなかったため通常の両面テープと脱落防止のためマスキングテープで雑に固定しました。両面テープ程度の厚さなら反応するようです。

![IMG_6344.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/200387/1014c239-6345-4774-ad2e-616cae54cc45.jpeg)


## Node-REDのフロー設計
Node-REDは管理がしにくいため、できる限り簡素な作りにとどめる方が無難だと思います。
今回は、リクエストを受け取りGPIOをオンオフするだけにとどめてあります。
Node-Redにはラズパイ向けにGPIO制御の拡張機能がありますがUbuntu 24.04ではうまく動作しなかったため、自作のPythonスクリプトで対応しました。
![スクリーンショット 2025-04-04 113454.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/200387/80a7756f-87dc-472f-bf09-55140afb8361.png)


## FlutterでのUI作成
リクエストを送れればなんでも良かったのですが、２年前ぐらいにちょっと触ったのを思い出して採用しました。

ボタンをグリッドに並べただけの簡素な作りです。クリックイベントにリクエストを投げるようにしてます。

![スクリーンショット 2025-04-04 095941.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/200387/4687907c-6e08-4423-a6d7-599747badda9.png)

# システム全体
このような感じになってます。ラズパイには画面をつけたのでタッチで操作できます。
外装は3Dプリンターで作成する予定です。
![IMG_6345.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/200387/641e3443-14f2-4c54-a25c-4b9fe1c00fa5.jpeg)

# 限界と改善点
現状のシステムには、いくつかの課題があります。
1. VPNなしでは外部から操作できない
    - 現在はVPN経由でしかアクセスできず、一般的なユーザーには敷居が高い
2. Flutterアプリから直接ラズパイにリクエストを送信している
    - セキュリティ的に問題があるため、認証認可の仕組みを導入する必要がある
    - 操作履歴の記録（誰がいつ解錠したか）も必要

## 今後の改善策
- 認証認可サーバー を導入し、安全にアクセスできるようにする
- 専用のWeb API を用意し、Flutterアプリから直接ラズパイへアクセスしない構成にする
    - これにより、VPNなしでも安全に操作できる環境を構築する

# まとめ
このシステムが完成すれば、パートナーや友人が私の不在時でも帰宅できるようになります。
まだまだ改善すべき点は多いですが、開発を加速させていきます！

# エピソード（実際に助かった話）
出社初日、私は普段とは違うカバンを持って行ったのですが、鍵を入れ替えるのを忘れてしまいました。

帰りの電車でそのことに気づき、焦りながら帰宅。
「このままではマンションに入れない…！」

そこで、自作システムのことを思い出す。
駅から歩きながらVPNに接続し、スマホでWebアプリを開く。

マンションのオートロックの前で、インターホンを呼び出し、解錠ボタンをタップ。
すると——

「うぃーん…」（自動ドアが開いた）

ぶっつけ本番だったので、本当に動作するか不安だったが、問題なく解錠できた。
心からホッとした瞬間だった。

「もう鍵は忘れないようにしよう…」
VOICEVOX Engineを多重起動してnginxでロードバランシングしてみる

# はじめに
とある事情で毎時大量にVOICEVOXに合成音声を作ってもらいたいのですが、どうやらVOICEVOX Engineは複数の音声合成作業を同時に行ってくれないようです。

じゃあ、VOICEVOX Engineをたくさん用意して上手いことAPIアクセスを振り分ければいいのでは？

# 今回の検証環境
- ProLiant TM200 (Intel(R) Xeon(R) CPU D-1518) 上のProxmoxから 仮想6コア
- メインメモリ 6GB
- VOICEVOX Linux CPU版 v0.23.0

# VOICEVOX Engineを多重起動する

こういうことはsystemdに丸投げしてデーモン化してみましょう。今回はEngineを6機用意して検証します。

```text:/etc/systemd/system/voicevoxN.service
[Unit]
Description=VOICEVOX Engine Service(N)

[Service]
Type=simple
User=user
PIDFile=/run/voicevox.pid
ExecStart=/home/user/bin/VOICEVOX/vv-engine/run --port 5002N --cpu_num_threads 2
TimeoutStopSec=20
PrivateTmp=true
Restart=always

[Install]
WantedBy=multi-user.target
```
※`N`は連番で置き換えて複数のサービスを用意し、起動してください。

# nginxでロードバランシングする

ロードバランシングの方法には`Least-Connected`を採用します。

```nginx:default
upstream voicevox {
        least_conn; 
        server localhost:50021;
        server localhost:50022;
        server localhost:50023;
        server localhost:50024;
        server localhost:50025;
        server localhost:50026;
}

server {
        listen 80 default_server;
        server_name 172.16.0.nn;
        location / {
                proxy_pass http://voicevox;
        }
}
```

# 検証コード

下記の通りPythonで検証用のコードを書きました。

```python:voicevox_multi.py
import sys
import time
import requests
import datetime
import concurrent.futures

_ENGINE_HOST = "http://172.16.0.nn"
_VV_SPEAKER = 3
_SAMPLE_STR = "はじめましてなのだ。ボイスボックスずんだもんなのだ。"

args = sys.argv
max_workers = int(args[1]) if(len(args) > 1) else 1

voices_executor = concurrent.futures.ProcessPoolExecutor(max_workers=max_workers)

def createVoice(text, filename):
    audio_query_params = {'text': text, 'speaker': _VV_SPEAKER}
    audio_query_res = requests.post(_ENGINE_HOST + '/audio_query', params=audio_query_params, data=None)
    if audio_query_res.status_code != 200:
        return

    synth_params = {'speaker': _VV_SPEAKER}
    synth_res = requests.post(_ENGINE_HOST + '/synthesis', params=synth_params, data=audio_query_res.text.encode('utf-8'))
    if synth_res.status_code != 200:
        return

    with open(filename, mode='wb') as f:
        f.write(synth_res.content)

print("max_workers is setted to %d." % max_workers)
ts_start = datetime.datetime.now().timestamp()

for i in range(1, 101, 1):
    filename = "%03d.wav" % (i)
    voices_executor.submit(createVoice , _SAMPLE_STR, filename)

voices_executor.shutdown(wait=True)

ts_end = datetime.datetime.now().timestamp()
exec_sec = ts_end - ts_start

print("Exec. time: %fsec." % exec_sec)
```

# 実行時間

最大ワーカー数、すなわちAPIサーバーへの最大接続数を変えて検証コードの実行時間を計測しました。

|max_worker|実行時間(秒)|
|--:|--:|
|1|420.56|
|2|241.99|
|3|230.38|
|4|238.90|
|5|246.49|
|6|244.64|

最も効率の良くなった最大ワーカー数3の場合で、ワーカー数1のときより1.82倍高速という結果になりました。
Reactとバックエンドで作る！実践Web開発入門 | 第8章: アプリをデプロイする

## はじめに
第7章で認証機能を追加しました。今回は、完成したアプリを本番環境にデプロイします。ReactフロントエンドとNode.jsバックエンドをそれぞれVercelとHerokuにデプロイする方法を学びます。

## 目標
- Reactアプリをビルドしてデプロイする  
- バックエンドをクラウドにデプロイする  
- フロントエンドとバックエンドを連携させる  

## フロントエンドのデプロイ（Vercel）
### 1. Reactアプリのビルド
`frontend`フォルダで以下を実行：
```bash
npm run build
```
`build`フォルダが生成されます。

### 2. Vercelのセットアップ
1. Vercel CLIをインストール：
   ```bash
   npm install -g vercel
   ```
2. `frontend`フォルダでログイン：
   ```bash
   vercel login
   ```
3. デプロイ実行：
   ```bash
   vercel
   ```
   - 指示に従い、設定を進めます（デフォルトでOK）。
   - 完了後、URL（例: `https://your-app.vercel.app`）が提供されます。

### 3. 環境変数の設定
バックエンドのURLをReactで使うため、`frontend/.env`を作成：
```
REACT_APP_API_URL=https://your-backend.herokuapp.com
```
`App.tsx`の`fetch`を修正：
```tsx
const response = await fetch(`${process.env.REACT_APP_API_URL}/products`);
```
ビルドし直し、再デプロイ：
```bash
npm run build
vercel --prod
```

## バックエンドのデプロイ（Heroku）
### 1. Herokuの準備
1. Heroku CLIをインストール（公式サイトから）。
2. `backend`フォルダでログイン：
   ```bash
   heroku login
   ```

### 2. アプリの設定
1. Herokuアプリを作成：
   ```bash
   heroku create your-backend
   ```
2. MongoDBをクラウドで使用する場合（例: MongoDB Atlas）：
   - Atlasで無料クラスタを作成し、接続文字列を取得。
   - `index.js`の`mongoose.connect`を更新：
     ```javascript
     mongoose.connect('mongodb+srv://<username>:<password>@cluster0.mongodb.net/productdb', {
       useNewUrlParser: true,
       useUnifiedTopology: true,
     });
     ```
3. Heroku環境変数を設定：
   ```bash
   heroku config:set JWT_SECRET=secret_key
   ```

### 3. デプロイ
1. `backend`に`Procfile`を作成：
   ```
   web: node index.js
   ```
2. Gitで管理：
   ```bash
   git init
   git add .
   git commit -m "Initial commit"
   ```
3. Herokuにプッシュ：
   ```bash
   heroku git:remote -a your-backend
   git push heroku main
   ```

### 4. 動作確認
`https://your-backend.herokuapp.com/products`にアクセスし、データが返るか確認。

## フロントエンドとバックエンドの連携
- Reactの`REACT_APP_API_URL`をHerokuのURLに設定。
- CORSを調整（`backend/index.js`）：
  ```javascript
  app.use(cors({ origin: 'https://your-app.vercel.app' }));
  ```
- 再デプロイ（HerokuとVercel両方）。

## 動作確認
1. `https://your-app.vercel.app`にアクセス。
2. ログインし、商品リストの表示と追加をテスト。

## 次回予告
第9章では、パフォーマンスの最適化と追加機能を学びます。お楽しみに！

---

この記事が役に立ったら、ぜひ「いいね」や「ストック」をお願いします！質問や感想はコメント欄で気軽にどうぞ。次回も一緒に学びましょう！
あなたはハラスメントしてないですか？

（久々のQiita投稿。。。）
# ・はじめに
昨今○○ハラスメントって言葉をよく耳にします。
ハラスメントについてしっかりと把握出来ていないので少し調べてみました。
.
ハラスメントってなんだろう？簡単に言うと
#### ハラスメント＝嫌がらせ
嫌がらせをされたら誰でも嫌ですよね。

ハラスメントについて調べると
「こんなに！？」とハラスメントがもりもり出てきます。
.
.
これがハラスメントだ！という決まりは定められてなく
時代によって変わったりするそうです
難しいですよね…
.
.
ハラスメントをすることによるデメリットとは？

## ・ハラスメントをすることによるデメリット
自分がハラスメントをしたらどうなるか。

##### ハラスメントされた側(受けた側)
実際出せる業務のパフォーマンスが著しく低下する
→仕事が終わらないので周りにも負担がかかる
→された側の人は休職又は退職することで人手が足りなくなる
目に見えて状況が悪化していくのが想像出来ると思います
.
他の人も疲弊しドミノ倒しのようにどんどん倒れていきます。
.
また仕事だけではなく私生活にも悪影響を及ぼすことになるため
良いことが何１つとしてないですよね

##### ハラスメントした側
ハラスメントの事実が会社にバレた際は降格、更に左遷もあり得ます。
最悪の場合懲戒処分になり、再就職などは絶望的になるかと思います。
.
.
.
.
逆にいうとハラスメントをしなければ働きやすい環境を作ることによって
みんなのパフォーマンスは上がるとも言えます！
…ちょっと大げさに言っていますがハラスメントをしないことは
#### 職場を守り、自分も守ることが出来ます！
ハラスメントについて知ること、しないことはとても大事だと思いませんか？
知らないうちにハラスメントをしてしまってないか今一度確認してみましょう！
.
ここでどんな事がハラスメントになるのかいくつか紹介します

### ・パワーハラスメント(パワハラ)
ハラスメントの中で１番多いのではないでしょうか？
環境における地位が高い者が他の人に対して行う言動で当人又は周りを傷つけることです。
.
毎日のように理不尽に怒鳴る
過剰な業務の押し付け
特定の人物を無視、仲間外れ
物を投げるや殴るの暴力
.
例を挙げたらいくらでも挙がるかと思います
.
またされた経験がある人も多いのではないでしょうか？
.
余談ですが自分は新卒で入社した会社で殴る、壁に叩きつけられる、人格否定
オンパレードを食らった経験があります(;_:)
.
なのでされたら辞めろまでは言いませんが、我慢する必要はないので心当たりある方は
職場の同期、友達、家族など周りの人に相談を…！

### ・セクシュアルハラスメント(セクハラ)
１番有名なハラスメントだと思います
触った！セクハラだ！なんてやり取りをしたことある人も少なくないかと思います。
.
.
ですが実際のセクハラはそんな軽く言えるものではないです。
.
.
立場を利用して不用意なボディタッチを行う
何かする代わりに性的な対価を要求する
性的なこと連想する発言でもアウトです。
.
連絡先を必要以上に聞くこともハラスメントにあたります。
.
.
また個人に対してだけではなく
休憩スペースなど他の人がいる場で性的な話をするのもアウトです。
耳にするだけでも嫌悪感を覚えるからです。
.
職場では控えておくことが無難です。
.
.
性的なことだけでなく
女なんだから。
男なんだから。
などの性別においての固定概念を発言することもNGです。
最近では**ジェンダーハラスメント**などとも言うそうです。

### ・マタニティハラスメント（マタハラ）
実は3大ハラスメントと言われるほど多いハラスメント
.
女性の方は出産前～産後でどうしても休まなくてなりません。
育児休暇も合わせると最長で2年間ほど職場から離れることになります。
.
その女性労働者に対して
休むなら辞めてほしい
休まれると迷惑がかかるの分かってる？
など精神的に追い込むことはハラスメントにあたります
.
妊娠育児休暇を理由に役職をはく奪、契約を更新しないこともNGとされています。
.
また男性労働者の育児に関するハラスメントを
**パタニティハラスメント（パタハラ）** と言います


### ・テクノロジー・ハラスメント(テクハラ)
ITに関して相手が知らないと分かっていて専門用語を沢山使い、
分からないことを責めたり見下したりすることです
.
新卒も入り勉強不足だったり経験が少ない方も職場に何人かいるかと思います
○○について調べておいてねなど
知らないことに関して責めたりせずに接するとハラスメントにならずに済むと思います


# ・最後に

以上４つ紹介させていただきました

紹介したもの以外にも沢山のハラスメントが存在しますので是非調べてみてください
.
コミュニケーションをとることでハラスメントが減るとも言われています
相手の人となりを知ることで何をされるのが嫌なのか知れればハラスメントを回避出来そうですよね
.
.
より良い環境作りをするためにハラスメントをみんなで気をつけましょう！
【Kubernetes】ノードに均等にPodを配置する topologySpreadConstraints



# topologySpreadConstraints
DeploymentなどのPodテンプレートに指定することで、Podがスケジュールされるときに、指定したトポロジキー（例えばノードやAZ）に対して偏りなく配置されるよう制約をかけるものです。

https://kubernetes.io/ja/docs/concepts/scheduling-eviction/topology-spread-constraints/?utm_source=chatgpt.com

## 例

```yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: netshoot
spec:
  selector:
    matchLabels:
      app: netshoot
  replicas: 3
  template:
    metadata:
      labels:
        app: netshoot
    spec:
      topologySpreadConstraints:
        - maxSkew: 1
          topologyKey: "kubernetes.io/hostname"
          whenUnsatisfiable: ScheduleAnyway
          labelSelector:
            matchLabels:
              app: netshoot
      containers:
        - name: netshoot
          image: nicolaka/netshoot
          command: ["sleep", "infinity"]
          resources:
            limits:
              cpu: "125m"
              memory: "100Mi"
            requests:
              cpu: "125m"
              memory: "100Mi"

```

パラメータ	意味
- **maxSkew**
各トポロジ単位（ノードなど）でのPod数の最大の偏り幅。
例：1なら「多いところと少ないところで1個までの差」しか許さない。
- **topologyKey**
分散対象の単位となるノードラベルを指定します。
    - `kubernetes.io/hostname` : ノード単位
    - `topology.kubernetes.io/zone` : AZ単位
- **whenUnsatisfiable**
制約を満たせないときの動作。
    - `DoNotSchedule`：Podのスケジューリングを拒否
    - `ScheduleAnyway`：偏ってもスケジューリングする
- **labelSelector**
このラベルセレクタに一致するPodが分散対象となります。分散対象となるPodのラベルセレクター。
例えば、app: my-app のPodを分散対象にする。
RaspberryPi Pico と trykernelで dualcoreマイコン開発　その1 ベースソフトと開発環境

## 開発環境

・windows11
・Pico-Visual Studio CodeというVSCodeを使ったIDEで開発していきます。
・詳細はInterface誌2024年11月号を参考に構築します。
・pico sdk 1.5.1を使用します。
・環境構築は、下記のInterfaceの特設サイトに詳しく載っています。
　https://interface.cqpub.co.jp/202411multicoreos/

## ベースソフト

・ベースソフトは以下の構成で作ります。Interface誌2024年11月号の特集、第6部で紹介されているハイブリッドシステムをベースに進めます。
・理由としてはBTstackやTCP/IPstackなどを最終的に載せたいのですが、、、１から移植する腕がないためです。
・基本的に開発していくのはコア1のほうで、コア0はミドルウェアなどの実装を考えています。

![test.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3657924/b33afdde-bb11-4f1a-8b82-e7f4c66605f1.png)

今日はここまで
postfix で DKIM の実装をしてみる

# 対象とか
https://qiita.com/je3kmz/items/16d678b85118539c8972
# 目的
上記の記事では sendmail を調理しました
今回は postfix で簡単便利に仕上げてみますw

っま、殆ど大差ないけん差異だけ書いとくけん、読みにくさはご勘弁m(__)m
# mail/postfix のインスコ
```shell:ports skeleton からインスコの場合
cd /usr/ports/mail/postfix && make install && make clean
```
```shell:バイナリ pkg(8) からインスコの場合
pkg install postfix
```
パイプの作成場所の元ディレクトリに所有者とグループを標準のアカウントに変更ってトコが、postfix 用にオーナーを変えておく必要があるが、逆に postfix 側の実行権限を変えても良き🥰
```shell:この例じゃと postfix に寄せてるw
mkdir /var/run/milteropendkim && chown postfix:postfix /var/run/milteropendkim/opendkim.pipe
sysrc milteropendkim_gid=postfix
sysrc milteropendkim_uid=postfix
```
* main.cf 書き換えるよ～✌
```config:/usr/local/etc/postfix/main.cf
smtpd_milters = unix:/var/run/milteropendkim/opendkim.pipe
non_smtpd_milters = $smtpd_milters
milter_default_action = accept
```
あ、コレ、追記しんちゃい。
* daemon の起動許可及び起動
postfix も働かせよう
```shell
service postfix enable
service postfix start
```
当然じゃが、main.cf の基本的な設定は済ませんちゃいな(下記に雛型乗せとく)
# ログの確認方法
```log:正常時起動ログ
*日付* *ホスト名(短縮)* opendkim[PID]: OpenDKIM Filter v2.10.3 starting (args: -l -u postfix -P /var/run/milteropendkim/pid -x /usr/local/etc/mail/opendkim.conf)
```
-u の引数が milteropendkim_uid の内容となっていれば◎
# postfix の main.cf 雛型?
```config:postconf -n 実行結果
alias_database = hash:/etc/mail/aliases
alias_maps = hash:/etc/mail/aliases
command_directory = /usr/local/sbin
compatibility_level = 3.9
daemon_directory = /usr/local/libexec/postfix
data_directory = /var/db/postfix
debug_peer_level = 2
home_mailbox = Maildir/
html_directory = /usr/local/share/doc/postfix
inet_protocols = ipv6
mail_owner = postfix
mailq_path = /usr/local/bin/mailq
manpage_directory = /usr/local/share/man
meta_directory = /usr/local/libexec/postfix
milter_default_action = accept
mydomain = *ホスト名*
mynetworks = [::1]/128 [2001:db8::]/48
mynetworks_style = host
newaliases_path = /usr/local/bin/newaliases
non_smtpd_milters = $smtpd_milters
queue_directory = /var/spool/postfix
readme_directory = /usr/local/share/doc/postfix
sample_directory = /usr/local/etc/postfix
sendmail_path = /usr/local/sbin/sendmail
setgid_group = maildrop
shlib_directory = /usr/local/lib/postfix
smtp_tls_CApath = /etc/ssl/certs
smtpd_milters = unix:/var/run/milteropendkim/opendkim.pipe
smtpd_tls_CAfile = /usr/local/share/certs/ca-root-nss.crt
smtpd_tls_auth_only = no
smtpd_tls_cert_file = /usr/local/etc/dehydrated/certs/*dehydratedで作ったディレクトリ名*/fullchain.pem
smtpd_tls_key_file = /usr/local/etc/dehydrated/certs/*dehydratedで作ったディレクトリ名*/privkey.pem
smtpd_tls_loglevel = 1
smtpd_tls_security_level = may
smtpd_tls_session_cache_database = /var/db/postfix/smtpd_scache
unknown_local_recipient_reject_code = 550
```
# 誰かの役にたてたらいいな
時が経て記事の鮮度が落ちた時には是非追試記事を書いてみてください
【Go】crypto/rsaを使用して暗号化、復号化を実装してみた


## 概要

- [**暗号技術入門 第3版**](https://amzn.asia/d/4EzqngT)に出てきたRSA暗号を使用した暗号化、復号化を実装してみた
- crypto/rsaを使用

## サンプルコード


```go
package rsademo

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha256"
	"encoding/base64"
	"fmt"
	"log"
)

// RSAによる暗号化
func encryptRSA(pub *rsa.PublicKey, message string) (string, error) {
	ciphertext, err := rsa.EncryptOAEP(sha256.New(), rand.Reader, pub, []byte(message), nil)
	if err != nil {
		return "", err
	}
	return base64.StdEncoding.EncodeToString(ciphertext), nil
}

// RSAによる復号化
func decryptRSA(priv *rsa.PrivateKey, ciphertext string) (string, error) {
	decodedCiphertext, err := base64.StdEncoding.DecodeString(ciphertext)
	if err != nil {
		return "", err
	}
	plaintext, err := rsa.DecryptOAEP(sha256.New(), rand.Reader, priv, decodedCiphertext, nil)
	if err != nil {
		return "", err
	}
	return string(plaintext), nil
}

func main() {
	// RSA鍵ペアを生成
	privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
	if err != nil {
		log.Fatalf("鍵ペアの生成に失敗しました: %v", err)
	}
	publicKey := &privateKey.PublicKey

	// 平文
	message := "Hello, World!"

	// 暗号化
	ciphertext, err := encryptRSA(publicKey, message)
	if err != nil {
		log.Fatalf("暗号化に失敗しました: %v", err)
	}
	fmt.Println("暗号化されたデータ:", ciphertext)

	// 復号化
	decryptedMessage, err := decryptRSA(privateKey, ciphertext)
	if err != nil {
		log.Fatalf("復号化に失敗しました: %v", err)
	}
	fmt.Println("復号化されたデータ:", decryptedMessage)
}

// 出力
// 暗号化されたデータ: RAAYXaNj4lLeGpoBq3hjEmVQwVyQJdREK9jA8X56jwgGit6NB4ygvHtog7coZ1e72TPfkRDjbjUrL4gXKnVi6JjcfBwnBRIzxDnT5V4baceIfE4rlJFFv0g8Ut6vFLDPiKWnsmfqAu8Vmt2iejP6r8cADdwkK8HMs4/bH+VSzzDgabQJOeT7p4/i5xTynNZifrH2hYK7lwrh6FwojDMex/gb1XxQrtpwseZdYxZqB/YgYQNZY/DHnQYZ3QL3Ev6e68qPUvUqAsg7qWZ3SlyfEu7nWQhUslB6C6mAVohpNbQ+Ks5rjG1u8yE5Q7PJVDmDJutpXerL1dd0z4I1vb1F3A==
// 復号化されたデータ: Hello, RSA Encryption!
```
pnpmでモノレポ構成時のパッケージ参照方法

# はじめに
`pnpm`コマンドを使って、モノレポ構成のプロジェクトを作っています。

私がよく使う場面としては、**クライアントサイドをReact** で、**サーバーサイドをexpress**、そしてそれらのやり取りで使う **共通のタイプを定義するパッケージプロジェクト**の３つがあるときです。

:::note info
#### 参考: モノレポ(Monorepo)構成
複数のプロジェクトやパッケージを１つのリポジトリで管理する手法。
##### 特徴
- **１つのリポジトリに複数のプロジェクト** が含まれる
- それぞれのプロジェクトが **パッケージ（モジュール）** として管理される
- **パッケージ間で依存関係を持てる** （共有コードが簡単に利用できる）
:::

# :skull_crossbones:今回解決する課題: パッケージの定義を参照できない
Reactやexpressから、共通タイプとして定義したプロジェクトを参照しようとすると、そんなパッケージありませんというエラーが出ます。

具体的には👇このエラーが出るという問題です。
```sh
> pnpm add @my-monorepo/my-shared-types --filter clnt-react
ERR_PNPM_FETCH_404  GET https://registry.npmjs.org/@my-monorepo%2Fmy-shared-types: Not Found - 404
```
**本家npmサーバーを見に行って、ないよと言っている**。そりゃないよ！自分の足元を見てよ！という話。

パッケージの`package.json`に`"name": "@my-monorepo/my-shared-types"`と書いてあっても、`pnpm install`しても、`@my-monorepo/my-shared-types@workspace`と呼んでも（これ正しいのかわからない）、ｋできません。

# :bulb:解決方法
利用するプロジェクトの`package.json`へ、**直接手入力で** 依存関係を書きます。
```json:package.json(一部)
"dependencies": {
    "@my-monorepo/my-shared-types": "workspace:*"
}
```

# おわりに
調べるのにいちいち時間がかかるので自分向け備忘録でした。誰かの参考になれば幸いです。:smiley:

pnpmコマンドを使わず手で追記しないといけないというのは、Geminiさんが教えてくれました。ChatGPTさん、perplexityさんの言うことを素直に聞いてコマンドで頑張ると、沼に落ちます。(2025/4時点)

ではよいpnpmライフを！
"🐍 Pythonで簡単に学べる！1時間、30分でマスターできるウェブ開発💻"

こんにちは、韓国在住のsanaです！

こんにちは！😄 Pythonを学びたい皆さん！今日は、Pythonを使った楽しいウェブ開発の講座をご紹介します🎉 これらの講座は初心者でも簡単に進められる内容で、Pythonを使って実用的なプロジェクトまで作れるようになります！🚀



1️⃣ 1時間で終わるSpringBoot RESTAPI
👉 この講座では、PythonとSpringBootを使ってREST APIを作る方法を学べます。短時間でポイントをしっかり学べるので、忙しい方にもぴったり！⏳

https://u.inf.run/41VQjsD


2️⃣ ChatGPT Python株自動売買アプリWeb投資リポート作成
👉 株投資に興味がある方は、ChatGPTを使った株自動売買アプリを作ってみましょう！この講座では、自動で投資リポートを作成してくれるアプリを作る実習を通じて、Pythonを楽しく学べます📈

https://u.inf.run/4iTYax3

3️⃣ Jeju Coding Web開発30分要約
👉 ウェブ開発を始めたい方に必須の講座！たった30分でウェブ開発の基本を理解し、初めてのプロジェクトを完成させることができます🌐

https://u.inf.run/42jbVy5

ぜひ、今日からPython挑戦してみてください💪　ファイティン！
QAが応用できるClean Code


# はじめに

こんにちは、QAエンジニアのヨシナです。

ここ半年間、ウォーキングコンテストをきっかけにオーディオブックを聴くようになりました。（ちなみに、1カ月で78万歩を歩き、3位になりました）
最初は歩きながらソフトウェア開発やマネジメントに関する書籍を聴いていましたが、ウォーキングコンテストが終わった今は、主に電車の中でオーディオブックを楽しんでいます。最近は『Clean Code』という本を聴き、そこからQAの自動テストに活かせるポイントをいくつか見つけました。

本記事では、『Clean Code』から学んだ内容を、QAの視点で解説していきます。

# Clean Codeとは？

『[Clean Code](https://amzn.asia/d/gtVmE8Q)』は、そのタイトル通り、きれいなコードを書くための手法や考え方を学べる本です。具体的には「コードを洗練させる過程で必要な判断やアプローチ」について詳しく解説されています。

全528ページの書籍で、Javaを使ったサンプルコードが豊富に掲載されており、実践的な内容が多いのが特徴です。

# テストについて

本書は主にコードを書く際の指針について述べられていますが、9章ではテストに関する重要なポイントが解説されています。ユニットテストが中心ですが、QAの自動テストにも応用できる内容がいくつかありました。特に以下の3点が参考になりました。

* **テストはきれいに保つべき**
* **1つのテストケースで確認する内容を最小限にするべき**
* **FIRST**

以下、それぞれのポイントについて詳しく説明します。

# QA自動テストに応用可能なポイント

## テストは綺麗に保つべき

テストをきれいに保つことで、メンテナンスコストを最小限に抑えることができます。メンテナンスコストが増えると、テストの実行が困難になり、最終的にテストが使われなくなるリスクがあります。

テストが除外されると品質の低下につながるため、テストは常にきれいで読みやすい状態を維持することが重要です。長期的な運用を考える際の大事なポイントです。

## テストケースで確認する内容を最低限にすべき

1つのテストケースで複数の確認を行うと、1つのチェックでエラーが発生した場合に後続の確認が実行されなくなる可能性があります。このような状態になると、バグの早期発見が難しくなり、結果的に修正コストが増大します。

また、確認内容が多すぎるとテストフローが複雑になり、可読性が低下します。これを防ぐため、本書では以下のシンプルなフローを推奨しています。

1. セットアップ
2. テスト実行
3. 確認

この手順を守ることで、テストケースが明確になり、可読性が向上します。

## FIRST

良いテストの条件として「FIRST」という概念が紹介されています。以下はその略語の意味と説明です。

|文字 | 意味 | 説明 |
| --- | --- | --- |
| F | Fast（早い）| テストは迅速に終わるべき |
| I |	Independent（独立している） |	テストケース同士が依存しない状態を保つ |
| R |	Repeatable（再現可能） |	どんな環境でも同じ結果が得られる |
| S	| Self-Validating（自己検証可能） |	テスト結果がすぐに分かる状態である |
| T |	Timely（タイムリー） |	適切な時間内でテストが書ける |

この5つの条件を満たすことで、効率的かつ信頼性の高いテストケースを作成することができます。

# まとめ

私たちのQAチームでは、FIRSTの原則のうちいくつかは既に実践できています。例えば、自動テストの実行時間は約30分であり、実行速度を意識した運用を行っています。

しかし、複数環境でのテスト実行が現状では難しく、またテストケースの可読性が低いため、理解に時間がかかるという課題があります。この点についてはまだチーム内で具体的な話し合いが行われておらず、今後の課題として相談していきたいと考えています。

---

# KIYOラーニング株式会社について

当社のビジョンは『世界一「学びやすく、分かりやすく、続けやすい」学習手段を提供する』ことです。革新的な教育サービスを作り成長させていく事で、オンライン教育分野でナンバーワンの存在となり、世界に展開していくことを目指しています。

https://kiyo-learning.com/

## プロダクト

* [スタディング](https://studying.jp/)：「学びやすく・わかりやすく・続けやすい」オンライン資格対策講座
* [スタディングキャリア](https://career.studying.jp/)：資格取得者の仕事探しやキャリア形成を支援する転職サービス
* [AirCourse](https://aircourse.com/)：受け放題の動画研修がついたeラーニングシステム（LMS）

### KIYOラーニング株式会社では一緒に働く仲間を募集しています

https://www.wantedly.com/companies/kiyo-learning/projects

https://herp.careers/v1/kiyolearning
WeasyPrintで自動改ページされるページがあると出力が途中で止まる現象

# 現象

A4サイズで印字することを想定したPDFをWeasyPrintで生成しようとしたところ改行できず処理が途中で止まってしまった。

* PDF化するHTMLを作成

```html:test.html
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>PDF Sample</title>
    <style>
        body {
            /* A4用紙を意識したサイズ指定 */
            width: 172mm;
            height: 251mm;
            page-break-after: always;
        }
    </style>
</head>
<body>
    <h1>あ</h1>
    <h1>い</h1>
    <h1>う</h1>
    <h1>え</h1>
    <h1>お</h1>
    <h1>か</h1>
    <h1>き</h1>
    <h1>く</h1>
    <h1>け</h1>
    <h1>こ</h1>
    <h1>さ</h1>
    <h1>し</h1>
    <h1>す</h1>
    <h1>せ</h1>
    <h1>そ</h1>
    <h1>た</h1>
    <h1>ち</h1>
    <h1>つ</h1>
    <h1>て</h1>
    <h1>と</h1>
    <h1>な</h1>
    <h1>に</h1>
    <h1>ぬ</h1>
    <h1>ね</h1>
    <h1>の</h1>
</body>
</html>

```

* HTML出力結果（一番下までスクロールした結果）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3618505/fa09b879-56e1-4268-ad8b-ac8e55384a6d.png)
    * 最後まで出力できている

* このHTMLをWeasyPrintでPDF化してみる

```python:code.py

from weasyprint import HTML

html_content = open("./test.html","r",encoding="utf-8")

# HTMLからPDFを生成して保存
HTML(string=html_content.read()).write_pdf("./output.pdf")

```

* PDF出力結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3618505/13909a2a-186d-4a4b-868e-4241ef9c6bdf.png)
    * 改ページされるタイミングあたりで尻切れになって出力されてしまった

# 解決

* bodyのCSSからheightの指定を無効化してみると想定通りのPDFが出力された
```html:test.html
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>PDF Sample</title>
    <style>
        body {
            /* A4用紙を意識したサイズ指定 */
            width: 172mm;
            /* height: 251mm; ここをコメントアウト！！ */
            page-break-after: always;
        }
    </style>
</head>
<body>
    <h1>あ</h1>
    <h1>い</h1>
    <h1>う</h1>
    <h1>え</h1>
    <h1>お</h1>
    <h1>か</h1>
    <h1>き</h1>
    <h1>く</h1>
    <h1>け</h1>
    <h1>こ</h1>
    <h1>さ</h1>
    <h1>し</h1>
    <h1>す</h1>
    <h1>せ</h1>
    <h1>そ</h1>
    <h1>た</h1>
    <h1>ち</h1>
    <h1>つ</h1>
    <h1>て</h1>
    <h1>と</h1>
    <h1>な</h1>
    <h1>に</h1>
    <h1>ぬ</h1>
    <h1>ね</h1>
    <h1>の</h1>
</body>
</html>

```
* PDF出力結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3618505/dd5cdaf9-cb51-4969-aa92-e053f69bd1c5.png)
    * ちゃんと出た。

# あとがき

* ここでは書かなかったが現象が発生したPDFでは複数のHTMLを組み合わせてその繋ぎ目で明示的な改ページを行なっておりそちらは正常に改ページできていた。
* そこから推測するにおそらくPDFとして出力する基準がbodyの範囲になっており、そこの範囲外に出力対象があると処理が途中で止まってしまうっぽい
* HTMLの方も本来はPDFと同じ出力結果になるけどブラウザが気を効かせてはみ出した部分も出力してくれていたものと思われる
* ロジック要因っぽい現象だったためまさかCSSが絡んでいると思わず原因の特定にかなり時間を食った
    * WeasyPrintのバグを疑った・・・ごめんなさい・・・
個人的備忘録：アルゴリズムって結局どう違うの？基本をざっくり記事にアウトプットしてみた

## はじめに
アルゴリズムは、問題解決の手順や処理の流れを定義したものです。**プログラミングやソフトウェア開発を行う上で、効率的かつ再現性のあるコードを書くために必要不可欠な知識です。**

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

ここでは、自分用の備忘録として基本的なアルゴリズムの種類や特徴を簡潔にまとめておきます。

## 書こうと思ったきっかけ
実務や学習中に「***アルゴリズムの違いって何だっけ？***」と迷うことが多くなり、調べ直す機会が増えてきました。

https://techplay.jp/column/298

理解を深め、今後効率よく活用するためにも、よく使うアルゴリズムを体系的に整理しておきたいと思いました。

## よく使われる基本アルゴリズム

### ソートアルゴリズム
- **バブルソート**：隣接する要素を比較して交換。処理は単純だが遅い。
- **選択ソート**：最小値（最大値）を見つけて順に並べる。
- **挿入ソート**：既に整列済みの部分に要素を挿入していく。
- **クイックソート**：分割統治法を利用。実用的で高速。
- **マージソート**：分割してからマージする。安定性あり。

### 探索アルゴリズム
- **線形探索**：先頭から順に確認する。小規模データに向く。
- **二分探索**：整列済みデータに対して中央を基点に探索。

### グラフアルゴリズム
- **幅優先探索（BFS）**：キューを使って段階的に探索。
- **深さ優先探索（DFS）**：スタック（再帰）を使って深く探索。
- **ダイクストラ法**：最短経路問題を解くアルゴリズム。
- **ワーシャルフロイド法**：全点対最短経路。

### その他の重要なアルゴリズム
- **動的計画法（DP）**：部分問題の解を再利用して効率化。
- **貪欲法（Greedy）**：局所的に最適な選択を繰り返す。
- **分割統治法（Divide and Conquer）**：問題を分割して統合する。

**参考にした文献**

https://web-camp.io/magazine/archives/110411/

https://qiita.com/square1001/items/6d414167ca95c97bd8b2

## まとめ
アルゴリズムは「どう処理するか」の視点で選択する必要があります。問題の特性やデータ量に応じて、適切な手法を使い分けることが重要です。今後も実装や実務を通じて、応用力を磨いていきたいと思います！