OmniAICreator/Qiita-1.07M huggingface dataset:
Apache Proxy 動作確認

プロキシサーバー構築・動作確認


前提条件
・パブリックサブネットに1台のWebサーバー（プロキシサーバー）
・プライベートサブネットに1台のWebサーバー（バックエンドサーバー）


パブリックサブネットのEC2で実行する手順
--
１．Apacheをインストールする
```bash
dnf install httpd -y
```

２．Apacheを自動起動設定にする

```bash
systemctl enable httpd
```

３．Apacheのユニットファイルが生成されたことを確認する
```
systemctl list-unit-files -t service | grep httpd
```

メモ
・ユニットファイルの有効化
　・ユニットファイルが有効になることで、システム起動時に自動的にそのサービスが開始されます。

・httpd.service と default.target
　・httpd.service ユニットファイルが default.target にリンクされることで、システム起動時にこのサービスが起動されるようになります。
以下
```bash
systemctl list-dependencies $(systemctl get-default)

graphical.target
○ tqdisplay-manager.service
○ tqsystemd-update-utmp-runlevel.service
● mqmulti-user.target
●   tqamazon-ssm-agent.service
●   tqatd.service
●   tqauditd.service
●   tqchronyd.service
○   tqhibinit-agent.service
●   tqhttpd.service
```


４．モジュールの確認をする
```bash
cat /etc/httpd/conf.modules.d/00-proxy.conf | grep "proxy_module modules/mod_proxy.so"
cat /etc/httpd/conf.modules.d/00-proxy.conf | grep "proxy_http_module modules/mod_proxy_http.so"
```

メモ
・proxy_http_module modules/mod_proxy_http.so
有効化することで、HTTPプロトコルを使用してバックエンドサーバーと通信することができる

・proxy_module modules/mod_proxy.so
Apacheにプロキシ機能を有効化し、Apacheはリクエストを他のサーバーに転送することができる

５．プロキシの設定をする

```bash
vi /etc/httpd/conf.d/reverse-proxy.conf
```

６．以下を追記する
```
<VirtualHost *:80>
    ServerName localip  # プロキシサーバのローカルIP

    ProxyPreserveHost On
    ProxyPass / http://backendip/  # バックエンドサーバのIP
    ProxyPassReverse / http://backendip/
</VirtualHost>
```

メモ：
ServerName: プロキシサーバのIPアドレスを指定する
ProxyPreserveHost: 元のホスト名を保持する設定
ProxyPass と ProxyPassReverse: バックエンドサーバのIPアドレスを指定し、リクエストを転送する設定


６．Apacheを再起動する
```bash
systemctl restart httpd
```

７．Apacheのステータス確認
```bash
systemctl status httpd

```


プライベートサブネットに配置したEC2の設定をする
---
１．Apacheをインストールする

```bash
dnf install httpd -y
```

```bash
systemctl enable httpd
```

３．SELinuxを無効化する
```bash
vi /etc/selinux/config
```

・ファイル内の行を以下のように変更します。
```
SELINUX=disabled
```

４．再起動をする

```bash
reboot
```

５．SELinuxが無効化されたことを確認する

```bash
getenforce
Disabled
```

６．Apacheのステータス確認

```bash
systemctl status httpd
```

７．コンテンツファイルを作成する

```bash
echo "<h1>Private Web Server</h1>" > /var/www/html/index.html
```

８．コンテンツファイルが作成されたか確認をする
```bash
curl localhost
```

```html
<h1>Private Web Server</h1>
```
ブラウザ上での確認
プロキシサーバのパブリックIPにブラウザでアクセスし、バックエンドサーバからのコンテンツが表示されることを確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3146038/f02b4dbb-4564-4d29-d843-f256768a6e2b.png)



備考
--
メモの部分をまとめることを途中から重視していました
ずっと前から整理したく、、こんなことしてるから時間が足りない
terraform基礎

## 最初に

terraformを業務で使用することになりました。
そのため勉強したことをまとめています。
terraformで構築したことが多いため構築メインで書くことが多いです。

# terraformとは

IaC(Infrastructure as code)に一種。
インフラをコードで定義し構築/運用/削除を行うことが可能
インフラを構築等することは可能ですがサーバ内の管理（ツールのインストール等）は行うことはできないためAnsible等を組み合わる必要がある。
terraformで構築できる対象はAWS/GCP/Azure等と幅広いプラットフォームに対応している。

# terraformの利点

インフラをコード化できるためもし同じ環境を構築したいとなった場合作業工数が少なく済む

コード化していることで作業ミス（作業漏れ）を防ぐことができる

公式ドキュメントがわかりやすい

# terrformのファイルについて

①tfファイル
terraformでインフラを構築する場合に作成するファイル。
main.tf/outputs.tf等の名前で作成することが多いように思います。
.tf前のmain/outputsについてはルールはないためわかりやすいファイル名にして問題ない。

②tfstateファイル
terraformでインフラを構築した後に自動で作成されるファイル。
ファイルの格納先は任意ですがaws環境ではs3に自動作成されるよう定義することが多いように思います。
tfstateファイルはterraformにおける最重要ファイルになります。
terraformはtfstateファイルでterraformが作成したリソースか等を確認します。
そのためもしterraform管理したくないリソースがある場合はtfstateファイルを削除するだけでterraformはそのリソースをterraformで構築したことを忘れます。

# terraformの動き

terraformでリソースを構築する場合構築コマンドを実行したフォルダ内に格納されているすべてのtfファイルを見に行きます。
もし階層構造になっている場合、再帰的に子フォルダすべてを見に行きtfファイルを実行したりは基本的にしません。
# terraform構築コマンド

- terraform init
terraformで構築するとき最初に実行するコマンド
プロバイダーのダウンロード等を行い実行環境の準備をおこないます。
上記コマンドではリソースの構築等は行いません。

- terraform validate
構文チェックコマンド
tfファイルに記載されている内容を確認し構文として正しいか確認してくれる。

- terraform fmt
tfファイルを確認しフォーマットをきれいに整えてくれます。

- terraform plan
tfファイルをもとに構築/変更/削除するリソースの内容確認

- terraform apply
tfファイルを元にリソースを構築する。

- terraform destroy
構築したリソースの削除をする。

# 最後に
今回はterraformについて構築をメインに記載しました。
現在terraformを使いaws環境を構築することが多いため次はawsvpc構築の際書いたコードについて記載したい
【準備中】Raspberry Pi AI Camera上で独自のYoloモデルを変換して実行する

# 流れ
1. Yoloのモデルを準備する
【準備中】別記事にデータセットとモデル
2. モデル量子化してonnxのモデルに変換する（Colab上で実施）
→躓く#１
（提供されているColab notebookを参考にしつつバグがあったため、mctバージョンを2.1.0を利用する。https://github.com/sony/model_optimization/issues/1236）
【準備中】修正したColabをアップする
参考
https://github.com/sony/model_optimization/tree/v2.1.0/tutorials/notebooks/imx500_notebooks
github上でmainから消えていたりするから注意。過去のを探す。

3. コンバーターツールで圧縮する（ローカルPCで実施）
→躓く#2
Windowsでは動かないため、WSLで実行。
仮想環境で切り分けたほうがよい。
    ```shell
    pip install model_compression_toolkit
    #pytorchの場合
    pip install imx500-converter[pt]
    imxconv-pt -i <compressed ONNX model> -o <output folder>
    ```
    これでoutput folder内にpackerOut.zipができるため、これをRPiに転送する。
参考
https://www.raspberrypi.com/documentation/accessories/ai-camera.html#quantisation-and-compression
https://developer.aitrios.sony-semicon.com/en/raspberrypi-ai-camera/documentation/imx500-converter?version=3.14.1&progLang=

4. RPi AI Cameraに乗せるためにパッケージ化する（RPi上で実行）
→躓く#3
（パッケージツールにバグがあり公開されてなく待っていたが、最近リリース
当時ここまで来てこの記事を見つけて絶望した。
 https://forums.raspberrypi.com/viewtopic.php?t=377842）
    ~~~shell
    sudo apt-get install jq=1.6-2.1
    sudo apt-get install libarchive-dev=3.4.3-2+deb11u1
    sudo apt-get install libarchive-dev

    sudo apt install imx500-tools
    #imx500-package.sh -i <path to packerOut.zip> -o <output folder>
    imx500-package -i <path to packerOut.zip> -o <output folder>
    ~~~

5. RPi上で実行する
参考：https://www.raspberrypi.com/documentation/accessories/ai-camera.html#picamera2
    ```shell
    python imx500_object_detection_demo.py --model /usr/share/imx500-models/imx500_network_yolov8n_pp.rpk 
    ```
# 【準備中】つづく
一通り終わってオリジナルモデルで実行できるところまで確認できたため一安心。
RPiオフィシャルカメラで簡単に使えると思ってたら棘道だった...
サンプルのコードとデータセット、モデルも公開予定。
YOLOレベルも30fpsで回るので、物体検知＋αや、カメラに走行推論を任せてRPiでは統合とか上位の判断するロジックを考えてみたい。

# 参考
箱から空けて使うまで
https://qiita.com/daiki0321/items/4223611adb453d6c7aa2
2つの言語を比べる#1:C#とGoの出力

### 概要

C#とGoの出力処理・文法の比較

### C#の出力

```
using System;

namespace QiitaLesson
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World");
        }
    }
}
```
・usingで基本的な機能の読み込み
・namespaceで名前空間を作成
・クラスを作成
・プログラムが実行されるときに最初に呼び出されるメソッドである
Mainメソッドを作成

### Goの出力

```
package main
import "fmt"
func main(){
    
    fmt.Println("Hello World")
}
```
・プログラムが開始されるmainパッケージを作成
・importでパッケージをインポート
・func 関数名で関数を作成
【Laravel】BladeでCollectionが空かどうかを判定するには

