OmniAICreator/Qiita-1.07M huggingface dataset:
モブプログラミング入門と生産性

## 記事の内容

- モブプログラミング入門の講座を受講したこと
- モブプログラミング導入に関する疑問について、私が考えたこと

:::note warn
モブプログラミングや講座の内容については深く扱いません。
:::

## 受講した講座

『リーダー＆マネージャーのための チームの生産性をあげるモブプログラミング入門』

https://event.shoeisha.jp/cza/remote-mob

## モブプログラミングとは

モブプログラミングとは、チームメンバーが集まって一緒に同じタスクに取り組む手法です。
モブプログラミングを最初に広めたとされるWoody Zuill氏は次のように説明しています。[^1]
[^1]:https://www.slideshare.net/andrefaria/mob-programming

> All the brilliant minds working on
> the same thing...
> at the same time...
> in the same space...
> on the same computer...
>    Just like a real mob.

また、モブプログラミングにルールというものはなく、自分たちに合った方法を見つけることが推奨されています。

https://twitter.com/woodyzuill/status/1117493450734276611

（ところで"real mob"とは何を示しているのでしょうかね？”モブ（mob）”とは「破壊的で無秩序な集団」をイメージさせる単語のようです。"real"を付けることで「生産的で組織的な集団」というような意味になるのでしょうか？）

## モブプログラミングを体験してた感想

モブプログラミングに入門した背景には、開発現場で課題に感じている点があります。それは、チーム内での情報の非対称性や、それぞれの知識が共有されない点、また特定のメンバーにタスクが集中する、といったことがあります。このような課題を解決するために、メンバーが一緒に作業をするような手法に興味がありました。
そのような考えで参加したところ、体験を通して課題の解決につながりそうな期待が感じられました。初対面の参加者でチームを組んだのでぎこちなさはありましたが、それでもアイデアを出して議論したり出来ていたので、情報や知識の共有について期待できそうです。また、全員で最後までやりきるので一部の工程がメンバーに集中するような状況も防げそうだと感じました。モブプログラミングを導入することで、現場で課題に感じていることについて改善が期待できるかもしれません。

## モブプログラミング導入について

さて、今回体験したモブプログラミングを現場に導入する、という観点でいくつかの疑問について考えてみたいと思います。

### 上司や顧客への説明は必要なのか

上司や顧客への説明は常に必要ではないと思います。何故なら、モブプログラミングは開発の手法だからです。開発の手法とは我々が日々の業務を進める手段であり、結果を出すための手段として基本的には一任されているものではないでしょうか。なので設計やリファクタリングと同様に、説明が必須とは限らないと思います。
（ただし説明できなければならないとは思います）

### モブプログラミングは生産性あげるのか

講座のタイトルにも「チームの生産性をあげる」とありますが、システム開発における生産性は非常に難しいテーマであると認識しています。例えば、生産性の観点はさまざまです。[^2] また、学習や教育など人材育成的な側面は定量化が難しい部分です。[^3] なので、導入にあたって生産性が焦点になる場合は慎重に議論したほうがよさそうです。
（プロジェクトの生産性と組織の人材育成のバランスをどう考えるか、という疑問をぶつけるとどのような回答が返ってくるかはところです。）

[^2]:https://qiita.com/hirokidaichi/items/53f0865398829bdebef1
[^3]:人材育成への投資は経営ポリシーによって決まる。『エンジニアのためのマネジメント入門』

### どのように導入するか

新しいプラクティスや技術を導入する際は、小さく始めるのが良いと思います。関心のある小数のメンバーと小さな課題について始めるのが良さそうです。”モブプログラミング”というキーワードも使わず、単に”一緒に作業する”といもの良いかもしれません。新しい試みに対して「良い結果になりそうなイメージが持てる」ということが大切だと思います。

## まとめ

「モブプログラミング入門講座で体験したこと」「導入にあたっての疑問について考えたこと」について記事にしてみました。
私自身は、モブプログラミングのような一緒に作業する機会を積極的に導入することにより良い結果になりそうなイメージを抱きました。というのも、多くの知識や経験が失われていた活用できいないと感じているからです。開発や業務に関する知識や経験を共有したり、迅速なフィードバックを得られる機会を導入することについて、積極的に考えたいところです。
MVVMの理論

　ソフトウェアアーキテクチャはソフトウェアの構造であり、ソフトウェアをどう分割して構成するかを決めます。
 
　MVVMは以下の規則により、ソフトウェアを分割します。

規則１：　ソフトウェアをUIとUI以外に分ける。
規則２：　ソフトウェアをView/ViewModel/Modelに分ける
規則３：　ViewとViewModelはUIである。
規則４：　ViewにはUIの状態とコードを含まない。
規則５：　依存の方向は View->ViewModel->Modelの一方向のみ


規則３と規則４より、UIの状態とUIコードはViewModelに存在する。

規則３より、ModelはUI以外であり、UIの状態とUIコードは含まれない。

規則５より、
   ViewModelはViewを知らない。（アクセスできない）
   ModelはViewModelを知らない。（アクセスできない）




　アーキテクチャはソフトウェアをどう構成するかを決めるものです。
実装の詳細は決めません。


※）バインドはMVVMではありません。
バインドはMVVMの実装の一例です。

ViewModelのデータの変化でViewを変化させるよう
フレームワークで対応する場合
Viewのコードで対応する場合

もあります。
mindの研究 その41 - mind良いところ、悪いところ

# 概要
mindが謎なので調べてみた。
良いところ、悪いところ。


# 良いところ
- 日本語で書ける。
- exeが作れる。
- win32apiが簡単に呼べる。

# 悪いところ
- jsonパーサが無い。
- 正規表現エンジンが無い。
- ソケットが弱い。
- guiが無い。
- ActiveXが呼べない。
- データーベースが無い。
- 乱数が無い。

# 使い所

- RPAだな。

以上。
広報１号として転職してきて手始めにやったこと

この記事は デジタルキューブ ＆ ヘプタゴン Advent Calendar 2023 の 12月4日分の記事として執筆しています。
https://qiita.com/advent-calendar/2023/digitalcube-heptagon

