OmniAICreator/Qiita-1.07M huggingface dataset:
Cloudformationのパラメータの表示順を制御する

# はじめに
テンプレート作成の時には、パラメータを用途毎にまとめて書いているのにも関わらず、コンソールからスタック作成する時には、表示される順序がバラバラになっていてもやもやしている方必見です。

# 解決策
Metadataの「AWS::CloudFormation::Interface」を使用することで解決できます。

# 使い方
使い方は簡単です。
metadataセクションを宣言し、ParameterGroupsプロパティにリストでグループ毎に宣言するのみになります。
AWS公式ドキュメント：https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-cloudformation-interface.html
###### 詳しく説明していきます。
- 何も設定していない場合
```yml
AWSTemplateFormatVersion: "2010-09-09"

Parameters:
#りソース名などに使用する環境パラメータ
  SystemName:
    Type: String
  Environment:
    Type: String

#EC2に使用するパラメータ
  EC2ImageId:
    Description: Specifies the AMI ID for your instances.
    Type: String
    Default: ami-XXXXXXXX
  KeyPairName:
    Description: Name of key pair to use
    Type: AWS::EC2::KeyPair::KeyName
```
![スクリーンショット 2023-06-28 20.23.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3472762/1c1f3039-950e-17b5-1078-b00465ba502e.png)

このようにテンプレート上ではグループ毎に記述しているにも関わらず、コンソール上ではバラバラになってしまいます。
できるのであれば記述通りに表示されて欲しいものです。


- Metadataを宣言した場合
```yml
AWSTemplateFormatVersion: "2010-09-09"

Parameters:
#りソース名などに使用する環境パラメータ
  SystemName:
    Type: String
  Environment:
    Type: String

#EC2に使用するパラメータ
  EC2ImageId:
    Description: Specifies the AMI ID for your instances.
    Type: String
    Default: ami-XXXXXXXX
  KeyPairName:
    Description: Name of key pair to use
    Type: AWS::EC2::KeyPair::KeyName

Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label:
          default: "Environment Configuration"
        Parameters:
          - SystemName
          - Environment
      - Label:
          default: "EC2 Configuration"
        Parameters:
          - EC2ImageId
          - KeyPairName
```
![スクリーンショット 2023-06-28 20.24.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3472762/501800c6-e1d0-d732-f686-a5421e79ec8a.png)

ちゃんとMetadataに記述した通りに表示されました！
Labelを指定することで、パラメータのグループ名をつけることも可能です。

# 最後に
パラメータの順序を制御することでスタック作成時に見やすくなり、
ヒューマンエラーを予防することにもつながるので、コンソールからスタックを作成する場合は是非使っていきたい機能だと感じました。
ファインチューニングと転移学習の違い


## はじめに

卒業研究においてファインチューニングと転移学習の違いがすぐに思い出せなかったので、改めて学習し直しました。

本記事はその学習メモおよび備忘録です。

## 転移学習

- **あるデータセットで学習したモデルを用いて、別のデータセットを学習させること**
- 転移学習のメリット
    - データが少なくても高い精度を出せる
    - 大量のデータと学習にかかる時間を大幅に削減可能

## ファインチューニング

- **学習済みモデルのある特定の層の重みを微調整すること**
- 新たに学習するデータセットもサイズが大きいときは転移学習するとかえって時間がかかってしまう
    - そのため、モデル自体を設問に合わせたものに微調整する＝ファインチューニング

