OmniAICreator/Qiita-1.07M huggingface dataset:
【2023年11月版】【Next.js】AmplifyJS がv5→v6とアップデートされたので、改めてAmplify Authの基本的な記法をまとめました

## はじめに
Amplify Authはとても簡単に認証ロジックを実装できるライブラリとして広く使われていますが、既存記事はv5を使用したものがほとんどです。
それもそのはずで、Amplify JavaScript v6が一般提供されたのは2023年11月15日です。（→[参考](https://aws.amazon.com/jp/about-aws/whats-new/2023/11/aws-amplify-javascript-v6/))

Next.jsを使用していて、認証をAmplify Authで実装することになったので、改めてv6を使用した記法を簡単にまとめてみました。

## 対象者
この記事は下記のような人を対象にしています。

- Next.jsで認証ロジックを実装したい方
- Cognitoを使って認証ロジックを実装したいが、最新バージョンでの書き方がわからない方

※当記事のみで実装を完結できる構成にはなっておりません。予めご了承ください。

## 前提
- Next.jsのバージョン: 13.4.19 **（※Pages Routerなので十分ご注意ください）**
- @aws-amplify/ui-react: 6.0.3
- aws-amplify: 6.0.5

## 結論
とは言っても、導入までの流れはほとんど変わっていないので、途中までは既にある良質な記事をご覧いただければと思います。  
ここでは、コード例のみ記載します。

```tsx:_app.tsx
import { Authenticator, View, Image, useTheme } from '@aws-amplify/ui-react'
import '@aws-amplify/ui-react/styles.css'
import { Amplify } from 'aws-amplify'
import type { AppProps } from 'next/app'
import React from 'react'
import awsExports from '@/aws-exports.js'

Amplify.configure(awsExports)

const authComponents = {
  Header() {
    const { tokens } = useTheme()

    return (
      <View textAlign='center' padding={tokens.space.xxxl}>
        <Image alt='logo' src='/logo.png' />
      </View>
    )
  },
}

export default function App({ Component, pageProps: { session, ...pageProps } }: AppProps) {
  return (
        <Authenticator components={authComponents}>
          <Component {...pageProps} />
        </Authenticator>
  )
}

```

上記のように `_app.tsx` に記載することで、おなじみのUIを実装することができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/852882/0f1fd3f4-39d5-6ed7-58f9-389633eb9207.png)

## 補足
signIn, signOut, fetchAuthSessionなどがよく使う関数かと思いますので、簡単に使用例を記載しておきます。

```tsx:Sample.tsx
import { signIn, signOut, fetchAuthSession } from 'aws-amplify/auth'
import React from 'react'

const Sample = () => {
  return (
    <>
      <button onClick={() => signIn} />
      <button onClick={() => signOut} />
      <button
        onClick={() => {
          const data = fetchData()
          console.log(data)
        }}
      />
    </>
  )
}

export default Sample

const fetchData = async (): Promise<any> => {
  const { idToken } = (await fetchAuthSession()).tokens ?? {}
  const response = await fetch('https://sample.com/endpoint', {
    method: 'GET',
    headers: {
      Accept: 'application/json',
      Authorization: idToken?.toString() ?? '',
    },
  })

  const json = await response.json()
  return json
}

```

## おわりに
以上、AmplifyJSのアップデートに伴う　Amplify Authの記法の変更点についてまとめました。  
途中にも記載しましたが、当記事のみで実装を完結することはできず、他記事等と併せてお読みいただくことを前提とすること、ご容赦いただければ幸いです。