自分用

DBを取得したけど件数が0。
でもなぜかnullやらemptyやらで判定してもTrueになってしまう…

そんな時は

```PHP
@forelse($replies as $reply)
    <p>{{ $reply->body }}</p>
@empty
    <p>No replies</p>
@endforelse
```

と`@forelse`～`@empty`(取得件数が0件だった時の処理)～`@endforelse`

でいけました。

参考 : https://stackoverflow.com/questions/32652818/laravel-blade-check-empty-foreach
(3.3V)M5Nano C6で温度センサー、Grove-Temperature_Sensorで遊ぶ(V1.0)



x かころぐみよ

参考


https://wiki.seeedstudio.com/Grove-Temperature_Sensor/


結果




![o_coq374.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/8ff3c2b1-58eb-37f5-acfa-9b8a95d2c4e1.jpeg)





![o_coq375.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/9c5f304c-809c-b9c1-ba2f-87ee8a29bfc0.jpeg)


![o_coq597.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/19b23928-7340-dc7c-bb85-5462d73d1882.jpeg)


![o_coq598.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/e5aec463-329a-c378-85cc-9117938804fa.jpeg)



```c++

/*
/* Grove - Temperature Sensor demo v1.0
*  This sensor detects the environment temperature,
*  Connect the signal of this sensor to A0, use the
*  Serial monitor to get the result.
*  By: https://www.seeedstudio.com
*/
#include <math.h>
int a;
float temperature;
int B=3975;                  //B value of the thermistor
float resistance;

void setup()
{
    Serial.begin(9600);
}

void loop()
{
    //a=analogRead(0);
    //a=analogRead(15);
    a=analogRead(1);
    //resistance=(float)(1023-a)*10000/a; //get the resistance of the sensor;

    float v1,a1,o1; 
    v1 = ((float)a) * 0.001;      //電圧を求める
    a1 = v1 / 10000.0;            //電流を求める
    o1 = 5.0 / a1;                //全体の抵抗を求める
    resistance = o1 - 10000.0;    //全体の抵抗から検出抵抗を引く
    //Serial.println(v1); //debug
        
    temperature=1/(log(resistance/10000)/B+1/298.15)-273.15;//convert to temperature via datasheet&nbsp;;
    delay(1000);
    Serial.print("Current temperature is ");
    Serial.println(temperature);
}


```
知らないとヤバいCookieの属性、4選

ブラウザCookieの属性には、Cookieのセキュリティや動作を制御する重要なものがいくつかあります。特にセキュリティやプライバシーを強化するために重要な属性を以下に示します。

### 1. `HttpOnly`

- **説明**: CookieがJavaScript経由でアクセスできないように制限する属性です。
- **重要性**: この属性を設定することで、XSS（クロスサイトスクリプティング）攻撃によってCookieが盗まれるリスクを軽減します。`HttpOnly`属性があるCookieは、`document.cookie`などのJavaScriptコードではアクセスできません。
- **使用例**:
  ```
  Set-Cookie: session_id=abc123; HttpOnly;
  ```

### 2. `Secure`

- **説明**: CookieをHTTPS接続でのみ送信するように制限する属性です。
- **重要性**: Cookieの送信を暗号化されたHTTPS接続のみに限定することで、通信の盗聴によるCookie情報の漏洩を防ぐことができます。特にセッションIDやユーザー認証情報など、重要な情報を含むCookieに必須です。
- **使用例**:
  ```
  Set-Cookie: session_id=abc123; Secure;
  ```

### 3. `SameSite`

- **説明**: クロスサイトのリクエストに対してCookieを送信するかどうかを制御する属性です。
- **重要性**: CSRF（クロスサイトリクエストフォージェリ）攻撃を防ぐために重要です。`SameSite`属性には以下の3つの設定があります:
  - `Strict`: Cookieは同一オリジンのリクエストにのみ送信される。セキュリティを最も強化するオプション。
  - `Lax`: 一部のクロスサイトリクエスト（ナビゲーションやGETリクエスト）ではCookieが送信されるが、フォーム送信やPOSTリクエストなどには送信されない。
  - `None`: クロスサイトリクエストにもCookieが送信されるが、このオプションを使用する場合、`Secure`属性も付ける必要があります。
- **使用例**:
  ```
  Set-Cookie: session_id=abc123; SameSite=Strict;
  ```

### 4. `Expires` **または** `Max-Age`

- **説明**: Cookieの有効期限を設定する属性です。`Expires`は特定の日時を設定し、`Max-Age`は有効期間を秒数で指定します。
- **重要性**: Cookieのライフサイクルを制御することで、セッションCookieや長期間保持されるCookieの管理が可能になります。セキュリティ上の理由から、重要なCookieは有効期限を短く設定することが推奨されます。
- **使用例**:
  ```
  Set-Cookie: session_id=abc123; Expires=Wed, 21 Oct 2023 07:28:00 GMT;
  ```
  ‌
  ```
  Set-Cookie: session_id=abc123; Max-Age=3600; // 1時間
  ```

### **これらの属性の重要性**

- `HttpOnly` と `Secure` を組み合わせることで、CookieがJavaScript経由でアクセスされることを防ぎ、HTTPSを通じてのみ安全に送信されます。
- `SameSite` 属性を設定することで、CSRF攻撃を防ぐ効果があります。
- `Expires`**/**`Max-Age` により、有効期限を管理することで、セキュリティ上のリスクを最小化しつつ、Cookieの動作を制御できます。

これらの属性を適切に設定することで、Cookieを使用したWebアプリケーションのセキュリティとプライバシーが大きく向上します。
「これ、全部書くんですか…？」バーコード貼付記録をデジタル化！

こんにちは。小売業で野菜・果実を取り扱っています。
私の会社では商品に**バーコード**を貼る際に、日時・産地・製造数・製造者などの詳細を記録した**カードを作成**する必要があります。これが一部商品では無く、自店でバーコードを貼った**商品のほぼ全て**に適用される為、かなりの<font color="red">**作業負担**</font>になっています。

この負担をどうにか軽減出来ないかと思い、**AppSheetでアプリ化**に挑戦してみました。

# 使用ツール
・AppSheet
・Google スプレッドシート


# 完成動画
https://x.com/naokisu379274/status/1853117822848880738

# 作成手順
### Google スプレッドシートからアプリを自動作成
まずはGoogle スプレッドシートに項目を打ち込みます。
今回は簡略化する為に実際の物より項目を減らしています。

![スクリーンショット (68).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3885070/153730a9-86fd-3d47-f06b-09371b84eeed.png)
<br>
<br>
「Extensions」 の 「AppSheet」 の 「Creat an app」をクリックします。

![スクリーンショット (69).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3885070/dc98a9ce-ede2-2887-bbd9-e890c6263b0c.png)
<br>

項目を自動で読み込んでアプリが作成されます。

![スクリーンショット (71).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3885070/e2b80acc-73f4-b09b-aebd-8f18ad25a71c.png)
<br>
<br>

### 詳細を設定する
左側の「データ」をクリックして編集していきます。
![スクリーンショット (73).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3885070/87a2eecb-064f-15c7-3d6b-beab0864fef6.png)
<br>

「日付」の横の「TYPE」を「Date」から「DateTime」に変更し、
画面中央を横にスライドして 「INITIAL VALUE」 を「TODAY()」から「NOW()」に変更します。
![スクリーンショット (75).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3885070/6815fe6d-2347-16fb-4418-5b93e4c8fd7f.png)

:::note info
日付だけだとデータが重複して入力出来ないアラートが出た為、
時間を追加することでこれを解決しました。
:::

<br>
<br>

製造者の「TYPE」を「Enum」に変更し、左の編集ボタンをクリックします。
![スクリーンショット (77).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3885070/a00fa9f3-510c-abc8-cc51-3ad0b0bae61c.png)
<br>
<br>

「Type Details」の「Values」の「add」をクリックして選択肢（名前）を追加していき、

![スクリーンショット (78).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3885070/e5a859ae-f38f-81aa-6c96-7254507c7e35.png)

<br>
<br>
「Allow other values」にチェックを入れ、「Input mode」は「Dropdown」を選択します。

![スクリーンショット (79).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3885070/e95085db-1526-9814-42f2-cea28f4fb07c.png)


<br>

これで製造者の項目が選択式になりました。
<img width="250" alt="スクリーンショット (80).png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3885070/7f270090-a07a-e168-fb0b-d168ac3db0a8.png">

値付者も同様に設定します。

<br>
<br>


# メンバーからの評価
今回は実際に職場のメンバー1人に操作して頂きました。

:::note warn
実際に運用する事はまだ不可能なので、
あくまで、こういうものがあったらどうかという想定の話です。
:::

まずはバーコード貼付管理に関して意見を聞くと、
やはり「**めんどくさい**」という声が返ってきました。

そこで「**こんな感じのアプリがあったらどうですか？**」と
私のスマホを渡して試用して頂きました。

すると商品名を入力する場面からすでに少し**戸惑い**がありました。
詳しく聞いてみた所、以下のような理由でした。

<br>
・画面が見ずらい

・そこまでスマホ入力に慣れてない私は入力するより書いた方が楽

・私はいいけど、スマホとかに抵抗ある人もいるからねぇ
<br>

確かに、「サニーレタス少量」の入力なんて<font color="red">**20回ぐらいタップ**</font>しないといけないか…
スマホ入力に慣れていて、フリック入力をするとしても時間がかかります。
今回の試用により示された**改善余地**は以下になります。

<br>
・バーコードを貼る商品は限られているので、入力式よりも選択式の方が早いかも。

・更に**予測変換**の様に選択式の**項目が呼び出せれば**更に早い。

・スマホが小さい為、**UIを見やすく**する必要がある。

:::note info
本来は記録表に実際のバーコードを貼りますが、
アプリ化した場合はカメラ撮影で解決出来るかと思います。
:::

# 今後に向けて
実現には多くの課題が残されていますが、
今回、試用してみた事で改善点があぶり出されて、
少しでも前進出来たのではないかと思います。

しかし一番の難関は
年齢が上の方の**デジタルへの苦手意識**ではないでしょうか。
**見やすいUI・入力の簡易化**を目標に手段を模索していきます。

先日、**Glide**というノーコードツールはAppSheetに比べて
見た目のカスタムが可能という事を聞いたので、
そちらを使用しての作成もいいかもしれません。

https://www.glideapps.com/

最後まで読んで頂きありがとうございました！
githubに追跡無視したいファイルをアップした後、.gitignoreファイルの設定を反映させる方法

### はじめに
.gitignoreファイルを作成するの忘れて、プッシュしたくないファイルをgithub上から消したい&.gitignoreファイルの設定を適用したい時の備忘録✍️

### 手順
① .gitignoreファイルを作成する
② githubの追跡キャッシュを削除する
```
// キャッシュを削除
git rm -r --cached .
```
③ 再度プッシュ対象のファイル達をステージングする　※これで無視したいファイルを除いたフォルダやファイルだけがステージングされる
```
// ステージング
git add .
```
④ コミットメッセージに「追跡設定をする」的なメッセージを付ける　※個人だと不要だが複数人で開発しているようなら付けた方が丁寧⭕️
⑤ プッシュする

注意点として、追跡無視の設定をすることで他の人がpullする時に反映されなくなる訳なのでFTPなどを使用したり共有できる環境があるといい🙆‍♀️
VercelのEdge Functionで525 SSL Handshake Failed

VercelのEdge FunctionでDeepL REST APIを叩こうとした際に、SSL認証エラーが起きました。

- Vercel CLI 37.14.0
- Next.js version: 14.2.15

# 事象
ローカル環境で`vercel dev`した場合は問題なく叩けますが、ステージング環境から叩けない事象が起きました。その他のEdge Functionは動いているので、設定が間違っているとかではなさそう。

```app/api/deepl/route.ts
export const dynamic = "force-dynamic";
export const runtime = "edge";

export async function POST(request: Request): Promise<Response> {
    ...
    // URLSearchParamsでリクエストデータを作成
    const data = new URLSearchParams((await request.json()).params);
    
    // fetchリクエストの送信
    const fetchResponse = await fetch(apiUrl, {
      method: "POST",
      headers: {
        Authorization: `DeepL-Auth-Key ${apiKey}`,
        "Content-Type": "application/x-www-form-urlencoded",
      },
      body: data.toString(),
    });
    ...
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610063/13403c66-57a7-34e7-14e6-549c2cbb27f9.png)

# 解決策
ランタイムをNode.jsに変更します。以下の変更だけで問題なく動作しました。

```app/api/deepl/route.ts
export const runtime = "nodejs";
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610063/c7a28ad2-1ad4-c60f-d5c3-a08478bd2a81.png)

# 原因
原因については色々調べてみましたが、難しかったので諦めました。サーバーレス関数がデプロイされるEdgeでSSL証明書の期限が切れているなどが可能性として考えられますが、その場合こちらからは設定できそうにありません。ご存知の方がいれば教えてください。

# 補足
fetchでも支障ないですが、Node.js環境であればDeepLのライブラリが使えるので以下のようにします。
```app/api/deepl/route.ts
import * as deepl from "deepl-node";
import { isDynamicServerError } from "next/dist/client/components/hooks-server-context";

/**
 * DeepL Translate APIのリクエスト型
 */
