OmniAICreator/Qiita-1.07M huggingface dataset:
Next.jsとLaravelで作ったアプリをAWSにデプロイ出来なかった話

### はじめに
- 今回のお話はAWSにアプリをデプロイ**出来なかった**話です。
あんまり技術的な学びはないので、真面目な気持ちでクリックしてしまったあなたはブラウザバックしてください。
逆に仕事が捗らなくて技術記事読むフリして休憩したいそんなあなたにはピッタリの内容になっております。

### どんなアプリを作ったか
- 今回の本筋ではないのですが、デプロイ出来なかったのでここでアプリを供養しておきます。
簡単なTodoアプリで、以前Nuxt.jsとFirebaseで作ったものをNext.jsとLaravelで作り直したという感じです。
未経験からエンジニア転職する際に作ったポートフォリオだったので、思い入れのあるアプリです。
- CSSフレームワークとしてChakraUIを使いました。
class名でスタイリングしていくわけではなく、スタイリングされたcomponentを使う形式です。
型定義バッチリされてるし、propsで値渡せばそこそこ柔軟にスタイル変えられるし最高でした。
- リポジトリは[こちら](https://github.com/chillout2san/next_laravel_todoapp)
ローカルでの立ち上げ方もREADME付けているので、是非触ってもらえたら嬉しいです。
![スクリーンショット 2022-02-28 0.13.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/973482/92d36c6a-5b97-c7a3-e2de-4aabda5d35fc.png)

### やろうとしたこと
- Next.jsをVercelにデプロイ。
- LaravelとMySQLをAWSのEC2にデプロイ。
    - 初めてAWSを触るので、学習コストを少しでも下げるためにRDSは使わない。

### 出来たことと挫折したこと
- Next.jsをVercelにデプロイ。
    - 簡単とは聞いていたが、ビックリするくらい簡単だった。何回かクリックしたらデプロイが終わった。
    - masterブランチにpushする度に自動デプロイされて、便利なことこの上なかった。
- LaravelとMySQLをAWSのEC2にデプロイ。
    - タイトル詐欺と言われそうだが、実はEC2にLaravelとMySQLをデプロイ自体は出来た。
    もう少し言うとローカルのフロントからEC2上のLaravelのAPIを叩くところまでは出来た。
    - CORSエラーに苦しんだが、nginxでリクエストにheaderを付けることで解消出来た。
    - ELBを使っていなかったので、EC2はhttpのままで、Vercelはhttpsだったので、プロトコルが違って通信出来なかった。今回ここで挫折。

### そもそもやらなかった（が今後やりたい）こと
- Next.js
    - getStaticPropsとgetServerPropsは使わずじまいだった。useEffectで誤魔化してました。
    - 初めてのNext.jsだったので、Recoilは使わず。useContextで行きました。
    - パフォーマンスの意識。とにかく動けばOKの精神で作ってしまった。
- Laravel
    - ステートフルにしたかったが、Laravelのsessionの使い方がよく分からず見送り。
    - [以前の記事](https://qiita.com/2san/items/57d6a2cbe053dd314223)でも書きましたが、モダンフロントとLaravelの組み合わせの記事が本当に少なくて、Blade前提のものばかりで参考にならない・・・。
    - CORSとCSRFはどちらもちゃんと対策できず、セキュリティガバガバアプリになった。

### 今後勉強しないといけないなと思ったこと
- Linux
    - 参考文献のコマンドをコピペで打ってデプロイしただけなので、あんまりコマンドの意味も分からずやっていた。
    - 普段意識しないようなパーミッションの概念が全然分かっていなかった。
- nginx(別にapacheでも良いけど）
    - 前述の通りCORSエラーにめっちゃ苦しめられた。サーバーアプリはちゃんと勉強しておかないとインフラの理解なんて出来る訳がないなと痛感した。
- AWS
    - 当たり前だけど、AWS自体も全然分かっていなかった。一度に色んなことをやろうとしたのが敗因。
    - 基本的なVPC、EC2、Route53、ELBあたりを一つずつちゃんと学習するべきだった。

### さいごに
- 暗い記事になってしまいましたが、初めてNext.js、AWSを触れたので学びは多かったです。
- 休憩ということで、次はバックエンドとインフラをAmplifyとかFirebase使って、フロントに集中したアプリを作りたい。

### 参考文献
- [【AWS入門】EC2+NGINX+MySQL環境へLaravelをデプロイする手順](https://brainlog.jp/server/aws/post-2808/)
- [nginxのadd_headerでレスポンスヘッダが付かない時の対処法](https://www.mussyu1204.com/wordpress/it/?p=1425)
- [【初心者向け】AWSのサービスを使ってWebサーバーをHTTPS化する](https://dev.classmethod.jp/articles/aws-web-server-https-for-beginner/)
「マイクロサービスパターン」の復習 5章 ビジネスロジック

## 概要

- Java読書会でせっかく勉強したのにつぎつぎと忘れていくので、印象に残ったところを記録していく

http://www.javareading.com/bof/

## 5章 マイクロサービスアーキテクチャにおけるビジネスロジックの設計

ビジネスロジックを設計上、どのように構築していくか

## Transaction scriptパターン

- もっともシンプルな形態
- ビジネスロジックはサービスクラスに持たせて、エンティティクラスはデータだけ
- エンンティティの方はORマッパーでマッピングする
- 利点
    - 設計が簡単
        - エンティティ側がデータだけなので、責務について悩むこともない
    - ロジックとデータが分離されている
        - ビジネスロジックとデータ（データベースのスキーマ）の変更頻度・ライフサイクルは違うので別れている方が良いという考え方もある
- 欠点
    - ビジネスロジックの重複
        - ビジネスロジック間の共通ロジックについて、共通化がきれいにできない
        - ロジックは全部サービス側の責務になってしまっているので、共通化したとしても結構汚くなるでしょう。例えば、AbstractServiceみたいな親クラスを作って、共通ロジックを親クラス持ちにするのもよくあるけど、数あるサブクラスのうち2つの共通化にしかなってないとそれは親クラスの責務なのか？となるでしょう。

Transaction Scriptについては、利点も協力だけど、この本のスタンスとしては、より柔軟で強力なDomain Modelパターン（とその系譜）の方が複雑なロジックを実現するには適しているとしている。

## Domain Modelパターン

- 柔軟かつ強力な形態
- 外部からの要求の受け口となるサービスクラスは入口となるメソッドはもつが、ほとんどのビジネスロジックはモデルクラスが持ち、サービスクラスはモデルのメソッドを呼ぶだけ
- 利点
    - （オブジェクト指向的な意味で）設計が分かりやすい
        - モデルクラスがデータとそれに関連するビジネスロジックを持つので、オブくジェクト指向的な役割分担がなされる
        - 結果的に共通化が促進される（共通ロジックもどのクラスの責務なのかが明確になり各モデルクラスに分散されていく）
    - テストがしやすい
        - これが真実かどうかは作り方によるが、この本に出てくるモデルクラスはそれなりにテストしやすくなっている
    - オブジェクト指向の本来持っている柔軟性が活かしやすい
        - GOFのデザインパターンなどが活かしやすい
- 欠点
    - 設計難易度が高い
    - ORマッパーとの使い方に気を使う
        - Transaction Scriptならテーブルとのマッピング用なので、クラスからスキーマを作るので、スキーマからクラスを作るのでもどちらでも良かったけど、Domainモデルでは単純にはいかない。

個人的にはDomain Modelパターンは、うまく行けば綺麗だけど、設計が崩れないよう、細心の注意を払わなければいけないので、かならずしも好きではないです


## Aggregateパターン

- AggregateはもともとDDDの言葉。
- モデリング後の各エンティティを、従属関係、ライフサイクル、意味論などの観点からグルーピンングし、各グループをAggregateと呼ぶ
- 例
    - Orderアグリゲートとは、Order、PaymentInfo、DeliveryInfo、OrderLineItemの集合となる。アグリゲートの中で特に中核となるものをアグリゲートルートとよぶ
    - 通常のドメインモデルであっても実際に作ったことのある人ならOrderLineItemはOrderの一部分であり、Orderが削除されるときにはOrderLineItemも削除されるのは感覚的に分かるはず
    - 一方で、Orderテーブルの外部キーとして、Restaurantへの参照があったとしも、RestaurantはOrderとは別のライフサイクルを持つことも感覚的に分かるはず
    - Aggregateパターンでは、これらを明確にする
- Aggregateパターンでは、Aggregateが整合性の単位となる。
- 例
    - OrderLineItemを個別に作成・削除するのではなく、Orderに対する更新処理として表現する
    - 実装上はTransactionが使えるなら、一つのTransactionに収まるようにする

## アグリゲートとマイクロサービス

- 概ねマイクロサービスとアグリゲートを1対1にすると、それらしい設計になる
    - 場合によっては1対多でもいいのかもしれない。
    - 単純なDomain Modelだと、Domain Modelのどの部分がどのサービスに担当になるのか不明瞭
    - アグリゲートというモデル上の境界を導入することで、サービスへの対応がしやすくなる
    - この章の本題であるビジネスロジックは、各アグリゲートが持つようにする
        - Domain Modelを強化したようなもの
- 例：OrderサービスとOrderアグリゲート、KitchenサービスとKitchenアグリゲート
- アグリゲートをまたがる参照は、主キーの参照を持つようにする
- サーガによって、サービスを渡り歩く処理をする場合は、1つのアグリゲートごとに１つのトランザクションを作る


## Domain Eventパターン

- アグリゲートが生成、更新されたときはイベントを発行するということ
  - 単位がアグリゲートなのが大事
- イベントの用途1：ユーザーへの通知
    - 例えばOrderの配送開始をユーザーにメールで通知するなど
    - Orderの更新処理の一部として、メール通知のロジックも含めることでも実現可能である
    - イベント経由にしておいたほうが、関心事が分離されて、拡張性もある。
        - 通知手段はいくつでも増やせるなど
- イベントの用途2：別のマイクロサービスへの通知
    - 7章のCQRSパターンのように、更新とクエリーが別サービスになっている場合は、クエリー側のサービスは更新側サービスからイベントを拾って、クエリーのためのDBを更新する
    - マイクロサービスでは、各サービスごとにDBをもつので、結果的に何らかミラーリングが必要になることも多くなる。そのためにはイベントは欠かせなくなる

## 感想

- アグリゲートは、マイクロサービスの構成を考える上で、根幹をなす考え方だと思われる
- イベントというのは組み込みでは当たり前のものだけど、ついにWebの世界でもイベントが必要な時代になったかと感じる
Spring boot でBootstrap、jQueryを使用する（WebJars）

Spring Bootを学習中、Webjarsという便利なライブラリに触れたので、
使い方を忘れないようにアウトプットする🖋

## 前提条件
♦︎Spring-boot-suite：4-4.13.1

# WebJarsとは
Javascriptやcssのライブラリを、Mavenで利用できるようにするライブラリのこと。

## 早速実装!

### 実装するもの
①jQuery 3.5.1
②Bootstrap 4.5.3
③WebJars-Locator

### WebJars-Locatorとは？？
WebJars-Locatorを使用すると、WebJarsで使用するライブラリのバージョンを意識せずコードを書けるようになる優れもの。具体的にどういうことかは後述。

## ①pom.xmlのdependenciesタグ内に下記を追記し、`Maven インストール`を実行

```xml
        <!-- jQuery -->
		<dependency>
			<groupId>org.webjars</groupId>
			<artifactId>jquery</artifactId>
			<version>3.5.1</version>
		</dependency>
		
		<!-- bootstrap -->
		<dependency>
			<groupId>org.webjars</groupId>
			<artifactId>bootstrap</artifactId>
			<version>4.5.3</version>
		</dependency>
		
		<!-- webjars-locator -->
		<dependency>
			<groupId>org.webjars</groupId>
			<artifactId>webjars-locator</artifactId>
			<version>0.40</version>
		</dependency>

```

インストールが完了後すると、`Maven依存関係`内にjQuery3.5.1のjarファイルが入る。
![スクリーンショット 2022-02-28 1.03.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/754263/c156f97c-0b41-44a9-095b-75bf655d7097.png)


## ②htmlファイルにパスを追加して実装完了！！
```html
<head>
<!-- CSS読み込み -->
<link rel="stylesheet"
	th:href="@{/webjars/bootstrap/css/bootstrap.min.css}">
<!-- JS読み込み -->
<script th:src="@{/webjars/jquery/jquery.min.js}" defer></script>
<script th:src="@{/webjars/bootstrap/js/bootstrap.min.js}" defer></script>
</head>
```

♦︎パスの指定にはthymeleafを使用

#### ここでWebJars-Locatorの出番✨
本来はMavan依存関係のフォルダ構成と同じ、3.5.1も記載しないといけないので、下記のようなパスになる。

```html
<script th:src="@{/webjars/jquery/3.5.1/jquery.min.js}" defer>
```
しかしWebJars-Locatorを入れておけば、バージョン番号の分を省略する事ができ、WebJarsで使用するライブラリのバージョンを変えても、パスのコードを変更する手間が省ける。✨

##### ※補足 deferについて
deferはHTMLの属性の一つ。
defer属性をつけると、HTMLの読み込みと　Javascriptの読み込みを並行して実施してくれるので、画面表示の性能が改善される！


## 参考文献
♦︎WebJars 使い方メモ
https://qiita.com/opengl-8080/items/c8c5f787613c230a9827

♦︎ライブラリとは
https://wa3.i-3-i.info/word1473.html

♦︎Spring boot spring 解体新書
Amazon EFS を EC2 インスタンスにマウントする手順のメモ

# はじめに

掲題通り。 よく忘れて、都度検索してしまうのでメモ。
Amazon Linux 2 での動作させることを想定。

# 準備 (EFS以外のリソース)

- [EFS を作成する VPC で「DNS解決の有効化」および「DNSホスト名を有効化」しておく](https://docs.aws.amazon.com/ja_jp/efs/latest/ug/mounting-fs-mount-cmd-dns-name.html)
    - ウィザードなどに頼らずにVPCを作成した場合、デフォルトでDNSホスト名が無効になっているため注意
    - 単にDNSホスト名を有効化しただけだと、なぜか反映されない時があったが、その時は改めて「DNS解決」および「DNSホスト名」を無効化⇒有効化したら反映された
- EFS へのアクセス元からの `TCP: 2049` インバウンドアクセスを許可するセキュリティグループを作成しておく
    - ここでは名前を `efs` とする。


# EFS の作成

Management Console から利用するVPCを選んで作成していく。
この時、EFSのマウントターゲットを指定する際、先の手順で作成したセキュリティグループ `efs` を指定してアクセスを許可する。
EFSのマウントターゲットのsubnetを指定するが、**Q. 作成時のsubnet指定は、EFSをマウントするEC2インスタンスが所属しているsubnetを選択すればよいですか？** の回答によれば、[基本はメインで利用するインスタンスから近いサブネットを指定する](https://aws.amazon.com/jp/blogs/news/webinar-bb-efs-2018/) のが良いようだ。 ただし、もちろん別のサブネットであっても、アクセスの条件を満たしていればアクセスは可能。

# 準備

`amazon-efs-utils` をインストール。

```bash
$ sudo yum -y install amazon-efs-utils
```

https://docs.aws.amazon.com/ja_jp/efs/latest/ug/using-amazon-efs-utils.html


# 起動時にEFSをマウントするようにする

サーバー上のマウントポイントに `sudo mkdir -p /mnt/efs` で空ディレクトリを作成。 その後 `/etc/fstab` に以下を追加。

```bash
# フォーマット
<EFSのファイルシステムID>.efs.<リージョン>.amazonaws.com:<EFSのマウントポイント> <ローカルのマウントポイント> efs tls,noresvport,_netdev 0 0

# 具体例: EFSの / をローカルの /mnt/efs にマウントする
# fs-0123456789abcdef.efs.ap-northeast-1.amazonaws.com:/ /mnt/efs efs tls,noresvport,_netdev 0 0
```

AWSマウントヘルパに記載のある `-o tls` に対応する部分がここ。 実際には IAM 認証の場合は暗号化設定が必要なので、この場合に利用する方法が[紹介されている](https://docs.aws.amazon.com/ja_jp/efs/latest/ug/efs-mount-helper.html#mount-iam-ec2-profile)。


https://docs.aws.amazon.com/ja_jp/efs/latest/ug/efs-mount-helper.html

**2023/09/20 追記**

暗号化前提でよいと思うので defaults を tls に変更。 また、自動的再マウントオプションがない場合に AWS Health Dashboard に警告が届いたので、再マウントオプション `noresvport` を追加。

https://docs.aws.amazon.com/ja_jp/efs/latest/ug/automount-with-efs-mount-helper.html

https://docs.aws.amazon.com/ja_jp/efs/latest/ug/mounting-fs-nfs-mount-settings.html


# 参考

https://qiita.com/taichimachima/items/f7d47b68aac2a11ea7cc
大手Sier3年目SEがCloudFormationに使われた話

IaC（Infrastructure as Code）としてAWSではCloudFormationを利用して
インフラのプロビジョニングを行うことがある。

Githubなどで公開されたyamlを利用することで簡単に
AWSのインフラサービスが使用可能な状態（セキュリティグループ設定、IAM設定、バッチ処理（Lambda））になっていたりする。

今回Githubなどで公開されたyamlをベースに追加のリソースや設定の追加を行う際に
特に考えずともインフラが払い出せてしまい、カスタマイズやチューニングする際に
CloudFormationに書かれていることを解読する羽目になり、
シーケンス図を記載したりしながら依存関係を洗うこととなった。
こんな大変なら
全部手書きできるほうが早いじゃんと思ったので
まずIaC化に対する目的から整理し、道具に使われてしまうSEが減ることを願って
足跡を残していこうと思う。

## IaC（CloudFormation）の目的を整理してみた

>①自動化することで、人件費を削減したい
>②自動化することで、人為的なミスを減らし、担当者の負荷を下げたい
>③インフラの管理コストを削減したい

https://udemy.benesse.co.jp/development/system/iac.html

環境の再構築時を自動化でき、
コード自体が環境のバックアップとなる。
また、Gitなどを用いたバージョン管理により、Excelなどに比べ管理しやすい


GUIで作っちゃった環境も
**Former2**を利用すれば、既存環境のリソースに合わせた設定値のテンプレートを作成することができる
https://former2.com/
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2551344/72ba5ded-8079-64a0-605d-20a3db9f697e.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2551344/3505e459-2f95-ce7a-1b8c-f09e81d641ce.png)

CloudFormationだけでなく、TerraformをはじめとしたOutputsを無料で提供しているため、
IaCを何時からでも始めることはできる。

ここで一つ問題が発生する。
Former2で作成するテンプレートには環境固有値（IamgeID）が入ってしまうため、
バックアップには使用できる＆10分以内に作成できるが、
テンプレートとしてプロビジョニング用途には使用ができない。

そこでCloudFormationの書き方を勉強することになった。

CloudFormationの書き方は以下を参照
https://dev.classmethod.jp/articles/cloudformation-beginner01/

## 結論
書きなれていなければ、GUIで考えながら作ったほうが圧倒的にデプロイは早い（と思う）
ただ、品質や再現性、管理工数を鑑みるにIaCテンプレートを作成したほうが良い場合もある。

どれだけテンプレートを作成したとしても、使う人間が考えずに使用すればまた道具に使われてしまうだけなので
目的と運用の立て付けが必要だと感じた。
OSI参照モデルに入門してみた。

### 背景 ###

未経験者が転職活動する際、企業側からITの素養があるのか、技術に対して興味関心を持っているのかを問われることがあります。私は最近転職した身なのですが、面接でOSI参照モデルについて問われてうまく答えられなかったことがありました。最近になって「マスタリングTCP/IP 入門編」を購入し、少しだけ知識が増えたということで、改めてOSI参照モデルについてまとめてみます。稚拙な箇所が見受けられるかも知れませんが、どうか温かい目で見守っていただけると幸いです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280830/5da64105-d537-69d8-aaf8-d682f1cd5a44.png)

### 概要
- OSI参照モデルとは、ISOによって定められた通信プロトコルを設計するための指標として策定された標準化規格。
- 通信に必要な機能を７つの階層に分けて、ネットワークのプロトコルを単純化したもの。

それぞれについて、説明していきます。

#### 物理層

送信されたデータを電気信号に変換し、ケーブルなどの物理的な電装媒体に流し込む機能を提供する。コンピュータを無線LANや有線LANでケーブルまで繋げる。またコンピュータのデータを電気信号に変え、ルータまで運び、光ファイバーケーブルを伝って通信を外部のネットワークまで繋げる。海外のインターネットには海底ケーブルで接続されており、まれにサメがケーブルを傷つける（シャークバイト）ことが深刻な問題になっている。


#### データリンク層
物理層で直接接続された機器間の通信を可能にする。LANボードなどの機器を制御・識別し、そのための伝送路を確保する。回線やネットワークで隣接した2台の機器の間でデータの受け渡しを担当する。

- 通信相手の受け渡しや認識
- 伝送路上の信号の衝突検知や回避
- データの送受信単位(フレーム)への分割や組み立て
- 伝送路上での誤りの検知・訂正

といった仕様を規定。



#### ネットワーク層
個々のコンピュータにIPアドレスを割り当てて識別する機能を提供する。IPアドレスを伝って通信相手までデータを転送する。具体的にはルータがこの役割を果たす。ルータはネットワーク同士を接続する機器で、バケツリレーの要領でデータを目的のコンピュータまで運ぶ。複数のネットワークを接続し、全体を１つのネットワークとして相互に通信可能な状態にする。

- ネットワーク内のアドレスの形式や割り当ての方式
- 伝送経路の選択

といった仕様を規定。

#### トランスポート層
仮想回線を作り出す。「確実に通信相手にデータを届けること」を役割とする。上位層のアプリケーション層のアプリケーションを識別し、決められたタイミングに従ってコネクションの確立や切断を行う。また、各コンピュータ間でデータが正しく届いたかを判断し、届かなかったデータを再送する。**通信の信頼性**を確保する。

- 伝送路の確立・切断
- データ圧縮
- 誤り検出・訂正
- 再送制御

といった仕様を規定。


#### セッション層
セッション（通信の確立・維持・終了）の管理を行う。利用者の認証やログイン、ログアウトなどの**状態管理**を行うことが可能。


#### プレゼンテーション層
アプリケーション間でやり取りされるデータの表現形式を定義する。

- 通信に用いられるデータのファイル形式・データ形式
- 暗号化や圧縮
- 文字コードの定義や形式間の変換

といった仕様を規定。

#### アプリケーション層

実際にユーザが使うようなアプリケーションの種類やサービスに必要な機能を実装する。

- 利用者が操作するソフトウェアが提供する具体的な機能や通信手段
- データ形式

といった仕様を規定。
 

 -- 以上 --
【Heroku】アプリを削除したい！（Heroku CLI）

# 要約

以下のコマンドでHeroku上のアプリを消せる。
```bash
$ heroku apps:destroy -a アプリの名前
```

# 概要
Heroku CLIを使ってサッとアプリを削除したいときの備忘録として。

# 環境
* OS: Ubuntu 20.04（WSL2を使用）
```bash
$ cat /etc/lsb-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=20.04
DISTRIB_CODENAME=focal
DISTRIB_DESCRIPTION="Ubuntu 20.04.4 LTS"
```

* Heroku CLIのバージョン
```bash
$ heroku --version
heroku/7.59.2 linux-x64 node-v12.21.0
```

※Heroku CLIのインストールは[公式ドキュメント](https://devcenter.heroku.com/ja/articles/heroku-cli)を参照

# コマンド
以下のコマンドでHeroku上のアプリを消せる。また、`git remote`の一覧からもリモートリポジトリが削除される。
```bash
$ heroku apps:destroy -a アプリの名前
```

* 例：「my-app」という名前のアプリを消したいとき
```bash
$ heroku apps:destroy -a my-app
 ▸    WARNING: This will delete ⬢ my-app including all add-ons.
 ▸    To proceed, type my-app or re-run this command with --confirm my-app

> my-app # アプリ名を入力
Destroying ⬢ my-app (including all add-ons)... done
```

ちなみに作成済みのアプリの一覧は以下で取得できる。

```bash
$ heroku apps
=== hogehoge@gmail.com Apps
my-app
```

# 参考
[Heroku CLIコマンド](https://devcenter.heroku.com/ja/articles/heroku-cli-commands)
Single source of truth(SSOT), Single version of the truth(SVOT), Single version of facts(SVOF) について

# この記事はなに？

SSOT,SVOT,SVOFについての情報が意外と少なかったので、その違いについて記述します。
これらはそれぞれ情報技術や設計で使われる言葉です。

# Single source of truth(SSOT) とは

SSOT はすべてのデータが1箇所で生成され、編集され、消えるようなデータアーキテクチャを指す用語です。
SSOT を実現するには、保持してるデータを正規化し、データの重複をなくし、さらに重複データを持たないように関係するシステムはデータをリンクしたり参照するように実装することになります。

具体な実装例としては

- MDM(マスターデータマネジメントシステム)
- Data warehouse

等が挙げられます。

# Single version of the truth(SVOT) とは

SVOT は Data warehouse（DW） のコンセプトで複数のシステムからデータを DW に集めることにより「DW のデータが唯一の真実だよ（だから分析につかって！）」というものです。
データの変更は業務アプリケーション側で起こり、そのコピーが DW に流れてくるというデータフローをとります。
SVOT ができると会社中の業務アプリケーションのデータを DW に集約することになるので、業務を横断した分析が可能になり、各業務アプリケーション単独で分析システムを持つよりも、全社的な分析やBIが可能になります。

# Single version of facts(SVOF) とは

SVOF は上の2つに比べすこし知名度が低い考え方ですが、 "all the data, all of the time (すべての時点のすべてのデータ)" というように、良いデータも悪いデータ(ビジネス的に誤ったものでも)もすべて履歴として保持してしまおうという考え方です。
このコンセプトを実現するには CDC(Change Data Capture) を実装したり、データモデルを SCD (Slowly Change Dimension) にしたりしないといけないなど、正規化のようなデータ重複を排除するアプリケーションよりの設計手法とは離れてきます。

これを採用してる有名な手法は Data Vault ですね。

# 最後に

割と基本的な用語でも日本語の解説がない界隈、それがデータエンジニアリングの界隈です。
みんなが興味を持ってたくさん記事を作ってくれると嬉しいなと思いました。
djangoでビューを通さずに、テンプレートの変数を設定する

romです。

専門用語を使うならば、djangoでコンテキストプロセッサを使おう！という話なのですが、
コンテキストプロセッサという言葉を知らなければたどり着けないのでその言葉を使わないようにしておきます。

ビューの関数でテンプレートを使う際にコンテキストを設定して、テンプレートに変数を埋め込むというのはDjangoを使っているならばわかると思います。




```
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, "templates")],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
                'myapp.[ファイル名].［関数名］'
            ],
        },
    },
]
```
djangoのルートディレクトリの、アプリ名/setting.pyのTEMPLTESの部分に'myapp.[ファイル名].［関数名］'
を追加してあげまして、
setting.pyと同じ階層にファイル名.pyというファイルを作り、[関数名]という関数を作ってあげて、返り値を辞書にすると
そのテンプレート変数が全てのテンプレートに適用されるようになるようです。


ビュー関数に前処理など手を加えなくても、テンプレート変数はこのように追加出来るのでメモをしておきました。

これでよくわからなかったら、django コンテキストプロセッサ　と検索すれば詳細が書いてある記事にたどり着けると思います。


では、楽しいdjango生活を。
RDS Proxy利用時のLAST_INSERT_IDの挙動

# 目的

コネクションプーリングをしても採番テーブル(MySQL)が正しく利用できるかを確認したい。

# 目的（詳細）

まず、以下のように、採番テーブルからidを取得する仕組みがあるとする（採番テーブルについては [こちらの記事](https://qiita.com/Nyokki/items/c68dddff2368c557c4bf) を参照）

```
UPDATE table_name SET id = LAST_INSERT_ID(id + 1);
SELECT LAST_INSERT_ID() AS id;
```

ここで、UPDATE文とSELECT文の間に排他制御を掛ける必要はない。
理由は、MySQLの「LAST_INSERT_ID」はグローバルな値ではなく、`connection` 単位で定義されるためである。

[公式ドキュメント](https://dev.mysql.com/doc/refman/5.6/en/information-functions.html#function_last-insert-id)より

> "The ID that was generated is maintained in the server on a per-connection basis. This means that the value returned by the function to a given client is the first AUTO_INCREMENT value generated for most recent statement affecting an AUTO_INCREMENT column by that client. This value cannot be affected by other clients, even if they generate AUTO_INCREMENT values of their own. This behavior ensures that each client can retrieve its own ID without concern for the activity of other clients, and without the need for locks or transactions."

要するに、別の `connection` でLAST_INSERT_IDが更新されても影響されることはないようになっている。

一方、RDS Proxyを使ってDBのコネクションプーリングをする予定がある。ここでいう `connection` がDBのコネクションのことを指しているのであれば、コネクションを共有した別のクライアントにLAST_INSERT_IDを上書きされてしまうことはないかと考えた。

そこで、実際の挙動を確認してみる。

# 試験
## シナリオ
1. 複数のMySQLクライアントを起動し、RDS Proxy経由でMySQLのライターインスタンスに接続
2. 各クライアントで順にLAST_INSERT_IDを更新していく
3. 各クライアントで順にLAST_INSERT_IDの値を取得

### 期待する挙動

各クライアントのLAST_INSERT_IDの値が異なる（連番になっている）こと。すなわち、手順2でクライアント自身が設定したときの値となっており、他のクライアントの更新の影響を受けないこと

### 期待しない挙動

各クライアントのLAST_INSERT_IDの値が全て一致していること。手順2で最後のクライアントが設定したときの値となっており、他のクライアントの更新の影響を受けていること

## 試験結果

期待する挙動となっている（LAST_INSERT_IDはクライアント毎に異なる）。

▼6個のクライアントで順にLAST_INSERT_IDを更新していく様子

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79697/9c65aac1-aba5-af68-0beb-8517fe6e4ffa.png)

▼6個のクライアントでLAST_INSERT_IDの値を取得し、それぞれの値が異なっている様子

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79697/2b578171-45b9-9f68-507a-695f6d2ad017.png)


# 結論

RDS Proxy(コネクションプーリング)利用時にLAST_INSERT_IDの値がDBコネクション間で共有されるということはなく、クライアントに固有であることがわかった。おそらく採番テーブルとして利用して問題ないと考えられる。


# 補足: モニタリング情報

## RDS Proxyのメトリクス
クライアント接続数＞DB接続数となっている

 - ClientConenctions: 0→6に増加(+6)
 - DatabaseConnections: 3→4に増加(+1)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79697/0470f9bc-68d7-69cb-019e-a68506066d00.png)

## RDS Proxyのログ
 - クライアント〜Proxy〜MySQL接続確立時のRDS Proxyのログ
 - コネクションプーリングをしている様子がわかる
 - clientConnectionが6種類確立されているのに対して、dbConnectionはid=800703549, 3258076081の2種類が都度使い回されている

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79697/e03e2844-ab5e-3f2a-68d4-cd2316a008af.png)
Firebase Authentication 各種機能実装記事集

# はじめに
個人サービスを制作するにあたり、Googleが提供しているBaasであるFirebaseを採用しました。その際に、認証周りの様々な機能を提供してくれるFirebase Authenticationを使用しましたが、公式のリファレンスのみでは理解が難しい場面が多々あったので、各機能の実装にあたって参考にさせていただいた記事集を作りました。JavaScript（React等)で以下機能を実装したい方は、こちらの記事集を是非参考にしていただければと思います。

* ユーザーのサインアップ・ログイン
* サインアップ後の認証メール送信
* パスワード再設定用メール送信
* Firestoreへユーザー情報を登録
* Storageへプロフィール画像をアップロード
* 匿名ログイン


# ※注意事項
Firebaseのバージョンは、頻繁に更新されており、同一の処理である場合もバージョンによって記述方法が**異なっています。**（現在の最新バージョンは9系。）こちらで紹介している記事は、それぞれ投稿された日時が異なりますので、バージョン及び実装方法も一部異なる場合がありますので、下記の公式リファレンス等を参考にして各自で補完していただければと思います。

## Authentication 公式リファレンス

[Firebase Authentication | Firebase Documentation](https://firebase.google.com/docs/auth?hl=ja)

## 基本的な機能 （ログイン・サインアップ・ログインユーザーのみのアクセス権付与・サインアップ時の確認メール送信機能）

[Next.js、TypeScript、FirebaseAuthでメール認証(確認メールつき) - Qiita](https://qiita.com/d0ne1s/items/066d8ced7bb3ac3ce05b)

## パスワード再設定用メールの送信

[Firebase Authenticationでパスワードの再設定をする](https://zenn.dev/peg/articles/a7a7b79600211d)

## ログイン時にFirestoreへユーザーデータを保存

[Firebase Auth でログイン時に Firestore にユーザー用のドキュメントを作る方法](https://zenn.dev/phi/articles/firebase-create-user-docment-in-firestore-at-login)

## 登録時のCloud Strageへのプロフィール用画像アップロード

[ReactとFirebaseを使ってチャットアプリにアイコン画像を追加する方法](https://zenn.dev/danbo/articles/77aea0b5696f70)


## 匿名ログインの実装

[React+Firebaseでゲストユーザーログイン機能を実装しよう！！](https://www.notion.so/Authntication-edc2cf92dafc4ebf9b06ed121dad443c#3fe69a31e11449e3acc8d6a69417591d)

[Firebase Authで匿名ログインを実装して作成したuserデータを取得する](https://www.notion.so/Authntication-edc2cf92dafc4ebf9b06ed121dad443c#377a692048eb41068e2a643ec5d1a45f9)
MediaPlayerを使用してURLから音声を再生する方法

# はじめに
最近音声を再生する機会が多く、都度調べている気がしますので、簡単にまとめておこうと思います。

# 再生方法
まず、メンバ変数としてMediaPlayerを初期化しておきます。

```
private val mediaPlayer = MediaPlayer()
```

次に、MediaPlayerに再生したいURLと再生に伴う設定を行います。

```
setDataSource(url)
setAudioAttributes(
   AudioAttributes
      .Builder()
      .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
      .setUsage(AudioAttributes.USAGE_MEDIA)
      .build()
)
```
上記で再生する為の設定は完了です。
簡単に呼び出している関数を解説します。

```
setDataSource
```
この関数に再生したいURLを設定します、他にもAssetFileDescriptorやMediaDataSourceにも対応しているので、さまざまなパターンでの再生が可能なようです。

```
setContentType
```
この関数は名称の通り、再生するコンテンツの種類を設定します。
ContentTypeの種類に関しては、以下を参照してみてください。
https://developer.android.com/reference/android/media/AudioAttributes.Builder#setContentType(int)


```
setUsage
```
この関数はsetContentTypeと似ていますが、使用目的に当てはまるものをを設定します。
Usageの種類に関しては、以下を参照してみてください。
https://developer.android.com/reference/android/media/AudioAttributes.Builder#setUsage(int)

設定は以上になります。
あとは、以下のようにprepare関数とstart関数を呼び出すだけです。

```
prepare()
start()
```

# おまけ
MediaPlayerは画面が破棄されたタイミングで開放してあげないとメモリーリークしますので、以下に記述されているように破棄してあげると良いかと思います。

https://developer.android.com/guide/topics/media/mediaplayer?hl=ja#releaseplayer

# さいごに
URLをAPIから受け取って再生というのがよくある実装かと思いますが、MediaPlayerを使用するだけで比較的簡単に実装が可能です。
今回はメモ程度に触れましたが、どこかで時間が作れたらMediaPlayerの機能をもう少し詳細にまとめたいと思います。
【React】ReactをVSCodeでデバッグする方法

## はじめに
　本記事は、プログラミング初学者が、学習を進めていて疑問に思った点について調べた結果を備忘録も兼ねてまとめたものです。
　そのため、記事の内容に誤りが含まれている可能性があります。ご容赦ください。
　間違いを見つけた方は、お手数ですが、ご指摘いただけますと幸いです。

## VSCodeでReactをデバッグする方法
VSCodeを開きます。
`F5`キーを押すと以下のようなリストが表示されます。
リストの中から`Chrome`を選択します。
![スクリーンショット!2022-02-27 22.50.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342443/5e0eb745-d22a-9cad-3eec-42b6df7dfefd.png)
すると、.vscodeディレクトリにlaunch.jsonファイルが作成されます。
![スクリーンショット!2022-02-27 22.52.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342443/75125255-eb53-193b-413b-2d7bd9a622ae.png)
launch.jsonファイルの中身を以下のように書き換えます。

```json:launch.json
{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Chrome",
      "type": "chrome",
      "request": "launch",
      "url": "http://localhost:3000",
      "webRoot": "${workspaceRoot}/src",
      "sourceMapPathOverrides": {
          "webpack:///src/*": "${webRoot}/*"
      }
    }
  ]
}
```

これでVSCode上のデバッグ準備は完了になります。

`F5`キーを押すと以下のような画面が表示されます。
![スクリーンショット 2022-02-27 23.10.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342443/78956d22-fcf2-f49a-4d83-9e17bf74a0e8.png)

サーバーが起動されていないことが原因のため、ターミナルで`npm　start`を実行してください。

```:ターミナル
$ npm　start
```

すると、　正常に画面が表示されるはずです。

以上の操作をすることで以下の画像のようにブレークポイントで処理を止めたり、デバッグコンソールに出力させたりすることができるようになります。

![スクリーンショット 2022-02-27 23.17.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342443/a81e8004-82e2-dac8-603c-15c883eabfb3.png)
斜めに置かれた書類をまっすぐにしたい。画像変形する技術がすごい

# ドキュメントスキャナーのような画像変形を行う方法です。
![Feb-27-2022 23-14-57](https://user-images.githubusercontent.com/23278992/155886008-ecf1d7cb-eea0-4923-9e42-d9499c4911e2.gif)

# おいた書類をまっすぐに切り取りたい
書類などを机に置いて斜めから撮影すると、台形に移ります。
これをまっすぐな長方形にするには、画像を変形させる必要があります。

# DDPerspectiveTransformが使える
ライブラリを使えば、手動で簡単に画像を整形できるUIがつかえます。

https://github.com/d-dotsenko/DDPerspectiveTransform

# 使い方

DDPerspectiveTransformをプロジェクトに入れ、UIを表示します。

```swift
let cropViewController = DDPerspectiveTransformViewController()
cropViewController.delegate = self
cropViewController.image = image
self.navigationController?.pushViewController(cropViewController, animated: true)
```

クロップボタンは付いていないので、ボタンを追加してDDPerspectiveTransformViewControllerのcropActionを追加します。
整形した画像はデリゲートで受け取れます。

```swift
func perspectiveTransformingDidFinish(controller: DDPerspectiveTransformViewController, croppedImage: UIImage) {
    self.imageView.image = croppedImage
}
```

文字の角度も綺麗に整形されます。

🐣
****
フリーランスエンジニアです。
お仕事のご相談こちらまで
rockyshikoku@gmail.com

Core MLやARKitを使ったアプリを作っています。
機械学習／AR関連の情報を発信しています。

[Twitter](https://twitter.com/JackdeS11)
[Medium](https://medium.com/@rockyshikoku)
化石エンジニアの野良リカレント教育~どうやってITをキャッチアップするか？~

# 前段：

レガシーな化石エンジニア（現在地・太古の海の底）が上陸→人類を目指して、勉強を始めた。

# リカレント教育とは：

「社会人の（大学や大学院等での）学び直し」のことである。
学び直し、とても前向きな響きだ。人生100年時代、いつになっても新しい学びは良いものだ（とされている）。
だが、アンモナイトに華々しい輝くような「リカレント教育」で語られる大学や大学院へ行く資金などない。
今月の密林課金額で緊急家族会議が招集され、糾弾の嵐の中、被告人席で「で、でも、オライリーの本はもともと1冊が高いから……」と震え声で主張したアンモナイトにとって、大学（院）の高額な入学金は地球←→月（38万km）よりも遠い。
自分で調べて、自分で学んで、自分で自分を教育するしかない。
つまり、アンモナイトが今やっていることは、「野良リカレント教育」なのである。
いかにお金をかけずに、知りたいことを学び、身に着けていくか。
ここがポイントなのだ。

# 野良リカレント教育の経緯：

0. WFでPL等をたしなむ・技術的にはほぼ死んでる（鰓呼吸）
1. ある日、アジャイルPJへ突っ込まれる
2. スクラムへ進む
3. CI/CDや自動テストを考えて、「やっぱ最低限の技術がわからないとダメだわ」となって少し調査、2022年現在、自分とは違う世界（肺呼吸の世界）が展開していると悟る
4. Pythonに手を付けたのち、Kaggleに挑戦してみる
5. 独力の限界を悟り、他社研修（サイボウズ様）の視聴を始める
6. 一通り研修内容に目を通し終わり、倒れる**←いまここ。研修見終わるのに2か月かかった**

# 7までやってわかったこと：

体系だった知識を学ぶには他社研修はとても有効な手段。
研修はすべて見ないと意味がない。
つまり、すべて見ないと「自分がわかるところ」「わからないところ」がわからないのだ。
研修を見終わると、「わからないところ」がどこかわかる。そこを今度は攻略する。
具体的にはKubernetesとかgRPCが特にわからなかったから、そのあたりを攻略だ。
（正規表現はものすごくおもしろかった・あとやっぱりDB周りが好き）

……と、簡単に「攻略する」と書いたが、こんなん独力で攻略できるのか？
いや、いまからフルスタックのエンジニアになろうとはしないよ。
ニワトリ1羽分の唐揚げが不死鳥となって空を飛ぶようなもんだよ。それはわかってる。
でもせめて、なんらかのデプロイができるようになりたいじゃない？
もしくは得意分野（Kaggleとか）を持ちたいじゃない？
Udemyという翼もあるが、有料なんだよな。無理。

# これからの野良リカレント教育方針：

よもや自分で自分の教育方針を決める日が来るとは思わなかった。
だが、もう決めるしかないので、下記のように今後の教育方針を決めた。

1. Kaggleを再開してそっち系の技術を学ぶ
2. 他社研修を受けた結果、わからないことリストを作成し、意味を調べる
3. リストの中で重要だと自分が感じたもの（頻出する単語や関連が多い単語など）はできるだけ触って、実体験をしてみる
4. スクラム系の知識・学びを深める

1-4は並行なので、どこまでできるかは神の味噌汁。
やるしかないので、やるしかない。

# まとめ：

野良リカレント教育の概要：
野良リカレント教育はできるだけ安価に・自分で自分の学び直しを行うことである。
手順をまとめると、下記。

現在地把握（他社研修視聴）
　→わからないこと把握（リスト化・見える化し、調査）
　　→のばしたいところ把握（やりたいこと・興味を感じたこと）
　　　→わからないところ・のばしたいところ、それぞれをフォローしていく

わからないところ・のばしたいところには濃淡ができると思う。
やってみてもわからないものもあるだろうし。
だが、ぜんぜん知らないより「ここはわからない」と把握しているほうがはるかにマシなので、やってみる。

ちなみに全く別枠でCSMのSEU貯めないとな案件もあるので、並行して英語もやらねばならぬ。
SM用のToolkitの記事＆動画が英語なので……。

本日は以上で終わりです。お付き合いいただき、ありがとうございました！
【2022年度版】Oracle試験の申込みとJavaSE11試験の勉強方法について

# はじめに
先日、Java SE 11 Programmer II試験を受けて合格し、Java Programmer, Gold SE 11の資格を取得しましたので、これから受験する方の参考になればと思い、Oracle試験全般の申し込み方法とJavaSE11のSilver,Goldの勉強方法について記載していきます。

ちなみに筆者はSilverは一発で受かりましたが、Goldは2回目の受験で合格しています。
1回目の受験で点数を落としてしまった部分についても可能な限り、載せていきます。

# 残念な情報
毎年始め、Oracleでは「再受験無料キャンペーン」なるキャンペーンを実施していましたが、**2022年度は実施しないことが判明**しています。

（セミナーで講師の方が今年は実施しないと発言されていたとのことです。2023年は実施されるのかについては不透明のため、再受験キャンペーンが来るまで受験しないと考えていた方については、残念ですが、一発で合格出来るようにきちんと勉強した上で受験しましょう。）

（私も再受験無料キャンペーンがない状態でJavaGoldSE11を受けました。前述の通り、Goldで1回やらかしてしまったので、受験料3万を水の泡にしてしまっています。高額の資格試験はハイリスクハイリターンですね……）

# 申し込み手順
下記の方の記事が分かりやすかったため、こちらを申し込み時の参考にしました。
2022年度も申し込み手順に変更点はないため、同じように申し込みを行えば、間違いはないと思います。

https://qiita.com/MxShun/items/5dd8748d43d6ae7b8995

# JavaSE11試験の勉強方法について
下記の教材で勉強を行ってください。
有名な参考書として白本も存在しますが、こちらは筆者の感想としてはSilver,Gold共に購入は必要ないかなと思います。

■JavaSE11 Silver
・紫本
・黒本

■JavaSE11 Gold
・紫本
・黒本
・公式ドキュメント
https://docs.oracle.com/javase/jp/11/docs/api/index.html
・Java コーディングスタンダード CERT/Oracle 版
https://www.jpcert.or.jp/java-rules/

勉強方法ですが、基本は紫本で暗記を行い、紫本の問題や黒本の問題でアウトプットを繰り返すスタイルになります。

その中でも特に意識してほしいこととしては、問題は解いてそのまま終わりにするのではなく、**「間違えた箇所はなぜ間違えたのか」を自分の中で理解し、「正解するまで解き直す」ことが大事です。**

Goldでは紫本や黒本で出てこなかったメソッドも時々出てくるため、公式ドキュメントで可能な限り、覚えておきましょう。

また、紫本のセキュアコーディングに同様の記載がされていますが、セキュアコーディングは「Java コーディングスタンダード CERT/Oracle 版」から出題されますので、**出題率の高い5つのトピックについては他項番も含めて勉強をしてください。**

# Goldの1回目受験で失敗した原因について
筆者がGoldの1回目受験に失敗した原因ですが、出題率の多い問題の分野の勉強が不十分だったことがあげられます。

当時はGoldSE11の黒本が出ていなかったため、GoldSE8の黒本で勉強を行っていましたが、SE8の黒本では「モジュールシステム」と「セキュアコーディング」の対策が出来ず、実際の試験ではこの2分野が集中的に問われたため、点数を落とす要因になっていました。

2022年現在では、GoldSE11についても黒本が出ていますので、同じ間違いをする方はいないかなと思います。

GoldSE11では「ストリームAPI」「モジュールシステム」「セキュアコーディング」が特に重視され、実際の問題でも多く出題される形となりますので、この3分野に関しては、公式ドキュメントを補助資料として他分野以上に理解を深めておいてください。

# おわりに
2022年度版のOracle試験の申込みとJavaSE11試験の勉強方法は以上になります。
この記事が今後、Oracle試験、JavaSE11試験を受験される方の参考になれば、嬉しく思います。

何か質問がある方はtwitterのリプにご連絡ください。
※Qiitaのコメント欄での質問は受付していません。
The Basic of Kafka

# What is Kafka?

https://kafka.apache.org/

> Apache Kafka is an open-source distributed event streaming platform used by thousands of companies for high-performance data pipelines, streaming analytics, data integration, and mission-critical applications.

# Concepts of Kafka

https://kafka.apache.org/documentation/#intro_concepts_and_terms

## event

> An event records the fact that "something happened" in the world or in your business.

## producer

> Producers are those client applications that publish (write) events to Kafka,

## consumer

> consumers are those that subscribe to (read and process) these events.

## topic

> Events are organized and durably stored in topics. Very simplified, a topic is similar to a folder in a filesystem, and the events are the files in that folder

## partition

> Topics are partitioned, meaning a topic is spread over a number of "buckets" located on different Kafka brokers.

NOTE: Order of messages is guaranteed only within a partition (not across partitions)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349999/640ad915-742b-e63f-98e8-f2387752d21e.png)


## brokers

https://kafka.apache.org/documentation/#intro_nutshell

> Servers: Kafka is run as a cluster of one or more servers that can span multiple data centers or cloud regions. Some of these servers form the storage layer, called the brokers.

## zookeeper

https://www.cloudkarafka.com/blog/2018-07-04-cloudkarafka_what_is_zookeeper.html

> Zookeeper keeps track of status of the Kafka cluster nodes and it also keeps track of Kafka topics, partitions etc.

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349999/133e9d20-dd49-d238-4c02-702f1ac38d24.png)

## Offset

Each message within a partition gets an incremental id, called offset. 

> The offset is a simple integer number that is used by Kafka to maintain the current position of a consumer. That's it. The current offset is a pointer to the last record that Kafka has already sent to a consumer in the most recent poll. So, the consumer doesn't get the same record twice because of the current offset.

ref.) https://www.learningjournal.guru/courses/kafka/kafka-foundation-training/offset-management/#:~:text=The%20offset%20is%20a%20simple,because%20of%20the%20current%20offset.

## Replication factor

Each topic should have a replication factor (usually 2 or 3)

* Example: Topic-A with 2 partitions and a replication factor of 2, three brokers (broker1, broker2, and broker 3)
  * Broker 1 → partition 1, Broker 2 → partition 1 and 2, Broker 3 → partition 2
  * **If we lost broker 2, broker 1 and 3 can still serve the data**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349999/3b15dd06-0303-3385-afdb-25f1870f4515.png)


## Keys

Basically, events are sent to Kafka as key and value pairs. Producers can choose to send a key with the message (string, number, etc...). If a key is sent, then all messages for that key will always go to the same partition.

# Get started

https://kafka.apache.org/quickstart
DeBankのスクリーンショット自動撮影コードを書いてみた

ブロックチェーンベンチャーで暗号資産周辺の会計処理、内部統制の構築に関連する業務を行なっている公認会計士のyuk_tです。

簡単に複数ウォレット内の残高等を確認できるようにDeBankのポートフォリオ管理画面のスクリーンショットを自動で撮影してくれるコードを書きました。
環境構築など気にせず、どなたにも使いやすいように前回同様google colaboratoryで動かすことを想定しています。
GoogleアカウントとGoogle chromeが使える状態であればすぐに動かせます！

## DeBankとは
簡単に言うと、DeBankはウォレットのポートフォリオ管理ツールです。
情報が欲しいウォレットを接続、もしくはアドレスを入力するとウォレット残高やDeFiの使用状況を確認することができる優れものです。
他にもRevokeができたり最近のトランザクションを確認できたりDeFiの統計データが閲覧できたり・・・何かと便利です。

## このコードで出来ること

https://debank.com/

ポートフォリオ管理画面のスクリーンショット撮影を複数アドレス分自動で行ってくれます。
これによって、指定したアドレスの残高、DeFi使用状況等をスクリーンショットの画像で確認することができます。

アドレスは複数でも大丈夫ですので、どこのウォレットで何をしているのか、残高がいくらかわからなくなってしまったという場合も簡単に確認することができます。

また、こちらの記事のコードと組み合わせて使用するのも有効です。

https://qiita.com/yuk_t/items/578c5e6d0dad4acc10e5

こちらは複数のブロックチェーンエクスプローラーから取引履歴を抽出するコードなのですが、この記事のコードで確認できる残高と、リンク先のコードで出力したトランザクションの累計で算出した残高が一致していることの確認によって把握するべきトランザクションの漏れが無いかチェックできます。
（リンク先のコードはAPIで取得できる取引以外は確認できないため、用途によってはこのような作業が必要になります）

なお、当記事のコードは実行したタイミングでの情報を取得するものです。特定の時点の残高を確認する機能はございませんのでご了承ください。
年末に動かすと確定申告にも役立つかもしれません。


## 使い方
1. myaddsの[ ]内にデータが欲しいウォレットのアドレスを入力します。アドレスは "" で囲んでくださいね。複数ある場合は , で区切ってください。
2. 作成されたスクリーンショットのサイズが小さい場合はWidthとHeightの数字を大きくしてみてください。
3. 実行後、colab内のフォルダのマークをクリックすると出力されたデータが確認できます。![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2526353/b81d7988-8b0b-4199-71fa-b1fdaac98098.png)


```python:DeBank自動撮影コード
# seleniumのインストール
!apt-get update
!apt install chromium-chromedriver
!cp /usr/lib/chromium-browser/chromedriver /usr/bin
!pip install selenium

from google.colab import files
import time
import os
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

# 対象となるアドレスをここに入力します
myadds=[ ]

# Chromeのヘッドレスモードを起動
options = webdriver.ChromeOptions()
options.add_argument('--headless')
options.add_argument('--no-sandbox')
driver = webdriver.Chrome('chromedriver',options=options)
driver.implicitly_wait(5)

#スクショ画面の横幅をここで調整
Width = 1200
#縦幅。下の方まで表示させたかったら↓の数字を大きく
Height = 2000

# 繰り返し処理
for myadd in myadds:

    # URL取得
    url = f'https://debank.com/profile/{myadd}'
    driver.get(url)
    WebDriverWait(driver, 20).until(EC.presence_of_all_elements_located)

    # ウインドウ幅・高さ指定
    driver.set_window_size(Width, Height)

    # スクロール
    driver.execute_script('window.scrollTo(0, 2000)')
    time.sleep(1)

    # スクリーンショットの保管
    driver.save_screenshot(myadd + '.png')
    time.sleep(1)

# ブラウザ稼働終了
driver.quit()
```
直接マイドライブに出力、保存したい方は下記のコードでマウントして、上記コードの('myadd + '.png')の部分を('/content/drive/My Drive/'+myadd + '.png')に置き換えてみてください。

```python:
from google.colab import drive
drive.mount('/content/drive')
```
GitHub の複数アカウント切替を楽にする shell コマンドの設定方法

GitHub のアカウントを複数個持っており、アカウント切替を簡便化したいという人向けの記事です。
今回初めて shell コマンドを設定したので、自分用備忘録として記録を残します。
※bash 利用前提。

## .bashrc を用意する
### .bashrc とは
.bashrc とは、シェル を起動する際に毎回実行されるファイルのことです。
このファイルで関数定義しておくと、定義した関数名を bash 上で叩くことで、関数定義した諸々の処理が実行されます。（楽…！）

### .bashrc の配置場所
ホームディレクトリに配置します。（~/.bashrc）
既にファイルが存在する人はそのファイルを編集、無い人は新規作成します。（自分の場合は無かったため作成しました。）
```bash
$ touch ~/.bashrc
```

### .bashrc の中身
```.bashrc
function gitUserA() {
  git config --global user.name "{GitHub に登録しているアカウント名A}"
  git config --global user.email "{アカウントAで登録しているメールアドレス}"
  git config --list
}

function gitUserB() {
  git config --global user.name "{GitHub に登録しているアカウント名B}"
  git config --global user.email "{アカウントBで登録しているメールアドレス}"
  git config --list
}
```
これで設定完了です。
各関数の3行目は、確認用のコマンドなので、確認不要な人は省いてＯＫです。

## アカウントの切替方法
bash 上で定義した関数名を叩くだけです。
例えば、上記の例でアカウントAに切り替える場合は、以下のように叩く。
```Bash
$ gitUserA
```

## 終わりに
やってみるととても簡単。設定ファイルに関数定義するだけなのですね。
他にも色々と楽にしていきたいなと思います。
【PHP】データ登録処理後、再読み込みしたら同じデータが再び登録される

## 環境
PHP 7

## はじめに
今回は、ユーザー登録機能を実装している時に、起きた問題です。ユーザー登録後リロードをすると同じデータが再び登録されてしまうという現象が起きました。
調べて分かった事などを備忘録として残していきたいと思います。

## リロードや再読み込みで再送信される
ユーザー登録後に、再読み込みをすると同じユーザーが再び登録される、、
URLを直撃ちしてページをみると、この現象は起きないのですが毎回こんなことできないですね、、

リロードをすると下記の画像のアラートが出力されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/697259/ad0ffb28-37a2-e1e3-c4d2-6c3765bf869e.png)

「続行」を押すと同じデータが登録されます。
この現象は何度も繰り返されるため、無限にデータが増えることになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/697259/e600aea3-a105-6ce7-e4dd-2a008b546920.png)

## リロードによる多重登録の原因
なぜ、リロードをすると同じデータが登録されてしまうのか、、
原因を調べてみると、フォーム送信をするとPHPでは`$_POST`という変数に格納されます。この変数は、送信後も保持されているためリロードすると再度この変数の値が登録されてしまうのが原因です。
このことを踏まえ`$_POST`を初期化してみましたがダメでした、、

## 解決策
解決策としては大きく分けて二つ
- `セッション`などを使って、ページ遷移の正当性を確認する
- `header関数`を使い自動リダイレクトを行い、パラメータのない状態にする。
今回は二つ目の自動リダイレクトを使って解決をしました。

一つ目でも解決できるみたいですが、生のPHPでやると結構大変みたいです。
余裕があるときに挑戦したいと思います。

### 自動リダイレクトさせてみる
```PHP:index.php
header("Location:URL");
exit;
```
このコードを処理の最後に記述します。
header関数でページ遷移させます。URLの部分に読み込みたいページのURLを記入してください。
今回は、同じページに戻るので`　./`　を書きました。

最後に、`exit;` でPHPのプログラムを終了させます。

:::note alert
最初のhtmlタグよりも前に記述しないと、「Warning: Cannot modify header information – headers already sent by …」というエラーが起きます。
:::


以上となります！
Docker備忘録

# CLIコマンド
## docker container
### 停止している不要コンテナを一括削除する
```
$ docker container prune

# -f, --force: 削除時の確認を行わない
$ docker container prune -f
```

参考:
[停止コンテナ一括削除](https://qiita.com/reflet/items/5caa88abcf1e8964783a#%E5%81%9C%E6%AD%A2%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A%E4%B8%80%E6%8B%AC%E5%89%8A%E9%99%A4)
[docker container prune](https://docs.docker.com/engine/reference/commandline/container_prune/)

## docker-compose
### コンテナを作成し起動する
```
$ docker-compose up

# -d, --detach: バックグラウンドでコンテナを起動する
$ docker-compose up -d

# --force-recreate: 設定変更の有無にかかわらずコンテナを再作成してから起動する
$ docker-compose up --force-recreate
```

参考:
[docker-compose up](https://docs.docker.com/compose/reference/up/)

# 小技
## コンテナ起動時にinit.db内のSQLが実行されない場合の解決方法
- volumeにマウントされたSQLが古いままであることが原因である
- 一旦volumeを削除し、更新したSQLを再度マウントし直せばよい

参考:
[docker-compose起動時に、init.dbのSQLが実行されない場合の対処](https://qiita.com/kondo0602/items/ab0a85fb1e731234eb1a)

## ポートフォワーディング
TBD
https://qiita.com/tatsuo-iriyama/items/e4bf2404411343116e3e
wails v2でvueテンプレートを作った手順

# 要約

wails v2でvueを使えるテンプレートを作った。[これ](https://github.com/woogea/wails_vue_template)
これはその時の作業記録

# 動機

マルチプラットフォーム対応のデスクトップアプリケーションをgoで作りたかった。
できるだけ一つのプラットフォームで開発して、他は最低限の確認すればOKとしたかったため、ブラウザエンジン組み込み型にすることにした。
フロントのフレームワークはvueを使いたかったけど、v2には公式テンプレートが用意されていない。
すでにテンプレートを[作ってくれている人はいる](https://github.com/misitebao/wails-template-vue)けど、viteを使っていて慣れなかったのでnpmを使ったものを作ることにした。
あと、wailsの仕組みを理解するのにもいいかなと思った。

# wails

比較的アクティブで、構成がシンプルで何をやっているかわかりそうな[wails](https://github.com/wailsapp/wails)を選択した。
v2がbetaになっていたため、今から使い始めるならv2を使おうと決めた。v1ならvueの公式テンプレートがある。

# 作成手順

## wailsセットアップ

wailsのインストール手順は公式ページ通り。現在特に詰まることはない。
今回はwindows環境で作業。
go, nodeをインストールしておく。
手順通りにインストールして、プロジェクトを作成し、`wails dev` でアプリケーションが立ち上がることを確認する。

## テンプレート生成

wailsには空のテンプレートを生成する機能がある。
`wails generate template -name wails_vue_template`
を実行すると、wails_vue_templateディレクトリにシンプルなテンプレートが生成される。
これをもとにテンプレートを作成していく。

## vueのインストール

vue-cliをglobalインストールする。
別の作業用ディレクトリで `vue create <name>` を実行して、あとからテンプレートのfrontendディレクトリにコピーする作戦。
vueの構成としては、vuexとvue-routerを入れたかったので、手動設定を選んで、以下のようにした。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86918/3d8f5384-f31b-b434-eb2d-7d0ab205e9eb.png)
念のため、`npm run serve`を実行、ブラウザで`localhost:3000`を開いて、正常に動作していることを確認する。

## frontendへ移動

作成したvueのプロジェクトをfrontendディレクトリにコピーする。とりあえず全部上書きコピーしてしまえば大丈夫。
https://github.com/woogea/wails_vue_template/blob/main/app.tmpl.go#L35 このMethodをvue側から呼び出してみる。
https://github.com/woogea/wails_vue_template/blob/main/frontend/src/components/HelloWorld.vue#L22 のように呼び出せるよう、wailsによってbindされる。
package.jsonはwails initの実行時にテンプレートが適用されるので、リポジトリには含めない。package.tmpl.jsonにpackage.jsonの内容を転記する。テンプレーティングが必要なnameとauthorは変数化しておく。
その他リポジトリに含めないものは [.gitignore](https://github.com/woogea/wails_vue_template/blob/main/.gitignore)を参照。
goとnodeの.gitignoreテンプレートを元に、wailsに関するものは最後のほうに記載してある。

## 実行してみる

ここまでの成果物をリポジトリに一旦上げてみて、うまくいくか確認する。
`wails init -t <リポジトリ> -n <project name>`
を実行して、今上げたリポジトリのテンプレートを使ってプロジェクトを生成する。
`wails dev`を実行して、想定通りに動作していることを確認する。
うまくいかなかったら、今作成したプロジェクトで試行錯誤して、テンプレートのほうにフィードバックしていく。

## ハマリポイント

いくつか気にするポイントがある。
* Vue-routerでHistoryを有効にする場合、モードは`WebHashHistory`の方を選択すること。`WebHistory`だと、ページ遷移時にgoに埋め込まれたリソース参照でエラーになる。wails側で想定されていない模様。
* frontend/distを空にしたり、リポジトリにディレクトリを含めないようにしたくなるが、wailsはディレクトリが存在しないか、ディレクトリ内にgo:embedできないファイルしかない状態だと`wails dev`の実行でエラーになる。適当なダミーのhtmlファイルを置いておくと、frontendの`npm install && npm run build`が実行され、成果物がdistに上書きされてうまくいく。埋め込み可能ファイルがあるかどうかのチェックと、distの更新処理の順番が逆になってしまっている模様。

## 使い方

wailsをインストールした状態で
`wails init -t https://github.com/woogea/wails_vue_template -n <project name>`
を実行する。
<project name>に移動して
`wails dev`
を実行する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86918/cf4f9d38-edbd-edbd-3c9b-c0190b3ea271.png)
このようなシンプルな画面が表示されたらうまくいっている。
この状態で、別ターミナルを開いて、<project name>/frontend に移動し、`npm run watch`を実行すると、front側の変更がhot reloadされる。

本当は、`wails dev`コマンドだけでhot reloadできると良いが今はforegroundで完了を待つためwatchすると先に進まない。https://github.com/wailsapp/wails/pull/1170 がリリースされるとバックグラウンド実行が可能になるので更新予定。
PowerAutomate: 投稿記事をタグ別にまとめて定期更新させてみる

# 背景
備忘録として使わせてもらっている為、タグ別の自己記事一覧の記事が欲しくなった。
ってことで、Qiita API を試していけそうだったので作成した記録

# API の試行は以下で実施済み

https://qiita.com/baku2san/items/69876662473c5d8cf493

# 機能
- 選択した Tag に対しての専用更新とする。
    - 対象としては、とりあえず以下
        - PowerAutomate
        - PowerBI
        - PowerAutomateDesktop
        - PowerQuery
        - PostgreSQL < 2022/6/2 追加
    - 一定投稿数を全部とかやろうとしたけど、tag の種類が意外に多く自動投稿してしまうと目的からずれると感じた為
- 投稿まとめ用の記事は、以下条件で生成
    - 新規Tagが追加されたら、追加 (POST)
    - まとめ記事があれば、更新 (PATCH) 
- 各記事の、記事内への展開は、以下
    - H1 でタイトル ＆ Link 
        - まとめ記事には Surfix を付ける "[automated]"
    - 記事の投稿日、更新日が相違あれば括弧で追記
    - Tag 全部を追記
   <br>例<br>![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120072/1089c1d0-70aa-10a5-bffb-f59ce49f7454.png)

# フロー概要
## 記事取得
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120072/e9f3164a-501d-ef90-982d-543688b5029e.png)

## 記事投稿
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120072/e07f4d12-c411-6f40-d944-36fc2ddaa38d.png)



# 注意点
- 利用制限について

以下なので、普通に使ってれば特に問題ないかな、と

仮に投稿数が 1000 としても、limit 100/page にすれば 更新用 PATCH 使っても 15 回程度しか使わない予定

> 利用制限
認証している状態ではユーザごとに1時間に1000回まで、認証していない状態ではIPアドレスごとに1時間に60回までリクエストを受け付けます。

https://qiita.com/api/v2/docs#%E5%88%A9%E7%94%A8%E5%88%B6%E9%99%90
Azure Storage Accountのアクセス監査ログをONにする設定

**Storage Accountの監査(クラシック)のところに診断設定(クラシック)を有効にする。**
**ログの保存日数は必要に応じて設定する。**
![Storage Account監査ON設定01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2547888/04970a73-284a-93d4-2ede-1f4087f7e439.png)
![Storage Account監査ON設定02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2547888/f35ad89c-b992-54c8-0830-b3a5479fae39.png)
![Storage Account監査ON設定03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2547888/29699c6e-f07d-7230-6a14-da4e81663c0c.png)
![Storage Account監査ON設定04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2547888/e0379432-6249-1034-cadb-564fb745b3bd.png)

**Storage Accountの監査ログは該当Storage Accountの$logs配下に記録される。**
![Storage Account監査ON設定05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2547888/03c4ff2c-3d9e-86a5-5e6b-dda3bbe4ae3d.png)
Laravel ルートモデル結合について

# はじめに
ルートモデル結合についての備忘録です。

Laravel 8.X

# ルートモデル結合
ルートパラメータからモデルを取得するとき、コントローラでIDなどを受け取って、データベースクエリでモデルインスタンスを取得できる。
ただ、ルートとモデルを結合することでその手間を省けるらしい。

例えば、Userモデルがあり、以下のようなルート情報だとし、コントローラはこんな感じだとする。
```php:web.php
Route::get('profile/{id}','UserController@index')
```
```php:UserController.php
public function index ($id){
    $user = User::find($id);
    return view('profilePage', ['User' => $user]);
}
```

ただLaravelはルートモデル結合があるので、実際はコントローラはこれでも問題ない。

```php:UserController.php
use App\Models\User;

public function index (User $id){ // <- Userクラスを指定するだけでデータクエリ要らず
    return view('profilePage', ['User' => $id]);
}
```

## 暗黙の結合
上記のルートモデル結合は、モデルを読み込んだ上でコントローラの関数の引数にモデルクラスを指定するだけでできる。
パラメータをそのモデルのプライマリキーと判断し、自動的に該当するモデルインスタンスを関数に渡してくれる。

これを **暗黙の結合** というらしい。

## 明示的結合
ただこのモデルクラスを指定する手間もRouteServiceProviderで省くことはできる。

app/Providers/RouteServiceProvider.phpに以下の様な記述を追加する。
```php:app/Providers/RouteServiceProvider.php
use App\Models\User;

public function boot()
{
    Route::model('user', User::class); // <- userパラメータとUserモデルを結合
}
```
そうすると、「user」というルートパラメータはUserモデルと結合される。つまり以下の様な記述で処理が可能になる。
```php:web.php
Route::get('profile/{user}','UserController@index') // <- userパラメータ
```
```php:UserController.php
public function index ($user){ // <- Userの記述が要らなくなった
    return view('profilePage', ['User' => $user]);
}
```

# 最後に
他にも、自動的に判断するキーのカラムをパラメータから定義で指定したりも可能だったりするらいい。
さらに、バリデーションも自動的に対応してくれる。

詳しくは[ドキュメント(ルートモデル結合)](https://readouble.com/laravel/8.x/ja/routing.html#route-model-binding) を参照してください。

# 参考文献
[PHPフレームワーク Laravel 実践開発 - 掌田津耶乃・著](https://www.amazon.co.jp/PHP%E3%83%95%E3%83%AC%E3%83%BC%E3%83%A0%E3%83%AF%E3%83%BC%E3%82%AF-Laravel%E5%AE%9F%E8%B7%B5%E9%96%8B%E7%99%BA-%E6%8E%8C%E7%94%B0-%E6%B4%A5%E8%80%B6%E4%B9%83/dp/4798059072)
【Ruby】　nからmの数の総和を求める

※初心者向け
※アウトプット練習の為

開発環境
rails 6.0.4.4
ruby 2.6.5


**概要**
与えられた数字n,mについて、nからmまでの数の総和を求めるプログラムを実装します。


# 問題内容
以下の要件を満たすsum_numberメソッドを実装しましょう。

第一仮引数をnとし、第二仮引数をmとすること
nからmまでの数を足し合わせていき、その総和を出力すること（例：n=1,m=5の場合、総和は1+2+3+4+5=15）


**雛形**
```ruby
def sum_number(n, m)
  #処理を記述
end

#呼び出し例
sum_number(5,15)

```
**出力例**
sum_number(5,15) → 110 ＜計算内容5+6+7+8…+15＞

# 解答
```ruby
def sum_number(n, m)
  total = 0 # 総和を代入する変数を、初期値0で定義しておく
  while n <= m   # n<=mの条件を満たす間、処理を繰り返す
    total = total + n # 変数totalに変数nを足す
    n = n + 1
  end
  puts total
end

# 呼び出し例
sum_number(5,15)
```

**解説**
まず、1行目で、仮引数nとmを設定し、数値をそれぞれ受け取ります。

2行目で、総和を代入する変数totalを、初期値0で定義しておきます。

3行目から6行目でwhile文を使用し、繰り返し処理を記述しています。**条件はn <= m**としています。これは、**nがmより小さいまたは同じ**という条件を満たす時、ブロック内の処理を繰り返すと言う意味です。

繰り返しの処理内では、まず変数totalにnの数値を足します。そして、nに1を足して、次の繰り返し処理を行います。nがmより大きい数値になったら、処理が終了となります。
TextView内の設定でImageを設定する方法

TextViewとImageViewを分けていましたが、TextView内にImageViewを設定し、コードを簡潔にすることができます！
早速コードで説明していきます！


```
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        android:layout_gravity="center"
        app:drawableEndCompat="@drawable/star"/>

</FrameLayout>
```


　![スクリーンショット 2022-02-27 22.28.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2394054/7c96b6f5-7b37-9f52-e700-7c5cb93868d6.png)


テキストの先頭にイメージを設定したい場合は
```
app:drawableStartCompat
```

テキストの後ろにイメージを設定したい場合は

```
app:drawableEndCompat
```
とTextView内に設定しましょう

また、テキストとイメージの間にスペースを作りたい場合は

```
android:drawablePadding=""
```
を一緒に設定し、もたせたいスペースを設定しましょう
Fiber#raiseによって外部から発されたエラーをFiber内でrescueしてみる

個人的なメモです。
趣味のrubyの非同期タスクスケジューラーの実装に必要になった。
自分で手を動かして挙動を見ておきたかっただけ。


## 公式ドキュメント


https://docs.ruby-lang.org/ja/latest/method/Fiber/i/raise.html

## 動作検証(エラーキャッチなし)

```rb
f = Fiber.new do
  Fiber.yield(:a)
  Fiber.yield(:b)
  Fiber.yield(:c)
  Fiber.yield(:d)
end

> p f.resume
# => :a

> f.raise StandardError.new("Fiber Interruption from #raise")
# in `yield': Fiber Interruption from #raise (StandardError)

> p f.resume
# attempt to resume a terminated fiber (FiberError)
```

## 動作検証(エラーキャッチあり)


```rb
f = Fiber.new do
  Fiber.yield(:a)
  begin
    Fiber.yield(:b)            ## 2回resumeした後にraiseしたらここでエラーが発される。
  rescue StandardError => e
    Fiber.yield e.message
  end
  Fiber.yield(:c)
  Fiber.yield(:d)
end
```

結果は以下。ちゃんとfiber内でrescueできた。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/534442/17b7cde0-2baf-ac2e-5304-50fe0069557f.png)

ドキュメントにある通り、

> selfが表すファイバーが最後に Fiber.yield を呼んだ場所で例外を発生させます。
10080番ポートに注意

# 結論

・Chrome91以降では10080番ポートが使用できない。
・ポートを別のポートに指定した方が無難。

## 業務中に困ったこと。。。
DockerでSpring Bootの環境を作成した時の話で、
VSCode(IntelliJ)のターミナルには正常にSpringが立ち上がっている様子だが、
いざブラウザ(Chrome)にアクセスすると**ERR_UNSAFE_PORT**とエラーが出る。

当時はエラーメッセージをよく読まず、ソースコードの設定を色々と
いじっていたのですが、よくよくエラーを見てみるとERR_UNSAFE_PORTと
あったので調査することに。。。
(Dockerだとエラー内容がERR_EMPTY_RESPONSEだった気がします。うろ覚えですみません)

検索するとすぐ出てくるのですが、
**ChromeがNAT Slipstreaming v2攻撃への対策**のために行ったものでした。。

確かSafariとかでは普通に表示できた？気がします。サーバ間通信等は問題ないので
わざわざブラウザ表示しなくてもいいと思うかもしれませんが、Spring Bootを
ブラウザに表示させることが環境構築の最終ゴールでしたのでポートを変更して対応しました。
(もちろんフロントでも10080番ポートは使えないので注意が必要です。)

エラー文をよく読むことと、ブラウザのバージョンでも
予期せぬエラー(特にフロントエンド)が出たりするのでデバックの際は
その辺も意識していこうと思います。