## 参考記事
[Amplify JavaScript公式 - Add authentication](https://docs.amplify.aws/react/start/getting-started/auth/#add-the-amplify-ui-authenticator-component)
[React] MUI - Buttonのchildrenを英字の文字列にすると大文字になってしまう現象を回避する

# はじめに
React・MUI学習中に少し詰まったので、備忘録として残しておきます。

# 結論
以下のようにレイアウトを指定すれば回避できる！
```typescript
'textTransform: "none"'を追加！
<Button variant='contained' sx={{ textTransform: "none" }}>
    Download
</Button>
```
REST APIについて

# Web APIとは
HTTPなどのインターネット関連技術を利用してプログラムが読み書きしやすい形でメッセージを送受信できるように定義した規約、または規約を実装したサービスのこと。 <br />第三者が情報を利活用して新たな機能を開発できるようになり、"APIエコノミー"の実現による自社サービスの発展が可能になる。

### API(Application Programming Interface)
外部から機能やデータを呼び出し利用できるように定めた規約

# REST(REpresentational State Transfer)
「REST」とは分散型システムにおける設計原則群、設計ルールのことで、「RESTが求める原則」に従っていることを「RESTful」と呼ぶ。

### RESTが求める原則
- クライアント/サーバー
- ステートレス
- キャッシュ制御
- 統一インターフェース
- 階層化システム
- コードオンデマンド

## クライアント/サーバー制約
クライアントとサーバーが互いに独立して影響し合わない構造にする考え方。
基本的にトリガーはクライアントで、サーバーは受け身となる。

## ステートレス
クライアントの状態をサーバーが保持しないこと。以前のリクエストやトランザクションとの依存関係を持たないため、各リクエスト内に処理を完結させるのに必要な情報を全て保持させる。

## キャッシュ制御
キャッシュを有効利用することでリソースを効率化することができるが、古いデータを使用する可能性があり、信頼性が低下する恐れがある。

## 統一インターフェース
リソースに一意のURIを用いてアクセスし、リソース操作に関してはHTTPプロトコルのメソッドを用いて行う。システムアーキテクチャ全体が簡素化され、分かりやすくなる。

## 階層化システム
クライアントとサーバー間にロードバランサーを設置して負荷分散をしたり、
プロキシを設置してアクセスを制限をする等、階層構築するアーキテクチャスタイル。変更や再利用しやすくなるメリットがある。

## コードオンデマンド
プログラムのコードをサーバーからダウンロードし、クライアント側で実行するアーキテクチャのこと。ダウンロードしたプログラムをクライアント側で実行することで新たな機能が追加でき、サーバーの負荷を下げることができる。

# REST API
REST APIとは、前述した原則に則って設計されたAPIのことを言う。
また、movieをリソースとして基本的なCRUD操作行うURIとHTTPメソッドを定義すると以下のようになる。

| URI | HTTPメソッド  | 内容 |
|-----|------------|-----|
| /movies |  GET  | movieのデータ取得 |
| /movies |  POST  | movieの新規登録  |
| /movies/{id} |  GET  | movieの詳細情報取得  |
| /movies/{id} |  PUT  | movieの情報を更新  |
| /movies/{id} |  DELETE  | movieの情報を削除  |

#### 参考記事
[0からREST APIについて調べてみた](https://qiita.com/masato44gm/items/dffb8281536ad321fb08)
qrcodeライブラリで簡単QRコード作成術

QRコードを生成するためには、Pythonのqrcodeというライブラリを使用することで生成ができます。本記事では、QRコードを生成し、それを画像として保存する方法を解説します。

↓実際に生成したQRコード
![qr_code.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/632959/2cc79cb6-16b3-b260-3524-1cc42f8b8bd2.png)

必要なもの
Python: プログラミング言語として使用。
qrcode: QRコードを生成するためのライブラリ。

## 1: qrcodeライブラリのインストール
まず、必要なライブラリをインストールする必要があります。コマンドラインまたはターミナルで以下のコマンドを実行してください。

```
pip install qrcode[pil]
```

## 2: QRコードの生成
以下のコードは、data変数に入力したデータのQRコードを生成し、それを画像ファイルとして保存します。
```
import qrcode

# 生成するQRコードのデータ
data = "https://www.example.com"

# QRコードを生成
qr = qrcode.QRCode(
    version=1,  # QRコードのサイズ（1〜40）
    error_correction=qrcode.constants.ERROR_CORRECT_L,  # 誤り訂正レベル
    box_size=10,  # ボックスのサイズ
    border=4,  # ボーダーの幅
)
qr.add_data(data)
qr.make(fit=True)

# QRコードを画像として出力
img = qr.make_image(fill_color="black", back_color="white")

# 画像を保存
img.save("qr_code.png")

```

このコードはqr_code.pngという名前のファイルでQRコードを保存します。data変数に任意の文字列やURLを入力して、QRコードを生成できます。
【AWS】DevelopersIO BASECAMPに参加して学んだこと【デベキャン】

みなさんこんにちは。寒い日が続きます。
ずーーーーっと前から書こうと思っていて書けなかったデベキャンの記事を書こうと思います。

# デベキャンとは？
DevelopersIO BASECAMP、通称デベキャン。

AWSのことなら日本でNo. 1の会社、クラスメソッドが主催する実践形式のトレーニングです。

動画コンテンツとかで見て学ぶ。とかではなく実際に設計書を書いてみましょう！作ってみましょう！というトレーニングです。

それでクラスメソッドのエンジニアさんに質問ができる。さらに直々にレビューをいただける。しかもこれがなんと無料、いたせりつくせり。

本当にいいのか？なんならお金出しますよ。と言いたくなるくらい。

ちなみに自分が参加したのはデベキャンの“エッセンシャル”というやつ。Slackでチャットしながら進めるもの

本チャンのデベキャンは実際にオンライン通話し要件定義から設計から構築までできるというもの。

https://dev.classmethod.jp/news/developersio-basecamp/

# デベキャンではどんなことをしたのか？
まず課題が渡されます。例えば課題1では構成図を渡されてお客さんからの依頼の要件を読みながら進めます。

-----------------

お世話になっております。
AWS環境の構築を御社にお願いします。
要件の整理と、構成図を描くところまではできたものの、細かいパラメータや採用しているAWSのサービスについて自信がないため、サポートいただきたいです。
・・・

-----------------
などというメールの文言から始まり
お題として、、

- DBの負荷が上がることが予測されるので分けて使いたい
- Auto Scalingスケーリングは使用率50％を超えたらスケールさせたい。
　落ち着いたらコスト削減のため台数を減らしたい
・・・

などとお客さんからの要望が出されます。

そこからSlackのチャットを利用して質問をしながら進めて行くといった流れです。

課題1では基本構成図からAWS環境構築するための詳細設計書の作成
課題2でオンプレからクラウドへの移行
課題3では自由課題

といった流れで進んでいきました。

しかし課題2からは一気に難易度が高くなり自分はほぼ挫折状態でした。。。苦し紛れで提出しフィードバックをいただき課題3では自由課題。IAMのスイッチロールの設定やこれまでの課題の復習期間にあてて質問をしていました。

# 課題で使用していたサービス

- EC2
- Systems manager
- Amazon S3
- NAT ゲートウェイ
- AWS Certificate Manager
- Amazon CloudFront
- ALB
- AWS WAF
- EventBridge
- GuardDuty
- Amazon SNS
- RDS Aulora MySQL
- Elasticashe

# デベキャンとAWS教材との違い
UDEMYでの教材や書籍にもAWSの教材はあるしなにもデベキャンに参加しなくてもいいでしょ。と思います。

動画での教材、書籍だとわからないことがあったときにすぐに質問することができません。

AWSの動画教材や書籍でももちろん学習はできますし体系的に学べます。ただ実際の現場でどんな風に進めていったらいいのか？設計書を書いたところで本当に良いものか？不安に感じます・・

### 有識者に直接質問・レビューしてもらえる
デベキャンの最大のメリットは実務経験が豊富で日本ではAWSでNo. 1のエンジニアさんに質問できる。作った設計書にレビューがもらえる。

もしくは構築して行く流れで行き詰まったときに質問ができるということが大きいです。

またお客さんにどのようにメリットを提示して提案すればいいのか？を指導してもらえます。

自分でわかっているつもりですが実際に設計書を見てもらうことでできていなかったり、理解していなかったりすることがあります。

とても基本的なことを指摘されたりするので恥ずかしい思いをしますがこれも学びです。例えば、、

- セッションマネージャー を使う際はポート22を空けなくてもいい。ポートを開けないのがセッションマネージャーでEC2にリモートアクセスをするメリット

- ターゲットグループ のALB→EC2の通信プロトコルはiHTTPSにしてしまうとターゲットにサーバ証明書が必要になる。ALB（ターゲットグループ ）→EC2のこの通信間ではAWSのVPC内でのトラフィックとなるので厳しい暗号化要件がない限りは暗号化不要である。
（閉じたネットワークの通信なので）

- もしもNACLで制御するならインバウンドトラフィックと対になるアウトバンドトラフィックの許可も必須です。セキュリティグループで制御するならACLで制御しないのが推奨です。

- 他のリソース名と統一感を持たせるように、最初にリソースの命名規則を決めておくこと
この環境のリソースには、以下のように命名規則を設けます。
-<プロジェクト名>-<リソース名>-<サービス名>
※Amazon Amazon S3バケット等、重複できない名前は末尾にAWSアカウントIDを付けます。

- GuardDutyでSNS通知を送信できるようにEventBridgeルールを設定しておくこと。

- RDSとAurolaの選定する際の選ぶ基準について、もしRDSではなくAuloraを選定するのか？ディスク容量を決める必要がなくディスクフルにはならない、従来の仮想マシン構成にまつわる悩みがAuloraでは軽減されるため

などなど、、

### 他の現役エンジニアの質問回答が見れる

自分の質問回答だけでなく他の参加者の質問回答が見れます。1期生はSlackで運用していて常に質問が飛び交っていました。

似たようなところでつまづくこともあれば、違う観点での質問もありとても大きな学びとなりました。

### もくもく会が開ける！
一人でやるのもいいのですが誰かと一緒に学習する時間を約束すると身が入ります。
ちなみにもくもく会が私が勝手にチャットルーム内で呼びかけました(笑）

募集をしたところ2、3名ほど集まり途中から講師の方にも飛び入りで参加してもらいました。
そこでも質問回答会がありましたね。オンプレからクラウド移行についてのお話など、


私が参加した時は第1期、少し前に2期生の募集がありクローズになっています。AWSの技術を高めたい！という方はクラスメソッド 、DevelperIOの情報を追ってみてください。また募集がかかるかもしれません。

他にはこんな記事があります！

https://qiita.com/mmurm/items/c5c8b1328e8864144c26

https://qiita.com/mmurm/items/79107fe85915f30f9fa3

https://qiita.com/mmurm/items/1d42ab3d47dd85e4efbd
certbot renew時にnginxを再起動して、mattermostへ通知する

毎度、ググっても出てこない小ネタを取り扱っております。
本記事は個人的な見解であり、筆者の所属するいかなる団体にも関係ございません。

# 0. はじめに

certbotを使って自動的に証明書を発行し、更新も自動化されているかたも多いかと思います。
しかし、certbot renewしていてもnginxを再起動してくれるわけでもないので、証明書が更新されているのにもかかわらず、ブラウザーで見ると「証明書が古いです」とか言われて、慌ててnginxを再起動するなんて事もありますね。

定期的に再起動するような運用をしていたら問題ありませんが、そうでない場合このトラップに引っかかるなんて事もあります。

そこで、certbotのhookを使って自動的にnginxを再起動して、さらに証明書が更新されたというのが通知されると安心便利なので通知もしちゃおうよ、という話です。

# 1. nginx リロードスクリプト

以下のgistを元にnginx-reload.shを保存します。
Let's Encrypt Certbot post hook command for Nginx which checks the updated configuration files and reloads the server if everything validates.
https://gist.github.com/justinhartman/79c527076ce7cc740423eb92b5600d52

```io
cd /etc/letsencrypt/renewal-hooks/deploy/
sudo wget https://gist.githubusercontent.com/justinhartman/79c527076ce7cc740423eb92b5600d52/raw/5709ad0ff74d713b3ab26c6b7c8999f98838ff0a/01_nginx-reload-post-hook.sh
```

実行権限を付けてテストしておきます。
```io
sudo chmod a+x /etc/letsencrypt/renewal-hooks/post/nginx-reload.sh
sudo nginx -t && sudo systemctl reload nginx
```

# 2. mattermostへ通知します

通知スクリプト

```bash:02_send-renewal_notify-hook.sh
#!/bin/bash

# Hostname
HOST='hoge server'

# XXXX team
MATTERMOST_WEBHOOK_URL='https://＜MatttermostURL＞/hooks/hogehogehoge'

function post_mattermost ()
{
  eval postmsg="$1"
  curl -i -s -o /dev/null -X POST -H 'Content-Type: application/json' -d "${postmsg}" $MATTERMOST_WEBHOOK_URL
}

msg="{\"text\": \"$HOSTの証明書を更新しました。確認をお願いします。\"}"
curl -i -s -o /dev/null -X POST -H 'Content-Type: application/json' -d "$msg" $MATTERMOST_WEBHOOK_URL
```

実行権を付けてテストしておきます。
```
sudo chmod a+x ./02_send-renewal_notify-hook.sh
bash ./02_send-renewal_notify-hook.sh
```

# 参考資料

Let's Encrypt Certbot post hook command for Nginx which checks the updated configuration files and reloads the server if everything validates.
https://gist.github.com/justinhartman/79c527076ce7cc740423eb92b5600d52

Let's EncryptのSSL証明書更新時にサービスを再起動する #Let’sEncrypt - Qiita
https://qiita.com/kazuhidet/items/9d58a104f93d9ff7302d

Let's Encryptの証明書自動更新のSlack通知を賢く行う - Ryoto's Blog
https://www.ryotosaito.com/blog/?p=485
Docker is all you need.

# Docker is all you need.
Dockerを利用してVSCodeでPythonのVersionを管理する方法を解説します。
多分これが一番バージョン管理しやすいと思います。
10分でできます。多分。Node.jsやNGINXとかの環境構築にも使えます。
# Dockerをインストールする
<a href="https://www.docker.com/ja-jp/">Docker公式サイト</a>

# VSCodeでDev Containersをインストールする
![スクリーンショット 2023-12-30 16.59.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3311006/f706f33b-6e3d-6fad-645d-ca056cfb0972.png)

# コマンドパレットを開く
Winなら```Ctrl+Shift+P```, Macなら```Cmd+Shift+P```ですかね。
コマンドパレットを開いたらまずは```Dev Containers```と打ちましょう。
![スクリーンショット 2023-12-30 16.59.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3311006/b9277b7d-4db8-07db-750c-7e08773258ad.png)

```Open Folder in Container```をクリックして```Python 3```と入力してください。
![スクリーンショット 2023-12-30 17.01.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3311006/38141ea1-e498-7f81-fe2e-1a45d6bf370b.png)
```Python 3 devcontainers```をクリックして使うPythonのVersionを指定しましょう。
![スクリーンショット 2023-12-30 17.01.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3311006/7c63f575-0c3b-0be8-8d80-89eae28c85ca.png)
好きなPython Versionをクリックするとコンテナがビルドされ, その環境の中で作業ができます。ね, 簡単でしょ?
【flutter】.copyWith()について

## .copyWith()とは
.copyWith()はイミュータブルなオブジェクトを簡単に更新することができる非常に便利なメソッドです。これはProviderなどの状態管理においてよく使われます。

## .copyWith()の使い方
```
@immutable
class User {
  final String name;
  final int age;

  User({required this.name, required this.age});
}

void main() {
  User originalUser = User(name: 'John', age: 25);

  // copyWithでageを更新
 originalUser = originalUser.copyWith(age: 26);

  print(originalUser.name);  // 'John' (元の値を引き継いでいる)
  print(originalUser.age);   // 26 (新しい値がセットされている)
}
```

上記のコードでは、copyWithメソッドを利用してoriginalUserオブジェクトのageプロパティを更新しています。この方法を用いることで、元のオブジェクトは変更されずに、指定したプロパティだけが新しい値に更新されたコピーが生成されます。そして、originalUserにこのコピーを再代入することで、元のオブジェクトを更新しています。これにより、イミュータブルな特性を保ちつつ、必要なプロパティを変更することができます。

イメージ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2984024/6f2e0602-8e14-2d6b-6354-7463867b71ea.png)

## 最後に
.copyWith()は深く考えると複雑なメソッドではありますが、要するにイミュータブルなオブジェクトのプロパティを更新することができる素晴らしいメソッドです。状態管理などで私もいつも助けられています。感謝して使っていこうと思います。
[React×Typescript] setStateした直後に更新後のstate変数を扱う方法

# はじめに
### setStateとは?
React hooksの代表的な機能で、主にクラスコンポーネントにおいて用いられるメソッド。
Reactを使うなら `useState`と`this.setState()`は避けれない(やってることは同じ..だと思う)

### なぜこんなことを考えたのか
とあるコードにおいて新しい配列をstateに反映し、その直後に更新したstate変数の配列をいじるようなことになった。

以下がその例

```react
//とあるクラスコンポーネント内


//選択したタスクアイテムを編集可能にしてフォーカスするためのメソッド
changeItemEditable(id:string){
    const items: CheckListItem[] = this.state.checklist.map((item:CheckListItem) => {
        return {
            id: item.id,
            name: item.name,
            isChecked: item.isChecked,
            isReadOnly: item.id === id?!item.isReadOnly:item.isReadOnly
        }
    })
    const target = document.getElementById(`text-input-id=${id}`);
    target?target.focus():undefined
    this.setState({checklist: items})
} 

//新しいタスクを追加するメソッド
addNewTask() { 
    const tasks = this.state.checklist
    const task = new Task(tasks);//タスクの追加・削除・検索などを管理するクラス
    const newTask = task.addTask()
    this.setState({checklist: [...tasks, newTask]});
    this.changeItemEditable(newTask.id);
}

```

このコードにおいて
1. タスクを追加
1. 空のタスクが追加され、編集するために追加後に自動フォーカス

を実現したかった。

しかしうまくいかなかった。

# うまくいかなかった原因と解決策

### うまくいかなかった原因
**setStateは非同期関数だった**
つまり上記の例でいくと先に`this.changeItemEditable`が実行されていたのだ。
そのため`changeItemEditable`メソッド内のsetState(更新前)の実行と、addNewTaskのsetState(更新)が同時に発火し、更新した直後に更新前に戻されるということが発生したのが原因だった。

### 解決策
`setStateは非同期関数である`
このことに着目すると答えは簡単で`async/await`を使えばいい

以下が修正後のコード
```jsx

changeItemEditable(id:string){
    const items: CheckListItem[] = this.state.checklist.map((item:CheckListItem) => {
        return {
            id: item.id,
            name: item.name,
            isChecked: item.isChecked,
            isReadOnly: item.id === id?!item.isReadOnly:item.isReadOnly
        }
    })
    const target = document.getElementById(`text-input-id=${id}`);
    target?target.focus():undefined
    this.setState({checklist: items})
} 
    

async addNewTask() { 
    const tasks = this.state.checklist
    const task = new Task(tasks);
    const newTask = task.addTask()
    await this.setState({checklist: [...tasks, newTask]});
    await this.changeItemEditable(newTask.id);
}

```
演算子と判定構造（Java Silver勉強）

# 最初に
この記事は、以下の書籍を通じてJava silverの合格に向けて自分が学んだ内容を体系的にアウトプットする記事である。（目指せ年内合格！）
[徹底攻略Java SE 11 Silver問題集[1Z0-815]対応](https://www.amazon.co.jp/%E5%BE%B9%E5%BA%95%E6%94%BB%E7%95%A5Java-SE-11-Silver%E5%95%8F%E9%A1%8C%E9%9B%86-1Z0-815/dp/4295007625/ref=asc_df_4295007625/?tag=jpgo-22&linkCode=df0&hvadid=342700130622&hvpos=&hvnetw=g&hvrand=9715255718806805548&hvpone=&hvptwo=&hvqmt=&hvdev=c&hvdvcmdl=&hvlocint=&hvlocphy=1009303&hvtargid=pla-830348236008&psc=1&mcid=e12ce628cc5a33b29a77da3a85a6cdf0&th=1&psc=1)

# 勉強記録
## 3章 演算子と判定構造
### 型変換
- 大きな値を小さな値に変換する時は、以下のように明示的な型変換が必要
- ```clike
    int a = 10;
    short b = (short) a;
    ```
- ただし、byte型やshort型に変換する場合のみ、明示しなくても良い
- ちなみに各型の範囲
    - byte型（8ビット）: -128 ~ 127
    - short型（16ビット）: -32,768 ~ 32,767
    - char型（16ビット）：0~65535（非負の値のみ）
    - int型（32ビット）: -2147,483,648 ~ 2,147,483,647
    - long型（64ビット）: -9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807

### 浮動小数点

- 浮動小数とは
    - 計算で誤差が出ることが前提とされている代わりにより大きな範囲の値を表すことができるデータ
    - float型とdouble型がある

| 特性 | float型 | double型 |
| --- | --- | --- |
| ビット数 | 32ビット | 64ビット |
| 精度 | おおよそ6〜7桁の有効数字 | おおよそ15〜16桁の有効数字 |
| 符号ビット | 1ビット | 1ビット |
| 指数部 | 8ビット | 11ビット |
| 仮数部 | 23ビット | 52ビット |
| 最小値 | 約 -3.4028235 × 10^38 | 約 -1.7976931348623157 × 10^308 |
| 最大値 | 約 3.4028235 × 10^38 | 約 1.7976931348623157 × 10^308 |
- 正確な計算を要する場合は、BigDecimalを使用する
    - 浮動小数点は、2進数で値を保持するが、10進数から2進数に変換する際に、正確に表現できる値の違いにより誤差が生じる。一方で、BIgDecimalは、10進数で値を保持するため、同様の誤差が発生しない。
    - https://programming-tips.jp/archives/i/5/index.html

### 同一性と同値性

- 同一性：同じインスタンスであること（同じインスタンスを参照していること）
    - ==で判定
- 同値性：同じ値であること
    - equalsメソッドで判定
- String型は、==で同値性を判定できる
    - コンスタントプールという仕組み
        - Java仮想マシンにおいて、定数やシンボルを保存する領域のこと。 ヒープ内のメソッドエリア（テキストセグメントのような場所）に存在し、全てのスレッドから共有される。
        - 同じ文字リテラルがプログラム内に再び登場すると、定数用のメモリ空間にある文字列インスタンスへの参照が使い回しされる。
            - https://naipaka.hatenablog.com/entry/2018/12/05/185730
        - コンスタントプールは、文字列リテラルを用いて宣言した時のみ有効。明示的にnewを使って宣言した場合は、コンスタントプールに作成されない。
    - internメソッド
        - コンスタントプールを含むメモリ内の文字列を探して、再利用するためのメソッド

### switch文

- 条件式が戻せる型
    - 戻せる
        - int型以下の整数型とラッパークラス
        - 文字と文字列
        - 列挙型
    - 戻せない
        - long型
        - float型、double型
        - boolean型
- case値（分岐で使う値）
    - 使用できる値
        - 条件式が戻す値と同じ型か、互換性がある型
        - 定数であるか、コンパイル時に値を決めることができること
        - nullではないこと
    - 変数は使用できない！
- breakが現れるまで、次のcase式の処理を実行し続ける。

### 他

- if文の中括弧は省略できる
    - 省略した場合は、次の一文だけ処理される
    - ただ、どこまでがif分岐内かわかりづらいため、推奨されない

## 感想

- float型や、double型で正確な計算ができないことは知っていたが、その仕組みが新数変換によるものだというのまでは理解できておらず、勉強になった。
- コンスタントプールは、初耳すぎた。メモリが節約できてありがたいんだろうけど、どれだけありがたいかあんまり想像つかん。
- あと、数日体調崩していて、年内合格が厳しそう。。。1/3までに合格を目標とし軌道修正。
goroutineをわかりやすく解説したい

## はじめに
Goのgoroutineついてわかりやすく説明することを目標に記事を書きました。

## 要点
・goの並行処理goroutineの実装は至って簡単で、並行処理させたい関数の前に go をつけるだけ。
・並行処理はメインスレッドとは異なるスレッドで同時に実行する。メインスレッドが終了したら実行中のgoroutineも強制終了
・goroutineを必ず実行させたいなら、WaitGroupなどの機能を利用しよう。

## 実際にコードを見てみよう

```
package main

import (
    "fmt"
    "time"
)

func rush(shout string) {
    for i := 0; i < 4; i++ {
        fmt.Println(shout)
        time.Sleep(time.Second)
    }
}

func main() {
    go rush("無駄")
    rush("オラ")
}
```

これで上のコードを実行すると、下の結果になります。

```
オラ
無駄
無駄
オラ
オラ
無駄
無駄
オラ
```

ご覧の通り、go rush("無駄") の部分が並行実行され、文字が不規則に交互に出力されます。ここで func rush 内のtime.Sleep(time.Second) を削除してみましょう。すると下の出力になります。


```
オラ
オラ
オラ
オラ
```

あれ？何回やっても"無駄"が出力されませんね。
実はGoはメインスレッドというメインの処理が流れる場所があり、go routineはそのメインスレッドとは別にサブのスレッドを用意し、そこで処理を並行実行するのです。
ここで重要なことは、goはメインスレッドの処理が終了したら他のサブスレッドの状況に関係なく動作を終了してしまうのです。
そのため、 time.Sleep(time.Second) を削除するとメインスレッドが一切の待機時間なく処理を行い、 rush("オラ") がループの最大値まで即座に実行されます。するとサブスレッドで実行中の go rush("無駄") は割り込む間も見つけれず、何もできずに終了してしまうのです。
また、試しに両方ともgoroutineにしてみましょう。

```
func main() {
    go rush("無駄")
    go rush("オラ")
}
```

今度は何も出力されません。これは両方の処理がそれぞれ異なるサブスレッドに移動し、メインスレッドでは何もしないため、即座にメインスレッドが終了してしまったのです。よって両者共に強制終了となりました。
ではどうすればメインスレッドが終わってもgoroutineが終わらないようにできるのでしょうか。そこで sync パッケージの WaitGroupを使用してみましょう。

```
package main

import (
    "fmt"
    "sync"
    "time"
)

func mainRush(shout string) {
    for i := 0; i < 4; i++ {
        fmt.Println(shout)
    }
}

func subRush(wg *sync.WaitGroup, shout string) {
    defer wg.Done()
    for i := 0; i < 4; i++ {
        fmt.Println(shout)
    }
}

func main() {
    var wg sync.WaitGroup
    wg.Add(1)
    go subRush(&wg, "無駄")
    mainRush("オラ")
    wg.Wait()
}
```

実行結果は下のようになります。

```
オラ
オラ
オラ
オラ
無駄 // メインスレッドが終了しても実行してくれる
無駄
無駄
無駄
```

WaitGroupの Add() メソッドはカウンターを+1(初期値0)し、 Done() メソッドはカウンターを-1します。そして、 Wait() メソッドはWaitGroupのカウンターが 0 になるまで待ちます。これら機能を利用することでメインスレッドが終了してもgoroutineを実行することができます。
注意しなければならないのはカウンターを減算する処理を忘れてしまうといつまで経っても処理が終了しないdead lock状態になってしまいます。試しに func subRush() の defer wg.Done() を消してみてください。dead lockが発生します。
SQLAlchemyにおいて、relationshipでJOINして取得するデータにORDER BY句やWHERE句を適用する

## はじめに
SQLAlchemyではrelationshipを用いて簡単にJOINを行うことができますが、そこにソートや絞り込みなどの条件を追加する方法の日本語記事がなかったので執筆しました。

## 対象者
この記事は下記のような人を対象にしています。

- SQLAlchemyのrelationshipを使用しており、取得条件を追加したい人

## 結論
ProjectsテーブルとUsersテーブル、およびその中間テーブルに相当するProjectUsersを例に記述してみます。
`relationship` にてORDER BY句やWHERE句を指定します。ModelBaseは今回の内容とは直接関係ありませんが、実装の参考にしていただければと思います。


```python:models.php
from sqlalchemy import Column, Integer, String, ForeignKey, DATETIME, func, Boolean
from sqlalchemy.orm import declarative_base, relationship, mapped_column

Base = declarative_base()

# どのテーブルでも基本的に使われるカラムを別途定義し、継承させています。
# sort_orderを使用するとカラムの位置を統一することができ、見栄えがよくなります。
class ModelBase:
    # UUIDを使用する場合はString型になります。
    id = mapped_column(Integer, primary_key=True, sort_order=-10)
    is_hidden = mapped_column(
        Boolean, nullable=False, server_default="0", sort_order=96
    )
    order_num = mapped_column(
        Integer, nullable=False, server_default="1000000", sort_order=97
    )
    created_at = mapped_column(
        DATETIME, nullable=False, server_default=func.now(), sort_order=98
    )
    updated_at = mapped_column(
        DATETIME,
        nullable=False,
        server_default=func.now(),
        onupdate=func.now(),
        sort_order=99,
    )


class ProjectUser(Base, ModelBase):
    __tablename__ = "projects_users"
    project_id = Column(Integer, ForeignKey("projects.id"), nullable=False)
    user_id = Column(Integer, ForeignKey("users.id"), nullable=False)
    user = relationship("User")
    project = relationship("Project")


class Project(Base, ModelBase):
    __tablename__ = "projects"
    title = Column(String(255), server_default="", nullable=False)
    users = relationship(
        "User",
        secondary=ProjectUser.__tablename__,
        back_populates="projects",
        viewonly=True,
    )
    project_user = relationship(
        "ProjectUser",
        overlaps="project",
        # ORDER BY句, WHERE句を指定
        order_by="ProjectUser.order_num",
        primaryjoin="and_(Project.id==ProjectUser.project_id, ProjectUser.is_hidden == 0)",
    )


class User(Base, ModelBase):
    __tablename__ = "users"
    name = Column(String(255), nullable=False)
    projects = relationship(
        "Project",
        secondary=ProjectUser.__tablename__,
        back_populates="users",
        viewonly=True,
    )
    project_user = relationship("ProjectUser", overlaps="user")

```

## おわりに
SQLAlchemyにおいて、relationshipでJOINして取得するデータにORDER BY句やWHERE句を適用するについてまとめました。

## 参考記事
[alembicで作成されるカラムの順序を指定するmapped_columnとsort_orderの使い方](https://qiita.com/RyoSakon001/items/7917fc7476845c507af2)
[SQLAlchemy公式ドキュメント](https://www.sqlalchemy.org/)
neofetchで簡単に環境を伝えよう！

## neofetchとは？

someone「お前のパソコンの環境教えてー」
俺「スッ…（ﾄﾞﾔｯ」

![スクリーンショット 2023-12-30 16.49.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628782/e73de3c3-0f00-c303-16d7-84081832d33a.png)

**はい俺かっこいいー！！！パソコンできるー！！！！！！てんさーい🙌**

ってできます。

## インストール

macOSとUbuntuで動作を確認しました。

macOSの方（homebrewは入れといてね）

``` zsh:
% brew install neofetch
```

ubuntuの方

``` zsh:
% sudo apt install neofetch
```

## 使い方

任意のターミナルウィンドウを開いて

``` zsh
% neofetch
```

するだけです。![スクリーンショット 2023-12-30 16.54.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628782/fae7619a-f7ff-bbfa-8b9f-3fb74c1721df.png)

## おわりに

ごめんなさい
Wagtailにマークダウンテキストエディタを導入

Wagtailで、投稿の本文にコードを記述したかったので
リッチテキストエディタ→マークダウンテキストエディタに変更した時にうまくいい数に苦労したのでやり方をメモします。

マークダウンテキストエディタの導入は色々やり方があるようで
早速、chtatGTPに聞いてみました。
chtatGTPが勧めるやり方で、"wagtailmarkdown"をインストールを試みましたが、
インストールが上手くいかず(バージョンのエラー的な？？)断念。

自分で調べたり、chtatGTPに違う聞き方をして試行錯誤し最終的には
<span style="color: red; ">wagtail-markdown</span>を使うとう結論に至りました。

## wagtail-markdownについて

wagtail-markdownについては、こちらが公式？のドキュメントになります。
https://github.com/torchbox/wagtail-markdown


最初に試したwagtailmarkdownとは、ハイフンがあるかないかくらいし違いがわかりませんが。

## wagtail-markdownでマークダウンのテキストエディタの導入

まずは
```
pip install wagtail-markdown
```

settingディレクトリのbase.pyに、以下のように追記します。

```python:base.py
INSTALLED_APPS = [
    "home",
    "search",
    "wagtail.contrib.forms",
    "wagtail.contrib.redirects",
    "wagtail.embeds",
    "wagtail.sites",
    "wagtail.users",
    "wagtail.snippets",
    "wagtail.documents",
    "wagtail.images",
    "wagtail.search",
    "wagtail.admin",
    "wagtail",
    "wagtailmarkdown",#←追加
    "modelcluster",

    ///省略///
]


///省略///



#追加
WAGTAILMARKDOWN = {
    #"autodownload_fontawesome": False,#←これは要らない
    "allowed_tags": [],  # optional. a list of HTML tags. e.g. ['div', 'p', 'a']
    "allowed_styles": [],  # optional. a list of styles
    "allowed_attributes": {},  # optional. a dict with HTML tag as key and a list of attributes as value
    "allowed_settings_mode": "extend",  # optional. Possible values: "extend" or "override". Defaults to "extend".
    "extensions": [],  # optional. a list of python-markdown supported extensions
    "extension_configs": {},  # optional. a dictionary with the extension name as key, and its configuration as value
    "extensions_settings_mode": "extend",  # optional. Possible values: "extend" or "override". Defaults to "extend".
}


```

ちょっと詰まった点として、ドキュメントでWAGTAILMARKDOWNの中に
"autodownload_fontawesome": False,が入っているのですが
これを書くとてテキストエディタのメニューのアイコンが消えてしまいました。(操作はします。)
アイコンはfontawesomを使っているようで、fontawesomのバージョン変更などのカスタマイズする際は必要なようですが
自分はカスタマイズは不要でとりあえずアイコンがきちんと表示できれば問題なかったので今回は消しました。

これで設定は終わりです。
次は実際に実装していきましょう。

モデル側でマークダウンをう使う場合は以下のように書きます。
```python:models.py
from wagtail.core.models import Page
from wagtailmarkdown.fields import MarkdownField　#　MarkdownFieldインポート

class MyPage(Page):
    body = MarkdownField()
```

MarkdownFieldをインポートすることで、マークダウンの入力フィールドが使用可能になります。
あとは、マークダウンにしたい項目をMarkdownField()にすればOKです。
既に、他のフィールを設定している場合でもMarkdownField()に書き換えて変更できます。

また、テンプレートは一部抜粋ですがこのように書きます。
```html:my_page.html
    <p>
        {% load wagtailmarkdown %}
        {{ page.body|markdown }}
    </p>
```
markdownというフィルタを適応することで、マークダウンがHTMLの表示になります。
このフィルタを使用する前に、 {% load wagtailmarkdown %}を読み込んでおくことに注意ください。

ここまで来たら、マイグレートします。
```
python manage.py makemigrations
python manage.py migrate
```
サーバーを立ち上げ、管理画面を見てみるとマークダウンのエディタになっていることが確認できます。
![marld.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3332453/7c5e993f-edfc-d943-ee22-09d44cf438ce.png)
以上で完了です。
＜簡単＞Pythonで強化学習の環境を構築

# 導入
こんにちは、
12月30日に強化学習をしたくなったので備忘録です、
Akira_0809です、

# 必要なやつら
- Python 
- Gymnasium

Gymnasiumとは前身のOpenAIGymのサポート終了後にメンテナンスを引き継いだ団体によるライブラリです。

# インストール
```python
pip install gymnasium[box2d]
```

今回は、box2dベースの物理演算とPyGameベースのレンダリングを使用した、物理演算に基づいたおもちゃのゲームをインストールします。

他のものは[公式ドキュメント](https://gymnasium.farama.org/)に載っているので気になるものをインストールするといいでしょう。

# コード
コードについて解説していきます。
```python
import gymnasium as gym

env = gym.make("CarRacing-v2", render_mode="human")

observation, info = env.reset()

for _ in range(1000):
    action = env.action_space.sample()
    observation, reward, terminated, truncated, info = env.step(action)
    if terminated or truncated:
        observation, info = env.reset()

env.close()
```
まず初めに謎のレースゲームの環境を作成します。
```python
env = gym.make("CarRacing-v2", render_mode="human")
```
ゲーム環境の初期化を行います。
```python 
observation, info = env.reset()
```
環境からランダムな行動を取得します。
本来であればAIが行動を決定するところです。
```python
action = env.action_space.sample()
```
行動を実行すると環境の状態が更新される。
```python
observation, reward, terminated, truncated, info = env.step(action)
```
ゲームが終了したら、環境を初期化して再開。
```python
if terminated or truncated:
    observation, info = env.reset()
```
環境を閉じる。メモリの解放とかしてる。
```python
env.close()
```

実行すると画面が出てきてプレイ画面が見えるはず、

# 人間がプレイするためには
自分で試してみたいときはライブラリをインストールした所でこれを実行するとプレイできます。
```python
python gymnasium/envs/box2d/car_racing.py
```

# 最後に
意外と簡単に環境構築が出来たので強化学習にチャレンジしてみようと思います。
間違ってたら教えて！！

# 参考文献
https://note.com/kikaben/n/n57584c49d5c2

https://gymnasium.farama.org/

https://github.com/Farama-Foundation/Gymnasium
LambdaでExcelファイルをエクスポートすると文字化けしてしまう

## はじめに
バックエンドにLambdaを使用しているのですが、Excelファイルをそのまま返却しようとすると文字化けしてしまうエラーが発生しましたので、解消方法を記載しておきます。

## 対象者
この記事は下記のような人を対象にしています。

- PythonのopenpyxlでExcelファイルを作成している
- Lambdaを使用している

## 結論
Base64エンコードを使用します。

【サーバー側】
```python:excel.py
import openpyxl
import base64
import tempfile
import json


def export_excel():
    wb = openpyxl.Workbook()
    # ここでシートにデータを書き込む

    with tempfile.NamedTemporaryFile(delete=False, suffix=".xlsx") as tmpfile:
        wb.save(tmpfile.name)
    with open(tmpfile.name, "rb") as excel_file:
        # Lambdaの場合エンコード処理が必要
        encoded_content = base64.b64encode(excel_file.read()).decode()

    response = {
        "headers": {
            "Content-Type": "application/json",
        },
        "body": json.dumps(
            {
                "data": encoded_content,
                "headers": {
                    "Content-Disposition": "attachment; filename=filename.xlsx"
                },
            }
        ),
    }

    return response
```

【フロントエンド側】
```TypeScript:exportExcel.ts
import axios from 'axios'

const exportExcel = async () => {
  try {
    const response = await axios.get(`https://sample.com/excel`)
    const responseBody = JSON.parse(response.data.body)
    const encodedContent = responseBody.data
    const blob = b64toBlob(
      encodedContent,
      'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
    )
    const url = window.URL.createObjectURL(blob)
    const a = document.createElement('a')
    a.href = url
    a.setAttribute('download', 'filename.xlsx')

    document.body.appendChild(a)
    a.click()
    document.body.removeChild(a)
    window.URL.revokeObjectURL(url)
  } catch (error) {
    throw new Error(`${error as string}`)
  }
}

function b64toBlob(b64Data: string, contentType: string): Blob {
  const byteCharacters = atob(b64Data)
  const byteArrays = []

  for (let offset = 0; offset < byteCharacters.length; offset += 512) {
    const slice = byteCharacters.slice(offset, offset + 512)
    const byteNumbers = new Array(slice.length)

    for (let i = 0; i < slice.length; i++) {
      byteNumbers[i] = slice.charCodeAt(i)
    }

    const byteArray = new Uint8Array(byteNumbers)
    byteArrays.push(byteArray)
  }

  return new Blob(byteArrays, { type: contentType })
}

```


## おわりに
LambdaでExcelファイルをエクスポートすると文字化けしてしまうエラーの解消方法についてまとめました。

## 参考記事
[AWS Lambdaで遊ぼう（第6話: Excelをダウンロード）](https://tercel-tech.hatenablog.com/entry/2022/02/02/233703)
GLSLでincludeファイルを対応する

GLSLではHLSLのように`#include`ができないので、テキストファイル化したシェーダーに、インクルードしたいファイルを挿入する形で実装する必要があります。

```C++:Shader.cpp
    Shader::Shader(const char* vertexFile, const char* fragmentFile)
    {
    	//シェーダーをロードする。
    	std::string vertexCode = get_file_contents(vertexFile);
    	std::string fragmentCode = get_file_contents(fragmentFile);

        //インクルードファイルを追加する。
    	vertexCode = AddIncludeShaderFile(vertexCode, "test.h");
    	fragmentCode = AddIncludeShaderFile(fragmentCode, "test.h");
    }
```

```C++:Shader.cpp
	/// <summary>
	/// シェーダーにインクルードファイルを挿入する。
	/// </summary>
	/// <param name="mainShader">元になるシェーダー</param>
	/// <param name="includeFile">インクルードしたいファイル</param>
	/// <returns></returns>
    std::string Shader::AddIncludeShaderFile(
        std::string mainShader,
        const char* includeFile
    ) {
        //ロードするシェーダーはAssets/shader/内にあるので、文字列を追加して読み込む。
    	std::string fileName = "Assets/shader/";
    	fileName = fileName + includeFile;
    	std::string includedShaderCode = get_file_contents(const_cast<char*>(fileName.c_str()));

        //シェーダー内の#include test.hを置き換えたいので、文字列を追加して検索する。
    	std::string includeFileStr = includeFile;
    	includeFileStr = "#include " + includeFileStr;
    
    	// includeする位置にファイルの内容を挿入する
    	size_t includePos = mainShader.find(includeFileStr);
    	if (includePos != std::string::npos) {
    		mainShader.replace(
              includePos,
              strlen(const_cast<char*>(includeFileStr.c_str())),
              includedShaderCode
            );
    	}
    
    	return mainShader;
    }
```
`mainShader.find()`でシェーダー内にある`#include test.h`を検索し、
`mainShader.replace()`で検索した位置にインクルードファイルを置き換えています。
2023年を振り返る、そして2024年の目標

# 自己紹介
 福岡でエンジニアをやっている25歳（エンジニア2年目）です。
 
# 2023年の振り返り
個人的に2023年は変化の多い1年でした。
備忘録としてまとめますので、興味があれば読んでくださると嬉しいです。
## 6月~8月　転職活動

## 悩みと転職活動
去年は新卒1年目で夏に部署配属されてからは主に単体テスト〜総合テストに従事していました。単体テストではSelenideを使用したテストコードの実装、結合〜総合テストでは主に手動テストを経験しました。単体テストのテスト自動化については経験したことがなかったので勉強になった一方で、結合テスト以降のフェーズは「テストってこうやるんだ〜」とか「意外とバグあるんだな〜」とか経験できたのは良かったですが、テスト仕様書に基づいて画面操作を行うといった、言ってしまえば誰でもできるような作業が多かったです。1年目なので仕方ないと思う一方で、「そのシステムについてどれだけ詳しくなるか」が求められていて、エンジニアとしてのスキルが上がる実感が湧かなかったので自分の市場価値に対して不安を覚えていました。個人的には開発に携わって技術力をつけたいと思っていたのですが、2年目になっても状況は変わりそうになかったので、6月ごろから思い切って転職活動をすることにしました。

## 厳しい現実
複数のエージェントとコンタクトを取り、今回はレバテックさんにお世話になりました。私はエンジニアのキャリアとして将来的にはITコンサル、上流SEとしてやっていきたいと考えていたため、上流工程から開発まで携われる現職に入社したので、今回も新卒就活時の軸はブラしませんでした。

結果としては１次選考を通過した企業が１社のみと厳しい結果になり、それと同時に自分の市場価値の低さを痛感しました。選考に通らなかった原因としては、一番は開発経験の少なさ、そして個人開発や技術記事などのアウトプットの少なさが指摘されました。業務で開発に関われるかは個人の能力だけでなくタイミングや運もあるのでなかなかコントロールが難しい部分はありますが、個人開発や技術記事のアウトプットに関しては一応やってはいましたが、ポートフォリオなどは作成していなかったのでそこが選考に進めなかった原因だと痛感しました。これを機に危機感を感じてプライベートでも勉強を継続してするようになりました。またこのタイミングで転職するのは得策ではないと思ったので、転職は見送りました。


## 10月~12月　部署異動と新たな技術の習得
転職活動は失敗に終わりましたが、部署異動をすることができたので、心機一転、異動先の部署で開発に携わることができるようになりました。異動先の部署の案件ではTypeScriptとAngular、Expressが使用されているのでこれらの新たな技術を学びました。今までフロントエンドはほとんど触ったことがなかったので、Angularの学習はつまづくことも多かったですが、開発に携わることができて、非常に充実していました。また、11月からは社内アプリの開発を任され、設計〜テストまで一人で行い、開発もフロント、バックエンド、サーバ構築とフルスタックで行わさせていただきました。そもそも実務での開発経験自体ほとんどなかったので、もちろん土日も勉強して、しんどかった一方で設計から行ったので、デザインパターンを学んで、メンテナンス性を上げるための設計などを考えるのがすごく勉強になったし、楽しかったです。その際の記事は以下に貼っておきます。

https://qiita.com/Kyle1106/items/325272e93b247cd45b43

また応用情報技術試験にも合格することができました。

![スクリーンショット 2023-12-30 16.44.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3458671/bdc7f82b-3bbe-2b84-6fd0-68d98372b1d9.png)



# 2024年をどんな年にするか

2023年は転職活動を経験して、自分の市場価値の低さを痛感して危機感を持ちました。ただ、こういったことに気づくのは早い方がいいので、やって良かったなと思うし、行動したことで状況を改善できたことは良かったです。企業がエンジニアに求めているレベル感も感じることができていい経験になりました。来年は以下のことを頑張りたいです。

## 実務での開発経験を積む
まずは本業重視で実務で経験を積みたんで必要なことを学んでいきたいと思っています。1月以降に案件に本格的にアサインされ、そこではAngularとExpressを使用する予定なので、よりこれらの技術に対する知見を深めたいです。積極的にタスクをとりにいって、技術面以外でも市場価値を上げられる経験をさせてもらえるように努力をしようと思います。

## AWS
転職活動時にエージェントの方からAWSエンジニアの引き合いの多さについて聞き、いつか勉強したいと思っていました。現在はAWSのハンズオンで少し触れたことのある程度のレベルです。実務で触れる可能性は低いですが、もしそのような場面が来たときのために勉強したいと思っています。自分の専門領域をフロントエンドなのかバックエンドなのか、インフラなのかどこにするか分かりませんがどの領域も一通り触れるようにはなりたいので、手広くAWSにも手を出していきたいと思っています。また、AWSクラウドプラクティショナーとソリューションアーキテクトも受けようかなと思っています。

## ポートフォリオ作成
転職時にポートフォリオがないのはネックでした。加えて開発を通じて技術に対する知見を深めたいので今年はポートフォリオを作る予定です。私の現在のスキルレベルとしてはフロントエンドをAngularでバックエンドをNode.jsでDockerで環境をコンテナ管理して、一応Webアプリを作れるくらいのレベル感です。正直、個人でアプリを開発してそれをリリースできるレベルかというとそこまでは達していないです。ですので来年はセキュリティ面なんかも含めて（めっちゃ大雑把なので開発していく中で具現化します）、リリースしても問題ないレベルのアプリが作れるようになりたいです。

## アウトプット活動
転職活動を機に少しずつQiitaなどで発信するようになりましたが、まだ記事の質が低く、量としても多くないので、来年は週１ペースで、読み手にとって有益な記事を書いて発信できたらと思っています。またオフラインのイベントなどに参加したいとずっと思っていますが、結局2023年もオンラインのみの参加でオフラインイベントに参加した経験がないのでそういった場に参加していろんな方と繋がりたいと思ってます。

# まとめ
１年間を振り返るといろんなことがありましたが来年も精進していきます。
最後まで読んでいただきありがとうございました！
PythonだけでWebアプリを作る

![2023-12-30_16h14_16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3655530/3bdb0940-6be0-b068-a80e-662b6e555c1c.png)
## PythonだけでCSVファイルをアップロードしてテーブル表示させる簡単なWebアプリを作る


Streamlitを使ってPythonで手軽にWebアプリを作る。今回はCSVファイルのアップロードとブラウザ上でのテーブル表示させる

### 1. Streamlitのインストール
```bash
pip install streamlit
```

### 2. Pythonスクリプトの作成
```python
import streamlit as st
import pandas as pd

# CSVファイルのアップロード
uploaded_file = st.file_uploader("CSVファイルをアップロードしてください", type=["csv"])

# アップロードされたファイルをデータフレームに読み込む
if uploaded_file is not None:
    df = pd.read_csv(uploaded_file)

    # テーブルの表示
    st.write("### アップロードされたテーブル")
    st.write(df)
```

### 3. アプリの実行
```bash
streamlit run app.py
```

Streamlitを使用して簡単にPythonだけでWebアプリを作成できる。
StreamlitはPythonベースの軽量Webフレームワークで、データ可視化、グラフやチャートを簡単にできるので、機械学習モデルのデモやデータのダッシュボード作成とかになら捗りそう。
PostmanでLaravelのAPIを叩いた時、HTMLが返ってくる現象の解決法

# はじめに
PostmanでLaravelで構築されたapiのテストをしていた時、エラーになるはず(バリデーションに適合しない)リクエストを送っているのに、返ってきたボディはHTMLだしステータスコードも`200`だしで意味がわからなかったので調べました。

# 問題
こんなかんじ。一応FE側(React)ではErrorがちゃんと返ってきており、メッセージも存在している。
![スクリーンショット 2023-12-30 15.33.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2778030/cace5f4b-b2f3-e18c-c547-8e170fda6aa9.png)

# 解決方法
リクエストのヘッダーの`Accept`を`application/json`に設定する。
![スクリーンショット 2023-12-30 15.42.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2778030/12eb7a61-80cb-02db-94b3-0f8b730fa322.png)

デフォルトでは`*/*`となっていますが、これは「どういった形式で返してくれてもいい」という宣言らしいです。

これだとLaravelはHTMLで返してくるっぽいです。(HTMLが返ってきたことを"正常だ"とPostmanが判定してステータスコードが200になる?)

人間的には`json`で返してくれた方が嬉しいので、`application/json`、つまり`json`で返してくれと明示的に宣言することで`json`形式でレスポンスを返してくれます。

# おわりに
この辺りの知識が薄いので時間をつかってしまいました。
もっと勉強しなきゃなりませんね...
ChatGPTによる「蜘蛛の糸」のストーリー変更

# はじめに
遠い昔、ラジオ放送でやってました。著名な作家の小説のストーリーを変えて朗読するコーナー。ストーリーは変えますが、その作品らしさ、文章の言い回しなどを変えません。
そんなストーリー変更がChatGPTならば可能かな、と思ってやってみました。青空文庫の芥川龍之介作「蜘蛛の糸」の一説をChatGPTに読ませて、変更を指示します。

https://www.aozora.gr.jp/index.html

「蜘蛛の糸」のストーリーは、ハッピーエンドとは言えません。これをハッピーエンドにします。ChatGPTに指示したのは、以下２点です。

* 蜘蛛の糸が切れずに全員が地獄から抜け出すことができた
* 最後、御釈迦様が大喜びする

# 結果

以下、ChatGPTが出力した文章です。場面は蜘蛛の糸を手繰って主人公カンダタ（青空文庫では犍陀多にルビ）と大勢の罪人が昇ってくるところです。

> もし途中で断たれてしまったら、せっかくここまで登ってきた自分も、元の地獄に逆戻りしなければなりません。そんな事態になったら、とても困ります。
しかし、驚くべきことに、蜘蛛の糸がしっかりと全員を支え続けています。数え切れないほどの罪人たちが一列になって登ってきて、地獄から抜け出すことができたのです！カンダタは喜びに満ちた表情で、周囲を見渡しました。彼らは皆、自由の地へと歩みを進めるのです。
御釈迦様は極楽の蓮池のふちに立ち、この一部始終をじっと見ておられました。そして、カンダタたちが地獄から抜け出し、成功したことを知った瞬間、御釈迦様 の顔は大きな喜びに包まれました。御顔を輝かせ、手を打ち合わせながら喜びの言葉を述べられました。「素晴らしい！ この蜘蛛の糸は巧妙に織りなされており、全員が地獄から抜け出すことができたのだ。皆さん、おめでとうございます！」御釈迦様 の声は喜びに満ち、極楽の空に響き渡りました。
《略》
御釈迦様 は大きな喜びを胸に抱き、蓮池のふちに立っている全員を見渡しました。彼らが地獄から抜け出し、極楽の自由な世界に辿り着いたことに心から感謝し、幸せな笑顔を浮かべました。「おめでとう！ 皆さんは本当に立派です。自分の力だけで抜け出すのではなく、困難を乗り越えて助け合い、結束して上に登ってきた。これが真の勇気と団結の証です。極楽への道は険しいものでしたが、皆さんの努力と信念が報われました。喜びの瞬間です！」御釈迦様 の言葉は皆の心に響き渡り、大いなる喜びと感動が広がりました。
極楽の空は、幸せと喜びで満たされ、御釈迦様 の御姿は輝きに包まれました。全員が幸せに微笑み合いながら、新たなる自由の旅路を歩み始めました。

《略》の部位は、原文とほぼ同じ文章です。この文章は糸が切れたことに御釈迦様がネガティブに反応する内容となっていますので、矛盾する文脈になっていました。

# 終わりに
ストーリーは気持ち悪く陳腐な内容に変換されました。内容はちっとも面白くはありません。作家が絶対選ばないようなストーリーに変えて、でも、語り口が同じようなところを楽しむという点では、まあまあ面白いかなと思います。また、原文の方が圧倒的な味わいがあることも分かります。
著作権を考慮すれば、どの作品でも行えるわけではありません。そして《略》の部位のように文脈がおかしくなる点、完全自動というわけにはいかないでしょう。やっぱり、人の推敲が必要です。
NSX-V、NSX（NSX-T）環境間の移行テストとNSX-Vへ切り戻したVMの通信不可について

NSX-V環境とNSX（NSX-T）環境の2面をネスト環境で準備し、Advanced Cross vCenter vMotionによる移行を試しました。

また、NSX環境からNSX-V環境へ切り戻した場合の、仮想マシンの通信不可事象と回避策について記載します。

## 準備した環境

ネストでESXi 7.0u3環境を4ホスト分用意し、それぞれ2ホストづつNSX-V用とNSX用としました。ネストのESXiが接続されているネットワークはVLAN TrunkのNSX VLANセグメントを用意して使用しています。

NSXのバージョンは下記になります。
NSX-V: 6.4.14
NSX: 4.1.1

![画像3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3657075/523fca95-e7e6-1888-2ce7-b7ff13e0266a.png)




NSX-V、NSXそれぞれでVLANセグメントを用意し、移行用と分散FW確認用の仮想マシン、移行中にPingを打つ先となるゲートウェイを準備しています。

![画像4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3657075/ad5d0635-3e52-2a7e-7675-9ba5990219cc.png)



## 移行テスト結果
移行 : NSX-V環境からNSX環境への移行
切り戻し : NSX環境からNSX-V環境への移行

※両環境とも同等の分散FWルールを適用しています



|  | GWへのPing | 他VMへの通信 |
|:-:|:-:|:-:|
| 移行1回目  | 欠け無し  | 分散FW通り可能  |
| 切り戻し1回目 | 移行後通信不可  | 移行後通信不可  |
| 移行2回目  | 欠け無し  | 分散FW通り可能  |
| 切り戻し2回目  | 移行後通信不可  | 移行後通信不可  |
| 移行3回目  | 欠け無し  | 分散FW通り可能  |
| 切り戻し3回目  | 移行後通信不可  | 移行後通信不可  |

移行と切り戻しを3回づつ実施し、各回とも同様の結果となりました。
NSX-V環境からNSX環境への移行については特に問題ありませんでしたが
NSX環境からNSX-V環境へ切り戻したVMは通信不可となりました。

切り戻しの際の通信不可事象については仮想マシンvNICの再接続にて復旧しました。


## 切り戻し時の通信不可を回避

NSX-V環境へ切り戻しの移行を行う前に、NSX（NSX-T）環境側で該当の仮想マシンに対して、分散ファイアウォールの除外設定を入れることで、移行後の通信不可を回避できます。（下記リンクの設定手順）
[ファイアウォール除外リストの管理](https://docs.vmware.com/jp/VMware-NSX/4.1/administration/GUID-3B3C278D-4E35-4CE9-A4E2-ED6B1F25ABCE.html "ファイアウォール除外リストの管理")


NSXからNSX-V環境へ切り戻す際に、該当の仮想マシンへ分散ファイアウォールの除外設定をいれてから実施した場合、試験の結果は下記となりました。

|  | GWへのPing | 他VMへの通信 |
|:-:|:-:|:-:|
| 移行1回目  | 欠け無し  | 分散FW通り可能  |
| 切り戻し1回目 | 欠け無し  | 分散FW通り可能  |
| 移行2回目  | 1パケット  | 分散FW通り可能  |
| 切り戻し2回目  | 欠け無し  | 分散FW通り可能  |
| 移行3回目  | 欠け無し  | 分散FW通り可能  |
| 切り戻し3回目  | 欠け無し  | 分散FW通り可能  |

1点、NSX環境からNSX-V環境へ仮想マシンを移行後に、ファイアウォール除外リストに設定したメンバー仮想マシンがNSX環境側で存在しなくなるため、除外リストに設定したグループから該当の仮想マシンを削除時には「削除されたエンティティを表示」というリンクから残ったUUIDを削除するという流れになります。（仮想マシンのリストには表示されません）

![画像7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3657075/d5daba81-f7bc-3914-fe89-9bbd4413f945.png)
Tomcat起動／停止を実行した際にコンソールが文字化けしている【解決方法】

# Tomcat起動（及び停止）を実行した際にコンソールが文字化けしている【解決方法】

Tomcatを起動する為「startup.bat」を実行した際に、
以下のように文字化けしている。

![スクリーンショット 2023-12-30 151632.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1162001/4d7ac340-a2b6-2297-3426-5485fdc925f9.png)

## 解決方法
Tomcatのconfフォルダにある「logging.properties」ファイルを編集すると解決出来ます。
「java.util.logging.ConsoleHandler.encoding」を検索して、指定している文字コードを変更します。
今回の場合UTF-8から、Windowsでよく使われる「SJIS」に変更しました。

```
java.util.logging.ConsoleHandler.encoding = SJIS
```

変更したらTomcatを再起動してください。

![スクリーンショット 2023-12-30 153616.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1162001/3fc22b7d-d908-8bd8-edfb-b5716a28ac2e.png)



※Windowsでの操作方法を想定しています。
Wordpressで過去の時間なのに、予約投稿になっている矛盾について

WordPressで記事を書く際に、予約投稿という機能を使用したことはありますでしょうか。

意外と変なところではまっていたので、このことについて共有します。

結論から言うと協定世界時（以下、UTC）を+9に設定すると治ります。
Wordpressの予約投稿は、設定しているUTCに基づいてWordpressの予約投稿は行われるので、UTC設定を行わなければいけないわけです。

# 問題発生の経緯
今回は下記のようなロゴスライダーを作成しようとしていました。
![](https://gyazo.com/13e3c1cbc3fc7a3e50f2f89c41638715.png)

上記のものを作成する際に、並び順を変えたいと思いOrder方法を見ると、簡単に変えられそうなものは名前か日付だけでした。

このことからそれぞれのロゴの投稿を１時間ずらして投稿したことにすることで並び替えキーを作成しようという考えに至ったわけです。

# 問題発生
それぞれに設定していくと、７番目のロゴに問題が発生しました。
なんと、現在時刻を上回っているにもかかわらず、予約投稿になってしまいました。
これにより、画像が表示されなくなってしまうエラーにハマりました。


冒頭でも紹介しましたが、Wordpress内部はUTCに基づいて動いております。
そのため、下記の画像でいうところの、日本での現在時刻ではなく、UTC+0のロンドンの時刻に基づくわけです。
![](https://gyazo.com/139f207515b9c8be48ff5b344f38531f.png)

# 問題解決
原因がUTCの設定だとわかりました。早速解決に向かって設定していきましょう。

日本の時間協定世界時は+9です。
![](https://gyazo.com/634cd4fd57d39e1d889d44b06d6d29bf.png)

早速、設定してみましょう。
設定 > タイムゾーン > UTC+9
![](https://gyazo.com/337ab18796023eac05840a4fdf131e59.png)

これで設定が完了しました。

この後、5分くらいしたら設定が反映されてうまく表示されるように治りました。
ここまで読んでくださり、大変ありがとうございます。

今後とも、皆さんの役に立つ記事を発信していきます。

参考にさせていただいたサイト：
[WordPressで記事の予約投稿をする方法と失敗の対処法](https://www.sungrove.co.jp/reserve-post/)
Powershellでパスワード変更（TeamViewerで標準ユーザ編）

powershellを7.4にしておく、そのあとrun as administratorで起動
```
PS>Get-LocalUser
Name               Enabled Description
----               ------- -----------
ユーザ名              True

PS>$PASSWORD = ConvertTo-SecureString -AsPlainText -Force -String "変更したいパスワード"
PS>Set-LocalUser -Name "パスワード変更するユーザ名" -Password $PASSWORD
```
WebSocket connection Errorについて

## 環境

### kubernetes
- ubuntu：22.0.4.1 LTS
- kubernetes：1.28.2
- containerd：1.6.24
- IP Address: 172.21.0.8

### Nodejsコンテナ
- node:18.17.1-alpine3.17
- react@18.2.0
- 環境変数
    - PORT=3000

### 構成
- NodePortサービス
    - TargetPort: 3000/TCP
    - NodePort: 30669/TCP

## 事象
kubernetesでReactを起動し、ブラウザでアクセスしたところ以下のログが発生。

```Error
WebSocket connection to 'ws://<IP Address>:<Port No.>/ws' failed:
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2604097/dc8ff607-6535-765f-07b6-553a4519e2a1.png)

最初のアクセスは問題なくアクセスできるためページは表示され、その後上記のログがわらわらと出力されました。
エラーログをよくよく見てみると、アクセス先のPort番号が、コンテナで設定した環境変数（PORT=3000）になっているのが原因っぽいです。
いろいろ調べてみましたが、ホットリロード用のポートの指定が別途必要そうです。

## トラブルシューティング
以下の環境変数を設定して、npm startを実行する。

```cmd
WDS_SOCKET_PORT=0
```

そのため、ConfigMapを作成し、Podに適用させるようにしました。

### ConfigMapを作成
データのキー名は適当で大丈夫です。

```configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  labels:
    app: develop
  name: develop-client-cm
  namespace: develop
data:
  socket_port: "0"
```

### Podへの適用
valueFromを利用して、環境変数の設定を追加してあげます。

```pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name:
    develop-client
  labels:
    app: develop-client
  namespace: develop
spec:
  containers:
  - env:
    - name: WDS_SOCKET_PORT
      valueFrom:
        configMapKeyRef:
          name: develop-client-cm
          key: socket_port
```

上記の環境変数を設定することで、ログが止まりました。
おそらくプロキシを利用している場合も設定が必要になると思います。

## 参考記事
https://github.com/webpack/webpack-dev-server/issues/4217
https://create-react-app.dev/docs/advanced-configuration/
【2023年版】pydantic2でFastAPIでスネークケースをキャメルケースに自動変換する

## はじめに
Pythonがスネークケースが主流であるのに対し、JavaScriptはキャメルケースが主流です。
JSONを返却する際にキャメルケースに変換する方法をまとめました。
FastAPIは、2023年現在ではまだまだ発展途上のフレームワークで、記述方法も都度変わっています。2023年時点での最新情報であることをご了承ください。

## 対象者
この記事は下記のような人を対象にしています。

- FastAPIでAPI開発をしている人
- pydantic1を使用していたが、

## 前提
- **FastAPIのバージョン: 0.103.2**
- **pydanticのバージョン: 2.4.2**


pydanticのバージョンが古い場合とは書き方が異なることに十分ご注意ください。

## 結論
データベースからUserデータを取得し、JSON形式で返却する場合を考えます。
`ConfigDict` を使用するのがポイントです。

```python:user_schema.py
from pydantic import BaseModel, ConfigDict
from pydantic.alias_generators import to_camel

class BaseSchema(BaseModel):
    model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True)


class UserSchema(BaseSchema):
    id: int
    name: str
```

## おわりに
【2023年版】 pydantic2でFastAPIでスネークケースをキャメルケースに自動変換するについてまとめました。

## 参考記事
[Convert FastAPI snake case json response to camel case](https://medium.com/@drewscatterday/convert-fastapi-snake-case-json-response-to-camel-case-d94c20e92b52)
[FastAPIがPydantic v2対応したので、V2移行のポイントを紹介する(意外と簡単)](https://zenn.dev/tk_resilie/articles/fastapi0100_pydanticv2#(%E6%8E%A8%E5%A5%A8)to_camel%E3%81%AE%E6%A8%99%E6%BA%96%E6%90%AD%E8%BC%89%E3%81%8A%E3%82%88%E3%81%B3allow_population_by_field_name%E3%81%8Cpopulate_by_name%E3%81%AB%E5%A4%89%E6%9B%B4%E3%81%AB%E3%81%AA%E3%81%A3%E3%81%9F)
C# : Brushes型とColor型の変換

# Brushes型とColor型の変換

https://hikaruright.hatenadiary.org/entry/20111124/1322102015

http://surigoma.net/wiki/index.php?Color%A4%F2Brush%A4%CB%CA%D1%B4%B9%A4%B9%A4%EB

```C
new SolidColorBrush(Color );
```
で変換できるはずだが・・・

`SolidColorBrush`が見つからない

多分、NET6より前とかはできたんじゃないかな・・・

## 解決

https://learn.microsoft.com/ja-jp/dotnet/desktop/winforms/advanced/how-to-create-a-solid-brush?view=netframeworkdesktop-4.8

```C#
var color = new System.Drawing.SolidBrush(f3_text_color);

g.FillPolygon(color, offset_polygon(ps1, x, y), FillMode.Winding);
```
これは通った！

`new System.Drawing.SolidBrush(Color)`とすれば変換できるみたいです。
nginx プロキシサーバ

## nginx プロキシサーバにDjangoをバインドする

Django のアプリケーションはエラー無く、python3 manage.py runserver が起動するものとする。
つまり、nginx 側の設定を紹介することにします。

### 環境

Ubuntu 22.04.02 LTS
nginx 1.18.0
Python 3.10.12
Django 5.0

```bash:/etc/nginx/nginx.conf

#実際に記入するときには、{}を外して書いてください。
#以下のupstreamとserverの記述を追加してください。

http {
    upstream {xxx.mydomain.net} {
        server 127.0.0.1:8000;
    }

    server {
        listen 80;
        server_name {xxx.mydomain.net};
        root /home/{ユーザー名}/{Django-App};

        location / {
            proxy_pass http://{xxx.mydomain.net};
        }
    }
}
```

次に、settings.pyです。

```bash:settings.py
ALLOWED_HOSTS = ['*']

もしくは

#実際に記入するときには、{}を外して書いてください。

ALLOWED_HOSTS = ['{xxx.mydomain.net}']

#ワイルドカードでも、ホスト名が決まっていればそれを書いても動きます。
#無いと正常に動きません

```
text型のindex設定時は長さを指定する

# 前提
- Rails 7.0.4
- MySQL8系（docker）

# エラー内容
text型のindex設定でマイグレーションしたら

```
add_index :table_name, [:not_text_column, :text_column]
add_index :table_name, :text_column
```

エラーが起きた

```
...
rails aborted!
StandardError: An error has occurred, all later migrations canceled:

Mysql2::Error: BLOB/TEXT column 'normalized_value' used in key specification without a key length
...
Caused by:
ActiveRecord::StatementInvalid: Mysql2::Error: BLOB/TEXT column 'normalized_value' used in key specification without a key length
...
Caused by:
Mysql2::Error: BLOB/TEXT column 'normalized_value' used in key specification without a key length
```

# 対応
[こちら](https://www.utakata.work/entry/mysql/index-length)を参考にすると、MySQLでは文字列カラムのインデックス長が制限されているとのこと

[Railsドキュメント](https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-add_index)を参考に、インデックス長を指定すればOK

```
add_index :table_name, [:not_text_column, :text_column], length: {text_column: 10}
add_index :table_name, :text_column, length: 10
```
【Rails】　local: trueについて

# local: trueについて

## local: trueとは
Railsはデフォルトで同期処理になっています。
しかし、form_withはデフォルトで非同期処理(ajax)になっています。
※Rails5.2< あなたが使用しているRailsのバージョン < Rails 7.0の状態の場合

そこで、下記のようにform_withにlocal: trueオプションを付与することで、form_withが非同期処理から同期処理に変更されます。
それがlocal: trueオプションになります。

```ruby
<%= form_with model: @board, class: 'new_board', local: true do |f| %>
        <div class="mb-3">
          <%= f.label :title %>
          <%= f.text_field :title, class: 'form-control' %>
        </div>
         <div class="mb-3">
          <%= f.label :body %>
          <%= f.text_area :body, class: 'form-control', rows: '10', style: 'height: 200px' %>
        </div>
           <%= f.submit class: 'btn btn-primary'%>
      <% end %>
```

しかし、現在Railsを使用している方は大方Rails７以降のバージョンを使用されていると思います。

Rails7以前のRails6.0代ではform_withが非同期処理になっていたことから、ページ全体のリロードが必要であり、その際には
```ruby
<%= form_with model: @board, class: 'new_board', local: true do |f| %>
```
と記載する必要があった。
ex)エラーのフラッシュメッセージの表示等
（Rails5.2以前はデフォルトで同期処理になっていた）

しかし、Rails7.0以降からは、再度、form_withが同期処理（Railsのデフォルトと同じ仕様）に戻ったため、
```ruby
local: true
```
の明記することなく、form_withを使用しても、同期処理のままとなり、エラーメッセージも問題なく表示されるようになっている。

**バージョンによって仕様が変更されている**
Railsに限りませんが、バージョンをごとに使用が変更されているので、ドキュメントや、最新の情報にアクセスすることが重要だと実感しました。

私はRails7で実装していたのですが、Rails6の記事を参考にしており、Rails6のform_withが非同期処理なら、Rails7も非同期だろうと**勝手な憶測を持って実装していました**が、この意識で実装していると後々大事故に発展しかねないといい教訓になりました。

初学者の方は、私も含め割とそのような感覚で実装されているからが多いと思います。

これからはしっかり上記の意識を持って学習していこうと思います。