export type DeepLTranslateAPIRequest = {
  text: string;
  source_lang: SourceLanguageCode | null;
  target_lang: TargetLanguageCode;
};

export const dynamic = "force-dynamic";
export const runtime = "nodejs";

export async function POST(request: Request): Promise<Response> {
  try {
    const apiKey = process.env.DEEPL_API_KEY;
    if (!apiKey) {
      throw new Error("DeepL API key is not defined.");
    }

    const translator = new deepl.Translator(apiKey);

    const { text, source_lang, target_lang }: DeepLTranslateAPIRequest = (
      await request.json()
    ).params;

    const result = await translator.translateText(
      text,
      source_lang,
      target_lang
    );

    return new Response(JSON.stringify(result.text), {
      headers: { "Content-Type": "application/json" },
    });
  } catch (error) {
    // 動的サーバーエラーは再度スロー
    if (isDynamicServerError(error)) {
      throw error;
    }
    // 実行時エラー
    return new Response(`Internal Server Error : ${error}`, { status: 500 });
  }
}

```

# 参考

https://developers.deepl.com/docs/api-reference/translate
状態管理に使えるCookieとサーバSessionの役割と関係

**Cookie**と**サーバーSession**は、Webアプリケーションでクライアントとサーバー間の状態を管理するための技術です。それぞれの役割と関係について説明します。

### 1. **Cookieの役割と仕組み**

**Cookie**は、Webブラウザ側（クライアント）に保存される小さなテキストファイルで、ユーザーの状態や情報を保存し、同じユーザーが再訪したときにサーバーに情報を送信するために使われます。

- **役割**:
  - **ユーザー識別**: ログイン状態を保持したり、ユーザーの設定情報を記憶したりする。
  - **トラッキング**: ユーザーのWebサイト上での行動を追跡し、パーソナライズされたコンテンツを提供する。
  - **セッション管理**: 一時的なセッションIDを保持し、サーバーセッションを識別するために使われる。
- **種類**:
  - **セッションクッキー**: ブラウザを閉じると削除される一時的なCookie。
  - **永続クッキー**: 有効期限を設定することでブラウザを閉じた後も保持されるCookie。
- **セキュリティ属性**:
  - `HttpOnly`: JavaScriptからCookieにアクセスできないようにし、XSS（クロスサイトスクリプティング）攻撃を防ぐ。
  - `Secure`: HTTPS接続でのみ送信されるようにする。
  - `SameSite`: クロスサイトリクエスト時にCookieが送信される条件を指定し、CSRF（クロスサイトリクエストフォージェリ）攻撃を防ぐ。

### 2. **サーバーSessionの役割と仕組み**

**サーバーSession**は、サーバー側で管理されるユーザーの情報や状態を保持する仕組みです。クライアントからの一連のリクエストを同一ユーザーのものとして識別し、ユーザーごとにセッション情報を保存します。

- **役割**:
  - **状態管理**: ログイン情報やユーザーの選択状態をセッションとしてサーバー側で保持。
  - **一時的なデータ保持**: フォーム入力途中のデータやショッピングカートの内容をセッションに保存し、ユーザーがページ遷移しても情報が保持される。
- **仕組み**:
  - ユーザーがサーバーにアクセスすると、サーバーはユニークな**セッションID**を生成してクライアントに返します。
  - このセッションIDは通常、Cookieに保存され、後続のリクエストに付与されてサーバーに送信されます。
  - サーバーは、受け取ったセッションIDに基づいてセッションストレージ（例えばメモリ、データベース、ファイル）からユーザーの情報を取得します。

### 3. **CookieとサーバーSessionの関係**

CookieとサーバーSessionは、一般的に連携してユーザーのセッション管理を行います。

- **セッションIDの役割**:
  - サーバーSessionの識別子として**セッションID**をCookieに保存します。クライアントがサーバーにリクエストを送るたびに、このCookieがサーバーに送信され、サーバーはセッションIDをもとに正しいセッション情報を取得します。
- **状態管理の流れ**:
  1. **ユーザーがWebアプリケーションにアクセス**すると、サーバーは新しいセッションを作成し、セッションIDを生成。
  2. サーバーはこの**セッションIDをCookieにセット**してクライアントに送信。
  3. 次回以降のリクエストでは、クライアントがCookieをサーバーに送信し、サーバーがセッションIDをもとにユーザーの情報を取得。
  4. **サーバーSession**にはユーザーごとに異なる情報（例えばログイン状態や選択内容）が保存され、必要に応じて更新される。

### 4. **それぞれの役割の違い**

- **Cookie**:
  - クライアント側に保存され、データを保持し、ブラウザを閉じても永続可能。
  - セッションIDやユーザー情報の保持に使われ、セキュリティ設定で制御される。
- **サーバーSession**:
  - サーバー側でデータを保持し、ユーザーの状態を安全に管理。
  - クライアントからは見えないため、セキュリティリスクを軽減。

### **まとめ**

- **Cookie**はクライアント側にデータを保存し、状態を管理するために利用され、セッションIDなどを保持してサーバーとのやり取りを助けます。
- **サーバーSession**はサーバー側でユーザーの情報や状態を安全に管理し、セッションIDを通じてクライアントのリクエストを識別します。

この組み合わせにより、Webアプリケーションはユーザーごとの状態を維持し、セッションの安全性を確保することができます。
Laravelの開発環境で php artisan serve が停止しない時の解決策

## はじめに
`php artisan serve` を停止したいけれど、方法がわからない。または停止できない問題を解決する方法を紹介します。
## `php artisan serve` の基本的な停止方法
基本的vscodeのターミナルなどで `php artisan serve` していると `Control + c` で停止することができます。
## 例外1
`php artisan serve` したターミナルを閉じてしまった方は `ps` コマンドを実行してプロセスを確認します。

```
$ ps
```

プロセス一覧を確認すると、以下のような表示が出ると思います
```
PID TTY           TIME CMD
```

プロセスの「CMD」の列に 127.0.0.1:8000 と表示されている行が、ローカルサーバーを指しているはずです。この行に表示されているPIDが対象となります。

該当するPIDを見つけたら、以下のように入力してプロセスを停止します。
```
kill -9 1234
```
ちなみに、`-9` はプロセスを強制終了させるオプションです。

再度、プロセスを確認するとプロセスが消えて停止できているはずです。
## 例外2
プロセス終了させたはずなのに `php artisan serve` が停止されない方は以下のコマンドを試してみてください。
```
$ kill $(lsof -t -i:8000)
```
または、以下のコマンドを実行してみてください。
```
$ fuser -n tcp -k 8000
```

## 参考
https://qiita.com/janet_parker/items/9bac1173b33175cc54df

https://stackoverflow.com/questions/78583889/php-artisan-serve-failed-to-listen-on-127-0-0-18000-reason
GitHub SSHキーを設定する手順

### 前提
git インストール済み
* [Windows](https://gitforwindows.org)
* [MacのGit](#mac環境のgitについて)

#### 1. SSHキーの確認
まず、SSHキーがすでに生成されているか確認します。

```
ls ~/.ssh
```
id_rsaやid_rsa.pubなどのファイルが表示されれば、SSHキーは存在します。

#### 2. SSHキーの生成（必要な場合）
SSHキーが存在しない場合、以下のコマンドで生成します：

```
ssh-keygen -t rsa -b 4096 -C "your_email@example.com"
```

生成されたキーは通常~/.ssh/id_rsa（秘密鍵）と~/.ssh/id_rsa.pub（公開鍵）に保存されます。

※`Enter file in which to save the key…`などが出てきた場合、
特に変更がなければ、Enterキーを押すことでデフォルトのパスに保存されます。もし別の名前や場所に保存したい場合は、そのパスを入力してEnterを押してください。デフォルトのままで問題ない場合は、何も入力せずにEnterを押してください。

#### 3. SSHキーをGitHubに追加
生成した公開鍵をGitHubに追加します。

1. 公開鍵の内容をクリップボードにコピーします：
   ```
   cat ~/.ssh/id_rsa.pub
   ```

1. GitHubにログインし、右上のプロフィールアイコンから「Settings」を選択します。

1. 左のメニューから「SSH and GPG keys」を選び、「New SSH key」をクリックします。

1. タイトルを入力し、コピーした公開鍵をペーストして「Add SSH key」をクリックします。

#### 4. SSH接続をテスト
設定が完了したら、SSH接続をテストします：

```
ssh -T git@github.com
```

#### その他
* git cloneで下記エラーがでてきた場合、上記の手順を参考して解決できるはず。

  ```
  Cloning into 'gemini'...
  git@github.com: Permission denied (publickey).
  fatal: Could not read from remote repository.

  Please make sure you have the correct access rights
  and the repository exists.
  ```

## Mac環境のGitについて
`git --version` コマンドを実行する

* gitのバージョンが返ってくる場合、gitが正常動作しています。

* 「xcrun: error」というエラーが出る場合、いくつかの原因が考えられます。主な原因と対策を以下に示します：

1. **Xcode Command Line Tools がインストールされていない**
   macOS では Git を使用するために Xcode の Command Line Tools が必要です。これがインストールされていないと、`xcrun: error` が表示されることがあります。

   **解決策**:
   - ターミナルで次のコマンドを実行して、Command Line Tools をインストールします：
     ```
     xcode-select --install
     ```
   - インストール後、再度 `git --version` を実行してみてください。

2. **Xcode Command Line Tools の設定が不正**
   Xcode が正しく設定されていない場合にもエラーが発生することがあります。

   **解決策**:
   - 次のコマンドを実行して、正しいパスを設定します：
     ```
     sudo xcode-select --reset
     ```
   - これで再度 `git --version` を実行して、正常に動作するか確認してください。

3. **Git が正しくインストールされていない**
   Git 自体がインストールされていない場合も、このエラーが表示されることがあります。

   **解決策**:
   - Git をインストールするために、Homebrew を使う方法があります。まず Homebrew がインストールされていない場合、以下のコマンドでインストールします：
     ```
     /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
     ```
   - その後、Git をインストールします：
     ```
     brew install git
     ```

これらの方法で問題が解決するか試してみてください。それでも問題が解決しない場合は、さらに詳しく調査が必要です。


----
## **「Gitが難しい…」と思っている人に最初な1冊！**
**📖 「いちばんやさしいGit&GitHub」** 

✅ **初心者向けに超わかりやすい！** 図解たっぷりで直感的に理解できる  
✅ **Gitの基本からGitHubの活用まで網羅！** 実践的な流れで学べる  
✅ **手を動かしながら学べる！** 実際にコマンドを試しながら習得  

  
📕 [Amazonでチェック（Kindle）](https://amzn.to/4kuAMHy) 🚀  
📕 [Amazonでチェック（単行本（ソフトカバー））](https://amzn.to/3FAr3PZ) 🚀  

#Git #GitHub #初心者向け #エンジニア #バージョン管理
【DynamoDB/Aurora】グローバルデータベースとグローバルテーブルの違いがスッキリわかる解説　#AWS


## はじめに
AWSのソリューションアーキテクト・プロフェッショナル試験に挑むにあたり、**グローバルデータベース**と**グローバルテーブル**の違いが何度出てきても混乱してしまう、という方も多いのではないでしょうか。
私も業務でデータベースに触れる機会が少なく、試験対策で何度もこの2つの違いに立ち返る必要がありました。そこで、ここでその違いをスッキリ理解し、まとめておきたいと思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3806193/04a23bdb-0cf6-ac3f-32b3-32b6dd9af6c8.png)


## 一言要約
:::: note info 
- **グローバルテーブル**は<font color="Red">**DynamoDB専用**</font>で、複数リージョン間でリアルタイムなデータ同期を行う機能。**レイテンシー低減(ユーザに「おせぇな」と感じさせない)が目的**
- **グローバルデータベース**は<font color="Red">**Aurora専用**</font>で、データの耐障害性と災害復旧のために遠隔リージョンへのレプリケーションを提供。**高可用性・障害対応（地震とか起きても大丈夫だった(*´ω`*)ﾎｯ）と思わせる）が目的**

