OmniAICreator/Qiita-1.07M huggingface dataset:
矩形選択と多言語化(Localizable.strings)の相性の良さについて

# はじめに

汎用的なアプリが完成した時って、日本に限らず世界にリリースしたい！！
と思うのは常ですが、表示文字列を１つ１つ翻訳サイトで翻訳して、
延々と延々とコピペコピペコピペ・・・

これは、本〜当にうんざりします。

そんな時、矩形選択を用いることで大幅に面倒を解消できたので、その方法を紹介します。



### まず矩形選択とは

選択範囲を長方形にできる機能です。
XCodeの場合、optionキー + ドラッグで行います。
ドラッグを開始した時点が選択範囲の始点になります。

![タイトルなし.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4005361/147e349a-1278-615b-c730-8abbb27dd9be.gif)


### Localizable.stringsを編集していく

実際の編集手順です。
例として、次のLocalizable.strings(日本語)を英語に変換していきます。

```ja.lproj/Localizable.strings
"text1" = "指定なし";
"text2" = "削除を実行しますか？";
"text3" = "現在のデータを上書きします";
"text4" = "保存データをクリアしますか？";
"text5" = "データなし";
"text6" = "編集を再開";
"text7" = "最初から";
```
Localizable.strings(英語)は↓のようにしておきます。

```en.lproj/Localizable.strings
"text1" = "";
"text2" = "";
"text3" = "";
"text4" = "";
"text5" = "";
"text6" = "";
"text7" = "";
```

で、翻訳する文字列はメモ帳などに控えておいて
```notePad
指定なし
削除を実行しますか？
現在のデータを上書きします
保存データをクリアしますか？
データなし
編集を再開
最初から
```



これを翻訳サイトで一気に翻訳！！！

![スクリーンショット 2025-02-08 15.56.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4005361/b2c43e44-223c-56b7-46b7-9147944a1e79.png)

翻訳結果をコピーして、Localizable.stringsに
<strong>「貼り付ける場所を矩形選択してから」</strong>ペーストします。

![タイトルなし２.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4005361/3e8f9f39-1206-5698-2dc0-879afd47cb2d.gif)


あとは
・翻訳サイトの英語→別の言語に切り替えてコピー
・Localizable.stringsを別の言語に切り替えて、同様に矩形選択してから貼り付け

を繰り返します。

この手順で例えば

**15言語 ✖️ 20行**

くらいで５分〜10分程度で終わると思います。


# おわりに

Google翻訳の場合、一度に翻訳できる文字数の制限があり、何百行とかを一気にやることはできないので、その場合は小分けにしてやる必要があります。
他の翻訳サイトでも、翻訳前と翻訳後の改行の数が同じであれば、同様にスムーズに多言語化できると思います。

矩形選択、使い方に工夫を要することが多いので使っていて楽しいです。
ぜひお役立てください。
鍵暗号方式を覚えられないので整理

よく聞く3つの暗号方式
1. 共通鍵暗号方式
1. 公開鍵暗号方式
1. ハイブリッド型暗号方式

を全然覚えられないのでGPTに壁打ちしながら整理した。

## 結論
覚えるためのミソは用語ではなく、数と特徴からわかる動きにあると思った。

## 共通鍵暗号方式
関係者間で共有して暗号復号に使われる。公開しない。
鍵の数は、共通のデータを使用する人の間だけで使われる。
作成される鍵の数は、nC2通り。
特徴は、処理が早いが盗られると覗き放題になる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2932592/a6e660a5-7c0f-b98c-19c9-ab3764ac377c.png)

## 公開鍵暗号方式
受信者が暗号用、復号用鍵を作る。自分だけ復号用を持っている。

公開鍵を渡す
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2932592/43b5c1e7-04cc-f3e6-23b1-ccb22903d67d.png)

暗号化したデータを受け取って復号化する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2932592/94201dba-fc32-cc2b-8762-a84900052ec1.png)

鍵の数は、2n通り。
特徴は、秘密鍵を受信者だけが持つので、盗聴されてもデータの復号はできない。
処理が遅い。

## 共通鍵と公開鍵比較
| 暗号方式 | 鍵の数| 特徴|
|---------------|------------------------|--------------|
| **共通鍵暗号方式** | nC2通り（ペアごとに異なる鍵が必要） | 処理が速いが、鍵が漏れると覗き放題になる。|
| **公開鍵暗号方式** | 2n通り（各受信者に公開鍵と秘密鍵） | 秘密鍵を公開しないためセキュリティが強いが、処理が遅い。|

## ハイブリッド型方式
上記二つの良いところどり。
データの暗号復号には共通鍵を使い、共通鍵に公開鍵秘密鍵で暗号復号を行うことで安全性を高めている。

共通鍵を公開鍵で暗号化、秘密鍵で復号化
共通鍵は公開鍵暗号で暗号化されるため、安全に相手に送ることができる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2932592/cf834e77-09d3-0a2d-0078-aeb49f213cf5.png)

共通鍵で暗号化されたデータを復号化
データへの暗号復号を共通鍵で行い、高速に通信できる。公開鍵暗号方式の処理速度を補完している。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2932592/d68f4722-52c4-5eb8-fa49-36c84d7354a1.png)

## 3つの暗号方式の比較
| 暗号方式| 鍵の数| 特徴|
|--------------------|-------------------------------|----------------|
| **共通鍵暗号方式**   | nC2通り（ペアごとに鍵が必要） | 処理が速いが、鍵が漏れると覗き放題になる。|
| **公開鍵暗号方式**   | 2n通り（各ユーザーに公開鍵と秘密鍵） | 秘密鍵を公開しないためセキュリティが強いが、処理が遅い。|
| **ハイブリッド型暗号方式** | 2n通り + 共通鍵1つ             | 公開鍵暗号で共通鍵を安全に交換し、その後は共通鍵暗号で処理する。処理速度とセキュリティのバランスが良い。 |

## まとめ
各暗号方式について、もともと名称でどうにか覚えようとしていたが、
データのやり取りの中で何がリスクになるのかを中心に据えつつ、作られる数がイメージとしてわかりやすく、覚えるために良い情報だと思った。


## 補足
いろいろ調べてみると、受信者と送信者を「人」として表現していたのは適切ではなかった。
実際にはk⑥エラの暗号化技術はクライアントとサーバー間の通信を安全にするために使われている。

例えば、HTTPS(HTTP + TLS)では、TLSがこの暗号化プロトコルに該当する。
TLSでは、最初に公開鍵暗号方式で共通鍵（セッション鍵）を安全に交換し、その後は共通鍵で高速にデータ通信を行う。

つまり、
共通鍵は「ユーザー単位」ではなく「セッション単位」で新しく作成される、クライアントとサーバーの通信を保護するための仕組み
というのが今回の理解だ。
いったん「理解しやすい形でまとめた」ということで、ここまでの整理としておく。
Pythonの for-else を完全理解！ found == False との違いを比較

Pythonの for ループには else 節を付けることができ、特定の条件が満たされなかった場合に処理を実行するのに便利です。一般的に for-else は、ループが 途中で break されなかった場合 に else の処理が実行されます。

### for-else の基本構造

```python
for item in iterable:
    if 条件:
        break  # 条件が満たされたらループを終了
else:
    # break されなかった場合のみ実行
```

### for-else の例

- 例えば、リスト内に特定の値があるかどうかを調べ、見つかった場合に処理を止めるコードを考えます。

```python
numbers = [1, 3, 5, 7, 9]

for num in numbers:
    if num == 5:
        print("Found 5!")
        break  # 5が見つかったらループ終了
else:
    print("5 was not found.")
```

- 実行結果
```
Found 5!
```
break によって else は実行されません。

- リストに値がない場合 (else が実行される)

```python
numbers = [1, 3, 7, 9]

for num in numbers:
    if num == 5:
        print("Found 5!")
        break
else:
    print("5 was not found.")
```

- 実行結果

```text
5 was not found.
```


### found == False での比較

for-else を使わずに、found 変数を使って同じ処理を実装することもできます。

```python
numbers = [1, 3, 7, 9]

for index, num in enumerate(numbers):
    if num == 10:
        print(f"10が見つかりました！ インデックス: {index}")
        break
else:
    print("10はリストにありませんでした。")

```

- 実行結果

```
10はリストにありませんでした。
```

### for else を使う理由
- enumerate(numbers) を使うことで、index も取得できる！
- if で 10 を見つけたら break してすぐ終了。
- もし break されなかったら（10 がなかったら）else が実行される！

特に 「値が見つからなかった場合のみ実行する処理」 を書くときは、for-else のほうがスッキリ書けますね。




https://docs.python.org/ja/3/reference/compound_stmts.html#for
php artisan test --filterで実行するテストクラスを限定しても、メソッドを持たないテストクラスが存在する限りCIワークフローが失敗する

## やりたいこと
Laravel11を使用したプロジェクトにて、main宛のPR作成時にGithub Actionsを使ったCIワークフローにてPHPUnitによる自動テストを実行したい

## 状況
一部のテストクラスのみ完成したため、まずはそのテストクラスだけをCI環境で自動テストとして動かしたい。そこで、php artisan testコマンドに存在するfilterオプションを使って、テスト実行対象クラスを限定させる（下記参照）。そしていざ実行
```yaml:ci_sample.yaml
name: phpunit-tests

on:
  pull_request:
    branches: [main]

jobs:
  test-company:
    runs-on: ubuntu-latest
    steps:
    # 省略
      - name: Run tests
        run: |
          php artisan test --filter "AuthenticationTest|EmailVerificationTest"
```