# 自己紹介
2023年10月10日付けでデジタルキューブに入社しました、タカバシと申します。デジタルキューブで初となる広報職です。肩書きは広報室 室長となっていますが、ひとり広報なので、イメージする部屋は「駅にあるリモートワークブース」くらいの広さです。[詳しい自己紹介](https://note.com/digitalcube/n/n9e2ab127fbac "詳しい自己紹介")もぜひご覧いただけたら幸いです。

今回、何を書こうか迷っていたんですが、室長仲間の恩田さんから「広報室所信表明、手始めにやったことと手応えなど」というテーマが飛んできたので、そんなことを書きたいと思います。ちなみに、今のところデジタルキューブには「◯◯室」がふたつあって、僕は広報室、恩田さんは人材開発室の室長です。

# 入社前から要望していたこと その1 「1on1」

さて、広報として入社前から要望を出していたことがふたつありました。ひとつはメンバー全員との 1on1 です。デジタルキューブはフルリモートワークの会社なので、移動の手間はかかりません。なので、隙間時間をいただいてちょっとした雑談をしつつ、個性を知ることができたらいいなと考えていましたが、入社から約1ヶ月でデジタルキューブグループの35名の方と 1on1 を行い、お話することができました。

1on1 の後には、お話をした内容で印象に残ったことを、それぞれ４つ抜き出してキーワード的にタグ付けさせていただいていました。例えば、某メンバーのタグ付けはこんな感じです。

**Sさん**
#小中学校皆勤賞
#校長経験者
#ハイスキルだけど流されやすい人
#最終目的地はフランス

**Oさん**
#ロボカップ
#AIとディープラーニング
#登山とトレランとスノボ
#筋金入りの登山家系

30分お話してのタグ付けはTV番組の『激レアさんを連れてきた。』のラベリングを参考にしたもので、どんな人だったか、どんな話題があったのかをカジュアルに振り返ることができつつ、社内に公開することで、他の人が興味を持つきっかけになれば、という狙いがありました。

僕はまだ新参者ですが、できれば古参のメンバーでも知らないことを引き出せないかな〜、って感じでお話を聞いていました。個人的にはユニークな個性をいろいろと見つけることができて、充実した 1on1 になりました。

# 入社前から要望していたこと その2 「note アカウント開設」

入社前から要望していたこと、もうひとつは note のアカウント開設です。前職でも広報として note を運用していたのですが、社風や雰囲気を伝える媒体として好感触を得ていました。さらに note というプラットフォーム内でのコミュニケーションが良い刺激になることもあり、広報としては「やるしかない！」と考えていました。

![screencapture-note-digitalcube-2023-11-22-15_58_15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588665/6dde54d8-ef6e-e44f-c5cb-196f591252be.png)

デジタルキューブの新サービス「[FinanScope（ファイナンスコープ）](https://finanscope.jp/ "FinanScope（ファイナンスコープ）")」をローンチしたこともあり、note アカウントの開設＆執筆に少し時間がかかりましたが、11月1日から週1更新で、会社の制度の紹介、イベントレポート、インタビューなど、いろいろなことを書いています。

以上、振り返ってみると、いずれもコミュニケーションに関することでした。広報はコミュニケーションが仕事なので、当たり前といえば当たり前なのですが、まずは要望していたことがしっかりと実現できたことに手応えを感じています。

# さて、2024年は…

グループ企業・ヘプタゴンとのシナジーや新サービスの「FinanScope」にドライブをかけつつ、コミュニケーションを通じて、デジタルキューブグループの認知を拡大していきたいと考えています。

そのためには、コミュニケーションメッセージの確立、ツールの制作、これまでとは異なるコミュニティへの参加ということも必要になってきそうです。いずれにしても note と X でも発信していきますので、皆様、今後ともよろしくお願いいたします。

【note】 https://note.com/digitalcube
【X】 https://twitter.com/DigitalCubeX
[Elastic Beanstalk]appじゃなくてapplicationだった

## 概要
Elastic Beanstalkでflaskを実装しようとしたら
`application`が見つからないというエラーが出た


## ログ
```
----------------------------------------
/var/log/web.stdout.log
----------------------------------------
(省略)
Nov 28 05:46:30 ip-172-31-24-160 web[4572]: Failed to find attribute 'application' in 'application'.
Nov 28 05:46:30 ip-172-31-24-160 web[4572]: [2023-11-28 05:46:30 +0000] [4572] [INFO] Worker exiting (pid: 4572)
Nov 28 05:46:30 ip-172-31-24-160 web[4568]: [2023-11-28 05:46:30 +0000] [4568] [ERROR] Worker (pid:4572) exited with code 4
Nov 28 05:46:30 ip-172-31-24-160 web[4568]: [2023-11-28 05:46:30 +0000] [4568] [ERROR] Shutting down: Master
Nov 28 05:46:30 ip-172-31-24-160 web[4568]: [2023-11-28 05:46:30 +0000] [4568] [ERROR] Reason: App failed to load.
```

## 解決方法
コード内の`app`を`application`に変えるだけだった

```diff_python
from flask import Flask

# 変更前
- app = Flask(__name__)
- @app.route("/callback", methods=['POST'])
    # 省略

# 変更後
+ application = Flask(__name__)
+ @application.route("/callback", methods=['POST'])
    # 省略
    
```
Next.js 13のMetadata APIでcanonicalを設定する方法

Next.js 13でのmetaタグの管理にはMetadata APIを使用しますが、その際`canonical`の表示に少しハマったのでメモします。

## Metadata APIとは

Next.js 12以前まではHeadコンポーネントを利用して直接書いていましたが、Next.js 13以降では推奨されていません。
```react:index.tsx
import Head from 'next/head'
 
function IndexPage() {
  return (
    <div>
      <Head>
        <title>My page title</title>
        <meta property="og:title" content="My page title" key="title" />
        <link rel="canonical" href="https://example.com/" />
      </Head>
    </div>
  )
}
 
export default IndexPage
```

https://nextjs.org/docs/pages/api-reference/components/head

以下のコードは公式サイトの[ドキュメント](https://nextjs.org/docs/app/building-your-application/optimizing/metadata)に記載されているものです。

静的ページの場合はmetadata関数内にメタデータを記述します。動的ページはgenerateMetadata関数を利用して記事ページなどのIDをparamsとして受け取り、そこからフェッチしたデータからメタデータを取得します。

```react:layout.tsx
import { Metadata } from 'next'
 
// 静的メタデータの場合
export const metadata: Metadata = {
  title: '...',
}
```

```react:layout.tsx
import { Metadata } from 'next'

type Props = {
  params: { id: string }
}
 
// 動的メタデータの場合
export async function generateMetadata({ params }: Props): Promise<Metadata> {
  const { id } = params
  const post = await fetch(`endpoint/${id}`).then((res) => res.json())

  return {
    title: post.title,
  }
}
```

## canonicalはalternateもセットに考える

ただし、canonicalはそのまま`canonical`と記述するとエラーとなるため、以下のようにalternatesプロパティを使う必要があります。

```diff_javascript:layout.tsx
import { Metadata } from 'next'

// 静的メタデータの場合
export const metadata: Metadata = {
  title: "Your title",
  description: "Your meta description",
- canonical: "https://example.com",
+ alternates: {
+   canonical: "https://example.com",
+ },
}
```

```diff_javascript:layout.tsx
import { Metadata } from 'next'

type Props = {
  params: { id: string, slug: string }
}

// 動的メタデータの場合
export async function generateMetadata({ params }: Props): Promise<Metadata> {
  const { id, slug } = params;
  const post = await fetch(`endpoint/${id}`).then((res) => res.json());

  return {
    title: post.title,
    description: post.description,
-   canonical: `https://example.com/${slug}`,
+   alternates: {
+     canonical: `https://example.com/${slug}`,
+   },
  };
}
```

私自身`alternate`タグを意識して使ったことがなかったのですが、PC用ページとは別にスマホ用ページが存在する場合、URL正規化するためにGoogleに伝えるのが`alternate`タグだそうです。

https://seolaboratory.jp/41731/

Metadata APIの場合、`alternate`と`canonical`をセットとして覚える必要がありそうですね。

## 参考資料

この記事はこちらを参考に作成しました。

https://stackoverflow.com/questions/75938020/how-to-set-canonical-tag-in-nextjs-13
mindの研究 その40 - mindでプログラムランチャーを作る

# 概要
mindが謎なので調べてみた。
練習問題やって見た。

# 練習問題
mindでプログラムランチャーを作れ。

# 方針
- batの力を借りる。

# サンプルコード

```
出力ファイルは　ファイル。



メインとは
　文字列１は　文字列
　「0：　メモ帳を開く」を　一行表示する
　「1：　電卓」を　一行表示する
　「2：　C#開発環境」を　一行表示する
　文字列入力し　文字列１に　入れ
　文字列１が　「0」に　等しい文字列　ならば
　　「notepad.exe」を　プログラム実行
　つぎに
　文字列１が　「1」に　等しい文字列　ならば
　　「calc.exe」を　プログラム実行
　つぎに
　文字列１が　「2」に　等しい文字列　ならば
　　「test.bat」で　出力ファイルを　新規オープンし
　　「@echo off」で　出力ファイルに　一行書き込み
　　「set PATH=C:\Windows\Microsoft.NET\Framework\v4.0.30319;%PATH%」で　出力ファイルに　一行書き込み
　　「echo C#開発環境」で　出力ファイルに　一行書き込み
　　出力ファイルを　クローズし
　　「test.bat」を　プログラム実行
　つぎに


```

# 実行結果

```
0：　メモ帳を開く
1：　電卓
2：　C#開発環境

```

以上。
Web開発にAIを取り入れる　〜Golangの活用〜

# はじめに
AIの進化は、Web開発の新たな可能性を開いています。CloudflareのWorkers AIは、この進化を具現化し、Stable DiffusionとCode Llamaを通じてWebアプリケーションにAIの力を容易に組み込む機能を提供します。このブログでは、特にGolangを使用してこれらのAI機能を活用する方法を解説します。

## Cloudflare Workers AIについて
Cloudflare Workers AIは、Stable Diffusionによる画像生成とCode Llamaによるコード生成の2つの強力な機能を提供します。開発者は、テキストから画像を生成したり、多様なプログラミング言語でコードを生成したりすることができます。

## GolangによるWorkers AIの利用
Golangは高性能で効率的なWebサービス開発に適した言語です。ここでは、Golangを使用してCloudflareのWorkers AIにアクセスし、AI機能をWebアプリケーションに組み込む方法を紹介します。

### 1. 環境の設定
Golangの開発環境をセットアップします。Golangの公式サイトからインストーラーをダウンロードし、指示に従ってインストールします。

### 2.Cloudflare APIの使用
CloudflareのAPIを使用するためには、APIトークンが必要です。以下は、GolangでCloudflare APIを呼び出すサンプルコードです。
```go
package main

import (
    "bytes"
    "fmt"
    "net/http"
)

func main() {
    apiUrl := "https://api.cloudflare.com/client/v4/accounts/{account-id}/ai/run/@cf/stabilityai/stable-diffusion-xl-base-1.0"
    apiToken := "{api-token}" // あなたのAPIトークンをここに入れてください

    jsonStr := []byte(`{"prompt": "A happy llama running through an orange cloud"}`)
    req, err := http.NewRequest("POST", apiUrl, bytes.NewBuffer(jsonStr))
    if err != nil {
        panic(err)
    }

    req.Header.Set("Authorization", "Bearer " + apiToken)
    req.Header.Set("Content-Type", "application/json")

    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()

    fmt.Println("response Status:", resp.Status)
    // ここでレスポンスの処理を行います
}
```
このコードは、Stable Diffusionを使用して指定されたプロンプトに基づいた画像を生成するリクエストを送信します。

### 3.結果の取得と処理
リクエストを送信した後、レスポンスを受け取り、画像データや生成されたコードを処理します。Golangの標準ライブラリは、HTTPレスポンスの処理に優れています。

## 応用例
Golangを使用してWorkers AIを活用する応用例をいくつか紹介します。
+ 画像生成
 ブログやウェブサイトにユニークな画像を生成し、コンテンツをより魅力的にする
+ コード自動生成
 難しいプログラミング課題のためのコードを生成し、開発プロセスを加速する
+ データ解析
生成されたデータを解析し、ビジネスの意思決定に役立つ洞察を得る

# まとめ
CloudflareのWorkers AIとGolangを組み合わせることで、Webアプリケーション開発の新しい地平が開けます。この技術は、画像生成やコード生成の分野で革新的な可能性を提供し、開発者に新たな創造的な道を開くものです。Golangの柔軟性とパフォーマンスを活かし、AIの力をWebアプリケーションに組み込むことで、よりリッチでインタラクティブなユーザーエクスペリエンスを提供することができます。

https://blog.cloudflare.com/workers-ai-update-stable-diffusion-code-llama-workers-ai-in-100-cities/
中学生も競プロしようぜ!!

# 中学生こそ競プロするべき3つの理由
今年からAtcoderを始めたので振り返ります。

## 1.プログラミングに関する基礎が学べる
競プロではたいてい基礎的な問題からめちゃ難しい問題まであります。Atcoderでは自分もまだ3完(A~C)できるかできないかくらいですが、何度もコンテストに出ると少しずつプログラムがかけるようになってきます。
さらに高校では情報Ⅰで基礎的なプログラミングについて学ぶので、先に少しやっておくといいと思います。脳内コンパイル的な能力はすぐには身につかなそう。
また、ハイレベルな問題になると高校数学的な知識も必要になるらしく、数学を学ぶ目的にもなるかも（未検証）
## 2.全国の中高生と戦えて競争意識を持てる
またAtcoderではAJL(Atcoder Junior Leage)と呼ばれる全国の同学年の中高生とスコアで競い合うことができます。学校で同じ話題で話せる人が少なかったり、コンテストが少なくてプログラミングへのモチベがないとかであればモチベにつながると思います。
[Atcoder公式 AJL](https://atcoder.jp/contests/ajl2023)
また、JOI（情報オリンピック,~高2）という競プロのすごい人を選ぶコンテストもあるのでこれもプログラミング学習のモチベになります。
[JOI公式](https://www.ioi-jp.org/joi/2023)
## 3.楽しい!!
シンプルにこれが大きいです!!競プロをしていると少しずつ問題が解けていくようになったり、良いコードがかけるようになったり、処理速度が早くなったりと楽しいことがたくさんです。

競プロしよう!!
Node.jsのrequire.mainについて

# require.main
require.moduleは、モジュールオブジェクトで、Node.jsの起動時にロードされたエントリースクリプト表す。

# 例
Node実行時のディレクトリが /abusolute/path/to で、app.jsを実行する場合。

```entry.js
console.log(require.main);
```

Node.jsでapp.jsを実行
```
node entry.js
```
コンソールへの出力
```
Module {
  id: '.',
  path: '/absolute/path/to',
  exports: {},
  filename: '/absolute/path/to/entry.js',
  loaded: false,
  children: [],
  paths:
   [ '/absolute/path/to/node_modules',
     '/absolute/path/node_modules',
     '/absolute/node_modules',
     '/node_modules' ] }
```


## require.mainの利用
require.mainで取得できるモジュールオブジェクトの中には、エントリーファイルのpathを表す**filename** が含まれている。

これを利用して、Node.jsプロジェクトの実行ディレクトリを取得する記述が以下の通り。
/absolute/path/to/util/path.js
```path.js
const path = require('path');

module.exports = path.dirname(require.main.filename);
```

exportしているので、他のファイルからrequireして利用することができる。
/absolute/path/to/routes/sample.js

```sample.js
const path = require('path')
const express = require('express');

const router = express.Router();

// path.jsからexportしたディレクトリを利用
const routeDir = require('../util/path');

// /absolute/path/to/views/sample.htmlを返却
router.get('/',(req, res, next) => {
  res.sendFile(path.join(routeDir, 'views', 'sample.html'))
});
```
/absolute/path/to/app.js
```app.js
const express = require('express')
const bodyParser = require('body-parser')

// add routes
const sampleRoutes = require('./routes/sample');

const app = express();

// add body parser
app.use(bodyParser.urlencoded({extended: false}))

app.use(sampleRoutes);

app.listen(3000);
```

# 参考
Node.jsのドキュメントはこちら
https://nodejs.org/api/modules.html#requiremain
LinuxでSDマウント

SD内でパーティション作成するだけだと、マウントできない

以下を行う必要がある
1. SDカード内にパーティションを作成(e.g. sudo fdisk /dev/sda)
1. パーティションをファイルシステム指定してフォーマット(e.g. sudo mke2fs -t ext4 /dev/sda1)
1. パーティションをマウント(e.g. sudo mount /dev/sda1 /mnt)
MetaQuest3でトンカツを眺めながら飯を食ったらバズった件

# 経緯
私の所属する株式会社PsychicVRLabでは、毎週木曜にオープン社内報ラジオ[水谷享平のサイキックラジオ](https://www.youtube.com/@psychic-radio)というYouTubeライブ配信を行っています。
弊社水谷がメインパーソナリティとして、主に社内のメンバーや関わりの深い人をゲストとして迎えてXR業界の話や、全く関係ないアニメの話をしています。

で、10/26の放送では私がゲストで出演して、Meta Quest 3について語る回にするという予定でした。
個人的に最速でデバイスを買ったものの忙しくてあまり触れておらず、せっかく語るのに「いや〜忙しくてまだ何も触ってないんですよね〜」みたいなしょっぱいコメントするのもな。
と思ってたんですよね。

そんじゃ、ま、いっちょTLを温めておきますか、ぐらいのノリでネタを投下したら何かバズった。

https://x.com/hacha/status/1716823948112105630?s=20

ちなみに件のゲスト回はこちら
<iframe width="560" height="315" src="https://www.youtube.com/embed/Lyt8DiU9QMk?si=q1PoRimSWACWkuW4" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>

# 技術解説

では具体的にどのようなアプリを使って実現しているかを解説していきます。
ざっくりと大まかな手順は以下の通り。

1. トンカツを買ってくる
2. Scaniverseでスキャンする
3. Blenderで不要な頂点を削る
4. モデルをSketchfabへアップロード
5. FigminXRでモデルを表示

では１つずつ見ていきましょう。

## １． トンカツを買ってくる

まずはなるべく美味しそうなトンカツを買ってきます。
せっかくなんでここはケチらず大きいやつを買いましょう！

## 2． Scaniverseでスキャンする

言わずと知れた無料3DスキャンアプリのScaniverse

https://apps.apple.com/jp/app/scaniverse-3d-scanner/id1541433223

今回はLiDAR搭載のiPhone12 proを使ったが、LiDAR非搭載の機種でもフォトグラメトリ処理でスキャンが可能。
どうスキャンするのがベストなのかは難しいところだが、とりあえずライト3つ当ててみた。
<img width="30%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5671/a61c23ff-9c7a-8f90-7b46-d3173327627a.jpeg" />

## 3． Blenderで不要な頂点を削る

<img width="50%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5671/4fafaa04-14ab-6571-90a9-0775ee88f75a.png" />

皿ごとスキャンしているので、うまくトンカツだけを残して不要な頂点を削除していきます。
この作業が地味にめんどくさい。
ただ後述する理由によりトンカツだけを切り抜いておく必要があります。

## 4． モデルをSketchfabへアップロード

加工したモデルデータは[Sketchfab](https://sketchfab.com/)へアップロードして公開状態にします。
ちなみにアップロードしたモデルデータはこちら

https://sketchfab.com/3d-models/tonkatsu-c6d6b90a66654b558e912507a7b0ae67

## 5． FigminXRでモデルを表示

FigminXRをMeta Quest 3にインストールします。
こちらは有料アプリですが、探した中ではQuest3のパススルーに対応していてSketchfabからのモデルインポートできる唯一のアプリでした。
もし他にも使えそうなアプリがあったら是非教えてください。

https://www.meta.com/ja-jp/experiences/6849182851823457/

"tonkatsu"で検索すると先程アップロードしたモデルが見つかるので、それをキャベツを盛り付けた皿の上にうまく盛り付けます。
ちなみにオクルージョン機能はないので、トンカツより手前に物理現実のオブジェクトがあっても無視されて最前面に表示されます。
スキャンデータから皿を取り除いてトンカツだけ切り出していたのはこのためで、皿ありのモデルだとキャベツより手前に皿が表示されてしまうからです。

# あとがき

Meta Quest 3は価格と性能のバランスがかなり良いデバイスですね。
Questシリーズは全て買ってきましたがかなり軽くなってるし、当面はデファクトスタンダードなデバイスになるんじゃないかと思います。
今年のサンタさんへのおねだりはMeta Quest 3で決まりですね！

# 宣伝
PsychicVRLabではUnityエンジニア・サーバーサイドエンジニアを募集しています！！
ご応募お待ちしています！！

https://www.wantedly.com/projects/1360135

https://www.wantedly.com/projects/1360123
mergeで結合したDataFrameの行の順番が変だと慌てたけれど単純なミスだったという話

# 結論
pandas.merge()の引数の順番に気を付けましょう！！

# もっと詳しく
pandasのmergeは引数として書かれるDataFrameの順番によって、結合後のDataFrameの順番が変わります。
「先に書いた方(left)のDataFrameを基準」に、後に書いたもの(right)が共通項をもとに順番が入れ替わってくっつくためです。
![df_merge.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3629447/95b8b43c-429f-af43-bfa5-b141281ddc96.png)

この仕様、言われてみれば「そりゃそうじゃ」という話ですがお恥ずかしながらわたくし、一度やらかしかけました。

pandas.merge()の公式ドキュメントはこちら: 
 https://pandas.pydata.org/docs/reference/api/pandas.merge.html

## 具体例
以下のような2つのDataFrameがあるとします。
```python
import pandas as pd

df = pd.DataFrame({'column1':['a', 'b', 'c']}, index=['row1', 'row2', 'row3'])
df2 = pd.DataFrame({'column1':['b', 'c', 'a'], 'column2':['bb', 'cc', 'aa']}, index=['row1', 'row2', 'row3'])
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3629447/b77815d8-e9fc-4f10-d30f-abea2e9926ea.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3629447/ef18648b-5ad1-d2cf-e1d1-4006aabb4ff7.png)
これらをmerge関数で結合するときに（1つ目と2つ目の引数の順番に注目！）
```python
df_merged = pd.merge(df2, df, on=['column1'], how='inner') # 先に書かれているdf2が基準になる
```
としてしまうと
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3629447/4322047d-84b4-3dd1-18a8-8cf0ad058dbf.png)
まあ、こうなりますね。