::::

## 理解するためのおすすめの順番

:::: note info 
1. マルチAZ、クロスリージョンとは
2. クロスリージョンとクロスデータベース
:::: 

※もし「①はわかる」という方は読み飛ばしててもOKです。


## まずは基礎から：①マルチAZとクロスリージョンとは？

AWSでは、データの可用性や安全性を向上させるために「マルチAZ (アベイラビリティゾーン)」と「クロスリージョン」があります。似ていますが役割が異なります。

- **マルチAZ**  
  - **概要**: 1つのリージョン内で複数のアベイラビリティゾーン (AZ) にデータをレプリケートする構成です。
  - **用途**: 主に障害対策です。同じリージョン内でのゾーン障害があっても、データの冗長性を確保できます。
  - **具体例**: 東京リージョンの2つのAZ (ap-northeast-1a と ap-northeast-1c) にデータを配置して、片方のゾーンに障害があっても自動的に切り替わる。

- **クロスリージョン**  
  - **概要**: 複数のリージョン間でデータをレプリケートする構成です。
  - **用途**: 地理的な災害対策や、ユーザーが複数のリージョンからアクセスする場合に便利です。
  - **具体例**: 東京リージョンとオレゴンリージョンの間でデータを同期して、どちらかのリージョンがダウンしてもアクセス可能にする。



## ②グローバルテーブルとグローバルデータベースの違いをわかりやすく解説

### 1. グローバルテーブル (DynamoDB専用)
**特徴**:  
グローバルテーブルはDynamoDBで利用できる機能で、複数のリージョン間でデータをリアルタイムに自動的に同期します。各リージョンが独立してデータの読み書きを行え、リージョン間のレイテンシを低減します。

**具体例**:  
例えば、日本とアメリカに支社がある企業が、各拠点のDynamoDBテーブルをグローバルテーブルで接続しているとします。日本のユーザーがデータを更新すると、アメリカのDynamoDBにも即時にその変更が反映され、両国のユーザーが同じデータをシームレスに共有できます。

**用途**:  
複数リージョンでリアルタイムなデータの整合性が必要な場合や、ユーザーがグローバルにアクセスするようなシナリオで最適です。

### 2. グローバルデータベース (Aurora専用)
**特徴**:  
グローバルデータベースはAurora専用の機能で、1つのリージョンに配置されたプライマリデータベースが他のリージョンに読み取り専用のレプリカを持つ構成です。レプリカはプライマリデータベースの変更を継続的に受け取り、災害発生時には他のリージョンのレプリカをマスターに昇格して使用します。

**具体例**:  
例えば、東京リージョンにプライマリAuroraデータベース、シンガポールリージョンにリードレプリカを配置します。東京で障害が発生した際には、シンガポールのレプリカが自動的にプライマリに昇格し、ダウンタイムを最小限に抑えて業務を継続できます。

**用途**:  
主に災害復旧や耐障害性を重視する場合に利用します。グローバルにアクセスするアプリケーションで、書き込みは少なく、読み取り中心のデータベースにも適しています。



## まとめ

| 機能               | サービス   | 主な用途              | 特徴                                                        | 具体例                                                                                             |
|------------------|-----------|----------------------|-----------------------------------------------------------|------------------------------------------------------------------------------------------------|
| **グローバルテーブル**   | DynamoDB | リアルタイムのデータ共有      | 複数リージョン間で即時にデータを同期                                 | 日本とアメリカの両方で同じデータを扱いたいアプリケーション                                           |
| **グローバルデータベース** | Aurora   | 災害対策・耐障害性向上      | 1つのプライマリと複数リージョンにリードレプリカを配置                     | 東京で障害発生時にシンガポールのレプリカをマスターに昇格し、業務を継続する                             |

