OmniAICreator/Qiita-1.07M huggingface dataset:
新卒エンジニアが2年目にやって良かったことTOP3!!

# はじめに

こんにちは！ takuminoidと申します。
私は22卒でエンジニアとして入社し、丸2年が経とうとしています。新卒で入社し1年が経ち少し余裕が出てきた2年目に、私自身やって良かったことを本記事で紹介します。

# 筆者の背景について少々

2年目と言ってもさまざまな背景の方がいらっしゃると思うので、筆者の背景について軽く紹介します。

* 情報系の修士卒
* 学生時代アプリ開発の経験アリ （動けばokのいわゆる学生開発..）
* そこそこ大きめの会社にエンジニア入社（ベンチャーではない）
* バックエンド開発＋DevOps領域の運用改善などを担当
* 主言語はPython
* 最近ちょっと痩せた

よろしくお願いします〜！

# やって良かったこと TOP3

さて、やって良かったことは以下です。

1. ソフトウェアエンジニアリングの基礎を1から学んだ
1. 振り返りをこまめにするようにした ＋ それを上長に共有するようにした
1. テック系カンファレンス（オフライン）に参加した

1つずつ紹介していきます。


## 1. ソフトウェアエンジニアリングの基礎を1から学んだ

基礎を知っていることで「気付き」の量と質が圧倒的に上がりました。率直に言って世界が変わりました (笑)
基礎を知らず目の前の問題を解決するためだけに鍛えていくと**会社の内側**で活躍できる知識やスキルはついていきます。一方で、基礎を知ることで目の前の問題をより広い視点で捉えることができ、それを解決するために鍛えることで**会社の外側**でも通用できる知識やスキルが付いていっていると感じます。

入社前にアプリ開発経験はあったものの動けばokな学生開発だったので、クラスやオブジェクト指向など知っているだけの状態でした。
そこで私は基礎スキル、具体的にはソフトウェア設計周りのクラスや継承、オブジェクト指向、デザインパターン、SOLID原則を1から勉強しました。

私が学習に用いた主な教材は以下です。

https://www.udemy.com/course/oop-python/

--> 講座を見て写経しました

https://www.techscore.com/tech/DesignPattern/

--> 記事を読んで、サンプルコードがjavaなので自分でpythonに直しながら書いてみました

https://www.udemy.com/course/python-mx/

--> コードを写経して、↑で自分で書いてみたPythonのコードと見比べました

余談ですが、自分のGitHubに「practice」というリポジトリを作って勉強で書いたコードをpushするようにしたら、草が生えてモチベーション上がりました。


## 2. 振り返りをこまめにするようにした ＋ 上長に共有するようにした

日頃の業務に対する振り返りをこまめに行い、それを上長（所属する組織のエンジニアリングマネージャー）に共有するようにしました。これによって高い頻度でのフィードバックサイクルを実現でき成長速度が爆上がり（自称）しました。

弊社では2週間に1回、上長との1on1があります。そこでYWT(やったこと・わかったこと・次にやること)を共有するようにしました。
「やったこと」は2週間でやったことを羅列する、「わかったこと」はやったことから学んだこととそこでの自分の感情を書く、「次にやること」は..あまり書かないです(笑)。最も重要なポイントは **「わかったこと」で自分の感情を書くこと**です。〜をやっている時は楽しかった、〜できて嬉しかった、〜な時はイライラした、といった感じです。また、オリジナル項目で「雑感」を書いていて、業務以外でやったことや思ったことなどなどラフに共有したりします。

上長に共有すると色々とフィードバックをもらえるので、これがなかなか良いです。これ大事だと思うんですが、上司はやっぱりスゴイです。学びの量と質が上がるのはもちろん、自分のやるべきことが明確になります。


## 3. テック系カンファレンス（オフライン）に参加した