で、これに気付かずに
「dfは上から'a,b,c'の順になっていたはず！じゃあその通りの順にcolumn4を作っちゃうぞ～^^」
とかやるとですね。
```python
df_merged['column3'] = ['aaa', 'bbb', 'ccc']
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3629447/dc35e673-249a-20e9-2555-46d9a5ad3d49.png)

こうなってしまいます。アルファベットがずれていますね。

もちろん以下のようにすればアルファベットがそろいます。
```python
df_merged = pd.merge(df, df2, on=['column1', 'column2'], how='inner') # df→df2の順になっている
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3629447/9bcd22f0-ddb8-cd3b-0798-15342d6b1208.png)
```python
df_merged['column3'] = ['aaa', 'bbb', 'ccc']
df_merged
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3629447/35f27ee5-a2e0-5854-02bd-c16d9716fcde.png)

（やったー！）

# まとめ
今回は単なるアルファベットという順番やずれがわかりやすいもので例示しましたが、実際には順番とかわからない数字や文字列だったりするわけですね（実体験）。で、ずれてしまったまま気付かずにスルーしてた可能性もあるわけで……。

皆様もmergeの引数に……というか、単純なミスにはお気を付けくださいませ！
コミットメッセージのスタイルを統一する

HRBrain Advent Calendar 2023 12/3 の記事です．

https://qiita.com/advent-calendar/2023/hrbrain

## はじめに
開発をする際に，コミットメッセージを書くことが常だと思いますが，どのようなメッセージの書き方が適切か迷ったことがありませんか？

わかりやすく書かれているコミットメッセージで以下のようなメリットがあります．
- 共同開発者（または未来の自分）に作業内容が伝わる
- 変更履歴が追いやすくなる・コントリビュートしやすくなる
- 自動化ツールが作成しやすくなる（changelog 生成など）

この記事では，コミットメッセージの書き方の規約の一つである Conventional Commits と，コミットメッセージを作成する際に使用できるコマンドラインツール Commitizen を紹介します．

## コミットメッセージの規約
[Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) 規約では，通常のコミットメッセージに軽量な枠組みを追加したものになります．
> The Conventional Commits specification is a lightweight convention on top of commit messages.

構造としては以下のようになります．
```
<type>[optional scope]: <description>