## 起こったこと
テストは全て成功しているのに、なぜかエラーが発生し失敗として扱われる
```
Run tests

   PASS  Tests\Feature\Auth\AuthenticationTest
  ✓ login screen can be rendered                                         0.37s  
  ✓ users can authenticate using the login screen                        0.07s  
  ✓ users can not authenticate with invalid password                     0.25s  
  ✓ users can logout                                                     0.03s  

   PASS  Tests\Feature\Auth\EmailVerificationTest
  ✓ email verification screen can be rendered                            0.04s  
  ✓ email can be verified                                                0.03s  
  ✓ email is not verified with invalid hash                              0.03s  

  Tests:    7 passed (14 assertions)
  Duration: 0.87s

Error: Process completed with exit code 1.
```

## 原因
テストメソッド未定義のテストクラスが存在していた。
下記テストクラスを削除したらCIワークフローが成功した。
```php:tests/Feature/HogeControllerTest.php
<?php

namespace Tests\Feature;

use Tests\TestCase;
use Illuminate\Foundation\Testing\RefreshDatabase;

class HogeControllerTest extends TestCase
{
    use RefreshDatabase;

    // 
}
```
## まとめ
php artisan testのfilterでテスト実行対象クラスを限定させていても、対象外のテストクラスにメソッドを持たないものがあると、CIワークフローは失敗してしまうみたい。

ちなみに、テストに失敗する「filterで指定されていない実行対象外のテストクラス」があることは問題ない（filterによって実行までは移らないため）

そのため、とにかく「テストメソッドが未定義のテストクラス」が存在する状況は避けた方が良い。
SQSから呼び出したLambdaではDLQは使えない

(※2023-11-17に書いた個人ブログの転記です。)

## 概要
SQSから呼び出した(=イベントソースにSQSを指定した)Lambda関数が、失敗しているにも関わらずLambdaに設定したDLQにメッセージが送信されていなかった。長らく原因がわからなずにいたが、AWSのサポートの方に教えてもらったのでメモしておく。

## 解説
SQSから呼び出した場合、Lambda関数は[同期呼び出し](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/invocation-sync.html)になる。LambdaのDLQは非同期呼び出しの際に有効な機能である。そのため、LambdaイベントソースマッピングにSQSを使用している関数ではLambdaがエラーになってもDLQにメッセージは送られない。

リトライは、SQS側で可視性タイムアウト後に(メッセージ保持期間の間は)自動的に行われるが、それでも失敗した場合はSQSに設定したDLQにメッセージが送られる。そのため、SQS→Lambdaの場合のアラートなどはSQS側のDLQを対象とする必要がある。