つい先日、[Developers Summit 2024](https://event.shoeisha.jp/devsumi/20240215) に聴講者として参加してきました！テック系のカンファレンスは初参戦でした。
羽田空港で死ぬほど迷ったのはさておき...率直にめちゃくちゃ楽しかったです。普通に業務をしているだけでは得られない気付きや学びが多くありましたし、超単純に、エンジニアがたくさん居て興奮しました！

また最も良かったのは、社外の空気に触れられたことだと思ってます。多くのセッションで社外の話を聞きながら自分の社内の組織や文化と比較しながら聞くことができ、自分のキャリアについてより視野を広く持てたと感じます。

新卒エンジニアは全員テック系カンファレンスに参加した方がいいんじゃないかな〜など思いました (笑)


# さいごに

ここまで読んでいただいてありがとうございました！
色々書きましたが、2年目のタイミングで体系的に学ぶことで成長の土台を早いうちに作ることが大事かなと思いました。また新卒だからこそ「社内」に閉じてしまう危険性はあると思っていて、早いうちから「社外」に触れて広い視点で自分のキャリアについて考えられると良いのかなと思います。
あくまで私自身の気付きとして本記事にまとめました！

本記事が良いなと思った方はぜひ「いいね！」していただけるとめちゃくちゃ喜びますmm
【Rails 7】アプリを日本時間に

# 【Rails 7】アプリを日本時間に

## 前提
・Ruby 3.2.2
・Rails 7.0.8

## config/application.rbファイルの編集
タイムゾーンを変更する
```config\application.rb
class Application < Rails::Application
    config.time_zone = "Asia/Tokyo"
end
```

## 表記の変更
年:%Y、月:%m、日:%d
時:%H、分:%M、秒:%s

スラッシュや漢字などを挟むときは、上記表記の間にそのまま入れる

```ruby:html.erb
<%= user.created_at.strftime('%Y/%m/%d') %>
<%= user.created_at.strftime('%Y/%m/%d %H:%M:%S') %>
<%= user.created_at.strftime('%Y年%m月%d日') %>
<%= user.created_at.strftime('%Y年%m月%d日 %H時%M分%S秒') %>
```
複数のサーバーを使用したアプリケーションの開発で詰まった件(スティッキーセッション)

## 複数のサーバを使用したアプリケーションの開発で詰まった件

#### 発生した事象
・図書登録機能を実装する際に、国立国会図書APIを利用してサムネイル画像を取得していた。その際に、取得したサムネイル画像が登録フォームに表示されたりされなかったりした。
（画像をサーバーに保存することはできていた上に、その保存先のファイルパスも正確に指定していた。）


#### 原因

・サーバーを複数使用していた。
・どうやらデフォルトではロードバランサーは、選択したロードバランシングアルゴリズムというものに基づいて、登録されたターゲットに各リクエストを個別にルーティングするらしかった。

#### 解決策
・そのためスティッキーセッション機能 (セッションアフィニティとも呼ばれる) を使用して、ロードバランサーがユーザーのセッションを特定のターゲットにバインドするように設定する必要があった。

→つまり、画像が保存されているサーバーに対して画像を呼び出せた際は、画像が表示されていたが、別のサーバーに対して呼び出している際は画像が表示されなかった。そのため、同セッション中は同じサーバーに対してアクセスするという設定をすることによって、毎度画像が表示されるようになった。


[参考サイト]
・https://docs.aws.amazon.com/ja_jp/elasticloadbalancing/latest/application/sticky-sessions.html
・https://qiita.com/takuma-jpn/items/873f6fb52ee5dc3500f6
ローカルのコードをGitHubのリポジトリにアップロードする方法

#### １. アップしたいコードがあるディレクトリに移動する
```
cd 'your-project-name'
```
#### ２. ローカルリポジトリの初期化
```
git init
```
#### ３. 変更をステージングエリアに追加
```
git add .
```
#### ４. ステージングエリアに追加した変更をコミット
```
git commit -m "(好きなコミットメッセージ)"
```
#### 5. GitHub上でリポジトリを作成
#### 6. 作成したリポジトリで「README」ファイルや「.gitignore」ファイルを作成した場合は、ファイルを削除
・GitHub上で削除ボタンを押下した後「Commit Changes」を押下する。
#### 7.ローカルリポジトリにリモートリポジトリを追加
```
git remote add origin https://github.com/{ 
GitHubのプロジェクト名 }/　{GitHubのリポジトリ}.git
```
#### 8.リモートリポジトリへプッシュ
```
git push -u origin main
```
#### 9.リモートリポジトリの変更をローカルに統合
```
git pull --rebase origin main
From https://github.com/{ 
GitHubのプロジェクト名 }/　{GitHubのリポジトリ}
```
### 10.プッシュ
```
git push origin main
```

これでGitHubのリポジトリにローカルディレクトリ内のコードをアップロードできます。
基本情報技術者試験

2月15日に基本情報技術者試験を受けて合格できました。

使った教材は「栢木先生の基本情報技術者教室」「出るとこだけ！基本情報技術者科目B」の２冊に、IPAが公開している科目Bのサンプル問題すべて(２０問のと、６問のをやりました。他にも数問あったような気がします)やりました。過去問道場も何周とこなしましたが、サンプル問題のトレースの仕方が身につけば参考書は買う必要がなかったように思います。youtube上で解説している動画などが豊富にあるからですね。
試験の点数は科目Aが750点ほど、科目Bが630点ほどとぎりぎりだったので、情報セキュリティの勉強はしっかりやっておけばもっと得点できたかもしれません。

次は応用情報技術者試験に向けて勉強を頑張ります。
TypeScriptでdocxを実行するとcreator: (_a = options2.creator) != null ? _a : "Un-named"エラーが出る

# はじめに

TypeScriptでDocxを実行する際にあまりネットに情報がなかったので躓いたところまとめています

# 問題

以下のコードを実行したところエラーが発生しました

```

```ts
function createDocument(articles: Article[]) {
  const doc = new Document();
```


```
      creator: (_a = options2.creator) != null ? _a : "Un-named",
                              ^
TypeError: Cannot read properties of undefined (reading 'creator')
    at new File (/home/watanabejin/workspace/shun-news/node_modules/docx/build/index.cjs:15434:31)
    at createDocument (/home/watanabejin/workspace/shun-news/src/index.ts:30:15)
    at Object.<anonymous> (/home/watanabejin/workspace/shun-news/src/index.ts:58:13)
    at Module._compile (node:internal/modules/cjs/loader:1254:14)
    at Module.m._compile (/home/watanabejin/.npm/_npx/1bf7c3c15bf47d04/node_modules/ts-node/src/index.ts:1618:23)
    at Module._extensions..js (node:internal/modules/cjs/loader:1308:10)
    at Object.require.extensions.<computed> [as .ts] (/home/watanabejin/.npm/_npx/1bf7c3c15bf47d04/node_modules/ts-node/src/index.ts:1621:12)
    at Module.load (node:internal/modules/cjs/loader:1117:32)
    at Function.Module._load (node:internal/modules/cjs/loader:958:12)
    at ModuleWrap.<anonymous> (node:internal/modules/esm/translators:169:29)
```

# 解決方法

以下のようにすることで実行できるようになりました

```ts
function createDocument(articles: Article[]) {
  const doc = new Document({
    sections: [],
  });
```

# おわりに

型エラーは以前起きていますが実行できて一安心です
Arduino IDE 2.x にバージョンアップしたら ATmega328P (Old Bootloader) が無い？　そもそも、ブートローダって何？

# １．ATmega328P (Old Bootloader) が無い？

Arduino IDE を バージョン 2.x に更新した後、久しぶりに Arduino NANO 互換ボードを使おうと思ったら［ツール］メニューに Processor: "ATmega328P (Old Bootloader)" が見当たりません！？　あれれ？ 以前の1.8.xではこの辺にあったのに？　と焦ったのですが Arduinoのフォーラムで解決策をみつけました。原文はこちらです ⇩⇩

https://forum.arduino.cc/t/atmega328p-old-bootloader/699129

ツール > ボード > Arduino AVR Boards > Arduino Nano を選択します。 

![ArduinoIDE2-ボードメニュー.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1834831/4b7f9703-108d-7a61-f1f9-03c8ad970292.jpeg)

これで [ツール] > [プロセッサ] メニューが追加されます。
その中から「ATmega328P (Old Bootloader)」を選択できます。

![ArduinoIDE2-プロセッサメニュー.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1834831/43027ef3-7995-2d01-b27c-50d7e77c2822.jpeg)

な～んだ、そーだったのかぁ～、ということであっけなく一件落着！　なわけですが・・

# ２．ブートローダとは？　書込み装置とは？

ところで (Old Bootloader)って何？　Old じゃないのもあるのかな？　新しいのと何が違うの？　ってゆーか、そもそも Arduino の「ブートローダ」とは？　

更に「ツール」メニューの下部の「書き込み装置」と「ブートローダを書き込む」というのも気になります。どんな装置で、どこに何を書くの？　と次から次へと疑問が湧きあがってきました。何年か前に初めてArduino IDEをインストールした日から今日まで一回もこれらをクリックした記憶がありません。　基本的、基礎的なことのように思えるけど必要ないから知らなかったのです・・　ということで検索しました。

下記ページにブートローダの簡潔な説明がありました。少し引用させて頂きます。

https://ht-deko.com/arduino/bootloader.html

:::note info
Arduino におけるブートローダーとは、外部プログラマ (書込装置) を必要とせずに **新しいファームウェアをインストールできる小さなファームウェアの事です。** これがないと Arduino ボードは USB 経由でスケッチを転送できません。
よって、**何らかの原因でブートローダーが破損してしまった場合には、ブートローダーを再度書き込む必要があります。**
:::

これを図で表すと、こんな ⇩⇩ イメージでしょうか。

![ブートローダと書込み装置.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1834831/1c3ffb6a-7bec-e3f5-30b0-9cc45dfde4dd.jpeg)

なるほど、幸いにもブートローダーが壊れるようなことが無かったから知らずにきたんだ。このページには様々な「書込み装置」について写真と図入りの丁寧な解説もあります。

■ Pro Mini, Pro Micro, ATtiny85 のような超小型の Arduino にはUSBコネクタがありません。こういったボードにプログラムを転送する際にも「書込み装置」が必要になるということですね。

■ 私の環境 Arduino IDE 2.3.2 で「書込み装置」を確認したところ Arduino / ESP32 / ラズパイPico でかなり異なってました。

![ArduinoIDE2-ArduinoNANOの書込装置.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1834831/c94066f4-1581-d2ce-3084-1a4dc6068b84.jpeg)

![ArduinoIDE2-ラESP32の書込装置.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1834831/4eb68ca7-1ef6-aeaf-f6ee-9a428936e296.jpeg)

![ArduinoIDE2-ラズパイPiciの書込装置.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1834831/a905c0a6-a3ec-a018-4801-dd741de87dde.jpeg)

■ ATMega328P / ATMega328P (Old Bootloader) / ATMega168 の違いについては下記ページの考察が参考になりました。

https://zenn.dev/suzuky/articles/004e8a39d414bf

# 【他の投稿記事：Raspberry Pi Pico】

https://qiita.com/totuto/items/4c9a43b1510eab2fde3d

https://qiita.com/totuto/items/38349e66865d518c45d8

https://qiita.com/totuto/items/0268e0e73caeb0d545d3

https://qiita.com/totuto/items/3e4bf52189515015cb90

https://qiita.com/totuto/items/6a3ab3c5f888d49739b1

https://qiita.com/totuto/items/2a8db63abd1d3c82880f

https://qiita.com/totuto/items/81944426dc81ab4b7e57

https://qiita.com/totuto/items/d3bc435d3d2f545c1c02

# 【他の投稿記事：ESP32】

https://qiita.com/totuto/items/27b8b25eba047560ce0f

https://qiita.com/totuto/items/42ce1b47e439ed719e08

https://qiita.com/totuto/items/2f58c0ce6e950bbc2eae

https://qiita.com/totuto/items/bb24760538af63b56039

https://qiita.com/totuto/items/885ee3939d0a06c5edc4
@RequestParamの省略記法が使えなくなった

- [Spring JPAチュートリアル](https://spring.pleiades.io/guides/gs/accessing-data-mysql) に取り組んでいたときのこと
- Spring Boot 3.2.3を使用
```java: MainController.java
    ...
    @PostMapping(path="/add") // Map ONLY POST Requests
  public @ResponseBody String addNewUser (@RequestParam String name, @RequestParam String email) {
    // @ResponseBody means the returned String is the response, not a view name
    // @RequestParam means it is a parameter from the GET or POST request

    User n = new User();
    n.setName(name);
    n.setEmail(email);
    userRepository.save(n);
    return "Saved";
  }
  ...
```
- 以下のエラーが発生
```
    Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: java.lang.IllegalArgumentException: Name for argument of type [java.lang.String] not specified, and parameter name information not available via reflection. Ensure that the compiler uses the '-parameters' flag.] with root cause
```
- RequestParamの変数名と対応する引数名が同じときはこの記法で大丈夫なはず、と思ったが、Spring Framework 6.1以降では```LocalVariableTableParameterNameDiscoverer```が使えなくなり、これに伴って省略記法も使えない(Spring Boot 3.2.3 はSpring Framework 6.1.4を使用)([参考](https://github.com/spring-projects/spring-framework/wiki/Upgrading-to-Spring-Framework-6.x#parameter-name-retention))
- https://github.com/spring-projects/spring-boot/issues/39095
squidでプロキシサーバの作成

テスト環境のビルが移行し、伴ってリモートアクセス周りの環境も変わり踏み台サーバが必要となったため構築。

### 環境
仮想化：VMware workstation-pro15
ホスト：Windows10
ゲスト：ubuntu22.04.3TLS

### Squidのインストール

``` sh:Squidのインストール
sudo apt update
sudo apt install squid
```

### Squidの基本設定

``` sh:squid.confをバックアップ
sudo cp /etc/squid/squid.conf /etc/squid/squid.conf.original
```

``` sh:squid.confを編集
sudo nano /etc/squid/squid.conf
```

最小限の設定として、以下のようにアクセス制御の設定を追加する。この例では、プライベートネットワーク（192.168.1.0/24）からのアクセスのみを許可。

```　sh:最小限の設定例
acl localnet src 192.168.1.0/24
http_access allow localnet
```

- `http_port 3128`: Squidがリクエストを受け付けるポートを指定（デフォルトは3128）。
- `acl localnet src 192.168.1.0/24`: `localnet` という名前のアクセスコントロールリスト（ACL）を作成し、192.168.1.0/24ネットワークからのアクセスを許可。
- `http_access allow localnet`: `localnet` ACLのアクセスを許可する。
- `http_access deny all`: 上記のルールにマッチしないすべてのアクセスを拒否する。


``` sh:リロードして変更を適用
sudo systemctl reload squid
```

### Squidの動作確認

``` sh:Squidの動作確認
sudo systemctl status squid
```
### Squidクライアント側の設定
プロキシを経由してインターネットにアクセスするクライアント側の設定
SquidサーバーのIPアドレスとポート3128を指定する。



#### (参考)デフォルトのsquid.conf設定 (コメント行以外の出力)

```
miwapinn@VM1:~$ grep -v '^#' /etc/squid/squid.conf | grep -v '^$'
acl localnet src 0.0.0.1-0.255.255.255  # RFC 1122 "this" network (LAN)
acl localnet src 10.0.0.0/8             # RFC 1918 local private network (LAN)
acl localnet src 100.64.0.0/10          # RFC 6598 shared address space (CGN)
acl localnet src 169.254.0.0/16         # RFC 3927 link-local (directly plugged) machines
acl localnet src 172.16.0.0/12          # RFC 1918 local private network (LAN)
acl localnet src 192.168.0.0/16         # RFC 1918 local private network (LAN)
acl localnet src fc00::/7               # RFC 4193 local private network range
acl localnet src fe80::/10              # RFC 4291 link-local (directly plugged) machines
acl SSL_ports port 443
acl Safe_ports port 80          # http
acl Safe_ports port 21          # ftp
acl Safe_ports port 443         # https
acl Safe_ports port 70          # gopher
acl Safe_ports port 210         # wais
acl Safe_ports port 1025-65535  # unregistered ports
acl Safe_ports port 280         # http-mgmt
acl Safe_ports port 488         # gss-http
acl Safe_ports port 591         # filemaker
acl Safe_ports port 777         # multiling http
http_access deny !Safe_ports
http_access deny CONNECT !SSL_ports
http_access allow localhost manager
http_access deny manager
include /etc/squid/conf.d/*.conf
http_access allow localhost
http_access deny all
http_port 3128
coredump_dir /var/spool/squid
refresh_pattern ^ftp:           1440    20%     10080
refresh_pattern ^gopher:        1440    0%      1440
refresh_pattern -i (/cgi-bin/|\?) 0     0%      0
refresh_pattern \/(Packages|Sources)(|\.bz2|\.gz|\.xz)$ 0 0% 0 refresh-ims
refresh_pattern \/Release(|\.gpg)$ 0 0% 0 refresh-ims
refresh_pattern \/InRelease$ 0 0% 0 refresh-ims
refresh_pattern \/(Translation-.*)(|\.bz2|\.gz|\.xz)$ 0 0% 0 refresh-ims
refresh_pattern .               0       20%     4320
miwapinn@VM1:~$ 
```
TSharkのインストール

TsharkはWiresharkのCLI版。今回はGUIを使わない環境でCLIで直接パケットキャプチャし解析を行い、スクリプト内で自動化する用途で構築。

### 環境
仮想化：VMware workstation-pro15
ホスト：Windows10
ゲスト：ubuntu22.04.3TLS

### TSharkのインストール

Ubuntuに`TShark`をインストールする

```
sudo apt install tshark
```
### インストール後の確認

利用可能なインターフェースのリストを表示

```
sudo tshark -D
```

表示されたインターフェースの中からキャプチャしたいインターフェースの番号（または名前）を指定して、以下のように`TShark`を実行。

```
sudo tshark -i インターフェース番号または名前
```




### 設定・出力例
インターフェースのリストの表示例
```
miwapinn@VM1:~$ sudo tshark -D
Running as user "root" and group "root". This could be dangerous.
1. ens33
2. ens34
3. ens35
4. br-34894d3fe50f
5. vethdf48cd7
6. veth87321e8
7. br-cfac700030c1
8. vethe6475e1
9. vethf264c88
10. vethb09c894
11. veth223737a
12. veth7ff872d
13. veth2bc5c4b
14. br-e420010af561
15. veth81b6228
16. veth2950d99
17. any
18. lo (Loopback)
19. docker0
20. bluetooth0
21. bluetooth-monitor
22. nflog
23. nfqueue
24. dbus-system
25. dbus-session
26. ciscodump (Cisco remote capture)
27. dpauxmon (DisplayPort AUX channel monitor capture)
28. randpkt (Random packet generator)
29. sdjournal (systemd Journal Export)
30. sshdump (SSH remote capture)
31. udpdump (UDP Listener remote capture)
miwapinn@VM1:~$ 
miwapinn@VM1:~$ 
```

`TShark`の実行例
```
miwapinn@VM1:~$ sudo tshark -i ens33
Running as user "root" and group "root". This could be dangerous.
Capturing on 'ens33'
 ** (tshark:192050) 08:47:57.628025 [Main MESSAGE] -- Capture started.
 ** (tshark:192050) 08:47:57.628139 [Main MESSAGE] -- File: "/tmp/wireshark_ens33DHHAJ2.pcapng"
    1 0.000000000 192.168.0.201 → 192.168.0.249 SSH 202 Server: Encrypted packet (len=148)
    2 0.040893473 192.168.0.249 → 192.168.0.201 TCP 60 58087 → 22 [ACK] Seq=1 Ack=149 Win=511 Len=0
    3 0.412210787 192.168.0.228 → 239.255.255.250 SSDP 217 M-SEARCH * HTTP/1.1 
    4 0.412210657 Elecom_01:11:80 → Broadcast    0x8899 60 Realtek Layer 2 Protocols
    5 0.514252766  192.168.0.1 → 224.0.0.1    IGMPv2 60 Membership Query, general
    6 0.515530460 192.168.0.201 → 192.168.0.249 SSH 218 Server: Encrypted packet (len=164)
    7 0.527750321 192.168.0.105 → 224.0.0.251  IGMPv2 60 Membership Report group 224.0.0.251
    8 0.556054131 192.168.0.249 → 192.168.0.201 TCP 60 58087 → 22 [ACK] Seq=1 Ack=313 Win=510 Len=0
    9 0.616965819 Microsof_65:93:c1 → Broadcast    ARP 60 Who has 192.168.0.9? Tell 192.168.0.242
   10 0.738206223 192.168.0.249 → 239.255.255.250 IGMPv2 60 Membership Report group 239.255.255.250
```
【AndroidStudio】カスタムフォントの追加方法とおすすめフォントまとめ

# はじめに
みなさん、普段開発されている際に使用されているフォントって何使われていますか？
現状のAndroid StudioではJetBrains Monoというフォントがデフォルトで設定されていると思いますが、特にこだわりがない場合にはこちらをそのまま使用しているケースが多いのではないでしょうか。

ですが、初学者の方だとスペースひとつでビルドエラーが発生し、どハマりする、なんてことが起こるかもしれません。
そこで今回は特に初学者の方にオススメのフォントと、合わせてAndroidStudioでカスタムフォントを設定方法をまとめておこうと思います。

# おすすめのカスタムフォント【HackGen】
初心者の方に特にオススメのフォントとしては、【HackGen】になります。

https://github.com/yuru7/HackGen/releases

こちらのフォントをオススメする理由としては、以下になります。

- 全角スペースを可視化することで、不意のビルドエラーを防げる
- 圧倒的に美しく見やすい文字
- 半角1:全角2で有名なRicty系のフォントと同様の等幅フォント

以上がオススメする理由になります。
HackGenも有名なRicty Diminishedと同様にプログラミング用のフォントになります。
源柔ゴシックがベースになっていることから全体的に丸みのある柔らかい文字が特徴で、非常に読みやすく、英字はもちろんひらがなやカタカナ、漢字と一通り読みやすいと感じます。

HackGen
![スクリーンショット 2024-02-25 16.25.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/354522/c0ba9cf3-fbb1-23b5-bbb2-31db1bda2b14.png)

また、何より初学者の方には全角スペースの可視化が可能になる点が非常にオススメです。
書き慣れていないと不意に全角スペースを入れてしまい、ビルドエラーになって詰む、なんて事故を防ぐことができます。

ちなみに上記フォントを入れることで、以下のように全角スペースが可視化されます。

![スクリーンショット 2024-02-25 15.29.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/354522/c89287ba-7e53-e2ac-a41a-aba32d071be5.png)

# Ricty系フォントは正式なサポートが終了に・・・
余談ではありますが、同様にプログラミング用のフォントとしてRicty Diminishedなどがあります。
こちらも大変美しいフォントで読みやすく、プログラマから根強い人気がありましたが、2023年1月28日をもってサポート終了となったようです。

https://qiita.com/sounisi5011/items/62e4da71458ca7ce73c7#%E3%81%9D%E3%82%8C%E3%81%98%E3%82%83%E3%81%82%E3%81%93%E3%82%8C%E3%81%8B%E3%82%89%E3%81%AF%E4%BD%95%E3%81%AE%E3%83%95%E3%82%A9%E3%83%B3%E3%83%88%E3%82%92%E4%BD%BF%E3%81%88%E3%81%B0%E3%81%84%E3%81%84%E3%81%AE

こちら過去には私も一時的ではありますが、使用していたことがあっただけに少々残念です。

# カスタムフォントの設定方法
さて、先ほどご紹介したHackGenですが、AndroidStudioへ設定する方法をご説明したいと思います。

AndroidStudio上にないカスタムフォントは設定画面からインポート等で追加することができません。
その為一覧にないカスタムフォントは以下ディレクトリへフォントを追加しておく必要があります。

```
~/Library/Fonts
```

上記に追加することで、Android Studio上の設定からフォントを選択することができるようになります。
また、これは余談ですが、恐らくJetBrains系のエディターも同様の手順でフォントが追加できそうです。

Fontsディレクトリへ追加ができましたら、設定画面よりフォントを選択します。

```
Settnigs>Editor>Fonts
```

![スクリーンショット 2024-02-25 15.45.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/354522/a38e604f-3ff7-d6f9-cd45-6ba6cd4ae140.png)

※追加したのに選択できないという場合には、一度AndroidStudioを再起動していただくことで選択ができる状態となっているかと思います。

Fontの一覧より、上記画像のようにHackGenを選択することでフォントの適用が可能です。
ちなみに35の方もありますが、文字幅比率が通常のHackGenとは異なり、且つ英字サイズも通常のHackGenより大きく見えます。
こちらは完全に好みなのでお好きな方を選んでいただければ良いと思います。

# さいごに
神は細部に宿るなんて言いますよね。
日々の小さな積み重ねが大きな成果につながることも大いにあると思いますので、フォント一つでもこだわりを持っていきたいところですね。
wovn-ignoreを入れ忘れない方法


Webサイトの多言語化・翻訳対応を一任できるサービスWOVN.ioにおいて、個人情報や動的な数字がWOVNに送信されないように翻訳抽出対象外にするwovn-ignoreを設定し忘れない方法を紹介します。

:::note warn

※ これは2021/03/29に[個人ブログ](https://bicstone.me)で公開した記事を移植し、[CC0-1.0](https://creativecommons.org/publicdomain/zero/1.0/deed.ja)で提供しています。情報は古い可能性があります。

:::

## WOVN.io について

Webサイトの多言語化・翻訳対応を丸投げできるSaaSです。資産は日本語のまま、JSタグを追加するだけでGoogle翻訳のように翻訳をしてくれます。Google翻訳とは異なり、辞書を登録したり、人力で翻訳をしてもらうこともできます。

ちなみに読み方は「ウォーブン」だそうです。私の周りでは「ウーヴン」や、「オーブン」など様々な呼び方がされていました。初期はみんなビビって「ダブリューオーヴィエヌ」と呼んでいました。ASUSみたいですね？

https://wovn.io/ja/

Webエンジニアとしては、システム上での多言語対応はほとんどしなくて良くなるのでかなり開発が楽になります。

しかし、サービスの特性上、ユーザーがサイトを開いたときに、表示されている文言がすべてWOVNに送信され自動登録されてしまいます。個人情報や、動的な数値などがすべて登録されてしまい、セキュリティ面や費用面でも問題があります。

## wovn-ignore 属性について

そこで、抽出対象外を設定する `wovn-ignore` 属性というものが用意されています。

https://wovn-support.zendesk.com/hc/ja/articles/360007899791

HTML属性に `wovn-ignore` を追加するだけでそのタグ内はすべて除外されます。

```html
会員名：<span wovn-ignore>おおいし</span>様
```

ちなみに似た設定に「コンテンツ除外ルール設定」や「ライブ編集」があるのですが、WOVNにログインしないと対象かわからなかったり、誤って設定を変更してしまったり、HTMLの構造変更で対象が認識されないなどの危険があります。確実な運用ができないので避けていました。

## wovn-ignore を入れ忘れる

WOVNを入れる予算が確保できるほどの大規模サイトであれば、wovn-ignore属性を設定するべき箇所は無数にあるはずです。

そんなプロジェクトで何人もの開発者が携わると絶対に発生するのが属性の「入れ忘れ」、「スコープ漏れ」、そして「スペルミス」です。しかも、入れ忘れていないか確認する方法がソースコードを確認するしかないという苦行です。

そこで、絶対に誰が開発しても「入れ忘れ」、「スコープ漏れ」と「スペルミス」を防止できる方法を発明したので、皆さんに伝授します。

```css
*[wovn-ignore] {
  background: fuchsia;
}
```

https://codesandbox.io/embed/wovn-ignore-ucer9y?theme=light&view=preview

なんということでしょう！wovn-ignore属性のただのテキストがたった3行のCSSでドギツい赤紫色に生まれ変わったではありませんか！

これなら絶対に間違えません。しかも、サイズなどに変更がまったくないのでデザインの影響もありません。 私が携わっていた案件では、あまりにもミスが多かったので開発環境のみこのCSSが設定されています。さすがに…という場合はブラウザのユーザースタイルシートや開発者ツールで設定するという方法もあります。

サイトのデザインと同化せず存在感を放つ色を選定してみてください。
Vue で空文字が Prop で渡ってきてバグってしまった話


数年前に作られていたvue 2 (TypeScriptなし) のプロダクトで空文字がPropで渡ってきてバグってしまった話と、注意点を備忘録として残します。

:::note warn

※ これは2022/12/05に[個人ブログ](https://bicstone.me)で公開した記事を移植し、[CC0-1.0](https://creativecommons.org/publicdomain/zero/1.0/deed.ja)で提供しています。情報は古い可能性があります。

:::

## 背景

数年前に作られていたvue 2 (TypeScriptなし) のプロダクトで次のようなコンポーネントがありました。

```js
export default {
  props: {
    sample: Boolean,
    default: false,
  },
};
```

このコンポーネントはこのように呼び出されています。

```vue
<template>
  <Sample :sample="sample2" />
</template>
```

このsample2、実は文字列が入っていました！

設計者の意図としては、JSの暗黙的な型変換と同様に空文字列であれば `false` 、 1文字以上であれば `true` という実装にしたかったのだと考えられます。

## 空文字列 `""` が `true` !?

ある日、 sample2に空文字列 `""` が入ってきました。しかし、 Sampleが `true` の場合の挙動をします。

なんと、 空文字 `""` が入ってきたときにコンポーネント内で `true` に変換されていたのです。

空文字 `""` はJavaScriptの真偽判定においてFalsyになるのでしばらく原因を理解するのに時間かかりました。

https://developer.mozilla.org/ja/docs/Glossary/Falsy

原因は、 `v-bind` において、 falsyな値は `null`, `undefined`, `false` だけという仕様があるためです。

https://v2.vuejs.org/v2/guide/migration.html#Truthiness-Falsiness-with-v-bind-changed

## なぜ

HTML Attributeと同じ挙動になるように揃えたみたいです。個人的にはReact (JSX) と同じ挙動なのでわかりやすく感じます。 v-ifなどは引き続きJavaScriptの真偽判定と同様の挙動をするようです。

## まとめ

- そもそもbooleanなpropに文字列を渡さないようにしよう
- このようなプログラムを書かれないように規約を設けるという意味でもTypeScriptは必須ですね
javaで直角三角形を表示するプログラムを作る

# 問題
int型の変数nを作成し、一辺がn個の直角三角形を表示するプログラムを作成せよ
例) nが4の場合

実行結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3717834/a2830098-1203-19bb-80da-7da2b6ae7165.png)

## 解答
```java
public class java01 {

	public static void main(String[] args) {

		int n = 4;

		for (int i = 0; i < n; i++) {
			dispCircle(i, n);
			System.out.println();
		}
	}

	static void dispCircle(int i, int n) {
		for (int j = 0; j < n; j++) {
			if (i > j) {
				System.out.print(" ");
			} else {
				System.out.print("●");
			}
		}
	}
}

```

## 解説
1行にn個空白もしくは●、どちらを出力するかを決めている`dispCircle`メソッドが肝で
`main`メソッド内のループ1回目では`i = 0`のため空白は出力されず●のみが出力される。
2回目のループでは`i = 1`なので空白が1回出力され残り3回●を出力する。
3回目のループでは`i = 2`なので空白が2回出力され残り2回●を出力する。
4回目のループでは`i = 3`なので空白が3回出力され残り1回●を出力する。


## 感想
1行ごとに空白もしくは●のどちらを出力するかを決める下記の処理を思いつくことができず
模範解答を見てしまったのが悔しい。
```java 
        for (int j = 0; j < n; j++) {
			if (i > j) {
				System.out.print(" ");
			} else {
				System.out.print("●");
			}
		}
```

TechProjinさんのjava練習問題は初学者の自分にはちょうどいい難易度なので
javaをやり始めた皆さんもぜひ解いてみてください。
リンクはこちら [TechProjin](https://tech.pjin.jp/blog/2017/11/07/java-programing-exercise-12/)
spring bootで掲示板アプリを作成してみました。

# 掲示板アプリを作成してみた。
企業研修が終わり、現在待機中。
spring bootを学習したけれども「はにゃ？」という感じだったので、復習を兼ねて簡単な掲示板アプリを実装しました。
もちろんしょぼいので、温かい目で見ていただけると感謝です。

URL↓

https://bulletinboard-5ado.onrender.com/login

ユーザー名、パスワードともにtestでログインできます。

git↓

https://github.com/izumida-rina/bullentinboard

## 開発環境
バックエンド：java17
フロントエンド：HTML/CSS, bootstrap
フレームワーク：spring boot
DB：H2
サーバー：Render(Docker)


## 目的
・CRUD操作の実装に慣れる
・HTML/CSSの使い方に慣れる。
・spring security の実装をやってみる。
・待機中の研修でデプロイを経験したので、もう一度デプロイをしてみる。

## よかった点
・フロントからどのように値が流れるのかだんだんわかってきた。
・調べる力がついた（Renderへのデプロイ方法、Dockerfileの作成、spring　securityの実装方法など…キリがありません）
・ローカル→サーバーという流れを経験できた。

## 課題点
・とにかく重い！！！そして、原因がわからない！！！
・H2データベースという簡易的なＤＢを使用したので、postgresに移行したい。
・パラメーター改ざんやSQLインジェクションなどの攻撃に対応できるようになっていない。
・見た目がいけてない。

## まとめ
やっぱりこれでは、課題解決感もでないしやり切れなかった部分もある…
次回の開発では、軽くて見やすいユーザー目線で考えたい。

### 参考文献
このサイトを見ながら、掲示板を作成しました。
ただし、springバージョンの関係でこの通りではうまくいかないので注意。
特にspring securityは別物になっています。

https://www.sys-link.jp/technical/SpringBoot/SpringBoot_menu.html
pythonにおけるクラスインスタンスのコピー

pythonにおけるクラスインスタンスのコピーについて、copyモジュールのcopyとdeepcopyの違いを確認したので、そのまとめと実験の結果です。

# 結論
pythonのコピーモジュールの`copy.copy`と`copy.deepcopy`について、それぞれ以下の動作をします
## copy.copy
- 新たなインスタンスが生成される
- 各インスタンス変数(self.xxx)は、コピー元のインスタンス変数それぞれにcopy.copyを行った値を持つ
- つまり、イミュータブルなオブジェクトであれば値のコピー、ミュータブルなオブジェクトであればコピー元のインスタンスのインスタンス変数への参照を持つクラスインスタンスが生成される

## copy.deepcopy
- 新たなインスタンスが生成される
- 各インスタンス変数は、コピー元のインスタンス変数それぞれにcopy.deepcopyを行った値を持つ
- つまり各インスタンス変数は、イミュータブル/ミュータブルに関わらず、コピー元のインスタンスと同じ値を持った新たなオブジェクトとなる

# 実験
## 実験内容
以下のコードで実験しました。

クラスを定義した後にディープコピーとシャローコピーを作成し、元のクラスの中身を書き換えます。

Test1クラスはインスタンス変数としてint型１つとlist型2つを持ち、書き換えの際、list1はlistオブジェクト自体を作り直し、list2はオブジェクトの中身だけを書き換えます。

```python3 
import random
import copy

class Test:

    def __init__(self) -> None:
        self.val_int = random.randint(0,9)
        self.val_list1 = [random.randint(0,9) for _ in range(5)]
        self.val_list2 = [random.randint(0,9) for _ in range(5)]
    
    # 値を再設定
    def reset(self):
        self.val_int = random.randint(0,9)
        # list1はリストごと初期化、list2は中身だけ初期化
        self.val_list1 = [random.randint(0,9) for _ in range(5)]
        for i in range(5):
            self.val_list2[i] = random.randint(0,9)
    
    def to_string(self):
        print(f'int  : {self.val_int}')
        print('list1: ', end='')
        print(*self.val_list1)
        print('list2: ', end='')
        print(*self.val_list2)
        print()


test = Test()
print('インスタンス初期状態')
test.to_string()

test_copy1 = copy.deepcopy(test)
test_copy2 = copy.copy(test)
test.reset()
print('値リセット後')
test.to_string()
print('リセット後のディープコピー')
test_copy1.to_string()
print('リセット後のシャローコピー')
test_copy2.to_string()
print('各インスタンスのオブジェクトID')
print(f'(test, test_copy1, test_copy2) = ({id(test)}, {id(test_copy1)}, {id(test_copy2)})')
```

## 実験結果
結果は以下のとおりです。

ディープコピーをしたインスタンスの各値は初期状態から変化が無く、元のインスタンスへの変更の影響を受けていません。

シャローコピーをしたインスタンスについては、list2が元のインスタンスと同じオブジェクトを参照したままであるため、元のインスタンスへの変更が反映されています。


```
インスタンス初期状態
int  : 0
list1: 0 4 8 5 5
list2: 0 4 9 3 1

値リセット後
int  : 6
list1: 4 8 7 2 2
list2: 0 3 6 7 1

リセット後のディープコピー
int  : 0
list1: 0 4 8 5 5
list2: 0 4 9 3 1

リセット後のシャローコピー
int  : 0
list1: 0 4 8 5 5
list2: 0 3 6 7 1

各インスタンスのオブジェクトID
(test, test_copy1, test_copy2) = (4592619712, 4592627488, 4592623360)
```
WARNING: VG datastore is missing PV と言われたら

apt upgrade 時などに以下のエラーが出るようになった。datastore は私の環境の VG 名。

<pre>WARNING: Couldn't find device with uuid HDX23A-oxyB-XkEt-91fs-mDRO-POuX-tnpVG9.
WARNING: VG datastore is missing PV HDX23A-oxyB-XkEt-91fs-mDRO-POuX-tnpVG9 (last written to /dev/sda1).
</pre>

以前ディスクを引っこ抜いて volume group から pv を削除し忘れたらしい。sudo vgreduce --removemissing <VG name> --force で見つからない PV をすべて削除できる。

<pre>$ sudo vgreduce --removemissing datastore --force
  WARNING: Couldn't find device with uuid HDX23A-oxyB-XkEt-91fs-mDRO-POuX-tnpVG9.
  WARNING: VG datastore is missing PV HDX23A-oxyB-XkEt-91fs-mDRO-POuX-tnpVG9 (last written to [unknown]).
  WARNING: Couldn't find device with uuid HDX23A-oxyB-XkEt-91fs-mDRO-POuX-tnpVG9.
  WARNING: Removing partial LV datastore/02.
  WARNING: Couldn't find device with uuid HDX23A-oxyB-XkEt-91fs-mDRO-POuX-tnpVG9.
  Logical volume "02" successfully removed
  Wrote out consistent volume group datastore.
</pre>

参照: https://serverfault.com/questions/319427/how-to-remove-bad-disk-from-lvm2-with-the-less-data-loss-on-other-pvs
KindleForPcで本を最初から順番に対応する文章を読み上げながら表示していくPGM

<h3>KindleForPcで本を最初から順番に対応する文章を読み上げながら表示していくPGM</h3>
KindleForPcで本を最初から順番に対応する文章を読み上げながら表示していくpowershellのスクリプト
を作成しました。

<hr>
PGM内容

```powershell
$source = @"
using System;
using System.Runtime.InteropServices;

public static class WinApi
{
    [DllImport("kernel32.dll")]
    public static extern bool Beep(int freq,int duration);
}
"@
Add-Type -TypeDefinition $source

#SendKeysを使うため、System.Windows.Forms名前空間読込
Add-Type -AssemblyName System.Windows.Forms

#テキスト読み上げ用
Add-Type -AssemblyName System.Speech

$ps_speak = New-Object System.Speech.Synthesis.SpeechSynthesizer
$ps_speak.SelectVoice("Microsoft Haruka Desktop")


#音声読み上げ用ファイルを読み込み
$arr = Get-Content "C:\hoge\hon-yomiage-text\kazu1.txt"
#echo $arr


#読み出し時間(秒)
$ReadSecond = 10
#何ページ分繰り返すか
$CountLength = 11
#読み出す本の最大ページ数
$BookPages = 22


 [WinApi]::Beep(440, 5000)

$page = 1

echo "順序読み出し開始"
#この間にトップ画面をkindleにする
Start-Sleep -s 60
echo "Start"

$ps_speak.Speak($arr[0])
Start-sleep -s 10

for($i=0; $i -lt $CountLength; $i++){
Start-Sleep -s 2
[System.Windows.Forms.SendKeys]::SendWait("{ENTER}")

Start-Sleep 10
$ps_speak.Speak($arr[($i+1)])

Start-Sleep -s $ReadSecond

}

 [WinApi]::Beep(440, 5000)
```
symbol-shoestring node(mainnet)よぅの🌺fast-sync🌻sfsdm🌻 

# うふふっ💞ぱそ美ちゃんです！

しゅ〜すとりんぐ（symbol-shoestring）で建てた のぉどよぅの🌺ふぁあすとしんく
🌻sfsdm🌻っていうの
めいんねっとよぅだよ
# つっかいっかたっ🌺
ん〜っとね？
さぎょうでぃれくとり（`docker-compose.yaml`がある場所だよ💗）でぇ
```
wget 10.dusanjp.com/fsdm/sfsdm
```
または？
```
wget 11.dusanjp.com/fsdm/sfsdm
```
ってやるとぉ
🌻sfsdm🌻ってふぁいるがね？落ちてくるの
のぉどはぁ？うごいていても🌺とまっていてもどっちでもいいよっ
```
sh sfsdm
```
ってやるとぉ？
ぶろっくでぇたの🌺だうんろぉどが始まりまぁす
なん時間かまってね

ぶろっくでぇたのだうんろぉどがおわったらぁ？かってにのぉどは始まるの
これでかんりょお〜🌻うふふっうふふっうふふふふふっっ💗
　

:::note info
blockDataはぁ？毎週？金曜日の夜に更新作業をしてるの🌺
だからぁ？金曜日の夜には使えないから？気をつけてねっ🌻
:::


# sfsdmのないよぉ🌺なの
```
#!/bin/bash
#
# 1) docker-compose down
#
# 2) recovery dbdata/data
#  10.dusanjp.com/fsdm/databases.tar.gz
#   -> $HOME/dbdata
#  10.dusanjp.com/fsdm/data.tar.gz
#   -> $HOME/data
#
# 3) docker-compose up -d
#
if [ $# -gt 0 ]; then
    shoestring_HOME=$(cd $(dirname $1) && pwd)/$(basename $1)
elif [ -z "$shoestring_HOME" ]; then
    shoestring_HOME=.
fi
if [ ! -d "$shoestring_HOME/keys" ]; then
    echo "$shoestring_HOME/Here is not shoestring directory!"
    exit 1
fi
S3_URL=10.dusanjp.com/fsdm
recover_data()
{
    echo "start recover data"
    #cd $shoestring_HOME/target/nodes/node/
    wget -O -  $S3_URL/data.tar.gz | tar zxf -
    echo "end recover data"
}
recover_databases()
{
    echo "start recover database"
    #cd $shoestring_HOME/target
    wget -O -  $S3_URL/databases.tar.gz | tar zxf -
    mv databases/db dbdata
    rm -rf databases
    echo "end recover database"
}
date
cd $shoestring_HOME
docker-compose down
#cp target/nodes/node/data/harvesters.dat bak.harvesters.dat
rm -rf $shoestring_HOME/data
rm -rf $shoestring_HOME/dbdata
recover_data &
recover_databases &
wait
docker-compose up -d
date
```
おとぉさんがぁ？🌺これ書いたからぁ？あたしも書きたくなっちゃったの🌻うふふ

https://qiita.com/dusanjp/items/1220ed8bfcfb16f9d906
地方SEのエンジニアとしてのキャリアを振り返り

# はじめに
Qiita×Findy記事投稿キャンペーン 「自分のエンジニアとしてのキャリアを振り返ろう！」の参加記事。
いいきっかけなので自分のキャリアを振り返りたいと思います。

# 背景
学歴なし（IT系専門卒）
地方Sier勤務就職

# テスター(2年)
SIerあるあるの出向(SES)というやつで、新卒入社早々国内SIer大手企業で働くことに
2年間のほとんどをテスト仕様通りにテストを行うことに費やす。
当時のSIerとしてはよくあることだが、長時間残業上等で雰囲気パワハラ系のいわゆるブラック職場
出向先に直接入社の同期が続々と辞めていく中、２年という期限付きだったので何とか耐えきる。

# 運用担当者(6年)
別の会社に出向、国内大手企業のIT子会社へ
一般会員向けWEBシステムの運用を担当
仕事内容は
- 画面(HTML)更新
- データ抽出
- インシデント対応（手順書ありの作業・振り分け）

運用担当者が行う主な作業は画面更新、ただHTMLのデザインはデザイン会社が行い、コードの更新は技術（保守担当）が行うので、納品されたデザイン・コードを既存ページに入れ込むことくらいか。

長時間残業は引き続きあたりまえで、インシデント発生時など24時間呼び出しがあるのはマイナス、
だが、パワハラ的な雰囲気がなくなったので、やってる当時はそこまで不満はなかった。
ただ、入社からエンジニアリングせず目立ったスキルが身につかないまま年数が過ぎることに、何とも言えない不安感を感じることもしばしば。
本社側の上司に開発へ移動したいと８年言い続けながら資格を取ってアピール

# サービスデスク(1～2年)
別の職場に出向し、パッケージ商品のサービスデスクを担当
エンドユーザーからの電話を受け付けて、マニュアルなどで解決できそうな内容ならそのまま1次解答
技術関係の内容なら保守会社（技術）へエスカレーション

完全にエンジニアではなくなった感
情報処理技術者試験プロジェクトマネージャーを取った翌年にやる仕事かという思いと
エンジニアとしてのキャリアが積んだことを感じたので、第1回目の転職活動を開始
地方の仕事も少なく関東への引っ越しを検討していたころ、次の仕事のオファーが来たため転職活動中止

# PL(1年)
以前運用を担当していた会社から、開発側のPLとしての仕事のオファーがあり
当時から開発がしたいと言っていたことを覚えていてもらったことに感謝しつつ、２つ返事で受諾
プロジェクトマネージャーの資格が役に立ったか？

仕事内容はPMが決めたQCDのとおりプロジェクトが進むように、
コーディングは外注しているので手を動かすことはないが、WBSとにらめっこしつつユーザー側担当者とスコープ調整をする毎日に満足感
ただこの開発部隊自体が会社命令で関東に移動することとなり、そのころ子供も生まれていたこともあって
関東にはついていかず出向解除を選択

# 保守担当者(2年)
10年を超えて初めて自社で勤務
パッケージアプリケーションの保守開発の仕事
C/C++で初めてコーディングの仕事を経験
ウォーターフォール開発でRD～STを一通りこなす

仕事内容に不満はなかったが、チームの雰囲気が非常に悪かったことと、そこからのキャリアアップの道筋が見えなかったため、2回目の転職活動を開始
リモートワーク普及後だったためフルリモートの仕事があり、いくつか最終面談まで進んでいたものの社内公募に受かったため、転職活動を終了

# 新規事業のエンジニアリングマネージャー(2年～)
現在の仕事
社内公募を発見し応募、定員に対して応募多数だったらしいが、スキル・人となりを評価されてか合格
新規事業のアジャイルチームの開発担当者から始まり、いつの間にかエンジニアリングマネージャー（エンジニアリーダー）のポジションに収まる

# 振り返っての感想
ほとんどエンジニアとは言いにくいキャリアをたどってきたが、振り返ってみると幅広く経験出来て悪いことばかりではなかったかと
当時はスキルが身についていない感じがしていた運用の仕事についても、エンジニアに必要な論理思考や問題解決能力が鍛えられた側面はあった気がしてきた

# 今、エンジニアとして大切にしていること
キャリアの中で転職というカードをもっておくと精神安定上よい
そのために、市場価値の高いエンジニアであることを意識している。

市場価値の高いエンジニアになるため気を付けていること
- 誠実な人間であること：結局は、人となり・ヒューマンスキルが一番大事
- 資格を持つ：一番簡単に知識・スキルの証明ができる手段
- 得意分野をたくさんもつ：１つをきわめてトップ数％に入る難易度は高い。スキルの掛け算で希少価値の高い人材を目指す方が現実的
pg_hba.conf

## 主な認証方式
| 認証方式 | 説明                                       |
|----------|--------------------------------------------|
| trust    | 常に許可                                   |
| reject   | 常に拒否                                   |
| md5      | MD5で暗号化されたパスワード認証を行う      |
| password | 暗号化されていない平文でのパスワード認証を行う |
サーバレスで Minecraft と外部サービスを連動させてみた話

この記事は２分で読めます。
色々あるので一部ぼやかして書いてます。

# はじめに
AWS の Lambda と API Gateway を使って Minecraft と外部サービスを連携させてごにょごにょ色んなことをやってみた話を簡単にまとめました。
Minecraft は全世界でもっともプレイヤー数の多い人気ゲームですが、アプリ自体は旧世代のアプリケーションなので現代のサービスと連携して有効利用しようと思うとそれなりに工夫が必要になります。Steam や Discord との連携機能も無ければ、Web からのアクセスもできなければ、サーバコンソールからでも Minecraft 専用のコマンドが必要になるという勝手の悪いゲームです。ついでに公式のサーバにはヘルスチェックやトラップも備わっていないので Minecraft サーバは壊れたところで自動では何もできません。
ということで何とかします。

# やったこと
Lambda と API Gateway を使ってバニラの Minecraft を外部サービスと連携させます。プラグインも Mod も要らないので公式のバニラサーバでも実装できます。
外部サービスからコマンド発行したい場合は API Gateway を介して Lambda を起動させればよいです。逆にバニラの Minecraft サーバからはログ監視しかすることがないので、検知したログを使って Lambda を起動させます。
あら簡単、これで Minecraft を API 化して操作したり、ワールド内の見える化ができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172479/ea5d2527-067a-02ad-0bfc-cd482eb8bd20.png)

とりあえず双方向にサーバコンソール以外のインタフェースを提供してあげただけですが、サーバレスなのでサーバコンソールに馴染みの無い人でもメンテナンスできて良いね、というだけです。
これだけだとほとんど意味は無いんですが、これで某クラウドでやってるような Web ベースの管理者機能を作ったりアプリを充実させれば、マイクラとホームページを連動した遊びができたり、自前のマイクラホスティングサービスを提供したりとかもできるんでしょうね。

# まとめ
ゲームとかアプリって使う側からはインフラの事なんて何も見えないから、インフラの何が大変なのか全く理解してもらえないけど安定稼働させようとすると実は大変なんですよね。でも大変なものを大変なまま放っておくとずっと大変な環境から抜け出せないし、新しいことにチャレンジする時間もできないから、こういうところを少しずつ自動化したりシステム化して息を抜く努力をしてる訳ですね。
これでワンオペのオペレータでも簡単に四六時中マインクラフトと添い寝できますね（？）。
Google Gemma-7bを試しました。

先週Googleが商用利用可能なOSSLLM「Gemma」を公開しました。
Hugging Faceが公開したベンチマークスコアによると、Mistral-7BやPHI-2を上回っています。



![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3556459/fe98c3ef-725a-7d10-c10f-b896534e898c.png)

私も試してみました。
英語のモデルですが、まずは日本語で質問してみることにしました。
「エネルギー業界の最適化ユースケースを教えて」と質問したところ、複数回試した結果、最も良い回答は以下となります。質問は理解しましたが、回答は英語、日本語、韓国語が混在している状態でした。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3556459/75b388fb-74f6-63c1-e8c8-da54d8233b96.png)

英語で聞いたら特に問題なく回答が生成されました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3556459/fc334630-8c5b-c040-2f03-daa4099c3ab8.png)


あと日本語で質問すると、時々”RuntimeError: probability tensor contains either `inf`, `nan` or element < 0”のエラーが発生します。調べてみたところ、パラメーターの設定が原因のようですが、質問の文面を少し変更するとエラーがなくなることもあります。やはり日本語支援しないためにエラーが頻発する傾向にあるようです。

他にも色々基本的な質問を試しましたが、試した感想としては、Mistral-7bと大きな違いを感じることはありませんでした。
ただ、今後Gemmaを基にした日本語の継続学習LLMやfinetuningモデルも公開されると思いますので、それには期待しています。

⇩Huggingfaceで簡単にfine-tuningもできますので、次回試してみたいと思います。

https://huggingface.co/blog/gemma-peft

興味ある方:

https://ai.google.dev/gemma/docs/get_started
【swift】delegateパターンとは

# delegateパターン
delegateは日本語訳で「委譲」という意味だそうですが、
自分の理解のためにもdelegateパターンについてまとめようと思います。
# delegateパターンとは？
delegateパターンとは**オブジェクト間のイベント通知の一種**です。
イベント通知は3種類に分けられます。
1. デリゲートパターン
1. クロージャ
1. オブザーバパターン

デリゲートパターンはこの中の1種類に該当するものです。

## イベント通知とは何か？
イベント通知とは、**イベントの発生箇所となるオブジェクトが、他のオブジェクトにイベントの発生を伝えること**です。
イベントというのは、アプリケーション内で発生するあらゆる事象のことになります。
例：UI要素のタップ、プロパティの値の変更
## 実装方法
### コード例
```swift
        protocol GameDelegate: class{
        	var numberOfPlayers: Int { get }
        	func gameDidStart(_ game: Game)
        	func gameDidEnd(_ game: Game)
        }
        
        class TwoPersonGameDelegate: GameDelegate{
        	var numberOfPlayers: Int {return 2}
        	func gameDidStart(_ game: Game) {print("ゲーム開始")}
        	func gemeDidEnd(_ game: Game) {print("ゲーム終了")}
        }
        
        class Game {
        	weak var delegate: GameDelegate?
        
        	func start(){
        		print("プレイヤーの人数は\(delegate?.numberOfPlayers ?? 1)人です")
        		delegate?.gameDidStart(self)
        		print("プレイ中です")
        		delegate?.gameDidEnd(self)
        	}
        }
        
        let delegate = TwoPersonGameDelegate()
        let twoPersonsGame = Game()
        twoPersonsGame.delegate = delegate
        twoPersonGame.start()
```

```swift
        //実行結果
        プレイヤーの人数は2人です
        ゲーム開始
        プレイ中です
        ゲーム終了
```         
               
        
### 実装手法
- 任せる処理をプロトコルのメソッドとして宣言する
```swift
func gameDidStart(_ game: Game)
func gameDidEnd(_ game: Game)
```
- デリゲート先のオブジェクトはそのプロトコルに準拠し、処理の委譲に応えられるようにする。
```swift
class TwoPersonGameDelegate: GameDelegate
```
### 命名方法
このコード例についてまとめると下記のようになります。
- デリゲート元のオブジェクト名→Game
- デリゲート先のオブジェクト名→TwoPersonGameDelegate
#### 命名3規則
1. メソッド名はデリゲート元のオブジェクト名から始め、続けてイベントを説明する
 gameDidStart
    　→gameはデリゲート元のオブジェクト名
    　→DidStartはイベント
gameDidEnd
    　→gameはデリゲート元のオブジェクト名
    　→DidEndはイベント

2. didやwillなどの助動詞を用いてイベントのタイミングを示す

3. 第一引数にはデリゲート元のオブジェクトを渡す
gameDidStart(_ game: Game)
                
#### 命名規則を守ることによるメリット
- メソッド名を見ただけで、誰が、いつ、どういう場合に呼ぶのか明確になる
 - 名前の衝突を回避できる
- 利用する側が扱いやすい
    - 既存のCocoa,Cocoa Touchフレームワークと違和感なく協調させることができる

### 利用するべき時
- 2つのオブジェクト間で多くの種類のイベント通知を行うとき
- 外部からのカスタマイズを前提としたオブジェクトを設計する時
terraform init 実行時に TCPエラー が発生する

## 環境
- Windows11
- terraform v1.3.10 (on windows_amd64)

## 概要
`terraform init` 実行時、下記の通りエラーが発生しました。providerインストール時、バイナリの取得に失敗しているようです。

```bash
│ Error: Failed to install provider
│
│ Error while installing hashicorp/aws v5.38.0: releases.hashicorp.com: Get
│ "https://releases.hashicorp.com/terraform-provider-aws/5.38.0/terraform-provider-aws_5.38.0_windows_amd64.zip": read tcp
│ [240b:12:1dc0:1d10:f407:c2a:b51d:a6f4]:63187->[2600:9000:2219:5e00:5:e2b6:b380:93a1]:443: wsarecv: An existing connection
│ was forcibly closed by the remote host.
```

## 対策
エラーメッセージを参考に検索してみると、下記ページに辿り着きました。
- https://github.com/microsoft/terraform-provider-azuredevops/issues/592#issuecomment-1733180336

```
You need to disable IP V6 , I think it is not supported, I had similar issue till I disabled IPv6 in my network card, or use a proxy srever.
you can read about it here https://anassblog.vercel.app/post/terraform-init-error/
```

ipv6 を無効化することで解消するかもよとのことで、ネットワークアダプターのプロパティから、ipv6を無効化することで事象を解消させることが出来ました。

暫定対応感はありますが、同様の事象で困惑している方への参考情報になれば幸いです。

### 備考
TCPエラーということで、OS や .NET Framework の TLS関連設定 を疑ってみましたが、事象は解消しませんでした。

(※) 下記サイトを参考に試行錯誤しました。 
- https://jpdsi.github.io/blog/internet-explorer-microsoft-edge/dotnet-framework-tls12/
- https://www.asustor.com/ja/knowledge/detail/?id=10&group_id=1011
"cleanlog" symbol nodeの掃除

symbol nodeを長い間稼働していると
過去の log fileが溜まってきて disk容量をそれなりに圧迫します
この log fileを削除する shell scriptです

mainnetと testnetのどちらでも使えます
# 使い方
nodeを停止する必要はありません
nodeの作業ﾃﾞｨﾚｸﾄﾘ（targetの直上）で
```
wget 10.dusanjp.com/cleanlog
```
file "cleanlog"が downloadされます
```
sh cleanlog
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/582454/0ea8cb70-560c-63a8-2ccc-f606ca50558e.png)
実行前と実行後の targetの 容量を表示します　これで完了です
長期間稼働させていた node程効果があります

# cleanlog の内容
```
du -sh target
ls -t1 target/nodes/node/logs/catapult_broker*.log | tail -n+2 | xargs rm -rf
ls -t1 target/nodes/node/logs/catapult_server*.log | tail -n+2 | xargs rm -rf
ls -t1 target/nodes/node/logs/catapult_recovery*.log | tail -n+2 | xargs rm -rf
cd target/nodes/node/data/statedb
rm -rf AccountRestrictionCache/LOG.old*
rm -rf MetadataCache/LOG.old*
rm -rf NamespaceCache/LOG.old*
rm -rf AccountStateCache/LOG.old*
rm -rf MosaicCache/LOG.old*
rm -rf SecretLockInfoCache/LOG.old*
rm -rf HashCache/LOG.old*
rm -rf MosaicRestrictionCache/LOG.old*
rm -rf HashLockInfoCache/LOG.old*
rm -rf MultisigCache/LOG.old*
cd -
du -sh target
```
final修飾子

# 始めに
final修飾子は主に変数、クラス、メソッドで使用される修飾子であり、宣言後の変更を禁止する事が可能です。

# 変数
finalを変数宣言で使用すると、宣言後の代入を禁止する事が出来ます。
これによって変数を定数として扱う事が出来ます。

# クラス
クラス宣言でfinalを使用すると、継承を禁止する事が出来ます。

# メソッド
また、メソッドの宣言でfinalを使用すると子クラスでのオーバーライドを禁止する事が出来ます。
finalはアクセス修飾子(public等)やstatic修飾子の後、戻り値型またはvoidの前に表記します。

# 参考にした記事
https://www.sejuku.net/blog/24926#index_id4
Power Platformのアプリやフローはどのように共有や公開できますか？

もちろんです！Power Platformのアプリやフローを共有する方法をわかりやすく説明しますね。

## **キャンバス アプリを組織と共有する**
1. **前提条件**
   - アプリを共有するには、そのアプリをローカル以外の場所に保存して、アプリを公開する必要があります。
   - アプリにわかりやすい名前と明確な説明を追加し、他のユーザーがそれらの変更を表示できるようにします。
2. **共有手順**
   - Power Appsにサインインします。
   - 左のペインで「アプリ」を選択します。
   - 共有したいアプリを選択します。
   - コマンドバーで「共有」を選択します。
   - ユーザーのMicrosoftアカウントの名前、セキュリティグループの名前、またはエイリアスを指定して、アプリを共有します。
   - 組織全体でアプリを実行できるようにするには、「全員」と入力します。
   - ユーザーは「組織アプリ」フィルターを設定して、共有されたアプリを見つけることができます。

## **Power Automateのフローを共有する**
1. **Power Automate for cloudの場合**
   - Power Automateにサインインして「マイフロー」を選択します。
   - 共有したいフローを選択し、垂直の省略記号（⋮）をクリックしてから「共有」を選択します。

これで、Power Platformのアプリやフローを効果的に共有できるようになります！¹²³

ソース: Bing との会話 2024/2/25
(1) キャンバス アプリを組織と共有する - Power Apps | Microsoft Learn. https://learn.microsoft.com/ja-jp/power-apps/maker/canvas-apps/share-app.
(2) 【Power Automate for desktop】 作成したフローの共有方法. https://powerplatformnikki.com/powerautomatefordesktophowtoshareflow/.
(3) クラウド フローを共有する - Power Automate | Microsoft Learn. https://learn.microsoft.com/ja-jp/power-automate/create-team-flows.
【Progate】Ruby on Rails 5　まとめ

## 各コードの機能




### ターミナル
|コード|機能|    
|:-|:-|
|`rails new アプリ`|入力したアプリ名と同じファイルを作成|
|`rails server`|サーバーを起動|
|`rails g controller コントローラ名 アクション名`|コントローラとそれに対応したファイルを作成|
|`rails g model テーブル名 カラム名:データ型(※テーブル名は単数系&頭大文字)`|マイグレーションとモデルを作成|
|`rails g migration`|マイグレーションのみを作成(マイグレーションの中身は自分で作成する必要がある)|
|`rails console`|コンソールを起動|
|`quit`|コンソールを終了|
|`File.write(ファイルの場所,ファイルの中身)`|指定したファイルの場所にファイルの中身を記述する|
|`bundle install`|Gemfileに記述したgemをインストールする|
|`インスタンス名.save`|インスタンスをテーブルに保存|
|`モデル名.first`|テーブルから一つのデータを取得|
|`モデル名.all`|全てのデータを取得する|
|`モデル名.all[数値]`|数値+1のidにあるデータを取得する|
|`モデル名.find_by(カラム名:値)`|特定の値を持ったカラムのデータを取得する|
|`モデル名.order(カラム名:順序)`|カラム名を指定された順序にする|
|`インスタンス名.destroy`|インスタンスをテーブルから削除|
|`モデル名.where(カラム名:値)`|複数の値を持ったカラムのデータを取得する|




### エディタ
#### ビュー
|コード|機能|
|:-|:-|
|`<%= yield %>`|各ビューファイルを代入|
|`link_to(文字,URL)`|文字や画像にリンクを付与(※1)|
|`<%= form_tag(URL) do%><% end %>`|フォームに入力されたデータをURLに送信する(※2)|
|`<textarea name =“カラム名”>初期値</textarea>`|カラム名と初期値を指定した入力フォームができる|
|`変数名.errors.full_messages`|エラーメッセージの配列が入っている|
|`flash[:notice]`|一度だけ格納されている文字列を表示させる変数|
|`<input name=“カラム名” value=“初期値”>`|カラム名と初期値を指定した1行分の入力フォームができる|

※1 postとgetの両方あり、postで扱いたい場合には、第三引数で{method: “post か get”}を指定する)
※2 画像を送信したい場合には、第三引数で{multipart: true})を指定する




#### コントローラ
|コード|機能|
|:-|:-|
|`@変数名`|同じクラスの他のアクションやビューでも使える変数|
|`redirect_to(/URL)`|指定したURLに移動する|
|`render(URL)`|他のアクションで指定した@変数を保持したまま指定URLに移動する|
|`session[: キーID]`|代入した値はブラウザに保存される|
|`before_action :共通化のアクション名, [only: [:適用するアクション名,...]]`|適用するアクションの前に実行される共通化アクション(※1)|

※1 applicationコントローラで共通化のアクションを作り、各コントローラで上記の実装をする




#### ルーティング
|コード|機能|
|:-|:-|
|`post/:id`|”post/数値”をまとめたルート|
|`get ルート名`|データベースとsessionを変更しないルート|
|`post ルート名`|データベースとsessionを変更するルート|




#### マイグレーション
|コード|機能|
|:-|:-|
|`add_column :テーブル名, :カラム名, :データ型`|テーブルにカラムを追加|
|`remove_column`|テーブルからカラムを削除|




#### モデル
|コード|機能|
|:-|:-|
|`validates　:検証するカラム名, {presence: true}`|空を防ぐ|
|`validates　:検証するカラム名, {length: {maximum: 数値}}`|文字数の最大数値を設定|




#### ジェム
|コード|機能|
|:-|:-|
|`gem ‘bcrypt`|パスワードハッシュ化のためのジェム|
|`has_secure_password`|パスワードをハッシュ化するメソッド(モデルに追加)|
|`@変数名.authenticate(“文字列”)`|変数のパスワードと文字列のパスワードが一致することを確認できる(コントローラで使用)|




## 各ファイルのアドレス
|ファイル|アドレス|
|:-|:-|
|ビュー|`app/views/コントローラ名/アクション名.html.erb`|
|共通のレイアウト|`app/views/layout/application.html.erb`|
|コントローラ|`app/controllers/コントローラ名_controller.rb`|
|共通のコントローラ|`app/controllers/application_controller.rb`|
|ルーティング|`config/routes.rb`|
|CSS|`app/asssets/stylesheets/コントローラ名scss`|
|画像|`public/ここに配置`|
|マイグレーション|`db/migrate/日付_create_モデル名.rb`|
|モデル|`app/models/モデル名.rb`|
|ジェム|`Gemfile`|
三項演算子の使い方

三項演算子の便利さに気づいたのでシェアします。

# 三項演算子とは
そもそも三項演算子とはなにかを説明すると
条件に続いて疑問符 (?)、そして条件が真値であった場合に実行する式、コロン (:) が続き、条件が偽値であった場合に実行する式が最後に来る。といった感じのものです。
正直、if文と似ているので使いどころが難しい部分もありますが有効に使える書き方があるのでご紹介します。

例として波の高さによってサーフィンが可能かどうかを判定するプログラムを作成します。

## if文で書いた場合
```java
public class java01 {

	public static void main(String[] args) {
		String isSurfable = "";
		int waveheight = 500;
		
		if(waveheight < 1000) {
			isSurfable = "OK";
		} else {
			isSurfable = "NG";
		}
		
		System.out.println(isSurfable);
	}

}
```
よくある条件判定式なので特に違和感なく見れると思います。
ただ、今回のお題である三項演算子を使うとあら不思議。大変見やすくなります。
## 三項演算子で書いた場合
```java
public class java01 {

	public static void main(String[] args) {

		String isSurfable = "";
		int waveheight = 500;

		System.out.println(isSurfable = waveheight < 1000 ? "OK" : "NG");
	}
}
```
このようにコードの量も削減されてわかりやすくスッキリとした印象を受けるかと思います。

有効に活用すればif文より簡潔で分かりやすいコードになる三項演算子ですが、
if文で書いた方がわかりやすい場合もあります。

例として複数の条件分岐をした場合でプログラムを組んでみます。
## if文で書いた場合
```java
	public static void main(String[] args) {

		String surfCondition = "";
		int waveheight = 1000;

		if (waveheight == 300) {
			surfCondition = "small wave";

		} else if (waveheight == 500) {
			surfCondition = "good wave";

		} else if (waveheight == 800) {
			surfCondition = "big wave";

		} else {
			surfCondition = "dangerous wave";
		}
		
		System.out.println(surfCondition);
    }
```

## 三項演算子で書いた場合
```java
	public static void main(String[] args) {

		String surfCondition = "";
		int waveheight = 1000;

		System.out.println(
			surfCondition = 
			  waveheight == 300 ? "small wave"
			: waveheight == 500 ? "good wave"
			: waveheight == 800 ? "big wave" 
			: "dangerous wave");
	}
```
見比べてみると、三項演算子も何をしているかはわかるのですがどちらかというと
if文の方が複数条件の場合は見やすいかなといった印象です。

## まとめ
条件次第では三項演算子の方が分かりやすくも分かりづらくもなるので場面に応じて使い分けましょう。
一番大事なのは誰が見ても分かりやすいコードを書くということなので。
【React】空白文字を削除するtrim() メソッド

# はじめに
現在React×TypeSript学習を行っています。
Udemyの下記の講座を受講しています。

https://www.udemy.com/share/104d6k3@hasJAjnmdN8VAq283t_h__ElpTKvDarVjDOC0VoLWcASzf7-ahXuRluKxEuTp1FFVQ==/
# 問題
下記の画面にて、**「ユーザーIDが入力されていないときはログインボタンを押下できない処理」を実装しました。**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3675500/fd472bbd-c668-d593-1814-93a11aeef99f.png)

ログインボタンのタグで、**「userIdが空文字のときにdisabledがtrueになる」処理として``disabled={userId === ""}``と書いたのですが、ユーザーIDを入力しなくてもログインボタンが押せてしまいました。**
```tsx:tsx
const onClickLogin = () => login(userId);
       
      //～中略～
                    <PrimaryButton
                        disabled={userId === ""}
                        loading={loading}
                        onClick={onClickLogin} >ログイン
                    </PrimaryButton>
```
# 解決方法
**trim() メソッドを使いました。**
```tsx:tsx

const onClickLogin = () => {
        if (userId.trim() !== "") {
            login(userId);
        }
    };

    //～中略～
                    <PrimaryButton
                        disabled={userId === ""}
                        loading={loading}
                        onClick={onClickLogin} >ログイン
                    </PrimaryButton>
```

**trim() メソッドとは？**
**文字列の両端から空白文字を削除する**メソッドです。
# おわりに
**修正前のコード**
userId === "" の条件は、ユーザーが何も入力せずにログインボタンを押した場合にのみdisabledがtrue となり、ログインボタンが無効になります。**しかし、ユーザーがスペースや改行などの空白文字だけを入力した場合には、disabledがfalse となり、ログインボタンが有効になってしまいます。**

**修正後のコード**
**trim() メソッドを使用して余分な空白や空白文字を削除することで、ユーザーが間違ってスペースを入力したりしても、ログインが無効**になります。
# 参考

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/String/trim
順位のつけ方

## はじめに
私はプログラミング学習を初めて1か月の初心者です。

学習する際、順位を作成するプログラムに思いのほか苦戦しました。自分の学習のメモとして記事を作成しました。

## コード
```ruby
score = [2, 4, 3, 4, 5, 4, 1, 5, 2, 3]

score_order = score.sort.reverse

for i in 0..9 do
    print "#{score_order[i]}点が#{i+1-score_order.slice(0,i).count(score_order[i])}位。"
end

#出力　5点が1位。5点が1位。4点が3位。4点が3位。4点が3位。3点が6位。3点が6位。2点が8位。2点が8位。1点が10位。
```
## 解説
5点満点のテストを10人で行ったとき、各点数に対する順位を考えます。

点数は一つの配列にまとめられているとします。

最初にscore.sort.reverseで、点数を降順に並べ替えた配列を作成します。

そして、並べ替えた配列を利用して順位を考えます。配列番号の前に同じ値を持った要素が何個存在するかが、順位算出のカギとなる考えです。

score_order.slice(0,i).count(score_order[i])では、sliiceメソッドで自分の配列番号までの配列を抽出し、countメソッドで抽出した配列から同じ値をもつ要素数を算出します。

配列番号から同じ値を持つ要素数を引くことで順位を求めることができます。

## おわりに
最初に順位を出力するコードを考えた時、1位が10人いる場合、9人いる場合、8人いる場合など場合分けをして考えていました。

配列番号に注目することで、それまでの重複分を引けば順位を簡単に求めることに途中で気が付きました。

他に順位を求めるコードのアイディアなどあれば、ぜひとも教えていただきたいです！