![https://udemy.benesse.co.jp/wp-content/uploads/8db9d3f9409d59db598e157692eebe85.png](https://udemy.benesse.co.jp/wp-content/uploads/8db9d3f9409d59db598e157692eebe85.png)

引用元：[https://udemy.benesse.co.jp/data-science/deep-learning/transfer-learning.html](https://udemy.benesse.co.jp/data-science/deep-learning/transfer-learning.html)
CDN でのクエリパラメータの取り扱い方を間違えて、想定外の結果が表示されたケーススタディ

# はじめに

WordPress のウェブサイトのトップページ ( `https://www.example.com/` ) にアクセスした。 その結果「検索結果が見つかりませんでした」という内容のページが表示されてしまった。

アプリケーションのバグかと思ってサーバーを確かめると、サーバー内で `localhost` 向けに `curl` を実行する分には正常にレスポンスが表示されており、上記のような問題は発生していない。 

何か間違えたかと思って、改めてトップページにアクセスすると、今度は正常にページが表示された。

この問題について調査したのだが、複合的な要因が噛み合うことで問題が起こっていた。 そのため、その現象を説明する。

# 個別の事象
## WordPress のデフォルト検索機能 

WordPress ではデフォルトでクエリ `s` を付与することでサイト内検索を行うことができる。 例えば `https://www.example.com/?s=検索` としてアクセスすればよい。

個人的には色々と思うところがあるが、事実としてデフォルトではこのような機能がある。


## 外部サイトからのリンクに独自のクエリパラメータが追加されることと CDN の設定

例えば Facebook からの流入であれば `fbcid=...` といったクエリが付与された状態でページが呼び出される。 例としては `https://www.example.com/?fbcid=xxxxxxxx` など。
その他、アクセス解析などの理由でこのような独自のクエリを付与して外部ウェブサイトを呼び出す仕組みはかなり多い。

しかし、ウェブページ全体を見ると、クエリを表示制御に利用しないページの方が多い。 例えばウェブサイトのトップページはそうである。
`https://www.example.com/?fbcid=xxxxxxxx` であっても `https://www.example.com/?token=yyyyyy` であっても、内容の変わらない同じトップページが表示されてほしい。

同じページが表示されてほしいというのであれば、**CDN や nginx によるウェブサイトキャッシュを利用している場合、それらのキャッシュを共用して、キャッシュヒット率を上げたい、という考えが出てくるのは自然である。** 
クエリを無視してキャッシュを作るのであれば、例えどんなクエリを付与する外部システムからのリンクであっても、共用のキャッシュを利用できる。 言い換えると、個別のクエリを見てキャッシュを生成・利用する場合、先の2つのアクセスを別物ととらえてしまうため、外部から付与されるクエリ毎にサーバーに問い合わせが行われるため、サーバー側の負荷が上がってしまう。

そのため、多くの CDN ではクエリパラメータとキャッシュをどのように関連付けるか、と言った設定を提供している。 クエリをすべて無視するのであれば、`/` と `/?q=aaa` と `/?q=bbb` と `/?s=aaa` はすべて同じキャッシュを利用する設定になるし、クエリすべてを利用するのであれば先の4つは異なるキャッシュを利用する設定になる。 もちろん、特定のクエリのみ利用 / 特定のクエリのみ除外、という機能も併せて提供されていることが多い。

WordPress 側はデフォルトだとおそらく `Cache-Control: no-cache` を返すが、CDN を利用するためにこのヘッダを変更しているか、あるいは CDN 側で Cache-Control を無視して強制キャッシュを行うといった方法を取ることもできる。

## 任意のクエリを付与した検索汚染

例えば、以下のページにまとめられているが、要は「クエリを付与した有効な URL を配置したウェブページを準備し、そのウェブページを検索エンジンのクローラーにアクセスされることで、それらの URL を 検索結果に表示させる」ことが行われている。 その結果、それらの URL も Google などの検索結果として表示されてしまうという現象が生じている。

https://blog.ja.dev/entry/blog/2023/02/08/site-search-spam

現在の多くのウェブサイトは WordPress で構築されていることもあり、この汚染には当然 `s` パラメータを利用した URL は多く用いられている。 


# 何が起こったか

先に挙げた内容から「ウェブサイトの特定ページに任意のクエリを付与してアクセスすること」自体はごく当たり前に行われることである。

しかしそうなると WordPress のデフォルト検索機能が問題になってくる。

**`https://www.example.com` ではウェブサイトのトップページを表示したいにも関わらず、`https://www.example.com/?s=検索` では検索結果を表示してしまうのである。** 

通常のウェブサイトであればこれで問題は起こらないのだが、**CDN でクエリを無視している** 場合は話が別になる。 クエリを無視しているということは、**ウェブサイトのトップページと検索結果を同じキャッシュを利用して表示する** ということに他ならない。

まとめると、最初に説明した事象は以下の仕組みで発生した。

1. 外部利用者が `https://www.example.com/?s=.....` として検索結果が0件となるリクエストを行い、CDN がこの結果をキャッシュした
2. この状態で `https://www.example.com/` にアクセスするとCDNがクエリを無視する設定になっていたため 1 で作成したキャッシュを利用者に返した
3. そのため、トップページにアクセスしているにも関わらず、検索結果が0件ですというメッセージが表示された
4. キャッシュの有効期限が切れた状態でトップページにアクセスすることで、正常なトップページが表示された


対応策として、クエリを無視する設定をやめ、クエリ毎に異なるキャッシュを利用するような CDN の設定とすることで、この問題を回避した。 負荷の上昇は許容範囲であったため、そのような対処を取った。

# まとめ

そもそもの問題の発生頻度が低く、また、正常に利用している限りは起こりえない問題のため原因の特定には苦労したが、理屈を並べてみると腑に落ちた。

サイトの構成要素全体が俯瞰できないとなぜこのような問題が起こるかが分からないため、システム全体の理解のためのケーススタディとして有用ではないかと思い、ここで紹介した。
Amazon LinuxにMySQLをインストールし、データベースを作成・権限付与の設定

## はじめに
この記事では、この記事では、EC2インスタンスにアクセスし、MySQLをインストールし、データベースを作成し、ユーザーに権限を付与するまでの手順を説明します。

## この記事の対象者
```
AWS EC2を設定できている方
RDSでDBサーバーを構築できている方
```


## 手順1: EC2へのアクセス
まず、SSHを使用してEC2インスタンスにアクセスします。以下のコマンドを使用します。

```
ssh -i <キーペアのパス> ec2-user@<EC2インスタンスのIPアドレス>
```

## 手順2: MySQLのインストール
次に、以下のコマンドを使用してMySQLをインストールします。

```
sudo yum -y install mysql
```

## 手順3: MySQLへの接続
次に、以下のコマンドを使用してMySQLに接続します。

```
mysql -h <RDSエンドポイント> -u <指定したユーザー名> -p
```

## 手順4: データベースの作成
次に、以下のコマンドを使用してデータベースを作成します。

```
CREATE DATABASE IF NOT EXISTS '<作成するデータベース名>' DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
```

 - IF NOT EXISTS: このフレーズは、指定した名前のデータベースがすでに存在する場合には新たに作成しないという意味です。これにより、同じスクリプトを何度も実行してもエラーが発生しないようになります。

## 手順5: ユーザーの作成と権限の付与
次に、以下のコマンドを使用してユーザーを作成し、作成したデータベースに対する全ての権限を付与します。

```
CREATE USER '<作成するデータベース名>' IDENTIFIED BY 'password';
GRANT ALL ON '<作成するデータベース名>'.* TO '<作成するデータベース名>'@'%';
FLUSH PRIVILEGES;
```

- `IDENTIFIED BY 'password'`: これはユーザーのパスワードを指定します。この例ではパスワードとして password を設定しています。
- ``GRANT ALL ON '<作成するデータベース名>'.* TO '<作成するデータベース名>'@'%';`` : このコマンドは、特定のユーザーに対して特定のデータベースに対する権限を付与するためのものです。
- ``FLUSH PRIVILEGES;`` は、MySQLのコマンドで、ユーザーの権限に関する変更を即時に反映させるためのものです。

以上で、MySQLのインストールとデータベースの作成、ユーザーの作成と権限の付与が完了しました。これで、作成したデータベースを使用してデータの操作が可能になります。
Linux仮想マシン Syslog転送設定の方法

# はじめまして
関西でインフラエンジニアをしている、新卒3年目の　~~コンニャクンです！(※蒟蒻君って書きます)~~　しゅうペーです！※改名しました。

業務の中で技術的に詰まった問題やTipsを忘備として書いていきたいと思っています。
初投稿なので、Markdown記法もなれないですが、私の記事が誰かの手助けになれば幸いです。

# 今回の経緯
業務の中で[Winsyslog](https://www.jtc-i.co.jp/product/winsyslog/index.html)といった、Windows上でSyslogを管理するサーバを構築しました。
このWinsyslogでは、Linux仮想マシンに加え、仮想化基盤であるESXiや各種NW機器からのSyslogをこのサーバに集め、管理を行いました。

https://www.jtc-i.co.jp/product/winsyslog/index.html

その際にLinuxの仮想マシンからSyslogを、このWinsyslogに飛ばす設定をそれそれのLinux仮想マシンに入れる必要があったのでいろいろ調べた際の作業記録になります。

(実はログローテーションでもかなりてこずったので、それはまたの機会に...)

# ざっくりログ収集イメージ

画像上部のログソースとしては右から、Linuxの仮想マシン、仮想化基盤(サーバ機器)、一番左はFW機器の図のイメージですが、実際はもっといろいろのNW機器からのSyslogを集めました。

![画像1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3494465/a1b9b290-91c7-3b00-8e4d-604324fa556f.png)

ちなみにWindows仮想マシンがログソースにいないのは、Windowsのログ形式がSyslogではなく「Windows イベントログ」といった別の形式のためです。
(Windows仮想マシンのログ管理は、各マシンのイベントログ出力先の保存先容量を変更することで、対応しました。)

# Linux仮想マシン側の設定方法(Syslog転送設定)

さて、この記事の本題になります。つらつらと手順を記載していきます。


**1.管理者ユーザにてLinuxマシンにログインをします。**


**2.以下のコマンドを入力し、UDP514ポートが開いていることを確認します。**

`firewall-cmd --list-all`

UDP514ポートが開いていない場合は以下のコマンドを実行し、ポートを開きます。

`firewall-cmd --add-port=514/udp --permanent`
`firewall-cmd --reload`

**3.以下のコマンドを入力し、rsyslog.confの変更を行います。**
(rsyslogはログを管理するデーモンです。詳しくは調べてみてください！)

`vi /etc/rsyslog.conf`

**4.rsyslog.conf内の以下の内容を変更します。(画像は変更後となります。)**

- 「`#$ModLoad imudp`」を「`$ModLoad imudp`」に変更
- 「`#$UDPServerRun`」を「`$UDPServerRun`」に変更
- 「`#*.* @@remote-host:514`」を「`*.* @(WinsyslogのIP):514`」

![画像2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3494465/cf1edb4a-aff4-afe7-149b-60b2e6c7d200.png)
![画像4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3494465/691c43d7-860b-2792-cffb-a46802ff3b2d.png)

**5.viモードを終了保存します。**

`:wq`

**6.以下のコマンドを入力し、サービス再起動と起動の確認を行います。**

`systemctl restart rsyslog`
`systemctl status rsyslog`

State:runningとなっていることを確認できれば、仮想マシン側の設定は完了です！
![画像6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3494465/f9bafa1f-d2d8-4500-93d9-2ac157b559ab.png)

# 参考 (Syslog転送確認)
Syslogが転送されることの確認もしておきましょう。

以下のコマンドで、ログを出力できます。(コマンドの詳しい内容は調べみてください)
`logger -p syslog.info -t test "Hello!WinSyslog!"`

正常にSyslogが出力されているかは、以下のコマンドで確認します。
`tail /var/log/messages`

Syslogサーバ側の確認はそれぞれでご確認下さい。


# さいごに
今回が初投稿で、時間をかけて丁寧に記事を作成してみました。
今後は空いた時間に多くの記事をかけるよう、スピードアップしながら、質のいいものを書いていきたいです。
質問があればコメントしていただければと思います。
それではまたどこかで。
NetBoxをdockerでサクッと構築する

## 導入
```
$ git clone -b release https://github.com/netbox-community/netbox-docker.git
```

## とりあえず起動
```
$ cd netbox-docker
docker-compose up
```
## ステータス確認
```
root@shoma:/home/shoma/netbox-docker# docker-compose ps
           Name                          Command                   State         Ports
----------------------------------------------------------------------------------------
netbox-docker_netbox_1        /usr/bin/tini -- /opt/netb ...   Up (unhealthy)
netbox-docker_postgres_1      docker-entrypoint.sh postgres    Up               5432/tcp
netbox-docker_redis-cache_1   docker-entrypoint.sh sh -c ...   Up               6379/tcp
netbox-docker_redis_1         docker-entrypoint.sh sh -c ...   Up               6379/tcp
```
ポート開放されてないやん。

## NETBOXをポート開放する
```
version: '3.4'
services:
  netbox: &netbox
    ports:
      - 28080:8080
    image: docker.io/netboxcommunity/netbox:${VERSION-v3.5-2.6.1}
    depends_on:
    - postgres
    - redis
    - redis-cache
    env_file: env/netbox.env
```

## アクセス
おお、すごいねえ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2738475/ca569c1f-6791-3587-5dc4-524adf1a9875.png)
brew doctorを実行したら”Warning: You have unlinked kegs in your Cellar.”とメッセージが出た

# 環境
>・MacOS Ventura 13.2
・python3.10.4
・Homebrew 4.0.6
・Homebrew/homebrew-core (git revision 344423e6906; last commit 2023-03-10)
・Homebrew/homebrew-cask (git revision 9a50794e6f; last commit 2023-03-11)

# brew doctorを実行したら<font color='red'>”Warning: You have unlinked kegs in your Cellar.”</font>とメッセージが出た。

```python
% brew doctor
==> Downloading https://formulae.brew.sh/api/formula.jws.json
######################################################################## 100.0%
Please note that these warnings are just used to help the Homebrew maintainers
with debugging if you file an issue. If everything you use Homebrew for is
working fine: please don't worry or file an issue; just ignore this. Thanks!

Warning: You have unlinked kegs in your Cellar.
Leaving kegs unlinked can lead to build-trouble and cause formulae that depend on
those kegs to fail to run properly once built. Run `brew link` on these:
  openssl@3
```
# Chat GPTに対処方法を質問したところ
>「brew doctor の実行時に表示された警告メッセージですね。この警告は、Homebrewのメンテナーが問題をデバッグするために使用されるものです。もしHomebrewを使用しているすべての機能が正常に動作している場合は、心配する必要はありませんし、問題を報告する必要もありません。この警告を無視して構いません。
ただし、警告の内容としては、未リンクのkeg（バージョン管理されたパッケージ）が存在しているというものです。未リンクのままにしておくと、ビルドの際に問題が発生する可能性があり、これらのkegに依存しているパッケージが正しく実行されなくなる可能性もあります。
**解決方法としては、警告メッセージに示された通り、brew link コマンドを実行することです。具体的には、以下のようにコマンドを入力します。」**
```python
% brew link openssl@3
```
と言われた。

# ので、その通り「brew link openssl@3」コマンドを実行してみた
```python
% brew link openssl@3
Linking /usr/local/Cellar/openssl@3/3.0.8... 5535 symlinks created.
```

>再度 brew dotorを実行してみると
```python
% brew doctor        
Your system is ready to brew.
```
(システムはbrewする準備ができています。by google翻訳)
となった。Chat GPT有難う。
基本情報に25時間の勉強で受かったのでやったことと感想を書く



私事ながら基本情報技術者試験に合格したので勉強方法と感想などを書いていこうと思います。この記事が参考になる人はある程度コンピューターサイエンスの基礎知識はあるからコスパよく基本情報とろうと思う大学生とかだと思います。

プログラミング経験が多少あり、過去問道場の模擬試験でいきなり50％超える人は同じ試験対策で合格できると思います。

なのでまずは一回過去問道場の模擬試験を受けてみることをおすすめします。

# 事前知識

現在社会人二年目でIT企業ではあるものの総合職でエンジニアリングにはほとんど関わっていません。

大学院は人工知能系の研究をしていたのでプログラミングやAIはそこそこわかっていました。

しかし情報系ではなかったのでネットワークなどの知識はあまりありませんでした。かわりにサプライチェーンやマネジメントに関しては多少知識がありました。

## A試験関係

いつか受けようと過去問道場を少しだけかじっていました。

その時はだいたい正答率60％ぎりぎり超えないぐらいでした。

一番低かったのがテクノロジーで正答率50％くらいでした。

## B試験関係

競技プログラミングをやっており、現在は茶色コーダーです。

今のB試験は競技プログラミングの知識があればそれだけで簡単に合格してしまいます。

目安はAtcoderのB問題は間違えないレベルだと感じています。

# 勉強期間

三週間勉強しました。学習時間でいうと25時間ぐらいだと思います。

# 勉強方法

## A試験対策

まずテクノロジーがわからない理由の大半は体系だって勉強した経験がないことだったので基本情報のテキストを買ってきて一通り通読しました。もともとプログラミングをやっていたこともあり結構頭にすんなり入ってきました。

使ったテキストは**[令和04年 イメージ&クレバー方式でよくわかる 栢木先生の基本情報技術者教室 (情報処理技術者試験)](https://www.amazon.co.jp/令和04年-イメージ-クレバー方式でよくわかる-栢木先生の基本情報技術者教室-情報処理技術者試験/dp/4297123932)**を使いました。

通読が勉強時間で一番かかったところで、10時間くらいかかった気がします

知識の肉付けに関しては一回読んだだけで合格レベルに達したので後は何回か実践に近い形で過去問道場を回しました。

これはたまたま研究室の後輩に新試験になってから基本情報を受けた人がいたので聞いた対策なのですが、平成21年以降に絞って過去問道場の模擬試験を回していくという方法を行いました。

時間を測って問題をとき、だいたい4回位行いました。

このときの結果が70～75％くらいだったので試験としては安全圏に入ったなと感じていました。

一方で今になっての反省ですが、知識が完全に身についたかというと嘘になると思っています。分かる範囲は広くなったので改めてコンピューターサイエンスについて勉強しようと思います。

## B試験対策

B試験対策ですが、正直これといって勉強はあまりしませんでした。

新形式なので過去問がなかったためにIPAから出されているサンプル問題を一通りやりました。

ついでに不安だったので**[令和05年【上期】基本情報技術者 パーフェクトラーニング予想問題集](https://www.amazon.co.jp/令和05年【上期】基本情報技術者-パーフェクトラーニング予想問題集-山本-三雄/dp/4297131781/ref=asc_df_4297131781/?tag=jpgo-22&linkCode=df0&hvadid=622955539433&hvpos=&hvnetw=g&hvrand=15303332682977232601&hvpone=&hvptwo=&hvqmt=&hvdev=c&hvdvcmdl=&hvlocint=&hvlocphy=9053302&hvtargid=pla-1884997570786&psc=1&th=1&psc=1)**という本を買い、A試験B試験を一年分ときました。結果から言うと受かるだけならここまでやる必要はありませんでした。

というのもB試験は本当に競技プログラミングとの相性がいいです。問題に出てくる疑似コードの内容も競技プログラミングの典型問題のような感じなので、問題文をしっかり読まなくてもコードを読めばやりたいことがわかってしまい選択式の回答なのも相まって個人的には非常に勉強量を減らすことが出来ました。

一応セキュリティの問題が4問ありますがこれも文章読解力とITリテラシーがあれば4問全ミスすることはないので十分に安全マージンを確保できます。

# 感想

まず、資格をとった目的についてです。

現在社会人二年目なのですが、エンジニアへの転職を考えています。その際にやはりもってて損がない国家資格ということでとりました。

ただ難易度がおそらく旧試験よりも、特にB問題が簡単になっているのではと感じました。実際合格率も跳ね上がっており、資格としての価値は少し下がっていると思います。

この状態であれば応用情報技術者試験までとらないと能力の証明にはならないかもしれませんので応用情報も受けようか検討中です。

一方でとってみて思ったのは問題はよく出来ていて広範な内容をカバーしているので非常に勉強になりました。CSの勉強を続けていくモチベーションにもなりそうです。
Teams Wikiタブが消えた後にWikiの内容を確認したい時のメモ

# はじめに
Microsoft TeamsチャネルのWikiタブが2023年6月で廃止される旨アナウンスがあり、7月以降はTeamsのチャネルからWikiタブが非表示になりました。

期間内にOneNoteにエクスポートしていれば問題ないのですが、後からエクスポートに失敗しているセクションが見つかったため、慌てて対応した際のメモです。

とても簡易な内容ですが、同じように慌てている方の助けになるかもしれないので投稿しておきます。

# Wikiのデータはどこにある？

Microsoftのドキュメントに記載がありました。
> 1. Wiki が存在するチャネルの SharePoint サイトに移動します。
> 1. [ サイトコンテンツ] を選択します。
> 1. Teams Wiki データ フォルダーで、チャネルの特定のフォルダーを開きます。
> 1. .mht ファイルとイメージをダウンロードします。

https://support.microsoft.com/ja-jp/office/microsoft-teams-%E3%81%A7-wiki-%E3%82%92-onenote-%E3%83%8E%E3%83%BC%E3%83%88%E3%83%96%E3%83%83%E3%82%AF%E3%81%AB%E3%82%A8%E3%82%AF%E3%82%B9%E3%83%9D%E3%83%BC%E3%83%88%E3%81%99%E3%82%8B-8cd8ab0c-2314-42b0-a1d0-5c6c4c5e1547?storagetype=stage#bkmk_accesswikiinsharepoint

# Wikiデータは何で開ける？
Wikiのデータは.mhtファイル形式ですが、右クリック > 開く > ブラウザで開く を選択すると.eml形式でダウンロードされます。
.eml形式のファイルはoutlookで開くことができます。

# おわりに
1. SharePointのサイトコンテンツから.emlファイルをダウンロード
1. Outlookで開く
1. 中身をOneNoteに貼り付ける

上記3ステップでとりあえずWikiのエクスポートに失敗しているセクションの救出に成功しました。
焦りました。。
ISOフォーマット文字列の日付が存在するかチェックする

# 行いたいこと

- 2/31のような存在しない日付が入力されたときに日付の存在チェック
- 入力値はISOフォーマットの文字列

# 弊害となっていること

- Number.isNaN(new Date(isoString).getTime())は2/31のような日付の場合にチェック漏れしてしまう。
- ISOフォーマットには末尾にタイムゾーンを表す文字列があり、この文字列次第で日付の正規表現抽出し、new Date(iso)で作成したDateオブジェクトのgetDateで比較を行う処理に失敗する。

# 対処方法

- チェック時のみタイムゾーンをZに置き換えて日付を比較する

# サンプルコード

```
/**
 * ISOフォーマット文字列の検証
 * @param {*} isoString ISOフォーマット文字列
 * @returns 警告判定
 */
export const isoValidator = (isoString) => {
    // タイムゾーンを無視するため強制的に末尾をZに置き換える
    const enforceUtcIso = isoString.replace(/[+|-]\d{1,2}:\d{1,2}$/, "Z");
    // isoフォーマットが正常か判定する (例: 2023-01-01T01:02:03.444Z)
    const match = enforceUtcIso.match(
            /^(\d{4})-(\d{2})-(\d{2})T(0[0-9]|1[0-9]|2[0-3]):([0-5]\d):([0-5]\d)(.\d+Z|Z)$/
        );

    if (match == null || match.length < 8) {
        // isoフォーマットに不正確な部分がある場合
        return true;
    }

    const parsedDate = new Date(enforceUtcIso);
    if (Number.isNaN(parsedDate.getTime())) {
        // ISO文字列のパースに失敗した場合
        return true;
    }

    return (
        parsedDate.getUTCFullYear() !== parseInt(match[1], 10) ||
        (parsedDate.getUTCMonth() + 1) !== parseInt(match[2], 10) ||
        parsedDate.getUTCDate() !== parseInt(match[3], 10)
    );
};
```
AZ-900 合格体験記

先日、AZ-900に無事合格しましたので、勉強方法などをまとめておきます。
どなたかの参考になれば幸いです。

#### 試験概要
AZ-900（Microsoft Azure Fundamentals）とは、Microsoftの認定資格の一つで、クラウドやAzureの基本についての知識があることを証明できる最も基礎的な資格になります。

【試験時間】45分
【問題数】35問程度
【合格点】700/1000
【受験費】13,750円（税込み）
【受験場所】試験センター or オンライン

#### 試験範囲
・クラウドの概念について説明する (25–30%)
・Azure のアーキテクチャとサービスについて説明する (35–40%)
・Azure の管理とガバナンスについて説明する (30–35%)

#### 勉強方法
①Microsoft Learn で学習
　Microsoft Learnは、Azureやその他のMicrosoft製品について学ぶことができる無料の学習サイトです。試験範囲である下記３つのラーニングパスの学習を行いました。

　・[Microsoft Azure の基礎: クラウドの概念について説明する](https://learn.microsoft.com/ja-jp/training/paths/microsoft-azure-fundamentals-describe-cloud-concepts/)
　・[Azure の基礎: Azure のアーキテクチャとサービスについて説明する](https://learn.microsoft.com/ja-jp/training/paths/azure-fundamentals-describe-azure-architecture-services/)
　・[Azure の基礎: Azure の管理とガバナンスについて説明する](https://learn.microsoft.com/ja-jp/training/paths/describe-azure-management-governance/)

②「AZ-900 : Microsoft Azure Fundamentals 試験対策」の視聴
　Microsoftが提供する、オンデマンドのAZ-900解説動画があるのでそちらを視聴しました。
　
　[AZ-900 : Microsoft Azure Fundamentals 試験対策](https://info.microsoft.com/JA-HCSAzureHybridInfra-WBNR-FY21-02Feb-11-AZ900PrepSession-SRDEM61308_LP02OnDemandRegistration-ForminBody.html)
　
　上記のサイトでフォームに必要事項を入力して登録すると、記載したメールアドレスに動画のURLが送られてきます。画像付きで分かりやすいですし、①で学習した知識が整理できるのでかなりおすすめです。動画内でも言及されていますが、サービスの名称が変更されているものもあるのでその点だけ注意が必要です。

③Udemyの問題集を解く
　[最短で合格！Azure Fundamentals AZ-900 試験対策問題集](https://www.udemy.com/course/microsoft-azure-az900-exam/)
　
　こちらのコースを購入しました。過去問というわけではないですが、こちらに載っている問題が、そのまま本試験に出るなんてこともあります。実際に私はいくつか見たことのある問題が出て、とても助かりました。

④【AZ-900】Microsoft Azure Fundamentals 勉強アプリ
　https://simple-was-best.com/az-900/
　
　こちらはやらなくても問題はないです。時間に余裕のある方や、③を終わらせたけど不安だから初見で何か問題解きたいという人向けです。

#### おすすめの手順
①だけでインプットを完了させるのはなかなか難しいです。
はじめに①をさらっと読み、大枠を入れた状態で、②③でアウトプットしながら知識を固めていくのが効率のいい方法だと思います。

私は、イメージの掴みにくいネットワーク系の問題や、様々なサービスがあり混乱しやすいセキュリティ系の問題に少し苦戦しました。
ネットワーク系は、②で映像を見ながら学ぶとわかりやすかったです。
セキュリティ系は、③で問題を解きながら①を見返し、メモ帳などにサービスを一覧化して整理をつけると落とし込めたのでおすすめです。

**体感ですが、③の問題集を繰り返し解き、８割以上取れるようになっていれば、試験には高確率で合格できると思います！**


#### 受験方法
受験場所は、試験センターもしくはオンラインを選択できます。
私はオンライン（自宅）で受験しました。

[Microsoft 認定資格 | オンライン受験方法について](https://www.microsoft.com/ja-jp/events/top/certification)
オンライン試験を申し込む前に、こちらのサイト内にある「システムテストの実施」ができるか確認しておいた方がいいです。自宅のPCやネットワークが試験に適しているかなどを調べることができできます。

当日は、試験の30分前から入室が可能で、「OnVUE」という試験アプリの指示に従い、下記の手続きを済ませる必要があります。少し焦るので、早めの入室がおすすめです。
　・本人確認（顔写真、本人確認書類の写真を送る）
　・受験環境の確認（机の周り前後左右の写真を送る）

試験中はカメラオン、マイクオンの状態です。試験官の姿はこちらからは見えませんが、顔がカメラに収まっていないなど、何かある場合は向こうからチャットが飛んできます。
ちなみに私は、ぶつぶつ呟きながら問題を解いていたら控えるよう指示が来ました（笑）

結果は完了のボタンを押すのと同時にわかります。

#### まとめ
895点で一発合格することができました。
先ほども書きましたが、Udemyの問題集で８割以上取れるくらい知識が整理されていれば合格できると思うので、あまり身構えず挑戦してよいと思います！

私事ですが、次は更新のいらない（ここ大事）PL-900の資格を取得したいと考えています。
ちなみにAZ-900も更新いりません。

長くなりましたが、読んでいただきありがとうございました。
MoneyFoward MEのマイデータをスクレイピングして自分の好きな分類で集計する(Python)(2)

## はじめに
前回、ログインまでやりました。

https://qiita.com/yoshi_yast/items/04d52eda57bd4d58e208

①「Python・Selenium」を使用してMoneyFoward MEにログインする
②保有している資産をスクレイピングして、それぞれの保有資産を自分の欲しい分類にて集計 **(今回)**
③EXCELファイルにてダウンロードする **(今回)**

例として、「投資信託」について、投資先(全世界・先進国・S&P500・国内など)による区分ごとにダウンロードすることをやってみます。

## データ取得

保有している「資産名称」・「評価額」・「評価損益」をスクレイピング。
同時に、「元本」を計算する。それぞれの保有資産を、あらかじめ作成しておいた分類ファイル(ローカル)を読み取って、自分の好きな資産分類に集計できるようにしておく。

~~~mf_get.py
def get_asset_class(name):

    asset_class = ''
    for i in range(len(inv_trust_list.index)):
        if name == inv_trust_list['name'][i]:
            asset_class = inv_trust_list['asset_class'][i]
            break

    return asset_class

print('start getting portfolio')

# ローカルに用意したファイルの読み込み
inv_trust_list = pd.read_csv('./investment_list.csv',encoding='SHIFT-JIS')

# 項目の用意
set_column5 = ['asset_class','total','value','gainloss','name']

# 投資信託の表を取得
table_mf = browser.find_element(by=By.CLASS_NAME, value='table-mf')
trs = table_mf.find_elements(By.TAG_NAME,'tr')
nrow = len(trs)

mf_data = []

for i in range(1,nrow):
    mf_name = trs[i].find_elements(By.TAG_NAME,'td')[0].text      #銘柄名
    mf_value = trs[i].find_elements(By.TAG_NAME,'td')[4].text     #評価額
    mf_gainloss = trs[i].find_elements(By.TAG_NAME,'td')[6].text  #評価損益

    asset_class = get_asset_class(mf_name)

    mf_value = int(mf_value.replace('円','').replace(',',''))
    mf_gainloss = int(mf_gainloss.replace('円','').replace(',',''))
    mf_total = mf_value - mf_gainloss

    mf_data.append([asset_class,mf_total,mf_value,mf_gainloss,mf_name])

mf_df = pd.DataFrame(data=mf_data,columns=set_column5)
~~~

例）ローカルに用意したデータ(investment_list.csv)
![スクリーンショット 2023-06-26 141750.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167772/d13bb4d4-89bc-a179-335a-c5810c915333.png)


#### 集計 & EXCELファイルにダウンロード

~~~ mf_get.py
print('start data editing & start download excel')

# 集計
df = mf_df.groupby(['asset_class']).sum(['total','value','gainloss'])

# 日付をファイル名につけて、その日の資産一覧を保存
filename = './' + datetime.date.today().strftime('%Y%m%d') + '_asset-list.xlsx'

with pd.ExcelWriter(filename) as writer:
    df.to_excel(writer, sheet_name='投資信託集計')

print('done')
~~~

例）投資信託の区分ごとに、以下の表がダウンロードされる
![スクリーンショット 2023-06-26 141056.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167772/1292bb0c-be67-d1e1-35c3-c75b1c4dba1b.png)

# まとめ
- MoneyFoward MEの資産データを取得し、自分の好きな分類にてして、集計・ダウンロードしました。
- 使用しているサービスが100%自分の思い通りのものであればよいですが、そうでない場合は、自分でカスタマイズを加えることができるのは、プログラムを勉強するモチベーションになるかと思います。
nodebrewによるNode.jsのインストール手順まとめ

# はじめに

nodebrewによるNode.jsのインストール手順をまとめました。

**実行環境**
- Apple M2 Max
- macOS Ventura 13.2.1

# インストール手順
## 準備：Homebrewをインストール
Homebrewがインストールされているかを確認。
```bash
brew -v
```

バージョンが表示されない場合はHomebrewをインストール。
([Homebrewのホームページ](https://brew.sh/index_ja)にあるインストール用のスクリプトを実行)

## nodebrewをインストール

以下のコマンドでnodebrewをインストール。

```bash
brew install nodebrew
```

インストールできたら以下のコマンドを実行しておく。

```bash
nodebrew setup
```

パスに関する記述を`~/.zshrc`に追加する。
```bash
export PATH=$HOME/.nodebrew/current/bin:$PATH
```

`~/.zshrc`を読み込む。

```bash
source ~/.zshrc
```

## Node.jsをインストール

インストール可能なNode.jsバージョンを確認。

```bash
nodebrew ls-remote
```

バージョンを指定してNode.jsをインストール。

```bash
nodebrew install-binary v18.16.0
```

※安定版をインストールしたい場合は次のコマンドを実行

```bash
nodebrew install-binary stable
```

※最新版をインストールしたい場合は次のコマンドを実行

```bash
nodebrew install-binary latest
```

指定したバージョンがインストールされたかを確認。
(指定したバージョンが表示されたらOK)

```bash
nodebrew ls
```

## 使用するNode.jsバージョンを設定

使用するNode.jsのバージョンを指定する。
```bash
nodebrew use v18.16.0
```

使用されているNode.jsのバージョンを確認。
(指定したバージョンが表示されたらOK)

```bash
node -v
```
[Xcode]ライブラリのログが表示されない場合の対処法

# 投稿の経緯
外部ライブラリを用いた開発で不具合が発生し、解決に向けてサポートチームの方とやり取りを進める中でログの提出を求められた。

だが求められたログはどこにもない...

サンプルアプリではログは表示されている。しかしこちらで開発しているアプリでは表示されていない。。

調査を進める中でログが表示されていなかった原因を特定したので記事にする。


# 対処法
![スクリーンショット 2023-06-23 11.12.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643372/b2f771fb-faf0-c956-5757-d35996056d5c.png)

**Edit Scheme**...を選択

![スクリーンショット 2023-06-23 11.16.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643372/260d966c-9ab5-6088-e237-990466f8f1ca.png)

**OS_ACTIVITY_MODE**のチェックを外してcloseをタップして保存

これで外部ライブラリのログを確認することができます。

逆にライブラリの不要なログを表示したくない場合は、

1. OS_ACTIVITY_MODEを追加
2. Valueにdisableと入力
3. チェックをつける

で非表示になります。
MarkDownk記法のまとめ

#

### 記述例

### 結果

# 見出し
### 記述例
1個から6個のシャープ(#)で見出しを記述する。※シャープと見出しの間には半角スペースを一ついれること。
```
# 見出しh1
## 見出しh2
### 見出しh3
#### 見出しh4
##### 見出しh5
###### 見出しh6
```
### 結果

># 見出しh1
>## 見出しh2
>### 見出しh3
>#### 見出しh4
>##### 見出しh5
>###### 見出しh6

# 箇条書きリスト
ハイフン(-)、プラス(+)、アスタリスク(tab)のいずれかで箇条書きリストを記述可能。
### 記述例
```
- リスト1
    - ネスト リスト1_1
        - ネスト リスト1_1_1
        - ネスト リスト1_1_2
    - ネスト　リスト1_2
- リスト2
- リスト3 
```
### 結果

> - リスト1
    - ネスト リスト1_1
        - ネスト リスト1_1_1
        - ネスト リスト1_1_2
    - ネスト　リスト1_2
> - リスト2
> - リスト3

# 番号付きリスト
数値+半角ドットで番号付きリストを記述可能。
### 記述例
```
1. 番号付きリスト1
    1. 番号付きリスト1_1
    1. 番号付きリスト1_2
1. 番号付きリスト2
1. 番号付きリスト3

```
### 結果
> 1. 番号付きリスト1
    1. 番号付きリスト1_1
    1. 番号付きリスト1_2
> 1. 番号付きリスト2
> 1. 番号付きリスト3
# 引用

### 記述例
```
> Change before you have to.
> Jack Welch（ジャック・ウェルチ）
> 変革せよ。変革を迫られる前に。
> （米国の経営者 / 1935～2020）
```
### 結果
> Change before you have to.
> Jack Welch（ジャック・ウェルチ）
> 変革せよ。変革を迫られる前に。
> （米国の経営者 / 1935～2020）
# 二重引用

### 記述例
```
>　Steve Jobs
>> Stay hungry. Stay foolish.
```
### 結果
> Steve Jobs
>> Stay hungry. Stay foolish.
# pre記法(スペース4 or タブ)
コードの前にスペース4 or タブを挿入すると、コードブロック表示になります。

### 記述例
```
    class Hoge
        def hoge
            print 'hoge'
        end
    end
```
### 結果
    class Hoge
        def hoge
            print 'hoge'
        end
    end

# code記法
バッククオート(\`)で囲むことでコードの一部を表示可能です。
※クオート（'）とバッククオート（\`）は似ているので注意

### 記述例
```
`sudo rm -rf /*` は使ってはいけません
```
### 結果
> `sudo rm -rf /*`　は使ってはいけません
# 斜体（italic）
```
🙋‍♂️ _🙋‍♂️_　🙋‍♂️
*🐈* 🐈
```
### 記述例
> 🙋‍♂️ _🙋‍♂️_　🙋‍♂️
> *🐈* 🐈
### 結果
# 太字（bold）
### 記述例
```
normal **bold**
normal __bold__
🐈 **. . .** . . . . . .
```
### 結果
normal **bold**
normal __bold__
🐈 **. . .** . . . . . .

# 平行線
### 記述例
```
***
---
____
* * *
```
### 結果
>***
> ---
> ____
> * * *

# リンク
### 記述例
`[表示文字](リンクURL)`形式でリンクを記述できます。
```
[Google](https://www.google.com)
```
### 結果
> [Google](https://www.google.com)

# リンク2
リンクを置くだけで良い感じにしてくれます。
### 記述例
```
https://www.google.co.jp/
```
### 結果
https://www.google.co.jp/

# 取り消し線
### 記述例
```
~~🙋‍♂️~~ 私はやってません
```
### 結果
~~🙋‍♂️~~　私はやってません

# シンタックスハイライト
言語を指定することで良い感じに色をつけてくれます。
### 記述例
~~~
```python
print('Aくん:ピザって10回言って！')
for i in range(10):
    print('Bさん: ピザ')
```
~~~
### 結果

```python
print('Aくん:ピザって1000回言って！')
for i in range(1000):
    print('Bさん: ピザ')
```
assertEqualsとは

## アサーション

:::note
**プログラム内で条件が真であることを確認するための仕組み**
アサーションを使うと、特定の条件がプログラムの実行時に満たされているかどうかをチェックできる
:::

## assertEquals

:::note
アサーションの一種
**JUnitフレームワークなどのテストフレームワークで使用されるメソッド
→期待される値と実際の値を比較し、それらが等しいかどうかを検証**
:::


### 例

```java
import static org.junit.Assert.assertEquals;

public class ExampleTest {
    @Test
    public void testAddition() {
        int result = add(2, 3);
        assertEquals(5, result); // 期待値と実際の値を比較
    }
    
    private int add(int a, int b) {
        return a + b;
    }
}
```

testAdditionメソッド内でaddメソッドを呼び出す
→その結果をassertEqualsで検証

引数として期待される値（5）と実際の値（addメソッドの結果）を渡して比較
→等しいかどうかを判定

☆等しくない場合
→アサーションエラーが発生して、テストがfail

*** 

- アサーションはテストコード内で使用
→**予期しない結果やバグを検出するための手段**

- **assertEqualsメソッドは、値の等価性を検証する際によく使用されるアサーション**
【python】中身がわからないファイルの拡張子の取得方法

## はじめに
requestsを使用してファイルをダウンロードするときに、ファイルの種類がわからず拡張子もないために正しくダウンロードできないという問題が発生していまいました…
それの対処法を記載したいと思います。
## 実行環境
Windows11
python : 3.7.9

## 発生した問題
requestsモジュールを使用してファイルをダウンロードをしたかったのですが、そのファイルのファイル名も種類もわからないため、何の拡張子をつければわからないという問題に直面しました。

## 解決策
私は[mimetypesモジュール](https://docs.python.org/ja/3/library/mimetypes.html)を使用して、ヘッダー情報からファイルの拡張子を推測するという解決策でこの問題を解消しました。

### 1. mimetypesモジュールのインポート
以下をpythonファイルに記載してmimetypesモジュールをインポートします
`import mimetypes`
### 2. requestsモジュールを使用してファイルのダウンロード
```
response = requests.get(取得したいファイルのURL)
```
### 3. ヘッダー情報から拡張子の推測
```
拡張子 = mimetypes.guess_extention(response.headers['Content-Type'])
```
これによってrequestsモジュールで取得したファイルのヘッダー情報から拡張子を推測することができました。

## 最後に
もし種類がわからないファイルの拡張子を取得したい状況になった際にご参考になれば幸いです。
ご覧いただき有難うございました！
2023年07月何故かベストアンサーをつけてくれない質問

[2023年7月の質問の中から整理します](https://qiita.com/Keiji_otsubo/items/79ce317a8518d9dc5732)


+ [We can’t find an attribute item record for the 'Hardware Color' Global Attribute. ](https://trailhead.salesforce.com/trailblazer-community/feed/0D54V0000794hWo)
+ [I have the following error: "We can’t find an attribute item record for the 'Hardware Color' Global Attribute." but I have created it.](https://trailhead.salesforce.com/trailblazer-community/feed/0D54V00007UsMkW)

:::note
I completed this trial, but I haven't created configuration attributes Hardware, and Measurements in the product-related list. I have created only System Voltage in the product-related list, The remaining two are created in the configuration attributes record page itself, and then create attribute sets Hardware and Measurements in the attribute sets record page itself. After that in each attribute sets(Hardware and Measurement) related list create attribute items and Product attribute sets. 

https://trailhead.salesforce.com/trailblazer-community/feed/0D54S00000Eo4gKSAR
:::

+ [How can the following hard-code URL be replaced in Lightning when using email template?](https://trailhead.salesforce.com/trailblazer-community/feed/0D54V000077lbLV)

:::note

Try this

```
LEFT({!$Api.Enterprise_Server_URL_580} , FIND('/services', {!$Api.Enterprise_Server_URL_580})) & "/lightning/r/Contact/"& {!Contact.Id} &"/view"
```

:::


# 解決したらしいけど、内容は全く理解できない

+ [Only owner can view email message under activity feed.](https://trailhead.salesforce.com/trailblazer-community/feed/0D54V000078RItB)

:::note
This issue is resolved. I've to set the Status as '3' in the flow (on creation of activity feed on Object: email message)otherwise the status on creating the record it will be default to draft and the other users can't view it. 
:::

+ [Unable to validate the challenge Control Access to fiels](https://trailhead.salesforce.com/trailblazer-community/feed/0D54V0000798LO0)
as演算子について

as演算子とは？

キャスト演算子。
as演算子、as?演算子、as!演算子の3種類がある。

キャストとは？
型を変換する際に行う操作。
アップキャスト：型をより汎用的なものに変換する。
ダウンキャスト：型をより具体的なものに変換する。

・as演算子
→アップキャスト時に用いる。
変換できない場合、エラーが発生する。

・as?演算子
→ダウンキャスト時に用いる。
変換できる場合は、その型に変換したオプショナル型で返す。
変換できない場合、nilを返す。

・as!演算子
→ダウンキャスト時に用いる。
変換できる場合、その型に変換した非オプショナル型で返す。
変換できない場合はエラーが発生するため、
変換の成功が保証されている場合に使用する。

間違った型にキャストすると、実行時エラーが発生する可能性がある。
【 Thymeleaf】複数項目の間に「/(スラッシュ)」を適度に入れる

複数項目を表示する際に、項目を見やすくするように「/」を適度に入れる実装をしました。メモして残しておきます。

# やりたいこと
バックエンド側からフロント側に、複数の項目が渡ってきます。複数の項目を画面に表示する際に、項目間に「/（スラッシュ）」を入れる。
項目は、毎回同じ値、同じ数が渡ってこない前提で、検討する。



# 実装
Javaとthymeafを使って実装をします。

```java:SlashController.java
package com.example.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class SlashController {
	
	@GetMapping("/slash")
	public String displayIndex(Model model) {
		model.addAttribute("category", "果物");
		model.addAttribute("apple", "1");
		model.addAttribute("orange", "1");
		model.addAttribute("grape", "1");
		return "slash/index";
	}
}

```

まず、Javaで「～/slash」でリクエストされると、「category」「apple」「orange」「grape」を画面に渡すコードを書きます。本稿では「thymeaf」での制御を示したいので、テストデータ的な位置付けです。
「apple」「orange」「grape」は0または1が入ってくる想定です。

```html:index.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
  <head>
    <title>Hello</title>
    <meta charset="utf-8" />
  </head>
  <body>
    
    <table border="1">
      <thead>
        <tr>
          <th>分類</th>
          <th>内容</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td th:text="${category}"></td>
          <td th:text="${(apple == '1' ? 'りんご' : '') +
            (apple == '1' && orange == '1' ? '/' : '') +
            (orange == '1' ? 'オレンジ' : '') +
            ((apple == '1' || orange == '1') && grape == '1' ? '/' : '') +
            (grape == '1' ? 'ぶどう' : '')}"></td>
        </tr>
      </tbody>
    </table>
    
  </body>
</html>
```

テーブルタグとthymeafを組み合わせて、バックエンド側からの値を受け取ります。「apple」「orange」「grape」が1のときは、対応する日本語名に置き換えています。そして、↓スクショのように「内容」の列に「りんご/オレンジ/ぶどう」と項目間に「/」が入っています。

画面
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/605915/5a3de8fa-b512-0240-7bfb-96c7ae065ceb.png)

「/」を入れる部分はこちらです。「/」の前の項目と次の項目があれば、「/」をいれるように記載しています。こうすると、バックエンドから値が2つだけ渡ってきたとしても、項目間だけに「/」が入ります。
```html
(apple == '1' && orange == '1' ? '/' : '') +
```
```html
((apple == '1' || orange == '1') && grape == '1' ? '/' : '') +
```

ひとまず、やりたいことは表現できました。

もし誰かの役に立ったら、嬉しいです。
最後まで読んでいただいた方、ありがとうございました。
【合格体験記】AWS-SAAを更新しました

3年ぶりにAWS-SAAを受験し合格しました。AWS資格は有効期限があって、取得したSAAの期限が切れそうでしたので再度挑戦しました。前回は全く勉強方法が違ったので、メモとして残しておきます。1回目の受験より、勉強期間も勉強量も少なくて済みました。

本投稿はAWS-SAA資格の「更新」の合格体験記です。SAA試験の紹介は他の記事に譲ります。


1回目の受験の体験記はこちら

https://qiita.com/aki_number16/items/0b93027fe39e083c815c


# 受験の理由
・更新期限が迫ってきたため
・3年前にSAA資格を取得した際は苦労したので、失効するのはもったいないと思ったため
・AWSはアップデートが早く、知識を刷新した方がいいかなと思ったため
・開発エンジニアだが、AWSの知識も必要になる場面が増えてきたため

# 前提
・2020年2月にSAAを取得
・実務での構築経験はほぼなし
・AWS-CLFは取得、更新済

# 勉強方法・期間
・[Tech Stock](https://techstock.jp/ "techstock")で問題を繰り返りし解き直す（問題番号100番以降を2周しました）
・勉強期間は2週間くらい
・5/7の正答率を目指す
・間違えた問題の解説はしっかり読む
・わからない用語は都度調べる（なるべく公式ドキュメントにあたる）
・仕様変更などで知識の更新が必要なことは手元のメモに残しておく
・毎日最低1セットは問題を解く
・勉強を始める前に、受験日を決めて申し込みを済ませる

# 結果
・2023年2月　合格

# 思ったこと
AWSはアップデートも早いですが、テスト問題もどんどん変わっています。テストがどんどん改定されるため、知識のアップデートとしてSAA試験を受け続けるのは有益だと思いました。テスト問題改定でどこが変わったのかを、公式で確認しておくとよいかと思います。AWSがどこに注力しているのかを示していると思うので。
＊
セキュリティ系はしっかり勉強しておきたいですね。実務でも大事な要素ですし、どんなアプリケーションにも必要な知識になります。AWSのサービスだけの知識を覚えるのではなく、基礎的な知識──例えばファイヤーウォールとは とか　も調べて業界一般的に使える知識を入れておきたいです

知識を覚えるだけでは足りないとも思いました。問題慣れをしていないと、本番慌ててしまいそうです。サービスの特徴だけでなく、利用シーンや他のサービスとの組み合わせも知っている必要があります。

＊

一度合格しているので、思い出す作業＋知っている知識のアップデート＋新しい知識のインプットが今回の主な勉強内容でした。割合としては、3:1:1のイメージです。インスタンスタイプなど細かい知識は忘れていたので、もう一度知識を入れ込む必要がありました。

ただ、1回目の受験の時より、勉強がかなり楽になりました。基礎的な知識はある程度入っていますし、勉強方法も確立できていたからです。試行錯誤する時間が減り、やるべきことを淡々と進めるだけでした。また、今回は参考書を買いませんでした。公式を見れば、必要な情報は手に入るし、参考書を熟読する時間をもなかったからです。

基本的には問題演習に注力して、足りないところは調べるやり方でうまく行きました。1回目の受験の時とは、問題が改定されていたので、試験範囲は勉強の前に確認していました。個人的な感覚としては、問題は難化していたと思いますが、1回目の受験の時の経験が土台となって、なんとか乗り切れました。

＊

一番変わっていたのは、「合格発表の仕方」でしょうか。2020年2月に1回目の受験の時は、受験が終わると、アンケートに答えると合否が表示されていました。そして、何時間後かに点数が申し込みをしたサイトから見えるようになっていました。

が、今回は、受験のその場ではわからない（合否が表示されない）ようになっていました。メールで合否がわかるようになっていました。私は平日の朝受験したのですが、夕方には申し込みをしたAWSのサイトで合否が表示されされていました。（自分でサイトを見に行くと、メールよりはやく合否がわかります）

＊

SAA資格を更新しようか、迷った時期もありましたが、うけてみてよかったです。それは1回目の受験の時よりも、自分が強くなったことを少しは実感できましたし、知識の調整ができたからです。

開発系であっても、インフラの知識が必要になる瞬間もあります。自己研鑽として知識をアップデートするのに資格勉強はちょうどいいかと思いました。知識の正解不正解がわかるのためです。

合格体験記は多いと思いますが、更新の合格体験記は少ないと思います。この記事が誰かの役に立ったら嬉しいです。

最後まで読んでいただいた方、ありがとうございました。
Uvicornのローカルサーバーへは 127.0.0.1 でアクセスしよう

船井総研デジタルのよもぎたです。

FastAPIで開発しているときにUvicornでたてたローカルサーバーに ```http://127.0.0.1:8000/``` でアクセスするとページが表示されるのに、 ```http://localhost:8000/``` でアクセスすると接続すらできないという状況に遭遇しました。原因と調べて分かったことを共有いたします。

# 原因と結論

## 原因
1. Uvicorn はデフォルトでは ```127.0.0.1:8000``` をListenする。```::1:8000``` （IPv6ループバックアドレスのTCP8000番ポート）はListenしない。
1. おそらくOSの動作（仕様）だと思うが、 ```localhost``` は ```::1``` に名前解決されていた。
1. そこで、ブラウザはListenされていないIPアドレス＆ポートの組み合わせにアクセスすることとなり、接続すらできなかった。

## 結論
Uvicorn がIPv4アドレスでListenしているのに、ブラウザがIPv6アドレスでアクセスして、すれ違いが生じてしまっていた。それを避けるために、特にListenするアドレスを指定せずにUvicornを起動した場合は、 ```http://127.0.0.1:8000/``` と明示的にIPv4アドレスでアクセスするのがよいでしょう。


# 調べて分かったこと

## 実際の挙動の観察してみる

* ブラウザが ```http://localhost:8000/``` でアクセスしたときに、IPv6ループバックアドレスのTCP8000番ポート宛に ```TCP-SYN``` が送信されているのをWireshakで確認。このときはループバックインターフェースを ```tcp port 8000``` でフィルタしてキャプチャしました。
* Uvicornが 127.0.0.1:8000 でListenしているのは、Windows/Linuxそれぞれ次のコマンドで確認できます。
    * Windows: ```netstat -abn -p tcp``` （管理者権限で実行するのが確実）
    * Linux: ```sudo ss -lnpt```
* 私が確認したのは、Ubuntu 22.04.2 LTS環境でした。

## 調べなかったこと

localhostをIPv4とIPv6どちらのループバックアドレスに解決するか、いろいろなOSを用意しての調査はしていません。

## Uvicornの公式ドキュメントをあたってみた

* [Socket Binding のセクション](https://www.uvicorn.org/settings/#socket-binding) に、デフォルトで **127.0.0.1をListenする** と明記されていました。
* ListenするIPアドレスを明示的に指定するには ```--host ADDR``` とすればよいことが分かりました。
* IPv6にも対応していることが明記されていました。
* 複数のIPアドレスとポートの組をListenさせる手順について記載が見つけられませんでした。

## ドキュメントに記載がないので実際に試してみて挙動を確認してみた

* ```--host ADDR``` を複数指定してみた ⇒ 後に指定した *ADDR* **だけ** をListenした。
    * 例１） ```--host 127.0.0.1 --host ::1``` とした場合、**::1:8000だけをListenして、127.0.0.1はListenしませんでした。**
    * 例２） ```--host fe80::dead:beef --host 192.168.1.100``` とした場合、 **192.168.1.100:8000だけをLisnして、fe80::dead:beefはListenしませんでした。**
* ```--host ADDR1,ADDR2``` などいくつか思いついたセパレータでアドレスを区切って2つ指定してみたが、エラーになってUvicornの起動すらできませんでした。

## それならソースコードを追うしかないじゃない！

ソースコードは正義です。ジャスティスです。夢も希望も（時には絶望も）あります。

追って行って注目したのは次の2か所です。（2023/06/28 06:30 JSTに確認しました）

[https://github.com/encode/uvicorn/blob/master/uvicorn/main.py#L360](https://github.com/encode/uvicorn/blob/master/uvicorn/main.py#L360)

[https://github.com/encode/uvicorn/blob/master/uvicorn/config.py#L513](https://github.com/encode/uvicorn/blob/master/uvicorn/config.py#L513)

ひとつの ```--host``` オプションに複数のアドレスが指定されてくることを想定していないし、```--host```オプションが複数回指定されることも想定していません。

今回は絶望を引いてしまいました。

## 複数のIPアドレスとポートの組をListenするにはどうすればよいか

[こちらのIssueでのやりとり](https://github.com/encode/uvicorn/issues/571)を見る限りでは、[```Hypercorn```](https://github.com/pgjones/hypercorn) や [```Gunicorn```](https://gunicorn.org/) をつかうと実現できるようです。
FastAPIはASGIでの運用が想定されているようで、上記の2つのうちそれに対応しているのは ```Hypercorn``` の方ですので、そちらを使うのが良さそうです。
また、UvicornのまえにNGINXを置くという解決方法もあるようですが、ローカル開発には過剰なような気もします。

# まとめ
今回はたまたまFastAPI+Uvicornの環境で発生しましたが、ローカル開発では様々なケース（開発ツール等）で起こりうるシナリオだと思います。本件を頭の片隅に置いておいていただくと、似たケースかも、と問題解決に役立つときが来るかもしれません。もちろん、そもそも問題が起きなければそれが一番ですが。

最後までお読みいただきありがとうございました。
TypeScriptのany型を厳重に扱えるようになりたい

「any型はダメ！」TypeScriptを学ぶ時に一番はじめに学びます。
結局どうすればいいのか？知識が曖昧だったので調べ直してみることにしました！

:::note info
【本記事の結論】
any型を使うことは、プロダクトに対して地雷を設置していくような行為。
利用時にはチームで話し合った上で利用の是非について決定しないといけないもので気軽に使えるものではない。
:::

## TypeScriptのany型の問題点とは？
実行エラーが発生するようなコードでコンパイル時にエラーを発生させないことだと言えます。

TypeScriptは、変数や関数に静的な型付けをすることでコードのバグを発見できることにあると思います。

any型を利用することはコンパイラーが型のチェックを行わない、TypeScriptの利点を自ら放棄してしまっているような状態になってしまいます…。

:::note info
TypeScriptはコンテキストから型を推論できない時は、型をany型として扱います。
tsconfig.jsonに`noImplicitAny: true`を設定することで暗黙のanyを禁止できます！
:::

any型を利用したいと考えたときに**プログラムの設計を変更してロジックをシンプルにする**ことを検討しany型が本当に適切か？をよく検討してください。

any型の定義を定義してしまうことは**プロダクトにとって重大な罪であると認識することが重要**だと感じました。

## any型の利用について
any型は技術負債になるため原則は使わないとされつつ、**やむを得ずany型を使用する場合**が存在しているからです。

下記の記事で詳しくかつ、わかりやすく解説されているのでこの記事では長く記載しません。
自身のメモとしてany型をしたいと思ったときに使いたい記述例を残しておきます。

https://qiita.com/uhyo/items/aae57ba0734e36ee846a

### オブジェクトにanyを使いたくなったとき

```diff_typescript
- function(obj: any): number {
+ function(obj: {num?: number }): number {
  // 処理省略
}
```
オブジェクトで、`num`プロパティを持ってなくてもいいけど、持っている場合はプロパティ`num`はnumber型とする。
の意味になるらしい。

### 型アサーション`as`を利用する
型を強制的に修正する他mの構文なのでこちらもany型と同様にできるだけ使わない方が良いとされています。
ただ、`as`を利用せざるを得ないパターンもあるらしく、それをメモとして残します。

```diff_typescript
function hode<T extends object>(obj: T): T {
-  const hogehoge = {};
+  const hogehoge = {} as T;
  for (const key in obj) {
    hogehoge[key] = obj[key];
  }
  return hogehoge;
}
```

`const hogehoge = {}`の部分の型が`{}`として推論されます。
関数はT型を返そうとしているためエラーになります。
そこで`as`を使ってT型であるとすることでエラーを解消できます。

`as`は強制的に型を変えるので、TypeScriptの型システムに抗う行為になるため危険な拡張だと言えます。
ただ、今回のパターンだと関数の中に危険性が閉じ込められている。
スコープが切られているという点で`as`の利用を許容するという判断ができます。


## がんばらないTypeScriptというアプローチもある
運用歴の長いサイトではいきなり全てをTypeScriptに置き換えていくのは困難だと思います。

:::note alert
教育や運用などいろんなコストが必要になります。
技術的負債も多い場合はさらにリソースの確保が難しくなると言えます。
:::

ではやめるか？となるのではなくて、少しずつ取り入れていくアプローチとして「がんばらないTypeScript」というのが提唱されています。

https://typescriptbook.jp/reference/values-types-variables/any#%E3%81%8C%E3%82%93%E3%81%B0%E3%82%89%E3%81%AA%E3%81%84typescript

少しでもTypeScriptを利用して安全な状態にしておく方がいいよね…の精神に則ってできる一部だけ進めるというアプローチです。

## 【まとめ】TypeScriptのany型について
本記事ではany型について書きました。
any型について考え方を改めることができました。

どこかでany型を定義するのは仕方ないと考えてしまっていました。

**とにかくany型は使わない**

TypeScriptの熟練度はまだまだですが、any型の利用だけは厳格に守っていきたいと改めて思いました。

## 参考記事
https://qiita.com/uhyo/items/e2fdef2d3236b9bfe74a#any%E5%9E%8B

https://qiita.com/uhyo/items/aae57ba0734e36ee846a
Certified Ethical Hacker (CEH)　ホワイトハッカーとは

# はじめに
日本では2025年問題や2040年問題が危惧されているように、高齢化社会に対する労働力不足は重要な課題の一つです。

IT業界のセキュリティ分野も例外ではありません。
総務省の公開している[サイバーセキュリティ政策の動向](https://www.soumu.go.jp/johotsusintokei/whitepaper/ja/r04/html/nd245700.html)などを踏まえて、セキュリティ人材も不足しているのが現状です。

ホワイトハッカーの国際資格として、Certified Ethical Hacker (CEH)が認知されています。
本記事を書くにあたり、後述するGSX社のトレーニングコースを受けて、CEH試験に合格しました。

本記事ではホワイトハッカーに必要な知識・スキルを証明するためのCEHの概要について記載しています。

## Certified Ethical Hacker（CEH）

### Ethical Hacker
Ethical Hackerとは何でしょうか？

Ethical Hackerを日本語に訳すと倫理的ハッキングと呼びます。
倫理的ハッキングとは、ハッキング技術を使用してコンピュータ システム、ネットワーク、アプリケーションの脆弱性を特定し、暴露する行為を指します。

ホワイトハッカーはセキュリティ向上など善意的な目的によって倫理的ハッキングを行い、発見した脆弱性などをシステム管理者に報告します。

Ethical Hackerについて、以下snykのブログが分かりやすいです。
ブログ内でもEthical Hackerとして必要な専門知識やスキルを証明するため方法として、CEHの記載があります。

https://snyk.io/blog/day-in-the-life-ethical-hacker/?utm_campaign=aom_2023&utm_medium=social&utm_source=twitter-organic&utm_content=day-in-the-life-ethical-hacker

::: note
ハッカーとは、本来コンピュータに対する専門スキルを持ち、ハードウェアやソフトウェアの知識を利用して問題解決を図る人やサブカルチャーの意味合いで使用されていました。
ホワイトハッカーは、ハッキングのスキルを防御のためのセキュリティに活かす人を指します。従って知識や技術だけでなく、倫理観も問われます。
:::

### 受験方法
CEHは、[**EC-Council**](https://en.wikipedia.org/wiki/EC-Council)によって認定されます。

世界145カ国以上で通用する国際資格です。

https://www.eccouncil.org/

CEHの受験方法は大きく2つあります。
トレーニングを受けて受講する場合は、日本ではGSX社などの代理店を通して受講することができます。

https://www.gsx.co.jp/services/securitylearning/eccouncil/ceh.html

なお、情報セキュリティに関する2年間の実務経験がある場合は、トレーニングを受けることなく試験を受けられます。

CEHにはバージョンがあり、現在の最新バージョンはCEH v12です。
最新のトレンドや動向を踏まえて、12〜18ヶ月毎の期間で更新されます。

また、CEHのコースキットにはCEH Proや、CEH Eliteの種類があります。
本記事執筆時点でCEH Eliteの場合は、CEHPractical（実技試験）のバウチャーも付属します。

::: note
CEHとCEHPractical（実技試験）に合格すると、「CEH MASTER」の称号が与えられます。
:::

### 試験概要
試験はピアソンなどテストセンターで受けるか、自宅で受けることもできます。

合格基準の70%はあくまで目安です。
EC-Councilのサイト上でも明確な基準は公表されていなく、認定に関する質問には60%〜80%と記載があります。

- 問題数：125問
- 合格基準：70%以上
- 試験時間：制限時間4時間
- 出題形式：選択式

### 学習方法
トレーニングコースに申し込むと、以下のコンテンツが利用できます。
試験の準備については、CEH Eliteのみの特典です。

- 電子テキスト
- ラボ
- 試験の準備（101個のトピックに関するテストと、125問の模擬問題が2セット）

::: note warn
コンテンツを利用するにあたり、ASPENやラボ環境についてはアカウントの登録が必要です。
:::

### 資格の更新
CEHの認定日から3年間有効です。

更新する場合は、年間USD80と、120のECEクレジットが必要です。
詳細については、[ECE POLICY](https://cert.eccouncil.org/ece-policy.html)から確認できます。

# おわりに
CEHは、米国国防総省、英国諜報機関GCHQなどで採用されている資格です。

米国国防総省指令8570により国防総省の従業員は、セキュリティスキルの証明として、CEHを含む特定の資格を取得することが求められています。

セキュリティに関する資格がまた一つ増えたので、今後のキャリアに活かせていければと思います。
開発・ステージング環境限定の 組み込みMC・RFS 環境構築のすゝめ

## はじめに

様々な事情で

- 「開発・ステージング環境」用に `RFS` 構築したいんだけど、本番と同等のサーバを構築するのはしんどい。
- 本番環境と違って、必要な時だけ起動して使いたいので、出来るだけ運用のコストを下げたい。

といった場合、何か手軽に環境を構築する方法はないだろうか？

実際に活用するかどうかは別にして、意外と知られていない「`組み込みMC`を使った環境構築」について紹介したいと思います。

:::note
`組み込みMC` では本番環境として連続運用[^1]するのではなく、簡易的にスポットで使用することを推奨します。
:::

[^1]: 仕様として想定されていない利用法

## `組み込みMC` とは

[オンラインマニュアル](https://docshield.kofax.com/RPA/ja_JA/11.3.0_5cdzhlgb3t/help/rpa_help/help_main/mgmtconsole/c_intromgmtconsolestructure.html)には
> **組み込みモード**: インストール後に`デモやテストを目的` として Management Console をすばやく起動するための方法。組み込みモードについては、以下の組み込み Management Console の構成を参照してください。

と記述があります。

BizRobo! をインストールした端末のスタートメニューから `Start Management Console ...` を選択すると起動する Management Console です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3090491/b6639411-4863-b707-9d3a-f9b845ff5162.png)
以下のコマンドが実行され、`RoboServer` 内の組み込み機能として `MC` が起動します。
```powershell
"C:\xxx\bin\RoboServer.exe" -p 50000 `
                            -MC -pauseAfterStartupError `
                            -mcUrl http://xxx:xxx@localhost:50080
```
コンソールが表示され様々 `WARNING` が表示されるのですが、その一番最初に `組み込みMC` の利用に関する注意書きが表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3090491/df2a2302-57a3-2948-0f33-301d75aa23f9.png)
> WARNING: running the MC embedded is deprecated and not intended for production usage, refer to the Administrators Guide for information about how to install in a Tomcat instance or run the MC in a Docker container.
(警告: 組み込み MC の実行は非推奨であり、運用環境での使用を目的としていません。Tomcat インスタンスにインストールする方法、または Docker コンテナで MC を実行する方法については、管理者ガイドを参照してください。: by Google翻訳)


## `組み込みMC` で `RFS`

`RFS` については残念ながら「組み込み」版は存在しないので、以下の記事に従って Tomcat をZIPダウンロードして構築する必要があります。

https://qiita.com/ParakeetOnTheHead/items/7177e81d30275e9b7193


`組み込みMC` と `RFS` の関係は下図のような感じです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3090491/7e350426-4fbd-6ae8-815f-fb966a5bd8f5.png)
MCでは `名前` と `プロジェクト名` を本番環境と同一にし、`パス` に開発環境独自の値を設定することで、本番・開発で同じ設定をしておきながら、異なる値を参照する（本番と開発を切り離す）ことができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3090491/0f146824-76e7-84a2-9334-43f1eee490f2.png)
ゆえに「Excelの設定ファイルを用意して、ロボットが操作するファイルパスを開発と本番で使い分ける。」といった手順[^2]も不要になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3090491/e5feb838-082b-8227-fd05-f253021e3059.png)

## `組み込みMC` 利用上の注意

あくまで `デモやテスト` など簡易的な利用を目的とした `組み込みMC` は、過負荷を掛けたり使い方を間違うと破損することがあります。

例えば、BizRobo! インストール後に初めて `組み込みMC` を起動する時、環境によっては起動に5分以上時間がかかることもあります。[^3]

不慣れなユーザの場合、起動処理中に「あれ、固まったかな？」と勘違いしてコンソールを閉じてしまうことがあります。特にこのような場合、ファイル展開中のプログラム強制停止によりシステムが破損し、起動しなくなります。

Management Console が起動しなくなってしまった場合には `組み込みMC` が展開される アプリケーションデータフォルダ配下の `Temp` フォルダを削除して最初からプログラムの起動をやり直す必要があります。

```cmd:v11.3.0.2 の場合には以下のフォルダ
C:\Users\%username%\AppData\Local\Kofax RPA\11.3.0.2_358\Temp
```

https://knowledge.bizrobo.com/hc/ja/articles/360027602891

## まとめ

`組み込みMC` を本番環境として連続稼働させるのはやめましょう。

[^2]:ロボットを本番にアップロードする都度、またはロボットに変更が入り読み込むファイルパスに変更が生じるたびにロボットの動く本番サーバにログインしてファイルを置き換えるといったセキュリティ的に微妙な作業も不要になります。

[^3]:war形式で圧縮されたファイルを解凍する時間、プログラムを起動する時間、端末にインストールされているセキュリティソフトがオンラインスキャンを実施して待たされる時間、プログラム起動後に各種関連モジュールが疎通確認等を行う初期処理の時間など。
React Bootstrapが提供するのは制御コンポーネント？非制御コンポーネント？

# はじめに
最近、業務上で`React Hook Form` & `React Bootstrap`でのコーディングを行うようになりました。
その中で、タイトルの内容について開発チーム内で話題になったのですが、ネット上で調べる限り直接的な回答は得られず。
ドキュメントを読んだり実装を行い、自分なりに結論付けたので、記事として残しておこうと思います。

# 制御コンポーネント（Controlled component）とは
公式曰く**入力フォームの値をReactのstateによって管理する**コンポーネントのことを指します。
制御 = React側で値を制御
ということですね。
参考：https://ja.legacy.reactjs.org/docs/forms.html#controlled-components （古いドキュメントだが分かりやすい）

# 非制御コンポーネント(Uncontrolled component)とは
公式曰く**入力フォームの値をDOMよって管理する**コンポーネントのことを指します。
非制御 = React側では値を制御しない
といった認識で良いかと思います。
参考：https://ja.legacy.reactjs.org/docs/uncontrolled-components.html

# React Bootstrapが提供するコンポーネントは？
結論としては、React Bootstrapが提供するコンポーネントは**使い方によって制御コンポーネントにも非制御コンポーネントにもなりうる**と言うのが正しいのではないかと思います。
試しにReact Bootstrapの[Form.Control](https://react-bootstrap.netlify.app/docs/forms/form-control/)で実装してみました。

## 制御コンポーネント的な使い方
React Bootstrapのドキュメント読んだ印象として、基本的にはReact Bootstrapのコンポーネントは制御的に扱うことを想定していそうです。
以下の例では、stateで値を管理し、onChangeで値の変更を行っています。

```react
import { Form } from 'react-bootstrap';
...

export const TestComponent = () => {
    const [testValue, setTestValue] = useState('');
 
    const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
        setTestValue(event.target.value);
    };
 
    const handleSubmit = (event: FormEvent<HTMLFormElement>) => {
        event.preventDefault();
        // stateで管理していた値を取得
        console.log("testValue", testValue);
    };
 
    return (
        <>
            <Form onSubmit={handleSubmit}>
                <Form.Control
                    type="text"
                    value={testValue}
                    onChange={handleChange}
                    placeholder="値を入力してください"
                />
                <button type="submit">送信</button>
            </Form>
        </>
    );
};
```

## 非制御コンポーネント的な使い方
React Bootstrapのコンポーネントには、`ref`属性を指定することもできるようです。
`ref`属性を指定することで、DOMを参照して値を取得できるようになります。

```react
import { Form } from 'react-bootstrap';
...

export const TestComponent = () => {
    const inputRef = useRef<HTMLInputElement | null>(null);
 
    const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
        event.preventDefault();
        // DOMから値を取得
        const testValue = inputRef.current?.value;
        console.log("testValue", testValue);
    };
 
    return (
        <>
            <Form onSubmit={handleSubmit}>
                <Form.Control
                    type="text"
                    ref={inputRef}
                    placeholder="値を入力してください"
                />
                <button type="submit">送信</button>
            </Form>
        </>
    );
};
```

# 最後に
React Hook Formでは、扱うコンポーネントが制御コンポーネントか非制御コンポーネントかによってバリデーションの実装方法が変わったり、何かと制御/非制御を意識する場面が出てくるかと思います。
簡単な記事でしたが、皆さんの参考になれば幸いです。

以上