OmniAICreator/Qiita-1.07M huggingface dataset:
【Google Chrome】普段使っている拡張機能

# はじめに
筆者は普段フロントエンド開発を担当しています。
今回はGoogle Chromeのおすすめ拡張機能を紹介しょうと思います！

# Wappalyzer
https://chromewebstore.google.com/detail/wappalyzer-technology-pro/gppongmhjkpfnbhagpmjfkannfbllamg?hl=ja

開いているページでどういった技術が使われいるかわかる
↓はQiitaの例
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2523103/611a6f64-8d9a-aa48-23a0-a422e16c2061.png)


# GoFullPage
https://chromewebstore.google.com/detail/gofullpage-full-page-scre/fdpohaocaechififmbbbbbknoalclacl?hl=ja

画面全体のキャプチャを取得できる
エビデンスや画面仕様書を作成する際に重宝する

# Image Downloader
https://chromewebstore.google.com/detail/image-downloader/cnpniohnfphhjihaiiggeabnkjhpaldj?hl=ja&

サイト内の画像を一括DLできる
写経コーディングする際に便利

# What Font
https://chromewebstore.google.com/detail/whatfont/jabopobgcpjmedljpbcaablpmlmfcogm?hl=ja

サイト内の文字をホバーするとフォントを表示してくれる

# Responsive Viewer
https://chromewebstore.google.com/detail/responsive-viewer/inmopeiepgfljkpkidclfgbgbmfcennb?hl=ja

色々な画面サイズを一括で表示してくれる
SPのデザインなどを確認するのに便利


# Talend API Tester
https://chromewebstore.google.com/detail/talend-api-tester-free-ed/aejoelaoggembcahagimdiliamlcdmfm?hl=ja

APIのテストができる

# Google翻訳
https://chromewebstore.google.com/detail/google-%E7%BF%BB%E8%A8%B3/aapbdbdomjkkjkaonfhkkikfgjllcleb?hl=ja

さくっと英単語の意味を調べるのに便利

# CSS Peeper
https://chromewebstore.google.com/detail/css-peeper/mbnbehikldjhnfehhnaidhjhoofhpehk?hl=ja

サイト内のCSS要素を調べられる
フォントサイズや背景色がすぐわかって良い


# おわりに
ご覧いただきありがとうございました！
他にもおすすめの機能があれば教えていただきたいです！
子コンポーネントにpropsで渡した文字列をウィンドウサイズによって改行させたりさせなかったりしたい

# 課題
* propsの文字列に`<br>`を含ませ、v-htmlで表示すれば解決するのは分かっていたが、XSSの危険性があるので使いたくなかった
* propsの文字列に改行コード`\n`を含ませ、`white-space: pre-wrap`で改行させる方法も考えたが、半角スペースが入ってしてしまいデザインが崩れて困った

# 解決方法
window.innerWidthが768px以下になったとき、改行コードをreplaceする処理を子コンポーネントに入れた。

```vuejs:ChildComponent.vue
<template>
  <div class="text">{{ displayText }}</div>
</template>

<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount, watch } from 'vue'

type Props = {
  text: string
}
const props = withDefaults(defineProps<Props>(), {
  text: ''
})
const displayText = ref('')

const handleResize = async () => {
  if (window.innerWidth >= 768) {
    displayText.value = props.text.replace('\n', '')
  } else {
    displayText.value = props.text
  }
}

onMounted(async () => {
  handleResize()
  window.addEventListener('resize', handleResize)
})

onBeforeUnmount(() => {
  window.removeEventListener('resize', handleResize)
})

watch(
  () => props.text,
  () => {
    handleResize()
  }
)
</script>

<style scoped lang="scss">
.text {
  white-space: pre-wrap;
}
</style>

```

# 所感
改行させたいだけなのになんだか複雑になってしまった😥
けっこう頻発しそうな問題な気がしますが、みなさまどうやって解決しているのでしょう。
なにかもっと良い方法がありましたら教えてほしいです。
ZIOのScopeの有効期間を制御する

# 概要