## 参考
- [非同期呼び出し - デッドレターキュー](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/invocation-async.html#invocation-dlq)
- [Amazon SQS での Lambda の使用](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/with-sqs.html)
SAML 2.0 フェデレーション用のIAMロールをCDKで作る

(※2023-11-29に書いた個人ブログの転記です。)

# 概要

社内のSAML IDプロバイダーとAWSを連携して、特定のサービスにだけアクセスできるようにする。前提として、[IAMに該当するSAML IDプロバイダーは作成ずみ](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/id_roles_providers_create_saml.html)とする。

流れとしては、以下の手順が必要となる。
1. IdP側でロールを作成する
2. IAM で SAML プロバイダーを作成する(今回はスキップ)
3. IAMでSAML用ロールを作成する
4. IdPソフトウェアを設定して、SAML信頼を確立する

# CDK
3. IAMでSAML用ロールを作成する
```ts
const role = new iam.Role(this, "MySamlRole", {
      roleName: "role name",
      description:
        "description",
      assumedBy: new iam.FederatedPrincipal(
        `arn:aws:iam::********:saml-provider/*********`, // 作成したSAMLプロバイダーのarnを指定
        {
          StringEquals: {
            "SAML:aud": "https://signin.aws.amazon.com/saml"
          }
        },
        "sts:AssumeRoleWithSAML"
      )
    });

    role.addManagedPolicy() // ←ここで任意の権限を付与
```

# 参考
- [SAML 2.0 フェデレーション用のロールの作成 (コンソール)](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/id_roles_create_for-idp_saml.html)
- あとはIdP側のドキュメント
FFmpegのコマンドメモ

(※2022-11-12に書いた個人ブログの転記です。)

### 基礎(ffprobe)

余計な出力なし。インプットとして渡す動画は、ローカルでもネット上の動画でもいける。すごいね！

```shell
ffprobe -hide_banner -pretty -show_format -show_streams input.mp4
```

### プログレッシブ→インタレース

インタレースがなぜだめなのかを実験する。

```shell
ffmpeg -i input.mp4 -vf "tinterlace=5" interlace.mp4
```

### VBR/CBRの判定

- ffprobe nal-hrd vbr / cbr 記載なし
- maxrateが平均より上だったらVBR

### フレームレート変更

```shell
ffmpeg -i input.mp4 -r 10 framerate.mp4
```

### 参照フレーム数取得

ffprobeでrefsを参照(この値は注意が必要らしい。詳細はよくわからず)。

### ラウドネス取得
ffmpegでebur128フィルタを使用する。

```shell
ffmpeg -nostats -i input.mp4 -vn -y -filter_complex "ebur128=framelog=verbose" -f null -
```

### ラウドネス可視化

```shell
ffmpeg -i input.mp4 -filter_complex "ebur128=video=1:target=-23.0" loudness_visual.mp4
```

### ラウドネスを-18LKFSにする

```shell
ffmpeg -i input.mp4 -filter_complex "loudnorm=I=-18" loudness.mp4
```

### 音声ピーク値(サンプルピーク)取得

ffmpegでebur128フィルタを使用する。

```shell
ffmpeg -nostats -i input.mp4 -vn -y -filter_complex "ebur128=framelog=verbose:peak=sample" -f null -
```

## 参考
- [『FFmpegの本』](https://techbookfest.org/product/5702243030401024)
- [『FFmpegのオーディオフィルタの本』](https://techbookfest.org/product/5973716200587264?productVariantID=6220649567617024)
別のAWSアカウントでサブドメインを使う(CDK)

(※2023-10-18に書いた個人ブログの転記です。)

## 概要
別のAWSアカウントで管理しているドメインのサブドメインを、別のAWSアカウントで使いたい。そんな時は、サブドメインを別のAWSアカウントのHosted Zoneに権限委譲すれば良い。それをCDKで行う手順をまとめる。

例として、以下のような状態を想定する。
- AWSアカウント1(ドメインを管理している)
  - masatora.com
- AWSアカウント2(委譲先)
  - stg.masatora.com←使いたいサブドメイン

## 手順とCDK
① AWSアカウント2(委譲先)でホストゾーンを作成する
```ts
private createBuyerAJAVisionZone() {
    return new route53.PublicHostedZone(this, 'HostedZone', {
        zoneName: 'stg.masatora.com',
    })
}
```

② 1で作成されたホストゾーンのNSレコードの値をコピー
③ AWSアカウント1(ドメインを管理している)でNSレコードを作成する


```ts
new route53.NsRecord(this, 'NsRecord', {
    zone: hostedZone,
    recordName: 'stg',
    values: [
    // ここに2でコピーしたNSレコードの値を入れる(4つある。追加も削除もせず全部入れることを推奨
    ],
})
```

## 参考
- ほぼこれを見ながらやった→[[Route 53] 別のAWSアカウントでサブドメインを使えるように権限移譲する](https://dev.classmethod.jp/articles/route53-transfer-hostedzones-2021/)
- [Amplifyのカスタムドメインの設定](https://docs.aws.amazon.com/ja_jp/amplify/latest/userguide/custom-domains.html)
    - 今回はAmplifyに設定したかったので。AmplifyではSSL/TLS証明書は自動発行・更新してくれる。
- https://docs.aws.amazon.com/ja_jp/Route53/latest/DeveloperGuide/SOA-NSrecords.html
enpitという授業を再度お勧めします。

お久しぶりです。いいえ、ほとんどの人は初めまして。筑波大学大学院の東地亮太と申します。自身２つめの投稿となるこの記事は、enpitという授業を2度取った人の体験記になります。
2年ほど前、大学3年の時に私はenpitという授業に出会い、アジャイル開発を体験してアプリケーション開発にチームで勤しみました。右も左もわからなかった私は、開発に対しての貢献がほとんどありませんでした。それでは終われないとリベンジを誓い、2年越しに再度授業を取ったということです。倍率は２倍ほどだったと言います。私の身勝手なリベンジを許可してくださった先生、ありがとうございます。
結論から言うと、すごく成功しました。詳細はこれから追って書くのですが、前回の記事とは毛色が多少異なり、成功体験記として書かせていただこうと思います。しかしながら、前回と比べて開発能力が向上したわけではありません。にもかかわらず、なぜ成功と言えるのか、できる限り記したいと思います。

## enpitってなに
enpitとは、「プロジェクト型学習(Project Based Learning, PBL)を基軸に、学生がチームを組んで自律的に自分達のテーマの具現化を目指します。チームでのプロダクト開発を通じて、チーム運営や、ユーザが本当に必要としているものを掘り下げる技術を体験します。」とのことです。
すみません、前回の私の記事に書いたものを丸々書かせていただきました。以前は春からの授業だったのですが、どういうことやら今回は夏からでした。去年の自己学習部分が省略された形になります。

## やったこと
私たちは、書い忘れをなくしたい大学生向けの買い物メモ作成アプリ”ゼロメモ”を作りました。これは、カードをスワイプするだけで簡単に買い物メモの作成ができ、既存のメモアプリとは違い、買いたいものを思い出す、メモする手間を省くことができます。
そして、このプロダクトは筑波、琉球大合同発表会で優秀賞を獲得することができました。ちなみに、最初に言った成功とはこのことを指します。最優秀賞じゃなかったら成功じゃないでしょという意見は聞かないでおきます。最優秀賞は琉球大学のチームでした。賛辞を送るとともに、悔しかったという感想をここに残しておきます。たかが大学の授業で悔しくなるほど本気になれることなんてそうありません。その機会を提供してくれるのがenpitになります。我ながら良い宣伝ですね。それはさておき、チームメイトには感謝しかないです。私だけだと開発能力に乏しいゆえまともなアプリが作れたかも怪しいです。

## 成功の秘訣１　”真”心理的安全性
成功の裏には必ず理由がありますが、その１つは心理的安全性をずっと確保できたことにあると思います。2年前に書いた記事では、心理的安全性の難しさと確保のコツについて、技術に乏しい側の視点で話をしました。心理的安全性は、発言力があったり目上の方の方が意識すべきで、確保するには最初に発言してみて、発言しやすい空気を作るという話です。今回は、さらにそれをアップデートして、環境づくりに励みました。具体的にいうと、話したことやアプリのことを、モニターやホワイトボード、Miroなどで常に視覚化したことです。これの利点は２つあります。１つ目は、話したいことなどを事前に書いておいたり、実際に見せたりすることで、説明の手間を省き、発言がしやすくなることです。実際、議論では頭の回転が早かったり弁が立つ人の方が発言権があるのは当たり前です。ですが、それを環境でカバーすることができるのです。そして２つ目は、誰にでも役にたつことです。議論を視覚化しておくことは、みんなの共通認識を助け、スムーズに進めることができます。なので、この方法は導入しやすく、受け入れられることでしょう。なんなら、議論が得意な側に回ることができるかもしれません。おすすめです。

## 成功の秘訣２　圧倒的ユーザー目線
秘訣２つ目は、圧倒的なユーザー目線をすることです。そのためにしたことを下に挙げていきます。
・自分たちで日常的にアプリを使う
→内側からのフィードバックの獲得、ユーザー目線の獲得
・メンターを授業中買い物に連れて行く、使ってもらう
→目の前でのユーザー観察
・アプリの中身は全て動くもの（動かないボタンなどを搭載しない）
→ユーザーが使えないと意味がない
・最大限簡素なものに
→多機能はユーザー満足度に直結しないという考え
→ユーザーに刺しに行くには、何かの上位互換ではなく、別ベクトルの尖が必要

このようにいっぱいのことを考えて作りました。何か１つでも参考になればと思います。

## 最後に
この記事では、enpitという授業、並びにそれを開講している筑波大学、琉球大学へのお薦めをして締めたいと思います。私はこの2年で、学生から就活生になりました（と自負しています）。就活をしていく中で、この授業がやっていることがいかに実践的で、大切なことを学べるかを日々感じています。おすすめの対象である高３生にはもしかしたら届かない思いかもしれませんが、この記事に辿り着いた方は、一回私のいうことを信じてみてもいいかもしれません。そのくらい価値があると思ってます。チームで開発したい方、ユーザーの力になれる仕事をしたいと考えている方、とにかくチームで何か成し遂げたい方など、なんでも大丈夫です。それを歓迎してくれる受け皿がここにはあります。短めではありますがここで締めたいと思います。ご拝読ありがとうございました。

（最後にAMFを載せます。このチームでの学びみたいなものです。）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3131416/779bf833-72e8-2070-871d-b5bd057f25b7.png)
WindowsにElixirをインストールする

# はじめに

今まで、WSL上でElixirインストールして使ってきたのですが、事情があって、WSLを使わないでインストールすることになったので、方法をメモしておきます。

コマンドを順に入力するだけで、インストール完了できました。

# scoopをインストール

https://github.com/ScoopInstaller/Install#readme

power shellで次のコマンドを実行してネットワークから取得して実行することを許可する
```PS
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
```

scoopをインストール
```PS
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
```

# アプリをインストール

```
scoop bucket add versions
scoop install git
scoop install elixir
scoop install erlang
scoop bucket add extras
scoop install vcredist2022
scoop uninstall vcredist2022
```

vcredist2022をinstallしないとiex.batが動作しませんでした。

```
'vcredist2022' (14.42.34433.0) was installed successfully!
Notes
-----
You can now remove this installer with 'scoop uninstall vcredist2022'
```

vcredist2022をインストールした後、uninstallできるというメッセージが表示されたのでuninstallしました。
uninstallしてもiex.batは使えました。謎ですが、こういうものらしいです。

iexと入力するとpowershellのiexコマンドが動作してしまいます。iex.batで起動できました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/532235/0ce45002-535f-e376-8d45-96e7546f526d.png)

ついでにPythonもインストールしておきます。

```
scoop install python
```

vscodeもscoopでインストールできるようですが、自動的にアップデートされないようだったので、vscodeは普通にユーザインストールすることにしました。

# まとめ

- scoopを使ってみた
- シェルからのコマンドでgitも含めて必要なアプリをインストールできた
メッセージ認証コード(MAC)


下記の暗号化技術に関する記事の続きです。
- [共通鍵暗号](https://qiita.com/tom_S/items/e5c6e183064354fee8b2)
- [公開鍵暗号](https://qiita.com/tom_S/items/e29f4cc3efb6bef40daf)
- [一方向ハッシュ関数](https://qiita.com/tom_S/items/bd0d8dfd808be41edb60)

今回はメッセージ認証コード(MAC)について説明します。

# メッセージ認証コード(MAC)とは

メッセージ認証コードは、送られたデータの正真性(完全性)を保つためのもので、SWIFT、IPsec、SSL/TLSで利用されています。メッセージ認証コードはmessage authentication codeの頭文字を取って、MACとも呼ばれます。
具体的な使われ方は下記のようになります。

![メッセージ認証コード.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266944/f98483f9-bb6c-7663-b096-97c92ba9be04.png)

使い方としては一方向ハッシュ関数とよく似ていますが、メッセージ認証コードは一方向ハッシュ関数の代わりに共有鍵を使用します。ただ、[共通鍵暗号の記事](https://qiita.com/tom_S/items/e5c6e183064354fee8b2)にも書いたように、共通鍵を使うため、鍵配送問題が発生します。
メッセージ認証コードとは、鍵に依存した一方向ハッシュ関数のようなもの、と理解すればわかりやすいかと思います。

# メッセージ認証コードの実現方法

ここからは、メッセージ認証コードを実現するための方法についていくつか記します。

## HMAC

共有鍵とデータから、一方向ハッシュ関数でメッセージ認証コードを算出する方法です。
使用する一方向ハッシュ関数は選ぶことができますが、SHA-2(SHA-256, SHA-384, SHA-512)を使用する場合が多いです。また、使用する一方向ハッシュ関数を付与した形で、「HMAC-SHA-256、 HMAC-SHA-384、 HMAC-SHA-512」とも呼ばれます。
メッセージ認証コードの算出方法としては、共有鍵が適切なブロック長になるように0をパディングし、一方向ハッシュ関数によって変換を行います。

## AES-CMAC

AESのブロック暗号で暗号化し、最終ブロックをメッセージ認証コードとする方法です。

# メッセージ認証コードへの攻撃

メッセージ認証コードに対する攻撃としては、再生攻撃が考えられます。
再生攻撃とは、一度だけ送られた正常な送信データを、繰り返して送信するような行為のことです。例えば、送金時のメッセージを何度も繰り返すことにより、実害となる危険性があります。
シーケンス番号をつけたり、タイムスタンプをつけるなどの対策や、ノンスによる一時的な鍵をつけることで対策を行う必要があります。


# 参考文献

- 暗号技術入門 秘密の国のアリス(書籍)
https://cr.hyuki.net/
アジャイル開発：VSMとは？

# はじめに
アジャイル開発におけるVSM（Value Stream Management）は、顧客に対して真の価値を提供するための重要な手法です。

## VSMとは

VSMは「Value Stream Management」の略で、アジャイル開発の効果を最大化し、顧客への価値提供を管理する手法です。アジャイル開発が目的化してしまい、顧客へのアウトカムが不明確になるという課題を解決するために注目されています。

## VSMの主な特徴

1. **価値の定義**: まず、ステークホルダー間で「価値」を明確に定義します。
2. **フローの可視化**: 開発プロセス全体を可視化し、価値を生み出す流れを明確にします。
3. **データドリブン**: 改善のためにデータによる可視化と透明性を重視します。
4. **継続的改善**: データをリアルタイムに計測し、迅速に対策を講じます。

## VSMの利点

- 顧客への価値提供を最大化できます。
- ボトルネックや無駄な作業を特定し、効率化できます。
- チーム間のコラボレーションが促進されます。
- 市場の変化に迅速に対応できる組織を形成できます。

## VSM導入の具体的なステップ
顧客価値を中心に据え、データに基づいて継続的に改善することで、真に価値あるプロダクトを迅速に提供することができます。

VSMを導入する際の具体的なステップは以下の通りです:

1. **準備段階**
   - チーム編成: 多機能チームを組織し、関係者全員を巻き込みます。
   - 小規模プロジェクトの選定: 初めは特定のチームや業務プロセスなど、<INS>**規模の小さいプロジェクトから始めます**</INS>。

2. **現状分析**
   - データ収集: リードタイム、サイクルタイム、在庫などの重要指標を収集します。
   - 現状マップの作成: 現在のプロセスを詳細に描写し、すべての工程を可視化します。

3. **改善点の特定**
   - 分析と識別: 収集したデータを基に、ムダやボトルネックを特定します。
   - 課題の洗い出し: 長い作業時間や多数の待ち時間が発生している部分、手戻り率が高い工程を探します。
   - 目標設定: 具体的な改善目標を明確にし、具体的な数値で定量的に表現し、優先順位をつけます。

:::note info
指標と優先順位付け

①具体的な指標の設定: 
　価値を測定可能な形で定義します。
　例えば、「顧客満足度スコアを20%向上させる」や「市場投入までの時間を30%短縮する」などの具体的な目標を設定します。

②優先順位付け: 
　定義された価値の中で、最も重要なものから順に優先順位をつけます。
　これにより、リソースの効果的な配分が可能になります。
:::

 
4. **未来の状態設計**
   - 理想のフローを描く: ボトルネックを解消し、価値を最大化する理想的なプロセスを設計します。
   - 改善策の立案: 特定された課題に対する解決策を考えます。

5. **実行とフィードバック**
   - 実行計画の策定: 具体的なアクションプランを作成し、担当者を明確にします。
   - 改善の実施: 計画に基づいて改善策を実行します。
   - 評価と改善: 定期的に成果を評価し、必要に応じてプロセスを最適化します。

これらのステップを通じて、VSMを効果的に導入し、継続的な改善活動を支援することができます。

# VSMを導入する際の課題
VSMを導入する際の主な課題は以下の通りです:

1. **全体像の把握の困難さ**
   - 膨大な会社全体のプロセスを洗い出す際のスペース不足
   - エクセルでは全体を俯瞰しづらい問題
   - ホワイトボードでは多くの関係者との同期が困難

2. **組織的な課題**
   - リーダーシップのサポート不足
   - 関係者全員の巻き込みの難しさ

3. **プロセスの複雑さ**
   - 生産プロセスのボトルネックの特定と改善
   - 確立されたプロセスの修正に伴う時間と費用

4. **データの活用**
   - 改善に必要なデータの収集と分析
   - データドリブンな意思決定への移行

5. **価値の定義と共有**
   - ステークホルダー間での「価値」の定義の難しさ
   - 顧客視点での価値の特定と優先順位付け

6. **継続的な改善の実施**
   - 定期的な評価と改善サイクルの維持
   - 長期的なロードマップの設定と実行

これらの課題に対処するためには、小規模なプロジェクトから始め、適切なツールを活用し、関係者全員の協力を得ながら、段階的にVSMを導入していくことが重要です。

# VSMの長期的な取り組みを維持するためには？
1. **継続的な教育とスキル開発**
   - 体系的な研修プログラムを整備し、従業員のスキルを常に向上させます。
   - 外部資格取得を奨励し、専門知識の深化を促進します。

2. **アジャイルな改善サイクル**
   - 短期間でのPDCAサイクルを実施し、迅速な改善を可能にします。
   - フィードバックループを強化し、常に改善点を見出します。

3. **組織文化の醸成**
   - 失敗を許容する文化（フェイルファスト）を育成し、イノベーションを促進します。
   - 定期的な成果発表会や改善事例コンテストを開催し、モチベーションを維持します。

4. **データドリブンな意思決定**
   - VSMツールを活用し、Value StreamのEnd to Endでのリアルタイムな可視化を実現します。
   - メトリクスを継続的に分析し、ビジネスバリューの最適化を図ります。

5. **長期的な視点の維持**
   - ESG（環境・社会・ガバナンス）視点を導入し、持続可能性を考慮します。
   - SDGs（持続可能な開発目標）との整合性を確保し、社会的価値創造を追求します。

6. **定期的な見直しと最適化**
   - 長年にわたり、パイプラインの最適化を支援し、新たな無駄を生み出さないようにします。
   - 適切なデータを基に、人員増員や新技術導入のタイミングを判断します。

これらの戦略を組み合わせることで、VSMの長期的な取り組みを効果的に維持し、継続的な改善と価値創造を実現できます。

# まとめ
VSMは様々な関係者を巻き込む必要があるので、ハッキリ言って時間が掛かります。
また、細かい改善活動を後ろ回しにしている中長期的なプロジェクトこそ、VSMをやる価値があります。

ただし、往々にしてあるのがVSMの最初のステップで開発プロセス全体を可視化して苦労して疲れ果てて終わることもありますが、それでは意味がありません笑 

最後のステップである<font color="red">**継続的に改善活動をすること**</font>に意義があります:smirk_cat: 
もちろん、改善活動の方が何倍も大変ですが、長期的な目線で考えればこの活動は実施し続けないと負債となりリードタイム増大に繋がってしまうので非常に重要な活動となります

※何よりも開発メンバが疲弊し続けることにもなるし、モチベーション低下にもなるので実施すべきですね。

以上
弱小プログラマー、個人開発1年勝負：初めてのモバイルアプリ

## アイデア探し
前回の更新から1カ月弱が経過しましたが、着実にチャレンジを続けています。前回計画していたアフィリエイトを活用したWebサイトは断念し、新しい方向性を模索することになりました。そこで、IndieHackerとProduct Huntを中心にアイデアを探ることにしました。Product Huntは現段階では挑戦のハードルが高いと判断しましたが、IndieHackerからはいろんな有用な記事やアイデアなどありました。

## 家庭菜園アプリへの着手
色々とアイデアをもんだ結果、結局私の趣味である家庭菜園に関連したiOSアプリの開発に着手することを決めました。しかし、開発を2週間ほど進めたところで、プロジェクトの規模が徐々に拡大し、モバイルアプリの範囲を超える状況となりました。その上、需要不明なのにもかかわらず、実装自体に悩んだり、マーケテイングなどを考え始めると、このまま行くと前回と同じ轍（時間だけかけて何も成果がない）を踏みそうになったので、一旦中断を決断しました。

## シンプルなiosアプリ開発へ方針変換
そこで、iOSアプリ開発が初挑戦であることを考慮し、まずはシンプルなアプリをApp Storeに公開することを目標に設定し直しました。
また、規模の拡大を防ぐため、今回は2週間という具体的な期限を設け、レーダーチャート作成アプリという明確な機能に絞って開発を行いました。

このアプリでは、以下のスクリーンショットの通り、好きな食べ物や自己評価をレーダーチャートとして保存・エクスポートできます。

![Frame 1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3987263/504b5ab1-18ee-bb38-df47-1ae7f0b17cd6.png)

https://apps.apple.com/jp/app/%E3%83%AC%E3%83%BC%E3%83%80%E3%83%BC%E3%83%81%E3%83%A3%E3%83%BC%E3%83%88-%E7%B0%A1%E5%8D%98%E4%BD%9C%E6%88%90-%E3%82%A8%E3%82%AF%E3%82%B9%E3%83%9D%E3%83%BC%E3%83%88/id6741140710

武士の情けで使ってみてください（泣）デザインは微妙かもしれませんが、使い勝手はよいように作りました。もし使ってもらって何か指摘とかいただけるととてもうれしいです。。。

ひとまず、今後は2週間を目安に、小規模なアプリを継続的にリリースしていく形でチャレンジを続けていきます。
まだまだ糸口はつかめていませんが、少しづつ進めていって、その経験を発信できたらと思います。
【Python】Noneオブジェクト

## 概要
PythonのNoneは「値が存在しない」ことを明示する特殊なオブジェクトです。
他のプログラミング言語のnull/nilに相当し、変数の初期化や関数の戻り値として
広く利用されます。

## 特徴
* Noneのデータ型は**NonType型**です。
    ```python:python
    a = None
    print(a)
    >>>
    None
    
    print(type(a))
    >>>
    <class 'NoneType'>
    ```
* NoneはFalse(偽)として判定されます。
    ```python:python
    if None:
        print("True")
    else:
        print("False")
    >>>
    False
    ```


## 使用用途
1. 変数の初期化
    未設定状態を明確に表示する。
    ```python:python
    user_data = None # 後でデータが代入される
    ```

1. 関数の戻り値
    処理結果がない場合の戻り値として設定する
    ```python:python
    def fetch_data(data):
    # データ取得処理
        return data if data > 10 else None

    print(fetch_data(0))
    >>>
    None
    ```

1. 関数のデフォルト引数
    引数がない場合の判定が容易になる
    ```python:python
    def feb(num,L=None):
        if L is None:
            L = []
        L.append(num)
        return L
    ```

## 気をつけること
1. 比較演算子はisを使う
    ==だと結果が誤ることがある。
    ```python:python
    class Example:
    def __eq__(self, other):
        # すべての比較をTrueとする
        return True

    x =  Example()
    if x == None:
        print("xはNoneです")
    else:
        print("xはNoneではありません")
    >>>
    xはNoneです

    y =  Example()
    if y is None:
        print("yはNoneです")
    else:
        print("yはNoneではありません")
    >>>
    yはNoneではありません
  
    ```


1. 関数のデフォルト引数
    ```python:python
    def feb(n,L=[]):
        L.append(n)
        return L
    
    print(feb(1))
    print(feb(2))
    print(feb(3))
    >>>
    [1]
    [1,2]
    [1,2,3]

    def feb_2(n,L=None):
        if L is None:
            L = []
        L.append(n)
        return L
    
    print(feb_2(1))
    print(feb_2(2))
    print(feb_2(3))
    >>>
    [1]
    [2]
    [3]
    ```
Snowflakeでのセキュリティ キー管理について

# 1.キー管理について

Snowflakeでは階層構造のキー管理を行なっております。
マネージドサービスのため、ユーザーが直接意識しておくことではないですが、
最低限、開発者側はどのような設計でSnowflakeが暗号化されているかはクライアント側から説明を求められた場合などで、理解しておいた方が良いかと思います。

 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2526914/8345453d-9f72-410e-c7f7-c540b5b99eb7.png)

上図のファイルキーが仮に、漏洩してしまった場合でも、
階層構造で管理している以上、他のファイルを参照されることはありません。

逆に言えば、ルートキーが漏洩してしまった場合は、大きなセキュリティインシデントになります。

ルートキーについては、AWSをプロバイダーとして設定している場合は、
Cloud HSMというものを使用して、キー管理を行なっております。

KMSなど、他のキー管理サービスなどはありますが、より厳格に管理を行いたい場合などは、
HSMを選定するというのが一般的です。

そもそもHSMというものは、
ハードウェアセキュリティモジュールの役割であり、
鍵を守るためだけの専用のハードウェアで管理されているものになります。
詳しくは以下のリンクから詳しく知ることができます。

https://www.sarion.co.jp/ref/ref_hsm_primer_sec1.html#:~:text=HSM(Hardware%20Security%20Module)%E3%81%A8,%E3%82%92%E3%81%99%E3%82%8B%E3%83%8F%E3%83%BC%E3%83%89%E3%82%A6%E3%82%A7%E3%82%A2%E3%81%A7%E3%81%99%E3%80%82&text=%E3%81%9D%E3%81%93%E3%81%A7%E3%80%81%E9%8D%B5%E3%82%92%E4%BF%9D%E7%AE%A1%E3%81%99%E3%82%8B,%E3%81%8C%E5%82%99%E3%81%88%E3%82%89%E3%82%8C%E3%81%A6%E3%81%84%E3%81%BE%E3%81%99%E3%80%82


# 2. E2EE Encryption
エンドツーエンドエンクリプションの略語であり、Snowflake内部の暗号化は
マネージドサービスであるので、直接意識する必要はありませんが、
クライアントマシンからSnowflakeの内部ステージなどにデータ転送する場合や
クライアントマシンから自身のデータレイク（S３など）を外部ステージなどにする場合、

当然ですが、データ転送する上では、その転送自体にもどのように暗号化されているのかは理解しておく必要があります。

全体を通して、データを暗号化させるという意味で
E2EEという考え方があります。

外部ステージなどにする場合
開発側で、暗号化オプションを選択することができます。

内部ステージなどにクライアントマシンからPUTを行う場合、
Snowflake内部で自動的に暗号化されます。

```sql
-- create encrypted stage
create stage encrypted_customer_stage
url='s3://customer-bucket/data/'
credentials=(AWS_KEY_ID='ABCDEFGH' AWS_SECRET_KEY='12345678')
encryption=(MASTER_KEY='eSxX...=');
```

https://docs.snowflake.com/ja/user-guide/security-encryption-end-to-end
virtualboxのvmdk移動したらUUID already exists


2025/02/07
単発ネタ。virtualbox小ネタ

## 状況
- 初期設定でvirtualbox使ってたらCドライブ容量枯渇
- Dドライブへvmdk移動すれば良いかな　→　コピーしてディスク変更しようとすると、、
- 「Cannot register the hard disk because a hard disk with UUID already exists」

```
C:\Users\user>ver
Microsoft Windows [Version 10.0.19045.5247]

C:\Users\user>vboxmanage -v
7.0.12r159484
```

## 事象対処
- 案１　コピー先UUID変更「vboxmanage internalcommands sethduuid D:\your\path\diskname.vmdk」
  - 複数同時にやるならDドライブのコピー先へ移動してforで一括処理
  cd /d D:\your\path\
  for /r %i in (*.vmdk) do vboxmanage internalcommands sethduuid %i
- 案２　先にvirtualboxマネージャ上からコピー元ディスク削除でも対処可。
　→仮想マシンの設定から一度旧ディスクのストレージコントローラ除去。
　→「vboxmanage closemedium disk <uuid> --delete」
　→ストレージコントローラにコピー先のディスクを追加して起動確認。
　＃万が一の場合に後戻りできることを考えて、案１のUUID変更手順の方が良いと思う。

## 根本対処
- virtualbox導入時にvm配置場所をDドライブとかへ変更しておく。

以上
【Web】特定の要素へスクロールさせる方法まとめ

# はじめに
今年に入ってWebの開発を進めています。
その際、画面を特定の要素までスクロールさせるために少々調べたので、今回は備忘録的に実装方法をまとめておこうと思います。

# 実装方法
まず、実装の全体像は以下になります。

```page.tsx
import { useRef } from "react";

export default function Page() {
  const testRefs = useRef<(HTMLDivElement | null)[]>([]); 

  const testList = [
    { id: 1, value: "テストテスト" },
    { id: 2, value: "テストテスト" },
    { id: 3, value: "テストテスト" },
  ]; 

  const handleSelectItem = (index: number) => {
    if (skillRefs.current[index]) {
      skillRefs.current[index]?.scrollIntoView({ behavior: "smooth", block: "start" });
    }
  };

  return (
    <div>
      {testList.map((item, index) => (
        <div key={item.id} ref={(el) => { testRefs.current[index] = el; }}>
          <TestComponent 
              item={item} 
              onSelectItem={(index) => handleSelectItem(index)
          />
        </div>
      ))}
    </div>
  );
}
```
実装の全体像としては以上です。
重要なのは主に2つになります。

1. `useRef` を使って各 TestComponent の要素を配列で管理
2. `scrollIntoView`関数を使用し、受け取ったindexに該当する要素へスクロール

たったこれだけでWebでは綺麗にスクロールしてくれます、非常に簡単で便利ですね。

ちなみにscrollIntoView関数に渡すbehaviorとblockには他にも以下値を設定可能です。

`behavior`（スクロールの動き）
| 値        | 動作                        | 特徴                                       |
|-----------|-----------------------------|--------------------------------------------|
| `auto`    | デフォルトのスクロール動作   | ブラウザの標準動作（通常 `instant`）       |
| `smooth`  | スムーズスクロール           | なめらかにスクロールする |
| `instant` | 一瞬でスクロール（瞬間移動） | 瞬時に目的の位置へスクロール（アニメーションなし）  |

`block`（要素の表示位置）
| 値        | 動作                        | スクロール後の表示位置                     |
|-----------|-----------------------------|--------------------------------------------|
| `start`   | 要素の先頭をスクロール先に   | **画面の一番上** に要素が来る              |
| `center`  | 要素を中央に配置             | **画面の中央** に要素が来る                |
| `end`     | 要素の末尾をスクロール先に   | **画面の下部** に要素が来る                |
| `nearest` | 近い位置へスクロール         | **現在のスクロール位置から最小の移動量** で調整 |

以上になります。
オートスクロール実装時の参考にしてもらえればと思います。

# さいごに
順調に減量中です、引き続き健康管理に気合いを入れつつWebの実装も進めていきます！
「IT技術」と「デジタル技術」の違いとは？

現代社会において、IT技術とデジタル技術という言葉はよく耳にします。しかし、これらは似ているようでいて、その役割や対象範囲には明確な違いがあります。今回は、エンドユーザーの目に直接触れるかどうかという観点から、両者の違いについて直感的に理解しやすく整理してみました。
※この記事は、ChatGPTの出力を基に作成しています。

---

## IT技術：見えない基盤の重要性

**IT技術（Information Technology）**とは、主に企業や組織の内部で情報を管理・処理するための「インフラ」や基盤部分を担う技術です。具体的には、以下のようなシステムが含まれます。

- **コンピューターシステム・サーバー**  
- **ネットワークインフラ**  
- **データベース管理システム**  
- **セキュリティ対策などの基盤技術**

これらは、エンドユーザーが直接目にすることは少なく、「裏側」でしっかりと機能することで、私たちが日々利用するサービスを支えています。

---

## デジタル技術：目に見える革新のサービス

一方、**デジタル技術**は、IT技術という基盤の上に構築される、エンドユーザーが実際に触れるサービスや製品を指します。例えば、次のようなものがあります。

- **スマートフォンアプリ**  
- **クラウドサービス**  
- **Webサイトやオンラインショッピング**  
- **IoTデバイス、スマート家電**  
- **AIを活用したサービス**

これらのデジタル技術は、日常生活やビジネスにおいて直接的な利便性や新しい体験を提供し、私たちの生活を豊かにする役割を果たしています。

---

## 例え話で理解する―建物の基礎とスマート設備

より直感的に理解するために、以下のような例え話が有効です。

- **IT技術は「建物の土台や配管」**  
  建物を建てる際、まずはしっかりした基礎や電気・水道などのインフラが必要です。IT技術もこれと同様に、情報システムの「基盤」として、企業内のデータ管理や通信を支える役割を担っています。

- **デジタル技術は「建物をスマートにする最新の設備」**  
  土台が整った建物に、最新のスマート家電やセキュリティシステム、快適な生活を実現する設備を導入することで、建物全体がより魅力的で便利になります。同じように、デジタル技術はIT基盤の上に新たなサービスや機能を追加し、ユーザーに直接価値を提供します。

---

## まとめ

- **IT技術**  
  → **役割**: 情報の収集、処理、管理、伝達を行うための見えないインフラ。  
  → **例**: 社内システム、サーバー、ネットワーク、データベースなど。

- **デジタル技術**  
  → **役割**: IT技術を基盤にして、エンドユーザーが実際に利用できる革新的なサービスや製品を提供。  
  → **例**: スマートフォンアプリ、クラウドサービス、IoTデバイス、Webサービスなど。

このように、**IT技術は「目に見えない基盤」であり、デジタル技術はその上に築かれる「目に見えるサービスや製品」として捉えることができます。**  
現代のデジタルトランスフォーメーション（DX）を推進する上で、どちらの技術も欠かせない要素であり、互いに補完し合う関係にあると言えるでしょう。
Mayaの作業環境をプロジェクトで統一する

## 環境

* Windows
* Autodesk Maya 2025.3


## 作業環境の統一

ビューポートやフレームレートのように、プロジェクトごとに指定が決まっているものはユーザーに個別で設定させるのではなく、プロジェクトのスタートアップスクリプトで自動設定させるようにすると設定ミスやプリファレンスのリセットによるトラブルを防止できるため、おすすめです。


###  スタートアップスクリプト

Mayaでスタートアップスクリプトを作成するのは簡単で、melスクリプトとして作成したい場合は ```userSetup.mel```、Pythonファイルとして作成したい場合は ```userSetup.py``` というファイル名で保存するだけです。

上記のファイル名のスクリプトは常にMaya起動時に実行されるため、このファイルを利用してプロジェクトの作業環境を統一することができます。

また、Mayaではmelスクリプトのディレクトリは環境変数 ```MAYA_SCRIPT_PATH```、Pythonスクリプトのディレクトリは ```PYTHONPATH``` で指定可能です。

実際のプロジェクトではMaya起動前に環境変数で使用するディレクトリをセットしてスタートアップスクリプトを読み込ませるのが良いかと思います。


### userSetup.pyの例

```python:userSetup.py
from maya import cmds


def set_preferences() -> None:
    cmds.optionVar(
        cat="PROJECT NAME",
        sv=[
            ["vp2RenderingEngine", "DirectX11"],
            ["workingUnitTimeDefault", "120fps"],
            ["workingUnitTime", "120fps"],
        ],
    )


if __name__ == "__main__":
    set_preferences()
```

プリファレンスの多くの設定は　```optionVar``` コマンドで設定できます。このコマンドにはカテゴリを設定するためのオプションとして ```category(cat)``` があります。こちらにプロジェクト名などを指定しておけばプリファレンスファイル内で1つのカテゴリとしてまとめられるため、デバッグも行いやすくなります。


### プリファレンスファイル

Maya 2025 のプリファレンスはWindows環境での既定の場所として ``` %USERPROFILE%\Documents\maya\2025\prefs ``` に保存されています。
例えば、Mayaのプリファレンスウィンドウで設定できる多くの項目は ```userPrefs.mel``` に保存されており、拡張子でもわかるように中身はmelスクリプトの形式で記述されています。


### スプラッシュスクリーンの変更

Maya 2025 の起動時に表示されるスプラッシュスクリーンの画像はWindowsの既定では
```C:\Program Files\Autodesk\Maya2025\icons\MayaStartupImage.png``` が表示されます。

スプラッシュスクリーンの画像は環境変数 ```XBMLANGPATH``` で指定した場所に同名のファイル ```MayaStartupImage.png``` を配置することで変更が可能です。

こちらを設定しておくと視覚的に退屈させないだけでなく、現在読み込まれている設定がプロジェクトのものかユーザーが判断しやすくなります。

## 参考

* [Maya の起動時に MEL コマンドを実行する](https://help.autodesk.com/view/MAYAUL/2025/JPN//?guid=GUID-F3D60949-2372-47F5-B8D6-78D73F78D587)
* [プリファレンス(Preferences)ウィンドウ](https://help.autodesk.com/view/MAYAUL/2025/JPN//?guid=GUID-A62CF59A-3600-4709-A3B7-0F2DB83EC829)
* [optionVar](https://help.autodesk.com/view/MAYAUL/2025/JPN//?guid=__CommandsPython_index_html)
アジャイル開発：USM

# はじめに
アジャイル開発におけるユーザーストーリーマッピング（以下USM）について学んだ結果を記事にまとめてみる。

# USMとは？
アジャイル開発におけるユーザーストーリーマッピングは、製品開発を効果的に進めるための強力なツールです。

ユーザーストーリーマッピングとは、ユーザーの視点に立って製品の機能や要件を整理し、視覚化する手法です。これにより、開発チーム全体が製品のビジョンを共有し、ユーザーのニーズに焦点を当てた開発を行うことができます。

## ユーザーストーリーマッピングの基本要素

1. **ユーザーストーリー**: ユーザーの目標や要望を短い文で表現します。
2. **ビジュアルマップ**: ユーザーの行動、タスク、目標を視覚的に表現します。
3. **優先順位付け**: ストーリーに優先順位をつけ、開発の順序を決定します。

## ユーザーストーリーマッピングの進め方

### 1. ペルソナの設定

まず、製品を使用する具体的なユーザー像（ペルソナ）を設定します。例えば、「30代の営業マン」や「大学生の女性」など、具体的な属性を持つユーザーを想定します。

### 2. ユーザーの行動を時系列で整理

次に、ペルソナの行動を時系列に沿って整理します。例えば、「アプリを開く」→「商品を検索する」→「商品を選択する」→「購入する」といった具合です。

### 3. ユーザーストーリーの作成

各行動に対して、ユーザーストーリーを作成します。形式は以下のようになります：

「〜として、〜したい。なぜなら〜だからだ。」

例：「営業マンとして、顧客情報を素早く検索したい。なぜなら、商談の直前に情報を確認する必要があるからだ。」

### 4. 機能のマッピング

ユーザーストーリーに対応する機能や製品の要素をマッピングします。これにより、各機能がどのユーザーニーズを満たすのかが明確になります。

### 5. 優先順位付けとスプリント計画

最後に、マッピングした機能に優先順位をつけ、スプリント（短期の開発サイクル）の計画を立てます。

## ユーザーストーリーマッピングの利点

1. **ユーザー中心の開発**: 常にユーザーのニーズを念頭に置いた開発が可能になります。
2. **全体像の把握**: 製品の機能や要件の全体像を視覚的に理解できます。
3. **コミュニケーションの促進**: チーム内でのコミュニケーションが活性化し、共通理解が深まります。
4. **柔軟な計画変更**: アジャイル開発の特性を活かし、優先順位の変更や新たな要件の追加が容易になります。

ユーザーストーリーマッピングを活用することで、開発チームは常にユーザーの視点を忘れず、価値のある製品を効率的に開発することができます。

# ユーザーストーリーマッピングの具体的なステップ

1. ペルソナの設定
   - 製品やサービスを利用する具体的なユーザー像を作成します。
   - 性別、年齢、職業、趣味、行動パターン、生活スタイルなどを詳細に定義します。

2. ユーザーの行動を時系列で整理
   - ペルソナの大まかな行動を時系列に沿って並べます。
   - 必要に応じて、大きな行動を詳細なステップに分解します。

3. ユーザーストーリーの作成
   - 各行動や詳細なステップに対して、具体的なユーザーストーリーを作成します。
   - 「〜として、〜したい。なぜなら〜だからだ。」という形式で記述します。

4. 機能やプロダクトのマッピング
   - ユーザーの各行動に対して、必要な機能やプロダクトをマッピングします。
   - ユーザーに提供する価値や解決すべき課題を考慮しながら、アイデアを自由に書き出します。

5. 優先順位付け
   - マッピングした機能やアイデアに優先順位をつけます。
   - プロジェクトの目標や重要度に基づいて順位を決定します。

6. リリース計画の策定
   - 優先度の高い機能から開発を開始し、段階的なリリース計画を立てます。
   - プロジェクトの進行に合わせて、マップを定期的に見直し、更新します。

これらのステップを通じて、開発チーム全体がプロダクトのビジョンを共有し、ユーザー中心の開発を行うことができます。ワークショップ形式で進めることで、チームメンバー間のコミュニケーションも促進されます。

# 具体例
最後に、ユーザーストーリーマッピング（USM）の具体例を記載していきます。
この例では、オンラインショッピングアプリケーションを想定します。

## ペルソナ設定

30代の会社員、スティーブさん。忙しい日々を送っており、効率的に買い物をしたいと考えています。

## ユーザーの行動（横軸）

1. アプリを開く
2. 商品を探す
3. 商品を選択する
4. カートに追加する
5. 注文を確定する
6. 支払いを行う
7. 配送状況を確認する

## ユーザーストーリーと機能（縦軸）

1. アプリを開く
   - ユーザーストーリー: アプリにすぐにアクセスしたい
   - 機能: クイックログイン、指紋認証

2. 商品を探す
   - ユーザーストーリー: 欲しい商品を素早く見つけたい
   - 機能: 検索機能、カテゴリ分類、おすすめ商品表示

3. 商品を選択する
   - ユーザーストーリー: 商品の詳細情報を確認したい
   - 機能: 商品詳細ページ、レビュー表示、関連商品推奨

4. カートに追加する
   - ユーザーストーリー: 複数の商品をまとめて購入したい
   - 機能: カート機能、数量変更、保存機能

5. 注文を確定する
   - ユーザーストーリー: 注文内容を最終確認したい
   - 機能: 注文確認画面、配送先選択、クーポン適用

6. 支払いを行う
   - ユーザーストーリー: 安全かつ簡単に支払いたい
   - 機能: 複数の支払い方法、セキュアな決済システム

7. 配送状況を確認する
   - ユーザーストーリー: 商品がいつ届くか知りたい
   - 機能: リアルタイム配送追跡、通知機能

このマッピングを通じて、開発チームは顧客体験の全体像を把握し、優先順位を決定できます。
例えば、初期リリースでは基本的な購入フローに焦点を当て、その後のアップデートで配送追跡機能を追加するなど、段階的な開発計画を立てることができます。


以上
CORSを完全に理解したい

clientが `Origin` を送らないと server が `Access-Control-Allow-Origin` を返してくれないパターンでハマった


## CORS に関わるヘッダー（クライアント側とサーバー側）

| ヘッダー | クライアント側 | サーバー側 | 説明 |
|---|---|---|---|
| **Origin** | 必須 | 必須 | リクエストの送信元 (スキーム、ドメイン、ポート) を示す。 |
| **Access-Control-Request-Method** | preflighted request の場合 | - | 許可を求める HTTP メソッド (例: GET, POST, PUT, DELETE)。 |
| **Access-Control-Request-Headers** | preflighted request の場合 | - | 許可を求めるカスタムヘッダー。 |
| **Access-Control-Allow-Origin** | - | 必須 | アクセスを許可する送信元 (Origin) を示す。`*` で全ての送信元を許可することも可能。 |
| **Access-Control-Allow-Methods** | - | preflighted request の場合 | 許可する HTTP メソッド (例: GET, POST, PUT, DELETE)。 |
| **Access-Control-Allow-Headers** | - | preflighted request の場合 | 許可するカスタムヘッダー。 |
| **Access-Control-Allow-Credentials** | - | - | `true` の場合、Cookie や Authorization ヘッダーを送信可能にする。 |
| **Access-Control-Expose-Headers** | - | - | クライアントがアクセスできるレスポンスヘッダーを指定する。 |
| **Access-Control-Max-Age** | - | preflighted request の場合 | preflighted request の結果をキャッシュする時間 (秒)。 |

## 補足

* **preflighted request**: POST メソッドで Content-Type が `application/x-www-form-urlencoded`、`multipart/form-data`、`text/plain` 以外の場合や、カスタムヘッダーを含むリクエストは、事前にサーバーに許可を求める preflighted request となる。
* **単純リクエスト**: GET、HEAD、POST メソッドで Content-Type が `application/x-www-form-urlencoded`、`multipart/form-data`、`text/plain` のいずれかで、カスタムヘッダーを含まないリクエストは、単純リクエストとなり、preflighted request は発生しない。

## 注意点

* CORS の設定はサーバー側で行う必要があります。
* クライアント側は、CORS に対応したブラウザを使用していれば、特別な設定は不要です。
* セキュリティ上の理由から、`Access-Control-Allow-Origin: *` は本番環境では避けるべきです。

上記以外にも、CORS に関連するヘッダーは存在しますが、主要なものは上記で網羅されています。

より詳しい情報が必要な場合は、MDN のドキュメントなどを参照してください。

* MDN: Cross-Origin Resource Sharing (CORS) [https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS)

ご不明な点があれば、お気軽にご質問ください。
「忘備録」Node.js インストール手順(win11)　

Node.jsのインストールに際して，行った事を手順として以下に記述した．

#### 用語集
インストールに際して，不明な単語と想定される用語を羅列した．

npm
    Node.jsのパッケージマネージャ，依存関係の管理を行う．
    
nvm
    Node Version Manager  Node.jsのパージョンを管理する為に用いる．

package.json
    nmpによってパッケージ情報が書き込まれる

#### Node.js本体のインストール
以下より，LTS版をインストールした．
Node.jsをインストールするとnpmが付属する．
https://nodejs.org/ja



#### nvmのインストール
nvm for windowsを以下のサイトからインストール
"nvm-setup.exe"と記述がある
https://github.com/coreybutler/nvm-windows/releases



#### Node.jsの実行方法
windowsだと，Powershellを用いる事になる．

以下のコマンドで確認をする

```
node -v
npm -v
nvm version
```
【メモ帳アプリ作成①】XIBファイルを使って画面遷移ができるようになったのでメモ

メモ帳アプリをMain.storyboradとSenceDelegateを使わず作ろうとしています。
XIBを使った画面遷移がうまくいかなくて苦戦していましたが、
やっとエラーを潰せたので
メモ代わりにソースコードを載せようと思います。

①ViewController.swift
・メモの一覧表を載せます(未実装、後日追加する)
・ナビゲーションコントローラーに
　　　・タイトル「メモ一覧」を表示
　　　・＋ボタン（Addボタン）を追加
・＋ボタンを押したら画面遷移（メモの新規作成画面に移動）

```:ViewController.swift
import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var Label: UILabel!
    //**テスト用に表示させてるだけ**//

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white  // 背景色を設定（確認用）

        // ナビゲーションバーのタイトルを設定
        title = "メモ一覧"

        // ナビゲーションバーに「+」ボタンを追加
        navigationItem.rightBarButtonItem = UIBarButtonItem(
            barButtonSystemItem: .add,
            target: self,
            action: #selector(addMemo)
        )
    }
    @objc func addMemo() {
        let addMemoVC = AddMemoViewController()
        navigationController?.pushViewController(addMemoVC, animated: true)
    }
  
}
``` 

②AddMemoViewController.swift（メモの新規作成画面）
・ViewController.swiftの＋ボタンを押したらここに画面遷移

```:AddMemoViewController.swift
import UIKit

class AddMemoViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white // 背景色を設定（確認用）
    }
    
    init() {
        super.init(nibName: "AddMemoViewController", bundle: nil)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}
```

まだ何の処理もできていませんが、画面遷移できたのが嬉しくてメモ代わりに
この記事を書きました。

 あとはメモの詳細画面を表示させたり、
 設定画面を表示させたりしたいので
 今度は複数の画面を扱えるようにがんばります。
 とりあえず1画面分、画面遷移できるようになった！
Codewards 7 kyu Find the capitals

7 kyu Find the capitals
https://www.codewars.com/kata/539ee3b6757843632d00026b/python
Write a function that takes a single non-empty string of only lowercase and uppercase ascii letters (word) as its argument, and returns an ordered list containing the indices of all capital (uppercase) letters in the string.

*verbalization
Take word  in order from words.(use for i in words)
Search it it is capital. (use isupper())
Change to ordered list (use index)
Return list numbers (use append())

*Code 
def capitals(words):
    n = []
    for i in words:
        if i.isupper():
            n.append(words.index(i))
    return n        

-> Passed in TEST, but failed at ATTEMPT.
Failed examples: 
 ("sHxbDKysubDswD")
[1, 4, 5, 4, 4] should equal [1, 4, 5, 10, 13]

("bGfaIEiZcjbKrKsWryduimBW")
[1, 4, 5, 7, 11, 11, 15, 22, 15] should equal [1, 4, 5, 7, 11, 13, 15, 22, 23]

Why does this happen? It seems to happen in long sentences..the errors happen at same alphabet that already appeared? But why?

-> The index() method finds the FIRST occurrence of the specified value.
(reference: https://www.w3schools.com/python/ref_string_index.asp)

Use enumerate? So, 
*verbalization
Take word  in order from words and Change to ordered list (use for a, b in enumerate(A, n):)
Search it it is capital. (use isupper())
Return list numbers (use append())

def capitals(words):
    n = []
    for a,b in enumerate(words):
        if b.isupper():
            n.append(a)
    return n
【Supabase】1対1のテーブルのリレーション先のデータをオブジェクト形式で取得する方法

# はじめに

お疲れ様です、りつです。

タイトルの通りなのですが、1対1のリレーションが貼られているテーブルに対して、リレーション先のデータ形式を配列ではなくオブジェクトで取得する方法をまとめました。

文章だとわかりにくいかと思いますので例を示すと、以下のようなイメージです。

- Before
  ```jsonc:
  {
      "user_id": "ritsu",
      "profiles": [ // ここが配列形式の中にオブジェクトとして返却されている
          {
              "user_name": "りつ",
              "avatar_url": null
          }
      ]
  }
  ```
- After
  ```jsonc
  {
      "user_id": "ritsu",
      "profiles": { // オブジェクト形式で返却されている
          "user_name": "りつ",
          "avatar_url": null
      }
  }
  ```

なお、1対多、多対多のテーブル構造の場合の設定については以下の公式ドキュメントに記載があります。

https://supabase.com/docs/guides/database/joins-and-nesting

# 問題

以下の1対1の関係のテーブルが存在している状況で、ユーザー情報（ユーザーID、ユーザー名、アバターのU）の取得を行いたいと考えていました。

### accounts

ユーザーアカウント

| No | PK | FK | カラム名 | 型 | NotNull | デフォルト |
| -- | :-: | :-: | ----- | -- | :----: | -------- |
| 1 | 〇 |  | user_id | varchar | 〇 |  |
| 2 |  |  | password | varchar | 〇 |  |

### profiles

ユーザープロフィール

| No | PK | FK | カラム名 | 型 | NotNull | デフォルト |
| -- | :-: | :-: | ----- | -- | :----: | -------- |
| 1 | 〇 |  | profile_id | uuid | 〇 |  |
| 2 |  | 〇 | user_id | varchar | 〇 |  |
| 3 |  |  | user_name | varchar | 〇 |  |
| 4 |  |  | avatar_url | varchar |  |  |
| 5 |  |  | introduction | text |  |  |

`accounts.user_id`と`profiles.user_id`は外部キーでリレーションを設定しているのですが、以下のソースコードを使用してデータ取得を行ったところ、レスポンスの`data`の`profiles`が配列になっていました。

```react:src/utils/supabaseFunctions.ts
import { User } from '@/domains/user';
import { supabase } from './supabase';