[optional body]

[optional footer(s)]
```

- `<type>` は，`fix`，`feat` などのコミット内容の種類が一言で表されています．
  - [commitlint]() というリンターでは，`build`，`chore`，`ci`，`docs`，`style`，`refactor`，`pref`，`test` なども推奨されています．（参考：[@commitlint/config-conventional](https://github.com/conventional-changelog/commitlint/tree/master/%40commitlint/config-conventional)）
- `<description>` は，変更内容の短い要約が入ります．

## Commitizen の導入
[Commitizen](https://github.com/commitizen/cz-cli) は，Conventional Commits 規約でコミットメッセージを作成するためのコマンドラインユーティリティです．

導入方法としては，以下のようになります．
- Commitizen をインストールします．
```
npm install commitizen -g
```

- プロジェクトを [Commitizen フレンドリー](https://github.com/commitizen/cz-cli#making-your-repo-commitizen-friendly) にします．
  - `config.commitizen` が `package.json` に追記されます．
```
# npm
commitizen init cz-conventional-changelog --save-dev --save-exact

# yarn
commitizen init cz-conventional-changelog --yarn --dev --exact
```

- `git commit` の代わりに Commitizen を使用します．
  - 対話型でコミットメッセージを作成していきます．
```
git cz
```

### Commitizen のカスタマイズ
Commitizen で [cz-customizable](https://github.com/leoforfree/cz-customizable) プラグインを追加することによって，独自のルールを定義し自由にカスタマイズすることができます．

Commitizen がすでにインストールされている前提で，導入方法としては，以下のようになります．
- cz-customizable プラグインをインストールします．
```
npm install cz-customizable -g
```

- `package.json` 内のパスを変更します．
```json:package.json
...
"config": {
  "commitizen": {
    "path": "node_modules/cz-customizable"
  }
}
```

- リポジトリ内に `.cz-config.js` を作成し，自由にカスタマイズします．
  - [leoforfree/cz-customizable/.cz-config-EXAMPLE.js](https://github.com/leoforfree/cz-customizable/blob/master/cz-config-EXAMPLE.js) が参考になります．

## 実際に使ってみた
CLI で動かすと以下のようになります．
![](https://github.com/pasca-l/qiita-hrbrain/assets/70369563/49080975-87e8-4ad4-a7b4-e460b42f4a5b)

コミットメッセージが `<type>: <description>` の形になっていることが確認できました．
![](https://github.com/pasca-l/qiita-hrbrain/assets/70369563/2b456856-d404-47f6-b227-ddb84ceb403f)

## おまけ：git-cz について
[git-cz](https://github.com/streamich/git-cz) という，Commitizen ツールのラッパーもあります．大きな違いとしては，インストール後の初期化コマンドを実行する必要がなくなることです．

導入方法もほとんど同じで，以下のようになります．
- git-cz をインストールします．
```
npm install -g git-cz
```

- `git commit` の代わりに git-cz を使用します．
```
git cz
```

## まとめ
コミットメッセージの規約 Conventional Commits と，その規約に則ったメッセージ生成ツール Commitizen の紹介をしました．プロジェクトに合わせてコミットメッセージを適切に保ち，円滑な開発を目指しましょう！
【データベース勉強中】さくっとSQL勉強するならMAMPがいい


# はじめに
今までSQLを勉強するためにDockerでSQL環境を構築してたけど、MAMPならdocker-compose.ymlやdockerfileを見る必要もなくボタン押すだけで起動できて便利だった。

# MAMPについて

### MAMPとは
MAMPは、Macに「Apache」と「MySQL」と「PHP」をインストールして、ローカルにサーバー環境を構築することができるソフトウェアのこと。

https://www.mamp.info/en/windows/

### とりあえずダウンロードする
ダウンロードは下記。
それぞれの環境に合ったverをダウンロードする

https://www.mamp.info/en/downloads/

### MAMPの簡単な使い方
とりあえずindex.phpをブラウザに表示させてみる。
MAMPのドキュメントルートは「/Applications/MAMP/htdocs/」なのでそこにindex.phpを置く。

```
cd /Applications/MAMP/htdocs/
touch index.php
vi index.php
```

index.phpの中身は「Hello!」を記載した。

#### WEBサーバーを起動する
右上の「Start」を押すとWEBサーバー（Apache）が起動する。

![スクリーンショット 2023-11-28 13.57.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3587166/6fbc9b64-c6a2-5ae6-a963-61341a04ded2.png)

起動したらその横の「WebStart」を押すと

![スクリーンショット 2023-11-28 13.58.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3587166/8eec40c0-c416-b066-c7d8-ee095505486b.png)

スタートページが表示される。

![スクリーンショット 2023-11-28 13.59.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3587166/85f16922-b5e6-1e92-04ac-32ca0803d562.png)

さらに「My Website」から遷移すると先ほど作成したindex.phpの「Hello!」が表示されるはず！

![スクリーンショット 2023-11-28 14.01.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3587166/d0775c43-935f-6979-5de7-c3b1f6e3c5bc.png)

# MAMPでSQLをコマンドで触ってみる
MAMPのコマンドでコマンドを実行するには「/Applications/MAMP/Library/bin/」に移動する。

```
cd /Applications/MAMP/Library/bin/
```

MySQLへログインする

```
./mysql -u root -p
Enter password:
```
※初期パスっワードはroot

```
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 19
Server version: 5.7.39 MySQL Community Server (GPL)

