OmniAICreator/Qiita-1.07M huggingface dataset:
【DAY13】Laravel Eloquentについて

## ORMについて
ORM（オーアールマッパー）とは、Object-Relational Mappingの略で、RDB（ Relational Database）のデータ操作をオブジェクト指向プログラミング言語で扱えるようにするというものです。
例えば、usersテーブルにあるユーザーのデータを取得する場合、SQLでは以下のように記述すると思います。
```sql
select * from users;
```
これが、私が使用しているLaravel Eloquent（ORMの一種）では、以下のように記述することができます。
```php
$users = User::all();
```

このように、ORMを使用することで、DBをオブジェクト指向プログラミング言語で操作することができます。

## Laravel Eloquentのメソッド
ここからは私が使用しているORMであるLaravel Eloquentのメソッドについてまとめます。
前提として、Postモデルと対応するpostsテーブルを作成しているものとします。
### allメソッド
モデルテーブルの全レコードを結果として返します。
```php
$posts = Post::all();
```
## getメソッド
クエリに制約を付け加えて結果を取得するときに使用します。
```php
$posts = Post::where('user_id', 1)->get();
```
## findメソッド
主キーを指定して1レコードだけ取得します。
```php
$post = Post::find(1);
```

## firstメソッド
クエリ条件にマッチした最初の1レコードだけ取得します。
```php
$post = Post::where('user_id', 1)->first();
```
## findOrFailメソッド・firstOrFailメソッド
該当するレコードが見つからない場合に例外を投げます。例外がキャッチされないと自動的に404HTTPレスポンスがユーザに送り返されます。
```php
$post = Post::findOrFail(1);

$post = Post::where('user_id', 1)->firstOrFail();
```
## saveメソッド
モデルから新しいレコードを作成するときに使用します。
```php
class PostController extends Controller
{
    public function store(Request $request)
    {
        $post = new Post;

        $post->name = $request->name;

        $post->save();
    }
}
```
## deleteメソッド・destroyメソッド
レコードを削除するときに使用します。モデルの主キーが分かっている場合は、destroyメソッドでモデルを取得せずに削除できます。
```php
$post = Post::where('user_id', 1)->first();
$post->delete();

Post::destroy(1);
```
React map関数で展開しているのに表示されない（エラーもでない）

propsで受け取ったデータをmap関数で出力するだけの簡単な処理でしたが、迷走していました。
とりあえず解決はできたのですが、なぜかといわれると説明はまだできないです

結果として、map関数部分をreturnで囲めばOKでした
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3892241/d3381431-4323-d27f-60ff-1aa31fdafd57.png)

