OmniAICreator/Qiita-1.07M huggingface dataset:
プロになるためのWeb技術入門を読破したので感想

## はじめに
・[プロになるためのWeb技術入門](https://www.amazon.co.jp/%E3%80%8C%E3%83%97%E3%83%AD%E3%81%AB%E3%81%AA%E3%82%8B%E3%81%9F%E3%82%81%E3%81%AEWeb%E6%8A%80%E8%A1%93%E5%85%A5%E9%96%80%E3%80%8D-%E2%80%95%E2%80%95%E3%81%AA%E3%81%9C%E3%80%81%E3%81%82%E3%81%AA%E3%81%9F%E3%81%AFWeb%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E3%82%92%E9%96%8B%E7%99%BA%E3%81%A7%E3%81%8D%E3%81%AA%E3%81%84%E3%81%AE%E3%81%8B-%E5%B0%8F%E6%A3%AE-%E8%A3%95%E4%BB%8B/dp/4774142352)を読みました。
こちら簡単ではありますが、自分のアウトプットも含めて用語をまとめてみました。

## 用語
・**ステートフルとは？**
まずは言葉の意味から見ていきます！ステートフル(stateful)のステート(state)とは「状況や」「状態」を意味する言葉です。そしてフルとは「ビューティフル」などの単語も見られるように「~に満ちた」と言う意味になります。
つまりステートフルとは **「状態を維持すること」** を意味します。
IT業界におけるステートフルと言う言葉は、以前のセッション状態を保持してその後の結果に反映させるプロトコルやアプリケーションを指しています。

・**通信プロトコルの例**
ステートフルなプロトコルの例は「FTP」「TCP」「SSH」など


・**ステートレスとは？**
ステートレス(stateless)の「レス」とは不足していることを意味します。
ITにおいては、前後の状況に応じて処理の仕方をさまざまなに変えるステートフルなプロトコルに対して、以前のセッション情報を保持せず「前後の状況に関係なくその都度いつも同じレスポンスを返すようなプロトコル」がステートレス

・**通信プロトコルの例**
ステートレスなプロトコルの例としては「HTTP」「UDP」「DNS」など

・**リクエストとは？**
「リクエスト(Request)」は、クライアントからサーバーに対して何らかの情報や要求するために送信するメッセージです。

・**レスポンスとは？**
「レスポンス（Response）」は、サーバーがクライアントに対して送信するメッセージであり、クライアントのリクエストに対する応答です。

・**クッキーとは？**
Cookie(クッキー)とは、Webサーバーからクライアントコンピュータに預けておく小さなファイルのことです。クライアントコンピュータがあるWebサーバーに初めて接続した際にWebサーバー専用のCookieファイルを作成します。通信するごとにクッキーが存在します。

・**プロトコルとは？**
プロトコルは、通信やデーターの交換における規則や手順のセットです。
異なるコンピュータやシステム間での相互接続と通信を可能にします。
プロトコルは、信頼性の確保やデータの正確な送受信を促進し、ネットワークの効率的な動作をします。

・**ポート番号とは？**
ポート番号は、コンピュータネットワークにおいて、特定のプロトコルやサービスが通信するために予約された番号です。
TCP/IPネットワークでは、０から６５５３５までの範囲のポート番号があり、特定のプロトコルやサービスが割り当てられています。
例えば、Webサーバーは通常、HTTP通信に使用されるポート番号８０を利用します。

## 感想
Web業界で働くには基本的な概要や用語が含まれており大変勉強になりました。
また、実務を経験した後に読むとさらに理解が深まる内容でした。(私は2回読みました)
Rubyのハッシュについて

# はじめに

#### <font color="red">※以下の内容には誤りが含まれる可能性があります</font>


# ハッシュとは何か


ハッシュはキーと値の組み合わせや、複数のオブジェクトへの参照をまとめて管理することができるオブジェクトです。


似たような存在として配列があります。


配列も複数のデータへの参照をまとめて管理できるオブジェクトですが、


配列の場合は、名札に０からの番号が自動で順番に割り当てられるイメージです。


ハッシュの場合は名札の名前を自由につけることができます。


文字列などの任意のオブジェクトを使って、自由につけることができます。


文字列や数値など複数の種類のオブジェクトを格納する場合


は、番号で管理する配列よりも、自由に名前をつけられるハッシュの方が管理しやすいです。



# ハッシュの要素


ハッシュの中の名札は、要素と呼ばれます。


Rubyでは要素はオブジェクトのことを示しています。


# ハッシュのキー


要素に割り当てた任意のオブジェクトは、キーと呼ばれます。


各要素を特定するために、キーが使われます。


キーを指定することで、キーに対応するオブジェクトを参照することができます。



# まとめ



・ハッシュは複数のオブジェクトへの参照をまとめて管理できる


・ハッシュは複数の名札が繋がって自由に名前を付けられるイメージ


・複数の種類のオブジェクトを格納する場合は、ハッシュのほうが識別しやすい。
Java 11メモリ管理：JVMオプションの理解と適用

# はじめに

Java 11におけるメモリ管理の一部として、Java Virtual Machine（JVM）オプションは重要な役割を果たします。これらのオプションを使用して、JavaアプリケーションのヒープメモリとMetaspaceのサイズを調整できます。しかし、これらの設定は時として混乱を招くこともあります。それでは具体的なオプションとそれらがどのように動作するのかを見てみましょう。

# JVMオプションとは何か

JVMオプションはJavaアプリケーションのメモリ設定を行うために使用されます。ヒープメモリの初期および最大サイズ、Metaspaceの初期および最大サイズを指定することができます。

# Java 11における主要なJVMオプション

Java 11では、以下のようなJVMオプションを用いてメモリ設定を調整できます：

1. `-Xms1024m`：Javaアプリケーションが起動するときにJVMによって割り当てられるヒープの初期サイズを指定します。この例では、初期ヒープサイズは1024メガバイト（1ギガバイト）に設定されています。

2. `-Xmx2048m`：JVMが割り当て可能なヒープの最大サイズを指定します。この例では、最大ヒープサイズは2048メガバイト（2ギガバイト）に設定されています。

3. `-XX:MetaspaceSize=512m`：Metaspaceの初期サイズを指定します。Metaspaceは、Java 8以降でクラスメタデータを格納するために使用される領域です。この例では、初期Metaspaceサイズを512メガバイトに設定します。

4. `-XX:MaxMetaspaceSize=1024m`：Metaspaceの最大サイズを指定します。この例では、最大Metaspaceサイズを1024メガバイト（1ギガバイト）に設定します。

# 注意点

以上の設定はあくまで一例です。適切な設定は、アプリケーションの特性、利用するリソース、パフォーマンス要件などにより異なります。また、設定を調整する際には十分なテストを行い、パフォーマンスの影響を監視する
GitHubのIssueに何を書く？

GitHubのIssueは、プロジェクトやリポジトリに関する問題や改善提案、タスクなどを追跡するための機能。Issueを作成する際には、以下のような内容を記述することが一般的です。

## タイトル
Issueの内容を簡潔に表すタイトルを付けます。タイトルは明確かつ具体的にします。

## 説明
Issueの詳細な説明を記述します。以下の情報を含めることが一般的です。

## 問題の背景
Issueが何に関連しているのか、どのような状況や問題があるのかを説明します。
目的: Issueを作成する目的や解決すべき課題を明確にします。
期待する結果: Issueが解決された場合に得られるべき結果や期待される動作を説明します。

## 再現手順
問題を再現するための手順や環境の情報を提供します。
関連する情報: Issueが関連する他のIssue、プルリクエスト、またはコミットがある場合は、それらのリンクを追加します。関連する情報を提供することで、コンテキストを共有しやすくなります。

## 優先度やラベル
Issueの優先度や重要度を示すために、適切なラベルを付けることができます。ラベルはカスタマイズ可能で、プロジェクトに合わせて設定することができます。

## 割り当て
Issueを特定の担当者に割り当てる場合は、割り当てるユーザーを指定します。これにより、誰がIssueに取り組む責任を持つのかが明確になります。

## ディスカッション
Issueに関する質問、コメント、意見などのディスカッションを追加することができます。他の開発者とのコミュニケーションやフィードバックを行うために利用できます。

Issueは、プロジェクトの進行管理や問題の追跡に役立つ重要なツールです。より具体的で明確な情報を提供することで、他の開発者がIssueを理解しやすくなり、問題の解決や改善に向けた協力がスムーズに進みます。

## この記事の補足
個人的なメモとして書きました。
PDF,Excelとで通貨単位を百万円単位、一円単位に分けて出力する【Cognos Analytics】

#### 概要
　BIから出力されるレポートにおいて、「定期的に出力されるレポートについてはざっくりした金額を出力してチェックしたい。一方でマクロのインプットとする場合は正確な金額を出力したい。」という用途があります。
　金額をチェックするためのレポートをPDF出力で、マクロのインプット用にはExcel出力とし、各出力で金額を百万円単位・一円単位とする方法が取られます。
　ここでは、PDF・Excelで出力する場合の金額のスケールを変更する方法をご紹介します。

---

#### 手順
レポートを新規作成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255563/37e79963-8400-3e47-01ee-139e12f0dc0a.png)