Copyright (c) 2000, 2022, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql>
```

ログイン成功したらSQL使えるようになる！

```
mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
4 rows in set (0.00 sec)
```

試しにデータベースを作成してみる。

```
create database takegons;
Query OK, 1 row affected (0.02 sec)

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
| takegons           |
+--------------------+
5 rows in set (0.00 sec)
```

phpMyAdminでも確認できた！

http://localhost:8888/phpMyAdmin5/

![スクリーンショット 2023-11-28 14.12.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3587166/4e351ad9-b442-95b8-ff36-0f8902767f98.png)
[Ruby]配列やハッシュの展開

配列はオブジェクトの前に`*`を、ハッシュはオブジェクトの前に`**`を付けることで「展開」することができます。「展開」が何を意味しているかは、[マージ](#マージ)や[引数](#引数)の欄を参照してイメージを掴んでください。

## マージ

`merge`メソッドや`reverse_merge`メソッドを使ってもハッシュをマージできますが、`**`で展開すると簡潔に書けます。

```ruby
pp ([1, *[2, 3]])
# => [1, 2, 3]

pp ({ hoge: 1, **{ fuga: 2 } })
# => { hoge: 1, fuga: 2 }
```

ハッシュのキーが重複している場合は、後に指定した値が優先されます。ただし、展開したハッシュの値が展開先のハッシュの値を上書きする場合は、警告が出ます。

```ruby
pp ({ hoge: 1, **{ hoge: 2 } })
# warning: key :hoge is duplicated and overwritten on line 1
# => { hoge: 2 }

pp ({ **{ hoge: 2 }, hoge: 1 })
# => { hoge: 1 }
```

## 引数

配列の展開を使うと、位置引数にまとめて引数を指定できます。

```ruby
def add(x, y)
    x + y
end

ary = [1, 2]
pp add(*ary)
# => 3
```

ハッシュの展開を使うと、キーワード引数にまとめて引数を指定できます。Ruby3.0より前では、単にハッシュを渡せばキーワード引数を指定したものとして扱われていましたが、3.0以降では展開を明示する必要があります。(参考: [https://www.ruby-lang.org/ja/news/2019/12/12/separation-of-positional-and-keyword-arguments-in-ruby-3-0/](https://www.ruby-lang.org/ja/news/2019/12/12/separation-of-positional-and-keyword-arguments-in-ruby-3-0/))

```ruby
def full_name(first_name:, last_name:)
    "#{first_name}/#{last_name}"
end

hash = {
    first_name: "TARO",
    last_name: "TANAKA"
}
pp full_name(**hash)
# => "TARO/TANAKA"
```

メタプログラミングを行うときには、位置引数(`args`)とキーワード引数(`kargs`)をまとめて取り扱うことがあります。

```ruby
def call(func, args, kargs):
    func(*args, **kargs)
end
```
簡単にWordpressのデータ移行｜UpdraftPlus Backup/Restore で引越しする方法

# 移行元Wordpressで、プラグイン "UpdraftPlus Backup/Restore"をインストール

1. Wordpress管理画面の左サイドメニューから「プラグイン」を選択
2. 「新規プラグイン」ボタンを押して、"UpdraftPlus Backup/Restore"を検索。インストールボタンを押して、インストールできたら「有効化」ボタンを押す
3.  Wordpress管理画面の左サイドメニューの「設定」の「UpdraftPlusバックアップ」を選択

# 移行元でバックアップ作成

バックアップ/復元タブの中央右側あたりある「今すぐバックアップ」をクリック。
記事数が多い場合は時間がかかるので、そのまま10分程度放置しておく。

# バックアップをPCにダウンロード
バックアップが作成されると、バックアップ/復元タブの下の方にある「既存のバックアップ」という表にバックアップが作成されているので、「データをバックアップ (クリックしてダウンロード)」の列の自分が移動させたい内容のボタンをクリックする。
ブログ記事だけ移動させたい場合は「データベース」をクリック。

「既存のバックアップ」という表の上に表示される「お使いのコンピューターにダウンロード」をクリックするとPCヘ移行元のデータベースの内容がダウンロードされる。

# 移行先にプラグイン "UpdraftPlus Backup/Restore"をインストール
先ほど移行元で行った時と同様に、プラグインをインストールして、Wordpress管理画面の左サイドメニューの「設定」の「UpdraftPlusバックアップ」を選択して開く。

# アップロード作業(引越し作業)
バックアップ/復元タブの「既存のバックアップ」という文字の下に「他のタスク」があり、そこの並びにある「バックアップファイルをアップロード」をクリック。

ファイル選択のポップアップが出るので、先ほど移行元でダウンロードしたファイルを選択してぽちぽちすると移行元の内容が復元ができる。

この時、管理画面のユーザー情報も一緒に移行されてしまうので、前に使っていた管理者パスワードが使えなくなる。移行元のユーザー名とパスワードを使用すれば、ログインできる。
KL Divergence and Breman Distance


Kullback-Leibler (KL) Divergence is an important mathematical idea frequently used in Deep Learning literature. It is defined as the following:
$$
	D\_KL(P||Q) = \sum\_{x\in \mathcal{X}} P(x) \log (\frac{P(x)}{Q(x)}) \tag{1}
$$

One of the famous usage is in the GAN (at least original) loss function, where the optimization function is defined as:

$$
	\min\_G \max\_D \mathrm{E}\_{x \sim p\_r} \log [D(x)] + \mathrm{E}\_{z \sim p\_z} \log [1-D(G(z))]
$$

which equivalently optimizes KL divergence between $p_g$ and $p_r$.

By staring at eq. (1), one might ask if this value can be negative. Since sometimes the argument of the log could be very small and causing the result to be very negative.

But the answer is NO. KL divergence will never be negative. It can be proved using Gibbs' inequality whose proof is easy enough to be written here! First we need to assume 2 discrete probability distributions $P={p_1,…,p_n}$ and $Q={q_1…,q_n}$. Each element will represent a number between 0 and 1 (both inclusive) and sum in each distribution would be 1.

$$
	- \sum\_{i \in I} p\_i \ln \frac{q\_i}{p\_i} \ge - \sum\_{i \in I} p\_i \big( \ln \frac{q\_i}{p\_i} - 1\big) = -\sum\_{i\in I} q\_i + \sum\_{i\in I}p\_i = - \sum\_{i\in I} q\_i + 1 \ge 0
$$

Equivalently, over the index set $I$, we will have

$$
	- \sum\_{i\in I}p\_i \ln \frac{q\_i}{p\_i} \ge 0
$$

which finishes the proof.

Another way of proof is use the fact that KL divergence is a kind of Bregman distance.

What is Bregman distance? Well, it is a measure of distance between points defined in terms of a strictly convex function where points are interpreted as probability distributions.

I will provide the definition to Bregman distance to finish this essay.

Let $F:Σ→R$ be a continuously-differentiable, strictly convex function defined on a closed convex set $Σ$

The Bregman distance associated with F for points $p,q∈Σ$ is the difference between the value of F at point $p$ and the value of the first-order Taylor expansion of F around point $q$ evaluated at point $p$

$$
	D\_F(p,q) = F(p)-F(q)-\langle \nabla F(q), p-q \rangle
$$

Thanks for reading! Cheers!
MacBookで起動プロセスが完了しなくなってしまった

# 状況
知人のMacBookがある日突然起動するとこのような画面が表示されるようになってしまった．![macos-startup-screen-disk-unavailable.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1734281/809ea82e-abe4-f800-b3d5-351316d51207.jpeg)
# やったこと
1. ディスクユーティリティ
まずは，[こちらの記事](https://support.apple.com/ja-jp/102611)にある通り，ディスクユーティリティを試してみました．しかし，First Aidがうまくいかず，断念．
2. 発見
1\.をやっている中で，フォルダ内にデータが残っていることを発見．そこで，取り出せないかと色々やっている中で，起動後すぐに`command (⌘) + R`→`ユーティリティ`内にターミナルがあることを発見．ここからアプローチをすることに．
3. ファイルコピー
`/Volumes/Macintosh HD - Data`内にデータがあるので，`cdコマンド`で移動する．
ここで利用するのが，`cpコマンド`です．
```:使い方
cp 元ファイルのパス コピー先のパス
```
よって，元ファイルを`/Volumes/Macintosh HD - Data/...`とし，コピー先のパスをHDD内の適当なパスを選んでやれば良いということになります．
# あとがき
様々な記事を検索している中で，この方法が載っていなかったために，執筆しました．誰かの役に立てば嬉しいです．
【2023年11月版】Spring Boot 3 で Tomcat のアクセスログ出力をする

# はじめに

Spring Boot で　Tomcat 側のアクセスログを出力したいな。
設定追加だけでいけるっしょ！
。。。からハマリがありました。

# 環境

- Spring Boot 3 (3.1.x)
- AmazonLinux 2023

# 結論

`server.tomcat.basedir` が設定されていないと動かない

```
server:
  tomcat:
    basedir: ./