AWSのグローバルテーブルとグローバルデータベースは、それぞれ特定のユースケースに応じて利用する設計がされています。DynamoDBのグローバルテーブルは、複数リージョンでリアルタイムなデータ同期を求める場面で活躍し、Auroraのグローバルデータベースは、災害発生時の復旧や耐障害性を高めたい場合に最適です。どちらを利用するかは、データの利用方法やアクセス頻度、目的に応じて選択すると良いでしょう。
SQL文 まとめです

## はじめに
　SQLについて基礎文法を一通り知っておこうということで
「ゼロからはじめるデータベース操作SQL」を拝読したため簡単にまとめます

## SQLとは
　SQL(Structured Query Language)はリレーショナルデータベースを操作するための言語であり、元々はデータベースから効率よくデータを検索するために生まれた言語だったがデータの登録も削除などのデータベースの操作をほとんどできるようになった。

## SQL文まとめ

### DDL (Data Definition Language)
データを格納する入れ物であるデータベース、テーブルなどを操作する

#### - CREATE
```
// データベースの作成
CREATE DATABASE <データベース名>;
CREATE DATABASE Example;
```
```
// テーブルの作成
CREATE TABLE <テーブル名>
(<列名1><データ型><この列の制約(省略可)>,
 <列名2><データ型><この列の制約(省略可)>,
 <列名3><データ型><この列の制約(省略可)>,
            ：
　<このテーブルの制約1>,<このテーブルの制約2>, ...);

CREATE TABLE example_table
(example_id CHAR(4) NOT NULL,
 example_name VARCHAR(100) NOT NULL,
 example_age VARCHAR(32) ,
            ：
 PRIMARY_KEY (example_id);
             
```


#### - DROP
```
// データベースの削除
DROP DATABASE <データベース名>
DROP DATABASE Example

// テーブルの削除
DROP TABLE <テーブル名>;
DROP TABLE example_table;
```



#### - ALTER 
```
//テーブルのカラム追加
ALTER TABLE <テーブル名> ADD COLUMN <列の定義>;
ALTER TABLE example_table ADD COLUMN example_column VARCHAR(100);

//テーブルのカラム削除
ALTER TABLE <テーブル名> DROP COLUMN <列の定義>;
ALTER TABLE example_table DROP COLUMN example_column;
```






### DML (Data Manipulation Language)
テーブルの行を検索したり変更の操作を行う


#### - SELECT
```
//テーブルから列データ取得
SELECT <列名>,... FROM <テーブル名>　WHERE <条件式>;
SELECT exmaple_id FROM example_table WHERE example_age = 10;
```

#### - INSERT
```
// テーブルの列と値を追加する
INSERT INTO <テーブル> (<列1>,<列2>,<列3>,..) VALUES (<値1>,<値2>,<値3>,...)
INSERT INTO example_table (exmaple_id,example_name,example_age) VALUES (1,"sasuke",15)
```

#### - UPDATE
```
//テーブルの行の値を更新する
UPDATE <テーブル名> SET <列名> = <式> ;
UPDATE example_table SET exmaple_name = "naruto"
```

#### - DELETE
```
DELETE FROM <テーブル名> WHERE <条件>;
DELETE FROM example_table WHERE example_age = 1;
```

### DCL (Data Control Language)
#### - BEGIN
```
//Transactionを開始する
BEGIN;
```

#### - COMMIT
```
//TRANSACTIONでの変更を確定し終了する
COMMIT;
```

#### - ROLLBACK
```
//TRANSACTIONでの変更を取り消す
ROLLBACK;
```

#### - GRANT
```
//ユーザーに権限を与える
GRANT <権限名> ON <オブジェクト名> TO <ユーザ>;
GRANT ALL PRIVILEGES ON Exmple TO example_user;
```

#### - REVOKE
```
//ユーザの権限を削除
REVOKE <権限名> ON <オブジェクト名> FROM <ユーザ>
REVOKE ALL PRIVILEGES ON FROM example_user;
```


## おわりに
意外とINSERT文とかUPDATEとかよく使うのにちょっと忘れちゃうことが多いのでよく使うやつぐらいはパパッと書けるようになりたいですね。
LINEのグループチャットで返信を活用する

この記事は、細々とメンテナンスを続けているランニング記録LINE botの中で、「返信」の活用を試したことをまとめたものです。

LINEのグループチャットでは、返信を使うことがあります。誰かの発言に対して、返信で何かしたことをトリガに、ボットが反応する。そんなシチュエーションを実装したのでざっくりまとめました。

https://qiita.com/quwaji/items/b3356ea833b3d739e942#line%E3%82%A2%E3%83%97%E3%83%AA%E3%81%A7%E3%81%AF%E8%BF%94%E4%BF%A1%E3%81%8C%E5%8F%AF%E8%A6%96%E5%8C%96%E3%81%95%E3%82%8C%E3%82%8B%E3%81%AE%E3%81%AB

当時、APIが提供されておらず、返信したり返信元の情報を参照することができませんでした。

最近、久しぶりにAPIドキュメントを読み直してみると、なんとサポートされているではありませんか！

https://developers.line.biz/ja/docs/messaging-api/receiving-messages/#receive-quote-messages-sent-by-user

## 最近ぶち当たった課題
ラン仲間が活発にラン画像を共有してくれる結果、「修正」や「削除」で対象の記録をリストから選ぶのに使っていたクイックリプライが、上限13個のリミットに到達することがちょいちょい発生するようになりました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3381/24bb37b7-d0e4-8975-159f-1a03d9f50aac.png)

こうなると14個以上のactionをリストにしてAPIに投げて、エラーになる状況が発生しました。
APIがエラーになっても何も返していなかったため、グループチャット上では何が起きているのか分からず、「ボットがお休みしてしまったのかな？」ということになってしまいます。

この課題に対しては、「エラーが起きないようにすること」「13件を超した場合は直近のものから13件を選択対象にする」ということで回避しましたが、選択できない記録を操作できないという問題が残りました。

https://github.com/run-dreams/linebot-gas/issues/144

そこで、当時は採用できなかった「返信」を使った特定の記録への操作を実装してみました。

https://github.com/run-dreams/linebot-gas/issues/147

## quotedMessageIdとReply ResponseのIdで返信元を参照
「返信」で投稿されたメッセージには、Webhookのメッセージ内にquotedMessageIdが含まれるようになり、どのメッセージへの返信かがわかるようになりました。

しかしここで一つ問題が。ボットが応答した「ナイスラン！」の記録表示はボットからの発信でWebhookによる記録はされていませんでした。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3381/ee0f6398-b152-f1b0-c04e-5a607008d69c.png)

① ボットの応答への返信を想定して、まず応答のレスポンスに含まれるIdを記録しておくようにしました。
② 「修正」が投げられた際、それが返信によるものかをquotedMessageIdの有無で確認し、返信先がボットの記録応答であれば、記録修正のためのクイックリプライを返します。
③ クイックリプライ付きのメッセージを組み立てる段階で、キーボードの表示やあらかじめ表示するテキストの指定もおこなわれます。

## 「追加」にも応用
ボットへの指示を返信で行うことで、対象の選択を省く以外にも、「追加」を使って手動で記録する際、依頼メッセージへの返信で「追加」とすることにより、ひながたの名前を自動的に設定しておくことができ、一手間省けるようになりました。

https://github.com/run-dreams/linebot-gas/issues/140

以下が、このために追加した quotedMessageIdから発言者のUserIdを調べる関数です。

```js:util.gs
function getQuotedUserId(quotedMessageId) {

  var ss = SpreadsheetApp.getActive()
  var sheet = ss.getSheetByName('Webhook Log');

  const lastRow = sheet.getLastRow();

  for (var i = lastRow; i > lastRow - 100; i--) {
    // 追加はだいたいすぐに行われるので、記録の下から100件だけ探す。
    var requestObj = JSON.parse(sheet.getRange(i, 3).getValue());
    if(requestObj.events.length > 0) {
      for(j=0; j < requestObj.events.length; j++) {
        var event = requestObj.events[j];
        if(event.type == 'message' && event.message.id == quotedMessageId) {
          return event.source.userId;
        }
      }
    }
  }
  return 'not found';

}
```

## LTに初登壇します！
2024年11月23日（土・祝）、以下のイベントでLTに初登壇します！

https://linedevelopercommunity.connpass.com/event/333879/

> 「ラン仲間のグループチャットで使うLINEボットを作った話」
暗号化と署名（Signature）の違い

**暗号化**と\*\*署名（Signature）\*\*はどちらもデータのセキュリティを保つために使われますが、異なる目的と機能を持っています。それぞれの違いについて詳しく説明します。

### 1. **暗号化（Encryption）**

**暗号化**は、データの内容を第三者が読み取れないようにすることを目的としています。これにより、データは送信中または保管中でも非公開状態を維持し、機密情報が保護されます。

- **目的**: データの**機密性**を保護し、第三者がデータの内容を理解できないようにする。
- **プロセス**: データを暗号化する際には、アルゴリズムと\*\*鍵（Key）\*\*を使用してデータを難読化し、許可された受信者のみが鍵を使用して復号（データを元に戻す）できるようにします。
- **対称暗号**: 1つの鍵で暗号化・復号化を行う方法（例：AES）。
- **非対称暗号**: 公開鍵で暗号化し、対応する秘密鍵で復号化を行う方法（例：RSA）。

**例**: 銀行の取引情報をオンラインで送信する際、暗号化を使用してその内容が第三者に盗み見されないようにします。

### 2. **署名（Signature）**

**署名**は、データの**真正性**と**改ざん防止**を保証することを目的としています。署名を使用することで、データが送信者によって生成されたものであり、送信中に改ざんされていないことを確認できます。