export const fetchUser = async (id: string): Promise<User> => {
  const { data, error } = await supabase.from('accounts').select('user_id, profiles(user_name)').eq('user_id', id).single();

  if (error) {
    throw new Error(error.message);
  }

  return data;
};
```

```json:レスポンスデータ（dataの中身）
{
    "user_id": "ritsu",
    "profiles": [
        {
            "user_name": "りつ",
            "avatar_url": null
        }
    ]
}
```

1対1の関係なので、`profiles`の中身は配列ではなく、オブジェクトで取得できればと思い、設定方法を調べました。

# 解決方法

結論、`profiles.user_id`に対して、ユニークキー制約を設定してあげる必要がありました。

Supabaseの管理画面から設定する方法は以下の通りです。

1. テーブルエディターを開く
2. 対象テーブル名の横にある`・・・` > `Edit table`を選択
![スクリーンショット 2025-02-08 135236.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3911933/6a16ad24-7a9d-189e-d42c-a7d15fd04275.png)
3. `Columns`セクションの対象カラムの右側にある歯車マーク > `Is Unique`にチェックを入れて保存
![スクリーンショット 2025-02-08 135116.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3911933/c84636f9-33bb-94be-e6ab-b3ba21f52c14.png)

上記設定後、以下のようにデータが返却されるようになりました。

```json:レスポンスデータ（dataの中身）
{
    "user_id": "ritsu",
    "profiles": {
        "user_name": "りつ",
        "avatar_url": null
    }
}
```

# おわりに

:::note
**まとめ**
1対1のリレーションテーブルからデータ取得を行う際は、リレーション先テーブルの外部キーにユニークキー制約の設定が行うと、レスポンスデータがシンプルになる
:::

今回のケースの場合、必要な情報（`user_id`、`user_name`、`avatar_url`）は全て`profiles`テーブルから取得可能なため、リレーションで取得する必要性は低いかもしれません。

ただ、今後両テーブルそれぞれからデータの取得が必要になったパターンを見越して1対1のリレーション取得方法を調べてみました。

どなたかのご参考になれば幸いです。

# 参考

https://www.restack.io/docs/supabase-knowledge-one-to-one-vs-one-to-many-supabase
【備忘録】FFmpeg + ImageMagickを使って動画ファイルからGIF画像を作ってみる

# はじめに
SE2年目の天然水♂です。  

仕事で新規参画者向けドキュメントを書いていたときに、「これ、GIFで表現したらわかりやすそうだな」みたいな箇所がありまして、web上にあるGIFを作ってくれるサイトを使わず、できればローカル環境で作成できないかなと思ったのが発端です。  

ChatGPTに聞いたところ、`FFmpeg`と`ImageMagick`を使うことで作れるそう。

備忘録は業務と関係ないので、自分の好きなゲームのワンシーンを使ってGIF画像を作ります。

(……と思ったんだけど、作成したGIFのファイルサイズが35MBで、Qiitaに載せられなかった笑)
とりま、使用させていただいた動画↓
[ラファエル各兵種モデル＆戦闘モーション ファイアーエムブレム 風花雪月](https://youtu.be/TJidW19Cj6g?si=-AlLKXQxAgSqAV-c)

## FFmpegとは？
![スクリーンショット 2025-02-08 12.57.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3932951/0a09f5cf-1db4-a09b-31a9-feefb7eeaac0.png)

公式: [FFmpeg](https://www.ffmpeg.org/)

> A complete, cross-platform solution to record, convert and stream audio and video.
(オーディオとビデオを録音、変換、ストリーミングするための完全なクロスプラットフォームソリューションです)


CUIを使って、動画や音声を変換できるツールですね。

割といろんなことができそうっすね。

【参照】
[FFmpegコマンド集](https://qiita.com/Frezledz/items/41bc9bb32ef1663fbd79)

## ImageMagickとは？
![ImageMagick_logo.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3932951/9c7421ff-f3d8-453f-96ec-9e2833cab06d.png)

公式: [imageMagick](https://imagemagick.org/index.php)

怪しい魔法使いのおじいさんが出迎えてくれます。

ちなみに、ImageMagickは脆弱性に問題があるとか言われていますが、ImageMagickを導入しているwebサイトでなどで、悪意のある画像ファイルを流すと起きるとかなんとか(おそらくそれはローカルでも同じ)。  

ImageMagick自体はローカルで使用し、使用する動画ファイルも自分で撮ってこれる場合はその方が安全だと思います。

【参照】
[さようならImageMagick](http://blog.cybozu.io/entry/2018/08/21/080000)  
[「さようなら ImageMagick」の考察](https://qiita.com/yoya/items/2076c1f5137d4041e3aa)


## 作成方法

### 1. FFmpeg、ImageMagick をインストールする  
自分はHomebrewを使いました。以下のコマンドでインストールできます。Homebrewインストールしてない場合は、インストールもらえればと。

```bash
brew install ffmpeg
brew install imagemagick
```

無事にインストールされているかの確認

```
ffmpeg -version
magick -version
```

 ### 2.動画ファイルがあるディレクトリに移動し、動画ファイルをフレームに変換する

ffmpegの以下コマンドを実行します。  
```bash
cd movie_dir
ffmpeg -i fe_motion.mp4 -vf "fps=10" -ss 5 -t 5 frame_%4d.png
```
`fe_motion.mp4`：使用する動画ファイル名

`fps=10`で1秒間に10コマの画像に分けてます。  
`-ss 5 -t -5` は動画ファイルの開始5秒目 ~ 10秒目までの指定をしています。開始の指定が必要なければ `-ss 秒数`はいらないかと。 
`frame_%4d.png` 出力ファイル名の形式を設定します。今回はpngファイルにしてます。

実行すると、動画ファイルのあるディレクトリに以下のように分割した画像が出力されます。
![スクリーンショット 2025-02-08 13.39.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3932951/b2271e0c-b71b-3db7-6df2-760baa34874b.png)

 
### 3.作成した.png画像をつなげてGIF画像にする
```bash
magick -delay 10 -loop 0 frame_*.png sample.gif
```

`magick`コマンドでGIF画像にします。`convert`コマンドでもできますが、ImageMagick ver.7以降 **`magick`コマンド推奨**らしいです。`convert`コマンドを実行すると以下の警告がでます。


```bash
WARNING: The convert command is deprecated in IMv7, use "magick" insted of "convert" or "magick convert"
```

## メモ
仕事で使う場合、画像のどこを切り取ってもセキュリティ的に気を使う必要があるので、どうしてもweb上にある変換ツールは怖いなと思った次第です。


## 追記 (2025/02/16) 
作成したGIF、Qiitaには載せられなかったけど、
自身の[WordPress](https://tennensui4.wordpress.com/2025/02/16/ffmpeg%e3%81%a8imagemagick%e3%82%92%e4%bd%bf%e3%81%a3%e3%81%a6%e4%bd%9c%e6%88%90%e3%81%97%e3%81%9fgif/)には載せられたのでよければ見てみてください。
Only plain objects, and a few built-ins, can be passed to Client Components from Server Components. Classes or null prototypes are not supportedエラーが出る

# はじめに

サーバーサイドとクライアントサイドの挙動についてつまづいたところがあったのでまとめておきます

# 問題

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

```tsx
export default async function Popular() {
type ArticleJson = {
  title: string;
  description: string;
  author: string;
  cretedAt: number;
  viewCount: number;
};

const getArticles = async () => {
  const response = await fetch("http://localhost:3000/api/blogs/popular");
  const data = await response.json();
  return data;
};

export default async function Popular() {
  const articlesData = (await getArticles()) as ArticleJson[];
  const articles = articlesData.map((article: ArticleJson) => {
    return new Article(
      article.title,
      article.description,
      article.author,
      article.cretedAt,
      article.viewCount
    );
  });

  if (!articles) return <div>Loading...</div>;

  return (
    <div className="container mx-auto px-4 py-8">
      {/* ... 残りのコード ... */}
      <PopularArticleList articles={articles} />
    </div>
  );
}
```

```
Only plain objects, and a few built-ins, can be passed to Client Components from Server Components. Classes or null prototypes are not supported.   <... article={{title: ..., description: ..., author: ..., createdAt: ..., viewCount: ...}}>                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
```

ここで`PopularArticleList`はクライアントサイド(use client)になっています

# 解決方法

```tsx
export default async function Popular() {
  const articlesData = (await getArticles()) as ArticleJson[];
  // クラスのインスタンスを作成せず、プレーンオブジェクトとして渡す
  const articles = articlesData.map((article: ArticleJson) => ({
    title: article.title,
    description: article.description,
    author: article.author,
    createdAt: article.cretedAt,
    viewCount: article.viewCount
  }));

  if (!articles) return <div>Loading...</div>;

  return (
    <div className="container mx-auto px-4 py-8">
      {/* ... 残りのコード ... */}
      <PopularArticleList articles={articles} />
    </div>
  );
}
```

```tsx
type ArticleProps = {
  title: string;
  description: string;
  author: string;
  createdAt: number;
  viewCount: number;
};

export function PopularArticleList({ articles }: { articles: ArticleProps[] }) {
  // ... コンポーネントの実装 ...
}
```

クラスではなくオブジェクトを渡すことでうまくいきました

このエラーの原因は、React Server Components (RSC) とClient Componentsの間のデータの受け渡し方に関する制限に起因します

Server ComponentsからClient Componentsにデータを渡す際、そのデータは必ずネットワーク越しに送信される必要があります
そのため、データは必ずJSONとしてシリアライズ可能である必要があります
クラスのインスタンスはメソッドや特別なプロトタイプを持っているため、単純にJSONとしてシリアライズすることができません

# おわりに

この挙動を知らなかったのでしれてよかったで