```

# 手順

プロパティ設定一覧を見て、簡単に設定するだけで動くでしょ。

[Spring アプリケーションプロパティ設定一覧](https://spring.pleiades.io/spring-boot/docs/current/reference/html/application-properties.html#application-properties.server.server.tomcat.accesslog.buffered)

ChatGPTさんも、そのようにおっしゃってる。

```text:ChatGPTさんの教えてくれた設定
server:
  tomcat:
    accesslog:
      enabled: true
      pattern: common
      directory: logs
      prefix: access_log
      suffix: .log
      rotate: true
```

動かしてみる。。。動かない。

```text:basedirを加えてみる
server:
  tomcat:
    basedir: ./
    accesslog:
      enabled: true
      pattern: common
      directory: logs
      prefix: access_log
      suffix: .log
      rotate: true
```

動く、logs フォルダに、ログが出力されている。
`basedir` を設定しないと、一時ディレクトリが使用されるらしい。

ハマって、 server.tomcat の設定読み進めるまで、わかんなかった。

# おわりに

かんたんでしたね
AWS Resilience Hub を触ってみた

本日参加した「WPS301 IS your AWS GovCloud(US) architecture resilient?」というセッションで、AWS Resilience Hubというあまり聞きなじみのないサービスを触ってみたので記事にまとめました。

# AWS Resilience Hub
AWS Resilience Hub は、AWS 上のアプリケーションの回復力を定義、検証、追跡するための一元的な場所を提供します。
AWSにおいて、回復性に関する推奨事項を提供する最も推奨される方法です。
RTOとRPOが最適になるよう、推奨事項を最適化し、そのためにどのような変更が必要なのかも提示してくれます。
* AWS Resilience Hub は、レジリエンシーポリシーで定義された RTO/RPO 要件を満たすためのアーキテクチャの変更を含むオプションなど、評価レポート内でレジリエンスの推奨事項を提供します。

## 利用方法
### ① レジリエンスポリシーの作成
RTOとRPOの目標を定義するポリシーを作成します。
作成済みのポリシーセットから選択することも、独自のポリシーを作成することも可能です。

### ② アプリケーションの追加
リソースコレクションとEKSの2つのデータソースでアプリケーションを記述可能です。
以下
* リソースコレクション：以下のうち1つからリソースを検出します。
CloudFormation スタック、AWS Resource Groups、AppRegistry アプリケーション、Terraform state filesの
* EKS のみ：EKSクラスター内の名前空間のリソースを選択する場合に選択します。
* リソースコレクション ＆ EKS：上記のどちらともからリソースを検出したい場合に選択します。

### ③ 評価レポートを確認
AWS Resilience Hubのアプリケーションの構造タブに移動します。
評価レポートを確認します。
AWS Resilience Hub に、アプリケーションで識別されたすべてのリソースが表示される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3115411/11627af8-8311-7c47-eab1-696f4dea048a.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3115411/597d5a96-eca4-1e30-cdf0-e712802a7a83.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3115411/f2a150f2-43d6-2094-d36f-dd6c786257dc.png)

AWS Resilience Hub は、レジリエンシーポリシーで定義された RTO/RPO 要件を満たすためのアーキテクチャの変更を含むオプションなど、評価レポート内でレジリエンスの推奨事項も提供されます。

### ④ 回復性向上のための推奨事項を確認
アプリケーションの各コンポーネントについて、AWS Resilience Hub はレジリエンス要件を満たすための推奨オプションを提示します。
また、提案されたアプリケーションアーキテクチャの変更によって追加コストが発生する可能性があるかどうかも示されます。
以下の観点からオプションは1～3つ出てくるようです。
* コストの最適化
* 最小限の変更で最適化
* アベイラビリティゾーン（AZ）のRTO/RPOのために最適化

※S3 についての以下の推定コストは、 $0 と表示されますが、このS3 バケットにはデータが保存されていないためです。AWS Resilience Hub は、予想されるコストを見積もって提供します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3115411/c640c2eb-11d8-930f-0fe3-f6a09ce8b1cd.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3115411/40625187-7da0-2bfd-cdf0-7460d7d5d96f.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3115411/0bc432e4-ea0a-eb5b-1106-64c8ad227637.png)

### ⑤ リソースの設定を変更し、再評価を実行
ポリシーに合格しました！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3115411/edd3bd29-13b8-d546-79a4-b6d3b4ebeca1.png)


### ドリフト検出
* AWS Resilience Hub は、レジリエンスドリフト検出を提供します。この機能を使用すると、SNS トピックを介した通知をオプトインして、アプリケーションがビジネスで設定された復旧目標を満たさなくなった場合に警告を発することができます。

## 運用上の推奨事項
AWS Resilience Hubの運用にあたり、以下の3点が推奨されています。

### アラーム
アプリケーションの正常性を監視し、指定したメトリクスがしきい値に達した場合にアラートを出すように設定しましょう。
アラームをテストして、しきい値が適切に設定されていることを確認することが重要です。
AWS Resilience Hub は、アプリケーションのレジリエンスターゲットに基づいて推奨されるアラームしきい値を提供します。

### 標準操作手順 (SOP)
中断が発生した場合にアプリケーションを回復するのに役立つ規範的な手順を提供します。
SOPは、アラームのトリガーとして使用可能で、特定の手順に従う必要がある場合に利用しましょう。

### AWS Fault Injection Service 実験テンプレート
AWS Fault Injection Serviceは、AWS リソースに制御された中断を導入することで、アプリケーションの回復力を検証するように設計されています。
カオスエンジニアリングは、本番アプリケーションの中断に耐えられるシステムの能力に自信を持たせるために、障害をシミュレートしてシステム上で実験する分野です。AWS Resilience Hub は、アプリケーションの耐障害性を理解して改善するための優れた方法ですが、カオスエンジニアリングでは、障害に対するアプリケーションの応答を確認できるため、信頼性が高まります。中断に耐えられるという仮説が正確かどうかをテストしましょう。

## おわりに
本番環境に入る前に問題を特定するために利用することが有用だと感じました。
回復性について、ダッシュボードから一元的に確認できるのは便利ですね！
JavaScriptでNextCloudからファイルをダウンロードする方法

```typescript
import fetch from 'node-fetch';
import * as fs from 'fs';

const nextCloudUrl = 'http://localhost:8081';
const username = 'username';
const password = '******';
const filePath = '/Readme.md';

const download = async () => {
  const url = `${nextCloudUrl}/remote.php/dav/files/${username}${filePath}`;
  const response = await fetch(url, {
    headers: {
      'Authorization': 'Basic ' + Buffer.from(username + ':' + password).toString('base64')
    }
  });

  if (!response.ok) {
    throw new Error(`Error: ${response.status}`);
  }

  if (response.body) {
    const fileStream = fs.createWriteStream('./downloaded_file.txt');
    response.body.pipe(fileStream);
    fileStream.on('finish', () => {
      console.log('File downloaded successfully.');
    });
  } else {
    console.log('No response body to download.');
  }
};

download().catch(error => console.error(error));
```

## クラス版

```typescript
import fetch from 'node-fetch';
import * as fs from 'fs';

class NextCloudClient {
  private nextCloudUrl: string;
  private username: string;
  private password: string;

  constructor(nextCloudUrl: string, username: string, password: string) {
    this.nextCloudUrl = nextCloudUrl;
    this.username = username;
    this.password = password;
  }

  private getAuthHeader(): string {
    return 'Basic ' + Buffer.from(this.username + ':' + this.password).toString('base64');
  }

  public async download(filePath: string, destination: string): Promise<void> {
    const url = `${this.nextCloudUrl}/remote.php/dav/files/${this.username}${filePath}`;
    const response = await fetch(url, {
      headers: {
        'Authorization': this.getAuthHeader()
      }
    });

    if (!response.ok) {
      throw new Error(`Error: ${response.status}`);
    }

    if (response.body) {
      const fileStream = fs.createWriteStream(destination);
      response.body.pipe(fileStream);
      return new Promise((resolve, reject) => {
        fileStream.on('finish', () => {
          console.log('File downloaded successfully.');
          resolve();
        });
        fileStream.on('error', reject);
      });
    } else {
      console.log('No response body to download.');
    }
  }
}

// Usage
const client = new NextCloudClient('http://localhost:8081', 'user', '******');
client.download('/Readme.md', './downloaded_file.txt').catch(console.error);

```
FastAPI でデータをダウンロードさせる

Python で生成したデータを FastAPI のエンドポイントからダウンロードさせるにはどうしたらよいか、と思ってやってみたら普通に `Response` に `Content-Disposition` を指定するだけでできた、という話。

```python
from fastapi import FastAPI, Response

app = FastAPI()

@app.get("/download")
def get_download():
    """データを CSV 形式でダウンロードする"""
    content = "あ,い,う\n1,2,3"  # CSV data
    filename = "data.csv"
    return Response(
        content=content.encode("cp932"),  # Microsoft Excel で開くために CP932 (≒ Shift_JIS) にエンコードする
        headers={"Content-Disposition": f'attachment; filename="{filename}"'},
        media_type="text/csv",
    )