[前回の記事](https://qiita.com/tobita_yoshiki/items/04c028d87903262cd528)では、
ZIOのScopeについて簡単に共有しました。

今回の記事では、より実績的なScopeの有効期間の制御例について共有させていただきます。

# Scopeの動的な有効期間について

`Scope`の有効期間は動的です。
`Scope`を必要とするZIOエフェクトがある場合、それを`flatMap`して新しいエフェクトを作成できます。

新しいZIOエフェクトは元の`Scope`の有効期間を延長します。
したがって、Scopeを閉じない限り、リソースは解放されず、`Scope`を閉じるまでリソースはどんどん大きくなります。

## Scopeが延長される例

以下のサンプルプログラムは、使い終わったら削除されるような一時ファイル`tmpFile`を`Scope`によって取り扱ったものです。
`checkFileExists`でファイルが存在するかどうか(`Scope`が閉じられたかどうか)が確認できるようになっています。

```scala
import zio.*
import java.io.File

object Main extends ZIOAppDefault:
  val PathToFile = "/tmp/tmp.txt"

  def tmpFile: ZIO[Scope, Throwable, Unit] = for {
    file <- ZIO.acquireRelease(
      ZIO.attemptBlocking {
        val file = new File(PathToFile)
        file.createNewFile()
        file
      }
    )(deleteFile)
  } yield ()

  def deleteFile(file: File): UIO[Unit] =
    ZIO.attemptBlocking(file.delete()).orDie.unit

  def checkFileExists: Task[Unit] = for {
    exists <- ZIO.attemptBlocking(new File(PathToFile).exists())
    _ <- Console.printLine(exists)
  } yield ()

  override def run: ZIO[ZIOAppArgs & Scope, Any, Any] =
    for {
      _ <- ZIO.debug("開始")
      _ <- tmpFile
      _ <- checkFileExists
      _ <- ZIO.debug("終了")
      _ <- checkFileExists
    } yield ()
```

```txt:実行結果
開始
true
終了
true
```

```shell:プログラム終了後にファイルの存在を確認した結果
$ pwd
/tmp
$ ls | grep 'tmp.txt'
```

`実行結果`および`ls`コマンドの結果より、
一時ファイルは作られてからプログラム終了時まで存在し続けていたことが示唆されます。

# Scope#scopedでScopeを閉じる

次にサンプルプログラムを改変して、以下のように`tmpFile`を`ZIO.scoped`で包むようにしてみます。
これにより`tmpFile`の`Scope`が延長されないようになります。

```scala
  override def run: ZIO[ZIOAppArgs & Scope, Any, Any] =
    for {
      _ <- ZIO.debug("開始")
      _ <- ZIO.scoped(tmpFile)
      _ <- checkFileExists
      _ <- ZIO.debug("終了")
      _ <- checkFileExists
    } yield ()
```

```txt:実行結果
開始
false
終了
false
```

`実行結果`より`ZIO.scoped(tmpFile)`以降の処理ではファイルが存在しない(`Scope`が閉じられた)ことが確認できます。
今日から始める

# はじめまして
8月から未経験社内SEになる予定のシングルマザーです。
不安だらけだし、正直前の会社を辞めたことを凄く後悔しているけれど、もう戻れない。
この暗い気持ちがのちのち笑い話になればいいなと思いつつ、今は勉強するのみです。

## 新しい仕事の内容
インフラ系社内SE
当面はセキュリティ分野を担当する

セキュリティというと、webで調べたらインフラ系や開発系を経験してからじゃないと厳しい世界らしい。

おそらく、がっつりIT系のお仕事をやるというよりも、情報セキュリティ事務（PマークとかISMS）対応がメインになるんだと思う。

でも、たくさん資格とって上を目指したい。
応援お願いします🥲
【決定版！】IPsecの構造について理解し分かったつもりを解消しよう


## １．はじめに
IPsecについては、資格試験や仕事の中で使っているので分かったつもりではいたが、いざ構造を説明しろとなると、**AH、IKEなんだっけ？**　とかになるので、記事にしっかりアウトプットして理解を深めようと思う。また、そんな方への助けにも慣れたらと思う❗



## ２．IPsecとは
**AH、ESP、IKE**などのプロトコルで構成され、IPパケットの完全性と機密性を保証します。
:::note
まず、IPsecとはプロトコルではない。上記のプロトコルを使用してIP（インターネットプロトコル）の**完全性と機密性**を保護するフレームワーク
:::


## ３．IPsecを構成するプロトコル
| プロトコル | 役割 |
| ---- | ---- |
| **AH**（Authentication Header） | データの完全性と認証を提供（HMAC）しますが、機密性（データの暗号化）は提供しない。|
| **ESP**（Encapsulating Security Payload）| データの機密性（暗号化）、完全性、および認証を提供（HMAC）します。（パケットのペイロード部を暗号化） |
| **IKE**（nternet Key Exchange）| IPsecにおける安全なキー交換メカニズム |
:::note
**AH**は暗号化しないが、**ESP**はデータを暗号化する。認証についてはHMAC（Hash-based Message Authentication Code）というメッセージ認証コードを生成する仕組みで行う。
:::



## ４．IPsec通信の流れ
#### ◯IKEのフェーズ１、２について
- フェーズ１
  ISAKMP　SA（アイサカンプエスエー）を確立する。ISAKMP SAは接続相手を認証し、IPsecSAごとの共通秘密鍵を安全に共有するための※SAです。ISAKMPはDiffie-Hellman方式を使って通信の暗号化されていない環境で秘密鍵を安全に共有するための**鍵交換方式**
※SecurityAssociationの略で、両者をつなぐコネクションのようなもの
参考：認証に使われるID情報の暗号化有無でモードが異なる。
暗号化あり：メインモード
暗号化なし：アグレッシブモード
- フェーズ２
  IPsecSAを確立するための情報を交換する。フェーズ１で確立された経路を使用するためフェーズ２のやり取りは暗号化され、以下の情報やり取りする。
  
| やり取りする情報 | 内容 |
| ---- | ---- |
| 暗号アルゴリズム | IPsecSAでやり取りするデータを暗号化するためのアルゴリズム |
| ハッシュアルゴリズム | IPsecSAでやり取りするデータのための認証と鍵計算に使われるハッシュアルゴリズム |
| IPsecの寿命 | 左のとおり |
| DHグループ | 鍵計算のためのDH（DeffieHellman）方式のパラメータ |
| ID | ESPまたはAH通信を識別するためのID情報 |
| ※PFS | IPsecSAで使用する共通秘密鍵をより安全に生成するための情報 |

※Perfect Forward Secrecy（完全前方秘匿性）の略で、暗号通信のセキュリティ概念の一つです。PFSを実装することにより、セッションキー（通信を暗号化するために使われる一時的なキー）が漏洩した場合でも、過去の通信内容が暗号解読されるリスクを最小限に抑えることができます。
:::note
IKEのフェーズ１、２は実際に通信する際に使用されるIPsecSA確立前のフェーズ
:::

#### ◯トンネルモードとトランスポートモード
- トンネルモード
  **元のIPパケット全体を新たなIPヘッダでカプセリング**する方式で、新たに追加したIPヘッダは単に転送用として使用される。データの暗号化と認証の範囲はAHとESPでことなる。
- トランスポートモード
  **元のIPパケットを使って転送**する。
暗号化の範囲については以下の記事が図解されており分かりやすかったのでリンクを記載
[ネットワークエンジニアとして　IPsec-トランスポートモード・トンネルモード](https://www.infraexpert.com/study/ipsec6.html)

:::note alert
NAT（IPマスカレード）との共存には注意が必要！AHだとIPアドレスが変換されることで認証できなかったり、ESPの場合はトランスポート層が暗号化されているので、ポート番号変換ができない。以下早見表

| プロトコル | NAT | IPマスカレード |
| ---- | ---- | ---- |
| AH | 不可 | 不可 |
| ESP | 可 | 不可 |

*NAT・IPマスカレードとIPsecの対応表*
:::
【Tips】PostgreSQLでテンポラリーテーブルをコミット時にDropする。

## はじめに
表題通り、テンポラリーテーブルをコミット時にテンポラリーテーブルをDropする。
データコンバート実施時に、テンポラリーテーブルをよく作ります。テンポラリーテーブルを作った後、最後にDROPする必要があるのですが、本番環境でDROPコマンドを打ちたくなかったです。
そこで、コミット時に一時テーブルをDROPするコマンドを記載します。

## SQL

```sql:
BEGIN TRANSACTION;
 
CREATE TEMP TABLE tmp_table ON COMMIT DROP AS 
(
    id SERIAL PRIMARY KEY,
    name TEXT NOT NULL
);
COMMIT;
```

`ON COMMIT DROP`を追加するだけです。これでCOMMIT実行後に一時テーブルはDROPされます。
毎日投稿を10日間続けた結果、アウトプット以外にも変化が出始めた

## 結論
毎日投稿を10日間続けた結果、「**転用思考**」がより強くなりました。
そして習慣化しました。

## きっかけ

私はQiita Enginieer Festa2024に参加しています。

https://qiita.com/official-campaigns/engineer-festa/2024

投稿マラソンで毎日投稿しており、この記事で10日目になります。

10日間連続で技術記事を書いたのは人生で初めてです。

10日連続で毎日投稿を続けた結果、技術記事を書くこと以外にも変化があったので、その変化を共有したいと思います。

## 「次に活かせないか？」

技術記事は、学んだことや経験したことをアウトプットできます。

技術記事を書き続けることで、学びや経験をどうアウトプットするかを考える習慣ができました。

同時に、技術記事に限らず「次にどう活かすか？」を考えるクセがつきました。

例えば、アプリを開発したとき、この経験をどう技術記事でアウトプットするか？を考えるのはもちろんですが、同時に「他のアプリ開発でも使えないか？」「新人教育に活かせないか？」なども考えられるようになりました。

一つの経験が、他の領域でも価値を生むようになりました。
シナジーが高くなるような転用思考を自然にできるようになりました。

結果として、成果が大きくなったように思います。

## おわりに

この記事では、毎日投稿を10日間続けた変化について共有しました。

投稿マラソンのゴールを目指して続けていきたいと思います。

また、投稿マラソン期間に、QiitaのContributions 2000を達成できました。
いつも応援いただいている皆さん、ありがとうございました。

![スクリーンショット 2024-06-19 22.30.06.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/616496/d4c82abe-df90-2b71-5fc5-6032b361a8bb.jpeg)
Linuxでディスク利用率が100%になった場合の対処法

今回はLinuxでディスク利用率が100%になった場合の対処法についてご紹介致します。

あくまでも個人の意見となりますのでご参考程度とご理解いただけますと幸いです。

## 現在の使用率を確認する

初めに現在のディスク使用率を確認します。

下記コマンドを使用し確認する事が出来ます。

```
df -h
```

下記はディスク容量が20Gとなっており使用率が100%になっている状態の例となります。
```
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda1        20G   20G     0 100% /
```

## 急ぎ容量を確保する

サーバーではディスク使用率が100%になるとサービスが正常に提供できなくなる可能性がございます。

また最悪の場合、OS等がハングアップし操作不能となる場合もございます。

その為、根本解決よりも先に迅速に容量を確保する必要がございます。

### ログの削除

容量を確保するおすすめの手段の1つにログの削除があります。

基本的にサービスとして公開されているサーバーはログ等も含め、複数世代のバックアップがとられている場合が多いと思います。

その為、ログ等は本番環境で削除を実施しても、後ほど切り戻し対応できる場合が多くございます。

ただし環境や構成によっては切り戻し対応できない場合もございます。

実行される際は構成を把握した上、自己責任で実施をよろしくお願い致します。

下記「/var/log」配下のログを期間指定で削除するコマンドとなります。

```
#削除前の対象確認
find /var/log -mtime +30 -type f

#削除前の対象確認
find /var/log -mtime +30 -type f | xargs rm
```

上記は直近30日以前のログを削除するコマンドとなります。

ログの保存期間が180日や365日となっていた場合、ある程度の容量を確保できるかと思います。

ログの削除前には内容を確認し、重要な情報が含まれていないか注意してください。

## 原因の確認

次にディスク使用率逼迫の原因を確認します。

様々な特定方法があるかと思いますが私はトップ階層か順にディスク容量を確認し逼迫原因となっているファイル等を特定しています。

下記コマンドで「/」配下に移動します。

```
cd /
```

次に下記コマンドを使用し逼迫原因となっているファイルの調査を行います。
```
du -sh ./*
```

上記の結果を踏まえ今後の対応を検討を行います。
【DAY2】Laravelについて

# Laravelとは
Laravelとは、PHPのフレームワークの一つです。
LaravelのプロジェクトはMVCモデルに基づいて構成されています。
## フレームワークとは
フレームワークとは、WEBアプリケーションやシステムを開発するために必要な機能があらかじめ用意された枠組みです。
通常フレームワークには、セキュリティを強化したり、データベースアクセスを効率よくプログラミングしたりするのに便利な、多種多様な機能が備わっています。
## フレームワークの利点
フレームワークを活用すると、フレームワークの思想に沿った共通な形式でのコーディングが可能になるため、エンジニアによる記述の際を減少させられ、ソースコードの可読性と保守性を向上させることができます。
## フレームワークの欠点
フレームワークには使用法の決まりがあるため、自由にコードが書けず開発に制限が生じます。フレームワークイン決まりやスタイルを押し付けられ、いい設計を適用したくてもできなくなってしまう問題です。
## フレームワークとライブラリの違い
フレームワークは、**アプリケーションの「枠組み」を提供するもの**です。
一方ライブラリは、**よく使う機能をパッケージ化した部品**であり、構築したアプリケーションに読み込んで利用するものです。
git submoduleを知った夏

最近仕事でgitのサブモジュール機能について触れる機会あって「なにそれ」となったので備忘録。
ちな初投稿なのとまだまだ未熟なのでツッコミ歓迎対戦オナシャス。

# サブモジュールとは

ひとつのリポジトリに、別のリポジトリをネストする機能。
独立したプロジェクトを一つの親リポジトリで管理可能。

らしい。
なんか便利そう。

# コマンド

```
git submodule add <repository> [命名したい名前]
```
repositoryはプロジェクトのURLを指定。ネーミングも可能。デフォルトはプロジェクト名

```
git submodule add https://github.com/DynamicDaikon/vue-pinia.git my-sub
```
コマンド実行したところにmy-subという名前でcloneされる。
サブモジュール化した後は初期化とアップデートを実施
（ガチチュートリアルなリポジトリのURL貼るの手震える）

```
git submodule init
git submodule update
```
モジュールの初期化＆最新コミットをチェックアウト
イマイチよくわからんけどおまじないと思ってまずは受け入れ
.gitmoduleみたいなファイル出来てたし多分ここに色々書き込まれるんだろう

```
git submodule update —remote
```
リポジトリの変更を取り込み
親側で更新入れてたらスススっと更新取り込んできた

こういう使い方かぁ

# ところで
サブモジュールで行った変更はどうなるのか

cloneってことは普通にブランチ操作とかcommitとか可能なんだよね？　どうなるの？
と思ったのでちょっと試そう

- ギモン
    - リポジトリ元に反映される？
    - 独立した修正になる？

## やってみた

- submoduleを追加した子リポジトリで修正
    - git commitまでは変わらず
    - git push時に以下のワーニング発生
    
    ```bash
    Dynamic_Daikon:~/git/projects/vueText/components-basics/vue-pinia
    % git push                                                                                                                                                                                                                               HEAD
    fatal: You are not currently on a branch.
    To push the history leading to the current (detached HEAD)
    state now, use
    
        git push origin HEAD:<name-of-remote-branch>
    ```
    
    - どうやら他のブランチのコミットを直接チェックアウトしている時と同じ挙動になる様子
    - なのでログの通りにoriginのHEADに直接pushすれば解消
    - 無事、大元のサブモジュールにしたプロジェクトにコミットが追加された
    

正確には本体側の参照するコミットIDがsubmoduleでコミットされたものに切り替わってるっぽい。具体的な内容は追えてないけどこれ起因の不具合とか起きそうで怖い……。（臆病なgit弱者）

サブモジュールを含むプロジェクトを更にサブモジュール化してもおそらく同様の挙動なんだろう。知らんけど。

## 色々見てると

子プロジェクトでcommitすればpush前でも親プロジェクトから参照が可能っぽい。
なのでやろうと思えば「子プロジェクトでまだpushするつもりなかったのにー」というものも親プロジェクト側でpushできちゃうみたい。

いや怖くね？

しかも子側でブランチ切ると場合によってはデグレのリスクもついてくると。
```
pre-commit
```
のコマンドで対応可能だけど、そんなデリケートな操作したくないなぁ、という気持ち。


https://qiita.com/ex_SOUL/items/be1a7299be7b09d90a50

## ということで結果

> リポジトリ元に反映される？
> 
→YES。される。

### 【メリット】

- プロジェクトを独立して管理、あちこちの別プロジェクトでサブモジュール化することで共通部品的な使い方が可能。
- 親子で並行開発可能で、最新状態の取り込みもコマンド一発でOK
- 双方向で修正は可能だが、もしミスってgit pushを無邪気にやってもoriginのHEADを指定しないと送信されないので一応安心？（設定によるかもだけど）

### 【デメリット】

- プロジェクト同士の依存関係の管理が煩雑になる
    - 大規模なプロジェクトだとこの辺の整理だけで工数無限に溶けそう
- gitに関する理解度がある程度必要
    - 事故った時キツそう
- コミットやブランチ戦略のルールをしっかり決めないとデグレのリスクあり
    - 基本は子の方で修正しない方が吉っぽい



以上
paddleocrでpythonがクラッシュする


## 事象
下記の環境でPaddleを使おうとすると、Pythonがクラッシュしてしまった。

- M2 macbook
- OS Sonoma 14.4
- Poetry仮想環境
- Python 3.12.4
- paddleocr 2.7.3 
- paddlepaddle 2.6.1

```shell
$ poetry run python test.py
# Pythonがクラッシュ
[1]    65704 illegal hardware instruction  poetry run python test.py
 ```

## 解消法

色々調べてみても同じ事象が出てこなかったので、Python3.11.4で試してみたら、クラッシュしなくなった。
依存関係のエラーが出た(下記)ので、numpy<2を入れることで動くようになった!
```shell
$ poetry run paddleocr

A module that was compiled using NumPy 1.x cannot be run in
NumPy 2.0.0 as it may crash. To support both 1.x and 2.x
versions of NumPy, modules must be compiled with NumPy 2.0.
Some module may need to rebuild instead e.g. with 'pybind11>=2.12'.

If you are a user of the module, the easiest solution will be to
downgrade to 'numpy<2' or try to upgrade the affected module.
We expect that some modules will need time to support NumPy 2.

$ poetry add "numpy<2"
```
Salesforceでテキスト型の項目値を通貨型に変更する方法

Salesforceでは項目により項目型というものがあります

今回はName項目のようなテキスト型項目を通貨型に変更する数式項目を作成します


### 基本数式
基本的な数式は以下となります

VALUE(Name)

![数式01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531631/124393db-ab52-90f5-06ba-77b23bcd4585.png)

これにより「30000」などのテキスト項目値を通貨「JPY3000」に変更することが可能です
![成功0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531631/6542336f-61e6-2c7a-2e67-9a1bc53a100e.png)


しかしこの数式では「30,000」など「,」がある場合通貨項目は表示されません
![表示されない.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531631/e97eb81e-5cbd-2479-12fa-cb14824565f0.png)

###  応用数式

もしも「30,000」など「,」がある場合通貨項目を作成したい場合、数式は以下となります
VALUE(SUBSTITUTE(Name,",",""))

このような数式を作成すれば「30,000」など「,」がある場合通貨項目「JPY3000」に変更することが可能です
![成功01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531631/90aeaf37-d522-da26-0cb8-cc983170cd72.png)
Salesforce MVP　Eric氏の数式項目FAQを和訳してみた

Salesforce MVPのEric氏がSalesforce　Helpで数式項目のFAQを公開しました。
今回はこちらのHelpについて和訳します

#### 元のHELPはこちら
Formula FAQ
https://help.salesforce.com/s/articleView?id=000366420&type=1


## Q1 数式項目を作成し、数式で選択リスト値を参照していますが、なぜか反映されません。なぜでしょうか？

### A1 数式項目で選択リスト値のAPI 名を参照していることを確認してください。表示ラベルとAPI参照名は異なる場合があります。
たとえば、ラベルが「Other」で、API参照名が「XOther」に設定された値を持つことができます。 簡単な IF 式は次のようになります。
IF(ISPICKVAL(Picklist__c,'XOther', true の場合の結果、false の場合の結果)
![Picklist.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531631/eb18fa74-d3db-01e0-ccb7-eefc48b7c1f4.png)


## Q2 特定のプロファイル/ロール/ユーザーに対して入力規則を有効にしたくありません

### A2 $ 記号を使用すれば、ログインしているユーザーを対象に入力規則の設定が可能です。


これで、例えば次のことができます。
プロファイル: $Profile.Name<>'システム管理者'
ロール: $UserRole.DeveloperName<>'CEO'
ユーザー: $User.Alias<>'田中'

なお、特定のユーザーに対して入力規則を有効にしないようにするもう 1 つの方法は、
1.最初にカスタム権限を作成 (「ValidationBypass」)、
2.次に権限セットを作成して、この権限セットをユーザにそれに割り当てることです。

選択したユーザーに権限セットを割り当てることができるようになりました。 
あとは、これを権限セットに追加するだけです。

AND(NOT($Permission.ValidationBypass), [ここに数式を入れます])


設定の詳細はこちらをご確認ください

#### Salesforceで特定の複数ユーザのみ入力規則を適用させない方法
https://qiita.com/Takaa/items/931a0fd13ea1effcc83c

## Q3 $Profile.Name (または $UserRole.Name) を参照して、入力規則を除外していますが、フランス語ユーザなど英語を話さない同僚には入力規則が除外されないようです

### A3:　Salesforceが他の言語設定されているため、入力規則がうまく動いてないと思います。例えば、英語の「System Administrator」はフランス語では「Administrateur système」になりますので、$Profile.Name<>「System Administrator」を使った入力規則はフランス語の言語設定はうまくいきません

入力規則にロールを使用する場合DeveloperName（$UserRole.DeveloperName）の使用をお勧めします

残念ながらプロファイルはProfile.Name　しか使えないです。
この場合、$Profile.IDを使用して式にIDを直接入力することもできますが、別の環境の際の挙動確認のことも考えると、直接ID入力はふさわしくないと思います。

よりよい解決策は、プロファイルに割り当てるカスタム権限を作成し、入力規則でそのカスタム権限を除外することでしょう。


## Q4 2つの日付の差を計算し、週末を除外したい。

### A4 以下の数式をテンプレートとして使用することができます。



/*2つの日付の間の日数を計算する*/。
(EndDate__c - StartDate__c)-
(FLOOR((EndDate__c - StartDate__c)/7)*2
+
IF(AND(WEEKDAY(StartDate__c )=1, WEEKDAY(EndDate__c )<>7)),1,
IF(CASE(WEEKDAY(StartDate__c ),1,8,WEEKDAY(StartDate__c ))>CASE(WEEKDAY(EndDate__c ),1,8,WEEKDAY(EndDate__c )))，2,
IF(OR (WEEKDAY(EndDate__c )=7, WEEKDAY(StartDate__c )=1),1,
IF(OR (WEEKDAY(EndDate__c)=1、WEEKDAY(StartDate__c )=7),2,
0)))))
Windows server2012にOpenSSHを導入

# はじめに
仕事の都合上、windows server2012にLinuxマシンから``scp``コマンドを用いてファイル転送を行う必要がありました。そもそもOpenSSHがインストールされていなかったので手順をまとめときます。

# OpenSSHのインストール
1. 
https://github.com/PowerShell/Win32-OpenSSH/releases
上記リンクから、必要なファイルをダウンロードしてください。(`OpenSSH-Win64.zip`)
2. 
ダウンロードしたら、ローカルマシンの任意の場所に展開してください。ここでは、`C:¥Program Files¥OpenSSH-Win64`に配置します。
3. 
環境変数からパスを通してください。
4.
2.で配置したディレクトリに移動して管理者権限でPowerShellを開いてください。そして以下コマンドを実行してください。
```
.\install-sshd.ps1
```
以上です。

# 参考文献
https://www.server-world.info/query?os=Windows_Server_2012&p=openssh
JavascriptでUUID4を取得する

```javascript
function uuid4(){
	return [...new Array(36)].map((_,i) => {
		switch(i){
			case 8:
			case 13:
			case 18:
			case 23:
				return '-';
			case 14:
				return '4';
			case 19:
				return parseInt(Math.random() * 4 + 8, 10).toString(16);
			default:
				return parseInt(Math.random() * 16).toString(16);
		}
	}).join("");
}
```
自分用メモ。

```javascript
"RRRRRRRR-RRRR-4RRR-rRRR-RRRRRRRRRRRR".replaceAll(/R/g,()=>parseInt(Math.random()*16).toString(16)).replaceAll(/r/g,()=>parseInt(Math.random()*4+8).toString(16))
```
単行実装ならこっち。
Linuxでファイルサイズを確認する方法

# はじめに
Linuxでファイルサイズを知りたいときに見てください。

# コマンド例
容量を知りたいファイルが存在するディレクトリに移動して以下コマンドを実行してください。

```
ls -lh -s
```


参考文献
* [ファイルサイズの確認](https://docs.oracle.com/cd/E19253-01/819-0379/6n2qekgco/index.html)
【Drupal】「日付と時刻」の入力UIで秒を隠したい

Drupal標準の「日付と時刻」って秒の入力が必須ですよね。

![スクリーンショット 2024-06-19 20.43.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3334980/b1314ed9-739d-da87-f851-c95e460e8872.png)

なにかと「秒入力邪魔だな」ってシチュエーション結構あると思います。

便利なモジュールがあるので紹介します。

## DateTime hide Seconds

> This module adds a "Hide seconds" setting to datetime field widgets.

datetime フィールドウィジェットに `Hide seconds` 設定を追加してくれるようです。

https://www.drupal.org/project/datetimehideseconds

モジュール導入後、DateFieldのフォームの設定画面を開くと、「Hide seconds チェックボックス」が用意されています。

![スクリーンショット 2024-06-19 20.43.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3334980/d827172f-1990-ee3f-5a0e-19f798aa962e.png)

チェックを付けて保存すると、コンテンツの登録画面で秒入力欄が外れるようになります。（便利 🎉）

![スクリーンショット 2024-06-19 20.43.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3334980/bd0e3955-f5a3-6d3b-976b-bfe957082745.png)

## ちなみに...。
Databaseへの登録のされ方に変更はないようです。
「00秒」でデータ登録されるんですね。

![スクリーンショット 2024-06-19 20.49.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3334980/227e033b-238c-288c-06ab-a9d1fd5c3061.png)

## もう１つちなみに...。
TimeStamp型には対応していないようです。
【Python】数値の範囲を変更する

お疲れ様です。秋並です。

入力された数値の範囲を変更する関数（[arduinoでいうmap関数](https://novicengineering.com/%E3%80%90arduino%E3%80%91map%E9%96%A2%E6%95%B0/)）をPythonでは以下のように実装できます。
<br>

```python
    def range_transform(value, before_min, before_max, after_min, after_max):
        return after_min + (after_max - after_min) * ((value - before_min) / (before_max - before_min))
```
各引数の意味は以下になります。
- `value`：変換する値
- `before_min`：範囲変換前の最小値
- `before_max`：範囲変換前の最大値
- `after_min`：範囲変換後の最小値
- `after_max`：範囲変換後の最大値

<br>

返り値は以下になります。
- 範囲変換後の `value`

<br>

使用例：
```python
# 変換前の値
before_value = 0.2

# 変換前の範囲
before_min = 0.1
before_max = 1

# 変換後の範囲
after_min = 1
after_max = 10

after_value = range_transform(before_value, before_min, before_max, after_min, after_max)
print(after_value) # 2
```
Ubuntuの更新方法【チートシート】

## はじめに

:::note warn
こちらは自分用にまとめたものとなっております。
:::

## コピペ用コマンド

```bash:依存関係の大きな変更が必要ない場合のコマンド
sudo apt update
sudo apt upgrade
```

```bash:依存関係も含めて包括的なアップグレードを行いたい場合のコマンド
sudo apt update
sudo apt dist-upgrade
```

## コマンドの簡単な説明

### apt update

Ubuntuのシステムのパッケージ情報を更新するためのものです。システムのパッケージ情報を最新にしますが**アップグレードは行いません**。

### apt upgrade

Ubuntuのシステムに既にインストールされているパッケージをアップグレードします。基本的には`apt update`を行った後に実行してください。

:::note warn
現在のパッケージに影響を与えない範囲でのアップグレードとなります
:::

### apt dist-upgrade

Ubuntuのシステムのパッケージを更新するだけでなく、パッケージの依存関係に変化があった場合でも適切に対処し、必要に応じて新しいパッケージのインストールや既存のパッケージの削除を行います。

### apt upgrade と apt dist-upgrade の簡単な違い

apt upgrade : 依存関係が変わる場合は**保留**にする
apt dist-upgrade : 依存関係が変わる場合も**適切に対応**する

## おわりに

自分の頭の整理のために記事を書きました。理解しやすいように書いたのでもし極端に間違えていることがありましたらコメントで教えてください。


## 参考記事

https://qiita.com/010Ri/items/ab1b8034c51fe032a5ad

https://qiita.com/masoo/items/8ebc51a6a9f32417d4a3
アプリケーション開発に注力するために工夫していること

# ■はじめに
Vue.jsを学習中のエンジニアです👩🏻‍💻
今回は、アプリケーション開発に注力するために時間管理をどう意識しているかについて**2つ**書こうと思います！
集中力が続かない方や効率的にタスクをこなしたい方に読んでいただけると嬉しいです🙌🏻

# ■１．１日のTodoを決めてからタスクに取り掛かる
Notionにタイムボックスのフォーマットを作成し、30分間隔で予定を簡潔に書いていきます。そうすることで何時に何をすればいいのか一目で理解できます。
もしNotionで作成するのが面倒な場合は、カレンダーに予定を書くでもいいと思います🙆🏻‍♀️

:::note info
タイムボックスとは、時間管理や作業効率を向上させるための手法の１つです。
:::

![スクリーンショット 2024-06-19 21.37.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3748502/fe9dfdb2-3936-b561-d146-fa72497c52d3.png)


### ポイント
完璧に作成する必要はないので書き出してみるということが大事だと思います！

### 取り入れてからの気づき
- 書き出すことで頭の整理ができるようになる
- タスクの優先順位をつけることができるようになる
- 時間がかかってしまったタスクや意外に短時間でできたなというタスクに気づくことができる
- 効率化のための改善点が見つかることもある
- 過去の振り返りが容易になる

# ■２．ポモドーロを使用して仕事の生産性を上げる
ポモドーロとは、25 分間の作業セッションに 5 分間の休憩をはさんだ時間管理戦略のことです。
私は次の方法で実践しています。
１. 30分で終わりそうなタスクを先ほど説明したタイムボックスにまとめる
２. タイマーで計測
３.　タイマーが終了したら数分休憩する

**実際に休憩時に行っていること**
・身体を伸ばす
・お手洗いに行く
・お菓子を食べる
・調べたいことを調べる🔍

ポモドーロを行うメリットは２つあります。
1．メリハリをつけて作業を行うため、仕事の生産性アップに繋がる
2．１つのタスクを粒度を細かく分割し処理していけるため、精神的な疲れを軽減できる

私は実際に以下のアプリを使用しています↓

https://apps.apple.com/jp/app/as-timer/id512464723?mt=12

タイマーが邪魔だと感じる場合は、スマートフォンや物理的な時計をデスクに用意するのもおすすめです！
時間を意識し、一つひとつのタスクに集中することで、仕事の進捗が向上すると思いますので、ぜひ試してみてください✊🏻

# ■おわりに
ここまで読んでくださりありがとうございます🙇🏻‍♀️
簡単なことだけど継続することが難しいと感じると
これだった自分も出来そうだなというものを試していただき、少しでも誰かの役に立てたら嬉しいです
！また、実際に取り組んでいただく中で自分なりの方法を見つけてみてください✊🏻
リクエストラッパー

```CachingRequestBodyWrapper.java
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;

public class RequestWrapper extends HttpServletRequestWrapper {
    private final String body;

    public RequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        StringBuilder stringBuilder = new StringBuilder();
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(request.getInputStream()));
        String line;
        while ((line = bufferedReader.readLine()) != null) {
            stringBuilder.append(line);
        }
        body = stringBuilder.toString();
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());
        ServletInputStream servletInputStream = new ServletInputStream() {
            public int read() {
                return byteArrayInputStream.read();
            }

            @Override
            public boolean isFinished() {
                return byteArrayInputStream.available() == 0;
            }

            @Override
            public boolean isReady() {
                return true;
            }

            @Override
            public void setReadListener(ReadListener readListener) {
                throw new RuntimeException("Not implemented");
            }
        };
        return servletInputStream;
    }

    public String getBody() {
        return body;
    }
}
```



```
import com.fasterxml.jackson.databind.ObjectMapper;

public class CustomPreAuthFilter extends AbstractPreAuthenticatedProcessingFilter {
    @Override
    protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
        try {
            RequestWrapper requestWrapper = new RequestWrapper(request);
            ObjectMapper mapper = new ObjectMapper();
            UserCredentials userCredentials = mapper.readValue(requestWrapper.getBody(), UserCredentials.class);
            return userCredentials.getUsername();  // 例としてusernameを返す
        } catch (IOException e) {
            throw new RuntimeException("Failed to read or parse request body", e);
        }
    }

    @Override
    protected Object getPreAuthenticatedCredentials(HttpServletRequest request) {
        return "";
    }
}

```
Power Automateでブログ更新通知を受け取る方法

今回はマイクロソフトの「Power Automate」を使用しブログ更新通知を受け取る方法についてご紹介致します。

## Power Automatとは？

「Power Automat」はマイクロソフトサービス等での作業を自動化する事が出来るRPAツールとなっております。

一部有料機能等もございますが今回は無料機能のみで実装可能となっております。

題名の通り今回は「Power Automat」のRSS機能と通知機能を使用して更新通知を行うフローを作成します。

## テンプレートで作成できる

![pa001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3668800/fadc2701-b61b-c2a9-1d6a-21f97cc452bc.png)

「Power Automat」では多くのテンプレートが公開されております。

テンプレートを利用する事で効率的に自動化フローを作成する事が出来ます。

[ブログ更新通知テンプレート](https://powerautomate.microsoft.com/ja-jp/templates/details/16d8d2208dee11e6b0dc69a5d7a699a9/power-automate-%E3%83%96%E3%83%AD%E3%82%B0%E3%81%AE%E6%9B%B4%E6%96%B0%E6%83%85%E5%A0%B1%E3%82%92%E5%90%AB%E3%82%80%E3%83%97%E3%83%83%E3%82%B7%E3%83%A5%E9%80%9A%E7%9F%A5%E3%82%92%E5%8F%97%E3%81%91%E5%8F%96%E3%82%8B/)


## フローの作成

![pa002.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3668800/aa7a7370-a681-f314-21b2-cf512fe8a891.png)


先程のテンプレートを選択すると上記の様なベースのフローが作成されます。

初めにオレンジ色のRSS機能をクリックし更新を確認したいサイト情報を入力致します。

![pa003.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3668800/046df738-6b73-4023-3045-8f869d320bdf.png)

デフォルトではマイクロソフト社のフィードのURLが登録されておりました。

「RSS フィードの URL」項目に任意のURLを入力する事で更新通知を確認する事が出来ます。

今回は下記のブログサイト「https://r1999.com/」を登録します。

[インフラ技術部](https://r1999.com/)

WordPressを使用し構築しているサイトでは下記のURLでフィードが生成されております。

```
https://ドメイン/feed/
```

その為、今回は以下を登録します。

```
https://r1999.com/feed/
```

また用途に合わせて項目を確認する頻度を調整する事が出来ます。

頻度には、秒、分、時間、日付、週等が設定可能となっております。

ご利用の用途に合わせてご設定をよろしくお願いいたします。

以上で設定完了となります。

## 動作確認

それでは動作確認の為にブログ更新を行ってみます。

![pa004.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3668800/e2dc649f-b158-29ac-ee0f-674e72595292.jpeg)

ブログ更新を行うとスマホアプリに上記の様に通知が行われます。
Zoho Cliqのチャンネルにメッセージを投稿する方法(GAS)

# はじめに
　Googleカレンダーの予定、メール、TODOリストの情報を取得・操作するのに、GASはとても便利ですよね。
　弊社はメールやカレンダーの標準ツールにGoogleを使用しているのですが、
　そのような会社では、コミュニケーションツールはTeamsやslack等、Googleではない場合が殆どではないでしょうか。
　私は、slack、Teams、Chatworks、Zohoを業務で利用しています。
　複数ツールを使用していると、GASから外部のREST APIを使用したい場面が多々あります。

 :::note info
今回は、**GASからZohoのWebhook APIを呼び出すプログラム** を実装したので、手順をご紹介します。
:::

# やってみたこと
Googleスプレッドシート上の「出勤」ボタンを押下するとGASが実行される。
そして、Googleカレンダーより本日分の予定会議とTODOリストを取得し、
Zoho Cliqのチャンネルにメッセージ投稿する。
（私が出勤したことを社内メンバーにお知らせする意味も兼ねています。）

# この記事で書かないこと
:negative_squared_cross_mark:GASでの、予定やTODOリスト取得の実装方法
（Web検索すると多くの記事がHitしますので、ここでは説明しません。）

# この記事で書くこと
:white_check_mark:Zoho Cliqへ メッセージ投稿するための、Webhook URLの調べ方
:white_check_mark:GASでZoho Cliqへ メッセージ投稿するサンプルソース

# 必要な情報(Webhook URL)の取得
まず、メッセージ投稿先の Zoho CliqのWebhook URLを調べます。
## Webhook URLの調べ方
1. Zoho Cliq 起動
1. 右上にあるプロフィール画像をクリック
1. Bots & Tools に移動
1. Webhook Tokensに移動（左下から２番目）
1. IDの認証画面が表示されたらパスワード認証して 次画面へ進む
1. Webhookトークン一覧画面にて、トークンを選択。(1件目でOK）
1. [WebhookのURLを取得する]ボタン押下して 「WebhookのURL取得」画面へ進む
    1. タブで「チャンネル」を選択（チャンネル、ボット、拡張機能 から選べる）
    1. チャンネルを選択
    1. 「WebhookのURL」の文字列が表示されます。
    1. 右端の コピー ボタン押下
![スクリーンショット 2024-06-19 200422.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3315076/f1c4a65f-d6f8-f29f-5641-fd0c9acac38e.png)

# サンプルソース
GASの実装です。（Zoho Cliqへメッセージ投稿する箇所のみ抜粋）

```javascript:postToZohoCliq.gs

function postToZohoCliq(strMsg) {
  // 「必要な情報(Webhook URL)の取得」の手順で取得したWebhook URLをペースト
  var url = "https://cliq.zoho.com/api/v2/channelsbyname/{$channel-unique-name}/message?zapikey={paste_webhook_token_here}";  
  
  var payload = {
    text: strMsg //送信したいメッセージをここに書きます
  };
  
  var options = {
    method : "post",
    contentType: "application/json",
    payload: JSON.stringify(payload)
  };
  
  try {
    var response = UrlFetchApp.fetch(url, options);
    Logger.log(response.getContentText());
  } catch (e) {
    Logger.log("メッセージ送信に失敗しました: " + e.message);
  }
}
```

# 実行イメージ
![TPIYDM1KWIKGoJjUkOsS1718797909-1718797996.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3315076/60e96e9a-e158-26f3-5b39-5a7e42f93b26.gif)
1. 「出勤」ボタンでGAS起動
1. Googleカレンダーより本日の予定会議とTODOリストを取得
1. 投稿メッセージ文字列を生成
1. Webhook APIで、Zoho Cliqへメッセージ投稿
意図した通り、メッセージ投稿されましたね！

私の場合は、このように日々の作業メモ用にスプレッドシートを用意してメモを取ったり、
毎朝の機械化可能なルーチンワークを「出勤」ボタンのGASに集約して省力化しています。

ご参考になれば幸いです。

# 参照したリファレンス
Working with Webhook Tokens

https://www.zoho.com/cliq/help/platform/webhook-tokens.html
【Slack】面倒くさがりでも続けられる！シンプルなToDo管理法

## 00. はじめに

最近細かな業務が増えてきて、管理が面倒になってきたなぁ、という所でToDo管理どうするかという悩みが出てきました。

## 01. 前提条件

- プロジェクトに関するタスクは別途管理している
- 主なコミュニケーションツールはSlack
- 管理に困っているのはSlackで来る、細々とした連絡事項（勤怠とか、作業指示とか）
- 筆者は超絶面倒くさがり

基本は記憶力頼りで、Slackを定期的に見直したりでやってきましたが、ヒヤッとする瞬間が出てきたので、いい加減システム化したい。かといって管理ツール使うとか、メモ取るとかは無理です。続かないのが目に見えています。

となるともう、普段使用している**Slackでそのまま、まとめて管理**すればいいのでは？ということで調べたら、ちゃんと役に立つ機能は備わっていました。

## 02. 方法

:::note

1. 後で確認、対応が必要なメッセージは**とにかく即ブックマーク**する
2. 期日が設定された作業については**リマインダー**をかける
3. Slack以外で来たタスクは簡単に自分にDMで送ってこれも**ブックマーク**
4. 毎日 **「後で」** を確認して、完了したものはブックマークを外す
:::

ブックマーク　 ⇒ 　「後で」を確認　 ⇒ 　対応完了したらブックマークを外す
基本はこの繰り返しです。
3ヶ月くらい続けて感じたメリットとしては以下のような感じです。

- 「後で」に情報が集約されている
- 「あの連絡どこのチャンネルで来ていたっけ？」と探し回ることがなくなる
- とにかく楽

:::note warn
無料版のSlackは90日でメッセージが消えるため、長期のToDo管理の際はご注意ください
:::

### ブックマーク

やり方は簡単です。対象のメッセージにフォーカスを当て（スマホなら長押し）、ブックマークアイコンを押すだけです。
![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2153171/880071a3-48e1-0e2f-ada5-7acdbb8656c1.png)
似た機能にピン留めがありますが、こちらはチャンネル内の他メンバーにも有効なため個人のToDo管理には向きません。一方、ブックマークは自分のみに有効な機能なので好きなように扱えます。
対応が終わったものは「完了」にして整理し、対応は不要だがメッセージを後で見返したい場合はアーカイブしておくと探す手間が省けます。

### リマインダー

リマインダーを設定したいメッセージ横の3点リーダーからメニューを開きます。「後でリマインドする」を押して、時間を設定します。時間は選択肢から選ぶか、カスタムで自由に設定することも可能です。
リマインダーを設定したメッセージは「後で」で確認できるため、必要に応じて完了にしたり、時間の調整も可能です。
![スクリーンショット 2024-06-10 124435.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2153171/83b9c6e6-4a94-6f94-8946-1a5dbfe6a653.jpeg)

## 03. さいごに

Slackでのタスク管理にはcanvasを活用した方法などもありますが、個人の小さなToDoであれば、ブックマークとリマインダーのみで割と十分だなと思ってます。
Slackを使っていて、細々としたToDo管理で悩んでいる方の役に少しでも立てれば幸いです。
DM(チャット)機能非同期化の失敗

# はじめに
ポートフォリオ制作中です
今まで、いいねやコメント、フォローと非同期化してきましたが、
今回は大きく躓いてしまったのでアウトプットします

⚠️こちらは失敗のアウトプットです
非同期化の全体の流れは以下の記事にまとめてます🌱

https://qiita.com/3rarara/items/b434d7331f88c5ee5a49

# 一つ目のエラー

```
ActionController::UnknownFormat in Public::ChatsController#create
Public::ChatsController#create is missing a template for this request format and variant.
request.formats: ["text/html"]
request.variant: []
 raise ActionController::UnknownFormat, message
```

テンプレートフォーマットがないよと言われています
これはシンプルに`local: true`を`local: false`に変え忘れです
ここは簡単なエラーなのでさらっといきます

# 二つ目のエラー

これが問題のエラーです！
```
ActionView::Template::Error (undefined method `each' for nil:NilClass):
    1: <div class="row justify-content-center">
    2:   <% chats.each do |chat| %>
    3:     <% if chat.user == current_user %>
    4:       <div class="col-10 text-right mb-3">
    5:         <div class="says px-3">
  
app/views/public/chats/_chats.html.erb:2
app/views/public/chats/create.js.erb:1
```

チャット一覧のeach（chats)がnilだと言われています
非同期でない時はテンプレートとして使えているのにどうして？
となったのですが、そもそも非同期化していると順序が違うのでエラーになってしまうのでした！

# 非同期化時の流れ

#### ①フォームを送信してcreateする

```chats_controller.rb
class Public::ChatsController < ApplicationController
  def show
:
    @chats = @room.chats
    @chat = Chat.new(room_id: @room.id)
  end

  def create
    @chat = current_user.chats.new(chat_params)
    @chat.save
      # redirect_to request.referer
  end
end
```
・show画面でフォームから送信されたデータはcreateにいく
・createでは通常時`redirect_to request.referer`を使用するが、今回は非同期化のため`create.js.erb`にいく
```create.js.erb
$('.chat-index').html("<%= j(render "public/chats/chats", chats: @chats) %>");
$('.chat_textfield').val("");
```
・`create.js.erb`でrenderするため、@chatsはcreateアクションで定義しないと使用することができない
ではもう一度コントローラーを見てみましょう

```chats_controller.rb
class Public::ChatsController < ApplicationController
  def show
:
    @chats = @room.chats
    @chat = Chat.new(room_id: @room.id)
  end

  def create
    @chat = current_user.chats.new(chat_params)
    @chat.save
      # redirect_to request.referer
  end
end
```

createに定義されていないですね！！！
非同期化でない場合は必要ないので盲点でした😭

```chats_controller.rb
class Public::ChatsController < ApplicationController
  def create
    room = Room.find(params[:chat][:room_id])
    @chats = room.chats
    @chat = current_user.chats.new(chat_params)
    @chat.save
  end
end
```

# さいごに
これで無事非同期化できました！
今回は失敗のアウトプットです！
dependency-cruiser-report-actionをmonorepoに使おうとしてハマったメモ

# 概要
[dependency-cruiser-report-action](https://github.com/MH4GF/dependency-cruiser-report-action)を使って、typescriptの依存性の可視化をプルリクのコミットメッセージに入れた。


**2025.01.06 追記**
コメントにあるように、作者様が修正済なので、下記の記事は古い情報となる。
今では`MH4GF/dependency-cruiser-report-action@v2`の指定で問題ない。
[試したプルリク](https://github.com/hibohiboo/Yakumi/pull/10)

---
下記は古い情報となっているので注意してください。
当時の記事を一応残しておきます。

# 概要
[dependency-cruiser-report-action](https://github.com/MH4GF/dependency-cruiser-report-action)を使って、typescriptの依存性の可視化をプルリクのコミットメッセージに入れた。
思ったより苦労したのでメモ。


[プルリク](https://github.com/hibohiboo/Yakumi/pull/7)
[ソースコード(Github Actions)](https://github.com/hibohiboo/Yakumi/blob/0a8da528fca5518e27dbaca09c8bdb26af3a16ec/.github/workflows/depcruise.yml)

# 結論

packageVersionを指定している場合は、`MH4GF/dependency-cruiser-report-action@v2.5.0`のようにマイナーバージョンまで指定しないとエラーとなる。


# 試行錯誤
下記のエラーが発生した。

```
found 0 vulnerabilities
/usr/local/bin/yarn run -s depcruise --output-type mermaid --config .dependency-cruiser.cjs --focus ^.dependency-cruiser.cjs|^src/domain/fallMagia/hooks/udonTutorialHooks.ts .dependency-cruiser.cjs src/domain/fallMagia/hooks/udonTutorialHooks.ts
error This project's package.json defines "packageManager": "yarn@npm@10.8.1". However the current global version of Yarn is 1.22.22.

Presence of the "packageManager" field indicates that the project is meant to be used with Corepack, a tool included by default with all official Node.js distributions starting from 16.9 and 14.19.
Corepack must currently be enabled by running corepack enable in your terminal. For more information, check out https://yarnpkg.com/corepack.
```

[issue](https://github.com/MH4GF/dependency-cruiser-report-action/issues/287)に同様のエラーが上がっているけどCloseされており、首をかしげることになった。

このとき、`MH4GF/dependency-cruiser-report-action@v2`は最新のバージョンが利用されると思っていた。

# 無事、レポートの作成ができた。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99876/f5647ea6-81d9-9e99-2712-596e1c067882.png)



# 参考

[ESLintだけでは守れない。Dependency cruiserによるアーキテクチャー保護](https://zenn.dev/hedrall/articles/f565147bee5da8)
[dependency-cruiser-report-actionでPRの変更ファイルの依存関係を可視化してコメントする](https://zenn.dev/mh4gf/articles/12fcdcba14e576)  
[GitHub Actions の uses でメジャーバージョンを参照するのはリスク](https://zenn.dev/snowcait/articles/53ec922a414dde)

[dependency-cruiser-report-action](https://github.com/MH4GF/dependency-cruiser-report-action)
mock関数をclassにまとめてpytest.fixtureを使う

mock関数をclassでまとめたいときにpytest.fixture周りで詰まった点に関しての記事です。

ここでは、S3とAPI callの例で説明します。

```python
# サンプルコード
class S3Client:
    def __init__(self, bucket: str):
        session = boto3.Session()
        self._s3_client = session.client("s3")

    def sample_func(self, s3_object_key: str) -> bool:
        return "output"

def api_call():
    return "output"


class TestClass:
    def __init__(self, s3_client):
        self._s3_client = s3_client

    def run(self, api_call):
        return api_call.call()
```

# mock関数に書く方法

以前は、mock関数以下のように作成していました。
```python
# mock関数
@pytest.fixture
def mocker_s3_client(mocker):
    return mocker.Mock(spec=S3Client)


@pytest.fixture
def mocker_api_call(mocker):
    return mocker.Mock(call=mocker.Mock(return_value=None))


# テストコード
def test_A(mocker_s3_client, mocker_api_call):
    actual = TestClass(s3_client=mocker_s3_client).run(mocker_api_call)
    assert actual == "test"
```

この場合、
- mockする関数が増えた場合に引数が増える
- テストファイルを分けるときに、importが増える

ため、なるべくclassとしてまとめたいと思いました。


# mock関数をclassにまとめる方法
まず、mock関数を`MockClass`にまとめます。
```python
# mockクラス
class MockClass:
    def __init__(self, mocker):
        self._mocker = mocker

    def mocker_s3_client(self):
        return self._mocker.Mock(spec=S3Client)

    def mocker_api_call(self):
        return self._mocker.Mock(call=self._mocker.Mock(return_value=None))
```

この状態で、pytest.fixutreをclassか関数の上にデコレートしようとしましたが、動きませんでした。

うまくいったやり方としては、`MockClass`を呼び出す関数を作ることです。
```python
@pytest.fixture
def create_mock_class(mocker):
    return MockClass(mocker=mocker)
```


テストコード側は`create_mock_class`を呼び出します。
```python
def test_A(create_mock_class: MockClass):
    mock_s3_client = create_mock_class.mocker_s3_client()
    mock_api_call = create_mock_class.mocker_api_call()

    mock_s3_client.sample_func.return_value = True

    actual = TestClass(s3_client=mocker_s3_client).run(mocker_api_call)
    assert actual == "test"

def test_B(create_mock_class: MockClass):
    mock_s3_client = create_mock_class.mocker_s3_client()
    mock_api_call = create_mock_class.mocker_api_call()

    mock_s3_client.sample_func.return_value = None

    actual = TestClass(s3_client=mocker_s3_client).run(mocker_api_call)
    assert actual == "test"
```
Excel VBA ヘッダーを残してセル範囲を削除する1番簡単な書き方

## CurrentRegion と Offset を組み合わせて楽をする

Excelの神髄（https://excel-ubara.com/excelvba1r/EXCELVBA508.html）
にて表の範囲を削除するための合理的なコードが解説されているので、メモ代わりに投稿します。こちらのサイトは、Excelのことでわからないことがあったら、よく参考にさせていただいています。当サイトの運営者さま、いつもありがとうございます。

CurrentRegion で値が入っているセル範囲を指定して、Offset で1行ずらします。
Do While とか For Next でも同じことはできますが、この書き方なら1行で書けます。途中に空白の行がある場合はすべて拾いきれないので、空白行がある可能性のある表だとEnd(xlUp)で最終行を指定して、For Next でループする必要があると思います。ただし、空白セルがあっても同じ行の他の列に値が入っていれば、CurrentRegion と Offset で対応できます。

```vb
Sub reset()
 
  Sheets("シート名").Range("B2").CurrentRegion.Offset(1, 0).Select

End Sub
```
![表をリセット.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3812230/edd6b0d7-9b33-8d65-2aa7-fde88cebfa98.png)
チームでビンゴの進捗確認をする時間が一番楽しいかもしれないお話

# はじめに

以前にもチームビンゴに関するお話は投稿したのですが、楽しく継続できています！

https://qiita.com/does_not_exist/items/b8d01aea587f6962d87b

ビンゴを取り入れてから長い期間が経ちますが、
当時と比べても会話量は増えましたし、気づいたら各々が新しいことにチャレンジする宣言の場にもなっています。

# 実際のビンゴ

私のチームは6人いますので、5×5サイズで1人4マス担当です。
各々でその月に達成したいこと、やりたいことを決めます。
毎週進捗を簡単に報告し合い、達成できたら赤線で×を付けます。
ちなみに今月は四隅のマスを私が担当することになったので責任重大です…。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2889192/20a3c009-3e66-b881-f7c0-1344b7d35977.png)


# おわりに
ビンゴの達成具合で何かご褒美を設けてもいいかもしれないですね。
私のチームはご褒美関係なしに単純に楽しんでいるので実は凄いことなのかもしれません。
チームのコミュニケーション促進に困っている方がいましたら是非やってみてはいかがでしょうか！
Snowmobileってもうサービス終了してた件

## はじめに
AWSのsnowファミリーの勉強をしていました。
そこで、
「へぇーテラバイトとかペタバイトのデータをAWSに移行するにはこのサービスを使えばいいんだ」
と思っていました。
そこで、ふと、snowファミリーのsnowmobileって実際使われているのかと疑問に思い、調べた結果・・・

**サービス終了してました・・・！**

## そもそもsnowファミリーとは
snowファミリーとは多量のデータをAWSのストレージへ転送するサービスです。
利用者のもとに大容量のストーレージが送られてきて、そこにオンプレミス環境のデータ等を転送して、AWSに返送するとAWSがAWSストレージサービスにデータを移行してくれるというものです。
sonwファミリーには以下の3つがあります。
- Snowcore
    - 使用可能なHDD：8TB
- Snowball
    - 使用可能なHDD: 80TB
- Snowmobile(サービス終了)
    - 使用可能なHDD: 100PB

snowballではオプションとして、lambda関数をデバイスに設定しておいて、データの加工処理などを施せるようです。

## いつ終了したか
4月に出た記事で明らかになりました。
米CNBCの取材に対してAWSの広報がサービスが終了したことを認めたそうです。

https://www.itmedia.co.jp/news/articles/2404/23/news093.html

取材で明らかになったので、終了になったのはもっと前かもしれません。

公式のsnowファミリーの種類にもSnowmobileはありません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1328154/430225f0-8201-61e2-ee86-f9a8d80a0739.png)

## 終わりに
snowファミリーで一番大きなデータ移行サービスでしたが、あまり需要がなかったんでしょうか。
それとも、もっとより良い方法が見つかったのか。

Snowmobileは日本にはなかったようですが、一度どんなものか見てみたかったです。
（大型トラックで運ばれてくるようです）
WSUSの承認処理を楽にする

## WSUS

WSUSの管理ってみんなどうやっているんだ？
と思うくらい情報が出てこないけど、見つかった情報と
実際の動作からいくつか管理用PowerShellScriptを作っていたりするので
紹介してみる。

## あるグループに承認済みの更新を別のグループにも承認する

* 例えば限定テストグループから全部に展開するとか


```powershell:update_commit.ps1
$_mstGroupName = $args[0]
$_tgtGroupName = $args[1]

$WSUS = Get-WsusServer
$ApprovalTestedGrp = $WSUS.GetComputerTargetGroups().where({$_.Name -eq $_mstGroupName})
$AprovalTgtGrps = $WSUS.GetComputerTargetGroups().where({$_.Name -eq $_tgtGroupName})
if($ApprovalTestedGrp.Length -ne 1){
    Write-Error ($_tgtGroupName+"が見つかりません")
    exit 1
}
if($AprovalTgtGrps.Length -ne 1){
    Write-Error ($_tgtGroupName+"が見つかりません")
    exit 1
}
$AprovalTgtGrp = $AprovalTgtGrps[0]
$Updates = $WSUS.GetUpdates();

$MastterGrpId = $ApprovalTestedGrp.Id
$AprovalTgtGrpId = $AprovalTgtGrp.Id
$TgtUpdates = @()
Write-Output ("対象グループ："+$AprovalTgtGrp.Name + "("+ $AprovalTgtGrpId.ToString() + ")")
$approvalCnt = 0
Write-Output("アップデートの承認処理を開始します")
foreach($update in $Updates){
    $_apprs = $update.GetUpdateApprovals()
    $_appcnt = ($_apprs | ForEach-Object{if(($_.ComputerTargetGroupId -eq $MastterGrpId) -and ($_.Action -eq [Microsoft.UpdateServices.Administration.UpdateApprovalAction]::Install)){$_}}).Length
    if($_appcnt -ne 0){
        $_updated = $update.GetUpdateApprovals()
        $_updatedCnt = ($_updated  | ForEach-Object{if(($_.ComputerTargetGroupId -eq $AprovalTgtGrpId) -and ($_.Action -eq [Microsoft.UpdateServices.Administration.UpdateApprovalAction]::Install)){$_}}).Length
        if($_updatedCnt -eq 0){
            Write-Output ("「"+($update.Title)+"」を承認しました。")
            $update.Approve([Microsoft.UpdateServices.Administration.UpdateApprovalAction]::Install,$AprovalTgtGrp) | Out-Null
            $TgtUpdates += $update
            $approvalCnt++
        }
    }
}
if($approvalCnt -eq 0){
    Write-Output "追加するアップデートはありませんでした。"
}else{
    Write-Output ($approvalCnt.ToString()+ "個のアップデートを承認しました。")
}
exit
```

```powershell
powershell.exe update_commit.ps1 TEST AllPC
```

こんな感じで「TEST」に承認済みの更新を「AllPC」にも承認できる。
これをタスクスケジューラに登録しておけば一定期間内に外さない限り自動で全展開も出来る。

んだけど、ほぼ適用辞めることはないので最初から全展開しても問題ほぼ起きないのでわざわざ使われないのかな？

一応分散してタイミングを変えるとかには使えるけど、あまり分散して適用までの時間を空けちゃうのもアレですし。


## 次回予告？
他にもPowerShellでのWSUS管理ネタがあるので、書いてみる。
AWS EKSでHelmを使ってみよう

# 前提条件

- EKSクラスターが作成済みであること

もしない場合は以下の記事で作成することができます。
https://qiita.com/Takkesan/items/83ed99a36bca5b105d97

# Helmとは

- Kubernetesのパッケージマネージャ
- チャートと呼ばれるパッケージを管理する
- チャートはKubernetesのリソースを定義するYAMLファイルの集まり

# HelmでPrometheusをインストール

今回使うのはこのチャート！
PrometheusからGrafanaまで一気にインストールできる！

https://artifacthub.io/packages/helm/prometheus-community/kube-prometheus-stack

## 1. リポジトリを追加

```bash
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
```

## 2. インストール

```bash
helm install kube-prometheus-stack prometheus-community/kube-prometheus-stack
```

<details>PS C:\Users\user> helm install kube-prometheus-stack prometheus-community/kube-prometheus-stack
NAME: kube-prometheus-stack
LAST DEPLOYED: Tue Jun 18 17:23:48 2024
NAMESPACE: default
STATUS: deployed
REVISION: 1
NOTES:
kube-prometheus-stack has been installed. Check its status by running:
kubectl --namespace default get pods -l "release=kube-prometheus-stack"

Visit https://github.com/prometheus-operator/kube-prometheus for instructions on how to create & configure Alertmanager and Prometheus instances using the Operator.
<summary>こんな出力があれば成功</summary></details>

## 3. 確認

```bash
kubectl get pods
```

# まとめ

Helmを使うことで先人の方が作成したものを簡単に使い回すことができます。
AWS EKSと組み合わせ使ってみましょう！
[eslint] EACCES: permission denied, mkdir '/Users/xxxxx'の解決法

#はじめに
あるreactのプロジェクトでローカルのサーバーを起動させようとすると、見慣れないエラーになりました。。。

```console
$ npm start
[eslint] EACCES: permission denied, mkdir '/Users/xxxxx/node_modules/.cache'
ERROR in [eslint] EACCES: permission denied, mkdir '/Users/xxxxx/node_modules/.cache'
```

`node_modules/.cache`ディレクトリを作成するためのアクセス権が無いようです。

PCを乗り換えた後などにおそらく起こりやすい問題かと、、、。

# 権限を与えるコマンドを実行

Macで権限付与の時にはお馴染みの`chown`コマンドを使います。（`change owner`の意味）

UNIX系のOSでは個々のファイルごとにオーナーが存在し、初期状態ではファイルを作成したユーザーがオーナーとなり、その他のユーザーはファイルを書き換えられないようにモードが設定されています。
chownはファイルのオーナーを変更するコマンドです。


```console
sudo chown -R $USER:$GROUP /Users/xxxxx/node_modules
```

`$USER`と`$GROUP`を自分の環境に置き換えて、上記のコマンドを実行すれば解決します！

## `$USER:$GROUP`の確認方法

### $USER
```console
 echo $USER
 ```

シェル環境変数である$USERの値（つまりuserの名前）が表示

### $GROUP

```console
id -gn   
```

これでグループ名も表示されます。
おそらく`staff`か`admin`が表示されるかと。

グループに関することはなかなか意識することがないのですが、👇の記事が面白かったです

https://web-y.dev/2021/05/23/mac-unix-user-group-overview/



# 参考

https://dev.classmethod.jp/articles/handling-eacces-permission-denied-for-npm-commands/
go-vcrでレスポンスを加工してからカセットに記録する

# TL; DR

- `*recorder.AddHook` でカセットを加工する関数を登録
    - https://github.com/dnaeon/go-vcr/tree/v3?tab=readme-ov-file#hooks

# はじめに

go-vcrはHTTPリクエストを行う関数のテストに使用するライブラリです。

https://github.com/dnaeon/go-vcr

初回テストでは実際にAPIリクエストを発生させ、リクエスト、レスポンスの組み合わせをカセット（実体はyamlファイル）に保存します。
2回目以降はクライアントがカセットをもとにレスポンスを返すため、実際にはリクエストが発生しません[^cassette]。

これにより、
- 実際のAPIリクエスト、レスポンスでテストできる
- リクエストを発生させずにテストできる

を両立することができます。

テスト実行時に対象サーバーへの疎通性やリクエスト負荷等を気にする必要はありません。また、冪等性のないリクエストが含まれていても繰り返し実行可能です。

# レスポンスの加工

ところで、テストでは実際のレスポンスをそのまま使うと都合が悪い場合もあります。

- 認証情報等コミットしたくない情報がカセットに載ってしまう
- 実際のリクエストでは発生しなかった特殊なエラーレスポンスに対応する異常系をテストしたい

記録したカセットを手で書き直すことでも対応可能ですが、vcrの `hooks` を使えばより手軽に修正ができます。

## hooksを使用

hooksは、リクエストやレスポンスをカセット(yamlファイル)に保存する前に加工する関数です。

```go:公式リポジトリのREADMEより抜粋
// Authorizationヘッダを消すhook
hook := func(i *cassette.Interaction) error {
	delete(i.Request.Headers, "Authorization")
	return nil
}
// hookを追加し、レスポンスを受け取った後に適用されるようにする
r.AddHook(hook, recorder.AfterCaptureHook)
```

https://github.com/dnaeon/go-vcr/tree/v3?tab=readme-ov-file#hooks

### 実例：httpbinのIPアドレス取得API
今回は実例として https://httpbin.org/ の IPアドレス取得APIを使用します。

https://httpbin.org/#/Request_inspection/get_ip

`GET /ip` は、リクエスト元の(グローバル)IPアドレスを返します。

```bash
# この端末のグローバルIPアドレスが返ってくる（以下はxで伏字にしている）
$ curl httpbin.org/ip
{
  "origin": "xxx.xxx.xxx.xxx"
}
```

このAPIを呼び出す関数をgo-vcrを使用してテストする場合

- セキュリティ上IPアドレス（=開発環境のグローバルIPアドレス）を含むカセットをコミットしたくない

という問題があります。そこで、hookを使ってIPアドレスをローカルホストに置き換えます[^localhost]。

まずはhookを作ります。パスが `/ip` に一致したとき、レスポンスボディで `127.0.0.1` を返すようにします[^path]。

```go
func patchGetIPResponse(i *cassette.Interaction) error {
	if strings.Contains(i.Request.URL, "/ip") {
		i.Response.Body = `{"origin": "127.0.0.1"}`
	}

	return nil
}
```

そして、recorder生成時にこのhookを追加します。

```go
func runRecorder(cassette string, hooks []func(*cassette.Interaction) error) (*recorder.Recorder, error) {
	r, err := recorder.New(cassette)

	// 受け取ったhookをすべて追加
	for _, hook := range hooks {
		r.AddHook(hook, recorder.AfterCaptureHook)
	}

	return r, err
}
```

```go:テスト本体
func TestShowIPAddress(t *testing.T) {
	// 上記のhooksを指定
	hooks := []func(*cassette.Interaction) error{
		patchGetIPResponse,
	}

	r, err := runRecorder("testdata/get_ip", hooks)
	if err != nil {
		t.Fatal(err)
	}
	defer r.Stop()

	var out bytes.Buffer
	err = ShowIPAddress(&out, r.GetDefaultClient())
	if err != nil {
		t.Error(err)
	}

	// レスポンスを書き換えてからカセットに記録しているため、assertionも `127.0.0.1` を使用した内容になる
	expected := "IP Address: 127.0.0.1"
	actual := out.String()

	if expected != actual {
		t.Errorf("%q != %q", expected, actual)
	}
}
```

実装側も含めたコード全体については以下のリポジトリをご覧ください。

https://github.com/Syuparn/vcr-custom-sample

[^cassette]: モード変更により「常に上書き」「記録しない」等の動作も可能です。
[^localhost]: ローカルホストでなくても良いのですが、誤ってリクエストしても問題ないIPアドレスにするのが安全です。
[^path]: テストケースが多い場合は、HTTPメソッドやステータスコード等でも絞りこんだほうが良いと思います。
サーバレス構成とテスト駆動開発の相性が良いお話

# はじめに
- バックエンドがサーバレスな構成で開発を進めていた
- 動作確認するためには一度デプロイをする必要があった
- 後からテストを導入したことによりテスト駆動開発ができるようになった
- テストが通るようにコードを実装するため、毎回デプロイして動きを確かめる必要がなくなった

# 所感
一回のデプロイで約5分程度かかるため、変数の型エラーやデータ参照周りでのエラーで毎回デプロイし直すと大変です。この問題点をテスト駆動開発で事前にクリアできるので相性が良いように感じました。
テストを入れてテスト駆動開発を始めてからは、動作確認のためのデプロイ頻度は明らかに減ったと感じます。

テスト駆動開発について取り組んだお話はこちらの記事で取り上げています！

https://qiita.com/does_not_exist/items/0006ff14936ce9113e12

https://qiita.com/does_not_exist/items/2551bb4c8b7398253368

サーバレスデプロイについてはこちらの記事で取り上げていますのでよろしければご参考ください！

https://qiita.com/does_not_exist/items/a130a157a92fe26a3981

# おわりに
最近は開発体験を良くしたいという意識が強くなっているので、気になった技術は積極的に挑戦したいと思います。