- **目的**: データの**真正性**と**整合性**を確認し、データが特定の送信者からのものであり改ざんされていないことを保証する。
- **プロセス**: 署名を生成するには、データをハッシュ化し、そのハッシュ値に送信者の**秘密鍵**を用いて署名を作成します。受信者は送信者の**公開鍵**を使用して署名を検証し、データが改ざんされていないかを確認できます。
- **非対称鍵の使用**: 署名は非対称暗号に基づいており、送信者の秘密鍵で署名し、受信者は対応する公開鍵で検証します。

**例**: 電子メールの内容に署名を付けることで、受信者はそのメールが送信者によって送られたものであり、途中で改ざんされていないことを確認できます。

### **暗号化と署名の違い**

 | 特徴 | 暗号化（Encryption） | 署名（Signature） |
 |  ----  |  ----  | ----  |
 | 目的 | データの機密性を確保 | データの真正性と整合性を保証 |
 | 主な機能 | データを第三者が読めないようにする | データの送信者の証明および改ざん防止 |
 | 使用する鍵 | 対称鍵または非対称鍵 | 非対称鍵の秘密鍵（署名）と公開鍵（検証） |
 | 受信者の動作 | 復号化してデータを元に戻す |公開鍵を使用して署名を検証しデータの整合性を確認 |
 | 例 | HTTPS通信、ファイルの暗号化 | 電子署名、JWTの署名部分 |
 | データの可視性 | 暗号化された状態ではデータは見えない |署名部分はデータの可視性には影響しない |

### **実際の利用シーン**

- **暗号化**は、Web通信（HTTPS）やファイルの暗号化、ストレージ保護などに使用されます。ユーザーがパスワードやクレジットカード情報を送信する際、暗号化を使用して情報が漏洩しないようにします。
- **署名**は、電子署名のほか、JWT（JSON Web Token）の検証、ソフトウェアの配布時の改ざん検証などで使用されます。署名により、データが正当な発行元からのものであることを確認できます。

### **まとめ**

- **暗号化**は、データの内容を隠すことが目的であり、情報を第三者から守ります。
- **署名**は、データの発行者を証明し、データが改ざんされていないことを保証するものです。

暗号化と署名は、異なる目的のために使用されますが、両者を組み合わせることでデータの機密性、真正性、整合性を高めることができます。例えば、データを暗号化し、その暗号化データに署名を付けることで、情報を安全に送信し、かつ正当性を保証することができます。
知名度が無いけど実力が付く資格

自分は資格をいくつか持っているのですが評価されている資格が統計検定2級とポートフォリオのKaggleのコンペくらいなのです。
しかし自分はそこに至るまでに2つほどマイナーな資格を取って実力をつけたので今回はそれを紹介していきたいと思います。
この記事が少しでもその資格の役に立てばと思います。
# ビジネス統計スペシャリスト エクセル分析スペシャリスト
https://stat.odyssey-com.co.jp/

文字通りエクセルを使ったデータ分析の資格です。
内容としてはデータの理解、仮説検証、線形モデルになります。ここまで聞くと統計検定2級の確率がないバージョンじゃんと思うかもしれません。
実際その通りです。ですがこの資格のいい所は統計検定2級の内容をエクセルを用いて実際にデータを扱うという点です。
Youtubeのはやたすさんという方の説明ではデータを扱った経験がある人がデータサイエンティストの転職には有効とありますが

https://youtu.be/Wb2uLMOsZsg?si=zXPCQ3i0HtZoFlnx

この資格では実際にデータを扱って仮説検証や線形モデルを作ります。そのほかにも可視化や要約統計量も結構詳しくやり、問題もラジオボタンではなくチェックボックスになっており、過不足なく当てなきゃいけないという構成になっております。
また、統計2級ではやらなかった判別分析もやります。
個人的には統計検定2級を受ける前に受験しておくと勉強がはかどると思います。
理論は統計検定2級
実戦はビジネス統計
という感じで。
実際自分も統計検定2級は仮説検証はほぼ無勉強で受かったのはビジネス統計スペシャリストのおかげだと思っています。

# Python3エンジニア認定データ分析試験
https://www.pythonic-exam.com/exam/analyist

これもよく不要といわれる資格ですが、確かに難易度はそこまで高くありませんし有名でもありません。
けれど自分はなんやかんやで必要だと思います。
というのもPythonを使った可視化やPandasやNumpyの細かい操作などを学べてこれからデータ分析をする方にはうってつけの資格だと思います。
もちろんもともとデータ分析をやっている人ならすぐ受かる資格ではあります(自分は2週間で受かった)。
ただ、自分は当時Pandasをあまり使っていなかったのでとてもいい勉強になったのと、Numpyやmatplotlibの細かい操作は初めて知った部分が多く、特にNumpyはその後Paizaで役に立っています。
特にPandasについては初めて勉強したのでここから因果推論についても応用ができるようになったり、また分からなくなったときにどのサイトを使えばいいかも覚えました。

# まとめ
ビジネス統計スペシャリストは統計2級の実践版
Python3エンジニア認定データ分析試験はデータ分析の基礎
Why Use Cadence?