```

ハンドラが返す型は以下のようにすればよさそう。

- (今回のように) インメモリなデータをそのまま返したいときは通常の Response
- ファイルとして保存されているデータを返したいときは FileResponse
- BytesIO などでストリームを扱いたい場合は StreamingResponse

(参考) [Custom Response - HTML, Stream, File, others - FastAPI](https://fastapi.tiangolo.com/advanced/custom-response/)

## (追記) エンコーディングを UTF-8 with BOM にする場合
UTF-8 文字列は必ずしも CP932 にエンコードできるわけではない。

``` python
'🍣'.encode('cp932')  #=> UnicodeEncodeError: 'cp932' codec can't encode character '\U0001f363' in position 0: illegal multibyte sequence
```

🍣 という文字は CP932 には存在しないので当然で、これを正面から解決することは困難といえる。

しかし、CP932 にエンコードしたい理由が「エクセルで開きたいから」なのであれば、UTF-8 のまま BOM をつけるという方法がある。Microsoft Excel は、BOM さえあれば UTF-8 の CSV ファイルを開くことができる。

UTF-8 with BOM にエンコードしたい場合、Python では `utf-8-sig` が使える。

``` python
        content=content.encode("utf-8-sig"),
```

(参考) [codecs — Codec registry and base classes — Python documentation](https://docs.python.org/3/library/codecs.html)

## (追記) ファイル名に非 ASCII 文字を含みたい場合
ファイル名に日本語などの非 ASCII 文字を使用すると「latin-1 に変換できない」と怒られる。

``` python
filename = "日本語ファイル名.csv"
return Response(
    content="あ,い,う\n1,2,3".encode("utf-8-sig"),
    headers={"Content-Disposition": f'attachment; filename="{filename}"'},
    media_type="text/csv",
)

#=> UnicodeEncodeError: 'latin-1' codec can't encode character in position ...
```

これは FastAPI のバグではなく「HTTP ヘッダは ISO-8859-1 (= latin-1) を使用する」と [RFC7230](https://datatracker.ietf.org/doc/html/rfc7230) で定められているからで、それ以外の文字を HTTP ヘッダで使いたい場合は URL エンコードする必要がある。

また [RFC6266](https://datatracker.ietf.org/doc/html/rfc6266) によると、`Content-Disposition` ヘッダでは `filename*=UTF-8''ファイル名` と指定することで、URL エンコードされた UTF-8 文字列を送ることが出来るらしい。

``` python
from urllib.parse import quote

filename = "日本語ファイル名.csv"
return Response(
    content="あ,い,う\n1,2,3".encode("utf-8-sig"),
    headers={"Content-Disposition": f"attachment; filename*=UTF-8''{quote(filename)}"},
    media_type="text/csv",
)
```

Python だとこうすればできた。
JavaScriptでtar.bz2で圧縮されたファイルを展開する

```typescript
import fs from 'fs';
import tar from 'tar';
import bz2 from 'unbzip2-stream';

function extractTarBz2(filePath: string, outputPath: string) {
  const inputStream = fs.createReadStream(filePath); 
  const bz2Stream = bz2();

  inputStream.pipe(bz2Stream).pipe(tar.x({C: outputPath}));

  bz2Stream.on('error', function() {
    console.log('Error extracting tar.bzip2 file');
  });

  tar.x({C: outputPath}).on('end', function() {
    console.log('Extraction complete!');
  }); 
}

extractTarBz2('data.tar.bz2', 'output');
```


## クラス版

```javascript
import fs from 'fs';
import tar from 'tar';
import bz2 from 'unbzip2-stream';

class Extractor {
  private filePath: string;
  private outputPath: string;

  constructor(filePath: string, outputPath: string) {
    this.filePath = filePath;
    this.outputPath = outputPath;
  }

  public extract() {
    const inputStream = fs.createReadStream(this.filePath); 
    const bz2Stream = bz2();

    inputStream.pipe(bz2Stream).pipe(tar.x({C: this.outputPath}));

    bz2Stream.on('error', function() {
      console.log('Error extracting tar.bzip2 file');
    });

    tar.x({C: this.outputPath}).on('end', function() {
      console.log('Extraction complete!');
    }); 
  }
}

const extractor = new Extractor('data.tar.bz2', 'output');
extractor.extract();
```
【個人的バグTips】iTerm2で日本語が入力できない件について


# はじめに
SQLの勉強を行なっている際、
terminalでDBへ日本語の文字データをinsertしようとしたところ、日本語が認識されないことがあった。
原因と対応内容をメモ。

# 実行環境
- OS：mac M1
- Terminal: iTerm2
- MySQL


# 事象
teamsテーブルへ日本語文字列を含むチームデータを追加（insert）しようとしたところTerminalで認識されなかった（入力しても反応しなかった）。

※teamsというテーブルへ追加する。

**追加するデータ**
| id | name |
|:-----------|------------:|
|1|広島カープ|
|2|ヤクルトスワローズ|
|3|横浜DeNAベイスターズ|
|4|阪神タイガーズ|
|5|中日ドラゴンズ|
|6|巨人ジャイアンツ|

**入力したコマンド**
別のテキストエディアでコマンドを作成し、terminalへ貼り付けるとチーム名部分（日本語）が抜け落ちる。
```

INSERT INTO teams
  (id, name)
VALUES 
(1, ''),
(2, ''),
(3, ''),
(4, ''),
(5, ''),
(6, '');
```

# 原因と対応
原因はbashの文字コードが問題だった。

まず現在の文字コードの確認。
```ruby:terminal
locale

LANG="ja_JP.UTF-8"
LC_COLLATE="ja_JP.UTF-8"
LC_CTYPE="ja_JP.UTF-8"
LC_MESSAGES="ja_JP.UTF-8"
LC_MONETARY="ja_JP.UTF-8"
LC_NUMERIC="ja_JP.UTF-8"
LC_TIME="ja_JP.UTF-8"
LC_ALL=
```

.bash_profileに下記を追加する。
※.bash_profileはコマンド（シェル）の設定ファイル（不可視ファイル）

```
vi ~/.bash_profile