作成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255563/b7de4aec-f1fc-35b3-8acb-c5c839224a56.png)

ソースを選択で、GOデータウェアハウス（クエリー）を選択。
ページ1に「リスト」を配置し、製品と収益をリストに追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255563/37a68f64-0611-f48c-f3a1-d93848be00cb.png)

「製品」ごとの収益額を、PDFでは百万円単位表示、Excel出力では一円単位表示にします。
「収益」のプロパティ、「条件付きスタイル」から「詳細な条件付きスタイル」を新規作成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255563/19dc97b3-d08a-ff16-c8f5-f33dc130fcd1.png)

名前をPDF出力とし、「詳細な条件を新規作成」をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255563/08fe966a-91f6-7d07-964d-82d13aa9f44f.png)

式の定義に下記をセット。これで、レポート出力がPDFである条件を定義しました。次に、この場合の書式をセットしていきます。
~~~
ReportOutput() = "PDF"
~~~
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255563/405bab5c-7731-d8c3-06eb-e725f77dcff2.png)

鉛筆マークから、スタイルを編集
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255563/becb391d-2db5-85d1-4730-58526580297f.png)

データ形式を編集
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255563/d9121a5b-f415-66b3-a780-a7fbbf32d7e2.png)

表示形式を「数値」、スケールを「-6」とセット。スケールを-6とすることで、6桁文小数点を移動させることができ、百万円単位で表示ができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255563/1447ccbd-ed75-c662-8bd6-1be5bd74ba75.png)

スケールを-6とすることで小数点以下桁数が増えてしまいます。PDFでは、百万円単位の概算値を見る目的のため、「小数点以下の桁数」を1としましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255563/2c2dc3d4-33e6-6bba-1845-5bced176e252.png)

OKを選択して、条件付きスタイルの画面まで戻り、Excel出力の条件を追加していきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255563/6fa2f966-699b-84d0-957f-c95060cacb49.png)

下記式の定義として、「Excel出力」条件を追加
~~~
ReportOutput() = "spreadsheetML"
~~~
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255563/4c1b450a-a08a-307c-2271-08f4d083ef62.png)

データ形式について、PDF出力の場合と同様に表示形式は数値とし、小数点以下の桁数は1とします。Excel出力の場合には一円単位で出力をさせるため、スケールを0とします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255563/6c34f0c5-f276-be5e-4ecf-5b91b4c46fc3.png)

PDFの出力結果を確認すると、スケールを-6としたため桁が左に6つ移動し、112.9　(百万円)と出力されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255563/1553f0f5-323a-12ab-f9b8-610caad94b9a.png)

一方、スケールを0としたExcel出力では一円単位で表示されたことが確認できました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255563/d61b38db-17ba-c048-20d8-6976d9be915f.png)
Renderでのデプロイが失敗した時の確認

<h2>Renderでログインや新規登録アイテム投稿などのページに遷移できないエラーが発生した時</h2>
ログイン画面に遷移するボタンをクリックした際に以下のような画面になる。

![861e024473e055de04e373ac62de6f80.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3310854/17992658-535c-ccd0-889d-6268032c3a33.png)

We're sorry, but something went wrong.
If you are the application owner check the logs for more information.

<h3>今回発生したエラーの経緯</h3>
勉強のためフリマサイトのクローンを作成している際に発生。トップページのデプロイ時は問題なく、表示ができている状態。

<h3>確認するべきこと</h3>

1. Render.comのダッシュボードのLogsを開いてLogを確認する
2. Searchの検索機能で「error」または「 rake aborted!」と入力してエラーの原因となる個所を探る
3. errorの記述周辺にエラー原因の記述があった