console.log(表示されています +～
と書いてコンソールでは出力されるのに何で出ないんや！となっていましたがとりあえずどうにかなったので記録しておきます
docker, docker-compose を使って rails 開発を行うときのコマンド

## docker コマンド一覧
(1) 一覧
```
$ docker ps -a
$ docker ps
$ docker images
```
(2) ログ
```
$ docker logs <container_id>
```
(3) 構築
```
$ docker-compose stop
$ docker-compose down
$ docker-compose build
$ docker-compose up
```
(4) 破壊
```
$ docker container prune
$ docker images prune -a
$ docker images prune
$ docker system prune -a
$ docker volume prune
```

## rails コマンド
(1) ファイル生成
```
$ docker-compose run web bundle exec rails generate model Hello
$ docker-compose run web bundle exec rails generate controller Welcome index show
```
※ ファイルが生成されたという成功ログが出ているのにも関わらず、ファイルが実際に生成されない問題が起きたら、Dockerfile と docker-compse.yml のマウントするディレクトリ名が一致していないミスを起こしているかもしれない。
私は[この記事](https://teratail.com/questions/263843) でこれが原因であることに気が付いた。

(2) DB
```
$ docker-compose run web rails db:create
$ docker-compose run web rails db:migrate
$ docker-compose run web rails db:reset
```
(3) コンソール
```
$ docker-compose run web rails dbconsole
$ docker-compose run web rails console
```
※ コンソールが起動しているときは、`docker-compose down` が機能しないので、コンソール上で exit を使って抜けてください。

(4) `A server is already running. Check /my_app/tmp/pids/server.pid.` エラー
`docker-compose down` ではなくて、CTRL+C でコンテナを停止すると、間違いなく 3000 ポートで起動してないのにも関わらず、再び 3000 ポートを使った起動ができない問題が起きる。
`/my_app/tmp/pids/server.pid` というキャッシュファイルが残っているのが原因みたい。
```
$ docker-compose up
...
A server is already running. Check /my_app/tmp/pids/server.pid.
...
$ lsof -i :3000
$ docker-compose run web rm /my_app/tmp/pids/server.pid
$ docker-compose up
```
[Python] FirebaseのRegion設定 (Cloud Functions)

## 内容

Firebase Cloud FunctionsをPythonで実装する際に設定するデコレータの**Region**の解説を行います。
Firebaseが公開している公式ドキュメントと、実際のソースコードを見ながら解説をしていきます。


## 目次

- [1. デコレータとは](#1-デコレータとは)
- [2. Regionの設定](#2-regionの設定)
- [3. 実装](#3-実装)
- [4. まとめ](#4-まとめ)

## 1. デコレータとは
デコレータとは**関数やクラスの前後に特定の処理を追加出来る機能**です。
Pythonでは、関数やクラスの定義する前に`@`をつけることで設定出来ます。

```Python sample.py
@https_fn.on_request()
def fetch_data(req: https_fn.Request) -> https_fn.Response:
    #省略
```

今回のFirebaseのデコレータは`@https_fn.on_request()`になります。


## 2. Regionの設定

### FirebaseのRegionとは?
Regionとは、デプロイ先の地域になります。
利用するユーザーに近いRegionを設定するのが良いと思います。

### Cloud Functionsのリージョン一覧

デプロイする関数をどのリージョンで実行するかを指定します。
Firebaseのソースコード内の`SupportedRegion`クラスに設定されるリージョンが定義されています。

```firebase_functions.options.py
class SupportedRegion(str, _enum.Enum):
    """
    All regions supported by Cloud Functions (2nd gen).
    """

    ASIA_NORTHEAST1 = "asia-northeast1"
    ASIA_EAST1 = "asia-east1"
    ASIA_NORTHEAST2 = "asia-northeast2"
    EUROPE_NORTH1 = "europe-north1"
    EUROPE_WEST1 = "europe-west1"
    EUROPE_WEST4 = "europe-west4"
    US_CENTRAL1 = "us-central1"
    US_EAST1 = "us-east1"
    US_EAST4 = "us-east4"
    US_WEST1 = "us-west1"
    ASIA_EAST2 = "asia-east2"
    ASIA_NORTHEAST3 = "asia-northeast3"
    ASIA_SOUTHEAST1 = "asia-southeast1"
    ASIA_SOUTHEAST2 = "asia-southeast2"
    ASIA_SOUTH1 = "asia-south1"
    AUSTRALIA_SOUTHEAST1 = "australia-southeast1"
    EUROPE_CENTRAL2 = "europe-central2"
    EUROPE_WEST2 = "europe-west2"
    EUROPE_WEST3 = "europe-west3"
    EUROPE_WEST6 = "europe-west6"
    NORTHAMERICA_NORTHEAST1 = "northamerica-northeast1"
    SOUTHAMERICA_EAST1 = "southamerica-east1"
    US_WEST2 = "us-west2"
    US_WEST3 = "us-west3"
    US_WEST4 = "us-west4"

```
デフォルトの値は`us-central1`です。
なお、日本のリージョンの値は以下なので参考にしてください。
東京: `asia-northeast1`
大阪: `asia-northeast2`


### Regionごとの料金

Firebaseではリージョンによって料金設定が異なるようです。
![スクリーンショット 2024-09-22 20.33.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3767231/bb36dbe7-e836-9ad0-f66f-8255bb7c922e.png)


全てのリージョンが同一の価格設定ではなく、二酸化炭素の排出量を理由に2つの料金形態に分かれているようです。

[公式ドキュメント](https://cloud.google.com/functions/pricing?hl=ja)によると、Tier1の方がTier2よりも安いようです。
おそらく`asia-northeast1(東京)`, `asia-northeast2(大阪)`を選択する方が大多数だと思うので、料金については特に気にする必要はいと思います。

### Cloud Functionsのロケーション(公式ドキュメント)

https://firebase.google.com/docs/functions/locations?hl=ja


## 3. 実装

今回はデプロイ先を`asia-northeast1(東京)`に設定しました。

```Python sample.py
from firebase_functions import https_fn


@https_fn.on_request(region="asia-northeast1")
def fetch_data(req: https_fn.Request) -> https_fn.Response:
    #省略
```

これでリージョンを設定することが出来ます。


## 4. まとめ
今回はFirebase Cloud FunctionsをPythonでデプロイする際に必要なデコレータの設定の中の1つである、**Region**についての解説を行いました。今回はPythonでの書き方を解説しましたが、TypeScriptについても同様の設定があります。こちらについては、過去に執筆したので是非参考にしてみてください。


#### TypeScriptでのロケーション設定

https://qiita.com/kikudesuyo/items/3d9c73cbf0dad0f75eef
Markdown メモ

# はじめに

Markdownで忘れがちの記述や使ったことないけど面白そうなものをまとめました。

# 強調

**テキスト**

```
**テキスト**
```

# テーブル

| 1 | 2 | 3 |
|:-:|:-:|:-:|
|   |   |   |
|   |   |   |
|   |   |   |
|   |   |   |

下記実行後に表示される「テーブル挿入」を押下
```
3x4
```
x(エックス)。掛けるではない。


|aaaa|bbbb|cccc|
|:-|:-:|-:|
|a|b|c|
|a|b|c|
|a|b|c|

```
|aaaa|bbbb|cccc|
|:-|:-:|-:|
|a|b|c|
|a|b|c|
|a|b|c|
```

# Diff

``` diff_java
- System.out.println("Hello");
+ System.out.println("World");
```

~~~ md
``` diff_java
- System.out.println("Hello");
+ System.out.println("World");
```
~~~

# コードブロック

チルダで記載したmdはダブルクオーテで囲むことでコードブロック内に下記のように表示可能。逆でも可能
~~~ md
``` md

```
~~~

``` md
~~~ md

~~~
```

# コードスパン

`test`
`#000000`
`rgb(0,0,0)`

```
`test`
`#000000`
`rgb(0,0,0)`
```

# note

:::note info
a
b
:::

:::note warn
a
b
:::

:::note alert
a
b
:::

```
:::note info
a
b
:::

:::note warn
a
b
:::

:::note alert
a
b
:::
```

# 参考

https://qiita.com/Qiita/items/c686397e4a0f4f11683d
CSM(認定スクラムマスター)合格までの流れまとめ

# はじめに
今月初めにCSM講習試験を受けていたのですが、ありがたいことに無事合格しました。
今回はこの試験を受けた背景と、試験合格までの簡単な流れを残しておこうと思います。

# なぜCSMを受験したのか
今回試験を受けた理由としては主に三つほどあります。
- 社内のプロダクトにスクラムを適用させ、チームの今を知りたいから
- 後続のキャリア選択の参考にしてほしいから
- スクラムをより深く学びたかった

以上になります。
弊社の都合上一つ目が最も大きな理由で、あとは後続がマネジメントというキャリアを選択しやすくするために、まずは自分がということになります。

# CSMの講習概要
まず、CSMを取得する為には、指定講師による講習を受ける必要があります。
この講習はお金を積めば合格できるものではなく、しっかりと合格基準があり、ここを超えて初めて CSMの試験を受けられる状態となります。

今回私は[Odd-e](https://www.odd-e.jp/ja/)の[オンライン講習](https://odd-e.tech-kai.com/tech/user/courses)（講師：江端氏）を受講しました。

日程は月曜日から金曜日の5日間、13:00~19:00でした。
また、基本90分ほどで一回10分ほどの休憩を取る形で進めていました。
ただし、上記時間は基本参加者同士で話し合いながら決める形となるので、みんな疲れてきたら少し長めに取ったり、逆に短くしたりと調整しながら進めていく感じです。

講習時に使用していたツールはmeetとmiroのみになります。

# 講習受講前にやったこと
講習が開始する2週間ほど前に当日の詳細がメールで届きました。
その際に予習資料として、講師よりドキュメントを共有いただき、理解したい事や確認したい内容を整理してから参加するように指示がありました。

私はこちらを熟読すると共に書籍を読んだり、スクラムの開発チームとして動いているメンバーに詳細を聞いたりなど、できる限り当日に不足がないようインプットしていきました。

# 講習内容
初日は簡単に講習の内容に関しての説明と座学、あとはmeetのグループワークがメインでした。
2日目以降はほぼグループワークがメインになります、8割ほどグループワークで残りは講師への質問にご回答いただいたり、今我々が進めていることに対しての助言をいただくなどの時間でした。
グループワークはかなり実践的な内容で、お題に対してチーム内でひたすらディスカッションを繰り返すような形でスクラムの基礎を身につけていくようなスタイルになります。

# 講習終了から受験まで
講習中は講師により参加者の能力を評価されており、ここを越えた方のみ講習終了後にトレーナーより認定スクラムマスター試験受験の為のメールが届きます。
上記メールは大体講習終了から1週間〜2週間ほどで届く形になります。

メールが到着してから90日以内に試験を受ける必要があり、2回まで無料で受けることが可能です。
すぐに試験が受けられるように、メールが到着したらアカウントの登録だけはすぐに済ませてしまうと良いと思います。
ちなみにこの試験はそんなに難しいものではないと講師も述べておりますし、私も同じ認識ですのでそこまで身構える必要はないと思います。

# 試験について
認定スクラムマスター試験はオンラインで受ける形になります。
60分50問の選択形式で、ボーダーラインは74%とのことです。

上記試験は真面目に講習を受講すれば問題なく合格できると感じました、講習前の資料などをよく読んでおけば十分回答できる内容だと思います。
ちなみに私は1問だけ間違えてしまいましたが、問題なくパスできました。

# 講習を受講しての感想
とにかく学びの多い素晴らしい講習内容でした。
知識としてスクラムとは何かを詰め込んで臨みましたが、そんなことでは決してうまくいかないということを学び、初日で私の浅はかな考えは崩れ去っていきました・・・
その上で改めてスクラムマスターとして最も重要なマインドとは何か、というのを5日間チーム内でディスカッションしながらずっと考えを巡らせていました。

最終的に実践の場では決まったベストプラクティスなどは存在せず、スクラムはあくまでフレームワークであり、組織によってアプローチは違っていいということを学びました。
私はこの柔軟性を持ってスクラムマスターとして活動していくことを忘れないようにしたいと思います。

# さいごに
本当に濃密な五日間でした。
理論武装するエンジニアとは全く目線が異なっており、スクラムマスターはその性質上、経営者に近い目線なのかなというふうにも感じました。
CSM以外にも講習はあるので、またタイミングみて受けたいと思います。
Github Actions キャッシュされない

## 問題
キャッシュの設定もして、キーもきちんと生成されているのに、毎回キャッシュが見つからない。というか生成すらされていない。

## 解決
前のプロセスが失敗しているとキャッシュ生成プロセスが飛ばされる！
Chainsを使ってChat GPT APIの仕組みをさわってみる

### なぜやろうと思ったか
Chainsの理解をしてみようと思った。

### そもそもChainsとは？
ModelとPromptを連結させてくれる。

### やってみたこと

Promptでテンプレートを決めてその内容についてModelで回答を出してもらうことをやってみた。
本当は画面を作って、入力したものをPromptのテンプレートに渡して、回答を画面に出すまでやりたかったが
本筋の目標としては、langChainの内部処理の理解なので今回はやめた。
ひとまず数字の計算をしてみた。

```python
import langchain
from langchain.chains import LLMChain
from langchain.chat_models import ChatOpenAI
from langchain.prompts import PromptTemplate

langchain.verbose = True

# Model を用意
chat = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0)

# Prompt を用意
template = """
次の計算をしてください。

計算結果: {answer}
"""
prompt = PromptTemplate(
    input_variables=["answer"],
    template=template,
)

# Chain を作成してModelとPromptを連結
chain = LLMChain(llm=chat, prompt=prompt)

# 実行
result = chain.run("5*6")
print(result)

```

結果

とても単純だが、実際にコンソール画面に結果が出るのは気持ちよかった！
今後はもっと複雑なことをやっていく予定。

```
次の計算をしてください。

計算結果: 5*6


> Finished chain.
計算結果は30です。
```
forkしたリポジトリでPull Request作成ができない

## 初めに

疑問に思ったことや上手くいかなかったことのアウトプットをしています。
自分なりの理解でアウトプットしていきます。初学者なので誤りもあると思います。
その際はご指摘いただけると幸いです。

## 問題
forkした自分のリポジトリ
↓
Pull request
↓
New pull request
↓
base repositoryをクリックして自分のアカウントをクリック　←ここ！

自分のアカウントが見つけられませんでした。

## 解決
URLを見てみます。よく見ると自分のリポジトリじゃなくなってます。
フォーク元の名前を消して自分のユーザー名をいれましょう。
baseはそのまま、compareの方にpullしたブランチを選択しましょう。

これで大丈夫です！
Godotで足音 ランダム再生とトンネルで響く

Godotで音を鳴らす仕組み部分のみの備忘録

こんな感じの足音を実現する

https://x.com/tatmos/status/1837516010720813182

使用したaddon
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96808/580adb97-b6bf-954b-c733-63fe6fe8c681.png)


# 足音を鳴らす条件

- 床に足がついている時に移動量がある
- 前回再生より一定時間すぎていたら鳴らす

# リアル目の足音
- いくつかの素材をランダムに鳴らす
- トンネルとかで響く

# Godotでランダム再生

- audioフォルダにランダムで鳴らすwavを入れておく
- footstep_soundsのリストにpreloadしておく
- 再生リクエストのたびにrandi()でstreamを差し替える

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96808/28475b0c-d64e-a00c-ac8b-344426f187e3.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96808/3e5d67c1-f455-102d-6a05-8659ca375901.png)

``` .py
# 足音のオーディオファイルリスト
var footstep_sounds = [
	preload("res://audio/footstep_asphalt_1.wav"),
	preload("res://audio/footstep_asphalt_2.wav")
]

# ランダム再生
func play_walk():
	var random_index = randi() % footstep_sounds.size()
	$AudioStreamPlayer3D.stream = footstep_sounds[random_index]
	$AudioStreamPlayer3D.play()

```

# 一定時間ごとに鳴らす
- 鳴らす間隔を決める
- 最後になった時間を記録しておく

``` .py
var step_interval = 0.3  # 足音の間隔
var time_since_last_step = 0.0   # 最後に足音を再生してからの経過時間

func move_player(delta): 
    # 動きがあって床時
	if direction.length() > 0 and is_on_floor():
		time_since_last_step += delta
		if time_since_last_step >= step_interval:
			play_walk()
			time_since_last_step = 0.0
 ```

# 洞窟で響かせる

- Area3dでコライダー判定　子ノードにシリンダー状の当たり判定設置
- ミキサーのバスとしてエフェクトの残響違いを用意
- Areaの入り・出で接続するバスのルーティングを変更

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96808/95010e11-70f4-5366-b221-39a1dafb58f7.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96808/87fffa4a-e690-f61e-d454-c6a5fb2e9860.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96808/d3df9fdf-1748-4b92-3529-5d841a632beb.png)

``` .py
func _ready():
    # Areaの入り・出のシグナルを受ける
	$"../Floor3/Tunnel/CaveArea".body_entered.connect(_on_cave_area_area_entered)
	$"../Floor3/Tunnel/CaveArea".body_exited.connect(_on_cave_area_area_exited)


func _on_cave_area_area_entered(area: Object) -> void:
	print("areaEnter")
	$AudioStreamPlayer3D.set_bus("Cave")

func _on_cave_area_area_exited(area: Object) -> void:
	print("areaExit")
	$AudioStreamPlayer3D.set_bus("Outdoor")
	
```
[DB] 3 Schema Architecture in DBMS

# 一覧
|名称|詳細<br>例|
|:--|:--|
|概念スキーマ|データベース化対象の業務とデータの内容を論理的なデータモデル、として表現したもの|
||- リレーショナルモデル: E-R図の作成、表定義（制約t）、表の正規化<br>- ネットワークモデル: |
|外部スキーマ|概念スキーマで定義された、データモデル上に利用者ごとの目的に応じた見方して、表現したもの|
||- リレーショナルモデル: ビュー<br>- ネットワークモデル: サブスキーマ|
|内部スキーマ|概念スキーマで定義された、データモデル上に記憶装置上にどのような形式で格納するかを、表現したもの<br>|
||- リレーショナルモデル: ファイル設定<br>- ネットワークモデル: インデックス設定|


# Image
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2955010/a7360c80-f604-6135-8af1-e6764baf4f53.png)

# 参考リンク
- [データベースのスキーマとは何か？3層スキーマと関連用語も解説](https://meetsmore.com/product-services/databases/media/99441)
- [令和4春27](https://www.ap-siken.com/kakomon/04_haru/q27.html)
【Ruby】ユーザの入力を受け付ける方法

ユーザーからの入力を受け付けるには`gets`メソッドを使用します。

## 基本的な使い方

`gets`メソッドは、標準入力から1行のデータを読み取ります。
以下は、`gets`メソッドの基本的な使用例です。

```ruby:example.rb
puts "名前を入力してください"

name = gets

puts "こんにちは" + name + "さん"
```

このコードを実行すると、ユーザーに名前の入力を促し、入力された文字列を使用してメッセージを出力します。

```terminal
$ ruby example.rb
名前を入力してください
sample
こんにちはsample
さん
```

## 改行文字の取り扱い

`gets`メソッドは、入力されたデータの末尾に改行文字を含みます。
この改行文字は`chomp`メソッドを使用することで削除できます。

```ruby
puts "名前を入力してください"

name = gets.chomp

puts "こんにちは" + name + "さん"
```

上記のようにすることで末尾の改行文字が削除されます。

```terminal
$ ruby example.rb
名前を入力してください
sample
こんにちはsampleさん
```
JupyterNotebookのフォントを変更する方法

# JupyterNotebookのインストール
venv環境でインストールすることを前提とします。
方法は以下の通りです。

```bash:venvで仮想環境を作成した上でJupyterNotebookをインストール・起動
pythn -m venv jupyter
cd jupyter
source bin/activate
pip install jupyter
jupyter notebook
```

# デフォルトのフォント
デフォルトのフォントはこのような感じです。

![スクリーンショット 2024-09-22 19.18.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/119732/58c618d4-4a71-97a5-0f1d-40f1ca2f5c82.png)

# フォントをMonacoに変更

手順は以下の通りです。

1. JupyterNotebookを開きます。
2. ツールバーで「Settings」をクリックします。
3. 「Settings Editor」を選択します。
4. 「Notebook」セクションを探します。
5. 「Font Family」の設定を見つけます。
6. 希望するフォントの名前を入力します。

MacのMonacoが好きなのでMonacoに変更しました。

![スクリーンショット 2024-09-22 19.23.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/119732/ce90bb11-309c-d290-15ce-ddcaf69f22d4.png)

ネットにあまり情報が落ちていなかったので備忘録です。
みなさんもお気に入りのフォントでぜひ快適なJupyterライフを！
サーバー移行時のDNSの重要性とNAT導入の検討

サーバー移行を行う際、IPアドレスの変更やDNS設定が重要なポイントとなります。
本記事では、移行時にDNSを利用した名前解決がどのように影響するか、またNAT導入のメリットについて解説します。

## 変更時に影響を受ける対象

移行対象のサーバーに直接影響を受けるのは、そのサーバーに通信を行っているクライアントや他のサーバーです。
具体的には、以下の2つの通信パターンが考えられます。

### 1. PCクライアント等の不特定多数の通信元
ほとんどの場合、DNSを使って名前解決を行っているため、影響は小さいと考えられます。

### 2. サーバー間の通信や連携
サーバー間の通信では、DNSを利用していれば問題は少ないです。
ただし、DNSを利用していない場合、手動で設定を調査する必要があります。

#### サーバー単位でのDNS利用確認
DNSログとサーバー一覧を参照することで、DNSを利用しているかどうか確認できます。
一定期間ログに出力がない場合、そのサーバーはDNSを利用していない可能性が高いです。

#### 個々の通信がDNSを利用しているかの確認
通信ログからの判断は難しいです。
アプリケーションやソフトウェアの設定を調べ、IPアドレスが直接記述されていないかを確認します。

### 私見
DNSを利用した名前解決が行われているかどうかがポイントとなります。
しかし、スマートで確実な確認方法は存在しないのが現状です。

## 維持した場合の課題

サーバー移行において、IPアドレスの変更を避けるためには、同じネットワークセグメント内での移行が必要です。
ただし、新旧のサーバーが同時稼働しないことが前提条件となります。

### 私見
この選択肢は一見シンプルに見えますが、前提条件が多く事故のリスクが高いです。
派生的な解決策としてNATを利用してIPアドレスを変換することも考えられます。

## 結論：どうすれば良いか

いろいろな要素を考慮および様々な前提を無視した結果、以下の手順で進めるのが最善とこの記事ではお伝えします。

1. **IPアドレスの変更を前提として移行を進める**
2. **各サーバーの名前解決の利用状況を調査する**
   - 調査は基本的に人力で行う必要があります。
3. **NATの導入を検討する**
   - 名前解決の利用有無や調査の難易度
   - 通信が途絶えた場合の影響
4. **新サーバーへの切り替え手順**
   - NATに新旧IPアドレスのマッピングを登録する
   - DNSサーバーのホスト名とIPアドレスのマッピングを変更する
5. **NATのログを確認し、旧IPアドレスへの通信があるかどうかを確認する**
6. **旧IPアドレスへの通信があれば、通信元の接続設定を変更する**
7. **最終的にNATへの通信がなくなったら、NATを廃止する**

---

サーバー移行時のIPアドレス変更やDNS利用の確認は、手間がかかる作業ですが、適切な手順を踏むことでスムーズに移行が完了します。問題が起きないよう、DNSやNATの設定を事前にしっかり確認して進めてください。

---
Qiita Hackathon 2024に参加してみた

## はじめに
人生初ハッカソンに参加して、先ほど無事完走したので、身体がホクホクしているうちに振り返っておこうと思います。

### Qiita Hackathon 2024.9.21-9.22
https://qiita.com/official-campaigns/hackathon/2024

## ハッカソン一緒に出ませんか？
ハッカソン開催の1ヶ月ほど前に、前職の同僚から、「ハッカソン一緒に出ませんか？」と声を掛けていただいたのが嬉しくて、ハッカソンが何者かも分からないまま、その場で「出ますと！」と言って参加することになりました。

## 参加メンバー
フルスタックの方がチームのリーダー的なポジションの方で、メンバー5人の担当領域と関係はざっとこんな感じでした。

1. フルスタック（リーダー）
2. デザイナー（リーダーの奥さん）
3. フロントエンド（リーダーの元同僚）
4. バックエンド（私の元同僚）
5. バックエンド、インフラ（私）

自分は主にバックエンド＋インフラというポジションでやらせて貰いました。

## 準備ってどうした？
リポジトリは当日ハッカソン開始してから作成するなど、開発環境の準備などゼロの状態でしたが、メンバー同士は事前にオンラインで集まって自己紹介するなどして、お互いのこと軽く知る時間は用意して頂きました。
この段階でチームメンバー全員、いい人でコミュニケーション取りやすい方しかいなかったので、プロダクト作りは上手くやっていけそうという謎の自身はありましたw

## ハッカソン
### 制限時間
今回参加したQiitaHackathonの予選は2日間の開催で、1日目 10:00 から、2日目13:20発表スライド提出までが時間制限になります。

### テーマ
QiitaHackathonにはテーマがありまして、そのテーマにそったプロダクトを開発していきます。テーマは当日発表されるので、そこからチームで話し合ってプロダクトを作っていく流れで、今回のQiitaHackathonのテーマは **「オープン」** でした。

## ハッカソン1日目
### 解決する課題
**「オープン」** というテーマに対して、私たちは「引きこもりの悩み」という課題を設定しました。この課題には、「コミュニケーションが苦手、難しいこと」が原因のひとつと仮定して、AIを使ってコミュニケーション能力を向上させるという方向性でプロダクトを開発することにしました。しかもAIは個性的なキャラを設定をして、ゲーム感覚でサービスを使えることを目指しました。

**まとめると**
**癖つよキャラのAIと会話を通じて、コミュニケーションを爆上げするサービス**

### 技術スタック
プロダクトが決まったところで、詳細は省きますが、技術選定というところで、主にこのような技術スタックで開発しました。
- FE：Next.js, React
- BE：Nest.js, TypeORM
- DB：PostgreSQL, sqlite
- 文章生成AI：GPT, LangChain.js
- 音声合成：VoiceVox

### その他もろもろ
実装する画面、必要なAPI、エンティティなども含めて、ここまでスタートから2時間程度で終えて、そこからリポジトリを作成して実装に入りました。

### 苦労したこと
`Nest.js`, `TypeORM`を扱ったことがなかったので、バックエンドAPIを作る以前に環境構築で苦労しました。ハッカソンはとにかくスピード勝負というところで、ちょっとした詰まりポイントでも、解決まで時間が掛かってしまったので、クセが分かっている得意な言語や技術で構成できるなら、それに越したことはないかなと思いましたが、一方で扱ったことのない技術に触れること自体、学びが多いので、あえて使ったことのない技術を選んでみるのも面白かったと思いました。
とはいえ、APIの開発工数が足りそうになかったので、途中から得意な技術を導入するなど、バックエンドの開発方針を柔軟に変えて実装しました。

## ハッカソン2日目
フロントとバックエンドの繋ぎ込みがメインで、とにかく動くものを作り終えるということにチーム一丸で開発を進めました。ここで、特にフルスタックのリーダーの腕力により無事実装終えました。
ハッカソンは実装だけではなく、サービス紹介のスライドの用意もしなくてはならないので、ラストスパートは実装の仕上げとスライド作成の二手に分かれて1時間弱でスライドを仕上げました。

### 発表スライド
https://speakerdeck.com/aki366/aikimeratohui-hua-toreninguapuri

### デモ動画
https://www.youtube.com/watch?v=UMw7Bpwo26Q

## 学び
### 0→1
メンバー同士まったく違う会社で、一緒に仕事をしたことがない人とゼロからプロダクトを作るとどうなるんだろう。と内心ドキドキしていましたが、限られた時間の中、共通の目標、熱量、技術が揃えば、短時間の間にプロダクトを作ることが出来てしまうという体験出来ました。この体験を通じて、0→1のスタートップの雰囲気を味わえたことが個人的に良い学びでした。

### 開発生産性 × コミュニケーション
今回全員リモートで開発を行いましたが、自宅の開発環境が各々整っていることと、Meetやハドルで常にコミュニケーションが取れる状態にしていたことで、生産性は非常に高かったと思います。
特に耳に入ってくる情報を、ツールを使ってコントロール出来るところはが良かったと感じました。例えば声をかけたい時はマイクオンですぐ話せて、集中したい時はMeetから抜けるだけで話が聞こえてこないといったメリハリがツールを使うことで簡単にコントロールできる点が自分は良かったと思いました。
オフラインで集まると、自分が意識を向けなくていい会話も聞こえてきて集中力を維持することに無意識に疲れるので、今回のような短期戦は全員リモートで良かったと思いました。

### ペアプロ
バックエンドの実装は初めて扱う技術ということもあり、ほぼペアプロで進めましたが、速を出しながら、バグも抑えるという観点で、悪くないスタイルだったと思います。特にエラーやバグで詰まった時もそのタイミングでリアルタイムにエラーの状況を把握できている点（今この作業をしていて、このコマンドを実行して、ここのコードで...）といったコミュニケーションが不要なので、その点は良かったと感じます。

## 最後に
初めてハッカソンに参加させていただきましたが、0→1の開発を雰囲気を体験出来たのは、非常に良かったと思います。
また、全てにおいて有利な訳ではないですが、コミュニケーション面でもオンラインであることの強みを実感することが出来たことも良かったですし、チームメンバーの相性・お互いをリスペクトする姿勢なんかも短期で結果を出すには非常に重要と感じたので、この知見を今後に活かしていければなと思います。

どうですか
**みなさんもハッカソン出てみませんか？**

最後までお読みいただき、ありがとうございました。
【Ubuntu22.04】sudo apt-get updateしたら404 Not Foundエラーが出たときの対処法

## 概要
WSLでsudo apt-get updateしようとしたら以下のようなエラーが出たので、対処法をメモ。
```terminal:
Err:7 https://apt.llvm.org/jammy llvm-toolchain-jammy-10 Release
  404  Not Found [IP: 999.999.999.999 999]
--------------------
E: The repository 'http://apt.llvm.org/jammy llvm-toolchain-jammy-10 Release' does not have a Release file.
```
## 環境
- OS: Windows 11 64bit
- WSL: 2.2.4.0 (Ubuntu 22.04)

## エラー内容
```terminal:
$ sudo apt-get upgrade
Get:1 file:/var/cuda-repo-wsl-ubuntu-12-2-local  InRelease [1572 B]
Get:2 file:/var/cudnn-local-repo-ubuntu2204-9.4.0  InRelease [1572 B]
-----------------------------------------
Hit:8 http://jp.archive.ubuntu.com/ubuntu jammy InRelease
Err:7 https://apt.llvm.org/jammy llvm-toolchain-jammy-10 Release
  404  Not Found [IP: 999.999.999 999]
  ----------------------------------------
E: The repository 'http://apt.llvm.org/jammy llvm-toolchain-jammy-10 Release' does not have a Release file.
```
このようなエラーが出て、sudo apt-get updateできない。

## 対処方法
エラーメッセージから原因のリポジトリ設定ファイルを探す。
今回は"llvm-toolchain-jammy-10"というのが原因みたいです。
調べてみると、、、
```terminal:
$ grep -r "llvm" /etc/apt/sources.list.d/
/etc/apt/sources.list.d/archive_uri-http_apt_llvm_org_jammy_-jammy.list: deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-10 main
/etc/apt/sources.list.d/archive_uri-http_apt_llvm_org_jammy_-jammy.list:# deb-src http://apt.llvm.org/jammy/ llvm-toolchain-jammy-10 main
```
ファイル名が分かったので設定ファイルをコメントアウトしておきます。
```terminal:
$ sudo nano /etc/apt/sources.list.d/archive_uri-http_apt_llvm_org_jammy_-jammy.list
# deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-10 main
```
これでupdateできるようになるはず。
```terminal:
$ sudo apt-get update
-----------------
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
25 packages can be upgraded. Run 'apt list --upgradable' to see them.
```
できました！！
## 原因
エラー内容から(http://apt.llvm.org/jammy) に
"**llvm-toolchain-jammy-10 Release**"が無いとのことなので調べてみると...

../
llvm-toolchain-jammy/                              22-Sep-2024 07:37                   -
llvm-toolchain-jammy-13/                           17-Feb-2022 13:21                   -
llvm-toolchain-jammy-14/                           31-Jan-2023 15:27                   -
llvm-toolchain-jammy-15/                           20-Oct-2023 01:27                   -
llvm-toolchain-jammy-16/                           12-Nov-2023 15:11                   -
llvm-toolchain-jammy-17/                           09-Dec-2023 15:54                   -
llvm-toolchain-jammy-18/                           01-Aug-2024 04:19                   -
llvm-toolchain-jammy-19/                           20-Sep-2024 13:39  

こんな感じで、"llvm-toolchain-jammy-10"は無かったです。
代わりに"llvm-toolchain-jammy-18"をインストールしておきました。
[初級] C++: COMスマートポインタの基本

COM スマートポインタの基本
C++、COM、_com_ptr_t

:::note info
本記事の前提条件は以下の通りです。
* 初心者向け
* とは言っても、何らかのプログラムはそれなりに書けるけど、C とか C++ はちょっと、という人向け
* ざっくり概要しか説明しないので細かいことは気にしないでいただきたい
* Visual Studio 2013 くらい～
* Windowsプログラム (CUI, GUI)
* コードの検証
    * 開発環境: Visual Studio 2022, x64, Release ビルド
    * 実行環境: Windows 10
:::

:::note info
* 本記事は上から順番に読む前提となっている
:::

:::note info
* 「Visual Studio 2013 くらい～」と書いてあるが、本記事の内容だと現在過去未来のどのバージョンでもほぼ同じ
:::

:::note info
前提知識として必要と思われる情報
* [[初級] COMの基本的な仕組み（超ざっくり） - 拙著](https://qiita.com/yasu62347/items/ff9a48c9f97293dff360)
* [[初級] C++: COMコンポーネントの使い方（ビルド関係） - 拙著](https://qiita.com/yasu62347/items/af56de70a55d9357dfa2)
* [[初級] C++: COMコンポーネントの使い方（COMオブジェクト生成） - 拙著](https://qiita.com/yasu62347/items/72a9c3889b8431df0bee)
* [[初級] C++: COMコンポーネントの使い方（COMインターフェース） - 拙著](https://qiita.com/yasu62347/items/b3dd1df3dde24b316cce)
:::

# COM スマートポインタを使う

_com_ptr_tを使うわけだが、そのまま使おうとすると、

```cpp:c++
#include <comdef.h>
_com_ptr_t<_com_IIID<IPlayback, &__uuidof(IPlayback)>> pPlayback;
pPlayback.CreateInstance(...);
```

と、ややこしいので、素直に `.tlh` にある `_COM_SMARTPTR_TYPEDEF` マクロを利用する。
`_COM_SMARTPTR_TYPEDEF` マクロは、インターフェースが `IPlayback` であれば、`IPlaybackPtr` というスマートポインタを定義してくれるので、以下のように書けるようになる。

```cpp:c++
#include <comdef.h>
IPlaybackPtr pPlayback;
pPlayback.CreateInstance(CLSID_MusicPlayer, NULL, CLSCTX_INPROC_SERVER);
pPlayback->Play();
pPlayback->Stop();
```

# メリット

## RAII によるリソース管理

`Release` の呼び出しは COM スマートポインタが RAII に基づいて処理してくれるので不要になる。

```cpp:c++
#include <comdef.h>
IPlaybackPtr pPlayback;
pPlayback.CreateInstance(CLSID_MusicPlayer, NULL, CLSCTX_INPROC_SERVER);
pPlayback->Play();
pPlayback->Stop();
// pPlaybackがスコープを抜ける時に自動的にReleaseされる
//pPlayback->Release();
```

:::note info
不要になるというか、明示的に呼び出すと参照カウントが異常になるので、注意が必要。
:::

## キャストによる COM インターフェース取得

COM スマートポインタのキャスト（~~コピー~~変換コンストラクタ、~~コピー~~（変換）代入演算子、型変換演算子）はすべて `QueryInterface` の呼び出しによる COM インターフェースの取得処理となる。

:::note info
すべて、と言っても、あくまでも、COM スマートポインタから 別の型の COM スマートポインタにキャストする場合。
:::

したがって、COM スマートポインタを使わない場合に下記のように書いていたものが、

```cpp:c++
	IPlayback* pPlayback;
	CoCreateInstance(CLSID_MusicPlayer,
		NULL,
		CLSCTX_INPROC_SERVER,
		IID_IPlayback,
		(void**)&pPlayback);

	pPlayback->Play();
	pPlayback->Stop();

	IVolumeControl* pVolumeControl;
	pPlayback->QueryInterface(IID_IVolumeControl, (void**)&pVolumeControl);

	pVolumeControl->Up();
	pVolumeControl->Down();
	pVolumeControl->Release();

	pPlayback->Release();
```

次のように書けることになる。

```cpp:c++
	IPlaybackPtr pPlayback;
	pPlayback.CreateInstance(CLSID_MusicPlayer,
		NULL,
		CLSCTX_INPROC_SERVER);

	pPlayback->Play();
	pPlayback->Stop();

	IVolumeControlPtr pVolumeControl{ pPlayback };

	pVolumeControl->Up();
	pVolumeControl->Down();
```

便利である。

:::note info
便利ではあるが、`QueryInterface` を考えればすぐわかるが、いつキャストが失敗するかわからない、ということと、`_com_ptr_t` 自体がテンプレートクラスであり、コンパイル時、いかなる COM インターフェース間のキャストであっても呼び出しを解決してコンパイル出来てしまう、というところに注意が必要である。
:::

以上。
jetpack composeでの幅取得方法

# はじめに
今回はJetpackComposeでよく使う幅の取得方法を紹介していきます
### 本文
端末自体の画面幅を取得するには下記を使用します
`LocalConfiguration.current.screenWidthDp`
Componentの画面幅を取得したい場合は`BoxWithConstraints`を使用することで取得することができます
### さいごに
BoxWithConstraintsに関してはメインの機能は幅の取得ではないですが、いろいろ便利なのでまた何か記事を書こうと思います
[初級] C++: COMコンポーネントの使い方（COMインターフェース）

COM オブジェクトから COM インターフェースを取得する方法
C++、COM、QueryInterface

:::note info
本記事の前提条件は以下の通りです。
* 初心者向け
* とは言っても、何らかのプログラムはそれなりに書けるけど、C とか C++ はちょっと、という人向け
* ざっくり概要しか説明しないので細かいことは気にしないでいただきたい
* Visual Studio 2013 くらい～
* Windowsプログラム (CUI, GUI)
* コードの検証
    * 開発環境: Visual Studio 2022, x64, Release ビルド
    * 実行環境: Windows 10
:::

:::note info
* 本記事は上から順番に読む前提となっている
:::

:::note info
* 「Visual Studio 2013 くらい～」と書いてあるが、本記事の内容だと現在過去未来のどのバージョンでもほぼ同じ
:::

:::note info
前提知識として必要と思われる情報
* [[初級] COMの基本的な仕組み（超ざっくり） - 拙著](https://qiita.com/yasu62347/items/ff9a48c9f97293dff360)
* [[初級] C++: COMコンポーネントの使い方（ビルド関係） - 拙著](https://qiita.com/yasu62347/items/af56de70a55d9357dfa2)
* [[初級] C++: COMコンポーネントの使い方（COMオブジェクト生成） - 拙著](https://qiita.com/yasu62347/items/72a9c3889b8431df0bee)
:::

# はじめに

ミュージック・プレイヤー・コンポーネントを考えてみる。
下図のようなインターフェースを持っているとする。

```mermaid
classDiagram
class IPlayback {
+Play()
+Stop()
+TogglePause()
+Previous()
+Next()
}
class IVolumeControl {
+Up()
+Down()
+ToggleMute()
}
```

また、ToneControl プラグインを追加することで下記インターフェースが使えるようになるとする。


```mermaid
classDiagram
class IToneControl {
+Treble(int level)
+Middle(int level)
+Bass(int level)
}
```

:::note info
再生するファイルを選択したり、オプション設定したり、その他の機能も必要と思われるが、それらについては割愛する。
:::

:::note info
なお、以降のサンプルコードではエラーチェックなどなにもしていないのでご了承いただきたい。（この状態での動作は確認している。COM サーバー側が想定通りであれば、だが）
また、COM システムの初期化・解放も別途必要なので留意。
:::

# COM オブジェクトの生成

`CoCreateInstance` を使った場合、COM オブジェクトの生成と同時に、COM インターフェースも取得する。
ここでは `MusicPlayer` オブジェクトを生成し、`IPlayback` インターフェースを取得している。

```cpp:c++
	IPlayback* pPlayback;
	CoCreateInstance(CLSID_MusicPlayer,
		NULL,
		CLSCTX_INPROC_SERVER,
		IID_IPlayback,
		(void**)&pPlayback);

	pPlayback->Play();
	pPlayback->Stop();

	// COMインターフェースを使い終わったら必ずReleaseする。
	pPlayback->Release();
```

# 別のインターフェースを取得する

COM インターフェースから `QueryInterface` を使って別のインターフェースを取得する。
ここでは、`IPlayback` インターフェースから `IVolumeControl` インターフェースを取得している。

```cpp:c++
	IPlayback* pPlayback;
	CoCreateInstance(CLSID_MusicPlayer,
		NULL,
		CLSCTX_INPROC_SERVER,
		IID_IPlayback,
		(void**)&pPlayback);

	pPlayback->Play();
	pPlayback->Stop();

	IVolumeControl* pVolumeControl;
	pPlayback->QueryInterface(IID_IVolumeControl, (void**)&pVolumeControl);

	pVolumeControl->Up();
	pVolumeControl->Down();
	pVolumeControl->Release();

	pPlayback->Release();
```

# インターフェースが取得できない場合に対応

ToneControl プラグインがないと `IToneControl` インターフェースは取得できない仕様なので、取得できたときだけ処理するようにする。

```cpp:c++
	IPlayback* pPlayback;
	CoCreateInstance(CLSID_MusicPlayer,
		NULL,
		CLSCTX_INPROC_SERVER,
		IID_IPlayback,
		(void**)&pPlayback);

	pPlayback->Play();
	pPlayback->Stop();

	IToneControl* pToneControl;
	HRESULT hr = pPlayback->QueryInterface(IID_IToneControl, (void**)&pToneControl);
	if (hr != E_NOINTERFACE)
	{
		pToneControl->Treble(80);
		pToneControl->Bass(65);
		pToneControl->Release();
	}

	pPlayback->Release();
```

以上。
[初級] C++: COMコンポーネントの使い方（COMオブジェクト生成）

COM オブジェクトの生成方法
C++, COM

:::note info
本記事の前提条件は以下の通りです。
* 初心者向け
* とは言っても、何らかのプログラムはそれなりに書けるけど、C とか C++ はちょっと、という人向け
* ざっくり概要しか説明しないので細かいことは気にしないでいただきたい
* Visual Studio 2013 くらい～
* Windowsプログラム (CUI, GUI)
* コードの検証
    * 開発環境: Visual Studio 2022, x64, Release ビルド
    * 実行環境: Windows 10
:::

:::note info
* 本記事は上から順番に読む前提となっている
:::

:::note info
* 「Visual Studio 2013 くらい～」と書いてあるが、本記事の内容だと現在過去未来のどのバージョンでもほぼ同じ
:::

:::note info
前提知識として必要と思われる情報
* [[初級] COMの基本的な仕組み（超ざっくり） - 拙著](https://qiita.com/yasu62347/items/ff9a48c9f97293dff360)
* [[初級] C++: COMコンポーネントの使い方（ビルド関係） - 拙著](https://qiita.com/yasu62347/items/af56de70a55d9357dfa2)
:::

# COM オブジェクト利用時の流れ

:::note info
簡潔にするために、STA モデルの COM サーバーをシングルスレッドのプログラムから呼び出すだけの場合を考えるものとするのでご了承いただきたい。
:::

## COM システムの初期化

`CoInitialize` (または `CoInitializeEx`)を呼び出して COM システムを初期化しておく必要がある。
なお、COM システムを使い終わったら `CoUninitialize` で各種リソースを解放する必要がある。

```cpp:c++
	CoInitialize(NULL);
 
	// COM の利用
 
	CoUninitialize();
```

## COM オブジェクトの生成

`CoCreateInstance` で COM オブジェクトを作成する。ポイントは引数で渡す COM コンポーネントの CLSID と、取得するインターフェースの IID である。

```cpp:c++
	CoInitialize(NULL);
    {
		IHogehogePtr p;
		CoCreateInstance(CLSID_Hoge,
			NULL,
			CLSCTX_INPROC_SERVER,
			IID_Fuga,
			(void**)&p);
		p->Hoge();
		p->Fuga();
	}
	CoUninitialize();
```

:::note info
CLSID から COM オブジェクトを生成する仕組みは下記参照。
* [[初級] COMの基本的な仕組み（超ざっくり） - 拙著](https://qiita.com/yasu62347/items/ff9a48c9f97293dff360)
:::

:::note info
`CLSID_Xxx`、`IID_Xxx` という識別子を利用するには、`#import` でタイプライブラリをインポートする際、`named_guids` オプションが必要なので注意。
:::

以上。
[初級] C++: COMコンポーネントの使い方（ビルド関係）

COM クライアントプログラムのビルドについて
C++、COM、タイプ ライブラリ

:::note info
本記事の前提条件は以下の通りです。
* 初心者向け
* とは言っても、何らかのプログラムはそれなりに書けるけど、C とか C++ はちょっと、という人向け
* ざっくり概要しか説明しないので細かいことは気にしないでいただきたい
* Visual Studio 2013 くらい～
* Windowsプログラム (CUI, GUI)
* コードの検証
    * 開発環境: Visual Studio 2022, x64, Release ビルド
    * 実行環境: Windows 10
:::

:::note info
* 本記事は上から順番に読む前提となっている
:::

:::note info
* 「Visual Studio 2013 くらい～」と書いてあるが、本記事の内容だと現在過去未来のどのバージョンでもほぼ同じ
:::

:::note info
前提知識として必要と思われる情報
* [[初級] COMの基本的な仕組み（超ざっくり） - 拙著](https://qiita.com/yasu62347/items/ff9a48c9f97293dff360)
:::

# はじめに

C++ で COM クライアントのプログラムを作成する場合、一般的な C++ のプログラムと少々異なる状況になるので、これについて書いてみる。

ポイントとしては、「ヘッダーファイルがない！」「どうやってリンクするの？」というところかと思う。

まず「タイプ ライブラリ」について説明する。

# タイプ ライブラリ

* COM コンポーネントの情報は、「タイプ ライブラリ」というバイナリ形式のデータとして保存される
* タイプ ライブラリには、COM コンポーネントの情報や、COM インターフェースの情報、メソッドのシグネチャ（関数名、引数など）の情報が含まれている
* タイプ ライブラリは MIDL (IDL コンパイラ) が、IDL ファイルをコンパイルすることによって生成される

## タイプ ライブラリの格納先

* 設定によるが、基本的にプロジェクトのビルド時に MIDL によって、`.tlb` として作成される
* これも設定によるが、COM コンポーネントの DLL (EXE, OCX なども含む) 自体に格納される場合がある

## タイプ ライブラリの参照

* C++ ソースファイルの `#import` プリプロセッサ ディレクティブによって、指定した `.dll` `.tlb` などタイプ ライブラリを含むファイルから COM インターフェースのシグネチャ情報を抽出し、C++ コード向けのヘッダファイルが生成される
* ヘッダファイルは通常中間ディレクトリに出力される
* ヘッダファイルの拡張子は、`.tlh` と `.tli` である

:::note info
なお、オートメーションとして利用される場合は、IDispatch インターフェースによって実行時にクライアントから参照される。
:::

# ヘッダーファイル

前述のように、`#import` プリプロセッサ ディレクティブによって生成された `.tlh` `.tli` がインクルードされることで、コンパイル可能となる。

# リンクは不要

C++ において、COM インターフェースはすべてが純粋仮想関数であり、そもそも実装がないのでリンクもされない。実行時に動的に解決される。

以上。
[初級] COMの基本的な仕組み（超ざっくり）

COMの基本的な仕組み（超ざっくり）

:::note info
本記事の前提条件は以下の通りです。
* 初心者向け
* とは言っても、それなりにWindowsでCOMを使うということがどういうことわかっている人向け
* あくまでもプログラマー向け
* 利用する言語は特に想定していない
* ざっくり概要しか説明しないので細かいことは気にしないでいただきたい
:::

:::note info
* 本記事は上から順番に読む前提となっている
:::

# そもそもどういうものか

とりあえずは、DLL (Dynamic Link Library) を、特別な仕様に基づいてすごく拡張したもの、と考えてよいと思う。
ただし、COM オブジェクトはあくまでも「オブジェクト」であり、つまり状態を持つ、ということに注意が必要ではある。

:::note info
とはいっても、DLL がグローバルのデータを持てるということと、別に COM オブジェクトに状態を持たせる必要はない、ということを考えると、結局大した違いはないとも言える。
:::

:::note info
なお、ここで言っている「状態」は、COM 特有の参照カウントを用いたライフサイクル管理の話ではなく、ADO の場合の DB への接続であったり、EXCEL の場合のシートのデータであったり、オブジェクトがデータを保持しており、それを操作することが多い、という話なのであしあからず。
:::

# COM サーバーとクライアント

ようするに、あるオブジェクトの実装およびそのオブジェクトを生成するためのファクトリ・メソッドを持った実行可能モジュールを COM サーバーといい、作成されたインスタンスが COM オブジェクトであり、それを利用するコードを COM クライアントと言う、ということである。

:::note info
COM サーバーにはインプロセスサーバーとアウトプロセスサーバーがある。
インプロセスサーバーはDLLであり、例えば VBA で ADO を使う場合はこんな風にするが、これは（これも例えばであるが）`msado15.dll` などを COM サーバーとして呼び出すことになる。

```vb:vb
Set ado = CreateObject("ADODB.Connection")
```

これに対して、例えば Excel はアウトプロセスサーバーであり、下記のようにすると、インストールされた `EXCEL.EXE` が呼び出される。

```vb:vb
Set excel = CreateObject("Excel.Appplication")
```
:::

:::note info
アウトプロセスサーバーにすると、例えば 32bit の COM サーバーを 64bit の COM クライアントから呼び出すことができるので、いざという時に微妙に便利だったりする。
:::

# COM オブジェクトの生成

どのような言語でもたいてい、`CreateObject` とか `CreateInstance` のような関数で COM オブジェクトを作成することになるが、この時、目的とする COM コンポーネントの ProgID か CLSID を引数で指定することになる。

## ProgID と CLSID

ProgID は、例えば、`ADODB.Connection` のような文字列である。
CLSID は、GUID であり、例えば `{00000514-0000-0010-8000-00AA006D2EA4}` のようなものとなる。

## レジストリから COM サーバーモジュールのパスを取得する

* ProgID が指定された場合、COM システムはレジストリの`HKEY_CLASSES_ROOT` から ProgID を探す。見つかったキーのサブキー `CLSID` から CLSID を取得する
* CLSID が指定された場合、あるいは ProgID から CLSID が取得できた場合は、レジストリの `HKEY_CLASSES_ROOT\CLSID` から CLSID を探す
* 見つかったキーのサブキー `InprocServer32` に該当 DLL へのパスが書いてあるので、この DLL をロードする（アウトプロセスサーバーの場合は `LocalServer32` にある EXE へのパス）

:::note info
いうまでもなく、レジストリのキー群は `regsvr32.exe` によって書き込まれたものである。
※厳密に言うと `regsvr32.exe` はCOM サーバーに実装されているはずのレジストリ登録用関数を呼び出すだけなので、実質的には COM コンポーネント自体が書き込んでいるのだが。
:::

:::note warn
64bit Windows での 32bit アプリケーションの場合や DCOM の場合は、レジストリの設定方法も異なるので留意。
:::

## COM オブジェクトの生成

COM システムは、DLL あるいは EXE をロードし、COM の仕様に従って COM コンポーネントのインスタンス（=COMオブジェクト）を生成し、呼び出し元に返却する。

以上。
【豆知識】meta name での警告が発生！非推奨？

# はじめに
今回は、開発者ツールに表示されたエラーではなく警告？が表示されていたので調べてみました

[![Image from Gyazo](https://i.gyazo.com/f5910ffd2645db05c18fced4ca10e868.png)](https://gyazo.com/f5910ffd2645db05c18fced4ca10e868)

なんか`Please`されてるけど？？どういう意味なのか？:rolling_eyes:


## 環境
- Windows, WSL
- Docker
- Ruby 3.2.3
- Rails 7.1.3


## そもそも`meta name`の部分が何か？

https://qiita.com/matri/items/da27143d66bcd02a1a8a

https://mugenweb-note.com/web/iphone/apple-mobile-web-app-capable

👆上記2つの記事が実際の画面もあり分かりやすかったです。

ざっとまとめると以下の通り
- ウェブアプリケーションがモバイルデバイス上で「フルスクリーン」モードで動作することを示すメタタグ。
- このタグがあることで、ユーザーがウェブアプリを「ホーム画面」に追加したときに、アプリがブラウザのUI要素（アドレスバーやナビゲーションバーなど）を表示せず、よりアプリっぽいインターフェースで表示される
- ポイントは次の3つ。**フルスクリーン表示**、**ユーザー体験の向上**、**PWA（Progressive Web App）対応**


## 修正方法
では早速`app/views/layouts/application.html.erb`（またはメタタグが含まれているファイル）を開いて、以下のように変更していきます。

```
<!-- 旧 -->
<meta name="apple-mobile-web-app-capable" content="yes">

<!-- 新 -->
<meta name="mobile-web-app-capable" content="yes">
```

# さいごにまとめ
少しコードを修正することで、その後再読み込みすることで無事に警告は解消されました！
エラーではないけど、警告が表示されると少し焦りますね💦
今回の記事が何か参考になれば幸いです。
道路ネットワークのグラフ生成（OpenStreetMap）

こんにちは。
OpenStreetMap データを用いて、道路ネットワークの[混合グラフ](https://ja.wikipedia.org/wiki/混合グラフ)の生成[^1]および各種機能を作りました。
* https://github.com/kkdd/GraphFromOSM

これは、グラフ生成機能を持つオリジナル（https://github.com/MatveiT/GraphFromOSM ）に基づいており、今回これに下記の機能を追加しました。どの機能も混合グラフ（directed と undirected edge が混在）に対処しています[^3]。
* ```displayGraph(graph)```: 地図上可視化
* ```extractConnectedWith(graph, highway)```: 指定した highway タイプの道路およびそれにグラフ的に連結した部分を抽出
* ```simplifyGraph(graph)```: simplification[^2] すなわち interstitial degree-2 vertices の削除[^4]
* ```dijkstra(source, destination, graph)```[^5]: 最短経路

[^1]: OpenStreetMap データモデルは、[mixed](https://en.wikipedia.org/wiki/Mixed_graph) [multigraph](https://en.wikipedia.org/wiki/Multigraph) と見なすことができます（directed and undirected ways が混在）。
[^2]: 参考解説例：[OSMnx: Python for Street Networks](https://geoffboeing.com/2016/11/osmnx-python-street-networks/) の中の "3. Correct and simplify network topology"
[^3]: 先行する実現例が見つからず今回自力で作りました。
[^4]: directed edge に対しては simplification の削除対象は indegree 1 and outdegree 1 の vertex です。
[^5]: 参考例：「[Dijkstra 法（priority queue 付き Map 利用, Javascript）](https://qiita.com/kkdd/items/6e79eebab215c4db9d53)」

#### 地図上可視化例
高速道路（highway=motorway）に連結した道路の可視化例です[^6]。連結対象は highway=motorway_link, service を指定しています[^7]。また最短経路例を黄色線で表示しています。

![mini1730911290.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54617/35dfeb61-d42f-eb3c-6958-3dd87be22ed0.jpeg)

```console.log
The imported OSM data contains 1582029 nodes and 338083 ways.
The converted graph contains 608127 vertices and 461491 edges.
The simplified graph contains 566531 vertices and 419895 edges.
The extracted graph contains 6530 vertices and 8622 edges.
The shortest path has 160 edges
```

[^6]: 参考例：「[日本の高速道路を OSM データから抽出](https://qiita.com/kkdd/items/1c39f87fe34c2fb406f3)」
[^7]: 今回 highway=service も対象としているため、これより広い領域のデータ取得は成功しませんでした。

##### 拡大表示
上記を拡大表示すると、interstitial degree-2 vertices が除去されており、また連結対象道路が抽出されていることが確認できます。
![mini1730445317.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54617/641928ee-2a4b-87d4-76f1-a3c98cb70a7f.jpeg)

##### directed and undirected ways が混在する例
有向辺と無向辺を水色と緑色の線で区別していることが確認できます。
![mini1730293512.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54617/ff87415d-3d83-b3e1-ec38-40fc56db09f8.jpeg)
\\だとバグるが、\\\\だとバグらないプログラム(正規表現)

# 話のはじまり
こんにちは、はじめです。
皆さんはよき正規表現ライフを送っているでしょうか？正規表現を使いこなしたいですよね。
正規表現で文字列パターンを指定する際に、「\」(バックスラッシュ記号もしくは半角￥記号。区別のため、この1文字を「」付きで記述します)という文字列が含まれているか調べたくなることがあると思います。
たとえば[『スッキリわかるJava入門 第4版』](https://sukkiri.jp/books/sukkiri_java4) の次の練習問題のシチュエーションを考えてみましょう。
> 練習15-2
> フォルダ名が入っている変数folderと、ファイル名が入っている変数fileがあります。fileは必ず「readme.txt」のような形式をしていますが、folderは末尾に\記号が付いている場合と付いていない場合の両方がありえます。たとえば、「c:\javadev」や「c:\user\」のどちらもfolderの値としては正しい形式です。
> folderとfileを連結して、「c:\javadev\readme.txt」のような完全なファイル名を表す文字列を組み立てるメソッドを作成してください。

これは、「c:\user\」はフォルダの階層関係のなかで既に子フォルダかファイルを持つ親フォルダであり、「c:\javadev」はそうなっていない末端のフォルダであるという現実的なシチュエーションを想定しています。
そこでフォルダの文字列の末尾が「\」であるか否かをチェックできればいいわけです。
# 正規表現の活用
条件を言い換えると、フォルダの文字列が「任意の文字列 + \」という文字列パターンに一致するか、という条件になります。この考え方で、`String`クラスの`matches`メソッドを使うとどうにかなりそうですね。(**落とし穴その1**)
任意の文字列を表現する文字列パターンは`.*`(任意の1文字の任意回の繰り返し)でした。これに「\」を表現する文字列`\\`を連結した文字列パターンでマッチングを調べれば良さそうです。
```ConcatString.java
public class ConcatString {

	public static String concatPath(String folder, String file) {
		if (!folder.matches(".*\\")) {
			folder += "\\";
		}
		return folder + file;
	}
}
```
これをmainメソッドで実行しましょう。
```Main.java
public class Main {

	public static void main(String[] args) {
		String folder1 = "c:\\user\\";
		String folder2 = "c:\\javadev\\";
		String file = "readme.txt";
		
		System.out.println(ConcatString.concatPath(folder1, file));
		System.out.println(ConcatString.concatPath(folder2, file));
	}
}
```
これを実行すると、例外 `Exception in thread "main" java.util.regex.PatternSyntaxException: Unexpected internal error near index 3`

が返ってきます。**バグりました！！！！** 何がいけなかったのでしょうか？(**落とし穴2**)
# 風呂敷を畳む
## 落とし穴2の解決
「\」は特殊文字であったため、文字列リテラルとして指定する際には`\\`というエスケープシーケンスで書く必要がありました。
しかし、「\」は正規表現(文字列パターン)においても特殊記号であるため、正規表現としてもエスケープシーケンスで書く必要があります。[参照](https://docs.oracle.com/javase/jp/7/api/java/util/regex/Pattern.html)
つまり、「\」を文字列パターンを指定するには2重にエスケープシーケンスで書いて`\\\\`と書く必要があります。
```ConcatString.java
public class ConcatString {

	public static String concatPath(String folder, String file) {
		if (!folder.matches(".*\\\\")) {
			folder += "\\";
		}
		return folder + file;
	}
}
```
`concatPath`メソッドをこのように書き換えて、`main`メソッドを実行すると無事にコンパイルが通り、
```
c:\user\readme.txt
c:\javadev\readme.txt
```
がコンソールに表示されます。
## 落とし穴1の解決
ここまででフォルダの文字列が「任意の文字列 + \」という文字列パターンに一致するか、という条件をmatchesメソッドで調べました。
しかし、`String`クラスには「末尾が指定された文字列で終わるか」を調べる`endWith`メソッドが存在します。そのため、わざわざ文字列パターンを考えなくとも下記で済みます。
```ConcatString.java
public class ConcatString {

	public static String concatPath(String folder, String file) {
		if (!folder.endsWith("\\")) {
			folder += "\\";
		}
		return folder + file;
	}
}
```
### まとめ by ChatGPT
**正規表現での「\」の扱い**
- 正規表現で「\」を表現するには、文字列パターンで`\\\\`と書く必要がある
- 「\」は文字列リテラルと正規表現の両方で特殊文字であるため、二重のエスケープが必要である

**フォルダパス連結の問題**
- フォルダ名の末尾に「\」があるかどうかを確認し、必要に応じて追加する処理が必要である
- `String.matches`メソッドを使用する代わりに、`String.endsWith`メソッドを使用するとより簡単に実装できる

**コーディングの注意点**
- 特殊文字を扱う際は、文字列リテラルと正規表現の両方でのエスケープを考慮する
- 既存のメソッド（例：endsWith）を活用することで、より簡潔で効率的なコードを書ける
Libertyでsystemdの設定してみました

## SystemdにLibertyをサービス登録して起動を実行　→　失敗


### あれれうまくいかない。。。



## 以下の対応が必要でした。

### bashでの起動が必要。まぁjavaを起動するから当たり前と言えば当たり前ですが

## bash -l -cを付けている説明

### -lはログインスクリプトのような動作
manからの引用

言いたいこと：ログインするときと同じだよってこと
※読み込ませたい変数がある場合は、こっちがおススメ。


bash が対話的なログインシェルとして起動されるか、 
--login オプション付きの非対話的シェルとして起動されると、
/etc/profile  ファイルが存在すれば、
bashはまずここからコマンドを読み込んで実行します。
このファイルを読んだ後、   bash   は  ~/.bash_profile,  ~/.bash_login,  ~/.profile  をこの順番で探します。
bashは、この中で最初に見つかり、かつ読み込みが可能であるファイルからコマンドを読み込んで実行します。--noprofileオプションを使ってシェルを起動すれば、 
この動作を行わないようにできます。


### -cはbashとして実行


```
[Unit]
Description=WebSphere Liberty Application Server
After=network-online.target
Wants=network-online.target

[Service]
Type=forking
PIDFile=/opt/wlp/usr/servers/.pid/%I.pid
ExecStart=/usr/bin/bash -l -c "/opt/wlp/bin/server start %I"
ExecStop=/usr/bin/bash -l -c "/opt/wlp/bin/server stop %I"
# Allow a minute for ExecStop before systemd sends SIGTERM
TimeoutStopSec=60
# JVM returns 143 after being killed with SIGTERM
SuccessExitStatus=143

[Install]
WantedBy=default.target
[root@mq1 system]#
```

https://www.ibm.com/support/pages/managing-websphere-liberty-services-linux-using-systemd

### 公式サイトに書いておいてよ。
※自分の理解が低いだけかも。。。。
Djangoでoperationalerrorの解決方法

## operationalerrorにどハマりした件について

### どうしてこうなったのか

初心者である僕は一生懸命以下2つのコマンドをポンポン叩いていました。
```
python manage.py makemigrations
```

```
python manage.py migrate
```
すると、どこかでモデル修正を間違えてしまい```operationalerror```に遭遇してしまいました。
```makemigrations```は通るのに```migrate```でエラーになって通らない！！
もう冷や汗ダラダラ最悪😭って感じでした。とほほ




### そもそもoperationalerrorとは？ 
モデルを作成・編集したのに、変更した内容をデータベースに反映していないよ！というエラーです。
このエラーが出てきたらまずは```python manage.py migrate```を実行していないことを疑ってもいいかもしれませんね。

ですが、この記事を見ている方は恐らく私と同じ```python manage.py makemigrations```は通るのに```python manage.py migrate```が通らない方でしょう。

### 解決方法
#### Step1 migrationの履歴を見てみよう

```
(venv) % python manage.py showmigrations
admin
 [X] 0001_initial
 [X] 0002_logentry_remove_auto_add
 [X] 0003_logentry_add_action_flag_choices
auth
 [X] 0001_initial
 [X] 0002_alter_permission_name_max_length
blog
 [X] 0001_initial
        .
        .
        .
 [ ] 0017_notification_created_at
```

ここで```blog```の```0017_notification_created_at```に「X」がついていないことが判明しました。
※ Xが付いていない = migrateしてないよ！とうい意味です。
これが原因で```migrate```が通らないことがわかりました。

#### Step2 migrationsファイルを探す

例えばですが、こんなファイル構造だったとします。
```
project                    
├── config                    
│   ├── __init__.py
│   ├── asgi.py
│   ├── settings.py
│   ├── urls.py           
│   └── wsgi.py # 重要
├── blog              
│   ├── __init__.py
│   ├── admin.py
│   ├── apps.py
│   ├── migrations  ここの中にあるmigrationsファイルを探します。
│   ├── models.py
│   ├── tests.py
│   └── views.py
└── manage.py
```

さっきの「X」がついていない箇所が「blog」でしたね。
なので私は ***blog*** の ***migrations*** の中から特定のファイルを探します。
そして無事```0017_notification_created_at```ファイルを見つけました！！

#### Step3 見つけたmigrationsファイルを削除
あとは簡単です。
ファイルを削除するだけです。
この時に間違えて別のファイルを削除しないように気をつけてください。

#### Step4 データベースに反映させる

ここまできたらいつものmakemigrationsとmigrateをするだけです。

```
python manage.py makemigrations

python manage.py migrate
```

その際に以下のような質問を投げられるかもしれません
```
It is impossible to add the field 'created_at （カラム名）' with 'auto_now_add=True （ここの値をデフォルトで値を設定しないとデータベースに反映できない）' to notification without providing a default. This is because the database needs something to populate existing rows.

 1) Provide a one-off default now which will be set on all existing rowsc
 2) Quit and manually define a default value in models.py.
 ```
 
 これは1を選択すると、自動でデフォルト値を決めることになるのでその場で質問形式でデフォルト値を決めることができます。
 
 2を選択すると手動でデフォルト値または、データベースに必須ではないことを教える必要があります。
 こんな感じでしょうか
 ```
 ※これはあくまで例です。
 
 defaultを追加する場合
 title = models.CharField(verbose_name='タイトル', max_length=30, default='タイトルです。')

 データベースに保存することを必須にしない場合
 title = models.CharField(verbose_name='タイトル', max_length=30, null=False)
 ```
これでOKです。

### 最後に
エラー解決できましたでしょうか？
私はこれを知るまではいちいちデータベースの中身を全て消して空っぽにしてから再度migrateしてました。笑
migrationsファイルを削除するだけで解決できるとは。。。いい時代になったものだ。
オーディオ CD のバックアップソフト Exact Audio Copy のインストール

Exact Audio Copy (EAC) は、オーディオCDを高品質でリッピング（デジタル化）できるソフトウェアです。EACはその正確さと詳細なエラー検出機能が評価されています。個人は無料で使うことができ、商業利用の場合はライセンスの購入が必要です。この記事では 個人ユーザ向けに EAC のインストール方法を紹介します。

![41_tool.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3160433/8fb3540e-f1bd-4a21-0e8e-e3848c89cff6.png)
## Exact Audio Copy を使う目的
### オーディオCDのアーカイブ
FLACやWAVなどのロスレスフォーマットでCDをデジタルアーカイブとして保存できます。

### エラーチェックと訂正
CD の表面に傷や汚れがある場合でも、EACはデータを何度も読み込み、エラーを検出・訂正しながらデジタルコピーを作成します。

### 大量の CD の管理
大量の CD を物理的に持ち運ぶ代わりに、アーカイブに変換することで、パソコンに入れて楽に持ち運ぶことができます。

## Exact Audio Copy のシステム要件
Exact Audio Copy のシステム要件は以下の通りです。

- オペレーティングシステム: Windows XP, Vista, 7, 8/8.1, 10, 11
- .NET Framework: .NET Framework 2.0 以降

ここでは Exact Audio Copy 1.8 を例にして、Windows 11 へのインストール方法を紹介します。

## Exact Audio Copy のインストール手順
Exact Audio Copyのダウンロードページにアクセスします。

[https://www.exactaudiocopy.de/en/index.php/resources/download/](https://www.exactaudiocopy.de/en/index.php/resources/download/)

ページが表示されたら、「Download」のリンクをクリックします。

![01_officialsite.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3160433/01425cc5-ce77-2181-e02e-52e08437615e.png)
### インストーラの起動
ダウンロードが完了したら、ファイルをダブルクリックし、インストーラを実行します。

![11_download.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3160433/b8caa00b-c59a-d3cf-8f5b-56088a5f530e.png)
### ユーザーアカウント制御の確認
ユーザーアカウント制御が表示されたら、「**はい**」をクリックします。

![13_download.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3160433/e8461350-3dd6-bbed-7d88-d61bee42d995.png)
### セットアップ画面の確認
セットアップ画面が表示されたら、「**Next**」をクリックします。

![21_install.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3160433/4c7e5058-e62a-d70d-f46c-b0ed97442963.png)
### ライセンスの確認
使用許諾契約の内容を確認して、「**I Agree**」をクリックします。

![22_install.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3160433/5745c47f-97e1-4db7-aee1-1ba9d7341f99.png)
### インストール先の選択
インストール先フォルダの選択画面が表示されたら、「**Next**」をクリックします

![23_install.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3160433/772e1e2e-2351-6950-4375-e622e08b41f6.png)
### スタートメニューの選択
スタートメニューフォルダの選択画面が表示されたら、「**Next**」をクリックします

![24_install.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3160433/079bb386-9217-967b-eaa7-281f202ec36c.png)
### インストールする機能の確認
インストールする機能の選択画面が表示されたら、「**Install**」をクリックします。

![25_install.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3160433/8341ed85-44f1-875c-a2f1-51bfc3ef9463.png)
### インストールの終了確認
インストールの完了画面が表示されたら、「**Finish**」をクリックします。

![26_install.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3160433/955a2c64-6a5d-0ca5-9574-67f68d239b0c.png)
Android Studioのエミュレータで5Gを使用する



# 設定方法

前回紹介した

https://qiita.com/Tsubasa12/items/45464016958949ad177c

こちらの記事を参考にエミュレータの設定を開きます。

**Cellular**の中の、Network typeで5Gを選択します。

以上になります。
![スクリーンショット 2024-09-22 18.06.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2394054/56ba276f-b6e8-c10d-4ccc-17ab78925cea.png)
【React】useStateで更新した値を確認する



useStateでsetしたあとにconsole.logで表示しても値は確認できない。
```
const [hoge, setHoge] = useState<string>('')

const handleOnChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    setHoge(e.target.value)
    console.log(hoge); // undefinded
}
```

この場合は
- 代入する値を直接見る
- useEffectで監視する
らしいけど、useEffectで監視するがよさそう

```
const [hoge, setHoge] = useState<string>('')

const handleOnChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    setHoge(e.target.value)
    console.log(hoge);
}

useEffect(() => {
    console.log(hoge);
}, [hoge])
```

これならconsole.logで確認できる！