export LC_ALL=en_US.UTF-8
export LANG=en_US.UTF-8
```

.bash_profileの変更を反映する。

```
source ~/.bash_profile
```

これで日本語が打てるようになる！
Live2dをUnityに入れた時のnull CubismFadeStateObserver.cs:224エラー

Live2dのモデルをunityに入れてモデルを少しでも公式と違うやりかたで差し替えると表題のエラーが出る
これの対処のしかた
Live2d直下のLive2D.fadeMotionListというファイルは一度目にモデルをUnityのエディタにドラッグで取り込んだ時に作られる。このファイルはまるごとドラッグの時しか生成されない。なのでFinderでモデルのファイルをいろいろいじっているとモデルとfadeMotionListがうまく噛み合わなくなって以下のエラーが頻繁に出る。
NullReferenceException: Object reference not set to an instance of an object
Live2D.Cubism.Framework.MotionFade.CubismFadeStateObserver.
このときは一旦モデルフォルダを削除して入れ直してfadeMotionListを再度正常に生成させないといけない。
Visual Studio2022コードスタイル設定

## バージョン
Visual Studioバージョン：VS2022

## 設定
### 1.タブとスペース表示
Tools → Option → Text Edit → Display.
![1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2445123/bb1f98a8-4262-9e5b-5ae0-4e06f75a91c6.jpeg)

### 2.タブをスペースに変換
Tools→ Option→Text Edit→ C++ → Tabs. 
![1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2445123/7ace4ffb-b280-7726-6964-197a1f0b8534.jpeg)

### 3.Indentation
Tools→ Option→Text Edit→ C++ → Code Styles → Formatting→ Indentation
![1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2445123/8943e684-8e4b-774a-c961-3caf8310e13b.jpeg)

### 4.Spacing
Tools→ Option→Text Edit→ C++ → Code Styles → Formatting→ Spancing
![1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2445123/a3621f0a-c46f-77a1-8393-79811129bba2.jpeg)
LPIC/Linux学習記録#4

〇自戒のための自己への宣誓
1. お前に足りないのはインプットだ！！！インプットしろ！！！インプットの奴隷になれ！！！
2. 記事の書き方とか今はどうでもいいから書け！！！インプットをアウトプットしろ！！！やる前からこだわりとか持つな！！！
3. 見られることとか評価されることとか気にするな！！！MECEとか構造化されてるかどうかとか気にするな！！！構造化はアウトプットの整地であって、インプットが足りないお前が手を出す領域ではない！！！ていうかお前頭良いんだからインプットに注力しろ！！歴史を刻め！！！歴史をまとめようとするな！！！未来を描け！！過去を清算するな若者め！！！！
4. 疑問を持つことを恥じるな！！！お前は頭はいいがインプットが足りない未熟者だ！！！インプットの奴隷になれ（再掲）！！！

〇学習記録
シェルとカーネル。
シェルは人間がカーネル(OS)と対話するためのインターフェース・窓口。
カーネルは存在。シェルはその存在にアクセスするための窓口。なんでこうなってるのかは知らない。多分色々と便利なんだろうな。NWでノード間を全部つながないように、人間が全ての人間と繋がっていないように、全てが直接つながっていないほうが色々と便利らしい。便利というかコストがかからないというか、その辺の絶妙な感じは神秘。
シェルは色々種類がある。
sh: bourne shell (simple and light shell. ancient)
bash: bourne again shell (versatile sh)
zsh: z shell(more versatile sh than bash)
ksh: korn shell (sh + bash)
csh: c shell (c-language-like shell)
tcsh: tenex c shell (versatile csh)
fish: friendly interactive shell (colorful and versatile shell)

ログインシェルってのが解決した。各ユーザーはログインシェルという情報を持ってる。これは、ログインしたらまずどのシェルを開くかというのを表してる。
rootなら基本的に/bin/bashぽい。

シェルを開くと出てくるのがプロンプト。入力ができるようになっているので、「入力を受けつけるプロンプト」とか言ったりするらしい。
[username]@[host name]:[current directory]$が基本らしい。

環境変数PS1にプロンプトの表記方法が書いてある。エスケープ文字とかいろいろあってわからない。LPIC101で復習できそうなので放置。
```
[node1] (local) root@10.5.6.2 ~
$ env | grep PS1
PS1=\[\e[1m\e[31m\]\h\[\e[32m\]($(docker-prompt))\[\e[34m\]\u$(hostname -i)\[\e[35m\] \w\[\e[0m\]$
```
シェル操作ショートカット：
色々あったけど、Ctrl + ←→くらいを覚えとけば一旦よさそう。

環境変数とシェル変数
環境変数を確認したいときはenvかprintenv。シェル変数を確認したいときはset。ただsetだと環境変数とシェル変数が全て出てくるので、| grep XXXなどで見たいものを抽出するのがよさそう。
環境変数とシェル変数っていうのは変数名で区別するのが基本だけど、実際はexportコマンドを使ってexportしたものが環境変数になるというのが正しい理解かも。

実験→
①変数を設定（シェル変数）：test1=hello
②環境変数に新たに値を代入（環境変数の値を変更=まだシェル変数. 元々はnode1）：HOSTNAME=testingNow
③envとset | grep test1とset | grep testingNowで①②の値を表示
④logoutして再度③を確認。

結果→
loguout後、①のtest1変数はなくなり、②のHOSTNAMEはtestingNowではなくnode1になった。やはり、環境変数は、デフォルトで設定されている変数だけでなく、ログインからログアウトまでの間で設定した変数・値をexportしたものも環境変数に含まれる。

と思ってexportも試してみてたら、logoutしたらexportしたやつも消えてしまった。exportで環境変数にするってなんやねん。もしかしたら今試してるのがあるIT学習サービスのもので、AWSのLinuxインスタンスを使っているものであるのが関係してたりする？わかんねー。

GPT先生曰く→
環境変数は１つのセッション中のすべての子プロセスで適用できる変数。シェル変数は新しいシェル、サブシェル、子プロセスでは有効にならないらしい。

うーんサブシェルとか子プロセスとかよくわからん。
とりあえずシェルってのは複数個作る（GUIならいけるらしい）こともできて、ログインログアウトは認証の話なのでシェルセッションとか微妙にずれるっぽい。例えばbashって打つと新しいシェルを開くことができるみたいで、そうすると環境変数に設定したものは新しいシェルの方で使えるけどシェル変数だと使えないっぽい。

    なんかよくわからんが、bashを打つとそのシェルの中でシェルを開くことができて、pstree -pを打つとプロセスがツリー上に表示される。そのツリーを見ると、bash(XXX)----bash(YYY)みたいに表示されていた。で、logoutしようとするとexitしかできねぇよみたいなことを言われた。
そういえばCiscoのスイッチとかルーターでログインするのにenableとかなんとか打って中に入るみたいなプロセスがあった。入れ子構造にするのはよくあるのかねぇ。よくわかんねぇ。
C++桁数設定

# C++桁数設定

std::fixedとstd::setprecision組み合わせで設定できる。

## std::setprecision

サンプル
```c++
std::ostringstream oss;
double num = 3.14159265359;
double num2 = 12.3456789;
oss << std::setprecision(5) << num; 
std::cout << oss.str() << std::endl;

std::cout << std::endl;

oss.str("");
oss << std::setprecision(5) << num2; 
std::cout << oss.str() << std::endl;
```
出力結果（４桁と３桁）
```
3.1416

12.346
```

## std::fixedとstd::setprecision組み合わせ

```
std::ostringstream oss;
double num = 3.14159265359;
double num2 = 12.3456789;
oss << std::fixed << std::setprecision(5) << num;
std::cout << oss.str() << std::endl;

std::cout << std::endl;

oss.str("");
oss << std::fixed << std::setprecision(5) << num2;
std::cout << oss.str() << std::endl;
```

出力結果（５桁）
```
3.14159

12.34568
```
JJUG CCC 2023 Fall で登壇した

## はじめに
JJUG CCC 2023 Fall に登壇してきました。
エンジニアとしてのキャリアを積むために、積極的にカンファレンスに参加していますが、登壇は初です。
修士を取ったので大人数の前で発表するのは特別なことではありませんでしたが、社会人で時間が無いなか準備をするのは大変でした......

JJUG CCC については公式をご参考に。

https://ccc2023fall.java-users.jp/

以下のブログに当日の登壇資料がまとめられています。私のものも掲載していただきました、ありがとうございます。

https://yujisoftware.hatenablog.com/entry/jjug_ccc_2023_fall

発表内容や準備から当日の流れを簡単にまとめておきます。

## 発表内容
データベースとリポジトリのテストができる Database Rider というライブラリを紹介しました。
アジャイルで開発しているためユニットテストとテスト自動化は必須です。
なので、リポジトリのテストをするのが当たり前だと思っていたのですが、登壇中の反応や、Twitter での反応を見ると様々な考え方があり興味深かったです。

当日使った資料と GitHub のリポジトリは以下です。

https://speakerdeck.com/contour_gara/database-rider-woyong-itatetahesutesuto

https://github.com/contour-gara/database-rider-demo

## 登壇前
### Proposal 提出
スクラムマスターの勧めで勢いで参加を決めました。
研究をしていた時も、まず申し込みしてからどうするか考えていたので、何も変わっていませんね。
テーマは、使っていたライブラリの日本語の記事があまりなかったので Database Rider にしようと決めました。

### 準備
**全く進みません。**
懇親会の LT でもありましたが、資料を作ろうと思うと何故か部屋が綺麗になったり、調査だけがはかどったりするものです。(誰かこの普遍的問題を解決して欲しい。)
わかったことは、毎日数時間取り組んでも資料は完成しないということです。
日々の隙間時間に調査をし、仕上げにどこかのタイミングで山にでも籠もって資料作成に取り組むべきですね。
文豪が旅館で執筆する気持ちがわかりました。

## 当日
### 登壇前
受付で QR コードがなくオドオドしているところを、鈴木雄介さんに助けていただきました。
![20231111-100205-L1070651-LEICA Q.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2600771/15267e64-2ef3-45ec-582c-08ec40513250.jpeg)

資料が直前にできたこともあり、発表のシミュレーションをしていてあまり集中して話を聞けませんでした......
一番の心残りです。

登壇は午後からだったので、天丼を食いしっかりと英気を養いました。
![20231111-121704-L1070652-LEICA Q.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2600771/fe09e2c4-1511-505b-6b94-db307089f504.jpeg)

自分の発表の前から部屋に入っておこうと思ったのですが、満員で入れず。
ナイトセミナーで知り合った方がいたので、一緒にアンカンファレンスにいました。
こんな感じでゆるりと話が聞けるのも面白いですね。
![20231111-162633-L1070654-LEICA Q.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2600771/b37054d1-66f8-9c83-2198-00e0b1360564.jpeg)

### 登壇
部屋が狭いこともあり、満員でした。
20 人くらいかなと思っていたので拍子抜けしました......
![20231111_164602.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2600771/b9bd44b5-745c-7892-d78b-fe7b6491fd50.jpeg)

聞いてくださった皆さん、ありがとうございました。

### 懇親会
懇親会では、はじめて話しかけていただく経験をしました。
Database Rider を使われている方との情報交換もでき、大変有意義なカンファレンスになったと思います。

そして、寿司とビールは最高。
LINE ヤフーさん、星野リゾートさん、ありがとうございます。
![20231111-180808-L1070656-LEICA Q.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2600771/1f54121d-858b-e8b2-4ac4-1f0efbccb7e1.jpeg)

LT もありました。
有志の LT もあり面白かったです。
![20231111-182724-L1070671-LEICA Q.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2600771/2469f03c-f141-7458-1635-e24123fa1928.jpeg)

## まとめ
また登壇したいなと思いますが、登壇はなかなか体力を使いますね...... 次回は反省を活かし、どこかにしばらく籠もり、スライドを仕上げたいと思います。