<h3>エラーの原因</h3>
I, [2023-06-10T22:17:15.399052 #70]  INFO -- : [340111ec-77b1-4007-b942-9e601bb7280f] Completed 500 Internal Server Error in 74ms (ActiveRecord: 35.5ms | Allocations: 6576)<br><br>
 [340111ec-77b1-4007-b942-9e601bb7280f] ActiveRecord::StatementInvalid (PG::UndefinedTable:<font color="red"> ERROR:  relation "users" does not exist</font><br><br>
 LINE 8:  WHERE a.attrelid = '"users"'::regclass

<br><br>
今回はusersテーブルが存在しないため、それに関するページに遷移した時に発生していた模様。テーブルの設定ができていなかったようなのでVScodeの記述を確認した。

```bin/render-build.sh
#!/usr/bin/env bash
# exit on error
set -o errexit

bundle install
bundle exec rake assets:precompile
bundle exec rake assets:clean
```

となっていてデータベースをmigrateするための記述が抜けていることが分かった。

```bin/render-build.sh
#!/usr/bin/env bash
# exit on error
set -o errexit

bundle install
bundle exec rake assets:precompile
bundle exec rake assets:clean
bundle exec rake db:migrate  #この記述を追加
```

上記のように編集したのちGitHubにコミット、プッシュしてデプロイ完了後確認すると問題なく表示ができるようになった。<br><br>

ちなみに、Renderで作成したデータベースをリセットしたいときは以下の通り行う。

<h3>Renderで作成したデータベースをリセットする方法</h3>

```bin/render-build.sh
#!/usr/bin/env bash
# exit on error
set -o errexit

bundle install
bundle exec rake assets:precompile
bundle exec rake assets:clean
# bundle exec rake db:migrate   ←コメントアウト
DISABLE_DATABASE_ENVIRONMENT_CHECK=1 bundle exec rake db:migrate:reset　　#追記
```

`DISABLE_DATABASE_ENVIRONMENT_CHECK=1`が、本番環境のデータベースを削除するために必要な設定。本番用のデータベースには重要なデータが含まれるため、通常は削除できないようになってる。

今回はリセットを行いため、明示的に削除を許可する設定を記述。

`bundle exec rake db:migrate:reset`でデータベースをリセットする。以下の３つを順に実行したのと同じ動作をします。

rake db:drop（データベースの削除）
rake db:create（データベースの作成）
rake db:migrate（マイグレーションファイルの実行）

<h2>まとめ</h2>

* エラーが発生した時はLogを見る
* エラー発生個所がわからない場合はサーチ窓に「error」「rake abbord!」を入力してみる。
* bin/render-build.shファイルの記述はRenderにて実行するコマンドを入力しておくファイル
* db:migrateの記述がないとデータベースが本番環境にてmigrateされない


初歩的なところで躓いてしまったが、今回のエラーでrender.build.shファイルの役割が勉強できてよかった。
【Herokuデプロイ時のエラー】 UnsupportedClassVersionErrorとError R10 (Boot timeout)

## **やろうとしていたこと**

JavaとVert.xを使用してWebアプリケーションを開発し、そのアプリケーションをHerokuにデプロイしようとしていました。

## 発生したエラー

デプロイを試みたところ、Herokuのログに2つのエラーが表示されました。

1. UnsupportedClassVersionError: your-project-directly/ has been compiled by a more recent version of the Java Runtime (class file version 61.0), this version of the Java Runtime only recognizes class file versions up to 52.0
2. Error R10 (Boot timeout) -> Web process failed to bind to $PORT within 90 seconds of launch

## **エラーの発生理由**

1. UnsupportedClassVersionErrorは、開発環境で使用しているJavaのバージョンがHeroku上のJavaランタイムのバージョンよりも新しいために発生したようです。具体的には、開発環境でJava 17を使用してコンパイルされたクラスファイルを、Heroku上のJava 8環境で実行しようとしたため、このエラーが発生しました。
2. Error R10は、Herokuがアプリケーションに対して提供している$PORT環境変数のポートに、アプリケーションが90秒以内にバインドできなかったことを示すエラーです。Herokuでは、この時間制限内にアプリケーションが起動しなければならないという制約があります。

## **解決方法**

1. UnsupportedClassVersionErrorの解決方法:
HerokuのJavaランタイム環境を、アプリケーションが要求するJavaバージョンに合わせることで解決しました。Herokuのシステムプロパティ設定ファイルを通じてJavaのバージョンを指定することで実現できます。具体的やり方は、`system.properties` ファイルをプロジェクトのルートディレクトリに作成し、その中に `java.runtime.version=17` のような形式でバージョンを指定します。記述する内容はコレだけです！
2. Error R10の解決方法:
アプリケーションが、Herokuが指定するポートにバインドするように修正する必要があります。今回の私のようにJavaとVert.xを使用している場合は、`System.getenv("PORT")`を使用してポート番号を取得し、そのポートにアプリケーションをバインドすることができます。また、アプリケーションの起動時間が長すぎる場合は、Herokuのダイノを使用する方法もあるようです。

## **参考記事**

1. Heroku Dev Center - **[Java Support](https://devcenter.heroku.com/articles/java-support#specifying-a-java-version)**
2. Heroku Dev Center - **[Error R10 (Boot timeout)](https://devcenter.heroku.com/articles/error-codes#r10-boot-timeout)**
Heroku PostgreSQLに接続する際のVert.xのSSL設定について

# ****Heroku PostgreSQLに接続する際のVert.xのSSL設定について****

## **やろうとしていたこと**

JavaとVert.xについて学習するためにAPIを開発していく中でheroku PostgreSQLに接続できずに困ることがありました。

Javaの経験も浅くあまり理解しきれていないので、色々間違っているかもしれません。

## **どのようなエラーなのか**

Vert.xの`PgConnectOptions`と`PgPool`を使ってHeroku PostgreSQLに接続しようとした際に、次のエラーが発生しました。

```
Failed to initialize the database connection pool: Trust options must be specified under verify-full or verify-ca sslmode
```

## **エラーの発生理由**

エラーメッセージは、SSLモードが `verify-full` または `verify-ca` の下で信頼オプションが指定されていなければならないと言っています。これは、SSL接続を確立するために必要な証明書が設定されていないことを示してます。

このエラーは、`.setTrustAll(true)`が原因で発生したようです。PostgreSQLはデフォルトでこのオプションを無効にしているようなので、エラーが発生したと考えています。

## **解決方法**

Heroku上のPostgreSQLサーバーに対しては、Herokuが提供している証明書を使用できます。証明書は通常、HerokuのPostgreSQLアドオン設定から取得できます。その証明書をプロジェクトに含めて、それを使用して信頼オプションを設定することで解決できました！

1. HerokuのPostgreSQL設定から証明書をダウンロードします。
2. ダウンロードした証明書をプロジェクトのリソースディレクトリ（例:**`src/main/resources`**）に移動します。
3. **`.setTrustOptions`** メソッドを使用して証明書を設定します。証明書の場所を参照するには、**`JksOptions`** を使用します。

以下に、証明書を設定したコードの例を示します。

```
PgConnectOptions connectOptions = new PgConnectOptions()
    .setPort(port)
    .setHost(host)
    .setDatabase(database)
    .setUser(username)
    .setPassword(password)
    .setSsl(true)
    .setTrustOptions(new JksOptions()
        .setPath("/path/to/your/cert.crt") // リソースディレクトリからの相対パスを指定します。
        .setPassword("the-password")); // もし証明書がパスワードで保護されている場合は設定します。
```

## ちょっと**疑問**

### **Railsではそんな設定必要なかったのに。。**

Ruby on Railsの環境では、SSLの検証を気にせずにHeroku PostgreSQLに接続できましたが、なぜJavaとVert.xの環境ではこの問題が発生したのか？

この違いは、各フレームワークやライブラリがデフォルトで提供する機能と、セキュリティに対する考え方による部分が大きいと思われます。

JavaのJDBCは、基本的にはデータベース接続の最低限の機能しか提供していないようです。一方、Ruby on RailsのActiveRecordなどのORMライブラリは、データベース接続だけでなく、SQLの生成、モデルとデータベースのマッピング、そしてデータベース接続の設定など、アプリケーション開発に必要な多くの機能を提供しています。

以上の違いからRailsでは今回の設定は必要なかったのかなと考えています。

詳しくは調べていません。
エンジニアは資格よりも個人開発を優先すべき理由

## はじめに
私は3社経験しており、1社目がインフラ系、2社目がSES、3社目が事業会社です。
資格の取り扱いについては下記の通りでした。

1社目
海外ベンダー製品のテクニカルヘルプデスク部に配属。
その業務に就くのにそのベンダーの資格が必要で、会社に入って最初にやったことがその資格勉強。

その会社では資格を取るほど、毎月の給与をアップさせるという資格制度を出していたため、
ひたすら資格をとっていた。

2社目
毎年自分の目標を立てて、その目標の達成度を一つの評価指標にしていた。
だいたいどの人も資格取得を目標の中に取り入れていて、資格取得を年1でやっていた。

3社目
資格取得時の受験費用だけ出す。会社としてはそれほど重視してない。

というふうに1社目、2社目では必然的に資格を取らないといけない環境でした。
今振り返ってみて、資格取得について思うところを書いていきます。

## 資格取得よりも個人開発を優先すべし！
本記事のタイトルにもある通り、これが一番大きいです。
理由を書き連ねていきます。

### 転職時には資格よりも個人開発のほうが評価される
過去2回（活動だけやって結局今の会社に残る判断をしたこともあるため、実質3回）の転職で、
実際の面接時に聞かれた内容は資格のことよりも個人開発の話でした。

転職エージェントも何度か使ったのですが、毎回アドバイスされることはGitHubのURLを職歴書に書くことでした。
資格についてはとりあえず書いとけぐらいでした・・・

特にインフラエンジニアからWeb系SESに転職する際は開発経験がありませんでしたから、
余計個人で作ったものをアピールする必要がありました。

### 資格には有効期限があるものが多い
IPAの資格には有効期限はないのですが、ベンダー資格はどれもだいたい有効期限があります。

有効期限はだいたい3年～5年程度です。
最初はこれだけ有効期限があれば大丈夫と思うのですが、時が過ぎ去るのはあっという間です。

有効期限が切れてしまうと資格取得によるアピール自体できなくなったしまいます。
資格取得をするにしても大前提、有効期限がないもの、IPAなどの認知度が大きいものを選択すべきでしょう。

### 資格は応用が効かない
資格は体系的に学べるというメリットはあるのですが、資格を取った後何をするか悩みがちです。

一方で個人開発は作るまでの過程で、開発環境やデザインカンプなど必要なものを揃えたり、
作成期間の計画を立てるなど、実際の開発業務に近いことを個人でやることができます。

そこから実業務への応用が効くことが多々あります。

## 資格のコスパについて
資格を取るためには資格勉強が必要で、だいたいの勉強期間は3ヶ月ぐらいという人が多いです。

それだけ時間をかけても受かるかどうかは分からないし、
受かった場合でないと受験費用を出さないという会社がほとんどです。

私の1社目は資格重視の会社だったので、ひたすら資格を取るのに必死でしたが、
それに掛けた時間が今ためになっているかというと、あまり自分のためになっていないと思います。
あとは受からなかった資格もあり、それは自腹というのがかなり痛かったです。

## まとめ
資格については体系的に学べるというメリットはあるものの、時間工数、資格の価値の低さ、落ちた場合の自腹リスクなどデメリットも非常に多いものだと感じています。

資格取得を前提にした勉強の仕方よりも、個人開発を前提にした勉強の仕方のほうがはるかに効率が良いと思います。

資格については既に知っている技術について、自分を試す意味で受けてみるとかおまけ程度でいいんじゃないでしょうか。
ここ1年私は資格勉強を一切やらなくなりましたが、それほど困ることはありませんでした。

最後に受けたのはIPAの応用情報ですが、有名だから取っておいたらいいことあるんじゃないかと思ってましたが、
今のところ恩恵は受けていません・・・

そもそも資格をアピールする場って転職時ぐらいしかなくて、しかもその転職時にもあまり評価された覚えがないので、資格取得ってほぼ自己満足なのでは？と最近思っています。
Vue : ヘッダーでログインボタンを押下するとメニューのボタンが表示する。

・技術要素　Vue.js
・初期表示でApp.vueが表示されます。
App.vue構成はヘッダー　メニューバー領域・ダッシュボード領域です。

・ヘッダー領域は左側にはダッシュボードの文字が　右側には・アイコン　ログインボタン
メニューバー領域は　空白です。
→　領域ごとにVue.jsで切り分けてください。コードを示してください。

上のコードを利用して以下の機能を追加してください。
ヘッダー領域にはログインボタンがあります。ログインボタンを押下します。
成功したらメニューバーに成功というボタンを表示してください。


App.vueファイルの更新:
```javascript
<template>
  <div>
    <Header @login-success="showSuccessButton" />
    <MenuBar :showSuccessButton="successButtonVisible" />
    <Dashboard />
  </div>
</template>

<script>
import Header from './components/Header.vue';
import MenuBar from './components/MenuBar.vue';
import Dashboard from './components/Dashboard.vue';

export default {
  name: 'App',
  components: {
    Header,
    MenuBar,
    Dashboard
  },
  data() {
    return {
      successButtonVisible: false
    };
  },
  methods: {
    showSuccessButton() {
      this.successButtonVisible = true;
    }
  }
};
</script>
```
Header.vueファイルの更新:
```javascript
<template>
  <header>
    <div class="left-section">
      <span>ダッシュボード</span>
    </div>
    <div class="right-section">
      <i class="icon"></i>
      <button v-if="!loggedIn" @click="login">ログイン</button>
    </div>
  </header>
</template>

<script>
export default {
  name: 'Header',
  data() {
    return {
      loggedIn: false
    };
  },
  methods: {
    login() {
      // ログイン処理（成功時に親コンポーネントに通知）
      this.loggedIn = true;
      this.$emit('login-success');
    }
  }
};
</script>
```
MenuBar.vueファイルの更新:
```javascript

<template>
  <div>
    <button v-if="showSuccessButton">成功</button>
  </div>
</template>

<script>
export default {
  name: 'MenuBar',
  props: {
    showSuccessButton: {
      type: Boolean,
      default: false
    }
  }
};
</script>
```
RailsのViewでHTMLタグとERBコードを同時にコメントアウトする方法について考えた話

## 概要
RailsのViewでは、主にHTMLタグとERBコードを同時に使用することがあると思います。これら2つの記法には、それぞれ別々のコメントアウト方法があり、どのようにコメントアウトすれば良いか悩んでおりました。そこで、それぞれのコメントアウト方法とそれらを同時にコメントアウトする方法を、私なりに考え、それらを下記に記述させて頂きます。

## HTMLのコメントアウト
まずは、基本のHTMLでコメントアウトするには、以下のように`<!--`と`-->`を使用します。
```html
<!-- これはコメントです -->
```

この書き方で、`<!--`と`-->`の間にあるすべての内容がコメントアウトされます。

## ERBのコメントアウト

一方、ERBのコードをコメントアウトするには、以下のように`<%#`と`%>`を使用します。

```erb
<%# これはコメントです %>
```

この書き方で、`<%#`と`%>`の間にあるERBコードがコメントアウトされます。

## 本題: HTMLタグとERBコードをまとめてコメントアウトする
等々本題ですが、HTMLタグとERBコードをまとめてコメントアウトする方法は、先述させて頂いた内容では、解決致しません。上記のHTMLのコメントはHTMLに、ERBのコメントはRubyコードにしか影響しないからです。

それでも、まとめて複数行をコメントアウトしたい場合、以下のような方法ができないかと考えました。

```erb
<%
=begin
%>
<!-- 
<div>
  <p>This is some HTML</p>
  <%= "And this is some Ruby code" %>
</div>
-->
<%
=end
%>
```

上記の例では、まずERBのコメントでRubyの複数行コメント（`=begin`と`=end`）を開始してからHTMLのコメントを開始します。そして、HTMLのコメントを終了した後にERBのコメントを終了しています。この方法でHTMLとERBのコードを一緒にコメントアウトすることができます。

しかし、この方法は少々面倒で、一般的にはHTMLとERBをそれぞれ個別にコメントアウトする方がより簡単でわかりやすいとされているようです。

## まとめ
以上をもとに予期せぬエラーを防ぐためにも、それぞれ個別にコメントアウトすることが推奨されます。若干やりづらさを感じるかもしれませんが止む無しです。
【Mac】launchedの使用方法

## 設定方法

用途ごとに設定ファイルを以下の場所に保存します。
| 場所                                   | 用途                                           |
| :------------------------------------- | :--------------------------------------------- |
| `~/Library/LaunchAgents/<Label>.plist` | ユーザー単位のエージェント                     |
| `/Library/LaunchAgents/<Label>.plist`  | 管理者が提供するユーザーごとのエージェント     |
| `/Library/LaunchDaemons/<Label>.plist` | 管理者によって提供されるシステム全体のデーモン |

以下のコマンドを実行すると定期実行されるようになります。

```zsh:ターミナル
launchctl load /path/to/your.plist
```

以下のコマンドを実行すると定期実行されなくなります。

```zsh:ターミナル
launchctl unload /path/to/your.plist
```

## サンプル

### 30秒ごとにsample.shを実行

```xml:~/Library/LaunchAgents/sample.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key>
  <string>sample</string>
  <key>Program</key>
  <string>/Users/mofu/script/sample.sh</string>
  <key>StartInterval</key>
  <integer>10</integer>
  <key>StandardOutPath</key>
  <string>/Users/user_name/script/sample.out</string>
  <key>StandardErrorPath</key>
  <string>/Users/user_name/script/sample.err</string>
</dict>
</plist>
```

### 毎日3時にsample.shを実行

```xml:~/Library/LaunchAgents/sample.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key>
  <string>sample</string>
  <key>Program</key>
  <string>/Users/user_name/script/sample.sh</string>
  <key>StartCalendarInterval</key>
  <dict>
      <key>Minute</key>
      <integer>0</integer>
      <key>Hour</key>
      <integer>3</integer>
  </dict>
  <key>StandardOutPath</key>
  <string>/Users/user_name/script/sample.out</string>
  <key>StandardErrorPath</key>
  <string>/Users/user_name/script/sample.err</string>
</dict>
</plist>
```
【SQL】SELECT文の基本的な使い方

# はじめに
SELECTはテーブルからデータを取得するときに使います。
その基本的な使い方についてご説明します。

# 例として使用するテーブル
今回の例として以下のmembersテーブルをご用意しました。
**membersテーブル**
|id|name|age|team_number|
|---|---|---|---|
|1|hitomi|39|2|
|2|nibe|25|1|
|3|mikuni|21|3|
|4|yosoya|31|2|
|5|ikarugi|42|1|
|6|mutsuki|29|3|

# SELECTの基本構文
最も基本となる構文は以下の通りです。
```.sql
SELECT (カラム名) FROM (テーブル名);
```
今回の例では(カラム名) は「id」「name」「age」「team_number」、 (テーブル名)は「members」です。
試しにnameを抽出してみましょう。
```sample.sql
SELECT name FROM members;
```
出力結果は以下のようになります。
|name|
|---|
|hitomi|
|nibe|
|mikuni|
|yosoya|
|ikarugi|
|mutsuki|

## 「,」で複数指定
カラム名の間に「,」を入れることで複数項目を指定できます。
```sample.sql
SELECT name,age FROM members;
```
出力結果は以下のようになります。
|name|age|
|---|---|
|hitomi|39|
|nibe|25|
|mikuni|21|
|yosoya|31|
|ikarugi|42|
|mutsuki|29|

## 「*」で全指定
カラム名の代わりに「*」を入れることですべてのカラムを取得します。
```sample.sql
SELECT * FROM members;
```
出力結果は以下のようになります。
|id|name|age|team_number|
|---|---|---|---|
|1|hitomi|39|2|
|2|nibe|25|1|
|3|mikuni|21|3|
|4|yosoya|31|2|
|5|ikarugi|42|1|
|6|mutsuki|29|3|

## 「WHERE」で条件を設定
先ほどの基本構文の後ろに「WHERE (条件文)」を挿入することで、条件に一致するデータのみを取得できます。
```.sql
SELECT (カラム名) FROM (テーブル名) WHERE (条件文);
```
試しに30歳以上のメンバーを取得してみます。
```sample.sql
SELECT * FROM members WHERE age >= 30;
```
出力結果は以下のようになります。
|id|name|age|team_number|
|---|---|---|---|
|1|hitomi|39|2|
|4|yosoya|31|2|
|5|ikarugi|42|1|

比較によく使う記号は以下の通りです。
|記号| 条件|
|---|---|
|=|等しい|
|<|小さい|
|>|大きい|
|<=|以下|
|>=|以上|
|!=|等しくない|

# 「ORDER BY」で並べ替え
基本構文の後ろに「ORDER BY (カラム名) [ASCまたはDESC]」を挿入することで、(カラム名)の値に基づき、昇順または降順でデータを並べ替えできます。
「ASC」で昇順(小さい方から)、「DESC」で降順(大きい方から)になります。「ASC」は省略できます。
```.sql
SELECT (カラム名) FROM (テーブル名) ORDER BY (カラム名) ASCまたはDESC;
```

メンバーをチーム番号順で昇順に並べ替えてみます。(ASC省略)
```sample.sql
SELECT * FROM members ORDER BY team_number;
```
出力結果は以下のようになります。
|id|name|age|team_number|
|---|---|---|---|
|2|nibe|25|1|
|5|ikarugi|42|1|
|1|hitomi|39|2|
|4|yosoya|31|2|
|3|mikuni|21|3|
|6|mutsuki|29|3|

チーム番号の小さい方から順になっているのがわかります。

# 「WHERE」と「ORDER_BY」の組み合わせ
「WHERE」と「ORDER BY」を組み合わる場合、以下のように書くことができます。
SQL文が長い場合、読みやすいように改行やインデントを入れるのが通例となっています。
```.sql
  SELECT (カラム名)
    FROM (テーブル名) 
   WHERE (条件文)
ORDER BY (カラム名) ASCまたはDESC;
```
以下は「members」テーブルから「team_number」が2でないものすべて（*）を「age」で降順（DESC）で並べ替えて取り出すSQL文です。
```sample.sql
  SELECT *
    FROM members 
   WHERE team_number != 2
ORDER BY age DESC;
```
出力結果は以下のようになります。
|id|name|age|team_number|
|---|---|---|---|
|5|ikarugi|42|1|
|6|mutsuki|29|3|
|2|nibe|25|1|
|3|mikuni|21|3|
2023/6/14主にITとかセキュリティの記事

再び「MOVEit Transfer」に深刻な脆弱性 - CVE番号は採番待ち
https://www.security-next.com/146892

アバントグループ子会社でランサム被害 - サービスは再開へ
https://www.security-next.com/146834

自動車保険料の請求に見せかけたフィッシング
https://www.security-next.com/146913

「水道料金滞納」「給水停止」と不安煽る水道局装うSMS
https://www.security-next.com/146866

社労士向け業務システム「社労夢」がランサム被害、利用不能1週間も復旧めど立たず(6/12)
https://xtech.nikkei.com/atcl/nxt/news/18/15394/

BlackBerry、2023年第１四半期版 脅威インテリジェンスレポートの日本語版を公開銀行、医療機関、食品小売業者がサイバー犯罪の主な標的であることが判明(6/12)
https://prtimes.jp/main/html/rd/p/000000038.000060255.html

中小企業に最新サイバー防衛策　GMO系、機能絞り安く(6/12)
https://www.nikkei.com/article/DGXZQOUC145P70U2A211C2000000/

敵対的プロンプトによる疑似攻撃で大規模言語モデルのセキュリティリスクを可視化─GMOイエラエ(6/12)
https://it.impress.co.jp/articles/-/24940

5月のフィッシング、報告件数が過去最高記録 - ファミペイの悪用が最多(6/12)
https://news.mynavi.jp/techplus/article/20230612-2699058/

怖いのは改ざんに気付かず結果を盲信--デルCTOが語るAIのセキュリティリスク(6/12)
https://japan.zdnet.com/article/35204842/

ランサムウェア攻撃の93％でバックアップデータの破壊が試みられる、Veeam調査(6/12)
https://news.nicovideo.jp/watch/nw12739975

元SECインターネット執行部責任者、「暗号資産プラットフォームから今すぐ撤退せよ」と警告(6/12)
https://www.neweconomy.jp/posts/318862

トヨタ紡織の欧州子会社で不正アクセス、一部のデータを盗まれる(6/12)
https://xtech.nikkei.com/atcl/nxt/news/18/15389/

ソーシャルエンジニアリング攻撃の頻度が飛躍的に増加か──ベライゾン・ビジネス調査(6/12)
https://enterprisezine.jp/news/detail/17874

ASUSのWi-Fiルーター「RT-AX3000」にSecure属性なしのCookieを使用する脆弱性、対策ファームウェアへの更新を(6/12)
https://news.yahoo.co.jp/articles/04292210e2b33f7c1d7054bde56864fbed248682

ランサムウェアの新手口「マルチターゲット攻撃」が増加中(6/12)
https://news.mynavi.jp/techplus/article/20230612-2701983/

スマホのマルウェア感染が疑われる3つの兆候(6/12)
https://japan.zdnet.com/article/35204820/

分散型金融（Defi）の仕組みや特徴・現状と今後活用される領域とは(6/12)
https://the-owner.jp/archives/14665

教育機関へサイバー攻撃拡大中、原因は「予算の制限」「人材不足」「古い機器」(6/12)
https://news.mynavi.jp/techplus/article/20230612-2698040/

Facebookフィッシング詐欺、Google Cloud Runが悪用される新手法(6/12)
https://news.mynavi.jp/techplus/article/20230612-2699500/

5月マルウェアランキング、4位のダウンローダ「GuLoader」に注意(6/12)
https://news.mynavi.jp/techplus/article/20230612-2701490/

鹿児島県　５３件の公文書データで個人情報約４万人分が閲覧できる状態に　県「適正な管理徹底したい」(6/12)
https://news.yahoo.co.jp/articles/795184eb7753d27e4ba3b8749685ee400f8e92a4

「国会議員カード」を作れ！ トラブル続出のマイナカードへ、国民から強烈な皮肉(6/12)
https://news.yahoo.co.jp/articles/a8106cec13deeda2ef44d71a3f093c143db579fc?source=rss

「チャットＧＰＴ」新潟市で利用開始　業務の効率化を目指して試験的に≪新潟≫(6/12)
https://news.yahoo.co.jp/articles/d90c5353f86b49c69f4bff442704cc8ff716ef48?source=rss

１０秒で１０個のアイデア！“チャットＧＰＴ”試行利用開始「自分では思いつかない回答が…」【新潟市】(6/12)
https://news.yahoo.co.jp/articles/6a8b224b6971e4fd466630af0f848d49035dae37?source=rss

日本を飛び出しタイで起業した狙いは？電子決済ユニコーンOPNの世界戦略を直撃(6/13)
https://xtech.nikkei.com/atcl/nxt/column/18/00677/060100137/

「情報窃取した」と脆弱性が見つかったMOVEit利用企業を脅迫、6月14日が期限(6/13)
https://xtech.nikkei.com/atcl/nxt/column/18/00001/08112/

ゼロトラストの第一歩はパソコンから、進化するセキュリティー保護専用ハード(6/13)
https://xtech.nikkei.com/atcl/nxt/column/18/02491/060900002/

Officeソフトを使うならサブスクかパッケージか、使えるサービスとサポートに大きな差(6/13)
https://xtech.nikkei.com/atcl/nxt/column/18/02467/052500001/

東京都に派遣された人物が「HER-SYS」から個人情報持ち出し、LINEで外部に送信(6/13)
https://xtech.nikkei.com/atcl/nxt/column/18/00598/021300220/
インフラ初心者がUbuntuのVPSを立ててみた話

# この記事について
この記事はインフラ初心者がWebページをVPS上で公開したい！って思ってやったことを備忘録として書き残したものです。素人が書いているため情報の信憑性は保証しません。詳細は各自で調べながらやることをおすすめします。記事のとおりにやっておかしなことになっても責任は取れませんのでご了承ください。

# VPSを立てる

筆者は[KAGOYA Cloud VPS](https://vps.kagoya.com)を使いました。日本サーバーで月550円〜という安さ！他の会社のVPSを立てる場合は「SSH接続する」のセクションまで飛ばしてOK

**CPU**
1コア
**メモリ**
1GB
**SSD**
25GB
**価格**
月額上限550円


私が申し込んだときは、Kagoyaから送られてくる確認メールに返信して登録完了になりました。メール要チェック！

申込みが完了したらマイページが作られます。

以降のセットアップは基本的にKagoyaの[マニュアル](https://support.kagoya.jp/vps/index.html)に従って行っていきます。

## セキュリティグループを設定
「セキュリティ」タブの「セキュリティグループを追加」から追加します。

SSH接続のための20/tcpポートは自宅からアクセスできれば十分だと考え、自宅のアドレスだけにしました。

Linux, Macの場合は`curl inet-ip.info`でグローバルIPアドレスが確認できるはず。

20ポートの他にHTTP接続のための80/tcp、HTTPSのための443/tcpは任意のIPからアクセスできるようにします。

## ログイン認証用キーを追加
サーバーにSSH接続するのに必要です。大切に保管してください。

## インスタンスの作成
「インスタンス」タブの「インスタンス作成」から追加します。

OSは`Ubuntu22.04LTS`、スペックは一番安いやつにしました。
先程作ったログイン用認証キーを指定し、インスタンス作成！

# SSH接続する


## 流れ
1. rootでログイン
1. 一般ユーザーを作成
1. root権限を作成したアカウントに与える
1. 作成したアカウントにSSH接続できるようにする

## rootでログイン

参考:
https://support.kagoya.jp/vps/manual/index.php?action=artikel&cat=25&id=9&artlang=ja
https://support.kagoya.jp/vps/manual/index.php?action=artikel&cat=30&id=113&artlang=ja

ダウンロードした秘密鍵(kagoyaの場合はログイン認証キー)を用意しておいてください。

ローカルPCのシェルで以下のコマンドを実行します。IPアドレスはインスタンス一覧ページから確認できます。

まず、ダウンロードした秘密鍵の権限を変更します。これをしないとSSH認証で弾かれた。
```
chmod 400 {秘密鍵のパス}
```
これを実行した後に
```
ssh -i {秘密鍵のパス} root@{インスタンスのIPアドレス}
```

するとサーバーのシェルにログインできるはずです。

## 一般ユーザーを作成
rootで作業をするのは危険なので、一般ユーザーを作成して普段はそっちで作業することにします。

参考: AmazonLinux2で新しいユーザを作成してec2-userを削除する
https://itneko.com/ec2-user-delete/

下記のコマンドでユーザーを作成します。
**ここでパスワードを設定することになりますが、パスワードは必ず複雑なものを使用してください。**
あとでSSHログインは公開鍵認証でしかできないようにするのですが、現時点ではパスワードでもログインできてしまうため、簡単なパスワードだと不正ログインされます。(←本当は先にSSHでパスワード認証できないようにすべき。)

```
adduser {アカウント名}
```

## root権限の付与
`visudo`を実行すると多分nanoエディタが開く。下の方に行くと
```
root ALL=(ALL) ALL
```
みたいなのが書かれてるところが見つかると思うのでその下に以下の文を付け足す。
```
{アカウント名} ALL=(ALL) ALL

例) kotaro ALL=(ALL) ALL
```
これで一般ユーザーでもsudoをつけてコマンド実行すれば管理者権限で処理できるようになる。

## 作成したアカウントでSSH接続できるようにする
`sudo su {アカウント名}`で作成したアカウントにログインできます。
ここにSSH関連の設定をしていきます。

まず、ローカルのPCで公開鍵を作成します。

ローカルPCで以下を実行。
```
ssh-keygen -f ~/.ssh/{SSHキーのファイル名}
```
パスワードを設定しておくと安全。

作成した公開鍵の内容をコピーしておきましょう。
```
cat ~/.ssh/{SSHキーのファイル名}.pub
```

次にサーバー側(一般ユーザーアカウントにログインした状態)で
```
mkdir ~/.ssh
vi ~/.ssh/authorized_keys
```
を実行し、先程コピーした公開鍵の内容をペーストします。viエディタは`:wq`と入力してEnterを押すとセーブして終了できます。(`w`が書き込み、`q`が終了)


## パスワードでログインできないようにする
現状だとパスワードが漏洩すると悪意のある第三者がSSH接続できてしまいます。なのでパスワードではそもそもログインできなくします。

参考: https://atmarkit.itmedia.co.jp/flinux/rensai/linuxtips/430dnypsswdacces.html

サーバー側にログインした状態で
```
vi /etc/ssh/sshd_config
```
ファイルの真ん中あたりに`PasswordAuthentication`と書かれた部分があるので
```
PasswordAuthentication no
```
とします。ついでにrootでもログインできなくします。
```
PermitRootLogin no
```
> 一般にはrootは削除したほうが良いと思います。私の場合はrootを消そうとしたら他のプロセスで使われてるって言われて消せなかった。

# おわり
これでSSH関連の設定は終了です。`ufw`とかでファイアウォールを適宜設定した方がいいと思うのでそれは各自でお願いします。

# 参考文献
カゴヤ・ジャパン サポートセンター
https://support.kagoya.jp/vps/index.html

AmazonLinux2で新しいユーザを作成してec2-userを削除する
https://itneko.com/ec2-user-delete/
Rust のテストのために DI は必要か？

[yuki](https://twitter.com/helloyuki_) さんの下記の記事を読みました。

https://techblog.paild.co.jp/entry/2023/06/12/170637

DI の実現方法について、たくさんの用法を示していただき、大変勉強になりました。
（モナドが出てくるとは...）

さて、私自身も最初のころ静的ディスパッチによる実装を導入していましたが、今はもっと単純な実装をするようになったので、紹介しておきます。

## 条件付きコンパイルによる実装
簡単に言うと、以下のようなアプローチです。

```rust

struct UserRepositoryImpl{
    db: Database
};

impl UserRepositoryImpl {
    pub fn find_user(&self, id: String) -> Result<Option<User>> 
    {
        // 本番用の実装
    }
}

struct UserRepositoryMock {
    data: Vec<Result<Option<User>>>
}

impl UserRepositoryMock {
    pub fn find_user(&self, id: String) -> Result<Option<User>> 
    {
        // テストの実装
    }

    pub fn insert_user(&mut self, Result<Option<User>>) {
        // テスト用のヘルパーメソッド。
    }
}

// テストと本番で切り替え
#[cfg(not(test))]
type UserRepository = UserRepositoryImpl;
#[cfg(test)]
type UserRepository = UserRepositoryMock;


// 利用は UserRepository を使う。
struct AppState {
    user_repository: UserRepository
}
```

trait によるインターフェースの定義もせず、Rust のコンパイラの力に頼り切った（cargo check/test がそれぞれ通れば良い）実装ですが、とてもシンプルな実装方法です。

## 前提
念のために言いますが、元の記事で DI を挙げているのは、テストの目的以外も含めているのかもしれず、上のアイデアは記事のもともとの主題から少しずれています。
（DI にはテスト以外の用途にも対応できる柔軟性がありますが、多くのプロジェクトはテストのためだけに DI を利用している印象があります。）

本番/テストの2種類をただ切り分けるという目的だけなら、上のような単純な実装で割り切っても良いのかなと思いました。

## 最後に
複雑なプロジェクトでは本番とテストの2種類の切り替えだけでは不足するかもしれませんが、私のいくつかのプロジェクトではこれで十分でした。

一口に DI といってもさまざまな実装方法があることがわかるので、ぜひ [yuki さんの記事](https://techblog.paild.co.jp/entry/2023/06/12/170637) をご覧になってください。
PHP/Laravelで自分で用意したPDFに文字を入力する方法

タイトルにある通り
PHP/LaravelでテンプレPDFに文字を書き込んでいく方法に難航したため保存しておきます。

まず、必要なライブラリがあるため以下手順でインストールしていきます

TCPDFのインストール　⇒　FPDIをインストール


# 1.TCPDFインストール

gitから「Download zip」でzipファイルをダウンロード

https://github.com/tecnickcom/tcpdf

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2361500/133952dd-4a16-1a4c-0364-a748534099d5.png)

# 2.FPDIインストール

以下公式サイトからzipファイルをダウンロード

https://www.setasign.com/products/fpdi/downloads/

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2361500/665622ae-2cbc-7d5c-0df7-7f2fd0622656.png)

# 3.手順1.2でダウンロードしたファイルを解凍しまとめる

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2361500/0001f6f7-a1fc-4dce-f333-92b1fe7538b4.png)

# 4.テンプレートになるPDFを格納する

適当にネットとかで拾ってきたPDFを、今回はtemplateフォルダ作成して格納

すると、以下のようなフォルダ構成になると思います（若干いらないファイル削除しました）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2361500/754f7b6c-4e5c-f0fb-de73-b7323292d7ce.png)


# 5.PDF出力のソースコードを書く

以下のように記載していきます

```
<?php

namespace App\Http\Controllers;

use setasign\Fpdi\Tcpdf\Fpdi;

class OutResumePdfController extends Controller
{
    //
    function pdfResume()
    {
        //読み込むパスは個人で変えてください
        require_once(__DIR__.'/../../lib/tcpdf/tcpdf.php');
        require_once(__DIR__.'/../../lib/tcpdf/fpdi/autoload.php');
        
        $pdf = new Fpdi();
        $pdf->setPrintHeader( false );

        //テンプレートPDFの読み込み
        $pdf->setSourceFile(__DIR__."/../../lib/tcpdf/template/resume_template1.pdf");

        //PDFのサイズなどを設定（LANDSCAPE（横）にA3サイズ）
        $pdf->AddPage('LANDSCAPE','A3');
        $tpl = $pdf->importPage(1);
        $pdf->useTemplate($tpl);

        //書き込む文字のフォント・サイズを設定
        $pdf->SetFont('kozminproregular', '', 20);

        //書き込む文字の座標と文字を設定
        $pdf->Text(40, 50, "テスト太郎");

        //PDFの出力
        $pdf->Output("resume.pdf", "I");
    }
}
```

細かい仕様はドキュメントを確認してください
公式ドキュメントか分からないですが、ここを参考にしました

http://tcpdf.penlabo.net/

文字の座標などは、adobeAcrobatReaderなどを使うとわかりやすいかもしれません
テンプレPDFのサイズなどもこのソフトで確認出来ます。

https://get.adobe.com/jp/reader/?promoid=TTGWL47M

# 6.実際に実行してみる

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2361500/22395f13-f393-dd09-dc67-0a23a8a068e4.png)


文字が出力されました

プラスαでフォントの変更や画像埋め込みをしたい場合は以下サイトを参考にすることで実装出来ました

https://xiyuan.jp/php/427/

https://office-obata.com/report/memorandum/php/post-5292/
20分でまとめる備忘録　RobloxでのgamePad入力の取り方

## 備忘録のきっかけ
ゲームパッド接続時、今までキーボード入力のみ対応した箇所をパッド入力で置き換えないといけなかったので使い方を調べた

## コードサンプル
```Lua
local UserInputService = game:GetService("UserInputService")

UserInputService.InputBegan:Connect(function(input: InputObject, gameProcessed)
	if input.UserInputType == Enum.UserInputType.Keyboard then
		print("A key is being pushed down! Key:", input.KeyCode)
	elseif input.UserInputType == Enum.UserInputType.MouseButton1 then
		print("The left mouse button has been pressed down at", input.Position)
	elseif input.UserInputType == Enum.UserInputType.MouseButton2 then
		print("The right mouse button has been pressed down at", input.Position)
	elseif input.UserInputType == Enum.UserInputType.Touch then
		print("A touchscreen input has started at", input.Position)
	elseif input.UserInputType == Enum.UserInputType.Gamepad1 then
		print("A button is being pressed on a gamepad! Button:", input.KeyCode)
	elseif input.UserInputType == Enum.UserInputType.Gamepad2 then
		print("A button is being pressed on a gamepad! Button:", input.KeyCode)
	end

	if gameProcessed then
		print("The game engine internally observed this input!")
	else
		print("The game engine did not internally observe this input!")
	end
end)
```
[input:InputObject](https://create.roblox.com/docs/reference/engine/classes/InputObject) 入力されたキーコードや、マウス・パッドなどの情報が取れるのでそれ経由で判定する。注意点として、UserInputTypeのGamepadにはGamePad1~8まであるので、Gamepad1固定で判定してしまうと複数台繋がれた時に反応しないように見えるケースが発生する

## おわりに
ボタンやキーにゅ力の場合はUserInputService.InputBeganかInputEndを使い、スティックなどの入力の場合はUserInputService.InputChangedを使いわけて取得することになりそう
Debian bookworm以降は無線LANを使うときに`crda`パッケージが要らなくなった

## 要約
* 無線LANを使うなら`sudo apt install wireless-regdb`しよう
  * `crda`パッケージはDebian bookwormから存在しないよ

## 理由
先日Debian 12 (コードネーム:bookworm) が[リリースされた](https://www.debian.org/News/2023/20230610)ことにより多くのパッケージが更新されました。`platform regulatory.0: Direct firmware load for regulatory.db failed with error -2`というエラーでGoogle検索すると[USB-GbEドングルのドライバロード時のエラー](http://bam-system.ddo.jp/wordpress/ura/2022/03/21/usb-gbe%E3%83%89%E3%83%B3%E3%82%B0%E3%83%AB%E3%81%AE%E3%83%89%E3%83%A9%E3%82%A4%E3%83%90%E3%83%AD%E3%83%BC%E3%83%89%E6%99%82%E3%81%AE%E3%82%A8%E3%83%A9%E3%83%BC/)という記事が一番上に出てきますが、当該の[`crda`](https://packages.debian.org/bullseye/crda)パッケージは存在しません。`crda`は`wireless-regdb`パッケージが持つデータをLinuxカーネルへ送るために使われていましたが、Debian 11以降で採用しているLinuxカーネルは (`crda`のサポートも続けているものの) [カーネル自身がデータを読み込めるようになったため](https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1003903)、必要ないと判断され、testing (=現在のbookworm) から[消された](https://ftp-master.debian.org/removals-2022.txt)ためにbookwormに存在しません。

よって、おそらく安全に`wireless-regdb`パッケージに置き換えることが出来ます。
Sambaの設定ファイル「smb.conf」の[global]セクションに記述するWinbind関連の主な設定項目

●Sambaの設定ファイル「smb.conf」の[global]セクションに記述するWinbind関連の主な設定項目

・idmap config * : backend=バックエンド　ldmap機構で使用するバックエンドの指定(tdb,ldapなど)

・idmap config * :range=最小UID・GID - 最大UID・GID　Linuxユーザに割り当てるUID・GIDの範囲の指定
Salesforce VisualforcePageで外部CSS, JSファイルを読み込む方法

# Salesforce VisualforceでカスタムなCSSファイル, JSファイルを読み込む方法
<!-- 発端や概要を記載 -->

# 目次
<!-- タイトルとアンカー名を編集 -->
1. [前提条件](#Chapter1)
1. [Step1](#Chapter2)

<!-- 各チャプター -->
<a id="#Chapter1"></a>
# 前提条件
・開発手法：VScode

<a id="#Chapter2"></a>
# Step1（完結）
VScodeで「Visualforce Page」を新規作成
適当にCSS, JSファイルを作成してSalesforceの設定にある「静的リソース」から各ファイルをアップロードしておく
静的ファイル(CSS, JS)の名称を読み込むことでSFに反映できる

```javascript: visualforce
<apex:page showHeader="false">
    <!-- import css file -->
    <apex:stylesheet value="{!$Resource.CustomCSS}" />
    <!-- import JavaScript file -->
    <apex:includeScript value="{!$Resource.CustomJS}"/>

    <h1>Hello World!!</h1>

    <button onclick="sample()">Click here!</button>

</apex:page>
                                        
```



```css: CustomCSS
h1 {
    color: red;
    }
```


```javascript: CustomJS
function sample() {
    alert('Hello!!');
}
```

SF組織にデプロイして、CSS, JSが反映されていればOK！
公式も外部ファイルを用意して、コールさせるのがベストプラクティスっぽい
だいたいが「同じファイル内にCSS書いていくパターン」or「SFのDesign System」で対応するやり方しか載ってなかったので、参考になれば幸いです🙌
※サンプル用に適当にソース書いているので、コピペじゃ動かない可能性ありなのはご了承ください〜
※CSS, JS読み込む部分は公式から引用してるので問題ないよ！
Cloudflare D1の奥地へ。D1は、何が美味しいの？

# 概要

去年の5月に発表された Cloudflare D1。去年11月の段階でオープンアルファ版として使用できるようになっています。
今回は、そんな D1 についてどんなものなのかをまとめていきます。

https://blog.cloudflare.com/ja-jp/introducing-d1-ja-jp/

## Cloudflare D1 とは

* D1 とは、CDN のエッジロケーション上で動く Cloudflare のエッジデータベースサービスです。
* Cloudflare には AWS でいうところのリージョンの概念がなく、285以上のエッジがマスターとなります。
* このマスターたちでマルチクラスター構成をとり、リレーショナルデータベースを作ることができるサービスなのです。
* ちなみに、Cloudflare が提供している Cloudflare Workers や Cloudflare R2 などのサービスも同じように分散されたエッジロケーション上で実行されています。
* 利用方法としては、Cloudflare Workers や pages からクエリを実行することができます。
* ちなみに、実体としてはエッジロケーション上で分散して動く、SQLite によるデータベースサービスです。


## Cloudflare D1 の面白いところ

* どうやら Cloudflare によると、D1の目的は Cloudflare Workers とシームレスに統合することにありそうだ。
* Workers に SQL をサーバレスの形で組み合わせることができることに旨みを感じている。
* どのように動くのか。それは、世界中のエッジロケーションを利用し、ユーザの近くにデータベースの読み取り専用クローン(Read Replicas)を作成し、変更が発生した時に全てのクローンに同期するような動きとなるようです。
* ただ、現時点では Cloudflare はこのレプリケーションの仕組みがどのように機能するかについて、まだ詳細を発表していません。

なので一番美味しいところとしては、世界中のエッジロケーションにより、どこからアクセスしたとしても低レイテンシーで高速なデータベースアクセスを可能にすることを目指しているところなのかもしれません。

ちなみに、このレプリケーションについて、Cloudflare のエンジニア greg-m が Hacker News で書き込んでおり、リードレプリカのない D1 は強い一貫性を持っているが、リードレプリカを追加すると、レプリケーションの遅延が発生する可能性があり、強い一貫性が失われてしまうとのことです。

> Just clarifying - D1 without read replicas is strongly consistent. If you add read replicas, those can have replication lag and will not be strongly consistent.
Disclaimer: I work at Cloudflare :)

https://news.ycombinator.com/item?id=31340942

## D1 の主な特徴
### リードレプリケーション

* 先ほども触れたように、世界中のエッジロケーションを利用し、ユーザの近くにデータベースの読み取り専用クローン(Read Replicas)を作成し、変更が発生した時に全てのクローンに同期するようなレプリケーションを行います。

### バッチ処理

* D1 の API にはバッチ処理機能があります。単一の SQL を配列で送ることができます。
* これにより、1回のHTTP ラウンドトリップで複数の処理を実行することができます。下記が js での一例になります。

```js
async function recordPurchase(userId, productId, amount) { 
  const result = await env.DB.exec([
    [
      `UPDATE users SET balance = balance - $amount WHERE user_id = $user_id`,
      { $amount: amount, $user_id: userId },
    ],
    [
      'UPDATE product SET total_sales = total_sales + $amount WHERE product_id = $product_id',
      { $amount: amount, $product_id: productId },
    ],
  ])
  return result
}
```

### バックアップと冗長性

* D1 では、Cloudflare のクラウドストレージサービスである R2 にデータベースのスナップショットを定期的に自動保存して、1クリックで復元できるようにしています。

### データのインポートとエクスポート

* 他のソースやデータベースから気温のデータセットをインポートしたい場合、D1 ではデータのインポート/エクスポート機能ができるように取り組んでいます。
* これにより、例えば、アプリケーションの検証環境があれば、そのデータのスナップショットをローカルマシンに複製し、それに基づいて開発を行うことができます。

## オープンアルファという位置付け

* 現時点(2023/6/13)では、オープンアルファ版で提供されています。
* そのため、商用環境での利用が推奨されていない上に、API 仕様も変更になる恐れがあります。
* ちなみに、オープンアルファという概念は、Cloudflare にとって初めての概念だそうです。
* オープンβという表現ではなく、オープンアルファという表現にしたのは、まだ開発とテスト中の重要な部分があるため、実際のアプリの構築を開始できる完成した D1 をパブリックベータ版としてリリースしたいからだそうです。


# まとめ

* CDNのエッジロケーション上で動き、マスターとリードレプリカのレプリケーションのレイテンシーが下がればかなりやばいサービスなのは間違いない。
* そんな D1 のオープンベータ版公開を心待ちにしております。


# 参考文献

https://developers.cloudflare.com/d1/

https://dev.classmethod.jp/articles/cloudflare-d1-start/

https://blog.cloudflare.com/ja-jp/introducing-d1-ja-jp/

https://zenn.dev/kameoncloud/articles/6264967e5fd1da

https://www.infoq.com/jp/news/2022/06/cloudflare-d1-sqlite-workers/

https://news.ycombinator.com/item?id=31340942
Rspecの編集機能の結合テストにて投稿内容がフォーム内に入力されてるか確認する際、ActiveHashの選択肢を用いている場合の記述方法

## 経緯
洋服投稿のオリジナルアプリの編集機能の結合テストを作成中、ActiveHashを用いた選択肢のテスト方法につまづいたため備忘録として記載します。

### 編集前(誤った記述方法)
基本的な記述方法は通常のフォーム内容と同様で、findに要素を指定し、eqに選択肢のIDを取得するよう記述した。
```ruby
expect(
  find('要素').value
).to eq (選択肢のid)
```
```ruby
expect(
  find('#garment_genre_id').value
).to eq (@garment1.genre_id)
```
しかしこのままだとターミナル上で以下のエラーが出てしまう。
![スクリーンショット 2023-06-14 0.29.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3349201/b440e5ef-abd9-9aea-06e3-ebadb16b06d2.png)

内容的に、`expected: 2, got: "2"`と数値の2を期待したところ文字列としての"2"を取得してしまっているため、不一致となってしまっていると考えられる。

### 編集後
上記の異なったIDを一致させるためにeq下の記述を式展開を用いつつ、文字列として期待できるように記述する。

```ruby
expect(
 find('#garment_genre_id').value
).to eq "#{@garment1.genre_id}"
```

これで問題なくテストは成功しました。
テキストと選択リストの複合input

選択リスト（comboBox)とテキスト入力（input）の融合です。
テキストも、選択リストどっちからでも入力できないかなと思って調べてみました。
どうやらlightning-inputでは無理そう？なので
HTMLにsldsのclassを割り当てて、それっぽくしましょう。
```html:inputAndCombobox.html
<input id="input" name="input" list="valueList" class="slds-input" type="text">
<datalist id="valueList">
    <template for:each={values} for:item='item'>
        <option key={item.key} value={item.value}>{item.value}</option>
    </template>
</datalist>
```
```javascript:inputAndCombobox.js
import { LightningElement, api } from 'lwc';

export default class inputAndCombobox extends LightningElement {
    const values = [
      { label: 'Option 1', value: 'option1' },
      { label: 'Option 2', value: 'option2' },
      { label: 'Option 3', value: 'option3' }
    ];
    initialized = false;
    
    renderedCallback() {
        if (this.initialized) {
            return;
        }
        this.initialized = true;
        // inputとdatalistを融合
        let listId = this.template.querySelector('datalist').id;
        this.template.querySelector("input").setAttribute("list", listId);
    }
}
```
こんな感じですね。
inputのclassに'slds-input'を割り当ててますが、レイアウトが少し気持ち悪かった気がします。
リストの中身の数にもよりますけど、ボタンからモーダル等で選択させた方が親切かな。
イレギュラー対応を取るんであれば、「その他」選択肢作って別途入力欄設ける方が無難ですかね。

気を付ける点としては
・親cmpの中にそのままinputやら書いてrenderedCallbackの処理を一緒にしちゃうと、うまくquerySelectorが動かないので子cmpとして呼び出す方が良いと思います。

今回は以上です。
Reactで作ったアプリをウェブ上に公開: AWS Amplifyでの自動デプロイとAWS Route53によるカスタムURL設定

Reactで作成したアプリケーションをウェブ上に公開するために、AWS AmplifyとAWS Route53というサービスを使用しました。これらを用いることで、Reactで作成したアプリの公開の仕方や自動デプロイのやり方も知らない人でも、以下のことが簡単に実現可能です。

1. **ウェブ上への公開**: AWS Amplifyを利用することで、アプリケーションをウェブ上に公開することができます。

1. **自動デプロイの設定**: GitHubにプッシュしたコードの変更をトリガーに、AWS Amplifyがアプリケーションのビルドとデプロイを自動的に行うことが可能になります。

1. **独自ドメインの適用**: AWS Route53を利用して独自のドメインを取得し、それをアプリケーションに適用することが可能です。これにより、アプリケーションがそれっぽく見え、信頼性が増します。

1. **手軽なデプロイ管理**: AWS Amplifyのインターフェイスを通じて、デプロイのプロセスを一元管理することができます。また、異なるブランチで異なるバージョンのアプリケーションをデプロイすることも可能です。


**前準備(これらを完了させてから読み進めてください)**
- AWSアカウントを作成済み
- Reactのプロジェクト作成済み
- GitHubにそのプロジェクトをアップロード済み

## AWS Amplifyの初期設定

1. Amplifyの設定ファイルを用意

    フォルダ構成は以下の通りにしました。

    ```bash
    リポジトリのルート
    |-Reactのプロジェクト(筆者はfrontendという名前で作成)
    |-amplify.yml
    ```

    amplify.ymlでは以下のように設定します。フォルダ構成やnpmを使っているかyarnを使っているかで変わってくると思います。

    ```bash
    version: 1
    frontend:
      phases:
        preBuild: 
          commands:
            - cd frontend
            - npm ci
        build: 
          commands:
            - npm run build
      artifacts: 
        baseDirectory: frontend/build
        files:
          - '**/*'
      cache:
        paths:
          - frontend/node_modules/**/*
    ```

この設定により、ビルド前に必要な依存関係をインストールし、アプリケーションをビルドしてその結果を保存します。

### 2. AWS Amplify の設定

次に、AWS コンソールにログインし、Amplifyサービスを開きます。「Get Started」を選択し、GitHubをソースプロバイダとして選択します。

### 3. リポジトリの選択

GitHubアカウントを連携し、デプロイしたいリポジトリとブランチを選択します。

### 4. ビルド設定の定義

事前に用意した`amplify.yml`を選択して利用します。

### 5. アプリケーションのレビューとデプロイ

ビルド設定が適切に定義されたら、「Next」を選択し、アプリケーションの設定をレビューします。問題がなければ、「Save and deploy」を選択します。

### 6. Web アプリケーションの公開

デプロイが成功すると、公開されたWebアプリケーションへのリンクが表示されます。このリンクをクリックすると、デプロイされた Reactアプリケーションを確認できます。



## URLの変更

次に、デプロイされたアプリケーションのURLを独自ドメインに変更します。

### 1. AWS Route53でドメインを取得

まず、AWS Route53を使用してドメインを取得します。

### 2. AWS Amplify Consoleでドメイン管理を開く

AWS Amplify Consoleのダッシュボードでドメイン管理を開きます。

### 3. 取得したドメインを接続

取得したドメインを入力し、接続します。

### 4. サブドメインを設定

ドメイン接続後、必要に応じてサブドメインを設定します。

以上で、ReactプロジェクトのAWS Amplifyを使用したデプロイと、独自ドメインへの設定が完了します。

---

AWS Route53でドメイン設定をしない場合、デフォルトのURL（`https://[app-name]-[branch-name].amplifyapp.com`みたいな形式）が使用されます。

`[app-name]`と`[branch-name]`はそれぞれAmplifyとGitHubで設定することができますが、`amplifyapp.com`部分を変更するためには独自のドメインが必要となります。また、`[branch-name]`は個人的にはURLを含めたくありません。
shellでループせずにsignal待ちする案

shell scriptで pause() したいなーと思うケースがたまにあるんですけど、すっきりした答えを見たことがなかったのでメモ。

```shell
pause() {
  trap true 1 2 3 15
  sleep infinity& 
  wait $!
}

# [なんかする]

pause

# [後処理]
```

これでいけそう。
trapは待ち受けたいsignalを羅列しないといけないんですが、特殊用途でなければこれでよいでしょう。 
sleepコマンドは実行中はsignalで止まらないので、勢い
```shell
while true; do sleep 1; done
```
みたいになってしまいがちですが、定期的になんか動いてるのがダサいし処理粒度が1秒なのもダサい。
なのでバックグラウンドにしてwaitで待つことにすればゼロ負荷でsignal待ちできます。
vite-plugin-pwa をpnpmで利用しようとした時のエラーを解決する


vite-plugin-pwa pnpmで利用した時に、`virtual:pwa-register`がerrorになる

## 0. pnpmでvite react typescriptを作成する

```terminal
% mkdir vite-react-pwa-sample
% cd vite-react-pwa-sample
% pnpm init
% touch pnpm-workspace.yaml
% pnpm create vite
✔ Project name: … vite-react-pwa
✔ Select a framework: › React
✔ Select a variant: › TypeScript
```

```pnpm-workspace.yaml
packages:
  - "vite-react-pwa"
```

```package.json
"scripts": {
  "dev": "pnpm  --parallel --filter \"./**\" dev"
}
```

```terminal
% pnpm install
% pnpm dev
```

![Vite-React-TS (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144543/3c55af70-e051-25ff-83f0-3b294479947b.png)

## 1. 

vite-plugin-pwaをinstallして、設定する

```terminal
% pnpm add -D vite-plugin-pwa
```

```vite-config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
// 下を追加
import { VitePWA } from 'vite-plugin-pwa'

export default defineConfig({
  plugins: [
    react(),
    VitePWA({
      workbox: {
        sourcemap: true
      }
    })
  ],
})
```

```
[vite] Cannot find package 'vite-plugin-pwa' imported from PACKAGES
```

`vite-plugin-pwa`の型定義ファイルがないため、Errorとなっているので`tsconfig.json`を設定していきます。[ref](https://vite-pwa-org.netlify.app/guide/faq.html#ide-errors-cannot-find-module-ts2307)

```tsconfig.json
"compilerOptions": {
  "types": ["vite-plugin-pwa/client"],
}
```

## 2. virtual:pwa-registerを利用する際にErrorがでる

registerSWを利用しようとした時 `import { registerSW } from 'virtual:pwa-register'を利用しようとした場合に、

```
[plugin:vite:import-analysis] Failed to resolve import "virtual:pwa-register" from "src/main.tsx". Does the file exist?
```
とでるので、.npmrcを追加して依存関係のpackageを整理する。

```terminal
% touch .npmrc
```

```.npmrc
auto-install-peers=true
```

```terminal
% pnpm install
```

追加でinstallするとErrorが消えました。

[issue](https://github.com/vite-pwa/vite-plugin-pwa/issues/89)