[Previous << Introduction to Cadence](https://qiita.com/flowcadence/items/87147a26f1d2b89b46ca)
[Next >> 1. First Steps](https://qiita.com/flowcadence/items/d74e796356935d409ba4)

https://qiita.com/flowcadence/items/3073d69c6e9895e68b5a#cadenceの言語仕様を学ぶ

<h1 id="Security-and-Safety">Security and Safety</h1>

Cadenceは、セキュアなスマートコントラクトの開発を大幅に簡素化するセキュリティと安全性の保証を提供します。スマートコントラクトは価値の高い資産を扱うことが多いため、Cadenceは、資産は同時に1つの場所にしか存在できず、コピーもできず、誤って失われたり削除されたりすることもないことを保証するリソース指向のプログラミングパラダイムを提供します。Cadenceには、バグの発生を防止するいくつかの言語機能が含まれています。これらのセキュリティと安全性の機能により、スマートコントラクトの開発者は、事故や攻撃の防止に時間を割かれることなく、スマートコントラクトのビジネスロジック作成に集中することができます。
<h1 id="Composability">Composability</h1>

Cadenceはコンポーザビリティ(結合による可能性)を実現します。リソース（任意のユーザー定義されたデータ型である）はユーザーのアカウント内に直接保存され、また、スマートコントラクト間で自由に流動させることができます。関数の引数として渡したり、関数から返したり、あるいは任意のデータ構造によって結合することも可能です。これにより、ビジネスロジックの実装が容易になり、より自然になり、既存のロジックの再利用が促進されます。

<h1 id="Simplicity">Simplicity</h1>

Cadenceの構文は、[Swift](https://developer.apple.com/swift/)、[Kotlin](https://kotlinlang.org/)、[Rust](https://www.rust-lang.org/)などの人気の高い現代的な汎用プログラミング言語から着想を得ている(補足: メインはSwiftから着想が来ており、Linear TypeはRustにビルトインであることからそこから着想が来ている)ため、開発者はその構文と意味論(the syntax and the semantics)に親しみを感じることでしょう。実用的なツール、ドキュメント、および例により、開発者は迅速かつ効果的にプログラムの作成を開始することができます。数百人の開発者がCadenceを短期間で習得し、すぐに本番品質のスマートコントラクトを開発することができるようになりました。

翻訳元

https://cadence-lang.org/docs/why

***
<b>[Previous << Introduction to Cadence](https://qiita.com/flowcadence/items/87147a26f1d2b89b46ca)
</b>

[Flow blockchain / Cadence version1.0ドキュメント](https://qiita.com/flowcadence/items/3073d69c6e9895e68b5a) (Why Use Cadence?)

<b>[Next >> 1. First Steps](https://qiita.com/flowcadence/items/d74e796356935d409ba4)</b>
Android実機からスクリーンショットをPCに即保存する方法

Androidの実機でデバッグしている時、スクリーンショットを撮って保存したいことがあると思います。
ただ、スマホでスクショを撮影してもPCにファイルを移動させるのがなかなか手間ですよね。
そんな時にワンコマンドでPCの任意のディレクトリにスクリーンショットを保存できる便利なコマンドを紹介していきたいと思います。

## 前準備
- adbコマンドが使用できるか確認
`adb version`とターミナル(もしくはコマンドプロンプト)と打ち込んでバージョン情報が表示されたらOKです

```zsh
$ adb version        

Android Debug Bridge version 1.0.41
Version 35.0.2-12147458
Installed as /Users/***********/Library/Android/sdk/platform-tools/adb
Running on Darwin 24.0.0 (arm64)
```

もし、`adb command not found`などが出たら「Mac adb 導入」などで検索して導入してください。(5分もあれば終わると思います)

- スマホとPCを繋ぐ
スマホとPCを有線で繋ぎ、以下のコマンドを打ちます。

```zsh
$ adb devices
List of devices attached
********	device // ←これが表示されていればOK
```
表示されていない場合や、`unauthorized`と表示されている場合などは何かしらの設定、権限が足りていないのでよしなに対応してください。(USBデバッグの有効化など)

## スクリーンショットを保存する
前置きが長くなってしまいましたが、本題です。以下のコマンドをターミナルに打ち込んでください！

```zsh
adb exec-out screencap -p > ~/Pictures/$(date +"%Y-%m-%d_%H-%M-%S").png
```

<details><summary>windowsの場合</summary>
おそらくそのままでは動かないのでchatGPTなどでwindows用に変換してもらってください。動作確認はしていないですが、一応以下のコードがコマンドプロンプト用に変換されたコマンドです。

```
for /f "tokens=1-4 delims=/ " %a in ("%date%") do for /f "tokens=1-2 delims=:." %b in ("%time%") do adb exec-out screencap -p > %userprofile%\Pictures\%a-%b-%c_%d-%e.png
```
</details>

成功していた場合、Finderで`Pictures`ディレクトリを開くとスクリーンショットが保存されていると思います。

ファイル名、保存先ディレクトリの変更はお好きなものに変更しても問題ないです。

## コマンドの解説
ここからは、このコマンドの解説を少ししていきます。
各部分ごとに解説していきます。

`adb`
Android Debug Bridgeの略です。AndroidデバイスとPC間で通信を行うためのツールで、端末の操作やデバッグ、ファイル転送などが可能になります。便利。

`exec-out`
指定したコマンドの出力を標準出力に書き出すオプションです。今回の場合、screencapコマンドの出力（画像データ）がPCに送られるようになります。

`screencap`
スクリーンショットを撮るためのコマンドです。

`-p`
PNG形式でスクリーンショットを取得するscreencapんpオプションです。このオプションを付けることで、画像がPNG形式で出力されます。

`>`
リダイレクト演算子で、標準出力のデータをファイルに保存するために使われます。今回の場合、スクリーンショットのPNGデータが指定したファイルに保存されます。

`~/Pictures/`
保存先のディレクトリです。

`$(date +"%Y-%m-%d_%H-%M-%S").png`
現在の日付と時刻を取得し、ファイル名として使用する部分です。dateコマンドで日時をフォーマットし、%Y-%m-%d_%H-%M-%S形式で表示します。ファイル名に日時が入るため、重複を避けられますしわかりやすいです。
`+`は、dateを+の後に続くフォーマットで表示するようにするプレフィックスです。

## おわりに
最後までお読みいただきありがとうございました！
abd、便利ですね。もっと活用すればデバッグの時などかなり役に立ちそうです。
【Postman】Testsにおいて各種値を取得する方法

## ボディ

```js
const responseJson = pm.response.json()
```

## ヘッダー

```js
const contentType = pm.response.headers.get('Content-Type')
```

## クッキー

```js
const isLoggedIn = pm.cookies.get('isLoggedIn')
```

## 参考

https://learning.postman.com/docs/tests-and-scripts/write-scripts/test-examples/#make-assertions-on-the-http-response
Yupのnullableはいつ使うのか

## はじめに
Yupを業務で使用しているときに`nullable()`という機能があることを知りました。
当初、nullを設定できるようにするためのものかと思っていましたが、それであるならば`required()`をつけなければ済む話です。

では、`nullable()`がどのような場合に有効なのかを調べてみました。

## APIのレスポンスを受け取る場合を考える
ユーザー情報を編集するフォームを例にして、APIから以下のようなレスポンスを受け取る場合を考えてみます。
ユーザーID、ユーザー名、マネージャーIDを受け取ることを考えます。
```typescript:APIレスポンス例
{
  "id": 2,
  "name": "山田花子",
  "managerId": 1     // マネージャーが設定されている場合
}

// または
{
  "id": 1,
  "name": "田中太郎",
  "managerId": null  // マネージャーが未設定の場合
}


// 型定義
interface User {
  id: number;
  name: string;
  managerId: number | null;
}
```

マネージャーは未設定な場合も考えられるものとし、その場合には`null`が設定されるものとします。

次に、Yupのスキーマ定義を考えてみましょう。
次の2つのパターンを比較してみます。
```typescript

// パターン1: nullable()を使用しない場合
const schemaWithoutNullable = yup.object({
  name: yup.string().required(),
  managerId: yup.number()
});

// パターン2: nullable()を使用する場合
const schemaWithNullable = yup.object({
  name: yup.string().required(),
  managerId: yup.number().nullable()
});

// 型の取り出し
type InferredType1 = yup.InferType<typeof schemaWithoutNullable>;
type InferredType2 = yup.InferType<typeof schemaWithNullable>;
```

ここで、重要な違いが出てきます。
```typescript
// パターン1の型
type InferredType1 = {
  name: string;
  managerId: number | undefined;  // undefinedは許容されるがnullは許容されない
}

// パターン2の型
type InferredType2 = {
  name: string;
  managerId: number | null | undefined;  // nullが設定可能
}

// 型の整合性チェック
const updateUser = (data: User) => {
  // パターン1の場合：型エラー
  const form1: InferredType1 = {
    name: data.name,
    managerId: data.managerId  // Error: 'null' is not assignable to 'number | undefined'
  };

  // パターン2の場合：OK
  const form2: InferredType2 = {
    name: data.name,
    managerId: data.managerId  // OK: 型が一致する
  };
};
```

nullableを設定しなかったほうは、numberまたはundefinedとされ、nullを設定することができません。
nullableを設定したほうは、nullを問題なく設定することができるようになっています。

このように、nullが入る可能性のあるフォームに対して、`nullablle`を設定することで、型定義の整合性を保つことができるようになります。

## まとめ
`nullable`を設定することで、推論される型にnullが追加されることがわかりました。
明示的にnullを設定したり、外部APIと連携する際にnullが設定される可能性がある項目に対してnullableを指定するのが良さそうです。
[MySQL] SQLの基本

## 概念の理解

### リレーショナルデータベースとは？

ざっくりいうと表のデータベースです。表のデータベースではないデータベースのことは、NoSQL(Not only SQL)といいます。

### RDBMSとは？

RDBMSとは、リレーショナルデータベースを採用しているソフトウェアのことです。MySQLやPostgreSQLのことを「データベース」と呼ぶのは少し誤りで、正しくは「リレーショナルデータベースマネジメントシステム」です。

## クエリ(問い合わせ)

### クエリの書き順

```
SELECT(カラム指定)
FROM(どのテーブルが対象か)
JOIN(テーブルの結合)
WHERE(条件絞り込み)
GROUP BY(グループ化)
HAVING(グループ化の後に絞り込み)
ORDER BY(順番の並べ替え)
LIMIT(結果セットの制限)
```

例えばSELECTをFROM句の下に持ってくるとエラーになります。
クエリの最後はセミコロン「;」で終わりを示しましょう。

### クエリの実行順序

```
FROM(テーブルを持ってきて)
 ↓
JOIN(テーブルを結合して)
 ↓
WHERE(条件によって絞り込んで)
 ↓
GROUP BY(グループ化して)
 ↓
HAVING(グループ化した後に絞り込んで)
 ↓
SELECT(指定したカラムに結果をセットして)
 ↓
ORDER BY(結果セットを並び替える)
```

人間がクエリを書くときの順番と内部での実行順序は異なるため、理解には注意が必要です。

## 内部結合と外部結合

内部結合(INNER JOIN)と外部結合(OUTER JOIN)は、どちらも複数のテーブルを結合するのに使います。

### 使い分けは？

内部結合は、結合したい両方のテーブルに共通する(どちらにもある)行だけを抽出して結合します。

外部結合は、片方のテーブルにだけデータが存在する場合でも結合を行います。

「両方のテーブルにしっかりデータがあるよ」という場合はINNER JOINを使い、「どちらか片方のテーブルにはデータ抜けがあるかも…」という場合はOUTER JOINを使いましょう。

### 参考記事

https://ayudante.jp/column/2024-04-25/13-00/
Google Gemini APIを利用したPythonプログラムを開始

## はじめ
Google Gemini APIの生成AIを利用して、Pythonでプログラミングを始める手順を紹介する。

## 環境準備
* 前提
  * Pythonインストール済み

1. 下記のコマンドでGemini API SDKをインストールする
   ```
   pip install -q -U google-generativeai
   ```

1. Gemini APIキーを設定する
  1. [APIキー取得リンク](https://aistudio.google.com/app/apikey?hl=ja)でAPIキーを取得する
     * 必要なもの
       * Googleアカウント
     * 注意事項
       * API キーを共有したり公開コードに埋め込んだりしないでください
  
  1. 下記コマンドでAPIキーを設定する
      ```
      export API_KEY=<YOUR_API_KEY>
      ```
      ※`<YOUR_API_KEY>`を生成されたAPIキーを置換する

## Gemini APIを利用したPython言語での開発
    # テキスト生成の例

    import google.generativeai as genai
    import os

    # APIキーを設定 
    genai.configure(api_key=os.environ["API_KEY"])

    # Gemini API モデルを選択
    model = genai.GenerativeModel("gemini-1.5-flash")

    # APIを利用
    user_input = input("知りたいことを聞いてください: ")
    response = model.generate_content(user_input)
    print(response.text)

## その他
1. [APIリファレンス](https://ai.google.dev/api?hl=ja&lang=python)

1. [Gemini API 料金](https://ai.google.dev/pricing?hl=ja#1_5flash)

1. [GITHUB その他のサンプル](https://github.com/SMZPP/gemini/tree/main/python)
【個人開発】Google Material Design を使ってスタイルを改善した

# 初めに

　個人開発アプリのデザインは正直重要視していなかった。しかしイケてるデザインはユーザー獲得に大きく貢献するのではないかと思うようになった。理由はすごく単純なのだが、それは様々な WEB サイトのトップページを眺めている中でどれも凝っているなあと思い、これだけ多くのサイトがデザインに凝るということはそれだけユーザーに与える影響が大きいという、当たり前の結論に至ったからだ。そこで以前から興味のあった Google Material Design を個人開発アプリに取り入れてみることにした。

**個人開発アプリ**

https://kokoehon.oizumi-yuta.com/

# Google Material Design とは

https://m3.material.io/

 ChatGPT に聞いてみた。

 > マテリアルデザインの目的は、ユーザーに一貫した、直感的で快適な体験を提供することです。これを実現するために、物理的な「マテリアル」（素材）の概念をデジタルの世界に持ち込みました。このアプローチにより、画面上の要素が触覚的に「リアル」な感覚を生み出すことを目指しています。

# Material Design による改善点

Material Design を取り入れた結果、どのように個人開発アプリのデザインが変わったかを紹介する。

## Cards を使用してトップページを改善

- 修正前

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/a810225f-9c33-5e76-f889-4df7e3a20fcb.png)

- 修正後

![localhost_3000_books_519fef94-6623-4dbc-ba32-71e795768b47_read.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/864aa01c-b129-f877-a0f7-2ebb8fa2c912.png)

モダンなデザインはトップページにカードを使っている印象があった。

**例 1**

https://www.paperpillar.com/

**例 2**

https://www.awwwards.com/

**例 3**

https://www.digital.go.jp/

**例 4**

https://stripe.com/jp

そこで以下の Material Design のカードを参考に個人アプリにも取り入れることにした。

https://m3.material.io/components/cards/overview

3 種類の中からシンプルな 3. Outlined card を選択した。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/46830459-c056-86a5-6f2e-54bdb2917b62.png)

## ナビゲーションバーを改善

- 修正前

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/aab8a5d5-a566-6e11-8b40-06e399ef381b.png)

- 修正後

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/e2d5f2b7-103e-2bf9-ed89-8f887a3e0cd4.png)

Material Design では、サイトのどこにいるのかがわかるように選択中のアイテムの背景色を変えている。さらに選択中のアイコンを塗りつぶすというガイドラインなのだが、今回そこまで実装できなかった。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/1c90eda9-ec1f-a543-8926-553298f59e7e.png)

https://m3.material.io/components/navigation-bar/overview

# 今後の改善方針

　トップページに画像を背景にしている WEB サイトがおしゃれだと思うので取り入れたい。

https://www.semplice.com/

 デザインを探している途中に見つけた以下のサイトはとてもかっこいい。マウスの動きに応じて水しぶきが飛ぶような動きをする。スクロールの動きが斜め方向というのもすごい。流れている音楽もかっこいい。

 https://activetheory.net/

# スタイル管理

　Figma を使おうと思ったが操作がわからなすぎて結局使っていない。ちょっとずつ触って慣れていきたい。Figma 使えるとかなりできることが増えそう。
　ストーリーブックというとても便利そうなものを見つけた。

https://storybook.js.org/

デジタル庁が公開しているものをそのうち見る予定。

https://digital-go-jp.github.io/design-system-example-components/

デジタル庁が公開しているデザインシステムのようなものを個人開発アプリでも時間があったら作りたい。

https://design.digital.go.jp/

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/5bf09c15-4098-2ccd-ed8c-a56a01ca0e86.png)

一人で開発しているとここら辺は適当になってしまう。しかし個人開発でここまでやってたら開発が進まなくなってそのうち飽きそうだ。それだったらどんどん開発して機能アップデートにすることに時間を使ったほうがよい気がしてきた。
AI音楽生成ツールの紹介：高速・高品質・無料で創作が可能なサイト

音楽制作の新しい時代が訪れました。今では、テクノロジーの進化により誰でも簡単に音楽を作ることができます。その中心にあるのがAI音楽生成ツールです。これらのツールは、AIの力を活用して、わずかな時間で高品質な音楽を生成し、音楽制作のプロセスを大きく変えています。

1. AI音楽生成ツールとは？
AI音楽生成ツールは、人工知能を利用して音楽を自動的に生成するソフトウェアやウェブサイトです。使いやすいインターフェースを備え、音楽の専門知識がなくても簡単に作曲が可能です。例えば、メロディーのアイデアやオーディオトラックの自動生成に活用され、さまざまなジャンルやスタイルで音楽を作ることができます。

2. 高速な生成スピード
従来の音楽制作では、曲の構成やミックス、編集など、膨大な時間が必要でしたが、AI音楽生成ツールを使うことで、そのプロセスが数秒から数分に短縮されます。ユーザーがテーマや雰囲気を指定すると、瞬時に楽曲を生成し、アイデアをすぐに形にすることができます。

3. 高品質の音楽
「AI生成」というと品質に不安を感じるかもしれませんが、現在のAI技術は驚くべき精度でプロフェッショナルなレベルの音楽を作り出します。多くのツールは、大規模な音楽データセットを学習しており、クリエイティブで多様な音楽スタイルを再現できます。特に有名なサイトでは、サウンドの細部にまでこだわり、高品質なトラックを提供しています。

4. 無料で使える利便性
さらに嬉しいことに、これらのAI音楽生成ツールは無料で利用できるものが多いです。多くのサービスは基本機能を無料で提供しており、気軽に試してみることができます。もちろん、有料プランを利用することで、より高度な機能やカスタマイズオプションを活用することも可能ですが、無料プランでも十分な機能を備えています。

5. おすすめのAI音楽生成ツール
以下に、高速・高品質・無料の特徴を持つAI音楽生成ツールをいくつか紹介します。

Suno AI Music(https://sunoai-music.com/)：豊富な選択肢とカスタマイズ機能を備えた音楽生成サイト。ユーザーは瞬時に楽曲を作成し、さまざまなジャンルでクリエイティブなアイデアを実現できます。
Amper Music：簡単にBGMやテーマ曲を生成可能。商用利用も可能なオプションあり。
Soundraw：ジャンルやムードを指定して即時に楽曲を生成。クリエイティブに特化。
Aiva：クラシック音楽やオーケストラ風の曲作りに適しており、無料で試用が可能。
まとめ
AI音楽生成ツールは、音楽制作を手軽かつ迅速に楽しむための画期的な手段です。無料で利用でき、高品質なトラックを短時間で提供するこれらのツールをぜひ活用して、あなたのクリエイティブなプロジェクトを充実させてください。
自宅サーバーにChromeをインストールする

<!-- 自宅サーバーにChromeをインストールする -->

## はじめに

自宅サーバーでDiscord Botを動かすときに、``selenuim``を使う必要があったので、Chromeをインストールしなければなりませんでした。

というわけで、インストール手順と注意点をメモしておきます。

## インストール手順

1. まず、Chromeのインストールに必要なパッケージをインストールします。

    ```bash
    sudo apt-get install -y wget curl unzip
    ```

2. 次に、Chromeをダウンロードします。

    ```bash
    wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
    ```

3. ダウンロードしたファイルをインストールします。

    ```bash
    sudo apt install -y ./google-chrome-stable_current_amd64.deb
    ```

4. インストールが完了したら、バージョンを確認します。これは後でChromeDriverをダウンロードするときに必要です。

    ```bash
    google-chrome --version
    ```

5. 次に、ChromeDriverをインストールします。[このサイト](https://googlechromelabs.github.io/chrome-for-testing/)からバージョンや実行環境に合ったChromeDriverのURLを見つけ、コピーします。今回はlinux64のものとします。

6. コピーしたURLを使って、ChromeDriverをダウンロードします。``[version]``はさっき控えたChromeのバージョンに置き換えてください。バージョンが違うと動かないので注意です。

    ```bash
    curl -O https://chromedriver.storage.googleapis.com/[version]/chromedriver_linux64.zip
    ```

7. ダウンロードしたファイルを解凍します。解凍後は``zip``ファイルは削除しても構いません。

    ```bash
    unzip chromedriver_linux64.zip
    ```

8. 解凍したファイルを``/usr/bin``などに移動します。人によっては``/usr/local/bin``に移動することもあるようです。

    ```bash
    sudo mv chromedriver /usr/bin
    ```

9. 最後に、ChromeDriverのバージョンを確認します。以下のように、Chromeのバージョンと同じかどうか確認しましょう。

    ```bash
    chromedriver --version
      ChromeDriver 130.0.6723.91
    google-chrome --version
      Google Chrome 130.0.6723.91
    ```

## おわりに

これで、ChromeとChromeDriverのインストールが完了しました。これで、Seleniumを使ってChromeを操作し、スクレイピングなどができるようになります。

バージョンをそろえる必要があるところと、ChromeDriverの移動先が結構個人的にはハマったので、そこには注意するとよいと思います。

## 参考文献

- [Ubuntu 22.04 + Python × Selenium 環境構築まとめ](https://zenn.dev/techike/articles/a8e28c55c9aa11)
- [Ubuntuにchromedriverをインストールする #Ubuntu - Qiita](https://qiita.com/pyon_kiti_jp/items/0ba98f140142d4ac6b25)
[Go][tips][ProblemSolver] 文字列を辞書順 sort

tipsです。

# 文字列を辞書順ソート

**お題** 「"defacb"を辞書順に並べ替えて、"abcedf"にしてください。」

goにはchar型がないです。代わりに、`[]byte`や`[]rune`を使用します。

`[]rune`をちゃんと理解したい人は参考サイトへ。

## 方法1 `[]rune`型を使用

```go
package main

import (
	"fmt"
	"sort"
	"strings"
)

func main(){
	var s string
	s = "defacb"

	runes := []rune(s)
	// stringの文字列を辞書順並べ替え
	sort.Slice(runes, func(i, j int) bool {
		return runes[i] < runes[j]
	})
    fmt.Println(string(runes))
}
```

```go
abcdef
```

## 方法2 charっぽく並べ替え

1. `strings.Split("defacb", "")`として1文字ずつのsliceにして並べ替える。
2. 並べ替え操作を施した1文字ずつのsliceをくっつける

です。

```go
package main

import (
	"fmt"
	"sort"
	"strings"
)

func main(){
	var s string
	s = "defacb"

	chars := strings.Split(s, "")
	// stringの文字列を辞書順並べ替え
	sort.Slice(chars, func(i, j int) bool {
		return chars[i] < chars[j]
	})
    fmt.Println(strings.Join(chars, ""))
}
```

```go
abcdef
```

## 参考

[Goのruneを理解するためのUnicode知識](https://qiita.com/seihmd/items/4a878e7fa340d7963fee)
[GoのSliceをSortする(sort.Sliceとsort.SliceStable)](https://qiita.com/Sekky0905/items/2d5ccd6d076106e9d21c)
[golangでstringをsliceのように扱う](https://zenn.dev/takotako/articles/54a4a8f2bcef45)
[Goで回文判定](https://blog.ebiiim.com/posts/go-palindrome/)